From 50521e4bf0f802efbf8c631b49f1dc6bbc65711b Mon Sep 17 00:00:00 2001 From: Frank Hoffmann <15r10nk-git@polarbit.de> Date: Mon, 12 Feb 2024 22:30:21 +0100 Subject: [PATCH] feat(wip): mutable globals --- src/lazy_imports_lite/_globals.py | 100 ++++++++++++++++++++++++++++++ src/lazy_imports_lite/_hooks.py | 11 ---- tests/test_globals.py | 77 +++++++++++++++++++++++ tests/test_loader.py | 14 +++-- tests/test_transformer.py | 37 ----------- 5 files changed, 187 insertions(+), 52 deletions(-) create mode 100644 src/lazy_imports_lite/_globals.py create mode 100644 tests/test_globals.py diff --git a/src/lazy_imports_lite/_globals.py b/src/lazy_imports_lite/_globals.py new file mode 100644 index 0000000..e8cf39e --- /dev/null +++ b/src/lazy_imports_lite/_globals.py @@ -0,0 +1,100 @@ +from collections.abc import MutableMapping + +from ._hooks import LazyObject + +used_keys = ("globals", "__lazy_imports_lite__") + + +class LazyGlobals(MutableMapping): + def __init__(self, original): + self.data = original + + def _loaded_data(self): + return { + key: value._lazy_value if isinstance(value, LazyObject) else value + for key, value in dict(self.data()).items() + if key not in used_keys + } + + def __len__(self): + return len(self.data()) + + def __getitem__(self, key): + if key in self.data(): + value = self.data()[key] + if isinstance(value, LazyObject): + value = value._lazy_value + return value + raise KeyError(key) + + def __setitem__(self, key, item): + if key in self.data(): + value = self.data()[key] + if isinstance(value, LazyObject): + value._lazy_value = item + return + + self.data()[key] = item + + def __delitem__(self, key): + del self.data()[key] + + def __iter__(self): + return (k for k in self.data() if k not in used_keys) + + def keys(self): + "D.keys() -> a set-like object providing a view on D's keys" + return {k: 0 for k in self.data().keys() if k not in used_keys}.keys() + + def items(self): + "D.items() -> a set-like object providing a view on D's items" + return self._loaded_data().items() + + def values(self): + "D.values() -> an object providing a view on D's values" + return self._loaded_data().values() + + def __contains__(self, key): + return key in self.data() and key not in used_keys + + def __repr__(self): + return repr(self._loaded_data()) + + def __or__(self, other): + if isinstance(other, LazyGlobals): + return dict(self._loaded_data() | other._loaded_data()) + if isinstance(other, dict): + return dict(self._loaded_data() | other) + return NotImplemented + + def __ror__(self, other): + if isinstance(other, LazyGlobals): + return dict(other._loaded_data() | self._loaded_data()) + if isinstance(other, dict): + return dict(other | self._loaded_data()) + return NotImplemented + + def __ior__(self, other): + data = self.data() + if isinstance(other, LazyGlobals): + data |= other._loaded_data() + else: + data |= other + return self + + def __copy__(self): + return self._loaded_data() + + def copy(self): + return self._loaded_data() + + @classmethod + def fromkeys(cls, iterable, value=None): + assert False, "LazyGlobals should no be initialized in this way" + + +def make_globals(global_provider): + def globals(): + return LazyGlobals(global_provider) + + return globals diff --git a/src/lazy_imports_lite/_hooks.py b/src/lazy_imports_lite/_hooks.py index 1cda574..1304354 100644 --- a/src/lazy_imports_lite/_hooks.py +++ b/src/lazy_imports_lite/_hooks.py @@ -90,14 +90,3 @@ def __getattr__(self, name): return module else: assert False - - -def make_globals(global_provider): - def g(): - return { - key: value._lazy_value if isinstance(value, LazyObject) else value - for key, value in dict(global_provider()).items() - if key not in ("globals", "__lazy_imports_lite__") - } - - return g diff --git a/tests/test_globals.py b/tests/test_globals.py new file mode 100644 index 0000000..b90ae66 --- /dev/null +++ b/tests/test_globals.py @@ -0,0 +1,77 @@ +from inline_snapshot import snapshot + +from .test_transformer import check_transform + + +def test_globals(): + check_transform( + """ +from bar.foo import a + +for e in sorted(globals().items()): + if e[0]!="__file__": + print(*e) + + """, + snapshot( + """\ +import lazy_imports_lite._hooks as __lazy_imports_lite__ +globals = __lazy_imports_lite__.make_globals(lambda g=globals: g()) +a = __lazy_imports_lite__.ImportFrom(__package__, 'bar.foo', 'a') +for e in sorted(globals().items()): + if e[0] != '__file__': + print(*e)\ +""" + ), + snapshot( + """\ +__annotations__ {} +__builtins__ +__cached__ None +__doc__ None +__loader__ <_frozen_importlib_external.SourceFileLoader object at > +__name__ __main__ +__package__ None +__spec__ None +a bar.foo.a +""" + ), + snapshot(""), + ) + + +def test_mutate_globals(): + check_transform( + """ +from bar.foo import a + +globals()["x"]="x value" +print(x,a) + +globals()["x"]="x2 value" +globals()["a"]="a2 value" + +print(x,a) + + + """, + snapshot( + """\ +import lazy_imports_lite._hooks as __lazy_imports_lite__ +globals = __lazy_imports_lite__.make_globals(lambda g=globals: g()) +a = __lazy_imports_lite__.ImportFrom(__package__, 'bar.foo', 'a') +globals()['x'] = 'x value' +print(x, a._lazy_value) +globals()['x'] = 'x2 value' +globals()['a'] = 'a2 value' +print(x, a._lazy_value)\ +""" + ), + snapshot( + """\ +x value bar.foo.a +x2 value a2 value +""" + ), + snapshot(""), + ) diff --git a/tests/test_loader.py b/tests/test_loader.py index b20aec4..48678cc 100644 --- a/tests/test_loader.py +++ b/tests/test_loader.py @@ -324,15 +324,19 @@ def later(): print("outside",vars(test_pck).keys()) +test_pck.later() +print(test_pck.x) test_pck.later() """, transformed_stdout=snapshot( """\ inside dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__path__', '__file__', '__cached__', '__builtins__', 'x']) -mx /test_pck/mx.py'> -inside dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__path__', '__file__', '__cached__', '__builtins__', 'x', 'mx']) -outside dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__path__', '__file__', '__cached__', '__builtins__', 'x', 'mx', 'later']) -later dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__path__', '__file__', '__cached__', '__builtins__', 'x', 'mx', 'later']) +no mx +inside dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__path__', '__file__', '__cached__', '__builtins__', 'x']) +outside dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__path__', '__file__', '__cached__', '__builtins__', 'x', 'later']) +later dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__path__', '__file__', '__cached__', '__builtins__', 'x', 'later']) +5 +later dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__path__', '__file__', '__cached__', '__builtins__', 'x', 'later', 'mx']) """ ), transformed_stderr=snapshot(""), @@ -343,6 +347,8 @@ def later(): inside dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__path__', '__file__', '__cached__', '__builtins__', 'mx', 'x']) outside dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__path__', '__file__', '__cached__', '__builtins__', 'mx', 'x', 'later']) later dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__path__', '__file__', '__cached__', '__builtins__', 'mx', 'x', 'later']) +5 +later dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__path__', '__file__', '__cached__', '__builtins__', 'mx', 'x', 'later']) """ ), normal_stderr=snapshot(""), diff --git a/tests/test_transformer.py b/tests/test_transformer.py index 46c61b1..fd77a14 100644 --- a/tests/test_transformer.py +++ b/tests/test_transformer.py @@ -269,43 +269,6 @@ def f(b=a._lazy_value): ) -def test_globals(): - check_transform( - """ -from bar.foo import a - -for e in sorted(globals().items()): - if e[0]!="__file__": - print(*e) - - """, - snapshot( - """\ -import lazy_imports_lite._hooks as __lazy_imports_lite__ -globals = __lazy_imports_lite__.make_globals(lambda g=globals: g()) -a = __lazy_imports_lite__.ImportFrom(__package__, 'bar.foo', 'a') -for e in sorted(globals().items()): - if e[0] != '__file__': - print(*e)\ -""" - ), - snapshot( - """\ -__annotations__ {} -__builtins__ -__cached__ None -__doc__ None -__loader__ <_frozen_importlib_external.SourceFileLoader object at > -__name__ __main__ -__package__ None -__spec__ None -a bar.foo.a -""" - ), - snapshot(""), - ) - - def test_import(): check_transform( """