@@ -522,32 +522,28 @@ def import_path(
522
522
raise ImportError (path )
523
523
524
524
if mode is ImportMode .importlib :
525
- pkg_path = resolve_package_path (path )
526
- if pkg_path is not None :
527
- pkg_root = pkg_path .parent
528
- names = list (path .with_suffix ("" ).relative_to (pkg_root ).parts )
529
- if names [- 1 ] == "__init__" :
530
- names .pop ()
531
- module_name = "." .join (names )
532
- else :
533
- pkg_root = path .parent
534
- module_name = path .stem
535
-
536
- print ("IMPORTMODE" )
537
- print (f" { path = } " )
525
+ # Obtain the canonical name of this path if we can resolve it to a python package,
526
+ # and try to import it without changing sys.path.
527
+ # If it works, we import it and return the module.
528
+ _ , module_name = resolve_pkg_root_and_module_name (path )
538
529
try :
539
530
importlib .import_module (module_name )
540
- except (ImportError , ImportWarning ) as e :
541
- print (f" FAILED: { e } " )
531
+ except (ImportError , ImportWarning ):
532
+ # Cannot be imported normally with the current sys.path, so fallback
533
+ # to the more complex import-path mechanism.
534
+ pass
542
535
else :
543
- print (f" WORKED: { module_name } " )
536
+ # Double check that the imported module is the one we
537
+ # were given, otherwise it is easy to import modules with common names like "test"
538
+ # from another location.
544
539
mod = sys .modules [module_name ]
545
540
if mod .__file__ and Path (mod .__file__ ) == path :
546
541
return mod
547
542
543
+ # Could not import the module with the current sys.path, so we fall back
544
+ # to importing the file as a standalone module, not being a part of a package.
548
545
module_name = module_name_from_path (path , root )
549
546
with contextlib .suppress (KeyError ):
550
- print (f" CACHED { module_name = } " )
551
547
return sys .modules [module_name ]
552
548
553
549
for meta_importer in sys .meta_path :
@@ -563,19 +559,9 @@ def import_path(
563
559
sys .modules [module_name ] = mod
564
560
spec .loader .exec_module (mod ) # type: ignore[union-attr]
565
561
insert_missing_modules (sys .modules , module_name )
566
- print (f" IMPORTED_AS { module_name = } " )
567
562
return mod
568
563
569
- pkg_path = resolve_package_path (path )
570
- if pkg_path is not None :
571
- pkg_root = pkg_path .parent
572
- names = list (path .with_suffix ("" ).relative_to (pkg_root ).parts )
573
- if names [- 1 ] == "__init__" :
574
- names .pop ()
575
- module_name = "." .join (names )
576
- else :
577
- pkg_root = path .parent
578
- module_name = path .stem
564
+ pkg_root , module_name = resolve_pkg_root_and_module_name (path )
579
565
580
566
# Change sys.path permanently: restoring it at the end of this function would cause surprising
581
567
# problems because of delayed imports: for example, a conftest.py file imported by this function
@@ -717,6 +703,36 @@ def resolve_package_path(path: Path) -> Optional[Path]:
717
703
return result
718
704
719
705
706
+ def resolve_pkg_root_and_module_name (path : Path ) -> Tuple [Path , str ]:
707
+ """
708
+ Return the path to the directory of the root package that contains the
709
+ given Python file, and its module name:
710
+
711
+ src/
712
+ app/
713
+ __init__.py
714
+ core/
715
+ __init__.py
716
+ models.py
717
+
718
+ Passing the full path to `models.py` will yield Path("src") and "app.core.models".
719
+
720
+ If the given path does not belong to a package (missing __init__.py) files, returns
721
+ just the parent path and the name of the file as module name.
722
+ """
723
+ pkg_path = resolve_package_path (path )
724
+ if pkg_path is not None :
725
+ pkg_root = pkg_path .parent
726
+ names = list (path .with_suffix ("" ).relative_to (pkg_root ).parts )
727
+ if names [- 1 ] == "__init__" :
728
+ names .pop ()
729
+ module_name = "." .join (names )
730
+ else :
731
+ pkg_root = path .parent
732
+ module_name = path .stem
733
+ return pkg_root , module_name
734
+
735
+
720
736
def scandir (
721
737
path : Union [str , "os.PathLike[str]" ],
722
738
sort_key : Callable [["os.DirEntry[str]" ], object ] = lambda entry : entry .name ,
0 commit comments