8
8
import distutils .errors
9
9
import itertools
10
10
import stat
11
+ import warnings
12
+ from pathlib import Path
13
+ from setuptools ._deprecation_warning import SetuptoolsDeprecationWarning
11
14
from setuptools .extern .more_itertools import unique_everseen
12
15
13
16
@@ -129,6 +132,7 @@ def analyze_manifest(self):
129
132
src_dirs [assert_relative (self .get_package_dir (package ))] = package
130
133
131
134
self .run_command ('egg_info' )
135
+ checker = _IncludePackageDataAbuse ()
132
136
ei_cmd = self .get_finalized_command ('egg_info' )
133
137
for path in ei_cmd .filelist .files :
134
138
d , f = os .path .split (assert_relative (path ))
@@ -139,8 +143,13 @@ def analyze_manifest(self):
139
143
d , df = os .path .split (d )
140
144
f = os .path .join (df , f )
141
145
if d in src_dirs :
142
- if path .endswith ('.py' ) and f == oldf :
143
- continue # it's a module, not data
146
+ if f == oldf :
147
+ if checker .is_module (f ):
148
+ continue # it's a module, not data
149
+ else :
150
+ importable = checker .importable_item (src_dirs [d ], f )
151
+ if importable :
152
+ checker .warn (importable )
144
153
mf .setdefault (src_dirs [d ], []).append (path )
145
154
146
155
def get_data_files (self ):
@@ -240,3 +249,46 @@ def assert_relative(path):
240
249
% path
241
250
)
242
251
raise DistutilsSetupError (msg )
252
+
253
+
254
+ class _IncludePackageDataAbuse :
255
+ """Inform users that package or module is included as 'data file'"""
256
+
257
+ MESSAGE = """\
258
+ !!\n \n
259
+ ###################################
260
+ # Package/module would be ignored #
261
+ ###################################
262
+ Python recognizes {importable!r} as an importable package or module, however
263
+ it is included in the distribution as "data".
264
+ This behavior is likely to change in future versions of setuptools (and
265
+ therefore is considered deprecated).
266
+
267
+ Please make sure that {importable!r} is recognized as a package/module by using
268
+ setuptools' `packages` configuration field or the proper package discovery methods.
269
+
270
+ To find more information, look for "package discovery" and "data files" on
271
+ setuptools documentation page.
272
+ \n \n !!
273
+ """
274
+
275
+ def __init__ (self ):
276
+ self ._already_warned = set ()
277
+
278
+ def is_module (self , file ):
279
+ return file .endswith (".py" ) and file [:- len (".py" )].isidentifier ()
280
+
281
+ def importable_item (self , pkg , file ):
282
+ path = Path (file )
283
+ parents = path .parent .parts
284
+ module = [path .stem ] if tuple (path .suffixes ) == (".py" ,) else []
285
+ parts = list (itertools .takewhile (str .isidentifier , [* parents , * module ]))
286
+ if parts :
287
+ return "." .join ([pkg , * parts ])
288
+ return None
289
+
290
+ def warn (self , importable ):
291
+ if importable not in self ._already_warned :
292
+ msg = textwrap .dedent (self .MESSAGE ).format (importable = importable )
293
+ warnings .warn (msg , SetuptoolsDeprecationWarning , stacklevel = 2 )
294
+ self ._already_warned .add (importable )
0 commit comments