Skip to content
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 2 upgrade #264

Merged
merged 15 commits into from
Feb 4, 2024
Merged

Conversation

mwcraig
Copy link
Contributor

@mwcraig mwcraig commented Feb 1, 2024

This is a pretty big one. I'd recommend focusing on the tests and whether they seem to reasonably cover the code if you don't want to dive into the details of pydantic 2.

Although it was a bit of a slog, pydantic 2 makes it much easier to handle astropy types by writing type annotators (see the new docstrings and tests).

One thing that I know isn't quite right yet is tapping into the serialization that astropy can already do to Table and asdf. I found and am using the _represent_as_dict method to do an initial pass at this that will work for now for the types we need.

@adonath -- I thought this might be of interest to gammapy.

@JuanCab -- if you could aim for Monday or Tuesday that would be great -- I'll keep working on unrelated issues and/or base new pull requests off this one if needed.

Edit: Closes #262

@mwcraig
Copy link
Contributor Author

mwcraig commented Feb 1, 2024

The test failure is an http timeout, I think this is ready for review. I'll try re-running the remote_data test later today.

@codecov-commenter
Copy link

codecov-commenter commented Feb 1, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Comparison is base (685d214) 69.89% compared to head (aee5d1e) 69.74%.
Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #264      +/-   ##
==========================================
- Coverage   69.89%   69.74%   -0.16%     
==========================================
  Files          25       24       -1     
  Lines        2966     2938      -28     
==========================================
- Hits         2073     2049      -24     
+ Misses        893      889       -4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@mwcraig mwcraig force-pushed the pydantic-2-upgrade branch from e598225 to 85b8338 Compare February 1, 2024 22:30
@mwcraig mwcraig force-pushed the pydantic-2-upgrade branch from 3cb6419 to e7d52fe Compare February 2, 2024 15:02
Copy link
Contributor

@JuanCab JuanCab left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly a few typos. I did review the tests and think you missed a few (but not anything "major". If you add the tests, I am happy approving this.

def validate_by_instantiating(source_type):
"""
Return a function that tries to create an instance of source_type from a value.
The intended use of this is as a vallidotr in pydantic.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume you mean "validator" and not "vallidotr".

# since the first thing in the chain has to handle the value coming
# from json,while the last thing generates the python value for the
# input. With the choice below we will *always* want`mode="validation"`
# because pydantic cannot generate a schema fora Unit or Quantity.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"fora" -> "for a"

return hash(self.physical_type)


# We have lost default titles and exmples, but that is maybe not so bad
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"exmples" -> "examples"


# We have lost default titles and exmples, but that is maybe not so bad

# This is really nice compared to pydantiv v1...
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"pydantiv" -> "pydantic"


In principle, we ought to be able to use the astropy serialization stuff
that is used in writing tables to ecsv here, but that is not quite working
yet.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just an opinion, I don't like leaving "dated" comments that might be forgotten without actually dating them. eg. "As of January 2024, this is not quite working." In general, I understand the need to comment on an incomplete feature, but I can imagine forgetting to update it without something like a date to stand out in the vision while reviewing the code.

gap: conint(ge=1) = Field(autoui=CustomBoundedIntTex, default=1)
annulus_width: conint(ge=1) = Field(autoui=CustomBoundedIntTex, default=1)
# model_config = MODEL_DEFAULT_CONFIGURATION

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the pydantic uninitiated, should we add a comment saying we are restricting radius, gap, and annulus_width to be integers with a value greater than 1 in the docstring (lines 218-224)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made two changes here that you'll see when I push the code in a bit:

  1. Add notes to the docstrings indicating values must be positive
  2. Used PositiveInt and PositiveFloat from pydantic 2 instead of conint(ge=1) and confloat(ge=0). The former are much less opaque than the latter

gap: conint(ge=1) = Field(autoui=CustomBoundedIntTex, default=1)
annulus_width: conint(ge=1) = Field(autoui=CustomBoundedIntTex, default=1)
# model_config = MODEL_DEFAULT_CONFIGURATION

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should add a comment for the pydantic uninitiated that these lines ensure radius, gap, annulus_width should all be integers greater than or equal to 1.

"init",
[
Unit("m"),
1,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you test with a float as well and a float with a unit? Yeah, a little silly, but trying to be through.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by "float with a unit"? I think 5 * Unit('m') is already guaranteed to be a Unit so it is covered by the Unit("m") case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by "float with a unit"? I think 5 * Unit('m') is already guaranteed to be a Unit so it is covered by the Unit("m") case.

Oh, I still have this idea that 5 * Unit('m') is different from 5.0*Unit('m') since Astropy values with units I thought got stored as numerical type + unit information. If I am incorrect, forget it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is what I wrote down needed to be tested BEFORE reviewing your tests (after reviewing the rest of the code) along with my comments after reviewing your tests

  • Make sure both Unit and Quantity can be created from a string or a float or an
    instance of the same type. So we need to check for all of those cases. [MOSTLY
    DONE, missing float tests I think]
  • Test use of UnitType [Not tested directly, but as part of above calls]
  • Test use of QuantityType [Not tested directly, but as part of above calls]
  • Test use of EquivalentTo [TESTED, including validation failures]
  • Test use of WithPhysicalType [TESTED, including failures]
  • Test use of AstropyValidator [TESTED, but MISSING tests for validation failures]

The only thing I really think is missing from the tests are exploits tests for floats with Quantity and Unit creation and the confirmation that expected validation failures occur with AstropyValidator.

@JuanCab
Copy link
Contributor

JuanCab commented Feb 4, 2024

BTW, I understand the need to use the private AstroPy API to serialize astropy types. I like how you wrapped it with another function. Any chance we can ask AstroPy to expose _represent_as_dict() in a PR, since other people using pydantic 2.x may want to use this. In fact, I am wondering if astropy_pydantic.py could be released as a separate library for others, since it could be helpful to other projects using astropy.

@mwcraig mwcraig requested a review from JuanCab February 4, 2024 20:33
"init",
[
Unit("m"),
1,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by "float with a unit"? I think 5 * Unit('m') is already guaranteed to be a Unit so it is covered by the Unit("m") case.

Oh, I still have this idea that 5 * Unit('m') is different from 5.0*Unit('m') since Astropy values with units I thought got stored as numerical type + unit information. If I am incorrect, forget it.

@mwcraig
Copy link
Contributor Author

mwcraig commented Feb 4, 2024

BTW, I understand the need to use the private AstroPy API to serialize astropy types. I like how you wrapped it with another function. Any chance we can ask AstroPy to expose _represent_as_dict() in a PR, since other people using pydantic 2.x may want to use this. In fact, I am wondering if astropy_pydantic.py could be released as a separate library for others, since it could be helpful to other projects using astropy.

Forgot to respond to this -- if we can figure out how to translate the YAML schema astropy has to a JSON schema then I think it would make sense to release a separate package. I may ask in slack this week about how to do that.

The intent in astropy right now is that you load astropy.io.misc.yam and then yaml.dump(soome_astropy_thing). I'm not entirely sure why _represent_as_dict is private.

@mwcraig mwcraig merged commit 7b4b3da into feder-observatory:main Feb 4, 2024
9 checks passed
@mwcraig mwcraig deleted the pydantic-2-upgrade branch February 8, 2024 15:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Update code to pydantic 2
3 participants