fluidattacks_core_logging 12.0.0__tar.gz → 12.1.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 (28) hide show
  1. fluidattacks_core_logging-12.1.0/.envrc +1 -0
  2. fluidattacks_core_logging-12.1.0/.gitignore +15 -0
  3. {fluidattacks_core_logging-12.0.0 → fluidattacks_core_logging-12.1.0}/PKG-INFO +1 -1
  4. {fluidattacks_core_logging-12.0.0 → fluidattacks_core_logging-12.1.0}/fluidattacks_core/logging/__init__.py +15 -0
  5. fluidattacks_core_logging-12.1.0/fluidattacks_core/logging/capture.py +57 -0
  6. {fluidattacks_core_logging-12.0.0 → fluidattacks_core_logging-12.1.0}/import-linter.cfg +2 -10
  7. {fluidattacks_core_logging-12.0.0 → fluidattacks_core_logging-12.1.0}/pyproject.toml +1 -1
  8. fluidattacks_core_logging-12.1.0/tests/test_capture.py +69 -0
  9. {fluidattacks_core_logging-12.0.0 → fluidattacks_core_logging-12.1.0}/tests/test_logging.py +85 -0
  10. {fluidattacks_core_logging-12.0.0 → fluidattacks_core_logging-12.1.0}/uv.lock +1 -1
  11. fluidattacks_core_logging-12.0.0/.envrc +0 -1
  12. fluidattacks_core_logging-12.0.0/.gitignore +0 -5
  13. fluidattacks_core_logging-12.0.0/build/default.nix +0 -19
  14. fluidattacks_core_logging-12.0.0/build/filter.nix +0 -13
  15. fluidattacks_core_logging-12.0.0/flake.lock +0 -303
  16. fluidattacks_core_logging-12.0.0/flake.nix +0 -29
  17. {fluidattacks_core_logging-12.0.0 → fluidattacks_core_logging-12.1.0}/fluidattacks_core/logging/filters.py +0 -0
  18. {fluidattacks_core_logging-12.0.0 → fluidattacks_core_logging-12.1.0}/fluidattacks_core/logging/formatters.py +0 -0
  19. {fluidattacks_core_logging-12.0.0 → fluidattacks_core_logging-12.1.0}/fluidattacks_core/logging/handlers.py +0 -0
  20. {fluidattacks_core_logging-12.0.0 → fluidattacks_core_logging-12.1.0}/fluidattacks_core/logging/presets.py +0 -0
  21. {fluidattacks_core_logging-12.0.0 → fluidattacks_core_logging-12.1.0}/fluidattacks_core/logging/py.typed +0 -0
  22. {fluidattacks_core_logging-12.0.0 → fluidattacks_core_logging-12.1.0}/fluidattacks_core/logging/sources/__init__.py +0 -0
  23. {fluidattacks_core_logging-12.0.0 → fluidattacks_core_logging-12.1.0}/fluidattacks_core/logging/sources/types.py +0 -0
  24. {fluidattacks_core_logging-12.0.0 → fluidattacks_core_logging-12.1.0}/fluidattacks_core/logging/sources/utils.py +0 -0
  25. {fluidattacks_core_logging-12.0.0 → fluidattacks_core_logging-12.1.0}/fluidattacks_core/logging/utils.py +0 -0
  26. {fluidattacks_core_logging-12.0.0 → fluidattacks_core_logging-12.1.0}/mypy.ini +0 -0
  27. {fluidattacks_core_logging-12.0.0 → fluidattacks_core_logging-12.1.0}/ruff.toml +0 -0
  28. {fluidattacks_core_logging-12.0.0 → fluidattacks_core_logging-12.1.0}/tests/__init__.py +0 -0
@@ -0,0 +1 @@
1
+ use flake ../../..#fluidattacks-core-logging
@@ -0,0 +1,15 @@
1
+ dist/
2
+ dist-*/
3
+ *.zip
4
+ /utils/terraform-modules/**/.terraform.lock.hcl
5
+ /.cargo-cache
6
+ target/
7
+ .direnv/
8
+ .venv/
9
+ .vscode/settings.json
10
+ .zed/settings.json
11
+ # each package's shell writes its own, beside the package rather than here
12
+ **/.vscode/settings.json
13
+ *.so
14
+ # the publish check builds into its own tree per architecture
15
+ target-*/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: fluidattacks_core_logging
3
- Version: 12.0.0
3
+ Version: 12.1.0
4
4
  Summary: Fluid Attacks Core Logging Library
5
5
  Author-email: Development <development@fluidattacks.com>
6
6
  License: MPL-2.0
@@ -5,6 +5,7 @@ import logging.config
5
5
  import sys
6
6
  from typing import TYPE_CHECKING
7
7
 
8
+ from fluidattacks_core.logging.capture import CapturedLog, CaptureHandler
8
9
  from fluidattacks_core.logging.presets import DATE_FORMAT, PRODUCT_LOGGING
9
10
  from fluidattacks_core.logging.sources.utils import (
10
11
  set_commit_ref_name,
@@ -45,10 +46,24 @@ def init_logging(preset: _DictConfigArgs | None = None) -> None:
45
46
  init_uncaught_exception_logging()
46
47
 
47
48
 
49
+ def init_logging_captured(preset: _DictConfigArgs | None = None) -> CapturedLog:
50
+ """`init_logging`, that also keeps a copy of everything logged.
51
+
52
+ The console is unchanged; the copy is what a program reads back to hand its output to
53
+ someone else once the run is over. Call it once: each call adds another copy.
54
+ """
55
+ init_logging(preset)
56
+ capture = CapturedLog()
57
+ logging.getLogger().addHandler(CaptureHandler(capture))
58
+ return capture
59
+
60
+
48
61
  __all__ = [
49
62
  "DATE_FORMAT",
50
63
  "PRODUCT_LOGGING",
64
+ "CapturedLog",
51
65
  "init_logging",
66
+ "init_logging_captured",
52
67
  "init_uncaught_exception_logging",
53
68
  "set_commit_ref_name",
54
69
  "set_commit_sha",
@@ -0,0 +1,57 @@
1
+ """Opt-in copy of what the logger writes.
2
+
3
+ For a program that has to hand its output to someone else after the run.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import logging
9
+ import threading
10
+
11
+ from fluidattacks_core.logging.presets import DATE_FORMAT
12
+
13
+
14
+ class CapturedLog:
15
+ """What the logger wrote since `init_logging_captured`, as plain text lines.
16
+
17
+ Each line reads `<date> [<level>] [<logger>] <message>`, followed by the traceback
18
+ when the record carries one. It is the same whatever the environment, so it holds no
19
+ ANSI colors and no JSON, unlike the console.
20
+
21
+ It keeps everything: it never trims, and whoever receives it decides how much to
22
+ accept. Safe to read while other threads log.
23
+ """
24
+
25
+ def __init__(self) -> None:
26
+ self._lock = threading.Lock()
27
+ self._lines: list[str] = []
28
+
29
+ def _push(self, text: str) -> None:
30
+ with self._lock:
31
+ self._lines.append(text)
32
+
33
+ def contents(self) -> str:
34
+ """Return the output so far."""
35
+ with self._lock:
36
+ return "".join(self._lines)
37
+
38
+
39
+ class CaptureHandler(logging.Handler):
40
+ """Feeds every record it receives into a `CapturedLog`."""
41
+
42
+ def __init__(self, capture: CapturedLog) -> None:
43
+ super().__init__()
44
+ self.capture = capture
45
+ self.setFormatter(
46
+ logging.Formatter(
47
+ "{asctime} [{levelname}] [{name}] {message}",
48
+ datefmt=DATE_FORMAT,
49
+ style="{",
50
+ ),
51
+ )
52
+
53
+ def emit(self, record: logging.LogRecord) -> None:
54
+ try:
55
+ self.capture._push(self.format(record) + "\n") # noqa: SLF001
56
+ except Exception: # noqa: BLE001
57
+ self.handleError(record)
@@ -1,16 +1,7 @@
1
1
  [importlinter]
2
- root_package = fluidattacks_core
2
+ root_package = fluidattacks_core.logging
3
3
  include_external_packages = True
4
4
 
5
- [importlinter:contract:portion]
6
- name = Single Portion
7
- type = layers
8
- containers =
9
- fluidattacks_core
10
- exhaustive = true
11
- layers =
12
- logging
13
-
14
5
  [importlinter:contract:dag]
15
6
  name = Direct Acyclic Graph
16
7
  type = layers
@@ -18,6 +9,7 @@
18
9
  fluidattacks_core.logging
19
10
  exhaustive = true
20
11
  layers =
12
+ capture
21
13
  presets
22
14
  handlers
23
15
  formatters
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fluidattacks_core_logging"
3
- version = "12.0.0"
3
+ version = "12.1.0"
4
4
  description = "Fluid Attacks Core Logging Library"
5
5
  authors = [{ name = "Development", email = "development@fluidattacks.com" }]
6
6
  license = { text = "MPL-2.0" }
@@ -0,0 +1,69 @@
1
+ import logging
2
+ from collections.abc import Iterator
3
+
4
+ import pytest
5
+
6
+ from fluidattacks_core.logging import CapturedLog, init_logging_captured
7
+ from fluidattacks_core.logging.capture import CaptureHandler
8
+
9
+
10
+ @pytest.fixture
11
+ def root_logger() -> Iterator[logging.Logger]:
12
+ root = logging.getLogger()
13
+ handlers, level = list(root.handlers), root.level
14
+ yield root
15
+ root.handlers[:] = handlers
16
+ root.setLevel(level)
17
+
18
+
19
+ def _written(capture: CapturedLog, text: str) -> None:
20
+ capture._push(text) # noqa: SLF001
21
+
22
+
23
+ def test_a_capture_keeps_what_the_logger_wrote(root_logger: logging.Logger) -> None:
24
+ capture = init_logging_captured()
25
+ logging.getLogger("scan").info("first")
26
+ logging.getLogger("scan").warning("second")
27
+ lines = capture.contents().splitlines()
28
+ assert len(lines) == 2
29
+ assert lines[0].endswith("[INFO] [scan] first")
30
+ assert lines[1].endswith("[WARNING] [scan] second")
31
+ assert root_logger.handlers
32
+
33
+
34
+ def test_the_copy_has_no_color_codes(root_logger: logging.Logger) -> None:
35
+ capture = init_logging_captured()
36
+ logging.getLogger("scan").error("boom")
37
+ assert "\x1b" not in capture.contents()
38
+ assert root_logger.handlers
39
+
40
+
41
+ def test_the_copy_is_the_same_in_production(
42
+ monkeypatch: pytest.MonkeyPatch, root_logger: logging.Logger
43
+ ) -> None:
44
+ monkeypatch.setenv("CI_COMMIT_REF_NAME", "trunk")
45
+ capture = init_logging_captured()
46
+ logging.getLogger("scan").info("plain")
47
+ assert capture.contents().endswith("[INFO] [scan] plain\n")
48
+ assert root_logger.handlers
49
+
50
+
51
+ def test_a_traceback_is_kept(root_logger: logging.Logger) -> None:
52
+ capture = CapturedLog()
53
+ root_logger.addHandler(CaptureHandler(capture))
54
+ root_logger.setLevel(logging.INFO)
55
+ try:
56
+ message = "bad value"
57
+ raise ValueError(message) # noqa: TRY301
58
+ except ValueError:
59
+ logging.getLogger("scan").exception("failed")
60
+ assert "Traceback" in capture.contents()
61
+ assert "ValueError: bad value" in capture.contents()
62
+
63
+
64
+ def test_a_long_log_is_kept_whole() -> None:
65
+ capture = CapturedLog()
66
+ lines = [f"line {number}\n" for number in range(100_000)]
67
+ for line in lines:
68
+ _written(capture, line)
69
+ assert capture.contents() == "".join(lines)
@@ -1,10 +1,18 @@
1
+ import io
1
2
  import logging
2
3
  import sys
4
+ import time
5
+ from queue import Queue
3
6
 
4
7
  import pytest
5
8
  import simplejson as json
6
9
 
7
10
  from fluidattacks_core.logging import init_logging
11
+ from fluidattacks_core.logging.handlers import (
12
+ CustomQueueHandler,
13
+ ProductionAsyncHandler,
14
+ TelemetryAsyncHandler,
15
+ )
8
16
  from fluidattacks_core.logging.utils import set_telemetry_metadata
9
17
 
10
18
 
@@ -321,3 +329,80 @@ def test_colorful_formatter_uses_colors_for_warning(
321
329
  assert "\x1b[0m" in log_message
322
330
  assert "[WARNING] [product]" in log_message
323
331
  assert "A warning message" in log_message
332
+
333
+
334
+ def _listener_consumes_new_records(handler: ProductionAsyncHandler | TelemetryAsyncHandler) -> bool:
335
+ queue = handler.queue
336
+ assert isinstance(queue, Queue)
337
+
338
+ record = logging.LogRecord("test", logging.INFO, __file__, 1, "Test message", None, None)
339
+ queue.put_nowait(record)
340
+ time.sleep(0.2)
341
+
342
+ return queue.empty()
343
+
344
+
345
+ def test_custom_queue_handler_defaults_leave_no_listener() -> None:
346
+ # A fresh handler carries no listener on any interpreter: the class default
347
+ # leaves it empty, and `QueueHandler.__init__` clears it on Python 3.12+.
348
+ # Attaching is therefore the only thing that can install one, which the
349
+ # subclass tests below observe.
350
+ queue: Queue[logging.LogRecord] = Queue()
351
+
352
+ handler = CustomQueueHandler(queue)
353
+
354
+ assert handler.listener is None
355
+ assert handler.shutting_down is False
356
+
357
+
358
+ def test_production_async_handler_ends_init_with_a_running_listener() -> None:
359
+ handler = ProductionAsyncHandler(io.StringIO())
360
+ listener = handler.listener
361
+
362
+ handler.close()
363
+
364
+ assert listener is not None
365
+ assert isinstance(handler.queue, Queue)
366
+
367
+
368
+ def test_telemetry_async_handler_ends_init_with_a_running_listener() -> None:
369
+ handler = TelemetryAsyncHandler("test-service", "test-source", "test-token")
370
+ listener = handler.listener
371
+
372
+ handler.close()
373
+
374
+ assert listener is not None
375
+ assert isinstance(handler.queue, Queue)
376
+
377
+
378
+ def test_production_async_handler_close_stops_listener_and_is_idempotent() -> None:
379
+ handler = ProductionAsyncHandler(io.StringIO())
380
+
381
+ handler.close()
382
+ handler.close()
383
+
384
+ assert not _listener_consumes_new_records(handler)
385
+
386
+
387
+ def test_telemetry_async_handler_close_stops_listener_and_is_idempotent() -> None:
388
+ handler = TelemetryAsyncHandler("test-service", "test-source", "test-token")
389
+
390
+ handler.close()
391
+ handler.close()
392
+
393
+ assert not _listener_consumes_new_records(handler)
394
+
395
+
396
+ def test_async_handler_init_leaves_a_closable_handler() -> None:
397
+ # The class-level defaults make a close() racing __init__ a safe no-op, but
398
+ # __init__ must still finish with a handler that a later close() can stop:
399
+ # a listener left running behind an already-true shutting_down would never
400
+ # be drained.
401
+ handler = ProductionAsyncHandler(io.StringIO())
402
+
403
+ assert handler.shutting_down is False
404
+ assert handler.listener is not None
405
+
406
+ handler.close()
407
+
408
+ assert not _listener_consumes_new_records(handler)
@@ -148,7 +148,7 @@ wheels = [
148
148
 
149
149
  [[package]]
150
150
  name = "fluidattacks-core-logging"
151
- version = "12.0.0"
151
+ version = "12.1.0"
152
152
  source = { editable = "." }
153
153
  dependencies = [
154
154
  { name = "python-json-logger" },
@@ -1 +0,0 @@
1
- use_flake ".#python311.devShell"
@@ -1,5 +0,0 @@
1
- target/
2
- .direnv/
3
- .venv/
4
- .vscode/settings.json
5
- .zed/settings.json
@@ -1,19 +0,0 @@
1
- { src, workspace_root }:
2
- {
3
- inherit src workspace_root;
4
- root_path = "develops/fluidattacks-core/python/logging";
5
- module_name = "fluidattacks_core";
6
- pypi_token = {
7
- method = "oidc";
8
- };
9
- override = {
10
- checks = {
11
- basedpyright = false;
12
- };
13
- configs = {
14
- import_linter = ../import-linter.cfg;
15
- mypy = ../mypy.ini;
16
- ruff = ../ruff.toml;
17
- };
18
- };
19
- }
@@ -1,13 +0,0 @@
1
- path_filter: src:
2
- path_filter {
3
- root = src;
4
- include = [
5
- "fluidattacks_core"
6
- "tests"
7
- "import-linter.cfg"
8
- "mypy.ini"
9
- "pyproject.toml"
10
- "ruff.toml"
11
- "uv.lock"
12
- ];
13
- }
@@ -1,303 +0,0 @@
1
- {
2
- "nodes": {
3
- "flake-parts": {
4
- "inputs": {
5
- "nixpkgs-lib": "nixpkgs-lib"
6
- },
7
- "locked": {
8
- "lastModified": 1762810396,
9
- "narHash": "sha256-dxFVgQPG+R72dkhXTtqUm7KpxElw3u6E+YlQ2WaDgt8=",
10
- "owner": "hercules-ci",
11
- "repo": "flake-parts",
12
- "rev": "0bdadb1b265fb4143a75bd1ec7d8c915898a9923",
13
- "type": "github"
14
- },
15
- "original": {
16
- "owner": "hercules-ci",
17
- "repo": "flake-parts",
18
- "type": "github"
19
- }
20
- },
21
- "nix_filter": {
22
- "locked": {
23
- "lastModified": 1757882181,
24
- "narHash": "sha256-+cCxYIh2UNalTz364p+QYmWHs0P+6wDhiWR4jDIKQIU=",
25
- "owner": "numtide",
26
- "repo": "nix-filter",
27
- "rev": "59c44d1909c72441144b93cf0f054be7fe764de5",
28
- "type": "github"
29
- },
30
- "original": {
31
- "owner": "numtide",
32
- "repo": "nix-filter",
33
- "type": "github"
34
- }
35
- },
36
- "nixpkgs": {
37
- "locked": {
38
- "lastModified": 1758690382,
39
- "narHash": "sha256-NY3kSorgqE5LMm1LqNwGne3ZLMF2/ILgLpFr1fS4X3o=",
40
- "owner": "nixos",
41
- "repo": "nixpkgs",
42
- "rev": "e643668fd71b949c53f8626614b21ff71a07379d",
43
- "type": "github"
44
- },
45
- "original": {
46
- "owner": "nixos",
47
- "ref": "nixos-unstable",
48
- "repo": "nixpkgs",
49
- "type": "github"
50
- }
51
- },
52
- "nixpkgs-lib": {
53
- "locked": {
54
- "lastModified": 1761765539,
55
- "narHash": "sha256-b0yj6kfvO8ApcSE+QmA6mUfu8IYG6/uU28OFn4PaC8M=",
56
- "owner": "nix-community",
57
- "repo": "nixpkgs.lib",
58
- "rev": "719359f4562934ae99f5443f20aa06c2ffff91fc",
59
- "type": "github"
60
- },
61
- "original": {
62
- "owner": "nix-community",
63
- "repo": "nixpkgs.lib",
64
- "type": "github"
65
- }
66
- },
67
- "nixpkgs_2": {
68
- "locked": {
69
- "lastModified": 1758690382,
70
- "narHash": "sha256-NY3kSorgqE5LMm1LqNwGne3ZLMF2/ILgLpFr1fS4X3o=",
71
- "owner": "nixos",
72
- "repo": "nixpkgs",
73
- "rev": "e643668fd71b949c53f8626614b21ff71a07379d",
74
- "type": "github"
75
- },
76
- "original": {
77
- "owner": "nixos",
78
- "ref": "nixos-unstable",
79
- "repo": "nixpkgs",
80
- "type": "github"
81
- }
82
- },
83
- "nixpkgs_3": {
84
- "locked": {
85
- "lastModified": 1761373498,
86
- "narHash": "sha256-Q/uhWNvd7V7k1H1ZPMy/vkx3F8C13ZcdrKjO7Jv7v0c=",
87
- "owner": "nixos",
88
- "repo": "nixpkgs",
89
- "rev": "6a08e6bb4e46ff7fcbb53d409b253f6bad8a28ce",
90
- "type": "github"
91
- },
92
- "original": {
93
- "owner": "nixos",
94
- "ref": "nixos-unstable",
95
- "repo": "nixpkgs",
96
- "type": "github"
97
- }
98
- },
99
- "nixpkgs_flake": {
100
- "locked": {
101
- "lastModified": 1778253243,
102
- "narHash": "sha256-KkH2QvU6iRLDTGGEwH5jnMqHWj4zMTuHxi1S1zdr01w=",
103
- "owner": "nixos",
104
- "repo": "nixpkgs",
105
- "rev": "4fcda7103530b23c5ca44286344912082aad4220",
106
- "type": "github"
107
- },
108
- "original": {
109
- "owner": "nixos",
110
- "repo": "nixpkgs",
111
- "type": "github"
112
- }
113
- },
114
- "pyproject-build-systems": {
115
- "inputs": {
116
- "nixpkgs": "nixpkgs",
117
- "pyproject-nix": "pyproject-nix",
118
- "uv2nix": "uv2nix"
119
- },
120
- "locked": {
121
- "lastModified": 1761781027,
122
- "narHash": "sha256-YDvxPAm2WnxrznRqWwHLjryBGG5Ey1ATEJXrON+TWt8=",
123
- "owner": "pyproject-nix",
124
- "repo": "build-system-pkgs",
125
- "rev": "795a980d25301e5133eca37adae37283ec3c8e66",
126
- "type": "github"
127
- },
128
- "original": {
129
- "owner": "pyproject-nix",
130
- "repo": "build-system-pkgs",
131
- "type": "github"
132
- }
133
- },
134
- "pyproject-nix": {
135
- "inputs": {
136
- "nixpkgs": [
137
- "python_builder",
138
- "pyproject-build-systems",
139
- "nixpkgs"
140
- ]
141
- },
142
- "locked": {
143
- "lastModified": 1758265079,
144
- "narHash": "sha256-amLaLNwKSZPShQHzfgmc/9o76dU8xzN0743dWgvYlr8=",
145
- "owner": "nix-community",
146
- "repo": "pyproject.nix",
147
- "rev": "02e9418fd4af638447dca4b17b1280da95527fc9",
148
- "type": "github"
149
- },
150
- "original": {
151
- "owner": "nix-community",
152
- "repo": "pyproject.nix",
153
- "type": "github"
154
- }
155
- },
156
- "pyproject-nix_2": {
157
- "inputs": {
158
- "nixpkgs": "nixpkgs_2"
159
- },
160
- "locked": {
161
- "lastModified": 1762427963,
162
- "narHash": "sha256-CkPlAbIQ87wmjy5qHibfzk4DmMGBNqFer+lLfXjpP5M=",
163
- "owner": "pyproject-nix",
164
- "repo": "pyproject.nix",
165
- "rev": "4540ea004e04fcd12dd2738d51383d10f956f7b9",
166
- "type": "github"
167
- },
168
- "original": {
169
- "owner": "pyproject-nix",
170
- "repo": "pyproject.nix",
171
- "type": "github"
172
- }
173
- },
174
- "pyproject-nix_3": {
175
- "inputs": {
176
- "nixpkgs": [
177
- "python_builder",
178
- "uv2nix",
179
- "nixpkgs"
180
- ]
181
- },
182
- "locked": {
183
- "lastModified": 1760402624,
184
- "narHash": "sha256-jF6UKLs2uGc2rtved8Vrt58oTWjTQoAssuYs/0578Z4=",
185
- "owner": "pyproject-nix",
186
- "repo": "pyproject.nix",
187
- "rev": "84c4ea102127c77058ea1ed7be7300261fafc7d2",
188
- "type": "github"
189
- },
190
- "original": {
191
- "owner": "pyproject-nix",
192
- "repo": "pyproject.nix",
193
- "type": "github"
194
- }
195
- },
196
- "python_builder": {
197
- "inputs": {
198
- "nix_filter": "nix_filter",
199
- "nixpkgs_flake": "nixpkgs_flake",
200
- "pyproject-build-systems": "pyproject-build-systems",
201
- "pyproject-nix": "pyproject-nix_2",
202
- "shell-helpers": "shell-helpers",
203
- "uv2nix": "uv2nix_2"
204
- },
205
- "locked": {
206
- "dir": "common/utils/python-builder",
207
- "lastModified": 1787078362,
208
- "narHash": "sha256-HYL7Rn6SuNjYxkt/PwFLksZjsauCiY+ENbOWryLsIlw=",
209
- "ref": "refs/heads/trunk",
210
- "rev": "09fd25a13309eb5e3b5b2a061bb340df3ae5d281",
211
- "shallow": true,
212
- "type": "git",
213
- "url": "ssh://git@gitlab.com/fluidattacks/universe"
214
- },
215
- "original": {
216
- "dir": "common/utils/python-builder",
217
- "rev": "09fd25a13309eb5e3b5b2a061bb340df3ae5d281",
218
- "shallow": true,
219
- "type": "git",
220
- "url": "ssh://git@gitlab.com/fluidattacks/universe"
221
- }
222
- },
223
- "root": {
224
- "inputs": {
225
- "python_builder": "python_builder"
226
- }
227
- },
228
- "shell-helpers": {
229
- "inputs": {
230
- "flake-parts": "flake-parts",
231
- "nixpkgs": [
232
- "python_builder",
233
- "nixpkgs_flake"
234
- ]
235
- },
236
- "locked": {
237
- "dir": "common/utils/shell-helpers",
238
- "lastModified": 1762806066,
239
- "narHash": "sha256-Wz99Fl7SpPU1NkTHJ36XOo6DjOEAMXCw3ThRABOS2Hs=",
240
- "ref": "refs/heads/trunk",
241
- "rev": "d73cfdc7ae9e7f547e22a4481d4c2ccce38b26a8",
242
- "shallow": true,
243
- "type": "git",
244
- "url": "ssh://git@gitlab.com/fluidattacks/universe"
245
- },
246
- "original": {
247
- "dir": "common/utils/shell-helpers",
248
- "rev": "d73cfdc7ae9e7f547e22a4481d4c2ccce38b26a8",
249
- "shallow": true,
250
- "type": "git",
251
- "url": "ssh://git@gitlab.com/fluidattacks/universe"
252
- }
253
- },
254
- "uv2nix": {
255
- "inputs": {
256
- "nixpkgs": [
257
- "python_builder",
258
- "pyproject-build-systems",
259
- "nixpkgs"
260
- ],
261
- "pyproject-nix": [
262
- "python_builder",
263
- "pyproject-build-systems",
264
- "pyproject-nix"
265
- ]
266
- },
267
- "locked": {
268
- "lastModified": 1758933732,
269
- "narHash": "sha256-HAmm1GBS1myZCFuog0DC2ZLaynvZtiUI2Crmo+cdQI0=",
270
- "owner": "pyproject-nix",
271
- "repo": "uv2nix",
272
- "rev": "273ce18f913d8559e0d04f820d724308966d7c4d",
273
- "type": "github"
274
- },
275
- "original": {
276
- "owner": "pyproject-nix",
277
- "repo": "uv2nix",
278
- "type": "github"
279
- }
280
- },
281
- "uv2nix_2": {
282
- "inputs": {
283
- "nixpkgs": "nixpkgs_3",
284
- "pyproject-nix": "pyproject-nix_3"
285
- },
286
- "locked": {
287
- "lastModified": 1761872265,
288
- "narHash": "sha256-i25GRgp2vUOebY70L3NTAgkd+Pr1hnn5xM3qHxH0ONU=",
289
- "owner": "pyproject-nix",
290
- "repo": "uv2nix",
291
- "rev": "74dfb62871be152ad3673b143b0cc56105a4f3c5",
292
- "type": "github"
293
- },
294
- "original": {
295
- "owner": "pyproject-nix",
296
- "repo": "uv2nix",
297
- "type": "github"
298
- }
299
- }
300
- },
301
- "root": "root",
302
- "version": 7
303
- }
@@ -1,29 +0,0 @@
1
- {
2
- description = "Fluid Attacks Core logging package.";
3
-
4
- inputs = {
5
- python_builder = {
6
- url = "git+ssh://git@gitlab.com/fluidattacks/universe?shallow=1&rev=09fd25a13309eb5e3b5b2a061bb340df3ae5d281&dir=common/utils/python-builder";
7
- };
8
- };
9
-
10
- outputs =
11
- { self, ... }@inputs:
12
- let
13
- build_args =
14
- {
15
- system,
16
- python_version,
17
- nixpkgs,
18
- builders,
19
- scripts,
20
- }:
21
- import ./build {
22
- src = import ./build/filter.nix nixpkgs.nix-filter self;
23
- workspace_root = ./.;
24
- };
25
- in
26
- {
27
- packages = inputs.python_builder.outputs.build build_args;
28
- };
29
- }