Skip to content

Commit d06d5b1

Browse files
committed
feat: Add custom exception hierarchy
- Implement comprehensive exception classes for error handling - Add rich error context with status codes and request IDs - Create dedicated exceptions for auth, validation, timeout, and file errors - Add unit tests for all exception classes with 100% coverage
1 parent c4f756f commit d06d5b1

6 files changed

Lines changed: 833 additions & 11 deletions

File tree

IMPLEMENTATIONPLAN.md

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
{\rtf1\ansi\ansicpg1252\cocoartf2822
2+
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fnil\fcharset0 Monaco;}
3+
{\colortbl;\red255\green255\blue255;\red0\green0\blue0;\red0\green0\blue0;\red152\green152\blue152;
4+
}
5+
{\*\expandedcolortbl;;\cssrgb\c0\c0\c0;\csgray\c0\c0;\cssrgb\c66083\c66083\c66083;
6+
}
7+
\margl1440\margr1440\vieww37580\viewh22140\viewkind0
8+
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\partightenfactor0
9+
10+
\f0\fs24 \cf2 \cb3 \CocoaLigature0 Nutrient DWS Python Client - Implementation Plan\
11+
\
12+
Overview\
13+
\
14+
This plan outlines a systematic approach to building a high-quality Python client for Nutrient DWS API, emphasizing clean architecture, maintainability, and excellent developer experience.\
15+
\
16+
Phase 1: Foundation & Infrastructure (Day 1)\
17+
\
18+
1.1 Project Setup\
19+
\
20+
- Initialize project with modern Python packaging standards\
21+
- Create pyproject.toml with:\
22+
- Modern build system (setuptools with PEP 517/518)\
23+
- Development dependencies grouped appropriately\
24+
- Python 3.8+ requirement for broad compatibility\
25+
- Set up pre-commit hooks for code quality\
26+
- Configure ruff, mypy, and pytest\
27+
\
28+
1.2 Core Package Structure\
29+
\
30+
nutrient-dws-client-python/\
31+
\uc0\u9500 \u9472 \u9472 src/\
32+
\uc0\u9474 \u9492 \u9472 \u9472 nutrient/\
33+
\uc0\u9474 \u9500 \u9472 \u9472 __init__.py\
34+
\uc0\u9474 \u9500 \u9472 \u9472 client.py # Main NutrientClient class\
35+
\uc0\u9474 \u9500 \u9472 \u9472 builder.py # BuildAPIWrapper class\
36+
\uc0\u9474 \u9500 \u9472 \u9472 exceptions.py # Custom exceptions\
37+
\uc0\u9474 \u9500 \u9472 \u9472 file_handler.py # File I/O utilities\
38+
\uc0\u9474 \u9500 \u9472 \u9472 http_client.py # HTTP layer abstraction\
39+
\uc0\u9474 \u9492 \u9472 \u9472 api/\
40+
\uc0\u9474 \u9500 \u9472 \u9472 __init__.py\
41+
\uc0\u9474 \u9492 \u9472 \u9472 direct.py # Direct API methods\
42+
\uc0\u9500 \u9472 \u9472 tests/\
43+
\uc0\u9474 \u9500 \u9472 \u9472 unit/\
44+
\uc0\u9474 \u9500 \u9472 \u9472 integration/\
45+
\uc0\u9474 \u9492 \u9472 \u9472 fixtures/\
46+
\uc0\u9500 \u9472 \u9472 docs/\
47+
\uc0\u9500 \u9472 \u9472 .github/\
48+
\uc0\u9474 \u9492 \u9472 \u9472 workflows/\
49+
\uc0\u9492 \u9472 \u9472 pyproject.toml\
50+
\
51+
1.3 Git Strategy - Initial Setup\
52+
\
53+
- Commit 1: "Initial commit: Project structure and configuration"\
54+
- Commit 2: "Add development tooling and pre-commit hooks"\
55+
- Push to main branch\
56+
\
57+
Phase 2: Core Components (Day 1-2)\
58+
\
59+
2.1 Exception Hierarchy\
60+
\
61+
- Implement custom exceptions with rich error information\
62+
- Include request/response details for debugging\
63+
- Commit: "feat: Add custom exception hierarchy"\
64+
\
65+
2.2 HTTP Client Layer\
66+
\
67+
- Create abstraction over requests library\
68+
- Implement:\
69+
- Connection pooling with requests.Session\
70+
- Retry logic with exponential backoff\
71+
- Proper timeout handling\
72+
- Request/response logging (debug level)\
73+
- Feature Branch: feature/http-client\
74+
- Commit: "feat: Add HTTP client with connection pooling and retry logic"\
75+
\
76+
2.3 File Handler Module\
77+
\
78+
- Implement unified file input handling (path, bytes, file-like)\
79+
- Add streaming support for large files\
80+
- Memory-efficient file operations\
81+
- Commit: "feat: Add file handling utilities with streaming support"\
82+
\
83+
Phase 3: OpenAPI Integration & Direct API (Day 2-3)\
84+
\
85+
3.1 OpenAPI Analysis\
86+
\
87+
- Download and analyze the OpenAPI specification\
88+
- Create a script to parse and extract tool definitions\
89+
- Identify patterns and required parameters\
90+
- Commit: "feat: Add OpenAPI spec parser for tool discovery"\
91+
\
92+
3.2 Direct API Implementation\
93+
\
94+
Key Decision: Generate static methods for better IDE support and type hints\
95+
- Create method generator script from OpenAPI spec\
96+
- Generate strongly-typed method signatures\
97+
- Include comprehensive docstrings\
98+
- Feature Branch: feature/direct-api\
99+
- Commits:\
100+
- "feat: Add Direct API method generator"\
101+
- "feat: Generate Direct API methods from OpenAPI spec"\
102+
- "test: Add comprehensive tests for Direct API methods"\
103+
\
104+
Phase 4: Builder API (Day 3-4)\
105+
\
106+
4.1 Builder Pattern Implementation\
107+
\
108+
- Design immutable builder with method chaining\
109+
- Implement step validation\
110+
- Create efficient request construction\
111+
- Feature Branch: feature/builder-api\
112+
- Commits:\
113+
- "feat: Add BuildAPIWrapper with fluent interface"\
114+
- "feat: Implement execute method with multipart handling"\
115+
- "test: Add Builder API test suite"\
116+
\
117+
Phase 5: Main Client Integration (Day 4)\
118+
\
119+
5.1 NutrientClient Assembly\
120+
\
121+
- Integrate all components\
122+
- Add authentication with API key management\
123+
- Implement factory method for Builder API\
124+
- Add configuration validation\
125+
- Commit: "feat: Complete NutrientClient with authentication and configuration"\
126+
\
127+
Phase 6: Testing & Quality (Day 5)\
128+
\
129+
6.1 Comprehensive Testing\
130+
\
131+
- Unit tests with mocked HTTP responses\
132+
- Integration test suite (optional, env-gated)\
133+
- Edge case testing (large files, errors, etc.)\
134+
- Code coverage target: 90%+\
135+
- Commits:\
136+
- "test: Add unit test suite with mocked responses"\
137+
- "test: Add integration tests with real API"\
138+
\
139+
6.2 Quality Checks\
140+
\
141+
- Type hints validation with mypy\
142+
- Code formatting with ruff\
143+
- Security audit of dependencies\
144+
- Commit: "chore: Add type hints and fix linting issues"\
145+
\
146+
Phase 7: Documentation (Day 5-6)\
147+
\
148+
7.1 API Documentation\
149+
\
150+
- Set up Sphinx with modern theme\
151+
- Configure autodoc for API reference\
152+
- Write comprehensive docstrings\
153+
- Feature Branch: feature/documentation\
154+
- Commits:\
155+
- "docs: Set up Sphinx documentation"\
156+
- "docs: Add comprehensive API reference"\
157+
\
158+
7.2 User Documentation\
159+
\
160+
- Quickstart guide\
161+
- Advanced usage examples\
162+
- Migration guide (if applicable)\
163+
- Commit: "docs: Add user guides and examples"\
164+
\
165+
Phase 8: CI/CD & Distribution (Day 6)\
166+
\
167+
8.1 GitHub Actions\
168+
\
169+
- Test workflow (matrix: Python 3.8-3.12)\
170+
- Documentation build and deploy\
171+
- Package build verification\
172+
- Commit: "ci: Add GitHub Actions workflows"\
173+
\
174+
8.2 Distribution Prep\
175+
\
176+
- Version management setup\
177+
- PyPI packaging configuration\
178+
- Release checklist\
179+
- Commit: "chore: Prepare for PyPI distribution"\
180+
\
181+
Key Technical Decisions\
182+
\
183+
1. Static vs Dynamic API Methods\
184+
\
185+
Decision: Static generation from OpenAPI\
186+
- Pros: Better IDE support, type checking, discoverable API\
187+
- Implementation: Generate Python code from OpenAPI spec during development\
188+
\
189+
2. Memory Management\
190+
\
191+
Strategy: Streaming for large files\
192+
- Use generators for file reading\
193+
- Chunk-based upload/download\
194+
- Optional in-memory operations for small files\
195+
\
196+
3. Error Handling Philosophy\
197+
\
198+
Approach: Fail fast with rich context\
199+
- Detailed error messages\
200+
- Preserve original API responses\
201+
- No silent failures\
202+
\
203+
4. Testing Strategy\
204+
\
205+
Approach: Mock-first with optional integration\
206+
- Use responses library for HTTP mocking\
207+
- Separate integration tests behind environment flag\
208+
- Fixture-based test data\
209+
\
210+
5. Type Hints\
211+
\
212+
Standard: Full typing with Python 3.8+ support\
213+
- Use typing_extensions for newer features\
214+
- Runtime type checking optional\
215+
- mypy strict mode\
216+
\
217+
Potential Challenges & Mitigations\
218+
\
219+
1. OpenAPI Spec Complexity\
220+
\
221+
Risk: Complex or inconsistent spec\
222+
Mitigation: Build robust parser with fallbacks, manual overrides where needed\
223+
\
224+
2. Large File Handling\
225+
\
226+
Risk: Memory exhaustion\
227+
Mitigation: Streaming by default, chunk-based processing, progress callbacks\
228+
\
229+
3. API Rate Limits\
230+
\
231+
Risk: Client overwhelming the API\
232+
Mitigation: Built-in rate limiting, retry logic, clear error messages\
233+
\
234+
4. Version Compatibility\
235+
\
236+
Risk: API changes breaking client\
237+
Mitigation: Version pinning, compatibility layer, clear upgrade paths\
238+
\
239+
Git Workflow & Commit Strategy\
240+
\
241+
Branch Strategy\
242+
\
243+
- main: Stable, release-ready code\
244+
- feature/*: New features\
245+
- fix/*: Bug fixes\
246+
- docs/*: Documentation updates\
247+
\
248+
Commit Message Format\
249+
\
250+
<type>: <subject>\
251+
\
252+
<body>\
253+
\
254+
<footer>\
255+
Types: feat, fix, docs, test, chore, refactor\
256+
\
257+
Release Strategy\
258+
\
259+
- Semantic versioning (MAJOR.MINOR.PATCH)\
260+
- Changelog maintenance\
261+
- Tagged releases\
262+
- Automated PyPI deployment\
263+
\
264+
Success Metrics\
265+
\
266+
- Clean, intuitive API matching specification\
267+
- 90%+ test coverage\
268+
- <5s for full test suite\
269+
- Zero security vulnerabilities\
270+
- Comprehensive documentation\
271+
- Easy installation: pip install nutrient\
272+
\
273+
Timeline\
274+
\
275+
- Days 1-2: Foundation and core components\
276+
- Days 2-3: OpenAPI integration and Direct API\
277+
- Days 3-4: Builder API\
278+
- Day 4: Client integration\
279+
- Day 5: Testing and quality\
280+
- Days 5-6: Documentation and distribution\
281+
\
282+
This plan prioritizes iterative development with working software at each phase, comprehensive testing, and clean git history for long-term maintainability.}

src/nutrient/__init__.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,22 @@
44
"""
55

66
from nutrient.client import NutrientClient
7-
from nutrient.exceptions import APIError, AuthenticationError, NutrientError
7+
from nutrient.exceptions import (
8+
APIError,
9+
AuthenticationError,
10+
FileProcessingError,
11+
NutrientError,
12+
TimeoutError,
13+
ValidationError,
14+
)
815

916
__version__ = "0.1.0"
10-
__all__ = ["NutrientClient", "NutrientError", "AuthenticationError", "APIError"]
17+
__all__ = [
18+
"NutrientClient",
19+
"NutrientError",
20+
"AuthenticationError",
21+
"APIError",
22+
"ValidationError",
23+
"TimeoutError",
24+
"FileProcessingError",
25+
]

src/nutrient/exceptions.py

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Custom exceptions for Nutrient DWS client."""
22

3-
from typing import Optional
3+
from typing import Any, Dict, Optional
44

55

66
class NutrientError(Exception):
@@ -10,9 +10,18 @@ class NutrientError(Exception):
1010

1111

1212
class AuthenticationError(NutrientError):
13-
"""Raised when authentication fails (401/403 errors)."""
13+
"""Raised when authentication fails (401/403 errors).
14+
15+
This typically indicates:
16+
- Missing API key
17+
- Invalid API key
18+
- Expired API key
19+
- Insufficient permissions
20+
"""
1421

15-
pass
22+
def __init__(self, message: str = "Authentication failed") -> None:
23+
"""Initialize AuthenticationError."""
24+
super().__init__(message)
1625

1726

1827
class APIError(NutrientError):
@@ -21,15 +30,54 @@ class APIError(NutrientError):
2130
Attributes:
2231
status_code: HTTP status code from the API.
2332
response_body: Raw response body from the API for debugging.
33+
request_id: Request ID for tracking (if available).
2434
"""
2535

2636
def __init__(
2737
self,
2838
message: str,
2939
status_code: Optional[int] = None,
3040
response_body: Optional[str] = None,
41+
request_id: Optional[str] = None,
3142
) -> None:
3243
"""Initialize APIError with status code and response body."""
3344
super().__init__(message)
3445
self.status_code = status_code
35-
self.response_body = response_body
46+
self.response_body = response_body
47+
self.request_id = request_id
48+
49+
def __str__(self) -> str:
50+
"""String representation with all available error details."""
51+
parts = [str(self.args[0]) if self.args else "API Error"]
52+
53+
if self.status_code:
54+
parts.append(f"Status: {self.status_code}")
55+
56+
if self.request_id:
57+
parts.append(f"Request ID: {self.request_id}")
58+
59+
if self.response_body:
60+
parts.append(f"Response: {self.response_body}")
61+
62+
return " | ".join(parts)
63+
64+
65+
class ValidationError(NutrientError):
66+
"""Raised when request validation fails."""
67+
68+
def __init__(self, message: str, errors: Optional[Dict[str, Any]] = None) -> None:
69+
"""Initialize ValidationError with validation details."""
70+
super().__init__(message)
71+
self.errors = errors or {}
72+
73+
74+
class TimeoutError(NutrientError):
75+
"""Raised when a request times out."""
76+
77+
pass
78+
79+
80+
class FileProcessingError(NutrientError):
81+
"""Raised when file processing fails."""
82+
83+
pass

0 commit comments

Comments
 (0)