python-werkzeug/tests/test_local.py

616 lines
13 KiB
Python
Raw Normal View History

2023-03-14 14:48:50 +08:00
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) == "<LocalProxy unbound>"
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) == "<LocalProxy unbound>"
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) == "<LocalProxy unbound>"
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())