| 109 | return obj |
| 110 | |
| 111 | def __getattr__(self, key: str) -> Any: |
| 112 | # NOTE: due to default Python attribute-lookup semantics, "real" |
| 113 | # attributes will always be yielded on attribute access and this method |
| 114 | # is skipped. That behavior is good for us (it's more intuitive than |
| 115 | # having a config key accidentally shadow a real attribute or method). |
| 116 | try: |
| 117 | return self._get(key) |
| 118 | except KeyError: |
| 119 | # Proxy most special vars to config for dict procotol. |
| 120 | if key in self._proxies: |
| 121 | return getattr(self._config, key) |
| 122 | # Otherwise, raise useful AttributeError to follow getattr proto. |
| 123 | err = "No attribute or config key found for {!r}".format(key) |
| 124 | attrs = [x for x in dir(self.__class__) if not x.startswith("_")] |
| 125 | err += "\n\nValid keys: {!r}".format( |
| 126 | sorted(list(self._config.keys())) |
| 127 | ) |
| 128 | err += "\n\nValid real attributes: {!r}".format(attrs) |
| 129 | raise AttributeError(err) |
| 130 | |
| 131 | def __setattr__(self, key: str, value: Any) -> None: |
| 132 | # Turn attribute-sets into config updates anytime we don't have a real |