コード例 #1
0
def test_context_inner_function_reassigning_deleted_value_on_deletion_of_reassignemnt_should_not_see_outer_value(
):
    context_keys = set()

    ctx = ContextLocal()

    @ctx
    def testcall():
        ctx.var1 = 2
        assert ctx.var1 == 2
        del ctx.var1

        with pytest.raises(AttributeError):
            ctx.var1
        ctx.var1 = 3
        assert ctx.var1 == 3
        del ctx.var1
        # Previously deleted value should remain "deleted"
        with pytest.raises(AttributeError):
            ctx.var1

    ctx.var1 = 1
    testcall()
    # Value deleted in inner context must be available here
    assert ctx.var1 == 1
コード例 #2
0
def test_context_isolates_async_loop():

    ctx = ContextLocal()
    ctx.aa = 1
    results = []

    @ctx
    async def aiter():
        ctx.aa = 3
        assert ctx.aa == 3
        print(ctx.aa)
        assert ctx.aa == 3
        ctx.aa += 1
        assert ctx.aa == 4
        yield 2
        assert ctx.aa == 4

    @ctx
    async def entry():
        ctx.aa = 2
        counter = 0
        async for i in aiter():
            assert ctx.aa == 2 + counter
            ctx.aa += 10
            counter += 10

    asyncio.run(entry())
    assert ctx.aa == 1
コード例 #3
0
def test_context_local_in_with_block_dont_mixup_with_other_context():

    ctx1 = ContextLocal()
    ctx2 = ContextLocal()

    ctx1.value = 1
    ctx2.value = 2

    with ctx1:
        ctx1.value = 3
        with ctx2:
            assert ctx1.value == 3
            ctx2.value = 4
            assert ctx1.value == 3
            assert ctx2.value == 4
            with ctx1:
                ctx1.value = 5
                assert ctx1.value == 5
                assert ctx2.value == 4
            assert ctx2.value == 4
            assert ctx1.value == 3

        assert ctx1.value == 3
        assert ctx2.value == 2

    assert ctx1.value == 1
    assert ctx2.value == 2
コード例 #4
0
def test_context_local_enter_new_context_in_with_block():

    ctx = ContextLocal()

    ctx.value = 1

    with ctx:
        ctx.value = 2
        assert ctx.value == 2

    assert ctx.value == 1
コード例 #5
0
def test_context_local_in_with_block_can_see_outside_values():

    ctx = ContextLocal()

    ctx.value = 1

    with ctx:
        assert ctx.value == 1
        ctx.value = 2
        assert ctx.value == 2

    assert ctx.value == 1
コード例 #6
0
def test_context_run_method_isolates_context():
    context_keys = set()

    ctx = ContextLocal()

    def testcall():
        assert ctx.var1 == 1
        ctx.var1 = 2
        assert ctx.var1 == 2
        del ctx.var1

    ctx.var1 = 1
    assert ctx.var1 == 1
    ctx._run(testcall)
    assert ctx.var1 == 1
コード例 #7
0
def test_context_local_async_reflect_changes_made_downstream():


    ctx = ContextLocal()

    results = set()

    @ctx
    async def worker(value):
        ctx.value = value
        results.add(ctx.value)
        await second_stage_worker()
        assert ctx.value == value + 1

    async def second_stage_worker():
        await asyncio.sleep((10 - ctx.value) * 0.01)
        ctx.value += 1
        results.add(ctx.value)

    @ctx
    def manager():
        ctx.value = -1
        tasks = asyncio.gather(*(worker(i) for i in range(0, 10, 2)))
        loop = asyncio.get_event_loop()
        loop.run_until_complete(tasks)
        assert all(i in results for i in range(10))
        assert ctx.value == -1

    manager()
コード例 #8
0
def test_context_local_vars_work_for_async():


    ctx = ContextLocal()

    results = set()

    @ctx
    async def worker(value):
        ctx.value = value
        await asyncio.sleep((10 - value) * 0.01)

        assert value == ctx.value
        results.add(ctx.value)

    @ctx
    def manager():
        ctx.value = -1
        tasks = asyncio.gather(*(worker(i) for i in range(10)))
        loop = asyncio.get_event_loop()
        loop.run_until_complete(tasks)
        assert all(i in results for i in range(10))
        assert ctx.value == -1

    manager()
コード例 #9
0
def test_context_local_vars_work_for_threads():

    ctx = ContextLocal()

    results = set()

    @ctx
    def worker(value):
        ctx.value = value
        time.sleep((10 - value) * 0.01)
        assert value == ctx.value
        results.add(ctx.value)

    @ctx
    def manager():
        ctx.value = -1
        tasks = [
            threading.Thread(target=worker, args=(i, )) for i in range(10)
        ]
        consume(t.start() for t in tasks)
        consume(t.join() for t in tasks)
        assert all(i in results for i in range(10))
        assert ctx.value == -1

    manager()
コード例 #10
0
def test_context_in_with_block_deleted_var_must_be_undeleted_outside():

    ctx = ContextLocal()

    ctx.value = 1

    with ctx:
        ctx.value = 2
        assert ctx.value == 2
        del ctx.value
        assert ctx.value == 1
        del ctx.value
        with pytest.raises(AttributeError):
            assert ctx.value

    assert ctx.value == 1
コード例 #11
0
def test_context_local_doesnt_leak_from_generator():
    ctx = ContextLocal()

    @ctx
    def gen():
        ctx.value = 2
        yield
        assert ctx.value == 2

    ctx.value = 1
    g = gen()
    assert ctx.value == 1
    next(g)
    assert ctx.value == 1
    next(g, None)
    assert ctx.value == 1
コード例 #12
0
def test_contexts_keep_separate_variables():
    c1 = ContextLocal()
    c2 = ContextLocal()

    @c1
    @c2
    def inner():
        c1.a = 1
        c2.a = 2
        assert c1.a == 1
        assert c2.a == 2
        del c2.a
        with pytest.raises(AttributeError):
            c2.a
        assert c1.a == 1

    inner()
コード例 #13
0
def test_context_dir():

    ctx = ContextLocal()

    @ctx
    def testcall():

        ctx.var2 = 2
        assert "var1" in dir(ctx)
        assert "var2" in dir(ctx)

        del ctx.var1

    ctx.var1 = 1
    assert "var1" in dir(ctx)
    testcall()
    assert "var1" in dir(ctx)
    assert "var2" not in dir(ctx)
コード例 #14
0
def test_context_inner_function_deleting_attribute_can_reassign_it():
    context_keys = set()

    ctx = ContextLocal()

    @ctx
    def testcall():
        ctx.var1 = 2
        assert ctx.var1 == 2
        del ctx.var1
        with pytest.raises(AttributeError):
            ctx.var1
        ctx.var1 = 3
        assert ctx.var1 == 3

    ctx.var1 = 1
    testcall()
    # Value deleted in inner context must be available here
    assert ctx.var1 == 1
コード例 #15
0
def test_context_local_vars_work_as_decorator():
    ctx = ContextLocal()

    @ctx
    def func():
        ctx.value = 1
        assert ctx.value == 1

    func()
    with pytest.raises(AttributeError):
        assert ctx.value == 1
コード例 #16
0
def test_each_call_creates_unique_context_and_clean_up():
    context_keys = set()

    ctx = ContextLocal()

    @ctx
    def testcall():
        context_keys.update(ctx._registry.keys())

    for i in range(10):
        testcall()

    assert len(context_keys) == 10
    assert len(list(ctx._registry.keys())) == 0
コード例 #17
0
def test_context_granddaugher_works_nice_with_daughter_deleting_attribute():
    context_keys = set()

    ctx = ContextLocal()

    @ctx
    def granddaughter():
        with pytest.raises(AttributeError):
            ctx.var1
        ctx.var1 = 2
        assert ctx.var1 == 2

    @ctx
    def daughter():
        assert ctx.var1 == 1
        del ctx.var1
        granddaughter()
        with pytest.raises(AttributeError):
            ctx.var1

    ctx.var1 = 1
    daughter()
    assert ctx.var1 == 1
コード例 #18
0
def test_unique_context_for_generators_is_cleaned_up():
    context_keys = set()

    ctx = ContextLocal()

    @ctx
    def testcall():
        context_keys.update(k.value for k in ctx._registry.keys())
        yield None

    for i in range(100):
        for _ in testcall():
            pass
    gc.collect()

    assert len(context_keys) == 100
    assert len(list(ctx._registry.keys())) == 0
コード例 #19
0
def test_context_local_vars_work_for_generators():

    ctx = ContextLocal()

    results = []

    @contextmanager
    def use_mode(mode):
        previous = ctx.mode
        ctx.mode = mode
        try:
            yield
        finally:
            ctx.mode = previous

    @ctx
    def first():
        ctx.mode = 0
        results.append(("starting", ctx.mode))
        with use_mode(1):
            results.append(("entered first", ctx.mode))
            it = second()
            next(it)
            results.append(("back in first", ctx.mode))
            next(it, None)
            results.append(("ended second", ctx.mode))
        results.append(("exited first context manager", ctx.mode))

    @ctx
    def second():
        with use_mode(2):
            results.append(("entered second", ctx.mode))
            yield
            results.append(("back in second", ctx.mode))
        results.append(("exited second context manager", ctx.mode))

    first()
    assert results == [('starting', 0), ('entered first', 1),
                       ('entered second', 2), ('back in first', 1),
                       ('back in second', 2),
                       ('exited second context manager', 1),
                       ('ended second', 1),
                       ('exited first context manager', 0)]
コード例 #20
0
def test_context_local_enter_new_context_in_nested_with_blocks():

    ctx = ContextLocal()

    ctx.value = 1

    with ctx:
        ctx.value = 2
        with ctx:
            ctx.value = 3
            with ctx:
                ctx.value = 4
                assert ctx.value == 4
            assert ctx.value == 3
        assert ctx.value == 2

    assert ctx.value == 1
コード例 #21
0
https://stackoverflow.com/questions/53611690/how-do-i-write-consistent-stateful-context-managers/57448146


"""

# Code demonstrating how generators
# entered in different calls can each
# have a separate context

from contextlib import contextmanager
#from contextvars import ContextVar, Context, copy_context

from extracontext import ContextLocal

ctx = ContextLocal()


@contextmanager
def use_mode(mode):
    ctx.MODE = mode
    print("entering use_mode")
    print_mode()
    try:
        yield
    finally:

        pass


def print_mode():