ssm-cache 2.9__tar.gz → 3.0.0__tar.gz
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.
- ssm_cache-3.0.0/LICENSE +21 -0
- ssm_cache-3.0.0/PKG-INFO +467 -0
- ssm_cache-3.0.0/README.md +434 -0
- ssm_cache-3.0.0/pyproject.toml +95 -0
- ssm_cache-3.0.0/ssm_cache/__init__.py +21 -0
- ssm_cache-3.0.0/ssm_cache/exceptions.py +13 -0
- ssm_cache-3.0.0/ssm_cache/filters.py +91 -0
- ssm_cache-3.0.0/ssm_cache/groups.py +121 -0
- ssm_cache-3.0.0/ssm_cache/parameters.py +133 -0
- ssm_cache-3.0.0/ssm_cache/py.typed +0 -0
- ssm_cache-3.0.0/ssm_cache/refreshable.py +180 -0
- ssm_cache-3.0.0/ssm_cache/utils.py +20 -0
- ssm_cache-3.0.0/ssm_cache.egg-info/PKG-INFO +467 -0
- ssm_cache-3.0.0/ssm_cache.egg-info/SOURCES.txt +23 -0
- ssm_cache-3.0.0/ssm_cache.egg-info/requires.txt +12 -0
- {ssm-cache-2.9 → ssm_cache-3.0.0}/ssm_cache.egg-info/top_level.txt +1 -1
- ssm_cache-3.0.0/tests/test_filters.py +165 -0
- ssm_cache-3.0.0/tests/test_groups.py +279 -0
- ssm_cache-3.0.0/tests/test_parameters.py +105 -0
- ssm_cache-3.0.0/tests/test_refreshable.py +229 -0
- ssm_cache-3.0.0/tests/test_secrets.py +64 -0
- ssm_cache-3.0.0/tests/test_utils.py +46 -0
- ssm_cache-3.0.0/tests/test_versioning.py +169 -0
- ssm-cache-2.9/PKG-INFO +0 -385
- ssm-cache-2.9/README.md +0 -372
- ssm-cache-2.9/setup.py +0 -21
- ssm-cache-2.9/ssm_cache/__init__.py +0 -9
- ssm-cache-2.9/ssm_cache/cache.py +0 -350
- ssm-cache-2.9/ssm_cache/filters.py +0 -85
- ssm-cache-2.9/ssm_cache.egg-info/PKG-INFO +0 -385
- ssm-cache-2.9/ssm_cache.egg-info/SOURCES.txt +0 -18
- ssm-cache-2.9/ssm_cache.egg-info/requires.txt +0 -2
- ssm-cache-2.9/tests/__init__.py +0 -65
- ssm-cache-2.9/tests/cache_test.py +0 -244
- ssm-cache-2.9/tests/decorator_test.py +0 -146
- ssm-cache-2.9/tests/filters_test.py +0 -255
- ssm-cache-2.9/tests/hierarchy_test.py +0 -233
- ssm-cache-2.9/tests/override_test.py +0 -98
- ssm-cache-2.9/tests/secrets_test.py +0 -84
- ssm-cache-2.9/tests/versioning_test.py +0 -185
- {ssm-cache-2.9 → ssm_cache-3.0.0}/setup.cfg +0 -0
- {ssm-cache-2.9 → ssm_cache-3.0.0}/ssm_cache.egg-info/dependency_links.txt +0 -0
ssm_cache-3.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018 Alex Casalboni
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
ssm_cache-3.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ssm-cache
|
|
3
|
+
Version: 3.0.0
|
|
4
|
+
Summary: AWS System Manager Parameter Store caching client for Python
|
|
5
|
+
Author-email: Alex Casalboni <alex@alexcasalboni.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/alexcasalboni/ssm-cache-python
|
|
8
|
+
Project-URL: Repository, https://github.com/alexcasalboni/ssm-cache-python
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/alexcasalboni/ssm-cache-python/issues
|
|
10
|
+
Keywords: aws,amazon-web-services,aws-lambda,aws-ssm,parameter-store
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: boto3>=1.5.0
|
|
22
|
+
Requires-Dist: botocore>=1.7.12
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: pytest>=8.3; extra == "dev"
|
|
25
|
+
Requires-Dist: pytest-cov>=5.0; extra == "dev"
|
|
26
|
+
Requires-Dist: moto>=4.0; extra == "dev"
|
|
27
|
+
Requires-Dist: freezegun>=1.5.5; extra == "dev"
|
|
28
|
+
Requires-Dist: placebo>=0.10.0; extra == "dev"
|
|
29
|
+
Requires-Dist: ruff>=0.15; extra == "dev"
|
|
30
|
+
Requires-Dist: mypy>=1.11; extra == "dev"
|
|
31
|
+
Requires-Dist: coveralls>=4.0; extra == "dev"
|
|
32
|
+
Dynamic: license-file
|
|
33
|
+
|
|
34
|
+
AWS System Manager Parameter Store Caching Client for Python
|
|
35
|
+
==========================================================
|
|
36
|
+
|
|
37
|
+
[](https://github.com/alexcasalboni/ssm-cache-python/actions/workflows/ci.yml)
|
|
38
|
+
[](https://coveralls.io/github/alexcasalboni/ssm-cache-python)
|
|
39
|
+
[](https://badge.fury.io/py/ssm-cache)
|
|
40
|
+
[](https://pypi.org/project/ssm-cache/)
|
|
41
|
+
[](https://github.com/alexcasalboni/ssm-cache-python/blob/master/LICENSE)
|
|
42
|
+
[](https://GitHub.com/alexcasalboni/ssm-cache-python/graphs/commit-activity)
|
|
43
|
+
[](https://github.com/alexcasalboni/ssm-cache-python/issues)
|
|
44
|
+
[](https://github.com/alexcasalboni/ssm-cache-python/stargazers)
|
|
45
|
+
|
|
46
|
+
This module wraps the AWS Parameter Store and adds a caching and grouping layer with max-age invalidation.
|
|
47
|
+
|
|
48
|
+
You can use this module with AWS Lambda to read and refresh parameters and secrets. Your IAM role will require `ssm:GetParameters` permissions (optionally, also `kms:Decrypt` if you use `SecureString` params).
|
|
49
|
+
|
|
50
|
+
## Package structure
|
|
51
|
+
|
|
52
|
+
The library is split into focused modules — import from the top-level `ssm_cache` package for everyday use, or from a specific submodule when you need internals (e.g. for testing or extension).
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
ssm_cache/
|
|
56
|
+
├── __init__.py # public re-exports + __version__
|
|
57
|
+
├── exceptions.py # InvalidParameterError, InvalidPathError, InvalidVersionError
|
|
58
|
+
├── filters.py # SSMFilter, SSMFilterType, SSMFilterKeyId, …
|
|
59
|
+
├── groups.py # SSMParameterGroup
|
|
60
|
+
├── parameters.py # SSMParameter, SecretsManagerParameter
|
|
61
|
+
├── refreshable.py # Refreshable base class (caching, refresh_on_error, set_ssm_client)
|
|
62
|
+
└── utils.py # utcnow(), batch()
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The version is available at runtime:
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
import ssm_cache
|
|
69
|
+
print(ssm_cache.__version__) # e.g. "3.0.0"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## How to install
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pip install ssm-cache
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Dev and test dependencies are declared as an optional group in `pyproject.toml` and can be installed with:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
pip install "ssm-cache[dev]"
|
|
82
|
+
# or, from a local clone:
|
|
83
|
+
pip install -e ".[dev]"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## How to use it
|
|
87
|
+
|
|
88
|
+
### Simplest use case
|
|
89
|
+
|
|
90
|
+
A single parameter, configured by name.
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from ssm_cache import SSMParameter
|
|
94
|
+
param = SSMParameter('my_param_name')
|
|
95
|
+
value = param.value
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### With cache invalidation
|
|
99
|
+
|
|
100
|
+
You can configure the `max_age` in seconds, after which the values will be automatically refreshed.
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
from ssm_cache import SSMParameter
|
|
104
|
+
param_1 = SSMParameter('param_1', max_age=300) # 5 min
|
|
105
|
+
value_1 = param_1.value
|
|
106
|
+
|
|
107
|
+
param_2 = SSMParameter('param_2', max_age=3600) # 1 hour
|
|
108
|
+
value_2 = param_2.value
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### With multiple parameters
|
|
112
|
+
|
|
113
|
+
You can configure more than one parameter to be fetched, cached, and decrypted as a group.
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
from ssm_cache import SSMParameterGroup
|
|
117
|
+
group = SSMParameterGroup(max_age=300)
|
|
118
|
+
param_1 = group.parameter('param_1')
|
|
119
|
+
param_2 = group.parameter('param_2')
|
|
120
|
+
|
|
121
|
+
value_1 = param_1.value
|
|
122
|
+
value_2 = param_2.value
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### With hierarchical parameters
|
|
126
|
+
|
|
127
|
+
You can fetch and cache a group of parameters under a given prefix. Optionally, the group itself can have its own base path.
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
from ssm_cache import SSMParameterGroup
|
|
131
|
+
group = SSMParameterGroup(base_path="/Foo")
|
|
132
|
+
foo_bar = group.parameter('/Bar') # fetches /Foo/Bar
|
|
133
|
+
baz_params = group.parameters('/Baz') # fetches /Foo/Baz/1, /Foo/Baz/2, …
|
|
134
|
+
|
|
135
|
+
assert len(group) == 3
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
`group.parameters(...)` can be called multiple times. When caching is enabled, the group's expiry is anchored to the earliest `parameters()` call, so all prefixes age out together.
|
|
139
|
+
|
|
140
|
+
#### Hierarchical parameters and filters
|
|
141
|
+
|
|
142
|
+
Filter by parameter `Type` or KMS `KeyId`, either with a raw dict or a typed class (which validates values before the API call).
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
from ssm_cache import SSMParameterGroup
|
|
146
|
+
from ssm_cache.filters import SSMFilterType, SSMFilterKeyId
|
|
147
|
+
|
|
148
|
+
group = SSMParameterGroup()
|
|
149
|
+
|
|
150
|
+
# raw dict
|
|
151
|
+
params = group.parameters(
|
|
152
|
+
path="/Foo/Bar",
|
|
153
|
+
filters=[{'Key': 'Type', 'Option': 'Equals', 'Values': ['StringList']}],
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
# typed class — validates allowed values before calling the API
|
|
157
|
+
params = group.parameters(
|
|
158
|
+
path="/Foo/Bar",
|
|
159
|
+
filters=[SSMFilterType().value('StringList')],
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
# KeyId filter, begins-with
|
|
163
|
+
params = group.parameters(
|
|
164
|
+
path="/Foo/Bar",
|
|
165
|
+
filters=[SSMFilterKeyId('BeginsWith').value('alias/')],
|
|
166
|
+
)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
#### Non-recursive fetch
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
from ssm_cache import SSMParameterGroup
|
|
173
|
+
group = SSMParameterGroup()
|
|
174
|
+
|
|
175
|
+
# fetches /Foo/1, /Foo/2 but NOT /Foo/Bar/1
|
|
176
|
+
params = group.parameters(path="/Foo", recursive=False)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### With StringList parameters
|
|
180
|
+
|
|
181
|
+
`StringList` parameters are automatically split on commas and returned as Python lists.
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
from ssm_cache import SSMParameter
|
|
185
|
+
# "my_twitter_api_keys" is a StringList (four comma-separated values)
|
|
186
|
+
twitter_params = SSMParameter('my_twitter_api_keys')
|
|
187
|
+
key, secret, access_token, access_token_secret = twitter_params.value
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Explicit refresh
|
|
191
|
+
|
|
192
|
+
Force a refresh on a parameter or group at any time. When a parameter belongs to a group, refreshing it refreshes the whole group.
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
from ssm_cache import SSMParameter
|
|
196
|
+
param = SSMParameter('my_param_name')
|
|
197
|
+
value = param.value
|
|
198
|
+
param.refresh()
|
|
199
|
+
new_value = param.value
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
```python
|
|
203
|
+
from ssm_cache import SSMParameterGroup
|
|
204
|
+
group = SSMParameterGroup()
|
|
205
|
+
param_1 = group.parameter('param_1')
|
|
206
|
+
param_2 = group.parameter('param_2')
|
|
207
|
+
|
|
208
|
+
value_1 = param_1.value
|
|
209
|
+
value_2 = param_2.value
|
|
210
|
+
|
|
211
|
+
group.refresh() # refreshes all params in the group
|
|
212
|
+
param_1.refresh() # also refreshes the whole group
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Without decryption
|
|
216
|
+
|
|
217
|
+
Decryption is enabled by default. Disable it explicitly for `SSMParameter` or `SSMParameterGroup`.
|
|
218
|
+
|
|
219
|
+
```python
|
|
220
|
+
from ssm_cache import SSMParameter
|
|
221
|
+
param = SSMParameter('my_param_name', with_decryption=False)
|
|
222
|
+
value = param.value
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### AWS Secrets Manager integration
|
|
226
|
+
|
|
227
|
+
`SecretsManagerParameter` provides the same interface as `SSMParameter` and transparently accesses Secrets Manager values via the SSM parameter path `/aws/reference/secretsmanager/<name>`.
|
|
228
|
+
|
|
229
|
+
```python
|
|
230
|
+
from ssm_cache import SecretsManagerParameter
|
|
231
|
+
secret = SecretsManagerParameter('my_secret_name')
|
|
232
|
+
value = secret.value
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Secrets can be mixed with regular parameters inside a `SSMParameterGroup`. No group base path is applied to secrets.
|
|
236
|
+
|
|
237
|
+
```python
|
|
238
|
+
from ssm_cache import SSMParameterGroup
|
|
239
|
+
group = SSMParameterGroup()
|
|
240
|
+
param = group.parameter('my_param')
|
|
241
|
+
secret = group.secret('my_secret')
|
|
242
|
+
|
|
243
|
+
param_value = param.value
|
|
244
|
+
secret_value = secret.value
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Passing a name that starts with `/` raises `InvalidParameterError` immediately, since that would be ambiguous with a raw SSM path.
|
|
248
|
+
|
|
249
|
+
### Versioning support
|
|
250
|
+
|
|
251
|
+
SSM Parameter Store supports [version selectors](https://docs.aws.amazon.com/systems-manager/latest/userguide/sysman-paramstore-versions.html). Omitting the version always fetches the latest.
|
|
252
|
+
|
|
253
|
+
```python
|
|
254
|
+
from ssm_cache import SSMParameter
|
|
255
|
+
|
|
256
|
+
# always fetches the latest version
|
|
257
|
+
param = SSMParameter('my_param_name')
|
|
258
|
+
print(param.version) # int
|
|
259
|
+
|
|
260
|
+
# pinned to version 2 — refresh() will NOT advance to a newer version
|
|
261
|
+
param_v2 = SSMParameter('my_param_name:2')
|
|
262
|
+
value = param_v2.value
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## Usage with AWS Lambda
|
|
266
|
+
|
|
267
|
+
Parameters and secrets are initialised once outside the handler, so the cache persists across warm invocations.
|
|
268
|
+
|
|
269
|
+
```python
|
|
270
|
+
from ssm_cache import SSMParameter, SecretsManagerParameter
|
|
271
|
+
|
|
272
|
+
param = SSMParameter('my_param_name', max_age=300)
|
|
273
|
+
secret = SecretsManagerParameter('my_secret_name', max_age=300)
|
|
274
|
+
|
|
275
|
+
def lambda_handler(event, context):
|
|
276
|
+
dbname = param.value
|
|
277
|
+
password = secret.value
|
|
278
|
+
return f'Hello from Lambda with dbname {dbname}'
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## Complex invalidation based on signals
|
|
282
|
+
|
|
283
|
+
Explicitly call `refresh()` when an application-level error signals that a cached value is stale.
|
|
284
|
+
|
|
285
|
+
```python
|
|
286
|
+
from ssm_cache import SSMParameter
|
|
287
|
+
from my_db_lib import Client, InvalidCredentials # pseudo-code
|
|
288
|
+
|
|
289
|
+
param = SSMParameter('my_db_password')
|
|
290
|
+
my_db_client = Client(password=param.value)
|
|
291
|
+
|
|
292
|
+
def read_record(is_retry=False):
|
|
293
|
+
try:
|
|
294
|
+
return my_db_client.read_record()
|
|
295
|
+
except InvalidCredentials:
|
|
296
|
+
if not is_retry:
|
|
297
|
+
param.refresh()
|
|
298
|
+
my_db_client = Client(password=param.value)
|
|
299
|
+
return read_record(is_retry=True)
|
|
300
|
+
|
|
301
|
+
def lambda_handler(event, context):
|
|
302
|
+
return {'record': read_record()}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## Decorator utility
|
|
306
|
+
|
|
307
|
+
`refresh_on_error` codifies the retry pattern above as a decorator on any `SSMParameter` or `SSMParameterGroup` instance.
|
|
308
|
+
|
|
309
|
+
```python
|
|
310
|
+
from ssm_cache import SSMParameter
|
|
311
|
+
from my_db_lib import Client, InvalidCredentials # pseudo-code
|
|
312
|
+
|
|
313
|
+
param = SSMParameter('my_db_password')
|
|
314
|
+
my_db_client = Client(password=param.value)
|
|
315
|
+
|
|
316
|
+
def on_error_callback():
|
|
317
|
+
my_db_client = Client(password=param.value)
|
|
318
|
+
|
|
319
|
+
@param.refresh_on_error(InvalidCredentials, on_error_callback)
|
|
320
|
+
def read_record(is_retry=False):
|
|
321
|
+
return my_db_client.read_record()
|
|
322
|
+
|
|
323
|
+
def lambda_handler(event, context):
|
|
324
|
+
return {'record': read_record()}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
`refresh_on_error` accepts:
|
|
328
|
+
|
|
329
|
+
| Argument | Default | Description |
|
|
330
|
+
|---|---|---|
|
|
331
|
+
| `error_class` | `Exception` | Exception type to intercept |
|
|
332
|
+
| `error_callback` | `None` | Called after refresh, before retry |
|
|
333
|
+
| `retry_argument` | `"is_retry"` | Kwarg name injected on retry |
|
|
334
|
+
|
|
335
|
+
## Replacing the SSM client
|
|
336
|
+
|
|
337
|
+
`set_ssm_client` lives on `Refreshable`, the base class shared by `SSMParameter` and `SSMParameterGroup`. Call it on whichever class you want to override, or on `Refreshable` directly to affect all subclasses at once.
|
|
338
|
+
|
|
339
|
+
```python
|
|
340
|
+
from ssm_cache.refreshable import Refreshable
|
|
341
|
+
|
|
342
|
+
# affects SSMParameter, SSMParameterGroup, and any subclass
|
|
343
|
+
Refreshable.set_ssm_client(my_custom_client)
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
The replacement object must implement two methods: `get_parameters` and `get_parameters_by_path`.
|
|
347
|
+
|
|
348
|
+
A common use case is injecting a [Placebo](https://github.com/garnaat/placebo) client for offline or unit testing:
|
|
349
|
+
|
|
350
|
+
```python
|
|
351
|
+
import boto3, placebo
|
|
352
|
+
from ssm_cache.refreshable import Refreshable
|
|
353
|
+
|
|
354
|
+
session = boto3.Session()
|
|
355
|
+
pill = placebo.attach(session, data_path='/path/to/responses')
|
|
356
|
+
pill.playback()
|
|
357
|
+
|
|
358
|
+
Refreshable.set_ssm_client(session.client('ssm'))
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
## How to contribute
|
|
362
|
+
|
|
363
|
+
Clone the repo and install all dev dependencies in one step:
|
|
364
|
+
|
|
365
|
+
```bash
|
|
366
|
+
git clone https://github.com/alexcasalboni/ssm-cache-python.git
|
|
367
|
+
cd ssm-cache-python
|
|
368
|
+
python -m venv env
|
|
369
|
+
source env/bin/activate
|
|
370
|
+
pip install -e ".[dev]"
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Running the tests
|
|
374
|
+
|
|
375
|
+
```bash
|
|
376
|
+
pytest
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
With coverage:
|
|
380
|
+
|
|
381
|
+
```bash
|
|
382
|
+
pytest --cov=ssm_cache --cov-report=term-missing
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
HTML coverage report:
|
|
386
|
+
|
|
387
|
+
```bash
|
|
388
|
+
pytest --cov=ssm_cache --cov-report=html
|
|
389
|
+
open htmlcov/index.html
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
### Linting and formatting
|
|
393
|
+
|
|
394
|
+
The project uses [ruff](https://docs.astral.sh/ruff/) for both linting and formatting.
|
|
395
|
+
|
|
396
|
+
Check for lint violations:
|
|
397
|
+
|
|
398
|
+
```bash
|
|
399
|
+
ruff check .
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
Auto-fix all fixable violations:
|
|
403
|
+
|
|
404
|
+
```bash
|
|
405
|
+
ruff check --fix .
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
Check formatting:
|
|
409
|
+
|
|
410
|
+
```bash
|
|
411
|
+
ruff format --check .
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
Apply formatting:
|
|
415
|
+
|
|
416
|
+
```bash
|
|
417
|
+
ruff format .
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
The CI `lint` job runs both `ruff format --check` and `ruff check` on every push and pull request before the test matrix starts. A failing lint check blocks the test run.
|
|
421
|
+
|
|
422
|
+
Opening a PR triggers the GitHub Actions CI matrix across Python 3.10–3.14 and uploads coverage to Coveralls automatically.
|
|
423
|
+
|
|
424
|
+
### Test layout
|
|
425
|
+
|
|
426
|
+
Test files mirror the package modules:
|
|
427
|
+
|
|
428
|
+
| Test file | Covers |
|
|
429
|
+
|---|---|
|
|
430
|
+
| `tests/test_utils.py` | `ssm_cache.utils` — `utcnow`, `batch` |
|
|
431
|
+
| `tests/test_refreshable.py` | `ssm_cache.refreshable` — `Refreshable`, `refresh_on_error`, `set_ssm_client` |
|
|
432
|
+
| `tests/test_parameters.py` | `ssm_cache.parameters` — `SSMParameter` |
|
|
433
|
+
| `tests/test_groups.py` | `ssm_cache.groups` — `SSMParameterGroup`, hierarchy |
|
|
434
|
+
| `tests/test_filters.py` | `ssm_cache.filters` — `SSMFilter` and subclasses |
|
|
435
|
+
| `tests/test_secrets.py` | `SecretsManagerParameter` |
|
|
436
|
+
| `tests/test_versioning.py` | Versioning and version pinning (placebo-backed) |
|
|
437
|
+
|
|
438
|
+
## What's new?
|
|
439
|
+
|
|
440
|
+
* **version 3.0.0**:
|
|
441
|
+
* dropped support for Python <3.10 (3.8 and 3.9 are end-of-life)
|
|
442
|
+
* Python 3.10–3.14 supported and tested
|
|
443
|
+
* fully type-annotated; ships a `py.typed` marker (PEP 561) and is type-checked with mypy
|
|
444
|
+
* split monolithic `cache.py` into logically grouped modules (`exceptions`, `filters`, `groups`, `parameters`, `refreshable`, `utils`)
|
|
445
|
+
* `__version__` added to package
|
|
446
|
+
* `pyproject.toml` replaces `setup.py` and `requirements*.txt`
|
|
447
|
+
* `set_ssm_client` promoted to `Refreshable` base class
|
|
448
|
+
* `ParameterNotFound` ClientError now normalised to `InvalidParameterError`
|
|
449
|
+
* test suite restructured to mirror package layout
|
|
450
|
+
* ruff replaces pylint for linting and formatting
|
|
451
|
+
* migrated from Travis to GitHub Actions
|
|
452
|
+
* **version 2.10**: exclude tests folder from site-packages
|
|
453
|
+
* **version 2.9**: bugfix, versioning support, tests with Python 3.7
|
|
454
|
+
* **version 2.8**: bugfix, new tests, fixed Travis build config
|
|
455
|
+
* **version 2.7**: support for AWS Secrets Manager integration
|
|
456
|
+
* **version 2.5**: hierarchical parameters, filters, and non-recursiveness support
|
|
457
|
+
* **version 2.3**: StringList parameters support (auto-conversion)
|
|
458
|
+
* **version 2.2**: client replacement and boto3/botocore minimum requirements
|
|
459
|
+
* **version 2.1**: group refresh bugfix
|
|
460
|
+
* **version 2.0**: new interface, `SSMParameterGroup` support
|
|
461
|
+
* **version 1.3**: Python3 support
|
|
462
|
+
* **version 1.0**: initial release
|
|
463
|
+
|
|
464
|
+
## References and articles
|
|
465
|
+
|
|
466
|
+
* [You should use SSM Parameter Store over Lambda env variables](https://hackernoon.com/you-should-use-ssm-parameter-store-over-lambda-env-variables-5197fc6ea45b) by Yan Cui (similar Node.js implementation)
|
|
467
|
+
* [AWS System Manager Parameter Store doc](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-paramstore.html)
|