Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

--constraints in pex #335

Merged
merged 6 commits into from
Jan 11, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions pex/bin/pex.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,16 @@ def configure_clp():
help='Add requirements from the given requirements file. This option can be used multiple '
'times.')

parser.add_option(
'--constraints',
dest='constraint_files',
metavar='FILE',
default=[],
type=str,
action='append',
help='Add requirements from the given requirements file. This option can be used multiple '
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this read Add constraints from the given constraints file instead?

'times.')

parser.add_option(
'-v',
dest='verbosity',
Expand Down Expand Up @@ -477,6 +487,13 @@ def build_pex(args, options, resolver_option_builder):
for requirements_txt in options.requirement_files:
resolvables.extend(requirements_from_file(requirements_txt, resolver_option_builder))

for constraints_txt in options.constraint_files:
constraints = []
for r in requirements_from_file(constraints_txt, resolver_option_builder):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pip states the constraints format is identical to requirements:

https://pip.pypa.io/en/stable/user_guide/#constraints-files

so I figured the way to guarantee that in pex was to share the requirements code.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems like a good tidbit to put into a comment

r.is_constraint = True
constraints.append(r)
resolvables.extend(constraints)

resolver_kwargs = dict(interpreter=interpreter, platform=options.platform)

if options.cache_dir:
Expand Down
10 changes: 10 additions & 0 deletions pex/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,16 @@ def compatible(self, identity, platform=Platform.current()):
return True
return False

def __eq__(self, other):
return (self._name == other._name and
self._raw_version == other._raw_version and
self._py_tag == other._py_tag and
self._abi_tag == other._abi_tag and
self._arch_tag == other._arch_tag)

def __hash__(self):
return hash((self._name, self._raw_version, self._py_tag, self._abi_tag, self._arch_tag))


Package.register(SourcePackage)
Package.register(EggPackage)
Expand Down
2 changes: 2 additions & 0 deletions pex/resolvable.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ class InvalidRequirement(Error): pass

_REGISTRY = []

is_constraint = False

@classmethod
def register(cls, implementation):
"""Register an implementation of a Resolvable.
Expand Down
21 changes: 13 additions & 8 deletions pex/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,19 @@ def iter(self, req):
yield package


class _ResolvedPackages(namedtuple('_ResolvedPackages', 'resolvable packages parent')):
class _ResolvedPackages(namedtuple('_ResolvedPackages', 'resolvable packages parent constraint_only')):
@classmethod
def empty(cls):
return cls(None, OrderedSet(), None)
return cls(None, OrderedSet(), None, False)

def merge(self, other):
if other.resolvable is None:
return _ResolvedPackages(self.resolvable, self.packages, self.parent)
return _ResolvedPackages(self.resolvable, self.packages, self.parent, self.constraint_only)
return _ResolvedPackages(
self.resolvable,
self.packages & other.packages,
self.parent)
self.parent,
self.constraint_only and other.constraint_only)


class _ResolvableSet(object):
Expand Down Expand Up @@ -103,12 +104,12 @@ def _check(self):

def merge(self, resolvable, packages, parent=None):
"""Add a resolvable and its resolved packages."""
self.__tuples.append(_ResolvedPackages(resolvable, OrderedSet(packages), parent))
self.__tuples.append(_ResolvedPackages(resolvable, OrderedSet(packages), parent, resolvable.is_constraint))
self._check()

def get(self, name):
"""Get the set of compatible packages given a resolvable name."""
resolvable, packages, parent = self._collapse().get(
resolvable, packages, parent, constraint_only = self._collapse().get(
self.normalize(name), _ResolvedPackages.empty())
return packages

Expand All @@ -129,7 +130,7 @@ def replace_built(self, built_packages):
"""
def map_packages(resolved_packages):
packages = OrderedSet(built_packages.get(p, p) for p in resolved_packages.packages)
return _ResolvedPackages(resolved_packages.resolvable, packages, resolved_packages.parent)
return _ResolvedPackages(resolved_packages.resolvable, packages, resolved_packages.parent, resolved_packages.constraint_only)

return _ResolvableSet([map_packages(rp) for rp in self.__tuples])

Expand All @@ -139,6 +140,8 @@ class Resolver(object):

class Error(Exception): pass

is_constraint = False

@classmethod
def filter_packages_by_interpreter(cls, packages, interpreter, platform):
return [package for package in packages
Expand Down Expand Up @@ -188,7 +191,9 @@ def resolve(self, resolvables, resolvable_set=None):
processed_resolvables.add(resolvable)

built_packages = {}
for resolvable, packages, parent in resolvable_set.packages():
for resolvable, packages, parent, constraint_only in resolvable_set.packages():
if constraint_only:
continue
assert len(packages) > 0, 'ResolvableSet.packages(%s) should not be empty' % resolvable
package = next(iter(packages))
if resolvable.name in processed_packages:
Expand Down