diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 901a2515c..fe037da20 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -32,10 +32,6 @@ jobs: - ubuntu-latest - windows-latest - macos-latest - exclude: - # https://github.com/stac-utils/pystac/issues/1470 - - os: windows-latest - python-version: "3.13" steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 diff --git a/.gitignore b/.gitignore index fb913e75d..b36a5965e 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ stdout* /integration* .idea .vscode +.actrc # Sphinx documentation diff --git a/CHANGELOG.md b/CHANGELOG.md index afa2697e4..8a1047816 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Write STAC v1.1.0 ([#1427](https://github.com/stac-utils/pystac/pull/1427)) - Use [uv](https://github.com/astral-sh/uv) for development dependencies and docs ([#1439](https://github.com/stac-utils/pystac/pull/1439)) +- Correctly detect absolute file path ref on windows, reflecting change in python 3.13 ([#1475]https://github.com/stac-utils/pystac/pull/1475) (only effects python 3.13) ## [v1.11.0] - 2024-09-26 diff --git a/tests/test_catalog.py b/tests/test_catalog.py index cb8bdace1..dfb3db2ed 100644 --- a/tests/test_catalog.py +++ b/tests/test_catalog.py @@ -832,12 +832,16 @@ def test_generate_subcatalogs_works_for_subcatalogs_with_same_ids(self) -> None: assert len(result) == 6 catalog.normalize_hrefs("/") + for item in catalog.get_items(recursive=True): item_parent = item.get_parent() assert item_parent is not None parent_href = item_parent.self_href path_to_parent, _ = os.path.split(parent_href) - subcats = [el for el in path_to_parent.split("/") if el] + subcats = list( + Path(path_to_parent).parts[1:] + ) # Skip drive letter if present (Windows) + assert len(subcats) == 2, f" for item '{item.id}'" def test_map_items(self) -> None: diff --git a/tests/test_layout.py b/tests/test_layout.py index ff27f76b2..ad3996ed2 100644 --- a/tests/test_layout.py +++ b/tests/test_layout.py @@ -15,7 +15,12 @@ LayoutTemplate, TemplateLayoutStrategy, ) -from tests.utils import ARBITRARY_BBOX, ARBITRARY_GEOM, TestCases +from tests.utils import ( + ARBITRARY_BBOX, + ARBITRARY_GEOM, + TestCases, + path_includes_drive_letter, +) class LayoutTemplateTest(unittest.TestCase): @@ -412,6 +417,9 @@ def test_produces_layout_for_item(self) -> None: class AsIsLayoutStrategyTest(unittest.TestCase): def setUp(self) -> None: self.strategy = AsIsLayoutStrategy() + self.expected_local_href = ( + "/an/href" if not path_includes_drive_letter() else "D:/an/href" + ) def test_catalog(self) -> None: cat = pystac.Catalog(id="test", description="test desc") @@ -421,7 +429,7 @@ def test_catalog(self) -> None: href = self.strategy.get_href( cat, parent_dir="https://example.com", is_root=True ) - self.assertEqual(href, "/an/href") + self.assertEqual(href, self.expected_local_href) def test_collection(self) -> None: collection = TestCases.case_8() @@ -434,7 +442,7 @@ def test_collection(self) -> None: href = self.strategy.get_href( collection, parent_dir="https://example.com", is_root=True ) - self.assertEqual(href, "/an/href") + self.assertEqual(href, self.expected_local_href) def test_item(self) -> None: collection = TestCases.case_8() @@ -444,7 +452,7 @@ def test_item(self) -> None: self.strategy.get_href(item, parent_dir="http://example.com") item.set_self_href("/an/href") href = self.strategy.get_href(item, parent_dir="http://example.com") - self.assertEqual(href, "/an/href") + self.assertEqual(href, self.expected_local_href) class APILayoutStrategyTest(unittest.TestCase): diff --git a/tests/test_utils.py b/tests/test_utils.py index 76ea0d50a..5e9f85cef 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -21,7 +21,7 @@ safe_urlparse, str_to_datetime, ) -from tests.utils import TestCases +from tests.utils import TestCases, path_includes_drive_letter class UtilsTest(unittest.TestCase): @@ -219,7 +219,6 @@ def test_is_absolute_href(self) -> None: ("item.json", False), ("./item.json", False), ("../item.json", False), - ("/item.json", True), ("http://stacspec.org/item.json", True), ] @@ -227,9 +226,26 @@ def test_is_absolute_href(self) -> None: actual = is_absolute_href(href) self.assertEqual(actual, expected) + def test_is_absolute_href_os_aware(self) -> None: + # Test cases of (href, expected) + + is_windows = os.name == "nt" + incl_drive_letter = path_includes_drive_letter() + test_cases = [ + ("/item.json", not incl_drive_letter), + ("/home/someuser/Downloads/item.json", not incl_drive_letter), + ("d:/item.json", is_windows), + ("c:/files/more_files/item.json", is_windows), + ] + + for href, expected in test_cases: + actual = is_absolute_href(href) + self.assertEqual(actual, expected) + @pytest.mark.skipif(os.name != "nt", reason="Windows only test") def test_is_absolute_href_windows(self) -> None: # Test cases of (href, expected) + test_cases = [ ("item.json", False), (".\\item.json", False), diff --git a/tests/utils/__init__.py b/tests/utils/__init__.py index 1dc552f57..aa6af7c7e 100644 --- a/tests/utils/__init__.py +++ b/tests/utils/__init__.py @@ -4,6 +4,7 @@ "ARBITRARY_BBOX", "ARBITRARY_EXTENT", "MockStacIO", + "path_includes_drive_letter", ] from copy import deepcopy from datetime import datetime @@ -12,6 +13,7 @@ from dateutil.parser import parse import pystac +from tests.utils.os_utils import path_includes_drive_letter from tests.utils.stac_io_mock import MockStacIO from tests.utils.test_cases import ( ARBITRARY_BBOX, diff --git a/tests/utils/os_utils.py b/tests/utils/os_utils.py new file mode 100644 index 000000000..89f99b523 --- /dev/null +++ b/tests/utils/os_utils.py @@ -0,0 +1,8 @@ +import os +import sys + + +def path_includes_drive_letter() -> bool: + return ( + sys.version_info.major >= 3 and sys.version_info.minor >= 13 and os.name == "nt" + )