pyUSPTO 0.4.2__tar.gz → 0.4.3__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.2 → pyuspto-0.4.3}/.github/workflows/python-tests.yml +3 -4
- {pyuspto-0.4.2 → pyuspto-0.4.3}/CONTRIBUTING.md +2 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/PKG-INFO +20 -13
- {pyuspto-0.4.2 → pyuspto-0.4.3}/examples/ifw_example.py +40 -2
- {pyuspto-0.4.2 → pyuspto-0.4.3}/examples/patent_data_example.py +22 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/pyproject.toml +9 -16
- {pyuspto-0.4.2 → pyuspto-0.4.3}/requirements-dev.txt +29 -19
- {pyuspto-0.4.2 → pyuspto-0.4.3}/src/pyUSPTO/clients/patent_data.py +170 -14
- {pyuspto-0.4.2 → pyuspto-0.4.3}/src/pyUSPTO/models/patent_data.py +21 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/src/pyUSPTO.egg-info/PKG-INFO +20 -13
- {pyuspto-0.4.2 → pyuspto-0.4.3}/src/pyUSPTO.egg-info/requires.txt +18 -8
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tests/clients/test_patent_data_clients.py +416 -43
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tox.ini +2 -4
- {pyuspto-0.4.2 → pyuspto-0.4.3}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/.github/ISSUE_TEMPLATE/documentation-updates.yml +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/.github/dependabot.yml +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/.github/workflows/publish-docs.yaml +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/.github/workflows/publish-to-test-pypi.yml +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/.gitignore +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/.readthedocs.yaml +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/ADVANCED.md +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/CHANGELOG.md +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/CODE_OF_CONDUCT.md +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/LICENSE +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/README.md +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/_version.pyi +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/Makefile +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/_static/.gitkeep +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/advanced.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/api/clients/bulk_data.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/api/clients/index.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/api/clients/patent_data.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/api/clients/petition_decisions.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/api/clients/ptab_appeals.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/api/clients/ptab_interferences.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/api/clients/ptab_trials.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/api/config/config.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/api/config/http_config.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/api/config/index.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/api/exceptions.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/api/index.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/api/models/base.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/api/models/bulk_data.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/api/models/index.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/api/models/patent_data.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/api/models/petition_decisions.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/api/models/ptab.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/api/models/utils.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/api/warnings.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/conf.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/configuration.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/development.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/examples/bulk_data.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/examples/ifw_example.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/examples/index.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/examples/patent_data.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/examples/petition_decisions.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/examples/ptab_appeals.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/examples/ptab_interferences.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/examples/ptab_trials.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/index.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/installation.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/docs/source/quickstart.rst +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/examples/bulk_data_example.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/examples/error_handling_example.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/examples/petition_decisions_example.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/examples/ptab_appeals_example.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/examples/ptab_interferences_example.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/examples/ptab_trials_example.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/pyUSPTO.code-workspace +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/requirements.txt +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/setup.cfg +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/src/pyUSPTO/__init__.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/src/pyUSPTO/clients/__init__.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/src/pyUSPTO/clients/base.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/src/pyUSPTO/clients/bulk_data.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/src/pyUSPTO/clients/petition_decisions.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/src/pyUSPTO/clients/ptab_appeals.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/src/pyUSPTO/clients/ptab_interferences.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/src/pyUSPTO/clients/ptab_trials.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/src/pyUSPTO/config.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/src/pyUSPTO/exceptions.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/src/pyUSPTO/http_config.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/src/pyUSPTO/models/__init__.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/src/pyUSPTO/models/base.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/src/pyUSPTO/models/bulk_data.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/src/pyUSPTO/models/petition_decisions.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/src/pyUSPTO/models/ptab.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/src/pyUSPTO/models/utils.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/src/pyUSPTO/py.typed +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/src/pyUSPTO/utils/__init__.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/src/pyUSPTO/warnings.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/src/pyUSPTO.egg-info/SOURCES.txt +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/src/pyUSPTO.egg-info/dependency_links.txt +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/src/pyUSPTO.egg-info/top_level.txt +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/src/pyUSPTO.egg-info/zip-safe +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tests/__init__.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tests/clients/__init__.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tests/clients/test_base.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tests/clients/test_bulk_data_clients.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tests/clients/test_petition_decision_clients.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tests/clients/test_ptab_appeals_client.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tests/clients/test_ptab_interferences_client.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tests/clients/test_ptab_trials_client.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tests/conftest.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tests/integration/__init__.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tests/integration/conftest.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tests/integration/test_bulk_data_integration.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tests/integration/test_patent_data_integration.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tests/integration/test_petition_decisions_integration.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tests/integration/test_ptab_appeals_integration.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tests/integration/test_ptab_interferences_integration.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tests/integration/test_ptab_trials_integration.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tests/models/__init__.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tests/models/test_bulk_data_models.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tests/models/test_patent_data_models.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tests/models/test_petition_decision_models.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tests/models/test_ptab_models.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tests/models/test_utils.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tests/test_config.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tests/test_edge_cases.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tests/test_exceptions.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tests/test_http_config.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tests/test_init.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tests/test_version.py +0 -0
- {pyuspto-0.4.2 → pyuspto-0.4.3}/tests/utils/__init__.py +0 -0
|
@@ -25,8 +25,7 @@ jobs:
|
|
|
25
25
|
run: |
|
|
26
26
|
python -m pip install --upgrade pip
|
|
27
27
|
pip install -r requirements.txt
|
|
28
|
-
pip install -
|
|
29
|
-
pip install -e .
|
|
28
|
+
pip install -e ".[test]"
|
|
30
29
|
|
|
31
30
|
- name: Run tests
|
|
32
31
|
run: |
|
|
@@ -46,12 +45,12 @@ jobs:
|
|
|
46
45
|
- name: Set up Python
|
|
47
46
|
uses: actions/setup-python@v6
|
|
48
47
|
with:
|
|
49
|
-
python-version: "3.
|
|
48
|
+
python-version: "3.14"
|
|
50
49
|
|
|
51
50
|
- name: Install dependencies
|
|
52
51
|
run: |
|
|
53
52
|
python -m pip install --upgrade pip
|
|
54
|
-
pip install
|
|
53
|
+
pip install -e ".[lint]"
|
|
55
54
|
|
|
56
55
|
- name: Lint with ruff
|
|
57
56
|
run: |
|
|
@@ -22,6 +22,8 @@ cd pyUSPTO
|
|
|
22
22
|
|
|
23
23
|
### Set Up Development Environment
|
|
24
24
|
|
|
25
|
+
> **Note:** Python 3.11+ is required for the development environment. Some dev dependencies (e.g. `myst-parser`) do not support Python 3.10. The package itself supports Python 3.10+.
|
|
26
|
+
|
|
25
27
|
```bash
|
|
26
28
|
# Create and activate a virtual environment
|
|
27
29
|
python -m venv venv
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyUSPTO
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.3
|
|
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
|
|
@@ -43,19 +43,26 @@ License-File: LICENSE
|
|
|
43
43
|
Requires-Dist: requests>=2.32.5
|
|
44
44
|
Requires-Dist: typing-extensions>=4.15.0; python_version < "3.11"
|
|
45
45
|
Requires-Dist: tzdata>=2025.3
|
|
46
|
+
Provides-Extra: test
|
|
47
|
+
Requires-Dist: pytest>=9.0.2; extra == "test"
|
|
48
|
+
Requires-Dist: pytest-cov>=7.0.0; extra == "test"
|
|
49
|
+
Requires-Dist: pytest-mock>=3.15.1; extra == "test"
|
|
50
|
+
Requires-Dist: typing_extensions>=4.15.0; extra == "test"
|
|
51
|
+
Provides-Extra: docs
|
|
52
|
+
Requires-Dist: sphinx>=9.1.0; extra == "docs"
|
|
53
|
+
Requires-Dist: sphinx-rtd-theme>=3.1.0; extra == "docs"
|
|
54
|
+
Requires-Dist: sphinx_immaterial>=0.13.9; extra == "docs"
|
|
55
|
+
Requires-Dist: sphinx-autodoc-typehints>=3.9.5; extra == "docs"
|
|
56
|
+
Requires-Dist: sphinx-copybutton>=0.5.2; extra == "docs"
|
|
57
|
+
Requires-Dist: myst-parser>=5.0.0; extra == "docs"
|
|
58
|
+
Provides-Extra: lint
|
|
59
|
+
Requires-Dist: mypy>=1.19.0; extra == "lint"
|
|
60
|
+
Requires-Dist: types-requests>=2.32.4; extra == "lint"
|
|
61
|
+
Requires-Dist: ruff>=0.15.0; extra == "lint"
|
|
46
62
|
Provides-Extra: dev
|
|
47
|
-
Requires-Dist:
|
|
48
|
-
Requires-Dist:
|
|
49
|
-
Requires-Dist:
|
|
50
|
-
Requires-Dist: sphinx==8.1.3; extra == "dev"
|
|
51
|
-
Requires-Dist: sphinx-rtd-theme>=3.0.0; extra == "dev"
|
|
52
|
-
Requires-Dist: sphinx_immaterial>=0.13.8; extra == "dev"
|
|
53
|
-
Requires-Dist: sphinx-autodoc-typehints==3.0.1; extra == "dev"
|
|
54
|
-
Requires-Dist: sphinx-copybutton>=0.5.2; extra == "dev"
|
|
55
|
-
Requires-Dist: myst-parser>=4.0.1; extra == "dev"
|
|
56
|
-
Requires-Dist: mypy>=1.19.0; extra == "dev"
|
|
57
|
-
Requires-Dist: types-requests>=2.32.4; extra == "dev"
|
|
58
|
-
Requires-Dist: ruff>=0.15.0; extra == "dev"
|
|
63
|
+
Requires-Dist: pyUSPTO[test]; extra == "dev"
|
|
64
|
+
Requires-Dist: pyUSPTO[docs]; extra == "dev"
|
|
65
|
+
Requires-Dist: pyUSPTO[lint]; extra == "dev"
|
|
59
66
|
Dynamic: license-file
|
|
60
67
|
|
|
61
68
|
# pyUSPTO
|
|
@@ -1,7 +1,26 @@
|
|
|
1
1
|
"""Example usage of pyUSPTO for IFW data.
|
|
2
2
|
|
|
3
|
-
This example demonstrates how to use the PatentDataClient to
|
|
4
|
-
|
|
3
|
+
This example demonstrates how to use the PatentDataClient to retrieve Image File
|
|
4
|
+
Wrapper (IFW) data from the USPTO Patent Data API. It covers:
|
|
5
|
+
|
|
6
|
+
- get_IFW_metadata(): retrieve a PatentFileWrapper (with populated document_bag)
|
|
7
|
+
using any of the five supported identifiers:
|
|
8
|
+
- application_number
|
|
9
|
+
- patent_number
|
|
10
|
+
- publication_number
|
|
11
|
+
- PCT_app_number
|
|
12
|
+
- PCT_pub_number
|
|
13
|
+
|
|
14
|
+
- get_IFW(): retrieve metadata AND bulk-download all prosecution history documents
|
|
15
|
+
(PDF preferred, DOCX fallback; XML and formatless docs are skipped). Returns an
|
|
16
|
+
IFWResult with:
|
|
17
|
+
- wrapper: the PatentFileWrapper
|
|
18
|
+
- output_path: path to the ZIP archive (as_zip=True, default) or output directory
|
|
19
|
+
- downloaded_documents: dict mapping document_identifier -> filename, allowing
|
|
20
|
+
each Document in document_bag to be linked to its downloaded file
|
|
21
|
+
|
|
22
|
+
- download_archive() / download_publication(): download the pgpub or grant XML
|
|
23
|
+
archive from PrintedMetaData.
|
|
5
24
|
"""
|
|
6
25
|
|
|
7
26
|
import json
|
|
@@ -61,6 +80,25 @@ if pct_pub_no_ifw and pct_pub_no_ifw.application_meta_data:
|
|
|
61
80
|
print(f" - IFW Found based on PCT Pub No: {PCT_pub_number}")
|
|
62
81
|
|
|
63
82
|
|
|
83
|
+
print("\nGet IFW + download all prosecution docs as a ZIP archive -->")
|
|
84
|
+
ifw_result = client.get_IFW(application_number=application_number, destination="./download-example", overwrite=True)
|
|
85
|
+
if ifw_result:
|
|
86
|
+
print(f"Title: {ifw_result.wrapper.application_meta_data.invention_title if ifw_result.wrapper.application_meta_data else 'N/A'}")
|
|
87
|
+
print(f"Output: {ifw_result.output_path}")
|
|
88
|
+
doc_bag = ifw_result.wrapper.document_bag or []
|
|
89
|
+
print(f"Documents downloaded: {len(ifw_result.downloaded_documents)} of {len(doc_bag)}")
|
|
90
|
+
for doc in doc_bag:
|
|
91
|
+
if doc.document_identifier:
|
|
92
|
+
filename = ifw_result.downloaded_documents.get(doc.document_identifier)
|
|
93
|
+
status = f"-> {filename}" if filename else "(skipped)"
|
|
94
|
+
print(f" {doc.document_code} [{doc.document_identifier}] {status}")
|
|
95
|
+
|
|
96
|
+
print("\nGet IFW + download all prosecution docs as a directory (no ZIP) -->")
|
|
97
|
+
ifw_dir_result = client.get_IFW(application_number=application_number, destination="./download-example", overwrite=True, as_zip=False)
|
|
98
|
+
if ifw_dir_result:
|
|
99
|
+
print(f"Output directory: {ifw_dir_result.output_path}")
|
|
100
|
+
|
|
101
|
+
|
|
64
102
|
print("\nNow let's download the Patent Publication Text -->")
|
|
65
103
|
if app_no_ifw and app_no_ifw.pgpub_document_meta_data:
|
|
66
104
|
pgpub_archive = app_no_ifw.pgpub_document_meta_data
|
|
@@ -302,6 +302,28 @@ except Exception as e:
|
|
|
302
302
|
print(f"Error with POST search: {e}")
|
|
303
303
|
|
|
304
304
|
|
|
305
|
+
# Search by CPC classification code
|
|
306
|
+
# CPC codes containing spaces or slashes are automatically quoted for the Lucene query.
|
|
307
|
+
# cpc_classification_bag on ApplicationMetaData is a list[str] of all CPC codes assigned
|
|
308
|
+
# to the application, so each result may have multiple codes.
|
|
309
|
+
try:
|
|
310
|
+
print("\nSearching by CPC classification code 'H10D 64/667'...")
|
|
311
|
+
cpc_response = client.search_applications(
|
|
312
|
+
classification_q="H10D 64/667", limit=3
|
|
313
|
+
)
|
|
314
|
+
print(f"Found {cpc_response.count} applications with CPC code H10D 64/667.")
|
|
315
|
+
for patent_wrapper in cpc_response.patent_file_wrapper_data_bag:
|
|
316
|
+
app_meta = patent_wrapper.application_meta_data
|
|
317
|
+
if app_meta:
|
|
318
|
+
print(
|
|
319
|
+
f" - App No: {patent_wrapper.application_number_text}, Title: {app_meta.invention_title}"
|
|
320
|
+
)
|
|
321
|
+
if app_meta.cpc_classification_bag:
|
|
322
|
+
print(f" CPC codes: {', '.join(app_meta.cpc_classification_bag)}")
|
|
323
|
+
except Exception as e:
|
|
324
|
+
print(f"Error searching by CPC classification: {e}")
|
|
325
|
+
|
|
326
|
+
|
|
305
327
|
# Example of getting status codes
|
|
306
328
|
try:
|
|
307
329
|
print("\nGetting first 5 status codes...")
|
|
@@ -51,24 +51,17 @@ dependencies = [
|
|
|
51
51
|
dynamic = ["version"]
|
|
52
52
|
|
|
53
53
|
[project.optional-dependencies]
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
|
|
60
|
-
"sphinx==8.1.3",
|
|
61
|
-
"sphinx-rtd-theme>=3.0.0",
|
|
62
|
-
"sphinx_immaterial>=0.13.8",
|
|
63
|
-
"sphinx-autodoc-typehints==3.0.1",
|
|
54
|
+
test = ["pytest>=9.0.2", "pytest-cov>=7.0.0", "pytest-mock>=3.15.1", "typing_extensions>=4.15.0"]
|
|
55
|
+
docs = [
|
|
56
|
+
"sphinx>=9.1.0",
|
|
57
|
+
"sphinx-rtd-theme>=3.1.0",
|
|
58
|
+
"sphinx_immaterial>=0.13.9",
|
|
59
|
+
"sphinx-autodoc-typehints>=3.9.5",
|
|
64
60
|
"sphinx-copybutton>=0.5.2",
|
|
65
|
-
"myst-parser>=
|
|
66
|
-
# Type checking
|
|
67
|
-
"mypy>=1.19.0",
|
|
68
|
-
"types-requests>=2.32.4",
|
|
69
|
-
# Code quality and formatting
|
|
70
|
-
"ruff>=0.15.0",
|
|
61
|
+
"myst-parser>=5.0.0",
|
|
71
62
|
]
|
|
63
|
+
lint = ["mypy>=1.19.0", "types-requests>=2.32.4", "ruff>=0.15.0"]
|
|
64
|
+
dev = ["pyUSPTO[test]", "pyUSPTO[docs]", "pyUSPTO[lint]"]
|
|
72
65
|
|
|
73
66
|
[project.urls]
|
|
74
67
|
GitHub = "https://github.com/DunlapCoddingPC/pyUSPTO"
|
|
@@ -22,14 +22,14 @@ colorama==0.4.6
|
|
|
22
22
|
# sphinx
|
|
23
23
|
coverage==7.13.4
|
|
24
24
|
# via pytest-cov
|
|
25
|
-
docutils==0.
|
|
25
|
+
docutils==0.22.4
|
|
26
26
|
# via
|
|
27
27
|
# myst-parser
|
|
28
28
|
# sphinx
|
|
29
29
|
# sphinx-rtd-theme
|
|
30
30
|
idna==3.11
|
|
31
31
|
# via requests
|
|
32
|
-
imagesize==
|
|
32
|
+
imagesize==2.0.0
|
|
33
33
|
# via sphinx
|
|
34
34
|
iniconfig==2.3.0
|
|
35
35
|
# via pytest
|
|
@@ -39,7 +39,7 @@ jinja2==3.1.6
|
|
|
39
39
|
# sphinx
|
|
40
40
|
librt==0.8.1
|
|
41
41
|
# via mypy
|
|
42
|
-
markdown-it-py==
|
|
42
|
+
markdown-it-py==4.0.0
|
|
43
43
|
# via
|
|
44
44
|
# mdit-py-plugins
|
|
45
45
|
# myst-parser
|
|
@@ -52,11 +52,11 @@ mdit-py-plugins==0.5.0
|
|
|
52
52
|
mdurl==0.1.2
|
|
53
53
|
# via markdown-it-py
|
|
54
54
|
mypy==1.19.1
|
|
55
|
-
# via
|
|
55
|
+
# via pyuspto
|
|
56
56
|
mypy-extensions==1.1.0
|
|
57
57
|
# via mypy
|
|
58
|
-
myst-parser==
|
|
59
|
-
# via
|
|
58
|
+
myst-parser==5.0.0
|
|
59
|
+
# via pyuspto
|
|
60
60
|
packaging==26.0
|
|
61
61
|
# via
|
|
62
62
|
# pytest
|
|
@@ -81,41 +81,48 @@ pygments==2.19.2
|
|
|
81
81
|
# sphinx
|
|
82
82
|
pytest==9.0.2
|
|
83
83
|
# via
|
|
84
|
-
# pyUSPTO (pyproject.toml)
|
|
85
84
|
# pytest-cov
|
|
86
85
|
# pytest-mock
|
|
86
|
+
# pyuspto
|
|
87
87
|
pytest-cov==7.0.0
|
|
88
|
-
# via
|
|
88
|
+
# via pyuspto
|
|
89
89
|
pytest-mock==3.15.1
|
|
90
|
-
# via
|
|
90
|
+
# via pyuspto
|
|
91
|
+
pyuspto @ file:///C:/Users/andrewp/Documents/GitHub/pyUSPTO
|
|
92
|
+
# via
|
|
93
|
+
# pyUSPTO (pyproject.toml)
|
|
94
|
+
# pyuspto
|
|
91
95
|
pyyaml==6.0.3
|
|
92
96
|
# via myst-parser
|
|
93
97
|
requests==2.32.5
|
|
94
98
|
# via
|
|
95
99
|
# pyUSPTO (pyproject.toml)
|
|
100
|
+
# pyuspto
|
|
96
101
|
# sphinx
|
|
97
102
|
# sphinx-immaterial
|
|
103
|
+
roman-numerals==4.1.0
|
|
104
|
+
# via sphinx
|
|
98
105
|
ruff==0.15.4
|
|
99
|
-
# via
|
|
106
|
+
# via pyuspto
|
|
100
107
|
snowballstemmer==3.0.1
|
|
101
108
|
# via sphinx
|
|
102
|
-
sphinx==
|
|
109
|
+
sphinx==9.1.0
|
|
103
110
|
# via
|
|
104
111
|
# myst-parser
|
|
105
|
-
#
|
|
112
|
+
# pyuspto
|
|
106
113
|
# sphinx-autodoc-typehints
|
|
107
114
|
# sphinx-copybutton
|
|
108
115
|
# sphinx-immaterial
|
|
109
116
|
# sphinx-rtd-theme
|
|
110
117
|
# sphinxcontrib-jquery
|
|
111
|
-
sphinx-autodoc-typehints==3.
|
|
112
|
-
# via
|
|
118
|
+
sphinx-autodoc-typehints==3.9.6
|
|
119
|
+
# via pyuspto
|
|
113
120
|
sphinx-copybutton==0.5.2
|
|
114
|
-
# via
|
|
121
|
+
# via pyuspto
|
|
115
122
|
sphinx-immaterial==0.13.9
|
|
116
|
-
# via
|
|
123
|
+
# via pyuspto
|
|
117
124
|
sphinx-rtd-theme==3.1.0
|
|
118
|
-
# via
|
|
125
|
+
# via pyuspto
|
|
119
126
|
sphinxcontrib-applehelp==2.0.0
|
|
120
127
|
# via sphinx
|
|
121
128
|
sphinxcontrib-devhelp==2.0.0
|
|
@@ -131,19 +138,22 @@ sphinxcontrib-qthelp==2.0.0
|
|
|
131
138
|
sphinxcontrib-serializinghtml==2.0.0
|
|
132
139
|
# via sphinx
|
|
133
140
|
types-requests==2.32.4.20260107
|
|
134
|
-
# via
|
|
141
|
+
# via pyuspto
|
|
135
142
|
typing-extensions==4.15.0
|
|
136
143
|
# via
|
|
137
144
|
# mypy
|
|
138
145
|
# pydantic
|
|
139
146
|
# pydantic-core
|
|
140
147
|
# pydantic-extra-types
|
|
148
|
+
# pyuspto
|
|
141
149
|
# sphinx-immaterial
|
|
142
150
|
# typing-inspection
|
|
143
151
|
typing-inspection==0.4.2
|
|
144
152
|
# via pydantic
|
|
145
153
|
tzdata==2025.3
|
|
146
|
-
# via
|
|
154
|
+
# via
|
|
155
|
+
# pyUSPTO (pyproject.toml)
|
|
156
|
+
# pyuspto
|
|
147
157
|
urllib3==2.6.3
|
|
148
158
|
# via
|
|
149
159
|
# requests
|
|
@@ -4,7 +4,11 @@ This module provides a client for interacting with the USPTO Patent Data API.
|
|
|
4
4
|
It allows you to search for and retrieve patent application data.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
import dataclasses
|
|
8
|
+
import os
|
|
9
|
+
import tempfile
|
|
7
10
|
import warnings
|
|
11
|
+
import zipfile
|
|
8
12
|
from collections.abc import Iterator
|
|
9
13
|
from typing import Any
|
|
10
14
|
|
|
@@ -20,6 +24,7 @@ from pyUSPTO.models.patent_data import (
|
|
|
20
24
|
DocumentMimeType,
|
|
21
25
|
EventData,
|
|
22
26
|
ForeignPriority,
|
|
27
|
+
IFWResult,
|
|
23
28
|
PatentDataResponse,
|
|
24
29
|
PatentFileWrapper,
|
|
25
30
|
PatentTermAdjustmentData,
|
|
@@ -121,6 +126,9 @@ class PatentDataClient(BaseUSPTOClient[PatentDataResponse]):
|
|
|
121
126
|
# Example: "PCT/US2024/012345" -> "PCTUS2412345"
|
|
122
127
|
if raw.startswith("PCT"):
|
|
123
128
|
parts = raw.split("/")
|
|
129
|
+
if len(parts) == 1:
|
|
130
|
+
# Already sanitized (e.g. "PCTUS0812705"), return as-is
|
|
131
|
+
return raw
|
|
124
132
|
if len(parts) != 3:
|
|
125
133
|
raise ValueError(
|
|
126
134
|
f"Invalid PCT application format: {input_number}. "
|
|
@@ -298,9 +306,12 @@ class PatentDataClient(BaseUSPTOClient[PatentDataResponse]):
|
|
|
298
306
|
)
|
|
299
307
|
q_parts.append(f"assignmentBag.assigneeBag.assigneeNameText:{v}")
|
|
300
308
|
if classification_q:
|
|
301
|
-
|
|
302
|
-
f"
|
|
309
|
+
v = (
|
|
310
|
+
f'"{classification_q}"'
|
|
311
|
+
if any(c in classification_q for c in [" ", "/"])
|
|
312
|
+
else classification_q
|
|
303
313
|
)
|
|
314
|
+
q_parts.append(f"applicationMetaData.cpcClassificationBag:{v}")
|
|
304
315
|
if earliestPublicationNumber_q:
|
|
305
316
|
q_parts.append(
|
|
306
317
|
f"applicationMetaData.earliestPublicationNumber:{earliestPublicationNumber_q}"
|
|
@@ -439,9 +450,12 @@ class PatentDataClient(BaseUSPTOClient[PatentDataResponse]):
|
|
|
439
450
|
)
|
|
440
451
|
q_parts.append(f"assignmentBag.assigneeBag.assigneeNameText:{v}")
|
|
441
452
|
if classification_q:
|
|
442
|
-
|
|
443
|
-
f"
|
|
453
|
+
v = (
|
|
454
|
+
f'"{classification_q}"'
|
|
455
|
+
if any(c in classification_q for c in [" ", "/"])
|
|
456
|
+
else classification_q
|
|
444
457
|
)
|
|
458
|
+
q_parts.append(f"applicationMetaData.cpcClassificationBag:{v}")
|
|
445
459
|
|
|
446
460
|
if filing_date_from_q and filing_date_to_q:
|
|
447
461
|
q_parts.append(
|
|
@@ -1058,27 +1072,169 @@ class PatentDataClient(BaseUSPTOClient[PatentDataResponse]):
|
|
|
1058
1072
|
comprehensive data if found using one of the identifiers,
|
|
1059
1073
|
otherwise None.
|
|
1060
1074
|
"""
|
|
1075
|
+
wrapper = None
|
|
1061
1076
|
if application_number:
|
|
1062
|
-
|
|
1063
|
-
|
|
1077
|
+
wrapper = self.get_application_by_number(
|
|
1078
|
+
application_number=application_number
|
|
1079
|
+
)
|
|
1080
|
+
elif patent_number:
|
|
1064
1081
|
pdr = self.search_applications(patent_number_q=patent_number, limit=1)
|
|
1065
1082
|
if pdr.patent_file_wrapper_data_bag:
|
|
1066
|
-
|
|
1067
|
-
|
|
1083
|
+
wrapper = pdr.patent_file_wrapper_data_bag[0]
|
|
1084
|
+
elif publication_number:
|
|
1068
1085
|
pdr = self.search_applications(
|
|
1069
1086
|
earliestPublicationNumber_q=publication_number, limit=1
|
|
1070
1087
|
)
|
|
1071
1088
|
if pdr.patent_file_wrapper_data_bag:
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1089
|
+
wrapper = pdr.patent_file_wrapper_data_bag[0]
|
|
1090
|
+
elif PCT_app_number:
|
|
1091
|
+
wrapper = self.get_application_by_number(application_number=PCT_app_number)
|
|
1092
|
+
elif PCT_pub_number:
|
|
1076
1093
|
pdr = self.search_applications(
|
|
1077
1094
|
pctPublicationNumber_q=PCT_pub_number, limit=1
|
|
1078
1095
|
)
|
|
1079
1096
|
if pdr.patent_file_wrapper_data_bag:
|
|
1080
|
-
|
|
1081
|
-
|
|
1097
|
+
wrapper = pdr.patent_file_wrapper_data_bag[0]
|
|
1098
|
+
if wrapper is None:
|
|
1099
|
+
return None
|
|
1100
|
+
doc_bag = self.get_application_documents(wrapper.application_number_text)
|
|
1101
|
+
return dataclasses.replace(wrapper, document_bag=doc_bag)
|
|
1102
|
+
|
|
1103
|
+
def get_IFW(
|
|
1104
|
+
self,
|
|
1105
|
+
*,
|
|
1106
|
+
application_number: str | None = None,
|
|
1107
|
+
publication_number: str | None = None,
|
|
1108
|
+
patent_number: str | None = None,
|
|
1109
|
+
PCT_app_number: str | None = None,
|
|
1110
|
+
PCT_pub_number: str | None = None,
|
|
1111
|
+
destination: str | None = None,
|
|
1112
|
+
overwrite: bool = False,
|
|
1113
|
+
as_zip: bool = True,
|
|
1114
|
+
) -> IFWResult | None:
|
|
1115
|
+
"""Retrieve IFW metadata and download all prosecution documents.
|
|
1116
|
+
|
|
1117
|
+
Combines `get_IFW_metadata` with a bulk download of all available prosecution
|
|
1118
|
+
history documents (PDF preferred, DOCX fallback). Documents with no downloadable
|
|
1119
|
+
format (e.g., NPL references) are silently skipped. A warning is issued only
|
|
1120
|
+
if a document has a download URL but the download itself fails.
|
|
1121
|
+
|
|
1122
|
+
Args:
|
|
1123
|
+
application_number: USPTO application number (e.g., "16123456").
|
|
1124
|
+
publication_number: USPTO pre-grant publication number.
|
|
1125
|
+
patent_number: USPTO patent number.
|
|
1126
|
+
PCT_app_number: PCT application number.
|
|
1127
|
+
PCT_pub_number: PCT publication number.
|
|
1128
|
+
destination: Directory for output. Defaults to current directory.
|
|
1129
|
+
overwrite: Whether to overwrite an existing output. Default False.
|
|
1130
|
+
as_zip: If True (default), package all downloads into a ZIP archive
|
|
1131
|
+
at ``{destination}/{app_no}_ifw.zip``. If False, download files
|
|
1132
|
+
directly into ``{destination}/{app_no}_ifw/``.
|
|
1133
|
+
|
|
1134
|
+
Returns:
|
|
1135
|
+
IFWResult with the PatentFileWrapper, the output path, and a mapping
|
|
1136
|
+
of document_identifier to filename for each downloaded document.
|
|
1137
|
+
Returns None if no application was found.
|
|
1138
|
+
|
|
1139
|
+
Raises:
|
|
1140
|
+
FileExistsError: If the output path already exists and overwrite=False.
|
|
1141
|
+
"""
|
|
1142
|
+
wrapper = self.get_IFW_metadata(
|
|
1143
|
+
application_number=application_number,
|
|
1144
|
+
publication_number=publication_number,
|
|
1145
|
+
patent_number=patent_number,
|
|
1146
|
+
PCT_app_number=PCT_app_number,
|
|
1147
|
+
PCT_pub_number=PCT_pub_number,
|
|
1148
|
+
)
|
|
1149
|
+
if wrapper is None:
|
|
1150
|
+
return None
|
|
1151
|
+
|
|
1152
|
+
dest_dir = destination or "."
|
|
1153
|
+
app_no = wrapper.application_number_text or "unknown"
|
|
1154
|
+
downloaded_documents: dict[str, str] = {}
|
|
1155
|
+
|
|
1156
|
+
if as_zip:
|
|
1157
|
+
output_path = os.path.join(dest_dir, f"{app_no}_ifw.zip")
|
|
1158
|
+
if os.path.exists(output_path) and not overwrite:
|
|
1159
|
+
raise FileExistsError(
|
|
1160
|
+
f"ZIP archive already exists: {output_path}. Use overwrite=True to replace."
|
|
1161
|
+
)
|
|
1162
|
+
os.makedirs(dest_dir, exist_ok=True)
|
|
1163
|
+
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
1164
|
+
with zipfile.ZipFile(
|
|
1165
|
+
output_path, "w", compression=zipfile.ZIP_DEFLATED
|
|
1166
|
+
) as zf:
|
|
1167
|
+
for doc in wrapper.document_bag or []:
|
|
1168
|
+
if not doc.document_identifier:
|
|
1169
|
+
continue
|
|
1170
|
+
fmt_obj = next(
|
|
1171
|
+
(
|
|
1172
|
+
f
|
|
1173
|
+
for f in doc.document_formats
|
|
1174
|
+
if f.mime_type_identifier in ("PDF", "MS_WORD")
|
|
1175
|
+
and f.download_url
|
|
1176
|
+
),
|
|
1177
|
+
None,
|
|
1178
|
+
)
|
|
1179
|
+
if fmt_obj is None or not fmt_obj.download_url:
|
|
1180
|
+
continue
|
|
1181
|
+
try:
|
|
1182
|
+
downloaded = self._download_and_extract(
|
|
1183
|
+
url=fmt_obj.download_url,
|
|
1184
|
+
destination=tmp_dir,
|
|
1185
|
+
overwrite=True,
|
|
1186
|
+
)
|
|
1187
|
+
arcname = os.path.basename(downloaded)
|
|
1188
|
+
zf.write(downloaded, arcname=arcname)
|
|
1189
|
+
downloaded_documents[doc.document_identifier] = arcname
|
|
1190
|
+
except Exception as exc:
|
|
1191
|
+
warnings.warn(
|
|
1192
|
+
f"Failed to download document {doc.document_identifier} "
|
|
1193
|
+
f"({doc.document_code}): {exc}",
|
|
1194
|
+
stacklevel=2,
|
|
1195
|
+
)
|
|
1196
|
+
else:
|
|
1197
|
+
output_path = os.path.join(dest_dir, f"{app_no}_ifw")
|
|
1198
|
+
if os.path.exists(output_path) and not overwrite:
|
|
1199
|
+
raise FileExistsError(
|
|
1200
|
+
f"Output directory already exists: {output_path}. Use overwrite=True to replace."
|
|
1201
|
+
)
|
|
1202
|
+
os.makedirs(output_path, exist_ok=True)
|
|
1203
|
+
for doc in wrapper.document_bag or []:
|
|
1204
|
+
if not doc.document_identifier:
|
|
1205
|
+
continue
|
|
1206
|
+
fmt_obj = next(
|
|
1207
|
+
(
|
|
1208
|
+
f
|
|
1209
|
+
for f in doc.document_formats
|
|
1210
|
+
if f.mime_type_identifier in ("PDF", "MS_WORD")
|
|
1211
|
+
and f.download_url
|
|
1212
|
+
),
|
|
1213
|
+
None,
|
|
1214
|
+
)
|
|
1215
|
+
if fmt_obj is None or not fmt_obj.download_url:
|
|
1216
|
+
continue
|
|
1217
|
+
try:
|
|
1218
|
+
downloaded = self._download_and_extract(
|
|
1219
|
+
url=fmt_obj.download_url,
|
|
1220
|
+
destination=output_path,
|
|
1221
|
+
overwrite=overwrite,
|
|
1222
|
+
)
|
|
1223
|
+
downloaded_documents[doc.document_identifier] = os.path.basename(
|
|
1224
|
+
downloaded
|
|
1225
|
+
)
|
|
1226
|
+
except Exception as exc:
|
|
1227
|
+
warnings.warn(
|
|
1228
|
+
f"Failed to download document {doc.document_identifier} "
|
|
1229
|
+
f"({doc.document_code}): {exc}",
|
|
1230
|
+
stacklevel=2,
|
|
1231
|
+
)
|
|
1232
|
+
|
|
1233
|
+
return IFWResult(
|
|
1234
|
+
wrapper=wrapper,
|
|
1235
|
+
output_path=os.path.abspath(output_path),
|
|
1236
|
+
downloaded_documents=downloaded_documents,
|
|
1237
|
+
)
|
|
1082
1238
|
|
|
1083
1239
|
def download_archive(
|
|
1084
1240
|
self,
|
|
@@ -2048,6 +2048,7 @@ class PatentFileWrapper:
|
|
|
2048
2048
|
pgpub_document_meta_data: `PrintedMetaData` for Pre-Grant Publication.
|
|
2049
2049
|
grant_document_meta_data: `PrintedMetaData` for the granted patent.
|
|
2050
2050
|
last_ingestion_date_time: Timestamp of when this data was last ingested by the API (UTC).
|
|
2051
|
+
document_bag: `DocumentBag` containing associated documents and their metadata.
|
|
2051
2052
|
"""
|
|
2052
2053
|
|
|
2053
2054
|
application_number_text: str
|
|
@@ -2063,6 +2064,7 @@ class PatentFileWrapper:
|
|
|
2063
2064
|
pgpub_document_meta_data: PrintedMetaData | None = None
|
|
2064
2065
|
grant_document_meta_data: PrintedMetaData | None = None
|
|
2065
2066
|
last_ingestion_date_time: datetime | None = None
|
|
2067
|
+
document_bag: DocumentBag | None = None
|
|
2066
2068
|
|
|
2067
2069
|
@classmethod
|
|
2068
2070
|
def from_dict(
|
|
@@ -2209,6 +2211,25 @@ class PatentFileWrapper:
|
|
|
2209
2211
|
}
|
|
2210
2212
|
|
|
2211
2213
|
|
|
2214
|
+
@dataclass(frozen=True)
|
|
2215
|
+
class IFWResult:
|
|
2216
|
+
"""Result of a get_IFW call: metadata wrapper, output path, and document map.
|
|
2217
|
+
|
|
2218
|
+
Attributes:
|
|
2219
|
+
wrapper: The PatentFileWrapper containing all IFW metadata and document_bag.
|
|
2220
|
+
output_path: Absolute path to the ZIP archive (when as_zip=True) or the
|
|
2221
|
+
output directory (when as_zip=False).
|
|
2222
|
+
downloaded_documents: Maps document_identifier to the filename of the
|
|
2223
|
+
downloaded file — the arcname inside the ZIP (as_zip=True) or the
|
|
2224
|
+
basename inside the output directory (as_zip=False). Documents that
|
|
2225
|
+
were skipped (no PDF/DOCX URL) or failed to download are absent.
|
|
2226
|
+
"""
|
|
2227
|
+
|
|
2228
|
+
wrapper: PatentFileWrapper
|
|
2229
|
+
output_path: str
|
|
2230
|
+
downloaded_documents: dict[str, str]
|
|
2231
|
+
|
|
2232
|
+
|
|
2212
2233
|
@dataclass(frozen=True)
|
|
2213
2234
|
class PatentDataResponse:
|
|
2214
2235
|
"""Represents the overall response from a patent data API request.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyUSPTO
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.3
|
|
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
|
|
@@ -43,19 +43,26 @@ License-File: LICENSE
|
|
|
43
43
|
Requires-Dist: requests>=2.32.5
|
|
44
44
|
Requires-Dist: typing-extensions>=4.15.0; python_version < "3.11"
|
|
45
45
|
Requires-Dist: tzdata>=2025.3
|
|
46
|
+
Provides-Extra: test
|
|
47
|
+
Requires-Dist: pytest>=9.0.2; extra == "test"
|
|
48
|
+
Requires-Dist: pytest-cov>=7.0.0; extra == "test"
|
|
49
|
+
Requires-Dist: pytest-mock>=3.15.1; extra == "test"
|
|
50
|
+
Requires-Dist: typing_extensions>=4.15.0; extra == "test"
|
|
51
|
+
Provides-Extra: docs
|
|
52
|
+
Requires-Dist: sphinx>=9.1.0; extra == "docs"
|
|
53
|
+
Requires-Dist: sphinx-rtd-theme>=3.1.0; extra == "docs"
|
|
54
|
+
Requires-Dist: sphinx_immaterial>=0.13.9; extra == "docs"
|
|
55
|
+
Requires-Dist: sphinx-autodoc-typehints>=3.9.5; extra == "docs"
|
|
56
|
+
Requires-Dist: sphinx-copybutton>=0.5.2; extra == "docs"
|
|
57
|
+
Requires-Dist: myst-parser>=5.0.0; extra == "docs"
|
|
58
|
+
Provides-Extra: lint
|
|
59
|
+
Requires-Dist: mypy>=1.19.0; extra == "lint"
|
|
60
|
+
Requires-Dist: types-requests>=2.32.4; extra == "lint"
|
|
61
|
+
Requires-Dist: ruff>=0.15.0; extra == "lint"
|
|
46
62
|
Provides-Extra: dev
|
|
47
|
-
Requires-Dist:
|
|
48
|
-
Requires-Dist:
|
|
49
|
-
Requires-Dist:
|
|
50
|
-
Requires-Dist: sphinx==8.1.3; extra == "dev"
|
|
51
|
-
Requires-Dist: sphinx-rtd-theme>=3.0.0; extra == "dev"
|
|
52
|
-
Requires-Dist: sphinx_immaterial>=0.13.8; extra == "dev"
|
|
53
|
-
Requires-Dist: sphinx-autodoc-typehints==3.0.1; extra == "dev"
|
|
54
|
-
Requires-Dist: sphinx-copybutton>=0.5.2; extra == "dev"
|
|
55
|
-
Requires-Dist: myst-parser>=4.0.1; extra == "dev"
|
|
56
|
-
Requires-Dist: mypy>=1.19.0; extra == "dev"
|
|
57
|
-
Requires-Dist: types-requests>=2.32.4; extra == "dev"
|
|
58
|
-
Requires-Dist: ruff>=0.15.0; extra == "dev"
|
|
63
|
+
Requires-Dist: pyUSPTO[test]; extra == "dev"
|
|
64
|
+
Requires-Dist: pyUSPTO[docs]; extra == "dev"
|
|
65
|
+
Requires-Dist: pyUSPTO[lint]; extra == "dev"
|
|
59
66
|
Dynamic: license-file
|
|
60
67
|
|
|
61
68
|
# pyUSPTO
|