pyUSPTO 0.4.4__tar.gz → 0.4.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyuspto-0.4.4 → pyuspto-0.4.5}/PKG-INFO +1 -1
- {pyuspto-0.4.4 → pyuspto-0.4.5}/requirements-dev.txt +1 -5
- {pyuspto-0.4.4 → pyuspto-0.4.5}/src/pyUSPTO/clients/base.py +144 -64
- {pyuspto-0.4.4 → pyuspto-0.4.5}/src/pyUSPTO/clients/bulk_data.py +4 -9
- {pyuspto-0.4.4 → pyuspto-0.4.5}/src/pyUSPTO/clients/patent_data.py +29 -38
- {pyuspto-0.4.4 → pyuspto-0.4.5}/src/pyUSPTO/clients/petition_decisions.py +13 -16
- {pyuspto-0.4.4 → pyuspto-0.4.5}/src/pyUSPTO/clients/ptab_appeals.py +4 -6
- {pyuspto-0.4.4 → pyuspto-0.4.5}/src/pyUSPTO/clients/ptab_interferences.py +4 -6
- {pyuspto-0.4.4 → pyuspto-0.4.5}/src/pyUSPTO/clients/ptab_trials.py +10 -12
- {pyuspto-0.4.4 → pyuspto-0.4.5}/src/pyUSPTO/models/patent_data.py +9 -3
- {pyuspto-0.4.4 → pyuspto-0.4.5}/src/pyUSPTO/models/petition_decisions.py +6 -3
- {pyuspto-0.4.4 → pyuspto-0.4.5}/src/pyUSPTO.egg-info/PKG-INFO +1 -1
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tests/clients/test_base.py +87 -50
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tests/clients/test_bulk_data_clients.py +3 -3
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tests/clients/test_patent_data_clients.py +327 -280
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tests/clients/test_petition_decision_clients.py +130 -112
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tests/integration/test_patent_data_integration.py +15 -15
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tests/integration/test_petition_decisions_integration.py +23 -23
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tests/integration/test_ptab_appeals_integration.py +18 -14
- {pyuspto-0.4.4 → pyuspto-0.4.5}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/.github/ISSUE_TEMPLATE/documentation-updates.yml +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/.github/dependabot.yml +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/.github/workflows/publish-docs.yaml +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/.github/workflows/publish-to-test-pypi.yml +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/.github/workflows/python-tests.yml +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/.gitignore +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/.readthedocs.yaml +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/ADVANCED.md +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/CHANGELOG.md +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/CODE_OF_CONDUCT.md +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/CONTRIBUTING.md +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/LICENSE +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/README.md +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/_version.pyi +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/Makefile +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/_static/.gitkeep +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/advanced.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/api/clients/bulk_data.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/api/clients/index.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/api/clients/patent_data.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/api/clients/petition_decisions.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/api/clients/ptab_appeals.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/api/clients/ptab_interferences.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/api/clients/ptab_trials.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/api/config/config.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/api/config/http_config.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/api/config/index.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/api/exceptions.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/api/index.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/api/models/base.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/api/models/bulk_data.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/api/models/index.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/api/models/patent_data.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/api/models/petition_decisions.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/api/models/ptab.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/api/models/utils.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/api/warnings.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/conf.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/configuration.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/development.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/examples/bulk_data.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/examples/ifw_example.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/examples/index.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/examples/patent_data.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/examples/petition_decisions.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/examples/ptab_appeals.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/examples/ptab_interferences.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/examples/ptab_trials.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/index.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/installation.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/docs/source/quickstart.rst +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/examples/bulk_data_example.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/examples/error_handling_example.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/examples/ifw_example.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/examples/patent_data_example.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/examples/petition_decisions_example.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/examples/ptab_appeals_example.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/examples/ptab_interferences_example.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/examples/ptab_trials_example.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/pyUSPTO.code-workspace +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/pyproject.toml +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/requirements.txt +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/setup.cfg +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/src/pyUSPTO/__init__.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/src/pyUSPTO/clients/__init__.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/src/pyUSPTO/config.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/src/pyUSPTO/exceptions.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/src/pyUSPTO/http_config.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/src/pyUSPTO/models/__init__.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/src/pyUSPTO/models/base.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/src/pyUSPTO/models/bulk_data.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/src/pyUSPTO/models/ptab.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/src/pyUSPTO/models/utils.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/src/pyUSPTO/py.typed +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/src/pyUSPTO/utils/__init__.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/src/pyUSPTO/warnings.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/src/pyUSPTO.egg-info/SOURCES.txt +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/src/pyUSPTO.egg-info/dependency_links.txt +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/src/pyUSPTO.egg-info/requires.txt +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/src/pyUSPTO.egg-info/top_level.txt +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/src/pyUSPTO.egg-info/zip-safe +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tests/__init__.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tests/clients/__init__.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tests/clients/test_ptab_appeals_client.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tests/clients/test_ptab_interferences_client.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tests/clients/test_ptab_trials_client.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tests/conftest.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tests/integration/__init__.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tests/integration/conftest.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tests/integration/test_bulk_data_integration.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tests/integration/test_ptab_interferences_integration.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tests/integration/test_ptab_trials_integration.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tests/models/__init__.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tests/models/test_bulk_data_models.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tests/models/test_patent_data_models.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tests/models/test_petition_decision_models.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tests/models/test_ptab_models.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tests/models/test_utils.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tests/test_config.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tests/test_edge_cases.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tests/test_exceptions.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tests/test_http_config.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tests/test_init.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tests/test_version.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tests/utils/__init__.py +0 -0
- {pyuspto-0.4.4 → pyuspto-0.4.5}/tox.ini +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyUSPTO
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.5
|
|
4
4
|
Summary: A Modern Python client for accessing the United States Patent and Trademark Office (USPTO) Open Data Portal (ODP) APIs.
|
|
5
5
|
Author-email: Andrew Piechocki <apiechocki@dunlapcodding.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -88,10 +88,6 @@ pytest-cov==7.0.0
|
|
|
88
88
|
# via pyuspto
|
|
89
89
|
pytest-mock==3.15.1
|
|
90
90
|
# via pyuspto
|
|
91
|
-
pyuspto @ file:///C:/Users/andrewp/Documents/GitHub/pyUSPTO
|
|
92
|
-
# via
|
|
93
|
-
# pyUSPTO (pyproject.toml)
|
|
94
|
-
# pyuspto
|
|
95
91
|
pyyaml==6.0.3
|
|
96
92
|
# via myst-parser
|
|
97
93
|
requests==2.32.5
|
|
@@ -115,7 +111,7 @@ sphinx==9.1.0
|
|
|
115
111
|
# sphinx-immaterial
|
|
116
112
|
# sphinx-rtd-theme
|
|
117
113
|
# sphinxcontrib-jquery
|
|
118
|
-
sphinx-autodoc-typehints==3.9.
|
|
114
|
+
sphinx-autodoc-typehints==3.9.8
|
|
119
115
|
# via pyuspto
|
|
120
116
|
sphinx-copybutton==0.5.2
|
|
121
117
|
# via pyuspto
|
|
@@ -14,6 +14,10 @@ from typing import (
|
|
|
14
14
|
runtime_checkable,
|
|
15
15
|
)
|
|
16
16
|
|
|
17
|
+
try:
|
|
18
|
+
from typing import Self
|
|
19
|
+
except ImportError:
|
|
20
|
+
from typing_extensions import Self
|
|
17
21
|
import requests
|
|
18
22
|
|
|
19
23
|
from pyUSPTO.config import USPTOConfig
|
|
@@ -31,7 +35,7 @@ class FromDictProtocol(Protocol):
|
|
|
31
35
|
"""Protocol for classes that can be created from a dictionary."""
|
|
32
36
|
|
|
33
37
|
@classmethod
|
|
34
|
-
def from_dict(cls, data: dict[str, Any], include_raw_data: bool = False) ->
|
|
38
|
+
def from_dict(cls, data: dict[str, Any], include_raw_data: bool = False) -> Self:
|
|
35
39
|
"""Create an object from a dictionary."""
|
|
36
40
|
...
|
|
37
41
|
|
|
@@ -39,6 +43,9 @@ class FromDictProtocol(Protocol):
|
|
|
39
43
|
# Type variable for response classes
|
|
40
44
|
T = TypeVar("T", bound=FromDictProtocol)
|
|
41
45
|
|
|
46
|
+
# Type variable for response classes
|
|
47
|
+
M = TypeVar("M", bound=FromDictProtocol)
|
|
48
|
+
|
|
42
49
|
|
|
43
50
|
class BaseUSPTOClient(Generic[T]):
|
|
44
51
|
"""Base client class for USPTO API clients."""
|
|
@@ -166,49 +173,56 @@ class BaseUSPTOClient(Generic[T]):
|
|
|
166
173
|
),
|
|
167
174
|
) from json_err
|
|
168
175
|
|
|
169
|
-
def
|
|
176
|
+
def _build_url(
|
|
170
177
|
self,
|
|
171
|
-
method: str,
|
|
172
178
|
endpoint: str,
|
|
179
|
+
custom_url: str | None = None,
|
|
180
|
+
custom_base_url: str | None = None,
|
|
181
|
+
) -> str:
|
|
182
|
+
"""Build the request URL from endpoint or custom URL.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
endpoint: API endpoint path (without base URL)
|
|
186
|
+
custom_url: Optional full custom URL (overrides endpoint and base URL)
|
|
187
|
+
custom_base_url: Optional custom base URL instead of self.base_url
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
The resolved URL string.
|
|
191
|
+
"""
|
|
192
|
+
if custom_url:
|
|
193
|
+
return custom_url
|
|
194
|
+
base = custom_base_url if custom_base_url else self.base_url
|
|
195
|
+
return f"{base}/{endpoint.lstrip('/')}"
|
|
196
|
+
|
|
197
|
+
def _execute_request(
|
|
198
|
+
self,
|
|
199
|
+
method: str,
|
|
200
|
+
url: str,
|
|
173
201
|
params: dict[str, Any] | None = None,
|
|
174
202
|
json_data: dict[str, Any] | None = None,
|
|
175
203
|
stream: bool = False,
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
custom_base_url: str | None = None,
|
|
179
|
-
) -> dict[str, Any] | T | requests.Response:
|
|
180
|
-
"""Make an HTTP request to the USPTO API.
|
|
204
|
+
) -> requests.Response:
|
|
205
|
+
"""Execute an HTTP request and return the raw Response.
|
|
181
206
|
|
|
182
|
-
|
|
183
|
-
|
|
207
|
+
Handles URL dispatch, error translation, and timeout/connection errors.
|
|
208
|
+
Callers are responsible for interpreting the response body.
|
|
184
209
|
|
|
185
210
|
Args:
|
|
186
211
|
method: HTTP method (GET or POST only)
|
|
187
|
-
|
|
212
|
+
url: Fully resolved request URL
|
|
188
213
|
params: Optional query parameters
|
|
189
214
|
json_data: Optional JSON body for POST requests
|
|
190
215
|
stream: Whether to stream the response
|
|
191
|
-
response_class: Class to use for parsing the response
|
|
192
|
-
custom_url: Optional full custom URL to use (overrides endpoint and base URL)
|
|
193
|
-
custom_base_url: Optional custom base URL to use instead of self.base_url
|
|
194
216
|
|
|
195
217
|
Returns:
|
|
196
|
-
|
|
197
|
-
- If stream=True: requests.Response object
|
|
198
|
-
- If response_class is provided: Instance of response_class
|
|
199
|
-
- Otherwise: Dict[str, Any] containing the JSON response
|
|
218
|
+
The raw requests.Response after raise_for_status().
|
|
200
219
|
|
|
201
220
|
Raises:
|
|
202
221
|
ValueError: If an unsupported HTTP method is provided
|
|
222
|
+
USPTOApiError: On HTTP errors from the API
|
|
223
|
+
USPTOTimeout: On request timeout
|
|
224
|
+
USPTOConnectionError: On connection failure
|
|
203
225
|
"""
|
|
204
|
-
url: str = ""
|
|
205
|
-
if custom_url:
|
|
206
|
-
url = custom_url
|
|
207
|
-
else:
|
|
208
|
-
base = custom_base_url if custom_base_url else self.base_url
|
|
209
|
-
url = f"{base}/{endpoint.lstrip('/')}"
|
|
210
|
-
|
|
211
|
-
# Get timeout from HTTP config
|
|
212
226
|
timeout = self.http_config.get_timeout_tuple()
|
|
213
227
|
|
|
214
228
|
try:
|
|
@@ -228,26 +242,10 @@ class BaseUSPTOClient(Generic[T]):
|
|
|
228
242
|
raise ValueError(f"Unsupported HTTP method: {method}")
|
|
229
243
|
|
|
230
244
|
response.raise_for_status()
|
|
231
|
-
|
|
232
|
-
# Return the raw response for streaming requests
|
|
233
|
-
if stream:
|
|
234
|
-
return response
|
|
235
|
-
|
|
236
|
-
# Parse JSON response with error handling
|
|
237
|
-
json_data = self._parse_json_response(response, url)
|
|
238
|
-
|
|
239
|
-
# Parse the response based on the specified class
|
|
240
|
-
if response_class:
|
|
241
|
-
parsed_response: T = response_class.from_dict(
|
|
242
|
-
json_data, include_raw_data=self.config.include_raw_data
|
|
243
|
-
)
|
|
244
|
-
return parsed_response
|
|
245
|
-
|
|
246
|
-
# Return the raw JSON for other requests
|
|
247
|
-
return json_data
|
|
245
|
+
return response
|
|
248
246
|
|
|
249
247
|
except requests.exceptions.HTTPError as http_err:
|
|
250
|
-
client_operation_message = f"API request to '{url}' failed with HTTPError"
|
|
248
|
+
client_operation_message = f"API request to '{url}' failed with HTTPError"
|
|
251
249
|
|
|
252
250
|
# Include request body for POST debugging
|
|
253
251
|
if method.upper() == "POST" and json_data:
|
|
@@ -257,7 +255,6 @@ class BaseUSPTOClient(Generic[T]):
|
|
|
257
255
|
f"\nRequest body sent:\n{json.dumps(json_data, indent=2)}"
|
|
258
256
|
)
|
|
259
257
|
|
|
260
|
-
# Create APIErrorArgs directly from the HTTPError
|
|
261
258
|
current_error_args = APIErrorArgs.from_http_error(
|
|
262
259
|
http_error=http_err, client_operation_message=client_operation_message
|
|
263
260
|
)
|
|
@@ -266,7 +263,6 @@ class BaseUSPTOClient(Generic[T]):
|
|
|
266
263
|
raise api_exception_to_raise from http_err
|
|
267
264
|
|
|
268
265
|
except requests.exceptions.Timeout as timeout_err:
|
|
269
|
-
# Specific handling for timeout errors
|
|
270
266
|
raise USPTOTimeout(
|
|
271
267
|
message=f"Request to '{url}' timed out",
|
|
272
268
|
api_short_error="Timeout",
|
|
@@ -274,31 +270,120 @@ class BaseUSPTOClient(Generic[T]):
|
|
|
274
270
|
) from timeout_err
|
|
275
271
|
|
|
276
272
|
except requests.exceptions.ConnectionError as conn_err:
|
|
277
|
-
# Specific handling for connection errors (DNS, refused connection, etc.)
|
|
278
273
|
raise USPTOConnectionError(
|
|
279
274
|
message=f"Failed to connect to '{url}'",
|
|
280
275
|
api_short_error="Connection Error",
|
|
281
276
|
error_details=str(conn_err),
|
|
282
277
|
) from conn_err
|
|
283
278
|
|
|
284
|
-
except
|
|
285
|
-
|
|
286
|
-
) as req_err: # Catches other non-HTTP errors from requests
|
|
287
|
-
client_operation_message = (
|
|
288
|
-
f"API request to '{url}' failed" # 'url' is from _make_request scope
|
|
289
|
-
)
|
|
279
|
+
except requests.exceptions.RequestException as req_err:
|
|
280
|
+
client_operation_message = f"API request to '{url}' failed"
|
|
290
281
|
|
|
291
|
-
# Create APIErrorArgs from the generic RequestException
|
|
292
282
|
current_error_args = APIErrorArgs.from_request_exception(
|
|
293
283
|
request_exception=req_err,
|
|
294
|
-
client_operation_message=client_operation_message,
|
|
284
|
+
client_operation_message=client_operation_message,
|
|
295
285
|
)
|
|
296
286
|
|
|
297
|
-
api_exception_to_raise = get_api_exception(
|
|
298
|
-
current_error_args
|
|
299
|
-
) # Will default to USPTOApiError
|
|
287
|
+
api_exception_to_raise = get_api_exception(current_error_args)
|
|
300
288
|
raise api_exception_to_raise from req_err
|
|
301
289
|
|
|
290
|
+
def _stream_request(
|
|
291
|
+
self,
|
|
292
|
+
method: str,
|
|
293
|
+
endpoint: str,
|
|
294
|
+
params: dict[str, Any] | None = None,
|
|
295
|
+
json_data: dict[str, Any] | None = None,
|
|
296
|
+
custom_url: str | None = None,
|
|
297
|
+
custom_base_url: str | None = None,
|
|
298
|
+
) -> requests.Response:
|
|
299
|
+
"""Make a streaming HTTP request and return the raw Response.
|
|
300
|
+
|
|
301
|
+
Args:
|
|
302
|
+
method: HTTP method (GET or POST only)
|
|
303
|
+
endpoint: API endpoint path (without base URL)
|
|
304
|
+
params: Optional query parameters
|
|
305
|
+
json_data: Optional JSON body for POST requests
|
|
306
|
+
custom_url: Optional full custom URL (overrides endpoint and base URL)
|
|
307
|
+
custom_base_url: Optional custom base URL instead of self.base_url
|
|
308
|
+
|
|
309
|
+
Returns:
|
|
310
|
+
Streaming requests.Response object.
|
|
311
|
+
"""
|
|
312
|
+
url = self._build_url(
|
|
313
|
+
endpoint, custom_url=custom_url, custom_base_url=custom_base_url
|
|
314
|
+
)
|
|
315
|
+
return self._execute_request(
|
|
316
|
+
method=method, url=url, params=params, json_data=json_data, stream=True
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
def _get_model(
|
|
320
|
+
self,
|
|
321
|
+
method: str,
|
|
322
|
+
endpoint: str,
|
|
323
|
+
response_class: type[M],
|
|
324
|
+
params: dict[str, Any] | None = None,
|
|
325
|
+
json_data: dict[str, Any] | None = None,
|
|
326
|
+
custom_url: str | None = None,
|
|
327
|
+
custom_base_url: str | None = None,
|
|
328
|
+
) -> M:
|
|
329
|
+
"""Make an HTTP request and parse the response into a model.
|
|
330
|
+
|
|
331
|
+
Args:
|
|
332
|
+
method: HTTP method (GET or POST only)
|
|
333
|
+
endpoint: API endpoint path (without base URL)
|
|
334
|
+
response_class: Class to use for parsing the response
|
|
335
|
+
params: Optional query parameters
|
|
336
|
+
json_data: Optional JSON body for POST requests
|
|
337
|
+
custom_url: Optional full custom URL (overrides endpoint and base URL)
|
|
338
|
+
custom_base_url: Optional custom base URL instead of self.base_url
|
|
339
|
+
|
|
340
|
+
Returns:
|
|
341
|
+
Instance of response_class parsed from the JSON response.
|
|
342
|
+
"""
|
|
343
|
+
url = self._build_url(
|
|
344
|
+
endpoint, custom_url=custom_url, custom_base_url=custom_base_url
|
|
345
|
+
)
|
|
346
|
+
response = self._execute_request(
|
|
347
|
+
method=method, url=url, params=params, json_data=json_data
|
|
348
|
+
)
|
|
349
|
+
data = self._parse_json_response(response, url)
|
|
350
|
+
|
|
351
|
+
ret = response_class.from_dict(
|
|
352
|
+
data, include_raw_data=self.config.include_raw_data
|
|
353
|
+
)
|
|
354
|
+
assert isinstance(ret, response_class)
|
|
355
|
+
return ret
|
|
356
|
+
|
|
357
|
+
def _get_json(
|
|
358
|
+
self,
|
|
359
|
+
method: str,
|
|
360
|
+
endpoint: str,
|
|
361
|
+
params: dict[str, Any] | None = None,
|
|
362
|
+
json_data: dict[str, Any] | None = None,
|
|
363
|
+
custom_url: str | None = None,
|
|
364
|
+
custom_base_url: str | None = None,
|
|
365
|
+
) -> dict[str, Any]:
|
|
366
|
+
"""Make an HTTP request and return the parsed JSON response.
|
|
367
|
+
|
|
368
|
+
Args:
|
|
369
|
+
method: HTTP method (GET or POST only)
|
|
370
|
+
endpoint: API endpoint path (without base URL)
|
|
371
|
+
params: Optional query parameters
|
|
372
|
+
json_data: Optional JSON body for POST requests
|
|
373
|
+
custom_url: Optional full custom URL (overrides endpoint and base URL)
|
|
374
|
+
custom_base_url: Optional custom base URL instead of self.base_url
|
|
375
|
+
|
|
376
|
+
Returns:
|
|
377
|
+
Dict containing the JSON response.
|
|
378
|
+
"""
|
|
379
|
+
url = self._build_url(
|
|
380
|
+
endpoint, custom_url=custom_url, custom_base_url=custom_base_url
|
|
381
|
+
)
|
|
382
|
+
response = self._execute_request(
|
|
383
|
+
method=method, url=url, params=params, json_data=json_data
|
|
384
|
+
)
|
|
385
|
+
return self._parse_json_response(response, url)
|
|
386
|
+
|
|
302
387
|
def paginate_results(
|
|
303
388
|
self,
|
|
304
389
|
method_name: str,
|
|
@@ -785,19 +870,14 @@ class BaseUSPTOClient(Generic[T]):
|
|
|
785
870
|
Path to downloaded file
|
|
786
871
|
|
|
787
872
|
Raises:
|
|
788
|
-
TypeError: If response is not a valid Response object
|
|
789
873
|
FileExistsError: If file exists and overwrite is False
|
|
790
874
|
"""
|
|
791
|
-
response = self.
|
|
875
|
+
response = self._stream_request(
|
|
792
876
|
method="GET",
|
|
793
877
|
endpoint="",
|
|
794
|
-
stream=True,
|
|
795
878
|
custom_url=url,
|
|
796
879
|
)
|
|
797
880
|
|
|
798
|
-
if not isinstance(response, requests.Response):
|
|
799
|
-
raise TypeError(f"Expected Response, got {type(response)}")
|
|
800
|
-
|
|
801
881
|
return self._save_response_to_file(
|
|
802
882
|
response=response,
|
|
803
883
|
destination=destination,
|
|
@@ -109,13 +109,12 @@ class BulkDataClient(BaseUSPTOClient[BulkDataResponse]):
|
|
|
109
109
|
params["latest"] = str(latest).lower()
|
|
110
110
|
|
|
111
111
|
# Use response_class for clean parsing
|
|
112
|
-
response = self.
|
|
112
|
+
response = self._get_model(
|
|
113
113
|
method="GET",
|
|
114
114
|
endpoint=endpoint,
|
|
115
|
-
params=params if params else None,
|
|
116
115
|
response_class=BulkDataResponse,
|
|
116
|
+
params=params if params else None,
|
|
117
117
|
)
|
|
118
|
-
assert isinstance(response, BulkDataResponse)
|
|
119
118
|
|
|
120
119
|
# Extract the product from response
|
|
121
120
|
if response.bulk_data_product_bag:
|
|
@@ -251,13 +250,9 @@ class BulkDataClient(BaseUSPTOClient[BulkDataResponse]):
|
|
|
251
250
|
if fields is not None:
|
|
252
251
|
params["fields"] = ",".join(fields)
|
|
253
252
|
|
|
254
|
-
|
|
253
|
+
return self._get_model(
|
|
255
254
|
method="GET",
|
|
256
255
|
endpoint=self.ENDPOINTS["products_search"],
|
|
257
|
-
params=params,
|
|
258
256
|
response_class=BulkDataResponse,
|
|
257
|
+
params=params,
|
|
259
258
|
)
|
|
260
|
-
|
|
261
|
-
# Since we specified response_class=BulkDataResponse, the result should be a BulkDataResponse
|
|
262
|
-
assert isinstance(result, BulkDataResponse)
|
|
263
|
-
return result
|
|
@@ -263,12 +263,12 @@ class PatentDataClient(BaseUSPTOClient[PatentDataResponse]):
|
|
|
263
263
|
endpoint = self.ENDPOINTS["search_applications"]
|
|
264
264
|
|
|
265
265
|
if post_body is not None:
|
|
266
|
-
result = self.
|
|
266
|
+
result = self._get_model(
|
|
267
267
|
method="POST",
|
|
268
268
|
endpoint=endpoint,
|
|
269
|
+
response_class=PatentDataResponse,
|
|
269
270
|
json_data=post_body,
|
|
270
271
|
params=additional_query_params,
|
|
271
|
-
response_class=PatentDataResponse,
|
|
272
272
|
)
|
|
273
273
|
else:
|
|
274
274
|
params: dict[str, Any] = {}
|
|
@@ -366,13 +366,12 @@ class PatentDataClient(BaseUSPTOClient[PatentDataResponse]):
|
|
|
366
366
|
|
|
367
367
|
if additional_query_params:
|
|
368
368
|
params.update(additional_query_params)
|
|
369
|
-
result = self.
|
|
369
|
+
result = self._get_model(
|
|
370
370
|
method="GET",
|
|
371
371
|
endpoint=endpoint,
|
|
372
|
-
params=params,
|
|
373
372
|
response_class=PatentDataResponse,
|
|
373
|
+
params=params,
|
|
374
374
|
)
|
|
375
|
-
assert isinstance(result, PatentDataResponse)
|
|
376
375
|
return result
|
|
377
376
|
|
|
378
377
|
def get_search_results(
|
|
@@ -408,7 +407,7 @@ class PatentDataClient(BaseUSPTOClient[PatentDataResponse]):
|
|
|
408
407
|
if "format" not in post_body:
|
|
409
408
|
post_body["format"] = "json"
|
|
410
409
|
|
|
411
|
-
result = self.
|
|
410
|
+
result = self._get_json(
|
|
412
411
|
method="POST",
|
|
413
412
|
endpoint=endpoint,
|
|
414
413
|
json_data=post_body,
|
|
@@ -504,12 +503,11 @@ class PatentDataClient(BaseUSPTOClient[PatentDataResponse]):
|
|
|
504
503
|
if additional_query_params:
|
|
505
504
|
params.update(additional_query_params)
|
|
506
505
|
|
|
507
|
-
result = self.
|
|
506
|
+
result = self._get_json(
|
|
508
507
|
method="GET",
|
|
509
508
|
endpoint=endpoint,
|
|
510
509
|
params=params,
|
|
511
510
|
)
|
|
512
|
-
assert isinstance(result, dict)
|
|
513
511
|
amd_list = [
|
|
514
512
|
ApplicationMetaData.from_dict(item["applicationMetaData"])
|
|
515
513
|
for item in result["patentdata"]
|
|
@@ -541,14 +539,14 @@ class PatentDataClient(BaseUSPTOClient[PatentDataResponse]):
|
|
|
541
539
|
endpoint = self.ENDPOINTS["get_application_by_number"].format(
|
|
542
540
|
application_number=self.sanitize_application_number(application_number)
|
|
543
541
|
)
|
|
544
|
-
response_data = self.
|
|
542
|
+
response_data = self._get_model(
|
|
545
543
|
method="GET", endpoint=endpoint, response_class=PatentDataResponse
|
|
546
544
|
)
|
|
547
|
-
|
|
548
|
-
return self._get_wrapper_from_response(
|
|
545
|
+
ret = self._get_wrapper_from_response(
|
|
549
546
|
response_data=response_data,
|
|
550
547
|
application_number_for_validation=application_number,
|
|
551
548
|
)
|
|
549
|
+
return ret
|
|
552
550
|
|
|
553
551
|
def get_application_metadata(
|
|
554
552
|
self, application_number: str
|
|
@@ -575,10 +573,9 @@ class PatentDataClient(BaseUSPTOClient[PatentDataResponse]):
|
|
|
575
573
|
endpoint = self.ENDPOINTS["get_application_metadata"].format(
|
|
576
574
|
application_number=self.sanitize_application_number(application_number)
|
|
577
575
|
)
|
|
578
|
-
response_data = self.
|
|
576
|
+
response_data = self._get_model(
|
|
579
577
|
method="GET", endpoint=endpoint, response_class=PatentDataResponse
|
|
580
578
|
)
|
|
581
|
-
assert isinstance(response_data, PatentDataResponse)
|
|
582
579
|
wrapper = self._get_wrapper_from_response(response_data, application_number)
|
|
583
580
|
return wrapper.application_meta_data if wrapper else None
|
|
584
581
|
|
|
@@ -606,10 +603,9 @@ class PatentDataClient(BaseUSPTOClient[PatentDataResponse]):
|
|
|
606
603
|
endpoint = self.ENDPOINTS["get_application_adjustment"].format(
|
|
607
604
|
application_number=self.sanitize_application_number(application_number)
|
|
608
605
|
)
|
|
609
|
-
response_data = self.
|
|
606
|
+
response_data = self._get_model(
|
|
610
607
|
method="GET", endpoint=endpoint, response_class=PatentDataResponse
|
|
611
608
|
)
|
|
612
|
-
assert isinstance(response_data, PatentDataResponse)
|
|
613
609
|
wrapper = self._get_wrapper_from_response(response_data, application_number)
|
|
614
610
|
return wrapper.patent_term_adjustment_data if wrapper else None
|
|
615
611
|
|
|
@@ -638,10 +634,9 @@ class PatentDataClient(BaseUSPTOClient[PatentDataResponse]):
|
|
|
638
634
|
endpoint = self.ENDPOINTS["get_application_assignment"].format(
|
|
639
635
|
application_number=self.sanitize_application_number(application_number)
|
|
640
636
|
)
|
|
641
|
-
response_data = self.
|
|
637
|
+
response_data = self._get_model(
|
|
642
638
|
method="GET", endpoint=endpoint, response_class=PatentDataResponse
|
|
643
639
|
)
|
|
644
|
-
assert isinstance(response_data, PatentDataResponse)
|
|
645
640
|
wrapper = self._get_wrapper_from_response(response_data, application_number)
|
|
646
641
|
return wrapper.assignment_bag if wrapper else None
|
|
647
642
|
|
|
@@ -668,10 +663,9 @@ class PatentDataClient(BaseUSPTOClient[PatentDataResponse]):
|
|
|
668
663
|
endpoint = self.ENDPOINTS["get_application_attorney"].format(
|
|
669
664
|
application_number=self.sanitize_application_number(application_number)
|
|
670
665
|
)
|
|
671
|
-
response_data = self.
|
|
666
|
+
response_data = self._get_model(
|
|
672
667
|
method="GET", endpoint=endpoint, response_class=PatentDataResponse
|
|
673
668
|
)
|
|
674
|
-
assert isinstance(response_data, PatentDataResponse)
|
|
675
669
|
wrapper = self._get_wrapper_from_response(response_data, application_number)
|
|
676
670
|
return wrapper.record_attorney if wrapper else None
|
|
677
671
|
|
|
@@ -701,10 +695,9 @@ class PatentDataClient(BaseUSPTOClient[PatentDataResponse]):
|
|
|
701
695
|
endpoint = self.ENDPOINTS["get_application_continuity"].format(
|
|
702
696
|
application_number=self.sanitize_application_number(application_number)
|
|
703
697
|
)
|
|
704
|
-
response_data = self.
|
|
698
|
+
response_data = self._get_model(
|
|
705
699
|
method="GET", endpoint=endpoint, response_class=PatentDataResponse
|
|
706
700
|
)
|
|
707
|
-
assert isinstance(response_data, PatentDataResponse)
|
|
708
701
|
wrapper = self._get_wrapper_from_response(response_data, application_number)
|
|
709
702
|
return ApplicationContinuityData.from_wrapper(wrapper) if wrapper else None
|
|
710
703
|
|
|
@@ -733,10 +726,9 @@ class PatentDataClient(BaseUSPTOClient[PatentDataResponse]):
|
|
|
733
726
|
endpoint = self.ENDPOINTS["get_application_foreign_priority"].format(
|
|
734
727
|
application_number=self.sanitize_application_number(application_number)
|
|
735
728
|
)
|
|
736
|
-
response_data = self.
|
|
729
|
+
response_data = self._get_model(
|
|
737
730
|
method="GET", endpoint=endpoint, response_class=PatentDataResponse
|
|
738
731
|
)
|
|
739
|
-
assert isinstance(response_data, PatentDataResponse)
|
|
740
732
|
wrapper = self._get_wrapper_from_response(response_data, application_number)
|
|
741
733
|
return wrapper.foreign_priority_bag if wrapper else None
|
|
742
734
|
|
|
@@ -765,10 +757,9 @@ class PatentDataClient(BaseUSPTOClient[PatentDataResponse]):
|
|
|
765
757
|
endpoint = self.ENDPOINTS["get_application_transactions"].format(
|
|
766
758
|
application_number=self.sanitize_application_number(application_number)
|
|
767
759
|
)
|
|
768
|
-
response_data = self.
|
|
760
|
+
response_data = self._get_model(
|
|
769
761
|
method="GET", endpoint=endpoint, response_class=PatentDataResponse
|
|
770
762
|
)
|
|
771
|
-
assert isinstance(response_data, PatentDataResponse)
|
|
772
763
|
wrapper = self._get_wrapper_from_response(response_data, application_number)
|
|
773
764
|
return wrapper.event_data_bag if wrapper else None
|
|
774
765
|
|
|
@@ -819,11 +810,12 @@ class PatentDataClient(BaseUSPTOClient[PatentDataResponse]):
|
|
|
819
810
|
if official_date_to:
|
|
820
811
|
params["officialDateTo"] = official_date_to
|
|
821
812
|
|
|
822
|
-
|
|
823
|
-
method="GET",
|
|
813
|
+
return self._get_model(
|
|
814
|
+
method="GET",
|
|
815
|
+
endpoint=endpoint,
|
|
816
|
+
response_class=DocumentBag,
|
|
817
|
+
params=params if params else None,
|
|
824
818
|
)
|
|
825
|
-
assert isinstance(result_dict, dict)
|
|
826
|
-
return DocumentBag.from_dict(result_dict)
|
|
827
819
|
|
|
828
820
|
def get_application_associated_documents(
|
|
829
821
|
self, application_number: str
|
|
@@ -855,10 +847,9 @@ class PatentDataClient(BaseUSPTOClient[PatentDataResponse]):
|
|
|
855
847
|
endpoint = self.ENDPOINTS["get_application_associated_documents"].format(
|
|
856
848
|
application_number=self.sanitize_application_number(application_number)
|
|
857
849
|
)
|
|
858
|
-
response_data = self.
|
|
850
|
+
response_data = self._get_model(
|
|
859
851
|
method="GET", endpoint=endpoint, response_class=PatentDataResponse
|
|
860
852
|
)
|
|
861
|
-
assert isinstance(response_data, PatentDataResponse)
|
|
862
853
|
wrapper = self._get_wrapper_from_response(response_data, application_number)
|
|
863
854
|
return PrintedPublication.from_wrapper(wrapper) if wrapper else None
|
|
864
855
|
|
|
@@ -935,11 +926,12 @@ class PatentDataClient(BaseUSPTOClient[PatentDataResponse]):
|
|
|
935
926
|
status codes, a `StatusCodeCollection` of the `StatusCode`
|
|
936
927
|
objects (code and description), and a request identifier.
|
|
937
928
|
"""
|
|
938
|
-
|
|
939
|
-
method="GET",
|
|
929
|
+
return self._get_model(
|
|
930
|
+
method="GET",
|
|
931
|
+
endpoint=self.ENDPOINTS["status_codes"],
|
|
932
|
+
response_class=StatusCodeSearchResponse,
|
|
933
|
+
params=params,
|
|
940
934
|
)
|
|
941
|
-
assert isinstance(result_dict, dict)
|
|
942
|
-
return StatusCodeSearchResponse.from_dict(result_dict)
|
|
943
935
|
|
|
944
936
|
def search_status_codes(
|
|
945
937
|
self, search_request: dict[str, Any]
|
|
@@ -962,13 +954,12 @@ class PatentDataClient(BaseUSPTOClient[PatentDataResponse]):
|
|
|
962
954
|
status codes, a `StatusCodeCollection` of the `StatusCode`
|
|
963
955
|
objects (code and description), and a request identifier.
|
|
964
956
|
"""
|
|
965
|
-
|
|
957
|
+
return self._get_model(
|
|
966
958
|
method="POST",
|
|
967
959
|
endpoint=self.ENDPOINTS["status_codes"],
|
|
960
|
+
response_class=StatusCodeSearchResponse,
|
|
968
961
|
json_data=search_request,
|
|
969
962
|
)
|
|
970
|
-
assert isinstance(result_dict, dict)
|
|
971
|
-
return StatusCodeSearchResponse.from_dict(result_dict)
|
|
972
963
|
|
|
973
964
|
def download_document(
|
|
974
965
|
self,
|
|
@@ -184,12 +184,12 @@ class FinalPetitionDecisionsClient(BaseUSPTOClient[PetitionDecisionResponse]):
|
|
|
184
184
|
|
|
185
185
|
if post_body is not None:
|
|
186
186
|
# POST request path
|
|
187
|
-
result = self.
|
|
187
|
+
result = self._get_model(
|
|
188
188
|
method="POST",
|
|
189
189
|
endpoint=endpoint,
|
|
190
|
+
response_class=PetitionDecisionResponse,
|
|
190
191
|
json_data=post_body,
|
|
191
192
|
params=additional_query_params,
|
|
192
|
-
response_class=PetitionDecisionResponse,
|
|
193
193
|
)
|
|
194
194
|
else:
|
|
195
195
|
# GET request path
|
|
@@ -280,14 +280,12 @@ class FinalPetitionDecisionsClient(BaseUSPTOClient[PetitionDecisionResponse]):
|
|
|
280
280
|
if additional_query_params:
|
|
281
281
|
params.update(additional_query_params)
|
|
282
282
|
|
|
283
|
-
result = self.
|
|
283
|
+
result = self._get_model(
|
|
284
284
|
method="GET",
|
|
285
285
|
endpoint=endpoint,
|
|
286
|
-
params=params,
|
|
287
286
|
response_class=PetitionDecisionResponse,
|
|
287
|
+
params=params,
|
|
288
288
|
)
|
|
289
|
-
|
|
290
|
-
assert isinstance(result, PetitionDecisionResponse)
|
|
291
289
|
return result
|
|
292
290
|
|
|
293
291
|
def get_decision_by_id(
|
|
@@ -326,13 +324,12 @@ class FinalPetitionDecisionsClient(BaseUSPTOClient[PetitionDecisionResponse]):
|
|
|
326
324
|
if include_documents is not None:
|
|
327
325
|
params["includeDocuments"] = str(include_documents).lower()
|
|
328
326
|
|
|
329
|
-
response_data = self.
|
|
327
|
+
response_data = self._get_model(
|
|
330
328
|
method="GET",
|
|
331
329
|
endpoint=endpoint,
|
|
332
|
-
params=params if params else None,
|
|
333
330
|
response_class=PetitionDecisionResponse,
|
|
331
|
+
params=params if params else None,
|
|
334
332
|
)
|
|
335
|
-
assert isinstance(response_data, PetitionDecisionResponse)
|
|
336
333
|
return self._get_decision_from_response(
|
|
337
334
|
response_data=response_data,
|
|
338
335
|
petition_decision_record_identifier_for_validation=petition_decision_record_identifier,
|
|
@@ -480,17 +477,17 @@ class FinalPetitionDecisionsClient(BaseUSPTOClient[PetitionDecisionResponse]):
|
|
|
480
477
|
|
|
481
478
|
if format.lower() == "json":
|
|
482
479
|
# For JSON, parse the response
|
|
483
|
-
|
|
484
|
-
method="GET",
|
|
480
|
+
return self._get_model(
|
|
481
|
+
method="GET",
|
|
482
|
+
endpoint=endpoint,
|
|
483
|
+
response_class=PetitionDecisionDownloadResponse,
|
|
484
|
+
params=params,
|
|
485
485
|
)
|
|
486
|
-
assert isinstance(result_dict, dict)
|
|
487
|
-
return PetitionDecisionDownloadResponse.from_dict(result_dict)
|
|
488
486
|
else:
|
|
489
487
|
# For CSV or other formats, get streaming response
|
|
490
|
-
result = self.
|
|
491
|
-
method="GET", endpoint=endpoint, params=params
|
|
488
|
+
result = self._stream_request(
|
|
489
|
+
method="GET", endpoint=endpoint, params=params
|
|
492
490
|
)
|
|
493
|
-
assert isinstance(result, requests.Response)
|
|
494
491
|
|
|
495
492
|
if destination is not None:
|
|
496
493
|
# Save to file using the base class helper
|