Commits

Vinay Sajip committed bff28df Draft

Added support for interpreter arguments.

Comments (0)

Files changed (4)

distlib/scripts.py

             filenames.append(outname)
 
     def _make_script(self, entry, filenames, options=None):
-        shebang = self._get_shebang('utf-8', options=options)
+        post_interp = b''
+        if options:
+            args = options.get('interpreter_args', [])
+            if args:
+                args = ' %s' % ' '.join(args)
+                post_interp = args.encode('utf-8')
+        shebang = self._get_shebang('utf-8', post_interp, options=options)
         script = self._get_script_text(entry).encode('utf-8')
         name = entry.name
         scriptnames = set()

docs/reference.rst

    .. method:: __init__(base=None)
 
       Initialise a cache instance with a specific directory which holds the
-      cache. If base is not specified, the value ``resource-cache` in the
+      cache. If base is not specified, the value ``resource-cache`` in the
       directory returned by :func:`~distlib.util.get_cache_base` is used.
 
    .. method:: get(resource)
 
       :type specification: str
       :param options: If specified, a dictionary of options used to control
-                      script creation. Currently, only the key ``gui`` is
-                      checked: this should be a ``bool`` which, if ``True``,
-                      indicates that the script is a windowed application.
-                      This distinction is only drawn on Windows if
-                      ``add_launchers`` is ``True``, and results in a windowed
-                      native launcher application if ``options['gui']`` is
-                      ``True`` (otherwise, the native executable launcher is a
-                      console application).
+                      script creation. Currently, the following keys are
+                      checked:
 
+                          ``gui``: This should be a ``bool`` which, if ``True``,
+                          indicates that the script is a windowed
+                          application. This distinction is only drawn
+                          on Windows if ``add_launchers`` is ``True``,
+                          and results in a windowed native launcher
+                          application if ``options['gui']`` is ``True``
+                          (otherwise, the native executable launcher
+                          is a console application).
+
+                          ``interpreter_args``: If specified, this should be
+                          a list of strings which are appended to the
+                          interpreter executable in the shebang line. If there
+                          are values with spaces, you will need to surround
+                          them with double quotes.
+
+                          .. note:: Linux does not handle passing arguments
+                             to interpreters particularly well -- multiple
+                             arguments are bundled up into one when passing to
+                             the interpreter -- see
+                             https://en.wikipedia.org/wiki/Shebang_line#Portability
+                             for more information. This may also affect other
+                             POSIX platforms -- consult the OS documentation
+                             for your system if necessary. On Windows, the
+                             ``distlib`` native executable launchers *do* parse
+                             multiple arguments and pass them to the
+                             interpreter.
+
+      :type options: dict
       :returns: A list of absolute pathnames of files installed (or which
                 would have been installed, but for ``dry_run`` being true).
 

docs/tutorial.rst

   as the return code from the script.
 
   You can pass an optional ``options`` dictionary to the :meth:`make` method.
-  This is meant to contain options which control script generation. The only
-  option currently in use is ``'gui'``, which indicates on Windows that a
-  Windows executable launcher (rather than a launcher which is a console
-  application) should be used. (This only applies if ``add_launchers`` is
-  true.)
+  This is meant to contain options which control script generation. There are
+  two options currently in use:
+
+  ``gui``: This Boolean value, if ``True``, indicates on Windows that a Windows
+  executable launcher (rather than a launcher which is a console application)
+  should be used. (This only applies if ``add_launchers`` is true.)
+
+  ``interpreter_args``: If provided, this should be a list of strings which
+  are added to the shebang line following the interpreter. If there are values
+  with spaces, you will need to surround them with double quotes.
+
+  .. note:: Use of this feature may affect portability, since POSIX does not
+    standardise how these arguments are passed to the interpreter (see
+    https://en.wikipedia.org/wiki/Shebang_line#Portability for more
+    information).
 
   For example, you can pass ``{'gui': True}`` to generate a windowed script.
 
 the :attr:`executable` attribute of a :class:`ScriptMaker` instance to the
 absolute Unicode path of the executable which you want to be written to the
 shebang lines of scripts. If not specified, the executable running the
-:class:`ScriptMaker` code is used.
+:class:`ScriptMaker` code is used. If the value has spaces, you should
+surround it with double quotes.
 
 
 Generating variants of a script

tests/test_scripts.py

         for f in files:
             self.assertIn(os.stat(f).st_mode & 0o7777, (0o755, 0o775))
 
+    def test_interpreter_args(self):
+        executable = fsencode(get_executable())
+        options = {
+            'interpreter_args': ['-E', '"foo bar"', 'baz frobozz']
+        }
+        self.maker.variants = set([''])
+        files = self.maker.make('foo = bar:baz', options=options)
+        self.assertEqual(len(files), 1)
+        with open(files[0], 'rb') as f:
+            shebang_line = f.readline()
+        self.assertIn(executable, shebang_line)
+        self.assertIn(b' -E "foo bar" baz frobozz', shebang_line)
+
+
 if __name__ == '__main__':  # pragma: no cover
     unittest.main()