Переглянути джерело

Allow specifying entry point instead of script.

A launch.py script will automatically be generated.
Thomas Kluyver 11 роки тому
батько
коміт
9a0d86ccae
6 змінених файлів з 59 додано та 25 видалено
  1. 24 1
      doc/cfgfile.rst
  2. 5 11
      doc/index.rst
  3. 0 4
      examples/pyqt/README
  4. 1 1
      examples/pyqt/installer.cfg
  5. 0 5
      examples/pyqt/start.py
  6. 29 3
      nsist/__init__.py

+ 24 - 1
doc/cfgfile.rst

@@ -17,9 +17,32 @@ Application section
 
   The version number of your application.
 
+.. describe:: entry_point
+
+   The function to launch your application, in the format ``module:function``.
+   Dots are allowed in the module part. pynsist will create a script like this,
+   plus some boilerplate::
+
+       from module import function
+       function()
+
 .. describe:: script
 
-  Path to the Python script which launches your application.
+   Path to the Python script which launches your application.
+
+   Ensure that this boilerplate code is at the top of your script::
+
+       #!python3.3
+       import sys
+       sys.path.insert(0, 'pkgs')
+
+   The first line tells it which version of Python to run with. If you use
+   binary packages, packages compiled for Python 3.3 won't work with Python 3.4.
+   The other lines make sure it can find the packages installed along with your
+   application.
+
+.. note::
+   Either ``entry_point`` or ``script`` must be specified, but not both.
 
 .. describe:: icon (optional)
 

+ 5 - 11
doc/index.rst

@@ -3,7 +3,7 @@ pynsist |version|
 
 pynsist is a tool to build Windows installers for your Python applications. The
 installers bundle Python itself, so you can distribute your application to
-people who don't have Windows installed.
+people who don't have Python installed.
 
 At present, pynsist requires Python 3.3 or above.
 
@@ -13,21 +13,15 @@ Quickstart
 1. Get the tools. Install `NSIS <http://nsis.sourceforge.net/Download>`_, and
    then install pynsist from PyPI by running ``pip install pynsist``.
 
-2. Add this code to the top of your script, above any other imports::
-
-       import sys
-       sys.path.insert(0, 'pkgs')
-
-   This will let your installed application find the packages installed with it.
-
-3. Write a config file ``installer.cfg``, like this:
+2. Write a config file ``installer.cfg``, like this:
 
    .. code-block:: ini
    
        [Application]
        name=My App
        version=1.0
-       script=myapp.py
+       # How to launch the app - this calls the 'main' function from the 'myapp' package:
+       entry_point=myapp:main
        icon=myapp.ico
 
        [Python]
@@ -45,7 +39,7 @@ Quickstart
 
   See :doc:`cfgfile` for more details about this.
 
-4. Run ``pynsist installer.cfg`` to generate your installer. If ``pynsist`` isn't
+3. Run ``pynsist installer.cfg`` to generate your installer. If ``pynsist`` isn't
    found, you can use ``python -m nsist installer.cfg`` instead.
 
 Contents

+ 0 - 4
examples/pyqt/README

@@ -6,7 +6,3 @@ http://sourceforge.net/projects/pyqt/files/PyQt4/PyQt-4.10.4/PyQt4-4.10.4-gpl-Py
 Open it up with an archive tool, and extract the PyQt4 folder and sip.pyd from
 Lib/site-packages. Place them in a folder called pynsist_pkgs, and pynsist will
 use them in preference to the files it finds on your system.
-
-The '#!python3.3' line is used by the Python Windows launcher (PEP 397) to
-select which version of Python to run. This is important, because binary
-packages for Python 3.3 won't work on Python 3.4.

+ 1 - 1
examples/pyqt/installer.cfg

@@ -1,7 +1,7 @@
 [Application]
 name=List App (PyQt)
 version=1.0
-script=start.py
+entry_point=listapp:main
 
 [Python]
 version=3.3.5

+ 0 - 5
examples/pyqt/start.py

@@ -1,5 +0,0 @@
-#!python3.3
-import sys
-sys.path.insert(0, 'pkgs')
-import listapp
-listapp.main()

+ 29 - 3
nsist/__init__.py

@@ -44,6 +44,21 @@ def fetch_python(version=DEFAULT_PY_VERSION, bitness=DEFAULT_BITNESS,
     except FileNotFoundError:
         logger.warn("GPG not available - could not check signature of {0}".format(target))
 
+SCRIPT_TEMPLATE = """#!python{qualifier}
+import sys
+sys.path.insert(0, 'pkgs')
+from {module} import {func}
+{func}()
+"""
+
+def write_script(entrypt, python_version, bitness, target):
+    qualifier = '.'.join(python_version.split('.')[:2])
+    if bitness == 32:
+        qualifier += '-32'
+    module, func = entrypt.split(":")
+    with open(target, 'w') as f:
+        f.write(SCRIPT_TEMPLATE.format(qualifier=qualifier, module=module, func=func))
+
 def copy_extra_files(filelist, build_dir):
     results = []  # name, is_directory
     for file in filelist:
@@ -69,7 +84,7 @@ def make_installer_name(appname, version):
 def run_nsis(nsi_file):
     return call(['makensis', nsi_file])
 
-def all_steps(appname, version, script, icon=DEFAULT_ICON, console=False,
+def all_steps(appname, version, script=None, entry_point=None, icon=DEFAULT_ICON, console=False,
                 packages=None, extra_files=None, py_version=DEFAULT_PY_VERSION,
                 py_bitness=DEFAULT_BITNESS, build_dir=DEFAULT_BUILD_DIR,
                 installer_name=None, nsi_template=DEFAULT_NSI_TEMPLATE):
@@ -77,7 +92,17 @@ def all_steps(appname, version, script, icon=DEFAULT_ICON, console=False,
 
     os.makedirs(build_dir, exist_ok=True)
     fetch_python(version=py_version, bitness=py_bitness, destination=build_dir)
-    shutil.copy2(script, build_dir)
+
+    if entry_point is not None:
+        if script is not None:
+            raise ValueError('Both script and entry_point were specified.')
+        script = 'launch.py'
+        write_script(entry_point, py_version, py_bitness, pjoin(build_dir, script))
+    elif script is not None:
+        shutil.copy2(script, build_dir)
+    else:
+        raise ValueError('Neither script nor entry_point was specified.')
+
     shutil.copy2(icon, build_dir)
     
     # Packages
@@ -132,7 +157,8 @@ def main(argv=None):
     all_steps(
         appname = appcfg['name'],
         version = appcfg['version'],
-        script = appcfg['script'],
+        script = appcfg.get('script', fallback=None),
+        entry_point = appcfg.get('entry_point', fallback=None),
         icon = appcfg.get('icon', DEFAULT_ICON),
         console = appcfg.getboolean('console', fallback=False),
         packages = cfg.get('Include', 'packages', fallback='').splitlines(),