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
|