浏览代码

fix: sibling import issue in `path_deploy()`

wangweimin 3 年之前
父节点
当前提交
35209a7e79
共有 1 个文件被更改,包括 21 次插入8 次删除
  1. 21 8
      pywebio/platform/path_deploy.py

+ 21 - 8
pywebio/platform/path_deploy.py

@@ -1,5 +1,6 @@
 import os.path
 from functools import partial
+from contextlib import contextmanager
 
 import tornado
 from tornado import template
@@ -20,10 +21,11 @@ def filename_ok(f):
 
 
 def valid_and_norm_path(base, subpath):
-    """
+    """Join the sub-path to base path. This function always ensure the result path is a subpath of base path.
+
     :param str base: MUST a absolute path
-    :param str subpath:
-    :return: Normalize path. None returned if the sub path is not valid
+    :param str subpath: sub-path under the `base` path
+    :return: normalized result path. None returned if the sub path is not valid
     """
     subpath = subpath.lstrip('/')
     full_path = os.path.normpath(os.path.join(base, subpath))
@@ -42,19 +44,30 @@ _cached_modules = {}
 
 
 def _get_module(path, reload=False):
-    # Credit: https://stackoverflow.com/questions/67631/how-to-import-a-module-given-the-full-path
-
+    # https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly
+    # https://stackoverflow.com/questions/41861427/python-3-5-how-to-dynamically-import-a-module-given-the-full-file-path-in-the
     global _cached_modules
     import importlib.util
 
+    @contextmanager
+    def add_to_path(p):
+        import sys
+        sys.path.append(p)
+        try:
+            yield
+        finally:
+            sys.path.remove(p)
+
     if not reload and path in _cached_modules:
         return _cached_modules[path]
 
     # import_name will be the `__name__` of the imported module
     import_name = "__pywebio__"
-    spec = importlib.util.spec_from_file_location(import_name, path)
-    module = importlib.util.module_from_spec(spec)
-    spec.loader.exec_module(module)
+
+    with add_to_path(os.path.dirname(path)):
+        spec = importlib.util.spec_from_file_location(import_name, path, submodule_search_locations=None)
+        module = importlib.util.module_from_spec(spec)
+        spec.loader.exec_module(module)
     _cached_modules[path] = module
     return module