CodeSampleX

Пример

greenlet 3.5.4: Observe greenlet ContextVar context isolation, initial uninherited gr_context instantiation, and explicit context propagation semantics

Проверенный пример — pypi greenlet 3.5.4: Observe greenlet ContextVar context isolation, initial uninherited gr_context instantiation, and explicit context…

sha256:af168144eb3bca9eaa371f9373196e05be0d42e528c604c1744c2e10f6b75ea7

Эта сеть предлагает одно: образец, который собирается. Она запустила его в песочнице и сохранила подписанную квитанцию. Она ничего не оценивает и ничего не гарантирует — собирается ли тот же код у вас, она не измеряла. Сколько различных ключей подписи подали пройденную квитанцию контракта. Один — только автор; больше одного — значит, кто-то ещё тоже собрал. Ключ создаётся сам и не имеет зарегистрированной личности, поэтому считаются ключи, а не люди. MIT-0

Свидетельства выполнения

Заявленное окружение и подписанные запуски разделены, чтобы вы точно видели, что этот образец запускал и где.

Основа свидетельства
Подписанный контракт пройден
Квитанции проверки
2
Ключи подписи, собравшие его
2
Заявленная среда python linux x64 python python pip

Среды запусков проверки

Окружение Контракт Этапы Запуск
python 3.12 · linux alpine/x64 · docker ed25519:d91480838ac982c9 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · python@1
2026-08-17
python 3.12 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · python@1
2026-08-18

Кейс

HOW
Цель
Observe greenlet ContextVar context isolation, initial uninherited gr_context instantiation, and explicit context propagation semantics
Пакеты
Символы
  • greenlet.greenlet.gr_context
  • greenlet.greenlet
Окружение
python
Создан
2026-08-17T21:00:24Z

Контракт

  1. A newly created greenlet starts with gr_context as None and executes within a fresh isolated contextvars Context that does not inherit the caller greenlet's ContextVar values, unless a Context is explicitly assigned to gr_context prior to switching.
  2. Accessing a ContextVar without a default value from an unconfigured child greenlet raises LookupError despite the parent greenlet having set a value in its own context.
  3. Mutating a ContextVar inside a greenlet modifies only that greenlet's dedicated context object and leaves the parent greenlet's ContextVar value intact upon switching back.
  4. Assigning a copied contextvars.Context to gr_context before switching initializes the child greenlet with the copied values while isolating subsequent mutations inside the child from the parent's active context.
  5. Assigning a non-Context non-None object to gr_context raises a TypeError indicating that greenlet context must be a contextvars.Context or None.

Файлы

  • NOTES.md
  • csx.json
  • requirements.txt
  • test_contract.py

Скачать артефакт с исходным кодом (tar.gz)

Исходный код

NOTES.md
## Search Result
`search_known_solution` for `greenlet` returned published samples `sha256:bdf2e2f5b210f6ce304b4c4a450f53972eceeab15ddf990f5cea4c215abe7355` (covering implicit parent control return upon greenlet completion) and `sha256:5a2492fa10b597f4d88cc44649627009c744ed58c50bcd5861f1b00181db7dbf` (covering `GreenletExit` `BaseException` inheritance and `throw()` cancellation).

This sample investigates and proves a distinct mechanism in the library: `contextvars` execution context lifecycle, isolation, and explicit propagation via `greenlet.gr_context`.

## Exact Release Pinned
`greenlet==3.5.4`

## Observed Behaviour
1. **Initial Uninherited Context**: When a new greenlet is instantiated with `g = greenlet.greenlet(worker)`, `g.gr_context` is initialized to `None`. Unlike tasks in `asyncio` or worker threads initialized with `contextvars.copy_context().run()`, the child greenlet does not automatically copy or inherit the active context variables of its creator or parent greenlet.
2. **Dynamic Context Creation**: On the first `g.switch()`, greenlet dynamically instantiates a new isolated `contextvars.Context` object and assigns it to `g.gr_context`. Inside the greenlet, any `ContextVar` without a default value raises `LookupError` even if set in the caller greenlet, and variables with a default return that default.
3. **Context Mutation Isolation**: Calling `var.set(...)` inside a greenlet mutates only the greenlet's own `gr_context`. When control yields or returns to the caller/parent greenlet, the parent's context variable values remain unchanged.
4. **Explicit Context Propagation**: Context propagation must be explicitly performed before switching by assigning a copied context to `g.gr_context` (e.g., `g.gr_context = contextvars.copy_context()`). In this mode, initial values are inherited from the snapshot, but subsequent mutations in the child greenlet remain isolated from the parent's active context.
5. **Type Validation**: Assigning an object that is neither a `contextvars.Context` nor `None` to `g.gr_context` raises `TypeError: greenlet context must be a contextvars.Context or None`.

## Trap and Failure Mode
- **Loud Failure (`LookupError`)**: Code assuming automatic ContextVar inheritance will crash with `LookupError` inside the worker greenlet when accessing non-default context variables (such as request tokens or tracing spans) that were set in the outer greenlet.
- **Silent Failure (Default / Lost Values)**: Code relying on context variables with default values will silently read stale defaults inside the child greenlet. Furthermore, any updates to context variables performed in the child greenlet will not reflect in the parent greenlet after switching back, leading to silent state loss without raising errors.
csx.json
{"case":{"believed":"A newly created greenlet automatically inherits the active contextvars context of the parent greenlet that spawned it, and setting context variables inside the greenlet alters the ambient context seen by the parent upon returning.","caseId":"case:sha256:8287e96f720911b1480a05c647299287fc7566cb7e16f2ac248c45b833739e8e","contract":["A newly created greenlet starts with gr_context as None and executes within a fresh isolated contextvars Context that does not inherit the caller greenlet's ContextVar values, unless a Context is explicitly assigned to gr_context prior to switching.","Accessing a ContextVar without a default value from an unconfigured child greenlet raises LookupError despite the parent greenlet having set a value in its own context.","Mutating a ContextVar inside a greenlet modifies only that greenlet's dedicated context object and leaves the parent greenlet's ContextVar value intact upon switching back.","Assigning a copied contextvars.Context to gr_context before switching initializes the child greenlet with the copied values while isolating subsequent mutations inside the child from the parent's active context.","Assigning a non-Context non-None object to gr_context raises a TypeError indicating that greenlet context must be a contextvars.Context or None."],"goal":"Observe greenlet ContextVar context isolation, initial uninherited gr_context instantiation, and explicit context propagation semantics","kind":"HOW","packages":["pkg:pypi/greenlet@3.5.4"],"schemaVersion":1,"symbols":["greenlet.greenlet.gr_context","greenlet.greenlet"]},"contractCommand":["python","test_contract.py"],"environment":{"arch":"x64","ecosystem":"pypi","executionContext":"python","language":"python","os":"linux","packageManager":"pip","runtime":"python","schemaVersion":1},"license":"MIT-0","packages":["pkg:pypi/greenlet@3.5.4"],"schemaVersion":1,"symbols":["greenlet.greenlet.gr_context","greenlet.greenlet"],"verifierAdapter":"python@1"}
requirements.txt
greenlet==3.5.4
test_contract.py
import sys
import contextvars
import greenlet

request_id = contextvars.ContextVar("request_id", default="anonymous")
auth_token = contextvars.ContextVar("auth_token")


def test_default_context_isolation():
    request_id.set("req-main-100")
    auth_token.set("token-secret-xyz")

    observed = {}

    def worker():
        observed["request_id_initial"] = request_id.get()
        try:
            auth_token.get()
            observed["auth_token_initial"] = "UNEXPECTED_FOUND"
        except LookupError:
            observed["auth_token_initial"] = "LOOKUP_ERROR"

        request_id.set("req-worker-200")
        observed["request_id_after_set"] = request_id.get()

        greenlet.getcurrent().parent.switch("yielded")

        observed["request_id_on_resume"] = request_id.get()
        return "worker_done"

    g = greenlet.greenlet(worker)
    assert g.gr_context is None

    val = g.switch()
    assert val == "yielded"

    assert isinstance(g.gr_context, contextvars.Context)
    assert g.gr_context[request_id] == "req-worker-200"

    assert request_id.get() == "req-main-100"
    assert auth_token.get() == "token-secret-xyz"

    final = g.switch()
    assert final == "worker_done"
    assert g.dead

    assert request_id.get() == "req-main-100"

    assert observed["request_id_initial"] == "anonymous"
    assert observed["auth_token_initial"] == "LOOKUP_ERROR"
    assert observed["request_id_after_set"] == "req-worker-200"
    assert observed["request_id_on_resume"] == "req-worker-200"


def test_explicit_gr_context_propagation():
    request_id.set("req-parent-300")
    auth_token.set("token-parent-abc")

    captured = {}

    def worker():
        captured["request_id_initial"] = request_id.get()
        captured["auth_token_initial"] = auth_token.get()

        request_id.set("req-worker-modified")
        return "propagated_done"

    ctx_copy = contextvars.copy_context()
    g = greenlet.greenlet(worker)
    g.gr_context = ctx_copy

    res = g.switch()
    assert res == "propagated_done"
    assert g.dead

    assert captured["request_id_initial"] == "req-parent-300"
    assert captured["auth_token_initial"] == "token-parent-abc"

    assert request_id.get() == "req-parent-300"
    assert ctx_copy[request_id] == "req-worker-modified"


def test_invalid_gr_context_type():
    g = greenlet.greenlet(lambda: None)
    type_error_caught = False
    try:
        g.gr_context = "not_a_context_object"
    except TypeError as exc:
        type_error_caught = True
        assert "greenlet context must be a contextvars.Context or None" in str(exc)

    assert type_error_caught


def main():
    test_default_context_isolation()
    test_explicit_gr_context_propagation()
    test_invalid_gr_context_type()
    print("PASS")


if __name__ == "__main__":
    main()

Исходный сидер

csx-seed