Dictionary subclass enabling attribute lookup/assignment of keys/values. For example:: >>> m = AttributeDict({'foo': 'bar'}) >>> m.foo 'bar' >>> m.foo = 'not bar' >>> m['foo'] 'not bar' ``AttributeDict`` objects also provide ``.first()`` which
| 64 | |
| 65 | |
| 66 | class AttributeDict(dict): |
| 67 | """Dictionary subclass enabling attribute lookup/assignment of keys/values. |
| 68 | |
| 69 | For example:: |
| 70 | >>> m = AttributeDict({'foo': 'bar'}) |
| 71 | >>> m.foo |
| 72 | 'bar' |
| 73 | >>> m.foo = 'not bar' |
| 74 | >>> m['foo'] |
| 75 | 'not bar' |
| 76 | ``AttributeDict`` objects also provide ``.first()`` which acts like |
| 77 | ``.get()`` but accepts multiple keys as arguments, and returns the value of |
| 78 | the first hit, e.g.:: |
| 79 | >>> m = AttributeDict({'foo': 'bar', 'biz': 'baz'}) |
| 80 | >>> m.first('wrong', 'incorrect', 'foo', 'biz') |
| 81 | 'bar' |
| 82 | """ |
| 83 | |
| 84 | def __setattr__(self, key, value): |
| 85 | self[key] = value |
| 86 | |
| 87 | def __getattr__(self, key): |
| 88 | try: |
| 89 | return self[key] |
| 90 | except KeyError: |
| 91 | pass |
| 92 | # to conform with __getattr__ spec |
| 93 | # but get out of the except block so it doesn't look like a nested err |
| 94 | raise AttributeError(key) |
| 95 | |
| 96 | def set_read_only(self, names, msg="Attribute is read-only"): |
| 97 | """ |
| 98 | Designate named attributes as read-only with the corresponding msg |
| 99 | |
| 100 | Method is additive. Making additional calls to this method will update |
| 101 | existing messages and add to the current set of _read_only names. |
| 102 | """ |
| 103 | new_read_only = {name: msg for name in names} |
| 104 | if getattr(self, "_read_only", False): |
| 105 | self._read_only.update(new_read_only) |
| 106 | else: |
| 107 | object.__setattr__(self, "_read_only", new_read_only) |
| 108 | |
| 109 | def unset_read_only(self, keys): |
| 110 | if hasattr(self, "_read_only"): |
| 111 | for key in keys: |
| 112 | self._read_only.pop(key, None) |
| 113 | |
| 114 | def finalize(self, msg="Object is final: No new keys may be added."): |
| 115 | """Prevent any new keys being set.""" |
| 116 | object.__setattr__(self, "_final", msg) |
| 117 | |
| 118 | def __setitem__(self, key, val): |
| 119 | if key in self.__dict__.get("_read_only", {}): |
| 120 | raise AttributeError(self._read_only[key], key) |
| 121 | |
| 122 | final_msg = self.__dict__.get("_final") |
| 123 | if final_msg and key not in self: |
no outgoing calls
searching dependent graphs…