nodepyx 1.0.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 (184) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +399 -0
  3. package/binding.gyp +73 -0
  4. package/dist/core/PyCallable.d.ts +65 -0
  5. package/dist/core/PyCallable.d.ts.map +1 -0
  6. package/dist/core/PyCallable.js +109 -0
  7. package/dist/core/PyCallable.js.map +1 -0
  8. package/dist/core/PyContext.d.ts +76 -0
  9. package/dist/core/PyContext.d.ts.map +1 -0
  10. package/dist/core/PyContext.js +228 -0
  11. package/dist/core/PyContext.js.map +1 -0
  12. package/dist/core/PyIterator.d.ts +84 -0
  13. package/dist/core/PyIterator.d.ts.map +1 -0
  14. package/dist/core/PyIterator.js +243 -0
  15. package/dist/core/PyIterator.js.map +1 -0
  16. package/dist/core/PyModule.d.ts +55 -0
  17. package/dist/core/PyModule.d.ts.map +1 -0
  18. package/dist/core/PyModule.js +172 -0
  19. package/dist/core/PyModule.js.map +1 -0
  20. package/dist/core/PyProxy.d.ts +65 -0
  21. package/dist/core/PyProxy.d.ts.map +1 -0
  22. package/dist/core/PyProxy.js +483 -0
  23. package/dist/core/PyProxy.js.map +1 -0
  24. package/dist/core/PyRuntime.d.ts +105 -0
  25. package/dist/core/PyRuntime.d.ts.map +1 -0
  26. package/dist/core/PyRuntime.js +438 -0
  27. package/dist/core/PyRuntime.js.map +1 -0
  28. package/dist/env/CondaManager.d.ts +118 -0
  29. package/dist/env/CondaManager.d.ts.map +1 -0
  30. package/dist/env/CondaManager.js +401 -0
  31. package/dist/env/CondaManager.js.map +1 -0
  32. package/dist/env/PackageInstaller.d.ts +233 -0
  33. package/dist/env/PackageInstaller.d.ts.map +1 -0
  34. package/dist/env/PackageInstaller.js +609 -0
  35. package/dist/env/PackageInstaller.js.map +1 -0
  36. package/dist/env/PythonDetector.d.ts +103 -0
  37. package/dist/env/PythonDetector.d.ts.map +1 -0
  38. package/dist/env/PythonDetector.js +381 -0
  39. package/dist/env/PythonDetector.js.map +1 -0
  40. package/dist/env/VenvManager.d.ts +117 -0
  41. package/dist/env/VenvManager.d.ts.map +1 -0
  42. package/dist/env/VenvManager.js +331 -0
  43. package/dist/env/VenvManager.js.map +1 -0
  44. package/dist/index.d.ts +169 -0
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.js +393 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/plugins/Plugin.interface.d.ts +41 -0
  49. package/dist/plugins/Plugin.interface.d.ts.map +1 -0
  50. package/dist/plugins/Plugin.interface.js +12 -0
  51. package/dist/plugins/Plugin.interface.js.map +1 -0
  52. package/dist/plugins/PluginManager.d.ts +26 -0
  53. package/dist/plugins/PluginManager.d.ts.map +1 -0
  54. package/dist/plugins/PluginManager.js +174 -0
  55. package/dist/plugins/PluginManager.js.map +1 -0
  56. package/dist/plugins/builtin/NumpyPlugin.d.ts +17 -0
  57. package/dist/plugins/builtin/NumpyPlugin.d.ts.map +1 -0
  58. package/dist/plugins/builtin/NumpyPlugin.js +41 -0
  59. package/dist/plugins/builtin/NumpyPlugin.js.map +1 -0
  60. package/dist/plugins/builtin/PandasPlugin.d.ts +19 -0
  61. package/dist/plugins/builtin/PandasPlugin.d.ts.map +1 -0
  62. package/dist/plugins/builtin/PandasPlugin.js +57 -0
  63. package/dist/plugins/builtin/PandasPlugin.js.map +1 -0
  64. package/dist/plugins/builtin/TorchPlugin.d.ts +23 -0
  65. package/dist/plugins/builtin/TorchPlugin.d.ts.map +1 -0
  66. package/dist/plugins/builtin/TorchPlugin.js +50 -0
  67. package/dist/plugins/builtin/TorchPlugin.js.map +1 -0
  68. package/dist/plugins/index.d.ts +7 -0
  69. package/dist/plugins/index.d.ts.map +1 -0
  70. package/dist/plugins/index.js +12 -0
  71. package/dist/plugins/index.js.map +1 -0
  72. package/dist/serialization/DataFrameBridge.d.ts +141 -0
  73. package/dist/serialization/DataFrameBridge.d.ts.map +1 -0
  74. package/dist/serialization/DataFrameBridge.js +355 -0
  75. package/dist/serialization/DataFrameBridge.js.map +1 -0
  76. package/dist/serialization/MsgPackSerializer.d.ts +45 -0
  77. package/dist/serialization/MsgPackSerializer.d.ts.map +1 -0
  78. package/dist/serialization/MsgPackSerializer.js +242 -0
  79. package/dist/serialization/MsgPackSerializer.js.map +1 -0
  80. package/dist/serialization/NumpyBridge.d.ts +96 -0
  81. package/dist/serialization/NumpyBridge.d.ts.map +1 -0
  82. package/dist/serialization/NumpyBridge.js +323 -0
  83. package/dist/serialization/NumpyBridge.js.map +1 -0
  84. package/dist/serialization/Serializer.d.ts +78 -0
  85. package/dist/serialization/Serializer.d.ts.map +1 -0
  86. package/dist/serialization/Serializer.js +281 -0
  87. package/dist/serialization/Serializer.js.map +1 -0
  88. package/dist/types/PythonTypeMapper.d.ts +87 -0
  89. package/dist/types/PythonTypeMapper.d.ts.map +1 -0
  90. package/dist/types/PythonTypeMapper.js +449 -0
  91. package/dist/types/PythonTypeMapper.js.map +1 -0
  92. package/dist/types/StubCache.d.ts +109 -0
  93. package/dist/types/StubCache.d.ts.map +1 -0
  94. package/dist/types/StubCache.js +333 -0
  95. package/dist/types/StubCache.js.map +1 -0
  96. package/dist/types/TypeGenerator.d.ts +139 -0
  97. package/dist/types/TypeGenerator.d.ts.map +1 -0
  98. package/dist/types/TypeGenerator.js +372 -0
  99. package/dist/types/TypeGenerator.js.map +1 -0
  100. package/dist/types/addon.d.ts +114 -0
  101. package/dist/types/addon.d.ts.map +1 -0
  102. package/dist/types/addon.js +32 -0
  103. package/dist/types/addon.js.map +1 -0
  104. package/dist/types/config.d.ts +175 -0
  105. package/dist/types/config.d.ts.map +1 -0
  106. package/dist/types/config.js +35 -0
  107. package/dist/types/config.js.map +1 -0
  108. package/dist/types/index.d.ts +10 -0
  109. package/dist/types/index.d.ts.map +1 -0
  110. package/dist/types/index.js +12 -0
  111. package/dist/types/index.js.map +1 -0
  112. package/dist/types/python.d.ts +235 -0
  113. package/dist/types/python.d.ts.map +1 -0
  114. package/dist/types/python.js +7 -0
  115. package/dist/types/python.js.map +1 -0
  116. package/dist/utils/ErrorTranslator.d.ts +83 -0
  117. package/dist/utils/ErrorTranslator.d.ts.map +1 -0
  118. package/dist/utils/ErrorTranslator.js +210 -0
  119. package/dist/utils/ErrorTranslator.js.map +1 -0
  120. package/dist/utils/Logger.d.ts +27 -0
  121. package/dist/utils/Logger.d.ts.map +1 -0
  122. package/dist/utils/Logger.js +115 -0
  123. package/dist/utils/Logger.js.map +1 -0
  124. package/dist/utils/MemoryMonitor.d.ts +44 -0
  125. package/dist/utils/MemoryMonitor.d.ts.map +1 -0
  126. package/dist/utils/MemoryMonitor.js +143 -0
  127. package/dist/utils/MemoryMonitor.js.map +1 -0
  128. package/package.json +177 -0
  129. package/python/error_handler.py +433 -0
  130. package/python/nodepyx_runtime.py +575 -0
  131. package/python/serializer.py +379 -0
  132. package/python/type_inspector.py +288 -0
  133. package/scripts/build-native.js +68 -0
  134. package/scripts/download-prebuilds.js +99 -0
  135. package/scripts/generate-stubs.js +405 -0
  136. package/scripts/install.js +260 -0
  137. package/src/core/PyCallable.ts +137 -0
  138. package/src/core/PyContext.ts +296 -0
  139. package/src/core/PyIterator.ts +294 -0
  140. package/src/core/PyModule.ts +194 -0
  141. package/src/core/PyProxy.ts +605 -0
  142. package/src/core/PyRuntime.ts +504 -0
  143. package/src/env/CondaManager.ts +451 -0
  144. package/src/env/PackageInstaller.ts +738 -0
  145. package/src/env/PythonDetector.ts +414 -0
  146. package/src/env/VenvManager.ts +396 -0
  147. package/src/index.ts +425 -0
  148. package/src/native/gil_guard.cpp +26 -0
  149. package/src/native/gil_guard.h +175 -0
  150. package/src/native/nodepyx_addon.cpp +886 -0
  151. package/src/native/python_bridge.cpp +790 -0
  152. package/src/native/python_bridge.h +257 -0
  153. package/src/native/thread_pool.cpp +336 -0
  154. package/src/native/thread_pool.h +175 -0
  155. package/src/native/type_converter.cpp +901 -0
  156. package/src/native/type_converter.h +272 -0
  157. package/src/nextjs/PyProvider.tsx +123 -0
  158. package/src/nextjs/index.ts +21 -0
  159. package/src/nextjs/usePython.ts +106 -0
  160. package/src/nextjs/withnodepyx.ts +88 -0
  161. package/src/plugins/Plugin.interface.ts +51 -0
  162. package/src/plugins/PluginManager.ts +155 -0
  163. package/src/plugins/builtin/NumpyPlugin.ts +36 -0
  164. package/src/plugins/builtin/PandasPlugin.ts +49 -0
  165. package/src/plugins/builtin/TorchPlugin.ts +56 -0
  166. package/src/plugins/index.ts +7 -0
  167. package/src/serialization/DataFrameBridge.ts +398 -0
  168. package/src/serialization/MsgPackSerializer.ts +220 -0
  169. package/src/serialization/NumpyBridge.ts +332 -0
  170. package/src/serialization/Serializer.ts +320 -0
  171. package/src/types/PythonTypeMapper.ts +495 -0
  172. package/src/types/StubCache.ts +340 -0
  173. package/src/types/TypeGenerator.ts +491 -0
  174. package/src/types/addon.ts +170 -0
  175. package/src/types/config.ts +226 -0
  176. package/src/types/index.ts +55 -0
  177. package/src/types/python.ts +309 -0
  178. package/src/types/stubs/numpy.d.ts +441 -0
  179. package/src/types/stubs/pandas.d.ts +575 -0
  180. package/src/types/stubs/sklearn.d.ts +728 -0
  181. package/src/types/stubs/torch.d.ts +694 -0
  182. package/src/utils/ErrorTranslator.ts +220 -0
  183. package/src/utils/Logger.ts +119 -0
  184. package/src/utils/MemoryMonitor.ts +175 -0
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 nodepyx contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,399 @@
1
+ # nodepyx
2
+
3
+ > **Run Python libraries from Node.js as if they were native** — embed CPython in-process with full TypeScript types, Proxy-based API, async/await support, and Next.js integration.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/nodepyx.svg)](https://www.npmjs.com/package/nodepyx)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+ [![Node.js](https://img.shields.io/badge/Node.js-≥18-green.svg)](https://nodejs.org)
8
+ [![Python](https://img.shields.io/badge/Python-3.8%2B-blue.svg)](https://python.org)
9
+ ---
10
+
11
+
12
+ <img src="assets/images/nodepyx.jpg" width="1024" height="636">
13
+
14
+
15
+
16
+ ## Why nodepyx?
17
+
18
+ Traditional Python↔Node.js bridges spawn a **separate Python process** and communicate via stdin/stdout or HTTP — introducing serialization overhead, process startup latency, and complex lifecycle management.
19
+
20
+ **nodepyx embeds CPython directly inside the Node.js process** via a native N-API addon. Every Python call happens in-process: no IPC, no child processes, no HTTP servers.
21
+
22
+ | Feature | nodepyx | python-shell / pythonia | gRPC/HTTP |
23
+ |---------|--------|------------------------|-----------|
24
+ | In-process (zero IPC) | ✅ | ❌ | ❌ |
25
+ | TypeScript types | ✅ Full .d.ts | Partial | Manual |
26
+ | `await` on Python objects | ✅ Proxy thenable | ❌ | ❌ |
27
+ | NumPy zero-copy | ✅ SharedArrayBuffer | ❌ | ❌ |
28
+ | DataFrame native | ✅ DataFrameResult | ❌ | ❌ |
29
+ | Next.js integration | ✅ withnodepyx() | ❌ | ❌ |
30
+ | Auto venv/conda | ✅ | Partial | ❌ |
31
+
32
+ ---
33
+
34
+ ## Quick Start
35
+
36
+ ```bash
37
+ npm install nodepyx
38
+ ```
39
+
40
+ ```typescript
41
+ import { init, py } from 'nodepyx';
42
+
43
+ await init({
44
+ virtualenv: {
45
+ path: './.venv',
46
+ packages: ['pandas', 'numpy', 'scikit-learn'],
47
+ autoInstall: true,
48
+ },
49
+ });
50
+
51
+ // Import Python modules — works exactly like Python `import`
52
+ const pd = await py.import('pandas');
53
+ const np = await py.import('numpy');
54
+
55
+ // Call functions with await
56
+ const df = await pd.read_csv('data.csv');
57
+ const arr = await np.arange(100); // → Float64Array
58
+
59
+ // Chain attribute access
60
+ const mean = await df.describe().loc['mean'];
61
+
62
+ // Iterate Python generators
63
+ for await (const row of df.iterrows()) {
64
+ console.log(row);
65
+ }
66
+ ```
67
+
68
+ ---
69
+
70
+ ## Installation
71
+
72
+ ### Prerequisites
73
+
74
+ - **Node.js** ≥ 18.0.0
75
+ - **Python** 3.8 – 3.12 (auto-detected; or set `nodepyx_PYTHON`)
76
+ - **C++ build tools** (for native addon)
77
+ - Linux: `gcc`, `g++`, `make`, `python3`
78
+ - macOS: Xcode Command Line Tools (`xcode-select --install`)
79
+ - Windows: Visual Studio Build Tools + Python
80
+
81
+ ```bash
82
+ # Install from npm
83
+ npm install nodepyx
84
+
85
+ # Or with pre-built binary (skips compilation)
86
+ nodepyx_SKIP_BUILD=1 npm install nodepyx && npm run download-prebuilds
87
+ ```
88
+
89
+ ---
90
+
91
+ ## Configuration
92
+
93
+ ```typescript
94
+ import { init } from 'nodepyx';
95
+
96
+ await init({
97
+ // Python executable (default: auto-detect)
98
+ pythonExecutable: '/usr/bin/python3.11',
99
+
100
+ // Virtualenv (recommended)
101
+ virtualenv: {
102
+ path: './.venv',
103
+ packages: ['pandas>=2.0', 'numpy>=1.24', 'scikit-learn'],
104
+ autoInstall: true, // install missing packages automatically
105
+ autoCreate: true, // create venv if it does not exist
106
+ },
107
+
108
+ // OR use Conda
109
+ conda: {
110
+ envName: 'my-env',
111
+ packages: ['pytorch', 'torchvision'],
112
+ },
113
+
114
+ // Thread pool for GIL management
115
+ threadPoolSize: 4, // default: 4
116
+
117
+ // Logging
118
+ logLevel: 'info', // 'debug' | 'info' | 'warn' | 'error'
119
+
120
+ // Memory limits
121
+ memory: {
122
+ heapLimitMB: 2048,
123
+ gcThresholdMB: 512,
124
+ },
125
+ });
126
+ ```
127
+
128
+ ---
129
+
130
+ ## API Reference
131
+
132
+ ### Lifecycle
133
+
134
+ ```typescript
135
+ import { init, shutdown, isInitialized } from 'nodepyx';
136
+
137
+ await init(config?); // Initialize Python runtime
138
+ await shutdown(); // Release all Python resources
139
+ isInitialized(); // → boolean
140
+ ```
141
+
142
+ ### `py` — Main Proxy Object
143
+
144
+ ```typescript
145
+ import { py } from 'nodepyx';
146
+
147
+ // Import a module
148
+ const np = await py.import('numpy');
149
+
150
+ // Evaluate a Python expression
151
+ const result = await py.eval('1 + 2 + 3'); // → 6
152
+
153
+ // Execute statements
154
+ await py.exec(`
155
+ import sys
156
+ print(sys.version)
157
+ `);
158
+
159
+ // Run a .py file
160
+ await py.runFile('./my_script.py');
161
+
162
+ // Install packages at runtime
163
+ await py.installPackages(['requests', 'httpx']);
164
+ ```
165
+
166
+ ### Proxy API
167
+
168
+ Every Python object returned by nodepyx is a **JavaScript Proxy** with full `await` support:
169
+
170
+ ```typescript
171
+ const pd = await py.import('pandas');
172
+
173
+ // Attribute access
174
+ const version = await pd.__version__; // string
175
+
176
+ // Method calls
177
+ const df = await pd.DataFrame({ a: [1,2,3], b: [4,5,6] });
178
+ const shape = await df.shape; // [3, 2]
179
+ const desc = await df.describe(); // DataFrameResult
180
+
181
+ // Chained calls
182
+ const top5 = await df.sort_values('a', { ascending: false }).head(5);
183
+
184
+ // Indexing (df['column'])
185
+ const col = await df['a']; // SeriesResult
186
+ ```
187
+
188
+ ### NumPy Arrays
189
+
190
+ ```typescript
191
+ const np = await py.import('numpy');
192
+ const arr = await np.linspace(0, 1, 100); // NumPyArrayResult
193
+
194
+ // NumPyArrayResult interface:
195
+ arr.data // Float64Array (or Int32Array, Uint8Array, etc.)
196
+ arr.shape // number[] — e.g. [100] or [3, 4]
197
+ arr.dtype // 'float64' | 'float32' | 'int32' | ...
198
+ arr.ndim // number
199
+ arr.size // number — total element count
200
+
201
+ // Send TypedArrays back to Python
202
+ const NumpyBridge = new NumpyBridge();
203
+ const wire = NumpyBridge.serializeTypedArray(new Float64Array([1,2,3]), { dtype:'float64', itemsize:8 }, [3]);
204
+ ```
205
+
206
+ ### Pandas DataFrames
207
+
208
+ ```typescript
209
+ const pd = await py.import('pandas');
210
+ const df = await pd.read_csv('data.csv'); // DataFrameResult
211
+
212
+ // DataFrameResult interface:
213
+ df.columns // string[]
214
+ df.records // Record<string, unknown>[] — one object per row
215
+ df.index // unknown[]
216
+ df.dtypes // Record<string, string>
217
+ df.shape // [rows, cols]
218
+
219
+ // Static helpers
220
+ import { DataFrameBridge } from 'nodepyx';
221
+ const cols = DataFrameBridge.toColumnArrays(df); // column-keyed arrays
222
+ const filtered = DataFrameBridge.filterRows(df, row => row.score > 0.9);
223
+ const stats = DataFrameBridge.describeColumn(df, 'price'); // {count,mean,std,min,max}
224
+ ```
225
+
226
+ ### Error Handling
227
+
228
+ ```typescript
229
+ import { PythonError, isPythonError, isPythonErrorOfType } from 'nodepyx';
230
+
231
+ try {
232
+ await py.eval('1 / 0');
233
+ } catch (err) {
234
+ if (isPythonError(err)) {
235
+ console.log(err.pythonType); // 'ZeroDivisionError'
236
+ console.log(err.message); // '[Python ZeroDivisionError] division by zero'
237
+ console.log(err.traceback); // Full Python traceback
238
+ }
239
+ }
240
+
241
+ // Type-specific check
242
+ if (isPythonErrorOfType(err, 'ImportError')) { ... }
243
+ ```
244
+
245
+ ---
246
+
247
+ ## Environment Management
248
+
249
+ ### Virtualenv (recommended)
250
+
251
+ ```typescript
252
+ import { VenvManager } from 'nodepyx';
253
+
254
+ const mgr = new VenvManager({
255
+ venvPath: './.venv',
256
+ packages: ['pandas', 'numpy', 'scikit-learn'],
257
+ autoCreate: true,
258
+ });
259
+
260
+ const result = await mgr.setup();
261
+ console.log('Installed:', result.installedPackages);
262
+ console.log('Venv python:', result.pythonExecutable);
263
+ ```
264
+
265
+ ### Conda
266
+
267
+ ```typescript
268
+ import { CondaManager } from 'nodepyx';
269
+
270
+ const conda = new CondaManager({ envName: 'ml-env', packages: ['pytorch', 'pip::some-pip-package'] });
271
+ const result = await conda.setup();
272
+ ```
273
+
274
+ ### PackageInstaller
275
+
276
+ ```typescript
277
+ import { PackageInstaller } from 'nodepyx';
278
+
279
+ const pip = new PackageInstaller({ pythonExecutable: '/usr/bin/python3' });
280
+
281
+ // Install with progress events
282
+ pip.on('progress', (ev) => console.log(ev.package, ev.status));
283
+
284
+ const result = await pip.install(['pandas>=2.0', 'numpy>=1.24']);
285
+ console.log('installed:', result.installed);
286
+ console.log('already present:', result.alreadyInstalled);
287
+ ```
288
+
289
+ ---
290
+
291
+ ## TypeScript Stubs
292
+
293
+ ```bash
294
+ # Generate .d.ts stubs for Python modules
295
+ npx nodepyx-stubs pandas numpy sklearn torch
296
+
297
+ # Regenerate all cached stubs
298
+ npx nodepyx-stubs --all
299
+
300
+ # List cached stubs
301
+ npx nodepyx-stubs --list
302
+ ```
303
+
304
+ Then in your TypeScript code:
305
+ ```typescript
306
+ import type { DataFrame } from 'nodepyx/pandas'; // full IDE autocomplete
307
+ import type { ndarray } from 'nodepyx/numpy';
308
+ ```
309
+
310
+ ---
311
+
312
+ ## Next.js Integration
313
+
314
+ ```js
315
+ // next.config.js
316
+ const { withnodepyx } = require('nodepyx/next');
317
+
318
+ module.exports = withnodepyx({
319
+ // your Next.js config
320
+ }, {
321
+ virtualenv: { path: './.venv', packages: ['pandas'], autoInstall: true },
322
+ });
323
+ ```
324
+
325
+ ```typescript
326
+ // app/api/python/route.ts (Server Route — Node.js runtime only)
327
+ import { NextResponse } from 'next/server';
328
+ import { init, evalPython } from 'nodepyx';
329
+
330
+ let ready = false;
331
+ async function ensureReady() {
332
+ if (!ready) { await init(); ready = true; }
333
+ }
334
+
335
+ export async function GET() {
336
+ await ensureReady();
337
+ const result = await evalPython('list(range(10))');
338
+ return NextResponse.json(result);
339
+ }
340
+ ```
341
+
342
+ ```tsx
343
+ // Client component
344
+ 'use client';
345
+ import { usePython } from 'nodepyx/next';
346
+
347
+ export function Chart() {
348
+ const { data, loading, run } = usePython(async (py) => {
349
+ const np = await py.import('numpy');
350
+ return (np as any).random.randn(100).tolist();
351
+ });
352
+
353
+ return <button onClick={run}>{loading ? '…' : 'Generate'}</button>;
354
+ }
355
+ ```
356
+
357
+ ---
358
+
359
+ ## Plugins
360
+
361
+ ```typescript
362
+ import { init } from 'nodepyx';
363
+ import type { nodepyxPlugin } from 'nodepyx';
364
+
365
+ const myPlugin: nodepyxPlugin = {
366
+ name: 'my-plugin',
367
+ version: '1.0.0',
368
+ supportedModules: ['my_lib'],
369
+ handledTypes: ['MySpecialType'],
370
+
371
+ async onInit(ctx) {
372
+ ctx.registerTypeHandler('MySpecialType', (raw, typeHint) => {
373
+ // Transform raw wire dict into a JS value
374
+ return { specialData: JSON.parse(raw.data as string) };
375
+ });
376
+ },
377
+ };
378
+
379
+ await init({ plugins: [myPlugin] });
380
+ ```
381
+
382
+ ---
383
+
384
+ ## Supported Python Versions
385
+
386
+ | Python | Status |
387
+ |--------|--------|
388
+ | 3.12 | ✅ Recommended |
389
+ | 3.11 | ✅ Supported |
390
+ | 3.10 | ✅ Supported |
391
+ | 3.9 | ✅ Supported |
392
+ | 3.8 | ✅ Supported (EOL) |
393
+ | 3.7 | ❌ Not supported |
394
+
395
+ ---
396
+
397
+ ## License
398
+
399
+ MIT © nodepyx contributors
package/binding.gyp ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "targets": [{
3
+ "target_name": "nodepyx_addon",
4
+ "sources": [
5
+ "src/native/nodepyx_addon.cpp",
6
+ "src/native/python_bridge.cpp",
7
+ "src/native/type_converter.cpp",
8
+ "src/native/thread_pool.cpp",
9
+ "src/native/gil_guard.cpp"
10
+ ],
11
+ "include_dirs": [
12
+ "<!@(node -p \"require('node-addon-api').include\")"
13
+ ],
14
+ "cflags_cc": [
15
+ "-std=c++17",
16
+ "-O2",
17
+ "-Wall",
18
+ "-Wextra",
19
+ "-Wno-unused-parameter"
20
+ ],
21
+ "defines": [
22
+ "NAPI_DISABLE_CPP_EXCEPTIONS",
23
+ "NAPI_VERSION=8",
24
+ "PY_SSIZE_T_CLEAN"
25
+ ],
26
+ "conditions": [
27
+ ["OS=='linux'", {
28
+ "include_dirs+": [
29
+ "<!@(python3-config --includes 2>/dev/null | sed 's/-I//g' | tr ' ' '\\n' | head -1)"
30
+ ],
31
+ "libraries": [
32
+ "<!@(python3-config --ldflags 2>/dev/null)",
33
+ "<!@(python3-config --libs 2>/dev/null)"
34
+ ],
35
+ "cflags_cc": [
36
+ "-fno-exceptions",
37
+ "-fvisibility=hidden"
38
+ ]
39
+ }],
40
+ ["OS=='mac'", {
41
+ "include_dirs+": [
42
+ "<!@(python3-config --includes 2>/dev/null | sed 's/-I//g' | tr ' ' '\\n' | head -1)"
43
+ ],
44
+ "libraries": [
45
+ "<!@(python3-config --ldflags 2>/dev/null)"
46
+ ],
47
+ "xcode_settings": {
48
+ "MACOSX_DEPLOYMENT_TARGET": "11.0",
49
+ "OTHER_CFLAGS": ["-std=c++17", "-stdlib=libc++"],
50
+ "GCC_ENABLE_CPP_EXCEPTIONS": "NO",
51
+ "OTHER_LDFLAGS": [
52
+ "<!@(python3-config --libs 2>/dev/null)"
53
+ ]
54
+ }
55
+ }],
56
+ ["OS=='win'", {
57
+ "include_dirs+": [
58
+ "<!@(python -c \"import sysconfig; print(sysconfig.get_path('include'))\" 2>nul)"
59
+ ],
60
+ "libraries": [
61
+ "<!@(python -c \"import sysconfig; import os; print(os.path.join(sysconfig.get_config_var('LIBDIR') or '', sysconfig.get_config_var('LDLIBRARY') or 'python3.lib'))\" 2>nul)"
62
+ ],
63
+ "msvs_settings": {
64
+ "VCCLCompilerTool": {
65
+ "ExceptionHandling": 0,
66
+ "AdditionalOptions": ["/std:c++17"]
67
+ }
68
+ }
69
+ }]
70
+ ]
71
+ }]
72
+ }
73
+
@@ -0,0 +1,65 @@
1
+ /**
2
+ * nodepyx — PyCallable
3
+ * Represents a Python callable (function, method, class constructor).
4
+ * Extends PyProxy with call metadata and convenience methods.
5
+ */
6
+ import { PyProxy } from './PyProxy';
7
+ import type { NativeAddon } from '../types/addon';
8
+ import type { PyObjectId, AttributePath, PyCallableInfo } from '../types/python';
9
+ /**
10
+ * PyCallable — wraps a Python function or method.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * const np = await py.import('numpy');
15
+ * const zeros = await np.zeros; // PyCallable
16
+ * const arr = await zeros([3, 3], { dtype: 'float64' });
17
+ *
18
+ * // Or more concisely:
19
+ * const arr2 = await np.zeros([3, 3]);
20
+ * ```
21
+ */
22
+ export declare class PyCallable extends PyProxy {
23
+ private readonly _callableName;
24
+ private _info;
25
+ constructor(objectId: PyObjectId, path: AttributePath, callableName: string, addon: NativeAddon, callTimeout?: number);
26
+ /** Callable name */
27
+ get callableName(): string;
28
+ /**
29
+ * Call the callable with given arguments.
30
+ * Same as using the callable directly (proxy syntax).
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * const result = await callable.invoke([1, 2, 3], { axis: 0 });
35
+ * // equivalent to: await callable([1, 2, 3], { axis: 0 })
36
+ * ```
37
+ */
38
+ invoke(...args: unknown[]): Promise<unknown>;
39
+ /**
40
+ * Call with explicit positional args and keyword args.
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * const result = await callable.call([1, 2, 3], { axis: 0, keepdims: true });
45
+ * ```
46
+ */
47
+ call(args: unknown[], kwargs?: Record<string, unknown>): Promise<unknown>;
48
+ /**
49
+ * Partially apply arguments (like functools.partial in Python).
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * const readCsvUtf8 = await pd.read_csv.partial({ encoding: 'utf-8' });
54
+ * const df = await readCsvUtf8('data.csv');
55
+ * ```
56
+ */
57
+ partial(...partialArgs: unknown[]): PyCallable;
58
+ /**
59
+ * Get introspection information about this callable.
60
+ */
61
+ getInfo(): Promise<PyCallableInfo | null>;
62
+ static fromProxy(proxy: PyProxy, name: string): PyCallable;
63
+ toString(): string;
64
+ }
65
+ //# sourceMappingURL=PyCallable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PyCallable.d.ts","sourceRoot":"","sources":["../../src/core/PyCallable.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAoB,MAAM,WAAW,CAAC;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjF;;;;;;;;;;;;GAYG;AACH,qBAAa,UAAW,SAAQ,OAAO;IACrC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,KAAK,CAA+B;gBAG1C,QAAQ,EAAE,UAAU,EACpB,IAAI,EAAE,aAAa,EACnB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,WAAW,EAClB,WAAW,CAAC,EAAE,MAAM;IAatB,oBAAoB;IACpB,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED;;;;;;;;;OASG;IACG,MAAM,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAIlD;;;;;;;OAOG;IACG,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAO/E;;;;;;;;OAQG;IACH,OAAO,CAAC,GAAG,WAAW,EAAE,OAAO,EAAE,GAAG,UAAU;IAuB9C;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAO/C,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU;IAWjD,QAAQ,IAAI,MAAM;CAG5B"}
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ /**
3
+ * nodepyx — PyCallable
4
+ * Represents a Python callable (function, method, class constructor).
5
+ * Extends PyProxy with call metadata and convenience methods.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.PyCallable = void 0;
9
+ const PyProxy_1 = require("./PyProxy");
10
+ /**
11
+ * PyCallable — wraps a Python function or method.
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * const np = await py.import('numpy');
16
+ * const zeros = await np.zeros; // PyCallable
17
+ * const arr = await zeros([3, 3], { dtype: 'float64' });
18
+ *
19
+ * // Or more concisely:
20
+ * const arr2 = await np.zeros([3, 3]);
21
+ * ```
22
+ */
23
+ class PyCallable extends PyProxy_1.PyProxy {
24
+ _callableName;
25
+ _info = null;
26
+ constructor(objectId, path, callableName, addon, callTimeout) {
27
+ super({
28
+ objectId,
29
+ path,
30
+ addon,
31
+ isResolved: false,
32
+ callTimeout,
33
+ childCache: new Map(),
34
+ });
35
+ this._callableName = callableName;
36
+ }
37
+ /** Callable name */
38
+ get callableName() {
39
+ return this._callableName;
40
+ }
41
+ /**
42
+ * Call the callable with given arguments.
43
+ * Same as using the callable directly (proxy syntax).
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * const result = await callable.invoke([1, 2, 3], { axis: 0 });
48
+ * // equivalent to: await callable([1, 2, 3], { axis: 0 })
49
+ * ```
50
+ */
51
+ async invoke(...args) {
52
+ return this(...args);
53
+ }
54
+ /**
55
+ * Call with explicit positional args and keyword args.
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * const result = await callable.call([1, 2, 3], { axis: 0, keepdims: true });
60
+ * ```
61
+ */
62
+ async call(args, kwargs) {
63
+ if (kwargs) {
64
+ return this(...args, kwargs);
65
+ }
66
+ return this(...args);
67
+ }
68
+ /**
69
+ * Partially apply arguments (like functools.partial in Python).
70
+ *
71
+ * @example
72
+ * ```typescript
73
+ * const readCsvUtf8 = await pd.read_csv.partial({ encoding: 'utf-8' });
74
+ * const df = await readCsvUtf8('data.csv');
75
+ * ```
76
+ */
77
+ partial(...partialArgs) {
78
+ const internal = this[PyProxy_1.PYPROXY_INTERNAL];
79
+ const callableName = `${this._callableName}(partial)`;
80
+ const wrapper = new PyCallable(internal.objectId, internal.path, callableName, internal.addon, internal.callTimeout);
81
+ // Override: always prepend partialArgs (use arrow to capture outer `this` lexically)
82
+ const callSelf = (...a) => this(...a);
83
+ const overrideProxy = new Proxy(wrapper, {
84
+ apply(_target, _thisArg, args) {
85
+ return callSelf(...partialArgs, ...args);
86
+ },
87
+ });
88
+ return overrideProxy;
89
+ }
90
+ /**
91
+ * Get introspection information about this callable.
92
+ */
93
+ async getInfo() {
94
+ if (this._info) {
95
+ return this._info;
96
+ }
97
+ // Not yet implemented — would require Python introspection
98
+ return null;
99
+ }
100
+ static fromProxy(proxy, name) {
101
+ const internal = proxy[PyProxy_1.PYPROXY_INTERNAL];
102
+ return new PyCallable(internal.objectId, internal.path, name, internal.addon, internal.callTimeout);
103
+ }
104
+ toString() {
105
+ return `[PyCallable '${this._callableName}' id=${this[PyProxy_1.PYPROXY_INTERNAL].objectId}]`;
106
+ }
107
+ }
108
+ exports.PyCallable = PyCallable;
109
+ //# sourceMappingURL=PyCallable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PyCallable.js","sourceRoot":"","sources":["../../src/core/PyCallable.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAEH,uCAAsD;AAItD;;;;;;;;;;;;GAYG;AACH,MAAa,UAAW,SAAQ,iBAAO;IACpB,aAAa,CAAS;IAC/B,KAAK,GAA0B,IAAI,CAAC;IAE5C,YACE,QAAoB,EACpB,IAAmB,EACnB,YAAoB,EACpB,KAAkB,EAClB,WAAoB;QAEpB,KAAK,CAAC;YACJ,QAAQ;YACR,IAAI;YACJ,KAAK;YACL,UAAU,EAAE,KAAK;YACjB,WAAW;YACX,UAAU,EAAE,IAAI,GAAG,EAAE;SACtB,CAAC,CAAC;QACH,IAAI,CAAC,aAAa,GAAG,YAAY,CAAC;IACpC,CAAC;IAED,oBAAoB;IACpB,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,MAAM,CAAC,GAAG,IAAe;QAC7B,OAAQ,IAAgD,CAAC,GAAG,IAAI,CAAC,CAAC;IACpE,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,IAAI,CAAC,IAAe,EAAE,MAAgC;QAC1D,IAAI,MAAM,EAAE,CAAC;YACX,OAAQ,IAAgD,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;QAC5E,CAAC;QACD,OAAQ,IAAgD,CAAC,GAAG,IAAI,CAAC,CAAC;IACpE,CAAC;IAED;;;;;;;;OAQG;IACH,OAAO,CAAC,GAAG,WAAsB;QAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,0BAAgB,CAAC,CAAC;QACxC,MAAM,YAAY,GAAG,GAAG,IAAI,CAAC,aAAa,WAAW,CAAC;QACtD,MAAM,OAAO,GAAG,IAAI,UAAU,CAC5B,QAAQ,CAAC,QAAQ,EACjB,QAAQ,CAAC,IAAI,EACb,YAAY,EACZ,QAAQ,CAAC,KAAK,EACd,QAAQ,CAAC,WAAW,CACrB,CAAC;QAEF,qFAAqF;QACrF,MAAM,QAAQ,GAAG,CAAC,GAAG,CAAY,EAAE,EAAE,CAClC,IAAgD,CAAC,GAAG,CAAC,CAAC,CAAC;QAC1D,MAAM,aAAa,GAAG,IAAI,KAAK,CAAC,OAAO,EAAE;YACvC,KAAK,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAe;gBACtC,OAAO,QAAQ,CAAC,GAAG,WAAW,EAAE,GAAG,IAAI,CAAC,CAAC;YAC3C,CAAC;SACF,CAAC,CAAC;QAEH,OAAO,aAA2B,CAAC;IACrC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAO;QACX,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAAA,OAAO,IAAI,CAAC,KAAK,CAAC;QAAA,CAAC;QAEpC,6DAA6D;QAC7D,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,CAAC,SAAS,CAAC,KAAc,EAAE,IAAY;QAC3C,MAAM,QAAQ,GAAG,KAAK,CAAC,0BAAgB,CAAC,CAAC;QACzC,OAAO,IAAI,UAAU,CACnB,QAAQ,CAAC,QAAQ,EACjB,QAAQ,CAAC,IAAI,EACb,IAAI,EACJ,QAAQ,CAAC,KAAK,EACd,QAAQ,CAAC,WAAW,CACrB,CAAC;IACJ,CAAC;IAEQ,QAAQ;QACf,OAAO,gBAAgB,IAAI,CAAC,aAAa,QAAQ,IAAI,CAAC,0BAAgB,CAAC,CAAC,QAAQ,GAAG,CAAC;IACtF,CAAC;CACF;AAhHD,gCAgHC"}