From 705dd8fc8572c5b4c85d8bf06b2df77d8dd73ca8 Mon Sep 17 00:00:00 2001 From: amc-corey-cox <69321580+amc-corey-cox@users.noreply.github.com> Date: Thu, 4 Jun 2026 16:25:15 -0500 Subject: [PATCH 1/2] fix(linkml_files): correct GitHub raw/pages URLs and convert tests to pytest --- linkml_model/linkml_files.py | 19 +++- tests/test_linkml_files.py | 164 +++++++++++++++++++++++------------ 2 files changed, 122 insertions(+), 61 deletions(-) diff --git a/linkml_model/linkml_files.py b/linkml_model/linkml_files.py index 98005954d..4cec68ee6 100644 --- a/linkml_model/linkml_files.py +++ b/linkml_model/linkml_files.py @@ -119,8 +119,14 @@ def _build_path(source: Source, fmt: Format) -> str: return filename +# Published schema/artifact files live under the ``linkml_model`` package directory +# on GitHub (raw + GitHub Pages), so GitHub URLs need this prefix even though +# ``LOCAL_PATH_FOR`` does not (its base already points inside the package). +PACKAGE_DIR = "linkml_model" + + def _build_loc(base: str, source: Source, fmt: Format) -> str: - return f"{base}{_build_path(source, fmt)}".replace('blob/', '') + return f"{base}{PACKAGE_DIR}/{_build_path(source, fmt)}".replace('blob/', '') def URL_FOR(source: Source, fmt: Format) -> str: @@ -133,8 +139,13 @@ def LOCAL_PATH_FOR(source: Source, fmt: Format) -> str: return os.path.join(LOCAL_BASE, _build_path(source, fmt)) -def GITHUB_IO_PATH_FOR(source: Source, fmt: Format) -> str: - return _build_loc(GITHUB_IO_BASE, source, fmt) +def GITHUB_IO_PATH_FOR(source: Source, fmt: Format, version: str = "latest") -> str: + """Return the GitHub Pages URL for source in format. + + The docs site is versioned, so files live under ``/linkml_model/``; + ``version`` defaults to the ``latest`` alias. + """ + return f"{GITHUB_IO_BASE}{version}/{PACKAGE_DIR}/{_build_path(source, fmt)}" def GITHUB_PATH_FOR(source: Source, @@ -159,7 +170,7 @@ def tag_to_commit(tag: str) -> str: # Return the absolute latest entry for branch if release is ReleaseTag.LATEST or (release is ReleaseTag.CURRENT and branch != "main"): - return f"{GITHUB_BASE}{branch}/{_build_path(source, fmt)}" + return f"{GITHUB_BASE}{branch}/{PACKAGE_DIR}/{_build_path(source, fmt)}" # Return the latest published version elif release is ReleaseTag.CURRENT: diff --git a/tests/test_linkml_files.py b/tests/test_linkml_files.py index 87157d7a6..fe58c4233 100644 --- a/tests/test_linkml_files.py +++ b/tests/test_linkml_files.py @@ -1,65 +1,115 @@ import os -import unittest import re +from pathlib import Path + +import pytest import linkml_model.linkml_files as fileloc -from linkml_model.linkml_files import URL_FOR, Format, Source, LOCAL_PATH_FOR, GITHUB_IO_PATH_FOR, GITHUB_PATH_FOR, \ - ReleaseTag +from linkml_model.linkml_files import ( + URL_FOR, + Format, + Source, + LOCAL_PATH_FOR, + GITHUB_IO_PATH_FOR, + GITHUB_PATH_FOR, + ReleaseTag, +) from tests import abspath -from pathlib import Path root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "linkml_model")) -SKIP_GITHUB_API = False # True means don't do the github API tests - - -class LinkMLFilesTestCase(unittest.TestCase): - """ Test that linkml_model/linkml_files.py work """ - def test_basic_rules(self): - self.assertEqual("https://w3id.org/linkml/annotations.yaml", - URL_FOR(Source.ANNOTATIONS, Format.YAML)) - self.assertEqual("https://w3id.org/linkml/meta.model.context.jsonld", - URL_FOR(Source.META, Format.NATIVE_JSONLD)) - self.assertEqual(os.path.join(root_path, "model/schema/meta.yaml"), - LOCAL_PATH_FOR(Source.META, Format.YAML)) - print(LOCAL_PATH_FOR(Source.META, Format.YAML)) - self.assertEqual(os.path.join(root_path, "jsonld/types.model.context.jsonld"), - LOCAL_PATH_FOR(Source.TYPES, Format.NATIVE_JSONLD)) - self.assertEqual("https://linkml.github.io/linkml-model/model/schema/meta.yaml", - GITHUB_IO_PATH_FOR(Source.META, Format.YAML)) - self.assertEqual("https://linkml.github.io/linkml-model/jsonld/types.model.context.jsonld", - GITHUB_IO_PATH_FOR(Source.TYPES, Format.NATIVE_JSONLD)) - self.assertEqual("https://raw.githubusercontent.com/linkml/linkml-model/main/jsonld/meta.model.context.jsonld", - GITHUB_PATH_FOR(Source.META, Format.NATIVE_JSONLD, ReleaseTag.LATEST)) - self.assertEqual("https://raw.githubusercontent.com/linkml/linkml-model/testing_branch/owl/mappings.owl.ttl", - GITHUB_PATH_FOR(Source.MAPPINGS, Format.OWL, branch="testing_branch")) - - @unittest.skipIf(SKIP_GITHUB_API, "Github API tests skipped") - def test_github_specific_rules(self): - """ - Test accesses that require github API to access - This is separate because we can only do so many tests per hour w/o getting a 403 - """ - self.assertEqual("https://raw.githubusercontent.com/linkml/linkml-model/f30637f5a585f3fc4b12fd3dbb3e7e95108d4b42/jsonld/meta.model.context.jsonld", - GITHUB_PATH_FOR(Source.META, Format.NATIVE_JSONLD, "v0.0.1")) - current_loc = re.sub(r'linkml-model/[0-9a-f]*/', 'linkml-model/SHA/', GITHUB_PATH_FOR(Source.TYPES, Format.YAML)) - self.assertEqual("https://raw.githubusercontent.com/linkml/linkml-model/SHA/model/schema/types.yaml", current_loc) - # TODO: We may want to raise an error here? - self.assertEqual('https://raw.githubusercontent.com/linkml/linkml-model/missing_branch/owl/mappings.owl.ttl', - GITHUB_PATH_FOR(Source.MAPPINGS, Format.OWL, branch="missing_branch")) - - with self.assertRaises(ValueError) as e: - GITHUB_PATH_FOR(Source.META, Format.RDF, "vv0.0.1") - self.assertEqual("Tag: vv0.0.1 not found!", str(e.exception)) - - def test_shorthand_paths(self): - self.assertEqual('meta', str(fileloc.meta)) - self.assertEqual('meta.yaml', str(fileloc.meta.yaml)) - self.assertEqual('meta.py', str(fileloc.meta.python)) - self.assertEqual(Path(abspath('linkml_model/model/schema/meta.yaml')), Path(fileloc.meta.yaml.file)) - self.assertEqual('https://linkml.github.io/linkml-model/model/schema/meta.yaml', str(fileloc.meta.yaml.github_loc())) - self.assertEqual('https://raw.githubusercontent.com/linkml/linkml-model/f30637f5a585f3fc4b12fd3dbb3e7e95108d4b42/model/schema/meta.yaml', str(fileloc.meta.yaml.github_loc('v0.0.1'))) - - -if __name__ == '__main__': - unittest.main() +# The GitHub API tests resolve tags/releases to commits via the live GitHub API, +# which is rate-limited and flaky in CI, so they are opt-in. +# Set LINKML_TEST_GITHUB_API=1 to run them. +RUN_GITHUB_API = bool(os.environ.get("LINKML_TEST_GITHUB_API")) + + +def test_url_for(): + """URL_FOR builds w3id.org URLs from the format extension.""" + assert URL_FOR(Source.ANNOTATIONS, Format.YAML) == "https://w3id.org/linkml/annotations.yaml" + assert URL_FOR(Source.META, Format.NATIVE_JSONLD) == "https://w3id.org/linkml/meta.model.context.jsonld" + + +def test_local_path_for(): + """LOCAL_PATH_FOR points at the on-disk file inside the linkml_model package.""" + assert LOCAL_PATH_FOR(Source.META, Format.YAML) == os.path.join(root_path, "model/schema/meta.yaml") + assert LOCAL_PATH_FOR(Source.TYPES, Format.NATIVE_JSONLD) == os.path.join( + root_path, "jsonld/types.model.context.jsonld" + ) + + +def test_github_io_path_for(): + """GitHub Pages URLs are versioned and live under /linkml_model/.""" + assert ( + GITHUB_IO_PATH_FOR(Source.META, Format.YAML) + == "https://linkml.github.io/linkml-model/latest/linkml_model/model/schema/meta.yaml" + ) + assert ( + GITHUB_IO_PATH_FOR(Source.TYPES, Format.NATIVE_JSONLD) + == "https://linkml.github.io/linkml-model/latest/linkml_model/jsonld/types.model.context.jsonld" + ) + # the version segment is overridable + assert ( + GITHUB_IO_PATH_FOR(Source.META, Format.YAML, version="v1.11.0") + == "https://linkml.github.io/linkml-model/v1.11.0/linkml_model/model/schema/meta.yaml" + ) + + +def test_github_path_for_branch(): + """Branch / LATEST raw URLs are pure string formatting (no network).""" + assert ( + GITHUB_PATH_FOR(Source.META, Format.NATIVE_JSONLD, ReleaseTag.LATEST) + == "https://raw.githubusercontent.com/linkml/linkml-model/main/linkml_model/jsonld/meta.model.context.jsonld" + ) + assert ( + GITHUB_PATH_FOR(Source.MAPPINGS, Format.OWL, branch="testing_branch") + == "https://raw.githubusercontent.com/linkml/linkml-model/testing_branch/linkml_model/owl/mappings.owl.ttl" + ) + # TODO: a non-existent branch is not validated and yields a non-resolving URL + assert ( + GITHUB_PATH_FOR(Source.MAPPINGS, Format.OWL, branch="missing_branch") + == "https://raw.githubusercontent.com/linkml/linkml-model/missing_branch/linkml_model/owl/mappings.owl.ttl" + ) + + +def test_github_path_for_release_and_branch_conflict(): + """Specifying both a release and a non-main branch is an error (no network).""" + with pytest.raises(ValueError): + GITHUB_PATH_FOR(Source.META, Format.RDF, ReleaseTag.LATEST, branch="some_branch") + + +@pytest.mark.skipif( + not RUN_GITHUB_API, + reason="set LINKML_TEST_GITHUB_API=1 to run rate-limited GitHub API tests", +) +def test_github_specific_rules(): + """Tag / release resolution via the GitHub API. + + Note: pre-restructure tags (e.g. v0.0.1) predate the move of artifacts under + the ``linkml_model`` package directory, so the constructed URL is correct in + form but will not resolve for those very old tags. + """ + assert GITHUB_PATH_FOR(Source.META, Format.NATIVE_JSONLD, "v0.0.1") == ( + "https://raw.githubusercontent.com/linkml/linkml-model/" + "f30637f5a585f3fc4b12fd3dbb3e7e95108d4b42/linkml_model/jsonld/meta.model.context.jsonld" + ) + current_loc = re.sub( + r"linkml-model/[0-9a-f]{40}/", "linkml-model/SHA/", GITHUB_PATH_FOR(Source.TYPES, Format.YAML) + ) + assert current_loc == ( + "https://raw.githubusercontent.com/linkml/linkml-model/SHA/linkml_model/model/schema/types.yaml" + ) + with pytest.raises(ValueError, match="Tag: vv0.0.1 not found!"): + GITHUB_PATH_FOR(Source.META, Format.RDF, "vv0.0.1") + + +def test_shorthand_paths(): + """The ModelFile shorthand accessors mirror the underlying functions.""" + assert str(fileloc.meta) == "meta" + assert str(fileloc.meta.yaml) == "meta.yaml" + assert str(fileloc.meta.python) == "meta.py" + assert Path(fileloc.meta.yaml.file) == Path(abspath("linkml_model/model/schema/meta.yaml")) + assert ( + str(fileloc.meta.yaml.github_loc()) + == "https://linkml.github.io/linkml-model/latest/linkml_model/model/schema/meta.yaml" + ) From 8d13291ae5bce90d019f1e089d4f60996baff014 Mon Sep 17 00:00:00 2001 From: amc-corey-cox <69321580+amc-corey-cox@users.noreply.github.com> Date: Fri, 5 Jun 2026 09:18:18 -0500 Subject: [PATCH 2/2] Address PR review feedback --- tests/test_linkml_files.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/test_linkml_files.py b/tests/test_linkml_files.py index fe58c4233..ddd5f7c4a 100644 --- a/tests/test_linkml_files.py +++ b/tests/test_linkml_files.py @@ -21,7 +21,7 @@ # The GitHub API tests resolve tags/releases to commits via the live GitHub API, # which is rate-limited and flaky in CI, so they are opt-in. # Set LINKML_TEST_GITHUB_API=1 to run them. -RUN_GITHUB_API = bool(os.environ.get("LINKML_TEST_GITHUB_API")) +RUN_GITHUB_API = os.environ.get("LINKML_TEST_GITHUB_API", "").lower() in {"1", "true", "yes", "on"} def test_url_for(): @@ -85,9 +85,10 @@ def test_github_path_for_release_and_branch_conflict(): def test_github_specific_rules(): """Tag / release resolution via the GitHub API. - Note: pre-restructure tags (e.g. v0.0.1) predate the move of artifacts under - the ``linkml_model`` package directory, so the constructed URL is correct in - form but will not resolve for those very old tags. + Note: the builder applies the current ``linkml_model/`` layout uniformly to + all tags. Pre-restructure tags (e.g. v0.0.1) stored artifacts at the repo + root, so the URL generated for those very old tags uses the current layout + rather than their historical one and will not resolve. """ assert GITHUB_PATH_FOR(Source.META, Format.NATIVE_JSONLD, "v0.0.1") == ( "https://raw.githubusercontent.com/linkml/linkml-model/"