~ubuntu-branches/ubuntu/vivid/python-pex/vivid

« back to all changes in this revision

Viewing changes to pex/pex_info.py

  • Committer: Package Import Robot
  • Author(s): Barry Warsaw
  • Date: 2015-02-19 14:13:25 UTC
  • Revision ID: package-import@ubuntu.com-20150219141325-w62bie95l6rawuuv
Tags: upstream-0.8.6
ImportĀ upstreamĀ versionĀ 0.8.6

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2014 Pants project contributors (see CONTRIBUTORS.md).
 
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
 
3
 
 
4
from __future__ import absolute_import, print_function
 
5
 
 
6
import json
 
7
import os
 
8
import sys
 
9
import warnings
 
10
from collections import namedtuple
 
11
 
 
12
from .common import open_zip
 
13
from .compatibility import string as compatibility_string
 
14
from .orderedset import OrderedSet
 
15
 
 
16
PexPlatform = namedtuple('PexPlatform', 'interpreter version strict')
 
17
 
 
18
 
 
19
class PexInfo(object):
 
20
  """PEX metadata.
 
21
 
 
22
  # Build metadata:
 
23
  build_properties: BuildProperties # (key-value information about the build system)
 
24
  code_hash: str                    # sha1 hash of all names/code in the archive
 
25
  distributions: {dist_name: str}   # map from distribution name (i.e. path in
 
26
                                    # the internal cache) to its cache key (sha1)
 
27
 
 
28
  # Environment options
 
29
  pex_root: ~/.pex                   # root of all pex-related files
 
30
  entry_point: string                # entry point into this pex
 
31
  zip_safe: True, default False      # is this pex zip safe?
 
32
  inherit_path: True, default False  # should this pex inherit site-packages + PYTHONPATH?
 
33
  ignore_errors: True, default False # should we ignore inability to resolve dependencies?
 
34
  always_write_cache: False          # should we always write the internal cache to disk first?
 
35
                                     # this is useful if you have very large dependencies that
 
36
                                     # do not fit in RAM constrained environments
 
37
  requirements: list                 # list of requirements for this environment
 
38
 
 
39
 
 
40
  .. versionchanged:: 0.8
 
41
    Removed the ``repositories`` and ``indices`` information, as they were never
 
42
    implemented.
 
43
  """
 
44
 
 
45
  PATH = 'PEX-INFO'
 
46
  INTERNAL_CACHE = '.deps'
 
47
 
 
48
  @classmethod
 
49
  def make_build_properties(cls):
 
50
    from .interpreter import PythonInterpreter
 
51
    from pkg_resources import get_platform
 
52
 
 
53
    pi = PythonInterpreter.get()
 
54
    return {
 
55
      'class': pi.identity.interpreter,
 
56
      'version': pi.identity.version,
 
57
      'platform': get_platform(),
 
58
    }
 
59
 
 
60
  @classmethod
 
61
  def default(cls):
 
62
    pex_info = {
 
63
      'requirements': [],
 
64
      'distributions': {},
 
65
      'always_write_cache': False,
 
66
      'build_properties': cls.make_build_properties(),
 
67
    }
 
68
    return cls(info=pex_info)
 
69
 
 
70
  @classmethod
 
71
  def from_pex(cls, pex):
 
72
    if os.path.isfile(pex):
 
73
      with open_zip(pex) as zf:
 
74
        pex_info = zf.read(cls.PATH)
 
75
    else:
 
76
      with open(os.path.join(pex, cls.PATH)) as fp:
 
77
        pex_info = fp.read()
 
78
    return cls.from_json(pex_info)
 
79
 
 
80
  @classmethod
 
81
  def from_json(cls, content):
 
82
    if isinstance(content, bytes):
 
83
      content = content.decode('utf-8')
 
84
    return PexInfo(info=json.loads(content))
 
85
 
 
86
  @classmethod
 
87
  def _parse_requirement_tuple(cls, requirement_tuple):
 
88
    if isinstance(requirement_tuple, (tuple, list)):
 
89
      if len(requirement_tuple) != 3:
 
90
        raise ValueError('Malformed PEX requirement: %r' % (requirement_tuple,))
 
91
      # pre 0.8.x requirement type:
 
92
      warnings.warn('Attempting to use deprecated PEX feature.  Please upgrade past PEX 0.8.x.')
 
93
      return requirement_tuple[0]
 
94
    elif isinstance(requirement_tuple, compatibility_string):
 
95
      return requirement_tuple
 
96
    raise ValueError('Malformed PEX requirement: %r' % (requirement_tuple,))
 
97
 
 
98
  @classmethod
 
99
  def debug(cls, msg):
 
100
    if 'PEX_VERBOSE' in os.environ:
 
101
      print('PEX: %s' % msg, file=sys.stderr)
 
102
 
 
103
  def __init__(self, info=None):
 
104
    """Construct a new PexInfo.  This should not be used directly."""
 
105
 
 
106
    if info is not None and not isinstance(info, dict):
 
107
      raise ValueError('PexInfo can only be seeded with a dict, got: '
 
108
                       '%s of type %s' % (info, type(info)))
 
109
    self._pex_info = info or {}
 
110
    self._distributions = self._pex_info.get('distributions', {})
 
111
    requirements = self._pex_info.get('requirements', [])
 
112
    if not isinstance(requirements, (list, tuple)):
 
113
      raise ValueError('Expected requirements to be a list, got %s' % type(requirements))
 
114
    self._requirements = OrderedSet(self._parse_requirement_tuple(req) for req in requirements)
 
115
 
 
116
  @property
 
117
  def build_properties(self):
 
118
    """Information about the system on which this PEX was generated.
 
119
 
 
120
    :returns: A dictionary containing metadata about the environment used to build this PEX.
 
121
    """
 
122
    return self._pex_info.get('build_properties', {})
 
123
 
 
124
  @build_properties.setter
 
125
  def build_properties(self, value):
 
126
    if not isinstance(value, dict):
 
127
      raise TypeError('build_properties must be a dictionary!')
 
128
    self._pex_info['build_properties'] = self.make_build_properties()
 
129
    self._pex_info['build_properties'].update(value)
 
130
 
 
131
  @property
 
132
  def zip_safe(self):
 
133
    """Whether or not this PEX should be treated as zip-safe.
 
134
 
 
135
    If set to false and the PEX is zipped, the contents of the PEX will be unpacked into a
 
136
    directory within the PEX_ROOT prior to execution.  This allows code and frameworks depending
 
137
    upon __file__ existing on disk to operate normally.
 
138
 
 
139
    By default zip_safe is True.  May be overridden at runtime by the $PEX_FORCE_LOCAL environment
 
140
    variable.
 
141
    """
 
142
    if 'PEX_FORCE_LOCAL' in os.environ:
 
143
      self.debug('PEX_FORCE_LOCAL forcing zip_safe to False')
 
144
      return False
 
145
    return self._pex_info.get('zip_safe', True)
 
146
 
 
147
  @zip_safe.setter
 
148
  def zip_safe(self, value):
 
149
    self._pex_info['zip_safe'] = bool(value)
 
150
 
 
151
  @property
 
152
  def inherit_path(self):
 
153
    """Whether or not this PEX should be allowed to inherit system dependencies.
 
154
 
 
155
    By default, PEX environments are scrubbed of all system distributions prior to execution.
 
156
    This means that PEX files cannot rely upon preexisting system libraries.
 
157
 
 
158
    By default inherit_path is False.  This may be overridden at runtime by the $PEX_INHERIT_PATH
 
159
    environment variable.
 
160
    """
 
161
    if 'PEX_INHERIT_PATH' in os.environ:
 
162
      self.debug('PEX_INHERIT_PATH override detected')
 
163
      return True
 
164
    else:
 
165
      return self._pex_info.get('inherit_path', False)
 
166
 
 
167
  @inherit_path.setter
 
168
  def inherit_path(self, value):
 
169
    self._pex_info['inherit_path'] = bool(value)
 
170
 
 
171
  @property
 
172
  def ignore_errors(self):
 
173
    return self._pex_info.get('ignore_errors', False)
 
174
 
 
175
  @ignore_errors.setter
 
176
  def ignore_errors(self, value):
 
177
    self._pex_info['ignore_errors'] = bool(value)
 
178
 
 
179
  @property
 
180
  def code_hash(self):
 
181
    return self._pex_info.get('code_hash')
 
182
 
 
183
  @code_hash.setter
 
184
  def code_hash(self, value):
 
185
    self._pex_info['code_hash'] = value
 
186
 
 
187
  @property
 
188
  def entry_point(self):
 
189
    if 'PEX_MODULE' in os.environ:
 
190
      self.debug('PEX_MODULE override detected: %s' % os.environ['PEX_MODULE'])
 
191
      return os.environ['PEX_MODULE']
 
192
    return self._pex_info.get('entry_point')
 
193
 
 
194
  @entry_point.setter
 
195
  def entry_point(self, value):
 
196
    self._pex_info['entry_point'] = value
 
197
 
 
198
  def add_requirement(self, requirement):
 
199
    self._requirements.add(str(requirement))
 
200
 
 
201
  @property
 
202
  def requirements(self):
 
203
    return self._requirements
 
204
 
 
205
  def add_distribution(self, location, sha):
 
206
    self._distributions[location] = sha
 
207
 
 
208
  @property
 
209
  def distributions(self):
 
210
    return self._distributions
 
211
 
 
212
  @property
 
213
  def always_write_cache(self):
 
214
    if 'PEX_ALWAYS_CACHE' in os.environ:
 
215
      self.debug('PEX_ALWAYS_CACHE override detected: %s' % os.environ['PEX_ALWAYS_CACHE'])
 
216
      return True
 
217
    return self._pex_info.get('always_write_cache', False)
 
218
 
 
219
  @always_write_cache.setter
 
220
  def always_write_cache(self, value):
 
221
    self._pex_info['always_write_cache'] = bool(value)
 
222
 
 
223
  @property
 
224
  def pex_root(self):
 
225
    pex_root = self._pex_info.get('pex_root', os.path.join('~', '.pex'))
 
226
    return os.path.expanduser(os.environ.get('PEX_ROOT', pex_root))
 
227
 
 
228
  @pex_root.setter
 
229
  def pex_root(self, value):
 
230
    self._pex_info['pex_root'] = value
 
231
 
 
232
  @property
 
233
  def internal_cache(self):
 
234
    return self.INTERNAL_CACHE
 
235
 
 
236
  @property
 
237
  def install_cache(self):
 
238
    return os.path.join(self.pex_root, 'install')
 
239
 
 
240
  @property
 
241
  def zip_unsafe_cache(self):
 
242
    return os.path.join(self.pex_root, 'code')
 
243
 
 
244
  def dump(self):
 
245
    pex_info_copy = self._pex_info.copy()
 
246
    pex_info_copy['requirements'] = list(self._requirements)
 
247
    return json.dumps(pex_info_copy)
 
248
 
 
249
  def copy(self):
 
250
    return PexInfo(info=self._pex_info.copy())