snakemake-logger-plugin-spectio 0.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.
@@ -0,0 +1,22 @@
1
+ Metadata-Version: 2.4
2
+ Name: snakemake-logger-plugin-spectio
3
+ Version: 0.0.0
4
+ Summary: Snakemake logger plugin that writes run events as JSON lines for Spectio (early release)
5
+ License-Expression: MIT
6
+ Requires-Dist: snakemake-interface-logger-plugins>=2.0,<3
7
+ Requires-Python: >=3.11
8
+ Project-URL: Repository, https://github.com/spectio-org/spectio
9
+ Description-Content-Type: text/markdown
10
+
11
+ # snakemake-logger-plugin-spectio
12
+
13
+ A Snakemake logger plugin for [Spectio](https://github.com/spectio-org/spectio), the tool that launches, monitors and steers Nextflow and Snakemake runs.
14
+
15
+ This early `0.0.0` release writes every Snakemake log event as one JSON line to a file (`--logger-spectio-file`, default `spectio-events.jsonl`, created 0600, never through a symlink), so another program can follow a run:
16
+
17
+ ```bash
18
+ pip install snakemake-logger-plugin-spectio
19
+ snakemake --cores 1 --logger spectio --logger-spectio-file events.jsonl
20
+ ```
21
+
22
+ Each line holds the time, the level, the event name (`job_info`, `job_finished`, `error`…), the message and the event's own fields (rule name, job ID…). The full Spectio bridge comes with Spectio's first release.
@@ -0,0 +1,12 @@
1
+ # snakemake-logger-plugin-spectio
2
+
3
+ A Snakemake logger plugin for [Spectio](https://github.com/spectio-org/spectio), the tool that launches, monitors and steers Nextflow and Snakemake runs.
4
+
5
+ This early `0.0.0` release writes every Snakemake log event as one JSON line to a file (`--logger-spectio-file`, default `spectio-events.jsonl`, created 0600, never through a symlink), so another program can follow a run:
6
+
7
+ ```bash
8
+ pip install snakemake-logger-plugin-spectio
9
+ snakemake --cores 1 --logger spectio --logger-spectio-file events.jsonl
10
+ ```
11
+
12
+ Each line holds the time, the level, the event name (`job_info`, `job_finished`, `error`…), the message and the event's own fields (rule name, job ID…). The full Spectio bridge comes with Spectio's first release.
@@ -0,0 +1,15 @@
1
+ [project]
2
+ name = "snakemake-logger-plugin-spectio"
3
+ version = "0.0.0"
4
+ description = "Snakemake logger plugin that writes run events as JSON lines for Spectio (early release)"
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ requires-python = ">=3.11"
8
+ dependencies = ["snakemake-interface-logger-plugins>=2.0,<3"]
9
+
10
+ [project.urls]
11
+ Repository = "https://github.com/spectio-org/spectio"
12
+
13
+ [build-system]
14
+ requires = ["uv_build==0.11.32"] # = UV_VERSION in dev/versions.env
15
+ build-backend = "uv_build"
@@ -0,0 +1,79 @@
1
+ """Snakemake logger plugin: one JSON line per log event, for Spectio."""
2
+
3
+ import json
4
+ import logging
5
+ import os
6
+ import stat
7
+ from dataclasses import dataclass, field
8
+
9
+ from snakemake_interface_logger_plugins.base import LogHandlerBase
10
+ from snakemake_interface_logger_plugins.settings import LogHandlerSettingsBase
11
+
12
+ # Attributes every LogRecord has; anything else on a record is an event field set by Snakemake.
13
+ _STANDARD = set(vars(logging.makeLogRecord({}))) | {"message", "asctime"}
14
+
15
+
16
+ @dataclass
17
+ class LogHandlerSettings(LogHandlerSettingsBase):
18
+ file: str = field(
19
+ default="spectio-events.jsonl",
20
+ metadata={"help": "File the JSON lines are appended to."},
21
+ )
22
+
23
+
24
+ class LogHandler(LogHandlerBase):
25
+ def __post_init__(self) -> None:
26
+ # The CLI always passes settings; the interface's type allows None (an API caller).
27
+ settings = self.settings or LogHandlerSettings()
28
+ # Snakemake reads baseFilename from file-writing handlers, as on logging.FileHandler.
29
+ self.baseFilename = os.path.abspath(settings.file)
30
+ # O_NOFOLLOW: a symlink planted at the path is refused, never followed.
31
+ # O_NONBLOCK: a FIFO planted at the path fails or is refused below, instead of blocking the run.
32
+ flags = os.O_WRONLY | os.O_APPEND | os.O_CREAT | os.O_NOFOLLOW | os.O_NONBLOCK
33
+ fd = os.open(self.baseFilename, flags, 0o600)
34
+ st = os.fstat(fd)
35
+ if not stat.S_ISREG(st.st_mode) or st.st_uid != os.getuid() or st.st_nlink != 1:
36
+ os.close(fd)
37
+ raise PermissionError(f"refusing {self.baseFilename}: not a regular file of ours with one link")
38
+ os.fchmod(fd, 0o600)
39
+ self._out = os.fdopen(fd, "a", encoding="utf-8")
40
+
41
+ def emit(self, record: logging.LogRecord) -> None:
42
+ try:
43
+ line = {
44
+ "time": record.created,
45
+ "level": record.levelname,
46
+ "event": str(getattr(record, "event", "")) or None,
47
+ "message": record.getMessage(),
48
+ }
49
+ for key, value in vars(record).items():
50
+ if key not in _STANDARD and key not in line:
51
+ line[key] = value
52
+ self._out.write(json.dumps(line, default=str) + "\n")
53
+ self._out.flush()
54
+ except Exception:
55
+ self.handleError(record)
56
+
57
+ def close(self) -> None:
58
+ self._out.close()
59
+ super().close()
60
+
61
+ @property
62
+ def writes_to_stream(self) -> bool:
63
+ return False
64
+
65
+ @property
66
+ def writes_to_file(self) -> bool:
67
+ return True
68
+
69
+ @property
70
+ def has_filter(self) -> bool:
71
+ return False
72
+
73
+ @property
74
+ def has_formatter(self) -> bool:
75
+ return False
76
+
77
+ @property
78
+ def needs_rulegraph(self) -> bool:
79
+ return False