fluidattacks_integrates_dal 2.3.1__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.
Files changed (34) hide show
  1. {fluidattacks_integrates_dal-2.3.1 → fluidattacks_integrates_dal-3.0.0}/PKG-INFO +4 -3
  2. fluidattacks_integrates_dal-3.0.0/build/default.nix +13 -0
  3. {fluidattacks_integrates_dal-2.3.1 → fluidattacks_integrates_dal-3.0.0}/flake.lock +47 -81
  4. {fluidattacks_integrates_dal-2.3.1 → fluidattacks_integrates_dal-3.0.0}/flake.nix +5 -4
  5. {fluidattacks_integrates_dal-2.3.1 → fluidattacks_integrates_dal-3.0.0}/fluidattacks_integrates_dal/__init__.py +1 -1
  6. {fluidattacks_integrates_dal-2.3.1 → fluidattacks_integrates_dal-3.0.0}/fluidattacks_integrates_dal/_cli.py +4 -4
  7. fluidattacks_integrates_dal-3.0.0/fluidattacks_integrates_dal/_typing.py +11 -0
  8. {fluidattacks_integrates_dal-2.3.1 → fluidattacks_integrates_dal-3.0.0}/fluidattacks_integrates_dal/clients/group/__init__.py +2 -2
  9. {fluidattacks_integrates_dal-2.3.1 → fluidattacks_integrates_dal-3.0.0}/fluidattacks_integrates_dal/clients/organization/__init__.py +27 -18
  10. {fluidattacks_integrates_dal-2.3.1 → fluidattacks_integrates_dal-3.0.0}/fluidattacks_integrates_dal/clients/out_of_scope/__init__.py +2 -2
  11. {fluidattacks_integrates_dal-2.3.1 → fluidattacks_integrates_dal-3.0.0}/fluidattacks_integrates_dal/core.py +9 -7
  12. fluidattacks_integrates_dal-3.0.0/fluidattacks_integrates_dal/utils.py +38 -0
  13. {fluidattacks_integrates_dal-2.3.1 → fluidattacks_integrates_dal-3.0.0}/pyproject.toml +5 -4
  14. {fluidattacks_integrates_dal-2.3.1 → fluidattacks_integrates_dal-3.0.0}/ruff.toml +1 -1
  15. {fluidattacks_integrates_dal-2.3.1 → fluidattacks_integrates_dal-3.0.0}/tests/arch/arch.py +3 -8
  16. {fluidattacks_integrates_dal-2.3.1 → fluidattacks_integrates_dal-3.0.0}/tests/arch/test_arch.py +2 -2
  17. {fluidattacks_integrates_dal-2.3.1 → fluidattacks_integrates_dal-3.0.0}/tests/test_list_groups.py +6 -2
  18. fluidattacks_integrates_dal-3.0.0/uv.lock +756 -0
  19. fluidattacks_integrates_dal-2.3.1/build/default.nix +0 -13
  20. fluidattacks_integrates_dal-2.3.1/fluidattacks_integrates_dal/_typing.py +0 -17
  21. fluidattacks_integrates_dal-2.3.1/fluidattacks_integrates_dal/utils.py +0 -32
  22. fluidattacks_integrates_dal-2.3.1/uv.lock +0 -1082
  23. {fluidattacks_integrates_dal-2.3.1 → fluidattacks_integrates_dal-3.0.0}/.envrc +0 -0
  24. {fluidattacks_integrates_dal-2.3.1 → fluidattacks_integrates_dal-3.0.0}/build/filter.nix +0 -0
  25. {fluidattacks_integrates_dal-2.3.1 → fluidattacks_integrates_dal-3.0.0}/fluidattacks_integrates_dal/_logger.py +0 -0
  26. {fluidattacks_integrates_dal-2.3.1 → fluidattacks_integrates_dal-3.0.0}/fluidattacks_integrates_dal/client.py +0 -0
  27. {fluidattacks_integrates_dal-2.3.1 → fluidattacks_integrates_dal-3.0.0}/fluidattacks_integrates_dal/clients/__init__.py +0 -0
  28. {fluidattacks_integrates_dal-2.3.1 → fluidattacks_integrates_dal-3.0.0}/fluidattacks_integrates_dal/list_groups.py +0 -0
  29. {fluidattacks_integrates_dal-2.3.1 → fluidattacks_integrates_dal-3.0.0}/fluidattacks_integrates_dal/py.typed +0 -0
  30. {fluidattacks_integrates_dal-2.3.1 → fluidattacks_integrates_dal-3.0.0}/mypy.ini +0 -0
  31. {fluidattacks_integrates_dal-2.3.1 → fluidattacks_integrates_dal-3.0.0}/tests/__init__.py +0 -0
  32. {fluidattacks_integrates_dal-2.3.1 → fluidattacks_integrates_dal-3.0.0}/tests/arch/__init__.py +0 -0
  33. {fluidattacks_integrates_dal-2.3.1 → fluidattacks_integrates_dal-3.0.0}/tests/py.typed +0 -0
  34. {fluidattacks_integrates_dal-2.3.1 → fluidattacks_integrates_dal-3.0.0}/tests/test_core.py +0 -0
@@ -1,12 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fluidattacks_integrates_dal
3
- Version: 2.3.1
3
+ Version: 3.0.0
4
4
  Summary: ASM Synchronous Data Access Layer
5
5
  Author-email: Engineering Team <development@fluidattacks.com>
6
- Requires-Python: >=3.11
6
+ Requires-Python: >=3.13.14, <3.14
7
7
  Classifier: License :: OSI Approved :: MIT License
8
8
  Requires-Dist: boto3 >=1.40.53, <2.0.0
9
9
  Requires-Dist: click >=8.1.7, <9.0.0
10
10
  Requires-Dist: fa-purity >=2.5.2, <3.0.0
11
11
  Requires-Dist: fluidattacks-etl-utils >=5.1.0, <6.0.0
12
- Requires-Dist: fluidattacks-utils-logger >=3.0.0, <4.0.0
12
+ Requires-Dist: fluidattacks-utils-logger >=3.0.5, <4.0.0
13
+ Requires-Dist: mypy-boto3-dynamodb >=1.40.40, <2.0.0
@@ -0,0 +1,13 @@
1
+ { src }:
2
+ {
3
+ inherit src;
4
+ root_path = "observes/common/integrates-dal";
5
+ module_name = "fluidattacks_integrates_dal";
6
+ pypi_token = {
7
+ method = "oidc";
8
+ };
9
+ override.checks = {
10
+ deptry = false;
11
+ import_linter = false;
12
+ };
13
+ }
@@ -5,11 +5,11 @@
5
5
  "nixpkgs-lib": "nixpkgs-lib"
6
6
  },
7
7
  "locked": {
8
- "lastModified": 1762810396,
9
- "narHash": "sha256-dxFVgQPG+R72dkhXTtqUm7KpxElw3u6E+YlQ2WaDgt8=",
8
+ "lastModified": 1768135262,
9
+ "narHash": "sha256-PVvu7OqHBGWN16zSi6tEmPwwHQ4rLPU9Plvs8/1TUBY=",
10
10
  "owner": "hercules-ci",
11
11
  "repo": "flake-parts",
12
- "rev": "0bdadb1b265fb4143a75bd1ec7d8c915898a9923",
12
+ "rev": "80daad04eddbbf5a4d883996a73f3f542fa437ac",
13
13
  "type": "github"
14
14
  },
15
15
  "original": {
@@ -35,27 +35,27 @@
35
35
  },
36
36
  "nixpkgs": {
37
37
  "locked": {
38
- "lastModified": 1758690382,
39
- "narHash": "sha256-NY3kSorgqE5LMm1LqNwGne3ZLMF2/ILgLpFr1fS4X3o=",
38
+ "lastModified": 1785133411,
39
+ "narHash": "sha256-Yjv0WEg39KRYS0rBdTbu6Fc/or/ihAKk13W9sQ6VWd0=",
40
40
  "owner": "nixos",
41
41
  "repo": "nixpkgs",
42
- "rev": "e643668fd71b949c53f8626614b21ff71a07379d",
42
+ "rev": "2f5a153c270b70cb0f8c11f46d96d6d3bc39f4e3",
43
43
  "type": "github"
44
44
  },
45
45
  "original": {
46
46
  "owner": "nixos",
47
- "ref": "nixos-unstable",
48
47
  "repo": "nixpkgs",
48
+ "rev": "2f5a153c270b70cb0f8c11f46d96d6d3bc39f4e3",
49
49
  "type": "github"
50
50
  }
51
51
  },
52
52
  "nixpkgs-lib": {
53
53
  "locked": {
54
- "lastModified": 1761765539,
55
- "narHash": "sha256-b0yj6kfvO8ApcSE+QmA6mUfu8IYG6/uU28OFn4PaC8M=",
54
+ "lastModified": 1765674936,
55
+ "narHash": "sha256-k00uTP4JNfmejrCLJOwdObYC9jHRrr/5M/a/8L2EIdo=",
56
56
  "owner": "nix-community",
57
57
  "repo": "nixpkgs.lib",
58
- "rev": "719359f4562934ae99f5443f20aa06c2ffff91fc",
58
+ "rev": "2075416fcb47225d9b68ac469a5c4801a9c4dd85",
59
59
  "type": "github"
60
60
  },
61
61
  "original": {
@@ -82,11 +82,11 @@
82
82
  },
83
83
  "nixpkgs_3": {
84
84
  "locked": {
85
- "lastModified": 1761373498,
86
- "narHash": "sha256-Q/uhWNvd7V7k1H1ZPMy/vkx3F8C13ZcdrKjO7Jv7v0c=",
85
+ "lastModified": 1758690382,
86
+ "narHash": "sha256-NY3kSorgqE5LMm1LqNwGne3ZLMF2/ILgLpFr1fS4X3o=",
87
87
  "owner": "nixos",
88
88
  "repo": "nixpkgs",
89
- "rev": "6a08e6bb4e46ff7fcbb53d409b253f6bad8a28ce",
89
+ "rev": "e643668fd71b949c53f8626614b21ff71a07379d",
90
90
  "type": "github"
91
91
  },
92
92
  "original": {
@@ -96,67 +96,25 @@
96
96
  "type": "github"
97
97
  }
98
98
  },
99
- "nixpkgs_flake": {
99
+ "nixpkgs_4": {
100
100
  "locked": {
101
- "lastModified": 1762812764,
102
- "narHash": "sha256-MNqrovkO8AEQeYQK7e3X2GDx2H8vfDIxV4K/VEaIjjo=",
103
- "owner": "nixos",
104
- "repo": "nixpkgs",
105
- "rev": "400156fe527ab6b54cf6f70cca2c8385548a29c4",
106
- "type": "github"
107
- },
108
- "original": {
109
- "owner": "nixos",
110
- "repo": "nixpkgs",
111
- "type": "github"
112
- }
113
- },
114
- "nixpkgs_flake_2": {
115
- "locked": {
116
- "lastModified": 1761668286,
117
- "narHash": "sha256-YN/cm/hbm7pi7lMZCsNhu6jlBUi4wUirrIlyOMpbjKU=",
101
+ "lastModified": 1761373498,
102
+ "narHash": "sha256-Q/uhWNvd7V7k1H1ZPMy/vkx3F8C13ZcdrKjO7Jv7v0c=",
118
103
  "owner": "nixos",
119
104
  "repo": "nixpkgs",
120
- "rev": "1fdb9eb10eff581a40ce2e9511c4ed517eac0c3f",
105
+ "rev": "6a08e6bb4e46ff7fcbb53d409b253f6bad8a28ce",
121
106
  "type": "github"
122
107
  },
123
108
  "original": {
124
109
  "owner": "nixos",
110
+ "ref": "nixos-unstable",
125
111
  "repo": "nixpkgs",
126
112
  "type": "github"
127
113
  }
128
114
  },
129
- "observes_flake_builder": {
130
- "inputs": {
131
- "nix_filter": "nix_filter",
132
- "nixpkgs_flake": "nixpkgs_flake",
133
- "pyproject-build-systems": "pyproject-build-systems",
134
- "pyproject-nix": "pyproject-nix_2",
135
- "pythonix_flake": "pythonix_flake",
136
- "shell-helpers": "shell-helpers",
137
- "uv2nix": "uv2nix_2"
138
- },
139
- "locked": {
140
- "dir": "observes/common/std_flake_2",
141
- "lastModified": 1770672786,
142
- "narHash": "sha256-W4qMpuS/Zg9P/PooFDsqW7EzjF3A+ITsxKbp1SWdARI=",
143
- "ref": "refs/heads/trunk",
144
- "rev": "ad927cc6545df64a7d622696a43bcee225b9867b",
145
- "shallow": true,
146
- "type": "git",
147
- "url": "ssh://git@gitlab.com/fluidattacks/universe"
148
- },
149
- "original": {
150
- "dir": "observes/common/std_flake_2",
151
- "rev": "ad927cc6545df64a7d622696a43bcee225b9867b",
152
- "shallow": true,
153
- "type": "git",
154
- "url": "ssh://git@gitlab.com/fluidattacks/universe"
155
- }
156
- },
157
115
  "pyproject-build-systems": {
158
116
  "inputs": {
159
- "nixpkgs": "nixpkgs",
117
+ "nixpkgs": "nixpkgs_2",
160
118
  "pyproject-nix": "pyproject-nix",
161
119
  "uv2nix": "uv2nix"
162
120
  },
@@ -177,7 +135,7 @@
177
135
  "pyproject-nix": {
178
136
  "inputs": {
179
137
  "nixpkgs": [
180
- "observes_flake_builder",
138
+ "python_builder",
181
139
  "pyproject-build-systems",
182
140
  "nixpkgs"
183
141
  ]
@@ -198,7 +156,7 @@
198
156
  },
199
157
  "pyproject-nix_2": {
200
158
  "inputs": {
201
- "nixpkgs": "nixpkgs_2"
159
+ "nixpkgs": "nixpkgs_3"
202
160
  },
203
161
  "locked": {
204
162
  "lastModified": 1762427963,
@@ -217,7 +175,7 @@
217
175
  "pyproject-nix_3": {
218
176
  "inputs": {
219
177
  "nixpkgs": [
220
- "observes_flake_builder",
178
+ "python_builder",
221
179
  "uv2nix",
222
180
  "nixpkgs"
223
181
  ]
@@ -236,23 +194,30 @@
236
194
  "type": "github"
237
195
  }
238
196
  },
239
- "pythonix_flake": {
197
+ "python_builder": {
240
198
  "inputs": {
241
- "nixpkgs_flake": "nixpkgs_flake_2"
199
+ "nix_filter": "nix_filter",
200
+ "nixpkgs_flake": [
201
+ "nixpkgs"
202
+ ],
203
+ "pyproject-build-systems": "pyproject-build-systems",
204
+ "pyproject-nix": "pyproject-nix_2",
205
+ "shell-helpers": "shell-helpers",
206
+ "uv2nix": "uv2nix_2"
242
207
  },
243
208
  "locked": {
244
- "dir": "observes/common/pythonix_mirror",
245
- "lastModified": 1767370831,
246
- "narHash": "sha256-VR86TSrJsNHQq31Ksga+1dvsrkv9u1pKa0MKuKYeIAc=",
209
+ "dir": "common/utils/python-builder",
210
+ "lastModified": 1787753967,
211
+ "narHash": "sha256-1kWAiANGMA4B9flxHFj73EwvOpF8r0nXGg1RVXNKPK8=",
247
212
  "ref": "refs/heads/trunk",
248
- "rev": "0678f8ff3bfe02cfe23bc819bfa739c53bd6e4b6",
213
+ "rev": "2490a24f91f075f7580cd0b9df5bc01dbc467a50",
249
214
  "shallow": true,
250
215
  "type": "git",
251
216
  "url": "ssh://git@gitlab.com/fluidattacks/universe"
252
217
  },
253
218
  "original": {
254
- "dir": "observes/common/pythonix_mirror",
255
- "rev": "0678f8ff3bfe02cfe23bc819bfa739c53bd6e4b6",
219
+ "dir": "common/utils/python-builder",
220
+ "rev": "2490a24f91f075f7580cd0b9df5bc01dbc467a50",
256
221
  "shallow": true,
257
222
  "type": "git",
258
223
  "url": "ssh://git@gitlab.com/fluidattacks/universe"
@@ -260,30 +225,31 @@
260
225
  },
261
226
  "root": {
262
227
  "inputs": {
263
- "observes_flake_builder": "observes_flake_builder"
228
+ "nixpkgs": "nixpkgs",
229
+ "python_builder": "python_builder"
264
230
  }
265
231
  },
266
232
  "shell-helpers": {
267
233
  "inputs": {
268
234
  "flake-parts": "flake-parts",
269
235
  "nixpkgs": [
270
- "observes_flake_builder",
236
+ "python_builder",
271
237
  "nixpkgs_flake"
272
238
  ]
273
239
  },
274
240
  "locked": {
275
241
  "dir": "common/utils/shell-helpers",
276
- "lastModified": 1762806066,
277
- "narHash": "sha256-Wz99Fl7SpPU1NkTHJ36XOo6DjOEAMXCw3ThRABOS2Hs=",
242
+ "lastModified": 1787145059,
243
+ "narHash": "sha256-i5i+qLR9JhcNasrQvzG4S/0AotpZAqTHXa4/68MmvwA=",
278
244
  "ref": "refs/heads/trunk",
279
- "rev": "d73cfdc7ae9e7f547e22a4481d4c2ccce38b26a8",
245
+ "rev": "e0d58302690af6f38b7544265e59c625b63df76f",
280
246
  "shallow": true,
281
247
  "type": "git",
282
248
  "url": "ssh://git@gitlab.com/fluidattacks/universe"
283
249
  },
284
250
  "original": {
285
251
  "dir": "common/utils/shell-helpers",
286
- "rev": "d73cfdc7ae9e7f547e22a4481d4c2ccce38b26a8",
252
+ "rev": "e0d58302690af6f38b7544265e59c625b63df76f",
287
253
  "shallow": true,
288
254
  "type": "git",
289
255
  "url": "ssh://git@gitlab.com/fluidattacks/universe"
@@ -292,12 +258,12 @@
292
258
  "uv2nix": {
293
259
  "inputs": {
294
260
  "nixpkgs": [
295
- "observes_flake_builder",
261
+ "python_builder",
296
262
  "pyproject-build-systems",
297
263
  "nixpkgs"
298
264
  ],
299
265
  "pyproject-nix": [
300
- "observes_flake_builder",
266
+ "python_builder",
301
267
  "pyproject-build-systems",
302
268
  "pyproject-nix"
303
269
  ]
@@ -318,7 +284,7 @@
318
284
  },
319
285
  "uv2nix_2": {
320
286
  "inputs": {
321
- "nixpkgs": "nixpkgs_3",
287
+ "nixpkgs": "nixpkgs_4",
322
288
  "pyproject-nix": "pyproject-nix_3"
323
289
  },
324
290
  "locked": {
@@ -2,8 +2,10 @@
2
2
  description = "Common Integrates-dal";
3
3
 
4
4
  inputs = {
5
- observes_flake_builder = {
6
- url = "git+ssh://git@gitlab.com/fluidattacks/universe?shallow=1&rev=ad927cc6545df64a7d622696a43bcee225b9867b&dir=observes/common/std_flake_2";
5
+ nixpkgs.url = "github:nixos/nixpkgs/2f5a153c270b70cb0f8c11f46d96d6d3bc39f4e3";
6
+ python_builder = {
7
+ url = "git+ssh://git@gitlab.com/fluidattacks/universe?shallow=1&rev=2490a24f91f075f7580cd0b9df5bc01dbc467a50&dir=common/utils/python-builder";
8
+ inputs.nixpkgs_flake.follows = "nixpkgs";
7
9
  };
8
10
  };
9
11
 
@@ -19,11 +21,10 @@
19
21
  scripts,
20
22
  }:
21
23
  import ./build {
22
- inherit nixpkgs builders scripts;
23
24
  src = import ./build/filter.nix nixpkgs.nix-filter self;
24
25
  };
25
26
  in
26
27
  {
27
- packages = inputs.observes_flake_builder.outputs.build build_args;
28
+ packages = inputs.python_builder.outputs.build build_args;
28
29
  };
29
30
  }
@@ -6,6 +6,6 @@ from ._logger import (
6
6
  set_logger,
7
7
  )
8
8
 
9
- __version__ = "2.3.1"
9
+ __version__ = "3.0.0"
10
10
 
11
11
  Unsafe.compute(set_logger(__name__, __version__))
@@ -42,10 +42,10 @@ def list_all_groups() -> NoReturn:
42
42
  list_groups.list_all_groups(t[0], t[1])
43
43
  .map(lambda gs: "\n".join(g.name for g in gs))
44
44
  .bind(
45
- lambda s: Cmd.wrap_impure(
46
- lambda: sys.stdout.write(s + "\n"), # type: ignore[misc]
47
- ).map(
48
- lambda _: None, # type: ignore[misc]
45
+ lambda s: (
46
+ Cmd[int]
47
+ .wrap_impure(lambda: sys.stdout.write(s + "\n")) # type: ignore[misc]
48
+ .map(lambda _: None)
49
49
  ),
50
50
  )
51
51
  ),
@@ -0,0 +1,11 @@
1
+ from collections.abc import Callable, Iterable, Iterator
2
+ from typing import Generic, NoReturn, TypeVar
3
+
4
+ __all__ = [
5
+ "Callable",
6
+ "Generic",
7
+ "Iterable",
8
+ "Iterator",
9
+ "NoReturn",
10
+ "TypeVar",
11
+ ]
@@ -125,7 +125,7 @@ def _get_groups_page(
125
125
  LOG.debug(page)
126
126
  return page
127
127
 
128
- return Cmd.wrap_impure(_action)
128
+ return Cmd[_Page].wrap_impure(_action)
129
129
 
130
130
 
131
131
  def _get_groups(table: Table, org: OrganizationId) -> Stream[GroupId]:
@@ -137,7 +137,7 @@ def _get_groups(table: Table, org: OrganizationId) -> Stream[GroupId]:
137
137
  lambda p: (
138
138
  _get_groups_page(table, org, p.last_index).map(_union.inl)
139
139
  if p and p.last_index
140
- else Cmd.wrap_value(None).map(_union.inr)
140
+ else Cmd[None].wrap_value(None).map(_union.inr)
141
141
  ),
142
142
  ),
143
143
  init.map(_union.inl),
@@ -1,7 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import logging
4
- from typing import TYPE_CHECKING
4
+ from typing import (
5
+ TYPE_CHECKING,
6
+ )
5
7
 
6
8
  from fa_purity import (
7
9
  Cmd,
@@ -20,9 +22,11 @@ from fa_purity.json import (
20
22
  JsonPrimitiveFactory,
21
23
  JsonPrimitiveUnfolder,
22
24
  )
25
+ from mypy_boto3_dynamodb.type_defs import (
26
+ QueryOutputTypeDef,
27
+ )
23
28
 
24
29
  from fluidattacks_integrates_dal._typing import (
25
- Dict,
26
30
  Iterable,
27
31
  TypeVar,
28
32
  )
@@ -37,37 +41,40 @@ if TYPE_CHECKING:
37
41
  from mypy_boto3_dynamodb import (
38
42
  DynamoDBClient,
39
43
  )
40
- from mypy_boto3_dynamodb.type_defs import (
41
- QueryOutputTypeDef,
42
- )
43
44
  LOG = logging.getLogger(__name__)
44
45
  _T = TypeVar("_T")
45
46
  _K = TypeVar("_K")
46
47
  _DB_TABLE = "integrates_vms"
47
48
 
48
49
 
49
- def _require_key(item: Dict[_K, _T], key: _K) -> ResultE[_T]:
50
+ def _require_key(item: dict[_K, _T], key: _K) -> ResultE[_T]:
50
51
  try:
51
- return Result.success(item[key])
52
+ return Result[_T, Exception].success(item[key])
52
53
  except KeyError as err:
53
- return Result.failure(err).alt(cast_exception)
54
+ return Result[_T, KeyError].failure(err).alt(cast_exception)
54
55
 
55
56
 
56
- def _decode_dynamo_str(item: _T) -> ResultE[JsonPrimitive]:
57
+ def _decode_dynamo_str(item: object) -> ResultE[JsonPrimitive]:
57
58
  if isinstance(item, dict):
58
- return _require_key(item, "S").bind( # type: ignore[misc]
59
- JsonPrimitiveFactory.from_any,
59
+ # isinstance narrows to dict[Unknown, Unknown]; the actual key/value
60
+ # types are only known by convention (DynamoDB's typed-attribute maps).
61
+ s_value: ResultE[str] = _require_key(
62
+ item, # pyright: ignore[reportUnknownArgumentType]
63
+ "S",
60
64
  )
61
- return Result.failure(TypeError(f"Expected `dict` instance; got `{type(item)}`")).alt(
62
- cast_exception,
65
+ return s_value.bind(JsonPrimitiveFactory.from_any)
66
+ return (
67
+ Result[JsonPrimitive, TypeError]
68
+ .failure(TypeError(f"Expected `dict` instance; got `{type(item)}`"))
69
+ .alt(cast_exception)
63
70
  )
64
71
 
65
72
 
66
- def _decode_dynamo_key_str(item: Dict[str, _T], key: str) -> ResultE[JsonPrimitive]:
73
+ def _decode_dynamo_key_str(item: dict[str, _T], key: str) -> ResultE[JsonPrimitive]:
67
74
  return _require_key(item, key).bind(_decode_dynamo_str)
68
75
 
69
76
 
70
- def _decode_org_id(raw: Dict[str, _T]) -> ResultE[OrganizationId]:
77
+ def _decode_org_id(raw: dict[str, _T]) -> ResultE[OrganizationId]:
71
78
  _pk = _decode_dynamo_key_str(raw, "pk").bind(JsonPrimitiveUnfolder.to_str)
72
79
  _sk = _decode_dynamo_key_str(raw, "sk").bind(JsonPrimitiveUnfolder.to_str)
73
80
  return _pk.bind(lambda pk: _sk.bind(lambda sk: OrganizationId.new(pk, sk)))
@@ -85,7 +92,7 @@ def _to_items(page: QueryOutputTypeDef) -> FrozenList[OrganizationId]:
85
92
  def _all_orgs(client: DynamoDBClient) -> Stream[OrganizationId]:
86
93
  def _new_iter() -> Iterable[Cmd[QueryOutputTypeDef]]:
87
94
  LOG.debug("Getting all orgs")
88
- exp_attrs_values: Dict[str, Dict[str, str]] = {
95
+ exp_attrs_values: dict[str, dict[str, str]] = {
89
96
  ":pk": {"S": "ORG#all"},
90
97
  ":sk": {"S": "ORG#"},
91
98
  }
@@ -100,10 +107,12 @@ def _all_orgs(client: DynamoDBClient) -> Stream[OrganizationId]:
100
107
  IndexName="gsi_2",
101
108
  )
102
109
  LOG.debug("all orgs retrieved!")
103
- return map(Cmd.wrap_value, response) # type: ignore[misc]
110
+ return (Cmd[QueryOutputTypeDef].wrap_value(v) for v in response) # type: ignore[misc]
104
111
 
105
112
  data = StreamFactory.from_commands( # type: ignore[misc]
106
- Unsafe.pure_iter_from_cmd(Cmd.wrap_impure(_new_iter)), # type: ignore[misc]
113
+ Unsafe.pure_iter_from_cmd(
114
+ Cmd[Iterable[Cmd[QueryOutputTypeDef]]].wrap_impure(_new_iter), # type: ignore[misc]
115
+ ),
107
116
  )
108
117
  return (
109
118
  data.map(_to_items) # type: ignore[misc]
@@ -113,9 +113,9 @@ def _get_page(
113
113
  .transform(PureIterTransform.filter_maybe)
114
114
  .to_list()
115
115
  )
116
- return (roots, Maybe.from_optional(response.get("LastEvaluatedKey")))
116
+ return (roots, Maybe[_Cursor].from_optional(response.get("LastEvaluatedKey")))
117
117
 
118
- return Cmd.wrap_impure(_action)
118
+ return Cmd[tuple[FrozenList[OutOfScopeRoot], Maybe[_Cursor]]].wrap_impure(_action)
119
119
 
120
120
 
121
121
  def _get_roots(table: Table, org: OrganizationId) -> Stream[FrozenList[OutOfScopeRoot]]:
@@ -31,8 +31,8 @@ class GroupId:
31
31
  pattern = r"^[\w\s-]+$"
32
32
  if not re.match(pattern, _name):
33
33
  err = ValueError(rf"Group name is not `^[\w- ]+$` i.e. {_name}")
34
- return Result.failure(err, GroupId).alt(cast_exception)
35
- return Result.success(GroupId(_Private(), _name), Exception)
34
+ return Result[GroupId, ValueError].failure(err).alt(cast_exception)
35
+ return Result[GroupId, Exception].success(GroupId(_Private(), _name))
36
36
 
37
37
 
38
38
  @dataclass(frozen=True)
@@ -47,13 +47,13 @@ class OrganizationId:
47
47
  _uuid = uuid.removeprefix("ORG#")
48
48
  if not _name.isalnum():
49
49
  err = ValueError(f"Org name is not alphanum i.e. {_name}")
50
- return Result.failure(err, OrganizationId).alt(cast_exception)
50
+ return Result[OrganizationId, ValueError].failure(err).alt(cast_exception)
51
51
  uuidv4 = "^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$"
52
52
  if re.match(uuidv4, _uuid, re.IGNORECASE) is None:
53
53
  err = ValueError(f"Org id is not an UUIDv4 i.e. {_uuid}")
54
- return Result.failure(err, OrganizationId).alt(cast_exception)
54
+ return Result[OrganizationId, ValueError].failure(err).alt(cast_exception)
55
55
 
56
- return Result.success(OrganizationId(_Private(), _name, _uuid), Exception)
56
+ return Result[OrganizationId, Exception].success(OrganizationId(_Private(), _name, _uuid))
57
57
 
58
58
 
59
59
  @dataclass(frozen=True)
@@ -66,5 +66,7 @@ class OutOfScopeRoot:
66
66
  def new(nickname: str, status: str) -> ResultE[OutOfScopeRoot]:
67
67
  if not nickname:
68
68
  err = ValueError("Out-of-scope root nickname is empty")
69
- return Result.failure(err, OutOfScopeRoot).alt(cast_exception)
70
- return Result.success(OutOfScopeRoot(_Private(), nickname, status), Exception)
69
+ return Result[OutOfScopeRoot, ValueError].failure(err).alt(cast_exception)
70
+ return Result[OutOfScopeRoot, Exception].success(
71
+ OutOfScopeRoot(_Private(), nickname, status),
72
+ )
@@ -0,0 +1,38 @@
1
+ from __future__ import annotations
2
+
3
+ from boto3 import (
4
+ Session,
5
+ )
6
+ from fa_purity import (
7
+ Cmd,
8
+ )
9
+ from mypy_boto3_dynamodb import (
10
+ DynamoDBClient,
11
+ DynamoDBServiceResource,
12
+ )
13
+
14
+
15
+ def new_session() -> Cmd[Session]:
16
+ # This impure procedure gets inputs (credentials) through the environment
17
+ # e.g. AWS_DEFAULT_REGION
18
+ return Cmd[Session].wrap_impure(Session)
19
+
20
+
21
+ def new_resource(session: Session) -> Cmd[DynamoDBServiceResource]:
22
+ # boto3-stubs' resource() overload set is partially unknown because
23
+ # only mypy_boto3_dynamodb is installed.
24
+ return Cmd[DynamoDBServiceResource].wrap_impure(
25
+ lambda: session.resource( # pyright: ignore[reportUnknownMemberType]
26
+ service_name="dynamodb",
27
+ use_ssl=True,
28
+ verify=True,
29
+ ),
30
+ )
31
+
32
+
33
+ def new_client(session: Session) -> Cmd[DynamoDBClient]:
34
+ # boto3-stubs' client() overload set is partially unknown because
35
+ # only mypy_boto3_dynamodb is installed.
36
+ return Cmd[DynamoDBClient].wrap_impure(
37
+ lambda: session.client("dynamodb"), # pyright: ignore[reportUnknownMemberType]
38
+ )
@@ -6,13 +6,14 @@ authors = [
6
6
  classifiers = [
7
7
  "License :: OSI Approved :: MIT License",
8
8
  ]
9
- requires-python = ">=3.11"
9
+ requires-python = ">=3.13.14, <3.14"
10
10
  dependencies = [
11
11
  "boto3 >=1.40.53, <2.0.0",
12
12
  "click >=8.1.7, <9.0.0",
13
13
  "fa-purity >=2.5.2, <3.0.0",
14
14
  "fluidattacks-etl-utils >=5.1.0, <6.0.0",
15
- "fluidattacks-utils-logger >=3.0.0, <4.0.0",
15
+ "fluidattacks-utils-logger >=3.0.5, <4.0.0",
16
+ "mypy-boto3-dynamodb >=1.40.40, <2.0.0",
16
17
  ]
17
18
  description = "ASM Synchronous Data Access Layer"
18
19
  dynamic = ["version"]
@@ -33,9 +34,9 @@ dev = [
33
34
  "ruff >=0.14.1, <1.0.0",
34
35
  "botocore-stubs >=1.40.53, <2.0.0",
35
36
  "boto3-stubs >=1.40.53, <2.0.0",
36
- "mypy-boto3-dynamodb >=1.40.40, <2.0.0",
37
+ "basedpyright >=1.39.8, <2.0.0",
37
38
  ]
38
39
 
39
40
  [tool.uv]
40
41
  exclude-newer = "7 days"
41
- exclude-newer-package = { fluidattacks-core = "2099-01-01", fluid-blends = "2099-01-01" , fluidattacks-utils-logger = "2099-01-01" }
42
+ exclude-newer-package = { fluidattacks-core = "2099-01-02T00:00:00Z", fluid-blends = "2099-01-02T00:00:00Z" , fluidattacks-utils-logger = "2099-01-02T00:00:00Z" }
@@ -1,7 +1,7 @@
1
1
  cache-dir = ".ruff_cache"
2
2
  line-length = 100
3
3
  indent-width = 4
4
- target-version = "py311"
4
+ target-version = "py313"
5
5
 
6
6
  [lint]
7
7
  preview = true
@@ -8,13 +8,8 @@ from fa_purity import (
8
8
  FrozenList,
9
9
  )
10
10
 
11
- from fluidattacks_integrates_dal._typing import (
12
- Dict,
13
- FrozenSet,
14
- )
15
-
16
11
  root = FullPathModule.assert_module("fluidattacks_integrates_dal")
17
- _dag: Dict[str, FrozenList[FrozenList[str] | str]] = {
12
+ _dag: dict[str, FrozenList[FrozenList[str] | str]] = {
18
13
  "fluidattacks_integrates_dal": (
19
14
  "_cli",
20
15
  "list_groups",
@@ -35,8 +30,8 @@ def project_dag() -> DagMap:
35
30
  return item
36
31
 
37
32
 
38
- def forbidden_allowlist() -> Dict[FullPathModule, FrozenSet[FullPathModule]]:
39
- _raw: Dict[str, FrozenSet[str]] = {}
33
+ def forbidden_allowlist() -> dict[FullPathModule, frozenset[FullPathModule]]:
34
+ _raw: dict[str, frozenset[str]] = {}
40
35
  return {
41
36
  FullPathModule.assert_module(k): frozenset(FullPathModule.assert_module(i) for i in v)
42
37
  for k, v in _raw.items()
@@ -20,7 +20,7 @@ from .arch import (
20
20
 
21
21
 
22
22
  def test_dag_creation() -> None:
23
- project_dag()
23
+ _ = project_dag()
24
24
 
25
25
 
26
26
  def test_dag() -> None:
@@ -34,7 +34,7 @@ def test_dag_completeness() -> None:
34
34
 
35
35
 
36
36
  def test_forbidden_creation() -> None:
37
- forbidden_allowlist()
37
+ _ = forbidden_allowlist()
38
38
 
39
39
 
40
40
  def test_forbidden() -> None: