@descryy/runtime-adapter-python 0.3.0 → 0.4.1

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.
@@ -1,39 +1,23 @@
1
1
  /**
2
- * Python `SourceLocationResolver`.
2
+ * Python SourceLocationResolver.
3
3
  *
4
- * **Every location is `self-contained`, and that is a positive claim rather
5
- * than a fallback.** CPython captures the path and line of the `.py` file it
6
- * is actually executing; there is no compilation step, no separate map, and
7
- * therefore nothing that can drift or go missing. The frame IS the answer.
8
- * Contrast the Node resolver, which must decide between four values because
9
- * a source map is a second file that can be absent, stale, or wrong.
4
+ * Every location is `self-contained` (a positive claim, not a fallback):
5
+ * CPython captures the path/line of the .py file it's actually executing,
6
+ * no compile step or map to drift. Contrast the Node resolver, which must
7
+ * choose between four values because a source map can be absent/stale/wrong.
10
8
  *
11
- * This is the first resolver in the codebase for which the reliability
12
- * decision is genuinely trivial, and the RT-003 enum already anticipates it:
13
- * `self-contained` means "nothing was lost", not "we didn't look".
9
+ * Deliberately does not check the file exists on disk or still matches what
10
+ * ran — `self-contained` is a statement about mechanism, not the working
11
+ * tree, and a stat call per frame buys only latency. Pseudo-files
12
+ * (`<string>`, `<stdin>`, `<frozen importlib._bootstrap>`) pass through verbatim.
14
13
  *
15
- * **What this deliberately does not do:** check that the file exists on
16
- * disk, or that its current contents still match what ran. Neither would
17
- * change the reliability value — `self-contained` is a statement about the
18
- * *mechanism*, not about whether the working tree has since been edited —
19
- * and a stat call per frame would buy nothing but latency. CPython's
20
- * pseudo-files (`<string>`, `<stdin>`, `<frozen importlib._bootstrap>`) are
21
- * passed through verbatim for the same reason: they are the honest location,
22
- * and rewriting them to null would discard real information.
23
- *
24
- * `column` is always null. CPython does not print columns; 3.11+ prints a
25
- * caret line under the failing expression, from which a column *could* be
26
- * inferred by counting characters. That inference is not made here — it
27
- * would be a derived guess presented in a field callers read as captured
28
- * fact, and the caret line is preserved verbatim in `StackFrame.raw`
29
- * regardless.
14
+ * `column` is always null — CPython doesn't print one. 3.11+'s caret line
15
+ * could be used to infer a column by counting characters, but that would be
16
+ * a derived guess in a field callers read as fact; the caret line stays verbatim in `StackFrame.raw`.
30
17
  */
31
18
  export function createPythonSourceLocationResolver() {
32
19
  return {
33
- // The interface is async because other languages must do I/O to answer
34
- // (Node reads a source map, a JVM reader would read a LineNumberTable).
35
- // Python genuinely does not, so this body never awaits — returning a
36
- // resolved promise rather than inventing work to look symmetrical.
20
+ // Async to satisfy the interface (other languages do real I/O here); Python never awaits.
37
21
  resolve(frame) {
38
22
  return Promise.resolve({
39
23
  file: frame.file,
@@ -1 +1 @@
1
- {"version":3,"file":"python-source-location-resolver.js","sourceRoot":"","sources":["../src/python-source-location-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAKH,MAAM,UAAU,kCAAkC;IAChD,OAAO;QACL,uEAAuE;QACvE,wEAAwE;QACxE,qEAAqE;QACrE,mEAAmE;QACnE,OAAO,CAAC,KAAuB;YAC7B,OAAO,OAAO,CAAC,OAAO,CAAC;gBACrB,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,YAAY,EAAE,KAAK,CAAC,YAAY;gBAChC,WAAW,EAAE,gBAAgB;gBAC7B,WAAW,EAAE,IAAI;aAClB,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"python-source-location-resolver.js","sourceRoot":"","sources":["../src/python-source-location-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAKH,MAAM,UAAU,kCAAkC;IAChD,OAAO;QACL,0FAA0F;QAC1F,OAAO,CAAC,KAAuB;YAC7B,OAAO,OAAO,CAAC,OAAO,CAAC;gBACrB,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,YAAY,EAAE,KAAK,CAAC,YAAY;gBAChC,WAAW,EAAE,gBAAgB;gBAC7B,WAAW,EAAE,IAAI;aAClB,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -1,26 +1,16 @@
1
1
  /**
2
- * Composes the pure CPython line-recognition functions
3
- * (`python-frame-parsing.ts`) with a `SourceLocationResolver` to implement
4
- * the full async `StackTraceParser` contract.
2
+ * Composes the pure CPython line-recognition functions (python-frame-parsing.ts)
3
+ * with a SourceLocationResolver to implement the async StackTraceParser contract.
5
4
  *
6
- * **The reversal is the load-bearing line in this file.** CPython prints
7
- * oldest call first — the banner says so out loud, "most recent call last" —
8
- * and `StackTrace.frames` is contractually most-recent-first. Without the
9
- * reversal every consumer that reads `frames[0]` as the failing frame would
10
- * instead get the process entry point: a wrong answer that looks entirely
11
- * well-formed, with the right frames, the right count, and no error
12
- * anywhere. Root-cause traversal would point at `main` for every Python
13
- * failure ever captured.
5
+ * The reversal is the load-bearing line: CPython prints oldest call first
6
+ * ("most recent call last") but StackTrace.frames is contractually
7
+ * most-recent-first. Without it, `frames[0]` would be the process entry
8
+ * point — well-formed-looking but wrong, and root-cause traversal would
9
+ * point at `main` for every Python failure.
14
10
  *
15
- * `fidelity` is always reported `"synchronous"`. A CPython traceback is
16
- * built by walking the actual frame objects of the executing thread, so
17
- * every frame shown is a genuine, unbroken link. What such a traceback
18
- * cannot show is the logical caller across an `await` boundary or a thread
19
- * hand-off, where the chain truthfully ends — the same hard limit V8 stacks
20
- * have, and the same reason this is not reported as
21
- * `"concurrency-fragmented"`. Detecting a genuinely lost logical caller
22
- * would need interpreter-level task correlation, not text parsing; claiming
23
- * fragmentation without that evidence would be a guess.
11
+ * `fidelity` is always `"synchronous"` — walks real frame objects of the
12
+ * executing thread, but cannot show a logical caller across an await
13
+ * boundary or thread hand-off, same limit V8 stacks have.
24
14
  */
25
15
  import type { SourceLocationResolver, StackTraceParser } from "@descryy/runtime-backend-observation";
26
16
  export declare function createPythonStackTraceParser(resolver: SourceLocationResolver): StackTraceParser;
@@ -1 +1 @@
1
- {"version":3,"file":"python-stack-trace-parser.d.ts","sourceRoot":"","sources":["../src/python-stack-trace-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,KAAK,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAIrG,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,sBAAsB,GAAG,gBAAgB,CAmD/F"}
1
+ {"version":3,"file":"python-stack-trace-parser.d.ts","sourceRoot":"","sources":["../src/python-stack-trace-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAIrG,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,sBAAsB,GAAG,gBAAgB,CAkC/F"}
@@ -1,26 +1,16 @@
1
1
  /**
2
- * Composes the pure CPython line-recognition functions
3
- * (`python-frame-parsing.ts`) with a `SourceLocationResolver` to implement
4
- * the full async `StackTraceParser` contract.
2
+ * Composes the pure CPython line-recognition functions (python-frame-parsing.ts)
3
+ * with a SourceLocationResolver to implement the async StackTraceParser contract.
5
4
  *
6
- * **The reversal is the load-bearing line in this file.** CPython prints
7
- * oldest call first — the banner says so out loud, "most recent call last" —
8
- * and `StackTrace.frames` is contractually most-recent-first. Without the
9
- * reversal every consumer that reads `frames[0]` as the failing frame would
10
- * instead get the process entry point: a wrong answer that looks entirely
11
- * well-formed, with the right frames, the right count, and no error
12
- * anywhere. Root-cause traversal would point at `main` for every Python
13
- * failure ever captured.
5
+ * The reversal is the load-bearing line: CPython prints oldest call first
6
+ * ("most recent call last") but StackTrace.frames is contractually
7
+ * most-recent-first. Without it, `frames[0]` would be the process entry
8
+ * point — well-formed-looking but wrong, and root-cause traversal would
9
+ * point at `main` for every Python failure.
14
10
  *
15
- * `fidelity` is always reported `"synchronous"`. A CPython traceback is
16
- * built by walking the actual frame objects of the executing thread, so
17
- * every frame shown is a genuine, unbroken link. What such a traceback
18
- * cannot show is the logical caller across an `await` boundary or a thread
19
- * hand-off, where the chain truthfully ends — the same hard limit V8 stacks
20
- * have, and the same reason this is not reported as
21
- * `"concurrency-fragmented"`. Detecting a genuinely lost logical caller
22
- * would need interpreter-level task correlation, not text parsing; claiming
23
- * fragmentation without that evidence would be a guess.
11
+ * `fidelity` is always `"synchronous"` — walks real frame objects of the
12
+ * executing thread, but cannot show a logical caller across an await
13
+ * boundary or thread hand-off, same limit V8 stacks have.
24
14
  */
25
15
  import { parsePythonFrame } from "./python-frame-parsing.js";
26
16
  export function createPythonStackTraceParser(resolver) {
@@ -30,10 +20,7 @@ export function createPythonStackTraceParser(resolver) {
30
20
  const frames = [];
31
21
  for (const line of lines) {
32
22
  const raw = parsePythonFrame(line);
33
- // Continuation lines (source text, 3.11+ caret markers) and the
34
- // banner and trailer parse to null here. They are real parts of the
35
- // block and are preserved in the raw text the collector keeps; they
36
- // are simply not frames and none is fabricated into one.
23
+ // Continuation lines, banner, and trailer parse to null — real parts of the block, never fabricated into a frame.
37
24
  if (raw === null)
38
25
  continue;
39
26
  const location = await resolver.resolve({
@@ -45,29 +32,15 @@ export function createPythonStackTraceParser(resolver) {
45
32
  frames.push({ location, raw: line });
46
33
  }
47
34
  if (frames.length === 0) {
48
- // Nothing frame-shaped at all — not recognisable as a CPython
49
- // traceback, per this interface's contract: null, never an
50
- // empty-but-structured StackTrace standing in for "couldn't parse".
35
+ // Nothing frame-shaped — null, never an empty StackTrace standing in for "couldn't parse".
51
36
  return null;
52
37
  }
53
- // CPython prints oldest-first; the contract is most-recent-first.
54
- //
55
- // `primaryFrameIndex` is required so that every adapter states an
56
- // answer rather than inheriting one (RT-083). Python's is 0 **after
57
- // the reversal on this line** — the last frame CPython prints is the
58
- // one that raised, and reversing puts it first.
59
- //
60
- // **Corrected from the value's first draft**, which justified index 0
61
- // with "CPython prints no frames of its own above the application's".
62
- // That is measurably false: `json.loads("{not json}")` called from a
63
- // two-deep application chain prints
64
- // `File "/usr/lib/python3.12/json/decoder.py", line 353, in raw_decode`
65
- // as the LAST frame, i.e. the innermost — three stdlib frames sit
66
- // above the application's own. Index 0 is still correct, but for the
67
- // other reason: it marks the failure site, not the nearest owned
68
- // code, and the contract says outright that a path-shaped stdlib
69
- // filter "would break on any application that legitimately fails
70
- // inside a library frame."
38
+ // primaryFrameIndex 0, after reversal (RT-083): the last frame CPython
39
+ // prints is the one that raised. Not because CPython prints no frames
40
+ // above the application's own — it can (three stdlib frames can sit
41
+ // above it, e.g. json.loads failures) — but because index 0 marks the
42
+ // failure site, not the nearest owned code; a path-shaped stdlib
43
+ // filter would break on any app that legitimately fails inside a library frame.
71
44
  return { fidelity: "synchronous", frames: frames.reverse(), primaryFrameIndex: 0 };
72
45
  },
73
46
  };
@@ -1 +1 @@
1
- {"version":3,"file":"python-stack-trace-parser.js","sourceRoot":"","sources":["../src/python-stack-trace-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAKH,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAE7D,MAAM,UAAU,4BAA4B,CAAC,QAAgC;IAC3E,OAAO;QACL,KAAK,CAAC,KAAK,CAAC,OAAe;YACzB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YAE1C,MAAM,MAAM,GAAiB,EAAE,CAAC;YAChC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,MAAM,GAAG,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;gBACnC,gEAAgE;gBAChE,oEAAoE;gBACpE,oEAAoE;gBACpE,yDAAyD;gBACzD,IAAI,GAAG,KAAK,IAAI;oBAAE,SAAS;gBAE3B,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC;oBACtC,IAAI,EAAE,GAAG,CAAC,IAAI;oBACd,IAAI,EAAE,GAAG,CAAC,IAAI;oBACd,MAAM,EAAE,IAAI;oBACZ,YAAY,EAAE,GAAG,CAAC,YAAY;iBAC/B,CAAC,CAAC;gBACH,MAAM,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YACvC,CAAC;YAED,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACxB,8DAA8D;gBAC9D,2DAA2D;gBAC3D,oEAAoE;gBACpE,OAAO,IAAI,CAAC;YACd,CAAC;YAED,kEAAkE;YAClE,EAAE;YACF,kEAAkE;YAClE,oEAAoE;YACpE,qEAAqE;YACrE,gDAAgD;YAChD,EAAE;YACF,sEAAsE;YACtE,sEAAsE;YACtE,qEAAqE;YACrE,oCAAoC;YACpC,wEAAwE;YACxE,kEAAkE;YAClE,qEAAqE;YACrE,iEAAiE;YACjE,iEAAiE;YACjE,iEAAiE;YACjE,2BAA2B;YAC3B,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,EAAE,iBAAiB,EAAE,CAAC,EAAE,CAAC;QACrF,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"python-stack-trace-parser.js","sourceRoot":"","sources":["../src/python-stack-trace-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAE7D,MAAM,UAAU,4BAA4B,CAAC,QAAgC;IAC3E,OAAO;QACL,KAAK,CAAC,KAAK,CAAC,OAAe;YACzB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YAE1C,MAAM,MAAM,GAAiB,EAAE,CAAC;YAChC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,MAAM,GAAG,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;gBACnC,kHAAkH;gBAClH,IAAI,GAAG,KAAK,IAAI;oBAAE,SAAS;gBAE3B,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC;oBACtC,IAAI,EAAE,GAAG,CAAC,IAAI;oBACd,IAAI,EAAE,GAAG,CAAC,IAAI;oBACd,MAAM,EAAE,IAAI;oBACZ,YAAY,EAAE,GAAG,CAAC,YAAY;iBAC/B,CAAC,CAAC;gBACH,MAAM,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YACvC,CAAC;YAED,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACxB,2FAA2F;gBAC3F,OAAO,IAAI,CAAC;YACd,CAAC;YAED,uEAAuE;YACvE,sEAAsE;YACtE,oEAAoE;YACpE,sEAAsE;YACtE,iEAAiE;YACjE,gFAAgF;YAChF,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,EAAE,iBAAiB,EAAE,CAAC,EAAE,CAAC;QACrF,CAAC;KACF,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,9 +1,14 @@
1
1
  {
2
2
  "name": "@descryy/runtime-adapter-python",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "type": "module",
5
5
  "description": "Python runtime adapter: CPython traceback parsing, self-contained source locations, outbound httpx call-site instrumentation, the concrete RuntimeAdapter for Python.",
6
6
  "license": "UNLICENSED",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/descryhq-wq/descry-runtime.git",
10
+ "directory": "packages/adapter-python"
11
+ },
7
12
  "engines": {
8
13
  "node": ">=22.5"
9
14
  },
@@ -25,7 +30,7 @@
25
30
  "build": "tsc -b"
26
31
  },
27
32
  "dependencies": {
28
- "@descryy/runtime-contracts": "0.2.0",
29
- "@descryy/runtime-backend-observation": "0.2.0"
33
+ "@descryy/runtime-contracts": "0.3.1",
34
+ "@descryy/runtime-backend-observation": "0.3.1"
30
35
  }
31
36
  }