Coverage for src/importnb/finder.py: 98%
60 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-01-08 11:14 -0800
« prev ^ index » next coverage.py v6.5.0, created at 2023-01-08 11:14 -0800
1# coding: utf-8
2"""# `sys.path_hook` modifiers
4Many suggestions for importing notebooks use `sys.meta_paths`, but `importnb` relies on the `sys.path_hooks` to load any notebook in the path. `PathHooksContext` is a base class for the `importnb.Notebook` `SourceFileLoader`.
5"""
7import inspect
8import sys
9from importlib._bootstrap_external import FileFinder
10from importlib.machinery import ModuleSpec
11from pathlib import Path
13class SpecSort:
14 modified = "st_mtime"
15 created = "st_ctime"
17class FileModuleSpec(ModuleSpec):
18 def __init__(self, *args, **kwargs):
19 super().__init__(*args, **kwargs)
20 self._set_fileattr = True
23class FuzzySpec(FileModuleSpec):
24 def __init__(
25 self, name, loader, *, alias=None, origin=None, loader_state=None, is_package=None
26 ):
27 super().__init__(
28 name,
29 loader,
30 origin=origin,
31 loader_state=loader_state,
32 is_package=is_package,
33 )
34 self.alias = alias
37def fuzzy_query(str):
38 new = ""
39 for chr in str:
40 new += (not new.endswith("__") or chr != "_") and chr or ""
41 return new.replace("__", "*").replace("_", "?")
44def fuzzy_file_search(path, fullname):
45 results = []
46 id, details = get_loader_details()
47 for ext in sum((list(object[1]) for object in details), []):
48 results.extend(Path(path).glob(fullname + ext))
49 "_" in fullname and results.extend(Path(path).glob(fuzzy_query(fullname) + ext))
50 return results
53class FuzzyFinder(FileFinder):
54 """Adds the ability to open file names with special characters using underscores."""
56 def find_spec(self, fullname, target=None):
57 """Try to finder the spec and if it cannot be found, use the underscore starring syntax
58 to identify potential matches.
59 """
60 spec = super().find_spec(fullname, target=target)
61 raw = fullname
62 if spec is None:
63 original = fullname
65 if "." in fullname:
66 original, fullname = fullname.rsplit(".", 1)
67 else:
68 original, fullname = "", original
70 if "_" in fullname:
71 # find any files using the fuzzy convention
72 files = fuzzy_file_search(self.path, fullname)
73 if files:
74 # sort and create of a path of the chosen file
75 file = sorted(files, key=lambda x: x.stat().st_mtime, reverse=True)[0]
76 name = file.stem
77 if original:
78 name = ".".join((original, name))
79 name = (original + "." + file.stem).lstrip(".")
80 spec = super().find_spec(name, target=target)
81 spec = spec and FuzzySpec(
82 spec.name,
83 spec.loader,
84 origin=spec.origin,
85 loader_state=spec.loader_state,
86 alias=raw,
87 is_package=bool(spec.submodule_search_locations),
88 )
89 return spec
92def get_loader_details():
93 for id, path_hook in enumerate(sys.path_hooks):
94 try:
95 return (
96 id,
97 list(inspect.getclosurevars(path_hook).nonlocals["loader_details"]),
98 )
99 except:
100 continue
103def get_loader_index(ext):
104 path_id, details = get_loader_details()
105 for i, (loader, exts) in enumerate(details):
106 if ext in exts:
107 return path_id, i, details