log-foundry 0.10.2.dev61__tar.gz → 0.10.2.dev63__tar.gz
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.
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/PKG-INFO +4 -1
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/README.md +3 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/pyproject.toml +1 -1
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/_fork.py +100 -14
- log_foundry-0.10.2.dev63/src/log_foundry/_lifecycle.py +734 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/config.py +8 -1
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/decorator.py +43 -1
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/base.py +24 -6
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/file.py +10 -5
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/filtering.py +1 -1
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/logstash.py +1 -1
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/multi.py +1 -1
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/sentry.py +1 -1
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/transform.py +1 -1
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/worker.py +18 -0
- log_foundry-0.10.2.dev61/src/log_foundry/_lifecycle.py +0 -212
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/LICENSE +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/__init__.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/_diag.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/api.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/console.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/context.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/ids.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/model.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/py.typed +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/results.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sanitize.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/__init__.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/_batch.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/_chunk.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/_retry.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/_socket.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/_time.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/callback.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/clickhouse.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/datadog.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/elasticsearch.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/eventhubs.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/firehose.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/honeycomb.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/http.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/kafka.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/kinesis.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/logging_sink.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/loki.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/memory.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/mongodb.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/nats.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/newrelic.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/null.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/postgres.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/pubsub.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/rabbitmq.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/redis.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/sns.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/splunk.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/sqlite.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/sqs.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/stdout.py +0 -0
- {log_foundry-0.10.2.dev61 → log_foundry-0.10.2.dev63}/src/log_foundry/sinks/syslog.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: log-foundry
|
|
3
|
-
Version: 0.10.2.
|
|
3
|
+
Version: 0.10.2.dev63
|
|
4
4
|
Summary: Generate logs for your console and JSON events for downstream consumption.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -951,6 +951,8 @@ if (
|
|
|
951
951
|
|
|
952
952
|
`closing_sinks` is deliberately not a term here: it is briefly non-zero during a perfectly healthy
|
|
953
953
|
sink swap, so a single reading is not a fault. Watch it over time instead — see the table below.
|
|
954
|
+
`inherited_sink` is not a term either, for a different reason: it reports a *state* the process is
|
|
955
|
+
in rather than a loss it took, and in a prefork deployment it is `True` on every worker by design.
|
|
954
956
|
|
|
955
957
|
They tell you different things, and they want different responses:
|
|
956
958
|
|
|
@@ -963,6 +965,7 @@ They tell you different things, and they want different responses:
|
|
|
963
965
|
| `sink.failed` | The sink attempted delivery and could not confirm it — abandoned requests, partially-failed batches, responses it could not adjudicate. | Fix the destination. |
|
|
964
966
|
| `retired` + `submitted_after_shutdown` | `shutdown()` was called and the process **kept logging**. Those events are queued where nothing will drain them — total loss, for as long as the process runs. | Use `flush()`, not `shutdown()`, in a process that logs again. This is the serverless mistake below. |
|
|
965
967
|
| `incomplete_swaps` | A late `configure(sink=...)` could not confirm the previous sink was drained. The swap took effect; that sink was left open and some queued events may have gone to the new one. | Investigate the previous sink — it was hung or failing. Configure the sink before the first log where you can. |
|
|
968
|
+
| `inherited_sink` | This process is delivering to a sink it **inherited across a `fork`** and may not release, so it will not be closed here. Not a loss and not an alert term. | Nothing, usually. It explains a handle still open after `shutdown()`, and tells you a deployment shares one sink across a fork at all. `True` for a shared `StdoutSink` too, whose `close()` only flushes — so a `True` is not by itself evidence that anything is held. If you want the child to own its transport, build the sink in the worker process (see Forking). |
|
|
966
969
|
| `closing_sinks` | Swapped-out sinks inside `close()` **right now** — a live gauge, not a counter, and the only field that falls as well as rises. Non-zero on a single read is normal during a swap. | Nothing, unless it stays non-zero. That means a destination is stuck in `close()` and will not release its resources. |
|
|
967
970
|
|
|
968
971
|
`h.sink` is a `SinkLosses(dropped, failed)` or `None` — `None` when no worker exists yet, or when
|
|
@@ -915,6 +915,8 @@ if (
|
|
|
915
915
|
|
|
916
916
|
`closing_sinks` is deliberately not a term here: it is briefly non-zero during a perfectly healthy
|
|
917
917
|
sink swap, so a single reading is not a fault. Watch it over time instead — see the table below.
|
|
918
|
+
`inherited_sink` is not a term either, for a different reason: it reports a *state* the process is
|
|
919
|
+
in rather than a loss it took, and in a prefork deployment it is `True` on every worker by design.
|
|
918
920
|
|
|
919
921
|
They tell you different things, and they want different responses:
|
|
920
922
|
|
|
@@ -927,6 +929,7 @@ They tell you different things, and they want different responses:
|
|
|
927
929
|
| `sink.failed` | The sink attempted delivery and could not confirm it — abandoned requests, partially-failed batches, responses it could not adjudicate. | Fix the destination. |
|
|
928
930
|
| `retired` + `submitted_after_shutdown` | `shutdown()` was called and the process **kept logging**. Those events are queued where nothing will drain them — total loss, for as long as the process runs. | Use `flush()`, not `shutdown()`, in a process that logs again. This is the serverless mistake below. |
|
|
929
931
|
| `incomplete_swaps` | A late `configure(sink=...)` could not confirm the previous sink was drained. The swap took effect; that sink was left open and some queued events may have gone to the new one. | Investigate the previous sink — it was hung or failing. Configure the sink before the first log where you can. |
|
|
932
|
+
| `inherited_sink` | This process is delivering to a sink it **inherited across a `fork`** and may not release, so it will not be closed here. Not a loss and not an alert term. | Nothing, usually. It explains a handle still open after `shutdown()`, and tells you a deployment shares one sink across a fork at all. `True` for a shared `StdoutSink` too, whose `close()` only flushes — so a `True` is not by itself evidence that anything is held. If you want the child to own its transport, build the sink in the worker process (see Forking). |
|
|
930
933
|
| `closing_sinks` | Swapped-out sinks inside `close()` **right now** — a live gauge, not a counter, and the only field that falls as well as rises. Non-zero on a single read is normal during a swap. | Nothing, unless it stays non-zero. That means a destination is stuck in `close()` and will not release its resources. |
|
|
931
934
|
|
|
932
935
|
`h.sink` is a `SinkLosses(dropped, failed)` or `None` — `None` when no worker exists yet, or when
|
|
@@ -37,6 +37,36 @@ and all three would have to import this module for it to reach them; instead the
|
|
|
37
37
|
it, so this module imports nothing that imports it and there is no cycle to break later.
|
|
38
38
|
"""
|
|
39
39
|
|
|
40
|
+
_FORK_SKIP = ("reacquired_in_child",)
|
|
41
|
+
"""This module's own opt-out from the walk below (:data:`_SKIP_ATTRIBUTE`).
|
|
42
|
+
|
|
43
|
+
:data:`reacquired_in_child` holds **sinks**, so without this the next fork's walk enters the
|
|
44
|
+
previous fork's roster and re-collects every sink in it — including ones ``configure()``
|
|
45
|
+
superseded in between. Measured: a child's walk collected a sink two ``configure()`` calls out
|
|
46
|
+
of date, its hook was called, and the reclaim that follows then stamped it for the grandchild's
|
|
47
|
+
pid, so a sink no descendant ever acquired read ``releasable``. A ratchet, not a one-off: once a
|
|
48
|
+
hook-implementing sink enters a roster, every descendant's walk finds it again.
|
|
49
|
+
|
|
50
|
+
Exactly the hazard ``_lifecycle._owned`` already declares an opt-out for, reached by adding a
|
|
51
|
+
second global that holds sinks. The rebind below is the belt to this brace.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
reacquired_in_child: list[Any] = []
|
|
55
|
+
"""Sinks whose re-acquisition hook returned normally in this child (SPEC-042 FR-005).
|
|
56
|
+
|
|
57
|
+
Published rather than acted on, because what it means is an *ownership* claim and ownership is
|
|
58
|
+
``_lifecycle``'s: a sink that returns from the hook holds a transport of its own, so a child may
|
|
59
|
+
release it.
|
|
60
|
+
|
|
61
|
+
Rebound — never mutated in place — **before the repair walk runs**, so a second fork cannot
|
|
62
|
+
inherit the first one's roster even if the opt-out above were removed. The first version of this
|
|
63
|
+
claimed the rebind happened "at the start of every child repair" while doing it in the middle,
|
|
64
|
+
after the walk; both halves were false and nothing tested either.
|
|
65
|
+
|
|
66
|
+
This module still imports nothing but ``_diag`` (SPEC-039 FR-006); the reader registers a
|
|
67
|
+
handler and reads this, which is the same inversion :data:`_child_handlers` exists for.
|
|
68
|
+
"""
|
|
69
|
+
|
|
40
70
|
_installed = False
|
|
41
71
|
"""Whether :func:`install` has already registered the child handler in this process.
|
|
42
72
|
|
|
@@ -330,8 +360,12 @@ def _fresh_primitive(value: Any, memo: dict[int, Any], keepalive: list[Any]) ->
|
|
|
330
360
|
return fresh
|
|
331
361
|
|
|
332
362
|
|
|
333
|
-
|
|
334
|
-
"""The optional sink member a forked child asks to
|
|
363
|
+
_REACQUIRE_HOOK = "reacquire_after_fork"
|
|
364
|
+
"""The optional sink member a forked child asks to take its transport over.
|
|
365
|
+
|
|
366
|
+
Named for the larger half of what it does (SPEC-042 FR-005): stranding the parent's pending
|
|
367
|
+
bytes is one consequence, and re-acquiring the transport is the step — a sink that returns from
|
|
368
|
+
it has claimed the transport as this process's own, which is what makes releasing it safe.
|
|
335
369
|
|
|
336
370
|
Probed by name, as ``losses()`` and ``log_foundry_stop_signal`` are, so no existing sink stops
|
|
337
371
|
satisfying ``Sink`` by not having it. ``sinks/base.py`` states the contract; this is the only
|
|
@@ -339,7 +373,7 @@ place the name is read.
|
|
|
339
373
|
"""
|
|
340
374
|
|
|
341
375
|
|
|
342
|
-
def
|
|
376
|
+
def _offers_reacquire(holder: object) -> bool:
|
|
343
377
|
"""Whether this object carries FR-004's buffer hook, without letting the question raise.
|
|
344
378
|
|
|
345
379
|
Every other read the walk makes is guarded individually, and this one has to be too: an
|
|
@@ -361,11 +395,48 @@ def _offers_discard(holder: object) -> bool:
|
|
|
361
395
|
None.
|
|
362
396
|
"""
|
|
363
397
|
try:
|
|
364
|
-
return callable(getattr(holder,
|
|
398
|
+
return callable(getattr(holder, _REACQUIRE_HOOK, None))
|
|
365
399
|
except Exception:
|
|
366
400
|
return False
|
|
367
401
|
|
|
368
402
|
|
|
403
|
+
_SKIP_ATTRIBUTE = "_FORK_SKIP"
|
|
404
|
+
"""A module's opt-out: attribute names the walk must not read or descend into.
|
|
405
|
+
|
|
406
|
+
**Bookkeeping that pins objects is not live state to repair**, and the difference is invisible
|
|
407
|
+
to a walk that only sees a container. ``_lifecycle._owned`` is the case: SPEC-042 FR-001 requires
|
|
408
|
+
it to hold a **strong** reference to every sink this process ever acquired, so without an opt-out
|
|
409
|
+
the walk reaches sinks the process abandoned several ``configure()`` calls ago — replacing their
|
|
410
|
+
locks, which is merely wasteful, and calling their fork hooks, which is not. Measured: a forked
|
|
411
|
+
child announced a buffer-discard failure for a sink that had been superseded, and a ``FileSink``
|
|
412
|
+
in that state would have its file reopened on every fork for the life of the process.
|
|
413
|
+
|
|
414
|
+
Declared by the module that owns the state rather than listed here, which keeps this module's
|
|
415
|
+
rule that it imports nothing but ``_diag`` (SPEC-039 FR-006). Nothing is lost by skipping: a sink
|
|
416
|
+
that is still *live* is reached through the config and the worker, which are not opted out.
|
|
417
|
+
"""
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def _skipped_names(holder: object) -> frozenset[str]:
|
|
421
|
+
"""Returns the attribute names a holder has opted out of the walk.
|
|
422
|
+
|
|
423
|
+
Args:
|
|
424
|
+
holder: The module, class or instance the walk is about to read.
|
|
425
|
+
|
|
426
|
+
Returns:
|
|
427
|
+
The opted-out names, empty for anything that declares none.
|
|
428
|
+
|
|
429
|
+
Raises:
|
|
430
|
+
None. A malformed or unreadable declaration opts nothing out, so the walk does more work
|
|
431
|
+
rather than less — the safe direction for a repair whose absence is a hang.
|
|
432
|
+
"""
|
|
433
|
+
try:
|
|
434
|
+
declared = getattr(holder, _SKIP_ATTRIBUTE, None)
|
|
435
|
+
return frozenset(declared) if declared else frozenset()
|
|
436
|
+
except Exception:
|
|
437
|
+
return frozenset()
|
|
438
|
+
|
|
439
|
+
|
|
369
440
|
def _reinit_primitives() -> list[Any]:
|
|
370
441
|
"""Replaces every lock and event this package owns, wherever the walk reaches one.
|
|
371
442
|
|
|
@@ -391,7 +462,7 @@ def _reinit_primitives() -> list[Any]:
|
|
|
391
462
|
None.
|
|
392
463
|
|
|
393
464
|
Returns:
|
|
394
|
-
The owned instances carrying :data:`
|
|
465
|
+
The owned instances carrying :data:`_REACQUIRE_HOOK`, in the order the walk reached them.
|
|
395
466
|
|
|
396
467
|
Raises:
|
|
397
468
|
None.
|
|
@@ -415,9 +486,12 @@ def _reinit_primitives() -> list[Any]:
|
|
|
415
486
|
if not isinstance(holder, types.ModuleType | type):
|
|
416
487
|
if not _is_owned(holder):
|
|
417
488
|
continue
|
|
418
|
-
if
|
|
489
|
+
if _offers_reacquire(holder):
|
|
419
490
|
buffered.append(holder)
|
|
491
|
+
skip = _skipped_names(holder)
|
|
420
492
|
for name, value in _namespace_items(holder):
|
|
493
|
+
if name in skip:
|
|
494
|
+
continue
|
|
421
495
|
fresh = _fresh_primitive(value, memo, keepalive)
|
|
422
496
|
if fresh is not None:
|
|
423
497
|
_assign(holder, name, fresh)
|
|
@@ -426,19 +500,25 @@ def _reinit_primitives() -> list[Any]:
|
|
|
426
500
|
return buffered
|
|
427
501
|
|
|
428
502
|
|
|
429
|
-
def
|
|
430
|
-
"""Asks each sink that owns a buffered stream to
|
|
503
|
+
def _reacquire_transports(holders: list[Any]) -> list[Any]:
|
|
504
|
+
"""Asks each sink that owns a buffered stream to re-acquire it in this child (FR-004).
|
|
431
505
|
|
|
432
506
|
A fork landing inside ``emit`` — after the write loop, before the flush — leaves the child
|
|
433
507
|
holding the parent's unflushed bytes, which both processes then write: measured, the event
|
|
434
508
|
at the fork point appeared on disk twice. Without a ``before`` handler there is nowhere to
|
|
435
|
-
empty the buffer from (FR-001), so the child
|
|
509
|
+
empty the buffer from (FR-001), so the child re-opens instead, which strands the copy.
|
|
510
|
+
|
|
511
|
+
**Which hooks returned normally is published rather than acted on** (SPEC-042 FR-005). A
|
|
512
|
+
sink that returns from the hook has claimed the transport as this process's own, which is an
|
|
513
|
+
ownership fact — but ownership lives in ``_lifecycle``, and this module imports nothing but
|
|
514
|
+
``_diag``. So the roster is handed back and the handler ``_lifecycle`` registers re-stamps
|
|
515
|
+
it.
|
|
436
516
|
|
|
437
517
|
Args:
|
|
438
518
|
holders: What :func:`_reinit_primitives` collected on its walk.
|
|
439
519
|
|
|
440
520
|
Returns:
|
|
441
|
-
|
|
521
|
+
The holders whose hook returned normally, in the order they were called.
|
|
442
522
|
|
|
443
523
|
Raises:
|
|
444
524
|
None. One sink's failure is absorbed separately from the rest, so a hook that cannot
|
|
@@ -446,15 +526,19 @@ def _discard_buffers(holders: list[Any]) -> None:
|
|
|
446
526
|
sink its repair — and the consequence is named, because an absorbed failure is
|
|
447
527
|
invisible apart from that line.
|
|
448
528
|
"""
|
|
529
|
+
reacquired: list[Any] = []
|
|
449
530
|
for holder in holders:
|
|
450
531
|
try:
|
|
451
|
-
getattr(holder,
|
|
532
|
+
getattr(holder, _REACQUIRE_HOOK)()
|
|
533
|
+
reacquired.append(holder)
|
|
452
534
|
except Exception as exc:
|
|
453
535
|
_diag.absorbed(
|
|
454
|
-
"
|
|
536
|
+
"re-acquiring a transport after a fork",
|
|
455
537
|
exc,
|
|
456
|
-
f"{type(holder).__name__} may write the parent's pending bytes again"
|
|
538
|
+
f"{type(holder).__name__} may write the parent's pending bytes again, and this "
|
|
539
|
+
f"child will not release it",
|
|
457
540
|
)
|
|
541
|
+
return reacquired
|
|
458
542
|
|
|
459
543
|
|
|
460
544
|
def register_child_handler(fn: Callable[[], None]) -> None:
|
|
@@ -515,12 +599,14 @@ def _reinit_after_fork() -> None:
|
|
|
515
599
|
registers another simply causes it to run — and :func:`register_child_handler` is the
|
|
516
600
|
only writer, appending only.
|
|
517
601
|
"""
|
|
602
|
+
global reacquired_in_child
|
|
603
|
+
reacquired_in_child = []
|
|
518
604
|
buffered: list[Any] = []
|
|
519
605
|
try:
|
|
520
606
|
buffered = _reinit_primitives()
|
|
521
607
|
except Exception as exc:
|
|
522
608
|
_diag.absorbed("repairing the library after a fork", exc, "this child may block or lose")
|
|
523
|
-
|
|
609
|
+
reacquired_in_child = _reacquire_transports(buffered)
|
|
524
610
|
for handler in _child_handlers:
|
|
525
611
|
try:
|
|
526
612
|
handler()
|