#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
# Andre Anjos <andre.dos.anjos@gmail.com>
# Mon 4 Feb 14:12:24 2013
"""Builds custom scripts with the right paths for external dependencies
installed on different prefixes.
"""
import os
import sys
import time
import logging
from zc.recipe.egg import Scripts
from . import tools
from .envwrapper import EnvironmentWrapper
import zc.buildout.easy_install
# Monkey patches the default template for script generation
zc.buildout.easy_install.script_template = \
zc.buildout.easy_install.script_header + """ -S
# Automatically generated on %(date)s
'''Runs a specific user program'''
%%(relative_paths_setup)s
import sys
sys.path[0:0] = [
%%(path)s,
]
#see: http://bugs.python.org/issue30167
if sys.version_info[:2] >= (3, 6) and \
sys.modules['__main__'].__loader__ is not None:
_hack = str(sys.modules['__main__'].__loader__.__module__)
sys.modules['__main__'].__loader__.__module__ += '_'
import site #initializes site properly
site.main() #this is required for python>=3.4
if sys.version_info[:2] >= (3, 6) and \
sys.modules['__main__'].__loader__ is not None:
#restores original value
sys.modules['__main__'].__loader__.__module__ = _hack
import pkg_resources #initializes virtualenvs properly
%%(initialization)s
import %%(module_name)s
if __name__ == '__main__':
sys.exit(%%(module_name)s.%%(attrs)s(%%(arguments)s))
""" % {'date': time.asctime()}
[docs]class ScriptGenerator(object):
"""Replaces the default script generator so paths are properly filtered"""
def __init__(self, buildout, prefixes):
self.buildout = buildout
self.prefixes = prefixes
def __enter__(self):
self.__old__ = zc.buildout.easy_install._script
zc.buildout.easy_install._script = self
def __exit__(self, *exc_details):
zc.buildout.easy_install._script = self.__old__
def __call__(self, module_name, attrs, path, dest, arguments, initialization, rsetup):
"""Default script generator"""
if zc.buildout.easy_install.is_win32: dest += '-script.py'
python = zc.buildout.easy_install._safe_arg(sys.executable)
# the "difference": re-order python paths with a preference for locals
realpath = [k.strip().strip("'").strip('"') for k in path.split(",\n")]
realpath = [os.path.realpath(k.strip()) for k in realpath if k.strip()]
path = ",\n ".join(["'%s'" % k for k in realpath if k not in tools.site_paths(self.buildout['buildout'], self.prefixes)])
if not path: path = "''" #dummy path
contents = zc.buildout.easy_install.script_template % dict(
python = python,
path = path,
module_name = module_name,
attrs = attrs,
arguments = arguments,
initialization = initialization,
relative_paths_setup = rsetup,
)
return zc.buildout.easy_install._create_script(contents, dest)
[docs]class Recipe(Scripts):
"""Just creates a given script with the "correct" paths
"""
def __init__(self, buildout, name, options):
self.buildout = buildout
self.name = name
self.options = options
self.logger = logging.getLogger(self.name)
# Gets a personalized eggs list or the one from buildout
self.eggs = tools.eggs(buildout['buildout'], options, name)
# Gets a personalized prefixes list or the one from buildout
self.prefixes = tools.get_prefixes(buildout['buildout'])
self.user_paths = tools.find_site_packages(self.prefixes)
# Builds an environment wrapper, in case dependent packages need to be
# compiled
self.envwrapper = EnvironmentWrapper(self.logger,
tools.debug(buildout['buildout']), self.prefixes)
# initializes the script infrastructure
super(Recipe, self).__init__(buildout, name, options)
[docs] def working_set(self, extra=()):
"""Separate method to just get the working set - overriding zc.recipe.egg
This is intended for reuse by similar recipes.
"""
distributions = self.eggs + list(extra)
if tools.offline(self.buildout['buildout']):
ws = tools.working_set(self.buildout['buildout'])
ws = tools.filter_working_set_hard(ws, distributions)
else:
ws = tools.working_set(self.buildout['buildout'])
if tools.newest(self.buildout['buildout']):
for d in distributions:
tools.install_package(self.buildout['buildout'], d, ws)
else: #only installs packages which are not yet installed
_ws, to_install = tools.filter_working_set_soft(ws, distributions)
for d in to_install:
tools.install_package(self.buildout['buildout'], d, ws)
ws = tools.filter_working_set_hard(ws, distributions)
return self.eggs, ws
[docs] def install_on_wrapped_env(self):
with ScriptGenerator(self.buildout, self.prefixes) as sg:
return tuple(super(Recipe, self).install())
[docs] def install(self):
with self.envwrapper as ew:
return self.install_on_wrapped_env()
update = install