boto3-refresh-session 1.3.11__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,8 +1,21 @@
1
- from .custom import CustomRefreshableSession
2
- from .ecs import ECSRefreshableSession
3
- from .session import RefreshableSession
4
- from .sts import STSRefreshableSession
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/.
5
4
 
6
- __all__ = ["RefreshableSession"]
7
- __version__ = "1.3.11"
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"
17
+ __title__ = "boto3-refresh-session"
8
18
  __author__ = "Mike Letts"
19
+ __maintainer__ = "Mike Letts"
20
+ __license__ = "MPL-2.0"
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__}({repr(self.message)})"
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):
@@ -35,4 +141,10 @@ class BRSWarning(UserWarning):
35
141
  return self.message
36
142
 
37
143
  def __repr__(self) -> str:
38
- return f"{self.__class__.__name__}({repr(self.message)})"
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__)
@@ -0,0 +1,145 @@
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 refreshable session using a user-provided credential getter."""
6
+
7
+ __all__ = ["CustomRefreshableSession"]
8
+
9
+ from ..exceptions import BRSCredentialError, BRSWarning
10
+ from ..utils import (
11
+ BaseRefreshableSession,
12
+ CustomCredentialsMethod,
13
+ CustomCredentialsMethodArgs,
14
+ Identity,
15
+ TemporaryCredentials,
16
+ refreshable_session,
17
+ )
18
+
19
+
20
+ @refreshable_session
21
+ class CustomRefreshableSession(BaseRefreshableSession, registry_key="custom"):
22
+ """A :class:`boto3.session.Session` object that automatically refreshes
23
+ temporary credentials returned by a custom credential getter provided
24
+ by the user. Useful for users with highly sophisticated or idiosyncratic
25
+ authentication flows.
26
+
27
+ Parameters
28
+ ----------
29
+ custom_credentials_method: CustomCredentialsMethod
30
+ Required. Accepts a callable object that returns temporary AWS
31
+ security credentials. That object must return a dictionary containing
32
+ 'access_key', 'secret_key', 'token', and 'expiry_time' when called.
33
+ custom_credentials_method_args : CustomCredentialsMethodArgs, optional
34
+ Optional keyword arguments for the function passed to the
35
+ ``custom_credentials_method`` parameter.
36
+ defer_refresh : bool, optional
37
+ If ``True`` then temporary credentials are not automatically refreshed
38
+ until they are explicitly needed. If ``False`` then temporary
39
+ credentials refresh immediately upon expiration. It is highly
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.
58
+
59
+ Other Parameters
60
+ ----------------
61
+ kwargs : dict
62
+ Optional keyword arguments for the :class:`boto3.session.Session`
63
+ object.
64
+
65
+ Examples
66
+ --------
67
+ Write (or import) the callable object for obtaining temporary AWS security
68
+ credentials.
69
+
70
+ >>> def your_custom_credential_getter(your_param, another_param):
71
+ >>> ...
72
+ >>> return {
73
+ >>> 'access_key': ...,
74
+ >>> 'secret_key': ...,
75
+ >>> 'token': ...,
76
+ >>> 'expiry_time': ...,
77
+ >>> }
78
+
79
+ Pass that callable object to ``RefreshableSession``.
80
+
81
+ >>> sess = RefreshableSession(
82
+ >>> method='custom',
83
+ >>> custom_credentials_method=your_custom_credential_getter,
84
+ >>> custom_credentials_method_args=...,
85
+ >>> )
86
+ """
87
+
88
+ def __init__(
89
+ self,
90
+ custom_credentials_method: CustomCredentialsMethod,
91
+ custom_credentials_method_args: (
92
+ CustomCredentialsMethodArgs | None
93
+ ) = None,
94
+ **kwargs,
95
+ ):
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)
105
+
106
+ # initializing various other attributes
107
+ self._custom_get_credentials: CustomCredentialsMethod = (
108
+ custom_credentials_method
109
+ )
110
+ self._custom_get_credentials_args: CustomCredentialsMethodArgs = (
111
+ custom_credentials_method_args
112
+ if custom_credentials_method_args is not None
113
+ else {}
114
+ )
115
+
116
+ def _get_credentials(self) -> TemporaryCredentials:
117
+ credentials: TemporaryCredentials = self._custom_get_credentials(
118
+ **self._custom_get_credentials_args
119
+ )
120
+ required_keys = {"access_key", "secret_key", "token", "expiry_time"}
121
+
122
+ if missing := required_keys - credentials.keys():
123
+ raise BRSCredentialError(
124
+ "The dict returned by custom_credentials_method is missing "
125
+ "required key-value pairs.",
126
+ details={"missing": sorted(missing)},
127
+ )
128
+
129
+ return credentials
130
+
131
+ def get_identity(self) -> Identity:
132
+ """Returns metadata about the custom credential getter.
133
+
134
+ Returns
135
+ -------
136
+ Identity
137
+ Dict containing information about the custom credential getter.
138
+ """
139
+
140
+ source = getattr(
141
+ self._custom_get_credentials,
142
+ "__name__",
143
+ repr(self._custom_get_credentials),
144
+ )
145
+ return {"method": "custom", "source": repr(source)}
@@ -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__)
@@ -0,0 +1,46 @@
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
+
7
+ from __future__ import annotations
8
+
9
+ __all__ = ["IoTRefreshableSession"]
10
+
11
+ from typing import get_args
12
+
13
+ from ...exceptions import BRSValidationError
14
+ from ...utils import (
15
+ BaseIoTRefreshableSession,
16
+ BaseRefreshableSession,
17
+ IoTAuthenticationMethod,
18
+ )
19
+
20
+
21
+ class IoTRefreshableSession(BaseRefreshableSession, registry_key="iot"):
22
+ def __new__(
23
+ cls,
24
+ authentication_method: IoTAuthenticationMethod = "x509",
25
+ **kwargs,
26
+ ) -> BaseIoTRefreshableSession:
27
+ if authentication_method not in (
28
+ methods := cls.get_available_authentication_methods()
29
+ ):
30
+ raise BRSValidationError(
31
+ f"{authentication_method!r} is an invalid authentication "
32
+ "method parameter. Available authentication methods are "
33
+ f"{', '.join(repr(meth) for meth in methods)}.",
34
+ param="authentication_method",
35
+ value=authentication_method,
36
+ )
37
+
38
+ return BaseIoTRefreshableSession.registry[authentication_method](
39
+ **kwargs
40
+ )
41
+
42
+ @classmethod
43
+ def get_available_authentication_methods(cls) -> list[str]:
44
+ args = list(get_args(IoTAuthenticationMethod))
45
+ args.remove("__iot_sentinel__")
46
+ return args