-
Notifications
You must be signed in to change notification settings - Fork 29
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
Fix multilayer relative imports #368
base: main
Are you sure you want to change the base?
Conversation
can you add a set of minimal schemas that demonstrates the bug to the tests? see:
https://github.com/linkml/linkml-runtime/tree/main/tests/test_utils/input/imports_relative |
related to: #350 |
@sneakers-the-rat test added that fails with original implementation, and fix that passes all tests. Noted that some other work has been done to allow relative imports without './' prefix in #350. Can try and reconcile with the above if necessary. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems fine, does need to be synchronized with the other changes that are being made to this exact place tho :)
# resolve relative imports relative to the importing schema, rather than the | ||
# origin schema. Imports can be a URI or Curie, and imports from the same | ||
# directory don't require a ./, so if the current (sn) import is a relative | ||
# path, and the target import doesn't have : (as in a curie or a URI) | ||
# we prepend the relative path. This WILL make the key in the `schema_map` not | ||
# equal to the literal text specified in the importing schema, but this is | ||
# essential to sensible deduplication: eg. for | ||
# - main.yaml (imports ./types.yaml, ./subdir/subschema.yaml) | ||
# - types.yaml | ||
# - subdir/subschema.yaml (imports ./types.yaml) | ||
# - subdir/types.yaml | ||
# we should treat the two `types.yaml` as separate schemas from the POV of the | ||
# origin schema. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why strip out the comment?
else: | ||
temp = i | ||
i = resolve_import(sn, i) | ||
_ = sn, temp, i |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is this doing?
else: | ||
temp = i | ||
i = resolve_import(sn, i) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mild suggest:
else: | |
temp = i | |
i = resolve_import(sn, i) | |
temp = i | |
i = resolve_import(sn, i) |
everything after
if (condition):
continue
is an else
, but avoids needing to go 6 levels of indentation if we can avoid it
@@ -104,6 +105,19 @@ def is_absolute_path(path: str) -> bool: | |||
drive, tail = os.path.splitdrive(norm_path) | |||
return bool(drive and tail) | |||
|
|||
def resolve_import(sn: str, i: str) -> str: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we're splitting this out to a separate function, it would be great to not have the arguments be named sn
and i
@@ -104,6 +105,19 @@ def is_absolute_path(path: str) -> bool: | |||
drive, tail = os.path.splitdrive(norm_path) | |||
return bool(drive and tail) | |||
|
|||
def resolve_import(sn: str, i: str) -> str: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def resolve_import(sn: str, i: str) -> str: | |
def _resolve_import(sn: str, i: str) -> str: |
unless we expect this to be used elsewhere, probably make it private. it seems pretty specific to this one case in schemaview
path = os.path.normpath(str(Path(sn).parent / i)) | ||
if i.startswith(".") and not path.startswith("."): | ||
# Above condition handles cases where both sn and i are relative paths, and path is not already relative | ||
return f"./{path}" | ||
else: | ||
return path | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since we are already treating everything that isn't absolute or has a scheme as a path, i think this would be double-fixing the problem. e.g. tests pass with this
path = os.path.normpath(str(Path(sn).parent / i)) | |
if i.startswith(".") and not path.startswith("."): | |
# Above condition handles cases where both sn and i are relative paths, and path is not already relative | |
return f"./{path}" | |
else: | |
return path | |
return os.path.normpath(str(Path(sn).parent / i)) | |
Originally mentioned in this issue on the linkml repo.
When you attempt to use relative imports from a file that, itself, has been imported with a relative import, path resolution fails. This bug occurs both in
linkml
andlinkml-runtime
. Inlinkml-runtime
, the problem occurs inschemaview.py
:If both
sn
andi
are relative file paths, the path added totodo
is an absolute path (at least on Mac). Wheni
later than becomessn
, it's not picked up by the above clause and anything it imports does not hit the above condition and therefore just defined relative to the base file.Marking as draft. @sneakers-the-rat noticed you were the last to edit this section, specifically here. Would appreciate input as to whether there's a reason for the above / it is expected behaviour and I'm missing something.