Skip to content

Provide extra_index_urls in config #256

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

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
6 changes: 5 additions & 1 deletion nsist/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ class InstallerBuilder(object):
:param list pypi_wheel_reqs: Package specifications to fetch from PyPI as wheels
:param extra_wheel_sources: Directory paths to find wheels in.
:type extra_wheel_sources: list of Path objects
:param extra_index_urls: URLs to alternative package indexes to find wheels.
:type extra_index_urls: list of str
:param local_wheels: Glob paths matching wheel files to include
:type local_wheels: list of str
:param list extra_files: List of 2-tuples (file, destination) of files to include
Expand All @@ -109,7 +111,7 @@ def __init__(self, appname, version, shortcuts, *, publisher=None,
py_format='bundled', inc_msvcrt=True, build_dir=DEFAULT_BUILD_DIR,
installer_name=None, nsi_template=None,
exclude=None, pypi_wheel_reqs=None, extra_wheel_sources=None,
local_wheels=None, commands=None, license_file=None):
extra_index_urls=None, local_wheels=None, commands=None, license_file=None):
self.appname = appname
self.version = version
self.publisher = publisher
Expand All @@ -120,6 +122,7 @@ def __init__(self, appname, version, shortcuts, *, publisher=None,
self.extra_files = extra_files or []
self.pypi_wheel_reqs = pypi_wheel_reqs or []
self.extra_wheel_sources = extra_wheel_sources or []
self.extra_index_urls = extra_index_urls or []
self.local_wheels = local_wheels or []
self.commands = commands or {}
self.license_file = license_file
Expand Down Expand Up @@ -355,6 +358,7 @@ def prepare_packages(self):
wg = WheelGetter(self.pypi_wheel_reqs, self.local_wheels, build_pkg_dir,
py_version=self.py_version, bitness=self.py_bitness,
extra_sources=self.extra_wheel_sources,
extra_indexes=self.extra_index_urls,
exclude=self.exclude)
wg.get_all()

Expand Down
2 changes: 2 additions & 0 deletions nsist/configreader.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ def _check_invalid_keys(self, section_name, section):
('packages', False),
('pypi_wheels', False),
('extra_wheel_sources', False),
('extra_index_urls', False),
('files', False),
('exclude', False),
('local_wheels', False)
Expand Down Expand Up @@ -230,6 +231,7 @@ def get_installer_builder_args(config):
args['extra_wheel_sources'] = [Path(p) for p in
config.get('Include', 'extra_wheel_sources', fallback='').strip().splitlines()
]
args['extra_index_urls'] = config.get('Include', 'extra_index_urls', fallback='').strip().splitlines()
args['extra_files'] = read_extra_files(config)
args['py_version'] = config.get('Python', 'version', fallback=DEFAULT_PY_VERSION)
args['py_bitness'] = config.getint('Python', 'bitness', fallback=DEFAULT_BITNESS)
Expand Down
50 changes: 44 additions & 6 deletions nsist/wheels.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,11 @@ def is_compatible(self, whl_filename: str) -> bool:
return self.score(whl_filename) > 0

class WheelLocator(object):
def __init__(self, requirement, scorer, extra_sources=None):
def __init__(self, requirement, scorer, extra_sources=None, extra_indexes=None):
self.requirement = requirement
self.scorer = scorer
self.extra_sources = extra_sources or []
self.extra_indexes = extra_indexes or []

if requirement.count('==') != 1:
raise ValueError("Requirement {!r} did not match name==version".format(requirement))
Expand Down Expand Up @@ -122,17 +123,45 @@ def check_cache(self):

return release_dir / rel.filename

def get_from_extra_index_urls(self):
"""Find a compatible wheel in the specified extra_sources directories.

Returns a Path or None.
"""
for url in self.extra_indexes:
try:
return self.get_from_package_index(index_url=url)
except NoWheelError:
continue

return None

def get_from_pypi(self):
"""Download a compatible wheel from PyPI.

Downloads to the cache directory and returns the destination as a Path.
Raises NoWheelError if no compatible wheel is found.
"""
return self.get_from_package_index()

def get_from_package_index(self, index_url=None):
"""Download a compatible wheel from a package index at 'index_url'.

Downloads to the cache directory and returns the destination as a Path.
Raises NoWheelError if no compatible wheel is found.

The 'index_url' defaults to PyPI (https://pypi.python.org/pypi/).
When supplying another package index, it must support the PyPI protocol
and provide a json description (such as https://pypi.org/pypi/numpy/json)!
A simple directory listing won't do.
"""
if index_url is None:
index_url = 'https://pypi.python.org/pypi/'
try:
pypi_pkg = yarg.get(self.name)
pypi_pkg = yarg.get(self.name, index_url)
except yarg.HTTPError as e:
if e.status_code == 404:
raise NoWheelError("No package named {} found on PyPI".format(self.name))
raise NoWheelError("No package named {0} found on {1}".format(self.name, index_url))
raise

release_list = pypi_pkg.release(self.version)
Expand Down Expand Up @@ -165,7 +194,10 @@ def get_from_pypi(self):
return target

def fetch(self):
"""Find and return a compatible wheel (main interface)"""
"""Find and return a compatible wheel (main interface).

Checks extra sources first, cache second and PyPI as last resort.
"""
p = self.check_extra_sources()
if p is not None:
logger.info('Using wheel from extra directory: %s', p)
Expand All @@ -176,6 +208,11 @@ def fetch(self):
logger.info('Using cached wheel: %s', p)
return p

p = self.get_from_extra_index_urls()
if p is not None:
logger.info('Using wheel from package index: %s', p)
return p

return self.get_from_pypi()


Expand Down Expand Up @@ -277,13 +314,14 @@ def extract_wheel(whl_file, target_dir, exclude=None):

class WheelGetter:
def __init__(self, requirements, wheel_globs, target_dir,
py_version, bitness, extra_sources=None, exclude=None):
py_version, bitness, extra_sources=None, extra_indexes=None, exclude=None):
self.requirements = requirements
self.wheel_globs = wheel_globs
self.target_dir = target_dir
target_platform = 'win_amd64' if bitness == 64 else 'win32'
self.scorer = CompatibilityScorer(py_version, target_platform)
self.extra_sources = extra_sources
self.extra_indexes = extra_indexes
self.exclude = exclude

self.got_distributions = {}
Expand All @@ -294,7 +332,7 @@ def get_all(self):

def get_requirements(self):
for req in self.requirements:
wl = WheelLocator(req, self.scorer, self.extra_sources)
wl = WheelLocator(req, self.scorer, self.extra_sources, self.extra_indexes)
whl_file = wl.fetch()
extract_wheel(whl_file, self.target_dir, exclude=self.exclude)
self.got_distributions[wl.name] = whl_file
Expand Down