-
Notifications
You must be signed in to change notification settings - Fork 382
Description
Background
As of 0.14, the "use either attribute or dict syntax" behavior for Context/Config/DataProxy functions reasonably well, except for when setting brand new config keys that don't already exist, in which case the attribute approach doesn't work - it simply sets a real attribute on whatever DataProxy is being touched.
This is very foot-gunny as it leads to difficult to understand/troubleshoot behavior - no error, and attribute access sometimes works after due to happenstance, but any e.g. cloning of the config, or dict access, or etc, does not preserve the attribute; tests like "if key in config" fail; etc.
What should happen is that new attribute accesses do set config values...but only from "outside" the class, and not in, say, __init__, otherwise there's a chicken/egg problem re: how to set initial, real attributes.
Strongly related is an issue where the "real attributes win over config values w/ same name, during attribute access" behavior - changed for 0.14 - is inconsistent because it only looks at self/type, and not supertypes, so it still doesn't quite work when one is dealing with Context or Config (versus a nested config values, which becomes a 'raw' DataProxy).
Solutions
- 'Lock'/'unlock' attributes-become-config behavior, e.g. in a decorator around
__init__or other methods- Should work pretty solidly
- But requires implementers of subclasses to remember to use the decorator anyplace they create new attributes
- And is moderate bookkeeping, i.e. keep an attribute around as state for whether the obj is in "let me touch attrs or not" mode
- which then rolls into the supertypes issue above, because if that's not working, Context and Config both need their own initializations of the bookkeeping attribute
- besides that, it's just more state that can get set incorrectly, or cause threading issues, etc
- Main upside: when decorator is in place, decorated code can simply forget about the whole deal and use attributes normally
- Just remember to use
object.__setattr__anytime one makes new attributes, which skips our "clever" local implementation entirely.- Bit more footgunny for devs insofar as it's now a lot easier to forget (any attribute creation vs just set & forget once per method, with the above decorator approach)
- Also just more verbose
- Might have some quirks on eg Python 3 (tho I can't find the NOTE: I thought I left about this in the deep past...)
- But it's less bookkeeping
Neither of those is a clear winner but I'm leaning towards the latter (less code and state is better) so I may try it first to see just how verbose it is and whether it even works as expected.