diff --git a/.github/workflows/pytest.yaml b/.github/workflows/pytest.yaml index a44c84196..9debf5765 100644 --- a/.github/workflows/pytest.yaml +++ b/.github/workflows/pytest.yaml @@ -51,10 +51,10 @@ jobs: run: | uv run --no-sync \ pytest \ - -ra --color=yes --verbose \ + --color=yes --durations=20 -ra --verbose \ --sdmx-fetch-data \ --cov-report=xml \ - --numprocesses auto + --numprocesses=auto shell: bash - name: Upload test coverage to Codecov.io diff --git a/README.rst b/README.rst index 288f2bbe1..b98ad81fa 100644 --- a/README.rst +++ b/README.rst @@ -43,7 +43,7 @@ See https://sdmx1.readthedocs.io/en/latest/ for the latest docs per the ``main`` License ------- -Copyright 2014–2024, `sdmx1 developers `_ +Copyright 2014–2025, `sdmx1 developers `_ Licensed under the Apache License, Version 2.0 (the “License”); you may not use these files except in compliance with the License. You may obtain a copy of the License: diff --git a/doc/license.rst b/doc/license.rst index 55141dd79..34e5a1477 100644 --- a/doc/license.rst +++ b/doc/license.rst @@ -1,7 +1,7 @@ License ======= -Copyright 2014–2024, `sdmx1 developers `_. +Copyright 2014–2025, `sdmx1 developers `_. Licensed under the Apache License, Version 2.0 (the “License”); you may not use these files except in compliance with the License. You may obtain a copy of the diff --git a/doc/whatsnew.rst b/doc/whatsnew.rst index b73a24e9c..ecf48d77c 100644 --- a/doc/whatsnew.rst +++ b/doc/whatsnew.rst @@ -5,8 +5,11 @@ What's new? .. _2.21.1: -Next release -============ +.. Next release +.. ============ + +v2.21.1 (2025-01-14) +==================== - Bug fix for writing :xml:`` to SDMX-ML: invalid input SDMX-ML with non-standard classes tolerated in v2.21.0 (:pull:`218`) could not be round-tripped back to file (:pull:`221`). diff --git a/pyproject.toml b/pyproject.toml index 3e53e9650..fc87c2e11 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,6 +41,7 @@ dependencies = [ cache = ["requests-cache"] docs = ["furo", "IPython", "sphinx >= 8"] tests = [ + "filelock", "GitPython", "Jinja2", "pytest >= 5", diff --git a/sdmx/testing/__init__.py b/sdmx/testing/__init__.py index f456ac4e2..73dd92e37 100644 --- a/sdmx/testing/__init__.py +++ b/sdmx/testing/__init__.py @@ -9,6 +9,7 @@ import pandas as pd import pytest import responses +from filelock import FileLock from xdist import is_xdist_worker from sdmx.exceptions import HTTPError @@ -229,15 +230,20 @@ def msg(self, path): @pytest.fixture(scope="session") -def installed_schemas(mock_gh_api, tmp_path_factory): +def installed_schemas(worker_id, mock_gh_api, tmp_path_factory): """Fixture that ensures schemas are installed locally in a temporary directory.""" from sdmx.format.xml.common import install_schemas - dir = tmp_path_factory.mktemp("schemas") + # Determine a consistent path on each worker for schema installation + if worker_id == "master": # xdist controller *or* not using test distribution + dir = tmp_path_factory.mktemp("schemas") # pragma: no cover + else: # xdist worker: find relative to the parent of the basetemp for this worker + dir = tmp_path_factory.getbasetemp().parent.joinpath("schemas") - with mock_gh_api: + # Don't try to unpack from multiple workers at once + with mock_gh_api, FileLock(dir.with_suffix(".lock")): install_schemas(dir.joinpath("2.1"), Version["2.1"]) - install_schemas(dir.joinpath("3.0"), Version["3.0.0"]) + install_schemas(dir.joinpath("3.0.0"), Version["3.0.0"]) yield dir diff --git a/sdmx/tests/format/test_format_xml.py b/sdmx/tests/format/test_format_xml.py index 9ec8d77af..82be50624 100644 --- a/sdmx/tests/format/test_format_xml.py +++ b/sdmx/tests/format/test_format_xml.py @@ -31,7 +31,7 @@ def test_class_for_tag(): @pytest.mark.network -@pytest.mark.parametrize("version", ["2.1", "3.0"]) +@pytest.mark.parametrize("version", ["2.1", "3.0.0"]) def test_install_schemas(installed_schemas, version): """Test that XSD files are downloaded and ready for use in validation.""" # Look for a couple of the expected files @@ -62,10 +62,9 @@ def test_install_schemas_invalid_version(version): @pytest.mark.network -def test_validate_xml_from_v2_1_samples(tmp_path, specimen, installed_schemas): - """Use official samples to ensure validation of v2.1 messages works correctly.""" - # Samples are somewhat spread out, and some are known broken so we pick a bunch - for parts in [ +@pytest.mark.parametrize( + "parts", + [ ("v21", "xml", "common", "common.xml"), ("v21", "xml", "demography", "demography.xml"), ("v21", "xml", "demography", "esms.xml"), @@ -76,33 +75,39 @@ def test_validate_xml_from_v2_1_samples(tmp_path, specimen, installed_schemas): ("v21", "xml", "query", "response_cl_all.xml"), ("v21", "xml", "query", "query_esms_children.xml"), ("v21", "xml", "query", "response_esms_children.xml"), - ]: - with specimen(str(Path(*parts))) as sample: - assert sdmx.validate_xml( - sample, installed_schemas.joinpath("2.1"), version="2.1" - ) + ], +) +def test_validate_xml_from_v2_1_samples(tmp_path, specimen, installed_schemas, parts): + """Use official samples to ensure validation of v2.1 messages works correctly.""" + # Samples are somewhat spread out, and some are known broken so we pick a bunch + with specimen(str(Path(*parts))) as sample: + assert sdmx.validate_xml(sample, installed_schemas, version="2.1") + + +@pytest.fixture(scope="module") +def v30_zipball_path(installed_schemas): + yield _extracted_zipball(Version["3.0.0"]) @pytest.mark.network -def test_validate_xml_from_v3_0_samples(tmp_path, installed_schemas): +@pytest.mark.parametrize( + "parts", + [ + # Samples are somewhat spread out, and some are known broken so we pick a bunch + ("Codelist", "codelist.xml"), + ("Codelist", "codelist - extended.xml"), + ("Concept Scheme", "conceptscheme.xml"), + ("Data Structure Definition", "ECB_EXR.xml"), + ("Dataflow", "dataflow.xml"), + ("Geospatial", "geospatial_geographiccodelist.xml"), + ], +) +def test_validate_xml_from_v3_0_samples(installed_schemas, v30_zipball_path, parts): """Use official samples to ensure validation of v3.0 messages works correctly.""" - extracted_content = _extracted_zipball(Version["3.0.0"]) - - # Schemas as just in a flat directory - schema_dir = extracted_content.joinpath("schemas") - # Samples are somewhat spread out, and some are known broken so we pick a bunch - samples_dir = extracted_content.joinpath("samples") - samples = [ - samples_dir / "Codelist" / "codelist.xml", - samples_dir / "Codelist" / "codelist - extended.xml", - samples_dir / "Concept Scheme" / "conceptscheme.xml", - samples_dir / "Data Structure Definition" / "ECB_EXR.xml", - samples_dir / "Dataflow" / "dataflow.xml", - samples_dir / "Geospatial" / "geospatial_geographiccodelist.xml", - ] - for sample in samples: - assert sdmx.validate_xml(sample, schema_dir, version="3.0") + assert sdmx.validate_xml( + v30_zipball_path.joinpath("samples", *parts), installed_schemas, version="3.0.0" + ) @pytest.mark.network