python-fotoware-client 0.2.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_fotoware_client-0.2.0/LICENSE +21 -0
- python_fotoware_client-0.2.0/PKG-INFO +160 -0
- python_fotoware_client-0.2.0/README.md +139 -0
- python_fotoware_client-0.2.0/pyproject.toml +57 -0
- python_fotoware_client-0.2.0/setup.cfg +4 -0
- python_fotoware_client-0.2.0/src/fotoware_client/__init__.py +62 -0
- python_fotoware_client-0.2.0/src/fotoware_client/auth.py +302 -0
- python_fotoware_client-0.2.0/src/fotoware_client/client.py +919 -0
- python_fotoware_client-0.2.0/src/fotoware_client/constants.py +13 -0
- python_fotoware_client-0.2.0/src/fotoware_client/credentials.py +59 -0
- python_fotoware_client-0.2.0/src/fotoware_client/exceptions.py +110 -0
- python_fotoware_client-0.2.0/src/fotoware_client/limits.py +133 -0
- python_fotoware_client-0.2.0/src/fotoware_client/models.py +190 -0
- python_fotoware_client-0.2.0/src/fotoware_client/py.typed +0 -0
- python_fotoware_client-0.2.0/src/fotoware_client/retry.py +166 -0
- python_fotoware_client-0.2.0/src/fotoware_client/settings.py +54 -0
- python_fotoware_client-0.2.0/src/python_fotoware_client.egg-info/PKG-INFO +160 -0
- python_fotoware_client-0.2.0/src/python_fotoware_client.egg-info/SOURCES.txt +27 -0
- python_fotoware_client-0.2.0/src/python_fotoware_client.egg-info/dependency_links.txt +1 -0
- python_fotoware_client-0.2.0/src/python_fotoware_client.egg-info/requires.txt +2 -0
- python_fotoware_client-0.2.0/src/python_fotoware_client.egg-info/top_level.txt +1 -0
- python_fotoware_client-0.2.0/tests/test_auth.py +269 -0
- python_fotoware_client-0.2.0/tests/test_client.py +510 -0
- python_fotoware_client-0.2.0/tests/test_credentials.py +81 -0
- python_fotoware_client-0.2.0/tests/test_exceptions.py +111 -0
- python_fotoware_client-0.2.0/tests/test_limits.py +54 -0
- python_fotoware_client-0.2.0/tests/test_models.py +208 -0
- python_fotoware_client-0.2.0/tests/test_retry.py +195 -0
- python_fotoware_client-0.2.0/tests/test_settings.py +103 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 Michael Hoppe
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: python-fotoware-client
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A thin, honest client for the FotoWare API: one call, one request; refuses queries FotoWare would answer wrongly; counts what each request costs
|
|
5
|
+
Author: Michael Hoppe
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/hoppefamily/python-fotoware-client
|
|
8
|
+
Project-URL: Source, https://github.com/hoppefamily/python-fotoware-client
|
|
9
|
+
Project-URL: Issues, https://github.com/hoppefamily/python-fotoware-client/issues
|
|
10
|
+
Keywords: fotoware,fotoweb,api,client
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
|
+
Classifier: Typing :: Typed
|
|
15
|
+
Requires-Python: <3.15,>=3.14
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: requests>=2.31.0
|
|
19
|
+
Requires-Dist: urllib3<3,>=2.6.3
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
|
|
22
|
+
# python-fotoware-client
|
|
23
|
+
|
|
24
|
+
[](https://github.com/hoppefamily/python-fotoware-client/actions/workflows/tests.yml)
|
|
25
|
+
[](https://github.com/hoppefamily/python-fotoware-client/actions/workflows/pre-commit.yml)
|
|
26
|
+
|
|
27
|
+
A thin, read-only Python client for the [FotoWare](https://www.fotoware.com/)
|
|
28
|
+
API (FotoWeb). It does what the API does and shows what that costs; it knows
|
|
29
|
+
nothing about any particular archive's fields.
|
|
30
|
+
|
|
31
|
+
Not affiliated with or endorsed by FotoWare AS. "FotoWare" and "FotoWeb" are
|
|
32
|
+
their trademarks.
|
|
33
|
+
|
|
34
|
+
## What it does
|
|
35
|
+
|
|
36
|
+
- **One call is one request.** `search_assets` returns one page; `next_page`
|
|
37
|
+
and `iter_search` follow `paging.next`. Nothing is batched or asked again
|
|
38
|
+
behind the caller's back. The only exception are the rendition downloads,
|
|
39
|
+
which poll until FotoWare has prepared the file.
|
|
40
|
+
- **Refuses a query FotoWare would answer wrongly.** Over FotoWare's size limit
|
|
41
|
+
a search comes back as a 500 or, worse, as an empty list that looks exactly
|
|
42
|
+
like "no hits". `search_assets` estimates the query first and raises
|
|
43
|
+
`QueryTooLargeError` over the budget, without sending it.
|
|
44
|
+
`estimate_query_size` is public, so callers can batch to stay under it.
|
|
45
|
+
- **Retries what is transient.** urllib3's `Retry` on the session retries 429
|
|
46
|
+
(honouring `Retry-After`) and 502/503/504, twice by default. A 500 is not
|
|
47
|
+
retried: it was always caused by the query.
|
|
48
|
+
- **Counts the cost.** `cost_snapshot()` reports requests, retries, errors,
|
|
49
|
+
search pages and empty search pages.
|
|
50
|
+
- OAuth client credentials with automatic token refresh; context manager.
|
|
51
|
+
- **Takes hrefs as FotoWare gives them.** Answers carry paths on the tenant;
|
|
52
|
+
every call that takes an href accepts a path or a full URL.
|
|
53
|
+
|
|
54
|
+
## FotoWare's limits
|
|
55
|
+
|
|
56
|
+
Measured on 2026-10-23 against FotoWeb 8.1.1223.0; details and edges in
|
|
57
|
+
`fotoware_client/limits.py`.
|
|
58
|
+
|
|
59
|
+
| Limit | Value |
|
|
60
|
+
| --- | --- |
|
|
61
|
+
| Assets per search answer | 25, with `pageSize` ignored. FotoWare guarantees no page size, so the client follows `paging.next` and counts nothing |
|
|
62
|
+
| Cost of an answer | ~0.4 s with hits whether 1 or 25 assets; 130–220 ms empty |
|
|
63
|
+
| Search size | about 2,000 by `estimate_query_size`; budget 1,500 |
|
|
64
|
+
|
|
65
|
+
So the cheapest search asks for as many terms as fit the budget and fills
|
|
66
|
+
whole pages.
|
|
67
|
+
|
|
68
|
+
Two search settings of an installation (Site Configuration › Searching) are out
|
|
69
|
+
of the client's sight:
|
|
70
|
+
|
|
71
|
+
- **Maximum number of hits** caps a result set. A search with more hits ends
|
|
72
|
+
early, without saying so.
|
|
73
|
+
- **Search string modification** decides how plain words without an operator
|
|
74
|
+
are combined (AND, OR or as a phrase). Write `AND`/`OR` explicitly, and the
|
|
75
|
+
setting does not matter.
|
|
76
|
+
|
|
77
|
+
## Settings of an installation
|
|
78
|
+
|
|
79
|
+
What a FotoWare installation may set differently, the client takes as
|
|
80
|
+
`ServerSettings`; the defaults are FotoWeb's:
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
from fotoware_client import FotoWareClient, ServerSettings
|
|
84
|
+
|
|
85
|
+
settings = ServerSettings(
|
|
86
|
+
original_rendition_name="Originaldatei", # display names are configurable
|
|
87
|
+
download_timeout=300, # seconds per download or rendition
|
|
88
|
+
rendition_poll_interval=0.5, # first wait while a rendition is made
|
|
89
|
+
query_size_budget=1200, # if the installation's limit is lower
|
|
90
|
+
)
|
|
91
|
+
client = FotoWareClient.from_env(settings=settings)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Also: `oauth_token_path`, `api_descriptor_path`, `rendition_poll_interval_max`
|
|
95
|
+
and `term_overhead`. See `fotoware_client/settings.py`.
|
|
96
|
+
|
|
97
|
+
## Credentials
|
|
98
|
+
|
|
99
|
+
Pass them to the constructor, or let `FotoWareClient.from_env()` find them:
|
|
100
|
+
|
|
101
|
+
1. the environment: `FOTOWARE_TENANT_URL`, `FOTOWARE_CLIENT_ID`,
|
|
102
|
+
`FOTOWARE_CLIENT_SECRET`;
|
|
103
|
+
2. else the credentials file `~/.config/fotoware/credentials`
|
|
104
|
+
(`$XDG_CONFIG_HOME/fotoware/credentials`), one `NAME=value` per line,
|
|
105
|
+
refused unless only its owner may read it (`chmod 600`).
|
|
106
|
+
|
|
107
|
+
Never on a command line, where a secret lands in the process list and the
|
|
108
|
+
shell history.
|
|
109
|
+
|
|
110
|
+
## Usage
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
from fotoware_client import FotoWareClient
|
|
114
|
+
|
|
115
|
+
with FotoWareClient.from_env() as client:
|
|
116
|
+
archive = next(
|
|
117
|
+
a for a in client.list_archives() if a.href.startswith("/fotoweb/archives/5000-")
|
|
118
|
+
)
|
|
119
|
+
query = "(631:4000000000001 OR 631:4000000000002) AND (dt:image OR dt:graphic)"
|
|
120
|
+
for page in client.iter_search(archive.href, query):
|
|
121
|
+
for asset in page.assets:
|
|
122
|
+
print(asset.filename)
|
|
123
|
+
print(client.cost_snapshot())
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Other calls: `get_asset`, `get_field_labels`, `get_rendition_url`, `download_original`,
|
|
127
|
+
`download_original_to_file`, `download_rendition_to_file`, `download_bytes`.
|
|
128
|
+
|
|
129
|
+
## Errors
|
|
130
|
+
|
|
131
|
+
All derive from `FotoWareError`:
|
|
132
|
+
|
|
133
|
+
| Error | When |
|
|
134
|
+
| --- | --- |
|
|
135
|
+
| `QueryTooLargeError` | the query is over the size budget; nothing sent |
|
|
136
|
+
| `RateLimitError` | a 429 outlasted the retries |
|
|
137
|
+
| `RetryExhaustedError` | a 502/503/504, connection error or timeout outlasted the retries |
|
|
138
|
+
| `NotFoundError` | a 404 |
|
|
139
|
+
| `APIError` | any other error status, a 500 among them |
|
|
140
|
+
| `AuthenticationError` | the token could not be fetched |
|
|
141
|
+
| `ConfigurationError` | the client was set up wrongly |
|
|
142
|
+
|
|
143
|
+
## Install
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
pip install python-fotoware-client
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Development
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
make venv
|
|
153
|
+
make lint type test
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Pre-commit hooks: `pip install pre-commit && pre-commit install`.
|
|
157
|
+
|
|
158
|
+
## License
|
|
159
|
+
|
|
160
|
+
MIT, see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# python-fotoware-client
|
|
2
|
+
|
|
3
|
+
[](https://github.com/hoppefamily/python-fotoware-client/actions/workflows/tests.yml)
|
|
4
|
+
[](https://github.com/hoppefamily/python-fotoware-client/actions/workflows/pre-commit.yml)
|
|
5
|
+
|
|
6
|
+
A thin, read-only Python client for the [FotoWare](https://www.fotoware.com/)
|
|
7
|
+
API (FotoWeb). It does what the API does and shows what that costs; it knows
|
|
8
|
+
nothing about any particular archive's fields.
|
|
9
|
+
|
|
10
|
+
Not affiliated with or endorsed by FotoWare AS. "FotoWare" and "FotoWeb" are
|
|
11
|
+
their trademarks.
|
|
12
|
+
|
|
13
|
+
## What it does
|
|
14
|
+
|
|
15
|
+
- **One call is one request.** `search_assets` returns one page; `next_page`
|
|
16
|
+
and `iter_search` follow `paging.next`. Nothing is batched or asked again
|
|
17
|
+
behind the caller's back. The only exception are the rendition downloads,
|
|
18
|
+
which poll until FotoWare has prepared the file.
|
|
19
|
+
- **Refuses a query FotoWare would answer wrongly.** Over FotoWare's size limit
|
|
20
|
+
a search comes back as a 500 or, worse, as an empty list that looks exactly
|
|
21
|
+
like "no hits". `search_assets` estimates the query first and raises
|
|
22
|
+
`QueryTooLargeError` over the budget, without sending it.
|
|
23
|
+
`estimate_query_size` is public, so callers can batch to stay under it.
|
|
24
|
+
- **Retries what is transient.** urllib3's `Retry` on the session retries 429
|
|
25
|
+
(honouring `Retry-After`) and 502/503/504, twice by default. A 500 is not
|
|
26
|
+
retried: it was always caused by the query.
|
|
27
|
+
- **Counts the cost.** `cost_snapshot()` reports requests, retries, errors,
|
|
28
|
+
search pages and empty search pages.
|
|
29
|
+
- OAuth client credentials with automatic token refresh; context manager.
|
|
30
|
+
- **Takes hrefs as FotoWare gives them.** Answers carry paths on the tenant;
|
|
31
|
+
every call that takes an href accepts a path or a full URL.
|
|
32
|
+
|
|
33
|
+
## FotoWare's limits
|
|
34
|
+
|
|
35
|
+
Measured on 2026-10-23 against FotoWeb 8.1.1223.0; details and edges in
|
|
36
|
+
`fotoware_client/limits.py`.
|
|
37
|
+
|
|
38
|
+
| Limit | Value |
|
|
39
|
+
| --- | --- |
|
|
40
|
+
| Assets per search answer | 25, with `pageSize` ignored. FotoWare guarantees no page size, so the client follows `paging.next` and counts nothing |
|
|
41
|
+
| Cost of an answer | ~0.4 s with hits whether 1 or 25 assets; 130–220 ms empty |
|
|
42
|
+
| Search size | about 2,000 by `estimate_query_size`; budget 1,500 |
|
|
43
|
+
|
|
44
|
+
So the cheapest search asks for as many terms as fit the budget and fills
|
|
45
|
+
whole pages.
|
|
46
|
+
|
|
47
|
+
Two search settings of an installation (Site Configuration › Searching) are out
|
|
48
|
+
of the client's sight:
|
|
49
|
+
|
|
50
|
+
- **Maximum number of hits** caps a result set. A search with more hits ends
|
|
51
|
+
early, without saying so.
|
|
52
|
+
- **Search string modification** decides how plain words without an operator
|
|
53
|
+
are combined (AND, OR or as a phrase). Write `AND`/`OR` explicitly, and the
|
|
54
|
+
setting does not matter.
|
|
55
|
+
|
|
56
|
+
## Settings of an installation
|
|
57
|
+
|
|
58
|
+
What a FotoWare installation may set differently, the client takes as
|
|
59
|
+
`ServerSettings`; the defaults are FotoWeb's:
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from fotoware_client import FotoWareClient, ServerSettings
|
|
63
|
+
|
|
64
|
+
settings = ServerSettings(
|
|
65
|
+
original_rendition_name="Originaldatei", # display names are configurable
|
|
66
|
+
download_timeout=300, # seconds per download or rendition
|
|
67
|
+
rendition_poll_interval=0.5, # first wait while a rendition is made
|
|
68
|
+
query_size_budget=1200, # if the installation's limit is lower
|
|
69
|
+
)
|
|
70
|
+
client = FotoWareClient.from_env(settings=settings)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Also: `oauth_token_path`, `api_descriptor_path`, `rendition_poll_interval_max`
|
|
74
|
+
and `term_overhead`. See `fotoware_client/settings.py`.
|
|
75
|
+
|
|
76
|
+
## Credentials
|
|
77
|
+
|
|
78
|
+
Pass them to the constructor, or let `FotoWareClient.from_env()` find them:
|
|
79
|
+
|
|
80
|
+
1. the environment: `FOTOWARE_TENANT_URL`, `FOTOWARE_CLIENT_ID`,
|
|
81
|
+
`FOTOWARE_CLIENT_SECRET`;
|
|
82
|
+
2. else the credentials file `~/.config/fotoware/credentials`
|
|
83
|
+
(`$XDG_CONFIG_HOME/fotoware/credentials`), one `NAME=value` per line,
|
|
84
|
+
refused unless only its owner may read it (`chmod 600`).
|
|
85
|
+
|
|
86
|
+
Never on a command line, where a secret lands in the process list and the
|
|
87
|
+
shell history.
|
|
88
|
+
|
|
89
|
+
## Usage
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
from fotoware_client import FotoWareClient
|
|
93
|
+
|
|
94
|
+
with FotoWareClient.from_env() as client:
|
|
95
|
+
archive = next(
|
|
96
|
+
a for a in client.list_archives() if a.href.startswith("/fotoweb/archives/5000-")
|
|
97
|
+
)
|
|
98
|
+
query = "(631:4000000000001 OR 631:4000000000002) AND (dt:image OR dt:graphic)"
|
|
99
|
+
for page in client.iter_search(archive.href, query):
|
|
100
|
+
for asset in page.assets:
|
|
101
|
+
print(asset.filename)
|
|
102
|
+
print(client.cost_snapshot())
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Other calls: `get_asset`, `get_field_labels`, `get_rendition_url`, `download_original`,
|
|
106
|
+
`download_original_to_file`, `download_rendition_to_file`, `download_bytes`.
|
|
107
|
+
|
|
108
|
+
## Errors
|
|
109
|
+
|
|
110
|
+
All derive from `FotoWareError`:
|
|
111
|
+
|
|
112
|
+
| Error | When |
|
|
113
|
+
| --- | --- |
|
|
114
|
+
| `QueryTooLargeError` | the query is over the size budget; nothing sent |
|
|
115
|
+
| `RateLimitError` | a 429 outlasted the retries |
|
|
116
|
+
| `RetryExhaustedError` | a 502/503/504, connection error or timeout outlasted the retries |
|
|
117
|
+
| `NotFoundError` | a 404 |
|
|
118
|
+
| `APIError` | any other error status, a 500 among them |
|
|
119
|
+
| `AuthenticationError` | the token could not be fetched |
|
|
120
|
+
| `ConfigurationError` | the client was set up wrongly |
|
|
121
|
+
|
|
122
|
+
## Install
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
pip install python-fotoware-client
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Development
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
make venv
|
|
132
|
+
make lint type test
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Pre-commit hooks: `pip install pre-commit && pre-commit install`.
|
|
136
|
+
|
|
137
|
+
## License
|
|
138
|
+
|
|
139
|
+
MIT, see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=69", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "python-fotoware-client"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "A thin, honest client for the FotoWare API: one call, one request; refuses queries FotoWare would answer wrongly; counts what each request costs"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.14,<3.15"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
keywords = ["fotoware", "fotoweb", "api", "client"]
|
|
14
|
+
authors = [{ name = "Michael Hoppe" }]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Programming Language :: Python :: 3.14",
|
|
19
|
+
"Typing :: Typed",
|
|
20
|
+
]
|
|
21
|
+
dependencies = [
|
|
22
|
+
"requests>=2.31.0",
|
|
23
|
+
"urllib3>=2.6.3,<3",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
Homepage = "https://github.com/hoppefamily/python-fotoware-client"
|
|
28
|
+
Source = "https://github.com/hoppefamily/python-fotoware-client"
|
|
29
|
+
Issues = "https://github.com/hoppefamily/python-fotoware-client/issues"
|
|
30
|
+
|
|
31
|
+
[tool.setuptools]
|
|
32
|
+
package-dir = {"" = "src"}
|
|
33
|
+
|
|
34
|
+
[tool.setuptools.packages.find]
|
|
35
|
+
where = ["src"]
|
|
36
|
+
|
|
37
|
+
[tool.setuptools.package-data]
|
|
38
|
+
fotoware_client = ["py.typed"]
|
|
39
|
+
|
|
40
|
+
[tool.vulture]
|
|
41
|
+
# src/ and any scripts, deliberately without tests/: a function whose only
|
|
42
|
+
# caller is its own test then has no caller at all and gets reported. With
|
|
43
|
+
# tests/ in this list it would look used, which is how such code survives --
|
|
44
|
+
# coverage counts a test as an execution, so it even reads as fully covered.
|
|
45
|
+
# Add every other directory that holds callers (scripts/, utility/, ...), or
|
|
46
|
+
# their code will be reported as dead.
|
|
47
|
+
paths = ["src", "vulture_whitelist.py"]
|
|
48
|
+
# 60, because that is where vulture reports an unused *function*. At 80 it
|
|
49
|
+
# only states what it is certain of -- unused imports, unreachable code, dead
|
|
50
|
+
# variables -- and ruff already catches most of that, so a gate at 80 lets an
|
|
51
|
+
# uncalled function straight through. Tried and measured, not assumed.
|
|
52
|
+
#
|
|
53
|
+
# 60 also guesses. In a repo that starts here the guesses are few; in one that
|
|
54
|
+
# adopts this later, run `vulture --make-whitelist` once and commit the result
|
|
55
|
+
# as a baseline, so the gate speaks only about code written from then on.
|
|
56
|
+
min_confidence = 60
|
|
57
|
+
sort_by_size = true
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""FotoWare API Client - Layer 1: Pure API."""
|
|
2
|
+
|
|
3
|
+
from fotoware_client.auth import OAuthAuthenticator, OAuthToken
|
|
4
|
+
from fotoware_client.client import FotoWareClient
|
|
5
|
+
from fotoware_client.constants import DEFAULT_TIMEOUT
|
|
6
|
+
from fotoware_client.credentials import credentials_file, load_credentials
|
|
7
|
+
from fotoware_client.exceptions import (
|
|
8
|
+
APIError,
|
|
9
|
+
AuthenticationError,
|
|
10
|
+
ConfigurationError,
|
|
11
|
+
FotoWareError,
|
|
12
|
+
NotFoundError,
|
|
13
|
+
RateLimitError,
|
|
14
|
+
RetryExhaustedError,
|
|
15
|
+
)
|
|
16
|
+
from fotoware_client.limits import (
|
|
17
|
+
QUERY_SIZE_BUDGET,
|
|
18
|
+
QUERY_SIZE_LIMIT,
|
|
19
|
+
QueryTooLargeError,
|
|
20
|
+
estimate_query_size,
|
|
21
|
+
)
|
|
22
|
+
from fotoware_client.models import Archive, Asset, SearchResult
|
|
23
|
+
from fotoware_client.retry import NO_RETRY, STANDARD_RETRY, RetryConfig
|
|
24
|
+
from fotoware_client.settings import ServerSettings
|
|
25
|
+
|
|
26
|
+
__version__ = "0.2.0"
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
# Client
|
|
30
|
+
"FotoWareClient",
|
|
31
|
+
# Models
|
|
32
|
+
"Archive",
|
|
33
|
+
"Asset",
|
|
34
|
+
"SearchResult",
|
|
35
|
+
# Auth
|
|
36
|
+
"OAuthAuthenticator",
|
|
37
|
+
"OAuthToken",
|
|
38
|
+
# Exceptions
|
|
39
|
+
"FotoWareError",
|
|
40
|
+
"AuthenticationError",
|
|
41
|
+
"RateLimitError",
|
|
42
|
+
"NotFoundError",
|
|
43
|
+
"APIError",
|
|
44
|
+
"ConfigurationError",
|
|
45
|
+
"RetryExhaustedError",
|
|
46
|
+
"QueryTooLargeError",
|
|
47
|
+
# Installation
|
|
48
|
+
"ServerSettings",
|
|
49
|
+
# Credentials
|
|
50
|
+
"credentials_file",
|
|
51
|
+
"load_credentials",
|
|
52
|
+
# Limits
|
|
53
|
+
"QUERY_SIZE_BUDGET",
|
|
54
|
+
"QUERY_SIZE_LIMIT",
|
|
55
|
+
"estimate_query_size",
|
|
56
|
+
# Retry
|
|
57
|
+
"RetryConfig",
|
|
58
|
+
"STANDARD_RETRY",
|
|
59
|
+
"NO_RETRY",
|
|
60
|
+
# Constants
|
|
61
|
+
"DEFAULT_TIMEOUT",
|
|
62
|
+
]
|