python-epo-ops-client 3.1.3__tar.gz → 4.1.0__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.
- {python-epo-ops-client-3.1.3 → python-epo-ops-client-4.1.0}/CHANGELOG.md +14 -1
- python-epo-ops-client-3.1.3/LICENSE.txt → python-epo-ops-client-4.1.0/LICENSE +0 -25
- {python-epo-ops-client-3.1.3 → python-epo-ops-client-4.1.0}/MANIFEST.in +2 -2
- {python-epo-ops-client-3.1.3 → python-epo-ops-client-4.1.0}/Makefile +17 -14
- python-epo-ops-client-4.1.0/PKG-INFO +214 -0
- {python-epo-ops-client-3.1.3 → python-epo-ops-client-4.1.0}/README.md +5 -5
- python-epo-ops-client-3.1.3/AUTHORS.md → python-epo-ops-client-4.1.0/docs/authors.md +12 -3
- python-epo-ops-client-4.1.0/docs/backlog.md +35 -0
- python-epo-ops-client-4.1.0/docs/contributing.md +87 -0
- python-epo-ops-client-4.1.0/docs/sandbox.md +89 -0
- python-epo-ops-client-4.1.0/epo_ops/__version__.py +6 -0
- {python-epo-ops-client-3.1.3 → python-epo-ops-client-4.1.0}/epo_ops/api.py +8 -5
- {python-epo-ops-client-3.1.3 → python-epo-ops-client-4.1.0}/epo_ops/middlewares/cache/dogpile/dogpile.py +2 -1
- {python-epo-ops-client-3.1.3 → python-epo-ops-client-4.1.0}/epo_ops/middlewares/cache/dogpile/helpers.py +1 -1
- {python-epo-ops-client-3.1.3 → python-epo-ops-client-4.1.0}/epo_ops/middlewares/throttle/storages/sqlite.py +9 -4
- {python-epo-ops-client-3.1.3 → python-epo-ops-client-4.1.0}/epo_ops/models.py +7 -4
- {python-epo-ops-client-3.1.3 → python-epo-ops-client-4.1.0}/epo_ops/utils.py +2 -2
- python-epo-ops-client-4.1.0/pyproject.toml +95 -0
- python-epo-ops-client-4.1.0/python_epo_ops_client.egg-info/PKG-INFO +214 -0
- {python-epo-ops-client-3.1.3 → python-epo-ops-client-4.1.0}/python_epo_ops_client.egg-info/SOURCES.txt +11 -6
- python-epo-ops-client-4.1.0/python_epo_ops_client.egg-info/requires.txt +22 -0
- python-epo-ops-client-4.1.0/setup.py +96 -0
- python-epo-ops-client-4.1.0/tests/test_api.py +117 -0
- python-epo-ops-client-4.1.0/tests/test_models.py +87 -0
- python-epo-ops-client-4.1.0/tests/test_ops_quota.py +67 -0
- python-epo-ops-client-4.1.0/tests/test_utils.py +23 -0
- python-epo-ops-client-3.1.3/CONTRIBUTING.md +0 -138
- python-epo-ops-client-3.1.3/PKG-INFO +0 -307
- python-epo-ops-client-3.1.3/TODOS.md +0 -23
- python-epo-ops-client-3.1.3/__version__.py +0 -1
- python-epo-ops-client-3.1.3/epo_ops/__version__.py +0 -1
- python-epo-ops-client-3.1.3/python_epo_ops_client.egg-info/PKG-INFO +0 -307
- python-epo-ops-client-3.1.3/python_epo_ops_client.egg-info/requires.txt +0 -3
- python-epo-ops-client-3.1.3/setup.py +0 -64
- {python-epo-ops-client-3.1.3 → python-epo-ops-client-4.1.0}/epo_ops/__init__.py +0 -0
- {python-epo-ops-client-3.1.3 → python-epo-ops-client-4.1.0}/epo_ops/exceptions.py +0 -0
- {python-epo-ops-client-3.1.3 → python-epo-ops-client-4.1.0}/epo_ops/middlewares/__init__.py +0 -0
- {python-epo-ops-client-3.1.3 → python-epo-ops-client-4.1.0}/epo_ops/middlewares/cache/__init__.py +0 -0
- {python-epo-ops-client-3.1.3 → python-epo-ops-client-4.1.0}/epo_ops/middlewares/cache/dogpile/__init__.py +0 -0
- {python-epo-ops-client-3.1.3 → python-epo-ops-client-4.1.0}/epo_ops/middlewares/middleware.py +0 -0
- {python-epo-ops-client-3.1.3 → python-epo-ops-client-4.1.0}/epo_ops/middlewares/throttle/__init__.py +0 -0
- {python-epo-ops-client-3.1.3 → python-epo-ops-client-4.1.0}/epo_ops/middlewares/throttle/storages/__init__.py +0 -0
- {python-epo-ops-client-3.1.3 → python-epo-ops-client-4.1.0}/epo_ops/middlewares/throttle/storages/storage.py +0 -0
- {python-epo-ops-client-3.1.3 → python-epo-ops-client-4.1.0}/epo_ops/middlewares/throttle/throttler.py +0 -0
- {python-epo-ops-client-3.1.3 → python-epo-ops-client-4.1.0}/epo_ops/middlewares/throttle/utils.py +0 -0
- {python-epo-ops-client-3.1.3 → python-epo-ops-client-4.1.0}/python_epo_ops_client.egg-info/dependency_links.txt +0 -0
- {python-epo-ops-client-3.1.3 → python-epo-ops-client-4.1.0}/python_epo_ops_client.egg-info/not-zip-safe +0 -0
- {python-epo-ops-client-3.1.3 → python-epo-ops-client-4.1.0}/python_epo_ops_client.egg-info/top_level.txt +0 -0
- {python-epo-ops-client-3.1.3 → python-epo-ops-client-4.1.0}/setup.cfg +0 -0
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 4.1.0 (2024-01-25)
|
|
4
|
+
|
|
5
|
+
- Configure HTTP client to use a network timeout of 10 seconds
|
|
6
|
+
- Verify support for Python 3.10, 3.11, and 3.12
|
|
7
|
+
- Project: Use `versioningit` for versioning
|
|
8
|
+
- Tests: Remove dependency on Apiary Mock Server API
|
|
9
|
+
|
|
10
|
+
## 4.0.0 (2021-09-19)
|
|
11
|
+
|
|
12
|
+
- Upgrade dependencies
|
|
13
|
+
- Drop support for Python 2.7 and Python 3.5
|
|
14
|
+
- Add support for Python 3.9
|
|
15
|
+
|
|
3
16
|
## 3.1.3 (2020-09-23)
|
|
4
17
|
|
|
5
18
|
- Upgrade dependencies
|
|
@@ -11,7 +24,7 @@
|
|
|
11
24
|
## 3.1.1 (2019-10-28)
|
|
12
25
|
|
|
13
26
|
- GET instead of POST for family services, thanks to [amotl][]. See
|
|
14
|
-
[#33](https://github.com/
|
|
27
|
+
[#33](https://github.com/ip-tools/python-epo-ops-client/issues/33) for more
|
|
15
28
|
info.
|
|
16
29
|
|
|
17
30
|
## 3.1.0 (2019-10-27)
|
|
@@ -174,28 +174,3 @@
|
|
|
174
174
|
of your accepting any such warranty or additional liability.
|
|
175
175
|
|
|
176
176
|
END OF TERMS AND CONDITIONS
|
|
177
|
-
|
|
178
|
-
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
-
|
|
180
|
-
To apply the Apache License to your work, attach the following
|
|
181
|
-
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
-
replaced with your own identifying information. (Don't include
|
|
183
|
-
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
-
comment syntax for the file format. We also recommend that a
|
|
185
|
-
file or class name and description of purpose be included on the
|
|
186
|
-
same "printed page" as the copyright notice for easier
|
|
187
|
-
identification within third-party archives.
|
|
188
|
-
|
|
189
|
-
Copyright [yyyy] [name of copyright owner]
|
|
190
|
-
|
|
191
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
-
you may not use this file except in compliance with the License.
|
|
193
|
-
You may obtain a copy of the License at
|
|
194
|
-
|
|
195
|
-
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
-
|
|
197
|
-
Unless required by applicable law or agreed to in writing, software
|
|
198
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
-
See the License for the specific language governing permissions and
|
|
201
|
-
limitations under the License.
|
|
@@ -15,38 +15,41 @@ help: ## Display this help message
|
|
|
15
15
|
@echo "Please use \`make <target>' where <target> is one of the following:"
|
|
16
16
|
@perl -nle'print $& if m{^[\.a-zA-Z_-]+:.*?## .*$$}' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m %-25s\033[0m %s\n", $$1, $$2}'
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
pip-compile: ## Update compiled requirement files
|
|
22
|
-
bin/pip-compile.sh
|
|
18
|
+
install-develop: ## Install project into sandbox.
|
|
19
|
+
pip install --use-pep517 --prefer-binary --editable=.[develop,docs,test]
|
|
23
20
|
|
|
24
21
|
clean: clean-build clean-pyc
|
|
25
22
|
|
|
26
23
|
clean-build:
|
|
27
24
|
rm -fr build/
|
|
28
25
|
rm -fr dist/
|
|
29
|
-
rm -fr *.egg-info
|
|
30
26
|
|
|
31
27
|
clean-pyc:
|
|
32
28
|
find . -name '*.pyc' -exec rm -f {} +
|
|
33
29
|
find . -name '*.pyo' -exec rm -f {} +
|
|
34
30
|
find . -name '*~' -exec rm -f {} +
|
|
35
31
|
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
check: lint test ## Run linter and software tests
|
|
33
|
+
check-ci: lint test-ci ## Run linter and software tests on CI
|
|
34
|
+
|
|
35
|
+
lint: ## lint the project
|
|
36
|
+
ruff .
|
|
37
|
+
black --check .
|
|
38
|
+
|
|
39
|
+
format: ## Run code formatting
|
|
40
|
+
# Configure Ruff not to auto-fix (remove!):
|
|
41
|
+
# Ignore unused imports (F401), unused variables (F841), `print` statements (T201), and commented-out code (ERA001).
|
|
42
|
+
ruff --fix --ignore=ERA --ignore=F401 --ignore=F841 --ignore=T20 --ignore=ERA001 .
|
|
43
|
+
black .
|
|
38
44
|
|
|
39
45
|
test: clean ## Run tests with virtualenv Python
|
|
40
|
-
py.test -s -v --lf --cov-report term --cov
|
|
46
|
+
py.test -s -v --lf --cov epo_ops tests --cov-report term-missing --cov-report xml
|
|
41
47
|
|
|
42
48
|
test-ci: clean ## Run tests in CI environment with virtualenv Python
|
|
43
|
-
py.test -v --cov epo_ops --cov-report term-missing
|
|
44
|
-
|
|
45
|
-
tox: clean ## Run tests with all supported Python versions
|
|
46
|
-
tox
|
|
49
|
+
py.test -v --cov epo_ops tests --cov-report term-missing --cov-report xml
|
|
47
50
|
|
|
48
51
|
coverage: clean ## Check code coverage locally
|
|
49
|
-
py.test -s -v --cov-report
|
|
52
|
+
py.test -s -v --cov epo_ops tests --cov-report term-missing --cov-report xml --cov-report html
|
|
50
53
|
open htmlcov/index.html
|
|
51
54
|
|
|
52
55
|
release: clean # Package and upload a release to PyPI
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: python-epo-ops-client
|
|
3
|
+
Version: 4.1.0
|
|
4
|
+
Summary: Python client for EPO OPS, the European Patent Office's Open Patent Services API.
|
|
5
|
+
Home-page: https://github.com/ip-tools/python-epo-ops-client
|
|
6
|
+
Download-URL: https://pypi.org/project/python-epo-ops-client/#files
|
|
7
|
+
Author: George Song
|
|
8
|
+
Author-email: george@monozuku.com
|
|
9
|
+
Maintainer: Andreas Motl
|
|
10
|
+
Maintainer-email: andreas.motl@ip-tools.org
|
|
11
|
+
Keywords: ops,epo,epo-ops,patent-data,patent-office,patent-data-api,european patent office,open patent services
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Natural Language :: English
|
|
15
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
16
|
+
Classifier: Programming Language :: Python
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.6
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
25
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Requires-Dist: dogpile.cache<1.2
|
|
29
|
+
Requires-Dist: importlib-metadata; python_version < "3.8"
|
|
30
|
+
Requires-Dist: python-dateutil<2.9
|
|
31
|
+
Requires-Dist: requests<3,>=2.27
|
|
32
|
+
Requires-Dist: six<2
|
|
33
|
+
Provides-Extra: develop
|
|
34
|
+
Requires-Dist: black<24; extra == "develop"
|
|
35
|
+
Requires-Dist: ruff==0.0.285; python_version >= "3.7" and extra == "develop"
|
|
36
|
+
Requires-Dist: twine<5; extra == "develop"
|
|
37
|
+
Requires-Dist: wheel<1; extra == "develop"
|
|
38
|
+
Provides-Extra: test
|
|
39
|
+
Requires-Dist: pytest<8; extra == "test"
|
|
40
|
+
Requires-Dist: pytest-cache<2; extra == "test"
|
|
41
|
+
Requires-Dist: pytest-cov<4.2; extra == "test"
|
|
42
|
+
Requires-Dist: python-dotenv<0.20; extra == "test"
|
|
43
|
+
Requires-Dist: responses<0.24; extra == "test"
|
|
44
|
+
|
|
45
|
+
# python-epo-ops-client
|
|
46
|
+
|
|
47
|
+
[](https://pypi.org/project/python-epo-ops-client/)
|
|
48
|
+
[](https://pypi.org/project/python-epo-ops-client/)
|
|
49
|
+
[](https://github.com/ip-tools/python-epo-ops-client/actions/workflows/main.yml)
|
|
50
|
+
[](https://codecov.io/gh/ip-tools/python-epo-ops-client)
|
|
51
|
+
|
|
52
|
+
python-epo-ops-client is an [Apache2 licensed][apache license] client library
|
|
53
|
+
for accessing the [European Patent Office][epo]'s ("EPO") [Open Patent
|
|
54
|
+
Services][ops] ("OPS") v.3.2 (based on [v 1.3.16 of the reference guide][ops guide]).
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
import epo_ops
|
|
58
|
+
|
|
59
|
+
client = epo_ops.Client(key='abc', secret='xyz') # Instantiate client
|
|
60
|
+
response = client.published_data( # Retrieve bibliography data
|
|
61
|
+
reference_type = 'publication', # publication, application, priority
|
|
62
|
+
input = epo_ops.models.Docdb('1000000', 'EP', 'A1'), # original, docdb, epodoc
|
|
63
|
+
endpoint = 'biblio', # optional, defaults to biblio in case of published_data
|
|
64
|
+
constituents = [] # optional, list of constituents
|
|
65
|
+
)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Features
|
|
71
|
+
|
|
72
|
+
`python-epo-ops-client` abstracts away the complexities of accessing EPO OPS:
|
|
73
|
+
|
|
74
|
+
- Format the requests properly
|
|
75
|
+
- Bubble up quota problems as proper HTTP errors
|
|
76
|
+
- Handle token authentication and renewals automatically
|
|
77
|
+
- Handle throttling properly
|
|
78
|
+
- Add optional caching to minimize impact on the OPS servers
|
|
79
|
+
|
|
80
|
+
There are two main layers to `python-epo-ops-client`: Client and Middleware.
|
|
81
|
+
|
|
82
|
+
### Client
|
|
83
|
+
|
|
84
|
+
The Client contains all the formatting and token handling logic and is what
|
|
85
|
+
you'll interact with mostly.
|
|
86
|
+
|
|
87
|
+
When you issue a request, the response is a [requests.Response][] object. If
|
|
88
|
+
`response.status_code != 200` then a `requests.HTTPError` exception will be
|
|
89
|
+
raised — it's your responsibility to handle those exceptions if you want to. The
|
|
90
|
+
one case that's handled is when the access token has expired: in this case, the
|
|
91
|
+
client will automatically handle the HTTP 400 status and renew the token.
|
|
92
|
+
|
|
93
|
+
Note that the Client does not attempt to interpret the data supplied by OPS, so
|
|
94
|
+
it's your responsibility to parse the XML or JSON payload for your own purpose.
|
|
95
|
+
|
|
96
|
+
The following custom exceptions are raised for cases when OPS quotas are
|
|
97
|
+
exceeded, they are all in the `epo_ops.exceptions` module and are subclasses of
|
|
98
|
+
`requests.HTTPError`, and therefore offer the same behaviors:
|
|
99
|
+
|
|
100
|
+
- IndividualQuotaPerHourExceeded
|
|
101
|
+
- RegisteredQuotaPerWeekExceeded
|
|
102
|
+
|
|
103
|
+
Again, it's up to you to parse the response and decide what to do.
|
|
104
|
+
|
|
105
|
+
Currently the Client knows how to issue request for the following services:
|
|
106
|
+
|
|
107
|
+
| Client method | API end point | throttle |
|
|
108
|
+
| ----------------------------------------------------------------------------- | --------------------- | --------- |
|
|
109
|
+
| `family(reference_type, input, endpoint=None, constituents=None)` | family | inpadoc |
|
|
110
|
+
| `image(path, range=1, extension='tiff')` | published-data/images | images |
|
|
111
|
+
| `number(reference_type, input, output_format)` | number-service | other |
|
|
112
|
+
| `published_data(reference_type, input, endpoint='biblio', constituents=None)` | published-data | retrieval |
|
|
113
|
+
| `published_data_search(cql, range_begin=1, range_end=25, constituents=None)` | published-data/search | search |
|
|
114
|
+
| `register(reference_type, input, constituents=['biblio'])` | register | other |
|
|
115
|
+
| `register_search(cql, range_begin=1, range_end=25)` | register/search | other |
|
|
116
|
+
| `register_search(cql, range_begin=1, range_end=25)` | register/search | other |
|
|
117
|
+
|
|
118
|
+
Bulk operations can be achieved by passing a list of valid models to the
|
|
119
|
+
published_data input field.
|
|
120
|
+
|
|
121
|
+
See the [OPS guide][] or use the [Developer's Area][] for more information on
|
|
122
|
+
how to use each service.
|
|
123
|
+
|
|
124
|
+
Please submit pull requests for the following services by enhancing the
|
|
125
|
+
`epo_ops.api.Client` class:
|
|
126
|
+
|
|
127
|
+
- Legal service
|
|
128
|
+
|
|
129
|
+
### Middleware
|
|
130
|
+
|
|
131
|
+
All requests and responses are passed through each middleware object listed in
|
|
132
|
+
`client.middlewares`. Requests are processed in the order listed, and responses
|
|
133
|
+
are processed in the _reverse_ order.
|
|
134
|
+
|
|
135
|
+
Each middleware should subclass `middlewares.Middleware` and implement the
|
|
136
|
+
`process_request` and `process_response` methods.
|
|
137
|
+
|
|
138
|
+
There are two middleware classes out of the box: Throttler and Dogpile.
|
|
139
|
+
Throttler is in charge of the OPS throttling rules and will delay requests
|
|
140
|
+
accordingly. Dogpile is an optional cache which will cache all HTTP status 200,
|
|
141
|
+
404, 405, and 413 responses.
|
|
142
|
+
|
|
143
|
+
By default, only the Throttler middleware is enabled, if you want to enable
|
|
144
|
+
caching:
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
import epo_ops
|
|
148
|
+
|
|
149
|
+
middlewares = [
|
|
150
|
+
epo_ops.middlewares.Dogpile(),
|
|
151
|
+
epo_ops.middlewares.Throttler(),
|
|
152
|
+
]
|
|
153
|
+
client = epo_ops.Client(
|
|
154
|
+
key='key',
|
|
155
|
+
secret='secret',
|
|
156
|
+
middlewares=middlewares,
|
|
157
|
+
)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
You'll also need to install caching dependencies in your projects, such as `pip install dogpile.cache`.
|
|
161
|
+
|
|
162
|
+
_Note that caching middleware should be first in most cases._
|
|
163
|
+
|
|
164
|
+
#### Dogpile
|
|
165
|
+
|
|
166
|
+
Dogpile is based on (surprise) [dogpile.cache][]. By default it is instantiated
|
|
167
|
+
with a DBMBackend region with timeout of 2 weeks.
|
|
168
|
+
|
|
169
|
+
Dogpile takes three optional instantiation parameters:
|
|
170
|
+
|
|
171
|
+
- `region`: You can pass whatever valid [dogpile.cache Region][] you want to
|
|
172
|
+
backend the cache
|
|
173
|
+
- `kwargs_handlers`: A list of keyword argument handlers, which it will use to
|
|
174
|
+
process the kwargs passed to the request object in order to extract elements
|
|
175
|
+
for generating the cache key. Currently one handler is implemented (and
|
|
176
|
+
instantiated by default) to make sure that the range request header is part of
|
|
177
|
+
the cache key.
|
|
178
|
+
- `http_status_codes`: A list of HTTP status codes that you would like to have
|
|
179
|
+
cached. By default 200, 404, 405, and 413 responses are cached.
|
|
180
|
+
|
|
181
|
+
**Note**: dogpile.cache is not installed by default, if you want to use it, `pip install dogpile.cache` in your project.
|
|
182
|
+
|
|
183
|
+
#### Throttler
|
|
184
|
+
|
|
185
|
+
Throttler contains all the logic for handling different throttling scenarios.
|
|
186
|
+
Since OPS throttling is based on a one minute rolling window, we must persist
|
|
187
|
+
historical (at least for the past minute) throtting data in order to know what
|
|
188
|
+
the proper request frequency is. Each Throttler must be instantiated with a
|
|
189
|
+
Storage object.
|
|
190
|
+
|
|
191
|
+
##### Storage
|
|
192
|
+
|
|
193
|
+
The Storage object is responsible for:
|
|
194
|
+
|
|
195
|
+
1. Knowing how to update the historical record with each request
|
|
196
|
+
(`Storage.update()`), making sure to observe the one minute rolling window
|
|
197
|
+
rule.
|
|
198
|
+
2. Calculating how long to wait before issuing the next request
|
|
199
|
+
(`Storage.delay_for()`).
|
|
200
|
+
|
|
201
|
+
Currently the only Storage backend provided is SQLite, but you can easily write
|
|
202
|
+
your own Storage backend (such as file, Redis, etc.). To use a custom Storage
|
|
203
|
+
type, just pass the Storage object when you're instantiating a Throttler object.
|
|
204
|
+
See `epo_ops.middlewares.throttle.storages.Storage` for more implementation
|
|
205
|
+
details.
|
|
206
|
+
|
|
207
|
+
[apache license]: http://www.apache.org/licenses/LICENSE-2.0
|
|
208
|
+
[developer's area]: https://developers.epo.org/ops-v3-2/apis
|
|
209
|
+
[dogpile.cache region]: http://dogpilecache.readthedocs.org/en/latest/api.html#module-dogpile.cache.region
|
|
210
|
+
[dogpile.cache]: https://bitbucket.org/zzzeek/dogpile.cache
|
|
211
|
+
[epo]: http://epo.org
|
|
212
|
+
[ops guide]: https://link.epo.org/web/ops_v3.2_documentation_-_version_1.3.19_en.pdf
|
|
213
|
+
[ops]: https://www.epo.org/searching-for-patents/data/web-services/ops.html
|
|
214
|
+
[requests.response]: http://requests.readthedocs.org/en/latest/user/advanced/#request-and-response-objects
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://pypi.org/project/python-epo-ops-client/)
|
|
4
4
|
[](https://pypi.org/project/python-epo-ops-client/)
|
|
5
|
-
[](https://github.com/ip-tools/python-epo-ops-client/actions/workflows/main.yml)
|
|
6
|
+
[](https://codecov.io/gh/ip-tools/python-epo-ops-client)
|
|
7
7
|
|
|
8
8
|
python-epo-ops-client is an [Apache2 licensed][apache license] client library
|
|
9
9
|
for accessing the [European Patent Office][epo]'s ("EPO") [Open Patent
|
|
10
|
-
Services][ops] ("OPS") v.3.2 (based on [v 1.3.
|
|
10
|
+
Services][ops] ("OPS") v.3.2 (based on [v 1.3.16 of the reference guide][ops guide]).
|
|
11
11
|
|
|
12
12
|
```python
|
|
13
13
|
import epo_ops
|
|
@@ -165,6 +165,6 @@ details.
|
|
|
165
165
|
[dogpile.cache region]: http://dogpilecache.readthedocs.org/en/latest/api.html#module-dogpile.cache.region
|
|
166
166
|
[dogpile.cache]: https://bitbucket.org/zzzeek/dogpile.cache
|
|
167
167
|
[epo]: http://epo.org
|
|
168
|
-
[ops guide]:
|
|
169
|
-
[ops]:
|
|
168
|
+
[ops guide]: https://link.epo.org/web/ops_v3.2_documentation_-_version_1.3.19_en.pdf
|
|
169
|
+
[ops]: https://www.epo.org/searching-for-patents/data/web-services/ops.html
|
|
170
170
|
[requests.response]: http://requests.readthedocs.org/en/latest/user/advanced/#request-and-response-objects
|
|
@@ -1,14 +1,23 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Authors
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The following people contributed to the software in one way or another,
|
|
4
|
+
thank you so much. The list is alphabetically sorted.
|
|
5
|
+
|
|
6
|
+
## Development Leads
|
|
4
7
|
|
|
5
8
|
- [George Song](https://github.com/gsong)
|
|
6
9
|
|
|
7
|
-
##
|
|
10
|
+
## Maintainers
|
|
8
11
|
|
|
9
12
|
- [Andreas Motl](https://github.com/amotl)
|
|
13
|
+
|
|
14
|
+
## Contributors
|
|
15
|
+
|
|
16
|
+
- [Alexander Meinhardt Scheurer](https://github.com/BeneCollyridam)
|
|
10
17
|
- [Daniel Blasco](https://github.com/dablak)
|
|
18
|
+
- [fe60](https://github.com/fe60)
|
|
11
19
|
- [Felipe Eltermann](https://github.com/eltermann)
|
|
20
|
+
- [Geoffrey Cline](https://github.com/geoffcline)
|
|
12
21
|
- [Hiro Kobashi](https://github.com/kobaski)
|
|
13
22
|
- [Mike Matheson](https://github.com/mmath)
|
|
14
23
|
- [Roberto Faga](https://github.com/rfaga)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Backlog
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
## Iteration +1
|
|
5
|
+
|
|
6
|
+
- Improve and clean up README
|
|
7
|
+
- Set up project documentation on Read the Docs
|
|
8
|
+
- Switch to `pyproject.toml`
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## Iteration +2
|
|
12
|
+
|
|
13
|
+
This is the content of the original `TODOS.md` file.
|
|
14
|
+
|
|
15
|
+
- Feature: Should the non-standard oAuth be coded as a requests plug-in?
|
|
16
|
+
- Dogpile caching
|
|
17
|
+
- Generate the key based on a SortedDict of some kind to make sure the exact
|
|
18
|
+
same request with different argument order are still a hit
|
|
19
|
+
- Additional services
|
|
20
|
+
- Legal service
|
|
21
|
+
- Makefile: Comma in `echo` statements?
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
## Done
|
|
25
|
+
|
|
26
|
+
- Use new organization name `ip-tools`
|
|
27
|
+
- CI: Add GHA recipe, dissolve Travis configuration
|
|
28
|
+
- Dissolve `requirements` files, and inline them into `setup.py`
|
|
29
|
+
- Add support for Python 3.10 and 3.11
|
|
30
|
+
- Use `ruff` linter, dissolve `flake8` and `isort`
|
|
31
|
+
- Use `versioningit` for versioning, dissolve `bumpversion`
|
|
32
|
+
- Replace Apiary Mock server
|
|
33
|
+
https://github.com/ip-tools/python-epo-ops-client/issues/65
|
|
34
|
+
- Testing: Replace Apiary tests with monkeypatch? Or
|
|
35
|
+
<https://pypi.python.org/pypi/pytest-localserver>.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Contributions are welcome, and they are greatly appreciated. Every bit
|
|
4
|
+
helps, and credit will always be given. You can contribute in many ways.
|
|
5
|
+
|
|
6
|
+
## Types of Contributions
|
|
7
|
+
|
|
8
|
+
### Report Bugs
|
|
9
|
+
|
|
10
|
+
Report bugs at <https://github.com/ip-tools/python-epo-ops-client/issues>.
|
|
11
|
+
|
|
12
|
+
If you are reporting a bug, please include:
|
|
13
|
+
|
|
14
|
+
- Your operating system name and version.
|
|
15
|
+
- Any details about your local setup that might be helpful in troubleshooting.
|
|
16
|
+
- Detailed steps to reproduce the bug.
|
|
17
|
+
|
|
18
|
+
### Fix Bugs
|
|
19
|
+
|
|
20
|
+
Look through the GitHub issues for bugs. Anything tagged with "bug" is open to
|
|
21
|
+
whoever wants to fix it.
|
|
22
|
+
|
|
23
|
+
### Implement Features
|
|
24
|
+
|
|
25
|
+
We are tracking all sorts of tasks within the [backlog](backlog.md) file and
|
|
26
|
+
as issues on GitHub. Just pick one, submit a corresponding patch, or start
|
|
27
|
+
a discussion around it. Anything tagged with "feature" is open to whoever wants
|
|
28
|
+
to implement it.
|
|
29
|
+
|
|
30
|
+
### Write Documentation
|
|
31
|
+
|
|
32
|
+
python-epo-ops-client could always use more documentation, whether as part of
|
|
33
|
+
the official python-epo-ops-client docs, in docstrings, or even on the web in
|
|
34
|
+
blog posts, articles, and such.
|
|
35
|
+
|
|
36
|
+
### Submit Feedback
|
|
37
|
+
|
|
38
|
+
The best way to send feedback is to file an issue at
|
|
39
|
+
https://github.com/ip-tools/python-epo-ops-client/issues.
|
|
40
|
+
|
|
41
|
+
If you are proposing a feature:
|
|
42
|
+
|
|
43
|
+
- Explain in detail how it would work.
|
|
44
|
+
- Keep the scope as narrow as possible, to make it easier to implement.
|
|
45
|
+
- Remember that this is a volunteer-driven project, and that contributions are
|
|
46
|
+
welcome. 😊
|
|
47
|
+
|
|
48
|
+
## Get Started!
|
|
49
|
+
|
|
50
|
+
Ready to contribute? Here's how to set up `python-epo-ops-client` for local
|
|
51
|
+
development. Before going into the forking process, you should first [install
|
|
52
|
+
a development sandbox](sandbox.md).
|
|
53
|
+
|
|
54
|
+
1. Fork the `python-epo-ops-client` repository on GitHub and clone your fork.
|
|
55
|
+
```shell
|
|
56
|
+
git clone git@github.com:[your_name_here]/python-epo-ops-client.git
|
|
57
|
+
cd python-epo-ops-client/
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
4. Create a branch for local development:
|
|
61
|
+
```shell
|
|
62
|
+
git switch -c <branchname>
|
|
63
|
+
```
|
|
64
|
+
Now, make your changes within your working tree.
|
|
65
|
+
|
|
66
|
+
5. After making changes to the code base, you may want to check that your
|
|
67
|
+
changes pass test and linting procedures. For that purpose, run
|
|
68
|
+
`make check`.
|
|
69
|
+
|
|
70
|
+
6. Commit your changes and push your branch to GitHub.
|
|
71
|
+
|
|
72
|
+
```shell
|
|
73
|
+
git add .
|
|
74
|
+
git commit -m "A detailed description of your changes."
|
|
75
|
+
git push origin <branchname>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
7. Submit a pull request through the GitHub website.
|
|
79
|
+
|
|
80
|
+
## Pull Request Guidelines
|
|
81
|
+
|
|
82
|
+
Before you submit a pull request, check that it meets these guidelines:
|
|
83
|
+
|
|
84
|
+
1. The pull request should include tests.
|
|
85
|
+
2. If the pull request adds functionality, the docs should be updated. Put your
|
|
86
|
+
new functionality into a function with a docstring, and add the feature to
|
|
87
|
+
the list in README.md.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Development Sandbox
|
|
2
|
+
|
|
3
|
+
Ready to contribute? This page describes how to set up `python-epo-ops-client`
|
|
4
|
+
for local development.
|
|
5
|
+
|
|
6
|
+
In order to learn how to fork the project, and submit changes back to mainline
|
|
7
|
+
on behalf of a pull request, see the [contributing guidelines](contributing.md).
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## Sandbox Setup
|
|
11
|
+
|
|
12
|
+
1. Acquire sources:
|
|
13
|
+
```shell
|
|
14
|
+
git clone https://github.com/ip-tools/python-epo-ops-client
|
|
15
|
+
cd python-epo-ops-client/
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
2. Install your local copy into a Python virtualenv, in order to isolate
|
|
19
|
+
it from your system Python.
|
|
20
|
+
```shell
|
|
21
|
+
python3 -m venv .venv
|
|
22
|
+
source .venv/bin/activate
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
3. Install required Python packages into development sandbox.
|
|
26
|
+
```shell
|
|
27
|
+
make install-develop
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
## Software Tests
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
### OPS Account Setup
|
|
35
|
+
|
|
36
|
+
Running the software tests require a working OPS account.
|
|
37
|
+
|
|
38
|
+
1. [Sign up for an OPS user login with EPO][ops registration].
|
|
39
|
+
2. Create an App at the OPS Console, which will provide you with a corresponding
|
|
40
|
+
pair of authentication credentials, the OPS application key and its secret.
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
### Prerequisites
|
|
44
|
+
|
|
45
|
+
Before running the software tests, you will need to define the
|
|
46
|
+
`OPS_KEY`, and `OPS_SECRET` environment variables.
|
|
47
|
+
|
|
48
|
+
You can either define them interactively using `export VARNAME=VALUE`, or store
|
|
49
|
+
them into an `.env` file within the same directory you are running the tests from.
|
|
50
|
+
See `example.env` for a blueprint.
|
|
51
|
+
|
|
52
|
+
```shell
|
|
53
|
+
export OPS_KEY=NKdGMmedZBGLRxTrUwCZMQCYp7Ak5a0u
|
|
54
|
+
export OPS_SECRET=v3vARPu7DFPEDB8i
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
_Note that the OPS credentials have been invalidated for demonstration purposes._
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
### Basics
|
|
61
|
+
|
|
62
|
+
```shell
|
|
63
|
+
make check
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
⚠️ Note that the software tests need a working internet connection, in order to
|
|
67
|
+
access the OPS service.
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
### Advanced Usage
|
|
71
|
+
|
|
72
|
+
In order to run either the linter, or the software tests exclusively,
|
|
73
|
+
use `make lint` vs. `make test`.
|
|
74
|
+
|
|
75
|
+
To focus on specific parts of the code base, you run a subset of the software
|
|
76
|
+
tests.
|
|
77
|
+
|
|
78
|
+
Run all tests in a specific module.
|
|
79
|
+
```shell
|
|
80
|
+
pytest tests/test_utils.py
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Run all tests containing "api".
|
|
84
|
+
```shell
|
|
85
|
+
pytest -k api
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
[ops registration]: https://developers.epo.org/user/register
|
|
@@ -8,7 +8,7 @@ from requests.exceptions import HTTPError
|
|
|
8
8
|
|
|
9
9
|
from . import exceptions
|
|
10
10
|
from .middlewares import Throttler
|
|
11
|
-
from .models import AccessToken, Request
|
|
11
|
+
from .models import NETWORK_TIMEOUT, AccessToken, Request
|
|
12
12
|
|
|
13
13
|
log = logging.getLogger(__name__)
|
|
14
14
|
|
|
@@ -134,7 +134,9 @@ class Client(object):
|
|
|
134
134
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
135
135
|
}
|
|
136
136
|
payload = {"grant_type": "client_credentials"}
|
|
137
|
-
response = requests.post(
|
|
137
|
+
response = requests.post(
|
|
138
|
+
self.__auth_url__, headers=headers, data=payload, timeout=NETWORK_TIMEOUT
|
|
139
|
+
)
|
|
138
140
|
response.raise_for_status()
|
|
139
141
|
self._access_token = AccessToken(response)
|
|
140
142
|
|
|
@@ -201,13 +203,13 @@ class Client(object):
|
|
|
201
203
|
else:
|
|
202
204
|
parts = parts_pre + parts_post
|
|
203
205
|
|
|
204
|
-
return
|
|
206
|
+
return "/".join(filter(None, parts))
|
|
205
207
|
|
|
206
208
|
# Service requests
|
|
207
209
|
# info: {service, reference_type, input, endpoint, constituents}
|
|
208
210
|
def _service_request(self, info):
|
|
209
211
|
_input = info["input"]
|
|
210
|
-
if
|
|
212
|
+
if isinstance(_input, list):
|
|
211
213
|
data = "\n".join([i.as_api_input() for i in _input])
|
|
212
214
|
info["input"] = _input[0]
|
|
213
215
|
else:
|
|
@@ -235,7 +237,8 @@ class Client(object):
|
|
|
235
237
|
if response.status_code != requests.codes.bad:
|
|
236
238
|
return response
|
|
237
239
|
|
|
238
|
-
|
|
240
|
+
# FIXME: S314 Using `xml` to parse untrusted data is known to be vulnerable to XML attacks; use `defusedxml` equivalents
|
|
241
|
+
message = ET.fromstring(response.content) # noqa: S314
|
|
239
242
|
if message.findtext("message") == "invalid_access_token":
|
|
240
243
|
self._acquire_token()
|
|
241
244
|
response = self._make_request(response.request.url, response.request.body)
|
|
@@ -15,7 +15,8 @@ from .helpers import kwarg_range_header_handler
|
|
|
15
15
|
|
|
16
16
|
log = logging.getLogger(__name__)
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
# FIXME: S108 Probable insecure usage of temporary file or directory: "/var/tmp/python-epo-ops-client/cache.dbm"
|
|
19
|
+
DEFAULT_DBM_PATH = "/var/tmp/python-epo-ops-client/cache.dbm" # noqa: S108
|
|
19
20
|
DEFAULT_TIMEOUT = 60 * 60 * 24 * 7 * 2 # 2 weeks in seconds
|
|
20
21
|
|
|
21
22
|
|