-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pydantic Evaluations Conversion #388
Pydantic Evaluations Conversion #388
Conversation
All that's really left is to ensure that everything works through the entire evaluation service pipeline. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great stuff, @christophertubbs! I left a few minor comments that should be pretty straight forward to get through. Thanks!
fields = { | ||
"properties": self.properties.copy() if self.properties else dict() | ||
} | ||
if exclude_defaults is None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not just set the parameter default values to the default that you are assigning here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a basic protection approach. I found one page describing some of it (here)[https://medium.com/python-features/how-to-avoid-classic-pitfall-while-passing-default-values-in-python-7002c0dc4c7c]. It's more important for objects, but it's a protection approach here because None
is an invalid, but acceptable input. I can go ahead and remove it for primitive values if you'd like.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, default object types are a for sure no-no in python, but "copy" types in python are totally fair to use as defaults and I think make the code more clear. If you feel obliged to change it, go for it! But im not going to hold this up over something this small!
python/lib/evaluations/dmod/evaluations/specification/fields.py
Outdated
Show resolved
Hide resolved
def apply_configuration( | ||
self, | ||
configuration: typing.Dict[str, typing.Any], | ||
template_manager: TemplateManager, | ||
decoder_type: typing.Type[json.JSONDecoder] = None | ||
): | ||
self.__where = configuration.get("where", self.__where) | ||
self.__datatype = configuration.get("datatype", self.__datatype) | ||
self.where = configuration.get("where", self.where) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels like it could lead to unexpected behavior. I know if I were testing this, I would provide the wrong type just to see what happens. It feels like the most sane thing to do is to check if it exists and is a str
then allow the mutation, otherwise throw. The same for datatype
. Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
self.where = configuration.get("where", self.where) | |
if "where" in configuration: | |
if not isinstance(configuration["where"], str): | |
raise TypeError("value of key 'where' must be type str.") | |
self.where = configuration["where"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated to mimic the provided logic for datatype
, but changed the logic for where
to make sure the given value matches the supplied literal values in the where
definition
python/lib/evaluations/dmod/evaluations/specification/locations.py
Outdated
Show resolved
Hide resolved
def __eq__(self, other: "SchemeSpecification") -> bool: | ||
metric_functions: typing.List[MetricSpecification] = Field( | ||
description="The metrics to perform within the evaluation", | ||
alias="metrics" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just pointing out, what can be a gotcha with pydantic
< 2. alias
field names do not propogate to subclasses unless you define them in Config.fields
(i.e. fields = {"metric_functions": "metrics"}
). I didnt see where this was being inherited from, so it is not an issue now, but just something to watch out for.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated. This might bite us soon since it looks like Config
classes are being deprecated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for making the requested changes! I have a few comments that should be pretty easy to work through and then this can be on its way! Thanks again, @christophertubbs!
@@ -68,8 +68,34 @@ def apply_configuration( | |||
template_manager: TemplateManager, | |||
decoder_type: typing.Type[json.JSONDecoder] = None | |||
): | |||
self.from_field = configuration.get("from_field", self.from_field) | |||
self.pattern = configuration.get("pattern", self.pattern) | |||
if "from_field" in configuration: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to my comment in the dmod.evaluations.specification.fields
module, could this be simplified by creating a new instance of LocationSpecification
from the configuration
parameter and mutating self
using the new instances parameters? Im probably missing something. I feel like is something you would have done if it were plausible here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LocationSpecification
is one of the few classes where this could work considering that all of its fields are optional (i.e. just about any combination is a-ok). What would that look like? Just something like:
location_template = self.__class__.parse_obj(configuration)
if location_template.from_field is not None:
# assign
# etc
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
location_template = self.__class__.parse_obj(configuration) if location_template.from_field is not None: # assign # etc
you could even just do this:
location_template = type(self).parse_obj(configuration)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for making the requested changes, @christophertubbs! Merge at will!
…' to 'backend_type' for Backend specifications and converted evaluation specification objects to Pydantic
… TypeDefinition.matches that used the old `complies` nomenclature.
…tion` (it's now just `backend`).
…o read and changed the type in the ValueSelector to be one of several optional string values rather than ANY string
…ere' and 'datatype' in a specification configuration
…ld be case insensitive
…eld` and updated the overriding logic for `from_field` and `pattern` in `LocationSpecification` to ensure that bytes are sufficiently decoded and string types are enforced.
…eld` and updated the overriding logic for `from_field` and `pattern` in `LocationSpecification` to ensure that bytes are sufficiently decoded and string types are enforced.
…s` to allow pre-2.0 behavior.
…nd added a TODO to the disk data_retriever to inform that the 'date_parser' logic needs to be replaced
…e specification before trying to reference it
ee563a9
to
e7abba4
Compare
closes #261
Converts Evaluation Specification classes to Pydantic models.