pyscriptc 0.1.0__py3-none-any.whl
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.
- pyscriptc/__init__.py +3 -0
- pyscriptc/__main__.py +3 -0
- pyscriptc/_posthog_token.py +1 -0
- pyscriptc/builtins.py +156 -0
- pyscriptc/canonical.py +167 -0
- pyscriptc/check.py +49 -0
- pyscriptc/cli.py +271 -0
- pyscriptc/coverage.py +388 -0
- pyscriptc/debug.py +120 -0
- pyscriptc/diagnostics.py +179 -0
- pyscriptc/errors.py +56 -0
- pyscriptc/ir.py +365 -0
- pyscriptc/llvm.py +2129 -0
- pyscriptc/log.py +127 -0
- pyscriptc/lower/__init__.py +1071 -0
- pyscriptc/lower/_calls.py +1499 -0
- pyscriptc/lower/_classes.py +969 -0
- pyscriptc/lower/_core.py +301 -0
- pyscriptc/lower/_expr.py +886 -0
- pyscriptc/lower/_stdlib.py +491 -0
- pyscriptc/lower/_stdlib_shared.py +93 -0
- pyscriptc/lower/_stmt.py +1173 -0
- pyscriptc/package.py +249 -0
- pyscriptc/restlower.py +1008 -0
- pyscriptc/runtime/ps_argv.c +28 -0
- pyscriptc/runtime/ps_b64.c +168 -0
- pyscriptc/runtime/ps_coll.c +133 -0
- pyscriptc/runtime/ps_dt.c +388 -0
- pyscriptc/runtime/ps_hash.c +411 -0
- pyscriptc/runtime/ps_help.c +219 -0
- pyscriptc/runtime/ps_hmac.c +120 -0
- pyscriptc/runtime/ps_io.c +198 -0
- pyscriptc/runtime/ps_island.c +272 -0
- pyscriptc/runtime/ps_json.c +402 -0
- pyscriptc/runtime/ps_mix.c +47 -0
- pyscriptc/runtime/ps_more.c +1089 -0
- pyscriptc/runtime/ps_osmore.c +447 -0
- pyscriptc/runtime/ps_path.c +547 -0
- pyscriptc/runtime/ps_re.c +1102 -0
- pyscriptc/runtime/ps_rt.c +1175 -0
- pyscriptc/runtime/ps_rt.h +189 -0
- pyscriptc/runtime/ps_struct.c +291 -0
- pyscriptc/runtime/ps_time.c +207 -0
- pyscriptc/runtime/ps_unicode.c +1521 -0
- pyscriptc/runtime/ps_url.c +327 -0
- pyscriptc/stdlib_queue.py +191 -0
- pyscriptc/stdmore.py +1596 -0
- pyscriptc/telemetry.py +219 -0
- pyscriptc-0.1.0.dist-info/METADATA +187 -0
- pyscriptc-0.1.0.dist-info/RECORD +54 -0
- pyscriptc-0.1.0.dist-info/WHEEL +5 -0
- pyscriptc-0.1.0.dist-info/entry_points.txt +2 -0
- pyscriptc-0.1.0.dist-info/licenses/LICENSE +201 -0
- pyscriptc-0.1.0.dist-info/top_level.txt +1 -0
pyscriptc/log.py
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""Structured audit logging: a tiny structlog-shaped JSON-lines layer.
|
|
2
|
+
|
|
3
|
+
Zero-dependency by design (self-host + PyPI slimness). `get_logger()` returns
|
|
4
|
+
a logger with `.debug/.info/.warning/.error(event, **kv)` writing one JSON
|
|
5
|
+
record per line to `~/.cache/pyscriptc/log/YYYYMMDD.jsonl` (or
|
|
6
|
+
`$PYSC_LOG_DIR/YYYYMMDD.jsonl`). Records carry `ts`, `level`, `event`,
|
|
7
|
+
`pysc`, `py`, `pid` plus the caller's key-values.
|
|
8
|
+
|
|
9
|
+
Level is INFO by default; DEBUG when `PYSC_DEBUG=1` or the CLI `-v` flag
|
|
10
|
+
flag. Files rotate by day and are capped near 10 MiB: on overflow the
|
|
11
|
+
oldest lines are dropped. Paths are fine to log; never log source
|
|
12
|
+
contents. Writes never raise — logging must not break a compile.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import datetime
|
|
18
|
+
import json
|
|
19
|
+
import os
|
|
20
|
+
import platform
|
|
21
|
+
import sys
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
from pyscriptc import __version__
|
|
25
|
+
|
|
26
|
+
_LEVELS = {"debug": 10, "info": 20, "warning": 30, "error": 40}
|
|
27
|
+
_MAX_BYTES = 10 * 1024 * 1024
|
|
28
|
+
_debug = os.environ.get("PYSC_DEBUG") == "1"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def enable_debug() -> None:
|
|
32
|
+
"""Turn on DEBUG records for this process (the CLI `--debug` flag)."""
|
|
33
|
+
global _debug
|
|
34
|
+
_debug = True
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def log_dir() -> Path:
|
|
38
|
+
override = os.environ.get("PYSC_LOG_DIR")
|
|
39
|
+
return Path(override) if override else Path.home() / ".cache" / "pyscriptc" / "log"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def log_path(now: datetime.date | None = None) -> Path:
|
|
43
|
+
day = (now or datetime.date.today()).strftime("%Y%m%d")
|
|
44
|
+
return log_dir() / f"{day}.jsonl"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _min_level() -> int:
|
|
48
|
+
if _debug or os.environ.get("PYSC_DEBUG") == "1":
|
|
49
|
+
return _LEVELS["debug"]
|
|
50
|
+
return _LEVELS["info"]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _cap(path: Path) -> None:
|
|
54
|
+
try:
|
|
55
|
+
if path.stat().st_size <= _MAX_BYTES:
|
|
56
|
+
return
|
|
57
|
+
lines = path.read_text(encoding="utf-8", errors="replace").splitlines(keepends=True)
|
|
58
|
+
keep: list[str] = []
|
|
59
|
+
size = 0
|
|
60
|
+
for line in reversed(lines):
|
|
61
|
+
size += len(line.encode("utf-8", errors="replace"))
|
|
62
|
+
if size > _MAX_BYTES:
|
|
63
|
+
break
|
|
64
|
+
keep.append(line)
|
|
65
|
+
path.write_text("".join(reversed(keep)), encoding="utf-8")
|
|
66
|
+
except OSError:
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _emit(level: str, event: str, kv: dict[str, object]) -> None:
|
|
71
|
+
if _LEVELS[level] < _min_level():
|
|
72
|
+
return
|
|
73
|
+
record = {
|
|
74
|
+
"ts": datetime.datetime.now(datetime.UTC).isoformat(timespec="milliseconds"),
|
|
75
|
+
"level": level,
|
|
76
|
+
"event": event,
|
|
77
|
+
"pysc": __version__,
|
|
78
|
+
"py": platform.python_version(),
|
|
79
|
+
"pid": os.getpid(),
|
|
80
|
+
**kv,
|
|
81
|
+
}
|
|
82
|
+
try:
|
|
83
|
+
path = log_path()
|
|
84
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
85
|
+
with path.open("a", encoding="utf-8") as fh:
|
|
86
|
+
fh.write(json.dumps(record, default=str) + "\n")
|
|
87
|
+
_cap(path)
|
|
88
|
+
except OSError:
|
|
89
|
+
pass
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class Logger:
|
|
93
|
+
"""Structlog-shaped API: `log.info("compile.start", source=..., fences=...)`."""
|
|
94
|
+
|
|
95
|
+
def debug(self, event: str, **kv: object) -> None:
|
|
96
|
+
_emit("debug", event, kv)
|
|
97
|
+
|
|
98
|
+
def info(self, event: str, **kv: object) -> None:
|
|
99
|
+
_emit("info", event, kv)
|
|
100
|
+
|
|
101
|
+
def warning(self, event: str, **kv: object) -> None:
|
|
102
|
+
_emit("warning", event, kv)
|
|
103
|
+
|
|
104
|
+
def error(self, event: str, **kv: object) -> None:
|
|
105
|
+
_emit("error", event, kv)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def get_logger() -> Logger:
|
|
109
|
+
return Logger()
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def tail(lines: int = 50, out: object = None) -> Path:
|
|
113
|
+
"""Print the audit-file path and its last `lines` lines. Returns the path."""
|
|
114
|
+
stream = out if out is not None else sys.stdout
|
|
115
|
+
path = log_path()
|
|
116
|
+
print(path, file=stream)
|
|
117
|
+
if not path.exists():
|
|
118
|
+
print("(no records yet)", file=stream)
|
|
119
|
+
return path
|
|
120
|
+
try:
|
|
121
|
+
content = path.read_text(encoding="utf-8", errors="replace").splitlines()
|
|
122
|
+
except OSError as exc:
|
|
123
|
+
print(f"(unreadable: {exc})", file=stream)
|
|
124
|
+
return path
|
|
125
|
+
for line in content[-lines:]:
|
|
126
|
+
print(line, file=stream)
|
|
127
|
+
return path
|