Skip to content

Commit

Permalink
Merge pull request #277 from JWCook/paginator
Browse files Browse the repository at this point in the history
Add async-compatible Paginator class for paginated iNatClient results
  • Loading branch information
JWCook committed Sep 12, 2021
2 parents 83c0623 + 6a51645 commit f256dcf
Show file tree
Hide file tree
Showing 43 changed files with 518 additions and 296 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-18.04
strategy:
matrix:
python-version: [3.6, 3.7, 3.8, 3.9, 3.10.0-rc.1]
python-version: [3.6, 3.7, 3.8, 3.9, 3.10.0-rc.2]

steps:
- uses: actions/checkout@v2
Expand Down
2 changes: 2 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
## 0.15.1 (2021-TBD)
* Remove `pyinaturalist.user_agent` global variable and API function keyword args, and recommend
setting on session object instead
* Fix `count_only=True`/`per_page=0` to not run full query


## 0.15.0 (2021-09-07)

Expand Down
4 changes: 2 additions & 2 deletions docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ pyinaturalist.models
:glob: true
:maxdepth: 1
modules/pyinaturalist.api_requests
modules/pyinaturalist.auth
modules/pyinaturalist.converters
modules/pyinaturalist.exceptions
modules/pyinaturalist.formatters
modules/pyinaturalist.pagination
modules/pyinaturalist.paginator
modules/pyinaturalist.request_params
modules/pyinaturalist.session
```
59 changes: 52 additions & 7 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion pyinaturalist/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@

# Ignore ImportErrors if this is imported outside a virtualenv
try:
from pyinaturalist.api_requests import ClientSession
from pyinaturalist.session import ClientSession
from pyinaturalist.auth import get_access_token
from pyinaturalist.client import iNatClient
from pyinaturalist.constants import *
from pyinaturalist.formatters import enable_logging, format_table, pprint
from pyinaturalist.models import *
from pyinaturalist.paginator import Paginator
from pyinaturalist.request_params import get_interval_ranges
from pyinaturalist.v0 import *
from pyinaturalist.v1 import *
Expand Down
2 changes: 1 addition & 1 deletion pyinaturalist/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
from keyring import get_password, set_password
from keyring.errors import KeyringError

from pyinaturalist.api_requests import post
from pyinaturalist.constants import API_V0_BASE_URL, KEYRING_KEY
from pyinaturalist.exceptions import AuthenticationError
from pyinaturalist.session import post

logger = getLogger(__name__)

Expand Down
46 changes: 33 additions & 13 deletions pyinaturalist/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@

from requests import Session

from pyinaturalist.api_requests import ClientSession
from pyinaturalist.auth import get_access_token
from pyinaturalist.constants import TOKEN_EXPIRATION, JsonResponse
from pyinaturalist.controllers import ObservationController, ProjectController, TaxonController
from pyinaturalist.models import T
from pyinaturalist.paginator import Paginator
from pyinaturalist.request_params import get_valid_kwargs, strip_empty_values
from pyinaturalist.session import ClientSession

logger = getLogger(__name__)

Expand Down Expand Up @@ -116,19 +118,11 @@ def _is_token_expired(self):
initialized = self._access_token and self._token_expires
return not (initialized and datetime.utcnow() < self._token_expires)

def request(
self, request_function: Callable, *args, auth: bool = False, **params
) -> JsonResponse:
def _update_kwargs(self, request_function, auth: bool = False, **kwargs):
"""Apply any applicable client settings to request parameters before sending a request.
Explicit keyword arguments will override any client settings.
Args:
request_function: The API request function to call, which should return a JSON response
args: Any positional arguments to pass to the request function
auth: Indicates that the request requires authentication
params: Original request parameters
"""
params = strip_empty_values(params)
kwargs = strip_empty_values(kwargs)
client_settings = {
'dry_run': self.dry_run,
'session': self.session,
Expand All @@ -140,5 +134,31 @@ def request(
client_settings.update(get_valid_kwargs(request_function, self.default_params))

for k, v in client_settings.items():
params.setdefault(k, v)
return request_function(*args, **params)
kwargs.setdefault(k, v)

return kwargs

def request(self, request_function: Callable, auth: bool = False, **kwargs) -> JsonResponse:
"""Send a request, with client settings applied
Args:
request_function: The API request function to call, which should return a JSON response
auth: Indicates that the request requires authentication
params: Original request parameters
"""
kwargs = self._update_kwargs(request_function, auth, **kwargs)
return request_function(**kwargs)

def paginate(
self, request_function: Callable, model: T, auth: bool = False, **kwargs
) -> Paginator[T]:
"""Create a paginator for a request, with client settings applied
Args:
request_function: The API request function to call, which should return a JSON response
model: Model class used for the response
auth: Indicates that the request requires authentication
params: Original request parameters
"""
kwargs = self._update_kwargs(request_function, auth, **kwargs)
return Paginator(request_function, model, **kwargs)

0 comments on commit f256dcf

Please sign in to comment.