pyUSPTO 0.4.3__tar.gz → 0.4.4__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.3 → pyuspto-0.4.4}/.github/workflows/publish-to-test-pypi.yml +1 -1
- {pyuspto-0.4.3 → pyuspto-0.4.4}/PKG-INFO +4 -3
- {pyuspto-0.4.3 → pyuspto-0.4.4}/README.md +2 -1
- {pyuspto-0.4.3 → pyuspto-0.4.4}/pyproject.toml +7 -2
- {pyuspto-0.4.3 → pyuspto-0.4.4}/requirements-dev.txt +3 -3
- {pyuspto-0.4.3 → pyuspto-0.4.4}/requirements.txt +1 -1
- {pyuspto-0.4.3 → pyuspto-0.4.4}/src/pyUSPTO/clients/patent_data.py +75 -1
- {pyuspto-0.4.3 → pyuspto-0.4.4}/src/pyUSPTO.egg-info/PKG-INFO +4 -3
- {pyuspto-0.4.3 → pyuspto-0.4.4}/tests/clients/test_patent_data_clients.py +163 -3
- {pyuspto-0.4.3 → pyuspto-0.4.4}/tests/integration/test_patent_data_integration.py +49 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/.github/ISSUE_TEMPLATE/bug-report.yml +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/.github/ISSUE_TEMPLATE/documentation-updates.yml +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/.github/ISSUE_TEMPLATE/feature-request.yml +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/.github/dependabot.yml +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/.github/workflows/publish-docs.yaml +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/.github/workflows/python-tests.yml +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/.gitignore +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/.readthedocs.yaml +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/ADVANCED.md +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/CHANGELOG.md +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/CODE_OF_CONDUCT.md +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/CONTRIBUTING.md +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/LICENSE +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/_version.pyi +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/Makefile +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/_static/.gitkeep +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/advanced.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/api/clients/bulk_data.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/api/clients/index.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/api/clients/patent_data.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/api/clients/petition_decisions.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/api/clients/ptab_appeals.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/api/clients/ptab_interferences.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/api/clients/ptab_trials.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/api/config/config.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/api/config/http_config.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/api/config/index.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/api/exceptions.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/api/index.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/api/models/base.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/api/models/bulk_data.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/api/models/index.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/api/models/patent_data.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/api/models/petition_decisions.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/api/models/ptab.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/api/models/utils.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/api/warnings.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/conf.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/configuration.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/development.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/examples/bulk_data.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/examples/ifw_example.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/examples/index.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/examples/patent_data.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/examples/petition_decisions.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/examples/ptab_appeals.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/examples/ptab_interferences.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/examples/ptab_trials.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/index.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/installation.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/docs/source/quickstart.rst +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/examples/bulk_data_example.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/examples/error_handling_example.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/examples/ifw_example.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/examples/patent_data_example.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/examples/petition_decisions_example.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/examples/ptab_appeals_example.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/examples/ptab_interferences_example.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/examples/ptab_trials_example.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/pyUSPTO.code-workspace +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/setup.cfg +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/src/pyUSPTO/__init__.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/src/pyUSPTO/clients/__init__.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/src/pyUSPTO/clients/base.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/src/pyUSPTO/clients/bulk_data.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/src/pyUSPTO/clients/petition_decisions.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/src/pyUSPTO/clients/ptab_appeals.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/src/pyUSPTO/clients/ptab_interferences.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/src/pyUSPTO/clients/ptab_trials.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/src/pyUSPTO/config.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/src/pyUSPTO/exceptions.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/src/pyUSPTO/http_config.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/src/pyUSPTO/models/__init__.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/src/pyUSPTO/models/base.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/src/pyUSPTO/models/bulk_data.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/src/pyUSPTO/models/patent_data.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/src/pyUSPTO/models/petition_decisions.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/src/pyUSPTO/models/ptab.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/src/pyUSPTO/models/utils.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/src/pyUSPTO/py.typed +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/src/pyUSPTO/utils/__init__.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/src/pyUSPTO/warnings.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/src/pyUSPTO.egg-info/SOURCES.txt +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/src/pyUSPTO.egg-info/dependency_links.txt +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/src/pyUSPTO.egg-info/requires.txt +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/src/pyUSPTO.egg-info/top_level.txt +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/src/pyUSPTO.egg-info/zip-safe +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/tests/__init__.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/tests/clients/__init__.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/tests/clients/test_base.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/tests/clients/test_bulk_data_clients.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/tests/clients/test_petition_decision_clients.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/tests/clients/test_ptab_appeals_client.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/tests/clients/test_ptab_interferences_client.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/tests/clients/test_ptab_trials_client.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/tests/conftest.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/tests/integration/__init__.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/tests/integration/conftest.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/tests/integration/test_bulk_data_integration.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/tests/integration/test_petition_decisions_integration.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/tests/integration/test_ptab_appeals_integration.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/tests/integration/test_ptab_interferences_integration.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/tests/integration/test_ptab_trials_integration.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/tests/models/__init__.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/tests/models/test_bulk_data_models.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/tests/models/test_patent_data_models.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/tests/models/test_petition_decision_models.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/tests/models/test_ptab_models.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/tests/models/test_utils.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/tests/test_config.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/tests/test_edge_cases.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/tests/test_exceptions.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/tests/test_http_config.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/tests/test_init.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/tests/test_version.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/tests/utils/__init__.py +0 -0
- {pyuspto-0.4.3 → pyuspto-0.4.4}/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.4
|
|
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
|
|
@@ -17,7 +17,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.13
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.14
|
|
19
19
|
Classifier: Operating System :: OS Independent
|
|
20
|
-
Classifier: Development Status ::
|
|
20
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
21
21
|
Classifier: Environment :: Console
|
|
22
22
|
Classifier: Intended Audience :: Developers
|
|
23
23
|
Classifier: Intended Audience :: Education
|
|
@@ -65,12 +65,13 @@ Requires-Dist: pyUSPTO[docs]; extra == "dev"
|
|
|
65
65
|
Requires-Dist: pyUSPTO[lint]; extra == "dev"
|
|
66
66
|
Dynamic: license-file
|
|
67
67
|
|
|
68
|
-
#
|
|
68
|
+
# USPTO Open Data Portal Client Library
|
|
69
69
|
|
|
70
70
|
[](https://badge.fury.io/py/pyUSPTO)
|
|
71
71
|
[](https://opensource.org/licenses/MIT)
|
|
72
72
|
[](https://www.python.org/downloads/)
|
|
73
73
|
[](https://pyuspto.readthedocs.io/en/latest/)
|
|
74
|
+
[](https://pepy.tech/projects/pyuspto)
|
|
74
75
|
|
|
75
76
|
A Python client library for interacting with the United Stated Patent and Trademark Office (USPTO) [Open Data Portal](https://data.uspto.gov/home) APIs.
|
|
76
77
|
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
#
|
|
1
|
+
# USPTO Open Data Portal Client Library
|
|
2
2
|
|
|
3
3
|
[](https://badge.fury.io/py/pyUSPTO)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
[](https://www.python.org/downloads/)
|
|
6
6
|
[](https://pyuspto.readthedocs.io/en/latest/)
|
|
7
|
+
[](https://pepy.tech/projects/pyuspto)
|
|
7
8
|
|
|
8
9
|
A Python client library for interacting with the United Stated Patent and Trademark Office (USPTO) [Open Data Portal](https://data.uspto.gov/home) APIs.
|
|
9
10
|
|
|
@@ -20,7 +20,7 @@ classifiers = [
|
|
|
20
20
|
"Programming Language :: Python :: 3.13",
|
|
21
21
|
"Programming Language :: Python :: 3.14",
|
|
22
22
|
"Operating System :: OS Independent",
|
|
23
|
-
"Development Status ::
|
|
23
|
+
"Development Status :: 5 - Production/Stable",
|
|
24
24
|
"Environment :: Console",
|
|
25
25
|
"Intended Audience :: Developers",
|
|
26
26
|
"Intended Audience :: Education",
|
|
@@ -51,7 +51,12 @@ dependencies = [
|
|
|
51
51
|
dynamic = ["version"]
|
|
52
52
|
|
|
53
53
|
[project.optional-dependencies]
|
|
54
|
-
test = [
|
|
54
|
+
test = [
|
|
55
|
+
"pytest>=9.0.2",
|
|
56
|
+
"pytest-cov>=7.0.0",
|
|
57
|
+
"pytest-mock>=3.15.1",
|
|
58
|
+
"typing_extensions>=4.15.0",
|
|
59
|
+
]
|
|
55
60
|
docs = [
|
|
56
61
|
"sphinx>=9.1.0",
|
|
57
62
|
"sphinx-rtd-theme>=3.1.0",
|
|
@@ -14,7 +14,7 @@ babel==2.18.0
|
|
|
14
14
|
# via sphinx
|
|
15
15
|
certifi==2026.2.25
|
|
16
16
|
# via requests
|
|
17
|
-
charset-normalizer==3.4.
|
|
17
|
+
charset-normalizer==3.4.5
|
|
18
18
|
# via requests
|
|
19
19
|
colorama==0.4.6
|
|
20
20
|
# via
|
|
@@ -102,7 +102,7 @@ requests==2.32.5
|
|
|
102
102
|
# sphinx-immaterial
|
|
103
103
|
roman-numerals==4.1.0
|
|
104
104
|
# via sphinx
|
|
105
|
-
ruff==0.15.
|
|
105
|
+
ruff==0.15.5
|
|
106
106
|
# via pyuspto
|
|
107
107
|
snowballstemmer==3.0.1
|
|
108
108
|
# via sphinx
|
|
@@ -115,7 +115,7 @@ sphinx==9.1.0
|
|
|
115
115
|
# sphinx-immaterial
|
|
116
116
|
# sphinx-rtd-theme
|
|
117
117
|
# sphinxcontrib-jquery
|
|
118
|
-
sphinx-autodoc-typehints==3.9.
|
|
118
|
+
sphinx-autodoc-typehints==3.9.7
|
|
119
119
|
# via pyuspto
|
|
120
120
|
sphinx-copybutton==0.5.2
|
|
121
121
|
# via pyuspto
|
|
@@ -163,7 +163,7 @@ class PatentDataClient(BaseUSPTOClient[PatentDataResponse]):
|
|
|
163
163
|
if not serial.isdigit():
|
|
164
164
|
raise ValueError(f"Invalid PCT serial: {serial}. Must be numeric.")
|
|
165
165
|
|
|
166
|
-
return f"PCT{country}{year}{serial}"
|
|
166
|
+
return f"PCT{country}{year}{serial.lstrip('0')}"
|
|
167
167
|
|
|
168
168
|
# Strip whitespace and remove commas/spaces
|
|
169
169
|
cleaned = raw.replace(",", "").replace(" ", "")
|
|
@@ -1039,6 +1039,80 @@ class PatentDataClient(BaseUSPTOClient[PatentDataResponse]):
|
|
|
1039
1039
|
overwrite=overwrite,
|
|
1040
1040
|
)
|
|
1041
1041
|
|
|
1042
|
+
def _resolve_by_search(self, **search_kwargs: Any) -> PatentFileWrapper | None:
|
|
1043
|
+
"""Search for an application and return the first matching wrapper.
|
|
1044
|
+
|
|
1045
|
+
This is a shared helper for convenience methods that resolve
|
|
1046
|
+
non-application-number identifiers to a PatentFileWrapper.
|
|
1047
|
+
|
|
1048
|
+
Args:
|
|
1049
|
+
**search_kwargs: Keyword arguments passed to search_applications().
|
|
1050
|
+
|
|
1051
|
+
Returns:
|
|
1052
|
+
Optional[PatentFileWrapper]: The first matching wrapper, or None.
|
|
1053
|
+
"""
|
|
1054
|
+
pdr = self.search_applications(**search_kwargs, limit=1)
|
|
1055
|
+
if pdr.patent_file_wrapper_data_bag:
|
|
1056
|
+
return pdr.patent_file_wrapper_data_bag[0]
|
|
1057
|
+
return None
|
|
1058
|
+
|
|
1059
|
+
def get_patent(self, patent_number: str) -> PatentFileWrapper | None:
|
|
1060
|
+
"""Retrieve application metadata by patent number.
|
|
1061
|
+
|
|
1062
|
+
Searches the USPTO API for the given patent number and returns
|
|
1063
|
+
the corresponding PatentFileWrapper. This is a lightweight lookup
|
|
1064
|
+
that does not fetch the full document bag.
|
|
1065
|
+
|
|
1066
|
+
Args:
|
|
1067
|
+
patent_number (str): The USPTO patent number (e.g., "11000000").
|
|
1068
|
+
|
|
1069
|
+
Returns:
|
|
1070
|
+
Optional[PatentFileWrapper]: The matching patent file wrapper,
|
|
1071
|
+
or None if not found.
|
|
1072
|
+
"""
|
|
1073
|
+
return self._resolve_by_search(patent_number_q=patent_number)
|
|
1074
|
+
|
|
1075
|
+
def get_publication(self, publication_number: str) -> PatentFileWrapper | None:
|
|
1076
|
+
"""Retrieve application metadata by publication number.
|
|
1077
|
+
|
|
1078
|
+
Searches the USPTO API for the given pre-grant publication number
|
|
1079
|
+
and returns the corresponding PatentFileWrapper. This is a lightweight
|
|
1080
|
+
lookup that does not fetch the full document bag.
|
|
1081
|
+
|
|
1082
|
+
Args:
|
|
1083
|
+
publication_number (str): The USPTO publication number
|
|
1084
|
+
(e.g., "20230123456").
|
|
1085
|
+
|
|
1086
|
+
Returns:
|
|
1087
|
+
Optional[PatentFileWrapper]: The matching patent file wrapper,
|
|
1088
|
+
or None if not found.
|
|
1089
|
+
"""
|
|
1090
|
+
return self._resolve_by_search(earliestPublicationNumber_q=publication_number)
|
|
1091
|
+
|
|
1092
|
+
def get_pct(self, pct_number: str) -> PatentFileWrapper | None:
|
|
1093
|
+
"""Retrieve application metadata by PCT number.
|
|
1094
|
+
|
|
1095
|
+
Accepts both PCT application numbers and PCT publication numbers.
|
|
1096
|
+
The format is auto-detected:
|
|
1097
|
+
|
|
1098
|
+
- PCT application numbers (starting with "PCT") are resolved via
|
|
1099
|
+
direct lookup using get_application_by_number.
|
|
1100
|
+
- PCT publication numbers (e.g., "WO2024012345A1") are resolved
|
|
1101
|
+
via search.
|
|
1102
|
+
|
|
1103
|
+
Args:
|
|
1104
|
+
pct_number (str): A PCT application number (e.g.,
|
|
1105
|
+
"PCT/US2024/012345") or PCT publication number
|
|
1106
|
+
(e.g., "WO2024012345A1").
|
|
1107
|
+
|
|
1108
|
+
Returns:
|
|
1109
|
+
Optional[PatentFileWrapper]: The matching patent file wrapper,
|
|
1110
|
+
or None if not found.
|
|
1111
|
+
"""
|
|
1112
|
+
if pct_number.strip().upper().startswith("PCT"):
|
|
1113
|
+
return self.get_application_by_number(application_number=pct_number)
|
|
1114
|
+
return self._resolve_by_search(pctPublicationNumber_q=pct_number)
|
|
1115
|
+
|
|
1042
1116
|
def get_IFW_metadata(
|
|
1043
1117
|
self,
|
|
1044
1118
|
*,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyUSPTO
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.4
|
|
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
|
|
@@ -17,7 +17,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.13
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.14
|
|
19
19
|
Classifier: Operating System :: OS Independent
|
|
20
|
-
Classifier: Development Status ::
|
|
20
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
21
21
|
Classifier: Environment :: Console
|
|
22
22
|
Classifier: Intended Audience :: Developers
|
|
23
23
|
Classifier: Intended Audience :: Education
|
|
@@ -65,12 +65,13 @@ Requires-Dist: pyUSPTO[docs]; extra == "dev"
|
|
|
65
65
|
Requires-Dist: pyUSPTO[lint]; extra == "dev"
|
|
66
66
|
Dynamic: license-file
|
|
67
67
|
|
|
68
|
-
#
|
|
68
|
+
# USPTO Open Data Portal Client Library
|
|
69
69
|
|
|
70
70
|
[](https://badge.fury.io/py/pyUSPTO)
|
|
71
71
|
[](https://opensource.org/licenses/MIT)
|
|
72
72
|
[](https://www.python.org/downloads/)
|
|
73
73
|
[](https://pyuspto.readthedocs.io/en/latest/)
|
|
74
|
+
[](https://pepy.tech/projects/pyuspto)
|
|
74
75
|
|
|
75
76
|
A Python client library for interacting with the United Stated Patent and Trademark Office (USPTO) [Open Data Portal](https://data.uspto.gov/home) APIs.
|
|
76
77
|
|
|
@@ -1334,7 +1334,7 @@ class TestGetIFW:
|
|
|
1334
1334
|
# Should call get_application_by_number first
|
|
1335
1335
|
assert mock_make_request.call_args_list[0] == call(
|
|
1336
1336
|
method="GET",
|
|
1337
|
-
endpoint="api/v1/patent/applications/
|
|
1337
|
+
endpoint="api/v1/patent/applications/PCTUS2412345",
|
|
1338
1338
|
response_class=PatentDataResponse,
|
|
1339
1339
|
)
|
|
1340
1340
|
assert result.application_number_text == mock_patent_file_wrapper.application_number_text
|
|
@@ -1348,7 +1348,7 @@ class TestGetIFW:
|
|
|
1348
1348
|
"""Test PCT application number sanitization with 2-digit year format (US24 vs US2024).
|
|
1349
1349
|
|
|
1350
1350
|
Verifies that PCT numbers with short year format (PCT/US24/012345) are correctly
|
|
1351
|
-
sanitized to
|
|
1351
|
+
sanitized to PCTUS2412345 before making API request.
|
|
1352
1352
|
|
|
1353
1353
|
Note: This will trigger a data mismatch warning because the mock_patent_file_wrapper
|
|
1354
1354
|
has application_number_text='12345678' but we're requesting a PCT number.
|
|
@@ -1369,7 +1369,7 @@ class TestGetIFW:
|
|
|
1369
1369
|
# Should call get_application_by_number first
|
|
1370
1370
|
assert mock_make_request.call_args_list[0] == call(
|
|
1371
1371
|
method="GET",
|
|
1372
|
-
endpoint="api/v1/patent/applications/
|
|
1372
|
+
endpoint="api/v1/patent/applications/PCTUS2412345",
|
|
1373
1373
|
response_class=PatentDataResponse,
|
|
1374
1374
|
)
|
|
1375
1375
|
assert result.application_number_text == mock_patent_file_wrapper.application_number_text
|
|
@@ -1550,6 +1550,166 @@ class TestGetIFW:
|
|
|
1550
1550
|
assert isinstance(result.document_bag, DocumentBag)
|
|
1551
1551
|
|
|
1552
1552
|
|
|
1553
|
+
class TestGetPatent:
|
|
1554
|
+
"""Tests for the get_patent convenience method."""
|
|
1555
|
+
|
|
1556
|
+
def test_get_patent_found(
|
|
1557
|
+
self,
|
|
1558
|
+
client_with_mocked_request: tuple[PatentDataClient, MagicMock],
|
|
1559
|
+
mock_patent_file_wrapper: PatentFileWrapper,
|
|
1560
|
+
) -> None:
|
|
1561
|
+
"""Test get_patent returns wrapper when patent is found."""
|
|
1562
|
+
client, mock_make_request = client_with_mocked_request
|
|
1563
|
+
mock_make_request.return_value = PatentDataResponse(
|
|
1564
|
+
count=1, patent_file_wrapper_data_bag=[mock_patent_file_wrapper]
|
|
1565
|
+
)
|
|
1566
|
+
|
|
1567
|
+
patent_num = "11000000"
|
|
1568
|
+
result = client.get_patent(patent_num)
|
|
1569
|
+
|
|
1570
|
+
assert mock_make_request.call_args == call(
|
|
1571
|
+
method="GET",
|
|
1572
|
+
endpoint="api/v1/patent/applications/search",
|
|
1573
|
+
params={
|
|
1574
|
+
"q": f"applicationMetaData.patentNumber:{patent_num}",
|
|
1575
|
+
"limit": 1,
|
|
1576
|
+
"offset": 0,
|
|
1577
|
+
},
|
|
1578
|
+
response_class=PatentDataResponse,
|
|
1579
|
+
)
|
|
1580
|
+
assert result is not None
|
|
1581
|
+
assert result.application_number_text == mock_patent_file_wrapper.application_number_text
|
|
1582
|
+
|
|
1583
|
+
def test_get_patent_not_found(
|
|
1584
|
+
self,
|
|
1585
|
+
client_with_mocked_request: tuple[PatentDataClient, MagicMock],
|
|
1586
|
+
) -> None:
|
|
1587
|
+
"""Test get_patent returns None when patent is not found."""
|
|
1588
|
+
client, mock_make_request = client_with_mocked_request
|
|
1589
|
+
mock_make_request.return_value = PatentDataResponse(
|
|
1590
|
+
count=0, patent_file_wrapper_data_bag=[]
|
|
1591
|
+
)
|
|
1592
|
+
|
|
1593
|
+
result = client.get_patent("nonexistent")
|
|
1594
|
+
assert result is None
|
|
1595
|
+
|
|
1596
|
+
|
|
1597
|
+
class TestGetPublication:
|
|
1598
|
+
"""Tests for the get_publication convenience method."""
|
|
1599
|
+
|
|
1600
|
+
def test_get_publication_found(
|
|
1601
|
+
self,
|
|
1602
|
+
client_with_mocked_request: tuple[PatentDataClient, MagicMock],
|
|
1603
|
+
mock_patent_file_wrapper: PatentFileWrapper,
|
|
1604
|
+
) -> None:
|
|
1605
|
+
"""Test get_publication returns wrapper when publication is found."""
|
|
1606
|
+
client, mock_make_request = client_with_mocked_request
|
|
1607
|
+
mock_make_request.return_value = PatentDataResponse(
|
|
1608
|
+
count=1, patent_file_wrapper_data_bag=[mock_patent_file_wrapper]
|
|
1609
|
+
)
|
|
1610
|
+
|
|
1611
|
+
pub_num = "20230123456"
|
|
1612
|
+
result = client.get_publication(pub_num)
|
|
1613
|
+
|
|
1614
|
+
assert mock_make_request.call_args == call(
|
|
1615
|
+
method="GET",
|
|
1616
|
+
endpoint="api/v1/patent/applications/search",
|
|
1617
|
+
params={
|
|
1618
|
+
"q": f"applicationMetaData.earliestPublicationNumber:{pub_num}",
|
|
1619
|
+
"limit": 1,
|
|
1620
|
+
"offset": 0,
|
|
1621
|
+
},
|
|
1622
|
+
response_class=PatentDataResponse,
|
|
1623
|
+
)
|
|
1624
|
+
assert result is not None
|
|
1625
|
+
assert result.application_number_text == mock_patent_file_wrapper.application_number_text
|
|
1626
|
+
|
|
1627
|
+
def test_get_publication_not_found(
|
|
1628
|
+
self,
|
|
1629
|
+
client_with_mocked_request: tuple[PatentDataClient, MagicMock],
|
|
1630
|
+
) -> None:
|
|
1631
|
+
"""Test get_publication returns None when publication is not found."""
|
|
1632
|
+
client, mock_make_request = client_with_mocked_request
|
|
1633
|
+
mock_make_request.return_value = PatentDataResponse(
|
|
1634
|
+
count=0, patent_file_wrapper_data_bag=[]
|
|
1635
|
+
)
|
|
1636
|
+
|
|
1637
|
+
result = client.get_publication("nonexistent")
|
|
1638
|
+
assert result is None
|
|
1639
|
+
|
|
1640
|
+
|
|
1641
|
+
class TestGetPCT:
|
|
1642
|
+
"""Tests for the get_pct convenience method."""
|
|
1643
|
+
|
|
1644
|
+
@pytest.fixture
|
|
1645
|
+
def mock_pct_file_wrapper(self) -> PatentFileWrapper:
|
|
1646
|
+
"""Provides a mock PatentFileWrapper with a sanitized PCT application number."""
|
|
1647
|
+
return PatentFileWrapper(application_number_text="PCTUS2412345")
|
|
1648
|
+
|
|
1649
|
+
def test_get_pct_with_app_number(
|
|
1650
|
+
self,
|
|
1651
|
+
client_with_mocked_request: tuple[PatentDataClient, MagicMock],
|
|
1652
|
+
mock_pct_file_wrapper: PatentFileWrapper,
|
|
1653
|
+
) -> None:
|
|
1654
|
+
"""Test get_pct with PCT application number uses direct lookup."""
|
|
1655
|
+
client, mock_make_request = client_with_mocked_request
|
|
1656
|
+
mock_make_request.return_value = PatentDataResponse(
|
|
1657
|
+
count=1, patent_file_wrapper_data_bag=[mock_pct_file_wrapper]
|
|
1658
|
+
)
|
|
1659
|
+
|
|
1660
|
+
pct_app = "PCT/US2024/012345"
|
|
1661
|
+
result = client.get_pct(pct_app)
|
|
1662
|
+
|
|
1663
|
+
# Should call get_application_by_number (direct lookup)
|
|
1664
|
+
assert mock_make_request.call_args == call(
|
|
1665
|
+
method="GET",
|
|
1666
|
+
endpoint="api/v1/patent/applications/PCTUS2412345",
|
|
1667
|
+
response_class=PatentDataResponse,
|
|
1668
|
+
)
|
|
1669
|
+
assert result is not None
|
|
1670
|
+
assert result.application_number_text == mock_pct_file_wrapper.application_number_text
|
|
1671
|
+
|
|
1672
|
+
def test_get_pct_with_pub_number(
|
|
1673
|
+
self,
|
|
1674
|
+
client_with_mocked_request: tuple[PatentDataClient, MagicMock],
|
|
1675
|
+
mock_patent_file_wrapper: PatentFileWrapper,
|
|
1676
|
+
) -> None:
|
|
1677
|
+
"""Test get_pct with PCT publication number uses search."""
|
|
1678
|
+
client, mock_make_request = client_with_mocked_request
|
|
1679
|
+
mock_make_request.return_value = PatentDataResponse(
|
|
1680
|
+
count=1, patent_file_wrapper_data_bag=[mock_patent_file_wrapper]
|
|
1681
|
+
)
|
|
1682
|
+
|
|
1683
|
+
pct_pub = "WO2024012345A1"
|
|
1684
|
+
result = client.get_pct(pct_pub)
|
|
1685
|
+
|
|
1686
|
+
assert mock_make_request.call_args == call(
|
|
1687
|
+
method="GET",
|
|
1688
|
+
endpoint="api/v1/patent/applications/search",
|
|
1689
|
+
params={
|
|
1690
|
+
"q": f"applicationMetaData.pctPublicationNumber:{pct_pub}",
|
|
1691
|
+
"limit": 1,
|
|
1692
|
+
"offset": 0,
|
|
1693
|
+
},
|
|
1694
|
+
response_class=PatentDataResponse,
|
|
1695
|
+
)
|
|
1696
|
+
assert result is not None
|
|
1697
|
+
assert result.application_number_text == mock_patent_file_wrapper.application_number_text
|
|
1698
|
+
|
|
1699
|
+
def test_get_pct_not_found(
|
|
1700
|
+
self,
|
|
1701
|
+
client_with_mocked_request: tuple[PatentDataClient, MagicMock],
|
|
1702
|
+
) -> None:
|
|
1703
|
+
"""Test get_pct returns None when PCT number is not found."""
|
|
1704
|
+
client, mock_make_request = client_with_mocked_request
|
|
1705
|
+
mock_make_request.return_value = PatentDataResponse(
|
|
1706
|
+
count=0, patent_file_wrapper_data_bag=[]
|
|
1707
|
+
)
|
|
1708
|
+
|
|
1709
|
+
result = client.get_pct("WO9999999999")
|
|
1710
|
+
assert result is None
|
|
1711
|
+
|
|
1712
|
+
|
|
1553
1713
|
class TestDownloadArchive:
|
|
1554
1714
|
"""Tests for downloading archive files."""
|
|
1555
1715
|
|
|
@@ -857,3 +857,52 @@ class TestPatentDataIntegration:
|
|
|
857
857
|
)
|
|
858
858
|
except Exception as e:
|
|
859
859
|
pytest.fail(f"Unexpected exception for invalid app number: {e}")
|
|
860
|
+
|
|
861
|
+
|
|
862
|
+
class TestHelperMethodsIntegration:
|
|
863
|
+
"""Integration tests for get_patent, get_publication, and get_pct helper methods."""
|
|
864
|
+
|
|
865
|
+
KNOWN_PATENT_NUMBER = "12565253"
|
|
866
|
+
KNOWN_PUBLICATION_NUMBER = "US20260054762A1"
|
|
867
|
+
KNOWN_PCT_APP_NUMBER = "PCT/US2025/047756"
|
|
868
|
+
KNOWN_PCT_PUB_NUMBER = "WO 2026/044302"
|
|
869
|
+
|
|
870
|
+
def test_get_patent(self, patent_data_client: PatentDataClient) -> None:
|
|
871
|
+
"""Test get_patent returns a PatentFileWrapper for a known patent number."""
|
|
872
|
+
result = patent_data_client.get_patent(self.KNOWN_PATENT_NUMBER)
|
|
873
|
+
|
|
874
|
+
assert isinstance(result, PatentFileWrapper)
|
|
875
|
+
assert result.application_number_text == "19378371"
|
|
876
|
+
assert result.application_meta_data is not None
|
|
877
|
+
assert isinstance(result.application_meta_data, ApplicationMetaData)
|
|
878
|
+
|
|
879
|
+
def test_get_publication(self, patent_data_client: PatentDataClient) -> None:
|
|
880
|
+
"""Test get_publication returns a PatentFileWrapper for a known publication number."""
|
|
881
|
+
result = patent_data_client.get_publication(self.KNOWN_PUBLICATION_NUMBER)
|
|
882
|
+
|
|
883
|
+
assert isinstance(result, PatentFileWrapper)
|
|
884
|
+
assert result.application_number_text == "19378371"
|
|
885
|
+
assert result.application_meta_data is not None
|
|
886
|
+
assert isinstance(result.application_meta_data, ApplicationMetaData)
|
|
887
|
+
|
|
888
|
+
def test_get_pct_with_app_number(
|
|
889
|
+
self, patent_data_client: PatentDataClient
|
|
890
|
+
) -> None:
|
|
891
|
+
"""Test get_pct with a PCT application number (direct lookup)."""
|
|
892
|
+
result = patent_data_client.get_pct(self.KNOWN_PCT_APP_NUMBER)
|
|
893
|
+
|
|
894
|
+
assert isinstance(result, PatentFileWrapper)
|
|
895
|
+
assert result.application_number_text == "PCTUS2547756"
|
|
896
|
+
assert result.application_meta_data is not None
|
|
897
|
+
assert result.application_meta_data.pct_publication_number == "WO2026044302"
|
|
898
|
+
|
|
899
|
+
def test_get_pct_with_pub_number(
|
|
900
|
+
self, patent_data_client: PatentDataClient
|
|
901
|
+
) -> None:
|
|
902
|
+
"""Test get_pct with a PCT publication number."""
|
|
903
|
+
result = patent_data_client.get_pct(self.KNOWN_PCT_PUB_NUMBER)
|
|
904
|
+
|
|
905
|
+
assert isinstance(result, PatentFileWrapper)
|
|
906
|
+
assert result.application_number_text == "PCTUS2547756"
|
|
907
|
+
assert result.application_meta_data is not None
|
|
908
|
+
assert result.application_meta_data.pct_publication_number == "WO2026044302"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|