2023-09-14 09:45:01 +00:00
|
|
|
import shutil
|
|
|
|
import os
|
2024-08-06 01:41:38 +00:00
|
|
|
import sys
|
2023-09-14 09:45:01 +00:00
|
|
|
import traceback
|
2024-08-06 01:41:38 +00:00
|
|
|
from pdb import post_mortem
|
2024-08-07 04:40:23 +00:00
|
|
|
import subprocess
|
2023-09-14 09:45:01 +00:00
|
|
|
|
2023-08-25 07:22:47 +00:00
|
|
|
from argparse import ArgumentParser
|
|
|
|
from pathlib import Path
|
|
|
|
from linkml_runtime.dumpers import yaml_dumper
|
2023-09-14 09:45:01 +00:00
|
|
|
from rich.live import Live
|
|
|
|
from rich.panel import Panel
|
|
|
|
from rich.console import Group
|
|
|
|
from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn, Column
|
|
|
|
from rich import print
|
2023-08-29 05:16:58 +00:00
|
|
|
from nwb_linkml.generators.pydantic import NWBPydanticGenerator
|
2023-08-25 07:22:47 +00:00
|
|
|
|
2024-07-10 06:52:58 +00:00
|
|
|
from nwb_linkml.providers import LinkMLProvider, PydanticProvider
|
2024-07-31 08:13:31 +00:00
|
|
|
from nwb_linkml.providers.git import NWB_CORE_REPO, HDMF_COMMON_REPO, GitRepo
|
2023-09-14 09:45:01 +00:00
|
|
|
from nwb_linkml.io import schema as io
|
2023-09-06 02:25:20 +00:00
|
|
|
|
2024-09-26 08:27:36 +00:00
|
|
|
|
2024-08-21 03:26:00 +00:00
|
|
|
def make_tmp_dir(clear: bool = False) -> Path:
|
|
|
|
# use a directory underneath this one as the temporary directory rather than
|
|
|
|
# the default hidden one
|
|
|
|
tmp_dir = Path(__file__).parent / "__tmp__"
|
|
|
|
if tmp_dir.exists() and clear:
|
|
|
|
for p in tmp_dir.iterdir():
|
|
|
|
if p.is_dir() and not p.name == "git":
|
2024-08-21 05:45:24 +00:00
|
|
|
shutil.rmtree(p)
|
2024-08-21 03:26:00 +00:00
|
|
|
tmp_dir.mkdir(exist_ok=True)
|
|
|
|
return tmp_dir
|
|
|
|
|
|
|
|
|
2024-08-05 23:05:44 +00:00
|
|
|
def generate_versions(
|
|
|
|
yaml_path: Path,
|
|
|
|
pydantic_path: Path,
|
|
|
|
dry_run: bool = False,
|
|
|
|
repo: GitRepo = NWB_CORE_REPO,
|
2024-08-06 01:41:38 +00:00
|
|
|
pdb=False,
|
2024-09-26 08:02:16 +00:00
|
|
|
latest: bool = False,
|
2024-08-05 23:05:44 +00:00
|
|
|
):
|
2023-09-14 09:45:01 +00:00
|
|
|
"""
|
|
|
|
Generate linkml models for all versions
|
|
|
|
"""
|
2024-08-05 23:05:44 +00:00
|
|
|
# repo.clone(force=True)
|
2023-09-14 09:45:01 +00:00
|
|
|
repo.clone()
|
|
|
|
|
2024-08-21 03:26:00 +00:00
|
|
|
tmp_dir = make_tmp_dir()
|
2023-09-14 09:45:01 +00:00
|
|
|
|
|
|
|
linkml_provider = LinkMLProvider(path=tmp_dir, verbose=False)
|
|
|
|
pydantic_provider = PydanticProvider(path=tmp_dir, verbose=False)
|
|
|
|
|
|
|
|
failed_versions = {}
|
|
|
|
|
2024-09-26 08:02:16 +00:00
|
|
|
if latest:
|
|
|
|
versions = [repo.namespace.versions[-1]]
|
|
|
|
else:
|
|
|
|
versions = repo.namespace.versions
|
|
|
|
|
2023-09-14 09:45:01 +00:00
|
|
|
overall_progress = Progress()
|
2024-09-26 08:02:16 +00:00
|
|
|
overall_task = overall_progress.add_task("All Versions", total=len(versions))
|
2023-09-14 09:45:01 +00:00
|
|
|
|
|
|
|
build_progress = Progress(
|
2024-08-05 23:05:44 +00:00
|
|
|
TextColumn(
|
|
|
|
"[bold blue]{task.fields[name]} - [bold green]{task.fields[action]}",
|
|
|
|
table_column=Column(ratio=1),
|
|
|
|
),
|
|
|
|
BarColumn(table_column=Column(ratio=1), bar_width=None),
|
2023-09-14 09:45:01 +00:00
|
|
|
)
|
|
|
|
panel = Panel(Group(build_progress, overall_progress))
|
|
|
|
|
2024-07-09 04:37:02 +00:00
|
|
|
try:
|
|
|
|
with Live(panel) as live:
|
|
|
|
# make pbar tasks
|
|
|
|
linkml_task = None
|
|
|
|
pydantic_task = None
|
|
|
|
|
2024-09-26 08:02:16 +00:00
|
|
|
for version in versions:
|
2024-07-09 04:37:02 +00:00
|
|
|
# build linkml
|
|
|
|
try:
|
|
|
|
# check out the version (this should also refresh the hdmf-common schema)
|
2024-08-05 23:05:44 +00:00
|
|
|
linkml_task = build_progress.add_task(
|
|
|
|
"", name=version, action="Checkout Version", total=3
|
|
|
|
)
|
2024-07-09 04:37:02 +00:00
|
|
|
repo.tag = version
|
|
|
|
build_progress.update(linkml_task, advance=1, action="Load Namespaces")
|
|
|
|
|
2024-07-31 08:13:31 +00:00
|
|
|
if repo.namespace == NWB_CORE_REPO:
|
2024-08-13 02:25:12 +00:00
|
|
|
# first load HDMF common
|
2024-08-05 23:05:44 +00:00
|
|
|
hdmf_common_ns = io.load_namespace_adapter(
|
|
|
|
repo.temp_directory / "hdmf-common-schema" / "common" / "namespace.yaml"
|
|
|
|
)
|
2024-08-13 02:25:12 +00:00
|
|
|
# then load nwb core
|
|
|
|
core_ns = io.load_namespace_adapter(
|
|
|
|
repo.namespace_file, imported=[hdmf_common_ns]
|
|
|
|
)
|
|
|
|
|
|
|
|
else:
|
|
|
|
# otherwise just load HDMF
|
|
|
|
core_ns = io.load_namespace_adapter(repo.namespace_file)
|
2024-07-31 08:13:31 +00:00
|
|
|
|
2024-07-09 04:37:02 +00:00
|
|
|
build_progress.update(linkml_task, advance=1, action="Build LinkML")
|
|
|
|
|
2024-10-03 03:20:34 +00:00
|
|
|
linkml_res = linkml_provider.build(core_ns, force=True)
|
2024-07-09 04:37:02 +00:00
|
|
|
build_progress.update(linkml_task, advance=1, action="Built LinkML")
|
|
|
|
|
|
|
|
# build pydantic
|
2024-07-31 08:13:31 +00:00
|
|
|
ns_files = [res.namespace for res in linkml_res.values()]
|
2024-07-09 04:37:02 +00:00
|
|
|
|
2024-08-05 23:05:44 +00:00
|
|
|
pydantic_task = build_progress.add_task(
|
|
|
|
"", name=version, action="", total=len(ns_files)
|
|
|
|
)
|
2024-07-30 00:34:28 +00:00
|
|
|
for schema in ns_files:
|
2024-08-05 23:05:44 +00:00
|
|
|
pbar_string = schema.parts[-3]
|
2024-07-09 04:37:02 +00:00
|
|
|
build_progress.update(pydantic_task, action=pbar_string)
|
2024-08-21 05:45:24 +00:00
|
|
|
pydantic_provider.build(
|
2024-10-03 03:20:34 +00:00
|
|
|
schema, versions=core_ns.versions, split=True, parallel=True, force=True
|
2024-08-21 05:45:24 +00:00
|
|
|
)
|
2024-07-09 04:37:02 +00:00
|
|
|
build_progress.update(pydantic_task, advance=1)
|
2024-08-05 23:05:44 +00:00
|
|
|
build_progress.update(pydantic_task, action="Built Pydantic")
|
2024-07-09 04:37:02 +00:00
|
|
|
|
|
|
|
except Exception as e:
|
2024-08-06 01:41:38 +00:00
|
|
|
if pdb:
|
|
|
|
live.stop()
|
|
|
|
post_mortem()
|
|
|
|
sys.exit(1)
|
|
|
|
|
2023-09-14 09:45:01 +00:00
|
|
|
build_progress.stop_task(linkml_task)
|
2024-07-09 04:37:02 +00:00
|
|
|
if linkml_task is not None:
|
2024-08-05 23:05:44 +00:00
|
|
|
build_progress.update(linkml_task, action="[bold red]LinkML Build Failed")
|
2024-07-09 04:37:02 +00:00
|
|
|
build_progress.stop_task(linkml_task)
|
|
|
|
if pydantic_task is not None:
|
2024-08-05 23:05:44 +00:00
|
|
|
build_progress.update(pydantic_task, action="[bold red]LinkML Build Failed")
|
2024-07-09 04:37:02 +00:00
|
|
|
build_progress.stop_task(pydantic_task)
|
|
|
|
failed_versions[version] = traceback.format_exception(e)
|
|
|
|
|
|
|
|
finally:
|
|
|
|
overall_progress.update(overall_task, advance=1)
|
|
|
|
linkml_task = None
|
|
|
|
pydantic_task = None
|
|
|
|
|
|
|
|
if not dry_run:
|
2024-08-28 03:49:53 +00:00
|
|
|
shutil.copytree(tmp_dir / "linkml", yaml_path, dirs_exist_ok=True)
|
|
|
|
shutil.copytree(tmp_dir / "pydantic", pydantic_path, dirs_exist_ok=True)
|
|
|
|
shutil.rmtree(tmp_dir / "linkml")
|
|
|
|
shutil.rmtree(tmp_dir / "pydantic")
|
2024-07-09 04:37:02 +00:00
|
|
|
|
|
|
|
# make inits to use the schema! we don't usually do this in the
|
|
|
|
# provider class because we directly import the files there.
|
2024-08-28 03:49:53 +00:00
|
|
|
with open(pydantic_path / "__init__.py", "w") as initfile:
|
2024-08-05 23:05:44 +00:00
|
|
|
initfile.write(" ")
|
2024-07-09 04:37:02 +00:00
|
|
|
|
2024-08-07 04:40:23 +00:00
|
|
|
subprocess.run(["black", "."])
|
|
|
|
|
2024-07-09 04:37:02 +00:00
|
|
|
finally:
|
|
|
|
if len(failed_versions) > 0:
|
2024-08-05 23:05:44 +00:00
|
|
|
print("Failed Building Versions:")
|
2024-07-09 04:37:02 +00:00
|
|
|
print(failed_versions)
|
2023-09-14 09:45:01 +00:00
|
|
|
|
|
|
|
|
2023-08-25 07:22:47 +00:00
|
|
|
def parser() -> ArgumentParser:
|
2024-08-05 23:05:44 +00:00
|
|
|
parser = ArgumentParser("Generate all available versions of NWB core schema")
|
2023-08-25 07:22:47 +00:00
|
|
|
parser.add_argument(
|
2024-08-05 23:05:44 +00:00
|
|
|
"--yaml",
|
2023-08-25 07:22:47 +00:00
|
|
|
help="directory to export linkML schema to",
|
|
|
|
type=Path,
|
2024-08-28 03:49:53 +00:00
|
|
|
default=Path(__file__).parent.parent
|
|
|
|
/ "nwb_models"
|
|
|
|
/ "src"
|
|
|
|
/ "nwb_models"
|
|
|
|
/ "schema"
|
|
|
|
/ "linkml",
|
2023-08-25 07:22:47 +00:00
|
|
|
)
|
|
|
|
parser.add_argument(
|
2024-08-05 23:05:44 +00:00
|
|
|
"--pydantic",
|
2023-08-25 07:22:47 +00:00
|
|
|
help="directory to export pydantic models",
|
|
|
|
type=Path,
|
2024-08-28 03:49:53 +00:00
|
|
|
default=Path(__file__).parent.parent
|
|
|
|
/ "nwb_models"
|
|
|
|
/ "src"
|
|
|
|
/ "nwb_models"
|
|
|
|
/ "models"
|
|
|
|
/ "pydantic",
|
2024-07-31 08:13:31 +00:00
|
|
|
)
|
2024-08-05 23:05:44 +00:00
|
|
|
parser.add_argument("--hdmf", help="Only generate the HDMF namespaces", action="store_true")
|
2023-09-14 09:45:01 +00:00
|
|
|
parser.add_argument(
|
2024-08-05 23:05:44 +00:00
|
|
|
"--latest",
|
2023-09-14 09:45:01 +00:00
|
|
|
help="Only generate the latest version of the core schemas.",
|
2024-08-05 23:05:44 +00:00
|
|
|
action="store_true",
|
2023-09-14 09:45:01 +00:00
|
|
|
)
|
|
|
|
parser.add_argument(
|
2024-08-05 23:05:44 +00:00
|
|
|
"--dry-run",
|
|
|
|
help=(
|
|
|
|
"Generate schema and pydantic models without moving them into the target directories,"
|
|
|
|
" for testing purposes"
|
|
|
|
),
|
|
|
|
action="store_true",
|
2023-09-14 09:45:01 +00:00
|
|
|
)
|
2024-09-04 07:04:21 +00:00
|
|
|
parser.add_argument(
|
|
|
|
"--debug",
|
|
|
|
help="Add annotations to generated schema that indicate how they were generated",
|
|
|
|
action="store_true",
|
|
|
|
)
|
2024-08-06 01:41:38 +00:00
|
|
|
parser.add_argument("--pdb", help="Launch debugger on an error", action="store_true")
|
2023-08-25 07:22:47 +00:00
|
|
|
return parser
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
args = parser().parse_args()
|
2024-08-21 03:26:00 +00:00
|
|
|
|
2024-09-04 07:04:21 +00:00
|
|
|
if args.debug:
|
|
|
|
os.environ["NWB_LINKML_DEBUG"] = "true"
|
|
|
|
else:
|
|
|
|
if "NWB_LINKML_DEBUG" in os.environ:
|
|
|
|
del os.environ["NWB_LINKML_DEBUG"]
|
|
|
|
|
2024-08-21 03:26:00 +00:00
|
|
|
tmp_dir = make_tmp_dir(clear=True)
|
|
|
|
git_dir = tmp_dir / "git"
|
|
|
|
git_dir.mkdir(exist_ok=True)
|
|
|
|
|
2024-07-31 08:13:31 +00:00
|
|
|
if args.hdmf:
|
2024-08-21 03:26:00 +00:00
|
|
|
repo = GitRepo(HDMF_COMMON_REPO, path=git_dir)
|
2024-07-31 08:13:31 +00:00
|
|
|
else:
|
2024-08-21 03:26:00 +00:00
|
|
|
repo = GitRepo(NWB_CORE_REPO, path=git_dir)
|
2024-07-31 08:13:31 +00:00
|
|
|
|
2023-09-14 09:45:01 +00:00
|
|
|
if not args.dry_run:
|
|
|
|
args.yaml.mkdir(exist_ok=True)
|
|
|
|
args.pydantic.mkdir(exist_ok=True)
|
2024-09-26 08:02:16 +00:00
|
|
|
|
|
|
|
generate_versions(
|
|
|
|
args.yaml, args.pydantic, args.dry_run, repo, pdb=args.pdb, latest=args.latest
|
|
|
|
)
|
2023-08-25 07:22:47 +00:00
|
|
|
|
2024-08-05 23:05:44 +00:00
|
|
|
|
2023-08-25 07:22:47 +00:00
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|