Skip to content

Commit

Permalink
Start working on support for applocal builds of Python 3.5+
Browse files Browse the repository at this point in the history
  • Loading branch information
takluyver committed Jun 18, 2015
1 parent f519674 commit 0b79a6f
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 5 deletions.
37 changes: 33 additions & 4 deletions nsist/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from subprocess import call
import sys
import fnmatch
import zipfile

PY2 = sys.version_info[0] == 2

Expand All @@ -24,7 +25,7 @@

from .copymodules import copy_modules
from .nsiswriter import NSISFileWriter
from .util import download, text_types
from .util import download, text_types, get_cache_dir

__version__ = '1.5'

Expand Down Expand Up @@ -81,7 +82,8 @@ class InstallerBuilder(object):
"""
def __init__(self, appname, version, shortcuts, icon=DEFAULT_ICON,
packages=None, extra_files=None, py_version=DEFAULT_PY_VERSION,
py_bitness=DEFAULT_BITNESS, build_dir=DEFAULT_BUILD_DIR,
py_bitness=DEFAULT_BITNESS, py_format='installer',
build_dir=DEFAULT_BUILD_DIR,
installer_name=None, nsi_template=None,
exclude=None):
self.appname = appname
Expand All @@ -97,6 +99,9 @@ def __init__(self, appname, version, shortcuts, icon=DEFAULT_ICON,
self.py_bitness = py_bitness
if py_bitness not in {32, 64}:
raise InputError('py_bitness', py_bitness, "32 or 64")
self.py_format = py_format
if py_format not in {'installer', 'bundled'}:
raise InputError('py_format', py_format, "installer or bundled")
self.build_dir = build_dir
self.installer_name = installer_name or self.make_installer_name()
self.nsi_template = nsi_template
Expand Down Expand Up @@ -139,6 +144,29 @@ def fetch_python(self):
logger.info('Downloading Python MSI...')
download(url, target)

def fetch_python_embeddable(self):
arch_tag = 'amd64' if (self.py_bitness==64) else 'win32'
filename = 'python-{}-embed-{}.zip'.format(self.py_version, arch_tag)
url = 'https://www.python.org/ftp/python/{}/{}'.format(self.py_version,
filename)
cache_file = get_cache_dir(ensure_existence=True) / filename
if not cache_file.is_file():
logger.info('Downloading embeddable Python build...')
download(url, cache_file)

logger.info('Unpacking Python...')
python_dir = pjoin(self.build_dir, 'Python')
try:
shutil.rmtree(python_dir)
except OSError as e:
if e.errno != errno.ENOENT:
raise

with zipfile.ZipFile(cache_file) as z:
z.extractall(python_dir)

self.install_dirs.append(python_dir)

def fetch_pylauncher(self):
"""Fetch the MSI for PyLauncher (required for Python2.x).
Expand Down Expand Up @@ -387,9 +415,9 @@ def main(argv=None):
dirname, config_file = os.path.split(options.config_file)
if dirname:
os.chdir(dirname)


from . import configreader
try:
from . import configreader
cfg = configreader.read_and_validate(config_file)
shortcuts = configreader.read_shortcuts_config(cfg)
except configreader.InvalidConfig as e:
Expand All @@ -408,6 +436,7 @@ def main(argv=None):
extra_files = configreader.read_extra_files(cfg),
py_version = cfg.get('Python', 'version', fallback=DEFAULT_PY_VERSION),
py_bitness = cfg.getint('Python', 'bitness', fallback=DEFAULT_BITNESS),
py_format = cfg.get('Python', 'format', fallback='installer'),
build_dir = cfg.get('Build', 'directory', fallback=DEFAULT_BUILD_DIR),
installer_name = cfg.get('Build', 'installer_name', fallback=None),
nsi_template = cfg.get('Build', 'nsi_template', fallback=None),
Expand Down
1 change: 1 addition & 0 deletions nsist/configreader.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ def _check_invalid_keys(self, section_name, section):
'Python': SectionValidator([
('version', True),
('bitness', False),
('format', False),
]),
'Shortcut': SectionValidator([
('entry_point', False),
Expand Down
30 changes: 29 additions & 1 deletion nsist/util.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import os
import errno
from pathlib import Path
import requests
import sys

Expand All @@ -15,9 +18,34 @@ def download(url, target):
This is like urllib.request.urlretrieve, but requests validates SSL
certificates by default.
"""
if isinstance(target, Path):
target = str(target)
r = requests.get(url, stream=True)
r.raise_for_status()
with open(target, 'wb') as f:
for chunk in r.iter_content(chunk_size=1024):
if chunk:
f.write(chunk)
f.write(chunk)

def get_cache_dir(ensure_existence=False):
if os.name == 'posix' and sys.platform != 'darwin':
# Linux, Unix, AIX, etc.
# use ~/.cache if empty OR not set
xdg = os.environ.get("XDG_CACHE_HOME", None) or (os.path.expanduser('~/.cache'))
p = Path(xdg, 'pynsist')

elif sys.platform == 'darwin':
p = Path(os.path.expanduser('~'), 'Library/Caches/flit')

else:
# Windows (hopefully)
local = os.environ.get('LOCALAPPDATA', None) or (os.path.expanduser('~\\AppData\\Local'))
p = Path(local, 'flit')

if ensure_existence:
try:
p.mkdir(parents=True)
except OSError as e:
# Py2 compatible equivalent of FileExistsError
if e.errno != errno.EEXIST:
raise
3 changes: 3 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
if PY2:
requirements.append('configparser >= 3.3.0r2')

if sys.version_info < (3, 4):
requirements.append('pathlib')

with open('README.rst', 'r') as f:
readme=f.read()

Expand Down

0 comments on commit 0b79a6f

Please sign in to comment.