[Python-checkins] python/nondist/sandbox/setuptools launcher.c, NONE, 1.1 EasyInstall.txt, 1.59, 1.60 easy_install.py, 1.26, 1.27 setup.py, 1.37, 1.38 setuptools.txt, 1.35, 1.36
pje@users.sourceforge.net
pje at users.sourceforge.net
Sat Sep 17 03:13:05 CEST 2005
Update of /cvsroot/python/python/nondist/sandbox/setuptools
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv8383
Modified Files:
EasyInstall.txt easy_install.py setup.py setuptools.txt
Added Files:
launcher.c
Log Message:
Added support to solve the infamous "we want .py on Windows, no
extension elsewhere" problem, while also bypassing the need for PATHEXT
on Windows, and in fact the need to even write script files at all, for
any platform. Instead, you define "entry points" in your setup script,
in this case the names of the scripts you want (without extensions) and
the functions that should be imported and run to implement the scripts.
Setuptools will then generate platform-appropriate script files at
install time, including an .exe wrapper when installing on Windows.
--- NEW FILE: launcher.c ---
/*
Setuptools Script Launcher for Windows
This is a stub executable for Windows that functions somewhat like
Effbot's "exemaker", in that it runs a script with the same name but
a .py extension, using information from a #! line. It differs in that
it spawns the actual Python executable, rather than attempting to
hook into the Python DLL. This means that the script will run with
sys.executable set to the Python executable, where exemaker ends up with
sys.executable pointing to itself. (Which means it won't work if you try
to run another Python process using sys.executable.)
To build/rebuild with mingw32, do this in the setuptools project directory:
gcc -mno-cygwin -O -s -o setuptools/launcher.exe launcher.c
It links to msvcrt.dll, but this shouldn't be a problem since it doesn't
actually run Python in the same process. Note that using 'exec' instead
of 'spawn' doesn't work, because on Windows this leads to the Python
executable running in the *background*, attached to the same console
window, meaning you get a command prompt back *before* Python even finishes
starting. So, we have to use spawnv() and wait for Python to exit before
continuing. :(
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include "windows.h"
int fail(char *format, char *data) {
/* Print error message to stderr and return 1 */
fprintf(stderr, format, data);
return 1;
}
int main(int argc, char **argv) {
char python[256]; /* python executable's filename*/
char script[256]; /* the script's filename */
HINSTANCE hPython; /* DLL handle for python executable */
int scriptf; /* file descriptor for script file */
char **newargs; /* argument array for exec */
char *ptr, *end; /* working pointers for string manipulation */
/* compute script name from our .exe name*/
GetModuleFileName(NULL, script, sizeof(script));
end = script + strlen(script);
while( end>script && *end != '.')
*end-- = '0円';
strcat(script, "py");
/* figure out the target python executable */
scriptf = open(script, O_RDONLY);
if (scriptf == -1) {
return fail("Cannot open %s\n", script);
}
end = python + read(scriptf, python, sizeof(python));
close(scriptf);
ptr = python-1;
while(++ptr < end && *ptr && *ptr!='\n' && *ptr!='\r') {
if (*ptr=='/')
*ptr='\\'; /* convert slashes to avoid LoadLibrary crashes... */
}
*ptr = '0円';
while (ptr>python && isspace(*ptr)) *ptr-- = '0円'; /* strip trailing sp */
if (strncmp(python, "#!", 2)) {
/* default to python.exe if no #! header */
strcpy(python, "#!python.exe");
}
/* At this point, the python buffer contains "#!pythonfilename" */
/* Using spawnv() can fail strangely if you e.g. find the Cygwin
Python, so we'll make sure Windows can find and load it */
hPython = LoadLibraryEx(python+2, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
if (!hPython) {
return fail("Cannot find Python executable %s\n", python+2);
}
/* And we'll use the absolute filename for spawnv */
GetModuleFileName(hPython, python, sizeof(python));
/* printf("Python executable: %s\n", python); */
/* Argument array needs to be argc+1 for args, plus 1 for null sentinel */
newargs = (char **)calloc(argc+2, sizeof(char *));
newargs[0] = python;
newargs[1] = script;
memcpy(newargs+2, argv+1, (argc-1)*sizeof(char *));
newargs[argc+1] = NULL;
/* printf("args 0: %s\nargs 1: %s\n", newargs[0], newargs[1]); */
return spawnv(P_WAIT, newargs[0], (const char * const *)(newargs));
}
Index: EasyInstall.txt
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/EasyInstall.txt,v
retrieving revision 1.59
retrieving revision 1.60
diff -u -d -r1.59 -r1.60
--- EasyInstall.txt 14 Sep 2005 16:41:23 -0000 1.59
+++ EasyInstall.txt 17 Sep 2005 01:13:01 -0000 1.60
@@ -46,12 +46,8 @@
setuptools being present; if so, you must be sure to delete it entirely, along
with the old ``pkg_resources`` module if it's present on ``sys.path``.
-An ``easy_install.py`` script will be installed in the normal location for
-Python scripts on your platform. In the examples below, you'll need to replace
-references to ``easy_install`` with the correct invocation to run
-``easy_install.py`` on your system. If you have Python 2.4 or better, you can
-also use ``python -m easy_install``, which will have the same effect, but which
-may be easier for you to type.
+An ``easy_install`` script will be installed in the normal location for
+Python scripts on your platform.
(Note: the ``ez_setup.py`` script accepts the same `Command-Line Options`_ and
`Configuration Files`_ as ``easy_install`` itself, so you can use them to
@@ -61,6 +57,28 @@
it afterwards.)
+Windows Installation
+~~~~~~~~~~~~~~~~~~~~
+
+On Windows, an ``easy_install.exe`` launcher will also be installed, so that
+you can just type ``easy_install`` as long as it's on your ``PATH``. If typing
+``easy_install`` at the command prompt doesn't work, check to make sure your
+``PATH`` includes the appropriate ``C:\\Python2X\\Scripts`` directory. On
+most current versions of Windows, you can change the ``PATH`` by right-clicking
+"My Computer", choosing "Properties" and selecting the "Advanced" tab, then
+clicking the "Environment Variables" button. ``PATH`` will be in the "System
+Variables" section, and you will probably need to reboot for the change to take
+effect. Be sure to add a ``;`` after the last item on ``PATH`` before adding
+the scripts directory to it.
+
+Note that instead of changing your ``PATH`` to include the Python scripts
+directory, you can also retarget the installtion location for scripts so they
+go on a directory that's already on the ``PATH``. For more information see the
+sections below on `Command-Line Options`_ and `Configuration Files`_. You
+can pass command line options (such as ``--script-dir``) to ``ez_setup.py`` to
+control where ``easy_install.exe`` will be installed.
+
+
Downloading and Installing a Package
------------------------------------
@@ -758,6 +776,15 @@
in Exemaker. So, don't use Exemaker to wrap ``easy_install.py``, or at any
rate don't expect it to work with all packages.
+0.6a2
+ * EasyInstall can now install "console_scripts" defined by packages that use
+ ``setuptools`` and define appropriate entry points. On Windows, console
+ scripts get an ``.exe`` wrapper so you can just type their name. On other
+ platforms, the scripts are installed without a file extension.
+
+ * Using ``python -m easy_install`` is now DEPRECATED, since an
+ ``easy_install`` wrapper is now available on all platforms.
+
0.6a1
* EasyInstall now does MD5 validation of downloads from PyPI, or from any link
that has an "#md5=..." trailer with a 32-digit lowercase hex md5 digest.
Index: easy_install.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/easy_install.py,v
retrieving revision 1.26
retrieving revision 1.27
diff -u -d -r1.26 -r1.27
--- easy_install.py 6 Jul 2005 02:10:47 -0000 1.26
+++ easy_install.py 17 Sep 2005 01:13:02 -0000 1.27
@@ -15,5 +15,8 @@
from setuptools.command.easy_install import *
if __name__ == '__main__':
+ print >>sys.stderr, "NOTE: python -m easy_install is deprecated."
+ print >>sys.stderr, "Please use the 'easy_install' command instead."
+ print >>sys.stderr
main(sys.argv[1:])
Index: setup.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setup.py,v
retrieving revision 1.37
retrieving revision 1.38
diff -u -d -r1.37 -r1.38
--- setup.py 22 Aug 2005 03:50:19 -0000 1.37
+++ setup.py 17 Sep 2005 01:13:02 -0000 1.38
@@ -34,12 +34,12 @@
keywords = "CPAN PyPI distutils eggs package management",
url = "http://peak.telecommunity.com/DevCenter/setuptools",
test_suite = 'setuptools.tests.test_suite',
-
packages = find_packages(),
+ package_data = {'setuptools': ['launcher.exe']},
py_modules = ['pkg_resources', 'easy_install'],
- scripts = ['easy_install.py'],
+
- zip_safe = False, # We want 'python -m easy_install' to work :(
+ zip_safe = False, # We want 'python -m easy_install' to work, for now :(
entry_points = {
"distutils.commands" : [
"%(cmd)s = setuptools.command.%(cmd)s:%(cmd)s" % locals()
@@ -63,9 +63,9 @@
"top_level.txt = setuptools.command.egg_info:write_toplevel_names",
"depends.txt = setuptools.command.egg_info:warn_depends_obsolete",
],
+ "console_scripts":
+ ["easy_install = setuptools.command.easy_install:main"],
},
- # uncomment for testing
- # setup_requires = ['setuptools>=0.6a0'],
classifiers = [f.strip() for f in """
Development Status :: 3 - Alpha
@@ -78,5 +78,46 @@
Topic :: System :: Archiving :: Packaging
Topic :: System :: Systems Administration
Topic :: Utilities""".splitlines() if f.strip()]
+
+
+ # uncomment for testing
+ # setup_requires = ['setuptools>=0.6a0'],
)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Index: setuptools.txt
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/setuptools/setuptools.txt,v
retrieving revision 1.35
retrieving revision 1.36
diff -u -d -r1.35 -r1.36
--- setuptools.txt 3 Sep 2005 04:53:10 -0000 1.35
+++ setuptools.txt 17 Sep 2005 01:13:02 -0000 1.36
@@ -199,7 +199,8 @@
defining the entry points. Entry points are used to support dynamic
discovery of services or plugins provided by a project. See `Dynamic
Discovery of Services and Plugins`_ for details and examples of the format
- of this argument.
+ of this argument. In addition, this keyword is used to support `Automatic
+ Script Creation`_.
``extras_require``
A dictionary mapping names of "extras" (optional features of your project)
@@ -295,6 +296,49 @@
top-level packages or subpackages.
+Automatic Script Creation
+=========================
+
+Packaging and installing scripts can be a bit awkward with the distutils. For
+one thing, there's no easy way to have a script's filename match local
+conventions on both Windows and POSIX platforms. For another, you often have
+to create a separate file just for the "main" script, when your actual "main"
+is a function in a module somewhere. And even in Python 2.4, using the ``-m``
+option only works for actual ``.py`` files that aren't installed in a package.
+
+``setuptools`` fixes all of these problems by automatically generating scripts
+for you with the correct extension, and on Windows it will even create an
+``.exe`` file so that users don't have to change their ``PATHEXT`` settings.
+The way to use this feature is to define "entry points" in your setup script
+that indicate what function the generated script should import and run. For
+example, to create two scripts called ``foo`` and ``bar``, you might do
+something like this::
+
+ setup(
+ # other arguments here...
+ entry_points = {
+ 'console_scripts': [
+ 'foo = my_package.some_module:main_func',
+ 'bar = other_module:some_func',
+ ]
+ }
+ )
+
+When this project is installed on non-Windows platforms (using "setup.py
+install", "setup.py develop", or by using EasyInstall), a pair of ``foo`` and
+``bar`` scripts will be installed that import ``main_func`` and ``some_func``
+from the specified modules. The functions you specify are called with no
+arguments, and their return value is passed to ``sys.exit()``, so you can
+return an errorlevel or message to print to stderr.
+
+You may define as many "console script" entry points as you like, and each one
+can optionally specify "extras" that it depends on, and that will be added to
+``sys.path`` when the script is run. For more information on "extras", see
+section below on `Declaring Extras`_. For more information on "entry points"
+in general, see the section below on `Dynamic Discovery of Services and
+Plugins`_.
+
+
Declaring Dependencies
======================
@@ -350,6 +394,9 @@
using ``setup.py develop``.)
+.. _Declaring Extras:
+
+
Declaring "Extras" (optional features with their own dependencies)
------------------------------------------------------------------
@@ -372,7 +419,33 @@
}
)
-And that project B needs project A, *with* PDF support::
+As you can see, the ``extras_require`` argument takes a dictionary mapping
+names of "extra" features, to strings or lists of strings describing those
+features' requirements. These requirements will *not* be automatically
+installed unless another package depends on them (directly or indirectly) by
+including the desired "extras" in square brackets after the associated project
+name. (Or if the extras were listed in a requirement spec on the EasyInstall
+command line.)
+
+Extras can be used by a project's `entry points`_ to specify dynamic
+dependencies. For example, if Project A includes a "rst2pdf" script, it might
+declare it like this, so that the "PDF" requirements are only resolved if the
+"rst2pdf" script is run::
+
+ setup(
+ name="Project-A",
+ ...
+ entry_points = {
+ 'console_scripts':
+ ['rst2pdf = project_a.tools.pdfgen [PDF]'],
+ ['rst2html = project_a.tools.htmlgen'],
+ # more script entry points ...
+ }
+ )
+
+Projects can also use another project's extras when specifying dependencies.
+For example, if project B needs "project A" with PDF support installed, it
+might declare the dependency like this::
setup(
name="Project-B",
@@ -389,19 +462,11 @@
ReportLab in order to provide PDF support, Project B's setup information does
not need to change, but the right packages will still be installed if needed.
-As you can see, the ``extras_require`` argument takes a dictionary mapping
-names of "extra" features, to strings or lists of strings describing those
-features' requirements. These requirements will *not* be automatically
-installed unless another package depends on them (directly or indirectly) by
-including the desired "extras" in square brackets after the associated project
-name. (Or if the extras were listed in a requirement spec on the EasyInstall
-command line.)
-
Note, by the way, that if a project ends up not needing any other packages to
support a feature, it should keep an empty requirements list for that feature
in its ``extras_require`` argument, so that packages depending on that feature
don't break (due to an invalid feature name). For example, if Project A above
-builds in PDF support and no longer needs ReportLab, it should change its
+builds in PDF support and no longer needs ReportLab, it could change its
setup to this::
setup(
@@ -417,7 +482,6 @@
specifier.
-
Including Data Files
====================
@@ -576,6 +640,8 @@
======================================
+.. _Entry Points:
+
Dynamic Discovery of Services and Plugins
-----------------------------------------
@@ -1776,6 +1842,12 @@
Release Notes/Change History
----------------------------
+0.6a2
+ * Added ``console_scripts`` entry point group to allow installing scripts
+ without the need to create separate script files. On Windows, console
+ scripts get an ``.exe`` wrapper so you can just type their name. On other
+ platforms, the scripts are written without a file extension.
+
0.6a1
* Added support for building "old-style" RPMs that don't install an egg for
the target package, using a ``--no-egg`` option.
More information about the Python-checkins
mailing list