@@ -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