Skip to content

Commit 76f8145

Browse files
committed
enable precommit hook
1 parent 3c57afc commit 76f8145

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+595
-365
lines changed

.pre-commit-config.yaml

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,18 @@ repos:
33
rev: v4.5.0
44
hooks:
55
- id: trailing-whitespace
6-
# - id: end-of-file-fixer
6+
- id: end-of-file-fixer
77
- id: check-yaml
88
- id: check-added-large-files
99
- id: check-json
1010
- id: check-toml
1111
- id: check-merge-conflict
1212
- id: debug-statements
1313
- id: mixed-line-ending
14-
#
15-
# - repo: https://github.com/astral-sh/ruff-pre-commit
16-
# rev: v0.1.11
17-
# hooks:
18-
# - id: ruff
19-
# args: [--fix]
20-
# - id: ruff-format
21-
#
22-
# - repo: https://github.com/pre-commit/mirrors-mypy
23-
# rev: v1.8.0
24-
# hooks:
25-
# - id: mypy
26-
# additional_dependencies: [types-requests]
27-
# args: [--strict, --no-implicit-reexport]
28-
# files: ^src/
14+
15+
- repo: https://github.com/pre-commit/mirrors-mypy
16+
rev: v1.17.1
17+
hooks:
18+
- id: mypy
19+
additional_dependencies: [types-aiofiles, httpx]
20+
files: ^src/nutrient_dws

src/nutrient_dws/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
__all__ = [
1616
"APIError",
1717
"AuthenticationError",
18+
"NetworkError",
1819
"NutrientError",
1920
"ValidationError",
20-
"NetworkError",
2121
]

src/nutrient_dws/builder/base_builder.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
"""Base builder class that all builders extend from."""
2+
23
from abc import ABC, abstractmethod
3-
from typing import Any, Generic, Literal, TypeVar, Union
4+
from typing import Any, Literal
45

5-
from nutrient_dws.builder.staged_builders import TypedWorkflowResult, BufferOutput, ContentOutput, JsonContentOutput
6+
from nutrient_dws.builder.staged_builders import (
7+
TypedWorkflowResult,
8+
)
69
from nutrient_dws.http import (
710
AnalyzeBuildRequestData,
811
BuildRequestData,
@@ -22,15 +25,15 @@ def __init__(self, client_options: NutrientClientOptions) -> None:
2225

2326
async def _send_request(
2427
self,
25-
path: Union[Literal["/build"], Literal["/analyze_build"]],
26-
options: Union[BuildRequestData, AnalyzeBuildRequestData],
28+
path: Literal["/build"] | Literal["/analyze_build"],
29+
options: BuildRequestData | AnalyzeBuildRequestData,
2730
) -> Any:
2831
"""Sends a request to the API."""
29-
config: RequestConfig[Union[BuildRequestData, AnalyzeBuildRequestData]] = {
32+
config: RequestConfig[BuildRequestData | AnalyzeBuildRequestData] = {
3033
"endpoint": path,
3134
"method": "POST",
3235
"data": options,
33-
"headers": None
36+
"headers": None,
3437
}
3538

3639
response: Any = await send_request(config, self.client_options)

src/nutrient_dws/builder/builder.py

Lines changed: 62 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
"""Staged workflow builder that provides compile-time safety through Python's type system."""
2+
23
from __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

68
from 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
810
from nutrient_dws.builder.staged_builders import (
911
ApplicableAction,
1012
BufferOutput,
@@ -14,13 +16,12 @@
1416
WorkflowDryRunResult,
1517
WorkflowError,
1618
WorkflowExecuteOptions,
17-
WorkflowInitialStage,
1819
WorkflowWithActionsStage,
1920
WorkflowWithOutputStage,
2021
WorkflowWithPartsStage,
2122
)
2223
from nutrient_dws.errors import ValidationError
23-
from nutrient_dws.http import NutrientClientOptions, BuildRequestData, AnalyzeBuildRequestData
24+
from nutrient_dws.http import AnalyzeBuildRequestData, BuildRequestData, NutrientClientOptions
2425
from nutrient_dws.inputs import (
2526
FileInput,
2627
NormalizedFileData,
@@ -30,11 +31,26 @@
3031
)
3132
from nutrient_dws.types.build_actions import BuildAction
3233
from 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+
)
3543
from 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

4056
class 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

Comments
 (0)