import io import sys from pathlib import Path import asdf import pytest import yaml def test_resources(): resources_root = Path(__file__).parent.parent / "resources" resource_manager = asdf.get_config().resource_manager for resource_path in resources_root.glob("**/*.yaml"): with resource_path.open("rb") as f: resource_content = f.read() resource = yaml.safe_load(resource_content) resource_uri = resource["id"] assert resource_manager[resource_uri] == resource_content def test_manifests(): manifests_root = Path(__file__).parent.parent / "resources" / "manifests" resource_manager = asdf.get_config().resource_manager for manifest_path in manifests_root.glob("*.yaml"): with manifest_path.open("rb") as f: manifest_content = f.read() manifest = yaml.safe_load(manifest_content) manifest_schema = asdf.schema.load_schema("asdf://asdf-format.org/core/schemas/extension_manifest-1.0.0") # The manifest must be valid against its own schema: asdf.schema.validate(manifest, schema=manifest_schema) for tag_definition in manifest["tags"]: # The tag's schema must be available: assert tag_definition["schema_uri"] in resource_manager def test_extensions(): package_and_uri_pairs = {(e.package_name, e.extension_uri) for e in asdf.get_config().extensions} assert ("asdf-astropy", "asdf://astropy.org/astropy/extensions/astropy-1.0.0") in package_and_uri_pairs assert ("asdf-astropy", "asdf://asdf-format.org/transform/extensions/transform-1.0.0") in package_and_uri_pairs assert ("asdf-astropy", "asdf://asdf-format.org/transform/extensions/transform-1.1.0") in package_and_uri_pairs assert ("asdf-astropy", "asdf://asdf-format.org/transform/extensions/transform-1.2.0") in package_and_uri_pairs assert ("asdf-astropy", "asdf://asdf-format.org/transform/extensions/transform-1.3.0") in package_and_uri_pairs assert ("asdf-astropy", "asdf://asdf-format.org/transform/extensions/transform-1.4.0") in package_and_uri_pairs assert ("asdf-astropy", "asdf://asdf-format.org/transform/extensions/transform-1.5.0") in package_and_uri_pairs _ASTROPY_MODULES = [ "astropy.coordinates", "astropy.io", "astropy.modeling", "astropy.table", "astropy.time", "astropy.units", ] @pytest.fixture() def _clean_astropy_imports(): """Temporally unload all astropy modules used by asdf-astropy""" # If any astropy or asdf_astropy modules are already imported # remove them from sys.modules so we can later in this test check # if those modules were imported. We have to store these as # many submodules will reference or use types defined in other # submodules previous_modules = {} for name in sys.modules.copy(): if name.startswith("asdf_astropy") or any(name.startswith(m) for m in _ASTROPY_MODULES): previous_modules[name] = sys.modules[name] del sys.modules[name] # Register a module finder that just raises an exception if # one of the tracked astropy modules is imported class _Finder: def find_spec(self, modulename, path=None, target=None): if any(modulename.startswith(m) for m in _ASTROPY_MODULES): msg = f"attempt to import astropy submodule({modulename}) during integration" raise Exception(msg) # noqa: TRY002 sys.meta_path.insert(0, _Finder()) # Setup complete return the test yield # Restore the previously imported modules. This is necessary as other code # may have already used the modules we removed from the cache (astropy.constants # defines some quantities). for name in previous_modules: sys.modules[name] = previous_modules[name] if isinstance(sys.meta_path[0], _Finder): sys.meta_path.pop(0) @pytest.mark.usefixtures("_clean_astropy_imports") def test_no_astropy_import(): """ Confirm that none of the ASDF plugins import astropy modules at import time. """ from asdf_astropy import integration integration.get_resource_mappings() integration.get_extensions() assert not any(k for k in sys.modules if any(k.startswith(m) for m in _ASTROPY_MODULES)) def test_no_core_extension_overwrite(): """ Check that this package (even though it implements converters for core types) does not overwrite the core extension provided by asdf """ import astropy.units as u # define a tree with a unit that will be serialized by this package # the tree itself will be serialized by asdf (which should result in # including asdf as a used extension) tree = {"meter": u.m} bio = io.BytesIO() asdf.AsdfFile(tree).write_to(bio) bio.seek(0) with asdf.open(bio) as af: packages = [ext["software"]["name"] for ext in af.tree["history"]["extensions"]] # check that both asdf and asdf-astropy are recorded as being used to # generate the file assert "asdf" in packages assert "asdf-astropy" in packages