1+ import os
2+ import pathlib
3+ import py_compile
4+ import shutil
15import textwrap
26import unittest
37import warnings
711from importlib import resources
812from importlib .resources .abc import Traversable
913from . import util
14+ from test .support import os_helper , import_helper
1015
1116
1217@contextlib .contextmanager
@@ -55,6 +60,26 @@ class OpenZipTests(FilesTests, util.ZipSetup, unittest.TestCase):
5560class OpenNamespaceTests (FilesTests , util .DiskSetup , unittest .TestCase ):
5661 MODULE = 'namespacedata01'
5762
63+ def test_non_paths_in_dunder_path (self ):
64+ """
65+ Non-path items in a namespace package's ``__path__`` are ignored.
66+
67+ As reported in python/importlib_resources#311, some tools
68+ like Setuptools, when creating editable packages, will inject
69+ non-paths into a namespace package's ``__path__``, a
70+ sentinel like
71+ ``__editable__.sample_namespace-1.0.finder.__path_hook__``
72+ to cause the ``PathEntryFinder`` to be called when searching
73+ for packages. In that case, resources should still be loadable.
74+ """
75+ import namespacedata01
76+
77+ namespacedata01 .__path__ .append (
78+ '__editable__.sample_namespace-1.0.finder.__path_hook__'
79+ )
80+
81+ resources .files (namespacedata01 )
82+
5883
5984class OpenNamespaceZipTests (FilesTests , util .ZipSetup , unittest .TestCase ):
6085 ZIP_MODULE = 'namespacedata01'
@@ -81,7 +106,7 @@ def test_module_resources(self):
81106 """
82107 A module can have resources found adjacent to the module.
83108 """
84- import mod
109+ import mod # type: ignore[import-not-found]
85110
86111 actual = resources .files (mod ).joinpath ('res.txt' ).read_text (encoding = 'utf-8' )
87112 assert actual == self .spec ['res.txt' ]
@@ -97,8 +122,8 @@ class ModuleFilesZipTests(DirectSpec, util.ZipSetup, ModulesFiles, unittest.Test
97122
98123class ImplicitContextFiles :
99124 set_val = textwrap .dedent (
100- """
101- import importlib. resources as res
125+ f """
126+ import { resources . __name__ } as res
102127 val = res.files().joinpath('res.txt').read_text(encoding='utf-8')
103128 """
104129 )
@@ -108,6 +133,10 @@ class ImplicitContextFiles:
108133 'submod.py' : set_val ,
109134 'res.txt' : 'resources are the best' ,
110135 },
136+ 'frozenpkg' : {
137+ '__init__.py' : set_val .replace (resources .__name__ , 'c_resources' ),
138+ 'res.txt' : 'resources are the best' ,
139+ },
111140 }
112141
113142 def test_implicit_files_package (self ):
@@ -122,6 +151,32 @@ def test_implicit_files_submodule(self):
122151 """
123152 assert importlib .import_module ('somepkg.submod' ).val == 'resources are the best'
124153
154+ def _compile_importlib (self ):
155+ """
156+ Make a compiled-only copy of the importlib resources package.
157+ """
158+ bin_site = self .fixtures .enter_context (os_helper .temp_dir ())
159+ c_resources = pathlib .Path (bin_site , 'c_resources' )
160+ sources = pathlib .Path (resources .__file__ ).parent
161+ shutil .copytree (sources , c_resources , ignore = lambda * _ : ['__pycache__' ])
162+
163+ for dirpath , _ , filenames in os .walk (c_resources ):
164+ for filename in filenames :
165+ source_path = pathlib .Path (dirpath ) / filename
166+ cfile = source_path .with_suffix ('.pyc' )
167+ py_compile .compile (source_path , cfile )
168+ pathlib .Path .unlink (source_path )
169+ self .fixtures .enter_context (import_helper .DirsOnSysPath (bin_site ))
170+
171+ def test_implicit_files_with_compiled_importlib (self ):
172+ """
173+ Caller detection works for compiled-only resources module.
174+
175+ python/cpython#123085
176+ """
177+ self ._compile_importlib ()
178+ assert importlib .import_module ('frozenpkg' ).val == 'resources are the best'
179+
125180
126181class ImplicitContextFilesDiskTests (
127182 DirectSpec , util .DiskSetup , ImplicitContextFiles , unittest .TestCase
0 commit comments