@reboot-dev/reboot 0.24.0 → 0.25.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.
- package/index.d.ts +62 -12
- package/index.js +256 -13
- package/package.json +6 -4
- package/rbt.js +5 -2
- package/reboot_native.cc +1186 -1389
- package/reboot_native.cjs +1 -0
- package/reboot_native.d.ts +8 -0
- package/version.d.ts +1 -1
- package/version.js +1 -1
package/reboot_native.cc
CHANGED
|
@@ -81,6 +81,23 @@ struct PythonNodeAdaptor {
|
|
|
81
81
|
|
|
82
82
|
template <typename F>
|
|
83
83
|
void ScheduleCallbackOnNodeEventLoop(F&& f) {
|
|
84
|
+
// It is possible that Node.js is shutting down and has already
|
|
85
|
+
// called the `thread_safe_function` finalizer and trying to use
|
|
86
|
+
// `thread_safe_function` will raise SIGABRT or SIGSEGV.
|
|
87
|
+
//
|
|
88
|
+
// Given that we're shutting down we just return!
|
|
89
|
+
//
|
|
90
|
+
// If the callback is to free memory, e.g., from a
|
|
91
|
+
// `NapiSafeReference`, that's not a problem because we're
|
|
92
|
+
// shutting down so all the memory will get freed then! ;-)
|
|
93
|
+
//
|
|
94
|
+
// If this is for some other critical function that is necessary
|
|
95
|
+
// to do _before_ shutting down then we'll need to rethink this
|
|
96
|
+
// approaach.
|
|
97
|
+
if (thread_safe_function_finalized) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
84
101
|
napi_status status = thread_safe_function.BlockingCall(
|
|
85
102
|
[f = std::forward<F>(f)](
|
|
86
103
|
Napi::Env env,
|
|
@@ -116,6 +133,8 @@ struct PythonNodeAdaptor {
|
|
|
116
133
|
|
|
117
134
|
Napi::ThreadSafeFunction thread_safe_function;
|
|
118
135
|
|
|
136
|
+
bool thread_safe_function_finalized = false;
|
|
137
|
+
|
|
119
138
|
std::thread thread;
|
|
120
139
|
std::mutex mutex;
|
|
121
140
|
std::condition_variable python_functions_not_empty;
|
|
@@ -133,6 +152,83 @@ static Napi::FunctionReference* js_launchSubprocessConsensus =
|
|
|
133
152
|
new Napi::FunctionReference();
|
|
134
153
|
|
|
135
154
|
|
|
155
|
+
struct NapiReferenceDeleter {
|
|
156
|
+
template <typename T>
|
|
157
|
+
void operator()(Napi::Reference<T>* reference) {
|
|
158
|
+
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
159
|
+
[reference](Napi::Env) {
|
|
160
|
+
delete reference;
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
template <typename T>
|
|
167
|
+
class NapiSafeReference {
|
|
168
|
+
public:
|
|
169
|
+
NapiSafeReference(T t)
|
|
170
|
+
: _reference(
|
|
171
|
+
new Napi::Reference<T>(Napi::Persistent(std::move(t))),
|
|
172
|
+
NapiReferenceDeleter()) {}
|
|
173
|
+
|
|
174
|
+
NapiSafeReference(Napi::Reference<T>&& reference)
|
|
175
|
+
: _reference(
|
|
176
|
+
new Napi::Reference<T>(std::move(reference)),
|
|
177
|
+
NapiReferenceDeleter()) {}
|
|
178
|
+
|
|
179
|
+
// Helper for getting the value of the reference. We require a
|
|
180
|
+
// `Napi::Env` to ensure we only try and get the value from within a
|
|
181
|
+
// Node thread (e.g., using the main thread or from within a NAPI
|
|
182
|
+
// thread safe function).
|
|
183
|
+
T Value(Napi::Env) const {
|
|
184
|
+
return _reference->Value();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private:
|
|
188
|
+
std::shared_ptr<Napi::Reference<T>> _reference;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
using NapiSafeFunctionReference = NapiSafeReference<Napi::Function>;
|
|
192
|
+
using NapiSafeObjectReference = NapiSafeReference<Napi::Object>;
|
|
193
|
+
|
|
194
|
+
// For every servicer we construct a _subclass_ of our Python
|
|
195
|
+
// generated Node adaptor, e.g., `GreeterServicerNodeAdaptor`, _at
|
|
196
|
+
// runtime_ which we add to the following Python module. This module
|
|
197
|
+
// also includes our C++ <-> Python class wrappers for storing NAPI
|
|
198
|
+
// objects in Python to properly handle memory management.
|
|
199
|
+
PYBIND11_EMBEDDED_MODULE(reboot_native, m) {
|
|
200
|
+
py::class_<NapiSafeFunctionReference>(
|
|
201
|
+
m,
|
|
202
|
+
"NapiSafeFunctionReference");
|
|
203
|
+
|
|
204
|
+
py::class_<NapiSafeObjectReference>(
|
|
205
|
+
m,
|
|
206
|
+
"NapiSafeObjectReference");
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
Napi::External<py::object> make_napi_external(
|
|
211
|
+
Napi::Env env,
|
|
212
|
+
py::object* py_object,
|
|
213
|
+
const napi_type_tag* type_tag = nullptr) {
|
|
214
|
+
auto js_external = Napi::External<py::object>::New(
|
|
215
|
+
env,
|
|
216
|
+
py_object,
|
|
217
|
+
[](Napi::Env, py::object* py_object) {
|
|
218
|
+
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
219
|
+
[py_object]() {
|
|
220
|
+
delete py_object;
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
if (type_tag != nullptr) {
|
|
225
|
+
js_external.TypeTag(type_tag);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return js_external;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
|
|
136
232
|
void PythonNodeAdaptor::Initialize(
|
|
137
233
|
Napi::Env& env,
|
|
138
234
|
const Napi::Function& js_callback) {
|
|
@@ -143,8 +239,12 @@ void PythonNodeAdaptor::Initialize(
|
|
|
143
239
|
/* Max queue size (0 = unlimited): */ 0,
|
|
144
240
|
/* Initial thread count: */ 1,
|
|
145
241
|
/* Context: */ this,
|
|
146
|
-
|
|
147
|
-
[](Napi::Env env, void*, PythonNodeAdaptor*) {
|
|
242
|
+
// Finalizer:
|
|
243
|
+
[this](Napi::Env env, void*, PythonNodeAdaptor*) {
|
|
244
|
+
// Set that we've been finalized so that we don't use
|
|
245
|
+
// `thread_safe_function` again, see comment in
|
|
246
|
+
// `ScheduleCallbackOnNodeEventLoop`.
|
|
247
|
+
thread_safe_function_finalized = true; },
|
|
148
248
|
/* Finalizer data: */ (void*) nullptr);
|
|
149
249
|
|
|
150
250
|
// We start with our thread safe function unreferenced so that
|
|
@@ -301,6 +401,476 @@ std::string message_from_js_error(const Napi::Object& js_error) {
|
|
|
301
401
|
}
|
|
302
402
|
|
|
303
403
|
|
|
404
|
+
template <typename F, typename T = std::invoke_result_t<F>>
|
|
405
|
+
T RunCallbackOnPythonEventLoop(F&& f) {
|
|
406
|
+
std::promise<T> promise;
|
|
407
|
+
|
|
408
|
+
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
409
|
+
[&f, &promise]() {
|
|
410
|
+
try {
|
|
411
|
+
if constexpr (std::is_void<T>::value) {
|
|
412
|
+
f();
|
|
413
|
+
promise.set_value();
|
|
414
|
+
} else {
|
|
415
|
+
promise.set_value(f());
|
|
416
|
+
}
|
|
417
|
+
} catch (const std::exception& e) {
|
|
418
|
+
promise.set_exception(
|
|
419
|
+
std::make_exception_ptr(
|
|
420
|
+
std::runtime_error(e.what())));
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
return promise.get_future().get();
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
template <typename F, typename T = std::invoke_result_t<F, Napi::Env>>
|
|
429
|
+
T RunCallbackOnNodeEventLoop(F&& f, bool warn = false) {
|
|
430
|
+
std::promise<T> promise;
|
|
431
|
+
|
|
432
|
+
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
433
|
+
[&f, &warn, &promise](Napi::Env env) {
|
|
434
|
+
try {
|
|
435
|
+
if constexpr (std::is_void<T>::value) {
|
|
436
|
+
f(env);
|
|
437
|
+
promise.set_value();
|
|
438
|
+
} else {
|
|
439
|
+
promise.set_value(f(env));
|
|
440
|
+
}
|
|
441
|
+
} catch (const Napi::Error& e) {
|
|
442
|
+
if (warn) {
|
|
443
|
+
env.Global()
|
|
444
|
+
.Get("console")
|
|
445
|
+
.As<Napi::Object>()
|
|
446
|
+
.Get("warn")
|
|
447
|
+
.As<Napi::Function>()
|
|
448
|
+
.Call({e.Value(), Napi::String::New(env, "\n")});
|
|
449
|
+
}
|
|
450
|
+
promise.set_exception(
|
|
451
|
+
std::make_exception_ptr(
|
|
452
|
+
std::runtime_error(e.what())));
|
|
453
|
+
} catch (const std::exception& e) {
|
|
454
|
+
if (warn) {
|
|
455
|
+
env.Global()
|
|
456
|
+
.Get("console")
|
|
457
|
+
.As<Napi::Object>()
|
|
458
|
+
.Get("warn")
|
|
459
|
+
.As<Napi::Function>()
|
|
460
|
+
.Call(
|
|
461
|
+
{Napi::String::New(env, e.what()),
|
|
462
|
+
Napi::String::New(env, "\n")});
|
|
463
|
+
}
|
|
464
|
+
promise.set_exception(
|
|
465
|
+
std::make_exception_ptr(
|
|
466
|
+
std::runtime_error(e.what())));
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
return promise.get_future().get();
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
template <typename PythonCallback, typename NodeCallback>
|
|
475
|
+
Napi::Promise NodePromiseFromPythonCallback(
|
|
476
|
+
Napi::Env env,
|
|
477
|
+
PythonCallback&& python_callback,
|
|
478
|
+
NodeCallback&& node_callback) {
|
|
479
|
+
auto deferred = std::make_shared<Napi::Promise::Deferred>(env);
|
|
480
|
+
auto promise = deferred->Promise();
|
|
481
|
+
|
|
482
|
+
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
483
|
+
[python_callback = std::forward<PythonCallback>(python_callback),
|
|
484
|
+
node_callback = std::forward<NodeCallback>(node_callback),
|
|
485
|
+
deferred = std::move(deferred)]() mutable {
|
|
486
|
+
try {
|
|
487
|
+
auto result = python_callback();
|
|
488
|
+
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
489
|
+
[node_callback = std::forward<NodeCallback>(node_callback),
|
|
490
|
+
deferred = std::move(deferred),
|
|
491
|
+
result = std::move(result)](Napi::Env env) mutable {
|
|
492
|
+
try {
|
|
493
|
+
deferred->Resolve(node_callback(env, std::move(result)));
|
|
494
|
+
} catch (const Napi::Error& e) {
|
|
495
|
+
deferred->Reject(e.Value());
|
|
496
|
+
} catch (const std::exception& e) {
|
|
497
|
+
// TODO: is this code unreachable because Node.js
|
|
498
|
+
// will properly always only pass us a `Napi::Error`?
|
|
499
|
+
deferred->Reject(
|
|
500
|
+
Napi::Error::New(env, e.what()).Value());
|
|
501
|
+
} catch (...) {
|
|
502
|
+
// TODO: is this code unreachable because Node.js
|
|
503
|
+
// will properly always only pass us a `Napi::Error`?
|
|
504
|
+
deferred->Reject(
|
|
505
|
+
Napi::Error::New(env, "Unknown error").Value());
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
} catch (pybind11::error_already_set& e) {
|
|
509
|
+
std::string exception = py_exception_str(e.value());
|
|
510
|
+
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
511
|
+
[deferred = std::move(deferred),
|
|
512
|
+
exception = std::move(exception)](Napi::Env env) mutable {
|
|
513
|
+
deferred->Reject(Napi::Error::New(env, exception).Value());
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
return promise;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
template <
|
|
523
|
+
typename PythonFutureCallback,
|
|
524
|
+
typename PythonDoneCallback,
|
|
525
|
+
typename NodeCallback>
|
|
526
|
+
Napi::Promise NodePromiseFromPythonFuture(
|
|
527
|
+
Napi::Env env,
|
|
528
|
+
PythonFutureCallback&& python_future_callback,
|
|
529
|
+
PythonDoneCallback&& python_done_callback,
|
|
530
|
+
NodeCallback&& node_callback) {
|
|
531
|
+
auto deferred = std::make_shared<Napi::Promise::Deferred>(env);
|
|
532
|
+
auto promise = deferred->Promise();
|
|
533
|
+
|
|
534
|
+
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
535
|
+
[python_future_callback = std::forward<PythonFutureCallback>(
|
|
536
|
+
python_future_callback),
|
|
537
|
+
python_done_callback = std::forward<PythonDoneCallback>(
|
|
538
|
+
python_done_callback),
|
|
539
|
+
node_callback = std::forward<NodeCallback>(node_callback),
|
|
540
|
+
deferred = std::move(deferred)]() mutable {
|
|
541
|
+
try {
|
|
542
|
+
// TODO: check that `py_future` is a future!
|
|
543
|
+
py::object py_future = python_future_callback();
|
|
544
|
+
|
|
545
|
+
py_future.attr("add_done_callback")(py::cpp_function(
|
|
546
|
+
[python_done_callback = std::forward<PythonDoneCallback>(
|
|
547
|
+
python_done_callback),
|
|
548
|
+
node_callback = std::forward<NodeCallback>(node_callback),
|
|
549
|
+
deferred = std::move(deferred),
|
|
550
|
+
// NOTE: need to keep a reference to `py_future` so that it
|
|
551
|
+
// doesn't get destroyed before completing!
|
|
552
|
+
py_future = new py::object(py_future)](py::object) mutable {
|
|
553
|
+
py::object py_exception = py_future->attr("exception")();
|
|
554
|
+
if (!py_exception.is_none()) {
|
|
555
|
+
py::str py_exception_string = py_exception_str(py_exception);
|
|
556
|
+
std::string exception = py_exception_string;
|
|
557
|
+
|
|
558
|
+
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
559
|
+
[deferred = std::move(deferred),
|
|
560
|
+
exception = std::move(exception)](Napi::Env env) {
|
|
561
|
+
deferred->Reject(
|
|
562
|
+
Napi::Error::New(env, exception).Value());
|
|
563
|
+
});
|
|
564
|
+
} else {
|
|
565
|
+
// TODO: try / catch around `python_done_callback`.
|
|
566
|
+
py::object py_result = py_future->attr("result")();
|
|
567
|
+
auto result = python_done_callback(std::move(py_result));
|
|
568
|
+
|
|
569
|
+
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
570
|
+
[node_callback =
|
|
571
|
+
std::forward<NodeCallback>(node_callback),
|
|
572
|
+
deferred = std::move(deferred),
|
|
573
|
+
result = std::move(result)](Napi::Env env) mutable {
|
|
574
|
+
// TODO: try / catch around `node_callback`.
|
|
575
|
+
auto js_result = node_callback(env, std::move(result));
|
|
576
|
+
deferred->Resolve(js_result);
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
delete py_future;
|
|
580
|
+
},
|
|
581
|
+
py::arg("future")));
|
|
582
|
+
} catch (pybind11::error_already_set& e) {
|
|
583
|
+
std::string exception = py_exception_str(e.value());
|
|
584
|
+
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
585
|
+
[deferred = std::move(deferred),
|
|
586
|
+
exception = std::move(exception)](Napi::Env env) mutable {
|
|
587
|
+
deferred->Reject(Napi::Error::New(env, exception).Value());
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
return promise;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
template <typename PythonFutureCallback>
|
|
597
|
+
Napi::Promise NodePromiseFromPythonFuture(
|
|
598
|
+
Napi::Env env,
|
|
599
|
+
PythonFutureCallback&& python_future_callback) {
|
|
600
|
+
return NodePromiseFromPythonFuture(
|
|
601
|
+
env,
|
|
602
|
+
std::forward<PythonFutureCallback>(python_future_callback),
|
|
603
|
+
[](py::object py_result) {
|
|
604
|
+
// TODO: check that `py_result` is `None`.
|
|
605
|
+
return std::nullopt;
|
|
606
|
+
},
|
|
607
|
+
[](Napi::Env env, std::nullopt_t) {
|
|
608
|
+
return env.Null();
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
template <
|
|
614
|
+
typename PythonTaskCallback,
|
|
615
|
+
typename PythonDoneCallback,
|
|
616
|
+
typename NodeCallback,
|
|
617
|
+
typename... CreateTaskArgs>
|
|
618
|
+
Napi::Promise NodePromiseFromPythonTask(
|
|
619
|
+
Napi::Env env,
|
|
620
|
+
std::string&& name,
|
|
621
|
+
std::tuple<std::string, std::string, CreateTaskArgs...>&& create_task,
|
|
622
|
+
PythonTaskCallback&& python_task_callback,
|
|
623
|
+
PythonDoneCallback&& python_done_callback,
|
|
624
|
+
NodeCallback&& node_callback) {
|
|
625
|
+
return NodePromiseFromPythonFuture(
|
|
626
|
+
env,
|
|
627
|
+
[name = std::move(name),
|
|
628
|
+
create_task = std::move(create_task),
|
|
629
|
+
python_task_callback = std::forward<PythonTaskCallback>(
|
|
630
|
+
python_task_callback)]() mutable {
|
|
631
|
+
py::object py_task = std::apply(
|
|
632
|
+
[&name, &python_task_callback](
|
|
633
|
+
const std::string& import,
|
|
634
|
+
const std::string& attr,
|
|
635
|
+
auto&&... args) mutable {
|
|
636
|
+
return py::module::import(import.c_str())
|
|
637
|
+
.attr(attr.c_str())(
|
|
638
|
+
python_task_callback(),
|
|
639
|
+
std::forward<decltype(args)>(args)...,
|
|
640
|
+
"name"_a = name.c_str());
|
|
641
|
+
},
|
|
642
|
+
std::move(create_task));
|
|
643
|
+
|
|
644
|
+
return py_task;
|
|
645
|
+
},
|
|
646
|
+
std::forward<PythonDoneCallback>(python_done_callback),
|
|
647
|
+
std::forward<NodeCallback>(node_callback));
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
template <typename PythonTaskCallback, typename... CreateTaskArgs>
|
|
652
|
+
Napi::Promise NodePromiseFromPythonTask(
|
|
653
|
+
Napi::Env env,
|
|
654
|
+
std::string&& name,
|
|
655
|
+
std::tuple<std::string, std::string, CreateTaskArgs...>&& create_task,
|
|
656
|
+
PythonTaskCallback&& python_task_callback) {
|
|
657
|
+
return NodePromiseFromPythonTask(
|
|
658
|
+
env,
|
|
659
|
+
std::move(name),
|
|
660
|
+
std::move(create_task),
|
|
661
|
+
std::forward<PythonTaskCallback>(python_task_callback),
|
|
662
|
+
[](py::object py_result) {
|
|
663
|
+
// TODO: check that `py_result` is `None`.
|
|
664
|
+
return std::nullopt;
|
|
665
|
+
},
|
|
666
|
+
[](Napi::Env env, std::nullopt_t) {
|
|
667
|
+
return env.Null();
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
template <
|
|
673
|
+
typename PythonTaskCallback,
|
|
674
|
+
typename PythonDoneCallback,
|
|
675
|
+
typename NodeCallback>
|
|
676
|
+
Napi::Promise NodePromiseFromPythonTaskWithContext(
|
|
677
|
+
Napi::Env env,
|
|
678
|
+
std::string&& name,
|
|
679
|
+
NapiSafeReference<Napi::External<py::object>>& js_context,
|
|
680
|
+
PythonTaskCallback&& python_task_callback,
|
|
681
|
+
PythonDoneCallback&& python_done_callback,
|
|
682
|
+
NodeCallback&& node_callback) {
|
|
683
|
+
py::object* py_context = js_context.Value(env).Data();
|
|
684
|
+
return NodePromiseFromPythonTask(
|
|
685
|
+
env,
|
|
686
|
+
std::move(name),
|
|
687
|
+
std::make_tuple(
|
|
688
|
+
std::string("reboot.nodejs.python"),
|
|
689
|
+
std::string("create_task_with_context"),
|
|
690
|
+
py_context),
|
|
691
|
+
[js_context, // Ensures `py_context` remains valid.
|
|
692
|
+
python_task_callback =
|
|
693
|
+
std::forward<PythonTaskCallback>(python_task_callback)]() mutable {
|
|
694
|
+
return python_task_callback();
|
|
695
|
+
},
|
|
696
|
+
std::forward<PythonDoneCallback>(python_done_callback),
|
|
697
|
+
std::forward<NodeCallback>(node_callback));
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
template <typename PythonTaskCallback>
|
|
702
|
+
Napi::Promise NodePromiseFromPythonTaskWithContext(
|
|
703
|
+
Napi::Env env,
|
|
704
|
+
std::string&& name,
|
|
705
|
+
NapiSafeReference<Napi::External<py::object>>& js_context,
|
|
706
|
+
PythonTaskCallback&& python_task_callback) {
|
|
707
|
+
return NodePromiseFromPythonTaskWithContext(
|
|
708
|
+
env,
|
|
709
|
+
std::move(name),
|
|
710
|
+
js_context,
|
|
711
|
+
std::forward<PythonTaskCallback>(python_task_callback),
|
|
712
|
+
[](py::object py_result) {
|
|
713
|
+
// TODO: check that `py_result` is `None`.
|
|
714
|
+
return std::nullopt;
|
|
715
|
+
},
|
|
716
|
+
[](Napi::Env env, std::nullopt_t) {
|
|
717
|
+
return env.Null();
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
|
|
722
|
+
template <
|
|
723
|
+
typename NodePromiseCallback,
|
|
724
|
+
typename NodePromiseResolvedCallback,
|
|
725
|
+
typename PythonCallback>
|
|
726
|
+
py::object PythonFutureFromNodePromise(
|
|
727
|
+
NodePromiseCallback&& node_promise_callback,
|
|
728
|
+
NodePromiseResolvedCallback&& node_promise_resolved_callback,
|
|
729
|
+
PythonCallback&& python_callback) {
|
|
730
|
+
py::object py_future = py::module::import("asyncio").attr("Future")();
|
|
731
|
+
|
|
732
|
+
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
733
|
+
[node_promise_callback =
|
|
734
|
+
std::forward<NodePromiseCallback>(node_promise_callback),
|
|
735
|
+
node_promise_resolved_callback =
|
|
736
|
+
std::forward<NodePromiseResolvedCallback>(
|
|
737
|
+
node_promise_resolved_callback),
|
|
738
|
+
python_callback = std::forward<PythonCallback>(python_callback),
|
|
739
|
+
py_future = new py::object(py_future)](Napi::Env env) mutable {
|
|
740
|
+
try {
|
|
741
|
+
Napi::Object js_promise = node_promise_callback(env);
|
|
742
|
+
|
|
743
|
+
js_promise
|
|
744
|
+
.Get("then")
|
|
745
|
+
.As<Napi::Function>()
|
|
746
|
+
.Call(
|
|
747
|
+
js_promise,
|
|
748
|
+
{Napi::Function::New(
|
|
749
|
+
env,
|
|
750
|
+
[node_promise_resolved_callback =
|
|
751
|
+
std::forward<NodePromiseResolvedCallback>(
|
|
752
|
+
node_promise_resolved_callback),
|
|
753
|
+
python_callback = std::forward<PythonCallback>(
|
|
754
|
+
python_callback),
|
|
755
|
+
py_future](const Napi::CallbackInfo& info) mutable {
|
|
756
|
+
// TODO: put a try/catch around
|
|
757
|
+
// `node_promise_resolved_callback` to handle
|
|
758
|
+
// any errors.
|
|
759
|
+
|
|
760
|
+
auto result = node_promise_resolved_callback(
|
|
761
|
+
info.Env(),
|
|
762
|
+
info[0]);
|
|
763
|
+
|
|
764
|
+
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
765
|
+
[python_callback = std::forward<PythonCallback>(
|
|
766
|
+
python_callback),
|
|
767
|
+
py_future,
|
|
768
|
+
result = std::move(result)]() mutable {
|
|
769
|
+
bool cancelled =
|
|
770
|
+
py_future->attr("cancelled")().cast<bool>();
|
|
771
|
+
if (!cancelled) {
|
|
772
|
+
// TODO: put a try/catch around
|
|
773
|
+
// `python_callback` to handle any
|
|
774
|
+
// errors.
|
|
775
|
+
py_future->attr("set_result")(
|
|
776
|
+
python_callback(std::move(result)));
|
|
777
|
+
}
|
|
778
|
+
delete py_future;
|
|
779
|
+
});
|
|
780
|
+
}),
|
|
781
|
+
Napi::Function::New(
|
|
782
|
+
env,
|
|
783
|
+
[py_future](const Napi::CallbackInfo& info) {
|
|
784
|
+
std::string message = message_from_js_error(
|
|
785
|
+
info[0].As<Napi::Object>());
|
|
786
|
+
|
|
787
|
+
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
788
|
+
[py_future, message = std::move(message)]() {
|
|
789
|
+
bool cancelled =
|
|
790
|
+
py_future->attr("cancelled")()
|
|
791
|
+
.cast<bool>();
|
|
792
|
+
if (!cancelled) {
|
|
793
|
+
py_future->attr("set_exception")(
|
|
794
|
+
py::module::import("builtins")
|
|
795
|
+
.attr("Exception")(
|
|
796
|
+
message));
|
|
797
|
+
}
|
|
798
|
+
delete py_future;
|
|
799
|
+
});
|
|
800
|
+
})});
|
|
801
|
+
} catch (const std::exception& e) {
|
|
802
|
+
// NOTE: we're just catching exception here vs `Napi::Error`
|
|
803
|
+
// since all we care about is `what()` but we're making sure
|
|
804
|
+
// that we'll get all `Napi::Error` with this
|
|
805
|
+
// `static_assert`.
|
|
806
|
+
static_assert(
|
|
807
|
+
std::is_base_of<std::exception, Napi::Error>::value,
|
|
808
|
+
"Expecting `Napi::Error` to be subclass of `std::exception`");
|
|
809
|
+
|
|
810
|
+
std::string message = e.what();
|
|
811
|
+
|
|
812
|
+
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
813
|
+
[py_future, message = std::move(message)]() {
|
|
814
|
+
bool cancelled =
|
|
815
|
+
py_future->attr("cancelled")().cast<bool>();
|
|
816
|
+
if (!cancelled) {
|
|
817
|
+
py_future->attr("set_exception")(
|
|
818
|
+
py::module::import("builtins")
|
|
819
|
+
.attr("Exception")(message));
|
|
820
|
+
}
|
|
821
|
+
delete py_future;
|
|
822
|
+
});
|
|
823
|
+
} catch (...) {
|
|
824
|
+
// TODO: is this code unreachable because Node.js
|
|
825
|
+
// will properly always only pass us a `Napi::Error`?
|
|
826
|
+
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
827
|
+
[py_future]() {
|
|
828
|
+
bool cancelled =
|
|
829
|
+
py_future->attr("cancelled")().cast<bool>();
|
|
830
|
+
if (!cancelled) {
|
|
831
|
+
py_future->attr("set_exception")(
|
|
832
|
+
py::module::import("builtins")
|
|
833
|
+
.attr("Exception")("Unknown error"));
|
|
834
|
+
}
|
|
835
|
+
delete py_future;
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
});
|
|
839
|
+
|
|
840
|
+
return py_future;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
|
|
844
|
+
template <
|
|
845
|
+
typename NodePromiseCallback,
|
|
846
|
+
typename NodePromiseResolvedCallback>
|
|
847
|
+
py::object PythonFutureFromNodePromise(
|
|
848
|
+
NodePromiseCallback&& node_promise_callback,
|
|
849
|
+
NodePromiseResolvedCallback&& node_promise_resolved_callback) {
|
|
850
|
+
return PythonFutureFromNodePromise(
|
|
851
|
+
std::forward<NodePromiseCallback>(node_promise_callback),
|
|
852
|
+
std::forward<NodePromiseResolvedCallback>(
|
|
853
|
+
node_promise_resolved_callback),
|
|
854
|
+
[](auto&& result) {
|
|
855
|
+
return std::move(result);
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
|
|
860
|
+
template <typename NodePromiseCallback>
|
|
861
|
+
py::object PythonFutureFromNodePromise(
|
|
862
|
+
NodePromiseCallback&& node_promise_callback) {
|
|
863
|
+
return PythonFutureFromNodePromise(
|
|
864
|
+
std::forward<NodePromiseCallback>(node_promise_callback),
|
|
865
|
+
[](Napi::Env, Napi::Value) {
|
|
866
|
+
return std::nullopt;
|
|
867
|
+
},
|
|
868
|
+
[](auto&&) {
|
|
869
|
+
return py::none();
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
|
|
304
874
|
void Initialize(const Napi::CallbackInfo& info) {
|
|
305
875
|
// Ensure we only initialize once, even if nodejs tries to load
|
|
306
876
|
// the module more than once.
|
|
@@ -327,17 +897,11 @@ void ImportPy(const Napi::CallbackInfo& info) {
|
|
|
327
897
|
std::string module = info[0].As<Napi::String>().Utf8Value();
|
|
328
898
|
std::string base64_encoded_rbt_py = info[1].As<Napi::String>().Utf8Value();
|
|
329
899
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
333
|
-
[&module, &base64_encoded_rbt_py, &promise]() {
|
|
900
|
+
RunCallbackOnPythonEventLoop(
|
|
901
|
+
[&module, &base64_encoded_rbt_py]() {
|
|
334
902
|
py::module::import("reboot.nodejs.python")
|
|
335
903
|
.attr("import_py")(module, base64_encoded_rbt_py);
|
|
336
|
-
|
|
337
|
-
promise.set_value();
|
|
338
904
|
});
|
|
339
|
-
|
|
340
|
-
promise.get_future().get();
|
|
341
905
|
}
|
|
342
906
|
|
|
343
907
|
|
|
@@ -401,41 +965,16 @@ std::string uint8array_to_str(const Napi::Uint8Array& arr) {
|
|
|
401
965
|
|
|
402
966
|
|
|
403
967
|
Napi::Value Reboot_constructor(const Napi::CallbackInfo& info) {
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
407
|
-
[&promise]() {
|
|
968
|
+
py::object* py_reboot = RunCallbackOnPythonEventLoop(
|
|
969
|
+
[]() {
|
|
408
970
|
py::object tests = py::module::import("reboot.aio.tests");
|
|
409
|
-
|
|
410
|
-
}
|
|
411
|
-
// TODO(benh): improve error handling mechanism to force all
|
|
412
|
-
// raised exceptions to be handled.
|
|
413
|
-
//
|
|
414
|
-
// [](py::error_already_set& e) {
|
|
415
|
-
|
|
416
|
-
// },
|
|
417
|
-
// [](std::exception& e) {
|
|
418
|
-
|
|
419
|
-
// },
|
|
420
|
-
// []() {
|
|
421
|
-
|
|
422
|
-
// }
|
|
423
|
-
);
|
|
971
|
+
return new py::object(tests.attr("Reboot")());
|
|
972
|
+
});
|
|
424
973
|
|
|
425
|
-
py::object
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
info.Env(),
|
|
430
|
-
py_reboot,
|
|
431
|
-
[](Napi::Env, py::object* py_reboot) {
|
|
432
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
433
|
-
[py_reboot]() {
|
|
434
|
-
delete py_reboot;
|
|
435
|
-
});
|
|
436
|
-
});
|
|
437
|
-
|
|
438
|
-
js_external_reboot.TypeTag(&reboot_aio_tests_Reboot);
|
|
974
|
+
Napi::External<py::object> js_external_reboot = make_napi_external(
|
|
975
|
+
info.Env(),
|
|
976
|
+
py_reboot,
|
|
977
|
+
&reboot_aio_tests_Reboot);
|
|
439
978
|
|
|
440
979
|
return js_external_reboot;
|
|
441
980
|
}
|
|
@@ -468,15 +1007,12 @@ Napi::Value Reboot_createExternalContext(const Napi::CallbackInfo& info) {
|
|
|
468
1007
|
app_internal = info[5].As<Napi::Boolean>();
|
|
469
1008
|
}
|
|
470
1009
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
1010
|
+
py::object* py_context = RunCallbackOnPythonEventLoop(
|
|
474
1011
|
[py_reboot,
|
|
475
1012
|
&name,
|
|
476
1013
|
&idempotency_seed,
|
|
477
1014
|
&bearer_token,
|
|
478
|
-
app_internal
|
|
479
|
-
&promise]() {
|
|
1015
|
+
app_internal]() {
|
|
480
1016
|
py::object py_idempotency_seed = py::none();
|
|
481
1017
|
if (idempotency_seed.has_value()) {
|
|
482
1018
|
py_idempotency_seed =
|
|
@@ -487,29 +1023,18 @@ Napi::Value Reboot_createExternalContext(const Napi::CallbackInfo& info) {
|
|
|
487
1023
|
if (bearer_token.has_value()) {
|
|
488
1024
|
py_bearer_token = py::str(*bearer_token);
|
|
489
1025
|
}
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
"app_internal"_a = app_internal)));
|
|
1026
|
+
return new py::object(
|
|
1027
|
+
py_reboot->attr("create_external_context")(
|
|
1028
|
+
"name"_a = name,
|
|
1029
|
+
"idempotency_seed"_a = py_idempotency_seed,
|
|
1030
|
+
"bearer_token"_a = py_bearer_token,
|
|
1031
|
+
"app_internal"_a = app_internal));
|
|
497
1032
|
});
|
|
498
1033
|
|
|
499
|
-
py::object
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
info.Env(),
|
|
504
|
-
py_context,
|
|
505
|
-
[](Napi::Env, py::object* py_context) {
|
|
506
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
507
|
-
[py_context]() {
|
|
508
|
-
delete py_context;
|
|
509
|
-
});
|
|
510
|
-
});
|
|
511
|
-
|
|
512
|
-
js_external_context.TypeTag(&reboot_aio_external_ExternalContext);
|
|
1034
|
+
Napi::External<py::object> js_external_context = make_napi_external(
|
|
1035
|
+
info.Env(),
|
|
1036
|
+
py_context,
|
|
1037
|
+
&reboot_aio_external_ExternalContext);
|
|
513
1038
|
|
|
514
1039
|
return js_from_native_external
|
|
515
1040
|
.Call(
|
|
@@ -518,61 +1043,6 @@ Napi::Value Reboot_createExternalContext(const Napi::CallbackInfo& info) {
|
|
|
518
1043
|
}
|
|
519
1044
|
|
|
520
1045
|
|
|
521
|
-
struct NapiReferenceDeleter {
|
|
522
|
-
template <typename T>
|
|
523
|
-
void operator()(Napi::Reference<T>* reference) {
|
|
524
|
-
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
525
|
-
[reference](Napi::Env) {
|
|
526
|
-
delete reference;
|
|
527
|
-
});
|
|
528
|
-
}
|
|
529
|
-
};
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
template <typename T>
|
|
533
|
-
class NapiSafeReference {
|
|
534
|
-
public:
|
|
535
|
-
NapiSafeReference(T t)
|
|
536
|
-
: _reference(
|
|
537
|
-
new Napi::Reference<T>(Napi::Persistent(std::move(t))),
|
|
538
|
-
NapiReferenceDeleter()) {}
|
|
539
|
-
|
|
540
|
-
NapiSafeReference(Napi::Reference<T>&& reference)
|
|
541
|
-
: _reference(
|
|
542
|
-
new Napi::Reference<T>(std::move(reference)),
|
|
543
|
-
NapiReferenceDeleter()) {}
|
|
544
|
-
|
|
545
|
-
// Helper for getting the value of the reference. We require a
|
|
546
|
-
// `Napi::Env` to ensure we only try and get the value from within a
|
|
547
|
-
// Node thread (e.g., using the main thread or from within a NAPI
|
|
548
|
-
// thread safe function).
|
|
549
|
-
T Value(Napi::Env) {
|
|
550
|
-
return _reference->Value();
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
private:
|
|
554
|
-
std::shared_ptr<Napi::Reference<T>> _reference;
|
|
555
|
-
};
|
|
556
|
-
|
|
557
|
-
using NapiSafeFunctionReference = NapiSafeReference<Napi::Function>;
|
|
558
|
-
using NapiSafeObjectReference = NapiSafeReference<Napi::Object>;
|
|
559
|
-
|
|
560
|
-
// For every servicer we construct a _subclass_ of our Python
|
|
561
|
-
// generated Node adaptor, e.g., `GreeterServicerNodeAdaptor`, _at
|
|
562
|
-
// runtime_ which we add to the following Python module. This module
|
|
563
|
-
// also includes our C++ <-> Python class wrappers for storing NAPI
|
|
564
|
-
// objects in Python to properly handle memory management.
|
|
565
|
-
PYBIND11_EMBEDDED_MODULE(reboot_native, m) {
|
|
566
|
-
py::class_<NapiSafeFunctionReference>(
|
|
567
|
-
m,
|
|
568
|
-
"NapiSafeFunctionReference");
|
|
569
|
-
|
|
570
|
-
py::class_<NapiSafeObjectReference>(
|
|
571
|
-
m,
|
|
572
|
-
"NapiSafeObjectReference");
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
|
|
576
1046
|
// NOTE: must be called within _Node_.
|
|
577
1047
|
Napi::Object make_js_context(
|
|
578
1048
|
Napi::Env& env,
|
|
@@ -580,58 +1050,34 @@ Napi::Object make_js_context(
|
|
|
580
1050
|
py::object* py_aborted,
|
|
581
1051
|
const std::string& kind) {
|
|
582
1052
|
Napi::External<py::object> js_external_context =
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
auto deferred = std::make_shared<Napi::Promise::Deferred>(env);
|
|
594
|
-
|
|
595
|
-
auto promise = deferred->Promise();
|
|
596
|
-
|
|
597
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
598
|
-
[py_aborted, deferred = std::move(deferred)]() {
|
|
599
|
-
py_aborted->attr("add_done_callback")(py::cpp_function(
|
|
600
|
-
[deferred = std::move(deferred)](
|
|
601
|
-
py::object py_future) {
|
|
602
|
-
py::object py_exception =
|
|
603
|
-
py_future.attr("exception")();
|
|
604
|
-
|
|
605
|
-
std::optional<std::string> exception;
|
|
606
|
-
|
|
607
|
-
if (!py_exception.is_none()) {
|
|
608
|
-
py::str py_exception_string =
|
|
609
|
-
py_exception_str(py_exception);
|
|
610
|
-
|
|
611
|
-
exception.emplace(py_exception_string);
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
615
|
-
[deferred = std::move(deferred),
|
|
616
|
-
exception = std::move(exception)](
|
|
617
|
-
Napi::Env env) {
|
|
618
|
-
if (!exception.has_value()) {
|
|
619
|
-
deferred->Resolve(env.Undefined());
|
|
620
|
-
} else {
|
|
621
|
-
deferred->Reject(
|
|
622
|
-
Napi::Error::New(env, *exception)
|
|
623
|
-
.Value());
|
|
624
|
-
}
|
|
625
|
-
});
|
|
626
|
-
}));
|
|
1053
|
+
make_napi_external(env, py_context);
|
|
1054
|
+
|
|
1055
|
+
Napi::Promise js_aborted = NodePromiseFromPythonFuture(
|
|
1056
|
+
env,
|
|
1057
|
+
[py_aborted]() {
|
|
1058
|
+
// After returning we won't need `py_aborted` anymore, so we
|
|
1059
|
+
// make a copy on the stack so we can delete our copy on the
|
|
1060
|
+
// heap.
|
|
1061
|
+
py::object py_aborted_on_stack = *py_aborted;
|
|
627
1062
|
delete py_aborted;
|
|
1063
|
+
return py_aborted_on_stack;
|
|
1064
|
+
},
|
|
1065
|
+
[](py::object py_result) {
|
|
1066
|
+
// TODO: check that `py_result` is `None`.
|
|
1067
|
+
return std::nullopt;
|
|
1068
|
+
},
|
|
1069
|
+
[](Napi::Env env, std::nullopt_t) {
|
|
1070
|
+
// NOTE: we're explicitly returning `undefined` here instead
|
|
1071
|
+
// of `null` which is the default if we were to not include
|
|
1072
|
+
// this lambda or the one above.
|
|
1073
|
+
return env.Undefined();
|
|
628
1074
|
});
|
|
629
1075
|
|
|
630
1076
|
return js_Context_fromNativeExternal
|
|
631
1077
|
->Call(
|
|
632
1078
|
{js_external_context,
|
|
633
1079
|
Napi::String::New(env, kind),
|
|
634
|
-
|
|
1080
|
+
js_aborted})
|
|
635
1081
|
.As<Napi::Object>();
|
|
636
1082
|
}
|
|
637
1083
|
|
|
@@ -661,21 +1107,17 @@ py::object make_py_authorizer(NapiSafeObjectReference js_authorizer) {
|
|
|
661
1107
|
py::object py_aborted,
|
|
662
1108
|
std::optional<std::string> bytes_state,
|
|
663
1109
|
std::optional<std::string> bytes_request) {
|
|
664
|
-
py::object py_future =
|
|
665
|
-
py::module::import("asyncio").attr("Future")();
|
|
666
|
-
|
|
667
1110
|
NapiSafeObjectReference* js_authorizer_reference =
|
|
668
1111
|
self.attr("_js_authorizer")
|
|
669
1112
|
.cast<NapiSafeObjectReference*>();
|
|
670
1113
|
|
|
671
|
-
|
|
1114
|
+
return PythonFutureFromNodePromise(
|
|
672
1115
|
[js_authorizer_reference,
|
|
673
1116
|
py_reader_context = new py::object(py_reader_context),
|
|
674
1117
|
py_aborted = new py::object(py_aborted),
|
|
675
1118
|
method_name,
|
|
676
1119
|
bytes_state = std::move(bytes_state),
|
|
677
|
-
bytes_request = std::move(bytes_request)
|
|
678
|
-
py_future = new py::object(py_future)](Napi::Env env) {
|
|
1120
|
+
bytes_request = std::move(bytes_request)](Napi::Env env) {
|
|
679
1121
|
std::vector<Napi::Value> js_args;
|
|
680
1122
|
|
|
681
1123
|
Napi::Object js_context =
|
|
@@ -695,63 +1137,18 @@ py::object make_py_authorizer(NapiSafeObjectReference js_authorizer) {
|
|
|
695
1137
|
js_args.push_back(js_bytes_state);
|
|
696
1138
|
js_args.push_back(js_bytes_request);
|
|
697
1139
|
|
|
698
|
-
|
|
699
|
-
js_authorizer.Get("_authorize")
|
|
700
|
-
.As<Napi::Function>()
|
|
701
|
-
.Call(js_authorizer, {js_args})
|
|
702
|
-
.As<Napi::Object>();
|
|
703
|
-
|
|
704
|
-
js_promise
|
|
705
|
-
.Get("then")
|
|
1140
|
+
return js_authorizer.Get("_authorize")
|
|
706
1141
|
.As<Napi::Function>()
|
|
707
|
-
.Call(
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
std::string bytes_decision = uint8array_to_str(
|
|
717
|
-
js_bytes_decision);
|
|
718
|
-
|
|
719
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
720
|
-
[py_future, bytes_decision]() {
|
|
721
|
-
bool cancelled =
|
|
722
|
-
py_future->attr("cancelled")()
|
|
723
|
-
.cast<bool>();
|
|
724
|
-
if (!cancelled) {
|
|
725
|
-
py_future->attr("set_result")(
|
|
726
|
-
py::bytes(bytes_decision));
|
|
727
|
-
}
|
|
728
|
-
delete py_future;
|
|
729
|
-
});
|
|
730
|
-
}),
|
|
731
|
-
Napi::Function::New(
|
|
732
|
-
env,
|
|
733
|
-
[py_future](const Napi::CallbackInfo& info) {
|
|
734
|
-
std::string message = message_from_js_error(
|
|
735
|
-
info[0].As<Napi::Object>());
|
|
736
|
-
|
|
737
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
738
|
-
[py_future,
|
|
739
|
-
message = std::move(message)]() {
|
|
740
|
-
bool cancelled =
|
|
741
|
-
py_future->attr("cancelled")()
|
|
742
|
-
.cast<bool>();
|
|
743
|
-
if (!cancelled) {
|
|
744
|
-
py_future->attr("set_exception")(
|
|
745
|
-
py::module::import("builtins")
|
|
746
|
-
.attr("Exception")(
|
|
747
|
-
message));
|
|
748
|
-
}
|
|
749
|
-
delete py_future;
|
|
750
|
-
});
|
|
751
|
-
})});
|
|
1142
|
+
.Call(js_authorizer, {js_args})
|
|
1143
|
+
.As<Napi::Object>();
|
|
1144
|
+
},
|
|
1145
|
+
[](Napi::Env env, Napi::Value js_bytes_decision) {
|
|
1146
|
+
return uint8array_to_str(
|
|
1147
|
+
js_bytes_decision.As<Napi::Uint8Array>());
|
|
1148
|
+
},
|
|
1149
|
+
[](std::string&& bytes_decision) {
|
|
1150
|
+
return py::bytes(bytes_decision);
|
|
752
1151
|
});
|
|
753
|
-
|
|
754
|
-
return py_future;
|
|
755
1152
|
},
|
|
756
1153
|
py::name("_authorize"),
|
|
757
1154
|
py::arg("method_name"),
|
|
@@ -795,7 +1192,7 @@ using ServicerDetails =
|
|
|
795
1192
|
|
|
796
1193
|
// NOTE: must be called within _Node_.
|
|
797
1194
|
std::vector<std::shared_ptr<ServicerDetails>> make_servicer_details(
|
|
798
|
-
Napi::Env
|
|
1195
|
+
Napi::Env env,
|
|
799
1196
|
const Napi::Array& js_servicers) {
|
|
800
1197
|
std::vector<std::shared_ptr<ServicerDetails>> servicer_details;
|
|
801
1198
|
|
|
@@ -887,13 +1284,10 @@ py::object make_py_user_servicer(
|
|
|
887
1284
|
py_self.attr("_js_servicer_constructor")
|
|
888
1285
|
.cast<NapiSafeFunctionReference*>();
|
|
889
1286
|
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
py_self = new py::object(py_self),
|
|
895
|
-
js_servicer_constructor](Napi::Env env) {
|
|
896
|
-
try {
|
|
1287
|
+
try {
|
|
1288
|
+
return RunCallbackOnNodeEventLoop(
|
|
1289
|
+
[py_self = new py::object(py_self),
|
|
1290
|
+
js_servicer_constructor](Napi::Env env) {
|
|
897
1291
|
Napi::Object js_servicer =
|
|
898
1292
|
js_servicer_constructor->Value(env).New({});
|
|
899
1293
|
|
|
@@ -901,35 +1295,16 @@ py::object make_py_user_servicer(
|
|
|
901
1295
|
// external so that we can call `read()` and `write()`
|
|
902
1296
|
// on it.
|
|
903
1297
|
Napi::External<py::object> js_external_self =
|
|
904
|
-
|
|
905
|
-
env,
|
|
906
|
-
py_self,
|
|
907
|
-
[](Napi::Env, py::object* py_self) {
|
|
908
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
909
|
-
[py_self]() {
|
|
910
|
-
delete py_self;
|
|
911
|
-
});
|
|
912
|
-
});
|
|
1298
|
+
make_napi_external(env, py_self);
|
|
913
1299
|
|
|
914
1300
|
js_servicer
|
|
915
1301
|
.Get("__storeExternal")
|
|
916
1302
|
.As<Napi::Function>()
|
|
917
1303
|
.Call(js_servicer, {js_external_self});
|
|
918
1304
|
|
|
919
|
-
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
.Get("console")
|
|
923
|
-
.As<Napi::Object>()
|
|
924
|
-
.Get("log")
|
|
925
|
-
.As<Napi::Function>()
|
|
926
|
-
.Call({e.Value(), Napi::String::New(env, "\n")});
|
|
927
|
-
promise.set_exception(
|
|
928
|
-
std::make_exception_ptr(std::runtime_error(e.what())));
|
|
929
|
-
}
|
|
930
|
-
});
|
|
931
|
-
try {
|
|
932
|
-
return promise.get_future().get();
|
|
1305
|
+
return NapiSafeObjectReference(js_servicer);
|
|
1306
|
+
},
|
|
1307
|
+
/* warn = */ true);
|
|
933
1308
|
} catch (const std::exception& e) {
|
|
934
1309
|
PyErr_SetString(
|
|
935
1310
|
py::module::import("reboot.aio.servers")
|
|
@@ -951,18 +1326,14 @@ py::object make_py_user_servicer(
|
|
|
951
1326
|
py::object py_aborted,
|
|
952
1327
|
const std::string& json_state,
|
|
953
1328
|
const std::string& json_request) {
|
|
954
|
-
|
|
955
|
-
py::module::import("asyncio").attr("Future")();
|
|
956
|
-
|
|
957
|
-
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
1329
|
+
return PythonFutureFromNodePromise(
|
|
958
1330
|
[&js_servicer_reference,
|
|
959
1331
|
kind,
|
|
960
1332
|
method,
|
|
961
1333
|
py_context = new py::object(py_context),
|
|
962
1334
|
py_aborted = new py::object(py_aborted),
|
|
963
1335
|
json_state,
|
|
964
|
-
json_request
|
|
965
|
-
py_future = new py::object(py_future)](Napi::Env env) {
|
|
1336
|
+
json_request](Napi::Env env) {
|
|
966
1337
|
std::vector<Napi::Value> js_args;
|
|
967
1338
|
|
|
968
1339
|
Napi::Object js_context =
|
|
@@ -976,94 +1347,37 @@ py::object make_py_user_servicer(
|
|
|
976
1347
|
Napi::Object js_servicer =
|
|
977
1348
|
js_servicer_reference.Value(env);
|
|
978
1349
|
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
.Get("_" + method)
|
|
982
|
-
.As<Napi::Function>()
|
|
983
|
-
.Call(js_servicer, js_args)
|
|
984
|
-
.As<Napi::Object>();
|
|
985
|
-
|
|
986
|
-
js_promise
|
|
987
|
-
.Get("then")
|
|
1350
|
+
return js_servicer
|
|
1351
|
+
.Get("_" + method)
|
|
988
1352
|
.As<Napi::Function>()
|
|
989
|
-
.Call(
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
std::string json =
|
|
995
|
-
info[0].As<Napi::String>().Utf8Value();
|
|
996
|
-
|
|
997
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
998
|
-
[py_future, json]() {
|
|
999
|
-
bool cancelled =
|
|
1000
|
-
py_future->attr("cancelled")()
|
|
1001
|
-
.cast<bool>();
|
|
1002
|
-
if (!cancelled) {
|
|
1003
|
-
py_future->attr("set_result")(json);
|
|
1004
|
-
}
|
|
1005
|
-
delete py_future;
|
|
1006
|
-
});
|
|
1007
|
-
}),
|
|
1008
|
-
Napi::Function::New(
|
|
1009
|
-
env,
|
|
1010
|
-
[py_future](const Napi::CallbackInfo& info) {
|
|
1011
|
-
std::string message = message_from_js_error(
|
|
1012
|
-
info[0].As<Napi::Object>());
|
|
1013
|
-
|
|
1014
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
1015
|
-
[py_future,
|
|
1016
|
-
message = std::move(message)]() {
|
|
1017
|
-
bool cancelled =
|
|
1018
|
-
py_future->attr("cancelled")()
|
|
1019
|
-
.cast<bool>();
|
|
1020
|
-
if (!cancelled) {
|
|
1021
|
-
py_future->attr("set_exception")(
|
|
1022
|
-
py::module::import("builtins")
|
|
1023
|
-
.attr("Exception")(
|
|
1024
|
-
message));
|
|
1025
|
-
}
|
|
1026
|
-
delete py_future;
|
|
1027
|
-
});
|
|
1028
|
-
})});
|
|
1353
|
+
.Call(js_servicer, js_args)
|
|
1354
|
+
.As<Napi::Object>();
|
|
1355
|
+
},
|
|
1356
|
+
[](Napi::Env env, Napi::Value value) {
|
|
1357
|
+
return std::string(value.As<Napi::String>().Utf8Value());
|
|
1029
1358
|
});
|
|
1030
|
-
|
|
1031
|
-
return py_future;
|
|
1032
1359
|
});
|
|
1033
1360
|
|
|
1034
1361
|
// Constructs and returns an Authorizer.
|
|
1035
1362
|
attributes["_construct_authorizer"] = py::cpp_function(
|
|
1036
1363
|
[](NapiSafeObjectReference& js_servicer_reference) -> py::object {
|
|
1037
|
-
std::
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1364
|
+
std::optional<NapiSafeObjectReference> js_authorizer;
|
|
1365
|
+
try {
|
|
1366
|
+
js_authorizer = RunCallbackOnNodeEventLoop(
|
|
1367
|
+
[&js_servicer_reference](Napi::Env env)
|
|
1368
|
+
-> std::optional<NapiSafeObjectReference> {
|
|
1041
1369
|
Napi::Object js_servicer = js_servicer_reference.Value(env);
|
|
1042
1370
|
Napi::Value js_authorizer =
|
|
1043
1371
|
js_servicer.Get("_authorizer")
|
|
1044
1372
|
.As<Napi::Function>()
|
|
1045
1373
|
.Call(js_servicer, {});
|
|
1046
1374
|
if (!js_authorizer.IsNull()) {
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
js_authorizer.As<Napi::Object>()));
|
|
1375
|
+
return NapiSafeObjectReference(
|
|
1376
|
+
js_authorizer.As<Napi::Object>());
|
|
1050
1377
|
} else {
|
|
1051
|
-
|
|
1378
|
+
return std::nullopt;
|
|
1052
1379
|
}
|
|
1053
|
-
}
|
|
1054
|
-
env.Global()
|
|
1055
|
-
.Get("console")
|
|
1056
|
-
.As<Napi::Object>()
|
|
1057
|
-
.Get("log")
|
|
1058
|
-
.As<Napi::Function>()
|
|
1059
|
-
.Call({e.Value(), Napi::String::New(env, "\n")});
|
|
1060
|
-
promise.set_exception(
|
|
1061
|
-
std::make_exception_ptr(std::runtime_error(e.what())));
|
|
1062
|
-
}
|
|
1063
|
-
});
|
|
1064
|
-
std::optional<NapiSafeObjectReference> js_authorizer;
|
|
1065
|
-
try {
|
|
1066
|
-
js_authorizer = promise.get_future().get();
|
|
1380
|
+
});
|
|
1067
1381
|
} catch (const std::exception& e) {
|
|
1068
1382
|
PyErr_SetString(
|
|
1069
1383
|
py::module::import("reboot.aio.servers")
|
|
@@ -1156,20 +1470,28 @@ py::object make_py_token_verifier(NapiSafeObjectReference js_token_verifier) {
|
|
|
1156
1470
|
[](py::object self,
|
|
1157
1471
|
py::object py_reader_context,
|
|
1158
1472
|
py::object py_aborted,
|
|
1159
|
-
py::object
|
|
1160
|
-
py::object py_future =
|
|
1161
|
-
py::module::import("asyncio").attr("Future")();
|
|
1162
|
-
|
|
1473
|
+
py::object py_token) {
|
|
1163
1474
|
NapiSafeObjectReference* js_token_verifier_reference =
|
|
1164
1475
|
self.attr("_js_token_verifier")
|
|
1165
1476
|
.cast<NapiSafeObjectReference*>();
|
|
1166
1477
|
|
|
1167
|
-
|
|
1478
|
+
// Converting to 'py::str' involves Python runtime access.
|
|
1479
|
+
// Perform this conversion here, on the Python thread, before entering
|
|
1480
|
+
// Node.
|
|
1481
|
+
std::optional<std::string> token;
|
|
1482
|
+
if (!py_token.is_none()) {
|
|
1483
|
+
token = py::str(py_token);
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
return PythonFutureFromNodePromise(
|
|
1168
1487
|
[js_token_verifier_reference,
|
|
1488
|
+
// We allocate 'py_aborted' and 'py_reader_context' with 'new
|
|
1489
|
+
// py::object' on the Python thread, ensuring they remain valid
|
|
1490
|
+
// when passed into Node. Cleanup is handled later on the Python
|
|
1491
|
+
// thread, see 'make_js_context'.
|
|
1169
1492
|
py_reader_context = new py::object(py_reader_context),
|
|
1170
1493
|
py_aborted = new py::object(py_aborted),
|
|
1171
|
-
token =
|
|
1172
|
-
py_future = new py::object(py_future)](Napi::Env env) {
|
|
1494
|
+
token = std::move(token)](Napi::Env env) {
|
|
1173
1495
|
std::vector<Napi::Value> js_args;
|
|
1174
1496
|
|
|
1175
1497
|
js_args.push_back(
|
|
@@ -1179,90 +1501,49 @@ py::object make_py_token_verifier(NapiSafeObjectReference js_token_verifier) {
|
|
|
1179
1501
|
py_aborted,
|
|
1180
1502
|
"reader"));
|
|
1181
1503
|
|
|
1182
|
-
if (token
|
|
1183
|
-
js_args.push_back(env
|
|
1504
|
+
if (token.has_value()) {
|
|
1505
|
+
js_args.push_back(Napi::String::New(env, *token));
|
|
1184
1506
|
} else {
|
|
1185
|
-
js_args.push_back(
|
|
1186
|
-
Napi::String::New(env, std::string(py::str(*token))));
|
|
1507
|
+
js_args.push_back(env.Undefined());
|
|
1187
1508
|
}
|
|
1188
1509
|
|
|
1189
1510
|
Napi::Object js_token_verifier =
|
|
1190
1511
|
js_token_verifier_reference->Value(env);
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
.As<Napi::Function>()
|
|
1195
|
-
.Call(js_token_verifier, {js_args})
|
|
1196
|
-
.As<Napi::Object>();
|
|
1197
|
-
|
|
1198
|
-
js_promise
|
|
1199
|
-
.Get("then")
|
|
1512
|
+
|
|
1513
|
+
return js_token_verifier
|
|
1514
|
+
.Get("_verifyToken")
|
|
1200
1515
|
.As<Napi::Function>()
|
|
1201
|
-
.Call(
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
.cast<bool>();
|
|
1218
|
-
if (!cancelled) {
|
|
1219
|
-
if (bytes_auth.has_value()) {
|
|
1220
|
-
py_future->attr("set_result")(
|
|
1221
|
-
py::bytes(*bytes_auth));
|
|
1222
|
-
} else {
|
|
1223
|
-
py_future->attr("set_result")(
|
|
1224
|
-
py::none());
|
|
1225
|
-
}
|
|
1226
|
-
}
|
|
1227
|
-
delete py_future;
|
|
1228
|
-
});
|
|
1229
|
-
}),
|
|
1230
|
-
Napi::Function::New(
|
|
1231
|
-
env,
|
|
1232
|
-
[py_future](const Napi::CallbackInfo& info) {
|
|
1233
|
-
std::string message = message_from_js_error(
|
|
1234
|
-
info[0].As<Napi::Object>());
|
|
1235
|
-
|
|
1236
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
1237
|
-
[py_future,
|
|
1238
|
-
message = std::move(message)]() {
|
|
1239
|
-
bool cancelled =
|
|
1240
|
-
py_future->attr("cancelled")()
|
|
1241
|
-
.cast<bool>();
|
|
1242
|
-
if (!cancelled) {
|
|
1243
|
-
py_future->attr("set_exception")(
|
|
1244
|
-
py::module::import("builtins")
|
|
1245
|
-
.attr("Exception")(
|
|
1246
|
-
message));
|
|
1247
|
-
}
|
|
1248
|
-
delete py_future;
|
|
1249
|
-
});
|
|
1250
|
-
})});
|
|
1516
|
+
.Call(js_token_verifier, {js_args})
|
|
1517
|
+
.As<Napi::Object>();
|
|
1518
|
+
},
|
|
1519
|
+
[](Napi::Env env, Napi::Value value) {
|
|
1520
|
+
std::optional<std::string> bytes_auth;
|
|
1521
|
+
if (!value.IsNull()) {
|
|
1522
|
+
bytes_auth = uint8array_to_str(value.As<Napi::Uint8Array>());
|
|
1523
|
+
}
|
|
1524
|
+
return bytes_auth;
|
|
1525
|
+
},
|
|
1526
|
+
[](std::optional<std::string>&& bytes_auth) -> py::object {
|
|
1527
|
+
if (bytes_auth.has_value()) {
|
|
1528
|
+
return py::bytes(*bytes_auth);
|
|
1529
|
+
} else {
|
|
1530
|
+
return py::none();
|
|
1531
|
+
}
|
|
1251
1532
|
});
|
|
1252
|
-
|
|
1253
|
-
return py_future;
|
|
1254
1533
|
},
|
|
1255
1534
|
py::name("_verify_token"),
|
|
1256
1535
|
py::arg("context"),
|
|
1257
1536
|
py::arg("aborted"),
|
|
1258
|
-
py::arg("
|
|
1537
|
+
py::arg("py_token"),
|
|
1259
1538
|
py::is_method(py::none()));
|
|
1260
1539
|
|
|
1261
1540
|
py::object py_parent_class =
|
|
1262
1541
|
py::module::import("reboot.nodejs.python")
|
|
1263
1542
|
.attr("NodeAdaptorTokenVerifier");
|
|
1543
|
+
|
|
1264
1544
|
py::object py_parent_metaclass = py::reinterpret_borrow<py::object>(
|
|
1265
1545
|
(PyObject*) &PyType_Type);
|
|
1546
|
+
|
|
1266
1547
|
py::object py_token_verifier = py_parent_metaclass(
|
|
1267
1548
|
"_NodeAdaptorTokenVerifier",
|
|
1268
1549
|
py::make_tuple(py_parent_class),
|
|
@@ -1283,94 +1564,45 @@ Napi::Value Reboot_up(const Napi::CallbackInfo& info) {
|
|
|
1283
1564
|
|
|
1284
1565
|
py::object* py_reboot = js_external_reboot.Value(info.Env()).Data();
|
|
1285
1566
|
|
|
1286
|
-
|
|
1567
|
+
auto js_external_application = NapiSafeReference(
|
|
1568
|
+
info[1].As<Napi::External<py::object>>());
|
|
1287
1569
|
|
|
1288
|
-
|
|
1289
|
-
if (info[2].IsObject()) {
|
|
1290
|
-
js_token_verifier = NapiSafeReference(info[2].As<Napi::Object>());
|
|
1291
|
-
}
|
|
1570
|
+
py::object* py_application = js_external_application.Value(info.Env()).Data();
|
|
1292
1571
|
|
|
1293
1572
|
bool local_envoy = false;
|
|
1294
|
-
if (!info[
|
|
1295
|
-
local_envoy = info[
|
|
1573
|
+
if (!info[2].IsUndefined()) {
|
|
1574
|
+
local_envoy = info[2].As<Napi::Boolean>();
|
|
1296
1575
|
}
|
|
1297
1576
|
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
auto promise = deferred->Promise();
|
|
1304
|
-
|
|
1305
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
1577
|
+
return NodePromiseFromPythonTask(
|
|
1578
|
+
info.Env(),
|
|
1579
|
+
"Reboot.up(...) in nodejs",
|
|
1580
|
+
{"asyncio", "create_task"},
|
|
1306
1581
|
[js_external_reboot, // Ensures `py_reboot` remains valid.
|
|
1307
1582
|
py_reboot,
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
"name"_a = "Reboot.up(...) in nodejs");
|
|
1331
|
-
|
|
1332
|
-
py_task.attr("add_done_callback")(py::cpp_function(
|
|
1333
|
-
[deferred = std::move(deferred),
|
|
1334
|
-
// NOTE: need to keep a reference to `py_task` so that it
|
|
1335
|
-
// doesn't get destroyed before completing!
|
|
1336
|
-
py_task = new py::object(py_task)](py::object py_future) {
|
|
1337
|
-
py::object py_exception = py_future.attr("exception")();
|
|
1338
|
-
|
|
1339
|
-
std::optional<std::string> application_id;
|
|
1340
|
-
std::optional<std::string> exception;
|
|
1341
|
-
|
|
1342
|
-
if (py_exception.is_none()) {
|
|
1343
|
-
py::str py_application_id =
|
|
1344
|
-
py_future.attr("result")().attr("application_id")();
|
|
1345
|
-
application_id.emplace(py_application_id);
|
|
1346
|
-
} else {
|
|
1347
|
-
py::str py_exception_string = py_exception_str(py_exception);
|
|
1348
|
-
|
|
1349
|
-
exception.emplace(py_exception_string);
|
|
1350
|
-
}
|
|
1351
|
-
|
|
1352
|
-
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
1353
|
-
[deferred = std::move(deferred),
|
|
1354
|
-
application_id = std::move(application_id),
|
|
1355
|
-
exception = std::move(exception)](Napi::Env env) {
|
|
1356
|
-
if (application_id.has_value()) {
|
|
1357
|
-
auto js_application_config = Napi::Object::New(env);
|
|
1358
|
-
js_application_config.Set(
|
|
1359
|
-
"applicationId",
|
|
1360
|
-
*application_id);
|
|
1361
|
-
deferred->Resolve(js_application_config);
|
|
1362
|
-
} else {
|
|
1363
|
-
deferred->Reject(
|
|
1364
|
-
Napi::Error::New(env, *exception).Value());
|
|
1365
|
-
}
|
|
1366
|
-
});
|
|
1367
|
-
|
|
1368
|
-
delete py_task;
|
|
1369
|
-
},
|
|
1370
|
-
py::arg("future")));
|
|
1583
|
+
js_external_application, // Ensures `py_application` remains valid.
|
|
1584
|
+
py_application,
|
|
1585
|
+
local_envoy]() {
|
|
1586
|
+
return py_reboot->attr("up")(
|
|
1587
|
+
py_application,
|
|
1588
|
+
// NOTE: while we support subprocess consensuses
|
|
1589
|
+
// for `rbt dev` and `rbt serve` we do not support
|
|
1590
|
+
// them for tests because we don't have a way to
|
|
1591
|
+
// clone a process like we do with multiprocessing
|
|
1592
|
+
// in Python.
|
|
1593
|
+
"in_process"_a = true,
|
|
1594
|
+
"local_envoy"_a = local_envoy);
|
|
1595
|
+
},
|
|
1596
|
+
[](py::object py_revision) {
|
|
1597
|
+
py::str py_application_id =
|
|
1598
|
+
py_revision.attr("config").attr("application_id")();
|
|
1599
|
+
return std::string(py_application_id);
|
|
1600
|
+
},
|
|
1601
|
+
[](Napi::Env env, std::string&& application_id) {
|
|
1602
|
+
Napi::Object js_revision = Napi::Object::New(env);
|
|
1603
|
+
js_revision.Set("applicationId", application_id);
|
|
1604
|
+
return js_revision;
|
|
1371
1605
|
});
|
|
1372
|
-
|
|
1373
|
-
return promise;
|
|
1374
1606
|
}
|
|
1375
1607
|
|
|
1376
1608
|
void Reboot_down(const Napi::CallbackInfo& info) {
|
|
@@ -1386,60 +1618,24 @@ Napi::Value Reboot_start(const Napi::CallbackInfo& info) {
|
|
|
1386
1618
|
|
|
1387
1619
|
// CHECK(js_external_reboot.CheckTypeTag(&reboot_aio_tests_Reboot));
|
|
1388
1620
|
|
|
1389
|
-
py::object* py_reboot = js_external_reboot.Value(info.Env()).Data();
|
|
1390
|
-
|
|
1391
|
-
// Call Ref() on our NAPI thread safe function so that it can't be cleaned up
|
|
1392
|
-
// until Unref() is called in `Reboot_stop()`.
|
|
1393
|
-
// See:
|
|
1394
|
-
// (check_line_length skip)
|
|
1395
|
-
// https://nodejs.github.io/node-addon-examples/special-topics/thread-safe-functions/#q-my-application-isnt-exiting-correctly-it-just-hangs.
|
|
1396
|
-
// Note that Ref() and Unref() will set and unset an "is_referenced" boolean
|
|
1397
|
-
// flag, rather than incrementing and decrementing a reference count.
|
|
1398
|
-
adaptor->thread_safe_function.Ref(info.Env());
|
|
1399
|
-
|
|
1400
|
-
auto deferred = std::make_shared<Napi::Promise::Deferred>(info.Env());
|
|
1401
|
-
|
|
1402
|
-
auto promise = deferred->Promise();
|
|
1403
|
-
|
|
1404
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
1405
|
-
[py_reboot, deferred = std::move(deferred)]() {
|
|
1406
|
-
py::object py_task =
|
|
1407
|
-
py::module::import("asyncio").attr("create_task")(
|
|
1408
|
-
py_reboot->attr("start")(),
|
|
1409
|
-
"name"_a = "Reboot.start() in nodejs");
|
|
1410
|
-
|
|
1411
|
-
py_task.attr("add_done_callback")(py::cpp_function(
|
|
1412
|
-
[deferred = std::move(deferred),
|
|
1413
|
-
// NOTE: need to keep a reference to `py_task` so that it
|
|
1414
|
-
// doesn't get destroyed before completing!
|
|
1415
|
-
py_task = new py::object(py_task)](py::object py_future) {
|
|
1416
|
-
py::object py_exception = py_future.attr("exception")();
|
|
1417
|
-
|
|
1418
|
-
std::optional<std::string> exception;
|
|
1419
|
-
|
|
1420
|
-
if (!py_exception.is_none()) {
|
|
1421
|
-
py::str py_exception_string = py_exception_str(py_exception);
|
|
1422
|
-
|
|
1423
|
-
exception.emplace(py_exception_string);
|
|
1424
|
-
}
|
|
1621
|
+
py::object* py_reboot = js_external_reboot.Value(info.Env()).Data();
|
|
1425
1622
|
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
}
|
|
1435
|
-
});
|
|
1623
|
+
// Call Ref() on our NAPI thread safe function so that it can't be cleaned up
|
|
1624
|
+
// until Unref() is called in `Reboot_stop()`.
|
|
1625
|
+
// See:
|
|
1626
|
+
// (check_line_length skip)
|
|
1627
|
+
// https://nodejs.github.io/node-addon-examples/special-topics/thread-safe-functions/#q-my-application-isnt-exiting-correctly-it-just-hangs.
|
|
1628
|
+
// Note that Ref() and Unref() will set and unset an "is_referenced" boolean
|
|
1629
|
+
// flag, rather than incrementing and decrementing a reference count.
|
|
1630
|
+
adaptor->thread_safe_function.Ref(info.Env());
|
|
1436
1631
|
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1632
|
+
return NodePromiseFromPythonTask(
|
|
1633
|
+
info.Env(),
|
|
1634
|
+
"Reboot.start() in nodejs",
|
|
1635
|
+
{"asyncio", "create_task"},
|
|
1636
|
+
[py_reboot]() {
|
|
1637
|
+
return py_reboot->attr("start")();
|
|
1440
1638
|
});
|
|
1441
|
-
|
|
1442
|
-
return promise;
|
|
1443
1639
|
}
|
|
1444
1640
|
|
|
1445
1641
|
Napi::Value Reboot_stop(const Napi::CallbackInfo& info) {
|
|
@@ -1453,58 +1649,34 @@ Napi::Value Reboot_stop(const Napi::CallbackInfo& info) {
|
|
|
1453
1649
|
|
|
1454
1650
|
py::object* py_reboot = js_external_reboot.Value(info.Env()).Data();
|
|
1455
1651
|
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
py::module::import("asyncio").attr("create_task")(
|
|
1464
|
-
py_reboot->attr("stop")(),
|
|
1465
|
-
"name"_a = "Reboot.stop() in nodejs");
|
|
1466
|
-
|
|
1467
|
-
py_task.attr("add_done_callback")(py::cpp_function(
|
|
1468
|
-
[deferred = std::move(deferred),
|
|
1469
|
-
// NOTE: need to keep a reference to `py_task` so that it
|
|
1470
|
-
// doesn't get destroyed before completing!
|
|
1471
|
-
py_task = new py::object(py_task)](py::object py_future) {
|
|
1472
|
-
py::object py_exception = py_future.attr("exception")();
|
|
1473
|
-
|
|
1474
|
-
std::optional<std::string> exception;
|
|
1475
|
-
|
|
1476
|
-
if (!py_exception.is_none()) {
|
|
1477
|
-
py::str py_exception_string = py_exception_str(py_exception);
|
|
1478
|
-
|
|
1479
|
-
exception.emplace(py_exception_string);
|
|
1480
|
-
}
|
|
1481
|
-
|
|
1482
|
-
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
1483
|
-
[deferred = std::move(deferred),
|
|
1484
|
-
exception = std::move(exception)](Napi::Env env) {
|
|
1485
|
-
if (!exception.has_value()) {
|
|
1486
|
-
deferred->Resolve(env.Null());
|
|
1487
|
-
} else {
|
|
1488
|
-
deferred->Reject(
|
|
1489
|
-
Napi::Error::New(env, *exception).Value());
|
|
1490
|
-
}
|
|
1491
|
-
// Call Unref() on our NAPI thread safe function so that the
|
|
1492
|
-
// object can be destroyed and the event loop can exit.
|
|
1493
|
-
// See:
|
|
1494
|
-
// (check_line_length skip)
|
|
1495
|
-
// https://nodejs.github.io/node-addon-examples/special-topics/thread-safe-functions/#q-my-application-isnt-exiting-correctly-it-just-hangs.
|
|
1496
|
-
// Note that Ref() and Unref() will set and unset an
|
|
1497
|
-
// "is_referenced" boolean flag, rather than incrementing
|
|
1498
|
-
// and decrementing a reference count.
|
|
1499
|
-
adaptor->thread_safe_function.Unref(env);
|
|
1500
|
-
});
|
|
1652
|
+
Napi::Promise js_promise = NodePromiseFromPythonTask(
|
|
1653
|
+
info.Env(),
|
|
1654
|
+
"Reboot.stop() in nodejs",
|
|
1655
|
+
{"asyncio", "create_task"},
|
|
1656
|
+
[py_reboot]() {
|
|
1657
|
+
return py_reboot->attr("stop")();
|
|
1658
|
+
});
|
|
1501
1659
|
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1660
|
+
// Call Unref() on our NAPI thread safe function so that the
|
|
1661
|
+
// object can be destroyed and the event loop can exit.
|
|
1662
|
+
// See:
|
|
1663
|
+
// (check_line_length skip)
|
|
1664
|
+
// https://nodejs.github.io/node-addon-examples/special-topics/thread-safe-functions/#q-my-application-isnt-exiting-correctly-it-just-hangs.
|
|
1665
|
+
// Note that Ref() and Unref() will set and unset an
|
|
1666
|
+
// "is_referenced" boolean flag, rather than incrementing
|
|
1667
|
+
// and decrementing a reference count.
|
|
1668
|
+
auto js_resolve_reject = Napi::Function::New(
|
|
1669
|
+
info.Env(),
|
|
1670
|
+
[](const Napi::CallbackInfo& info) {
|
|
1671
|
+
adaptor->thread_safe_function.Unref(info.Env());
|
|
1505
1672
|
});
|
|
1506
1673
|
|
|
1507
|
-
|
|
1674
|
+
js_promise
|
|
1675
|
+
.Get("then")
|
|
1676
|
+
.As<Napi::Function>()
|
|
1677
|
+
.Call(js_promise, {js_resolve_reject, js_resolve_reject});
|
|
1678
|
+
|
|
1679
|
+
return js_promise;
|
|
1508
1680
|
}
|
|
1509
1681
|
|
|
1510
1682
|
// NOTE: We block on a promise here, so this method should not be called outside
|
|
@@ -1520,15 +1692,13 @@ Napi::Value Reboot_url(const Napi::CallbackInfo& info) {
|
|
|
1520
1692
|
|
|
1521
1693
|
py::object* py_reboot = js_external_reboot.Value(info.Env()).Data();
|
|
1522
1694
|
|
|
1523
|
-
std::
|
|
1524
|
-
|
|
1525
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
1526
|
-
[py_reboot, &promise]() {
|
|
1695
|
+
std::string url = RunCallbackOnPythonEventLoop(
|
|
1696
|
+
[py_reboot]() {
|
|
1527
1697
|
py::str url = py_reboot->attr("url")();
|
|
1528
|
-
|
|
1698
|
+
return std::string(url);
|
|
1529
1699
|
});
|
|
1530
1700
|
|
|
1531
|
-
return Napi::String::New(info.Env(),
|
|
1701
|
+
return Napi::String::New(info.Env(), url);
|
|
1532
1702
|
}
|
|
1533
1703
|
|
|
1534
1704
|
Napi::Value Service_constructor(const Napi::CallbackInfo& info) {
|
|
@@ -1542,35 +1712,22 @@ Napi::Value Service_constructor(const Napi::CallbackInfo& info) {
|
|
|
1542
1712
|
|
|
1543
1713
|
std::string id = js_args.Get("id").As<Napi::String>().Utf8Value();
|
|
1544
1714
|
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
1548
|
-
[&rbt_module, &node_adaptor, &id, &promise]() {
|
|
1715
|
+
py::object* py_service = RunCallbackOnPythonEventLoop(
|
|
1716
|
+
[&rbt_module, &node_adaptor, &id]() {
|
|
1549
1717
|
py::object py_module = py::module::import(rbt_module.c_str());
|
|
1550
1718
|
py::object py_schedule_type =
|
|
1551
1719
|
py_module
|
|
1552
1720
|
.attr(node_adaptor.c_str())
|
|
1553
1721
|
.attr("_Schedule");
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
"schedule_type"_a = py_schedule_type)));
|
|
1722
|
+
return new py::object(py_module.attr(node_adaptor.c_str())(
|
|
1723
|
+
// The call will stay within the same application.
|
|
1724
|
+
"application_id"_a = py::none(),
|
|
1725
|
+
"state_id"_a = id,
|
|
1726
|
+
"schedule_type"_a = py_schedule_type));
|
|
1560
1727
|
});
|
|
1561
1728
|
|
|
1562
|
-
py::object* py_service = promise.get_future().get();
|
|
1563
|
-
|
|
1564
1729
|
Napi::External<py::object> js_external_service =
|
|
1565
|
-
|
|
1566
|
-
info.Env(),
|
|
1567
|
-
py_service,
|
|
1568
|
-
[](Napi::Env, py::object* py_service) {
|
|
1569
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
1570
|
-
[py_service]() {
|
|
1571
|
-
delete py_service;
|
|
1572
|
-
});
|
|
1573
|
-
});
|
|
1730
|
+
make_napi_external(info.Env(), py_service);
|
|
1574
1731
|
|
|
1575
1732
|
// auto type_tag = MakeTypeTag(module, type);
|
|
1576
1733
|
// js_external_service.TypeTag(&type_tag);
|
|
@@ -1612,23 +1769,20 @@ Napi::Value Service_call(const Napi::CallbackInfo& info) {
|
|
|
1612
1769
|
|
|
1613
1770
|
std::string json_options = js_args.Get("jsonOptions").As<Napi::String>();
|
|
1614
1771
|
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
1772
|
+
return NodePromiseFromPythonTaskWithContext(
|
|
1773
|
+
info.Env(),
|
|
1774
|
+
"servicer._" + kind + "(\"" + method + "\", ...) in nodejs",
|
|
1775
|
+
js_external_context,
|
|
1620
1776
|
[js_external_service, // Ensures `py_service` remains valid.
|
|
1621
1777
|
py_service,
|
|
1622
1778
|
kind,
|
|
1623
1779
|
method,
|
|
1624
1780
|
request_module,
|
|
1625
1781
|
request_type,
|
|
1626
|
-
// Ensures `py_context` remains valid.
|
|
1627
|
-
js_external_context,
|
|
1782
|
+
js_external_context, // Ensures `py_context` remains valid.
|
|
1628
1783
|
py_context,
|
|
1629
1784
|
json_request,
|
|
1630
|
-
json_options
|
|
1631
|
-
deferred = std::move(deferred)]() {
|
|
1785
|
+
json_options]() {
|
|
1632
1786
|
// If a type is a nested message type, we can't import it directly
|
|
1633
1787
|
// as 'from module import Foo.Bar.Baz', instead we need to import the
|
|
1634
1788
|
// top level 'Foo' and call 'Foo.Bar.Baz' on it.
|
|
@@ -1645,57 +1799,19 @@ Napi::Value Service_call(const Napi::CallbackInfo& info) {
|
|
|
1645
1799
|
py_request_type = py_request_type.attr(part.c_str());
|
|
1646
1800
|
}
|
|
1647
1801
|
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
.c_str());
|
|
1661
|
-
|
|
1662
|
-
py_task.attr("add_done_callback")(py::cpp_function(
|
|
1663
|
-
[deferred = std::move(deferred),
|
|
1664
|
-
// NOTE: need to keep a reference to `py_task` so that it
|
|
1665
|
-
// doesn't get destroyed before completing!
|
|
1666
|
-
py_task = new py::object(py_task)](py::object py_future) {
|
|
1667
|
-
py::object py_exception = py_future.attr("exception")();
|
|
1668
|
-
|
|
1669
|
-
std::optional<std::string> json;
|
|
1670
|
-
std::optional<std::string> exception;
|
|
1671
|
-
|
|
1672
|
-
if (py_exception.is_none()) {
|
|
1673
|
-
py::str py_json = py_future.attr("result")();
|
|
1674
|
-
json.emplace(py_json);
|
|
1675
|
-
} else {
|
|
1676
|
-
py::str py_exception_string = py_exception_str(py_exception);
|
|
1677
|
-
|
|
1678
|
-
exception.emplace(py_exception_string);
|
|
1679
|
-
}
|
|
1680
|
-
|
|
1681
|
-
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
1682
|
-
[deferred = std::move(deferred),
|
|
1683
|
-
json = std::move(json),
|
|
1684
|
-
exception = std::move(exception)](Napi::Env env) {
|
|
1685
|
-
if (json.has_value()) {
|
|
1686
|
-
deferred->Resolve(
|
|
1687
|
-
Napi::String::New(env, *json));
|
|
1688
|
-
} else {
|
|
1689
|
-
deferred->Reject(
|
|
1690
|
-
Napi::Error::New(env, *exception).Value());
|
|
1691
|
-
}
|
|
1692
|
-
});
|
|
1693
|
-
|
|
1694
|
-
delete py_task;
|
|
1695
|
-
}));
|
|
1802
|
+
return py_service->attr(("_" + kind).c_str())(
|
|
1803
|
+
method,
|
|
1804
|
+
py_context,
|
|
1805
|
+
py_request_type,
|
|
1806
|
+
json_request,
|
|
1807
|
+
json_options);
|
|
1808
|
+
},
|
|
1809
|
+
[](py::object py_json) {
|
|
1810
|
+
return py_json.cast<std::string>();
|
|
1811
|
+
},
|
|
1812
|
+
[](Napi::Env env, std::string&& json) {
|
|
1813
|
+
return Napi::String::New(env, json);
|
|
1696
1814
|
});
|
|
1697
|
-
|
|
1698
|
-
return promise;
|
|
1699
1815
|
}
|
|
1700
1816
|
|
|
1701
1817
|
|
|
@@ -1721,71 +1837,32 @@ Napi::Value Task_await(const Napi::CallbackInfo& info) {
|
|
|
1721
1837
|
|
|
1722
1838
|
std::string json_task_id = js_args.Get("jsonTaskId").As<Napi::String>();
|
|
1723
1839
|
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1840
|
+
return NodePromiseFromPythonTaskWithContext(
|
|
1841
|
+
info.Env(),
|
|
1842
|
+
"reboot.nodejs.python.task_await(\""
|
|
1843
|
+
+ state_name + "\", \""
|
|
1844
|
+
+ method + "\", ...) in nodejs",
|
|
1845
|
+
js_external_context,
|
|
1729
1846
|
[rbt_module = std::move(rbt_module),
|
|
1730
1847
|
state_name = std::move(state_name),
|
|
1731
1848
|
method = std::move(method),
|
|
1732
|
-
// Ensures `py_context` remains valid.
|
|
1733
|
-
js_external_context,
|
|
1849
|
+
js_external_context, // Ensures `py_context` remains valid.
|
|
1734
1850
|
py_context,
|
|
1735
|
-
json_task_id
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
.
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
+ state_name + "\", \""
|
|
1750
|
-
+ method + "\", ...) in nodejs")
|
|
1751
|
-
.c_str());
|
|
1752
|
-
py_task.attr("add_done_callback")(py::cpp_function(
|
|
1753
|
-
[deferred = std::move(deferred),
|
|
1754
|
-
// NOTE: need to keep a reference to `py_task` so that it
|
|
1755
|
-
// doesn't get destroyed before completing!
|
|
1756
|
-
py_task = new py::object(py_task)](py::object py_future) {
|
|
1757
|
-
py::object py_exception = py_future.attr("exception")();
|
|
1758
|
-
|
|
1759
|
-
std::optional<std::string> json;
|
|
1760
|
-
std::optional<std::string> exception;
|
|
1761
|
-
|
|
1762
|
-
if (py_exception.is_none()) {
|
|
1763
|
-
py::str py_json = py_future.attr("result")();
|
|
1764
|
-
json.emplace(py_json);
|
|
1765
|
-
} else {
|
|
1766
|
-
py::str py_exception_string = py_exception_str(py_exception);
|
|
1767
|
-
|
|
1768
|
-
exception.emplace(py_exception_string);
|
|
1769
|
-
}
|
|
1770
|
-
|
|
1771
|
-
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
1772
|
-
[deferred = std::move(deferred),
|
|
1773
|
-
json = std::move(json),
|
|
1774
|
-
exception = std::move(exception)](Napi::Env env) {
|
|
1775
|
-
if (json.has_value()) {
|
|
1776
|
-
deferred->Resolve(
|
|
1777
|
-
Napi::String::New(env, *json));
|
|
1778
|
-
} else {
|
|
1779
|
-
deferred->Reject(
|
|
1780
|
-
Napi::Error::New(env, *exception).Value());
|
|
1781
|
-
}
|
|
1782
|
-
});
|
|
1783
|
-
|
|
1784
|
-
delete py_task;
|
|
1785
|
-
}));
|
|
1851
|
+
json_task_id]() {
|
|
1852
|
+
return py::module::import("reboot.nodejs.python")
|
|
1853
|
+
.attr("task_await")(
|
|
1854
|
+
py_context,
|
|
1855
|
+
py::module::import(rbt_module.c_str())
|
|
1856
|
+
.attr(state_name.c_str()),
|
|
1857
|
+
method,
|
|
1858
|
+
json_task_id);
|
|
1859
|
+
},
|
|
1860
|
+
[](py::object py_json) {
|
|
1861
|
+
return py_json.cast<std::string>();
|
|
1862
|
+
},
|
|
1863
|
+
[](Napi::Env env, std::string&& json) {
|
|
1864
|
+
return Napi::String::New(env, json);
|
|
1786
1865
|
});
|
|
1787
|
-
|
|
1788
|
-
return promise;
|
|
1789
1866
|
}
|
|
1790
1867
|
|
|
1791
1868
|
Napi::Value ExternalContext_constructor(const Napi::CallbackInfo& info) {
|
|
@@ -1819,11 +1896,8 @@ Napi::Value ExternalContext_constructor(const Napi::CallbackInfo& info) {
|
|
|
1819
1896
|
.Utf8Value();
|
|
1820
1897
|
}
|
|
1821
1898
|
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
1825
|
-
[&promise,
|
|
1826
|
-
&name,
|
|
1899
|
+
py::object* py_external_context = RunCallbackOnPythonEventLoop(
|
|
1900
|
+
[&name,
|
|
1827
1901
|
&url,
|
|
1828
1902
|
&bearer_token,
|
|
1829
1903
|
&idempotency_seed,
|
|
@@ -1833,8 +1907,7 @@ Napi::Value ExternalContext_constructor(const Napi::CallbackInfo& info) {
|
|
|
1833
1907
|
"reboot.aio.external");
|
|
1834
1908
|
|
|
1835
1909
|
auto convert_str =
|
|
1836
|
-
[](
|
|
1837
|
-
const std::optional<std::string>& optional) -> py::object {
|
|
1910
|
+
[](const std::optional<std::string>& optional) -> py::object {
|
|
1838
1911
|
if (optional.has_value()) {
|
|
1839
1912
|
return py::str(*optional);
|
|
1840
1913
|
} else {
|
|
@@ -1842,30 +1915,20 @@ Napi::Value ExternalContext_constructor(const Napi::CallbackInfo& info) {
|
|
|
1842
1915
|
}
|
|
1843
1916
|
};
|
|
1844
1917
|
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
idempotency_required_reason))));
|
|
1918
|
+
return new py::object(py_external.attr("ExternalContext")(
|
|
1919
|
+
"name"_a = py::str(name),
|
|
1920
|
+
"url"_a = convert_str(url),
|
|
1921
|
+
"bearer_token"_a = convert_str(bearer_token),
|
|
1922
|
+
"idempotency_seed"_a = convert_str(idempotency_seed),
|
|
1923
|
+
"idempotency_required"_a = py::bool_(idempotency_required),
|
|
1924
|
+
"idempotency_required_reason"_a = convert_str(
|
|
1925
|
+
idempotency_required_reason)));
|
|
1854
1926
|
});
|
|
1855
|
-
py::object* py_external_context = promise.get_future().get();
|
|
1856
1927
|
|
|
1857
|
-
Napi::External<py::object> js_external_context =
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
[](Napi::Env, py::object* py_external_context) {
|
|
1862
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
1863
|
-
[py_external_context]() {
|
|
1864
|
-
delete py_external_context;
|
|
1865
|
-
});
|
|
1866
|
-
});
|
|
1867
|
-
|
|
1868
|
-
js_external_context.TypeTag(&reboot_aio_external_ExternalContext);
|
|
1928
|
+
Napi::External<py::object> js_external_context = make_napi_external(
|
|
1929
|
+
info.Env(),
|
|
1930
|
+
py_external_context,
|
|
1931
|
+
&reboot_aio_external_ExternalContext);
|
|
1869
1932
|
|
|
1870
1933
|
return js_external_context;
|
|
1871
1934
|
}
|
|
@@ -1876,62 +1939,190 @@ Napi::Value Application_constructor(const Napi::CallbackInfo& info) {
|
|
|
1876
1939
|
|
|
1877
1940
|
Napi::Array js_servicers = info[1].As<Napi::Array>();
|
|
1878
1941
|
|
|
1879
|
-
|
|
1880
|
-
|
|
1942
|
+
Napi::Object js_web_framework = info[2].As<Napi::Object>();
|
|
1943
|
+
|
|
1944
|
+
auto js_web_framework_start = NapiSafeFunctionReference(
|
|
1945
|
+
js_web_framework.Get("start").As<Napi::Function>());
|
|
1946
|
+
|
|
1947
|
+
auto js_web_framework_stop = NapiSafeFunctionReference(
|
|
1948
|
+
js_web_framework.Get("stop").As<Napi::Function>());
|
|
1949
|
+
|
|
1950
|
+
auto servicer_details = make_servicer_details(info.Env(), js_servicers);
|
|
1881
1951
|
|
|
1882
1952
|
auto js_initialize = NapiSafeFunctionReference(
|
|
1883
|
-
info[
|
|
1953
|
+
info[3].As<Napi::Function>());
|
|
1884
1954
|
|
|
1885
1955
|
std::optional<std::string> initialize_bearer_token;
|
|
1886
|
-
if (!info[
|
|
1887
|
-
initialize_bearer_token = info[
|
|
1956
|
+
if (!info[4].IsUndefined()) {
|
|
1957
|
+
initialize_bearer_token = info[4].As<Napi::String>().Utf8Value();
|
|
1888
1958
|
}
|
|
1889
1959
|
|
|
1890
1960
|
std::optional<NapiSafeObjectReference> js_token_verifier;
|
|
1891
|
-
if (!info[
|
|
1892
|
-
js_token_verifier = NapiSafeReference(info[
|
|
1961
|
+
if (!info[5].IsUndefined()) {
|
|
1962
|
+
js_token_verifier = NapiSafeReference(info[5].As<Napi::Object>());
|
|
1893
1963
|
}
|
|
1894
1964
|
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
servicer_details = std::move(servicer_details),
|
|
1965
|
+
py::object* py_application = RunCallbackOnPythonEventLoop(
|
|
1966
|
+
[servicer_details = std::move(servicer_details),
|
|
1967
|
+
js_web_framework_start = std::move(js_web_framework_start),
|
|
1968
|
+
js_web_framework_stop = std::move(js_web_framework_stop),
|
|
1900
1969
|
initialize_bearer_token = std::move(initialize_bearer_token),
|
|
1901
1970
|
js_initialize = std::move(js_initialize),
|
|
1902
1971
|
js_token_verifier,
|
|
1903
1972
|
js_from_native_external = std::move(js_from_native_external)]() {
|
|
1904
1973
|
py::list py_servicers = make_py_servicers(servicer_details);
|
|
1905
1974
|
|
|
1975
|
+
py::object py_web_framework_start = py::cpp_function(
|
|
1976
|
+
[js_web_framework_start = std::move(js_web_framework_start),
|
|
1977
|
+
js_from_native_external /* Need a copy because it is shared. */](
|
|
1978
|
+
std::string consensus_id,
|
|
1979
|
+
py::object py_port,
|
|
1980
|
+
py::object py_channel_manager) {
|
|
1981
|
+
std::optional<int> port;
|
|
1982
|
+
if (!py_port.is_none()) {
|
|
1983
|
+
port = py_port.cast<int>();
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
return PythonFutureFromNodePromise(
|
|
1987
|
+
[js_web_framework_start = std::move(js_web_framework_start),
|
|
1988
|
+
js_from_native_external = std::move(js_from_native_external),
|
|
1989
|
+
consensus_id = std::move(consensus_id),
|
|
1990
|
+
port,
|
|
1991
|
+
py_channel_manager = new py::object(py_channel_manager)](
|
|
1992
|
+
Napi::Env env) mutable {
|
|
1993
|
+
Napi::External<py::object> js_external_channel_manager =
|
|
1994
|
+
make_napi_external(env, py_channel_manager);
|
|
1995
|
+
|
|
1996
|
+
Napi::Function js_create_external_context =
|
|
1997
|
+
Napi::Function::New(
|
|
1998
|
+
env,
|
|
1999
|
+
[js_from_native_external = std::move(
|
|
2000
|
+
js_from_native_external),
|
|
2001
|
+
// Ensures `py_channel_manager` remains valid.
|
|
2002
|
+
js_external_channel_manager = NapiSafeReference(
|
|
2003
|
+
js_external_channel_manager)](
|
|
2004
|
+
const Napi::CallbackInfo& info) mutable {
|
|
2005
|
+
Napi::Object js_args = info[0].As<Napi::Object>();
|
|
2006
|
+
|
|
2007
|
+
std::string name =
|
|
2008
|
+
js_args
|
|
2009
|
+
.Get("name")
|
|
2010
|
+
.As<Napi::String>()
|
|
2011
|
+
.Utf8Value();
|
|
2012
|
+
|
|
2013
|
+
std::optional<std::string> bearer_token;
|
|
2014
|
+
if (!js_args.Get("bearerToken").IsUndefined()) {
|
|
2015
|
+
bearer_token =
|
|
2016
|
+
js_args.Get("bearerToken")
|
|
2017
|
+
.As<Napi::String>()
|
|
2018
|
+
.Utf8Value();
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2021
|
+
py::object* py_channel_manager =
|
|
2022
|
+
js_external_channel_manager
|
|
2023
|
+
.Value(info.Env())
|
|
2024
|
+
.Data();
|
|
2025
|
+
|
|
2026
|
+
return NodePromiseFromPythonCallback(
|
|
2027
|
+
info.Env(),
|
|
2028
|
+
[js_from_native_external,
|
|
2029
|
+
name = std::move(name),
|
|
2030
|
+
bearer_token = std::move(bearer_token),
|
|
2031
|
+
// Ensures `py_channel_manager`
|
|
2032
|
+
// remains valid. Need a copy
|
|
2033
|
+
// because
|
|
2034
|
+
// `js_create_external_context` may
|
|
2035
|
+
// get called more than once.
|
|
2036
|
+
js_external_channel_manager,
|
|
2037
|
+
py_channel_manager]() mutable {
|
|
2038
|
+
py::object py_bearer_token = py::none();
|
|
2039
|
+
if (bearer_token.has_value()) {
|
|
2040
|
+
py_bearer_token = py::str(*bearer_token);
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
return new py::object(
|
|
2044
|
+
py::module::import(
|
|
2045
|
+
"reboot.aio.external")
|
|
2046
|
+
.attr("ExternalContext")(
|
|
2047
|
+
"name"_a = py::str(name),
|
|
2048
|
+
"channel_manager"_a =
|
|
2049
|
+
py_channel_manager,
|
|
2050
|
+
"bearer_token"_a =
|
|
2051
|
+
py_bearer_token));
|
|
2052
|
+
},
|
|
2053
|
+
[js_from_native_external = std::move(
|
|
2054
|
+
js_from_native_external)](
|
|
2055
|
+
Napi::Env env,
|
|
2056
|
+
py::object* py_context) {
|
|
2057
|
+
// (check_line_length skip)
|
|
2058
|
+
Napi::External<py::object> js_external_context =
|
|
2059
|
+
make_napi_external(
|
|
2060
|
+
env,
|
|
2061
|
+
py_context,
|
|
2062
|
+
// (check_line_length skip)
|
|
2063
|
+
&reboot_aio_external_ExternalContext);
|
|
2064
|
+
|
|
2065
|
+
return js_from_native_external
|
|
2066
|
+
.Value(env)
|
|
2067
|
+
.Call(
|
|
2068
|
+
env.Global(),
|
|
2069
|
+
{js_external_context});
|
|
2070
|
+
});
|
|
2071
|
+
});
|
|
2072
|
+
|
|
2073
|
+
return js_web_framework_start
|
|
2074
|
+
.Value(env)
|
|
2075
|
+
.Call(
|
|
2076
|
+
env.Global(),
|
|
2077
|
+
{Napi::String::New(env, consensus_id),
|
|
2078
|
+
port.has_value()
|
|
2079
|
+
? Napi::Number::New(env, *port)
|
|
2080
|
+
: env.Null(),
|
|
2081
|
+
js_create_external_context})
|
|
2082
|
+
.As<Napi::Object>();
|
|
2083
|
+
},
|
|
2084
|
+
[](Napi::Env env, Napi::Value value) {
|
|
2085
|
+
Napi::Number js_port = value.As<Napi::Number>();
|
|
2086
|
+
return js_port.Int32Value();
|
|
2087
|
+
});
|
|
2088
|
+
});
|
|
2089
|
+
|
|
2090
|
+
py::object py_web_framework_stop = py::cpp_function(
|
|
2091
|
+
[js_web_framework_stop = std::move(js_web_framework_stop)](
|
|
2092
|
+
std::string consensus_id) {
|
|
2093
|
+
return PythonFutureFromNodePromise(
|
|
2094
|
+
[js_web_framework_stop = std::move(js_web_framework_stop),
|
|
2095
|
+
consensus_id = std::move(consensus_id)](
|
|
2096
|
+
Napi::Env env) mutable {
|
|
2097
|
+
return js_web_framework_stop
|
|
2098
|
+
.Value(env)
|
|
2099
|
+
.Call(
|
|
2100
|
+
env.Global(),
|
|
2101
|
+
{Napi::String::New(env, consensus_id)})
|
|
2102
|
+
.As<Napi::Object>();
|
|
2103
|
+
});
|
|
2104
|
+
});
|
|
2105
|
+
|
|
1906
2106
|
py::object py_initialize = py::cpp_function(
|
|
1907
2107
|
[js_initialize = std::move(js_initialize),
|
|
1908
|
-
js_from_native_external
|
|
2108
|
+
js_from_native_external /* Need a copy because it is shared. */](
|
|
1909
2109
|
py::object py_context) mutable {
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
py_context = new py::object(py_context)
|
|
1920
|
-
py_future = new py::object(py_future)](
|
|
2110
|
+
return PythonFutureFromNodePromise(
|
|
2111
|
+
[js_initialize,
|
|
2112
|
+
js_from_native_external, // NOTE: need a _copy_ of
|
|
2113
|
+
// both `js_initialize`
|
|
2114
|
+
// and
|
|
2115
|
+
// `js_from_native_external`
|
|
2116
|
+
// here since
|
|
2117
|
+
// `py_initialize` may be
|
|
2118
|
+
// called more than once!
|
|
2119
|
+
py_context = new py::object(py_context)](
|
|
1921
2120
|
Napi::Env env) mutable {
|
|
1922
2121
|
Napi::External<py::object> js_external_context =
|
|
1923
|
-
|
|
2122
|
+
make_napi_external(
|
|
1924
2123
|
env,
|
|
1925
2124
|
py_context,
|
|
1926
|
-
|
|
1927
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
1928
|
-
[py_context]() {
|
|
1929
|
-
delete py_context;
|
|
1930
|
-
});
|
|
1931
|
-
});
|
|
1932
|
-
|
|
1933
|
-
js_external_context.TypeTag(
|
|
1934
|
-
&reboot_aio_external_ExternalContext);
|
|
2125
|
+
&reboot_aio_external_ExternalContext);
|
|
1935
2126
|
|
|
1936
2127
|
Napi::Object js_context =
|
|
1937
2128
|
js_from_native_external
|
|
@@ -1941,57 +2132,11 @@ Napi::Value Application_constructor(const Napi::CallbackInfo& info) {
|
|
|
1941
2132
|
{js_external_context})
|
|
1942
2133
|
.As<Napi::Object>();
|
|
1943
2134
|
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
.As<Napi::Object>();
|
|
1949
|
-
|
|
1950
|
-
js_promise
|
|
1951
|
-
.Get("then")
|
|
1952
|
-
.As<Napi::Function>()
|
|
1953
|
-
.Call(
|
|
1954
|
-
js_promise,
|
|
1955
|
-
{Napi::Function::New(
|
|
1956
|
-
env,
|
|
1957
|
-
[py_future](const Napi::CallbackInfo& info) {
|
|
1958
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
1959
|
-
[py_future]() {
|
|
1960
|
-
bool cancelled =
|
|
1961
|
-
py_future
|
|
1962
|
-
->attr("cancelled")()
|
|
1963
|
-
.cast<bool>();
|
|
1964
|
-
if (!cancelled) {
|
|
1965
|
-
py_future->attr("set_result")(
|
|
1966
|
-
py::none());
|
|
1967
|
-
}
|
|
1968
|
-
delete py_future;
|
|
1969
|
-
});
|
|
1970
|
-
}),
|
|
1971
|
-
Napi::Function::New(
|
|
1972
|
-
env,
|
|
1973
|
-
[py_future](const Napi::CallbackInfo& info) {
|
|
1974
|
-
std::string message = message_from_js_error(
|
|
1975
|
-
info[0].As<Napi::Object>());
|
|
1976
|
-
|
|
1977
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
1978
|
-
[py_future,
|
|
1979
|
-
message = std::move(message)]() {
|
|
1980
|
-
bool cancelled =
|
|
1981
|
-
py_future->attr("cancelled")()
|
|
1982
|
-
.cast<bool>();
|
|
1983
|
-
if (!cancelled) {
|
|
1984
|
-
py_future->attr("set_exception")(
|
|
1985
|
-
py::module::import("builtins")
|
|
1986
|
-
.attr("Exception")(
|
|
1987
|
-
message));
|
|
1988
|
-
}
|
|
1989
|
-
delete py_future;
|
|
1990
|
-
});
|
|
1991
|
-
})});
|
|
2135
|
+
return js_initialize
|
|
2136
|
+
.Value(env)
|
|
2137
|
+
.Call(env.Global(), {js_context})
|
|
2138
|
+
.As<Napi::Object>();
|
|
1992
2139
|
});
|
|
1993
|
-
|
|
1994
|
-
return py_future;
|
|
1995
2140
|
});
|
|
1996
2141
|
|
|
1997
2142
|
py::object py_initialize_bearer_token = py::none();
|
|
@@ -2004,44 +2149,22 @@ Napi::Value Application_constructor(const Napi::CallbackInfo& info) {
|
|
|
2004
2149
|
py_token_verifier = make_py_token_verifier(*js_token_verifier);
|
|
2005
2150
|
}
|
|
2006
2151
|
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
// raised exceptions to be handled.
|
|
2019
|
-
//
|
|
2020
|
-
// [](py::error_already_set& e) {
|
|
2021
|
-
|
|
2022
|
-
// },
|
|
2023
|
-
// [](std::exception& e) {
|
|
2024
|
-
|
|
2025
|
-
// },
|
|
2026
|
-
// []() {
|
|
2027
|
-
|
|
2028
|
-
// }
|
|
2029
|
-
);
|
|
2030
|
-
|
|
2031
|
-
py::object* py_application = promise.get_future().get();
|
|
2032
|
-
|
|
2033
|
-
Napi::External<py::object> js_external_application =
|
|
2034
|
-
Napi::External<py::object>::New(
|
|
2035
|
-
info.Env(),
|
|
2036
|
-
py_application,
|
|
2037
|
-
[](Napi::Env, py::object* py_application) {
|
|
2038
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2039
|
-
[py_application]() {
|
|
2040
|
-
delete py_application;
|
|
2041
|
-
});
|
|
2042
|
-
});
|
|
2152
|
+
return new py::object(
|
|
2153
|
+
py::module::import("reboot.aio.applications")
|
|
2154
|
+
.attr("NodeApplication")(
|
|
2155
|
+
"servicers"_a = py_servicers,
|
|
2156
|
+
"web_framework_start"_a = py_web_framework_start,
|
|
2157
|
+
"web_framework_stop"_a = py_web_framework_stop,
|
|
2158
|
+
"initialize"_a = py_initialize,
|
|
2159
|
+
"initialize_bearer_token"_a =
|
|
2160
|
+
py_initialize_bearer_token,
|
|
2161
|
+
"token_verifier"_a = py_token_verifier));
|
|
2162
|
+
});
|
|
2043
2163
|
|
|
2044
|
-
js_external_application
|
|
2164
|
+
Napi::External<py::object> js_external_application = make_napi_external(
|
|
2165
|
+
info.Env(),
|
|
2166
|
+
py_application,
|
|
2167
|
+
&reboot_aio_applications_Application);
|
|
2045
2168
|
|
|
2046
2169
|
return js_external_application;
|
|
2047
2170
|
}
|
|
@@ -2064,58 +2187,45 @@ Napi::Value Application_run(const Napi::CallbackInfo& info) {
|
|
|
2064
2187
|
py::object* py_application =
|
|
2065
2188
|
js_external_application.Value(info.Env()).Data();
|
|
2066
2189
|
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2190
|
+
Napi::Promise js_promise = NodePromiseFromPythonTask(
|
|
2191
|
+
info.Env(),
|
|
2192
|
+
"Application.run() in nodejs",
|
|
2193
|
+
{"reboot.nodejs.python", "create_task"},
|
|
2072
2194
|
[js_external_application, // Ensures `py_application` remains valid.
|
|
2073
|
-
py_application
|
|
2074
|
-
|
|
2075
|
-
py::object py_task =
|
|
2076
|
-
py::module::import("reboot.nodejs.python")
|
|
2077
|
-
.attr("create_task")(
|
|
2078
|
-
py_application->attr("run")(),
|
|
2079
|
-
"name"_a = "Application.run() in nodejs");
|
|
2080
|
-
|
|
2081
|
-
py_task.attr("add_done_callback")(py::cpp_function(
|
|
2082
|
-
[deferred = std::move(deferred),
|
|
2083
|
-
// NOTE: need to keep a reference to `py_task` so that it
|
|
2084
|
-
// doesn't get destroyed before completing!
|
|
2085
|
-
py_task = new py::object(py_task)](py::object py_future) {
|
|
2086
|
-
py::object py_exception = py_future.attr("exception")();
|
|
2087
|
-
|
|
2088
|
-
std::optional<std::string> exception;
|
|
2089
|
-
|
|
2090
|
-
if (!py_exception.is_none()) {
|
|
2091
|
-
py::str py_exception_string = py_exception_str(py_exception);
|
|
2092
|
-
|
|
2093
|
-
exception.emplace(py_exception_string);
|
|
2094
|
-
}
|
|
2095
|
-
|
|
2096
|
-
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
2097
|
-
[deferred = std::move(deferred),
|
|
2098
|
-
exception = std::move(exception)](Napi::Env env) {
|
|
2099
|
-
if (!exception.has_value()) {
|
|
2100
|
-
deferred->Resolve(env.Null());
|
|
2101
|
-
} else {
|
|
2102
|
-
env.Global()
|
|
2103
|
-
.Get("process")
|
|
2104
|
-
.As<Napi::Object>()
|
|
2105
|
-
.Set("exitCode", 1);
|
|
2106
|
-
deferred->Reject(
|
|
2107
|
-
Napi::Error::New(env, *exception).Value());
|
|
2108
|
-
}
|
|
2109
|
-
// When `Application.run` exits, we unref our
|
|
2110
|
-
// thread_safe_function to cause the runtime to exit.
|
|
2111
|
-
adaptor->thread_safe_function.Unref(env);
|
|
2112
|
-
});
|
|
2113
|
-
|
|
2114
|
-
delete py_task;
|
|
2115
|
-
}));
|
|
2195
|
+
py_application]() {
|
|
2196
|
+
return py_application->attr("run")();
|
|
2116
2197
|
});
|
|
2117
2198
|
|
|
2118
|
-
|
|
2199
|
+
// More to do if `Application.run` returns ...
|
|
2200
|
+
js_promise
|
|
2201
|
+
.Get("then")
|
|
2202
|
+
.As<Napi::Function>()
|
|
2203
|
+
.Call(
|
|
2204
|
+
js_promise,
|
|
2205
|
+
{Napi::Function::New(
|
|
2206
|
+
info.Env(),
|
|
2207
|
+
[](const Napi::CallbackInfo& info) {
|
|
2208
|
+
// When `Application.run` exits, we unref our
|
|
2209
|
+
// thread_safe_function to cause the runtime to exit.
|
|
2210
|
+
adaptor->thread_safe_function.Unref(info.Env());
|
|
2211
|
+
}),
|
|
2212
|
+
Napi::Function::New(
|
|
2213
|
+
info.Env(),
|
|
2214
|
+
[](const Napi::CallbackInfo& info) {
|
|
2215
|
+
// There was an error, let's also set the
|
|
2216
|
+
// `process.exitCode` to reflect this.
|
|
2217
|
+
info.Env()
|
|
2218
|
+
.Global()
|
|
2219
|
+
.Get("process")
|
|
2220
|
+
.As<Napi::Object>()
|
|
2221
|
+
.Set("exitCode", 1);
|
|
2222
|
+
|
|
2223
|
+
// When `Application.run` exits, we unref our
|
|
2224
|
+
// thread_safe_function to cause the runtime to exit.
|
|
2225
|
+
adaptor->thread_safe_function.Unref(info.Env());
|
|
2226
|
+
})});
|
|
2227
|
+
|
|
2228
|
+
return js_promise;
|
|
2119
2229
|
}
|
|
2120
2230
|
|
|
2121
2231
|
|
|
@@ -2127,22 +2237,18 @@ Napi::Value Context_auth(const Napi::CallbackInfo& info) {
|
|
|
2127
2237
|
|
|
2128
2238
|
py::object* py_context = js_external_context.Data();
|
|
2129
2239
|
|
|
2130
|
-
std::
|
|
2131
|
-
|
|
2132
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2133
|
-
[py_context, &promise]() {
|
|
2240
|
+
std::optional<std::string> auth_bytes = RunCallbackOnPythonEventLoop(
|
|
2241
|
+
[py_context]() -> std::optional<std::string> {
|
|
2134
2242
|
py::object py_auth = py_context->attr("auth");
|
|
2135
2243
|
|
|
2136
2244
|
if (py_auth.is_none()) {
|
|
2137
|
-
|
|
2245
|
+
return std::nullopt;
|
|
2138
2246
|
} else {
|
|
2139
2247
|
std::string auth_bytes = py::bytes(py_auth.attr("to_proto_bytes")());
|
|
2140
|
-
|
|
2248
|
+
return auth_bytes;
|
|
2141
2249
|
}
|
|
2142
2250
|
});
|
|
2143
2251
|
|
|
2144
|
-
std::optional<std::string> auth_bytes = promise.get_future().get();
|
|
2145
|
-
|
|
2146
2252
|
if (auth_bytes.has_value()) {
|
|
2147
2253
|
Napi::Env env = info.Env();
|
|
2148
2254
|
return str_to_uint8array(env, *auth_bytes);
|
|
@@ -2160,15 +2266,13 @@ Napi::Value Context_stateId(const Napi::CallbackInfo& info) {
|
|
|
2160
2266
|
|
|
2161
2267
|
py::object* py_context = js_external_context.Data();
|
|
2162
2268
|
|
|
2163
|
-
std::
|
|
2164
|
-
|
|
2165
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2166
|
-
[py_context, &promise]() {
|
|
2269
|
+
std::string state_id = RunCallbackOnPythonEventLoop(
|
|
2270
|
+
[py_context]() {
|
|
2167
2271
|
py::str state_id = py_context->attr("state_id");
|
|
2168
|
-
|
|
2272
|
+
return std::string(state_id);
|
|
2169
2273
|
});
|
|
2170
2274
|
|
|
2171
|
-
return Napi::String::New(info.Env(),
|
|
2275
|
+
return Napi::String::New(info.Env(), state_id);
|
|
2172
2276
|
}
|
|
2173
2277
|
|
|
2174
2278
|
|
|
@@ -2180,21 +2284,17 @@ Napi::Value Context_iteration(const Napi::CallbackInfo& info) {
|
|
|
2180
2284
|
|
|
2181
2285
|
py::object* py_context = js_external_context.Data();
|
|
2182
2286
|
|
|
2183
|
-
std::
|
|
2184
|
-
|
|
2185
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2186
|
-
[py_context, &promise]() {
|
|
2287
|
+
std::optional<int> iteration = RunCallbackOnPythonEventLoop(
|
|
2288
|
+
[py_context]() -> std::optional<int> {
|
|
2187
2289
|
py::object iteration = py_context->attr("iteration");
|
|
2188
2290
|
|
|
2189
2291
|
if (iteration.is_none()) {
|
|
2190
|
-
|
|
2292
|
+
return std::nullopt;
|
|
2191
2293
|
} else {
|
|
2192
|
-
|
|
2294
|
+
return iteration.cast<int>();
|
|
2193
2295
|
}
|
|
2194
2296
|
});
|
|
2195
2297
|
|
|
2196
|
-
std::optional<int> iteration = promise.get_future().get();
|
|
2197
|
-
|
|
2198
2298
|
if (iteration.has_value()) {
|
|
2199
2299
|
return Napi::Number::New(info.Env(), *iteration);
|
|
2200
2300
|
} else {
|
|
@@ -2211,15 +2311,30 @@ Napi::Value Context_cookie(const Napi::CallbackInfo& info) {
|
|
|
2211
2311
|
|
|
2212
2312
|
py::object* py_context = js_external_context.Data();
|
|
2213
2313
|
|
|
2214
|
-
std::
|
|
2215
|
-
|
|
2216
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2217
|
-
[py_context, &promise]() {
|
|
2314
|
+
std::string cookie = RunCallbackOnPythonEventLoop(
|
|
2315
|
+
[py_context]() {
|
|
2218
2316
|
py::str cookie = py_context->attr("cookie");
|
|
2219
|
-
|
|
2317
|
+
return std::string(cookie);
|
|
2318
|
+
});
|
|
2319
|
+
|
|
2320
|
+
return Napi::String::New(info.Env(), cookie);
|
|
2321
|
+
}
|
|
2322
|
+
|
|
2323
|
+
|
|
2324
|
+
Napi::Value Context_appInternal(const Napi::CallbackInfo& info) {
|
|
2325
|
+
Napi::External<py::object> js_external_context =
|
|
2326
|
+
info[0].As<Napi::External<py::object>>();
|
|
2327
|
+
|
|
2328
|
+
// CHECK(...CheckTypeTag(...));
|
|
2329
|
+
|
|
2330
|
+
py::object* py_context = js_external_context.Data();
|
|
2331
|
+
|
|
2332
|
+
bool app_internal = RunCallbackOnPythonEventLoop(
|
|
2333
|
+
[py_context]() {
|
|
2334
|
+
return py_context->attr("app_internal").cast<bool>();
|
|
2220
2335
|
});
|
|
2221
2336
|
|
|
2222
|
-
return Napi::
|
|
2337
|
+
return Napi::Boolean::New(info.Env(), app_internal);
|
|
2223
2338
|
}
|
|
2224
2339
|
|
|
2225
2340
|
|
|
@@ -2254,19 +2369,15 @@ Napi::Value Context_generateIdempotentStateId(const Napi::CallbackInfo& info) {
|
|
|
2254
2369
|
alias = js_alias.As<Napi::String>().Utf8Value();
|
|
2255
2370
|
}
|
|
2256
2371
|
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
auto promise = deferred->Promise();
|
|
2260
|
-
|
|
2261
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2372
|
+
return NodePromiseFromPythonCallback(
|
|
2373
|
+
info.Env(),
|
|
2262
2374
|
[js_external_context, // Ensures `py_context` remains valid.
|
|
2263
2375
|
py_context,
|
|
2264
2376
|
state_type = std::move(state_type),
|
|
2265
2377
|
service_name = std::move(service_name),
|
|
2266
2378
|
method = std::move(method),
|
|
2267
2379
|
key = std::move(key),
|
|
2268
|
-
alias = std::move(alias)
|
|
2269
|
-
deferred = std::move(deferred)]() {
|
|
2380
|
+
alias = std::move(alias)]() {
|
|
2270
2381
|
py::object py_key = py::none();
|
|
2271
2382
|
if (key.has_value()) {
|
|
2272
2383
|
py_key = py::cast(*key);
|
|
@@ -2282,34 +2393,20 @@ Napi::Value Context_generateIdempotentStateId(const Napi::CallbackInfo& info) {
|
|
|
2282
2393
|
"key"_a = py_key,
|
|
2283
2394
|
"alias"_a = py_alias));
|
|
2284
2395
|
|
|
2285
|
-
py::object
|
|
2396
|
+
py::object py_generate_idempotent_state_id =
|
|
2286
2397
|
py_context->attr("generate_idempotent_state_id");
|
|
2287
|
-
try {
|
|
2288
|
-
py::object result = generate_idempotent_state_id(
|
|
2289
|
-
state_type,
|
|
2290
|
-
service_name,
|
|
2291
|
-
method,
|
|
2292
|
-
py_idempotency);
|
|
2293
2398
|
|
|
2294
|
-
|
|
2399
|
+
py::object py_state_id = py_generate_idempotent_state_id(
|
|
2400
|
+
state_type,
|
|
2401
|
+
service_name,
|
|
2402
|
+
method,
|
|
2403
|
+
py_idempotency);
|
|
2295
2404
|
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
});
|
|
2301
|
-
} catch (pybind11::error_already_set& e) {
|
|
2302
|
-
std::string exception_message = py_exception_str(e.value());
|
|
2303
|
-
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
2304
|
-
[exception_message = std::move(exception_message),
|
|
2305
|
-
deferred = std::move(deferred)](Napi::Env env) {
|
|
2306
|
-
deferred->Reject(
|
|
2307
|
-
Napi::Error::New(env, exception_message).Value());
|
|
2308
|
-
});
|
|
2309
|
-
}
|
|
2405
|
+
return py_state_id.cast<std::string>();
|
|
2406
|
+
},
|
|
2407
|
+
[](Napi::Env env, std::string&& state_id) {
|
|
2408
|
+
return Napi::String::New(env, state_id);
|
|
2310
2409
|
});
|
|
2311
|
-
|
|
2312
|
-
return promise;
|
|
2313
2410
|
}
|
|
2314
2411
|
|
|
2315
2412
|
|
|
@@ -2324,16 +2421,11 @@ Napi::Value WriterContext_set_sync(const Napi::CallbackInfo& info) {
|
|
|
2324
2421
|
|
|
2325
2422
|
bool sync = info[1].As<Napi::Boolean>();
|
|
2326
2423
|
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2330
|
-
[&py_context, sync, &promise]() {
|
|
2424
|
+
RunCallbackOnPythonEventLoop(
|
|
2425
|
+
[&py_context, sync]() {
|
|
2331
2426
|
py_context->attr("sync") = sync;
|
|
2332
|
-
promise.set_value();
|
|
2333
2427
|
});
|
|
2334
2428
|
|
|
2335
|
-
promise.get_future().get();
|
|
2336
|
-
|
|
2337
2429
|
return info.Env().Undefined();
|
|
2338
2430
|
}
|
|
2339
2431
|
|
|
@@ -2349,121 +2441,34 @@ Napi::Value retry_reactively_until(const Napi::CallbackInfo& info) {
|
|
|
2349
2441
|
auto js_condition = NapiSafeFunctionReference(
|
|
2350
2442
|
info[1].As<Napi::Function>());
|
|
2351
2443
|
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2444
|
+
return NodePromiseFromPythonTaskWithContext(
|
|
2445
|
+
info.Env(),
|
|
2446
|
+
"retry_reactively_until(...) in nodejs",
|
|
2447
|
+
js_external_context,
|
|
2357
2448
|
[js_external_context, // Ensures `py_context` remains valid.
|
|
2358
2449
|
py_context,
|
|
2359
|
-
js_condition = std::move(js_condition)
|
|
2360
|
-
deferred = std::move(deferred)]() {
|
|
2450
|
+
js_condition = std::move(js_condition)]() {
|
|
2361
2451
|
py::object py_condition = py::cpp_function(
|
|
2362
2452
|
[js_condition = std::move(js_condition)]() mutable {
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
.Call(env.Global(), {})
|
|
2377
|
-
.As<Napi::Object>();
|
|
2378
|
-
|
|
2379
|
-
js_promise
|
|
2380
|
-
.Get("then")
|
|
2381
|
-
.As<Napi::Function>()
|
|
2382
|
-
.Call(
|
|
2383
|
-
js_promise,
|
|
2384
|
-
{Napi::Function::New(
|
|
2385
|
-
env,
|
|
2386
|
-
[py_future](const Napi::CallbackInfo& info) {
|
|
2387
|
-
bool result = info[0].As<Napi::Boolean>();
|
|
2388
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2389
|
-
[py_future, result]() {
|
|
2390
|
-
bool cancelled =
|
|
2391
|
-
py_future->attr("cancelled")()
|
|
2392
|
-
.cast<bool>();
|
|
2393
|
-
if (!cancelled) {
|
|
2394
|
-
py_future->attr("set_result")(
|
|
2395
|
-
result);
|
|
2396
|
-
}
|
|
2397
|
-
delete py_future;
|
|
2398
|
-
});
|
|
2399
|
-
}),
|
|
2400
|
-
Napi::Function::New(
|
|
2401
|
-
env,
|
|
2402
|
-
[py_future](const Napi::CallbackInfo& info) {
|
|
2403
|
-
std::string message = message_from_js_error(
|
|
2404
|
-
info[0].As<Napi::Object>());
|
|
2405
|
-
|
|
2406
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2407
|
-
[py_future,
|
|
2408
|
-
message = std::move(message)]() {
|
|
2409
|
-
bool cancelled =
|
|
2410
|
-
py_future->attr("cancelled")()
|
|
2411
|
-
.cast<bool>();
|
|
2412
|
-
if (!cancelled) {
|
|
2413
|
-
py_future->attr("set_exception")(
|
|
2414
|
-
py::module::import("builtins")
|
|
2415
|
-
.attr("Exception")(
|
|
2416
|
-
message));
|
|
2417
|
-
}
|
|
2418
|
-
delete py_future;
|
|
2419
|
-
});
|
|
2420
|
-
})});
|
|
2453
|
+
return PythonFutureFromNodePromise(
|
|
2454
|
+
// NOTE: need a _copy_ of
|
|
2455
|
+
// `js_condition` here since
|
|
2456
|
+
// `py_condition` may be called more
|
|
2457
|
+
// than once!
|
|
2458
|
+
[js_condition](Napi::Env env) mutable {
|
|
2459
|
+
return js_condition
|
|
2460
|
+
.Value(env)
|
|
2461
|
+
.Call(env.Global(), {})
|
|
2462
|
+
.As<Napi::Object>();
|
|
2463
|
+
},
|
|
2464
|
+
[](Napi::Env, Napi::Value value) {
|
|
2465
|
+
return value.As<Napi::Boolean>().Value();
|
|
2421
2466
|
});
|
|
2422
|
-
|
|
2423
|
-
return py_future;
|
|
2424
2467
|
});
|
|
2425
2468
|
|
|
2426
|
-
py::
|
|
2427
|
-
|
|
2428
|
-
.attr("create_task_with_context")(
|
|
2429
|
-
(py::module::import("reboot.aio.contexts")
|
|
2430
|
-
.attr("retry_reactively_until"))(
|
|
2431
|
-
py_context,
|
|
2432
|
-
py_condition),
|
|
2433
|
-
py_context,
|
|
2434
|
-
"name"_a = "retry_reactively_until(...) in nodejs");
|
|
2435
|
-
|
|
2436
|
-
py_task.attr("add_done_callback")(py::cpp_function(
|
|
2437
|
-
[deferred = std::move(deferred),
|
|
2438
|
-
// NOTE: need to keep a reference to `py_task` so that it
|
|
2439
|
-
// doesn't get destroyed before completing!
|
|
2440
|
-
py_task = new py::object(py_task)](py::object py_future) {
|
|
2441
|
-
py::object py_exception = py_future.attr("exception")();
|
|
2442
|
-
|
|
2443
|
-
std::optional<std::string> exception;
|
|
2444
|
-
|
|
2445
|
-
if (!py_exception.is_none()) {
|
|
2446
|
-
py::str py_exception_string = py_exception_str(py_exception);
|
|
2447
|
-
|
|
2448
|
-
exception.emplace(py_exception_string);
|
|
2449
|
-
}
|
|
2450
|
-
|
|
2451
|
-
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
2452
|
-
[deferred = std::move(deferred),
|
|
2453
|
-
exception = std::move(exception)](Napi::Env env) {
|
|
2454
|
-
if (!exception.has_value()) {
|
|
2455
|
-
deferred->Resolve(env.Null());
|
|
2456
|
-
} else {
|
|
2457
|
-
deferred->Reject(
|
|
2458
|
-
Napi::Error::New(env, *exception).Value());
|
|
2459
|
-
}
|
|
2460
|
-
});
|
|
2461
|
-
|
|
2462
|
-
delete py_task;
|
|
2463
|
-
}));
|
|
2469
|
+
return py::module::import("reboot.aio.contexts")
|
|
2470
|
+
.attr("retry_reactively_until")(py_context, py_condition);
|
|
2464
2471
|
});
|
|
2465
|
-
|
|
2466
|
-
return promise;
|
|
2467
2472
|
}
|
|
2468
2473
|
|
|
2469
2474
|
|
|
@@ -2482,132 +2487,45 @@ Napi::Value atLeastOrMostOnce(const Napi::CallbackInfo& info) {
|
|
|
2482
2487
|
|
|
2483
2488
|
bool at_most_once = info[3].As<Napi::Boolean>();
|
|
2484
2489
|
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2490
|
+
return NodePromiseFromPythonTaskWithContext(
|
|
2491
|
+
info.Env(),
|
|
2492
|
+
"memoize(...) in nodejs",
|
|
2493
|
+
js_external_context,
|
|
2490
2494
|
[js_external_context, // Ensures `py_context` remains valid.
|
|
2491
2495
|
py_context,
|
|
2492
2496
|
js_callable = std::move(js_callable),
|
|
2493
2497
|
idempotency_alias = std::move(idempotency_alias),
|
|
2494
|
-
at_most_once
|
|
2495
|
-
deferred = std::move(deferred)]() {
|
|
2498
|
+
at_most_once]() {
|
|
2496
2499
|
py::object py_callable = py::cpp_function(
|
|
2497
2500
|
[js_callable = std::move(js_callable)]() mutable {
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
Napi::
|
|
2509
|
-
js_callable
|
|
2510
|
-
.Value(env)
|
|
2511
|
-
.Call(env.Global(), {})
|
|
2512
|
-
.As<Napi::Object>();
|
|
2513
|
-
|
|
2514
|
-
js_promise
|
|
2515
|
-
.Get("then")
|
|
2516
|
-
.As<Napi::Function>()
|
|
2517
|
-
.Call(
|
|
2518
|
-
js_promise,
|
|
2519
|
-
{Napi::Function::New(
|
|
2520
|
-
env,
|
|
2521
|
-
[py_future](const Napi::CallbackInfo& info) {
|
|
2522
|
-
std::string result =
|
|
2523
|
-
info[0].As<Napi::String>().Utf8Value();
|
|
2524
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2525
|
-
[py_future, result]() {
|
|
2526
|
-
bool cancelled =
|
|
2527
|
-
py_future->attr("cancelled")()
|
|
2528
|
-
.cast<bool>();
|
|
2529
|
-
if (!cancelled) {
|
|
2530
|
-
py_future->attr("set_result")(
|
|
2531
|
-
result);
|
|
2532
|
-
}
|
|
2533
|
-
delete py_future;
|
|
2534
|
-
});
|
|
2535
|
-
}),
|
|
2536
|
-
Napi::Function::New(
|
|
2537
|
-
env,
|
|
2538
|
-
[py_future](const Napi::CallbackInfo& info) {
|
|
2539
|
-
std::string message = message_from_js_error(
|
|
2540
|
-
info[0].As<Napi::Object>());
|
|
2541
|
-
|
|
2542
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2543
|
-
[py_future,
|
|
2544
|
-
message = std::move(message)]() {
|
|
2545
|
-
bool cancelled =
|
|
2546
|
-
py_future->attr("cancelled")()
|
|
2547
|
-
.cast<bool>();
|
|
2548
|
-
if (!cancelled) {
|
|
2549
|
-
py_future->attr("set_exception")(
|
|
2550
|
-
py::module::import("builtins")
|
|
2551
|
-
.attr("Exception")(
|
|
2552
|
-
message));
|
|
2553
|
-
}
|
|
2554
|
-
delete py_future;
|
|
2555
|
-
});
|
|
2556
|
-
})});
|
|
2501
|
+
return PythonFutureFromNodePromise(
|
|
2502
|
+
// NOTE: need a _copy_ of `js_callable` here since
|
|
2503
|
+
// `py_callable` may be called more than once!
|
|
2504
|
+
[js_callable](Napi::Env env) mutable {
|
|
2505
|
+
return js_callable
|
|
2506
|
+
.Value(env)
|
|
2507
|
+
.Call(env.Global(), {})
|
|
2508
|
+
.As<Napi::Object>();
|
|
2509
|
+
},
|
|
2510
|
+
[](Napi::Env, Napi::Value value) {
|
|
2511
|
+
return std::string(value.As<Napi::String>().Utf8Value());
|
|
2557
2512
|
});
|
|
2558
|
-
|
|
2559
|
-
return py_future;
|
|
2560
2513
|
});
|
|
2561
2514
|
|
|
2562
|
-
py::
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
[deferred = std::move(deferred),
|
|
2576
|
-
// NOTE: need to keep a reference to `py_task` so that it
|
|
2577
|
-
// doesn't get destroyed before completing!
|
|
2578
|
-
py_task = new py::object(py_task)](py::object py_future) {
|
|
2579
|
-
py::object py_exception = py_future.attr("exception")();
|
|
2580
|
-
|
|
2581
|
-
std::optional<std::string> json;
|
|
2582
|
-
std::optional<std::string> exception;
|
|
2583
|
-
|
|
2584
|
-
if (py_exception.is_none()) {
|
|
2585
|
-
py::str py_json = py_future.attr("result")();
|
|
2586
|
-
json.emplace(py_json);
|
|
2587
|
-
} else {
|
|
2588
|
-
py::str py_exception_string = py_exception_str(py_exception);
|
|
2589
|
-
|
|
2590
|
-
exception.emplace(py_exception_string);
|
|
2591
|
-
}
|
|
2592
|
-
|
|
2593
|
-
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
2594
|
-
[deferred = std::move(deferred),
|
|
2595
|
-
json = std::move(json),
|
|
2596
|
-
exception = std::move(exception)](Napi::Env env) {
|
|
2597
|
-
if (json.has_value()) {
|
|
2598
|
-
deferred->Resolve(
|
|
2599
|
-
Napi::String::New(env, *json));
|
|
2600
|
-
} else {
|
|
2601
|
-
deferred->Reject(
|
|
2602
|
-
Napi::Error::New(env, *exception).Value());
|
|
2603
|
-
}
|
|
2604
|
-
});
|
|
2605
|
-
|
|
2606
|
-
delete py_task;
|
|
2607
|
-
}));
|
|
2515
|
+
return py::module::import("reboot.aio.memoize")
|
|
2516
|
+
.attr("memoize")(
|
|
2517
|
+
py::str(idempotency_alias),
|
|
2518
|
+
py_context,
|
|
2519
|
+
py_callable,
|
|
2520
|
+
"type_t"_a = py::eval("str"),
|
|
2521
|
+
"at_most_once"_a = at_most_once);
|
|
2522
|
+
},
|
|
2523
|
+
[](py::object py_json) {
|
|
2524
|
+
return py_json.cast<std::string>();
|
|
2525
|
+
},
|
|
2526
|
+
[](Napi::Env env, std::string&& json) {
|
|
2527
|
+
return Napi::String::New(env, json);
|
|
2608
2528
|
});
|
|
2609
|
-
|
|
2610
|
-
return promise;
|
|
2611
2529
|
}
|
|
2612
2530
|
|
|
2613
2531
|
|
|
@@ -2626,60 +2544,22 @@ Napi::Value Servicer_read(const Napi::CallbackInfo& info) {
|
|
|
2626
2544
|
|
|
2627
2545
|
py::object* py_context = js_external_context.Value(info.Env()).Data();
|
|
2628
2546
|
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2547
|
+
return NodePromiseFromPythonTaskWithContext(
|
|
2548
|
+
info.Env(),
|
|
2549
|
+
"servicer._read(...) in nodejs",
|
|
2550
|
+
js_external_context,
|
|
2634
2551
|
[js_external_servicer, // Ensures `py_servicer` remains valid.
|
|
2635
2552
|
py_servicer,
|
|
2636
2553
|
js_external_context, // Ensures `py_context` remains valid.
|
|
2637
|
-
py_context
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
py_task.attr("add_done_callback")(py::cpp_function(
|
|
2647
|
-
[deferred = std::move(deferred),
|
|
2648
|
-
// NOTE: need to keep a reference to `py_task` so that it
|
|
2649
|
-
// doesn't get destroyed before completing!
|
|
2650
|
-
py_task = new py::object(py_task)](py::object py_future) {
|
|
2651
|
-
py::object py_exception = py_future.attr("exception")();
|
|
2652
|
-
|
|
2653
|
-
std::optional<std::string> json;
|
|
2654
|
-
std::optional<std::string> exception;
|
|
2655
|
-
|
|
2656
|
-
if (py_exception.is_none()) {
|
|
2657
|
-
py::str py_json = py_future.attr("result")();
|
|
2658
|
-
json.emplace(py_json);
|
|
2659
|
-
} else {
|
|
2660
|
-
py::str py_exception_string = py_exception_str(py_exception);
|
|
2661
|
-
|
|
2662
|
-
exception.emplace(py_exception_string);
|
|
2663
|
-
}
|
|
2664
|
-
|
|
2665
|
-
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
2666
|
-
[deferred = std::move(deferred),
|
|
2667
|
-
json = std::move(json),
|
|
2668
|
-
exception = std::move(exception)](Napi::Env env) {
|
|
2669
|
-
if (json.has_value()) {
|
|
2670
|
-
deferred->Resolve(
|
|
2671
|
-
Napi::String::New(env, *json));
|
|
2672
|
-
} else {
|
|
2673
|
-
deferred->Reject(
|
|
2674
|
-
Napi::Error::New(env, *exception).Value());
|
|
2675
|
-
}
|
|
2676
|
-
});
|
|
2677
|
-
|
|
2678
|
-
delete py_task;
|
|
2679
|
-
}));
|
|
2554
|
+
py_context]() {
|
|
2555
|
+
return py_servicer->attr("_read")(py_context);
|
|
2556
|
+
},
|
|
2557
|
+
[](py::object py_json) {
|
|
2558
|
+
return py_json.cast<std::string>();
|
|
2559
|
+
},
|
|
2560
|
+
[](Napi::Env env, std::string&& json) {
|
|
2561
|
+
return Napi::String::New(env, json);
|
|
2680
2562
|
});
|
|
2681
|
-
|
|
2682
|
-
return promise;
|
|
2683
2563
|
}
|
|
2684
2564
|
|
|
2685
2565
|
|
|
@@ -2703,135 +2583,48 @@ Napi::Value Servicer_write(const Napi::CallbackInfo& info) {
|
|
|
2703
2583
|
|
|
2704
2584
|
std::string json_options = info[3].As<Napi::String>().Utf8Value();
|
|
2705
2585
|
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2586
|
+
return NodePromiseFromPythonTaskWithContext(
|
|
2587
|
+
info.Env(),
|
|
2588
|
+
"servicer._write(...) in nodejs",
|
|
2589
|
+
js_external_context,
|
|
2711
2590
|
[js_external_servicer, // Ensures `py_servicer` remains valid.
|
|
2712
2591
|
py_servicer,
|
|
2713
2592
|
js_external_context, // Ensures `py_context` remains valid.
|
|
2714
2593
|
py_context,
|
|
2715
2594
|
js_writer = std::move(js_writer),
|
|
2716
|
-
json_options = std::move(json_options)
|
|
2717
|
-
deferred = std::move(deferred)]() {
|
|
2595
|
+
json_options = std::move(json_options)]() {
|
|
2718
2596
|
py::object py_writer = py::cpp_function(
|
|
2719
2597
|
[js_writer = std::move(js_writer)](
|
|
2720
2598
|
std::string state_json) mutable {
|
|
2721
|
-
|
|
2722
|
-
py::module::import("asyncio").attr("Future")();
|
|
2723
|
-
|
|
2724
|
-
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
2599
|
+
return PythonFutureFromNodePromise(
|
|
2725
2600
|
[js_writer, // NOTE: need a _copy_ of
|
|
2726
2601
|
// `js_writer` here since
|
|
2727
2602
|
// `py_writer` may be called more
|
|
2728
2603
|
// than once!
|
|
2729
|
-
state_json
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
Napi::Object js_promise =
|
|
2733
|
-
js_writer
|
|
2734
|
-
.Value(env)
|
|
2735
|
-
.Call(
|
|
2736
|
-
env.Global(),
|
|
2737
|
-
{Napi::String::New(env, state_json)})
|
|
2738
|
-
.As<Napi::Object>();
|
|
2739
|
-
|
|
2740
|
-
js_promise
|
|
2741
|
-
.Get("then")
|
|
2742
|
-
.As<Napi::Function>()
|
|
2604
|
+
state_json](Napi::Env env) mutable {
|
|
2605
|
+
return js_writer
|
|
2606
|
+
.Value(env)
|
|
2743
2607
|
.Call(
|
|
2744
|
-
|
|
2745
|
-
{Napi::
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2751
|
-
[py_future, result]() {
|
|
2752
|
-
bool cancelled =
|
|
2753
|
-
py_future->attr("cancelled")()
|
|
2754
|
-
.cast<bool>();
|
|
2755
|
-
if (!cancelled) {
|
|
2756
|
-
py_future->attr("set_result")(
|
|
2757
|
-
result);
|
|
2758
|
-
}
|
|
2759
|
-
delete py_future;
|
|
2760
|
-
});
|
|
2761
|
-
}),
|
|
2762
|
-
Napi::Function::New(
|
|
2763
|
-
env,
|
|
2764
|
-
[py_future](const Napi::CallbackInfo& info) {
|
|
2765
|
-
std::string message = message_from_js_error(
|
|
2766
|
-
info[0].As<Napi::Object>());
|
|
2767
|
-
|
|
2768
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2769
|
-
[py_future,
|
|
2770
|
-
message = std::move(message)]() {
|
|
2771
|
-
bool cancelled =
|
|
2772
|
-
py_future->attr("cancelled")()
|
|
2773
|
-
.cast<bool>();
|
|
2774
|
-
if (!cancelled) {
|
|
2775
|
-
py_future->attr("set_exception")(
|
|
2776
|
-
py::module::import("builtins")
|
|
2777
|
-
.attr("Exception")(
|
|
2778
|
-
message));
|
|
2779
|
-
}
|
|
2780
|
-
delete py_future;
|
|
2781
|
-
});
|
|
2782
|
-
})});
|
|
2608
|
+
env.Global(),
|
|
2609
|
+
{Napi::String::New(env, state_json)})
|
|
2610
|
+
.As<Napi::Object>();
|
|
2611
|
+
},
|
|
2612
|
+
[](Napi::Env env, Napi::Value value) {
|
|
2613
|
+
return std::string(value.As<Napi::String>().Utf8Value());
|
|
2783
2614
|
});
|
|
2784
|
-
|
|
2785
|
-
return py_future;
|
|
2786
2615
|
});
|
|
2787
2616
|
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
py_task.attr("add_done_callback")(py::cpp_function(
|
|
2799
|
-
[deferred = std::move(deferred),
|
|
2800
|
-
// NOTE: need to keep a reference to `py_task` so that it
|
|
2801
|
-
// doesn't get destroyed before completing!
|
|
2802
|
-
py_task = new py::object(py_task)](py::object py_future) {
|
|
2803
|
-
py::object py_exception = py_future.attr("exception")();
|
|
2804
|
-
|
|
2805
|
-
std::optional<std::string> result;
|
|
2806
|
-
std::optional<std::string> exception;
|
|
2807
|
-
|
|
2808
|
-
if (py_exception.is_none()) {
|
|
2809
|
-
py::str py_result = py_future.attr("result")();
|
|
2810
|
-
result.emplace(py_result);
|
|
2811
|
-
} else {
|
|
2812
|
-
py::str py_exception_string = py_exception_str(py_exception);
|
|
2813
|
-
|
|
2814
|
-
exception.emplace(py_exception_string);
|
|
2815
|
-
}
|
|
2816
|
-
|
|
2817
|
-
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
2818
|
-
[deferred = std::move(deferred),
|
|
2819
|
-
result = std::move(result),
|
|
2820
|
-
exception = std::move(exception)](Napi::Env env) {
|
|
2821
|
-
if (result.has_value()) {
|
|
2822
|
-
deferred->Resolve(
|
|
2823
|
-
Napi::String::New(env, *result));
|
|
2824
|
-
} else {
|
|
2825
|
-
deferred->Reject(
|
|
2826
|
-
Napi::Error::New(env, *exception).Value());
|
|
2827
|
-
}
|
|
2828
|
-
});
|
|
2829
|
-
|
|
2830
|
-
delete py_task;
|
|
2831
|
-
}));
|
|
2617
|
+
return py_servicer->attr("_write")(
|
|
2618
|
+
py_context,
|
|
2619
|
+
py_writer,
|
|
2620
|
+
json_options);
|
|
2621
|
+
},
|
|
2622
|
+
[](py::object py_result) {
|
|
2623
|
+
return py_result.cast<std::string>();
|
|
2624
|
+
},
|
|
2625
|
+
[](Napi::Env env, std::string&& result) {
|
|
2626
|
+
return Napi::String::New(env, result);
|
|
2832
2627
|
});
|
|
2833
|
-
|
|
2834
|
-
return promise;
|
|
2835
2628
|
}
|
|
2836
2629
|
|
|
2837
2630
|
|
|
@@ -2901,6 +2694,10 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) {
|
|
|
2901
2694
|
Napi::String::New(env, "Context_cookie"),
|
|
2902
2695
|
Napi::Function::New<Context_cookie>(env));
|
|
2903
2696
|
|
|
2697
|
+
exports.Set(
|
|
2698
|
+
Napi::String::New(env, "Context_appInternal"),
|
|
2699
|
+
Napi::Function::New<Context_appInternal>(env));
|
|
2700
|
+
|
|
2904
2701
|
exports.Set(
|
|
2905
2702
|
Napi::String::New(env, "Context_generateIdempotentStateId"),
|
|
2906
2703
|
Napi::Function::New<Context_generateIdempotentStateId>(env));
|