Calling `hasattr`/`getattr` creates trait if missing.
- Dominant language
- Python
- Stars
- 462
- Forks
- 90
- PR merge metrics
- No merged PRs in 30d
Description
I have observed some weird behavior when calling `hasattr` with a nonexistent attribute.
Assume the following as a minimal example:
```python
class AClass(ta.HasTraits):
pass
def observer(val):
print(f"observed: {val}")
inst = AClass()
inst.observe(observer, "*")
print(f"trait_names: {inst.trait_names()}")
print(f"hasattr('a'): {hasattr(inst, 'a')}")
print(f"trait_names: {inst.trait_names()}")
print(f"is 'a' in dir(inst)?: {'a' in dir(inst)}")
print(f"class traits of inst: {inst.__class_traits__.keys()}")
```
Yields:
```
trait_names: ['trait_added', 'trait_modified']
observed: TraitChangeEvent(object=<__main__.AClass object at 0x7f4031f304a0>, name='trait_added', old=, new='a')
hasattr: False
trait_names: ['trait_added', 'trait_modified', 'a']
is 'a' in dir(inst)?: True
class traits of inst: dict_keys(['trait_added', 'trait_modified', 'a'])
```
As one can see, after calling `hasattr` a new trait is added for the missing attribute 'a', it appears in the list of `trait_names` as well, but calling `getattr` now would cause an `AttributeError`.
Furthermore, using a different expression in the observer changes this behavior:
```python
...
inst.observe(observer, "trait_added")
...
```
```
trait_names: ['trait_added', 'trait_modified']
observed: TraitChangeEvent(object=<__main__.AClass object at 0x7f2068cf1450>, name='trait_added', old=, new='a')
hasattr: False
trait_names: ['trait_added', 'trait_modified']
is 'a' in dir(inst)?: False
class traits of inst: dict_keys(['trait_added', 'trait_modified', 'a'])
```
Now, the name of the missing attribute isn't added to the 'trait_names', but the attribute is still created as a class trait.
To my understanding, getting a value should not have such side effects, especially if getting the now appearing attribute in `trait_names` causes an error. `hasattr` and `trait_names` should not contradict each other, but the dependents of the observation expression makes this even less deterministic.
Am I using traits wrong at this point, is this intended behavior or is it a bug?
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.