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

feat!: AsdfPydanticConverter is no longer a singleton instance #38

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,13 @@ from asdf.extension import Extension
from asdf_pydantic.converter import AsdfPydanticConverter
from mypackage.shapes import Rectangle

AsdfPydanticConverter.add_models(Rectangle)
converter = AsdfPydanticConverter()
converter.add_models(Rectangle)

class ShapesExtension(Extension):
extension_uri = "asdf://asdf-pydantic/examples/extensions/shapes-1.0.0"
converters = [AsdfPydanticConverter()]
tags = [*AsdfPydanticConverter().tags]
converters = [converter]
tags = [*converter.tags]
```

Install the extension either by entry point specification or add it to
Expand Down
30 changes: 11 additions & 19 deletions asdf_pydantic/converter.py
Original file line number Diff line number Diff line change
@@ -1,37 +1,29 @@
from __future__ import annotations

from typing import Optional, Type
from typing import Type

from asdf.extension import Converter

from asdf_pydantic.model import AsdfPydanticModel

_ASDF_PYDANTIC_SINGLETON_CONVERTER: Optional[AsdfPydanticConverter] = None


class AsdfPydanticConverter(Converter):
"""Implements a converter compatible with all subclass of AsdfPydanticModel.

The instance is a singleton.
"""

_tag_to_class: dict[str, Type[AsdfPydanticModel]] = {}
"""Implements a converter compatible with all subclass of AsdfPydanticModel."""

def __init__(self) -> None:
global _ASDF_PYDANTIC_SINGLETON_CONVERTER
_tag_to_class: dict[str, Type[AsdfPydanticModel]]

if _ASDF_PYDANTIC_SINGLETON_CONVERTER is None:
_ASDF_PYDANTIC_SINGLETON_CONVERTER = self
def __init__(self, *model_classes: Type[AsdfPydanticModel]) -> None:
self._tag_to_class = {}
self.add_models(*model_classes)
super().__init__()

self = _ASDF_PYDANTIC_SINGLETON_CONVERTER

@classmethod
def add_models(
cls, *model_classes: Type[AsdfPydanticModel]
self, *model_classes: Type[AsdfPydanticModel]
) -> "AsdfPydanticConverter":
for model_class in model_classes:
cls._tag_to_class[model_class.get_tag_uri()] = model_class
return cls()
self._tag_to_class[model_class.get_tag_uri()] = model_class

return self

@property
def tags(self) -> tuple[str]:
Expand Down
7 changes: 4 additions & 3 deletions tests/examples/test_astropy_tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,14 @@ class Database(AsdfPydanticModel):
@pytest.fixture()
def asdf_extension():
"""Registers an ASDF extension containing models for this test."""
AsdfPydanticConverter.add_models(Database)
converter = AsdfPydanticConverter()
converter.add_models(Database)

class TestExtension(Extension):
extension_uri = "asdf://asdf-pydantic/examples/extensions/test-1.0.0"

converters = [AsdfPydanticConverter()] # type: ignore
tags = [*AsdfPydanticConverter().tags] # type: ignore
converters = [converter] # type: ignore
tags = [*converter.tags] # type: ignore

asdf.get_config().add_extension(TestExtension())

Expand Down
5 changes: 3 additions & 2 deletions tests/examples/test_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,13 @@ class AsdfNode(AsdfPydanticModel):
@pytest.fixture()
def asdf_extension():
"""Registers an ASDF extension containing models for this test."""
AsdfPydanticConverter.add_models(AsdfNode)
converter = AsdfPydanticConverter()
converter.add_models(AsdfNode)

class TestExtension(Extension):
extension_uri = "asdf://asdf-pydantic/examples/extensions/test-1.0.0"

converters = [AsdfPydanticConverter()] # type: ignore
converters = [converter] # type: ignore
tags = [AsdfNode.get_tag_definition()] # type: ignore

with asdf.config_context() as asdf_config:
Expand Down
5 changes: 3 additions & 2 deletions tests/examples/test_rectangle.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@
@pytest.fixture()
def asdf_extension():
"""Registers an ASDF extension containing models for this test."""
AsdfPydanticConverter.add_models(AsdfRectangle)
converter = AsdfPydanticConverter()
converter.add_models(AsdfRectangle)

class TestExtension(Extension):
extension_uri = "asdf://asdf-pydantic/examples/extensions/test-1.0.0"

converters = [AsdfPydanticConverter()] # type: ignore
converters = [converter] # type: ignore
tags = [AsdfRectangle.get_tag_definition()] # type: ignore

with asdf.config_context() as asdf_config:
Expand Down
7 changes: 4 additions & 3 deletions tests/patterns/astropy_types_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@ class DataContainer(AsdfPydanticModel):


def setup_module():
AsdfPydanticConverter.add_models(DataPoint, DataContainer)
converter = AsdfPydanticConverter()
converter.add_models(DataPoint, DataContainer)

class TestExtension(Extension):
extension_uri = "asdf://asdf-pydantic/examples/extensions/test-1.0.0"

converters = [AsdfPydanticConverter()] # type: ignore
tags = [*AsdfPydanticConverter().tags] # type: ignore
converters = [converter] # type: ignore
tags = [*converter.tags] # type: ignore

asdf.get_config().add_extension(TestExtension())

Expand Down
7 changes: 4 additions & 3 deletions tests/patterns/union_type_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@ class UnionObject(AsdfPydanticModel):


def setup_module():
AsdfPydanticConverter.add_models(UnionObject)
converter = AsdfPydanticConverter()
converter.add_models(UnionObject)

class TestExtension(Extension):
extension_uri = "asdf://asdf-pydantic/examples/extensions/test-1.0.0"

converters = [AsdfPydanticConverter()] # type: ignore
tags = [*AsdfPydanticConverter().tags] # type: ignore
converters = [converter] # type: ignore
tags = [*converter.tags] # type: ignore

asdf.get_config().add_extension(TestExtension())

Expand Down
7 changes: 4 additions & 3 deletions tests/schema_validation_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@


def setup_module():
AsdfPydanticConverter.add_models(AsdfRectangle)
converter = AsdfPydanticConverter()
converter.add_models(AsdfRectangle)

class TestExtension(Extension):
extension_uri = "asdf://asdf-pydantic/examples/extensions/test-1.0.0" # type: ignore

tags = [*AsdfPydanticConverter().tags] # type: ignore
converters = [AsdfPydanticConverter()] # type: ignore
tags = [*converter.tags] # type: ignore
converters = [converter] # type: ignore

# HACK: The schema URI should be referenced from `AsdfRectangle._schema`.
# Then there should be a way to automatically add the schema to ASDF
Expand Down
13 changes: 13 additions & 0 deletions tests/test_converter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import pytest

from asdf_pydantic.converter import AsdfPydanticConverter
from asdf_pydantic.model import AsdfPydanticModel


class TestModel(AsdfPydanticModel):
_tag = "https://example.org/test_model"


@pytest.mark.parametrize("args", [tuple(), (TestModel,)])
def test_converter_is_unscoped_by_default(args):
assert AsdfPydanticConverter(*args) is not AsdfPydanticConverter(*args)