Presenting asdf-pydantic
, create ASDF tags with pydantic models.
#1706
Replies: 10 comments
-
Thanks for sharing! I'm excited to try out your project. I'm curious, did you encounter any issues with ASDF (including any non-intuitive API) while developing the project (or are there features that if added to ASDF would make this project easier/cleaner)? |
Beta Was this translation helpful? Give feedback.
-
I agree that this is a good idea. I have been playing with the idea of fusing asdf to pydantic since I started working with ASDF. Several thoughts:
There are more things to consider once you dive in. |
Beta Was this translation helpful? Give feedback.
-
One part I decided to ignore was optimizing entry point installation as pointed out in various pages of the ASDF docs. What resulted was asdf-pydantic implements a Maybe a nice-to-have is the API or the docs does not encourage building generic Converter without adding your own fixings. I've no specific request here, but the limitations made me design *asdf-pydantic`'s converter as (1) a singleton and (2) stores a mapping between tags to types. |
Beta Was this translation helpful? Give feedback.
-
@WilliamJamieson Let me first address the questions that I can answer quickly then put more thoughts on the others (especially the schema and version question):
Oh I was so thrilled when this worked amazingly. See this passing test script that uses Look through the other test scripts as well.
The rule to follow is:
With those, subclassing follows (1) and a new tag needs to be defined (not yet enforced in asdf-pydantic).
If we agree the minimum the user must do is to define the object's data model in a class MyForeignObjectAdapterModel(ASDFPydanticModel):
...
my_foreign_object = ...
af.AsdfFile({
"obj": MyForeignObjectAdapterModel.parse_obj(my_foreign_object)
})
###
from pydantic import parse_obj_as
sequence_of_foreign_objects = ...
af.AsdfFile({
"objs": parse_obj_as(list[MyForeignObjectAdapterModel], sequence_of_foreign_objects)
}) Now how complicated the adapter is depends on your object. All Python standard types adaptable for free. You may take advantage of the object's |
Beta Was this translation helpful? Give feedback.
-
@ketozhang its great that yo have already considered many of the same things I have already. I'll take the time to browse through your code and play with it. There are two more things that I think would be useful:
af = asdf.AsdfFile()
af["rect"] = Rectangle(width=1, height=1) Instead, it would already exist (or be generated on the fly and cached) with access given via something like
|
Beta Was this translation helpful? Give feedback.
-
I just had a chance to look over the source code for However, currently as your example tests stand ASDF is not actually validating the data as it is being written to the ASDF file nor is it validating the data is it is reading from the ASDF file. Granted, I had to dig really deep and "watch" ASDF run its validation on the data as it is reading/writing. Basically, (I'm going to point at the latest PyPi release tag to explain what is going on; however, the current dev version is rapidly evolving in this area, see #1490 for what might be changing) During tree validation ASDF validates from the root node down through all the leaf nodes in a depth-first-search. When a "tag" is encountered during validation, as is the case for the tagged objects in your example, we hit the following block of code: Lines 306 to 312 in efafac8 In your particular case, you have a valid ASDF extension so it drops to: Lines 307 to 308 in efafac8 In your case Lines 314 to 320 in efafac8 It iterates over an empty list and so does not descend further. Moreover, it does not raise any kind of alarms. This means that ASDF cannot validate anything contained within the subtree result of your I don't think we quite recognized the consequences of this of allowing tags which don't have a schema to allow for ASDF to validate the contents. However, his makes it much simpler to just skip writing a schema for an object, but instead just defining a tag. @eslavich might be able to comment on why this was allowed in the first place. In this case, Thus once |
Beta Was this translation helpful? Give feedback.
-
@WilliamJamieson I encourage you to submit an issue to the repo for those suggestions that asdf-pydantic not yet support. Thank you, there many you mention that Data model metadata
This is already supported. A field defined with the type Versioning and backward compatibilityThe constraint is you can only have one Python package version installed. If you want backward-compatibility, the general rule is your latest version of your Python package must be a superset of the extension, tag, and schema versions. My take is because your Python package must claim to support an extension at some version, to be backward compatible, it must also register an older version of the extension in its manifest. You could have various strategies to build The fallback in I'm open to ideas. How has the community implemented backward compatibility? Are AsdfPydanticModel fields being validated in ASDF?@WilliamJamieson, replying to the comment just before this. ASDF is not doing any validation on the Try this example where I maliciously changed Click for exampleimport asdf
import astropy.units as u
from asdf.extension import Extension
from astropy.units import Quantity
from asdf_pydantic import AsdfPydanticConverter, AsdfPydanticModel
class DataPoint(AsdfPydanticModel):
_tag = "asdf://asdf-pydantic/examples/tags/datapoint-1.0.0"
distance: Quantity[u.m]
AsdfPydanticConverter.add_models(DataPoint)
class TestExtension(Extension):
extension_uri = "asdf://asdf-pydantic/examples/extensions/test-1.0.0"
converters = [AsdfPydanticConverter()] # type: ignore
tags = [*AsdfPydanticConverter().tags] # type: ignore
asdf.get_config().add_extension(TestExtension())
with open("test.asdf", "rb") as fp:
asdf.open(fp) #ASDF 1.0.0
#ASDF_STANDARD 1.5.0
%YAML 1.1
%TAG ! tag:stsci.edu:asdf/
--- !core/asdf-1.1.0
asdf_library: !core/software-1.0.0 {author: The ASDF Developers, homepage: 'http://github.com/asdf-format/asdf',
name: asdf, version: 2.14.4}
history:
extensions:
- !core/extension_metadata-1.0.0
extension_class: asdf.extension.BuiltinExtension
software: !core/software-1.0.0 {name: asdf, version: 2.14.4}
- !core/extension_metadata-1.0.0 {extension_class: astropy_types_test.setup_module.<locals>.TestExtension,
extension_uri: 'asdf://asdf-pydantic/examples/extensions/test-1.0.0'}
- !core/extension_metadata-1.0.0
extension_class: asdf_astropy._manifest.CompoundManifestExtension
extension_uri: asdf://asdf-format.org/core/extensions/core-1.5.0
software: !core/software-1.0.0 {name: asdf-astropy, version: 0.4.0}
positions:
- !<asdf://asdf-pydantic/examples/tags/datapoint-1.0.0>
distance: !unit/quantity-1.1.0 {datatype: float64, unit: !unit/unit-1.0.0 m. value: "foobar"}
time: !time/time-1.1.0 2023-01-01T00:00:00.000
- !<asdf://asdf-pydantic/examples/tags/datapoint-1.0.0>
distance: !unit/quantity-1.1.0 {datatype: float64, unit: !unit/unit-1.0.0 m, value: 1.0}
time: !time/time-1.1.0 2023-01-01T01:00:00.000
... As you pointed out and I've read in the docs, schema-less tags are possible and not discouraged. At least, with |
Beta Was this translation helpful? Give feedback.
-
I welcome any further questions and feedbacks here until the thread is closed. Do consider making an issue for the usual bug reports and suggestions in at https://github.com/ketozhang/asdf-pydantic/issues. |
Beta Was this translation helpful? Give feedback.
-
Yes you are correct that non I guess I should not attempt figure these things out while very tired. In any case, I wonder if it is possible to modify the |
Beta Was this translation helpful? Give feedback.
-
I'll open an issue directly in Honestly, I think the simplest for versioning |
Beta Was this translation helpful? Give feedback.
-
After
pip install asdf-pydantic
you can do something like this:Features
Rationale
Defining and prototyping composite types with Python standard types:
UserDict
,Dataclasses
,TypeDict
are okay, but nothing beats the flexibilty of pydantic. With ASDF you're already in the mental space of working with either YAML and JSON hierarchical structure. This feels natural with pydantic.There are big potentials beyond integrating with the Python ecosystem. For example, Tags created with asdf-pydantic is already readily JSON-serializable (other existing tags, like astropy, may require custom serialization) and the JSON schema comes for free. Initial adoption of JSON schema in another language (e.g., C++, C#, Java, javascript) could be much faster than with ASDF schema — and not to forget, this is the most common schema for HTTP APIs.
I am looking for commmunity interest and user feedback. Alongside contributors to incubate this project if ASDF is interested.
Beta Was this translation helpful? Give feedback.
All reactions