comparison env/lib/python3.9/site-packages/setuptools/_distutils/command/sdist.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4f3585e2f14b
1 """distutils.command.sdist
2
3 Implements the Distutils 'sdist' command (create a source distribution)."""
4
5 import os
6 import sys
7 from glob import glob
8 from warnings import warn
9
10 from distutils.core import Command
11 from distutils import dir_util
12 from distutils import file_util
13 from distutils import archive_util
14 from distutils.text_file import TextFile
15 from distutils.filelist import FileList
16 from distutils import log
17 from distutils.util import convert_path
18 from distutils.errors import DistutilsTemplateError, DistutilsOptionError
19
20
21 def show_formats():
22 """Print all possible values for the 'formats' option (used by
23 the "--help-formats" command-line option).
24 """
25 from distutils.fancy_getopt import FancyGetopt
26 from distutils.archive_util import ARCHIVE_FORMATS
27 formats = []
28 for format in ARCHIVE_FORMATS.keys():
29 formats.append(("formats=" + format, None,
30 ARCHIVE_FORMATS[format][2]))
31 formats.sort()
32 FancyGetopt(formats).print_help(
33 "List of available source distribution formats:")
34
35
36 class sdist(Command):
37
38 description = "create a source distribution (tarball, zip file, etc.)"
39
40 def checking_metadata(self):
41 """Callable used for the check sub-command.
42
43 Placed here so user_options can view it"""
44 return self.metadata_check
45
46 user_options = [
47 ('template=', 't',
48 "name of manifest template file [default: MANIFEST.in]"),
49 ('manifest=', 'm',
50 "name of manifest file [default: MANIFEST]"),
51 ('use-defaults', None,
52 "include the default file set in the manifest "
53 "[default; disable with --no-defaults]"),
54 ('no-defaults', None,
55 "don't include the default file set"),
56 ('prune', None,
57 "specifically exclude files/directories that should not be "
58 "distributed (build tree, RCS/CVS dirs, etc.) "
59 "[default; disable with --no-prune]"),
60 ('no-prune', None,
61 "don't automatically exclude anything"),
62 ('manifest-only', 'o',
63 "just regenerate the manifest and then stop "
64 "(implies --force-manifest)"),
65 ('force-manifest', 'f',
66 "forcibly regenerate the manifest and carry on as usual. "
67 "Deprecated: now the manifest is always regenerated."),
68 ('formats=', None,
69 "formats for source distribution (comma-separated list)"),
70 ('keep-temp', 'k',
71 "keep the distribution tree around after creating " +
72 "archive file(s)"),
73 ('dist-dir=', 'd',
74 "directory to put the source distribution archive(s) in "
75 "[default: dist]"),
76 ('metadata-check', None,
77 "Ensure that all required elements of meta-data "
78 "are supplied. Warn if any missing. [default]"),
79 ('owner=', 'u',
80 "Owner name used when creating a tar file [default: current user]"),
81 ('group=', 'g',
82 "Group name used when creating a tar file [default: current group]"),
83 ]
84
85 boolean_options = ['use-defaults', 'prune',
86 'manifest-only', 'force-manifest',
87 'keep-temp', 'metadata-check']
88
89 help_options = [
90 ('help-formats', None,
91 "list available distribution formats", show_formats),
92 ]
93
94 negative_opt = {'no-defaults': 'use-defaults',
95 'no-prune': 'prune' }
96
97 sub_commands = [('check', checking_metadata)]
98
99 READMES = ('README', 'README.txt', 'README.rst')
100
101 def initialize_options(self):
102 # 'template' and 'manifest' are, respectively, the names of
103 # the manifest template and manifest file.
104 self.template = None
105 self.manifest = None
106
107 # 'use_defaults': if true, we will include the default file set
108 # in the manifest
109 self.use_defaults = 1
110 self.prune = 1
111
112 self.manifest_only = 0
113 self.force_manifest = 0
114
115 self.formats = ['gztar']
116 self.keep_temp = 0
117 self.dist_dir = None
118
119 self.archive_files = None
120 self.metadata_check = 1
121 self.owner = None
122 self.group = None
123
124 def finalize_options(self):
125 if self.manifest is None:
126 self.manifest = "MANIFEST"
127 if self.template is None:
128 self.template = "MANIFEST.in"
129
130 self.ensure_string_list('formats')
131
132 bad_format = archive_util.check_archive_formats(self.formats)
133 if bad_format:
134 raise DistutilsOptionError(
135 "unknown archive format '%s'" % bad_format)
136
137 if self.dist_dir is None:
138 self.dist_dir = "dist"
139
140 def run(self):
141 # 'filelist' contains the list of files that will make up the
142 # manifest
143 self.filelist = FileList()
144
145 # Run sub commands
146 for cmd_name in self.get_sub_commands():
147 self.run_command(cmd_name)
148
149 # Do whatever it takes to get the list of files to process
150 # (process the manifest template, read an existing manifest,
151 # whatever). File list is accumulated in 'self.filelist'.
152 self.get_file_list()
153
154 # If user just wanted us to regenerate the manifest, stop now.
155 if self.manifest_only:
156 return
157
158 # Otherwise, go ahead and create the source distribution tarball,
159 # or zipfile, or whatever.
160 self.make_distribution()
161
162 def check_metadata(self):
163 """Deprecated API."""
164 warn("distutils.command.sdist.check_metadata is deprecated, \
165 use the check command instead", PendingDeprecationWarning)
166 check = self.distribution.get_command_obj('check')
167 check.ensure_finalized()
168 check.run()
169
170 def get_file_list(self):
171 """Figure out the list of files to include in the source
172 distribution, and put it in 'self.filelist'. This might involve
173 reading the manifest template (and writing the manifest), or just
174 reading the manifest, or just using the default file set -- it all
175 depends on the user's options.
176 """
177 # new behavior when using a template:
178 # the file list is recalculated every time because
179 # even if MANIFEST.in or setup.py are not changed
180 # the user might have added some files in the tree that
181 # need to be included.
182 #
183 # This makes --force the default and only behavior with templates.
184 template_exists = os.path.isfile(self.template)
185 if not template_exists and self._manifest_is_not_generated():
186 self.read_manifest()
187 self.filelist.sort()
188 self.filelist.remove_duplicates()
189 return
190
191 if not template_exists:
192 self.warn(("manifest template '%s' does not exist " +
193 "(using default file list)") %
194 self.template)
195 self.filelist.findall()
196
197 if self.use_defaults:
198 self.add_defaults()
199
200 if template_exists:
201 self.read_template()
202
203 if self.prune:
204 self.prune_file_list()
205
206 self.filelist.sort()
207 self.filelist.remove_duplicates()
208 self.write_manifest()
209
210 def add_defaults(self):
211 """Add all the default files to self.filelist:
212 - README or README.txt
213 - setup.py
214 - test/test*.py
215 - all pure Python modules mentioned in setup script
216 - all files pointed by package_data (build_py)
217 - all files defined in data_files.
218 - all files defined as scripts.
219 - all C sources listed as part of extensions or C libraries
220 in the setup script (doesn't catch C headers!)
221 Warns if (README or README.txt) or setup.py are missing; everything
222 else is optional.
223 """
224 self._add_defaults_standards()
225 self._add_defaults_optional()
226 self._add_defaults_python()
227 self._add_defaults_data_files()
228 self._add_defaults_ext()
229 self._add_defaults_c_libs()
230 self._add_defaults_scripts()
231
232 @staticmethod
233 def _cs_path_exists(fspath):
234 """
235 Case-sensitive path existence check
236
237 >>> sdist._cs_path_exists(__file__)
238 True
239 >>> sdist._cs_path_exists(__file__.upper())
240 False
241 """
242 if not os.path.exists(fspath):
243 return False
244 # make absolute so we always have a directory
245 abspath = os.path.abspath(fspath)
246 directory, filename = os.path.split(abspath)
247 return filename in os.listdir(directory)
248
249 def _add_defaults_standards(self):
250 standards = [self.READMES, self.distribution.script_name]
251 for fn in standards:
252 if isinstance(fn, tuple):
253 alts = fn
254 got_it = False
255 for fn in alts:
256 if self._cs_path_exists(fn):
257 got_it = True
258 self.filelist.append(fn)
259 break
260
261 if not got_it:
262 self.warn("standard file not found: should have one of " +
263 ', '.join(alts))
264 else:
265 if self._cs_path_exists(fn):
266 self.filelist.append(fn)
267 else:
268 self.warn("standard file '%s' not found" % fn)
269
270 def _add_defaults_optional(self):
271 optional = ['test/test*.py', 'setup.cfg']
272 for pattern in optional:
273 files = filter(os.path.isfile, glob(pattern))
274 self.filelist.extend(files)
275
276 def _add_defaults_python(self):
277 # build_py is used to get:
278 # - python modules
279 # - files defined in package_data
280 build_py = self.get_finalized_command('build_py')
281
282 # getting python files
283 if self.distribution.has_pure_modules():
284 self.filelist.extend(build_py.get_source_files())
285
286 # getting package_data files
287 # (computed in build_py.data_files by build_py.finalize_options)
288 for pkg, src_dir, build_dir, filenames in build_py.data_files:
289 for filename in filenames:
290 self.filelist.append(os.path.join(src_dir, filename))
291
292 def _add_defaults_data_files(self):
293 # getting distribution.data_files
294 if self.distribution.has_data_files():
295 for item in self.distribution.data_files:
296 if isinstance(item, str):
297 # plain file
298 item = convert_path(item)
299 if os.path.isfile(item):
300 self.filelist.append(item)
301 else:
302 # a (dirname, filenames) tuple
303 dirname, filenames = item
304 for f in filenames:
305 f = convert_path(f)
306 if os.path.isfile(f):
307 self.filelist.append(f)
308
309 def _add_defaults_ext(self):
310 if self.distribution.has_ext_modules():
311 build_ext = self.get_finalized_command('build_ext')
312 self.filelist.extend(build_ext.get_source_files())
313
314 def _add_defaults_c_libs(self):
315 if self.distribution.has_c_libraries():
316 build_clib = self.get_finalized_command('build_clib')
317 self.filelist.extend(build_clib.get_source_files())
318
319 def _add_defaults_scripts(self):
320 if self.distribution.has_scripts():
321 build_scripts = self.get_finalized_command('build_scripts')
322 self.filelist.extend(build_scripts.get_source_files())
323
324 def read_template(self):
325 """Read and parse manifest template file named by self.template.
326
327 (usually "MANIFEST.in") The parsing and processing is done by
328 'self.filelist', which updates itself accordingly.
329 """
330 log.info("reading manifest template '%s'", self.template)
331 template = TextFile(self.template, strip_comments=1, skip_blanks=1,
332 join_lines=1, lstrip_ws=1, rstrip_ws=1,
333 collapse_join=1)
334
335 try:
336 while True:
337 line = template.readline()
338 if line is None: # end of file
339 break
340
341 try:
342 self.filelist.process_template_line(line)
343 # the call above can raise a DistutilsTemplateError for
344 # malformed lines, or a ValueError from the lower-level
345 # convert_path function
346 except (DistutilsTemplateError, ValueError) as msg:
347 self.warn("%s, line %d: %s" % (template.filename,
348 template.current_line,
349 msg))
350 finally:
351 template.close()
352
353 def prune_file_list(self):
354 """Prune off branches that might slip into the file list as created
355 by 'read_template()', but really don't belong there:
356 * the build tree (typically "build")
357 * the release tree itself (only an issue if we ran "sdist"
358 previously with --keep-temp, or it aborted)
359 * any RCS, CVS, .svn, .hg, .git, .bzr, _darcs directories
360 """
361 build = self.get_finalized_command('build')
362 base_dir = self.distribution.get_fullname()
363
364 self.filelist.exclude_pattern(None, prefix=build.build_base)
365 self.filelist.exclude_pattern(None, prefix=base_dir)
366
367 if sys.platform == 'win32':
368 seps = r'/|\\'
369 else:
370 seps = '/'
371
372 vcs_dirs = ['RCS', 'CVS', r'\.svn', r'\.hg', r'\.git', r'\.bzr',
373 '_darcs']
374 vcs_ptrn = r'(^|%s)(%s)(%s).*' % (seps, '|'.join(vcs_dirs), seps)
375 self.filelist.exclude_pattern(vcs_ptrn, is_regex=1)
376
377 def write_manifest(self):
378 """Write the file list in 'self.filelist' (presumably as filled in
379 by 'add_defaults()' and 'read_template()') to the manifest file
380 named by 'self.manifest'.
381 """
382 if self._manifest_is_not_generated():
383 log.info("not writing to manually maintained "
384 "manifest file '%s'" % self.manifest)
385 return
386
387 content = self.filelist.files[:]
388 content.insert(0, '# file GENERATED by distutils, do NOT edit')
389 self.execute(file_util.write_file, (self.manifest, content),
390 "writing manifest file '%s'" % self.manifest)
391
392 def _manifest_is_not_generated(self):
393 # check for special comment used in 3.1.3 and higher
394 if not os.path.isfile(self.manifest):
395 return False
396
397 fp = open(self.manifest)
398 try:
399 first_line = fp.readline()
400 finally:
401 fp.close()
402 return first_line != '# file GENERATED by distutils, do NOT edit\n'
403
404 def read_manifest(self):
405 """Read the manifest file (named by 'self.manifest') and use it to
406 fill in 'self.filelist', the list of files to include in the source
407 distribution.
408 """
409 log.info("reading manifest file '%s'", self.manifest)
410 with open(self.manifest) as manifest:
411 for line in manifest:
412 # ignore comments and blank lines
413 line = line.strip()
414 if line.startswith('#') or not line:
415 continue
416 self.filelist.append(line)
417
418 def make_release_tree(self, base_dir, files):
419 """Create the directory tree that will become the source
420 distribution archive. All directories implied by the filenames in
421 'files' are created under 'base_dir', and then we hard link or copy
422 (if hard linking is unavailable) those files into place.
423 Essentially, this duplicates the developer's source tree, but in a
424 directory named after the distribution, containing only the files
425 to be distributed.
426 """
427 # Create all the directories under 'base_dir' necessary to
428 # put 'files' there; the 'mkpath()' is just so we don't die
429 # if the manifest happens to be empty.
430 self.mkpath(base_dir)
431 dir_util.create_tree(base_dir, files, dry_run=self.dry_run)
432
433 # And walk over the list of files, either making a hard link (if
434 # os.link exists) to each one that doesn't already exist in its
435 # corresponding location under 'base_dir', or copying each file
436 # that's out-of-date in 'base_dir'. (Usually, all files will be
437 # out-of-date, because by default we blow away 'base_dir' when
438 # we're done making the distribution archives.)
439
440 if hasattr(os, 'link'): # can make hard links on this system
441 link = 'hard'
442 msg = "making hard links in %s..." % base_dir
443 else: # nope, have to copy
444 link = None
445 msg = "copying files to %s..." % base_dir
446
447 if not files:
448 log.warn("no files to distribute -- empty manifest?")
449 else:
450 log.info(msg)
451 for file in files:
452 if not os.path.isfile(file):
453 log.warn("'%s' not a regular file -- skipping", file)
454 else:
455 dest = os.path.join(base_dir, file)
456 self.copy_file(file, dest, link=link)
457
458 self.distribution.metadata.write_pkg_info(base_dir)
459
460 def make_distribution(self):
461 """Create the source distribution(s). First, we create the release
462 tree with 'make_release_tree()'; then, we create all required
463 archive files (according to 'self.formats') from the release tree.
464 Finally, we clean up by blowing away the release tree (unless
465 'self.keep_temp' is true). The list of archive files created is
466 stored so it can be retrieved later by 'get_archive_files()'.
467 """
468 # Don't warn about missing meta-data here -- should be (and is!)
469 # done elsewhere.
470 base_dir = self.distribution.get_fullname()
471 base_name = os.path.join(self.dist_dir, base_dir)
472
473 self.make_release_tree(base_dir, self.filelist.files)
474 archive_files = [] # remember names of files we create
475 # tar archive must be created last to avoid overwrite and remove
476 if 'tar' in self.formats:
477 self.formats.append(self.formats.pop(self.formats.index('tar')))
478
479 for fmt in self.formats:
480 file = self.make_archive(base_name, fmt, base_dir=base_dir,
481 owner=self.owner, group=self.group)
482 archive_files.append(file)
483 self.distribution.dist_files.append(('sdist', '', file))
484
485 self.archive_files = archive_files
486
487 if not self.keep_temp:
488 dir_util.remove_tree(base_dir, dry_run=self.dry_run)
489
490 def get_archive_files(self):
491 """Return the list of archive files created when the command
492 was run, or None if the command hasn't run yet.
493 """
494 return self.archive_files