1
+ import os
2
+ import pathlib
3
+ import py_compile
4
+ import shutil
1
5
import textwrap
2
6
import unittest
3
7
import warnings
7
11
from importlib import resources
8
12
from importlib .resources .abc import Traversable
9
13
from . import util
14
+ from test .support import os_helper , import_helper
10
15
11
16
12
17
@contextlib .contextmanager
@@ -55,6 +60,26 @@ class OpenZipTests(FilesTests, util.ZipSetup, unittest.TestCase):
55
60
class OpenNamespaceTests (FilesTests , util .DiskSetup , unittest .TestCase ):
56
61
MODULE = 'namespacedata01'
57
62
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
+
58
83
59
84
class OpenNamespaceZipTests (FilesTests , util .ZipSetup , unittest .TestCase ):
60
85
ZIP_MODULE = 'namespacedata01'
@@ -81,7 +106,7 @@ def test_module_resources(self):
81
106
"""
82
107
A module can have resources found adjacent to the module.
83
108
"""
84
- import mod
109
+ import mod # type: ignore[import-not-found]
85
110
86
111
actual = resources .files (mod ).joinpath ('res.txt' ).read_text (encoding = 'utf-8' )
87
112
assert actual == self .spec ['res.txt' ]
@@ -97,8 +122,8 @@ class ModuleFilesZipTests(DirectSpec, util.ZipSetup, ModulesFiles, unittest.Test
97
122
98
123
class ImplicitContextFiles :
99
124
set_val = textwrap .dedent (
100
- """
101
- import importlib. resources as res
125
+ f """
126
+ import { resources . __name__ } as res
102
127
val = res.files().joinpath('res.txt').read_text(encoding='utf-8')
103
128
"""
104
129
)
@@ -108,6 +133,10 @@ class ImplicitContextFiles:
108
133
'submod.py' : set_val ,
109
134
'res.txt' : 'resources are the best' ,
110
135
},
136
+ 'frozenpkg' : {
137
+ '__init__.py' : set_val .replace (resources .__name__ , 'c_resources' ),
138
+ 'res.txt' : 'resources are the best' ,
139
+ },
111
140
}
112
141
113
142
def test_implicit_files_package (self ):
@@ -122,6 +151,32 @@ def test_implicit_files_submodule(self):
122
151
"""
123
152
assert importlib .import_module ('somepkg.submod' ).val == 'resources are the best'
124
153
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
+
125
180
126
181
class ImplicitContextFilesDiskTests (
127
182
DirectSpec , util .DiskSetup , ImplicitContextFiles , unittest .TestCase
0 commit comments