Repository URL to install this package:
|
Version:
1.26.0.dev0+gite506aa5f ▾
|
# Copyright 2014 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).
import os
from pex.pex_info import PexInfo
from twitter.common.collections import maybe_list
from pants.backend.python.targets.python_target import PythonTarget
from pants.base.exceptions import TargetDefinitionException
from pants.base.payload import Payload
from pants.base.payload_field import PrimitiveField
from pants.subsystem.subsystem import Subsystem
class PythonBinary(PythonTarget):
"""A Python binary.
Python binaries are pex files, self-contained executable shell
scripts that contain a complete Python environment capable of
running the target. For more information about pex files see
http://pantsbuild.github.io/python-readme.html#how-pex-files-work.
:API: public
"""
@classmethod
def alias(cls):
return "python_binary"
class Defaults(Subsystem):
options_scope = "python-binary"
@classmethod
def register_options(cls, register):
super(PythonBinary.Defaults, cls).register_options(register)
register(
"--pex-emit-warnings",
advanced=True,
type=bool,
default=True,
fingerprint=True,
help="Whether built pex binaries should emit pex warnings at runtime by default. "
"Can be over-ridden by specifying the `emit_warnings` parameter of individual "
"`{}` targets".format(PythonBinary.alias()),
)
@classmethod
def should_emit_warnings(cls, override=None):
return (
override
if override is not None
else cls.global_instance().options.pex_emit_warnings
)
@classmethod
def subsystems(cls):
return super().subsystems() + (cls.Defaults,)
# TODO(wickman) Consider splitting pex options out into a separate PexInfo builder that can be
# attached to the binary target. Ideally the PythonBinary target is agnostic about pex mechanics
def __init__(
self,
sources=None,
entry_point=None,
inherit_path=False, # pex option
zip_safe=True, # pex option
always_write_cache=False, # pex option
repositories=None, # pex option
indices=None, # pex option
ignore_errors=False, # pex option
shebang=None, # pex option
emit_warnings=None, # pex option
platforms=(),
**kwargs
):
"""
:param string entry_point: the default entry point for this binary. if None, drops into the entry
point that is defined by source. Something like
"pants.bin.pants_exe:main", where "pants.bin.pants_exe" is the package
name and "main" is the function name (if omitted, the module is
executed directly, presuming it has a ``__main.py__``).
:param sources: Zero or one source files. If more than one file is required, it should be put in
a python_library which should be added to dependencies.
:param inherit_path: inherit the sys.path of the environment that this binary runs in
:param zip_safe: whether or not this binary is safe to run in compacted (zip-file) form
:param always_write_cache: whether or not the .deps cache of this PEX file should always
be written to disk.
:param repositories: a list of repositories to query for dependencies.
:param indices: a list of indices to use for packages.
:param ignore_errors: should we ignore inability to resolve dependencies?
:param str shebang: Use this shebang for the generated pex.
:param bool emit_warnings: Whether or not to emit pex warnings.
:param platforms: extra platforms to target when building this binary. If this is, e.g.,
``['current', 'linux-x86_64', 'macosx-10.4-x86_64']``, then when building the pex, then
for any platform-dependent modules, Pants will include ``egg``\\s for Linux (64-bit Intel),
Mac OS X (version 10.4 or newer), and the current platform (whatever is being used when
making the PEX).
"""
if inherit_path is False:
inherit_path = "false"
payload = Payload()
payload.add_fields(
{
"entry_point": PrimitiveField(entry_point),
"inherit_path": PrimitiveField(inherit_path),
"zip_safe": PrimitiveField(bool(zip_safe)),
"always_write_cache": PrimitiveField(bool(always_write_cache)),
"repositories": PrimitiveField(maybe_list(repositories or [])),
"indices": PrimitiveField(maybe_list(indices or [])),
"ignore_errors": PrimitiveField(bool(ignore_errors)),
"platforms": PrimitiveField(tuple(maybe_list(platforms or []))),
"shebang": PrimitiveField(shebang),
"emit_warnings": PrimitiveField(self.Defaults.should_emit_warnings(emit_warnings)),
}
)
super().__init__(sources=sources, payload=payload, **kwargs)
if (not sources or not sources.files) and entry_point is None:
raise TargetDefinitionException(
self, "A python binary target must specify either a single source or entry_point."
)
if not isinstance(platforms, (list, tuple)) and not isinstance(platforms, str):
raise TargetDefinitionException(self, "platforms must be a list, tuple or str.")
if sources and sources.files and entry_point:
entry_point_module = entry_point.split(":", 1)[0]
entry_source = list(self.sources_relative_to_source_root())[0]
source_entry_point = self.translate_source_path_to_py_module_specifier(entry_source)
if entry_point_module != source_entry_point:
raise TargetDefinitionException(
self,
"Specified both source and entry_point but they do not agree: {} vs {}".format(
source_entry_point, entry_point_module
),
)
@property
def platforms(self):
return self.payload.platforms
# TODO(wickman) These should likely be attributes on PythonLibrary targets
# and not PythonBinary targets, or at the very worst, both.
@property
def repositories(self):
return self.payload.repositories
@property
def indices(self):
return self.payload.indices
@classmethod
def translate_source_path_to_py_module_specifier(self, source: str) -> str:
source_base, _ = os.path.splitext(source)
return source_base.replace(os.path.sep, ".")
@property
def entry_point(self):
if self.payload.entry_point:
return self.payload.entry_point
elif self.payload.sources.source_paths:
assert len(self.payload.sources.source_paths) == 1
entry_source = list(self.sources_relative_to_source_root())[0]
return self.translate_source_path_to_py_module_specifier(entry_source)
else:
return None
@property
def shebang(self):
return self.payload.shebang
@property
def pexinfo(self):
info = PexInfo.default()
for repo in self.repositories:
info.add_repository(repo)
for index in self.indices:
info.add_index(index)
info.zip_safe = self.payload.zip_safe
info.always_write_cache = self.payload.always_write_cache
info.inherit_path = self.payload.inherit_path
info.entry_point = self.entry_point
info.ignore_errors = self.payload.ignore_errors
info.emit_warnings = self.payload.emit_warnings
return info