Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorproate linkml-runtime, linkml-model/linkml-files.py changes from linkml-runtime #201

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.10"]
python-version: ["3.9", "3.10", "3.11"]


steps:

Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ test-schema:
$(RUN) gen-project -d tmp $(SOURCE_SCHEMA_PATH)

test-python:
$(RUN) python -m unittest discover
$(RUN) python -m pytest

# TODO: switch to linkml-run-examples when normalize is implemented
test-examples: $(SOURCE_SCHEMA_PATH)
Expand Down
164 changes: 107 additions & 57 deletions linkml_model/linkml_files.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import os
from pathlib import Path
from enum import Enum, auto
from typing import Optional, Union
from typing import Dict, Optional, Union, NamedTuple
from urllib.parse import urljoin
from dataclasses import dataclass

import requests
from rdflib import Namespace
Expand All @@ -9,14 +11,12 @@
LINKML_NAMESPACE = Namespace(LINKML_URL_BASE)
GITHUB_IO_BASE = "https://linkml.github.io/linkml-model/"
GITHUB_BASE = "https://raw.githubusercontent.com/linkml/linkml-model/"
LOCAL_BASE = os.path.abspath(os.path.dirname(__file__))
LOCAL_BASE = Path(__file__).parent.resolve()
GITHUB_API_BASE = "https://api.github.com/repos/linkml/linkml-model/"
GITHUB_RELEASES = GITHUB_BASE + "releases"
GITHUB_TAGS = GITHUB_BASE + "tags"




class _AutoName(Enum):
@staticmethod
def _generate_next_value_(name, start, count, last_values):
Expand All @@ -32,42 +32,84 @@ class Source(_AutoName):
EXTENSIONS = auto()


class Format(Enum):
class Format(_AutoName):
""" LinkML package formats """
GRAPHQL = "graphql"
HTML = ""
JSON = "json"
JSONLD = "context.jsonld"
JSON_SCHEMA = "schema.json"
NATIVE_JSONLD = "model.context.jsonld"
NATIVE_RDF = "model.ttl"
NATIVE_SHEXC = "model.shex"
NATIVE_SHEXJ = "model.shexj"
OWL = "owl.ttl"
PYTHON = "py"
RDF = "ttl"
SHEXC = "shex"
SHEXJ = "shexj"
YAML = "yaml"


class _Path(Enum):
EXCEL = auto()
GRAPHQL = auto()
JSON = auto()
JSONLD = auto()
JSON_SCHEMA = auto()
NATIVE_JSONLD = auto()
NATIVE_RDF = auto()
NATIVE_SHEXC = auto()
NATIVE_SHEXJ = auto()
OWL = auto()
PREFIXMAP = auto()
PROTOBUF = auto()
PYTHON = auto()
RDF = auto()
SHACL = auto()
SHEXC = auto()
SHEXJ = auto()
SQLDDL = auto()
SQLSCHEMA = auto()
YAML = auto()

@dataclass
class FormatPath:
path: str
extension: str

def model_path(self, model:str) -> Path:
return (Path(self.path) / model).with_suffix(self.extension)

class _Path:
""" LinkML Relative paths"""
GRAPHQL = "graphql"
HTML = "docs"
JSON = "json"
JSONLD = "jsonld"
JSON_SCHEMA = "jsonschema"
NATIVE_JSONLD = "jsonld"
NATIVE_RDF = "ttl"
NATIVE_SHEXC = "shex"
NATIVE_SHEXJ = "shex"
OWL = "owl"
PYTHON = "linkml_model"
RDF = "rdf"
SHEXC = "shex"
SHEXJ = "shex"
YAML = "model/schema"
EXCEL = FormatPath("excel","xlsx" )
GRAPHQL = FormatPath("graphql","graphql" )
JSON = FormatPath("json","json" )
JSONLD = FormatPath("jsonld","context.jsonld" )
JSON_SCHEMA = FormatPath("jsonschema", "schema.json" )
NATIVE_JSONLD = FormatPath("jsonld", "context.jsonld" )
NATIVE_RDF = FormatPath("rdf","ttl" )
NATIVE_SHEXC = FormatPath("shex","shex" )
NATIVE_SHEXJ = FormatPath("shex","shexj" )
OWL = FormatPath("owl","owl.ttl" )
PREFIXMAP = FormatPath('prefixmap','yaml' )
PROTOBUF = FormatPath("protobuf","proto" )
PYTHON = FormatPath("","py" )
RDF = FormatPath("rdf","ttl" )
SHACL = FormatPath("shacl","shacl.ttl" )
SHEXC = FormatPath("shex","shex" )
SHEXJ = FormatPath("shex","shexj" )
SQLDDL = FormatPath("sqlddl","sql" )
SQLSCHEMA = FormatPath("sqlschema","sql" )
YAML = FormatPath(str(Path("model") / "schema"),"yaml" )

@classmethod
def items(cls) -> Dict[str, FormatPath]:
return {k:v for k,v in cls.__dict__.items() if not k.startswith('_')}

@classmethod
def get(cls, item:Union[str,Format]) -> FormatPath:
if isinstance(item, Format):
item = item.name.upper()
return getattr(cls, item)

def __class_getitem__(cls, item:str) -> FormatPath:
return getattr(cls, item)


META_ONLY = (
Format.EXCEL,
Format.GRAPHQL,
Format.OWL,
Format.PREFIXMAP,
Format.PROTOBUF,
Format.SHACL,
Format.SQLDDL,
Format.SQLSCHEMA
)


class ReleaseTag(_AutoName):
Expand All @@ -78,26 +120,40 @@ class ReleaseTag(_AutoName):
CURRENT = auto()


def _build_path(source: Source, fmt: Format) -> str:
""" Create the relative path for source and fmt """
return f"{_Path[fmt.name].value}/{source.value}.{fmt.value}"
class PathParts(NamedTuple):
format: str
file: str


def _build_path(source: Source, fmt: Format) -> PathParts:
"""
Create the parts for a relative path for source and fmt.
Combined elsewhere into a complete path, since OS paths and URLs differ.
"""
fmt_path: FormatPath = _Path.get(fmt.name)
return PathParts(fmt_path.path, f"{source.value}.{fmt_path.extension}")


def _build_loc(base: str, source: Source, fmt: Format) -> str:
return f"{base}{_build_path(source, fmt)}".replace('blob/', '')
"""A GitHub location"""
# urls are always forward slash separated, so hardcoding is appropriate here
path = '/'.join(_build_path(source, fmt))
return urljoin(base, path).replace('blob/', '')


def URL_FOR(source: Source, fmt: Format) -> str:
""" Return the URL to retrieve source in format """
return f"{LINKML_URL_BASE}{source.value}.{fmt.value}"
fmt_path: FormatPath = _Path.get(fmt.name)
return f"{LINKML_URL_BASE}{source.value}.{fmt_path.extension}"


def LOCAL_PATH_FOR(source: Source, fmt: Format) -> str:
return os.path.join(LOCAL_BASE, _build_path(source, fmt))
return str(LOCAL_BASE.joinpath(*_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="latest") -> str:
path = '/'.join([version, 'linkml_model', *_build_path(source, fmt)])
return urljoin(GITHUB_IO_BASE, path)


def GITHUB_PATH_FOR(source: Source,
Expand All @@ -122,7 +178,8 @@ 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)}"
path = '/'.join([branch, 'linkml_model', *_build_path(source, fmt)])
return urljoin(GITHUB_BASE, path)

# Return the latest published version
elif release is ReleaseTag.CURRENT:
Expand All @@ -139,9 +196,10 @@ class ModelLoc:
def __init__(self, model: Source, fmt: Format) -> str:
self._model = model
self._format = fmt
self._fmt_path = _Path.get(fmt.name)

def __str__(self):
return f"{self._model.value}.{self._format.value}"
return f"{self._model.value}.{self._fmt_path.extension}"

def __repr__(self):
return str(self)
Expand Down Expand Up @@ -171,18 +229,10 @@ def __str__(self):
def __repr__(self):
return str(self)

@property
def yaml(self) -> ModelLoc:
return ModelFile.ModelLoc(self._model, Format.YAML)

@property
def graphql(self) -> ModelLoc:
return ModelFile.ModelLoc(self._model, Format.GRAPHQL)

@property
def html(self) -> ModelLoc:
return ModelFile.ModelLoc(self._model, Format.HTML)

@property
def json(self) -> ModelLoc:
return ModelFile.ModelLoc(self._model, Format.JSON)
Expand Down
Loading
Loading