import asyncio import copy import math import operator import time from contextvars import ContextVar from threading import Thread import pytest from werkzeug import local # Since the tests are creating local instances, use global context vars # to avoid accumulating anonymous context vars that can't be collected. _cv_ns = ContextVar("werkzeug.tests.ns") _cv_stack = ContextVar("werkzeug.tests.stack") _cv_val = ContextVar("werkzeug.tests.val") @pytest.fixture(autouse=True) def reset_context_vars(): ns_token = _cv_ns.set({}) stack_token = _cv_stack.set([]) yield _cv_ns.reset(ns_token) _cv_stack.reset(stack_token) def test_basic_local(): ns = local.Local(_cv_ns) ns.foo = 0 values = [] def value_setter(idx): time.sleep(0.01 * idx) ns.foo = idx time.sleep(0.02) values.append(ns.foo) threads = [Thread(target=value_setter, args=(x,)) for x in [1, 2, 3]] for thread in threads: thread.start() for thread in threads: thread.join() assert sorted(values) == [1, 2, 3] def delfoo(): del ns.foo delfoo() pytest.raises(AttributeError, lambda: ns.foo) pytest.raises(AttributeError, delfoo) local.release_local(ns) def test_basic_local_asyncio(): ns = local.Local(_cv_ns) ns.foo = 0 values = [] async def value_setter(idx): await asyncio.sleep(0.01 * idx) ns.foo = idx await asyncio.sleep(0.02) values.append(ns.foo) async def main(): futures = [asyncio.ensure_future(value_setter(i)) for i in [1, 2, 3]] await asyncio.gather(*futures) asyncio.run(main()) assert sorted(values) == [1, 2, 3] def delfoo(): del ns.foo delfoo() pytest.raises(AttributeError, lambda: ns.foo) pytest.raises(AttributeError, delfoo) local.release_local(ns) def test_local_release(): ns = local.Local(_cv_ns) ns.foo = 42 local.release_local(ns) assert not hasattr(ns, "foo") ls = local.LocalStack(_cv_stack) ls.push(42) local.release_local(ls) assert ls.top is None def test_local_stack(): ls = local.LocalStack(_cv_stack) assert ls.top is None ls.push(42) assert ls.top == 42 ls.push(23) assert ls.top == 23 ls.pop() assert ls.top == 42 ls.pop() assert ls.top is None assert ls.pop() is None assert ls.pop() is None proxy = ls() ls.push([1, 2]) assert proxy == [1, 2] ls.push((1, 2)) assert proxy == (1, 2) ls.pop() ls.pop() assert repr(proxy) == "" def test_local_stack_asyncio(): ls = local.LocalStack(_cv_stack) ls.push(1) async def task(): ls.push(1) assert len(ls._storage.get()) == 2 async def main(): futures = [asyncio.ensure_future(task()) for _ in range(3)] await asyncio.gather(*futures) asyncio.run(main()) def test_proxy_local(): ns = local.Local(_cv_ns) ns.foo = [] p = local.LocalProxy(ns, "foo") p.append(42) p.append(23) p[1:] = [1, 2, 3] assert p == [42, 1, 2, 3] assert p == ns.foo ns.foo += [1] assert list(p) == [42, 1, 2, 3, 1] p_from_local = ns("foo") p_from_local.append(2) assert p == p_from_local assert p._get_current_object() is ns.foo def test_proxy_callable(): value = 42 p = local.LocalProxy(lambda: value) assert p == 42 value = [23] p.append(42) assert p == [23, 42] assert value == [23, 42] assert p._get_current_object() is value def test_proxy_wrapped(): class SomeClassWithWrapped: __wrapped__ = "wrapped" proxy = local.LocalProxy(_cv_val) assert proxy.__wrapped__ is _cv_val _cv_val.set(42) with pytest.raises(AttributeError): proxy.__wrapped__ ns = local.Local(_cv_ns) ns.foo = SomeClassWithWrapped() ns.bar = 42 assert ns("foo").__wrapped__ == "wrapped" with pytest.raises(AttributeError): ns("bar").__wrapped__ def test_proxy_doc(): def example(): """example doc""" assert local.LocalProxy(lambda: example).__doc__ == "example doc" # The __doc__ descriptor shouldn't block the LocalProxy's class doc. assert local.LocalProxy.__doc__.startswith("A proxy") def test_proxy_fallback(): local_stack = local.LocalStack(_cv_stack) local_proxy = local_stack() assert repr(local_proxy) == "" assert isinstance(local_proxy, local.LocalProxy) assert local_proxy.__class__ is local.LocalProxy assert "LocalProxy" in local_proxy.__doc__ local_stack.push(42) assert repr(local_proxy) == "42" assert isinstance(local_proxy, int) assert local_proxy.__class__ is int assert "int(" in local_proxy.__doc__ def test_proxy_unbound(): ns = local.Local(_cv_ns) p = ns("value") assert repr(p) == "" assert not p assert dir(p) == [] def _make_proxy(value): ns = local.Local(_cv_ns) ns.value = value p = ns("value") return ns, p def test_proxy_type(): _, p = _make_proxy([]) assert isinstance(p, list) assert p.__class__ is list assert issubclass(type(p), local.LocalProxy) assert type(p) is local.LocalProxy def test_proxy_string_representations(): class Example: def __repr__(self): return "a" def __bytes__(self): return b"b" def __index__(self): return 23 _, p = _make_proxy(Example()) assert str(p) == "a" assert repr(p) == "a" assert bytes(p) == b"b" # __index__ assert bin(p) == "0b10111" assert oct(p) == "0o27" assert hex(p) == "0x17" def test_proxy_hash(): ns, p = _make_proxy("abc") assert hash(ns.value) == hash(p) @pytest.mark.parametrize( "op", [ operator.lt, operator.le, operator.eq, operator.ne, operator.gt, operator.ge, operator.add, operator.sub, operator.mul, operator.truediv, operator.floordiv, operator.mod, divmod, pow, operator.lshift, operator.rshift, operator.and_, operator.or_, operator.xor, ], ) def test_proxy_binop_int(op): _, p = _make_proxy(2) assert op(p, 3) == op(2, 3) # r-op assert op(3, p) == op(3, 2) @pytest.mark.parametrize("op", [operator.neg, operator.pos, abs, operator.invert]) def test_proxy_uop_int(op): _, p = _make_proxy(-2) assert op(p) == op(-2) def test_proxy_numeric(): class Example: def __complex__(self): return 1 + 2j def __int__(self): return 1 def __float__(self): return 2.1 def __round__(self, n=None): if n is not None: return 3.3 return 3 def __trunc__(self): return 4 def __floor__(self): return 5 def __ceil__(self): return 6 def __index__(self): return 2 _, p = _make_proxy(Example()) assert complex(p) == 1 + 2j assert int(p) == 1 assert float(p) == 2.1 assert round(p) == 3 assert round(p, 2) == 3.3 assert math.trunc(p) == 4 assert math.floor(p) == 5 assert math.ceil(p) == 6 assert [1, 2, 3][p] == 3 # __index__ @pytest.mark.parametrize( "op", [ operator.iadd, operator.isub, operator.imul, operator.imatmul, operator.itruediv, operator.ifloordiv, operator.imod, operator.ipow, operator.ilshift, operator.irshift, operator.iand, operator.ior, operator.ixor, ], ) def test_proxy_iop(op): class Example: value = 1 def fake_op(self, other): self.value = other return self __iadd__ = fake_op __isub__ = fake_op __imul__ = fake_op __imatmul__ = fake_op __itruediv__ = fake_op __ifloordiv__ = fake_op __imod__ = fake_op __ipow__ = fake_op __ilshift__ = fake_op __irshift__ = fake_op __iand__ = fake_op __ior__ = fake_op __ixor__ = fake_op ns, p = _make_proxy(Example()) p_out = op(p, 2) assert type(p_out) is local.LocalProxy assert p.value == 2 assert ns.value.value == 2 def test_proxy_matmul(): class Example: def __matmul__(self, other): return 2 * other def __rmatmul__(self, other): return 2 * other _, p = _make_proxy(Example()) assert p @ 3 == 6 assert 4 @ p == 8 def test_proxy_str(): _, p = _make_proxy("{act} %s") assert p + " world" == "{act} %s world" assert "say " + p == "say {act} %s" assert p * 2 == "{act} %s{act} %s" assert 2 * p == p * 2 assert p % ("world",) == "{act} world" assert p.format(act="test") == "test %s" def test_proxy_list(): _, p = _make_proxy([1, 2, 3]) assert len(p) == 3 assert p[0] == 1 assert 3 in p assert 4 not in p assert tuple(p) == (1, 2, 3) assert list(reversed(p)) == [3, 2, 1] p[0] = 4 assert p == [4, 2, 3] del p[-1] assert p == [4, 2] p += [5] assert p[-1] == 5 p *= 2 assert len(p) == 6 p[:] = [] assert not p p.append(1) assert p assert p + [2] == [1, 2] assert [2] + p == [2, 1] def test_proxy_copy(): class Foo: def __copy__(self): return self def __deepcopy__(self, memo): return self ns, p = _make_proxy(Foo()) assert copy.copy(p) is ns.value assert copy.deepcopy(p) is ns.value a = [] _, p = _make_proxy([a]) assert copy.copy(p) == [a] assert copy.copy(p)[0] is a assert copy.deepcopy(p) == [a] assert copy.deepcopy(p)[0] is not a def test_proxy_iterator(): a = [1, 2, 3] _, p = _make_proxy(iter(a)) assert next(p) == 1 def test_proxy_length_hint(): class Example: def __length_hint__(self): return 2 _, p = _make_proxy(Example()) assert operator.length_hint(p) == 2 def test_proxy_context_manager(): class Example: value = 2 def __enter__(self): self.value += 1 return self def __exit__(self, exc_type, exc_val, exc_tb): self.value -= 1 _, p = _make_proxy(Example()) assert p.value == 2 with p: assert p.value == 3 assert p.value == 2 def test_proxy_class(): class Meta(type): def __instancecheck__(cls, instance): return True def __subclasscheck__(cls, subclass): return True class Parent: pass class Example(Parent, metaclass=Meta): pass class Child(Example): pass _, p = _make_proxy(Example) assert type(p()) is Example assert isinstance(1, p) assert issubclass(int, p) assert p.__mro__ == (Example, Parent, object) assert p.__bases__ == (Parent,) assert p.__subclasses__() == [Child] def test_proxy_attributes(): class Example: def __init__(self): object.__setattr__(self, "values", {}) def __getattribute__(self, name): if name == "ham": return "eggs" return super().__getattribute__(name) def __getattr__(self, name): return self.values.get(name) def __setattr__(self, name, value): self.values[name] = value def __delattr__(self, name): del self.values[name] def __dir__(self): return sorted(self.values.keys()) _, p = _make_proxy(Example()) assert p.nothing is None assert p.__dict__ == {"values": {}} assert dir(p) == [] p.x = 1 assert p.x == 1 assert dir(p) == ["x"] del p.x assert dir(p) == [] assert p.ham == "eggs" p.ham = "spam" assert p.ham == "eggs" assert p.values["ham"] == "spam" def test_proxy_await(): async def get(): return 1 _, p = _make_proxy(get()) async def main(): return await p out = asyncio.run(main()) assert out == 1 def test_proxy_aiter(): class Example: value = 3 def __aiter__(self): return self async def __anext__(self): if self.value: self.value -= 1 return self.value raise StopAsyncIteration _, p = _make_proxy(Example()) async def main(): out = [] async for v in p: out.append(v) return out out = asyncio.run(main()) assert out == [2, 1, 0] def test_proxy_async_context_manager(): class Example: value = 2 async def __aenter__(self): self.value += 1 return self async def __aexit__(self, exc_type, exc_val, exc_tb): self.value -= 1 _, p = _make_proxy(Example()) async def main(): async with p: assert p.value == 3 assert p.value == 2 return True assert asyncio.run(main())