Merge lp:~allenap/python-oops-datedir-repo/dont-depend-on-launchpadlib into lp:python-oops-datedir-repo

Proposed by Gavin Panella
Status: Work in progress
Proposed branch: lp:~allenap/python-oops-datedir-repo/dont-depend-on-launchpadlib
Merge into: lp:python-oops-datedir-repo
Diff against target: 560 lines (+507/-15)
3 files modified
.bzrignore (+9/-7)
distribute_setup.py (+485/-0)
setup.py (+13/-8)
To merge this branch: bzr merge lp:~allenap/python-oops-datedir-repo/dont-depend-on-launchpadlib
Reviewer Review Type Date Requested Status
Robert Collins (community) Needs Fixing
Review via email: mp+90405@code.launchpad.net

Commit message

Make prune an optional feature.

To post a comment you must log in.
Revision history for this message
Robert Collins (lifeless) wrote :

Hi, this seems to conflate three entirely different things:
 - it reformats some stuff (shrug)
 - it adds a totally new way to install the package, with a -tonne- of boilerplate (yuck)
 - it does a one-line change:
+ 'prune = oops_datedir_repo.prune:main [prune]'

The last thing seems to do its work on it's own - what is is the huge boilerplate for? Why does it need to be copied into the tree?

review: Needs Fixing

Unmerged revisions

32. By Gavin Panella

Make prune optional.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2011-11-11 04:48:53 +0000
3+++ .bzrignore 2012-01-27 11:25:27 +0000
4@@ -1,11 +1,13 @@
5-./eggs/*
6 ./.installed.cfg
7+./.testrepository
8+./bin
9 ./develop-eggs
10-./bin
11+./dist
12+./distribute*.egg
13+./distribute*.tar.gz
14+./download-cache
15+./eggs
16+./eggs/*
17+./MANIFEST
18 ./oops_datedir_repo.egg-info
19 ./parts
20-./eggs
21-./download-cache
22-./dist
23-./MANIFEST
24-.testrepository
25
26=== added file 'distribute_setup.py'
27--- distribute_setup.py 1970-01-01 00:00:00 +0000
28+++ distribute_setup.py 2012-01-27 11:25:27 +0000
29@@ -0,0 +1,485 @@
30+#!python
31+"""Bootstrap distribute installation
32+
33+If you want to use setuptools in your package's setup.py, just include this
34+file in the same directory with it, and add this to the top of your setup.py::
35+
36+ from distribute_setup import use_setuptools
37+ use_setuptools()
38+
39+If you want to require a specific version of setuptools, set a download
40+mirror, or use an alternate download directory, you can do so by supplying
41+the appropriate options to ``use_setuptools()``.
42+
43+This file can also be run as a script to install or upgrade setuptools.
44+"""
45+import os
46+import sys
47+import time
48+import fnmatch
49+import tempfile
50+import tarfile
51+from distutils import log
52+
53+try:
54+ from site import USER_SITE
55+except ImportError:
56+ USER_SITE = None
57+
58+try:
59+ import subprocess
60+
61+ def _python_cmd(*args):
62+ args = (sys.executable,) + args
63+ return subprocess.call(args) == 0
64+
65+except ImportError:
66+ # will be used for python 2.3
67+ def _python_cmd(*args):
68+ args = (sys.executable,) + args
69+ # quoting arguments if windows
70+ if sys.platform == 'win32':
71+ def quote(arg):
72+ if ' ' in arg:
73+ return '"%s"' % arg
74+ return arg
75+ args = [quote(arg) for arg in args]
76+ return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
77+
78+DEFAULT_VERSION = "0.6.24"
79+DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
80+SETUPTOOLS_FAKED_VERSION = "0.6c11"
81+
82+SETUPTOOLS_PKG_INFO = """\
83+Metadata-Version: 1.0
84+Name: setuptools
85+Version: %s
86+Summary: xxxx
87+Home-page: xxx
88+Author: xxx
89+Author-email: xxx
90+License: xxx
91+Description: xxx
92+""" % SETUPTOOLS_FAKED_VERSION
93+
94+
95+def _install(tarball):
96+ # extracting the tarball
97+ tmpdir = tempfile.mkdtemp()
98+ log.warn('Extracting in %s', tmpdir)
99+ old_wd = os.getcwd()
100+ try:
101+ os.chdir(tmpdir)
102+ tar = tarfile.open(tarball)
103+ _extractall(tar)
104+ tar.close()
105+
106+ # going in the directory
107+ subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
108+ os.chdir(subdir)
109+ log.warn('Now working in %s', subdir)
110+
111+ # installing
112+ log.warn('Installing Distribute')
113+ if not _python_cmd('setup.py', 'install'):
114+ log.warn('Something went wrong during the installation.')
115+ log.warn('See the error message above.')
116+ finally:
117+ os.chdir(old_wd)
118+
119+
120+def _build_egg(egg, tarball, to_dir):
121+ # extracting the tarball
122+ tmpdir = tempfile.mkdtemp()
123+ log.warn('Extracting in %s', tmpdir)
124+ old_wd = os.getcwd()
125+ try:
126+ os.chdir(tmpdir)
127+ tar = tarfile.open(tarball)
128+ _extractall(tar)
129+ tar.close()
130+
131+ # going in the directory
132+ subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
133+ os.chdir(subdir)
134+ log.warn('Now working in %s', subdir)
135+
136+ # building an egg
137+ log.warn('Building a Distribute egg in %s', to_dir)
138+ _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
139+
140+ finally:
141+ os.chdir(old_wd)
142+ # returning the result
143+ log.warn(egg)
144+ if not os.path.exists(egg):
145+ raise IOError('Could not build the egg.')
146+
147+
148+def _do_download(version, download_base, to_dir, download_delay):
149+ egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg'
150+ % (version, sys.version_info[0], sys.version_info[1]))
151+ if not os.path.exists(egg):
152+ tarball = download_setuptools(version, download_base,
153+ to_dir, download_delay)
154+ _build_egg(egg, tarball, to_dir)
155+ sys.path.insert(0, egg)
156+ import setuptools
157+ setuptools.bootstrap_install_from = egg
158+
159+
160+def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
161+ to_dir=os.curdir, download_delay=15, no_fake=True):
162+ # making sure we use the absolute path
163+ to_dir = os.path.abspath(to_dir)
164+ was_imported = 'pkg_resources' in sys.modules or \
165+ 'setuptools' in sys.modules
166+ try:
167+ try:
168+ import pkg_resources
169+ if not hasattr(pkg_resources, '_distribute'):
170+ if not no_fake:
171+ _fake_setuptools()
172+ raise ImportError
173+ except ImportError:
174+ return _do_download(version, download_base, to_dir, download_delay)
175+ try:
176+ pkg_resources.require("distribute>="+version)
177+ return
178+ except pkg_resources.VersionConflict:
179+ e = sys.exc_info()[1]
180+ if was_imported:
181+ sys.stderr.write(
182+ "The required version of distribute (>=%s) is not available,\n"
183+ "and can't be installed while this script is running. Please\n"
184+ "install a more recent version first, using\n"
185+ "'easy_install -U distribute'."
186+ "\n\n(Currently using %r)\n" % (version, e.args[0]))
187+ sys.exit(2)
188+ else:
189+ del pkg_resources, sys.modules['pkg_resources'] # reload ok
190+ return _do_download(version, download_base, to_dir,
191+ download_delay)
192+ except pkg_resources.DistributionNotFound:
193+ return _do_download(version, download_base, to_dir,
194+ download_delay)
195+ finally:
196+ if not no_fake:
197+ _create_fake_setuptools_pkg_info(to_dir)
198+
199+def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
200+ to_dir=os.curdir, delay=15):
201+ """Download distribute from a specified location and return its filename
202+
203+ `version` should be a valid distribute version number that is available
204+ as an egg for download under the `download_base` URL (which should end
205+ with a '/'). `to_dir` is the directory where the egg will be downloaded.
206+ `delay` is the number of seconds to pause before an actual download
207+ attempt.
208+ """
209+ # making sure we use the absolute path
210+ to_dir = os.path.abspath(to_dir)
211+ try:
212+ from urllib.request import urlopen
213+ except ImportError:
214+ from urllib2 import urlopen
215+ tgz_name = "distribute-%s.tar.gz" % version
216+ url = download_base + tgz_name
217+ saveto = os.path.join(to_dir, tgz_name)
218+ src = dst = None
219+ if not os.path.exists(saveto): # Avoid repeated downloads
220+ try:
221+ log.warn("Downloading %s", url)
222+ src = urlopen(url)
223+ # Read/write all in one block, so we don't create a corrupt file
224+ # if the download is interrupted.
225+ data = src.read()
226+ dst = open(saveto, "wb")
227+ dst.write(data)
228+ finally:
229+ if src:
230+ src.close()
231+ if dst:
232+ dst.close()
233+ return os.path.realpath(saveto)
234+
235+def _no_sandbox(function):
236+ def __no_sandbox(*args, **kw):
237+ try:
238+ from setuptools.sandbox import DirectorySandbox
239+ if not hasattr(DirectorySandbox, '_old'):
240+ def violation(*args):
241+ pass
242+ DirectorySandbox._old = DirectorySandbox._violation
243+ DirectorySandbox._violation = violation
244+ patched = True
245+ else:
246+ patched = False
247+ except ImportError:
248+ patched = False
249+
250+ try:
251+ return function(*args, **kw)
252+ finally:
253+ if patched:
254+ DirectorySandbox._violation = DirectorySandbox._old
255+ del DirectorySandbox._old
256+
257+ return __no_sandbox
258+
259+def _patch_file(path, content):
260+ """Will backup the file then patch it"""
261+ existing_content = open(path).read()
262+ if existing_content == content:
263+ # already patched
264+ log.warn('Already patched.')
265+ return False
266+ log.warn('Patching...')
267+ _rename_path(path)
268+ f = open(path, 'w')
269+ try:
270+ f.write(content)
271+ finally:
272+ f.close()
273+ return True
274+
275+_patch_file = _no_sandbox(_patch_file)
276+
277+def _same_content(path, content):
278+ return open(path).read() == content
279+
280+def _rename_path(path):
281+ new_name = path + '.OLD.%s' % time.time()
282+ log.warn('Renaming %s into %s', path, new_name)
283+ os.rename(path, new_name)
284+ return new_name
285+
286+def _remove_flat_installation(placeholder):
287+ if not os.path.isdir(placeholder):
288+ log.warn('Unkown installation at %s', placeholder)
289+ return False
290+ found = False
291+ for file in os.listdir(placeholder):
292+ if fnmatch.fnmatch(file, 'setuptools*.egg-info'):
293+ found = True
294+ break
295+ if not found:
296+ log.warn('Could not locate setuptools*.egg-info')
297+ return
298+
299+ log.warn('Removing elements out of the way...')
300+ pkg_info = os.path.join(placeholder, file)
301+ if os.path.isdir(pkg_info):
302+ patched = _patch_egg_dir(pkg_info)
303+ else:
304+ patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO)
305+
306+ if not patched:
307+ log.warn('%s already patched.', pkg_info)
308+ return False
309+ # now let's move the files out of the way
310+ for element in ('setuptools', 'pkg_resources.py', 'site.py'):
311+ element = os.path.join(placeholder, element)
312+ if os.path.exists(element):
313+ _rename_path(element)
314+ else:
315+ log.warn('Could not find the %s element of the '
316+ 'Setuptools distribution', element)
317+ return True
318+
319+_remove_flat_installation = _no_sandbox(_remove_flat_installation)
320+
321+def _after_install(dist):
322+ log.warn('After install bootstrap.')
323+ placeholder = dist.get_command_obj('install').install_purelib
324+ _create_fake_setuptools_pkg_info(placeholder)
325+
326+def _create_fake_setuptools_pkg_info(placeholder):
327+ if not placeholder or not os.path.exists(placeholder):
328+ log.warn('Could not find the install location')
329+ return
330+ pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1])
331+ setuptools_file = 'setuptools-%s-py%s.egg-info' % \
332+ (SETUPTOOLS_FAKED_VERSION, pyver)
333+ pkg_info = os.path.join(placeholder, setuptools_file)
334+ if os.path.exists(pkg_info):
335+ log.warn('%s already exists', pkg_info)
336+ return
337+
338+ log.warn('Creating %s', pkg_info)
339+ f = open(pkg_info, 'w')
340+ try:
341+ f.write(SETUPTOOLS_PKG_INFO)
342+ finally:
343+ f.close()
344+
345+ pth_file = os.path.join(placeholder, 'setuptools.pth')
346+ log.warn('Creating %s', pth_file)
347+ f = open(pth_file, 'w')
348+ try:
349+ f.write(os.path.join(os.curdir, setuptools_file))
350+ finally:
351+ f.close()
352+
353+_create_fake_setuptools_pkg_info = _no_sandbox(_create_fake_setuptools_pkg_info)
354+
355+def _patch_egg_dir(path):
356+ # let's check if it's already patched
357+ pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
358+ if os.path.exists(pkg_info):
359+ if _same_content(pkg_info, SETUPTOOLS_PKG_INFO):
360+ log.warn('%s already patched.', pkg_info)
361+ return False
362+ _rename_path(path)
363+ os.mkdir(path)
364+ os.mkdir(os.path.join(path, 'EGG-INFO'))
365+ pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
366+ f = open(pkg_info, 'w')
367+ try:
368+ f.write(SETUPTOOLS_PKG_INFO)
369+ finally:
370+ f.close()
371+ return True
372+
373+_patch_egg_dir = _no_sandbox(_patch_egg_dir)
374+
375+def _before_install():
376+ log.warn('Before install bootstrap.')
377+ _fake_setuptools()
378+
379+
380+def _under_prefix(location):
381+ if 'install' not in sys.argv:
382+ return True
383+ args = sys.argv[sys.argv.index('install')+1:]
384+ for index, arg in enumerate(args):
385+ for option in ('--root', '--prefix'):
386+ if arg.startswith('%s=' % option):
387+ top_dir = arg.split('root=')[-1]
388+ return location.startswith(top_dir)
389+ elif arg == option:
390+ if len(args) > index:
391+ top_dir = args[index+1]
392+ return location.startswith(top_dir)
393+ if arg == '--user' and USER_SITE is not None:
394+ return location.startswith(USER_SITE)
395+ return True
396+
397+
398+def _fake_setuptools():
399+ log.warn('Scanning installed packages')
400+ try:
401+ import pkg_resources
402+ except ImportError:
403+ # we're cool
404+ log.warn('Setuptools or Distribute does not seem to be installed.')
405+ return
406+ ws = pkg_resources.working_set
407+ try:
408+ setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools',
409+ replacement=False))
410+ except TypeError:
411+ # old distribute API
412+ setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools'))
413+
414+ if setuptools_dist is None:
415+ log.warn('No setuptools distribution found')
416+ return
417+ # detecting if it was already faked
418+ setuptools_location = setuptools_dist.location
419+ log.warn('Setuptools installation detected at %s', setuptools_location)
420+
421+ # if --root or --preix was provided, and if
422+ # setuptools is not located in them, we don't patch it
423+ if not _under_prefix(setuptools_location):
424+ log.warn('Not patching, --root or --prefix is installing Distribute'
425+ ' in another location')
426+ return
427+
428+ # let's see if its an egg
429+ if not setuptools_location.endswith('.egg'):
430+ log.warn('Non-egg installation')
431+ res = _remove_flat_installation(setuptools_location)
432+ if not res:
433+ return
434+ else:
435+ log.warn('Egg installation')
436+ pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO')
437+ if (os.path.exists(pkg_info) and
438+ _same_content(pkg_info, SETUPTOOLS_PKG_INFO)):
439+ log.warn('Already patched.')
440+ return
441+ log.warn('Patching...')
442+ # let's create a fake egg replacing setuptools one
443+ res = _patch_egg_dir(setuptools_location)
444+ if not res:
445+ return
446+ log.warn('Patched done.')
447+ _relaunch()
448+
449+
450+def _relaunch():
451+ log.warn('Relaunching...')
452+ # we have to relaunch the process
453+ # pip marker to avoid a relaunch bug
454+ if sys.argv[:3] == ['-c', 'install', '--single-version-externally-managed']:
455+ sys.argv[0] = 'setup.py'
456+ args = [sys.executable] + sys.argv
457+ sys.exit(subprocess.call(args))
458+
459+
460+def _extractall(self, path=".", members=None):
461+ """Extract all members from the archive to the current working
462+ directory and set owner, modification time and permissions on
463+ directories afterwards. `path' specifies a different directory
464+ to extract to. `members' is optional and must be a subset of the
465+ list returned by getmembers().
466+ """
467+ import copy
468+ import operator
469+ from tarfile import ExtractError
470+ directories = []
471+
472+ if members is None:
473+ members = self
474+
475+ for tarinfo in members:
476+ if tarinfo.isdir():
477+ # Extract directories with a safe mode.
478+ directories.append(tarinfo)
479+ tarinfo = copy.copy(tarinfo)
480+ tarinfo.mode = 448 # decimal for oct 0700
481+ self.extract(tarinfo, path)
482+
483+ # Reverse sort directories.
484+ if sys.version_info < (2, 4):
485+ def sorter(dir1, dir2):
486+ return cmp(dir1.name, dir2.name)
487+ directories.sort(sorter)
488+ directories.reverse()
489+ else:
490+ directories.sort(key=operator.attrgetter('name'), reverse=True)
491+
492+ # Set correct owner, mtime and filemode on directories.
493+ for tarinfo in directories:
494+ dirpath = os.path.join(path, tarinfo.name)
495+ try:
496+ self.chown(tarinfo, dirpath)
497+ self.utime(tarinfo, dirpath)
498+ self.chmod(tarinfo, dirpath)
499+ except ExtractError:
500+ e = sys.exc_info()[1]
501+ if self.errorlevel > 1:
502+ raise
503+ else:
504+ self._dbg(1, "tarfile: %s" % e)
505+
506+
507+def main(argv, version=DEFAULT_VERSION):
508+ """Install or upgrade setuptools and EasyInstall"""
509+ tarball = download_setuptools()
510+ _install(tarball)
511+
512+
513+if __name__ == '__main__':
514+ main(sys.argv[1:])
515
516=== modified file 'setup.py'
517--- setup.py 2011-12-08 03:05:06 +0000
518+++ setup.py 2012-01-27 11:25:27 +0000
519@@ -14,9 +14,11 @@
520 # You should have received a copy of the GNU Lesser General Public License
521 # along with this program. If not, see <http://www.gnu.org/licenses/>.
522 # GNU Lesser General Public License version 3 (see the file LICENSE).
523-
524-
525-from distutils.core import setup
526+
527+import distribute_setup
528+distribute_setup.use_setuptools()
529+
530+from setuptools import setup
531 import os.path
532
533 description = file(os.path.join(os.path.dirname(__file__), 'README'), 'rb').read()
534@@ -40,7 +42,6 @@
535 install_requires = [
536 'bson',
537 'iso8601',
538- 'launchpadlib', # Needed for pruning - perhaps should be optional.
539 'oops',
540 'pytz',
541 ],
542@@ -48,10 +49,14 @@
543 test=[
544 'fixtures',
545 'testtools',
546+ ],
547+ prune=[
548+ 'launchpadlib',
549+ ],
550+ ),
551+ entry_points=dict(
552+ console_scripts=[ # `console_scripts` is a magic name to setuptools
553+ 'prune = oops_datedir_repo.prune:main [prune]',
554 ]
555 ),
556- entry_points=dict(
557- console_scripts=[ # `console_scripts` is a magic name to setuptools
558- 'prune = oops_datedir_repo.prune:main',
559- ]),
560 )

Subscribers

People subscribed via source and target branches

to all changes: