add monkeypatch to schemaview

This commit is contained in:
sneakers-the-rat 2023-09-11 22:23:42 -07:00
parent 6ff7e6baab
commit 054ca8fd5e
2 changed files with 63 additions and 6 deletions

View file

@ -38,6 +38,63 @@ def patch_npytyping():
dataframe.DataFrameMeta.__module__ = property(new_module_dataframe) dataframe.DataFrameMeta.__module__ = property(new_module_dataframe)
base_meta_classes.SubscriptableMeta._get_module = new_get_module base_meta_classes.SubscriptableMeta._get_module = new_get_module
def patch_schemaview():
"""
Patch schemaview to correctly resolve multiple layers of relative imports.
References:
Returns:
"""
from typing import List
from functools import lru_cache
from linkml_runtime.utils.schemaview import SchemaView
from linkml_runtime.linkml_model import SchemaDefinitionName
@lru_cache()
def imports_closure(self, imports: bool = True, traverse=True, inject_metadata=True) -> List[SchemaDefinitionName]:
"""
Return all imports
:param traverse: if true, traverse recursively
:return: all schema names in the transitive reflexive imports closure
"""
if not imports:
return [self.schema.name]
if self.schema_map is None:
self.schema_map = {self.schema.name: self.schema}
closure = []
visited = set()
todo = [self.schema.name]
if not traverse:
return todo
while len(todo) > 0:
sn = todo.pop()
visited.add(sn)
if sn not in self.schema_map:
imported_schema = self.load_import(sn)
self.schema_map[sn] = imported_schema
s = self.schema_map[sn]
if sn not in closure:
closure.append(sn)
for i in s.imports:
if sn.startswith('.') and ':' not in i:
# prepend the relative part
i = '/'.join(sn.split('/')[:-1]) + '/' + i
if i not in visited:
todo.append(i)
if inject_metadata:
for s in self.schema_map.values():
for x in {**s.classes, **s.enums, **s.slots, **s.subsets, **s.types}.values():
x.from_schema = s.id
for c in s.classes.values():
for a in c.attributes.values():
a.from_schema = s.id
return closure
SchemaView.imports_closure = imports_closure
def apply_patches(): def apply_patches():
patch_npytyping() patch_npytyping()
patch_schemaview()

View file

@ -7,10 +7,9 @@ an NWB file, we need a bit of infrastructure for generating and caching
pydantic models on the fly. pydantic models on the fly.
Relationship to other modules: Relationship to other modules:
- :mod:`.adapters` manage the conversion from NWB schema language to linkML. * :mod:`.adapters` manage the conversion from NWB schema language to linkML.
- :mod:`.generators` create models like pydantic models from the linkML schema * :mod:`.generators` create models like pydantic models from the linkML schema
- :mod:`.providers` then use ``adapters`` and ``generators`` to provide models * :mod:`.providers` then use ``adapters`` and ``generators`` to provide models from generated schema!
from generated schema!
""" """
import pdb import pdb
from typing import Dict, TypedDict, List, Optional, Literal, TypeVar, Any, Dict from typing import Dict, TypedDict, List, Optional, Literal, TypeVar, Any, Dict
@ -155,7 +154,7 @@ class LinkMLProvider(Provider):
Like other :class:`.Provider` classes, this model is not a singleton but Like other :class:`.Provider` classes, this model is not a singleton but
behaves a bit like one in that when instantiated without arguments behaves a bit like one in that when instantiated without arguments
it is stateless (except for configuration by environment-level variables). it is stateless (except for configuration by environment-level variables).
So we don't use ``@classmethod``s here, but instantiating the class should remain So we don't use ``@classmethod`` s here, but instantiating the class should remain
cheap. cheap.
Namespaces can be built from: Namespaces can be built from:
@ -164,6 +163,7 @@ class LinkMLProvider(Provider):
* dictionaries, as are usually packaged in nwb files: :meth:`.build_from_dicts` * dictionaries, as are usually packaged in nwb files: :meth:`.build_from_dicts`
All of which feed into... All of which feed into...
* :class:`~.adapters.NamespacesAdapter` used throughout the rest of ``nwb_linkml`` - :meth:`.build` * :class:`~.adapters.NamespacesAdapter` used throughout the rest of ``nwb_linkml`` - :meth:`.build`
After a namespace is built, it can be accessed using :meth:`.LinkMLProvider.get`, which After a namespace is built, it can be accessed using :meth:`.LinkMLProvider.get`, which