comparison venv/lib/python2.7/site-packages/setuptools/command/egg_info.py @ 0:d67268158946 draft

planemo upload commit a3f181f5f126803c654b3a66dd4e83a48f7e203b
author bcclaywell
date Mon, 12 Oct 2015 17:43:33 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:d67268158946
1 """setuptools.command.egg_info
2
3 Create a distribution's .egg-info directory and contents"""
4
5 from distutils.filelist import FileList as _FileList
6 from distutils.util import convert_path
7 from distutils import log
8 import distutils.errors
9 import distutils.filelist
10 import os
11 import re
12 import sys
13
14 try:
15 from setuptools_svn import svn_utils
16 except ImportError:
17 pass
18
19 from setuptools import Command
20 from setuptools.command.sdist import sdist
21 from setuptools.compat import basestring, PY3, StringIO
22 from setuptools.command.sdist import walk_revctrl
23 from pkg_resources import (
24 parse_requirements, safe_name, parse_version,
25 safe_version, yield_lines, EntryPoint, iter_entry_points, to_filename)
26 import setuptools.unicode_utils as unicode_utils
27
28 from pkg_resources import packaging
29
30 class egg_info(Command):
31 description = "create a distribution's .egg-info directory"
32
33 user_options = [
34 ('egg-base=', 'e', "directory containing .egg-info directories"
35 " (default: top of the source tree)"),
36 ('tag-svn-revision', 'r',
37 "Add subversion revision ID to version number"),
38 ('tag-date', 'd', "Add date stamp (e.g. 20050528) to version number"),
39 ('tag-build=', 'b', "Specify explicit tag to add to version number"),
40 ('no-svn-revision', 'R',
41 "Don't add subversion revision ID [default]"),
42 ('no-date', 'D', "Don't include date stamp [default]"),
43 ]
44
45 boolean_options = ['tag-date', 'tag-svn-revision']
46 negative_opt = {'no-svn-revision': 'tag-svn-revision',
47 'no-date': 'tag-date'}
48
49 def initialize_options(self):
50 self.egg_name = None
51 self.egg_version = None
52 self.egg_base = None
53 self.egg_info = None
54 self.tag_build = None
55 self.tag_svn_revision = 0
56 self.tag_date = 0
57 self.broken_egg_info = False
58 self.vtags = None
59
60 def save_version_info(self, filename):
61 from setuptools.command.setopt import edit_config
62
63 values = dict(
64 egg_info=dict(
65 tag_svn_revision=0,
66 tag_date=0,
67 tag_build=self.tags(),
68 )
69 )
70 edit_config(filename, values)
71
72 def finalize_options(self):
73 self.egg_name = safe_name(self.distribution.get_name())
74 self.vtags = self.tags()
75 self.egg_version = self.tagged_version()
76
77 parsed_version = parse_version(self.egg_version)
78
79 try:
80 is_version = isinstance(parsed_version, packaging.version.Version)
81 spec = (
82 "%s==%s" if is_version else "%s===%s"
83 )
84 list(
85 parse_requirements(spec % (self.egg_name, self.egg_version))
86 )
87 except ValueError:
88 raise distutils.errors.DistutilsOptionError(
89 "Invalid distribution name or version syntax: %s-%s" %
90 (self.egg_name, self.egg_version)
91 )
92
93 if self.egg_base is None:
94 dirs = self.distribution.package_dir
95 self.egg_base = (dirs or {}).get('', os.curdir)
96
97 self.ensure_dirname('egg_base')
98 self.egg_info = to_filename(self.egg_name) + '.egg-info'
99 if self.egg_base != os.curdir:
100 self.egg_info = os.path.join(self.egg_base, self.egg_info)
101 if '-' in self.egg_name:
102 self.check_broken_egg_info()
103
104 # Set package version for the benefit of dumber commands
105 # (e.g. sdist, bdist_wininst, etc.)
106 #
107 self.distribution.metadata.version = self.egg_version
108
109 # If we bootstrapped around the lack of a PKG-INFO, as might be the
110 # case in a fresh checkout, make sure that any special tags get added
111 # to the version info
112 #
113 pd = self.distribution._patched_dist
114 if pd is not None and pd.key == self.egg_name.lower():
115 pd._version = self.egg_version
116 pd._parsed_version = parse_version(self.egg_version)
117 self.distribution._patched_dist = None
118
119 def write_or_delete_file(self, what, filename, data, force=False):
120 """Write `data` to `filename` or delete if empty
121
122 If `data` is non-empty, this routine is the same as ``write_file()``.
123 If `data` is empty but not ``None``, this is the same as calling
124 ``delete_file(filename)`. If `data` is ``None``, then this is a no-op
125 unless `filename` exists, in which case a warning is issued about the
126 orphaned file (if `force` is false), or deleted (if `force` is true).
127 """
128 if data:
129 self.write_file(what, filename, data)
130 elif os.path.exists(filename):
131 if data is None and not force:
132 log.warn(
133 "%s not set in setup(), but %s exists", what, filename
134 )
135 return
136 else:
137 self.delete_file(filename)
138
139 def write_file(self, what, filename, data):
140 """Write `data` to `filename` (if not a dry run) after announcing it
141
142 `what` is used in a log message to identify what is being written
143 to the file.
144 """
145 log.info("writing %s to %s", what, filename)
146 if PY3:
147 data = data.encode("utf-8")
148 if not self.dry_run:
149 f = open(filename, 'wb')
150 f.write(data)
151 f.close()
152
153 def delete_file(self, filename):
154 """Delete `filename` (if not a dry run) after announcing it"""
155 log.info("deleting %s", filename)
156 if not self.dry_run:
157 os.unlink(filename)
158
159 def tagged_version(self):
160 version = self.distribution.get_version()
161 # egg_info may be called more than once for a distribution,
162 # in which case the version string already contains all tags.
163 if self.vtags and version.endswith(self.vtags):
164 return safe_version(version)
165 return safe_version(version + self.vtags)
166
167 def run(self):
168 self.mkpath(self.egg_info)
169 installer = self.distribution.fetch_build_egg
170 for ep in iter_entry_points('egg_info.writers'):
171 ep.require(installer=installer)
172 writer = ep.resolve()
173 writer(self, ep.name, os.path.join(self.egg_info, ep.name))
174
175 # Get rid of native_libs.txt if it was put there by older bdist_egg
176 nl = os.path.join(self.egg_info, "native_libs.txt")
177 if os.path.exists(nl):
178 self.delete_file(nl)
179
180 self.find_sources()
181
182 def tags(self):
183 version = ''
184 if self.tag_build:
185 version += self.tag_build
186 if self.tag_svn_revision:
187 rev = self.get_svn_revision()
188 if rev: # is 0 if it's not an svn working copy
189 version += '-r%s' % rev
190 if self.tag_date:
191 import time
192
193 version += time.strftime("-%Y%m%d")
194 return version
195
196 @staticmethod
197 def get_svn_revision():
198 if 'svn_utils' not in globals():
199 return "0"
200 return str(svn_utils.SvnInfo.load(os.curdir).get_revision())
201
202 def find_sources(self):
203 """Generate SOURCES.txt manifest file"""
204 manifest_filename = os.path.join(self.egg_info, "SOURCES.txt")
205 mm = manifest_maker(self.distribution)
206 mm.manifest = manifest_filename
207 mm.run()
208 self.filelist = mm.filelist
209
210 def check_broken_egg_info(self):
211 bei = self.egg_name + '.egg-info'
212 if self.egg_base != os.curdir:
213 bei = os.path.join(self.egg_base, bei)
214 if os.path.exists(bei):
215 log.warn(
216 "-" * 78 + '\n'
217 "Note: Your current .egg-info directory has a '-' in its name;"
218 '\nthis will not work correctly with "setup.py develop".\n\n'
219 'Please rename %s to %s to correct this problem.\n' + '-' * 78,
220 bei, self.egg_info
221 )
222 self.broken_egg_info = self.egg_info
223 self.egg_info = bei # make it work for now
224
225
226 class FileList(_FileList):
227 """File list that accepts only existing, platform-independent paths"""
228
229 def append(self, item):
230 if item.endswith('\r'): # Fix older sdists built on Windows
231 item = item[:-1]
232 path = convert_path(item)
233
234 if self._safe_path(path):
235 self.files.append(path)
236
237 def extend(self, paths):
238 self.files.extend(filter(self._safe_path, paths))
239
240 def _repair(self):
241 """
242 Replace self.files with only safe paths
243
244 Because some owners of FileList manipulate the underlying
245 ``files`` attribute directly, this method must be called to
246 repair those paths.
247 """
248 self.files = list(filter(self._safe_path, self.files))
249
250 def _safe_path(self, path):
251 enc_warn = "'%s' not %s encodable -- skipping"
252
253 # To avoid accidental trans-codings errors, first to unicode
254 u_path = unicode_utils.filesys_decode(path)
255 if u_path is None:
256 log.warn("'%s' in unexpected encoding -- skipping" % path)
257 return False
258
259 # Must ensure utf-8 encodability
260 utf8_path = unicode_utils.try_encode(u_path, "utf-8")
261 if utf8_path is None:
262 log.warn(enc_warn, path, 'utf-8')
263 return False
264
265 try:
266 # accept is either way checks out
267 if os.path.exists(u_path) or os.path.exists(utf8_path):
268 return True
269 # this will catch any encode errors decoding u_path
270 except UnicodeEncodeError:
271 log.warn(enc_warn, path, sys.getfilesystemencoding())
272
273
274 class manifest_maker(sdist):
275 template = "MANIFEST.in"
276
277 def initialize_options(self):
278 self.use_defaults = 1
279 self.prune = 1
280 self.manifest_only = 1
281 self.force_manifest = 1
282
283 def finalize_options(self):
284 pass
285
286 def run(self):
287 self.filelist = FileList()
288 if not os.path.exists(self.manifest):
289 self.write_manifest() # it must exist so it'll get in the list
290 self.filelist.findall()
291 self.add_defaults()
292 if os.path.exists(self.template):
293 self.read_template()
294 self.prune_file_list()
295 self.filelist.sort()
296 self.filelist.remove_duplicates()
297 self.write_manifest()
298
299 def _manifest_normalize(self, path):
300 path = unicode_utils.filesys_decode(path)
301 return path.replace(os.sep, '/')
302
303 def write_manifest(self):
304 """
305 Write the file list in 'self.filelist' to the manifest file
306 named by 'self.manifest'.
307 """
308 self.filelist._repair()
309
310 # Now _repairs should encodability, but not unicode
311 files = [self._manifest_normalize(f) for f in self.filelist.files]
312 msg = "writing manifest file '%s'" % self.manifest
313 self.execute(write_file, (self.manifest, files), msg)
314
315 def warn(self, msg): # suppress missing-file warnings from sdist
316 if not msg.startswith("standard file not found:"):
317 sdist.warn(self, msg)
318
319 def add_defaults(self):
320 sdist.add_defaults(self)
321 self.filelist.append(self.template)
322 self.filelist.append(self.manifest)
323 rcfiles = list(walk_revctrl())
324 if rcfiles:
325 self.filelist.extend(rcfiles)
326 elif os.path.exists(self.manifest):
327 self.read_manifest()
328 ei_cmd = self.get_finalized_command('egg_info')
329 self._add_egg_info(cmd=ei_cmd)
330 self.filelist.include_pattern("*", prefix=ei_cmd.egg_info)
331
332 def _add_egg_info(self, cmd):
333 """
334 Add paths for egg-info files for an external egg-base.
335
336 The egg-info files are written to egg-base. If egg-base is
337 outside the current working directory, this method
338 searchs the egg-base directory for files to include
339 in the manifest. Uses distutils.filelist.findall (which is
340 really the version monkeypatched in by setuptools/__init__.py)
341 to perform the search.
342
343 Since findall records relative paths, prefix the returned
344 paths with cmd.egg_base, so add_default's include_pattern call
345 (which is looking for the absolute cmd.egg_info) will match
346 them.
347 """
348 if cmd.egg_base == os.curdir:
349 # egg-info files were already added by something else
350 return
351
352 discovered = distutils.filelist.findall(cmd.egg_base)
353 resolved = (os.path.join(cmd.egg_base, path) for path in discovered)
354 self.filelist.allfiles.extend(resolved)
355
356 def prune_file_list(self):
357 build = self.get_finalized_command('build')
358 base_dir = self.distribution.get_fullname()
359 self.filelist.exclude_pattern(None, prefix=build.build_base)
360 self.filelist.exclude_pattern(None, prefix=base_dir)
361 sep = re.escape(os.sep)
362 self.filelist.exclude_pattern(r'(^|' + sep + r')(RCS|CVS|\.svn)' + sep,
363 is_regex=1)
364
365
366 def write_file(filename, contents):
367 """Create a file with the specified name and write 'contents' (a
368 sequence of strings without line terminators) to it.
369 """
370 contents = "\n".join(contents)
371
372 # assuming the contents has been vetted for utf-8 encoding
373 contents = contents.encode("utf-8")
374
375 with open(filename, "wb") as f: # always write POSIX-style manifest
376 f.write(contents)
377
378
379 def write_pkg_info(cmd, basename, filename):
380 log.info("writing %s", filename)
381 if not cmd.dry_run:
382 metadata = cmd.distribution.metadata
383 metadata.version, oldver = cmd.egg_version, metadata.version
384 metadata.name, oldname = cmd.egg_name, metadata.name
385 try:
386 # write unescaped data to PKG-INFO, so older pkg_resources
387 # can still parse it
388 metadata.write_pkg_info(cmd.egg_info)
389 finally:
390 metadata.name, metadata.version = oldname, oldver
391
392 safe = getattr(cmd.distribution, 'zip_safe', None)
393 from setuptools.command import bdist_egg
394
395 bdist_egg.write_safety_flag(cmd.egg_info, safe)
396
397
398 def warn_depends_obsolete(cmd, basename, filename):
399 if os.path.exists(filename):
400 log.warn(
401 "WARNING: 'depends.txt' is not used by setuptools 0.6!\n"
402 "Use the install_requires/extras_require setup() args instead."
403 )
404
405
406 def _write_requirements(stream, reqs):
407 lines = yield_lines(reqs or ())
408 append_cr = lambda line: line + '\n'
409 lines = map(append_cr, lines)
410 stream.writelines(lines)
411
412
413 def write_requirements(cmd, basename, filename):
414 dist = cmd.distribution
415 data = StringIO()
416 _write_requirements(data, dist.install_requires)
417 extras_require = dist.extras_require or {}
418 for extra in sorted(extras_require):
419 data.write('\n[{extra}]\n'.format(**vars()))
420 _write_requirements(data, extras_require[extra])
421 cmd.write_or_delete_file("requirements", filename, data.getvalue())
422
423
424 def write_setup_requirements(cmd, basename, filename):
425 data = StringIO()
426 _write_requirements(data, cmd.distribution.setup_requires)
427 cmd.write_or_delete_file("setup-requirements", filename, data.getvalue())
428
429
430 def write_toplevel_names(cmd, basename, filename):
431 pkgs = dict.fromkeys(
432 [
433 k.split('.', 1)[0]
434 for k in cmd.distribution.iter_distribution_names()
435 ]
436 )
437 cmd.write_file("top-level names", filename, '\n'.join(sorted(pkgs)) + '\n')
438
439
440 def overwrite_arg(cmd, basename, filename):
441 write_arg(cmd, basename, filename, True)
442
443
444 def write_arg(cmd, basename, filename, force=False):
445 argname = os.path.splitext(basename)[0]
446 value = getattr(cmd.distribution, argname, None)
447 if value is not None:
448 value = '\n'.join(value) + '\n'
449 cmd.write_or_delete_file(argname, filename, value, force)
450
451
452 def write_entries(cmd, basename, filename):
453 ep = cmd.distribution.entry_points
454
455 if isinstance(ep, basestring) or ep is None:
456 data = ep
457 elif ep is not None:
458 data = []
459 for section, contents in sorted(ep.items()):
460 if not isinstance(contents, basestring):
461 contents = EntryPoint.parse_group(section, contents)
462 contents = '\n'.join(sorted(map(str, contents.values())))
463 data.append('[%s]\n%s\n\n' % (section, contents))
464 data = ''.join(data)
465
466 cmd.write_or_delete_file('entry points', filename, data, True)
467
468
469 def get_pkg_info_revision():
470 # See if we can get a -r### off of PKG-INFO, in case this is an sdist of
471 # a subversion revision
472 #
473 if os.path.exists('PKG-INFO'):
474 f = open('PKG-INFO', 'rU')
475 for line in f:
476 match = re.match(r"Version:.*-r(\d+)\s*$", line)
477 if match:
478 return int(match.group(1))
479 f.close()
480 return 0