pbundler-0.0.4/0000755000000000000000000000000011773032467010226 5ustar pbundler-0.0.4/PBundler/0000755000000000000000000000000011773032051011726 5ustar pbundler-0.0.4/PBundler/__init__.py0000644000000000000000000000004011727444651014045 0ustar from PBundler.pbundler import * pbundler-0.0.4/PBundler/entrypoints.py0000644000000000000000000000026511772706005014707 0ustar from PBundler import PBCli import sys def pbcli(): sys.exit(PBCli().run(sys.argv)) def pbpy(): argv = [sys.argv[0], "py"] + sys.argv[1:] sys.exit(PBCli().run(argv)) pbundler-0.0.4/PBundler/pbundler.py0000644000000000000000000002212711773031666014132 0ustar #!/usr/bin/env python import os import sys import json import hashlib import fnmatch import time import pip.req from pip.exceptions import InstallationError import virtualenv # initialize vcs support pip.version_control() class PBFile: @staticmethod def read(path, filename): try: with open(os.path.join(path, filename), 'r') as f: return f.read() except Exception, e: return None @staticmethod def find_upwards(fn, root=os.path.realpath(os.curdir)): if os.path.exists(os.path.join(root, fn)): return root up = os.path.abspath(os.path.join(root, '..')) if up == root: return None return PBFile.find_upwards(fn, up) REQUIREMENTS = 'requirements.txt' REQUIREMENTS_LAST = 'requirements.last' class PBBasepathNotFound(Exception): pass class FakeOptionsClass(object): def __hasattr__(self, name): return True def __getattr__(self, name): return None class PBundle: def __init__(self, basepath): self.basepath = basepath self.workpath = os.path.join(self.basepath, ".pbundle") self.virtualenvpath = os.path.join(self.workpath, "virtualenv") self.ensure_paths() self.ensure_virtualenv() self._requirements = None self._requirements_last = None @staticmethod def find_basepath(): return PBFile.find_upwards(REQUIREMENTS) def ensure_paths(self): if not os.path.exists(self.workpath): os.mkdir(self.workpath) def ensure_virtualenv(self): if not os.path.exists(os.path.join(self.virtualenvpath, 'bin')): os.system("virtualenv " + self.virtualenvpath + " 2>&1") def ensure_relocatable(self): self.make_scripts_relocatable() virtualenv.fixup_pth_and_egg_link(self.virtualenvpath) def make_scripts_relocatable(self): shebang_pfx = '#!' new_shebang = '#!/usr/bin/env pbundle-py' if sys.platform == 'win32': bin_suffix = 'Scripts' else: bin_suffix = 'bin' bin_dir = os.path.join(self.virtualenvpath, bin_suffix) for filename in os.listdir(bin_dir): filename = os.path.join(bin_dir, filename) if not os.path.isfile(filename): # ignore subdirs, e.g. .svn ones. continue f = open(filename, 'rb') lines = f.readlines() f.close() if not lines: # Empty. continue line0 = lines[0].strip() if not line0.startswith(shebang_pfx): # Probably a binary. continue if not "python" in line0 and not "pbundle" in line0: # Has shebang prefix, but not a python script. # Better ignore it. continue if line0 == new_shebang: # Already patched, skip rewrite. continue lines = [new_shebang+'\n'] + lines[1:] f = open(filename, 'wb') f.writelines(lines) f.close() def _parse_requirements(self, filename): reqs = {} try: try: for req in pip.req.parse_requirements( os.path.join(self.basepath, filename), options=FakeOptionsClass()): reqs[req.name] = req except InstallationError, e: pass except Exception, e: import traceback traceback.print_exc(e) return reqs @property def requirements(self): if not self._requirements: self._requirements = \ self._parse_requirements(REQUIREMENTS) return self._requirements @property def requirements_last(self): if not self._requirements_last: self._requirements_last = \ self._parse_requirements(REQUIREMENTS_LAST) return self._requirements_last def requirements_changed(self): return self.requirements_last != self.requirements def save_requirements(self): with open(os.path.join(self.workpath, REQUIREMENTS_LAST), "w") as f: f.write("#pbundle %s, written %s\n" % (REQUIREMENTS_LAST, time.time())) for r in self.requirements.values(): f.write("%s\n" % r) def run(self, command, verbose=True): if verbose: print "Running \"%s\" ..." % (' '.join(command),) if 'PYTHONHOME' in os.environ: del os.environ['PYTHONHOME'] os.environ['VIRTUAL_ENV'] = self.virtualenvpath os.environ['PATH'] = (os.path.join(self.virtualenvpath, "bin") + ':' + os.environ['PATH']) for key, value in self.envfile().iteritems(): os.environ[key] = value try: os.execvp(command[0], command) except OSError, e: print e return 127 def envfile(self): ef = {} try: execfile(os.path.join(self.workpath, "environment.py"), {}, ef) except IOError, e: # ignore non-existence of environment.json pass except Exception, e: print 'environment.py: %s' % e return ef def _call_program(self, command, verbose=True, raise_on_error=True): cmdline = ' '.join(command) if verbose: print "Running \"%s\" ..." % (cmdline,) rc = os.system(". " + self.virtualenvpath + "/bin/activate; PBUNDLE_REQ='" + self.basepath + "'; " + cmdline) # Note: rc is not the real return code, but checking == 0 should be # good enough. if rc != 0 and raise_on_error: raise PBCliError("External command %r failed with exit code %d" % (cmdline, (rc&0xFF00)>>8)) def uninstall_removed(self): to_remove = set(self.requirements_last.keys()) - \ set(self.requirements.keys()) for p in to_remove: self._call_program(["pip", "uninstall", p], raise_on_error=False) def install(self): self._call_program(["pip", "install", "-r", os.path.join(self.basepath, REQUIREMENTS)]) self.ensure_relocatable() def upgrade(self): self._call_program(["pip", "install", "--upgrade", "-r", os.path.join(self.basepath, REQUIREMENTS)]) self.ensure_relocatable() class PBCliError(Exception): def __init__(self, message): Exception.__init__(self, message) USAGE = """ pbundle Copyright 2012 Christian Hofstaedtler pbundle Usage: pbundle [install] - Run pip, if needed (also uninstalls removed requirements) pbundle upgrade - Run pip, with --upgrade pbundle init - Create empty requirements.txt pbundle run program - Run "program" in activated virtualenv pbundle py args - Run activated python with args To auto-enable your scripts, use "#!/usr/bin/env pbundle-py" as the shebang line. Website: https://github.com/zeha/pbundler Report bugs: https://github.com/zeha/pbundler/issues """ class PBCli(): def __init__(self): self._pb = None @property def pb(self): if not self._pb: basepath = PBundle.find_basepath() if not basepath: message = ("Could not find requirements.txt " + "in path from here to root.") raise PBCliError(message) self._pb = PBundle(basepath) return self._pb def handle_args(self, argv): args = argv[1:] command = "install" if args: command = args.pop(0) if command in ['--help', '-h']: command = 'help' if 'cmd_' + command in PBCli.__dict__: return PBCli.__dict__['cmd_' + command](self, args) else: raise PBCliError("Unknown command \"%s\"" % (command,)) def run(self, argv): try: return self.handle_args(argv) except PBCliError, e: print "E: " + str(e) return 1 except Exception, e: print "E: Internal error in pbundler:" print " ", e return 120 def cmd_help(self, args): print USAGE.strip() def cmd_init(self, args): # can't use PBundle here if os.path.exists(REQUIREMENTS): raise PBCliError("Cowardly refusing, as %s already exists here." % (REQUIREMENTS,)) with open(REQUIREMENTS, "w") as f: f.write("# pbundle MAGIC\n") f.write("#pbundle>=0\n") f.write("\n") def cmd_install(self, args): if self.pb.requirements_changed(): self.pb.uninstall_removed() self.pb.install() self.pb.save_requirements() def cmd_upgrade(self, args): self.pb.uninstall_removed() self.pb.upgrade() def cmd_run(self, args): return self.pb.run(args, verbose=False) def cmd_py(self, args): return self.pb.run(["python"] + args, verbose=False) pbundler-0.0.4/LICENSE0000644000000000000000000000214411727444651011235 0ustar Copyright (c) 2011, 2012 Christian Hofstaedtler Contributors: Markus Hametner Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pbundler-0.0.4/setup.py0000755000000000000000000000147611773032440011742 0ustar #!/usr/bin/env python from setuptools import setup, find_packages setup( name="pbundler", version="0.0.4", packages=find_packages(), zip_safe=False, install_requires=['virtualenv>=1.7', 'distribute', 'pip'], package_data={ '': ['*.md'], }, entry_points={ 'console_scripts': [ 'pbundle = PBundler.entrypoints:pbcli', 'pbundle-py = PBundler.entrypoints:pbpy', ], }, # metadata for upload to PyPI author="Christian Hofstaedtler", author_email="ch--pypi@zeha.at", description="Bundler for Python", license="MIT", keywords="bundler bundle pbundler pbundle dependency dependencies management virtualenv pip packages", url="http://github.com/zeha/pbundler/", download_url="https://github.com/zeha/pbundler/downloads", ) pbundler-0.0.4/THANKS0000644000000000000000000000005511772706005011134 0ustar Markus Hametner Michael Prokop Ulrich Dangel pbundler-0.0.4/pbundler.egg-info/0000755000000000000000000000000011727510360013523 5ustar pbundler-0.0.4/pbundler.egg-info/entry_points.txt0000644000000000000000000000013711773032064017023 0ustar [console_scripts] pbundle-py = PBundler.entrypoints:pbpy pbundle = PBundler.entrypoints:pbcli pbundler-0.0.4/pbundler.egg-info/requires.txt0000644000000000000000000000003611773032064016123 0ustar virtualenv>=1.7 distribute pippbundler-0.0.4/pbundler.egg-info/SOURCES.txt0000644000000000000000000000045311773032064015412 0ustar setup.py PBundler/__init__.py PBundler/entrypoints.py PBundler/pbundler.py pbundler.egg-info/PKG-INFO pbundler.egg-info/SOURCES.txt pbundler.egg-info/dependency_links.txt pbundler.egg-info/entry_points.txt pbundler.egg-info/not-zip-safe pbundler.egg-info/requires.txt pbundler.egg-info/top_level.txtpbundler-0.0.4/pbundler.egg-info/PKG-INFO0000644000000000000000000000061411773032064014622 0ustar Metadata-Version: 1.1 Name: pbundler Version: 0.0.4 Summary: Bundler for Python Home-page: http://github.com/zeha/pbundler/ Author: Christian Hofstaedtler Author-email: ch--pypi@zeha.at License: MIT Download-URL: https://github.com/zeha/pbundler/downloads Description: UNKNOWN Keywords: bundler bundle pbundler pbundle dependency dependencies management virtualenv pip packages Platform: UNKNOWN pbundler-0.0.4/pbundler.egg-info/dependency_links.txt0000644000000000000000000000000111773032064017572 0ustar pbundler-0.0.4/pbundler.egg-info/top_level.txt0000644000000000000000000000001111773032064016246 0ustar PBundler pbundler-0.0.4/pbundler.egg-info/not-zip-safe0000644000000000000000000000000111727510360015751 0ustar pbundler-0.0.4/debian/0000755000000000000000000000000011773032467011450 5ustar pbundler-0.0.4/debian/pbundler.docs0000644000000000000000000000001211772706005014121 0ustar README.md pbundler-0.0.4/debian/source/0000755000000000000000000000000011772706005012743 5ustar pbundler-0.0.4/debian/source/format0000644000000000000000000000001511772706005014152 0ustar 3.0 (native) pbundler-0.0.4/debian/copyright0000644000000000000000000000325511772706005013403 0ustar Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: pbundler Upstream-Contact: Christian Hofstaedtler Source: http://www.github.com/zeha/pbundler Files: * Copyright: 2011-2012 Christian Hofstaedtler License: Expat Copyright (c) 2011, 2012 Christian Hofstaedtler Contributors: Markus Hametner . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: . The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. . THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Files: debian/* Copyright: 2012 Christian Hofstaedtler License: permissive Copying and distribution of this package, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. pbundler-0.0.4/debian/changelog0000644000000000000000000000064311773032440013314 0ustar pbundler (0.0.4) unstable; urgency=low * Bug fix release: * Add versioned dependency for virtualenv * Abort with non-zero exit code when pip failed -- Christian Hofstaedtler Thu, 28 Jun 2012 12:35:23 +0200 pbundler (0.0.3) unstable; urgency=low * Initial upload (Closes: #664122) -- Christian Hofstaedtler Mon, 19 Mar 2012 12:25:34 +0100 pbundler-0.0.4/debian/rules0000755000000000000000000000012411772706005012520 0ustar #!/usr/bin/make -f include /usr/share/python/python.mk %: dh $@ --with python2 pbundler-0.0.4/debian/control0000644000000000000000000000154411773032440013046 0ustar Source: pbundler Maintainer: Christian Hofstaedtler Section: python Priority: optional Build-Depends: python-setuptools (>= 0.6b3), python-all (>= 2.6.6-3), debhelper (>= 7.4.3), python-pip Standards-Version: 3.9.3 Homepage: https://github.com/zeha/pbundler DM-Upload-Allowed: yes Package: pbundler Architecture: all Depends: ${misc:Depends}, ${python:Depends}, python-pip, python-virtualenv (>= 1.7), python-setuptools (>= 0.6.24), python-pkg-resources Description: Bundler for Python - Dependency Management Automatically manages Python application's dependencies from development to deployment on top of virtualenv. . Independent of the various Python application frameworks, but should work with all of them. . The Python Bundler is inspired by Bundler for Ruby (AKA gembundler), which is available as the "bundler" package. pbundler-0.0.4/debian/compat0000644000000000000000000000000211772706005012641 0ustar 7 pbundler-0.0.4/README.md0000644000000000000000000000341711772706000011500 0ustar Python Bundler ============== Simplifies virtualenv and pip usage. Aims to be compatible with all existing projects already carrying a "requirements.txt" in their source. Inspired by http://gembundler.com/ Howto ----- * easy\_install pbundler * cd into your project path * Run pbundle. It will install your project's dependencies into a fresh virtualenv. To run commands with the activated virtualenv: pbundle run bash -c 'echo "I am activated. virtualenv: $VIRTUAL_ENV"' Or, for python programs: pbundle py ./debug.py If you don't have a requirements.txt yet but have an existing project, try this: pip freeze > requirements.txt If you start fresh, try this for a project setup: mkdir myproject && cd myproject git init pbundle init Instructions you can give your users: git clone git://github.com/you/yourproject.git easy_install pbundler pbundle Making python scripts automatically use pbundle py -------------------------------------------------- Replace the shebang with "/usr/bin/env pbundle-py". Example: #!/usr/bin/env pbundle-py import sys print sys.path WSGI/Unicorn example -------------------- start-unicorn.sh: #!/bin/bash cd /srv/app/flaskr PYTHONPATH=/srv/app/wsgi exec pbundle run gunicorn -w 5 -b 127.0.0.1:4000 -n flaskrprod flaskr:app Custom environment variables ---------------------------- If you need to set custom ENV variables for the executed commands in your local copy, do this: echo "DJANGO_SETTINGS_MODULE='mysite.settings'" >> .pbundle/environment.py TODO ---- * Build inventory from what is installed, instead of requirements.last file * Handle failed egg installs * Really remove all no longer needed packages from virtualenv * Get rid of os.system * Reorganize library code