ALWAYS refer to SPECIFICATION.md before implementing any features. This document contains the complete design specification for the Nutrient DWS Python Client library.
Building a Python client library for the Nutrient Document Web Services (DWS) API with two main interfaces:
- Direct API: Static methods on
NutrientClientfor single operations - Builder API: Fluent, chainable interface for multi-step workflows
Python Version: Target Python 3.10+ and leverage type hints to their full extent for better developer experience and code quality.
- Main entry point:
NutrientClientclass - Builder pattern via
client.build()returnsBuildAPIWrapper - Only external dependency:
requestslibrary - Use
srclayout for package structure
- Direct API methods are snake_case versions of OpenAPI tool names
- All tool-specific parameters are keyword-only arguments
- File inputs accept: str (path), bytes, or file-like objects
- File outputs return bytes by default, or save to path if
output_pathprovided
Custom exceptions hierarchy:
NutrientError(base)AuthenticationError(401/403)APIError(other API errors with status_code and response_body)
- Run linting:
ruff check . - Run type checking:
mypy src/ - Run tests:
pytest - Format code:
ruff format .
Always run the quality checks above to ensure code meets standards.
- Refer to SPECIFICATION.md for requirements
- Implement features incrementally
- Write tests alongside implementation
- Update documentation/docstrings
- Run quality checks before marking tasks complete
- Use
ghcli tool for GitHub operations. - Always check out a feature branch when beginning work.
- When sending a PR, always wait for tests to run and if it fails, read the logs to fix it.
- Use
gh run listwith branch filter to not get confused.
PR Strategy: All GitHub PRs are squash merged to maintain clean commit history.
Many Nutrient DWS tools use the Build API (/build endpoint) rather than dedicated tool endpoints:
# Pattern for Build API tools
instructions = {
"parts": [{"file": "file", "pages": page_range}], # or other part config
"actions": [] # or specific actions for the tool
}
result = self._http_client.post("/build", files=files, json_data=instructions)- Page Ranges: Use
{"start": 0, "end": 4}(0-based, end inclusive) and{"start": 10}(to end) - Multiple Operations: Some tools require multiple API calls (one per page range/operation)
- Error Handling: API returns 400 with detailed errors when parameters are invalid
- Testing Strategy: Focus on integration tests with live API rather than unit test mocking
- File Handling: Use
prepare_file_for_upload()andsave_file_output()from file_handler module
def new_tool(
self,
input_file: FileInput,
output_path: Optional[str] = None,
# tool-specific parameters with proper typing
) -> Optional[bytes]:
"""Tool description following existing docstring patterns."""
# Use _process_file for simple tools or implement Build API pattern for complex ones
return self._process_file("tool-name", input_file, output_path, **options)