1- import os
1+ from pathlib import Path
22from enum import Enum , auto
3- from typing import Optional , Union
3+ from typing import Dict , Optional , Union , Tuple , NamedTuple
4+ from urllib .parse import urljoin
5+ from dataclasses import dataclass
46
57import requests
68from rdflib import Namespace
911LINKML_NAMESPACE = Namespace (LINKML_URL_BASE )
1012GITHUB_IO_BASE = "https://linkml.github.io/linkml-model/"
1113GITHUB_BASE = "https://raw.githubusercontent.com/linkml/linkml-model/"
12- LOCAL_BASE = os . path . abspath ( os . path . dirname ( __file__ ) )
14+ LOCAL_BASE = Path ( __file__ ). parent . resolve ( )
1315GITHUB_API_BASE = "https://api.github.com/repos/linkml/linkml-model/"
1416GITHUB_RELEASES = GITHUB_BASE + "releases"
1517GITHUB_TAGS = GITHUB_BASE + "tags"
1618
1719
18-
19-
2020class _AutoName (Enum ):
2121 @staticmethod
2222 def _generate_next_value_ (name , start , count , last_values ):
@@ -32,42 +32,84 @@ class Source(_AutoName):
3232 EXTENSIONS = auto ()
3333
3434
35- class Format (Enum ):
35+ class Format (_AutoName ):
3636 """ LinkML package formats """
37- GRAPHQL = "graphql"
38- HTML = ""
39- JSON = "json"
40- JSONLD = "context.jsonld"
41- JSON_SCHEMA = "schema.json"
42- NATIVE_JSONLD = "model.context.jsonld"
43- NATIVE_RDF = "model.ttl"
44- NATIVE_SHEXC = "model.shex"
45- NATIVE_SHEXJ = "model.shexj"
46- OWL = "owl.ttl"
47- PYTHON = "py"
48- RDF = "ttl"
49- SHEXC = "shex"
50- SHEXJ = "shexj"
51- YAML = "yaml"
52-
53-
54- class _Path (Enum ):
37+ EXCEL = auto ()
38+ GRAPHQL = auto ()
39+ JSON = auto ()
40+ JSONLD = auto ()
41+ JSON_SCHEMA = auto ()
42+ NATIVE_JSONLD = auto ()
43+ NATIVE_RDF = auto ()
44+ NATIVE_SHEXC = auto ()
45+ NATIVE_SHEXJ = auto ()
46+ OWL = auto ()
47+ PREFIXMAP = auto ()
48+ PROTOBUF = auto ()
49+ PYTHON = auto ()
50+ RDF = auto ()
51+ SHACL = auto ()
52+ SHEXC = auto ()
53+ SHEXJ = auto ()
54+ SQLDDL = auto ()
55+ SQLSCHEMA = auto ()
56+ YAML = auto ()
57+
58+ @dataclass
59+ class FormatPath :
60+ path : str
61+ extension : str
62+
63+ def model_path (self , model :str ) -> Path :
64+ return (Path (self .path ) / model ).with_suffix (self .extension )
65+
66+ class _Path :
5567 """ LinkML Relative paths"""
56- GRAPHQL = "graphql"
57- HTML = "docs"
58- JSON = "json"
59- JSONLD = "jsonld"
60- JSON_SCHEMA = "jsonschema"
61- NATIVE_JSONLD = "jsonld"
62- NATIVE_RDF = "ttl"
63- NATIVE_SHEXC = "shex"
64- NATIVE_SHEXJ = "shex"
65- OWL = "owl"
66- PYTHON = "linkml_model"
67- RDF = "rdf"
68- SHEXC = "shex"
69- SHEXJ = "shex"
70- YAML = "model/schema"
68+ EXCEL = FormatPath ("excel" ,"xlsx" )
69+ GRAPHQL = FormatPath ("graphql" ,"graphql" )
70+ JSON = FormatPath ("json" ,"json" )
71+ JSONLD = FormatPath ("jsonld" ,"context.jsonld" )
72+ JSON_SCHEMA = FormatPath ("jsonschema" , "schema.json" )
73+ NATIVE_JSONLD = FormatPath ("jsonld" , "context.jsonld" )
74+ NATIVE_RDF = FormatPath ("rdf" ,"ttl" )
75+ NATIVE_SHEXC = FormatPath ("shex" ,"shex" )
76+ NATIVE_SHEXJ = FormatPath ("shex" ,"shexj" )
77+ OWL = FormatPath ("owl" ,"owl.ttl" )
78+ PREFIXMAP = FormatPath ('prefixmap' ,'yaml' )
79+ PROTOBUF = FormatPath ("protobuf" ,"proto" )
80+ PYTHON = FormatPath ("" ,"py" )
81+ RDF = FormatPath ("rdf" ,"ttl" )
82+ SHACL = FormatPath ("shacl" ,"shacl.ttl" )
83+ SHEXC = FormatPath ("shex" ,"shex" )
84+ SHEXJ = FormatPath ("shex" ,"shexj" )
85+ SQLDDL = FormatPath ("sqlddl" ,"sql" )
86+ SQLSCHEMA = FormatPath ("sqlschema" ,"sql" )
87+ YAML = FormatPath (str (Path ("model" ) / "schema" ),"yaml" )
88+
89+ @classmethod
90+ def items (cls ) -> Dict [str , FormatPath ]:
91+ return {k :v for k ,v in cls .__dict__ .items () if not k .startswith ('_' )}
92+
93+ @classmethod
94+ def get (cls , item :Union [str ,Format ]) -> FormatPath :
95+ if isinstance (item , Format ):
96+ item = item .name .upper ()
97+ return getattr (cls , item )
98+
99+ def __class_getitem__ (cls , item :str ) -> FormatPath :
100+ return getattr (cls , item )
101+
102+
103+ META_ONLY = (
104+ Format .EXCEL ,
105+ Format .GRAPHQL ,
106+ Format .OWL ,
107+ Format .PREFIXMAP ,
108+ Format .PROTOBUF ,
109+ Format .SHACL ,
110+ Format .SQLDDL ,
111+ Format .SQLSCHEMA
112+ )
71113
72114
73115class ReleaseTag (_AutoName ):
@@ -78,26 +120,40 @@ class ReleaseTag(_AutoName):
78120 CURRENT = auto ()
79121
80122
81- def _build_path (source : Source , fmt : Format ) -> str :
82- """ Create the relative path for source and fmt """
83- return f"{ _Path [fmt .name ].value } /{ source .value } .{ fmt .value } "
123+ class PathParts (NamedTuple ):
124+ format : str
125+ file : str
126+
127+
128+ def _build_path (source : Source , fmt : Format ) -> PathParts :
129+ """
130+ Create the parts for a relative path for source and fmt.
131+ Combined elsewhere into a complete path, since OS paths and URLs differ.
132+ """
133+ fmt_path : FormatPath = _Path .get (fmt .name )
134+ return PathParts (fmt_path .path , f"{ source .value } .{ fmt_path .extension } " )
84135
85136
86137def _build_loc (base : str , source : Source , fmt : Format ) -> str :
87- return f"{ base } { _build_path (source , fmt )} " .replace ('blob/' , '' )
138+ """A github location"""
139+ # urls are always forward slash separated, so hardcoding is appropriate here
140+ path = '/' .join (_build_path (source , fmt ))
141+ return urljoin (base , path ).replace ('blob/' , '' )
88142
89143
90144def URL_FOR (source : Source , fmt : Format ) -> str :
91145 """ Return the URL to retrieve source in format """
92- return f"{ LINKML_URL_BASE } { source .value } .{ fmt .value } "
146+ fmt_path : FormatPath = _Path .get (fmt .name )
147+ return f"{ LINKML_URL_BASE } { source .value } .{ fmt_path .extension } "
93148
94149
95150def LOCAL_PATH_FOR (source : Source , fmt : Format ) -> str :
96- return os . path . join (LOCAL_BASE , _build_path (source , fmt ))
151+ return str (LOCAL_BASE . joinpath ( * _build_path (source , fmt ) ))
97152
98153
99- def GITHUB_IO_PATH_FOR (source : Source , fmt : Format ) -> str :
100- return _build_loc (GITHUB_IO_BASE , source , fmt )
154+ def GITHUB_IO_PATH_FOR (source : Source , fmt : Format , version = "latest" ) -> str :
155+ path = '/' .join ([version , 'linkml_model' , * _build_path (source , fmt )])
156+ return urljoin (GITHUB_IO_BASE , path )
101157
102158
103159def GITHUB_PATH_FOR (source : Source ,
@@ -122,7 +178,8 @@ def tag_to_commit(tag: str) -> str:
122178
123179 # Return the absolute latest entry for branch
124180 if release is ReleaseTag .LATEST or (release is ReleaseTag .CURRENT and branch != "main" ):
125- return f"{ GITHUB_BASE } { branch } /{ _build_path (source , fmt )} "
181+ path = '/' .join ([branch , 'linkml_model' , * _build_path (source , fmt )])
182+ return urljoin (GITHUB_BASE , path )
126183
127184 # Return the latest published version
128185 elif release is ReleaseTag .CURRENT :
@@ -139,9 +196,10 @@ class ModelLoc:
139196 def __init__ (self , model : Source , fmt : Format ) -> str :
140197 self ._model = model
141198 self ._format = fmt
199+ self ._fmt_path = _Path .get (fmt .name )
142200
143201 def __str__ (self ):
144- return f"{ self ._model .value } .{ self ._format . value } "
202+ return f"{ self ._model .value } .{ self ._fmt_path . extension } "
145203
146204 def __repr__ (self ):
147205 return str (self )
@@ -171,18 +229,10 @@ def __str__(self):
171229 def __repr__ (self ):
172230 return str (self )
173231
174- @property
175- def yaml (self ) -> ModelLoc :
176- return ModelFile .ModelLoc (self ._model , Format .YAML )
177-
178232 @property
179233 def graphql (self ) -> ModelLoc :
180234 return ModelFile .ModelLoc (self ._model , Format .GRAPHQL )
181235
182- @property
183- def html (self ) -> ModelLoc :
184- return ModelFile .ModelLoc (self ._model , Format .HTML )
185-
186236 @property
187237 def json (self ) -> ModelLoc :
188238 return ModelFile .ModelLoc (self ._model , Format .JSON )
0 commit comments