diff --git a/.gitignore b/.gitignore index f5e7a23..31b3d02 100644 --- a/.gitignore +++ b/.gitignore @@ -161,4 +161,6 @@ cython_debug/ #.idea/ src/spdx_python_model/bindings/ -gen/*.py +gen/**/* +!gen/.keep + diff --git a/gen/.keep b/gen/.keep new file mode 100644 index 0000000..e69de29 diff --git a/gen/generate-bindings b/gen/generate-bindings deleted file mode 100755 index b963c55..0000000 --- a/gen/generate-bindings +++ /dev/null @@ -1,25 +0,0 @@ -#! /bin/sh -# -# SPDX-License-Identifier: Apache-2.0 - -set -e - -# SPDX versions to generate -SPDX_VERSIONS="3.0.1" - -mkdir -p "gen" - -echo "# Import all versions" > __init__.py - -for v in $SPDX_VERSIONS; do - MODNAME="v$(echo "$v" | sed 's/[^a-zA-Z0-9_]/_/g')" - - shacl2code generate --input https://spdx.org/rdf/$v/spdx-model.ttl \ - --input https://spdx.org/rdf/$v/spdx-json-serialize-annotations.ttl \ - --context https://spdx.org/rdf/$v/spdx-context.jsonld \ - --license Apache-2.0 \ - python \ - -o "$MODNAME.py" - - echo "from . import $MODNAME" >> __init__.py -done diff --git a/pyproject.toml b/pyproject.toml index 60df960..b3811e9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,6 +23,7 @@ license = "Apache-2.0" [project.optional-dependencies] dev = [ "pytest >= 7.4", + "requests == 2.32.5", ] [project.urls] @@ -35,6 +36,7 @@ requires = [ "hatchling", "hatch-build-scripts", "shacl2code == 0.0.25", + "requests == 2.32.5", ] build-backend = "hatchling.build" @@ -51,7 +53,7 @@ out_dir = "src/spdx_python_model/bindings" work_dir = "gen" clean_out_dir = true commands = [ - "./generate-bindings" + "../scripts/generate-bindings" ] artifacts = [ "*.py" diff --git a/scripts/generate-bindings b/scripts/generate-bindings new file mode 100755 index 0000000..99685fb --- /dev/null +++ b/scripts/generate-bindings @@ -0,0 +1,108 @@ +#! /usr/bin/env python3 +# +# SPDX-License-Identifier: Apache-2.0 + + +import argparse +import sys +import requests +import subprocess +import re +import os +from pathlib import Path + +SPDX_VERSIONS = ("3.0.1",) + +THIS_DIR = Path(__file__).parent +OUT_DIR = THIS_DIR.parent / "gen" + +INIT_FILE_PATH = OUT_DIR / "__init__.py" + + +def download_url(when, session, url, output): + if output.exists(): + if when in ("if-missing", "never"): + print(f"File {output} already exists, skipping download.") + return + elif when == "never": + raise FileNotFoundError( + f"File {output} does not exist and download is disabled." + ) + + print(f"Downloading {url} to {output}...") + response = session.get(url) + response.raise_for_status() + with output.open("wb") as f: + f.write(response.content) + + +def main(): + parser = argparse.ArgumentParser( + description="Generate SPDX Python bindings from RDF SHACL model" + ) + parser.add_argument( + "--download", + choices=("always", "never", "if-missing"), + default=os.environ.get("SPDX_PYTHON_MODEL_DOWNLOAD", "if-missing"), + help="Controls how model files are downloaded. 'always' will force downloading files, 'never' will never download files, 'if-missing' will download the files if they do not already exits. Default is '%(default)s'", + ) + args = parser.parse_args() + + OUT_DIR.mkdir(parents=True, exist_ok=True) + + with INIT_FILE_PATH.open("w") as init_file, requests.Session() as session: + init_file.write("# Import all versions\n") + + for version in SPDX_VERSIONS: + modname = "v" + re.sub(r"[^a-zA-Z0-9_]", "_", version) + dl_dir = OUT_DIR / "download" / version + dl_dir.mkdir(parents=True, exist_ok=True) + + context_url = f"https://spdx.org/rdf/{version}/spdx-context.jsonld" + + download_url( + args.download, + session, + context_url, + dl_dir / "context.jsonld", + ) + download_url( + args.download, + session, + f"https://spdx.org/rdf/{version}/spdx-model.ttl", + dl_dir / "model.ttl", + ) + download_url( + args.download, + session, + f"https://spdx.org/rdf/{version}/spdx-json-serialize-annotations.ttl", + dl_dir / "annotations.ttl", + ) + p = subprocess.run( + [ + "shacl2code", + "generate", + "--input", + dl_dir / "model.ttl", + "--input", + dl_dir / "annotations.ttl", + "--context-url", + dl_dir / "context.jsonld", + context_url, + "--license", + "Apache-2.0", + "python", + "-o", + OUT_DIR / f"{modname}.py", + ] + ) + if p.returncode != 0: + return p.returncode + + init_file.write(f"from . import {modname}\n") + + return 0 + + +if __name__ == "__main__": + sys.exit(main())