Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(wip): mutable globals #1

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions src/lazy_imports_lite/_globals.py
Original file line number Diff line number Diff line change
@@ -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
11 changes: 0 additions & 11 deletions src/lazy_imports_lite/_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
77 changes: 77 additions & 0 deletions tests/test_globals.py
Original file line number Diff line number Diff line change
@@ -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__ <module 'builtins' (built-in)>
__cached__ None
__doc__ None
__loader__ <_frozen_importlib_external.SourceFileLoader object at <hex_value>>
__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(""),
)
14 changes: 10 additions & 4 deletions tests/test_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 <module 'test_pck.mx' from '<exec_prefix>/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("<equal to normal>"),
Expand All @@ -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(""),
Expand Down
37 changes: 0 additions & 37 deletions tests/test_transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__ <module 'builtins' (built-in)>
__cached__ None
__doc__ None
__loader__ <_frozen_importlib_external.SourceFileLoader object at <hex_value>>
__name__ __main__
__package__ None
__spec__ None
a bar.foo.a
"""
),
snapshot(""),
)


def test_import():
check_transform(
"""
Expand Down
Loading