mach-nix/mach_nix/requirements.py
DavHau a20691048e
version 2.2.0 (#60)
# 2.2.0 (09 Aug 2020)
Improved success rate, MacOS support, bugfixes, optimizations

### Features
 - Improved selection of wheel releases. MacOS is now supported and architectures besides x86_64 should be handled correctly.
 - Whenever mach-nix resolves dependencies, a visualization of the resulting dependency tree is printed on the terminal. 
 - The dependency DB is now accessed through a caching layer which reduces the resolver's CPU time significantly for larger environments.
 - The python platform context is now generated from the nix build environment variable `system`. This should decrease the chance of impurities during dependency resolution.
 
### Fixes
 - The requires_python attribute of wheels was not respected. This lead to failing builds especially for older python versions. Now `requires_python` is part of the dependency graph and affects resolution.
 - Detecting the correct package name for python packages in nixpkgs often failed since the attribute names don't follow a fixed schema. This lead to a handful of different errors in different situations. Now the package names are extracted from the pypi `url` inside the `src` attribute which is much more reliable. For packages which are not fetched from pypi, the `pname` attribute is used as fallback.
 - Fixed bug which lead to the error `attribute 'sdist' missing` if a package from the nixpkgs provider was used which doesn't publish it's source on pypi. (For example `tensorflow`)
 
### Other Changes
 - Mach-nix now uses a revision of the nixpkgs-unstable branch instead of nixos-20.03 as base fo the tool and the nixpkgs provider.
 - Updated revision of the dependency DB
2020-08-09 20:24:12 +07:00

70 lines
2.4 KiB
Python

from typing import Iterable
import distlib.markers
import pkg_resources
from distlib.markers import DEFAULT_CONTEXT
from packaging.version import parse, _Version
from pkg_resources._vendor.packaging.specifiers import SpecifierSet
from mach_nix.cache import cached
from mach_nix.versions import PyVer
def context(py_ver: PyVer, platform: str, system: str):
context = DEFAULT_CONTEXT.copy()
context.update(dict(
platform_version='', # remove impure platform_version
platform_release='', # remove impure kernel verison
platform_system=system[0].upper() + system[1:], # eg. Linux or Darwin
platform_machine=platform, # eg. x86_64
python_version=py_ver.python_version(),
python_full_version=py_ver.python_full_version()
))
return context
class Requirement(pkg_resources.Requirement):
def __init__(self, line):
super(Requirement, self).__init__(line)
self.name = self.name.lower().replace('_', '-')
self.specs = list(self.norm_specs(self.specs))
self.specifier = SpecifierSet(','.join(f"{op}{ver}" for op, ver in self.specs))
@staticmethod
def norm_specs(specs):
# PEP 440: Compatible Release
for spec in specs:
if spec[0] == "~=":
ver = spec[1]
yield ('>=', ver)
ver = parse(parse(ver).base_version)
ver_as_dict = ver._version._asdict()
ver_as_dict['release'] = ver_as_dict['release'][:-1] + ('*',)
ver._version = _Version(**ver_as_dict)
yield ('==', str(ver))
else:
yield spec
def filter_reqs_by_eval_marker(reqs: Iterable[Requirement], context: dict, selected_extras=None):
# filter requirements relevant for current environment
for req in reqs:
if req.marker is None:
yield req
elif selected_extras:
for extra in selected_extras:
extra_context = context.copy()
extra_context['extra'] = extra
if distlib.markers.interpret(str(req.marker), extra_context):
yield req
else:
if distlib.markers.interpret(str(req.marker), context):
yield req
@cached(lambda args: tuple(args[0]) if isinstance(args[0], list) else args[0])
def parse_reqs(strs):
reqs = list(pkg_resources.parse_requirements(strs))
for req in reqs:
r = Requirement(str(req))
yield r