Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(405)

Side by Side Diff: recipe_engine/third_party/setuptools/command/egg_info.py

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

Powered by Google App Engine
This is Rietveld 408576698