Browse Source

Merge pull request #131 from takluyver/drop-installer-mode

Drop installer mode
Thomas Kluyver 7 years ago
parent
commit
25e4715f0f

+ 3 - 0
.gitignore

@@ -5,3 +5,6 @@ MANIFEST
 dist/
 dist/
 doc/_build
 doc/_build
 pynsist_pkgs/
 pynsist_pkgs/
+
+# This is a big file, so let it sit here after download
+examples/pygi_mpl_numpy/pygi.exe

+ 0 - 5
.travis.yml

@@ -2,16 +2,11 @@ language: python
 python:
 python:
   - "3.6"
   - "3.6"
   - "3.5"
   - "3.5"
-  - "3.4"
-  - "3.3"
-  - "2.7"
 # command to run tests
 # command to run tests
 script: nosetests
 script: nosetests
 # Ensure dependencies are installed
 # Ensure dependencies are installed
 install:
 install:
   - pip install requests requests_download jinja2 yarg win_cli_launchers testpath
   - pip install requests requests_download jinja2 yarg win_cli_launchers testpath
-  - if [[ ${TRAVIS_PYTHON_VERSION} == '2.7' ]]; then pip install configparser pathlib2; fi
-  - if [[ ${TRAVIS_PYTHON_VERSION} == '3.3' ]]; then pip install pathlib2; fi
 
 
 # Enable new Travis stack, should speed up builds
 # Enable new Travis stack, should speed up builds
 sudo: false
 sudo: false

+ 1 - 1
README.rst

@@ -25,7 +25,7 @@ Quickstart
        icon=myapp.ico
        icon=myapp.ico
 
 
        [Python]
        [Python]
-       version=3.4.0
+       version=3.6.3
 
 
        [Include]
        [Include]
        # Importable packages that your application requires, one per line
        # Importable packages that your application requires, one per line

+ 10 - 46
doc/cfgfile.rst

@@ -39,7 +39,7 @@ Application section
 
 
    Ensure that this boilerplate code is at the top of your script::
    Ensure that this boilerplate code is at the top of your script::
 
 
-       #!python3.3
+       #!python3.6
        import sys
        import sys
        sys.path.insert(0, 'pkgs')
        sys.path.insert(0, 'pkgs')
 
 
@@ -146,8 +146,9 @@ Python section
 
 
 .. describe:: version
 .. describe:: version
 
 
-  The Python version to download and bundle with your application, e.g. ``3.4.3``.
-  Python 3.3 or later and 2.7 are supported.
+  The Python version to download and bundle with your application, e.g. ``3.6.3``.
+  Python 3.5 or later are supported. For older versions of Python, use Pynsist
+  1.x.
 
 
 .. describe:: bitness (optional)
 .. describe:: bitness (optional)
 
 
@@ -155,22 +156,9 @@ Python section
   defaults to the version you're using, so that compiled modules will match. On
   defaults to the version you're using, so that compiled modules will match. On
   other platforms, it defaults to 32-bit.
   other platforms, it defaults to 32-bit.
 
 
-.. describe:: format (optional)
-
-  - ``installer`` includes a copy of the Python MSI installer in your application
-    and runs it at install time, setting up Python systemwide. This is the
-    default for Python up to 3.5.
-  - ``bundled`` includes an embeddable Python build, which will be installed as
-    part of your application. This is available for Python 3.5 and above, and is
-    the default for Python 3.6 and above.
-
-  .. versionchanged:: 1.9
-
-     The default switched to ``bundled`` for Python 3.6 and above.
-
 .. describe:: include_msvcrt (optional)
 .. describe:: include_msvcrt (optional)
 
 
-  This option is only relevant with ``format = bundled``. The default is ``true``,
+  The default is ``true``,
   which will include an app-local copy of the Microsoft Visual C++ Runtime,
   which will include an app-local copy of the Microsoft Visual C++ Runtime,
   required for Python to run. The installer will only install this if it doesn't
   required for Python to run. The installer will only install this if it doesn't
   detect a system installation of the runtime.
   detect a system installation of the runtime.
@@ -187,35 +175,11 @@ Python section
 
 
   .. versionadded:: 1.9
   .. versionadded:: 1.9
 
 
-.. _python_bundled:
-
-Bundled Python
-~~~~~~~~~~~~~~
-
-.. versionadded:: 1.6
-   Support for bundling Python into the application.
-
-Using ``format = bundled``, an embeddable Python build will be downloaded at
-build time and packaged along with the application. When the installer runs, it
-will create a ``Python`` subfolder inside the install directory with the files
-Python needs to run.
-
-This has the advantage of producing smaller, quicker installers (~7.5 MB for a
-trivial application), and more standalone installations. But it has a number of
-limitations:
-
-- This option is only available for Python 3.5 and above. These versions of
-  Python have dropped support for Windows XP, so your application will only work
-  on Windows Vista and newer.
-- Installing in Windows Vista to 8.1 (inclusive) may install an app-local copy
-  of the Visual C++ runtime (see above). This isn't
-  needed on Windows 10, which includes the necessary files.
-- The embeddable Python builds don't include ``tkinter``, to save space.
-  Applications with a tkinter GUI can't easily use bundled Python. Workarounds
-  may be found in the future.
-- The user cannot easily install extra Python packages in the application's
-  Python. If your application has plugins based on Python packages, this might
-  require extra thought about how and where plugins are installed.
+.. note::
+
+   Pynsist 1.x also included a ``format=`` option to select between two ways to
+   use Python: *bundled* or *installer*. Pynsist 2 only supports *bundled*
+   Python. For the installer option, use Pynsist 1.x.
 
 
 .. _cfg_include:
 .. _cfg_include:
 
 

+ 10 - 0
doc/releasenotes.rst

@@ -1,6 +1,16 @@
 Release notes
 Release notes
 =============
 =============
 
 
+Version 2.0
+-----------
+
+Pynsist 2 only supports 'bundled' Python, and therefore only Python 3.5 and
+above. For 'installer' format Python and older Python versions, use Pynsist 1.x
+(``pip install pynsist<2``).
+
+* Pynsist installers can now install into a per-user directory, allowing them
+  to be used without admin access.
+
 Version 1.12
 Version 1.12
 ------------
 ------------
 
 

+ 1 - 2
examples/pygi_mpl_numpy/1_download.sh

@@ -1,4 +1,3 @@
 # Download the necessary files
 # Download the necessary files
 # Python-GI bindings, Matplotlib (64-bit, Python 3.4)
 # Python-GI bindings, Matplotlib (64-bit, Python 3.4)
-wget -O pygi.exe http://sourceforge.net/projects/pygobjectwin32/files/pygi-aio-3.14.0_rev21-setup.exe
-wget -O matplotlib.exe http://downloads.sourceforge.net/project/matplotlib/matplotlib/matplotlib-1.4.3/windows/matplotlib-1.4.3.win-amd64-py3.4.exe
+wget -O pygi.exe https://sourceforge.net/projects/pygobjectwin32/files/pygi-aio-3.24.1_rev1-setup_049a323fe25432b10f7e9f543b74598d4be74a39.exe

+ 2 - 8
examples/pygi_mpl_numpy/2_extract.sh

@@ -1,20 +1,14 @@
 # Extracts all dependencies and places them in the pynsist_pkgs folder
 # Extracts all dependencies and places them in the pynsist_pkgs folder
 # You might need to rename the "7z" calls to "7za" depending on your distribution
 # You might need to rename the "7z" calls to "7za" depending on your distribution
+set -e
 
 
 mkdir pynsist_pkgs
 mkdir pynsist_pkgs
 
 
 # Unzip the bindings
 # Unzip the bindings
-7z x numpy.whl -onumpy
-7z x matplotlib.exe -omatplotlib
 7z x pygi.exe -opygi
 7z x pygi.exe -opygi
 
 
-# Copy matplotlib and numpy into the pynsist_pkgs folder and delete the folders
-cp -r matplotlib/PLATLIB/matplotlib pynsist_pkgs
-rm -r matplotlib
-cp -r numpy/numpy pynsist_pkgs
-rm -r numpy
-
 # Copy the PyGI packages into the pynsist_pkgs folder
 # Copy the PyGI packages into the pynsist_pkgs folder
+# TODO: Update to Python 3.6
 7z x pygi/binding/py3.4-64/py3.4-64.7z -obindings
 7z x pygi/binding/py3.4-64/py3.4-64.7z -obindings
 cp -r bindings/* pynsist_pkgs
 cp -r bindings/* pynsist_pkgs
 rm -r bindings
 rm -r bindings

+ 9 - 9
examples/pygi_mpl_numpy/README.rst

@@ -1,4 +1,9 @@
-This example shows how to package a program that uses the PyGI-bindings of Gtk (or PyGObject). Python 3.4.3 64-bit will be used together with 64-bit dependencies.
+**This example does not currently work**: Pynsist 2 requires Python 3.5 or above,
+but PyGI is only available for Python 3.4 at most (as of October 2017).
+Hopefully it will be possible again in the future.
+
+This example shows how to package a program that uses the PyGI-bindings of Gtk (or PyGObject).
+Python 3.4.3 64-bit will be used together with 64-bit dependencies.
 
 
 The example program consists of a window with a matplotlib-plot and a button that triggers the window to close.
 The example program consists of a window with a matplotlib-plot and a button that triggers the window to close.
 
 
@@ -15,19 +20,14 @@ Debian-style distributions using:
 Building the program
 Building the program
 --------------------
 --------------------
 
 
-A shell script can be used to download some of the dependencies:
+A shell script can be used to download the PyGI Windows installer:
 
 
 ::
 ::
 
 
     sh 1_download.sh
     sh 1_download.sh
 
 
-The numpy 64-bit wheel can be downloaded here (numpy‑1.9.2+mkl‑cp34‑none‑win_amd64.whl):
-
-http://www.lfd.uci.edu/~gohlke/pythonlibs/#numpy
-
-Rename it to numpy.whl before starting the second script.
-
-The next script will copy all the dependencies into the ``pynsist_pkgs`` folder.
+The next script will extract the necessary GTK components into the
+``pynsist_pkgs`` folder.
 
 
 ::
 ::
 
 

+ 8 - 7
examples/pygi_mpl_numpy/installer.cfg

@@ -5,17 +5,18 @@ entry_point=pygi_test:main
 extra_preamble=gnome_preamble.py
 extra_preamble=gnome_preamble.py
 
 
 [Python]
 [Python]
-version=3.4.3
+version=3.6.3
 bitness=64
 bitness=64
 
 
 [Include]
 [Include]
+pypi_wheels = numpy==1.13.3
+    matplotlib==2.1.0
+    six==1.11.0
+    python-dateutil==2.6.1
+    pyparsing==2.2.0
+
 packages=gi
 packages=gi
     cairo
     cairo
-    dbus
+    #dbus  # needed?
     gnome
     gnome
     pygtkcompat
     pygtkcompat
-    numpy
-    matplotlib
-    six
-    dateutil
-    pyparsing

+ 0 - 21
examples/pygtk/README.rst

@@ -1,21 +0,0 @@
-This is an example of building a Windows installer for a pygtk application. This
-is a bit more complex than the other examples, because the GTK runtime needs to
-be set up. This needs two things:
-
-1. The pieces of the GTK runtime and its Python bindings. The script ``grab_files.sh``
-   downloads these, unpacks them, trims out unnecessary pieces, and places them
-   where pynsist will find them.
-2. The ``PATH`` environment variable must be modified before we try to import
-   the Python GTK bindings. This is done by the ``extra_preamble`` field in
-   ``installer.cfg``.
-
-I referred to the following sources of information to work this out:
-
-Bundling pygtk using py2exe:
-http://faq.pygtk.org/index.py?file=faq21.005.htp&req=show
-https://web.archive.org/web/20060208162511/http://www.anti-particle.com/py2exe-0.5.shtml
-
-Installing pygtk & deps: http://www.pygtk.org/downloads.html
-(inc links for pygtk, pycairo and pygobject installers)
-
-GTK bundles for Windows: http://www.gtk.org/download/win32.php

+ 0 - 40
examples/pygtk/grab_files.sh

@@ -1,40 +0,0 @@
-# Download the necessary files
-wget -O gtkbundle.zip http://ftp.gnome.org/pub/gnome/binaries/win32/gtk+/2.24/gtk+-bundle_2.24.10-20120208_win32.zip
-wget -O pygobject.exe http://ftp.gnome.org/pub/GNOME/binaries/win32/pygobject/2.28/pygobject-2.28.3.win32-py2.7.exe
-wget -O pycairo.exe http://ftp.gnome.org/pub/GNOME/binaries/win32/pycairo/1.8/pycairo-1.8.10.win32-py2.7.exe
-wget -O pygtk.exe http://ftp.gnome.org/pub/GNOME/binaries/win32/pygtk/2.24/pygtk-2.24.0.win32-py2.7.exe
-
-# GTK runtime
-mkdir gtkbundle
-unzip -d gtkbundle gtkbundle.zip
-cd gtkbundle
-rm -r src man include share/doc share/man share/gtk-doc share/gtk-2.0/demo bin/gtk-demo.exe etc/bash_completion.d
-cd ..
-
-# Python bindings
-mkdir pygobject
-unzip -d pygobject pygobject.exe
-mkdir pycairo
-unzip -d pycairo pycairo.exe
-mkdir pygtk
-unzip -d pygtk pygtk.exe
-
-# Reassemble into pynsist_pkgs
-echo -n "Assembling GTK files into pynsist_pkgs... "
-rm -r pynsist_pkgs
-mkdir pynsist_pkgs
-mv gtkbundle pynsist_pkgs/gtk
-
-cp -r pygobject/PLATLIB/* pynsist_pkgs
-rm -r pygobject
-
-cp -r pycairo/PLATLIB/* pynsist_pkgs
-cp -r pycairo/DATA/lib/site-packages/cairo/* pynsist_pkgs/cairo
-rm -r pycairo
-
-cp -r pygtk/PLATLIB/* pynsist_pkgs
-rm -r pygtk
-
-rm -r pynsist_pkgs/gtk-2.0/tests
-
-echo "done"

+ 0 - 2
examples/pygtk/gtk_preamble.py

@@ -1,2 +0,0 @@
-os.environ['PATH'] += os.pathsep + os.path.join(pkgdir, 'gtk/lib') + \
-    os.pathsep + os.path.join(pkgdir, 'gtk/bin')

+ 0 - 84
examples/pygtk/helloworld.py

@@ -1,84 +0,0 @@
-#!/usr/bin/env python
-
-# This example was adapted from http://pygtk.org/pygtk2tutorial/examples/helloworld.py
-
-import pygtk
-pygtk.require('2.0')
-import gtk
-
-class HelloWorld:
-    def msgbox(self, text):
-        "Display a simple message box"
-        md = gtk.MessageDialog(self.window, gtk.DIALOG_DESTROY_WITH_PARENT,
-                          gtk.MESSAGE_INFO, gtk.BUTTONS_CLOSE, text)
-        md.run()
-
-    # This is a callback function. The data arguments are ignored
-    # in this example. More on callbacks below.
-    def hello(self, widget, data=None):
-        self.msgbox("Hello, world!")
-
-    def delete_event(self, widget, event, data=None):
-        # If you return FALSE in the "delete_event" signal handler,
-        # GTK will emit the "destroy" signal. Returning TRUE means
-        # you don't want the window to be destroyed.
-        # This is useful for popping up 'are you sure you want to quit?'
-        # type dialogs.
-        self.msgbox("delete event occurred")
-
-        # Change FALSE to TRUE and the main window will not be destroyed
-        # with a "delete_event".
-        return False
-
-    def destroy(self, widget, data=None):
-        gtk.main_quit()
-
-    def __init__(self):
-        # create a new window
-        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
-    
-        # When the window is given the "delete_event" signal (this is given
-        # by the window manager, usually by the "close" option, or on the
-        # titlebar), we ask it to call the delete_event () function
-        # as defined above. The data passed to the callback
-        # function is NULL and is ignored in the callback function.
-        self.window.connect("delete_event", self.delete_event)
-    
-        # Here we connect the "destroy" event to a signal handler.  
-        # This event occurs when we call gtk_widget_destroy() on the window,
-        # or if we return FALSE in the "delete_event" callback.
-        self.window.connect("destroy", self.destroy)
-    
-        # Sets the border width of the window.
-        self.window.set_border_width(10)
-    
-        # Creates a new button with the label "Hello World".
-        self.button = gtk.Button("Hello World")
-    
-        # When the button receives the "clicked" signal, it will call the
-        # function hello() passing it None as its argument.  The hello()
-        # function is defined above.
-        self.button.connect("clicked", self.hello, None)
-    
-        # This will cause the window to be destroyed by calling
-        # gtk_widget_destroy(window) when "clicked".  Again, the destroy
-        # signal could come from here, or the window manager.
-        self.button.connect_object("clicked", gtk.Widget.destroy, self.window)
-    
-        # This packs the button into the window (a GTK container).
-        self.window.add(self.button)
-    
-        # The final step is to display this newly created widget.
-        self.button.show()
-    
-        # and the window
-        self.window.show()
-
-    def main(self):
-        # All PyGTK applications must have a gtk.main(). Control ends here
-        # and waits for an event to occur (like a key press or mouse event).
-        gtk.main()
-
-def main():
-    hello = HelloWorld()
-    hello.main()

+ 0 - 11
examples/pygtk/installer.cfg

@@ -1,11 +0,0 @@
-[Application]
-name=Hello World (PyGTK)
-version=1.0
-entry_point=helloworld:main
-extra_preamble=gtk_preamble.py
-
-[Python]
-version=2.7.7
-
-[Include]
-packages=pygtk

+ 0 - 101
examples/pygtk_mpl_numpy/README.rst

@@ -1,101 +0,0 @@
-This is an example of building a Windows installer for a pygtk application that
-includes matplotlib and numpy.
-
-Requirements
-------------
-
-This example needs 7zip in order to work. You can install it for example on
-Debian-style distributions using:
-
-::
-
-    sudo apt-get install p7zip
-
-Running the Example
--------------------
-
-In order to build the example application on Linux you have to run the following
-two commands in the example directory:
-
-::
-
-    sh grab_files.sh
-    python -m nsist installer.cfg
-
-The first line will download the dependencies, extract them and copy them into
-the :code:`pynsist_pkgs`-directory. It will then remove the temporary directories
-used for extraction, but will leave the downloaded archives intact in the
-example directory.
-
-Matplotlib
-----------
-
-The example downloads the 32-bit Python 2.7 bindings of Matplotlib 1.4.3
-(matplotlib-1.4.3.win32-py2.7.exe).
-
-In the :code:`installer.cfg` Matplotlib additionally requires the six, dateutil
-and pyparsing packages:
-
-::
-
-    [Include]
-    packages=pygtk
-        numpy
-        matplotlib
-        six
-        dateutil
-        pyparsing
-
-Numpy
------
-
-The example downloads the 32-bit Python 2.7 bindings of Numpy 1.9.2
-(numpy-1.9.2-win32-superpack-python2.7.exe).
-
-PyGTK
------
-
-PyGTK is a bit more complex than the other examples, because the GTK runtime
-needs to be set up. This needs two things:
-
-1. The pieces of the GTK runtime and its Python bindings. The script ``grab_files.sh``
-   downloads these, unpacks them, trims out unnecessary pieces, and places them
-   where pynsist will find them.
-2. The ``PATH`` environment variable must be modified before we try to import
-   the Python GTK bindings. This is done by the ``extra_preamble`` field in
-   ``installer.cfg``.
-
-I referred to the following sources of information to work this out:
-
-Bundling pygtk using py2exe:
-http://faq.pygtk.org/index.py?file=faq21.005.htp&req=show
-https://web.archive.org/web/20060208162511/http://www.anti-particle.com/py2exe-0.5.shtml
-
-Installing pygtk & deps: http://www.pygtk.org/downloads.html
-(inc links for pygtk, pycairo and pygobject installers)
-
-GTK bundles for Windows: http://www.gtk.org/download/win32.php
-
-Installer.cfg
--------------
-
-The example is customized for 32-bit and Python 2.7. This is expressed in the
-:code:`installer.cfg`-file like this:
-
-::
-
-    version=2.7.9
-    bitness=32
-
-The include section requires pygtk, numpy and matplotlib. In order to satisfy the
-requirements of Matplotlib the packages six, dateutil, and pyparsing are needed.
-
-::
-
-    [Include]
-    packages=pygtk
-        numpy
-        matplotlib
-        six
-        dateutil
-        pyparsing

+ 0 - 56
examples/pygtk_mpl_numpy/grab_files.sh

@@ -1,56 +0,0 @@
-# Download the necessary files
-wget -O gtkbundle.zip http://ftp.gnome.org/pub/gnome/binaries/win32/gtk+/2.24/gtk+-bundle_2.24.10-20120208_win32.zip
-wget -O pygobject.exe http://ftp.gnome.org/pub/GNOME/binaries/win32/pygobject/2.28/pygobject-2.28.3.win32-py2.7.exe
-wget -O pycairo.exe http://ftp.gnome.org/pub/GNOME/binaries/win32/pycairo/1.8/pycairo-1.8.10.win32-py2.7.exe
-wget -O pygtk.exe http://ftp.gnome.org/pub/GNOME/binaries/win32/pygtk/2.24/pygtk-2.24.0.win32-py2.7.exe
-wget -O numpy.exe http://downloads.sourceforge.net/project/numpy/NumPy/1.9.2/numpy-1.9.2-win32-superpack-python2.7.exe
-wget -O matplotlib.exe https://downloads.sourceforge.net/project/matplotlib/matplotlib/matplotlib-1.4.3/windows/matplotlib-1.4.3.win32-py2.7.exe
-
-# GTK runtime
-mkdir gtkbundle
-unzip -d gtkbundle gtkbundle.zip
-cd gtkbundle
-rm -r src man include share/doc share/man share/gtk-doc share/gtk-2.0/demo bin/gtk-demo.exe etc/bash_completion.d
-cd ..
-
-# Python bindings
-mkdir pygobject
-unzip -d pygobject pygobject.exe
-mkdir pycairo
-unzip -d pycairo pycairo.exe
-mkdir pygtk
-unzip -d pygtk pygtk.exe
-mkdir numpy
-unzip -d numpy numpy.exe
-mkdir matplotlib
-unzip -d matplotlib matplotlib.exe
-
-# Reassemble into pynsist_pkgs
-echo -n "Assembling GTK files into pynsist_pkgs... "
-rm -r pynsist_pkgs
-mkdir pynsist_pkgs
-mv gtkbundle pynsist_pkgs/gtk
-
-cp -r pygobject/PLATLIB/* pynsist_pkgs
-rm -r pygobject
-
-cp -r pycairo/PLATLIB/* pynsist_pkgs
-cp -r pycairo/DATA/lib/site-packages/cairo/* pynsist_pkgs/cairo
-rm -r pycairo
-
-cp -r pygtk/PLATLIB/* pynsist_pkgs
-rm -r pygtk
-
-cp -r matplotlib/PLATLIB/* pynsist_pkgs
-rm -r matplotlib
-
-#Unzip numpy into base directory
-7z e numpy.exe
-#Unzip the NoSSE package into the numpy directory
-7z x numpy-1.9.2-nosse.exe -onumpy
-cp -r numpy/PLATLIB/* pynsist_pkgs
-rm -r numpy
-
-rm -r pynsist_pkgs/gtk-2.0/tests
-
-echo "done"

+ 0 - 2
examples/pygtk_mpl_numpy/gtk_preamble.py

@@ -1,2 +0,0 @@
-os.environ['PATH'] += os.pathsep + os.path.join(pkgdir, 'gtk/lib') + \
-    os.pathsep + os.path.join(pkgdir, 'gtk/bin')

+ 0 - 42
examples/pygtk_mpl_numpy/hellomatplotlib.py

@@ -1,42 +0,0 @@
-#!/usr/bin/env python
-
-# This example was adapted from http://matplotlib.org/examples/user_interfaces/embedding_in_gtk.html, and http://pygtk.org/pygtk2tutorial/examples/helloworld.py
-
-import pygtk
-pygtk.require('2.0')
-import gtk
-from matplotlib.figure import Figure
-from numpy import arange, sin, pi
-from matplotlib.backends.backend_gtkagg import FigureCanvasGTKAgg as FigureCanvas
-
-class HelloMatplotlib:
-    def __init__(self):
-        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
-        self.window.connect("delete_event", self.delete_event)
-        self.window.connect("destroy", self.destroy)
-        self.window.set_size_request(400, 400)
-        self.window.set_border_width(10)
-
-        f = Figure(figsize=(5,4), dpi=100)
-        a = f.add_subplot(111)
-        t = arange(0.0,3.0,0.01)
-        s = sin(2*pi*t)
-        a.plot(t,s)
-
-        self.canvas = FigureCanvas(f)
-        self.canvas.show()
-        self.window.add(self.canvas)
-        self.window.show()
-
-    def delete_event(self, widget, event, data=None):
-        gtk.main_quit()
-
-    def destroy(self, widget, data=None):
-        gtk.main_quit()
-
-    def main(self):
-        gtk.main()
-
-def main():
-    hello = HelloMatplotlib()
-    hello.main()

+ 0 - 17
examples/pygtk_mpl_numpy/installer.cfg

@@ -1,17 +0,0 @@
-[Application]
-name=Hello Matplotlib (PyGTK)
-version=1.0
-entry_point=hellomatplotlib:main
-extra_preamble=gtk_preamble.py
-
-[Python]
-version=2.7.9
-bitness=32
-
-[Include]
-packages=pygtk
-    numpy
-    matplotlib
-    six
-    dateutil
-    pyparsing

+ 0 - 14
examples/pyqt4/README.md

@@ -1,14 +0,0 @@
-This is an example that uses PyQt4 binary packages.
-
-To make the installer on a non-Windows system, first run fetch_pyqt_windows.sh.
-This will download a PyQt Windows installer from Sourceforge, unpack the files
-from it, and copy the necessary ones into pynsist_pkgs where pynsist will
-pick them up.
-
-If you want to use PyQt in a '[bundled format](https://pynsist.readthedocs.io/en/latest/cfgfile.html#bundled-python)'
-installer with Python 3.5 or later, you'll need to ensure the file `msvcp140.dll`
-is included. If you have a Visual Studio installation, you can find it in there;
-otherwise download the [Visual C++ Redistributable](https://www.microsoft.com/en-us/download/details.aspx?id=48145).
-On Linux, you can extract the DLL from the exe using `cabextract` (after extracting
-files from the exe, run it again on a file called `a10`). Place `msvcp140.dll`
-inside the `PyQt4` folder, next to files like `QtCore4.dll`.

+ 0 - 31
examples/pyqt4/fetch_pyqt_windows.sh

@@ -1,31 +0,0 @@
-#!/usr/bin/env bash
-# Download and the PyQt4 Windows installer and unpack files from it into
-# pynsist_pkgs
-
-set -e
-
-PY_VERSION=3.4
-PYQT_VERSION=4.11.3
-QT_VERSION=4.8.6
-BITNESS=32
-
-INSTALLER_FILE=PyQt4-${PYQT_VERSION}-gpl-Py${PY_VERSION}-Qt${QT_VERSION}-x${BITNESS}.exe
-URL=http://sourceforge.net/projects/pyqt/files/PyQt4/PyQt-${PYQT_VERSION}/${INSTALLER_FILE}
-wget -O "$INSTALLER_FILE" "$URL"
-
-rm -rf pyqt4-windows
-mkdir pyqt4-windows
-7z x -opyqt4-windows "$INSTALLER_FILE"
-
-rm -rf pynsist_pkgs
-mkdir pynsist_pkgs
-
-echo "Rearranging files into pynsist_pkgs..."
-mv 'pyqt4-windows/Lib/site-packages'/* pynsist_pkgs/
-rm pynsist_pkgs/PyQt4/assistant.exe pynsist_pkgs/PyQt4/designer.exe
-mv 'pyqt4-windows/$_OUTDIR/'*.pyd pynsist_pkgs/PyQt4/
-# These may not be necessary:
-mv 'pyqt4-windows/$_OUTDIR/qsci/' 'pyqt4-windows/$_OUTDIR/sip/' pynsist_pkgs/PyQt4/
-
-rm -r pyqt4-windows
-echo "Done"

+ 0 - 13
examples/pyqt4/installer.cfg

@@ -1,13 +0,0 @@
-[Application]
-name=List App (PyQt)
-version=1.0
-entry_point=listapp:main
-
-[Python]
-version=3.3.5
-
-[Include]
-packages=listapp
-    PyQt4
-    sip
-files = README.md

+ 0 - 33
examples/pyqt4/listapp/__init__.py

@@ -1,33 +0,0 @@
-import sys
-from PyQt4 import QtGui
-
-from .main import Ui_MainWindow
-
-class Main(QtGui.QMainWindow):
-    def __init__(self):
-        super().__init__()
-        self.ui = Ui_MainWindow()
-        self.ui.setupUi(self)
-        
-        self.ui.add_button.clicked.connect(self.add_item)
-
-    def get_radio_option(self):
-        if self.ui.radio_1.isChecked():
-            return 'Thing 1'
-        elif self.ui.radio_2.isChecked():
-            return 'Thing 2'
-        elif self.ui.radio_3.isChecked():
-            return 'Thing 3'
-        elif self.ui.radio_4.isChecked():
-            return 'Last thing'
-        return 'No thing'
-        
-    def add_item(self):
-        text = self.get_radio_option()
-        QtGui.QListWidgetItem(text, self.ui.listWidget)        
-
-def main():
-    app = QtGui.QApplication(sys.argv)
-    window = Main()
-    window.show()
-    sys.exit(app.exec_())

+ 0 - 95
examples/pyqt4/listapp/main.py

@@ -1,95 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Form implementation generated from reading ui file 'buttonapp/main.ui'
-#
-# Created: Wed Apr  2 16:57:10 2014
-#      by: PyQt4 UI code generator 4.10.3
-#
-# WARNING! All changes made in this file will be lost!
-
-from PyQt4 import QtCore, QtGui
-
-try:
-    _fromUtf8 = QtCore.QString.fromUtf8
-except AttributeError:
-    def _fromUtf8(s):
-        return s
-
-try:
-    _encoding = QtGui.QApplication.UnicodeUTF8
-    def _translate(context, text, disambig):
-        return QtGui.QApplication.translate(context, text, disambig, _encoding)
-except AttributeError:
-    def _translate(context, text, disambig):
-        return QtGui.QApplication.translate(context, text, disambig)
-
-class Ui_MainWindow(object):
-    def setupUi(self, MainWindow):
-        MainWindow.setObjectName(_fromUtf8("MainWindow"))
-        MainWindow.resize(393, 606)
-        self.centralwidget = QtGui.QWidget(MainWindow)
-        self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
-        self.horizontalLayout = QtGui.QHBoxLayout(self.centralwidget)
-        self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
-        self.groupBox = QtGui.QGroupBox(self.centralwidget)
-        self.groupBox.setObjectName(_fromUtf8("groupBox"))
-        self.verticalLayout = QtGui.QVBoxLayout(self.groupBox)
-        self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
-        self.radio_1 = QtGui.QRadioButton(self.groupBox)
-        self.radio_1.setObjectName(_fromUtf8("radio_1"))
-        self.verticalLayout.addWidget(self.radio_1)
-        self.radio_2 = QtGui.QRadioButton(self.groupBox)
-        self.radio_2.setObjectName(_fromUtf8("radio_2"))
-        self.verticalLayout.addWidget(self.radio_2)
-        self.radio_3 = QtGui.QRadioButton(self.groupBox)
-        self.radio_3.setObjectName(_fromUtf8("radio_3"))
-        self.verticalLayout.addWidget(self.radio_3)
-        self.radio_4 = QtGui.QRadioButton(self.groupBox)
-        self.radio_4.setObjectName(_fromUtf8("radio_4"))
-        self.verticalLayout.addWidget(self.radio_4)
-        self.add_button = QtGui.QPushButton(self.groupBox)
-        self.add_button.setObjectName(_fromUtf8("add_button"))
-        self.verticalLayout.addWidget(self.add_button)
-        spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
-        self.verticalLayout.addItem(spacerItem)
-        self.horizontalLayout.addWidget(self.groupBox)
-        self.listWidget = QtGui.QListWidget(self.centralwidget)
-        self.listWidget.setObjectName(_fromUtf8("listWidget"))
-        self.horizontalLayout.addWidget(self.listWidget)
-        MainWindow.setCentralWidget(self.centralwidget)
-        self.menubar = QtGui.QMenuBar(MainWindow)
-        self.menubar.setGeometry(QtCore.QRect(0, 0, 393, 24))
-        self.menubar.setObjectName(_fromUtf8("menubar"))
-        self.menuFile = QtGui.QMenu(self.menubar)
-        self.menuFile.setObjectName(_fromUtf8("menuFile"))
-        MainWindow.setMenuBar(self.menubar)
-        self.statusbar = QtGui.QStatusBar(MainWindow)
-        self.statusbar.setObjectName(_fromUtf8("statusbar"))
-        MainWindow.setStatusBar(self.statusbar)
-        self.actionNew = QtGui.QAction(MainWindow)
-        self.actionNew.setObjectName(_fromUtf8("actionNew"))
-        self.actionOpen = QtGui.QAction(MainWindow)
-        self.actionOpen.setObjectName(_fromUtf8("actionOpen"))
-        self.actionSave = QtGui.QAction(MainWindow)
-        self.actionSave.setObjectName(_fromUtf8("actionSave"))
-        self.menuFile.addAction(self.actionNew)
-        self.menuFile.addAction(self.actionOpen)
-        self.menuFile.addAction(self.actionSave)
-        self.menubar.addAction(self.menuFile.menuAction())
-
-        self.retranslateUi(MainWindow)
-        QtCore.QMetaObject.connectSlotsByName(MainWindow)
-
-    def retranslateUi(self, MainWindow):
-        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow", None))
-        self.groupBox.setTitle(_translate("MainWindow", "Click things", None))
-        self.radio_1.setText(_translate("MainWindow", "Thing 1", None))
-        self.radio_2.setText(_translate("MainWindow", "Thing 2", None))
-        self.radio_3.setText(_translate("MainWindow", "Thing 3", None))
-        self.radio_4.setText(_translate("MainWindow", "Last thing", None))
-        self.add_button.setText(_translate("MainWindow", "Add to list", None))
-        self.menuFile.setTitle(_translate("MainWindow", "File", None))
-        self.actionNew.setText(_translate("MainWindow", "New", None))
-        self.actionOpen.setText(_translate("MainWindow", "Open", None))
-        self.actionSave.setText(_translate("MainWindow", "Save", None))
-

+ 0 - 118
examples/pyqt4/listapp/main.ui

@@ -1,118 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>MainWindow</class>
- <widget class="QMainWindow" name="MainWindow">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>393</width>
-    <height>606</height>
-   </rect>
-  </property>
-  <property name="windowTitle">
-   <string>MainWindow</string>
-  </property>
-  <widget class="QWidget" name="centralwidget">
-   <layout class="QHBoxLayout" name="horizontalLayout">
-    <item>
-     <widget class="QGroupBox" name="groupBox">
-      <property name="title">
-       <string>Click things</string>
-      </property>
-      <layout class="QVBoxLayout" name="verticalLayout">
-       <item>
-        <widget class="QRadioButton" name="radio_1">
-         <property name="text">
-          <string>Thing 1</string>
-         </property>
-        </widget>
-       </item>
-       <item>
-        <widget class="QRadioButton" name="radio_2">
-         <property name="text">
-          <string>Thing 2</string>
-         </property>
-        </widget>
-       </item>
-       <item>
-        <widget class="QRadioButton" name="radio_3">
-         <property name="text">
-          <string>Thing 3</string>
-         </property>
-        </widget>
-       </item>
-       <item>
-        <widget class="QRadioButton" name="radio_4">
-         <property name="text">
-          <string>Last thing</string>
-         </property>
-        </widget>
-       </item>
-       <item>
-        <widget class="QPushButton" name="add_button">
-         <property name="text">
-          <string>Add to list</string>
-         </property>
-        </widget>
-       </item>
-       <item>
-        <spacer name="verticalSpacer">
-         <property name="orientation">
-          <enum>Qt::Vertical</enum>
-         </property>
-         <property name="sizeHint" stdset="0">
-          <size>
-           <width>20</width>
-           <height>40</height>
-          </size>
-         </property>
-        </spacer>
-       </item>
-      </layout>
-     </widget>
-    </item>
-    <item>
-     <widget class="QListWidget" name="listWidget"/>
-    </item>
-   </layout>
-  </widget>
-  <widget class="QMenuBar" name="menubar">
-   <property name="geometry">
-    <rect>
-     <x>0</x>
-     <y>0</y>
-     <width>393</width>
-     <height>24</height>
-    </rect>
-   </property>
-   <widget class="QMenu" name="menuFile">
-    <property name="title">
-     <string>File</string>
-    </property>
-    <addaction name="actionNew"/>
-    <addaction name="actionOpen"/>
-    <addaction name="actionSave"/>
-   </widget>
-   <addaction name="menuFile"/>
-  </widget>
-  <widget class="QStatusBar" name="statusbar"/>
-  <action name="actionNew">
-   <property name="text">
-    <string>New</string>
-   </property>
-  </action>
-  <action name="actionOpen">
-   <property name="text">
-    <string>Open</string>
-   </property>
-  </action>
-  <action name="actionSave">
-   <property name="text">
-    <string>Save</string>
-   </property>
-  </action>
- </widget>
- <resources/>
- <connections/>
-</ui>

+ 0 - 13
examples/tkinter/exampleapp.py

@@ -1,13 +0,0 @@
-from tkinter import *
-
-def main():
-    root = Tk()
-    root.title("Python Example App")
-    t = Text(root)
-    t.insert(END, "Type stuff here.")
-    t.pack()
-
-    w = Label(root, text="Hello, world!")
-    w.pack()
-
-    root.mainloop()

+ 0 - 7
examples/tkinter/installer.cfg

@@ -1,7 +0,0 @@
-[Application]
-name=My App
-version=1.0
-entry_point=exampleapp:main
-
-[Include]
-packages=exampleapp

+ 1 - 3
flit.ini

@@ -5,19 +5,17 @@ author-email = thomas@kluyver.me.uk
 dist-name = pynsist
 dist-name = pynsist
 home-page = https://pynsist.readthedocs.io/en/latest/
 home-page = https://pynsist.readthedocs.io/en/latest/
 description-file = README.rst
 description-file = README.rst
+requires-python = >=3.5
 requires = requests
 requires = requests
     requests_download
     requests_download
     jinja2
     jinja2
     yarg
     yarg
     win_cli_launchers
     win_cli_launchers
-    configparser; python_version == '2.7'
-    pathlib2; python_version == '2.7' or python_version == '3.3'
 dev-requires = testpath
 dev-requires = testpath
 classifiers = License :: OSI Approved :: MIT License
 classifiers = License :: OSI Approved :: MIT License
     Intended Audience :: Developers
     Intended Audience :: Developers
     Environment :: Win32 (MS Windows)
     Environment :: Win32 (MS Windows)
     Programming Language :: Python :: 3
     Programming Language :: Python :: 3
-    Programming Language :: Python :: 2.7
     Topic :: Software Development
     Topic :: Software Development
     Topic :: System :: Installation/Setup
     Topic :: System :: Installation/Setup
     Topic :: System :: Software Distribution
     Topic :: System :: Software Distribution

+ 28 - 88
nsist/__init__.py

@@ -6,10 +6,7 @@ import logging
 import ntpath
 import ntpath
 import operator
 import operator
 import os
 import os
-try:
-    from pathlib import Path
-except ImportError:
-    from pathlib2 import Path  # Backport
+from pathlib import Path
 import re
 import re
 import shutil
 import shutil
 from subprocess import call
 from subprocess import call
@@ -17,13 +14,8 @@ import sys
 import fnmatch
 import fnmatch
 import zipfile
 import zipfile
 
 
-PY2 = sys.version_info[0] == 2
-
 if os.name == 'nt':
 if os.name == 'nt':
-    if PY2:
-        import _winreg as winreg
-    else:
-        import winreg
+    import winreg
 else:
 else:
     winreg = None
     winreg = None
 
 
@@ -34,13 +26,13 @@ from .nsiswriter import NSISFileWriter
 from .pypi import fetch_pypi_wheels
 from .pypi import fetch_pypi_wheels
 from .util import download, text_types, get_cache_dir
 from .util import download, text_types, get_cache_dir
 
 
-__version__ = '1.12'
+__version__ = '2.0'
 
 
 pjoin = os.path.join
 pjoin = os.path.join
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)
 
 
 _PKGDIR = os.path.abspath(os.path.dirname(__file__))
 _PKGDIR = os.path.abspath(os.path.dirname(__file__))
-DEFAULT_PY_VERSION = '2.7.13' if PY2 else '3.6.1'
+DEFAULT_PY_VERSION = '3.6.3'
 DEFAULT_BUILD_DIR = pjoin('build', 'nsis')
 DEFAULT_BUILD_DIR = pjoin('build', 'nsis')
 DEFAULT_ICON = pjoin(_PKGDIR, 'glossyorb.ico')
 DEFAULT_ICON = pjoin(_PKGDIR, 'glossyorb.ico')
 if os.name == 'nt' and sys.maxsize == (2**63)-1:
 if os.name == 'nt' and sys.maxsize == (2**63)-1:
@@ -88,10 +80,10 @@ class InstallerBuilder(object):
     :param list exclude: Paths of files to exclude that would otherwise be included
     :param list exclude: Paths of files to exclude that would otherwise be included
     :param str py_version: Full version of Python to bundle
     :param str py_version: Full version of Python to bundle
     :param int py_bitness: Bitness of bundled Python (32 or 64)
     :param int py_bitness: Bitness of bundled Python (32 or 64)
-    :param str py_format: 'installer' or 'bundled'. Default 'bundled' for Python
-            >= 3.6, 'installer' for older versions.
+    :param str py_format: (deprecated) 'bundled'. Use Pynsist 1.x for
+            'installer' option.
     :param bool inc_msvcrt: True to include the Microsoft C runtime with 'bundled'
     :param bool inc_msvcrt: True to include the Microsoft C runtime with 'bundled'
-            Python. Ignored when py_format='installer'.
+            Python.
     :param str build_dir: Directory to run the build in
     :param str build_dir: Directory to run the build in
     :param str installer_name: Filename of the installer to produce
     :param str installer_name: Filename of the installer to produce
     :param str nsi_template: Path to a template NSI file to use
     :param str nsi_template: Path to a template NSI file to use
@@ -99,7 +91,7 @@ class InstallerBuilder(object):
     def __init__(self, appname, version, shortcuts, publisher=None,
     def __init__(self, appname, version, shortcuts, publisher=None,
                 icon=DEFAULT_ICON, packages=None, extra_files=None,
                 icon=DEFAULT_ICON, packages=None, extra_files=None,
                 py_version=DEFAULT_PY_VERSION, py_bitness=DEFAULT_BITNESS,
                 py_version=DEFAULT_PY_VERSION, py_bitness=DEFAULT_BITNESS,
-                py_format=None, inc_msvcrt=True, build_dir=DEFAULT_BUILD_DIR,
+                py_format='bundled', inc_msvcrt=True, build_dir=DEFAULT_BUILD_DIR,
                 installer_name=None, nsi_template=None,
                 installer_name=None, nsi_template=None,
                 exclude=None, pypi_wheel_reqs=None, commands=None):
                 exclude=None, pypi_wheel_reqs=None, commands=None):
         self.appname = appname
         self.appname = appname
@@ -119,6 +111,9 @@ class InstallerBuilder(object):
             if not os.environ.get('PYNSIST_PY_PRERELEASE'):
             if not os.environ.get('PYNSIST_PY_PRERELEASE'):
                 raise InputError('py_version', py_version,
                 raise InputError('py_version', py_version,
                                  "a full Python version like '3.4.0'")
                                  "a full Python version like '3.4.0'")
+        if self.py_version_tuple < (3, 5):
+            raise InputError('py_version', py_version,
+                             "Python >= 3.5.0 (use Pynsist 1.x for older Python.")
         self.py_bitness = py_bitness
         self.py_bitness = py_bitness
         if py_bitness not in {32, 64}:
         if py_bitness not in {32, 64}:
             raise InputError('py_bitness', py_bitness, "32 or 64")
             raise InputError('py_bitness', py_bitness, "32 or 64")
@@ -126,18 +121,11 @@ class InstallerBuilder(object):
         if self.py_bitness == 32:
         if self.py_bitness == 32:
             self.py_qualifier += '-32'
             self.py_qualifier += '-32'
 
 
-        if py_format is not None:
-            self.py_format = py_format
-        elif self.py_version_tuple >= (3, 6):
-            self.py_format = 'bundled'
-        else:
-            self.py_format = 'installer'
-        if self.py_version_tuple >= (3, 5):
-            if self.py_format not in {'installer', 'bundled'}:
-                raise InputError('py_format', self.py_format, "installer or bundled")
-        else:
-            if self.py_format != 'installer':
-                raise InputError('py_format', self.py_format, "installer (for Python < 3.5)")
+        if py_format == 'installer':
+            raise InputError('py_format', py_format, "'bundled' (use Pynsist 1.x for 'installer')")
+        elif py_format != 'bundled':
+            raise InputError('py_format', py_format, "'bundled'")
+
         self.inc_msvcrt = inc_msvcrt
         self.inc_msvcrt = inc_msvcrt
 
 
         # Build details
         # Build details
@@ -145,15 +133,10 @@ class InstallerBuilder(object):
         self.installer_name = installer_name or self.make_installer_name()
         self.installer_name = installer_name or self.make_installer_name()
         self.nsi_template = nsi_template
         self.nsi_template = nsi_template
         if self.nsi_template is None:
         if self.nsi_template is None:
-            if self.py_format == 'bundled':
-                if self.inc_msvcrt:
-                    self.nsi_template = 'pyapp_msvcrt.nsi'
-                else:
-                    self.nsi_template = 'pyapp.nsi'
-            elif self.py_version_tuple < (3, 3):
-                self.nsi_template = 'pyapp_w_pylauncher.nsi'
+            if self.inc_msvcrt:
+                self.nsi_template = 'pyapp_msvcrt.nsi'
             else:
             else:
-                self.nsi_template = 'pyapp_installpy.nsi'
+                self.nsi_template = 'pyapp.nsi'
 
 
         self.nsi_file = pjoin(self.build_dir, 'installer.nsi')
         self.nsi_file = pjoin(self.build_dir, 'installer.nsi')
 
 
@@ -180,39 +163,19 @@ class InstallerBuilder(object):
     def _python_download_url_filename(self):
     def _python_download_url_filename(self):
         version = self.py_version
         version = self.py_version
         bitness = self.py_bitness
         bitness = self.py_bitness
-        if self.py_version_tuple >= (3, 5):
-            if self.py_format == 'bundled':
-                filename = 'python-{}-embed-{}.zip'.format(version,
-                                           'amd64' if bitness==64 else 'win32')
-            else:
-                filename = 'python-{}{}.exe'.format(version,
-                                            '-amd64' if bitness==64 else '')
-        else:
-            filename = 'python-{0}{1}.msi'.format(version,
-                                            '.amd64' if bitness==64 else '')
+        filename = 'python-{}-embed-{}.zip'.format(version,
+                                   'amd64' if bitness==64 else 'win32')
 
 
         version_minus_prerelease = re.sub(r'(a|b|rc)\d+$', '', self.py_version)
         version_minus_prerelease = re.sub(r'(a|b|rc)\d+$', '', self.py_version)
         return 'https://www.python.org/ftp/python/{0}/{1}'.format(
         return 'https://www.python.org/ftp/python/{0}/{1}'.format(
                 version_minus_prerelease, filename), filename
                 version_minus_prerelease, filename), filename
 
 
-    def fetch_python(self):
-        """Fetch the MSI for the specified version of Python.
+    def fetch_python_embeddable(self):
+        """Fetch the embeddable Windows build for the specified Python version
 
 
-        It will be placed in the build directory.
+        It will be unpacked into the build directory.
         """
         """
         url, filename = self._python_download_url_filename()
         url, filename = self._python_download_url_filename()
-
-        cache_file = get_cache_dir(ensure_existence=True) / filename
-        if not cache_file.is_file():
-            logger.info('Downloading Python installer...')
-            logger.info('Getting %s', url)
-            download(url, cache_file)
-
-        logger.info('Copying Python installer to build directory')
-        shutil.copy2(str(cache_file), self.build_dir)
-
-    def fetch_python_embeddable(self):
-        url, filename = self._python_download_url_filename()
         cache_file = get_cache_dir(ensure_existence=True) / filename
         cache_file = get_cache_dir(ensure_existence=True) / filename
         if not cache_file.is_file():
         if not cache_file.is_file():
             logger.info('Downloading embeddable Python build...')
             logger.info('Downloading embeddable Python build...')
@@ -246,21 +209,6 @@ class InstallerBuilder(object):
 
 
         shutil.copytree(src, dst)
         shutil.copytree(src, dst)
 
 
-    def fetch_pylauncher(self):
-        """Fetch the MSI for PyLauncher (required for Python2.x).
-
-        It will be placed in the build directory.
-        """
-        arch_tag = '.amd64' if (self.py_bitness == 64) else ''
-        url = ("https://bitbucket.org/vinay.sajip/pylauncher/downloads/"
-               "launchwin{0}.msi".format(arch_tag))
-        target = pjoin(self.build_dir, 'launchwin{0}.msi'.format(arch_tag))
-        if os.path.isfile(target):
-            logger.info('PyLauncher MSI already in build directory.')
-            return
-        logger.info('Downloading PyLauncher MSI...')
-        download(url, target)
-
     SCRIPT_TEMPLATE = """#!python{qualifier}
     SCRIPT_TEMPLATE = """#!python{qualifier}
 import sys, os
 import sys, os
 scriptdir, script = os.path.split(__file__)
 scriptdir, script = os.path.split(__file__)
@@ -340,10 +288,7 @@ if __name__ == '__main__':
                 else:
                 else:
                     shutil.copy2(sc['script'], self.build_dir)
                     shutil.copy2(sc['script'], self.build_dir)
 
 
-                if self.py_format == 'bundled':
-                    target = '$INSTDIR\Python\python{}.exe'
-                else:
-                    target = 'py{}'
+                target = '$INSTDIR\Python\python{}.exe'
                 sc['target'] = target.format('' if sc['console'] else 'w')
                 sc['target'] = target.format('' if sc['console'] else 'w')
                 sc['parameters'] = '"%s"' % ntpath.join('$INSTDIR', sc['script'])
                 sc['parameters'] = '"%s"' % ntpath.join('$INSTDIR', sc['script'])
                 files.add(os.path.basename(sc['script']))
                 files.add(os.path.basename(sc['script']))
@@ -477,14 +422,9 @@ if __name__ == '__main__':
             if e.errno != errno.EEXIST:
             if e.errno != errno.EEXIST:
                 raise e
                 raise e
 
 
-        if self.py_format == 'bundled':
-            self.fetch_python_embeddable()
-            if self.inc_msvcrt:
-                self.prepare_msvcrt()
-        else:
-            self.fetch_python()
-            if self.py_version < '3.3':
-                self.fetch_pylauncher()
+        self.fetch_python_embeddable()
+        if self.inc_msvcrt:
+            self.prepare_msvcrt()
 
 
         self.prepare_shortcuts()
         self.prepare_shortcuts()
 
 

+ 0 - 1
nsist/configreader.py

@@ -223,7 +223,6 @@ def get_installer_builder_args(config):
     args['extra_files'] = read_extra_files(config)
     args['extra_files'] = read_extra_files(config)
     args['py_version'] = config.get('Python', 'version', fallback=DEFAULT_PY_VERSION)
     args['py_version'] = config.get('Python', 'version', fallback=DEFAULT_PY_VERSION)
     args['py_bitness'] = config.getint('Python', 'bitness', fallback=DEFAULT_BITNESS)
     args['py_bitness'] = config.getint('Python', 'bitness', fallback=DEFAULT_BITNESS)
-    args['py_format'] = config.get('Python', 'format', fallback=None)
     args['inc_msvcrt'] = config.getboolean('Python', 'include_msvcrt', fallback=True)
     args['inc_msvcrt'] = config.getboolean('Python', 'include_msvcrt', fallback=True)
     args['build_dir'] = config.get('Build', 'directory', fallback=DEFAULT_BUILD_DIR)
     args['build_dir'] = config.get('Build', 'directory', fallback=DEFAULT_BUILD_DIR)
     args['installer_name'] = config.get('Build', 'installer_name', fallback=None)
     args['installer_name'] = config.get('Build', 'installer_name', fallback=None)

+ 1 - 5
nsist/nsiswriter.py

@@ -56,13 +56,9 @@ class NSISFileWriter(object):
             'single_shortcut': len(installerbuilder.shortcuts) == 1,
             'single_shortcut': len(installerbuilder.shortcuts) == 1,
             'pynsist_pkg_dir': _PKGDIR,
             'pynsist_pkg_dir': _PKGDIR,
             'has_commands': len(installerbuilder.commands) > 0,
             'has_commands': len(installerbuilder.commands) > 0,
+            'python': '"$INSTDIR\\Python\\python"'
         }
         }
 
 
-        if installerbuilder.py_format == 'bundled':
-            self.namespace['python'] = '"$INSTDIR\\Python\\python"'
-        else:
-            self.namespace['python'] = 'py -{}'.format(installerbuilder.py_qualifier)
-
     def write(self, target):
     def write(self, target):
         """Fill out the template and write the result to 'target'.
         """Fill out the template and write the result to 'target'.
         
         

+ 0 - 16
nsist/pyapp.nsi

@@ -12,7 +12,6 @@
  
  
 SetCompressor lzma
 SetCompressor lzma
 
 
-[% if ib.py_format == 'bundled' %]
 !define MULTIUSER_EXECUTIONLEVEL Highest
 !define MULTIUSER_EXECUTIONLEVEL Highest
 !define MULTIUSER_INSTALLMODE_DEFAULT_CURRENTUSER
 !define MULTIUSER_INSTALLMODE_DEFAULT_CURRENTUSER
 !define MULTIUSER_MUI
 !define MULTIUSER_MUI
@@ -22,7 +21,6 @@ SetCompressor lzma
 !define MULTIUSER_INSTALLMODE_FUNCTION correct_prog_files
 !define MULTIUSER_INSTALLMODE_FUNCTION correct_prog_files
 [% endif %]
 [% endif %]
 !include MultiUser.nsh
 !include MultiUser.nsh
-[% endif %]
 
 
 [% block modernui %]
 [% block modernui %]
 ; Modern UI installer stuff 
 ; Modern UI installer stuff 
@@ -33,9 +31,7 @@ SetCompressor lzma
 ; UI pages
 ; UI pages
 [% block ui_pages %]
 [% block ui_pages %]
 !insertmacro MUI_PAGE_WELCOME
 !insertmacro MUI_PAGE_WELCOME
-[% if ib.py_format == 'bundled' %]
 !insertmacro MULTIUSER_PAGE_INSTALLMODE
 !insertmacro MULTIUSER_PAGE_INSTALLMODE
-[% endif %]
 !insertmacro MUI_PAGE_DIRECTORY
 !insertmacro MUI_PAGE_DIRECTORY
 !insertmacro MUI_PAGE_INSTFILES
 !insertmacro MUI_PAGE_INSTFILES
 !insertmacro MUI_PAGE_FINISH
 !insertmacro MUI_PAGE_FINISH
@@ -45,9 +41,6 @@ SetCompressor lzma
 
 
 Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
 Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
 OutFile "${INSTALLER_NAME}"
 OutFile "${INSTALLER_NAME}"
-[% if ib.py_format != 'bundled' %]
-InstallDir "$PROGRAMFILES${BITNESS}\${PRODUCT_NAME}"
-[% endif %]
 ShowInstDetails show
 ShowInstDetails show
 
 
 Section -SETTINGS
 Section -SETTINGS
@@ -65,13 +58,11 @@ Section "!${PRODUCT_NAME}" sec_app
   File /r "pkgs\*.*"
   File /r "pkgs\*.*"
   SetOutPath "$INSTDIR"
   SetOutPath "$INSTDIR"
 
 
-  [% if ib.py_format == 'bundled' %]
   ; Marker file for per-user install
   ; Marker file for per-user install
   StrCmp $MultiUser.InstallMode CurrentUser 0 +3
   StrCmp $MultiUser.InstallMode CurrentUser 0 +3
     FileOpen $0 "$INSTDIR\${USER_INSTALL_MARKER}" w
     FileOpen $0 "$INSTDIR\${USER_INSTALL_MARKER}" w
     FileClose $0
     FileClose $0
     SetFileAttributes "$INSTDIR\${USER_INSTALL_MARKER}" HIDDEN
     SetFileAttributes "$INSTDIR\${USER_INSTALL_MARKER}" HIDDEN
-  [% endif %]
 
 
   [% block install_files %]
   [% block install_files %]
   ; Install files
   ; Install files
@@ -207,7 +198,6 @@ Function .onMouseOverSection
     [% endblock mouseover_messages %]
     [% endblock mouseover_messages %]
 FunctionEnd
 FunctionEnd
 
 
-[% if ib.py_format == 'bundled' %]
 Function .onInit
 Function .onInit
   !insertmacro MULTIUSER_INIT
   !insertmacro MULTIUSER_INIT
 FunctionEnd
 FunctionEnd
@@ -224,9 +214,3 @@ Function correct_prog_files
     StrCpy $INSTDIR "$PROGRAMFILES64\${MULTIUSER_INSTALLMODE_INSTDIR}"
     StrCpy $INSTDIR "$PROGRAMFILES64\${MULTIUSER_INSTALLMODE_INSTDIR}"
 FunctionEnd
 FunctionEnd
 [% endif %]
 [% endif %]
-
-[% else %]
-Function .onInit
-  SetShellVarContext all
-FunctionEnd
-[% endif %]

+ 0 - 38
nsist/pyapp_installpy.nsi

@@ -1,38 +0,0 @@
-[% extends "pyapp.nsi" %]
-
-[% block ui_pages %]
-[# We only need to add COMPONENTS, but they have to be in order #]
-!insertmacro MUI_PAGE_WELCOME
-!insertmacro MUI_PAGE_COMPONENTS
-!insertmacro MUI_PAGE_DIRECTORY
-!insertmacro MUI_PAGE_INSTFILES
-!insertmacro MUI_PAGE_FINISH
-[% endblock ui_pages %]
-
-[% block sections %]
-Section "Python ${PY_VERSION}" sec_py
-
-  DetailPrint "Installing Python ${PY_MAJOR_VERSION}, ${BITNESS} bit"
-  [% if ib.py_version_tuple >= (3, 5) %]
-    [% set filename = 'python-' ~ ib.py_version ~ ('-amd64' if ib.py_bitness==64 else '') ~ '.exe' %]
-    File "[[filename]]"
-    ExecWait '"$INSTDIR\[[filename]]" /passive Include_test=0 InstallAllUsers=1'
-  [% else %]
-    [% set filename = 'python-' ~ ib.py_version ~ ('.amd64' if ib.py_bitness==64 else '') ~ '.msi' %]
-    File "[[filename]]"
-    ExecWait 'msiexec /i "$INSTDIR\[[filename]]" \
-            /qb ALLUSERS=1 TARGETDIR="$COMMONFILES${BITNESS}\Python\${PY_MAJOR_VERSION}"'
-  [% endif %]
-  Delete "$INSTDIR\[[filename]]"
-SectionEnd
-
-[[ super() ]]
-[% endblock sections %]
-
-[% block mouseover_messages %]
-    StrCmp $0 ${sec_py} 0 +2
-      SendMessage $R0 ${WM_SETTEXT} 0 "STR:The Python interpreter. \
-            This is required for ${PRODUCT_NAME} to run."
-
-[[ super() ]]
-[% endblock mouseover_messages %]

+ 0 - 25
nsist/pyapp_w_pylauncher.nsi

@@ -1,25 +0,0 @@
-[% extends "pyapp_installpy.nsi" %]
-[# For Python 2, add the py/pyw Windows launcher. Python 3 includes it already. #]
-
-[% block sections %]
-Section "PyLauncher" sec_pylauncher
-    ; Check for the existence of the pyw command, skip installing if it exists
-    nsExec::Exec 'where pyw'
-    Pop $0
-    IntCmp $0 0 SkipPylauncher
-    ; Extract the py/pyw launcher msi and run it.
-    File "launchwin${ARCH_TAG}.msi"
-    ExecWait 'msiexec /i "$INSTDIR\launchwin${ARCH_TAG}.msi" /qb ALLUSERS=1'
-    Delete "$INSTDIR\launchwin${ARCH_TAG}.msi"
-    SkipPylauncher:
-SectionEnd
-
-[[ super() ]]
-[% endblock %]
-
-[% block mouseover_messages %]
-[[ super() ]]
-    StrCmp $0 ${sec_app} "" +2
-      SendMessage $R0 ${WM_SETTEXT} 0 "STR:The Python launcher. \
-          This is required for ${PRODUCT_NAME} to run."
-[% endblock %]

+ 0 - 1
nsist/tests/test_configuration_validator.py

@@ -43,7 +43,6 @@ def test_valid_config_with_values_starting_on_new_line():
 
 
     assert args['py_version'] == '3.6.0'
     assert args['py_version'] == '3.6.0'
     assert args['py_bitness'] == 64
     assert args['py_bitness'] == 64
-    assert args['py_format'] == 'bundled'
     assert args['inc_msvcrt'] == True
     assert args['inc_msvcrt'] == True
 
 
     assert args['build_dir'] == 'build/'
     assert args['build_dir'] == 'build/'