tywrap 0.6.1 → 0.8.0

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 (110) hide show
  1. package/README.md +15 -5
  2. package/dist/core/annotation-parser.d.ts.map +1 -1
  3. package/dist/core/annotation-parser.js.map +1 -1
  4. package/dist/core/emit-call.d.ts.map +1 -1
  5. package/dist/core/emit-call.js +1 -1
  6. package/dist/core/emit-call.js.map +1 -1
  7. package/dist/core/generator.d.ts.map +1 -1
  8. package/dist/core/generator.js +40 -9
  9. package/dist/core/generator.js.map +1 -1
  10. package/dist/dev.d.ts.map +1 -1
  11. package/dist/dev.js +1 -3
  12. package/dist/dev.js.map +1 -1
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js.map +1 -1
  16. package/dist/runtime/base-bridge.d.ts +57 -0
  17. package/dist/runtime/base-bridge.d.ts.map +1 -0
  18. package/dist/runtime/base-bridge.js +72 -0
  19. package/dist/runtime/base-bridge.js.map +1 -0
  20. package/dist/runtime/frame-codec.d.ts +111 -0
  21. package/dist/runtime/frame-codec.d.ts.map +1 -0
  22. package/dist/runtime/frame-codec.js +352 -0
  23. package/dist/runtime/frame-codec.js.map +1 -0
  24. package/dist/runtime/http-transport.d.ts +11 -1
  25. package/dist/runtime/http-transport.d.ts.map +1 -1
  26. package/dist/runtime/http-transport.js +19 -0
  27. package/dist/runtime/http-transport.js.map +1 -1
  28. package/dist/runtime/http.d.ts +5 -12
  29. package/dist/runtime/http.d.ts.map +1 -1
  30. package/dist/runtime/http.js +6 -29
  31. package/dist/runtime/http.js.map +1 -1
  32. package/dist/runtime/index.d.ts +2 -2
  33. package/dist/runtime/index.d.ts.map +1 -1
  34. package/dist/runtime/index.js +1 -1
  35. package/dist/runtime/index.js.map +1 -1
  36. package/dist/runtime/node.d.ts +25 -19
  37. package/dist/runtime/node.d.ts.map +1 -1
  38. package/dist/runtime/node.js +19 -34
  39. package/dist/runtime/node.js.map +1 -1
  40. package/dist/runtime/pooled-transport.d.ts +21 -2
  41. package/dist/runtime/pooled-transport.d.ts.map +1 -1
  42. package/dist/runtime/pooled-transport.js +16 -0
  43. package/dist/runtime/pooled-transport.js.map +1 -1
  44. package/dist/runtime/pyodide-bootstrap-core.generated.d.ts.map +1 -1
  45. package/dist/runtime/pyodide-bootstrap-core.generated.js +1 -1
  46. package/dist/runtime/pyodide-bootstrap-core.generated.js.map +1 -1
  47. package/dist/runtime/pyodide-transport.d.ts +12 -1
  48. package/dist/runtime/pyodide-transport.d.ts.map +1 -1
  49. package/dist/runtime/pyodide-transport.js +20 -0
  50. package/dist/runtime/pyodide-transport.js.map +1 -1
  51. package/dist/runtime/pyodide.d.ts +5 -12
  52. package/dist/runtime/pyodide.d.ts.map +1 -1
  53. package/dist/runtime/pyodide.js +6 -29
  54. package/dist/runtime/pyodide.js.map +1 -1
  55. package/dist/runtime/rpc-client.d.ts +14 -1
  56. package/dist/runtime/rpc-client.d.ts.map +1 -1
  57. package/dist/runtime/rpc-client.js +68 -6
  58. package/dist/runtime/rpc-client.js.map +1 -1
  59. package/dist/runtime/subprocess-transport.d.ts +177 -3
  60. package/dist/runtime/subprocess-transport.d.ts.map +1 -1
  61. package/dist/runtime/subprocess-transport.js +526 -26
  62. package/dist/runtime/subprocess-transport.js.map +1 -1
  63. package/dist/runtime/transport.d.ts +141 -0
  64. package/dist/runtime/transport.d.ts.map +1 -1
  65. package/dist/runtime/transport.js +21 -0
  66. package/dist/runtime/transport.js.map +1 -1
  67. package/dist/types/index.d.ts +59 -0
  68. package/dist/types/index.d.ts.map +1 -1
  69. package/dist/tywrap.d.ts.map +1 -1
  70. package/dist/tywrap.js +204 -149
  71. package/dist/tywrap.js.map +1 -1
  72. package/dist/utils/codec.d.ts +2 -0
  73. package/dist/utils/codec.d.ts.map +1 -1
  74. package/dist/utils/codec.js +205 -6
  75. package/dist/utils/codec.js.map +1 -1
  76. package/dist/version.js +1 -1
  77. package/package.json +7 -1
  78. package/runtime/__pycache__/_tywrap_conformance_chunking_fixtures.cpython-311.pyc +0 -0
  79. package/runtime/__pycache__/_tywrap_member_fixtures.cpython-311.pyc +0 -0
  80. package/runtime/__pycache__/_tywrap_w4_chunking_fixture.cpython-311.pyc +0 -0
  81. package/runtime/__pycache__/_tywrap_w5_request_chunking_fixture.cpython-311.pyc +0 -0
  82. package/runtime/__pycache__/_tywrap_w6_pool_chunking_fixture.cpython-311.pyc +0 -0
  83. package/runtime/__pycache__/frame_codec.cpython-311.pyc +0 -0
  84. package/runtime/__pycache__/safe_codec.cpython-311.pyc +0 -0
  85. package/runtime/__pycache__/tywrap_bridge_core.cpython-311.pyc +0 -0
  86. package/runtime/frame_codec.py +424 -0
  87. package/runtime/python_bridge.py +241 -42
  88. package/runtime/tywrap_bridge_core.py +152 -13
  89. package/src/core/annotation-parser.ts +2 -1
  90. package/src/core/emit-call.ts +1 -7
  91. package/src/core/generator.ts +50 -11
  92. package/src/dev.ts +1 -3
  93. package/src/index.ts +1 -0
  94. package/src/runtime/base-bridge.ts +106 -0
  95. package/src/runtime/frame-codec.ts +469 -0
  96. package/src/runtime/http-transport.ts +21 -1
  97. package/src/runtime/http.ts +7 -51
  98. package/src/runtime/index.ts +2 -6
  99. package/src/runtime/node.ts +42 -53
  100. package/src/runtime/pooled-transport.ts +25 -2
  101. package/src/runtime/pyodide-bootstrap-core.generated.ts +1 -1
  102. package/src/runtime/pyodide-transport.ts +22 -0
  103. package/src/runtime/pyodide.ts +7 -52
  104. package/src/runtime/rpc-client.ts +91 -7
  105. package/src/runtime/subprocess-transport.ts +629 -30
  106. package/src/runtime/transport.ts +169 -0
  107. package/src/types/index.ts +62 -0
  108. package/src/tywrap.ts +265 -162
  109. package/src/utils/codec.ts +245 -7
  110. package/src/version.ts +1 -1
@@ -560,6 +560,7 @@ ${callPrelude}${guards} return getRuntimeBridge().call('${moduleId}', '${func.n
560
560
  this.getTypeParameters(cls.typeParameters),
561
561
  [
562
562
  ...cls.properties.map(property => property.type),
563
+ ...(cls.accessors ?? []).map(accessor => accessor.type),
563
564
  ...cls.methods.flatMap(method => [
564
565
  method.returnType,
565
566
  ...method.parameters.map(p => p.type),
@@ -604,6 +605,15 @@ ${callPrelude}${guards} return getRuntimeBridge().call('${moduleId}', '${func.n
604
605
  `${this.escapeIdentifier(p.name)}: ${this.typeToTsFromPython(p.type, classGenericContext, 'value')};`
605
606
  )
606
607
  .join(' ');
608
+ // @property / cached_property members are bridge-accessed getters; mirror
609
+ // the concrete class's `get name(): Promise<T>` as readonly Promise props
610
+ // so protocol typings include them too (IR 0.3.0).
611
+ const accessors = (cls.accessors ?? [])
612
+ .map(
613
+ a =>
614
+ `readonly ${this.escapeIdentifier(a.name)}: Promise<${this.typeToTsFromPython(a.type, classGenericContext, 'return')}>;`
615
+ )
616
+ .join(' ');
607
617
  const methods = cls.methods
608
618
  .filter(m => m.name !== '__init__')
609
619
  .map(m => {
@@ -628,7 +638,7 @@ ${callPrelude}${guards} return getRuntimeBridge().call('${moduleId}', '${func.n
628
638
  return `${this.escapeIdentifier(m.name)}: ${methodTypeParamDecl}(${paramsDecl}) => ${returnType};`;
629
639
  })
630
640
  .join(' ');
631
- return wrapAlias(`{ ${props} ${methods} }`);
641
+ return wrapAlias(`{ ${props} ${accessors} ${methods} }`);
632
642
  }
633
643
 
634
644
  if (cls.kind === 'dataclass' || cls.kind === 'pydantic') {
@@ -643,6 +653,7 @@ ${callPrelude}${guards} return getRuntimeBridge().call('${moduleId}', '${func.n
643
653
  return wrapAlias(`{ ${props} }`);
644
654
  }
645
655
 
656
+ const moduleId = moduleName ?? '__main__';
646
657
  const sortedMethods = [...cls.methods].sort((a, b) => a.name.localeCompare(b.name));
647
658
  const methodBodies: string[] = [];
648
659
  const methodDeclarations: string[] = [];
@@ -650,6 +661,12 @@ ${callPrelude}${guards} return getRuntimeBridge().call('${moduleId}', '${func.n
650
661
  sortedMethods
651
662
  .filter(method => method.name !== '__init__')
652
663
  .forEach(method => {
664
+ // @classmethod and @staticmethod become `static` members invoked through
665
+ // the class (`Class.method`), not the instance handle. Instance methods
666
+ // (the default, or an absent methodKind) keep the existing emission so
667
+ // their output stays byte-identical.
668
+ const isStatic = method.methodKind === 'class' || method.methodKind === 'static';
669
+ const staticPrefix = isStatic ? 'static ' : '';
653
670
  const fparams = method.parameters.filter(p => p.name !== 'self' && p.name !== 'cls');
654
671
  const methodOwnGenericContext = this.buildGenericRenderContext(
655
672
  this.getTypeParameters(method.typeParameters),
@@ -737,11 +754,11 @@ ${callPrelude}${guards} return getRuntimeBridge().call('${moduleId}', '${func.n
737
754
  }
738
755
  rest.push(`kwargs: ${kwargsType}`);
739
756
  overloads.push(
740
- ` ${mname}${methodTypeParamDecl}(${[...head, ...rest].join(', ')}): Promise<${returnType}>;`
757
+ ` ${staticPrefix}${mname}${methodTypeParamDecl}(${[...head, ...rest].join(', ')}): Promise<${returnType}>;`
741
758
  );
742
759
  if (varArgsParam && needsVarArgsArray) {
743
760
  overloads.push(
744
- ` ${mname}${methodTypeParamDecl}(${[...head, `kwargs: ${kwargsType}`].join(', ')}): Promise<${returnType}>;`
761
+ ` ${staticPrefix}${mname}${methodTypeParamDecl}(${[...head, `kwargs: ${kwargsType}`].join(', ')}): Promise<${returnType}>;`
745
762
  );
746
763
  }
747
764
  }
@@ -767,13 +784,18 @@ ${callPrelude}${guards} return getRuntimeBridge().call('${moduleId}', '${func.n
767
784
  const guardLines = emitArgGuards(callDescriptor);
768
785
  const guards = guardLines.length > 0 ? `${guardLines.join('\n')}\n` : '';
769
786
 
770
- methodBodies.push(`${overloadDecl} async ${mname}${methodTypeParamDecl}(${paramsDecl}): Promise<${returnType}> {
771
- ${callPrelude}${guards} return getRuntimeBridge().callMethod(this.__handle, '${method.name}', __args${
772
- needsKwargsParam ? ', __kwargs' : ''
773
- });
787
+ const callExpr = isStatic
788
+ ? `getRuntimeBridge().call('${moduleId}', '${cls.name}.${method.name}', __args${
789
+ needsKwargsParam ? ', __kwargs' : ''
790
+ })`
791
+ : `getRuntimeBridge().callMethod(this.__handle, '${method.name}', __args${
792
+ needsKwargsParam ? ', __kwargs' : ''
793
+ })`;
794
+ methodBodies.push(`${overloadDecl} ${staticPrefix}async ${mname}${methodTypeParamDecl}(${paramsDecl}): Promise<${returnType}> {
795
+ ${callPrelude}${guards} return ${callExpr};
774
796
  }`);
775
797
  methodDeclarations.push(
776
- `${overloadDecl}${overloads.length > 0 ? '' : ` ${mname}${methodTypeParamDecl}(${paramsDecl}): Promise<${returnType}>;\n`}`
798
+ `${overloadDecl}${overloads.length > 0 ? '' : ` ${staticPrefix}${mname}${methodTypeParamDecl}(${paramsDecl}): Promise<${returnType}>;\n`}`
777
799
  );
778
800
  });
779
801
 
@@ -898,7 +920,24 @@ ${callPrelude}${guards} return getRuntimeBridge().callMethod(this.__handle, '
898
920
  };
899
921
  })();
900
922
 
901
- const moduleId = moduleName ?? '__main__';
923
+ // @property / functools.cached_property TS getters returning Promise<T>.
924
+ // Reading the attribute fires the getter on the Python side; the bridge
925
+ // resolves it via callMethod with no args. Sorted for stable output.
926
+ const accessorBodies: string[] = [];
927
+ const accessorDeclarations: string[] = [];
928
+ const sortedAccessors = [...(cls.accessors ?? [])].sort((a, b) => a.name.localeCompare(b.name));
929
+ sortedAccessors.forEach(accessor => {
930
+ const aname = this.escapeIdentifier(accessor.name);
931
+ const accessorType = this.typeToTsFromPython(accessor.type, classGenericContext, 'return');
932
+ const jsdocAcc = this.generateJsDoc(accessor.docstring);
933
+ accessorBodies.push(
934
+ `${jsdocAcc} get ${aname}(): Promise<${accessorType}> { return getRuntimeBridge().callMethod(this.__handle, '${accessor.name}', []); }`
935
+ );
936
+ accessorDeclarations.push(`${jsdocAcc} get ${aname}(): Promise<${accessorType}>;\n`);
937
+ });
938
+ const accessorsImpl = accessorBodies.length > 0 ? `${accessorBodies.join('\n')}\n` : '';
939
+ const accessorsDecl = accessorDeclarations.length > 0 ? `${accessorDeclarations.join('')}` : '';
940
+
902
941
  const methodsSection = methodBodies.length > 0 ? `\n${methodBodies.join('\n')}\n` : '\n';
903
942
  const declarationMethodsSection =
904
943
  methodDeclarations.length > 0 ? `\n${methodDeclarations.join('')}\n` : '\n';
@@ -913,14 +952,14 @@ ${ctorSpec.callPrelude}${ctorGuards} const handle = await getRuntimeBridge().
913
952
  });
914
953
  return ${newClassExpr};
915
954
  }
916
- static fromHandle${classTypeParamDecl}(handle: string): ${classSelfType} { return ${newClassExpr}; }${methodsSection} async disposeHandle(): Promise<void> { await getRuntimeBridge().disposeInstance(this.__handle); }
955
+ static fromHandle${classTypeParamDecl}(handle: string): ${classSelfType} { return ${newClassExpr}; }${methodsSection}${accessorsImpl} async disposeHandle(): Promise<void> { await getRuntimeBridge().disposeInstance(this.__handle); }
917
956
  }
918
957
  `;
919
958
 
920
959
  const declaration = `${jsdoc}export class ${cname}${classTypeParamDecl} {
921
960
  private readonly __handle: string;
922
961
  private constructor(handle: string);
923
- ${ctorSpec.declaration} static fromHandle${classTypeParamDecl}(handle: string): ${classSelfType};${declarationMethodsSection} disposeHandle(): Promise<void>;
962
+ ${ctorSpec.declaration} static fromHandle${classTypeParamDecl}(handle: string): ${classSelfType};${declarationMethodsSection}${accessorsDecl} disposeHandle(): Promise<void>;
924
963
  }
925
964
  `;
926
965
 
package/src/dev.ts CHANGED
@@ -343,9 +343,7 @@ async function listChildDirectories(current: string): Promise<string[]> {
343
343
  return [];
344
344
  }
345
345
 
346
- return entries
347
- .filter(entry => entry.isDirectory())
348
- .map(entry => join(current, entry.name));
346
+ return entries.filter(entry => entry.isDirectory()).map(entry => join(current, entry.name));
349
347
  }
350
348
 
351
349
  async function isWatchableDirectory(current: string, ignoredPaths: string[]): Promise<boolean> {
package/src/index.ts CHANGED
@@ -68,6 +68,7 @@ export type {
68
68
  TypeMappingConfig,
69
69
  TypePreset,
70
70
  BridgeInfo,
71
+ BridgeTransportInfo,
71
72
  AnalysisResult,
72
73
  AnalysisError,
73
74
  AnalysisWarning,
@@ -0,0 +1,106 @@
1
+ /**
2
+ * BasePythonBridge - shared RPC delegation for the bridge facades.
3
+ *
4
+ * The three bridge facades (NodeBridge/HttpBridge/PyodideBridge) all extend
5
+ * DisposableBase (lifecycle/resources) and implement PythonRuntime by HOLDING
6
+ * an RpcClient. The PythonRuntime delegation was byte-identical across all
7
+ * three: each method does `await this.ensureReady()` then forwards to the held
8
+ * RpcClient. This base collapses that duplication onto a single
9
+ * `getRpcClient()` accessor while leaving each facade free to own its own
10
+ * RpcClient field (for constructor wiring, resource tracking, and — in
11
+ * NodeBridge's case — a caching override of call()).
12
+ *
13
+ * It carries no transport/codec/lifecycle specifics: doInit/doDispose remain
14
+ * abstract on DisposableBase and stay per-facade.
15
+ *
16
+ * @see https://github.com/bbopen/tywrap/issues/149
17
+ */
18
+
19
+ import type { PythonRuntime, BridgeInfo } from '../types/index.js';
20
+
21
+ import { DisposableBase } from './bounded-context.js';
22
+ import type { RpcClient, GetBridgeInfoOptions } from './rpc-client.js';
23
+ import type { TransportCapabilities } from './transport.js';
24
+
25
+ /**
26
+ * Shared base for the bridge facades. Implements the PythonRuntime RPC
27
+ * delegation (plus getBridgeInfo) over a single held RpcClient, exposed by the
28
+ * abstract {@link getRpcClient} accessor. Lifecycle (doInit/doDispose) and the
29
+ * RpcClient's construction/ownership remain the subclass's responsibility.
30
+ */
31
+ export abstract class BasePythonBridge extends DisposableBase implements PythonRuntime {
32
+ /**
33
+ * Return the held RpcClient that the shared delegating methods forward to.
34
+ * Subclasses construct, track, and own the RpcClient; this accessor lets the
35
+ * base reach it without dictating how it is stored.
36
+ */
37
+ protected abstract getRpcClient(): RpcClient;
38
+
39
+ // ===========================================================================
40
+ // RPC METHODS (delegate to the held RpcClient)
41
+ // ===========================================================================
42
+
43
+ async call<T = unknown>(
44
+ module: string,
45
+ functionName: string,
46
+ args: unknown[],
47
+ kwargs?: Record<string, unknown>
48
+ ): Promise<T> {
49
+ await this.ensureReady();
50
+ return this.getRpcClient().call<T>(module, functionName, args, kwargs);
51
+ }
52
+
53
+ async instantiate<T = unknown>(
54
+ module: string,
55
+ className: string,
56
+ args: unknown[],
57
+ kwargs?: Record<string, unknown>
58
+ ): Promise<T> {
59
+ await this.ensureReady();
60
+ return this.getRpcClient().instantiate<T>(module, className, args, kwargs);
61
+ }
62
+
63
+ async callMethod<T = unknown>(
64
+ handle: string,
65
+ methodName: string,
66
+ args: unknown[],
67
+ kwargs?: Record<string, unknown>
68
+ ): Promise<T> {
69
+ await this.ensureReady();
70
+ return this.getRpcClient().callMethod<T>(handle, methodName, args, kwargs);
71
+ }
72
+
73
+ async disposeInstance(handle: string): Promise<void> {
74
+ await this.ensureReady();
75
+ return this.getRpcClient().disposeInstance(handle);
76
+ }
77
+
78
+ /**
79
+ * Fetch bridge diagnostics and feature availability.
80
+ */
81
+ async getBridgeInfo(options?: GetBridgeInfoOptions): Promise<BridgeInfo> {
82
+ await this.ensureReady();
83
+ return this.getRpcClient().getBridgeInfo(options);
84
+ }
85
+
86
+ /**
87
+ * Describe this backend's transport-level capabilities (Arrow/binary/chunking/
88
+ * streaming support, max frame size). The descriptor is static and does NOT
89
+ * depend on lifecycle state, so this intentionally does not `ensureReady()` —
90
+ * callers can query it before connecting to decide how to use the bridge.
91
+ */
92
+ capabilities(): TransportCapabilities {
93
+ return this.getRpcClient().capabilities();
94
+ }
95
+
96
+ /**
97
+ * Ensure the facade is initialized before delegating an RPC. Replicates the
98
+ * auto-init that the bounded execute path provided pre-composition, so the
99
+ * facade's own doInit pre-work runs before any RPC.
100
+ */
101
+ protected async ensureReady(): Promise<void> {
102
+ if (!this.isReady) {
103
+ await this.init();
104
+ }
105
+ }
106
+ }