Skip to content

Commit

Permalink
Allow manipulation of default session configuration (#218)
Browse files Browse the repository at this point in the history
* git add .

* small fixes

* format

* defaults mechanism

* revert forcelist

* try reset

* revert

* revert base

* add to docs

* doc fixes

* better docs

* more docs

* add test

* format

* bool logic
  • Loading branch information
bdpedigo authored Sep 10, 2024
1 parent 064c6f7 commit 59742e0
Show file tree
Hide file tree
Showing 9 changed files with 234 additions and 31 deletions.
3 changes: 2 additions & 1 deletion caveclient/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
__version__ = "5.28.1"

from .frameworkclient import CAVEclient
from .session_config import get_session_defaults, set_session_defaults

__all__ = ["CAVEclient"]
__all__ = ["CAVEclient", "set_session_defaults", "get_session_defaults"]
4 changes: 2 additions & 2 deletions caveclient/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from packaging.specifiers import SpecifierSet
from packaging.version import Version

from .session_config import patch_session
from .session_config import _patch_session

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -193,7 +193,7 @@ def __init__(
self._default_url_mapping = {server_name: self._server_address}
self.verify = verify
self.session = requests.Session()
patch_session(
_patch_session(
self.session,
max_retries=max_retries,
pool_block=pool_block,
Expand Down
50 changes: 36 additions & 14 deletions caveclient/frameworkclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
from .materializationengine import MaterializationClient, MaterializationClientType
from .skeletonservice import SkeletonClient

DEFAULT_RETRIES = 3


class GlobalClientError(Exception):
pass
Expand All @@ -29,7 +27,7 @@ def __new__(
auth_token_key=None,
auth_token=None,
global_only=False,
max_retries=DEFAULT_RETRIES,
max_retries=None,
pool_maxsize=None,
pool_block=None,
desired_resolution=None,
Expand All @@ -42,9 +40,11 @@ def __new__(
This client wraps all the other clients and keeps track of the things that need to be consistent across them.
To instantiate a client:
from caveclient import CAVEclient
client = CAVEclient(datastack_name='my_datastack',
server_address='www.myserver.com',
auth_token_file='~/.mysecrets/secrets.json')
server_address='www.myserver.com',
auth_token_file='~/.mysecrets/secrets.json')
Then:
Expand Down Expand Up @@ -79,11 +79,15 @@ def __new__(
Direct entry of an auth token. If None, uses the file arguments to find the token.
Optional, default is None.
max_retries : int or None, optional
Sets the default number of retries on failed requests. Optional, by default 2.
Sets the default number of retries on failed requests.
If None, uses the value set in the session defaults.
pool_maxsize : int or None, optional
Sets the max number of threads in a requests pool, although this value will be exceeded if pool_block is set to False. Optional, uses requests defaults if None.
Sets the max number of threads in a requests pool, although this value will
be exceeded if pool_block is set to False. If None, uses the value set in
the session defaults.
pool_block: bool or None, optional
If True, prevents the number of threads in a requests pool from exceeding the max size. Optional, uses requests defaults (False) if None.
If True, prevents the number of threads in a requests pool from exceeding
the max size. If None, uses the value set in the session defaults.
desired_resolution : Iterable[float]or None, optional
If given, should be a list or array of the desired resolution you want queries returned in
useful for materialization queries.
Expand Down Expand Up @@ -133,7 +137,7 @@ def __init__(
auth_token_file=None,
auth_token_key=None,
auth_token=None,
max_retries=DEFAULT_RETRIES,
max_retries=None,
pool_maxsize=None,
pool_block=None,
info_cache=None,
Expand All @@ -143,9 +147,11 @@ def __init__(
This client wraps all the other clients and keeps track of the things that need to be consistent across them.
To instantiate a client:
from caveclient import CAVEclient
client = CAVEclient(datastack_name='my_datastack',
server_address='www.myserver.com',
auth_token_file='~/.mysecrets/secrets.json')
server_address='www.myserver.com',
auth_token_file='~/.mysecrets/secrets.json')
Then:
Expand Down Expand Up @@ -178,6 +184,13 @@ def __init__(
If True, prevents the number of threads in a requests pool from exceeding the max size. Optional, uses requests defaults (False) if None.
info_cache: dict or None, optional
Pre-computed info cache, bypassing the lookup of datastack info from the info service. Should only be used in cases where this information is cached and thus repetitive lookups can be avoided.
See Also
--------
[set_session_defaults](../extended_api/session_config.md/#caveclient.session_config.set_session_defaults)
[get_session_defaults](../extended_api/session_config.md/#caveclient.session_config.get_session_defaults)
"""
if server_address is None:
server_address = default_global_server_address
Expand Down Expand Up @@ -320,7 +333,7 @@ def __init__(
auth_token_file=default_token_file,
auth_token_key="token",
auth_token=None,
max_retries=DEFAULT_RETRIES,
max_retries=None,
pool_maxsize=None,
pool_block=None,
desired_resolution=None,
Expand All @@ -332,9 +345,11 @@ def __init__(
This client wraps all the other clients and keeps track of the things that need to be consistent across them.
To instantiate a client:
from caveclient import CAVEclient
client = CAVEclient(datastack_name='my_datastack',
server_address='www.myserver.com',
auth_token_file='~/.mysecrets/secrets.json')
server_address='www.myserver.com',
auth_token_file='~/.mysecrets/secrets.json')
Then
Expand Down Expand Up @@ -380,6 +395,13 @@ def __init__(
version:
The default materialization version of the datastack to use. If None, the
latest version is used. Optional, defaults to None.
See Also
--------
[set_session_defaults](../extended_api/session_config.md/#caveclient.session_config.set_session_defaults)
[get_session_defaults](../extended_api/session_config.md/#caveclient.session_config.get_session_defaults)
"""
super(CAVEclientFull, self).__init__(
server_address=server_address,
Expand Down
116 changes: 103 additions & 13 deletions caveclient/session_config.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,99 @@
from typing import Collection, Optional, Union

import requests
from urllib3.util.retry import Retry

DEFAULT_RETRIES = requests.adapters.DEFAULT_RETRIES
DEFAULT_POOLSIZE = requests.adapters.DEFAULT_POOLSIZE
DEFAULT_POOLBLOCK = requests.adapters.DEFAULT_POOLBLOCK
SESSION_DEFAULTS = {}


def set_session_defaults(
max_retries: int = 3,
pool_block: bool = False,
pool_maxsize: int = 10,
backoff_factor: Union[float, int] = 0.1,
backoff_max: Union[float, int] = 120,
status_forcelist: Optional[Collection] = (502, 503, 504),
) -> None:
"""Set global default values to configure how all clients will communicate with
servers. Should be done prior to initializing a client.
Note that these values are only used when not set at the client level.
Parameters
----------
max_retries :
The maximum number of retries each connection should attempt. Set to 0 to fail
on the first retry.
pool_block :
Whether the connection pool should block for connections.
pool_maxsize :
The maximum number of connections to save in the pool.
backoff_factor :
A backoff factor to apply between attempts after the second try (most errors
are resolved immediately by a second try without a delay). The query will sleep
for:
```{backoff factor} * (2 ^ ({number of total retries} - 1))``` seconds.
For example, if the `backoff_factor` is 0.1, then will sleep for
[0.0s, 0.2s, 0.4s, 0.8s, …] between retries. No backoff will ever be longer than
`backoff_max`.
backoff_max :
The maximum backoff time.
status_forcelist :
A set of integer HTTP status codes that we should force a retry on.
Usage
-----
from caveclient import set_session_defaults
set_session_defaults(
max_retries=5, # would increase the default number of retries
backoff_factor=0.5, # would increase the default backoff factor between retries
backoff_max=240, # would increase the default maximum backoff time
status_forcelist=(502, 503, 504, 505), # would add 505 to the default list
)
set_session_defaults() # would revert all defaults to their original values
Notes
-----
Calling this function will set the default values for all clients created after the
call.
Calling this function with any arguments missing will reset that value to the
default value.
See Also:
---------
def patch_session(
[urllib3.util.Retry][]
[requests.adapters.HTTPAdapter][]
"""
SESSION_DEFAULTS["max_retries"] = max_retries
SESSION_DEFAULTS["pool_block"] = pool_block
SESSION_DEFAULTS["pool_maxsize"] = pool_maxsize
SESSION_DEFAULTS["backoff_factor"] = backoff_factor
SESSION_DEFAULTS["backoff_max"] = backoff_max
SESSION_DEFAULTS["status_forcelist"] = status_forcelist


set_session_defaults()


def get_session_defaults() -> dict:
"""Get the current default values for session configuration.
Returns
-------
:
Dictionary of current default values for session configuration.
"""
return SESSION_DEFAULTS


def _patch_session(
session,
max_retries=None,
pool_block=None,
Expand All @@ -26,17 +113,20 @@ def patch_session(
Sets the max number of threads in the pool, by default None. If None, defaults to requests package default.
"""
if max_retries is None:
retries = DEFAULT_RETRIES
else:
retries = Retry(
total=max_retries,
backoff_factor=0.1,
status_forcelist=[502, 503, 504],
)
max_retries = SESSION_DEFAULTS["max_retries"]
if pool_block is None:
pool_block = DEFAULT_POOLBLOCK
pool_block = SESSION_DEFAULTS["pool_block"]
if pool_maxsize is None:
pool_maxsize = DEFAULT_POOLSIZE
pool_maxsize = SESSION_DEFAULTS["pool_maxsize"]

retries = Retry(
total=max_retries,
backoff_factor=SESSION_DEFAULTS["backoff_factor"],
status_forcelist=SESSION_DEFAULTS["status_forcelist"],
allowed_methods=frozenset(["GET", "POST"]),
backoff_max=SESSION_DEFAULTS["backoff_max"],
raise_on_status=False,
)

http = requests.adapters.HTTPAdapter(
pool_maxsize=pool_maxsize,
Expand Down
11 changes: 11 additions & 0 deletions docs/client_api/config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
title: Configuration
---

::: caveclient.set_session_defaults
options:
show_root_heading: true

::: caveclient.get_session_defaults
options:
show_root_heading: true
32 changes: 32 additions & 0 deletions docs/tutorials/advanced.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
title: Advanced Usage
---

## Changing session configurations

It is possible to change details of how a client talks to the various servers it needs
to interface with. For instance, the `CAVEclient` will attempt to retry
specific failed requests to the server, but will only try a specific number of times,
and will only wait specific amounts of time between retries. These values can be changed
via the `set_session_defaults` method. For instance, to change the number of retries to
5, and to increase the delay between subsequent retries, you could do:

```python
from caveclient import set_session_defaults

set_session_defaults(max_retries=5, backoff_factor=0.5)
```

Note that this needs to happen before initializing the client for this to work
properly. Some of these parameters are also adjustable at the client level.

To view the current session defaults, you can use the `get_session_defaults` method:

```python
from caveclient import get_session_defaults

client.get_session_defaults()
```

More information on the available parameters can be found in the
[API documentation](../client_api/config.md).
1 change: 1 addition & 0 deletions docs/tutorials/framework.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,4 @@ is named `client`, the subclients for each service are:
- EM Annotation Schemas : `client.schemas`
- JSON Neuroglancer State Service : `client.state`
- Skeleton Service : `client.skeletonservice`

3 changes: 3 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ nav:
- tutorials/schemas.md
- tutorials/state.md
- tutorials/materialization.md
- tutorials/advanced.md
- Client API: # these match the client property names
- client_api/index.md
- client_api/client.md
Expand All @@ -28,6 +29,7 @@ nav:
- client_api/materialize.md
- client_api/schema.md
- client_api/state.md
- client_api/config.md
- Extended API: # these match the python file names
- extended_api/index.md
- extended_api/annotationengine.md
Expand Down Expand Up @@ -107,6 +109,7 @@ plugins:
- https://networkx.org/documentation/stable/objects.inv
- https://docs.scipy.org/doc/scipy/objects.inv
- https://requests.readthedocs.io/en/latest/objects.inv
- https://urllib3.readthedocs.io/en/stable/objects.inv
options:
show_source: false
docstring_style: numpy
Expand Down
Loading

0 comments on commit 59742e0

Please sign in to comment.