-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathassignonce.py
More file actions
39 lines (31 loc) · 1.34 KB
/
Copy pathassignonce.py
File metadata and controls
39 lines (31 loc) · 1.34 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# -*- coding: utf-8 -*-
"""Assign-once environment."""
__all__ = ["assignonce"]
from .env import env as _envcls
class assignonce(_envcls):
"""Environment with assign-once names.
In Scheme terms, this makes ``define`` and ``set!`` look different::
with assignonce() as e:
e.foo = "bar" # new definition, ok
e.set("foo", "tavern") # explicitly rebind e.foo, ok
e << ("foo", "tavern") # same (but returns e instead of new value)
e.foo = "quux" # AttributeError, e.foo already defined.
If you don't need the automatic clear on exiting the `with` block::
e = assignonce()
e.foo = "bar"
e.set("foo", "tavern")
e.foo = "quux" # AttributeError
"""
def __setattr__(self, name, value):
if name in self._reserved_names or name not in self:
return super().__setattr__(name, value)
else:
raise AttributeError(f"name {repr(name)} is already defined")
def set(self, name, value):
"""Rebind an existing name to a new value."""
env = self._env
if name not in env:
raise AttributeError(f"name {repr(name)} is not defined")
# important part: bypass our own __setattr__, which would refuse the update.
super().__setattr__(name, value)
return value