script.py 4.83 KB
Newer Older
1
2
3
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
# Andre Anjos <andre.dos.anjos@gmail.com>
4
# Mon  4 Feb 14:12:24 2013
5
6
7
8
9

"""Builds custom scripts with the right paths for external dependencies
installed on different prefixes.
"""

André Anjos's avatar
André Anjos committed
10
import os
11
import sys
André Anjos's avatar
André Anjos committed
12
import time
13
import logging
14
15
from zc.recipe.egg import Scripts

16
from . import tools
17
from .envwrapper import EnvironmentWrapper
18
19

import zc.buildout.easy_install
20

André Anjos's avatar
André Anjos committed
21
22
# Monkey patches the default template for script generation
zc.buildout.easy_install.script_template = \
23
    zc.buildout.easy_install.script_header + """ -S
André Anjos's avatar
André Anjos committed
24
25
26
27
28
29
30
31
32
# Automatically generated on %(date)s

'''Runs a specific user program'''

%%(relative_paths_setup)s
import sys
sys.path[0:0] = [
  %%(path)s,
  ]
André Anjos's avatar
André Anjos committed
33
34
35
36

#see: http://bugs.python.org/issue30167
if sys.version_info[:2] >= (3, 6) and \
    sys.modules['__main__'].__loader__ is not None:
37
38
  _hack = str(sys.modules['__main__'].__loader__.__module__)
  sys.modules['__main__'].__loader__.__module__ += '_'
39
import site #initializes site properly
40
site.main() #this is required for python>=3.4
André Anjos's avatar
André Anjos committed
41
42
43
if sys.version_info[:2] >= (3, 6) and \
    sys.modules['__main__'].__loader__ is not None:
  #restores original value
44
  sys.modules['__main__'].__loader__.__module__ = _hack
45
import pkg_resources #initializes virtualenvs properly
André Anjos's avatar
André Anjos committed
46
47
48
49
50
51
52
%%(initialization)s
import %%(module_name)s

if __name__ == '__main__':
    sys.exit(%%(module_name)s.%%(attrs)s(%%(arguments)s))
""" % {'date': time.asctime()}

53
54
class ScriptGenerator(object):
  """Replaces the default script generator so paths are properly filtered"""
55

56
  def __init__(self, buildout, prefixes):
57

58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
    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)
74

75
76
77
78
79
    # 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
80

81
82
83
84
85
86
87
88
89
    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,
        )
90

91
    return zc.buildout.easy_install._create_script(contents, dest)
André Anjos's avatar
André Anjos committed
92

93
94
95
96
97
98
99
100
101
102
103
104
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)

André Anjos's avatar
André Anjos committed
105
    # Gets a personalized eggs list or the one from buildout
106
    self.eggs = tools.eggs(buildout['buildout'], options, name)
107

André Anjos's avatar
André Anjos committed
108
    # Gets a personalized prefixes list or the one from buildout
André Anjos's avatar
André Anjos committed
109
110
    self.prefixes = tools.get_prefixes(buildout['buildout'])
    self.user_paths = tools.find_site_packages(self.prefixes)
André Anjos's avatar
André Anjos committed
111

112
113
    # Builds an environment wrapper, in case dependent packages need to be
    # compiled
114
    self.envwrapper = EnvironmentWrapper(self.logger,
André Anjos's avatar
André Anjos committed
115
        tools.debug(buildout['buildout']), self.prefixes)
116
117

    # initializes the script infrastructure
118
    super(Recipe, self).__init__(buildout, name, options)
119
120
121
122
123
124
125
126
127

  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)

128
129
    if tools.offline(self.buildout['buildout']):

130
      ws = tools.working_set(self.buildout['buildout'])
131
132
133
134
      ws = tools.filter_working_set_hard(ws, distributions)

    else:

135
      ws = tools.working_set(self.buildout['buildout'])
136
137

      if tools.newest(self.buildout['buildout']):
138
139
140
141

        for d in distributions:
          tools.install_package(self.buildout['buildout'], d, ws)

142
143
      else: #only installs packages which are not yet installed

144
        _ws, to_install = tools.filter_working_set_soft(ws, distributions)
145
146
        for d in to_install:
          tools.install_package(self.buildout['buildout'], d, ws)
André Anjos's avatar
André Anjos committed
147

148
149
      ws = tools.filter_working_set_hard(ws, distributions)

150
151
    return self.eggs, ws

André Anjos's avatar
André Anjos committed
152
  def install_on_wrapped_env(self):
153
154
    with ScriptGenerator(self.buildout, self.prefixes) as sg:
      return tuple(super(Recipe, self).install())
André Anjos's avatar
André Anjos committed
155

156
  def install(self):
157
158
    with self.envwrapper as ew:
      return self.install_on_wrapped_env()
159
160

  update = install