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.
Files changed (54) hide show
  1. pyscriptc/__init__.py +3 -0
  2. pyscriptc/__main__.py +3 -0
  3. pyscriptc/_posthog_token.py +1 -0
  4. pyscriptc/builtins.py +156 -0
  5. pyscriptc/canonical.py +167 -0
  6. pyscriptc/check.py +49 -0
  7. pyscriptc/cli.py +271 -0
  8. pyscriptc/coverage.py +388 -0
  9. pyscriptc/debug.py +120 -0
  10. pyscriptc/diagnostics.py +179 -0
  11. pyscriptc/errors.py +56 -0
  12. pyscriptc/ir.py +365 -0
  13. pyscriptc/llvm.py +2129 -0
  14. pyscriptc/log.py +127 -0
  15. pyscriptc/lower/__init__.py +1071 -0
  16. pyscriptc/lower/_calls.py +1499 -0
  17. pyscriptc/lower/_classes.py +969 -0
  18. pyscriptc/lower/_core.py +301 -0
  19. pyscriptc/lower/_expr.py +886 -0
  20. pyscriptc/lower/_stdlib.py +491 -0
  21. pyscriptc/lower/_stdlib_shared.py +93 -0
  22. pyscriptc/lower/_stmt.py +1173 -0
  23. pyscriptc/package.py +249 -0
  24. pyscriptc/restlower.py +1008 -0
  25. pyscriptc/runtime/ps_argv.c +28 -0
  26. pyscriptc/runtime/ps_b64.c +168 -0
  27. pyscriptc/runtime/ps_coll.c +133 -0
  28. pyscriptc/runtime/ps_dt.c +388 -0
  29. pyscriptc/runtime/ps_hash.c +411 -0
  30. pyscriptc/runtime/ps_help.c +219 -0
  31. pyscriptc/runtime/ps_hmac.c +120 -0
  32. pyscriptc/runtime/ps_io.c +198 -0
  33. pyscriptc/runtime/ps_island.c +272 -0
  34. pyscriptc/runtime/ps_json.c +402 -0
  35. pyscriptc/runtime/ps_mix.c +47 -0
  36. pyscriptc/runtime/ps_more.c +1089 -0
  37. pyscriptc/runtime/ps_osmore.c +447 -0
  38. pyscriptc/runtime/ps_path.c +547 -0
  39. pyscriptc/runtime/ps_re.c +1102 -0
  40. pyscriptc/runtime/ps_rt.c +1175 -0
  41. pyscriptc/runtime/ps_rt.h +189 -0
  42. pyscriptc/runtime/ps_struct.c +291 -0
  43. pyscriptc/runtime/ps_time.c +207 -0
  44. pyscriptc/runtime/ps_unicode.c +1521 -0
  45. pyscriptc/runtime/ps_url.c +327 -0
  46. pyscriptc/stdlib_queue.py +191 -0
  47. pyscriptc/stdmore.py +1596 -0
  48. pyscriptc/telemetry.py +219 -0
  49. pyscriptc-0.1.0.dist-info/METADATA +187 -0
  50. pyscriptc-0.1.0.dist-info/RECORD +54 -0
  51. pyscriptc-0.1.0.dist-info/WHEEL +5 -0
  52. pyscriptc-0.1.0.dist-info/entry_points.txt +2 -0
  53. pyscriptc-0.1.0.dist-info/licenses/LICENSE +201 -0
  54. 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