boto3-refresh-session 2.0.5__py3-none-any.whl → 7.0.1__py3-none-any.whl

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.

Potentially problematic release.


This version of boto3-refresh-session might be problematic. Click here for more details.

@@ -1,9 +1,21 @@
1
- from .session import RefreshableSession
1
+ # This Source Code Form is subject to the terms of the Mozilla Public
2
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ # file, You can obtain one at https://mozilla.org/MPL/2.0/.
2
4
 
3
- __all__ = ["RefreshableSession"]
4
- __version__ = "2.0.5"
5
+ __all__ = []
6
+
7
+ from . import exceptions, session
8
+ from .exceptions import *
9
+ from .methods.custom import *
10
+ from .methods.iot import *
11
+ from .methods.sts import *
12
+ from .session import *
13
+
14
+ __all__.extend(session.__all__)
15
+ __all__.extend(exceptions.__all__)
16
+ __version__ = "7.0.1"
5
17
  __title__ = "boto3-refresh-session"
6
18
  __author__ = "Mike Letts"
7
19
  __maintainer__ = "Mike Letts"
8
- __license__ = "MIT"
20
+ __license__ = "MPL-2.0"
9
21
  __email__ = "lettsmt@gmail.com"
@@ -1,3 +1,26 @@
1
+ # This Source Code Form is subject to the terms of the Mozilla Public
2
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ # file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+
5
+ """Custom exception and warning types for boto3-refresh-session."""
6
+
7
+ __all__ = [
8
+ "BRSError",
9
+ "BRSValidationError",
10
+ "BRSConfigurationError",
11
+ "BRSCredentialError",
12
+ "BRSConnectionError",
13
+ "BRSRequestError",
14
+ "BRSCacheError",
15
+ "BRSCacheNotFoundError",
16
+ "BRSCacheExistsError",
17
+ "BRSWarning",
18
+ ]
19
+
20
+ import warnings
21
+ from typing import Any
22
+
23
+
1
24
  class BRSError(Exception):
2
25
  """The base exception for boto3-refresh-session.
3
26
 
@@ -5,17 +28,100 @@ class BRSError(Exception):
5
28
  ----------
6
29
  message : str, optional
7
30
  The message to raise.
31
+ code : str | int, optional
32
+ A short machine-friendly code for the error.
33
+ status_code : int, optional
34
+ An HTTP status code, if applicable.
35
+ details : dict[str, Any], optional
36
+ Extra structured metadata for debugging or logging.
37
+ param : str, optional
38
+ The parameter name related to the error.
39
+ value : Any, optional
40
+ The parameter value related to the error.
8
41
  """
9
42
 
10
- def __init__(self, message: str | None = None):
43
+ def __init__(
44
+ self,
45
+ message: str | None = None,
46
+ *,
47
+ code: str | int | None = None,
48
+ status_code: int | None = None,
49
+ details: dict[str, Any] | None = None,
50
+ param: str | None = None,
51
+ value: Any | None = None,
52
+ ):
11
53
  self.message = "" if message is None else message
54
+ self.code = code
55
+ self.status_code = status_code
56
+ self.details = details
57
+ self.param = param
58
+ self.value = value
12
59
  super().__init__(self.message)
13
60
 
14
61
  def __str__(self) -> str:
15
- return self.message
62
+ base = self.message
63
+ extras: list[str] = []
64
+ if self.code is not None:
65
+ extras.append(f"code={self.code!r}")
66
+ if self.status_code is not None:
67
+ extras.append(f"status_code={self.status_code!r}")
68
+ if self.param is not None:
69
+ extras.append(f"param={self.param!r}")
70
+ if self.value is not None:
71
+ extras.append(f"value={self.value!r}")
72
+ if self.details is not None:
73
+ extras.append(f"details={self.details!r}")
74
+ if extras:
75
+ if base:
76
+ return f"{base} ({', '.join(extras)})"
77
+ return ", ".join(extras)
78
+ return base
16
79
 
17
80
  def __repr__(self) -> str:
18
- return f"{self.__class__.__name__}({self.message!r})"
81
+ args = [repr(self.message)]
82
+ if self.code is not None:
83
+ args.append(f"code={self.code!r}")
84
+ if self.status_code is not None:
85
+ args.append(f"status_code={self.status_code!r}")
86
+ if self.param is not None:
87
+ args.append(f"param={self.param!r}")
88
+ if self.value is not None:
89
+ args.append(f"value={self.value!r}")
90
+ if self.details is not None:
91
+ args.append(f"details={self.details!r}")
92
+ return f"{self.__class__.__name__}({', '.join(args)})"
93
+
94
+
95
+ class BRSValidationError(BRSError):
96
+ """Raised when inputs are missing or invalid."""
97
+
98
+
99
+ class BRSConfigurationError(BRSError):
100
+ """Raised when configuration is incompatible or malformed."""
101
+
102
+
103
+ class BRSCredentialError(BRSError):
104
+ """Raised when credential data is malformed or incomplete."""
105
+
106
+
107
+ class BRSConnectionError(BRSError):
108
+ """Raised when a connection or transport fails."""
109
+
110
+
111
+ class BRSRequestError(BRSError):
112
+ """Raised when a remote request fails."""
113
+
114
+
115
+ class BRSCacheError(BRSError):
116
+ """Raised when cache operations violate the cache contract."""
117
+
118
+
119
+ class BRSCacheNotFoundError(BRSCacheError):
120
+ """Raised when a cached item is not found."""
121
+
122
+
123
+ class BRSCacheExistsError(BRSCacheError):
124
+ """Raised when attempting to insert an existing cached item."""
19
125
 
20
126
 
21
127
  class BRSWarning(UserWarning):
@@ -36,3 +142,9 @@ class BRSWarning(UserWarning):
36
142
 
37
143
  def __repr__(self) -> str:
38
144
  return f"{self.__class__.__name__}({self.message!r})"
145
+
146
+ @classmethod
147
+ def warn(cls, message: str, *, stacklevel: int = 2):
148
+ """Emits a BRSWarning with a consistent stacklevel."""
149
+
150
+ warnings.warn(cls(message), stacklevel=stacklevel)
@@ -0,0 +1,14 @@
1
+ # This Source Code Form is subject to the terms of the Mozilla Public
2
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ # file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+
5
+ __all__ = []
6
+
7
+ from . import custom, iot, sts
8
+ from .custom import *
9
+ from .iot import *
10
+ from .sts import *
11
+
12
+ __all__.extend(custom.__all__)
13
+ __all__.extend(iot.__all__)
14
+ __all__.extend(sts.__all__)
@@ -1,14 +1,23 @@
1
- from __future__ import annotations
1
+ # This Source Code Form is subject to the terms of the Mozilla Public
2
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ # file, You can obtain one at https://mozilla.org/MPL/2.0/.
2
4
 
3
- __all__ = ["CustomRefreshableSession"]
5
+ """Custom refreshable session using a user-provided credential getter."""
4
6
 
5
- from typing import Any, Callable
7
+ __all__ = ["CustomRefreshableSession"]
6
8
 
7
- from ..exceptions import BRSError
8
- from ..session import BaseRefreshableSession
9
- from ..utils import TemporaryCredentials
9
+ from ..exceptions import BRSCredentialError, BRSWarning
10
+ from ..utils import (
11
+ BaseRefreshableSession,
12
+ CustomCredentialsMethod,
13
+ CustomCredentialsMethodArgs,
14
+ Identity,
15
+ TemporaryCredentials,
16
+ refreshable_session,
17
+ )
10
18
 
11
19
 
20
+ @refreshable_session
12
21
  class CustomRefreshableSession(BaseRefreshableSession, registry_key="custom"):
13
22
  """A :class:`boto3.session.Session` object that automatically refreshes
14
23
  temporary credentials returned by a custom credential getter provided
@@ -17,11 +26,11 @@ class CustomRefreshableSession(BaseRefreshableSession, registry_key="custom"):
17
26
 
18
27
  Parameters
19
28
  ----------
20
- custom_credentials_method: Callable
29
+ custom_credentials_method: CustomCredentialsMethod
21
30
  Required. Accepts a callable object that returns temporary AWS
22
31
  security credentials. That object must return a dictionary containing
23
32
  'access_key', 'secret_key', 'token', and 'expiry_time' when called.
24
- custom_credentials_method_args : dict[str, Any], optional
33
+ custom_credentials_method_args : CustomCredentialsMethodArgs, optional
25
34
  Optional keyword arguments for the function passed to the
26
35
  ``custom_credentials_method`` parameter.
27
36
  defer_refresh : bool, optional
@@ -29,6 +38,23 @@ class CustomRefreshableSession(BaseRefreshableSession, registry_key="custom"):
29
38
  until they are explicitly needed. If ``False`` then temporary
30
39
  credentials refresh immediately upon expiration. It is highly
31
40
  recommended that you use ``True``. Default is ``True``.
41
+ advisory_timeout : int, optional
42
+ USE THIS ARGUMENT WITH CAUTION!!!
43
+
44
+ Botocore will attempt to refresh credentials early according to
45
+ this value (in seconds), but will continue using the existing
46
+ credentials if refresh fails. Default is 15 minutes (900 seconds).
47
+ mandatory_timeout : int, optional
48
+ USE THIS ARGUMENT WITH CAUTION!!!
49
+
50
+ Botocore requires a successful refresh before continuing. If
51
+ refresh fails in this window (in seconds), API calls may fail.
52
+ Default is 10 minutes (600 seconds).
53
+ cache_clients : bool, optional
54
+ If ``True`` then clients created by this session will be cached and
55
+ reused for subsequent calls to :meth:`client()` with the same
56
+ parameter signatures. Due to the memory overhead of clients, the
57
+ default is ``True`` in order to protect system resources.
32
58
 
33
59
  Other Parameters
34
60
  ----------------
@@ -61,26 +87,32 @@ class CustomRefreshableSession(BaseRefreshableSession, registry_key="custom"):
61
87
 
62
88
  def __init__(
63
89
  self,
64
- custom_credentials_method: Callable,
65
- custom_credentials_method_args: dict[str, Any] | None = None,
66
- defer_refresh: bool | None = None,
90
+ custom_credentials_method: CustomCredentialsMethod,
91
+ custom_credentials_method_args: (
92
+ CustomCredentialsMethodArgs | None
93
+ ) = None,
67
94
  **kwargs,
68
95
  ):
69
- super().__init__(**kwargs)
96
+ if "refresh_method" in kwargs:
97
+ BRSWarning.warn(
98
+ "'refresh_method' cannot be set manually. "
99
+ "Reverting to 'custom'."
100
+ )
101
+ del kwargs["refresh_method"]
102
+
103
+ # initializing BRSSession
104
+ super().__init__(refresh_method="custom", **kwargs)
70
105
 
71
- self._custom_get_credentials = custom_credentials_method
72
- self._custom_get_credentials_args = (
106
+ # initializing various other attributes
107
+ self._custom_get_credentials: CustomCredentialsMethod = (
108
+ custom_credentials_method
109
+ )
110
+ self._custom_get_credentials_args: CustomCredentialsMethodArgs = (
73
111
  custom_credentials_method_args
74
112
  if custom_credentials_method_args is not None
75
113
  else {}
76
114
  )
77
115
 
78
- self.initialize(
79
- credentials_method=self._get_credentials,
80
- defer_refresh=defer_refresh is not False,
81
- refresh_method="custom",
82
- )
83
-
84
116
  def _get_credentials(self) -> TemporaryCredentials:
85
117
  credentials: TemporaryCredentials = self._custom_get_credentials(
86
118
  **self._custom_get_credentials_args
@@ -88,20 +120,20 @@ class CustomRefreshableSession(BaseRefreshableSession, registry_key="custom"):
88
120
  required_keys = {"access_key", "secret_key", "token", "expiry_time"}
89
121
 
90
122
  if missing := required_keys - credentials.keys():
91
- raise BRSError(
92
- f"The dict returned by custom_credentials_method is missing "
93
- "these key-value pairs: "
94
- f"{', '.join(repr(param) for param in missing)}. "
123
+ raise BRSCredentialError(
124
+ "The dict returned by custom_credentials_method is missing "
125
+ "required key-value pairs.",
126
+ details={"missing": sorted(missing)},
95
127
  )
96
128
 
97
129
  return credentials
98
130
 
99
- def get_identity(self) -> dict[str, str]:
131
+ def get_identity(self) -> Identity:
100
132
  """Returns metadata about the custom credential getter.
101
133
 
102
134
  Returns
103
135
  -------
104
- dict[str, str]
136
+ Identity
105
137
  Dict containing information about the custom credential getter.
106
138
  """
107
139
 
@@ -0,0 +1,11 @@
1
+ # This Source Code Form is subject to the terms of the Mozilla Public
2
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ # file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+
5
+ __all__ = []
6
+
7
+ from . import core
8
+ from .core import IoTRefreshableSession
9
+ from .x509 import IOTX509RefreshableSession
10
+
11
+ __all__.extend(core.__all__)
@@ -1,42 +1,38 @@
1
+ # This Source Code Form is subject to the terms of the Mozilla Public
2
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ # file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+
5
+ """IoT refreshable session factory for selecting auth methods."""
6
+
1
7
  from __future__ import annotations
2
8
 
3
9
  __all__ = ["IoTRefreshableSession"]
4
10
 
5
11
  from typing import get_args
6
12
 
7
- from ...exceptions import BRSError
8
- from ...session import BaseRefreshableSession
13
+ from ...exceptions import BRSValidationError
9
14
  from ...utils import (
15
+ BaseIoTRefreshableSession,
16
+ BaseRefreshableSession,
10
17
  IoTAuthenticationMethod,
11
- BRSSession,
12
- CredentialProvider,
13
- Registry,
14
18
  )
15
19
 
16
20
 
17
- class BaseIoTRefreshableSession(
18
- Registry[IoTAuthenticationMethod],
19
- CredentialProvider,
20
- BRSSession,
21
- registry_key="__iot_sentinel__",
22
- ):
23
- def __init__(self, **kwargs):
24
- super().__init__(**kwargs)
25
-
26
-
27
21
  class IoTRefreshableSession(BaseRefreshableSession, registry_key="iot"):
28
22
  def __new__(
29
23
  cls,
30
- authentication_method: IoTAuthenticationMethod = "certificate",
24
+ authentication_method: IoTAuthenticationMethod = "x509",
31
25
  **kwargs,
32
26
  ) -> BaseIoTRefreshableSession:
33
27
  if authentication_method not in (
34
28
  methods := cls.get_available_authentication_methods()
35
29
  ):
36
- raise BRSError(
30
+ raise BRSValidationError(
37
31
  f"{authentication_method!r} is an invalid authentication "
38
32
  "method parameter. Available authentication methods are "
39
- f"{', '.join(repr(meth) for meth in methods)}."
33
+ f"{', '.join(repr(meth) for meth in methods)}.",
34
+ param="authentication_method",
35
+ value=authentication_method,
40
36
  )
41
37
 
42
38
  return BaseIoTRefreshableSession.registry[authentication_method](