Skip to content

Commit 57c0188

Browse files
committed
env: respect finalization status also for deletion
1 parent 461264c commit 57c0188

File tree

1 file changed

+25
-8
lines changed

1 file changed

+25
-8
lines changed

unpythonic/env.py

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,13 @@ class env:
4747
instance itself will remain alive due to Python's scoping rules.
4848
"""
4949
# do not allow bindings that would break functionality.
50-
_reserved_names = ("set", "clear", "finalize", "_env", "_allow_more_bindings",
50+
_reserved_names = ("set", "clear", "finalize", "_env", "_finalized",
5151
"_direct_write", "_reserved_names")
52-
_direct_write = ("_env", "_allow_more_bindings")
52+
_direct_write = ("_env", "_finalized")
5353

5454
def __init__(self, **bindings):
5555
self._env = {}
56-
self._allow_more_bindings = True # "let" disables this once env setup done
56+
self._finalized = False # "let" sets this once env setup done
5757
for name, value in bindings.items():
5858
setattr(self, name, value)
5959

@@ -66,7 +66,7 @@ def __setattr__(self, name, value):
6666
return super().__setattr__(name, value)
6767
if name in self._reserved_names:
6868
raise AttributeError("cannot overwrite reserved name '{:s}'; complete list: {}".format(name, self._reserved_names))
69-
if not self._allow_more_bindings and name not in self:
69+
if self._finalized and name not in self:
7070
raise AttributeError("name '{:s}' is not defined; adding new bindings to a finalized environment is not allowed".format(name))
7171
# Block invalid names in subscripting (which redirects here).
7272
if not name.isidentifier():
@@ -86,6 +86,8 @@ def __getattr__(self, name):
8686
def __delattr__(self, name):
8787
if not name.isidentifier():
8888
raise ValueError("'{}' is not a valid identifier".format(name))
89+
if self._finalized:
90+
raise TypeError("deleting bindings from a finalized environment not allowed; attempted to delete '{:s}'".format(name))
8991
e = self._env # __getattr__ not called if direct attr lookup succeeds, no need for hook.
9092
if name not in e:
9193
raise AttributeError("name '{:s}' is not defined".format(name))
@@ -118,14 +120,28 @@ def __len__(self):
118120

119121
# MutableMapping
120122
def pop(self, k, *default):
123+
if self._finalized:
124+
raise TypeError("deleting bindings from a finalized environment not allowed; attempted to delete '{:s}'".format(k))
121125
return self._env.pop(k, *default)
122126
def popitem(self):
127+
if self._finalized:
128+
raise TypeError("deleting bindings from a finalized environment not allowed")
123129
return self._env.popitem()
124130
def clear(self):
131+
if self._finalized:
132+
raise TypeError("clearing a finalized environment not allowed")
125133
return self._env.clear()
126134
def update(self, *mapping, **bindings):
135+
if mapping:
136+
m = mapping[0]
137+
if self._finalized and any(k not in self for k in m):
138+
raise AttributeError("adding new bindings to a finalized environment is not allowed")
139+
if self._finalized and any(k not in self for k in bindings):
140+
raise AttributeError("adding new bindings to a finalized environment is not allowed")
127141
return self._env.update(*mapping, **bindings)
128142
def setdefault(self, k, *default):
143+
if self._finalized and k not in self:
144+
raise AttributeError("name '{:s}' is not defined; adding new bindings to a finalized environment is not allowed".format(k))
129145
return self._env.setdefault(k, *default)
130146

131147
# subscripting
@@ -143,7 +159,7 @@ def __enter__(self):
143159
return self
144160

145161
def __exit__(self, exctype, excvalue, traceback):
146-
self.clear()
162+
self._env.clear() # on context exit, clear even if we are a finalized env
147163

148164
# pretty-printing
149165
def __repr__(self):
@@ -182,12 +198,13 @@ def __lshift__(self, arg):
182198
def finalize(self):
183199
"""Finalize environment.
184200
185-
This stops the instance from accepting any more new bindings.
201+
This stops the instance from accepting any more new bindings,
202+
or any deletions of existing bindings.
186203
187-
Existing bindings can still be overwritten even in a finalized
204+
Existing bindings can still be given new values even in a finalized
188205
environment.
189206
"""
190-
self._allow_more_bindings = False
207+
self._finalized = True
191208

192209
# For rebind syntax: "e.foo << newval" --> "e.foo.__lshift__(newval)",
193210
# so foo.__lshift__() must be set up to rebind e.foo.

0 commit comments

Comments
 (0)