@reboot-dev/reboot 0.23.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +94 -19
- package/index.js +306 -42
- package/package.json +7 -4
- package/rbt.js +5 -2
- package/reboot_native.cc +1207 -1383
- package/reboot_native.cjs +3 -1
- package/reboot_native.d.ts +12 -3
- package/secrets/index.d.ts +37 -0
- package/secrets/index.js +96 -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
|
-
);
|
|
424
|
-
|
|
425
|
-
py::object* py_reboot = promise.get_future().get();
|
|
971
|
+
return new py::object(tests.attr("Reboot")());
|
|
972
|
+
});
|
|
426
973
|
|
|
427
|
-
Napi::External<py::object> js_external_reboot =
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
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
|
-
|
|
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,85 +1501,49 @@ py::object make_py_token_verifier(NapiSafeObjectReference js_token_verifier) {
|
|
|
1179
1501
|
py_aborted,
|
|
1180
1502
|
"reader"));
|
|
1181
1503
|
|
|
1182
|
-
|
|
1504
|
+
if (token.has_value()) {
|
|
1505
|
+
js_args.push_back(Napi::String::New(env, *token));
|
|
1506
|
+
} else {
|
|
1507
|
+
js_args.push_back(env.Undefined());
|
|
1508
|
+
}
|
|
1183
1509
|
|
|
1184
1510
|
Napi::Object js_token_verifier =
|
|
1185
1511
|
js_token_verifier_reference->Value(env);
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
.As<Napi::Function>()
|
|
1190
|
-
.Call(js_token_verifier, {js_args})
|
|
1191
|
-
.As<Napi::Object>();
|
|
1192
|
-
|
|
1193
|
-
js_promise
|
|
1194
|
-
.Get("then")
|
|
1512
|
+
|
|
1513
|
+
return js_token_verifier
|
|
1514
|
+
.Get("_verifyToken")
|
|
1195
1515
|
.As<Napi::Function>()
|
|
1196
|
-
.Call(
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
.cast<bool>();
|
|
1213
|
-
if (!cancelled) {
|
|
1214
|
-
if (bytes_auth.has_value()) {
|
|
1215
|
-
py_future->attr("set_result")(
|
|
1216
|
-
py::bytes(*bytes_auth));
|
|
1217
|
-
} else {
|
|
1218
|
-
py_future->attr("set_result")(
|
|
1219
|
-
py::none());
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
delete py_future;
|
|
1223
|
-
});
|
|
1224
|
-
}),
|
|
1225
|
-
Napi::Function::New(
|
|
1226
|
-
env,
|
|
1227
|
-
[py_future](const Napi::CallbackInfo& info) {
|
|
1228
|
-
std::string message = message_from_js_error(
|
|
1229
|
-
info[0].As<Napi::Object>());
|
|
1230
|
-
|
|
1231
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
1232
|
-
[py_future,
|
|
1233
|
-
message = std::move(message)]() {
|
|
1234
|
-
bool cancelled =
|
|
1235
|
-
py_future->attr("cancelled")()
|
|
1236
|
-
.cast<bool>();
|
|
1237
|
-
if (!cancelled) {
|
|
1238
|
-
py_future->attr("set_exception")(
|
|
1239
|
-
py::module::import("builtins")
|
|
1240
|
-
.attr("Exception")(
|
|
1241
|
-
message));
|
|
1242
|
-
}
|
|
1243
|
-
delete py_future;
|
|
1244
|
-
});
|
|
1245
|
-
})});
|
|
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
|
+
}
|
|
1246
1532
|
});
|
|
1247
|
-
|
|
1248
|
-
return py_future;
|
|
1249
1533
|
},
|
|
1250
1534
|
py::name("_verify_token"),
|
|
1251
1535
|
py::arg("context"),
|
|
1252
1536
|
py::arg("aborted"),
|
|
1253
|
-
py::arg("
|
|
1537
|
+
py::arg("py_token"),
|
|
1254
1538
|
py::is_method(py::none()));
|
|
1255
1539
|
|
|
1256
1540
|
py::object py_parent_class =
|
|
1257
1541
|
py::module::import("reboot.nodejs.python")
|
|
1258
1542
|
.attr("NodeAdaptorTokenVerifier");
|
|
1543
|
+
|
|
1259
1544
|
py::object py_parent_metaclass = py::reinterpret_borrow<py::object>(
|
|
1260
1545
|
(PyObject*) &PyType_Type);
|
|
1546
|
+
|
|
1261
1547
|
py::object py_token_verifier = py_parent_metaclass(
|
|
1262
1548
|
"_NodeAdaptorTokenVerifier",
|
|
1263
1549
|
py::make_tuple(py_parent_class),
|
|
@@ -1278,94 +1564,45 @@ Napi::Value Reboot_up(const Napi::CallbackInfo& info) {
|
|
|
1278
1564
|
|
|
1279
1565
|
py::object* py_reboot = js_external_reboot.Value(info.Env()).Data();
|
|
1280
1566
|
|
|
1281
|
-
|
|
1567
|
+
auto js_external_application = NapiSafeReference(
|
|
1568
|
+
info[1].As<Napi::External<py::object>>());
|
|
1282
1569
|
|
|
1283
|
-
|
|
1284
|
-
if (info[2].IsObject()) {
|
|
1285
|
-
js_token_verifier = NapiSafeReference(info[2].As<Napi::Object>());
|
|
1286
|
-
}
|
|
1570
|
+
py::object* py_application = js_external_application.Value(info.Env()).Data();
|
|
1287
1571
|
|
|
1288
1572
|
bool local_envoy = false;
|
|
1289
|
-
if (!info[
|
|
1290
|
-
local_envoy = info[
|
|
1573
|
+
if (!info[2].IsUndefined()) {
|
|
1574
|
+
local_envoy = info[2].As<Napi::Boolean>();
|
|
1291
1575
|
}
|
|
1292
1576
|
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
auto promise = deferred->Promise();
|
|
1299
|
-
|
|
1300
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
1577
|
+
return NodePromiseFromPythonTask(
|
|
1578
|
+
info.Env(),
|
|
1579
|
+
"Reboot.up(...) in nodejs",
|
|
1580
|
+
{"asyncio", "create_task"},
|
|
1301
1581
|
[js_external_reboot, // Ensures `py_reboot` remains valid.
|
|
1302
1582
|
py_reboot,
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
"name"_a = "Reboot.up(...) in nodejs");
|
|
1326
|
-
|
|
1327
|
-
py_task.attr("add_done_callback")(py::cpp_function(
|
|
1328
|
-
[deferred = std::move(deferred),
|
|
1329
|
-
// NOTE: need to keep a reference to `py_task` so that it
|
|
1330
|
-
// doesn't get destroyed before completing!
|
|
1331
|
-
py_task = new py::object(py_task)](py::object py_future) {
|
|
1332
|
-
py::object py_exception = py_future.attr("exception")();
|
|
1333
|
-
|
|
1334
|
-
std::optional<std::string> application_id;
|
|
1335
|
-
std::optional<std::string> exception;
|
|
1336
|
-
|
|
1337
|
-
if (py_exception.is_none()) {
|
|
1338
|
-
py::str py_application_id =
|
|
1339
|
-
py_future.attr("result")().attr("application_id")();
|
|
1340
|
-
application_id.emplace(py_application_id);
|
|
1341
|
-
} else {
|
|
1342
|
-
py::str py_exception_string = py_exception_str(py_exception);
|
|
1343
|
-
|
|
1344
|
-
exception.emplace(py_exception_string);
|
|
1345
|
-
}
|
|
1346
|
-
|
|
1347
|
-
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
1348
|
-
[deferred = std::move(deferred),
|
|
1349
|
-
application_id = std::move(application_id),
|
|
1350
|
-
exception = std::move(exception)](Napi::Env env) {
|
|
1351
|
-
if (application_id.has_value()) {
|
|
1352
|
-
auto js_application_config = Napi::Object::New(env);
|
|
1353
|
-
js_application_config.Set(
|
|
1354
|
-
"applicationId",
|
|
1355
|
-
*application_id);
|
|
1356
|
-
deferred->Resolve(js_application_config);
|
|
1357
|
-
} else {
|
|
1358
|
-
deferred->Reject(
|
|
1359
|
-
Napi::Error::New(env, *exception).Value());
|
|
1360
|
-
}
|
|
1361
|
-
});
|
|
1362
|
-
|
|
1363
|
-
delete py_task;
|
|
1364
|
-
},
|
|
1365
|
-
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;
|
|
1366
1605
|
});
|
|
1367
|
-
|
|
1368
|
-
return promise;
|
|
1369
1606
|
}
|
|
1370
1607
|
|
|
1371
1608
|
void Reboot_down(const Napi::CallbackInfo& info) {
|
|
@@ -1392,49 +1629,13 @@ Napi::Value Reboot_start(const Napi::CallbackInfo& info) {
|
|
|
1392
1629
|
// flag, rather than incrementing and decrementing a reference count.
|
|
1393
1630
|
adaptor->thread_safe_function.Ref(info.Env());
|
|
1394
1631
|
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
py::object py_task =
|
|
1402
|
-
py::module::import("asyncio").attr("create_task")(
|
|
1403
|
-
py_reboot->attr("start")(),
|
|
1404
|
-
"name"_a = "Reboot.start() in nodejs");
|
|
1405
|
-
|
|
1406
|
-
py_task.attr("add_done_callback")(py::cpp_function(
|
|
1407
|
-
[deferred = std::move(deferred),
|
|
1408
|
-
// NOTE: need to keep a reference to `py_task` so that it
|
|
1409
|
-
// doesn't get destroyed before completing!
|
|
1410
|
-
py_task = new py::object(py_task)](py::object py_future) {
|
|
1411
|
-
py::object py_exception = py_future.attr("exception")();
|
|
1412
|
-
|
|
1413
|
-
std::optional<std::string> exception;
|
|
1414
|
-
|
|
1415
|
-
if (!py_exception.is_none()) {
|
|
1416
|
-
py::str py_exception_string = py_exception_str(py_exception);
|
|
1417
|
-
|
|
1418
|
-
exception.emplace(py_exception_string);
|
|
1419
|
-
}
|
|
1420
|
-
|
|
1421
|
-
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
1422
|
-
[deferred = std::move(deferred),
|
|
1423
|
-
exception = std::move(exception)](Napi::Env env) {
|
|
1424
|
-
if (!exception.has_value()) {
|
|
1425
|
-
deferred->Resolve(env.Null());
|
|
1426
|
-
} else {
|
|
1427
|
-
deferred->Reject(
|
|
1428
|
-
Napi::Error::New(env, *exception).Value());
|
|
1429
|
-
}
|
|
1430
|
-
});
|
|
1431
|
-
|
|
1432
|
-
delete py_task;
|
|
1433
|
-
},
|
|
1434
|
-
py::arg("future")));
|
|
1632
|
+
return NodePromiseFromPythonTask(
|
|
1633
|
+
info.Env(),
|
|
1634
|
+
"Reboot.start() in nodejs",
|
|
1635
|
+
{"asyncio", "create_task"},
|
|
1636
|
+
[py_reboot]() {
|
|
1637
|
+
return py_reboot->attr("start")();
|
|
1435
1638
|
});
|
|
1436
|
-
|
|
1437
|
-
return promise;
|
|
1438
1639
|
}
|
|
1439
1640
|
|
|
1440
1641
|
Napi::Value Reboot_stop(const Napi::CallbackInfo& info) {
|
|
@@ -1448,58 +1649,34 @@ Napi::Value Reboot_stop(const Napi::CallbackInfo& info) {
|
|
|
1448
1649
|
|
|
1449
1650
|
py::object* py_reboot = js_external_reboot.Value(info.Env()).Data();
|
|
1450
1651
|
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
py::module::import("asyncio").attr("create_task")(
|
|
1459
|
-
py_reboot->attr("stop")(),
|
|
1460
|
-
"name"_a = "Reboot.stop() in nodejs");
|
|
1461
|
-
|
|
1462
|
-
py_task.attr("add_done_callback")(py::cpp_function(
|
|
1463
|
-
[deferred = std::move(deferred),
|
|
1464
|
-
// NOTE: need to keep a reference to `py_task` so that it
|
|
1465
|
-
// doesn't get destroyed before completing!
|
|
1466
|
-
py_task = new py::object(py_task)](py::object py_future) {
|
|
1467
|
-
py::object py_exception = py_future.attr("exception")();
|
|
1468
|
-
|
|
1469
|
-
std::optional<std::string> exception;
|
|
1470
|
-
|
|
1471
|
-
if (!py_exception.is_none()) {
|
|
1472
|
-
py::str py_exception_string = py_exception_str(py_exception);
|
|
1473
|
-
|
|
1474
|
-
exception.emplace(py_exception_string);
|
|
1475
|
-
}
|
|
1476
|
-
|
|
1477
|
-
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
1478
|
-
[deferred = std::move(deferred),
|
|
1479
|
-
exception = std::move(exception)](Napi::Env env) {
|
|
1480
|
-
if (!exception.has_value()) {
|
|
1481
|
-
deferred->Resolve(env.Null());
|
|
1482
|
-
} else {
|
|
1483
|
-
deferred->Reject(
|
|
1484
|
-
Napi::Error::New(env, *exception).Value());
|
|
1485
|
-
}
|
|
1486
|
-
// Call Unref() on our NAPI thread safe function so that the
|
|
1487
|
-
// object can be destroyed and the event loop can exit.
|
|
1488
|
-
// See:
|
|
1489
|
-
// (check_line_length skip)
|
|
1490
|
-
// https://nodejs.github.io/node-addon-examples/special-topics/thread-safe-functions/#q-my-application-isnt-exiting-correctly-it-just-hangs.
|
|
1491
|
-
// Note that Ref() and Unref() will set and unset an
|
|
1492
|
-
// "is_referenced" boolean flag, rather than incrementing
|
|
1493
|
-
// and decrementing a reference count.
|
|
1494
|
-
adaptor->thread_safe_function.Unref(env);
|
|
1495
|
-
});
|
|
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
|
+
});
|
|
1496
1659
|
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
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());
|
|
1500
1672
|
});
|
|
1501
1673
|
|
|
1502
|
-
|
|
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;
|
|
1503
1680
|
}
|
|
1504
1681
|
|
|
1505
1682
|
// NOTE: We block on a promise here, so this method should not be called outside
|
|
@@ -1515,15 +1692,13 @@ Napi::Value Reboot_url(const Napi::CallbackInfo& info) {
|
|
|
1515
1692
|
|
|
1516
1693
|
py::object* py_reboot = js_external_reboot.Value(info.Env()).Data();
|
|
1517
1694
|
|
|
1518
|
-
std::
|
|
1519
|
-
|
|
1520
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
1521
|
-
[py_reboot, &promise]() {
|
|
1695
|
+
std::string url = RunCallbackOnPythonEventLoop(
|
|
1696
|
+
[py_reboot]() {
|
|
1522
1697
|
py::str url = py_reboot->attr("url")();
|
|
1523
|
-
|
|
1698
|
+
return std::string(url);
|
|
1524
1699
|
});
|
|
1525
1700
|
|
|
1526
|
-
return Napi::String::New(info.Env(),
|
|
1701
|
+
return Napi::String::New(info.Env(), url);
|
|
1527
1702
|
}
|
|
1528
1703
|
|
|
1529
1704
|
Napi::Value Service_constructor(const Napi::CallbackInfo& info) {
|
|
@@ -1537,35 +1712,22 @@ Napi::Value Service_constructor(const Napi::CallbackInfo& info) {
|
|
|
1537
1712
|
|
|
1538
1713
|
std::string id = js_args.Get("id").As<Napi::String>().Utf8Value();
|
|
1539
1714
|
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
1543
|
-
[&rbt_module, &node_adaptor, &id, &promise]() {
|
|
1715
|
+
py::object* py_service = RunCallbackOnPythonEventLoop(
|
|
1716
|
+
[&rbt_module, &node_adaptor, &id]() {
|
|
1544
1717
|
py::object py_module = py::module::import(rbt_module.c_str());
|
|
1545
1718
|
py::object py_schedule_type =
|
|
1546
1719
|
py_module
|
|
1547
1720
|
.attr(node_adaptor.c_str())
|
|
1548
1721
|
.attr("_Schedule");
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
"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));
|
|
1555
1727
|
});
|
|
1556
1728
|
|
|
1557
|
-
py::object* py_service = promise.get_future().get();
|
|
1558
|
-
|
|
1559
1729
|
Napi::External<py::object> js_external_service =
|
|
1560
|
-
|
|
1561
|
-
info.Env(),
|
|
1562
|
-
py_service,
|
|
1563
|
-
[](Napi::Env, py::object* py_service) {
|
|
1564
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
1565
|
-
[py_service]() {
|
|
1566
|
-
delete py_service;
|
|
1567
|
-
});
|
|
1568
|
-
});
|
|
1730
|
+
make_napi_external(info.Env(), py_service);
|
|
1569
1731
|
|
|
1570
1732
|
// auto type_tag = MakeTypeTag(module, type);
|
|
1571
1733
|
// js_external_service.TypeTag(&type_tag);
|
|
@@ -1607,23 +1769,20 @@ Napi::Value Service_call(const Napi::CallbackInfo& info) {
|
|
|
1607
1769
|
|
|
1608
1770
|
std::string json_options = js_args.Get("jsonOptions").As<Napi::String>();
|
|
1609
1771
|
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
1772
|
+
return NodePromiseFromPythonTaskWithContext(
|
|
1773
|
+
info.Env(),
|
|
1774
|
+
"servicer._" + kind + "(\"" + method + "\", ...) in nodejs",
|
|
1775
|
+
js_external_context,
|
|
1615
1776
|
[js_external_service, // Ensures `py_service` remains valid.
|
|
1616
1777
|
py_service,
|
|
1617
1778
|
kind,
|
|
1618
1779
|
method,
|
|
1619
1780
|
request_module,
|
|
1620
1781
|
request_type,
|
|
1621
|
-
// Ensures `py_context` remains valid.
|
|
1622
|
-
js_external_context,
|
|
1782
|
+
js_external_context, // Ensures `py_context` remains valid.
|
|
1623
1783
|
py_context,
|
|
1624
1784
|
json_request,
|
|
1625
|
-
json_options
|
|
1626
|
-
deferred = std::move(deferred)]() {
|
|
1785
|
+
json_options]() {
|
|
1627
1786
|
// If a type is a nested message type, we can't import it directly
|
|
1628
1787
|
// as 'from module import Foo.Bar.Baz', instead we need to import the
|
|
1629
1788
|
// top level 'Foo' and call 'Foo.Bar.Baz' on it.
|
|
@@ -1640,73 +1799,25 @@ Napi::Value Service_call(const Napi::CallbackInfo& info) {
|
|
|
1640
1799
|
py_request_type = py_request_type.attr(part.c_str());
|
|
1641
1800
|
}
|
|
1642
1801
|
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
.c_str());
|
|
1656
|
-
|
|
1657
|
-
py_task.attr("add_done_callback")(py::cpp_function(
|
|
1658
|
-
[deferred = std::move(deferred),
|
|
1659
|
-
// NOTE: need to keep a reference to `py_task` so that it
|
|
1660
|
-
// doesn't get destroyed before completing!
|
|
1661
|
-
py_task = new py::object(py_task)](py::object py_future) {
|
|
1662
|
-
py::object py_exception = py_future.attr("exception")();
|
|
1663
|
-
|
|
1664
|
-
std::optional<std::string> json;
|
|
1665
|
-
std::optional<std::string> exception;
|
|
1666
|
-
|
|
1667
|
-
if (py_exception.is_none()) {
|
|
1668
|
-
py::str py_json = py_future.attr("result")();
|
|
1669
|
-
json.emplace(py_json);
|
|
1670
|
-
} else {
|
|
1671
|
-
py::str py_exception_string = py_exception_str(py_exception);
|
|
1672
|
-
|
|
1673
|
-
exception.emplace(py_exception_string);
|
|
1674
|
-
}
|
|
1675
|
-
|
|
1676
|
-
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
1677
|
-
[deferred = std::move(deferred),
|
|
1678
|
-
json = std::move(json),
|
|
1679
|
-
exception = std::move(exception)](Napi::Env env) {
|
|
1680
|
-
if (json.has_value()) {
|
|
1681
|
-
deferred->Resolve(
|
|
1682
|
-
Napi::String::New(env, *json));
|
|
1683
|
-
} else {
|
|
1684
|
-
deferred->Reject(
|
|
1685
|
-
Napi::Error::New(env, *exception).Value());
|
|
1686
|
-
}
|
|
1687
|
-
});
|
|
1688
|
-
|
|
1689
|
-
delete py_task;
|
|
1690
|
-
}));
|
|
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);
|
|
1691
1814
|
});
|
|
1692
|
-
|
|
1693
|
-
return promise;
|
|
1694
1815
|
}
|
|
1695
1816
|
|
|
1696
1817
|
|
|
1697
|
-
Napi::Value
|
|
1818
|
+
Napi::Value Task_await(const Napi::CallbackInfo& info) {
|
|
1698
1819
|
Napi::Object js_args = info[0].As<Napi::Object>();
|
|
1699
1820
|
|
|
1700
|
-
// NOTE: we immediately get a safe reference to the `Napi::External`
|
|
1701
|
-
// so that Node will not garbage collect it and the `py::object*` we
|
|
1702
|
-
// get out of it will remain valid.
|
|
1703
|
-
auto js_external_service = NapiSafeReference(
|
|
1704
|
-
js_args.Get("external").As<Napi::External<py::object>>());
|
|
1705
|
-
|
|
1706
|
-
// CHECK(js_external_service.CheckTypeTag(...));
|
|
1707
|
-
|
|
1708
|
-
py::object* py_service = js_external_service.Value(info.Env()).Data();
|
|
1709
|
-
|
|
1710
1821
|
// NOTE: we immediately get a safe reference to the `Napi::External`
|
|
1711
1822
|
// so that Node will not garbage collect it and the `py::object*` we
|
|
1712
1823
|
// get out of it will remain valid.
|
|
@@ -1718,71 +1829,40 @@ Napi::Value Future_await(const Napi::CallbackInfo& info) {
|
|
|
1718
1829
|
py::object* py_context =
|
|
1719
1830
|
js_external_context.Value(info.Env()).Data();
|
|
1720
1831
|
|
|
1721
|
-
std::string
|
|
1832
|
+
std::string rbt_module = js_args.Get("rbtModule").As<Napi::String>();
|
|
1722
1833
|
|
|
1723
|
-
std::string
|
|
1834
|
+
std::string state_name = js_args.Get("stateName").As<Napi::String>();
|
|
1724
1835
|
|
|
1725
|
-
|
|
1836
|
+
std::string method = js_args.Get("method").As<Napi::String>();
|
|
1726
1837
|
|
|
1727
|
-
|
|
1838
|
+
std::string json_task_id = js_args.Get("jsonTaskId").As<Napi::String>();
|
|
1728
1839
|
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1840
|
+
return NodePromiseFromPythonTaskWithContext(
|
|
1841
|
+
info.Env(),
|
|
1842
|
+
"reboot.nodejs.python.task_await(\""
|
|
1843
|
+
+ state_name + "\", \""
|
|
1844
|
+
+ method + "\", ...) in nodejs",
|
|
1845
|
+
js_external_context,
|
|
1846
|
+
[rbt_module = std::move(rbt_module),
|
|
1847
|
+
state_name = std::move(state_name),
|
|
1848
|
+
method = std::move(method),
|
|
1849
|
+
js_external_context, // Ensures `py_context` remains valid.
|
|
1735
1850
|
py_context,
|
|
1736
|
-
json_task_id
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
.
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
[deferred = std::move(deferred),
|
|
1751
|
-
// NOTE: need to keep a reference to `py_task` so that it
|
|
1752
|
-
// doesn't get destroyed before completing!
|
|
1753
|
-
py_task = new py::object(py_task)](py::object py_future) {
|
|
1754
|
-
py::object py_exception = py_future.attr("exception")();
|
|
1755
|
-
|
|
1756
|
-
std::optional<std::string> json;
|
|
1757
|
-
std::optional<std::string> exception;
|
|
1758
|
-
|
|
1759
|
-
if (py_exception.is_none()) {
|
|
1760
|
-
py::str py_json = py_future.attr("result")();
|
|
1761
|
-
json.emplace(py_json);
|
|
1762
|
-
} else {
|
|
1763
|
-
py::str py_exception_string = py_exception_str(py_exception);
|
|
1764
|
-
|
|
1765
|
-
exception.emplace(py_exception_string);
|
|
1766
|
-
}
|
|
1767
|
-
|
|
1768
|
-
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
1769
|
-
[deferred = std::move(deferred),
|
|
1770
|
-
json = std::move(json),
|
|
1771
|
-
exception = std::move(exception)](Napi::Env env) {
|
|
1772
|
-
if (json.has_value()) {
|
|
1773
|
-
deferred->Resolve(
|
|
1774
|
-
Napi::String::New(env, *json));
|
|
1775
|
-
} else {
|
|
1776
|
-
deferred->Reject(
|
|
1777
|
-
Napi::Error::New(env, *exception).Value());
|
|
1778
|
-
}
|
|
1779
|
-
});
|
|
1780
|
-
|
|
1781
|
-
delete py_task;
|
|
1782
|
-
}));
|
|
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);
|
|
1783
1865
|
});
|
|
1784
|
-
|
|
1785
|
-
return promise;
|
|
1786
1866
|
}
|
|
1787
1867
|
|
|
1788
1868
|
Napi::Value ExternalContext_constructor(const Napi::CallbackInfo& info) {
|
|
@@ -1816,11 +1896,8 @@ Napi::Value ExternalContext_constructor(const Napi::CallbackInfo& info) {
|
|
|
1816
1896
|
.Utf8Value();
|
|
1817
1897
|
}
|
|
1818
1898
|
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
1822
|
-
[&promise,
|
|
1823
|
-
&name,
|
|
1899
|
+
py::object* py_external_context = RunCallbackOnPythonEventLoop(
|
|
1900
|
+
[&name,
|
|
1824
1901
|
&url,
|
|
1825
1902
|
&bearer_token,
|
|
1826
1903
|
&idempotency_seed,
|
|
@@ -1830,8 +1907,7 @@ Napi::Value ExternalContext_constructor(const Napi::CallbackInfo& info) {
|
|
|
1830
1907
|
"reboot.aio.external");
|
|
1831
1908
|
|
|
1832
1909
|
auto convert_str =
|
|
1833
|
-
[](
|
|
1834
|
-
const std::optional<std::string>& optional) -> py::object {
|
|
1910
|
+
[](const std::optional<std::string>& optional) -> py::object {
|
|
1835
1911
|
if (optional.has_value()) {
|
|
1836
1912
|
return py::str(*optional);
|
|
1837
1913
|
} else {
|
|
@@ -1839,30 +1915,20 @@ Napi::Value ExternalContext_constructor(const Napi::CallbackInfo& info) {
|
|
|
1839
1915
|
}
|
|
1840
1916
|
};
|
|
1841
1917
|
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
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)));
|
|
1851
1926
|
});
|
|
1852
|
-
py::object* py_external_context = promise.get_future().get();
|
|
1853
1927
|
|
|
1854
|
-
Napi::External<py::object> js_external_context =
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
[](Napi::Env, py::object* py_external_context) {
|
|
1859
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
1860
|
-
[py_external_context]() {
|
|
1861
|
-
delete py_external_context;
|
|
1862
|
-
});
|
|
1863
|
-
});
|
|
1864
|
-
|
|
1865
|
-
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);
|
|
1866
1932
|
|
|
1867
1933
|
return js_external_context;
|
|
1868
1934
|
}
|
|
@@ -1873,62 +1939,190 @@ Napi::Value Application_constructor(const Napi::CallbackInfo& info) {
|
|
|
1873
1939
|
|
|
1874
1940
|
Napi::Array js_servicers = info[1].As<Napi::Array>();
|
|
1875
1941
|
|
|
1876
|
-
|
|
1877
|
-
|
|
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);
|
|
1878
1951
|
|
|
1879
1952
|
auto js_initialize = NapiSafeFunctionReference(
|
|
1880
|
-
info[
|
|
1953
|
+
info[3].As<Napi::Function>());
|
|
1881
1954
|
|
|
1882
1955
|
std::optional<std::string> initialize_bearer_token;
|
|
1883
|
-
if (!info[
|
|
1884
|
-
initialize_bearer_token = info[
|
|
1956
|
+
if (!info[4].IsUndefined()) {
|
|
1957
|
+
initialize_bearer_token = info[4].As<Napi::String>().Utf8Value();
|
|
1885
1958
|
}
|
|
1886
1959
|
|
|
1887
1960
|
std::optional<NapiSafeObjectReference> js_token_verifier;
|
|
1888
|
-
if (!info[
|
|
1889
|
-
js_token_verifier = NapiSafeReference(info[
|
|
1961
|
+
if (!info[5].IsUndefined()) {
|
|
1962
|
+
js_token_verifier = NapiSafeReference(info[5].As<Napi::Object>());
|
|
1890
1963
|
}
|
|
1891
1964
|
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
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),
|
|
1897
1969
|
initialize_bearer_token = std::move(initialize_bearer_token),
|
|
1898
1970
|
js_initialize = std::move(js_initialize),
|
|
1899
1971
|
js_token_verifier,
|
|
1900
1972
|
js_from_native_external = std::move(js_from_native_external)]() {
|
|
1901
1973
|
py::list py_servicers = make_py_servicers(servicer_details);
|
|
1902
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
|
+
|
|
1903
2106
|
py::object py_initialize = py::cpp_function(
|
|
1904
2107
|
[js_initialize = std::move(js_initialize),
|
|
1905
|
-
js_from_native_external
|
|
2108
|
+
js_from_native_external /* Need a copy because it is shared. */](
|
|
1906
2109
|
py::object py_context) mutable {
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
py_context = new py::object(py_context)
|
|
1917
|
-
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)](
|
|
1918
2120
|
Napi::Env env) mutable {
|
|
1919
2121
|
Napi::External<py::object> js_external_context =
|
|
1920
|
-
|
|
2122
|
+
make_napi_external(
|
|
1921
2123
|
env,
|
|
1922
2124
|
py_context,
|
|
1923
|
-
|
|
1924
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
1925
|
-
[py_context]() {
|
|
1926
|
-
delete py_context;
|
|
1927
|
-
});
|
|
1928
|
-
});
|
|
1929
|
-
|
|
1930
|
-
js_external_context.TypeTag(
|
|
1931
|
-
&reboot_aio_external_ExternalContext);
|
|
2125
|
+
&reboot_aio_external_ExternalContext);
|
|
1932
2126
|
|
|
1933
2127
|
Napi::Object js_context =
|
|
1934
2128
|
js_from_native_external
|
|
@@ -1938,57 +2132,11 @@ Napi::Value Application_constructor(const Napi::CallbackInfo& info) {
|
|
|
1938
2132
|
{js_external_context})
|
|
1939
2133
|
.As<Napi::Object>();
|
|
1940
2134
|
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
.As<Napi::Object>();
|
|
1946
|
-
|
|
1947
|
-
js_promise
|
|
1948
|
-
.Get("then")
|
|
1949
|
-
.As<Napi::Function>()
|
|
1950
|
-
.Call(
|
|
1951
|
-
js_promise,
|
|
1952
|
-
{Napi::Function::New(
|
|
1953
|
-
env,
|
|
1954
|
-
[py_future](const Napi::CallbackInfo& info) {
|
|
1955
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
1956
|
-
[py_future]() {
|
|
1957
|
-
bool cancelled =
|
|
1958
|
-
py_future
|
|
1959
|
-
->attr("cancelled")()
|
|
1960
|
-
.cast<bool>();
|
|
1961
|
-
if (!cancelled) {
|
|
1962
|
-
py_future->attr("set_result")(
|
|
1963
|
-
py::none());
|
|
1964
|
-
}
|
|
1965
|
-
delete py_future;
|
|
1966
|
-
});
|
|
1967
|
-
}),
|
|
1968
|
-
Napi::Function::New(
|
|
1969
|
-
env,
|
|
1970
|
-
[py_future](const Napi::CallbackInfo& info) {
|
|
1971
|
-
std::string message = message_from_js_error(
|
|
1972
|
-
info[0].As<Napi::Object>());
|
|
1973
|
-
|
|
1974
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
1975
|
-
[py_future,
|
|
1976
|
-
message = std::move(message)]() {
|
|
1977
|
-
bool cancelled =
|
|
1978
|
-
py_future->attr("cancelled")()
|
|
1979
|
-
.cast<bool>();
|
|
1980
|
-
if (!cancelled) {
|
|
1981
|
-
py_future->attr("set_exception")(
|
|
1982
|
-
py::module::import("builtins")
|
|
1983
|
-
.attr("Exception")(
|
|
1984
|
-
message));
|
|
1985
|
-
}
|
|
1986
|
-
delete py_future;
|
|
1987
|
-
});
|
|
1988
|
-
})});
|
|
2135
|
+
return js_initialize
|
|
2136
|
+
.Value(env)
|
|
2137
|
+
.Call(env.Global(), {js_context})
|
|
2138
|
+
.As<Napi::Object>();
|
|
1989
2139
|
});
|
|
1990
|
-
|
|
1991
|
-
return py_future;
|
|
1992
2140
|
});
|
|
1993
2141
|
|
|
1994
2142
|
py::object py_initialize_bearer_token = py::none();
|
|
@@ -2001,44 +2149,22 @@ Napi::Value Application_constructor(const Napi::CallbackInfo& info) {
|
|
|
2001
2149
|
py_token_verifier = make_py_token_verifier(*js_token_verifier);
|
|
2002
2150
|
}
|
|
2003
2151
|
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
// raised exceptions to be handled.
|
|
2016
|
-
//
|
|
2017
|
-
// [](py::error_already_set& e) {
|
|
2018
|
-
|
|
2019
|
-
// },
|
|
2020
|
-
// [](std::exception& e) {
|
|
2021
|
-
|
|
2022
|
-
// },
|
|
2023
|
-
// []() {
|
|
2024
|
-
|
|
2025
|
-
// }
|
|
2026
|
-
);
|
|
2027
|
-
|
|
2028
|
-
py::object* py_application = promise.get_future().get();
|
|
2029
|
-
|
|
2030
|
-
Napi::External<py::object> js_external_application =
|
|
2031
|
-
Napi::External<py::object>::New(
|
|
2032
|
-
info.Env(),
|
|
2033
|
-
py_application,
|
|
2034
|
-
[](Napi::Env, py::object* py_application) {
|
|
2035
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2036
|
-
[py_application]() {
|
|
2037
|
-
delete py_application;
|
|
2038
|
-
});
|
|
2039
|
-
});
|
|
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
|
+
});
|
|
2040
2163
|
|
|
2041
|
-
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);
|
|
2042
2168
|
|
|
2043
2169
|
return js_external_application;
|
|
2044
2170
|
}
|
|
@@ -2061,58 +2187,45 @@ Napi::Value Application_run(const Napi::CallbackInfo& info) {
|
|
|
2061
2187
|
py::object* py_application =
|
|
2062
2188
|
js_external_application.Value(info.Env()).Data();
|
|
2063
2189
|
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2190
|
+
Napi::Promise js_promise = NodePromiseFromPythonTask(
|
|
2191
|
+
info.Env(),
|
|
2192
|
+
"Application.run() in nodejs",
|
|
2193
|
+
{"reboot.nodejs.python", "create_task"},
|
|
2069
2194
|
[js_external_application, // Ensures `py_application` remains valid.
|
|
2070
|
-
py_application
|
|
2071
|
-
|
|
2072
|
-
py::object py_task =
|
|
2073
|
-
py::module::import("reboot.nodejs.python")
|
|
2074
|
-
.attr("create_task")(
|
|
2075
|
-
py_application->attr("run")(),
|
|
2076
|
-
"name"_a = "Application.run() in nodejs");
|
|
2077
|
-
|
|
2078
|
-
py_task.attr("add_done_callback")(py::cpp_function(
|
|
2079
|
-
[deferred = std::move(deferred),
|
|
2080
|
-
// NOTE: need to keep a reference to `py_task` so that it
|
|
2081
|
-
// doesn't get destroyed before completing!
|
|
2082
|
-
py_task = new py::object(py_task)](py::object py_future) {
|
|
2083
|
-
py::object py_exception = py_future.attr("exception")();
|
|
2084
|
-
|
|
2085
|
-
std::optional<std::string> exception;
|
|
2086
|
-
|
|
2087
|
-
if (!py_exception.is_none()) {
|
|
2088
|
-
py::str py_exception_string = py_exception_str(py_exception);
|
|
2089
|
-
|
|
2090
|
-
exception.emplace(py_exception_string);
|
|
2091
|
-
}
|
|
2092
|
-
|
|
2093
|
-
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
2094
|
-
[deferred = std::move(deferred),
|
|
2095
|
-
exception = std::move(exception)](Napi::Env env) {
|
|
2096
|
-
if (!exception.has_value()) {
|
|
2097
|
-
deferred->Resolve(env.Null());
|
|
2098
|
-
} else {
|
|
2099
|
-
env.Global()
|
|
2100
|
-
.Get("process")
|
|
2101
|
-
.As<Napi::Object>()
|
|
2102
|
-
.Set("exitCode", 1);
|
|
2103
|
-
deferred->Reject(
|
|
2104
|
-
Napi::Error::New(env, *exception).Value());
|
|
2105
|
-
}
|
|
2106
|
-
// When `Application.run` exits, we unref our
|
|
2107
|
-
// thread_safe_function to cause the runtime to exit.
|
|
2108
|
-
adaptor->thread_safe_function.Unref(env);
|
|
2109
|
-
});
|
|
2110
|
-
|
|
2111
|
-
delete py_task;
|
|
2112
|
-
}));
|
|
2195
|
+
py_application]() {
|
|
2196
|
+
return py_application->attr("run")();
|
|
2113
2197
|
});
|
|
2114
2198
|
|
|
2115
|
-
|
|
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;
|
|
2116
2229
|
}
|
|
2117
2230
|
|
|
2118
2231
|
|
|
@@ -2124,22 +2237,18 @@ Napi::Value Context_auth(const Napi::CallbackInfo& info) {
|
|
|
2124
2237
|
|
|
2125
2238
|
py::object* py_context = js_external_context.Data();
|
|
2126
2239
|
|
|
2127
|
-
std::
|
|
2128
|
-
|
|
2129
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2130
|
-
[py_context, &promise]() {
|
|
2240
|
+
std::optional<std::string> auth_bytes = RunCallbackOnPythonEventLoop(
|
|
2241
|
+
[py_context]() -> std::optional<std::string> {
|
|
2131
2242
|
py::object py_auth = py_context->attr("auth");
|
|
2132
2243
|
|
|
2133
2244
|
if (py_auth.is_none()) {
|
|
2134
|
-
|
|
2245
|
+
return std::nullopt;
|
|
2135
2246
|
} else {
|
|
2136
2247
|
std::string auth_bytes = py::bytes(py_auth.attr("to_proto_bytes")());
|
|
2137
|
-
|
|
2248
|
+
return auth_bytes;
|
|
2138
2249
|
}
|
|
2139
2250
|
});
|
|
2140
2251
|
|
|
2141
|
-
std::optional<std::string> auth_bytes = promise.get_future().get();
|
|
2142
|
-
|
|
2143
2252
|
if (auth_bytes.has_value()) {
|
|
2144
2253
|
Napi::Env env = info.Env();
|
|
2145
2254
|
return str_to_uint8array(env, *auth_bytes);
|
|
@@ -2157,15 +2266,13 @@ Napi::Value Context_stateId(const Napi::CallbackInfo& info) {
|
|
|
2157
2266
|
|
|
2158
2267
|
py::object* py_context = js_external_context.Data();
|
|
2159
2268
|
|
|
2160
|
-
std::
|
|
2161
|
-
|
|
2162
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2163
|
-
[py_context, &promise]() {
|
|
2269
|
+
std::string state_id = RunCallbackOnPythonEventLoop(
|
|
2270
|
+
[py_context]() {
|
|
2164
2271
|
py::str state_id = py_context->attr("state_id");
|
|
2165
|
-
|
|
2272
|
+
return std::string(state_id);
|
|
2166
2273
|
});
|
|
2167
2274
|
|
|
2168
|
-
return Napi::String::New(info.Env(),
|
|
2275
|
+
return Napi::String::New(info.Env(), state_id);
|
|
2169
2276
|
}
|
|
2170
2277
|
|
|
2171
2278
|
|
|
@@ -2177,21 +2284,17 @@ Napi::Value Context_iteration(const Napi::CallbackInfo& info) {
|
|
|
2177
2284
|
|
|
2178
2285
|
py::object* py_context = js_external_context.Data();
|
|
2179
2286
|
|
|
2180
|
-
std::
|
|
2181
|
-
|
|
2182
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2183
|
-
[py_context, &promise]() {
|
|
2287
|
+
std::optional<int> iteration = RunCallbackOnPythonEventLoop(
|
|
2288
|
+
[py_context]() -> std::optional<int> {
|
|
2184
2289
|
py::object iteration = py_context->attr("iteration");
|
|
2185
2290
|
|
|
2186
2291
|
if (iteration.is_none()) {
|
|
2187
|
-
|
|
2292
|
+
return std::nullopt;
|
|
2188
2293
|
} else {
|
|
2189
|
-
|
|
2294
|
+
return iteration.cast<int>();
|
|
2190
2295
|
}
|
|
2191
2296
|
});
|
|
2192
2297
|
|
|
2193
|
-
std::optional<int> iteration = promise.get_future().get();
|
|
2194
|
-
|
|
2195
2298
|
if (iteration.has_value()) {
|
|
2196
2299
|
return Napi::Number::New(info.Env(), *iteration);
|
|
2197
2300
|
} else {
|
|
@@ -2200,6 +2303,41 @@ Napi::Value Context_iteration(const Napi::CallbackInfo& info) {
|
|
|
2200
2303
|
}
|
|
2201
2304
|
|
|
2202
2305
|
|
|
2306
|
+
Napi::Value Context_cookie(const Napi::CallbackInfo& info) {
|
|
2307
|
+
Napi::External<py::object> js_external_context =
|
|
2308
|
+
info[0].As<Napi::External<py::object>>();
|
|
2309
|
+
|
|
2310
|
+
// CHECK(...CheckTypeTag(...));
|
|
2311
|
+
|
|
2312
|
+
py::object* py_context = js_external_context.Data();
|
|
2313
|
+
|
|
2314
|
+
std::string cookie = RunCallbackOnPythonEventLoop(
|
|
2315
|
+
[py_context]() {
|
|
2316
|
+
py::str cookie = py_context->attr("cookie");
|
|
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>();
|
|
2335
|
+
});
|
|
2336
|
+
|
|
2337
|
+
return Napi::Boolean::New(info.Env(), app_internal);
|
|
2338
|
+
}
|
|
2339
|
+
|
|
2340
|
+
|
|
2203
2341
|
Napi::Value Context_generateIdempotentStateId(const Napi::CallbackInfo& info) {
|
|
2204
2342
|
// NOTE: we immediately get a safe reference to the `Napi::External`
|
|
2205
2343
|
// so that Node will not garbage collect it and the `py::object*` we
|
|
@@ -2231,19 +2369,15 @@ Napi::Value Context_generateIdempotentStateId(const Napi::CallbackInfo& info) {
|
|
|
2231
2369
|
alias = js_alias.As<Napi::String>().Utf8Value();
|
|
2232
2370
|
}
|
|
2233
2371
|
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
auto promise = deferred->Promise();
|
|
2237
|
-
|
|
2238
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2372
|
+
return NodePromiseFromPythonCallback(
|
|
2373
|
+
info.Env(),
|
|
2239
2374
|
[js_external_context, // Ensures `py_context` remains valid.
|
|
2240
2375
|
py_context,
|
|
2241
2376
|
state_type = std::move(state_type),
|
|
2242
2377
|
service_name = std::move(service_name),
|
|
2243
2378
|
method = std::move(method),
|
|
2244
2379
|
key = std::move(key),
|
|
2245
|
-
alias = std::move(alias)
|
|
2246
|
-
deferred = std::move(deferred)]() {
|
|
2380
|
+
alias = std::move(alias)]() {
|
|
2247
2381
|
py::object py_key = py::none();
|
|
2248
2382
|
if (key.has_value()) {
|
|
2249
2383
|
py_key = py::cast(*key);
|
|
@@ -2259,34 +2393,20 @@ Napi::Value Context_generateIdempotentStateId(const Napi::CallbackInfo& info) {
|
|
|
2259
2393
|
"key"_a = py_key,
|
|
2260
2394
|
"alias"_a = py_alias));
|
|
2261
2395
|
|
|
2262
|
-
py::object
|
|
2396
|
+
py::object py_generate_idempotent_state_id =
|
|
2263
2397
|
py_context->attr("generate_idempotent_state_id");
|
|
2264
|
-
try {
|
|
2265
|
-
py::object result = generate_idempotent_state_id(
|
|
2266
|
-
state_type,
|
|
2267
|
-
service_name,
|
|
2268
|
-
method,
|
|
2269
|
-
py_idempotency);
|
|
2270
2398
|
|
|
2271
|
-
|
|
2399
|
+
py::object py_state_id = py_generate_idempotent_state_id(
|
|
2400
|
+
state_type,
|
|
2401
|
+
service_name,
|
|
2402
|
+
method,
|
|
2403
|
+
py_idempotency);
|
|
2272
2404
|
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
});
|
|
2278
|
-
} catch (pybind11::error_already_set& e) {
|
|
2279
|
-
std::string exception_message = py_exception_str(e.value());
|
|
2280
|
-
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
2281
|
-
[exception_message = std::move(exception_message),
|
|
2282
|
-
deferred = std::move(deferred)](Napi::Env env) {
|
|
2283
|
-
deferred->Reject(
|
|
2284
|
-
Napi::Error::New(env, exception_message).Value());
|
|
2285
|
-
});
|
|
2286
|
-
}
|
|
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);
|
|
2287
2409
|
});
|
|
2288
|
-
|
|
2289
|
-
return promise;
|
|
2290
2410
|
}
|
|
2291
2411
|
|
|
2292
2412
|
|
|
@@ -2301,16 +2421,11 @@ Napi::Value WriterContext_set_sync(const Napi::CallbackInfo& info) {
|
|
|
2301
2421
|
|
|
2302
2422
|
bool sync = info[1].As<Napi::Boolean>();
|
|
2303
2423
|
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2307
|
-
[&py_context, sync, &promise]() {
|
|
2424
|
+
RunCallbackOnPythonEventLoop(
|
|
2425
|
+
[&py_context, sync]() {
|
|
2308
2426
|
py_context->attr("sync") = sync;
|
|
2309
|
-
promise.set_value();
|
|
2310
2427
|
});
|
|
2311
2428
|
|
|
2312
|
-
promise.get_future().get();
|
|
2313
|
-
|
|
2314
2429
|
return info.Env().Undefined();
|
|
2315
2430
|
}
|
|
2316
2431
|
|
|
@@ -2326,121 +2441,34 @@ Napi::Value retry_reactively_until(const Napi::CallbackInfo& info) {
|
|
|
2326
2441
|
auto js_condition = NapiSafeFunctionReference(
|
|
2327
2442
|
info[1].As<Napi::Function>());
|
|
2328
2443
|
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2444
|
+
return NodePromiseFromPythonTaskWithContext(
|
|
2445
|
+
info.Env(),
|
|
2446
|
+
"retry_reactively_until(...) in nodejs",
|
|
2447
|
+
js_external_context,
|
|
2334
2448
|
[js_external_context, // Ensures `py_context` remains valid.
|
|
2335
2449
|
py_context,
|
|
2336
|
-
js_condition = std::move(js_condition)
|
|
2337
|
-
deferred = std::move(deferred)]() {
|
|
2450
|
+
js_condition = std::move(js_condition)]() {
|
|
2338
2451
|
py::object py_condition = py::cpp_function(
|
|
2339
2452
|
[js_condition = std::move(js_condition)]() mutable {
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
.Call(env.Global(), {})
|
|
2354
|
-
.As<Napi::Object>();
|
|
2355
|
-
|
|
2356
|
-
js_promise
|
|
2357
|
-
.Get("then")
|
|
2358
|
-
.As<Napi::Function>()
|
|
2359
|
-
.Call(
|
|
2360
|
-
js_promise,
|
|
2361
|
-
{Napi::Function::New(
|
|
2362
|
-
env,
|
|
2363
|
-
[py_future](const Napi::CallbackInfo& info) {
|
|
2364
|
-
bool result = info[0].As<Napi::Boolean>();
|
|
2365
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2366
|
-
[py_future, result]() {
|
|
2367
|
-
bool cancelled =
|
|
2368
|
-
py_future->attr("cancelled")()
|
|
2369
|
-
.cast<bool>();
|
|
2370
|
-
if (!cancelled) {
|
|
2371
|
-
py_future->attr("set_result")(
|
|
2372
|
-
result);
|
|
2373
|
-
}
|
|
2374
|
-
delete py_future;
|
|
2375
|
-
});
|
|
2376
|
-
}),
|
|
2377
|
-
Napi::Function::New(
|
|
2378
|
-
env,
|
|
2379
|
-
[py_future](const Napi::CallbackInfo& info) {
|
|
2380
|
-
std::string message = message_from_js_error(
|
|
2381
|
-
info[0].As<Napi::Object>());
|
|
2382
|
-
|
|
2383
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2384
|
-
[py_future,
|
|
2385
|
-
message = std::move(message)]() {
|
|
2386
|
-
bool cancelled =
|
|
2387
|
-
py_future->attr("cancelled")()
|
|
2388
|
-
.cast<bool>();
|
|
2389
|
-
if (!cancelled) {
|
|
2390
|
-
py_future->attr("set_exception")(
|
|
2391
|
-
py::module::import("builtins")
|
|
2392
|
-
.attr("Exception")(
|
|
2393
|
-
message));
|
|
2394
|
-
}
|
|
2395
|
-
delete py_future;
|
|
2396
|
-
});
|
|
2397
|
-
})});
|
|
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();
|
|
2398
2466
|
});
|
|
2399
|
-
|
|
2400
|
-
return py_future;
|
|
2401
2467
|
});
|
|
2402
2468
|
|
|
2403
|
-
py::
|
|
2404
|
-
|
|
2405
|
-
.attr("create_task_with_context")(
|
|
2406
|
-
(py::module::import("reboot.aio.contexts")
|
|
2407
|
-
.attr("retry_reactively_until"))(
|
|
2408
|
-
py_context,
|
|
2409
|
-
py_condition),
|
|
2410
|
-
py_context,
|
|
2411
|
-
"name"_a = "retry_reactively_until(...) in nodejs");
|
|
2412
|
-
|
|
2413
|
-
py_task.attr("add_done_callback")(py::cpp_function(
|
|
2414
|
-
[deferred = std::move(deferred),
|
|
2415
|
-
// NOTE: need to keep a reference to `py_task` so that it
|
|
2416
|
-
// doesn't get destroyed before completing!
|
|
2417
|
-
py_task = new py::object(py_task)](py::object py_future) {
|
|
2418
|
-
py::object py_exception = py_future.attr("exception")();
|
|
2419
|
-
|
|
2420
|
-
std::optional<std::string> exception;
|
|
2421
|
-
|
|
2422
|
-
if (!py_exception.is_none()) {
|
|
2423
|
-
py::str py_exception_string = py_exception_str(py_exception);
|
|
2424
|
-
|
|
2425
|
-
exception.emplace(py_exception_string);
|
|
2426
|
-
}
|
|
2427
|
-
|
|
2428
|
-
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
2429
|
-
[deferred = std::move(deferred),
|
|
2430
|
-
exception = std::move(exception)](Napi::Env env) {
|
|
2431
|
-
if (!exception.has_value()) {
|
|
2432
|
-
deferred->Resolve(env.Null());
|
|
2433
|
-
} else {
|
|
2434
|
-
deferred->Reject(
|
|
2435
|
-
Napi::Error::New(env, *exception).Value());
|
|
2436
|
-
}
|
|
2437
|
-
});
|
|
2438
|
-
|
|
2439
|
-
delete py_task;
|
|
2440
|
-
}));
|
|
2469
|
+
return py::module::import("reboot.aio.contexts")
|
|
2470
|
+
.attr("retry_reactively_until")(py_context, py_condition);
|
|
2441
2471
|
});
|
|
2442
|
-
|
|
2443
|
-
return promise;
|
|
2444
2472
|
}
|
|
2445
2473
|
|
|
2446
2474
|
|
|
@@ -2459,132 +2487,45 @@ Napi::Value atLeastOrMostOnce(const Napi::CallbackInfo& info) {
|
|
|
2459
2487
|
|
|
2460
2488
|
bool at_most_once = info[3].As<Napi::Boolean>();
|
|
2461
2489
|
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2490
|
+
return NodePromiseFromPythonTaskWithContext(
|
|
2491
|
+
info.Env(),
|
|
2492
|
+
"memoize(...) in nodejs",
|
|
2493
|
+
js_external_context,
|
|
2467
2494
|
[js_external_context, // Ensures `py_context` remains valid.
|
|
2468
2495
|
py_context,
|
|
2469
2496
|
js_callable = std::move(js_callable),
|
|
2470
2497
|
idempotency_alias = std::move(idempotency_alias),
|
|
2471
|
-
at_most_once
|
|
2472
|
-
deferred = std::move(deferred)]() {
|
|
2498
|
+
at_most_once]() {
|
|
2473
2499
|
py::object py_callable = py::cpp_function(
|
|
2474
2500
|
[js_callable = std::move(js_callable)]() mutable {
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
Napi::
|
|
2486
|
-
js_callable
|
|
2487
|
-
.Value(env)
|
|
2488
|
-
.Call(env.Global(), {})
|
|
2489
|
-
.As<Napi::Object>();
|
|
2490
|
-
|
|
2491
|
-
js_promise
|
|
2492
|
-
.Get("then")
|
|
2493
|
-
.As<Napi::Function>()
|
|
2494
|
-
.Call(
|
|
2495
|
-
js_promise,
|
|
2496
|
-
{Napi::Function::New(
|
|
2497
|
-
env,
|
|
2498
|
-
[py_future](const Napi::CallbackInfo& info) {
|
|
2499
|
-
std::string result =
|
|
2500
|
-
info[0].As<Napi::String>().Utf8Value();
|
|
2501
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2502
|
-
[py_future, result]() {
|
|
2503
|
-
bool cancelled =
|
|
2504
|
-
py_future->attr("cancelled")()
|
|
2505
|
-
.cast<bool>();
|
|
2506
|
-
if (!cancelled) {
|
|
2507
|
-
py_future->attr("set_result")(
|
|
2508
|
-
result);
|
|
2509
|
-
}
|
|
2510
|
-
delete py_future;
|
|
2511
|
-
});
|
|
2512
|
-
}),
|
|
2513
|
-
Napi::Function::New(
|
|
2514
|
-
env,
|
|
2515
|
-
[py_future](const Napi::CallbackInfo& info) {
|
|
2516
|
-
std::string message = message_from_js_error(
|
|
2517
|
-
info[0].As<Napi::Object>());
|
|
2518
|
-
|
|
2519
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2520
|
-
[py_future,
|
|
2521
|
-
message = std::move(message)]() {
|
|
2522
|
-
bool cancelled =
|
|
2523
|
-
py_future->attr("cancelled")()
|
|
2524
|
-
.cast<bool>();
|
|
2525
|
-
if (!cancelled) {
|
|
2526
|
-
py_future->attr("set_exception")(
|
|
2527
|
-
py::module::import("builtins")
|
|
2528
|
-
.attr("Exception")(
|
|
2529
|
-
message));
|
|
2530
|
-
}
|
|
2531
|
-
delete py_future;
|
|
2532
|
-
});
|
|
2533
|
-
})});
|
|
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());
|
|
2534
2512
|
});
|
|
2535
|
-
|
|
2536
|
-
return py_future;
|
|
2537
2513
|
});
|
|
2538
2514
|
|
|
2539
|
-
py::
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
[deferred = std::move(deferred),
|
|
2553
|
-
// NOTE: need to keep a reference to `py_task` so that it
|
|
2554
|
-
// doesn't get destroyed before completing!
|
|
2555
|
-
py_task = new py::object(py_task)](py::object py_future) {
|
|
2556
|
-
py::object py_exception = py_future.attr("exception")();
|
|
2557
|
-
|
|
2558
|
-
std::optional<std::string> json;
|
|
2559
|
-
std::optional<std::string> exception;
|
|
2560
|
-
|
|
2561
|
-
if (py_exception.is_none()) {
|
|
2562
|
-
py::str py_json = py_future.attr("result")();
|
|
2563
|
-
json.emplace(py_json);
|
|
2564
|
-
} else {
|
|
2565
|
-
py::str py_exception_string = py_exception_str(py_exception);
|
|
2566
|
-
|
|
2567
|
-
exception.emplace(py_exception_string);
|
|
2568
|
-
}
|
|
2569
|
-
|
|
2570
|
-
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
2571
|
-
[deferred = std::move(deferred),
|
|
2572
|
-
json = std::move(json),
|
|
2573
|
-
exception = std::move(exception)](Napi::Env env) {
|
|
2574
|
-
if (json.has_value()) {
|
|
2575
|
-
deferred->Resolve(
|
|
2576
|
-
Napi::String::New(env, *json));
|
|
2577
|
-
} else {
|
|
2578
|
-
deferred->Reject(
|
|
2579
|
-
Napi::Error::New(env, *exception).Value());
|
|
2580
|
-
}
|
|
2581
|
-
});
|
|
2582
|
-
|
|
2583
|
-
delete py_task;
|
|
2584
|
-
}));
|
|
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);
|
|
2585
2528
|
});
|
|
2586
|
-
|
|
2587
|
-
return promise;
|
|
2588
2529
|
}
|
|
2589
2530
|
|
|
2590
2531
|
|
|
@@ -2603,60 +2544,22 @@ Napi::Value Servicer_read(const Napi::CallbackInfo& info) {
|
|
|
2603
2544
|
|
|
2604
2545
|
py::object* py_context = js_external_context.Value(info.Env()).Data();
|
|
2605
2546
|
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2547
|
+
return NodePromiseFromPythonTaskWithContext(
|
|
2548
|
+
info.Env(),
|
|
2549
|
+
"servicer._read(...) in nodejs",
|
|
2550
|
+
js_external_context,
|
|
2611
2551
|
[js_external_servicer, // Ensures `py_servicer` remains valid.
|
|
2612
2552
|
py_servicer,
|
|
2613
2553
|
js_external_context, // Ensures `py_context` remains valid.
|
|
2614
|
-
py_context
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
py_task.attr("add_done_callback")(py::cpp_function(
|
|
2624
|
-
[deferred = std::move(deferred),
|
|
2625
|
-
// NOTE: need to keep a reference to `py_task` so that it
|
|
2626
|
-
// doesn't get destroyed before completing!
|
|
2627
|
-
py_task = new py::object(py_task)](py::object py_future) {
|
|
2628
|
-
py::object py_exception = py_future.attr("exception")();
|
|
2629
|
-
|
|
2630
|
-
std::optional<std::string> json;
|
|
2631
|
-
std::optional<std::string> exception;
|
|
2632
|
-
|
|
2633
|
-
if (py_exception.is_none()) {
|
|
2634
|
-
py::str py_json = py_future.attr("result")();
|
|
2635
|
-
json.emplace(py_json);
|
|
2636
|
-
} else {
|
|
2637
|
-
py::str py_exception_string = py_exception_str(py_exception);
|
|
2638
|
-
|
|
2639
|
-
exception.emplace(py_exception_string);
|
|
2640
|
-
}
|
|
2641
|
-
|
|
2642
|
-
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
2643
|
-
[deferred = std::move(deferred),
|
|
2644
|
-
json = std::move(json),
|
|
2645
|
-
exception = std::move(exception)](Napi::Env env) {
|
|
2646
|
-
if (json.has_value()) {
|
|
2647
|
-
deferred->Resolve(
|
|
2648
|
-
Napi::String::New(env, *json));
|
|
2649
|
-
} else {
|
|
2650
|
-
deferred->Reject(
|
|
2651
|
-
Napi::Error::New(env, *exception).Value());
|
|
2652
|
-
}
|
|
2653
|
-
});
|
|
2654
|
-
|
|
2655
|
-
delete py_task;
|
|
2656
|
-
}));
|
|
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);
|
|
2657
2562
|
});
|
|
2658
|
-
|
|
2659
|
-
return promise;
|
|
2660
2563
|
}
|
|
2661
2564
|
|
|
2662
2565
|
|
|
@@ -2680,135 +2583,48 @@ Napi::Value Servicer_write(const Napi::CallbackInfo& info) {
|
|
|
2680
2583
|
|
|
2681
2584
|
std::string json_options = info[3].As<Napi::String>().Utf8Value();
|
|
2682
2585
|
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2586
|
+
return NodePromiseFromPythonTaskWithContext(
|
|
2587
|
+
info.Env(),
|
|
2588
|
+
"servicer._write(...) in nodejs",
|
|
2589
|
+
js_external_context,
|
|
2688
2590
|
[js_external_servicer, // Ensures `py_servicer` remains valid.
|
|
2689
2591
|
py_servicer,
|
|
2690
2592
|
js_external_context, // Ensures `py_context` remains valid.
|
|
2691
2593
|
py_context,
|
|
2692
2594
|
js_writer = std::move(js_writer),
|
|
2693
|
-
json_options = std::move(json_options)
|
|
2694
|
-
deferred = std::move(deferred)]() {
|
|
2595
|
+
json_options = std::move(json_options)]() {
|
|
2695
2596
|
py::object py_writer = py::cpp_function(
|
|
2696
2597
|
[js_writer = std::move(js_writer)](
|
|
2697
2598
|
std::string state_json) mutable {
|
|
2698
|
-
|
|
2699
|
-
py::module::import("asyncio").attr("Future")();
|
|
2700
|
-
|
|
2701
|
-
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
2599
|
+
return PythonFutureFromNodePromise(
|
|
2702
2600
|
[js_writer, // NOTE: need a _copy_ of
|
|
2703
2601
|
// `js_writer` here since
|
|
2704
2602
|
// `py_writer` may be called more
|
|
2705
2603
|
// than once!
|
|
2706
|
-
state_json
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
Napi::Object js_promise =
|
|
2710
|
-
js_writer
|
|
2711
|
-
.Value(env)
|
|
2712
|
-
.Call(
|
|
2713
|
-
env.Global(),
|
|
2714
|
-
{Napi::String::New(env, state_json)})
|
|
2715
|
-
.As<Napi::Object>();
|
|
2716
|
-
|
|
2717
|
-
js_promise
|
|
2718
|
-
.Get("then")
|
|
2719
|
-
.As<Napi::Function>()
|
|
2604
|
+
state_json](Napi::Env env) mutable {
|
|
2605
|
+
return js_writer
|
|
2606
|
+
.Value(env)
|
|
2720
2607
|
.Call(
|
|
2721
|
-
|
|
2722
|
-
{Napi::
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2728
|
-
[py_future, result]() {
|
|
2729
|
-
bool cancelled =
|
|
2730
|
-
py_future->attr("cancelled")()
|
|
2731
|
-
.cast<bool>();
|
|
2732
|
-
if (!cancelled) {
|
|
2733
|
-
py_future->attr("set_result")(
|
|
2734
|
-
result);
|
|
2735
|
-
}
|
|
2736
|
-
delete py_future;
|
|
2737
|
-
});
|
|
2738
|
-
}),
|
|
2739
|
-
Napi::Function::New(
|
|
2740
|
-
env,
|
|
2741
|
-
[py_future](const Napi::CallbackInfo& info) {
|
|
2742
|
-
std::string message = message_from_js_error(
|
|
2743
|
-
info[0].As<Napi::Object>());
|
|
2744
|
-
|
|
2745
|
-
adaptor->ScheduleCallbackOnPythonEventLoop(
|
|
2746
|
-
[py_future,
|
|
2747
|
-
message = std::move(message)]() {
|
|
2748
|
-
bool cancelled =
|
|
2749
|
-
py_future->attr("cancelled")()
|
|
2750
|
-
.cast<bool>();
|
|
2751
|
-
if (!cancelled) {
|
|
2752
|
-
py_future->attr("set_exception")(
|
|
2753
|
-
py::module::import("builtins")
|
|
2754
|
-
.attr("Exception")(
|
|
2755
|
-
message));
|
|
2756
|
-
}
|
|
2757
|
-
delete py_future;
|
|
2758
|
-
});
|
|
2759
|
-
})});
|
|
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());
|
|
2760
2614
|
});
|
|
2761
|
-
|
|
2762
|
-
return py_future;
|
|
2763
2615
|
});
|
|
2764
2616
|
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
py_task.attr("add_done_callback")(py::cpp_function(
|
|
2776
|
-
[deferred = std::move(deferred),
|
|
2777
|
-
// NOTE: need to keep a reference to `py_task` so that it
|
|
2778
|
-
// doesn't get destroyed before completing!
|
|
2779
|
-
py_task = new py::object(py_task)](py::object py_future) {
|
|
2780
|
-
py::object py_exception = py_future.attr("exception")();
|
|
2781
|
-
|
|
2782
|
-
std::optional<std::string> result;
|
|
2783
|
-
std::optional<std::string> exception;
|
|
2784
|
-
|
|
2785
|
-
if (py_exception.is_none()) {
|
|
2786
|
-
py::str py_result = py_future.attr("result")();
|
|
2787
|
-
result.emplace(py_result);
|
|
2788
|
-
} else {
|
|
2789
|
-
py::str py_exception_string = py_exception_str(py_exception);
|
|
2790
|
-
|
|
2791
|
-
exception.emplace(py_exception_string);
|
|
2792
|
-
}
|
|
2793
|
-
|
|
2794
|
-
adaptor->ScheduleCallbackOnNodeEventLoop(
|
|
2795
|
-
[deferred = std::move(deferred),
|
|
2796
|
-
result = std::move(result),
|
|
2797
|
-
exception = std::move(exception)](Napi::Env env) {
|
|
2798
|
-
if (result.has_value()) {
|
|
2799
|
-
deferred->Resolve(
|
|
2800
|
-
Napi::String::New(env, *result));
|
|
2801
|
-
} else {
|
|
2802
|
-
deferred->Reject(
|
|
2803
|
-
Napi::Error::New(env, *exception).Value());
|
|
2804
|
-
}
|
|
2805
|
-
});
|
|
2806
|
-
|
|
2807
|
-
delete py_task;
|
|
2808
|
-
}));
|
|
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);
|
|
2809
2627
|
});
|
|
2810
|
-
|
|
2811
|
-
return promise;
|
|
2812
2628
|
}
|
|
2813
2629
|
|
|
2814
2630
|
|
|
@@ -2874,6 +2690,14 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) {
|
|
|
2874
2690
|
Napi::String::New(env, "Context_iteration"),
|
|
2875
2691
|
Napi::Function::New<Context_iteration>(env));
|
|
2876
2692
|
|
|
2693
|
+
exports.Set(
|
|
2694
|
+
Napi::String::New(env, "Context_cookie"),
|
|
2695
|
+
Napi::Function::New<Context_cookie>(env));
|
|
2696
|
+
|
|
2697
|
+
exports.Set(
|
|
2698
|
+
Napi::String::New(env, "Context_appInternal"),
|
|
2699
|
+
Napi::Function::New<Context_appInternal>(env));
|
|
2700
|
+
|
|
2877
2701
|
exports.Set(
|
|
2878
2702
|
Napi::String::New(env, "Context_generateIdempotentStateId"),
|
|
2879
2703
|
Napi::Function::New<Context_generateIdempotentStateId>(env));
|
|
@@ -2891,8 +2715,8 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) {
|
|
|
2891
2715
|
Napi::Function::New<atLeastOrMostOnce>(env));
|
|
2892
2716
|
|
|
2893
2717
|
exports.Set(
|
|
2894
|
-
Napi::String::New(env, "
|
|
2895
|
-
Napi::Function::New<
|
|
2718
|
+
Napi::String::New(env, "Task_await"),
|
|
2719
|
+
Napi::Function::New<Task_await>(env));
|
|
2896
2720
|
|
|
2897
2721
|
exports.Set(
|
|
2898
2722
|
Napi::String::New(env, "ExternalContext_constructor"),
|