Replies: 1 comment
-
This proposal was implemented in #91 |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Parsing Approach
Currently, parsing logic for
ParseDict
acrossSchema
classes is a little wonky. We should have built-in parsing functions for major data types, which can be used directly, and leaveSchema
subclasses to make own implementations if needed for rarer data types.Old Approach
Right now, we have parsing generally split between a base-level function called
ElementFromDict
, and individual, per-class-element functions of the form_parse<ElementName>
, e.g._parseTableName
or_parseProjectID
.ElementFromDict
takes in the dictionary being parsed, an optional logger, the name (or names) of the element to pull out, a function to parse those elements, and a default value:It will try each element in the
element_names
list, in order, until it finds one in the dictionary.If none of the items in
element_names
is a valid key inall_elements
, the function will give a warning and use the default value.The
parser_function
is one of those of the form_parse<ElementName>
, discussed next.ElementFromDict
will call the parse function, passing in the element retrieved from the dict._parse<ElementName>
takes just a single value, and is hardcoded to attempt to parse that value as a given type. The parse attempt may vary based on the type of the given value, e.g. if the parse function is meant to return an int, its behavior may be different for astr
given value than afloat
given value.New Approach
Instead of having a weird recursive relationship, where subclasses define parse functions, which are run by a general function from the parent class, we'll keep everything... well, bottom-up from the perspective of class hierarchy.
Each class will still implement a function for each individual item to parse.
However, this will effectively just be a wrapper to call the parent helper function.
These individual functions define the valid names to search for in the dict, the default value, and which type parser to use.
The helper would then do what it currently does, calling the appropriate parsing function after looping over all valid keys to search for and finding one.
The parsing functions will be on a type-by-type basis; could actually have this operate on some kind of match-case potentially, taking just a type rather than needing the caller to pass in a parser function.
In particular, this match-case could work with a matching like the following:
Note that some experimentation will be needed to figure out how to handle types that are not Python builtins, e.g. datetimes.
With this done, the
FromDict
of each class will effectively just be a series of calls to the private functions, but with even fewer variables to pass along.Hopefully, then, these become very simple functions.
Further,
__init__
functions should then use the parse functions as fallbacks if there is no value given for a particular param.Parsing
FromDict
FunctionalityThe changes above allow us to better solve an existing issue with how
FromDict
functions.Have so far had a hell of a time coming up with a good way to use
FromDict
approach to parsing/instantiating Schemas in a way that accomplishes the following two important things:Old approach
Currently, each
Schema
class is supposed to just implement aFromDict
that parses everything needed from a dict, and return an instance of the class.However, suppose we have a hierarchy where
SchemaB
inherits fromSchemaB
:Then
SchemaB
would need to redundantly parse out everything needed bySchemaA
to call the super constructor, e.g.Redundancy isn't good, so we try to separate this out by adding a second layer, where there is a
_fromDict
that accepts pre-parsed params to pass directly to constructor, after parsing out subclass elements:However, this would require an additional level of "from dict" sub-function for each level of the hierarchy, and that's not clean at all.
An even older approach was to just have the
__init__
function act as a de factoFromDict
, where all required elements are parsed from a dictionary.However, this makes for really, really awkward functions that need to call the constructor but don't have a dictionary ready to go.
Writing such functions requires checking and double-checking exactly what is parsed by the init, to understand what keys to use in a custom-built dictionary.
That's not any fun, and it is much, much nicer to just use actual parameters, where linting and autocomplete help ensure the right data is passed in.
New Approach
With the changes outlined to parser functions, we'll have a situation where
__init__
functions can easily use a dictionary to look for parameters that were not directly passed in.Thus, we simply change conventions to have all class-specific parameters to
__init__
marked asOptional
.Within the
FromDict
, we can then call only the specific functions for the class, and pass inNone
for all the parent params.The parent
__init__
will then getNone
in those spots, and call its own parser functions as fallbacks.Alternately, probably better, would be to just go ahead and call parent parser functions right within the
FromDict
, since each should be a very simple one-liner.In that case, we can pretty directly pass along everything from the Dict.
The only thing we really lose here is the ability to have a super-easy "leftovers" variable to let us remove elements from the dictionary.
However, this could be done by just having the parse function
del unparsed_elements[key]
for whateverkey
actually gets used.Beta Was this translation helpful? Give feedback.
All reactions