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
@@ -0,0 +1,175 @@
1
+ /**
2
+ * nodepyx — Thread Pool Header
3
+ * Manages a pool of worker threads for executing Python code
4
+ * without blocking the Node.js event loop.
5
+ *
6
+ * Design:
7
+ * - Worker threads acquire the GIL before running Python
8
+ * - Tasks are queued using a lock-free MPMC queue
9
+ * - Results are delivered back to Node.js via libuv async handles
10
+ * - Backpressure is applied when queue exceeds maxQueueSize
11
+ */
12
+
13
+ #pragma once
14
+
15
+ #include <napi.h>
16
+ #include <functional>
17
+ #include <memory>
18
+ #include <thread>
19
+ #include <mutex>
20
+ #include <condition_variable>
21
+ #include <deque>
22
+ #include <atomic>
23
+ #include <string>
24
+ #include <vector>
25
+ #include <chrono>
26
+
27
+ namespace nodepyx {
28
+
29
+ /**
30
+ * Task result — carries either a success value or an error.
31
+ */
32
+ struct TaskResult {
33
+ bool success = false;
34
+ std::string resultJson; // JSON-encoded result for JS
35
+ std::string errorType; // Python exception type
36
+ std::string errorMessage; // Python exception message
37
+ std::string errorTraceback; // Full Python traceback
38
+ uint32_t objectId = 0; // For Python object references
39
+ bool isObject = false; // Whether result is a Python object ref
40
+ };
41
+
42
+ /**
43
+ * A task to be executed in the Python thread pool.
44
+ */
45
+ struct PythonTask {
46
+ /** Unique task ID for tracking */
47
+ uint64_t id;
48
+ /** The actual Python work to do (runs with GIL held) */
49
+ std::function<TaskResult()> work;
50
+ /** Callback invoked in the event loop thread with the result */
51
+ std::function<void(TaskResult)> callback;
52
+ /** Timestamp when task was submitted (for timeout tracking) */
53
+ std::chrono::steady_clock::time_point submittedAt;
54
+ /** Timeout in milliseconds (0 = no timeout) */
55
+ uint32_t timeoutMs;
56
+ };
57
+
58
+ /**
59
+ * Pool statistics for monitoring.
60
+ */
61
+ struct PoolStats {
62
+ std::atomic<uint64_t> totalTasksSubmitted{0};
63
+ std::atomic<uint64_t> totalTasksCompleted{0};
64
+ std::atomic<uint64_t> totalTasksFailed{0};
65
+ std::atomic<uint64_t> totalTasksTimedOut{0};
66
+ std::atomic<uint64_t> currentQueueSize{0};
67
+ std::atomic<uint32_t> activeThreads{0};
68
+ std::atomic<uint64_t> totalExecutionTimeUs{0};
69
+ };
70
+
71
+ /**
72
+ * PythonThreadPool — manages N worker threads that execute Python tasks.
73
+ *
74
+ * Thread lifecycle:
75
+ * 1. Worker thread starts and calls PyEval_RestoreThread to get GIL state
76
+ * 2. Worker enters wait loop (releases GIL while waiting for work)
77
+ * 3. When task arrives, worker acquires GIL, executes task, releases GIL
78
+ * 4. Result is posted back to JS via libuv
79
+ */
80
+ class PythonThreadPool {
81
+ public:
82
+ explicit PythonThreadPool(uint32_t numThreads = 4, uint32_t maxQueueSize = 1000);
83
+ ~PythonThreadPool();
84
+
85
+ // Non-copyable, non-movable
86
+ PythonThreadPool(const PythonThreadPool&) = delete;
87
+ PythonThreadPool& operator=(const PythonThreadPool&) = delete;
88
+ PythonThreadPool(PythonThreadPool&&) = delete;
89
+ PythonThreadPool& operator=(PythonThreadPool&&) = delete;
90
+
91
+ /**
92
+ * Initialize the thread pool. Must be called from the main thread
93
+ * after Python is initialized (Py_Initialize() called).
94
+ */
95
+ void initialize(Napi::Env env);
96
+
97
+ /**
98
+ * Submit a task to the pool. Thread-safe.
99
+ *
100
+ * @param work - Function to run with GIL held in a worker thread
101
+ * @param callback - Function to call in event loop with result
102
+ * @param timeoutMs - Task timeout in milliseconds (0 = no timeout)
103
+ * @returns task ID, or 0 if queue is full
104
+ */
105
+ uint64_t submit(
106
+ std::function<TaskResult()> work,
107
+ std::function<void(TaskResult)> callback,
108
+ uint32_t timeoutMs = 0
109
+ );
110
+
111
+ /**
112
+ * Resize the thread pool. Blocks until all queued tasks complete.
113
+ */
114
+ void resize(uint32_t newNumThreads);
115
+
116
+ /**
117
+ * Graceful shutdown — waits for all queued tasks to complete.
118
+ */
119
+ void shutdown();
120
+
121
+ /**
122
+ * Force shutdown — cancels queued tasks, waits for active tasks.
123
+ */
124
+ void forceShutdown();
125
+
126
+ /** Get current pool statistics */
127
+ const PoolStats& getStats() const noexcept { return _stats; }
128
+
129
+ /** Get number of pending tasks in queue */
130
+ size_t getQueueSize() const;
131
+
132
+ /** Get number of active worker threads */
133
+ uint32_t getActiveThreadCount() const noexcept { return _stats.activeThreads.load(); }
134
+
135
+ /** Get total thread count */
136
+ uint32_t getThreadCount() const noexcept { return static_cast<uint32_t>(_threads.size()); }
137
+
138
+ /** Check if pool is running */
139
+ bool isRunning() const noexcept { return _running.load(); }
140
+
141
+ private:
142
+ /** Worker thread entry point */
143
+ void _workerThread(uint32_t threadId);
144
+
145
+ /** Post result back to event loop via libuv */
146
+ void _postResult(uint64_t taskId, TaskResult result, std::function<void(TaskResult)> callback);
147
+
148
+ /** Check and handle task timeout */
149
+ bool _isTimedOut(const PythonTask& task) const;
150
+
151
+ uint32_t _numThreads;
152
+ uint32_t _maxQueueSize;
153
+ std::atomic<bool> _running{false};
154
+ std::atomic<bool> _draining{false};
155
+ std::atomic<uint64_t> _nextTaskId{1};
156
+
157
+ // Task queue
158
+ std::deque<PythonTask> _queue;
159
+ mutable std::mutex _queueMutex;
160
+ std::condition_variable _queueCv;
161
+ std::condition_variable _drainCv;
162
+
163
+ // Worker threads
164
+ std::vector<std::thread> _threads;
165
+
166
+ // libuv async handle for posting results back to JS
167
+ // (Using Napi::ThreadSafeFunction for safe cross-thread callback)
168
+ Napi::ThreadSafeFunction _tsfn;
169
+
170
+ // Statistics
171
+ PoolStats _stats;
172
+ };
173
+
174
+ } // namespace nodepyx
175
+