11"""Staged workflow builder that provides compile-time safety through Python's type system."""
2+
23from __future__ import annotations
34
4- from typing import Any , Literal , Optional , cast , List , TypeGuard , Dict , Callable
5+ from collections .abc import Callable
6+ from typing import Literal , TypeGuard , cast
57
68from nutrient_dws .builder .base_builder import BaseBuilder
7- from nutrient_dws .builder .constant import BuildOutputs , ActionWithFileInput
9+ from nutrient_dws .builder .constant import ActionWithFileInput , BuildOutputs
810from nutrient_dws .builder .staged_builders import (
911 ApplicableAction ,
1012 BufferOutput ,
1416 WorkflowDryRunResult ,
1517 WorkflowError ,
1618 WorkflowExecuteOptions ,
17- WorkflowInitialStage ,
1819 WorkflowWithActionsStage ,
1920 WorkflowWithOutputStage ,
2021 WorkflowWithPartsStage ,
2122)
2223from nutrient_dws .errors import ValidationError
23- from nutrient_dws .http import NutrientClientOptions , BuildRequestData , AnalyzeBuildRequestData
24+ from nutrient_dws .http import AnalyzeBuildRequestData , BuildRequestData , NutrientClientOptions
2425from nutrient_dws .inputs import (
2526 FileInput ,
2627 NormalizedFileData ,
3031)
3132from nutrient_dws .types .build_actions import BuildAction
3233from nutrient_dws .types .build_instruction import BuildInstructions
33- from nutrient_dws .types .build_output import PDFOutput , BuildOutput , PDFOutputOptions , PDFAOutputOptions , \
34- PDFUAOutputOptions , ImageOutputOptions , JSONContentOutputOptions
34+ from nutrient_dws .types .build_output import (
35+ BuildOutput ,
36+ ImageOutputOptions ,
37+ JSONContentOutputOptions ,
38+ PDFAOutputOptions ,
39+ PDFOutput ,
40+ PDFOutputOptions ,
41+ PDFUAOutputOptions ,
42+ )
3543from nutrient_dws .types .file_handle import FileHandle , RemoteFileHandle
36- from nutrient_dws .types .input_parts import FilePart , HTMLPart , NewPagePart , DocumentPart , FilePartOptions , \
37- HTMLPartOptions , NewPagePartOptions , DocumentPartOptions
44+ from nutrient_dws .types .input_parts import (
45+ DocumentPart ,
46+ DocumentPartOptions ,
47+ FilePart ,
48+ FilePartOptions ,
49+ HTMLPart ,
50+ HTMLPartOptions ,
51+ NewPagePart ,
52+ NewPagePartOptions ,
53+ )
3854
3955
4056class StagedWorkflowBuilder (
@@ -54,7 +70,7 @@ def __init__(self, client_options: NutrientClientOptions) -> None:
5470 """
5571 super ().__init__ (client_options )
5672 self .build_instructions : BuildInstructions = {"parts" : []}
57- self .assets : Dict [str , FileInput ] = {}
73+ self .assets : dict [str , FileInput ] = {}
5874 self .asset_index = 0
5975 self .current_step = 0
6076 self .is_executed = False
@@ -94,7 +110,7 @@ def _validate(self) -> None:
94110 raise ValidationError ("Workflow has no parts to execute" )
95111
96112 if "output" not in self .build_instructions :
97- self .build_instructions ["output" ] = cast (PDFOutput , {"type" : "pdf" })
113+ self .build_instructions ["output" ] = cast (" PDFOutput" , {"type" : "pdf" })
98114
99115 def _process_action (self , action : ApplicableAction ) -> BuildAction :
100116 """Process an action, registering files if needed.
@@ -113,9 +129,11 @@ def _process_action(self, action: ApplicableAction) -> BuildAction:
113129 file_handle = self ._register_asset (action .fileInput )
114130 return action .createAction (file_handle )
115131 else :
116- return cast (BuildAction , action )
132+ return cast (" BuildAction" , action )
117133
118- def _is_action_with_file_input (self , action : ApplicableAction ) -> TypeGuard [ActionWithFileInput ]:
134+ def _is_action_with_file_input (
135+ self , action : ApplicableAction
136+ ) -> TypeGuard [ActionWithFileInput ]:
119137 """Type guard to check if action needs file registration.
120138
121139 Args:
@@ -167,8 +185,8 @@ def _cleanup(self) -> None:
167185 def add_file_part (
168186 self ,
169187 file : FileInput ,
170- options : Optional [ FilePartOptions ] = None ,
171- actions : Optional [ List [ ApplicableAction ]] = None ,
188+ options : FilePartOptions | None = None ,
189+ actions : list [ ApplicableAction ] | None = None ,
172190 ) -> WorkflowWithPartsStage :
173191 """Add a file part to the workflow.
174192
@@ -208,9 +226,9 @@ def add_file_part(
208226 def add_html_part (
209227 self ,
210228 html : FileInput ,
211- assets : Optional [ List [ FileInput ]] = None ,
212- options : Optional [ HTMLPartOptions ] = None ,
213- actions : Optional [ List [ ApplicableAction ]] = None ,
229+ assets : list [ FileInput ] | None = None ,
230+ options : HTMLPartOptions | None = None ,
231+ actions : list [ ApplicableAction ] | None = None ,
214232 ) -> WorkflowWithPartsStage :
215233 """Add an HTML part to the workflow.
216234
@@ -265,8 +283,8 @@ def add_html_part(
265283
266284 def add_new_page (
267285 self ,
268- options : Optional [ NewPagePartOptions ] = None ,
269- actions : Optional [ List [ ApplicableAction ]] = None ,
286+ options : NewPagePartOptions | None = None ,
287+ actions : list [ ApplicableAction ] | None = None ,
270288 ) -> WorkflowWithPartsStage :
271289 """Add a new blank page to the workflow.
272290
@@ -304,8 +322,8 @@ def add_new_page(
304322 def add_document_part (
305323 self ,
306324 document_id : str ,
307- options : Optional [ DocumentPartOptions ] = None ,
308- actions : Optional [ List [ ApplicableAction ]] = None ,
325+ options : DocumentPartOptions | None = None ,
326+ actions : list [ ApplicableAction ] | None = None ,
309327 ) -> WorkflowWithPartsStage :
310328 """Add a document part to the workflow by referencing an existing document by ID.
311329
@@ -351,7 +369,7 @@ def add_document_part(
351369
352370 # Action methods (WorkflowWithPartsStage)
353371
354- def apply_actions (self , actions : List [ApplicableAction ]) -> WorkflowWithActionsStage :
372+ def apply_actions (self , actions : list [ApplicableAction ]) -> WorkflowWithActionsStage :
355373 """Apply multiple actions to the workflow.
356374
357375 Args:
@@ -367,7 +385,7 @@ def apply_actions(self, actions: List[ApplicableAction]) -> WorkflowWithActionsS
367385
368386 processed_actions = [self ._process_action (action ) for action in actions ]
369387 self .build_instructions ["actions" ].extend (processed_actions )
370- return cast (WorkflowWithActionsStage , self )
388+ return cast (" WorkflowWithActionsStage" , self )
371389
372390 def apply_action (self , action : ApplicableAction ) -> WorkflowWithActionsStage :
373391 """Apply a single action to the workflow.
@@ -390,80 +408,79 @@ def _output(self, output: BuildOutput) -> StagedWorkflowBuilder:
390408
391409 def output_pdf (
392410 self ,
393- options : Optional [ PDFOutputOptions ] = None ,
411+ options : PDFOutputOptions | None = None ,
394412 ) -> WorkflowWithOutputStage :
395413 """Set the output format to PDF."""
396414 self ._output (BuildOutputs .pdf (options ))
397- return cast (WorkflowWithOutputStage , self )
415+ return cast (" WorkflowWithOutputStage" , self )
398416
399417 def output_pdfa (
400418 self ,
401- options : Optional [ PDFAOutputOptions ] = None ,
419+ options : PDFAOutputOptions | None = None ,
402420 ) -> WorkflowWithOutputStage :
403421 """Set the output format to PDF/A."""
404422 self ._output (BuildOutputs .pdfa (options ))
405- return cast (WorkflowWithOutputStage , self )
423+ return cast (" WorkflowWithOutputStage" , self )
406424
407425 def output_pdfua (
408426 self ,
409- options : Optional [ PDFUAOutputOptions ] = None ,
427+ options : PDFUAOutputOptions | None = None ,
410428 ) -> WorkflowWithOutputStage :
411429 """Set the output format to PDF/UA."""
412430 self ._output (BuildOutputs .pdfua (options ))
413- return cast (WorkflowWithOutputStage , self )
431+ return cast (" WorkflowWithOutputStage" , self )
414432
415433 def output_image (
416434 self ,
417435 format : Literal ["png" , "jpeg" , "jpg" , "webp" ],
418- options : Optional [ ImageOutputOptions ] = None ,
436+ options : ImageOutputOptions | None = None ,
419437 ) -> WorkflowWithOutputStage :
420438 """Set the output format to an image format."""
421439 if not options or not any (k in options for k in ["dpi" , "width" , "height" ]):
422440 raise ValidationError (
423441 "Image output requires at least one of the following options: dpi, height, width"
424442 )
425443 self ._output (BuildOutputs .image (format , options ))
426- return cast (WorkflowWithOutputStage , self )
444+ return cast (" WorkflowWithOutputStage" , self )
427445
428446 def output_office (
429447 self ,
430448 format : Literal ["docx" , "xlsx" , "pptx" ],
431449 ) -> WorkflowWithOutputStage :
432450 """Set the output format to an Office document format."""
433451 self ._output (BuildOutputs .office (format ))
434- return cast (WorkflowWithOutputStage , self )
452+ return cast (" WorkflowWithOutputStage" , self )
435453
436454 def output_html (
437- self ,
438- layout : Optional [Literal ["page" , "reflow" ]] = None
455+ self , layout : Literal ["page" , "reflow" ] | None = None
439456 ) -> WorkflowWithOutputStage :
440457 """Set the output format to HTML."""
441458 casted_layout : Literal ["page" , "reflow" ] = "page"
442459 if layout is not None :
443460 casted_layout = layout
444461 self ._output (BuildOutputs .html (casted_layout ))
445- return cast (WorkflowWithOutputStage , self )
462+ return cast (" WorkflowWithOutputStage" , self )
446463
447464 def output_markdown (
448465 self ,
449466 ) -> WorkflowWithOutputStage :
450467 """Set the output format to Markdown."""
451468 self ._output (BuildOutputs .markdown ())
452- return cast (WorkflowWithOutputStage , self )
469+ return cast (" WorkflowWithOutputStage" , self )
453470
454471 def output_json (
455472 self ,
456- options : Optional [ JSONContentOutputOptions ] = None ,
473+ options : JSONContentOutputOptions | None = None ,
457474 ) -> WorkflowWithOutputStage :
458475 """Set the output format to JSON content."""
459476 self ._output (BuildOutputs .jsonContent (options ))
460- return cast (WorkflowWithOutputStage , self )
477+ return cast (" WorkflowWithOutputStage" , self )
461478
462479 # Execution methods (WorkflowWithOutputStage)
463480
464481 async def execute (
465482 self ,
466- options : Optional [ WorkflowExecuteOptions ] = None ,
483+ options : WorkflowExecuteOptions | None = None ,
467484 ) -> TypedWorkflowResult :
468485 """Execute the workflow and return the result.
469486
@@ -486,13 +503,13 @@ async def execute(
486503 # Step 1: Validate
487504 self .current_step = 1
488505 if options and options .get ("onProgress" ):
489- cast (Callable [[int , int ], None ], options ["onProgress" ])(self .current_step , 3 )
506+ cast (" Callable[[int, int], None]" , options ["onProgress" ])(self .current_step , 3 )
490507 self ._validate ()
491508
492509 # Step 2: Prepare files
493510 self .current_step = 2
494511 if options and options .get ("onProgress" ):
495- cast (Callable [[int , int ], None ], options ["onProgress" ])(self .current_step , 3 )
512+ cast (" Callable[[int, int], None]" , options ["onProgress" ])(self .current_step , 3 )
496513
497514 output_config = self .build_instructions .get ("output" )
498515 if not output_config :
@@ -509,7 +526,7 @@ async def execute(
509526 # Step 3: Process response
510527 self .current_step = 3
511528 if options and options .get ("onProgress" ):
512- cast (Callable [[int , int ], None ], options ["onProgress" ])(self .current_step , 3 )
529+ cast (" Callable[[int, int], None]" , options ["onProgress" ])(self .current_step , 3 )
513530
514531 if output_config ["type" ] == "json-content" :
515532 result ["success" ] = True
@@ -539,7 +556,7 @@ async def execute(
539556 "step" : self .current_step ,
540557 "error" : error if isinstance (error , Exception ) else Exception (str (error )),
541558 }
542- cast (List [WorkflowError ], result ["errors" ]).append (workflow_error )
559+ cast ("list [WorkflowError]" , result ["errors" ]).append (workflow_error )
543560
544561 finally :
545562 self ._cleanup ()
@@ -565,8 +582,7 @@ async def dry_run(self) -> WorkflowDryRunResult:
565582 self ._validate ()
566583
567584 response = await self ._send_request (
568- "/analyze_build" ,
569- AnalyzeBuildRequestData (instructions = self .build_instructions )
585+ "/analyze_build" , AnalyzeBuildRequestData (instructions = self .build_instructions )
570586 )
571587
572588 result ["success" ] = True
@@ -580,6 +596,6 @@ async def dry_run(self) -> WorkflowDryRunResult:
580596 "step" : 0 ,
581597 "error" : error if isinstance (error , Exception ) else Exception (str (error )),
582598 }
583- cast (List [WorkflowError ], result ["errors" ]).append (workflow_error )
599+ cast ("list [WorkflowError]" , result ["errors" ]).append (workflow_error )
584600
585601 return result
0 commit comments