Skip to content

Commit b36dad0

Browse files
authored
Merge pull request #704 from pyinat/refactor
Reorganize client-related modules into `client` subpackage
2 parents 31e3b01 + ea6d6b8 commit b36dad0

50 files changed

Lines changed: 316 additions & 333 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

HISTORY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ In addition, checking attributes on nested objects will not raise `AttributeErro
124124
* Deprecate `FileLockSQLiteBucket` in favor of `use_file_lock` (but still available for backwards-compatibility)
125125
* Drop support for python 3.8 and 3.9 (removed upstream)
126126
* Remove `pyinaturalist.rest_api` and `pyinaturalist.node_api` modules (deprecated in 0.14)
127+
* Remove `get_observation` (deprecated in 0.18 and replaced by `get_observations` and `get_observations_by_id`)
127128

128129
## 0.21.1 (2026-02-13)
129130
* Update `v2.create_observation()` and `update_observation()` to accept multiple observation field values, consistent with v0 and v1 APIs.

docs/reference.md

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,8 @@ pyinaturalist.models
3232
:glob: true
3333
:maxdepth: 1
3434
35-
modules/pyinaturalist.auth
3635
modules/pyinaturalist.converters
3736
modules/pyinaturalist.exceptions
3837
modules/pyinaturalist.formatters
39-
modules/pyinaturalist.paginator
4038
modules/pyinaturalist.request_params
41-
modules/pyinaturalist.session
4239
```

docs/user_guide/authentication.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ Direct keyring integration is provided via [python keyring](https://github.com/j
217217

218218
To store your credentials in the keyring, run {py:func}`.set_keyring_credentials`:
219219
```python
220-
>>> from pyinaturalist.auth import set_keyring_credentials
220+
>>> from pyinaturalist import set_keyring_credentials
221221
>>> set_keyring_credentials(
222222
>>> username='my_inaturalist_username',
223223
>>> password='my_inaturalist_password',

pyinaturalist/__init__.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
# ruff: noqa: F401, F403
22
# isort: skip_file
3-
from pyinaturalist.auth import get_access_token, get_access_token_via_auth_code
4-
from pyinaturalist.oauth_callback import build_authorize_url, get_auth_code_via_server
5-
from pyinaturalist.client import iNatClient
3+
from pyinaturalist.client import *
64
from pyinaturalist.constants import *
75
from pyinaturalist.formatters import enable_logging, format_table, pprint, pprint_tree
86
from pyinaturalist.models import *
9-
from pyinaturalist.paginator import Paginator, IDPaginator, WrapperPaginator
107
from pyinaturalist.request_params import get_interval_ranges
11-
from pyinaturalist.session import ClientSession, FileLockSQLiteBucket, clear_cache
128
from pyinaturalist.v0 import *
139
from pyinaturalist.v2 import *
1410
from pyinaturalist.v1 import *

pyinaturalist/client/__init__.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# ruff: noqa: F401, F403, F405
2+
# isort: skip_file
3+
from pyinaturalist.client.paginator import *
4+
from pyinaturalist.client.session import *
5+
from pyinaturalist.client.oauth import *
6+
from pyinaturalist.client.oauth_callback import *
7+
from pyinaturalist.client.client import iNatClient
8+
9+
__all__ = [
10+
'AutocompletePaginator',
11+
'ClientSession',
12+
'FileLockSQLiteBucket',
13+
'IDPaginator',
14+
'IDRangePaginator',
15+
'JsonPaginator',
16+
'Paginator',
17+
'WrapperPaginator',
18+
'build_authorize_url',
19+
'clear_cache',
20+
'delete',
21+
'get',
22+
'get_access_token',
23+
'get_access_token_via_auth_code',
24+
'get_auth_code_via_server',
25+
'get_local_session',
26+
'iNatClient',
27+
'paginate_all',
28+
'post',
29+
'put',
30+
'set_keyring_credentials',
31+
]
Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,13 @@
1111

1212
from requests import HTTPError
1313

14-
from pyinaturalist.auth import _decode_jwt_exp, get_access_token, get_access_token_via_auth_code
14+
from pyinaturalist.client.oauth import (
15+
_decode_jwt_exp,
16+
get_access_token,
17+
get_access_token_via_auth_code,
18+
)
19+
from pyinaturalist.client.paginator import Paginator
20+
from pyinaturalist.client.session import ClientSession
1521
from pyinaturalist.constants import RequestParams
1622
from pyinaturalist.controllers import (
1723
AnnotationController,
@@ -26,9 +32,7 @@
2632
)
2733
from pyinaturalist.exceptions import AuthenticationError
2834
from pyinaturalist.models import T
29-
from pyinaturalist.paginator import Paginator
3035
from pyinaturalist.request_params import get_valid_kwargs, strip_empty_values
31-
from pyinaturalist.session import ClientSession
3236

3337
JWT_EXPIRY_BUFFER = timedelta(seconds=60)
3438

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,18 @@
1111
from keyring.errors import KeyringError
1212
from requests import HTTPError, Response
1313

14-
from pyinaturalist.constants import API_V0, API_V1, KEYRING_KEY
15-
from pyinaturalist.exceptions import AuthenticationError
16-
from pyinaturalist.oauth_callback import (
14+
from pyinaturalist.client.oauth_callback import (
1715
_build_token_payload,
1816
_generate_pkce_pair,
1917
_obtain_auth_code,
2018
_resolve_auth_code_creds,
2119
build_authorize_url,
2220
)
23-
from pyinaturalist.session import ClientSession, get_local_session
21+
from pyinaturalist.client.session import ClientSession, get_local_session
22+
from pyinaturalist.constants import API_V0, API_V1, KEYRING_KEY
23+
from pyinaturalist.exceptions import AuthenticationError
2424

25-
logger = getLogger(__name__)
25+
_logger = getLogger(__name__)
2626

2727

2828
def _decode_jwt_exp(token: str) -> datetime | None:
@@ -115,7 +115,7 @@ def get_access_token(
115115
if not all(payload.values()):
116116
payload.update(get_keyring_credentials())
117117
if all(payload.values()):
118-
logger.info('Retrieved credentials from keyring')
118+
_logger.info('Retrieved credentials from keyring')
119119
else:
120120
raise AuthenticationError('Not all authentication parameters were provided')
121121

@@ -251,7 +251,7 @@ def _get_cached_jwt(refresh: bool) -> tuple[ClientSession, str | None]:
251251
session = get_local_session()
252252
response = _get_jwt(session, only_if_cached=True)
253253
if response.ok and not refresh:
254-
logger.info('Using cached access token')
254+
_logger.info('Using cached access token')
255255
return session, response.json()['api_token']
256256
return session, None
257257

@@ -280,7 +280,7 @@ def get_keyring_credentials() -> dict[str, str | None]:
280280
'client_secret': get_password(KEYRING_KEY, 'app_secret'),
281281
}
282282
except KeyringError as e:
283-
logger.warning(e)
283+
_logger.warning(e)
284284
return {}
285285

286286

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from pyinaturalist.constants import KEYRING_KEY, OAUTH_AUTHORIZE_URL
2020
from pyinaturalist.exceptions import AuthenticationError
2121

22-
logger = getLogger(__name__)
22+
_logger = getLogger(__name__)
2323

2424

2525
def get_auth_code_via_server(
@@ -62,7 +62,7 @@ def _serve_until_result():
6262
try:
6363
server.handle_request()
6464
except Exception as e:
65-
logger.exception(e)
65+
_logger.exception(e)
6666
break
6767
if result.auth_code or result.auth_error:
6868
break
@@ -72,7 +72,7 @@ def _serve_until_result():
7272
server_thread = threading.Thread(target=_serve_until_result, daemon=True)
7373
server_thread.start()
7474

75-
logger.info('Opening browser for authorization: %s', authorize_url)
75+
_logger.info('Opening browser for authorization: %s', authorize_url)
7676
(open_url or webbrowser.open)(authorize_url)
7777

7878
try:
@@ -141,7 +141,7 @@ def _resolve_auth_code_creds(
141141
if not use_pkce and not app_secret:
142142
app_secret = get_password(KEYRING_KEY, 'app_secret')
143143
except KeyringError as e:
144-
logger.warning(e)
144+
_logger.warning(e)
145145
if not app_id:
146146
raise AuthenticationError('app_id is required for authorization code flow')
147147
if not use_pkce and not app_secret:
@@ -183,7 +183,7 @@ def _obtain_auth_code(
183183
) -> str:
184184
"""Open the browser and obtain an authorization code via OOB or local server."""
185185
if use_oob:
186-
logger.info('Opening browser for authorization: %s', authorize_url)
186+
_logger.info('Opening browser for authorization: %s', authorize_url)
187187
(open_url or webbrowser.open)(authorize_url)
188188
if get_code:
189189
return get_code(authorize_url)
@@ -272,7 +272,7 @@ def do_GET(self):
272272
)
273273

274274
def log_message(self, format: str, *args: object):
275-
"""Route HTTP server logs through the module logger."""
276-
logger.debug(format, *args)
275+
"""Route HTTP server logs through the module _logger."""
276+
_logger.debug(format, *args)
277277

278278
return _OAuthCallbackHandler, result
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
)
2626
from pyinaturalist.models import T
2727

28-
logger = getLogger(__name__)
28+
_logger = getLogger(__name__)
2929

3030

3131
# TODO: Add per-endpoint 'max_per_page' parameter to use with Paginator.all()
@@ -69,7 +69,7 @@ def __init__(
6969
log_kwargs = {
7070
k: v for k, v in self.request_kwargs.items() if k not in ['session', 'access_token']
7171
}
72-
logger.debug(
72+
_logger.debug(
7373
f'Prepared paginated request: {self.request_function.__name__}'
7474
f'(args={self.request_args}, kwargs={log_kwargs})'
7575
)
@@ -194,13 +194,13 @@ def _estimate(self):
194194
"""
195195
total_requests = ceil(self.total_results / self.per_page)
196196
est_delay = ceil((total_requests / REQUESTS_PER_MINUTE) * 60) - 1
197-
logger.info(
197+
_logger.info(
198198
f'This query will fetch {self.total_results} results in {total_requests} requests. '
199199
f'Estimated total request time: {est_delay} seconds'
200200
)
201201

202202
if self.total_results > LARGE_REQUEST_WARNING:
203-
logger.warning(
203+
_logger.warning(
204204
'This request is larger than recommended for API usage. For bulk requests, consider '
205205
f'using the iNat export tool instead: {EXPORT_URL}'
206206
)

0 commit comments

Comments
 (0)