pynect 0.0.0 → 0.2.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.
Files changed (191) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +503 -2
  3. package/SECURITY.md +34 -0
  4. package/binding.gyp +67 -0
  5. package/dist/cli/codegen.d.ts +3 -0
  6. package/dist/cli/codegen.d.ts.map +1 -0
  7. package/dist/cli/codegen.js +543 -0
  8. package/dist/cli/codegen.js.map +1 -0
  9. package/dist/cli/sync.d.ts +3 -0
  10. package/dist/cli/sync.d.ts.map +1 -0
  11. package/dist/cli/sync.js +47 -0
  12. package/dist/cli/sync.js.map +1 -0
  13. package/dist/config/project.d.ts +113 -0
  14. package/dist/config/project.d.ts.map +1 -0
  15. package/dist/config/project.js +344 -0
  16. package/dist/config/project.js.map +1 -0
  17. package/dist/core/isolated-worker.d.ts +39 -0
  18. package/dist/core/isolated-worker.d.ts.map +1 -0
  19. package/dist/core/isolated-worker.js +178 -0
  20. package/dist/core/isolated-worker.js.map +1 -0
  21. package/dist/core/py-callable.d.ts +67 -0
  22. package/dist/core/py-callable.d.ts.map +1 -0
  23. package/dist/core/py-callable.js +104 -0
  24. package/dist/core/py-callable.js.map +1 -0
  25. package/dist/core/py-context.d.ts +76 -0
  26. package/dist/core/py-context.d.ts.map +1 -0
  27. package/dist/core/py-context.js +239 -0
  28. package/dist/core/py-context.js.map +1 -0
  29. package/dist/core/py-iterator.d.ts +84 -0
  30. package/dist/core/py-iterator.d.ts.map +1 -0
  31. package/dist/core/py-iterator.js +243 -0
  32. package/dist/core/py-iterator.js.map +1 -0
  33. package/dist/core/py-module.d.ts +53 -0
  34. package/dist/core/py-module.d.ts.map +1 -0
  35. package/dist/core/py-module.js +162 -0
  36. package/dist/core/py-module.js.map +1 -0
  37. package/dist/core/py-proxy.d.ts +75 -0
  38. package/dist/core/py-proxy.d.ts.map +1 -0
  39. package/dist/core/py-proxy.js +563 -0
  40. package/dist/core/py-proxy.js.map +1 -0
  41. package/dist/core/py-runtime.d.ts +113 -0
  42. package/dist/core/py-runtime.d.ts.map +1 -0
  43. package/dist/core/py-runtime.js +528 -0
  44. package/dist/core/py-runtime.js.map +1 -0
  45. package/dist/env/executables.d.ts +3 -0
  46. package/dist/env/executables.d.ts.map +1 -0
  47. package/dist/env/executables.js +14 -0
  48. package/dist/env/executables.js.map +1 -0
  49. package/dist/env/package-installer.d.ts +232 -0
  50. package/dist/env/package-installer.d.ts.map +1 -0
  51. package/dist/env/package-installer.js +615 -0
  52. package/dist/env/package-installer.js.map +1 -0
  53. package/dist/env/project-environment.d.ts +64 -0
  54. package/dist/env/project-environment.d.ts.map +1 -0
  55. package/dist/env/project-environment.js +388 -0
  56. package/dist/env/project-environment.js.map +1 -0
  57. package/dist/env/pyfig-adapter.d.ts +38 -0
  58. package/dist/env/pyfig-adapter.d.ts.map +1 -0
  59. package/dist/env/pyfig-adapter.js +10 -0
  60. package/dist/env/pyfig-adapter.js.map +1 -0
  61. package/dist/env/python-detector.d.ts +48 -0
  62. package/dist/env/python-detector.d.ts.map +1 -0
  63. package/dist/env/python-detector.js +182 -0
  64. package/dist/env/python-detector.js.map +1 -0
  65. package/dist/env/python-provisioner.d.ts +16 -0
  66. package/dist/env/python-provisioner.d.ts.map +1 -0
  67. package/dist/env/python-provisioner.js +42 -0
  68. package/dist/env/python-provisioner.js.map +1 -0
  69. package/dist/env/venv-manager.d.ts +56 -0
  70. package/dist/env/venv-manager.d.ts.map +1 -0
  71. package/dist/env/venv-manager.js +160 -0
  72. package/dist/env/venv-manager.js.map +1 -0
  73. package/dist/index.d.ts +182 -0
  74. package/dist/index.d.ts.map +1 -0
  75. package/dist/index.js +394 -0
  76. package/dist/index.js.map +1 -0
  77. package/dist/native/native-addon-loader.d.ts +13 -0
  78. package/dist/native/native-addon-loader.d.ts.map +1 -0
  79. package/dist/native/native-addon-loader.js +171 -0
  80. package/dist/native/native-addon-loader.js.map +1 -0
  81. package/dist/plugins/builtin/numpy-plugin.d.ts +17 -0
  82. package/dist/plugins/builtin/numpy-plugin.d.ts.map +1 -0
  83. package/dist/plugins/builtin/numpy-plugin.js +41 -0
  84. package/dist/plugins/builtin/numpy-plugin.js.map +1 -0
  85. package/dist/plugins/builtin/pandas-plugin.d.ts +19 -0
  86. package/dist/plugins/builtin/pandas-plugin.d.ts.map +1 -0
  87. package/dist/plugins/builtin/pandas-plugin.js +57 -0
  88. package/dist/plugins/builtin/pandas-plugin.js.map +1 -0
  89. package/dist/plugins/builtin/torch-plugin.d.ts +23 -0
  90. package/dist/plugins/builtin/torch-plugin.d.ts.map +1 -0
  91. package/dist/plugins/builtin/torch-plugin.js +50 -0
  92. package/dist/plugins/builtin/torch-plugin.js.map +1 -0
  93. package/dist/plugins/index.d.ts +7 -0
  94. package/dist/plugins/index.d.ts.map +1 -0
  95. package/dist/plugins/index.js +12 -0
  96. package/dist/plugins/index.js.map +1 -0
  97. package/dist/plugins/plugin-manager.d.ts +26 -0
  98. package/dist/plugins/plugin-manager.d.ts.map +1 -0
  99. package/dist/plugins/plugin-manager.js +174 -0
  100. package/dist/plugins/plugin-manager.js.map +1 -0
  101. package/dist/plugins/plugin.interface.d.ts +41 -0
  102. package/dist/plugins/plugin.interface.d.ts.map +1 -0
  103. package/dist/plugins/plugin.interface.js +12 -0
  104. package/dist/plugins/plugin.interface.js.map +1 -0
  105. package/dist/serialization/data-frame-bridge.d.ts +141 -0
  106. package/dist/serialization/data-frame-bridge.d.ts.map +1 -0
  107. package/dist/serialization/data-frame-bridge.js +355 -0
  108. package/dist/serialization/data-frame-bridge.js.map +1 -0
  109. package/dist/serialization/msg-pack-serializer.d.ts +45 -0
  110. package/dist/serialization/msg-pack-serializer.d.ts.map +1 -0
  111. package/dist/serialization/msg-pack-serializer.js +242 -0
  112. package/dist/serialization/msg-pack-serializer.js.map +1 -0
  113. package/dist/serialization/numpy-bridge.d.ts +96 -0
  114. package/dist/serialization/numpy-bridge.d.ts.map +1 -0
  115. package/dist/serialization/numpy-bridge.js +323 -0
  116. package/dist/serialization/numpy-bridge.js.map +1 -0
  117. package/dist/serialization/serializer.d.ts +78 -0
  118. package/dist/serialization/serializer.d.ts.map +1 -0
  119. package/dist/serialization/serializer.js +281 -0
  120. package/dist/serialization/serializer.js.map +1 -0
  121. package/dist/types/addon.d.ts +131 -0
  122. package/dist/types/addon.d.ts.map +1 -0
  123. package/dist/types/addon.js +32 -0
  124. package/dist/types/addon.js.map +1 -0
  125. package/dist/types/config.d.ts +236 -0
  126. package/dist/types/config.d.ts.map +1 -0
  127. package/dist/types/config.js +61 -0
  128. package/dist/types/config.js.map +1 -0
  129. package/dist/types/index.d.ts +10 -0
  130. package/dist/types/index.d.ts.map +1 -0
  131. package/dist/types/index.js +12 -0
  132. package/dist/types/index.js.map +1 -0
  133. package/dist/types/python.d.ts +235 -0
  134. package/dist/types/python.d.ts.map +1 -0
  135. package/dist/types/python.js +7 -0
  136. package/dist/types/python.js.map +1 -0
  137. package/dist/utils/error-translator.d.ts +83 -0
  138. package/dist/utils/error-translator.d.ts.map +1 -0
  139. package/dist/utils/error-translator.js +210 -0
  140. package/dist/utils/error-translator.js.map +1 -0
  141. package/dist/utils/locking.d.ts +3 -0
  142. package/dist/utils/locking.d.ts.map +1 -0
  143. package/dist/utils/locking.js +10 -0
  144. package/dist/utils/locking.js.map +1 -0
  145. package/dist/utils/logger.d.ts +27 -0
  146. package/dist/utils/logger.d.ts.map +1 -0
  147. package/dist/utils/logger.js +115 -0
  148. package/dist/utils/logger.js.map +1 -0
  149. package/dist/utils/memory-monitor.d.ts +43 -0
  150. package/dist/utils/memory-monitor.d.ts.map +1 -0
  151. package/dist/utils/memory-monitor.js +143 -0
  152. package/dist/utils/memory-monitor.js.map +1 -0
  153. package/package.json +93 -17
  154. package/prebuilds/darwin-arm64/pynect-prebuild.json +8 -0
  155. package/prebuilds/darwin-arm64/pynect_addon.node +0 -0
  156. package/prebuilds/darwin-x64/pynect-prebuild.json +8 -0
  157. package/prebuilds/darwin-x64/pynect_addon.node +0 -0
  158. package/prebuilds/linux-arm64/pynect-prebuild.json +9 -0
  159. package/prebuilds/linux-arm64/pynect_addon.node +0 -0
  160. package/prebuilds/linux-x64/pynect-prebuild.json +9 -0
  161. package/prebuilds/linux-x64/pynect_addon.node +0 -0
  162. package/prebuilds/win32-arm64/pynect-prebuild.json +8 -0
  163. package/prebuilds/win32-arm64/pynect_addon.node +0 -0
  164. package/prebuilds/win32-x64/pynect-prebuild.json +8 -0
  165. package/prebuilds/win32-x64/pynect_addon.node +0 -0
  166. package/python/ast_inspector.py +248 -0
  167. package/python/error_handler.py +432 -0
  168. package/python/isolated_worker.py +94 -0
  169. package/python/pynect_dispatcher.py +382 -0
  170. package/python/pynect_runtime.py +574 -0
  171. package/python/serializer.py +373 -0
  172. package/python/type_inspector.py +315 -0
  173. package/scripts/build-native.js +50 -0
  174. package/scripts/cli.js +21 -0
  175. package/scripts/ensure-native.js +85 -0
  176. package/scripts/postinstall.js +40 -0
  177. package/scripts/pyfig.js +128 -0
  178. package/scripts/python-provisioner.js +23 -0
  179. package/scripts/smoke-python-boundary.js +103 -0
  180. package/src/native/gil_guard.cpp +25 -0
  181. package/src/native/gil_guard.h +174 -0
  182. package/src/native/native-addon-loader.ts +218 -0
  183. package/src/native/pynect_addon.cpp +942 -0
  184. package/src/native/python_bridge.cpp +904 -0
  185. package/src/native/python_bridge.h +263 -0
  186. package/src/native/thread_pool.cpp +375 -0
  187. package/src/native/thread_pool.h +185 -0
  188. package/src/native/type_converter.cpp +906 -0
  189. package/src/native/type_converter.h +271 -0
  190. package/index.d.ts +0 -3
  191. package/index.js +0 -3
@@ -0,0 +1,185 @@
1
+ /**
2
+ * pynect - 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 pynect {
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
+ /** Optional external cancellation key. */
57
+ std::string cancelKey;
58
+ };
59
+
60
+ /**
61
+ * Pool statistics for monitoring.
62
+ */
63
+ struct PoolStats {
64
+ std::atomic<uint64_t> totalTasksSubmitted{0};
65
+ std::atomic<uint64_t> totalTasksCompleted{0};
66
+ std::atomic<uint64_t> totalTasksFailed{0};
67
+ std::atomic<uint64_t> totalTasksTimedOut{0};
68
+ std::atomic<uint64_t> currentQueueSize{0};
69
+ std::atomic<uint32_t> activeThreads{0};
70
+ std::atomic<uint64_t> totalExecutionTimeUs{0};
71
+ };
72
+
73
+ /**
74
+ * PythonThreadPool - manages N worker threads that execute Python tasks.
75
+ *
76
+ * Thread lifecycle:
77
+ * 1. Worker thread starts and calls PyEval_RestoreThread to get GIL state
78
+ * 2. Worker enters wait loop (releases GIL while waiting for work)
79
+ * 3. When task arrives, worker acquires GIL, executes task, releases GIL
80
+ * 4. Result is posted back to JS via libuv
81
+ */
82
+ class PythonThreadPool {
83
+ public:
84
+ explicit PythonThreadPool(uint32_t numThreads = 4, uint32_t maxQueueSize = 1000);
85
+ ~PythonThreadPool();
86
+
87
+ // Non-copyable, non-movable
88
+ PythonThreadPool(const PythonThreadPool&) = delete;
89
+ PythonThreadPool& operator=(const PythonThreadPool&) = delete;
90
+ PythonThreadPool(PythonThreadPool&&) = delete;
91
+ PythonThreadPool& operator=(PythonThreadPool&&) = delete;
92
+
93
+ /**
94
+ * Initialize the thread pool. Must be called from the main thread
95
+ * after Python is initialized (Py_Initialize() called).
96
+ */
97
+ void initialize(Napi::Env env);
98
+
99
+ /**
100
+ * Submit a task to the pool. Thread-safe.
101
+ *
102
+ * @param work - Function to run with GIL held in a worker thread
103
+ * @param callback - Function to call in event loop with result
104
+ * @param timeoutMs - Task timeout in milliseconds (0 = no timeout)
105
+ * @returns task ID, or 0 if queue is full
106
+ */
107
+ uint64_t submit(
108
+ std::function<TaskResult()> work,
109
+ std::function<void(TaskResult)> callback,
110
+ uint32_t timeoutMs = 0,
111
+ std::string cancelKey = {}
112
+ );
113
+
114
+ /** Cancel a task that has not started. */
115
+ bool cancel(const std::string& cancelKey);
116
+
117
+ /**
118
+ * Resize the thread pool. Blocks until all queued tasks complete.
119
+ */
120
+ void resize(uint32_t newNumThreads);
121
+
122
+ /**
123
+ * Graceful shutdown - waits for all queued tasks to complete.
124
+ */
125
+ void shutdown();
126
+
127
+ /**
128
+ * Force shutdown - cancels queued tasks, waits for active tasks.
129
+ */
130
+ void forceShutdown();
131
+
132
+ /** Get current pool statistics */
133
+ const PoolStats& getStats() const noexcept { return _stats; }
134
+
135
+ /** Get number of pending tasks in queue */
136
+ size_t getQueueSize() const;
137
+
138
+ /** Get number of active worker threads */
139
+ uint32_t getActiveThreadCount() const noexcept { return _stats.activeThreads.load(); }
140
+
141
+ /** Get total thread count */
142
+ uint32_t getThreadCount() const noexcept { return static_cast<uint32_t>(_threads.size()); }
143
+
144
+ /** Check if pool is running */
145
+ bool isRunning() const noexcept { return _running.load(); }
146
+
147
+ private:
148
+ /** Worker thread entry point */
149
+ void _workerThread(uint32_t threadId);
150
+
151
+ /** Post result back to event loop via libuv */
152
+ void _postResult(uint64_t taskId, TaskResult result, std::function<void(TaskResult)> callback);
153
+
154
+ /** Allow process exit while idle without dropping callbacks for active tasks. */
155
+ void _resultDelivered(Napi::Env env);
156
+
157
+ /** Check and handle task timeout */
158
+ bool _isTimedOut(const PythonTask& task) const;
159
+
160
+ uint32_t _numThreads;
161
+ uint32_t _maxQueueSize;
162
+ std::atomic<bool> _running{false};
163
+ std::atomic<bool> _draining{false};
164
+ std::atomic<uint64_t> _nextTaskId{1};
165
+
166
+ // Task queue
167
+ std::deque<PythonTask> _queue;
168
+ mutable std::mutex _queueMutex;
169
+ std::condition_variable _queueCv;
170
+ std::condition_variable _drainCv;
171
+
172
+ // Worker threads
173
+ std::vector<std::thread> _threads;
174
+
175
+ // libuv async handle for posting results back to JS
176
+ // (Using Napi::ThreadSafeFunction for safe cross-thread callback)
177
+ Napi::ThreadSafeFunction _tsfn;
178
+ napi_env _env{nullptr};
179
+ std::atomic<uint32_t> _pendingResults{0};
180
+
181
+ // Statistics
182
+ PoolStats _stats;
183
+ };
184
+
185
+ } // namespace pynect