summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjan.nijtmans <nijtmans@users.sourceforge.net>2011-03-22 10:10:05 (GMT)
committerjan.nijtmans <nijtmans@users.sourceforge.net>2011-03-22 10:10:05 (GMT)
commitb9f74bb43e5903dbe3fadd8c4d382c1724ee883f (patch)
treefed1fb5e28145b501c3d3b462be3b590566e964a
parent1f5f6de21bf647360a9f73bfb4dfce60c25c6312 (diff)
parent5117e836e0f5cc70648d831ac6dead709a56dbcb (diff)
downloadtcl-b9f74bb43e5903dbe3fadd8c4d382c1724ee883f.zip
tcl-b9f74bb43e5903dbe3fadd8c4d382c1724ee883f.tar.gz
tcl-b9f74bb43e5903dbe3fadd8c4d382c1724ee883f.tar.bz2
[Bug #3216070] Loading extension libraries from embedded Tcl applications.
-rw-r--r--ChangeLog5
-rw-r--r--unix/tclLoadDl.c20
-rw-r--r--unix/tclLoadDyld.c10
3 files changed, 26 insertions, 9 deletions
diff --git a/ChangeLog b/ChangeLog
index d912365..3284144 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2011-03-21 Jan Nijtmans <nijtmans@users.sf.net>
+
+ * unix/tclLoadDl.c: [Bug #3216070] Loading extension libraries
+ * unix/tclLoadDyld.c: from embedded Tcl applications.
+
2011-03-16 Jan Nijtmans <nijtmans@users.sf.net>
* generic/tclCkalloc.c: [Bug #3197864] pointer truncation on Win64
diff --git a/unix/tclLoadDl.c b/unix/tclLoadDl.c
index e9ff134..e38c280 100644
--- a/unix/tclLoadDl.c
+++ b/unix/tclLoadDl.c
@@ -19,17 +19,17 @@
/*
* In some systems, like SunOS 4.1.3, the RTLD_NOW flag isn't defined and this
- * argument to dlopen must always be 1. The RTLD_GLOBAL flag is needed on some
- * systems (e.g. SCO and UnixWare) but doesn't exist on others; if it doesn't
- * exist, set it to 0 so it has no effect.
+ * argument to dlopen must always be 1. The RTLD_LOCAL flag doesn't exist on
+ * some platforms; if it doesn't exist, set it to 0 so it has no effect.
+ * See [Bug #3216070]
*/
#ifndef RTLD_NOW
# define RTLD_NOW 1
#endif
-#ifndef RTLD_GLOBAL
-# define RTLD_GLOBAL 0
+#ifndef RTLD_LOCAL
+# define RTLD_LOCAL 0
#endif
/*
@@ -73,7 +73,10 @@ TclpDlopen(
*/
native = Tcl_FSGetNativePath(pathPtr);
- handle = dlopen(native, RTLD_NOW | RTLD_GLOBAL);
+ /*
+ * Use (RTLD_NOW|RTLD_LOCAL) always, see [Bug #3216070]
+ */
+ handle = dlopen(native, RTLD_NOW | RTLD_LOCAL);
if (handle == NULL) {
/*
* Let the OS loader examine the binary search path for whatever
@@ -85,7 +88,10 @@ TclpDlopen(
char *fileName = Tcl_GetString(pathPtr);
native = Tcl_UtfToExternalDString(NULL, fileName, -1, &ds);
- handle = dlopen(native, RTLD_NOW | RTLD_GLOBAL);
+ /*
+ * Use (RTLD_NOW|RTLD_LOCAL) always, see [Bug #3216070]
+ */
+ handle = dlopen(native, RTLD_NOW | RTLD_LOCAL);
Tcl_DStringFree(&ds);
}
diff --git a/unix/tclLoadDyld.c b/unix/tclLoadDyld.c
index 08d651c..0a36215 100644
--- a/unix/tclLoadDyld.c
+++ b/unix/tclLoadDyld.c
@@ -196,7 +196,10 @@ TclpDlopen(
if (tclMacOSXDarwinRelease >= 8)
#endif
{
- dlHandle = dlopen(nativePath, RTLD_NOW | RTLD_GLOBAL);
+ /*
+ * Use (RTLD_NOW|RTLD_LOCAL) always, see [Bug #3216070]
+ */
+ dlHandle = dlopen(nativePath, RTLD_NOW | RTLD_LOCAL);
if (!dlHandle) {
/*
* Let the OS loader examine the binary search path for whatever
@@ -206,7 +209,10 @@ TclpDlopen(
fileName = Tcl_GetString(pathPtr);
nativeFileName = Tcl_UtfToExternalDString(NULL, fileName, -1, &ds);
- dlHandle = dlopen(nativeFileName, RTLD_NOW | RTLD_GLOBAL);
+ /*
+ * Use (RTLD_NOW|RTLD_LOCAL) always, see [Bug #3216070]
+ */
+ dlHandle = dlopen(nativeFileName, RTLD_NOW | RTLD_LOCAL);
}
if (dlHandle) {
TclLoadDbgMsg("dlopen() successful");
t'>111
-rw-r--r--Doc/library/packaging.compiler.rst681
-rw-r--r--Doc/library/packaging.database.rst324
-rw-r--r--Doc/library/packaging.depgraph.rst199
-rw-r--r--Doc/library/packaging.dist.rst108
-rw-r--r--Doc/library/packaging.fancy_getopt.rst75
-rw-r--r--Doc/library/packaging.install.rst112
-rw-r--r--Doc/library/packaging.metadata.rst122
-rw-r--r--Doc/library/packaging.pypi.dist.rst114
-rw-r--r--Doc/library/packaging.pypi.rst74
-rw-r--r--Doc/library/packaging.pypi.simple.rst218
-rw-r--r--Doc/library/packaging.pypi.xmlrpc.rst143
-rw-r--r--Doc/library/packaging.rst75
-rw-r--r--Doc/library/packaging.tests.pypi_server.rst105
-rw-r--r--Doc/library/packaging.util.rst155
-rw-r--r--Doc/library/packaging.version.rst104
-rw-r--r--Doc/library/platform.rst4
-rw-r--r--Doc/library/python.rst1
-rw-r--r--Doc/library/random.rst6
-rw-r--r--Doc/library/re.rst9
-rw-r--r--Doc/library/readline.rst4
-rw-r--r--Doc/library/resource.rst8
-rw-r--r--Doc/library/sched.rst65
-rw-r--r--Doc/library/select.rst97
-rw-r--r--Doc/library/shlex.rst36
-rw-r--r--Doc/library/shutil.rst28
-rw-r--r--Doc/library/signal.rst165
-rw-r--r--Doc/library/site.rst26
-rw-r--r--Doc/library/smtplib.rst62
-rw-r--r--Doc/library/socket.rst377
-rw-r--r--Doc/library/socketserver.rst15
-rw-r--r--Doc/library/sqlite3.rst16
-rw-r--r--Doc/library/ssl.rst143
-rw-r--r--Doc/library/stdtypes.rst49
-rw-r--r--Doc/library/struct.rst21
-rw-r--r--Doc/library/subprocess.rst151
-rw-r--r--Doc/library/sys.rst136
-rw-r--r--Doc/library/tarfile.rst31
-rw-r--r--Doc/library/telnetlib.rst6
-rw-r--r--Doc/library/tempfile.rst2
-rw-r--r--Doc/library/test.rst124
-rw-r--r--Doc/library/threading.rst56
-rw-r--r--Doc/library/time.rst96
-rw-r--r--Doc/library/tokenize.rst57
-rw-r--r--Doc/library/unicodedata.rst9
-rw-r--r--Doc/library/unittest.rst50
-rw-r--r--Doc/library/urllib.error.rst12
-rw-r--r--Doc/library/urllib.request.rst63
-rw-r--r--Doc/library/warnings.rst3
-rw-r--r--Doc/library/winreg.rst48
-rw-r--r--Doc/library/xmlrpc.client.rst53
-rw-r--r--Doc/library/xmlrpc.server.rst28
-rw-r--r--Doc/library/zipfile.rst2
-rw-r--r--Doc/library/zipimport.rst5
-rw-r--r--Doc/library/zlib.rst43
-rw-r--r--Doc/license.rst2
-rw-r--r--Doc/packaging/builtdist.rst302
-rw-r--r--Doc/packaging/commandhooks.rst47
-rw-r--r--Doc/packaging/commandref.rst374
-rw-r--r--Doc/packaging/configfile.rst125
-rw-r--r--Doc/packaging/examples.rst334
-rw-r--r--Doc/packaging/extending.rst95
-rw-r--r--Doc/packaging/index.rst45
-rw-r--r--Doc/packaging/introduction.rst193
-rw-r--r--Doc/packaging/packageindex.rst104
-rw-r--r--Doc/packaging/setupcfg.rst890
-rw-r--r--Doc/packaging/setupscript.rst693
-rw-r--r--Doc/packaging/sourcedist.rst266
-rw-r--r--Doc/packaging/tutorial.rst112
-rw-r--r--Doc/packaging/uploading.rst80
-rw-r--r--Doc/reference/datamodel.rst28
-rw-r--r--Doc/reference/lexical_analysis.rst17
-rw-r--r--Doc/tools/sphinxext/indexcontent.html8
-rw-r--r--Doc/tools/sphinxext/indexsidebar.html2
-rw-r--r--Doc/tools/sphinxext/pyspecific.py2
-rw-r--r--Doc/tools/sphinxext/susp-ignored.csv119
-rw-r--r--Doc/tutorial/classes.rst4
-rw-r--r--Doc/tutorial/datastructures.rst24
-rw-r--r--Doc/tutorial/interpreter.rst14
-rw-r--r--Doc/tutorial/stdlib.rst2
-rw-r--r--Doc/tutorial/stdlib2.rst6
-rw-r--r--Doc/using/cmdline.rst16
-rw-r--r--Doc/whatsnew/3.2.rst8
-rw-r--r--Doc/whatsnew/3.3.rst884
-rw-r--r--Doc/whatsnew/index.rst1
-rw-r--r--Grammar/Grammar2
-rw-r--r--Include/Python-ast.h46
-rw-r--r--Include/Python.h38
-rw-r--r--Include/abstract.h12
-rw-r--r--Include/asdl.h1
-rw-r--r--Include/ast.h1
-rw-r--r--Include/code.h6
-rw-r--r--Include/codecs.h2
-rw-r--r--Include/compile.h14
-rw-r--r--Include/complexobject.h5
-rw-r--r--Include/descrobject.h1
-rw-r--r--Include/dictobject.h2
-rw-r--r--Include/floatobject.h17
-rw-r--r--Include/funcobject.h2
-rw-r--r--Include/import.h29
-rw-r--r--Include/intrcheck.h6
-rw-r--r--Include/listobject.h2
-rw-r--r--Include/longobject.h16
-rw-r--r--Include/memoryobject.h1
-rw-r--r--Include/moduleobject.h4
-rw-r--r--Include/object.h12
-rw-r--r--Include/opcode.h1
-rw-r--r--Include/parsetok.h9
-rw-r--r--Include/patchlevel.h12
-rw-r--r--Include/py_curses.h1
-rw-r--r--Include/pydebug.h3
-rw-r--r--Include/pyerrors.h62
-rw-r--r--Include/pymacro.h55
-rw-r--r--Include/pymath.h6
-rw-r--r--Include/pyport.h2
-rw-r--r--Include/pystate.h10
-rw-r--r--Include/pythonrun.h4
-rw-r--r--Include/pythread.h4
-rw-r--r--Include/setobject.h2
-rw-r--r--Include/symtable.h11
-rw-r--r--Include/traceback.h38
-rw-r--r--Include/ucnhash.h6
-rw-r--r--Include/unicodeobject.h994
-rw-r--r--LICENSE1
-rw-r--r--Lib/_dummy_thread.py6
-rw-r--r--Lib/_pyio.py105
-rw-r--r--Lib/abc.py27
-rw-r--r--Lib/argparse.py50
-rw-r--r--Lib/ast.py1
-rw-r--r--Lib/asynchat.py2
-rw-r--r--Lib/asyncore.py18
-rw-r--r--Lib/binhex.py1
-rw-r--r--Lib/bz2.py413
-rwxr-xr-xLib/cgi.py13
-rw-r--r--Lib/cgitb.py1
-rw-r--r--Lib/codecs.py14
-rw-r--r--Lib/collections/__init__.py (renamed from Lib/collections.py)98
-rw-r--r--Lib/collections/abc.py (renamed from Lib/_abcoll.py)37
-rw-r--r--Lib/concurrent/futures/_base.py23
-rw-r--r--Lib/concurrent/futures/process.py142
-rw-r--r--Lib/concurrent/futures/thread.py2
-rw-r--r--Lib/configparser.py3
-rw-r--r--Lib/contextlib.py1
-rw-r--r--Lib/copy.py77
-rw-r--r--Lib/crypt.py62
-rw-r--r--Lib/ctypes/test/test_callbacks.py2
-rw-r--r--Lib/ctypes/test/test_memfunctions.py2
-rw-r--r--Lib/ctypes/test/test_parameters.py9
-rw-r--r--Lib/ctypes/test/test_python_api.py3
-rw-r--r--Lib/ctypes/test/test_refcounts.py3
-rw-r--r--Lib/ctypes/test/test_stringptr.py2
-rw-r--r--Lib/ctypes/util.py46
-rw-r--r--Lib/curses/__init__.py46
-rw-r--r--Lib/curses/wrapper.py50
-rw-r--r--Lib/datetime.py4
-rw-r--r--Lib/decimal.py143
-rw-r--r--Lib/difflib.py23
-rw-r--r--Lib/distutils/__init__.py2
-rw-r--r--Lib/distutils/command/bdist_wininst.py6
-rw-r--r--Lib/distutils/command/build_ext.py3
-rw-r--r--Lib/distutils/command/build_scripts.py7
-rw-r--r--Lib/distutils/tests/test_bdist_rpm.py9
-rw-r--r--Lib/doctest.py9
-rw-r--r--Lib/email/_parseaddr.py27
-rw-r--r--Lib/email/errors.py5
-rw-r--r--Lib/email/feedparser.py31
-rw-r--r--Lib/email/generator.py66
-rw-r--r--Lib/email/parser.py43
-rw-r--r--Lib/email/policy.py174
-rw-r--r--Lib/email/utils.py74
-rw-r--r--Lib/encodings/cp65001.py40
-rw-r--r--Lib/encodings/idna.py22
-rw-r--r--Lib/fileinput.py3
-rw-r--r--Lib/fnmatch.py10
-rw-r--r--Lib/ftplib.py107
-rw-r--r--Lib/functools.py30
-rw-r--r--Lib/getopt.py19
-rw-r--r--Lib/getpass.py4
-rw-r--r--Lib/gzip.py34
-rw-r--r--Lib/http/client.py167
-rw-r--r--Lib/http/server.py50
-rw-r--r--Lib/idlelib/PyShell.py4
-rw-r--r--Lib/idlelib/configHandler.py3
-rw-r--r--Lib/idlelib/idlever.py2
-rw-r--r--Lib/imaplib.py33
-rw-r--r--Lib/importlib/__init__.py3
-rw-r--r--Lib/importlib/_bootstrap.py69
-rw-r--r--Lib/importlib/abc.py4
-rw-r--r--Lib/importlib/test/__main__.py7
-rw-r--r--Lib/importlib/test/regrtest.py7
-rw-r--r--Lib/importlib/test/source/test_abc_loader.py4
-rw-r--r--Lib/importlib/test/test_util.py10
-rw-r--r--Lib/inspect.py127
-rw-r--r--Lib/json/decoder.py3
-rw-r--r--Lib/lib2to3/__main__.py4
-rw-r--r--Lib/lib2to3/fixer_base.py4
-rw-r--r--Lib/lib2to3/refactor.py4
-rw-r--r--Lib/logging/__init__.py166
-rw-r--r--Lib/logging/config.py18
-rw-r--r--Lib/logging/handlers.py80
-rw-r--r--Lib/lzma.py398
-rw-r--r--Lib/mailbox.py7
-rw-r--r--Lib/mailcap.py6
-rw-r--r--Lib/mimetypes.py1
-rw-r--r--Lib/modulefinder.py16
-rw-r--r--Lib/multiprocessing/connection.py404
-rw-r--r--Lib/multiprocessing/dummy/__init__.py15
-rw-r--r--Lib/multiprocessing/forking.py46
-rw-r--r--Lib/multiprocessing/heap.py1
-rw-r--r--Lib/multiprocessing/managers.py6
-rw-r--r--Lib/multiprocessing/process.py20
-rw-r--r--Lib/multiprocessing/queues.py20
-rw-r--r--Lib/multiprocessing/reduction.py36
-rw-r--r--Lib/multiprocessing/sharedctypes.py1
-rw-r--r--Lib/multiprocessing/synchronize.py5
-rw-r--r--Lib/multiprocessing/util.py24
-rw-r--r--Lib/nntplib.py18
-rw-r--r--Lib/numbers.py14
-rw-r--r--Lib/opcode.py1
-rw-r--r--Lib/optparse.py31
-rw-r--r--Lib/os.py2
-rw-r--r--Lib/packaging/__init__.py17
-rw-r--r--Lib/packaging/_trove.py571
-rw-r--r--Lib/packaging/command/__init__.py63
-rw-r--r--Lib/packaging/command/bdist.py141
-rw-r--r--Lib/packaging/command/bdist_dumb.py139
-rw-r--r--Lib/packaging/command/bdist_msi.py744
-rw-r--r--Lib/packaging/command/bdist_wininst.py345
-rw-r--r--Lib/packaging/command/build.py151
-rw-r--r--Lib/packaging/command/build_clib.py197
-rw-r--r--Lib/packaging/command/build_ext.py642
-rw-r--r--Lib/packaging/command/build_py.py392
-rw-r--r--Lib/packaging/command/build_scripts.py154
-rw-r--r--Lib/packaging/command/check.py88
-rw-r--r--Lib/packaging/command/clean.py76
-rw-r--r--Lib/packaging/command/cmd.py461
-rw-r--r--Lib/packaging/command/command_template35
-rw-r--r--Lib/packaging/command/config.py349
-rw-r--r--Lib/packaging/command/install_data.py79
-rw-r--r--Lib/packaging/command/install_dist.py605
-rw-r--r--Lib/packaging/command/install_distinfo.py143
-rw-r--r--Lib/packaging/command/install_headers.py43
-rw-r--r--Lib/packaging/command/install_lib.py188
-rw-r--r--Lib/packaging/command/install_scripts.py59
-rw-r--r--Lib/packaging/command/register.py263
-rw-r--r--Lib/packaging/command/sdist.py347
-rw-r--r--Lib/packaging/command/test.py80
-rw-r--r--Lib/packaging/command/upload.py168
-rw-r--r--Lib/packaging/command/upload_docs.py131
-rw-r--r--Lib/packaging/command/wininst-10.0-amd64.exebin0 -> 222208 bytes-rw-r--r--Lib/packaging/command/wininst-10.0.exebin0 -> 190464 bytes-rw-r--r--Lib/packaging/command/wininst-6.0.exebin0 -> 61440 bytes-rw-r--r--Lib/packaging/command/wininst-7.1.exebin0 -> 65536 bytes-rw-r--r--Lib/packaging/command/wininst-8.0.exebin0 -> 61440 bytes-rw-r--r--Lib/packaging/command/wininst-9.0-amd64.exebin0 -> 223744 bytes-rw-r--r--Lib/packaging/command/wininst-9.0.exebin0 -> 196096 bytes-rw-r--r--Lib/packaging/compat.py51
-rw-r--r--Lib/packaging/compiler/__init__.py274
-rw-r--r--Lib/packaging/compiler/bcppcompiler.py355
-rw-r--r--Lib/packaging/compiler/ccompiler.py863
-rw-r--r--Lib/packaging/compiler/cygwinccompiler.py351
-rw-r--r--Lib/packaging/compiler/extension.py121
-rw-r--r--Lib/packaging/compiler/msvc9compiler.py720
-rw-r--r--Lib/packaging/compiler/msvccompiler.py635
-rw-r--r--Lib/packaging/compiler/unixccompiler.py339
-rw-r--r--Lib/packaging/config.py380
-rw-r--r--Lib/packaging/create.py676
-rw-r--r--Lib/packaging/database.py650
-rw-r--r--Lib/packaging/depgraph.py270
-rw-r--r--Lib/packaging/dist.py769
-rw-r--r--Lib/packaging/errors.py138
-rw-r--r--Lib/packaging/fancy_getopt.py388
-rw-r--r--Lib/packaging/install.py529
-rw-r--r--Lib/packaging/manifest.py377
-rw-r--r--Lib/packaging/markers.py187
-rw-r--r--Lib/packaging/metadata.py570
-rw-r--r--Lib/packaging/pypi/__init__.py9
-rw-r--r--Lib/packaging/pypi/base.py48
-rw-r--r--Lib/packaging/pypi/dist.py544
-rw-r--r--Lib/packaging/pypi/errors.py39
-rw-r--r--Lib/packaging/pypi/mirrors.py52
-rw-r--r--Lib/packaging/pypi/simple.py462
-rw-r--r--Lib/packaging/pypi/wrapper.py99
-rw-r--r--Lib/packaging/pypi/xmlrpc.py200
-rw-r--r--Lib/packaging/run.py666
-rw-r--r--Lib/packaging/tests/LONG_DESC.txt44
-rw-r--r--Lib/packaging/tests/PKG-INFO57
-rw-r--r--Lib/packaging/tests/SETUPTOOLS-PKG-INFO182
-rw-r--r--Lib/packaging/tests/SETUPTOOLS-PKG-INFO2183
-rw-r--r--Lib/packaging/tests/__init__.py28
-rw-r--r--Lib/packaging/tests/__main__.py24
-rw-r--r--Lib/packaging/tests/fake_dists/babar-0.1.dist-info/INSTALLER (renamed from Lib/email/test/__init__.py)0
-rw-r--r--Lib/packaging/tests/fake_dists/babar-0.1.dist-info/METADATA4
-rw-r--r--Lib/packaging/tests/fake_dists/babar-0.1.dist-info/RECORD0
-rw-r--r--Lib/packaging/tests/fake_dists/babar-0.1.dist-info/REQUESTED0
-rw-r--r--Lib/packaging/tests/fake_dists/babar-0.1.dist-info/RESOURCES2
-rw-r--r--Lib/packaging/tests/fake_dists/babar.cfg1
-rw-r--r--Lib/packaging/tests/fake_dists/babar.png0
-rw-r--r--Lib/packaging/tests/fake_dists/bacon-0.1.egg-info/PKG-INFO6
-rw-r--r--Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/PKG-INFO18
-rw-r--r--Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/SOURCES.txt0
-rw-r--r--Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/dependency_links.txt1
-rw-r--r--Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/entry_points.txt3
-rw-r--r--Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/not-zip-safe1
-rw-r--r--Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/requires.txt6
-rw-r--r--Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/top_level.txt0
-rw-r--r--Lib/packaging/tests/fake_dists/cheese-2.0.2.egg-info5
-rw-r--r--Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/INSTALLER0
-rw-r--r--Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/METADATA9
-rw-r--r--Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/RECORD0
-rw-r--r--Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/REQUESTED0
-rw-r--r--Lib/packaging/tests/fake_dists/choxie-2.0.0.9/choxie/__init__.py1
-rw-r--r--Lib/packaging/tests/fake_dists/choxie-2.0.0.9/choxie/chocolate.py10
-rw-r--r--Lib/packaging/tests/fake_dists/choxie-2.0.0.9/truffles.py5
-rw-r--r--Lib/packaging/tests/fake_dists/coconuts-aster-10.3.egg-info/PKG-INFO5
-rw-r--r--Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/INSTALLER0
-rw-r--r--Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/METADATA5
-rw-r--r--Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/RECORD0
-rw-r--r--Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/REQUESTED0
-rw-r--r--Lib/packaging/tests/fake_dists/grammar-1.0a4/grammar/__init__.py1
-rw-r--r--Lib/packaging/tests/fake_dists/grammar-1.0a4/grammar/utils.py8
-rw-r--r--Lib/packaging/tests/fake_dists/nut-funkyversion.egg-info3
-rw-r--r--Lib/packaging/tests/fake_dists/strawberry-0.6.eggbin0 -> 1402 bytes-rw-r--r--Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/INSTALLER0
-rw-r--r--Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/METADATA7
-rw-r--r--Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/RECORD0
-rw-r--r--Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/REQUESTED0
-rw-r--r--Lib/packaging/tests/fake_dists/towel_stuff-0.1/towel_stuff/__init__.py18
-rw-r--r--Lib/packaging/tests/fake_dists/truffles-5.0.egg-info3
-rw-r--r--Lib/packaging/tests/fixer/__init__.py0
-rw-r--r--Lib/packaging/tests/fixer/fix_idioms.py134
-rw-r--r--Lib/packaging/tests/pypi_server.py449
-rw-r--r--Lib/packaging/tests/pypi_test_server.py59
-rw-r--r--Lib/packaging/tests/pypiserver/downloads_with_md5/packages/source/f/foobar/foobar-0.1.tar.gzbin0 -> 110 bytes-rw-r--r--Lib/packaging/tests/pypiserver/downloads_with_md5/simple/badmd5/badmd5-0.1.tar.gz0
-rw-r--r--Lib/packaging/tests/pypiserver/downloads_with_md5/simple/badmd5/index.html3
-rw-r--r--Lib/packaging/tests/pypiserver/downloads_with_md5/simple/foobar/index.html3
-rw-r--r--Lib/packaging/tests/pypiserver/downloads_with_md5/simple/index.html2
-rw-r--r--Lib/packaging/tests/pypiserver/foo_bar_baz/simple/bar/index.html6
-rw-r--r--Lib/packaging/tests/pypiserver/foo_bar_baz/simple/baz/index.html6
-rw-r--r--Lib/packaging/tests/pypiserver/foo_bar_baz/simple/foo/index.html6
-rw-r--r--Lib/packaging/tests/pypiserver/foo_bar_baz/simple/index.html3
-rw-r--r--Lib/packaging/tests/pypiserver/project_list/simple/index.html5
-rw-r--r--Lib/packaging/tests/pypiserver/test_found_links/simple/foobar/index.html6
-rw-r--r--Lib/packaging/tests/pypiserver/test_found_links/simple/index.html1
-rw-r--r--Lib/packaging/tests/pypiserver/test_pypi_server/external/index.html1
-rw-r--r--Lib/packaging/tests/pypiserver/test_pypi_server/simple/index.html1
-rw-r--r--Lib/packaging/tests/pypiserver/with_externals/external/external.html3
-rw-r--r--Lib/packaging/tests/pypiserver/with_externals/simple/foobar/index.html4
-rw-r--r--Lib/packaging/tests/pypiserver/with_externals/simple/index.html1
-rw-r--r--Lib/packaging/tests/pypiserver/with_norel_links/external/homepage.html7
-rw-r--r--Lib/packaging/tests/pypiserver/with_norel_links/external/nonrel.html1
-rw-r--r--Lib/packaging/tests/pypiserver/with_norel_links/simple/foobar/index.html6
-rw-r--r--Lib/packaging/tests/pypiserver/with_norel_links/simple/index.html1
-rw-r--r--Lib/packaging/tests/pypiserver/with_real_externals/simple/foobar/index.html4
-rw-r--r--Lib/packaging/tests/pypiserver/with_real_externals/simple/index.html1
-rw-r--r--Lib/packaging/tests/support.py392
-rw-r--r--Lib/packaging/tests/test_ccompiler.py15
-rw-r--r--Lib/packaging/tests/test_command_bdist.py61
-rw-r--r--Lib/packaging/tests/test_command_bdist_dumb.py91
-rw-r--r--Lib/packaging/tests/test_command_bdist_msi.py25
-rw-r--r--Lib/packaging/tests/test_command_bdist_wininst.py32
-rw-r--r--Lib/packaging/tests/test_command_build.py55
-rw-r--r--Lib/packaging/tests/test_command_build_clib.py141
-rw-r--r--Lib/packaging/tests/test_command_build_ext.py393
-rw-r--r--Lib/packaging/tests/test_command_build_py.py136
-rw-r--r--Lib/packaging/tests/test_command_build_scripts.py109
-rw-r--r--Lib/packaging/tests/test_command_check.py161
-rw-r--r--Lib/packaging/tests/test_command_clean.py46
-rw-r--r--Lib/packaging/tests/test_command_cmd.py102
-rw-r--r--Lib/packaging/tests/test_command_config.py76
-rw-r--r--Lib/packaging/tests/test_command_install_data.py148
-rw-r--r--Lib/packaging/tests/test_command_install_dist.py241
-rw-r--r--Lib/packaging/tests/test_command_install_distinfo.py252
-rw-r--r--Lib/packaging/tests/test_command_install_headers.py38
-rw-r--r--Lib/packaging/tests/test_command_install_lib.py110
-rw-r--r--Lib/packaging/tests/test_command_install_scripts.py75
-rw-r--r--Lib/packaging/tests/test_command_register.py260
-rw-r--r--Lib/packaging/tests/test_command_sdist.py395
-rw-r--r--Lib/packaging/tests/test_command_test.py224
-rw-r--r--Lib/packaging/tests/test_command_upload.py159
-rw-r--r--Lib/packaging/tests/test_command_upload_docs.py186
-rw-r--r--Lib/packaging/tests/test_compiler.py66
-rw-r--r--Lib/packaging/tests/test_config.py496
-rw-r--r--Lib/packaging/tests/test_create.py234
-rw-r--r--Lib/packaging/tests/test_cygwinccompiler.py88
-rw-r--r--Lib/packaging/tests/test_database.py686
-rw-r--r--Lib/packaging/tests/test_depgraph.py310
-rw-r--r--Lib/packaging/tests/test_dist.py264
-rw-r--r--Lib/packaging/tests/test_extension.py15
-rw-r--r--Lib/packaging/tests/test_install.py391
-rw-r--r--Lib/packaging/tests/test_manifest.py263
-rw-r--r--Lib/packaging/tests/test_markers.py71
-rw-r--r--Lib/packaging/tests/test_metadata.py454
-rw-r--r--Lib/packaging/tests/test_mixin2to3.py81
-rw-r--r--Lib/packaging/tests/test_msvc9compiler.py140
-rw-r--r--Lib/packaging/tests/test_pypi_dist.py287
-rw-r--r--Lib/packaging/tests/test_pypi_server.py88
-rw-r--r--Lib/packaging/tests/test_pypi_simple.py353
-rw-r--r--Lib/packaging/tests/test_pypi_xmlrpc.py101
-rw-r--r--Lib/packaging/tests/test_run.py75
-rw-r--r--Lib/packaging/tests/test_support.py78
-rw-r--r--Lib/packaging/tests/test_uninstall.py124
-rw-r--r--Lib/packaging/tests/test_unixccompiler.py132
-rw-r--r--Lib/packaging/tests/test_util.py1008
-rw-r--r--Lib/packaging/tests/test_version.py262
-rw-r--r--Lib/packaging/util.py1480
-rw-r--r--Lib/packaging/version.py449
-rw-r--r--Lib/pickle.py6
-rw-r--r--Lib/pickletools.py7
-rw-r--r--Lib/pipes.py23
-rw-r--r--Lib/pkgutil.py8
-rw-r--r--Lib/plat-linux/CDROM.py (renamed from Lib/plat-linux2/CDROM.py)0
-rw-r--r--Lib/plat-linux/DLFCN.py (renamed from Lib/plat-linux2/DLFCN.py)0
-rw-r--r--Lib/plat-linux/IN.py (renamed from Lib/plat-linux2/IN.py)0
-rw-r--r--Lib/plat-linux/TYPES.py (renamed from Lib/plat-linux2/TYPES.py)0
-rwxr-xr-xLib/plat-linux/regen (renamed from Lib/plat-linux2/regen)0
-rwxr-xr-xLib/platform.py110
-rw-r--r--Lib/plistlib.py10
-rw-r--r--Lib/poplib.py17
-rw-r--r--Lib/pstats.py1
-rwxr-xr-xLib/pydoc.py289
-rw-r--r--Lib/pydoc_data/topics.py20
-rw-r--r--Lib/random.py2
-rw-r--r--Lib/re.py15
-rw-r--r--Lib/sched.py78
-rw-r--r--Lib/shlex.py20
-rw-r--r--Lib/shutil.py85
-rw-r--r--Lib/site.py17
-rwxr-xr-xLib/smtpd.py46
-rw-r--r--Lib/smtplib.py152
-rw-r--r--Lib/socket.py11
-rw-r--r--Lib/socketserver.py20
-rw-r--r--Lib/sqlite3/test/hooks.py52
-rw-r--r--Lib/sqlite3/test/regression.py22
-rw-r--r--Lib/sqlite3/test/types.py2
-rw-r--r--Lib/sre_compile.py4
-rw-r--r--Lib/sre_parse.py2
-rw-r--r--Lib/ssl.py55
-rw-r--r--Lib/string.py24
-rw-r--r--Lib/subprocess.py685
-rw-r--r--Lib/sysconfig.cfg111
-rw-r--r--Lib/sysconfig.py305
-rwxr-xr-xLib/tabnanny.py3
-rw-r--r--Lib/tarfile.py158
-rw-r--r--Lib/tempfile.py56
-rw-r--r--Lib/test/buffer_tests.py8
-rw-r--r--Lib/test/crashers/README4
-rw-r--r--Lib/test/crashers/compiler_recursion.py12
-rw-r--r--Lib/test/datetimetester.py18
-rw-r--r--Lib/test/decimaltestdata/extra.decTest13
-rw-r--r--Lib/test/exception_hierarchy.txt21
-rw-r--r--Lib/test/fork_wait.py10
-rw-r--r--Lib/test/future_test1.py (renamed from Lib/test/test_future1.py)0
-rw-r--r--Lib/test/future_test2.py (renamed from Lib/test/test_future2.py)0
-rw-r--r--Lib/test/json_tests/test_dump.py19
-rw-r--r--Lib/test/json_tests/test_scanstring.py11
-rw-r--r--Lib/test/keycert.passwd.pem33
-rw-r--r--Lib/test/list_tests.py41
-rw-r--r--Lib/test/lock_tests.py6
-rw-r--r--Lib/test/mailcap.txt39
-rw-r--r--Lib/test/math_testcases.txt114
-rw-r--r--Lib/test/mock_socket.py3
-rw-r--r--Lib/test/pickletester.py39
-rwxr-xr-xLib/test/regrtest.py287
-rw-r--r--Lib/test/script_helper.py5
-rw-r--r--Lib/test/ssl_key.passwd.pem18
-rw-r--r--Lib/test/ssl_servers.py6
-rw-r--r--Lib/test/string_tests.py49
-rw-r--r--Lib/test/support.py314
-rw-r--r--Lib/test/test__locale.py36
-rw-r--r--Lib/test/test_abc.py196
-rw-r--r--Lib/test/test_abstract_numbers.py2
-rw-r--r--Lib/test/test_argparse.py94
-rwxr-xr-xLib/test/test_array.py32
-rw-r--r--Lib/test/test_ast.py419
-rw-r--r--Lib/test/test_asyncore.py178
-rw-r--r--Lib/test/test_bigmem.py178
-rw-r--r--Lib/test/test_bool.py10
-rw-r--r--Lib/test/test_builtin.py44
-rw-r--r--Lib/test/test_bytes.py180
-rw-r--r--Lib/test/test_bz2.py408
-rw-r--r--Lib/test/test_calendar.py2
-rw-r--r--Lib/test/test_capi.py5
-rw-r--r--Lib/test/test_cgi.py8
-rw-r--r--Lib/test/test_cgitb.py55
-rw-r--r--Lib/test/test_cmd_line.py12
-rw-r--r--Lib/test/test_code.py2
-rw-r--r--Lib/test/test_codeccallbacks.py159
-rw-r--r--Lib/test/test_codecencodings_cn.py21
-rw-r--r--Lib/test/test_codecencodings_hk.py4
-rw-r--r--Lib/test/test_codecencodings_jp.py96
-rw-r--r--Lib/test/test_codecencodings_kr.py25
-rw-r--r--Lib/test/test_codecencodings_tw.py4
-rw-r--r--Lib/test/test_codecmaps_tw.py3
-rw-r--r--Lib/test/test_codecs.py401
-rw-r--r--Lib/test/test_collections.py96
-rw-r--r--Lib/test/test_compile.py9
-rw-r--r--Lib/test/test_concurrent_futures.py62
-rw-r--r--Lib/test/test_configparser.py (renamed from Lib/test/test_cfgparser.py)0
-rw-r--r--Lib/test/test_copy.py187
-rw-r--r--Lib/test/test_cprofile.py23
-rw-r--r--Lib/test/test_crashers.py38
-rw-r--r--Lib/test/test_crypt.py19
-rw-r--r--Lib/test/test_curses.py52
-rw-r--r--Lib/test/test_dbm.py4
-rw-r--r--Lib/test/test_decimal.py26
-rw-r--r--Lib/test/test_descr.py106
-rw-r--r--Lib/test/test_descrtut.py3
-rw-r--r--Lib/test/test_devpoll.py94
-rw-r--r--Lib/test/test_dis.py107
-rw-r--r--Lib/test/test_doctest.py444
-rw-r--r--Lib/test/test_dummy_thread.py4
-rw-r--r--Lib/test/test_email.py14
-rw-r--r--Lib/test/test_email/__init__.py45
-rw-r--r--Lib/test/test_email/__main__.py3
-rw-r--r--Lib/test/test_email/data/PyBanner048.gif (renamed from Lib/email/test/data/PyBanner048.gif)bin954 -> 954 bytes-rw-r--r--Lib/test/test_email/data/audiotest.au (renamed from Lib/email/test/data/audiotest.au)bin28144 -> 28144 bytes-rw-r--r--Lib/test/test_email/data/msg_01.txt (renamed from Lib/email/test/data/msg_01.txt)0
-rw-r--r--Lib/test/test_email/data/msg_02.txt (renamed from Lib/email/test/data/msg_02.txt)0
-rw-r--r--Lib/test/test_email/data/msg_03.txt (renamed from Lib/email/test/data/msg_03.txt)0
-rw-r--r--Lib/test/test_email/data/msg_04.txt (renamed from Lib/email/test/data/msg_04.txt)0
-rw-r--r--Lib/test/test_email/data/msg_05.txt (renamed from Lib/email/test/data/msg_05.txt)0
-rw-r--r--Lib/test/test_email/data/msg_06.txt (renamed from Lib/email/test/data/msg_06.txt)0
-rw-r--r--Lib/test/test_email/data/msg_07.txt (renamed from Lib/email/test/data/msg_07.txt)0
-rw-r--r--Lib/test/test_email/data/msg_08.txt (renamed from Lib/email/test/data/msg_08.txt)0
-rw-r--r--Lib/test/test_email/data/msg_09.txt (renamed from Lib/email/test/data/msg_09.txt)0
-rw-r--r--Lib/test/test_email/data/msg_10.txt (renamed from Lib/email/test/data/msg_10.txt)0
-rw-r--r--Lib/test/test_email/data/msg_11.txt (renamed from Lib/email/test/data/msg_11.txt)0
-rw-r--r--Lib/test/test_email/data/msg_12.txt (renamed from Lib/email/test/data/msg_12.txt)0
-rw-r--r--Lib/test/test_email/data/msg_12a.txt (renamed from Lib/email/test/data/msg_12a.txt)0
-rw-r--r--Lib/test/test_email/data/msg_13.txt (renamed from Lib/email/test/data/msg_13.txt)0
-rw-r--r--Lib/test/test_email/data/msg_14.txt (renamed from Lib/email/test/data/msg_14.txt)0
-rw-r--r--Lib/test/test_email/data/msg_15.txt (renamed from Lib/email/test/data/msg_15.txt)0
-rw-r--r--Lib/test/test_email/data/msg_16.txt (renamed from Lib/email/test/data/msg_16.txt)0
-rw-r--r--Lib/test/test_email/data/msg_17.txt (renamed from Lib/email/test/data/msg_17.txt)0
-rw-r--r--Lib/test/test_email/data/msg_18.txt (renamed from Lib/email/test/data/msg_18.txt)0
-rw-r--r--Lib/test/test_email/data/msg_19.txt (renamed from Lib/email/test/data/msg_19.txt)0
-rw-r--r--Lib/test/test_email/data/msg_20.txt (renamed from Lib/email/test/data/msg_20.txt)0
-rw-r--r--Lib/test/test_email/data/msg_21.txt (renamed from Lib/email/test/data/msg_21.txt)0
-rw-r--r--Lib/test/test_email/data/msg_22.txt (renamed from Lib/email/test/data/msg_22.txt)0
-rw-r--r--Lib/test/test_email/data/msg_23.txt (renamed from Lib/email/test/data/msg_23.txt)0
-rw-r--r--Lib/test/test_email/data/msg_24.txt (renamed from Lib/email/test/data/msg_24.txt)0
-rw-r--r--Lib/test/test_email/data/msg_25.txt (renamed from Lib/email/test/data/msg_25.txt)0
-rw-r--r--Lib/test/test_email/data/msg_26.txt (renamed from Lib/email/test/data/msg_26.txt)0
-rw-r--r--Lib/test/test_email/data/msg_27.txt (renamed from Lib/email/test/data/msg_27.txt)0
-rw-r--r--Lib/test/test_email/data/msg_28.txt (renamed from Lib/email/test/data/msg_28.txt)0
-rw-r--r--Lib/test/test_email/data/msg_29.txt (renamed from Lib/email/test/data/msg_29.txt)0
-rw-r--r--Lib/test/test_email/data/msg_30.txt (renamed from Lib/email/test/data/msg_30.txt)0
-rw-r--r--Lib/test/test_email/data/msg_31.txt (renamed from Lib/email/test/data/msg_31.txt)0
-rw-r--r--Lib/test/test_email/data/msg_32.txt (renamed from Lib/email/test/data/msg_32.txt)0
-rw-r--r--Lib/test/test_email/data/msg_33.txt (renamed from Lib/email/test/data/msg_33.txt)0
-rw-r--r--Lib/test/test_email/data/msg_34.txt (renamed from Lib/email/test/data/msg_34.txt)0
-rw-r--r--Lib/test/test_email/data/msg_35.txt (renamed from Lib/email/test/data/msg_35.txt)0
-rw-r--r--Lib/test/test_email/data/msg_36.txt (renamed from Lib/email/test/data/msg_36.txt)0
-rw-r--r--Lib/test/test_email/data/msg_37.txt (renamed from Lib/email/test/data/msg_37.txt)0
-rw-r--r--Lib/test/test_email/data/msg_38.txt (renamed from Lib/email/test/data/msg_38.txt)0
-rw-r--r--Lib/test/test_email/data/msg_39.txt (renamed from Lib/email/test/data/msg_39.txt)0
-rw-r--r--Lib/test/test_email/data/msg_40.txt (renamed from Lib/email/test/data/msg_40.txt)0
-rw-r--r--Lib/test/test_email/data/msg_41.txt (renamed from Lib/email/test/data/msg_41.txt)0
-rw-r--r--Lib/test/test_email/data/msg_42.txt (renamed from Lib/email/test/data/msg_42.txt)0
-rw-r--r--Lib/test/test_email/data/msg_43.txt (renamed from Lib/email/test/data/msg_43.txt)0
-rw-r--r--Lib/test/test_email/data/msg_44.txt (renamed from Lib/email/test/data/msg_44.txt)0
-rw-r--r--Lib/test/test_email/data/msg_45.txt (renamed from Lib/email/test/data/msg_45.txt)0
-rw-r--r--Lib/test/test_email/data/msg_46.txt (renamed from Lib/email/test/data/msg_46.txt)0
-rw-r--r--Lib/test/test_email/test_asian_codecs.py (renamed from Lib/email/test/test_email_codecs.py)15
-rw-r--r--Lib/test/test_email/test_email.py (renamed from Lib/email/test/test_email.py)329
-rw-r--r--Lib/test/test_email/test_generator.py136
-rw-r--r--Lib/test/test_email/test_policy.py148
-rw-r--r--Lib/test/test_email/test_utils.py45
-rw-r--r--Lib/test/test_email/torture_test.py (renamed from Lib/email/test/test_email_torture.py)0
-rw-r--r--Lib/test/test_exceptions.py48
-rw-r--r--Lib/test/test_extcall.py87
-rw-r--r--Lib/test/test_faulthandler.py554
-rw-r--r--Lib/test/test_fileinput.py630
-rw-r--r--Lib/test/test_float.py2
-rw-r--r--Lib/test/test_ftplib.py162
-rw-r--r--Lib/test/test_funcattrs.py27
-rw-r--r--Lib/test/test_functools.py93
-rw-r--r--Lib/test/test_future.py12
-rw-r--r--Lib/test/test_gc.py6
-rw-r--r--Lib/test/test_gdb.py34
-rw-r--r--Lib/test/test_genericpath.py27
-rw-r--r--Lib/test/test_genexps.py8
-rw-r--r--Lib/test/test_getargs2.py9
-rw-r--r--Lib/test/test_glob.py6
-rw-r--r--Lib/test/test_grammar.py91
-rw-r--r--Lib/test/test_gzip.py25
-rw-r--r--Lib/test/test_hash.py3
-rw-r--r--Lib/test/test_http_cookiejar.py11
-rw-r--r--Lib/test/test_httplib.py94
-rw-r--r--Lib/test/test_httpservers.py73
-rw-r--r--Lib/test/test_imaplib.py55
-rw-r--r--Lib/test/test_imp.py13
-rw-r--r--Lib/test/test_import.py20
-rw-r--r--Lib/test/test_importhooks.py9
-rw-r--r--Lib/test/test_inspect.py2
-rw-r--r--Lib/test/test_io.py53
-rw-r--r--Lib/test/test_itertools.py33
-rw-r--r--Lib/test/test_keywordonlyarg.py2
-rw-r--r--Lib/test/test_lib2to3.py4
-rw-r--r--Lib/test/test_locale.py2
-rw-r--r--Lib/test/test_logging.py1642
-rw-r--r--Lib/test/test_long.py80
-rw-r--r--Lib/test/test_lzma.py1335
-rw-r--r--Lib/test/test_mailbox.py9
-rw-r--r--Lib/test/test_mailcap.py221
-rw-r--r--Lib/test/test_marshal.py17
-rw-r--r--Lib/test/test_math.py43
-rw-r--r--Lib/test/test_memoryview.py27
-rw-r--r--Lib/test/test_metaclass.py16
-rw-r--r--Lib/test/test_minidom.py83
-rw-r--r--Lib/test/test_mmap.py32
-rw-r--r--Lib/test/test_module.py6
-rw-r--r--Lib/test/test_modulefinder.py48
-rw-r--r--Lib/test/test_multibytecodec.py16
-rw-r--r--Lib/test/test_multibytecodec_support.py15
-rw-r--r--Lib/test/test_multiprocessing.py61
-rw-r--r--Lib/test/test_nntplib.py27
-rw-r--r--Lib/test/test_ntpath.py7
-rw-r--r--Lib/test/test_optparse.py4
-rw-r--r--Lib/test/test_os.py473
-rw-r--r--Lib/test/test_ossaudiodev.py16
-rw-r--r--Lib/test/test_osx_env.py3
-rw-r--r--Lib/test/test_packaging.py5
-rw-r--r--Lib/test/test_parser.py8
-rw-r--r--Lib/test/test_pdb.py3
-rw-r--r--Lib/test/test_peepholer.py47
-rw-r--r--Lib/test/test_pep277.py66
-rw-r--r--Lib/test/test_pep292.py33
-rw-r--r--Lib/test/test_pep3120.py4
-rw-r--r--Lib/test/test_pep3131.py7
-rw-r--r--Lib/test/test_pep3151.py201
-rw-r--r--Lib/test/test_pipes.py15
-rw-r--r--Lib/test/test_pkgimport.py4
-rw-r--r--Lib/test/test_platform.py43
-rw-r--r--Lib/test/test_poplib.py15
-rw-r--r--Lib/test/test_posix.py584
-rw-r--r--Lib/test/test_posixpath.py12
-rw-r--r--Lib/test/test_property.py23
-rw-r--r--Lib/test/test_pulldom.py347
-rw-r--r--Lib/test/test_pydoc.py12
-rw-r--r--Lib/test/test_raise.py7
-rw-r--r--Lib/test/test_range.py81
-rw-r--r--Lib/test/test_re.py11
-rw-r--r--Lib/test/test_reprlib.py38
-rw-r--r--Lib/test/test_richcmp.py1
-rw-r--r--Lib/test/test_runpy.py14
-rw-r--r--Lib/test/test_sax.py4
-rw-r--r--Lib/test/test_sched.py35
-rw-r--r--Lib/test/test_scope.py19
-rw-r--r--Lib/test/test_select.py22
-rw-r--r--Lib/test/test_shelve.py6
-rw-r--r--Lib/test/test_shlex.py21
-rw-r--r--Lib/test/test_shutil.py258
-rw-r--r--Lib/test/test_signal.py471
-rw-r--r--Lib/test/test_smtpd.py44
-rw-r--r--Lib/test/test_smtplib.py60
-rw-r--r--Lib/test/test_smtpnet.py42
-rw-r--r--Lib/test/test_socket.py2674
-rw-r--r--Lib/test/test_ssl.py240
-rw-r--r--Lib/test/test_string.py68
-rw-r--r--Lib/test/test_strlit.py2
-rw-r--r--Lib/test/test_struct.py66
-rw-r--r--Lib/test/test_subprocess.py136
-rw-r--r--Lib/test/test_super.py10
-rw-r--r--Lib/test/test_support.py188
-rw-r--r--Lib/test/test_sys.py70
-rw-r--r--Lib/test/test_sys_settrace.py10
-rw-r--r--Lib/test/test_sysconfig.py83
-rw-r--r--Lib/test/test_tarfile.py91
-rw-r--r--Lib/test/test_telnetlib.py1
-rw-r--r--Lib/test/test_tempfile.py23
-rw-r--r--Lib/test/test_threaded_import.py13
-rw-r--r--Lib/test/test_threading.py19
-rw-r--r--Lib/test/test_threadsignals.py6
-rw-r--r--Lib/test/test_time.py264
-rw-r--r--Lib/test/test_tokenize.py2
-rw-r--r--Lib/test/test_trace.py11
-rw-r--r--Lib/test/test_ucn.py88
-rw-r--r--Lib/test/test_unicode.py261
-rw-r--r--Lib/test/test_unicode_file.py17
-rw-r--r--Lib/test/test_unicodedata.py12
-rw-r--r--Lib/test/test_urllib.py25
-rw-r--r--Lib/test/test_urllib2.py46
-rw-r--r--Lib/test/test_userlist.py6
-rwxr-xr-xLib/test/test_userstring.py4
-rw-r--r--Lib/test/test_uuid.py4
-rw-r--r--Lib/test/test_wait3.py7
-rw-r--r--Lib/test/test_warnings.py14
-rw-r--r--Lib/test/test_xml_etree.py14
-rw-r--r--Lib/test/test_xmlrpc.py103
-rw-r--r--Lib/test/test_xmlrpc_net.py4
-rw-r--r--Lib/test/test_zipfile.py76
-rw-r--r--Lib/test/test_zipfile64.py20
-rw-r--r--Lib/test/test_zipimport.py10
-rw-r--r--Lib/test/test_zipimport_support.py5
-rw-r--r--Lib/test/test_zlib.py32
-rw-r--r--Lib/test/threaded_import_hangers.py13
-rw-r--r--Lib/textwrap.py2
-rw-r--r--Lib/threading.py75
-rw-r--r--Lib/timeit.py8
-rw-r--r--Lib/tkinter/__init__.py7
-rw-r--r--Lib/tkinter/filedialog.py2
-rw-r--r--Lib/tokenize.py114
-rw-r--r--Lib/turtle.py1
-rw-r--r--Lib/unittest/case.py131
-rw-r--r--Lib/unittest/main.py1
-rw-r--r--Lib/unittest/result.py1
-rw-r--r--Lib/unittest/test/_test_warnings.py1
-rw-r--r--Lib/unittest/test/test_assertions.py73
-rw-r--r--Lib/unittest/test/test_case.py63
-rw-r--r--Lib/unittest/test/test_loader.py4
-rw-r--r--Lib/urllib/error.py3
-rw-r--r--Lib/urllib/request.py80
-rw-r--r--Lib/urllib/response.py7
-rw-r--r--Lib/wsgiref.egg-info8
-rw-r--r--Lib/xdrlib.py6
-rw-r--r--Lib/xml/dom/pulldom.py6
-rw-r--r--Lib/xml/parsers/expat.py2
-rw-r--r--Lib/xmlrpc/client.py129
-rw-r--r--Lib/xmlrpc/server.py34
-rw-r--r--Makefile.pre.in354
-rw-r--r--Misc/ACKS146
-rw-r--r--Misc/NEWS1752
-rw-r--r--Misc/README1
-rw-r--r--Misc/RPM/python-3.3.spec (renamed from Misc/RPM/python-3.2.spec)4
-rw-r--r--Misc/python.man17
-rw-r--r--Misc/svnmap.txt72546
-rw-r--r--Misc/valgrind-python.supp24
-rw-r--r--Modules/Setup.dist5
-rw-r--r--Modules/_bisectmodule.c8
-rw-r--r--Modules/_bz2module.c590
-rw-r--r--Modules/_codecsmodule.c255
-rw-r--r--Modules/_collectionsmodule.c26
-rw-r--r--Modules/_cryptmodule.c (renamed from Modules/cryptmodule.c)4
-rw-r--r--Modules/_csv.c149
-rw-r--r--Modules/_ctypes/_ctypes.c46
-rw-r--r--Modules/_ctypes/callproc.c18
-rw-r--r--Modules/_ctypes/cfield.c84
-rw-r--r--Modules/_ctypes/stgdict.c12
-rw-r--r--Modules/_cursesmodule.c700
-rw-r--r--Modules/_datetimemodule.c197
-rw-r--r--Modules/_dbmmodule.c12
-rw-r--r--Modules/_elementtree.c112
-rw-r--r--Modules/_functoolsmodule.c172
-rw-r--r--Modules/_gestalt.c2
-rw-r--r--Modules/_hashopenssl.c8
-rw-r--r--Modules/_io/_iomodule.c122
-rw-r--r--Modules/_io/_iomodule.h12
-rw-r--r--Modules/_io/bufferedio.c253
-rw-r--r--Modules/_io/bytesio.c2
-rw-r--r--Modules/_io/fileio.c110
-rw-r--r--Modules/_io/iobase.c36
-rw-r--r--Modules/_io/stringio.c184
-rw-r--r--Modules/_io/textio.c678
-rw-r--r--Modules/_json.c436
-rw-r--r--Modules/_localemodule.c98
-rw-r--r--Modules/_lsprof.c45
-rw-r--r--Modules/_lzmamodule.c1111
-rw-r--r--Modules/_multiprocessing/connection.h527
-rw-r--r--Modules/_multiprocessing/multiprocessing.c196
-rw-r--r--Modules/_multiprocessing/multiprocessing.h80
-rw-r--r--Modules/_multiprocessing/pipe_connection.c149
-rw-r--r--Modules/_multiprocessing/semaphore.c56
-rw-r--r--Modules/_multiprocessing/socket_connection.c202
-rw-r--r--Modules/_multiprocessing/win32_functions.c550
-rw-r--r--Modules/_pickle.c235
-rw-r--r--Modules/_posixsubprocess.c15
-rw-r--r--Modules/_sqlite/cache.c21
-rw-r--r--Modules/_sqlite/connection.c99
-rw-r--r--Modules/_sqlite/cursor.c40
-rw-r--r--Modules/_sqlite/cursor.h1
-rw-r--r--Modules/_sqlite/microprotocols.c8
-rw-r--r--Modules/_sqlite/module.c3
-rw-r--r--Modules/_sqlite/row.c10
-rw-r--r--Modules/_sqlite/statement.c4
-rw-r--r--Modules/_sre.c620
-rw-r--r--Modules/_ssl.c373
-rw-r--r--Modules/_struct.c90
-rw-r--r--Modules/_testcapimodule.c116
-rw-r--r--Modules/_threadmodule.c22
-rw-r--r--Modules/_tkinter.c83
-rw-r--r--Modules/arraymodule.c222
-rw-r--r--Modules/audioop.c4
-rw-r--r--Modules/binascii.c8
-rw-r--r--Modules/bz2module.c2169
-rw-r--r--Modules/cjkcodecs/_codecs_cn.c14
-rw-r--r--Modules/cjkcodecs/_codecs_hk.c2
-rw-r--r--Modules/cjkcodecs/_codecs_iso2022.c2
-rw-r--r--Modules/cjkcodecs/_codecs_jp.c34
-rw-r--r--Modules/cjkcodecs/_codecs_kr.c18
-rw-r--r--Modules/cjkcodecs/_codecs_tw.c4
-rw-r--r--Modules/cjkcodecs/multibytecodec.c48
-rw-r--r--Modules/errnomodule.c55
-rw-r--r--Modules/faulthandler.c1329
-rw-r--r--Modules/fcntlmodule.c5
-rw-r--r--Modules/fpectlmodule.c11
-rw-r--r--Modules/gc_weakref.txt6
-rw-r--r--Modules/gcmodule.c11
-rw-r--r--Modules/getbuildinfo.c18
-rw-r--r--Modules/getpath.c16
-rw-r--r--Modules/itertoolsmodule.c30
-rw-r--r--Modules/main.c18
-rw-r--r--Modules/mathmodule.c75
-rw-r--r--Modules/md5module.c20
-rw-r--r--Modules/mmapmodule.c62
-rw-r--r--Modules/nismodule.c2
-rw-r--r--Modules/operator.c27
-rw-r--r--Modules/ossaudiodev.c101
-rw-r--r--Modules/parsermodule.c13
-rw-r--r--Modules/posixmodule.c3953
-rw-r--r--Modules/pyexpat.c43
-rw-r--r--Modules/readline.c13
-rw-r--r--Modules/resource.c16
-rw-r--r--Modules/selectmodule.c404
-rw-r--r--Modules/sha1module.c20
-rw-r--r--Modules/sha256module.c18
-rw-r--r--Modules/sha512module.c18
-rw-r--r--Modules/signalmodule.c444
-rw-r--r--Modules/socketmodule.c1330
-rw-r--r--Modules/socketmodule.h16
-rw-r--r--Modules/sre.h4
-rw-r--r--Modules/syslogmodule.c14
-rw-r--r--Modules/termios.c5
-rw-r--r--Modules/testcapi_long.h29
-rw-r--r--Modules/timemodule.c269
-rw-r--r--Modules/unicodedata.c301
-rw-r--r--Modules/unicodename_db.h35013
-rw-r--r--Modules/xxlimited.c3
-rw-r--r--Modules/zipimport.c507
-rw-r--r--Modules/zlibmodule.c27
-rw-r--r--Objects/abstract.c125
-rw-r--r--Objects/bytearrayobject.c317
-rw-r--r--Objects/bytesobject.c231
-rw-r--r--Objects/classobject.c14
-rw-r--r--Objects/codeobject.c116
-rw-r--r--Objects/complexobject.c36
-rw-r--r--Objects/descrobject.c112
-rw-r--r--Objects/dictobject.c62
-rw-r--r--Objects/exceptions.c689
-rw-r--r--Objects/fileobject.c34
-rw-r--r--Objects/floatobject.c32
-rw-r--r--Objects/funcobject.c90
-rw-r--r--Objects/genobject.c6
-rw-r--r--Objects/listobject.c72
-rw-r--r--Objects/longobject.c144
-rw-r--r--Objects/memoryobject.c37
-rw-r--r--Objects/methodobject.c3
-rw-r--r--Objects/moduleobject.c106
-rw-r--r--Objects/object.c439
-rw-r--r--Objects/obmalloc.c54
-rw-r--r--Objects/rangeobject.c145
-rw-r--r--Objects/setobject.c136
-rw-r--r--Objects/sliceobject.c71
-rw-r--r--Objects/stringlib/asciilib.h34
-rw-r--r--Objects/stringlib/codecs.h353
-rw-r--r--Objects/stringlib/count.h9
-rw-r--r--Objects/stringlib/eq.h23
-rw-r--r--Objects/stringlib/fastsearch.h76
-rw-r--r--Objects/stringlib/find.h89
-rw-r--r--Objects/stringlib/find_max_char.h136
-rw-r--r--Objects/stringlib/formatter.h1518
-rw-r--r--Objects/stringlib/localeutil.h27
-rw-r--r--Objects/stringlib/partition.h12
-rw-r--r--Objects/stringlib/split.h26
-rw-r--r--Objects/stringlib/stringdefs.h4
-rw-r--r--Objects/stringlib/ucs1lib.h35
-rw-r--r--Objects/stringlib/ucs2lib.h34
-rw-r--r--Objects/stringlib/ucs4lib.h34
-rw-r--r--Objects/stringlib/undef.h11
-rw-r--r--Objects/stringlib/unicode_format.h (renamed from Objects/stringlib/string_format.h)401
-rw-r--r--Objects/stringlib/unicodedefs.h4
-rw-r--r--Objects/tupleobject.c17
-rw-r--r--Objects/typeobject.c478
-rw-r--r--Objects/typeslots.inc2
-rw-r--r--Objects/typeslots.py2
-rw-r--r--Objects/unicodeobject.c10860
-rw-r--r--Objects/weakrefobject.c61
-rw-r--r--PC/VC6/_multiprocessing.dsp8
-rw-r--r--PC/VC6/pythoncore.dsp4
-rw-r--r--PC/VC6/readme.txt4
-rw-r--r--PC/VS7.1/pythoncore.vcproj24
-rw-r--r--PC/VS7.1/readme.txt4
-rw-r--r--PC/VS8.0/_multiprocessing.vcproj12
-rw-r--r--PC/VS8.0/build_ssl.bat4
-rw-r--r--PC/VS8.0/kill_python.c2
-rw-r--r--PC/VS8.0/pyproject.vsprops2
-rw-r--r--PC/_msi.c18
-rw-r--r--PC/_subprocess.c85
-rw-r--r--PC/config.c2
-rw-r--r--PC/example_nt/example.vcproj4
-rw-r--r--PC/frozen_dllmain.c4
-rw-r--r--PC/getpathp.c6
-rw-r--r--PC/import_nt.c131
-rwxr-xr-xPC/msvcrtmodule.c12
-rw-r--r--PC/os2emx/Makefile2
-rw-r--r--PC/os2emx/README.os2emx2
-rw-r--r--PC/os2emx/pyconfig.h2
-rw-r--r--PC/os2emx/python33.def (renamed from PC/os2emx/python27.def)250
-rw-r--r--PC/pyconfig.h11
-rw-r--r--PC/python3.def1374
-rw-r--r--PC/python3.mak12
-rw-r--r--PC/python33gen.py (renamed from PC/python32gen.py)9
-rw-r--r--PC/python33stub.def (renamed from PC/python32stub.def)2
-rw-r--r--PC/winreg.c78
-rw-r--r--PC/winsound.c2
-rw-r--r--PCbuild/_bz2.vcproj (renamed from PCbuild/bz2.vcproj)4
-rw-r--r--PCbuild/_lzma.vcproj537
-rw-r--r--PCbuild/_multiprocessing.vcproj12
-rw-r--r--PCbuild/build_ssl.bat4
-rw-r--r--PCbuild/kill_python.c2
-rw-r--r--PCbuild/pcbuild.sln23
-rw-r--r--PCbuild/pyproject.vsprops6
-rw-r--r--PCbuild/pythoncore.vcproj8
-rw-r--r--PCbuild/readme.txt14
-rw-r--r--Parser/Python.asdl14
-rw-r--r--Parser/asdl.py25
-rwxr-xr-xParser/asdl_c.py59
-rw-r--r--Parser/intrcheck.c174
-rw-r--r--Parser/parsetok.c37
-rw-r--r--Parser/parsetok_pgen.c2
-rw-r--r--Parser/pgenmain.c2
-rw-r--r--Parser/tokenizer.c79
-rw-r--r--Parser/tokenizer.h9
-rw-r--r--Python/Python-ast.c1174
-rw-r--r--Python/_warnings.c71
-rw-r--r--Python/ast.c614
-rw-r--r--Python/bltinmodule.c107
-rw-r--r--Python/ceval.c677
-rw-r--r--Python/codecs.c220
-rw-r--r--Python/compile.c318
-rw-r--r--Python/dtoa.c4
-rw-r--r--Python/dynload_aix.c5
-rw-r--r--Python/dynload_dl.c16
-rw-r--r--Python/dynload_hpux.c2
-rw-r--r--Python/dynload_next.c4
-rw-r--r--Python/dynload_os2.c2
-rw-r--r--Python/dynload_shlib.c2
-rw-r--r--Python/dynload_win.c55
-rw-r--r--Python/errors.c89
-rw-r--r--Python/fileutils.c44
-rw-r--r--Python/formatter_unicode.c1471
-rw-r--r--Python/getargs.c128
-rw-r--r--Python/import.c2589
-rw-r--r--Python/importdl.c84
-rw-r--r--Python/importdl.h2
-rwxr-xr-xPython/makeopcodetargets.py3
-rw-r--r--Python/marshal.c50
-rw-r--r--Python/modsupport.c16
-rw-r--r--Python/peephole.c189
-rw-r--r--Python/pystrtod.c2
-rw-r--r--Python/pythonrun.c178
-rw-r--r--Python/symtable.c158
-rw-r--r--Python/sysmodule.c102
-rw-r--r--Python/thread.c126
-rw-r--r--Python/thread_cthread.h112
-rw-r--r--Python/thread_lwp.h113
-rw-r--r--Python/thread_pth.h3
-rw-r--r--Python/thread_pthread.h10
-rw-r--r--Python/thread_sgi.h259
-rw-r--r--Python/thread_solaris.h130
-rw-r--r--Python/thread_wince.h136
-rw-r--r--Python/traceback.c289
-rw-r--r--README30
-rw-r--r--Tools/buildbot/external-common.bat5
-rw-r--r--Tools/gdb/libpython.py95
-rw-r--r--Tools/iobench/iobench.py19
-rw-r--r--Tools/msi/msi.py11
-rw-r--r--Tools/msi/uuids.py60
-rwxr-xr-xTools/pybench/pybench.py1
-rw-r--r--Tools/scripts/README3
-rwxr-xr-xTools/scripts/findnocoding.py4
-rwxr-xr-xTools/scripts/patchcheck.py30
-rwxr-xr-xTools/scripts/pysetup34
-rwxr-xr-xTools/scripts/pysource.py2
-rwxr-xr-xTools/scripts/reindent.py18
-rwxr-xr-xTools/scripts/run_tests.py47
-rw-r--r--Tools/unicode/comparecodecs.py2
-rw-r--r--Tools/unicode/makeunicodedata.py271
-rw-r--r--Tools/unittestgui/unittestgui.py1
-rwxr-xr-xconfigure1258
-rw-r--r--configure.in291
-rw-r--r--pyconfig.h.in205
-rw-r--r--setup.py74
1078 files changed, 194672 insertions, 45503 deletions
diff --git a/.gitignore b/.gitignore
index 7dfc5ad..8efcd2e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,7 @@ Doc/tools/jinja2/
Doc/tools/pygments/
Doc/tools/sphinx/
Lib/lib2to3/*.pickle
+Lib/_sysconfigdata.py
Makefile
Makefile.pre
Misc/python.pc
diff --git a/.hgeol b/.hgeol
index ed13171..afb1e6b 100644
--- a/.hgeol
+++ b/.hgeol
@@ -28,8 +28,9 @@
Lib/email/test/data/msg_26.txt = BIN
Lib/test/cjkencodings/* = BIN
-Lib/test/sndhdrdata/sndhdr.* = BIN
Lib/test/decimaltestdata/*.decTest = BIN
+Lib/test/sndhdrdata/sndhdr.* = BIN
+Lib/test/test_email/data/msg_26.txt = BIN
# All other files (which presumably are human-editable) are "native".
# This must be the last rule!
diff --git a/.hgignore b/.hgignore
index 24df3b9..0fd8562 100644
--- a/.hgignore
+++ b/.hgignore
@@ -5,7 +5,8 @@ Makefile$
Makefile.pre$
TAGS$
autom4te.cache$
-build/
+^build/
+^Doc/build/
buildno$
config.cache
config.log
@@ -32,6 +33,7 @@ Modules/config.c
Modules/ld_so_aix$
Parser/pgen$
Parser/pgen.stamp$
+PCbuild/amd64/
^core
^python-gdb.py
^python.exe-gdb.py
@@ -47,6 +49,7 @@ libpython*.so*
*.pyd
*.cover
*~
+Lib/_sysconfigdata.py
Lib/lib2to3/*.pickle
Lib/test/data/*
Misc/*.wpu
@@ -62,7 +65,10 @@ PCbuild/*.exp
PCbuild/*.o
PCbuild/*.ncb
PCbuild/*.bsc
+PCbuild/*.user
+PCbuild/*.suo
PCbuild/Win32-temp-*
+PCbuild/x64-temp-*
__pycache__
Modules/_testembed
.coverage
diff --git a/Doc/ACKS.txt b/Doc/ACKS.txt
index 70063c0..f9e4d3b 100644
--- a/Doc/ACKS.txt
+++ b/Doc/ACKS.txt
@@ -210,6 +210,7 @@ docs@python.org), and we'll be glad to correct the problem.
* David Turner
* Sandro Tosi
* Ville Vainio
+ * Nadeem Vawda
* Martijn Vries
* Charles G. Waldman
* Greg Ward
diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst
index d4dda7c..196aa77 100644
--- a/Doc/c-api/arg.rst
+++ b/Doc/c-api/arg.rst
@@ -146,7 +146,7 @@ Unless otherwise stated, buffers are not NUL-terminated.
Like ``u#``, but the Python object may also be ``None``, in which case the
:c:type:`Py_UNICODE` pointer is set to *NULL*.
-``U`` (:class:`str`) [PyUnicodeObject \*]
+``U`` (:class:`str`) [PyObject \*]
Requires that the Python object is a Unicode object, without attempting
any conversion. Raises :exc:`TypeError` if the object is not a Unicode
object. The C variable may also be declared as :c:type:`PyObject\*`.
@@ -260,9 +260,11 @@ Numbers
``n`` (:class:`int`) [Py_ssize_t]
Convert a Python integer to a C :c:type:`Py_ssize_t`.
-``c`` (:class:`bytes` of length 1) [char]
- Convert a Python byte, represented as a :class:`bytes` object of length 1,
- to a C :c:type:`char`.
+``c`` (:class:`bytes` or :class:`bytearray` of length 1) [char]
+ Convert a Python byte, represented as a :class:`bytes` or
+ :class:`bytearray` object of length 1, to a C :c:type:`char`.
+
+ .. versionchanged:: 3.3 Allow :class:`bytearray` objects
``C`` (:class:`str` of length 1) [int]
Convert a Python character, represented as a :class:`str` object of
diff --git a/Doc/c-api/dict.rst b/Doc/c-api/dict.rst
index 6df84e0..ac714a6 100644
--- a/Doc/c-api/dict.rst
+++ b/Doc/c-api/dict.rst
@@ -209,3 +209,10 @@ Dictionary Objects
for key, value in seq2:
if override or key not in a:
a[key] = value
+
+
+.. c:function:: int PyDict_ClearFreeList()
+
+ Clear the free list. Return the total number of freed items.
+
+ .. versionadded:: 3.3
diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst
index 6f13c80..c7252ed 100644
--- a/Doc/c-api/exceptions.rst
+++ b/Doc/c-api/exceptions.rst
@@ -525,7 +525,7 @@ recursion depth automatically).
Marks a point where a recursive C-level call is about to be performed.
- If :const:`USE_STACKCHECK` is defined, this function checks if the the OS
+ If :const:`USE_STACKCHECK` is defined, this function checks if the OS
stack overflowed using :c:func:`PyOS_CheckStack`. In this is the case, it
sets a :exc:`MemoryError` and returns a nonzero value.
@@ -582,65 +582,116 @@ All standard Python exceptions are available as global variables whose names are
:c:type:`PyObject\*`; they are all class objects. For completeness, here are all
the variables:
-+-------------------------------------+----------------------------+----------+
-| C Name | Python Name | Notes |
-+=====================================+============================+==========+
-| :c:data:`PyExc_BaseException` | :exc:`BaseException` | \(1) |
-+-------------------------------------+----------------------------+----------+
-| :c:data:`PyExc_Exception` | :exc:`Exception` | \(1) |
-+-------------------------------------+----------------------------+----------+
-| :c:data:`PyExc_ArithmeticError` | :exc:`ArithmeticError` | \(1) |
-+-------------------------------------+----------------------------+----------+
-| :c:data:`PyExc_LookupError` | :exc:`LookupError` | \(1) |
-+-------------------------------------+----------------------------+----------+
-| :c:data:`PyExc_AssertionError` | :exc:`AssertionError` | |
-+-------------------------------------+----------------------------+----------+
-| :c:data:`PyExc_AttributeError` | :exc:`AttributeError` | |
-+-------------------------------------+----------------------------+----------+
-| :c:data:`PyExc_EOFError` | :exc:`EOFError` | |
-+-------------------------------------+----------------------------+----------+
-| :c:data:`PyExc_EnvironmentError` | :exc:`EnvironmentError` | \(1) |
-+-------------------------------------+----------------------------+----------+
-| :c:data:`PyExc_FloatingPointError` | :exc:`FloatingPointError` | |
-+-------------------------------------+----------------------------+----------+
-| :c:data:`PyExc_IOError` | :exc:`IOError` | |
-+-------------------------------------+----------------------------+----------+
-| :c:data:`PyExc_ImportError` | :exc:`ImportError` | |
-+-------------------------------------+----------------------------+----------+
-| :c:data:`PyExc_IndexError` | :exc:`IndexError` | |
-+-------------------------------------+----------------------------+----------+
-| :c:data:`PyExc_KeyError` | :exc:`KeyError` | |
-+-------------------------------------+----------------------------+----------+
-| :c:data:`PyExc_KeyboardInterrupt` | :exc:`KeyboardInterrupt` | |
-+-------------------------------------+----------------------------+----------+
-| :c:data:`PyExc_MemoryError` | :exc:`MemoryError` | |
-+-------------------------------------+----------------------------+----------+
-| :c:data:`PyExc_NameError` | :exc:`NameError` | |
-+-------------------------------------+----------------------------+----------+
-| :c:data:`PyExc_NotImplementedError` | :exc:`NotImplementedError` | |
-+-------------------------------------+----------------------------+----------+
-| :c:data:`PyExc_OSError` | :exc:`OSError` | |
-+-------------------------------------+----------------------------+----------+
-| :c:data:`PyExc_OverflowError` | :exc:`OverflowError` | |
-+-------------------------------------+----------------------------+----------+
-| :c:data:`PyExc_ReferenceError` | :exc:`ReferenceError` | \(2) |
-+-------------------------------------+----------------------------+----------+
-| :c:data:`PyExc_RuntimeError` | :exc:`RuntimeError` | |
-+-------------------------------------+----------------------------+----------+
-| :c:data:`PyExc_SyntaxError` | :exc:`SyntaxError` | |
-+-------------------------------------+----------------------------+----------+
-| :c:data:`PyExc_SystemError` | :exc:`SystemError` | |
-+-------------------------------------+----------------------------+----------+
-| :c:data:`PyExc_SystemExit` | :exc:`SystemExit` | |
-+-------------------------------------+----------------------------+----------+
-| :c:data:`PyExc_TypeError` | :exc:`TypeError` | |
-+-------------------------------------+----------------------------+----------+
-| :c:data:`PyExc_ValueError` | :exc:`ValueError` | |
-+-------------------------------------+----------------------------+----------+
-| :c:data:`PyExc_WindowsError` | :exc:`WindowsError` | \(3) |
-+-------------------------------------+----------------------------+----------+
-| :c:data:`PyExc_ZeroDivisionError` | :exc:`ZeroDivisionError` | |
-+-------------------------------------+----------------------------+----------+
++-----------------------------------------+---------------------------------+----------+
+| C Name | Python Name | Notes |
++=========================================+=================================+==========+
+| :c:data:`PyExc_BaseException` | :exc:`BaseException` | \(1) |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_Exception` | :exc:`Exception` | \(1) |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_ArithmeticError` | :exc:`ArithmeticError` | \(1) |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_LookupError` | :exc:`LookupError` | \(1) |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_AssertionError` | :exc:`AssertionError` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_AttributeError` | :exc:`AttributeError` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_BlockingIOError` | :exc:`BlockingIOError` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_BrokenPipeError` | :exc:`BrokenPipeError` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_ChildProcessError` | :exc:`ChildProcessError` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_ConnectionError` | :exc:`ConnectionError` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_ConnectionAbortedError` | :exc:`ConnectionAbortedError` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_ConnectionRefusedError` | :exc:`ConnectionRefusedError` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_ConnectionResetError` | :exc:`ConnectionResetError` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_FileExistsError` | :exc:`FileExistsError` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_FileNotFoundError` | :exc:`FileNotFoundError` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_EOFError` | :exc:`EOFError` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_FloatingPointError` | :exc:`FloatingPointError` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_ImportError` | :exc:`ImportError` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_IndexError` | :exc:`IndexError` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_InterruptedError` | :exc:`InterruptedError` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_IsADirectoryError` | :exc:`IsADirectoryError` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_KeyError` | :exc:`KeyError` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_KeyboardInterrupt` | :exc:`KeyboardInterrupt` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_MemoryError` | :exc:`MemoryError` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_NameError` | :exc:`NameError` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_NotADirectoryError` | :exc:`NotADirectoryError` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_NotImplementedError` | :exc:`NotImplementedError` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_OSError` | :exc:`OSError` | \(1) |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_OverflowError` | :exc:`OverflowError` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_PermissionError` | :exc:`PermissionError` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_ProcessLookupError` | :exc:`ProcessLookupError` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_ReferenceError` | :exc:`ReferenceError` | \(2) |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_RuntimeError` | :exc:`RuntimeError` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_SyntaxError` | :exc:`SyntaxError` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_SystemError` | :exc:`SystemError` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_TimeoutError` | :exc:`TimeoutError` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_SystemExit` | :exc:`SystemExit` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_TypeError` | :exc:`TypeError` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_ValueError` | :exc:`ValueError` | |
++-----------------------------------------+---------------------------------+----------+
+| :c:data:`PyExc_ZeroDivisionError` | :exc:`ZeroDivisionError` | |
++-----------------------------------------+---------------------------------+----------+
+
+.. versionadded:: 3.3
+ :c:data:`PyExc_BlockingIOError`, :c:data:`PyExc_BrokenPipeError`,
+ :c:data:`PyExc_ChildProcessError`, :c:data:`PyExc_ConnectionError`,
+ :c:data:`PyExc_ConnectionAbortedError`, :c:data:`PyExc_ConnectionRefusedError`,
+ :c:data:`PyExc_ConnectionResetError`, :c:data:`PyExc_FileExistsError`,
+ :c:data:`PyExc_FileNotFoundError`, :c:data:`PyExc_InterruptedError`,
+ :c:data:`PyExc_IsADirectoryError`, :c:data:`PyExc_NotADirectoryError`,
+ :c:data:`PyExc_PermissionError`, :c:data:`PyExc_ProcessLookupError`
+ and :c:data:`PyExc_TimeoutError` were introduced following :pep:`3151`.
+
+
+These are compatibility aliases to :c:data:`PyExc_OSError`:
+
++-------------------------------------+----------+
+| C Name | Notes |
++=====================================+==========+
+| :c:data:`PyExc_EnvironmentError` | |
++-------------------------------------+----------+
+| :c:data:`PyExc_IOError` | |
++-------------------------------------+----------+
+| :c:data:`PyExc_WindowsError` | \(3) |
++-------------------------------------+----------+
+
+.. versionchanged:: 3.3
+ These aliases used to be separate exception types.
+
.. index::
single: PyExc_BaseException
@@ -649,28 +700,42 @@ the variables:
single: PyExc_LookupError
single: PyExc_AssertionError
single: PyExc_AttributeError
+ single: PyExc_BlockingIOError
+ single: PyExc_BrokenPipeError
+ single: PyExc_ConnectionError
+ single: PyExc_ConnectionAbortedError
+ single: PyExc_ConnectionRefusedError
+ single: PyExc_ConnectionResetError
single: PyExc_EOFError
- single: PyExc_EnvironmentError
+ single: PyExc_FileExistsError
+ single: PyExc_FileNotFoundError
single: PyExc_FloatingPointError
- single: PyExc_IOError
single: PyExc_ImportError
single: PyExc_IndexError
+ single: PyExc_InterruptedError
+ single: PyExc_IsADirectoryError
single: PyExc_KeyError
single: PyExc_KeyboardInterrupt
single: PyExc_MemoryError
single: PyExc_NameError
+ single: PyExc_NotADirectoryError
single: PyExc_NotImplementedError
single: PyExc_OSError
single: PyExc_OverflowError
+ single: PyExc_PermissionError
+ single: PyExc_ProcessLookupError
single: PyExc_ReferenceError
single: PyExc_RuntimeError
single: PyExc_SyntaxError
single: PyExc_SystemError
single: PyExc_SystemExit
+ single: PyExc_TimeoutError
single: PyExc_TypeError
single: PyExc_ValueError
- single: PyExc_WindowsError
single: PyExc_ZeroDivisionError
+ single: PyExc_EnvironmentError
+ single: PyExc_IOError
+ single: PyExc_WindowsError
Notes:
diff --git a/Doc/c-api/function.rst b/Doc/c-api/function.rst
index 31805fd..ad98322 100644
--- a/Doc/c-api/function.rst
+++ b/Doc/c-api/function.rst
@@ -38,6 +38,16 @@ There are a few functions specific to Python functions.
object, the argument defaults and closure are set to *NULL*.
+.. c:function:: PyObject* PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname)
+
+ As :c:func:`PyFunction_New`, but also allows to set the function object's
+ ``__qualname__`` attribute. *qualname* should be a unicode object or NULL;
+ if NULL, the ``__qualname__`` attribute is set to the same value as its
+ ``__name__`` attribute.
+
+ .. versionadded:: 3.3
+
+
.. c:function:: PyObject* PyFunction_GetCode(PyObject *op)
Return the code object associated with the function object *op*.
diff --git a/Doc/c-api/import.rst b/Doc/c-api/import.rst
index cf48363..b168751 100644
--- a/Doc/c-api/import.rst
+++ b/Doc/c-api/import.rst
@@ -57,7 +57,7 @@ Importing Modules
:c:func:`PyImport_ImportModule`.
-.. c:function:: PyObject* PyImport_ImportModuleLevel(char *name, PyObject *globals, PyObject *locals, PyObject *fromlist, int level)
+.. c:function:: PyObject* PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals, PyObject *locals, PyObject *fromlist, int level)
Import a module. This is best described by referring to the built-in Python
function :func:`__import__`, as the standard :func:`__import__` function calls
@@ -68,6 +68,13 @@ Importing Modules
the return value when a submodule of a package was requested is normally the
top-level package, unless a non-empty *fromlist* was given.
+ .. versionadded:: 3.3
+
+
+.. c:function:: PyObject* PyImport_ImportModuleLevel(char *name, PyObject *globals, PyObject *locals, PyObject *fromlist, int level)
+
+ Similar to :c:func:`PyImport_ImportModuleLevelObject`, but the name is an
+ UTF-8 encoded string instead of a Unicode object.
.. c:function:: PyObject* PyImport_Import(PyObject *name)
@@ -86,7 +93,7 @@ Importing Modules
an exception set on failure (the module still exists in this case).
-.. c:function:: PyObject* PyImport_AddModule(const char *name)
+.. c:function:: PyObject* PyImport_AddModuleObject(PyObject *name)
Return the module object corresponding to a module name. The *name* argument
may be of the form ``package.module``. First check the modules dictionary if
@@ -100,6 +107,14 @@ Importing Modules
or one of its variants to import a module. Package structures implied by a
dotted name for *name* are not created if not already present.
+ .. versionadded:: 3.3
+
+
+.. c:function:: PyObject* PyImport_AddModule(const char *name)
+
+ Similar to :c:func:`PyImport_AddModuleObject`, but the name is a UTF-8
+ encoded string instead of a Unicode object.
+
.. c:function:: PyObject* PyImport_ExecCodeModule(char *name, PyObject *co)
@@ -136,14 +151,23 @@ Importing Modules
See also :c:func:`PyImport_ExecCodeModuleWithPathnames`.
-.. c:function:: PyObject* PyImport_ExecCodeModuleWithPathnames(char *name, PyObject *co, char *pathname, char *cpathname)
+.. c:function:: PyObject* PyImport_ExecCodeModuleObject(PyObject *name, PyObject *co, PyObject *pathname, PyObject *cpathname)
Like :c:func:`PyImport_ExecCodeModuleEx`, but the :attr:`__cached__`
attribute of the module object is set to *cpathname* if it is
non-``NULL``. Of the three functions, this is the preferred one to use.
+ .. versionadded:: 3.3
+
+
+.. c:function:: PyObject* PyImport_ExecCodeModuleWithPathnames(char *name, PyObject *co, char *pathname, char *cpathname)
+
+ Like :c:func:`PyImport_ExecCodeModuleObject`, but *name*, *pathname* and
+ *cpathname* are UTF-8 encoded strings.
+
.. versionadded:: 3.2
+
.. c:function:: long PyImport_GetMagicNumber()
Return the magic number for Python bytecode files (a.k.a. :file:`.pyc` and
@@ -200,7 +224,7 @@ Importing Modules
For internal use only.
-.. c:function:: int PyImport_ImportFrozenModule(char *name)
+.. c:function:: int PyImport_ImportFrozenModuleObject(PyObject *name)
Load a frozen module named *name*. Return ``1`` for success, ``0`` if the
module is not found, and ``-1`` with an exception set if the initialization
@@ -208,6 +232,14 @@ Importing Modules
:c:func:`PyImport_ImportModule`. (Note the misnomer --- this function would
reload the module if it was already imported.)
+ .. versionadded:: 3.3
+
+
+.. c:function:: int PyImport_ImportFrozenModule(char *name)
+
+ Similar to :c:func:`PyImport_ImportFrozenModuleObject`, but the name is a
+ UTF-8 encoded string instead of a Unicode object.
+
.. c:type:: struct _frozen
@@ -247,13 +279,13 @@ Importing Modules
Structure describing a single entry in the list of built-in modules. Each of
these structures gives the name and initialization function for a module built
- into the interpreter. Programs which embed Python may use an array of these
- structures in conjunction with :c:func:`PyImport_ExtendInittab` to provide
- additional built-in modules. The structure is defined in
- :file:`Include/import.h` as::
+ into the interpreter. The name is an ASCII encoded string. Programs which
+ embed Python may use an array of these structures in conjunction with
+ :c:func:`PyImport_ExtendInittab` to provide additional built-in modules.
+ The structure is defined in :file:`Include/import.h` as::
struct _inittab {
- char *name;
+ char *name; /* ASCII encoded string */
PyObject* (*initfunc)(void);
};
diff --git a/Doc/c-api/list.rst b/Doc/c-api/list.rst
index feb9015..5b263a7 100644
--- a/Doc/c-api/list.rst
+++ b/Doc/c-api/list.rst
@@ -142,3 +142,10 @@ List Objects
Return a new tuple object containing the contents of *list*; equivalent to
``tuple(list)``.
+
+
+.. c:function:: int PyList_ClearFreeList()
+
+ Clear the free list. Return the total number of freed items.
+
+ .. versionadded:: 3.3
diff --git a/Doc/c-api/long.rst b/Doc/c-api/long.rst
index b2295e0..4c295fa 100644
--- a/Doc/c-api/long.rst
+++ b/Doc/c-api/long.rst
@@ -100,6 +100,20 @@ All integers are implemented as "long" integer objects of arbitrary size.
string is first encoded to a byte string using :c:func:`PyUnicode_EncodeDecimal`
and then converted using :c:func:`PyLong_FromString`.
+ .. deprecated-removed:: 3.3 4.0
+ Part of the old-style :c:type:`Py_UNICODE` API; please migrate to using
+ :c:func:`PyLong_FromUnicodeObject`.
+
+
+.. c:function:: PyObject* PyLong_FromUnicodeObject(PyObject *u, int base)
+
+ Convert a sequence of Unicode digits in the string *u* to a Python integer
+ value. The Unicode string is first encoded to a byte string using
+ :c:func:`PyUnicode_EncodeDecimal` and then converted using
+ :c:func:`PyLong_FromString`.
+
+ .. versionadded:: 3.3
+
.. c:function:: PyObject* PyLong_FromVoidPtr(void *p)
diff --git a/Doc/c-api/module.rst b/Doc/c-api/module.rst
index ffd68e3..32587be 100644
--- a/Doc/c-api/module.rst
+++ b/Doc/c-api/module.rst
@@ -29,7 +29,7 @@ There are only a few functions special to module objects.
:c:data:`PyModule_Type`.
-.. c:function:: PyObject* PyModule_New(const char *name)
+.. c:function:: PyObject* PyModule_NewObject(PyObject *name)
.. index::
single: __name__ (module attribute)
@@ -40,6 +40,14 @@ There are only a few functions special to module objects.
Only the module's :attr:`__doc__` and :attr:`__name__` attributes are filled in;
the caller is responsible for providing a :attr:`__file__` attribute.
+ .. versionadded:: 3.3
+
+
+.. c:function:: PyObject* PyModule_New(const char *name)
+
+ Similar to :c:func:`PyImport_NewObject`, but the name is an UTF-8 encoded
+ string instead of a Unicode object.
+
.. c:function:: PyObject* PyModule_GetDict(PyObject *module)
@@ -52,7 +60,7 @@ There are only a few functions special to module objects.
manipulate a module's :attr:`__dict__`.
-.. c:function:: char* PyModule_GetName(PyObject *module)
+.. c:function:: PyObject* PyModule_GetNameObject(PyObject *module)
.. index::
single: __name__ (module attribute)
@@ -61,15 +69,13 @@ There are only a few functions special to module objects.
Return *module*'s :attr:`__name__` value. If the module does not provide one,
or if it is not a string, :exc:`SystemError` is raised and *NULL* is returned.
+ .. versionadded:: 3.3
-.. c:function:: char* PyModule_GetFilename(PyObject *module)
- Similar to :c:func:`PyModule_GetFilenameObject` but return the filename
- encoded to 'utf-8'.
+.. c:function:: char* PyModule_GetName(PyObject *module)
- .. deprecated:: 3.2
- :c:func:`PyModule_GetFilename` raises :c:type:`UnicodeEncodeError` on
- unencodable filenames, use :c:func:`PyModule_GetFilenameObject` instead.
+ Similar to :c:func:`PyModule_GetNameObject` but return the name encoded to
+ ``'utf-8'``.
.. c:function:: PyObject* PyModule_GetFilenameObject(PyObject *module)
@@ -81,11 +87,21 @@ There are only a few functions special to module objects.
Return the name of the file from which *module* was loaded using *module*'s
:attr:`__file__` attribute. If this is not defined, or if it is not a
unicode string, raise :exc:`SystemError` and return *NULL*; otherwise return
- a reference to a :c:type:`PyUnicodeObject`.
+ a reference to a Unicode object.
.. versionadded:: 3.2
+.. c:function:: char* PyModule_GetFilename(PyObject *module)
+
+ Similar to :c:func:`PyModule_GetFilenameObject` but return the filename
+ encoded to 'utf-8'.
+
+ .. deprecated:: 3.2
+ :c:func:`PyModule_GetFilename` raises :c:type:`UnicodeEncodeError` on
+ unencodable filenames, use :c:func:`PyModule_GetFilenameObject` instead.
+
+
.. c:function:: void* PyModule_GetState(PyObject *module)
Return the "state" of the module, that is, a pointer to the block of memory
diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst
index d0d45ad..88ba5ac 100644
--- a/Doc/c-api/object.rst
+++ b/Doc/c-api/object.rst
@@ -6,6 +6,19 @@ Object Protocol
===============
+.. c:var:: PyObject* Py_NotImplemented
+
+ The ``NotImplemented`` singleton, used to signal that an operation is
+ not implemented for the given type combination.
+
+
+.. c:macro:: Py_RETURN_NOTIMPLEMENTED
+
+ Properly handle returning :c:data:`Py_NotImplemented` from within a C
+ function (that is, increment the reference count of NotImplemented and
+ return it).
+
+
.. c:function:: int PyObject_Print(PyObject *o, FILE *fp, int flags)
Print an object *o*, on file *fp*. Returns ``-1`` on error. The flags argument
diff --git a/Doc/c-api/set.rst b/Doc/c-api/set.rst
index 66b47c4..5f0ef90 100644
--- a/Doc/c-api/set.rst
+++ b/Doc/c-api/set.rst
@@ -157,3 +157,10 @@ subtypes but not for instances of :class:`frozenset` or its subtypes.
.. c:function:: int PySet_Clear(PyObject *set)
Empty an existing set of all elements.
+
+
+.. c:function:: int PySet_ClearFreeList()
+
+ Clear the free list. Return the total number of freed items.
+
+ .. versionadded:: 3.3
diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst
index 3500654..43e3d2f 100644
--- a/Doc/c-api/unicode.rst
+++ b/Doc/c-api/unicode.rst
@@ -6,38 +6,72 @@ Unicode Objects and Codecs
--------------------------
.. sectionauthor:: Marc-Andre Lemburg <mal@lemburg.com>
+.. sectionauthor:: Georg Brandl <georg@python.org>
Unicode Objects
^^^^^^^^^^^^^^^
+Since the implementation of :pep:`393` in Python 3.3, Unicode objects internally
+use a variety of representations, in order to allow handling the complete range
+of Unicode characters while staying memory efficient. There are special cases
+for strings where all code points are below 128, 256, or 65536; otherwise, code
+points must be below 1114112 (which is the full Unicode range).
+
+:c:type:`Py_UNICODE*` and UTF-8 representations are created on demand and cached
+in the Unicode object. The :c:type:`Py_UNICODE*` representation is deprecated
+and inefficient; it should be avoided in performance- or memory-sensitive
+situations.
+
+Due to the transition between the old APIs and the new APIs, unicode objects
+can internally be in two states depending on how they were created:
+
+* "canonical" unicode objects are all objects created by a non-deprecated
+ unicode API. They use the most efficient representation allowed by the
+ implementation.
+
+* "legacy" unicode objects have been created through one of the deprecated
+ APIs (typically :c:func:`PyUnicode_FromUnicode`) and only bear the
+ :c:type:`Py_UNICODE*` representation; you will have to call
+ :c:func:`PyUnicode_READY` on them before calling any other API.
+
+
Unicode Type
""""""""""""
These are the basic Unicode object types used for the Unicode implementation in
Python:
+.. c:type:: Py_UCS4
+ Py_UCS2
+ Py_UCS1
+
+ These types are typedefs for unsigned integer types wide enough to contain
+ characters of 32 bits, 16 bits and 8 bits, respectively. When dealing with
+ single Unicode characters, use :c:type:`Py_UCS4`.
+
+ .. versionadded:: 3.3
+
.. c:type:: Py_UNICODE
- This type represents the storage type which is used by Python internally as
- basis for holding Unicode ordinals. Python's default builds use a 16-bit type
- for :c:type:`Py_UNICODE` and store Unicode values internally as UCS2. It is also
- possible to build a UCS4 version of Python (most recent Linux distributions come
- with UCS4 builds of Python). These builds then use a 32-bit type for
- :c:type:`Py_UNICODE` and store Unicode data internally as UCS4. On platforms
- where :c:type:`wchar_t` is available and compatible with the chosen Python
- Unicode build variant, :c:type:`Py_UNICODE` is a typedef alias for
- :c:type:`wchar_t` to enhance native platform compatibility. On all other
- platforms, :c:type:`Py_UNICODE` is a typedef alias for either :c:type:`unsigned
- short` (UCS2) or :c:type:`unsigned long` (UCS4).
+ This is a typedef of :c:type:`wchar_t`, which is a 16-bit type or 32-bit type
+ depending on the platform.
-Note that UCS2 and UCS4 Python builds are not binary compatible. Please keep
-this in mind when writing extensions or interfaces.
+ .. versionchanged:: 3.3
+ In previous versions, this was a 16-bit type or a 32-bit type depending on
+ whether you selected a "narrow" or "wide" Unicode version of Python at
+ build time.
-.. c:type:: PyUnicodeObject
+.. c:type:: PyASCIIObject
+ PyCompactUnicodeObject
+ PyUnicodeObject
- This subtype of :c:type:`PyObject` represents a Python Unicode object.
+ These subtypes of :c:type:`PyObject` represent a Python Unicode object. In
+ almost all cases, they shouldn't be used directly, since all API functions
+ that deal with Unicode objects take and return :c:type:`PyObject` pointers.
+
+ .. versionadded:: 3.3
.. c:var:: PyTypeObject PyUnicode_Type
@@ -45,10 +79,10 @@ this in mind when writing extensions or interfaces.
This instance of :c:type:`PyTypeObject` represents the Python Unicode type. It
is exposed to Python code as ``str``.
+
The following APIs are really C macros and can be used to do fast checks and to
access internal read-only data of Unicode objects:
-
.. c:function:: int PyUnicode_Check(PyObject *o)
Return true if the object *o* is a Unicode object or an instance of a Unicode
@@ -61,28 +95,106 @@ access internal read-only data of Unicode objects:
subtype.
-.. c:function:: Py_ssize_t PyUnicode_GET_SIZE(PyObject *o)
+.. c:function:: int PyUnicode_READY(PyObject *o)
- Return the size of the object. *o* has to be a :c:type:`PyUnicodeObject` (not
- checked).
+ Ensure the string object *o* is in the "canonical" representation. This is
+ required before using any of the access macros described below.
+ .. XXX expand on when it is not required
-.. c:function:: Py_ssize_t PyUnicode_GET_DATA_SIZE(PyObject *o)
+ Returns 0 on success and -1 with an exception set on failure, which in
+ particular happens if memory allocation fails.
- Return the size of the object's internal buffer in bytes. *o* has to be a
- :c:type:`PyUnicodeObject` (not checked).
+ .. versionadded:: 3.3
-.. c:function:: Py_UNICODE* PyUnicode_AS_UNICODE(PyObject *o)
+.. c:function:: Py_ssize_t PyUnicode_GET_LENGTH(PyObject *o)
+
+ Return the length of the Unicode string, in code points. *o* has to be a
+ Unicode object in the "canonical" representation (not checked).
+
+ .. versionadded:: 3.3
+
+
+.. c:function:: Py_UCS1* PyUnicode_1BYTE_DATA(PyObject *o)
+ Py_UCS2* PyUnicode_2BYTE_DATA(PyObject *o)
+ Py_UCS4* PyUnicode_4BYTE_DATA(PyObject *o)
+
+ Return a pointer to the canonical representation cast to UCS1, UCS2 or UCS4
+ integer types for direct character access. No checks are performed if the
+ canonical representation has the correct character size; use
+ :c:func:`PyUnicode_KIND` to select the right macro. Make sure
+ :c:func:`PyUnicode_READY` has been called before accessing this.
+
+ .. versionadded:: 3.3
+
+
+.. c:macro:: PyUnicode_WCHAR_KIND
+ PyUnicode_1BYTE_KIND
+ PyUnicode_2BYTE_KIND
+ PyUnicode_4BYTE_KIND
+
+ Return values of the :c:func:`PyUnicode_KIND` macro.
+
+ .. versionadded:: 3.3
+
+
+.. c:function:: int PyUnicode_KIND(PyObject *o)
+
+ Return one of the PyUnicode kind constants (see above) that indicate how many
+ bytes per character this Unicode object uses to store its data. *o* has to
+ be a Unicode object in the "canonical" representation (not checked).
+
+ .. XXX document "0" return value?
+
+ .. versionadded:: 3.3
+
+
+.. c:function:: void* PyUnicode_DATA(PyObject *o)
+
+ Return a void pointer to the raw unicode buffer. *o* has to be a Unicode
+ object in the "canonical" representation (not checked).
+
+ .. versionadded:: 3.3
+
+
+.. c:function:: void PyUnicode_WRITE(int kind, void *data, Py_ssize_t index, \
+ Py_UCS4 value)
+
+ Write into a canonical representation *data* (as obtained with
+ :c:func:`PyUnicode_DATA`). This macro does not do any sanity checks and is
+ intended for usage in loops. The caller should cache the *kind* value and
+ *data* pointer as obtained from other macro calls. *index* is the index in
+ the string (starts at 0) and *value* is the new code point value which should
+ be written to that location.
+
+ .. versionadded:: 3.3
+
+
+.. c:function:: Py_UCS4 PyUnicode_READ(int kind, void *data, Py_ssize_t index)
+
+ Read a code point from a canonical representation *data* (as obtained with
+ :c:func:`PyUnicode_DATA`). No checks or ready calls are performed.
+
+ .. versionadded:: 3.3
+
+
+.. c:function:: Py_UCS4 PyUnicode_READ_CHAR(PyObject *o, Py_ssize_t index)
+
+ Read a character from a Unicode object *o*, which must be in the "canonical"
+ representation. This is less efficient than :c:func:`PyUnicode_READ` if you
+ do multiple consecutive reads.
- Return a pointer to the internal :c:type:`Py_UNICODE` buffer of the object. *o*
- has to be a :c:type:`PyUnicodeObject` (not checked).
+ .. versionadded:: 3.3
-.. c:function:: const char* PyUnicode_AS_DATA(PyObject *o)
+.. c:function:: PyUnicode_MAX_CHAR_VALUE(PyObject *o)
- Return a pointer to the internal buffer of the object. *o* has to be a
- :c:type:`PyUnicodeObject` (not checked).
+ Return the maximum code point that is suitable for creating another string
+ based on *o*, which must be in the "canonical" representation. This is
+ always an approximation but more efficient than iterating over the string.
+
+ .. versionadded:: 3.3
.. c:function:: int PyUnicode_ClearFreeList()
@@ -90,6 +202,46 @@ access internal read-only data of Unicode objects:
Clear the free list. Return the total number of freed items.
+.. c:function:: Py_ssize_t PyUnicode_GET_SIZE(PyObject *o)
+
+ Return the size of the deprecated :c:type:`Py_UNICODE` representation, in
+ code units (this includes surrogate pairs as 2 units). *o* has to be a
+ Unicode object (not checked).
+
+ .. deprecated-removed:: 3.3 4.0
+ Part of the old-style Unicode API, please migrate to using
+ :c:func:`PyUnicode_GET_LENGTH`.
+
+
+.. c:function:: Py_ssize_t PyUnicode_GET_DATA_SIZE(PyObject *o)
+
+ Return the size of the deprecated :c:type:`Py_UNICODE` representation in
+ bytes. *o* has to be a Unicode object (not checked).
+
+ .. deprecated-removed:: 3.3 4.0
+ Part of the old-style Unicode API, please migrate to using
+ :c:func:`PyUnicode_GET_LENGTH`.
+
+
+.. c:function:: Py_UNICODE* PyUnicode_AS_UNICODE(PyObject *o)
+ const char* PyUnicode_AS_DATA(PyObject *o)
+
+ Return a pointer to a :c:type:`Py_UNICODE` representation of the object. The
+ ``AS_DATA`` form casts the pointer to :c:type:`const char *`. *o* has to be
+ a Unicode object (not checked).
+
+ .. versionchanged:: 3.3
+ This macro is now inefficient -- because in many cases the
+ :c:type:`Py_UNICODE` representation does not exist and needs to be created
+ -- and can fail (return *NULL* with an exception set). Try to port the
+ code to use the new :c:func:`PyUnicode_nBYTE_DATA` macros or use
+ :c:func:`PyUnicode_WRITE` or :c:func:`PyUnicode_READ`.
+
+ .. deprecated-removed:: 3.3 4.0
+ Part of the old-style Unicode API, please migrate to using the
+ :c:func:`PyUnicode_nBYTE_DATA` family of macros.
+
+
Unicode Character Properties
""""""""""""""""""""""""""""
@@ -195,31 +347,66 @@ These APIs can be used for fast direct character conversions:
possible. This macro does not raise exceptions.
-Plain Py_UNICODE
-""""""""""""""""
+These APIs can be used to work with surrogates:
+
+.. c:macro:: Py_UNICODE_IS_SURROGATE(ch)
+
+ Check if *ch* is a surrogate (``0xD800 <= ch <= 0xDFFF``).
+
+.. c:macro:: Py_UNICODE_IS_HIGH_SURROGATE(ch)
+
+ Check if *ch* is an high surrogate (``0xD800 <= ch <= 0xDBFF``).
+
+.. c:macro:: Py_UNICODE_IS_LOW_SURROGATE(ch)
+
+ Check if *ch* is a low surrogate (``0xDC00 <= ch <= 0xDFFF``).
+
+.. c:macro:: Py_UNICODE_JOIN_SURROGATES(high, low)
+
+ Join two surrogate characters and return a single Py_UCS4 value.
+ *high* and *low* are respectively the leading and trailing surrogates in a
+ surrogate pair.
+
+
+Creating and accessing Unicode strings
+""""""""""""""""""""""""""""""""""""""
To create Unicode objects and access their basic sequence properties, use these
APIs:
+.. c:function:: PyObject* PyUnicode_New(Py_ssize_t size, Py_UCS4 maxchar)
-.. c:function:: PyObject* PyUnicode_FromUnicode(const Py_UNICODE *u, Py_ssize_t size)
+ Create a new Unicode object. *maxchar* should be the true maximum code point
+ to be placed in the string. As an approximation, it can be rounded up to the
+ nearest value in the sequence 127, 255, 65535, 1114111.
+
+ This is the recommended way to allocate a new Unicode object. Objects
+ created using this function are not resizable.
+
+ .. versionadded:: 3.3
- Create a Unicode object from the Py_UNICODE buffer *u* of the given size. *u*
- may be *NULL* which causes the contents to be undefined. It is the user's
- responsibility to fill in the needed data. The buffer is copied into the new
- object. If the buffer is not *NULL*, the return value might be a shared object.
- Therefore, modification of the resulting Unicode object is only allowed when *u*
- is *NULL*.
+
+.. c:function:: PyObject* PyUnicode_FromKindAndData(int kind, const void *buffer, \
+ Py_ssize_t size)
+
+ Create a new Unicode object with the given *kind* (possible values are
+ :c:macro:`PyUnicode_1BYTE_KIND` etc., as returned by
+ :c:func:`PyUnicode_KIND`). The *buffer* must point to an array of *size*
+ units of 1, 2 or 4 bytes per character, as given by the kind.
+
+ .. versionadded:: 3.3
.. c:function:: PyObject* PyUnicode_FromStringAndSize(const char *u, Py_ssize_t size)
- Create a Unicode object from the char buffer *u*. The bytes will be interpreted
- as being UTF-8 encoded. *u* may also be *NULL* which
- causes the contents to be undefined. It is the user's responsibility to fill in
- the needed data. The buffer is copied into the new object. If the buffer is not
- *NULL*, the return value might be a shared object. Therefore, modification of
- the resulting Unicode object is only allowed when *u* is *NULL*.
+ Create a Unicode object from the char buffer *u*. The bytes will be
+ interpreted as being UTF-8 encoded. The buffer is copied into the new
+ object. If the buffer is not *NULL*, the return value might be a shared
+ object, i.e. modification of the data is not allowed.
+
+ If *u* is *NULL*, this function behaves like :c:func:`PyUnicode_FromUnicode`
+ with the buffer set to *NULL*. This usage is deprecated in favor of
+ :c:func:`PyUnicode_New`.
.. c:function:: PyObject *PyUnicode_FromString(const char *u)
@@ -260,18 +447,27 @@ APIs:
| :attr:`%ld` | long | Exactly equivalent to |
| | | ``printf("%ld")``. |
+-------------------+---------------------+--------------------------------+
+ | :attr:`%li` | long | Exactly equivalent to |
+ | | | ``printf("%li")``. |
+ +-------------------+---------------------+--------------------------------+
| :attr:`%lu` | unsigned long | Exactly equivalent to |
| | | ``printf("%lu")``. |
+-------------------+---------------------+--------------------------------+
| :attr:`%lld` | long long | Exactly equivalent to |
| | | ``printf("%lld")``. |
+-------------------+---------------------+--------------------------------+
+ | :attr:`%lli` | long long | Exactly equivalent to |
+ | | | ``printf("%lli")``. |
+ +-------------------+---------------------+--------------------------------+
| :attr:`%llu` | unsigned long long | Exactly equivalent to |
| | | ``printf("%llu")``. |
+-------------------+---------------------+--------------------------------+
| :attr:`%zd` | Py_ssize_t | Exactly equivalent to |
| | | ``printf("%zd")``. |
+-------------------+---------------------+--------------------------------+
+ | :attr:`%zi` | Py_ssize_t | Exactly equivalent to |
+ | | | ``printf("%zi")``. |
+ +-------------------+---------------------+--------------------------------+
| :attr:`%zu` | size_t | Exactly equivalent to |
| | | ``printf("%zu")``. |
+-------------------+---------------------+--------------------------------+
@@ -322,27 +518,163 @@ APIs:
.. versionchanged:: 3.2
Support for ``"%lld"`` and ``"%llu"`` added.
+ .. versionchanged:: 3.3
+ Support for ``"%li"``, ``"%lli"`` and ``"%zi"`` added.
+
.. c:function:: PyObject* PyUnicode_FromFormatV(const char *format, va_list vargs)
Identical to :c:func:`PyUnicode_FromFormat` except that it takes exactly two
arguments.
+
+.. c:function:: PyObject* PyUnicode_FromEncodedObject(PyObject *obj, \
+ const char *encoding, const char *errors)
+
+ Coerce an encoded object *obj* to an Unicode object and return a reference with
+ incremented refcount.
+
+ :class:`bytes`, :class:`bytearray` and other char buffer compatible objects
+ are decoded according to the given *encoding* and using the error handling
+ defined by *errors*. Both can be *NULL* to have the interface use the default
+ values (see the next section for details).
+
+ All other objects, including Unicode objects, cause a :exc:`TypeError` to be
+ set.
+
+ The API returns *NULL* if there was an error. The caller is responsible for
+ decref'ing the returned objects.
+
+
+.. c:function:: Py_ssize_t PyUnicode_GetLength(PyObject *unicode)
+
+ Return the length of the Unicode object, in code points.
+
+ .. versionadded:: 3.3
+
+
+.. c:function:: int PyUnicode_CopyCharacters(PyObject *to, Py_ssize_t to_start, \
+ PyObject *to, Py_ssize_t from_start, Py_ssize_t how_many)
+
+ Copy characters from one Unicode object into another. This function performs
+ character conversion when necessary and falls back to :c:func:`memcpy` if
+ possible. Returns ``-1`` and sets an exception on error, otherwise returns
+ ``0``.
+
+ .. versionadded:: 3.3
+
+
+.. c:function:: int PyUnicode_WriteChar(PyObject *unicode, Py_ssize_t index, \
+ Py_UCS4 character)
+
+ Write a character to a string. The string must have been created through
+ :c:func:`PyUnicode_New`. Since Unicode strings are supposed to be immutable,
+ the string must not be shared, or have been hashed yet.
+
+ This function checks that *unicode* is a Unicode object, that the index is
+ not out of bounds, and that the object can be modified safely (i.e. that it
+ its reference count is one), in contrast to the macro version
+ :c:func:`PyUnicode_WRITE_CHAR`.
+
+ .. versionadded:: 3.3
+
+
+.. c:function:: Py_UCS4 PyUnicode_ReadChar(PyObject *unicode, Py_ssize_t index)
+
+ Read a character from a string. This function checks that *unicode* is a
+ Unicode object and the index is not out of bounds, in contrast to the macro
+ version :c:func:`PyUnicode_READ_CHAR`.
+
+ .. versionadded:: 3.3
+
+
+.. c:function:: PyObject* PyUnicode_Substring(PyObject *str, Py_ssize_t start, \
+ Py_ssize_t end)
+
+ Return a substring of *str*, from character index *start* (included) to
+ character index *end* (excluded). Negative indices are not supported.
+
+ .. versionadded:: 3.3
+
+
+.. c:function:: Py_UCS4* PyUnicode_AsUCS4(PyObject *u, Py_UCS4 *buffer, \
+ Py_ssize_t buflen, int copy_null)
+
+ Copy the string *u* into a UCS4 buffer, including a null character, if
+ *copy_null* is set. Returns *NULL* and sets an exception on error (in
+ particular, a :exc:`ValueError` if *buflen* is smaller than the length of
+ *u*). *buffer* is returned on success.
+
+ .. versionadded:: 3.3
+
+
+.. c:function:: Py_UCS4* PyUnicode_AsUCS4Copy(PyObject *u)
+
+ Copy the string *u* into a new UCS4 buffer that is allocated using
+ :c:func:`PyMem_Malloc`. If this fails, *NULL* is returned with a
+ :exc:`MemoryError` set.
+
+ .. versionadded:: 3.3
+
+
+Deprecated Py_UNICODE APIs
+""""""""""""""""""""""""""
+
+.. deprecated-removed:: 3.3 4.0
+
+These API functions are deprecated with the implementation of :pep:`393`.
+Extension modules can continue using them, as they will not be removed in Python
+3.x, but need to be aware that their use can now cause performance and memory hits.
+
+
+.. c:function:: PyObject* PyUnicode_FromUnicode(const Py_UNICODE *u, Py_ssize_t size)
+
+ Create a Unicode object from the Py_UNICODE buffer *u* of the given size. *u*
+ may be *NULL* which causes the contents to be undefined. It is the user's
+ responsibility to fill in the needed data. The buffer is copied into the new
+ object.
+
+ If the buffer is not *NULL*, the return value might be a shared object.
+ Therefore, modification of the resulting Unicode object is only allowed when
+ *u* is *NULL*.
+
+ If the buffer is *NULL*, :c:func:`PyUnicode_READY` must be called once the
+ string content has been filled before using any of the access macros such as
+ :c:func:`PyUnicode_KIND`.
+
+ Please migrate to using :c:func:`PyUnicode_FromKindAndData` or
+ :c:func:`PyUnicode_New`.
+
+
+.. c:function:: Py_UNICODE* PyUnicode_AsUnicode(PyObject *unicode)
+
+ Return a read-only pointer to the Unicode object's internal
+ :c:type:`Py_UNICODE` buffer, or *NULL* on error. This will create the
+ :c:type:`Py_UNICODE*` representation of the object if it is not yet
+ available. Note that the resulting :c:type:`Py_UNICODE` string may contain
+ embedded null characters, which would cause the string to be truncated when
+ used in most C functions.
+
+ Please migrate to using :c:func:`PyUnicode_AsUCS4`,
+ :c:func:`PyUnicode_Substring`, :c:func:`PyUnicode_ReadChar` or similar new
+ APIs.
+
+
.. c:function:: PyObject* PyUnicode_TransformDecimalToASCII(Py_UNICODE *s, Py_ssize_t size)
Create a Unicode object by replacing all decimal digits in
:c:type:`Py_UNICODE` buffer of the given *size* by ASCII digits 0--9
- according to their decimal value. Return *NULL* if an exception
- occurs.
+ according to their decimal value. Return *NULL* if an exception occurs.
-.. c:function:: Py_UNICODE* PyUnicode_AsUnicode(PyObject *unicode)
+.. c:function:: Py_UNICODE* PyUnicode_AsUnicodeAndSize(PyObject *unicode, Py_ssize_t *size)
- Return a read-only pointer to the Unicode object's internal
- :c:type:`Py_UNICODE` buffer, *NULL* if *unicode* is not a Unicode object.
- Note that the resulting :c:type:`Py_UNICODE*` string may contain embedded
- null characters, which would cause the string to be truncated when used in
- most C functions.
+ Like :c:func:`PyUnicode_AsUnicode`, but also saves the :c:func:`Py_UNICODE`
+ array length in *size*. Note that the resulting :c:type:`Py_UNICODE*` string
+ may contain embedded null characters, which would cause the string to be
+ truncated when used in most C functions.
+
+ .. versionadded:: 3.3
.. c:function:: Py_UNICODE* PyUnicode_AsUnicodeCopy(PyObject *unicode)
@@ -350,44 +682,76 @@ APIs:
Create a copy of a Unicode string ending with a nul character. Return *NULL*
and raise a :exc:`MemoryError` exception on memory allocation failure,
otherwise return a new allocated buffer (use :c:func:`PyMem_Free` to free
- the buffer). Note that the resulting :c:type:`Py_UNICODE*` string may contain
- embedded null characters, which would cause the string to be truncated when
- used in most C functions.
+ the buffer). Note that the resulting :c:type:`Py_UNICODE*` string may
+ contain embedded null characters, which would cause the string to be
+ truncated when used in most C functions.
.. versionadded:: 3.2
+ Please migrate to using :c:func:`PyUnicode_AsUCS4Copy` or similar new APIs.
+
.. c:function:: Py_ssize_t PyUnicode_GetSize(PyObject *unicode)
- Return the length of the Unicode object.
+ Return the size of the deprecated :c:type:`Py_UNICODE` representation, in
+ code units (this includes surrogate pairs as 2 units).
+ Please migrate to using :c:func:`PyUnicode_GetLength`.
-.. c:function:: PyObject* PyUnicode_FromEncodedObject(PyObject *obj, const char *encoding, const char *errors)
- Coerce an encoded object *obj* to an Unicode object and return a reference with
- incremented refcount.
+.. c:function:: PyObject* PyUnicode_FromObject(PyObject *obj)
- :class:`bytes`, :class:`bytearray` and other char buffer compatible objects
- are decoded according to the given *encoding* and using the error handling
- defined by *errors*. Both can be *NULL* to have the interface use the default
- values (see the next section for details).
+ Shortcut for ``PyUnicode_FromEncodedObject(obj, NULL, "strict")`` which is used
+ throughout the interpreter whenever coercion to Unicode is needed.
- All other objects, including Unicode objects, cause a :exc:`TypeError` to be
- set.
- The API returns *NULL* if there was an error. The caller is responsible for
- decref'ing the returned objects.
+Locale Encoding
+"""""""""""""""
+The current locale encoding can be used to decode text from the operating
+system.
-.. c:function:: PyObject* PyUnicode_FromObject(PyObject *obj)
+.. c:function:: PyObject* PyUnicode_DecodeLocaleAndSize(const char *str, Py_ssize_t len, int surrogateescape)
- Shortcut for ``PyUnicode_FromEncodedObject(obj, NULL, "strict")`` which is used
- throughout the interpreter whenever coercion to Unicode is needed.
+ Decode a string from the current locale encoding. The decoder is strict if
+ *surrogateescape* is equal to zero, otherwise it uses the
+ ``'surrogateescape'`` error handler (:pep:`383`) to escape undecodable
+ bytes. If a byte sequence can be decoded as a surrogate character and
+ *surrogateescape* is not equal to zero, the byte sequence is escaped using
+ the ``'surrogateescape'`` error handler instead of being decoded. *str*
+ must end with a null character but cannot contain embedded null characters.
-If the platform supports :c:type:`wchar_t` and provides a header file wchar.h,
-Python can interface directly to this type using the following functions.
-Support is optimized if Python's own :c:type:`Py_UNICODE` type is identical to
-the system's :c:type:`wchar_t`.
+ .. seealso::
+
+ Use :c:func:`PyUnicode_DecodeFSDefaultAndSize` to decode a string from
+ :c:data:`Py_FileSystemDefaultEncoding` (the locale encoding read at
+ Python startup).
+
+ .. versionadded:: 3.3
+
+
+.. c:function:: PyObject* PyUnicode_DecodeLocale(const char *str, int surrogateescape)
+
+ Similar to :c:func:`PyUnicode_DecodeLocaleAndSize`, but compute the string
+ length using :c:func:`strlen`.
+
+ .. versionadded:: 3.3
+
+
+.. c:function:: PyObject* PyUnicode_EncodeLocale(PyObject *unicode, int surrogateescape)
+
+ Encode a Unicode object to the current locale encoding. The encoder is
+ strict if *surrogateescape* is equal to zero, otherwise it uses the
+ ``'surrogateescape'`` error handler (:pep:`383`). Return a :class:`bytes`
+ object. *str* cannot contain embedded null characters.
+
+ .. seealso::
+
+ Use :c:func:`PyUnicode_EncodeFSDefault` to encode a string to
+ :c:data:`Py_FileSystemDefaultEncoding` (the locale encoding read at
+ Python startup).
+
+ .. versionadded:: 3.3
File System Encoding
@@ -430,6 +794,13 @@ used, passing :c:func:`PyUnicode_FSDecoder` as the conversion function:
If :c:data:`Py_FileSystemDefaultEncoding` is not set, fall back to the
locale encoding.
+ .. seealso::
+
+ :c:data:`Py_FileSystemDefaultEncoding` is initialized at startup from the
+ locale encoding and cannot be modified later. If you need to decode a
+ string from the current locale encoding, use
+ :c:func:`PyUnicode_DecodeLocaleAndSize`.
+
.. versionchanged:: 3.2
Use ``'strict'`` error handler on Windows.
@@ -458,6 +829,13 @@ used, passing :c:func:`PyUnicode_FSDecoder` as the conversion function:
If :c:data:`Py_FileSystemDefaultEncoding` is not set, fall back to the
locale encoding.
+ .. seealso::
+
+ :c:data:`Py_FileSystemDefaultEncoding` is initialized at startup from the
+ locale encoding and cannot be modified later. If you need to encode a
+ string to the current locale encoding, use
+ :c:func:`PyUnicode_EncodeLocale`.
+
.. versionadded:: 3.2
@@ -479,9 +857,9 @@ wchar_t Support
Copy the Unicode object contents into the :c:type:`wchar_t` buffer *w*. At most
*size* :c:type:`wchar_t` characters are copied (excluding a possibly trailing
0-termination character). Return the number of :c:type:`wchar_t` characters
- copied or -1 in case of an error. Note that the resulting :c:type:`wchar_t`
+ copied or -1 in case of an error. Note that the resulting :c:type:`wchar_t*`
string may or may not be 0-terminated. It is the responsibility of the caller
- to make sure that the :c:type:`wchar_t` string is 0-terminated in case this is
+ to make sure that the :c:type:`wchar_t*` string is 0-terminated in case this is
required by the application. Also, note that the :c:type:`wchar_t*` string
might contain null characters, which would cause the string to be truncated
when used with most C functions.
@@ -497,12 +875,32 @@ wchar_t Support
Returns a buffer allocated by :c:func:`PyMem_Alloc` (use
:c:func:`PyMem_Free` to free it) on success. On error, returns *NULL*,
*\*size* is undefined and raises a :exc:`MemoryError`. Note that the
- resulting :c:type:`wchar_t*` string might contain null characters, which
+ resulting :c:type:`wchar_t` string might contain null characters, which
would cause the string to be truncated when used with most C functions.
.. versionadded:: 3.2
+UCS4 Support
+""""""""""""
+
+.. versionadded:: 3.3
+
+.. XXX are these meant to be public?
+
+.. c:function:: size_t Py_UCS4_strlen(const Py_UCS4 *u)
+ Py_UCS4* Py_UCS4_strcpy(Py_UCS4 *s1, const Py_UCS4 *s2)
+ Py_UCS4* Py_UCS4_strncpy(Py_UCS4 *s1, const Py_UCS4 *s2, size_t n)
+ Py_UCS4* Py_UCS4_strcat(Py_UCS4 *s1, const Py_UCS4 *s2)
+ int Py_UCS4_strcmp(const Py_UCS4 *s1, const Py_UCS4 *s2)
+ int Py_UCS4_strncmp(const Py_UCS4 *s1, const Py_UCS4 *s2, size_t n)
+ Py_UCS4* Py_UCS4_strchr(const Py_UCS4 *s, Py_UCS4 c)
+ Py_UCS4* Py_UCS4_strrchr(const Py_UCS4 *s, Py_UCS4 c)
+
+ These utility functions work on strings of :c:type:`Py_UCS4` characters and
+ otherwise behave like the C standard library functions with the same name.
+
+
.. _builtincodecs:
Built-in Codecs
@@ -537,7 +935,8 @@ Generic Codecs
These are the generic codec APIs:
-.. c:function:: PyObject* PyUnicode_Decode(const char *s, Py_ssize_t size, const char *encoding, const char *errors)
+.. c:function:: PyObject* PyUnicode_Decode(const char *s, Py_ssize_t size, \
+ const char *encoding, const char *errors)
Create a Unicode object by decoding *size* bytes of the encoded string *s*.
*encoding* and *errors* have the same meaning as the parameters of the same name
@@ -546,7 +945,18 @@ These are the generic codec APIs:
the codec.
-.. c:function:: PyObject* PyUnicode_Encode(const Py_UNICODE *s, Py_ssize_t size, const char *encoding, const char *errors)
+.. c:function:: PyObject* PyUnicode_AsEncodedString(PyObject *unicode, \
+ const char *encoding, const char *errors)
+
+ Encode a Unicode object and return the result as Python bytes object.
+ *encoding* and *errors* have the same meaning as the parameters of the same
+ name in the Unicode :meth:`encode` method. The codec to be used is looked up
+ using the Python codec registry. Return *NULL* if an exception was raised by
+ the codec.
+
+
+.. c:function:: PyObject* PyUnicode_Encode(const Py_UNICODE *s, Py_ssize_t size, \
+ const char *encoding, const char *errors)
Encode the :c:type:`Py_UNICODE` buffer *s* of the given *size* and return a Python
bytes object. *encoding* and *errors* have the same meaning as the
@@ -554,14 +964,9 @@ These are the generic codec APIs:
to be used is looked up using the Python codec registry. Return *NULL* if an
exception was raised by the codec.
-
-.. c:function:: PyObject* PyUnicode_AsEncodedString(PyObject *unicode, const char *encoding, const char *errors)
-
- Encode a Unicode object and return the result as Python bytes object.
- *encoding* and *errors* have the same meaning as the parameters of the same
- name in the Unicode :meth:`encode` method. The codec to be used is looked up
- using the Python codec registry. Return *NULL* if an exception was raised by
- the codec.
+ .. deprecated-removed:: 3.3 4.0
+ Part of the old-style :c:type:`Py_UNICODE` API; please migrate to using
+ :c:func:`PyUnicode_AsEncodedString`.
UTF-8 Codecs
@@ -576,7 +981,8 @@ These are the UTF-8 codec APIs:
*s*. Return *NULL* if an exception was raised by the codec.
-.. c:function:: PyObject* PyUnicode_DecodeUTF8Stateful(const char *s, Py_ssize_t size, const char *errors, Py_ssize_t *consumed)
+.. c:function:: PyObject* PyUnicode_DecodeUTF8Stateful(const char *s, Py_ssize_t size, \
+ const char *errors, Py_ssize_t *consumed)
If *consumed* is *NULL*, behave like :c:func:`PyUnicode_DecodeUTF8`. If
*consumed* is not *NULL*, trailing incomplete UTF-8 byte sequences will not be
@@ -584,18 +990,45 @@ These are the UTF-8 codec APIs:
that have been decoded will be stored in *consumed*.
+.. c:function:: PyObject* PyUnicode_AsUTF8String(PyObject *unicode)
+
+ Encode a Unicode object using UTF-8 and return the result as Python bytes
+ object. Error handling is "strict". Return *NULL* if an exception was
+ raised by the codec.
+
+
+.. c:function:: char* PyUnicode_AsUTF8AndSize(PyObject *unicode, Py_ssize_t *size)
+
+ Return a pointer to the default encoding (UTF-8) of the Unicode object, and
+ store the size of the encoded representation (in bytes) in *size*. *size*
+ can be *NULL*, in this case no size will be stored.
+
+ In the case of an error, *NULL* is returned with an exception set and no
+ *size* is stored.
+
+ This caches the UTF-8 representation of the string in the Unicode object, and
+ subsequent calls will return a pointer to the same buffer. The caller is not
+ responsible for deallocating the buffer.
+
+ .. versionadded:: 3.3
+
+
+.. c:function:: char* PyUnicode_AsUTF8(PyObject *unicode)
+
+ As :c:func:`PyUnicode_AsUTF8AndSize`, but does not store the size.
+
+ .. versionadded:: 3.3
+
+
.. c:function:: PyObject* PyUnicode_EncodeUTF8(const Py_UNICODE *s, Py_ssize_t size, const char *errors)
Encode the :c:type:`Py_UNICODE` buffer *s* of the given *size* using UTF-8 and
return a Python bytes object. Return *NULL* if an exception was raised by
the codec.
-
-.. c:function:: PyObject* PyUnicode_AsUTF8String(PyObject *unicode)
-
- Encode a Unicode object using UTF-8 and return the result as Python bytes
- object. Error handling is "strict". Return *NULL* if an exception was
- raised by the codec.
+ .. deprecated-removed:: 3.3 4.0
+ Part of the old-style :c:type:`Py_UNICODE` API; please migrate to using
+ :c:func:`PyUnicode_AsUTF8String` or :c:func:`PyUnicode_AsUTF8AndSize`.
UTF-32 Codecs
@@ -604,7 +1037,8 @@ UTF-32 Codecs
These are the UTF-32 codec APIs:
-.. c:function:: PyObject* PyUnicode_DecodeUTF32(const char *s, Py_ssize_t size, const char *errors, int *byteorder)
+.. c:function:: PyObject* PyUnicode_DecodeUTF32(const char *s, Py_ssize_t size, \
+ const char *errors, int *byteorder)
Decode *size* bytes from a UTF-32 encoded buffer string and return the
corresponding Unicode object. *errors* (if non-*NULL*) defines the error
@@ -632,7 +1066,8 @@ These are the UTF-32 codec APIs:
Return *NULL* if an exception was raised by the codec.
-.. c:function:: PyObject* PyUnicode_DecodeUTF32Stateful(const char *s, Py_ssize_t size, const char *errors, int *byteorder, Py_ssize_t *consumed)
+.. c:function:: PyObject* PyUnicode_DecodeUTF32Stateful(const char *s, Py_ssize_t size, \
+ const char *errors, int *byteorder, Py_ssize_t *consumed)
If *consumed* is *NULL*, behave like :c:func:`PyUnicode_DecodeUTF32`. If
*consumed* is not *NULL*, :c:func:`PyUnicode_DecodeUTF32Stateful` will not treat
@@ -641,7 +1076,15 @@ These are the UTF-32 codec APIs:
that have been decoded will be stored in *consumed*.
-.. c:function:: PyObject* PyUnicode_EncodeUTF32(const Py_UNICODE *s, Py_ssize_t size, const char *errors, int byteorder)
+.. c:function:: PyObject* PyUnicode_AsUTF32String(PyObject *unicode)
+
+ Return a Python byte string using the UTF-32 encoding in native byte
+ order. The string always starts with a BOM mark. Error handling is "strict".
+ Return *NULL* if an exception was raised by the codec.
+
+
+.. c:function:: PyObject* PyUnicode_EncodeUTF32(const Py_UNICODE *s, Py_ssize_t size, \
+ const char *errors, int byteorder)
Return a Python bytes object holding the UTF-32 encoded value of the Unicode
data in *s*. Output is written according to the following byte order::
@@ -658,12 +1101,9 @@ These are the UTF-32 codec APIs:
Return *NULL* if an exception was raised by the codec.
-
-.. c:function:: PyObject* PyUnicode_AsUTF32String(PyObject *unicode)
-
- Return a Python byte string using the UTF-32 encoding in native byte
- order. The string always starts with a BOM mark. Error handling is "strict".
- Return *NULL* if an exception was raised by the codec.
+ .. deprecated-removed:: 3.3 4.0
+ Part of the old-style :c:type:`Py_UNICODE` API; please migrate to using
+ :c:func:`PyUnicode_AsUTF32String`.
UTF-16 Codecs
@@ -672,7 +1112,8 @@ UTF-16 Codecs
These are the UTF-16 codec APIs:
-.. c:function:: PyObject* PyUnicode_DecodeUTF16(const char *s, Py_ssize_t size, const char *errors, int *byteorder)
+.. c:function:: PyObject* PyUnicode_DecodeUTF16(const char *s, Py_ssize_t size, \
+ const char *errors, int *byteorder)
Decode *size* bytes from a UTF-16 encoded buffer string and return the
corresponding Unicode object. *errors* (if non-*NULL*) defines the error
@@ -699,7 +1140,8 @@ These are the UTF-16 codec APIs:
Return *NULL* if an exception was raised by the codec.
-.. c:function:: PyObject* PyUnicode_DecodeUTF16Stateful(const char *s, Py_ssize_t size, const char *errors, int *byteorder, Py_ssize_t *consumed)
+.. c:function:: PyObject* PyUnicode_DecodeUTF16Stateful(const char *s, Py_ssize_t size, \
+ const char *errors, int *byteorder, Py_ssize_t *consumed)
If *consumed* is *NULL*, behave like :c:func:`PyUnicode_DecodeUTF16`. If
*consumed* is not *NULL*, :c:func:`PyUnicode_DecodeUTF16Stateful` will not treat
@@ -708,7 +1150,15 @@ These are the UTF-16 codec APIs:
number of bytes that have been decoded will be stored in *consumed*.
-.. c:function:: PyObject* PyUnicode_EncodeUTF16(const Py_UNICODE *s, Py_ssize_t size, const char *errors, int byteorder)
+.. c:function:: PyObject* PyUnicode_AsUTF16String(PyObject *unicode)
+
+ Return a Python byte string using the UTF-16 encoding in native byte
+ order. The string always starts with a BOM mark. Error handling is "strict".
+ Return *NULL* if an exception was raised by the codec.
+
+
+.. c:function:: PyObject* PyUnicode_EncodeUTF16(const Py_UNICODE *s, Py_ssize_t size, \
+ const char *errors, int byteorder)
Return a Python bytes object holding the UTF-16 encoded value of the Unicode
data in *s*. Output is written according to the following byte order::
@@ -726,12 +1176,9 @@ These are the UTF-16 codec APIs:
Return *NULL* if an exception was raised by the codec.
-
-.. c:function:: PyObject* PyUnicode_AsUTF16String(PyObject *unicode)
-
- Return a Python byte string using the UTF-16 encoding in native byte
- order. The string always starts with a BOM mark. Error handling is "strict".
- Return *NULL* if an exception was raised by the codec.
+ .. deprecated-removed:: 3.3 4.0
+ Part of the old-style :c:type:`Py_UNICODE` API; please migrate to using
+ :c:func:`PyUnicode_AsUTF16String`.
UTF-7 Codecs
@@ -746,7 +1193,8 @@ These are the UTF-7 codec APIs:
*s*. Return *NULL* if an exception was raised by the codec.
-.. c:function:: PyObject* PyUnicode_DecodeUTF7Stateful(const char *s, Py_ssize_t size, const char *errors, Py_ssize_t *consumed)
+.. c:function:: PyObject* PyUnicode_DecodeUTF7Stateful(const char *s, Py_ssize_t size, \
+ const char *errors, Py_ssize_t *consumed)
If *consumed* is *NULL*, behave like :c:func:`PyUnicode_DecodeUTF7`. If
*consumed* is not *NULL*, trailing incomplete UTF-7 base-64 sections will not
@@ -754,7 +1202,8 @@ These are the UTF-7 codec APIs:
bytes that have been decoded will be stored in *consumed*.
-.. c:function:: PyObject* PyUnicode_EncodeUTF7(const Py_UNICODE *s, Py_ssize_t size, int base64SetO, int base64WhiteSpace, const char *errors)
+.. c:function:: PyObject* PyUnicode_EncodeUTF7(const Py_UNICODE *s, Py_ssize_t size, \
+ int base64SetO, int base64WhiteSpace, const char *errors)
Encode the :c:type:`Py_UNICODE` buffer of the given size using UTF-7 and
return a Python bytes object. Return *NULL* if an exception was raised by
@@ -765,6 +1214,11 @@ These are the UTF-7 codec APIs:
nonzero, whitespace will be encoded in base-64. Both are set to zero for the
Python "utf-7" codec.
+ .. deprecated-removed:: 3.3 4.0
+ Part of the old-style :c:type:`Py_UNICODE` API.
+
+ .. XXX replace with what?
+
Unicode-Escape Codecs
"""""""""""""""""""""
@@ -772,24 +1226,29 @@ Unicode-Escape Codecs
These are the "Unicode Escape" codec APIs:
-.. c:function:: PyObject* PyUnicode_DecodeUnicodeEscape(const char *s, Py_ssize_t size, const char *errors)
+.. c:function:: PyObject* PyUnicode_DecodeUnicodeEscape(const char *s, \
+ Py_ssize_t size, const char *errors)
Create a Unicode object by decoding *size* bytes of the Unicode-Escape encoded
string *s*. Return *NULL* if an exception was raised by the codec.
+.. c:function:: PyObject* PyUnicode_AsUnicodeEscapeString(PyObject *unicode)
+
+ Encode a Unicode object using Unicode-Escape and return the result as Python
+ string object. Error handling is "strict". Return *NULL* if an exception was
+ raised by the codec.
+
+
.. c:function:: PyObject* PyUnicode_EncodeUnicodeEscape(const Py_UNICODE *s, Py_ssize_t size)
Encode the :c:type:`Py_UNICODE` buffer of the given *size* using Unicode-Escape and
return a Python string object. Return *NULL* if an exception was raised by the
codec.
-
-.. c:function:: PyObject* PyUnicode_AsUnicodeEscapeString(PyObject *unicode)
-
- Encode a Unicode object using Unicode-Escape and return the result as Python
- string object. Error handling is "strict". Return *NULL* if an exception was
- raised by the codec.
+ .. deprecated-removed:: 3.3 4.0
+ Part of the old-style :c:type:`Py_UNICODE` API; please migrate to using
+ :c:func:`PyUnicode_AsUnicodeEscapeString`.
Raw-Unicode-Escape Codecs
@@ -798,19 +1257,13 @@ Raw-Unicode-Escape Codecs
These are the "Raw Unicode Escape" codec APIs:
-.. c:function:: PyObject* PyUnicode_DecodeRawUnicodeEscape(const char *s, Py_ssize_t size, const char *errors)
+.. c:function:: PyObject* PyUnicode_DecodeRawUnicodeEscape(const char *s, \
+ Py_ssize_t size, const char *errors)
Create a Unicode object by decoding *size* bytes of the Raw-Unicode-Escape
encoded string *s*. Return *NULL* if an exception was raised by the codec.
-.. c:function:: PyObject* PyUnicode_EncodeRawUnicodeEscape(const Py_UNICODE *s, Py_ssize_t size, const char *errors)
-
- Encode the :c:type:`Py_UNICODE` buffer of the given *size* using Raw-Unicode-Escape
- and return a Python string object. Return *NULL* if an exception was raised by
- the codec.
-
-
.. c:function:: PyObject* PyUnicode_AsRawUnicodeEscapeString(PyObject *unicode)
Encode a Unicode object using Raw-Unicode-Escape and return the result as
@@ -818,6 +1271,18 @@ These are the "Raw Unicode Escape" codec APIs:
was raised by the codec.
+.. c:function:: PyObject* PyUnicode_EncodeRawUnicodeEscape(const Py_UNICODE *s, \
+ Py_ssize_t size, const char *errors)
+
+ Encode the :c:type:`Py_UNICODE` buffer of the given *size* using Raw-Unicode-Escape
+ and return a Python string object. Return *NULL* if an exception was raised by
+ the codec.
+
+ .. deprecated-removed:: 3.3 4.0
+ Part of the old-style :c:type:`Py_UNICODE` API; please migrate to using
+ :c:func:`PyUnicode_AsRawUnicodeEscapeString`.
+
+
Latin-1 Codecs
""""""""""""""
@@ -831,18 +1296,22 @@ ordinals and only these are accepted by the codecs during encoding.
*s*. Return *NULL* if an exception was raised by the codec.
+.. c:function:: PyObject* PyUnicode_AsLatin1String(PyObject *unicode)
+
+ Encode a Unicode object using Latin-1 and return the result as Python bytes
+ object. Error handling is "strict". Return *NULL* if an exception was
+ raised by the codec.
+
+
.. c:function:: PyObject* PyUnicode_EncodeLatin1(const Py_UNICODE *s, Py_ssize_t size, const char *errors)
Encode the :c:type:`Py_UNICODE` buffer of the given *size* using Latin-1 and
return a Python bytes object. Return *NULL* if an exception was raised by
the codec.
-
-.. c:function:: PyObject* PyUnicode_AsLatin1String(PyObject *unicode)
-
- Encode a Unicode object using Latin-1 and return the result as Python bytes
- object. Error handling is "strict". Return *NULL* if an exception was
- raised by the codec.
+ .. deprecated-removed:: 3.3 4.0
+ Part of the old-style :c:type:`Py_UNICODE` API; please migrate to using
+ :c:func:`PyUnicode_AsLatin1String`.
ASCII Codecs
@@ -858,18 +1327,22 @@ codes generate errors.
*s*. Return *NULL* if an exception was raised by the codec.
+.. c:function:: PyObject* PyUnicode_AsASCIIString(PyObject *unicode)
+
+ Encode a Unicode object using ASCII and return the result as Python bytes
+ object. Error handling is "strict". Return *NULL* if an exception was
+ raised by the codec.
+
+
.. c:function:: PyObject* PyUnicode_EncodeASCII(const Py_UNICODE *s, Py_ssize_t size, const char *errors)
Encode the :c:type:`Py_UNICODE` buffer of the given *size* using ASCII and
return a Python bytes object. Return *NULL* if an exception was raised by
the codec.
-
-.. c:function:: PyObject* PyUnicode_AsASCIIString(PyObject *unicode)
-
- Encode a Unicode object using ASCII and return the result as Python bytes
- object. Error handling is "strict". Return *NULL* if an exception was
- raised by the codec.
+ .. deprecated-removed:: 3.3 4.0
+ Part of the old-style :c:type:`Py_UNICODE` API; please migrate to using
+ :c:func:`PyUnicode_AsASCIIString`.
Character Map Codecs
@@ -898,7 +1371,8 @@ characters to different code points.
These are the mapping codec APIs:
-.. c:function:: PyObject* PyUnicode_DecodeCharmap(const char *s, Py_ssize_t size, PyObject *mapping, const char *errors)
+.. c:function:: PyObject* PyUnicode_DecodeCharmap(const char *s, Py_ssize_t size, \
+ PyObject *mapping, const char *errors)
Create a Unicode object by decoding *size* bytes of the encoded string *s* using
the given *mapping* object. Return *NULL* if an exception was raised by the
@@ -908,13 +1382,6 @@ These are the mapping codec APIs:
treated as "undefined mapping".
-.. c:function:: PyObject* PyUnicode_EncodeCharmap(const Py_UNICODE *s, Py_ssize_t size, PyObject *mapping, const char *errors)
-
- Encode the :c:type:`Py_UNICODE` buffer of the given *size* using the given
- *mapping* object and return a Python string object. Return *NULL* if an
- exception was raised by the codec.
-
-
.. c:function:: PyObject* PyUnicode_AsCharmapString(PyObject *unicode, PyObject *mapping)
Encode a Unicode object using the given *mapping* object and return the result
@@ -924,7 +1391,8 @@ These are the mapping codec APIs:
The following codec API is special in that maps Unicode to Unicode.
-.. c:function:: PyObject* PyUnicode_TranslateCharmap(const Py_UNICODE *s, Py_ssize_t size, PyObject *table, const char *errors)
+.. c:function:: PyObject* PyUnicode_TranslateCharmap(const Py_UNICODE *s, Py_ssize_t size, \
+ PyObject *table, const char *errors)
Translate a :c:type:`Py_UNICODE` buffer of the given *size* by applying a
character mapping *table* to it and return the resulting Unicode object. Return
@@ -937,6 +1405,22 @@ The following codec API is special in that maps Unicode to Unicode.
and sequences work well. Unmapped character ordinals (ones which cause a
:exc:`LookupError`) are left untouched and are copied as-is.
+ .. deprecated-removed:: 3.3 4.0
+ Part of the old-style :c:type:`Py_UNICODE` API.
+
+ .. XXX replace with what?
+
+
+.. c:function:: PyObject* PyUnicode_EncodeCharmap(const Py_UNICODE *s, Py_ssize_t size, \
+ PyObject *mapping, const char *errors)
+
+ Encode the :c:type:`Py_UNICODE` buffer of the given *size* using the given
+ *mapping* object and return a Python string object. Return *NULL* if an
+ exception was raised by the codec.
+
+ .. deprecated-removed:: 3.3 4.0
+ Part of the old-style :c:type:`Py_UNICODE` API; please migrate to using
+ :c:func:`PyUnicode_AsCharmapString`.
MBCS codecs for Windows
@@ -953,7 +1437,8 @@ the user settings on the machine running the codec.
Return *NULL* if an exception was raised by the codec.
-.. c:function:: PyObject* PyUnicode_DecodeMBCSStateful(const char *s, int size, const char *errors, int *consumed)
+.. c:function:: PyObject* PyUnicode_DecodeMBCSStateful(const char *s, int size, \
+ const char *errors, int *consumed)
If *consumed* is *NULL*, behave like :c:func:`PyUnicode_DecodeMBCS`. If
*consumed* is not *NULL*, :c:func:`PyUnicode_DecodeMBCSStateful` will not decode
@@ -961,18 +1446,31 @@ the user settings on the machine running the codec.
in *consumed*.
+.. c:function:: PyObject* PyUnicode_AsMBCSString(PyObject *unicode)
+
+ Encode a Unicode object using MBCS and return the result as Python bytes
+ object. Error handling is "strict". Return *NULL* if an exception was
+ raised by the codec.
+
+
+.. c:function:: PyObject* PyUnicode_EncodeCodePage(int code_page, PyObject *unicode, const char *errors)
+
+ Encode the Unicode object using the specified code page and return a Python
+ bytes object. Return *NULL* if an exception was raised by the codec. Use
+ :c:data:`CP_ACP` code page to get the MBCS encoder.
+
+ .. versionadded:: 3.3
+
+
.. c:function:: PyObject* PyUnicode_EncodeMBCS(const Py_UNICODE *s, Py_ssize_t size, const char *errors)
Encode the :c:type:`Py_UNICODE` buffer of the given *size* using MBCS and return
a Python bytes object. Return *NULL* if an exception was raised by the
codec.
-
-.. c:function:: PyObject* PyUnicode_AsMBCSString(PyObject *unicode)
-
- Encode a Unicode object using MBCS and return the result as Python bytes
- object. Error handling is "strict". Return *NULL* if an exception was
- raised by the codec.
+ .. deprecated-removed:: 3.3 4.0
+ Part of the old-style :c:type:`Py_UNICODE` API; please migrate to using
+ :c:func:`PyUnicode_AsMBCSString` or :c:func:`PyUnicode_EncodeCodePage`.
Methods & Slots
@@ -1011,7 +1509,8 @@ They all return *NULL* or ``-1`` if an exception occurs.
characters are not included in the resulting strings.
-.. c:function:: PyObject* PyUnicode_Translate(PyObject *str, PyObject *table, const char *errors)
+.. c:function:: PyObject* PyUnicode_Translate(PyObject *str, PyObject *table, \
+ const char *errors)
Translate a string by applying a character mapping table to it and return the
resulting Unicode object.
@@ -1033,14 +1532,16 @@ They all return *NULL* or ``-1`` if an exception occurs.
Unicode string.
-.. c:function:: int PyUnicode_Tailmatch(PyObject *str, PyObject *substr, Py_ssize_t start, Py_ssize_t end, int direction)
+.. c:function:: int PyUnicode_Tailmatch(PyObject *str, PyObject *substr, \
+ Py_ssize_t start, Py_ssize_t end, int direction)
Return 1 if *substr* matches ``str[start:end]`` at the given tail end
(*direction* == -1 means to do a prefix match, *direction* == 1 a suffix match),
0 otherwise. Return ``-1`` if an error occurred.
-.. c:function:: Py_ssize_t PyUnicode_Find(PyObject *str, PyObject *substr, Py_ssize_t start, Py_ssize_t end, int direction)
+.. c:function:: Py_ssize_t PyUnicode_Find(PyObject *str, PyObject *substr, \
+ Py_ssize_t start, Py_ssize_t end, int direction)
Return the first position of *substr* in ``str[start:end]`` using the given
*direction* (*direction* == 1 means to do a forward search, *direction* == -1 a
@@ -1049,13 +1550,27 @@ They all return *NULL* or ``-1`` if an exception occurs.
occurred and an exception has been set.
-.. c:function:: Py_ssize_t PyUnicode_Count(PyObject *str, PyObject *substr, Py_ssize_t start, Py_ssize_t end)
+.. c:function:: Py_ssize_t PyUnicode_FindChar(PyObject *str, Py_UCS4 ch, \
+ Py_ssize_t start, Py_ssize_t end, int direction)
+
+ Return the first position of the character *ch* in ``str[start:end]`` using
+ the given *direction* (*direction* == 1 means to do a forward search,
+ *direction* == -1 a backward search). The return value is the index of the
+ first match; a value of ``-1`` indicates that no match was found, and ``-2``
+ indicates that an error occurred and an exception has been set.
+
+ .. versionadded:: 3.3
+
+
+.. c:function:: Py_ssize_t PyUnicode_Count(PyObject *str, PyObject *substr, \
+ Py_ssize_t start, Py_ssize_t end)
Return the number of non-overlapping occurrences of *substr* in
``str[start:end]``. Return ``-1`` if an error occurred.
-.. c:function:: PyObject* PyUnicode_Replace(PyObject *str, PyObject *substr, PyObject *replstr, Py_ssize_t maxcount)
+.. c:function:: PyObject* PyUnicode_Replace(PyObject *str, PyObject *substr, \
+ PyObject *replstr, Py_ssize_t maxcount)
Replace at most *maxcount* occurrences of *substr* in *str* with *replstr* and
return the resulting Unicode object. *maxcount* == -1 means replace all
@@ -1103,8 +1618,8 @@ They all return *NULL* or ``-1`` if an exception occurs.
Check whether *element* is contained in *container* and return true or false
accordingly.
- *element* has to coerce to a one element Unicode string. ``-1`` is returned if
- there was an error.
+ *element* has to coerce to a one element Unicode string. ``-1`` is returned
+ if there was an error.
.. c:function:: void PyUnicode_InternInPlace(PyObject **string)
@@ -1123,7 +1638,6 @@ They all return *NULL* or ``-1`` if an exception occurs.
.. c:function:: PyObject* PyUnicode_InternFromString(const char *v)
A combination of :c:func:`PyUnicode_FromString` and
- :c:func:`PyUnicode_InternInPlace`, returning either a new unicode string object
- that has been interned, or a new ("owned") reference to an earlier interned
- string object with the same value.
-
+ :c:func:`PyUnicode_InternInPlace`, returning either a new unicode string
+ object that has been interned, or a new ("owned") reference to an earlier
+ interned string object with the same value.
diff --git a/Doc/contents.rst b/Doc/contents.rst
index e938fcd..e9d1771 100644
--- a/Doc/contents.rst
+++ b/Doc/contents.rst
@@ -11,7 +11,7 @@
library/index.rst
extending/index.rst
c-api/index.rst
- distutils/index.rst
+ packaging/index.rst
install/index.rst
documenting/index.rst
howto/index.rst
diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat
index c7d7bd1..a1004ad 100644
--- a/Doc/data/refcounts.dat
+++ b/Doc/data/refcounts.dat
@@ -465,6 +465,11 @@ PyFunction_New:PyObject*::+1:
PyFunction_New:PyObject*:code:+1:
PyFunction_New:PyObject*:globals:+1:
+PyFunction_NewWithQualName:PyObject*::+1:
+PyFunction_NewWithQualName:PyObject*:code:+1:
+PyFunction_NewWithQualName:PyObject*:globals:+1:
+PyFunction_NewWithQualName:PyObject*:qualname:+1:
+
PyFunction_SetClosure:int:::
PyFunction_SetClosure:PyObject*:op:0:
PyFunction_SetClosure:PyObject*:closure:+1:
diff --git a/Doc/distutils/apiref.rst b/Doc/distutils/apiref.rst
index b3def22..97f1bed 100644
--- a/Doc/distutils/apiref.rst
+++ b/Doc/distutils/apiref.rst
@@ -160,7 +160,7 @@ the full reference.
.. class:: Extension
The Extension class describes a single C or C++extension module in a setup
- script. It accepts the following keyword arguments in its constructor
+ script. It accepts the following keyword arguments in its constructor:
+------------------------+--------------------------------+---------------------------+
| argument name | value | type |
@@ -1162,12 +1162,11 @@ other utility module.
.. function:: grok_environment_error(exc[, prefix='error: '])
- Generate a useful error message from an :exc:`EnvironmentError` (:exc:`IOError`
- or :exc:`OSError`) exception object. Handles Python 1.5.1 and later styles,
- and does what it can to deal with exception objects that don't have a filename
- (which happens when the error is due to a two-file operation, such as
- :func:`rename` or :func:`link`). Returns the error message as a string
- prefixed with *prefix*.
+ Generate a useful error message from an :exc:`OSError` exception object.
+ Handles Python 1.5.1 and later styles, and does what it can to deal with
+ exception objects that don't have a filename (which happens when the error
+ is due to a two-file operation, such as :func:`rename` or :func:`link`).
+ Returns the error message as a string prefixed with *prefix*.
.. function:: split_quoted(s)
diff --git a/Doc/distutils/index.rst b/Doc/distutils/index.rst
index ace8280..c8dd9f4 100644
--- a/Doc/distutils/index.rst
+++ b/Doc/distutils/index.rst
@@ -14,9 +14,12 @@ the module developer's point of view, describing how to use the Distutils to
make Python modules and extensions easily available to a wider audience with
very little overhead for build/release/install mechanics.
+.. deprecated:: 3.3
+ :mod:`packaging` replaces Distutils. See :ref:`packaging-index` and
+ :ref:`packaging-install-index`.
+
.. toctree::
:maxdepth: 2
- :numbered:
introduction.rst
setupscript.rst
@@ -29,3 +32,10 @@ very little overhead for build/release/install mechanics.
extending.rst
commandref.rst
apiref.rst
+
+Another document describes how to install modules and extensions packaged
+following the above guidelines:
+
+.. toctree::
+
+ install.rst
diff --git a/Doc/distutils/install.rst b/Doc/distutils/install.rst
new file mode 100644
index 0000000..b20f1fb
--- /dev/null
+++ b/Doc/distutils/install.rst
@@ -0,0 +1,1086 @@
+.. highlightlang:: none
+
+.. _install-index:
+
+*****************************
+ Installing Python Modules
+*****************************
+
+:Author: Greg Ward
+:Release: |version|
+:Date: |today|
+
+.. TODO: Fill in XXX comments
+
+.. The audience for this document includes people who don't know anything
+ about Python and aren't about to learn the language just in order to
+ install and maintain it for their users, i.e. system administrators.
+ Thus, I have to be sure to explain the basics at some point:
+ sys.path and PYTHONPATH at least. Should probably give pointers to
+ other docs on "import site", PYTHONSTARTUP, PYTHONHOME, etc.
+
+ Finally, it might be useful to include all the material from my "Care
+ and Feeding of a Python Installation" talk in here somewhere. Yow!
+
+.. topic:: Abstract
+
+ This document describes the Python Distribution Utilities ("Distutils") from the
+ end-user's point-of-view, describing how to extend the capabilities of a
+ standard Python installation by building and installing third-party Python
+ modules and extensions.
+
+
+.. _inst-intro:
+
+Introduction
+============
+
+Although Python's extensive standard library covers many programming needs,
+there often comes a time when you need to add some new functionality to your
+Python installation in the form of third-party modules. This might be necessary
+to support your own programming, or to support an application that you want to
+use and that happens to be written in Python.
+
+In the past, there has been little support for adding third-party modules to an
+existing Python installation. With the introduction of the Python Distribution
+Utilities (Distutils for short) in Python 2.0, this changed.
+
+This document is aimed primarily at the people who need to install third-party
+Python modules: end-users and system administrators who just need to get some
+Python application running, and existing Python programmers who want to add some
+new goodies to their toolbox. You don't need to know Python to read this
+document; there will be some brief forays into using Python's interactive mode
+to explore your installation, but that's it. If you're looking for information
+on how to distribute your own Python modules so that others may use them, see
+the :ref:`distutils-index` manual.
+
+
+.. _inst-trivial-install:
+
+Best case: trivial installation
+-------------------------------
+
+In the best case, someone will have prepared a special version of the module
+distribution you want to install that is targeted specifically at your platform
+and is installed just like any other software on your platform. For example,
+the module developer might make an executable installer available for Windows
+users, an RPM package for users of RPM-based Linux systems (Red Hat, SuSE,
+Mandrake, and many others), a Debian package for users of Debian-based Linux
+systems, and so forth.
+
+In that case, you would download the installer appropriate to your platform and
+do the obvious thing with it: run it if it's an executable installer, ``rpm
+--install`` it if it's an RPM, etc. You don't need to run Python or a setup
+script, you don't need to compile anything---you might not even need to read any
+instructions (although it's always a good idea to do so anyway).
+
+Of course, things will not always be that easy. You might be interested in a
+module distribution that doesn't have an easy-to-use installer for your
+platform. In that case, you'll have to start with the source distribution
+released by the module's author/maintainer. Installing from a source
+distribution is not too hard, as long as the modules are packaged in the
+standard way. The bulk of this document is about building and installing
+modules from standard source distributions.
+
+
+.. _inst-new-standard:
+
+The new standard: Distutils
+---------------------------
+
+If you download a module source distribution, you can tell pretty quickly if it
+was packaged and distributed in the standard way, i.e. using the Distutils.
+First, the distribution's name and version number will be featured prominently
+in the name of the downloaded archive, e.g. :file:`foo-1.0.tar.gz` or
+:file:`widget-0.9.7.zip`. Next, the archive will unpack into a similarly-named
+directory: :file:`foo-1.0` or :file:`widget-0.9.7`. Additionally, the
+distribution will contain a setup script :file:`setup.py`, and a file named
+:file:`README.txt` or possibly just :file:`README`, which should explain that
+building and installing the module distribution is a simple matter of running
+one command from a terminal::
+
+ python setup.py install
+
+For Windows, this command should be run from a command prompt window
+(:menuselection:`Start --> Accessories`)::
+
+ setup.py install
+
+If all these things are true, then you already know how to build and install the
+modules you've just downloaded: Run the command above. Unless you need to
+install things in a non-standard way or customize the build process, you don't
+really need this manual. Or rather, the above command is everything you need to
+get out of this manual.
+
+
+.. _inst-standard-install:
+
+Standard Build and Install
+==========================
+
+As described in section :ref:`inst-new-standard`, building and installing a module
+distribution using the Distutils is usually one simple command to run from a
+terminal::
+
+ python setup.py install
+
+
+.. _inst-platform-variations:
+
+Platform variations
+-------------------
+
+You should always run the setup command from the distribution root directory,
+i.e. the top-level subdirectory that the module source distribution unpacks
+into. For example, if you've just downloaded a module source distribution
+:file:`foo-1.0.tar.gz` onto a Unix system, the normal thing to do is::
+
+ gunzip -c foo-1.0.tar.gz | tar xf - # unpacks into directory foo-1.0
+ cd foo-1.0
+ python setup.py install
+
+On Windows, you'd probably download :file:`foo-1.0.zip`. If you downloaded the
+archive file to :file:`C:\\Temp`, then it would unpack into
+:file:`C:\\Temp\\foo-1.0`; you can use either a archive manipulator with a
+graphical user interface (such as WinZip) or a command-line tool (such as
+:program:`unzip` or :program:`pkunzip`) to unpack the archive. Then, open a
+command prompt window and run::
+
+ cd c:\Temp\foo-1.0
+ python setup.py install
+
+
+.. _inst-splitting-up:
+
+Splitting the job up
+--------------------
+
+Running ``setup.py install`` builds and installs all modules in one run. If you
+prefer to work incrementally---especially useful if you want to customize the
+build process, or if things are going wrong---you can use the setup script to do
+one thing at a time. This is particularly helpful when the build and install
+will be done by different users---for example, you might want to build a module
+distribution and hand it off to a system administrator for installation (or do
+it yourself, with super-user privileges).
+
+For example, you can build everything in one step, and then install everything
+in a second step, by invoking the setup script twice::
+
+ python setup.py build
+ python setup.py install
+
+If you do this, you will notice that running the :command:`install` command
+first runs the :command:`build` command, which---in this case---quickly notices
+that it has nothing to do, since everything in the :file:`build` directory is
+up-to-date.
+
+You may not need this ability to break things down often if all you do is
+install modules downloaded off the 'net, but it's very handy for more advanced
+tasks. If you get into distributing your own Python modules and extensions,
+you'll run lots of individual Distutils commands on their own.
+
+
+.. _inst-how-build-works:
+
+How building works
+------------------
+
+As implied above, the :command:`build` command is responsible for putting the
+files to install into a *build directory*. By default, this is :file:`build`
+under the distribution root; if you're excessively concerned with speed, or want
+to keep the source tree pristine, you can change the build directory with the
+:option:`--build-base` option. For example::
+
+ python setup.py build --build-base=/tmp/pybuild/foo-1.0
+
+(Or you could do this permanently with a directive in your system or personal
+Distutils configuration file; see section :ref:`inst-config-files`.) Normally, this
+isn't necessary.
+
+The default layout for the build tree is as follows::
+
+ --- build/ --- lib/
+ or
+ --- build/ --- lib.<plat>/
+ temp.<plat>/
+
+where ``<plat>`` expands to a brief description of the current OS/hardware
+platform and Python version. The first form, with just a :file:`lib` directory,
+is used for "pure module distributions"---that is, module distributions that
+include only pure Python modules. If a module distribution contains any
+extensions (modules written in C/C++), then the second form, with two ``<plat>``
+directories, is used. In that case, the :file:`temp.{plat}` directory holds
+temporary files generated by the compile/link process that don't actually get
+installed. In either case, the :file:`lib` (or :file:`lib.{plat}`) directory
+contains all Python modules (pure Python and extensions) that will be installed.
+
+In the future, more directories will be added to handle Python scripts,
+documentation, binary executables, and whatever else is needed to handle the job
+of installing Python modules and applications.
+
+
+.. _inst-how-install-works:
+
+How installation works
+----------------------
+
+After the :command:`build` command runs (whether you run it explicitly, or the
+:command:`install` command does it for you), the work of the :command:`install`
+command is relatively simple: all it has to do is copy everything under
+:file:`build/lib` (or :file:`build/lib.{plat}`) to your chosen installation
+directory.
+
+If you don't choose an installation directory---i.e., if you just run ``setup.py
+install``\ ---then the :command:`install` command installs to the standard
+location for third-party Python modules. This location varies by platform and
+by how you built/installed Python itself. On Unix (and Mac OS X, which is also
+Unix-based), it also depends on whether the module distribution being installed
+is pure Python or contains extensions ("non-pure"):
+
++-----------------+-----------------------------------------------------+--------------------------------------------------+-------+
+| Platform | Standard installation location | Default value | Notes |
++=================+=====================================================+==================================================+=======+
+| Unix (pure) | :file:`{prefix}/lib/python{X.Y}/site-packages` | :file:`/usr/local/lib/python{X.Y}/site-packages` | \(1) |
++-----------------+-----------------------------------------------------+--------------------------------------------------+-------+
+| Unix (non-pure) | :file:`{exec-prefix}/lib/python{X.Y}/site-packages` | :file:`/usr/local/lib/python{X.Y}/site-packages` | \(1) |
++-----------------+-----------------------------------------------------+--------------------------------------------------+-------+
+| Windows | :file:`{prefix}\\Lib\\site-packages` | :file:`C:\\Python{XY}\\Lib\\site-packages` | \(2) |
++-----------------+-----------------------------------------------------+--------------------------------------------------+-------+
+
+Notes:
+
+(1)
+ Most Linux distributions include Python as a standard part of the system, so
+ :file:`{prefix}` and :file:`{exec-prefix}` are usually both :file:`/usr` on
+ Linux. If you build Python yourself on Linux (or any Unix-like system), the
+ default :file:`{prefix}` and :file:`{exec-prefix}` are :file:`/usr/local`.
+
+(2)
+ The default installation directory on Windows was :file:`C:\\Program
+ Files\\Python` under Python 1.6a1, 1.5.2, and earlier.
+
+:file:`{prefix}` and :file:`{exec-prefix}` stand for the directories that Python
+is installed to, and where it finds its libraries at run-time. They are always
+the same under Windows, and very often the same under Unix and Mac OS X. You
+can find out what your Python installation uses for :file:`{prefix}` and
+:file:`{exec-prefix}` by running Python in interactive mode and typing a few
+simple commands. Under Unix, just type ``python`` at the shell prompt. Under
+Windows, choose :menuselection:`Start --> Programs --> Python X.Y -->
+Python (command line)`. Once the interpreter is started, you type Python code
+at the prompt. For example, on my Linux system, I type the three Python
+statements shown below, and get the output as shown, to find out my
+:file:`{prefix}` and :file:`{exec-prefix}`::
+
+ Python 2.4 (#26, Aug 7 2004, 17:19:02)
+ Type "help", "copyright", "credits" or "license" for more information.
+ >>> import sys
+ >>> sys.prefix
+ '/usr'
+ >>> sys.exec_prefix
+ '/usr'
+
+A few other placeholders are used in this document: :file:`{X.Y}` stands for the
+version of Python, for example ``3.2``; :file:`{abiflags}` will be replaced by
+the value of :data:`sys.abiflags` or the empty string for platforms which don't
+define ABI flags; :file:`{distname}` will be replaced by the name of the module
+distribution being installed. Dots and capitalization are important in the
+paths; for example, a value that uses ``python3.2`` on UNIX will typically use
+``Python32`` on Windows.
+
+If you don't want to install modules to the standard location, or if you don't
+have permission to write there, then you need to read about alternate
+installations in section :ref:`inst-alt-install`. If you want to customize your
+installation directories more heavily, see section :ref:`inst-custom-install` on
+custom installations.
+
+
+.. _inst-alt-install:
+
+Alternate Installation
+======================
+
+Often, it is necessary or desirable to install modules to a location other than
+the standard location for third-party Python modules. For example, on a Unix
+system you might not have permission to write to the standard third-party module
+directory. Or you might wish to try out a module before making it a standard
+part of your local Python installation. This is especially true when upgrading
+a distribution already present: you want to make sure your existing base of
+scripts still works with the new version before actually upgrading.
+
+The Distutils :command:`install` command is designed to make installing module
+distributions to an alternate location simple and painless. The basic idea is
+that you supply a base directory for the installation, and the
+:command:`install` command picks a set of directories (called an *installation
+scheme*) under this base directory in which to install files. The details
+differ across platforms, so read whichever of the following sections applies to
+you.
+
+Note that the various alternate installation schemes are mutually exclusive: you
+can pass ``--user``, or ``--home``, or ``--prefix`` and ``--exec-prefix``, or
+``--install-base`` and ``--install-platbase``, but you can't mix from these
+groups.
+
+
+.. _inst-alt-install-user:
+
+Alternate installation: the user scheme
+---------------------------------------
+
+This scheme is designed to be the most convenient solution for users that don't
+have write permission to the global site-packages directory or don't want to
+install into it. It is enabled with a simple option::
+
+ python setup.py install --user
+
+Files will be installed into subdirectories of :data:`site.USER_BASE` (written
+as :file:`{userbase}` hereafter). This scheme installs pure Python modules and
+extension modules in the same location (also known as :data:`site.USER_SITE`).
+Here are the values for UNIX, including Mac OS X:
+
+=============== ===========================================================
+Type of file Installation directory
+=============== ===========================================================
+modules :file:`{userbase}/lib/python{X.Y}/site-packages`
+scripts :file:`{userbase}/bin`
+data :file:`{userbase}`
+C headers :file:`{userbase}/include/python{X.Y}{abiflags}/{distname}`
+=============== ===========================================================
+
+And here are the values used on Windows:
+
+=============== ===========================================================
+Type of file Installation directory
+=============== ===========================================================
+modules :file:`{userbase}\\Python{XY}\\site-packages`
+scripts :file:`{userbase}\\Scripts`
+data :file:`{userbase}`
+C headers :file:`{userbase}\\Python{XY}\\Include\\{distname}`
+=============== ===========================================================
+
+The advantage of using this scheme compared to the other ones described below is
+that the user site-packages directory is under normal conditions always included
+in :data:`sys.path` (see :mod:`site` for more information), which means that
+there is no additional step to perform after running the :file:`setup.py` script
+to finalize the installation.
+
+The :command:`build_ext` command also has a ``--user`` option to add
+:file:`{userbase}/include` to the compiler search path for header files and
+:file:`{userbase}/lib` to the compiler search path for libraries as well as to
+the runtime search path for shared C libraries (rpath).
+
+
+.. _inst-alt-install-home:
+
+Alternate installation: the home scheme
+---------------------------------------
+
+The idea behind the "home scheme" is that you build and maintain a personal
+stash of Python modules. This scheme's name is derived from the idea of a
+"home" directory on Unix, since it's not unusual for a Unix user to make their
+home directory have a layout similar to :file:`/usr/` or :file:`/usr/local/`.
+This scheme can be used by anyone, regardless of the operating system they
+are installing for.
+
+Installing a new module distribution is as simple as ::
+
+ python setup.py install --home=<dir>
+
+where you can supply any directory you like for the :option:`--home` option. On
+Unix, lazy typists can just type a tilde (``~``); the :command:`install` command
+will expand this to your home directory::
+
+ python setup.py install --home=~
+
+To make Python find the distributions installed with this scheme, you may have
+to :ref:`modify Python's search path <inst-search-path>` or edit
+:mod:`sitecustomize` (see :mod:`site`) to call :func:`site.addsitedir` or edit
+:data:`sys.path`.
+
+The :option:`--home` option defines the installation base directory. Files are
+installed to the following directories under the installation base as follows:
+
+=============== ===========================================================
+Type of file Installation directory
+=============== ===========================================================
+modules :file:`{home}/lib/python`
+scripts :file:`{home}/bin`
+data :file:`{home}`
+C headers :file:`{home}/include/python/{distname}`
+=============== ===========================================================
+
+(Mentally replace slashes with backslashes if you're on Windows.)
+
+
+.. _inst-alt-install-prefix-unix:
+
+Alternate installation: Unix (the prefix scheme)
+------------------------------------------------
+
+The "prefix scheme" is useful when you wish to use one Python installation to
+perform the build/install (i.e., to run the setup script), but install modules
+into the third-party module directory of a different Python installation (or
+something that looks like a different Python installation). If this sounds a
+trifle unusual, it is---that's why the user and home schemes come before. However,
+there are at least two known cases where the prefix scheme will be useful.
+
+First, consider that many Linux distributions put Python in :file:`/usr`, rather
+than the more traditional :file:`/usr/local`. This is entirely appropriate,
+since in those cases Python is part of "the system" rather than a local add-on.
+However, if you are installing Python modules from source, you probably want
+them to go in :file:`/usr/local/lib/python2.{X}` rather than
+:file:`/usr/lib/python2.{X}`. This can be done with ::
+
+ /usr/bin/python setup.py install --prefix=/usr/local
+
+Another possibility is a network filesystem where the name used to write to a
+remote directory is different from the name used to read it: for example, the
+Python interpreter accessed as :file:`/usr/local/bin/python` might search for
+modules in :file:`/usr/local/lib/python2.{X}`, but those modules would have to
+be installed to, say, :file:`/mnt/{@server}/export/lib/python2.{X}`. This could
+be done with ::
+
+ /usr/local/bin/python setup.py install --prefix=/mnt/@server/export
+
+In either case, the :option:`--prefix` option defines the installation base, and
+the :option:`--exec-prefix` option defines the platform-specific installation
+base, which is used for platform-specific files. (Currently, this just means
+non-pure module distributions, but could be expanded to C libraries, binary
+executables, etc.) If :option:`--exec-prefix` is not supplied, it defaults to
+:option:`--prefix`. Files are installed as follows:
+
+================= ==========================================================
+Type of file Installation directory
+================= ==========================================================
+Python modules :file:`{prefix}/lib/python{X.Y}/site-packages`
+extension modules :file:`{exec-prefix}/lib/python{X.Y}/site-packages`
+scripts :file:`{prefix}/bin`
+data :file:`{prefix}`
+C headers :file:`{prefix}/include/python{X.Y}{abiflags}/{distname}`
+================= ==========================================================
+
+There is no requirement that :option:`--prefix` or :option:`--exec-prefix`
+actually point to an alternate Python installation; if the directories listed
+above do not already exist, they are created at installation time.
+
+Incidentally, the real reason the prefix scheme is important is simply that a
+standard Unix installation uses the prefix scheme, but with :option:`--prefix`
+and :option:`--exec-prefix` supplied by Python itself as ``sys.prefix`` and
+``sys.exec_prefix``. Thus, you might think you'll never use the prefix scheme,
+but every time you run ``python setup.py install`` without any other options,
+you're using it.
+
+Note that installing extensions to an alternate Python installation has no
+effect on how those extensions are built: in particular, the Python header files
+(:file:`Python.h` and friends) installed with the Python interpreter used to run
+the setup script will be used in compiling extensions. It is your
+responsibility to ensure that the interpreter used to run extensions installed
+in this way is compatible with the interpreter used to build them. The best way
+to do this is to ensure that the two interpreters are the same version of Python
+(possibly different builds, or possibly copies of the same build). (Of course,
+if your :option:`--prefix` and :option:`--exec-prefix` don't even point to an
+alternate Python installation, this is immaterial.)
+
+
+.. _inst-alt-install-prefix-windows:
+
+Alternate installation: Windows (the prefix scheme)
+---------------------------------------------------
+
+Windows has no concept of a user's home directory, and since the standard Python
+installation under Windows is simpler than under Unix, the :option:`--prefix`
+option has traditionally been used to install additional packages in separate
+locations on Windows. ::
+
+ python setup.py install --prefix="\Temp\Python"
+
+to install modules to the :file:`\\Temp\\Python` directory on the current drive.
+
+The installation base is defined by the :option:`--prefix` option; the
+:option:`--exec-prefix` option is not supported under Windows, which means that
+pure Python modules and extension modules are installed into the same location.
+Files are installed as follows:
+
+=============== ==========================================================
+Type of file Installation directory
+=============== ==========================================================
+modules :file:`{prefix}\\Lib\\site-packages`
+scripts :file:`{prefix}\\Scripts`
+data :file:`{prefix}`
+C headers :file:`{prefix}\\Include\\{distname}`
+=============== ==========================================================
+
+
+.. _inst-custom-install:
+
+Custom Installation
+===================
+
+Sometimes, the alternate installation schemes described in section
+:ref:`inst-alt-install` just don't do what you want. You might want to tweak just
+one or two directories while keeping everything under the same base directory,
+or you might want to completely redefine the installation scheme. In either
+case, you're creating a *custom installation scheme*.
+
+To create a custom installation scheme, you start with one of the alternate
+schemes and override some of the installation directories used for the various
+types of files, using these options:
+
+====================== =======================
+Type of file Override option
+====================== =======================
+Python modules ``--install-purelib``
+extension modules ``--install-platlib``
+all modules ``--install-lib``
+scripts ``--install-scripts``
+data ``--install-data``
+C headers ``--install-headers``
+====================== =======================
+
+These override options can be relative, absolute,
+or explicitly defined in terms of one of the installation base directories.
+(There are two installation base directories, and they are normally the same---
+they only differ when you use the Unix "prefix scheme" and supply different
+``--prefix`` and ``--exec-prefix`` options; using ``--install-lib`` will
+override values computed or given for ``--install-purelib`` and
+``--install-platlib``, and is recommended for schemes that don't make a
+difference between Python and extension modules.)
+
+For example, say you're installing a module distribution to your home directory
+under Unix---but you want scripts to go in :file:`~/scripts` rather than
+:file:`~/bin`. As you might expect, you can override this directory with the
+:option:`--install-scripts` option; in this case, it makes most sense to supply
+a relative path, which will be interpreted relative to the installation base
+directory (your home directory, in this case)::
+
+ python setup.py install --home=~ --install-scripts=scripts
+
+Another Unix example: suppose your Python installation was built and installed
+with a prefix of :file:`/usr/local/python`, so under a standard installation
+scripts will wind up in :file:`/usr/local/python/bin`. If you want them in
+:file:`/usr/local/bin` instead, you would supply this absolute directory for the
+:option:`--install-scripts` option::
+
+ python setup.py install --install-scripts=/usr/local/bin
+
+(This performs an installation using the "prefix scheme," where the prefix is
+whatever your Python interpreter was installed with--- :file:`/usr/local/python`
+in this case.)
+
+If you maintain Python on Windows, you might want third-party modules to live in
+a subdirectory of :file:`{prefix}`, rather than right in :file:`{prefix}`
+itself. This is almost as easy as customizing the script installation directory
+---you just have to remember that there are two types of modules to worry about,
+Python and extension modules, which can conveniently be both controlled by one
+option::
+
+ python setup.py install --install-lib=Site
+
+The specified installation directory is relative to :file:`{prefix}`. Of
+course, you also have to ensure that this directory is in Python's module
+search path, such as by putting a :file:`.pth` file in a site directory (see
+:mod:`site`). See section :ref:`inst-search-path` to find out how to modify
+Python's search path.
+
+If you want to define an entire installation scheme, you just have to supply all
+of the installation directory options. The recommended way to do this is to
+supply relative paths; for example, if you want to maintain all Python
+module-related files under :file:`python` in your home directory, and you want a
+separate directory for each platform that you use your home directory from, you
+might define the following installation scheme::
+
+ python setup.py install --home=~ \
+ --install-purelib=python/lib \
+ --install-platlib=python/lib.$PLAT \
+ --install-scripts=python/scripts
+ --install-data=python/data
+
+or, equivalently, ::
+
+ python setup.py install --home=~/python \
+ --install-purelib=lib \
+ --install-platlib='lib.$PLAT' \
+ --install-scripts=scripts
+ --install-data=data
+
+``$PLAT`` is not (necessarily) an environment variable---it will be expanded by
+the Distutils as it parses your command line options, just as it does when
+parsing your configuration file(s).
+
+Obviously, specifying the entire installation scheme every time you install a
+new module distribution would be very tedious. Thus, you can put these options
+into your Distutils config file (see section :ref:`inst-config-files`)::
+
+ [install]
+ install-base=$HOME
+ install-purelib=python/lib
+ install-platlib=python/lib.$PLAT
+ install-scripts=python/scripts
+ install-data=python/data
+
+or, equivalently, ::
+
+ [install]
+ install-base=$HOME/python
+ install-purelib=lib
+ install-platlib=lib.$PLAT
+ install-scripts=scripts
+ install-data=data
+
+Note that these two are *not* equivalent if you supply a different installation
+base directory when you run the setup script. For example, ::
+
+ python setup.py install --install-base=/tmp
+
+would install pure modules to :file:`/tmp/python/lib` in the first case, and
+to :file:`/tmp/lib` in the second case. (For the second case, you probably
+want to supply an installation base of :file:`/tmp/python`.)
+
+You probably noticed the use of ``$HOME`` and ``$PLAT`` in the sample
+configuration file input. These are Distutils configuration variables, which
+bear a strong resemblance to environment variables. In fact, you can use
+environment variables in config files on platforms that have such a notion but
+the Distutils additionally define a few extra variables that may not be in your
+environment, such as ``$PLAT``. (And of course, on systems that don't have
+environment variables, such as Mac OS 9, the configuration variables supplied by
+the Distutils are the only ones you can use.) See section :ref:`inst-config-files`
+for details.
+
+.. XXX need some Windows examples---when would custom installation schemes be
+ needed on those platforms?
+
+
+.. XXX Move this to Doc/using
+
+.. _inst-search-path:
+
+Modifying Python's Search Path
+------------------------------
+
+When the Python interpreter executes an :keyword:`import` statement, it searches
+for both Python code and extension modules along a search path. A default value
+for the path is configured into the Python binary when the interpreter is built.
+You can determine the path by importing the :mod:`sys` module and printing the
+value of ``sys.path``. ::
+
+ $ python
+ Python 2.2 (#11, Oct 3 2002, 13:31:27)
+ [GCC 2.96 20000731 (Red Hat Linux 7.3 2.96-112)] on linux2
+ Type "help", "copyright", "credits" or "license" for more information.
+ >>> import sys
+ >>> sys.path
+ ['', '/usr/local/lib/python2.3', '/usr/local/lib/python2.3/plat-linux2',
+ '/usr/local/lib/python2.3/lib-tk', '/usr/local/lib/python2.3/lib-dynload',
+ '/usr/local/lib/python2.3/site-packages']
+ >>>
+
+The null string in ``sys.path`` represents the current working directory.
+
+The expected convention for locally installed packages is to put them in the
+:file:`{...}/site-packages/` directory, but you may want to install Python
+modules into some arbitrary directory. For example, your site may have a
+convention of keeping all software related to the web server under :file:`/www`.
+Add-on Python modules might then belong in :file:`/www/python`, and in order to
+import them, this directory must be added to ``sys.path``. There are several
+different ways to add the directory.
+
+The most convenient way is to add a path configuration file to a directory
+that's already on Python's path, usually to the :file:`.../site-packages/`
+directory. Path configuration files have an extension of :file:`.pth`, and each
+line must contain a single path that will be appended to ``sys.path``. (Because
+the new paths are appended to ``sys.path``, modules in the added directories
+will not override standard modules. This means you can't use this mechanism for
+installing fixed versions of standard modules.)
+
+Paths can be absolute or relative, in which case they're relative to the
+directory containing the :file:`.pth` file. See the documentation of
+the :mod:`site` module for more information.
+
+A slightly less convenient way is to edit the :file:`site.py` file in Python's
+standard library, and modify ``sys.path``. :file:`site.py` is automatically
+imported when the Python interpreter is executed, unless the :option:`-S` switch
+is supplied to suppress this behaviour. So you could simply edit
+:file:`site.py` and add two lines to it::
+
+ import sys
+ sys.path.append('/www/python/')
+
+However, if you reinstall the same major version of Python (perhaps when
+upgrading from 2.2 to 2.2.2, for example) :file:`site.py` will be overwritten by
+the stock version. You'd have to remember that it was modified and save a copy
+before doing the installation.
+
+There are two environment variables that can modify ``sys.path``.
+:envvar:`PYTHONHOME` sets an alternate value for the prefix of the Python
+installation. For example, if :envvar:`PYTHONHOME` is set to ``/www/python``,
+the search path will be set to ``['', '/www/python/lib/pythonX.Y/',
+'/www/python/lib/pythonX.Y/plat-linux2', ...]``.
+
+The :envvar:`PYTHONPATH` variable can be set to a list of paths that will be
+added to the beginning of ``sys.path``. For example, if :envvar:`PYTHONPATH` is
+set to ``/www/python:/opt/py``, the search path will begin with
+``['/www/python', '/opt/py']``. (Note that directories must exist in order to
+be added to ``sys.path``; the :mod:`site` module removes paths that don't
+exist.)
+
+Finally, ``sys.path`` is just a regular Python list, so any Python application
+can modify it by adding or removing entries.
+
+
+.. _inst-config-files:
+
+Distutils Configuration Files
+=============================
+
+As mentioned above, you can use Distutils configuration files to record personal
+or site preferences for any Distutils options. That is, any option to any
+command can be stored in one of two or three (depending on your platform)
+configuration files, which will be consulted before the command-line is parsed.
+This means that configuration files will override default values, and the
+command-line will in turn override configuration files. Furthermore, if
+multiple configuration files apply, values from "earlier" files are overridden
+by "later" files.
+
+
+.. _inst-config-filenames:
+
+Location and names of config files
+----------------------------------
+
+The names and locations of the configuration files vary slightly across
+platforms. On Unix and Mac OS X, the three configuration files (in the order
+they are processed) are:
+
++--------------+----------------------------------------------------------+-------+
+| Type of file | Location and filename | Notes |
++==============+==========================================================+=======+
+| system | :file:`{prefix}/lib/python{ver}/distutils/distutils.cfg` | \(1) |
++--------------+----------------------------------------------------------+-------+
+| personal | :file:`$HOME/.pydistutils.cfg` | \(2) |
++--------------+----------------------------------------------------------+-------+
+| local | :file:`setup.cfg` | \(3) |
++--------------+----------------------------------------------------------+-------+
+
+And on Windows, the configuration files are:
+
++--------------+-------------------------------------------------+-------+
+| Type of file | Location and filename | Notes |
++==============+=================================================+=======+
+| system | :file:`{prefix}\\Lib\\distutils\\distutils.cfg` | \(4) |
++--------------+-------------------------------------------------+-------+
+| personal | :file:`%HOME%\\pydistutils.cfg` | \(5) |
++--------------+-------------------------------------------------+-------+
+| local | :file:`setup.cfg` | \(3) |
++--------------+-------------------------------------------------+-------+
+
+On all platforms, the "personal" file can be temporarily disabled by
+passing the `--no-user-cfg` option.
+
+Notes:
+
+(1)
+ Strictly speaking, the system-wide configuration file lives in the directory
+ where the Distutils are installed; under Python 1.6 and later on Unix, this is
+ as shown. For Python 1.5.2, the Distutils will normally be installed to
+ :file:`{prefix}/lib/python1.5/site-packages/distutils`, so the system
+ configuration file should be put there under Python 1.5.2.
+
+(2)
+ On Unix, if the :envvar:`HOME` environment variable is not defined, the user's
+ home directory will be determined with the :func:`getpwuid` function from the
+ standard :mod:`pwd` module. This is done by the :func:`os.path.expanduser`
+ function used by Distutils.
+
+(3)
+ I.e., in the current directory (usually the location of the setup script).
+
+(4)
+ (See also note (1).) Under Python 1.6 and later, Python's default "installation
+ prefix" is :file:`C:\\Python`, so the system configuration file is normally
+ :file:`C:\\Python\\Lib\\distutils\\distutils.cfg`. Under Python 1.5.2, the
+ default prefix was :file:`C:\\Program Files\\Python`, and the Distutils were not
+ part of the standard library---so the system configuration file would be
+ :file:`C:\\Program Files\\Python\\distutils\\distutils.cfg` in a standard Python
+ 1.5.2 installation under Windows.
+
+(5)
+ On Windows, if the :envvar:`HOME` environment variable is not defined,
+ :envvar:`USERPROFILE` then :envvar:`HOMEDRIVE` and :envvar:`HOMEPATH` will
+ be tried. This is done by the :func:`os.path.expanduser` function used
+ by Distutils.
+
+
+.. _inst-config-syntax:
+
+Syntax of config files
+----------------------
+
+The Distutils configuration files all have the same syntax. The config files
+are grouped into sections. There is one section for each Distutils command,
+plus a ``global`` section for global options that affect every command. Each
+section consists of one option per line, specified as ``option=value``.
+
+For example, the following is a complete config file that just forces all
+commands to run quietly by default::
+
+ [global]
+ verbose=0
+
+If this is installed as the system config file, it will affect all processing of
+any Python module distribution by any user on the current system. If it is
+installed as your personal config file (on systems that support them), it will
+affect only module distributions processed by you. And if it is used as the
+:file:`setup.cfg` for a particular module distribution, it affects only that
+distribution.
+
+You could override the default "build base" directory and make the
+:command:`build\*` commands always forcibly rebuild all files with the
+following::
+
+ [build]
+ build-base=blib
+ force=1
+
+which corresponds to the command-line arguments ::
+
+ python setup.py build --build-base=blib --force
+
+except that including the :command:`build` command on the command-line means
+that command will be run. Including a particular command in config files has no
+such implication; it only means that if the command is run, the options in the
+config file will apply. (Or if other commands that derive values from it are
+run, they will use the values in the config file.)
+
+You can find out the complete list of options for any command using the
+:option:`--help` option, e.g.::
+
+ python setup.py build --help
+
+and you can find out the complete list of global options by using
+:option:`--help` without a command::
+
+ python setup.py --help
+
+See also the "Reference" section of the "Distributing Python Modules" manual.
+
+
+.. _inst-building-ext:
+
+Building Extensions: Tips and Tricks
+====================================
+
+Whenever possible, the Distutils try to use the configuration information made
+available by the Python interpreter used to run the :file:`setup.py` script.
+For example, the same compiler and linker flags used to compile Python will also
+be used for compiling extensions. Usually this will work well, but in
+complicated situations this might be inappropriate. This section discusses how
+to override the usual Distutils behaviour.
+
+
+.. _inst-tweak-flags:
+
+Tweaking compiler/linker flags
+------------------------------
+
+Compiling a Python extension written in C or C++ will sometimes require
+specifying custom flags for the compiler and linker in order to use a particular
+library or produce a special kind of object code. This is especially true if the
+extension hasn't been tested on your platform, or if you're trying to
+cross-compile Python.
+
+In the most general case, the extension author might have foreseen that
+compiling the extensions would be complicated, and provided a :file:`Setup` file
+for you to edit. This will likely only be done if the module distribution
+contains many separate extension modules, or if they often require elaborate
+sets of compiler flags in order to work.
+
+A :file:`Setup` file, if present, is parsed in order to get a list of extensions
+to build. Each line in a :file:`Setup` describes a single module. Lines have
+the following structure::
+
+ module ... [sourcefile ...] [cpparg ...] [library ...]
+
+
+Let's examine each of the fields in turn.
+
+* *module* is the name of the extension module to be built, and should be a
+ valid Python identifier. You can't just change this in order to rename a module
+ (edits to the source code would also be needed), so this should be left alone.
+
+* *sourcefile* is anything that's likely to be a source code file, at least
+ judging by the filename. Filenames ending in :file:`.c` are assumed to be
+ written in C, filenames ending in :file:`.C`, :file:`.cc`, and :file:`.c++` are
+ assumed to be C++, and filenames ending in :file:`.m` or :file:`.mm` are assumed
+ to be in Objective C.
+
+* *cpparg* is an argument for the C preprocessor, and is anything starting with
+ :option:`-I`, :option:`-D`, :option:`-U` or :option:`-C`.
+
+* *library* is anything ending in :file:`.a` or beginning with :option:`-l` or
+ :option:`-L`.
+
+If a particular platform requires a special library on your platform, you can
+add it by editing the :file:`Setup` file and running ``python setup.py build``.
+For example, if the module defined by the line ::
+
+ foo foomodule.c
+
+must be linked with the math library :file:`libm.a` on your platform, simply add
+:option:`-lm` to the line::
+
+ foo foomodule.c -lm
+
+Arbitrary switches intended for the compiler or the linker can be supplied with
+the :option:`-Xcompiler` *arg* and :option:`-Xlinker` *arg* options::
+
+ foo foomodule.c -Xcompiler -o32 -Xlinker -shared -lm
+
+The next option after :option:`-Xcompiler` and :option:`-Xlinker` will be
+appended to the proper command line, so in the above example the compiler will
+be passed the :option:`-o32` option, and the linker will be passed
+:option:`-shared`. If a compiler option requires an argument, you'll have to
+supply multiple :option:`-Xcompiler` options; for example, to pass ``-x c++``
+the :file:`Setup` file would have to contain ``-Xcompiler -x -Xcompiler c++``.
+
+Compiler flags can also be supplied through setting the :envvar:`CFLAGS`
+environment variable. If set, the contents of :envvar:`CFLAGS` will be added to
+the compiler flags specified in the :file:`Setup` file.
+
+
+.. _inst-non-ms-compilers:
+
+Using non-Microsoft compilers on Windows
+----------------------------------------
+
+.. sectionauthor:: Rene Liebscher <R.Liebscher@gmx.de>
+
+
+
+Borland/CodeGear C++
+^^^^^^^^^^^^^^^^^^^^
+
+This subsection describes the necessary steps to use Distutils with the Borland
+C++ compiler version 5.5. First you have to know that Borland's object file
+format (OMF) is different from the format used by the Python version you can
+download from the Python or ActiveState Web site. (Python is built with
+Microsoft Visual C++, which uses COFF as the object file format.) For this
+reason you have to convert Python's library :file:`python25.lib` into the
+Borland format. You can do this as follows:
+
+.. Should we mention that users have to create cfg-files for the compiler?
+.. see also http://community.borland.com/article/0,1410,21205,00.html
+
+::
+
+ coff2omf python25.lib python25_bcpp.lib
+
+The :file:`coff2omf` program comes with the Borland compiler. The file
+:file:`python25.lib` is in the :file:`Libs` directory of your Python
+installation. If your extension uses other libraries (zlib, ...) you have to
+convert them too.
+
+The converted files have to reside in the same directories as the normal
+libraries.
+
+How does Distutils manage to use these libraries with their changed names? If
+the extension needs a library (eg. :file:`foo`) Distutils checks first if it
+finds a library with suffix :file:`_bcpp` (eg. :file:`foo_bcpp.lib`) and then
+uses this library. In the case it doesn't find such a special library it uses
+the default name (:file:`foo.lib`.) [#]_
+
+To let Distutils compile your extension with Borland C++ you now have to type::
+
+ python setup.py build --compiler=bcpp
+
+If you want to use the Borland C++ compiler as the default, you could specify
+this in your personal or system-wide configuration file for Distutils (see
+section :ref:`inst-config-files`.)
+
+
+.. seealso::
+
+ `C++Builder Compiler <http://www.codegear.com/downloads/free/cppbuilder>`_
+ Information about the free C++ compiler from Borland, including links to the
+ download pages.
+
+ `Creating Python Extensions Using Borland's Free Compiler <http://www.cyberus.ca/~g_will/pyExtenDL.shtml>`_
+ Document describing how to use Borland's free command-line C++ compiler to build
+ Python.
+
+
+GNU C / Cygwin / MinGW
+^^^^^^^^^^^^^^^^^^^^^^
+
+This section describes the necessary steps to use Distutils with the GNU C/C++
+compilers in their Cygwin and MinGW distributions. [#]_ For a Python interpreter
+that was built with Cygwin, everything should work without any of these
+following steps.
+
+Not all extensions can be built with MinGW or Cygwin, but many can. Extensions
+most likely to not work are those that use C++ or depend on Microsoft Visual C
+extensions.
+
+To let Distutils compile your extension with Cygwin you have to type::
+
+ python setup.py build --compiler=cygwin
+
+and for Cygwin in no-cygwin mode [#]_ or for MinGW type::
+
+ python setup.py build --compiler=mingw32
+
+If you want to use any of these options/compilers as default, you should
+consider writing it in your personal or system-wide configuration file for
+Distutils (see section :ref:`inst-config-files`.)
+
+Older Versions of Python and MinGW
+""""""""""""""""""""""""""""""""""
+The following instructions only apply if you're using a version of Python
+inferior to 2.4.1 with a MinGW inferior to 3.0.0 (with
+binutils-2.13.90-20030111-1).
+
+These compilers require some special libraries. This task is more complex than
+for Borland's C++, because there is no program to convert the library. First
+you have to create a list of symbols which the Python DLL exports. (You can find
+a good program for this task at
+http://www.emmestech.com/software/pexports-0.43/download_pexports.html).
+
+.. I don't understand what the next line means. --amk
+.. (inclusive the references on data structures.)
+
+::
+
+ pexports python25.dll >python25.def
+
+The location of an installed :file:`python25.dll` will depend on the
+installation options and the version and language of Windows. In a "just for
+me" installation, it will appear in the root of the installation directory. In
+a shared installation, it will be located in the system directory.
+
+Then you can create from these information an import library for gcc. ::
+
+ /cygwin/bin/dlltool --dllname python25.dll --def python25.def --output-lib libpython25.a
+
+The resulting library has to be placed in the same directory as
+:file:`python25.lib`. (Should be the :file:`libs` directory under your Python
+installation directory.)
+
+If your extension uses other libraries (zlib,...) you might have to convert
+them too. The converted files have to reside in the same directories as the
+normal libraries do.
+
+
+.. seealso::
+
+ `Building Python modules on MS Windows platform with MinGW <http://www.zope.org/Members/als/tips/win32_mingw_modules>`_
+ Information about building the required libraries for the MinGW environment.
+
+
+.. rubric:: Footnotes
+
+.. [#] This also means you could replace all existing COFF-libraries with OMF-libraries
+ of the same name.
+
+.. [#] Check http://sources.redhat.com/cygwin/ and http://www.mingw.org/ for more
+ information
+
+.. [#] Then you have no POSIX emulation available, but you also don't need
+ :file:`cygwin1.dll`.
diff --git a/Doc/documenting/style.rst b/Doc/documenting/style.rst
index 2548cb0..71a52f2 100644
--- a/Doc/documenting/style.rst
+++ b/Doc/documenting/style.rst
@@ -14,9 +14,10 @@ document.
Use of whitespace
-----------------
-All reST files use an indentation of 3 spaces. The maximum line length is 80
-characters for normal text, but tables, deeply indented code samples and long
-links may extend beyond that.
+All reST files use an indentation of 3 spaces; no tabs are allowed. The
+maximum line length is 80 characters for normal text, but tables, deeply
+indented code samples and long links may extend beyond that. Code example
+bodies should use normal Python 4-space indentation.
Make generous use of blank lines where applicable; they help grouping things
together.
diff --git a/Doc/extending/newtypes.rst b/Doc/extending/newtypes.rst
index 2ba01bc..6a65941 100644
--- a/Doc/extending/newtypes.rst
+++ b/Doc/extending/newtypes.rst
@@ -288,18 +288,16 @@ strings, so we provide a new method::
self = (Noddy *)type->tp_alloc(type, 0);
if (self != NULL) {
self->first = PyString_FromString("");
- if (self->first == NULL)
- {
+ if (self->first == NULL) {
Py_DECREF(self);
return NULL;
- }
+ }
self->last = PyString_FromString("");
- if (self->last == NULL)
- {
+ if (self->last == NULL) {
Py_DECREF(self);
return NULL;
- }
+ }
self->number = 0;
}
diff --git a/Doc/faq/design.rst b/Doc/faq/design.rst
index e45aaaa..1521f6c 100644
--- a/Doc/faq/design.rst
+++ b/Doc/faq/design.rst
@@ -645,7 +645,7 @@ construction of large programs.
Python 2.6 adds an :mod:`abc` module that lets you define Abstract Base Classes
(ABCs). You can then use :func:`isinstance` and :func:`issubclass` to check
whether an instance or a class implements a particular ABC. The
-:mod:`collections` module defines a set of useful ABCs such as
+:mod:`collections.abc` module defines a set of useful ABCs such as
:class:`Iterable`, :class:`Container`, and :class:`MutableMapping`.
For Python, many of the advantages of interface specifications can be obtained
diff --git a/Doc/faq/extending.rst b/Doc/faq/extending.rst
index 7c684a0..fa245c7 100644
--- a/Doc/faq/extending.rst
+++ b/Doc/faq/extending.rst
@@ -445,34 +445,3 @@ In Python 2.2, you can inherit from built-in classes such as :class:`int`,
The Boost Python Library (BPL, http://www.boost.org/libs/python/doc/index.html)
provides a way of doing this from C++ (i.e. you can inherit from an extension
class written in C++ using the BPL).
-
-
-When importing module X, why do I get "undefined symbol: PyUnicodeUCS2*"?
--------------------------------------------------------------------------
-
-You are using a version of Python that uses a 4-byte representation for Unicode
-characters, but some C extension module you are importing was compiled using a
-Python that uses a 2-byte representation for Unicode characters (the default).
-
-If instead the name of the undefined symbol starts with ``PyUnicodeUCS4``, the
-problem is the reverse: Python was built using 2-byte Unicode characters, and
-the extension module was compiled using a Python with 4-byte Unicode characters.
-
-This can easily occur when using pre-built extension packages. RedHat Linux
-7.x, in particular, provided a "python2" binary that is compiled with 4-byte
-Unicode. This only causes the link failure if the extension uses any of the
-``PyUnicode_*()`` functions. It is also a problem if an extension uses any of
-the Unicode-related format specifiers for :c:func:`Py_BuildValue` (or similar) or
-parameter specifications for :c:func:`PyArg_ParseTuple`.
-
-You can check the size of the Unicode character a Python interpreter is using by
-checking the value of sys.maxunicode:
-
- >>> import sys
- >>> if sys.maxunicode > 65535:
- ... print('UCS4 build')
- ... else:
- ... print('UCS2 build')
-
-The only way to solve this problem is to use extension modules compiled with a
-Python binary built using the same size for Unicode characters.
diff --git a/Doc/glossary.rst b/Doc/glossary.rst
index 3b211ae..f5ca0d9 100644
--- a/Doc/glossary.rst
+++ b/Doc/glossary.rst
@@ -34,7 +34,7 @@ Glossary
subclasses, which are classes that don't inherit from a class but are
still recognized by :func:`isinstance` and :func:`issubclass`; see the
:mod:`abc` module documentation. Python comes with many built-in ABCs for
- data structures (in the :mod:`collections` module), numbers (in the
+ data structures (in the :mod:`collections.abc` module), numbers (in the
:mod:`numbers` module), streams (in the :mod:`io` module), import finders
and loaders (in the :mod:`importlib.abc` module). You can create your own
ABCs with the :mod:`abc` module.
@@ -434,8 +434,8 @@ Glossary
mapping
A container object that supports arbitrary key lookups and implements the
- methods specified in the :class:`~collections.Mapping` or
- :class:`~collections.MutableMapping`
+ methods specified in the :class:`~collections.abc.Mapping` or
+ :class:`~collections.abc.MutableMapping`
:ref:`abstract base classes <collections-abstract-base-classes>`. Examples
include :class:`dict`, :class:`collections.defaultdict`,
:class:`collections.OrderedDict` and :class:`collections.Counter`.
@@ -544,6 +544,24 @@ Glossary
for piece in food:
print(piece)
+ qualified name
+ A dotted name showing the "path" from a module's global scope to a
+ class, function or method defined in that module, as defined in
+ :pep:`3155`. For top-level functions and classes, the qualified name
+ is the same as the object's name::
+
+ >>> class C:
+ ... class D:
+ ... def meth(self):
+ ... pass
+ ...
+ >>> C.__qualname__
+ 'C'
+ >>> C.D.__qualname__
+ 'C.D'
+ >>> C.D.meth.__qualname__
+ 'C.D.meth'
+
reference count
The number of references to an object. When the reference count of an
object drops to zero, it is deallocated. Reference counting is
@@ -586,6 +604,14 @@ Glossary
an :term:`expression` or a one of several constructs with a keyword, such
as :keyword:`if`, :keyword:`while` or :keyword:`for`.
+ struct sequence
+ A tuple with named elements. Struct sequences expose an interface similar
+ to :term:`named tuple` in that elements can either be accessed either by
+ index or as an attribute. However, they do not have any of the named tuple
+ methods like :meth:`~collections.somenamedtuple._make` or
+ :meth:`~collections.somenamedtuple._asdict`. Examples of struct sequences
+ include :data:`sys.float_info` and the return value of :func:`os.stat`.
+
triple-quoted string
A string which is bound by three instances of either a quotation mark
(") or an apostrophe ('). While they don't provide any functionality
diff --git a/Doc/howto/descriptor.rst b/Doc/howto/descriptor.rst
index 1616f67..0b513f9 100644
--- a/Doc/howto/descriptor.rst
+++ b/Doc/howto/descriptor.rst
@@ -36,9 +36,7 @@ continuing through the base classes of ``type(a)`` excluding metaclasses. If the
looked-up value is an object defining one of the descriptor methods, then Python
may override the default behavior and invoke the descriptor method instead.
Where this occurs in the precedence chain depends on which descriptor methods
-were defined. Note that descriptors are only invoked for new style objects or
-classes (a class is new style if it inherits from :class:`object` or
-:class:`type`).
+were defined.
Descriptors are a powerful, general purpose protocol. They are the mechanism
behind properties, methods, static methods, class methods, and :func:`super()`.
@@ -89,8 +87,6 @@ of ``obj``. If ``d`` defines the method :meth:`__get__`, then ``d.__get__(obj)`
is invoked according to the precedence rules listed below.
The details of invocation depend on whether ``obj`` is an object or a class.
-Either way, descriptors only work for new style objects and classes. A class is
-new style if it is a subclass of :class:`object`.
For objects, the machinery is in :meth:`object.__getattribute__` which
transforms ``b.x`` into ``type(b).__dict__['x'].__get__(b, type(b))``. The
@@ -115,7 +111,6 @@ The important points to remember are:
* descriptors are invoked by the :meth:`__getattribute__` method
* overriding :meth:`__getattribute__` prevents automatic descriptor calls
-* :meth:`__getattribute__` is only available with new style classes and objects
* :meth:`object.__getattribute__` and :meth:`type.__getattribute__` make
different calls to :meth:`__get__`.
* data descriptors always override instance dictionaries.
@@ -128,10 +123,7 @@ and then returns ``A.__dict__['m'].__get__(obj, A)``. If not a descriptor,
``m`` is returned unchanged. If not in the dictionary, ``m`` reverts to a
search using :meth:`object.__getattribute__`.
-Note, in Python 2.2, ``super(B, obj).m()`` would only invoke :meth:`__get__` if
-``m`` was a data descriptor. In Python 2.3, non-data descriptors also get
-invoked unless an old-style class is involved. The implementation details are
-in :c:func:`super_getattro()` in
+The implementation details are in :c:func:`super_getattro()` in
`Objects/typeobject.c <http://svn.python.org/view/python/trunk/Objects/typeobject.c?view=markup>`_
and a pure Python equivalent can be found in `Guido's Tutorial`_.
diff --git a/Doc/howto/sockets.rst b/Doc/howto/sockets.rst
index e2703f5..8ee334a 100644
--- a/Doc/howto/sockets.rst
+++ b/Doc/howto/sockets.rst
@@ -23,8 +23,8 @@ It's not really a tutorial - you'll still have work to do in getting things
working. It doesn't cover the fine points (and there are a lot of them), but I
hope it will give you enough background to begin using them decently.
-I'm only going to talk about INET sockets, but they account for at least 99% of
-the sockets in use. And I'll only talk about STREAM sockets - unless you really
+I'm only going to talk about INET (i.e. IPv4) sockets, but they account for at least 99% of
+the sockets in use. And I'll only talk about STREAM (i.e. TCP) sockets - unless you really
know what you're doing (in which case this HOWTO isn't for you!), you'll get
better behavior and performance from a STREAM socket than anything else. I will
try to clear up the mystery of what a socket is, as well as some hints on how to
@@ -206,10 +206,10 @@ length message::
totalsent = totalsent + sent
def myreceive(self):
- msg = ''
+ msg = b''
while len(msg) < MSGLEN:
chunk = self.sock.recv(MSGLEN-len(msg))
- if chunk == '':
+ if chunk == b'':
raise RuntimeError("socket connection broken")
msg = msg + chunk
return msg
@@ -369,12 +369,6 @@ have created a new socket to ``connect`` to someone else, put it in the
potential_writers list. If it shows up in the writable list, you have a decent
chance that it has connected.
-One very nasty problem with ``select``: if somewhere in those input lists of
-sockets is one which has died a nasty death, the ``select`` will fail. You then
-need to loop through every single damn socket in all those lists and do a
-``select([sock],[],[],0)`` until you find the bad one. That timeout of 0 means
-it won't take long, but it's ugly.
-
Actually, ``select`` can be handy even with blocking sockets. It's one way of
determining whether you will block - the socket returns as readable when there's
something in the buffers. However, this still doesn't help with the problem of
@@ -384,26 +378,6 @@ determining whether the other end is done, or just busy with something else.
files. Don't try this on Windows. On Windows, ``select`` works with sockets
only. Also note that in C, many of the more advanced socket options are done
differently on Windows. In fact, on Windows I usually use threads (which work
-very, very well) with my sockets. Face it, if you want any kind of performance,
-your code will look very different on Windows than on Unix.
-
-
-Performance
------------
+very, very well) with my sockets.
-There's no question that the fastest sockets code uses non-blocking sockets and
-select to multiplex them. You can put together something that will saturate a
-LAN connection without putting any strain on the CPU.
-
-The trouble is that an app written this way can't do much of anything else -
-it needs to be ready to shuffle bytes around at all times. Assuming that your
-app is actually supposed to do something more than that, threading is the
-optimal solution, (and using non-blocking sockets will be faster than using
-blocking sockets).
-
-Finally, remember that even though blocking sockets are somewhat slower than
-non-blocking, in many cases they are the "right" solution. After all, if your
-app is driven by the data it receives over a socket, there's not much sense in
-complicating the logic just so your app can wait on ``select`` instead of
-``recv``.
diff --git a/Doc/includes/noddy2.c b/Doc/includes/noddy2.c
index 9b8eafb..9641558 100644
--- a/Doc/includes/noddy2.c
+++ b/Doc/includes/noddy2.c
@@ -24,18 +24,16 @@ Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
self = (Noddy *)type->tp_alloc(type, 0);
if (self != NULL) {
self->first = PyUnicode_FromString("");
- if (self->first == NULL)
- {
+ if (self->first == NULL) {
Py_DECREF(self);
return NULL;
- }
-
+ }
+
self->last = PyUnicode_FromString("");
- if (self->last == NULL)
- {
+ if (self->last == NULL) {
Py_DECREF(self);
return NULL;
- }
+ }
self->number = 0;
}
@@ -50,10 +48,10 @@ Noddy_init(Noddy *self, PyObject *args, PyObject *kwds)
static char *kwlist[] = {"first", "last", "number", NULL};
- if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist,
- &first, &last,
+ if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist,
+ &first, &last,
&self->number))
- return -1;
+ return -1;
if (first) {
tmp = self->first;
@@ -86,15 +84,6 @@ static PyMemberDef Noddy_members[] = {
static PyObject *
Noddy_name(Noddy* self)
{
- static PyObject *format = NULL;
- PyObject *args, *result;
-
- if (format == NULL) {
- format = PyUnicode_FromString("%s %s");
- if (format == NULL)
- return NULL;
- }
-
if (self->first == NULL) {
PyErr_SetString(PyExc_AttributeError, "first");
return NULL;
@@ -105,14 +94,7 @@ Noddy_name(Noddy* self)
return NULL;
}
- args = Py_BuildValue("OO", self->first, self->last);
- if (args == NULL)
- return NULL;
-
- result = PyUnicode_Format(format, args);
- Py_DECREF(args);
-
- return result;
+ return PyUnicode_FromFormat("%S %S", self->first, self->last);
}
static PyMethodDef Noddy_methods[] = {
@@ -145,12 +127,12 @@ static PyTypeObject NoddyType = {
Py_TPFLAGS_DEFAULT |
Py_TPFLAGS_BASETYPE, /* tp_flags */
"Noddy objects", /* tp_doc */
- 0, /* tp_traverse */
- 0, /* tp_clear */
- 0, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
Noddy_methods, /* tp_methods */
Noddy_members, /* tp_members */
0, /* tp_getset */
@@ -173,7 +155,7 @@ static PyModuleDef noddy2module = {
};
PyMODINIT_FUNC
-PyInit_noddy2(void)
+PyInit_noddy2(void)
{
PyObject* m;
diff --git a/Doc/includes/noddy3.c b/Doc/includes/noddy3.c
index 89f3a77..8a5a753 100644
--- a/Doc/includes/noddy3.c
+++ b/Doc/includes/noddy3.c
@@ -24,18 +24,16 @@ Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
self = (Noddy *)type->tp_alloc(type, 0);
if (self != NULL) {
self->first = PyUnicode_FromString("");
- if (self->first == NULL)
- {
+ if (self->first == NULL) {
Py_DECREF(self);
return NULL;
- }
-
+ }
+
self->last = PyUnicode_FromString("");
- if (self->last == NULL)
- {
+ if (self->last == NULL) {
Py_DECREF(self);
return NULL;
- }
+ }
self->number = 0;
}
@@ -50,10 +48,10 @@ Noddy_init(Noddy *self, PyObject *args, PyObject *kwds)
static char *kwlist[] = {"first", "last", "number", NULL};
- if (! PyArg_ParseTupleAndKeywords(args, kwds, "|SSi", kwlist,
- &first, &last,
+ if (! PyArg_ParseTupleAndKeywords(args, kwds, "|SSi", kwlist,
+ &first, &last,
&self->number))
- return -1;
+ return -1;
if (first) {
tmp = self->first;
@@ -88,22 +86,22 @@ Noddy_getfirst(Noddy *self, void *closure)
static int
Noddy_setfirst(Noddy *self, PyObject *value, void *closure)
{
- if (value == NULL) {
- PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");
- return -1;
- }
-
- if (! PyUnicode_Check(value)) {
- PyErr_SetString(PyExc_TypeError,
- "The first attribute value must be a string");
- return -1;
- }
-
- Py_DECREF(self->first);
- Py_INCREF(value);
- self->first = value;
-
- return 0;
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");
+ return -1;
+ }
+
+ if (! PyUnicode_Check(value)) {
+ PyErr_SetString(PyExc_TypeError,
+ "The first attribute value must be a string");
+ return -1;
+ }
+
+ Py_DECREF(self->first);
+ Py_INCREF(value);
+ self->first = value;
+
+ return 0;
}
static PyObject *
@@ -116,30 +114,30 @@ Noddy_getlast(Noddy *self, void *closure)
static int
Noddy_setlast(Noddy *self, PyObject *value, void *closure)
{
- if (value == NULL) {
- PyErr_SetString(PyExc_TypeError, "Cannot delete the last attribute");
- return -1;
- }
-
- if (! PyUnicode_Check(value)) {
- PyErr_SetString(PyExc_TypeError,
- "The last attribute value must be a string");
- return -1;
- }
-
- Py_DECREF(self->last);
- Py_INCREF(value);
- self->last = value;
-
- return 0;
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Cannot delete the last attribute");
+ return -1;
+ }
+
+ if (! PyUnicode_Check(value)) {
+ PyErr_SetString(PyExc_TypeError,
+ "The last attribute value must be a string");
+ return -1;
+ }
+
+ Py_DECREF(self->last);
+ Py_INCREF(value);
+ self->last = value;
+
+ return 0;
}
static PyGetSetDef Noddy_getseters[] = {
- {"first",
+ {"first",
(getter)Noddy_getfirst, (setter)Noddy_setfirst,
"first name",
NULL},
- {"last",
+ {"last",
(getter)Noddy_getlast, (setter)Noddy_setlast,
"last name",
NULL},
@@ -149,23 +147,7 @@ static PyGetSetDef Noddy_getseters[] = {
static PyObject *
Noddy_name(Noddy* self)
{
- static PyObject *format = NULL;
- PyObject *args, *result;
-
- if (format == NULL) {
- format = PyUnicode_FromString("%s %s");
- if (format == NULL)
- return NULL;
- }
-
- args = Py_BuildValue("OO", self->first, self->last);
- if (args == NULL)
- return NULL;
-
- result = PyUnicode_Format(format, args);
- Py_DECREF(args);
-
- return result;
+ return PyUnicode_FromFormat("%S %S", self->first, self->last);
}
static PyMethodDef Noddy_methods[] = {
@@ -198,12 +180,12 @@ static PyTypeObject NoddyType = {
Py_TPFLAGS_DEFAULT |
Py_TPFLAGS_BASETYPE, /* tp_flags */
"Noddy objects", /* tp_doc */
- 0, /* tp_traverse */
- 0, /* tp_clear */
- 0, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
Noddy_methods, /* tp_methods */
Noddy_members, /* tp_members */
Noddy_getseters, /* tp_getset */
@@ -226,7 +208,7 @@ static PyModuleDef noddy3module = {
};
PyMODINIT_FUNC
-PyInit_noddy3(void)
+PyInit_noddy3(void)
{
PyObject* m;
diff --git a/Doc/includes/noddy4.c b/Doc/includes/noddy4.c
index 6a96fac..eb9622a 100644
--- a/Doc/includes/noddy4.c
+++ b/Doc/includes/noddy4.c
@@ -27,7 +27,7 @@ Noddy_traverse(Noddy *self, visitproc visit, void *arg)
return 0;
}
-static int
+static int
Noddy_clear(Noddy *self)
{
PyObject *tmp;
@@ -58,18 +58,16 @@ Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
self = (Noddy *)type->tp_alloc(type, 0);
if (self != NULL) {
self->first = PyUnicode_FromString("");
- if (self->first == NULL)
- {
+ if (self->first == NULL) {
Py_DECREF(self);
return NULL;
- }
-
+ }
+
self->last = PyUnicode_FromString("");
- if (self->last == NULL)
- {
+ if (self->last == NULL) {
Py_DECREF(self);
return NULL;
- }
+ }
self->number = 0;
}
@@ -84,10 +82,10 @@ Noddy_init(Noddy *self, PyObject *args, PyObject *kwds)
static char *kwlist[] = {"first", "last", "number", NULL};
- if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist,
- &first, &last,
+ if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist,
+ &first, &last,
&self->number))
- return -1;
+ return -1;
if (first) {
tmp = self->first;
@@ -120,15 +118,6 @@ static PyMemberDef Noddy_members[] = {
static PyObject *
Noddy_name(Noddy* self)
{
- static PyObject *format = NULL;
- PyObject *args, *result;
-
- if (format == NULL) {
- format = PyUnicode_FromString("%s %s");
- if (format == NULL)
- return NULL;
- }
-
if (self->first == NULL) {
PyErr_SetString(PyExc_AttributeError, "first");
return NULL;
@@ -139,14 +128,7 @@ Noddy_name(Noddy* self)
return NULL;
}
- args = Py_BuildValue("OO", self->first, self->last);
- if (args == NULL)
- return NULL;
-
- result = PyUnicode_Format(format, args);
- Py_DECREF(args);
-
- return result;
+ return PyUnicode_FromFormat("%S %S", self->first, self->last);
}
static PyMethodDef Noddy_methods[] = {
@@ -182,10 +164,10 @@ static PyTypeObject NoddyType = {
"Noddy objects", /* tp_doc */
(traverseproc)Noddy_traverse, /* tp_traverse */
(inquiry)Noddy_clear, /* tp_clear */
- 0, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
Noddy_methods, /* tp_methods */
Noddy_members, /* tp_members */
0, /* tp_getset */
@@ -208,7 +190,7 @@ static PyModuleDef noddy4module = {
};
PyMODINIT_FUNC
-PyInit_noddy4(void)
+PyInit_noddy4(void)
{
PyObject* m;
diff --git a/Doc/install/index.rst b/Doc/install/index.rst
index b20f1fb..bb2e9c5 100644
--- a/Doc/install/index.rst
+++ b/Doc/install/index.rst
@@ -1,12 +1,10 @@
-.. highlightlang:: none
+.. _packaging-install-index:
-.. _install-index:
+******************************
+ Installing Python Projects
+******************************
-*****************************
- Installing Python Modules
-*****************************
-
-:Author: Greg Ward
+:Author: The Fellowship of the Packaging
:Release: |version|
:Date: |today|
@@ -16,1071 +14,43 @@
about Python and aren't about to learn the language just in order to
install and maintain it for their users, i.e. system administrators.
Thus, I have to be sure to explain the basics at some point:
- sys.path and PYTHONPATH at least. Should probably give pointers to
+ sys.path and PYTHONPATH at least. Should probably give pointers to
other docs on "import site", PYTHONSTARTUP, PYTHONHOME, etc.
Finally, it might be useful to include all the material from my "Care
- and Feeding of a Python Installation" talk in here somewhere. Yow!
+ and Feeding of a Python Installation" talk in here somewhere. Yow!
.. topic:: Abstract
- This document describes the Python Distribution Utilities ("Distutils") from the
- end-user's point-of-view, describing how to extend the capabilities of a
- standard Python installation by building and installing third-party Python
- modules and extensions.
-
-
-.. _inst-intro:
-
-Introduction
-============
-
-Although Python's extensive standard library covers many programming needs,
-there often comes a time when you need to add some new functionality to your
-Python installation in the form of third-party modules. This might be necessary
-to support your own programming, or to support an application that you want to
-use and that happens to be written in Python.
-
-In the past, there has been little support for adding third-party modules to an
-existing Python installation. With the introduction of the Python Distribution
-Utilities (Distutils for short) in Python 2.0, this changed.
-
-This document is aimed primarily at the people who need to install third-party
-Python modules: end-users and system administrators who just need to get some
-Python application running, and existing Python programmers who want to add some
-new goodies to their toolbox. You don't need to know Python to read this
-document; there will be some brief forays into using Python's interactive mode
-to explore your installation, but that's it. If you're looking for information
-on how to distribute your own Python modules so that others may use them, see
-the :ref:`distutils-index` manual.
-
-
-.. _inst-trivial-install:
-
-Best case: trivial installation
--------------------------------
-
-In the best case, someone will have prepared a special version of the module
-distribution you want to install that is targeted specifically at your platform
-and is installed just like any other software on your platform. For example,
-the module developer might make an executable installer available for Windows
-users, an RPM package for users of RPM-based Linux systems (Red Hat, SuSE,
-Mandrake, and many others), a Debian package for users of Debian-based Linux
-systems, and so forth.
-
-In that case, you would download the installer appropriate to your platform and
-do the obvious thing with it: run it if it's an executable installer, ``rpm
---install`` it if it's an RPM, etc. You don't need to run Python or a setup
-script, you don't need to compile anything---you might not even need to read any
-instructions (although it's always a good idea to do so anyway).
-
-Of course, things will not always be that easy. You might be interested in a
-module distribution that doesn't have an easy-to-use installer for your
-platform. In that case, you'll have to start with the source distribution
-released by the module's author/maintainer. Installing from a source
-distribution is not too hard, as long as the modules are packaged in the
-standard way. The bulk of this document is about building and installing
-modules from standard source distributions.
-
-
-.. _inst-new-standard:
-
-The new standard: Distutils
----------------------------
-
-If you download a module source distribution, you can tell pretty quickly if it
-was packaged and distributed in the standard way, i.e. using the Distutils.
-First, the distribution's name and version number will be featured prominently
-in the name of the downloaded archive, e.g. :file:`foo-1.0.tar.gz` or
-:file:`widget-0.9.7.zip`. Next, the archive will unpack into a similarly-named
-directory: :file:`foo-1.0` or :file:`widget-0.9.7`. Additionally, the
-distribution will contain a setup script :file:`setup.py`, and a file named
-:file:`README.txt` or possibly just :file:`README`, which should explain that
-building and installing the module distribution is a simple matter of running
-one command from a terminal::
-
- python setup.py install
-
-For Windows, this command should be run from a command prompt window
-(:menuselection:`Start --> Accessories`)::
-
- setup.py install
-
-If all these things are true, then you already know how to build and install the
-modules you've just downloaded: Run the command above. Unless you need to
-install things in a non-standard way or customize the build process, you don't
-really need this manual. Or rather, the above command is everything you need to
-get out of this manual.
-
-
-.. _inst-standard-install:
-
-Standard Build and Install
-==========================
-
-As described in section :ref:`inst-new-standard`, building and installing a module
-distribution using the Distutils is usually one simple command to run from a
-terminal::
-
- python setup.py install
-
-
-.. _inst-platform-variations:
-
-Platform variations
--------------------
-
-You should always run the setup command from the distribution root directory,
-i.e. the top-level subdirectory that the module source distribution unpacks
-into. For example, if you've just downloaded a module source distribution
-:file:`foo-1.0.tar.gz` onto a Unix system, the normal thing to do is::
-
- gunzip -c foo-1.0.tar.gz | tar xf - # unpacks into directory foo-1.0
- cd foo-1.0
- python setup.py install
-
-On Windows, you'd probably download :file:`foo-1.0.zip`. If you downloaded the
-archive file to :file:`C:\\Temp`, then it would unpack into
-:file:`C:\\Temp\\foo-1.0`; you can use either a archive manipulator with a
-graphical user interface (such as WinZip) or a command-line tool (such as
-:program:`unzip` or :program:`pkunzip`) to unpack the archive. Then, open a
-command prompt window and run::
-
- cd c:\Temp\foo-1.0
- python setup.py install
-
-
-.. _inst-splitting-up:
-
-Splitting the job up
---------------------
-
-Running ``setup.py install`` builds and installs all modules in one run. If you
-prefer to work incrementally---especially useful if you want to customize the
-build process, or if things are going wrong---you can use the setup script to do
-one thing at a time. This is particularly helpful when the build and install
-will be done by different users---for example, you might want to build a module
-distribution and hand it off to a system administrator for installation (or do
-it yourself, with super-user privileges).
-
-For example, you can build everything in one step, and then install everything
-in a second step, by invoking the setup script twice::
-
- python setup.py build
- python setup.py install
-
-If you do this, you will notice that running the :command:`install` command
-first runs the :command:`build` command, which---in this case---quickly notices
-that it has nothing to do, since everything in the :file:`build` directory is
-up-to-date.
-
-You may not need this ability to break things down often if all you do is
-install modules downloaded off the 'net, but it's very handy for more advanced
-tasks. If you get into distributing your own Python modules and extensions,
-you'll run lots of individual Distutils commands on their own.
-
-
-.. _inst-how-build-works:
-
-How building works
-------------------
-
-As implied above, the :command:`build` command is responsible for putting the
-files to install into a *build directory*. By default, this is :file:`build`
-under the distribution root; if you're excessively concerned with speed, or want
-to keep the source tree pristine, you can change the build directory with the
-:option:`--build-base` option. For example::
-
- python setup.py build --build-base=/tmp/pybuild/foo-1.0
-
-(Or you could do this permanently with a directive in your system or personal
-Distutils configuration file; see section :ref:`inst-config-files`.) Normally, this
-isn't necessary.
-
-The default layout for the build tree is as follows::
-
- --- build/ --- lib/
- or
- --- build/ --- lib.<plat>/
- temp.<plat>/
-
-where ``<plat>`` expands to a brief description of the current OS/hardware
-platform and Python version. The first form, with just a :file:`lib` directory,
-is used for "pure module distributions"---that is, module distributions that
-include only pure Python modules. If a module distribution contains any
-extensions (modules written in C/C++), then the second form, with two ``<plat>``
-directories, is used. In that case, the :file:`temp.{plat}` directory holds
-temporary files generated by the compile/link process that don't actually get
-installed. In either case, the :file:`lib` (or :file:`lib.{plat}`) directory
-contains all Python modules (pure Python and extensions) that will be installed.
-
-In the future, more directories will be added to handle Python scripts,
-documentation, binary executables, and whatever else is needed to handle the job
-of installing Python modules and applications.
-
-
-.. _inst-how-install-works:
-
-How installation works
-----------------------
-
-After the :command:`build` command runs (whether you run it explicitly, or the
-:command:`install` command does it for you), the work of the :command:`install`
-command is relatively simple: all it has to do is copy everything under
-:file:`build/lib` (or :file:`build/lib.{plat}`) to your chosen installation
-directory.
-
-If you don't choose an installation directory---i.e., if you just run ``setup.py
-install``\ ---then the :command:`install` command installs to the standard
-location for third-party Python modules. This location varies by platform and
-by how you built/installed Python itself. On Unix (and Mac OS X, which is also
-Unix-based), it also depends on whether the module distribution being installed
-is pure Python or contains extensions ("non-pure"):
-
-+-----------------+-----------------------------------------------------+--------------------------------------------------+-------+
-| Platform | Standard installation location | Default value | Notes |
-+=================+=====================================================+==================================================+=======+
-| Unix (pure) | :file:`{prefix}/lib/python{X.Y}/site-packages` | :file:`/usr/local/lib/python{X.Y}/site-packages` | \(1) |
-+-----------------+-----------------------------------------------------+--------------------------------------------------+-------+
-| Unix (non-pure) | :file:`{exec-prefix}/lib/python{X.Y}/site-packages` | :file:`/usr/local/lib/python{X.Y}/site-packages` | \(1) |
-+-----------------+-----------------------------------------------------+--------------------------------------------------+-------+
-| Windows | :file:`{prefix}\\Lib\\site-packages` | :file:`C:\\Python{XY}\\Lib\\site-packages` | \(2) |
-+-----------------+-----------------------------------------------------+--------------------------------------------------+-------+
-
-Notes:
-
-(1)
- Most Linux distributions include Python as a standard part of the system, so
- :file:`{prefix}` and :file:`{exec-prefix}` are usually both :file:`/usr` on
- Linux. If you build Python yourself on Linux (or any Unix-like system), the
- default :file:`{prefix}` and :file:`{exec-prefix}` are :file:`/usr/local`.
-
-(2)
- The default installation directory on Windows was :file:`C:\\Program
- Files\\Python` under Python 1.6a1, 1.5.2, and earlier.
-
-:file:`{prefix}` and :file:`{exec-prefix}` stand for the directories that Python
-is installed to, and where it finds its libraries at run-time. They are always
-the same under Windows, and very often the same under Unix and Mac OS X. You
-can find out what your Python installation uses for :file:`{prefix}` and
-:file:`{exec-prefix}` by running Python in interactive mode and typing a few
-simple commands. Under Unix, just type ``python`` at the shell prompt. Under
-Windows, choose :menuselection:`Start --> Programs --> Python X.Y -->
-Python (command line)`. Once the interpreter is started, you type Python code
-at the prompt. For example, on my Linux system, I type the three Python
-statements shown below, and get the output as shown, to find out my
-:file:`{prefix}` and :file:`{exec-prefix}`::
-
- Python 2.4 (#26, Aug 7 2004, 17:19:02)
- Type "help", "copyright", "credits" or "license" for more information.
- >>> import sys
- >>> sys.prefix
- '/usr'
- >>> sys.exec_prefix
- '/usr'
-
-A few other placeholders are used in this document: :file:`{X.Y}` stands for the
-version of Python, for example ``3.2``; :file:`{abiflags}` will be replaced by
-the value of :data:`sys.abiflags` or the empty string for platforms which don't
-define ABI flags; :file:`{distname}` will be replaced by the name of the module
-distribution being installed. Dots and capitalization are important in the
-paths; for example, a value that uses ``python3.2`` on UNIX will typically use
-``Python32`` on Windows.
-
-If you don't want to install modules to the standard location, or if you don't
-have permission to write there, then you need to read about alternate
-installations in section :ref:`inst-alt-install`. If you want to customize your
-installation directories more heavily, see section :ref:`inst-custom-install` on
-custom installations.
-
-
-.. _inst-alt-install:
-
-Alternate Installation
-======================
-
-Often, it is necessary or desirable to install modules to a location other than
-the standard location for third-party Python modules. For example, on a Unix
-system you might not have permission to write to the standard third-party module
-directory. Or you might wish to try out a module before making it a standard
-part of your local Python installation. This is especially true when upgrading
-a distribution already present: you want to make sure your existing base of
-scripts still works with the new version before actually upgrading.
-
-The Distutils :command:`install` command is designed to make installing module
-distributions to an alternate location simple and painless. The basic idea is
-that you supply a base directory for the installation, and the
-:command:`install` command picks a set of directories (called an *installation
-scheme*) under this base directory in which to install files. The details
-differ across platforms, so read whichever of the following sections applies to
-you.
-
-Note that the various alternate installation schemes are mutually exclusive: you
-can pass ``--user``, or ``--home``, or ``--prefix`` and ``--exec-prefix``, or
-``--install-base`` and ``--install-platbase``, but you can't mix from these
-groups.
-
-
-.. _inst-alt-install-user:
-
-Alternate installation: the user scheme
----------------------------------------
-
-This scheme is designed to be the most convenient solution for users that don't
-have write permission to the global site-packages directory or don't want to
-install into it. It is enabled with a simple option::
-
- python setup.py install --user
-
-Files will be installed into subdirectories of :data:`site.USER_BASE` (written
-as :file:`{userbase}` hereafter). This scheme installs pure Python modules and
-extension modules in the same location (also known as :data:`site.USER_SITE`).
-Here are the values for UNIX, including Mac OS X:
-
-=============== ===========================================================
-Type of file Installation directory
-=============== ===========================================================
-modules :file:`{userbase}/lib/python{X.Y}/site-packages`
-scripts :file:`{userbase}/bin`
-data :file:`{userbase}`
-C headers :file:`{userbase}/include/python{X.Y}{abiflags}/{distname}`
-=============== ===========================================================
-
-And here are the values used on Windows:
-
-=============== ===========================================================
-Type of file Installation directory
-=============== ===========================================================
-modules :file:`{userbase}\\Python{XY}\\site-packages`
-scripts :file:`{userbase}\\Scripts`
-data :file:`{userbase}`
-C headers :file:`{userbase}\\Python{XY}\\Include\\{distname}`
-=============== ===========================================================
-
-The advantage of using this scheme compared to the other ones described below is
-that the user site-packages directory is under normal conditions always included
-in :data:`sys.path` (see :mod:`site` for more information), which means that
-there is no additional step to perform after running the :file:`setup.py` script
-to finalize the installation.
-
-The :command:`build_ext` command also has a ``--user`` option to add
-:file:`{userbase}/include` to the compiler search path for header files and
-:file:`{userbase}/lib` to the compiler search path for libraries as well as to
-the runtime search path for shared C libraries (rpath).
-
-
-.. _inst-alt-install-home:
-
-Alternate installation: the home scheme
----------------------------------------
-
-The idea behind the "home scheme" is that you build and maintain a personal
-stash of Python modules. This scheme's name is derived from the idea of a
-"home" directory on Unix, since it's not unusual for a Unix user to make their
-home directory have a layout similar to :file:`/usr/` or :file:`/usr/local/`.
-This scheme can be used by anyone, regardless of the operating system they
-are installing for.
-
-Installing a new module distribution is as simple as ::
-
- python setup.py install --home=<dir>
-
-where you can supply any directory you like for the :option:`--home` option. On
-Unix, lazy typists can just type a tilde (``~``); the :command:`install` command
-will expand this to your home directory::
-
- python setup.py install --home=~
-
-To make Python find the distributions installed with this scheme, you may have
-to :ref:`modify Python's search path <inst-search-path>` or edit
-:mod:`sitecustomize` (see :mod:`site`) to call :func:`site.addsitedir` or edit
-:data:`sys.path`.
-
-The :option:`--home` option defines the installation base directory. Files are
-installed to the following directories under the installation base as follows:
-
-=============== ===========================================================
-Type of file Installation directory
-=============== ===========================================================
-modules :file:`{home}/lib/python`
-scripts :file:`{home}/bin`
-data :file:`{home}`
-C headers :file:`{home}/include/python/{distname}`
-=============== ===========================================================
-
-(Mentally replace slashes with backslashes if you're on Windows.)
-
-
-.. _inst-alt-install-prefix-unix:
-
-Alternate installation: Unix (the prefix scheme)
-------------------------------------------------
-
-The "prefix scheme" is useful when you wish to use one Python installation to
-perform the build/install (i.e., to run the setup script), but install modules
-into the third-party module directory of a different Python installation (or
-something that looks like a different Python installation). If this sounds a
-trifle unusual, it is---that's why the user and home schemes come before. However,
-there are at least two known cases where the prefix scheme will be useful.
-
-First, consider that many Linux distributions put Python in :file:`/usr`, rather
-than the more traditional :file:`/usr/local`. This is entirely appropriate,
-since in those cases Python is part of "the system" rather than a local add-on.
-However, if you are installing Python modules from source, you probably want
-them to go in :file:`/usr/local/lib/python2.{X}` rather than
-:file:`/usr/lib/python2.{X}`. This can be done with ::
-
- /usr/bin/python setup.py install --prefix=/usr/local
-
-Another possibility is a network filesystem where the name used to write to a
-remote directory is different from the name used to read it: for example, the
-Python interpreter accessed as :file:`/usr/local/bin/python` might search for
-modules in :file:`/usr/local/lib/python2.{X}`, but those modules would have to
-be installed to, say, :file:`/mnt/{@server}/export/lib/python2.{X}`. This could
-be done with ::
-
- /usr/local/bin/python setup.py install --prefix=/mnt/@server/export
-
-In either case, the :option:`--prefix` option defines the installation base, and
-the :option:`--exec-prefix` option defines the platform-specific installation
-base, which is used for platform-specific files. (Currently, this just means
-non-pure module distributions, but could be expanded to C libraries, binary
-executables, etc.) If :option:`--exec-prefix` is not supplied, it defaults to
-:option:`--prefix`. Files are installed as follows:
-
-================= ==========================================================
-Type of file Installation directory
-================= ==========================================================
-Python modules :file:`{prefix}/lib/python{X.Y}/site-packages`
-extension modules :file:`{exec-prefix}/lib/python{X.Y}/site-packages`
-scripts :file:`{prefix}/bin`
-data :file:`{prefix}`
-C headers :file:`{prefix}/include/python{X.Y}{abiflags}/{distname}`
-================= ==========================================================
-
-There is no requirement that :option:`--prefix` or :option:`--exec-prefix`
-actually point to an alternate Python installation; if the directories listed
-above do not already exist, they are created at installation time.
-
-Incidentally, the real reason the prefix scheme is important is simply that a
-standard Unix installation uses the prefix scheme, but with :option:`--prefix`
-and :option:`--exec-prefix` supplied by Python itself as ``sys.prefix`` and
-``sys.exec_prefix``. Thus, you might think you'll never use the prefix scheme,
-but every time you run ``python setup.py install`` without any other options,
-you're using it.
-
-Note that installing extensions to an alternate Python installation has no
-effect on how those extensions are built: in particular, the Python header files
-(:file:`Python.h` and friends) installed with the Python interpreter used to run
-the setup script will be used in compiling extensions. It is your
-responsibility to ensure that the interpreter used to run extensions installed
-in this way is compatible with the interpreter used to build them. The best way
-to do this is to ensure that the two interpreters are the same version of Python
-(possibly different builds, or possibly copies of the same build). (Of course,
-if your :option:`--prefix` and :option:`--exec-prefix` don't even point to an
-alternate Python installation, this is immaterial.)
-
-
-.. _inst-alt-install-prefix-windows:
-
-Alternate installation: Windows (the prefix scheme)
----------------------------------------------------
-
-Windows has no concept of a user's home directory, and since the standard Python
-installation under Windows is simpler than under Unix, the :option:`--prefix`
-option has traditionally been used to install additional packages in separate
-locations on Windows. ::
-
- python setup.py install --prefix="\Temp\Python"
-
-to install modules to the :file:`\\Temp\\Python` directory on the current drive.
-
-The installation base is defined by the :option:`--prefix` option; the
-:option:`--exec-prefix` option is not supported under Windows, which means that
-pure Python modules and extension modules are installed into the same location.
-Files are installed as follows:
-
-=============== ==========================================================
-Type of file Installation directory
-=============== ==========================================================
-modules :file:`{prefix}\\Lib\\site-packages`
-scripts :file:`{prefix}\\Scripts`
-data :file:`{prefix}`
-C headers :file:`{prefix}\\Include\\{distname}`
-=============== ==========================================================
-
-
-.. _inst-custom-install:
-
-Custom Installation
-===================
-
-Sometimes, the alternate installation schemes described in section
-:ref:`inst-alt-install` just don't do what you want. You might want to tweak just
-one or two directories while keeping everything under the same base directory,
-or you might want to completely redefine the installation scheme. In either
-case, you're creating a *custom installation scheme*.
-
-To create a custom installation scheme, you start with one of the alternate
-schemes and override some of the installation directories used for the various
-types of files, using these options:
-
-====================== =======================
-Type of file Override option
-====================== =======================
-Python modules ``--install-purelib``
-extension modules ``--install-platlib``
-all modules ``--install-lib``
-scripts ``--install-scripts``
-data ``--install-data``
-C headers ``--install-headers``
-====================== =======================
-
-These override options can be relative, absolute,
-or explicitly defined in terms of one of the installation base directories.
-(There are two installation base directories, and they are normally the same---
-they only differ when you use the Unix "prefix scheme" and supply different
-``--prefix`` and ``--exec-prefix`` options; using ``--install-lib`` will
-override values computed or given for ``--install-purelib`` and
-``--install-platlib``, and is recommended for schemes that don't make a
-difference between Python and extension modules.)
-
-For example, say you're installing a module distribution to your home directory
-under Unix---but you want scripts to go in :file:`~/scripts` rather than
-:file:`~/bin`. As you might expect, you can override this directory with the
-:option:`--install-scripts` option; in this case, it makes most sense to supply
-a relative path, which will be interpreted relative to the installation base
-directory (your home directory, in this case)::
-
- python setup.py install --home=~ --install-scripts=scripts
-
-Another Unix example: suppose your Python installation was built and installed
-with a prefix of :file:`/usr/local/python`, so under a standard installation
-scripts will wind up in :file:`/usr/local/python/bin`. If you want them in
-:file:`/usr/local/bin` instead, you would supply this absolute directory for the
-:option:`--install-scripts` option::
-
- python setup.py install --install-scripts=/usr/local/bin
-
-(This performs an installation using the "prefix scheme," where the prefix is
-whatever your Python interpreter was installed with--- :file:`/usr/local/python`
-in this case.)
-
-If you maintain Python on Windows, you might want third-party modules to live in
-a subdirectory of :file:`{prefix}`, rather than right in :file:`{prefix}`
-itself. This is almost as easy as customizing the script installation directory
----you just have to remember that there are two types of modules to worry about,
-Python and extension modules, which can conveniently be both controlled by one
-option::
-
- python setup.py install --install-lib=Site
-
-The specified installation directory is relative to :file:`{prefix}`. Of
-course, you also have to ensure that this directory is in Python's module
-search path, such as by putting a :file:`.pth` file in a site directory (see
-:mod:`site`). See section :ref:`inst-search-path` to find out how to modify
-Python's search path.
-
-If you want to define an entire installation scheme, you just have to supply all
-of the installation directory options. The recommended way to do this is to
-supply relative paths; for example, if you want to maintain all Python
-module-related files under :file:`python` in your home directory, and you want a
-separate directory for each platform that you use your home directory from, you
-might define the following installation scheme::
+ This document describes Packaging from the end-user's point of view: it
+ explains how to extend the functionality of a standard Python installation by
+ building and installing third-party Python modules and applications.
- python setup.py install --home=~ \
- --install-purelib=python/lib \
- --install-platlib=python/lib.$PLAT \
- --install-scripts=python/scripts
- --install-data=python/data
-or, equivalently, ::
+This guide is split into a simple overview followed by a longer presentation of
+the :program:`pysetup` script, the Python package management tool used to
+build, distribute, search for, install, remove and list Python distributions.
- python setup.py install --home=~/python \
- --install-purelib=lib \
- --install-platlib='lib.$PLAT' \
- --install-scripts=scripts
- --install-data=data
+.. TODO integrate install and pysetup instead of duplicating
-``$PLAT`` is not (necessarily) an environment variable---it will be expanded by
-the Distutils as it parses your command line options, just as it does when
-parsing your configuration file(s).
+.. toctree::
+ :maxdepth: 2
+ :numbered:
-Obviously, specifying the entire installation scheme every time you install a
-new module distribution would be very tedious. Thus, you can put these options
-into your Distutils config file (see section :ref:`inst-config-files`)::
-
- [install]
- install-base=$HOME
- install-purelib=python/lib
- install-platlib=python/lib.$PLAT
- install-scripts=python/scripts
- install-data=python/data
-
-or, equivalently, ::
-
- [install]
- install-base=$HOME/python
- install-purelib=lib
- install-platlib=lib.$PLAT
- install-scripts=scripts
- install-data=data
-
-Note that these two are *not* equivalent if you supply a different installation
-base directory when you run the setup script. For example, ::
-
- python setup.py install --install-base=/tmp
-
-would install pure modules to :file:`/tmp/python/lib` in the first case, and
-to :file:`/tmp/lib` in the second case. (For the second case, you probably
-want to supply an installation base of :file:`/tmp/python`.)
-
-You probably noticed the use of ``$HOME`` and ``$PLAT`` in the sample
-configuration file input. These are Distutils configuration variables, which
-bear a strong resemblance to environment variables. In fact, you can use
-environment variables in config files on platforms that have such a notion but
-the Distutils additionally define a few extra variables that may not be in your
-environment, such as ``$PLAT``. (And of course, on systems that don't have
-environment variables, such as Mac OS 9, the configuration variables supplied by
-the Distutils are the only ones you can use.) See section :ref:`inst-config-files`
-for details.
-
-.. XXX need some Windows examples---when would custom installation schemes be
- needed on those platforms?
-
-
-.. XXX Move this to Doc/using
-
-.. _inst-search-path:
-
-Modifying Python's Search Path
-------------------------------
-
-When the Python interpreter executes an :keyword:`import` statement, it searches
-for both Python code and extension modules along a search path. A default value
-for the path is configured into the Python binary when the interpreter is built.
-You can determine the path by importing the :mod:`sys` module and printing the
-value of ``sys.path``. ::
-
- $ python
- Python 2.2 (#11, Oct 3 2002, 13:31:27)
- [GCC 2.96 20000731 (Red Hat Linux 7.3 2.96-112)] on linux2
- Type "help", "copyright", "credits" or "license" for more information.
- >>> import sys
- >>> sys.path
- ['', '/usr/local/lib/python2.3', '/usr/local/lib/python2.3/plat-linux2',
- '/usr/local/lib/python2.3/lib-tk', '/usr/local/lib/python2.3/lib-dynload',
- '/usr/local/lib/python2.3/site-packages']
- >>>
-
-The null string in ``sys.path`` represents the current working directory.
-
-The expected convention for locally installed packages is to put them in the
-:file:`{...}/site-packages/` directory, but you may want to install Python
-modules into some arbitrary directory. For example, your site may have a
-convention of keeping all software related to the web server under :file:`/www`.
-Add-on Python modules might then belong in :file:`/www/python`, and in order to
-import them, this directory must be added to ``sys.path``. There are several
-different ways to add the directory.
-
-The most convenient way is to add a path configuration file to a directory
-that's already on Python's path, usually to the :file:`.../site-packages/`
-directory. Path configuration files have an extension of :file:`.pth`, and each
-line must contain a single path that will be appended to ``sys.path``. (Because
-the new paths are appended to ``sys.path``, modules in the added directories
-will not override standard modules. This means you can't use this mechanism for
-installing fixed versions of standard modules.)
-
-Paths can be absolute or relative, in which case they're relative to the
-directory containing the :file:`.pth` file. See the documentation of
-the :mod:`site` module for more information.
-
-A slightly less convenient way is to edit the :file:`site.py` file in Python's
-standard library, and modify ``sys.path``. :file:`site.py` is automatically
-imported when the Python interpreter is executed, unless the :option:`-S` switch
-is supplied to suppress this behaviour. So you could simply edit
-:file:`site.py` and add two lines to it::
-
- import sys
- sys.path.append('/www/python/')
-
-However, if you reinstall the same major version of Python (perhaps when
-upgrading from 2.2 to 2.2.2, for example) :file:`site.py` will be overwritten by
-the stock version. You'd have to remember that it was modified and save a copy
-before doing the installation.
-
-There are two environment variables that can modify ``sys.path``.
-:envvar:`PYTHONHOME` sets an alternate value for the prefix of the Python
-installation. For example, if :envvar:`PYTHONHOME` is set to ``/www/python``,
-the search path will be set to ``['', '/www/python/lib/pythonX.Y/',
-'/www/python/lib/pythonX.Y/plat-linux2', ...]``.
-
-The :envvar:`PYTHONPATH` variable can be set to a list of paths that will be
-added to the beginning of ``sys.path``. For example, if :envvar:`PYTHONPATH` is
-set to ``/www/python:/opt/py``, the search path will begin with
-``['/www/python', '/opt/py']``. (Note that directories must exist in order to
-be added to ``sys.path``; the :mod:`site` module removes paths that don't
-exist.)
-
-Finally, ``sys.path`` is just a regular Python list, so any Python application
-can modify it by adding or removing entries.
-
-
-.. _inst-config-files:
-
-Distutils Configuration Files
-=============================
-
-As mentioned above, you can use Distutils configuration files to record personal
-or site preferences for any Distutils options. That is, any option to any
-command can be stored in one of two or three (depending on your platform)
-configuration files, which will be consulted before the command-line is parsed.
-This means that configuration files will override default values, and the
-command-line will in turn override configuration files. Furthermore, if
-multiple configuration files apply, values from "earlier" files are overridden
-by "later" files.
-
-
-.. _inst-config-filenames:
-
-Location and names of config files
-----------------------------------
-
-The names and locations of the configuration files vary slightly across
-platforms. On Unix and Mac OS X, the three configuration files (in the order
-they are processed) are:
-
-+--------------+----------------------------------------------------------+-------+
-| Type of file | Location and filename | Notes |
-+==============+==========================================================+=======+
-| system | :file:`{prefix}/lib/python{ver}/distutils/distutils.cfg` | \(1) |
-+--------------+----------------------------------------------------------+-------+
-| personal | :file:`$HOME/.pydistutils.cfg` | \(2) |
-+--------------+----------------------------------------------------------+-------+
-| local | :file:`setup.cfg` | \(3) |
-+--------------+----------------------------------------------------------+-------+
-
-And on Windows, the configuration files are:
-
-+--------------+-------------------------------------------------+-------+
-| Type of file | Location and filename | Notes |
-+==============+=================================================+=======+
-| system | :file:`{prefix}\\Lib\\distutils\\distutils.cfg` | \(4) |
-+--------------+-------------------------------------------------+-------+
-| personal | :file:`%HOME%\\pydistutils.cfg` | \(5) |
-+--------------+-------------------------------------------------+-------+
-| local | :file:`setup.cfg` | \(3) |
-+--------------+-------------------------------------------------+-------+
-
-On all platforms, the "personal" file can be temporarily disabled by
-passing the `--no-user-cfg` option.
-
-Notes:
-
-(1)
- Strictly speaking, the system-wide configuration file lives in the directory
- where the Distutils are installed; under Python 1.6 and later on Unix, this is
- as shown. For Python 1.5.2, the Distutils will normally be installed to
- :file:`{prefix}/lib/python1.5/site-packages/distutils`, so the system
- configuration file should be put there under Python 1.5.2.
-
-(2)
- On Unix, if the :envvar:`HOME` environment variable is not defined, the user's
- home directory will be determined with the :func:`getpwuid` function from the
- standard :mod:`pwd` module. This is done by the :func:`os.path.expanduser`
- function used by Distutils.
-
-(3)
- I.e., in the current directory (usually the location of the setup script).
-
-(4)
- (See also note (1).) Under Python 1.6 and later, Python's default "installation
- prefix" is :file:`C:\\Python`, so the system configuration file is normally
- :file:`C:\\Python\\Lib\\distutils\\distutils.cfg`. Under Python 1.5.2, the
- default prefix was :file:`C:\\Program Files\\Python`, and the Distutils were not
- part of the standard library---so the system configuration file would be
- :file:`C:\\Program Files\\Python\\distutils\\distutils.cfg` in a standard Python
- 1.5.2 installation under Windows.
-
-(5)
- On Windows, if the :envvar:`HOME` environment variable is not defined,
- :envvar:`USERPROFILE` then :envvar:`HOMEDRIVE` and :envvar:`HOMEPATH` will
- be tried. This is done by the :func:`os.path.expanduser` function used
- by Distutils.
-
-
-.. _inst-config-syntax:
-
-Syntax of config files
-----------------------
-
-The Distutils configuration files all have the same syntax. The config files
-are grouped into sections. There is one section for each Distutils command,
-plus a ``global`` section for global options that affect every command. Each
-section consists of one option per line, specified as ``option=value``.
-
-For example, the following is a complete config file that just forces all
-commands to run quietly by default::
-
- [global]
- verbose=0
-
-If this is installed as the system config file, it will affect all processing of
-any Python module distribution by any user on the current system. If it is
-installed as your personal config file (on systems that support them), it will
-affect only module distributions processed by you. And if it is used as the
-:file:`setup.cfg` for a particular module distribution, it affects only that
-distribution.
-
-You could override the default "build base" directory and make the
-:command:`build\*` commands always forcibly rebuild all files with the
-following::
-
- [build]
- build-base=blib
- force=1
-
-which corresponds to the command-line arguments ::
-
- python setup.py build --build-base=blib --force
-
-except that including the :command:`build` command on the command-line means
-that command will be run. Including a particular command in config files has no
-such implication; it only means that if the command is run, the options in the
-config file will apply. (Or if other commands that derive values from it are
-run, they will use the values in the config file.)
-
-You can find out the complete list of options for any command using the
-:option:`--help` option, e.g.::
-
- python setup.py build --help
-
-and you can find out the complete list of global options by using
-:option:`--help` without a command::
-
- python setup.py --help
-
-See also the "Reference" section of the "Distributing Python Modules" manual.
-
-
-.. _inst-building-ext:
-
-Building Extensions: Tips and Tricks
-====================================
-
-Whenever possible, the Distutils try to use the configuration information made
-available by the Python interpreter used to run the :file:`setup.py` script.
-For example, the same compiler and linker flags used to compile Python will also
-be used for compiling extensions. Usually this will work well, but in
-complicated situations this might be inappropriate. This section discusses how
-to override the usual Distutils behaviour.
-
-
-.. _inst-tweak-flags:
-
-Tweaking compiler/linker flags
-------------------------------
-
-Compiling a Python extension written in C or C++ will sometimes require
-specifying custom flags for the compiler and linker in order to use a particular
-library or produce a special kind of object code. This is especially true if the
-extension hasn't been tested on your platform, or if you're trying to
-cross-compile Python.
-
-In the most general case, the extension author might have foreseen that
-compiling the extensions would be complicated, and provided a :file:`Setup` file
-for you to edit. This will likely only be done if the module distribution
-contains many separate extension modules, or if they often require elaborate
-sets of compiler flags in order to work.
-
-A :file:`Setup` file, if present, is parsed in order to get a list of extensions
-to build. Each line in a :file:`Setup` describes a single module. Lines have
-the following structure::
-
- module ... [sourcefile ...] [cpparg ...] [library ...]
-
-
-Let's examine each of the fields in turn.
-
-* *module* is the name of the extension module to be built, and should be a
- valid Python identifier. You can't just change this in order to rename a module
- (edits to the source code would also be needed), so this should be left alone.
-
-* *sourcefile* is anything that's likely to be a source code file, at least
- judging by the filename. Filenames ending in :file:`.c` are assumed to be
- written in C, filenames ending in :file:`.C`, :file:`.cc`, and :file:`.c++` are
- assumed to be C++, and filenames ending in :file:`.m` or :file:`.mm` are assumed
- to be in Objective C.
-
-* *cpparg* is an argument for the C preprocessor, and is anything starting with
- :option:`-I`, :option:`-D`, :option:`-U` or :option:`-C`.
-
-* *library* is anything ending in :file:`.a` or beginning with :option:`-l` or
- :option:`-L`.
-
-If a particular platform requires a special library on your platform, you can
-add it by editing the :file:`Setup` file and running ``python setup.py build``.
-For example, if the module defined by the line ::
-
- foo foomodule.c
-
-must be linked with the math library :file:`libm.a` on your platform, simply add
-:option:`-lm` to the line::
-
- foo foomodule.c -lm
-
-Arbitrary switches intended for the compiler or the linker can be supplied with
-the :option:`-Xcompiler` *arg* and :option:`-Xlinker` *arg* options::
-
- foo foomodule.c -Xcompiler -o32 -Xlinker -shared -lm
-
-The next option after :option:`-Xcompiler` and :option:`-Xlinker` will be
-appended to the proper command line, so in the above example the compiler will
-be passed the :option:`-o32` option, and the linker will be passed
-:option:`-shared`. If a compiler option requires an argument, you'll have to
-supply multiple :option:`-Xcompiler` options; for example, to pass ``-x c++``
-the :file:`Setup` file would have to contain ``-Xcompiler -x -Xcompiler c++``.
-
-Compiler flags can also be supplied through setting the :envvar:`CFLAGS`
-environment variable. If set, the contents of :envvar:`CFLAGS` will be added to
-the compiler flags specified in the :file:`Setup` file.
-
-
-.. _inst-non-ms-compilers:
-
-Using non-Microsoft compilers on Windows
-----------------------------------------
-
-.. sectionauthor:: Rene Liebscher <R.Liebscher@gmx.de>
-
-
-
-Borland/CodeGear C++
-^^^^^^^^^^^^^^^^^^^^
-
-This subsection describes the necessary steps to use Distutils with the Borland
-C++ compiler version 5.5. First you have to know that Borland's object file
-format (OMF) is different from the format used by the Python version you can
-download from the Python or ActiveState Web site. (Python is built with
-Microsoft Visual C++, which uses COFF as the object file format.) For this
-reason you have to convert Python's library :file:`python25.lib` into the
-Borland format. You can do this as follows:
-
-.. Should we mention that users have to create cfg-files for the compiler?
-.. see also http://community.borland.com/article/0,1410,21205,00.html
-
-::
-
- coff2omf python25.lib python25_bcpp.lib
-
-The :file:`coff2omf` program comes with the Borland compiler. The file
-:file:`python25.lib` is in the :file:`Libs` directory of your Python
-installation. If your extension uses other libraries (zlib, ...) you have to
-convert them too.
-
-The converted files have to reside in the same directories as the normal
-libraries.
-
-How does Distutils manage to use these libraries with their changed names? If
-the extension needs a library (eg. :file:`foo`) Distutils checks first if it
-finds a library with suffix :file:`_bcpp` (eg. :file:`foo_bcpp.lib`) and then
-uses this library. In the case it doesn't find such a special library it uses
-the default name (:file:`foo.lib`.) [#]_
-
-To let Distutils compile your extension with Borland C++ you now have to type::
-
- python setup.py build --compiler=bcpp
-
-If you want to use the Borland C++ compiler as the default, you could specify
-this in your personal or system-wide configuration file for Distutils (see
-section :ref:`inst-config-files`.)
-
-
-.. seealso::
-
- `C++Builder Compiler <http://www.codegear.com/downloads/free/cppbuilder>`_
- Information about the free C++ compiler from Borland, including links to the
- download pages.
-
- `Creating Python Extensions Using Borland's Free Compiler <http://www.cyberus.ca/~g_will/pyExtenDL.shtml>`_
- Document describing how to use Borland's free command-line C++ compiler to build
- Python.
-
-
-GNU C / Cygwin / MinGW
-^^^^^^^^^^^^^^^^^^^^^^
-
-This section describes the necessary steps to use Distutils with the GNU C/C++
-compilers in their Cygwin and MinGW distributions. [#]_ For a Python interpreter
-that was built with Cygwin, everything should work without any of these
-following steps.
-
-Not all extensions can be built with MinGW or Cygwin, but many can. Extensions
-most likely to not work are those that use C++ or depend on Microsoft Visual C
-extensions.
-
-To let Distutils compile your extension with Cygwin you have to type::
-
- python setup.py build --compiler=cygwin
-
-and for Cygwin in no-cygwin mode [#]_ or for MinGW type::
-
- python setup.py build --compiler=mingw32
-
-If you want to use any of these options/compilers as default, you should
-consider writing it in your personal or system-wide configuration file for
-Distutils (see section :ref:`inst-config-files`.)
-
-Older Versions of Python and MinGW
-""""""""""""""""""""""""""""""""""
-The following instructions only apply if you're using a version of Python
-inferior to 2.4.1 with a MinGW inferior to 3.0.0 (with
-binutils-2.13.90-20030111-1).
-
-These compilers require some special libraries. This task is more complex than
-for Borland's C++, because there is no program to convert the library. First
-you have to create a list of symbols which the Python DLL exports. (You can find
-a good program for this task at
-http://www.emmestech.com/software/pexports-0.43/download_pexports.html).
-
-.. I don't understand what the next line means. --amk
-.. (inclusive the references on data structures.)
-
-::
-
- pexports python25.dll >python25.def
-
-The location of an installed :file:`python25.dll` will depend on the
-installation options and the version and language of Windows. In a "just for
-me" installation, it will appear in the root of the installation directory. In
-a shared installation, it will be located in the system directory.
-
-Then you can create from these information an import library for gcc. ::
-
- /cygwin/bin/dlltool --dllname python25.dll --def python25.def --output-lib libpython25.a
-
-The resulting library has to be placed in the same directory as
-:file:`python25.lib`. (Should be the :file:`libs` directory under your Python
-installation directory.)
-
-If your extension uses other libraries (zlib,...) you might have to convert
-them too. The converted files have to reside in the same directories as the
-normal libraries do.
+ install
+ pysetup
+ pysetup-config
+ pysetup-servers
.. seealso::
- `Building Python modules on MS Windows platform with MinGW <http://www.zope.org/Members/als/tips/win32_mingw_modules>`_
- Information about building the required libraries for the MinGW environment.
-
-
-.. rubric:: Footnotes
-
-.. [#] This also means you could replace all existing COFF-libraries with OMF-libraries
- of the same name.
-
-.. [#] Check http://sources.redhat.com/cygwin/ and http://www.mingw.org/ for more
- information
+ :ref:`packaging-index`
+ The manual for developers of Python projects who want to package and
+ distribute them. This describes how to use :mod:`packaging` to make
+ projects easily found and added to an existing Python installation.
-.. [#] Then you have no POSIX emulation available, but you also don't need
- :file:`cygwin1.dll`.
+ :mod:`packaging`
+ A library reference for developers of packaging tools wanting to use
+ standalone building blocks like :mod:`~packaging.version` or
+ :mod:`~packaging.metadata`, or extend Packaging itself.
diff --git a/Doc/install/install.rst b/Doc/install/install.rst
new file mode 100644
index 0000000..b88d3a6
--- /dev/null
+++ b/Doc/install/install.rst
@@ -0,0 +1,1119 @@
+.. highlightlang:: none
+
+====================================
+Installing Python projects: overwiew
+====================================
+
+.. _packaging-install-intro:
+
+Introduction
+============
+
+Although Python's extensive standard library covers many programming needs,
+there often comes a time when you need to add new functionality to your Python
+installation in the form of third-party modules. This might be necessary to
+support your own programming, or to support an application that you want to use
+and that happens to be written in Python.
+
+In the past, there was little support for adding third-party modules to an
+existing Python installation. With the introduction of the Python Distribution
+Utilities (Distutils for short) in Python 2.0, this changed. However, not all
+problems were solved; end-users had to rely on ``easy_install`` or
+``pip`` to download third-party modules from PyPI, uninstall distributions or do
+other maintenance operations. Packaging is a more complete replacement for
+Distutils, in the standard library, with a backport named Distutils2 available
+for older Python versions.
+
+This document is aimed primarily at people who need to install third-party
+Python modules: end-users and system administrators who just need to get some
+Python application running, and existing Python programmers who want to add
+new goodies to their toolbox. You don't need to know Python to read this
+document; there will be some brief forays into using Python's interactive mode
+to explore your installation, but that's it. If you're looking for information
+on how to distribute your own Python modules so that others may use them, see
+the :ref:`packaging-index` manual.
+
+
+.. _packaging-trivial-install:
+
+Best case: trivial installation
+-------------------------------
+
+In the best case, someone will have prepared a special version of the module
+distribution you want to install that is targeted specifically at your platform
+and can be installed just like any other software on your platform. For example,
+the module's developer might make an executable installer available for Windows
+users, an RPM package for users of RPM-based Linux systems (Red Hat, SuSE,
+Mandrake, and many others), a Debian package for users of Debian and derivative
+systems, and so forth.
+
+In that case, you would use the standard system tools to download and install
+the specific installer for your platform and its dependencies.
+
+Of course, things will not always be that easy. You might be interested in a
+module whose distribution doesn't have an easy-to-use installer for your
+platform. In that case, you'll have to start with the source distribution
+released by the module's author/maintainer. Installing from a source
+distribution is not too hard, as long as the modules are packaged in the
+standard way. The bulk of this document addresses the building and installing
+of modules from standard source distributions.
+
+
+.. _packaging-distutils:
+
+The Python standard: Distutils
+------------------------------
+
+If you download a source distribution of a module, it will be obvious whether
+it was packaged and distributed using Distutils. First, the distribution's name
+and version number will be featured prominently in the name of the downloaded
+archive, e.g. :file:`foo-1.0.tar.gz` or :file:`widget-0.9.7.zip`. Next, the
+archive will unpack into a similarly-named directory: :file:`foo-1.0` or
+:file:`widget-0.9.7`. Additionally, the distribution may contain a
+:file:`setup.cfg` file and a file named :file:`README.txt` ---or possibly just
+:file:`README`--- explaining that building and installing the module
+distribution is a simple matter of issuing the following command at your shell's
+prompt::
+
+ python setup.py install
+
+Third-party projects have extended Distutils to work around its limitations or
+add functionality. After some years of near-inactivity in Distutils, a new
+maintainer has started to standardize good ideas in PEPs and implement them in a
+new, improved version of Distutils, called Distutils2 or Packaging.
+
+
+.. _packaging-new-standard:
+
+The new standard: Packaging
+---------------------------
+
+The rules described in the first paragraph above apply to Packaging-based
+projects too: a source distribution will have a name like
+:file:`widget-0.9.7.zip`. One of the main differences with Distutils is that
+distributions no longer have a :file:`setup.py` script; it used to cause a
+number of issues. Now there is a unique script installed with Python itself::
+
+ pysetup install widget-0.9.7.zip
+
+Running this command is enough to build and install projects (Python modules or
+packages, scripts or whole applications), without even having to unpack the
+archive. It is also compatible with Distutils-based distributions.
+
+Unless you have to perform non-standard installations or customize the build
+process, you can stop reading this manual ---the above command is everything you
+need to get out of it.
+
+With :program:`pysetup`, you won't even have to manually download a distribution
+before installing it; see :ref:`packaging-pysetup`.
+
+
+.. _packaging-standard-install:
+
+Standard build and install
+==========================
+
+As described in section :ref:`packaging-new-standard`, building and installing
+a module distribution using Packaging usually comes down to one simple
+command::
+
+ pysetup run install_dist
+
+This is a command that should be run in a terminal. On Windows, it is called a
+command prompt and found in :menuselection:`Start --> Accessories`; Powershell
+is a popular alternative.
+
+
+.. _packaging-platform-variations:
+
+Platform variations
+-------------------
+
+The setup command is meant to be run from the root directory of the source
+distribution, i.e. the top-level subdirectory that the module source
+distribution unpacks into. For example, if you've just downloaded a module
+source distribution :file:`foo-1.0.tar.gz` onto a Unix system, the normal
+steps to follow are these::
+
+ gunzip -c foo-1.0.tar.gz | tar xf - # unpacks into directory foo-1.0
+ cd foo-1.0
+ pysetup run install_dist
+
+On Windows, you'd probably download :file:`foo-1.0.zip`. If you downloaded the
+archive file to :file:`C:\\Temp`, then it would unpack into
+:file:`C:\\Temp\\foo-1.0`. To actually unpack the archive, you can use either
+an archive manipulator with a graphical user interface (such as WinZip or 7-Zip)
+or a command-line tool (such as :program:`unzip`, :program:`pkunzip` or, again,
+:program:`7z`). Then, open a command prompt window and run::
+
+ cd c:\Temp\foo-1.0
+ pysetup run install_dist
+
+
+.. _packaging-splitting-up:
+
+Splitting the job up
+--------------------
+
+Running ``pysetup run install_dist`` builds and installs all modules in one go. If you
+prefer to work incrementally ---especially useful if you want to customize the
+build process, or if things are going wrong--- you can use the setup script to
+do one thing at a time. This is a valuable tool when different users will perform
+separately the build and install steps. For example, you might want to build a
+module distribution and hand it off to a system administrator for installation
+(or do it yourself, but with super-user or admin privileges).
+
+For example, to build everything in one step and then install everything
+in a second step, you aptly invoke two distinct Packaging commands::
+
+ pysetup run build
+ pysetup run install_dist
+
+If you do this, you will notice that invoking the :command:`install_dist` command
+first runs the :command:`build` command, which ---in this case--- quickly
+notices it can spare itself the work, since everything in the :file:`build`
+directory is up-to-date.
+
+You may often ignore this ability to divide the process in steps if all you do
+is installing modules downloaded from the Internet, but it's very handy for
+more advanced tasks. If you find yourself in the need for distributing your own
+Python modules and extensions, though, you'll most likely run many individual
+Packaging commands.
+
+
+.. _packaging-how-build-works:
+
+How building works
+------------------
+
+As implied above, the :command:`build` command is responsible for collecting
+and placing the files to be installed into a *build directory*. By default,
+this is :file:`build`, under the distribution root. If you're excessively
+concerned with speed, or want to keep the source tree pristine, you can specify
+a different build directory with the :option:`--build-base` option. For example::
+
+ pysetup run build --build-base /tmp/pybuild/foo-1.0
+
+(Or you could do this permanently with a directive in your system or personal
+Packaging configuration file; see section :ref:`packaging-config-files`.)
+In the usual case, however, all this is unnecessary.
+
+The build tree's default layout looks like so::
+
+ --- build/ --- lib/
+ or
+ --- build/ --- lib.<plat>/
+ temp.<plat>/
+
+where ``<plat>`` expands to a brief description of the current OS/hardware
+platform and Python version. The first form, with just a :file:`lib` directory,
+is used for pure module distributions (module distributions that
+include only pure Python modules). If a module distribution contains any
+extensions (modules written in C/C++), then the second form, with two ``<plat>``
+directories, is used. In that case, the :file:`temp.{plat}` directory holds
+temporary files generated during the compile/link process which are not intended
+to be installed. In either case, the :file:`lib` (or :file:`lib.{plat}`) directory
+contains all Python modules (pure Python and extensions) to be installed.
+
+In the future, more directories will be added to handle Python scripts,
+documentation, binary executables, and whatever else is required to install
+Python modules and applications.
+
+
+.. _packaging-how-install-works:
+
+How installation works
+----------------------
+
+After the :command:`build` command is run (whether explicitly or by the
+:command:`install_dist` command on your behalf), the work of the :command:`install_dist`
+command is relatively simple: all it has to do is copy the contents of
+:file:`build/lib` (or :file:`build/lib.{plat}`) to the installation directory
+of your choice.
+
+If you don't choose an installation directory ---i.e., if you just run
+``pysetup run install_dist``\ --- then the :command:`install_dist` command
+installs to the standard location for third-party Python modules. This location
+varies by platform and depending on how you built/installed Python itself. On
+Unix (and Mac OS X, which is also Unix-based), it also depends on whether the
+module distribution being installed is pure Python or contains extensions
+("non-pure"):
+
++-----------------+-----------------------------------------------------+--------------------------------------------------+-------+
+| Platform | Standard installation location | Default value | Notes |
++=================+=====================================================+==================================================+=======+
+| Unix (pure) | :file:`{prefix}/lib/python{X.Y}/site-packages` | :file:`/usr/local/lib/python{X.Y}/site-packages` | \(1) |
++-----------------+-----------------------------------------------------+--------------------------------------------------+-------+
+| Unix (non-pure) | :file:`{exec-prefix}/lib/python{X.Y}/site-packages` | :file:`/usr/local/lib/python{X.Y}/site-packages` | \(1) |
++-----------------+-----------------------------------------------------+--------------------------------------------------+-------+
+| Windows | :file:`{prefix}\\Lib\\site-packages` | :file:`C:\\Python{XY}\\Lib\\site-packages` | \(2) |
++-----------------+-----------------------------------------------------+--------------------------------------------------+-------+
+
+Notes:
+
+(1)
+ Most Linux distributions include Python as a standard part of the system, so
+ :file:`{prefix}` and :file:`{exec-prefix}` are usually both :file:`/usr` on
+ Linux. If you build Python yourself on Linux (or any Unix-like system), the
+ default :file:`{prefix}` and :file:`{exec-prefix}` are :file:`/usr/local`.
+
+(2)
+ The default installation directory on Windows was :file:`C:\\Program
+ Files\\Python` under Python 1.6a1, 1.5.2, and earlier.
+
+:file:`{prefix}` and :file:`{exec-prefix}` stand for the directories that Python
+is installed to, and where it finds its libraries at run-time. They are always
+the same under Windows, and very often the same under Unix and Mac OS X. You
+can find out what your Python installation uses for :file:`{prefix}` and
+:file:`{exec-prefix}` by running Python in interactive mode and typing a few
+simple commands.
+
+.. TODO link to Doc/using instead of duplicating
+
+To start the interactive Python interpreter, you need to follow a slightly
+different recipe for each platform. Under Unix, just type :command:`python` at
+the shell prompt. Under Windows (assuming the Python executable is on your
+:envvar:`PATH`, which is the usual case), you can choose :menuselection:`Start --> Run`,
+type ``python`` and press ``enter``. Alternatively, you can simply execute
+:command:`python` at a command prompt (:menuselection:`Start --> Accessories`)
+or in Powershell.
+
+Once the interpreter is started, you type Python code at the prompt. For
+example, on my Linux system, I type the three Python statements shown below,
+and get the output as shown, to find out my :file:`{prefix}` and :file:`{exec-prefix}`::
+
+ Python 3.3 (r32:88445, Apr 2 2011, 10:43:54)
+ Type "help", "copyright", "credits" or "license" for more information.
+ >>> import sys
+ >>> sys.prefix
+ '/usr'
+ >>> sys.exec_prefix
+ '/usr'
+
+A few other placeholders are used in this document: :file:`{X.Y}` stands for the
+version of Python, for example ``3.2``; :file:`{abiflags}` will be replaced by
+the value of :data:`sys.abiflags` or the empty string for platforms which don't
+define ABI flags; :file:`{distname}` will be replaced by the name of the module
+distribution being installed. Dots and capitalization are important in the
+paths; for example, a value that uses ``python3.2`` on UNIX will typically use
+``Python32`` on Windows.
+
+If you don't want to install modules to the standard location, or if you don't
+have permission to write there, then you need to read about alternate
+installations in section :ref:`packaging-alt-install`. If you want to customize your
+installation directories more heavily, see section :ref:`packaging-custom-install`.
+
+
+.. _packaging-alt-install:
+
+Alternate installation
+======================
+
+Often, it is necessary or desirable to install modules to a location other than
+the standard location for third-party Python modules. For example, on a Unix
+system you might not have permission to write to the standard third-party module
+directory. Or you might wish to try out a module before making it a standard
+part of your local Python installation. This is especially true when upgrading
+a distribution already present: you want to make sure your existing base of
+scripts still works with the new version before actually upgrading.
+
+The Packaging :command:`install_dist` command is designed to make installing module
+distributions to an alternate location simple and painless. The basic idea is
+that you supply a base directory for the installation, and the
+:command:`install_dist` command picks a set of directories (called an *installation
+scheme*) under this base directory in which to install files. The details
+differ across platforms, so read whichever of the following sections applies to
+you.
+
+Note that the various alternate installation schemes are mutually exclusive: you
+can pass ``--user``, or ``--home``, or ``--prefix`` and ``--exec-prefix``, or
+``--install-base`` and ``--install-platbase``, but you can't mix from these
+groups.
+
+
+.. _packaging-alt-install-user:
+
+Alternate installation: the user scheme
+---------------------------------------
+
+This scheme is designed to be the most convenient solution for users that don't
+have write permission to the global site-packages directory or don't want to
+install into it. It is enabled with a simple option::
+
+ pysetup run install_dist --user
+
+Files will be installed into subdirectories of :data:`site.USER_BASE` (written
+as :file:`{userbase}` hereafter). This scheme installs pure Python modules and
+extension modules in the same location (also known as :data:`site.USER_SITE`).
+Here are the values for UNIX, including non-framework builds on Mac OS X:
+
+=============== ===========================================================
+Type of file Installation directory
+=============== ===========================================================
+modules :file:`{userbase}/lib/python{X.Y}/site-packages`
+scripts :file:`{userbase}/bin`
+data :file:`{userbase}`
+C headers :file:`{userbase}/include/python{X.Y}`
+=============== ===========================================================
+
+Framework builds on Mac OS X use these paths:
+
+=============== ===========================================================
+Type of file Installation directory
+=============== ===========================================================
+modules :file:`{userbase}/lib/python/site-packages`
+scripts :file:`{userbase}/bin`
+data :file:`{userbase}`
+C headers :file:`{userbase}/include/python`
+=============== ===========================================================
+
+And here are the values used on Windows:
+
+=============== ===========================================================
+Type of file Installation directory
+=============== ===========================================================
+modules :file:`{userbase}\\Python{XY}\\site-packages`
+scripts :file:`{userbase}\\Scripts`
+data :file:`{userbase}`
+C headers :file:`{userbase}\\Python{XY}\\Include`
+=============== ===========================================================
+
+The advantage of using this scheme compared to the other ones described below is
+that the user site-packages directory is under normal conditions always included
+in :data:`sys.path` (see :mod:`site` for more information), which means that
+there is no additional step to perform after running ``pysetup`` to finalize the
+installation.
+
+The :command:`build_ext` command also has a ``--user`` option to add
+:file:`{userbase}/include` to the compiler search path for header files and
+:file:`{userbase}/lib` to the compiler search path for libraries as well as to
+the runtime search path for shared C libraries (rpath).
+
+
+.. _packaging-alt-install-home:
+
+Alternate installation: the home scheme
+---------------------------------------
+
+The idea behind the "home scheme" is that you build and maintain a personal
+stash of Python modules. This scheme's name is derived from the concept of a
+"home" directory on Unix, since it's not unusual for a Unix user to make their
+home directory have a layout similar to :file:`/usr/` or :file:`/usr/local/`.
+In spite of its name's origin, this scheme can be used by anyone, regardless
+of the operating system.
+
+Installing a new module distribution in this way is as simple as ::
+
+ pysetup run install_dist --home <dir>
+
+where you can supply any directory you like for the :option:`--home` option. On
+Unix, lazy typists can just type a tilde (``~``); the :command:`install_dist` command
+will expand this to your home directory::
+
+ pysetup run install_dist --home ~
+
+To make Python find the distributions installed with this scheme, you may have
+to :ref:`modify Python's search path <inst-search-path>` or edit
+:mod:`sitecustomize` (see :mod:`site`) to call :func:`site.addsitedir` or edit
+:data:`sys.path`.
+
+The :option:`--home` option defines the base directory for the installation.
+Under it, files are installed to the following directories:
+
+=============== ===========================================================
+Type of file Installation directory
+=============== ===========================================================
+modules :file:`{home}/lib/python`
+scripts :file:`{home}/bin`
+data :file:`{home}`
+C headers :file:`{home}/include/python`
+=============== ===========================================================
+
+(Mentally replace slashes with backslashes if you're on Windows.)
+
+
+.. _packaging-alt-install-prefix-unix:
+
+Alternate installation: Unix (the prefix scheme)
+------------------------------------------------
+
+The "prefix scheme" is useful when you wish to use one Python installation to
+run the build command, but install modules into the third-party module directory
+of a different Python installation (or something that looks like a different
+Python installation). If this sounds a trifle unusual, it is ---that's why the
+user and home schemes come before. However, there are at least two known cases
+where the prefix scheme will be useful.
+
+First, consider that many Linux distributions put Python in :file:`/usr`, rather
+than the more traditional :file:`/usr/local`. This is entirely appropriate,
+since in those cases Python is part of "the system" rather than a local add-on.
+However, if you are installing Python modules from source, you probably want
+them to go in :file:`/usr/local/lib/python2.{X}` rather than
+:file:`/usr/lib/python2.{X}`. This can be done with ::
+
+ pysetup run install_dist --prefix /usr/local
+
+Another possibility is a network filesystem where the name used to write to a
+remote directory is different from the name used to read it: for example, the
+Python interpreter accessed as :file:`/usr/local/bin/python` might search for
+modules in :file:`/usr/local/lib/python2.{X}`, but those modules would have to
+be installed to, say, :file:`/mnt/{@server}/export/lib/python2.{X}`. This could
+be done with ::
+
+ pysetup run install_dist --prefix=/mnt/@server/export
+
+In either case, the :option:`--prefix` option defines the installation base, and
+the :option:`--exec-prefix` option defines the platform-specific installation
+base, which is used for platform-specific files. (Currently, this just means
+non-pure module distributions, but could be expanded to C libraries, binary
+executables, etc.) If :option:`--exec-prefix` is not supplied, it defaults to
+:option:`--prefix`. Files are installed as follows:
+
+================= ==========================================================
+Type of file Installation directory
+================= ==========================================================
+Python modules :file:`{prefix}/lib/python{X.Y}/site-packages`
+extension modules :file:`{exec-prefix}/lib/python{X.Y}/site-packages`
+scripts :file:`{prefix}/bin`
+data :file:`{prefix}`
+C headers :file:`{prefix}/include/python{X.Y}{abiflags}`
+================= ==========================================================
+
+.. XXX misses an entry for platinclude
+
+There is no requirement that :option:`--prefix` or :option:`--exec-prefix`
+actually point to an alternate Python installation; if the directories listed
+above do not already exist, they are created at installation time.
+
+Incidentally, the real reason the prefix scheme is important is simply that a
+standard Unix installation uses the prefix scheme, but with :option:`--prefix`
+and :option:`--exec-prefix` supplied by Python itself as ``sys.prefix`` and
+``sys.exec_prefix``. Thus, you might think you'll never use the prefix scheme,
+but every time you run ``pysetup run install_dist`` without any other
+options, you're using it.
+
+Note that installing extensions to an alternate Python installation doesn't have
+anything to do with how those extensions are built: in particular, extensions
+will be compiled using the Python header files (:file:`Python.h` and friends)
+installed with the Python interpreter used to run the build command. It is
+therefore your responsibility to ensure compatibility between the interpreter
+intended to run extensions installed in this way and the interpreter used to
+build these same extensions. To avoid problems, it is best to make sure that
+the two interpreters are the same version of Python (possibly different builds,
+or possibly copies of the same build). (Of course, if your :option:`--prefix`
+and :option:`--exec-prefix` don't even point to an alternate Python installation,
+this is immaterial.)
+
+
+.. _packaging-alt-install-prefix-windows:
+
+Alternate installation: Windows (the prefix scheme)
+---------------------------------------------------
+
+Windows has a different and vaguer notion of home directories than Unix, and
+since its standard Python installation is simpler, the :option:`--prefix` option
+has traditionally been used to install additional packages to arbitrary
+locations. ::
+
+ pysetup run install_dist --prefix "\Temp\Python"
+
+to install modules to the :file:`\\Temp\\Python` directory on the current drive.
+
+The installation base is defined by the :option:`--prefix` option; the
+:option:`--exec-prefix` option is not supported under Windows, which means that
+pure Python modules and extension modules are installed into the same location.
+Files are installed as follows:
+
+=============== ==========================================================
+Type of file Installation directory
+=============== ==========================================================
+modules :file:`{prefix}\\Lib\\site-packages`
+scripts :file:`{prefix}\\Scripts`
+data :file:`{prefix}`
+C headers :file:`{prefix}\\Include`
+=============== ==========================================================
+
+
+.. _packaging-custom-install:
+
+Custom installation
+===================
+
+Sometimes, the alternate installation schemes described in section
+:ref:`packaging-alt-install` just don't do what you want. You might want to tweak
+just one or two directories while keeping everything under the same base
+directory, or you might want to completely redefine the installation scheme.
+In either case, you're creating a *custom installation scheme*.
+
+To create a custom installation scheme, you start with one of the alternate
+schemes and override some of the installation directories used for the various
+types of files, using these options:
+
+====================== =======================
+Type of file Override option
+====================== =======================
+Python modules ``--install-purelib``
+extension modules ``--install-platlib``
+all modules ``--install-lib``
+scripts ``--install-scripts``
+data ``--install-data``
+C headers ``--install-headers``
+====================== =======================
+
+These override options can be relative, absolute,
+or explicitly defined in terms of one of the installation base directories.
+(There are two installation base directories, and they are normally the same
+---they only differ when you use the Unix "prefix scheme" and supply different
+``--prefix`` and ``--exec-prefix`` options; using ``--install-lib`` will
+override values computed or given for ``--install-purelib`` and
+``--install-platlib``, and is recommended for schemes that don't make a
+difference between Python and extension modules.)
+
+For example, say you're installing a module distribution to your home directory
+under Unix, but you want scripts to go in :file:`~/scripts` rather than
+:file:`~/bin`. As you might expect, you can override this directory with the
+:option:`--install-scripts` option and, in this case, it makes most sense to supply
+a relative path, which will be interpreted relative to the installation base
+directory (in our example, your home directory)::
+
+ pysetup run install_dist --home ~ --install-scripts scripts
+
+Another Unix example: suppose your Python installation was built and installed
+with a prefix of :file:`/usr/local/python`. Thus, in a standard installation,
+scripts will wind up in :file:`/usr/local/python/bin`. If you want them in
+:file:`/usr/local/bin` instead, you would supply this absolute directory for
+the :option:`--install-scripts` option::
+
+ pysetup run install_dist --install-scripts /usr/local/bin
+
+This command performs an installation using the "prefix scheme", where the
+prefix is whatever your Python interpreter was installed with ---in this case,
+:file:`/usr/local/python`.
+
+If you maintain Python on Windows, you might want third-party modules to live in
+a subdirectory of :file:`{prefix}`, rather than right in :file:`{prefix}`
+itself. This is almost as easy as customizing the script installation directory
+---you just have to remember that there are two types of modules to worry about,
+Python and extension modules, which can conveniently be both controlled by one
+option::
+
+ pysetup run install_dist --install-lib Site
+
+.. XXX Nothing is installed right under prefix in windows, is it??
+
+The specified installation directory is relative to :file:`{prefix}`. Of
+course, you also have to ensure that this directory is in Python's module
+search path, such as by putting a :file:`.pth` file in a site directory (see
+:mod:`site`). See section :ref:`packaging-search-path` to find out how to modify
+Python's search path.
+
+If you want to define an entire installation scheme, you just have to supply all
+of the installation directory options. Using relative paths is recommended here.
+For example, if you want to maintain all Python module-related files under
+:file:`python` in your home directory, and you want a separate directory for
+each platform that you use your home directory from, you might define the
+following installation scheme::
+
+ pysetup run install_dist --home ~ \
+ --install-purelib python/lib \
+ --install-platlib python/'lib.$PLAT' \
+ --install-scripts python/scripts \
+ --install-data python/data
+
+or, equivalently, ::
+
+ pysetup run install_dist --home ~/python \
+ --install-purelib lib \
+ --install-platlib 'lib.$PLAT' \
+ --install-scripts scripts \
+ --install-data data
+
+``$PLAT`` doesn't need to be defined as an environment variable ---it will also
+be expanded by Packaging as it parses your command line options, just as it
+does when parsing your configuration file(s). (More on that later.)
+
+Obviously, specifying the entire installation scheme every time you install a
+new module distribution would be very tedious. To spare you all that work, you
+can store it in a Packaging configuration file instead (see section
+:ref:`packaging-config-files`), like so::
+
+ [install_dist]
+ install-base = $HOME
+ install-purelib = python/lib
+ install-platlib = python/lib.$PLAT
+ install-scripts = python/scripts
+ install-data = python/data
+
+or, equivalently, ::
+
+ [install_dist]
+ install-base = $HOME/python
+ install-purelib = lib
+ install-platlib = lib.$PLAT
+ install-scripts = scripts
+ install-data = data
+
+Note that these two are *not* equivalent if you override their installation
+base directory when running the setup script. For example, ::
+
+ pysetup run install_dist --install-base /tmp
+
+would install pure modules to :file:`/tmp/python/lib` in the first case, and
+to :file:`/tmp/lib` in the second case. (For the second case, you'd probably
+want to supply an installation base of :file:`/tmp/python`.)
+
+You may have noticed the use of ``$HOME`` and ``$PLAT`` in the sample
+configuration file. These are Packaging configuration variables, which
+bear a strong resemblance to environment variables. In fact, you can use
+environment variables in configuration files on platforms that have such a notion, but
+Packaging additionally defines a few extra variables that may not be in your
+environment, such as ``$PLAT``. Of course, on systems that don't have
+environment variables, such as Mac OS 9, the configuration variables supplied by
+the Packaging are the only ones you can use. See section :ref:`packaging-config-files`
+for details.
+
+.. XXX which vars win out eventually in case of clash env or Packaging?
+
+.. XXX need some Windows examples---when would custom installation schemes be
+ needed on those platforms?
+
+
+.. XXX Move this section to Doc/using
+
+.. _packaging-search-path:
+
+Modifying Python's search path
+------------------------------
+
+When the Python interpreter executes an :keyword:`import` statement, it searches
+for both Python code and extension modules along a search path. A default value
+for this path is configured into the Python binary when the interpreter is built.
+You can obtain the search path by importing the :mod:`sys` module and printing
+the value of ``sys.path``. ::
+
+ $ python
+ Python 2.2 (#11, Oct 3 2002, 13:31:27)
+ [GCC 2.96 20000731 (Red Hat Linux 7.3 2.96-112)] on linux2
+ Type "help", "copyright", "credits" or "license" for more information.
+ >>> import sys
+ >>> sys.path
+ ['', '/usr/local/lib/python2.3', '/usr/local/lib/python2.3/plat-linux2',
+ '/usr/local/lib/python2.3/lib-tk', '/usr/local/lib/python2.3/lib-dynload',
+ '/usr/local/lib/python2.3/site-packages']
+ >>>
+
+The null string in ``sys.path`` represents the current working directory.
+
+The expected convention for locally installed packages is to put them in the
+:file:`{...}/site-packages/` directory, but you may want to choose a different
+location for some reason. For example, if your site kept by convention all web
+server-related software under :file:`/www`. Add-on Python modules might then
+belong in :file:`/www/python`, and in order to import them, this directory would
+have to be added to ``sys.path``. There are several ways to solve this problem.
+
+The most convenient way is to add a path configuration file to a directory
+that's already on Python's path, usually to the :file:`.../site-packages/`
+directory. Path configuration files have an extension of :file:`.pth`, and each
+line must contain a single path that will be appended to ``sys.path``. (Because
+the new paths are appended to ``sys.path``, modules in the added directories
+will not override standard modules. This means you can't use this mechanism for
+installing fixed versions of standard modules.)
+
+Paths can be absolute or relative, in which case they're relative to the
+directory containing the :file:`.pth` file. See the documentation of
+the :mod:`site` module for more information.
+
+A slightly less convenient way is to edit the :file:`site.py` file in Python's
+standard library, and modify ``sys.path``. :file:`site.py` is automatically
+imported when the Python interpreter is executed, unless the :option:`-S` switch
+is supplied to suppress this behaviour. So you could simply edit
+:file:`site.py` and add two lines to it::
+
+ import sys
+ sys.path.append('/www/python/')
+
+However, if you reinstall the same major version of Python (perhaps when
+upgrading from 3.3 to 3.3.1, for example) :file:`site.py` will be overwritten by
+the stock version. You'd have to remember that it was modified and save a copy
+before doing the installation.
+
+Alternatively, there are two environment variables that can modify ``sys.path``.
+:envvar:`PYTHONHOME` sets an alternate value for the prefix of the Python
+installation. For example, if :envvar:`PYTHONHOME` is set to ``/www/python``,
+the search path will be set to ``['', '/www/python/lib/pythonX.Y/',
+'/www/python/lib/pythonX.Y/plat-linux2', ...]``.
+
+The :envvar:`PYTHONPATH` variable can be set to a list of paths that will be
+added to the beginning of ``sys.path``. For example, if :envvar:`PYTHONPATH` is
+set to ``/www/python:/opt/py``, the search path will begin with
+``['/www/python', '/opt/py']``. (Note that directories must exist in order to
+be added to ``sys.path``; the :mod:`site` module removes non-existent paths.)
+
+Finally, ``sys.path`` is just a regular Python list, so any Python application
+can modify it by adding or removing entries.
+
+
+.. _packaging-config-files:
+
+Configuration files for Packaging
+=================================
+
+As mentioned above, you can use configuration files to store personal or site
+preferences for any option supported by any Packaging command. Depending on your
+platform, you can use one of two or three possible configuration files. These
+files will be read before parsing the command-line, so they take precedence over
+default values. In turn, the command-line will override configuration files.
+Lastly, if there are multiple configuration files, values from files read
+earlier will be overridden by values from files read later.
+
+.. XXX "one of two or three possible..." seems wrong info. Below always 3 files
+ are indicated in the tables.
+
+
+.. _packaging-config-filenames:
+
+Location and names of configuration files
+-----------------------------------------
+
+The name and location of the configuration files vary slightly across
+platforms. On Unix and Mac OS X, these are the three configuration files listed
+in the order they are processed:
+
++--------------+----------------------------------------------------------+-------+
+| Type of file | Location and filename | Notes |
++==============+==========================================================+=======+
+| system | :file:`{prefix}/lib/python{ver}/packaging/packaging.cfg` | \(1) |
++--------------+----------------------------------------------------------+-------+
+| personal | :file:`$HOME/.pydistutils.cfg` | \(2) |
++--------------+----------------------------------------------------------+-------+
+| local | :file:`setup.cfg` | \(3) |
++--------------+----------------------------------------------------------+-------+
+
+Similarly, the configuration files on Windows ---also listed in the order they
+are processed--- are these:
+
++--------------+-------------------------------------------------+-------+
+| Type of file | Location and filename | Notes |
++==============+=================================================+=======+
+| system | :file:`{prefix}\\Lib\\packaging\\packaging.cfg` | \(4) |
++--------------+-------------------------------------------------+-------+
+| personal | :file:`%HOME%\\pydistutils.cfg` | \(5) |
++--------------+-------------------------------------------------+-------+
+| local | :file:`setup.cfg` | \(3) |
++--------------+-------------------------------------------------+-------+
+
+On all platforms, the *personal* file can be temporarily disabled by
+means of the `--no-user-cfg` option.
+
+Notes:
+
+(1)
+ Strictly speaking, the system-wide configuration file lives in the directory
+ where Packaging is installed.
+
+(2)
+ On Unix, if the :envvar:`HOME` environment variable is not defined, the
+ user's home directory will be determined with the :func:`getpwuid` function
+ from the standard :mod:`pwd` module. Packaging uses the
+ :func:`os.path.expanduser` function to do this.
+
+(3)
+ I.e., in the current directory (usually the location of the setup script).
+
+(4)
+ (See also note (1).) Python's default installation prefix is
+ :file:`C:\\Python`, so the system configuration file is normally
+ :file:`C:\\Python\\Lib\\packaging\\packaging.cfg`.
+
+(5)
+ On Windows, if the :envvar:`HOME` environment variable is not defined,
+ :envvar:`USERPROFILE` then :envvar:`HOMEDRIVE` and :envvar:`HOMEPATH` will
+ be tried. Packaging uses the :func:`os.path.expanduser` function to do this.
+
+
+.. _packaging-config-syntax:
+
+Syntax of configuration files
+-----------------------------
+
+All Packaging configuration files share the same syntax. Options defined in
+them are grouped into sections, and each Packaging command gets its own section.
+Additionally, there's a ``global`` section for options that affect every command.
+Sections consist of one or more lines containing a single option specified as
+``option = value``.
+
+.. XXX use dry-run in the next example or use a pysetup option as example
+
+For example, here's a complete configuration file that forces all commands to
+run quietly by default::
+
+ [global]
+ verbose = 0
+
+If this was the system configuration file, it would affect all processing
+of any Python module distribution by any user on the current system. If it was
+installed as your personal configuration file (on systems that support them),
+it would affect only module distributions processed by you. Lastly, if it was
+used as the :file:`setup.cfg` for a particular module distribution, it would
+affect that distribution only.
+
+.. XXX "(on systems that support them)" seems wrong info
+
+If you wanted to, you could override the default "build base" directory and
+make the :command:`build\*` commands always forcibly rebuild all files with
+the following::
+
+ [build]
+ build-base = blib
+ force = 1
+
+which corresponds to the command-line arguments::
+
+ pysetup run build --build-base blib --force
+
+except that including the :command:`build` command on the command-line means
+that command will be run. Including a particular command in configuration files
+has no such implication; it only means that if the command is run, the options
+for it in the configuration file will apply. (This is also true if you run
+other commands that derive values from it.)
+
+You can find out the complete list of options for any command using the
+:option:`--help` option, e.g.::
+
+ pysetup run build --help
+
+and you can find out the complete list of global options by using
+:option:`--help` without a command::
+
+ pysetup run --help
+
+See also the "Reference" section of the "Distributing Python Modules" manual.
+
+.. XXX no links to the relevant section exist.
+
+
+.. _packaging-building-ext:
+
+Building extensions: tips and tricks
+====================================
+
+Whenever possible, Packaging tries to use the configuration information made
+available by the Python interpreter used to run `pysetup`.
+For example, the same compiler and linker flags used to compile Python will also
+be used for compiling extensions. Usually this will work well, but in
+complicated situations this might be inappropriate. This section discusses how
+to override the usual Packaging behaviour.
+
+
+.. _packaging-tweak-flags:
+
+Tweaking compiler/linker flags
+------------------------------
+
+Compiling a Python extension written in C or C++ will sometimes require
+specifying custom flags for the compiler and linker in order to use a particular
+library or produce a special kind of object code. This is especially true if the
+extension hasn't been tested on your platform, or if you're trying to
+cross-compile Python.
+
+.. TODO update to new setup.cfg
+
+In the most general case, the extension author might have foreseen that
+compiling the extensions would be complicated, and provided a :file:`Setup` file
+for you to edit. This will likely only be done if the module distribution
+contains many separate extension modules, or if they often require elaborate
+sets of compiler flags in order to work.
+
+A :file:`Setup` file, if present, is parsed in order to get a list of extensions
+to build. Each line in a :file:`Setup` describes a single module. Lines have
+the following structure::
+
+ module ... [sourcefile ...] [cpparg ...] [library ...]
+
+
+Let's examine each of the fields in turn.
+
+* *module* is the name of the extension module to be built, and should be a
+ valid Python identifier. You can't just change this in order to rename a module
+ (edits to the source code would also be needed), so this should be left alone.
+
+* *sourcefile* is anything that's likely to be a source code file, at least
+ judging by the filename. Filenames ending in :file:`.c` are assumed to be
+ written in C, filenames ending in :file:`.C`, :file:`.cc`, and :file:`.c++` are
+ assumed to be C++, and filenames ending in :file:`.m` or :file:`.mm` are assumed
+ to be in Objective C.
+
+* *cpparg* is an argument for the C preprocessor, and is anything starting with
+ :option:`-I`, :option:`-D`, :option:`-U` or :option:`-C`.
+
+* *library* is anything ending in :file:`.a` or beginning with :option:`-l` or
+ :option:`-L`.
+
+If a particular platform requires a special library on your platform, you can
+add it by editing the :file:`Setup` file and running ``pysetup run build``.
+For example, if the module defined by the line ::
+
+ foo foomodule.c
+
+must be linked with the math library :file:`libm.a` on your platform, simply add
+:option:`-lm` to the line::
+
+ foo foomodule.c -lm
+
+Arbitrary switches intended for the compiler or the linker can be supplied with
+the :option:`-Xcompiler` *arg* and :option:`-Xlinker` *arg* options::
+
+ foo foomodule.c -Xcompiler -o32 -Xlinker -shared -lm
+
+The next option after :option:`-Xcompiler` and :option:`-Xlinker` will be
+appended to the proper command line, so in the above example the compiler will
+be passed the :option:`-o32` option, and the linker will be passed
+:option:`-shared`. If a compiler option requires an argument, you'll have to
+supply multiple :option:`-Xcompiler` options; for example, to pass ``-x c++``
+the :file:`Setup` file would have to contain ``-Xcompiler -x -Xcompiler c++``.
+
+Compiler flags can also be supplied through setting the :envvar:`CFLAGS`
+environment variable. If set, the contents of :envvar:`CFLAGS` will be added to
+the compiler flags specified in the :file:`Setup` file.
+
+
+.. _packaging-non-ms-compilers:
+
+Using non-Microsoft compilers on Windows
+----------------------------------------
+
+.. sectionauthor:: Rene Liebscher <R.Liebscher@gmx.de>
+
+
+
+Borland/CodeGear C++
+^^^^^^^^^^^^^^^^^^^^
+
+This subsection describes the necessary steps to use Packaging with the Borland
+C++ compiler version 5.5. First you have to know that Borland's object file
+format (OMF) is different from the format used by the Python version you can
+download from the Python or ActiveState Web site. (Python is built with
+Microsoft Visual C++, which uses COFF as the object file format.) For this
+reason, you have to convert Python's library :file:`python25.lib` into the
+Borland format. You can do this as follows:
+
+.. Should we mention that users have to create cfg-files for the compiler?
+.. see also http://community.borland.com/article/0,1410,21205,00.html
+
+::
+
+ coff2omf python25.lib python25_bcpp.lib
+
+The :file:`coff2omf` program comes with the Borland compiler. The file
+:file:`python25.lib` is in the :file:`Libs` directory of your Python
+installation. If your extension uses other libraries (zlib, ...) you have to
+convert them too.
+
+The converted files have to reside in the same directories as the normal
+libraries.
+
+How does Packaging manage to use these libraries with their changed names? If
+the extension needs a library (eg. :file:`foo`) Packaging checks first if it
+finds a library with suffix :file:`_bcpp` (eg. :file:`foo_bcpp.lib`) and then
+uses this library. In the case it doesn't find such a special library it uses
+the default name (:file:`foo.lib`.) [#]_
+
+To let Packaging compile your extension with Borland, C++ you now have to
+type::
+
+ pysetup run build --compiler bcpp
+
+If you want to use the Borland C++ compiler as the default, you could specify
+this in your personal or system-wide configuration file for Packaging (see
+section :ref:`packaging-config-files`.)
+
+
+.. seealso::
+
+ `C++Builder Compiler <http://www.codegear.com/downloads/free/cppbuilder>`_
+ Information about the free C++ compiler from Borland, including links to the
+ download pages.
+
+ `Creating Python Extensions Using Borland's Free Compiler <http://www.cyberus.ca/~g_will/pyExtenDL.shtml>`_
+ Document describing how to use Borland's free command-line C++ compiler to build
+ Python.
+
+
+GNU C / Cygwin / MinGW
+^^^^^^^^^^^^^^^^^^^^^^
+
+This section describes the necessary steps to use Packaging with the GNU C/C++
+compilers in their Cygwin and MinGW distributions. [#]_ For a Python interpreter
+that was built with Cygwin, everything should work without any of these
+following steps.
+
+Not all extensions can be built with MinGW or Cygwin, but many can. Extensions
+most likely to not work are those that use C++ or depend on Microsoft Visual C
+extensions.
+
+To let Packaging compile your extension with Cygwin, you have to type::
+
+ pysetup run build --compiler=cygwin
+
+and for Cygwin in no-cygwin mode [#]_ or for MinGW, type::
+
+ pysetup run build --compiler=mingw32
+
+If you want to use any of these options/compilers as default, you should
+consider writing it in your personal or system-wide configuration file for
+Packaging (see section :ref:`packaging-config-files`.)
+
+Older Versions of Python and MinGW
+""""""""""""""""""""""""""""""""""
+The following instructions only apply if you're using a version of Python
+inferior to 2.4.1 with a MinGW inferior to 3.0.0 (with
+:file:`binutils-2.13.90-20030111-1`).
+
+These compilers require some special libraries. This task is more complex than
+for Borland's C++, because there is no program to convert the library. First
+you have to create a list of symbols which the Python DLL exports. (You can find
+a good program for this task at
+http://www.emmestech.com/software/pexports-0.43/download_pexports.html).
+
+.. I don't understand what the next line means. --amk
+ (inclusive the references on data structures.)
+
+::
+
+ pexports python25.dll > python25.def
+
+The location of an installed :file:`python25.dll` will depend on the
+installation options and the version and language of Windows. In a "just for
+me" installation, it will appear in the root of the installation directory. In
+a shared installation, it will be located in the system directory.
+
+Then you can create from these information an import library for gcc. ::
+
+ /cygwin/bin/dlltool --dllname python25.dll --def python25.def --output-lib libpython25.a
+
+The resulting library has to be placed in the same directory as
+:file:`python25.lib`. (Should be the :file:`libs` directory under your Python
+installation directory.)
+
+If your extension uses other libraries (zlib,...) you might have to convert
+them too. The converted files have to reside in the same directories as the
+normal libraries do.
+
+
+.. seealso::
+
+ `Building Python modules on MS Windows platform with MinGW <http://www.zope.org/Members/als/tips/win32_mingw_modules>`_
+ Information about building the required libraries for the MinGW
+ environment.
+
+
+.. rubric:: Footnotes
+
+.. [#] This also means you could replace all existing COFF-libraries with
+ OMF-libraries of the same name.
+
+.. [#] Check http://sources.redhat.com/cygwin/ and http://www.mingw.org/ for
+ more information.
+
+.. [#] Then you have no POSIX emulation available, but you also don't need
+ :file:`cygwin1.dll`.
diff --git a/Doc/install/pysetup-config.rst b/Doc/install/pysetup-config.rst
new file mode 100644
index 0000000..a473bfe
--- /dev/null
+++ b/Doc/install/pysetup-config.rst
@@ -0,0 +1,44 @@
+.. _packaging-pysetup-config:
+
+=====================
+Pysetup Configuration
+=====================
+
+Pysetup supports two configuration files: :file:`.pypirc` and :file:`packaging.cfg`.
+
+.. FIXME integrate with configfile instead of duplicating
+
+Configuring indexes
+-------------------
+
+You can configure additional indexes in :file:`.pypirc` to be used for index-related
+operations. By default, all configured index-servers and package-servers will be used
+in an additive fashion. To limit operations to specific indexes, use the :option:`--index`
+and :option:`--package-server options`::
+
+ $ pysetup install --index pypi --package-server django some.project
+
+Adding indexes to :file:`.pypirc`::
+
+ [packaging]
+ index-servers =
+ pypi
+ other
+
+ package-servers =
+ django
+
+ [pypi]
+ repository: <repository-url>
+ username: <username>
+ password: <password>
+
+ [other]
+ repository: <repository-url>
+ username: <username>
+ password: <password>
+
+ [django]
+ repository: <repository-url>
+ username: <username>
+ password: <password>
diff --git a/Doc/install/pysetup-servers.rst b/Doc/install/pysetup-servers.rst
new file mode 100644
index 0000000..c6106de
--- /dev/null
+++ b/Doc/install/pysetup-servers.rst
@@ -0,0 +1,61 @@
+.. _packaging-pysetup-servers:
+
+===============
+Package Servers
+===============
+
+Pysetup supports installing Python packages from *Package Servers* in addition
+to PyPI indexes and mirrors.
+
+Package Servers are simple directory listings of Python distributions. Directories
+can be served via HTTP or a local file system. This is useful when you want to
+dump source distributions in a directory and not worry about the full index structure.
+
+Serving distributions from Apache
+---------------------------------
+::
+
+ $ mkdir -p /var/www/html/python/distributions
+ $ cp *.tar.gz /var/www/html/python/distributions/
+
+ <VirtualHost python.example.org:80>
+ ServerAdmin webmaster@domain.com
+ DocumentRoot "/var/www/html/python"
+ ServerName python.example.org
+ ErrorLog logs/python.example.org-error.log
+ CustomLog logs/python.example.org-access.log common
+ Options Indexes FollowSymLinks MultiViews
+ DirectoryIndex index.html index.htm
+
+ <Directory "/var/www/html/python/distributions">
+ Options Indexes FollowSymLinks MultiViews
+ Order allow,deny
+ Allow from all
+ </Directory>
+ </VirtualHost>
+
+Add the Apache based distribution server to :file:`.pypirc`::
+
+ [packaging]
+ package-servers =
+ apache
+
+ [apache]
+ repository: http://python.example.org/distributions/
+
+
+Serving distributions from a file system
+----------------------------------------
+::
+
+ $ mkdir -p /data/python/distributions
+ $ cp *.tar.gz /data/python/distributions/
+
+Add the directory to :file:`.pypirc`::
+
+ [packaging]
+ package-servers =
+ local
+
+ [local]
+ repository: file:///data/python/distributions/
diff --git a/Doc/install/pysetup.rst b/Doc/install/pysetup.rst
new file mode 100644
index 0000000..d472c24
--- /dev/null
+++ b/Doc/install/pysetup.rst
@@ -0,0 +1,164 @@
+.. _packaging-pysetup:
+
+================
+Pysetup Tutorial
+================
+
+Getting started
+---------------
+
+Pysetup is a simple script that supports the following features:
+
+- install, remove, list, and verify Python packages;
+- search for available packages on PyPI or any *Simple Index*;
+- verify installed packages (md5sum, installed files, version).
+
+
+Finding out what's installed
+----------------------------
+
+Pysetup makes it easy to find out what Python packages are installed::
+
+ $ pysetup list virtualenv
+ 'virtualenv' 1.6 at '/opt/python3.3/lib/python3.3/site-packages/virtualenv-1.6-py3.3.egg-info'
+
+ $ pysetup list
+ 'pyverify' 0.8.1 at '/opt/python3.3/lib/python3.3/site-packages/pyverify-0.8.1.dist-info'
+ 'virtualenv' 1.6 at '/opt/python3.3/lib/python3.3/site-packages/virtualenv-1.6-py3.3.egg-info'
+ ...
+
+
+Installing a distribution
+-------------------------
+
+Pysetup can install a Python project from the following sources:
+
+- PyPI and Simple Indexes;
+- source directories containing a valid :file:`setup.py` or :file:`setup.cfg`;
+- distribution source archives (:file:`project-1.0.tar.gz`, :file:`project-1.0.zip`);
+- HTTP (http://host/packages/project-1.0.tar.gz).
+
+
+Installing from PyPI and Simple Indexes::
+
+ $ pysetup install project
+ $ pysetup install project==1.0
+
+Installing from a distribution source archive::
+
+ $ pysetup install project-1.0.tar.gz
+
+Installing from a source directory containing a valid :file:`setup.py` or
+:file:`setup.cfg`::
+
+ $ cd path/to/source/directory
+ $ pysetup install
+
+ $ pysetup install path/to/source/directory
+
+Installing from HTTP::
+
+ $ pysetup install http://host/packages/project-1.0.tar.gz
+
+
+Retrieving metadata
+-------------------
+
+You can gather metadata from two sources, a project's source directory or an
+installed distribution. The `pysetup metadata` command can retrieve one or
+more metadata fields using the `-f` option and a metadata field as the
+argument. ::
+
+ $ pysetup metadata virtualenv -f version -f name
+ Version:
+ 1.6
+ Name:
+ virtualenv
+
+ $ pysetup metadata virtualenv
+ Metadata-Version:
+ 1.0
+ Name:
+ virtualenv
+ Version:
+ 1.6
+ Platform:
+ UNKNOWN
+ Summary:
+ Virtual Python Environment builder
+ ...
+
+.. seealso::
+
+ There are three metadata versions, 1.0, 1.1, and 1.2. The following PEPs
+ describe specifics of the field names, and their semantics and usage. 1.0
+ :PEP:`241`, 1.1 :PEP:`314`, and 1.2 :PEP:`345`
+
+
+Removing a distribution
+-----------------------
+
+You can remove one or more installed distributions using the `pysetup remove`
+command::
+
+ $ pysetup remove virtualenv
+ removing 'virtualenv':
+ /opt/python3.3/lib/python3.3/site-packages/virtualenv-1.6-py3.3.egg-info/dependency_links.txt
+ /opt/python3.3/lib/python3.3/site-packages/virtualenv-1.6-py3.3.egg-info/entry_points.txt
+ /opt/python3.3/lib/python3.3/site-packages/virtualenv-1.6-py3.3.egg-info/not-zip-safe
+ /opt/python3.3/lib/python3.3/site-packages/virtualenv-1.6-py3.3.egg-info/PKG-INFO
+ /opt/python3.3/lib/python3.3/site-packages/virtualenv-1.6-py3.3.egg-info/SOURCES.txt
+ /opt/python3.3/lib/python3.3/site-packages/virtualenv-1.6-py3.3.egg-info/top_level.txt
+ Proceed (y/n)? y
+ success: removed 6 files and 1 dirs
+
+The optional '-y' argument auto confirms, skipping the conformation prompt::
+
+ $ pysetup remove virtualenv -y
+
+
+Getting help
+------------
+
+All pysetup actions take the `-h` and `--help` options which prints the commands
+help string to stdout. ::
+
+ $ pysetup remove -h
+ Usage: pysetup remove dist [-y]
+ or: pysetup remove --help
+
+ Uninstall a Python package.
+
+ positional arguments:
+ dist installed distribution name
+
+ optional arguments:
+ -y auto confirm package removal
+
+Getting a list of all pysetup actions and global options::
+
+ $ pysetup --help
+ Usage: pysetup [options] action [action_options]
+
+ Actions:
+ run: Run one or several commands
+ metadata: Display the metadata of a project
+ install: Install a project
+ remove: Remove a project
+ search: Search for a project in the indexes
+ list: List installed projects
+ graph: Display a graph
+ create: Create a project
+ generate-setup: Generate a backward-compatible setup.py
+
+ To get more help on an action, use:
+
+ pysetup action --help
+
+ Global options:
+ --verbose (-v) run verbosely (default)
+ --quiet (-q) run quietly (turns verbosity off)
+ --dry-run (-n) don't actually do anything
+ --help (-h) show detailed help message
+ --no-user-cfg ignore pydistutils.cfg in your home directory
+ --version Display the version
diff --git a/Doc/library/_thread.rst b/Doc/library/_thread.rst
index 369e9cd..e7e7504 100644
--- a/Doc/library/_thread.rst
+++ b/Doc/library/_thread.rst
@@ -35,6 +35,9 @@ It defines the following constants and functions:
Raised on thread-specific errors.
+ .. versionchanged:: 3.3
+ This is now a synonym of the built-in :exc:`RuntimeError`.
+
.. data:: LockType
diff --git a/Doc/library/abc.rst b/Doc/library/abc.rst
index 1048b24..6f42222 100644
--- a/Doc/library/abc.rst
+++ b/Doc/library/abc.rst
@@ -18,7 +18,7 @@ regarding a type hierarchy for numbers based on ABCs.)
The :mod:`collections` module has some concrete classes that derive from
ABCs; these can, of course, be further derived. In addition the
-:mod:`collections` module has some ABCs that can be used to test whether
+:mod:`collections.abc` submodule has some ABCs that can be used to test whether
a class or instance provides a particular interface, for example, is it
hashable or a mapping.
@@ -55,6 +55,9 @@ This module provides the following class:
assert issubclass(tuple, MyABC)
assert isinstance((), MyABC)
+ .. versionchanged:: 3.3
+ Returns the registered subclass, to allow usage as a class decorator.
+
You can also override this method in an abstract base class:
.. method:: __subclasshook__(subclass)
@@ -124,19 +127,18 @@ This module provides the following class:
available as a method of ``Foo``, so it is provided separately.
-It also provides the following decorators:
+The :mod:`abc` module also provides the following decorators:
.. decorator:: abstractmethod(function)
A decorator indicating abstract methods.
- Using this decorator requires that the class's metaclass is :class:`ABCMeta` or
- is derived from it.
- A class that has a metaclass derived from :class:`ABCMeta`
- cannot be instantiated unless all of its abstract methods and
- properties are overridden.
- The abstract methods can be called using any of the normal 'super' call
- mechanisms.
+ Using this decorator requires that the class's metaclass is :class:`ABCMeta`
+ or is derived from it. A class that has a metaclass derived from
+ :class:`ABCMeta` cannot be instantiated unless all of its abstract methods
+ and properties are overridden. The abstract methods can be called using any
+ of the normal 'super' call mechanisms. :func:`abstractmethod` may be used
+ to declare abstract methods for properties and descriptors.
Dynamically adding abstract methods to a class, or attempting to modify the
abstraction status of a method or class once it is created, are not
@@ -144,12 +146,52 @@ It also provides the following decorators:
regular inheritance; "virtual subclasses" registered with the ABC's
:meth:`register` method are not affected.
- Usage::
+ When :func:`abstractmethod` is applied in combination with other method
+ descriptors, it should be applied as the innermost decorator, as shown in
+ the following usage examples::
class C(metaclass=ABCMeta):
@abstractmethod
def my_abstract_method(self, ...):
...
+ @classmethod
+ @abstractmethod
+ def my_abstract_classmethod(cls, ...):
+ ...
+ @staticmethod
+ @abstractmethod
+ def my_abstract_staticmethod(...):
+ ...
+
+ @property
+ @abstractmethod
+ def my_abstract_property(self):
+ ...
+ @my_abstract_property.setter
+ @abstractmethod
+ def my_abstract_property(self, val):
+ ...
+
+ @abstractmethod
+ def _get_x(self):
+ ...
+ @abstractmethod
+ def _set_x(self, val):
+ ...
+ x = property(_get_x, _set_x)
+
+ In order to correctly interoperate with the abstract base class machinery,
+ the descriptor must identify itself as abstract using
+ :attr:`__isabstractmethod__`. In general, this attribute should be ``True``
+ if any of the methods used to compose the descriptor are abstract. For
+ example, Python's built-in property does the equivalent of::
+
+ class Descriptor:
+ ...
+ @property
+ def __isabstractmethod__(self):
+ return any(getattr(f, '__isabstractmethod__', False) for
+ f in (self._fget, self._fset, self._fdel))
.. note::
@@ -174,6 +216,8 @@ It also provides the following decorators:
...
.. versionadded:: 3.2
+ .. deprecated:: 3.3
+ Use :class:`classmethod` with :func:`abstractmethod` instead
.. decorator:: abstractstaticmethod(function)
@@ -189,18 +233,19 @@ It also provides the following decorators:
...
.. versionadded:: 3.2
+ .. deprecated:: 3.3
+ Use :class:`staticmethod` with :func:`abstractmethod` instead
-.. function:: abstractproperty(fget=None, fset=None, fdel=None, doc=None)
+.. decorator:: abstractproperty(fget=None, fset=None, fdel=None, doc=None)
A subclass of the built-in :func:`property`, indicating an abstract property.
- Using this function requires that the class's metaclass is :class:`ABCMeta` or
- is derived from it.
- A class that has a metaclass derived from :class:`ABCMeta` cannot be
- instantiated unless all of its abstract methods and properties are overridden.
- The abstract properties can be called using any of the normal
- 'super' call mechanisms.
+ Using this function requires that the class's metaclass is :class:`ABCMeta`
+ or is derived from it. A class that has a metaclass derived from
+ :class:`ABCMeta` cannot be instantiated unless all of its abstract methods
+ and properties are overridden. The abstract properties can be called using
+ any of the normal 'super' call mechanisms.
Usage::
@@ -217,6 +262,9 @@ It also provides the following decorators:
def setx(self, value): ...
x = abstractproperty(getx, setx)
+ .. deprecated:: 3.3
+ Use :class:`property` with :func:`abstractmethod` instead
+
.. rubric:: Footnotes
diff --git a/Doc/library/archiving.rst b/Doc/library/archiving.rst
index f2d08ad..b7d400c 100644
--- a/Doc/library/archiving.rst
+++ b/Doc/library/archiving.rst
@@ -5,7 +5,8 @@ Data Compression and Archiving
******************************
The modules described in this chapter support data compression with the zlib,
-gzip, and bzip2 algorithms, and the creation of ZIP- and tar-format archives.
+gzip, bzip2 and lzma algorithms, and the creation of ZIP- and tar-format
+archives.
.. toctree::
@@ -13,5 +14,6 @@ gzip, and bzip2 algorithms, and the creation of ZIP- and tar-format archives.
zlib.rst
gzip.rst
bz2.rst
+ lzma.rst
zipfile.rst
tarfile.rst
diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst
index 305a546..0d6925d 100644
--- a/Doc/library/argparse.rst
+++ b/Doc/library/argparse.rst
@@ -362,16 +362,16 @@ formatter_class
^^^^^^^^^^^^^^^
:class:`ArgumentParser` objects allow the help formatting to be customized by
-specifying an alternate formatting class. Currently, there are three such
+specifying an alternate formatting class. Currently, there are four such
classes:
.. class:: RawDescriptionHelpFormatter
RawTextHelpFormatter
ArgumentDefaultsHelpFormatter
+ MetavarTypeHelpFormatter
-The first two allow more control over how textual descriptions are displayed,
-while the last automatically adds information about argument default values.
-
+:class:`RawDescriptionHelpFormatter` and :class:`RawTextHelpFormatter` give
+more control over how textual descriptions are displayed.
By default, :class:`ArgumentParser` objects line-wrap the description_ and
epilog_ texts in command-line help messages::
@@ -424,8 +424,8 @@ should not be line-wrapped::
:class:`RawTextHelpFormatter` maintains whitespace for all sorts of help text,
including argument descriptions.
-The other formatter class available, :class:`ArgumentDefaultsHelpFormatter`,
-will add information about the default value of each of the arguments::
+:class:`ArgumentDefaultsHelpFormatter` automatically adds information about
+default values to each of the argument help messages::
>>> parser = argparse.ArgumentParser(
... prog='PROG',
@@ -442,6 +442,25 @@ will add information about the default value of each of the arguments::
-h, --help show this help message and exit
--foo FOO FOO! (default: 42)
+:class:`MetavarTypeHelpFormatter` uses the name of the type_ argument for each
+argument as the display name for its values (rather than using the dest_
+as the regular formatter does)::
+
+ >>> parser = argparse.ArgumentParser(
+ ... prog='PROG',
+ ... formatter_class=argparse.MetavarTypeHelpFormatter)
+ >>> parser.add_argument('--foo', type=int)
+ >>> parser.add_argument('bar', type=float)
+ >>> parser.print_help()
+ usage: PROG [-h] [--foo int] float
+
+ positional arguments:
+ float
+
+ optional arguments:
+ -h, --help show this help message and exit
+ --foo int
+
conflict_handler
^^^^^^^^^^^^^^^^
diff --git a/Doc/library/array.rst b/Doc/library/array.rst
index d563cce..3e275a2 100644
--- a/Doc/library/array.rst
+++ b/Doc/library/array.rst
@@ -14,36 +14,44 @@ them is constrained. The type is specified at object creation time by using a
:dfn:`type code`, which is a single character. The following type codes are
defined:
-+-----------+----------------+-------------------+-----------------------+
-| Type code | C Type | Python Type | Minimum size in bytes |
-+===========+================+===================+=======================+
-| ``'b'`` | signed char | int | 1 |
-+-----------+----------------+-------------------+-----------------------+
-| ``'B'`` | unsigned char | int | 1 |
-+-----------+----------------+-------------------+-----------------------+
-| ``'u'`` | Py_UNICODE | Unicode character | 2 (see note) |
-+-----------+----------------+-------------------+-----------------------+
-| ``'h'`` | signed short | int | 2 |
-+-----------+----------------+-------------------+-----------------------+
-| ``'H'`` | unsigned short | int | 2 |
-+-----------+----------------+-------------------+-----------------------+
-| ``'i'`` | signed int | int | 2 |
-+-----------+----------------+-------------------+-----------------------+
-| ``'I'`` | unsigned int | int | 2 |
-+-----------+----------------+-------------------+-----------------------+
-| ``'l'`` | signed long | int | 4 |
-+-----------+----------------+-------------------+-----------------------+
-| ``'L'`` | unsigned long | int | 4 |
-+-----------+----------------+-------------------+-----------------------+
-| ``'f'`` | float | float | 4 |
-+-----------+----------------+-------------------+-----------------------+
-| ``'d'`` | double | float | 8 |
-+-----------+----------------+-------------------+-----------------------+
-
-.. note::
-
- The ``'u'`` typecode corresponds to Python's unicode character. On narrow
- Unicode builds this is 2-bytes, on wide builds this is 4-bytes.
++-----------+--------------------+-------------------+-----------------------+-------+
+| Type code | C Type | Python Type | Minimum size in bytes | Notes |
++===========+====================+===================+=======================+=======+
+| ``'b'`` | signed char | int | 1 | |
++-----------+--------------------+-------------------+-----------------------+-------+
+| ``'B'`` | unsigned char | int | 1 | |
++-----------+--------------------+-------------------+-----------------------+-------+
+| ``'u'`` | Py_UCS4 | Unicode character | 4 | |
++-----------+--------------------+-------------------+-----------------------+-------+
+| ``'h'`` | signed short | int | 2 | |
++-----------+--------------------+-------------------+-----------------------+-------+
+| ``'H'`` | unsigned short | int | 2 | |
++-----------+--------------------+-------------------+-----------------------+-------+
+| ``'i'`` | signed int | int | 2 | |
++-----------+--------------------+-------------------+-----------------------+-------+
+| ``'I'`` | unsigned int | int | 2 | |
++-----------+--------------------+-------------------+-----------------------+-------+
+| ``'l'`` | signed long | int | 4 | |
++-----------+--------------------+-------------------+-----------------------+-------+
+| ``'L'`` | unsigned long | int | 4 | |
++-----------+--------------------+-------------------+-----------------------+-------+
+| ``'q'`` | signed long long | int | 8 | \(1) |
++-----------+--------------------+-------------------+-----------------------+-------+
+| ``'Q'`` | unsigned long long | int | 8 | \(1) |
++-----------+--------------------+-------------------+-----------------------+-------+
+| ``'f'`` | float | float | 4 | |
++-----------+--------------------+-------------------+-----------------------+-------+
+| ``'d'`` | double | float | 8 | |
++-----------+--------------------+-------------------+-----------------------+-------+
+
+Notes:
+
+(1)
+ The ``'q'`` and ``'Q'`` type codes are available only if
+ the platform C compiler used to build Python supports C :c:type:`long long`,
+ or, on Windows, :c:type:`__int64`.
+
+ .. versionadded:: 3.3
The actual representation of values is determined by the machine architecture
(strictly speaking, by the C implementation). The actual size can be accessed
diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst
index e2c0b6d..16de3ca 100644
--- a/Doc/library/ast.rst
+++ b/Doc/library/ast.rst
@@ -96,9 +96,6 @@ Node classes
Abstract Grammar
----------------
-The module defines a string constant ``__version__`` which is the decimal
-Subversion revision number of the file shown below.
-
The abstract grammar is currently defined as follows:
.. literalinclude:: ../../Parser/Python.asdl
diff --git a/Doc/library/asyncore.rst b/Doc/library/asyncore.rst
index 619b7bb..5411c30 100644
--- a/Doc/library/asyncore.rst
+++ b/Doc/library/asyncore.rst
@@ -184,12 +184,14 @@ any that have been added to the map during asynchronous service) is closed.
Most of these are nearly identical to their socket partners.
- .. method:: create_socket(family, type)
+ .. method:: create_socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
This is identical to the creation of a normal socket, and will use the
same options for creation. Refer to the :mod:`socket` documentation for
information on creating sockets.
+ .. versionchanged:: 3.3 family and type arguments can be omitted.
+
.. method:: connect(address)
@@ -280,7 +282,7 @@ implement its socket handling::
def __init__(self, host, path):
asyncore.dispatcher.__init__(self)
- self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.create_socket()
self.connect( (host, 80) )
self.buffer = bytes('GET %s HTTP/1.0\r\nHost: %s\r\n\r\n' %
(path, host), 'ascii')
@@ -327,7 +329,7 @@ connections and dispatches the incoming connections to a handler::
def __init__(self, host, port):
asyncore.dispatcher.__init__(self)
- self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.create_socket()
self.set_reuse_addr()
self.bind((host, port))
self.listen(5)
diff --git a/Doc/library/atexit.rst b/Doc/library/atexit.rst
index f2dccc2..54131f5 100644
--- a/Doc/library/atexit.rst
+++ b/Doc/library/atexit.rst
@@ -67,8 +67,9 @@ automatically when the program terminates without relying on the application
making an explicit call into this module at termination. ::
try:
- _count = int(open("/tmp/counter").read())
- except IOError:
+ with open("/tmp/counter") as infile:
+ _count = int(infile.read())
+ except FileNotFoundError:
_count = 0
def incrcounter(n):
@@ -76,7 +77,8 @@ making an explicit call into this module at termination. ::
_count = _count + n
def savecounter():
- open("/tmp/counter", "w").write("%d" % _count)
+ with open("/tmp/counter", "w") as outfile:
+ outfile.write("%d" % _count)
import atexit
atexit.register(savecounter)
diff --git a/Doc/library/bz2.rst b/Doc/library/bz2.rst
index d9a2bad..dbf938a 100644
--- a/Doc/library/bz2.rst
+++ b/Doc/library/bz2.rst
@@ -1,189 +1,172 @@
-:mod:`bz2` --- Compression compatible with :program:`bzip2`
-===========================================================
+:mod:`bz2` --- Support for :program:`bzip2` compression
+=======================================================
.. module:: bz2
- :synopsis: Interface to compression and decompression routines
- compatible with bzip2.
+ :synopsis: Interfaces for bzip2 compression and decompression.
.. moduleauthor:: Gustavo Niemeyer <niemeyer@conectiva.com>
+.. moduleauthor:: Nadeem Vawda <nadeem.vawda@gmail.com>
.. sectionauthor:: Gustavo Niemeyer <niemeyer@conectiva.com>
+.. sectionauthor:: Nadeem Vawda <nadeem.vawda@gmail.com>
-This module provides a comprehensive interface for the bz2 compression library.
-It implements a complete file interface, one-shot (de)compression functions, and
-types for sequential (de)compression.
+This module provides a comprehensive interface for compressing and
+decompressing data using the bzip2 compression algorithm.
-For other archive formats, see the :mod:`gzip`, :mod:`zipfile`, and
+For related file formats, see the :mod:`gzip`, :mod:`lzma`, :mod:`zipfile`, and
:mod:`tarfile` modules.
-Here is a summary of the features offered by the bz2 module:
+The :mod:`bz2` module contains:
-* :class:`BZ2File` class implements a complete file interface, including
- :meth:`~BZ2File.readline`, :meth:`~BZ2File.readlines`,
- :meth:`~BZ2File.writelines`, :meth:`~BZ2File.seek`, etc;
+* The :class:`BZ2File` class for reading and writing compressed files.
+* The :class:`BZ2Compressor` and :class:`BZ2Decompressor` classes for
+ incremental (de)compression.
+* The :func:`compress` and :func:`decompress` functions for one-shot
+ (de)compression.
-* :class:`BZ2File` class implements emulated :meth:`~BZ2File.seek` support;
-
-* :class:`BZ2File` class implements universal newline support;
-
-* :class:`BZ2File` class offers an optimized line iteration using a readahead
- algorithm;
-
-* Sequential (de)compression supported by :class:`BZ2Compressor` and
- :class:`BZ2Decompressor` classes;
-
-* One-shot (de)compression supported by :func:`compress` and :func:`decompress`
- functions;
-
-* Thread safety uses individual locking mechanism.
+All of the classes in this module may safely be accessed from multiple threads.
(De)compression of files
------------------------
-Handling of compressed files is offered by the :class:`BZ2File` class.
-
-
-.. class:: BZ2File(filename, mode='r', buffering=0, compresslevel=9)
-
- Open a bz2 file. Mode can be either ``'r'`` or ``'w'``, for reading (default)
- or writing. When opened for writing, the file will be created if it doesn't
- exist, and truncated otherwise. If *buffering* is given, ``0`` means
- unbuffered, and larger numbers specify the buffer size; the default is
- ``0``. If *compresslevel* is given, it must be a number between ``1`` and
- ``9``; the default is ``9``. Add a ``'U'`` to mode to open the file for input
- with universal newline support. Any line ending in the input file will be
- seen as a ``'\n'`` in Python. Also, a file so opened gains the attribute
- :attr:`newlines`; the value for this attribute is one of ``None`` (no newline
- read yet), ``'\r'``, ``'\n'``, ``'\r\n'`` or a tuple containing all the
- newline types seen. Universal newlines are available only when
- reading. Instances support iteration in the same way as normal :class:`file`
- instances.
+.. class:: BZ2File(filename=None, mode='r', buffering=None, compresslevel=9, fileobj=None)
- :class:`BZ2File` supports the :keyword:`with` statement.
+ Open a bzip2-compressed file.
- .. versionchanged:: 3.1
- Support for the :keyword:`with` statement was added.
+ The :class:`BZ2File` can wrap an existing :term:`file object` (given by
+ *fileobj*), or operate directly on a named file (named by *filename*).
+ Exactly one of these two parameters should be provided.
+ The *mode* argument can be either ``'r'`` for reading (default), ``'w'`` for
+ overwriting, or ``'a'`` for appending. If *fileobj* is provided, a mode of
+ ``'w'`` does not truncate the file, and is instead equivalent to ``'a'``.
- .. method:: close()
+ The *buffering* argument is ignored. Its use is deprecated.
- Close the file. Sets data attribute :attr:`closed` to true. A closed file
- cannot be used for further I/O operations. :meth:`close` may be called
- more than once without error.
+ If *mode* is ``'w'`` or ``'a'``, *compresslevel* can be a number between
+ ``1`` and ``9`` specifying the level of compression: ``1`` produces the
+ least compression, and ``9`` (default) produces the most compression.
+ If *mode* is ``'r'``, the input file may be the concatenation of multiple
+ compressed streams.
- .. method:: read([size])
+ :class:`BZ2File` provides all of the members specified by the
+ :class:`io.BufferedIOBase`, except for :meth:`detach` and :meth:`truncate`.
+ Iteration and the :keyword:`with` statement are supported.
- Read at most *size* uncompressed bytes, returned as a byte string. If the
- *size* argument is negative or omitted, read until EOF is reached.
+ :class:`BZ2File` also provides the following method:
+ .. method:: peek([n])
- .. method:: readline([size])
+ Return buffered data without advancing the file position. At least one
+ byte of data will be returned (unless at EOF). The exact number of bytes
+ returned is unspecified.
- Return the next line from the file, as a byte string, retaining newline.
- A non-negative *size* argument limits the maximum number of bytes to
- return (an incomplete line may be returned then). Return an empty byte
- string at EOF.
+ .. versionadded:: 3.3
+ .. versionchanged:: 3.1
+ Support for the :keyword:`with` statement was added.
- .. method:: readlines([size])
-
- Return a list of lines read. The optional *size* argument, if given, is an
- approximate bound on the total number of bytes in the lines returned.
+ .. versionchanged:: 3.3
+ The :meth:`fileno`, :meth:`readable`, :meth:`seekable`, :meth:`writable`,
+ :meth:`read1` and :meth:`readinto` methods were added.
+ .. versionchanged:: 3.3
+ The *fileobj* argument to the constructor was added.
- .. method:: seek(offset[, whence])
+ .. versionchanged:: 3.3
+ The ``'a'`` (append) mode was added, along with support for reading
+ multi-stream files.
- Move to new file position. Argument *offset* is a byte count. Optional
- argument *whence* defaults to ``os.SEEK_SET`` or ``0`` (offset from start
- of file; offset should be ``>= 0``); other values are ``os.SEEK_CUR`` or
- ``1`` (move relative to current position; offset can be positive or
- negative), and ``os.SEEK_END`` or ``2`` (move relative to end of file;
- offset is usually negative, although many platforms allow seeking beyond
- the end of a file).
- Note that seeking of bz2 files is emulated, and depending on the
- parameters the operation may be extremely slow.
+Incremental (de)compression
+---------------------------
+.. class:: BZ2Compressor(compresslevel=9)
- .. method:: tell()
+ Create a new compressor object. This object may be used to compress data
+ incrementally. For one-shot compression, use the :func:`compress` function
+ instead.
- Return the current file position, an integer.
+ *compresslevel*, if given, must be a number between ``1`` and ``9``. The
+ default is ``9``.
+ .. method:: compress(data)
- .. method:: write(data)
+ Provide data to the compressor object. Returns a chunk of compressed data
+ if possible, or an empty byte string otherwise.
- Write the byte string *data* to file. Note that due to buffering,
- :meth:`close` may be needed before the file on disk reflects the data
- written.
+ When you have finished providing data to the compressor, call the
+ :meth:`flush` method to finish the compression process.
- .. method:: writelines(sequence_of_byte_strings)
+ .. method:: flush()
- Write the sequence of byte strings to the file. Note that newlines are not
- added. The sequence can be any iterable object producing byte strings.
- This is equivalent to calling write() for each byte string.
+ Finish the compression process. Returns the compressed data left in
+ internal buffers.
+ The compressor object may not be used after this method has been called.
-Sequential (de)compression
---------------------------
-Sequential compression and decompression is done using the classes
-:class:`BZ2Compressor` and :class:`BZ2Decompressor`.
+.. class:: BZ2Decompressor()
+ Create a new decompressor object. This object may be used to decompress data
+ incrementally. For one-shot compression, use the :func:`decompress` function
+ instead.
-.. class:: BZ2Compressor(compresslevel=9)
+ .. note::
+ This class does not transparently handle inputs containing multiple
+ compressed streams, unlike :func:`decompress` and :class:`BZ2File`. If
+ you need to decompress a multi-stream input with :class:`BZ2Decompressor`,
+ you must use a new decompressor for each stream.
- Create a new compressor object. This object may be used to compress data
- sequentially. If you want to compress data in one shot, use the
- :func:`compress` function instead. The *compresslevel* parameter, if given,
- must be a number between ``1`` and ``9``; the default is ``9``.
+ .. method:: decompress(data)
- .. method:: compress(data)
+ Provide data to the decompressor object. Returns a chunk of decompressed
+ data if possible, or an empty byte string otherwise.
- Provide more data to the compressor object. It will return chunks of
- compressed data whenever possible. When you've finished providing data to
- compress, call the :meth:`flush` method to finish the compression process,
- and return what is left in internal buffers.
+ Attempting to decompress data after the end of the current stream is
+ reached raises an :exc:`EOFError`. If any data is found after the end of
+ the stream, it is ignored and saved in the :attr:`unused_data` attribute.
- .. method:: flush()
+ .. attribute:: eof
- Finish the compression process and return what is left in internal
- buffers. You must not use the compressor object after calling this method.
+ True if the end-of-stream marker has been reached.
+ .. versionadded:: 3.3
-.. class:: BZ2Decompressor()
- Create a new decompressor object. This object may be used to decompress data
- sequentially. If you want to decompress data in one shot, use the
- :func:`decompress` function instead.
+ .. attribute:: unused_data
- .. method:: decompress(data)
+ Data found after the end of the compressed stream.
- Provide more data to the decompressor object. It will return chunks of
- decompressed data whenever possible. If you try to decompress data after
- the end of stream is found, :exc:`EOFError` will be raised. If any data
- was found after the end of stream, it'll be ignored and saved in
- :attr:`unused_data` attribute.
+ If this attribute is accessed before the end of the stream has been
+ reached, its value will be ``b''``.
One-shot (de)compression
------------------------
-One-shot compression and decompression is provided through the :func:`compress`
-and :func:`decompress` functions.
+.. function:: compress(data, compresslevel=9)
+ Compress *data*.
-.. function:: compress(data, compresslevel=9)
+ *compresslevel*, if given, must be a number between ``1`` and ``9``. The
+ default is ``9``.
- Compress *data* in one shot. If you want to compress data sequentially, use
- an instance of :class:`BZ2Compressor` instead. The *compresslevel* parameter,
- if given, must be a number between ``1`` and ``9``; the default is ``9``.
+ For incremental compression, use a :class:`BZ2Compressor` instead.
.. function:: decompress(data)
- Decompress *data* in one shot. If you want to decompress data sequentially,
- use an instance of :class:`BZ2Decompressor` instead.
+ Decompress *data*.
+
+ If *data* is the concatenation of multiple compressed streams, decompress
+ all of the streams.
+
+ For incremental decompression, use a :class:`BZ2Decompressor` instead.
+
+ .. versionchanged:: 3.3
+ Support for multi-stream inputs was added.
diff --git a/Doc/library/chunk.rst b/Doc/library/chunk.rst
index d3558a4..c1ba497 100644
--- a/Doc/library/chunk.rst
+++ b/Doc/library/chunk.rst
@@ -84,8 +84,9 @@ instance will fail with a :exc:`EOFError` exception.
Close and skip to the end of the chunk. This does not close the
underlying file.
- The remaining methods will raise :exc:`IOError` if called after the
- :meth:`close` method has been called.
+ The remaining methods will raise :exc:`OSError` if called after the
+ :meth:`close` method has been called. Before Python 3.3, they used to
+ raise :exc:`IOError`, now an alias of :exc:`OSError`.
.. method:: isatty()
diff --git a/Doc/library/cmd.rst b/Doc/library/cmd.rst
index fd7f453..0c43bb8 100644
--- a/Doc/library/cmd.rst
+++ b/Doc/library/cmd.rst
@@ -285,8 +285,8 @@ immediate playback::
def do_playback(self, arg):
'Playback commands from a file: PLAYBACK rose.cmd'
self.close()
- cmds = open(arg).read().splitlines()
- self.cmdqueue.extend(cmds)
+ with open(arg) as f:
+ self.cmdqueue.extend(f.read().splitlines())
def precmd(self, line):
line = line.lower()
if self.file and 'playback' not in line:
diff --git a/Doc/library/codecs.rst b/Doc/library/codecs.rst
index 7747794..a9fae95 100644
--- a/Doc/library/codecs.rst
+++ b/Doc/library/codecs.rst
@@ -458,7 +458,8 @@ define in order to be compatible with the Python codec registry.
.. method:: reset()
- Reset the encoder to the initial state.
+ Reset the encoder to the initial state. The output is discarded: call
+ ``.encode('', final=True)`` to reset the encoder and to get the output.
.. method:: IncrementalEncoder.getstate()
@@ -786,11 +787,9 @@ methods and attributes from the underlying stream.
Encodings and Unicode
---------------------
-Strings are stored internally as sequences of codepoints (to be precise
-as :c:type:`Py_UNICODE` arrays). Depending on the way Python is compiled (either
-via ``--without-wide-unicode`` or ``--with-wide-unicode``, with the
-former being the default) :c:type:`Py_UNICODE` is either a 16-bit or 32-bit data
-type. Once a string object is used outside of CPU and memory, CPU endianness
+Strings are stored internally as sequences of codepoints in range ``0 - 10FFFF``
+(see :pep:`393` for more details about the implementation).
+Once a string object is used outside of CPU and memory, CPU endianness
and how these arrays are stored as bytes become an issue. Transforming a
string object into a sequence of bytes is called encoding and recreating the
string object from the sequence of bytes is known as decoding. There are many
@@ -901,6 +900,15 @@ is meant to be exhaustive. Notice that spelling alternatives that only differ in
case or use a hyphen instead of an underscore are also valid aliases; therefore,
e.g. ``'utf-8'`` is a valid alias for the ``'utf_8'`` codec.
+.. impl-detail::
+
+ Some common encodings can bypass the codecs lookup machinery to
+ improve performance. These optimization opportunities are only
+ recognized by CPython for a limited set of aliases: utf-8, utf8,
+ latin-1, latin1, iso-8859-1, mbcs (Windows only), ascii, utf-16,
+ and utf-32. Using alternative spellings for these encodings may
+ result in slower execution.
+
Many of the character sets support the same languages. They vary in individual
characters (e.g. whether the EURO SIGN is supported or not), and in the
assignment of characters to code positions. For the European languages in
@@ -1003,6 +1011,11 @@ particular, the following variants typically exist:
+-----------------+--------------------------------+--------------------------------+
| cp1258 | windows-1258 | Vietnamese |
+-----------------+--------------------------------+--------------------------------+
+| cp65001 | | Windows only: Windows UTF-8 |
+| | | (``CP_UTF8``) |
+| | | |
+| | | .. versionadded:: 3.3 |
++-----------------+--------------------------------+--------------------------------+
| euc_jp | eucjp, ujis, u-jis | Japanese |
+-----------------+--------------------------------+--------------------------------+
| euc_jis_2004 | jisx0213, eucjis2004 | Japanese |
@@ -1160,6 +1173,8 @@ particular, the following variants typically exist:
| unicode_internal | | Return the internal |
| | | representation of the |
| | | operand |
+| | | |
+| | | .. deprecated:: 3.3 |
+--------------------+---------+---------------------------+
The following codecs provide bytes-to-bytes mappings.
@@ -1272,12 +1287,13 @@ functions can be used directly if desired.
.. module:: encodings.mbcs
:synopsis: Windows ANSI codepage
-Encode operand according to the ANSI codepage (CP_ACP). This codec only
-supports ``'strict'`` and ``'replace'`` error handlers to encode, and
-``'strict'`` and ``'ignore'`` error handlers to decode.
+Encode operand according to the ANSI codepage (CP_ACP).
Availability: Windows only.
+.. versionchanged:: 3.3
+ Support any error handler.
+
.. versionchanged:: 3.2
Before 3.2, the *errors* argument was ignored; ``'replace'`` was always used
to encode, and ``'ignore'`` to decode.
diff --git a/Doc/library/collections.abc.rst b/Doc/library/collections.abc.rst
new file mode 100644
index 0000000..9873489
--- /dev/null
+++ b/Doc/library/collections.abc.rst
@@ -0,0 +1,182 @@
+:mod:`collections.abc` --- Abstract Base Classes for Containers
+===============================================================
+
+.. module:: collections.abc
+ :synopsis: Abstract base classes for containers
+.. moduleauthor:: Raymond Hettinger <python at rcn.com>
+.. sectionauthor:: Raymond Hettinger <python at rcn.com>
+
+.. versionadded:: 3.3
+ Formerly, this module was part of the :mod:`collections` module.
+
+.. testsetup:: *
+
+ from collections import *
+ import itertools
+ __name__ = '<doctest>'
+
+**Source code:** :source:`Lib/collections/abc.py`
+
+--------------
+
+This module provides :term:`abstract base classes <abstract base class>` that
+can be used to test whether a class provides a particular interface; for
+example, whether it is hashable or whether it is a mapping.
+
+
+.. _collections-abstract-base-classes:
+
+Collections Abstract Base Classes
+---------------------------------
+
+The collections module offers the following :term:`ABCs <abstract base class>`:
+
+========================= ===================== ====================== ====================================================
+ABC Inherits from Abstract Methods Mixin Methods
+========================= ===================== ====================== ====================================================
+:class:`Container` ``__contains__``
+:class:`Hashable` ``__hash__``
+:class:`Iterable` ``__iter__``
+:class:`Iterator` :class:`Iterable` ``__next__`` ``__iter__``
+:class:`Sized` ``__len__``
+:class:`Callable` ``__call__``
+
+:class:`Sequence` :class:`Sized`, ``__getitem__`` ``__contains__``, ``__iter__``, ``__reversed__``,
+ :class:`Iterable`, ``index``, and ``count``
+ :class:`Container`
+
+:class:`MutableSequence` :class:`Sequence` ``__setitem__``, Inherited :class:`Sequence` methods and
+ ``__delitem__``, ``append``, ``reverse``, ``extend``, ``pop``,
+ ``insert`` ``remove``, ``clear``, and ``__iadd__``
+
+:class:`Set` :class:`Sized`, ``__le__``, ``__lt__``, ``__eq__``, ``__ne__``,
+ :class:`Iterable`, ``__gt__``, ``__ge__``, ``__and__``, ``__or__``,
+ :class:`Container` ``__sub__``, ``__xor__``, and ``isdisjoint``
+
+:class:`MutableSet` :class:`Set` ``add``, Inherited :class:`Set` methods and
+ ``discard`` ``clear``, ``pop``, ``remove``, ``__ior__``,
+ ``__iand__``, ``__ixor__``, and ``__isub__``
+
+:class:`Mapping` :class:`Sized`, ``__getitem__`` ``__contains__``, ``keys``, ``items``, ``values``,
+ :class:`Iterable`, ``get``, ``__eq__``, and ``__ne__``
+ :class:`Container`
+
+:class:`MutableMapping` :class:`Mapping` ``__setitem__``, Inherited :class:`Mapping` methods and
+ ``__delitem__`` ``pop``, ``popitem``, ``clear``, ``update``,
+ and ``setdefault``
+
+
+:class:`MappingView` :class:`Sized` ``__len__``
+:class:`ItemsView` :class:`MappingView`, ``__contains__``,
+ :class:`Set` ``__iter__``
+:class:`KeysView` :class:`MappingView`, ``__contains__``,
+ :class:`Set` ``__iter__``
+:class:`ValuesView` :class:`MappingView` ``__contains__``, ``__iter__``
+========================= ===================== ====================== ====================================================
+
+
+.. class:: Container
+ Hashable
+ Sized
+ Callable
+
+ ABCs for classes that provide respectively the methods :meth:`__contains__`,
+ :meth:`__hash__`, :meth:`__len__`, and :meth:`__call__`.
+
+.. class:: Iterable
+
+ ABC for classes that provide the :meth:`__iter__` method.
+ See also the definition of :term:`iterable`.
+
+.. class:: Iterator
+
+ ABC for classes that provide the :meth:`__iter__` and :meth:`next` methods.
+ See also the definition of :term:`iterator`.
+
+.. class:: Sequence
+ MutableSequence
+
+ ABCs for read-only and mutable :term:`sequences <sequence>`.
+
+.. class:: Set
+ MutableSet
+
+ ABCs for read-only and mutable sets.
+
+.. class:: Mapping
+ MutableMapping
+
+ ABCs for read-only and mutable :term:`mappings <mapping>`.
+
+.. class:: MappingView
+ ItemsView
+ KeysView
+ ValuesView
+
+ ABCs for mapping, items, keys, and values :term:`views <view>`.
+
+
+These ABCs allow us to ask classes or instances if they provide
+particular functionality, for example::
+
+ size = None
+ if isinstance(myvar, collections.Sized):
+ size = len(myvar)
+
+Several of the ABCs are also useful as mixins that make it easier to develop
+classes supporting container APIs. For example, to write a class supporting
+the full :class:`Set` API, it only necessary to supply the three underlying
+abstract methods: :meth:`__contains__`, :meth:`__iter__`, and :meth:`__len__`.
+The ABC supplies the remaining methods such as :meth:`__and__` and
+:meth:`isdisjoint` ::
+
+ class ListBasedSet(collections.Set):
+ ''' Alternate set implementation favoring space over speed
+ and not requiring the set elements to be hashable. '''
+ def __init__(self, iterable):
+ self.elements = lst = []
+ for value in iterable:
+ if value not in lst:
+ lst.append(value)
+ def __iter__(self):
+ return iter(self.elements)
+ def __contains__(self, value):
+ return value in self.elements
+ def __len__(self):
+ return len(self.elements)
+
+ s1 = ListBasedSet('abcdef')
+ s2 = ListBasedSet('defghi')
+ overlap = s1 & s2 # The __and__() method is supported automatically
+
+Notes on using :class:`Set` and :class:`MutableSet` as a mixin:
+
+(1)
+ Since some set operations create new sets, the default mixin methods need
+ a way to create new instances from an iterable. The class constructor is
+ assumed to have a signature in the form ``ClassName(iterable)``.
+ That assumption is factored-out to an internal classmethod called
+ :meth:`_from_iterable` which calls ``cls(iterable)`` to produce a new set.
+ If the :class:`Set` mixin is being used in a class with a different
+ constructor signature, you will need to override :meth:`_from_iterable`
+ with a classmethod that can construct new instances from
+ an iterable argument.
+
+(2)
+ To override the comparisons (presumably for speed, as the
+ semantics are fixed), redefine :meth:`__le__` and
+ then the other operations will automatically follow suit.
+
+(3)
+ The :class:`Set` mixin provides a :meth:`_hash` method to compute a hash value
+ for the set; however, :meth:`__hash__` is not defined because not all sets
+ are hashable or immutable. To add set hashabilty using mixins,
+ inherit from both :meth:`Set` and :meth:`Hashable`, then define
+ ``__hash__ = Set._hash``.
+
+.. seealso::
+
+ * `OrderedSet recipe <http://code.activestate.com/recipes/576694/>`_ for an
+ example built on :class:`MutableSet`.
+
+ * For more about ABCs, see the :mod:`abc` module and :pep:`3119`.
diff --git a/Doc/library/collections.rst b/Doc/library/collections.rst
index e512bf1..c9e3864 100644
--- a/Doc/library/collections.rst
+++ b/Doc/library/collections.rst
@@ -12,7 +12,7 @@
import itertools
__name__ = '<doctest>'
-**Source code:** :source:`Lib/collections.py` and :source:`Lib/_abcoll.py`
+**Source code:** :source:`Lib/collections/__init__.py`
--------------
@@ -23,6 +23,7 @@ Python's general purpose built-in containers, :class:`dict`, :class:`list`,
===================== ====================================================================
:func:`namedtuple` factory function for creating tuple subclasses with named fields
:class:`deque` list-like container with fast appends and pops on either end
+:class:`ChainMap` dict-like class for creating a single view of multiple mappings
:class:`Counter` dict subclass for counting hashable objects
:class:`OrderedDict` dict subclass that remembers the order entries were added
:class:`defaultdict` dict subclass that calls a factory function to supply missing values
@@ -31,12 +32,125 @@ Python's general purpose built-in containers, :class:`dict`, :class:`list`,
:class:`UserString` wrapper around string objects for easier string subclassing
===================== ====================================================================
-In addition to the concrete container classes, the collections module provides
-:ref:`abstract base classes <collections-abstract-base-classes>` that can be
-used to test whether a class provides a particular interface, for example,
-whether it is hashable or a mapping.
+.. versionchanged:: 3.3
+ Moved :ref:`collections-abstract-base-classes` to the :mod:`collections.abc` module.
+ For backwards compatibility, they continue to be visible in this module
+ as well.
+:class:`ChainMap` objects
+-------------------------
+
+A :class:`ChainMap` class is provided for quickly linking a number of mappings
+so they can be treated as a single unit. It is often much faster than creating
+a new dictionary and running multiple :meth:`~dict.update` calls.
+
+The class can be used to simulate nested scopes and is useful in templating.
+
+.. class:: ChainMap(*maps)
+
+ A :class:`ChainMap` groups multiple dicts or other mappings together to
+ create a single, updateable view. If no *maps* are specified, a single empty
+ dictionary is provided so that a new chain always has at least one mapping.
+
+ The underlying mappings are stored in a list. That list is public and can
+ accessed or updated using the *maps* attribute. There is no other state.
+
+ Lookups search the underlying mappings successively until a key is found. In
+ contrast, writes, updates, and deletions only operate on the first mapping.
+
+ A :class:`ChainMap` incorporates the underlying mappings by reference. So, if
+ one of the underlying mappings gets updated, those changes will be reflected
+ in :class:`ChainMap`.
+
+ All of the usual dictionary methods are supported. In addition, there is a
+ *maps* attribute, a method for creating new subcontexts, and a property for
+ accessing all but the first mapping:
+
+ .. attribute:: maps
+
+ A user updateable list of mappings. The list is ordered from
+ first-searched to last-searched. It is the only stored state and can
+ be modified to change which mappings are searched. The list should
+ always contain at least one mapping.
+
+ .. method:: new_child()
+
+ Returns a new :class:`ChainMap` containing a new :class:`dict` followed by
+ all of the maps in the current instance. A call to ``d.new_child()`` is
+ equivalent to: ``ChainMap({}, *d.maps)``. This method is used for
+ creating subcontexts that can be updated without altering values in any
+ of the parent mappings.
+
+ .. method:: parents()
+
+ Returns a new :class:`ChainMap` containing all of the maps in the current
+ instance except the first one. This is useful for skipping the first map
+ in the search. The use-cases are similar to those for the
+ :keyword:`nonlocal` keyword used in :term:`nested scopes <nested scope>`.
+ The use-cases also parallel those for the builtin :func:`super` function.
+ A reference to ``d.parents`` is equivalent to: ``ChainMap(*d.maps[1:])``.
+
+ .. versionadded:: 3.3
+
+ Example of simulating Python's internal lookup chain::
+
+ import builtins
+ pylookup = ChainMap(locals(), globals(), vars(builtins))
+
+ Example of letting user specified values take precedence over environment
+ variables which in turn take precedence over default values::
+
+ import os, argparse
+ defaults = {'color': 'red', 'user': guest}
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-u', '--user')
+ parser.add_argument('-c', '--color')
+ user_specified = vars(parser.parse_args())
+ combined = ChainMap(user_specified, os.environ, defaults)
+
+ Example patterns for using the :class:`ChainMap` class to simulate nested
+ contexts::
+
+ c = ChainMap() Create root context
+ d = c.new_child() Create nested child context
+ e = c.new_child() Child of c, independent from d
+ e.maps[0] Current context dictionary -- like Python's locals()
+ e.maps[-1] Root context -- like Python's globals()
+ e.parents Enclosing context chain -- like Python's nonlocals
+
+ d['x'] Get first key in the chain of contexts
+ d['x'] = 1 Set value in current context
+ del['x'] Delete from current context
+ list(d) All nested values
+ k in d Check all nested values
+ len(d) Number of nested values
+ d.items() All nested items
+ dict(d) Flatten into a regular dictionary
+
+ .. seealso::
+
+ * The `MultiContext class
+ <http://svn.enthought.com/svn/enthought/CodeTools/trunk/enthought/contexts/multi_context.py>`_
+ in the Enthought `CodeTools package
+ <https://github.com/enthought/codetools>`_ has options to support
+ writing to any mapping in the chain.
+
+ * Django's `Context class
+ <http://code.djangoproject.com/browser/django/trunk/django/template/context.py>`_
+ for templating is a read-only chain of mappings. It also features
+ pushing and popping of contexts similar to the
+ :meth:`~collections.ChainMap.new_child` method and the
+ :meth:`~collections.ChainMap.parents` property.
+
+ * The `Nested Contexts recipe
+ <http://code.activestate.com/recipes/577434/>`_ has options to control
+ whether writes and other mutations apply only to the first mapping or to
+ any mapping in the chain.
+
+ * A `greatly simplified read-only version of Chainmap
+ <http://code.activestate.com/recipes/305268/>`_.
+
:class:`Counter` objects
------------------------
@@ -149,7 +263,7 @@ Common patterns for working with :class:`Counter` objects::
c.items() # convert to a list of (elem, cnt) pairs
Counter(dict(list_of_pairs)) # convert from a list of (elem, cnt) pairs
c.most_common()[:-n:-1] # n least common elements
- c += Counter() # remove zero and negative counts
+ +c # remove zero and negative counts
Several mathematical operations are provided for combining :class:`Counter`
objects to produce multisets (counters that have counts greater than zero).
@@ -169,6 +283,18 @@ counts, but the output will exclude results with counts of zero or less.
>>> c | d # union: max(c[x], d[x])
Counter({'a': 3, 'b': 2})
+Unary addition and substraction are shortcuts for adding an empty counter
+or subtracting from an empty counter.
+
+ >>> c = Counter(a=2, b=-4)
+ >>> +c
+ Counter({'a': 2})
+ >>> -c
+ Counter({'b': 4})
+
+.. versionadded:: 3.3
+ Added support for unary plus, unary minus, and in-place multiset operations.
+
.. note::
Counters were primarily designed to work with positive integers to represent
@@ -398,7 +524,8 @@ in Unix::
def tail(filename, n=10):
'Return the last n lines of a file'
- return deque(open(filename), n)
+ with open(filename) as f:
+ return deque(f, n)
Another approach to using deques is to maintain a sequence of recently
added elements by appending to the right and popping to the left::
@@ -545,7 +672,7 @@ Setting the :attr:`default_factory` to :class:`set` makes the
... d[k].add(v)
...
>>> list(d.items())
- [('blue', set([2, 4])), ('red', set([1, 3]))]
+ [('blue', {2, 4}), ('red', {1, 3})]
:func:`namedtuple` Factory Function for Tuples with Named Fields
@@ -578,7 +705,9 @@ they add the ability to access fields by name instead of position index.
converted to ``['abc', '_1', 'ghi', '_3']``, eliminating the keyword
``def`` and the duplicate fieldname ``abc``.
- If *verbose* is true, the class definition is printed just before being built.
+ If *verbose* is true, the class definition is printed after it is
+ built. This option is outdated; instead, it is simpler to print the
+ :attr:`_source` attribute.
Named tuple instances do not have per-instance dictionaries, so they are
lightweight and require no more memory than regular tuples.
@@ -592,53 +721,6 @@ they add the ability to access fields by name instead of position index.
>>> # Basic example
>>> Point = namedtuple('Point', ['x', 'y'])
- >>> p = Point(x=10, y=11)
-
- >>> # Example using the verbose option to print the class definition
- >>> Point = namedtuple('Point', 'x y', verbose=True)
- class Point(tuple):
- 'Point(x, y)'
- <BLANKLINE>
- __slots__ = ()
- <BLANKLINE>
- _fields = ('x', 'y')
- <BLANKLINE>
- def __new__(_cls, x, y):
- 'Create a new instance of Point(x, y)'
- return _tuple.__new__(_cls, (x, y))
- <BLANKLINE>
- @classmethod
- def _make(cls, iterable, new=tuple.__new__, len=len):
- 'Make a new Point object from a sequence or iterable'
- result = new(cls, iterable)
- if len(result) != 2:
- raise TypeError('Expected 2 arguments, got %d' % len(result))
- return result
- <BLANKLINE>
- def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + '(x=%r, y=%r)' % self
- <BLANKLINE>
- def _asdict(self):
- 'Return a new OrderedDict which maps field names to their values'
- return OrderedDict(zip(self._fields, self))
- <BLANKLINE>
- __dict__ = property(_asdict)
- <BLANKLINE>
- def _replace(_self, **kwds):
- 'Return a new Point object replacing specified fields with new values'
- result = _self._make(map(kwds.pop, ('x', 'y'), _self))
- if kwds:
- raise ValueError('Got unexpected field names: %r' % list(kwds.keys()))
- return result
- <BLANKLINE>
- def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return tuple(self)
- <BLANKLINE>
- x = _property(_itemgetter(0), doc='Alias for field number 0')
- y = _property(_itemgetter(1), doc='Alias for field number 1')
-
>>> p = Point(11, y=22) # instantiate with positional or keyword arguments
>>> p[0] + p[1] # indexable like the plain tuple (11, 22)
33
@@ -667,7 +749,7 @@ by the :mod:`csv` or :mod:`sqlite3` modules::
print(emp.name, emp.title)
In addition to the methods inherited from tuples, named tuples support
-three additional methods and one attribute. To prevent conflicts with
+three additional methods and two attributes. To prevent conflicts with
field names, the method and attribute names start with an underscore.
.. classmethod:: somenamedtuple._make(iterable)
@@ -705,6 +787,15 @@ field names, the method and attribute names start with an underscore.
>>> for partnum, record in inventory.items():
... inventory[partnum] = record._replace(price=newprices[partnum], timestamp=time.now())
+.. attribute:: somenamedtuple._source
+
+ A string with the pure Python source code used to create the named
+ tuple class. The source makes the named tuple self-documenting.
+ It can be printed, executed using :func:`exec`, or saved to a file
+ and imported.
+
+ .. versionadded:: 3.3
+
.. attribute:: somenamedtuple._fields
Tuple of strings listing the field names. Useful for introspection
@@ -753,7 +844,6 @@ a fixed-width print format:
The subclass shown above sets ``__slots__`` to an empty tuple. This helps
keep memory requirements low by preventing the creation of instance dictionaries.
-
Subclassing is not useful for adding new, stored fields. Instead, simply
create a new named tuple type from the :attr:`_fields` attribute:
@@ -765,6 +855,7 @@ customize a prototype instance:
>>> Account = namedtuple('Account', 'owner balance transaction_count')
>>> default_account = Account('<owner name>', 0.0, 0)
>>> johns_account = default_account._replace(owner='John')
+ >>> janes_account = default_account._replace(owner='Jane')
Enumerated constants can be implemented with named tuples, but it is simpler
and more efficient to use a simple class declaration:
@@ -983,161 +1074,3 @@ attribute.
be an instance of :class:`bytes`, :class:`str`, :class:`UserString` (or a
subclass) or an arbitrary sequence which can be converted into a string using
the built-in :func:`str` function.
-
-
-.. _collections-abstract-base-classes:
-
-ABCs - abstract base classes
-----------------------------
-
-The collections module offers the following :term:`ABCs <abstract base class>`:
-
-========================= ===================== ====================== ====================================================
-ABC Inherits from Abstract Methods Mixin Methods
-========================= ===================== ====================== ====================================================
-:class:`Container` ``__contains__``
-:class:`Hashable` ``__hash__``
-:class:`Iterable` ``__iter__``
-:class:`Iterator` :class:`Iterable` ``__next__`` ``__iter__``
-:class:`Sized` ``__len__``
-:class:`Callable` ``__call__``
-
-:class:`Sequence` :class:`Sized`, ``__getitem__`` ``__contains__``, ``__iter__``, ``__reversed__``,
- :class:`Iterable`, ``index``, and ``count``
- :class:`Container`
-
-:class:`MutableSequence` :class:`Sequence` ``__setitem__``, Inherited :class:`Sequence` methods and
- ``__delitem__``, ``append``, ``reverse``, ``extend``, ``pop``,
- ``insert`` ``remove``, and ``__iadd__``
-
-:class:`Set` :class:`Sized`, ``__le__``, ``__lt__``, ``__eq__``, ``__ne__``,
- :class:`Iterable`, ``__gt__``, ``__ge__``, ``__and__``, ``__or__``,
- :class:`Container` ``__sub__``, ``__xor__``, and ``isdisjoint``
-
-:class:`MutableSet` :class:`Set` ``add``, Inherited :class:`Set` methods and
- ``discard`` ``clear``, ``pop``, ``remove``, ``__ior__``,
- ``__iand__``, ``__ixor__``, and ``__isub__``
-
-:class:`Mapping` :class:`Sized`, ``__getitem__`` ``__contains__``, ``keys``, ``items``, ``values``,
- :class:`Iterable`, ``get``, ``__eq__``, and ``__ne__``
- :class:`Container`
-
-:class:`MutableMapping` :class:`Mapping` ``__setitem__``, Inherited :class:`Mapping` methods and
- ``__delitem__`` ``pop``, ``popitem``, ``clear``, ``update``,
- and ``setdefault``
-
-
-:class:`MappingView` :class:`Sized` ``__len__``
-:class:`ItemsView` :class:`MappingView`, ``__contains__``,
- :class:`Set` ``__iter__``
-:class:`KeysView` :class:`MappingView`, ``__contains__``,
- :class:`Set` ``__iter__``
-:class:`ValuesView` :class:`MappingView` ``__contains__``, ``__iter__``
-========================= ===================== ====================== ====================================================
-
-
-.. class:: Container
- Hashable
- Sized
- Callable
-
- ABCs for classes that provide respectively the methods :meth:`__contains__`,
- :meth:`__hash__`, :meth:`__len__`, and :meth:`__call__`.
-
-.. class:: Iterable
-
- ABC for classes that provide the :meth:`__iter__` method.
- See also the definition of :term:`iterable`.
-
-.. class:: Iterator
-
- ABC for classes that provide the :meth:`__iter__` and :meth:`next` methods.
- See also the definition of :term:`iterator`.
-
-.. class:: Sequence
- MutableSequence
-
- ABCs for read-only and mutable :term:`sequences <sequence>`.
-
-.. class:: Set
- MutableSet
-
- ABCs for read-only and mutable sets.
-
-.. class:: Mapping
- MutableMapping
-
- ABCs for read-only and mutable :term:`mappings <mapping>`.
-
-.. class:: MappingView
- ItemsView
- KeysView
- ValuesView
-
- ABCs for mapping, items, keys, and values :term:`views <view>`.
-
-
-These ABCs allow us to ask classes or instances if they provide
-particular functionality, for example::
-
- size = None
- if isinstance(myvar, collections.Sized):
- size = len(myvar)
-
-Several of the ABCs are also useful as mixins that make it easier to develop
-classes supporting container APIs. For example, to write a class supporting
-the full :class:`Set` API, it only necessary to supply the three underlying
-abstract methods: :meth:`__contains__`, :meth:`__iter__`, and :meth:`__len__`.
-The ABC supplies the remaining methods such as :meth:`__and__` and
-:meth:`isdisjoint` ::
-
- class ListBasedSet(collections.Set):
- ''' Alternate set implementation favoring space over speed
- and not requiring the set elements to be hashable. '''
- def __init__(self, iterable):
- self.elements = lst = []
- for value in iterable:
- if value not in lst:
- lst.append(value)
- def __iter__(self):
- return iter(self.elements)
- def __contains__(self, value):
- return value in self.elements
- def __len__(self):
- return len(self.elements)
-
- s1 = ListBasedSet('abcdef')
- s2 = ListBasedSet('defghi')
- overlap = s1 & s2 # The __and__() method is supported automatically
-
-Notes on using :class:`Set` and :class:`MutableSet` as a mixin:
-
-(1)
- Since some set operations create new sets, the default mixin methods need
- a way to create new instances from an iterable. The class constructor is
- assumed to have a signature in the form ``ClassName(iterable)``.
- That assumption is factored-out to an internal classmethod called
- :meth:`_from_iterable` which calls ``cls(iterable)`` to produce a new set.
- If the :class:`Set` mixin is being used in a class with a different
- constructor signature, you will need to override :meth:`_from_iterable`
- with a classmethod that can construct new instances from
- an iterable argument.
-
-(2)
- To override the comparisons (presumably for speed, as the
- semantics are fixed), redefine :meth:`__le__` and
- then the other operations will automatically follow suit.
-
-(3)
- The :class:`Set` mixin provides a :meth:`_hash` method to compute a hash value
- for the set; however, :meth:`__hash__` is not defined because not all sets
- are hashable or immutable. To add set hashabilty using mixins,
- inherit from both :meth:`Set` and :meth:`Hashable`, then define
- ``__hash__ = Set._hash``.
-
-.. seealso::
-
- * `OrderedSet recipe <http://code.activestate.com/recipes/576694/>`_ for an
- example built on :class:`MutableSet`.
-
- * For more about ABCs, see the :mod:`abc` module and :pep:`3119`.
diff --git a/Doc/library/concurrent.futures.rst b/Doc/library/concurrent.futures.rst
index 29ffc0d..a88f10c 100644
--- a/Doc/library/concurrent.futures.rst
+++ b/Doc/library/concurrent.futures.rst
@@ -169,6 +169,12 @@ to a :class:`ProcessPoolExecutor` will result in deadlock.
of at most *max_workers* processes. If *max_workers* is ``None`` or not
given, it will default to the number of processors on the machine.
+ .. versionchanged:: 3.3
+ When one of the worker processes terminates abruptly, a
+ :exc:`BrokenProcessPool` error is now raised. Previously, behaviour
+ was undefined but operations on the executor or its futures would often
+ freeze or deadlock.
+
.. _processpoolexecutor-example:
@@ -369,3 +375,16 @@ Module Functions
:pep:`3148` -- futures - execute computations asynchronously
The proposal which described this feature for inclusion in the Python
standard library.
+
+
+Exception classes
+-----------------
+
+.. exception:: BrokenProcessPool
+
+ Derived from :exc:`RuntimeError`, this exception class is raised when
+ one of the workers of a :class:`ProcessPoolExecutor` has terminated
+ in a non-clean fashion (for example, if it was killed from the outside).
+
+ .. versionadded:: 3.3
+
diff --git a/Doc/library/crypt.rst b/Doc/library/crypt.rst
index 0be571e..1ba2ed3 100644
--- a/Doc/library/crypt.rst
+++ b/Doc/library/crypt.rst
@@ -15,9 +15,9 @@
This module implements an interface to the :manpage:`crypt(3)` routine, which is
a one-way hash function based upon a modified DES algorithm; see the Unix man
-page for further details. Possible uses include allowing Python scripts to
-accept typed passwords from the user, or attempting to crack Unix passwords with
-a dictionary.
+page for further details. Possible uses include storing hashed passwords
+so you can check passwords without storing the actual password, or attempting
+to crack Unix passwords with a dictionary.
.. index:: single: crypt(3)
@@ -26,15 +26,74 @@ the :manpage:`crypt(3)` routine in the running system. Therefore, any
extensions available on the current implementation will also be available on
this module.
+Hashing Methods
+---------------
-.. function:: crypt(word, salt)
+.. versionadded:: 3.3
+
+The :mod:`crypt` module defines the list of hashing methods (not all methods
+are available on all platforms):
+
+.. data:: METHOD_SHA512
+
+ A Modular Crypt Format method with 16 character salt and 86 character
+ hash. This is the strongest method.
+
+.. data:: METHOD_SHA256
+
+ Another Modular Crypt Format method with 16 character salt and 43
+ character hash.
+
+.. data:: METHOD_MD5
+
+ Another Modular Crypt Format method with 8 character salt and 22
+ character hash.
+
+.. data:: METHOD_CRYPT
+
+ The traditional method with a 2 character salt and 13 characters of
+ hash. This is the weakest method.
+
+
+Module Attributes
+-----------------
+
+.. versionadded:: 3.3
+
+.. attribute:: methods
+
+ A list of available password hashing algorithms, as
+ ``crypt.METHOD_*`` objects. This list is sorted from strongest to
+ weakest, and is guaranteed to have at least ``crypt.METHOD_CRYPT``.
+
+
+Module Functions
+----------------
+
+The :mod:`crypt` module defines the following functions:
+
+.. function:: crypt(word, salt=None)
*word* will usually be a user's password as typed at a prompt or in a graphical
- interface. *salt* is usually a random two-character string which will be used
- to perturb the DES algorithm in one of 4096 ways. The characters in *salt* must
- be in the set ``[./a-zA-Z0-9]``. Returns the hashed password as a string, which
- will be composed of characters from the same alphabet as the salt (the first two
- characters represent the salt itself).
+ interface. The optional *salt* is either a string as returned from
+ :func:`mksalt`, one of the ``crypt.METHOD_*`` values (though not all
+ may be available on all platforms), or a full encrypted password
+ including salt, as returned by this function. If *salt* is not
+ provided, the strongest method will be used (as returned by
+ :func:`methods`.
+
+ Checking a password is usually done by passing the plain-text password
+ as *word* and the full results of a previous :func:`crypt` call,
+ which should be the same as the results of this call.
+
+ *salt* (either a random 2 or 16 character string, possibly prefixed with
+ ``$digit$`` to indicate the method) which will be used to perturb the
+ encryption algorithm. The characters in *salt* must be in the set
+ ``[./a-zA-Z0-9]``, with the exception of Modular Crypt Format which
+ prefixes a ``$digit$``.
+
+ Returns the hashed password as a string, which will be composed of
+ characters from the same alphabet as the salt.
.. index:: single: crypt(3)
@@ -42,18 +101,48 @@ this module.
different sizes in the *salt*, it is recommended to use the full crypted
password as salt when checking for a password.
+ .. versionchanged:: 3.3
+ Accept ``crypt.METHOD_*`` values in addition to strings for *salt*.
+
+
+.. function:: mksalt(method=None)
+
+ Return a randomly generated salt of the specified method. If no
+ *method* is given, the strongest method available as returned by
+ :func:`methods` is used.
+
+ The return value is a string either of 2 characters in length for
+ ``crypt.METHOD_CRYPT``, or 19 characters starting with ``$digit$`` and
+ 16 random characters from the set ``[./a-zA-Z0-9]``, suitable for
+ passing as the *salt* argument to :func:`crypt`.
+
+ .. versionadded:: 3.3
+
+Examples
+--------
+
A simple example illustrating typical use::
- import crypt, getpass, pwd
+ import pwd
+ import crypt
+ import getpass
def login():
- username = input('Python login:')
+ username = input('Python login: ')
cryptedpasswd = pwd.getpwnam(username)[1]
if cryptedpasswd:
if cryptedpasswd == 'x' or cryptedpasswd == '*':
- raise "Sorry, currently no support for shadow passwords"
+ raise ValueError('no support for shadow passwords')
cleartext = getpass.getpass()
return crypt.crypt(cleartext, cryptedpasswd) == cryptedpasswd
else:
- return 1
+ return True
+
+To generate a hash of a password using the strongest available method and
+check it against the original::
+
+ import crypt
+ hashed = crypt.crypt(plaintext)
+ if hashed != crypt.crypt(plaintext, hashed):
+ raise ValueError("hashed version doesn't validate against original")
diff --git a/Doc/library/csv.rst b/Doc/library/csv.rst
index edbe726..ec0dfcc 100644
--- a/Doc/library/csv.rst
+++ b/Doc/library/csv.rst
@@ -11,15 +11,15 @@
pair: data; tabular
The so-called CSV (Comma Separated Values) format is the most common import and
-export format for spreadsheets and databases. There is no "CSV standard", so
-the format is operationally defined by the many applications which read and
-write it. The lack of a standard means that subtle differences often exist in
-the data produced and consumed by different applications. These differences can
-make it annoying to process CSV files from multiple sources. Still, while the
-delimiters and quoting characters vary, the overall format is similar enough
-that it is possible to write a single module which can efficiently manipulate
-such data, hiding the details of reading and writing the data from the
-programmer.
+export format for spreadsheets and databases. CSV format was used for many
+years prior to attempts to describe the format in a standardized way in
+:rfc:`4180`. The lack of a well-defined standard means that subtle differences
+often exist in the data produced and consumed by different applications. These
+differences can make it annoying to process CSV files from multiple sources.
+Still, while the delimiters and quoting characters vary, the overall format is
+similar enough that it is possible to write a single module which can
+efficiently manipulate such data, hiding the details of reading and writing the
+data from the programmer.
The :mod:`csv` module implements classes to read and write tabular data in CSV
format. It allows programmers to say, "write this data in the format preferred
diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst
index fce5913..8a22e94 100644
--- a/Doc/library/ctypes.rst
+++ b/Doc/library/ctypes.rst
@@ -39,9 +39,14 @@ loads libraries which export functions using the standard ``cdecl`` calling
convention, while *windll* libraries call functions using the ``stdcall``
calling convention. *oledll* also uses the ``stdcall`` calling convention, and
assumes the functions return a Windows :c:type:`HRESULT` error code. The error
-code is used to automatically raise a :class:`WindowsError` exception when the
+code is used to automatically raise a :class:`OSError` exception when the
function call fails.
+.. versionchanged:: 3.3
+ Windows errors used to raise :exc:`WindowsError`, which is now an alias
+ of :exc:`OSError`.
+
+
Here are some examples for Windows. Note that ``msvcrt`` is the MS standard C
library containing most standard C functions, and uses the cdecl calling
convention::
@@ -189,7 +194,7 @@ argument values::
>>> windll.kernel32.GetModuleHandleA(32) # doctest: +WINDOWS
Traceback (most recent call last):
File "<stdin>", line 1, in ?
- WindowsError: exception: access violation reading 0x00000020
+ OSError: exception: access violation reading 0x00000020
>>>
There are, however, enough ways to crash Python with :mod:`ctypes`, so you
@@ -491,7 +496,7 @@ useful to check for error return values and automatically raise an exception::
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 3, in ValidHandle
- WindowsError: [Errno 126] The specified module could not be found.
+ OSError: [Errno 126] The specified module could not be found.
>>>
``WinError`` is a function which will call Windows ``FormatMessage()`` api to
@@ -1345,7 +1350,10 @@ way is to instantiate one of the following classes:
assumed to return the windows specific :class:`HRESULT` code. :class:`HRESULT`
values contain information specifying whether the function call failed or
succeeded, together with additional error code. If the return value signals a
- failure, an :class:`WindowsError` is automatically raised.
+ failure, an :class:`OSError` is automatically raised.
+
+ .. versionchanged:: 3.3
+ :exc:`WindowsError` used to be raised.
.. class:: WinDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False)
@@ -1966,11 +1974,14 @@ Utility functions
.. function:: WinError(code=None, descr=None)
Windows only: this function is probably the worst-named thing in ctypes. It
- creates an instance of WindowsError. If *code* is not specified,
+ creates an instance of OSError. If *code* is not specified,
``GetLastError`` is called to determine the error code. If *descr* is not
specified, :func:`FormatError` is called to get a textual description of the
error.
+ .. versionchanged:: 3.3
+ An instance of :exc:`WindowsError` used to be created.
+
.. function:: wstring_at(address, size=-1)
diff --git a/Doc/library/curses.rst b/Doc/library/curses.rst
index f31b9c5..ff3a793 100644
--- a/Doc/library/curses.rst
+++ b/Doc/library/curses.rst
@@ -598,6 +598,17 @@ The module :mod:`curses` defines the following functions:
Only one *ch* can be pushed before :meth:`getch` is called.
+.. function:: unget_wch(ch)
+
+ Push *ch* so the next :meth:`get_wch` will return it.
+
+ .. note::
+
+ Only one *ch* can be pushed before :meth:`get_wch` is called.
+
+ .. versionadded:: 3.3
+
+
.. function:: ungetmouse(id, x, y, z, bstate)
Push a :const:`KEY_MOUSE` event onto the input queue, associating the given
@@ -642,7 +653,7 @@ Window Objects
--------------
Window objects, as returned by :func:`initscr` and :func:`newwin` above, have
-the following methods:
+the following methods and attributes:
.. method:: window.addch([y, x,] ch[, attr])
@@ -823,6 +834,16 @@ the following methods:
event.
+.. attribute:: window.encoding
+
+ Encoding used to encode method arguments (Unicode strings and characters).
+ The encoding attribute is inherited from by parent window when a subwindow
+ is created, for example with :meth:`window.subwin`. By default, the locale
+ encoding is used (see :func:`locale.getpreferredencoding`).
+
+ .. versionadded:: 3.3
+
+
.. method:: window.erase()
Clear the window.
@@ -846,6 +867,14 @@ the following methods:
until a key is pressed.
+.. method:: window.get_wch([y, x])
+
+ Get a wide character. Like :meth:`getch`, but the integer returned is the
+ Unicode code point for the key pressed, so it can be passed to :func:`chr`.
+
+ .. versionadded:: 3.3
+
+
.. method:: window.getkey([y, x])
Get a character, returning a string instead of an integer, as :meth:`getch`
diff --git a/Doc/library/datatypes.rst b/Doc/library/datatypes.rst
index 6b4a71a..8e33c1f 100644
--- a/Doc/library/datatypes.rst
+++ b/Doc/library/datatypes.rst
@@ -21,6 +21,7 @@ The following modules are documented in this chapter:
datetime.rst
calendar.rst
collections.rst
+ collections.abc.rst
heapq.rst
bisect.rst
array.rst
diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst
index 1f4cfba..a6c1997 100644
--- a/Doc/library/datetime.rst
+++ b/Doc/library/datetime.rst
@@ -721,6 +721,22 @@ Other constructors, all class methods:
It's common for this to be restricted to years in 1970 through 2038. See also
:meth:`fromtimestamp`.
+ On the POSIX compliant platforms, ``utcfromtimestamp(timestamp)``
+ is equivalent to the following expression::
+
+ datetime(1970, 1, 1) + timedelta(seconds=timestamp)
+
+ There is no method to obtain the timestamp from a :class:`datetime`
+ instance, but POSIX timestamp corresponding to a :class:`datetime`
+ instance ``dt`` can be easily calculated as follows. For a naive
+ ``dt``::
+
+ timestamp = (dt - datetime(1970, 1, 1)) / timedelta(seconds=1)
+
+ And for an aware ``dt``::
+
+ timestamp = (dt - datetime(1970, 1, 1, tzinfo=timezone.utc)) / timedelta(seconds=1)
+
.. classmethod:: datetime.fromordinal(ordinal)
@@ -1564,11 +1580,12 @@ only EST (fixed offset -5 hours), or only EDT (fixed offset -4 hours)).
:class:`timezone` Objects
--------------------------
-A :class:`timezone` object represents a timezone that is defined by a
-fixed offset from UTC. Note that objects of this class cannot be used
-to represent timezone information in the locations where different
-offsets are used in different days of the year or where historical
-changes have been made to civil time.
+The :class:`timezone` class is a subclass of :class:`tzinfo`, each
+instance of which represents a timezone defined by a fixed offset from
+UTC. Note that objects of this class cannot be used to represent
+timezone information in the locations where different offsets are used
+in different days of the year or where historical changes have been
+made to civil time.
.. class:: timezone(offset[, name])
@@ -1737,8 +1754,7 @@ format codes.
| | decimal number [00,99]. | |
+-----------+--------------------------------+-------+
| ``%Y`` | Year with century as a decimal | \(5) |
-| | number [0001,9999] (strptime), | |
-| | [1000,9999] (strftime). | |
+| | number [0001,9999]. | |
+-----------+--------------------------------+-------+
| ``%z`` | UTC offset in the form +HHMM | \(6) |
| | or -HHMM (empty string if the | |
@@ -1772,10 +1788,7 @@ Notes:
calculations when the day of the week and the year are specified.
(5)
- For technical reasons, :meth:`strftime` method does not support
- dates before year 1000: ``t.strftime(format)`` will raise a
- :exc:`ValueError` when ``t.year < 1000`` even if ``format`` does
- not contain ``%Y`` directive. The :meth:`strptime` method can
+ The :meth:`strptime` method can
parse years in the full [1, 9999] range, but years < 1000 must be
zero-filled to 4-digit width.
@@ -1783,6 +1796,10 @@ Notes:
In previous versions, :meth:`strftime` method was restricted to
years >= 1900.
+ .. versionchanged:: 3.3
+ In version 3.2, :meth:`strftime` method was restricted to
+ years >= 1000.
+
(6)
For example, if :meth:`utcoffset` returns ``timedelta(hours=-3, minutes=-30)``,
``%z`` is replaced with the string ``'-0330'``.
diff --git a/Doc/library/debug.rst b/Doc/library/debug.rst
index b2ee4fa..c69fb1c 100644
--- a/Doc/library/debug.rst
+++ b/Doc/library/debug.rst
@@ -10,7 +10,8 @@ allowing you to identify bottlenecks in your programs.
.. toctree::
bdb.rst
+ faulthandler.rst
pdb.rst
profile.rst
timeit.rst
- trace.rst \ No newline at end of file
+ trace.rst
diff --git a/Doc/library/depgraph-output.png b/Doc/library/depgraph-output.png
new file mode 100644
index 0000000..960bb1b
--- /dev/null
+++ b/Doc/library/depgraph-output.png
Binary files differ
diff --git a/Doc/library/difflib.rst b/Doc/library/difflib.rst
index bdc37b3..836e240 100644
--- a/Doc/library/difflib.rst
+++ b/Doc/library/difflib.rst
@@ -752,8 +752,8 @@ It is also contained in the Python source distribution, as
# we're passing these as arguments to the diff function
fromdate = time.ctime(os.stat(fromfile).st_mtime)
todate = time.ctime(os.stat(tofile).st_mtime)
- fromlines = open(fromfile, 'U').readlines()
- tolines = open(tofile, 'U').readlines()
+ with open(fromlines) as fromf, open(tofile) as tof:
+ fromlines, tolines = list(fromf), list(tof)
if options.u:
diff = difflib.unified_diff(fromlines, tolines, fromfile, tofile,
diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst
index 79cc583..cb429c8 100644
--- a/Doc/library/dis.rst
+++ b/Doc/library/dis.rst
@@ -171,11 +171,6 @@ The Python compiler currently generates the following bytecode instructions.
**General instructions**
-.. opcode:: STOP_CODE
-
- Indicates end-of-code to the compiler, not used by the interpreter.
-
-
.. opcode:: NOP
Do nothing code. Used as a placeholder by the bytecode optimizer.
diff --git a/Doc/library/distutils.rst b/Doc/library/distutils.rst
index 238b79d..53a69ae 100644
--- a/Doc/library/distutils.rst
+++ b/Doc/library/distutils.rst
@@ -12,18 +12,26 @@ additional modules into a Python installation. The new modules may be either
100%-pure Python, or may be extension modules written in C, or may be
collections of Python packages which include modules coded in both Python and C.
-This package is discussed in two separate chapters:
+.. deprecated:: 3.3
+ :mod:`packaging` replaces Distutils. See :ref:`packaging-index` and
+ :ref:`packaging-install-index`.
+User documentation and API reference are provided in another document:
+
.. seealso::
:ref:`distutils-index`
The manual for developers and packagers of Python modules. This describes
how to prepare :mod:`distutils`\ -based packages so that they may be
- easily installed into an existing Python installation.
+ easily installed into an existing Python installation. If also contains
+ instructions for end-users wanting to install a distutils-based package,
+ :ref:`install-index`.
+
+
+.. trick to silence a Sphinx warning
- :ref:`install-index`
- An "administrators" manual which includes information on installing
- modules into an existing Python installation. You do not need to be a
- Python programmer to read this manual.
+.. toctree::
+ :hidden:
+ ../distutils/index
diff --git a/Doc/library/email.generator.rst b/Doc/library/email.generator.rst
index 85b32fe..847d7e4 100644
--- a/Doc/library/email.generator.rst
+++ b/Doc/library/email.generator.rst
@@ -32,7 +32,8 @@ Here are the public methods of the :class:`Generator` class, imported from the
:mod:`email.generator` module:
-.. class:: Generator(outfp, mangle_from_=True, maxheaderlen=78)
+.. class:: Generator(outfp, mangle_from_=True, maxheaderlen=78, *, \
+ policy=policy.default)
The constructor for the :class:`Generator` class takes a :term:`file-like object`
called *outfp* for an argument. *outfp* must support the :meth:`write` method
@@ -53,10 +54,16 @@ Here are the public methods of the :class:`Generator` class, imported from the
:class:`~email.header.Header` class. Set to zero to disable header wrapping.
The default is 78, as recommended (but not required) by :rfc:`2822`.
+ The *policy* keyword specifies a :mod:`~email.policy` object that controls a
+ number of aspects of the generator's operation. The default policy
+ maintains backward compatibility.
+
+ .. versionchanged:: 3.3 Added the *policy* keyword.
+
The other public :class:`Generator` methods are:
- .. method:: flatten(msg, unixfrom=False, linesep='\\n')
+ .. method:: flatten(msg, unixfrom=False, linesep=None)
Print the textual representation of the message object structure rooted at
*msg* to the output file specified when the :class:`Generator` instance
@@ -72,12 +79,13 @@ Here are the public methods of the :class:`Generator` class, imported from the
Note that for subparts, no envelope header is ever printed.
Optional *linesep* specifies the line separator character used to
- terminate lines in the output. It defaults to ``\n`` because that is
- the most useful value for Python application code (other library packages
- expect ``\n`` separated lines). ``linesep=\r\n`` can be used to
- generate output with RFC-compliant line separators.
+ terminate lines in the output. If specified it overrides the value
+ specified by the ``Generator``\'s ``policy``.
- Messages parsed with a Bytes parser that have a
+ Because strings cannot represent non-ASCII bytes, ``Generator`` ignores
+ the value of the :attr:`~email.policy.Policy.must_be_7bit`
+ :mod:`~email.policy` setting and operates as if it were set ``True``.
+ This means that messages parsed with a Bytes parser that have a
:mailheader:`Content-Transfer-Encoding` of 8bit will be converted to a
use a 7bit Content-Transfer-Encoding. Non-ASCII bytes in the headers
will be :rfc:`2047` encoded with a charset of `unknown-8bit`.
@@ -103,7 +111,8 @@ As a convenience, see the :class:`~email.message.Message` methods
formatted string representation of a message object. For more detail, see
:mod:`email.message`.
-.. class:: BytesGenerator(outfp, mangle_from_=True, maxheaderlen=78)
+.. class:: BytesGenerator(outfp, mangle_from_=True, maxheaderlen=78, *, \
+ policy=policy.default)
The constructor for the :class:`BytesGenerator` class takes a binary
:term:`file-like object` called *outfp* for an argument. *outfp* must
@@ -125,19 +134,31 @@ formatted string representation of a message object. For more detail, see
wrapping. The default is 78, as recommended (but not required) by
:rfc:`2822`.
+ The *policy* keyword specifies a :mod:`~email.policy` object that controls a
+ number of aspects of the generator's operation. The default policy
+ maintains backward compatibility.
+
+ .. versionchanged:: 3.3 Added the *policy* keyword.
+
The other public :class:`BytesGenerator` methods are:
- .. method:: flatten(msg, unixfrom=False, linesep='\n')
+ .. method:: flatten(msg, unixfrom=False, linesep=None)
Print the textual representation of the message object structure rooted
at *msg* to the output file specified when the :class:`BytesGenerator`
instance was created. Subparts are visited depth-first and the resulting
- text will be properly MIME encoded. If the input that created the *msg*
- contained bytes with the high bit set and those bytes have not been
- modified, they will be copied faithfully to the output, even if doing so
- is not strictly RFC compliant. (To produce strictly RFC compliant
- output, use the :class:`Generator` class.)
+ text will be properly MIME encoded. If the :mod:`~email.policy` option
+ :attr:`~email.policy.Policy.must_be_7bit` is ``False`` (the default),
+ then any bytes with the high bit set in the original parsed message that
+ have not been modified will be copied faithfully to the output. If
+ ``must_be_7bit`` is true, the bytes will be converted as needed using an
+ ASCII content-transfer-encoding. In particular, RFC-invalid non-ASCII
+ bytes in headers will be encoded using the MIME ``unknown-8bit``
+ character set, thus rendering them RFC-compliant.
+
+ .. XXX: There should be a complementary option that just does the RFC
+ compliance transformation but leaves CTE 8bit parts alone.
Messages parsed with a Bytes parser that have a
:mailheader:`Content-Transfer-Encoding` of 8bit will be reconstructed
@@ -152,10 +173,8 @@ formatted string representation of a message object. For more detail, see
Note that for subparts, no envelope header is ever printed.
Optional *linesep* specifies the line separator character used to
- terminate lines in the output. It defaults to ``\n`` because that is
- the most useful value for Python application code (other library packages
- expect ``\n`` separated lines). ``linesep=\r\n`` can be used to
- generate output with RFC-compliant line separators.
+ terminate lines in the output. If specified it overrides the value
+ specified by the ``Generator``\ 's ``policy``.
.. method:: clone(fp)
diff --git a/Doc/library/email.parser.rst b/Doc/library/email.parser.rst
index 77a0b69..e70b429 100644
--- a/Doc/library/email.parser.rst
+++ b/Doc/library/email.parser.rst
@@ -58,12 +58,18 @@ list of defects that it can find.
Here is the API for the :class:`FeedParser`:
-.. class:: FeedParser(_factory=email.message.Message)
+.. class:: FeedParser(_factory=email.message.Message, *, policy=policy.default)
Create a :class:`FeedParser` instance. Optional *_factory* is a no-argument
callable that will be called whenever a new message object is needed. It
defaults to the :class:`email.message.Message` class.
+ The *policy* keyword specifies a :mod:`~email.policy` object that controls a
+ number of aspects of the parser's operation. The default policy maintains
+ backward compatibility.
+
+ .. versionchanged:: 3.3 Added the *policy* keyword.
+
.. method:: feed(data)
Feed the :class:`FeedParser` some more data. *data* should be a string
@@ -94,15 +100,17 @@ Parser class API
The :class:`Parser` class, imported from the :mod:`email.parser` module,
provides an API that can be used to parse a message when the complete contents
of the message are available in a string or file. The :mod:`email.parser`
-module also provides a second class, called :class:`HeaderParser` which can be
-used if you're only interested in the headers of the message.
-:class:`HeaderParser` can be much faster in these situations, since it does not
-attempt to parse the message body, instead setting the payload to the raw body
-as a string. :class:`HeaderParser` has the same API as the :class:`Parser`
-class.
+module also provides header-only parsers, called :class:`HeaderParser` and
+:class:`BytesHeaderParser`, which can be used if you're only interested in the
+headers of the message. :class:`HeaderParser` and :class:`BytesHeaderParser`
+can be much faster in these situations, since they do not attempt to parse the
+message body, instead setting the payload to the raw body as a string. They
+have the same API as the :class:`Parser` and :class:`BytesParser` classes.
+.. versionadded:: 3.3 BytesHeaderParser
-.. class:: Parser(_class=email.message.Message, strict=None)
+
+.. class:: Parser(_class=email.message.Message, *, policy=policy.default)
The constructor for the :class:`Parser` class takes an optional argument
*_class*. This must be a callable factory (such as a function or a class), and
@@ -110,13 +118,13 @@ class.
:class:`~email.message.Message` (see :mod:`email.message`). The factory will
be called without arguments.
- The optional *strict* flag is ignored.
+ The *policy* keyword specifies a :mod:`~email.policy` object that controls a
+ number of aspects of the parser's operation. The default policy maintains
+ backward compatibility.
- .. deprecated:: 2.4
- Because the :class:`Parser` class is a backward compatible API wrapper
- around the new-in-Python 2.4 :class:`FeedParser`, *all* parsing is
- effectively non-strict. You should simply stop passing a *strict* flag to
- the :class:`Parser` constructor.
+ .. versionchanged:: 3.3
+ Removed the *strict* argument that was deprecated in 2.4. Added the
+ *policy* keyword.
The other public :class:`Parser` methods are:
@@ -147,12 +155,18 @@ class.
the entire contents of the file.
-.. class:: BytesParser(_class=email.message.Message, strict=None)
+.. class:: BytesParser(_class=email.message.Message, *, policy=policy.default)
This class is exactly parallel to :class:`Parser`, but handles bytes input.
The *_class* and *strict* arguments are interpreted in the same way as for
- the :class:`Parser` constructor. *strict* is supported only to make porting
- code easier; it is deprecated.
+ the :class:`Parser` constructor.
+
+ The *policy* keyword specifies a :mod:`~email.policy` object that
+ controls a number of aspects of the parser's operation. The default
+ policy maintains backward compatibility.
+
+ .. versionchanged:: 3.3
+ Removed the *strict* argument. Added the *policy* keyword.
.. method:: parse(fp, headeronly=False)
@@ -190,34 +204,48 @@ in the top-level :mod:`email` package namespace.
.. currentmodule:: email
-.. function:: message_from_string(s, _class=email.message.Message, strict=None)
+.. function:: message_from_string(s, _class=email.message.Message, *, \
+ policy=policy.default)
Return a message object structure from a string. This is exactly equivalent to
- ``Parser().parsestr(s)``. Optional *_class* and *strict* are interpreted as
+ ``Parser().parsestr(s)``. *_class* and *policy* are interpreted as
with the :class:`Parser` class constructor.
-.. function:: message_from_bytes(s, _class=email.message.Message, strict=None)
+ .. versionchanged:: 3.3
+ Removed the *strict* argument. Added the *policy* keyword.
+
+.. function:: message_from_bytes(s, _class=email.message.Message, *, \
+ policy=policy.default)
Return a message object structure from a byte string. This is exactly
equivalent to ``BytesParser().parsebytes(s)``. Optional *_class* and
*strict* are interpreted as with the :class:`Parser` class constructor.
.. versionadded:: 3.2
+ .. versionchanged:: 3.3
+ Removed the *strict* argument. Added the *policy* keyword.
-.. function:: message_from_file(fp, _class=email.message.Message, strict=None)
+.. function:: message_from_file(fp, _class=email.message.Message, *, \
+ policy=policy.default)
Return a message object structure tree from an open :term:`file object`.
- This is exactly equivalent to ``Parser().parse(fp)``. Optional *_class*
- and *strict* are interpreted as with the :class:`Parser` class constructor.
+ This is exactly equivalent to ``Parser().parse(fp)``. *_class*
+ and *policy* are interpreted as with the :class:`Parser` class constructor.
+
+ .. versionchanged::
+ Removed the *strict* argument. Added the *policy* keyword.
-.. function:: message_from_binary_file(fp, _class=email.message.Message, strict=None)
+.. function:: message_from_binary_file(fp, _class=email.message.Message, *, \
+ policy=policy.default)
Return a message object structure tree from an open binary :term:`file
object`. This is exactly equivalent to ``BytesParser().parse(fp)``.
- Optional *_class* and *strict* are interpreted as with the :class:`Parser`
+ *_class* and *policy* are interpreted as with the :class:`Parser`
class constructor.
.. versionadded:: 3.2
+ .. versionchanged:: 3.3
+ Removed the *strict* argument. Added the *policy* keyword.
Here's an example of how you might use this at an interactive Python prompt::
diff --git a/Doc/library/email.policy.rst b/Doc/library/email.policy.rst
new file mode 100644
index 0000000..d9a292c
--- /dev/null
+++ b/Doc/library/email.policy.rst
@@ -0,0 +1,184 @@
+:mod:`email`: Policy Objects
+----------------------------
+
+.. module:: email.policy
+ :synopsis: Controlling the parsing and generating of messages
+
+.. versionadded:: 3.3
+
+
+The :mod:`email` package's prime focus is the handling of email messages as
+described by the various email and MIME RFCs. However, the general format of
+email messages (a block of header fields each consisting of a name followed by
+a colon followed by a value, the whole block followed by a blank line and an
+arbitrary 'body'), is a format that has found utility outside of the realm of
+email. Some of these uses conform fairly closely to the main RFCs, some do
+not. And even when working with email, there are times when it is desirable to
+break strict compliance with the RFCs.
+
+Policy objects give the email package the flexibility to handle all these
+disparate use cases.
+
+A :class:`Policy` object encapsulates a set of attributes and methods that
+control the behavior of various components of the email package during use.
+:class:`Policy` instances can be passed to various classes and methods in the
+email package to alter the default behavior. The settable values and their
+defaults are described below. The :mod:`policy` module also provides some
+pre-created :class:`Policy` instances. In addition to a :const:`default`
+instance, there are instances tailored for certain applications. For example
+there is an :const:`SMTP` :class:`Policy` with defaults appropriate for
+generating output to be sent to an SMTP server. These are listed `below
+<Policy Instances>`.
+
+In general an application will only need to deal with setting the policy at the
+input and output boundaries. Once parsed, a message is represented by a
+:class:`~email.message.Message` object, which is designed to be independent of
+the format that the message has "on the wire" when it is received, transmitted,
+or displayed. Thus, a :class:`Policy` can be specified when parsing a message
+to create a :class:`~email.message.Message`, and again when turning the
+:class:`~email.message.Message` into some other representation. While often a
+program will use the same :class:`Policy` for both input and output, the two
+can be different.
+
+As an example, the following code could be used to read an email message from a
+file on disk and pass it to the system ``sendmail`` program on a Unix system::
+
+ >>> from email import msg_from_binary_file
+ >>> from email.generator import BytesGenerator
+ >>> import email.policy
+ >>> from subprocess import Popen, PIPE
+ >>> with open('mymsg.txt', 'b') as f:
+ ... msg = msg_from_binary_file(f, policy=email.policy.mbox)
+ >>> p = Popen(['sendmail', msg['To'][0].address], stdin=PIPE)
+ >>> g = BytesGenerator(p.stdin, policy=email.policy.SMTP)
+ >>> g.flatten(msg)
+ >>> p.stdin.close()
+ >>> rc = p.wait()
+
+.. XXX email.policy.mbox/MBOX does not exist yet
+
+Some email package methods accept a *policy* keyword argument, allowing the
+policy to be overridden for that method. For example, the following code uses
+the :meth:`~email.message.Message.as_string` method of the *msg* object from the
+previous example and re-write it to a file using the native line separators for
+the platform on which it is running::
+
+ >>> import os
+ >>> mypolicy = email.policy.Policy(linesep=os.linesep)
+ >>> with open('converted.txt', 'wb') as f:
+ ... f.write(msg.as_string(policy=mypolicy))
+
+Policy instances are immutable, but they can be cloned, accepting the same
+keyword arguments as the class constructor and returning a new :class:`Policy`
+instance that is a copy of the original but with the specified attributes
+values changed. For example, the following creates an SMTP policy that will
+raise any defects detected as errors::
+
+ >>> strict_SMTP = email.policy.SMTP.clone(raise_on_defect=True)
+
+Policy objects can also be combined using the addition operator, producing a
+policy object whose settings are a combination of the non-default values of the
+summed objects::
+
+ >>> strict_SMTP = email.policy.SMTP + email.policy.strict
+
+This operation is not commutative; that is, the order in which the objects are
+added matters. To illustrate::
+
+ >>> Policy = email.policy.Policy
+ >>> apolicy = Policy(max_line_length=100) + Policy(max_line_length=80)
+ >>> apolicy.max_line_length
+ 80
+ >>> apolicy = Policy(max_line_length=80) + Policy(max_line_length=100)
+ >>> apolicy.max_line_length
+ 100
+
+
+.. class:: Policy(**kw)
+
+ The valid constructor keyword arguments are any of the attributes listed
+ below.
+
+ .. attribute:: max_line_length
+
+ The maximum length of any line in the serialized output, not counting the
+ end of line character(s). Default is 78, per :rfc:`5322`. A value of
+ ``0`` or :const:`None` indicates that no line wrapping should be
+ done at all.
+
+ .. attribute:: linesep
+
+ The string to be used to terminate lines in serialized output. The
+ default is ``\n`` because that's the internal end-of-line discipline used
+ by Python, though ``\r\n`` is required by the RFCs. See `Policy
+ Instances`_ for policies that use an RFC conformant linesep. Setting it
+ to :attr:`os.linesep` may also be useful.
+
+ .. attribute:: must_be_7bit
+
+ If ``True``, data output by a bytes generator is limited to ASCII
+ characters. If :const:`False` (the default), then bytes with the high
+ bit set are preserved and/or allowed in certain contexts (for example,
+ where possible a content transfer encoding of ``8bit`` will be used).
+ String generators act as if ``must_be_7bit`` is ``True`` regardless of
+ the policy in effect, since a string cannot represent non-ASCII bytes.
+
+ .. attribute:: raise_on_defect
+
+ If :const:`True`, any defects encountered will be raised as errors. If
+ :const:`False` (the default), defects will be passed to the
+ :meth:`register_defect` method.
+
+ :mod:`Policy` object also have the following methods:
+
+ .. method:: handle_defect(obj, defect)
+
+ *obj* is the object on which to register the defect. *defect* should be
+ an instance of a subclass of :class:`~email.errors.Defect`.
+ If :attr:`raise_on_defect`
+ is ``True`` the defect is raised as an exception. Otherwise *obj* and
+ *defect* are passed to :meth:`register_defect`. This method is intended
+ to be called by parsers when they encounter defects, and will not be
+ called by code that uses the email library unless that code is
+ implementing an alternate parser.
+
+ .. method:: register_defect(obj, defect)
+
+ *obj* is the object on which to register the defect. *defect* should be
+ a subclass of :class:`~email.errors.Defect`. This method is part of the
+ public API so that custom ``Policy`` subclasses can implement alternate
+ handling of defects. The default implementation calls the ``append``
+ method of the ``defects`` attribute of *obj*.
+
+ .. method:: clone(obj, *kw)
+
+ Return a new :class:`Policy` instance whose attributes have the same
+ values as the current instance, except where those attributes are
+ given new values by the keyword arguments.
+
+
+Policy Instances
+^^^^^^^^^^^^^^^^
+
+The following instances of :class:`Policy` provide defaults suitable for
+specific common application domains.
+
+.. data:: default
+
+ An instance of :class:`Policy` with all defaults unchanged.
+
+.. data:: SMTP
+
+ Output serialized from a message will conform to the email and SMTP
+ RFCs. The only changed attribute is :attr:`linesep`, which is set to
+ ``\r\n``.
+
+.. data:: HTTP
+
+ Suitable for use when serializing headers for use in HTTP traffic.
+ :attr:`linesep` is set to ``\r\n``, and :attr:`max_line_length` is set to
+ :const:`None` (unlimited).
+
+.. data:: strict
+
+ :attr:`raise_on_defect` is set to :const:`True`.
diff --git a/Doc/library/email.rst b/Doc/library/email.rst
index 4530b95..fc206f4 100644
--- a/Doc/library/email.rst
+++ b/Doc/library/email.rst
@@ -51,6 +51,7 @@ Contents of the :mod:`email` package documentation:
email.message.rst
email.parser.rst
email.generator.rst
+ email.policy.rst
email.mime.rst
email.header.rst
email.charset.rst
diff --git a/Doc/library/email.util.rst b/Doc/library/email.util.rst
index f7b777a..2f9ef89 100644
--- a/Doc/library/email.util.rst
+++ b/Doc/library/email.util.rst
@@ -29,13 +29,20 @@ There are several useful utilities provided in the :mod:`email.utils` module:
fails, in which case a 2-tuple of ``('', '')`` is returned.
-.. function:: formataddr(pair)
+.. function:: formataddr(pair, charset='utf-8')
The inverse of :meth:`parseaddr`, this takes a 2-tuple of the form ``(realname,
email_address)`` and returns the string value suitable for a :mailheader:`To` or
:mailheader:`Cc` header. If the first element of *pair* is false, then the
second element is returned unmodified.
+ Optional *charset* is the character set that will be used in the :rfc:`2047`
+ encoding of the ``realname`` if the ``realname`` contains non-ASCII
+ characters. Can be an instance of :class:`str` or a
+ :class:`~email.charset.Charset`. Defaults to ``utf-8``.
+
+ .. versionchanged: 3.3 added the *charset* option
+
.. function:: getaddresses(fieldvalues)
@@ -74,6 +81,20 @@ There are several useful utilities provided in the :mod:`email.utils` module:
indexes 6, 7, and 8 of the result tuple are not usable.
+.. function:: parsedate_to_datetime(date)
+
+ The inverse of :func:`format_datetime`. Performs the same function as
+ :func:`parsedate`, but on success returns a :mod:`~datetime.datetime`. If
+ the input date has a timezone of ``-0000``, the ``datetime`` will be a naive
+ ``datetime``, and if the date is conforming to the RFCs it will represent a
+ time in UTC but with no indication of the actual source timezone of the
+ message the date comes from. If the input date has any other valid timezone
+ offset, the ``datetime`` will be an aware ``datetime`` with the
+ corresponding a :class:`~datetime.timezone` :class:`~datetime.tzinfo`.
+
+ .. versionadded:: 3.3
+
+
.. function:: mktime_tz(tuple)
Turn a 10-tuple as returned by :func:`parsedate_tz` into a UTC timestamp. It
@@ -105,6 +126,20 @@ There are several useful utilities provided in the :mod:`email.utils` module:
``False``. The default is ``False``.
+.. function:: format_datetime(dt, usegmt=False)
+
+ Like ``formatdate``, but the input is a :mod:`datetime` instance. If it is
+ a naive datetime, it is assumed to be "UTC with no information about the
+ source timezone", and the conventional ``-0000`` is used for the timezone.
+ If it is an aware ``datetime``, then the numeric timezone offset is used.
+ If it is an aware timezone with offset zero, then *usegmt* may be set to
+ ``True``, in which case the string ``GMT`` is used instead of the numeric
+ timezone offset. This provides a way to generate standards conformant HTTP
+ date headers.
+
+ .. versionadded:: 3.3
+
+
.. function:: make_msgid(idstring=None, domain=None)
Returns a string suitable for an :rfc:`2822`\ -compliant
diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst
index ca3ad3e..49cec1e 100644
--- a/Doc/library/exceptions.rst
+++ b/Doc/library/exceptions.rst
@@ -34,6 +34,10 @@ programmers are encouraged to at least derive new exceptions from the
defining exceptions is available in the Python Tutorial under
:ref:`tut-userexceptions`.
+
+Base classes
+------------
+
The following exceptions are used mostly as base classes for other exceptions.
.. exception:: BaseException
@@ -90,27 +94,8 @@ The following exceptions are used mostly as base classes for other exceptions.
can be raised directly by :func:`codecs.lookup`.
-.. exception:: EnvironmentError
-
- The base class for exceptions that can occur outside the Python system:
- :exc:`IOError`, :exc:`OSError`. When exceptions of this type are created with a
- 2-tuple, the first item is available on the instance's :attr:`errno` attribute
- (it is assumed to be an error number), and the second item is available on the
- :attr:`strerror` attribute (it is usually the associated error message). The
- tuple itself is also available on the :attr:`args` attribute.
-
- When an :exc:`EnvironmentError` exception is instantiated with a 3-tuple, the
- first two items are available as above, while the third item is available on the
- :attr:`filename` attribute. However, for backwards compatibility, the
- :attr:`args` attribute contains only a 2-tuple of the first two constructor
- arguments.
-
- The :attr:`filename` attribute is ``None`` when this exception is created with
- other than 3 arguments. The :attr:`errno` and :attr:`strerror` attributes are
- also ``None`` when the instance was created with other than 2 or 3 arguments.
- In this last case, :attr:`args` contains the verbatim constructor arguments as a
- tuple.
-
+Concrete exceptions
+-------------------
The following exceptions are the exceptions that are usually raised.
@@ -151,16 +136,6 @@ The following exceptions are the exceptions that are usually raised.
it is technically not an error.
-.. exception:: IOError
-
- Raised when an I/O operation (such as the built-in :func:`print` or
- :func:`open` functions or a method of a :term:`file object`) fails for an
- I/O-related reason, e.g., "file not found" or "disk full".
-
- This class is derived from :exc:`EnvironmentError`. See the discussion above
- for more information on exception instance attributes.
-
-
.. exception:: ImportError
Raised when an :keyword:`import` statement fails to find the module definition
@@ -221,17 +196,30 @@ The following exceptions are the exceptions that are usually raised.
.. index:: module: errno
- This exception is derived from :exc:`EnvironmentError`. It is raised when a
- function returns a system-related error (not for illegal argument types or
- other incidental errors). The :attr:`errno` attribute is a numeric error
- code from :c:data:`errno`, and the :attr:`strerror` attribute is the
- corresponding string, as would be printed by the C function :c:func:`perror`.
- See the module :mod:`errno`, which contains names for the error codes defined
- by the underlying operating system.
+ This exception is raised when a system function returns a system-related
+ error, including I/O failures such as "file not found" or "disk full"
+ (not for illegal argument types or other incidental errors). Often a
+ subclass of :exc:`OSError` will actually be raised as described in
+ `OS exceptions`_ below. The :attr:`errno` attribute is a numeric error
+ code from the C variable :c:data:`errno`.
+
+ Under Windows, the :attr:`winerror` attribute gives you the native
+ Windows error code. The :attr:`errno` attribute is then an approximate
+ translation, in POSIX terms, of that native error code.
- For exceptions that involve a file system path (such as :func:`chdir` or
- :func:`unlink`), the exception instance will contain a third attribute,
- :attr:`filename`, which is the file name passed to the function.
+ Under all platforms, the :attr:`strerror` attribute is the corresponding
+ error message as provided by the operating system (as formatted by the C
+ functions :c:func:`perror` under POSIX, and :c:func:`FormatMessage`
+ Windows).
+
+ For exceptions that involve a file system path (such as :func:`open` or
+ :func:`os.unlink`), the exception instance will contain an additional
+ attribute, :attr:`filename`, which is the file name passed to the function.
+
+ .. versionchanged:: 3.3
+ :exc:`EnvironmentError`, :exc:`IOError`, :exc:`WindowsError`,
+ :exc:`VMSError`, :exc:`socket.error`, :exc:`select.error` and
+ :exc:`mmap.error` have been merged into :exc:`OSError`.
.. exception:: OverflowError
@@ -372,27 +360,142 @@ The following exceptions are the exceptions that are usually raised.
more precise exception such as :exc:`IndexError`.
-.. exception:: VMSError
+.. exception:: ZeroDivisionError
- Only available on VMS. Raised when a VMS-specific error occurs.
+ Raised when the second argument of a division or modulo operation is zero. The
+ associated value is a string indicating the type of the operands and the
+ operation.
+
+
+The following exceptions are kept for compatibility with previous versions;
+starting from Python 3.3, they are aliases of :exc:`OSError`.
+
+.. exception:: EnvironmentError
+
+.. exception:: IOError
+.. exception:: VMSError
+
+ Only available on VMS.
.. exception:: WindowsError
- Raised when a Windows-specific error occurs or when the error number does not
- correspond to an :c:data:`errno` value. The :attr:`winerror` and
- :attr:`strerror` values are created from the return values of the
- :c:func:`GetLastError` and :c:func:`FormatMessage` functions from the Windows
- Platform API. The :attr:`errno` value maps the :attr:`winerror` value to
- corresponding ``errno.h`` values. This is a subclass of :exc:`OSError`.
+ Only available on Windows.
-.. exception:: ZeroDivisionError
+OS exceptions
+^^^^^^^^^^^^^
+
+The following exceptions are subclasses of :exc:`OSError`, they get raised
+depending on the system error code.
+
+.. exception:: BlockingIOError
+
+ Raised when an operation would block on an object (e.g. socket) set
+ for non-blocking operation.
+ Corresponds to :c:data:`errno` ``EAGAIN``, ``EALREADY``,
+ ``EWOULDBLOCK`` and ``EINPROGRESS``.
+
+ In addition to those of :exc:`OSError`, :exc:`BlockingIOError` can have
+ one more attribute:
+
+ .. attribute:: characters_written
+
+ An integer containing the number of characters written to the stream
+ before it blocked. This attribute is available when using the
+ buffered I/O classes from the :mod:`io` module.
+
+.. exception:: ChildProcessError
+
+ Raised when an operation on a child process failed.
+ Corresponds to :c:data:`errno` ``ECHILD``.
+
+.. exception:: ConnectionError
+
+ A base class for connection-related issues. Subclasses are
+ :exc:`BrokenPipeError`, :exc:`ConnectionAbortedError`,
+ :exc:`ConnectionRefusedError` and :exc:`ConnectionResetError`.
+
+ .. exception:: BrokenPipeError
+
+ A subclass of :exc:`ConnectionError`, raised when trying to write on a
+ pipe while the other end has been closed, or trying to write on a socket
+ which has been shutdown for writing.
+ Corresponds to :c:data:`errno` ``EPIPE`` and ``ESHUTDOWN``.
+
+ .. exception:: ConnectionAbortedError
+
+ A subclass of :exc:`ConnectionError`, raised when a connection attempt
+ is aborted by the peer.
+ Corresponds to :c:data:`errno` ``ECONNABORTED``.
+
+ .. exception:: ConnectionRefusedError
+
+ A subclass of :exc:`ConnectionError`, raised when a connection attempt
+ is refused by the peer.
+ Corresponds to :c:data:`errno` ``ECONNREFUSED``.
+
+ .. exception:: ConnectionResetError
+
+ A subclass of :exc:`ConnectionError`, raised when a connection is
+ reset by the peer.
+ Corresponds to :c:data:`errno` ``ECONNRESET``.
+
+.. exception:: FileExistsError
+
+ Raised when trying to create a file or directory which already exists.
+ Corresponds to :c:data:`errno` ``EEXIST``.
+
+.. exception:: FileNotFoundError
+
+ Raised when a file or directory is requested but doesn't exist.
+ Corresponds to :c:data:`errno` ``ENOENT``.
+
+.. exception:: InterruptedError
+
+ Raised when a system call is interrupted by an incoming signal.
+ Corresponds to :c:data:`errno` ``EEINTR``.
+
+.. exception:: IsADirectoryError
+
+ Raised when a file operation (such as :func:`os.remove`) is requested
+ on a directory.
+ Corresponds to :c:data:`errno` ``EISDIR``.
+
+.. exception:: NotADirectoryError
+
+ Raised when a directory operation (such as :func:`os.listdir`) is requested
+ on something which is not a directory.
+ Corresponds to :c:data:`errno` ``ENOTDIR``.
+
+.. exception:: PermissionError
+
+ Raised when trying to run an operation without the adequate access
+ rights - for example filesystem permissions.
+ Corresponds to :c:data:`errno` ``EACCES`` and ``EPERM``.
+
+.. exception:: ProcessLookupError
+
+ Raised when a given process doesn't exist.
+ Corresponds to :c:data:`errno` ``ESRCH``.
+
+.. exception:: TimeoutError
+
+ Raised when a system function timed out at the system level.
+ Corresponds to :c:data:`errno` ``ETIMEDOUT``.
+
+.. versionadded:: 3.3
+ All the above :exc:`OSError` subclasses were added.
+
+
+.. seealso::
+
+ :pep:`3151` - Reworking the OS and IO exception hierarchy
+ PEP written and implemented by Antoine Pitrou.
- Raised when the second argument of a division or modulo operation is zero. The
- associated value is a string indicating the type of the operands and the
- operation.
+Warnings
+--------
The following exceptions are used as warning categories; see the :mod:`warnings`
module for more information.
diff --git a/Doc/library/faulthandler.rst b/Doc/library/faulthandler.rst
new file mode 100644
index 0000000..c9b9546
--- /dev/null
+++ b/Doc/library/faulthandler.rst
@@ -0,0 +1,136 @@
+:mod:`faulthandler` --- Dump the Python traceback
+=================================================
+
+.. module:: faulthandler
+ :synopsis: Dump the Python traceback.
+
+This module contains functions to dump Python tracebacks explicitly, on a fault,
+after a timeout, or on a user signal. Call :func:`faulthandler.enable` to
+install fault handlers for the :const:`SIGSEGV`, :const:`SIGFPE`,
+:const:`SIGABRT`, :const:`SIGBUS`, and :const:`SIGILL` signals. You can also
+enable them at startup by setting the :envvar:`PYTHONFAULTHANDLER` environment
+variable or by using :option:`-X` ``faulthandler`` command line option.
+
+The fault handler is compatible with system fault handlers like Apport or the
+Windows fault handler. The module uses an alternative stack for signal handlers
+if the :c:func:`sigaltstack` function is available. This allows it to dump the
+traceback even on a stack overflow.
+
+The fault handler is called on catastrophic cases and therefore can only use
+signal-safe functions (e.g. it cannot allocate memory on the heap). Because of
+this limitation traceback dumping is minimal compared to normal Python
+tracebacks:
+
+* Only ASCII is supported. The ``backslashreplace`` error handler is used on
+ encoding.
+* Each string is limited to 100 characters.
+* Only the filename, the function name and the line number are
+ displayed. (no source code)
+* It is limited to 100 frames and 100 threads.
+
+By default, the Python traceback is written to :data:`sys.stderr`. To see
+tracebacks, applications must be run in the terminal. A log file can
+alternatively be passed to :func:`faulthandler.enable`.
+
+The module is implemented in C, so tracebacks can be dumped on a crash or when
+Python is deadlocked.
+
+.. versionadded:: 3.3
+
+
+Dump the traceback
+------------------
+
+.. function:: dump_traceback(file=sys.stderr, all_threads=True)
+
+ Dump the tracebacks of all threads into *file*. If *all_threads* is
+ ``False``, dump only the current thread.
+
+
+Fault handler state
+-------------------
+
+.. function:: enable(file=sys.stderr, all_threads=True)
+
+ Enable the fault handler: install handlers for the :const:`SIGSEGV`,
+ :const:`SIGFPE`, :const:`SIGABRT`, :const:`SIGBUS` and :const:`SIGILL`
+ signals to dump the Python traceback. If *all_threads* is ``True``,
+ produce tracebacks for every running thread. Otherwise, dump only the current
+ thread.
+
+.. function:: disable()
+
+ Disable the fault handler: uninstall the signal handlers installed by
+ :func:`enable`.
+
+.. function:: is_enabled()
+
+ Check if the fault handler is enabled.
+
+
+Dump the tracebacks after a timeout
+-----------------------------------
+
+.. function:: dump_tracebacks_later(timeout, repeat=False, file=sys.stderr, exit=False)
+
+ Dump the tracebacks of all threads, after a timeout of *timeout* seconds, or
+ every *timeout* seconds if *repeat* is ``True``. If *exit* is ``True``, call
+ :c:func:`_exit` with status=1 after dumping the tracebacks. (Note
+ :c:func:`_exit` exits the process immediately, which means it doesn't do any
+ cleanup like flushing file buffers.) If the function is called twice, the new
+ call replaces previous parameters and resets the timeout. The timer has a
+ sub-second resolution.
+
+ This function is implemented using a watchdog thread and therefore is not
+ available if Python is compiled with threads disabled.
+
+.. function:: cancel_dump_tracebacks_later()
+
+ Cancel the last call to :func:`dump_tracebacks_later`.
+
+
+Dump the traceback on a user signal
+-----------------------------------
+
+.. function:: register(signum, file=sys.stderr, all_threads=True, chain=False)
+
+ Register a user signal: install a handler for the *signum* signal to dump
+ the traceback of all threads, or of the current thread if *all_threads* is
+ ``False``, into *file*. Call the previous handler if chain is ``True``.
+
+ Not available on Windows.
+
+.. function:: unregister(signum)
+
+ Unregister a user signal: uninstall the handler of the *signum* signal
+ installed by :func:`register`. Return ``True`` if the signal was registered,
+ ``False`` otherwise.
+
+ Not available on Windows.
+
+
+File descriptor issue
+---------------------
+
+:func:`enable`, :func:`dump_tracebacks_later` and :func:`register` keep the
+file descriptor of their *file* argument. If the file is closed and its file
+descriptor is reused by a new file, or if :func:`os.dup2` is used to replace
+the file descriptor, the traceback will be written into a different file. Call
+these functions again each time that the file is replaced.
+
+
+Example
+-------
+
+Example of a segmentation fault on Linux: ::
+
+ $ python -q -X faulthandler
+ >>> import ctypes
+ >>> ctypes.string_at(0)
+ Fatal Python error: Segmentation fault
+
+ Current thread 0x00007fb899f39700:
+ File "/home/python/cpython/Lib/ctypes/__init__.py", line 486 in string_at
+ File "<stdin>", line 1 in <module>
+ Segmentation fault
+
diff --git a/Doc/library/fcntl.rst b/Doc/library/fcntl.rst
index 6192400..9a9cdc1 100644
--- a/Doc/library/fcntl.rst
+++ b/Doc/library/fcntl.rst
@@ -19,6 +19,11 @@ argument. This can be an integer file descriptor, such as returned by
``sys.stdin.fileno()``, or a :class:`io.IOBase` object, such as ``sys.stdin``
itself, which provides a :meth:`fileno` that returns a genuine file descriptor.
+.. versionchanged:: 3.3
+ Operations in this module used to raise a :exc:`IOError` where they now
+ raise a :exc:`OSError`.
+
+
The module defines the following functions:
@@ -40,7 +45,7 @@ The module defines the following functions:
larger than 1024 bytes, this is most likely to result in a segmentation
violation or a more subtle data corruption.
- If the :c:func:`fcntl` fails, an :exc:`IOError` is raised.
+ If the :c:func:`fcntl` fails, an :exc:`OSError` is raised.
.. function:: ioctl(fd, op[, arg[, mutate_flag]])
@@ -107,7 +112,7 @@ The module defines the following functions:
When *operation* is :const:`LOCK_SH` or :const:`LOCK_EX`, it can also be
bitwise ORed with :const:`LOCK_NB` to avoid blocking on lock acquisition.
If :const:`LOCK_NB` is used and the lock cannot be acquired, an
- :exc:`IOError` will be raised and the exception will have an *errno*
+ :exc:`OSError` will be raised and the exception will have an *errno*
attribute set to :const:`EACCES` or :const:`EAGAIN` (depending on the
operating system; for portability, check for both values). On at least some
systems, :const:`LOCK_EX` can only be used if the file descriptor refers to a
diff --git a/Doc/library/fileinput.rst b/Doc/library/fileinput.rst
index ac44311..f8ec436 100644
--- a/Doc/library/fileinput.rst
+++ b/Doc/library/fileinput.rst
@@ -28,7 +28,10 @@ as the first argument to :func:`.input`. A single file name is also allowed.
All files are opened in text mode by default, but you can override this by
specifying the *mode* parameter in the call to :func:`.input` or
:class:`FileInput`. If an I/O error occurs during opening or reading a file,
-:exc:`IOError` is raised.
+:exc:`OSError` is raised.
+
+.. versionchanged:: 3.3
+ :exc:`IOError` used to be raised; it is now an alias of :exc:`OSError`.
If ``sys.stdin`` is used more than once, the second and further use will return
no lines, except perhaps for interactive use, or if it has been explicitly reset
@@ -168,10 +171,6 @@ and the backup file remains around; by default, the extension is ``'.bak'`` and
it is deleted when the output file is closed. In-place filtering is disabled
when standard input is read.
-.. note::
-
- The current implementation does not work for MS-DOS 8+3 filesystems.
-
The two following opening hooks are provided by this module:
diff --git a/Doc/library/ftplib.rst b/Doc/library/ftplib.rst
index 5bbef4f..3cc295a 100644
--- a/Doc/library/ftplib.rst
+++ b/Doc/library/ftplib.rst
@@ -40,7 +40,7 @@ Here's a sample session using the :mod:`ftplib` module::
The module defines the following items:
-.. class:: FTP(host='', user='', passwd='', acct=''[, timeout])
+.. class:: FTP(host='', user='', passwd='', acct='', timeout=None, source_address=None)
Return a new instance of the :class:`FTP` class. When *host* is given, the
method call ``connect(host)`` is made. When *user* is given, additionally
@@ -48,7 +48,8 @@ The module defines the following items:
*acct* default to the empty string when not given). The optional *timeout*
parameter specifies a timeout in seconds for blocking operations like the
connection attempt (if is not specified, the global default timeout setting
- will be used).
+ will be used). *source_address* is a 2-tuple ``(host, port)`` for the socket
+ to bind to as its source address before connecting.
:class:`FTP` class supports the :keyword:`with` statement. Here is a sample
on how using it:
@@ -68,8 +69,11 @@ The module defines the following items:
.. versionchanged:: 3.2
Support for the :keyword:`with` statement was added.
+ .. versionchanged:: 3.3
+ *source_address* parameter was added.
-.. class:: FTP_TLS(host='', user='', passwd='', acct='', [keyfile[, certfile[, context[, timeout]]]])
+
+.. class:: FTP_TLS(host='', user='', passwd='', acct='', keyfile=None, certfile=None, context=None, timeout=None, source_address=None)
A :class:`FTP` subclass which adds TLS support to FTP as described in
:rfc:`4217`.
@@ -80,10 +84,15 @@ The module defines the following items:
private key and certificate chain file name for the SSL connection.
*context* parameter is a :class:`ssl.SSLContext` object which allows
bundling SSL configuration options, certificates and private keys into a
- single (potentially long-lived) structure.
+ single (potentially long-lived) structure. *source_address* is a 2-tuple
+ ``(host, port)`` for the socket to bind to as its source address before
+ connecting.
.. versionadded:: 3.2
+ .. versionchanged:: 3.3
+ *source_address* parameter was added.
+
Here's a sample session using the :class:`FTP_TLS` class:
>>> from ftplib import FTP_TLS
@@ -135,8 +144,7 @@ The module defines the following items:
The set of all exceptions (as a tuple) that methods of :class:`FTP`
instances may raise as a result of problems with the FTP connection (as
opposed to programming errors made by the caller). This set includes the
- four exceptions listed above as well as :exc:`socket.error` and
- :exc:`IOError`.
+ four exceptions listed above as well as :exc:`OSError`.
.. seealso::
@@ -174,7 +182,7 @@ followed by ``lines`` for the text version or ``binary`` for the binary version.
debugging output, logging each line sent and received on the control connection.
-.. method:: FTP.connect(host='', port=0[, timeout])
+.. method:: FTP.connect(host='', port=0, timeout=None, source_address=None)
Connect to the given host and port. The default port number is ``21``, as
specified by the FTP protocol specification. It is rarely needed to specify a
@@ -182,10 +190,14 @@ followed by ``lines`` for the text version or ``binary`` for the binary version.
instance; it should not be called at all if a host was given when the instance
was created. All other methods can only be used after a connection has been
made.
-
The optional *timeout* parameter specifies a timeout in seconds for the
connection attempt. If no *timeout* is passed, the global default timeout
setting will be used.
+ *source_address* is a 2-tuple ``(host, port)`` for the socket to bind to as
+ its source address before connecting.
+
+ .. versionchanged:: 3.3
+ *source_address* parameter was added.
.. method:: FTP.getwelcome()
@@ -241,13 +253,12 @@ followed by ``lines`` for the text version or ``binary`` for the binary version.
Retrieve a file or directory listing in ASCII transfer mode. *cmd* should be
an appropriate ``RETR`` command (see :meth:`retrbinary`) or a command such as
- ``LIST``, ``NLST`` or ``MLSD`` (usually just the string ``'LIST'``).
+ ``LIST`` or ``NLST`` (usually just the string ``'LIST'``).
``LIST`` retrieves a list of files and information about those files.
- ``NLST`` retrieves a list of file names. On some servers, ``MLSD`` retrieves
- a machine readable list of files and information about those files. The
- *callback* function is called for each line with a string argument containing
- the line with the trailing CRLF stripped. The default *callback* prints the
- line to ``sys.stdout``.
+ ``NLST`` retrieves a list of file names.
+ The *callback* function is called for each line with a string argument
+ containing the line with the trailing CRLF stripped. The default *callback*
+ prints the line to ``sys.stdout``.
.. method:: FTP.set_pasv(boolean)
@@ -307,6 +318,20 @@ followed by ``lines`` for the text version or ``binary`` for the binary version.
in :meth:`transfercmd`.
+.. method:: FTP.mlsd(path="", facts=[])
+
+ List a directory in a standardized format by using MLSD command
+ (:rfc:`3659`). If *path* is omitted the current directory is assumed.
+ *facts* is a list of strings representing the type of information desired
+ (e.g. ``["type", "size", "perm"]``). Return a generator object yielding a
+ tuple of two elements for every file found in path. First element is the
+ file name, the second one is a dictionary containing facts about the file
+ name. Content of this dictionary might be limited by the *facts* argument
+ but server is not guaranteed to return all requested facts.
+
+ .. versionadded:: 3.3
+
+
.. method:: FTP.nlst(argument[, ...])
Return a list of file names as returned by the ``NLST`` command. The
@@ -314,6 +339,8 @@ followed by ``lines`` for the text version or ``binary`` for the binary version.
directory). Multiple arguments can be used to pass non-standard options to
the ``NLST`` command.
+ .. deprecated:: 3.3 use :meth:`mlsd` instead.
+
.. method:: FTP.dir(argument[, ...])
@@ -324,6 +351,8 @@ followed by ``lines`` for the text version or ``binary`` for the binary version.
as a *callback* function as for :meth:`retrlines`; the default prints to
``sys.stdout``. This method returns ``None``.
+ .. deprecated:: 3.3 use :meth:`mlsd` instead.
+
.. method:: FTP.rename(fromname, toname)
@@ -396,6 +425,14 @@ FTP_TLS Objects
Set up secure control connection by using TLS or SSL, depending on what specified in :meth:`ssl_version` attribute.
+.. method:: FTP_TLS.ccc()
+
+ Revert control channel back to plaintext. This can be useful to take
+ advantage of firewalls that know how to handle NAT with non-secure FTP
+ without opening fixed ports.
+
+ .. versionadded:: 3.3
+
.. method:: FTP_TLS.prot_p()
Set up secure data connection.
diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst
index e5d4a99..3cf5335 100644
--- a/Doc/library/functions.rst
+++ b/Doc/library/functions.rst
@@ -152,10 +152,6 @@ are always available. They are listed here in alphabetical order.
1,114,111 (0x10FFFF in base 16). :exc:`ValueError` will be raised if *i* is
outside that range.
- Note that on narrow Unicode builds, the result is a string of
- length two for *i* greater than 65,535 (0xFFFF in hexadecimal).
-
-
.. function:: classmethod(function)
@@ -780,10 +776,10 @@ are always available. They are listed here in alphabetical order.
:meth:`__index__` method that returns an integer.
-.. function:: open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True)
+.. function:: open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
Open *file* and return a corresponding stream. If the file cannot be opened,
- an :exc:`IOError` is raised.
+ an :exc:`OSError` is raised.
*file* is either a string or bytes object giving the pathname (absolute or
relative to the current working directory) of the file to be opened or
@@ -887,6 +883,15 @@ are always available. They are listed here in alphabetical order.
closed. If a filename is given *closefd* has no effect and must be ``True``
(the default).
+ A custom opener can be used by passing a callable as *opener*. The underlying
+ file descriptor for the file object is then obtained by calling *opener* with
+ (*file*, *flags*). *opener* must return an open file descriptor (passing
+ :mod:`os.open` as *opener* results in functionality similar to passing
+ ``None``).
+
+ .. versionchanged:: 3.3
+ The *opener* parameter was added.
+
The type of file object returned by the :func:`open` function depends on the
mode. When :func:`open` is used to open a file in a text mode (``'w'``,
``'r'``, ``'wt'``, ``'rt'``, etc.), it returns a subclass of
@@ -912,6 +917,9 @@ are always available. They are listed here in alphabetical order.
(where :func:`open` is declared), :mod:`os`, :mod:`os.path`, :mod:`tempfile`,
and :mod:`shutil`.
+ .. versionchanged:: 3.3
+ :exc:`IOError` used to be raised, it is now an alias of :exc:`OSError`.
+
.. XXX works for bytes too, but should it?
.. function:: ord(c)
@@ -921,9 +929,6 @@ are always available. They are listed here in alphabetical order.
point of that character. For example, ``ord('a')`` returns the integer ``97``
and ``ord('\u2020')`` returns ``8224``. This is the inverse of :func:`chr`.
- On wide Unicode builds, if the argument length is not one, a
- :exc:`TypeError` will be raised. On narrow Unicode builds, strings
- of length two are accepted when they form a UTF-16 surrogate pair.
.. function:: pow(x, y[, z])
@@ -1037,7 +1042,9 @@ are always available. They are listed here in alphabetical order.
...]``. If *step* is positive, the last element is the largest ``start + i *
step`` less than *stop*; if *step* is negative, the last element is the
smallest ``start + i * step`` greater than *stop*. *step* must not be zero
- (or else :exc:`ValueError` is raised). Example:
+ (or else :exc:`ValueError` is raised). Range objects have read-only data
+ attributes :attr:`start`, :attr:`stop` and :attr:`step` which return the
+ argument values (or their default). Example:
>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
@@ -1074,6 +1081,13 @@ are always available. They are listed here in alphabetical order.
>>> r[-1]
18
+ Testing range objects for equality with ``==`` and ``!=`` compares
+ them as sequences. That is, two range objects are considered equal if
+ they represent the same sequence of values. (Note that two range
+ objects that compare equal might have different :attr:`start`,
+ :attr:`stop` and :attr:`step` attributes, for example ``range(0) ==
+ range(2, 1, 3)`` or ``range(0, 3, 2) == range(0, 4, 2)``.)
+
Ranges containing absolute values larger than :data:`sys.maxsize` are permitted
but some features (such as :func:`len`) will raise :exc:`OverflowError`.
@@ -1083,6 +1097,14 @@ are always available. They are listed here in alphabetical order.
Test integers for membership in constant time instead of iterating
through all items.
+ .. versionchanged:: 3.3
+ Define '==' and '!=' to compare range objects based on the
+ sequence of values they define (instead of comparing based on
+ object identity).
+
+ .. versionadded:: 3.3
+ The :attr:`start`, :attr:`stop` and :attr:`step` attributes.
+
.. function:: repr(object)
diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst
index 2316e80..4eaf54e 100644
--- a/Doc/library/functools.rst
+++ b/Doc/library/functools.rst
@@ -40,7 +40,7 @@ The :mod:`functools` module defines the following functions:
.. versionadded:: 3.2
-.. decorator:: lru_cache(maxsize=100)
+.. decorator:: lru_cache(maxsize=100, typed=False)
Decorator to wrap a function with a memoizing callable that saves up to the
*maxsize* most recent calls. It can save time when an expensive or I/O bound
@@ -52,6 +52,10 @@ The :mod:`functools` module defines the following functions:
If *maxsize* is set to None, the LRU feature is disabled and the cache
can grow without bound.
+ If *typed* is set to True, function arguments of different types will be
+ cached separately. For example, ``f(3)`` and ``f(3.0)`` will be treated
+ as distinct calls with distinct results.
+
To help measure the effectiveness of the cache and tune the *maxsize*
parameter, the wrapped function is instrumented with a :func:`cache_info`
function that returns a :term:`named tuple` showing *hits*, *misses*,
@@ -67,8 +71,8 @@ The :mod:`functools` module defines the following functions:
An `LRU (least recently used) cache
<http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used>`_ works
- best when more recent calls are the best predictors of upcoming calls (for
- example, the most popular articles on a news server tend to change daily).
+ best when the most recent calls are the best predictors of upcoming calls (for
+ example, the most popular articles on a news server tend to change each day).
The cache's size limit assures that the cache does not grow without bound on
long-running processes such as web servers.
@@ -111,6 +115,9 @@ The :mod:`functools` module defines the following functions:
.. versionadded:: 3.2
+ .. versionchanged:: 3.3
+ Added the *typed* option.
+
.. decorator:: total_ordering
Given a class defining one or more rich comparison ordering methods, this
diff --git a/Doc/library/gettext.rst b/Doc/library/gettext.rst
index 0fa022c..825311b 100644
--- a/Doc/library/gettext.rst
+++ b/Doc/library/gettext.rst
@@ -185,10 +185,13 @@ class can also install themselves in the built-in namespace as the function
translation object from the cache; the actual instance data is still shared with
the cache.
- If no :file:`.mo` file is found, this function raises :exc:`IOError` if
+ If no :file:`.mo` file is found, this function raises :exc:`OSError` if
*fallback* is false (which is the default), and returns a
:class:`NullTranslations` instance if *fallback* is true.
+ .. versionchanged:: 3.3
+ :exc:`IOError` used to be raised instead of :exc:`OSError`.
+
.. function:: install(domain, localedir=None, codeset=None, names=None)
@@ -342,7 +345,7 @@ The entire set of key/value pairs are placed into a dictionary and set as the
If the :file:`.mo` file's magic number is invalid, or if other problems occur
while reading the file, instantiating a :class:`GNUTranslations` class can raise
-:exc:`IOError`.
+:exc:`OSError`.
The following methods are overridden from the base class implementation:
diff --git a/Doc/library/gzip.rst b/Doc/library/gzip.rst
index 48a8694..4491acc 100644
--- a/Doc/library/gzip.rst
+++ b/Doc/library/gzip.rst
@@ -21,7 +21,7 @@ Note that additional file formats which can be decompressed by the
:program:`gzip` and :program:`gunzip` programs, such as those produced by
:program:`compress` and :program:`pack`, are not supported by this module.
-For other archive formats, see the :mod:`bz2`, :mod:`zipfile`, and
+For related file formats, see the :mod:`bz2`, :mod:`lzma`, :mod:`zipfile`, and
:mod:`tarfile` modules.
The module defines the following items:
@@ -72,7 +72,7 @@ The module defines the following items:
:class:`GzipFile` supports the :class:`io.BufferedIOBase` interface,
including iteration and the :keyword:`with` statement. Only the
- :meth:`read1` and :meth:`truncate` methods aren't implemented.
+ :meth:`truncate` method isn't implemented.
:class:`GzipFile` also provides the following method:
@@ -94,6 +94,9 @@ The module defines the following items:
.. versionchanged:: 3.2
Support for unseekable files was added.
+ .. versionchanged:: 3.3
+ The :meth:`io.BufferedIOBase.read1` method is now implemented.
+
.. function:: open(filename, mode='rb', compresslevel=9)
diff --git a/Doc/library/http.client.rst b/Doc/library/http.client.rst
index c1ce15b..7fce91f 100644
--- a/Doc/library/http.client.rst
+++ b/Doc/library/http.client.rst
@@ -502,6 +502,12 @@ statement.
Reads and returns the response body, or up to the next *amt* bytes.
+.. method:: HTTPResponse.readinto(b)
+
+ Reads up to the next len(b) bytes of the response body into the buffer *b*.
+ Returns the number of bytes read.
+
+ .. versionadded:: 3.3
.. method:: HTTPResponse.getheader(name, default=None)
diff --git a/Doc/library/http.cookiejar.rst b/Doc/library/http.cookiejar.rst
index 9771496..1fe775f 100644
--- a/Doc/library/http.cookiejar.rst
+++ b/Doc/library/http.cookiejar.rst
@@ -40,7 +40,11 @@ The module defines the following exception:
.. exception:: LoadError
Instances of :class:`FileCookieJar` raise this exception on failure to load
- cookies from a file. :exc:`LoadError` is a subclass of :exc:`IOError`.
+ cookies from a file. :exc:`LoadError` is a subclass of :exc:`OSError`.
+
+ .. versionchanged:: 3.3
+ LoadError was made a subclass of :exc:`OSError` instead of
+ :exc:`IOError`.
The following classes are provided:
@@ -257,9 +261,12 @@ contained :class:`Cookie` objects.
Arguments are as for :meth:`save`.
The named file must be in the format understood by the class, or
- :exc:`LoadError` will be raised. Also, :exc:`IOError` may be raised, for
+ :exc:`LoadError` will be raised. Also, :exc:`OSError` may be raised, for
example if the file does not exist.
+ .. versionchanged:: 3.3
+ :exc:`IOError` used to be raised, it is now an alias of :exc:`OSError`.
+
.. method:: FileCookieJar.revert(filename=None, ignore_discard=False, ignore_expires=False)
diff --git a/Doc/library/http.server.rst b/Doc/library/http.server.rst
index e3a3a10..d9aaa72 100644
--- a/Doc/library/http.server.rst
+++ b/Doc/library/http.server.rst
@@ -179,19 +179,29 @@ of which this module provides three different variants:
.. method:: send_response(code, message=None)
- Sends a response header and logs the accepted request. The HTTP response
- line is sent, followed by *Server* and *Date* headers. The values for
- these two headers are picked up from the :meth:`version_string` and
- :meth:`date_time_string` methods, respectively.
+ Adds a response header to the headers buffer and logs the accepted
+ request. The HTTP response line is written to the internal buffer,
+ followed by *Server* and *Date* headers. The values for these two headers
+ are picked up from the :meth:`version_string` and
+ :meth:`date_time_string` methods, respectively. If the server does not
+ intend to send any other headers using the :meth:`send_header` method,
+ then :meth:`send_response` should be followed by a :meth:`end_headers`
+ call.
+
+ .. versionchanged:: 3.3
+ Headers are stored to an internal buffer and :meth:`end_headers`
+ needs to be called explicitly.
+
.. method:: send_header(keyword, value)
- Stores the HTTP header to an internal buffer which will be written to the
- output stream when :meth:`end_headers` method is invoked.
- *keyword* should specify the header keyword, with *value*
- specifying its value.
+ Adds the HTTP header to an internal buffer which will be written to the
+ output stream when either :meth:`end_headers` or :meth:`flush_headers` is
+ invoked. *keyword* should specify the header keyword, with *value*
+ specifying its value. Note that, after the send_header calls are done,
+ :meth:`end_headers` MUST BE called in order to complete the operation.
- .. versionchanged:: 3.2 Storing the headers in an internal buffer
+ .. versionchanged:: 3.2 Headers are stored in an internal buffer.
.. method:: send_response_only(code, message=None)
@@ -205,10 +215,19 @@ of which this module provides three different variants:
.. method:: end_headers()
- Write the buffered HTTP headers to the output stream and send a blank
- line, indicating the end of the HTTP headers in the response.
+ Adds a blank line
+ (indicating the end of the HTTP headers in the response)
+ to the headers buffer and calls :meth:`flush_headers()`.
+
+ .. versionchanged:: 3.2
+ The buffered headers are written to the output stream.
+
+ .. method:: flush_headers()
+
+ Finally send the headers to the output stream and flush the internal
+ headers buffer.
- .. versionchanged:: 3.2 Writing the buffered headers to the output stream.
+ .. versionadded:: 3.3
.. method:: log_request(code='-', size='-')
@@ -299,7 +318,7 @@ of which this module provides three different variants:
response if the :func:`listdir` fails.
If the request was mapped to a file, it is opened and the contents are
- returned. Any :exc:`IOError` exception in opening the requested file is
+ returned. Any :exc:`OSError` exception in opening the requested file is
mapped to a ``404``, ``'File not found'`` error. Otherwise, the content
type is guessed by calling the :meth:`guess_type` method, which in turn
uses the *extensions_map* variable.
diff --git a/Doc/library/imaplib.rst b/Doc/library/imaplib.rst
index 3f45c95..038355c 100644
--- a/Doc/library/imaplib.rst
+++ b/Doc/library/imaplib.rst
@@ -64,14 +64,21 @@ Three exceptions are defined as attributes of the :class:`IMAP4` class:
There's also a subclass for secure connections:
-.. class:: IMAP4_SSL(host='', port=IMAP4_SSL_PORT, keyfile=None, certfile=None)
+.. class:: IMAP4_SSL(host='', port=IMAP4_SSL_PORT, keyfile=None, certfile=None, ssl_context=None)
This is a subclass derived from :class:`IMAP4` that connects over an SSL
encrypted socket (to use this class you need a socket module that was compiled
with SSL support). If *host* is not specified, ``''`` (the local host) is used.
If *port* is omitted, the standard IMAP4-over-SSL port (993) is used. *keyfile*
and *certfile* are also optional - they can contain a PEM formatted private key
- and certificate chain file for the SSL connection.
+ and certificate chain file for the SSL connection. *ssl_context* parameter is a
+ :class:`ssl.SSLContext` object which allows bundling SSL configuration
+ options, certificates and private keys into a single (potentially long-lived)
+ structure. Note that the *keyfile*/*certfile* parameters are mutually exclusive with *ssl_context*,
+ a :class:`ValueError` is thrown if *keyfile*/*certfile* is provided along with *ssl_context*.
+
+ .. versionchanged:: 3.3
+ *ssl_context* parameter added.
The second subclass allows for connections created by a child process:
diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst
index d127ce8..ac6ae99 100644
--- a/Doc/library/inspect.rst
+++ b/Doc/library/inspect.rst
@@ -355,17 +355,25 @@ Retrieving source code
argument may be a module, class, method, function, traceback, frame, or code
object. The source code is returned as a list of the lines corresponding to the
object and the line number indicates where in the original source file the first
- line of code was found. An :exc:`IOError` is raised if the source code cannot
+ line of code was found. An :exc:`OSError` is raised if the source code cannot
be retrieved.
+ .. versionchanged:: 3.3
+ :exc:`OSError` is raised instead of :exc:`IOError`, now an alias of the
+ former.
+
.. function:: getsource(object)
Return the text of the source code for an object. The argument may be a module,
class, method, function, traceback, frame, or code object. The source code is
- returned as a single string. An :exc:`IOError` is raised if the source code
+ returned as a single string. An :exc:`OSError` is raised if the source code
cannot be retrieved.
+ .. versionchanged:: 3.3
+ :exc:`OSError` is raised instead of :exc:`IOError`, now an alias of the
+ former.
+
.. function:: cleandoc(doc)
diff --git a/Doc/library/io.rst b/Doc/library/io.rst
index 972820c..85e8c5b 100644
--- a/Doc/library/io.rst
+++ b/Doc/library/io.rst
@@ -33,6 +33,10 @@ giving a :class:`str` object to the ``write()`` method of a binary stream
will raise a ``TypeError``. So will giving a :class:`bytes` object to the
``write()`` method of a text stream.
+.. versionchanged:: 3.3
+ Operations defined in this module used to raise :exc:`IOError`, which is
+ now an alias of :exc:`OSError`.
+
Text I/O
^^^^^^^^
@@ -109,21 +113,13 @@ High-level Module Interface
.. exception:: BlockingIOError
- Error raised when blocking would occur on a non-blocking stream. It inherits
- :exc:`IOError`.
-
- In addition to those of :exc:`IOError`, :exc:`BlockingIOError` has one
- attribute:
-
- .. attribute:: characters_written
-
- An integer containing the number of characters written to the stream
- before it blocked.
+ This is a compatibility alias for the builtin :exc:`BlockingIOError`
+ exception.
.. exception:: UnsupportedOperation
- An exception inheriting :exc:`IOError` and :exc:`ValueError` that is raised
+ An exception inheriting :exc:`OSError` and :exc:`ValueError` that is raised
when an unsupported operation is called on a stream.
@@ -202,8 +198,8 @@ I/O Base Classes
Even though :class:`IOBase` does not declare :meth:`read`, :meth:`readinto`,
or :meth:`write` because their signatures will vary, implementations and
clients should consider those methods part of the interface. Also,
- implementations may raise a :exc:`IOError` when operations they do not
- support are called.
+ implementations may raise a :exc:`ValueError` (or :exc:`UnsupportedOperation`)
+ when operations they do not support are called.
The basic type used for binary data read from or written to a file is
:class:`bytes`. :class:`bytearray`\s are accepted too, and in some cases
@@ -211,7 +207,7 @@ I/O Base Classes
:class:`str` data.
Note that calling any method (even inquiries) on a closed stream is
- undefined. Implementations may raise :exc:`IOError` in this case.
+ undefined. Implementations may raise :exc:`ValueError` in this case.
IOBase (and its subclasses) support the iterator protocol, meaning that an
:class:`IOBase` object can be iterated over yielding the lines in a stream.
@@ -244,7 +240,7 @@ I/O Base Classes
.. method:: fileno()
Return the underlying file descriptor (an integer) of the stream if it
- exists. An :exc:`IOError` is raised if the IO object does not use a file
+ exists. An :exc:`OSError` is raised if the IO object does not use a file
descriptor.
.. method:: flush()
@@ -260,7 +256,7 @@ I/O Base Classes
.. method:: readable()
Return ``True`` if the stream can be read from. If False, :meth:`read`
- will raise :exc:`IOError`.
+ will raise :exc:`OSError`.
.. method:: readline(limit=-1)
@@ -298,7 +294,7 @@ I/O Base Classes
.. method:: seekable()
Return ``True`` if the stream supports random access. If ``False``,
- :meth:`seek`, :meth:`tell` and :meth:`truncate` will raise :exc:`IOError`.
+ :meth:`seek`, :meth:`tell` and :meth:`truncate` will raise :exc:`OSError`.
.. method:: tell()
@@ -316,7 +312,7 @@ I/O Base Classes
.. method:: writable()
Return ``True`` if the stream supports writing. If ``False``,
- :meth:`write` and :meth:`truncate` will raise :exc:`IOError`.
+ :meth:`write` and :meth:`truncate` will raise :exc:`OSError`.
.. method:: writelines(lines)
@@ -450,7 +446,7 @@ I/O Base Classes
Write the given bytes or bytearray object, *b* and return the number
of bytes written (never less than ``len(b)``, since if the write fails
- an :exc:`IOError` will be raised). Depending on the actual
+ an :exc:`OSError` will be raised). Depending on the actual
implementation, these bytes may be readily written to the underlying
stream, or held in a buffer for performance and latency reasons.
@@ -462,7 +458,7 @@ I/O Base Classes
Raw File I/O
^^^^^^^^^^^^
-.. class:: FileIO(name, mode='r', closefd=True)
+.. class:: FileIO(name, mode='r', closefd=True, opener=None)
:class:`FileIO` represents an OS-level file containing bytes data.
It implements the :class:`RawIOBase` interface (and therefore the
@@ -483,6 +479,15 @@ Raw File I/O
The :meth:`read` (when called with a positive argument), :meth:`readinto`
and :meth:`write` methods on this class will only make one system call.
+ A custom opener can be used by passing a callable as *opener*. The underlying
+ file descriptor for the file object is then obtained by calling *opener* with
+ (*name*, *flags*). *opener* must return an open file descriptor (passing
+ :mod:`os.open` as *opener* results in functionality similar to passing
+ ``None``).
+
+ .. versionchanged:: 3.3
+ The *opener* parameter was added.
+
In addition to the attributes and methods from :class:`IOBase` and
:class:`RawIOBase`, :class:`FileIO` provides the following data
attributes and methods:
@@ -710,7 +715,8 @@ Text I/O
written.
-.. class:: TextIOWrapper(buffer, encoding=None, errors=None, newline=None, line_buffering=False)
+.. class:: TextIOWrapper(buffer, encoding=None, errors=None, newline=None, \
+ line_buffering=False, write_through=False)
A buffered text stream over a :class:`BufferedIOBase` binary stream.
It inherits :class:`TextIOBase`.
@@ -741,6 +747,13 @@ Text I/O
If *line_buffering* is ``True``, :meth:`flush` is implied when a call to
write contains a newline character.
+ If *write_through* is ``True``, calls to :meth:`write` are guaranteed
+ not to be buffered: any data written on the :class:`TextIOWrapper`
+ object is immediately handled to its underlying binary *buffer*.
+
+ .. versionchanged:: 3.3
+ The *write_through* argument has been added.
+
:class:`TextIOWrapper` provides one attribute in addition to those of
:class:`TextIOBase` and its parents:
diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst
index 28625e8..7b5922a 100644
--- a/Doc/library/itertools.rst
+++ b/Doc/library/itertools.rst
@@ -46,7 +46,7 @@ Iterator Arguments Results
==================== ============================ ================================================= =============================================================
Iterator Arguments Results Example
==================== ============================ ================================================= =============================================================
-:func:`accumulate` p p0, p0+p1, p0+p1+p2, ... ``accumulate([1,2,3,4,5]) --> 1 3 6 10 15``
+:func:`accumulate` p [,func] p0, p0+p1, p0+p1+p2, ... ``accumulate([1,2,3,4,5]) --> 1 3 6 10 15``
:func:`chain` p, q, ... p0, p1, ... plast, q0, q1, ... ``chain('ABC', 'DEF') --> A B C D E F``
:func:`compress` data, selectors (d[0] if s[0]), (d[1] if s[1]), ... ``compress('ABCDEF', [1,0,1,0,1,1]) --> A C E F``
:func:`dropwhile` pred, seq seq[n], seq[n+1], starting when pred fails ``dropwhile(lambda x: x<5, [1,4,6,4,1]) --> 6 4 1``
@@ -84,23 +84,61 @@ The following module functions all construct and return iterators. Some provide
streams of infinite length, so they should only be accessed by functions or
loops that truncate the stream.
-.. function:: accumulate(iterable)
+.. function:: accumulate(iterable[, func])
Make an iterator that returns accumulated sums. Elements may be any addable
- type including :class:`Decimal` or :class:`Fraction`. Equivalent to::
+ type including :class:`Decimal` or :class:`Fraction`. If the optional
+ *func* argument is supplied, it should be a function of two arguments
+ and it will be used instead of addition.
- def accumulate(iterable):
+ Equivalent to::
+
+ def accumulate(iterable, func=operator.add):
'Return running totals'
# accumulate([1,2,3,4,5]) --> 1 3 6 10 15
+ # accumulate([1,2,3,4,5], operator.mul) --> 1 2 6 24 120
it = iter(iterable)
total = next(it)
yield total
for element in it:
- total = total + element
+ total = func(total, element)
yield total
+ There are a number of uses for the *func* argument. It can be set to
+ :func:`min` for a running minimum, :func:`max` for a running maximum, or
+ :func:`operator.mul` for a running product. Amortization tables can be
+ built by accumulating interest and applying payments. First-order
+ `recurrence relations <http://en.wikipedia.org/wiki/Recurrence_relation>`_
+ can be modeled by supplying the initial value in the iterable and using only
+ the accumulated total in *func* argument::
+
+ >>> data = [3, 4, 6, 2, 1, 9, 0, 7, 5, 8]
+ >>> list(accumulate(data, operator.mul)) # running product
+ [3, 12, 72, 144, 144, 1296, 0, 0, 0, 0]
+ >>> list(accumulate(data, max)) # running maximum
+ [3, 4, 6, 6, 6, 9, 9, 9, 9, 9]
+
+ # Amortize a 5% loan of 1000 with 4 annual payments of 90
+ >>> cashflows = [1000, -90, -90, -90, -90]
+ >>> list(accumulate(cashflows, lambda bal, pmt: bal*1.05 + pmt))
+ [1000, 960.0, 918.0, 873.9000000000001, 827.5950000000001]
+
+ # Chaotic recurrence relation http://en.wikipedia.org/wiki/Logistic_map
+ >>> logistic_map = lambda x, _: r * x * (1 - x)
+ >>> r = 3.8
+ >>> x0 = 0.4
+ >>> inputs = repeat(x0, 36) # only the initial value is used
+ >>> [format(x, '.2f') for x in accumulate(inputs, logistic_map)]
+ ['0.40', '0.91', '0.30', '0.81', '0.60', '0.92', '0.29', '0.79', '0.63',
+ '0.88' ,'0.39', '0.90', '0.33', '0.84', '0.52', '0.95', '0.18', '0.57',
+ '0.93', '0.25', '0.71', '0.79', '0.63', '0.88', '0.39', '0.91', '0.32',
+ '0.83', '0.54', '0.95', '0.20', '0.60', '0.91', '0.30', '0.80', '0.60']
+
.. versionadded:: 3.2
+ .. versionchanged:: 3.3
+ Added the optional *func* parameter.
+
.. function:: chain(*iterables)
Make an iterator that returns elements from the first iterable until it is
diff --git a/Doc/library/logging.handlers.rst b/Doc/library/logging.handlers.rst
index c4dd438..3911d4d 100644
--- a/Doc/library/logging.handlers.rst
+++ b/Doc/library/logging.handlers.rst
@@ -452,6 +452,15 @@ supports sending logging messages to a remote or local Unix syslog.
behaviour) but can be set to ``False`` on a ``SysLogHandler`` instance
in order for that instance to *not* append the NUL terminator.
+ .. versionchanged:: 3.3
+ (See: :issue:`12419`.) In earlier versions, there was no facility for
+ an "ident" or "tag" prefix to identify the source of the message. This
+ can now be specified using a class-level attribute, defaulting to
+ ``""`` to preserve existing behaviour, but which can be overridden on
+ a ``SysLogHandler`` instance in order for that instance to prepend
+ the ident to every message handled. Note that the provided ident must
+ be text, not bytes, and is prepended to the message exactly as is.
+
.. method:: encodePriority(facility, priority)
Encodes the facility and priority into an integer. You can pass in strings
@@ -859,6 +868,15 @@ possible, while any potentially slow operations (such as sending an email via
Note that if you don't call this before your application exits, there
may be some records still left on the queue, which won't be processed.
+ .. method:: enqueue_sentinel()
+
+ Writes a sentinel to the queue to tell the listener to quit. This
+ implementation uses ``put_nowait()``. You may want to override this
+ method if you want to use timeouts or work with custom queue
+ implementations.
+
+ .. versionadded:: 3.3
+
.. seealso::
diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst
index 2d71552..859d275 100644
--- a/Doc/library/logging.rst
+++ b/Doc/library/logging.rst
@@ -195,6 +195,9 @@ instantiated directly, but always through the module-level function
Logs a message with level :const:`WARNING` on this logger. The arguments are
interpreted as for :meth:`debug`.
+ .. note:: There is an obsolete method ``warn`` which is functionally
+ identical to ``warning``. As ``warn`` is deprecated, please do not use
+ it - use ``warning`` instead.
.. method:: Logger.error(msg, *args, **kwargs)
@@ -467,6 +470,19 @@ The useful mapping keys in a :class:`LogRecord` are given in the section on
want all logging times to be shown in GMT, set the ``converter``
attribute in the ``Formatter`` class.
+ .. versionchanged:: 3.3
+ Previously, the default ISO 8601 format was hard-coded as in this
+ example: ``2010-09-06 22:38:15,292`` where the part before the comma is
+ handled by a strptime format string (``'%Y-%m-%d %H:%M:%S'``), and the
+ part after the comma is a millisecond value. Because strptime does not
+ have a format placeholder for milliseconds, the millisecond value is
+ appended using another format string, ``'%s,%03d'`` – and both of these
+ format strings have been hardcoded into this method. With the change,
+ these strings are defined as class-level attributes which can be
+ overridden at the instance level when desired. The names of the
+ attributes are ``default_time_format`` (for the strptime format string)
+ and ``default_msec_format`` (for appending the millisecond value).
+
.. method:: formatException(exc_info)
Formats the specified exception information (a standard exception tuple as
@@ -873,8 +889,12 @@ functions.
.. function:: warning(msg, *args, **kwargs)
- Logs a message with level :const:`WARNING` on the root logger. The arguments are
- interpreted as for :func:`debug`.
+ Logs a message with level :const:`WARNING` on the root logger. The arguments
+ are interpreted as for :func:`debug`.
+
+ .. note:: There is an obsolete function ``warn`` which is functionally
+ identical to ``warning``. As ``warn`` is deprecated, please do not use
+ it - use ``warning`` instead.
.. function:: error(msg, *args, **kwargs)
@@ -999,12 +1019,27 @@ functions.
| ``stream`` | Use the specified stream to initialize the |
| | StreamHandler. Note that this argument is |
| | incompatible with 'filename' - if both are |
- | | present, 'stream' is ignored. |
+ | | present, a ``ValueError`` is raised. |
+ +--------------+---------------------------------------------+
+ | ``handlers`` | If specified, this should be an iterable of |
+ | | already created handlers to add to the root |
+ | | logger. Any handlers which don't already |
+ | | have a formatter set will be assigned the |
+ | | default formatter created in this function. |
+ | | Note that this argument is incompatible |
+ | | with 'filename' or 'stream' - if both are |
+ | | present, a ``ValueError`` is raised. |
+--------------+---------------------------------------------+
.. versionchanged:: 3.2
The ``style`` argument was added.
+ .. versionchanged:: 3.3
+ The ``handlers`` argument was added. Additional checks were added to
+ catch situations where incompatible arguments are specified (e.g.
+ ``handlers`` together with ``stream`` or ``filename``, or ``stream``
+ together with ``filename``).
+
.. function:: shutdown()
diff --git a/Doc/library/lzma.rst b/Doc/library/lzma.rst
new file mode 100644
index 0000000..4e6db15
--- /dev/null
+++ b/Doc/library/lzma.rst
@@ -0,0 +1,344 @@
+:mod:`lzma` --- Compression using the LZMA algorithm
+====================================================
+
+.. module:: lzma
+ :synopsis: A Python wrapper for the liblzma compression library.
+.. moduleauthor:: Nadeem Vawda <nadeem.vawda@gmail.com>
+.. sectionauthor:: Nadeem Vawda <nadeem.vawda@gmail.com>
+
+.. versionadded:: 3.3
+
+
+This module provides classes and convenience functions for compressing and
+decompressing data using the LZMA compression algorithm. Also included is a file
+interface supporting the ``.xz`` and legacy ``.lzma`` file formats used by the
+:program:`xz` utility, as well as raw compressed streams.
+
+For related file formats, see the :mod:`bz2`, :mod:`gzip`, :mod:`zipfile`, and
+:mod:`tarfile` modules.
+
+The interface provided by this module is very similar to that of the :mod:`bz2`
+module. However, note that :class:`LZMAFile` is *not* thread-safe, unlike
+:class:`bz2.BZ2File`, so if you need to use a single :class:`LZMAFile` instance
+from multiple threads, it is necessary to protect it with a lock.
+
+
+.. exception:: LZMAError
+
+ This exception is raised when an error occurs during compression or
+ decompression, or while initializing the compressor/decompressor state.
+
+
+Reading and writing compressed files
+------------------------------------
+
+.. class:: LZMAFile(filename=None, mode="r", fileobj=None, format=None, check=-1, preset=None, filters=None)
+
+ Open an LZMA-compressed file.
+
+ An :class:`LZMAFile` can wrap an existing :term:`file object` (given by
+ *fileobj*), or operate directly on a named file (named by *filename*).
+ Exactly one of these two parameters should be provided. If *fileobj* is
+ provided, it is not closed when the :class:`LZMAFile` is closed.
+
+ The *mode* argument can be either ``"r"`` for reading (default), ``"w"`` for
+ overwriting, or ``"a"`` for appending. If *fileobj* is provided, a mode of
+ ``"w"`` does not truncate the file, and is instead equivalent to ``"a"``.
+
+ When opening a file for reading, the input file may be the concatenation of
+ multiple separate compressed streams. These are transparently decoded as a
+ single logical stream.
+
+ When opening a file for reading, the *format* and *filters* arguments have
+ the same meanings as for :class:`LZMADecompressor`. In this case, the *check*
+ and *preset* arguments should not be used.
+
+ When opening a file for writing, the *format*, *check*, *preset* and
+ *filters* arguments have the same meanings as for :class:`LZMACompressor`.
+
+ :class:`LZMAFile` supports all the members specified by
+ :class:`io.BufferedIOBase`, except for :meth:`detach` and :meth:`truncate`.
+ Iteration and the :keyword:`with` statement are supported.
+
+ The following method is also provided:
+
+ .. method:: peek(size=-1)
+
+ Return buffered data without advancing the file position. At least one
+ byte of data will be returned, unless EOF has been reached. The exact
+ number of bytes returned is unspecified (the *size* argument is ignored).
+
+
+Compressing and decompressing data in memory
+--------------------------------------------
+
+.. class:: LZMACompressor(format=FORMAT_XZ, check=-1, preset=None, filters=None)
+
+ Create a compressor object, which can be used to compress data incrementally.
+
+ For a more convenient way of compressing a single chunk of data, see
+ :func:`compress`.
+
+ The *format* argument specifies what container format should be used.
+ Possible values are:
+
+ * :const:`FORMAT_XZ`: The ``.xz`` container format.
+ This is the default format.
+
+ * :const:`FORMAT_ALONE`: The legacy ``.lzma`` container format.
+ This format is more limited than ``.xz`` -- it does not support integrity
+ checks or multiple filters.
+
+ * :const:`FORMAT_RAW`: A raw data stream, not using any container format.
+ This format specifier does not support integrity checks, and requires that
+ you always specify a custom filter chain (for both compression and
+ decompression). Additionally, data compressed in this manner cannot be
+ decompressed using :const:`FORMAT_AUTO` (see :class:`LZMADecompressor`).
+
+ The *check* argument specifies the type of integrity check to include in the
+ compressed data. This check is used when decompressing, to ensure that the
+ data has not been corrupted. Possible values are:
+
+ * :const:`CHECK_NONE`: No integrity check.
+ This is the default (and the only acceptable value) for
+ :const:`FORMAT_ALONE` and :const:`FORMAT_RAW`.
+
+ * :const:`CHECK_CRC32`: 32-bit Cyclic Redundancy Check.
+
+ * :const:`CHECK_CRC64`: 64-bit Cyclic Redundancy Check.
+ This is the default for :const:`FORMAT_XZ`.
+
+ * :const:`CHECK_SHA256`: 256-bit Secure Hash Algorithm.
+
+ If the specified check is not supported, an :class:`LZMAError` is raised.
+
+ The compression settings can be specified either as a preset compression
+ level (with the *preset* argument), or in detail as a custom filter chain
+ (with the *filters* argument).
+
+ The *preset* argument (if provided) should be an integer between ``0`` and
+ ``9`` (inclusive), optionally OR-ed with the constant
+ :const:`PRESET_EXTREME`. If neither *preset* nor *filters* are given, the
+ default behavior is to use :const:`PRESET_DEFAULT` (preset level ``6``).
+ Higher presets produce smaller output, but make compression more CPU- and
+ memory-intensive, and also increase the memory required for decompression.
+
+ The *filters* argument (if provided) should be a filter chain specifier.
+ See :ref:`filter-chain-specs` for details.
+
+ .. method:: compress(data)
+
+ Compress *data* (a :class:`bytes` object), returning a :class:`bytes`
+ object containing compressed data for at least part of the input. Some of
+ *data* may be buffered internally, for use in later calls to
+ :meth:`compress` and :meth:`flush`. The returned data should be
+ concatenated with the output of any previous calls to :meth:`compress`.
+
+ .. method:: flush()
+
+ Finish the compression process, returning a :class:`bytes` object
+ containing any data stored in the compressor's internal buffers.
+
+ The compressor cannot be used after this method has been called.
+
+
+.. class:: LZMADecompressor(format=FORMAT_AUTO, memlimit=None, filters=None)
+
+ Create a decompressor object, which can be used to decompress data
+ incrementally.
+
+ For a more convenient way of decompressing an entire compressed stream at
+ once, see :func:`decompress`.
+
+ The *format* argument specifies the container format that should be used. The
+ default is :const:`FORMAT_AUTO`, which can decompress both ``.xz`` and
+ ``.lzma`` files. Other possible values are :const:`FORMAT_XZ`,
+ :const:`FORMAT_ALONE`, and :const:`FORMAT_RAW`.
+
+ The *memlimit* argument specifies a limit (in bytes) on the amount of memory
+ that the decompressor can use. When this argument is used, decompression will
+ fail with an :class:`LZMAError` if it is not possible to decompress the input
+ within the given memory limit.
+
+ The *filters* argument specifies the filter chain that was used to create
+ the stream being decompressed. This argument is required if *format* is
+ :const:`FORMAT_RAW`, but should not be used for other formats.
+ See :ref:`filter-chain-specs` for more information about filter chains.
+
+ .. note::
+ This class does not transparently handle inputs containing multiple
+ compressed streams, unlike :func:`decompress` and :class:`LZMAFile`. To
+ decompress a multi-stream input with :class:`LZMADecompressor`, you must
+ create a new decompressor for each stream.
+
+ .. method:: decompress(data)
+
+ Decompress *data* (a :class:`bytes` object), returning a :class:`bytes`
+ object containing the decompressed data for at least part of the input.
+ Some of *data* may be buffered internally, for use in later calls to
+ :meth:`decompress`. The returned data should be concatenated with the
+ output of any previous calls to :meth:`decompress`.
+
+ .. attribute:: check
+
+ The ID of the integrity check used by the input stream. This may be
+ :const:`CHECK_UNKNOWN` until enough of the input has been decoded to
+ determine what integrity check it uses.
+
+ .. attribute:: eof
+
+ True if the end-of-stream marker has been reached.
+
+ .. attribute:: unused_data
+
+ Data found after the end of the compressed stream.
+
+ Before the end of the stream is reached, this will be ``b""``.
+
+
+.. function:: compress(data, format=FORMAT_XZ, check=-1, preset=None, filters=None)
+
+ Compress *data* (a :class:`bytes` object), returning the compressed data as a
+ :class:`bytes` object.
+
+ See :class:`LZMACompressor` above for a description of the *format*, *check*,
+ *preset* and *filters* arguments.
+
+
+.. function:: decompress(data, format=FORMAT_AUTO, memlimit=None, filters=None)
+
+ Decompress *data* (a :class:`bytes` object), returning the uncompressed data
+ as a :class:`bytes` object.
+
+ If *data* is the concatenation of multiple distinct compressed streams,
+ decompress all of these streams, and return the concatenation of the results.
+
+ See :class:`LZMADecompressor` above for a description of the *format*,
+ *memlimit* and *filters* arguments.
+
+
+Miscellaneous
+-------------
+
+.. function:: check_is_supported(check)
+
+ Returns true if the given integrity check is supported on this system.
+
+ :const:`CHECK_NONE` and :const:`CHECK_CRC32` are always supported.
+ :const:`CHECK_CRC64` and :const:`CHECK_SHA256` may be unavailable if you are
+ using a version of :program:`liblzma` that was compiled with a limited
+ feature set.
+
+
+.. _filter-chain-specs:
+
+Specifying custom filter chains
+-------------------------------
+
+A filter chain specifier is a sequence of dictionaries, where each dictionary
+contains the ID and options for a single filter. Each dictionary must contain
+the key ``"id"``, and may contain additional keys to specify filter-dependent
+options. Valid filter IDs are as follows:
+
+* Compression filters:
+ * :const:`FILTER_LZMA1` (for use with :const:`FORMAT_ALONE`)
+ * :const:`FILTER_LZMA2` (for use with :const:`FORMAT_XZ` and :const:`FORMAT_RAW`)
+
+* Delta filter:
+ * :const:`FILTER_DELTA`
+
+* Branch-Call-Jump (BCJ) filters:
+ * :const:`FILTER_X86`
+ * :const:`FILTER_IA64`
+ * :const:`FILTER_ARM`
+ * :const:`FILTER_ARMTHUMB`
+ * :const:`FILTER_POWERPC`
+ * :const:`FILTER_SPARC`
+
+A filter chain can consist of up to 4 filters, and cannot be empty. The last
+filter in the chain must be a compression filter, and any other filters must be
+delta or BCJ filters.
+
+Compression filters support the following options (specified as additional
+entries in the dictionary representing the filter):
+
+ * ``preset``: A compression preset to use as a source of default values for
+ options that are not specified explicitly.
+ * ``dict_size``: Dictionary size in bytes. This should be between 4KiB and
+ 1.5GiB (inclusive).
+ * ``lc``: Number of literal context bits.
+ * ``lp``: Number of literal position bits. The sum ``lc + lp`` must be at
+ most 4.
+ * ``pb``: Number of position bits; must be at most 4.
+ * ``mode``: :const:`MODE_FAST` or :const:`MODE_NORMAL`.
+ * ``nice_len``: What should be considered a "nice length" for a match.
+ This should be 273 or less.
+ * ``mf``: What match finder to use -- :const:`MF_HC3`, :const:`MF_HC4`,
+ :const:`MF_BT2`, :const:`MF_BT3`, or :const:`MF_BT4`.
+ * ``depth``: Maximum search depth used by match finder. 0 (default) means to
+ select automatically based on other filter options.
+
+The delta filter stores the differences between bytes, producing more repetitive
+input for the compressor in certain circumstances. It only supports a single
+The delta filter supports only one option, ``dist``. This indicates the distance
+between bytes to be subtracted. The default is 1, i.e. take the differences
+between adjacent bytes.
+
+The BCJ filters are intended to be applied to machine code. They convert
+relative branches, calls and jumps in the code to use absolute addressing, with
+the aim of increasing the redundancy that can be exploited by the compressor.
+These filters support one option, ``start_offset``. This specifies the address
+that should be mapped to the beginning of the input data. The default is 0.
+
+
+Examples
+--------
+
+Reading in a compressed file::
+
+ import lzma
+ with lzma.LZMAFile("file.xz") as f:
+ file_content = f.read()
+
+Creating a compressed file::
+
+ import lzma
+ data = b"Insert Data Here"
+ with lzma.LZMAFile("file.xz", "w") as f:
+ f.write(data)
+
+Compressing data in memory::
+
+ import lzma
+ data_in = b"Insert Data Here"
+ data_out = lzma.compress(data_in)
+
+Incremental compression::
+
+ import lzma
+ lzc = lzma.LZMACompressor()
+ out1 = lzc.compress(b"Some data\n")
+ out2 = lzc.compress(b"Another piece of data\n")
+ out3 = lzc.compress(b"Even more data\n")
+ out4 = lzc.flush()
+ # Concatenate all the partial results:
+ result = b"".join([out1, out2, out3, out4])
+
+Writing compressed data to an already-open file::
+
+ import lzma
+ with open("file.xz", "wb") as f:
+ f.write(b"This data will not be compressed\n")
+ with lzma.LZMAFile(fileobj=f, mode="w") as lzf:
+ lzf.write(b"This *will* be compressed\n")
+ f.write(b"Not compressed\n")
+
+Creating a compressed file using a custom filter chain::
+
+ import lzma
+ my_filters = [
+ {"id": lzma.FILTER_DELTA, "dist": 5},
+ {"id": lzma.FILTER_LZMA2, "preset": 7 | lzma.PRESET_EXTREME},
+ ]
+ with lzma.LZMAFile("file.xz", "w", filters=my_filters) as f:
+ f.write(b"blah blah blah")
diff --git a/Doc/library/math.rst b/Doc/library/math.rst
index 98c5b33..62c0f34 100644
--- a/Doc/library/math.rst
+++ b/Doc/library/math.rst
@@ -184,6 +184,19 @@ Power and logarithmic functions
result is calculated in a way which is accurate for *x* near zero.
+.. function:: log2(x)
+
+ Return the base-2 logarithm of *x*. This is usually more accurate than
+ ``log(x, 2)``.
+
+ .. versionadded:: 3.3
+
+ .. seealso::
+
+ :meth:`int.bit_length` returns the number of bits necessary to represent
+ an integer in binary, excluding the sign and leading zeros.
+
+
.. function:: log10(x)
Return the base-10 logarithm of *x*. This is usually more accurate
diff --git a/Doc/library/mmap.rst b/Doc/library/mmap.rst
index 5f0f004..1a19a7e 100644
--- a/Doc/library/mmap.rst
+++ b/Doc/library/mmap.rst
@@ -196,12 +196,16 @@ To map anonymous memory, -1 should be passed as the fileno along with the length
move will raise a :exc:`TypeError` exception.
- .. method:: read(num)
+ .. method:: read([n])
- Return a :class:`bytes` containing up to *num* bytes starting from the
- current file position; the file position is updated to point after the
- bytes that were returned.
+ Return a :class:`bytes` containing up to *n* bytes starting from the
+ current file position. If the argument is omitted, *None* or negative,
+ return all bytes from the current file position to the end of the
+ mapping. The file position is updated to point after the bytes that were
+ returned.
+ .. versionchanged:: 3.3
+ Argument can be omitted or *None*.
.. method:: read_byte()
diff --git a/Doc/library/msvcrt.rst b/Doc/library/msvcrt.rst
index 889a0c5..9d23720 100644
--- a/Doc/library/msvcrt.rst
+++ b/Doc/library/msvcrt.rst
@@ -20,6 +20,11 @@ api. The normal API deals only with ASCII characters and is of limited use
for internationalized applications. The wide char API should be used where
ever possible
+.. versionchanged:: 3.3
+ Operations in this module now raise :exc:`OSError` where :exc:`IOError`
+ was raised.
+
+
.. _msvcrt-files:
File Operations
@@ -29,7 +34,7 @@ File Operations
.. function:: locking(fd, mode, nbytes)
Lock part of a file based on file descriptor *fd* from the C runtime. Raises
- :exc:`IOError` on failure. The locked region of the file extends from the
+ :exc:`OSError` on failure. The locked region of the file extends from the
current file position for *nbytes* bytes, and may continue beyond the end of the
file. *mode* must be one of the :const:`LK_\*` constants listed below. Multiple
regions in a file may be locked at the same time, but may not overlap. Adjacent
@@ -41,13 +46,13 @@ File Operations
Locks the specified bytes. If the bytes cannot be locked, the program
immediately tries again after 1 second. If, after 10 attempts, the bytes cannot
- be locked, :exc:`IOError` is raised.
+ be locked, :exc:`OSError` is raised.
.. data:: LK_NBLCK
LK_NBRLCK
- Locks the specified bytes. If the bytes cannot be locked, :exc:`IOError` is
+ Locks the specified bytes. If the bytes cannot be locked, :exc:`OSError` is
raised.
@@ -73,7 +78,7 @@ File Operations
.. function:: get_osfhandle(fd)
- Return the file handle for the file descriptor *fd*. Raises :exc:`IOError` if
+ Return the file handle for the file descriptor *fd*. Raises :exc:`OSError` if
*fd* is not recognized.
@@ -144,4 +149,4 @@ Other Functions
.. function:: heapmin()
Force the :c:func:`malloc` heap to clean itself up and return unused blocks to
- the operating system. On failure, this raises :exc:`IOError`.
+ the operating system. On failure, this raises :exc:`OSError`.
diff --git a/Doc/library/multiprocessing.rst b/Doc/library/multiprocessing.rst
index d8a554d..851b3cf 100644
--- a/Doc/library/multiprocessing.rst
+++ b/Doc/library/multiprocessing.rst
@@ -297,7 +297,7 @@ The :mod:`multiprocessing` package mostly replicates the API of the
:class:`Process` and exceptions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.. class:: Process([group[, target[, name[, args[, kwargs]]]]])
+.. class:: Process([group[, target[, name[, args[, kwargs]]]]], *, daemon=None)
Process objects represent activity that is run in a separate process. The
:class:`Process` class has equivalents of all the methods of
@@ -312,13 +312,19 @@ The :mod:`multiprocessing` package mostly replicates the API of the
:sub:`1`,N\ :sub:`2`,...,N\ :sub:`k` is a sequence of integers whose length
is determined by the *generation* of the process. *args* is the argument
tuple for the target invocation. *kwargs* is a dictionary of keyword
- arguments for the target invocation. By default, no arguments are passed to
- *target*.
+ arguments for the target invocation. If provided, the keyword-only *daemon* argument
+ sets the process :attr:`daemon` flag to ``True`` or ``False``. If ``None``
+ (the default), this flag will be inherited from the creating process.
+
+ By default, no arguments are passed to *target*.
If a subclass overrides the constructor, it must make sure it invokes the
base class constructor (:meth:`Process.__init__`) before doing anything else
to the process.
+ .. versionchanged:: 3.3
+ Added the *daemon* argument.
+
.. method:: run()
Method representing the process's activity.
@@ -337,10 +343,9 @@ The :mod:`multiprocessing` package mostly replicates the API of the
.. method:: join([timeout])
- Block the calling thread until the process whose :meth:`join` method is
- called terminates or until the optional timeout occurs.
-
- If *timeout* is ``None`` then there is no timeout.
+ If the optional argument *timeout* is ``None`` (the default), the method
+ blocks until the process whose :meth:`join` method is called terminates.
+ If *timeout* is a positive number, it blocks at most *timeout* seconds.
A process can be joined many times.
@@ -405,6 +410,20 @@ The :mod:`multiprocessing` package mostly replicates the API of the
See :ref:`multiprocessing-auth-keys`.
+ .. attribute:: sentinel
+
+ A numeric handle of a system object which will become "ready" when
+ the process ends.
+
+ On Windows, this is an OS handle usable with the ``WaitForSingleObject``
+ and ``WaitForMultipleObjects`` family of API calls. On Unix, this is
+ a file descriptor usable with primitives from the :mod:`select` module.
+
+ You can use this value if you want to wait on several events at once.
+ Otherwise calling :meth:`join()` is simpler.
+
+ .. versionadded:: 3.3
+
.. method:: terminate()
Terminate the process. On Unix this is done using the ``SIGTERM`` signal;
@@ -765,9 +784,14 @@ Connection objects usually created using :func:`Pipe` -- see also
to receive and the other end has closed.
If *maxlength* is specified and the message is longer than *maxlength*
- then :exc:`IOError` is raised and the connection will no longer be
+ then :exc:`OSError` is raised and the connection will no longer be
readable.
+ .. versionchanged:: 3.3
+ This function used to raise a :exc:`IOError`, which is now an
+ alias of :exc:`OSError`.
+
+
.. method:: recv_bytes_into(buffer[, offset])
Read into *buffer* a complete message of byte data sent from the other end
diff --git a/Doc/library/nntplib.rst b/Doc/library/nntplib.rst
index ef507e1..62da72c 100644
--- a/Doc/library/nntplib.rst
+++ b/Doc/library/nntplib.rst
@@ -70,10 +70,23 @@ The module itself defines the following classes:
connecting to an NNTP server on the local machine and intend to call
reader-specific commands, such as ``group``. If you get unexpected
:exc:`NNTPPermanentError`\ s, you might need to set *readermode*.
+ :class:`NNTP` class supports the :keyword:`with` statement to
+ unconditionally consume :exc:`socket.error` exceptions and to close the NNTP
+ connection when done. Here is a sample on how using it:
+
+ >>> from nntplib import NNTP
+ >>> with NNTP('news.gmane.org') as n:
+ ... n.group('gmane.comp.python.committers')
+ ...
+ ('211 1755 1 1755 gmane.comp.python.committers', 1755, 1, 1755, 'gmane.comp.python.committers')
+ >>>
+
.. versionchanged:: 3.2
*usenetrc* is now False by default.
+ .. versionchanged:: 3.3
+ Support for the :keyword:`with` statement was added.
.. class:: NNTP_SSL(host, port=563, user=None, password=None, ssl_context=None, readermode=None, usenetrc=False, [timeout])
@@ -504,6 +517,9 @@ them have been superseded by newer commands in :rfc:`3977`.
article with message ID *id*. Most of the time, this extension is not
enabled by NNTP server administrators.
+ .. deprecated:: 3.3
+ The XPATH extension is not actively used.
+
.. XXX deprecated:
diff --git a/Doc/library/os.rst b/Doc/library/os.rst
index df9a6b6..bb1ebd9 100644
--- a/Doc/library/os.rst
+++ b/Doc/library/os.rst
@@ -221,6 +221,17 @@ process and user.
Availability: Unix.
+.. function:: getgrouplist(user, group)
+
+ Return list of group ids that *user* belongs to. If *group* is not in the
+ list, it is included; typically, *group* is specified as the group ID
+ field from the password record for *user*.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
.. function:: getgroups()
Return list of supplemental group ids associated with the current process.
@@ -288,6 +299,22 @@ process and user.
.. versionchanged:: 3.2
Added support for Windows.
+.. function:: getpriority(which, who)
+
+ .. index:: single: process; scheduling priority
+
+ Get program scheduling priority. The value *which* is one of
+ :const:`PRIO_PROCESS`, :const:`PRIO_PGRP`, or :const:`PRIO_USER`, and *who*
+ is interpreted relative to *which* (a process identifier for
+ :const:`PRIO_PROCESS`, process group identifier for :const:`PRIO_PGRP`, and a
+ user ID for :const:`PRIO_USER`). A zero value for *who* denotes
+ (respectively) the calling process, the process group of the calling process,
+ or the real user ID of the calling process.
+
+ Availability: Unix
+
+ .. versionadded:: 3.3
+
.. function:: getresuid()
Return a tuple (ruid, euid, suid) denoting the current process's
@@ -338,6 +365,15 @@ process and user.
.. versionadded:: 3.2
+.. data:: PRIO_PROCESS
+ PRIO_PGRP
+ PRIO_USER
+
+ Parameters for :func:`getpriority` and :func:`setpriority` functions.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
.. function:: putenv(key, value)
@@ -407,6 +443,25 @@ process and user.
Availability: Unix.
+.. function:: setpriority(which, who, priority)
+
+ .. index:: single: process; scheduling priority
+
+ Set program scheduling priority. The value *which* is one of
+ :const:`PRIO_PROCESS`, :const:`PRIO_PGRP`, or :const:`PRIO_USER`, and *who*
+ is interpreted relative to *which* (a process identifier for
+ :const:`PRIO_PROCESS`, process group identifier for :const:`PRIO_PGRP`, and a
+ user ID for :const:`PRIO_USER`). A zero value for *who* denotes
+ (respectively) the calling process, the process group of the calling process,
+ or the real user ID of the calling process.
+ *priority* is a value in the range -20 to 19. The default priority is 0;
+ lower priorities cause more favorable scheduling.
+
+ Availability: Unix
+
+ .. versionadded:: 3.3
+
+
.. function:: setregid(rgid, egid)
Set the current process's real and effective group ids.
@@ -564,6 +619,21 @@ associated with a :term:`file object` when required. Note that using the file
descriptor directly will bypass the file object methods, ignoring aspects such
as internal buffering of data.
+.. data:: AT_SYMLINK_NOFOLLOW
+ AT_EACCESS
+ AT_FDCWD
+ AT_REMOVEDIR
+ AT_SYMLINK_FOLLOW
+ UTIME_NOW
+ UTIME_OMIT
+
+ These parameters are used as flags to the \*at family of functions.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
.. function:: close(fd)
Close file descriptor *fd*.
@@ -612,6 +682,19 @@ as internal buffering of data.
Availability: Unix, Windows.
+.. function:: faccessat(dirfd, path, mode, flags=0)
+
+ Like :func:`access` but if *path* is relative, it is taken as relative to *dirfd*.
+ *flags* is optional and can be constructed by ORing together zero or more
+ of these values: :data:`AT_SYMLINK_NOFOLLOW`, :data:`AT_EACCESS`.
+ If *path* is relative and *dirfd* is the special value :data:`AT_FDCWD`, then *path*
+ is interpreted relative to the current working directory.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
.. function:: fchmod(fd, mode)
Change the mode of the file given by *fd* to the numeric *mode*. See the docs
@@ -620,6 +703,18 @@ as internal buffering of data.
Availability: Unix.
+.. function:: fchmodat(dirfd, path, mode, flags=0)
+
+ Like :func:`chmod` but if *path* is relative, it is taken as relative to *dirfd*.
+ *flags* is optional and may be 0 or :data:`AT_SYMLINK_NOFOLLOW`.
+ If *path* is relative and *dirfd* is the special value :data:`AT_FDCWD`, then *path*
+ is interpreted relative to the current working directory.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
.. function:: fchown(fd, uid, gid)
Change the owner and group id of the file given by *fd* to the numeric *uid*
@@ -628,6 +723,18 @@ as internal buffering of data.
Availability: Unix.
+.. function:: fchownat(dirfd, path, uid, gid, flags=0)
+
+ Like :func:`chown` but if *path* is relative, it is taken as relative to *dirfd*.
+ *flags* is optional and may be 0 or :data:`AT_SYMLINK_NOFOLLOW`.
+ If *path* is relative and *dirfd* is the special value :data:`AT_FDCWD`, then *path*
+ is interpreted relative to the current working directory.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
.. function:: fdatasync(fd)
Force write of file with filedescriptor *fd* to disk. Does not force update of
@@ -639,6 +746,47 @@ as internal buffering of data.
This function is not available on MacOS.
+.. function:: fgetxattr(fd, attr)
+
+ This works exactly like :func:`getxattr` but operates on a file descriptor,
+ *fd*, instead of a path.
+
+ Availability: Linux
+
+ .. versionadded:: 3.3
+
+
+.. function:: flistxattr(fd)
+
+ This is exactly like :func:`listxattr` but operates on a file descriptor,
+ *fd*, instead of a path.
+
+ Availability: Linux
+
+ .. versionadded:: 3.3
+
+
+.. function:: fdlistdir(fd)
+
+ Like :func:`listdir`, but uses a file descriptor instead and always returns
+ strings. After execution of this function, *fd* will be closed.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
+.. function:: fexecve(fd, args, env)
+
+ Execute the program specified by a file descriptor *fd* with arguments given
+ by *args* and environment given by *env*, replacing the current process.
+ *args* and *env* are given as in :func:`execve`.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
.. function:: fpathconf(fd, name)
Return system configuration information relevant to an open file. *name*
@@ -663,6 +811,17 @@ as internal buffering of data.
Availability: Unix, Windows.
+.. function:: fstatat(dirfd, path, flags=0)
+
+ Like :func:`stat` but if *path* is relative, it is taken as relative to *dirfd*.
+ *flags* is optional and may be 0 or :data:`AT_SYMLINK_NOFOLLOW`.
+ If *path* is relative and *dirfd* is the special value :data:`AT_FDCWD`, then *path*
+ is interpreted relative to the current working directory.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
.. function:: fstatvfs(fd)
@@ -692,6 +851,80 @@ as internal buffering of data.
Availability: Unix.
+.. function:: fremovexattr(fd, attr)
+
+ This works exactly like :func:`removexattr` but operates on a file
+ descriptor, *fd*, instead of a path.
+
+ Availability: Linux
+
+ .. versionadded:: 3.3
+
+
+.. function:: fsetxattr(fd, attr, value, flags=0)
+
+ This works exactly like :func:`setxattr` but on a file descriptor, *fd*,
+ instead of a path.
+
+
+ Availability: Linux
+
+ .. versionadded:: 3.3
+
+
+.. function:: futimesat(dirfd, path[, times])
+
+ Like :func:`utime` but if *path* is relative, it is taken as relative to *dirfd*.
+ If *path* is relative and *dirfd* is the special value :data:`AT_FDCWD`, then *path*
+ is interpreted relative to the current working directory. *times* must be a
+ 2-tuple of numbers, of the form ``(atime, mtime)``, or None.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
+.. function:: futimens(fd[, atimes, mtimes])
+
+ Updates the timestamps of a file specified by the file descriptor *fd*, with
+ nanosecond precision.
+ If no second argument is given, set *atime* and *mtime* to the current time.
+ *atimes* and *mtimes* must be 2-tuples of numbers, of the form
+ ``(atime_sec, atime_nsec)`` and ``(mtime_sec, mtime_nsec)`` respectively,
+ or ``None``.
+ If *atime_nsec* or *mtime_nsec* is specified as :data:`UTIME_NOW`, the corresponding
+ timestamp is updated to the current time.
+ If *atime_nsec* or *mtime_nsec* is specified as :data:`UTIME_OMIT`, the corresponding
+ timestamp is not updated.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
+.. data:: UTIME_NOW
+ UTIME_OMIT
+
+ Flags used with :func:`futimens` to specify that the timestamp must be
+ updated either to the current time or not updated at all.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
+.. function:: futimes(fd[, times])
+
+ Set the access and modified time of the file specified by the file
+ descriptor *fd* to the given values. *atimes* must be a 2-tuple of numbers,
+ of the form ``(atime, mtime)``, or None. If no second argument is used,
+ set the access and modified times to the current time.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
.. function:: isatty(fd)
Return ``True`` if the file descriptor *fd* is open and connected to a
@@ -700,6 +933,44 @@ as internal buffering of data.
Availability: Unix.
+.. function:: linkat(srcfd, srcpath, dstfd, dstpath, flags=0)
+
+ Like :func:`link` but if *srcpath* is relative, it is taken as relative to *srcfd*
+ and if *dstpath* is relative, it is taken as relative to *dstfd*.
+ *flags* is optional and may be 0 or :data:`AT_SYMLINK_FOLLOW`.
+ If *srcpath* is relative and *srcfd* is the special value :data:`AT_FDCWD`, then
+ *srcpath* is interpreted relative to the current working directory. This
+ also applies for *dstpath*.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
+.. function:: lockf(fd, cmd, len)
+
+ Apply, test or remove a POSIX lock on an open file descriptor.
+ *fd* is an open file descriptor.
+ *cmd* specifies the command to use - one of :data:`F_LOCK`, :data:`F_TLOCK`,
+ :data:`F_ULOCK` or :data:`F_TEST`.
+ *len* specifies the section of the file to lock.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
+.. data:: F_LOCK
+ F_TLOCK
+ F_ULOCK
+ F_TEST
+
+ Flags that specify what action :func:`lockf` will take.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
.. function:: lseek(fd, pos, how)
Set the current position of file descriptor *fd* to position *pos*, modified
@@ -719,6 +990,39 @@ as internal buffering of data.
respectively. Availability: Windows, Unix.
+.. function:: mkdirat(dirfd, path, mode=0o777)
+
+ Like :func:`mkdir` but if *path* is relative, it is taken as relative to *dirfd*.
+ If *path* is relative and *dirfd* is the special value :data:`AT_FDCWD`, then *path*
+ is interpreted relative to the current working directory.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
+.. function:: mkfifoat(dirfd, path, mode=0o666)
+
+ Like :func:`mkfifo` but if *path* is relative, it is taken as relative to *dirfd*.
+ If *path* is relative and *dirfd* is the special value :data:`AT_FDCWD`, then *path*
+ is interpreted relative to the current working directory.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
+.. function:: mknodat(dirfd, path, mode=0o600, device=0)
+
+ Like :func:`mknod` but if *path* is relative, it is taken as relative to *dirfd*.
+ If *path* is relative and *dirfd* is the special value :data:`AT_FDCWD`, then *path*
+ is interpreted relative to the current working directory.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
.. function:: open(file, flags[, mode])
Open the file *file* and set various flags according to *flags* and possibly
@@ -741,6 +1045,17 @@ as internal buffering of data.
wrap a file descriptor in a file object, use :func:`fdopen`.
+.. function:: openat(dirfd, path, flags, mode=0o777)
+
+ Like :func:`open` but if *path* is relative, it is taken as relative to *dirfd*.
+ If *path* is relative and *dirfd* is the special value :data:`AT_FDCWD`, then *path*
+ is interpreted relative to the current working directory.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
.. function:: openpty()
.. index:: module: pty
@@ -760,6 +1075,79 @@ as internal buffering of data.
Availability: Unix, Windows.
+.. function:: pipe2(flags)
+
+ Create a pipe with *flags* set atomically.
+ *flags* can be constructed by ORing together one or more of these values:
+ :data:`O_NONBLOCK`, :data:`O_CLOEXEC`.
+ Return a pair of file descriptors ``(r, w)`` usable for reading and writing,
+ respectively.
+
+ Availability: some flavors of Unix.
+
+ .. versionadded:: 3.3
+
+
+.. function:: posix_fallocate(fd, offset, len)
+
+ Ensures that enough disk space is allocated for the file specified by *fd*
+ starting from *offset* and continuing for *len* bytes.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
+.. function:: posix_fadvise(fd, offset, len, advice)
+
+ Announces an intention to access data in a specific pattern thus allowing
+ the kernel to make optimizations.
+ The advice applies to the region of the file specified by *fd* starting at
+ *offset* and continuing for *len* bytes.
+ *advice* is one of :data:`POSIX_FADV_NORMAL`, :data:`POSIX_FADV_SEQUENTIAL`,
+ :data:`POSIX_FADV_RANDOM`, :data:`POSIX_FADV_NOREUSE`,
+ :data:`POSIX_FADV_WILLNEED` or :data:`POSIX_FADV_DONTNEED`.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
+.. data:: POSIX_FADV_NORMAL
+ POSIX_FADV_SEQUENTIAL
+ POSIX_FADV_RANDOM
+ POSIX_FADV_NOREUSE
+ POSIX_FADV_WILLNEED
+ POSIX_FADV_DONTNEED
+
+ Flags that can be used in *advice* in :func:`posix_fadvise` that specify
+ the access pattern that is likely to be used.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
+.. function:: pread(fd, buffersize, offset)
+
+ Read from a file descriptor, *fd*, at a position of *offset*. It will read up
+ to *buffersize* number of bytes. The file offset remains unchanged.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
+.. function:: pwrite(fd, string, offset)
+
+ Write *string* to a file descriptor, *fd*, from *offset*, leaving the file
+ offset unchanged.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
.. function:: read(fd, n)
Read at most *n* bytes from file descriptor *fd*. Return a bytestring containing the
@@ -777,6 +1165,93 @@ as internal buffering of data.
:meth:`~file.readline` methods.
+.. function:: sendfile(out, in, offset, nbytes)
+ sendfile(out, in, offset, nbytes, headers=None, trailers=None, flags=0)
+
+ Copy *nbytes* bytes from file descriptor *in* to file descriptor *out*
+ starting at *offset*.
+ Return the number of bytes sent. When EOF is reached return 0.
+
+ The first function notation is supported by all platforms that define
+ :func:`sendfile`.
+
+ On Linux, if *offset* is given as ``None``, the bytes are read from the
+ current position of *in* and the position of *in* is updated.
+
+ The second case may be used on Mac OS X and FreeBSD where *headers* and
+ *trailers* are arbitrary sequences of buffers that are written before and
+ after the data from *in* is written. It returns the same as the first case.
+
+ On Mac OS X and FreeBSD, a value of 0 for *nbytes* specifies to send until
+ the end of *in* is reached.
+
+ On Solaris, *out* may be the file descriptor of a regular file or the file
+ descriptor of a socket. On all other platforms, *out* must be the file
+ descriptor of an open socket.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
+.. data:: SF_NODISKIO
+ SF_MNOWAIT
+ SF_SYNC
+
+ Parameters to the :func:`sendfile` function, if the implementation supports
+ them.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
+.. function:: readlinkat(dirfd, path)
+
+ Like :func:`readlink` but if *path* is relative, it is taken as relative to *dirfd*.
+ If *path* is relative and *dirfd* is the special value :data:`AT_FDCWD`, then *path*
+ is interpreted relative to the current working directory.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
+.. function:: renameat(olddirfd, oldpath, newdirfd, newpath)
+
+ Like :func:`rename` but if *oldpath* is relative, it is taken as relative to
+ *olddirfd* and if *newpath* is relative, it is taken as relative to *newdirfd*.
+ If *oldpath* is relative and *olddirfd* is the special value :data:`AT_FDCWD`, then
+ *oldpath* is interpreted relative to the current working directory. This
+ also applies for *newpath*.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
+.. function:: symlinkat(src, dstfd, dst)
+
+ Like :func:`symlink` but if *dst* is relative, it is taken as relative to *dstfd*.
+ If *dst* is relative and *dstfd* is the special value :data:`AT_FDCWD`, then *dst*
+ is interpreted relative to the current working directory.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
+.. function:: readv(fd, buffers)
+
+ Read from a file descriptor into a number of writable buffers. *buffers* is
+ an arbitrary sequence of writable buffers. Returns the total number of bytes
+ read.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
.. function:: tcgetpgrp(fd)
Return the process group associated with the terminal given by *fd* (an open
@@ -802,6 +1277,38 @@ as internal buffering of data.
Availability: Unix.
+.. function:: unlinkat(dirfd, path, flags=0)
+
+ Like :func:`unlink` but if *path* is relative, it is taken as relative to *dirfd*.
+ *flags* is optional and may be 0 or :data:`AT_REMOVEDIR`. If :data:`AT_REMOVEDIR` is
+ specified, :func:`unlinkat` behaves like :func:`rmdir`.
+ If *path* is relative and *dirfd* is the special value :data:`AT_FDCWD`, then *path*
+ is interpreted relative to the current working directory.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
+.. function:: utimensat(dirfd, path[, atime=(atime_sec, atime_nsec), mtime=(mtime_sec, mtime_nsec), flags=0])
+
+ Updates the timestamps of a file with nanosecond precision.
+ The *atime* and *mtime* tuples default to ``None``, which sets those
+ values to the current time.
+ If *atime_nsec* or *mtime_nsec* is specified as :data:`UTIME_NOW`, the corresponding
+ timestamp is updated to the current time.
+ If *atime_nsec* or *mtime_nsec* is specified as :data:`UTIME_OMIT`, the corresponding
+ timestamp is not updated.
+ If *path* is relative, it is taken as relative to *dirfd*.
+ *flags* is optional and may be 0 (the default) or :data:`AT_SYMLINK_NOFOLLOW`.
+ If *path* is relative and *dirfd* is the special value :data:`AT_FDCWD`, then *path*
+ is interpreted relative to the current working directory.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
.. function:: write(fd, str)
Write the bytestring in *str* to file descriptor *fd*. Return the number of
@@ -818,6 +1325,17 @@ as internal buffering of data.
:meth:`~file.write` method.
+.. function:: writev(fd, buffers)
+
+ Write the contents of *buffers* to file descriptor *fd*, where *buffers*
+ is an arbitrary sequence of buffers.
+ Returns the total number of bytes written.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
.. _open-constants:
``open()`` flag constants
@@ -849,9 +1367,12 @@ or `the MSDN <http://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Window
O_NOCTTY
O_SHLOCK
O_EXLOCK
+ O_CLOEXEC
These constants are only available on Unix.
+ .. versionchanged:: 3.3
+ Add :data:`O_CLOEXEC` constant.
.. data:: O_BINARY
O_NOINHERIT
@@ -874,6 +1395,19 @@ or `the MSDN <http://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Window
the C library.
+.. data:: RTLD_LAZY
+ RTLD_NOW
+ RTLD_GLOBAL
+ RTLD_LOCAL
+ RTLD_NODELETE
+ RTLD_NOLOAD
+ RTLD_DEEPBIND
+
+ See the Unix manual page :manpage:`dlopen(3)`.
+
+ .. versionadded:: 3.3
+
+
.. _os-file-dir:
Files and Directories
@@ -909,11 +1443,8 @@ Files and Directories
try:
fp = open("myfile")
- except IOError as e:
- if e.errno == errno.EACCES:
- return "some default data"
- # Not a permission error.
- raise
+ except PermissionError:
+ return "some default data"
else:
with fp:
return fp.read()
@@ -1049,9 +1580,23 @@ Files and Directories
Change the owner and group id of *path* to the numeric *uid* and *gid*. To leave
one of the ids unchanged, set it to -1.
+ See :func:`shutil.chown` for a higher-level function that accepts names in
+ addition to numeric ids.
+
Availability: Unix.
+.. function:: getxattr(path, attr)
+
+ Return the value of the extended filesystem attribute *attr* for
+ *path*. *attr* can be bytes or str. If it is str, it is encoded with the
+ filesystem encoding.
+
+ Availability: Linux
+
+ .. versionadded:: 3.3
+
+
.. function:: lchflags(path, flags)
Set the flags of *path* to the numeric *flags*, like :func:`chflags`, but do not
@@ -1077,6 +1622,15 @@ Files and Directories
Availability: Unix.
+.. function:: lgetxattr(path, attr)
+
+ This works exactly like :func:`getxattr` but doesn't follow symlinks.
+
+ Availability: Linux
+
+ .. versionadded:: 3.3
+
+
.. function:: link(source, link_name)
Create a hard link pointing to *source* named *link_name*.
@@ -1101,6 +1655,44 @@ Files and Directories
.. versionchanged:: 3.2
The *path* parameter became optional.
+
+.. function:: listxattr(path)
+
+ Return a list of the extended filesystem attributes on *path*. Attributes are
+ returned as string decoded with the filesystem encoding.
+
+ Availability: Linux
+
+ .. versionadded:: 3.3
+
+
+.. function:: llistxattr(path)
+
+ This works exactly like :func:`listxattr` but doesn't follow symlinks.
+
+ Availability: Linux
+
+ .. versionadded:: 3.3
+
+
+.. function:: lremovexattr(path, attr)
+
+ This works exactly like :func:`removexattr` but doesn't follow symlinks.
+
+ Availability: Linux
+
+ .. versionadded:: 3.3
+
+
+.. function:: lsetxattr(path, attr, value, flags=0)
+
+ This works exactly like :func:`setxattr` but doesn't follow symlinks.
+
+ Availability: Linux
+
+ .. versionadded:: 3.3
+
+
.. function:: lstat(path)
Perform the equivalent of an :c:func:`lstat` system call on the given path.
@@ -1112,6 +1704,18 @@ Files and Directories
Added support for Windows 6.0 (Vista) symbolic links.
+.. function:: lutimes(path[, times])
+
+ Like :func:`utime`, but if *path* is a symbolic link, it is not
+ dereferenced. *times* must be a 2-tuple of numbers, of the form
+ ``(atime, mtime)``, or None.
+
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
.. function:: mkfifo(path[, mode])
Create a FIFO (a named pipe) named *path* with numeric mode *mode*. The
@@ -1263,6 +1867,17 @@ Files and Directories
successfully removed.
+.. function:: removexattr(path, attr)
+
+ Removes the extended filesystem attribute *attr* from *path*. *attr* should
+ be bytes or str. If it is a string, it is encoded with the filesystem
+ encoding.
+
+ Availability: Linux
+
+ .. versionadded:: 3.3
+
+
.. function:: rename(src, dst)
Rename the file or directory *src* to *dst*. If *dst* is a directory,
@@ -1299,6 +1914,44 @@ Files and Directories
Availability: Unix, Windows.
+.. data:: XATTR_SIZE_MAX
+
+ The maximum size the value of an extended attribute can be. Currently, this
+ is 64 kilobytes on Linux.
+
+
+.. data:: XATTR_CREATE
+
+ This is a possible value for the flags argument in :func:`setxattr`. It
+ indicates the operation must create an attribute.
+
+
+.. data:: XATTR_REPLACE
+
+ This is a possible value for the flags argument in :func:`setxattr`. It
+ indicates the operation must replace an existing attribute.
+
+
+.. function:: setxattr(path, attr, value, flags=0)
+
+ Set the extended filesystem attribute *attr* on *path* to *value*. *attr*
+ must be a bytes or str with no embedded NULs. If it is str, it is encoded
+ with the filesystem encoding. *flags* may be :data:`XATTR_REPLACE` or
+ :data:`XATTR_CREATE`. If :data:`XATTR_REPLACE` is given and the attribute
+ does not exist, ``EEXISTS`` will be raised. If :data:`XATTR_CREATE` is given
+ and the attribute already exists, the attribute will not be created and
+ ``ENODATA`` will be raised.
+
+ Availability: Linux
+
+ .. note::
+
+ A bug in Linux kernel versions less than 2.6.39 caused the flags argument
+ to be ignored on some filesystems.
+
+ .. versionadded:: 3.3
+
+
.. function:: stat(path)
Perform the equivalent of a :c:func:`stat` system call on the given path.
@@ -1456,6 +2109,25 @@ Files and Directories
Added support for Windows 6.0 (Vista) symbolic links.
+.. function:: sync()
+
+ Force write of everything to disk.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
+.. function:: truncate(path, length)
+
+ Truncate the file corresponding to *path*, so that it is at most
+ *length* bytes in size.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
.. function:: unlink(path)
Remove (delete) the file *path*. This is the same function as
@@ -1465,18 +2137,19 @@ Files and Directories
Availability: Unix, Windows.
-.. function:: utime(path, times)
+.. function:: utime(path[, times])
Set the access and modified times of the file specified by *path*. If *times*
- is ``None``, then the file's access and modified times are set to the current
- time. (The effect is similar to running the Unix program :program:`touch` on
- the path.) Otherwise, *times* must be a 2-tuple of numbers, of the form
- ``(atime, mtime)`` which is used to set the access and modified times,
- respectively. Whether a directory can be given for *path* depends on whether
- the operating system implements directories as files (for example, Windows
- does not). Note that the exact times you set here may not be returned by a
- subsequent :func:`~os.stat` call, depending on the resolution with which your
- operating system records access and modification times; see :func:`~os.stat`.
+ is ``None`` or not specified, then the file's access and modified times are
+ set to the current time. (The effect is similar to running the Unix program
+ :program:`touch` on the path.) Otherwise, *times* must be a 2-tuple of
+ numbers, of the form ``(atime, mtime)`` which is used to set the access and
+ modified times, respectively. Whether a directory can be given for *path*
+ depends on whether the operating system implements directories as files
+ (for example, Windows does not). Note that the exact times you set here may
+ not be returned by a subsequent :func:`~os.stat` call, depending on the
+ resolution with which your operating system records access and modification
+ times; see :func:`~os.stat`.
Availability: Unix, Windows.
@@ -1828,6 +2501,8 @@ written in Python, such as a mail server's external command delivery program.
will be set to *sig*. The Windows version of :func:`kill` additionally takes
process handles to be killed.
+ See also :func:`signal.pthread_kill`.
+
.. versionadded:: 3.2
Windows support.
@@ -2038,6 +2713,58 @@ written in Python, such as a mail server's external command delivery program.
Availability: Unix.
+.. function:: waitid(idtype, id, options)
+
+ Wait for the completion of one or more child processes.
+ *idtype* can be :data:`P_PID`, :data:`P_PGID` or :data:`P_ALL`.
+ *id* specifies the pid to wait on.
+ *options* is constructed from the ORing of one or more of :data:`WEXITED`,
+ :data:`WSTOPPED` or :data:`WCONTINUED` and additionally may be ORed with
+ :data:`WNOHANG` or :data:`WNOWAIT`. The return value is an object
+ representing the data contained in the :c:type:`siginfo_t` structure, namely:
+ :attr:`si_pid`, :attr:`si_uid`, :attr:`si_signo`, :attr:`si_status`,
+ :attr:`si_code` or ``None`` if :data:`WNOHANG` is specified and there are no
+ children in a waitable state.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+.. data:: P_PID
+ P_PGID
+ P_ALL
+
+ These are the possible values for *idtype* in :func:`waitid`. They affect
+ how *id* is interpreted.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+.. data:: WEXITED
+ WSTOPPED
+ WNOWAIT
+
+ Flags that can be used in *options* in :func:`waitid` that specify what
+ child signal to wait for.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
+.. data:: CLD_EXITED
+ CLD_DUMPED
+ CLD_TRAPPED
+ CLD_CONTINUED
+
+ These are the possible values for :attr:`si_code` in the result returned by
+ :func:`waitid`.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
.. function:: waitpid(pid, options)
@@ -2179,6 +2906,155 @@ used to determine the disposition of a process.
Availability: Unix.
+Interface to the scheduler
+--------------------------
+
+These functions control how a process is allocated CPU time by the operating
+system. They are only available on some Unix platforms. For more detailed
+information, consult your Unix manpages.
+
+.. versionadded:: 3.3
+
+The following scheduling policies are exposed if they are a supported by the
+operating system.
+
+.. data:: SCHED_OTHER
+
+ The default scheduling policy.
+
+.. data:: SCHED_BATCH
+
+ Scheduling policy for CPU-intensive processes that tries to preserve
+ interactivity on the rest of the computer.
+
+.. data:: SCHED_IDLE
+
+ Scheduling policy for extremely low priority background tasks.
+
+.. data:: SCHED_SPORADIC
+
+ Scheduling policy for sporadic server programs.
+
+.. data:: SCHED_FIFO
+
+ A First In First Out scheduling policy.
+
+.. data:: SCHED_RR
+
+ A round-robin scheduling policy.
+
+.. data:: SCHED_RESET_ON_FORK
+
+ This flag can OR'ed with any other scheduling policy. When a process with
+ this flag set forks, its child's scheduling policy and priority are reset to
+ the default.
+
+
+.. class:: sched_param(sched_priority)
+
+ This class represents tunable scheduling parameters used in
+ :func:`sched_setparam`, :func:`sched_setscheduler`, and
+ :func:`sched_getparam`. It is immutable.
+
+ At the moment, there is only one possible parameter:
+
+ .. attribute:: sched_priority
+
+ The scheduling priority for a scheduling policy.
+
+
+.. function:: sched_get_priority_min(policy)
+
+ Get the minimum priority value for *policy*. *policy* is one of the
+ scheduling policy constants above.
+
+
+.. function:: sched_get_priority_max(policy)
+
+ Get the maximum priority value for *policy*. *policy* is one of the
+ scheduling policy constants above.
+
+
+.. function:: sched_setscheduler(pid, policy, param)
+
+ Set the scheduling policy for the process with PID *pid*. A *pid* of 0 means
+ the calling process. *policy* is one of the scheduling policy constants
+ above. *param* is a :class:`sched_param` instance.
+
+
+.. function:: sched_getscheduler(pid)
+
+ Return the scheduling policy for the process with PID *pid*. A *pid* of 0
+ means the calling process. The result is one of the scheduling policy
+ constants above.
+
+
+.. function:: sched_setparam(pid, param)
+
+ Set a scheduling parameters for the process with PID *pid*. A *pid* of 0 means
+ the calling process. *param* is a :class:`sched_param` instance.
+
+
+.. function:: sched_getparam(pid)
+
+ Return the scheduling parameters as a :class:`sched_param` instance for the
+ process with PID *pid*. A *pid* of 0 means the calling process.
+
+
+.. function:: sched_rr_get_interval(pid)
+
+ Return the round-robin quantum in seconds for the process with PID *pid*. A
+ *pid* of 0 means the calling process.
+
+
+.. function:: sched_yield()
+
+ Voluntarily relinquish the CPU.
+
+
+.. class:: cpu_set(ncpus)
+
+ :class:`cpu_set` represents a set of CPUs on which a process is eligible to
+ run. *ncpus* is the number of CPUs the set should describe. Methods on
+ :class:`cpu_set` allow CPUs to be add or removed.
+
+ :class:`cpu_set` supports the AND, OR, and XOR bitwise operations. For
+ example, given two cpu_sets, ``one`` and ``two``, ``one | two`` returns a
+ :class:`cpu_set` containing the cpus enabled both in ``one`` and ``two``.
+
+ .. method:: set(i)
+
+ Enable CPU *i*.
+
+ .. method:: clear(i)
+
+ Remove CPU *i*.
+
+ .. method:: isset(i)
+
+ Return ``True`` if CPU *i* is enabled in the set.
+
+ .. method:: count()
+
+ Return the number of enabled CPUs in the set.
+
+ .. method:: zero()
+
+ Clear the set completely.
+
+
+.. function:: sched_setaffinity(pid, mask)
+
+ Restrict the process with PID *pid* to a set of CPUs. *mask* is a
+ :class:`cpu_set` instance.
+
+
+.. function:: sched_getaffinity(pid, size)
+
+ Return the :class:`cpu_set` the process with PID *pid* is restricted to. The
+ result will contain *size* CPUs.
+
+
.. _os-path:
Miscellaneous System Information
diff --git a/Doc/library/ossaudiodev.rst b/Doc/library/ossaudiodev.rst
index 0a08428..51c5857 100644
--- a/Doc/library/ossaudiodev.rst
+++ b/Doc/library/ossaudiodev.rst
@@ -38,6 +38,10 @@ the standard audio interface for Linux and recent versions of FreeBSD.
This probably all warrants a footnote or two, but I don't understand
things well enough right now to write it! --GPW
+.. versionchanged:: 3.3
+ Operations in this module now raise :exc:`OSError` where :exc:`IOError`
+ was raised.
+
.. seealso::
@@ -56,7 +60,7 @@ the standard audio interface for Linux and recent versions of FreeBSD.
what went wrong.
(If :mod:`ossaudiodev` receives an error from a system call such as
- :c:func:`open`, :c:func:`write`, or :c:func:`ioctl`, it raises :exc:`IOError`.
+ :c:func:`open`, :c:func:`write`, or :c:func:`ioctl`, it raises :exc:`OSError`.
Errors detected directly by :mod:`ossaudiodev` result in :exc:`OSSAudioError`.)
(For backwards compatibility, the exception class is also available as
@@ -168,7 +172,7 @@ The following methods each map to exactly one :func:`ioctl` system call. The
correspondence is obvious: for example, :meth:`setfmt` corresponds to the
``SNDCTL_DSP_SETFMT`` ioctl, and :meth:`sync` to ``SNDCTL_DSP_SYNC`` (this can
be useful when consulting the OSS documentation). If the underlying
-:func:`ioctl` fails, they all raise :exc:`IOError`.
+:func:`ioctl` fails, they all raise :exc:`OSError`.
.. method:: oss_audio_device.nonblock()
@@ -344,7 +348,7 @@ The mixer object provides two file-like methods:
.. method:: oss_mixer_device.close()
This method closes the open mixer device file. Any further attempts to use the
- mixer after this file is closed will raise an :exc:`IOError`.
+ mixer after this file is closed will raise an :exc:`OSError`.
.. method:: oss_mixer_device.fileno()
@@ -403,7 +407,7 @@ The remaining methods are specific to audio mixing:
returned, but both volumes are the same.
Raises :exc:`OSSAudioError` if an invalid control was is specified, or
- :exc:`IOError` if an unsupported control is specified.
+ :exc:`OSError` if an unsupported control is specified.
.. method:: oss_mixer_device.set(control, (left, right))
@@ -427,7 +431,7 @@ The remaining methods are specific to audio mixing:
.. method:: oss_mixer_device.set_recsrc(bitmask)
Call this function to specify a recording source. Returns a bitmask indicating
- the new recording source (or sources) if successful; raises :exc:`IOError` if an
+ the new recording source (or sources) if successful; raises :exc:`OSError` if an
invalid source was specified. To set the current recording source to the
microphone input::
diff --git a/Doc/library/packaging-misc.rst b/Doc/library/packaging-misc.rst
new file mode 100644
index 0000000..5e56247
--- /dev/null
+++ b/Doc/library/packaging-misc.rst
@@ -0,0 +1,27 @@
+.. temporary file for modules that don't need a dedicated file yet
+
+:mod:`packaging.errors` --- Packaging exceptions
+================================================
+
+.. module:: packaging.errors
+ :synopsis: Packaging exceptions.
+
+
+Provides exceptions used by the Packaging modules. Note that Packaging modules
+may raise standard exceptions; in particular, SystemExit is usually raised for
+errors that are obviously the end-user's fault (e.g. bad command-line arguments).
+
+This module is safe to use in ``from ... import *`` mode; it only exports
+symbols whose names start with ``Packaging`` and end with ``Error``.
+
+
+:mod:`packaging.manifest` --- The Manifest class
+================================================
+
+.. module:: packaging.manifest
+ :synopsis: The Manifest class, used for poking about the file system and
+ building lists of files.
+
+
+This module provides the :class:`Manifest` class, used for poking about the
+filesystem and building lists of files.
diff --git a/Doc/library/packaging.command.rst b/Doc/library/packaging.command.rst
new file mode 100644
index 0000000..6a85351
--- /dev/null
+++ b/Doc/library/packaging.command.rst
@@ -0,0 +1,111 @@
+:mod:`packaging.command` --- Standard Packaging commands
+========================================================
+
+.. module:: packaging.command
+ :synopsis: Standard packaging commands.
+
+
+This subpackage contains one module for each standard Packaging command, such as
+:command:`build` or :command:`upload`. Each command is implemented as a
+separate module, with the command name as the name of the module and of the
+class defined therein.
+
+
+
+:mod:`packaging.command.cmd` --- Abstract base class for Packaging commands
+===========================================================================
+
+.. module:: packaging.command.cmd
+ :synopsis: Abstract base class for commands.
+
+
+This module supplies the abstract base class :class:`Command`. This class is
+subclassed by the modules in the packaging.command subpackage.
+
+
+.. class:: Command(dist)
+
+ Abstract base class for defining command classes, the "worker bees" of the
+ Packaging. A useful analogy for command classes is to think of them as
+ subroutines with local variables called *options*. The options are declared
+ in :meth:`initialize_options` and defined (given their final values) in
+ :meth:`finalize_options`, both of which must be defined by every command
+ class. The distinction between the two is necessary because option values
+ might come from the outside world (command line, config file, ...), and any
+ options dependent on other options must be computed after these outside
+ influences have been processed --- hence :meth:`finalize_options`. The body
+ of the subroutine, where it does all its work based on the values of its
+ options, is the :meth:`run` method, which must also be implemented by every
+ command class.
+
+ The class constructor takes a single argument *dist*, a
+ :class:`~packaging.dist.Distribution` instance.
+
+
+Creating a new Packaging command
+--------------------------------
+
+This section outlines the steps to create a new Packaging command.
+
+.. XXX the following paragraph is focused on the stdlib; expand it to document
+ how to write and register a command in third-party projects
+
+A new command lives in a module in the :mod:`packaging.command` package. There
+is a sample template in that directory called :file:`command_template`. Copy
+this file to a new module with the same name as the new command you're
+implementing. This module should implement a class with the same name as the
+module (and the command). So, for instance, to create the command
+``peel_banana`` (so that users can run ``setup.py peel_banana``), you'd copy
+:file:`command_template` to :file:`packaging/command/peel_banana.py`, then edit
+it so that it's implementing the class :class:`peel_banana`, a subclass of
+:class:`Command`. It must define the following methods:
+
+.. method:: Command.initialize_options()
+
+ Set default values for all the options that this command supports. Note that
+ these defaults may be overridden by other commands, by the setup script, by
+ config files, or by the command line. Thus, this is not the place to code
+ dependencies between options; generally, :meth:`initialize_options`
+ implementations are just a bunch of ``self.foo = None`` assignments.
+
+
+.. method:: Command.finalize_options()
+
+ Set final values for all the options that this command supports. This is
+ always called as late as possible, i.e. after any option assignments from the
+ command line or from other commands have been done. Thus, this is the place
+ to code option dependencies: if *foo* depends on *bar*, then it is safe to
+ set *foo* from *bar* as long as *foo* still has the same value it was
+ assigned in :meth:`initialize_options`.
+
+
+.. method:: Command.run()
+
+ A command's raison d'etre: carry out the action it exists to perform,
+ controlled by the options initialized in :meth:`initialize_options`,
+ customized by other commands, the setup script, the command line, and config
+ files, and finalized in :meth:`finalize_options`. All terminal output and
+ filesystem interaction should be done by :meth:`run`.
+
+
+Command classes may define this attribute:
+
+
+.. attribute:: Command.sub_commands
+
+ *sub_commands* formalizes the notion of a "family" of commands,
+ e.g. ``install_dist`` as the parent with sub-commands ``install_lib``,
+ ``install_headers``, etc. The parent of a family of commands defines
+ *sub_commands* as a class attribute; it's a list of 2-tuples ``(command_name,
+ predicate)``, with *command_name* a string and *predicate* a function, a
+ string or ``None``. *predicate* is a method of the parent command that
+ determines whether the corresponding command is applicable in the current
+ situation. (E.g. ``install_headers`` is only applicable if we have any C
+ header files to install.) If *predicate* is ``None``, that command is always
+ applicable.
+
+ *sub_commands* is usually defined at the *end* of a class, because
+ predicates can be methods of the class, so they must already have been
+ defined. The canonical example is the :command:`install_dist` command.
+
+.. XXX document how to add a custom command to another one's subcommands
diff --git a/Doc/library/packaging.compiler.rst b/Doc/library/packaging.compiler.rst
new file mode 100644
index 0000000..f23c551
--- /dev/null
+++ b/Doc/library/packaging.compiler.rst
@@ -0,0 +1,681 @@
+:mod:`packaging.compiler` --- Compiler classes
+==============================================
+
+.. module:: packaging.compiler
+ :synopsis: Compiler classes to build C/C++ extensions or libraries.
+
+
+This subpackage contains an abstract base class representing a compiler and
+concrete implementations for common compilers. The compiler classes should not
+be instantiated directly, but created using the :func:`new_compiler` factory
+function. Compiler types provided by Packaging are listed in
+:ref:`packaging-standard-compilers`.
+
+
+Public functions
+----------------
+
+.. function:: new_compiler(plat=None, compiler=None, dry_run=False, force=False)
+
+ Factory function to generate an instance of some
+ :class:`~.ccompiler.CCompiler` subclass for the requested platform or
+ compiler type.
+
+ If no argument is given for *plat* and *compiler*, the default compiler type
+ for the platform (:attr:`os.name`) will be used: ``'unix'`` for Unix and
+ Mac OS X, ``'msvc'`` for Windows.
+
+ If *plat* is given, it must be one of ``'posix'``, ``'darwin'`` or ``'nt'``.
+ An invalid value will not raise an exception but use the default compiler
+ type for the current platform.
+
+ .. XXX errors should never pass silently; this behavior is particularly
+ harmful when a compiler type is given as first argument
+
+ If *compiler* is given, *plat* will be ignored, allowing you to get for
+ example a ``'unix'`` compiler object under Windows or an ``'msvc'`` compiler
+ under Unix. However, not all compiler types can be instantiated on every
+ platform.
+
+
+.. function:: customize_compiler(compiler)
+
+ Do any platform-specific customization of a CCompiler instance. Mainly
+ needed on Unix to plug in the information that varies across Unices and is
+ stored in CPython's Makefile.
+
+
+.. function:: gen_lib_options(compiler, library_dirs, runtime_library_dirs, libraries)
+
+ Generate linker options for searching library directories and linking with
+ specific libraries. *libraries* and *library_dirs* are, respectively, lists
+ of library names (not filenames!) and search directories. Returns a list of
+ command-line options suitable for use with some compiler (depending on the
+ two format strings passed in).
+
+
+.. function:: gen_preprocess_options(macros, include_dirs)
+
+ Generate C preprocessor options (:option:`-D`, :option:`-U`, :option:`-I`) as
+ used by at least two types of compilers: the typical Unix compiler and Visual
+ C++. *macros* is the usual thing, a list of 1- or 2-tuples, where ``(name,)``
+ means undefine (:option:`-U`) macro *name*, and ``(name, value)`` means
+ define (:option:`-D`) macro *name* to *value*. *include_dirs* is just a list
+ of directory names to be added to the header file search path (:option:`-I`).
+ Returns a list of command-line options suitable for either Unix compilers or
+ Visual C++.
+
+
+.. function:: get_default_compiler(osname, platform)
+
+ Determine the default compiler to use for the given platform.
+
+ *osname* should be one of the standard Python OS names (i.e. the ones
+ returned by ``os.name``) and *platform* the common value returned by
+ ``sys.platform`` for the platform in question.
+
+ The default values are ``os.name`` and ``sys.platform``.
+
+
+.. function:: set_compiler(location)
+
+ Add or change a compiler
+
+
+.. function:: show_compilers()
+
+ Print list of available compilers (used by the :option:`--help-compiler`
+ options to :command:`build`, :command:`build_ext`, :command:`build_clib`).
+
+
+.. _packaging-standard-compilers:
+
+Standard compilers
+------------------
+
+Concrete subclasses of :class:`~.ccompiler.CCompiler` are provided in submodules
+of the :mod:`packaging.compiler` package. You do not need to import them, using
+:func:`new_compiler` is the public API to use. This table documents the
+standard compilers; be aware that they can be replaced by other classes on your
+platform.
+
+=============== ======================================================== =======
+name description notes
+=============== ======================================================== =======
+``'unix'`` typical Unix-style command-line C compiler [#]_
+``'msvc'`` Microsoft compiler [#]_
+``'bcpp'`` Borland C++ compiler
+``'cygwin'`` Cygwin compiler (Windows port of GCC)
+``'mingw32'`` Mingw32 port of GCC (same as Cygwin in no-Cygwin mode)
+=============== ======================================================== =======
+
+
+.. [#] The Unix compiler class assumes this behavior:
+
+ * macros defined with :option:`-Dname[=value]`
+
+ * macros undefined with :option:`-Uname`
+
+ * include search directories specified with :option:`-Idir`
+
+ * libraries specified with :option:`-llib`
+
+ * library search directories specified with :option:`-Ldir`
+
+ * compile handled by :program:`cc` (or similar) executable with
+ :option:`-c` option: compiles :file:`.c` to :file:`.o`
+
+ * link static library handled by :program:`ar` command (possibly with
+ :program:`ranlib`)
+
+ * link shared library handled by :program:`cc` :option:`-shared`
+
+
+.. [#] On Windows, extension modules typically need to be compiled with the same
+ compiler that was used to compile CPython (for example Microsoft Visual
+ Studio .NET 2003 for CPython 2.4 and 2.5). The AMD64 and Itanium
+ binaries are created using the Platform SDK.
+
+ Under the hood, there are actually two different subclasses of
+ :class:`~.ccompiler.CCompiler` defined: one is compatible with MSVC 2005
+ and 2008, the other works with older versions. This should not be a
+ concern for regular use of the functions in this module.
+
+ Packaging will normally choose the right compiler, linker etc. on its
+ own. To override this choice, the environment variables
+ *DISTUTILS_USE_SDK* and *MSSdk* must be both set. *MSSdk* indicates that
+ the current environment has been setup by the SDK's ``SetEnv.Cmd``
+ script, or that the environment variables had been registered when the
+ SDK was installed; *DISTUTILS_USE_SDK* indicates that the user has made
+ an explicit choice to override the compiler selection done by Packaging.
+
+ .. TODO document the envvars in Doc/using and the man page
+
+
+:mod:`packaging.compiler.ccompiler` --- CCompiler base class
+============================================================
+
+.. module:: packaging.compiler.ccompiler
+ :synopsis: Abstract CCompiler class.
+
+
+This module provides the abstract base class for the :class:`CCompiler`
+classes. A :class:`CCompiler` instance can be used for all the compile and
+link steps needed to build a single project. Methods are provided to set
+options for the compiler --- macro definitions, include directories, link path,
+libraries and the like.
+
+.. class:: CCompiler(dry_run=False, force=False)
+
+ The abstract base class :class:`CCompiler` defines the interface that must be
+ implemented by real compiler classes. The class also has some utility
+ methods used by several compiler classes.
+
+ The basic idea behind a compiler abstraction class is that each instance can
+ be used for all the compile/link steps in building a single project. Thus,
+ attributes common to all of those compile and link steps --- include
+ directories, macros to define, libraries to link against, etc. --- are
+ attributes of the compiler instance. To allow for variability in how
+ individual files are treated, most of those attributes may be varied on a
+ per-compilation or per-link basis.
+
+ The constructor for each subclass creates an instance of the Compiler object.
+ Flags are *dry_run* (don't actually execute
+ the steps) and *force* (rebuild everything, regardless of dependencies). All
+ of these flags default to ``False`` (off). Note that you probably don't want to
+ instantiate :class:`CCompiler` or one of its subclasses directly - use the
+ :func:`new_compiler` factory function instead.
+
+ The following methods allow you to manually alter compiler options for the
+ instance of the Compiler class.
+
+
+ .. method:: CCompiler.add_include_dir(dir)
+
+ Add *dir* to the list of directories that will be searched for header
+ files. The compiler is instructed to search directories in the order in
+ which they are supplied by successive calls to :meth:`add_include_dir`.
+
+
+ .. method:: CCompiler.set_include_dirs(dirs)
+
+ Set the list of directories that will be searched to *dirs* (a list of
+ strings). Overrides any preceding calls to :meth:`add_include_dir`;
+ subsequent calls to :meth:`add_include_dir` add to the list passed to
+ :meth:`set_include_dirs`. This does not affect any list of standard
+ include directories that the compiler may search by default.
+
+
+ .. method:: CCompiler.add_library(libname)
+
+ Add *libname* to the list of libraries that will be included in all links
+ driven by this compiler object. Note that *libname* should *not* be the
+ name of a file containing a library, but the name of the library itself:
+ the actual filename will be inferred by the linker, the compiler, or the
+ compiler class (depending on the platform).
+
+ The linker will be instructed to link against libraries in the order they
+ were supplied to :meth:`add_library` and/or :meth:`set_libraries`. It is
+ perfectly valid to duplicate library names; the linker will be instructed
+ to link against libraries as many times as they are mentioned.
+
+
+ .. method:: CCompiler.set_libraries(libnames)
+
+ Set the list of libraries to be included in all links driven by this
+ compiler object to *libnames* (a list of strings). This does not affect
+ any standard system libraries that the linker may include by default.
+
+
+ .. method:: CCompiler.add_library_dir(dir)
+
+ Add *dir* to the list of directories that will be searched for libraries
+ specified to :meth:`add_library` and :meth:`set_libraries`. The linker
+ will be instructed to search for libraries in the order they are supplied
+ to :meth:`add_library_dir` and/or :meth:`set_library_dirs`.
+
+
+ .. method:: CCompiler.set_library_dirs(dirs)
+
+ Set the list of library search directories to *dirs* (a list of strings).
+ This does not affect any standard library search path that the linker may
+ search by default.
+
+
+ .. method:: CCompiler.add_runtime_library_dir(dir)
+
+ Add *dir* to the list of directories that will be searched for shared
+ libraries at runtime.
+
+
+ .. method:: CCompiler.set_runtime_library_dirs(dirs)
+
+ Set the list of directories to search for shared libraries at runtime to
+ *dirs* (a list of strings). This does not affect any standard search path
+ that the runtime linker may search by default.
+
+
+ .. method:: CCompiler.define_macro(name[, value=None])
+
+ Define a preprocessor macro for all compilations driven by this compiler
+ object. The optional parameter *value* should be a string; if it is not
+ supplied, then the macro will be defined without an explicit value and the
+ exact outcome depends on the compiler used (XXX true? does ANSI say
+ anything about this?)
+
+
+ .. method:: CCompiler.undefine_macro(name)
+
+ Undefine a preprocessor macro for all compilations driven by this compiler
+ object. If the same macro is defined by :meth:`define_macro` and
+ undefined by :meth:`undefine_macro` the last call takes precedence
+ (including multiple redefinitions or undefinitions). If the macro is
+ redefined/undefined on a per-compilation basis (i.e. in the call to
+ :meth:`compile`), then that takes precedence.
+
+
+ .. method:: CCompiler.add_link_object(object)
+
+ Add *object* to the list of object files (or analogues, such as explicitly
+ named library files or the output of "resource compilers") to be included
+ in every link driven by this compiler object.
+
+
+ .. method:: CCompiler.set_link_objects(objects)
+
+ Set the list of object files (or analogues) to be included in every link
+ to *objects*. This does not affect any standard object files that the
+ linker may include by default (such as system libraries).
+
+ The following methods implement methods for autodetection of compiler
+ options, providing some functionality similar to GNU :program:`autoconf`.
+
+
+ .. method:: CCompiler.detect_language(sources)
+
+ Detect the language of a given file, or list of files. Uses the instance
+ attributes :attr:`language_map` (a dictionary), and :attr:`language_order`
+ (a list) to do the job.
+
+
+ .. method:: CCompiler.find_library_file(dirs, lib[, debug=0])
+
+ Search the specified list of directories for a static or shared library file
+ *lib* and return the full path to that file. If *debug* is true, look for a
+ debugging version (if that makes sense on the current platform). Return
+ ``None`` if *lib* wasn't found in any of the specified directories.
+
+
+ .. method:: CCompiler.has_function(funcname [, includes=None, include_dirs=None, libraries=None, library_dirs=None])
+
+ Return a boolean indicating whether *funcname* is supported on the current
+ platform. The optional arguments can be used to augment the compilation
+ environment by providing additional include files and paths and libraries and
+ paths.
+
+
+ .. method:: CCompiler.library_dir_option(dir)
+
+ Return the compiler option to add *dir* to the list of directories searched for
+ libraries.
+
+
+ .. method:: CCompiler.library_option(lib)
+
+ Return the compiler option to add *dir* to the list of libraries linked into the
+ shared library or executable.
+
+
+ .. method:: CCompiler.runtime_library_dir_option(dir)
+
+ Return the compiler option to add *dir* to the list of directories searched for
+ runtime libraries.
+
+
+ .. method:: CCompiler.set_executables(**args)
+
+ Define the executables (and options for them) that will be run to perform the
+ various stages of compilation. The exact set of executables that may be
+ specified here depends on the compiler class (via the 'executables' class
+ attribute), but most will have:
+
+ +--------------+------------------------------------------+
+ | attribute | description |
+ +==============+==========================================+
+ | *compiler* | the C/C++ compiler |
+ +--------------+------------------------------------------+
+ | *linker_so* | linker used to create shared objects and |
+ | | libraries |
+ +--------------+------------------------------------------+
+ | *linker_exe* | linker used to create binary executables |
+ +--------------+------------------------------------------+
+ | *archiver* | static library creator |
+ +--------------+------------------------------------------+
+
+ On platforms with a command line (Unix, DOS/Windows), each of these is a string
+ that will be split into executable name and (optional) list of arguments.
+ (Splitting the string is done similarly to how Unix shells operate: words are
+ delimited by spaces, but quotes and backslashes can override this. See
+ :func:`packaging.util.split_quoted`.)
+
+ The following methods invoke stages in the build process.
+
+
+ .. method:: CCompiler.compile(sources[, output_dir=None, macros=None, include_dirs=None, debug=0, extra_preargs=None, extra_postargs=None, depends=None])
+
+ Compile one or more source files. Generates object files (e.g. transforms a
+ :file:`.c` file to a :file:`.o` file.)
+
+ *sources* must be a list of filenames, most likely C/C++ files, but in reality
+ anything that can be handled by a particular compiler and compiler class (e.g.
+ an ``'msvc'`` compiler can handle resource files in *sources*). Return a list of
+ object filenames, one per source filename in *sources*. Depending on the
+ implementation, not all source files will necessarily be compiled, but all
+ corresponding object filenames will be returned.
+
+ If *output_dir* is given, object files will be put under it, while retaining
+ their original path component. That is, :file:`foo/bar.c` normally compiles to
+ :file:`foo/bar.o` (for a Unix implementation); if *output_dir* is *build*, then
+ it would compile to :file:`build/foo/bar.o`.
+
+ *macros*, if given, must be a list of macro definitions. A macro definition is
+ either a ``(name, value)`` 2-tuple or a ``(name,)`` 1-tuple. The former defines
+ a macro; if the value is ``None``, the macro is defined without an explicit
+ value. The 1-tuple case undefines a macro. Later
+ definitions/redefinitions/undefinitions take precedence.
+
+ *include_dirs*, if given, must be a list of strings, the directories to add to
+ the default include file search path for this compilation only.
+
+ *debug* is a boolean; if true, the compiler will be instructed to output debug
+ symbols in (or alongside) the object file(s).
+
+ *extra_preargs* and *extra_postargs* are implementation-dependent. On platforms
+ that have the notion of a command line (e.g. Unix, DOS/Windows), they are most
+ likely lists of strings: extra command-line arguments to prepend/append to the
+ compiler command line. On other platforms, consult the implementation class
+ documentation. In any event, they are intended as an escape hatch for those
+ occasions when the abstract compiler framework doesn't cut the mustard.
+
+ *depends*, if given, is a list of filenames that all targets depend on. If a
+ source file is older than any file in depends, then the source file will be
+ recompiled. This supports dependency tracking, but only at a coarse
+ granularity.
+
+ Raises :exc:`CompileError` on failure.
+
+
+ .. method:: CCompiler.create_static_lib(objects, output_libname[, output_dir=None, debug=0, target_lang=None])
+
+ Link a bunch of stuff together to create a static library file. The "bunch of
+ stuff" consists of the list of object files supplied as *objects*, the extra
+ object files supplied to :meth:`add_link_object` and/or
+ :meth:`set_link_objects`, the libraries supplied to :meth:`add_library` and/or
+ :meth:`set_libraries`, and the libraries supplied as *libraries* (if any).
+
+ *output_libname* should be a library name, not a filename; the filename will be
+ inferred from the library name. *output_dir* is the directory where the library
+ file will be put. XXX defaults to what?
+
+ *debug* is a boolean; if true, debugging information will be included in the
+ library (note that on most platforms, it is the compile step where this matters:
+ the *debug* flag is included here just for consistency).
+
+ *target_lang* is the target language for which the given objects are being
+ compiled. This allows specific linkage time treatment of certain languages.
+
+ Raises :exc:`LibError` on failure.
+
+
+ .. method:: CCompiler.link(target_desc, objects, output_filename[, output_dir=None, libraries=None, library_dirs=None, runtime_library_dirs=None, export_symbols=None, debug=0, extra_preargs=None, extra_postargs=None, build_temp=None, target_lang=None])
+
+ Link a bunch of stuff together to create an executable or shared library file.
+
+ The "bunch of stuff" consists of the list of object files supplied as *objects*.
+ *output_filename* should be a filename. If *output_dir* is supplied,
+ *output_filename* is relative to it (i.e. *output_filename* can provide
+ directory components if needed).
+
+ *libraries* is a list of libraries to link against. These are library names,
+ not filenames, since they're translated into filenames in a platform-specific
+ way (e.g. *foo* becomes :file:`libfoo.a` on Unix and :file:`foo.lib` on
+ DOS/Windows). However, they can include a directory component, which means the
+ linker will look in that specific directory rather than searching all the normal
+ locations.
+
+ *library_dirs*, if supplied, should be a list of directories to search for
+ libraries that were specified as bare library names (i.e. no directory
+ component). These are on top of the system default and those supplied to
+ :meth:`add_library_dir` and/or :meth:`set_library_dirs`. *runtime_library_dirs*
+ is a list of directories that will be embedded into the shared library and used
+ to search for other shared libraries that \*it\* depends on at run-time. (This
+ may only be relevant on Unix.)
+
+ *export_symbols* is a list of symbols that the shared library will export.
+ (This appears to be relevant only on Windows.)
+
+ *debug* is as for :meth:`compile` and :meth:`create_static_lib`, with the
+ slight distinction that it actually matters on most platforms (as opposed to
+ :meth:`create_static_lib`, which includes a *debug* flag mostly for form's
+ sake).
+
+ *extra_preargs* and *extra_postargs* are as for :meth:`compile` (except of
+ course that they supply command-line arguments for the particular linker being
+ used).
+
+ *target_lang* is the target language for which the given objects are being
+ compiled. This allows specific linkage time treatment of certain languages.
+
+ Raises :exc:`LinkError` on failure.
+
+
+ .. method:: CCompiler.link_executable(objects, output_progname[, output_dir=None, libraries=None, library_dirs=None, runtime_library_dirs=None, debug=0, extra_preargs=None, extra_postargs=None, target_lang=None])
+
+ Link an executable. *output_progname* is the name of the file executable, while
+ *objects* are a list of object filenames to link in. Other arguments are as for
+ the :meth:`link` method.
+
+
+ .. method:: CCompiler.link_shared_lib(objects, output_libname[, output_dir=None, libraries=None, library_dirs=None, runtime_library_dirs=None, export_symbols=None, debug=0, extra_preargs=None, extra_postargs=None, build_temp=None, target_lang=None])
+
+ Link a shared library. *output_libname* is the name of the output library,
+ while *objects* is a list of object filenames to link in. Other arguments are
+ as for the :meth:`link` method.
+
+
+ .. method:: CCompiler.link_shared_object(objects, output_filename[, output_dir=None, libraries=None, library_dirs=None, runtime_library_dirs=None, export_symbols=None, debug=0, extra_preargs=None, extra_postargs=None, build_temp=None, target_lang=None])
+
+ Link a shared object. *output_filename* is the name of the shared object that
+ will be created, while *objects* is a list of object filenames to link in.
+ Other arguments are as for the :meth:`link` method.
+
+
+ .. method:: CCompiler.preprocess(source[, output_file=None, macros=None, include_dirs=None, extra_preargs=None, extra_postargs=None])
+
+ Preprocess a single C/C++ source file, named in *source*. Output will be written
+ to file named *output_file*, or *stdout* if *output_file* not supplied.
+ *macros* is a list of macro definitions as for :meth:`compile`, which will
+ augment the macros set with :meth:`define_macro` and :meth:`undefine_macro`.
+ *include_dirs* is a list of directory names that will be added to the default
+ list, in the same way as :meth:`add_include_dir`.
+
+ Raises :exc:`PreprocessError` on failure.
+
+ The following utility methods are defined by the :class:`CCompiler` class, for
+ use by the various concrete subclasses.
+
+
+ .. method:: CCompiler.executable_filename(basename[, strip_dir=0, output_dir=''])
+
+ Returns the filename of the executable for the given *basename*. Typically for
+ non-Windows platforms this is the same as the basename, while Windows will get
+ a :file:`.exe` added.
+
+
+ .. method:: CCompiler.library_filename(libname[, lib_type='static', strip_dir=0, output_dir=''])
+
+ Returns the filename for the given library name on the current platform. On Unix
+ a library with *lib_type* of ``'static'`` will typically be of the form
+ :file:`liblibname.a`, while a *lib_type* of ``'dynamic'`` will be of the form
+ :file:`liblibname.so`.
+
+
+ .. method:: CCompiler.object_filenames(source_filenames[, strip_dir=0, output_dir=''])
+
+ Returns the name of the object files for the given source files.
+ *source_filenames* should be a list of filenames.
+
+
+ .. method:: CCompiler.shared_object_filename(basename[, strip_dir=0, output_dir=''])
+
+ Returns the name of a shared object file for the given file name *basename*.
+
+
+ .. method:: CCompiler.execute(func, args[, msg=None, level=1])
+
+ Invokes :func:`packaging.util.execute` This method invokes a Python function
+ *func* with the given arguments *args*, after logging and taking into account
+ the *dry_run* flag. XXX see also.
+
+
+ .. method:: CCompiler.spawn(cmd)
+
+ Invokes :func:`packaging.util.spawn`. This invokes an external process to run
+ the given command. XXX see also.
+
+
+ .. method:: CCompiler.mkpath(name[, mode=511])
+
+ Invokes :func:`packaging.dir_util.mkpath`. This creates a directory and any
+ missing ancestor directories. XXX see also.
+
+
+ .. method:: CCompiler.move_file(src, dst)
+
+ Invokes :meth:`packaging.file_util.move_file`. Renames *src* to *dst*. XXX see
+ also.
+
+
+:mod:`packaging.compiler.extension` --- The Extension class
+===========================================================
+
+.. module:: packaging.compiler.extension
+ :synopsis: Class used to represent C/C++ extension modules.
+
+
+This module provides the :class:`Extension` class, used to represent C/C++
+extension modules.
+
+.. class:: Extension
+
+ The Extension class describes a single C or C++ extension module. It accepts
+ the following keyword arguments in its constructor:
+
+ +------------------------+--------------------------------+---------------------------+
+ | argument name | value | type |
+ +========================+================================+===========================+
+ | *name* | the full name of the | string |
+ | | extension, including any | |
+ | | packages --- i.e. *not* a | |
+ | | filename or pathname, but | |
+ | | Python dotted name | |
+ +------------------------+--------------------------------+---------------------------+
+ | *sources* | list of source filenames, | list of strings |
+ | | relative to the distribution | |
+ | | root (where the setup script | |
+ | | lives), in Unix form (slash- | |
+ | | separated) for portability. | |
+ | | Source files may be C, C++, | |
+ | | SWIG (.i), platform-specific | |
+ | | resource files, or whatever | |
+ | | else is recognized by the | |
+ | | :command:`build_ext` command | |
+ | | as source for a Python | |
+ | | extension. | |
+ +------------------------+--------------------------------+---------------------------+
+ | *include_dirs* | list of directories to search | list of strings |
+ | | for C/C++ header files (in | |
+ | | Unix form for portability) | |
+ +------------------------+--------------------------------+---------------------------+
+ | *define_macros* | list of macros to define; each | list of tuples |
+ | | macro is defined using a | |
+ | | 2-tuple ``(name, value)``, | |
+ | | where *value* is | |
+ | | either the string to define it | |
+ | | to or ``None`` to define it | |
+ | | without a particular value | |
+ | | (equivalent of ``#define FOO`` | |
+ | | in source or :option:`-DFOO` | |
+ | | on Unix C compiler command | |
+ | | line) | |
+ +------------------------+--------------------------------+---------------------------+
+ | *undef_macros* | list of macros to undefine | list of strings |
+ | | explicitly | |
+ +------------------------+--------------------------------+---------------------------+
+ | *library_dirs* | list of directories to search | list of strings |
+ | | for C/C++ libraries at link | |
+ | | time | |
+ +------------------------+--------------------------------+---------------------------+
+ | *libraries* | list of library names (not | list of strings |
+ | | filenames or paths) to link | |
+ | | against | |
+ +------------------------+--------------------------------+---------------------------+
+ | *runtime_library_dirs* | list of directories to search | list of strings |
+ | | for C/C++ libraries at run | |
+ | | time (for shared extensions, | |
+ | | this is when the extension is | |
+ | | loaded) | |
+ +------------------------+--------------------------------+---------------------------+
+ | *extra_objects* | list of extra files to link | list of strings |
+ | | with (e.g. object files not | |
+ | | implied by 'sources', static | |
+ | | library that must be | |
+ | | explicitly specified, binary | |
+ | | resource files, etc.) | |
+ +------------------------+--------------------------------+---------------------------+
+ | *extra_compile_args* | any extra platform- and | list of strings |
+ | | compiler-specific information | |
+ | | to use when compiling the | |
+ | | source files in 'sources'. For | |
+ | | platforms and compilers where | |
+ | | a command line makes sense, | |
+ | | this is typically a list of | |
+ | | command-line arguments, but | |
+ | | for other platforms it could | |
+ | | be anything. | |
+ +------------------------+--------------------------------+---------------------------+
+ | *extra_link_args* | any extra platform- and | list of strings |
+ | | compiler-specific information | |
+ | | to use when linking object | |
+ | | files together to create the | |
+ | | extension (or to create a new | |
+ | | static Python interpreter). | |
+ | | Similar interpretation as for | |
+ | | 'extra_compile_args'. | |
+ +------------------------+--------------------------------+---------------------------+
+ | *export_symbols* | list of symbols to be exported | list of strings |
+ | | from a shared extension. Not | |
+ | | used on all platforms, and not | |
+ | | generally necessary for Python | |
+ | | extensions, which typically | |
+ | | export exactly one symbol: | |
+ | | ``init`` + extension_name. | |
+ +------------------------+--------------------------------+---------------------------+
+ | *depends* | list of files that the | list of strings |
+ | | extension depends on | |
+ +------------------------+--------------------------------+---------------------------+
+ | *language* | extension language (i.e. | string |
+ | | ``'c'``, ``'c++'``, | |
+ | | ``'objc'``). Will be detected | |
+ | | from the source extensions if | |
+ | | not provided. | |
+ +------------------------+--------------------------------+---------------------------+
+ | *optional* | specifies that a build failure | boolean |
+ | | in the extension should not | |
+ | | abort the build process, but | |
+ | | simply skip the extension. | |
+ +------------------------+--------------------------------+---------------------------+
+
+To distribute extension modules that live in a package (e.g. ``package.ext``),
+you need to create a :file:`{package}/__init__.py` file to let Python recognize
+and import your module.
diff --git a/Doc/library/packaging.database.rst b/Doc/library/packaging.database.rst
new file mode 100644
index 0000000..aaa2cb9
--- /dev/null
+++ b/Doc/library/packaging.database.rst
@@ -0,0 +1,324 @@
+:mod:`packaging.database` --- Database of installed distributions
+=================================================================
+
+.. module:: packaging.database
+ :synopsis: Functions to query and manipulate installed distributions.
+
+
+This module provides an implementation of :PEP:`376`. It was originally
+intended to land in :mod:`pkgutil`, but with the inclusion of Packaging in the
+standard library, it was thought best to include it in a submodule of
+:mod:`packaging`, leaving :mod:`pkgutil` to deal with imports.
+
+Installed Python distributions are represented by instances of
+:class:`Distribution`, or :class:`EggInfoDistribution` for legacy egg formats.
+Most functions also provide an extra argument ``use_egg_info`` to take legacy
+distributions into account.
+
+
+Classes representing installed distributions
+--------------------------------------------
+
+.. class:: Distribution(path)
+
+ Class representing an installed distribution. It is different from
+ :class:`packaging.dist.Distribution` which holds the list of files, the
+ metadata and options during the run of a Packaging command.
+
+ Instantiate with the *path* to a ``.dist-info`` directory. Instances can be
+ compared and sorted. Other available methods are:
+
+ .. XXX describe how comparison works
+
+ .. method:: get_distinfo_file(path, binary=False)
+
+ Return a read-only file object for a file located at
+ :file:`{project}-{version}.dist-info/{path}`. *path* should be a
+ ``'/'``-separated path relative to the ``.dist-info`` directory or an
+ absolute path; if it is an absolute path and doesn't start with the path
+ to the :file:`.dist-info` directory, a :class:`PackagingError` is raised.
+
+ If *binary* is ``True``, the file is opened in binary mode.
+
+ .. method:: get_resource_path(relative_path)
+
+ .. TODO
+
+ .. method:: list_distinfo_files(local=False)
+
+ Return an iterator over all files located in the :file:`.dist-info`
+ directory. If *local* is ``True``, each returned path is transformed into
+ a local absolute path, otherwise the raw value found in the :file:`RECORD`
+ file is returned.
+
+ .. method:: list_installed_files(local=False)
+
+ Iterate over the files installed with the distribution and registered in
+ the :file:`RECORD` file and yield a tuple ``(path, md5, size)`` for each
+ line. If *local* is ``True``, the returned path is transformed into a
+ local absolute path, otherwise the raw value is returned.
+
+ A local absolute path is an absolute path in which occurrences of ``'/'``
+ have been replaced by :data:`os.sep`.
+
+ .. method:: uses(path)
+
+ Check whether *path* was installed by this distribution (i.e. if the path
+ is present in the :file:`RECORD` file). *path* can be a local absolute
+ path or a relative ``'/'``-separated path. Returns a boolean.
+
+ Available attributes:
+
+ .. attribute:: metadata
+
+ Instance of :class:`packaging.metadata.Metadata` filled with the contents
+ of the :file:`{project}-{version}.dist-info/METADATA` file.
+
+ .. attribute:: name
+
+ Shortcut for ``metadata['Name']``.
+
+ .. attribute:: version
+
+ Shortcut for ``metadata['Version']``.
+
+ .. attribute:: requested
+
+ Boolean indicating whether this distribution was requested by the user of
+ automatically installed as a dependency.
+
+
+.. class:: EggInfoDistribution(path)
+
+ Class representing a legacy distribution. It is compatible with distutils'
+ and setuptools' :file:`.egg-info` and :file:`.egg` files and directories.
+
+ .. FIXME should be named EggDistribution
+
+ Instantiate with the *path* to an egg file or directory. Instances can be
+ compared and sorted. Other available methods are:
+
+ .. method:: list_installed_files(local=False)
+
+ .. method:: uses(path)
+
+ Available attributes:
+
+ .. attribute:: metadata
+
+ Instance of :class:`packaging.metadata.Metadata` filled with the contents
+ of the :file:`{project-version}.egg-info/PKG-INFO` or
+ :file:`{project-version}.egg` file.
+
+ .. attribute:: name
+
+ Shortcut for ``metadata['Name']``.
+
+ .. attribute:: version
+
+ Shortcut for ``metadata['Version']``.
+
+
+Functions to work with the database
+-----------------------------------
+
+.. function:: get_distribution(name, use_egg_info=False, paths=None)
+
+ Return an instance of :class:`Distribution` or :class:`EggInfoDistribution`
+ for the first installed distribution matching *name*. Egg distributions are
+ considered only if *use_egg_info* is true; if both a dist-info and an egg
+ file are found, the dist-info prevails. The directories to be searched are
+ given in *paths*, which defaults to :data:`sys.path`. Return ``None`` if no
+ matching distribution is found.
+
+ .. FIXME param should be named use_egg
+
+
+.. function:: get_distributions(use_egg_info=False, paths=None)
+
+ Return an iterator of :class:`Distribution` instances for all installed
+ distributions found in *paths* (defaults to :data:`sys.path`). If
+ *use_egg_info* is true, also return instances of :class:`EggInfoDistribution`
+ for legacy distributions found.
+
+
+.. function:: get_file_users(path)
+
+ Return an iterator over all distributions using *path*, a local absolute path
+ or a relative ``'/'``-separated path.
+
+ .. XXX does this work with prefixes or full file path only?
+
+
+.. function:: obsoletes_distribution(name, version=None, use_egg_info=False)
+
+ Return an iterator over all distributions that declare they obsolete *name*.
+ *version* is an optional argument to match only specific releases (see
+ :mod:`packaging.version`). If *use_egg_info* is true, legacy egg
+ distributions will be considered as well.
+
+
+.. function:: provides_distribution(name, version=None, use_egg_info=False)
+
+ Return an iterator over all distributions that declare they provide *name*.
+ *version* is an optional argument to match only specific releases (see
+ :mod:`packaging.version`). If *use_egg_info* is true, legacy egg
+ distributions will be considered as well.
+
+
+Utility functions
+-----------------
+
+.. function:: distinfo_dirname(name, version)
+
+ Escape *name* and *version* into a filename-safe form and return the
+ directory name built from them, for example
+ :file:`{safename}-{safeversion}.dist-info.` In *name*, runs of
+ non-alphanumeric characters are replaced with one ``'_'``; in *version*,
+ spaces become dots, and runs of other non-alphanumeric characters (except
+ dots) a replaced by one ``'-'``.
+
+ .. XXX wth spaces in version numbers?
+
+For performance purposes, the list of distributions is being internally
+cached. Caching is enabled by default, but you can control it with these
+functions:
+
+.. function:: clear_cache()
+
+ Clear the cache.
+
+.. function:: disable_cache()
+
+ Disable the cache, without clearing it.
+
+.. function:: enable_cache()
+
+ Enable the internal cache, without clearing it.
+
+
+Examples
+--------
+
+Print all information about a distribution
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Given a path to a ``.dist-info`` distribution, we shall print out all
+information that can be obtained using functions provided in this module::
+
+ import sys
+ import packaging.database
+
+ path = input()
+ # first create the Distribution instance
+ try:
+ dist = packaging.database.Distribution(path)
+ except FileNotFoundError:
+ sys.exit('No such distribution')
+
+ print('Information about %r' % dist.name)
+ print()
+
+ print('Files')
+ print('=====')
+ for path, md5, size in dist.list_installed_files():
+ print('* Path: %s' % path)
+ print(' Hash %s, Size: %s bytes' % (md5, size))
+ print()
+
+ print('Metadata')
+ print('========')
+ for key, value in dist.metadata.items():
+ print('%20s: %s' % (key, value))
+ print()
+
+ print('Extra')
+ print('=====')
+ if dist.requested:
+ print('* It was installed by user request')
+ else:
+ print('* It was installed as a dependency')
+
+If we save the script above as ``print_info.py``, we can use it to extract
+information from a :file:`.dist-info` directory. By typing in the console:
+
+.. code-block:: sh
+
+ $ echo /tmp/choxie/choxie-2.0.0.9.dist-info | python3 print_info.py
+
+we get the following output:
+
+.. code-block:: none
+
+ Information about 'choxie'
+
+ Files
+ =====
+ * Path: ../tmp/distutils2/tests/fake_dists/choxie-2.0.0.9/truffles.py
+ Hash 5e052db6a478d06bad9ae033e6bc08af, Size: 111 bytes
+ * Path: ../tmp/distutils2/tests/fake_dists/choxie-2.0.0.9/choxie/chocolate.py
+ Hash ac56bf496d8d1d26f866235b95f31030, Size: 214 bytes
+ * Path: ../tmp/distutils2/tests/fake_dists/choxie-2.0.0.9/choxie/__init__.py
+ Hash 416aab08dfa846f473129e89a7625bbc, Size: 25 bytes
+ * Path: ../tmp/distutils2/tests/fake_dists/choxie-2.0.0.9.dist-info/INSTALLER
+ Hash d41d8cd98f00b204e9800998ecf8427e, Size: 0 bytes
+ * Path: ../tmp/distutils2/tests/fake_dists/choxie-2.0.0.9.dist-info/METADATA
+ Hash 696a209967fef3c8b8f5a7bb10386385, Size: 225 bytes
+ * Path: ../tmp/distutils2/tests/fake_dists/choxie-2.0.0.9.dist-info/REQUESTED
+ Hash d41d8cd98f00b204e9800998ecf8427e, Size: 0 bytes
+ * Path: ../tmp/distutils2/tests/fake_dists/choxie-2.0.0.9.dist-info/RECORD
+ Hash None, Size: None bytes
+
+ Metadata
+ ========
+ Metadata-Version: 1.2
+ Name: choxie
+ Version: 2.0.0.9
+ Platform: []
+ Supported-Platform: UNKNOWN
+ Summary: Chocolate with a kick!
+ Description: UNKNOWN
+ Keywords: []
+ Home-page: UNKNOWN
+ Author: UNKNOWN
+ Author-email: UNKNOWN
+ Maintainer: UNKNOWN
+ Maintainer-email: UNKNOWN
+ License: UNKNOWN
+ Classifier: []
+ Download-URL: UNKNOWN
+ Obsoletes-Dist: ['truffles (<=0.8,>=0.5)', 'truffles (<=0.9,>=0.6)']
+ Project-URL: []
+ Provides-Dist: ['truffles (1.0)']
+ Requires-Dist: ['towel-stuff (0.1)']
+ Requires-Python: UNKNOWN
+ Requires-External: []
+
+ Extra
+ =====
+ * It was installed as a dependency
+
+
+Find out obsoleted distributions
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Now, we take tackle a different problem, we are interested in finding out
+which distributions have been obsoleted. This can be easily done as follows::
+
+ import packaging.database
+
+ # iterate over all distributions in the system
+ for dist in packaging.database.get_distributions():
+ name, version = dist.name, dist.version
+ # find out which distributions obsolete this name/version combination
+ replacements = packaging.database.obsoletes_distribution(name, version)
+ if replacements:
+ print('%r %s is obsoleted by' % (name, version),
+ ', '.join(repr(r.name) for r in replacements))
+
+This is how the output might look like:
+
+.. code-block:: none
+
+ 'strawberry' 0.6 is obsoleted by 'choxie'
+ 'grammar' 1.0a4 is obsoleted by 'towel-stuff'
diff --git a/Doc/library/packaging.depgraph.rst b/Doc/library/packaging.depgraph.rst
new file mode 100644
index 0000000..c384788
--- /dev/null
+++ b/Doc/library/packaging.depgraph.rst
@@ -0,0 +1,199 @@
+:mod:`packaging.depgraph` --- Dependency graph builder
+======================================================
+
+.. module:: packaging.depgraph
+ :synopsis: Graph builder for dependencies between releases.
+
+
+This module provides the means to analyse the dependencies between various
+distributions and to create a graph representing these dependency relationships.
+In this document, "distribution" refers to an instance of
+:class:`packaging.database.Distribution` or
+:class:`packaging.database.EggInfoDistribution`.
+
+.. XXX terminology problem with dist vs. release: dists are installed, but deps
+ use releases
+
+.. XXX explain how to use it with dists not installed: Distribution can only be
+ instantiated with a path, but this module is useful for remote dist too
+
+.. XXX functions should accept and return iterators, not lists
+
+
+The :class:`DependencyGraph` class
+----------------------------------
+
+.. class:: DependencyGraph
+
+ Represent a dependency graph between releases. The nodes are distribution
+ instances; the edge model dependencies. An edge from ``a`` to ``b`` means
+ that ``a`` depends on ``b``.
+
+ .. method:: add_distribution(distribution)
+
+ Add *distribution* to the graph.
+
+ .. method:: add_edge(x, y, label=None)
+
+ Add an edge from distribution *x* to distribution *y* with the given
+ *label* (string).
+
+ .. method:: add_missing(distribution, requirement)
+
+ Add a missing *requirement* (string) for the given *distribution*.
+
+ .. method:: repr_node(dist, level=1)
+
+ Print a subgraph starting from *dist*. *level* gives the depth of the
+ subgraph.
+
+ Direct access to the graph nodes and edges is provided through these
+ attributes:
+
+ .. attribute:: adjacency_list
+
+ Dictionary mapping distributions to a list of ``(other, label)`` tuples
+ where ``other`` is a distribution and the edge is labeled with ``label``
+ (i.e. the version specifier, if such was provided).
+
+ .. attribute:: reverse_list
+
+ Dictionary mapping distributions to a list of predecessors. This allows
+ efficient traversal.
+
+ .. attribute:: missing
+
+ Dictionary mapping distributions to a list of requirements that were not
+ provided by any distribution.
+
+
+Auxiliary functions
+-------------------
+
+.. function:: dependent_dists(dists, dist)
+
+ Recursively generate a list of distributions from *dists* that are dependent
+ on *dist*.
+
+ .. XXX what does member mean here: "dist is a member of *dists* for which we
+ are interested"
+
+.. function:: generate_graph(dists)
+
+ Generate a :class:`DependencyGraph` from the given list of distributions.
+
+ .. XXX make this alternate constructor a DepGraph classmethod or rename;
+ 'generate' can suggest it creates a file or an image, use 'make'
+
+.. function:: graph_to_dot(graph, f, skip_disconnected=True)
+
+ Write a DOT output for the graph to the file-like object *f*.
+
+ If *skip_disconnected* is true, all distributions that are not dependent on
+ any other distribution are skipped.
+
+ .. XXX why is this not a DepGraph method?
+
+
+Example Usage
+-------------
+
+Depict all dependenciess in the system
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+First, we shall generate a graph of all the distributions on the system
+and then create an image out of it using the tools provided by
+`Graphviz <http://www.graphviz.org/>`_::
+
+ from packaging.database import get_distributions
+ from packaging.depgraph import generate_graph
+
+ dists = list(get_distributions())
+ graph = generate_graph(dists)
+
+It would be interesting to print out the missing requirements. This can be done
+as follows::
+
+ for dist, reqs in graph.missing.items():
+ if reqs:
+ reqs = ' ,'.join(repr(req) for req in reqs)
+ print('Missing dependencies for %r: %s' % (dist.name, reqs))
+
+Example output is:
+
+.. code-block:: none
+
+ Missing dependencies for 'TurboCheetah': 'Cheetah'
+ Missing dependencies for 'TurboGears': 'ConfigObj', 'DecoratorTools', 'RuleDispatch'
+ Missing dependencies for 'jockey': 'PyKDE4.kdecore', 'PyKDE4.kdeui', 'PyQt4.QtCore', 'PyQt4.QtGui'
+ Missing dependencies for 'TurboKid': 'kid'
+ Missing dependencies for 'TurboJson: 'DecoratorTools', 'RuleDispatch'
+
+Now, we proceed with generating a graphical representation of the graph. First
+we write it to a file, and then we generate a PNG image using the
+:program:`dot` command-line tool::
+
+ from packaging.depgraph import graph_to_dot
+ with open('output.dot', 'w') as f:
+ # only show the interesting distributions, skipping the disconnected ones
+ graph_to_dot(graph, f, skip_disconnected=True)
+
+We can create the final picture using:
+
+.. code-block:: sh
+
+ $ dot -Tpng output.dot > output.png
+
+An example result is:
+
+.. figure:: depgraph-output.png
+ :alt: Example PNG output from packaging.depgraph and dot
+
+If you want to include egg distributions as well, then the code requires only
+one change, namely the line::
+
+ dists = list(packaging.database.get_distributions())
+
+has to be replaced with::
+
+ dists = list(packaging.database.get_distributions(use_egg_info=True))
+
+On many platforms, a richer graph is obtained because at the moment most
+distributions are provided in the egg rather than the new standard
+``.dist-info`` format.
+
+.. XXX missing image
+
+ An example of a more involved graph for illustrative reasons can be seen
+ here:
+
+ .. image:: depgraph_big.png
+
+
+List all dependent distributions
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+We will list all distributions that are dependent on some given distibution.
+This time, egg distributions will be considered as well::
+
+ import sys
+ from packaging.database import get_distribution, get_distributions
+ from packaging.depgraph import dependent_dists
+
+ dists = list(get_distributions(use_egg_info=True))
+ dist = get_distribution('bacon', use_egg_info=True)
+ if dist is None:
+ sys.exit('No such distribution in the system')
+
+ deps = dependent_dists(dists, dist)
+ deps = ', '.join(repr(x.name) for x in deps)
+ print('Distributions depending on %r: %s' % (dist.name, deps))
+
+And this is example output:
+
+.. with the dependency relationships as in the previous section
+ (depgraph_big)
+
+.. code-block:: none
+
+ Distributions depending on 'bacon': 'towel-stuff', 'choxie', 'grammar'
diff --git a/Doc/library/packaging.dist.rst b/Doc/library/packaging.dist.rst
new file mode 100644
index 0000000..25cb62b
--- /dev/null
+++ b/Doc/library/packaging.dist.rst
@@ -0,0 +1,108 @@
+:mod:`packaging.dist` --- The Distribution class
+================================================
+
+.. module:: packaging.dist
+ :synopsis: Core Distribution class.
+
+
+This module provides the :class:`Distribution` class, which represents the
+module distribution being built/packaged/distributed/installed.
+
+.. class:: Distribution(arguments)
+
+ A :class:`Distribution` describes how to build, package, distribute and
+ install a Python project.
+
+ The arguments accepted by the constructor are laid out in the following
+ table. Some of them will end up in a metadata object, the rest will become
+ data attributes of the :class:`Distribution` instance.
+
+ .. TODO improve constructor to take a Metadata object + named params?
+ (i.e. Distribution(metadata, cmdclass, py_modules, etc)
+ .. TODO also remove obsolete(?) script_name, etc. parameters? see what
+ py2exe and other tools need
+
+ +--------------------+--------------------------------+-------------------------------------------------------------+
+ | argument name | value | type |
+ +====================+================================+=============================================================+
+ | *name* | The name of the project | a string |
+ +--------------------+--------------------------------+-------------------------------------------------------------+
+ | *version* | The version number of the | a string |
+ | | release; see | |
+ | | :mod:`packaging.version` | |
+ +--------------------+--------------------------------+-------------------------------------------------------------+
+ | *summary* | A single line describing the | a string |
+ | | project | |
+ +--------------------+--------------------------------+-------------------------------------------------------------+
+ | *description* | Longer description of the | a string |
+ | | project | |
+ +--------------------+--------------------------------+-------------------------------------------------------------+
+ | *author* | The name of the project author | a string |
+ +--------------------+--------------------------------+-------------------------------------------------------------+
+ | *author_email* | The email address of the | a string |
+ | | project author | |
+ +--------------------+--------------------------------+-------------------------------------------------------------+
+ | *maintainer* | The name of the current | a string |
+ | | maintainer, if different from | |
+ | | the author | |
+ +--------------------+--------------------------------+-------------------------------------------------------------+
+ | *maintainer_email* | The email address of the | a string |
+ | | current maintainer, if | |
+ | | different from the author | |
+ +--------------------+--------------------------------+-------------------------------------------------------------+
+ | *home_page* | A URL for the proejct | a string |
+ | | (homepage) | |
+ +--------------------+--------------------------------+-------------------------------------------------------------+
+ | *download_url* | A URL to download the project | a string |
+ +--------------------+--------------------------------+-------------------------------------------------------------+
+ | *packages* | A list of Python packages that | a list of strings |
+ | | packaging will manipulate | |
+ +--------------------+--------------------------------+-------------------------------------------------------------+
+ | *py_modules* | A list of Python modules that | a list of strings |
+ | | packaging will manipulate | |
+ +--------------------+--------------------------------+-------------------------------------------------------------+
+ | *scripts* | A list of standalone scripts | a list of strings |
+ | | to be built and installed | |
+ +--------------------+--------------------------------+-------------------------------------------------------------+
+ | *ext_modules* | A list of Python extensions to | a list of instances of |
+ | | be built | :class:`packaging.compiler.extension.Extension` |
+ +--------------------+--------------------------------+-------------------------------------------------------------+
+ | *classifiers* | A list of categories for the | a list of strings; valid classifiers are listed on `PyPi |
+ | | distribution | <http://pypi.python.org/pypi?:action=list_classifiers>`_. |
+ +--------------------+--------------------------------+-------------------------------------------------------------+
+ | *distclass* | the :class:`Distribution` | a subclass of |
+ | | class to use | :class:`packaging.dist.Distribution` |
+ +--------------------+--------------------------------+-------------------------------------------------------------+
+ | *script_name* | The name of the setup.py | a string |
+ | | script - defaults to | |
+ | | ``sys.argv[0]`` | |
+ +--------------------+--------------------------------+-------------------------------------------------------------+
+ | *script_args* | Arguments to supply to the | a list of strings |
+ | | setup script | |
+ +--------------------+--------------------------------+-------------------------------------------------------------+
+ | *options* | default options for the setup | a string |
+ | | script | |
+ +--------------------+--------------------------------+-------------------------------------------------------------+
+ | *license* | The license for the | a string |
+ | | distribution; should be used | |
+ | | when there is no suitable | |
+ | | License classifier, or to | |
+ | | refine a classifier | |
+ +--------------------+--------------------------------+-------------------------------------------------------------+
+ | *keywords* | Descriptive keywords; used by | a list of strings or a comma-separated string |
+ | | catalogs such as PyPI | |
+ +--------------------+--------------------------------+-------------------------------------------------------------+
+ | *platforms* | Platforms compatible with this | a list of strings or a comma-separated string |
+ | | distribution; should be used | |
+ | | when there is no suitable | |
+ | | Platform classifier | |
+ +--------------------+--------------------------------+-------------------------------------------------------------+
+ | *cmdclass* | A mapping of command names to | a dictionary |
+ | | :class:`Command` subclasses | |
+ +--------------------+--------------------------------+-------------------------------------------------------------+
+ | *data_files* | A list of data files to | a list |
+ | | install | |
+ +--------------------+--------------------------------+-------------------------------------------------------------+
+ | *package_dir* | A mapping of Python packages | a dictionary |
+ | | to directory names | |
+ +--------------------+--------------------------------+-------------------------------------------------------------+
diff --git a/Doc/library/packaging.fancy_getopt.rst b/Doc/library/packaging.fancy_getopt.rst
new file mode 100644
index 0000000..2c69341
--- /dev/null
+++ b/Doc/library/packaging.fancy_getopt.rst
@@ -0,0 +1,75 @@
+:mod:`packaging.fancy_getopt` --- Wrapper around the getopt module
+==================================================================
+
+.. module:: packaging.fancy_getopt
+ :synopsis: Additional getopt functionality.
+
+
+.. warning::
+ This module is deprecated and will be replaced with :mod:`optparse`.
+
+This module provides a wrapper around the standard :mod:`getopt` module that
+provides the following additional features:
+
+* short and long options are tied together
+
+* options have help strings, so :func:`fancy_getopt` could potentially create a
+ complete usage summary
+
+* options set attributes of a passed-in object
+
+* boolean options can have "negative aliases" --- e.g. if :option:`--quiet` is
+ the "negative alias" of :option:`--verbose`, then :option:`--quiet` on the
+ command line sets *verbose* to false.
+
+.. function:: fancy_getopt(options, negative_opt, object, args)
+
+ Wrapper function. *options* is a list of ``(long_option, short_option,
+ help_string)`` 3-tuples as described in the constructor for
+ :class:`FancyGetopt`. *negative_opt* should be a dictionary mapping option names
+ to option names, both the key and value should be in the *options* list.
+ *object* is an object which will be used to store values (see the :meth:`getopt`
+ method of the :class:`FancyGetopt` class). *args* is the argument list. Will use
+ ``sys.argv[1:]`` if you pass ``None`` as *args*.
+
+
+.. class:: FancyGetopt([option_table=None])
+
+ The option_table is a list of 3-tuples: ``(long_option, short_option,
+ help_string)``
+
+ If an option takes an argument, its *long_option* should have ``'='`` appended;
+ *short_option* should just be a single character, no ``':'`` in any case.
+ *short_option* should be ``None`` if a *long_option* doesn't have a
+ corresponding *short_option*. All option tuples must have long options.
+
+The :class:`FancyGetopt` class provides the following methods:
+
+
+.. method:: FancyGetopt.getopt([args=None, object=None])
+
+ Parse command-line options in args. Store as attributes on *object*.
+
+ If *args* is ``None`` or not supplied, uses ``sys.argv[1:]``. If *object* is
+ ``None`` or not supplied, creates a new :class:`OptionDummy` instance, stores
+ option values there, and returns a tuple ``(args, object)``. If *object* is
+ supplied, it is modified in place and :func:`getopt` just returns *args*; in
+ both cases, the returned *args* is a modified copy of the passed-in *args* list,
+ which is left untouched.
+
+ .. TODO and args returned are?
+
+
+.. method:: FancyGetopt.get_option_order()
+
+ Returns the list of ``(option, value)`` tuples processed by the previous run of
+ :meth:`getopt` Raises :exc:`RuntimeError` if :meth:`getopt` hasn't been called
+ yet.
+
+
+.. method:: FancyGetopt.generate_help([header=None])
+
+ Generate help text (a list of strings, one per suggested line of output) from
+ the option table for this :class:`FancyGetopt` object.
+
+ If supplied, prints the supplied *header* at the top of the help.
diff --git a/Doc/library/packaging.install.rst b/Doc/library/packaging.install.rst
new file mode 100644
index 0000000..3e00750
--- /dev/null
+++ b/Doc/library/packaging.install.rst
@@ -0,0 +1,112 @@
+:mod:`packaging.install` --- Installation tools
+===============================================
+
+.. module:: packaging.install
+ :synopsis: Download and installation building blocks
+
+
+Packaging provides a set of tools to deal with downloads and installation of
+distributions. Their role is to download the distribution from indexes, resolve
+the dependencies, and provide a safe way to install distributions. An operation
+that fails will cleanly roll back, not leave half-installed distributions on the
+system. Here's the basic process followed:
+
+#. Move all distributions that will be removed to a temporary location.
+
+#. Install all the distributions that will be installed in a temporary location.
+
+#. If the installation fails, move the saved distributions back to their
+ location and delete the installed distributions.
+
+#. Otherwise, move the installed distributions to the right location and delete
+ the temporary locations.
+
+This is a higher-level module built on :mod:`packaging.database` and
+:mod:`packaging.pypi`.
+
+
+Public functions
+----------------
+
+.. function:: get_infos(requirements, index=None, installed=None, \
+ prefer_final=True)
+
+ Return information about what's going to be installed and upgraded.
+ *requirements* is a string containing the requirements for this
+ project, for example ``'FooBar 1.1'`` or ``'BarBaz (<1.2)'``.
+
+ .. XXX are requirements comma-separated?
+
+ If you want to use another index than the main PyPI, give its URI as *index*
+ argument.
+
+ *installed* is a list of already installed distributions used to find
+ satisfied dependencies, obsoleted distributions and eventual conflicts.
+
+ By default, alpha, beta and candidate versions are not picked up. Set
+ *prefer_final* to false to accept them too.
+
+ The results are returned in a dictionary containing all the information
+ needed to perform installation of the requirements with the
+ :func:`install_from_infos` function:
+
+ >>> get_install_info("FooBar (<=1.2)")
+ {'install': [<FooBar 1.1>], 'remove': [], 'conflict': []}
+
+ .. TODO should return tuple or named tuple, not dict
+ .. TODO use "predicate" or "requirement" consistently in version and here
+ .. FIXME "info" cannot be plural in English, s/infos/info/
+
+
+.. function:: install(project)
+
+
+.. function:: install_dists(dists, path, paths=None)
+
+ Safely install all distributions provided in *dists* into *path*. *paths* is
+ a list of paths where already-installed distributions will be looked for to
+ find satisfied dependencies and conflicts (default: :data:`sys.path`).
+ Returns a list of installed dists.
+
+ .. FIXME dists are instances of what?
+
+
+.. function:: install_from_infos(install_path=None, install=[], remove=[], \
+ conflicts=[], paths=None)
+
+ Safely install and remove given distributions. This function is designed to
+ work with the return value of :func:`get_infos`: *install*, *remove* and
+ *conflicts* should be list of distributions returned by :func:`get_infos`.
+ If *install* is not empty, *install_path* must be given to specify the path
+ where the distributions should be installed. *paths* is a list of paths
+ where already-installed distributions will be looked for (default:
+ :data:`sys.path`).
+
+ This function is a very basic installer; if *conflicts* is not empty, the
+ system will be in a conflicting state after the function completes. It is a
+ building block for more sophisticated installers with conflict resolution
+ systems.
+
+ .. TODO document typical value for install_path
+ .. TODO document integration with default schemes, esp. user site-packages
+
+
+.. function:: install_local_project(path)
+
+ Install a distribution from a source directory, which must contain either a
+ Packaging-compliant :file:`setup.cfg` file or a legacy Distutils
+ :file:`setup.py` script (in which case Distutils will be used under the hood
+ to perform the installation).
+
+
+.. function:: remove(project_name, paths=None, auto_confirm=True)
+
+ Remove one distribution from the system.
+
+ .. FIXME this is the only function using "project" instead of dist/release
+
+..
+ Example usage
+ --------------
+
+ Get the scheme of what's gonna be installed if we install "foobar":
diff --git a/Doc/library/packaging.metadata.rst b/Doc/library/packaging.metadata.rst
new file mode 100644
index 0000000..332d69d
--- /dev/null
+++ b/Doc/library/packaging.metadata.rst
@@ -0,0 +1,122 @@
+:mod:`packaging.metadata` --- Metadata handling
+===============================================
+
+.. module:: packaging.metadata
+ :synopsis: Class holding the metadata of a release.
+
+
+.. TODO use sphinx-autogen to generate basic doc from the docstrings
+
+.. class:: Metadata
+
+ This class can read and write metadata files complying with any of the
+ defined versions: 1.0 (:PEP:`241`), 1.1 (:PEP:`314`) and 1.2 (:PEP:`345`). It
+ implements methods to parse Metadata files and write them, and a mapping
+ interface to its contents.
+
+ The :PEP:`345` implementation supports the micro-language for the environment
+ markers, and displays warnings when versions that are supposed to be
+ :PEP:`386`-compliant are violating the specification.
+
+
+Reading metadata
+----------------
+
+The :class:`Metadata` class can be instantiated
+with the path of the metadata file, and provides a dict-like interface to the
+values::
+
+ >>> from packaging.metadata import Metadata
+ >>> metadata = Metadata('PKG-INFO')
+ >>> metadata.keys()[:5]
+ ('Metadata-Version', 'Name', 'Version', 'Platform', 'Supported-Platform')
+ >>> metadata['Name']
+ 'CLVault'
+ >>> metadata['Version']
+ '0.5'
+ >>> metadata['Requires-Dist']
+ ["pywin32; sys.platform == 'win32'", "Sphinx"]
+
+
+The fields that support environment markers can be automatically ignored if
+the object is instantiated using the ``platform_dependent`` option.
+:class:`Metadata` will interpret in this case
+the markers and will automatically remove the fields that are not compliant
+with the running environment. Here's an example under Mac OS X. The win32
+dependency we saw earlier is ignored::
+
+ >>> from packaging.metadata import Metadata
+ >>> metadata = Metadata('PKG-INFO', platform_dependent=True)
+ >>> metadata['Requires-Dist']
+ ['Sphinx']
+
+
+If you want to provide your own execution context, let's say to test the
+metadata under a particular environment that is not the current environment,
+you can provide your own values in the ``execution_context`` option, which
+is the dict that may contain one or more keys of the context the micro-language
+expects.
+
+Here's an example, simulating a win32 environment::
+
+ >>> from packaging.metadata import Metadata
+ >>> context = {'sys.platform': 'win32'}
+ >>> metadata = Metadata('PKG-INFO', platform_dependent=True,
+ ... execution_context=context)
+ ...
+ >>> metadata['Requires-Dist'] = ["pywin32; sys.platform == 'win32'",
+ ... "Sphinx"]
+ ...
+ >>> metadata['Requires-Dist']
+ ['pywin32', 'Sphinx']
+
+
+Writing metadata
+----------------
+
+Writing metadata can be done using the ``write`` method::
+
+ >>> metadata.write('/to/my/PKG-INFO')
+
+The class will pick the best version for the metadata, depending on the values
+provided. If all the values provided exist in all versions, the class will
+use :attr:`PKG_INFO_PREFERRED_VERSION`. It is set by default to 1.0, the most
+widespread version.
+
+
+Conflict checking and best version
+----------------------------------
+
+Some fields in :PEP:`345` have to comply with the version number specification
+defined in :PEP:`386`. When they don't comply, a warning is emitted::
+
+ >>> from packaging.metadata import Metadata
+ >>> metadata = Metadata()
+ >>> metadata['Requires-Dist'] = ['Funky (Groovie)']
+ "Funky (Groovie)" is not a valid predicate
+ >>> metadata['Requires-Dist'] = ['Funky (1.2)']
+
+See also :mod:`packaging.version`.
+
+
+.. TODO talk about check()
+
+
+:mod:`packaging.markers` --- Environment markers
+================================================
+
+.. module:: packaging.markers
+ :synopsis: Micro-language for environment markers
+
+
+This is an implementation of environment markers `as defined in PEP 345
+<http://www.python.org/dev/peps/pep-0345/#environment-markers>`_. It is used
+for some metadata fields.
+
+.. function:: interpret(marker, execution_context=None)
+
+ Interpret a marker and return a boolean result depending on the environment.
+ Example:
+
+ >>> interpret("python_version > '1.0'")
+ True
diff --git a/Doc/library/packaging.pypi.dist.rst b/Doc/library/packaging.pypi.dist.rst
new file mode 100644
index 0000000..aaaaab7
--- /dev/null
+++ b/Doc/library/packaging.pypi.dist.rst
@@ -0,0 +1,114 @@
+:mod:`packaging.pypi.dist` --- Classes representing query results
+=================================================================
+
+.. module:: packaging.pypi.dist
+ :synopsis: Classes representing the results of queries to indexes.
+
+
+Information coming from the indexes is held in instances of the classes defined
+in this module.
+
+Keep in mind that each project (eg. FooBar) can have several releases
+(eg. 1.1, 1.2, 1.3), and each of these releases can be provided in multiple
+distributions (eg. a source distribution, a binary one, etc).
+
+
+ReleaseInfo
+-----------
+
+Each release has a project name, version, metadata, and related distributions.
+
+This information is stored in :class:`ReleaseInfo`
+objects.
+
+.. class:: ReleaseInfo
+
+
+DistInfo
+---------
+
+:class:`DistInfo` is a simple class that contains
+information related to distributions; mainly the URLs where distributions
+can be found.
+
+.. class:: DistInfo
+
+
+ReleasesList
+------------
+
+The :mod:`~packaging.pypi.dist` module provides a class which works
+with lists of :class:`ReleaseInfo` classes;
+used to filter and order results.
+
+.. class:: ReleasesList
+
+
+Example usage
+-------------
+
+Build a list of releases and order them
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Assuming we have a list of releases::
+
+ >>> from packaging.pypi.dist import ReleasesList, ReleaseInfo
+ >>> fb10 = ReleaseInfo("FooBar", "1.0")
+ >>> fb11 = ReleaseInfo("FooBar", "1.1")
+ >>> fb11a = ReleaseInfo("FooBar", "1.1a1")
+ >>> ReleasesList("FooBar", [fb11, fb11a, fb10])
+ >>> releases.sort_releases()
+ >>> releases.get_versions()
+ ['1.1', '1.1a1', '1.0']
+ >>> releases.add_release("1.2a1")
+ >>> releases.get_versions()
+ ['1.1', '1.1a1', '1.0', '1.2a1']
+ >>> releases.sort_releases()
+ ['1.2a1', '1.1', '1.1a1', '1.0']
+ >>> releases.sort_releases(prefer_final=True)
+ >>> releases.get_versions()
+ ['1.1', '1.0', '1.2a1', '1.1a1']
+
+
+Add distribution related information to releases
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+It's easy to add distribution information to releases::
+
+ >>> from packaging.pypi.dist import ReleasesList, ReleaseInfo
+ >>> r = ReleaseInfo("FooBar", "1.0")
+ >>> r.add_distribution("sdist", url="http://example.org/foobar-1.0.tar.gz")
+ >>> r.dists
+ {'sdist': FooBar 1.0 sdist}
+ >>> r['sdist'].url
+ {'url': 'http://example.org/foobar-1.0.tar.gz', 'hashname': None, 'hashval':
+ None, 'is_external': True}
+
+
+Getting attributes from the dist objects
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+To abstract querying information returned from the indexes, attributes and
+release information can be retrieved directly from dist objects.
+
+For instance, if you have a release instance that does not contain the metadata
+attribute, it can be fetched by using the "fetch_metadata" method::
+
+ >>> r = Release("FooBar", "1.1")
+ >>> print r.metadata
+ None # metadata field is actually set to "None"
+ >>> r.fetch_metadata()
+ <Metadata for FooBar 1.1>
+
+.. XXX add proper roles to these constructs
+
+
+It's possible to retrieve a project's releases (`fetch_releases`),
+metadata (`fetch_metadata`) and distributions (`fetch_distributions`) using
+a similar work flow.
+
+.. XXX what is possible?
+
+Internally, this is possible because while retrieving information about
+projects, releases or distributions, a reference to the client used is
+stored which can be accessed using the objects `_index` attribute.
diff --git a/Doc/library/packaging.pypi.rst b/Doc/library/packaging.pypi.rst
new file mode 100644
index 0000000..14602ce
--- /dev/null
+++ b/Doc/library/packaging.pypi.rst
@@ -0,0 +1,74 @@
+:mod:`packaging.pypi` --- Interface to projects indexes
+=======================================================
+
+.. module:: packaging.pypi
+ :synopsis: Low-level and high-level APIs to query projects indexes.
+
+
+Packaging queries PyPI to get information about projects or download them. The
+low-level facilities used internally are also part of the public API designed to
+be used by other tools.
+
+The :mod:`packaging.pypi` package provides those facilities, which can be
+used to access information about Python projects registered at indexes, the
+main one being PyPI, located ad http://pypi.python.org/.
+
+There is two ways to retrieve data from these indexes: a screen-scraping
+interface called the "simple API", and XML-RPC. The first one uses HTML pages
+located under http://pypi.python.org/simple/, the second one makes XML-RPC
+requests to http://pypi.python.org/pypi/. All functions and classes also work
+with other indexes such as mirrors, which typically implement only the simple
+interface.
+
+Packaging provides a class that wraps both APIs to provide full query and
+download functionality: :class:`packaging.pypi.client.ClientWrapper`. If you
+want more control, you can use the underlying classes
+:class:`packaging.pypi.simple.Crawler` and :class:`packaging.pypi.xmlrpc.Client`
+to connect to one specific interface.
+
+
+:mod:`packaging.pypi.client` --- High-level query API
+=====================================================
+
+.. module:: packaging.pypi.client
+ :synopsis: Wrapper around :mod;`packaging.pypi.xmlrpc` and
+ :mod:`packaging.pypi.simple` to query indexes.
+
+
+This module provides a high-level API to query indexes and search
+for releases and distributions. The aim of this module is to choose the best
+way to query the API automatically, either using XML-RPC or the simple index,
+with a preference toward the latter.
+
+.. class:: ClientWrapper
+
+ Instances of this class will use the simple interface or XML-RPC requests to
+ query indexes and return :class:`packaging.pypi.dist.ReleaseInfo` and
+ :class:`packaging.pypi.dist.ReleasesList` objects.
+
+ .. method:: find_projects
+
+ .. method:: get_release
+
+ .. method:: get_releases
+
+
+:mod:`packaging.pypi.base` --- Base class for index crawlers
+============================================================
+
+.. module:: packaging.pypi.base
+ :synopsis: Base class used to implement crawlers.
+
+
+.. class:: BaseClient(prefer_final, prefer_source)
+
+ Base class containing common methods for the index crawlers or clients. One
+ method is currently defined:
+
+ .. method:: download_distribution(requirements, temp_path=None, \
+ prefer_source=None, prefer_final=None)
+
+ Download a distribution from the last release according to the
+ requirements. If *temp_path* is provided, download to this path,
+ otherwise, create a temporary directory for the download. If a release is
+ found, the full path to the downloaded file is returned.
diff --git a/Doc/library/packaging.pypi.simple.rst b/Doc/library/packaging.pypi.simple.rst
new file mode 100644
index 0000000..126c224
--- /dev/null
+++ b/Doc/library/packaging.pypi.simple.rst
@@ -0,0 +1,218 @@
+:mod:`packaging.pypi.simple` --- Crawler using the PyPI "simple" interface
+==========================================================================
+
+.. module:: packaging.pypi.simple
+ :synopsis: Crawler using the screen-scraping "simple" interface to fetch info
+ and distributions.
+
+
+The class provided by :mod:`packaging.pypi.simple` can access project indexes
+and provide useful information about distributions. PyPI, other indexes and
+local indexes are supported.
+
+You should use this module to search distributions by name and versions, process
+index external pages and download distributions. It is not suited for things
+that will end up in too long index processing (like "finding all distributions
+with a specific version, no matter the name"); use :mod:`packaging.pypi.xmlrpc`
+for that.
+
+
+API
+---
+
+.. class:: Crawler(index_url=DEFAULT_SIMPLE_INDEX_URL, \
+ prefer_final=False, prefer_source=True, \
+ hosts=('*',), follow_externals=False, \
+ mirrors_url=None, mirrors=None, timeout=15, \
+ mirrors_max_tries=0)
+
+ *index_url* is the address of the index to use for requests.
+
+ The first two parameters control the query results. *prefer_final*
+ indicates whether a final version (not alpha, beta or candidate) is to be
+ preferred over a newer but non-final version (for example, whether to pick
+ up 1.0 over 2.0a3). It is used only for queries that don't give a version
+ argument. Likewise, *prefer_source* tells whether to prefer a source
+ distribution over a binary one, if no distribution argument was prodived.
+
+ Other parameters are related to external links (that is links that go
+ outside the simple index): *hosts* is a list of hosts allowed to be
+ processed if *follow_externals* is true (default behavior is to follow all
+ hosts), *follow_externals* enables or disables following external links
+ (default is false, meaning disabled).
+
+ The remaining parameters are related to the mirroring infrastructure
+ defined in :PEP:`381`. *mirrors_url* gives a URL to look on for DNS
+ records giving mirror adresses; *mirrors* is a list of mirror URLs (see
+ the PEP). If both *mirrors* and *mirrors_url* are given, *mirrors_url*
+ will only be used if *mirrors* is set to ``None``. *timeout* is the time
+ (in seconds) to wait before considering a URL has timed out;
+ *mirrors_max_tries"* is the number of times to try requesting informations
+ on mirrors before switching.
+
+ The following methods are defined:
+
+ .. method:: get_distributions(project_name, version)
+
+ Return the distributions found in the index for the given release.
+
+ .. method:: get_metadata(project_name, version)
+
+ Return the metadata found on the index for this project name and
+ version. Currently downloads and unpacks a distribution to read the
+ PKG-INFO file.
+
+ .. method:: get_release(requirements, prefer_final=None)
+
+ Return one release that fulfills the given requirements.
+
+ .. method:: get_releases(requirements, prefer_final=None, force_update=False)
+
+ Search for releases and return a
+ :class:`~packaging.pypi.dist.ReleasesList` object containing the
+ results.
+
+ .. method:: search_projects(name=None)
+
+ Search the index for projects containing the given name and return a
+ list of matching names.
+
+ See also the base class :class:`packaging.pypi.base.BaseClient` for inherited
+ methods.
+
+
+.. data:: DEFAULT_SIMPLE_INDEX_URL
+
+ The address used by default by the crawler class. It is currently
+ ``'http://a.pypi.python.org/simple/'``, the main PyPI installation.
+
+
+
+
+Usage Exemples
+---------------
+
+To help you understand how using the `Crawler` class, here are some basic
+usages.
+
+Request the simple index to get a specific distribution
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Supposing you want to scan an index to get a list of distributions for
+the "foobar" project. You can use the "get_releases" method for that.
+The get_releases method will browse the project page, and return
+:class:`ReleaseInfo` objects for each found link that rely on downloads. ::
+
+ >>> from packaging.pypi.simple import Crawler
+ >>> crawler = Crawler()
+ >>> crawler.get_releases("FooBar")
+ [<ReleaseInfo "Foobar 1.1">, <ReleaseInfo "Foobar 1.2">]
+
+
+Note that you also can request the client about specific versions, using version
+specifiers (described in `PEP 345
+<http://www.python.org/dev/peps/pep-0345/#version-specifiers>`_)::
+
+ >>> client.get_releases("FooBar < 1.2")
+ [<ReleaseInfo "FooBar 1.1">, ]
+
+
+`get_releases` returns a list of :class:`ReleaseInfo`, but you also can get the
+best distribution that fullfil your requirements, using "get_release"::
+
+ >>> client.get_release("FooBar < 1.2")
+ <ReleaseInfo "FooBar 1.1">
+
+
+Download distributions
+^^^^^^^^^^^^^^^^^^^^^^
+
+As it can get the urls of distributions provided by PyPI, the `Crawler`
+client also can download the distributions and put it for you in a temporary
+destination::
+
+ >>> client.download("foobar")
+ /tmp/temp_dir/foobar-1.2.tar.gz
+
+
+You also can specify the directory you want to download to::
+
+ >>> client.download("foobar", "/path/to/my/dir")
+ /path/to/my/dir/foobar-1.2.tar.gz
+
+
+While downloading, the md5 of the archive will be checked, if not matches, it
+will try another time, then if fails again, raise `MD5HashDoesNotMatchError`.
+
+Internally, that's not the Crawler which download the distributions, but the
+`DistributionInfo` class. Please refer to this documentation for more details.
+
+
+Following PyPI external links
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The default behavior for packaging is to *not* follow the links provided
+by HTML pages in the "simple index", to find distributions related
+downloads.
+
+It's possible to tell the PyPIClient to follow external links by setting the
+`follow_externals` attribute, on instantiation or after::
+
+ >>> client = Crawler(follow_externals=True)
+
+or ::
+
+ >>> client = Crawler()
+ >>> client.follow_externals = True
+
+
+Working with external indexes, and mirrors
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The default `Crawler` behavior is to rely on the Python Package index stored
+on PyPI (http://pypi.python.org/simple).
+
+As you can need to work with a local index, or private indexes, you can specify
+it using the index_url parameter::
+
+ >>> client = Crawler(index_url="file://filesystem/path/")
+
+or ::
+
+ >>> client = Crawler(index_url="http://some.specific.url/")
+
+
+You also can specify mirrors to fallback on in case the first index_url you
+provided doesnt respond, or not correctly. The default behavior for
+`Crawler` is to use the list provided by Python.org DNS records, as
+described in the :PEP:`381` about mirroring infrastructure.
+
+If you don't want to rely on these, you could specify the list of mirrors you
+want to try by specifying the `mirrors` attribute. It's a simple iterable::
+
+ >>> mirrors = ["http://first.mirror","http://second.mirror"]
+ >>> client = Crawler(mirrors=mirrors)
+
+
+Searching in the simple index
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+It's possible to search for projects with specific names in the package index.
+Assuming you want to find all projects containing the "distutils" keyword::
+
+ >>> c.search_projects("distutils")
+ [<Project "collective.recipe.distutils">, <Project "Distutils">, <Project
+ "Packaging">, <Project "distutilscross">, <Project "lpdistutils">, <Project
+ "taras.recipe.distutils">, <Project "zerokspot.recipe.distutils">]
+
+
+You can also search the projects starting with a specific text, or ending with
+that text, using a wildcard::
+
+ >>> c.search_projects("distutils*")
+ [<Project "Distutils">, <Project "Packaging">, <Project "distutilscross">]
+
+ >>> c.search_projects("*distutils")
+ [<Project "collective.recipe.distutils">, <Project "Distutils">, <Project
+ "lpdistutils">, <Project "taras.recipe.distutils">, <Project
+ "zerokspot.recipe.distutils">]
diff --git a/Doc/library/packaging.pypi.xmlrpc.rst b/Doc/library/packaging.pypi.xmlrpc.rst
new file mode 100644
index 0000000..5242e4c
--- /dev/null
+++ b/Doc/library/packaging.pypi.xmlrpc.rst
@@ -0,0 +1,143 @@
+:mod:`packaging.pypi.xmlrpc` --- Crawler using the PyPI XML-RPC interface
+=========================================================================
+
+.. module:: packaging.pypi.xmlrpc
+ :synopsis: Client using XML-RPC requests to fetch info and distributions.
+
+
+Indexes can be queried using XML-RPC calls, and Packaging provides a simple
+way to interface with XML-RPC.
+
+You should **use** XML-RPC when:
+
+* Searching the index for projects **on other fields than project
+ names**. For instance, you can search for projects based on the
+ author_email field.
+* Searching all the versions that have existed for a project.
+* you want to retrieve METADATAs information from releases or
+ distributions.
+
+
+You should **avoid using** XML-RPC method calls when:
+
+* Retrieving the last version of a project
+* Getting the projects with a specific name and version.
+* The simple index can match your needs
+
+
+When dealing with indexes, keep in mind that the index queries will always
+return you :class:`packaging.pypi.dist.ReleaseInfo` and
+:class:`packaging.pypi.dist.ReleasesList` objects.
+
+Some methods here share common APIs with the one you can find on
+:class:`packaging.pypi.simple`, internally, :class:`packaging.pypi.client`
+is inherited by :class:`Client`
+
+
+API
+---
+
+.. class:: Client
+
+
+Usage examples
+--------------
+
+Use case described here are use case that are not common to the other clients.
+If you want to see all the methods, please refer to API or to usage examples
+described in :class:`packaging.pypi.client.Client`
+
+
+Finding releases
+^^^^^^^^^^^^^^^^
+
+It's a common use case to search for "things" within the index. We can
+basically search for projects by their name, which is the most used way for
+users (eg. "give me the last version of the FooBar project").
+
+This can be accomplished using the following syntax::
+
+ >>> client = xmlrpc.Client()
+ >>> client.get_release("Foobar (<= 1.3))
+ <FooBar 1.2.1>
+ >>> client.get_releases("FooBar (<= 1.3)")
+ [FooBar 1.1, FooBar 1.1.1, FooBar 1.2, FooBar 1.2.1]
+
+
+And we also can find for specific fields::
+
+ >>> client.search_projects(field=value)
+
+
+You could specify the operator to use, default is "or"::
+
+ >>> client.search_projects(field=value, operator="and")
+
+
+The specific fields you can search are:
+
+* name
+* version
+* author
+* author_email
+* maintainer
+* maintainer_email
+* home_page
+* license
+* summary
+* description
+* keywords
+* platform
+* download_url
+
+
+Getting metadata information
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+XML-RPC is a preferred way to retrieve metadata information from indexes.
+It's really simple to do so::
+
+ >>> client = xmlrpc.Client()
+ >>> client.get_metadata("FooBar", "1.1")
+ <ReleaseInfo FooBar 1.1>
+
+
+Assuming we already have a :class:`packaging.pypi.ReleaseInfo` object defined,
+it's possible to pass it to the xmlrpc client to retrieve and complete its
+metadata::
+
+ >>> foobar11 = ReleaseInfo("FooBar", "1.1")
+ >>> client = xmlrpc.Client()
+ >>> returned_release = client.get_metadata(release=foobar11)
+ >>> returned_release
+ <ReleaseInfo FooBar 1.1>
+
+
+Get all the releases of a project
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+To retrieve all the releases for a project, you can build them using
+`get_releases`::
+
+ >>> client = xmlrpc.Client()
+ >>> client.get_releases("FooBar")
+ [<ReleaseInfo FooBar 0.9>, <ReleaseInfo FooBar 1.0>, <ReleaseInfo 1.1>]
+
+
+Get information about distributions
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Indexes have information about projects, releases **and** distributions.
+If you're not familiar with those, please refer to the documentation of
+:mod:`packaging.pypi.dist`.
+
+It's possible to retrieve information about distributions, e.g "what are the
+existing distributions for this release ? How to retrieve them ?"::
+
+ >>> client = xmlrpc.Client()
+ >>> release = client.get_distributions("FooBar", "1.1")
+ >>> release.dists
+ {'sdist': <FooBar 1.1 sdist>, 'bdist': <FooBar 1.1 bdist>}
+
+As you see, this does not return a list of distributions, but a release,
+because a release can be used like a list of distributions.
diff --git a/Doc/library/packaging.rst b/Doc/library/packaging.rst
new file mode 100644
index 0000000..c6bff47
--- /dev/null
+++ b/Doc/library/packaging.rst
@@ -0,0 +1,75 @@
+:mod:`packaging` --- Packaging support
+======================================
+
+.. module:: packaging
+ :synopsis: Packaging system and building blocks for other packaging systems.
+.. sectionauthor:: Fred L. Drake, Jr. <fdrake@acm.org>, distutils and packaging
+ contributors
+
+
+The :mod:`packaging` package provides support for building, packaging,
+distributing and installing additional projects into a Python installation.
+Projects may include Python modules, extension modules, packages and scripts.
+:mod:`packaging` also provides building blocks for other packaging systems
+that are not tied to the command system.
+
+This manual is the reference documentation for those standalone building
+blocks and for extending Packaging. If you're looking for the user-centric
+guides to install a project or package your own code, head to `See also`__.
+
+
+Building blocks
+---------------
+
+.. toctree::
+ :maxdepth: 2
+
+ packaging-misc
+ packaging.version
+ packaging.metadata
+ packaging.database
+ packaging.depgraph
+ packaging.pypi
+ packaging.pypi.dist
+ packaging.pypi.simple
+ packaging.pypi.xmlrpc
+ packaging.install
+
+
+The command machinery
+---------------------
+
+.. toctree::
+ :maxdepth: 2
+
+ packaging.dist
+ packaging.command
+ packaging.compiler
+ packaging.fancy_getopt
+
+
+Other utilities
+----------------
+
+.. toctree::
+ :maxdepth: 2
+
+ packaging.util
+ packaging.tests.pypi_server
+
+.. XXX missing: compat config create (dir_util) run pypi.{base,mirrors}
+
+
+.. __:
+
+.. seealso::
+
+ :ref:`packaging-index`
+ The manual for developers of Python projects who want to package and
+ distribute them. This describes how to use :mod:`packaging` to make
+ projects easily found and added to an existing Python installation.
+
+ :ref:`packaging-install-index`
+ A user-centered manual which includes information on adding projects
+ into an existing Python installation. You do not need to be a Python
+ programmer to read this manual.
diff --git a/Doc/library/packaging.tests.pypi_server.rst b/Doc/library/packaging.tests.pypi_server.rst
new file mode 100644
index 0000000..f3b7720
--- /dev/null
+++ b/Doc/library/packaging.tests.pypi_server.rst
@@ -0,0 +1,105 @@
+:mod:`packaging.tests.pypi_server` --- PyPI mock server
+=======================================================
+
+.. module:: packaging.tests.pypi_server
+ :synopsis: Mock server used to test PyPI-related modules and commands.
+
+
+When you are testing code that works with Packaging, you might find these tools
+useful.
+
+
+The mock server
+---------------
+
+.. class:: PyPIServer
+
+ PyPIServer is a class that implements an HTTP server running in a separate
+ thread. All it does is record the requests for further inspection. The recorded
+ data is available under ``requests`` attribute. The default
+ HTTP response can be overridden with the ``default_response_status``,
+ ``default_response_headers`` and ``default_response_data`` attributes.
+
+ By default, when accessing the server with urls beginning with `/simple/`,
+ the server also record your requests, but will look for files under
+ the `/tests/pypiserver/simple/` path.
+
+ You can tell the sever to serve static files for other paths. This could be
+ accomplished by using the `static_uri_paths` parameter, as below::
+
+ server = PyPIServer(static_uri_paths=["first_path", "second_path"])
+
+
+ You need to create the content that will be served under the
+ `/tests/pypiserver/default` path. If you want to serve content from another
+ place, you also can specify another filesystem path (which needs to be under
+ `tests/pypiserver/`. This will replace the default behavior of the server, and
+ it will not serve content from the `default` dir ::
+
+ server = PyPIServer(static_filesystem_paths=["path/to/your/dir"])
+
+
+ If you just need to add some paths to the existing ones, you can do as shown,
+ keeping in mind that the server will always try to load paths in reverse order
+ (e.g here, try "another/super/path" then the default one) ::
+
+ server = PyPIServer(test_static_path="another/super/path")
+ server = PyPIServer("another/super/path")
+ # or
+ server.static_filesystem_paths.append("another/super/path")
+
+
+ As a result of what, in your tests, while you need to use the PyPIServer, in
+ order to isolates the test cases, the best practice is to place the common files
+ in the `default` folder, and to create a directory for each specific test case::
+
+ server = PyPIServer(static_filesystem_paths = ["default", "test_pypi_server"],
+ static_uri_paths=["simple", "external"])
+
+
+Base class and decorator for tests
+----------------------------------
+
+.. class:: PyPIServerTestCase
+
+ ``PyPIServerTestCase`` is a test case class with setUp and tearDown methods that
+ take care of a single PyPIServer instance attached as a ``pypi`` attribute on
+ the test class. Use it as one of the base classes in your test case::
+
+
+ class UploadTestCase(PyPIServerTestCase):
+
+ def test_something(self):
+ cmd = self.prepare_command()
+ cmd.ensure_finalized()
+ cmd.repository = self.pypi.full_address
+ cmd.run()
+
+ environ, request_data = self.pypi.requests[-1]
+ self.assertEqual(request_data, EXPECTED_REQUEST_DATA)
+
+
+.. decorator:: use_pypi_server
+
+ You also can use a decorator for your tests, if you do not need the same server
+ instance along all you test case. So, you can specify, for each test method,
+ some initialisation parameters for the server.
+
+ For this, you need to add a `server` parameter to your method, like this::
+
+ class SampleTestCase(TestCase):
+
+ @use_pypi_server()
+ def test_something(self, server):
+ ...
+
+
+ The decorator will instantiate the server for you, and run and stop it just
+ before and after your method call. You also can pass the server initializer,
+ just like this::
+
+ class SampleTestCase(TestCase):
+
+ @use_pypi_server("test_case_name")
+ def test_something(self, server):
+ ...
diff --git a/Doc/library/packaging.util.rst b/Doc/library/packaging.util.rst
new file mode 100644
index 0000000..e628c32
--- /dev/null
+++ b/Doc/library/packaging.util.rst
@@ -0,0 +1,155 @@
+:mod:`packaging.util` --- Miscellaneous utility functions
+=========================================================
+
+.. module:: packaging.util
+ :synopsis: Miscellaneous utility functions.
+
+
+This module contains various helpers for the other modules.
+
+.. XXX a number of functions are missing, but the module may be split first
+ (it's ginormous right now, some things could go to compat for example)
+
+.. function:: get_platform()
+
+ Return a string that identifies the current platform. This is used mainly to
+ distinguish platform-specific build directories and platform-specific built
+ distributions. Typically includes the OS name and version and the
+ architecture (as supplied by 'os.uname()'), although the exact information
+ included depends on the OS; e.g. for IRIX the architecture isn't particularly
+ important (IRIX only runs on SGI hardware), but for Linux the kernel version
+ isn't particularly important.
+
+ Examples of returned values:
+
+ * ``linux-i586``
+ * ``linux-alpha``
+ * ``solaris-2.6-sun4u``
+ * ``irix-5.3``
+ * ``irix64-6.2``
+
+ For non-POSIX platforms, currently just returns ``sys.platform``.
+
+ For Mac OS X systems the OS version reflects the minimal version on which
+ binaries will run (that is, the value of ``MACOSX_DEPLOYMENT_TARGET``
+ during the build of Python), not the OS version of the current system.
+
+ For universal binary builds on Mac OS X the architecture value reflects
+ the univeral binary status instead of the architecture of the current
+ processor. For 32-bit universal binaries the architecture is ``fat``,
+ for 64-bit universal binaries the architecture is ``fat64``, and
+ for 4-way universal binaries the architecture is ``universal``. Starting
+ from Python 2.7 and Python 3.2 the architecture ``fat3`` is used for
+ a 3-way universal build (ppc, i386, x86_64) and ``intel`` is used for
+ a univeral build with the i386 and x86_64 architectures
+
+ Examples of returned values on Mac OS X:
+
+ * ``macosx-10.3-ppc``
+
+ * ``macosx-10.3-fat``
+
+ * ``macosx-10.5-universal``
+
+ * ``macosx-10.6-intel``
+
+ .. XXX reinvention of platform module?
+
+
+.. function:: convert_path(pathname)
+
+ Return 'pathname' as a name that will work on the native filesystem, i.e.
+ split it on '/' and put it back together again using the current directory
+ separator. Needed because filenames in the setup script are always supplied
+ in Unix style, and have to be converted to the local convention before we
+ can actually use them in the filesystem. Raises :exc:`ValueError` on
+ non-Unix-ish systems if *pathname* either starts or ends with a slash.
+
+
+.. function:: change_root(new_root, pathname)
+
+ Return *pathname* with *new_root* prepended. If *pathname* is relative, this
+ is equivalent to ``os.path.join(new_root,pathname)`` Otherwise, it requires
+ making *pathname* relative and then joining the two, which is tricky on
+ DOS/Windows.
+
+
+.. function:: check_environ()
+
+ Ensure that 'os.environ' has all the environment variables we guarantee that
+ users can use in config files, command-line options, etc. Currently this
+ includes:
+
+ * :envvar:`HOME` - user's home directory (Unix only)
+ * :envvar:`PLAT` - description of the current platform, including hardware
+ and OS (see :func:`get_platform`)
+
+
+.. function:: find_executable(executable, path=None)
+
+ Search the path for a given executable name.
+
+
+.. function:: execute(func, args, msg=None, dry_run=False)
+
+ Perform some action that affects the outside world (for instance, writing to
+ the filesystem). Such actions are special because they are disabled by the
+ *dry_run* flag. This method takes care of all that bureaucracy for you;
+ all you have to do is supply the function to call and an argument tuple for
+ it (to embody the "external action" being performed), and an optional message
+ to print.
+
+
+.. function:: newer(source, target)
+
+ Return true if *source* exists and is more recently modified than *target*,
+ or if *source* exists and *target* doesn't. Return false if both exist and
+ *target* is the same age or newer than *source*. Raise
+ :exc:`PackagingFileError` if *source* does not exist.
+
+
+.. function:: strtobool(val)
+
+ Convert a string representation of truth to true (1) or false (0).
+
+ True values are ``y``, ``yes``, ``t``, ``true``, ``on`` and ``1``; false
+ values are ``n``, ``no``, ``f``, ``false``, ``off`` and ``0``. Raises
+ :exc:`ValueError` if *val* is anything else.
+
+
+.. function:: byte_compile(py_files, optimize=0, force=0, prefix=None, \
+ base_dir=None, dry_run=0, direct=None)
+
+ Byte-compile a collection of Python source files to either :file:`.pyc` or
+ :file:`.pyo` files in a :file:`__pycache__` subdirectory (see :pep:`3147`),
+ or to the same directory when using the distutils2 backport on Python
+ versions older than 3.2.
+
+ *py_files* is a list of files to compile; any files that don't end in
+ :file:`.py` are silently skipped. *optimize* must be one of the following:
+
+ * ``0`` - don't optimize (generate :file:`.pyc`)
+ * ``1`` - normal optimization (like ``python -O``)
+ * ``2`` - extra optimization (like ``python -OO``)
+
+ This function is independent from the running Python's :option:`-O` or
+ :option:`-B` options; it is fully controlled by the parameters passed in.
+
+ If *force* is true, all files are recompiled regardless of timestamps.
+
+ The source filename encoded in each :term:`bytecode` file defaults to the filenames
+ listed in *py_files*; you can modify these with *prefix* and *basedir*.
+ *prefix* is a string that will be stripped off of each source filename, and
+ *base_dir* is a directory name that will be prepended (after *prefix* is
+ stripped). You can supply either or both (or neither) of *prefix* and
+ *base_dir*, as you wish.
+
+ If *dry_run* is true, doesn't actually do anything that would affect the
+ filesystem.
+
+ Byte-compilation is either done directly in this interpreter process with the
+ standard :mod:`py_compile` module, or indirectly by writing a temporary
+ script and executing it. Normally, you should let :func:`byte_compile`
+ figure out to use direct compilation or not (see the source for details).
+ The *direct* flag is used by the script generated in indirect mode; unless
+ you know what you're doing, leave it set to ``None``.
diff --git a/Doc/library/packaging.version.rst b/Doc/library/packaging.version.rst
new file mode 100644
index 0000000..f36cdab
--- /dev/null
+++ b/Doc/library/packaging.version.rst
@@ -0,0 +1,104 @@
+:mod:`packaging.version` --- Version number classes
+===================================================
+
+.. module:: packaging.version
+ :synopsis: Classes that represent project version numbers.
+
+
+This module contains classes and functions useful to deal with version numbers.
+It's an implementation of version specifiers `as defined in PEP 345
+<http://www.python.org/dev/peps/pep-0345/#version-specifiers>`_.
+
+
+Version numbers
+---------------
+
+.. class:: NormalizedVersion(self, s, error_on_huge_major_num=True)
+
+ A specific version of a distribution, as described in PEP 345. *s* is a
+ string object containing the version number (for example ``'1.2b1'``),
+ *error_on_huge_major_num* a boolean specifying whether to consider an
+ apparent use of a year or full date as the major version number an error.
+
+ The rationale for the second argument is that there were projects using years
+ or full dates as version numbers, which could cause problems with some
+ packaging systems sorting.
+
+ Instances of this class can be compared and sorted::
+
+ >>> NormalizedVersion('1.2b1') < NormalizedVersion('1.2')
+ True
+
+ :class:`NormalizedVersion` is used internally by :class:`VersionPredicate` to
+ do its work.
+
+
+.. class:: IrrationalVersionError
+
+ Exception raised when an invalid string is given to
+ :class:`NormalizedVersion`.
+
+ >>> NormalizedVersion("irrational_version_number")
+ ...
+ IrrationalVersionError: irrational_version_number
+
+
+.. function:: suggest_normalized_version(s)
+
+ Before standardization in PEP 386, various schemes were in use. Packaging
+ provides a function to try to convert any string to a valid, normalized
+ version::
+
+ >>> suggest_normalized_version('2.1-rc1')
+ 2.1c1
+
+
+ If :func:`suggest_normalized_version` can't make sense of the given string,
+ it will return ``None``::
+
+ >>> print(suggest_normalized_version('not a version'))
+ None
+
+
+Version predicates
+------------------
+
+.. class:: VersionPredicate(predicate)
+
+ This class deals with the parsing of field values like
+ ``ProjectName (>=version)``.
+
+ .. method:: match(version)
+
+ Test if a version number matches the predicate:
+
+ >>> version = VersionPredicate("ProjectName (<1.2, >1.0)")
+ >>> version.match("1.2.1")
+ False
+ >>> version.match("1.1.1")
+ True
+
+
+Validation helpers
+------------------
+
+If you want to use :term:`LBYL`-style checks instead of instantiating the
+classes and catching :class:`IrrationalVersionError` and :class:`ValueError`,
+you can use these functions:
+
+.. function:: is_valid_version(predicate)
+
+ Check whether the given string is a valid version number. Example of valid
+ strings: ``'1.2'``, ``'4.2.0.dev4'``, ``'2.5.4.post2'``.
+
+
+.. function:: is_valid_versions(predicate)
+
+ Check whether the given string is a valid value for specifying multiple
+ versions, such as in the Requires-Python field. Example: ``'2.7, >=3.2'``.
+
+
+.. function:: is_valid_predicate(predicate)
+
+ Check whether the given string is a valid version predicate. Examples:
+ ``'some.project == 4.5, <= 4.7'``, ``'speciallib (> 1.0, != 1.4.2, < 2.0)'``.
diff --git a/Doc/library/platform.rst b/Doc/library/platform.rst
index 929936e..ce079cf 100644
--- a/Doc/library/platform.rst
+++ b/Doc/library/platform.rst
@@ -214,6 +214,10 @@ Win95/98 specific
preferring :func:`win32pipe.popen`. On Windows NT, :func:`win32pipe.popen`
should work; on Windows 9x it hangs due to bugs in the MS C library.
+ .. deprecated:: 3.3
+ This function is obsolete. Use the :mod:`subprocess` module. Check
+ especially the :ref:`subprocess-replacements` section.
+
Mac OS Platform
---------------
diff --git a/Doc/library/python.rst b/Doc/library/python.rst
index b67fbfc..07eadb4 100644
--- a/Doc/library/python.rst
+++ b/Doc/library/python.rst
@@ -25,4 +25,5 @@ overview:
inspect.rst
site.rst
fpectl.rst
+ packaging.rst
distutils.rst
diff --git a/Doc/library/random.rst b/Doc/library/random.rst
index 31cb945..2b10e6e 100644
--- a/Doc/library/random.rst
+++ b/Doc/library/random.rst
@@ -43,6 +43,12 @@ The :mod:`random` module also provides the :class:`SystemRandom` class which
uses the system function :func:`os.urandom` to generate random numbers
from sources provided by the operating system.
+.. warning::
+
+ The generators of the :mod:`random` module should not be used for security
+ purposes. Use :func:`ssl.RAND_bytes` if you require a cryptographically
+ secure pseudorandom number generator.
+
Bookkeeping functions:
diff --git a/Doc/library/re.rst b/Doc/library/re.rst
index 0c2c4fa..7232cde 100644
--- a/Doc/library/re.rst
+++ b/Doc/library/re.rst
@@ -694,9 +694,12 @@ form.
.. function:: escape(string)
- Return *string* with all non-alphanumerics backslashed; this is useful if you
- want to match an arbitrary literal string that may have regular expression
- metacharacters in it.
+ Escape all the characters in pattern except ASCII letters, numbers and ``'_'``.
+ This is useful if you want to match an arbitrary literal string that may
+ have regular expression metacharacters in it.
+
+ .. versionchanged:: 3.3
+ The ``'_'`` character is no longer escaped.
.. function:: purge()
diff --git a/Doc/library/readline.rst b/Doc/library/readline.rst
index ab55197..1134619 100644
--- a/Doc/library/readline.rst
+++ b/Doc/library/readline.rst
@@ -199,7 +199,7 @@ normally be executed automatically during interactive sessions from the user's
histfile = os.path.join(os.path.expanduser("~"), ".pyhist")
try:
readline.read_history_file(histfile)
- except IOError:
+ except FileNotFoundError:
pass
import atexit
atexit.register(readline.write_history_file, histfile)
@@ -224,7 +224,7 @@ support history save/restore. ::
if hasattr(readline, "read_history_file"):
try:
readline.read_history_file(histfile)
- except IOError:
+ except FileNotFoundError:
pass
atexit.register(self.save_history, histfile)
diff --git a/Doc/library/resource.rst b/Doc/library/resource.rst
index c16b013..03a7cb5 100644
--- a/Doc/library/resource.rst
+++ b/Doc/library/resource.rst
@@ -14,13 +14,15 @@ resources utilized by a program.
Symbolic constants are used to specify particular system resources and to
request usage information about either the current process or its children.
-A single exception is defined for errors:
+An :exc:`OSError` is raised on syscall failure.
.. exception:: error
- The functions described below may raise this error if the underlying system call
- failures unexpectedly.
+ A deprecated alias of :exc:`OSError`.
+
+ .. versionchanged:: 3.3
+ Following :pep:`3151`, this class was made an alias of :exc:`OSError`.
Resource Limits
diff --git a/Doc/library/sched.rst b/Doc/library/sched.rst
index 000dba0..957bdd3 100644
--- a/Doc/library/sched.rst
+++ b/Doc/library/sched.rst
@@ -14,7 +14,7 @@
The :mod:`sched` module defines a class which implements a general purpose event
scheduler:
-.. class:: scheduler(timefunc, delayfunc)
+.. class:: scheduler(timefunc=time.time, delayfunc=time.sleep)
The :class:`scheduler` class defines a generic interface to scheduling events.
It needs two functions to actually deal with the "outside world" --- *timefunc*
@@ -25,6 +25,12 @@ scheduler:
event is run to allow other threads an opportunity to run in multi-threaded
applications.
+ .. versionchanged:: 3.3
+ *timefunc* and *delayfunc* parameters are optional.
+ .. versionchanged:: 3.3
+ :class:`scheduler` class can be safely used in multi-threaded
+ environments.
+
Example::
>>> import sched, time
@@ -44,33 +50,6 @@ Example::
From print_time 930343700.273
930343700.276
-In multi-threaded environments, the :class:`scheduler` class has limitations
-with respect to thread-safety, inability to insert a new task before
-the one currently pending in a running scheduler, and holding up the main
-thread until the event queue is empty. Instead, the preferred approach
-is to use the :class:`threading.Timer` class instead.
-
-Example::
-
- >>> import time
- >>> from threading import Timer
- >>> def print_time():
- ... print("From print_time", time.time())
- ...
- >>> def print_some_times():
- ... print(time.time())
- ... Timer(5, print_time, ()).start()
- ... Timer(10, print_time, ()).start()
- ... time.sleep(11) # sleep while time-delay events execute
- ... print(time.time())
- ...
- >>> print_some_times()
- 930343690.257
- From print_time 930343695.274
- From print_time 930343700.273
- 930343701.301
-
-
.. _scheduler-objects:
Scheduler Objects
@@ -79,26 +58,38 @@ Scheduler Objects
:class:`scheduler` instances have the following methods and attributes:
-.. method:: scheduler.enterabs(time, priority, action, argument)
+.. method:: scheduler.enterabs(time, priority, action, argument=[], kwargs={})
Schedule a new event. The *time* argument should be a numeric type compatible
with the return value of the *timefunc* function passed to the constructor.
Events scheduled for the same *time* will be executed in the order of their
*priority*.
- Executing the event means executing ``action(*argument)``. *argument* must be a
- sequence holding the parameters for *action*.
+ Executing the event means executing ``action(*argument, **kwargs)``.
+ *argument* must be a sequence holding the parameters for *action*.
+ *kwargs* must be a dictionary holding the keyword parameters for *action*.
Return value is an event which may be used for later cancellation of the event
(see :meth:`cancel`).
+ .. versionchanged:: 3.3
+ *argument* parameter is optional.
-.. method:: scheduler.enter(delay, priority, action, argument)
+ .. versionadded:: 3.3
+ *kwargs* parameter was added.
+
+
+.. method:: scheduler.enter(delay, priority, action, argument=[], kwargs={})
Schedule an event for *delay* more time units. Other than the relative time, the
other arguments, the effect and the return value are the same as those for
:meth:`enterabs`.
+ .. versionchanged:: 3.3
+ *argument* parameter is optional.
+
+ .. versionadded:: 3.3
+ *kwargs* parameter was added.
.. method:: scheduler.cancel(event)
@@ -111,12 +102,15 @@ Scheduler Objects
Return true if the event queue is empty.
-.. method:: scheduler.run()
+.. method:: scheduler.run(blocking=True)
- Run all scheduled events. This function will wait (using the :func:`delayfunc`
+ Run all scheduled events. This method will wait (using the :func:`delayfunc`
function passed to the constructor) for the next event, then execute it and so
on until there are no more scheduled events.
+ If *blocking* is False executes the scheduled events due to expire soonest
+ (if any) and then return.
+
Either *action* or *delayfunc* can raise an exception. In either case, the
scheduler will maintain a consistent state and propagate the exception. If an
exception is raised by *action*, the event will not be attempted in future calls
@@ -127,6 +121,9 @@ Scheduler Objects
the calling code is responsible for canceling events which are no longer
pertinent.
+ .. versionadded:: 3.3
+ *blocking* parameter was added.
+
.. attribute:: scheduler.queue
Read-only attribute returning a list of upcoming events in the order they
diff --git a/Doc/library/select.rst b/Doc/library/select.rst
index f1fd126..72fba73 100644
--- a/Doc/library/select.rst
+++ b/Doc/library/select.rst
@@ -6,7 +6,8 @@
This module provides access to the :c:func:`select` and :c:func:`poll` functions
-available in most operating systems, :c:func:`epoll` available on Linux 2.5+ and
+available in most operating systems, :c:func:`devpoll` available on
+Solaris and derivatives, :c:func:`epoll` available on Linux 2.5+ and
:c:func:`kqueue` available on most BSD.
Note that on Windows, it only works for sockets; on other operating systems,
it also works for other file types (in particular, on Unix, it works on pipes).
@@ -18,10 +19,25 @@ The module defines the following:
.. exception:: error
- The exception raised when an error occurs. The accompanying value is a pair
- containing the numeric error code from :c:data:`errno` and the corresponding
- string, as would be printed by the C function :c:func:`perror`.
+ A deprecated alias of :exc:`OSError`.
+ .. versionchanged:: 3.3
+ Following :pep:`3151`, this class was made an alias of :exc:`OSError`.
+
+
+.. function:: devpoll()
+
+ (Only supported on Solaris and derivatives.) Returns a ``/dev/poll``
+ polling object; see section :ref:`devpoll-objects` below for the
+ methods supported by devpoll objects.
+
+ :c:func:`devpoll` objects are linked to the number of file
+ descriptors allowed at the time of instantiation. If your program
+ reduces this value, :c:func:`devpoll` will fail. If your program
+ increases this value, :c:func:`devpoll` may return an
+ incomplete list of active file descriptors.
+
+ .. versionadded:: 3.3
.. function:: epoll(sizehint=-1)
@@ -106,6 +122,74 @@ The module defines the following:
.. versionadded:: 3.2
+.. _devpoll-objects:
+
+``/dev/poll`` Polling Objects
+----------------------------------------------
+
+ http://developers.sun.com/solaris/articles/using_devpoll.html
+ http://developers.sun.com/solaris/articles/polling_efficient.html
+
+Solaris and derivatives have ``/dev/poll``. While :c:func:`select` is
+O(highest file descriptor) and :c:func:`poll` is O(number of file
+descriptors), ``/dev/poll`` is O(active file descriptors).
+
+``/dev/poll`` behaviour is very close to the standard :c:func:`poll`
+object.
+
+
+.. method:: devpoll.register(fd[, eventmask])
+
+ Register a file descriptor with the polling object. Future calls to the
+ :meth:`poll` method will then check whether the file descriptor has any pending
+ I/O events. *fd* can be either an integer, or an object with a :meth:`fileno`
+ method that returns an integer. File objects implement :meth:`fileno`, so they
+ can also be used as the argument.
+
+ *eventmask* is an optional bitmask describing the type of events you want to
+ check for. The constants are the same that with :c:func:`poll`
+ object. The default value is a combination of the constants :const:`POLLIN`,
+ :const:`POLLPRI`, and :const:`POLLOUT`.
+
+ .. warning::
+
+ Registering a file descriptor that's already registered is not an
+ error, but the result is undefined. The appropiate action is to
+ unregister or modify it first. This is an important difference
+ compared with :c:func:`poll`.
+
+
+.. method:: devpoll.modify(fd[, eventmask])
+
+ This method does an :meth:`unregister` followed by a
+ :meth:`register`. It is (a bit) more efficient that doing the same
+ explicitly.
+
+
+.. method:: devpoll.unregister(fd)
+
+ Remove a file descriptor being tracked by a polling object. Just like the
+ :meth:`register` method, *fd* can be an integer or an object with a
+ :meth:`fileno` method that returns an integer.
+
+ Attempting to remove a file descriptor that was never registered is
+ safely ignored.
+
+
+.. method:: devpoll.poll([timeout])
+
+ Polls the set of registered file descriptors, and returns a possibly-empty list
+ containing ``(fd, event)`` 2-tuples for the descriptors that have events or
+ errors to report. *fd* is the file descriptor, and *event* is a bitmask with
+ bits set for the reported events for that descriptor --- :const:`POLLIN` for
+ waiting input, :const:`POLLOUT` to indicate that the descriptor can be written
+ to, and so forth. An empty list indicates that the call timed out and no file
+ descriptors had any events to report. If *timeout* is given, it specifies the
+ length of time in milliseconds which the system will wait for events before
+ returning. If *timeout* is omitted, -1, or :const:`None`, the call will
+ block until there is an event for this poll object.
+
+
.. _epoll-objects:
Edge and Level Trigger Polling (epoll) Objects
@@ -165,11 +249,6 @@ Edge and Level Trigger Polling (epoll) Objects
Register a fd descriptor with the epoll object.
- .. note::
-
- Registering a file descriptor that's already registered raises an
- IOError -- contrary to :ref:`poll-objects`'s register.
-
.. method:: epoll.modify(fd, eventmask)
diff --git a/Doc/library/shlex.rst b/Doc/library/shlex.rst
index 0113fb7..908f996 100644
--- a/Doc/library/shlex.rst
+++ b/Doc/library/shlex.rst
@@ -34,6 +34,39 @@ The :mod:`shlex` module defines the following functions:
passing ``None`` for *s* will read the string to split from standard
input.
+
+.. function:: quote(s)
+
+ Return a shell-escaped version of the string *s*. The returned value is a
+ string that can safely be used as one token in a shell command line, for
+ cases where you cannot use a list.
+
+ This idiom would be unsafe::
+
+ >>> filename = 'somefile; rm -rf ~'
+ >>> command = 'ls -l {}'.format(filename)
+ >>> print(command) # executed by a shell: boom!
+ ls -l somefile; rm -rf ~
+
+ :func:`quote` lets you plug the security hole::
+
+ >>> command = 'ls -l {}'.format(quote(filename))
+ >>> print(command)
+ ls -l 'somefile; rm -rf ~'
+ >>> remote_command = 'ssh home {}'.format(quote(command))
+ >>> print(remote_command)
+ ssh home 'ls -l '"'"'somefile; rm -rf ~'"'"''
+
+ The quoting is compatible with UNIX shells and with :func:`split`:
+
+ >>> remote_command = split(remote_command)
+ >>> remote_command
+ ['ssh', 'home', "ls -l 'somefile; rm -rf ~'"]
+ >>> command = split(remote_command[-1])
+ >>> command
+ ['ls', '-l', 'somefile; rm -rf ~']
+
+
The :mod:`shlex` module defines the following class:
@@ -282,5 +315,4 @@ parsing rules.
* EOF is signaled with a :const:`None` value;
-* Quoted empty strings (``''``) are allowed;
-
+* Quoted empty strings (``''``) are allowed.
diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst
index 8b4aabb..bb18203 100644
--- a/Doc/library/shutil.rst
+++ b/Doc/library/shutil.rst
@@ -51,11 +51,14 @@ Directory and files operations
*dst* must be the complete target file name; look at :func:`copy` for a copy that
accepts a target directory path. If *src* and *dst* are the same files,
:exc:`Error` is raised.
- The destination location must be writable; otherwise, an :exc:`IOError` exception
+ The destination location must be writable; otherwise, an :exc:`OSError` exception
will be raised. If *dst* already exists, it will be replaced. Special files
such as character or block devices and pipes cannot be copied with this
function. *src* and *dst* are path names given as strings.
+ .. versionchanged:: 3.3
+ :exc:`IOError` used to be raised instead of :exc:`OSError`.
+
.. function:: copymode(src, dst)
@@ -173,6 +176,29 @@ Directory and files operations
used. Otherwise, *src* is copied (using :func:`copy2`) to *dst* and then
removed.
+.. function:: disk_usage(path)
+
+ Return disk usage statistics about the given path as a :term:`named tuple`
+ with the attributes *total*, *used* and *free*, which are the amount of
+ total, used and free space, in bytes.
+
+ .. versionadded:: 3.3
+
+ Availability: Unix, Windows.
+
+.. function:: chown(path, user=None, group=None)
+
+ Change owner *user* and/or *group* of the given *path*.
+
+ *user* can be a system user name or a uid; the same applies to *group*. At
+ least one argument is required.
+
+ See also :func:`os.chown`, the underlying function.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
.. exception:: Error
diff --git a/Doc/library/signal.rst b/Doc/library/signal.rst
index 698b1e7..4fc3fd6 100644
--- a/Doc/library/signal.rst
+++ b/Doc/library/signal.rst
@@ -13,9 +13,6 @@ rules for working with signals and their handlers:
underlying implementation), with the exception of the handler for
:const:`SIGCHLD`, which follows the underlying implementation.
-* There is no way to "block" signals temporarily from critical sections (since
- this is not supported by all Unix flavors).
-
* Although Python signal handlers are called asynchronously as far as the Python
user is concerned, they can only occur between the "atomic" instructions of the
Python interpreter. This means that signals arriving during long calculations
@@ -119,6 +116,28 @@ The variables defined in the :mod:`signal` module are:
in user and kernel space. SIGPROF is delivered upon expiration.
+.. data:: SIG_BLOCK
+
+ A possible value for the *how* parameter to :func:`pthread_sigmask`
+ indicating that signals are to be blocked.
+
+ .. versionadded:: 3.3
+
+.. data:: SIG_UNBLOCK
+
+ A possible value for the *how* parameter to :func:`pthread_sigmask`
+ indicating that signals are to be unblocked.
+
+ .. versionadded:: 3.3
+
+.. data:: SIG_SETMASK
+
+ A possible value for the *how* parameter to :func:`pthread_sigmask`
+ indicating that the signal mask is to be replaced.
+
+ .. versionadded:: 3.3
+
+
The :mod:`signal` module defines one exception:
.. exception:: ItimerError
@@ -126,7 +145,11 @@ The :mod:`signal` module defines one exception:
Raised to signal an error from the underlying :func:`setitimer` or
:func:`getitimer` implementation. Expect this error if an invalid
interval timer or a negative time is passed to :func:`setitimer`.
- This error is a subtype of :exc:`IOError`.
+ This error is a subtype of :exc:`OSError`.
+
+ .. versionadded:: 3.3
+ This error used to be a subtype of :exc:`IOError`, which is now an
+ alias of :exc:`OSError`.
The :mod:`signal` module defines the following functions:
@@ -160,6 +183,60 @@ The :mod:`signal` module defines the following functions:
will then be called. Returns nothing. Not on Windows. (See the Unix man page
:manpage:`signal(2)`.)
+ See also :func:`sigwait`, :func:`sigwaitinfo`, :func:`sigtimedwait` and
+ :func:`sigpending`.
+
+
+.. function:: pthread_kill(thread_id, signum)
+
+ Send the signal *signum* to the thread *thread_id*, another thread in the same
+ process as the caller. The signal is asynchronously directed to thread.
+
+ Use :func:`threading.get_ident()` or the :attr:`~threading.Thread.ident`
+ attribute of :attr:`threading.Thread` to get a 'thread identifier' for
+ *thread_id*.
+
+ If *signum* is 0, then no signal is sent, but error checking is still
+ performed; this can be used to check if a thread is still running.
+
+ Availability: Unix (see the man page :manpage:`pthread_kill(3)` for further
+ information).
+
+ See also :func:`os.kill`.
+
+ .. versionadded:: 3.3
+
+
+.. function:: pthread_sigmask(how, mask)
+
+ Fetch and/or change the signal mask of the calling thread. The signal mask
+ is the set of signals whose delivery is currently blocked for the caller.
+ Return the old signal mask as a set of signals.
+
+ The behavior of the call is dependent on the value of *how*, as follows.
+
+ * :data:`SIG_BLOCK`: The set of blocked signals is the union of the current
+ set and the *mask* argument.
+ * :data:`SIG_UNBLOCK`: The signals in *mask* are removed from the current
+ set of blocked signals. It is permissible to attempt to unblock a
+ signal which is not blocked.
+ * :data:`SIG_SETMASK`: The set of blocked signals is set to the *mask*
+ argument.
+
+ *mask* is a set of signal numbers (e.g. {:const:`signal.SIGINT`,
+ :const:`signal.SIGTERM`}). Use ``range(1, signal.NSIG)`` for a full mask
+ including all signals.
+
+ For example, ``signal.pthread_sigmask(signal.SIG_BLOCK, [])`` reads the
+ signal mask of the calling thread.
+
+ Availability: Unix. See the man page :manpage:`sigprocmask(3)` and
+ :manpage:`pthread_sigmask(3)` for further information.
+
+ See also :func:`pause`, :func:`sigpending` and :func:`sigwait`.
+
+ .. versionadded:: 3.3
+
.. function:: setitimer(which, seconds[, interval])
@@ -189,13 +266,17 @@ The :mod:`signal` module defines the following functions:
.. function:: set_wakeup_fd(fd)
- Set the wakeup fd to *fd*. When a signal is received, a ``'\0'`` byte is
- written to the fd. This can be used by a library to wakeup a poll or select
- call, allowing the signal to be fully processed.
+ Set the wakeup file descriptor to *fd*. When a signal is received, the
+ signal number is written as a single byte into the fd. This can be used by
+ a library to wakeup a poll or select call, allowing the signal to be fully
+ processed.
The old wakeup fd is returned. *fd* must be non-blocking. It is up to the
library to remove any bytes before calling poll or select again.
+ Use for example ``struct.unpack('%uB' % len(data), data)`` to decode the
+ signal numbers list.
+
When threads are enabled, this function can only be called from the main thread;
attempting to call it from other threads will cause a :exc:`ValueError`
exception to be raised.
@@ -235,6 +316,74 @@ The :mod:`signal` module defines the following functions:
:const:`SIGTERM`. A :exc:`ValueError` will be raised in any other case.
+.. function:: sigpending()
+
+ Examine the set of signals that are pending for delivery to the calling
+ thread (i.e., the signals which have been raised while blocked). Return the
+ set of the pending signals.
+
+ Availability: Unix (see the man page :manpage:`sigpending(2)` for further
+ information).
+
+ See also :func:`pause`, :func:`pthread_sigmask` and :func:`sigwait`.
+
+ .. versionadded:: 3.3
+
+
+.. function:: sigwait(sigset)
+
+ Suspend execution of the calling thread until the delivery of one of the
+ signals specified in the signal set *sigset*. The function accepts the signal
+ (removes it from the pending list of signals), and returns the signal number.
+
+ Availability: Unix (see the man page :manpage:`sigwait(3)` for further
+ information).
+
+ See also :func:`pause`, :func:`pthread_sigmask`, :func:`sigpending`,
+ :func:`sigwaitinfo` and :func:`sigtimedwait`.
+
+ .. versionadded:: 3.3
+
+
+.. function:: sigwaitinfo(sigset)
+
+ Suspend execution of the calling thread until the delivery of one of the
+ signals specified in the signal set *sigset*. The function accepts the
+ signal and removes it from the pending list of signals. If one of the
+ signals in *sigset* is already pending for the calling thread, the function
+ will return immediately with information about that signal. The signal
+ handler is not called for the delivered signal. The function raises an
+ :exc:`InterruptedError` if it is interrupted by a signal that is not in
+ *sigset*.
+
+ The return value is an object representing the data contained in the
+ :c:type:`siginfo_t` structure, namely: :attr:`si_signo`, :attr:`si_code`,
+ :attr:`si_errno`, :attr:`si_pid`, :attr:`si_uid`, :attr:`si_status`,
+ :attr:`si_band`.
+
+ Availability: Unix (see the man page :manpage:`sigwaitinfo(2)` for further
+ information).
+
+ See also :func:`pause`, :func:`sigwait` and :func:`sigtimedwait`.
+
+ .. versionadded:: 3.3
+
+
+.. function:: sigtimedwait(sigset, (timeout_sec, timeout_nsec))
+
+ Like :func:`sigtimedwait`, but takes a tuple of ``(seconds, nanoseconds)``
+ as an additional argument specifying a timeout. If both *timeout_sec* and
+ *timeout_nsec* are specified as :const:`0`, a poll is performed. Returns
+ :const:`None` if a timeout occurs.
+
+ Availability: Unix (see the man page :manpage:`sigtimedwait(2)` for further
+ information).
+
+ See also :func:`pause`, :func:`sigwait` and :func:`sigwaitinfo`.
+
+ .. versionadded:: 3.3
+
+
.. _signal-example:
Example
@@ -251,7 +400,7 @@ be sent, and the handler raises an exception. ::
def handler(signum, frame):
print('Signal handler called with signal', signum)
- raise IOError("Couldn't open device!")
+ raise OSError("Couldn't open device!")
# Set the signal handler and a 5-second alarm
signal.signal(signal.SIGALRM, handler)
diff --git a/Doc/library/site.rst b/Doc/library/site.rst
index db96add..b987897 100644
--- a/Doc/library/site.rst
+++ b/Doc/library/site.rst
@@ -16,7 +16,14 @@ import can be suppressed using the interpreter's :option:`-S` option.
.. index:: triple: module; search; path
Importing this module will append site-specific paths to the module search path
-and add a few builtins.
+and add a few builtins, unless :option:`-S` was used. In that case, this module
+can be safely imported with no automatic modifications to the module search path
+or additions to the builtins. To explicitly trigger the usual site-specific
+additions, call the :func:`site.main` function.
+
+.. versionchanged:: 3.3
+ Importing the module used to trigger paths manipulation even when using
+ :option:`-S`.
.. index::
pair: site-python; directory
@@ -127,10 +134,21 @@ empty, and the path manipulations are skipped; however the import of
:func:`getuserbase` hasn't been called yet. Default value is
:file:`~/.local` for UNIX and Mac OS X non-framework builds,
:file:`~/Library/Python/{X.Y}` for Mac framework builds, and
- :file:`{%APPDATA%}\\Python` for Windows. This value is used by Distutils to
+ :file:`{%APPDATA%}\\Python` for Windows. This value is used by Packaging to
compute the installation directories for scripts, data files, Python modules,
- etc. for the :ref:`user installation scheme <inst-alt-install-user>`. See
- also :envvar:`PYTHONUSERBASE`.
+ etc. for the :ref:`user installation scheme <packaging-alt-install-user>`.
+ See also :envvar:`PYTHONUSERBASE`.
+
+
+.. function:: main()
+
+ Adds all the standard site-specific directories to the module search
+ path. This function is called automatically when this module is imported,
+ unless the :program:`python` interpreter was started with the :option:`-S`
+ flag.
+
+ .. versionchanged:: 3.3
+ This function used to be called unconditionnally.
.. function:: addsitedir(sitedir, known_paths=None)
diff --git a/Doc/library/smtplib.rst b/Doc/library/smtplib.rst
index 5978a8f..7dd038d 100644
--- a/Doc/library/smtplib.rst
+++ b/Doc/library/smtplib.rst
@@ -20,7 +20,7 @@ details of SMTP and ESMTP operation, consult :rfc:`821` (Simple Mail Transfer
Protocol) and :rfc:`1869` (SMTP Service Extensions).
-.. class:: SMTP(host='', port=0, local_hostname=None[, timeout])
+.. class:: SMTP(host='', port=0, local_hostname=None[, timeout], source_address=None)
A :class:`SMTP` instance encapsulates an SMTP connection. It has methods
that support a full repertoire of SMTP and ESMTP operations. If the optional
@@ -29,13 +29,34 @@ Protocol) and :rfc:`1869` (SMTP Service Extensions).
raised if the specified host doesn't respond correctly. The optional
*timeout* parameter specifies a timeout in seconds for blocking operations
like the connection attempt (if not specified, the global default timeout
- setting will be used).
+ setting will be used). The optional source_address parameter allows to bind to some
+ specific source address in a machine with multiple network interfaces,
+ and/or to some specific source TCP port. It takes a 2-tuple (host, port),
+ for the socket to bind to as its source address before connecting. If
+ omitted (or if host or port are ``''`` and/or 0 respectively) the OS default
+ behavior will be used.
For normal use, you should only require the initialization/connect,
:meth:`sendmail`, and :meth:`quit` methods. An example is included below.
+ The :class:`SMTP` class supports the :keyword:`with` statement. When used
+ like this, the SMTP ``QUIT`` command is issued automatically when the
+ :keyword:`with` statement exits. E.g.::
-.. class:: SMTP_SSL(host='', port=0, local_hostname=None, keyfile=None, certfile=None[, timeout])
+ >>> from smtplib import SMTP
+ >>> with SMTP("domain.org") as smtp:
+ ... smtp.noop()
+ ...
+ (250, b'Ok')
+ >>>
+
+ .. versionchanged:: 3.3
+ Support for the :keyword:`with` statement was added.
+
+ .. versionchanged:: 3.3
+ source_address argument was added.
+
+.. class:: SMTP_SSL(host='', port=0, local_hostname=None, keyfile=None, certfile=None[, timeout], context=None, source_address=None)
A :class:`SMTP_SSL` instance behaves exactly the same as instances of
:class:`SMTP`. :class:`SMTP_SSL` should be used for situations where SSL is
@@ -43,18 +64,33 @@ Protocol) and :rfc:`1869` (SMTP Service Extensions).
not appropriate. If *host* is not specified, the local host is used. If
*port* is zero, the standard SMTP-over-SSL port (465) is used. *keyfile*
and *certfile* are also optional, and can contain a PEM formatted private key
- and certificate chain file for the SSL connection. The optional *timeout*
+ and certificate chain file for the SSL connection. *context* also optional, can contain
+ a SSLContext, and is an alternative to keyfile and certfile; If it is specified both
+ keyfile and certfile must be None. The optional *timeout*
parameter specifies a timeout in seconds for blocking operations like the
connection attempt (if not specified, the global default timeout setting
- will be used).
+ will be used). The optional source_address parameter allows to bind to some
+ specific source address in a machine with multiple network interfaces,
+ and/or to some specific source tcp port. It takes a 2-tuple (host, port),
+ for the socket to bind to as its source address before connecting. If
+ omitted (or if host or port are ``''`` and/or 0 respectively) the OS default
+ behavior will be used.
+
+ .. versionchanged:: 3.3
+ *context* was added.
+ .. versionchanged:: 3.3
+ source_address argument was added.
-.. class:: LMTP(host='', port=LMTP_PORT, local_hostname=None)
+
+.. class:: LMTP(host='', port=LMTP_PORT, local_hostname=None, source_address=None)
The LMTP protocol, which is very similar to ESMTP, is heavily based on the
- standard SMTP client. It's common to use Unix sockets for LMTP, so our :meth:`connect`
- method must support that as well as a regular host:port server. To specify a
- Unix socket, you must use an absolute path for *host*, starting with a '/'.
+ standard SMTP client. It's common to use Unix sockets for LMTP, so our
+ :meth:`connect` method must support that as well as a regular host:port
+ server. The optional arguments local_hostname and source_address have the
+ same meaning as that of SMTP client. To specify a Unix socket, you must use
+ an absolute path for *host*, starting with a '/'.
Authentication is supported, using the regular SMTP mechanism. When using a Unix
socket, LMTP generally don't support or require any authentication, but your
@@ -242,7 +278,7 @@ An :class:`SMTP` instance has the following methods:
No suitable authentication method was found.
-.. method:: SMTP.starttls(keyfile=None, certfile=None)
+.. method:: SMTP.starttls(keyfile=None, certfile=None, context=None)
Put the SMTP connection in TLS (Transport Layer Security) mode. All SMTP
commands that follow will be encrypted. You should then call :meth:`ehlo`
@@ -251,6 +287,9 @@ An :class:`SMTP` instance has the following methods:
If *keyfile* and *certfile* are provided, these are passed to the :mod:`socket`
module's :func:`ssl` function.
+ Optional *context* parameter is a :class:`ssl.SSLContext` object; This is an alternative to
+ using a keyfile and a certfile and if specified both *keyfile* and *certfile* should be None.
+
If there has been no previous ``EHLO`` or ``HELO`` command this session,
this method tries ESMTP ``EHLO`` first.
@@ -263,6 +302,9 @@ An :class:`SMTP` instance has the following methods:
:exc:`RuntimeError`
SSL/TLS support is not available to your Python interpreter.
+ .. versionchanged:: 3.3
+ *context* was added.
+
.. method:: SMTP.sendmail(from_addr, to_addrs, msg, mail_options=[], rcpt_options=[])
diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst
index ef81c4d..77f5bb8 100644
--- a/Doc/library/socket.rst
+++ b/Doc/library/socket.rst
@@ -40,9 +40,23 @@ Socket families
Depending on the system and the build options, various socket families
are supported by this module.
-Socket addresses are represented as follows:
-
-- A single string is used for the :const:`AF_UNIX` address family.
+The address format required by a particular socket object is automatically
+selected based on the address family specified when the socket object was
+created. Socket addresses are represented as follows:
+
+- The address of an :const:`AF_UNIX` socket bound to a file system node
+ is represented as a string, using the file system encoding and the
+ ``'surrogateescape'`` error handler (see :pep:`383`). An address in
+ Linux's abstract namespace is returned as a :class:`bytes` object with
+ an initial null byte; note that sockets in this namespace can
+ communicate with normal file system sockets, so programs intended to
+ run on Linux may need to deal with both types of address. A string or
+ :class:`bytes` object can be used for either type of address when
+ passing it as an argument.
+
+ .. versionchanged:: 3.3
+ Previously, :const:`AF_UNIX` socket paths were assumed to use UTF-8
+ encoding.
- A pair ``(host, port)`` is used for the :const:`AF_INET` address family,
where *host* is a string representing either a hostname in Internet domain
@@ -80,6 +94,11 @@ Socket addresses are represented as follows:
If *addr_type* is :const:`TIPC_ADDR_ID`, then *v1* is the node, *v2* is the
reference, and *v3* should be set to 0.
+- A tuple ``(interface, )`` is used for the :const:`AF_CAN` address family,
+ where *interface* is a string representing a network interface name like
+ ``'can0'``. The network interface name ``''`` can be used to receive packets
+ from all network interfaces of this family.
+
- Certain other address families (:const:`AF_BLUETOOTH`, :const:`AF_PACKET`)
support specific representations.
@@ -99,8 +118,9 @@ resolution and/or the host configuration. For deterministic behavior use a
numeric address in *host* portion.
All errors raise exceptions. The normal exceptions for invalid argument types
-and out-of-memory conditions can be raised; errors related to socket or address
-semantics raise :exc:`socket.error` or one of its subclasses.
+and out-of-memory conditions can be raised; starting from Python 3.3, errors
+related to socket or address semantics raise :exc:`OSError` or one of its
+subclasses (they used to raise :exc:`socket.error`).
Non-blocking mode is supported through :meth:`~socket.setblocking`. A
generalization of this based on timeouts is supported through
@@ -115,20 +135,15 @@ The module :mod:`socket` exports the following constants and functions:
.. exception:: error
- .. index:: module: errno
+ A deprecated alias of :exc:`OSError`.
- A subclass of :exc:`IOError`, this exception is raised for socket-related
- errors. It is recommended that you inspect its ``errno`` attribute to
- discriminate between different kinds of errors.
-
- .. seealso::
- The :mod:`errno` module contains symbolic names for the error codes
- defined by the underlying operating system.
+ .. versionchanged:: 3.3
+ Following :pep:`3151`, this class was made an alias of :exc:`OSError`.
.. exception:: herror
- A subclass of :exc:`socket.error`, this exception is raised for
+ A subclass of :exc:`OSError`, this exception is raised for
address-related errors, i.e. for functions that use *h_errno* in the POSIX
C API, including :func:`gethostbyname_ex` and :func:`gethostbyaddr`.
The accompanying value is a pair ``(h_errno, string)`` representing an
@@ -136,10 +151,12 @@ The module :mod:`socket` exports the following constants and functions:
*string* represents the description of *h_errno*, as returned by the
:c:func:`hstrerror` C function.
+ .. versionchanged:: 3.3
+ This class was made a subclass of :exc:`OSError`.
.. exception:: gaierror
- A subclass of :exc:`socket.error`, this exception is raised for
+ A subclass of :exc:`OSError`, this exception is raised for
address-related errors by :func:`getaddrinfo` and :func:`getnameinfo`.
The accompanying value is a pair ``(error, string)`` representing an error
returned by a library call. *string* represents the description of
@@ -147,15 +164,19 @@ The module :mod:`socket` exports the following constants and functions:
numeric *error* value will match one of the :const:`EAI_\*` constants
defined in this module.
+ .. versionchanged:: 3.3
+ This class was made a subclass of :exc:`OSError`.
.. exception:: timeout
- A subclass of :exc:`socket.error`, this exception is raised when a timeout
+ A subclass of :exc:`OSError`, this exception is raised when a timeout
occurs on a socket which has had timeouts enabled via a prior call to
:meth:`~socket.settimeout` (or implicitly through
:func:`~socket.setdefaulttimeout`). The accompanying value is a string
whose value is currently always "timed out".
+ .. versionchanged:: 3.3
+ This class was made a subclass of :exc:`OSError`.
.. data:: AF_UNIX
AF_INET
@@ -198,6 +219,7 @@ The module :mod:`socket` exports the following constants and functions:
SOMAXCONN
MSG_*
SOL_*
+ SCM_*
IPPROTO_*
IPPORT_*
INADDR_*
@@ -215,6 +237,32 @@ The module :mod:`socket` exports the following constants and functions:
in the Unix header files are defined; for a few symbols, default values are
provided.
+.. data:: AF_CAN
+ PF_CAN
+ SOL_CAN_*
+ CAN_*
+
+ Many constants of these forms, documented in the Linux documentation, are
+ also defined in the socket module.
+
+ Availability: Linux >= 2.6.25.
+
+ .. versionadded:: 3.3
+
+
+.. data:: AF_RDS
+ PF_RDS
+ SOL_RDS
+ RDS_*
+
+ Many constants of these forms, documented in the Linux documentation, are
+ also defined in the socket module.
+
+ Availability: Linux >= 2.6.30.
+
+ .. versionadded:: 3.3
+
+
.. data:: SIO_*
RCVALL_*
@@ -386,10 +434,15 @@ The module :mod:`socket` exports the following constants and functions:
Create a new socket using the given address family, socket type and protocol
number. The address family should be :const:`AF_INET` (the default),
- :const:`AF_INET6` or :const:`AF_UNIX`. The socket type should be
- :const:`SOCK_STREAM` (the default), :const:`SOCK_DGRAM` or perhaps one of the
- other ``SOCK_`` constants. The protocol number is usually zero and may be
- omitted in that case.
+ :const:`AF_INET6`, :const:`AF_UNIX`, :const:`AF_CAN` or :const:`AF_RDS`. The
+ socket type should be :const:`SOCK_STREAM` (the default),
+ :const:`SOCK_DGRAM`, :const:`SOCK_RAW` or perhaps one of the other ``SOCK_``
+ constants. The protocol number is usually zero and may be omitted in that
+ case or :const:`CAN_RAW` in case the address family is :const:`AF_CAN`.
+
+ .. versionchanged:: 3.3
+ The AF_CAN family was added.
+ The AF_RDS family was added.
.. function:: socketpair([family[, type[, proto]]])
@@ -457,7 +510,7 @@ The module :mod:`socket` exports the following constants and functions:
Unix manual page :manpage:`inet(3)` for details.
If the IPv4 address string passed to this function is invalid,
- :exc:`socket.error` will be raised. Note that exactly what is valid depends on
+ :exc:`OSError` will be raised. Note that exactly what is valid depends on
the underlying C implementation of :c:func:`inet_aton`.
:func:`inet_aton` does not support IPv6, and :func:`inet_pton` should be used
@@ -474,7 +527,7 @@ The module :mod:`socket` exports the following constants and functions:
argument.
If the byte sequence passed to this function is not exactly 4 bytes in
- length, :exc:`socket.error` will be raised. :func:`inet_ntoa` does not
+ length, :exc:`OSError` will be raised. :func:`inet_ntoa` does not
support IPv6, and :func:`inet_ntop` should be used instead for IPv4/v6 dual
stack support.
@@ -488,7 +541,7 @@ The module :mod:`socket` exports the following constants and functions:
Supported values for *address_family* are currently :const:`AF_INET` and
:const:`AF_INET6`. If the IP address string *ip_string* is invalid,
- :exc:`socket.error` will be raised. Note that exactly what is valid depends on
+ :exc:`OSError` will be raised. Note that exactly what is valid depends on
both the value of *address_family* and the underlying implementation of
:c:func:`inet_pton`.
@@ -506,11 +559,54 @@ The module :mod:`socket` exports the following constants and functions:
Supported values for *address_family* are currently :const:`AF_INET` and
:const:`AF_INET6`. If the string *packed_ip* is not the correct length for the
specified address family, :exc:`ValueError` will be raised. A
- :exc:`socket.error` is raised for errors from the call to :func:`inet_ntop`.
+ :exc:`OSError` is raised for errors from the call to :func:`inet_ntop`.
Availability: Unix (maybe not all platforms).
+..
+ XXX: Are sendmsg(), recvmsg() and CMSG_*() available on any
+ non-Unix platforms? The old (obsolete?) 4.2BSD form of the
+ interface, in which struct msghdr has no msg_control or
+ msg_controllen members, is not currently supported.
+
+.. function:: CMSG_LEN(length)
+
+ Return the total length, without trailing padding, of an ancillary
+ data item with associated data of the given *length*. This value
+ can often be used as the buffer size for :meth:`~socket.recvmsg` to
+ receive a single item of ancillary data, but :rfc:`3542` requires
+ portable applications to use :func:`CMSG_SPACE` and thus include
+ space for padding, even when the item will be the last in the
+ buffer. Raises :exc:`OverflowError` if *length* is outside the
+ permissible range of values.
+
+ Availability: most Unix platforms, possibly others.
+
+ .. versionadded:: 3.3
+
+
+.. function:: CMSG_SPACE(length)
+
+ Return the buffer size needed for :meth:`~socket.recvmsg` to
+ receive an ancillary data item with associated data of the given
+ *length*, along with any trailing padding. The buffer space needed
+ to receive multiple items is the sum of the :func:`CMSG_SPACE`
+ values for their associated data lengths. Raises
+ :exc:`OverflowError` if *length* is outside the permissible range
+ of values.
+
+ Note that some systems might support ancillary data without
+ providing this function. Also note that setting the buffer size
+ using the results of this function may not precisely limit the
+ amount of ancillary data that can be received, since additional
+ data may be able to fit into the padding area.
+
+ Availability: most Unix platforms, possibly others.
+
+ .. versionadded:: 3.3
+
+
.. function:: getdefaulttimeout()
Return the default timeout in seconds (float) for new socket objects. A value
@@ -526,6 +622,49 @@ The module :mod:`socket` exports the following constants and functions:
meanings.
+.. function:: sethostname(name)
+
+ Set the machine's hostname to *name*. This will raise a
+ :exc:`OSError` if you don't have enough rights.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
+.. function:: if_nameindex()
+
+ Return a list of network interface information
+ (index int, name string) tuples.
+ :exc:`OSError` if the system call fails.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
+.. function:: if_nametoindex(if_name)
+
+ Return a network interface index number corresponding to an
+ interface name.
+ :exc:`OSError` if no interface with the given name exists.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
+.. function:: if_indextoname(if_index)
+
+ Return a network interface name corresponding to a
+ interface index number.
+ :exc:`OSError` if no interface with the given index exists.
+
+ Availability: Unix.
+
+ .. versionadded:: 3.3
+
+
.. data:: SocketType
This is a Python type object that represents the socket object type. It is the
@@ -699,6 +838,109 @@ correspond to Unix system calls applicable to sockets.
to zero. (The format of *address* depends on the address family --- see above.)
+.. method:: socket.recvmsg(bufsize[, ancbufsize[, flags]])
+
+ Receive normal data (up to *bufsize* bytes) and ancillary data from
+ the socket. The *ancbufsize* argument sets the size in bytes of
+ the internal buffer used to receive the ancillary data; it defaults
+ to 0, meaning that no ancillary data will be received. Appropriate
+ buffer sizes for ancillary data can be calculated using
+ :func:`CMSG_SPACE` or :func:`CMSG_LEN`, and items which do not fit
+ into the buffer might be truncated or discarded. The *flags*
+ argument defaults to 0 and has the same meaning as for
+ :meth:`recv`.
+
+ The return value is a 4-tuple: ``(data, ancdata, msg_flags,
+ address)``. The *data* item is a :class:`bytes` object holding the
+ non-ancillary data received. The *ancdata* item is a list of zero
+ or more tuples ``(cmsg_level, cmsg_type, cmsg_data)`` representing
+ the ancillary data (control messages) received: *cmsg_level* and
+ *cmsg_type* are integers specifying the protocol level and
+ protocol-specific type respectively, and *cmsg_data* is a
+ :class:`bytes` object holding the associated data. The *msg_flags*
+ item is the bitwise OR of various flags indicating conditions on
+ the received message; see your system documentation for details.
+ If the receiving socket is unconnected, *address* is the address of
+ the sending socket, if available; otherwise, its value is
+ unspecified.
+
+ On some systems, :meth:`sendmsg` and :meth:`recvmsg` can be used to
+ pass file descriptors between processes over an :const:`AF_UNIX`
+ socket. When this facility is used (it is often restricted to
+ :const:`SOCK_STREAM` sockets), :meth:`recvmsg` will return, in its
+ ancillary data, items of the form ``(socket.SOL_SOCKET,
+ socket.SCM_RIGHTS, fds)``, where *fds* is a :class:`bytes` object
+ representing the new file descriptors as a binary array of the
+ native C :c:type:`int` type. If :meth:`recvmsg` raises an
+ exception after the system call returns, it will first attempt to
+ close any file descriptors received via this mechanism.
+
+ Some systems do not indicate the truncated length of ancillary data
+ items which have been only partially received. If an item appears
+ to extend beyond the end of the buffer, :meth:`recvmsg` will issue
+ a :exc:`RuntimeWarning`, and will return the part of it which is
+ inside the buffer provided it has not been truncated before the
+ start of its associated data.
+
+ On systems which support the :const:`SCM_RIGHTS` mechanism, the
+ following function will receive up to *maxfds* file descriptors,
+ returning the message data and a list containing the descriptors
+ (while ignoring unexpected conditions such as unrelated control
+ messages being received). See also :meth:`sendmsg`. ::
+
+ import socket, array
+
+ def recv_fds(sock, msglen, maxfds):
+ fds = array.array("i") # Array of ints
+ msg, ancdata, flags, addr = sock.recvmsg(msglen, socket.CMSG_LEN(maxfds * fds.itemsize))
+ for cmsg_level, cmsg_type, cmsg_data in ancdata:
+ if (cmsg_level == socket.SOL_SOCKET and cmsg_type == socket.SCM_RIGHTS):
+ # Append data, ignoring any truncated integers at the end.
+ fds.fromstring(cmsg_data[:len(cmsg_data) - (len(cmsg_data) % fds.itemsize)])
+ return msg, list(fds)
+
+ Availability: most Unix platforms, possibly others.
+
+ .. versionadded:: 3.3
+
+
+.. method:: socket.recvmsg_into(buffers[, ancbufsize[, flags]])
+
+ Receive normal data and ancillary data from the socket, behaving as
+ :meth:`recvmsg` would, but scatter the non-ancillary data into a
+ series of buffers instead of returning a new bytes object. The
+ *buffers* argument must be an iterable of objects that export
+ writable buffers (e.g. :class:`bytearray` objects); these will be
+ filled with successive chunks of the non-ancillary data until it
+ has all been written or there are no more buffers. The operating
+ system may set a limit (:func:`~os.sysconf` value ``SC_IOV_MAX``)
+ on the number of buffers that can be used. The *ancbufsize* and
+ *flags* arguments have the same meaning as for :meth:`recvmsg`.
+
+ The return value is a 4-tuple: ``(nbytes, ancdata, msg_flags,
+ address)``, where *nbytes* is the total number of bytes of
+ non-ancillary data written into the buffers, and *ancdata*,
+ *msg_flags* and *address* are the same as for :meth:`recvmsg`.
+
+ Example::
+
+ >>> import socket
+ >>> s1, s2 = socket.socketpair()
+ >>> b1 = bytearray(b'----')
+ >>> b2 = bytearray(b'0123456789')
+ >>> b3 = bytearray(b'--------------')
+ >>> s1.send(b'Mary had a little lamb')
+ 22
+ >>> s2.recvmsg_into([b1, memoryview(b2)[2:9], b3])
+ (22, [], 0, None)
+ >>> [b1, b2, b3]
+ [bytearray(b'Mary'), bytearray(b'01 had a 9'), bytearray(b'little lamb---')]
+
+ Availability: most Unix platforms, possibly others.
+
+ .. versionadded:: 3.3
+
+
.. method:: socket.recvfrom_into(buffer[, nbytes[, flags]])
Receive data from the socket, writing it into *buffer* instead of creating a
@@ -746,6 +988,41 @@ correspond to Unix system calls applicable to sockets.
above.)
+.. method:: socket.sendmsg(buffers[, ancdata[, flags[, address]]])
+
+ Send normal and ancillary data to the socket, gathering the
+ non-ancillary data from a series of buffers and concatenating it
+ into a single message. The *buffers* argument specifies the
+ non-ancillary data as an iterable of buffer-compatible objects
+ (e.g. :class:`bytes` objects); the operating system may set a limit
+ (:func:`~os.sysconf` value ``SC_IOV_MAX``) on the number of buffers
+ that can be used. The *ancdata* argument specifies the ancillary
+ data (control messages) as an iterable of zero or more tuples
+ ``(cmsg_level, cmsg_type, cmsg_data)``, where *cmsg_level* and
+ *cmsg_type* are integers specifying the protocol level and
+ protocol-specific type respectively, and *cmsg_data* is a
+ buffer-compatible object holding the associated data. Note that
+ some systems (in particular, systems without :func:`CMSG_SPACE`)
+ might support sending only one control message per call. The
+ *flags* argument defaults to 0 and has the same meaning as for
+ :meth:`send`. If *address* is supplied and not ``None``, it sets a
+ destination address for the message. The return value is the
+ number of bytes of non-ancillary data sent.
+
+ The following function sends the list of file descriptors *fds*
+ over an :const:`AF_UNIX` socket, on systems which support the
+ :const:`SCM_RIGHTS` mechanism. See also :meth:`recvmsg`. ::
+
+ import socket, array
+
+ def send_fds(sock, msg, fds):
+ return sock.sendmsg([msg], [(socket.SOL_SOCKET, socket.SCM_RIGHTS, array.array("i", fds))])
+
+ Availability: most Unix platforms, possibly others.
+
+ .. versionadded:: 3.3
+
+
.. method:: socket.setblocking(flag)
Set blocking or non-blocking mode of the socket: if *flag* is false, the
@@ -934,13 +1211,13 @@ sends traffic to the first one connected successfully. ::
af, socktype, proto, canonname, sa = res
try:
s = socket.socket(af, socktype, proto)
- except socket.error as msg:
+ except OSError as msg:
s = None
continue
try:
s.bind(sa)
s.listen(1)
- except socket.error as msg:
+ except OSError as msg:
s.close()
s = None
continue
@@ -969,12 +1246,12 @@ sends traffic to the first one connected successfully. ::
af, socktype, proto, canonname, sa = res
try:
s = socket.socket(af, socktype, proto)
- except socket.error as msg:
+ except OSError as msg:
s = None
continue
try:
s.connect(sa)
- except socket.error as msg:
+ except OSError as msg:
s.close()
s = None
continue
@@ -988,7 +1265,7 @@ sends traffic to the first one connected successfully. ::
print('Received', repr(data))
-The last example shows how to write a very simple network sniffer with raw
+The next example shows how to write a very simple network sniffer with raw
sockets on Windows. The example requires administrator privileges to modify
the interface::
@@ -1013,11 +1290,51 @@ the interface::
# disabled promiscuous mode
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
+The last example shows how to use the socket interface to communicate to a CAN
+network. This example might require special priviledge::
+
+ import socket
+ import struct
+
+
+ # CAN frame packing/unpacking (see `struct can_frame` in <linux/can.h>)
+
+ can_frame_fmt = "=IB3x8s"
+ can_frame_size = struct.calcsize(can_frame_fmt)
+
+ def build_can_frame(can_id, data):
+ can_dlc = len(data)
+ data = data.ljust(8, b'\x00')
+ return struct.pack(can_frame_fmt, can_id, can_dlc, data)
+
+ def dissect_can_frame(frame):
+ can_id, can_dlc, data = struct.unpack(can_frame_fmt, frame)
+ return (can_id, can_dlc, data[:can_dlc])
+
+
+ # create a raw socket and bind it to the `vcan0` interface
+ s = socket.socket(socket.AF_CAN, socket.SOCK_RAW, socket.CAN_RAW)
+ s.bind(('vcan0',))
+
+ while True:
+ cf, addr = s.recvfrom(can_frame_size)
+
+ print('Received: can_id=%x, can_dlc=%x, data=%s' % dissect_can_frame(cf))
+
+ try:
+ s.send(cf)
+ except OSError:
+ print('Error sending CAN frame')
+
+ try:
+ s.send(build_can_frame(0x01, b'\x01\x02\x03'))
+ except OSError:
+ print('Error sending CAN frame')
Running an example several times with too small delay between executions, could
lead to this error::
- socket.error: [Errno 98] Address already in use
+ OSError: [Errno 98] Address already in use
This is because the previous execution has left the socket in a ``TIME_WAIT``
state, and can't be immediately reused.
diff --git a/Doc/library/socketserver.rst b/Doc/library/socketserver.rst
index 366dac4..497e3ee 100644
--- a/Doc/library/socketserver.rst
+++ b/Doc/library/socketserver.rst
@@ -154,8 +154,21 @@ Server Objects
.. method:: BaseServer.serve_forever(poll_interval=0.5)
Handle requests until an explicit :meth:`shutdown` request. Polls for
- shutdown every *poll_interval* seconds.
+ shutdown every *poll_interval* seconds. It also calls
+ :meth:`service_actions` which may be used by a subclass or Mixin to provide
+ various cleanup actions. For e.g. ForkingMixin class uses
+ :meth:`service_actions` to cleanup the zombie child processes.
+ .. versionchanged:: 3.3
+ Added service_actions call to the serve_forever method.
+
+
+.. method:: BaseServer.service_actions()
+
+ This is called by the serve_forever loop. This method is can be overridden
+ by Mixin's to add cleanup or service specific actions.
+
+ .. versionadded:: 3.3
.. method:: BaseServer.shutdown()
diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst
index 786bb04..f3acdc5 100644
--- a/Doc/library/sqlite3.rst
+++ b/Doc/library/sqlite3.rst
@@ -369,6 +369,22 @@ Connection Objects
method with :const:`None` for *handler*.
+.. method:: Connection.set_trace_callback(trace_callback)
+
+ Registers *trace_callback* to be called for each SQL statement that is
+ actually executed by the SQLite backend.
+
+ The only argument passed to the callback is the statement (as string) that
+ is being executed. The return value of the callback is ignored. Note that
+ the backend does not only run statements passed to the :meth:`Cursor.execute`
+ methods. Other sources include the transaction management of the Python
+ module and the execution of triggers defined in the current database.
+
+ Passing :const:`None` as *trace_callback* will disable the trace callback.
+
+ .. versionadded:: 3.3
+
+
.. method:: Connection.enable_load_extension(enabled)
This routine allows/disallows the SQLite engine to load SQLite extensions
diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst
index 497c5ba..69eaf8b 100644
--- a/Doc/library/ssl.rst
+++ b/Doc/library/ssl.rst
@@ -53,9 +53,53 @@ Functions, Constants, and Exceptions
(currently provided by the OpenSSL library). This signifies some
problem in the higher-level encryption and authentication layer that's
superimposed on the underlying network connection. This error
- is a subtype of :exc:`socket.error`, which in turn is a subtype of
- :exc:`IOError`. The error code and message of :exc:`SSLError` instances
- are provided by the OpenSSL library.
+ is a subtype of :exc:`OSError`. The error code and message of
+ :exc:`SSLError` instances are provided by the OpenSSL library.
+
+ .. versionchanged:: 3.3
+ :exc:`SSLError` used to be a subtype of :exc:`socket.error`.
+
+.. exception:: SSLZeroReturnError
+
+ A subclass of :exc:`SSLError` raised when trying to read or write and
+ the SSL connection has been closed cleanly. Note that this doesn't
+ mean that the underlying transport (read TCP) has been closed.
+
+ .. versionadded:: 3.3
+
+.. exception:: SSLWantReadError
+
+ A subclass of :exc:`SSLError` raised by a :ref:`non-blocking SSL socket
+ <ssl-nonblocking>` when trying to read or write data, but more data needs
+ to be received on the underlying TCP transport before the request can be
+ fulfilled.
+
+ .. versionadded:: 3.3
+
+.. exception:: SSLWantWriteError
+
+ A subclass of :exc:`SSLError` raised by a :ref:`non-blocking SSL socket
+ <ssl-nonblocking>` when trying to read or write data, but more data needs
+ to be sent on the underlying TCP transport before the request can be
+ fulfilled.
+
+ .. versionadded:: 3.3
+
+.. exception:: SSLSyscallError
+
+ A subclass of :exc:`SSLError` raised when a system error was encountered
+ while trying to fulfill an operation on a SSL socket. Unfortunately,
+ there is no easy way to inspect the original errno number.
+
+ .. versionadded:: 3.3
+
+.. exception:: SSLEOFError
+
+ A subclass of :exc:`SSLError` raised when the SSL connection has been
+ terminated abruptly. Generally, you shouldn't try to reuse the underlying
+ transport when this error is encountered.
+
+ .. versionadded:: 3.3
.. exception:: CertificateError
@@ -162,6 +206,35 @@ instead.
Random generation
^^^^^^^^^^^^^^^^^
+.. function:: RAND_bytes(num)
+
+ Returns *num* cryptographically strong pseudo-random bytes. Raises an
+ :class:`SSLError` if the PRNG has not been seeded with enough data or if the
+ operation is not supported by the current RAND method. :func:`RAND_status`
+ can be used to check the status of the PRNG and :func:`RAND_add` can be used
+ to seed the PRNG.
+
+ Read the Wikipedia article, `Cryptographically secure pseudorandom number
+ generator (CSPRNG)
+ <http://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator>`_,
+ to get the requirements of a cryptographically generator.
+
+ .. versionadded:: 3.3
+
+.. function:: RAND_pseudo_bytes(num)
+
+ Returns (bytes, is_cryptographic): bytes are *num* pseudo-random bytes,
+ is_cryptographic is True if the bytes generated are cryptographically
+ strong. Raises an :class:`SSLError` if the operation is not supported by the
+ current RAND method.
+
+ Generated pseudo-random byte sequences will be unique if they are of
+ sufficient length, but are not necessarily unpredictable. They can be used
+ for non-cryptographic purposes and for certain purposes in cryptographic
+ protocols, but usually not for key generation etc.
+
+ .. versionadded:: 3.3
+
.. function:: RAND_status()
Returns True if the SSL pseudo-random number generator has been seeded with
@@ -171,7 +244,7 @@ Random generation
.. function:: RAND_egd(path)
- If you are running an entropy-gathering daemon (EGD) somewhere, and ``path``
+ If you are running an entropy-gathering daemon (EGD) somewhere, and *path*
is the pathname of a socket connection open to it, this will read 256 bytes
of randomness from the socket, and add it to the SSL pseudo-random number
generator to increase the security of generated secret keys. This is
@@ -182,8 +255,8 @@ Random generation
.. function:: RAND_add(bytes, entropy)
- Mixes the given ``bytes`` into the SSL pseudo-random number generator. The
- parameter ``entropy`` (a float) is a lower bound on the entropy contained in
+ Mixes the given *bytes* into the SSL pseudo-random number generator. The
+ parameter *entropy* (a float) is a lower bound on the entropy contained in
string (so you can always use :const:`0.0`). See :rfc:`1750` for more
information on sources of entropy.
@@ -239,6 +312,9 @@ Certificate handling
will attempt to validate the server certificate against that set of root
certificates, and will fail if the validation attempt fails.
+ .. versionchanged:: 3.3
+ This function is now IPv6-compatible.
+
.. function:: DER_cert_to_PEM_cert(DER_cert_bytes)
Given a certificate as a DER-encoded blob of bytes, returns a PEM-encoded
@@ -345,6 +421,13 @@ Constants
.. versionadded:: 3.2
+.. data:: OP_CIPHER_SERVER_PREFERENCE
+
+ Use the server's cipher ordering preference, rather than the client's.
+ This option has no effect on client sockets and SSLv2 server sockets.
+
+ .. versionadded:: 3.3
+
.. data:: HAS_SNI
Whether the OpenSSL library has built-in support for the *Server Name
@@ -354,6 +437,13 @@ Constants
.. versionadded:: 3.2
+.. data:: CHANNEL_BINDING_TYPES
+
+ List of supported TLS channel binding types. Strings in this list
+ can be used as arguments to :meth:`SSLSocket.get_channel_binding`.
+
+ .. versionadded:: 3.3
+
.. data:: OPENSSL_VERSION
The version string of the OpenSSL library loaded by the interpreter::
@@ -463,6 +553,18 @@ SSL sockets also have the following additional methods and attributes:
version of the SSL protocol that defines its use, and the number of secret
bits being used. If no connection has been established, returns ``None``.
+.. method:: SSLSocket.get_channel_binding(cb_type="tls-unique")
+
+ Get channel binding data for current connection, as a bytes object. Returns
+ ``None`` if not connected or the handshake has not been completed.
+
+ The *cb_type* parameter allow selection of the desired channel binding
+ type. Valid channel binding types are listed in the
+ :data:`CHANNEL_BINDING_TYPES` list. Currently only the 'tls-unique' channel
+ binding, defined by :rfc:`5929`, is supported. :exc:`ValueError` will be
+ raised if an unsupported channel binding type is requested.
+
+ .. versionadded:: 3.3
.. method:: SSLSocket.unwrap()
@@ -502,7 +604,7 @@ to speed up repeated connections from the same clients.
:class:`SSLContext` objects have the following methods and attributes:
-.. method:: SSLContext.load_cert_chain(certfile, keyfile=None)
+.. method:: SSLContext.load_cert_chain(certfile, keyfile=None, password=None)
Load a private key and the corresponding certificate. The *certfile*
string must be the path to a single file in PEM format containing the
@@ -513,9 +615,25 @@ to speed up repeated connections from the same clients.
:ref:`ssl-certificates` for more information on how the certificate
is stored in the *certfile*.
+ The *password* argument may be a function to call to get the password for
+ decrypting the private key. It will only be called if the private key is
+ encrypted and a password is necessary. It will be called with no arguments,
+ and it should return a string, bytes, or bytearray. If the return value is
+ a string it will be encoded as UTF-8 before using it to decrypt the key.
+ Alternatively a string, bytes, or bytearray value may be supplied directly
+ as the *password* argument. It will be ignored if the private key is not
+ encrypted and no password is needed.
+
+ If the *password* argument is not specified and a password is required,
+ OpenSSL's built-in password prompting mechanism will be used to
+ interactively prompt the user for a password.
+
An :class:`SSLError` is raised if the private key doesn't
match with the certificate.
+ .. versionchanged:: 3.3
+ New optional argument *password*.
+
.. method:: SSLContext.load_verify_locations(cafile=None, capath=None)
Load a set of "certification authority" (CA) certificates used to validate
@@ -933,13 +1051,10 @@ to be aware of:
try:
sock.do_handshake()
break
- except ssl.SSLError as err:
- if err.args[0] == ssl.SSL_ERROR_WANT_READ:
- select.select([sock], [], [])
- elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE:
- select.select([], [sock], [])
- else:
- raise
+ except ssl.SSLWantReadError:
+ select.select([sock], [], [])
+ except ssl.SSLWantWriteError:
+ select.select([], [sock], [])
.. _ssl-security:
diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst
index ae0793b..49222f5 100644
--- a/Doc/library/stdtypes.rst
+++ b/Doc/library/stdtypes.rst
@@ -15,6 +15,10 @@ interpreter.
The principal built-in types are numerics, sequences, mappings, classes,
instances and exceptions.
+Some collection classes are mutable. The methods that add, subtract, or
+rearrange their members in place, and don't return a specific item, never return
+the collection instance itself but ``None``.
+
Some operations are supported by several object types; in particular,
practically all objects can be compared, tested for truth value, and converted
to a string (with the :func:`repr` function or the slightly different
@@ -1667,6 +1671,8 @@ Note that while lists allow their items to be of any type, bytearray object
single: append() (sequence method)
single: extend() (sequence method)
single: count() (sequence method)
+ single: clear() (sequence method)
+ single: copy() (sequence method)
single: index() (sequence method)
single: insert() (sequence method)
single: pop() (sequence method)
@@ -1698,6 +1704,12 @@ Note that while lists allow their items to be of any type, bytearray object
| ``s.extend(x)`` | same as ``s[len(s):len(s)] = | \(2) |
| | x`` | |
+------------------------------+--------------------------------+---------------------+
+| ``s.clear()`` | remove all items from ``s`` | |
+| | | |
++------------------------------+--------------------------------+---------------------+
+| ``s.copy()`` | return a shallow copy of ``s`` | |
+| | | |
++------------------------------+--------------------------------+---------------------+
| ``s.count(x)`` | return number of *i*'s for | |
| | which ``s[i] == x`` | |
+------------------------------+--------------------------------+---------------------+
@@ -1776,6 +1788,9 @@ Notes:
(8)
:meth:`sort` is not supported by :class:`bytearray` objects.
+ .. versionadded:: 3.3
+ :meth:`clear` and :meth:`!copy` methods.
+
.. _bytes-methods:
@@ -1793,6 +1808,12 @@ the objects to strings, they have a :func:`decode` method.
Wherever one of these methods needs to interpret the bytes as characters
(e.g. the :func:`is...` methods), the ASCII character set is assumed.
+.. versionadded:: 3.3
+ The functions :func:`count`, :func:`find`, :func:`index`,
+ :func:`rfind` and :func:`rindex` have additional semantics compared to
+ the corresponding string functions: They also accept an integer in
+ range 0 to 255 (a byte) as their first argument.
+
.. note::
The methods on bytes and bytearray objects don't accept strings as their
@@ -2383,6 +2404,19 @@ copying. Memory is generally interpreted as simple bytes.
Notice how the size of the memoryview object cannot be changed.
+ Memoryviews of hashable (read-only) types are also hashable and their
+ hash value matches the corresponding bytes object::
+
+ >>> v = memoryview(b'abcefg')
+ >>> hash(v) == hash(b'abcefg')
+ True
+ >>> hash(v[2:4]) == hash(b'ce')
+ True
+
+ .. versionchanged:: 3.3
+ Memoryview objects are now hashable.
+
+
:class:`memoryview` has several methods:
.. method:: tobytes()
@@ -2698,7 +2732,7 @@ The Null Object
This object is returned by functions that don't explicitly return a value. It
supports no special operations. There is exactly one null object, named
-``None`` (a built-in name).
+``None`` (a built-in name). ``type(None)()`` produces the same singleton.
It is written as ``None``.
@@ -2710,7 +2744,8 @@ The Ellipsis Object
This object is commonly used by slicing (see :ref:`slicings`). It supports no
special operations. There is exactly one ellipsis object, named
-:const:`Ellipsis` (a built-in name).
+:const:`Ellipsis` (a built-in name). ``type(Ellipsis)()`` produces the
+:const:`Ellipsis` singleton.
It is written as ``Ellipsis`` or ``...``.
@@ -2722,7 +2757,8 @@ The NotImplemented Object
This object is returned from comparisons and binary operations when they are
asked to operate on types they don't support. See :ref:`comparisons` for more
-information.
+information. There is exactly one ``NotImplemented`` object.
+``type(NotImplemented)()`` produces the singleton instance.
It is written as ``NotImplemented``.
@@ -2788,6 +2824,13 @@ types, where they are relevant. Some of these are not reported by the
The name of the class or type.
+.. attribute:: class.__qualname__
+
+ The :term:`qualified name` of the class or type.
+
+ .. versionadded:: 3.3
+
+
.. attribute:: class.__mro__
This attribute is a tuple of classes that are considered when looking for
diff --git a/Doc/library/struct.rst b/Doc/library/struct.rst
index 12820e0..994506c 100644
--- a/Doc/library/struct.rst
+++ b/Doc/library/struct.rst
@@ -187,17 +187,24 @@ platform-dependent.
| ``Q`` | :c:type:`unsigned long | integer | 8 | \(2), \(3) |
| | long` | | | |
+--------+--------------------------+--------------------+----------------+------------+
-| ``f`` | :c:type:`float` | float | 4 | \(4) |
+| ``n`` | :c:type:`ssize_t` | integer | | \(4) |
+--------+--------------------------+--------------------+----------------+------------+
-| ``d`` | :c:type:`double` | float | 8 | \(4) |
+| ``N`` | :c:type:`size_t` | integer | | \(4) |
++--------+--------------------------+--------------------+----------------+------------+
+| ``f`` | :c:type:`float` | float | 4 | \(5) |
++--------+--------------------------+--------------------+----------------+------------+
+| ``d`` | :c:type:`double` | float | 8 | \(5) |
+--------+--------------------------+--------------------+----------------+------------+
| ``s`` | :c:type:`char[]` | bytes | | |
+--------+--------------------------+--------------------+----------------+------------+
| ``p`` | :c:type:`char[]` | bytes | | |
+--------+--------------------------+--------------------+----------------+------------+
-| ``P`` | :c:type:`void \*` | integer | | \(5) |
+| ``P`` | :c:type:`void \*` | integer | | \(6) |
+--------+--------------------------+--------------------+----------------+------------+
+.. versionchanged:: 3.3
+ Added support for the ``'n'`` and ``'N'`` formats.
+
Notes:
(1)
@@ -219,11 +226,17 @@ Notes:
Use of the :meth:`__index__` method for non-integers is new in 3.2.
(4)
+ The ``'n'`` and ``'N'`` conversion codes are only available for the native
+ size (selected as the default or with the ``'@'`` byte order character).
+ For the standard size, you can use whichever of the other integer formats
+ fits your application.
+
+(5)
For the ``'f'`` and ``'d'`` conversion codes, the packed representation uses
the IEEE 754 binary32 (for ``'f'``) or binary64 (for ``'d'``) format,
regardless of the floating-point format used by the platform.
-(5)
+(6)
The ``'P'`` format character is only available for the native byte ordering
(selected as the default or with the ``'@'`` byte order character). The byte
order character ``'='`` chooses to use little- or big-endian ordering based
diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst
index 19a29cb..fa91c0f 100644
--- a/Doc/library/subprocess.rst
+++ b/Doc/library/subprocess.rst
@@ -30,16 +30,21 @@ convenience functions for all use cases they can handle. For more advanced
use cases, the underlying :class:`Popen` interface can be used directly.
-.. function:: call(args, *, stdin=None, stdout=None, stderr=None, shell=False)
+.. function:: call(args, *, stdin=None, stdout=None, stderr=None, shell=False, timeout=None)
Run the command described by *args*. Wait for command to complete, then
return the :attr:`returncode` attribute.
The arguments shown above are merely the most common ones, described below
- in :ref:`frequently-used-arguments` (hence the slightly odd notation in
- the abbreviated signature). The full function signature is the same as
- that of the :class:`Popen` constructor - this functions passes all
- supplied arguments directly through to that interface.
+ in :ref:`frequently-used-arguments` (hence the use of keyword-only notation
+ in the abbreviated signature). The full function signature is largely the
+ same as that of the :class:`Popen` constructor - this function passes all
+ supplied arguments other than *timeout* directly through to that interface.
+
+ The *timeout* argument is passed to :meth:`Popen.wait`. If the timeout
+ expires, the child process will be killed and then waited for again. The
+ :exc:`TimeoutExpired` exception will be re-raised after the child process
+ has terminated.
Examples::
@@ -62,8 +67,11 @@ use cases, the underlying :class:`Popen` interface can be used directly.
process may block if it generates enough output to a pipe to fill up
the OS pipe buffer.
+ .. versionchanged:: 3.3
+ *timeout* was added.
+
-.. function:: check_call(args, *, stdin=None, stdout=None, stderr=None, shell=False)
+.. function:: check_call(args, *, stdin=None, stdout=None, stderr=None, shell=False, timeout=None)
Run command with arguments. Wait for command to complete. If the return
code was zero then return, otherwise raise :exc:`CalledProcessError`. The
@@ -71,10 +79,15 @@ use cases, the underlying :class:`Popen` interface can be used directly.
:attr:`returncode` attribute.
The arguments shown above are merely the most common ones, described below
- in :ref:`frequently-used-arguments` (hence the slightly odd notation in
- the abbreviated signature). The full function signature is the same as
- that of the :class:`Popen` constructor - this functions passes all
- supplied arguments directly through to that interface.
+ in :ref:`frequently-used-arguments` (hence the use of keyword-only notation
+ in the abbreviated signature). The full function signature is largely the
+ same as that of the :class:`Popen` constructor - this function passes all
+ supplied arguments other than *timeout* directly through to that interface.
+
+ The *timeout* argument is passed to :meth:`Popen.wait`. If the timeout
+ expires, the child process will be killed and then waited for again. The
+ :exc:`TimeoutExpired` exception will be re-raised after the child process
+ has terminated.
Examples::
@@ -86,8 +99,6 @@ use cases, the underlying :class:`Popen` interface can be used directly.
...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1
- .. versionadded:: 2.5
-
.. warning::
Invoking the system shell with ``shell=True`` can be a security hazard
@@ -101,8 +112,11 @@ use cases, the underlying :class:`Popen` interface can be used directly.
process may block if it generates enough output to a pipe to fill up
the OS pipe buffer.
+ .. versionchanged:: 3.3
+ *timeout* was added.
+
-.. function:: check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False)
+.. function:: check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False, timeout=None)
Run command with arguments and return its output as a byte string.
@@ -112,11 +126,17 @@ use cases, the underlying :class:`Popen` interface can be used directly.
attribute.
The arguments shown above are merely the most common ones, described below
- in :ref:`frequently-used-arguments` (hence the slightly odd notation in
- the abbreviated signature). The full function signature is largely the
- same as that of the :class:`Popen` constructor, except that *stdout* is
- not permitted as it is used internally. All other supplied arguments are
- passed directly through to the :class:`Popen` constructor.
+ in :ref:`frequently-used-arguments` (hence the use of keyword-only notation
+ in the abbreviated signature). The full function signature is largely the
+ same as that of the :class:`Popen` constructor - this functions passes all
+ supplied arguments other than *timeout* directly through to that interface.
+ In addition, *stdout* is not permitted as an argument, as it is used
+ internally to collect the output from the subprocess.
+
+ The *timeout* argument is passed to :meth:`Popen.wait`. If the timeout
+ expires, the child process will be killed and then waited for again. The
+ :exc:`TimeoutExpired` exception will be re-raised after the child process
+ has terminated.
Examples::
@@ -147,7 +167,7 @@ use cases, the underlying :class:`Popen` interface can be used directly.
... shell=True)
'ls: non_existent_file: No such file or directory\n'
- .. versionadded:: 2.7
+ .. versionadded:: 3.1
.. warning::
@@ -161,6 +181,18 @@ use cases, the underlying :class:`Popen` interface can be used directly.
read in the current process, the child process may block if it
generates enough output to the pipe to fill up the OS pipe buffer.
+ .. versionchanged:: 3.3
+ *timeout* was added.
+
+
+.. data:: DEVNULL
+
+ Special value that can be used as the *stdin*, *stdout* or *stderr* argument
+ to :class:`Popen` and indicates that the special file :data:`os.devnull`
+ will be used.
+
+ .. versionadded:: 3.3
+
.. data:: PIPE
@@ -196,13 +228,14 @@ default values. The arguments that are most commonly needed are:
*stdin*, *stdout* and *stderr* specify the executed program's standard input,
standard output and standard error file handles, respectively. Valid values
- are :data:`PIPE`, an existing file descriptor (a positive integer), an
- existing file object, and ``None``. :data:`PIPE` indicates that a new pipe
- to the child should be created. With the default settings of ``None``, no
- redirection will occur; the child's file handles will be inherited from the
- parent. Additionally, *stderr* can be :data:`STDOUT`, which indicates that
- the stderr data from the child process should be captured into the same file
- handle as for stdout.
+ are :data:`PIPE`, :data:`DEVNULL`, an existing file descriptor (a positive
+ integer), an existing file object, and ``None``. :data:`PIPE` indicates
+ that a new pipe to the child should be created. :data:`DEVNULL` indicates
+ that the special file :data:`os.devnull` will be used. With the default
+ settings of ``None``, no redirection will occur; the child's file handles
+ will be inherited from the parent. Additionally, *stderr* can be
+ :data:`STDOUT`, which indicates that the stderr data from the child
+ process should be captured into the same file handle as for *stdout*.
When *stdout* or *stderr* are pipes and *universal_newlines* is
:const:`True` then the output data is assumed to be encoded as UTF-8 and
@@ -331,13 +364,14 @@ functions.
*stdin*, *stdout* and *stderr* specify the executed program's standard input,
standard output and standard error file handles, respectively. Valid values
- are :data:`PIPE`, an existing file descriptor (a positive integer), an
- existing :term:`file object`, and ``None``. :data:`PIPE` indicates that a
- new pipe to the child should be created. With the default settings of
- ``None``, no redirection will occur; the child's file handles will be
- inherited from the parent. Additionally, *stderr* can be :data:`STDOUT`,
- which indicates that the stderr data from the applications should be
- captured into the same file handle as for stdout.
+ are :data:`PIPE`, :data:`DEVNULL`, an existing file descriptor (a positive
+ integer), an existing :term:`file object`, and ``None``. :data:`PIPE`
+ indicates that a new pipe to the child should be created. :data:`DEVNULL`
+ indicates that the special file :data:`os.devnull` will be used. With the
+ default settings of ``None``, no redirection will occur; the child's file
+ handles will be inherited from the parent. Additionally, *stderr* can be
+ :data:`STDOUT`, which indicates that the stderr data from the applications
+ should be captured into the same file handle as for stdout.
If *preexec_fn* is set to a callable object, this object will be called in the
child process just before the child is executed.
@@ -456,6 +490,15 @@ arguments.
:exc:`CalledProcessError` if the called process returns a non-zero return
code.
+All of the functions and methods that accept a *timeout* parameter, such as
+:func:`call` and :meth:`Popen.communicate` will raise :exc:`TimeoutExpired` if
+the timeout expires before the process exits.
+
+Exceptions defined in this module all inherit from :exc:`SubprocessError`.
+
+ .. versionadded:: 3.3
+ The :exc:`SubprocessError` base class was added.
+
Security
^^^^^^^^
@@ -479,11 +522,15 @@ Instances of the :class:`Popen` class have the following methods:
attribute.
-.. method:: Popen.wait()
+.. method:: Popen.wait(timeout=None)
Wait for child process to terminate. Set and return :attr:`returncode`
attribute.
+ If the process does not terminate after *timeout* seconds, raise a
+ :exc:`TimeoutExpired` exception. It is safe to catch this exception and
+ retry the wait.
+
.. warning::
This will deadlock when using ``stdout=PIPE`` and/or
@@ -491,13 +538,17 @@ Instances of the :class:`Popen` class have the following methods:
a pipe such that it blocks waiting for the OS pipe buffer to
accept more data. Use :meth:`communicate` to avoid that.
+ .. versionchanged:: 3.3
+ *timeout* was added.
-.. method:: Popen.communicate(input=None)
+
+.. method:: Popen.communicate(input=None, timeout=None)
Interact with process: Send data to stdin. Read data from stdout and stderr,
- until end-of-file is reached. Wait for process to terminate. The optional
- *input* argument should be a byte string to be sent to the child process, or
- ``None``, if no data should be sent to the child.
+ until end-of-file is reached. Wait for process to terminate. The optional
+ *input* argument should be data to be sent to the child process, or
+ ``None``, if no data should be sent to the child. The type of *input*
+ must be bytes or, if *universal_newlines* was ``True``, a string.
:meth:`communicate` returns a tuple ``(stdoutdata, stderrdata)``.
@@ -506,11 +557,29 @@ Instances of the :class:`Popen` class have the following methods:
``None`` in the result tuple, you need to give ``stdout=PIPE`` and/or
``stderr=PIPE`` too.
+ If the process does not terminate after *timeout* seconds, a
+ :exc:`TimeoutExpired` exception will be raised. Catching this exception and
+ retrying communication will not lose any output.
+
+ The child process is not killed if the timeout expires, so in order to
+ cleanup properly a well-behaved application should kill the child process and
+ finish communication::
+
+ proc = subprocess.Popen(...)
+ try:
+ outs, errs = proc.communicate(timeout=15)
+ except TimeoutExpired:
+ proc.kill()
+ outs, errs = proc.communicate()
+
.. note::
The data read is buffered in memory, so do not use this method if the data
size is large or unlimited.
+ .. versionchanged:: 3.3
+ *timeout* was added.
+
.. method:: Popen.send_signal(signal)
@@ -952,3 +1021,9 @@ runtime):
backslash. If the number of backslashes is odd, the last
backslash escapes the next double quotation mark as
described in rule 3.
+
+
+.. seealso::
+
+ :mod:`shlex`
+ Module which provides function to parse and escape command lines.
diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst
index 18f916b..72414f6 100644
--- a/Doc/library/sys.rst
+++ b/Doc/library/sys.rst
@@ -233,14 +233,13 @@ always available.
.. data:: flags
- The struct sequence *flags* exposes the status of command line flags. The
- attributes are read only.
+ The :term:`struct sequence` *flags* exposes the status of command line
+ flags. The attributes are read only.
============================= =============================
attribute flag
============================= =============================
:const:`debug` :option:`-d`
- :const:`division_warning` :option:`-Q`
:const:`inspect` :option:`-i`
:const:`interactive` :option:`-i`
:const:`optimize` :option:`-O` or :option:`-OO`
@@ -256,15 +255,18 @@ always available.
.. versionchanged:: 3.2
Added ``quiet`` attribute for the new :option:`-q` flag.
+ .. versionchanged:: 3.3
+ Removed obsolete ``division_warning`` attribute.
+
.. data:: float_info
- A structseq holding information about the float type. It contains low level
- information about the precision and internal representation. The values
- correspond to the various floating-point constants defined in the standard
- header file :file:`float.h` for the 'C' programming language; see section
- 5.2.4.2.2 of the 1999 ISO/IEC C standard [C99]_, 'Characteristics of
- floating types', for details.
+ A :term:`struct sequence` holding information about the float type. It
+ contains low level information about the precision and internal
+ representation. The values correspond to the various floating-point
+ constants defined in the standard header file :file:`float.h` for the 'C'
+ programming language; see section 5.2.4.2.2 of the 1999 ISO/IEC C standard
+ [C99]_, 'Characteristics of floating types', for details.
+---------------------+----------------+--------------------------------------------------+
| attribute | float.h macro | explanation |
@@ -514,8 +516,9 @@ always available.
.. data:: hash_info
- A structseq giving parameters of the numeric hash implementation. For
- more details about hashing of numeric types, see :ref:`numeric-hash`.
+ A :term:`struct sequence` giving parameters of the numeric hash
+ implementation. For more details about hashing of numeric types, see
+ :ref:`numeric-hash`.
+---------------------+--------------------------------------------------+
| attribute | explanation |
@@ -550,8 +553,8 @@ always available.
This is called ``hexversion`` since it only really looks meaningful when viewed
as the result of passing it to the built-in :func:`hex` function. The
- struct sequence :data:`sys.version_info` may be used for a more human-friendly
- encoding of the same information.
+ :term:`struct sequence` :data:`sys.version_info` may be used for a more
+ human-friendly encoding of the same information.
The ``hexversion`` is a 32-bit number with the following layout:
@@ -579,8 +582,8 @@ always available.
.. data:: int_info
- A struct sequence that holds information about Python's
- internal representation of integers. The attributes are read only.
+ A :term:`struct sequence` that holds information about Python's internal
+ representation of integers. The attributes are read only.
+-------------------------+----------------------------------------------+
| Attribute | Explanation |
@@ -635,9 +638,13 @@ always available.
.. data:: maxunicode
- An integer giving the largest supported code point for a Unicode character. The
- value of this depends on the configuration option that specifies whether Unicode
- characters are stored as UCS-2 or UCS-4.
+ An integer giving the value of the largest Unicode code point,
+ i.e. ``1114111`` (``0x10FFFF`` in hexadecimal).
+
+ .. versionchanged:: 3.3
+ Before :pep:`393`, ``sys.maxunicode`` used to be either ``0xFFFF``
+ or ``0x10FFFF``, depending on the configuration option that specified
+ whether Unicode characters were stored as UCS-2 or UCS-4.
.. data:: meta_path
@@ -712,36 +719,35 @@ always available.
This string contains a platform identifier that can be used to append
platform-specific components to :data:`sys.path`, for instance.
- For most Unix systems, this is the lowercased OS name as returned by ``uname
- -s`` with the first part of the version as returned by ``uname -r`` appended,
- e.g. ``'sunos5'``, *at the time when Python was built*. Unless you want to
- test for a specific system version, it is therefore recommended to use the
- following idiom::
+ For Unix systems, except on Linux, this is the lowercased OS name as
+ returned by ``uname -s`` with the first part of the version as returned by
+ ``uname -r`` appended, e.g. ``'sunos5'`` or ``'freebsd8'``, *at the time
+ when Python was built*. Unless you want to test for a specific system
+ version, it is therefore recommended to use the following idiom::
if sys.platform.startswith('freebsd'):
# FreeBSD-specific code here...
elif sys.platform.startswith('linux'):
# Linux-specific code here...
- .. versionchanged:: 3.2.2
- Since lots of code check for ``sys.platform == 'linux2'``, and there is
- no essential change between Linux 2.x and 3.x, ``sys.platform`` is always
- set to ``'linux2'``, even on Linux 3.x. In Python 3.3 and later, the
- value will always be set to ``'linux'``, so it is recommended to always
- use the ``startswith`` idiom presented above.
-
For other systems, the values are:
- ====================== ===========================
- System :data:`platform` value
- ====================== ===========================
- Linux (2.x *and* 3.x) ``'linux2'``
- Windows ``'win32'``
- Windows/Cygwin ``'cygwin'``
- Mac OS X ``'darwin'``
- OS/2 ``'os2'``
- OS/2 EMX ``'os2emx'``
- ====================== ===========================
+ ================ ===========================
+ System :data:`platform` value
+ ================ ===========================
+ Linux ``'linux'``
+ Windows ``'win32'``
+ Windows/Cygwin ``'cygwin'``
+ Mac OS X ``'darwin'``
+ OS/2 ``'os2'``
+ OS/2 EMX ``'os2emx'``
+ ================ ===========================
+
+ .. versionchanged:: 3.3
+ On Linux, :attr:`sys.platform` doesn't contain the major version anymore.
+ It is always ``'linux'``, instead of ``'linux2'`` or ``'linux3'``. Since
+ older Python versions include the version number, it is recommended to
+ always use the ``startswith`` idiom presented above.
.. seealso::
:attr:`os.name` has a coarser granularity. :func:`os.uname` gives
@@ -750,6 +756,7 @@ always available.
The :mod:`platform` module provides detailed checks for the
system's identity.
+
.. data:: prefix
A string giving the site-specific directory prefix where the platform
@@ -798,11 +805,11 @@ always available.
the interpreter loads extension modules. Among other things, this will enable a
lazy resolving of symbols when importing a module, if called as
``sys.setdlopenflags(0)``. To share symbols across extension modules, call as
- ``sys.setdlopenflags(ctypes.RTLD_GLOBAL)``. Symbolic names for the
- flag modules can be either found in the :mod:`ctypes` module, or in the :mod:`DLFCN`
- module. If :mod:`DLFCN` is not available, it can be generated from
- :file:`/usr/include/dlfcn.h` using the :program:`h2py` script. Availability:
- Unix.
+ ``sys.setdlopenflags(os.RTLD_GLOBAL)``. Symbolic names for the flag modules
+ can be found in the :mod:`os` module (``RTLD_xxx`` constants, e.g.
+ :data:`os.RTLD_LAZY`).
+
+ Availability: Unix.
.. function:: setprofile(profilefunc)
@@ -995,22 +1002,33 @@ always available.
to a console and Python apps started with :program:`pythonw`.
-.. data:: subversion
+.. data:: thread_info
- A triple (repo, branch, version) representing the Subversion information of the
- Python interpreter. *repo* is the name of the repository, ``'CPython'``.
- *branch* is a string of one of the forms ``'trunk'``, ``'branches/name'`` or
- ``'tags/name'``. *version* is the output of ``svnversion``, if the interpreter
- was built from a Subversion checkout; it contains the revision number (range)
- and possibly a trailing 'M' if there were local modifications. If the tree was
- exported (or svnversion was not available), it is the revision of
- ``Include/patchlevel.h`` if the branch is a tag. Otherwise, it is ``None``.
+ A :term:`struct sequence` holding information about the thread
+ implementation.
- .. deprecated:: 3.2.1
- Python is now `developed <http://docs.python.org/devguide/>`_ using
- Mercurial. In recent Python 3.2 bugfix releases, :data:`subversion`
- therefore contains placeholder information. It is removed in Python
- 3.3.
+ +------------------+---------------------------------------------------------+
+ | Attribute | Explanation |
+ +==================+=========================================================+
+ | :const:`name` | Name of the thread implementation: |
+ | | |
+ | | * ``'nt'``: Windows threads |
+ | | * ``'os2'``: OS/2 threads |
+ | | * ``'pthread'``: POSIX threads |
+ | | * ``'solaris'``: Solaris threads |
+ +------------------+---------------------------------------------------------+
+ | :const:`lock` | Name of the lock implementation: |
+ | | |
+ | | * ``'semaphore'``: a lock uses a semaphore |
+ | | * ``'mutex+cond'``: a lock uses a mutex |
+ | | and a condition variable |
+ | | * ``None`` if this information is unknown |
+ +------------------+---------------------------------------------------------+
+ | :const:`version` | Name and version of the thread library. It is a string, |
+ | | or ``None`` if these informations are unknown. |
+ +------------------+---------------------------------------------------------+
+
+ .. versionadded:: 3.3
.. data:: tracebacklimit
diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst
index b56058b..4a9db8e 100644
--- a/Doc/library/tarfile.rst
+++ b/Doc/library/tarfile.rst
@@ -13,12 +13,12 @@
--------------
The :mod:`tarfile` module makes it possible to read and write tar
-archives, including those using gzip or bz2 compression.
+archives, including those using gzip, bz2 and lzma compression.
(:file:`.zip` files can be read and written using the :mod:`zipfile` module.)
Some facts and figures:
-* reads and writes :mod:`gzip` and :mod:`bz2` compressed archives.
+* reads and writes :mod:`gzip`, :mod:`bz2` and :mod:`lzma` compressed archives.
* read/write support for the POSIX.1-1988 (ustar) format.
@@ -32,6 +32,9 @@ Some facts and figures:
character devices and block devices and is able to acquire and restore file
information like timestamp, access permissions and owner.
+.. versionchanged:: 3.3
+ Added support for :mod:`lzma` compression.
+
.. function:: open(name=None, mode='r', fileobj=None, bufsize=10240, \*\*kwargs)
@@ -55,6 +58,8 @@ Some facts and figures:
+------------------+---------------------------------------------+
| ``'r:bz2'`` | Open for reading with bzip2 compression. |
+------------------+---------------------------------------------+
+ | ``'r:xz'`` | Open for reading with lzma compression. |
+ +------------------+---------------------------------------------+
| ``'a' or 'a:'`` | Open for appending with no compression. The |
| | file is created if it does not exist. |
+------------------+---------------------------------------------+
@@ -64,11 +69,13 @@ Some facts and figures:
+------------------+---------------------------------------------+
| ``'w:bz2'`` | Open for bzip2 compressed writing. |
+------------------+---------------------------------------------+
+ | ``'w:xz'`` | Open for lzma compressed writing. |
+ +------------------+---------------------------------------------+
- Note that ``'a:gz'`` or ``'a:bz2'`` is not possible. If *mode* is not suitable
- to open a certain (compressed) file for reading, :exc:`ReadError` is raised. Use
- *mode* ``'r'`` to avoid this. If a compression method is not supported,
- :exc:`CompressionError` is raised.
+ Note that ``'a:gz'``, ``'a:bz2'`` or ``'a:xz'`` is not possible. If *mode*
+ is not suitable to open a certain (compressed) file for reading,
+ :exc:`ReadError` is raised. Use *mode* ``'r'`` to avoid this. If a
+ compression method is not supported, :exc:`CompressionError` is raised.
If *fileobj* is specified, it is used as an alternative to a :term:`file object`
opened in binary mode for *name*. It is supposed to be at position 0.
@@ -99,6 +106,9 @@ Some facts and figures:
| ``'r|bz2'`` | Open a bzip2 compressed *stream* for |
| | reading. |
+-------------+--------------------------------------------+
+ | ``'r|xz'`` | Open a lzma compressed *stream* for |
+ | | reading. |
+ +-------------+--------------------------------------------+
| ``'w|'`` | Open an uncompressed *stream* for writing. |
+-------------+--------------------------------------------+
| ``'w|gz'`` | Open a gzip compressed *stream* for |
@@ -107,6 +117,9 @@ Some facts and figures:
| ``'w|bz2'`` | Open a bzip2 compressed *stream* for |
| | writing. |
+-------------+--------------------------------------------+
+ | ``'w|xz'`` | Open an lzma compressed *stream* for |
+ | | writing. |
+ +-------------+--------------------------------------------+
.. class:: TarFile
@@ -262,9 +275,9 @@ be finalized; only the internally used file object will be closed. See the
If *errorlevel* is ``0``, all errors are ignored when using :meth:`TarFile.extract`.
Nevertheless, they appear as error messages in the debug output, when debugging
- is enabled. If ``1``, all *fatal* errors are raised as :exc:`OSError` or
- :exc:`IOError` exceptions. If ``2``, all *non-fatal* errors are raised as
- :exc:`TarError` exceptions as well.
+ is enabled. If ``1``, all *fatal* errors are raised as :exc:`OSError`
+ exceptions. If ``2``, all *non-fatal* errors are raised as :exc:`TarError`
+ exceptions as well.
The *encoding* and *errors* arguments define the character encoding to be
used for reading or writing the archive and how conversion errors are going
diff --git a/Doc/library/telnetlib.rst b/Doc/library/telnetlib.rst
index 646634d..9bc79c5 100644
--- a/Doc/library/telnetlib.rst
+++ b/Doc/library/telnetlib.rst
@@ -162,9 +162,13 @@ Telnet Objects
.. method:: Telnet.write(buffer)
Write a byte string to the socket, doubling any IAC characters. This can
- block if the connection is blocked. May raise :exc:`socket.error` if the
+ block if the connection is blocked. May raise :exc:`OSError` if the
connection is closed.
+ .. versionchanged:: 3.3
+ This method used to raise :exc:`socket.error`, which is now an alias
+ of :exc:`OSError`.
+
.. method:: Telnet.interact()
diff --git a/Doc/library/tempfile.rst b/Doc/library/tempfile.rst
index fff6c4e..dfeb250 100644
--- a/Doc/library/tempfile.rst
+++ b/Doc/library/tempfile.rst
@@ -25,7 +25,7 @@ instead a string of six random characters is used.
Also, all the user-callable functions now take additional arguments which
allow direct control over the location and name of temporary files. It is
-no longer necessary to use the global *tempdir* and *template* variables.
+no longer necessary to use the global *tempdir* variable.
To maintain backward compatibility, the argument order is somewhat odd; it
is recommended to use keyword arguments for clarity.
diff --git a/Doc/library/test.rst b/Doc/library/test.rst
index c27ee08..5e4a1cb 100644
--- a/Doc/library/test.rst
+++ b/Doc/library/test.rst
@@ -223,14 +223,14 @@ The :mod:`test.support` module defines the following constants:
.. data:: verbose
- :const:`True` when verbose output is enabled. Should be checked when more
+ ``True`` when verbose output is enabled. Should be checked when more
detailed information is desired about a running test. *verbose* is set by
:mod:`test.regrtest`.
.. data:: is_jython
- :const:`True` if the running interpreter is Jython.
+ ``True`` if the running interpreter is Jython.
.. data:: TESTFN
@@ -249,7 +249,7 @@ The :mod:`test.support` module defines the following functions:
.. function:: is_resource_enabled(resource)
- Return :const:`True` if *resource* is enabled and available. The list of
+ Return ``True`` if *resource* is enabled and available. The list of
available resources is only set when :mod:`test.regrtest` is executing the
tests.
@@ -258,7 +258,7 @@ The :mod:`test.support` module defines the following functions:
Raise :exc:`ResourceDenied` if *resource* is not available. *msg* is the
argument to :exc:`ResourceDenied` if it is raised. Always returns
- :const:`True` if called by a function whose ``__name__`` is ``'__main__'``.
+ ``True`` if called by a function whose ``__name__`` is ``'__main__'``.
Used when tests are executed by :mod:`test.regrtest`.
@@ -286,6 +286,15 @@ The :mod:`test.support` module defines the following functions:
This will run all tests defined in the named module.
+.. function:: run_doctest(module, verbosity=None)
+
+ Run :func:`doctest.testmod` on the given *module*. Return
+ ``(failure_count, test_count)``.
+
+ If *verbosity* is ``None``, :func:`doctest.testmod` is run with verbosity
+ set to :data:`verbose`. Otherwise, it is run with verbosity set to
+ ``None``.
+
.. function:: check_warnings(\*filters, quiet=True)
A convenience wrapper for :func:`warnings.catch_warnings()` that makes it
@@ -296,12 +305,12 @@ The :mod:`test.support` module defines the following functions:
``check_warnings`` accepts 2-tuples of the form ``("message regexp",
WarningCategory)`` as positional arguments. If one or more *filters* are
- provided, or if the optional keyword argument *quiet* is :const:`False`,
+ provided, or if the optional keyword argument *quiet* is ``False``,
it checks to make sure the warnings are as expected: each specified filter
must match at least one of the warnings raised by the enclosed code or the
test fails, and if any warnings are raised that do not match any of the
specified filters the test fails. To disable the first of these checks,
- set *quiet* to :const:`True`.
+ set *quiet* to ``True``.
If no arguments are specified, it defaults to::
@@ -316,7 +325,7 @@ The :mod:`test.support` module defines the following functions:
representing the most recent warning can also be accessed directly through
the recorder object (see example below). If no warning has been raised,
then any of the attributes that would otherwise be expected on an object
- representing a warning will return :const:`None`.
+ representing a warning will return ``None``.
The recorder object also has a :meth:`reset` method, which clears the
warnings list.
@@ -354,7 +363,7 @@ The :mod:`test.support` module defines the following functions:
.. function:: captured_stdout()
- This is a context manager that runs the :keyword:`with` statement body using
+ A context manager that runs the :keyword:`with` statement body using
a :class:`StringIO.StringIO` object as sys.stdout. That object can be
retrieved using the ``as`` clause of the :keyword:`with` statement.
@@ -365,6 +374,57 @@ The :mod:`test.support` module defines the following functions:
assert s.getvalue() == "hello"
+.. function:: temp_cwd(name='tempcwd', quiet=False, path=None)
+
+ A context manager that temporarily changes the current working
+ directory (CWD).
+
+ An existing path may be provided as *path*, in which case this function
+ makes no changes to the file system.
+
+ Otherwise, the new CWD is created in the current directory and it's named
+ *name*. If *quiet* is ``False`` and it's not possible to create or
+ change the CWD, an error is raised. If it's ``True``, only a warning
+ is raised and the original CWD is used.
+
+
+.. function:: temp_umask(umask)
+
+ A context manager that temporarily sets the process umask.
+
+
+.. function:: can_symlink()
+
+ Return ``True`` if the OS supports symbolic links, ``False``
+ otherwise.
+
+
+.. decorator:: skip_unless_symlink()
+
+ A decorator for running tests that require support for symbolic links.
+
+
+.. decorator:: anticipate_failure(condition)
+
+ A decorator to conditionally mark tests with
+ :func:`unittest.expectedFailure`. Any use of this decorator should
+ have an associated comment identifying the relevant tracker issue.
+
+
+.. decorator:: run_with_locale(catstr, *locales)
+
+ A decorator for running a function in a different locale, correctly
+ resetting it after it has finished. *catstr* is the locale category as
+ a string (for example ``"LC_ALL"``). The *locales* passed will be tried
+ sequentially, and the first valid locale will be used.
+
+
+.. function:: make_bad_fd()
+
+ Create an invalid file descriptor by opening and closing a temporary file,
+ and returning its descripor.
+
+
.. function:: import_module(name, deprecated=False)
This function imports and returns the named module. Unlike a normal
@@ -372,7 +432,7 @@ The :mod:`test.support` module defines the following functions:
cannot be imported.
Module and package deprecation messages are suppressed during this import
- if *deprecated* is :const:`True`.
+ if *deprecated* is ``True``.
.. versionadded:: 3.1
@@ -396,9 +456,9 @@ The :mod:`test.support` module defines the following functions:
``sys.modules`` when the fresh import is complete.
Module and package deprecation messages are suppressed during this import
- if *deprecated* is :const:`True`.
+ if *deprecated* is ``True``.
- This function will raise :exc:`unittest.SkipTest` is the named module
+ This function will raise :exc:`unittest.SkipTest` if the named module
cannot be imported.
Example use::
@@ -413,6 +473,48 @@ The :mod:`test.support` module defines the following functions:
.. versionadded:: 3.1
+.. function:: bind_port(sock, host=HOST)
+
+ Bind the socket to a free port and return the port number. Relies on
+ ephemeral ports in order to ensure we are using an unbound port. This is
+ important as many tests may be running simultaneously, especially in a
+ buildbot environment. This method raises an exception if the
+ ``sock.family`` is :const:`~socket.AF_INET` and ``sock.type`` is
+ :const:`~socket.SOCK_STREAM`, and the socket has
+ :const:`~socket.SO_REUSEADDR` or :const:`~socket.SO_REUSEPORT` set on it.
+ Tests should never set these socket options for TCP/IP sockets.
+ The only case for setting these options is testing multicasting via
+ multiple UDP sockets.
+
+ Additionally, if the :const:`~socket.SO_EXCLUSIVEADDRUSE` socket option is
+ available (i.e. on Windows), it will be set on the socket. This will
+ prevent anyone else from binding to our host/port for the duration of the
+ test.
+
+
+.. function:: find_unused_port(family=socket.AF_INET, socktype=socket.SOCK_STREAM)
+
+ Returns an unused port that should be suitable for binding. This is
+ achieved by creating a temporary socket with the same family and type as
+ the ``sock`` parameter (default is :const:`~socket.AF_INET`,
+ :const:`~socket.SOCK_STREAM`),
+ and binding it to the specified host address (defaults to ``0.0.0.0``)
+ with the port set to 0, eliciting an unused ephemeral port from the OS.
+ The temporary socket is then closed and deleted, and the ephemeral port is
+ returned.
+
+ Either this method or :func:`bind_port` should be used for any tests
+ where a server socket needs to be bound to a particular port for the
+ duration of the test.
+ Which one to use depends on whether the calling code is creating a python
+ socket, or if an unused port needs to be provided in a constructor
+ or passed to an external program (i.e. the ``-accept`` argument to
+ openssl's s_server mode). Always prefer :func:`bind_port` over
+ :func:`find_unused_port` where possible. Using a hard coded port is
+ discouraged since it can makes multiple instances of the test impossible to
+ run simultaneously, which is a problem for buildbots.
+
+
The :mod:`test.support` module defines the following classes:
.. class:: TransientResource(exc, **kwargs)
diff --git a/Doc/library/threading.rst b/Doc/library/threading.rst
index c226dd4..aaec588 100644
--- a/Doc/library/threading.rst
+++ b/Doc/library/threading.rst
@@ -20,17 +20,6 @@ The :mod:`dummy_threading` module is provided for situations where
methods and functions in this module in the Python 2.x series are still
supported by this module.
-.. impl-detail::
-
- Due to the :term:`Global Interpreter Lock`, in CPython only one thread
- can execute Python code at once (even though certain performance-oriented
- libraries might overcome this limitation).
- If you want your application to make better of use of the computational
- resources of multi-core machines, you are advised to use
- :mod:`multiprocessing` or :class:`concurrent.futures.ProcessPoolExecutor`.
- However, threading is still an appropriate model if you want to run
- multiple I/O-bound tasks simultaneously.
-
This module defines the following functions and objects:
@@ -59,6 +48,17 @@ This module defines the following functions and objects:
returned.
+.. function:: get_ident()
+
+ Return the 'thread identifier' of the current thread. This is a nonzero
+ integer. Its value has no direct meaning; it is intended as a magic cookie
+ to be used e.g. to index a dictionary of thread-specific data. Thread
+ identifiers may be recycled when a thread exits and another thread is
+ created.
+
+ .. versionadded:: 3.3
+
+
.. function:: enumerate()
Return a list of all :class:`Thread` objects currently alive. The list
@@ -241,7 +241,7 @@ changed through the :attr:`name` attribute.
A thread can be flagged as a "daemon thread". The significance of this flag is
that the entire Python program exits when only daemon threads are left. The
initial value is inherited from the creating thread. The flag can be set
-through the :attr:`daemon` property.
+through the :attr:`daemon` property or the *daemon* constructor argument.
There is a "main thread" object; this corresponds to the initial thread of
control in the Python program. It is not a daemon thread.
@@ -254,7 +254,8 @@ daemonic, and cannot be :meth:`join`\ ed. They are never deleted, since it is
impossible to detect the termination of alien threads.
-.. class:: Thread(group=None, target=None, name=None, args=(), kwargs={})
+.. class:: Thread(group=None, target=None, name=None, args=(), kwargs={},
+ verbose=None, *, daemon=None)
This constructor should always be called with keyword arguments. Arguments
are:
@@ -273,10 +274,19 @@ impossible to detect the termination of alien threads.
*kwargs* is a dictionary of keyword arguments for the target invocation.
Defaults to ``{}``.
+ *verbose* is a flag used for debugging messages.
+
+ If not ``None``, *daemon* explicitly sets whether the thread is daemonic.
+ If ``None`` (the default), the daemonic property is inherited from the
+ current thread.
+
If the subclass overrides the constructor, it must make sure to invoke the
base class constructor (``Thread.__init__()``) before doing anything else to
the thread.
+ .. versionchanged:: 3.3
+ Added the *daemon* argument.
+
.. method:: start()
Start the thread's activity.
@@ -333,10 +343,10 @@ impossible to detect the termination of alien threads.
.. attribute:: ident
The 'thread identifier' of this thread or ``None`` if the thread has not
- been started. This is a nonzero integer. See the
- :func:`thread.get_ident()` function. Thread identifiers may be recycled
- when a thread exits and another thread is created. The identifier is
- available even after the thread has exited.
+ been started. This is a nonzero integer. See the :func:`get_ident()`
+ function. Thread identifiers may be recycled when a thread exits and
+ another thread is created. The identifier is available even after the
+ thread has exited.
.. method:: is_alive()
@@ -364,6 +374,18 @@ impossible to detect the termination of alien threads.
property instead.
+.. impl-detail::
+
+ Due to the :term:`Global Interpreter Lock`, in CPython only one thread
+ can execute Python code at once (even though certain performance-oriented
+ libraries might overcome this limitation).
+ If you want your application to make better of use of the computational
+ resources of multi-core machines, you are advised to use
+ :mod:`multiprocessing` or :class:`concurrent.futures.ProcessPoolExecutor`.
+ However, threading is still an appropriate model if you want to run
+ multiple I/O-bound tasks simultaneously.
+
+
.. _lock-objects:
Lock Objects
diff --git a/Doc/library/time.rst b/Doc/library/time.rst
index 7c464ac..668651a 100644
--- a/Doc/library/time.rst
+++ b/Doc/library/time.rst
@@ -41,25 +41,6 @@ An explanation of some terminology and conventions is in order.
parsed, they are converted according to the POSIX and ISO C standards: values
69--99 are mapped to 1969--1999, and values 0--68 are mapped to 2000--2068.
- For backward compatibility, years with less than 4 digits are treated
- specially by :func:`asctime`, :func:`mktime`, and :func:`strftime` functions
- that operate on a 9-tuple or :class:`struct_time` values. If year (the first
- value in the 9-tuple) is specified with less than 4 digits, its interpretation
- depends on the value of ``accept2dyear`` variable.
-
- If ``accept2dyear`` is true (default), a backward compatibility behavior is
- invoked as follows:
-
- - for 2-digit year, century is guessed according to POSIX rules for
- ``%y`` strptime format. A deprecation warning is issued when century
- information is guessed in this way.
-
- - for 3-digit or negative year, a :exc:`ValueError` exception is raised.
-
- If ``accept2dyear`` is false (set by the program or as a result of a
- non-empty value assigned to ``PYTHONY2K`` environment variable) all year
- values are interpreted as given.
-
.. index::
single: UTC
single: Coordinated Universal Time
@@ -117,24 +98,6 @@ An explanation of some terminology and conventions is in order.
The module defines the following functions and data items:
-
-.. data:: accept2dyear
-
- Boolean value indicating whether two-digit year values will be
- mapped to 1969--2068 range by :func:`asctime`, :func:`mktime`, and
- :func:`strftime` functions. This is true by default, but will be
- set to false if the environment variable :envvar:`PYTHONY2K` has
- been set to a non-empty string. It may also be modified at run
- time.
-
- .. deprecated:: 3.2
- Mapping of 2-digit year values by :func:`asctime`,
- :func:`mktime`, and :func:`strftime` functions to 1969--2068
- range is deprecated. Programs that need to process 2-digit
- years should use ``%y`` code available in :func:`strptime`
- function or convert 2-digit year values to 4-digit themselves.
-
-
.. data:: altzone
The offset of the local DST timezone, in seconds west of UTC, if one is defined.
@@ -173,6 +136,54 @@ The module defines the following functions and data items:
microsecond.
+.. function:: clock_getres(clk_id)
+
+ Return the resolution (precision) of the specified clock *clk_id*.
+
+ .. versionadded:: 3.3
+
+.. function:: clock_gettime(clk_id)
+
+ Return the time of the specified clock *clk_id*.
+
+ .. versionadded:: 3.3
+
+.. data:: CLOCK_REALTIME
+
+ System-wide real-time clock. Setting this clock requires appropriate
+ privileges.
+
+ .. versionadded:: 3.3
+
+.. data:: CLOCK_MONOTONIC
+
+ Clock that cannot be set and represents monotonic time since some
+ unspecified starting point.
+
+ .. versionadded:: 3.3
+
+.. data:: CLOCK_MONOTONIC_RAW
+
+ Similar to :data:`CLOCK_MONOTONIC`, but provides access to a raw
+ hardware-based time that is not subject to NTP adjustments.
+
+ Availability: Linux 2.6.28 or later.
+
+ .. versionadded:: 3.3
+
+.. data:: CLOCK_PROCESS_CPUTIME_ID
+
+ High-resolution per-process timer from the CPU.
+
+ .. versionadded:: 3.3
+
+.. data:: CLOCK_THREAD_CPUTIME_ID
+
+ Thread-specific CPU-time clock.
+
+ .. versionadded:: 3.3
+
+
.. function:: ctime([secs])
Convert a time expressed in seconds since the epoch to a string representing
@@ -308,7 +319,7 @@ The module defines the following functions and data items:
| ``%y`` | Year without century as a decimal number | |
| | [00,99]. | |
+-----------+------------------------------------------------+-------+
- | ``%Y`` | Year with century as a decimal number. | \(4) |
+ | ``%Y`` | Year with century as a decimal number. | |
| | | |
+-----------+------------------------------------------------+-------+
| ``%Z`` | Time zone name (no characters if no time zone | |
@@ -332,12 +343,6 @@ The module defines the following functions and data items:
When used with the :func:`strptime` function, ``%U`` and ``%W`` are only used in
calculations when the day of the week and the year are specified.
- (4)
- Produces different results depending on the value of
- ``time.accept2dyear`` variable. See :ref:`Year 2000 (Y2K)
- issues <time-y2kissues>` for details.
-
-
Here is an example, a format for dates compatible with that specified in the
:rfc:`2822` Internet email standard. [#]_ ::
@@ -418,8 +423,7 @@ The module defines the following functions and data items:
+-------+-------------------+---------------------------------+
Note that unlike the C structure, the month value is a range of [1, 12], not
- [0, 11]. A year value will be handled as described under :ref:`Year 2000
- (Y2K) issues <time-y2kissues>` above. A ``-1`` argument as the daylight
+ [0, 11]. A ``-1`` argument as the daylight
savings flag, passed to :func:`mktime` will usually result in the correct
daylight savings state to be filled in.
diff --git a/Doc/library/tokenize.rst b/Doc/library/tokenize.rst
index 577d7cc..050d74c 100644
--- a/Doc/library/tokenize.rst
+++ b/Doc/library/tokenize.rst
@@ -15,6 +15,9 @@ implemented in Python. The scanner in this module returns comments as tokens
as well, making it useful for implementing "pretty-printers," including
colorizers for on-screen displays.
+Tokenizing Input
+----------------
+
The primary entry point is a :term:`generator`:
.. function:: tokenize(readline)
@@ -116,6 +119,26 @@ function it uses to do this is available:
.. versionadded:: 3.2
+.. _tokenize-cli:
+
+Command-Line Usage
+------------------
+
+.. versionadded:: 3.3
+
+The :mod:`tokenize` module can be executed as a script from the command line.
+It is as simple as:
+
+.. code-block:: sh
+
+ python -m tokenize [filename.py]
+
+If :file:`filename.py` is specified its contents are tokenized to stdout.
+Otherwise, tokenization is performed on stdin.
+
+Examples
+------------------
+
Example of a script rewriter that transforms float literals into Decimal
objects::
@@ -158,3 +181,37 @@ objects::
result.append((toknum, tokval))
return untokenize(result).decode('utf-8')
+Example of tokenizing from the command line. The script::
+
+ def say_hello():
+ print("Hello, World!")
+
+ say_hello()
+
+will be tokenized to the following output where the first column is the range
+of the line/column coordinates where the token is found, the second column is
+the name of the token, and the final column is the value of the token (if any)
+
+.. code-block:: sh
+
+ $ python -m tokenize hello.py
+ 0,0-0,0: ENCODING 'utf-8'
+ 1,0-1,3: NAME 'def'
+ 1,4-1,13: NAME 'say_hello'
+ 1,13-1,14: OP '('
+ 1,14-1,15: OP ')'
+ 1,15-1,16: OP ':'
+ 1,16-1,17: NEWLINE '\n'
+ 2,0-2,4: INDENT ' '
+ 2,4-2,9: NAME 'print'
+ 2,9-2,10: OP '('
+ 2,10-2,25: STRING '"Hello, World!"'
+ 2,25-2,26: OP ')'
+ 2,26-2,27: NEWLINE '\n'
+ 3,0-3,1: NL '\n'
+ 4,0-4,0: DEDENT ''
+ 4,0-4,9: NAME 'say_hello'
+ 4,9-4,10: OP '('
+ 4,10-4,11: OP ')'
+ 4,11-4,12: NEWLINE '\n'
+ 5,0-5,0: ENDMARKER ''
diff --git a/Doc/library/unicodedata.rst b/Doc/library/unicodedata.rst
index bcb3da3..d0783a2 100644
--- a/Doc/library/unicodedata.rst
+++ b/Doc/library/unicodedata.rst
@@ -29,6 +29,9 @@ following functions:
Look up character by name. If a character with the given name is found, return
the corresponding character. If not found, :exc:`KeyError` is raised.
+ .. versionchanged:: 3.3
+ Support for name aliases [#]_ and named sequences [#]_ has been added.
+
.. function:: name(chr[, default])
@@ -160,3 +163,9 @@ Examples:
>>> unicodedata.bidirectional('\u0660') # 'A'rabic, 'N'umber
'AN'
+
+.. rubric:: Footnotes
+
+.. [#] http://www.unicode.org/Public/6.0.0/ucd/NameAliases.txt
+
+.. [#] http://www.unicode.org/Public/6.0.0/ucd/NamedSequences.txt
diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst
index 5833868..520f2e8 100644
--- a/Doc/library/unittest.rst
+++ b/Doc/library/unittest.rst
@@ -792,11 +792,14 @@ Test cases
Run the test, collecting the result into the test result object passed as
*result*. If *result* is omitted or ``None``, a temporary result
object is created (by calling the :meth:`defaultTestResult` method) and
- used. The result object is not returned to :meth:`run`'s caller.
+ used. The result object is returned to :meth:`run`'s caller.
The same effect may be had by simply calling the :class:`TestCase`
instance.
+ .. versionchanged:: 3.3
+ Previous versions of ``run`` did not return the result. Neither did
+ calling an instance.
.. method:: skipTest(reason)
@@ -857,10 +860,11 @@ Test cases
| <TestCase.assertNotIsInstance>` | | |
+-----------------------------------------+-----------------------------+---------------+
- All the assert methods (except :meth:`assertRaises`,
- :meth:`assertRaisesRegex`, :meth:`assertWarns`, :meth:`assertWarnsRegex`)
- accept a *msg* argument that, if specified, is used as the error message on
- failure (see also :data:`longMessage`).
+ All the assert methods accept a *msg* argument that, if specified, is used
+ as the error message on failure (see also :data:`longMessage`).
+ Note that the *msg* keyword argument can be passed to :meth:`assertRaises`,
+ :meth:`assertRaisesRegex`, :meth:`assertWarns`, :meth:`assertWarnsRegex`
+ only when they are used as a context manager.
.. method:: assertEqual(first, second, msg=None)
@@ -955,7 +959,7 @@ Test cases
+---------------------------------------------------------+--------------------------------------+------------+
.. method:: assertRaises(exception, callable, *args, **kwds)
- assertRaises(exception)
+ assertRaises(exception, msg=None)
Test that an exception is raised when *callable* is called with any
positional or keyword arguments that are also passed to
@@ -964,12 +968,16 @@ Test cases
To catch any of a group of exceptions, a tuple containing the exception
classes may be passed as *exception*.
- If only the *exception* argument is given, returns a context manager so
- that the code under test can be written inline rather than as a function::
+ If only the *exception* and possibly the *msg* arguments are given,
+ return a context manager so that the code under test can be written
+ inline rather than as a function::
with self.assertRaises(SomeException):
do_something()
+ When used as a context manager, :meth:`assertRaises` accepts the
+ additional keyword argument *msg*.
+
The context manager will store the caught exception object in its
:attr:`exception` attribute. This can be useful if the intention
is to perform additional checks on the exception raised::
@@ -986,9 +994,12 @@ Test cases
.. versionchanged:: 3.2
Added the :attr:`exception` attribute.
+ .. versionchanged:: 3.3
+ Added the *msg* keyword argument when used as a context manager.
+
.. method:: assertRaisesRegex(exception, regex, callable, *args, **kwds)
- assertRaisesRegex(exception, regex)
+ assertRaisesRegex(exception, regex, msg=None)
Like :meth:`assertRaises` but also tests that *regex* matches
on the string representation of the raised exception. *regex* may be
@@ -1005,12 +1016,16 @@ Test cases
.. versionadded:: 3.1
under the name ``assertRaisesRegexp``.
+
.. versionchanged:: 3.2
Renamed to :meth:`assertRaisesRegex`.
+ .. versionchanged:: 3.3
+ Added the *msg* keyword argument when used as a context manager.
+
.. method:: assertWarns(warning, callable, *args, **kwds)
- assertWarns(warning)
+ assertWarns(warning, msg=None)
Test that a warning is triggered when *callable* is called with any
positional or keyword arguments that are also passed to
@@ -1019,12 +1034,16 @@ Test cases
To catch any of a group of warnings, a tuple containing the warning
classes may be passed as *warnings*.
- If only the *warning* argument is given, returns a context manager so
- that the code under test can be written inline rather than as a function::
+ If only the *warning* and possibly the *msg* arguments are given,
+ returns a context manager so that the code under test can be written
+ inline rather than as a function::
with self.assertWarns(SomeWarning):
do_something()
+ When used as a context manager, :meth:`assertRaises` accepts the
+ additional keyword argument *msg*.
+
The context manager will store the caught warning object in its
:attr:`warning` attribute, and the source line which triggered the
warnings in the :attr:`filename` and :attr:`lineno` attributes.
@@ -1042,9 +1061,12 @@ Test cases
.. versionadded:: 3.2
+ .. versionchanged:: 3.3
+ Added the *msg* keyword argument when used as a context manager.
+
.. method:: assertWarnsRegex(warning, regex, callable, *args, **kwds)
- assertWarnsRegex(warning, regex)
+ assertWarnsRegex(warning, regex, msg=None)
Like :meth:`assertWarns` but also tests that *regex* matches on the
message of the triggered warning. *regex* may be a regular expression
@@ -1062,6 +1084,8 @@ Test cases
.. versionadded:: 3.2
+ .. versionchanged:: 3.3
+ Added the *msg* keyword argument when used as a context manager.
There are also other methods used to perform more specific checks, such as:
diff --git a/Doc/library/urllib.error.rst b/Doc/library/urllib.error.rst
index 282329f..e20db27 100644
--- a/Doc/library/urllib.error.rst
+++ b/Doc/library/urllib.error.rst
@@ -8,21 +8,23 @@
The :mod:`urllib.error` module defines the exception classes for exceptions
-raised by :mod:`urllib.request`. The base exception class is :exc:`URLError`,
-which inherits from :exc:`IOError`.
+raised by :mod:`urllib.request`. The base exception class is :exc:`URLError`.
The following exceptions are raised by :mod:`urllib.error` as appropriate:
.. exception:: URLError
The handlers raise this exception (or derived exceptions) when they run into
- a problem. It is a subclass of :exc:`IOError`.
+ a problem. It is a subclass of :exc:`OSError`.
.. attribute:: reason
The reason for this error. It can be a message string or another
- exception instance (:exc:`socket.error` for remote URLs, :exc:`OSError`
- for local URLs).
+ exception instance.
+
+ .. versionchanged:: 3.3
+ :exc:`URLError` has been made a subclass of :exc:`OSError` instead
+ of :exc:`IOError`.
.. exception:: HTTPError
diff --git a/Doc/library/urllib.request.rst b/Doc/library/urllib.request.rst
index 6b2b013..abd9f35 100644
--- a/Doc/library/urllib.request.rst
+++ b/Doc/library/urllib.request.rst
@@ -132,7 +132,7 @@ The :mod:`urllib.request` module defines the following functions:
The following classes are provided:
-.. class:: Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False)
+.. class:: Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)
This class is an abstraction of a URL request.
@@ -140,8 +140,8 @@ The following classes are provided:
*data* may be a string specifying additional data to send to the
server, or ``None`` if no such data is needed. Currently HTTP
- requests are the only ones that use *data*; the HTTP request will
- be a POST instead of a GET when the *data* parameter is provided.
+ requests are the only ones that use *data*, in order to choose between
+ ``'GET'`` and ``'POST'`` when *method* is not specified.
*data* should be a buffer in the standard
:mimetype:`application/x-www-form-urlencoded` format. The
:func:`urllib.parse.urlencode` function takes a mapping or sequence
@@ -157,8 +157,8 @@ The following classes are provided:
:mod:`urllib`'s default user agent string is
``"Python-urllib/2.6"`` (on Python 2.6).
- The final two arguments are only of interest for correct handling
- of third-party HTTP cookies:
+ The following two arguments, *origin_req_host* and *unverifiable*,
+ are only of interest for correct handling of third-party HTTP cookies:
*origin_req_host* should be the request-host of the origin
transaction, as defined by :rfc:`2965`. It defaults to
@@ -175,6 +175,13 @@ The following classes are provided:
document, and the user had no option to approve the automatic
fetching of the image, this should be true.
+ *method* should be a string that indicates the HTTP request method that
+ will be used (e.g. ``'HEAD'``). Its value is stored in the
+ :attr:`~Request.method` attribute and is used by :meth:`get_method()`.
+
+ .. versionchanged:: 3.3
+ :attr:`Request.method` argument is added to the Request class.
+
.. class:: OpenerDirector()
@@ -240,10 +247,11 @@ The following classes are provided:
.. class:: HTTPBasicAuthHandler(password_mgr=None)
- Handle authentication with the remote host. *password_mgr*, if given, should be
- something that is compatible with :class:`HTTPPasswordMgr`; refer to section
- :ref:`http-password-mgr` for information on the interface that must be
- supported.
+ Handle authentication with the remote host. *password_mgr*, if given, should
+ be something that is compatible with :class:`HTTPPasswordMgr`; refer to
+ section :ref:`http-password-mgr` for information on the interface that must
+ be supported. HTTPBasicAuthHandler will raise a :exc:`ValueError` when
+ presented with a wrong Authentication scheme.
.. class:: ProxyBasicAuthHandler(password_mgr=None)
@@ -265,10 +273,19 @@ The following classes are provided:
.. class:: HTTPDigestAuthHandler(password_mgr=None)
- Handle authentication with the remote host. *password_mgr*, if given, should be
- something that is compatible with :class:`HTTPPasswordMgr`; refer to section
- :ref:`http-password-mgr` for information on the interface that must be
- supported.
+ Handle authentication with the remote host. *password_mgr*, if given, should
+ be something that is compatible with :class:`HTTPPasswordMgr`; refer to
+ section :ref:`http-password-mgr` for information on the interface that must
+ be supported. When both Digest Authentication Handler and Basic
+ Authentication Handler are both added, Digest Authentication is always tried
+ first. If the Digest Authentication returns a 40x response again, it is sent
+ to Basic Authentication handler to Handle. This Handler method will raise a
+ :exc:`ValueError` when presented with an authentication scheme other than
+ Digest or Basic.
+
+ .. versionchanged:: 3.3
+ Raise :exc:`ValueError` on unsupported Authentication Scheme.
+
.. class:: ProxyDigestAuthHandler(password_mgr=None)
@@ -359,6 +376,15 @@ request.
boolean, indicates whether the request is unverifiable as defined
by RFC 2965.
+.. attribute:: Request.method
+
+ The HTTP request method to use. This value is used by
+ :meth:`~Request.get_method` to override the computed HTTP request
+ method that would otherwise be returned. This attribute is initialized with
+ the value of the *method* argument passed to the constructor.
+
+ .. versionadded:: 3.3
+
.. method:: Request.add_data(data)
Set the :class:`Request` data to *data*. This is ignored by all handlers except
@@ -368,8 +394,13 @@ request.
.. method:: Request.get_method()
- Return a string indicating the HTTP request method. This is only meaningful for
- HTTP requests, and currently always returns ``'GET'`` or ``'POST'``.
+ Return a string indicating the HTTP request method. If
+ :attr:`Request.method` is not ``None``, return its value, otherwise return
+ ``'GET'`` if :attr:`Request.data` is ``None``, or ``'POST'`` if it's not.
+ This is only meaningful for HTTP requests.
+
+ .. versionchanged:: 3.3
+ get_method now looks at the value of :attr:`Request.method`.
.. method:: Request.has_data()
@@ -1162,7 +1193,7 @@ some point in the future.
*key_file* and *cert_file* are supported to provide an SSL key and certificate;
both are needed to support client authentication.
- :class:`URLopener` objects will raise an :exc:`IOError` exception if the server
+ :class:`URLopener` objects will raise an :exc:`OSError` exception if the server
returns an error code.
.. method:: open(fullurl, data=None)
diff --git a/Doc/library/warnings.rst b/Doc/library/warnings.rst
index 8af19a2..8387f5a 100644
--- a/Doc/library/warnings.rst
+++ b/Doc/library/warnings.rst
@@ -339,8 +339,7 @@ Available Functions
Write a warning to a file. The default implementation calls
``formatwarning(message, category, filename, lineno, line)`` and writes the
resulting string to *file*, which defaults to ``sys.stderr``. You may replace
- this function with an alternative implementation by assigning to
- ``warnings.showwarning``.
+ this function with any callable by assigning to ``warnings.showwarning``.
*line* is a line of source code to be included in the warning
message; if *line* is not supplied, :func:`showwarning` will
try to read the line specified by *filename* and *lineno*.
diff --git a/Doc/library/winreg.rst b/Doc/library/winreg.rst
index 5cf30ee..376752e 100644
--- a/Doc/library/winreg.rst
+++ b/Doc/library/winreg.rst
@@ -38,7 +38,11 @@ This module offers the following functions:
*key* is the predefined handle to connect to.
The return value is the handle of the opened key. If the function fails, a
- :exc:`WindowsError` exception is raised.
+ :exc:`OSError` exception is raised.
+
+ .. versionchanged:: 3.3
+ This function used to raise a :exc:`WindowsError`, which is now an
+ alias of :exc:`OSError`.
.. function:: CreateKey(key, sub_key)
@@ -57,7 +61,11 @@ This module offers the following functions:
If the key already exists, this function opens the existing key.
The return value is the handle of the opened key. If the function fails, a
- :exc:`WindowsError` exception is raised.
+ :exc:`OSError` exception is raised.
+
+ .. versionchanged:: 3.3
+ This function used to raise a :exc:`WindowsError`, which is now an
+ alias of :exc:`OSError`.
.. function:: CreateKeyEx(key, sub_key, reserved=0, access=KEY_ALL_ACCESS)
@@ -82,10 +90,14 @@ This module offers the following functions:
If the key already exists, this function opens the existing key.
The return value is the handle of the opened key. If the function fails, a
- :exc:`WindowsError` exception is raised.
+ :exc:`OSError` exception is raised.
.. versionadded:: 3.2
+ .. versionchanged:: 3.3
+ This function used to raise a :exc:`WindowsError`, which is now an
+ alias of :exc:`OSError`.
+
.. function:: DeleteKey(key, sub_key)
@@ -100,7 +112,11 @@ This module offers the following functions:
*This method can not delete keys with subkeys.*
If the method succeeds, the entire key, including all of its values, is removed.
- If the method fails, a :exc:`WindowsError` exception is raised.
+ If the method fails, a :exc:`OSError` exception is raised.
+
+ .. versionchanged:: 3.3
+ This function used to raise a :exc:`WindowsError`, which is now an
+ alias of :exc:`OSError`.
.. function:: DeleteKeyEx(key, sub_key, access=KEY_ALL_ACCESS, reserved=0)
@@ -129,12 +145,16 @@ This module offers the following functions:
*This method can not delete keys with subkeys.*
If the method succeeds, the entire key, including all of its values, is
- removed. If the method fails, a :exc:`WindowsError` exception is raised.
+ removed. If the method fails, a :exc:`OSError` exception is raised.
On unsupported Windows versions, :exc:`NotImplementedError` is raised.
.. versionadded:: 3.2
+ .. versionchanged:: 3.3
+ This function used to raise a :exc:`WindowsError`, which is now an
+ alias of :exc:`OSError`.
+
.. function:: DeleteValue(key, value)
@@ -156,9 +176,13 @@ This module offers the following functions:
*index* is an integer that identifies the index of the key to retrieve.
The function retrieves the name of one subkey each time it is called. It is
- typically called repeatedly until a :exc:`WindowsError` exception is
+ typically called repeatedly until a :exc:`OSError` exception is
raised, indicating, no more values are available.
+ .. versionchanged:: 3.3
+ This function used to raise a :exc:`WindowsError`, which is now an
+ alias of :exc:`OSError`.
+
.. function:: EnumValue(key, index)
@@ -170,7 +194,7 @@ This module offers the following functions:
*index* is an integer that identifies the index of the value to retrieve.
The function retrieves the name of one subkey each time it is called. It is
- typically called repeatedly, until a :exc:`WindowsError` exception is
+ typically called repeatedly, until a :exc:`OSError` exception is
raised, indicating no more values.
The result is a tuple of 3 items:
@@ -189,6 +213,10 @@ This module offers the following functions:
| | :meth:`SetValueEx`) |
+-------+--------------------------------------------+
+ .. versionchanged:: 3.3
+ This function used to raise a :exc:`WindowsError`, which is now an
+ alias of :exc:`OSError`.
+
.. function:: ExpandEnvironmentStrings(str)
@@ -260,10 +288,14 @@ This module offers the following functions:
The result is a new handle to the specified key.
- If the function fails, :exc:`WindowsError` is raised.
+ If the function fails, :exc:`OSError` is raised.
.. versionchanged:: 3.2 Allow the use of named arguments.
+ .. versionchanged:: 3.3
+ This function used to raise a :exc:`WindowsError`, which is now an
+ alias of :exc:`OSError`.
+
.. function:: OpenKeyEx()
diff --git a/Doc/library/xmlrpc.client.rst b/Doc/library/xmlrpc.client.rst
index e72770a..1871c99 100644
--- a/Doc/library/xmlrpc.client.rst
+++ b/Doc/library/xmlrpc.client.rst
@@ -8,7 +8,7 @@
.. XXX Not everything is documented yet. It might be good to describe
- Marshaller, Unmarshaller, getparser, dumps, loads, and Transport.
+ Marshaller, Unmarshaller, getparser and Transport.
**Source code:** :source:`Lib/xmlrpc/client.py`
@@ -21,7 +21,12 @@ supports writing XML-RPC client code; it handles all the details of translating
between conformable Python objects and XML on the wire.
-.. class:: ServerProxy(uri, transport=None, encoding=None, verbose=False, allow_none=False, use_datetime=False)
+.. class:: ServerProxy(uri, transport=None, encoding=None, verbose=False, \
+ allow_none=False, use_datetime=False, \
+ use_builtin_types=False)
+
+ .. versionchanged:: 3.3
+ The *use_builtin_types* flag was added.
A :class:`ServerProxy` instance is an object that manages communication with a
remote XML-RPC server. The required first argument is a URI (Uniform Resource
@@ -34,9 +39,13 @@ between conformable Python objects and XML on the wire.
XML; the default behaviour is for ``None`` to raise a :exc:`TypeError`. This is
a commonly-used extension to the XML-RPC specification, but isn't supported by
all clients and servers; see http://ontosys.com/xml-rpc/extensions.php for a
- description. The *use_datetime* flag can be used to cause date/time values to
- be presented as :class:`datetime.datetime` objects; this is false by default.
- :class:`datetime.datetime` objects may be passed to calls.
+ description. The *use_builtin_types* flag can be used to cause date/time values
+ to be presented as :class:`datetime.datetime` objects and binary data to be
+ presented as :class:`bytes` objects; this flag is false by default.
+ :class:`datetime.datetime` and :class:`bytes` objects may be passed to calls.
+
+ The obsolete *use_datetime* flag is similar to *use_builtin_types* but it
+ applies only to date/time values.
Both the HTTP and HTTPS transports support the URL syntax extension for HTTP
Basic Authentication: ``http://user:pass@host:port/path``. The ``user:pass``
@@ -78,12 +87,12 @@ between conformable Python objects and XML on the wire.
| | only their *__dict__* attribute is |
| | transmitted. |
+---------------------------------+---------------------------------------------+
- | :const:`dates` | in seconds since the epoch (pass in an |
- | | instance of the :class:`DateTime` class) or |
+ | :const:`dates` | In seconds since the epoch. Pass in an |
+ | | instance of the :class:`DateTime` class or |
| | a :class:`datetime.datetime` instance. |
+---------------------------------+---------------------------------------------+
- | :const:`binary data` | pass in an instance of the :class:`Binary` |
- | | wrapper class |
+ | :const:`binary data` | Pass in an instance of the :class:`Binary` |
+ | | wrapper class or a :class:`bytes` instance. |
+---------------------------------+---------------------------------------------+
This is the full set of data types supported by XML-RPC. Method calls may also
@@ -98,8 +107,9 @@ between conformable Python objects and XML on the wire.
ensure that the string is free of characters that aren't allowed in XML, such as
the control characters with ASCII values between 0 and 31 (except, of course,
tab, newline and carriage return); failing to do this will result in an XML-RPC
- request that isn't well-formed XML. If you have to pass arbitrary strings via
- XML-RPC, use the :class:`Binary` wrapper class described below.
+ request that isn't well-formed XML. If you have to pass arbitrary bytes
+ via XML-RPC, use the :class:`bytes` class or the class:`Binary` wrapper class
+ described below.
:class:`Server` is retained as an alias for :class:`ServerProxy` for backwards
compatibility. New code should use :class:`ServerProxy`.
@@ -249,7 +259,7 @@ The client code for the preceding server::
Binary Objects
--------------
-This class may be initialized from string data (which may include NULs). The
+This class may be initialized from bytes data (which may include NULs). The
primary access to the content of a :class:`Binary` object is provided by an
attribute:
@@ -257,15 +267,15 @@ attribute:
.. attribute:: Binary.data
The binary data encapsulated by the :class:`Binary` instance. The data is
- provided as an 8-bit string.
+ provided as a :class:`bytes` object.
:class:`Binary` objects have the following methods, supported mainly for
internal use by the marshalling/unmarshalling code:
-.. method:: Binary.decode(string)
+.. method:: Binary.decode(bytes)
- Accept a base64 string and decode it as the instance's new data.
+ Accept a base64 :class:`bytes` object and decode it as the instance's new data.
.. method:: Binary.encode(out)
@@ -471,14 +481,21 @@ Convenience Functions
it via an extension, provide a true value for *allow_none*.
-.. function:: loads(data, use_datetime=False)
+.. function:: loads(data, use_datetime=False, use_builtin_types=False)
Convert an XML-RPC request or response into Python objects, a ``(params,
methodname)``. *params* is a tuple of argument; *methodname* is a string, or
``None`` if no method name is present in the packet. If the XML-RPC packet
represents a fault condition, this function will raise a :exc:`Fault` exception.
- The *use_datetime* flag can be used to cause date/time values to be presented as
- :class:`datetime.datetime` objects; this is false by default.
+ The *use_builtin_types* flag can be used to cause date/time values to be
+ presented as :class:`datetime.datetime` objects and binary data to be
+ presented as :class:`bytes` objects; this flag is false by default.
+
+ The obsolete *use_datetime* flag is similar to *use_builtin_types* but it
+ applies only to date/time values.
+
+ .. versionchanged:: 3.3
+ The *use_builtin_types* flag was added.
.. _xmlrpc-client-example:
diff --git a/Doc/library/xmlrpc.server.rst b/Doc/library/xmlrpc.server.rst
index 67feba6..6493fd4 100644
--- a/Doc/library/xmlrpc.server.rst
+++ b/Doc/library/xmlrpc.server.rst
@@ -16,7 +16,9 @@ servers written in Python. Servers can either be free standing, using
:class:`CGIXMLRPCRequestHandler`.
-.. class:: SimpleXMLRPCServer(addr, requestHandler=SimpleXMLRPCRequestHandler, logRequests=True, allow_none=False, encoding=None, bind_and_activate=True)
+.. class:: SimpleXMLRPCServer(addr, requestHandler=SimpleXMLRPCRequestHandler,\
+ logRequests=True, allow_none=False, encoding=None,\
+ bind_and_activate=True, use_builtin_types=False)
Create a new server instance. This class provides methods for registration of
functions that can be called by the XML-RPC protocol. The *requestHandler*
@@ -25,18 +27,31 @@ servers written in Python. Servers can either be free standing, using
are passed to the :class:`socketserver.TCPServer` constructor. If *logRequests*
is true (the default), requests will be logged; setting this parameter to false
will turn off logging. The *allow_none* and *encoding* parameters are passed
- on to :mod:`xmlrpc.client` and control the XML-RPC responses that will be returned
+ on to :mod:`xmlrpc.client` and control the XML-RPC responses that will be returned
from the server. The *bind_and_activate* parameter controls whether
:meth:`server_bind` and :meth:`server_activate` are called immediately by the
constructor; it defaults to true. Setting it to false allows code to manipulate
the *allow_reuse_address* class variable before the address is bound.
+ The *use_builtin_types* parameter is passed to the
+ :func:`~xmlrpc.client.loads` function and controls which types are processed
+ when date/times values or binary data are received; it defaults to false.
+ .. versionchanged:: 3.3
+ The *use_builtin_types* flag was added.
-.. class:: CGIXMLRPCRequestHandler(allow_none=False, encoding=None)
+
+.. class:: CGIXMLRPCRequestHandler(allow_none=False, encoding=None,\
+ use_builtin_types=False)
Create a new instance to handle XML-RPC requests in a CGI environment. The
*allow_none* and *encoding* parameters are passed on to :mod:`xmlrpc.client`
and control the XML-RPC responses that will be returned from the server.
+ The *use_builtin_types* parameter is passed to the
+ :func:`~xmlrpc.client.loads` function and controls which types are processed
+ when date/times values or binary data are received; it defaults to false.
+
+ .. versionchanged:: 3.3
+ The *use_builtin_types* flag was added.
.. class:: SimpleXMLRPCRequestHandler()
@@ -233,12 +248,17 @@ to HTTP GET requests. Servers can either be free standing, using
:class:`DocCGIXMLRPCRequestHandler`.
-.. class:: DocXMLRPCServer(addr, requestHandler=DocXMLRPCRequestHandler, logRequests=True, allow_none=False, encoding=None, bind_and_activate=True)
+.. class:: DocXMLRPCServer(addr, requestHandler=DocXMLRPCRequestHandler,\
+ logRequests=True, allow_none=False, encoding=None,\
+ bind_and_activate=True, use_builtin_types=True)
Create a new server instance. All parameters have the same meaning as for
:class:`SimpleXMLRPCServer`; *requestHandler* defaults to
:class:`DocXMLRPCRequestHandler`.
+ .. versionchanged:: 3.3
+ The *use_builtin_types* flag was added.
+
.. class:: DocCGIXMLRPCRequestHandler()
diff --git a/Doc/library/zipfile.rst b/Doc/library/zipfile.rst
index 6f84bcc..c3e13ed 100644
--- a/Doc/library/zipfile.rst
+++ b/Doc/library/zipfile.rst
@@ -23,7 +23,7 @@ decryption of encrypted files in ZIP archives, but it currently cannot
create an encrypted file. Decryption is extremely slow as it is
implemented in native Python rather than C.
-For other archive formats, see the :mod:`bz2`, :mod:`gzip`, and
+For related file formats, see the :mod:`bz2`, :mod:`gzip`, :mod:`lzma`, and
:mod:`tarfile` modules.
The module defines the following items:
diff --git a/Doc/library/zipimport.rst b/Doc/library/zipimport.rst
index 4f17092..b47c35b 100644
--- a/Doc/library/zipimport.rst
+++ b/Doc/library/zipimport.rst
@@ -85,9 +85,12 @@ zipimporter Objects
.. method:: get_data(pathname)
- Return the data associated with *pathname*. Raise :exc:`IOError` if the
+ Return the data associated with *pathname*. Raise :exc:`OSError` if the
file wasn't found.
+ .. versionchanged:: 3.3
+ :exc:`IOError` used to be raised instead of :exc:`OSError`.
+
.. method:: get_filename(fullname)
diff --git a/Doc/library/zlib.rst b/Doc/library/zlib.rst
index a7b8343..7ad3624 100644
--- a/Doc/library/zlib.rst
+++ b/Doc/library/zlib.rst
@@ -18,8 +18,8 @@ order. This documentation doesn't attempt to cover all of the permutations;
consult the zlib manual at http://www.zlib.net/manual.html for authoritative
information.
-For reading and writing ``.gz`` files see the :mod:`gzip` module. For
-other archive formats, see the :mod:`bz2`, :mod:`zipfile`, and
+For reading and writing ``.gz`` files see the :mod:`gzip` module. For other
+related file formats, see the :mod:`bz2`, :mod:`lzma`, :mod:`zipfile`, and
:mod:`tarfile` modules.
The available exception and functions in this module are:
@@ -122,6 +122,7 @@ The available exception and functions in this module are:
won't fit into memory at once. The *wbits* parameter controls the size of the
window buffer.
+
Compression objects support the following methods:
@@ -152,7 +153,7 @@ Compression objects support the following methods:
compress a set of data that share a common initial prefix.
-Decompression objects support the following methods, and two attributes:
+Decompression objects support the following methods and attributes:
.. attribute:: Decompress.unused_data
@@ -162,13 +163,6 @@ Decompression objects support the following methods, and two attributes:
available. If the whole bytestring turned out to contain compressed data, this is
``b""``, an empty bytes object.
- The only way to determine where a bytestring of compressed data ends is by actually
- decompressing it. This means that when compressed data is contained part of a
- larger file, you can only find the end of it by reading data and feeding it
- followed by some non-empty bytestring into a decompression object's
- :meth:`decompress` method until the :attr:`unused_data` attribute is no longer
- empty.
-
.. attribute:: Decompress.unconsumed_tail
@@ -179,6 +173,17 @@ Decompression objects support the following methods, and two attributes:
:meth:`decompress` method call in order to get correct output.
+.. attribute:: Decompress.eof
+
+ A boolean indicating whether the end of the compressed data stream has been
+ reached.
+
+ This makes it possible to distinguish between a properly-formed compressed
+ stream, and an incomplete or truncated one.
+
+ .. versionadded:: 3.3
+
+
.. method:: Decompress.decompress(data[, max_length])
Decompress *data*, returning a bytes object containing the uncompressed data
@@ -213,6 +218,24 @@ Decompression objects support the following methods, and two attributes:
seeks into the stream at a future point.
+Information about the version of the zlib library in use is available through
+the following constants:
+
+
+.. data:: ZLIB_VERSION
+
+ The version string of the zlib library that was used for building the module.
+ This may be different from the zlib library actually used at runtime, which
+ is available as :const:`ZLIB_RUNTIME_VERSION`.
+
+
+.. data:: ZLIB_RUNTIME_VERSION
+
+ The version string of the zlib library actually loaded by the interpreter.
+
+ .. versionadded:: 3.3
+
+
.. seealso::
Module :mod:`gzip`
diff --git a/Doc/license.rst b/Doc/license.rst
index 8693a0f..5050ff0 100644
--- a/Doc/license.rst
+++ b/Doc/license.rst
@@ -118,6 +118,8 @@ been GPL-compatible; the table below summarizes the various releases.
+----------------+--------------+------------+------------+-----------------+
| 3.2.2 | 3.2.1 | 2011 | PSF | yes |
+----------------+--------------+------------+------------+-----------------+
+| 3.3 | 3.2 | 2012 | PSF | yes |
++----------------+--------------+------------+------------+-----------------+
.. note::
diff --git a/Doc/packaging/builtdist.rst b/Doc/packaging/builtdist.rst
new file mode 100644
index 0000000..1d9a349
--- /dev/null
+++ b/Doc/packaging/builtdist.rst
@@ -0,0 +1,302 @@
+.. _packaging-built-dist:
+
+****************************
+Creating Built Distributions
+****************************
+
+A "built distribution" is what you're probably used to thinking of either as a
+"binary package" or an "installer" (depending on your background). It's not
+necessarily binary, though, because it might contain only Python source code
+and/or byte-code; and we don't call it a package, because that word is already
+spoken for in Python. (And "installer" is a term specific to the world of
+mainstream desktop systems.)
+
+A built distribution is how you make life as easy as possible for installers of
+your module distribution: for users of RPM-based Linux systems, it's a binary
+RPM; for Windows users, it's an executable installer; for Debian-based Linux
+users, it's a Debian package; and so forth. Obviously, no one person will be
+able to create built distributions for every platform under the sun, so the
+Distutils are designed to enable module developers to concentrate on their
+specialty---writing code and creating source distributions---while an
+intermediary species called *packagers* springs up to turn source distributions
+into built distributions for as many platforms as there are packagers.
+
+Of course, the module developer could be his own packager; or the packager could
+be a volunteer "out there" somewhere who has access to a platform which the
+original developer does not; or it could be software periodically grabbing new
+source distributions and turning them into built distributions for as many
+platforms as the software has access to. Regardless of who they are, a packager
+uses the setup script and the :command:`bdist` command family to generate built
+distributions.
+
+As a simple example, if I run the following command in the Distutils source
+tree::
+
+ python setup.py bdist
+
+then the Distutils builds my module distribution (the Distutils itself in this
+case), does a "fake" installation (also in the :file:`build` directory), and
+creates the default type of built distribution for my platform. The default
+format for built distributions is a "dumb" tar file on Unix, and a simple
+executable installer on Windows. (That tar file is considered "dumb" because it
+has to be unpacked in a specific location to work.)
+
+Thus, the above command on a Unix system creates
+:file:`Distutils-1.0.{plat}.tar.gz`; unpacking this tarball from the right place
+installs the Distutils just as though you had downloaded the source distribution
+and run ``python setup.py install``. (The "right place" is either the root of
+the filesystem or Python's :file:`{prefix}` directory, depending on the options
+given to the :command:`bdist_dumb` command; the default is to make dumb
+distributions relative to :file:`{prefix}`.)
+
+Obviously, for pure Python distributions, this isn't any simpler than just
+running ``python setup.py install``\ ---but for non-pure distributions, which
+include extensions that would need to be compiled, it can mean the difference
+between someone being able to use your extensions or not. And creating "smart"
+built distributions, such as an executable installer for
+Windows, is far more convenient for users even if your distribution doesn't
+include any extensions.
+
+The :command:`bdist` command has a :option:`--formats` option, similar to the
+:command:`sdist` command, which you can use to select the types of built
+distribution to generate: for example, ::
+
+ python setup.py bdist --format=zip
+
+would, when run on a Unix system, create :file:`Distutils-1.0.{plat}.zip`\
+---again, this archive would be unpacked from the root directory to install the
+Distutils.
+
+The available formats for built distributions are:
+
++-------------+------------------------------+---------+
+| Format | Description | Notes |
++=============+==============================+=========+
+| ``gztar`` | gzipped tar file | (1),(3) |
+| | (:file:`.tar.gz`) | |
++-------------+------------------------------+---------+
+| ``tar`` | tar file (:file:`.tar`) | \(3) |
++-------------+------------------------------+---------+
+| ``zip`` | zip file (:file:`.zip`) | (2),(4) |
++-------------+------------------------------+---------+
+| ``wininst`` | self-extracting ZIP file for | \(4) |
+| | Windows | |
++-------------+------------------------------+---------+
+| ``msi`` | Microsoft Installer. | |
++-------------+------------------------------+---------+
+
+
+Notes:
+
+(1)
+ default on Unix
+
+(2)
+ default on Windows
+
+(3)
+ requires external utilities: :program:`tar` and possibly one of :program:`gzip`
+ or :program:`bzip2`
+
+(4)
+ requires either external :program:`zip` utility or :mod:`zipfile` module (part
+ of the standard Python library since Python 1.6)
+
+You don't have to use the :command:`bdist` command with the :option:`--formats`
+option; you can also use the command that directly implements the format you're
+interested in. Some of these :command:`bdist` "sub-commands" actually generate
+several similar formats; for instance, the :command:`bdist_dumb` command
+generates all the "dumb" archive formats (``tar``, ``gztar``, and
+``zip``). The :command:`bdist` sub-commands, and the formats generated by
+each, are:
+
++--------------------------+-----------------------+
+| Command | Formats |
++==========================+=======================+
+| :command:`bdist_dumb` | tar, gztar, zip |
++--------------------------+-----------------------+
+| :command:`bdist_wininst` | wininst |
++--------------------------+-----------------------+
+| :command:`bdist_msi` | msi |
++--------------------------+-----------------------+
+
+The following sections give details on the individual :command:`bdist_\*`
+commands.
+
+
+.. _packaging-creating-dumb:
+
+Creating dumb built distributions
+=================================
+
+.. XXX Need to document absolute vs. prefix-relative packages here, but first
+ I have to implement it!
+
+
+.. _packaging-creating-wininst:
+
+Creating Windows Installers
+===========================
+
+Executable installers are the natural format for binary distributions on
+Windows. They display a nice graphical user interface, display some information
+about the module distribution to be installed taken from the metadata in the
+setup script, let the user select a few options, and start or cancel the
+installation.
+
+Since the metadata is taken from the setup script, creating Windows installers
+is usually as easy as running::
+
+ python setup.py bdist_wininst
+
+or the :command:`bdist` command with the :option:`--formats` option::
+
+ python setup.py bdist --formats=wininst
+
+If you have a pure module distribution (only containing pure Python modules and
+packages), the resulting installer will be version independent and have a name
+like :file:`foo-1.0.win32.exe`. These installers can even be created on Unix
+platforms or Mac OS X.
+
+If you have a non-pure distribution, the extensions can only be created on a
+Windows platform, and will be Python version dependent. The installer filename
+will reflect this and now has the form :file:`foo-1.0.win32-py2.0.exe`. You
+have to create a separate installer for every Python version you want to
+support.
+
+The installer will try to compile pure modules into :term:`bytecode` after installation
+on the target system in normal and optimizing mode. If you don't want this to
+happen for some reason, you can run the :command:`bdist_wininst` command with
+the :option:`--no-target-compile` and/or the :option:`--no-target-optimize`
+option.
+
+By default the installer will display the cool "Python Powered" logo when it is
+run, but you can also supply your own 152x261 bitmap which must be a Windows
+:file:`.bmp` file with the :option:`--bitmap` option.
+
+The installer will also display a large title on the desktop background window
+when it is run, which is constructed from the name of your distribution and the
+version number. This can be changed to another text by using the
+:option:`--title` option.
+
+The installer file will be written to the "distribution directory" --- normally
+:file:`dist/`, but customizable with the :option:`--dist-dir` option.
+
+.. _packaging-cross-compile-windows:
+
+Cross-compiling on Windows
+==========================
+
+Starting with Python 2.6, packaging is capable of cross-compiling between
+Windows platforms. In practice, this means that with the correct tools
+installed, you can use a 32bit version of Windows to create 64bit extensions
+and vice-versa.
+
+To build for an alternate platform, specify the :option:`--plat-name` option
+to the build command. Valid values are currently 'win32', 'win-amd64' and
+'win-ia64'. For example, on a 32bit version of Windows, you could execute::
+
+ python setup.py build --plat-name=win-amd64
+
+to build a 64bit version of your extension. The Windows Installers also
+support this option, so the command::
+
+ python setup.py build --plat-name=win-amd64 bdist_wininst
+
+would create a 64bit installation executable on your 32bit version of Windows.
+
+To cross-compile, you must download the Python source code and cross-compile
+Python itself for the platform you are targetting - it is not possible from a
+binary installtion of Python (as the .lib etc file for other platforms are
+not included.) In practice, this means the user of a 32 bit operating
+system will need to use Visual Studio 2008 to open the
+:file:`PCBuild/PCbuild.sln` solution in the Python source tree and build the
+"x64" configuration of the 'pythoncore' project before cross-compiling
+extensions is possible.
+
+Note that by default, Visual Studio 2008 does not install 64bit compilers or
+tools. You may need to reexecute the Visual Studio setup process and select
+these tools (using Control Panel->[Add/Remove] Programs is a convenient way to
+check or modify your existing install.)
+
+.. _packaging-postinstallation-script:
+
+The Postinstallation script
+---------------------------
+
+Starting with Python 2.3, a postinstallation script can be specified with the
+:option:`--install-script` option. The basename of the script must be
+specified, and the script filename must also be listed in the scripts argument
+to the setup function.
+
+This script will be run at installation time on the target system after all the
+files have been copied, with ``argv[1]`` set to :option:`-install`, and again at
+uninstallation time before the files are removed with ``argv[1]`` set to
+:option:`-remove`.
+
+The installation script runs embedded in the windows installer, every output
+(``sys.stdout``, ``sys.stderr``) is redirected into a buffer and will be
+displayed in the GUI after the script has finished.
+
+Some functions especially useful in this context are available as additional
+built-in functions in the installation script.
+
+.. currentmodule:: bdist_wininst-postinst-script
+
+.. function:: directory_created(path)
+ file_created(path)
+
+ These functions should be called when a directory or file is created by the
+ postinstall script at installation time. It will register *path* with the
+ uninstaller, so that it will be removed when the distribution is uninstalled.
+ To be safe, directories are only removed if they are empty.
+
+
+.. function:: get_special_folder_path(csidl_string)
+
+ This function can be used to retrieve special folder locations on Windows like
+ the Start Menu or the Desktop. It returns the full path to the folder.
+ *csidl_string* must be one of the following strings::
+
+ "CSIDL_APPDATA"
+
+ "CSIDL_COMMON_STARTMENU"
+ "CSIDL_STARTMENU"
+
+ "CSIDL_COMMON_DESKTOPDIRECTORY"
+ "CSIDL_DESKTOPDIRECTORY"
+
+ "CSIDL_COMMON_STARTUP"
+ "CSIDL_STARTUP"
+
+ "CSIDL_COMMON_PROGRAMS"
+ "CSIDL_PROGRAMS"
+
+ "CSIDL_FONTS"
+
+ If the folder cannot be retrieved, :exc:`OSError` is raised.
+
+ Which folders are available depends on the exact Windows version, and probably
+ also the configuration. For details refer to Microsoft's documentation of the
+ :c:func:`SHGetSpecialFolderPath` function.
+
+
+.. function:: create_shortcut(target, description, filename[, arguments[, workdir[, iconpath[, iconindex]]]])
+
+ This function creates a shortcut. *target* is the path to the program to be
+ started by the shortcut. *description* is the description of the shortcut.
+ *filename* is the title of the shortcut that the user will see. *arguments*
+ specifies the command-line arguments, if any. *workdir* is the working directory
+ for the program. *iconpath* is the file containing the icon for the shortcut,
+ and *iconindex* is the index of the icon in the file *iconpath*. Again, for
+ details consult the Microsoft documentation for the :class:`IShellLink`
+ interface.
+
+
+Vista User Access Control (UAC)
+===============================
+
+Starting with Python 2.6, bdist_wininst supports a :option:`--user-access-control`
+option. The default is 'none' (meaning no UAC handling is done), and other
+valid values are 'auto' (meaning prompt for UAC elevation if Python was
+installed for all users) and 'force' (meaning always prompt for elevation).
diff --git a/Doc/packaging/commandhooks.rst b/Doc/packaging/commandhooks.rst
new file mode 100644
index 0000000..b261d00
--- /dev/null
+++ b/Doc/packaging/commandhooks.rst
@@ -0,0 +1,47 @@
+.. TODO integrate this in commandref and configfile
+
+.. _packaging-command-hooks:
+
+=============
+Command hooks
+=============
+
+Packaging provides a way of extending its commands by the use of pre- and
+post-command hooks. Hooks are Python functions (or any callable object) that
+take a command object as argument. They're specified in :ref:`config files
+<packaging-config-filenames>` using their fully qualified names. After a
+command is finalized (its options are processed), the pre-command hooks are
+executed, then the command itself is run, and finally the post-command hooks are
+executed.
+
+See also global setup hooks in :ref:`setupcfg-spec`.
+
+
+.. _packaging-finding-hooks:
+
+Finding hooks
+=============
+
+As a hook is configured with a Python dotted name, it must either be defined in
+a module installed on the system, or in a module present in the project
+directory, where the :file:`setup.cfg` file lives::
+
+ # file: _setuphooks.py
+
+ def hook(install_cmd):
+ metadata = install_cmd.dist.metadata
+ print('Hooked while installing %r %s!' % (metadata['Name'],
+ metadata['Version']))
+
+Then you need to configure it in :file:`setup.cfg`::
+
+ [install_dist]
+ pre-hook.a = _setuphooks.hook
+
+Packaging will add the project directory to :data:`sys.path` and find the
+``_setuphooks`` module.
+
+Hooks defined in different config files (system-wide, user-wide and
+project-wide) do not override each other as long as they are specified with
+different aliases (additional names after the dot). The alias in the example
+above is ``a``.
diff --git a/Doc/packaging/commandref.rst b/Doc/packaging/commandref.rst
new file mode 100644
index 0000000..2165b56
--- /dev/null
+++ b/Doc/packaging/commandref.rst
@@ -0,0 +1,374 @@
+.. _packaging-command-reference:
+
+*****************
+Command Reference
+*****************
+
+This reference briefly documents all standard Packaging commands and some of
+their options.
+
+.. FIXME does not work: Use pysetup run --help-commands to list all
+ standard and extra commands availavble on your system, with their
+ description. Use pysetup run <command> --help to get help about the options
+ of one command.
+
+.. XXX sections from this document should be merged with other docs (e.g. check
+ and upload with uploading.rst, install_* with install/install.rst, etc.);
+ there is no value in partially duplicating information. this file could
+ however serve as an index, i.e. just a list of all commands with links to
+ every section that describes options or usage
+
+
+Preparing distributions
+=======================
+
+:command:`check`
+----------------
+
+Perform some tests on the metadata of a distribution.
+
+For example, it verifies that all required metadata fields are provided in the
+:file:`setup.cfg` file.
+
+.. TODO document reST checks
+
+
+:command:`test`
+---------------
+
+Run a test suite.
+
+When doing test-driven development, or running automated builds that need
+testing before they are installed for downloading or use, it's often useful to
+be able to run a project's unit tests without actually installing the project
+anywhere. The :command:`test` command runs project's unit tests without
+actually installing it, by temporarily putting the project's source on
+:data:`sys.path`, after first running :command:`build_ext -i` to ensure that any
+C extensions are built.
+
+You can use this command in one of two ways: either by specifying a
+unittest-compatible test suite for your project (or any callable that returns
+it) or by passing a test runner function that will run your tests and display
+results in the console. Both options take a Python dotted name in the form
+``package.module.callable`` to specify the object to use.
+
+If none of these options are specified, Packaging will try to perform test
+discovery using either unittest (for Python 3.2 and higher) or unittest2 (for
+older versions, if installed).
+
+.. this is a pseudo-command name used to disambiguate the options in indexes and
+ links
+.. program:: packaging test
+
+.. cmdoption:: --suite=NAME, -s NAME
+
+ Specify the test suite (or module, class, or method) to be run. The default
+ for this option can be set by in the project's :file:`setup.cfg` file:
+
+ .. code-block:: cfg
+
+ [test]
+ suite = mypackage.tests.get_all_tests
+
+.. cmdoption:: --runner=NAME, -r NAME
+
+ Specify the test runner to be called.
+
+
+:command:`config`
+-----------------
+
+Perform distribution configuration.
+
+
+The build step
+==============
+
+This step is mainly useful to compile C/C++ libraries or extension modules. The
+build commands can be run manually to check for syntax errors or packaging
+issues (for example if the addition of a new source file was forgotten in the
+:file:`setup.cfg` file), and is also run automatically by commands which need
+it. Packaging checks the mtime of source and built files to avoid re-building
+if it's not necessary.
+
+
+:command:`build`
+----------------
+
+Build all files of a distribution, delegating to the other :command:`build_*`
+commands to do the work.
+
+
+:command:`build_clib`
+---------------------
+
+Build C libraries.
+
+
+:command:`build_ext`
+--------------------
+
+Build C/C++ extension modules.
+
+
+:command:`build_py`
+-------------------
+
+Build the Python modules (just copy them to the build directory) and
+:term:`byte-compile <bytecode>` them to :file:`.pyc` and/or :file:`.pyo` files.
+
+The byte compilation is controlled by two sets of options:
+
+- ``--compile`` and ``--no-compile`` are used to control the creation of
+ :file:`.pyc` files; the default is ``--no-compile``.
+
+- ``--optimize N`` (or ``-ON``) is used to control the creation of :file:`.pyo`
+ files: ``-O1`` turns on basic optimizations, ``-O2`` also discards docstrings,
+ ``-O0`` does not create :file:`.pyo` files; the default is ``-O0``.
+
+You can mix and match these options: for example, ``--no-compile --optimize 2``
+will create :file:`.pyo` files but no :file:`.pyc` files.
+
+.. XXX these option roles do not work
+
+Calling Python with :option:`-O` or :option:`-B` does not control the creation
+of bytecode files, only the options described above do.
+
+
+:command:`build_scripts`
+------------------------
+Build the scripts (just copy them to the build directory and adjust their
+shebang if they're Python scripts).
+
+
+:command:`clean`
+----------------
+
+Clean the build tree of the release.
+
+.. program:: packaging clean
+
+.. cmdoption:: --all, -a
+
+ Remove build directories for modules, scripts, etc., not only temporary build
+ by-products.
+
+
+Creating source and built distributions
+=======================================
+
+:command:`sdist`
+----------------
+
+Build a source distribution for a release.
+
+It is recommended that you always build and upload a source distribution. Users
+of OSes with easy access to compilers and users of advanced packaging tools will
+prefer to compile from source rather than using pre-built distributions. For
+Windows users, providing a binary installer is also recommended practice.
+
+
+:command:`bdist`
+----------------
+
+Build a binary distribution for a release.
+
+This command will call other :command:`bdist_*` commands to create one or more
+distributions depending on the options given. The default is to create a
+.tar.gz archive on Unix and a zip archive on Windows or OS/2.
+
+.. program:: packaging bdist
+
+.. cmdoption:: --formats
+
+ Binary formats to build (comma-separated list).
+
+.. cmdoption:: --show-formats
+
+ Dump list of available formats.
+
+
+:command:`bdist_dumb`
+---------------------
+
+Build a "dumb" installer, a simple archive of files that could be unpacked under
+``$prefix`` or ``$exec_prefix``.
+
+
+:command:`bdist_wininst`
+------------------------
+
+Build a Windows installer.
+
+
+:command:`bdist_msi`
+--------------------
+
+Build a `Microsoft Installer`_ (.msi) file.
+
+.. _Microsoft Installer: http://msdn.microsoft.com/en-us/library/cc185688(VS.85).aspx
+
+In most cases, the :command:`bdist_msi` installer is a better choice than the
+:command:`bdist_wininst` installer, because it provides better support for Win64
+platforms, allows administrators to perform non-interactive installations, and
+allows installation through group policies.
+
+
+Publishing distributions
+========================
+
+:command:`register`
+-------------------
+
+This command registers the current release with the Python Package Index. This
+is described in more detail in :PEP:`301`.
+
+.. TODO explain user and project registration with the web UI
+
+
+:command:`upload`
+-----------------
+
+Upload source and/or binary distributions to PyPI.
+
+The distributions have to be built on the same command line as the
+:command:`upload` command; see :ref:`packaging-package-upload` for more info.
+
+.. program:: packaging upload
+
+.. cmdoption:: --sign, -s
+
+ Sign each uploaded file using GPG (GNU Privacy Guard). The ``gpg`` program
+ must be available for execution on the system ``PATH``.
+
+.. cmdoption:: --identity=NAME, -i NAME
+
+ Specify the identity or key name for GPG to use when signing. The value of
+ this option will be passed through the ``--local-user`` option of the
+ ``gpg`` program.
+
+.. cmdoption:: --show-response
+
+ Display the full response text from server; this is useful for debugging
+ PyPI problems.
+
+.. cmdoption:: --repository=URL, -r URL
+
+ The URL of the repository to upload to. Defaults to
+ http://pypi.python.org/pypi (i.e., the main PyPI installation).
+
+.. cmdoption:: --upload-docs
+
+ Also run :command:`upload_docs`. Mainly useful as a default value in
+ :file:`setup.cfg` (on the command line, it's shorter to just type both
+ commands).
+
+
+:command:`upload_docs`
+----------------------
+
+Upload HTML documentation to PyPI.
+
+PyPI now supports publishing project documentation at a URI of the form
+``http://packages.python.org/<project>``. :command:`upload_docs` will create
+the necessary zip file out of a documentation directory and will post to the
+repository.
+
+Note that to upload the documentation of a project, the corresponding version
+must already be registered with PyPI, using the :command:`register` command ---
+just like with :command:`upload`.
+
+Assuming there is an ``Example`` project with documentation in the subdirectory
+:file:`docs`, for example::
+
+ Example/
+ example.py
+ setup.cfg
+ docs/
+ build/
+ html/
+ index.html
+ tips_tricks.html
+ conf.py
+ index.txt
+ tips_tricks.txt
+
+You can simply specify the directory with the HTML files in your
+:file:`setup.cfg` file:
+
+.. code-block:: cfg
+
+ [upload_docs]
+ upload-dir = docs/build/html
+
+
+.. program:: packaging upload_docs
+
+.. cmdoption:: --upload-dir
+
+ The directory to be uploaded to the repository. By default documentation
+ is searched for in ``docs`` (or ``doc``) directory in project root.
+
+.. cmdoption:: --show-response
+
+ Display the full response text from server; this is useful for debugging
+ PyPI problems.
+
+.. cmdoption:: --repository=URL, -r URL
+
+ The URL of the repository to upload to. Defaults to
+ http://pypi.python.org/pypi (i.e., the main PyPI installation).
+
+
+The install step
+================
+
+These commands are used by end-users of a project using :program:`pysetup` or
+another compatible installer. Each command will run the corresponding
+:command:`build_*` command and then move the built files to their destination on
+the target system.
+
+
+:command:`install_dist`
+-----------------------
+
+Install a distribution, delegating to the other :command:`install_*` commands to
+do the work. See :ref:`packaging-how-install-works` for complete usage
+instructions.
+
+
+:command:`install_data`
+-----------------------
+
+Install data files.
+
+
+:command:`install_distinfo`
+---------------------------
+
+Install files recording details of the installation as specified in :PEP:`376`.
+
+
+:command:`install_headers`
+--------------------------
+
+Install C/C++ header files.
+
+
+:command:`install_lib`
+----------------------
+
+Install all modules (extensions and pure Python).
+
+.. XXX what about C libraries created with build_clib?
+
+Similarly to ``build_py``, there are options to control the compilation of
+Python code to :term:`bytecode` files (see above). By default, :file:`.pyc`
+files will be created (``--compile``) and :file:`.pyo` files will not
+(``--optimize 0``).
+
+
+:command:`install_scripts`
+--------------------------
+
+Install scripts.
diff --git a/Doc/packaging/configfile.rst b/Doc/packaging/configfile.rst
new file mode 100644
index 0000000..825b5cb
--- /dev/null
+++ b/Doc/packaging/configfile.rst
@@ -0,0 +1,125 @@
+.. _packaging-setup-config:
+
+************************************
+Writing the Setup Configuration File
+************************************
+
+Often, it's not possible to write down everything needed to build a distribution
+*a priori*: you may need to get some information from the user, or from the
+user's system, in order to proceed. As long as that information is fairly
+simple---a list of directories to search for C header files or libraries, for
+example---then providing a configuration file, :file:`setup.cfg`, for users to
+edit is a cheap and easy way to solicit it. Configuration files also let you
+provide default values for any command option, which the installer can then
+override either on the command line or by editing the config file.
+
+The setup configuration file is a useful middle-ground between the setup script
+---which, ideally, would be opaque to installers [#]_---and the command line to
+the setup script, which is outside of your control and entirely up to the
+installer. In fact, :file:`setup.cfg` (and any other Distutils configuration
+files present on the target system) are processed after the contents of the
+setup script, but before the command line. This has several useful
+consequences:
+
+.. If you have more advanced needs, such as determining which extensions to
+ build based on what capabilities are present on the target system, then you
+ need the Distutils auto-configuration facility. This started to appear in
+ Distutils 0.9 but, as of this writing, isn't mature or stable enough yet
+ for real-world use.
+
+* installers can override some of what you put in :file:`setup.py` by editing
+ :file:`setup.cfg`
+
+* you can provide non-standard defaults for options that are not easily set in
+ :file:`setup.py`
+
+* installers can override anything in :file:`setup.cfg` using the command-line
+ options to :file:`setup.py`
+
+The basic syntax of the configuration file is simple::
+
+ [command]
+ option = value
+ ...
+
+where *command* is one of the Distutils commands (e.g. :command:`build_py`,
+:command:`install_dist`), and *option* is one of the options that command supports.
+Any number of options can be supplied for each command, and any number of
+command sections can be included in the file. Blank lines are ignored, as are
+comments, which run from a ``'#'`` character until the end of the line. Long
+option values can be split across multiple lines simply by indenting the
+continuation lines.
+
+You can find out the list of options supported by a particular command with the
+universal :option:`--help` option, e.g. ::
+
+ > python setup.py --help build_ext
+ [...]
+ Options for 'build_ext' command:
+ --build-lib (-b) directory for compiled extension modules
+ --build-temp (-t) directory for temporary files (build by-products)
+ --inplace (-i) ignore build-lib and put compiled extensions into the
+ source directory alongside your pure Python modules
+ --include-dirs (-I) list of directories to search for header files
+ --define (-D) C preprocessor macros to define
+ --undef (-U) C preprocessor macros to undefine
+ --swig-opts list of SWIG command-line options
+ [...]
+
+.. XXX do we want to support ``setup.py --help metadata``?
+
+Note that an option spelled :option:`--foo-bar` on the command line is spelled
+:option:`foo_bar` in configuration files.
+
+For example, say you want your extensions to be built "in-place"---that is, you
+have an extension :mod:`pkg.ext`, and you want the compiled extension file
+(:file:`ext.so` on Unix, say) to be put in the same source directory as your
+pure Python modules :mod:`pkg.mod1` and :mod:`pkg.mod2`. You can always use the
+:option:`--inplace` option on the command line to ensure this::
+
+ python setup.py build_ext --inplace
+
+But this requires that you always specify the :command:`build_ext` command
+explicitly, and remember to provide :option:`--inplace`. An easier way is to
+"set and forget" this option, by encoding it in :file:`setup.cfg`, the
+configuration file for this distribution::
+
+ [build_ext]
+ inplace = 1
+
+This will affect all builds of this module distribution, whether or not you
+explicitly specify :command:`build_ext`. If you include :file:`setup.cfg` in
+your source distribution, it will also affect end-user builds---which is
+probably a bad idea for this option, since always building extensions in-place
+would break installation of the module distribution. In certain peculiar cases,
+though, modules are built right in their installation directory, so this is
+conceivably a useful ability. (Distributing extensions that expect to be built
+in their installation directory is almost always a bad idea, though.)
+
+Another example: certain commands take options that vary from project to
+project but not depending on the installation system, for example,
+:command:`test` needs to know where your test suite is located and what test
+runner to use; likewise, :command:`upload_docs` can find HTML documentation in
+a :file:`doc` or :file:`docs` directory, but needs an option to find files in
+:file:`docs/build/html`. Instead of having to type out these options each
+time you want to run the command, you can put them in the project's
+:file:`setup.cfg`::
+
+ [test]
+ suite = packaging.tests
+
+ [upload_docs]
+ upload-dir = docs/build/html
+
+
+.. seealso::
+
+ :ref:`packaging-config-syntax` in "Installing Python Projects"
+ More information on the configuration files is available in the manual for
+ system administrators.
+
+
+.. rubric:: Footnotes
+
+.. [#] This ideal probably won't be achieved until auto-configuration is fully
+ supported by the Distutils.
diff --git a/Doc/packaging/examples.rst b/Doc/packaging/examples.rst
new file mode 100644
index 0000000..594ade0
--- /dev/null
+++ b/Doc/packaging/examples.rst
@@ -0,0 +1,334 @@
+.. _packaging-examples:
+
+********
+Examples
+********
+
+This chapter provides a number of basic examples to help get started with
+Packaging.
+
+
+.. _packaging-pure-mod:
+
+Pure Python distribution (by module)
+====================================
+
+If you're just distributing a couple of modules, especially if they don't live
+in a particular package, you can specify them individually using the
+:option:`py_modules` option in the setup script.
+
+In the simplest case, you'll have two files to worry about: a setup script and
+the single module you're distributing, :file:`foo.py` in this example::
+
+ <root>/
+ setup.py
+ foo.py
+
+(In all diagrams in this section, *<root>* will refer to the distribution root
+directory.) A minimal setup script to describe this situation would be::
+
+ from packaging.core import setup
+ setup(name='foo',
+ version='1.0',
+ py_modules=['foo'])
+
+Note that the name of the distribution is specified independently with the
+:option:`name` option, and there's no rule that says it has to be the same as
+the name of the sole module in the distribution (although that's probably a good
+convention to follow). However, the distribution name is used to generate
+filenames, so you should stick to letters, digits, underscores, and hyphens.
+
+Since :option:`py_modules` is a list, you can of course specify multiple
+modules, e.g. if you're distributing modules :mod:`foo` and :mod:`bar`, your
+setup might look like this::
+
+ <root>/
+ setup.py
+ foo.py
+ bar.py
+
+and the setup script might be ::
+
+ from packaging.core import setup
+ setup(name='foobar',
+ version='1.0',
+ py_modules=['foo', 'bar'])
+
+You can put module source files into another directory, but if you have enough
+modules to do that, it's probably easier to specify modules by package rather
+than listing them individually.
+
+
+.. _packaging-pure-pkg:
+
+Pure Python distribution (by package)
+=====================================
+
+If you have more than a couple of modules to distribute, especially if they are
+in multiple packages, it's probably easier to specify whole packages rather than
+individual modules. This works even if your modules are not in a package; you
+can just tell the Distutils to process modules from the root package, and that
+works the same as any other package (except that you don't have to have an
+:file:`__init__.py` file).
+
+The setup script from the last example could also be written as ::
+
+ from packaging.core import setup
+ setup(name='foobar',
+ version='1.0',
+ packages=[''])
+
+(The empty string stands for the root package.)
+
+If those two files are moved into a subdirectory, but remain in the root
+package, e.g.::
+
+ <root>/
+ setup.py
+ src/
+ foo.py
+ bar.py
+
+then you would still specify the root package, but you have to tell the
+Distutils where source files in the root package live::
+
+ from packaging.core import setup
+ setup(name='foobar',
+ version='1.0',
+ package_dir={'': 'src'},
+ packages=[''])
+
+More typically, though, you will want to distribute multiple modules in the same
+package (or in sub-packages). For example, if the :mod:`foo` and :mod:`bar`
+modules belong in package :mod:`foobar`, one way to lay out your source tree is
+
+::
+
+ <root>/
+ setup.py
+ foobar/
+ __init__.py
+ foo.py
+ bar.py
+
+This is in fact the default layout expected by the Distutils, and the one that
+requires the least work to describe in your setup script::
+
+ from packaging.core import setup
+ setup(name='foobar',
+ version='1.0',
+ packages=['foobar'])
+
+If you want to put modules in directories not named for their package, then you
+need to use the :option:`package_dir` option again. For example, if the
+:file:`src` directory holds modules in the :mod:`foobar` package::
+
+ <root>/
+ setup.py
+ src/
+ __init__.py
+ foo.py
+ bar.py
+
+an appropriate setup script would be ::
+
+ from packaging.core import setup
+ setup(name='foobar',
+ version='1.0',
+ package_dir={'foobar': 'src'},
+ packages=['foobar'])
+
+Or, you might put modules from your main package right in the distribution
+root::
+
+ <root>/
+ setup.py
+ __init__.py
+ foo.py
+ bar.py
+
+in which case your setup script would be ::
+
+ from packaging.core import setup
+ setup(name='foobar',
+ version='1.0',
+ package_dir={'foobar': ''},
+ packages=['foobar'])
+
+(The empty string also stands for the current directory.)
+
+If you have sub-packages, they must be explicitly listed in :option:`packages`,
+but any entries in :option:`package_dir` automatically extend to sub-packages.
+(In other words, the Distutils does *not* scan your source tree, trying to
+figure out which directories correspond to Python packages by looking for
+:file:`__init__.py` files.) Thus, if the default layout grows a sub-package::
+
+ <root>/
+ setup.py
+ foobar/
+ __init__.py
+ foo.py
+ bar.py
+ subfoo/
+ __init__.py
+ blah.py
+
+then the corresponding setup script would be ::
+
+ from packaging.core import setup
+ setup(name='foobar',
+ version='1.0',
+ packages=['foobar', 'foobar.subfoo'])
+
+(Again, the empty string in :option:`package_dir` stands for the current
+directory.)
+
+
+.. _packaging-single-ext:
+
+Single extension module
+=======================
+
+Extension modules are specified using the :option:`ext_modules` option.
+:option:`package_dir` has no effect on where extension source files are found;
+it only affects the source for pure Python modules. The simplest case, a
+single extension module in a single C source file, is::
+
+ <root>/
+ setup.py
+ foo.c
+
+If the :mod:`foo` extension belongs in the root package, the setup script for
+this could be ::
+
+ from packaging.core import setup, Extension
+ setup(name='foobar',
+ version='1.0',
+ ext_modules=[Extension('foo', ['foo.c'])])
+
+If the extension actually belongs in a package, say :mod:`foopkg`, then
+
+With exactly the same source tree layout, this extension can be put in the
+:mod:`foopkg` package simply by changing the name of the extension::
+
+ from packaging.core import setup, Extension
+ setup(name='foobar',
+ version='1.0',
+ packages=['foopkg'],
+ ext_modules=[Extension('foopkg.foo', ['foo.c'])])
+
+
+Checking metadata
+=================
+
+The ``check`` command allows you to verify if your project's metadata
+meets the minimum requirements to build a distribution.
+
+To run it, just call it using your :file:`setup.py` script. If something is
+missing, ``check`` will display a warning.
+
+Let's take an example with a simple script::
+
+ from packaging.core import setup
+
+ setup(name='foobar')
+
+.. TODO configure logging StreamHandler to match this output
+
+Running the ``check`` command will display some warnings::
+
+ $ python setup.py check
+ running check
+ warning: check: missing required metadata: version, home_page
+ warning: check: missing metadata: either (author and author_email) or
+ (maintainer and maintainer_email) must be supplied
+
+
+If you use the reStructuredText syntax in the ``long_description`` field and
+`Docutils <http://docutils.sourceforge.net/>`_ is installed you can check if
+the syntax is fine with the ``check`` command, using the ``restructuredtext``
+option.
+
+For example, if the :file:`setup.py` script is changed like this::
+
+ from packaging.core import setup
+
+ desc = """\
+ Welcome to foobar!
+ ===============
+
+ This is the description of the ``foobar`` project.
+ """
+
+ setup(name='foobar',
+ version='1.0',
+ author=u'Tarek Ziadé',
+ author_email='tarek@ziade.org',
+ summary='Foobar utilities'
+ description=desc,
+ home_page='http://example.com')
+
+Where the long description is broken, ``check`` will be able to detect it
+by using the :mod:`docutils` parser::
+
+ $ python setup.py check --restructuredtext
+ running check
+ warning: check: Title underline too short. (line 2)
+ warning: check: Could not finish the parsing.
+
+
+.. _packaging-reading-metadata:
+
+Reading the metadata
+====================
+
+The :func:`packaging.core.setup` function provides a command-line interface
+that allows you to query the metadata fields of a project through the
+:file:`setup.py` script of a given project::
+
+ $ python setup.py --name
+ foobar
+
+This call reads the ``name`` metadata by running the
+:func:`packaging.core.setup` function. When a source or binary
+distribution is created with Distutils, the metadata fields are written
+in a static file called :file:`PKG-INFO`. When a Distutils-based project is
+installed in Python, the :file:`PKG-INFO` file is copied alongside the modules
+and packages of the distribution under :file:`NAME-VERSION-pyX.X.egg-info`,
+where ``NAME`` is the name of the project, ``VERSION`` its version as defined
+in the Metadata, and ``pyX.X`` the major and minor version of Python like
+``2.7`` or ``3.2``.
+
+You can read back this static file, by using the
+:class:`packaging.dist.Metadata` class and its
+:func:`read_pkg_file` method::
+
+ >>> from packaging.metadata import Metadata
+ >>> metadata = Metadata()
+ >>> metadata.read_pkg_file(open('distribute-0.6.8-py2.7.egg-info'))
+ >>> metadata.name
+ 'distribute'
+ >>> metadata.version
+ '0.6.8'
+ >>> metadata.description
+ 'Easily download, build, install, upgrade, and uninstall Python packages'
+
+Notice that the class can also be instantiated with a metadata file path to
+loads its values::
+
+ >>> pkg_info_path = 'distribute-0.6.8-py2.7.egg-info'
+ >>> Metadata(pkg_info_path).name
+ 'distribute'
+
+
+.. XXX These comments have been here for at least ten years. Write the
+ sections or delete the comments (we can maybe ask Greg Ward about
+ the planned contents). (Unindent to make them section titles)
+
+ .. multiple-ext::
+
+ Multiple extension modules
+ ==========================
+
+ Putting it all together
+ =======================
diff --git a/Doc/packaging/extending.rst b/Doc/packaging/extending.rst
new file mode 100644
index 0000000..f2d3863
--- /dev/null
+++ b/Doc/packaging/extending.rst
@@ -0,0 +1,95 @@
+.. _extending-packaging:
+
+*******************
+Extending Distutils
+*******************
+
+Distutils can be extended in various ways. Most extensions take the form of new
+commands or replacements for existing commands. New commands may be written to
+support new types of platform-specific packaging, for example, while
+replacements for existing commands may be made to modify details of how the
+command operates on a package.
+
+Most extensions of the packaging are made within :file:`setup.py` scripts that
+want to modify existing commands; many simply add a few file extensions that
+should be copied into packages in addition to :file:`.py` files as a
+convenience.
+
+Most packaging command implementations are subclasses of the
+:class:`packaging.cmd.Command` class. New commands may directly inherit from
+:class:`Command`, while replacements often derive from :class:`Command`
+indirectly, directly subclassing the command they are replacing. Commands are
+required to derive from :class:`Command`.
+
+.. .. _extend-existing:
+ Extending existing commands
+ ===========================
+
+
+.. .. _new-commands:
+ Writing new commands
+ ====================
+
+
+Integrating new commands
+========================
+
+There are different ways to integrate new command implementations into
+packaging. The most difficult is to lobby for the inclusion of the new features
+in packaging itself, and wait for (and require) a version of Python that
+provides that support. This is really hard for many reasons.
+
+The most common, and possibly the most reasonable for most needs, is to include
+the new implementations with your :file:`setup.py` script, and cause the
+:func:`packaging.core.setup` function use them::
+
+ from packaging.core import setup
+ from packaging.command.build_py import build_py as _build_py
+
+ class build_py(_build_py):
+ """Specialized Python source builder."""
+
+ # implement whatever needs to be different...
+
+ setup(..., cmdclass={'build_py': build_py})
+
+This approach is most valuable if the new implementations must be used to use a
+particular package, as everyone interested in the package will need to have the
+new command implementation.
+
+Beginning with Python 2.4, a third option is available, intended to allow new
+commands to be added which can support existing :file:`setup.py` scripts without
+requiring modifications to the Python installation. This is expected to allow
+third-party extensions to provide support for additional packaging systems, but
+the commands can be used for anything packaging commands can be used for. A new
+configuration option, :option:`command_packages` (command-line option
+:option:`--command-packages`), can be used to specify additional packages to be
+searched for modules implementing commands. Like all packaging options, this
+can be specified on the command line or in a configuration file. This option
+can only be set in the ``[global]`` section of a configuration file, or before
+any commands on the command line. If set in a configuration file, it can be
+overridden from the command line; setting it to an empty string on the command
+line causes the default to be used. This should never be set in a configuration
+file provided with a package.
+
+This new option can be used to add any number of packages to the list of
+packages searched for command implementations; multiple package names should be
+separated by commas. When not specified, the search is only performed in the
+:mod:`packaging.command` package. When :file:`setup.py` is run with the option
+:option:`--command-packages` :option:`distcmds,buildcmds`, however, the packages
+:mod:`packaging.command`, :mod:`distcmds`, and :mod:`buildcmds` will be searched
+in that order. New commands are expected to be implemented in modules of the
+same name as the command by classes sharing the same name. Given the example
+command-line option above, the command :command:`bdist_openpkg` could be
+implemented by the class :class:`distcmds.bdist_openpkg.bdist_openpkg` or
+:class:`buildcmds.bdist_openpkg.bdist_openpkg`.
+
+
+Adding new distribution types
+=============================
+
+Commands that create distributions (files in the :file:`dist/` directory) need
+to add ``(command, filename)`` pairs to ``self.distribution.dist_files`` so that
+:command:`upload` can upload it to PyPI. The *filename* in the pair contains no
+path information, only the name of the file itself. In dry-run mode, pairs
+should still be added to represent what would have been created.
diff --git a/Doc/packaging/index.rst b/Doc/packaging/index.rst
new file mode 100644
index 0000000..d3d0dec
--- /dev/null
+++ b/Doc/packaging/index.rst
@@ -0,0 +1,45 @@
+.. _packaging-index:
+
+##############################
+ Distributing Python Projects
+##############################
+
+:Authors: The Fellowship of the Packaging
+:Email: distutils-sig@python.org
+:Release: |version|
+:Date: |today|
+
+This document describes Packaging for Python authors, describing how to use the
+module to make Python applications, packages or modules easily available to a
+wider audience with very little overhead for build/release/install mechanics.
+
+.. toctree::
+ :maxdepth: 2
+ :numbered:
+
+ tutorial
+ setupcfg
+ introduction
+ setupscript
+ configfile
+ sourcedist
+ builtdist
+ packageindex
+ uploading
+ examples
+ extending
+ commandhooks
+ commandref
+
+
+.. seealso::
+
+ :ref:`packaging-install-index`
+ A user-centered manual which includes information on adding projects
+ into an existing Python installation. You do not need to be a Python
+ programmer to read this manual.
+
+ :mod:`packaging`
+ A library reference for developers of packaging tools wanting to use
+ standalone building blocks like :mod:`~packaging.version` or
+ :mod:`~packaging.metadata`, or extend Packaging itself.
diff --git a/Doc/packaging/introduction.rst b/Doc/packaging/introduction.rst
new file mode 100644
index 0000000..a757ffc
--- /dev/null
+++ b/Doc/packaging/introduction.rst
@@ -0,0 +1,193 @@
+.. _packaging-intro:
+
+*****************************
+An Introduction to Packaging
+*****************************
+
+This document covers using Packaging to distribute your Python modules,
+concentrating on the role of developer/distributor. If you're looking for
+information on installing Python modules you should refer to the
+:ref:`packaging-install-index` chapter.
+
+Throughout this documentation, the terms "Distutils", "the Distutils" and
+"Packaging" will be used interchangeably.
+
+.. _packaging-concepts:
+
+Concepts & Terminology
+======================
+
+Using Distutils is quite simple both for module developers and for
+users/administrators installing third-party modules. As a developer, your
+responsibilities (apart from writing solid, well-documented and well-tested
+code, of course!) are:
+
+* writing a setup script (:file:`setup.py` by convention)
+
+* (optional) writing a setup configuration file
+
+* creating a source distribution
+
+* (optional) creating one or more "built" (binary) distributions of your
+ project
+
+All of these tasks are covered in this document.
+
+Not all module developers have access to multiple platforms, so one cannot
+expect them to create buildt distributions for every platform. To remedy
+this, it is hoped that intermediaries called *packagers* will arise to address
+this need. Packagers take source distributions released by module developers,
+build them on one or more platforms and release the resulting built
+distributions. Thus, users on a greater range of platforms will be able to
+install the most popular Python modules in the most natural way for their
+platform without having to run a setup script or compile a single line of code.
+
+
+.. _packaging-simple-example:
+
+A Simple Example
+================
+
+A setup script is usually quite simple, although since it's written in Python
+there are no arbitrary limits to what you can do with it, though you should be
+careful about putting expensive operations in your setup script.
+Unlike, say, Autoconf-style configure scripts the setup script may be run
+multiple times in the course of building and installing a module
+distribution.
+
+If all you want to do is distribute a module called :mod:`foo`, contained in a
+file :file:`foo.py`, then your setup script can be as simple as::
+
+ from packaging.core import setup
+ setup(name='foo',
+ version='1.0',
+ py_modules=['foo'])
+
+Some observations:
+
+* most information that you supply to the Distutils is supplied as keyword
+ arguments to the :func:`setup` function
+
+* those keyword arguments fall into two categories: package metadata (name,
+ version number, etc.) and information about what's in the package (a list
+ of pure Python modules in this case)
+
+* modules are specified by module name, not filename (the same will hold true
+ for packages and extensions)
+
+* it's recommended that you supply a little more metadata than we have in the
+ example. In particular your name, email address and a URL for the
+ project if appropriate (see section :ref:`packaging-setup-script` for an example)
+
+To create a source distribution for this module you would create a setup
+script, :file:`setup.py`, containing the above code and run::
+
+ python setup.py sdist
+
+which will create an archive file (e.g., tarball on Unix, ZIP file on Windows)
+containing your setup script :file:`setup.py`, and your module :file:`foo.py`.
+The archive file will be named :file:`foo-1.0.tar.gz` (or :file:`.zip`), and
+will unpack into a directory :file:`foo-1.0`.
+
+If an end-user wishes to install your :mod:`foo` module all he has to do is
+download :file:`foo-1.0.tar.gz` (or :file:`.zip`), unpack it, and from the
+:file:`foo-1.0` directory run ::
+
+ python setup.py install
+
+which will copy :file:`foo.py` to the appropriate directory for
+third-party modules in their Python installation.
+
+This simple example demonstrates some fundamental concepts of Distutils.
+First, both developers and installers have the same basic user interface, i.e.
+the setup script. The difference is which Distutils *commands* they use: the
+:command:`sdist` command is almost exclusively for module developers, while
+:command:`install` is more often used by installers (although some developers
+will want to install their own code occasionally).
+
+If you want to make things really easy for your users, you can create more
+than one built distributions for them. For instance, if you are running on a
+Windows machine and want to make things easy for other Windows users, you can
+create an executable installer (the most appropriate type of built distribution
+for this platform) with the :command:`bdist_wininst` command. For example::
+
+ python setup.py bdist_wininst
+
+will create an executable installer, :file:`foo-1.0.win32.exe`, in the current
+directory. You can find out what distribution formats are available at any time
+by running ::
+
+ python setup.py bdist --help-formats
+
+
+.. _packaging-python-terms:
+
+General Python terminology
+==========================
+
+If you're reading this document, you probably have a good idea of what Python
+modules, extensions and so forth are. Nevertheless, just to be sure that
+everyone is on the same page, here's a quick overview of Python terms:
+
+module
+ The basic unit of code reusability in Python: a block of code imported by
+ some other code. Three types of modules are important to us here: pure
+ Python modules, extension modules and packages.
+
+pure Python module
+ A module written in Python and contained in a single :file:`.py` file (and
+ possibly associated :file:`.pyc` and/or :file:`.pyo` files). Sometimes
+ referred to as a "pure module."
+
+extension module
+ A module written in the low-level language of the Python implementation: C/C++
+ for Python, Java for Jython. Typically contained in a single dynamically
+ loaded pre-compiled file, e.g. a shared object (:file:`.so`) file for Python
+ extensions on Unix, a DLL (given the :file:`.pyd` extension) for Python
+ extensions on Windows, or a Java class file for Jython extensions. Note that
+ currently Distutils only handles C/C++ extensions for Python.
+
+package
+ A module that contains other modules, typically contained in a directory of
+ the filesystem and distinguished from other directories by the presence of a
+ file :file:`__init__.py`.
+
+root package
+ The root of the hierarchy of packages. (This isn't really a package,
+ since it doesn't have an :file:`__init__.py` file. But... we have to
+ call it something, right?) The vast majority of the standard library is
+ in the root package, as are many small standalone third-party modules that
+ don't belong to a larger module collection. Unlike regular packages,
+ modules in the root package can be found in many directories: in fact,
+ every directory listed in ``sys.path`` contributes modules to the root
+ package.
+
+
+.. _packaging-term:
+
+Distutils-specific terminology
+==============================
+
+The following terms apply more specifically to the domain of distributing Python
+modules using Distutils:
+
+module distribution
+ A collection of Python modules distributed together as a single downloadable
+ resource and meant to be installed all as one. Examples of some well-known
+ module distributions are NumPy, SciPy, PIL (the Python Imaging
+ Library) or mxBase. (Module distributions would be called a *package*,
+ except that term is already taken in the Python context: a single module
+ distribution may contain zero, one, or many Python packages.)
+
+pure module distribution
+ A module distribution that contains only pure Python modules and packages.
+ Sometimes referred to as a "pure distribution."
+
+non-pure module distribution
+ A module distribution that contains at least one extension module. Sometimes
+ referred to as a "non-pure distribution."
+
+distribution root
+ The top-level directory of your source tree (or source distribution). The
+ directory where :file:`setup.py` exists. Generally :file:`setup.py` will
+ be run from this directory.
diff --git a/Doc/packaging/packageindex.rst b/Doc/packaging/packageindex.rst
new file mode 100644
index 0000000..cd1d598
--- /dev/null
+++ b/Doc/packaging/packageindex.rst
@@ -0,0 +1,104 @@
+.. _packaging-package-index:
+
+**********************************
+Registering with the Package Index
+**********************************
+
+The Python Package Index (PyPI) holds metadata describing distributions
+packaged with packaging. The packaging command :command:`register` is used to
+submit your distribution's metadata to the index. It is invoked as follows::
+
+ python setup.py register
+
+Distutils will respond with the following prompt::
+
+ running register
+ We need to know who you are, so please choose either:
+ 1. use your existing login,
+ 2. register as a new user,
+ 3. have the server generate a new password for you (and email it to you), or
+ 4. quit
+ Your selection [default 1]:
+
+Note: if your username and password are saved locally, you will not see this
+menu.
+
+If you have not registered with PyPI, then you will need to do so now. You
+should choose option 2, and enter your details as required. Soon after
+submitting your details, you will receive an email which will be used to confirm
+your registration.
+
+Once you are registered, you may choose option 1 from the menu. You will be
+prompted for your PyPI username and password, and :command:`register` will then
+submit your metadata to the index.
+
+You may submit any number of versions of your distribution to the index. If you
+alter the metadata for a particular version, you may submit it again and the
+index will be updated.
+
+PyPI holds a record for each (name, version) combination submitted. The first
+user to submit information for a given name is designated the Owner of that
+name. They may submit changes through the :command:`register` command or through
+the web interface. They may also designate other users as Owners or Maintainers.
+Maintainers may edit the package information, but not designate other Owners or
+Maintainers.
+
+By default PyPI will list all versions of a given package. To hide certain
+versions, the Hidden property should be set to yes. This must be edited through
+the web interface.
+
+
+.. _packaging-pypirc:
+
+The .pypirc file
+================
+
+The format of the :file:`.pypirc` file is as follows::
+
+ [packaging]
+ index-servers =
+ pypi
+
+ [pypi]
+ repository: <repository-url>
+ username: <username>
+ password: <password>
+
+The *packaging* section defines a *index-servers* variable that lists the
+name of all sections describing a repository.
+
+Each section describing a repository defines three variables:
+
+- *repository*, that defines the url of the PyPI server. Defaults to
+ ``http://www.python.org/pypi``.
+- *username*, which is the registered username on the PyPI server.
+- *password*, that will be used to authenticate. If omitted the user
+ will be prompt to type it when needed.
+
+If you want to define another server a new section can be created and
+listed in the *index-servers* variable::
+
+ [packaging]
+ index-servers =
+ pypi
+ other
+
+ [pypi]
+ repository: <repository-url>
+ username: <username>
+ password: <password>
+
+ [other]
+ repository: http://example.com/pypi
+ username: <username>
+ password: <password>
+
+:command:`register` can then be called with the -r option to point the
+repository to work with::
+
+ python setup.py register -r http://example.com/pypi
+
+For convenience, the name of the section that describes the repository
+may also be used::
+
+ python setup.py register -r other
diff --git a/Doc/packaging/setupcfg.rst b/Doc/packaging/setupcfg.rst
new file mode 100644
index 0000000..a381017
--- /dev/null
+++ b/Doc/packaging/setupcfg.rst
@@ -0,0 +1,890 @@
+.. highlightlang:: cfg
+
+.. _setupcfg-spec:
+
+*******************************************
+Specification of the :file:`setup.cfg` file
+*******************************************
+
+:version: 0.9
+
+This document describes the :file:`setup.cfg`, an ini-style configuration file
+used by Packaging to replace the :file:`setup.py` file used by Distutils.
+This specification is language-agnostic, and will therefore repeat some
+information that's already documented for Python in the
+:class:`configparser.RawConfigParser` documentation.
+
+.. contents::
+ :depth: 3
+ :local:
+
+
+.. _setupcfg-syntax:
+
+Syntax
+======
+
+The ini-style format used in the configuration file is a simple collection of
+sections that group sets of key-value fields separated by ``=`` or ``:`` and
+optional whitespace. Lines starting with ``#`` or ``;`` are comments and will
+be ignored. Empty lines are also ignored. Example::
+
+ [section1]
+ # comment
+ name = value
+ name2 = "other value"
+
+ [section2]
+ foo = bar
+
+
+Parsing values
+---------------
+
+Here are a set of rules to parse values:
+
+- If a value is quoted with ``"`` chars, it's a string. If a quote character is
+ present in the quoted value, it can be escaped as ``\"`` or left as-is.
+
+- If the value is ``true``, ``t``, ``yes``, ``y`` (case-insensitive) or ``1``,
+ it's converted to the language equivalent of a ``True`` value; if it's
+ ``false``, ``f``, ``no``, ``n`` (case-insensitive) or ``0``, it's converted to
+ the equivalent of ``False``.
+
+- A value can contain multiple lines. When read, lines are converted into a
+ sequence of values. Each line after the first must start with a least one
+ space or tab character; this leading indentation will be stripped.
+
+- All other values are considered strings.
+
+Examples::
+
+ [section]
+ foo = one
+ two
+ three
+
+ bar = false
+ baz = 1.3
+ boo = "ok"
+ beee = "wqdqw pojpj w\"ddq"
+
+
+Extending files
+---------------
+
+A configuration file can be extended (i.e. included) by other files. For this,
+a ``DEFAULT`` section must contain an ``extends`` key whose value points to one
+or more files which will be merged into the current files by adding new sections
+and fields. If a file loaded by ``extends`` contains sections or keys that
+already exist in the original file, they will not override the previous values.
+
+Contents of :file:`one.cfg`::
+
+ [section1]
+ name = value
+
+ [section2]
+ foo = foo from one.cfg
+
+Contents of :file:`two.cfg`::
+
+ [DEFAULT]
+ extends = one.cfg
+
+ [section2]
+ foo = foo from two.cfg
+ baz = baz from two.cfg
+
+The result of parsing :file:`two.cfg` is equivalent to this file::
+
+ [section1]
+ name = value
+
+ [section2]
+ foo = foo from one.cfg
+ baz = baz from two.cfg
+
+Example use of multi-line notation to include more than one file::
+
+ [DEFAULT]
+ extends = one.cfg
+ two.cfg
+
+When several files are provided, they are processed sequentially, following the
+precedence rules explained above. This means that the list of files should go
+from most specialized to most common.
+
+**Tools will need to provide a way to produce a merged version of the
+file**. This will be useful to let users publish a single file.
+
+
+.. _setupcfg-sections:
+
+Description of sections and fields
+==================================
+
+Each section contains a description of its options.
+
+- Options that are marked *multi* can have multiple values, one value per
+ line.
+- Options that are marked *optional* can be omitted.
+- Options that are marked *environ* can use environment markers, as described
+ in :PEP:`345`.
+
+
+The sections are:
+
+global
+ Global options not related to one command.
+
+metadata
+ Name, version and other information defined by :PEP:`345`.
+
+files
+ Modules, scripts, data, documentation and other files to include in the
+ distribution.
+
+extension sections
+ Options used to build extension modules.
+
+command sections
+ Options given for specific commands, identical to those that can be given
+ on the command line.
+
+
+.. _setupcfg-section-global:
+
+Global options
+--------------
+
+Contains global options for Packaging. This section is shared with Distutils.
+
+
+commands
+ Defined Packaging command. A command is defined by its fully
+ qualified name. *optional*, *multi*
+
+ Examples::
+
+ [global]
+ commands =
+ package.setup.CustomSdistCommand
+ package.setup.BdistDeb
+
+compilers
+ Defined Packaging compiler. A compiler is defined by its fully
+ qualified name. *optional*, *multi*
+
+ Example::
+
+ [global]
+ compilers =
+ hotcompiler.SmartCCompiler
+
+setup_hooks
+ Defines a list of callables to be called right after the :file:`setup.cfg`
+ file is read, before any other processing. Each value is a Python dotted
+ name to an object, which has to be defined in a module present in the project
+ directory alonside :file:`setup.cfg` or on Python's :data:`sys.path` (see
+ :ref:`packaging-finding-hooks`). The callables are executed in the
+ order they're found in the file; if one of them cannot be found, tools should
+ not stop, but for example produce a warning and continue with the next line.
+ Each callable receives the configuration as a dictionary (keys are
+ :file:`setup.cfg` sections, values are dictionaries of fields) and can make
+ any change to it. *optional*, *multi*
+
+ Example::
+
+ [global]
+ setup_hooks = _setuphooks.customize_config
+
+
+
+.. _setupcfg-section-metadata:
+
+Metadata
+--------
+
+The metadata section contains the metadata for the project as described in
+:PEP:`345`. Field names are case-insensitive.
+
+Fields:
+
+name
+ Name of the project.
+
+version
+ Version of the project. Must comply with :PEP:`386`.
+
+platform
+ Platform specification describing an operating system
+ supported by the distribution which is not listed in the "Operating System"
+ Trove classifiers (:PEP:`301`). *optional*, *multi*
+
+supported-platform
+ Binary distributions containing a PKG-INFO file will
+ use the Supported-Platform field in their metadata to specify the OS and
+ CPU for which the binary distribution was compiled. The semantics of
+ the Supported-Platform field are free form. *optional*, *multi*
+
+summary
+ A one-line summary of what the distribution does.
+ (Used to be called *description* in Distutils1.)
+
+description
+ A longer description. (Used to be called *long_description*
+ in Distutils1.) A file can be provided in the *description-file* field.
+ *optional*
+
+keywords
+ A list of additional keywords to be used to assist searching
+ for the distribution in a larger catalog. Comma or space-separated.
+ *optional*
+
+home-page
+ The URL for the distribution's home page.
+
+download-url
+ The URL from which this version of the distribution
+ can be downloaded. *optional*
+
+author
+ Author's name. *optional*
+
+author-email
+ Author's e-mail. *optional*
+
+maintainer
+ Maintainer's name. *optional*
+
+maintainer-email
+ Maintainer's e-mail. *optional*
+
+license
+ A text indicating the term of uses, when a trove classifier does
+ not match. *optional*.
+
+classifiers
+ Classification for the distribution, as described in PEP 301.
+ *optional*, *multi*, *environ*
+
+requires-dist
+ name of another packaging project required as a dependency.
+ The format is *name (version)* where version is an optional
+ version declaration, as described in PEP 345. *optional*, *multi*, *environ*
+
+provides-dist
+ name of another packaging project contained within this
+ distribution. Same format than *requires-dist*. *optional*, *multi*,
+ *environ*
+
+obsoletes-dist
+ name of another packaging project this version obsoletes.
+ Same format than *requires-dist*. *optional*, *multi*, *environ*
+
+requires-python
+ Specifies the Python version the distribution requires. The value is a
+ comma-separated list of version predicates, as described in PEP 345.
+ *optional*, *environ*
+
+requires-externals
+ a dependency in the system. This field is free-form,
+ and just a hint for downstream maintainers. *optional*, *multi*,
+ *environ*
+
+project-url
+ A label, followed by a browsable URL for the project.
+ "label, url". The label is limited to 32 signs. *optional*, *multi*
+
+One extra field not present in PEP 345 is supported:
+
+description-file
+ Path to a text file that will be used to fill the ``description`` field.
+ Multiple values are accepted; they must be separated by whitespace.
+ ``description-file`` and ``description`` are mutually exclusive. *optional*
+
+
+
+Example::
+
+ [metadata]
+ name = pypi2rpm
+ version = 0.1
+ author = Tarek Ziadé
+ author-email = tarek@ziade.org
+ summary = Script that transforms an sdist archive into a RPM package
+ description-file = README
+ home-page = http://bitbucket.org/tarek/pypi2rpm/wiki/Home
+ project-url:
+ Repository, http://bitbucket.org/tarek/pypi2rpm/
+ RSS feed, https://bitbucket.org/tarek/pypi2rpm/rss
+ classifier =
+ Development Status :: 3 - Alpha
+ License :: OSI Approved :: Mozilla Public License 1.1 (MPL 1.1)
+
+You should not give any explicit value for metadata-version: it will be guessed
+from the fields present in the file.
+
+
+.. _setupcfg-section-files:
+
+Files
+-----
+
+This section describes the files included in the project.
+
+packages_root
+ the root directory containing all packages and modules
+ (default: current directory, i.e. the project's top-level
+ directory where :file:`setup.cfg` lives). *optional*
+
+packages
+ a list of packages the project includes *optional*, *multi*
+
+modules
+ a list of packages the project includes *optional*, *multi*
+
+scripts
+ a list of scripts the project includes *optional*, *multi*
+
+extra_files
+ a list of patterns for additional files to include in source distributions
+ (see :ref:`packaging-manifest`) *optional*, *multi*
+
+Example::
+
+ [files]
+ packages_root = src
+ packages =
+ pypi2rpm
+ pypi2rpm.command
+
+ scripts =
+ pypi2rpm/pypi2rpm.py
+
+ extra_files =
+ setup.py
+ README
+
+
+.. Note::
+ The :file:`setup.cfg` configuration file is included by default. Contrary to
+ Distutils, :file:`README` (or :file:`README.txt`) and :file:`setup.py` are
+ not included by default.
+
+
+Resources
+^^^^^^^^^
+
+This section describes the files used by the project which must not be installed
+in the same place that python modules or libraries, they are called
+**resources**. They are for example documentation files, script files,
+databases, etc...
+
+For declaring resources, you must use this notation::
+
+ source = destination
+
+Data-files are declared in the **resources** field in the **file** section, for
+example::
+
+ [files]
+ resources =
+ source1 = destination1
+ source2 = destination2
+
+The **source** part of the declaration are relative paths of resources files
+(using unix path separator **/**). For example, if you've this source tree::
+
+ foo/
+ doc/
+ doc.man
+ scripts/
+ foo.sh
+
+Your setup.cfg will look like::
+
+ [files]
+ resources =
+ doc/doc.man = destination_doc
+ scripts/foo.sh = destination_scripts
+
+The final paths where files will be placed are composed by : **source** +
+**destination**. In the previous example, **doc/doc.man** will be placed in
+**destination_doc/doc/doc.man** and **scripts/foo.sh** will be placed in
+**destination_scripts/scripts/foo.sh**. (If you want more control on the final
+path, take a look at :ref:`setupcfg-resources-base-prefix`).
+
+The **destination** part of resources declaration are paths with categories.
+Indeed, it's generally a bad idea to give absolute path as it will be cross
+incompatible. So, you must use resources categories in your **destination**
+declaration. Categories will be replaced by their real path at the installation
+time. Using categories is all benefit, your declaration will be simpler, cross
+platform and it will allow packager to place resources files where they want
+without breaking your code.
+
+Categories can be specified by using this syntax::
+
+ {category}
+
+Default categories are:
+
+* config
+* appdata
+* appdata.arch
+* appdata.persistent
+* appdata.disposable
+* help
+* icon
+* scripts
+* doc
+* info
+* man
+
+A special category also exists **{distribution.name}** that will be replaced by
+the name of the distribution, but as most of the defaults categories use them,
+so it's not necessary to add **{distribution.name}** into your destination.
+
+If you use categories in your declarations, and you are encouraged to do, final
+path will be::
+
+ source + destination_expanded
+
+.. _example_final_path:
+
+For example, if you have this setup.cfg::
+
+ [metadata]
+ name = foo
+
+ [files]
+ resources =
+ doc/doc.man = {doc}
+
+And if **{doc}** is replaced by **{datadir}/doc/{distribution.name}**, final
+path will be::
+
+ {datadir}/doc/foo/doc/doc.man
+
+Where {datafir} category will be platform-dependent.
+
+
+More control on source part
+"""""""""""""""""""""""""""
+
+Glob syntax
+'''''''''''
+
+When you declare source file, you can use a glob-like syntax to match multiples file, for example::
+
+ scripts/* = {script}
+
+Will match all the files in the scripts directory and placed them in the script category.
+
+Glob tokens are:
+
+ * ``*``: match all files.
+ * ``?``: match any character.
+ * ``**``: match any level of tree recursion (even 0).
+ * ``{}``: will match any part separated by comma (example: ``{sh,bat}``).
+
+.. TODO Add examples
+
+Order of declaration
+''''''''''''''''''''
+
+The order of declaration is important if one file match multiple rules. The last
+rules matched by file is used, this is useful if you have this source tree::
+
+ foo/
+ doc/
+ index.rst
+ setup.rst
+ documentation.txt
+ doc.tex
+ README
+
+And you want all the files in the doc directory to be placed in {doc} category,
+but README must be placed in {help} category, instead of listing all the files
+one by one, you can declare them in this way::
+
+ [files]
+ resources =
+ doc/* = {doc}
+ doc/README = {help}
+
+Exclude
+'''''''
+
+You can exclude some files of resources declaration by giving no destination, it
+can be useful if you have a non-resources file in the same directory of
+resources files::
+
+ foo/
+ doc/
+ RELEASES
+ doc.tex
+ documentation.txt
+ docu.rst
+
+Your **files** section will be::
+
+ [files]
+ resources =
+ doc/* = {doc}
+ doc/RELEASES =
+
+More control on destination part
+""""""""""""""""""""""""""""""""
+
+.. _setupcfg-resources-base-prefix:
+
+Defining a base prefix
+''''''''''''''''''''''
+
+When you define your resources, you can have more control of how the final path
+is computed.
+
+By default, the final path is::
+
+ destination + source
+
+This can generate long paths, for example (example_final_path_)::
+
+ {datadir}/doc/foo/doc/doc.man
+
+When you declare your source, you can use whitespace to split the source in
+**prefix** **suffix**. So, for example, if you have this source::
+
+ docs/ doc.man
+
+The **prefix** is "docs/" and the **suffix** is "doc.html".
+
+.. note::
+
+ Separator can be placed after a path separator or replace it. So these two
+ sources are equivalent::
+
+ docs/ doc.man
+ docs doc.man
+
+.. note::
+
+ Glob syntax is working the same way with standard source and split source.
+ So these rules::
+
+ docs/*
+ docs/ *
+ docs *
+
+ Will match all the files in the docs directory.
+
+When you use split source, the final path is computed this way::
+
+ destination + prefix
+
+So for example, if you have this setup.cfg::
+
+ [metadata]
+ name = foo
+
+ [files]
+ resources =
+ doc/ doc.man = {doc}
+
+And if **{doc}** is replaced by **{datadir}/doc/{distribution.name}**, final
+path will be::
+
+ {datadir}/doc/foo/doc.man
+
+
+Overwriting paths for categories
+""""""""""""""""""""""""""""""""
+
+This part is intended for system administrators or downstream OS packagers.
+
+The real paths of categories are registered in the *sysconfig.cfg* file
+installed in your python installation. This file uses an ini format too.
+The content of the file is organized into several sections:
+
+* globals: Standard categories's paths.
+* posix_prefix: Standard paths for categories and installation paths for posix
+ system.
+* other ones XXX
+
+Standard categories paths are platform independent, they generally refers to
+other categories, which are platform dependent. :mod:`sysconfig` will choose
+these category from sections matching os.name. For example::
+
+ doc = {datadir}/doc/{distribution.name}
+
+It refers to datadir category, which can be different between platforms. In
+posix system, it may be::
+
+ datadir = /usr/share
+
+So the final path will be::
+
+ doc = /usr/share/doc/{distribution.name}
+
+The platform-dependent categories are:
+
+* confdir
+* datadir
+* libdir
+* base
+
+
+Defining extra categories
+"""""""""""""""""""""""""
+
+.. TODO
+
+
+Examples
+""""""""
+
+These examples are incremental but work unitarily.
+
+Resources in root dir
+'''''''''''''''''''''
+
+Source tree::
+
+ babar-1.0/
+ README
+ babar.sh
+ launch.sh
+ babar.py
+
+:file:`setup.cfg`::
+
+ [files]
+ resources =
+ README = {doc}
+ *.sh = {scripts}
+
+So babar.sh and launch.sh will be placed in {scripts} directory.
+
+Now let's move all the scripts into a scripts directory.
+
+Resources in sub-directory
+''''''''''''''''''''''''''
+
+Source tree::
+
+ babar-1.1/
+ README
+ scripts/
+ babar.sh
+ launch.sh
+ LAUNCH
+ babar.py
+
+:file:`setup.cfg`::
+
+ [files]
+ resources =
+ README = {doc}
+ scripts/ LAUNCH = {doc}
+ scripts/ *.sh = {scripts}
+
+It's important to use the separator after scripts/ to install all the shell
+scripts into {scripts} instead of {scripts}/scripts.
+
+Now let's add some docs.
+
+Resources in multiple sub-directories
+'''''''''''''''''''''''''''''''''''''
+
+Source tree::
+
+ babar-1.2/
+ README
+ scripts/
+ babar.sh
+ launch.sh
+ LAUNCH
+ docs/
+ api
+ man
+ babar.py
+
+:file:`setup.cfg`::
+
+ [files]
+ resources =
+ README = {doc}
+ scripts/ LAUNCH = {doc}
+ scripts/ *.sh = {scripts}
+ doc/ * = {doc}
+ doc/ man = {man}
+
+You want to place all the file in the docs script into {doc} category, instead
+of man, which must be placed into {man} category, we will use the order of
+declaration of globs to choose the destination, the last glob that match the
+file is used.
+
+Now let's add some scripts for windows users.
+
+Complete example
+''''''''''''''''
+
+Source tree::
+
+ babar-1.3/
+ README
+ doc/
+ api
+ man
+ scripts/
+ babar.sh
+ launch.sh
+ babar.bat
+ launch.bat
+ LAUNCH
+
+:file:`setup.cfg`::
+
+ [files]
+ resources =
+ README = {doc}
+ scripts/ LAUNCH = {doc}
+ scripts/ *.{sh,bat} = {scripts}
+ doc/ * = {doc}
+ doc/ man = {man}
+
+We use brace expansion syntax to place all the shell and batch scripts into
+{scripts} category.
+
+
+.. _setupcfg-section-extensions:
+
+Extension modules sections
+--------------------------
+
+If a project includes extension modules written in C or C++, each one of them
+needs to have its options defined in a dedicated section. Here's an example::
+
+ [files]
+ packages = coconut
+
+ [extension: coconut._fastcoconut]
+ language = cxx
+ sources = cxx_src/cononut_utils.cxx
+ cxx_src/python_module.cxx
+ include_dirs = /usr/include/gecode
+ /usr/include/blitz
+ extra_compile_args =
+ -fPIC -O2
+ -DGECODE_VERSION=$(./gecode_version) -- sys.platform != 'win32'
+ /DGECODE_VERSION=win32 -- sys.platform == 'win32'
+
+The section name must start with ``extension:``; the right-hand part is used as
+the full name (including a parent package, if any) of the extension. Whitespace
+around the extension name is allowed. If the extension module is not standalone
+(e.g. ``_bisect``) but part of a package (e.g. ``thing._speedups``), the parent
+package must be listed in the ``packages`` field.
+Valid fields and their values are listed in the documentation of the
+:class:`packaging.compiler.extension.Extension` class; values documented as
+Python lists translate to multi-line values in the configuration file. In
+addition, multi-line values accept environment markers on each line, after a
+``--``.
+
+
+.. _setupcfg-section-commands:
+
+Commands sections
+-----------------
+
+To pass options to commands without having to type them on the command line
+for each invocation, you can write them in the :file:`setup.cfg` file, in a
+section named after the command. Example::
+
+ [sdist]
+ # special function to add custom files
+ manifest-builders = package.setup.list_extra_files
+
+ [build]
+ use-2to3 = True
+
+ [build_ext]
+ inplace = on
+
+ [check]
+ strict = on
+ all = on
+
+Option values given in the configuration file can be overriden on the command
+line. See :ref:`packaging-setup-config` for more information.
+
+These sections are also used to define :ref:`command hooks
+<packaging-command-hooks>`.
+
+
+.. _setupcfg-extensibility:
+
+Extensibility
+=============
+
+Every section can have fields that are not part of this specification. They are
+called **extensions**.
+
+An extension field starts with ``X-``. Example::
+
+ [metadata]
+ name = Distribute
+ X-Debian-Name = python-distribute
+
+
+.. _setupcfg-changes:
+
+Changes in the specification
+============================
+
+The versioning scheme for this specification is **MAJOR.MINOR**. Changes in the
+specification will cause the version number to be updated.
+
+Changes to the minor number reflect backwards-compatible changes:
+
+- New fields and sections (optional or mandatory) can be added.
+- Optional fields can be removed.
+
+The major number will be incremented for backwards-incompatible changes:
+
+- Mandatory fields or sections are removed.
+- Fields change their meaning.
+
+As a consequence, a tool written to consume 1.5 has these properties:
+
+- Can read 1.1, 1.2 and all versions < 1.5, since the tool knows what
+ optional fields weren't there.
+
+ .. XXX clarify
+
+- Can also read 1.6 and other 1.x versions: The tool will just ignore fields it
+ doesn't know about, even if they are mandatory in the new version. If
+ optional fields were removed, the tool will just consider them absent.
+
+- Cannot read 2.x and should refuse to interpret such files.
+
+A tool written to produce 1.x should have these properties:
+
+- Writes all mandatory fields.
+- May write optional fields.
+
+
+.. _setupcfg-acks:
+
+Acknowledgments
+===============
+
+This specification includes work and feedback from these people:
+
+- Tarek Ziadé
+- Julien Jehannet
+- Boris Feld
+- Éric Araujo
+
+(If your name is missing, please :ref:`let us know <reporting-bugs>`.)
diff --git a/Doc/packaging/setupscript.rst b/Doc/packaging/setupscript.rst
new file mode 100644
index 0000000..cafde20
--- /dev/null
+++ b/Doc/packaging/setupscript.rst
@@ -0,0 +1,693 @@
+.. _packaging-setup-script:
+
+************************
+Writing the Setup Script
+************************
+
+The setup script is the center of all activity in building, distributing, and
+installing modules using Distutils. The main purpose of the setup script is
+to describe your module distribution to Distutils, so that the various
+commands that operate on your modules do the right thing. As we saw in section
+:ref:`packaging-simple-example`, the setup script consists mainly of a
+call to :func:`setup` where the most information is supplied as
+keyword arguments to :func:`setup`.
+
+Here's a slightly more involved example, which we'll follow for the next couple
+of sections: a setup script that could be used for Packaging itself::
+
+ #!/usr/bin/env python
+
+ from packaging.core import setup, find_packages
+
+ setup(name='Packaging',
+ version='1.0',
+ summary='Python Distribution Utilities',
+ keywords=['packaging', 'packaging'],
+ author=u'Tarek Ziadé',
+ author_email='tarek@ziade.org',
+ home_page='http://bitbucket.org/tarek/packaging/wiki/Home',
+ license='PSF',
+ packages=find_packages())
+
+
+There are only two differences between this and the trivial one-file
+distribution presented in section :ref:`packaging-simple-example`: more
+metadata and the specification of pure Python modules by package rather than
+by module. This is important since Ristutils consist of a couple of dozen
+modules split into (so far) two packages; an explicit list of every module
+would be tedious to generate and difficult to maintain. For more information
+on the additional metadata, see section :ref:`packaging-metadata`.
+
+Note that any pathnames (files or directories) supplied in the setup script
+should be written using the Unix convention, i.e. slash-separated. The
+Distutils will take care of converting this platform-neutral representation into
+whatever is appropriate on your current platform before actually using the
+pathname. This makes your setup script portable across operating systems, which
+of course is one of the major goals of the Distutils. In this spirit, all
+pathnames in this document are slash-separated.
+
+This, of course, only applies to pathnames given to Distutils functions. If
+you, for example, use standard Python functions such as :func:`glob.glob` or
+:func:`os.listdir` to specify files, you should be careful to write portable
+code instead of hardcoding path separators::
+
+ glob.glob(os.path.join('mydir', 'subdir', '*.html'))
+ os.listdir(os.path.join('mydir', 'subdir'))
+
+
+.. _packaging-listing-packages:
+
+Listing whole packages
+======================
+
+The :option:`packages` option tells the Distutils to process (build, distribute,
+install, etc.) all pure Python modules found in each package mentioned in the
+:option:`packages` list. In order to do this, of course, there has to be a
+correspondence between package names and directories in the filesystem. The
+default correspondence is the most obvious one, i.e. package :mod:`packaging` is
+found in the directory :file:`packaging` relative to the distribution root.
+Thus, when you say ``packages = ['foo']`` in your setup script, you are
+promising that the Distutils will find a file :file:`foo/__init__.py` (which
+might be spelled differently on your system, but you get the idea) relative to
+the directory where your setup script lives. If you break this promise, the
+Distutils will issue a warning but still process the broken package anyway.
+
+If you use a different convention to lay out your source directory, that's no
+problem: you just have to supply the :option:`package_dir` option to tell the
+Distutils about your convention. For example, say you keep all Python source
+under :file:`lib`, so that modules in the "root package" (i.e., not in any
+package at all) are in :file:`lib`, modules in the :mod:`foo` package are in
+:file:`lib/foo`, and so forth. Then you would put ::
+
+ package_dir = {'': 'lib'}
+
+in your setup script. The keys to this dictionary are package names, and an
+empty package name stands for the root package. The values are directory names
+relative to your distribution root. In this case, when you say ``packages =
+['foo']``, you are promising that the file :file:`lib/foo/__init__.py` exists.
+
+Another possible convention is to put the :mod:`foo` package right in
+:file:`lib`, the :mod:`foo.bar` package in :file:`lib/bar`, etc. This would be
+written in the setup script as ::
+
+ package_dir = {'foo': 'lib'}
+
+A ``package: dir`` entry in the :option:`package_dir` dictionary implicitly
+applies to all packages below *package*, so the :mod:`foo.bar` case is
+automatically handled here. In this example, having ``packages = ['foo',
+'foo.bar']`` tells the Distutils to look for :file:`lib/__init__.py` and
+:file:`lib/bar/__init__.py`. (Keep in mind that although :option:`package_dir`
+applies recursively, you must explicitly list all packages in
+:option:`packages`: the Distutils will *not* recursively scan your source tree
+looking for any directory with an :file:`__init__.py` file.)
+
+
+.. _packaging-listing-modules:
+
+Listing individual modules
+==========================
+
+For a small module distribution, you might prefer to list all modules rather
+than listing packages---especially the case of a single module that goes in the
+"root package" (i.e., no package at all). This simplest case was shown in
+section :ref:`packaging-simple-example`; here is a slightly more involved
+example::
+
+ py_modules = ['mod1', 'pkg.mod2']
+
+This describes two modules, one of them in the "root" package, the other in the
+:mod:`pkg` package. Again, the default package/directory layout implies that
+these two modules can be found in :file:`mod1.py` and :file:`pkg/mod2.py`, and
+that :file:`pkg/__init__.py` exists as well. And again, you can override the
+package/directory correspondence using the :option:`package_dir` option.
+
+
+.. _packaging-describing-extensions:
+
+Describing extension modules
+============================
+
+Just as writing Python extension modules is a bit more complicated than writing
+pure Python modules, describing them to the Distutils is a bit more complicated.
+Unlike pure modules, it's not enough just to list modules or packages and expect
+the Distutils to go out and find the right files; you have to specify the
+extension name, source file(s), and any compile/link requirements (include
+directories, libraries to link with, etc.).
+
+.. XXX read over this section
+
+All of this is done through another keyword argument to :func:`setup`, the
+:option:`ext_modules` option. :option:`ext_modules` is just a list of
+:class:`Extension` instances, each of which describes a single extension module.
+Suppose your distribution includes a single extension, called :mod:`foo` and
+implemented by :file:`foo.c`. If no additional instructions to the
+compiler/linker are needed, describing this extension is quite simple::
+
+ Extension('foo', ['foo.c'])
+
+The :class:`Extension` class can be imported from :mod:`packaging.core` along
+with :func:`setup`. Thus, the setup script for a module distribution that
+contains only this one extension and nothing else might be::
+
+ from packaging.core import setup, Extension
+ setup(name='foo',
+ version='1.0',
+ ext_modules=[Extension('foo', ['foo.c'])])
+
+The :class:`Extension` class (actually, the underlying extension-building
+machinery implemented by the :command:`build_ext` command) supports a great deal
+of flexibility in describing Python extensions, which is explained in the
+following sections.
+
+
+Extension names and packages
+----------------------------
+
+The first argument to the :class:`Extension` constructor is always the name of
+the extension, including any package names. For example, ::
+
+ Extension('foo', ['src/foo1.c', 'src/foo2.c'])
+
+describes an extension that lives in the root package, while ::
+
+ Extension('pkg.foo', ['src/foo1.c', 'src/foo2.c'])
+
+describes the same extension in the :mod:`pkg` package. The source files and
+resulting object code are identical in both cases; the only difference is where
+in the filesystem (and therefore where in Python's namespace hierarchy) the
+resulting extension lives.
+
+If your distribution contains only one or more extension modules in a package,
+you need to create a :file:`{package}/__init__.py` file anyway, otherwise Python
+won't be able to import anything.
+
+If you have a number of extensions all in the same package (or all under the
+same base package), use the :option:`ext_package` keyword argument to
+:func:`setup`. For example, ::
+
+ setup(...,
+ ext_package='pkg',
+ ext_modules=[Extension('foo', ['foo.c']),
+ Extension('subpkg.bar', ['bar.c'])])
+
+will compile :file:`foo.c` to the extension :mod:`pkg.foo`, and :file:`bar.c` to
+:mod:`pkg.subpkg.bar`.
+
+
+Extension source files
+----------------------
+
+The second argument to the :class:`Extension` constructor is a list of source
+files. Since the Distutils currently only support C, C++, and Objective-C
+extensions, these are normally C/C++/Objective-C source files. (Be sure to use
+appropriate extensions to distinguish C++\ source files: :file:`.cc` and
+:file:`.cpp` seem to be recognized by both Unix and Windows compilers.)
+
+However, you can also include SWIG interface (:file:`.i`) files in the list; the
+:command:`build_ext` command knows how to deal with SWIG extensions: it will run
+SWIG on the interface file and compile the resulting C/C++ file into your
+extension.
+
+.. XXX SWIG support is rough around the edges and largely untested!
+
+This warning notwithstanding, options to SWIG can be currently passed like
+this::
+
+ setup(...,
+ ext_modules=[Extension('_foo', ['foo.i'],
+ swig_opts=['-modern', '-I../include'])],
+ py_modules=['foo'])
+
+Or on the command line like this::
+
+ > python setup.py build_ext --swig-opts="-modern -I../include"
+
+On some platforms, you can include non-source files that are processed by the
+compiler and included in your extension. Currently, this just means Windows
+message text (:file:`.mc`) files and resource definition (:file:`.rc`) files for
+Visual C++. These will be compiled to binary resource (:file:`.res`) files and
+linked into the executable.
+
+
+Preprocessor options
+--------------------
+
+Three optional arguments to :class:`Extension` will help if you need to specify
+include directories to search or preprocessor macros to define/undefine:
+``include_dirs``, ``define_macros``, and ``undef_macros``.
+
+For example, if your extension requires header files in the :file:`include`
+directory under your distribution root, use the ``include_dirs`` option::
+
+ Extension('foo', ['foo.c'], include_dirs=['include'])
+
+You can specify absolute directories there; if you know that your extension will
+only be built on Unix systems with X11R6 installed to :file:`/usr`, you can get
+away with ::
+
+ Extension('foo', ['foo.c'], include_dirs=['/usr/include/X11'])
+
+You should avoid this sort of non-portable usage if you plan to distribute your
+code: it's probably better to write C code like ::
+
+ #include <X11/Xlib.h>
+
+If you need to include header files from some other Python extension, you can
+take advantage of the fact that header files are installed in a consistent way
+by the Distutils :command:`install_header` command. For example, the Numerical
+Python header files are installed (on a standard Unix installation) to
+:file:`/usr/local/include/python1.5/Numerical`. (The exact location will differ
+according to your platform and Python installation.) Since the Python include
+directory---\ :file:`/usr/local/include/python1.5` in this case---is always
+included in the search path when building Python extensions, the best approach
+is to write C code like ::
+
+ #include <Numerical/arrayobject.h>
+
+.. TODO check if it's d2.sysconfig or the new sysconfig module now
+
+If you must put the :file:`Numerical` include directory right into your header
+search path, though, you can find that directory using the Distutils
+:mod:`packaging.sysconfig` module::
+
+ from packaging.sysconfig import get_python_inc
+ incdir = os.path.join(get_python_inc(plat_specific=1), 'Numerical')
+ setup(...,
+ Extension(..., include_dirs=[incdir]))
+
+Even though this is quite portable---it will work on any Python installation,
+regardless of platform---it's probably easier to just write your C code in the
+sensible way.
+
+You can define and undefine preprocessor macros with the ``define_macros`` and
+``undef_macros`` options. ``define_macros`` takes a list of ``(name, value)``
+tuples, where ``name`` is the name of the macro to define (a string) and
+``value`` is its value: either a string or ``None``. (Defining a macro ``FOO``
+to ``None`` is the equivalent of a bare ``#define FOO`` in your C source: with
+most compilers, this sets ``FOO`` to the string ``1``.) ``undef_macros`` is
+just a list of macros to undefine.
+
+For example::
+
+ Extension(...,
+ define_macros=[('NDEBUG', '1'),
+ ('HAVE_STRFTIME', None)],
+ undef_macros=['HAVE_FOO', 'HAVE_BAR'])
+
+is the equivalent of having this at the top of every C source file::
+
+ #define NDEBUG 1
+ #define HAVE_STRFTIME
+ #undef HAVE_FOO
+ #undef HAVE_BAR
+
+
+Library options
+---------------
+
+You can also specify the libraries to link against when building your extension,
+and the directories to search for those libraries. The ``libraries`` option is
+a list of libraries to link against, ``library_dirs`` is a list of directories
+to search for libraries at link-time, and ``runtime_library_dirs`` is a list of
+directories to search for shared (dynamically loaded) libraries at run-time.
+
+For example, if you need to link against libraries known to be in the standard
+library search path on target systems ::
+
+ Extension(...,
+ libraries=['gdbm', 'readline'])
+
+If you need to link with libraries in a non-standard location, you'll have to
+include the location in ``library_dirs``::
+
+ Extension(...,
+ library_dirs=['/usr/X11R6/lib'],
+ libraries=['X11', 'Xt'])
+
+(Again, this sort of non-portable construct should be avoided if you intend to
+distribute your code.)
+
+.. XXX Should mention clib libraries here or somewhere else!
+
+
+Other options
+-------------
+
+There are still some other options which can be used to handle special cases.
+
+The :option:`optional` option is a boolean; if it is true,
+a build failure in the extension will not abort the build process, but
+instead simply not install the failing extension.
+
+The :option:`extra_objects` option is a list of object files to be passed to the
+linker. These files must not have extensions, as the default extension for the
+compiler is used.
+
+:option:`extra_compile_args` and :option:`extra_link_args` can be used to
+specify additional command-line options for the respective compiler and linker
+command lines.
+
+:option:`export_symbols` is only useful on Windows. It can contain a list of
+symbols (functions or variables) to be exported. This option is not needed when
+building compiled extensions: Distutils will automatically add ``initmodule``
+to the list of exported symbols.
+
+The :option:`depends` option is a list of files that the extension depends on
+(for example header files). The build command will call the compiler on the
+sources to rebuild extension if any on this files has been modified since the
+previous build.
+
+Relationships between Distributions and Packages
+================================================
+
+.. FIXME rewrite to update to PEP 345 (but without dist/release confusion)
+
+A distribution may relate to packages in three specific ways:
+
+#. It can require packages or modules.
+
+#. It can provide packages or modules.
+
+#. It can obsolete packages or modules.
+
+These relationships can be specified using keyword arguments to the
+:func:`packaging.core.setup` function.
+
+Dependencies on other Python modules and packages can be specified by supplying
+the *requires* keyword argument to :func:`setup`. The value must be a list of
+strings. Each string specifies a package that is required, and optionally what
+versions are sufficient.
+
+To specify that any version of a module or package is required, the string
+should consist entirely of the module or package name. Examples include
+``'mymodule'`` and ``'xml.parsers.expat'``.
+
+If specific versions are required, a sequence of qualifiers can be supplied in
+parentheses. Each qualifier may consist of a comparison operator and a version
+number. The accepted comparison operators are::
+
+ < > ==
+ <= >= !=
+
+These can be combined by using multiple qualifiers separated by commas (and
+optional whitespace). In this case, all of the qualifiers must be matched; a
+logical AND is used to combine the evaluations.
+
+Let's look at a bunch of examples:
+
++-------------------------+----------------------------------------------+
+| Requires Expression | Explanation |
++=========================+==============================================+
+| ``==1.0`` | Only version ``1.0`` is compatible |
++-------------------------+----------------------------------------------+
+| ``>1.0, !=1.5.1, <2.0`` | Any version after ``1.0`` and before ``2.0`` |
+| | is compatible, except ``1.5.1`` |
++-------------------------+----------------------------------------------+
+
+Now that we can specify dependencies, we also need to be able to specify what we
+provide that other distributions can require. This is done using the *provides*
+keyword argument to :func:`setup`. The value for this keyword is a list of
+strings, each of which names a Python module or package, and optionally
+identifies the version. If the version is not specified, it is assumed to match
+that of the distribution.
+
+Some examples:
+
++---------------------+----------------------------------------------+
+| Provides Expression | Explanation |
++=====================+==============================================+
+| ``mypkg`` | Provide ``mypkg``, using the distribution |
+| | version |
++---------------------+----------------------------------------------+
+| ``mypkg (1.1)`` | Provide ``mypkg`` version 1.1, regardless of |
+| | the distribution version |
++---------------------+----------------------------------------------+
+
+A package can declare that it obsoletes other packages using the *obsoletes*
+keyword argument. The value for this is similar to that of the *requires*
+keyword: a list of strings giving module or package specifiers. Each specifier
+consists of a module or package name optionally followed by one or more version
+qualifiers. Version qualifiers are given in parentheses after the module or
+package name.
+
+The versions identified by the qualifiers are those that are obsoleted by the
+distribution being described. If no qualifiers are given, all versions of the
+named module or package are understood to be obsoleted.
+
+.. _packaging-installing-scripts:
+
+Installing Scripts
+==================
+
+So far we have been dealing with pure and non-pure Python modules, which are
+usually not run by themselves but imported by scripts.
+
+Scripts are files containing Python source code, intended to be started from the
+command line. Scripts don't require Distutils to do anything very complicated.
+The only clever feature is that if the first line of the script starts with
+``#!`` and contains the word "python", the Distutils will adjust the first line
+to refer to the current interpreter location. By default, it is replaced with
+the current interpreter location. The :option:`--executable` (or :option:`-e`)
+option will allow the interpreter path to be explicitly overridden.
+
+The :option:`scripts` option simply is a list of files to be handled in this
+way. From the PyXML setup script::
+
+ setup(...,
+ scripts=['scripts/xmlproc_parse', 'scripts/xmlproc_val'])
+
+All the scripts will also be added to the ``MANIFEST`` file if no template is
+provided. See :ref:`packaging-manifest`.
+
+.. _packaging-installing-package-data:
+
+Installing Package Data
+=======================
+
+Often, additional files need to be installed into a package. These files are
+often data that's closely related to the package's implementation, or text files
+containing documentation that might be of interest to programmers using the
+package. These files are called :dfn:`package data`.
+
+Package data can be added to packages using the ``package_data`` keyword
+argument to the :func:`setup` function. The value must be a mapping from
+package name to a list of relative path names that should be copied into the
+package. The paths are interpreted as relative to the directory containing the
+package (information from the ``package_dir`` mapping is used if appropriate);
+that is, the files are expected to be part of the package in the source
+directories. They may contain glob patterns as well.
+
+The path names may contain directory portions; any necessary directories will be
+created in the installation.
+
+For example, if a package should contain a subdirectory with several data files,
+the files can be arranged like this in the source tree::
+
+ setup.py
+ src/
+ mypkg/
+ __init__.py
+ module.py
+ data/
+ tables.dat
+ spoons.dat
+ forks.dat
+
+The corresponding call to :func:`setup` might be::
+
+ setup(...,
+ packages=['mypkg'],
+ package_dir={'mypkg': 'src/mypkg'},
+ package_data={'mypkg': ['data/*.dat']})
+
+
+All the files that match ``package_data`` will be added to the ``MANIFEST``
+file if no template is provided. See :ref:`packaging-manifest`.
+
+
+.. _packaging-additional-files:
+
+Installing Additional Files
+===========================
+
+The :option:`data_files` option can be used to specify additional files needed
+by the module distribution: configuration files, message catalogs, data files,
+anything which doesn't fit in the previous categories.
+
+:option:`data_files` specifies a sequence of (*directory*, *files*) pairs in the
+following way::
+
+ setup(...,
+ data_files=[('bitmaps', ['bm/b1.gif', 'bm/b2.gif']),
+ ('config', ['cfg/data.cfg']),
+ ('/etc/init.d', ['init-script'])])
+
+Note that you can specify the directory names where the data files will be
+installed, but you cannot rename the data files themselves.
+
+Each (*directory*, *files*) pair in the sequence specifies the installation
+directory and the files to install there. If *directory* is a relative path, it
+is interpreted relative to the installation prefix (Python's ``sys.prefix`` for
+pure-Python packages, ``sys.exec_prefix`` for packages that contain extension
+modules). Each file name in *files* is interpreted relative to the
+:file:`setup.py` script at the top of the package source distribution. No
+directory information from *files* is used to determine the final location of
+the installed file; only the name of the file is used.
+
+You can specify the :option:`data_files` options as a simple sequence of files
+without specifying a target directory, but this is not recommended, and the
+:command:`install_dist` command will print a warning in this case. To install data
+files directly in the target directory, an empty string should be given as the
+directory.
+
+All the files that match ``data_files`` will be added to the ``MANIFEST`` file
+if no template is provided. See :ref:`packaging-manifest`.
+
+
+
+.. _packaging-metadata:
+
+Metadata reference
+==================
+
+The setup script may include additional metadata beyond the name and version.
+This table describes required and additional information:
+
+.. TODO synchronize with setupcfg; link to it (but don't remove it, it's a
+ useful summary)
+
++----------------------+---------------------------+-----------------+--------+
+| Meta-Data | Description | Value | Notes |
++======================+===========================+=================+========+
+| ``name`` | name of the project | short string | \(1) |
++----------------------+---------------------------+-----------------+--------+
+| ``version`` | version of this release | short string | (1)(2) |
++----------------------+---------------------------+-----------------+--------+
+| ``author`` | project author's name | short string | \(3) |
++----------------------+---------------------------+-----------------+--------+
+| ``author_email`` | email address of the | email address | \(3) |
+| | project author | | |
++----------------------+---------------------------+-----------------+--------+
+| ``maintainer`` | project maintainer's name | short string | \(3) |
++----------------------+---------------------------+-----------------+--------+
+| ``maintainer_email`` | email address of the | email address | \(3) |
+| | project maintainer | | |
++----------------------+---------------------------+-----------------+--------+
+| ``home_page`` | home page for the project | URL | \(1) |
++----------------------+---------------------------+-----------------+--------+
+| ``summary`` | short description of the | short string | |
+| | project | | |
++----------------------+---------------------------+-----------------+--------+
+| ``description`` | longer description of the | long string | \(5) |
+| | project | | |
++----------------------+---------------------------+-----------------+--------+
+| ``download_url`` | location where the | URL | |
+| | project may be downloaded | | |
++----------------------+---------------------------+-----------------+--------+
+| ``classifiers`` | a list of classifiers | list of strings | \(4) |
++----------------------+---------------------------+-----------------+--------+
+| ``platforms`` | a list of platforms | list of strings | |
++----------------------+---------------------------+-----------------+--------+
+| ``license`` | license for the release | short string | \(6) |
++----------------------+---------------------------+-----------------+--------+
+
+Notes:
+
+(1)
+ These fields are required.
+
+(2)
+ It is recommended that versions take the form *major.minor[.patch[.sub]]*.
+
+(3)
+ Either the author or the maintainer must be identified.
+
+(4)
+ The list of classifiers is available from the `PyPI website
+ <http://pypi.python.org/pypi>`_. See also :mod:`packaging.create`.
+
+(5)
+ The ``description`` field is used by PyPI when you are registering a
+ release, to build its PyPI page.
+
+(6)
+ The ``license`` field is a text indicating the license covering the
+ distribution where the license is not a selection from the "License" Trove
+ classifiers. See the ``Classifier`` field. Notice that
+ there's a ``licence`` distribution option which is deprecated but still
+ acts as an alias for ``license``.
+
+'short string'
+ A single line of text, not more than 200 characters.
+
+'long string'
+ Multiple lines of plain text in reStructuredText format (see
+ http://docutils.sf.net/).
+
+'list of strings'
+ See below.
+
+In Python 2.x, "string value" means a unicode object. If a byte string (str or
+bytes) is given, it has to be valid ASCII.
+
+.. TODO move this section to the version document, keep a summary, add a link
+
+Encoding the version information is an art in itself. Python projects generally
+adhere to the version format *major.minor[.patch][sub]*. The major number is 0
+for initial, experimental releases of software. It is incremented for releases
+that represent major milestones in a project. The minor number is incremented
+when important new features are added to the project. The patch number
+increments when bug-fix releases are made. Additional trailing version
+information is sometimes used to indicate sub-releases. These are
+"a1,a2,...,aN" (for alpha releases, where functionality and API may change),
+"b1,b2,...,bN" (for beta releases, which only fix bugs) and "pr1,pr2,...,prN"
+(for final pre-release release testing). Some examples:
+
+0.1.0
+ the first, experimental release of a project
+
+1.0.1a2
+ the second alpha release of the first patch version of 1.0
+
+:option:`classifiers` are specified in a Python list::
+
+ setup(...,
+ classifiers=[
+ 'Development Status :: 4 - Beta',
+ 'Environment :: Console',
+ 'Environment :: Web Environment',
+ 'Intended Audience :: End Users/Desktop',
+ 'Intended Audience :: Developers',
+ 'Intended Audience :: System Administrators',
+ 'License :: OSI Approved :: Python Software Foundation License',
+ 'Operating System :: MacOS :: MacOS X',
+ 'Operating System :: Microsoft :: Windows',
+ 'Operating System :: POSIX',
+ 'Programming Language :: Python',
+ 'Topic :: Communications :: Email',
+ 'Topic :: Office/Business',
+ 'Topic :: Software Development :: Bug Tracking',
+ ])
+
+
+Debugging the setup script
+==========================
+
+Sometimes things go wrong, and the setup script doesn't do what the developer
+wants.
+
+Distutils catches any exceptions when running the setup script, and print a
+simple error message before the script is terminated. The motivation for this
+behaviour is to not confuse administrators who don't know much about Python and
+are trying to install a project. If they get a big long traceback from deep
+inside the guts of Distutils, they may think the project or the Python
+installation is broken because they don't read all the way down to the bottom
+and see that it's a permission problem.
+
+.. FIXME DISTUTILS_DEBUG is dead, document logging/warnings here
+
+On the other hand, this doesn't help the developer to find the cause of the
+failure. For this purpose, the DISTUTILS_DEBUG environment variable can be set
+to anything except an empty string, and Packaging will now print detailed
+information about what it is doing, and prints the full traceback in case an
+exception occurs.
diff --git a/Doc/packaging/sourcedist.rst b/Doc/packaging/sourcedist.rst
new file mode 100644
index 0000000..2cedc15
--- /dev/null
+++ b/Doc/packaging/sourcedist.rst
@@ -0,0 +1,266 @@
+.. _packaging-source-dist:
+
+******************************
+Creating a Source Distribution
+******************************
+
+As shown in section :ref:`packaging-simple-example`, you use the :command:`sdist` command
+to create a source distribution. In the simplest case, ::
+
+ python setup.py sdist
+
+(assuming you haven't specified any :command:`sdist` options in the setup script
+or config file), :command:`sdist` creates the archive of the default format for
+the current platform. The default format is a gzip'ed tar file
+(:file:`.tar.gz`) on Unix, and ZIP file on Windows.
+
+You can specify as many formats as you like using the :option:`--formats`
+option, for example::
+
+ python setup.py sdist --formats=gztar,zip
+
+to create a gzipped tarball and a zip file. The available formats are:
+
++-----------+-------------------------+---------+
+| Format | Description | Notes |
++===========+=========================+=========+
+| ``zip`` | zip file (:file:`.zip`) | (1),(3) |
++-----------+-------------------------+---------+
+| ``gztar`` | gzip'ed tar file | \(2) |
+| | (:file:`.tar.gz`) | |
++-----------+-------------------------+---------+
+| ``bztar`` | bzip2'ed tar file | |
+| | (:file:`.tar.bz2`) | |
++-----------+-------------------------+---------+
+| ``tar`` | tar file (:file:`.tar`) | |
++-----------+-------------------------+---------+
+
+Notes:
+
+(1)
+ default on Windows
+
+(2)
+ default on Unix
+
+(3)
+ requires either external :program:`zip` utility or :mod:`zipfile` module (part
+ of the standard Python library since Python 1.6)
+
+When using any ``tar`` format (``gztar``, ``bztar`` or
+``tar``) under Unix, you can specify the ``owner`` and ``group`` names
+that will be set for each member of the archive.
+
+For example, if you want all files of the archive to be owned by root::
+
+ python setup.py sdist --owner=root --group=root
+
+
+.. _packaging-manifest:
+
+Specifying the files to distribute
+==================================
+
+If you don't supply an explicit list of files (or instructions on how to
+generate one), the :command:`sdist` command puts a minimal default set into the
+source distribution:
+
+* all Python source files implied by the :option:`py_modules` and
+ :option:`packages` options
+
+* all C source files mentioned in the :option:`ext_modules` or
+ :option:`libraries` options
+
+* scripts identified by the :option:`scripts` option
+ See :ref:`packaging-installing-scripts`.
+
+* anything that looks like a test script: :file:`test/test\*.py` (currently, the
+ Packaging don't do anything with test scripts except include them in source
+ distributions, but in the future there will be a standard for testing Python
+ module distributions)
+
+* the configuration file :file:`setup.cfg`
+
+* all files that matches the ``package_data`` metadata.
+ See :ref:`packaging-installing-package-data`.
+
+* all files that matches the ``data_files`` metadata.
+ See :ref:`packaging-additional-files`.
+
+Contrary to Distutils, :file:`README` (or :file:`README.txt`) and
+:file:`setup.py` are not included by default.
+
+Sometimes this is enough, but usually you will want to specify additional files
+to distribute. The typical way to do this is to write a *manifest template*,
+called :file:`MANIFEST.in` by default. The manifest template is just a list of
+instructions for how to generate your manifest file, :file:`MANIFEST`, which is
+the exact list of files to include in your source distribution. The
+:command:`sdist` command processes this template and generates a manifest based
+on its instructions and what it finds in the filesystem.
+
+If you prefer to roll your own manifest file, the format is simple: one filename
+per line, regular files (or symlinks to them) only. If you do supply your own
+:file:`MANIFEST`, you must specify everything: the default set of files
+described above does not apply in this case.
+
+:file:`MANIFEST` files start with a comment indicating they are generated.
+Files without this comment are not overwritten or removed.
+
+See :ref:`packaging-manifest-template` section for a syntax reference.
+
+
+.. _packaging-manifest-options:
+
+Manifest-related options
+========================
+
+The normal course of operations for the :command:`sdist` command is as follows:
+
+* if the manifest file, :file:`MANIFEST` doesn't exist, read :file:`MANIFEST.in`
+ and create the manifest
+
+* if neither :file:`MANIFEST` nor :file:`MANIFEST.in` exist, create a manifest
+ with just the default file set
+
+* if either :file:`MANIFEST.in` or the setup script (:file:`setup.py`) are more
+ recent than :file:`MANIFEST`, recreate :file:`MANIFEST` by reading
+ :file:`MANIFEST.in`
+
+* use the list of files now in :file:`MANIFEST` (either just generated or read
+ in) to create the source distribution archive(s)
+
+There are a couple of options that modify this behaviour. First, use the
+:option:`--no-defaults` and :option:`--no-prune` to disable the standard
+"include" and "exclude" sets.
+
+Second, you might just want to (re)generate the manifest, but not create a
+source distribution::
+
+ python setup.py sdist --manifest-only
+
+:option:`-o` is a shortcut for :option:`--manifest-only`.
+
+
+.. _packaging-manifest-template:
+
+The MANIFEST.in template
+========================
+
+A :file:`MANIFEST.in` file can be added in a project to define the list of
+files to include in the distribution built by the :command:`sdist` command.
+
+When :command:`sdist` is run, it will look for the :file:`MANIFEST.in` file
+and interpret it to generate the :file:`MANIFEST` file that contains the
+list of files that will be included in the package.
+
+This mechanism can be used when the default list of files is not enough.
+(See :ref:`packaging-manifest`).
+
+Principle
+---------
+
+The manifest template has one command per line, where each command specifies a
+set of files to include or exclude from the source distribution. For an
+example, let's look at the Packaging' own manifest template::
+
+ include *.txt
+ recursive-include examples *.txt *.py
+ prune examples/sample?/build
+
+The meanings should be fairly clear: include all files in the distribution root
+matching :file:`\*.txt`, all files anywhere under the :file:`examples` directory
+matching :file:`\*.txt` or :file:`\*.py`, and exclude all directories matching
+:file:`examples/sample?/build`. All of this is done *after* the standard
+include set, so you can exclude files from the standard set with explicit
+instructions in the manifest template. (Or, you can use the
+:option:`--no-defaults` option to disable the standard set entirely.)
+
+The order of commands in the manifest template matters: initially, we have the
+list of default files as described above, and each command in the template adds
+to or removes from that list of files. Once we have fully processed the
+manifest template, we remove files that should not be included in the source
+distribution:
+
+* all files in the Packaging "build" tree (default :file:`build/`)
+
+* all files in directories named :file:`RCS`, :file:`CVS`, :file:`.svn`,
+ :file:`.hg`, :file:`.git`, :file:`.bzr` or :file:`_darcs`
+
+Now we have our complete list of files, which is written to the manifest for
+future reference, and then used to build the source distribution archive(s).
+
+You can disable the default set of included files with the
+:option:`--no-defaults` option, and you can disable the standard exclude set
+with :option:`--no-prune`.
+
+Following the Packaging' own manifest template, let's trace how the
+:command:`sdist` command builds the list of files to include in the Packaging
+source distribution:
+
+#. include all Python source files in the :file:`packaging` and
+ :file:`packaging/command` subdirectories (because packages corresponding to
+ those two directories were mentioned in the :option:`packages` option in the
+ setup script---see section :ref:`packaging-setup-script`)
+
+#. include :file:`README.txt`, :file:`setup.py`, and :file:`setup.cfg` (standard
+ files)
+
+#. include :file:`test/test\*.py` (standard files)
+
+#. include :file:`\*.txt` in the distribution root (this will find
+ :file:`README.txt` a second time, but such redundancies are weeded out later)
+
+#. include anything matching :file:`\*.txt` or :file:`\*.py` in the sub-tree
+ under :file:`examples`,
+
+#. exclude all files in the sub-trees starting at directories matching
+ :file:`examples/sample?/build`\ ---this may exclude files included by the
+ previous two steps, so it's important that the ``prune`` command in the manifest
+ template comes after the ``recursive-include`` command
+
+#. exclude the entire :file:`build` tree, and any :file:`RCS`, :file:`CVS`,
+ :file:`.svn`, :file:`.hg`, :file:`.git`, :file:`.bzr` and :file:`_darcs`
+ directories
+
+Just like in the setup script, file and directory names in the manifest template
+should always be slash-separated; the Packaging will take care of converting
+them to the standard representation on your platform. That way, the manifest
+template is portable across operating systems.
+
+Commands
+--------
+
+The manifest template commands are:
+
++-------------------------------------------+-----------------------------------------------+
+| Command | Description |
++===========================================+===============================================+
+| :command:`include pat1 pat2 ...` | include all files matching any of the listed |
+| | patterns |
++-------------------------------------------+-----------------------------------------------+
+| :command:`exclude pat1 pat2 ...` | exclude all files matching any of the listed |
+| | patterns |
++-------------------------------------------+-----------------------------------------------+
+| :command:`recursive-include dir pat1 pat2 | include all files under *dir* matching any of |
+| ...` | the listed patterns |
++-------------------------------------------+-----------------------------------------------+
+| :command:`recursive-exclude dir pat1 pat2 | exclude all files under *dir* matching any of |
+| ...` | the listed patterns |
++-------------------------------------------+-----------------------------------------------+
+| :command:`global-include pat1 pat2 ...` | include all files anywhere in the source tree |
+| | matching --- & any of the listed patterns |
++-------------------------------------------+-----------------------------------------------+
+| :command:`global-exclude pat1 pat2 ...` | exclude all files anywhere in the source tree |
+| | matching --- & any of the listed patterns |
++-------------------------------------------+-----------------------------------------------+
+| :command:`prune dir` | exclude all files under *dir* |
++-------------------------------------------+-----------------------------------------------+
+| :command:`graft dir` | include all files under *dir* |
++-------------------------------------------+-----------------------------------------------+
+
+The patterns here are Unix-style "glob" patterns: ``*`` matches any sequence of
+regular filename characters, ``?`` matches any single regular filename
+character, and ``[range]`` matches any of the characters in *range* (e.g.,
+``a-z``, ``a-zA-Z``, ``a-f0-9_.``). The definition of "regular filename
+character" is platform-specific: on Unix it is anything except slash; on Windows
+anything except backslash or colon.
diff --git a/Doc/packaging/tutorial.rst b/Doc/packaging/tutorial.rst
new file mode 100644
index 0000000..04f41e5
--- /dev/null
+++ b/Doc/packaging/tutorial.rst
@@ -0,0 +1,112 @@
+==================
+Packaging tutorial
+==================
+
+Welcome to the Packaging tutorial! We will learn how to use Packaging
+to package your project.
+
+.. TODO merge with introduction.rst
+
+
+Getting started
+---------------
+
+Packaging works with the *setup.cfg* file. It contains all the metadata for
+your project, as defined in PEP 345, but also declare what your project
+contains.
+
+Let's say you have a project called *CLVault* containing one package called
+*clvault*, and a few scripts inside. You can use the *pysetup* script to create
+a *setup.cfg* file for the project. The script will ask you a few questions::
+
+ $ mkdir CLVault
+ $ cd CLVault
+ $ pysetup create
+ Project name [CLVault]:
+ Current version number: 0.1
+ Package description:
+ >Command-line utility to store and retrieve passwords
+ Author name: Tarek Ziade
+ Author e-mail address: tarek@ziade.org
+ Project Home Page: http://bitbucket.org/tarek/clvault
+ Do you want to add a package ? (y/n): y
+ Package name: clvault
+ Do you want to add a package ? (y/n): n
+ Do you want to set Trove classifiers? (y/n): y
+ Please select the project status:
+
+ 1 - Planning
+ 2 - Pre-Alpha
+ 3 - Alpha
+ 4 - Beta
+ 5 - Production/Stable
+ 6 - Mature
+ 7 - Inactive
+
+ Status: 3
+ What license do you use: GPL
+ Matching licenses:
+
+ 1) License :: OSI Approved :: GNU General Public License (GPL)
+ 2) License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
+
+ Type the number of the license you wish to use or ? to try again:: 1
+ Do you want to set other trove identifiers (y/n) [n]: n
+ Wrote "setup.cfg".
+
+
+A setup.cfg file is created, containing the metadata of your project and the
+list of the packages it contains::
+
+ $ cat setup.cfg
+ [metadata]
+ name = CLVault
+ version = 0.1
+ author = Tarek Ziade
+ author_email = tarek@ziade.org
+ description = Command-line utility to store and retrieve passwords
+ home_page = http://bitbucket.org/tarek/clvault
+
+ classifier = Development Status :: 3 - Alpha
+ License :: OSI Approved :: GNU General Public License (GPL)
+
+ [files]
+ packages = clvault
+
+
+Our project will depend on the *keyring* project. Let's add it in the
+[metadata] section::
+
+ [metadata]
+ ...
+ requires_dist =
+ keyring
+
+
+Running commands
+----------------
+
+You can run useful commands on your project once the setup.cfg file is ready:
+
+- sdist: creates a source distribution
+- register: register your project to PyPI
+- upload: upload the distribution to PyPI
+- install_dist: install it
+
+All commands are run using the run script::
+
+ $ pysetup run install_dist
+ $ pysetup run sdist
+ $ pysetup run upload
+
+If you want to push a source distribution of your project to PyPI, do::
+
+ $ pysetup run sdist register upload
+
+
+Installing the project
+----------------------
+
+The project can be installed by manually running the packaging install command::
+
+ $ pysetup run install_dist
diff --git a/Doc/packaging/uploading.rst b/Doc/packaging/uploading.rst
new file mode 100644
index 0000000..297518b
--- /dev/null
+++ b/Doc/packaging/uploading.rst
@@ -0,0 +1,80 @@
+.. _packaging-package-upload:
+
+***************************************
+Uploading Packages to the Package Index
+***************************************
+
+The Python Package Index (PyPI) not only stores the package info, but also the
+package data if the author of the package wishes to. The packaging command
+:command:`upload` pushes the distribution files to PyPI.
+
+The command is invoked immediately after building one or more distribution
+files. For example, the command ::
+
+ python setup.py sdist bdist_wininst upload
+
+will cause the source distribution and the Windows installer to be uploaded to
+PyPI. Note that these will be uploaded even if they are built using an earlier
+invocation of :file:`setup.py`, but that only distributions named on the command
+line for the invocation including the :command:`upload` command are uploaded.
+
+The :command:`upload` command uses the username, password, and repository URL
+from the :file:`$HOME/.pypirc` file (see section :ref:`packaging-pypirc` for more on this
+file). If a :command:`register` command was previously called in the same
+command, and if the password was entered in the prompt, :command:`upload` will
+reuse the entered password. This is useful if you do not want to store a clear
+text password in the :file:`$HOME/.pypirc` file.
+
+You can specify another PyPI server with the :option:`--repository=*url*`
+option::
+
+ python setup.py sdist bdist_wininst upload -r http://example.com/pypi
+
+See section :ref:`packaging-pypirc` for more on defining several servers.
+
+You can use the :option:`--sign` option to tell :command:`upload` to sign each
+uploaded file using GPG (GNU Privacy Guard). The :program:`gpg` program must
+be available for execution on the system :envvar:`PATH`. You can also specify
+which key to use for signing using the :option:`--identity=*name*` option.
+
+Other :command:`upload` options include :option:`--repository=<url>` or
+:option:`--repository=<section>` where *url* is the url of the server and
+*section* the name of the section in :file:`$HOME/.pypirc`, and
+:option:`--show-response` (which displays the full response text from the PyPI
+server for help in debugging upload problems).
+
+PyPI package display
+====================
+
+The ``description`` field plays a special role at PyPI. It is used by
+the server to display a home page for the registered package.
+
+If you use the `reStructuredText <http://docutils.sourceforge.net/rst.html>`_
+syntax for this field, PyPI will parse it and display an HTML output for
+the package home page.
+
+The ``description`` field can be filled from a text file located in the
+project::
+
+ from packaging.core import setup
+
+ fp = open('README.txt')
+ try:
+ description = fp.read()
+ finally:
+ fp.close()
+
+ setup(name='Packaging',
+ description=description)
+
+In that case, :file:`README.txt` is a regular reStructuredText text file located
+in the root of the package besides :file:`setup.py`.
+
+To prevent registering broken reStructuredText content, you can use the
+:program:`rst2html` program that is provided by the :mod:`docutils` package
+and check the ``description`` from the command line::
+
+ $ python setup.py --description | rst2html.py > output.html
+
+:mod:`docutils` will display a warning if there's something wrong with your
+syntax.
diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst
index d01b1f2..55fd76b 100644
--- a/Doc/reference/datamodel.rst
+++ b/Doc/reference/datamodel.rst
@@ -276,16 +276,16 @@ Sequences
single: integer
single: Unicode
- The items of a string object are Unicode code units. A Unicode code
- unit is represented by a string object of one item and can hold either
- a 16-bit or 32-bit value representing a Unicode ordinal (the maximum
- value for the ordinal is given in ``sys.maxunicode``, and depends on
- how Python is configured at compile time). Surrogate pairs may be
- present in the Unicode object, and will be reported as two separate
- items. The built-in functions :func:`chr` and :func:`ord` convert
- between code units and nonnegative integers representing the Unicode
- ordinals as defined in the Unicode Standard 3.0. Conversion from and to
- other encodings are possible through the string method :meth:`encode`.
+ A string is a sequence of values that represent Unicode codepoints.
+ All the codepoints in range ``U+0000 - U+10FFFF`` can be represented
+ in a string. Python doesn't have a :c:type:`chr` type, and
+ every character in the string is represented as a string object
+ with length ``1``. The built-in function :func:`ord` converts a
+ character to its codepoint (as an integer); :func:`chr` converts
+ an integer in range ``0 - 10FFFF`` to the corresponding character.
+ :meth:`str.encode` can be used to convert a :class:`str` to
+ :class:`bytes` using the given encoding, and :meth:`bytes.decode` can
+ be used to achieve the opposite.
Tuples
.. index::
@@ -448,6 +448,11 @@ Callable types
+-------------------------+-------------------------------+-----------+
| :attr:`__name__` | The function's name | Writable |
+-------------------------+-------------------------------+-----------+
+ | :attr:`__qualname__` | The function's | Writable |
+ | | :term:`qualified name` | |
+ | | | |
+ | | .. versionadded:: 3.3 | |
+ +-------------------------+-------------------------------+-----------+
| :attr:`__module__` | The name of the module the | Writable |
| | function was defined in, or | |
| | ``None`` if unavailable. | |
@@ -1351,7 +1356,8 @@ access (use of, assignment to, or deletion of ``x.name``) for class instances.
.. method:: object.__dir__(self)
- Called when :func:`dir` is called on the object. A list must be returned.
+ Called when :func:`dir` is called on the object. A sequence must be
+ returned. :func:`dir` converts the returned sequence to a list and sorts it.
.. _descriptors:
diff --git a/Doc/reference/lexical_analysis.rst b/Doc/reference/lexical_analysis.rst
index 4b49738..5900daa 100644
--- a/Doc/reference/lexical_analysis.rst
+++ b/Doc/reference/lexical_analysis.rst
@@ -492,13 +492,13 @@ Escape sequences only recognized in string literals are:
+-----------------+---------------------------------+-------+
| Escape Sequence | Meaning | Notes |
+=================+=================================+=======+
-| ``\N{name}`` | Character named *name* in the | |
+| ``\N{name}`` | Character named *name* in the | \(4) |
| | Unicode database | |
+-----------------+---------------------------------+-------+
-| ``\uxxxx`` | Character with 16-bit hex value | \(4) |
+| ``\uxxxx`` | Character with 16-bit hex value | \(5) |
| | *xxxx* | |
+-----------------+---------------------------------+-------+
-| ``\Uxxxxxxxx`` | Character with 32-bit hex value | \(5) |
+| ``\Uxxxxxxxx`` | Character with 32-bit hex value | \(6) |
| | *xxxxxxxx* | |
+-----------------+---------------------------------+-------+
@@ -516,10 +516,14 @@ Notes:
with the given value.
(4)
+ .. versionchanged:: 3.3
+ Support for name aliases [#]_ has been added.
+
+(5)
Individual code units which form parts of a surrogate pair can be encoded using
this escape sequence. Exactly four hex digits are required.
-(5)
+(6)
Any Unicode character can be encoded this way, but characters outside the Basic
Multilingual Plane (BMP) will be encoded using a surrogate pair if Python is
compiled to use 16-bit code units (the default). Exactly eight hex digits
@@ -706,3 +710,8 @@ The following printing ASCII characters are not used in Python. Their
occurrence outside string literals and comments is an unconditional error::
$ ? `
+
+
+.. rubric:: Footnotes
+
+.. [#] http://www.unicode.org/Public/6.0.0/ucd/NameAliases.txt
diff --git a/Doc/tools/sphinxext/indexcontent.html b/Doc/tools/sphinxext/indexcontent.html
index d5e17cd..778346f 100644
--- a/Doc/tools/sphinxext/indexcontent.html
+++ b/Doc/tools/sphinxext/indexcontent.html
@@ -20,10 +20,10 @@
<span class="linkdescr">tutorial for C/C++ programmers</span></p>
<p class="biglink"><a class="biglink" href="{{ pathto("c-api/index") }}">Python/C API</a><br/>
<span class="linkdescr">reference for C/C++ programmers</span></p>
- <p class="biglink"><a class="biglink" href="{{ pathto("install/index") }}">Installing Python Modules</a><br/>
- <span class="linkdescr">information for installers &amp; sys-admins</span></p>
- <p class="biglink"><a class="biglink" href="{{ pathto("distutils/index") }}">Distributing Python Modules</a><br/>
- <span class="linkdescr">sharing modules with others</span></p>
+ <p class="biglink"><a class="biglink" href="{{ pathto("install/index") }}">Installing Python Projects</a><br/>
+ <span class="linkdescr">finding and installing modules and applications</span></p>
+ <p class="biglink"><a class="biglink" href="{{ pathto("packaging/index") }}">Distributing Python Projects</a><br/>
+ <span class="linkdescr">packaging and distributing modules and applications</span></p>
<p class="biglink"><a class="biglink" href="{{ pathto("documenting/index") }}">Documenting Python</a><br/>
<span class="linkdescr">guide for documentation authors</span></p>
<p class="biglink"><a class="biglink" href="{{ pathto("faq/index") }}">FAQs</a><br/>
diff --git a/Doc/tools/sphinxext/indexsidebar.html b/Doc/tools/sphinxext/indexsidebar.html
index 672492e..3ad24f9 100644
--- a/Doc/tools/sphinxext/indexsidebar.html
+++ b/Doc/tools/sphinxext/indexsidebar.html
@@ -3,7 +3,7 @@
<h3>Docs for other versions</h3>
<ul>
<li><a href="http://docs.python.org/2.7/">Python 2.7 (stable)</a></li>
- <li><a href="http://docs.python.org/3.1/">Python 3.1 (stable)</a></li>
+ <li><a href="http://docs.python.org/3.2/">Python 3.2 (stable)</a></li>
<li><a href="http://www.python.org/doc/versions/">Old versions</a></li>
</ul>
diff --git a/Doc/tools/sphinxext/pyspecific.py b/Doc/tools/sphinxext/pyspecific.py
index 4329281..d928cfd 100644
--- a/Doc/tools/sphinxext/pyspecific.py
+++ b/Doc/tools/sphinxext/pyspecific.py
@@ -10,7 +10,7 @@
"""
ISSUE_URI = 'http://bugs.python.org/issue%s'
-SOURCE_URI = 'http://hg.python.org/cpython/file/3.2/%s'
+SOURCE_URI = 'http://hg.python.org/cpython/file/default/%s'
from docutils import nodes, utils
from sphinx.util.nodes import split_explicit_title
diff --git a/Doc/tools/sphinxext/susp-ignored.csv b/Doc/tools/sphinxext/susp-ignored.csv
index 350af37..c8fd9c4 100644
--- a/Doc/tools/sphinxext/susp-ignored.csv
+++ b/Doc/tools/sphinxext/susp-ignored.csv
@@ -45,8 +45,8 @@ library/functions,,:stop,"a[start:stop, i]"
library/functions,,:stop,a[start:stop:step]
library/hotshot,,:lineno,"ncalls tottime percall cumtime percall filename:lineno(function)"
library/httplib,,:port,host:port
-library/imaplib,,:MM,"""DD-Mmm-YYYY HH:MM:SS +HHMM"""
-library/imaplib,,:SS,"""DD-Mmm-YYYY HH:MM:SS +HHMM"""
+library/imaplib,,:MM,"""DD-Mmm-YYYY HH:MM:SS"
+library/imaplib,,:SS,"""DD-Mmm-YYYY HH:MM:SS"
library/itertools,,:stop,elements from seq[start:stop:step]
library/itertools,,:step,elements from seq[start:stop:step]
library/linecache,,:sys,"sys:x:3:3:sys:/dev:/bin/sh"
@@ -185,6 +185,99 @@ documenting/fromlatex,152,:noindex,:noindex:
documenting/fromlatex,162,.. describe:,.. describe:: a == b
documenting/fromlatex,168,.. cmdoption:,.. cmdoption:: -O
documenting/fromlatex,168,.. envvar:,.. envvar:: PYTHONINSPECT
+documenting/markup,33,.. sectionauthor:,.. sectionauthor:: Guido van Rossum <guido@python.org>
+documenting/markup,42,:mod,:mod:`parrot` -- Dead parrot access
+documenting/markup,42,`,:mod:`parrot` -- Dead parrot access
+documenting/markup,42,.. module:,.. module:: parrot
+documenting/markup,42,:platform,":platform: Unix, Windows"
+documenting/markup,42,:synopsis,:synopsis: Analyze and reanimate dead parrots.
+documenting/markup,42,.. moduleauthor:,.. moduleauthor:: Eric Cleese <eric@python.invalid>
+documenting/markup,42,.. moduleauthor:,.. moduleauthor:: John Idle <john@python.invalid>
+documenting/markup,88,:noindex,:noindex:
+documenting/markup,95,.. function:,.. function:: spam(eggs)
+documenting/markup,95,:noindex,:noindex:
+documenting/markup,101,.. method:,.. method:: FileInput.input(...)
+documenting/markup,121,:function,c:function
+documenting/markup,121,.. c:,".. c:function:: PyObject* PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems)"
+documenting/markup,121,::,".. c:function:: PyObject* PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems)"
+documenting/markup,131,:member,c:member
+documenting/markup,131,.. c:,.. c:member:: PyObject* PyTypeObject.tp_bases
+documenting/markup,131,::,.. c:member:: PyObject* PyTypeObject.tp_bases
+documenting/markup,139,:macro,c:macro
+documenting/markup,143,:type,c:type
+documenting/markup,150,:var,c:var
+documenting/markup,150,.. cvar:,.. cvar:: PyObject* PyClass_Type
+documenting/markup,179,.. function:,".. function:: Timer.repeat([repeat=3[, number=1000000]])"
+documenting/markup,210,.. decorator:,.. decorator:: removename
+documenting/markup,210,.. decorator:,.. decorator:: setnewname(name)
+documenting/markup,210,:func,:func:
+documenting/markup,233,:meth,:meth:
+documenting/markup,246,.. cmdoption:,.. cmdoption:: -m <module>
+documenting/markup,264,.. describe:,.. describe:: opcode
+documenting/markup,293,.. highlightlang:,.. highlightlang:: c
+documenting/markup,313,.. literalinclude:,.. literalinclude:: example.py
+documenting/markup,328,:rolename,:rolename:`content`
+documenting/markup,328,`,:rolename:`content`
+documenting/markup,333,:role,:role:`title <target>`
+documenting/markup,333,`,:role:`title <target>`
+documenting/markup,339,:meth,:meth:`~Queue.Queue.get`
+documenting/markup,339,`,:meth:`~Queue.Queue.get`
+documenting/markup,387,:func,:func:`filter`
+documenting/markup,387,`,:func:`filter`
+documenting/markup,387,:func,:func:`foo.filter`
+documenting/markup,387,`,:func:`foo.filter`
+documenting/markup,393,:func,:func:`open`
+documenting/markup,393,`,:func:`open`
+documenting/markup,393,:func,:func:`.open`
+documenting/markup,393,`,:func:`.open`
+documenting/markup,409,:data,c:data
+documenting/markup,413,:func,c:func
+documenting/markup,417,:macro,c:macro
+documenting/markup,421,:type,c:type
+documenting/markup,426,:member,c:member
+documenting/markup,476,:file,... is installed in :file:`/usr/lib/python2.{x}/site-packages` ...
+documenting/markup,476,`,... is installed in :file:`/usr/lib/python2.{x}/site-packages` ...
+documenting/markup,495,:kbd,:kbd:`C-x C-f`
+documenting/markup,495,`,:kbd:`C-x C-f`
+documenting/markup,495,:kbd,:kbd:`Control-x Control-f`
+documenting/markup,495,`,:kbd:`Control-x Control-f`
+documenting/markup,509,:mailheader,:mailheader:`Content-Type`
+documenting/markup,509,`,:mailheader:`Content-Type`
+documenting/markup,518,:manpage,:manpage:`ls(1)`
+documenting/markup,518,`,:manpage:`ls(1)`
+documenting/markup,534,:menuselection,:menuselection:`Start --> Programs`
+documenting/markup,534,`,:menuselection:`Start --> Programs`
+documenting/markup,549,`,``code``
+documenting/markup,567,:file,:file:
+documenting/markup,567,`,``code``
+documenting/markup,602,:ref,:ref:`label-name`
+documenting/markup,602,`,:ref:`label-name`
+documenting/markup,606,:ref,"It refers to the section itself, see :ref:`my-reference-label`."
+documenting/markup,606,`,"It refers to the section itself, see :ref:`my-reference-label`."
+documenting/markup,615,:ref,:ref:
+documenting/markup,636,.. note:,.. note::
+documenting/markup,663,.. versionadded:,.. versionadded:: 3.1
+documenting/markup,688,::,.. impl-detail::
+documenting/markup,688,::,.. impl-detail:: This shortly mentions an implementation detail.
+documenting/markup,708,.. seealso:,.. seealso::
+documenting/markup,708,:mod,Module :mod:`zipfile`
+documenting/markup,708,`,Module :mod:`zipfile`
+documenting/markup,708,:mod,Documentation of the :mod:`zipfile` standard module.
+documenting/markup,708,`,Documentation of the :mod:`zipfile` standard module.
+documenting/markup,708,`,"`GNU tar manual, Basic Tar Format <http://link>`_"
+documenting/markup,722,.. centered:,.. centered::
+documenting/markup,767,.. toctree:,.. toctree::
+documenting/markup,767,:maxdepth,:maxdepth: 2
+documenting/markup,783,.. index:,.. index::
+documenting/markup,813,.. index:,".. index:: BNF, grammar, syntax, notation"
+documenting/markup,844,`,"unaryneg ::= ""-"" `integer`"
+documenting/markup,849,.. productionlist:,.. productionlist::
+documenting/markup,849,`,"try1_stmt: ""try"" "":"" `suite`"
+documenting/markup,849,`,": (""except"" [`expression` ["","" `target`]] "":"" `suite`)+"
+documenting/markup,849,`,": [""else"" "":"" `suite`]"
+documenting/markup,849,`,": [""finally"" "":"" `suite`]"
+documenting/markup,849,`,"try2_stmt: ""try"" "":"" `suite`"
+documenting/markup,849,`,": ""finally"" "":"" `suite`"
documenting/rest,33,`,``text``
documenting/rest,47,:rolename,:rolename:`content`
documenting/rest,47,`,:rolename:`content`
@@ -401,3 +494,25 @@ library/pprint,209,::,"'Programming Language :: Python :: 2.6',"
library/pprint,209,::,"'Programming Language :: Python :: 2.7',"
library/pprint,209,::,"'Topic :: Software Development :: Libraries',"
library/pprint,209,::,"'Topic :: Software Development :: Libraries :: Python Modules'],"
+packaging/examples,,`,This is the description of the ``foobar`` project.
+packaging/setupcfg,,::,Development Status :: 3 - Alpha
+packaging/setupcfg,,::,License :: OSI Approved :: Mozilla Public License 1.1 (MPL 1.1)
+packaging/setupscript,,::,"'Development Status :: 4 - Beta',"
+packaging/setupscript,,::,"'Environment :: Console',"
+packaging/setupscript,,::,"'Environment :: Web Environment',"
+packaging/setupscript,,::,"'Intended Audience :: End Users/Desktop',"
+packaging/setupscript,,::,"'Intended Audience :: Developers',"
+packaging/setupscript,,::,"'Intended Audience :: System Administrators',"
+packaging/setupscript,,::,"'License :: OSI Approved :: Python Software Foundation License',"
+packaging/setupscript,,::,"'Operating System :: MacOS :: MacOS X',"
+packaging/setupscript,,::,"'Operating System :: Microsoft :: Windows',"
+packaging/setupscript,,::,"'Operating System :: POSIX',"
+packaging/setupscript,,::,"'Programming Language :: Python',"
+packaging/setupscript,,::,"'Topic :: Communications :: Email',"
+packaging/setupscript,,::,"'Topic :: Office/Business',"
+packaging/setupscript,,::,"'Topic :: Software Development :: Bug Tracking',"
+packaging/tutorial,,::,1) License :: OSI Approved :: GNU General Public License (GPL)
+packaging/tutorial,,::,2) License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
+packaging/tutorial,,::,Type the number of the license you wish to use or ? to try again:: 1
+packaging/tutorial,,::,classifier = Development Status :: 3 - Alpha
+packaging/tutorial,,::,License :: OSI Approved :: GNU General Public License (GPL)
diff --git a/Doc/tutorial/classes.rst b/Doc/tutorial/classes.rst
index 6ee2e94..4926280 100644
--- a/Doc/tutorial/classes.rst
+++ b/Doc/tutorial/classes.rst
@@ -695,9 +695,9 @@ example, the following code will print B, C, D in that order::
class D(C):
pass
- for c in [B, C, D]:
+ for cls in [B, C, D]:
try:
- raise c()
+ raise cls()
except D:
print("D")
except C:
diff --git a/Doc/tutorial/datastructures.rst b/Doc/tutorial/datastructures.rst
index 5fb72fd..83b3012 100644
--- a/Doc/tutorial/datastructures.rst
+++ b/Doc/tutorial/datastructures.rst
@@ -19,13 +19,13 @@ objects:
.. method:: list.append(x)
:noindex:
- Add an item to the end of the list; equivalent to ``a[len(a):] = [x]``.
+ Add an item to the end of the list. Equivalent to ``a[len(a):] = [x]``.
.. method:: list.extend(L)
:noindex:
- Extend the list by appending all the items in the given list; equivalent to
+ Extend the list by appending all the items in the given list. Equivalent to
``a[len(a):] = L``.
@@ -40,8 +40,8 @@ objects:
.. method:: list.remove(x)
:noindex:
- Remove the first item from the list whose value is *x*. It is an error if there
- is no such item.
+ Remove the first item from the list whose value is *x*. It is an error if
+ there is no such item.
.. method:: list.pop([i])
@@ -70,13 +70,14 @@ objects:
.. method:: list.sort()
:noindex:
- Sort the items of the list, in place.
+ Sort the items of the list in place.
.. method:: list.reverse()
:noindex:
- Reverse the elements of the list, in place.
+ Reverse the elements of the list in place.
+
An example that uses most of the list methods::
@@ -99,6 +100,10 @@ An example that uses most of the list methods::
>>> a
[-1, 1, 66.25, 333, 333, 1234.5]
+You might have noticed that methods like ``insert``, ``remove`` or ``sort`` that
+modify the list have no return value printed -- they return ``None``. [1]_ This
+is a design principle for all mutable data structures in Python.
+
.. _tut-lists-as-stacks:
@@ -468,7 +473,7 @@ using a non-existent key.
Performing ``list(d.keys())`` on a dictionary returns a list of all the keys
used in the dictionary, in arbitrary order (if you want it sorted, just use
-``sorted(d.keys())`` instead). [1]_ To check whether a single key is in the
+``sorted(d.keys())`` instead). [2]_ To check whether a single key is in the
dictionary, use the :keyword:`in` keyword.
Here is a small example using a dictionary::
@@ -652,6 +657,9 @@ interpreter will raise a :exc:`TypeError` exception.
.. rubric:: Footnotes
-.. [1] Calling ``d.keys()`` will return a :dfn:`dictionary view` object. It
+.. [1] Other languages may return the mutated object, which allows method
+ chaining, such as ``d->insert("a")->remove("b")->sort();``.
+
+.. [2] Calling ``d.keys()`` will return a :dfn:`dictionary view` object. It
supports operations like membership test and iteration, but its contents
are not independent of the original dictionary -- it is only a *view*.
diff --git a/Doc/tutorial/interpreter.rst b/Doc/tutorial/interpreter.rst
index 2338465..8f08cd8 100644
--- a/Doc/tutorial/interpreter.rst
+++ b/Doc/tutorial/interpreter.rst
@@ -10,11 +10,11 @@ Using the Python Interpreter
Invoking the Interpreter
========================
-The Python interpreter is usually installed as :file:`/usr/local/bin/python3.2`
+The Python interpreter is usually installed as :file:`/usr/local/bin/python3.3`
on those machines where it is available; putting :file:`/usr/local/bin` in your
Unix shell's search path makes it possible to start it by typing the command ::
- python3.2
+ python3.3
to the shell. [#]_ Since the choice of the directory where the interpreter lives
is an installation option, other places are possible; check with your local
@@ -22,11 +22,11 @@ Python guru or system administrator. (E.g., :file:`/usr/local/python` is a
popular alternative location.)
On Windows machines, the Python installation is usually placed in
-:file:`C:\\Python32`, though you can change this when you're running the
+:file:`C:\\Python33`, though you can change this when you're running the
installer. To add this directory to your path, you can type the following
command into the command prompt in a DOS box::
- set path=%path%;C:\python32
+ set path=%path%;C:\python33
Typing an end-of-file character (:kbd:`Control-D` on Unix, :kbd:`Control-Z` on
Windows) at the primary prompt causes the interpreter to exit with a zero exit
@@ -93,8 +93,8 @@ with the *secondary prompt*, by default three dots (``...``). The interpreter
prints a welcome message stating its version number and a copyright notice
before printing the first prompt::
- $ python3.2
- Python 3.2 (py3k, Sep 12 2007, 12:21:02)
+ $ python3.3
+ Python 3.3 (py3k, Sep 12 2007, 12:21:02)
[GCC 3.4.6 20060404 (Red Hat 3.4.6-8)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>
@@ -147,7 +147,7 @@ Executable Python Scripts
On BSD'ish Unix systems, Python scripts can be made directly executable, like
shell scripts, by putting the line ::
- #! /usr/bin/env python3.2
+ #! /usr/bin/env python3.3
(assuming that the interpreter is on the user's :envvar:`PATH`) at the beginning
of the script and giving the file an executable mode. The ``#!`` must be the
diff --git a/Doc/tutorial/stdlib.rst b/Doc/tutorial/stdlib.rst
index 9729743..500ca7f 100644
--- a/Doc/tutorial/stdlib.rst
+++ b/Doc/tutorial/stdlib.rst
@@ -15,7 +15,7 @@ operating system::
>>> import os
>>> os.getcwd() # Return the current working directory
- 'C:\\Python31'
+ 'C:\\Python33'
>>> os.chdir('/server/accesslogs') # Change current working directory
>>> os.system('mkdir today') # Run the command mkdir in the system shell
0
diff --git a/Doc/tutorial/stdlib2.rst b/Doc/tutorial/stdlib2.rst
index fe7f027..85c88dc 100644
--- a/Doc/tutorial/stdlib2.rst
+++ b/Doc/tutorial/stdlib2.rst
@@ -141,7 +141,9 @@ standard size and in little-endian byte order::
import struct
- data = open('myfile.zip', 'rb').read()
+ with open('myfile.zip', 'rb') as f:
+ data = f.read()
+
start = 0
for i in range(3): # show the first 3 file headers
start += 14
@@ -271,7 +273,7 @@ applications include caching objects that are expensive to create::
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
d['primary'] # entry was automatically removed
- File "C:/python31/lib/weakref.py", line 46, in __getitem__
+ File "C:/python33/lib/weakref.py", line 46, in __getitem__
o = self.data[key]()
KeyError: 'primary'
diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst
index c73f1e0..bc38542 100644
--- a/Doc/using/cmdline.rst
+++ b/Doc/using/cmdline.rst
@@ -240,7 +240,9 @@ Miscellaneous options
.. cmdoption:: -S
Disable the import of the module :mod:`site` and the site-dependent
- manipulations of :data:`sys.path` that it entails.
+ manipulations of :data:`sys.path` that it entails. Also disable these
+ manipulations if :mod:`site` is explicitly imported later (call
+ :func:`site.main` if you want them to be triggered).
.. cmdoption:: -u
@@ -482,8 +484,8 @@ These environment variables influence Python's behavior.
Defines the :data:`user base directory <site.USER_BASE>`, which is used to
compute the path of the :data:`user site-packages directory <site.USER_SITE>`
- and :ref:`Distutils installation paths <inst-alt-install-user>` for ``python
- setup.py install --user``.
+ and :ref:`Packaging installation paths <packaging-alt-install-user>` for
+ ``pysetup run install_dist --user``.
.. seealso::
@@ -502,6 +504,14 @@ These environment variables influence Python's behavior.
separated string, it is equivalent to specifying :option:`-W` multiple
times.
+.. envvar:: PYTHONFAULTHANDLER
+
+ If this environment variable is set, :func:`faulthandler.enable` is called
+ at startup: install a handler for :const:`SIGSEGV`, :const:`SIGFPE`,
+ :const:`SIGABRT`, :const:`SIGBUS` and :const:`SIGILL` signals to dump the
+ Python traceback. This is equivalent to :option:`-X` ``faulthandler``
+ option.
+
Debug-mode variables
~~~~~~~~~~~~~~~~~~~~
diff --git a/Doc/whatsnew/3.2.rst b/Doc/whatsnew/3.2.rst
index 0553ec3..99827ff 100644
--- a/Doc/whatsnew/3.2.rst
+++ b/Doc/whatsnew/3.2.rst
@@ -270,7 +270,7 @@ launch of four parallel threads for copying files::
e.submit(shutil.copy, 'src1.txt', 'dest1.txt')
e.submit(shutil.copy, 'src2.txt', 'dest2.txt')
e.submit(shutil.copy, 'src3.txt', 'dest3.txt')
- e.submit(shutil.copy, 'src4.txt', 'dest4.txt')
+ e.submit(shutil.copy, 'src3.txt', 'dest4.txt')
.. seealso::
@@ -2354,7 +2354,7 @@ A number of small performance enhancements have been added:
(Contributed by Antoine Pitrou; :issue:`3001`.)
* The fast-search algorithm in stringlib is now used by the :meth:`split`,
- :meth:`splitlines` and :meth:`replace` methods on
+ :meth:`rsplit`, :meth:`splitlines` and :meth:`replace` methods on
:class:`bytes`, :class:`bytearray` and :class:`str` objects. Likewise, the
algorithm is also used by :meth:`rfind`, :meth:`rindex`, :meth:`rsplit` and
:meth:`rpartition`.
@@ -2471,14 +2471,14 @@ Code Repository
In addition to the existing Subversion code repository at http://svn.python.org
there is now a `Mercurial <http://mercurial.selenic.com/>`_ repository at
-http://hg.python.org/\.
+http://hg.python.org/ .
After the 3.2 release, there are plans to switch to Mercurial as the primary
repository. This distributed version control system should make it easier for
members of the community to create and share external changesets. See
:pep:`385` for details.
-To learn the new version control system, see the `tutorial by Joel
+To learn to use the new version control system, see the `tutorial by Joel
Spolsky <http://hginit.com>`_ or the `Guide to Mercurial Workflows
<http://mercurial.selenic.com/guide/>`_.
diff --git a/Doc/whatsnew/3.3.rst b/Doc/whatsnew/3.3.rst
new file mode 100644
index 0000000..8ca94c9
--- /dev/null
+++ b/Doc/whatsnew/3.3.rst
@@ -0,0 +1,884 @@
+****************************
+ What's New In Python 3.3
+****************************
+
+:Author: Raymond Hettinger
+:Release: |release|
+:Date: |today|
+
+.. Rules for maintenance:
+
+ * Anyone can add text to this document. Do not spend very much time
+ on the wording of your changes, because your text will probably
+ get rewritten to some degree.
+
+ * The maintainer will go through Misc/NEWS periodically and add
+ changes; it's therefore more important to add your changes to
+ Misc/NEWS than to this file.
+
+ * This is not a complete list of every single change; completeness
+ is the purpose of Misc/NEWS. Some changes I consider too small
+ or esoteric to include. If such a change is added to the text,
+ I'll just remove it. (This is another reason you shouldn't spend
+ too much time on writing your addition.)
+
+ * If you want to draw your new text to the attention of the
+ maintainer, add 'XXX' to the beginning of the paragraph or
+ section.
+
+ * It's OK to just add a fragmentary note about a change. For
+ example: "XXX Describe the transmogrify() function added to the
+ socket module." The maintainer will research the change and
+ write the necessary text.
+
+ * You can comment out your additions if you like, but it's not
+ necessary (especially when a final release is some months away).
+
+ * Credit the author of a patch or bugfix. Just the name is
+ sufficient; the e-mail address isn't necessary.
+
+ * It's helpful to add the bug/patch number as a comment:
+
+ XXX Describe the transmogrify() function added to the socket
+ module.
+ (Contributed by P.Y. Developer in :issue:`12345`.)
+
+ This saves the maintainer the effort of going through the Mercurial log
+ when researching a change.
+
+This article explains the new features in Python 3.3, compared to 3.2.
+
+
+.. _pep-393:
+
+PEP 393: Flexible String Representation
+=======================================
+
+The Unicode string type is changed to support multiple internal
+representations, depending on the character with the largest Unicode ordinal
+(1, 2, or 4 bytes) in the represented string. This allows a space-efficient
+representation in common cases, but gives access to full UCS-4 on all
+systems. For compatibility with existing APIs, several representations may
+exist in parallel; over time, this compatibility should be phased out.
+
+On the Python side, there should be no downside to this change.
+
+On the C API side, PEP 393 is fully backward compatible. The legacy API
+should remain available at least five years. Applications using the legacy
+API will not fully benefit of the memory reduction, or - worse - may use
+a bit more memory, because Python may have to maintain two versions of each
+string (in the legacy format and in the new efficient storage).
+
+Functionality
+-------------
+
+Changes introduced by :pep:`393` are the following:
+
+* Python now always supports the full range of Unicode codepoints, including
+ non-BMP ones (i.e. from ``U+0000`` to ``U+10FFFF``). The distinction between
+ narrow and wide builds no longer exists and Python now behaves like a wide
+ build, even under Windows.
+
+* With the death of narrow builds, the problems specific to narrow builds have
+ also been fixed, for example:
+
+ * :func:`len` now always returns 1 for non-BMP characters,
+ so ``len('\U0010FFFF') == 1``;
+
+ * surrogate pairs are not recombined in string literals,
+ so ``'\uDBFF\uDFFF' != '\U0010FFFF'``;
+
+ * indexing or slicing non-BMP characters returns the expected value,
+ so ``'\U0010FFFF'[0]`` now returns ``'\U0010FFFF'`` and not ``'\uDBFF'``;
+
+ * all other functions in the standard library now correctly handle
+ non-BMP codepoints.
+
+* The value of :data:`sys.maxunicode` is now always ``1114111`` (``0x10FFFF``
+ in hexadecimal). The :c:func:`PyUnicode_GetMax` function still returns
+ either ``0xFFFF`` or ``0x10FFFF`` for backward compatibility, and it should
+ not be used with the new Unicode API (see :issue:`13054`).
+
+* The :file:`./configure` flag ``--with-wide-unicode`` has been removed.
+
+Performance and resource usage
+------------------------------
+
+The storage of Unicode strings now depends on the highest codepoint in the string:
+
+* pure ASCII and Latin1 strings (``U+0000-U+00FF``) use 1 byte per codepoint;
+
+* BMP strings (``U+0000-U+FFFF``) use 2 bytes per codepoint;
+
+* non-BMP strings (``U+10000-U+10FFFF``) use 4 bytes per codepoint.
+
+The net effect is that for most applications, memory usage of string storage
+should decrease significantly - especially compared to former wide unicode
+builds - as, in many cases, strings will be pure ASCII even in international
+contexts (because many strings store non-human language data, such as XML
+fragments, HTTP headers, JSON-encoded data, etc.). We also hope that it
+will, for the same reasons, increase CPU cache efficiency on non-trivial
+applications.
+
+.. The memory usage of Python 3.3 is two to three times smaller than Python 3.2,
+ and a little bit better than Python 2.7, on a `Django benchmark
+ <http://mail.python.org/pipermail/python-dev/2011-September/113714.html>`_.
+ XXX The result should be moved in the PEP and a link to the PEP should
+ be added here.
+
+
+PEP 3151: Reworking the OS and IO exception hierarchy
+=====================================================
+
+:pep:`3151` - Reworking the OS and IO exception hierarchy
+ PEP written and implemented by Antoine Pitrou.
+
+The hierarchy of exceptions raised by operating system errors is now both
+simplified and finer-grained.
+
+You don't have to worry anymore about choosing the appropriate exception
+type between :exc:`OSError`, :exc:`IOError`, :exc:`EnvironmentError`,
+:exc:`WindowsError`, :exc:`mmap.error`, :exc:`socket.error` or
+:exc:`select.error`. All these exception types are now only one:
+:exc:`OSError`. The other names are kept as aliases for compatibility
+reasons.
+
+Also, it is now easier to catch a specific error condition. Instead of
+inspecting the ``errno`` attribute (or ``args[0]``) for a particular
+constant from the :mod:`errno` module, you can catch the adequate
+:exc:`OSError` subclass. The available subclasses are the following:
+
+* :exc:`BlockingIOError`
+* :exc:`ChildProcessError`
+* :exc:`ConnectionError`
+* :exc:`FileExistsError`
+* :exc:`FileNotFoundError`
+* :exc:`InterruptedError`
+* :exc:`IsADirectoryError`
+* :exc:`NotADirectoryError`
+* :exc:`PermissionError`
+* :exc:`ProcessLookupError`
+* :exc:`TimeoutError`
+
+And the :exc:`ConnectionError` itself has finer-grained subclasses:
+
+* :exc:`BrokenPipeError`
+* :exc:`ConnectionAbortedError`
+* :exc:`ConnectionRefusedError`
+* :exc:`ConnectionResetError`
+
+Thanks to the new exceptions, common usages of the :mod:`errno` can now be
+avoided. For example, the following code written for Python 3.2::
+
+ from errno import ENOENT, EACCES, EPERM
+
+ try:
+ with open("document.txt") as f:
+ content = f.read()
+ except IOError as err:
+ if err.errno == ENOENT:
+ print("document.txt file is missing")
+ elif err.errno in (EACCES, EPERM):
+ print("You are not allowed to read document.txt")
+ else:
+ raise
+
+can now be written without the :mod:`errno` import and without manual
+inspection of exception attributes::
+
+ try:
+ with open("document.txt") as f:
+ content = f.read()
+ except FileNotFoundError:
+ print("document.txt file is missing")
+ except PermissionError:
+ print("You are not allowed to read document.txt")
+
+
+PEP 3155: Qualified name for classes and functions
+==================================================
+
+:pep:`3155` - Qualified name for classes and functions
+ PEP written and implemented by Antoine Pitrou.
+
+Functions and class objects have a new ``__qualname__`` attribute representing
+the "path" from the module top-level to their definition. For global functions
+and classes, this is the same as ``__name__``. For other functions and classes,
+it provides better information about where they were actually defined, and
+how they might be accessible from the global scope.
+
+Example with (non-bound) methods::
+
+ >>> class C:
+ ... def meth(self):
+ ... pass
+ >>> C.meth.__name__
+ 'meth'
+ >>> C.meth.__qualname__
+ 'C.meth'
+
+Example with nested classes::
+
+ >>> class C:
+ ... class D:
+ ... def meth(self):
+ ... pass
+ ...
+ >>> C.D.__name__
+ 'D'
+ >>> C.D.__qualname__
+ 'C.D'
+ >>> C.D.meth.__name__
+ 'meth'
+ >>> C.D.meth.__qualname__
+ 'C.D.meth'
+
+Example with nested functions::
+
+ >>> def outer():
+ ... def inner():
+ ... pass
+ ... return inner
+ ...
+ >>> outer().__name__
+ 'inner'
+ >>> outer().__qualname__
+ 'outer.<locals>.inner'
+
+The string representation of those objects is also changed to include the
+new, more precise information::
+
+ >>> str(C.D)
+ "<class '__main__.C.D'>"
+ >>> str(C.D.meth)
+ '<function C.D.meth at 0x7f46b9fe31e0>'
+
+
+Other Language Changes
+======================
+
+Some smaller changes made to the core Python language are:
+
+* Added support for Unicode name aliases and named sequences.
+ Both :func:`unicodedata.lookup()` and ``'\N{...}'`` now resolve name aliases,
+ and :func:`unicodedata.lookup()` resolves named sequences too.
+
+ (Contributed by Ezio Melotti in :issue:`12753`)
+
+* Equality comparisons on :func:`range` objects now return a result reflecting
+ the equality of the underlying sequences generated by those range objects.
+
+ (:issue:`13021`)
+
+* The ``count()``, ``find()``, ``rfind()``, ``index()`` and ``rindex()``
+ methods of :class:`bytes` and :class:`bytearray` objects now accept an
+ integer between 0 and 255 as their first argument.
+
+ (:issue:`12170`)
+
+* Memoryview objects are now hashable when the underlying object is hashable.
+
+ (Contributed by Antoine Pitrou in :issue:`13411`)
+
+
+New and Improved Modules
+========================
+
+array
+-----
+
+The :mod:`array` module supports the :c:type:`long long` type using ``q`` and
+``Q`` type codes.
+
+(Contributed by Oren Tirosh and Hirokazu Yamamoto in :issue:`1172711`)
+
+
+codecs
+------
+
+The :mod:`~encodings.mbcs` codec has be rewritten to handle correclty
+``replace`` and ``ignore`` error handlers on all Windows versions. The
+:mod:`~encodings.mbcs` codec is now supporting all error handlers, instead of
+only ``replace`` to encode and ``ignore`` to decode.
+
+A new Windows-only codec has been added: ``cp65001`` (:issue:`13216`). It is
+the Windows code page 65001 (Windows UTF-8, ``CP_UTF8``). For example, it is
+used by ``sys.stdout`` if the console output code page is set to cp65001 (e.g.
+using ``chcp 65001`` command).
+
+Multibyte CJK decoders now resynchronize faster. They only ignore the first
+byte of an invalid byte sequence. For example, ``b'\xff\n'.decode('gb2312',
+'replace')`` now returns a ``\n`` after the replacement character.
+
+(:issue:`12016`)
+
+Don't reset incremental encoders of CJK codecs at each call to their encode()
+method anymore. For example::
+
+ $ ./python -q
+ >>> import codecs
+ >>> encoder = codecs.getincrementalencoder('hz')('strict')
+ >>> b''.join(encoder.encode(x) for x in '\u52ff\u65bd\u65bc\u4eba\u3002 Bye.')
+ b'~{NpJ)l6HK!#~} Bye.'
+
+This example gives ``b'~{Np~}~{J)~}~{l6~}~{HK~}~{!#~} Bye.'`` with older Python
+versions.
+
+(:issue:`12100`)
+
+The ``unicode_internal`` codec has been deprecated.
+
+crypt
+-----
+
+Addition of salt and modular crypt format and the :func:`~crypt.mksalt`
+function to the :mod:`crypt` module.
+
+(:issue:`10924`)
+
+curses
+------
+
+ * If the :mod:`curses` module is linked to the ncursesw library, use Unicode
+ functions when Unicode strings or characters are passed (e.g.
+ :c:func:`waddwstr`), and bytes functions otherwise (e.g. :c:func:`waddstr`).
+ * Use the locale encoding instead of ``utf-8`` to encode Unicode strings.
+ * :class:`curses.window` has a new :attr:`curses.window.encoding` attribute.
+ * The :class:`curses.window` class has a new :meth:`~curses.window.get_wch`
+ method to get a wide character
+ * The :mod:`curses` module has a new :meth:`~curses.unget_wch` function to
+ push a wide character so the next :meth:`~curses.window.get_wch` will return
+ it
+
+(Contributed by Iñigo Serna in :issue:`6755`)
+
+abc
+---
+
+Improved support for abstract base classes containing descriptors composed with
+abstract methods. The recommended approach to declaring abstract descriptors is
+now to provide :attr:`__isabstractmethod__` as a dynamically updated
+property. The built-in descriptors have been updated accordingly.
+
+ * :class:`abc.abstractproperty` has been deprecated, use :class:`property`
+ with :func:`abc.abstractmethod` instead.
+ * :class:`abc.abstractclassmethod` has been deprecated, use
+ :class:`classmethod` with :func:`abc.abstractmethod` instead.
+ * :class:`abc.abstractstaticmethod` has been deprecated, use
+ :class:`staticmethod` with :func:`abc.abstractmethod` instead.
+
+(Contributed by Darren Dale in :issue:`11610`)
+
+faulthandler
+------------
+
+New module: :mod:`faulthandler`.
+
+ * :envvar:`PYTHONFAULTHANDLER`
+ * :option:`-X` ``faulthandler``
+
+time
+----
+
+* The :mod:`time` module has new :func:`~time.clock_getres` and
+ :func:`~time.clock_gettime` functions and ``CLOCK_xxx`` constants.
+ :func:`~time.clock_gettime` can be used with :data:`time.CLOCK_MONOTONIC` to
+ get a monotonic clock.
+
+ (Contributed by Victor Stinner in :issue:`10278`)
+
+
+ftplib
+------
+
+The :class:`~ftplib.FTP_TLS` class now provides a new
+:func:`~ftplib.FTP_TLS.ccc` function to revert control channel back to
+plaintext. This can be useful to take advantage of firewalls that know how to
+handle NAT with non-secure FTP without opening fixed ports.
+
+(Contributed by Giampaolo Rodolà in :issue:`12139`)
+
+
+imaplib
+-------
+
+The :class:`~imaplib.IMAP4_SSL` constructor now accepts an SSLContext
+parameter to control parameters of the secure channel.
+
+(Contributed by Sijin Joseph in :issue:`8808`)
+
+
+lzma
+----
+
+The newly-added :mod:`lzma` module provides data compression and decompression
+using the LZMA algorithm, including support for the ``.xz`` and ``.lzma``
+file formats.
+
+(Contributed by Nadeem Vawda and Per Øyvind Karlsen in :issue:`6715`)
+
+
+math
+----
+
+The :mod:`math` module has a new function:
+
+ * :func:`~math.log2`: return the base-2 logarithm of *x*
+ (Written by Mark Dickinson in :issue:`11888`).
+
+
+nntplib
+-------
+
+The :class:`nntplib.NNTP` class now supports the context manager protocol to
+unconditionally consume :exc:`socket.error` exceptions and to close the NNTP
+connection when done::
+
+ >>> from nntplib import NNTP
+ >>> with NNTP('news.gmane.org') as n:
+ ... n.group('gmane.comp.python.committers')
+ ...
+ ('211 1755 1 1755 gmane.comp.python.committers', 1755, 1, 1755, 'gmane.comp.python.committers')
+ >>>
+
+(Contributed by Giampaolo Rodolà in :issue:`9795`)
+
+
+os
+--
+
+* The :mod:`os` module has a new :func:`~os.pipe2` function that makes it
+ possible to create a pipe with :data:`~os.O_CLOEXEC` or
+ :data:`~os.O_NONBLOCK` flags set atomically. This is especially useful to
+ avoid race conditions in multi-threaded programs.
+
+* The :mod:`os` module has a new :func:`~os.sendfile` function which provides
+ an efficent "zero-copy" way for copying data from one file (or socket)
+ descriptor to another. The phrase "zero-copy" refers to the fact that all of
+ the copying of data between the two descriptors is done entirely by the
+ kernel, with no copying of data into userspace buffers. :func:`~os.sendfile`
+ can be used to efficiently copy data from a file on disk to a network socket,
+ e.g. for downloading a file.
+
+ (Patch submitted by Ross Lagerwall and Giampaolo Rodolà in :issue:`10882`.)
+
+* The :mod:`os` module has two new functions: :func:`~os.getpriority` and
+ :func:`~os.setpriority`. They can be used to get or set process
+ niceness/priority in a fashion similar to :func:`os.nice` but extended to all
+ processes instead of just the current one.
+
+ (Patch submitted by Giampaolo Rodolà in :issue:`10784`.)
+
+* "at" functions (:issue:`4761`):
+
+ * :func:`~os.faccessat`
+ * :func:`~os.fchmodat`
+ * :func:`~os.fchownat`
+ * :func:`~os.fstatat`
+ * :func:`~os.futimesat`
+ * :func:`~os.futimesat`
+ * :func:`~os.linkat`
+ * :func:`~os.mkdirat`
+ * :func:`~os.mkfifoat`
+ * :func:`~os.mknodat`
+ * :func:`~os.openat`
+ * :func:`~os.readlinkat`
+ * :func:`~os.renameat`
+ * :func:`~os.symlinkat`
+ * :func:`~os.unlinkat`
+ * :func:`~os.utimensat`
+ * :func:`~os.utimensat`
+
+* extended attributes (:issue:`12720`):
+
+ * :func:`~os.fgetxattr`
+ * :func:`~os.flistxattr`
+ * :func:`~os.fremovexattr`
+ * :func:`~os.fsetxattr`
+ * :func:`~os.getxattr`
+ * :func:`~os.lgetxattr`
+ * :func:`~os.listxattr`
+ * :func:`~os.llistxattr`
+ * :func:`~os.lremovexattr`
+ * :func:`~os.lsetxattr`
+ * :func:`~os.removexattr`
+ * :func:`~os.setxattr`
+
+* Scheduler functions (:issue:`12655`):
+
+ * :func:`~os.sched_get_priority_max`
+ * :func:`~os.sched_get_priority_min`
+ * :func:`~os.sched_getaffinity`
+ * :func:`~os.sched_getparam`
+ * :func:`~os.sched_getscheduler`
+ * :func:`~os.sched_rr_get_interval`
+ * :func:`~os.sched_setaffinity`
+ * :func:`~os.sched_setparam`
+ * :func:`~os.sched_setscheduler`
+ * :func:`~os.sched_yield`
+
+* Add some extra posix functions to the os module (:issue:`10812`):
+
+ * :func:`~os.fexecve`
+ * :func:`~os.futimens`
+ * :func:`~os.futimens`
+ * :func:`~os.futimes`
+ * :func:`~os.futimes`
+ * :func:`~os.lockf`
+ * :func:`~os.lutimes`
+ * :func:`~os.lutimes`
+ * :func:`~os.posix_fadvise`
+ * :func:`~os.posix_fallocate`
+ * :func:`~os.pread`
+ * :func:`~os.pwrite`
+ * :func:`~os.readv`
+ * :func:`~os.sync`
+ * :func:`~os.truncate`
+ * :func:`~os.waitid`
+ * :func:`~os.writev`
+
+* Other new functions:
+
+ * :func:`~os.fdlistdir` (:issue:`10755`)
+ * :func:`~os.getgrouplist` (:issue:`9344`)
+
+
+packaging
+---------
+
+:mod:`distutils` has undergone additions and refactoring under a new name,
+:mod:`packaging`, to allow developers to break backward compatibility.
+:mod:`distutils` is still provided in the standard library, but users are
+encouraged to transition to :mod:`packaging`. For older versions of Python, a
+backport compatible with 2.4+ and 3.1+ will be made available on PyPI under the
+name :mod:`distutils2`.
+
+.. TODO add examples and howto to the packaging docs and link to them
+
+
+pydoc
+-----
+
+The Tk GUI and the :func:`~pydoc.serve` function have been removed from the
+:mod:`pydoc` module: ``pydoc -g`` and :func:`~pydoc.serve` have been deprecated
+in Python 3.2.
+
+
+sys
+---
+
+* The :mod:`sys` module has a new :data:`~sys.thread_info` :term:`struct
+ sequence` holding informations about the thread implementation.
+
+ (:issue:`11223`)
+
+
+signal
+------
+
+* The :mod:`signal` module has new functions:
+
+ * :func:`~signal.pthread_sigmask`: fetch and/or change the signal mask of the
+ calling thread (Contributed by Jean-Paul Calderone in :issue:`8407`) ;
+ * :func:`~signal.pthread_kill`: send a signal to a thread ;
+ * :func:`~signal.sigpending`: examine pending functions ;
+ * :func:`~signal.sigwait`: wait a signal.
+ * :func:`~signal.sigwaitinfo`: wait for a signal, returning detailed
+ information about it.
+ * :func:`~signal.sigtimedwait`: like :func:`~signal.sigwaitinfo` but with a
+ timeout.
+
+* The signal handler writes the signal number as a single byte instead of
+ a nul byte into the wakeup file descriptor. So it is possible to wait more
+ than one signal and know which signals were raised.
+
+* :func:`signal.signal` and :func:`signal.siginterrupt` raise an OSError,
+ instead of a RuntimeError: OSError has an errno attribute.
+
+socket
+------
+
+* The :class:`~socket.socket` class now exposes additional methods to process
+ ancillary data when supported by the underlying platform:
+
+ * :func:`~socket.socket.sendmsg`
+ * :func:`~socket.socket.recvmsg`
+ * :func:`~socket.socket.recvmsg_into`
+
+ (Contributed by David Watson in :issue:`6560`, based on an earlier patch by
+ Heiko Wundram)
+
+* The :class:`~socket.socket` class now supports the PF_CAN protocol family
+ (http://en.wikipedia.org/wiki/Socketcan), on Linux
+ (http://lwn.net/Articles/253425).
+
+ (Contributed by Matthias Fuchs, updated by Tiago Gonçalves in :issue:`10141`)
+
+* The :class:`~socket.socket` class now supports the PF_RDS protocol family
+ (http://en.wikipedia.org/wiki/Reliable_Datagram_Sockets and
+ http://oss.oracle.com/projects/rds/).
+
+ssl
+---
+
+* The :mod:`ssl` module has two new random generation functions:
+
+ * :func:`~ssl.RAND_bytes`: generate cryptographically strong
+ pseudo-random bytes.
+ * :func:`~ssl.RAND_pseudo_bytes`: generate pseudo-random bytes.
+
+ (Contributed by Victor Stinner in :issue:`12049`)
+
+* The :mod:`ssl` module now exposes a finer-grained exception hierarchy
+ in order to make it easier to inspect the various kinds of errors.
+
+ (Contributed by Antoine Pitrou in :issue:`11183`)
+
+* :meth:`~ssl.SSLContext.load_cert_chain` now accepts a *password* argument
+ to be used if the private key is encrypted.
+
+ (Contributed by Adam Simpkins in :issue:`12803`)
+
+* SSL sockets have a new :meth:`~ssl.SSLSocket.get_channel_binding` method
+ allowing the implementation of certain authentication mechanisms such as
+ SCRAM-SHA-1-PLUS.
+
+ (Contributed by Jacek Konieczny in :issue:`12551`)
+
+shutil
+------
+
+* The :mod:`shutil` module has these new fuctions:
+
+ * :func:`~shutil.disk_usage`: provides total, used and free disk space
+ statistics. (Contributed by Giampaolo Rodolà in :issue:`12442`)
+ * :func:`~shutil.chown`: allows one to change user and/or group of the given
+ path also specifying the user/group names and not only their numeric
+ ids. (Contributed by Sandro Tosi in :issue:`12191`)
+
+smtplib
+-------
+
+The :class:`~smtplib.SMTP_SSL` constructor and the :meth:`~smtplib.SMTP.starttls`
+method now accept an SSLContext parameter to control parameters of the secure
+channel.
+
+(Contributed by Kasun Herath in :issue:`8809`)
+
+urllib
+------
+
+The :class:`~urllib.request.Request` class, now accepts a *method* argument
+used by :meth:`~urllib.request.Request.get_method` to determine what HTTP method
+should be used. For example, this will send a ``'HEAD'`` request::
+
+ >>> urlopen(Request('http://www.python.org', method='HEAD'))
+
+(:issue:`1673007`)
+
+sched
+-----
+
+* :meth:`~sched.scheduler.run` now accepts a *blocking* parameter which when
+ set to False makes the method execute the scheduled events due to expire
+ soonest (if any) and then return immediately.
+ This is useful in case you want to use the :class:`~sched.scheduler` in
+ non-blocking applications. (Contributed by Giampaolo Rodolà in :issue:`13449`)
+
+* :class:`~sched.scheduler` class can now be safely used in multi-threaded
+ environments. (Contributed by Josiah Carlson and Giampaolo Rodolà in
+ :issue:`8684`)
+
+* *timefunc* and *delayfunct* parameters of :class:`~sched.scheduler` class
+ constructor are now optional and defaults to :func:`time.time` and
+ :func:`time.sleep` respectively. (Contributed by Chris Clark in
+ :issue:`13245`)
+
+* :meth:`~sched.scheduler.enter` and :meth:`~sched.scheduler.enterabs`
+ *argument* parameter is now optional. (Contributed by Chris Clark in
+ :issue:`13245`)
+
+* :meth:`~sched.scheduler.enter` and :meth:`~sched.scheduler.enterabs`
+ now accept a *kwargs* parameter. (Contributed by Chris Clark in
+ :issue:`13245`)
+
+Optimizations
+=============
+
+Major performance enhancements have been added:
+
+* Thanks to the :pep:`393`, some operations on Unicode strings has been optimized:
+
+ * the memory footprint is divided by 2 to 4 depending on the text
+ * encode an ASCII string to UTF-8 doesn't need to encode characters anymore,
+ the UTF-8 representation is shared with the ASCII representation
+ * the UTF-8 encoder has been optimized
+ * repeating a single ASCII letter and getting a substring of a ASCII strings
+ is 4 times faster
+
+
+Build and C API Changes
+=======================
+
+Changes to Python's build process and to the C API include:
+
+* The :pep:`393` added new Unicode types, macros and functions:
+
+ * High-level API:
+
+ * :c:func:`PyUnicode_CopyCharacters`
+ * :c:func:`PyUnicode_FindChar`
+ * :c:func:`PyUnicode_GetLength`, :c:macro:`PyUnicode_GET_LENGTH`
+ * :c:func:`PyUnicode_New`
+ * :c:func:`PyUnicode_Substring`
+ * :c:func:`PyUnicode_ReadChar`, :c:func:`PyUnicode_WriteChar`
+
+ * Low-level API:
+
+ * :c:type:`Py_UCS1`, :c:type:`Py_UCS2`, :c:type:`Py_UCS4` types
+ * :c:type:`PyASCIIObject` and :c:type:`PyCompactUnicodeObject` structures
+ * :c:macro:`PyUnicode_READY`
+ * :c:func:`PyUnicode_FromKindAndData`
+ * :c:func:`PyUnicode_AsUCS4`, :c:func:`PyUnicode_AsUCS4Copy`
+ * :c:macro:`PyUnicode_DATA`, :c:macro:`PyUnicode_1BYTE_DATA`,
+ :c:macro:`PyUnicode_2BYTE_DATA`, :c:macro:`PyUnicode_4BYTE_DATA`
+ * :c:macro:`PyUnicode_KIND` with :c:type:`PyUnicode_Kind` enum:
+ :c:data:`PyUnicode_WCHAR_KIND`, :c:data:`PyUnicode_1BYTE_KIND`,
+ :c:data:`PyUnicode_2BYTE_KIND`, :c:data:`PyUnicode_4BYTE_KIND`
+ * :c:macro:`PyUnicode_READ`, :c:macro:`PyUnicode_READ_CHAR`, :c:macro:`PyUnicode_WRITE`
+ * :c:macro:`PyUnicode_MAX_CHAR_VALUE`
+
+
+
+Deprecated
+==========
+
+Unsupported Operating Systems
+-----------------------------
+
+OS/2 and VMS are no longer supported due to the lack of a maintainer.
+
+Windows 2000 and Windows platforms which set ``COMSPEC`` to ``command.com``
+are no longer supported due to maintenance burden.
+
+
+Deprecated Python modules, functions and methods
+------------------------------------------------
+
+* The :mod:`packaging` module replaces the :mod:`distutils` module
+* The ``unicode_internal`` codec has been deprecated because of the
+ :pep:`393`, use UTF-8, UTF-16 (``utf-16-le`` or ``utf-16-le``), or UTF-32
+ (``utf-32-le`` or ``utf-32-le``)
+* :meth:`ftplib.FTP.nlst` and :meth:`ftplib.FTP.dir`: use
+ :meth:`ftplib.FTP.mlsd`
+* :func:`platform.popen`: use the :mod:`subprocess` module. Check especially
+ the :ref:`subprocess-replacements` section.
+* :issue:`13374`: The Windows bytes API has been deprecated in the :mod:`os`
+ module. Use Unicode filenames, instead of bytes filenames, to not depend on
+ the ANSI code page anymore and to support any filename.
+
+
+Deprecated functions and types of the C API
+-------------------------------------------
+
+The :c:type:`Py_UNICODE` has been deprecated by the :pep:`393` and will be
+removed in Python 4. All functions using this type are deprecated:
+
+Unicode functions and methods using :c:type:`Py_UNICODE` and
+:c:type:`Py_UNICODE*` types:
+
+ * :c:macro:`PyUnicode_FromUnicode`: use :c:func:`PyUnicode_FromWideChar` or
+ :c:func:`PyUnicode_FromKindAndData`
+ * :c:macro:`PyUnicode_AS_UNICODE`, :c:func:`PyUnicode_AsUnicode`,
+ :c:func:`PyUnicode_AsUnicodeAndSize`: use :c:func:`PyUnicode_AsWideCharString`
+ * :c:macro:`PyUnicode_AS_DATA`: use :c:macro:`PyUnicode_DATA` with
+ :c:macro:`PyUnicode_READ` and :c:macro:`PyUnicode_WRITE`
+ * :c:macro:`PyUnicode_GET_SIZE`, :c:func:`PyUnicode_GetSize`: use
+ :c:macro:`PyUnicode_GET_LENGTH` or :c:func:`PyUnicode_GetLength`
+ * :c:macro:`PyUnicode_GET_DATA_SIZE`: use
+ ``PyUnicode_GET_LENGTH(str) * PyUnicode_KIND(str)`` (only work on ready
+ strings)
+ * :c:func:`PyUnicode_AsUnicodeCopy`: use :c:func:`PyUnicode_AsUCS4Copy` or
+ :c:func:`PyUnicode_AsWideCharString`
+ * :c:func:`PyUnicode_GetMax`
+
+
+Functions and macros manipulating Py_UNICODE* strings:
+
+ * :c:macro:`Py_UNICODE_strlen`: use :c:func:`PyUnicode_GetLength` or
+ :c:macro:`PyUnicode_GET_LENGTH`
+ * :c:macro:`Py_UNICODE_strcat`: use :c:func:`PyUnicode_CopyCharacters` or
+ :c:func:`PyUnicode_FromFormat`
+ * :c:macro:`Py_UNICODE_strcpy`, :c:macro:`Py_UNICODE_strncpy`,
+ :c:macro:`Py_UNICODE_COPY`: use :c:func:`PyUnicode_CopyCharacters` or
+ :c:func:`PyUnicode_Substring`
+ * :c:macro:`Py_UNICODE_strcmp`: use :c:func:`PyUnicode_Compare`
+ * :c:macro:`Py_UNICODE_strncmp`: use :c:func:`PyUnicode_Tailmatch`
+ * :c:macro:`Py_UNICODE_strchr`, :c:macro:`Py_UNICODE_strrchr`: use
+ :c:func:`PyUnicode_FindChar`
+ * :c:macro:`Py_UNICODE_FILL`
+ * :c:macro:`Py_UNICODE_MATCH`
+
+Encoders:
+
+ * :c:func:`PyUnicode_Encode`: use :c:func:`PyUnicode_AsEncodedObject`
+ * :c:func:`PyUnicode_EncodeUTF7`
+ * :c:func:`PyUnicode_EncodeUTF8`: use :c:func:`PyUnicode_AsUTF8` or
+ :c:func:`PyUnicode_AsUTF8String`
+ * :c:func:`PyUnicode_EncodeUTF32`
+ * :c:func:`PyUnicode_EncodeUTF16`
+ * :c:func:`PyUnicode_EncodeUnicodeEscape:` use
+ :c:func:`PyUnicode_AsUnicodeEscapeString`
+ * :c:func:`PyUnicode_EncodeRawUnicodeEscape:` use
+ :c:func:`PyUnicode_AsRawUnicodeEscapeString`
+ * :c:func:`PyUnicode_EncodeLatin1`: use :c:func:`PyUnicode_AsLatin1String`
+ * :c:func:`PyUnicode_EncodeASCII`: use :c:func:`PyUnicode_AsASCIIString`
+ * :c:func:`PyUnicode_EncodeCharmap`
+ * :c:func:`PyUnicode_TranslateCharmap`
+ * :c:func:`PyUnicode_EncodeMBCS`: use :c:func:`PyUnicode_AsMBCSString` or
+ :c:func:`PyUnicode_EncodeCodePage` (with ``CP_ACP`` code_page)
+ * :c:func:`PyUnicode_EncodeDecimal`,
+ :c:func:`PyUnicode_TransformDecimalToASCII`
+
+
+Porting to Python 3.3
+=====================
+
+This section lists previously described changes and other bugfixes
+that may require changes to your code.
+
+Porting Python code
+-------------------
+
+* :issue:`12326`: On Linux, sys.platform doesn't contain the major version
+ anymore. It is now always 'linux', instead of 'linux2' or 'linux3' depending
+ on the Linux version used to build Python. Replace sys.platform == 'linux2'
+ with sys.platform.startswith('linux'), or directly sys.platform == 'linux' if
+ you don't need to support older Python versions.
+
+Porting C code
+--------------
+
+* Due to :ref:`PEP 393 <pep-393>`, the :c:type:`Py_UNICODE` type and all
+ functions using this type are deprecated (but will stay available for
+ at least five years). If you were using low-level Unicode APIs to
+ construct and access unicode objects and you want to benefit of the
+ memory footprint reduction provided by the PEP 393, you have to convert
+ your code to the new :doc:`Unicode API <../c-api/unicode>`.
+
+ However, if you only have been using high-level functions such as
+ :c:func:`PyUnicode_Concat()`, :c:func:`PyUnicode_Join` or
+ :c:func:`PyUnicode_FromFormat()`, your code will automatically take
+ advantage of the new unicode representations.
+
+Other issues
+------------
+
+.. Issue #11591: When :program:`python` was started with :option:`-S`,
+ ``import site`` will not add site-specific paths to the module search
+ paths. In previous versions, it did. See changeset for doc changes in
+ various files. Contributed by Carl Meyer with editions by Éric Araujo.
+
+.. Issue #10998: the -Q command-line flag and related artifacts have been
+ removed. Code checking sys.flags.division_warning will need updating.
+ Contributed by Éric Araujo.
diff --git a/Doc/whatsnew/index.rst b/Doc/whatsnew/index.rst
index 8220bd2..c60818a 100644
--- a/Doc/whatsnew/index.rst
+++ b/Doc/whatsnew/index.rst
@@ -11,6 +11,7 @@ anyone wishing to stay up-to-date after a new release.
.. toctree::
:maxdepth: 2
+ 3.3.rst
3.2.rst
3.1.rst
3.0.rst
diff --git a/Grammar/Grammar b/Grammar/Grammar
index cea68de..544852c 100644
--- a/Grammar/Grammar
+++ b/Grammar/Grammar
@@ -13,7 +13,7 @@
# Start symbols for the grammar:
# single_input is a single interactive statement;
# file_input is a module or sequence of commands read from an input file;
-# eval_input is the input for the eval() and input() functions.
+# eval_input is the input for the eval() functions.
# NB: compound_stmt in single_input is followed by extra NEWLINE!
single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE
file_input: (NEWLINE | stmt)* ENDMARKER
diff --git a/Include/Python-ast.h b/Include/Python-ast.h
index 0ad788b..9389049 100644
--- a/Include/Python-ast.h
+++ b/Include/Python-ast.h
@@ -36,6 +36,8 @@ typedef struct _keyword *keyword_ty;
typedef struct _alias *alias_ty;
+typedef struct _withitem *withitem_ty;
+
enum _mod_kind {Module_kind=1, Interactive_kind=2, Expression_kind=3,
Suite_kind=4};
@@ -64,10 +66,9 @@ struct _mod {
enum _stmt_kind {FunctionDef_kind=1, ClassDef_kind=2, Return_kind=3,
Delete_kind=4, Assign_kind=5, AugAssign_kind=6, For_kind=7,
While_kind=8, If_kind=9, With_kind=10, Raise_kind=11,
- TryExcept_kind=12, TryFinally_kind=13, Assert_kind=14,
- Import_kind=15, ImportFrom_kind=16, Global_kind=17,
- Nonlocal_kind=18, Expr_kind=19, Pass_kind=20, Break_kind=21,
- Continue_kind=22};
+ Try_kind=12, Assert_kind=13, Import_kind=14,
+ ImportFrom_kind=15, Global_kind=16, Nonlocal_kind=17,
+ Expr_kind=18, Pass_kind=19, Break_kind=20, Continue_kind=21};
struct _stmt {
enum _stmt_kind kind;
union {
@@ -128,8 +129,7 @@ struct _stmt {
} If;
struct {
- expr_ty context_expr;
- expr_ty optional_vars;
+ asdl_seq *items;
asdl_seq *body;
} With;
@@ -142,12 +142,8 @@ struct _stmt {
asdl_seq *body;
asdl_seq *handlers;
asdl_seq *orelse;
- } TryExcept;
-
- struct {
- asdl_seq *body;
asdl_seq *finalbody;
- } TryFinally;
+ } Try;
struct {
expr_ty test;
@@ -275,7 +271,7 @@ struct _expr {
} Str;
struct {
- string s;
+ bytes s;
} Bytes;
struct {
@@ -383,6 +379,11 @@ struct _alias {
identifier asname;
};
+struct _withitem {
+ expr_ty context_expr;
+ expr_ty optional_vars;
+};
+
#define Module(a0, a1) _Py_Module(a0, a1)
mod_ty _Py_Module(asdl_seq * body, PyArena *arena);
@@ -421,18 +422,16 @@ stmt_ty _Py_While(expr_ty test, asdl_seq * body, asdl_seq * orelse, int lineno,
#define If(a0, a1, a2, a3, a4, a5) _Py_If(a0, a1, a2, a3, a4, a5)
stmt_ty _Py_If(expr_ty test, asdl_seq * body, asdl_seq * orelse, int lineno,
int col_offset, PyArena *arena);
-#define With(a0, a1, a2, a3, a4, a5) _Py_With(a0, a1, a2, a3, a4, a5)
-stmt_ty _Py_With(expr_ty context_expr, expr_ty optional_vars, asdl_seq * body,
- int lineno, int col_offset, PyArena *arena);
+#define With(a0, a1, a2, a3, a4) _Py_With(a0, a1, a2, a3, a4)
+stmt_ty _Py_With(asdl_seq * items, asdl_seq * body, int lineno, int col_offset,
+ PyArena *arena);
#define Raise(a0, a1, a2, a3, a4) _Py_Raise(a0, a1, a2, a3, a4)
stmt_ty _Py_Raise(expr_ty exc, expr_ty cause, int lineno, int col_offset,
PyArena *arena);
-#define TryExcept(a0, a1, a2, a3, a4, a5) _Py_TryExcept(a0, a1, a2, a3, a4, a5)
-stmt_ty _Py_TryExcept(asdl_seq * body, asdl_seq * handlers, asdl_seq * orelse,
- int lineno, int col_offset, PyArena *arena);
-#define TryFinally(a0, a1, a2, a3, a4) _Py_TryFinally(a0, a1, a2, a3, a4)
-stmt_ty _Py_TryFinally(asdl_seq * body, asdl_seq * finalbody, int lineno, int
- col_offset, PyArena *arena);
+#define Try(a0, a1, a2, a3, a4, a5, a6) _Py_Try(a0, a1, a2, a3, a4, a5, a6)
+stmt_ty _Py_Try(asdl_seq * body, asdl_seq * handlers, asdl_seq * orelse,
+ asdl_seq * finalbody, int lineno, int col_offset, PyArena
+ *arena);
#define Assert(a0, a1, a2, a3, a4) _Py_Assert(a0, a1, a2, a3, a4)
stmt_ty _Py_Assert(expr_ty test, expr_ty msg, int lineno, int col_offset,
PyArena *arena);
@@ -502,7 +501,7 @@ expr_ty _Py_Num(object n, int lineno, int col_offset, PyArena *arena);
#define Str(a0, a1, a2, a3) _Py_Str(a0, a1, a2, a3)
expr_ty _Py_Str(string s, int lineno, int col_offset, PyArena *arena);
#define Bytes(a0, a1, a2, a3) _Py_Bytes(a0, a1, a2, a3)
-expr_ty _Py_Bytes(string s, int lineno, int col_offset, PyArena *arena);
+expr_ty _Py_Bytes(bytes s, int lineno, int col_offset, PyArena *arena);
#define Ellipsis(a0, a1, a2) _Py_Ellipsis(a0, a1, a2)
expr_ty _Py_Ellipsis(int lineno, int col_offset, PyArena *arena);
#define Attribute(a0, a1, a2, a3, a4, a5) _Py_Attribute(a0, a1, a2, a3, a4, a5)
@@ -547,6 +546,9 @@ arg_ty _Py_arg(identifier arg, expr_ty annotation, PyArena *arena);
keyword_ty _Py_keyword(identifier arg, expr_ty value, PyArena *arena);
#define alias(a0, a1, a2) _Py_alias(a0, a1, a2)
alias_ty _Py_alias(identifier name, identifier asname, PyArena *arena);
+#define withitem(a0, a1, a2) _Py_withitem(a0, a1, a2)
+withitem_ty _Py_withitem(expr_ty context_expr, expr_ty optional_vars, PyArena
+ *arena);
PyObject* PyAST_mod2obj(mod_ty t);
mod_ty PyAST_obj2mod(PyObject* ast, PyArena* arena, int mode);
diff --git a/Include/Python.h b/Include/Python.h
index 5972ffa..01b98f9 100644
--- a/Include/Python.h
+++ b/Include/Python.h
@@ -48,6 +48,7 @@
#include <assert.h>
#include "pyport.h"
+#include "pymacro.h"
#include "pyatomic.h"
@@ -126,43 +127,6 @@
#include "pystrcmp.h"
#include "dtoa.h"
#include "fileutils.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* _Py_Mangle is defined in compile.c */
-#ifndef Py_LIMITED_API
-PyAPI_FUNC(PyObject*) _Py_Mangle(PyObject *p, PyObject *name);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-/* Argument must be a char or an int in [-128, 127] or [0, 255]. */
-#define Py_CHARMASK(c) ((unsigned char)((c) & 0xff))
-
#include "pyfpe.h"
-/* These definitions must match corresponding definitions in graminit.h.
- There's code in compile.c that checks that they are the same. */
-#define Py_single_input 256
-#define Py_file_input 257
-#define Py_eval_input 258
-
-#ifdef HAVE_PTH
-/* GNU pth user-space thread support */
-#include <pth.h>
-#endif
-
-/* Define macros for inline documentation. */
-#define PyDoc_VAR(name) static char name[]
-#define PyDoc_STRVAR(name,str) PyDoc_VAR(name) = PyDoc_STR(str)
-#ifdef WITH_DOC_STRINGS
-#define PyDoc_STR(str) str
-#else
-#define PyDoc_STR(str) ""
-#endif
-
#endif /* !Py_PYTHON_H */
diff --git a/Include/abstract.h b/Include/abstract.h
index 0fe0956..3946ec5 100644
--- a/Include/abstract.h
+++ b/Include/abstract.h
@@ -7,6 +7,7 @@ extern "C" {
#ifdef PY_SSIZE_T_CLEAN
#define PyObject_CallFunction _PyObject_CallFunction_SizeT
#define PyObject_CallMethod _PyObject_CallMethod_SizeT
+#define _PyObject_CallMethodId _PyObject_CallMethodId_SizeT
#endif
/* Abstract Object Interface (many thanks to Jim Fulton) */
@@ -307,11 +308,22 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
Python expression: o.method(args).
*/
+ PyAPI_FUNC(PyObject *) _PyObject_CallMethodId(PyObject *o, _Py_Identifier *method,
+ char *format, ...);
+
+ /*
+ Like PyObject_CallMethod, but expect a _Py_Identifier* as the
+ method name.
+ */
+
PyAPI_FUNC(PyObject *) _PyObject_CallFunction_SizeT(PyObject *callable,
char *format, ...);
PyAPI_FUNC(PyObject *) _PyObject_CallMethod_SizeT(PyObject *o,
char *name,
char *format, ...);
+ PyAPI_FUNC(PyObject *) _PyObject_CallMethodId_SizeT(PyObject *o,
+ _Py_Identifier *name,
+ char *format, ...);
PyAPI_FUNC(PyObject *) PyObject_CallFunctionObjArgs(PyObject *callable,
...);
diff --git a/Include/asdl.h b/Include/asdl.h
index 9bb0697..42bbbf8 100644
--- a/Include/asdl.h
+++ b/Include/asdl.h
@@ -3,6 +3,7 @@
typedef PyObject * identifier;
typedef PyObject * string;
+typedef PyObject * bytes;
typedef PyObject * object;
/* It would be nice if the code generated by asdl_c.py was completely
diff --git a/Include/ast.h b/Include/ast.h
index a015336..055e8dc 100644
--- a/Include/ast.h
+++ b/Include/ast.h
@@ -4,6 +4,7 @@
extern "C" {
#endif
+PyAPI_FUNC(int) PyAST_Validate(mod_ty);
PyAPI_FUNC(mod_ty) PyAST_FromNode(
const node *n,
PyCompilerFlags *flags,
diff --git a/Include/code.h b/Include/code.h
index e773b6a..7c7e5bf 100644
--- a/Include/code.h
+++ b/Include/code.h
@@ -22,6 +22,7 @@ typedef struct {
PyObject *co_freevars; /* tuple of strings (free variable names) */
PyObject *co_cellvars; /* tuple of strings (cell variable names) */
/* The rest doesn't count for hash or comparisons */
+ unsigned char *co_cell2arg; /* Maps cell vars which are arguments. */
PyObject *co_filename; /* unicode (where it was loaded from) */
PyObject *co_name; /* unicode (name, for reference) */
int co_firstlineno; /* first source line number */
@@ -57,6 +58,11 @@ typedef struct {
#define CO_FUTURE_BARRY_AS_BDFL 0x40000
+/* This value is found in the co_cell2arg array when the associated cell
+ variable does not correspond to an argument. The maximum number of
+ arguments is 255 (indexed up to 254), so 255 work as a special flag.*/
+#define CO_CELL_NOT_AN_ARG 255
+
/* This should be defined if a future statement modifies the syntax.
For example, when a keyword is added.
*/
diff --git a/Include/codecs.h b/Include/codecs.h
index dff09e7..0d9e9b4 100644
--- a/Include/codecs.h
+++ b/Include/codecs.h
@@ -174,6 +174,8 @@ PyAPI_FUNC(PyObject *) PyCodec_XMLCharRefReplaceErrors(PyObject *exc);
/* replace the unicode encode error with backslash escapes (\x, \u and \U) */
PyAPI_FUNC(PyObject *) PyCodec_BackslashReplaceErrors(PyObject *exc);
+PyAPI_DATA(const char *) Py_hexdigits;
+
#ifdef __cplusplus
}
#endif
diff --git a/Include/compile.h b/Include/compile.h
index bc53b39..ac2636d 100644
--- a/Include/compile.h
+++ b/Include/compile.h
@@ -1,7 +1,7 @@
-#ifndef Py_LIMITED_API
#ifndef Py_COMPILE_H
#define Py_COMPILE_H
+#ifndef Py_LIMITED_API
#include "code.h"
#ifdef __cplusplus
@@ -38,9 +38,19 @@ PyAPI_FUNC(PyCodeObject *) PyAST_CompileEx(
PyArena *arena);
PyAPI_FUNC(PyFutureFeatures *) PyFuture_FromAST(struct _mod *, const char *);
+/* _Py_Mangle is defined in compile.c */
+PyAPI_FUNC(PyObject*) _Py_Mangle(PyObject *p, PyObject *name);
#ifdef __cplusplus
}
#endif
-#endif /* !Py_COMPILE_H */
+
#endif /* !Py_LIMITED_API */
+
+/* These definitions must match corresponding definitions in graminit.h.
+ There's code in compile.c that checks that they are the same. */
+#define Py_single_input 256
+#define Py_file_input 257
+#define Py_eval_input 258
+
+#endif /* !Py_COMPILE_H */
diff --git a/Include/complexobject.h b/Include/complexobject.h
index c379b08..3e4ecff 100644
--- a/Include/complexobject.h
+++ b/Include/complexobject.h
@@ -64,8 +64,9 @@ PyAPI_FUNC(Py_complex) PyComplex_AsCComplex(PyObject *op);
(Advanced String Formatting). */
#ifndef Py_LIMITED_API
PyAPI_FUNC(PyObject *) _PyComplex_FormatAdvanced(PyObject *obj,
- Py_UNICODE *format_spec,
- Py_ssize_t format_spec_len);
+ PyObject *format_spec,
+ Py_ssize_t start,
+ Py_ssize_t end);
#endif
#ifdef __cplusplus
diff --git a/Include/descrobject.h b/Include/descrobject.h
index 646b3cc..e2ba97f 100644
--- a/Include/descrobject.h
+++ b/Include/descrobject.h
@@ -42,6 +42,7 @@ typedef struct {
PyObject_HEAD
PyTypeObject *d_type;
PyObject *d_name;
+ PyObject *d_qualname;
} PyDescrObject;
#define PyDescr_COMMON PyDescrObject d_common
diff --git a/Include/dictobject.h b/Include/dictobject.h
index b026785..ed44e20 100644
--- a/Include/dictobject.h
+++ b/Include/dictobject.h
@@ -129,6 +129,8 @@ PyAPI_FUNC(int) _PyDict_Contains(PyObject *mp, PyObject *key, Py_hash_t hash);
PyAPI_FUNC(PyObject *) _PyDict_NewPresized(Py_ssize_t minused);
PyAPI_FUNC(void) _PyDict_MaybeUntrack(PyObject *mp);
PyAPI_FUNC(int) _PyDict_HasOnlyStringKeys(PyObject *mp);
+
+PyAPI_FUNC(int) PyDict_ClearFreeList(void);
#endif
/* PyDict_Update(mp, other) is equivalent to PyDict_Merge(mp, other, 1). */
diff --git a/Include/floatobject.h b/Include/floatobject.h
index 90f0a45..0ca4881 100644
--- a/Include/floatobject.h
+++ b/Include/floatobject.h
@@ -27,12 +27,12 @@ PyAPI_DATA(PyTypeObject) PyFloat_Type;
#define Py_RETURN_NAN return PyFloat_FromDouble(Py_NAN)
#endif
-#define Py_RETURN_INF(sign) do \
- if (copysign(1., sign) == 1.) { \
- return PyFloat_FromDouble(Py_HUGE_VAL); \
- } else { \
- return PyFloat_FromDouble(-Py_HUGE_VAL); \
- } while(0)
+#define Py_RETURN_INF(sign) do \
+ if (copysign(1., sign) == 1.) { \
+ return PyFloat_FromDouble(Py_HUGE_VAL); \
+ } else { \
+ return PyFloat_FromDouble(-Py_HUGE_VAL); \
+ } while(0)
PyAPI_FUNC(double) PyFloat_GetMax(void);
PyAPI_FUNC(double) PyFloat_GetMin(void);
@@ -113,8 +113,9 @@ PyAPI_FUNC(int) PyFloat_ClearFreeList(void);
/* Format the object based on the format_spec, as defined in PEP 3101
(Advanced String Formatting). */
PyAPI_FUNC(PyObject *) _PyFloat_FormatAdvanced(PyObject *obj,
- Py_UNICODE *format_spec,
- Py_ssize_t format_spec_len);
+ PyObject *format_spec,
+ Py_ssize_t start,
+ Py_ssize_t end);
#endif /* Py_LIMITED_API */
#ifdef __cplusplus
diff --git a/Include/funcobject.h b/Include/funcobject.h
index 521d87b..cc1426c 100644
--- a/Include/funcobject.h
+++ b/Include/funcobject.h
@@ -31,6 +31,7 @@ typedef struct {
PyObject *func_weakreflist; /* List of weak references */
PyObject *func_module; /* The __module__ attribute, can be anything */
PyObject *func_annotations; /* Annotations, a dict or NULL */
+ PyObject *func_qualname; /* The qualified name */
/* Invariant:
* func_closure contains the bindings for func_code->co_freevars, so
@@ -44,6 +45,7 @@ PyAPI_DATA(PyTypeObject) PyFunction_Type;
#define PyFunction_Check(op) (Py_TYPE(op) == &PyFunction_Type)
PyAPI_FUNC(PyObject *) PyFunction_New(PyObject *, PyObject *);
+PyAPI_FUNC(PyObject *) PyFunction_NewWithQualName(PyObject *, PyObject *, PyObject *);
PyAPI_FUNC(PyObject *) PyFunction_GetCode(PyObject *);
PyAPI_FUNC(PyObject *) PyFunction_GetGlobals(PyObject *);
PyAPI_FUNC(PyObject *) PyFunction_GetModule(PyObject *);
diff --git a/Include/import.h b/Include/import.h
index 400e97c..45544111 100644
--- a/Include/import.h
+++ b/Include/import.h
@@ -24,7 +24,16 @@ PyAPI_FUNC(PyObject *) PyImport_ExecCodeModuleWithPathnames(
char *pathname, /* decoded from the filesystem encoding */
char *cpathname /* decoded from the filesystem encoding */
);
+PyAPI_FUNC(PyObject *) PyImport_ExecCodeModuleObject(
+ PyObject *name,
+ PyObject *co,
+ PyObject *pathname,
+ PyObject *cpathname
+ );
PyAPI_FUNC(PyObject *) PyImport_GetModuleDict(void);
+PyAPI_FUNC(PyObject *) PyImport_AddModuleObject(
+ PyObject *name
+ );
PyAPI_FUNC(PyObject *) PyImport_AddModule(
const char *name /* UTF-8 encoded string */
);
@@ -35,7 +44,14 @@ PyAPI_FUNC(PyObject *) PyImport_ImportModuleNoBlock(
const char *name /* UTF-8 encoded string */
);
PyAPI_FUNC(PyObject *) PyImport_ImportModuleLevel(
- char *name, /* UTF-8 encoded string */
+ const char *name, /* UTF-8 encoded string */
+ PyObject *globals,
+ PyObject *locals,
+ PyObject *fromlist,
+ int level
+ );
+PyAPI_FUNC(PyObject *) PyImport_ImportModuleLevelObject(
+ PyObject *name,
PyObject *globals,
PyObject *locals,
PyObject *fromlist,
@@ -49,6 +65,9 @@ PyAPI_FUNC(PyObject *) PyImport_GetImporter(PyObject *path);
PyAPI_FUNC(PyObject *) PyImport_Import(PyObject *name);
PyAPI_FUNC(PyObject *) PyImport_ReloadModule(PyObject *m);
PyAPI_FUNC(void) PyImport_Cleanup(void);
+PyAPI_FUNC(int) PyImport_ImportFrozenModuleObject(
+ PyObject *name
+ );
PyAPI_FUNC(int) PyImport_ImportFrozenModule(
char *name /* UTF-8 encoded string */
);
@@ -65,17 +84,17 @@ PyAPI_FUNC(int) _PyImport_ReleaseLock(void);
PyAPI_FUNC(void) _PyImport_ReInitLock(void);
PyAPI_FUNC(PyObject *)_PyImport_FindBuiltin(
- char *name /* UTF-8 encoded string */
+ const char *name /* UTF-8 encoded string */
);
-PyAPI_FUNC(PyObject *)_PyImport_FindExtensionUnicode(char *, PyObject *);
+PyAPI_FUNC(PyObject *)_PyImport_FindExtensionObject(PyObject *, PyObject *);
PyAPI_FUNC(int)_PyImport_FixupBuiltin(
PyObject *mod,
char *name /* UTF-8 encoded string */
);
-PyAPI_FUNC(int)_PyImport_FixupExtensionUnicode(PyObject*, char *, PyObject *);
+PyAPI_FUNC(int)_PyImport_FixupExtensionObject(PyObject*, PyObject *, PyObject *);
struct _inittab {
- char *name;
+ char *name; /* ASCII encoded string */
PyObject* (*initfunc)(void);
};
PyAPI_DATA(struct _inittab *) PyImport_Inittab;
diff --git a/Include/intrcheck.h b/Include/intrcheck.h
index 3b67ed0..f53fee1 100644
--- a/Include/intrcheck.h
+++ b/Include/intrcheck.h
@@ -8,6 +8,12 @@ extern "C" {
PyAPI_FUNC(int) PyOS_InterruptOccurred(void);
PyAPI_FUNC(void) PyOS_InitInterrupts(void);
PyAPI_FUNC(void) PyOS_AfterFork(void);
+PyAPI_FUNC(int) _PyOS_IsMainThread(void);
+
+#ifdef MS_WINDOWS
+/* windows.h is not included by Python.h so use void* instead of HANDLE */
+PyAPI_FUNC(void*) _PyOS_SigintEvent(void);
+#endif
#ifdef __cplusplus
}
diff --git a/Include/listobject.h b/Include/listobject.h
index 949b1a3..6fd374b 100644
--- a/Include/listobject.h
+++ b/Include/listobject.h
@@ -62,6 +62,8 @@ PyAPI_FUNC(int) PyList_Reverse(PyObject *);
PyAPI_FUNC(PyObject *) PyList_AsTuple(PyObject *);
#ifndef Py_LIMITED_API
PyAPI_FUNC(PyObject *) _PyList_Extend(PyListObject *, PyObject *);
+
+PyAPI_FUNC(int) PyList_ClearFreeList(void);
#endif
/* Macro, trading safety for speed */
diff --git a/Include/longobject.h b/Include/longobject.h
index c09565a..c58ddf4 100644
--- a/Include/longobject.h
+++ b/Include/longobject.h
@@ -12,7 +12,7 @@ typedef struct _longobject PyLongObject; /* Revealed in longintrepr.h */
PyAPI_DATA(PyTypeObject) PyLong_Type;
#define PyLong_Check(op) \
- PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_LONG_SUBCLASS)
+ PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_LONG_SUBCLASS)
#define PyLong_CheckExact(op) (Py_TYPE(op) == &PyLong_Type)
PyAPI_FUNC(PyObject *) PyLong_FromLong(long);
@@ -80,6 +80,7 @@ PyAPI_FUNC(PY_LONG_LONG) PyLong_AsLongLongAndOverflow(PyObject *, int *);
PyAPI_FUNC(PyObject *) PyLong_FromString(char *, char **, int);
#ifndef Py_LIMITED_API
PyAPI_FUNC(PyObject *) PyLong_FromUnicode(Py_UNICODE*, Py_ssize_t, int);
+PyAPI_FUNC(PyObject *) PyLong_FromUnicodeObject(PyObject *u, int base);
#endif
#ifndef Py_LIMITED_API
@@ -121,8 +122,8 @@ PyAPI_FUNC(PyObject *) _PyLong_DivmodNear(PyObject *, PyObject *);
enough memory to create the Python long.
*/
PyAPI_FUNC(PyObject *) _PyLong_FromByteArray(
- const unsigned char* bytes, size_t n,
- int little_endian, int is_signed);
+ const unsigned char* bytes, size_t n,
+ int little_endian, int is_signed);
/* _PyLong_AsByteArray: Convert the least-significant 8*n bits of long
v to a base-256 integer, stored in array bytes. Normally return 0,
@@ -144,8 +145,8 @@ PyAPI_FUNC(PyObject *) _PyLong_FromByteArray(
case, but bytes holds the least-signficant n bytes of the true value.
*/
PyAPI_FUNC(int) _PyLong_AsByteArray(PyLongObject* v,
- unsigned char* bytes, size_t n,
- int little_endian, int is_signed);
+ unsigned char* bytes, size_t n,
+ int little_endian, int is_signed);
/* _PyLong_Format: Convert the long to a string object with given base,
@@ -155,8 +156,9 @@ PyAPI_FUNC(PyObject *) _PyLong_Format(PyObject *aa, int base);
/* Format the object based on the format_spec, as defined in PEP 3101
(Advanced String Formatting). */
PyAPI_FUNC(PyObject *) _PyLong_FormatAdvanced(PyObject *obj,
- Py_UNICODE *format_spec,
- Py_ssize_t format_spec_len);
+ PyObject *format_spec,
+ Py_ssize_t start,
+ Py_ssize_t end);
#endif /* Py_LIMITED_API */
/* These aren't really part of the long object, but they're handy. The
diff --git a/Include/memoryobject.h b/Include/memoryobject.h
index 62ecbd6..aff5d99 100644
--- a/Include/memoryobject.h
+++ b/Include/memoryobject.h
@@ -69,6 +69,7 @@ PyAPI_FUNC(PyObject *) PyMemoryView_FromBuffer(Py_buffer *info);
typedef struct {
PyObject_HEAD
Py_buffer view;
+ Py_hash_t hash;
} PyMemoryViewObject;
#endif
diff --git a/Include/moduleobject.h b/Include/moduleobject.h
index 7b2bf1c..8013dd9 100644
--- a/Include/moduleobject.h
+++ b/Include/moduleobject.h
@@ -12,10 +12,14 @@ PyAPI_DATA(PyTypeObject) PyModule_Type;
#define PyModule_Check(op) PyObject_TypeCheck(op, &PyModule_Type)
#define PyModule_CheckExact(op) (Py_TYPE(op) == &PyModule_Type)
+PyAPI_FUNC(PyObject *) PyModule_NewObject(
+ PyObject *name
+ );
PyAPI_FUNC(PyObject *) PyModule_New(
const char *name /* UTF-8 encoded string */
);
PyAPI_FUNC(PyObject *) PyModule_GetDict(PyObject *);
+PyAPI_FUNC(PyObject *) PyModule_GetNameObject(PyObject *);
PyAPI_FUNC(const char *) PyModule_GetName(PyObject *);
PyAPI_FUNC(const char *) PyModule_GetFilename(PyObject *);
PyAPI_FUNC(PyObject *) PyModule_GetFilenameObject(PyObject *);
diff --git a/Include/object.h b/Include/object.h
index 2528841..1666271 100644
--- a/Include/object.h
+++ b/Include/object.h
@@ -418,7 +418,7 @@ typedef struct _heaptypeobject {
a given operator (e.g. __getitem__).
see add_operators() in typeobject.c . */
PyBufferProcs as_buffer;
- PyObject *ht_name, *ht_slots;
+ PyObject *ht_name, *ht_slots, *ht_qualname;
/* here are optional user slots, followed by the members. */
} PyHeapTypeObject;
@@ -455,6 +455,7 @@ PyAPI_FUNC(unsigned int) PyType_ClearCache(void);
PyAPI_FUNC(void) PyType_Modified(PyTypeObject *);
/* Generic operations on objects */
+struct _Py_Identifier;
#ifndef Py_LIMITED_API
PyAPI_FUNC(int) PyObject_Print(PyObject *, FILE *, int);
PyAPI_FUNC(void) _Py_BreakPoint(void);
@@ -472,6 +473,10 @@ PyAPI_FUNC(int) PyObject_HasAttrString(PyObject *, const char *);
PyAPI_FUNC(PyObject *) PyObject_GetAttr(PyObject *, PyObject *);
PyAPI_FUNC(int) PyObject_SetAttr(PyObject *, PyObject *, PyObject *);
PyAPI_FUNC(int) PyObject_HasAttr(PyObject *, PyObject *);
+PyAPI_FUNC(int) _PyObject_IsAbstract(PyObject *);
+PyAPI_FUNC(PyObject *) _PyObject_GetAttrId(PyObject *, struct _Py_Identifier *);
+PyAPI_FUNC(int) _PyObject_SetAttrId(PyObject *, struct _Py_Identifier *, PyObject *);
+PyAPI_FUNC(int) _PyObject_HasAttrId(PyObject *, struct _Py_Identifier *);
#ifndef Py_LIMITED_API
PyAPI_FUNC(PyObject **) _PyObject_GetDictPtr(PyObject *);
#endif
@@ -515,6 +520,7 @@ PyAPI_FUNC(void) Py_ReprLeave(PyObject *);
#ifndef Py_LIMITED_API
PyAPI_FUNC(Py_hash_t) _Py_HashDouble(double);
PyAPI_FUNC(Py_hash_t) _Py_HashPointer(void*);
+PyAPI_FUNC(Py_hash_t) _Py_HashBytes(unsigned char*, Py_ssize_t);
#endif
/* Helper for passing objects to printf and the like */
@@ -793,6 +799,10 @@ not implemented for a given type combination.
PyAPI_DATA(PyObject) _Py_NotImplementedStruct; /* Don't use this directly */
#define Py_NotImplemented (&_Py_NotImplementedStruct)
+/* Macro for returning Py_NotImplemented from a function */
+#define Py_RETURN_NOTIMPLEMENTED \
+ return Py_INCREF(Py_NotImplemented), Py_NotImplemented
+
/* Rich comparison opcodes */
#define Py_LT 0
#define Py_LE 1
diff --git a/Include/opcode.h b/Include/opcode.h
index 6b10944..ece713e 100644
--- a/Include/opcode.h
+++ b/Include/opcode.h
@@ -7,7 +7,6 @@ extern "C" {
/* Instruction opcodes for compiled code */
-#define STOP_CODE 0
#define POP_TOP 1
#define ROT_TWO 2
#define ROT_THREE 3
diff --git a/Include/parsetok.h b/Include/parsetok.h
index 4b7694f..911dfc1 100644
--- a/Include/parsetok.h
+++ b/Include/parsetok.h
@@ -9,7 +9,10 @@ extern "C" {
typedef struct {
int error;
- const char *filename; /* decoded from the filesystem encoding */
+#ifndef PGEN
+ /* The filename is useless for pgen, see comment in tok_state structure */
+ PyObject *filename;
+#endif
int lineno;
int offset;
char *text; /* UTF-8-encoded string */
@@ -66,8 +69,10 @@ PyAPI_FUNC(node *) PyParser_ParseStringFlagsFilenameEx(
perrdetail *err_ret,
int *flags);
-/* Note that he following function is defined in pythonrun.c not parsetok.c. */
+/* Note that the following functions are defined in pythonrun.c,
+ not in parsetok.c */
PyAPI_FUNC(void) PyParser_SetError(perrdetail *);
+PyAPI_FUNC(void) PyParser_ClearError(perrdetail *);
#ifdef __cplusplus
}
diff --git a/Include/patchlevel.h b/Include/patchlevel.h
index 1b8e6d4..4996e8e 100644
--- a/Include/patchlevel.h
+++ b/Include/patchlevel.h
@@ -17,19 +17,15 @@
/* Version parsed out into numeric values */
/*--start constants--*/
#define PY_MAJOR_VERSION 3
-#define PY_MINOR_VERSION 2
-#define PY_MICRO_VERSION 2
-#define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_FINAL
+#define PY_MINOR_VERSION 3
+#define PY_MICRO_VERSION 0
+#define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_ALPHA
#define PY_RELEASE_SERIAL 0
/* Version as a string */
-#define PY_VERSION "3.2.2+"
+#define PY_VERSION "3.3.0a0"
/*--end constants--*/
-/* Subversion Revision number of this file (not of the repository). Empty
- since Mercurial migration. */
-#define PY_PATCHLEVEL_REVISION ""
-
/* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2.
Use this for numeric comparisons, e.g. #if PY_VERSION_HEX >= ... */
#define PY_VERSION_HEX ((PY_MAJOR_VERSION << 24) | \
diff --git a/Include/py_curses.h b/Include/py_curses.h
index a891c42..f2c08f6 100644
--- a/Include/py_curses.h
+++ b/Include/py_curses.h
@@ -76,6 +76,7 @@ extern "C" {
typedef struct {
PyObject_HEAD
WINDOW *win;
+ char *encoding;
} PyCursesWindowObject;
#define PyCursesWindow_Check(v) (Py_TYPE(v) == &PyCursesWindow_Type)
diff --git a/Include/pydebug.h b/Include/pydebug.h
index 70c88f6..7173fe3 100644
--- a/Include/pydebug.h
+++ b/Include/pydebug.h
@@ -16,7 +16,6 @@ PyAPI_DATA(int) Py_BytesWarningFlag;
PyAPI_DATA(int) Py_UseClassExceptionsFlag;
PyAPI_DATA(int) Py_FrozenFlag;
PyAPI_DATA(int) Py_IgnoreEnvironmentFlag;
-PyAPI_DATA(int) Py_DivisionWarningFlag;
PyAPI_DATA(int) Py_DontWriteBytecodeFlag;
PyAPI_DATA(int) Py_NoUserSiteDirectory;
PyAPI_DATA(int) Py_UnbufferedStdioFlag;
@@ -26,8 +25,6 @@ PyAPI_DATA(int) Py_UnbufferedStdioFlag;
PYTHONPATH and PYTHONHOME from the environment */
#define Py_GETENV(s) (Py_IgnoreEnvironmentFlag ? NULL : getenv(s))
-PyAPI_FUNC(void) Py_FatalError(const char *message);
-
#ifdef __cplusplus
}
#endif
diff --git a/Include/pyerrors.h b/Include/pyerrors.h
index 4bb3c01..44eb3d9 100644
--- a/Include/pyerrors.h
+++ b/Include/pyerrors.h
@@ -45,18 +45,18 @@ typedef struct {
PyObject *myerrno;
PyObject *strerror;
PyObject *filename;
-} PyEnvironmentErrorObject;
-
#ifdef MS_WINDOWS
-typedef struct {
- PyException_HEAD
- PyObject *myerrno;
- PyObject *strerror;
- PyObject *filename;
PyObject *winerror;
-} PyWindowsErrorObject;
#endif
+ Py_ssize_t written; /* only for BlockingIOError, -1 otherwise */
+} PyOSErrorObject;
+
+/* Compatibility typedefs */
+typedef PyOSErrorObject PyEnvironmentErrorObject;
+#ifdef MS_WINDOWS
+typedef PyOSErrorObject PyWindowsErrorObject;
#endif
+#endif /* !Py_LIMITED_API */
/* Error handling definitions */
@@ -70,7 +70,17 @@ PyAPI_FUNC(PyObject *) PyErr_Occurred(void);
PyAPI_FUNC(void) PyErr_Clear(void);
PyAPI_FUNC(void) PyErr_Fetch(PyObject **, PyObject **, PyObject **);
PyAPI_FUNC(void) PyErr_Restore(PyObject *, PyObject *, PyObject *);
-PyAPI_FUNC(void) Py_FatalError(const char *message);
+
+#if defined(__clang__) || \
+ (defined(__GNUC__) && \
+ ((__GNUC_MAJOR__ >= 3) || \
+ (__GNUC_MAJOR__ == 2) && (__GNUC_MINOR__ >= 5)))
+#define _Py_NO_RETURN __attribute__((__noreturn__))
+#else
+#define _Py_NO_RETURN
+#endif
+
+PyAPI_FUNC(void) Py_FatalError(const char *message) _Py_NO_RETURN;
#if defined(Py_DEBUG) || defined(Py_LIMITED_API)
#define _PyErr_OCCURRED() PyErr_Occurred()
@@ -122,10 +132,9 @@ PyAPI_DATA(PyObject *) PyExc_LookupError;
PyAPI_DATA(PyObject *) PyExc_AssertionError;
PyAPI_DATA(PyObject *) PyExc_AttributeError;
+PyAPI_DATA(PyObject *) PyExc_BufferError;
PyAPI_DATA(PyObject *) PyExc_EOFError;
PyAPI_DATA(PyObject *) PyExc_FloatingPointError;
-PyAPI_DATA(PyObject *) PyExc_EnvironmentError;
-PyAPI_DATA(PyObject *) PyExc_IOError;
PyAPI_DATA(PyObject *) PyExc_OSError;
PyAPI_DATA(PyObject *) PyExc_ImportError;
PyAPI_DATA(PyObject *) PyExc_IndexError;
@@ -150,6 +159,27 @@ PyAPI_DATA(PyObject *) PyExc_UnicodeDecodeError;
PyAPI_DATA(PyObject *) PyExc_UnicodeTranslateError;
PyAPI_DATA(PyObject *) PyExc_ValueError;
PyAPI_DATA(PyObject *) PyExc_ZeroDivisionError;
+
+PyAPI_DATA(PyObject *) PyExc_BlockingIOError;
+PyAPI_DATA(PyObject *) PyExc_BrokenPipeError;
+PyAPI_DATA(PyObject *) PyExc_ChildProcessError;
+PyAPI_DATA(PyObject *) PyExc_ConnectionError;
+PyAPI_DATA(PyObject *) PyExc_ConnectionAbortedError;
+PyAPI_DATA(PyObject *) PyExc_ConnectionRefusedError;
+PyAPI_DATA(PyObject *) PyExc_ConnectionResetError;
+PyAPI_DATA(PyObject *) PyExc_FileExistsError;
+PyAPI_DATA(PyObject *) PyExc_FileNotFoundError;
+PyAPI_DATA(PyObject *) PyExc_InterruptedError;
+PyAPI_DATA(PyObject *) PyExc_IsADirectoryError;
+PyAPI_DATA(PyObject *) PyExc_NotADirectoryError;
+PyAPI_DATA(PyObject *) PyExc_PermissionError;
+PyAPI_DATA(PyObject *) PyExc_ProcessLookupError;
+PyAPI_DATA(PyObject *) PyExc_TimeoutError;
+
+
+/* Compatibility aliases */
+PyAPI_DATA(PyObject *) PyExc_EnvironmentError;
+PyAPI_DATA(PyObject *) PyExc_IOError;
#ifdef MS_WINDOWS
PyAPI_DATA(PyObject *) PyExc_WindowsError;
#endif
@@ -157,8 +187,6 @@ PyAPI_DATA(PyObject *) PyExc_WindowsError;
PyAPI_DATA(PyObject *) PyExc_VMSError;
#endif
-PyAPI_DATA(PyObject *) PyExc_BufferError;
-
PyAPI_DATA(PyObject *) PyExc_RecursionErrorInst;
/* Predefined warning categories */
@@ -198,8 +226,6 @@ PyAPI_FUNC(PyObject *) PyErr_Format(
);
#ifdef MS_WINDOWS
-PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErrWithFilenameObject(
- int, const char *);
PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErrWithFilename(
int ierr,
const char *filename /* decoded from the filesystem encoding */
@@ -293,6 +319,12 @@ PyAPI_FUNC(PyObject *) PyUnicodeTranslateError_Create(
Py_ssize_t end,
const char *reason /* UTF-8 encoded string */
);
+PyAPI_FUNC(PyObject *) _PyUnicodeTranslateError_Create(
+ PyObject *object,
+ Py_ssize_t start,
+ Py_ssize_t end,
+ const char *reason /* UTF-8 encoded string */
+ );
#endif
/* get the encoding attribute */
diff --git a/Include/pymacro.h b/Include/pymacro.h
new file mode 100644
index 0000000..1dc0c61
--- /dev/null
+++ b/Include/pymacro.h
@@ -0,0 +1,55 @@
+#ifndef Py_PYMACRO_H
+#define Py_PYMACRO_H
+
+#define Py_MIN(x, y) (((x) > (y)) ? (y) : (x))
+#define Py_MAX(x, y) (((x) > (y)) ? (x) : (y))
+
+/* Argument must be a char or an int in [-128, 127] or [0, 255]. */
+#define Py_CHARMASK(c) ((unsigned char)((c) & 0xff))
+
+
+/* Assert a build-time dependency, as an expression.
+
+ Your compile will fail if the condition isn't true, or can't be evaluated
+ by the compiler. This can be used in an expression: its value is 0.
+
+ Example:
+
+ #define foo_to_char(foo) \
+ ((char *)(foo) \
+ + Py_BUILD_ASSERT_EXPR(offsetof(struct foo, string) == 0))
+
+ Written by Rusty Russell, public domain, http://ccodearchive.net/ */
+#define Py_BUILD_ASSERT_EXPR(cond) \
+ (sizeof(char [1 - 2*!(cond)]) - 1)
+
+/* Get the number of elements in a visible array
+
+ This does not work on pointers, or arrays declared as [], or function
+ parameters. With correct compiler support, such usage will cause a build
+ error (see Py_BUILD_ASSERT_EXPR).
+
+ Written by Rusty Russell, public domain, http://ccodearchive.net/ */
+#if defined(__GNUC__)
+/* Two gcc extensions.
+ &a[0] degrades to a pointer: a different type from an array */
+#define Py_ARRAY_LENGTH(array) \
+ (sizeof(array) / sizeof((array)[0]) \
+ + Py_BUILD_ASSERT_EXPR(!__builtin_types_compatible_p(typeof(array), \
+ typeof(&(array)[0]))))
+#else
+#define Py_ARRAY_LENGTH(array) \
+ (sizeof(array) / sizeof((array)[0]))
+#endif
+
+
+/* Define macros for inline documentation. */
+#define PyDoc_VAR(name) static char name[]
+#define PyDoc_STRVAR(name,str) PyDoc_VAR(name) = PyDoc_STR(str)
+#ifdef WITH_DOC_STRINGS
+#define PyDoc_STR(str) str
+#else
+#define PyDoc_STR(str) ""
+#endif
+
+#endif /* Py_PYMACRO_H */
diff --git a/Include/pymath.h b/Include/pymath.h
index b4eda66..62a6c42 100644
--- a/Include/pymath.h
+++ b/Include/pymath.h
@@ -37,12 +37,6 @@ extern double pow(double, double);
#endif /* __STDC__ */
#endif /* _MSC_VER */
-#ifdef _OSF_SOURCE
-/* OSF1 5.1 doesn't make these available with XOPEN_SOURCE_EXTENDED defined */
-extern int finite(double);
-extern double copysign(double, double);
-#endif
-
/* High precision defintion of pi and e (Euler)
* The values are taken from libc6's math.h.
*/
diff --git a/Include/pyport.h b/Include/pyport.h
index 62aa53a..5309de6 100644
--- a/Include/pyport.h
+++ b/Include/pyport.h
@@ -132,7 +132,7 @@ Used in: PY_LONG_LONG
#endif
/* Parameters used for the numeric hash implementation. See notes for
- _PyHash_Double in Objects/object.c. Numeric hashes are based on
+ _Py_HashDouble in Objects/object.c. Numeric hashes are based on
reduction modulo the prime 2**_PyHASH_BITS - 1. */
#if SIZEOF_VOID_P >= 8
diff --git a/Include/pystate.h b/Include/pystate.h
index b5fe1ad..1bbb4e2 100644
--- a/Include/pystate.h
+++ b/Include/pystate.h
@@ -74,9 +74,9 @@ typedef struct _ts {
struct _frame *frame;
int recursion_depth;
char overflowed; /* The stack has overflowed. Allow 50 more calls
- to handle the runtime error. */
- char recursion_critical; /* The current calls must not cause
- a stack overflow. */
+ to handle the runtime error. */
+ char recursion_critical; /* The current calls must not cause
+ a stack overflow. */
/* 'tracing' keeps track of the execution depth when tracing/profiling.
This is to prevent the actual trace/profile code from being recorded in
the trace/profile. */
@@ -160,6 +160,8 @@ typedef
enum {PyGILState_LOCKED, PyGILState_UNLOCKED}
PyGILState_STATE;
+#ifdef WITH_THREAD
+
/* Ensure that the current thread is ready to call the Python
C API, regardless of the current state of Python, or of its
thread lock. This may be called as many times as desired
@@ -201,6 +203,8 @@ PyAPI_FUNC(void) PyGILState_Release(PyGILState_STATE);
*/
PyAPI_FUNC(PyThreadState *) PyGILState_GetThisThreadState(void);
+#endif /* #ifdef WITH_THREAD */
+
/* The implementation of sys._current_frames() Returns a dict mapping
thread id to that thread's current frame.
*/
diff --git a/Include/pythonrun.h b/Include/pythonrun.h
index 00b4972..fc6c854 100644
--- a/Include/pythonrun.h
+++ b/Include/pythonrun.h
@@ -179,9 +179,6 @@ PyAPI_FUNC(const char *) Py_GetCopyright(void);
PyAPI_FUNC(const char *) Py_GetCompiler(void);
PyAPI_FUNC(const char *) Py_GetBuildInfo(void);
#ifndef Py_LIMITED_API
-PyAPI_FUNC(const char *) _Py_svnversion(void);
-PyAPI_FUNC(const char *) Py_SubversionRevision(void);
-PyAPI_FUNC(const char *) Py_SubversionShortBranch(void);
PyAPI_FUNC(const char *) _Py_hgidentifier(void);
PyAPI_FUNC(const char *) _Py_hgversion(void);
#endif
@@ -214,6 +211,7 @@ PyAPI_FUNC(void) PyByteArray_Fini(void);
PyAPI_FUNC(void) PyFloat_Fini(void);
PyAPI_FUNC(void) PyOS_FiniInterrupts(void);
PyAPI_FUNC(void) _PyGC_Fini(void);
+PyAPI_FUNC(void) PySlice_Fini(void);
PyAPI_DATA(PyThreadState *) _Py_Finalizing;
#endif
diff --git a/Include/pythread.h b/Include/pythread.h
index 9806c61..6e9f303 100644
--- a/Include/pythread.h
+++ b/Include/pythread.h
@@ -32,7 +32,7 @@ PyAPI_FUNC(int) PyThread_acquire_lock(PyThread_type_lock, int);
on a lock (see PyThread_acquire_lock_timed() below).
PY_TIMEOUT_MAX is the highest usable value (in microseconds) of that
type, and depends on the system threading API.
-
+
NOTE: this isn't the same value as `_thread.TIMEOUT_MAX`. The _thread
module exposes a higher-level API, with timeouts expressed in seconds
and floating-point numbers allowed.
@@ -74,6 +74,8 @@ PyAPI_FUNC(void) PyThread_release_lock(PyThread_type_lock);
PyAPI_FUNC(size_t) PyThread_get_stacksize(void);
PyAPI_FUNC(int) PyThread_set_stacksize(size_t);
+PyAPI_FUNC(PyObject*) PyThread_GetInfo(void);
+
/* Thread Local Storage (TLS) API */
PyAPI_FUNC(int) PyThread_create_key(void);
PyAPI_FUNC(void) PyThread_delete_key(int);
diff --git a/Include/setobject.h b/Include/setobject.h
index 6234111..00e5344 100644
--- a/Include/setobject.h
+++ b/Include/setobject.h
@@ -99,6 +99,8 @@ PyAPI_FUNC(int) _PySet_NextEntry(PyObject *set, Py_ssize_t *pos, PyObject **key,
PyAPI_FUNC(PyObject *) PySet_Pop(PyObject *set);
#ifndef Py_LIMITED_API
PyAPI_FUNC(int) _PySet_Update(PyObject *set, PyObject *iterable);
+
+PyAPI_FUNC(int) PySet_ClearFreeList(void);
#endif
#ifdef __cplusplus
diff --git a/Include/symtable.h b/Include/symtable.h
index fd7de04..82f6269 100644
--- a/Include/symtable.h
+++ b/Include/symtable.h
@@ -23,10 +23,13 @@ struct symtable {
PyObject *st_blocks; /* dict: map AST node addresses
* to symbol table entries */
PyObject *st_stack; /* list: stack of namespace info */
- PyObject *st_global; /* borrowed ref to st_top->st_symbols */
- int st_nblocks; /* number of blocks used */
+ PyObject *st_global; /* borrowed ref to st_top->ste_symbols */
+ int st_nblocks; /* number of blocks used. kept for
+ consistency with the corresponding
+ compiler structure */
PyObject *st_private; /* name of current class or NULL */
- PyFutureFeatures *st_future; /* module's future features */
+ PyFutureFeatures *st_future; /* module's future features that affect
+ the symbol table */
};
typedef struct _symtable_entry {
@@ -34,7 +37,7 @@ typedef struct _symtable_entry {
PyObject *ste_id; /* int: key in ste_table->st_blocks */
PyObject *ste_symbols; /* dict: variable names to flags */
PyObject *ste_name; /* string: name of current block */
- PyObject *ste_varnames; /* list of variable names */
+ PyObject *ste_varnames; /* list of function parameters */
PyObject *ste_children; /* list of child blocks */
_Py_block_ty ste_type; /* module, class, or function */
int ste_unoptimized; /* false if namespace is optimized */
diff --git a/Include/traceback.h b/Include/traceback.h
index 69e3d05..7734707 100644
--- a/Include/traceback.h
+++ b/Include/traceback.h
@@ -5,6 +5,8 @@
extern "C" {
#endif
+#include "pystate.h"
+
struct _frame;
/* Traceback interface */
@@ -28,6 +30,42 @@ PyAPI_FUNC(int) _Py_DisplaySourceLine(PyObject *, PyObject *, int, int);
PyAPI_DATA(PyTypeObject) PyTraceBack_Type;
#define PyTraceBack_Check(v) (Py_TYPE(v) == &PyTraceBack_Type)
+/* Write the Python traceback into the file 'fd'. For example:
+
+ Traceback (most recent call first):
+ File "xxx", line xxx in <xxx>
+ File "xxx", line xxx in <xxx>
+ ...
+ File "xxx", line xxx in <xxx>
+
+ This function is written for debug purpose only, to dump the traceback in
+ the worst case: after a segmentation fault, at fatal error, etc. That's why,
+ it is very limited. Strings are truncated to 100 characters and encoded to
+ ASCII with backslashreplace. It doesn't write the source code, only the
+ function name, filename and line number of each frame. Write only the first
+ 100 frames: if the traceback is truncated, write the line " ...".
+
+ This function is signal safe. */
+
+PyAPI_DATA(void) _Py_DumpTraceback(
+ int fd,
+ PyThreadState *tstate);
+
+/* Write the traceback of all threads into the file 'fd'. current_thread can be
+ NULL. Return NULL on success, or an error message on error.
+
+ This function is written for debug purpose only. It calls
+ _Py_DumpTraceback() for each thread, and so has the same limitations. It
+ only write the traceback of the first 100 threads: write "..." if there are
+ more threads.
+
+ This function is signal safe. */
+
+PyAPI_DATA(const char*) _Py_DumpTracebackThreads(
+ int fd, PyInterpreterState *interp,
+ PyThreadState *current_thread);
+
+
#ifdef __cplusplus
}
#endif
diff --git a/Include/ucnhash.h b/Include/ucnhash.h
index 70fdf13..8de9ba0 100644
--- a/Include/ucnhash.h
+++ b/Include/ucnhash.h
@@ -19,11 +19,13 @@ typedef struct {
success, zero if not. Does not set Python exceptions.
If self is NULL, data come from the default version of the database.
If it is not NULL, it should be a unicodedata.ucd_X_Y_Z object */
- int (*getname)(PyObject *self, Py_UCS4 code, char* buffer, int buflen);
+ int (*getname)(PyObject *self, Py_UCS4 code, char* buffer, int buflen,
+ int with_alias_and_seq);
/* Get character code for a given name. Same error handling
as for getname. */
- int (*getcode)(PyObject *self, const char* name, int namelen, Py_UCS4* code);
+ int (*getcode)(PyObject *self, const char* name, int namelen, Py_UCS4* code,
+ int with_named_seq);
} _PyUnicode_Name_CAPI;
diff --git a/Include/unicodeobject.h b/Include/unicodeobject.h
index 477f526..60bfcbe 100644
--- a/Include/unicodeobject.h
+++ b/Include/unicodeobject.h
@@ -64,16 +64,15 @@ Copyright (c) Corporation for National Research Initiatives.
/* Python 3.x requires unicode */
#define Py_USING_UNICODE
-/* FIXME: MvL's new implementation assumes that Py_UNICODE_SIZE is
- properly set, but the default rules below doesn't set it. I'll
- sort this out some other day -- fredrik@pythonware.com */
-
-#ifndef Py_UNICODE_SIZE
-#error Must define Py_UNICODE_SIZE
+#ifndef SIZEOF_WCHAR_T
+#error Must define SIZEOF_WCHAR_T
#endif
-/* Setting Py_UNICODE_WIDE enables UCS-4 storage. Otherwise, Unicode
- strings are stored as UCS-2 (with limited support for UTF-16) */
+#define Py_UNICODE_SIZE SIZEOF_WCHAR_T
+
+/* If wchar_t can be used for UCS-4 storage, set Py_UNICODE_WIDE.
+ Otherwise, Unicode strings are stored as UCS-2 (with limited support
+ for UTF-16) */
#if Py_UNICODE_SIZE >= 4
#define Py_UNICODE_WIDE
@@ -84,19 +83,14 @@ Copyright (c) Corporation for National Research Initiatives.
/* #define HAVE_WCHAR_H */
/* #define HAVE_USABLE_WCHAR_T */
-/* Defaults for various platforms */
-#ifndef PY_UNICODE_TYPE
-
-/* Windows has a usable wchar_t type (unless we're using UCS-4) */
-# if defined(MS_WIN32) && Py_UNICODE_SIZE == 2
-# define HAVE_USABLE_WCHAR_T
-# define PY_UNICODE_TYPE wchar_t
-# endif
-
-# if defined(Py_UNICODE_WIDE)
-# define PY_UNICODE_TYPE Py_UCS4
-# endif
+/* Py_UNICODE was the native Unicode storage format (code unit) used by
+ Python and represents a single Unicode element in the Unicode type.
+ With PEP 393, Py_UNICODE is deprecated and replaced with a
+ typedef to wchar_t. */
+#ifndef Py_LIMITED_API
+#define PY_UNICODE_TYPE wchar_t
+typedef wchar_t Py_UNICODE;
#endif
/* If the compiler provides a wchar_t type we try to support it
@@ -109,6 +103,10 @@ Copyright (c) Corporation for National Research Initiatives.
# endif
#endif
+#if defined(MS_WINDOWS)
+# define HAVE_MBCS
+#endif
+
#ifdef HAVE_WCHAR_H
/* Work around a cosmetic bug in BSDI 4.x wchar.h; thanks to Thomas Wouters */
# ifdef _HAVE_BSDI
@@ -117,201 +115,24 @@ Copyright (c) Corporation for National Research Initiatives.
# include <wchar.h>
#endif
-/*
- * Use this typedef when you need to represent a UTF-16 surrogate pair
- * as single unsigned integer.
- */
-#if SIZEOF_INT >= 4
+/* Py_UCS4 and Py_UCS2 are typedefs for the respective
+ unicode representations. */
+#if SIZEOF_INT == 4
typedef unsigned int Py_UCS4;
-#elif SIZEOF_LONG >= 4
+#elif SIZEOF_LONG == 4
typedef unsigned long Py_UCS4;
+#else
+#error "Could not find a proper typedef for Py_UCS4"
#endif
-/* Py_UNICODE is the native Unicode storage format (code unit) used by
- Python and represents a single Unicode element in the Unicode
- type. */
-
-#ifndef Py_LIMITED_API
-typedef PY_UNICODE_TYPE Py_UNICODE;
-#endif
-
-/* --- UCS-2/UCS-4 Name Mangling ------------------------------------------ */
-
-/* Unicode API names are mangled to assure that UCS-2 and UCS-4 builds
- produce different external names and thus cause import errors in
- case Python interpreters and extensions with mixed compiled in
- Unicode width assumptions are combined. */
-
-#ifndef Py_UNICODE_WIDE
-
-# define PyUnicode_AsASCIIString PyUnicodeUCS2_AsASCIIString
-# define PyUnicode_AsCharmapString PyUnicodeUCS2_AsCharmapString
-# define PyUnicode_AsDecodedObject PyUnicodeUCS2_AsDecodedObject
-# define PyUnicode_AsDecodedUnicode PyUnicodeUCS2_AsDecodedUnicode
-# define PyUnicode_AsEncodedObject PyUnicodeUCS2_AsEncodedObject
-# define PyUnicode_AsEncodedString PyUnicodeUCS2_AsEncodedString
-# define PyUnicode_AsEncodedUnicode PyUnicodeUCS2_AsEncodedUnicode
-# define PyUnicode_AsLatin1String PyUnicodeUCS2_AsLatin1String
-# define PyUnicode_AsRawUnicodeEscapeString PyUnicodeUCS2_AsRawUnicodeEscapeString
-# define PyUnicode_AsUTF32String PyUnicodeUCS2_AsUTF32String
-# define PyUnicode_AsUTF16String PyUnicodeUCS2_AsUTF16String
-# define PyUnicode_AsUTF8String PyUnicodeUCS2_AsUTF8String
-# define PyUnicode_AsUnicode PyUnicodeUCS2_AsUnicode
-# define PyUnicode_AsUnicodeEscapeString PyUnicodeUCS2_AsUnicodeEscapeString
-# define PyUnicode_AsWideChar PyUnicodeUCS2_AsWideChar
-# define PyUnicode_AsWideCharString PyUnicodeUCS2_AsWideCharString
-# define PyUnicode_ClearFreeList PyUnicodeUCS2_ClearFreelist
-# define PyUnicode_Compare PyUnicodeUCS2_Compare
-# define PyUnicode_CompareWithASCIIString PyUnicodeUCS2_CompareWithASCIIString
-# define PyUnicode_Concat PyUnicodeUCS2_Concat
-# define PyUnicode_Append PyUnicodeUCS2_Append
-# define PyUnicode_AppendAndDel PyUnicodeUCS2_AppendAndDel
-# define PyUnicode_Contains PyUnicodeUCS2_Contains
-# define PyUnicode_Count PyUnicodeUCS2_Count
-# define PyUnicode_Decode PyUnicodeUCS2_Decode
-# define PyUnicode_DecodeASCII PyUnicodeUCS2_DecodeASCII
-# define PyUnicode_DecodeCharmap PyUnicodeUCS2_DecodeCharmap
-# define PyUnicode_DecodeLatin1 PyUnicodeUCS2_DecodeLatin1
-# define PyUnicode_DecodeFSDefault PyUnicodeUCS2_DecodeFSDefault
-# define PyUnicode_DecodeFSDefaultAndSize PyUnicodeUCS2_DecodeFSDefaultAndSize
-# define PyUnicode_DecodeRawUnicodeEscape PyUnicodeUCS2_DecodeRawUnicodeEscape
-# define PyUnicode_DecodeUTF32 PyUnicodeUCS2_DecodeUTF32
-# define PyUnicode_DecodeUTF32Stateful PyUnicodeUCS2_DecodeUTF32Stateful
-# define PyUnicode_DecodeUTF16 PyUnicodeUCS2_DecodeUTF16
-# define PyUnicode_DecodeUTF16Stateful PyUnicodeUCS2_DecodeUTF16Stateful
-# define PyUnicode_DecodeUTF8 PyUnicodeUCS2_DecodeUTF8
-# define PyUnicode_DecodeUTF8Stateful PyUnicodeUCS2_DecodeUTF8Stateful
-# define PyUnicode_DecodeUnicodeEscape PyUnicodeUCS2_DecodeUnicodeEscape
-# define PyUnicode_Encode PyUnicodeUCS2_Encode
-# define PyUnicode_EncodeASCII PyUnicodeUCS2_EncodeASCII
-# define PyUnicode_EncodeCharmap PyUnicodeUCS2_EncodeCharmap
-# define PyUnicode_EncodeDecimal PyUnicodeUCS2_EncodeDecimal
-# define PyUnicode_EncodeLatin1 PyUnicodeUCS2_EncodeLatin1
-# define PyUnicode_EncodeRawUnicodeEscape PyUnicodeUCS2_EncodeRawUnicodeEscape
-# define PyUnicode_EncodeUTF32 PyUnicodeUCS2_EncodeUTF32
-# define PyUnicode_EncodeUTF16 PyUnicodeUCS2_EncodeUTF16
-# define PyUnicode_EncodeUTF8 PyUnicodeUCS2_EncodeUTF8
-# define PyUnicode_EncodeUnicodeEscape PyUnicodeUCS2_EncodeUnicodeEscape
-# define PyUnicode_Find PyUnicodeUCS2_Find
-# define PyUnicode_Format PyUnicodeUCS2_Format
-# define PyUnicode_FromEncodedObject PyUnicodeUCS2_FromEncodedObject
-# define PyUnicode_FromFormat PyUnicodeUCS2_FromFormat
-# define PyUnicode_FromFormatV PyUnicodeUCS2_FromFormatV
-# define PyUnicode_FromObject PyUnicodeUCS2_FromObject
-# define PyUnicode_FromOrdinal PyUnicodeUCS2_FromOrdinal
-# define PyUnicode_FromString PyUnicodeUCS2_FromString
-# define PyUnicode_FromStringAndSize PyUnicodeUCS2_FromStringAndSize
-# define PyUnicode_FromUnicode PyUnicodeUCS2_FromUnicode
-# define PyUnicode_FromWideChar PyUnicodeUCS2_FromWideChar
-# define PyUnicode_FSConverter PyUnicodeUCS2_FSConverter
-# define PyUnicode_FSDecoder PyUnicodeUCS2_FSDecoder
-# define PyUnicode_GetDefaultEncoding PyUnicodeUCS2_GetDefaultEncoding
-# define PyUnicode_GetMax PyUnicodeUCS2_GetMax
-# define PyUnicode_GetSize PyUnicodeUCS2_GetSize
-# define PyUnicode_IsIdentifier PyUnicodeUCS2_IsIdentifier
-# define PyUnicode_Join PyUnicodeUCS2_Join
-# define PyUnicode_Partition PyUnicodeUCS2_Partition
-# define PyUnicode_RPartition PyUnicodeUCS2_RPartition
-# define PyUnicode_RSplit PyUnicodeUCS2_RSplit
-# define PyUnicode_Replace PyUnicodeUCS2_Replace
-# define PyUnicode_Resize PyUnicodeUCS2_Resize
-# define PyUnicode_RichCompare PyUnicodeUCS2_RichCompare
-# define PyUnicode_Split PyUnicodeUCS2_Split
-# define PyUnicode_Splitlines PyUnicodeUCS2_Splitlines
-# define PyUnicode_Tailmatch PyUnicodeUCS2_Tailmatch
-# define PyUnicode_Translate PyUnicodeUCS2_Translate
-# define PyUnicode_TranslateCharmap PyUnicodeUCS2_TranslateCharmap
-# define _PyUnicode_AsDefaultEncodedString _PyUnicodeUCS2_AsDefaultEncodedString
-# define _PyUnicode_Fini _PyUnicodeUCS2_Fini
-# define _PyUnicode_Init _PyUnicodeUCS2_Init
-# define PyUnicode_strdup PyUnicodeUCS2_strdup
-
+#if SIZEOF_SHORT == 2
+typedef unsigned short Py_UCS2;
#else
-
-# define PyUnicode_AsASCIIString PyUnicodeUCS4_AsASCIIString
-# define PyUnicode_AsCharmapString PyUnicodeUCS4_AsCharmapString
-# define PyUnicode_AsDecodedObject PyUnicodeUCS4_AsDecodedObject
-# define PyUnicode_AsDecodedUnicode PyUnicodeUCS4_AsDecodedUnicode
-# define PyUnicode_AsEncodedObject PyUnicodeUCS4_AsEncodedObject
-# define PyUnicode_AsEncodedString PyUnicodeUCS4_AsEncodedString
-# define PyUnicode_AsEncodedUnicode PyUnicodeUCS4_AsEncodedUnicode
-# define PyUnicode_AsLatin1String PyUnicodeUCS4_AsLatin1String
-# define PyUnicode_AsRawUnicodeEscapeString PyUnicodeUCS4_AsRawUnicodeEscapeString
-# define PyUnicode_AsUTF32String PyUnicodeUCS4_AsUTF32String
-# define PyUnicode_AsUTF16String PyUnicodeUCS4_AsUTF16String
-# define PyUnicode_AsUTF8String PyUnicodeUCS4_AsUTF8String
-# define PyUnicode_AsUnicode PyUnicodeUCS4_AsUnicode
-# define PyUnicode_AsUnicodeEscapeString PyUnicodeUCS4_AsUnicodeEscapeString
-# define PyUnicode_AsWideChar PyUnicodeUCS4_AsWideChar
-# define PyUnicode_AsWideCharString PyUnicodeUCS4_AsWideCharString
-# define PyUnicode_ClearFreeList PyUnicodeUCS4_ClearFreelist
-# define PyUnicode_Compare PyUnicodeUCS4_Compare
-# define PyUnicode_CompareWithASCIIString PyUnicodeUCS4_CompareWithASCIIString
-# define PyUnicode_Concat PyUnicodeUCS4_Concat
-# define PyUnicode_Append PyUnicodeUCS4_Append
-# define PyUnicode_AppendAndDel PyUnicodeUCS4_AppendAndDel
-# define PyUnicode_Contains PyUnicodeUCS4_Contains
-# define PyUnicode_Count PyUnicodeUCS4_Count
-# define PyUnicode_Decode PyUnicodeUCS4_Decode
-# define PyUnicode_DecodeASCII PyUnicodeUCS4_DecodeASCII
-# define PyUnicode_DecodeCharmap PyUnicodeUCS4_DecodeCharmap
-# define PyUnicode_DecodeLatin1 PyUnicodeUCS4_DecodeLatin1
-# define PyUnicode_DecodeFSDefault PyUnicodeUCS4_DecodeFSDefault
-# define PyUnicode_DecodeFSDefaultAndSize PyUnicodeUCS4_DecodeFSDefaultAndSize
-# define PyUnicode_DecodeRawUnicodeEscape PyUnicodeUCS4_DecodeRawUnicodeEscape
-# define PyUnicode_DecodeUTF32 PyUnicodeUCS4_DecodeUTF32
-# define PyUnicode_DecodeUTF32Stateful PyUnicodeUCS4_DecodeUTF32Stateful
-# define PyUnicode_DecodeUTF16 PyUnicodeUCS4_DecodeUTF16
-# define PyUnicode_DecodeUTF16Stateful PyUnicodeUCS4_DecodeUTF16Stateful
-# define PyUnicode_DecodeUTF8 PyUnicodeUCS4_DecodeUTF8
-# define PyUnicode_DecodeUTF8Stateful PyUnicodeUCS4_DecodeUTF8Stateful
-# define PyUnicode_DecodeUnicodeEscape PyUnicodeUCS4_DecodeUnicodeEscape
-# define PyUnicode_Encode PyUnicodeUCS4_Encode
-# define PyUnicode_EncodeASCII PyUnicodeUCS4_EncodeASCII
-# define PyUnicode_EncodeCharmap PyUnicodeUCS4_EncodeCharmap
-# define PyUnicode_EncodeDecimal PyUnicodeUCS4_EncodeDecimal
-# define PyUnicode_EncodeLatin1 PyUnicodeUCS4_EncodeLatin1
-# define PyUnicode_EncodeRawUnicodeEscape PyUnicodeUCS4_EncodeRawUnicodeEscape
-# define PyUnicode_EncodeUTF32 PyUnicodeUCS4_EncodeUTF32
-# define PyUnicode_EncodeUTF16 PyUnicodeUCS4_EncodeUTF16
-# define PyUnicode_EncodeUTF8 PyUnicodeUCS4_EncodeUTF8
-# define PyUnicode_EncodeUnicodeEscape PyUnicodeUCS4_EncodeUnicodeEscape
-# define PyUnicode_Find PyUnicodeUCS4_Find
-# define PyUnicode_Format PyUnicodeUCS4_Format
-# define PyUnicode_FromEncodedObject PyUnicodeUCS4_FromEncodedObject
-# define PyUnicode_FromFormat PyUnicodeUCS4_FromFormat
-# define PyUnicode_FromFormatV PyUnicodeUCS4_FromFormatV
-# define PyUnicode_FromObject PyUnicodeUCS4_FromObject
-# define PyUnicode_FromOrdinal PyUnicodeUCS4_FromOrdinal
-# define PyUnicode_FromString PyUnicodeUCS4_FromString
-# define PyUnicode_FromStringAndSize PyUnicodeUCS4_FromStringAndSize
-# define PyUnicode_FromUnicode PyUnicodeUCS4_FromUnicode
-# define PyUnicode_FromWideChar PyUnicodeUCS4_FromWideChar
-# define PyUnicode_FSConverter PyUnicodeUCS4_FSConverter
-# define PyUnicode_FSDecoder PyUnicodeUCS4_FSDecoder
-# define PyUnicode_GetDefaultEncoding PyUnicodeUCS4_GetDefaultEncoding
-# define PyUnicode_GetMax PyUnicodeUCS4_GetMax
-# define PyUnicode_GetSize PyUnicodeUCS4_GetSize
-# define PyUnicode_IsIdentifier PyUnicodeUCS4_IsIdentifier
-# define PyUnicode_Join PyUnicodeUCS4_Join
-# define PyUnicode_Partition PyUnicodeUCS4_Partition
-# define PyUnicode_RPartition PyUnicodeUCS4_RPartition
-# define PyUnicode_RSplit PyUnicodeUCS4_RSplit
-# define PyUnicode_Replace PyUnicodeUCS4_Replace
-# define PyUnicode_Resize PyUnicodeUCS4_Resize
-# define PyUnicode_RichCompare PyUnicodeUCS4_RichCompare
-# define PyUnicode_Split PyUnicodeUCS4_Split
-# define PyUnicode_Splitlines PyUnicodeUCS4_Splitlines
-# define PyUnicode_Tailmatch PyUnicodeUCS4_Tailmatch
-# define PyUnicode_Translate PyUnicodeUCS4_Translate
-# define PyUnicode_TranslateCharmap PyUnicodeUCS4_TranslateCharmap
-# define _PyUnicode_AsDefaultEncodedString _PyUnicodeUCS4_AsDefaultEncodedString
-# define _PyUnicode_Fini _PyUnicodeUCS4_Fini
-# define _PyUnicode_Init _PyUnicodeUCS4_Init
-# define PyUnicode_strdup PyUnicodeUCS4_strdup
-
+#error "Could not find a proper typedef for Py_UCS2"
#endif
+typedef unsigned char Py_UCS1;
+
/* --- Internal Unicode Operations ---------------------------------------- */
/* Since splitting on whitespace is an important use case, and
@@ -350,7 +171,7 @@ typedef PY_UNICODE_TYPE Py_UNICODE;
Py_UNICODE_ISDIGIT(ch) || \
Py_UNICODE_ISNUMERIC(ch))
-#define Py_UNICODE_COPY(target, source, length) \
+#define Py_UNICODE_COPY(target, source, length) \
Py_MEMCPY((target), (source), (length)*sizeof(Py_UNICODE))
#define Py_UNICODE_FILL(target, value, length) \
@@ -358,13 +179,27 @@ typedef PY_UNICODE_TYPE Py_UNICODE;
for (i_ = 0; i_ < (length); i_++) t_[i_] = v_;\
} while (0)
+/* macros to work with surrogates */
+#define Py_UNICODE_IS_SURROGATE(ch) (0xD800 <= ch && ch <= 0xDFFF)
+#define Py_UNICODE_IS_HIGH_SURROGATE(ch) (0xD800 <= ch && ch <= 0xDBFF)
+#define Py_UNICODE_IS_LOW_SURROGATE(ch) (0xDC00 <= ch && ch <= 0xDFFF)
+/* Join two surrogate characters and return a single Py_UCS4 value. */
+#define Py_UNICODE_JOIN_SURROGATES(high, low) \
+ (((((Py_UCS4)(high) & 0x03FF) << 10) | \
+ ((Py_UCS4)(low) & 0x03FF)) + 0x10000)
+/* high surrogate = top 10 bits added to D800 */
+#define Py_UNICODE_HIGH_SURROGATE(ch) (0xD800 | (((ch) - 0x10000) >> 10))
+/* low surrogate = bottom 10 bits added to DC00 */
+#define Py_UNICODE_LOW_SURROGATE(ch) (0xDC00 | (((ch) - 0x10000) & 0x3FF))
+
/* Check if substring matches at given offset. The offset must be
valid, and the substring must not be empty. */
#define Py_UNICODE_MATCH(string, offset, substring) \
- ((*((string)->str + (offset)) == *((substring)->str)) && \
- ((*((string)->str + (offset) + (substring)->length-1) == *((substring)->str + (substring)->length-1))) && \
- !memcmp((string)->str + (offset), (substring)->str, (substring)->length*sizeof(Py_UNICODE)))
+ ((*((string)->wstr + (offset)) == *((substring)->wstr)) && \
+ ((*((string)->wstr + (offset) + (substring)->wstr_length-1) == *((substring)->wstr + (substring)->wstr_length-1))) && \
+ !memcmp((string)->wstr + (offset), (substring)->wstr, (substring)->wstr_length*sizeof(Py_UNICODE)))
+
#endif /* Py_LIMITED_API */
#ifdef __cplusplus
@@ -374,41 +209,375 @@ extern "C" {
/* --- Unicode Type ------------------------------------------------------- */
#ifndef Py_LIMITED_API
+
+/* ASCII-only strings created through PyUnicode_New use the PyASCIIObject
+ structure. state.ascii and state.compact are set, and the data
+ immediately follow the structure. utf8_length and wstr_length can be found
+ in the length field; the utf8 pointer is equal to the data pointer. */
typedef struct {
+ /* There are 4 forms of Unicode strings:
+
+ - compact ascii:
+
+ * structure = PyASCIIObject
+ * test: PyUnicode_IS_COMPACT_ASCII(op)
+ * kind = PyUnicode_1BYTE_KIND
+ * compact = 1
+ * ascii = 1
+ * ready = 1
+ * (length is the length of the utf8 and wstr strings)
+ * (data starts just after the structure)
+ * (since ASCII is decoded from UTF-8, the utf8 string are the data)
+
+ - compact:
+
+ * structure = PyCompactUnicodeObject
+ * test: PyUnicode_IS_ASCII(op) && !PyUnicode_IS_COMPACT(op)
+ * kind = PyUnicode_1BYTE_KIND, PyUnicode_2BYTE_KIND or
+ PyUnicode_4BYTE_KIND
+ * compact = 1
+ * ready = 1
+ * ascii = 0
+ * utf8 is not shared with data
+ * utf8_length = 0 if utf8 is NULL
+ * wstr is shared with data and wstr_length=length
+ if kind=PyUnicode_2BYTE_KIND and sizeof(wchar_t)=2
+ or if kind=PyUnicode_4BYTE_KIND and sizeof(wchar_t)=4
+ * wstr_length = 0 if wstr is NULL
+ * (data starts just after the structure)
+
+ - legacy string, not ready:
+
+ * structure = PyUnicodeObject
+ * test: kind == PyUnicode_WCHAR_KIND
+ * length = 0 (use wstr_length)
+ * hash = -1
+ * kind = PyUnicode_WCHAR_KIND
+ * compact = 0
+ * ascii = 0
+ * ready = 0
+ * interned = SSTATE_NOT_INTERNED
+ * wstr is not NULL
+ * data.any is NULL
+ * utf8 is NULL
+ * utf8_length = 0
+
+ - legacy string, ready:
+
+ * structure = PyUnicodeObject structure
+ * test: !PyUnicode_IS_COMPACT(op) && kind != PyUnicode_WCHAR_KIND
+ * kind = PyUnicode_1BYTE_KIND, PyUnicode_2BYTE_KIND or
+ PyUnicode_4BYTE_KIND
+ * compact = 0
+ * ready = 1
+ * data.any is not NULL
+ * utf8 is shared and utf8_length = length with data.any if ascii = 1
+ * utf8_length = 0 if utf8 is NULL
+ * wstr is shared with data.any and wstr_length = length
+ if kind=PyUnicode_2BYTE_KIND and sizeof(wchar_t)=2
+ or if kind=PyUnicode_4BYTE_KIND and sizeof(wchar_4)=4
+ * wstr_length = 0 if wstr is NULL
+
+ Compact strings use only one memory block (structure + characters),
+ whereas legacy strings use one block for the structure and one block
+ for characters.
+
+ Legacy strings are created by PyUnicode_FromUnicode() and
+ PyUnicode_FromStringAndSize(NULL, size) functions. They become ready
+ when PyUnicode_READY() is called.
+
+ See also _PyUnicode_CheckConsistency().
+ */
PyObject_HEAD
- Py_ssize_t length; /* Length of raw Unicode data in buffer */
- Py_UNICODE *str; /* Raw Unicode buffer */
+ Py_ssize_t length; /* Number of code points in the string */
Py_hash_t hash; /* Hash value; -1 if not set */
- int state; /* != 0 if interned. In this case the two
- * references from the dictionary to this object
- * are *not* counted in ob_refcnt. */
- PyObject *defenc; /* (Default) Encoded version as Python
- string, or NULL; this is used for
- implementing the buffer protocol */
+ struct {
+ /*
+ SSTATE_NOT_INTERNED (0)
+ SSTATE_INTERNED_MORTAL (1)
+ SSTATE_INTERNED_IMMORTAL (2)
+
+ If interned != SSTATE_NOT_INTERNED, the two references from the
+ dictionary to this object are *not* counted in ob_refcnt.
+ */
+ unsigned int interned:2;
+ /* Character size:
+
+ - PyUnicode_WCHAR_KIND (0):
+
+ * character type = wchar_t (16 or 32 bits, depending on the
+ platform)
+
+ - PyUnicode_1BYTE_KIND (1):
+
+ * character type = Py_UCS1 (8 bits, unsigned)
+ * all characters are in the range U+0000-U+00FF (latin1)
+ * if ascii is set, all characters are in the range U+0000-U+007F
+ (ASCII), otherwise at least one character is in the range
+ U+0080-U+00FF
+
+ - PyUnicode_2BYTE_KIND (2):
+
+ * character type = Py_UCS2 (16 bits, unsigned)
+ * all characters are in the range U+0000-U+FFFF (BMP)
+ * at least one character is in the range U+0100-U+FFFF
+
+ - PyUnicode_4BYTE_KIND (4):
+
+ * character type = Py_UCS4 (32 bits, unsigned)
+ * all characters are in the range U+0000-U+10FFFF
+ * at least one character is in the range U+10000-U+10FFFF
+ */
+ unsigned int kind:3;
+ /* Compact is with respect to the allocation scheme. Compact unicode
+ objects only require one memory block while non-compact objects use
+ one block for the PyUnicodeObject struct and another for its data
+ buffer. */
+ unsigned int compact:1;
+ /* The string only contains characters in the range U+0000-U+007F (ASCII)
+ and the kind is PyUnicode_1BYTE_KIND. If ascii is set and compact is
+ set, use the PyASCIIObject structure. */
+ unsigned int ascii:1;
+ /* The ready flag indicates whether the object layout is initialized
+ completely. This means that this is either a compact object, or
+ the data pointer is filled out. The bit is redundant, and helps
+ to minimize the test in PyUnicode_IS_READY(). */
+ unsigned int ready:1;
+ } state;
+ wchar_t *wstr; /* wchar_t representation (null-terminated) */
+} PyASCIIObject;
+
+/* Non-ASCII strings allocated through PyUnicode_New use the
+ PyCompactUnicodeObject structure. state.compact is set, and the data
+ immediately follow the structure. */
+typedef struct {
+ PyASCIIObject _base;
+ Py_ssize_t utf8_length; /* Number of bytes in utf8, excluding the
+ * terminating \0. */
+ char *utf8; /* UTF-8 representation (null-terminated) */
+ Py_ssize_t wstr_length; /* Number of code points in wstr, possible
+ * surrogates count as two code points. */
+} PyCompactUnicodeObject;
+
+/* Strings allocated through PyUnicode_FromUnicode(NULL, len) use the
+ PyUnicodeObject structure. The actual string data is initially in the wstr
+ block, and copied into the data block using _PyUnicode_Ready. */
+typedef struct {
+ PyCompactUnicodeObject _base;
+ union {
+ void *any;
+ Py_UCS1 *latin1;
+ Py_UCS2 *ucs2;
+ Py_UCS4 *ucs4;
+ } data; /* Canonical, smallest-form Unicode buffer */
} PyUnicodeObject;
#endif
PyAPI_DATA(PyTypeObject) PyUnicode_Type;
PyAPI_DATA(PyTypeObject) PyUnicodeIter_Type;
-#define SSTATE_NOT_INTERNED 0
-#define SSTATE_INTERNED_MORTAL 1
-#define SSTATE_INTERNED_IMMORTAL 2
-
#define PyUnicode_Check(op) \
PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_UNICODE_SUBCLASS)
#define PyUnicode_CheckExact(op) (Py_TYPE(op) == &PyUnicode_Type)
/* Fast access macros */
#ifndef Py_LIMITED_API
-#define PyUnicode_GET_SIZE(op) \
- (assert(PyUnicode_Check(op)),(((PyUnicodeObject *)(op))->length))
+
+#define PyUnicode_WSTR_LENGTH(op) \
+ (PyUnicode_IS_COMPACT_ASCII(op) ? \
+ ((PyASCIIObject*)op)->length : \
+ ((PyCompactUnicodeObject*)op)->wstr_length)
+
+/* Returns the deprecated Py_UNICODE representation's size in code units
+ (this includes surrogate pairs as 2 units).
+ If the Py_UNICODE representation is not available, it will be computed
+ on request. Use PyUnicode_GET_LENGTH() for the length in code points. */
+
+#define PyUnicode_GET_SIZE(op) \
+ (assert(PyUnicode_Check(op)), \
+ (((PyASCIIObject *)(op))->wstr) ? \
+ PyUnicode_WSTR_LENGTH(op) : \
+ ((void)PyUnicode_AsUnicode((PyObject *)(op)), \
+ assert(((PyASCIIObject *)(op))->wstr), \
+ PyUnicode_WSTR_LENGTH(op)))
+
#define PyUnicode_GET_DATA_SIZE(op) \
- (assert(PyUnicode_Check(op)),(((PyUnicodeObject *)(op))->length * sizeof(Py_UNICODE)))
+ (PyUnicode_GET_SIZE(op) * Py_UNICODE_SIZE)
+
+/* Alias for PyUnicode_AsUnicode(). This will create a wchar_t/Py_UNICODE
+ representation on demand. Using this macro is very inefficient now,
+ try to port your code to use the new PyUnicode_*BYTE_DATA() macros or
+ use PyUnicode_WRITE() and PyUnicode_READ(). */
+
#define PyUnicode_AS_UNICODE(op) \
- (assert(PyUnicode_Check(op)),(((PyUnicodeObject *)(op))->str))
+ (assert(PyUnicode_Check(op)), \
+ (((PyASCIIObject *)(op))->wstr) ? (((PyASCIIObject *)(op))->wstr) : \
+ PyUnicode_AsUnicode((PyObject *)(op)))
+
#define PyUnicode_AS_DATA(op) \
- (assert(PyUnicode_Check(op)),((const char *)((PyUnicodeObject *)(op))->str))
+ ((const char *)(PyUnicode_AS_UNICODE(op)))
+
+
+/* --- Flexible String Representation Helper Macros (PEP 393) -------------- */
+
+/* Values for PyASCIIObject.state: */
+
+/* Interning state. */
+#define SSTATE_NOT_INTERNED 0
+#define SSTATE_INTERNED_MORTAL 1
+#define SSTATE_INTERNED_IMMORTAL 2
+
+/* Return true if the string contains only ASCII characters, or 0 if not. The
+ string may be compact (PyUnicode_IS_COMPACT_ASCII) or not, but must be
+ ready. */
+#define PyUnicode_IS_ASCII(op) \
+ (assert(PyUnicode_Check(op)), \
+ assert(PyUnicode_IS_READY(op)), \
+ ((PyASCIIObject*)op)->state.ascii)
+
+/* Return true if the string is compact or 0 if not.
+ No type checks or Ready calls are performed. */
+#define PyUnicode_IS_COMPACT(op) \
+ (((PyASCIIObject*)(op))->state.compact)
+
+/* Return true if the string is a compact ASCII string (use PyASCIIObject
+ structure), or 0 if not. No type checks or Ready calls are performed. */
+#define PyUnicode_IS_COMPACT_ASCII(op) \
+ (((PyASCIIObject*)op)->state.ascii && PyUnicode_IS_COMPACT(op))
+
+/* String contains only wstr byte characters. This is only possible
+ when the string was created with a legacy API and _PyUnicode_Ready()
+ has not been called yet. */
+#define PyUnicode_WCHAR_KIND 0
+
+/* Return values of the PyUnicode_KIND() macro */
+enum PyUnicode_Kind {
+ PyUnicode_1BYTE_KIND = 1,
+ PyUnicode_2BYTE_KIND = 2,
+ PyUnicode_4BYTE_KIND = 4
+};
+
+/* Return pointers to the canonical representation cast to unsigned char,
+ Py_UCS2, or Py_UCS4 for direct character access.
+ No checks are performed, use PyUnicode_KIND() before to ensure
+ these will work correctly. */
+
+#define PyUnicode_1BYTE_DATA(op) ((Py_UCS1*)PyUnicode_DATA(op))
+#define PyUnicode_2BYTE_DATA(op) ((Py_UCS2*)PyUnicode_DATA(op))
+#define PyUnicode_4BYTE_DATA(op) ((Py_UCS4*)PyUnicode_DATA(op))
+
+/* Return one of the PyUnicode_*_KIND values defined above. */
+#define PyUnicode_KIND(op) \
+ (assert(PyUnicode_Check(op)), \
+ assert(PyUnicode_IS_READY(op)), \
+ ((PyASCIIObject *)(op))->state.kind)
+
+/* Return a void pointer to the raw unicode buffer. */
+#define _PyUnicode_COMPACT_DATA(op) \
+ (PyUnicode_IS_ASCII(op) ? \
+ ((void*)((PyASCIIObject*)(op) + 1)) : \
+ ((void*)((PyCompactUnicodeObject*)(op) + 1)))
+
+#define _PyUnicode_NONCOMPACT_DATA(op) \
+ (assert(((PyUnicodeObject*)(op))->data.any), \
+ ((((PyUnicodeObject *)(op))->data.any)))
+
+#define PyUnicode_DATA(op) \
+ (assert(PyUnicode_Check(op)), \
+ PyUnicode_IS_COMPACT(op) ? _PyUnicode_COMPACT_DATA(op) : \
+ _PyUnicode_NONCOMPACT_DATA(op))
+
+/* In the access macros below, "kind" may be evaluated more than once.
+ All other macro parameters are evaluated exactly once, so it is safe
+ to put side effects into them (such as increasing the index). */
+
+/* Write into the canonical representation, this macro does not do any sanity
+ checks and is intended for usage in loops. The caller should cache the
+ kind and data pointers obtained from other macro calls.
+ index is the index in the string (starts at 0) and value is the new
+ code point value which should be written to that location. */
+#define PyUnicode_WRITE(kind, data, index, value) \
+ do { \
+ switch ((kind)) { \
+ case PyUnicode_1BYTE_KIND: { \
+ ((Py_UCS1 *)(data))[(index)] = (Py_UCS1)(value); \
+ break; \
+ } \
+ case PyUnicode_2BYTE_KIND: { \
+ ((Py_UCS2 *)(data))[(index)] = (Py_UCS2)(value); \
+ break; \
+ } \
+ default: { \
+ assert((kind) == PyUnicode_4BYTE_KIND); \
+ ((Py_UCS4 *)(data))[(index)] = (Py_UCS4)(value); \
+ } \
+ } \
+ } while (0)
+
+/* Read a code point from the string's canonical representation. No checks
+ or ready calls are performed. */
+#define PyUnicode_READ(kind, data, index) \
+ ((Py_UCS4) \
+ ((kind) == PyUnicode_1BYTE_KIND ? \
+ ((const Py_UCS1 *)(data))[(index)] : \
+ ((kind) == PyUnicode_2BYTE_KIND ? \
+ ((const Py_UCS2 *)(data))[(index)] : \
+ ((const Py_UCS4 *)(data))[(index)] \
+ ) \
+ ))
+
+/* PyUnicode_READ_CHAR() is less efficient than PyUnicode_READ() because it
+ calls PyUnicode_KIND() and might call it twice. For single reads, use
+ PyUnicode_READ_CHAR, for multiple consecutive reads callers should
+ cache kind and use PyUnicode_READ instead. */
+#define PyUnicode_READ_CHAR(unicode, index) \
+ (assert(PyUnicode_Check(unicode)), \
+ assert(PyUnicode_IS_READY(unicode)), \
+ (Py_UCS4) \
+ (PyUnicode_KIND((unicode)) == PyUnicode_1BYTE_KIND ? \
+ ((const Py_UCS1 *)(PyUnicode_DATA((unicode))))[(index)] : \
+ (PyUnicode_KIND((unicode)) == PyUnicode_2BYTE_KIND ? \
+ ((const Py_UCS2 *)(PyUnicode_DATA((unicode))))[(index)] : \
+ ((const Py_UCS4 *)(PyUnicode_DATA((unicode))))[(index)] \
+ ) \
+ ))
+
+/* Returns the length of the unicode string. The caller has to make sure that
+ the string has it's canonical representation set before calling
+ this macro. Call PyUnicode_(FAST_)Ready to ensure that. */
+#define PyUnicode_GET_LENGTH(op) \
+ (assert(PyUnicode_Check(op)), \
+ assert(PyUnicode_IS_READY(op)), \
+ ((PyASCIIObject *)(op))->length)
+
+
+/* Fast check to determine whether an object is ready. Equivalent to
+ PyUnicode_IS_COMPACT(op) || ((PyUnicodeObject*)(op))->data.any) */
+
+#define PyUnicode_IS_READY(op) (((PyASCIIObject*)op)->state.ready)
+
+/* PyUnicode_READY() does less work than _PyUnicode_Ready() in the best
+ case. If the canonical representation is not yet set, it will still call
+ _PyUnicode_Ready().
+ Returns 0 on success and -1 on errors. */
+#define PyUnicode_READY(op) \
+ (assert(PyUnicode_Check(op)), \
+ (PyUnicode_IS_READY(op) ? \
+ 0 : _PyUnicode_Ready((PyObject *)(op))))
+
+/* Return a maximum character value which is suitable for creating another
+ string based on op. This is always an approximation but more efficient
+ than iterating over the string. */
+#define PyUnicode_MAX_CHAR_VALUE(op) \
+ (assert(PyUnicode_IS_READY(op)), \
+ (PyUnicode_IS_ASCII(op) ? \
+ (0x7f) : \
+ (PyUnicode_KIND(op) == PyUnicode_1BYTE_KIND ? \
+ (0xffU) : \
+ (PyUnicode_KIND(op) == PyUnicode_2BYTE_KIND ? \
+ (0xffffU) : \
+ (0x10ffffU)))))
+
#endif
/* --- Constants ---------------------------------------------------------- */
@@ -418,12 +587,70 @@ PyAPI_DATA(PyTypeObject) PyUnicodeIter_Type;
Unicode character U+FFFD is the official REPLACEMENT CHARACTER in
Unicode 3.0. */
-#define Py_UNICODE_REPLACEMENT_CHARACTER ((Py_UNICODE) 0xFFFD)
+#define Py_UNICODE_REPLACEMENT_CHARACTER ((Py_UCS4) 0xFFFD)
/* === Public API ========================================================= */
/* --- Plain Py_UNICODE --------------------------------------------------- */
+/* With PEP 393, this is the recommended way to allocate a new unicode object.
+ This function will allocate the object and its buffer in a single memory
+ block. Objects created using this function are not resizable. */
+#ifndef Py_LIMITED_API
+PyAPI_FUNC(PyObject*) PyUnicode_New(
+ Py_ssize_t size, /* Number of code points in the new string */
+ Py_UCS4 maxchar /* maximum code point value in the string */
+ );
+#endif
+
+/* Initializes the canonical string representation from a the deprecated
+ wstr/Py_UNICODE representation. This function is used to convert Unicode
+ objects which were created using the old API to the new flexible format
+ introduced with PEP 393.
+
+ Don't call this function directly, use the public PyUnicode_READY() macro
+ instead. */
+#ifndef Py_LIMITED_API
+PyAPI_FUNC(int) _PyUnicode_Ready(
+ PyObject *unicode /* Unicode object */
+ );
+#endif
+
+/* Get a copy of a Unicode string. */
+#ifndef Py_LIMITED_API
+PyAPI_FUNC(PyObject*) _PyUnicode_Copy(
+ PyObject *unicode
+ );
+#endif
+
+/* Copy character from one unicode object into another, this function performs
+ character conversion when necessary and falls back to memcpy if possible.
+
+ Fail if to is too small (smaller than how_many or smaller than
+ len(from)-from_start), or if kind(from[from_start:from_start+how_many]) >
+ kind(to), or if to has more than 1 reference.
+
+ Return the number of written character, or return -1 and raise an exception
+ on error.
+
+ Pseudo-code:
+
+ how_many = min(how_many, len(from) - from_start)
+ to[to_start:to_start+how_many] = from[from_start:from_start+how_many]
+ return how_many
+
+ Note: The function doesn't write a terminating null character.
+ */
+#ifndef Py_LIMITED_API
+PyAPI_FUNC(Py_ssize_t) PyUnicode_CopyCharacters(
+ PyObject *to,
+ Py_ssize_t to_start,
+ PyObject *from,
+ Py_ssize_t from_start,
+ Py_ssize_t how_many
+ );
+#endif
+
/* Create a Unicode Object from the Py_UNICODE buffer u of the given
size.
@@ -448,13 +675,45 @@ PyAPI_FUNC(PyObject*) PyUnicode_FromStringAndSize(
);
/* Similar to PyUnicode_FromUnicode(), but u points to null-terminated
- UTF-8 encoded bytes */
+ UTF-8 encoded bytes. The size is determined with strlen(). */
PyAPI_FUNC(PyObject*) PyUnicode_FromString(
const char *u /* UTF-8 encoded string */
);
+/* Create a new string from a buffer of Py_UCS1, Py_UCS2 or Py_UCS4 characters.
+ Scan the string to find the maximum character. */
+#ifndef Py_LIMITED_API
+PyAPI_FUNC(PyObject*) PyUnicode_FromKindAndData(
+ int kind,
+ const void *buffer,
+ Py_ssize_t size);
+#endif
+
+PyAPI_FUNC(PyObject*) PyUnicode_Substring(
+ PyObject *str,
+ Py_ssize_t start,
+ Py_ssize_t end);
+
+/* Copy the string into a UCS4 buffer including the null character if copy_null
+ is set. Return NULL and raise an exception on error. Raise a ValueError if
+ the buffer is smaller than the string. Return buffer on success.
+
+ buflen is the length of the buffer in (Py_UCS4) characters. */
+PyAPI_FUNC(Py_UCS4*) PyUnicode_AsUCS4(
+ PyObject *unicode,
+ Py_UCS4* buffer,
+ Py_ssize_t buflen,
+ int copy_null);
+
+/* Copy the string into a UCS4 buffer. A new buffer is allocated using
+ * PyMem_Malloc; if this fails, NULL is returned with a memory error
+ exception set. */
+PyAPI_FUNC(Py_UCS4*) PyUnicode_AsUCS4Copy(PyObject *unicode);
+
/* Return a read-only pointer to the Unicode object's internal
- Py_UNICODE buffer. */
+ Py_UNICODE buffer.
+ If the wchar_t/Py_UNICODE representation is not yet available, this
+ function will calculate it. */
#ifndef Py_LIMITED_API
PyAPI_FUNC(Py_UNICODE *) PyUnicode_AsUnicode(
@@ -462,30 +721,69 @@ PyAPI_FUNC(Py_UNICODE *) PyUnicode_AsUnicode(
);
#endif
+/* Return a read-only pointer to the Unicode object's internal
+ Py_UNICODE buffer and save the length at size.
+ If the wchar_t/Py_UNICODE representation is not yet available, this
+ function will calculate it. */
+
+#ifndef Py_LIMITED_API
+PyAPI_FUNC(Py_UNICODE *) PyUnicode_AsUnicodeAndSize(
+ PyObject *unicode, /* Unicode object */
+ Py_ssize_t *size /* location where to save the length */
+ );
+#endif
+
/* Get the length of the Unicode object. */
+PyAPI_FUNC(Py_ssize_t) PyUnicode_GetLength(
+ PyObject *unicode
+);
+
+/* Get the number of Py_UNICODE units in the
+ string representation. */
+
PyAPI_FUNC(Py_ssize_t) PyUnicode_GetSize(
PyObject *unicode /* Unicode object */
);
+/* Read a character from the string. */
+
+PyAPI_FUNC(Py_UCS4) PyUnicode_ReadChar(
+ PyObject *unicode,
+ Py_ssize_t index
+ );
+
+/* Write a character to the string. The string must have been created through
+ PyUnicode_New, must not be shared, and must not have been hashed yet.
+
+ Return 0 on success, -1 on error. */
+
+PyAPI_FUNC(int) PyUnicode_WriteChar(
+ PyObject *unicode,
+ Py_ssize_t index,
+ Py_UCS4 character
+ );
+
#ifndef Py_LIMITED_API
/* Get the maximum ordinal for a Unicode character. */
PyAPI_FUNC(Py_UNICODE) PyUnicode_GetMax(void);
#endif
-/* Resize an already allocated Unicode object to the new size length.
+/* Resize an Unicode object. The length is the number of characters, except
+ if the kind of the string is PyUnicode_WCHAR_KIND: in this case, the length
+ is the number of Py_UNICODE characters.
*unicode is modified to point to the new (resized) object and 0
returned on success.
- This API may only be called by the function which also called the
- Unicode constructor. The refcount on the object must be 1. Otherwise,
- an error is returned.
+ Try to resize the string in place (which is usually faster than allocating
+ a new string and copy characters), or create a new string.
Error handling is implemented as follows: an exception is set, -1
is returned and *unicode left untouched.
-*/
+ WARNING: The function doesn't check string content, the result may not be a
+ string in canonical representation. */
PyAPI_FUNC(int) PyUnicode_Resize(
PyObject **unicode, /* Pointer to the Unicode object */
@@ -545,8 +843,9 @@ PyAPI_FUNC(PyObject *) PyUnicode_FromFormat(
/* Format the object based on the format_spec, as defined in PEP 3101
(Advanced String Formatting). */
PyAPI_FUNC(PyObject *) _PyUnicode_FormatAdvanced(PyObject *obj,
- Py_UNICODE *format_spec,
- Py_ssize_t format_spec_len);
+ PyObject *format_spec,
+ Py_ssize_t start,
+ Py_ssize_t end);
#endif
PyAPI_FUNC(void) PyUnicode_InternInPlace(PyObject **);
@@ -559,7 +858,8 @@ PyAPI_FUNC(void) _Py_ReleaseInternedUnicodeStrings(void);
#endif
/* Use only if you know it's a string */
-#define PyUnicode_CHECK_INTERNED(op) (((PyUnicodeObject *)(op))->state)
+#define PyUnicode_CHECK_INTERNED(op) \
+ (((PyASCIIObject *)(op))->state.interned)
/* --- wchar_t support for platforms which support it --------------------- */
@@ -606,6 +906,10 @@ PyAPI_FUNC(wchar_t*) PyUnicode_AsWideCharString(
Py_ssize_t *size /* number of characters of the result */
);
+#ifndef Py_LIMITED_API
+PyAPI_FUNC(void*) _PyUnicode_AsKind(PyObject *s, unsigned int kind);
+#endif
+
#endif
/* --- Unicode ordinals --------------------------------------------------- */
@@ -651,50 +955,42 @@ PyAPI_FUNC(int) PyUnicode_ClearFreeList(void);
/* --- Manage the default encoding ---------------------------------------- */
-/* Return a Python string holding the default encoded value of the
- Unicode object.
-
- Same as PyUnicode_AsUTF8String() except
- the resulting string is cached in the Unicode object for subsequent
- usage by this function. The cached version is needed to implement
- the character buffer interface and will live (at least) as long as
- the Unicode object itself.
-
- The refcount of the string is *not* incremented.
-
- *** Exported for internal use by the interpreter only !!! ***
-
-*/
-
-#ifndef Py_LIMITED_API
-PyAPI_FUNC(PyObject *) _PyUnicode_AsDefaultEncodedString(
- PyObject *unicode,
- const char *errors);
-#endif
-
/* Returns a pointer to the default encoding (UTF-8) of the
Unicode object unicode and the size of the encoded representation
in bytes stored in *size.
In case of an error, no *size is set.
+ This function caches the UTF-8 encoded string in the unicodeobject
+ and subsequent calls will return the same string. The memory is released
+ when the unicodeobject is deallocated.
+
+ _PyUnicode_AsStringAndSize is a #define for PyUnicode_AsUTF8AndSize to
+ support the previous internal function with the same behaviour.
+
*** This API is for interpreter INTERNAL USE ONLY and will likely
*** be removed or changed in the future.
*** If you need to access the Unicode object as UTF-8 bytes string,
*** please use PyUnicode_AsUTF8String() instead.
-
*/
#ifndef Py_LIMITED_API
-PyAPI_FUNC(char *) _PyUnicode_AsStringAndSize(
+PyAPI_FUNC(char *) PyUnicode_AsUTF8AndSize(
PyObject *unicode,
Py_ssize_t *size);
+#define _PyUnicode_AsStringAndSize PyUnicode_AsUTF8AndSize
#endif
/* Returns a pointer to the default encoding (UTF-8) of the
Unicode object unicode.
+ Like PyUnicode_AsUTF8AndSize(), this also caches the UTF-8 representation
+ in the unicodeobject.
+
+ _PyUnicode_AsString is a #define for PyUnicode_AsUTF8 to
+ support the previous internal function with the same behaviour.
+
Use of this API is DEPRECATED since no size information can be
extracted from the returned data.
@@ -707,7 +1003,8 @@ PyAPI_FUNC(char *) _PyUnicode_AsStringAndSize(
*/
#ifndef Py_LIMITED_API
-PyAPI_FUNC(char *) _PyUnicode_AsString(PyObject *unicode);
+PyAPI_FUNC(char *) PyUnicode_AsUTF8(PyObject *unicode);
+#define _PyUnicode_AsString PyUnicode_AsUTF8
#endif
/* Returns "utf-8". */
@@ -812,6 +1109,12 @@ PyAPI_FUNC(PyObject*) PyUnicode_EncodeUTF7(
int base64WhiteSpace, /* Encode whitespace (sp, ht, nl, cr) in base64 */
const char *errors /* error handling */
);
+PyAPI_FUNC(PyObject*) _PyUnicode_EncodeUTF7(
+ PyObject *unicode, /* Unicode object */
+ int base64SetO, /* Encode RFC2152 Set O characters in base64 */
+ int base64WhiteSpace, /* Encode whitespace (sp, ht, nl, cr) in base64 */
+ const char *errors /* error handling */
+ );
#endif
/* --- UTF-8 Codecs ------------------------------------------------------- */
@@ -834,6 +1137,10 @@ PyAPI_FUNC(PyObject*) PyUnicode_AsUTF8String(
);
#ifndef Py_LIMITED_API
+PyAPI_FUNC(PyObject*) _PyUnicode_AsUTF8String(
+ PyObject *unicode,
+ const char *errors);
+
PyAPI_FUNC(PyObject*) PyUnicode_EncodeUTF8(
const Py_UNICODE *data, /* Unicode char buffer */
Py_ssize_t length, /* number of Py_UNICODE chars to encode */
@@ -915,6 +1222,11 @@ PyAPI_FUNC(PyObject*) PyUnicode_EncodeUTF32(
const char *errors, /* error handling */
int byteorder /* byteorder to use 0=BOM+native;-1=LE,1=BE */
);
+PyAPI_FUNC(PyObject*) _PyUnicode_EncodeUTF32(
+ PyObject *object, /* Unicode object */
+ const char *errors, /* error handling */
+ int byteorder /* byteorder to use 0=BOM+native;-1=LE,1=BE */
+ );
#endif
/* --- UTF-16 Codecs ------------------------------------------------------ */
@@ -995,6 +1307,11 @@ PyAPI_FUNC(PyObject*) PyUnicode_EncodeUTF16(
const char *errors, /* error handling */
int byteorder /* byteorder to use 0=BOM+native;-1=LE,1=BE */
);
+PyAPI_FUNC(PyObject*) _PyUnicode_EncodeUTF16(
+ PyObject* unicode, /* Unicode object */
+ const char *errors, /* error handling */
+ int byteorder /* byteorder to use 0=BOM+native;-1=LE,1=BE */
+ );
#endif
/* --- Unicode-Escape Codecs ---------------------------------------------- */
@@ -1064,6 +1381,10 @@ PyAPI_FUNC(PyObject*) PyUnicode_AsLatin1String(
);
#ifndef Py_LIMITED_API
+PyAPI_FUNC(PyObject*) _PyUnicode_AsLatin1String(
+ PyObject* unicode,
+ const char* errors);
+
PyAPI_FUNC(PyObject*) PyUnicode_EncodeLatin1(
const Py_UNICODE *data, /* Unicode char buffer */
Py_ssize_t length, /* Number of Py_UNICODE chars to encode */
@@ -1088,6 +1409,10 @@ PyAPI_FUNC(PyObject*) PyUnicode_AsASCIIString(
);
#ifndef Py_LIMITED_API
+PyAPI_FUNC(PyObject*) _PyUnicode_AsASCIIString(
+ PyObject* unicode,
+ const char* errors);
+
PyAPI_FUNC(PyObject*) PyUnicode_EncodeASCII(
const Py_UNICODE *data, /* Unicode char buffer */
Py_ssize_t length, /* Number of Py_UNICODE chars to encode */
@@ -1139,6 +1464,12 @@ PyAPI_FUNC(PyObject*) PyUnicode_EncodeCharmap(
(unicode ordinal -> char ordinal) */
const char *errors /* error handling */
);
+PyAPI_FUNC(PyObject*) _PyUnicode_EncodeCharmap(
+ PyObject *unicode, /* Unicode object */
+ PyObject *mapping, /* character mapping
+ (unicode ordinal -> char ordinal) */
+ const char *errors /* error handling */
+ );
#endif
/* Translate a Py_UNICODE buffer of the given length by applying a
@@ -1163,7 +1494,7 @@ PyAPI_FUNC(PyObject *) PyUnicode_TranslateCharmap(
);
#endif
-#ifdef MS_WIN32
+#ifdef HAVE_MBCS
/* --- MBCS codecs for Windows -------------------------------------------- */
@@ -1180,6 +1511,14 @@ PyAPI_FUNC(PyObject*) PyUnicode_DecodeMBCSStateful(
Py_ssize_t *consumed /* bytes consumed */
);
+PyAPI_FUNC(PyObject*) PyUnicode_DecodeCodePageStateful(
+ int code_page, /* code page number */
+ const char *string, /* encoded string */
+ Py_ssize_t length, /* size of string */
+ const char *errors, /* error handling */
+ Py_ssize_t *consumed /* bytes consumed */
+ );
+
PyAPI_FUNC(PyObject*) PyUnicode_AsMBCSString(
PyObject *unicode /* Unicode object */
);
@@ -1187,12 +1526,18 @@ PyAPI_FUNC(PyObject*) PyUnicode_AsMBCSString(
#ifndef Py_LIMITED_API
PyAPI_FUNC(PyObject*) PyUnicode_EncodeMBCS(
const Py_UNICODE *data, /* Unicode char buffer */
- Py_ssize_t length, /* Number of Py_UNICODE chars to encode */
+ Py_ssize_t length, /* number of Py_UNICODE chars to encode */
const char *errors /* error handling */
);
#endif
-#endif /* MS_WIN32 */
+PyAPI_FUNC(PyObject*) PyUnicode_EncodeCodePage(
+ int code_page, /* code page number */
+ PyObject *unicode, /* Unicode object */
+ const char *errors /* error handling */
+ );
+
+#endif /* HAVE_MBCS */
/* --- Decimal Encoder ---------------------------------------------------- */
@@ -1240,6 +1585,49 @@ PyAPI_FUNC(PyObject*) PyUnicode_TransformDecimalToASCII(
);
#endif
+/* Similar to PyUnicode_TransformDecimalToASCII(), but takes a PyObject
+ as argument instead of a raw buffer and length. This function additionally
+ transforms spaces to ASCII because this is what the callers in longobject,
+ floatobject, and complexobject did anyways. */
+
+#ifndef Py_LIMITED_API
+PyAPI_FUNC(PyObject*) _PyUnicode_TransformDecimalAndSpaceToASCII(
+ PyObject *unicode /* Unicode object */
+ );
+#endif
+
+/* --- Locale encoding --------------------------------------------------- */
+
+/* Decode a string from the current locale encoding. The decoder is strict if
+ *surrogateescape* is equal to zero, otherwise it uses the 'surrogateescape'
+ error handler (PEP 383) to escape undecodable bytes. If a byte sequence can
+ be decoded as a surrogate character and *surrogateescape* is not equal to
+ zero, the byte sequence is escaped using the 'surrogateescape' error handler
+ instead of being decoded. *str* must end with a null character but cannot
+ contain embedded null characters. */
+
+PyAPI_FUNC(PyObject*) PyUnicode_DecodeLocaleAndSize(
+ const char *str,
+ Py_ssize_t len,
+ const char *errors);
+
+/* Similar to PyUnicode_DecodeLocaleAndSize(), but compute the string
+ length using strlen(). */
+
+PyAPI_FUNC(PyObject*) PyUnicode_DecodeLocale(
+ const char *str,
+ const char *errors);
+
+/* Encode a Unicode object to the current locale encoding. The encoder is
+ strict is *surrogateescape* is equal to zero, otherwise the
+ "surrogateescape" error handler is used. Return a bytes object. The string
+ cannot contain embedded null characters.. */
+
+PyAPI_FUNC(PyObject*) PyUnicode_EncodeLocale(
+ PyObject *unicode,
+ const char *errors
+ );
+
/* --- File system encoding ---------------------------------------------- */
/* ParseTuple converter: encode str objects to bytes using
@@ -1292,7 +1680,7 @@ PyAPI_FUNC(PyObject*) PyUnicode_EncodeFSDefault(
These are capable of handling Unicode objects and strings on input
(we refer to them as strings in the descriptions) and return
- Unicode objects or integers as apporpriate. */
+ Unicode objects or integers as appropriate. */
/* Concat two strings giving a new Unicode string. */
@@ -1427,6 +1815,15 @@ PyAPI_FUNC(Py_ssize_t) PyUnicode_Find(
int direction /* Find direction: +1 forward, -1 backward */
);
+/* Like PyUnicode_Find, but search for single character only. */
+PyAPI_FUNC(Py_ssize_t) PyUnicode_FindChar(
+ PyObject *str,
+ Py_UCS4 ch,
+ Py_ssize_t start,
+ Py_ssize_t end,
+ int direction
+ );
+
/* Count the number of occurrences of substr in str[start:end]. */
PyAPI_FUNC(Py_ssize_t) PyUnicode_Count(
@@ -1463,7 +1860,7 @@ PyAPI_FUNC(int) PyUnicode_CompareWithASCIIString(
/* Rich compare two strings and return one of the following:
- NULL in case an exception was raised
- - Py_True or Py_False for successfuly comparisons
+ - Py_True or Py_False for successfully comparisons
- Py_NotImplemented in case the type combination is unknown
Note that Py_EQ and Py_NE comparisons can cause a UnicodeWarning in
@@ -1508,7 +1905,7 @@ PyAPI_FUNC(int) PyUnicode_IsIdentifier(PyObject *s);
#ifndef Py_LIMITED_API
/* Externally visible for str.strip(unicode) */
PyAPI_FUNC(PyObject *) _PyUnicode_XStrip(
- PyUnicodeObject *self,
+ PyObject *self,
int striptype,
PyObject *sepobj
);
@@ -1530,13 +1927,16 @@ PyAPI_FUNC(Py_ssize_t) _PyUnicode_InsertThousandsGroupingLocale(Py_UNICODE *buff
into the string pointed to by buffer. For the argument descriptions,
see Objects/stringlib/localeutil.h */
#ifndef Py_LIMITED_API
-PyAPI_FUNC(Py_ssize_t) _PyUnicode_InsertThousandsGrouping(Py_UNICODE *buffer,
- Py_ssize_t n_buffer,
- Py_UNICODE *digits,
- Py_ssize_t n_digits,
- Py_ssize_t min_width,
- const char *grouping,
- const char *thousands_sep);
+PyAPI_FUNC(Py_ssize_t) _PyUnicode_InsertThousandsGrouping(
+ PyObject *unicode,
+ int kind,
+ void *buffer,
+ Py_ssize_t n_buffer,
+ void *digits,
+ Py_ssize_t n_digits,
+ Py_ssize_t min_width,
+ const char *grouping,
+ const char *thousands_sep);
#endif
/* === Characters Type APIs =============================================== */
@@ -1670,6 +2070,46 @@ PyAPI_FUNC(Py_UNICODE*) PyUnicode_AsUnicodeCopy(
);
#endif /* Py_LIMITED_API */
+#if defined(Py_DEBUG) && !defined(Py_LIMITED_API)
+PyAPI_FUNC(int) _PyUnicode_CheckConsistency(
+ PyObject *op,
+ int check_content);
+#endif
+
+/********************* String Literals ****************************************/
+/* This structure helps managing static strings. The basic usage goes like this:
+ Instead of doing
+
+ r = PyObject_CallMethod(o, "foo", "args", ...);
+
+ do
+
+ _Py_IDENTIFIER(foo);
+ ...
+ r = _PyObject_CallMethodId(o, &PyId_foo, "args", ...);
+
+ PyId_foo is a static variable, either on block level or file level. On first
+ usage, the string "foo" is interned, and the structures are linked. On interpreter
+ shutdown, all strings are released (through _PyUnicode_ClearStaticStrings).
+
+ Alternatively, _Py_static_string allows to choose the variable name.
+ _PyUnicode_FromId returns a borrowed reference to the interned string.
+ _PyObject_{Get,Set,Has}AttrId are __getattr__ versions using _Py_Identifier*.
+*/
+typedef struct _Py_Identifier {
+ struct _Py_Identifier *next;
+ const char* string;
+ PyObject *object;
+} _Py_Identifier;
+
+#define _Py_static_string(varname, value) static _Py_Identifier varname = { 0, value, 0 }
+#define _Py_IDENTIFIER(varname) _Py_static_string(PyId_##varname, #varname)
+
+/* Return an interned Unicode object for an Identifier; may fail if there is no memory.*/
+PyAPI_FUNC(PyObject*) _PyUnicode_FromId(_Py_Identifier*);
+/* Clear all static strings. */
+PyAPI_FUNC(void) _PyUnicode_ClearStaticStrings(void);
+
#ifdef __cplusplus
}
#endif
diff --git a/LICENSE b/LICENSE
index 48dbba7..3a37dd6 100644
--- a/LICENSE
+++ b/LICENSE
@@ -73,6 +73,7 @@ the various releases.
3.2 3.1 2011 PSF yes
3.2.1 3.2 2011 PSF yes
3.2.2 3.2.1 2011 PSF yes
+ 3.3 3.2 2012 PSF yes
Footnotes:
diff --git a/Lib/_dummy_thread.py b/Lib/_dummy_thread.py
index ed50520..13b1f26 100644
--- a/Lib/_dummy_thread.py
+++ b/Lib/_dummy_thread.py
@@ -24,11 +24,7 @@ TIMEOUT_MAX = 2**31
# imports are done when needed on a function-by-function basis. Since threads
# are disabled, the import lock should not be an issue anyway (??).
-class error(Exception):
- """Dummy implementation of _thread.error."""
-
- def __init__(self, *args):
- self.args = args
+error = RuntimeError
def start_new_thread(function, args, kwargs={}):
"""Dummy implementation of _thread.start_new_thread().
diff --git a/Lib/_pyio.py b/Lib/_pyio.py
index a2faeb3..fcd548d 100644
--- a/Lib/_pyio.py
+++ b/Lib/_pyio.py
@@ -15,7 +15,6 @@ except ImportError:
import io
from io import (__all__, SEEK_SET, SEEK_CUR, SEEK_END)
-from errno import EINTR
# open() uses st_blksize whenever we can
DEFAULT_BUFFER_SIZE = 8 * 1024 # bytes
@@ -24,20 +23,12 @@ DEFAULT_BUFFER_SIZE = 8 * 1024 # bytes
# defined in io.py. We don't use real inheritance though, because we don't
# want to inherit the C implementations.
-
-class BlockingIOError(IOError):
-
- """Exception raised when I/O would block on a non-blocking I/O stream."""
-
- def __init__(self, errno, strerror, characters_written=0):
- super().__init__(errno, strerror)
- if not isinstance(characters_written, int):
- raise TypeError("characters_written must be a integer")
- self.characters_written = characters_written
+# Rebind for compatibility
+BlockingIOError = BlockingIOError
def open(file, mode="r", buffering=-1, encoding=None, errors=None,
- newline=None, closefd=True):
+ newline=None, closefd=True, opener=None):
r"""Open file and return a stream. Raise IOError upon failure.
@@ -132,6 +123,12 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
be kept open when the file is closed. This does not work when a file name is
given and must be True in that case.
+ A custom opener can be used by passing a callable as *opener*. The
+ underlying file descriptor for the file object is then obtained by calling
+ *opener* with (*file*, *flags*). *opener* must return an open file
+ descriptor (passing os.open as *opener* results in functionality similar to
+ passing None).
+
open() returns a file object whose type depends on the mode, and
through which the standard file operations such as reading and writing
are performed. When open() is used to open a file in a text mode ('w',
@@ -186,7 +183,7 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
(writing and "w" or "") +
(appending and "a" or "") +
(updating and "+" or ""),
- closefd)
+ closefd, opener=opener)
line_buffering = False
if buffering == 1 or buffering < 0 and raw.isatty():
buffering = -1
@@ -948,15 +945,19 @@ class BufferedReader(_BufferedIOMixin):
# Special case for when the number of bytes to read is unspecified.
if n is None or n == -1:
self._reset_read_buf()
+ if hasattr(self.raw, 'readall'):
+ chunk = self.raw.readall()
+ if chunk is None:
+ return buf[pos:] or None
+ else:
+ return buf[pos:] + chunk
chunks = [buf[pos:]] # Strip the consumed bytes.
current_size = 0
while True:
# Read until EOF or until read() would block.
try:
chunk = self.raw.read()
- except IOError as e:
- if e.errno != EINTR:
- raise
+ except InterruptedError:
continue
if chunk in empty_values:
nodata_val = chunk
@@ -978,9 +979,7 @@ class BufferedReader(_BufferedIOMixin):
while avail < n:
try:
chunk = self.raw.read(wanted)
- except IOError as e:
- if e.errno != EINTR:
- raise
+ except InterruptedError:
continue
if chunk in empty_values:
nodata_val = chunk
@@ -1013,9 +1012,7 @@ class BufferedReader(_BufferedIOMixin):
while True:
try:
current = self.raw.read(to_read)
- except IOError as e:
- if e.errno != EINTR:
- raise
+ except InterruptedError:
continue
break
if current:
@@ -1120,13 +1117,11 @@ class BufferedWriter(_BufferedIOMixin):
while self._write_buf:
try:
n = self.raw.write(self._write_buf)
+ except InterruptedError:
+ continue
except BlockingIOError:
raise RuntimeError("self.raw should implement RawIOBase: it "
"should not raise BlockingIOError")
- except IOError as e:
- if e.errno != EINTR:
- raise
- continue
if n is None:
raise BlockingIOError(
errno.EAGAIN,
@@ -1515,6 +1510,7 @@ class TextIOWrapper(TextIOBase):
self._snapshot = None # info for reconstructing decoder state
self._seekable = self._telling = self.buffer.seekable()
self._has_read1 = hasattr(self.buffer, 'read1')
+ self._b2cratio = 0.0
if self._seekable and self.writable():
position = self.buffer.tell()
@@ -1685,7 +1681,12 @@ class TextIOWrapper(TextIOBase):
else:
input_chunk = self.buffer.read(self._CHUNK_SIZE)
eof = not input_chunk
- self._set_decoded_chars(self._decoder.decode(input_chunk, eof))
+ decoded_chars = self._decoder.decode(input_chunk, eof)
+ self._set_decoded_chars(decoded_chars)
+ if decoded_chars:
+ self._b2cratio = len(input_chunk) / len(self._decoded_chars)
+ else:
+ self._b2cratio = 0.0
if self._telling:
# At the snapshot point, len(dec_buffer) bytes before the read,
@@ -1739,20 +1740,56 @@ class TextIOWrapper(TextIOBase):
# forward until it gives us enough decoded characters.
saved_state = decoder.getstate()
try:
+ # Fast search for an acceptable start point, close to our
+ # current pos.
+ # Rationale: calling decoder.decode() has a large overhead
+ # regardless of chunk size; we want the number of such calls to
+ # be O(1) in most situations (common decoders, non-crazy input).
+ # Actually, it will be exactly 1 for fixed-size codecs (all
+ # 8-bit codecs, also UTF-16 and UTF-32).
+ skip_bytes = int(self._b2cratio * chars_to_skip)
+ skip_back = 1
+ assert skip_bytes <= len(next_input)
+ while skip_bytes > 0:
+ decoder.setstate((b'', dec_flags))
+ # Decode up to temptative start point
+ n = len(decoder.decode(next_input[:skip_bytes]))
+ if n <= chars_to_skip:
+ b, d = decoder.getstate()
+ if not b:
+ # Before pos and no bytes buffered in decoder => OK
+ dec_flags = d
+ chars_to_skip -= n
+ break
+ # Skip back by buffered amount and reset heuristic
+ skip_bytes -= len(b)
+ skip_back = 1
+ else:
+ # We're too far ahead, skip back a bit
+ skip_bytes -= skip_back
+ skip_back = skip_back * 2
+ else:
+ skip_bytes = 0
+ decoder.setstate((b'', dec_flags))
+
# Note our initial start point.
- decoder.setstate((b'', dec_flags))
- start_pos = position
- start_flags, bytes_fed, chars_decoded = dec_flags, 0, 0
- need_eof = 0
+ start_pos = position + skip_bytes
+ start_flags = dec_flags
+ if chars_to_skip == 0:
+ # We haven't moved from the start point.
+ return self._pack_cookie(start_pos, start_flags)
# Feed the decoder one byte at a time. As we go, note the
# nearest "safe start point" before the current location
# (a point where the decoder has nothing buffered, so seek()
# can safely start from there and advance to this location).
- next_byte = bytearray(1)
- for next_byte[0] in next_input:
+ bytes_fed = 0
+ need_eof = 0
+ # Chars decoded since `start_pos`
+ chars_decoded = 0
+ for i in range(skip_bytes, len(next_input)):
bytes_fed += 1
- chars_decoded += len(decoder.decode(next_byte))
+ chars_decoded += len(decoder.decode(next_input[i:i+1]))
dec_buffer, dec_flags = decoder.getstate()
if not dec_buffer and chars_decoded <= chars_to_skip:
# Decoder buffer is empty, so this is a safe start point.
diff --git a/Lib/abc.py b/Lib/abc.py
index a6c2dc4..09778e8 100644
--- a/Lib/abc.py
+++ b/Lib/abc.py
@@ -26,7 +26,8 @@ def abstractmethod(funcobj):
class abstractclassmethod(classmethod):
- """A decorator indicating abstract classmethods.
+ """
+ A decorator indicating abstract classmethods.
Similar to abstractmethod.
@@ -36,6 +37,9 @@ class abstractclassmethod(classmethod):
@abstractclassmethod
def my_abstract_classmethod(cls, ...):
...
+
+ 'abstractclassmethod' is deprecated. Use 'classmethod' with
+ 'abstractmethod' instead.
"""
__isabstractmethod__ = True
@@ -46,7 +50,8 @@ class abstractclassmethod(classmethod):
class abstractstaticmethod(staticmethod):
- """A decorator indicating abstract staticmethods.
+ """
+ A decorator indicating abstract staticmethods.
Similar to abstractmethod.
@@ -56,6 +61,9 @@ class abstractstaticmethod(staticmethod):
@abstractstaticmethod
def my_abstract_staticmethod(...):
...
+
+ 'abstractstaticmethod' is deprecated. Use 'staticmethod' with
+ 'abstractmethod' instead.
"""
__isabstractmethod__ = True
@@ -66,7 +74,8 @@ class abstractstaticmethod(staticmethod):
class abstractproperty(property):
- """A decorator indicating abstract properties.
+ """
+ A decorator indicating abstract properties.
Requires that the metaclass is ABCMeta or derived from it. A
class that has a metaclass derived from ABCMeta cannot be
@@ -88,7 +97,11 @@ class abstractproperty(property):
def getx(self): ...
def setx(self, value): ...
x = abstractproperty(getx, setx)
+
+ 'abstractproperty' is deprecated. Use 'property' with 'abstractmethod'
+ instead.
"""
+
__isabstractmethod__ = True
@@ -133,11 +146,14 @@ class ABCMeta(type):
return cls
def register(cls, subclass):
- """Register a virtual subclass of an ABC."""
+ """Register a virtual subclass of an ABC.
+
+ Returns the subclass, to allow usage as a class decorator.
+ """
if not isinstance(subclass, type):
raise TypeError("Can only register classes")
if issubclass(subclass, cls):
- return # Already a subclass
+ return subclass # Already a subclass
# Subtle: test for cycles *after* testing for "already a subclass";
# this means we allow X.register(X) and interpret it as a no-op.
if issubclass(cls, subclass):
@@ -145,6 +161,7 @@ class ABCMeta(type):
raise RuntimeError("Refusing to create an inheritance cycle")
cls._abc_registry.add(subclass)
ABCMeta._abc_invalidation_counter += 1 # Invalidate negative cache
+ return subclass
def _dump_registry(cls, file=None):
"""Debug helper to print the ABC registry."""
diff --git a/Lib/argparse.py b/Lib/argparse.py
index 2202b57..87d0cef 100644
--- a/Lib/argparse.py
+++ b/Lib/argparse.py
@@ -71,6 +71,7 @@ __all__ = [
'ArgumentDefaultsHelpFormatter',
'RawDescriptionHelpFormatter',
'RawTextHelpFormatter',
+ 'MetavarTypeHelpFormatter',
'Namespace',
'Action',
'ONE_OR_MORE',
@@ -419,7 +420,8 @@ class HelpFormatter(object):
# produce all arg strings
elif not action.option_strings:
- part = self._format_args(action, action.dest)
+ default = self._get_default_metavar_for_positional(action)
+ part = self._format_args(action, default)
# if it's in a group, strip the outer []
if action in group_actions:
@@ -441,7 +443,7 @@ class HelpFormatter(object):
# if the Optional takes a value, format is:
# -s ARGS or --long ARGS
else:
- default = action.dest.upper()
+ default = self._get_default_metavar_for_optional(action)
args_string = self._format_args(action, default)
part = '%s %s' % (option_string, args_string)
@@ -527,7 +529,8 @@ class HelpFormatter(object):
def _format_action_invocation(self, action):
if not action.option_strings:
- metavar, = self._metavar_formatter(action, action.dest)(1)
+ default = self._get_default_metavar_for_positional(action)
+ metavar, = self._metavar_formatter(action, default)(1)
return metavar
else:
@@ -541,7 +544,7 @@ class HelpFormatter(object):
# if the Optional takes a value, format is:
# -s ARGS, --long ARGS
else:
- default = action.dest.upper()
+ default = self._get_default_metavar_for_optional(action)
args_string = self._format_args(action, default)
for option_string in action.option_strings:
parts.append('%s %s' % (option_string, args_string))
@@ -619,6 +622,12 @@ class HelpFormatter(object):
def _get_help_string(self, action):
return action.help
+ def _get_default_metavar_for_optional(self, action):
+ return action.dest.upper()
+
+ def _get_default_metavar_for_positional(self, action):
+ return action.dest
+
class RawDescriptionHelpFormatter(HelpFormatter):
"""Help message formatter which retains any formatting in descriptions.
@@ -628,7 +637,7 @@ class RawDescriptionHelpFormatter(HelpFormatter):
"""
def _fill_text(self, text, width, indent):
- return ''.join([indent + line for line in text.splitlines(True)])
+ return ''.join(indent + line for line in text.splitlines(keepends=True))
class RawTextHelpFormatter(RawDescriptionHelpFormatter):
@@ -659,6 +668,22 @@ class ArgumentDefaultsHelpFormatter(HelpFormatter):
return help
+class MetavarTypeHelpFormatter(HelpFormatter):
+ """Help message formatter which uses the argument 'type' as the default
+ metavar value (instead of the argument 'dest')
+
+ Only the name of this class is considered a public API. All the methods
+ provided by the class are considered an implementation detail.
+ """
+
+ def _get_default_metavar_for_optional(self, action):
+ return action.type.__name__
+
+ def _get_default_metavar_for_positional(self, action):
+ return action.type.__name__
+
+
+
# =====================
# Options and Arguments
# =====================
@@ -1940,17 +1965,12 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
# if we didn't consume all the argument strings, there were extras
extras.extend(arg_strings[stop_index:])
- # if we didn't use all the Positional objects, there were too few
- # arg strings supplied.
- if positionals:
- self.error(_('too few arguments'))
-
# make sure all required actions were present
- for action in self._actions:
- if action.required:
- if action not in seen_actions:
- name = _get_action_name(action)
- self.error(_('argument %s is required') % name)
+ required_actions = [_get_action_name(action) for action in self._actions
+ if action.required and action not in seen_actions]
+ if required_actions:
+ self.error(_('the following arguments are required: %s') %
+ ', '.join(required_actions))
# make sure all required groups had one option present
for group in self._mutually_exclusive_groups:
diff --git a/Lib/ast.py b/Lib/ast.py
index fb5adac..13f59f9 100644
--- a/Lib/ast.py
+++ b/Lib/ast.py
@@ -25,7 +25,6 @@
:license: Python License.
"""
from _ast import *
-from _ast import __version__
def parse(source, filename='<unknown>', mode='exec'):
diff --git a/Lib/asynchat.py b/Lib/asynchat.py
index 6558512..2199d1b 100644
--- a/Lib/asynchat.py
+++ b/Lib/asynchat.py
@@ -75,7 +75,7 @@ class async_chat (asyncore.dispatcher):
# sign of an application bug that we don't want to pass silently
use_encoding = 0
- encoding = 'latin1'
+ encoding = 'latin-1'
def __init__ (self, sock=None, map=None):
# for string terminator matching
diff --git a/Lib/asyncore.py b/Lib/asyncore.py
index 7f42d39..920444d 100644
--- a/Lib/asyncore.py
+++ b/Lib/asyncore.py
@@ -54,7 +54,7 @@ import warnings
import os
from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, EINVAL, \
- ENOTCONN, ESHUTDOWN, EINTR, EISCONN, EBADF, ECONNABORTED, EPIPE, EAGAIN, \
+ ENOTCONN, ESHUTDOWN, EISCONN, EBADF, ECONNABORTED, EPIPE, EAGAIN, \
errorcode
_DISCONNECTED = frozenset((ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED, EPIPE,
@@ -143,11 +143,8 @@ def poll(timeout=0.0, map=None):
try:
r, w, e = select.select(r, w, e, timeout)
- except select.error as err:
- if err.args[0] != EINTR:
- raise
- else:
- return
+ except InterruptedError:
+ return
for fd in r:
obj = map.get(fd)
@@ -184,15 +181,10 @@ def poll2(timeout=0.0, map=None):
if obj.writable() and not obj.accepting:
flags |= select.POLLOUT
if flags:
- # Only check for exceptions if object was either readable
- # or writable.
- flags |= select.POLLERR | select.POLLHUP | select.POLLNVAL
pollster.register(fd, flags)
try:
r = pollster.poll(timeout)
- except select.error as err:
- if err.args[0] != EINTR:
- raise
+ except InterruptedError:
r = []
for fd, flags in r:
obj = map.get(fd)
@@ -291,7 +283,7 @@ class dispatcher:
del map[fd]
self._fileno = None
- def create_socket(self, family, type):
+ def create_socket(self, family=socket.AF_INET, type=socket.SOCK_STREAM):
self.family_and_type = family, type
sock = socket.socket(family, type)
sock.setblocking(0)
diff --git a/Lib/binhex.py b/Lib/binhex.py
index 999a675..7bf9278 100644
--- a/Lib/binhex.py
+++ b/Lib/binhex.py
@@ -23,7 +23,6 @@ hexbin(inputfilename, outputfilename)
#
import io
import os
-import sys
import struct
import binascii
diff --git a/Lib/bz2.py b/Lib/bz2.py
new file mode 100644
index 0000000..5c59a9e
--- /dev/null
+++ b/Lib/bz2.py
@@ -0,0 +1,413 @@
+"""Interface to the libbzip2 compression library.
+
+This module provides a file interface, classes for incremental
+(de)compression, and functions for one-shot (de)compression.
+"""
+
+__all__ = ["BZ2File", "BZ2Compressor", "BZ2Decompressor", "compress",
+ "decompress"]
+
+__author__ = "Nadeem Vawda <nadeem.vawda@gmail.com>"
+
+import io
+import threading
+import warnings
+
+from _bz2 import BZ2Compressor, BZ2Decompressor
+
+
+_MODE_CLOSED = 0
+_MODE_READ = 1
+_MODE_READ_EOF = 2
+_MODE_WRITE = 3
+
+_BUFFER_SIZE = 8192
+
+
+class BZ2File(io.BufferedIOBase):
+
+ """A file object providing transparent bzip2 (de)compression.
+
+ A BZ2File can act as a wrapper for an existing file object, or refer
+ directly to a named file on disk.
+
+ Note that BZ2File provides a *binary* file interface - data read is
+ returned as bytes, and data to be written should be given as bytes.
+ """
+
+ def __init__(self, filename=None, mode="r", buffering=None,
+ compresslevel=9, fileobj=None):
+ """Open a bzip2-compressed file.
+
+ If filename is given, open the named file. Otherwise, operate on
+ the file object given by fileobj. Exactly one of these two
+ parameters should be provided.
+
+ mode can be 'r' for reading (default), or 'w' for writing.
+
+ buffering is ignored. Its use is deprecated.
+
+ If mode is 'w', compresslevel can be a number between 1 and 9
+ specifying the level of compression: 1 produces the least
+ compression, and 9 (default) produces the most compression.
+ """
+ # This lock must be recursive, so that BufferedIOBase's
+ # readline(), readlines() and writelines() don't deadlock.
+ self._lock = threading.RLock()
+ self._fp = None
+ self._closefp = False
+ self._mode = _MODE_CLOSED
+ self._pos = 0
+ self._size = -1
+
+ if buffering is not None:
+ warnings.warn("Use of 'buffering' argument is deprecated",
+ DeprecationWarning)
+
+ if not (1 <= compresslevel <= 9):
+ raise ValueError("compresslevel must be between 1 and 9")
+
+ if mode in ("", "r", "rb"):
+ mode = "rb"
+ mode_code = _MODE_READ
+ self._decompressor = BZ2Decompressor()
+ self._buffer = None
+ elif mode in ("w", "wb"):
+ mode = "wb"
+ mode_code = _MODE_WRITE
+ self._compressor = BZ2Compressor(compresslevel)
+ elif mode in ("a", "ab"):
+ mode = "ab"
+ mode_code = _MODE_WRITE
+ self._compressor = BZ2Compressor(compresslevel)
+ else:
+ raise ValueError("Invalid mode: {!r}".format(mode))
+
+ if filename is not None and fileobj is None:
+ self._fp = open(filename, mode)
+ self._closefp = True
+ self._mode = mode_code
+ elif fileobj is not None and filename is None:
+ self._fp = fileobj
+ self._mode = mode_code
+ else:
+ raise ValueError("Must give exactly one of filename and fileobj")
+
+ def close(self):
+ """Flush and close the file.
+
+ May be called more than once without error. Once the file is
+ closed, any other operation on it will raise a ValueError.
+ """
+ with self._lock:
+ if self._mode == _MODE_CLOSED:
+ return
+ try:
+ if self._mode in (_MODE_READ, _MODE_READ_EOF):
+ self._decompressor = None
+ elif self._mode == _MODE_WRITE:
+ self._fp.write(self._compressor.flush())
+ self._compressor = None
+ finally:
+ try:
+ if self._closefp:
+ self._fp.close()
+ finally:
+ self._fp = None
+ self._closefp = False
+ self._mode = _MODE_CLOSED
+ self._buffer = None
+
+ @property
+ def closed(self):
+ """True if this file is closed."""
+ return self._mode == _MODE_CLOSED
+
+ def fileno(self):
+ """Return the file descriptor for the underlying file."""
+ self._check_not_closed()
+ return self._fp.fileno()
+
+ def seekable(self):
+ """Return whether the file supports seeking."""
+ return self.readable()
+
+ def readable(self):
+ """Return whether the file was opened for reading."""
+ self._check_not_closed()
+ return self._mode in (_MODE_READ, _MODE_READ_EOF)
+
+ def writable(self):
+ """Return whether the file was opened for writing."""
+ self._check_not_closed()
+ return self._mode == _MODE_WRITE
+
+ # Mode-checking helper functions.
+
+ def _check_not_closed(self):
+ if self.closed:
+ raise ValueError("I/O operation on closed file")
+
+ def _check_can_read(self):
+ if not self.readable():
+ raise io.UnsupportedOperation("File not open for reading")
+
+ def _check_can_write(self):
+ if not self.writable():
+ raise io.UnsupportedOperation("File not open for writing")
+
+ def _check_can_seek(self):
+ if not self.seekable():
+ raise io.UnsupportedOperation("Seeking is only supported "
+ "on files open for reading")
+
+ # Fill the readahead buffer if it is empty. Returns False on EOF.
+ def _fill_buffer(self):
+ if self._buffer:
+ return True
+
+ if self._decompressor.unused_data:
+ rawblock = self._decompressor.unused_data
+ else:
+ rawblock = self._fp.read(_BUFFER_SIZE)
+
+ if not rawblock:
+ if self._decompressor.eof:
+ self._mode = _MODE_READ_EOF
+ self._size = self._pos
+ return False
+ else:
+ raise EOFError("Compressed file ended before the "
+ "end-of-stream marker was reached")
+
+ # Continue to next stream.
+ if self._decompressor.eof:
+ self._decompressor = BZ2Decompressor()
+
+ self._buffer = self._decompressor.decompress(rawblock)
+ return True
+
+ # Read data until EOF.
+ # If return_data is false, consume the data without returning it.
+ def _read_all(self, return_data=True):
+ blocks = []
+ while self._fill_buffer():
+ if return_data:
+ blocks.append(self._buffer)
+ self._pos += len(self._buffer)
+ self._buffer = None
+ if return_data:
+ return b"".join(blocks)
+
+ # Read a block of up to n bytes.
+ # If return_data is false, consume the data without returning it.
+ def _read_block(self, n, return_data=True):
+ blocks = []
+ while n > 0 and self._fill_buffer():
+ if n < len(self._buffer):
+ data = self._buffer[:n]
+ self._buffer = self._buffer[n:]
+ else:
+ data = self._buffer
+ self._buffer = None
+ if return_data:
+ blocks.append(data)
+ self._pos += len(data)
+ n -= len(data)
+ if return_data:
+ return b"".join(blocks)
+
+ def peek(self, n=0):
+ """Return buffered data without advancing the file position.
+
+ Always returns at least one byte of data, unless at EOF.
+ The exact number of bytes returned is unspecified.
+ """
+ with self._lock:
+ self._check_can_read()
+ if self._mode == _MODE_READ_EOF or not self._fill_buffer():
+ return b""
+ return self._buffer
+
+ def read(self, size=-1):
+ """Read up to size uncompressed bytes from the file.
+
+ If size is negative or omitted, read until EOF is reached.
+ Returns b'' if the file is already at EOF.
+ """
+ with self._lock:
+ self._check_can_read()
+ if self._mode == _MODE_READ_EOF or size == 0:
+ return b""
+ elif size < 0:
+ return self._read_all()
+ else:
+ return self._read_block(size)
+
+ def read1(self, size=-1):
+ """Read up to size uncompressed bytes with at most one read
+ from the underlying stream.
+
+ Returns b'' if the file is at EOF.
+ """
+ with self._lock:
+ self._check_can_read()
+ if (size == 0 or self._mode == _MODE_READ_EOF or
+ not self._fill_buffer()):
+ return b""
+ if 0 < size < len(self._buffer):
+ data = self._buffer[:size]
+ self._buffer = self._buffer[size:]
+ else:
+ data = self._buffer
+ self._buffer = None
+ self._pos += len(data)
+ return data
+
+ def readinto(self, b):
+ """Read up to len(b) bytes into b.
+
+ Returns the number of bytes read (0 for EOF).
+ """
+ with self._lock:
+ return io.BufferedIOBase.readinto(self, b)
+
+ def readline(self, size=-1):
+ """Read a line of uncompressed bytes from the file.
+
+ The terminating newline (if present) is retained. If size is
+ non-negative, no more than size bytes will be read (in which
+ case the line may be incomplete). Returns b'' if already at EOF.
+ """
+ if not hasattr(size, "__index__"):
+ raise TypeError("Integer argument expected")
+ size = size.__index__()
+ with self._lock:
+ return io.BufferedIOBase.readline(self, size)
+
+ def readlines(self, size=-1):
+ """Read a list of lines of uncompressed bytes from the file.
+
+ size can be specified to control the number of lines read: no
+ further lines will be read once the total size of the lines read
+ so far equals or exceeds size.
+ """
+ if not hasattr(size, "__index__"):
+ raise TypeError("Integer argument expected")
+ size = size.__index__()
+ with self._lock:
+ return io.BufferedIOBase.readlines(self, size)
+
+ def write(self, data):
+ """Write a byte string to the file.
+
+ Returns the number of uncompressed bytes written, which is
+ always len(data). Note that due to buffering, the file on disk
+ may not reflect the data written until close() is called.
+ """
+ with self._lock:
+ self._check_can_write()
+ compressed = self._compressor.compress(data)
+ self._fp.write(compressed)
+ self._pos += len(data)
+ return len(data)
+
+ def writelines(self, seq):
+ """Write a sequence of byte strings to the file.
+
+ Returns the number of uncompressed bytes written.
+ seq can be any iterable yielding byte strings.
+
+ Line separators are not added between the written byte strings.
+ """
+ with self._lock:
+ return io.BufferedIOBase.writelines(self, seq)
+
+ # Rewind the file to the beginning of the data stream.
+ def _rewind(self):
+ self._fp.seek(0, 0)
+ self._mode = _MODE_READ
+ self._pos = 0
+ self._decompressor = BZ2Decompressor()
+ self._buffer = None
+
+ def seek(self, offset, whence=0):
+ """Change the file position.
+
+ The new position is specified by offset, relative to the
+ position indicated by whence. Values for whence are:
+
+ 0: start of stream (default); offset must not be negative
+ 1: current stream position
+ 2: end of stream; offset must not be positive
+
+ Returns the new file position.
+
+ Note that seeking is emulated, so depending on the parameters,
+ this operation may be extremely slow.
+ """
+ with self._lock:
+ self._check_can_seek()
+
+ # Recalculate offset as an absolute file position.
+ if whence == 0:
+ pass
+ elif whence == 1:
+ offset = self._pos + offset
+ elif whence == 2:
+ # Seeking relative to EOF - we need to know the file's size.
+ if self._size < 0:
+ self._read_all(return_data=False)
+ offset = self._size + offset
+ else:
+ raise ValueError("Invalid value for whence: {}".format(whence))
+
+ # Make it so that offset is the number of bytes to skip forward.
+ if offset < self._pos:
+ self._rewind()
+ else:
+ offset -= self._pos
+
+ # Read and discard data until we reach the desired position.
+ if self._mode != _MODE_READ_EOF:
+ self._read_block(offset, return_data=False)
+
+ return self._pos
+
+ def tell(self):
+ """Return the current file position."""
+ with self._lock:
+ self._check_not_closed()
+ return self._pos
+
+
+def compress(data, compresslevel=9):
+ """Compress a block of data.
+
+ compresslevel, if given, must be a number between 1 and 9.
+
+ For incremental compression, use a BZ2Compressor object instead.
+ """
+ comp = BZ2Compressor(compresslevel)
+ return comp.compress(data) + comp.flush()
+
+
+def decompress(data):
+ """Decompress a block of data.
+
+ For incremental decompression, use a BZ2Decompressor object instead.
+ """
+ if len(data) == 0:
+ return b""
+
+ results = []
+ while True:
+ decomp = BZ2Decompressor()
+ results.append(decomp.decompress(data))
+ if not decomp.eof:
+ raise ValueError("Compressed data ended before the "
+ "end-of-stream marker was reached")
+ if not decomp.unused_data:
+ return b"".join(results)
+ # There is unused data left over. Proceed to next stream.
+ data = decomp.unused_data
diff --git a/Lib/cgi.py b/Lib/cgi.py
index 90a3345..e964f0c 100755
--- a/Lib/cgi.py
+++ b/Lib/cgi.py
@@ -76,7 +76,7 @@ def initlog(*allargs):
send an error message).
"""
- global logfp, log
+ global log, logfile, logfp
if logfile and not logfp:
try:
logfp = open(logfile, "a")
@@ -96,6 +96,15 @@ def nolog(*allargs):
"""Dummy function, assigned to log when logging is disabled."""
pass
+def closelog():
+ """Close the log file."""
+ global log, logfile, logfp
+ logfile = ''
+ if logfp:
+ logfp.close()
+ logfp = None
+ log = initlog
+
log = initlog # The current logging function
@@ -1003,7 +1012,7 @@ environment as well. Here are some common variable names:
def escape(s, quote=None):
"""Deprecated API."""
warn("cgi.escape is deprecated, use html.escape instead",
- PendingDeprecationWarning, stacklevel=2)
+ DeprecationWarning, stacklevel=2)
s = s.replace("&", "&amp;") # Must be done first!
s = s.replace("<", "&lt;")
s = s.replace(">", "&gt;")
diff --git a/Lib/cgitb.py b/Lib/cgitb.py
index 7b52c8e..e3ce2cb 100644
--- a/Lib/cgitb.py
+++ b/Lib/cgitb.py
@@ -31,7 +31,6 @@ import tempfile
import time
import tokenize
import traceback
-import types
def reset():
"""Return a string that resets the CGI and browser to a known state."""
diff --git a/Lib/codecs.py b/Lib/codecs.py
index b150d64..e63a0c6 100644
--- a/Lib/codecs.py
+++ b/Lib/codecs.py
@@ -484,7 +484,7 @@ class StreamReader(Codec):
if firstline:
newchars, decodedbytes = \
self.decode(data[:exc.start], self.errors)
- lines = newchars.splitlines(True)
+ lines = newchars.splitlines(keepends=True)
if len(lines)<=1:
raise
else:
@@ -526,7 +526,7 @@ class StreamReader(Codec):
self.charbuffer = self.linebuffer[0]
self.linebuffer = None
if not keepends:
- line = line.splitlines(False)[0]
+ line = line.splitlines(keepends=False)[0]
return line
readsize = size or 72
@@ -543,7 +543,7 @@ class StreamReader(Codec):
data += self.read(size=1, chars=1)
line += data
- lines = line.splitlines(True)
+ lines = line.splitlines(keepends=True)
if lines:
if len(lines) > 1:
# More than one line result; the first line is a full line
@@ -559,10 +559,10 @@ class StreamReader(Codec):
# only one remaining line, put it back into charbuffer
self.charbuffer = lines[0] + self.charbuffer
if not keepends:
- line = line.splitlines(False)[0]
+ line = line.splitlines(keepends=False)[0]
break
line0withend = lines[0]
- line0withoutend = lines[0].splitlines(False)[0]
+ line0withoutend = lines[0].splitlines(keepends=False)[0]
if line0withend != line0withoutend: # We really have a line end
# Put the rest back together and keep it until the next call
self.charbuffer = self._empty_charbuffer.join(lines[1:]) + \
@@ -575,7 +575,7 @@ class StreamReader(Codec):
# we didn't get anything or this was our only try
if not data or size is not None:
if line and not keepends:
- line = line.splitlines(False)[0]
+ line = line.splitlines(keepends=False)[0]
break
if readsize < 8000:
readsize *= 2
@@ -803,7 +803,7 @@ class StreamRecoder:
data = self.reader.read()
data, bytesencoded = self.encode(data, self.errors)
- return data.splitlines(1)
+ return data.splitlines(keepends=True)
def __next__(self):
diff --git a/Lib/collections.py b/Lib/collections/__init__.py
index d2625fe..6cae1dd 100644
--- a/Lib/collections.py
+++ b/Lib/collections/__init__.py
@@ -1,10 +1,11 @@
__all__ = ['deque', 'defaultdict', 'namedtuple', 'UserDict', 'UserList',
- 'UserString', 'Counter', 'OrderedDict']
-# For bootstrapping reasons, the collection ABCs are defined in _abcoll.py.
-# They should however be considered an integral part of collections.py.
-from _abcoll import *
-import _abcoll
-__all__ += _abcoll.__all__
+ 'UserString', 'Counter', 'OrderedDict', 'ChainMap']
+
+# For backwards compatibility, continue to make the collections ABCs
+# available through the collections module.
+from collections.abc import *
+import collections.abc
+__all__ += collections.abc.__all__
from _collections import deque, defaultdict
from operator import itemgetter as _itemgetter
@@ -364,8 +365,9 @@ def namedtuple(typename, field_names, verbose=False, rename=False):
except SyntaxError as e:
raise SyntaxError(e.msg + ':\n\n' + class_definition)
result = namespace[typename]
+ result._source = class_definition
if verbose:
- print(class_definition)
+ print(result._source)
# For pickling to work, the __module__ variable needs to be set to the frame
# where the named tuple is created. Bypass this step in enviroments where
@@ -674,12 +676,86 @@ class Counter(dict):
result[elem] = newcount
return result
+ def __pos__(self):
+ 'Adds an empty counter, effectively stripping negative and zero counts'
+ return self + Counter()
+
+ def __neg__(self):
+ '''Subtracts from an empty counter. Strips positive and zero counts,
+ and flips the sign on negative counts.
+
+ '''
+ return Counter() - self
+
+ def _keep_positive(self):
+ '''Internal method to strip elements with a negative or zero count'''
+ nonpositive = [elem for elem, count in self.items() if not count > 0]
+ for elem in nonpositive:
+ del self[elem]
+ return self
+
+ def __iadd__(self, other):
+ '''Inplace add from another counter, keeping only positive counts.
+
+ >>> c = Counter('abbb')
+ >>> c += Counter('bcc')
+ >>> c
+ Counter({'b': 4, 'c': 2, 'a': 1})
+
+ '''
+ for elem, count in other.items():
+ self[elem] += count
+ return self._keep_positive()
+
+ def __isub__(self, other):
+ '''Inplace subtract counter, but keep only results with positive counts.
+
+ >>> c = Counter('abbbc')
+ >>> c -= Counter('bccd')
+ >>> c
+ Counter({'b': 2, 'a': 1})
+
+ '''
+ for elem, count in other.items():
+ self[elem] -= count
+ return self._keep_positive()
+
+ def __ior__(self, other):
+ '''Inplace union is the maximum of value from either counter.
+
+ >>> c = Counter('abbb')
+ >>> c |= Counter('bcc')
+ >>> c
+ Counter({'b': 3, 'c': 2, 'a': 1})
+
+ '''
+ for elem, other_count in other.items():
+ count = self[elem]
+ if other_count > count:
+ self[elem] = other_count
+ return self._keep_positive()
+
+ def __iand__(self, other):
+ '''Inplace intersection is the minimum of corresponding counts.
+
+ >>> c = Counter('abbb')
+ >>> c &= Counter('bcc')
+ >>> c
+ Counter({'b': 1})
+
+ '''
+ for elem, count in self.items():
+ other_count = other[elem]
+ if other_count < count:
+ self[elem] = other_count
+ return self._keep_positive()
+
########################################################################
-### ChainMap (helper for configparser)
+### ChainMap (helper for configparser and string.Template)
########################################################################
-class _ChainMap(MutableMapping):
+class ChainMap(MutableMapping):
''' A ChainMap groups multiple dicts (or other mappings) together
to create a single, updateable view.
@@ -890,6 +966,8 @@ class UserList(MutableSequence):
def insert(self, i, item): self.data.insert(i, item)
def pop(self, i=-1): return self.data.pop(i)
def remove(self, item): self.data.remove(item)
+ def clear(self): self.data.clear()
+ def copy(self): return self.__class__(self)
def count(self, item): return self.data.count(item)
def index(self, item, *args): return self.data.index(item, *args)
def reverse(self): self.data.reverse()
@@ -1034,7 +1112,7 @@ class UserString(Sequence):
return self.data.split(sep, maxsplit)
def rsplit(self, sep=None, maxsplit=-1):
return self.data.rsplit(sep, maxsplit)
- def splitlines(self, keepends=0): return self.data.splitlines(keepends)
+ def splitlines(self, keepends=False): return self.data.splitlines(keepends)
def startswith(self, prefix, start=0, end=_sys.maxsize):
return self.data.startswith(prefix, start, end)
def strip(self, chars=None): return self.__class__(self.data.strip(chars))
diff --git a/Lib/_abcoll.py b/Lib/collections/abc.py
index 2417d18..7fbe84d 100644
--- a/Lib/_abcoll.py
+++ b/Lib/collections/abc.py
@@ -3,9 +3,7 @@
"""Abstract Base Classes (ABCs) for collections, according to PEP 3119.
-DON'T USE THIS MODULE DIRECTLY! The classes here should be imported
-via collections; they are defined here only to alleviate certain
-bootstrapping issues. Unit tests are in test_collections.
+Unit tests are in test_collections.
"""
from abc import ABCMeta, abstractmethod
@@ -48,6 +46,8 @@ dict_proxy = type(type.__dict__)
class Hashable(metaclass=ABCMeta):
+ __slots__ = ()
+
@abstractmethod
def __hash__(self):
return 0
@@ -65,6 +65,8 @@ class Hashable(metaclass=ABCMeta):
class Iterable(metaclass=ABCMeta):
+ __slots__ = ()
+
@abstractmethod
def __iter__(self):
while False:
@@ -80,6 +82,8 @@ class Iterable(metaclass=ABCMeta):
class Iterator(Iterable):
+ __slots__ = ()
+
@abstractmethod
def __next__(self):
raise StopIteration
@@ -111,6 +115,8 @@ Iterator.register(zip_iterator)
class Sized(metaclass=ABCMeta):
+ __slots__ = ()
+
@abstractmethod
def __len__(self):
return 0
@@ -125,6 +131,8 @@ class Sized(metaclass=ABCMeta):
class Container(metaclass=ABCMeta):
+ __slots__ = ()
+
@abstractmethod
def __contains__(self, x):
return False
@@ -139,6 +147,8 @@ class Container(metaclass=ABCMeta):
class Callable(metaclass=ABCMeta):
+ __slots__ = ()
+
@abstractmethod
def __call__(self, *args, **kwds):
return False
@@ -166,6 +176,8 @@ class Set(Sized, Iterable, Container):
then the other operations will automatically follow suit.
"""
+ __slots__ = ()
+
def __le__(self, other):
if not isinstance(other, Set):
return NotImplemented
@@ -277,6 +289,8 @@ Set.register(frozenset)
class MutableSet(Set):
+ __slots__ = ()
+
@abstractmethod
def add(self, value):
"""Add an element."""
@@ -350,6 +364,8 @@ MutableSet.register(set)
class Mapping(Sized, Iterable, Container):
+ __slots__ = ()
+
@abstractmethod
def __getitem__(self, key):
raise KeyError
@@ -453,6 +469,8 @@ ValuesView.register(dict_values)
class MutableMapping(Mapping):
+ __slots__ = ()
+
@abstractmethod
def __setitem__(self, key, value):
raise KeyError
@@ -532,6 +550,8 @@ class Sequence(Sized, Iterable, Container):
__getitem__, and __len__.
"""
+ __slots__ = ()
+
@abstractmethod
def __getitem__(self, index):
raise IndexError
@@ -577,12 +597,16 @@ class ByteString(Sequence):
XXX Should add all their methods.
"""
+ __slots__ = ()
+
ByteString.register(bytes)
ByteString.register(bytearray)
class MutableSequence(Sequence):
+ __slots__ = ()
+
@abstractmethod
def __setitem__(self, index, value):
raise IndexError
@@ -598,6 +622,13 @@ class MutableSequence(Sequence):
def append(self, value):
self.insert(len(self), value)
+ def clear(self):
+ try:
+ while True:
+ self.pop()
+ except IndexError:
+ pass
+
def reverse(self):
n = len(self)
for i in range(n//2):
diff --git a/Lib/concurrent/futures/_base.py b/Lib/concurrent/futures/_base.py
index 79b91d4..3b097b5 100644
--- a/Lib/concurrent/futures/_base.py
+++ b/Lib/concurrent/futures/_base.py
@@ -4,7 +4,6 @@
__author__ = 'Brian Quinlan (brian@sweetapp.com)'
import collections
-import functools
import logging
import threading
import time
@@ -536,15 +535,19 @@ class Executor(object):
fs = [self.submit(fn, *args) for args in zip(*iterables)]
- try:
- for future in fs:
- if timeout is None:
- yield future.result()
- else:
- yield future.result(end_time - time.time())
- finally:
- for future in fs:
- future.cancel()
+ # Yield must be hidden in closure so that the futures are submitted
+ # before the first iterator value is required.
+ def result_iterator():
+ try:
+ for future in fs:
+ if timeout is None:
+ yield future.result()
+ else:
+ yield future.result(end_time - time.time())
+ finally:
+ for future in fs:
+ future.cancel()
+ return result_iterator()
def shutdown(self, wait=True):
"""Clean-up the resources associated with the Executor.
diff --git a/Lib/concurrent/futures/process.py b/Lib/concurrent/futures/process.py
index 8082940..0575146 100644
--- a/Lib/concurrent/futures/process.py
+++ b/Lib/concurrent/futures/process.py
@@ -46,9 +46,11 @@ Process #1..n:
__author__ = 'Brian Quinlan (brian@sweetapp.com)'
import atexit
+import os
from concurrent.futures import _base
import queue
import multiprocessing
+from multiprocessing.queues import SimpleQueue, SentinelReady, Full
import threading
import weakref
@@ -121,7 +123,7 @@ def _process_worker(call_queue, result_queue):
call_item = call_queue.get(block=True)
if call_item is None:
# Wake up queue management thread
- result_queue.put(None)
+ result_queue.put(os.getpid())
return
try:
r = call_item.fn(*call_item.args, **call_item.kwargs)
@@ -193,51 +195,92 @@ def _queue_management_worker(executor_reference,
result_queue: A multiprocessing.Queue of _ResultItems generated by the
process workers.
"""
- nb_shutdown_processes = 0
- def shutdown_one_process():
- """Tell a worker to terminate, which will in turn wake us again"""
- nonlocal nb_shutdown_processes
- call_queue.put(None)
- nb_shutdown_processes += 1
+ executor = None
+
+ def shutting_down():
+ return _shutdown or executor is None or executor._shutdown_thread
+
+ def shutdown_worker():
+ # This is an upper bound
+ nb_children_alive = sum(p.is_alive() for p in processes.values())
+ for i in range(0, nb_children_alive):
+ call_queue.put_nowait(None)
+ # Release the queue's resources as soon as possible.
+ call_queue.close()
+ # If .join() is not called on the created processes then
+ # some multiprocessing.Queue methods may deadlock on Mac OS X.
+ for p in processes.values():
+ p.join()
+
while True:
_add_call_item_to_queue(pending_work_items,
work_ids_queue,
call_queue)
- result_item = result_queue.get(block=True)
- if result_item is not None:
- work_item = pending_work_items[result_item.work_id]
- del pending_work_items[result_item.work_id]
-
- if result_item.exception:
- work_item.future.set_exception(result_item.exception)
- else:
- work_item.future.set_result(result_item.result)
- continue
- # If we come here, we either got a timeout or were explicitly woken up.
- # In either case, check whether we should start shutting down.
+ sentinels = [p.sentinel for p in processes.values()]
+ assert sentinels
+ try:
+ result_item = result_queue.get(sentinels=sentinels)
+ except SentinelReady:
+ # Mark the process pool broken so that submits fail right now.
+ executor = executor_reference()
+ if executor is not None:
+ executor._broken = True
+ executor._shutdown_thread = True
+ executor = None
+ # All futures in flight must be marked failed
+ for work_id, work_item in pending_work_items.items():
+ work_item.future.set_exception(
+ BrokenProcessPool(
+ "A process in the process pool was "
+ "terminated abruptly while the future was "
+ "running or pending."
+ ))
+ pending_work_items.clear()
+ # Terminate remaining workers forcibly: the queues or their
+ # locks may be in a dirty state and block forever.
+ for p in processes.values():
+ p.terminate()
+ shutdown_worker()
+ return
+ if isinstance(result_item, int):
+ # Clean shutdown of a worker using its PID
+ # (avoids marking the executor broken)
+ assert shutting_down()
+ p = processes.pop(result_item)
+ p.join()
+ if not processes:
+ shutdown_worker()
+ return
+ elif result_item is not None:
+ work_item = pending_work_items.pop(result_item.work_id, None)
+ # work_item can be None if another process terminated (see above)
+ if work_item is not None:
+ if result_item.exception:
+ work_item.future.set_exception(result_item.exception)
+ else:
+ work_item.future.set_result(result_item.result)
+ # Check whether we should start shutting down.
executor = executor_reference()
# No more work items can be added if:
# - The interpreter is shutting down OR
# - The executor that owns this worker has been collected OR
# - The executor that owns this worker has been shutdown.
- if _shutdown or executor is None or executor._shutdown_thread:
- # Since no new work items can be added, it is safe to shutdown
- # this thread if there are no pending work items.
- if not pending_work_items:
- while nb_shutdown_processes < len(processes):
- shutdown_one_process()
- # If .join() is not called on the created processes then
- # some multiprocessing.Queue methods may deadlock on Mac OS
- # X.
- for p in processes:
- p.join()
- call_queue.close()
- return
- else:
- # Start shutting down by telling a process it can exit.
- shutdown_one_process()
- del executor
+ if shutting_down():
+ try:
+ # Since no new work items can be added, it is safe to shutdown
+ # this thread if there are no pending work items.
+ if not pending_work_items:
+ shutdown_worker()
+ return
+ else:
+ # Start shutting down by telling a process it can exit.
+ call_queue.put_nowait(None)
+ except Full:
+ # This is not a problem: we will eventually be woken up (in
+ # result_queue.get()) and be able to send a sentinel again.
+ pass
+ executor = None
_system_limits_checked = False
_system_limited = None
@@ -248,7 +291,6 @@ def _check_system_limits():
raise NotImplementedError(_system_limited)
_system_limits_checked = True
try:
- import os
nsems_max = os.sysconf("SC_SEM_NSEMS_MAX")
except (AttributeError, ValueError):
# sysconf not available or setting not available
@@ -264,6 +306,14 @@ def _check_system_limits():
_system_limited = "system provides too few semaphores (%d available, 256 necessary)" % nsems_max
raise NotImplementedError(_system_limited)
+
+class BrokenProcessPool(RuntimeError):
+ """
+ Raised when a process in a ProcessPoolExecutor terminated abruptly
+ while a future was in the running state.
+ """
+
+
class ProcessPoolExecutor(_base.Executor):
def __init__(self, max_workers=None):
"""Initializes a new ProcessPoolExecutor instance.
@@ -285,14 +335,20 @@ class ProcessPoolExecutor(_base.Executor):
# because futures in the call queue cannot be cancelled.
self._call_queue = multiprocessing.Queue(self._max_workers +
EXTRA_QUEUED_CALLS)
- self._result_queue = multiprocessing.Queue()
+ # Killed worker processes can produce spurious "broken pipe"
+ # tracebacks in the queue's own worker thread. But we detect killed
+ # processes anyway, so silence the tracebacks.
+ self._call_queue._ignore_epipe = True
+ self._result_queue = SimpleQueue()
self._work_ids = queue.Queue()
self._queue_management_thread = None
- self._processes = set()
+ # Map of pids to processes
+ self._processes = {}
# Shutdown is a two-step process.
self._shutdown_thread = False
self._shutdown_lock = threading.Lock()
+ self._broken = False
self._queue_count = 0
self._pending_work_items = {}
@@ -302,6 +358,8 @@ class ProcessPoolExecutor(_base.Executor):
def weakref_cb(_, q=self._result_queue):
q.put(None)
if self._queue_management_thread is None:
+ # Start the processes so that their sentinels are known.
+ self._adjust_process_count()
self._queue_management_thread = threading.Thread(
target=_queue_management_worker,
args=(weakref.ref(self, weakref_cb),
@@ -321,10 +379,13 @@ class ProcessPoolExecutor(_base.Executor):
args=(self._call_queue,
self._result_queue))
p.start()
- self._processes.add(p)
+ self._processes[p.pid] = p
def submit(self, fn, *args, **kwargs):
with self._shutdown_lock:
+ if self._broken:
+ raise BrokenProcessPool('A child process terminated '
+ 'abruptly, the process pool is not usable anymore')
if self._shutdown_thread:
raise RuntimeError('cannot schedule new futures after shutdown')
@@ -338,7 +399,6 @@ class ProcessPoolExecutor(_base.Executor):
self._result_queue.put(None)
self._start_queue_management_thread()
- self._adjust_process_count()
return f
submit.__doc__ = _base.Executor.submit.__doc__
diff --git a/Lib/concurrent/futures/thread.py b/Lib/concurrent/futures/thread.py
index fbac088..95bb682 100644
--- a/Lib/concurrent/futures/thread.py
+++ b/Lib/concurrent/futures/thread.py
@@ -74,7 +74,7 @@ def _worker(executor_reference, work_queue):
work_queue.put(None)
return
del executor
- except BaseException as e:
+ except BaseException:
_base.LOGGER.critical('Exception in worker', exc_info=True)
class ThreadPoolExecutor(_base.Executor):
diff --git a/Lib/configparser.py b/Lib/configparser.py
index d148b88..7bc4398 100644
--- a/Lib/configparser.py
+++ b/Lib/configparser.py
@@ -119,7 +119,8 @@ ConfigParser -- responsible for parsing a list of
between keys and values are surrounded by spaces.
"""
-from collections import MutableMapping, OrderedDict as _default_dict, _ChainMap
+from collections.abc import MutableMapping
+from collections import OrderedDict as _default_dict, ChainMap as _ChainMap
import functools
import io
import itertools
diff --git a/Lib/contextlib.py b/Lib/contextlib.py
index 5ebbbc6..2f8f00d 100644
--- a/Lib/contextlib.py
+++ b/Lib/contextlib.py
@@ -2,7 +2,6 @@
import sys
from functools import wraps
-from warnings import warn
__all__ = ["contextmanager", "closing", "ContextDecorator"]
diff --git a/Lib/copy.py b/Lib/copy.py
index 089d101..d96201e 100644
--- a/Lib/copy.py
+++ b/Lib/copy.py
@@ -173,8 +173,10 @@ def deepcopy(x, memo=None, _nil=[]):
"un(deep)copyable object of type %s" % cls)
y = _reconstruct(x, rv, 1, memo)
- memo[d] = y
- _keep_alive(x, memo) # Make sure x lives at least as long as d
+ # If is its own copy, don't memoize.
+ if y is not x:
+ memo[d] = y
+ _keep_alive(x, memo) # Make sure x lives at least as long as d
return y
_deepcopy_dispatch = d = {}
@@ -214,9 +216,10 @@ def _deepcopy_tuple(x, memo):
y = []
for a in x:
y.append(deepcopy(a, memo))
- d = id(x)
+ # We're not going to put the tuple in the memo, but it's still important we
+ # check for it, in case the tuple contains recursive mutable structures.
try:
- return memo[d]
+ return memo[id(x)]
except KeyError:
pass
for i in range(len(x)):
@@ -225,7 +228,6 @@ def _deepcopy_tuple(x, memo):
break
else:
y = x
- memo[d] = y
return y
d[tuple] = _deepcopy_tuple
@@ -321,68 +323,3 @@ del types
# Helper for instance creation without calling __init__
class _EmptyClass:
pass
-
-def _test():
- l = [None, 1, 2, 3.14, 'xyzzy', (1, 2), [3.14, 'abc'],
- {'abc': 'ABC'}, (), [], {}]
- l1 = copy(l)
- print(l1==l)
- l1 = map(copy, l)
- print(l1==l)
- l1 = deepcopy(l)
- print(l1==l)
- class C:
- def __init__(self, arg=None):
- self.a = 1
- self.arg = arg
- if __name__ == '__main__':
- import sys
- file = sys.argv[0]
- else:
- file = __file__
- self.fp = open(file)
- self.fp.close()
- def __getstate__(self):
- return {'a': self.a, 'arg': self.arg}
- def __setstate__(self, state):
- for key, value in state.items():
- setattr(self, key, value)
- def __deepcopy__(self, memo=None):
- new = self.__class__(deepcopy(self.arg, memo))
- new.a = self.a
- return new
- c = C('argument sketch')
- l.append(c)
- l2 = copy(l)
- print(l == l2)
- print(l)
- print(l2)
- l2 = deepcopy(l)
- print(l == l2)
- print(l)
- print(l2)
- l.append({l[1]: l, 'xyz': l[2]})
- l3 = copy(l)
- import reprlib
- print(map(reprlib.repr, l))
- print(map(reprlib.repr, l1))
- print(map(reprlib.repr, l2))
- print(map(reprlib.repr, l3))
- l3 = deepcopy(l)
- print(map(reprlib.repr, l))
- print(map(reprlib.repr, l1))
- print(map(reprlib.repr, l2))
- print(map(reprlib.repr, l3))
- class odict(dict):
- def __init__(self, d = {}):
- self.a = 99
- dict.__init__(self, d)
- def __setitem__(self, k, i):
- dict.__setitem__(self, k, i)
- self.a
- o = odict({"A" : "B"})
- x = deepcopy(o)
- print(o, x)
-
-if __name__ == '__main__':
- _test()
diff --git a/Lib/crypt.py b/Lib/crypt.py
new file mode 100644
index 0000000..e65b0cb
--- /dev/null
+++ b/Lib/crypt.py
@@ -0,0 +1,62 @@
+"""Wrapper to the POSIX crypt library call and associated functionality."""
+
+import _crypt
+import string
+from random import choice
+from collections import namedtuple
+
+
+_saltchars = string.ascii_letters + string.digits + './'
+
+
+class _Method(namedtuple('_Method', 'name ident salt_chars total_size')):
+
+ """Class representing a salt method per the Modular Crypt Format or the
+ legacy 2-character crypt method."""
+
+ def __repr__(self):
+ return '<crypt.METHOD_{}>'.format(self.name)
+
+
+
+def mksalt(method=None):
+ """Generate a salt for the specified method.
+
+ If not specified, the strongest available method will be used.
+
+ """
+ if method is None:
+ method = methods[0]
+ s = '${}$'.format(method.ident) if method.ident else ''
+ s += ''.join(choice(_saltchars) for _ in range(method.salt_chars))
+ return s
+
+
+def crypt(word, salt=None):
+ """Return a string representing the one-way hash of a password, with a salt
+ prepended.
+
+ If ``salt`` is not specified or is ``None``, the strongest
+ available method will be selected and a salt generated. Otherwise,
+ ``salt`` may be one of the ``crypt.METHOD_*`` values, or a string as
+ returned by ``crypt.mksalt()``.
+
+ """
+ if salt is None or isinstance(salt, _Method):
+ salt = mksalt(salt)
+ return _crypt.crypt(word, salt)
+
+
+# available salting/crypto methods
+METHOD_CRYPT = _Method('CRYPT', None, 2, 13)
+METHOD_MD5 = _Method('MD5', '1', 8, 34)
+METHOD_SHA256 = _Method('SHA256', '5', 16, 63)
+METHOD_SHA512 = _Method('SHA512', '6', 16, 106)
+
+methods = []
+for _method in (METHOD_SHA512, METHOD_SHA256, METHOD_MD5):
+ _result = crypt('', _method)
+ if _result and len(_result) == _method.total_size:
+ methods.append(_method)
+methods.append(METHOD_CRYPT)
+del _result, _method
diff --git a/Lib/ctypes/test/test_callbacks.py b/Lib/ctypes/test/test_callbacks.py
index c7207ea..5600b43 100644
--- a/Lib/ctypes/test/test_callbacks.py
+++ b/Lib/ctypes/test/test_callbacks.py
@@ -140,7 +140,7 @@ class Callbacks(unittest.TestCase):
def __del__(self):
gc.collect()
CFUNCTYPE(None)(lambda x=Nasty(): None)
-
+
try:
WINFUNCTYPE
diff --git a/Lib/ctypes/test/test_memfunctions.py b/Lib/ctypes/test/test_memfunctions.py
index aa2113b..aec4aaa 100644
--- a/Lib/ctypes/test/test_memfunctions.py
+++ b/Lib/ctypes/test/test_memfunctions.py
@@ -1,4 +1,5 @@
import sys
+from test import support
import unittest
from ctypes import *
@@ -49,6 +50,7 @@ class MemFunctionsTest(unittest.TestCase):
self.assertEqual(cast(a, POINTER(c_byte))[:7:7],
[97])
+ @support.refcount_test
def test_string_at(self):
s = string_at(b"foo bar")
# XXX The following may be wrong, depending on how Python
diff --git a/Lib/ctypes/test/test_parameters.py b/Lib/ctypes/test/test_parameters.py
index e83fd9a..9762fb9 100644
--- a/Lib/ctypes/test/test_parameters.py
+++ b/Lib/ctypes/test/test_parameters.py
@@ -73,13 +73,10 @@ class SimpleTypesTestCase(unittest.TestCase):
except ImportError:
## print "(No c_wchar_p)"
return
- s = "123"
- if sys.platform == "win32":
- self.assertTrue(c_wchar_p.from_param(s)._obj is s)
- self.assertRaises(TypeError, c_wchar_p.from_param, 42)
- # new in 0.9.1: convert (decode) ascii to unicode
- self.assertEqual(c_wchar_p.from_param("123")._obj, "123")
+ c_wchar_p.from_param("123")
+
+ self.assertRaises(TypeError, c_wchar_p.from_param, 42)
self.assertRaises(TypeError, c_wchar_p.from_param, b"123\377")
pa = c_wchar_p.from_param(c_wchar_p("123"))
diff --git a/Lib/ctypes/test/test_python_api.py b/Lib/ctypes/test/test_python_api.py
index 1f4c603..9de3980 100644
--- a/Lib/ctypes/test/test_python_api.py
+++ b/Lib/ctypes/test/test_python_api.py
@@ -1,5 +1,6 @@
from ctypes import *
import unittest, sys
+from test import support
from ctypes.test import is_resource_enabled
################################################################
@@ -25,6 +26,7 @@ class PythonAPITestCase(unittest.TestCase):
self.assertEqual(PyBytes_FromStringAndSize(b"abcdefghi", 3), b"abc")
+ @support.refcount_test
def test_PyString_FromString(self):
pythonapi.PyBytes_FromString.restype = py_object
pythonapi.PyBytes_FromString.argtypes = (c_char_p,)
@@ -56,6 +58,7 @@ class PythonAPITestCase(unittest.TestCase):
del res
self.assertEqual(grc(42), ref42)
+ @support.refcount_test
def test_PyObj_FromPtr(self):
s = "abc def ghi jkl"
ref = grc(s)
diff --git a/Lib/ctypes/test/test_refcounts.py b/Lib/ctypes/test/test_refcounts.py
index 35a81aa..5613e7a 100644
--- a/Lib/ctypes/test/test_refcounts.py
+++ b/Lib/ctypes/test/test_refcounts.py
@@ -1,4 +1,5 @@
import unittest
+from test import support
import ctypes
import gc
@@ -10,6 +11,7 @@ dll = ctypes.CDLL(_ctypes_test.__file__)
class RefcountTestCase(unittest.TestCase):
+ @support.refcount_test
def test_1(self):
from sys import getrefcount as grc
@@ -34,6 +36,7 @@ class RefcountTestCase(unittest.TestCase):
self.assertEqual(grc(callback), 2)
+ @support.refcount_test
def test_refcount(self):
from sys import getrefcount as grc
def func(*args):
diff --git a/Lib/ctypes/test/test_stringptr.py b/Lib/ctypes/test/test_stringptr.py
index 3d25fa5..95cd161 100644
--- a/Lib/ctypes/test/test_stringptr.py
+++ b/Lib/ctypes/test/test_stringptr.py
@@ -1,4 +1,5 @@
import unittest
+from test import support
from ctypes import *
import _ctypes_test
@@ -7,6 +8,7 @@ lib = CDLL(_ctypes_test.__file__)
class StringPtrTestCase(unittest.TestCase):
+ @support.refcount_test
def test__POINTER_c_char(self):
class X(Structure):
_fields_ = [("str", POINTER(c_char))]
diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py
index 1881e89..97d0c2f 100644
--- a/Lib/ctypes/util.py
+++ b/Lib/ctypes/util.py
@@ -1,5 +1,6 @@
import sys, os
import contextlib
+import subprocess
# find_library(name) returns the pathname of a library, or None.
if os.name == "nt":
@@ -136,16 +137,12 @@ elif os.name == "posix":
rv = f.close()
if rv == 10:
raise OSError('objdump command not found')
- with contextlib.closing(os.popen(cmd)) as f:
- data = f.read()
- res = re.search(r'\sSONAME\s+([^\s]+)', data)
+ res = re.search(r'\sSONAME\s+([^\s]+)', dump)
if not res:
return None
return res.group(1)
- if (sys.platform.startswith("freebsd")
- or sys.platform.startswith("openbsd")
- or sys.platform.startswith("dragonfly")):
+ if sys.platform.startswith(("freebsd", "openbsd", "dragonfly")):
def _num_version(libname):
# "libxyz.so.MAJOR.MINOR" => [ MAJOR, MINOR ]
@@ -171,22 +168,6 @@ elif os.name == "posix":
else:
- def _findLib_ldconfig(name):
- # XXX assuming GLIBC's ldconfig (with option -p)
- expr = r'/[^\(\)\s]*lib%s\.[^\(\)\s]*' % re.escape(name)
- with contextlib.closing(os.popen('/sbin/ldconfig -p 2>/dev/null')) as f:
- data = f.read()
- res = re.search(expr, data)
- if not res:
- # Hm, this works only for libs needed by the python executable.
- cmd = 'ldd %s 2>/dev/null' % sys.executable
- with contextlib.closing(os.popen(cmd)) as f:
- data = f.read()
- res = re.search(expr, data)
- if not res:
- return None
- return res.group(0)
-
def _findSoname_ldconfig(name):
import struct
if struct.calcsize('l') == 4:
@@ -203,14 +184,19 @@ elif os.name == "posix":
abi_type = mach_map.get(machine, 'libc6')
# XXX assuming GLIBC's ldconfig (with option -p)
- expr = r'(\S+)\s+\((%s(?:, OS ABI:[^\)]*)?)\)[^/]*(/[^\(\)\s]*lib%s\.[^\(\)\s]*)' \
- % (abi_type, re.escape(name))
- with contextlib.closing(os.popen('LC_ALL=C LANG=C /sbin/ldconfig -p 2>/dev/null')) as f:
- data = f.read()
- res = re.search(expr, data)
- if not res:
- return None
- return res.group(1)
+ regex = os.fsencode(
+ '\s+(lib%s\.[^\s]+)\s+\(%s' % (re.escape(name), abi_type))
+ try:
+ with subprocess.Popen(['/sbin/ldconfig', '-p'],
+ stdin=subprocess.DEVNULL,
+ stderr=subprocess.DEVNULL,
+ stdout=subprocess.PIPE,
+ env={'LC_ALL': 'C', 'LANG': 'C'}) as p:
+ res = re.search(regex, p.stdout.read())
+ if res:
+ return os.fsdecode(res.group(1))
+ except OSError:
+ pass
def find_library(name):
return _findSoname_ldconfig(name) or _get_soname(_findLib_gcc(name))
diff --git a/Lib/curses/__init__.py b/Lib/curses/__init__.py
index a3e9def..7bfa667 100644
--- a/Lib/curses/__init__.py
+++ b/Lib/curses/__init__.py
@@ -11,7 +11,6 @@ the package, and perhaps a particular module inside it.
"""
from _curses import *
-from curses.wrapper import wrapper
import os as _os
import sys as _sys
@@ -55,3 +54,48 @@ try:
has_key
except NameError:
from .has_key import has_key
+
+# Wrapper for the entire curses-based application. Runs a function which
+# should be the rest of your curses-based application. If the application
+# raises an exception, wrapper() will restore the terminal to a sane state so
+# you can read the resulting traceback.
+
+def wrapper(func, *args, **kwds):
+ """Wrapper function that initializes curses and calls another function,
+ restoring normal keyboard/screen behavior on error.
+ The callable object 'func' is then passed the main window 'stdscr'
+ as its first argument, followed by any other arguments passed to
+ wrapper().
+ """
+
+ try:
+ # Initialize curses
+ stdscr = initscr()
+
+ # Turn off echoing of keys, and enter cbreak mode,
+ # where no buffering is performed on keyboard input
+ noecho()
+ cbreak()
+
+ # In keypad mode, escape sequences for special keys
+ # (like the cursor keys) will be interpreted and
+ # a special value like curses.KEY_LEFT will be returned
+ stdscr.keypad(1)
+
+ # Start color, too. Harmless if the terminal doesn't have
+ # color; user can test with has_color() later on. The try/catch
+ # works around a minor bit of over-conscientiousness in the curses
+ # module -- the error return from C start_color() is ignorable.
+ try:
+ start_color()
+ except:
+ pass
+
+ return func(stdscr, *args, **kwds)
+ finally:
+ # Set everything back to normal
+ if 'stdscr' in locals():
+ stdscr.keypad(0)
+ echo()
+ nocbreak()
+ endwin()
diff --git a/Lib/curses/wrapper.py b/Lib/curses/wrapper.py
deleted file mode 100644
index 5183ce7..0000000
--- a/Lib/curses/wrapper.py
+++ /dev/null
@@ -1,50 +0,0 @@
-"""curses.wrapper
-
-Contains one function, wrapper(), which runs another function which
-should be the rest of your curses-based application. If the
-application raises an exception, wrapper() will restore the terminal
-to a sane state so you can read the resulting traceback.
-
-"""
-
-import curses
-
-def wrapper(func, *args, **kwds):
- """Wrapper function that initializes curses and calls another function,
- restoring normal keyboard/screen behavior on error.
- The callable object 'func' is then passed the main window 'stdscr'
- as its first argument, followed by any other arguments passed to
- wrapper().
- """
-
- try:
- # Initialize curses
- stdscr = curses.initscr()
-
- # Turn off echoing of keys, and enter cbreak mode,
- # where no buffering is performed on keyboard input
- curses.noecho()
- curses.cbreak()
-
- # In keypad mode, escape sequences for special keys
- # (like the cursor keys) will be interpreted and
- # a special value like curses.KEY_LEFT will be returned
- stdscr.keypad(1)
-
- # Start color, too. Harmless if the terminal doesn't have
- # color; user can test with has_color() later on. The try/catch
- # works around a minor bit of over-conscientiousness in the curses
- # module -- the error return from C start_color() is ignorable.
- try:
- curses.start_color()
- except:
- pass
-
- return func(stdscr, *args, **kwds)
- finally:
- # Set everything back to normal
- if 'stdscr' in locals():
- stdscr.keypad(0)
- curses.echo()
- curses.nocbreak()
- curses.endwin()
diff --git a/Lib/datetime.py b/Lib/datetime.py
index 65f95d2..c5eeca4 100644
--- a/Lib/datetime.py
+++ b/Lib/datetime.py
@@ -172,10 +172,6 @@ def _format_time(hh, mm, ss, us):
# Correctly substitute for %z and %Z escapes in strftime formats.
def _wrap_strftime(object, format, timetuple):
- year = timetuple[0]
- if year < 1000:
- raise ValueError("year=%d is before 1000; the datetime strftime() "
- "methods require year >= 1000" % year)
# Don't call utcoffset() or tzname() unless actually needed.
freplace = None # the string to use for %f
zreplace = None # the string to use for %z
diff --git a/Lib/decimal.py b/Lib/decimal.py
index f5277c5..e946182 100644
--- a/Lib/decimal.py
+++ b/Lib/decimal.py
@@ -1871,6 +1871,7 @@ class Decimal(object):
"""
other = _convert_other(other, raiseit=True)
+ third = _convert_other(third, raiseit=True)
# compute product; raise InvalidOperation if either operand is
# a signaling NaN or if the product is zero times infinity.
@@ -1900,7 +1901,6 @@ class Decimal(object):
str(int(self._int) * int(other._int)),
self._exp + other._exp)
- third = _convert_other(third, raiseit=True)
return product.__add__(third, context)
def _power_modulo(self, other, modulo, context=None):
@@ -2001,9 +2001,9 @@ class Decimal(object):
nonzero. For efficiency, other._exp should not be too large,
so that 10**abs(other._exp) is a feasible calculation."""
- # In the comments below, we write x for the value of self and
- # y for the value of other. Write x = xc*10**xe and y =
- # yc*10**ye.
+ # In the comments below, we write x for the value of self and y for the
+ # value of other. Write x = xc*10**xe and abs(y) = yc*10**ye, with xc
+ # and yc positive integers not divisible by 10.
# The main purpose of this method is to identify the *failure*
# of x**y to be exactly representable with as little effort as
@@ -2011,13 +2011,12 @@ class Decimal(object):
# eliminate the possibility of x**y being exact. Only if all
# these tests are passed do we go on to actually compute x**y.
- # Here's the main idea. First normalize both x and y. We
- # express y as a rational m/n, with m and n relatively prime
- # and n>0. Then for x**y to be exactly representable (at
- # *any* precision), xc must be the nth power of a positive
- # integer and xe must be divisible by n. If m is negative
- # then additionally xc must be a power of either 2 or 5, hence
- # a power of 2**n or 5**n.
+ # Here's the main idea. Express y as a rational number m/n, with m and
+ # n relatively prime and n>0. Then for x**y to be exactly
+ # representable (at *any* precision), xc must be the nth power of a
+ # positive integer and xe must be divisible by n. If y is negative
+ # then additionally xc must be a power of either 2 or 5, hence a power
+ # of 2**n or 5**n.
#
# There's a limit to how small |y| can be: if y=m/n as above
# then:
@@ -2089,21 +2088,43 @@ class Decimal(object):
return None
# now xc is a power of 2; e is its exponent
e = _nbits(xc)-1
- # find e*y and xe*y; both must be integers
- if ye >= 0:
- y_as_int = yc*10**ye
- e = e*y_as_int
- xe = xe*y_as_int
- else:
- ten_pow = 10**-ye
- e, remainder = divmod(e*yc, ten_pow)
- if remainder:
- return None
- xe, remainder = divmod(xe*yc, ten_pow)
- if remainder:
- return None
-
- if e*65 >= p*93: # 93/65 > log(10)/log(5)
+
+ # We now have:
+ #
+ # x = 2**e * 10**xe, e > 0, and y < 0.
+ #
+ # The exact result is:
+ #
+ # x**y = 5**(-e*y) * 10**(e*y + xe*y)
+ #
+ # provided that both e*y and xe*y are integers. Note that if
+ # 5**(-e*y) >= 10**p, then the result can't be expressed
+ # exactly with p digits of precision.
+ #
+ # Using the above, we can guard against large values of ye.
+ # 93/65 is an upper bound for log(10)/log(5), so if
+ #
+ # ye >= len(str(93*p//65))
+ #
+ # then
+ #
+ # -e*y >= -y >= 10**ye > 93*p/65 > p*log(10)/log(5),
+ #
+ # so 5**(-e*y) >= 10**p, and the coefficient of the result
+ # can't be expressed in p digits.
+
+ # emax >= largest e such that 5**e < 10**p.
+ emax = p*93//65
+ if ye >= len(str(emax)):
+ return None
+
+ # Find -e*y and -xe*y; both must be integers
+ e = _decimal_lshift_exact(e * yc, ye)
+ xe = _decimal_lshift_exact(xe * yc, ye)
+ if e is None or xe is None:
+ return None
+
+ if e > emax:
return None
xc = 5**e
@@ -2117,19 +2138,20 @@ class Decimal(object):
while xc % 5 == 0:
xc //= 5
e -= 1
- if ye >= 0:
- y_as_integer = yc*10**ye
- e = e*y_as_integer
- xe = xe*y_as_integer
- else:
- ten_pow = 10**-ye
- e, remainder = divmod(e*yc, ten_pow)
- if remainder:
- return None
- xe, remainder = divmod(xe*yc, ten_pow)
- if remainder:
- return None
- if e*3 >= p*10: # 10/3 > log(10)/log(2)
+
+ # Guard against large values of ye, using the same logic as in
+ # the 'xc is a power of 2' branch. 10/3 is an upper bound for
+ # log(10)/log(2).
+ emax = p*10//3
+ if ye >= len(str(emax)):
+ return None
+
+ e = _decimal_lshift_exact(e * yc, ye)
+ xe = _decimal_lshift_exact(xe * yc, ye)
+ if e is None or xe is None:
+ return None
+
+ if e > emax:
return None
xc = 2**e
else:
@@ -3881,28 +3903,6 @@ class Context(object):
return nc
__copy__ = copy
- # _clamp is provided for backwards compatibility with third-party
- # code. May be removed in Python >= 3.3.
- def _get_clamp(self):
- "_clamp mirrors the clamp attribute. Its use is deprecated."
- import warnings
- warnings.warn('Use of the _clamp attribute is deprecated. '
- 'Please use clamp instead.',
- DeprecationWarning)
- return self.clamp
-
- def _set_clamp(self, clamp):
- "_clamp mirrors the clamp attribute. Its use is deprecated."
- import warnings
- warnings.warn('Use of the _clamp attribute is deprecated. '
- 'Please use clamp instead.',
- DeprecationWarning)
- self.clamp = clamp
-
- # don't bother with _del_clamp; no sane 3rd party code should
- # be deleting the _clamp attribute
- _clamp = property(_get_clamp, _set_clamp)
-
def _raise_error(self, condition, explanation = None, *args):
"""Handles an error
@@ -5529,6 +5529,27 @@ def _normalize(op1, op2, prec = 0):
_nbits = int.bit_length
+def _decimal_lshift_exact(n, e):
+ """ Given integers n and e, return n * 10**e if it's an integer, else None.
+
+ The computation is designed to avoid computing large powers of 10
+ unnecessarily.
+
+ >>> _decimal_lshift_exact(3, 4)
+ 30000
+ >>> _decimal_lshift_exact(300, -999999999) # returns None
+
+ """
+ if n == 0:
+ return 0
+ elif e >= 0:
+ return n * 10**e
+ else:
+ # val_n = largest power of 10 dividing n.
+ str_n = str(abs(n))
+ val_n = len(str_n) - len(str_n.rstrip('0'))
+ return None if val_n < -e else n // 10**-e
+
def _sqrt_nearest(n, a):
"""Closest integer to the square root of the positive integer n. a is
an initial approximation to the square root. Any positive integer
diff --git a/Lib/difflib.py b/Lib/difflib.py
index e6cc6ee..ae377d7 100644
--- a/Lib/difflib.py
+++ b/Lib/difflib.py
@@ -204,7 +204,7 @@ class SequenceMatcher:
# returning true iff the element is "junk" -- this has
# subtle but helpful effects on the algorithm, which I'll
# get around to writing up someday <0.9 wink>.
- # DON'T USE! Only __chain_b uses this. Use isbjunk.
+ # DON'T USE! Only __chain_b uses this. Use "in self.bjunk".
# bjunk
# the items in b for which isjunk is True.
# bpopular
@@ -287,7 +287,6 @@ class SequenceMatcher:
# when self.isjunk is defined, junk elements don't show up in this
# map at all, which stops the central find_longest_match method
# from starting any matching block at a junk element ...
- # also creates the fast isbjunk function ...
# b2j also does not contain entries for "popular" elements, meaning
# elements that account for more than 1 + 1% of the total elements, and
# when the sequence is reasonably large (>= 200 elements); this can
@@ -800,7 +799,7 @@ class Differ:
... 2. Explicit is better than implicit.
... 3. Simple is better than complex.
... 4. Complex is better than complicated.
- ... '''.splitlines(1)
+ ... '''.splitlines(keepends=True)
>>> len(text1)
4
>>> text1[0][-1]
@@ -809,7 +808,7 @@ class Differ:
... 3. Simple is better than complex.
... 4. Complicated is better than complex.
... 5. Flat is better than nested.
- ... '''.splitlines(1)
+ ... '''.splitlines(keepends=True)
Next we instantiate a Differ object:
@@ -896,8 +895,8 @@ class Differ:
Example:
- >>> print(''.join(Differ().compare('one\ntwo\nthree\n'.splitlines(1),
- ... 'ore\ntree\nemu\n'.splitlines(1))),
+ >>> print(''.join(Differ().compare('one\ntwo\nthree\n'.splitlines(True),
+ ... 'ore\ntree\nemu\n'.splitlines(True))),
... end="")
- one
? ^
@@ -1269,8 +1268,8 @@ def context_diff(a, b, fromfile='', tofile='',
Example:
- >>> print(''.join(context_diff('one\ntwo\nthree\nfour\n'.splitlines(1),
- ... 'zero\none\ntree\nfour\n'.splitlines(1), 'Original', 'Current')),
+ >>> print(''.join(context_diff('one\ntwo\nthree\nfour\n'.splitlines(True),
+ ... 'zero\none\ntree\nfour\n'.splitlines(True), 'Original', 'Current')),
... end="")
*** Original
--- Current
@@ -1339,8 +1338,8 @@ def ndiff(a, b, linejunk=None, charjunk=IS_CHARACTER_JUNK):
Example:
- >>> diff = ndiff('one\ntwo\nthree\n'.splitlines(1),
- ... 'ore\ntree\nemu\n'.splitlines(1))
+ >>> diff = ndiff('one\ntwo\nthree\n'.splitlines(keepends=True),
+ ... 'ore\ntree\nemu\n'.splitlines(keepends=True))
>>> print(''.join(diff), end="")
- one
? ^
@@ -2034,8 +2033,8 @@ def restore(delta, which):
Examples:
- >>> diff = ndiff('one\ntwo\nthree\n'.splitlines(1),
- ... 'ore\ntree\nemu\n'.splitlines(1))
+ >>> diff = ndiff('one\ntwo\nthree\n'.splitlines(keepends=True),
+ ... 'ore\ntree\nemu\n'.splitlines(keepends=True))
>>> diff = list(diff)
>>> print(''.join(restore(diff, 1)), end="")
one
diff --git a/Lib/distutils/__init__.py b/Lib/distutils/__init__.py
index 9ec6165..f883916 100644
--- a/Lib/distutils/__init__.py
+++ b/Lib/distutils/__init__.py
@@ -13,5 +13,5 @@ used from a setup script as
# Updated automatically by the Python release process.
#
#--start constants--
-__version__ = "3.2.2"
+__version__ = "3.3a0"
#--end constants--
diff --git a/Lib/distutils/command/bdist_wininst.py b/Lib/distutils/command/bdist_wininst.py
index e3ed3ad..959a8bf 100644
--- a/Lib/distutils/command/bdist_wininst.py
+++ b/Lib/distutils/command/bdist_wininst.py
@@ -265,11 +265,11 @@ class bdist_wininst(Command):
cfgdata = cfgdata + b"\0"
if self.pre_install_script:
# We need to normalize newlines, so we open in text mode and
- # convert back to bytes. "latin1" simply avoids any possible
+ # convert back to bytes. "latin-1" simply avoids any possible
# failures.
with open(self.pre_install_script, "r",
- encoding="latin1") as script:
- script_data = script.read().encode("latin1")
+ encoding="latin-1") as script:
+ script_data = script.read().encode("latin-1")
cfgdata = cfgdata + script_data + b"\n\0"
else:
# empty pre-install script
diff --git a/Lib/distutils/command/build_ext.py b/Lib/distutils/command/build_ext.py
index 8d843d6..8baf538 100644
--- a/Lib/distutils/command/build_ext.py
+++ b/Lib/distutils/command/build_ext.py
@@ -240,8 +240,7 @@ class build_ext(Command):
# for extensions under Linux or Solaris with a shared Python library,
# Python's library directory must be appended to library_dirs
sysconfig.get_config_var('Py_ENABLE_SHARED')
- if ((sys.platform.startswith('linux') or sys.platform.startswith('gnu')
- or sys.platform.startswith('sunos'))
+ if (sys.platform.startswith(('linux', 'gnu', 'sunos'))
and sysconfig.get_config_var('Py_ENABLE_SHARED')):
if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")):
# building third party extensions
diff --git a/Lib/distutils/command/build_scripts.py b/Lib/distutils/command/build_scripts.py
index ec43477..4b5b22e 100644
--- a/Lib/distutils/command/build_scripts.py
+++ b/Lib/distutils/command/build_scripts.py
@@ -126,10 +126,9 @@ class build_scripts(Command):
"The shebang ({!r}) is not decodable "
"from the script encoding ({})"
.format(shebang, encoding))
- outf = open(outfile, "wb")
- outf.write(shebang)
- outf.writelines(f.readlines())
- outf.close()
+ with open(outfile, "wb") as outf:
+ outf.write(shebang)
+ outf.writelines(f.readlines())
if f:
f.close()
else:
diff --git a/Lib/distutils/tests/test_bdist_rpm.py b/Lib/distutils/tests/test_bdist_rpm.py
index 804fb13..9b0639a 100644
--- a/Lib/distutils/tests/test_bdist_rpm.py
+++ b/Lib/distutils/tests/test_bdist_rpm.py
@@ -28,6 +28,11 @@ class BuildRpmTestCase(support.TempdirManager,
unittest.TestCase):
def setUp(self):
+ try:
+ sys.executable.encode("UTF-8")
+ except UnicodeEncodeError:
+ raise unittest.SkipTest("sys.executable is not encodable to UTF-8")
+
super(BuildRpmTestCase, self).setUp()
self.old_location = os.getcwd()
self.old_sys_argv = sys.argv, sys.argv[:]
@@ -42,7 +47,7 @@ class BuildRpmTestCase(support.TempdirManager,
# XXX I am unable yet to make this test work without
# spurious sdtout/stderr output under Mac OS X
- if sys.platform != 'linux2':
+ if not sys.platform.startswith('linux'):
return
# this test will run only if the rpm commands are found
@@ -82,7 +87,7 @@ class BuildRpmTestCase(support.TempdirManager,
# XXX I am unable yet to make this test work without
# spurious sdtout/stderr output under Mac OS X
- if sys.platform != 'linux2':
+ if not sys.platform.startswith('linux'):
return
# http://bugs.python.org/issue1533164
diff --git a/Lib/doctest.py b/Lib/doctest.py
index 234733e..aba98dc 100644
--- a/Lib/doctest.py
+++ b/Lib/doctest.py
@@ -458,7 +458,6 @@ class Example:
return hash((self.source, self.want, self.lineno, self.indent,
self.exc_msg))
-
class DocTest:
"""
A collection of doctest examples that should be run in a single
@@ -1367,7 +1366,7 @@ class DocTestRunner:
m = self.__LINECACHE_FILENAME_RE.match(filename)
if m and m.group('name') == self.test.name:
example = self.test.examples[int(m.group('examplenum'))]
- return example.source.splitlines(True)
+ return example.source.splitlines(keepends=True)
else:
return self.save_linecache_getlines(filename, module_globals)
@@ -1413,6 +1412,7 @@ class DocTestRunner:
# Note that the interactive output will go to *our*
# save_stdout, even if that's not the real sys.stdout; this
# allows us to write test cases for the set_trace behavior.
+ save_trace = sys.gettrace()
save_set_trace = pdb.set_trace
self.debugger = _OutputRedirectingPdb(save_stdout)
self.debugger.reset()
@@ -1432,6 +1432,7 @@ class DocTestRunner:
finally:
sys.stdout = save_stdout
pdb.set_trace = save_set_trace
+ sys.settrace(save_trace)
linecache.getlines = self.save_linecache_getlines
sys.displayhook = save_displayhook
if clear_globs:
@@ -1628,8 +1629,8 @@ class OutputChecker:
# Check if we should use diff.
if self._do_a_fancy_diff(want, got, optionflags):
# Split want & got into lines.
- want_lines = want.splitlines(True) # True == keep line ends
- got_lines = got.splitlines(True)
+ want_lines = want.splitlines(keepends=True)
+ got_lines = got.splitlines(keepends=True)
# Use difflib to find their differences.
if optionflags & REPORT_UDIFF:
diff = difflib.unified_diff(want_lines, got_lines, n=2)
diff --git a/Lib/email/_parseaddr.py b/Lib/email/_parseaddr.py
index a295757..0342469 100644
--- a/Lib/email/_parseaddr.py
+++ b/Lib/email/_parseaddr.py
@@ -47,6 +47,21 @@ def parsedate_tz(data):
Accounts for military timezones.
"""
+ res = _parsedate_tz(data)
+ if res[9] is None:
+ res[9] = 0
+ return tuple(res)
+
+def _parsedate_tz(data):
+ """Convert date to extended time tuple.
+
+ The last (additional) element is the time zone offset in seconds, except if
+ the timezone was specified as -0000. In that case the last element is
+ None. This indicates a UTC timestamp that explicitly declaims knowledge of
+ the source timezone, as opposed to a +0000 timestamp that indicates the
+ source timezone really was UTC.
+
+ """
data = data.split()
# The FWS after the comma after the day-of-week is optional, so search and
# adjust for this.
@@ -99,6 +114,14 @@ def parsedate_tz(data):
tss = '0'
elif len(tm) == 3:
[thh, tmm, tss] = tm
+ elif len(tm) == 1 and '.' in tm[0]:
+ # Some non-compliant MUAs use '.' to separate time elements.
+ tm = tm[0].split('.')
+ if len(tm) == 2:
+ [thh, tmm] = tm
+ tss = 0
+ elif len(tm) == 3:
+ [thh, tmm, tss] = tm
else:
return None
try:
@@ -130,6 +153,8 @@ def parsedate_tz(data):
tzoffset = int(tz)
except ValueError:
pass
+ if tzoffset==0 and tz.startswith('-'):
+ tzoffset = None
# Convert a timezone offset into seconds ; -0500 -> -18000
if tzoffset:
if tzoffset < 0:
@@ -139,7 +164,7 @@ def parsedate_tz(data):
tzsign = 1
tzoffset = tzsign * ( (tzoffset//100)*3600 + (tzoffset % 100)*60)
# Daylight Saving Time flag is set to -1, since DST is unknown.
- return yy, mm, dd, thh, tmm, tss, 0, 1, -1, tzoffset
+ return [yy, mm, dd, thh, tmm, tss, 0, 1, -1, tzoffset]
def parsedate(data):
diff --git a/Lib/email/errors.py b/Lib/email/errors.py
index d52a624..c04deb4 100644
--- a/Lib/email/errors.py
+++ b/Lib/email/errors.py
@@ -32,7 +32,7 @@ class CharsetError(MessageError):
# These are parsing defects which the parser was able to work around.
-class MessageDefect:
+class MessageDefect(Exception):
"""Base class for a message defect."""
def __init__(self, line=None):
@@ -55,3 +55,6 @@ class MalformedHeaderDefect(MessageDefect):
class MultipartInvariantViolationDefect(MessageDefect):
"""A message claimed to be a multipart but no subparts were found."""
+
+class InvalidMultipartContentTransferEncodingDefect(MessageDefect):
+ """An invalid content transfer encoding was set on the multipart itself."""
diff --git a/Lib/email/feedparser.py b/Lib/email/feedparser.py
index 60a8325..e754d89 100644
--- a/Lib/email/feedparser.py
+++ b/Lib/email/feedparser.py
@@ -25,6 +25,7 @@ import re
from email import errors
from email import message
+from email import policy
NLCRE = re.compile('\r\n|\r|\n')
NLCRE_bol = re.compile('(\r\n|\r|\n)')
@@ -120,9 +121,6 @@ class BufferedSubFile(object):
# Reverse and insert at the front of the lines.
self._lines[:0] = lines[::-1]
- def is_closed(self):
- return self._closed
-
def __iter__(self):
return self
@@ -137,9 +135,16 @@ class BufferedSubFile(object):
class FeedParser:
"""A feed-style parser of email."""
- def __init__(self, _factory=message.Message):
- """_factory is called with no arguments to create a new message obj"""
+ def __init__(self, _factory=message.Message, *, policy=policy.default):
+ """_factory is called with no arguments to create a new message obj
+
+ The policy keyword specifies a policy object that controls a number of
+ aspects of the parser's operation. The default policy maintains
+ backward compatibility.
+
+ """
self._factory = _factory
+ self.policy = policy
self._input = BufferedSubFile()
self._msgstack = []
self._parse = self._parsegen().__next__
@@ -171,7 +176,8 @@ class FeedParser:
# Look for final set of defects
if root.get_content_maintype() == 'multipart' \
and not root.is_multipart():
- root.defects.append(errors.MultipartInvariantViolationDefect())
+ defect = errors.MultipartInvariantViolationDefect()
+ self.policy.handle_defect(root, defect)
return root
def _new_message(self):
@@ -284,7 +290,8 @@ class FeedParser:
# defined a boundary. That's a problem which we'll handle by
# reading everything until the EOF and marking the message as
# defective.
- self._cur.defects.append(errors.NoBoundaryInMultipartDefect())
+ defect = errors.NoBoundaryInMultipartDefect()
+ self.policy.handle_defect(self._cur, defect)
lines = []
for line in self._input:
if line is NeedMoreData:
@@ -293,6 +300,11 @@ class FeedParser:
lines.append(line)
self._cur.set_payload(EMPTYSTRING.join(lines))
return
+ # Make sure a valid content type was specified per RFC 2045:6.4.
+ if (self._cur.get('content-transfer-encoding', '8bit').lower()
+ not in ('7bit', '8bit', 'binary')):
+ defect = errors.InvalidMultipartContentTransferEncodingDefect()
+ self.policy.handle_defect(self._cur, defect)
# Create a line match predicate which matches the inter-part
# boundary as well as the end-of-multipart boundary. Don't push
# this onto the input stream until we've scanned past the
@@ -388,7 +400,8 @@ class FeedParser:
# that as a defect and store the captured text as the payload.
# Everything from here to the EOF is epilogue.
if capturing_preamble:
- self._cur.defects.append(errors.StartBoundaryNotFoundDefect())
+ defect = errors.StartBoundaryNotFoundDefect()
+ self.policy.handle_defect(self._cur, defect)
self._cur.set_payload(EMPTYSTRING.join(preamble))
epilogue = []
for line in self._input:
@@ -440,7 +453,7 @@ class FeedParser:
# is illegal, so let's note the defect, store the illegal
# line, and ignore it for purposes of headers.
defect = errors.FirstHeaderLineIsContinuationDefect(line)
- self._cur.defects.append(defect)
+ self.policy.handle_defect(self._cur, defect)
continue
lastvalue.append(line)
continue
diff --git a/Lib/email/generator.py b/Lib/email/generator.py
index f0e7a95..d8b8fa9 100644
--- a/Lib/email/generator.py
+++ b/Lib/email/generator.py
@@ -13,8 +13,10 @@ import random
import warnings
from io import StringIO, BytesIO
+from email import policy
from email.header import Header
from email.message import _has_surrogates
+import email.charset as _charset
UNDERSCORE = '_'
NL = '\n' # XXX: no longer used by the code below.
@@ -33,7 +35,8 @@ class Generator:
# Public interface
#
- def __init__(self, outfp, mangle_from_=True, maxheaderlen=78):
+ def __init__(self, outfp, mangle_from_=True, maxheaderlen=None, *,
+ policy=policy.default):
"""Create the generator for message flattening.
outfp is the output file-like object for writing the message to. It
@@ -49,16 +52,23 @@ class Generator:
defined in the Header class. Set maxheaderlen to zero to disable
header wrapping. The default is 78, as recommended (but not required)
by RFC 2822.
+
+ The policy keyword specifies a policy object that controls a number of
+ aspects of the generator's operation. The default policy maintains
+ backward compatibility.
+
"""
self._fp = outfp
self._mangle_from_ = mangle_from_
- self._maxheaderlen = maxheaderlen
+ self._maxheaderlen = (maxheaderlen if maxheaderlen is not None else
+ policy.max_line_length)
+ self.policy = policy
def write(self, s):
# Just delegate to the file object
self._fp.write(s)
- def flatten(self, msg, unixfrom=False, linesep='\n'):
+ def flatten(self, msg, unixfrom=False, linesep=None):
r"""Print the message object tree rooted at msg to the output file
specified when the Generator instance was created.
@@ -70,17 +80,15 @@ class Generator:
Note that for subobjects, no From_ line is printed.
linesep specifies the characters used to indicate a new line in
- the output. The default value is the most useful for typical
- Python applications, but it can be set to \r\n to produce RFC-compliant
- line separators when needed.
+ the output. The default value is determined by the policy.
"""
# We use the _XXX constants for operating on data that comes directly
# from the msg, and _encoded_XXX constants for operating on data that
# has already been converted (to bytes in the BytesGenerator) and
# inserted into a temporary buffer.
- self._NL = linesep
- self._encoded_NL = self._encode(linesep)
+ self._NL = linesep if linesep is not None else self.policy.linesep
+ self._encoded_NL = self._encode(self._NL)
self._EMPTY = ''
self._encoded_EMTPY = self._encode('')
if unixfrom:
@@ -297,10 +305,12 @@ class Generator:
# message/rfc822. Such messages are generated by, for example,
# Groupwise when forwarding unadorned messages. (Issue 7970.) So
# in that case we just emit the string body.
- payload = msg.get_payload()
+ payload = msg._payload
if isinstance(payload, list):
g.flatten(msg.get_payload(0), unixfrom=False, linesep=self._NL)
payload = s.getvalue()
+ else:
+ payload = self._encode(payload)
self._fp.write(payload)
# This used to be a module level function; we use a classmethod for this
@@ -336,7 +346,10 @@ class BytesGenerator(Generator):
Functionally identical to the base Generator except that the output is
bytes and not string. When surrogates were used in the input to encode
- bytes, these are decoded back to bytes for output.
+ bytes, these are decoded back to bytes for output. If the policy has
+ must_be_7bit set true, then the message is transformed such that the
+ non-ASCII bytes are properly content transfer encoded, using the
+ charset unknown-8bit.
The outfp object must accept bytes in its write method.
"""
@@ -359,21 +372,22 @@ class BytesGenerator(Generator):
# strings with 8bit bytes.
for h, v in msg._headers:
self.write('%s: ' % h)
- if isinstance(v, Header):
- self.write(v.encode(maxlinelen=self._maxheaderlen)+NL)
- elif _has_surrogates(v):
- # If we have raw 8bit data in a byte string, we have no idea
- # what the encoding is. There is no safe way to split this
- # string. If it's ascii-subset, then we could do a normal
- # ascii split, but if it's multibyte then we could break the
- # string. There's no way to know so the least harm seems to
- # be to not split the string and risk it being too long.
- self.write(v+NL)
- else:
- # Header's got lots of smarts and this string is safe...
- header = Header(v, maxlinelen=self._maxheaderlen,
- header_name=h)
- self.write(header.encode(linesep=self._NL)+self._NL)
+ if isinstance(v, str):
+ if _has_surrogates(v):
+ if not self.policy.must_be_7bit:
+ # If we have raw 8bit data in a byte string, we have no idea
+ # what the encoding is. There is no safe way to split this
+ # string. If it's ascii-subset, then we could do a normal
+ # ascii split, but if it's multibyte then we could break the
+ # string. There's no way to know so the least harm seems to
+ # be to not split the string and risk it being too long.
+ self.write(v+NL)
+ continue
+ h = Header(v, charset=_charset.UNKNOWN8BIT, header_name=h)
+ else:
+ h = Header(v, header_name=h)
+ self.write(h.encode(linesep=self._NL,
+ maxlinelen=self._maxheaderlen)+self._NL)
# A blank line always separates headers from body
self.write(self._NL)
@@ -382,7 +396,7 @@ class BytesGenerator(Generator):
# just write it back out.
if msg._payload is None:
return
- if _has_surrogates(msg._payload):
+ if _has_surrogates(msg._payload) and not self.policy.must_be_7bit:
self.write(msg._payload)
else:
super(BytesGenerator,self)._handle_text(msg)
diff --git a/Lib/email/parser.py b/Lib/email/parser.py
index 6caaff5..0f92160 100644
--- a/Lib/email/parser.py
+++ b/Lib/email/parser.py
@@ -4,18 +4,19 @@
"""A parser of RFC 2822 and MIME email messages."""
-__all__ = ['Parser', 'HeaderParser']
+__all__ = ['Parser', 'HeaderParser', 'BytesParser', 'BytesHeaderParser']
import warnings
from io import StringIO, TextIOWrapper
from email.feedparser import FeedParser
from email.message import Message
+from email import policy
class Parser:
- def __init__(self, *args, **kws):
+ def __init__(self, _class=Message, *, policy=policy.default):
"""Parser of RFC 2822 and MIME email messages.
Creates an in-memory object tree representing the email message, which
@@ -30,28 +31,14 @@ class Parser:
_class is the class to instantiate for new message objects when they
must be created. This class must have a constructor that can take
zero arguments. Default is Message.Message.
+
+ The policy keyword specifies a policy object that controls a number of
+ aspects of the parser's operation. The default policy maintains
+ backward compatibility.
+
"""
- if len(args) >= 1:
- if '_class' in kws:
- raise TypeError("Multiple values for keyword arg '_class'")
- kws['_class'] = args[0]
- if len(args) == 2:
- if 'strict' in kws:
- raise TypeError("Multiple values for keyword arg 'strict'")
- kws['strict'] = args[1]
- if len(args) > 2:
- raise TypeError('Too many arguments')
- if '_class' in kws:
- self._class = kws['_class']
- del kws['_class']
- else:
- self._class = Message
- if 'strict' in kws:
- warnings.warn("'strict' argument is deprecated (and ignored)",
- DeprecationWarning, 2)
- del kws['strict']
- if kws:
- raise TypeError('Unexpected keyword arguments')
+ self._class = _class
+ self.policy = policy
def parse(self, fp, headersonly=False):
"""Create a message structure from the data in a file.
@@ -61,7 +48,7 @@ class Parser:
parsing after reading the headers or not. The default is False,
meaning it parses the entire contents of the file.
"""
- feedparser = FeedParser(self._class)
+ feedparser = FeedParser(self._class, policy=self.policy)
if headersonly:
feedparser._set_headersonly()
while True:
@@ -134,3 +121,11 @@ class BytesParser:
"""
text = text.decode('ASCII', errors='surrogateescape')
return self.parser.parsestr(text, headersonly)
+
+
+class BytesHeaderParser(BytesParser):
+ def parse(self, fp, headersonly=True):
+ return BytesParser.parse(self, fp, headersonly=True)
+
+ def parsebytes(self, text, headersonly=True):
+ return BytesParser.parsebytes(self, text, headersonly=True)
diff --git a/Lib/email/policy.py b/Lib/email/policy.py
new file mode 100644
index 0000000..88877a2
--- /dev/null
+++ b/Lib/email/policy.py
@@ -0,0 +1,174 @@
+"""Policy framework for the email package.
+
+Allows fine grained feature control of how the package parses and emits data.
+"""
+
+__all__ = [
+ 'Policy',
+ 'default',
+ 'strict',
+ 'SMTP',
+ 'HTTP',
+ ]
+
+
+class _PolicyBase:
+
+ """Policy Object basic framework.
+
+ This class is useless unless subclassed. A subclass should define
+ class attributes with defaults for any values that are to be
+ managed by the Policy object. The constructor will then allow
+ non-default values to be set for these attributes at instance
+ creation time. The instance will be callable, taking these same
+ attributes keyword arguments, and returning a new instance
+ identical to the called instance except for those values changed
+ by the keyword arguments. Instances may be added, yielding new
+ instances with any non-default values from the right hand
+ operand overriding those in the left hand operand. That is,
+
+ A + B == A(<non-default values of B>)
+
+ The repr of an instance can be used to reconstruct the object
+ if and only if the repr of the values can be used to reconstruct
+ those values.
+
+ """
+
+ def __init__(self, **kw):
+ """Create new Policy, possibly overriding some defaults.
+
+ See class docstring for a list of overridable attributes.
+
+ """
+ for name, value in kw.items():
+ if hasattr(self, name):
+ super(_PolicyBase,self).__setattr__(name, value)
+ else:
+ raise TypeError(
+ "{!r} is an invalid keyword argument for {}".format(
+ name, self.__class__.__name__))
+
+ def __repr__(self):
+ args = [ "{}={!r}".format(name, value)
+ for name, value in self.__dict__.items() ]
+ return "{}({})".format(self.__class__.__name__, args if args else '')
+
+ def clone(self, **kw):
+ """Return a new instance with specified attributes changed.
+
+ The new instance has the same attribute values as the current object,
+ except for the changes passed in as keyword arguments.
+
+ """
+ for attr, value in self.__dict__.items():
+ if attr not in kw:
+ kw[attr] = value
+ return self.__class__(**kw)
+
+ def __setattr__(self, name, value):
+ if hasattr(self, name):
+ msg = "{!r} object attribute {!r} is read-only"
+ else:
+ msg = "{!r} object has no attribute {!r}"
+ raise AttributeError(msg.format(self.__class__.__name__, name))
+
+ def __add__(self, other):
+ """Non-default values from right operand override those from left.
+
+ The object returned is a new instance of the subclass.
+
+ """
+ return self.clone(**other.__dict__)
+
+
+class Policy(_PolicyBase):
+
+ """Controls for how messages are interpreted and formatted.
+
+ Most of the classes and many of the methods in the email package
+ accept Policy objects as parameters. A Policy object contains a set
+ of values and functions that control how input is interpreted and how
+ output is rendered. For example, the parameter 'raise_on_defect'
+ controls whether or not an RFC violation throws an error or not,
+ while 'max_line_length' controls the maximum length of output lines
+ when a Message is serialized.
+
+ Any valid attribute may be overridden when a Policy is created by
+ passing it as a keyword argument to the constructor. Policy
+ objects are immutable, but a new Policy object can be created
+ with only certain values changed by calling the Policy instance
+ with keyword arguments. Policy objects can also be added,
+ producing a new Policy object in which the non-default attributes
+ set in the right hand operand overwrite those specified in the
+ left operand.
+
+ Settable attributes:
+
+ raise_on_defect -- If true, then defects should be raised
+ as errors. Default False.
+
+ linesep -- string containing the value to use as
+ separation between output lines. Default '\n'.
+
+ must_be_7bit -- output must contain only 7bit clean data.
+ Default False.
+
+ max_line_length -- maximum length of lines, excluding 'linesep',
+ during serialization. None means no line
+ wrapping is done. Default is 78.
+
+ Methods:
+
+ register_defect(obj, defect)
+ defect is a Defect instance. The default implementation appends defect
+ to the objs 'defects' attribute.
+
+ handle_defect(obj, defect)
+ intended to be called by parser code that finds a defect. If
+ raise_on_defect is True, defect is raised as an error, otherwise
+ register_defect is called.
+
+ """
+
+ raise_on_defect = False
+ linesep = '\n'
+ must_be_7bit = False
+ max_line_length = 78
+
+ def handle_defect(self, obj, defect):
+ """Based on policy, either raise defect or call register_defect.
+
+ handle_defect(obj, defect)
+
+ defect should be a Defect subclass, but in any case must be an
+ Exception subclass. obj is the object on which the defect should be
+ registered if it is not raised. If the raise_on_defect is True, the
+ defect is raised as an error, otherwise the object and the defect are
+ passed to register_defect.
+
+ This class is intended to be called by parsers that discover defects,
+ and will not be called from code using the library unless that code is
+ implementing an alternate parser.
+
+ """
+ if self.raise_on_defect:
+ raise defect
+ self.register_defect(obj, defect)
+
+ def register_defect(self, obj, defect):
+ """Record 'defect' on 'obj'.
+
+ Called by handle_defect if raise_on_defect is False. This method is
+ part of the Policy API so that Policy subclasses can implement custom
+ defect handling. The default implementation calls the append method
+ of the defects attribute of obj.
+
+ """
+ obj.defects.append(defect)
+
+
+default = Policy()
+strict = default.clone(raise_on_defect=True)
+SMTP = default.clone(linesep='\r\n')
+HTTP = default.clone(linesep='\r\n', max_line_length=None)
diff --git a/Lib/email/utils.py b/Lib/email/utils.py
index ac4da37..aecea65 100644
--- a/Lib/email/utils.py
+++ b/Lib/email/utils.py
@@ -11,12 +11,14 @@ __all__ = [
'encode_rfc2231',
'formataddr',
'formatdate',
+ 'format_datetime',
'getaddresses',
'make_msgid',
'mktime_tz',
'parseaddr',
'parsedate',
'parsedate_tz',
+ 'parsedate_to_datetime',
'unquote',
]
@@ -26,6 +28,7 @@ import time
import base64
import random
import socket
+import datetime
import urllib.parse
import warnings
from io import StringIO
@@ -37,11 +40,13 @@ from email._parseaddr import mktime_tz
# We need wormarounds for bugs in these methods in older Pythons (see below)
from email._parseaddr import parsedate as _parsedate
from email._parseaddr import parsedate_tz as _parsedate_tz
+from email._parseaddr import _parsedate_tz as __parsedate_tz
from quopri import decodestring as _qdecode
# Intrapackage imports
from email.encoders import _bencode, _qencode
+from email.charset import Charset
COMMASPACE = ', '
EMPTYSTRING = ''
@@ -56,21 +61,36 @@ escapesre = re.compile(r'[][\\()"]')
# Helpers
-def formataddr(pair):
+def formataddr(pair, charset='utf-8'):
"""The inverse of parseaddr(), this takes a 2-tuple of the form
(realname, email_address) and returns the string value suitable
for an RFC 2822 From, To or Cc header.
If the first element of pair is false, then the second element is
returned unmodified.
+
+ Optional charset if given is the character set that is used to encode
+ realname in case realname is not ASCII safe. Can be an instance of str or
+ a Charset-like object which has a header_encode method. Default is
+ 'utf-8'.
"""
name, address = pair
+ # The address MUST (per RFC) be ascii, so throw a UnicodeError if it isn't.
+ address.encode('ascii')
if name:
- quotes = ''
- if specialsre.search(name):
- quotes = '"'
- name = escapesre.sub(r'\\\g<0>', name)
- return '%s%s%s <%s>' % (quotes, name, quotes, address)
+ try:
+ name.encode('ascii')
+ except UnicodeEncodeError:
+ if isinstance(charset, str):
+ charset = Charset(charset)
+ encoded_name = charset.header_encode(name)
+ return "%s <%s>" % (encoded_name, address)
+ else:
+ quotes = ''
+ if specialsre.search(name):
+ quotes = '"'
+ name = escapesre.sub(r'\\\g<0>', name)
+ return '%s%s%s <%s>' % (quotes, name, quotes, address)
return address
@@ -94,6 +114,14 @@ ecre = re.compile(r'''
''', re.VERBOSE | re.IGNORECASE)
+def _format_timetuple_and_zone(timetuple, zone):
+ return '%s, %02d %s %04d %02d:%02d:%02d %s' % (
+ ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'][timetuple[6]],
+ timetuple[2],
+ ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
+ 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][timetuple[1] - 1],
+ timetuple[0], timetuple[3], timetuple[4], timetuple[5],
+ zone)
def formatdate(timeval=None, localtime=False, usegmt=False):
"""Returns a date string as specified by RFC 2822, e.g.:
@@ -138,14 +166,25 @@ def formatdate(timeval=None, localtime=False, usegmt=False):
zone = 'GMT'
else:
zone = '-0000'
- return '%s, %02d %s %04d %02d:%02d:%02d %s' % (
- ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'][now[6]],
- now[2],
- ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
- 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][now[1] - 1],
- now[0], now[3], now[4], now[5],
- zone)
+ return _format_timetuple_and_zone(now, zone)
+
+def format_datetime(dt, usegmt=False):
+ """Turn a datetime into a date string as specified in RFC 2822.
+ If usegmt is True, dt must be an aware datetime with an offset of zero. In
+ this case 'GMT' will be rendered instead of the normal +0000 required by
+ RFC2822. This is to support HTTP headers involving date stamps.
+ """
+ now = dt.timetuple()
+ if usegmt:
+ if dt.tzinfo is None or dt.tzinfo != datetime.timezone.utc:
+ raise ValueError("usegmt option requires a UTC datetime")
+ zone = 'GMT'
+ elif dt.tzinfo is None:
+ zone = '-0000'
+ else:
+ zone = dt.strftime("%z")
+ return _format_timetuple_and_zone(now, zone)
def make_msgid(idstring=None, domain=None):
@@ -187,6 +226,15 @@ def parsedate_tz(data):
return None
return _parsedate_tz(data)
+def parsedate_to_datetime(data):
+ if not data:
+ return None
+ *dtuple, tz = __parsedate_tz(data)
+ if tz is None:
+ return datetime.datetime(*dtuple[:6])
+ return datetime.datetime(*dtuple[:6],
+ tzinfo=datetime.timezone(datetime.timedelta(seconds=tz)))
+
def parseaddr(addr):
addrs = _AddressList(addr).addresslist
diff --git a/Lib/encodings/cp65001.py b/Lib/encodings/cp65001.py
new file mode 100644
index 0000000..287eb87
--- /dev/null
+++ b/Lib/encodings/cp65001.py
@@ -0,0 +1,40 @@
+"""
+Code page 65001: Windows UTF-8 (CP_UTF8).
+"""
+
+import codecs
+import functools
+
+if not hasattr(codecs, 'code_page_encode'):
+ raise LookupError("cp65001 encoding is only available on Windows")
+
+### Codec APIs
+
+encode = functools.partial(codecs.code_page_encode, 65001)
+decode = functools.partial(codecs.code_page_decode, 65001)
+
+class IncrementalEncoder(codecs.IncrementalEncoder):
+ def encode(self, input, final=False):
+ return encode(input, self.errors)[0]
+
+class IncrementalDecoder(codecs.BufferedIncrementalDecoder):
+ _buffer_decode = decode
+
+class StreamWriter(codecs.StreamWriter):
+ encode = encode
+
+class StreamReader(codecs.StreamReader):
+ decode = decode
+
+### encodings module API
+
+def getregentry():
+ return codecs.CodecInfo(
+ name='cp65001',
+ encode=encode,
+ decode=decode,
+ incrementalencoder=IncrementalEncoder,
+ incrementaldecoder=IncrementalDecoder,
+ streamreader=StreamReader,
+ streamwriter=StreamWriter,
+ )
diff --git a/Lib/encodings/idna.py b/Lib/encodings/idna.py
index 583bdf1..ea40585 100644
--- a/Lib/encodings/idna.py
+++ b/Lib/encodings/idna.py
@@ -153,6 +153,20 @@ class Codec(codecs.Codec):
if not input:
return b'', 0
+ try:
+ result = input.encode('ascii')
+ except UnicodeEncodeError:
+ pass
+ else:
+ # ASCII name: fast path
+ labels = result.split(b'.')
+ for label in labels[:-1]:
+ if not (0 < len(label) < 64):
+ raise UnicodeError("label empty or too long")
+ if len(labels[-1]) >= 64:
+ raise UnicodeError("label too long")
+ return result, len(input)
+
result = bytearray()
labels = dots.split(input)
if labels and not labels[-1]:
@@ -179,6 +193,14 @@ class Codec(codecs.Codec):
if not isinstance(input, bytes):
# XXX obviously wrong, see #3232
input = bytes(input)
+
+ if ace_prefix not in input:
+ # Fast path
+ try:
+ return input.decode('ascii'), len(input)
+ except UnicodeDecodeError:
+ pass
+
labels = input.split(b".")
if labels and len(labels[-1]) == 0:
diff --git a/Lib/fileinput.py b/Lib/fileinput.py
index 554beb2..dbbbb21 100644
--- a/Lib/fileinput.py
+++ b/Lib/fileinput.py
@@ -398,9 +398,8 @@ def hook_compressed(filename, mode):
def hook_encoded(encoding):
- import codecs
def openhook(filename, mode):
- return codecs.open(filename, mode, encoding)
+ return open(filename, mode, encoding=encoding)
return openhook
diff --git a/Lib/fnmatch.py b/Lib/fnmatch.py
index 726fbe5..f446769 100644
--- a/Lib/fnmatch.py
+++ b/Lib/fnmatch.py
@@ -35,9 +35,9 @@ def fnmatch(name, pat):
pat = os.path.normcase(pat)
return fnmatchcase(name, pat)
-@functools.lru_cache(maxsize=250)
-def _compile_pattern(pat, is_bytes=False):
- if is_bytes:
+@functools.lru_cache(maxsize=250, typed=True)
+def _compile_pattern(pat):
+ if isinstance(pat, bytes):
pat_str = str(pat, 'ISO-8859-1')
res_str = translate(pat_str)
res = bytes(res_str, 'ISO-8859-1')
@@ -49,7 +49,7 @@ def filter(names, pat):
"""Return the subset of the list NAMES that match PAT."""
result = []
pat = os.path.normcase(pat)
- match = _compile_pattern(pat, isinstance(pat, bytes))
+ match = _compile_pattern(pat)
if os.path is posixpath:
# normcase on posix is NOP. Optimize it away from the loop.
for name in names:
@@ -67,7 +67,7 @@ def fnmatchcase(name, pat):
This is a version of fnmatch() which doesn't case-normalize
its arguments.
"""
- match = _compile_pattern(pat, isinstance(pat, bytes))
+ match = _compile_pattern(pat)
return match(name) is not None
diff --git a/Lib/ftplib.py b/Lib/ftplib.py
index 8e53023..18887a6 100644
--- a/Lib/ftplib.py
+++ b/Lib/ftplib.py
@@ -100,14 +100,15 @@ class FTP:
file = None
welcome = None
passiveserver = 1
- encoding = "latin1"
+ encoding = "latin-1"
# Initialization method (called by class instantiation).
# Initialize host to localhost, port to standard ftp port
# Optional arguments are host (for connect()),
# and user, passwd, acct (for login())
def __init__(self, host='', user='', passwd='', acct='',
- timeout=_GLOBAL_DEFAULT_TIMEOUT):
+ timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=None):
+ self.source_address = source_address
self.timeout = timeout
if host:
self.connect(host)
@@ -128,10 +129,12 @@ class FTP:
if self.sock is not None:
self.close()
- def connect(self, host='', port=0, timeout=-999):
+ def connect(self, host='', port=0, timeout=-999, source_address=None):
'''Connect to host. Arguments are:
- host: hostname to connect to (string, default previous host)
- port: port to connect to (integer, default previous port)
+ - source_address: a 2-tuple (host, port) for the socket to bind
+ to as its source address before connecting.
'''
if host != '':
self.host = host
@@ -139,7 +142,10 @@ class FTP:
self.port = port
if timeout != -999:
self.timeout = timeout
- self.sock = socket.create_connection((self.host, self.port), self.timeout)
+ if source_address is not None:
+ self.source_address = source_address
+ self.sock = socket.create_connection((self.host, self.port), self.timeout,
+ source_address=self.source_address)
self.af = self.sock.family
self.file = self.sock.makefile('r', encoding=self.encoding)
self.welcome = self.getresp()
@@ -169,10 +175,8 @@ class FTP:
# Internal: "sanitize" a string for printing
def sanitize(self, s):
- if s[:5] == 'pass ' or s[:5] == 'PASS ':
- i = len(s)
- while i > 5 and s[i-1] in {'\r', '\n'}:
- i = i-1
+ if s[:5] in {'pass ', 'PASS '}:
+ i = len(s.rstrip('\r\n'))
s = s[:5] + '*'*(i-5) + s[i:]
return repr(s)
@@ -335,7 +339,8 @@ class FTP:
size = None
if self.passiveserver:
host, port = self.makepasv()
- conn = socket.create_connection((host, port), self.timeout)
+ conn = socket.create_connection((host, port), self.timeout,
+ source_address=self.source_address)
try:
if rest is not None:
self.sendcmd("REST %s" % rest)
@@ -354,8 +359,7 @@ class FTP:
conn.close()
raise
else:
- sock = self.makeport()
- try:
+ with self.makeport() as sock:
if rest is not None:
self.sendcmd("REST %s" % rest)
resp = self.sendcmd(cmd)
@@ -367,8 +371,6 @@ class FTP:
conn, sockaddr = sock.accept()
if self.timeout is not _GLOBAL_DEFAULT_TIMEOUT:
conn.settimeout(self.timeout)
- finally:
- sock.close()
if resp[:3] == '150':
# this is conditional in case we received a 125
size = parse150(resp)
@@ -426,7 +428,7 @@ class FTP:
"""Retrieve data in line mode. A new port is created for you.
Args:
- cmd: A RETR, LIST, NLST, or MLSD command.
+ cmd: A RETR, LIST, or NLST command.
callback: An optional single parameter callable that is called
for each line with the trailing CRLF stripped.
[default: print_line()]
@@ -527,6 +529,34 @@ class FTP:
cmd = cmd + (' ' + arg)
self.retrlines(cmd, func)
+ def mlsd(self, path="", facts=[]):
+ '''List a directory in a standardized format by using MLSD
+ command (RFC-3659). If path is omitted the current directory
+ is assumed. "facts" is a list of strings representing the type
+ of information desired (e.g. ["type", "size", "perm"]).
+
+ Return a generator object yielding a tuple of two elements
+ for every file found in path.
+ First element is the file name, the second one is a dictionary
+ including a variable number of "facts" depending on the server
+ and whether "facts" argument has been provided.
+ '''
+ if facts:
+ self.sendcmd("OPTS MLST " + ";".join(facts) + ";")
+ if path:
+ cmd = "MLSD %s" % path
+ else:
+ cmd = "MLSD"
+ lines = []
+ self.retrlines(cmd, lines.append)
+ for line in lines:
+ facts_found, _, name = line.rstrip(CRLF).partition(' ')
+ entry = {}
+ for fact in facts_found[:-1].split(";"):
+ key, _, value = fact.partition("=")
+ entry[key.lower()] = value
+ yield (name, entry)
+
def rename(self, fromname, toname):
'''Rename a file.'''
resp = self.sendcmd('RNFR ' + fromname)
@@ -561,10 +591,7 @@ class FTP:
resp = self.sendcmd('SIZE ' + filename)
if resp[:3] == '213':
s = resp[3:].strip()
- try:
- return int(s)
- except (OverflowError, ValueError):
- return int(s)
+ return int(s)
def mkd(self, dirname):
'''Make a directory, return its full pathname.'''
@@ -596,11 +623,11 @@ class FTP:
def close(self):
'''Close the connection without assuming anything about it.'''
- if self.file:
+ if self.file is not None:
self.file.close()
+ if self.sock is not None:
self.sock.close()
- self.file = self.sock = None
-
+ self.file = self.sock = None
try:
import ssl
@@ -644,7 +671,7 @@ else:
def __init__(self, host='', user='', passwd='', acct='', keyfile=None,
certfile=None, context=None,
- timeout=_GLOBAL_DEFAULT_TIMEOUT):
+ timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=None):
if context is not None and keyfile is not None:
raise ValueError("context and keyfile arguments are mutually "
"exclusive")
@@ -655,7 +682,7 @@ else:
self.certfile = certfile
self.context = context
self._prot_p = False
- FTP.__init__(self, host, user, passwd, acct, timeout)
+ FTP.__init__(self, host, user, passwd, acct, timeout, source_address)
def login(self, user='', passwd='', acct='', secure=True):
if secure and not isinstance(self.sock, ssl.SSLSocket):
@@ -679,6 +706,14 @@ else:
self.file = self.sock.makefile(mode='r', encoding=self.encoding)
return resp
+ def ccc(self):
+ '''Switch back to a clear-text control connection.'''
+ if not isinstance(self.sock, ssl.SSLSocket):
+ raise ValueError("not using TLS")
+ resp = self.voidcmd('CCC')
+ self.sock = self.sock.unwrap()
+ return resp
+
def prot_p(self):
'''Set up secure data connection.'''
# PROT defines whether or not the data channel is to be protected.
@@ -715,8 +750,7 @@ else:
def retrbinary(self, cmd, callback, blocksize=8192, rest=None):
self.voidcmd('TYPE I')
- conn = self.transfercmd(cmd, rest)
- try:
+ with self.transfercmd(cmd, rest) as conn:
while 1:
data = conn.recv(blocksize)
if not data:
@@ -725,8 +759,6 @@ else:
# shutdown ssl layer
if isinstance(conn, ssl.SSLSocket):
conn.unwrap()
- finally:
- conn.close()
return self.voidresp()
def retrlines(self, cmd, callback = None):
@@ -734,7 +766,7 @@ else:
resp = self.sendcmd('TYPE A')
conn = self.transfercmd(cmd)
fp = conn.makefile('r', encoding=self.encoding)
- try:
+ with fp, conn:
while 1:
line = fp.readline()
if self.debugging > 2: print('*retr*', repr(line))
@@ -748,15 +780,11 @@ else:
# shutdown ssl layer
if isinstance(conn, ssl.SSLSocket):
conn.unwrap()
- finally:
- fp.close()
- conn.close()
return self.voidresp()
def storbinary(self, cmd, fp, blocksize=8192, callback=None, rest=None):
self.voidcmd('TYPE I')
- conn = self.transfercmd(cmd, rest)
- try:
+ with self.transfercmd(cmd, rest) as conn:
while 1:
buf = fp.read(blocksize)
if not buf: break
@@ -765,14 +793,11 @@ else:
# shutdown ssl layer
if isinstance(conn, ssl.SSLSocket):
conn.unwrap()
- finally:
- conn.close()
return self.voidresp()
def storlines(self, cmd, fp, callback=None):
self.voidcmd('TYPE A')
- conn = self.transfercmd(cmd)
- try:
+ with self.transfercmd(cmd) as conn:
while 1:
buf = fp.readline()
if not buf: break
@@ -784,8 +809,6 @@ else:
# shutdown ssl layer
if isinstance(conn, ssl.SSLSocket):
conn.unwrap()
- finally:
- conn.close()
return self.voidresp()
def abort(self):
@@ -818,11 +841,7 @@ def parse150(resp):
m = _150_re.match(resp)
if not m:
return None
- s = m.group(1)
- try:
- return int(s)
- except (OverflowError, ValueError):
- return int(s)
+ return int(m.group(1))
_227_re = None
diff --git a/Lib/functools.py b/Lib/functools.py
index 85ea257..092b1ab 100644
--- a/Lib/functools.py
+++ b/Lib/functools.py
@@ -21,7 +21,8 @@ except:
# update_wrapper() and wraps() are tools to help write
# wrapper functions that can handle naive introspection
-WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__', '__annotations__')
+WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__',
+ '__annotations__')
WRAPPER_UPDATES = ('__dict__',)
def update_wrapper(wrapper,
wrapped,
@@ -114,14 +115,23 @@ def cmp_to_key(mycmp):
__hash__ = None
return K
+try:
+ from _functools import cmp_to_key
+except ImportError:
+ pass
+
_CacheInfo = namedtuple("CacheInfo", "hits misses maxsize currsize")
-def lru_cache(maxsize=100):
+def lru_cache(maxsize=100, typed=False):
"""Least-recently-used cache decorator.
If *maxsize* is set to None, the LRU features are disabled and the cache
can grow without bound.
+ If *typed* is True, arguments of different types will be cached separately.
+ For example, f(3.0) and f(3) will be treated as distinct calls with
+ distinct results.
+
Arguments to the cached function must be hashable.
View the cache statistics named tuple (hits, misses, maxsize, currsize) with
@@ -137,7 +147,7 @@ def lru_cache(maxsize=100):
# to allow the implementation to change (including a possible C version).
def decorating_function(user_function,
- tuple=tuple, sorted=sorted, len=len, KeyError=KeyError):
+ *, tuple=tuple, sorted=sorted, map=map, len=len, type=type, KeyError=KeyError):
hits = misses = 0
kwd_mark = (object(),) # separates positional and keyword args
@@ -151,7 +161,12 @@ def lru_cache(maxsize=100):
nonlocal hits, misses
key = args
if kwds:
- key += kwd_mark + tuple(sorted(kwds.items()))
+ sorted_items = tuple(sorted(kwds.items()))
+ key += kwd_mark + sorted_items
+ if typed:
+ key += tuple(map(type, args))
+ if kwds:
+ key += tuple(type(v) for k, v in sorted_items)
try:
result = cache[key]
hits += 1
@@ -172,7 +187,12 @@ def lru_cache(maxsize=100):
nonlocal hits, misses
key = args
if kwds:
- key += kwd_mark + tuple(sorted(kwds.items()))
+ sorted_items = tuple(sorted(kwds.items()))
+ key += kwd_mark + sorted_items
+ if typed:
+ key += tuple(map(type, args))
+ if kwds:
+ key += tuple(type(v) for k, v in sorted_items)
with lock:
try:
result = cache[key]
diff --git a/Lib/getopt.py b/Lib/getopt.py
index 980861d..3d6ecbd 100644
--- a/Lib/getopt.py
+++ b/Lib/getopt.py
@@ -19,7 +19,7 @@ option involved with the exception.
# Gerrit Holl <gerrit@nl.linux.org> moved the string-based exceptions
# to class-based exceptions.
#
-# Peter Åstrand <astrand@lysator.liu.se> added gnu_getopt().
+# Peter Åstrand <astrand@lysator.liu.se> added gnu_getopt().
#
# TODO for gnu_getopt():
#
@@ -34,6 +34,11 @@ option involved with the exception.
__all__ = ["GetoptError","error","getopt","gnu_getopt"]
import os
+try:
+ from gettext import gettext as _
+except ImportError:
+ # Bootstrapping Python: gettext's dependencies not built yet
+ def _(s): return s
class GetoptError(Exception):
opt = ''
@@ -153,10 +158,10 @@ def do_longs(opts, opt, longopts, args):
if has_arg:
if optarg is None:
if not args:
- raise GetoptError('option --%s requires argument' % opt, opt)
+ raise GetoptError(_('option --%s requires argument') % opt, opt)
optarg, args = args[0], args[1:]
elif optarg is not None:
- raise GetoptError('option --%s must not have an argument' % opt, opt)
+ raise GetoptError(_('option --%s must not have an argument') % opt, opt)
opts.append(('--' + opt, optarg or ''))
return opts, args
@@ -166,7 +171,7 @@ def do_longs(opts, opt, longopts, args):
def long_has_args(opt, longopts):
possibilities = [o for o in longopts if o.startswith(opt)]
if not possibilities:
- raise GetoptError('option --%s not recognized' % opt, opt)
+ raise GetoptError(_('option --%s not recognized') % opt, opt)
# Is there an exact match?
if opt in possibilities:
return False, opt
@@ -176,7 +181,7 @@ def long_has_args(opt, longopts):
if len(possibilities) > 1:
# XXX since possibilities contains all valid continuations, might be
# nice to work them into the error msg
- raise GetoptError('option --%s not a unique prefix' % opt, opt)
+ raise GetoptError(_('option --%s not a unique prefix') % opt, opt)
assert len(possibilities) == 1
unique_match = possibilities[0]
has_arg = unique_match.endswith('=')
@@ -190,7 +195,7 @@ def do_shorts(opts, optstring, shortopts, args):
if short_has_arg(opt, shortopts):
if optstring == '':
if not args:
- raise GetoptError('option -%s requires argument' % opt,
+ raise GetoptError(_('option -%s requires argument') % opt,
opt)
optstring, args = args[0], args[1:]
optarg, optstring = optstring, ''
@@ -203,7 +208,7 @@ def short_has_arg(opt, shortopts):
for i in range(len(shortopts)):
if opt == shortopts[i] != ':':
return shortopts.startswith(':', i+1)
- raise GetoptError('option -%s not recognized' % opt, opt)
+ raise GetoptError(_('option -%s not recognized') % opt, opt)
if __name__ == '__main__':
import sys
diff --git a/Lib/getpass.py b/Lib/getpass.py
index dc02bd1..0044742 100644
--- a/Lib/getpass.py
+++ b/Lib/getpass.py
@@ -72,7 +72,7 @@ def unix_getpass(prompt='Password: ', stream=None):
finally:
termios.tcsetattr(fd, tcsetattr_flags, old)
stream.flush() # issue7208
- except termios.error as e:
+ except termios.error:
if passwd is not None:
# _raw_input succeeded. The final tcsetattr failed. Reraise
# instead of leaving the terminal in an unknown state.
@@ -145,8 +145,6 @@ def getuser():
"""
- import os
-
for name in ('LOGNAME', 'USER', 'LNAME', 'USERNAME'):
user = os.environ.get(name)
if user:
diff --git a/Lib/gzip.py b/Lib/gzip.py
index ba2149e..e6b8193 100644
--- a/Lib/gzip.py
+++ b/Lib/gzip.py
@@ -16,18 +16,6 @@ FTEXT, FHCRC, FEXTRA, FNAME, FCOMMENT = 1, 2, 4, 8, 16
READ, WRITE = 1, 2
-def U32(i):
- """Return i as an unsigned integer, assuming it fits in 32 bits.
- If it's >= 2GB when viewed as a 32-bit unsigned int, return a long.
- """
- if i < 0:
- i += 1 << 32
- return i
-
-def LOWU32(i):
- """Return the low-order 32 bits, as a non-negative int"""
- return i & 0xFFFFFFFF
-
def write32u(output, value):
# The L format writes the bit pattern correctly whether signed
# or unsigned.
@@ -348,6 +336,28 @@ class GzipFile(io.BufferedIOBase):
self.offset += size
return chunk
+ def read1(self, size=-1):
+ self._check_closed()
+ if self.mode != READ:
+ import errno
+ raise IOError(errno.EBADF, "read1() on write-only GzipFile object")
+
+ if self.extrasize <= 0 and self.fileobj is None:
+ return b''
+
+ try:
+ self._read()
+ except EOFError:
+ pass
+ if size < 0 or size > self.extrasize:
+ size = self.extrasize
+
+ offset = self.offset - self.extrastart
+ chunk = self.extrabuf[offset: offset + size]
+ self.extrasize -= size
+ self.offset += size
+ return chunk
+
def peek(self, n):
if self.mode != READ:
import errno
diff --git a/Lib/http/client.py b/Lib/http/client.py
index 745b999..24feae8 100644
--- a/Lib/http/client.py
+++ b/Lib/http/client.py
@@ -485,11 +485,17 @@ class HTTPResponse(io.RawIOBase):
self.close()
return b""
- if self.chunked:
- return self._read_chunked(amt)
+ if amt is not None:
+ # Amount is given, so call base class version
+ # (which is implemented in terms of self.readinto)
+ return super(HTTPResponse, self).read(amt)
+ else:
+ # Amount is not given (unbounded read) so we must check self.length
+ # and self.chunked
+
+ if self.chunked:
+ return self._readall_chunked()
- if amt is None:
- # unbounded read
if self.length is None:
s = self.fp.read()
else:
@@ -498,78 +504,127 @@ class HTTPResponse(io.RawIOBase):
self.close() # we read everything
return s
+ def readinto(self, b):
+ if self.fp is None:
+ return 0
+
+ if self._method == "HEAD":
+ self.close()
+ return 0
+
+ if self.chunked:
+ return self._readinto_chunked(b)
+
if self.length is not None:
- if amt > self.length:
+ if len(b) > self.length:
# clip the read to the "end of response"
- amt = self.length
+ b = memoryview(b)[0:self.length]
# we do not use _safe_read() here because this may be a .will_close
# connection, and the user is reading more bytes than will be provided
# (for example, reading in 1k chunks)
- s = self.fp.read(amt)
+ n = self.fp.readinto(b)
if self.length is not None:
- self.length -= len(s)
+ self.length -= n
if not self.length:
self.close()
- return s
+ return n
+
+ def _read_next_chunk_size(self):
+ # Read the next chunk size from the file
+ line = self.fp.readline(_MAXLINE + 1)
+ if len(line) > _MAXLINE:
+ raise LineTooLong("chunk size")
+ i = line.find(b";")
+ if i >= 0:
+ line = line[:i] # strip chunk-extensions
+ try:
+ return int(line, 16)
+ except ValueError:
+ # close the connection as protocol synchronisation is
+ # probably lost
+ self.close()
+ raise
- def _read_chunked(self, amt):
+ def _read_and_discard_trailer(self):
+ # read and discard trailer up to the CRLF terminator
+ ### note: we shouldn't have any trailers!
+ while True:
+ line = self.fp.readline(_MAXLINE + 1)
+ if len(line) > _MAXLINE:
+ raise LineTooLong("trailer line")
+ if not line:
+ # a vanishingly small number of sites EOF without
+ # sending the trailer
+ break
+ if line == b"\r\n":
+ break
+
+ def _readall_chunked(self):
assert self.chunked != _UNKNOWN
chunk_left = self.chunk_left
value = []
while True:
if chunk_left is None:
- line = self.fp.readline(_MAXLINE + 1)
- if len(line) > _MAXLINE:
- raise LineTooLong("chunk size")
- i = line.find(b";")
- if i >= 0:
- line = line[:i] # strip chunk-extensions
try:
- chunk_left = int(line, 16)
+ chunk_left = self._read_next_chunk_size()
+ if chunk_left == 0:
+ break
except ValueError:
- # close the connection as protocol synchronisation is
- # probably lost
- self.close()
raise IncompleteRead(b''.join(value))
- if chunk_left == 0:
- break
- if amt is None:
- value.append(self._safe_read(chunk_left))
- elif amt < chunk_left:
- value.append(self._safe_read(amt))
- self.chunk_left = chunk_left - amt
- return b''.join(value)
- elif amt == chunk_left:
- value.append(self._safe_read(amt))
+ value.append(self._safe_read(chunk_left))
+
+ # we read the whole chunk, get another
+ self._safe_read(2) # toss the CRLF at the end of the chunk
+ chunk_left = None
+
+ self._read_and_discard_trailer()
+
+ # we read everything; close the "file"
+ self.close()
+
+ return b''.join(value)
+
+ def _readinto_chunked(self, b):
+ assert self.chunked != _UNKNOWN
+ chunk_left = self.chunk_left
+
+ total_bytes = 0
+ mvb = memoryview(b)
+ while True:
+ if chunk_left is None:
+ try:
+ chunk_left = self._read_next_chunk_size()
+ if chunk_left == 0:
+ break
+ except ValueError:
+ raise IncompleteRead(bytes(b[0:total_bytes]))
+
+ if len(mvb) < chunk_left:
+ n = self._safe_readinto(mvb)
+ self.chunk_left = chunk_left - n
+ return n
+ elif len(mvb) == chunk_left:
+ n = self._safe_readinto(mvb)
self._safe_read(2) # toss the CRLF at the end of the chunk
self.chunk_left = None
- return b''.join(value)
+ return n
else:
- value.append(self._safe_read(chunk_left))
- amt -= chunk_left
+ temp_mvb = mvb[0:chunk_left]
+ n = self._safe_readinto(temp_mvb)
+ mvb = mvb[n:]
+ total_bytes += n
# we read the whole chunk, get another
self._safe_read(2) # toss the CRLF at the end of the chunk
chunk_left = None
- # read and discard trailer up to the CRLF terminator
- ### note: we shouldn't have any trailers!
- while True:
- line = self.fp.readline(_MAXLINE + 1)
- if len(line) > _MAXLINE:
- raise LineTooLong("trailer line")
- if not line:
- # a vanishingly small number of sites EOF without
- # sending the trailer
- break
- if line == b"\r\n":
- break
+ self._read_and_discard_trailer()
# we read everything; close the "file"
self.close()
- return b''.join(value)
+ return total_bytes
def _safe_read(self, amt):
"""Read the number of bytes requested, compensating for partial reads.
@@ -594,6 +649,22 @@ class HTTPResponse(io.RawIOBase):
amt -= len(chunk)
return b"".join(s)
+ def _safe_readinto(self, b):
+ """Same as _safe_read, but for reading into a buffer."""
+ total_bytes = 0
+ mvb = memoryview(b)
+ while total_bytes < len(b):
+ if MAXAMOUNT < len(mvb):
+ temp_mvb = mvb[0:MAXAMOUNT]
+ n = self.fp.readinto(temp_mvb)
+ else:
+ n = self.fp.readinto(mvb)
+ if not n:
+ raise IncompleteRead(bytes(mvb[0:total_bytes]), len(b))
+ mvb = mvb[n:]
+ total_bytes += n
+ return total_bytes
+
def fileno(self):
return self.fp.fileno()
@@ -700,7 +771,7 @@ class HTTPConnection:
self.send(connect_bytes)
for header, value in self._tunnel_headers.items():
header_str = "%s: %s\r\n" % (header, value)
- header_bytes = header_str.encode("latin1")
+ header_bytes = header_str.encode("latin-1")
self.send(header_bytes)
self.send(b'\r\n')
@@ -940,7 +1011,7 @@ class HTTPConnection:
values = list(values)
for i, one_value in enumerate(values):
if hasattr(one_value, 'encode'):
- values[i] = one_value.encode('latin1')
+ values[i] = one_value.encode('latin-1')
elif isinstance(one_value, int):
values[i] = str(one_value).encode('ascii')
value = b'\r\n\t'.join(values)
diff --git a/Lib/http/server.py b/Lib/http/server.py
index 86fa37f..b79d191 100644
--- a/Lib/http/server.py
+++ b/Lib/http/server.py
@@ -105,6 +105,7 @@ import copy
DEFAULT_ERROR_MESSAGE = """\
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
+<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>Error response</title>
@@ -355,6 +356,7 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
"""
self.send_response_only(100)
+ self.flush_headers()
return True
def handle_one_request(self):
@@ -432,7 +434,8 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
self.wfile.write(content.encode('UTF-8', 'replace'))
def send_response(self, code, message=None):
- """Send the response header and log the response code.
+ """Add the response header to the headers buffer and log the
+ response code.
Also send two standard headers with the server software
version and the current date.
@@ -451,16 +454,19 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
else:
message = ''
if self.request_version != 'HTTP/0.9':
- self.wfile.write(("%s %d %s\r\n" %
- (self.protocol_version, code, message)).encode('latin1', 'strict'))
+ if not hasattr(self, '_headers_buffer'):
+ self._headers_buffer = []
+ self._headers_buffer.append(("%s %d %s\r\n" %
+ (self.protocol_version, code, message)).encode(
+ 'latin-1', 'strict'))
def send_header(self, keyword, value):
- """Send a MIME header."""
+ """Send a MIME header to the headers buffer."""
if self.request_version != 'HTTP/0.9':
if not hasattr(self, '_headers_buffer'):
self._headers_buffer = []
self._headers_buffer.append(
- ("%s: %s\r\n" % (keyword, value)).encode('latin1', 'strict'))
+ ("%s: %s\r\n" % (keyword, value)).encode('latin-1', 'strict'))
if keyword.lower() == 'connection':
if value.lower() == 'close':
@@ -472,6 +478,10 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
"""Send the blank line ending the MIME headers."""
if self.request_version != 'HTTP/0.9':
self._headers_buffer.append(b"\r\n")
+ self.flush_headers()
+
+ def flush_headers(self):
+ if hasattr(self, '_headers_buffer'):
self.wfile.write(b"".join(self._headers_buffer))
self._headers_buffer = []
@@ -725,10 +735,16 @@ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
list.sort(key=lambda a: a.lower())
r = []
displaypath = html.escape(urllib.parse.unquote(self.path))
- r.append('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">')
- r.append("<html>\n<title>Directory listing for %s</title>\n" % displaypath)
- r.append("<body>\n<h2>Directory listing for %s</h2>\n" % displaypath)
- r.append("<hr>\n<ul>\n")
+ enc = sys.getfilesystemencoding()
+ title = 'Directory listing for %s' % displaypath
+ r.append('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" '
+ '"http://www.w3.org/TR/html4/strict.dtd">')
+ r.append('<html>\n<head>')
+ r.append('<meta http-equiv="Content-Type" '
+ 'content="text/html; charset=%s">' % enc)
+ r.append('<title>%s</title>\n</head>' % title)
+ r.append('<body>\n<h1>%s</h1>' % title)
+ r.append('<hr>\n<ul>')
for name in list:
fullname = os.path.join(path, name)
displayname = linkname = name
@@ -739,11 +755,10 @@ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
if os.path.islink(fullname):
displayname = name + "@"
# Note: a link to a directory displays with @ and links with /
- r.append('<li><a href="%s">%s</a>\n'
+ r.append('<li><a href="%s">%s</a></li>'
% (urllib.parse.quote(linkname), html.escape(displayname)))
- r.append("</ul>\n<hr>\n</body>\n</html>\n")
- enc = sys.getfilesystemencoding()
- encoded = ''.join(r).encode(enc)
+ r.append('</ul>\n<hr>\n</body>\n</html>\n')
+ encoded = '\n'.join(r).encode(enc)
f = io.BytesIO()
f.write(encoded)
f.seek(0)
@@ -888,11 +903,7 @@ def nobody_uid():
def executable(path):
"""Test for executable file."""
- try:
- st = os.stat(path)
- except os.error:
- return False
- return st.st_mode & 0o111 != 0
+ return os.access(path, os.X_OK)
class CGIHTTPRequestHandler(SimpleHTTPRequestHandler):
@@ -1006,7 +1017,7 @@ class CGIHTTPRequestHandler(SimpleHTTPRequestHandler):
scriptname)
return
ispy = self.is_python(scriptname)
- if not ispy:
+ if self.have_fork or not ispy:
if not self.is_executable(scriptfile):
self.send_error(403, "CGI script is not executable (%r)" %
scriptname)
@@ -1081,6 +1092,7 @@ class CGIHTTPRequestHandler(SimpleHTTPRequestHandler):
env.setdefault(k, "")
self.send_response(200, "Script output follows")
+ self.flush_headers()
decoded_query = query.replace('+', ' ')
diff --git a/Lib/idlelib/PyShell.py b/Lib/idlelib/PyShell.py
index e7c71b7..86e4eed 100644
--- a/Lib/idlelib/PyShell.py
+++ b/Lib/idlelib/PyShell.py
@@ -468,6 +468,10 @@ class ModifiedInterpreter(InteractiveInterpreter):
def kill_subprocess(self):
try:
+ self.rpcclt.listening_sock.close()
+ except AttributeError: # no socket
+ pass
+ try:
self.rpcclt.close()
except AttributeError: # no socket
pass
diff --git a/Lib/idlelib/configHandler.py b/Lib/idlelib/configHandler.py
index 73b8db5..79315ef 100644
--- a/Lib/idlelib/configHandler.py
+++ b/Lib/idlelib/configHandler.py
@@ -145,7 +145,8 @@ class IdleUserConfParser(IdleConfParser):
except IOError:
os.unlink(fname)
cfgFile = open(fname, 'w')
- self.write(cfgFile)
+ with cfgFile:
+ self.write(cfgFile)
else:
self.RemoveFile()
diff --git a/Lib/idlelib/idlever.py b/Lib/idlelib/idlever.py
index 97bf87b..5e9afb1 100644
--- a/Lib/idlelib/idlever.py
+++ b/Lib/idlelib/idlever.py
@@ -1 +1 @@
-IDLE_VERSION = "3.2.2"
+IDLE_VERSION = "3.3a0"
diff --git a/Lib/imaplib.py b/Lib/imaplib.py
index 1fcba21..bda2ae9 100644
--- a/Lib/imaplib.py
+++ b/Lib/imaplib.py
@@ -249,15 +249,7 @@ class IMAP4:
def read(self, size):
"""Read 'size' bytes from remote."""
- chunks = []
- read = 0
- while read < size:
- data = self.file.read(min(size-read, 4096))
- if not data:
- break
- read += len(data)
- chunks.append(data)
- return b''.join(chunks)
+ return self.file.read(size)
def readline(self):
@@ -1177,25 +1169,40 @@ if HAVE_SSL:
"""IMAP4 client class over SSL connection
- Instantiate with: IMAP4_SSL([host[, port[, keyfile[, certfile]]]])
+ Instantiate with: IMAP4_SSL([host[, port[, keyfile[, certfile[, ssl_context]]]]])
host - host's name (default: localhost);
- port - port number (default: standard IMAP4 SSL port).
+ port - port number (default: standard IMAP4 SSL port);
keyfile - PEM formatted file that contains your private key (default: None);
certfile - PEM formatted certificate chain file (default: None);
+ ssl_context - a SSLContext object that contains your certificate chain
+ and private key (default: None)
+ Note: if ssl_context is provided, then parameters keyfile or
+ certfile should not be set otherwise ValueError is thrown.
for more documentation see the docstring of the parent class IMAP4.
"""
- def __init__(self, host = '', port = IMAP4_SSL_PORT, keyfile = None, certfile = None):
+ def __init__(self, host='', port=IMAP4_SSL_PORT, keyfile=None, certfile=None, ssl_context=None):
+ if ssl_context is not None and keyfile is not None:
+ raise ValueError("ssl_context and keyfile arguments are mutually "
+ "exclusive")
+ if ssl_context is not None and certfile is not None:
+ raise ValueError("ssl_context and certfile arguments are mutually "
+ "exclusive")
+
self.keyfile = keyfile
self.certfile = certfile
+ self.ssl_context = ssl_context
IMAP4.__init__(self, host, port)
def _create_socket(self):
sock = IMAP4._create_socket(self)
- return ssl.wrap_socket(sock, self.keyfile, self.certfile)
+ if self.ssl_context:
+ return self.ssl_context.wrap_socket(sock)
+ else:
+ return ssl.wrap_socket(sock, self.keyfile, self.certfile)
def open(self, host='', port=IMAP4_SSL_PORT):
"""Setup connection to remote server on "host:port".
diff --git a/Lib/importlib/__init__.py b/Lib/importlib/__init__.py
index 2baaf93..9b20367 100644
--- a/Lib/importlib/__init__.py
+++ b/Lib/importlib/__init__.py
@@ -81,11 +81,10 @@ except ImportError:
except ImportError:
raise ImportError('posix, nt, or os2 module required for importlib')
_bootstrap._os = _os
-import imp, sys, marshal, errno, _io
+import imp, sys, marshal, _io
_bootstrap.imp = imp
_bootstrap.sys = sys
_bootstrap.marshal = marshal
-_bootstrap.errno = errno
_bootstrap._io = _io
import _warnings
_bootstrap._warnings = _warnings
diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py
index 90eb1a7..520c10a 100644
--- a/Lib/importlib/_bootstrap.py
+++ b/Lib/importlib/_bootstrap.py
@@ -7,7 +7,7 @@ work. One should use importlib as the public-facing version of this module.
"""
-# Injected modules are '_warnings', 'imp', 'sys', 'marshal', 'errno', '_io',
+# Injected modules are '_warnings', 'imp', 'sys', 'marshal', '_io',
# and '_os' (a.k.a. 'posix', 'nt' or 'os2').
# Injected attribute is path_sep.
#
@@ -80,9 +80,38 @@ def _path_absolute(path):
return _path_join(_os.getcwd(), path)
+def _write_atomic(path, data):
+ """Best-effort function to write data to a path atomically.
+ Be prepared to handle a FileExistsError if concurrent writing of the
+ temporary file is attempted."""
+ # Renaming should be atomic on most platforms (including Windows).
+ # Under Windows, the limitation is that we can't rename() to an existing
+ # path, while POSIX will overwrite it. But here we don't really care
+ # if there is a glimpse of time during which the final pyc file doesn't
+ # exist.
+ # id() is used to generate a pseudo-random filename.
+ path_tmp = '{}.{}'.format(path, id(path))
+ fd = _os.open(path_tmp, _os.O_EXCL | _os.O_CREAT | _os.O_WRONLY, 0o666)
+ try:
+ with _io.FileIO(fd, 'wb') as file:
+ file.write(data)
+ try:
+ _os.rename(path_tmp, path)
+ except FileExistsError:
+ # Windows (if we had access to MoveFileEx, we could overwrite)
+ _os.unlink(path)
+ _os.rename(path_tmp, path)
+ except OSError:
+ try:
+ _os.unlink(path_tmp)
+ except OSError:
+ pass
+ raise
+
+
def _wrap(new, old):
"""Simple substitute for functools.wraps."""
- for replace in ['__module__', '__name__', '__doc__']:
+ for replace in ['__module__', '__name__', '__qualname__', '__doc__']:
setattr(new, replace, getattr(old, replace))
new.__dict__.update(old.__dict__)
@@ -240,7 +269,7 @@ class BuiltinImporter:
@classmethod
@_requires_builtin
def is_package(cls, fullname):
- """Return None as built-in module are never packages."""
+ """Return None as built-in modules are never packages."""
return False
@@ -404,6 +433,7 @@ class SourceLoader(_LoaderBasics):
else:
found = marshal.loads(bytes_data)
if isinstance(found, code_type):
+ imp._fix_co_filename(found, source_path)
return found
else:
msg = "Non-code object in {}"
@@ -479,28 +509,19 @@ class _SourceFileLoader(_FileLoader, SourceLoader):
parent = _path_join(parent, part)
try:
_os.mkdir(parent)
- except OSError as exc:
+ except FileExistsError:
# Probably another Python process already created the dir.
- if exc.errno == errno.EEXIST:
- continue
- else:
- raise
- except IOError as exc:
+ continue
+ except PermissionError:
# If can't get proper access, then just forget about writing
# the data.
- if exc.errno == errno.EACCES:
- return
- else:
- raise
- try:
- with _io.FileIO(path, 'wb') as file:
- file.write(data)
- except IOError as exc:
- # Don't worry if you can't write bytecode.
- if exc.errno == errno.EACCES:
return
- else:
- raise
+ try:
+ _write_atomic(path, data)
+ except (PermissionError, FileExistsError):
+ # Don't worry if you can't write bytecode or someone is writing
+ # it at the same time.
+ pass
class _SourcelessFileLoader(_FileLoader, _LoaderBasics):
@@ -758,14 +779,14 @@ class _ImportLockContext:
_IMPLICIT_META_PATH = [BuiltinImporter, FrozenImporter, _DefaultPathFinder]
-_ERR_MSG = 'No module named {}'
+_ERR_MSG = 'No module named {!r}'
def _gcd_import(name, package=None, level=0):
"""Import and return the module based on its name, the package the call is
being made from, and the level adjustment.
This function represents the greatest common denominator of functionality
- between import_module and __import__. This includes settting __package__ if
+ between import_module and __import__. This includes setting __package__ if
the loader did not.
"""
@@ -857,7 +878,7 @@ def __import__(name, globals={}, locals={}, fromlist=[], level=0):
module = _gcd_import(name)
else:
# __package__ is not guaranteed to be defined or could be set to None
- # to represent that it's proper value is unknown
+ # to represent that its proper value is unknown
package = globals.get('__package__')
if package is None:
package = globals['__name__']
diff --git a/Lib/importlib/abc.py b/Lib/importlib/abc.py
index fa343f8..df8cd93 100644
--- a/Lib/importlib/abc.py
+++ b/Lib/importlib/abc.py
@@ -195,7 +195,7 @@ class PyLoader(SourceLoader):
"use SourceLoader instead. "
"See the importlib documentation on how to be "
"compatible with Python 3.1 onwards.",
- PendingDeprecationWarning)
+ DeprecationWarning)
path = self.source_path(fullname)
if path is None:
raise ImportError
@@ -234,7 +234,7 @@ class PyPycLoader(PyLoader):
"removal in Python 3.4; use SourceLoader instead. "
"If Python 3.1 compatibility is required, see the "
"latest documentation for PyLoader.",
- PendingDeprecationWarning)
+ DeprecationWarning)
source_timestamp = self.source_mtime(fullname)
# Try to use bytecode if it is available.
bytecode_path = self.bytecode_path(fullname)
diff --git a/Lib/importlib/test/__main__.py b/Lib/importlib/test/__main__.py
index decc53d..a1990b1 100644
--- a/Lib/importlib/test/__main__.py
+++ b/Lib/importlib/test/__main__.py
@@ -4,7 +4,6 @@ Specifying the ``--builtin`` flag will run tests, where applicable, with
builtins.__import__ instead of importlib.__import__.
"""
-import importlib
from importlib.test.import_ import util
import os.path
from test.support import run_unittest
@@ -13,11 +12,7 @@ import unittest
def test_main():
- if '__pycache__' in __file__:
- parts = __file__.split(os.path.sep)
- start_dir = sep.join(parts[:-2])
- else:
- start_dir = os.path.dirname(__file__)
+ start_dir = os.path.dirname(__file__)
top_dir = os.path.dirname(os.path.dirname(start_dir))
test_loader = unittest.TestLoader()
if '--builtin' in sys.argv:
diff --git a/Lib/importlib/test/regrtest.py b/Lib/importlib/test/regrtest.py
index b103ae7d..dc0eb97 100644
--- a/Lib/importlib/test/regrtest.py
+++ b/Lib/importlib/test/regrtest.py
@@ -5,13 +5,6 @@ invalidates are automatically skipped if the entire test suite is run.
Otherwise all command-line options valid for test.regrtest are also valid for
this script.
-XXX FAILING
- * test_import
- - test_incorrect_code_name
- file name differing between __file__ and co_filename (r68360 on trunk)
- - test_import_by_filename
- exception for trying to import by file name does not match
-
"""
import importlib
import sys
diff --git a/Lib/importlib/test/source/test_abc_loader.py b/Lib/importlib/test/source/test_abc_loader.py
index 3245907..3c19b0b 100644
--- a/Lib/importlib/test/source/test_abc_loader.py
+++ b/Lib/importlib/test/source/test_abc_loader.py
@@ -102,7 +102,7 @@ class PyLoaderMock(abc.PyLoader):
warnings.simplefilter("always")
path = super().get_filename(name)
assert len(w) == 1
- assert issubclass(w[0].category, PendingDeprecationWarning)
+ assert issubclass(w[0].category, DeprecationWarning)
return path
@@ -198,7 +198,7 @@ class PyPycLoaderMock(abc.PyPycLoader, PyLoaderMock):
warnings.simplefilter("always")
code_object = super().get_code(name)
assert len(w) == 1
- assert issubclass(w[0].category, PendingDeprecationWarning)
+ assert issubclass(w[0].category, DeprecationWarning)
return code_object
class PyLoaderTests(testing_abc.LoaderTests):
diff --git a/Lib/importlib/test/test_util.py b/Lib/importlib/test/test_util.py
index 602447f..c7cdad1 100644
--- a/Lib/importlib/test/test_util.py
+++ b/Lib/importlib/test/test_util.py
@@ -59,6 +59,11 @@ class ModuleForLoaderTests(unittest.TestCase):
self.raise_exception(name)
self.assertIs(module, sys.modules[name])
+ def test_decorator_attrs(self):
+ def fxn(self, module): pass
+ wrapped = util.module_for_loader(fxn)
+ self.assertEqual(wrapped.__name__, fxn.__name__)
+ self.assertEqual(wrapped.__qualname__, fxn.__qualname__)
class SetPackageTests(unittest.TestCase):
@@ -108,6 +113,11 @@ class SetPackageTests(unittest.TestCase):
module.__package__ = value
self.verify(module, value)
+ def test_decorator_attrs(self):
+ def fxn(module): pass
+ wrapped = util.set_package(fxn)
+ self.assertEqual(wrapped.__name__, fxn.__name__)
+ self.assertEqual(wrapped.__qualname__, fxn.__qualname__)
def test_main():
from test import support
diff --git a/Lib/inspect.py b/Lib/inspect.py
index 521d2a6..5a22076 100644
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -33,7 +33,6 @@ import sys
import os
import types
import itertools
-import string
import re
import imp
import tokenize
@@ -918,6 +917,43 @@ def formatargvalues(args, varargs, varkw, locals,
specs.append(formatvarkw(varkw) + formatvalue(locals[varkw]))
return '(' + ', '.join(specs) + ')'
+def _missing_arguments(f_name, argnames, pos, values):
+ names = [repr(name) for name in argnames if name not in values]
+ missing = len(names)
+ if missing == 1:
+ s = names[0]
+ elif missing == 2:
+ s = "{} and {}".format(*names)
+ else:
+ tail = ", {} and {}".format(names[-2:])
+ del names[-2:]
+ s = ", ".join(names) + tail
+ raise TypeError("%s() missing %i required %s argument%s: %s" %
+ (f_name, missing,
+ "positional" if pos else "keyword-only",
+ "" if missing == 1 else "s", s))
+
+def _too_many(f_name, args, kwonly, varargs, defcount, given, values):
+ atleast = len(args) - defcount
+ kwonly_given = len([arg for arg in kwonly if arg in values])
+ if varargs:
+ plural = atleast != 1
+ sig = "at least %d" % (atleast,)
+ elif defcount:
+ plural = True
+ sig = "from %d to %d" % (atleast, len(args))
+ else:
+ plural = len(args) != 1
+ sig = str(len(args))
+ kwonly_sig = ""
+ if kwonly_given:
+ msg = " positional argument%s (and %d keyword-only argument%s)"
+ kwonly_sig = (msg % ("s" if given != 1 else "", kwonly_given,
+ "s" if kwonly_given != 1 else ""))
+ raise TypeError("%s() takes %s positional argument%s but %d%s %s given" %
+ (f_name, sig, "s" if plural else "", given, kwonly_sig,
+ "was" if given == 1 and not kwonly_given else "were"))
+
def getcallargs(func, *positional, **named):
"""Get the mapping of arguments to values.
@@ -929,64 +965,53 @@ def getcallargs(func, *positional, **named):
f_name = func.__name__
arg2value = {}
+
if ismethod(func) and func.__self__ is not None:
# implicit 'self' (or 'cls' for classmethods) argument
positional = (func.__self__,) + positional
num_pos = len(positional)
- num_total = num_pos + len(named)
num_args = len(args)
num_defaults = len(defaults) if defaults else 0
- for arg, value in zip(args, positional):
- arg2value[arg] = value
+
+ n = min(num_pos, num_args)
+ for i in range(n):
+ arg2value[args[i]] = positional[i]
if varargs:
- if num_pos > num_args:
- arg2value[varargs] = positional[-(num_pos-num_args):]
- else:
- arg2value[varargs] = ()
- elif 0 < num_args < num_pos:
- raise TypeError('%s() takes %s %d positional %s (%d given)' % (
- f_name, 'at most' if defaults else 'exactly', num_args,
- 'arguments' if num_args > 1 else 'argument', num_total))
- elif num_args == 0 and num_total:
- if varkw or kwonlyargs:
- if num_pos:
- # XXX: We should use num_pos, but Python also uses num_total:
- raise TypeError('%s() takes exactly 0 positional arguments '
- '(%d given)' % (f_name, num_total))
- else:
- raise TypeError('%s() takes no arguments (%d given)' %
- (f_name, num_total))
-
- for arg in itertools.chain(args, kwonlyargs):
- if arg in named:
- if arg in arg2value:
- raise TypeError("%s() got multiple values for keyword "
- "argument '%s'" % (f_name, arg))
- else:
- arg2value[arg] = named.pop(arg)
- for kwonlyarg in kwonlyargs:
- if kwonlyarg not in arg2value:
- try:
- arg2value[kwonlyarg] = kwonlydefaults[kwonlyarg]
- except KeyError:
- raise TypeError("%s() needs keyword-only argument %s" %
- (f_name, kwonlyarg))
- if defaults: # fill in any missing values with the defaults
- for arg, value in zip(args[-num_defaults:], defaults):
- if arg not in arg2value:
- arg2value[arg] = value
+ arg2value[varargs] = tuple(positional[n:])
+ possible_kwargs = set(args + kwonlyargs)
if varkw:
- arg2value[varkw] = named
- elif named:
- unexpected = next(iter(named))
- raise TypeError("%s() got an unexpected keyword argument '%s'" %
- (f_name, unexpected))
- unassigned = num_args - len([arg for arg in args if arg in arg2value])
- if unassigned:
- num_required = num_args - num_defaults
- raise TypeError('%s() takes %s %d %s (%d given)' % (
- f_name, 'at least' if defaults else 'exactly', num_required,
- 'arguments' if num_required > 1 else 'argument', num_total))
+ arg2value[varkw] = {}
+ for kw, value in named.items():
+ if kw not in possible_kwargs:
+ if not varkw:
+ raise TypeError("%s() got an unexpected keyword argument %r" %
+ (f_name, kw))
+ arg2value[varkw][kw] = value
+ continue
+ if kw in arg2value:
+ raise TypeError("%s() got multiple values for argument %r" %
+ (f_name, kw))
+ arg2value[kw] = value
+ if num_pos > num_args and not varargs:
+ _too_many(f_name, args, kwonlyargs, varargs, num_defaults,
+ num_pos, arg2value)
+ if num_pos < num_args:
+ req = args[:num_args - num_defaults]
+ for arg in req:
+ if arg not in arg2value:
+ _missing_arguments(f_name, req, True, arg2value)
+ for i, arg in enumerate(args[num_args - num_defaults:]):
+ if arg not in arg2value:
+ arg2value[arg] = defaults[i]
+ missing = 0
+ for kwarg in kwonlyargs:
+ if kwarg not in arg2value:
+ if kwarg in kwonlydefaults:
+ arg2value[kwarg] = kwonlydefaults[kwarg]
+ else:
+ missing += 1
+ if missing:
+ _missing_arguments(f_name, kwonlyargs, False, arg2value)
return arg2value
# -------------------------------------------------- stack frame extraction
diff --git a/Lib/json/decoder.py b/Lib/json/decoder.py
index 3174e31..e7c0539 100644
--- a/Lib/json/decoder.py
+++ b/Lib/json/decoder.py
@@ -121,8 +121,7 @@ def py_scanstring(s, end, strict=True,
msg = "Invalid \\uXXXX escape"
raise ValueError(errmsg(msg, s, end))
uni = int(esc, 16)
- # Check for surrogate pair on UCS-4 systems
- if 0xd800 <= uni <= 0xdbff and sys.maxunicode > 65535:
+ if 0xd800 <= uni <= 0xdbff:
msg = "Invalid \\uXXXX\\uXXXX surrogate pair"
if not s[end + 5:end + 7] == '\\u':
raise ValueError(errmsg(msg, s, end))
diff --git a/Lib/lib2to3/__main__.py b/Lib/lib2to3/__main__.py
new file mode 100644
index 0000000..80688ba
--- /dev/null
+++ b/Lib/lib2to3/__main__.py
@@ -0,0 +1,4 @@
+import sys
+from .main import main
+
+sys.exit(main("lib2to3.fixes"))
diff --git a/Lib/lib2to3/fixer_base.py b/Lib/lib2to3/fixer_base.py
index afc0467..b176056 100644
--- a/Lib/lib2to3/fixer_base.py
+++ b/Lib/lib2to3/fixer_base.py
@@ -27,7 +27,6 @@ class BaseFix(object):
pattern_tree = None # Tree representation of the pattern
options = None # Options object passed to initializer
filename = None # The filename (set by set_filename)
- logger = None # A logger (set by set_filename)
numbers = itertools.count(1) # For new_name()
used_names = set() # A set of all used NAMEs
order = "post" # Does the fixer prefer pre- or post-order traversal
@@ -70,12 +69,11 @@ class BaseFix(object):
with_tree=True)
def set_filename(self, filename):
- """Set the filename, and a logger derived from it.
+ """Set the filename.
The main refactoring tool should call this.
"""
self.filename = filename
- self.logger = logging.getLogger(filename)
def match(self, node):
"""Returns match for a given parse tree node.
diff --git a/Lib/lib2to3/refactor.py b/Lib/lib2to3/refactor.py
index ae5e40f..3c06405 100644
--- a/Lib/lib2to3/refactor.py
+++ b/Lib/lib2to3/refactor.py
@@ -560,7 +560,7 @@ class RefactoringTool(object):
block_lineno = None
indent = None
lineno = 0
- for line in input.splitlines(True):
+ for line in input.splitlines(keepends=True):
lineno += 1
if line.lstrip().startswith(self.PS1):
if block is not None:
@@ -604,7 +604,7 @@ class RefactoringTool(object):
filename, lineno, err.__class__.__name__, err)
return block
if self.refactor_tree(tree, filename):
- new = str(tree).splitlines(True)
+ new = str(tree).splitlines(keepends=True)
# Undo the adjustment of the line numbers in wrap_toks() below.
clipped, new = new[:lineno-1], new[lineno-1:]
assert clipped == ["\n"] * (lineno-1), clipped
diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py
index 7dacddb..3b2b9bf 100644
--- a/Lib/logging/__init__.py
+++ b/Lib/logging/__init__.py
@@ -1,4 +1,4 @@
-# Copyright 2001-2010 by Vinay Sajip. All Rights Reserved.
+# Copyright 2001-2011 by Vinay Sajip. All Rights Reserved.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose and without fee is hereby granted,
@@ -36,15 +36,9 @@ __all__ = ['BASIC_FORMAT', 'BufferingFormatter', 'CRITICAL', 'DEBUG', 'ERROR',
'getLogRecordFactory', 'setLogRecordFactory', 'lastResort']
try:
- import codecs
-except ImportError:
- codecs = None
-
-try:
- import _thread as thread
import threading
-except ImportError:
- thread = None
+except ImportError: #pragma: no cover
+ threading = None
__author__ = "Vinay Sajip <vinay_sajip@red-dove.com>"
__status__ = "production"
@@ -65,16 +59,16 @@ else:
_srcfile = __file__
_srcfile = os.path.normcase(_srcfile)
-# next bit filched from 1.5.2's inspect.py
-def currentframe():
- """Return the frame object for the caller's stack frame."""
- try:
- raise Exception
- except:
- return sys.exc_info()[2].tb_frame.f_back
-if hasattr(sys, '_getframe'): currentframe = lambda: sys._getframe(3)
-# done filching
+if hasattr(sys, '_getframe'):
+ currentframe = lambda: sys._getframe(3)
+else: #pragma: no cover
+ def currentframe():
+ """Return the frame object for the caller's stack frame."""
+ try:
+ raise Exception
+ except:
+ return sys.exc_info()[2].tb_frame.f_back
# _srcfile is only used in conjunction with sys._getframe().
# To provide compatibility with older versions of Python, set _srcfile
@@ -92,22 +86,22 @@ _startTime = time.time()
#raiseExceptions is used to see if exceptions during handling should be
#propagated
#
-raiseExceptions = 1
+raiseExceptions = True
#
# If you don't want threading information in the log, set this to zero
#
-logThreads = 1
+logThreads = True
#
# If you don't want multiprocessing information in the log, set this to zero
#
-logMultiprocessing = 1
+logMultiprocessing = True
#
# If you don't want process information in the log, set this to zero
#
-logProcesses = 1
+logProcesses = True
#---------------------------------------------------------------------------
# Level related stuff
@@ -197,9 +191,9 @@ def _checkLevel(level):
#the lock would already have been acquired - so we need an RLock.
#The same argument applies to Loggers and Manager.loggerDict.
#
-if thread:
+if threading:
_lock = threading.RLock()
-else:
+else: #pragma: no cover
_lock = None
@@ -252,7 +246,7 @@ class LogRecord(object):
# during formatting, we test to see if the arg is present using
# 'if self.args:'. If the event being logged is e.g. 'Value is %d'
# and if the passed arg fails 'if self.args:' then no formatting
- # is done. For example, logger.warn('Value is %d', 0) would log
+ # is done. For example, logger.warning('Value is %d', 0) would log
# 'Value is %d' instead of 'Value is 0'.
# For the use case of passing a dictionary, this should not be a
# problem.
@@ -276,13 +270,13 @@ class LogRecord(object):
self.created = ct
self.msecs = (ct - int(ct)) * 1000
self.relativeCreated = (self.created - _startTime) * 1000
- if logThreads and thread:
- self.thread = thread.get_ident()
+ if logThreads and threading:
+ self.thread = threading.get_ident()
self.threadName = threading.current_thread().name
- else:
+ else: # pragma: no cover
self.thread = None
self.threadName = None
- if not logMultiprocessing:
+ if not logMultiprocessing: # pragma: no cover
self.processName = None
else:
self.processName = 'MainProcess'
@@ -294,7 +288,7 @@ class LogRecord(object):
# for an example
try:
self.processName = mp.current_process().name
- except StandardError:
+ except StandardError: #pragma: no cover
pass
if logProcesses and hasattr(os, 'getpid'):
self.process = os.getpid()
@@ -466,6 +460,9 @@ class Formatter(object):
self._fmt = self._style._fmt
self.datefmt = datefmt
+ default_time_format = '%Y-%m-%d %H:%M:%S'
+ default_msec_format = '%s,%03d'
+
def formatTime(self, record, datefmt=None):
"""
Return the creation time of the specified LogRecord as formatted text.
@@ -488,8 +485,8 @@ class Formatter(object):
if datefmt:
s = time.strftime(datefmt, ct)
else:
- t = time.strftime("%Y-%m-%d %H:%M:%S", ct)
- s = "%s,%03d" % (t, record.msecs) # the use of % here is internal
+ t = time.strftime(self.default_time_format, ct)
+ s = self.default_msec_format % (t, record.msecs)
return s
def formatException(self, ei):
@@ -642,11 +639,11 @@ class Filter(object):
yes. If deemed appropriate, the record may be modified in-place.
"""
if self.nlen == 0:
- return 1
+ return True
elif self.name == record.name:
- return 1
+ return True
elif record.name.find(self.name, 0, self.nlen) != 0:
- return 0
+ return False
return (record.name[self.nlen] == ".")
class Filterer(object):
@@ -686,14 +683,14 @@ class Filterer(object):
Allow filters to be just callables.
"""
- rv = 1
+ rv = True
for f in self.filters:
if hasattr(f, 'filter'):
result = f.filter(record)
else:
result = f(record) # assume callable - will raise if not
if not result:
- rv = 0
+ rv = False
break
return rv
@@ -771,9 +768,9 @@ class Handler(Filterer):
"""
Acquire a thread lock for serializing access to the underlying I/O.
"""
- if thread:
+ if threading:
self.lock = threading.RLock()
- else:
+ else: #pragma: no cover
self.lock = None
def acquire(self):
@@ -792,7 +789,7 @@ class Handler(Filterer):
def setLevel(self, level):
"""
- Set the logging level of this handler.
+ Set the logging level of this handler. level must be an int or a str.
"""
self.level = _checkLevel(level)
@@ -888,7 +885,7 @@ class Handler(Filterer):
None, sys.stderr)
sys.stderr.write('Logged from file %s, line %s\n' % (
record.filename, record.lineno))
- except IOError:
+ except IOError: #pragma: no cover
pass # see issue 5971
finally:
del ei
@@ -937,7 +934,7 @@ class StreamHandler(Handler):
stream.write(msg)
stream.write(self.terminator)
self.flush()
- except (KeyboardInterrupt, SystemExit):
+ except (KeyboardInterrupt, SystemExit): #pragma: no cover
raise
except:
self.handleError(record)
@@ -946,14 +943,12 @@ class FileHandler(StreamHandler):
"""
A handler class which writes formatted logging records to disk files.
"""
- def __init__(self, filename, mode='a', encoding=None, delay=0):
+ def __init__(self, filename, mode='a', encoding=None, delay=False):
"""
Open the specified file and use it as the stream for logging.
"""
#keep the absolute path, otherwise derived classes which use this
#may come a cropper when the current directory changes
- if codecs is None:
- encoding = None
self.baseFilename = os.path.abspath(filename)
self.mode = mode
self.encoding = encoding
@@ -981,11 +976,7 @@ class FileHandler(StreamHandler):
Open the current base file with the (original) mode and encoding.
Return the resulting stream.
"""
- if self.encoding is None:
- stream = open(self.baseFilename, self.mode)
- else:
- stream = codecs.open(self.baseFilename, self.mode, self.encoding)
- return stream
+ return open(self.baseFilename, self.mode, encoding=self.encoding)
def emit(self, record):
"""
@@ -1197,13 +1188,13 @@ class Logger(Filterer):
self.name = name
self.level = _checkLevel(level)
self.parent = None
- self.propagate = 1
+ self.propagate = True
self.handlers = []
- self.disabled = 0
+ self.disabled = False
def setLevel(self, level):
"""
- Set the logging level of this logger.
+ Set the logging level of this logger. level must be an int or a str.
"""
self.level = _checkLevel(level)
@@ -1243,7 +1234,10 @@ class Logger(Filterer):
if self.isEnabledFor(WARNING):
self._log(WARNING, msg, args, **kwargs)
- warn = warning
+ def warn(self, msg, *args, **kwargs):
+ warnings.warn("The 'warn' method is deprecated, "
+ "use 'warning' instead", DeprecationWarning, 2)
+ self.warning(msg, *args, **kwargs)
def error(self, msg, *args, **kwargs):
"""
@@ -1352,9 +1346,9 @@ class Logger(Filterer):
#IronPython can use logging.
try:
fn, lno, func, sinfo = self.findCaller(stack_info)
- except ValueError:
+ except ValueError: # pragma: no cover
fn, lno, func = "(unknown file)", 0, "(unknown function)"
- else:
+ else: # pragma: no cover
fn, lno, func = "(unknown file)", 0, "(unknown function)"
if exc_info:
if not isinstance(exc_info, tuple):
@@ -1466,7 +1460,7 @@ class Logger(Filterer):
Is this logger enabled for level 'level'?
"""
if self.manager.disable >= level:
- return 0
+ return False
return level >= self.getEffectiveLevel()
def getChild(self, suffix):
@@ -1556,7 +1550,10 @@ class LoggerAdapter(object):
"""
self.log(WARNING, msg, *args, **kwargs)
- warn = warning
+ def warn(self, msg, *args, **kwargs):
+ warnings.warn("The 'warn' method is deprecated, "
+ "use 'warning' instead", DeprecationWarning, 2)
+ self.warning(msg, *args, **kwargs)
def error(self, msg, *args, **kwargs):
"""
@@ -1568,7 +1565,7 @@ class LoggerAdapter(object):
"""
Delegate an exception call to the underlying logger.
"""
- kwargs["exc_info"] = 1
+ kwargs["exc_info"] = True
self.log(ERROR, msg, *args, **kwargs)
def critical(self, msg, *args, **kwargs):
@@ -1651,6 +1648,10 @@ def basicConfig(**kwargs):
stream Use the specified stream to initialize the StreamHandler. Note
that this argument is incompatible with 'filename' - if both
are present, 'stream' is ignored.
+ handlers If specified, this should be an iterable of already created
+ handlers, which will be added to the root handler. Any handler
+ in the list which does not have a formatter assigned will be
+ assigned the formatter created in this function.
Note that you could specify a stream created using open(filename, mode)
rather than passing the filename and mode in. However, it should be
@@ -1658,27 +1659,47 @@ def basicConfig(**kwargs):
using sys.stdout or sys.stderr), whereas FileHandler closes its stream
when the handler is closed.
- .. versionchanged: 3.2
+ .. versionchanged:: 3.2
Added the ``style`` parameter.
+
+ .. versionchanged:: 3.3
+ Added the ``handlers`` parameter. A ``ValueError`` is now thrown for
+ incompatible arguments (e.g. ``handlers`` specified together with
+ ``filename``/``filemode``, or ``filename``/``filemode`` specified
+ together with ``stream``, or ``handlers`` specified together with
+ ``stream``.
"""
# Add thread safety in case someone mistakenly calls
# basicConfig() from multiple threads
_acquireLock()
try:
if len(root.handlers) == 0:
- filename = kwargs.get("filename")
- if filename:
- mode = kwargs.get("filemode", 'a')
- hdlr = FileHandler(filename, mode)
+ handlers = kwargs.get("handlers")
+ if handlers is None:
+ if "stream" in kwargs and "filename" in kwargs:
+ raise ValueError("'stream' and 'filename' should not be "
+ "specified together")
else:
- stream = kwargs.get("stream")
- hdlr = StreamHandler(stream)
+ if "stream" in kwargs or "filename" in kwargs:
+ raise ValueError("'stream' or 'filename' should not be "
+ "specified together with 'handlers'")
+ if handlers is None:
+ filename = kwargs.get("filename")
+ if filename:
+ mode = kwargs.get("filemode", 'a')
+ h = FileHandler(filename, mode)
+ else:
+ stream = kwargs.get("stream")
+ h = StreamHandler(stream)
+ handlers = [h]
fs = kwargs.get("format", BASIC_FORMAT)
dfs = kwargs.get("datefmt", None)
style = kwargs.get("style", '%')
fmt = Formatter(fs, dfs, style)
- hdlr.setFormatter(fmt)
- root.addHandler(hdlr)
+ for h in handlers:
+ if h.formatter is None:
+ h.setFormatter(fmt)
+ root.addHandler(h)
level = kwargs.get("level")
if level is not None:
root.setLevel(level)
@@ -1742,7 +1763,10 @@ def warning(msg, *args, **kwargs):
basicConfig()
root.warning(msg, *args, **kwargs)
-warn = warning
+def warn(msg, *args, **kwargs):
+ warnings.warn("The 'warn' function is deprecated, "
+ "use 'warning' instead", DeprecationWarning, 2)
+ warning(msg, *args, **kwargs)
def info(msg, *args, **kwargs):
"""
@@ -1827,10 +1851,10 @@ class NullHandler(Handler):
package.
"""
def handle(self, record):
- pass
+ """Stub."""
def emit(self, record):
- pass
+ """Stub."""
def createLock(self):
self.lock = None
diff --git a/Lib/logging/config.py b/Lib/logging/config.py
index 373da2b..5ef5c91 100644
--- a/Lib/logging/config.py
+++ b/Lib/logging/config.py
@@ -24,13 +24,13 @@ Copyright (C) 2001-2010 Vinay Sajip. All Rights Reserved.
To use, simply 'import logging' and log away!
"""
-import sys, logging, logging.handlers, socket, struct, os, traceback, re
-import types, io
+import sys, logging, logging.handlers, socket, struct, traceback, re
+import io
try:
import _thread as thread
import threading
-except ImportError:
+except ImportError: #pragma: no cover
thread = None
from socketserver import ThreadingTCPServer, StreamRequestHandler
@@ -98,9 +98,6 @@ def _resolve(name):
def _strip_spaces(alist):
return map(lambda x: x.strip(), alist)
-def _encoded(s):
- return s if isinstance(s, str) else s.encode('utf-8')
-
def _create_formatters(cp):
"""Create and return formatters"""
flist = cp["formatters"]["keys"]
@@ -215,7 +212,7 @@ def _install_loggers(cp, handlers, disable_existing):
#avoid disabling child loggers of explicitly
#named loggers. With a sorted list it is easier
#to find the child loggers.
- existing.sort(key=_encoded)
+ existing.sort()
#We'll keep the list of existing loggers
#which are children of named loggers here...
child_loggers = []
@@ -588,7 +585,7 @@ class DictConfigurator(BaseConfigurator):
#avoid disabling child loggers of explicitly
#named loggers. With a sorted list it is easier
#to find the child loggers.
- existing.sort(key=_encoded)
+ existing.sort()
#We'll keep the list of existing loggers
#which are children of named loggers here...
child_loggers = []
@@ -786,7 +783,7 @@ def listen(port=DEFAULT_LOGGING_CONFIG_PORT):
and which you can join() when appropriate. To stop the server, call
stopListening().
"""
- if not thread:
+ if not thread: #pragma: no cover
raise NotImplementedError("listen() needs threading to work")
class ConfigStreamHandler(StreamRequestHandler):
@@ -804,7 +801,6 @@ def listen(port=DEFAULT_LOGGING_CONFIG_PORT):
struct.pack(">L", n), followed by the config file.
Uses fileConfig() to do the grunt work.
"""
- import tempfile
try:
conn = self.connection
chunk = conn.recv(4)
@@ -825,7 +821,7 @@ def listen(port=DEFAULT_LOGGING_CONFIG_PORT):
file = io.StringIO(chunk)
try:
fileConfig(file)
- except (KeyboardInterrupt, SystemExit):
+ except (KeyboardInterrupt, SystemExit): #pragma: no cover
raise
except:
traceback.print_exc()
diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py
index 4a6b959..52e18e5 100644
--- a/Lib/logging/handlers.py
+++ b/Lib/logging/handlers.py
@@ -25,18 +25,14 @@ To use, simply 'import logging.handlers' and log away!
"""
import logging, socket, os, pickle, struct, time, re
+from codecs import BOM_UTF8
from stat import ST_DEV, ST_INO, ST_MTIME
import queue
try:
import threading
-except ImportError:
+except ImportError: #pragma: no cover
threading = None
-try:
- import codecs
-except ImportError:
- codecs = None
-
#
# Some constants...
#
@@ -60,8 +56,6 @@ class BaseRotatingHandler(logging.FileHandler):
"""
Use the specified filename for streamed logging
"""
- if codecs is None:
- encoding = None
logging.FileHandler.__init__(self, filename, mode, encoding, delay)
self.mode = mode
self.encoding = encoding
@@ -77,7 +71,7 @@ class BaseRotatingHandler(logging.FileHandler):
if self.shouldRollover(record):
self.doRollover()
logging.FileHandler.emit(self, record)
- except (KeyboardInterrupt, SystemExit):
+ except (KeyboardInterrupt, SystemExit): #pragma: no cover
raise
except:
self.handleError(record)
@@ -391,7 +385,7 @@ class WatchedFileHandler(logging.FileHandler):
"""
if not os.path.exists(self.baseFilename):
stat = None
- changed = 1
+ changed = True
else:
stat = os.stat(self.baseFilename)
changed = (stat[ST_DEV] != self.dev) or (stat[ST_INO] != self.ino)
@@ -421,15 +415,15 @@ class SocketHandler(logging.Handler):
"""
Initializes the handler with a specific host address and port.
- The attribute 'closeOnError' is set to 1 - which means that if
- a socket error occurs, the socket is silently closed and then
- reopened on the next logging call.
+ When the attribute *closeOnError* is set to True - if a socket error
+ occurs, the socket is silently closed and then reopened on the next
+ logging call.
"""
logging.Handler.__init__(self)
self.host = host
self.port = port
self.sock = None
- self.closeOnError = 0
+ self.closeOnError = False
self.retryTime = None
#
# Exponential backoff parameters.
@@ -446,8 +440,12 @@ class SocketHandler(logging.Handler):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
if hasattr(s, 'settimeout'):
s.settimeout(timeout)
- s.connect((self.host, self.port))
- return s
+ try:
+ s.connect((self.host, self.port))
+ return s
+ except socket.error:
+ s.close()
+ raise
def createSocket(self):
"""
@@ -460,7 +458,7 @@ class SocketHandler(logging.Handler):
# is the first time back after a disconnect, or
# we've waited long enough.
if self.retryTime is None:
- attempt = 1
+ attempt = True
else:
attempt = (now >= self.retryTime)
if attempt:
@@ -493,14 +491,14 @@ class SocketHandler(logging.Handler):
try:
if hasattr(self.sock, "sendall"):
self.sock.sendall(s)
- else:
+ else: #pragma: no cover
sentsofar = 0
left = len(s)
while left > 0:
sent = self.sock.send(s[sentsofar:])
sentsofar = sentsofar + sent
left = left - sent
- except socket.error:
+ except socket.error: #pragma: no cover
self.sock.close()
self.sock = None # so we can call createSocket next time
@@ -545,7 +543,7 @@ class SocketHandler(logging.Handler):
try:
s = self.makePickle(record)
self.send(s)
- except (KeyboardInterrupt, SystemExit):
+ except (KeyboardInterrupt, SystemExit): #pragma: no cover
raise
except:
self.handleError(record)
@@ -575,7 +573,7 @@ class DatagramHandler(SocketHandler):
Initializes the handler with a specific host address and port.
"""
SocketHandler.__init__(self, host, port)
- self.closeOnError = 0
+ self.closeOnError = False
def makeSocket(self):
"""
@@ -716,10 +714,10 @@ class SysLogHandler(logging.Handler):
self.socktype = socktype
if isinstance(address, str):
- self.unixsocket = 1
+ self.unixsocket = True
self._connect_unixsocket(address)
else:
- self.unixsocket = 0
+ self.unixsocket = False
self.socket = socket.socket(socket.AF_INET, socktype)
if socktype == socket.SOCK_STREAM:
self.socket.connect(address)
@@ -752,8 +750,7 @@ class SysLogHandler(logging.Handler):
"""
Closes the socket.
"""
- if self.unixsocket:
- self.socket.close()
+ self.socket.close()
logging.Handler.close(self)
def mapPriority(self, levelName):
@@ -766,6 +763,7 @@ class SysLogHandler(logging.Handler):
"""
return self.priority_map.get(levelName, "warning")
+ ident = '' # prepended to all messages
append_nul = True # some old syslog daemons expect a NUL terminator
def emit(self, record):
@@ -776,6 +774,8 @@ class SysLogHandler(logging.Handler):
exception information is present, it is NOT sent to the server.
"""
msg = self.format(record)
+ if self.ident:
+ msg = self.ident + msg
if self.append_nul:
msg += '\000'
"""
@@ -787,9 +787,7 @@ class SysLogHandler(logging.Handler):
prio = prio.encode('utf-8')
# Message is a string. Convert to bytes as required by RFC 5424
msg = msg.encode('utf-8')
- if codecs:
- msg = codecs.BOM_UTF8 + msg
- msg = prio + msg
+ msg = prio + BOM_UTF8 + msg
try:
if self.unixsocket:
try:
@@ -801,7 +799,7 @@ class SysLogHandler(logging.Handler):
self.socket.sendto(msg, self.address)
else:
self.socket.sendall(msg)
- except (KeyboardInterrupt, SystemExit):
+ except (KeyboardInterrupt, SystemExit): #pragma: no cover
raise
except:
self.handleError(record)
@@ -878,7 +876,7 @@ class SMTPHandler(logging.Handler):
smtp.login(self.username, self.password)
smtp.sendmail(self.fromaddr, self.toaddrs, msg)
smtp.quit()
- except (KeyboardInterrupt, SystemExit):
+ except (KeyboardInterrupt, SystemExit): #pragma: no cover
raise
except:
self.handleError(record)
@@ -965,7 +963,7 @@ class NTEventLogHandler(logging.Handler):
type = self.getEventType(record)
msg = self.format(record)
self._welu.ReportEvent(self.appname, id, cat, type, [msg])
- except (KeyboardInterrupt, SystemExit):
+ except (KeyboardInterrupt, SystemExit): #pragma: no cover
raise
except:
self.handleError(record)
@@ -1048,9 +1046,11 @@ class HTTPHandler(logging.Handler):
s = ('u%s:%s' % self.credentials).encode('utf-8')
s = 'Basic ' + base64.b64encode(s).strip()
h.putheader('Authorization', s)
- h.endheaders(data if self.method == "POST" else None)
+ h.endheaders()
+ if self.method == "POST":
+ h.send(data.encode('utf-8'))
h.getresponse() #can't do anything with the result
- except (KeyboardInterrupt, SystemExit):
+ except (KeyboardInterrupt, SystemExit): #pragma: no cover
raise
except:
self.handleError(record)
@@ -1220,7 +1220,7 @@ class QueueHandler(logging.Handler):
"""
try:
self.enqueue(self.prepare(record))
- except (KeyboardInterrupt, SystemExit):
+ except (KeyboardInterrupt, SystemExit): #pragma: no cover
raise
except:
self.handleError(record)
@@ -1317,6 +1317,16 @@ if threading:
except queue.Empty:
break
+ def enqueue_sentinel(self):
+ """
+ This is used to enqueue the sentinel record.
+
+ The base implementation uses put_nowait. You may want to override this
+ method if you want to use timeouts or work with custom queue
+ implementations.
+ """
+ self.queue.put_nowait(self._sentinel)
+
def stop(self):
"""
Stop the listener.
@@ -1326,6 +1336,6 @@ if threading:
may be some records still left on the queue, which won't be processed.
"""
self._stop.set()
- self.queue.put_nowait(self._sentinel)
+ self.enqueue_sentinel()
self._thread.join()
self._thread = None
diff --git a/Lib/lzma.py b/Lib/lzma.py
new file mode 100644
index 0000000..780c666
--- /dev/null
+++ b/Lib/lzma.py
@@ -0,0 +1,398 @@
+"""Interface to the liblzma compression library.
+
+This module provides a class for reading and writing compressed files,
+classes for incremental (de)compression, and convenience functions for
+one-shot (de)compression.
+
+These classes and functions support both the XZ and legacy LZMA
+container formats, as well as raw compressed data streams.
+"""
+
+__all__ = [
+ "CHECK_NONE", "CHECK_CRC32", "CHECK_CRC64", "CHECK_SHA256",
+ "CHECK_ID_MAX", "CHECK_UNKNOWN",
+ "FILTER_LZMA1", "FILTER_LZMA2", "FILTER_DELTA", "FILTER_X86", "FILTER_IA64",
+ "FILTER_ARM", "FILTER_ARMTHUMB", "FILTER_POWERPC", "FILTER_SPARC",
+ "FORMAT_AUTO", "FORMAT_XZ", "FORMAT_ALONE", "FORMAT_RAW",
+ "MF_HC3", "MF_HC4", "MF_BT2", "MF_BT3", "MF_BT4",
+ "MODE_FAST", "MODE_NORMAL", "PRESET_DEFAULT", "PRESET_EXTREME",
+
+ "LZMACompressor", "LZMADecompressor", "LZMAFile", "LZMAError",
+ "compress", "decompress", "check_is_supported",
+]
+
+import io
+from _lzma import *
+
+
+_MODE_CLOSED = 0
+_MODE_READ = 1
+_MODE_READ_EOF = 2
+_MODE_WRITE = 3
+
+_BUFFER_SIZE = 8192
+
+
+class LZMAFile(io.BufferedIOBase):
+
+ """A file object providing transparent LZMA (de)compression.
+
+ An LZMAFile can act as a wrapper for an existing file object, or
+ refer directly to a named file on disk.
+
+ Note that LZMAFile provides a *binary* file interface - data read
+ is returned as bytes, and data to be written must be given as bytes.
+ """
+
+ def __init__(self, filename=None, mode="r", *,
+ fileobj=None, format=None, check=-1,
+ preset=None, filters=None):
+ """Open an LZMA-compressed file.
+
+ If filename is given, open the named file. Otherwise, operate on
+ the file object given by fileobj. Exactly one of these two
+ parameters should be provided.
+
+ mode can be "r" for reading (default), "w" for (over)writing, or
+ "a" for appending.
+
+ format specifies the container format to use for the file.
+ If mode is "r", this defaults to FORMAT_AUTO. Otherwise, the
+ default is FORMAT_XZ.
+
+ check specifies the integrity check to use. This argument can
+ only be used when opening a file for writing. For FORMAT_XZ,
+ the default is CHECK_CRC64. FORMAT_ALONE and FORMAT_RAW do not
+ support integrity checks - for these formats, check must be
+ omitted, or be CHECK_NONE.
+
+ When opening a file for reading, the *preset* argument is not
+ meaningful, and should be omitted. The *filters* argument should
+ also be omitted, except when format is FORMAT_RAW (in which case
+ it is required).
+
+ When opening a file for writing, the settings used by the
+ compressor can be specified either as a preset compression
+ level (with the *preset* argument), or in detail as a custom
+ filter chain (with the *filters* argument). For FORMAT_XZ and
+ FORMAT_ALONE, the default is to use the PRESET_DEFAULT preset
+ level. For FORMAT_RAW, the caller must always specify a filter
+ chain; the raw compressor does not support preset compression
+ levels.
+
+ preset (if provided) should be an integer in the range 0-9,
+ optionally OR-ed with the constant PRESET_EXTREME.
+
+ filters (if provided) should be a sequence of dicts. Each dict
+ should have an entry for "id" indicating ID of the filter, plus
+ additional entries for options to the filter.
+ """
+ self._fp = None
+ self._closefp = False
+ self._mode = _MODE_CLOSED
+ self._pos = 0
+ self._size = -1
+
+ if mode == "r":
+ if check != -1:
+ raise ValueError("Cannot specify an integrity check "
+ "when opening a file for reading")
+ if preset is not None:
+ raise ValueError("Cannot specify a preset compression "
+ "level when opening a file for reading")
+ if format is None:
+ format = FORMAT_AUTO
+ mode_code = _MODE_READ
+ # Save the args to pass to the LZMADecompressor initializer.
+ # If the file contains multiple compressed streams, each
+ # stream will need a separate decompressor object.
+ self._init_args = {"format":format, "filters":filters}
+ self._decompressor = LZMADecompressor(**self._init_args)
+ self._buffer = None
+ elif mode in ("w", "a"):
+ if format is None:
+ format = FORMAT_XZ
+ mode_code = _MODE_WRITE
+ self._compressor = LZMACompressor(format=format, check=check,
+ preset=preset, filters=filters)
+ else:
+ raise ValueError("Invalid mode: {!r}".format(mode))
+
+ if filename is not None and fileobj is None:
+ mode += "b"
+ self._fp = open(filename, mode)
+ self._closefp = True
+ self._mode = mode_code
+ elif fileobj is not None and filename is None:
+ self._fp = fileobj
+ self._mode = mode_code
+ else:
+ raise ValueError("Must give exactly one of filename and fileobj")
+
+ def close(self):
+ """Flush and close the file.
+
+ May be called more than once without error. Once the file is
+ closed, any other operation on it will raise a ValueError.
+ """
+ if self._mode == _MODE_CLOSED:
+ return
+ try:
+ if self._mode in (_MODE_READ, _MODE_READ_EOF):
+ self._decompressor = None
+ self._buffer = None
+ elif self._mode == _MODE_WRITE:
+ self._fp.write(self._compressor.flush())
+ self._compressor = None
+ finally:
+ try:
+ if self._closefp:
+ self._fp.close()
+ finally:
+ self._fp = None
+ self._closefp = False
+ self._mode = _MODE_CLOSED
+
+ @property
+ def closed(self):
+ """True if this file is closed."""
+ return self._mode == _MODE_CLOSED
+
+ def fileno(self):
+ """Return the file descriptor for the underlying file."""
+ self._check_not_closed()
+ return self._fp.fileno()
+
+ def seekable(self):
+ """Return whether the file supports seeking."""
+ return self.readable()
+
+ def readable(self):
+ """Return whether the file was opened for reading."""
+ self._check_not_closed()
+ return self._mode in (_MODE_READ, _MODE_READ_EOF)
+
+ def writable(self):
+ """Return whether the file was opened for writing."""
+ self._check_not_closed()
+ return self._mode == _MODE_WRITE
+
+ # Mode-checking helper functions.
+
+ def _check_not_closed(self):
+ if self.closed:
+ raise ValueError("I/O operation on closed file")
+
+ def _check_can_read(self):
+ if not self.readable():
+ raise io.UnsupportedOperation("File not open for reading")
+
+ def _check_can_write(self):
+ if not self.writable():
+ raise io.UnsupportedOperation("File not open for writing")
+
+ def _check_can_seek(self):
+ if not self.seekable():
+ raise io.UnsupportedOperation("Seeking is only supported "
+ "on files open for reading")
+
+ # Fill the readahead buffer if it is empty. Returns False on EOF.
+ def _fill_buffer(self):
+ if self._buffer:
+ return True
+
+ if self._decompressor.unused_data:
+ rawblock = self._decompressor.unused_data
+ else:
+ rawblock = self._fp.read(_BUFFER_SIZE)
+
+ if not rawblock:
+ if self._decompressor.eof:
+ self._mode = _MODE_READ_EOF
+ self._size = self._pos
+ return False
+ else:
+ raise EOFError("Compressed file ended before the "
+ "end-of-stream marker was reached")
+
+ # Continue to next stream.
+ if self._decompressor.eof:
+ self._decompressor = LZMADecompressor(**self._init_args)
+
+ self._buffer = self._decompressor.decompress(rawblock)
+ return True
+
+ # Read data until EOF.
+ # If return_data is false, consume the data without returning it.
+ def _read_all(self, return_data=True):
+ blocks = []
+ while self._fill_buffer():
+ if return_data:
+ blocks.append(self._buffer)
+ self._pos += len(self._buffer)
+ self._buffer = None
+ if return_data:
+ return b"".join(blocks)
+
+ # Read a block of up to n bytes.
+ # If return_data is false, consume the data without returning it.
+ def _read_block(self, n, return_data=True):
+ blocks = []
+ while n > 0 and self._fill_buffer():
+ if n < len(self._buffer):
+ data = self._buffer[:n]
+ self._buffer = self._buffer[n:]
+ else:
+ data = self._buffer
+ self._buffer = None
+ if return_data:
+ blocks.append(data)
+ self._pos += len(data)
+ n -= len(data)
+ if return_data:
+ return b"".join(blocks)
+
+ def peek(self, size=-1):
+ """Return buffered data without advancing the file position.
+
+ Always returns at least one byte of data, unless at EOF.
+ The exact number of bytes returned is unspecified.
+ """
+ self._check_can_read()
+ if self._mode == _MODE_READ_EOF or not self._fill_buffer():
+ return b""
+ return self._buffer
+
+ def read(self, size=-1):
+ """Read up to size uncompressed bytes from the file.
+
+ If size is negative or omitted, read until EOF is reached.
+ Returns b"" if the file is already at EOF.
+ """
+ self._check_can_read()
+ if self._mode == _MODE_READ_EOF or size == 0:
+ return b""
+ elif size < 0:
+ return self._read_all()
+ else:
+ return self._read_block(size)
+
+ def read1(self, size=-1):
+ """Read up to size uncompressed bytes with at most one read
+ from the underlying stream.
+
+ Returns b"" if the file is at EOF.
+ """
+ self._check_can_read()
+ if (size == 0 or self._mode == _MODE_READ_EOF or
+ not self._fill_buffer()):
+ return b""
+ if 0 < size < len(self._buffer):
+ data = self._buffer[:size]
+ self._buffer = self._buffer[size:]
+ else:
+ data = self._buffer
+ self._buffer = None
+ self._pos += len(data)
+ return data
+
+ def write(self, data):
+ """Write a bytes object to the file.
+
+ Returns the number of uncompressed bytes written, which is
+ always len(data). Note that due to buffering, the file on disk
+ may not reflect the data written until close() is called.
+ """
+ self._check_can_write()
+ compressed = self._compressor.compress(data)
+ self._fp.write(compressed)
+ self._pos += len(data)
+ return len(data)
+
+ # Rewind the file to the beginning of the data stream.
+ def _rewind(self):
+ self._fp.seek(0, 0)
+ self._mode = _MODE_READ
+ self._pos = 0
+ self._decompressor = LZMADecompressor(**self._init_args)
+ self._buffer = None
+
+ def seek(self, offset, whence=0):
+ """Change the file position.
+
+ The new position is specified by offset, relative to the
+ position indicated by whence. Possible values for whence are:
+
+ 0: start of stream (default): offset must not be negative
+ 1: current stream position
+ 2: end of stream; offset must not be positive
+
+ Returns the new file position.
+
+ Note that seeking is emulated, sp depending on the parameters,
+ this operation may be extremely slow.
+ """
+ self._check_can_seek()
+
+ # Recalculate offset as an absolute file position.
+ if whence == 0:
+ pass
+ elif whence == 1:
+ offset = self._pos + offset
+ elif whence == 2:
+ # Seeking relative to EOF - we need to know the file's size.
+ if self._size < 0:
+ self._read_all(return_data=False)
+ offset = self._size + offset
+ else:
+ raise ValueError("Invalid value for whence: {}".format(whence))
+
+ # Make it so that offset is the number of bytes to skip forward.
+ if offset < self._pos:
+ self._rewind()
+ else:
+ offset -= self._pos
+
+ # Read and discard data until we reach the desired position.
+ if self._mode != _MODE_READ_EOF:
+ self._read_block(offset, return_data=False)
+
+ return self._pos
+
+ def tell(self):
+ """Return the current file position."""
+ self._check_not_closed()
+ return self._pos
+
+
+def compress(data, format=FORMAT_XZ, check=-1, preset=None, filters=None):
+ """Compress a block of data.
+
+ Refer to LZMACompressor's docstring for a description of the
+ optional arguments *format*, *check*, *preset* and *filters*.
+
+ For incremental compression, use an LZMACompressor object instead.
+ """
+ comp = LZMACompressor(format, check, preset, filters)
+ return comp.compress(data) + comp.flush()
+
+
+def decompress(data, format=FORMAT_AUTO, memlimit=None, filters=None):
+ """Decompress a block of data.
+
+ Refer to LZMADecompressor's docstring for a description of the
+ optional arguments *format*, *check* and *filters*.
+
+ For incremental decompression, use a LZMADecompressor object instead.
+ """
+ results = []
+ while True:
+ decomp = LZMADecompressor(format, memlimit, filters)
+ results.append(decomp.decompress(data))
+ if not decomp.eof:
+ raise LZMAError("Compressed data ended before the "
+ "end-of-stream marker was reached")
+ if not decomp.unused_data:
+ return b"".join(results)
+ # There is unused data left over. Proceed to next stream.
+ data = decomp.unused_data
diff --git a/Lib/mailbox.py b/Lib/mailbox.py
index a677729..325b9c9 100644
--- a/Lib/mailbox.py
+++ b/Lib/mailbox.py
@@ -1106,8 +1106,7 @@ class MH(Mailbox):
def get_sequences(self):
"""Return a name-to-key-list dictionary to define each sequence."""
results = {}
- f = open(os.path.join(self._path, '.mh_sequences'), 'r')
- try:
+ with open(os.path.join(self._path, '.mh_sequences'), 'r', encoding='ASCII') as f:
all_keys = set(self.keys())
for line in f:
try:
@@ -1126,13 +1125,11 @@ class MH(Mailbox):
except ValueError:
raise FormatError('Invalid sequence specification: %s' %
line.rstrip())
- finally:
- f.close()
return results
def set_sequences(self, sequences):
"""Set sequences using the given name-to-key-list dictionary."""
- f = open(os.path.join(self._path, '.mh_sequences'), 'r+')
+ f = open(os.path.join(self._path, '.mh_sequences'), 'r+', encoding='ASCII')
try:
os.close(os.open(f.name, os.O_WRONLY | os.O_TRUNC))
for name, keys in sequences.items():
diff --git a/Lib/mailcap.py b/Lib/mailcap.py
index 4ae13d7..99f4958 100644
--- a/Lib/mailcap.py
+++ b/Lib/mailcap.py
@@ -33,10 +33,10 @@ def getcaps():
def listmailcapfiles():
"""Return a list of all mailcap files found on the system."""
- # XXX Actually, this is Unix-specific
+ # This is mostly a Unix thing, but we use the OS path separator anyway
if 'MAILCAPS' in os.environ:
- str = os.environ['MAILCAPS']
- mailcaps = str.split(':')
+ pathstr = os.environ['MAILCAPS']
+ mailcaps = pathstr.split(os.pathsep)
else:
if 'HOME' in os.environ:
home = os.environ['HOME']
diff --git a/Lib/mimetypes.py b/Lib/mimetypes.py
index 4b1e2f9..464c9a6 100644
--- a/Lib/mimetypes.py
+++ b/Lib/mimetypes.py
@@ -249,7 +249,6 @@ class MimeTypes:
yield ctype
i += 1
- default_encoding = sys.getdefaultencoding()
with _winreg.OpenKey(_winreg.HKEY_CLASSES_ROOT,
r'MIME\Database\Content Type') as mimedb:
for ctype in enum_types(mimedb):
diff --git a/Lib/modulefinder.py b/Lib/modulefinder.py
index f033ba9..c0910aa 100644
--- a/Lib/modulefinder.py
+++ b/Lib/modulefinder.py
@@ -1,6 +1,5 @@
"""Find modules used by a script, using introspection."""
-from __future__ import generators
import dis
import imp
import marshal
@@ -9,8 +8,6 @@ import sys
import types
import struct
-READ_MODE = "rU"
-
# XXX Clean up once str8's cstor matches bytes.
LOAD_CONST = bytes([dis.opname.index('LOAD_CONST')])
IMPORT_NAME = bytes([dis.opname.index('IMPORT_NAME')])
@@ -29,9 +26,7 @@ packagePathMap = {}
# A Public interface
def AddPackagePath(packagename, path):
- paths = packagePathMap.get(packagename, [])
- paths.append(path)
- packagePathMap[packagename] = paths
+ packagePathMap.setdefault(packagename, []).append(path)
replacePackageMap = {}
@@ -106,14 +101,14 @@ class ModuleFinder:
def run_script(self, pathname):
self.msg(2, "run_script", pathname)
- with open(pathname, READ_MODE) as fp:
+ with open(pathname) as fp:
stuff = ("", "r", imp.PY_SOURCE)
self.load_module('__main__', fp, pathname, stuff)
def load_file(self, pathname):
dir, name = os.path.split(pathname)
name, ext = os.path.splitext(name)
- with open(pathname, READ_MODE) as fp:
+ with open(pathname) as fp:
stuff = (ext, "r", imp.PY_SOURCE)
self.load_module(name, fp, pathname, stuff)
@@ -270,7 +265,8 @@ class ModuleFinder:
try:
m = self.load_module(fqname, fp, pathname, stuff)
finally:
- if fp: fp.close()
+ if fp:
+ fp.close()
if parent:
setattr(parent, partname, m)
self.msgout(3, "import_module ->", m)
@@ -662,4 +658,4 @@ if __name__ == '__main__':
try:
mf = test()
except KeyboardInterrupt:
- print("\n[interrupt]")
+ print("\n[interrupted]")
diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py
index d6c23fb..c6c6113 100644
--- a/Lib/multiprocessing/connection.py
+++ b/Lib/multiprocessing/connection.py
@@ -34,19 +34,31 @@
__all__ = [ 'Client', 'Listener', 'Pipe' ]
+import io
import os
import sys
+import pickle
+import select
import socket
+import struct
import errno
import time
import tempfile
import itertools
import _multiprocessing
-from multiprocessing import current_process, AuthenticationError
-from multiprocessing.util import get_temp_dir, Finalize, sub_debug, debug
-from multiprocessing.forking import duplicate, close
+from multiprocessing import current_process, AuthenticationError, BufferTooShort
+from multiprocessing.util import (
+ get_temp_dir, Finalize, sub_debug, debug, _eintr_retry)
+try:
+ from _multiprocessing import win32
+ from _subprocess import WAIT_OBJECT_0, WAIT_TIMEOUT, INFINITE
+except ImportError:
+ if sys.platform == 'win32':
+ raise
+ win32 = None
+_select = _eintr_retry(select.select)
#
#
@@ -110,6 +122,326 @@ def address_type(address):
else:
raise ValueError('address type of %r unrecognized' % address)
+
+class SentinelReady(Exception):
+ """
+ Raised when a sentinel is ready when polling.
+ """
+ def __init__(self, *args):
+ Exception.__init__(self, *args)
+ self.sentinels = args[0]
+
+#
+# Connection classes
+#
+
+class _ConnectionBase:
+ _handle = None
+
+ def __init__(self, handle, readable=True, writable=True):
+ handle = handle.__index__()
+ if handle < 0:
+ raise ValueError("invalid handle")
+ if not readable and not writable:
+ raise ValueError(
+ "at least one of `readable` and `writable` must be True")
+ self._handle = handle
+ self._readable = readable
+ self._writable = writable
+
+ # XXX should we use util.Finalize instead of a __del__?
+
+ def __del__(self):
+ if self._handle is not None:
+ self._close()
+
+ def _check_closed(self):
+ if self._handle is None:
+ raise IOError("handle is closed")
+
+ def _check_readable(self):
+ if not self._readable:
+ raise IOError("connection is write-only")
+
+ def _check_writable(self):
+ if not self._writable:
+ raise IOError("connection is read-only")
+
+ def _bad_message_length(self):
+ if self._writable:
+ self._readable = False
+ else:
+ self.close()
+ raise IOError("bad message length")
+
+ @property
+ def closed(self):
+ """True if the connection is closed"""
+ return self._handle is None
+
+ @property
+ def readable(self):
+ """True if the connection is readable"""
+ return self._readable
+
+ @property
+ def writable(self):
+ """True if the connection is writable"""
+ return self._writable
+
+ def fileno(self):
+ """File descriptor or handle of the connection"""
+ self._check_closed()
+ return self._handle
+
+ def close(self):
+ """Close the connection"""
+ if self._handle is not None:
+ try:
+ self._close()
+ finally:
+ self._handle = None
+
+ def send_bytes(self, buf, offset=0, size=None):
+ """Send the bytes data from a bytes-like object"""
+ self._check_closed()
+ self._check_writable()
+ m = memoryview(buf)
+ # HACK for byte-indexing of non-bytewise buffers (e.g. array.array)
+ if m.itemsize > 1:
+ m = memoryview(bytes(m))
+ n = len(m)
+ if offset < 0:
+ raise ValueError("offset is negative")
+ if n < offset:
+ raise ValueError("buffer length < offset")
+ if size is None:
+ size = n - offset
+ elif size < 0:
+ raise ValueError("size is negative")
+ elif offset + size > n:
+ raise ValueError("buffer length < offset + size")
+ self._send_bytes(m[offset:offset + size])
+
+ def send(self, obj):
+ """Send a (picklable) object"""
+ self._check_closed()
+ self._check_writable()
+ buf = pickle.dumps(obj, protocol=pickle.HIGHEST_PROTOCOL)
+ self._send_bytes(memoryview(buf))
+
+ def recv_bytes(self, maxlength=None):
+ """
+ Receive bytes data as a bytes object.
+ """
+ self._check_closed()
+ self._check_readable()
+ if maxlength is not None and maxlength < 0:
+ raise ValueError("negative maxlength")
+ buf = self._recv_bytes(maxlength)
+ if buf is None:
+ self._bad_message_length()
+ return buf.getvalue()
+
+ def recv_bytes_into(self, buf, offset=0):
+ """
+ Receive bytes data into a writeable buffer-like object.
+ Return the number of bytes read.
+ """
+ self._check_closed()
+ self._check_readable()
+ with memoryview(buf) as m:
+ # Get bytesize of arbitrary buffer
+ itemsize = m.itemsize
+ bytesize = itemsize * len(m)
+ if offset < 0:
+ raise ValueError("negative offset")
+ elif offset > bytesize:
+ raise ValueError("offset too large")
+ result = self._recv_bytes()
+ size = result.tell()
+ if bytesize < offset + size:
+ raise BufferTooShort(result.getvalue())
+ # Message can fit in dest
+ result.seek(0)
+ result.readinto(m[offset // itemsize :
+ (offset + size) // itemsize])
+ return size
+
+ def recv(self, sentinels=None):
+ """Receive a (picklable) object"""
+ self._check_closed()
+ self._check_readable()
+ buf = self._recv_bytes(sentinels=sentinels)
+ return pickle.loads(buf.getbuffer())
+
+ def poll(self, timeout=0.0):
+ """Whether there is any input available to be read"""
+ self._check_closed()
+ self._check_readable()
+ return self._poll(timeout)
+
+
+if win32:
+
+ class PipeConnection(_ConnectionBase):
+ """
+ Connection class based on a Windows named pipe.
+ Overlapped I/O is used, so the handles must have been created
+ with FILE_FLAG_OVERLAPPED.
+ """
+ _buffered = b''
+
+ def _close(self, _CloseHandle=win32.CloseHandle):
+ _CloseHandle(self._handle)
+
+ def _send_bytes(self, buf):
+ overlapped = win32.WriteFile(self._handle, buf, overlapped=True)
+ nwritten, complete = overlapped.GetOverlappedResult(True)
+ assert complete
+ assert nwritten == len(buf)
+
+ def _recv_bytes(self, maxsize=None, sentinels=()):
+ if sentinels:
+ self._poll(-1.0, sentinels)
+ buf = io.BytesIO()
+ firstchunk = self._buffered
+ if firstchunk:
+ lenfirstchunk = len(firstchunk)
+ buf.write(firstchunk)
+ self._buffered = b''
+ else:
+ # A reasonable size for the first chunk transfer
+ bufsize = 128
+ if maxsize is not None and maxsize < bufsize:
+ bufsize = maxsize
+ try:
+ overlapped = win32.ReadFile(self._handle, bufsize, overlapped=True)
+ lenfirstchunk, complete = overlapped.GetOverlappedResult(True)
+ firstchunk = overlapped.getbuffer()
+ assert lenfirstchunk == len(firstchunk)
+ except IOError as e:
+ if e.winerror == win32.ERROR_BROKEN_PIPE:
+ raise EOFError
+ raise
+ buf.write(firstchunk)
+ if complete:
+ return buf
+ navail, nleft = win32.PeekNamedPipe(self._handle)
+ if maxsize is not None and lenfirstchunk + nleft > maxsize:
+ return None
+ if nleft > 0:
+ overlapped = win32.ReadFile(self._handle, nleft, overlapped=True)
+ res, complete = overlapped.GetOverlappedResult(True)
+ assert res == nleft
+ assert complete
+ buf.write(overlapped.getbuffer())
+ return buf
+
+ def _poll(self, timeout, sentinels=()):
+ # Fast non-blocking path
+ navail, nleft = win32.PeekNamedPipe(self._handle)
+ if navail > 0:
+ return True
+ elif timeout == 0.0:
+ return False
+ # Blocking: use overlapped I/O
+ if timeout < 0.0:
+ timeout = INFINITE
+ else:
+ timeout = int(timeout * 1000 + 0.5)
+ overlapped = win32.ReadFile(self._handle, 1, overlapped=True)
+ try:
+ handles = [overlapped.event]
+ handles += sentinels
+ res = win32.WaitForMultipleObjects(handles, False, timeout)
+ finally:
+ # Always cancel overlapped I/O in the same thread
+ # (because CancelIoEx() appears only in Vista)
+ overlapped.cancel()
+ if res == WAIT_TIMEOUT:
+ return False
+ idx = res - WAIT_OBJECT_0
+ if idx == 0:
+ # I/O was successful, store received data
+ overlapped.GetOverlappedResult(True)
+ self._buffered += overlapped.getbuffer()
+ return True
+ assert 0 < idx < len(handles)
+ raise SentinelReady([handles[idx]])
+
+
+class Connection(_ConnectionBase):
+ """
+ Connection class based on an arbitrary file descriptor (Unix only), or
+ a socket handle (Windows).
+ """
+
+ if win32:
+ def _close(self, _close=win32.closesocket):
+ _close(self._handle)
+ _write = win32.send
+ _read = win32.recv
+ else:
+ def _close(self, _close=os.close):
+ _close(self._handle)
+ _write = os.write
+ _read = os.read
+
+ def _send(self, buf, write=_write):
+ remaining = len(buf)
+ while True:
+ n = write(self._handle, buf)
+ remaining -= n
+ if remaining == 0:
+ break
+ buf = buf[n:]
+
+ def _recv(self, size, sentinels=(), read=_read):
+ buf = io.BytesIO()
+ handle = self._handle
+ if sentinels:
+ handles = [handle] + sentinels
+ remaining = size
+ while remaining > 0:
+ if sentinels:
+ r = _select(handles, [], [])[0]
+ if handle not in r:
+ raise SentinelReady(r)
+ chunk = read(handle, remaining)
+ n = len(chunk)
+ if n == 0:
+ if remaining == size:
+ raise EOFError
+ else:
+ raise IOError("got end of file during message")
+ buf.write(chunk)
+ remaining -= n
+ return buf
+
+ def _send_bytes(self, buf):
+ # For wire compatibility with 3.2 and lower
+ n = len(buf)
+ self._send(struct.pack("!i", n))
+ # The condition is necessary to avoid "broken pipe" errors
+ # when sending a 0-length buffer if the other end closed the pipe.
+ if n > 0:
+ self._send(buf)
+
+ def _recv_bytes(self, maxsize=None, sentinels=()):
+ buf = self._recv(4, sentinels)
+ size, = struct.unpack("!i", buf.getvalue())
+ if maxsize is not None and size > maxsize:
+ return None
+ return self._recv(size, sentinels)
+
+ def _poll(self, timeout):
+ if timeout < 0.0:
+ timeout = None
+ r = _select([self._handle], [], [], timeout)[0]
+ return bool(r)
+
+
#
# Public functions
#
@@ -186,21 +518,17 @@ if sys.platform != 'win32':
'''
if duplex:
s1, s2 = socket.socketpair()
- c1 = _multiprocessing.Connection(os.dup(s1.fileno()))
- c2 = _multiprocessing.Connection(os.dup(s2.fileno()))
- s1.close()
- s2.close()
+ c1 = Connection(s1.detach())
+ c2 = Connection(s2.detach())
else:
fd1, fd2 = os.pipe()
- c1 = _multiprocessing.Connection(fd1, writable=False)
- c2 = _multiprocessing.Connection(fd2, readable=False)
+ c1 = Connection(fd1, writable=False)
+ c2 = Connection(fd2, readable=False)
return c1, c2
else:
- from _multiprocessing import win32
-
def Pipe(duplex=True):
'''
Returns pair of connection objects at either end of a pipe
@@ -216,26 +544,24 @@ else:
obsize, ibsize = 0, BUFSIZE
h1 = win32.CreateNamedPipe(
- address, openmode,
+ address, openmode | win32.FILE_FLAG_OVERLAPPED,
win32.PIPE_TYPE_MESSAGE | win32.PIPE_READMODE_MESSAGE |
win32.PIPE_WAIT,
1, obsize, ibsize, win32.NMPWAIT_WAIT_FOREVER, win32.NULL
)
h2 = win32.CreateFile(
- address, access, 0, win32.NULL, win32.OPEN_EXISTING, 0, win32.NULL
+ address, access, 0, win32.NULL, win32.OPEN_EXISTING,
+ win32.FILE_FLAG_OVERLAPPED, win32.NULL
)
win32.SetNamedPipeHandleState(
h2, win32.PIPE_READMODE_MESSAGE, None, None
)
- try:
- win32.ConnectNamedPipe(h1, win32.NULL)
- except WindowsError as e:
- if e.args[0] != win32.ERROR_PIPE_CONNECTED:
- raise
+ overlapped = win32.ConnectNamedPipe(h1, overlapped=True)
+ overlapped.GetOverlappedResult(True)
- c1 = _multiprocessing.PipeConnection(h1, writable=duplex)
- c2 = _multiprocessing.PipeConnection(h2, readable=duplex)
+ c1 = PipeConnection(h1, writable=duplex)
+ c2 = PipeConnection(h2, readable=duplex)
return c1, c2
@@ -266,7 +592,7 @@ class SocketListener(object):
def accept(self):
s, self._last_accepted = self._socket.accept()
fd = duplicate(s.fileno())
- conn = _multiprocessing.Connection(fd)
+ conn = Connection(fd)
s.close()
return conn
@@ -282,23 +608,9 @@ def SocketClient(address):
'''
family = address_type(address)
with socket.socket( getattr(socket, family) ) as s:
- t = _init_timeout()
-
- while 1:
- try:
- s.connect(address)
- except socket.error as e:
- if e.args[0] != errno.ECONNREFUSED or _check_timeout(t):
- debug('failed to connect to address %s', address)
- raise
- time.sleep(0.01)
- else:
- break
- else:
- raise
-
+ s.connect(address)
fd = duplicate(s.fileno())
- conn = _multiprocessing.Connection(fd)
+ conn = Connection(fd)
return conn
#
@@ -343,9 +655,9 @@ if sys.platform == 'win32':
try:
win32.ConnectNamedPipe(handle, win32.NULL)
except WindowsError as e:
- if e.args[0] != win32.ERROR_PIPE_CONNECTED:
+ if e.winerror != win32.ERROR_PIPE_CONNECTED:
raise
- return _multiprocessing.PipeConnection(handle)
+ return PipeConnection(handle)
@staticmethod
def _finalize_pipe_listener(queue, address):
@@ -366,8 +678,8 @@ if sys.platform == 'win32':
0, win32.NULL, win32.OPEN_EXISTING, 0, win32.NULL
)
except WindowsError as e:
- if e.args[0] not in (win32.ERROR_SEM_TIMEOUT,
- win32.ERROR_PIPE_BUSY) or _check_timeout(t):
+ if e.winerror not in (win32.ERROR_SEM_TIMEOUT,
+ win32.ERROR_PIPE_BUSY) or _check_timeout(t):
raise
else:
break
@@ -377,7 +689,7 @@ if sys.platform == 'win32':
win32.SetNamedPipeHandleState(
h, win32.PIPE_READMODE_MESSAGE, None, None
)
- return _multiprocessing.PipeConnection(h)
+ return PipeConnection(h)
#
# Authentication stuff
@@ -434,10 +746,10 @@ class ConnectionWrapper(object):
return self._loads(s)
def _xml_dumps(obj):
- return xmlrpclib.dumps((obj,), None, None, None, 1).encode('utf8')
+ return xmlrpclib.dumps((obj,), None, None, None, 1).encode('utf-8')
def _xml_loads(s):
- (obj,), method = xmlrpclib.loads(s.decode('utf8'))
+ (obj,), method = xmlrpclib.loads(s.decode('utf-8'))
return obj
class XmlListener(Listener):
@@ -451,3 +763,7 @@ def XmlClient(*args, **kwds):
global xmlrpclib
import xmlrpc.client as xmlrpclib
return ConnectionWrapper(Client(*args, **kwds), _xml_dumps, _xml_loads)
+
+
+# Late import because of circular import
+from multiprocessing.forking import duplicate, close
diff --git a/Lib/multiprocessing/dummy/__init__.py b/Lib/multiprocessing/dummy/__init__.py
index c4933d9..056acfc 100644
--- a/Lib/multiprocessing/dummy/__init__.py
+++ b/Lib/multiprocessing/dummy/__init__.py
@@ -46,12 +46,10 @@ import threading
import sys
import weakref
import array
-import itertools
-from multiprocessing import TimeoutError, cpu_count
from multiprocessing.dummy.connection import Pipe
from threading import Lock, RLock, Semaphore, BoundedSemaphore
-from threading import Event
+from threading import Event, Condition
from queue import Queue
#
@@ -84,17 +82,6 @@ class DummyProcess(threading.Thread):
#
#
-class Condition(threading._Condition):
- # XXX
- if sys.version_info < (3, 0):
- notify_all = threading._Condition.notify_all.__func__
- else:
- notify_all = threading._Condition.notify_all
-
-#
-#
-#
-
Process = DummyProcess
current_process = threading.current_thread
current_process()._children = weakref.WeakKeyDictionary()
diff --git a/Lib/multiprocessing/forking.py b/Lib/multiprocessing/forking.py
index cc7c326..47746cb 100644
--- a/Lib/multiprocessing/forking.py
+++ b/Lib/multiprocessing/forking.py
@@ -100,11 +100,12 @@ else:
#
if sys.platform != 'win32':
- import time
+ import select
exit = os._exit
duplicate = os.dup
close = os.close
+ _select = util._eintr_retry(select.select)
#
# We define a Popen class similar to the one from subprocess, but
@@ -118,8 +119,12 @@ if sys.platform != 'win32':
sys.stderr.flush()
self.returncode = None
+ r, w = os.pipe()
+ self.sentinel = r
+
self.pid = os.fork()
if self.pid == 0:
+ os.close(r)
if 'random' in sys.modules:
import random
random.seed()
@@ -128,6 +133,11 @@ if sys.platform != 'win32':
sys.stderr.flush()
os._exit(code)
+ # `w` will be closed when the child exits, at which point `r`
+ # will become ready for reading (using e.g. select()).
+ os.close(w)
+ util.Finalize(self, os.close, (r,))
+
def poll(self, flag=os.WNOHANG):
if self.returncode is None:
try:
@@ -145,26 +155,20 @@ if sys.platform != 'win32':
return self.returncode
def wait(self, timeout=None):
- if timeout is None:
- return self.poll(0)
- deadline = time.time() + timeout
- delay = 0.0005
- while 1:
- res = self.poll()
- if res is not None:
- break
- remaining = deadline - time.time()
- if remaining <= 0:
- break
- delay = min(delay * 2, remaining, 0.05)
- time.sleep(delay)
- return res
+ if self.returncode is None:
+ if timeout is not None:
+ r = _select([self.sentinel], [], [], timeout)[0]
+ if not r:
+ return None
+ # This shouldn't block if select() returned successfully.
+ return self.poll(os.WNOHANG if timeout == 0.0 else 0)
+ return self.returncode
def terminate(self):
if self.returncode is None:
try:
os.kill(self.pid, signal.SIGTERM)
- except OSError as e:
+ except OSError:
if self.wait(timeout=0.1) is None:
raise
@@ -180,11 +184,9 @@ else:
import _thread
import msvcrt
import _subprocess
- import time
- from pickle import dump, load, HIGHEST_PROTOCOL
- from _multiprocessing import win32, Connection, PipeConnection
- from .util import Finalize
+ from pickle import load, HIGHEST_PROTOCOL
+ from _multiprocessing import win32
def dump(obj, file, protocol=None):
ForkingPickler(file, protocol).dump(obj)
@@ -258,6 +260,7 @@ else:
self.pid = pid
self.returncode = None
self._handle = hp
+ self.sentinel = int(hp)
# send information to child
prep_data = get_preparation_data(process_obj._name)
@@ -411,6 +414,9 @@ else:
# Make (Pipe)Connection picklable
#
+ # Late import because of circular import
+ from .connection import Connection, PipeConnection
+
def reduce_connection(conn):
if not Popen.thread_is_spawning():
raise RuntimeError(
diff --git a/Lib/multiprocessing/heap.py b/Lib/multiprocessing/heap.py
index 0a25ef0..7366bd2 100644
--- a/Lib/multiprocessing/heap.py
+++ b/Lib/multiprocessing/heap.py
@@ -34,7 +34,6 @@
import bisect
import mmap
-import tempfile
import os
import sys
import threading
diff --git a/Lib/multiprocessing/managers.py b/Lib/multiprocessing/managers.py
index 5a57288..f42d353 100644
--- a/Lib/multiprocessing/managers.py
+++ b/Lib/multiprocessing/managers.py
@@ -39,19 +39,15 @@ __all__ = [ 'BaseManager', 'SyncManager', 'BaseProxy', 'Token' ]
# Imports
#
-import os
import sys
-import weakref
import threading
import array
import queue
from traceback import format_exc
-from pickle import PicklingError
from multiprocessing import Process, current_process, active_children, Pool, util, connection
from multiprocessing.process import AuthenticationString
-from multiprocessing.forking import exit, Popen, assert_spawning, ForkingPickler
-from multiprocessing.util import Finalize, info
+from multiprocessing.forking import exit, Popen, ForkingPickler
#
# Register some things for pickling
diff --git a/Lib/multiprocessing/process.py b/Lib/multiprocessing/process.py
index 5987af9..98ce0da 100644
--- a/Lib/multiprocessing/process.py
+++ b/Lib/multiprocessing/process.py
@@ -92,12 +92,16 @@ class Process(object):
'''
_Popen = None
- def __init__(self, group=None, target=None, name=None, args=(), kwargs={}):
+ def __init__(self, group=None, target=None, name=None, args=(), kwargs={},
+ *, daemon=None):
assert group is None, 'group argument must be None for now'
count = next(_current_process._counter)
self._identity = _current_process._identity + (count,)
self._authkey = _current_process._authkey
- self._daemonic = _current_process._daemonic
+ if daemon is not None:
+ self._daemonic = daemon
+ else:
+ self._daemonic = _current_process._daemonic
self._tempdir = _current_process._tempdir
self._parent_pid = os.getpid()
self._popen = None
@@ -130,6 +134,7 @@ class Process(object):
else:
from .forking import Popen
self._popen = Popen(self)
+ self._sentinel = self._popen.sentinel
_current_process._children.add(self)
def terminate(self):
@@ -216,6 +221,17 @@ class Process(object):
pid = ident
+ @property
+ def sentinel(self):
+ '''
+ Return a file descriptor (Unix) or handle (Windows) suitable for
+ waiting for process termination.
+ '''
+ try:
+ return self._sentinel
+ except AttributeError:
+ raise ValueError("process not started")
+
def __repr__(self):
if self is _current_process:
status = 'started'
diff --git a/Lib/multiprocessing/queues.py b/Lib/multiprocessing/queues.py
index 51d9912..c4f9cda 100644
--- a/Lib/multiprocessing/queues.py
+++ b/Lib/multiprocessing/queues.py
@@ -39,12 +39,12 @@ import os
import threading
import collections
import time
-import atexit
import weakref
+import errno
from queue import Empty, Full
import _multiprocessing
-from multiprocessing import Pipe
+from multiprocessing.connection import Pipe, SentinelReady
from multiprocessing.synchronize import Lock, BoundedSemaphore, Semaphore, Condition
from multiprocessing.util import debug, info, Finalize, register_after_fork
from multiprocessing.forking import assert_spawning
@@ -67,6 +67,8 @@ class Queue(object):
else:
self._wlock = Lock()
self._sem = BoundedSemaphore(maxsize)
+ # For use by concurrent.futures
+ self._ignore_epipe = False
self._after_fork()
@@ -75,11 +77,11 @@ class Queue(object):
def __getstate__(self):
assert_spawning(self)
- return (self._maxsize, self._reader, self._writer,
+ return (self._ignore_epipe, self._maxsize, self._reader, self._writer,
self._rlock, self._wlock, self._sem, self._opid)
def __setstate__(self, state):
- (self._maxsize, self._reader, self._writer,
+ (self._ignore_epipe, self._maxsize, self._reader, self._writer,
self._rlock, self._wlock, self._sem, self._opid) = state
self._after_fork()
@@ -182,7 +184,7 @@ class Queue(object):
self._thread = threading.Thread(
target=Queue._feed,
args=(self._buffer, self._notempty, self._send,
- self._wlock, self._writer.close),
+ self._wlock, self._writer.close, self._ignore_epipe),
name='QueueFeederThread'
)
self._thread.daemon = True
@@ -233,7 +235,7 @@ class Queue(object):
notempty.release()
@staticmethod
- def _feed(buffer, notempty, send, writelock, close):
+ def _feed(buffer, notempty, send, writelock, close, ignore_epipe):
debug('starting thread to feed data to pipe')
from .util import is_exiting
@@ -275,6 +277,8 @@ class Queue(object):
except IndexError:
pass
except Exception as e:
+ if ignore_epipe and getattr(e, 'errno', 0) == errno.EPIPE:
+ return
# Since this runs in a daemon thread the resources it uses
# may be become unusable while the process is cleaning up.
# We ignore errors which happen after the process has
@@ -376,10 +380,10 @@ class SimpleQueue(object):
def _make_methods(self):
recv = self._reader.recv
racquire, rrelease = self._rlock.acquire, self._rlock.release
- def get():
+ def get(*, sentinels=None):
racquire()
try:
- return recv()
+ return recv(sentinels)
finally:
rrelease()
self.get = get
diff --git a/Lib/multiprocessing/reduction.py b/Lib/multiprocessing/reduction.py
index 6e5e5bc..dda4a41 100644
--- a/Lib/multiprocessing/reduction.py
+++ b/Lib/multiprocessing/reduction.py
@@ -39,19 +39,20 @@ import os
import sys
import socket
import threading
+import struct
-import _multiprocessing
from multiprocessing import current_process
from multiprocessing.forking import Popen, duplicate, close, ForkingPickler
from multiprocessing.util import register_after_fork, debug, sub_debug
-from multiprocessing.connection import Client, Listener
+from multiprocessing.connection import Client, Listener, Connection
#
#
#
-if not(sys.platform == 'win32' or hasattr(_multiprocessing, 'recvfd')):
+if not(sys.platform == 'win32' or (hasattr(socket, 'CMSG_LEN') and
+ hasattr(socket, 'SCM_RIGHTS'))):
raise ImportError('pickling of connections not supported')
#
@@ -59,7 +60,6 @@ if not(sys.platform == 'win32' or hasattr(_multiprocessing, 'recvfd')):
#
if sys.platform == 'win32':
- import _subprocess
from _multiprocessing import win32
def send_handle(conn, handle, destination_pid):
@@ -77,10 +77,23 @@ if sys.platform == 'win32':
else:
def send_handle(conn, handle, destination_pid):
- _multiprocessing.sendfd(conn.fileno(), handle)
+ with socket.fromfd(conn.fileno(), socket.AF_UNIX, socket.SOCK_STREAM) as s:
+ s.sendmsg([b'x'], [(socket.SOL_SOCKET, socket.SCM_RIGHTS,
+ struct.pack("@i", handle))])
def recv_handle(conn):
- return _multiprocessing.recvfd(conn.fileno())
+ size = struct.calcsize("@i")
+ with socket.fromfd(conn.fileno(), socket.AF_UNIX, socket.SOCK_STREAM) as s:
+ msg, ancdata, flags, addr = s.recvmsg(1, socket.CMSG_LEN(size))
+ try:
+ cmsg_level, cmsg_type, cmsg_data = ancdata[0]
+ if (cmsg_level == socket.SOL_SOCKET and
+ cmsg_type == socket.SCM_RIGHTS):
+ return struct.unpack("@i", cmsg_data[:size])[0]
+ except (ValueError, IndexError, struct.error):
+ pass
+ raise RuntimeError('Invalid data received')
+
#
# Support for a per-process server thread which caches pickled handles
@@ -159,7 +172,7 @@ def rebuild_handle(pickled_data):
return new_handle
#
-# Register `_multiprocessing.Connection` with `ForkingPickler`
+# Register `Connection` with `ForkingPickler`
#
def reduce_connection(conn):
@@ -168,11 +181,11 @@ def reduce_connection(conn):
def rebuild_connection(reduced_handle, readable, writable):
handle = rebuild_handle(reduced_handle)
- return _multiprocessing.Connection(
+ return Connection(
handle, readable=readable, writable=writable
)
-ForkingPickler.register(_multiprocessing.Connection, reduce_connection)
+ForkingPickler.register(Connection, reduce_connection)
#
# Register `socket.socket` with `ForkingPickler`
@@ -201,6 +214,7 @@ ForkingPickler.register(socket.socket, reduce_socket)
#
if sys.platform == 'win32':
+ from multiprocessing.connection import PipeConnection
def reduce_pipe_connection(conn):
rh = reduce_handle(conn.fileno())
@@ -208,8 +222,8 @@ if sys.platform == 'win32':
def rebuild_pipe_connection(reduced_handle, readable, writable):
handle = rebuild_handle(reduced_handle)
- return _multiprocessing.PipeConnection(
+ return PipeConnection(
handle, readable=readable, writable=writable
)
- ForkingPickler.register(_multiprocessing.PipeConnection, reduce_pipe_connection)
+ ForkingPickler.register(PipeConnection, reduce_pipe_connection)
diff --git a/Lib/multiprocessing/sharedctypes.py b/Lib/multiprocessing/sharedctypes.py
index 1e694da..5826379 100644
--- a/Lib/multiprocessing/sharedctypes.py
+++ b/Lib/multiprocessing/sharedctypes.py
@@ -32,7 +32,6 @@
# SUCH DAMAGE.
#
-import sys
import ctypes
import weakref
diff --git a/Lib/multiprocessing/synchronize.py b/Lib/multiprocessing/synchronize.py
index 70ae825..e35bbff 100644
--- a/Lib/multiprocessing/synchronize.py
+++ b/Lib/multiprocessing/synchronize.py
@@ -37,14 +37,11 @@ __all__ = [
]
import threading
-import os
import sys
-from time import time as _time, sleep as _sleep
-
import _multiprocessing
from multiprocessing.process import current_process
-from multiprocessing.util import Finalize, register_after_fork, debug
+from multiprocessing.util import register_after_fork, debug
from multiprocessing.forking import assert_spawning, Popen
# Try to import the mp.synchronize module cleanly, if it fails
diff --git a/Lib/multiprocessing/util.py b/Lib/multiprocessing/util.py
index 30b7a85..0bbb87e 100644
--- a/Lib/multiprocessing/util.py
+++ b/Lib/multiprocessing/util.py
@@ -32,6 +32,7 @@
# SUCH DAMAGE.
#
+import functools
import itertools
import weakref
import atexit
@@ -84,7 +85,7 @@ def get_logger():
Returns logger used by multiprocessing
'''
global _logger
- import logging, atexit
+ import logging
logging._acquireLock()
try:
@@ -186,7 +187,11 @@ class Finalize(object):
_finalizer_registry[self._key] = self
- def __call__(self, wr=None):
+ def __call__(self, wr=None,
+ # Need to bind these locally because the globals can have
+ # been cleared at shutdown
+ _finalizer_registry=_finalizer_registry,
+ sub_debug=sub_debug):
'''
Run the callback unless it has already been called or cancelled
'''
@@ -315,3 +320,18 @@ class ForkAwareLocal(threading.local):
register_after_fork(self, lambda obj : obj.__dict__.clear())
def __reduce__(self):
return type(self), ()
+
+
+#
+# Automatic retry after EINTR
+#
+
+def _eintr_retry(func):
+ @functools.wraps(func)
+ def wrapped(*args, **kwargs):
+ while True:
+ try:
+ return func(*args, **kwargs)
+ except InterruptedError:
+ continue
+ return wrapped
diff --git a/Lib/nntplib.py b/Lib/nntplib.py
index bf66734..19a462d 100644
--- a/Lib/nntplib.py
+++ b/Lib/nntplib.py
@@ -346,6 +346,20 @@ class _NNTPBase:
# Log in and encryption setup order is left to subclasses.
self.authenticated = False
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *args):
+ is_connected = lambda: hasattr(self, "file")
+ if is_connected():
+ try:
+ self.quit()
+ except (socket.error, EOFError):
+ pass
+ finally:
+ if is_connected():
+ self._close()
+
def getwelcome(self):
"""Get the welcome message from the server
(this is read and squirreled away by __init__()).
@@ -814,7 +828,7 @@ class _NNTPBase:
- list: list of (name,title) strings"""
warnings.warn("The XGTITLE extension is not actively used, "
"use descriptions() instead",
- PendingDeprecationWarning, 2)
+ DeprecationWarning, 2)
line_pat = re.compile('^([^ \t]+)[ \t]+(.*)$')
resp, raw_lines = self._longcmdstring('XGTITLE ' + group, file)
lines = []
@@ -832,7 +846,7 @@ class _NNTPBase:
path: directory path to article
"""
warnings.warn("The XPATH extension is not actively used",
- PendingDeprecationWarning, 2)
+ DeprecationWarning, 2)
resp = self._shortcmd('XPATH {0}'.format(id))
if not resp.startswith('223'):
diff --git a/Lib/numbers.py b/Lib/numbers.py
index ecfad7c..b206457 100644
--- a/Lib/numbers.py
+++ b/Lib/numbers.py
@@ -5,7 +5,7 @@
TODO: Fill out more detailed documentation on the operators."""
-from abc import ABCMeta, abstractmethod, abstractproperty
+from abc import ABCMeta, abstractmethod
__all__ = ["Number", "Complex", "Real", "Rational", "Integral"]
@@ -50,7 +50,8 @@ class Complex(Number):
"""True if self != 0. Called for bool(self)."""
return self != 0
- @abstractproperty
+ @property
+ @abstractmethod
def real(self):
"""Retrieve the real component of this number.
@@ -58,7 +59,8 @@ class Complex(Number):
"""
raise NotImplementedError
- @abstractproperty
+ @property
+ @abstractmethod
def imag(self):
"""Retrieve the imaginary component of this number.
@@ -272,11 +274,13 @@ class Rational(Real):
__slots__ = ()
- @abstractproperty
+ @property
+ @abstractmethod
def numerator(self):
raise NotImplementedError
- @abstractproperty
+ @property
+ @abstractmethod
def denominator(self):
raise NotImplementedError
diff --git a/Lib/opcode.py b/Lib/opcode.py
index 8e15d13..b631b25 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -43,7 +43,6 @@ def jabs_op(name, op):
# Instruction opcodes for compiled code
# Blank lines correspond to available opcodes
-def_op('STOP_CODE', 0)
def_op('POP_TOP', 1)
def_op('ROT_TWO', 2)
def_op('ROT_THREE', 3)
diff --git a/Lib/optparse.py b/Lib/optparse.py
index d97a1f7..37764d3 100644
--- a/Lib/optparse.py
+++ b/Lib/optparse.py
@@ -86,10 +86,16 @@ def _repr(self):
# Id: errors.py 509 2006-04-20 00:58:24Z gward
try:
- from gettext import gettext
+ from gettext import gettext, ngettext
except ImportError:
def gettext(message):
return message
+
+ def ngettext(singular, plural, n):
+ if n == 1:
+ return singular
+ return plural
+
_ = gettext
@@ -411,11 +417,8 @@ def _parse_num(val, type):
def _parse_int(val):
return _parse_num(val, int)
-def _parse_long(val):
- return _parse_num(val, int)
-
_builtin_cvt = { "int" : (_parse_int, _("integer")),
- "long" : (_parse_long, _("long integer")),
+ "long" : (_parse_int, _("integer")),
"float" : (float, _("floating-point")),
"complex" : (complex, _("complex")) }
@@ -1483,11 +1486,10 @@ class OptionParser (OptionContainer):
if option.takes_value():
nargs = option.nargs
if len(rargs) < nargs:
- if nargs == 1:
- self.error(_("%s option requires an argument") % opt)
- else:
- self.error(_("%s option requires %d arguments")
- % (opt, nargs))
+ self.error(ngettext(
+ "%(option)s option requires %(number)d argument",
+ "%(option)s option requires %(number)d arguments",
+ nargs) % {"option": opt, "number": nargs})
elif nargs == 1:
value = rargs.pop(0)
else:
@@ -1522,11 +1524,10 @@ class OptionParser (OptionContainer):
nargs = option.nargs
if len(rargs) < nargs:
- if nargs == 1:
- self.error(_("%s option requires an argument") % opt)
- else:
- self.error(_("%s option requires %d arguments")
- % (opt, nargs))
+ self.error(ngettext(
+ "%(option)s option requires %(number)d argument",
+ "%(option)s option requires %(number)d arguments",
+ nargs) % {"option": opt, "number": nargs})
elif nargs == 1:
value = rargs.pop(0)
else:
diff --git a/Lib/os.py b/Lib/os.py
index a894ee0..28979bf 100644
--- a/Lib/os.py
+++ b/Lib/os.py
@@ -434,7 +434,7 @@ def get_exec_path(env=None):
# Change environ to automatically call putenv(), unsetenv if they exist.
-from _abcoll import MutableMapping # Can't use collections (bootstrap)
+from collections.abc import MutableMapping
class _Environ(MutableMapping):
def __init__(self, data, encodekey, decodekey, encodevalue, decodevalue, putenv, unsetenv):
diff --git a/Lib/packaging/__init__.py b/Lib/packaging/__init__.py
new file mode 100644
index 0000000..93b6117
--- /dev/null
+++ b/Lib/packaging/__init__.py
@@ -0,0 +1,17 @@
+"""Support for packaging, distribution and installation of Python projects.
+
+Third-party tools can use parts of packaging as building blocks
+without causing the other modules to be imported:
+
+ import packaging.version
+ import packaging.metadata
+ import packaging.pypi.simple
+ import packaging.tests.pypi_server
+"""
+
+from logging import getLogger
+
+__all__ = ['__version__', 'logger']
+
+__version__ = "1.0a3"
+logger = getLogger('packaging')
diff --git a/Lib/packaging/_trove.py b/Lib/packaging/_trove.py
new file mode 100644
index 0000000..f527bc4
--- /dev/null
+++ b/Lib/packaging/_trove.py
@@ -0,0 +1,571 @@
+"""Temporary helper for create."""
+
+# XXX get the list from PyPI and cache it instead of hardcoding
+
+# XXX see if it would be more useful to store it as another structure
+# than a list of strings
+
+all_classifiers = [
+'Development Status :: 1 - Planning',
+'Development Status :: 2 - Pre-Alpha',
+'Development Status :: 3 - Alpha',
+'Development Status :: 4 - Beta',
+'Development Status :: 5 - Production/Stable',
+'Development Status :: 6 - Mature',
+'Development Status :: 7 - Inactive',
+'Environment :: Console',
+'Environment :: Console :: Curses',
+'Environment :: Console :: Framebuffer',
+'Environment :: Console :: Newt',
+'Environment :: Console :: svgalib',
+"Environment :: Handhelds/PDA's",
+'Environment :: MacOS X',
+'Environment :: MacOS X :: Aqua',
+'Environment :: MacOS X :: Carbon',
+'Environment :: MacOS X :: Cocoa',
+'Environment :: No Input/Output (Daemon)',
+'Environment :: Other Environment',
+'Environment :: Plugins',
+'Environment :: Web Environment',
+'Environment :: Web Environment :: Buffet',
+'Environment :: Web Environment :: Mozilla',
+'Environment :: Web Environment :: ToscaWidgets',
+'Environment :: Win32 (MS Windows)',
+'Environment :: X11 Applications',
+'Environment :: X11 Applications :: Gnome',
+'Environment :: X11 Applications :: GTK',
+'Environment :: X11 Applications :: KDE',
+'Environment :: X11 Applications :: Qt',
+'Framework :: BFG',
+'Framework :: Buildout',
+'Framework :: Buildout :: Extension',
+'Framework :: Buildout :: Recipe',
+'Framework :: Chandler',
+'Framework :: CherryPy',
+'Framework :: CubicWeb',
+'Framework :: Django',
+'Framework :: IDLE',
+'Framework :: Paste',
+'Framework :: Plone',
+'Framework :: Plone :: 3.2',
+'Framework :: Plone :: 3.3',
+'Framework :: Plone :: 4.0',
+'Framework :: Plone :: 4.1',
+'Framework :: Plone :: 4.2',
+'Framework :: Plone :: 4.3',
+'Framework :: Pylons',
+'Framework :: Setuptools Plugin',
+'Framework :: Trac',
+'Framework :: Tryton',
+'Framework :: TurboGears',
+'Framework :: TurboGears :: Applications',
+'Framework :: TurboGears :: Widgets',
+'Framework :: Twisted',
+'Framework :: ZODB',
+'Framework :: Zope2',
+'Framework :: Zope3',
+'Intended Audience :: Customer Service',
+'Intended Audience :: Developers',
+'Intended Audience :: Education',
+'Intended Audience :: End Users/Desktop',
+'Intended Audience :: Financial and Insurance Industry',
+'Intended Audience :: Healthcare Industry',
+'Intended Audience :: Information Technology',
+'Intended Audience :: Legal Industry',
+'Intended Audience :: Manufacturing',
+'Intended Audience :: Other Audience',
+'Intended Audience :: Religion',
+'Intended Audience :: Science/Research',
+'Intended Audience :: System Administrators',
+'Intended Audience :: Telecommunications Industry',
+'License :: Aladdin Free Public License (AFPL)',
+'License :: CC0 1.0 Universal (CC0 1.0) Public Domain Dedication',
+'License :: DFSG approved',
+'License :: Eiffel Forum License (EFL)',
+'License :: Free For Educational Use',
+'License :: Free For Home Use',
+'License :: Free for non-commercial use',
+'License :: Freely Distributable',
+'License :: Free To Use But Restricted',
+'License :: Freeware',
+'License :: Netscape Public License (NPL)',
+'License :: Nokia Open Source License (NOKOS)',
+'License :: OSI Approved',
+'License :: OSI Approved :: Academic Free License (AFL)',
+'License :: OSI Approved :: Apache Software License',
+'License :: OSI Approved :: Apple Public Source License',
+'License :: OSI Approved :: Artistic License',
+'License :: OSI Approved :: Attribution Assurance License',
+'License :: OSI Approved :: BSD License',
+'License :: OSI Approved :: Common Public License',
+'License :: OSI Approved :: Eiffel Forum License',
+'License :: OSI Approved :: European Union Public Licence 1.0 (EUPL 1.0)',
+'License :: OSI Approved :: European Union Public Licence 1.1 (EUPL 1.1)',
+'License :: OSI Approved :: GNU Affero General Public License v3',
+'License :: OSI Approved :: GNU Free Documentation License (FDL)',
+'License :: OSI Approved :: GNU General Public License (GPL)',
+'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)',
+'License :: OSI Approved :: IBM Public License',
+'License :: OSI Approved :: Intel Open Source License',
+'License :: OSI Approved :: ISC License (ISCL)',
+'License :: OSI Approved :: Jabber Open Source License',
+'License :: OSI Approved :: MIT License',
+'License :: OSI Approved :: MITRE Collaborative Virtual Workspace License (CVW)',
+'License :: OSI Approved :: Motosoto License',
+'License :: OSI Approved :: Mozilla Public License 1.0 (MPL)',
+'License :: OSI Approved :: Mozilla Public License 1.1 (MPL 1.1)',
+'License :: OSI Approved :: Nethack General Public License',
+'License :: OSI Approved :: Nokia Open Source License',
+'License :: OSI Approved :: Open Group Test Suite License',
+'License :: OSI Approved :: Python License (CNRI Python License)',
+'License :: OSI Approved :: Python Software Foundation License',
+'License :: OSI Approved :: Qt Public License (QPL)',
+'License :: OSI Approved :: Ricoh Source Code Public License',
+'License :: OSI Approved :: Sleepycat License',
+'License :: OSI Approved :: Sun Industry Standards Source License (SISSL)',
+'License :: OSI Approved :: Sun Public License',
+'License :: OSI Approved :: University of Illinois/NCSA Open Source License',
+'License :: OSI Approved :: Vovida Software License 1.0',
+'License :: OSI Approved :: W3C License',
+'License :: OSI Approved :: X.Net License',
+'License :: OSI Approved :: zlib/libpng License',
+'License :: OSI Approved :: Zope Public License',
+'License :: Other/Proprietary License',
+'License :: Public Domain',
+'License :: Repoze Public License',
+'Natural Language :: Afrikaans',
+'Natural Language :: Arabic',
+'Natural Language :: Bengali',
+'Natural Language :: Bosnian',
+'Natural Language :: Bulgarian',
+'Natural Language :: Catalan',
+'Natural Language :: Chinese (Simplified)',
+'Natural Language :: Chinese (Traditional)',
+'Natural Language :: Croatian',
+'Natural Language :: Czech',
+'Natural Language :: Danish',
+'Natural Language :: Dutch',
+'Natural Language :: English',
+'Natural Language :: Esperanto',
+'Natural Language :: Finnish',
+'Natural Language :: French',
+'Natural Language :: German',
+'Natural Language :: Greek',
+'Natural Language :: Hebrew',
+'Natural Language :: Hindi',
+'Natural Language :: Hungarian',
+'Natural Language :: Icelandic',
+'Natural Language :: Indonesian',
+'Natural Language :: Italian',
+'Natural Language :: Japanese',
+'Natural Language :: Javanese',
+'Natural Language :: Korean',
+'Natural Language :: Latin',
+'Natural Language :: Latvian',
+'Natural Language :: Macedonian',
+'Natural Language :: Malay',
+'Natural Language :: Marathi',
+'Natural Language :: Norwegian',
+'Natural Language :: Panjabi',
+'Natural Language :: Persian',
+'Natural Language :: Polish',
+'Natural Language :: Portuguese',
+'Natural Language :: Portuguese (Brazilian)',
+'Natural Language :: Romanian',
+'Natural Language :: Russian',
+'Natural Language :: Serbian',
+'Natural Language :: Slovak',
+'Natural Language :: Slovenian',
+'Natural Language :: Spanish',
+'Natural Language :: Swedish',
+'Natural Language :: Tamil',
+'Natural Language :: Telugu',
+'Natural Language :: Thai',
+'Natural Language :: Turkish',
+'Natural Language :: Ukranian',
+'Natural Language :: Urdu',
+'Natural Language :: Vietnamese',
+'Operating System :: BeOS',
+'Operating System :: MacOS',
+'Operating System :: MacOS :: MacOS 9',
+'Operating System :: MacOS :: MacOS X',
+'Operating System :: Microsoft',
+'Operating System :: Microsoft :: MS-DOS',
+'Operating System :: Microsoft :: Windows',
+'Operating System :: Microsoft :: Windows :: Windows 3.1 or Earlier',
+'Operating System :: Microsoft :: Windows :: Windows 95/98/2000',
+'Operating System :: Microsoft :: Windows :: Windows CE',
+'Operating System :: Microsoft :: Windows :: Windows NT/2000',
+'Operating System :: OS/2',
+'Operating System :: OS Independent',
+'Operating System :: Other OS',
+'Operating System :: PalmOS',
+'Operating System :: PDA Systems',
+'Operating System :: POSIX',
+'Operating System :: POSIX :: AIX',
+'Operating System :: POSIX :: BSD',
+'Operating System :: POSIX :: BSD :: BSD/OS',
+'Operating System :: POSIX :: BSD :: FreeBSD',
+'Operating System :: POSIX :: BSD :: NetBSD',
+'Operating System :: POSIX :: BSD :: OpenBSD',
+'Operating System :: POSIX :: GNU Hurd',
+'Operating System :: POSIX :: HP-UX',
+'Operating System :: POSIX :: IRIX',
+'Operating System :: POSIX :: Linux',
+'Operating System :: POSIX :: Other',
+'Operating System :: POSIX :: SCO',
+'Operating System :: POSIX :: SunOS/Solaris',
+'Operating System :: Unix',
+'Programming Language :: Ada',
+'Programming Language :: APL',
+'Programming Language :: ASP',
+'Programming Language :: Assembly',
+'Programming Language :: Awk',
+'Programming Language :: Basic',
+'Programming Language :: C',
+'Programming Language :: C#',
+'Programming Language :: C++',
+'Programming Language :: Cold Fusion',
+'Programming Language :: Cython',
+'Programming Language :: Delphi/Kylix',
+'Programming Language :: Dylan',
+'Programming Language :: Eiffel',
+'Programming Language :: Emacs-Lisp',
+'Programming Language :: Erlang',
+'Programming Language :: Euler',
+'Programming Language :: Euphoria',
+'Programming Language :: Forth',
+'Programming Language :: Fortran',
+'Programming Language :: Haskell',
+'Programming Language :: Java',
+'Programming Language :: JavaScript',
+'Programming Language :: Lisp',
+'Programming Language :: Logo',
+'Programming Language :: ML',
+'Programming Language :: Modula',
+'Programming Language :: Objective C',
+'Programming Language :: Object Pascal',
+'Programming Language :: OCaml',
+'Programming Language :: Other',
+'Programming Language :: Other Scripting Engines',
+'Programming Language :: Pascal',
+'Programming Language :: Perl',
+'Programming Language :: PHP',
+'Programming Language :: Pike',
+'Programming Language :: Pliant',
+'Programming Language :: PL/SQL',
+'Programming Language :: PROGRESS',
+'Programming Language :: Prolog',
+'Programming Language :: Python',
+'Programming Language :: Python :: 2',
+'Programming Language :: Python :: 2.3',
+'Programming Language :: Python :: 2.4',
+'Programming Language :: Python :: 2.5',
+'Programming Language :: Python :: 2.6',
+'Programming Language :: Python :: 2.7',
+'Programming Language :: Python :: 3',
+'Programming Language :: Python :: 3.0',
+'Programming Language :: Python :: 3.1',
+'Programming Language :: Python :: 3.2',
+'Programming Language :: Python :: Implementation',
+'Programming Language :: Python :: Implementation :: CPython',
+'Programming Language :: Python :: Implementation :: IronPython',
+'Programming Language :: Python :: Implementation :: Jython',
+'Programming Language :: Python :: Implementation :: PyPy',
+'Programming Language :: Python :: Implementation :: Stackless',
+'Programming Language :: REBOL',
+'Programming Language :: Rexx',
+'Programming Language :: Ruby',
+'Programming Language :: Scheme',
+'Programming Language :: Simula',
+'Programming Language :: Smalltalk',
+'Programming Language :: SQL',
+'Programming Language :: Tcl',
+'Programming Language :: Unix Shell',
+'Programming Language :: Visual Basic',
+'Programming Language :: XBasic',
+'Programming Language :: YACC',
+'Programming Language :: Zope',
+'Topic :: Adaptive Technologies',
+'Topic :: Artistic Software',
+'Topic :: Communications',
+'Topic :: Communications :: BBS',
+'Topic :: Communications :: Chat',
+'Topic :: Communications :: Chat :: AOL Instant Messenger',
+'Topic :: Communications :: Chat :: ICQ',
+'Topic :: Communications :: Chat :: Internet Relay Chat',
+'Topic :: Communications :: Chat :: Unix Talk',
+'Topic :: Communications :: Conferencing',
+'Topic :: Communications :: Email',
+'Topic :: Communications :: Email :: Address Book',
+'Topic :: Communications :: Email :: Email Clients (MUA)',
+'Topic :: Communications :: Email :: Filters',
+'Topic :: Communications :: Email :: Mailing List Servers',
+'Topic :: Communications :: Email :: Mail Transport Agents',
+'Topic :: Communications :: Email :: Post-Office',
+'Topic :: Communications :: Email :: Post-Office :: IMAP',
+'Topic :: Communications :: Email :: Post-Office :: POP3',
+'Topic :: Communications :: Fax',
+'Topic :: Communications :: FIDO',
+'Topic :: Communications :: File Sharing',
+'Topic :: Communications :: File Sharing :: Gnutella',
+'Topic :: Communications :: File Sharing :: Napster',
+'Topic :: Communications :: Ham Radio',
+'Topic :: Communications :: Internet Phone',
+'Topic :: Communications :: Telephony',
+'Topic :: Communications :: Usenet News',
+'Topic :: Database',
+'Topic :: Database :: Database Engines/Servers',
+'Topic :: Database :: Front-Ends',
+'Topic :: Desktop Environment',
+'Topic :: Desktop Environment :: File Managers',
+'Topic :: Desktop Environment :: Gnome',
+'Topic :: Desktop Environment :: GNUstep',
+'Topic :: Desktop Environment :: K Desktop Environment (KDE)',
+'Topic :: Desktop Environment :: K Desktop Environment (KDE) :: Themes',
+'Topic :: Desktop Environment :: PicoGUI',
+'Topic :: Desktop Environment :: PicoGUI :: Applications',
+'Topic :: Desktop Environment :: PicoGUI :: Themes',
+'Topic :: Desktop Environment :: Screen Savers',
+'Topic :: Desktop Environment :: Window Managers',
+'Topic :: Desktop Environment :: Window Managers :: Afterstep',
+'Topic :: Desktop Environment :: Window Managers :: Afterstep :: Themes',
+'Topic :: Desktop Environment :: Window Managers :: Applets',
+'Topic :: Desktop Environment :: Window Managers :: Blackbox',
+'Topic :: Desktop Environment :: Window Managers :: Blackbox :: Themes',
+'Topic :: Desktop Environment :: Window Managers :: CTWM',
+'Topic :: Desktop Environment :: Window Managers :: CTWM :: Themes',
+'Topic :: Desktop Environment :: Window Managers :: Enlightenment',
+'Topic :: Desktop Environment :: Window Managers :: Enlightenment :: Epplets',
+'Topic :: Desktop Environment :: Window Managers :: Enlightenment :: Themes DR15',
+'Topic :: Desktop Environment :: Window Managers :: Enlightenment :: Themes DR16',
+'Topic :: Desktop Environment :: Window Managers :: Enlightenment :: Themes DR17',
+'Topic :: Desktop Environment :: Window Managers :: Fluxbox',
+'Topic :: Desktop Environment :: Window Managers :: Fluxbox :: Themes',
+'Topic :: Desktop Environment :: Window Managers :: FVWM',
+'Topic :: Desktop Environment :: Window Managers :: FVWM :: Themes',
+'Topic :: Desktop Environment :: Window Managers :: IceWM',
+'Topic :: Desktop Environment :: Window Managers :: IceWM :: Themes',
+'Topic :: Desktop Environment :: Window Managers :: MetaCity',
+'Topic :: Desktop Environment :: Window Managers :: MetaCity :: Themes',
+'Topic :: Desktop Environment :: Window Managers :: Oroborus',
+'Topic :: Desktop Environment :: Window Managers :: Oroborus :: Themes',
+'Topic :: Desktop Environment :: Window Managers :: Sawfish',
+'Topic :: Desktop Environment :: Window Managers :: Sawfish :: Themes 0.30',
+'Topic :: Desktop Environment :: Window Managers :: Sawfish :: Themes pre-0.30',
+'Topic :: Desktop Environment :: Window Managers :: Waimea',
+'Topic :: Desktop Environment :: Window Managers :: Waimea :: Themes',
+'Topic :: Desktop Environment :: Window Managers :: Window Maker',
+'Topic :: Desktop Environment :: Window Managers :: Window Maker :: Applets',
+'Topic :: Desktop Environment :: Window Managers :: Window Maker :: Themes',
+'Topic :: Desktop Environment :: Window Managers :: XFCE',
+'Topic :: Desktop Environment :: Window Managers :: XFCE :: Themes',
+'Topic :: Documentation',
+'Topic :: Education',
+'Topic :: Education :: Computer Aided Instruction (CAI)',
+'Topic :: Education :: Testing',
+'Topic :: Games/Entertainment',
+'Topic :: Games/Entertainment :: Arcade',
+'Topic :: Games/Entertainment :: Board Games',
+'Topic :: Games/Entertainment :: First Person Shooters',
+'Topic :: Games/Entertainment :: Fortune Cookies',
+'Topic :: Games/Entertainment :: Multi-User Dungeons (MUD)',
+'Topic :: Games/Entertainment :: Puzzle Games',
+'Topic :: Games/Entertainment :: Real Time Strategy',
+'Topic :: Games/Entertainment :: Role-Playing',
+'Topic :: Games/Entertainment :: Side-Scrolling/Arcade Games',
+'Topic :: Games/Entertainment :: Simulation',
+'Topic :: Games/Entertainment :: Turn Based Strategy',
+'Topic :: Home Automation',
+'Topic :: Internet',
+'Topic :: Internet :: File Transfer Protocol (FTP)',
+'Topic :: Internet :: Finger',
+'Topic :: Internet :: Log Analysis',
+'Topic :: Internet :: Name Service (DNS)',
+'Topic :: Internet :: Proxy Servers',
+'Topic :: Internet :: WAP',
+'Topic :: Internet :: WWW/HTTP',
+'Topic :: Internet :: WWW/HTTP :: Browsers',
+'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
+'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries',
+'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: Message Boards',
+'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: News/Diary',
+'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: Page Counters',
+'Topic :: Internet :: WWW/HTTP :: HTTP Servers',
+'Topic :: Internet :: WWW/HTTP :: Indexing/Search',
+'Topic :: Internet :: WWW/HTTP :: Session',
+'Topic :: Internet :: WWW/HTTP :: Site Management',
+'Topic :: Internet :: WWW/HTTP :: Site Management :: Link Checking',
+'Topic :: Internet :: WWW/HTTP :: WSGI',
+'Topic :: Internet :: WWW/HTTP :: WSGI :: Application',
+'Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware',
+'Topic :: Internet :: WWW/HTTP :: WSGI :: Server',
+'Topic :: Internet :: Z39.50',
+'Topic :: Multimedia',
+'Topic :: Multimedia :: Graphics',
+'Topic :: Multimedia :: Graphics :: 3D Modeling',
+'Topic :: Multimedia :: Graphics :: 3D Rendering',
+'Topic :: Multimedia :: Graphics :: Capture',
+'Topic :: Multimedia :: Graphics :: Capture :: Digital Camera',
+'Topic :: Multimedia :: Graphics :: Capture :: Scanners',
+'Topic :: Multimedia :: Graphics :: Capture :: Screen Capture',
+'Topic :: Multimedia :: Graphics :: Editors',
+'Topic :: Multimedia :: Graphics :: Editors :: Raster-Based',
+'Topic :: Multimedia :: Graphics :: Editors :: Vector-Based',
+'Topic :: Multimedia :: Graphics :: Graphics Conversion',
+'Topic :: Multimedia :: Graphics :: Presentation',
+'Topic :: Multimedia :: Graphics :: Viewers',
+'Topic :: Multimedia :: Sound/Audio',
+'Topic :: Multimedia :: Sound/Audio :: Analysis',
+'Topic :: Multimedia :: Sound/Audio :: Capture/Recording',
+'Topic :: Multimedia :: Sound/Audio :: CD Audio',
+'Topic :: Multimedia :: Sound/Audio :: CD Audio :: CD Playing',
+'Topic :: Multimedia :: Sound/Audio :: CD Audio :: CD Ripping',
+'Topic :: Multimedia :: Sound/Audio :: CD Audio :: CD Writing',
+'Topic :: Multimedia :: Sound/Audio :: Conversion',
+'Topic :: Multimedia :: Sound/Audio :: Editors',
+'Topic :: Multimedia :: Sound/Audio :: MIDI',
+'Topic :: Multimedia :: Sound/Audio :: Mixers',
+'Topic :: Multimedia :: Sound/Audio :: Players',
+'Topic :: Multimedia :: Sound/Audio :: Players :: MP3',
+'Topic :: Multimedia :: Sound/Audio :: Sound Synthesis',
+'Topic :: Multimedia :: Sound/Audio :: Speech',
+'Topic :: Multimedia :: Video',
+'Topic :: Multimedia :: Video :: Capture',
+'Topic :: Multimedia :: Video :: Conversion',
+'Topic :: Multimedia :: Video :: Display',
+'Topic :: Multimedia :: Video :: Non-Linear Editor',
+'Topic :: Office/Business',
+'Topic :: Office/Business :: Financial',
+'Topic :: Office/Business :: Financial :: Accounting',
+'Topic :: Office/Business :: Financial :: Investment',
+'Topic :: Office/Business :: Financial :: Point-Of-Sale',
+'Topic :: Office/Business :: Financial :: Spreadsheet',
+'Topic :: Office/Business :: Groupware',
+'Topic :: Office/Business :: News/Diary',
+'Topic :: Office/Business :: Office Suites',
+'Topic :: Office/Business :: Scheduling',
+'Topic :: Other/Nonlisted Topic',
+'Topic :: Printing',
+'Topic :: Religion',
+'Topic :: Scientific/Engineering',
+'Topic :: Scientific/Engineering :: Artificial Life',
+'Topic :: Scientific/Engineering :: Artificial Intelligence',
+'Topic :: Scientific/Engineering :: Astronomy',
+'Topic :: Scientific/Engineering :: Atmospheric Science',
+'Topic :: Scientific/Engineering :: Bio-Informatics',
+'Topic :: Scientific/Engineering :: Chemistry',
+'Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)',
+'Topic :: Scientific/Engineering :: GIS',
+'Topic :: Scientific/Engineering :: Human Machine Interfaces',
+'Topic :: Scientific/Engineering :: Image Recognition',
+'Topic :: Scientific/Engineering :: Information Analysis',
+'Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator',
+'Topic :: Scientific/Engineering :: Mathematics',
+'Topic :: Scientific/Engineering :: Medical Science Apps.',
+'Topic :: Scientific/Engineering :: Physics',
+'Topic :: Scientific/Engineering :: Visualization',
+'Topic :: Security',
+'Topic :: Security :: Cryptography',
+'Topic :: Sociology',
+'Topic :: Sociology :: Genealogy',
+'Topic :: Sociology :: History',
+'Topic :: Software Development',
+'Topic :: Software Development :: Assemblers',
+'Topic :: Software Development :: Bug Tracking',
+'Topic :: Software Development :: Build Tools',
+'Topic :: Software Development :: Code Generators',
+'Topic :: Software Development :: Compilers',
+'Topic :: Software Development :: Debuggers',
+'Topic :: Software Development :: Disassemblers',
+'Topic :: Software Development :: Documentation',
+'Topic :: Software Development :: Embedded Systems',
+'Topic :: Software Development :: Internationalization',
+'Topic :: Software Development :: Interpreters',
+'Topic :: Software Development :: Libraries',
+'Topic :: Software Development :: Libraries :: Application Frameworks',
+'Topic :: Software Development :: Libraries :: Java Libraries',
+'Topic :: Software Development :: Libraries :: Perl Modules',
+'Topic :: Software Development :: Libraries :: PHP Classes',
+'Topic :: Software Development :: Libraries :: Pike Modules',
+'Topic :: Software Development :: Libraries :: pygame',
+'Topic :: Software Development :: Libraries :: Python Modules',
+'Topic :: Software Development :: Libraries :: Ruby Modules',
+'Topic :: Software Development :: Libraries :: Tcl Extensions',
+'Topic :: Software Development :: Localization',
+'Topic :: Software Development :: Object Brokering',
+'Topic :: Software Development :: Object Brokering :: CORBA',
+'Topic :: Software Development :: Pre-processors',
+'Topic :: Software Development :: Quality Assurance',
+'Topic :: Software Development :: Testing',
+'Topic :: Software Development :: Testing :: Traffic Generation',
+'Topic :: Software Development :: User Interfaces',
+'Topic :: Software Development :: Version Control',
+'Topic :: Software Development :: Version Control :: CVS',
+'Topic :: Software Development :: Version Control :: RCS',
+'Topic :: Software Development :: Version Control :: SCCS',
+'Topic :: Software Development :: Widget Sets',
+'Topic :: System',
+'Topic :: System :: Archiving',
+'Topic :: System :: Archiving :: Backup',
+'Topic :: System :: Archiving :: Compression',
+'Topic :: System :: Archiving :: Mirroring',
+'Topic :: System :: Archiving :: Packaging',
+'Topic :: System :: Benchmark',
+'Topic :: System :: Boot',
+'Topic :: System :: Boot :: Init',
+'Topic :: System :: Clustering',
+'Topic :: System :: Console Fonts',
+'Topic :: System :: Distributed Computing',
+'Topic :: System :: Emulators',
+'Topic :: System :: Filesystems',
+'Topic :: System :: Hardware',
+'Topic :: System :: Hardware :: Hardware Drivers',
+'Topic :: System :: Hardware :: Mainframes',
+'Topic :: System :: Hardware :: Symmetric Multi-processing',
+'Topic :: System :: Installation/Setup',
+'Topic :: System :: Logging',
+'Topic :: System :: Monitoring',
+'Topic :: System :: Networking',
+'Topic :: System :: Networking :: Firewalls',
+'Topic :: System :: Networking :: Monitoring',
+'Topic :: System :: Networking :: Monitoring :: Hardware Watchdog',
+'Topic :: System :: Networking :: Time Synchronization',
+'Topic :: System :: Operating System',
+'Topic :: System :: Operating System Kernels',
+'Topic :: System :: Operating System Kernels :: BSD',
+'Topic :: System :: Operating System Kernels :: GNU Hurd',
+'Topic :: System :: Operating System Kernels :: Linux',
+'Topic :: System :: Power (UPS)',
+'Topic :: System :: Recovery Tools',
+'Topic :: System :: Shells',
+'Topic :: System :: Software Distribution',
+'Topic :: System :: Systems Administration',
+'Topic :: System :: Systems Administration :: Authentication/Directory',
+'Topic :: System :: Systems Administration :: Authentication/Directory :: LDAP',
+'Topic :: System :: Systems Administration :: Authentication/Directory :: NIS',
+'Topic :: System :: System Shells',
+'Topic :: Terminals',
+'Topic :: Terminals :: Serial',
+'Topic :: Terminals :: Telnet',
+'Topic :: Terminals :: Terminal Emulators/X Terminals',
+'Topic :: Text Editors',
+'Topic :: Text Editors :: Documentation',
+'Topic :: Text Editors :: Emacs',
+'Topic :: Text Editors :: Integrated Development Environments (IDE)',
+'Topic :: Text Editors :: Text Processing',
+'Topic :: Text Editors :: Word Processors',
+'Topic :: Text Processing',
+'Topic :: Text Processing :: Filters',
+'Topic :: Text Processing :: Fonts',
+'Topic :: Text Processing :: General',
+'Topic :: Text Processing :: Indexing',
+'Topic :: Text Processing :: Linguistic',
+'Topic :: Text Processing :: Markup',
+'Topic :: Text Processing :: Markup :: HTML',
+'Topic :: Text Processing :: Markup :: LaTeX',
+'Topic :: Text Processing :: Markup :: SGML',
+'Topic :: Text Processing :: Markup :: VRML',
+'Topic :: Text Processing :: Markup :: XML',
+'Topic :: Utilities',
+]
diff --git a/Lib/packaging/command/__init__.py b/Lib/packaging/command/__init__.py
new file mode 100644
index 0000000..cd34fc8
--- /dev/null
+++ b/Lib/packaging/command/__init__.py
@@ -0,0 +1,63 @@
+"""Subpackage containing all standard commands."""
+import os
+from packaging.errors import PackagingModuleError
+from packaging.util import resolve_name
+
+__all__ = ['get_command_names', 'set_command', 'get_command_class',
+ 'STANDARD_COMMANDS']
+
+_COMMANDS = {
+ 'check': 'packaging.command.check.check',
+ 'test': 'packaging.command.test.test',
+ 'build': 'packaging.command.build.build',
+ 'build_py': 'packaging.command.build_py.build_py',
+ 'build_ext': 'packaging.command.build_ext.build_ext',
+ 'build_clib': 'packaging.command.build_clib.build_clib',
+ 'build_scripts': 'packaging.command.build_scripts.build_scripts',
+ 'clean': 'packaging.command.clean.clean',
+ 'install_dist': 'packaging.command.install_dist.install_dist',
+ 'install_lib': 'packaging.command.install_lib.install_lib',
+ 'install_headers': 'packaging.command.install_headers.install_headers',
+ 'install_scripts': 'packaging.command.install_scripts.install_scripts',
+ 'install_data': 'packaging.command.install_data.install_data',
+ 'install_distinfo':
+ 'packaging.command.install_distinfo.install_distinfo',
+ 'sdist': 'packaging.command.sdist.sdist',
+ 'bdist': 'packaging.command.bdist.bdist',
+ 'bdist_dumb': 'packaging.command.bdist_dumb.bdist_dumb',
+ 'bdist_wininst': 'packaging.command.bdist_wininst.bdist_wininst',
+ 'register': 'packaging.command.register.register',
+ 'upload': 'packaging.command.upload.upload',
+ 'upload_docs': 'packaging.command.upload_docs.upload_docs',
+}
+
+# XXX this is crappy
+if os.name == 'nt':
+ _COMMANDS['bdist_msi'] = 'packaging.command.bdist_msi.bdist_msi'
+
+# XXX use OrderedDict to preserve the grouping (build-related, install-related,
+# distribution-related)
+STANDARD_COMMANDS = set(_COMMANDS)
+
+
+def get_command_names():
+ """Return registered commands"""
+ return sorted(_COMMANDS)
+
+
+def set_command(location):
+ cls = resolve_name(location)
+ # XXX we want to do the duck-type checking here
+ _COMMANDS[cls.get_command_name()] = cls
+
+
+def get_command_class(name):
+ """Return the registered command"""
+ try:
+ cls = _COMMANDS[name]
+ except KeyError:
+ raise PackagingModuleError("Invalid command %s" % name)
+ if isinstance(cls, str):
+ cls = resolve_name(cls)
+ _COMMANDS[name] = cls
+ return cls
diff --git a/Lib/packaging/command/bdist.py b/Lib/packaging/command/bdist.py
new file mode 100644
index 0000000..e390cdc
--- /dev/null
+++ b/Lib/packaging/command/bdist.py
@@ -0,0 +1,141 @@
+"""Create a built (binary) distribution.
+
+If a --formats option was given on the command line, this command will
+call the corresponding bdist_* commands; if the option was absent, a
+bdist_* command depending on the current platform will be called.
+"""
+
+import os
+
+from packaging import util
+from packaging.command.cmd import Command
+from packaging.errors import PackagingPlatformError, PackagingOptionError
+
+
+def show_formats():
+ """Print list of available formats (arguments to "--format" option).
+ """
+ from packaging.fancy_getopt import FancyGetopt
+ formats = []
+ for format in bdist.format_commands:
+ formats.append(("formats=" + format, None,
+ bdist.format_command[format][1]))
+ pretty_printer = FancyGetopt(formats)
+ pretty_printer.print_help("List of available distribution formats:")
+
+
+class bdist(Command):
+
+ description = "create a built (binary) distribution"
+
+ user_options = [('bdist-base=', 'b',
+ "temporary directory for creating built distributions"),
+ ('plat-name=', 'p',
+ "platform name to embed in generated filenames "
+ "(default: %s)" % util.get_platform()),
+ ('formats=', None,
+ "formats for distribution (comma-separated list)"),
+ ('dist-dir=', 'd',
+ "directory to put final built distributions in "
+ "[default: dist]"),
+ ('skip-build', None,
+ "skip rebuilding everything (for testing/debugging)"),
+ ('owner=', 'u',
+ "Owner name used when creating a tar file"
+ " [default: current user]"),
+ ('group=', 'g',
+ "Group name used when creating a tar file"
+ " [default: current group]"),
+ ]
+
+ boolean_options = ['skip-build']
+
+ help_options = [
+ ('help-formats', None,
+ "lists available distribution formats", show_formats),
+ ]
+
+ # This is of course very simplistic. The various UNIX family operating
+ # systems have their specific formats, but they are out of scope for us;
+ # bdist_dumb is, well, dumb; it's more a building block for other
+ # packaging tools than a real end-user binary format.
+ default_format = {'posix': 'gztar',
+ 'nt': 'zip',
+ 'os2': 'zip'}
+
+ # Establish the preferred order (for the --help-formats option).
+ format_commands = ['gztar', 'bztar', 'tar',
+ 'wininst', 'zip', 'msi']
+
+ # And the real information.
+ format_command = {'gztar': ('bdist_dumb', "gzip'ed tar file"),
+ 'bztar': ('bdist_dumb', "bzip2'ed tar file"),
+ 'tar': ('bdist_dumb', "tar file"),
+ 'wininst': ('bdist_wininst',
+ "Windows executable installer"),
+ 'zip': ('bdist_dumb', "ZIP file"),
+ 'msi': ('bdist_msi', "Microsoft Installer"),
+ }
+
+ def initialize_options(self):
+ self.bdist_base = None
+ self.plat_name = None
+ self.formats = None
+ self.dist_dir = None
+ self.skip_build = False
+ self.group = None
+ self.owner = None
+
+ def finalize_options(self):
+ # have to finalize 'plat_name' before 'bdist_base'
+ if self.plat_name is None:
+ if self.skip_build:
+ self.plat_name = util.get_platform()
+ else:
+ self.plat_name = self.get_finalized_command('build').plat_name
+
+ # 'bdist_base' -- parent of per-built-distribution-format
+ # temporary directories (eg. we'll probably have
+ # "build/bdist.<plat>/dumb", etc.)
+ if self.bdist_base is None:
+ build_base = self.get_finalized_command('build').build_base
+ self.bdist_base = os.path.join(build_base,
+ 'bdist.' + self.plat_name)
+
+ self.ensure_string_list('formats')
+ if self.formats is None:
+ try:
+ self.formats = [self.default_format[os.name]]
+ except KeyError:
+ raise PackagingPlatformError(
+ "don't know how to create built distributions "
+ "on platform %s" % os.name)
+
+ if self.dist_dir is None:
+ self.dist_dir = "dist"
+
+ def run(self):
+ # Figure out which sub-commands we need to run.
+ commands = []
+ for format in self.formats:
+ try:
+ commands.append(self.format_command[format][0])
+ except KeyError:
+ raise PackagingOptionError("invalid format '%s'" % format)
+
+ # Reinitialize and run each command.
+ for i in range(len(self.formats)):
+ cmd_name = commands[i]
+ sub_cmd = self.reinitialize_command(cmd_name)
+ sub_cmd.format = self.formats[i]
+
+ # passing the owner and group names for tar archiving
+ if cmd_name == 'bdist_dumb':
+ sub_cmd.owner = self.owner
+ sub_cmd.group = self.group
+
+ # If we're going to need to run this command again, tell it to
+ # keep its temporary files around so subsequent runs go faster.
+ if cmd_name in commands[i+1:]:
+ sub_cmd.keep_temp = True
+ self.run_command(cmd_name)
diff --git a/Lib/packaging/command/bdist_dumb.py b/Lib/packaging/command/bdist_dumb.py
new file mode 100644
index 0000000..548e3c4
--- /dev/null
+++ b/Lib/packaging/command/bdist_dumb.py
@@ -0,0 +1,139 @@
+"""Create a "dumb" built distribution.
+
+A dumb distribution is just an archive meant to be unpacked under
+sys.prefix or sys.exec_prefix.
+"""
+
+import os
+from shutil import rmtree
+from sysconfig import get_python_version
+
+from packaging.util import get_platform
+from packaging.command.cmd import Command
+from packaging.errors import PackagingPlatformError
+from packaging import logger
+
+
+class bdist_dumb(Command):
+
+ description = 'create a "dumb" built distribution'
+
+ user_options = [('bdist-dir=', 'd',
+ "temporary directory for creating the distribution"),
+ ('plat-name=', 'p',
+ "platform name to embed in generated filenames "
+ "(default: %s)" % get_platform()),
+ ('format=', 'f',
+ "archive format to create (tar, gztar, bztar, zip)"),
+ ('keep-temp', 'k',
+ "keep the pseudo-installation tree around after " +
+ "creating the distribution archive"),
+ ('dist-dir=', 'd',
+ "directory to put final built distributions in"),
+ ('skip-build', None,
+ "skip rebuilding everything (for testing/debugging)"),
+ ('relative', None,
+ "build the archive using relative paths"
+ "(default: false)"),
+ ('owner=', 'u',
+ "Owner name used when creating a tar file"
+ " [default: current user]"),
+ ('group=', 'g',
+ "Group name used when creating a tar file"
+ " [default: current group]"),
+ ]
+
+ boolean_options = ['keep-temp', 'skip-build', 'relative']
+
+ default_format = {'posix': 'gztar',
+ 'nt': 'zip',
+ 'os2': 'zip'}
+
+ def initialize_options(self):
+ self.bdist_dir = None
+ self.plat_name = None
+ self.format = None
+ self.keep_temp = False
+ self.dist_dir = None
+ self.skip_build = None
+ self.relative = False
+ self.owner = None
+ self.group = None
+
+ def finalize_options(self):
+ if self.bdist_dir is None:
+ bdist_base = self.get_finalized_command('bdist').bdist_base
+ self.bdist_dir = os.path.join(bdist_base, 'dumb')
+
+ if self.format is None:
+ try:
+ self.format = self.default_format[os.name]
+ except KeyError:
+ raise PackagingPlatformError(
+ "don't know how to create dumb built distributions "
+ "on platform %s" % os.name)
+
+ self.set_undefined_options('bdist',
+ 'dist_dir', 'plat_name', 'skip_build')
+
+ def run(self):
+ if not self.skip_build:
+ self.run_command('build')
+
+ install = self.reinitialize_command('install_dist',
+ reinit_subcommands=True)
+ install.root = self.bdist_dir
+ install.skip_build = self.skip_build
+ install.warn_dir = False
+
+ logger.info("installing to %s", self.bdist_dir)
+ self.run_command('install_dist')
+
+ # And make an archive relative to the root of the
+ # pseudo-installation tree.
+ archive_basename = "%s.%s" % (self.distribution.get_fullname(),
+ self.plat_name)
+
+ # OS/2 objects to any ":" characters in a filename (such as when
+ # a timestamp is used in a version) so change them to hyphens.
+ if os.name == "os2":
+ archive_basename = archive_basename.replace(":", "-")
+
+ pseudoinstall_root = os.path.join(self.dist_dir, archive_basename)
+ if not self.relative:
+ archive_root = self.bdist_dir
+ else:
+ if (self.distribution.has_ext_modules() and
+ (install.install_base != install.install_platbase)):
+ raise PackagingPlatformError(
+ "can't make a dumb built distribution where base and "
+ "platbase are different (%r, %r)" %
+ (install.install_base, install.install_platbase))
+ else:
+ archive_root = os.path.join(
+ self.bdist_dir,
+ self._ensure_relative(install.install_base))
+
+ # Make the archive
+ filename = self.make_archive(pseudoinstall_root,
+ self.format, root_dir=archive_root,
+ owner=self.owner, group=self.group)
+ if self.distribution.has_ext_modules():
+ pyversion = get_python_version()
+ else:
+ pyversion = 'any'
+ self.distribution.dist_files.append(('bdist_dumb', pyversion,
+ filename))
+
+ if not self.keep_temp:
+ if self.dry_run:
+ logger.info('removing %s', self.bdist_dir)
+ else:
+ rmtree(self.bdist_dir)
+
+ def _ensure_relative(self, path):
+ # copied from dir_util, deleted
+ drive, path = os.path.splitdrive(path)
+ if path[0:1] == os.sep:
+ path = drive + path[1:]
+ return path
diff --git a/Lib/packaging/command/bdist_msi.py b/Lib/packaging/command/bdist_msi.py
new file mode 100644
index 0000000..4f8eca6
--- /dev/null
+++ b/Lib/packaging/command/bdist_msi.py
@@ -0,0 +1,744 @@
+"""Create a Microsoft Installer (.msi) binary distribution."""
+
+# Copyright (C) 2005, 2006 Martin von Löwis
+# Licensed to PSF under a Contributor Agreement.
+
+import sys
+import os
+import msilib
+
+
+from sysconfig import get_python_version
+from shutil import rmtree
+from packaging.command.cmd import Command
+from packaging.version import NormalizedVersion
+from packaging.errors import PackagingOptionError
+from packaging import logger as log
+from packaging.util import get_platform
+from msilib import schema, sequence, text
+from msilib import Directory, Feature, Dialog, add_data
+
+class MSIVersion(NormalizedVersion):
+ """
+ MSI ProductVersion must be strictly numeric.
+ MSIVersion disallows prerelease and postrelease versions.
+ """
+ def __init__(self, *args, **kwargs):
+ super(MSIVersion, self).__init__(*args, **kwargs)
+ if not self.is_final:
+ raise ValueError("ProductVersion must be strictly numeric")
+
+class PyDialog(Dialog):
+ """Dialog class with a fixed layout: controls at the top, then a ruler,
+ then a list of buttons: back, next, cancel. Optionally a bitmap at the
+ left."""
+ def __init__(self, *args, **kw):
+ """Dialog(database, name, x, y, w, h, attributes, title, first,
+ default, cancel, bitmap=true)"""
+ super(PyDialog, self).__init__(*args)
+ ruler = self.h - 36
+ #if kw.get("bitmap", True):
+ # self.bitmap("Bitmap", 0, 0, bmwidth, ruler, "PythonWin")
+ self.line("BottomLine", 0, ruler, self.w, 0)
+
+ def title(self, title):
+ "Set the title text of the dialog at the top."
+ # name, x, y, w, h, flags=Visible|Enabled|Transparent|NoPrefix,
+ # text, in VerdanaBold10
+ self.text("Title", 15, 10, 320, 60, 0x30003,
+ r"{\VerdanaBold10}%s" % title)
+
+ def back(self, title, next, name = "Back", active = 1):
+ """Add a back button with a given title, the tab-next button,
+ its name in the Control table, possibly initially disabled.
+
+ Return the button, so that events can be associated"""
+ if active:
+ flags = 3 # Visible|Enabled
+ else:
+ flags = 1 # Visible
+ return self.pushbutton(name, 180, self.h-27 , 56, 17, flags, title, next)
+
+ def cancel(self, title, next, name = "Cancel", active = 1):
+ """Add a cancel button with a given title, the tab-next button,
+ its name in the Control table, possibly initially disabled.
+
+ Return the button, so that events can be associated"""
+ if active:
+ flags = 3 # Visible|Enabled
+ else:
+ flags = 1 # Visible
+ return self.pushbutton(name, 304, self.h-27, 56, 17, flags, title, next)
+
+ def next(self, title, next, name = "Next", active = 1):
+ """Add a Next button with a given title, the tab-next button,
+ its name in the Control table, possibly initially disabled.
+
+ Return the button, so that events can be associated"""
+ if active:
+ flags = 3 # Visible|Enabled
+ else:
+ flags = 1 # Visible
+ return self.pushbutton(name, 236, self.h-27, 56, 17, flags, title, next)
+
+ def xbutton(self, name, title, next, xpos):
+ """Add a button with a given title, the tab-next button,
+ its name in the Control table, giving its x position; the
+ y-position is aligned with the other buttons.
+
+ Return the button, so that events can be associated"""
+ return self.pushbutton(name, int(self.w*xpos - 28), self.h-27, 56, 17, 3, title, next)
+
+class bdist_msi(Command):
+
+ description = "create a Microsoft Installer (.msi) binary distribution"
+
+ user_options = [('bdist-dir=', None,
+ "temporary directory for creating the distribution"),
+ ('plat-name=', 'p',
+ "platform name to embed in generated filenames "
+ "(default: %s)" % get_platform()),
+ ('keep-temp', 'k',
+ "keep the pseudo-installation tree around after " +
+ "creating the distribution archive"),
+ ('target-version=', None,
+ "require a specific python version" +
+ " on the target system"),
+ ('no-target-compile', 'c',
+ "do not compile .py to .pyc on the target system"),
+ ('no-target-optimize', 'o',
+ "do not compile .py to .pyo (optimized)"
+ "on the target system"),
+ ('dist-dir=', 'd',
+ "directory to put final built distributions in"),
+ ('skip-build', None,
+ "skip rebuilding everything (for testing/debugging)"),
+ ('install-script=', None,
+ "basename of installation script to be run after"
+ "installation or before deinstallation"),
+ ('pre-install-script=', None,
+ "Fully qualified filename of a script to be run before "
+ "any files are installed. This script need not be in the "
+ "distribution"),
+ ]
+
+ boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize',
+ 'skip-build']
+
+ all_versions = ['2.0', '2.1', '2.2', '2.3', '2.4',
+ '2.5', '2.6', '2.7', '2.8', '2.9',
+ '3.0', '3.1', '3.2', '3.3', '3.4',
+ '3.5', '3.6', '3.7', '3.8', '3.9']
+ other_version = 'X'
+
+ def initialize_options(self):
+ self.bdist_dir = None
+ self.plat_name = None
+ self.keep_temp = False
+ self.no_target_compile = False
+ self.no_target_optimize = False
+ self.target_version = None
+ self.dist_dir = None
+ self.skip_build = None
+ self.install_script = None
+ self.pre_install_script = None
+ self.versions = None
+
+ def finalize_options(self):
+ self.set_undefined_options('bdist', 'skip_build')
+
+ if self.bdist_dir is None:
+ bdist_base = self.get_finalized_command('bdist').bdist_base
+ self.bdist_dir = os.path.join(bdist_base, 'msi')
+
+ short_version = get_python_version()
+ if (not self.target_version) and self.distribution.has_ext_modules():
+ self.target_version = short_version
+
+ if self.target_version:
+ self.versions = [self.target_version]
+ if not self.skip_build and self.distribution.has_ext_modules()\
+ and self.target_version != short_version:
+ raise PackagingOptionError("target version can only be %s, or the '--skip-build'" \
+ " option must be specified" % (short_version,))
+ else:
+ self.versions = list(self.all_versions)
+
+ self.set_undefined_options('bdist', 'dist_dir', 'plat_name')
+
+ if self.pre_install_script:
+ raise PackagingOptionError("the pre-install-script feature is not yet implemented")
+
+ if self.install_script:
+ for script in self.distribution.scripts:
+ if self.install_script == os.path.basename(script):
+ break
+ else:
+ raise PackagingOptionError("install_script '%s' not found in scripts" % \
+ self.install_script)
+ self.install_script_key = None
+
+
+ def run(self):
+ if not self.skip_build:
+ self.run_command('build')
+
+ install = self.reinitialize_command('install_dist',
+ reinit_subcommands=True)
+ install.prefix = self.bdist_dir
+ install.skip_build = self.skip_build
+ install.warn_dir = False
+
+ install_lib = self.reinitialize_command('install_lib')
+ # we do not want to include pyc or pyo files
+ install_lib.compile = False
+ install_lib.optimize = 0
+
+ if self.distribution.has_ext_modules():
+ # If we are building an installer for a Python version other
+ # than the one we are currently running, then we need to ensure
+ # our build_lib reflects the other Python version rather than ours.
+ # Note that for target_version!=sys.version, we must have skipped the
+ # build step, so there is no issue with enforcing the build of this
+ # version.
+ target_version = self.target_version
+ if not target_version:
+ assert self.skip_build, "Should have already checked this"
+ target_version = sys.version[0:3]
+ plat_specifier = ".%s-%s" % (self.plat_name, target_version)
+ build = self.get_finalized_command('build')
+ build.build_lib = os.path.join(build.build_base,
+ 'lib' + plat_specifier)
+
+ log.info("installing to %s", self.bdist_dir)
+ install.ensure_finalized()
+
+ # avoid warning of 'install_lib' about installing
+ # into a directory not in sys.path
+ sys.path.insert(0, os.path.join(self.bdist_dir, 'PURELIB'))
+
+ install.run()
+
+ del sys.path[0]
+
+ self.mkpath(self.dist_dir)
+ fullname = self.distribution.get_fullname()
+ installer_name = self.get_installer_filename(fullname)
+ installer_name = os.path.abspath(installer_name)
+ if os.path.exists(installer_name): os.unlink(installer_name)
+
+ metadata = self.distribution.metadata
+ author = metadata.author
+ if not author:
+ author = metadata.maintainer
+ if not author:
+ author = "UNKNOWN"
+ version = MSIVersion(metadata.get_version())
+ # Prefix ProductName with Python x.y, so that
+ # it sorts together with the other Python packages
+ # in Add-Remove-Programs (APR)
+ fullname = self.distribution.get_fullname()
+ if self.target_version:
+ product_name = "Python %s %s" % (self.target_version, fullname)
+ else:
+ product_name = "Python %s" % (fullname)
+ self.db = msilib.init_database(installer_name, schema,
+ product_name, msilib.gen_uuid(),
+ str(version), author)
+ msilib.add_tables(self.db, sequence)
+ props = [('DistVersion', version)]
+ email = metadata.author_email or metadata.maintainer_email
+ if email:
+ props.append(("ARPCONTACT", email))
+ if metadata.url:
+ props.append(("ARPURLINFOABOUT", metadata.url))
+ if props:
+ add_data(self.db, 'Property', props)
+
+ self.add_find_python()
+ self.add_files()
+ self.add_scripts()
+ self.add_ui()
+ self.db.Commit()
+
+ if hasattr(self.distribution, 'dist_files'):
+ tup = 'bdist_msi', self.target_version or 'any', fullname
+ self.distribution.dist_files.append(tup)
+
+ if not self.keep_temp:
+ log.info("removing temporary build directory %s", self.bdist_dir)
+ if not self.dry_run:
+ rmtree(self.bdist_dir)
+
+ def add_files(self):
+ db = self.db
+ cab = msilib.CAB("distfiles")
+ rootdir = os.path.abspath(self.bdist_dir)
+
+ root = Directory(db, cab, None, rootdir, "TARGETDIR", "SourceDir")
+ f = Feature(db, "Python", "Python", "Everything",
+ 0, 1, directory="TARGETDIR")
+
+ items = [(f, root, '')]
+ for version in self.versions + [self.other_version]:
+ target = "TARGETDIR" + version
+ name = default = "Python" + version
+ desc = "Everything"
+ if version is self.other_version:
+ title = "Python from another location"
+ level = 2
+ else:
+ title = "Python %s from registry" % version
+ level = 1
+ f = Feature(db, name, title, desc, 1, level, directory=target)
+ dir = Directory(db, cab, root, rootdir, target, default)
+ items.append((f, dir, version))
+ db.Commit()
+
+ seen = {}
+ for feature, dir, version in items:
+ todo = [dir]
+ while todo:
+ dir = todo.pop()
+ for file in os.listdir(dir.absolute):
+ afile = os.path.join(dir.absolute, file)
+ if os.path.isdir(afile):
+ short = "%s|%s" % (dir.make_short(file), file)
+ default = file + version
+ newdir = Directory(db, cab, dir, file, default, short)
+ todo.append(newdir)
+ else:
+ if not dir.component:
+ dir.start_component(dir.logical, feature, 0)
+ if afile not in seen:
+ key = seen[afile] = dir.add_file(file)
+ if file==self.install_script:
+ if self.install_script_key:
+ raise PackagingOptionError(
+ "Multiple files with name %s" % file)
+ self.install_script_key = '[#%s]' % key
+ else:
+ key = seen[afile]
+ add_data(self.db, "DuplicateFile",
+ [(key + version, dir.component, key, None, dir.logical)])
+ db.Commit()
+ cab.commit(db)
+
+ def add_find_python(self):
+ """Adds code to the installer to compute the location of Python.
+
+ Properties PYTHON.MACHINE.X.Y and PYTHON.USER.X.Y will be set from the
+ registry for each version of Python.
+
+ Properties TARGETDIRX.Y will be set from PYTHON.USER.X.Y if defined,
+ else from PYTHON.MACHINE.X.Y.
+
+ Properties PYTHONX.Y will be set to TARGETDIRX.Y\\python.exe"""
+
+ start = 402
+ for ver in self.versions:
+ install_path = r"SOFTWARE\Python\PythonCore\%s\InstallPath" % ver
+ machine_reg = "python.machine." + ver
+ user_reg = "python.user." + ver
+ machine_prop = "PYTHON.MACHINE." + ver
+ user_prop = "PYTHON.USER." + ver
+ machine_action = "PythonFromMachine" + ver
+ user_action = "PythonFromUser" + ver
+ exe_action = "PythonExe" + ver
+ target_dir_prop = "TARGETDIR" + ver
+ exe_prop = "PYTHON" + ver
+ if msilib.Win64:
+ # type: msidbLocatorTypeRawValue + msidbLocatorType64bit
+ Type = 2+16
+ else:
+ Type = 2
+ add_data(self.db, "RegLocator",
+ [(machine_reg, 2, install_path, None, Type),
+ (user_reg, 1, install_path, None, Type)])
+ add_data(self.db, "AppSearch",
+ [(machine_prop, machine_reg),
+ (user_prop, user_reg)])
+ add_data(self.db, "CustomAction",
+ [(machine_action, 51+256, target_dir_prop, "[" + machine_prop + "]"),
+ (user_action, 51+256, target_dir_prop, "[" + user_prop + "]"),
+ (exe_action, 51+256, exe_prop, "[" + target_dir_prop + "]\\python.exe"),
+ ])
+ add_data(self.db, "InstallExecuteSequence",
+ [(machine_action, machine_prop, start),
+ (user_action, user_prop, start + 1),
+ (exe_action, None, start + 2),
+ ])
+ add_data(self.db, "InstallUISequence",
+ [(machine_action, machine_prop, start),
+ (user_action, user_prop, start + 1),
+ (exe_action, None, start + 2),
+ ])
+ add_data(self.db, "Condition",
+ [("Python" + ver, 0, "NOT TARGETDIR" + ver)])
+ start += 4
+ assert start < 500
+
+ def add_scripts(self):
+ if self.install_script:
+ start = 6800
+ for ver in self.versions + [self.other_version]:
+ install_action = "install_script." + ver
+ exe_prop = "PYTHON" + ver
+ add_data(self.db, "CustomAction",
+ [(install_action, 50, exe_prop, self.install_script_key)])
+ add_data(self.db, "InstallExecuteSequence",
+ [(install_action, "&Python%s=3" % ver, start)])
+ start += 1
+ # XXX pre-install scripts are currently refused in finalize_options()
+ # but if this feature is completed, it will also need to add
+ # entries for each version as the above code does
+ if self.pre_install_script:
+ scriptfn = os.path.join(self.bdist_dir, "preinstall.bat")
+ with open(scriptfn, "w") as f:
+ # The batch file will be executed with [PYTHON], so that %1
+ # is the path to the Python interpreter; %0 will be the path
+ # of the batch file.
+ # rem ="""
+ # %1 %0
+ # exit
+ # """
+ # <actual script>
+ f.write('rem ="""\n%1 %0\nexit\n"""\n')
+ with open(self.pre_install_script) as fp:
+ f.write(fp.read())
+ add_data(self.db, "Binary",
+ [("PreInstall", msilib.Binary(scriptfn)),
+ ])
+ add_data(self.db, "CustomAction",
+ [("PreInstall", 2, "PreInstall", None),
+ ])
+ add_data(self.db, "InstallExecuteSequence",
+ [("PreInstall", "NOT Installed", 450),
+ ])
+
+ def add_ui(self):
+ db = self.db
+ x = y = 50
+ w = 370
+ h = 300
+ title = "[ProductName] Setup"
+
+ # see "Dialog Style Bits"
+ modal = 3 # visible | modal
+ modeless = 1 # visible
+
+ # UI customization properties
+ add_data(db, "Property",
+ # See "DefaultUIFont Property"
+ [("DefaultUIFont", "DlgFont8"),
+ # See "ErrorDialog Style Bit"
+ ("ErrorDialog", "ErrorDlg"),
+ ("Progress1", "Install"), # modified in maintenance type dlg
+ ("Progress2", "installs"),
+ ("MaintenanceForm_Action", "Repair"),
+ # possible values: ALL, JUSTME
+ ("WhichUsers", "ALL")
+ ])
+
+ # Fonts, see "TextStyle Table"
+ add_data(db, "TextStyle",
+ [("DlgFont8", "Tahoma", 9, None, 0),
+ ("DlgFontBold8", "Tahoma", 8, None, 1), #bold
+ ("VerdanaBold10", "Verdana", 10, None, 1),
+ ("VerdanaRed9", "Verdana", 9, 255, 0),
+ ])
+
+ # UI Sequences, see "InstallUISequence Table", "Using a Sequence Table"
+ # Numbers indicate sequence; see sequence.py for how these action integrate
+ add_data(db, "InstallUISequence",
+ [("PrepareDlg", "Not Privileged or Windows9x or Installed", 140),
+ ("WhichUsersDlg", "Privileged and not Windows9x and not Installed", 141),
+ # In the user interface, assume all-users installation if privileged.
+ ("SelectFeaturesDlg", "Not Installed", 1230),
+ # XXX no support for resume installations yet
+ #("ResumeDlg", "Installed AND (RESUME OR Preselected)", 1240),
+ ("MaintenanceTypeDlg", "Installed AND NOT RESUME AND NOT Preselected", 1250),
+ ("ProgressDlg", None, 1280)])
+
+ add_data(db, 'ActionText', text.ActionText)
+ add_data(db, 'UIText', text.UIText)
+ #####################################################################
+ # Standard dialogs: FatalError, UserExit, ExitDialog
+ fatal=PyDialog(db, "FatalError", x, y, w, h, modal, title,
+ "Finish", "Finish", "Finish")
+ fatal.title("[ProductName] Installer ended prematurely")
+ fatal.back("< Back", "Finish", active = 0)
+ fatal.cancel("Cancel", "Back", active = 0)
+ fatal.text("Description1", 15, 70, 320, 80, 0x30003,
+ "[ProductName] setup ended prematurely because of an error. Your system has not been modified. To install this program at a later time, please run the installation again.")
+ fatal.text("Description2", 15, 155, 320, 20, 0x30003,
+ "Click the Finish button to exit the Installer.")
+ c=fatal.next("Finish", "Cancel", name="Finish")
+ c.event("EndDialog", "Exit")
+
+ user_exit=PyDialog(db, "UserExit", x, y, w, h, modal, title,
+ "Finish", "Finish", "Finish")
+ user_exit.title("[ProductName] Installer was interrupted")
+ user_exit.back("< Back", "Finish", active = 0)
+ user_exit.cancel("Cancel", "Back", active = 0)
+ user_exit.text("Description1", 15, 70, 320, 80, 0x30003,
+ "[ProductName] setup was interrupted. Your system has not been modified. "
+ "To install this program at a later time, please run the installation again.")
+ user_exit.text("Description2", 15, 155, 320, 20, 0x30003,
+ "Click the Finish button to exit the Installer.")
+ c = user_exit.next("Finish", "Cancel", name="Finish")
+ c.event("EndDialog", "Exit")
+
+ exit_dialog = PyDialog(db, "ExitDialog", x, y, w, h, modal, title,
+ "Finish", "Finish", "Finish")
+ exit_dialog.title("Completing the [ProductName] Installer")
+ exit_dialog.back("< Back", "Finish", active = 0)
+ exit_dialog.cancel("Cancel", "Back", active = 0)
+ exit_dialog.text("Description", 15, 235, 320, 20, 0x30003,
+ "Click the Finish button to exit the Installer.")
+ c = exit_dialog.next("Finish", "Cancel", name="Finish")
+ c.event("EndDialog", "Return")
+
+ #####################################################################
+ # Required dialog: FilesInUse, ErrorDlg
+ inuse = PyDialog(db, "FilesInUse",
+ x, y, w, h,
+ 19, # KeepModeless|Modal|Visible
+ title,
+ "Retry", "Retry", "Retry", bitmap=False)
+ inuse.text("Title", 15, 6, 200, 15, 0x30003,
+ r"{\DlgFontBold8}Files in Use")
+ inuse.text("Description", 20, 23, 280, 20, 0x30003,
+ "Some files that need to be updated are currently in use.")
+ inuse.text("Text", 20, 55, 330, 50, 3,
+ "The following applications are using files that need to be updated by this setup. Close these applications and then click Retry to continue the installation or Cancel to exit it.")
+ inuse.control("List", "ListBox", 20, 107, 330, 130, 7, "FileInUseProcess",
+ None, None, None)
+ c=inuse.back("Exit", "Ignore", name="Exit")
+ c.event("EndDialog", "Exit")
+ c=inuse.next("Ignore", "Retry", name="Ignore")
+ c.event("EndDialog", "Ignore")
+ c=inuse.cancel("Retry", "Exit", name="Retry")
+ c.event("EndDialog","Retry")
+
+ # See "Error Dialog". See "ICE20" for the required names of the controls.
+ error = Dialog(db, "ErrorDlg",
+ 50, 10, 330, 101,
+ 65543, # Error|Minimize|Modal|Visible
+ title,
+ "ErrorText", None, None)
+ error.text("ErrorText", 50,9,280,48,3, "")
+ #error.control("ErrorIcon", "Icon", 15, 9, 24, 24, 5242881, None, "py.ico", None, None)
+ error.pushbutton("N",120,72,81,21,3,"No",None).event("EndDialog","ErrorNo")
+ error.pushbutton("Y",240,72,81,21,3,"Yes",None).event("EndDialog","ErrorYes")
+ error.pushbutton("A",0,72,81,21,3,"Abort",None).event("EndDialog","ErrorAbort")
+ error.pushbutton("C",42,72,81,21,3,"Cancel",None).event("EndDialog","ErrorCancel")
+ error.pushbutton("I",81,72,81,21,3,"Ignore",None).event("EndDialog","ErrorIgnore")
+ error.pushbutton("O",159,72,81,21,3,"Ok",None).event("EndDialog","ErrorOk")
+ error.pushbutton("R",198,72,81,21,3,"Retry",None).event("EndDialog","ErrorRetry")
+
+ #####################################################################
+ # Global "Query Cancel" dialog
+ cancel = Dialog(db, "CancelDlg", 50, 10, 260, 85, 3, title,
+ "No", "No", "No")
+ cancel.text("Text", 48, 15, 194, 30, 3,
+ "Are you sure you want to cancel [ProductName] installation?")
+ #cancel.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None,
+ # "py.ico", None, None)
+ c=cancel.pushbutton("Yes", 72, 57, 56, 17, 3, "Yes", "No")
+ c.event("EndDialog", "Exit")
+
+ c=cancel.pushbutton("No", 132, 57, 56, 17, 3, "No", "Yes")
+ c.event("EndDialog", "Return")
+
+ #####################################################################
+ # Global "Wait for costing" dialog
+ costing = Dialog(db, "WaitForCostingDlg", 50, 10, 260, 85, modal, title,
+ "Return", "Return", "Return")
+ costing.text("Text", 48, 15, 194, 30, 3,
+ "Please wait while the installer finishes determining your disk space requirements.")
+ c = costing.pushbutton("Return", 102, 57, 56, 17, 3, "Return", None)
+ c.event("EndDialog", "Exit")
+
+ #####################################################################
+ # Preparation dialog: no user input except cancellation
+ prep = PyDialog(db, "PrepareDlg", x, y, w, h, modeless, title,
+ "Cancel", "Cancel", "Cancel")
+ prep.text("Description", 15, 70, 320, 40, 0x30003,
+ "Please wait while the Installer prepares to guide you through the installation.")
+ prep.title("Welcome to the [ProductName] Installer")
+ c=prep.text("ActionText", 15, 110, 320, 20, 0x30003, "Pondering...")
+ c.mapping("ActionText", "Text")
+ c=prep.text("ActionData", 15, 135, 320, 30, 0x30003, None)
+ c.mapping("ActionData", "Text")
+ prep.back("Back", None, active=0)
+ prep.next("Next", None, active=0)
+ c=prep.cancel("Cancel", None)
+ c.event("SpawnDialog", "CancelDlg")
+
+ #####################################################################
+ # Feature (Python directory) selection
+ seldlg = PyDialog(db, "SelectFeaturesDlg", x, y, w, h, modal, title,
+ "Next", "Next", "Cancel")
+ seldlg.title("Select Python Installations")
+
+ seldlg.text("Hint", 15, 30, 300, 20, 3,
+ "Select the Python locations where %s should be installed."
+ % self.distribution.get_fullname())
+
+ seldlg.back("< Back", None, active=0)
+ c = seldlg.next("Next >", "Cancel")
+ order = 1
+ c.event("[TARGETDIR]", "[SourceDir]", ordering=order)
+ for version in self.versions + [self.other_version]:
+ order += 1
+ c.event("[TARGETDIR]", "[TARGETDIR%s]" % version,
+ "FEATURE_SELECTED AND &Python%s=3" % version,
+ ordering=order)
+ c.event("SpawnWaitDialog", "WaitForCostingDlg", ordering=order + 1)
+ c.event("EndDialog", "Return", ordering=order + 2)
+ c = seldlg.cancel("Cancel", "Features")
+ c.event("SpawnDialog", "CancelDlg")
+
+ c = seldlg.control("Features", "SelectionTree", 15, 60, 300, 120, 3,
+ "FEATURE", None, "PathEdit", None)
+ c.event("[FEATURE_SELECTED]", "1")
+ ver = self.other_version
+ install_other_cond = "FEATURE_SELECTED AND &Python%s=3" % ver
+ dont_install_other_cond = "FEATURE_SELECTED AND &Python%s<>3" % ver
+
+ c = seldlg.text("Other", 15, 200, 300, 15, 3,
+ "Provide an alternate Python location")
+ c.condition("Enable", install_other_cond)
+ c.condition("Show", install_other_cond)
+ c.condition("Disable", dont_install_other_cond)
+ c.condition("Hide", dont_install_other_cond)
+
+ c = seldlg.control("PathEdit", "PathEdit", 15, 215, 300, 16, 1,
+ "TARGETDIR" + ver, None, "Next", None)
+ c.condition("Enable", install_other_cond)
+ c.condition("Show", install_other_cond)
+ c.condition("Disable", dont_install_other_cond)
+ c.condition("Hide", dont_install_other_cond)
+
+ #####################################################################
+ # Disk cost
+ cost = PyDialog(db, "DiskCostDlg", x, y, w, h, modal, title,
+ "OK", "OK", "OK", bitmap=False)
+ cost.text("Title", 15, 6, 200, 15, 0x30003,
+ "{\DlgFontBold8}Disk Space Requirements")
+ cost.text("Description", 20, 20, 280, 20, 0x30003,
+ "The disk space required for the installation of the selected features.")
+ cost.text("Text", 20, 53, 330, 60, 3,
+ "The highlighted volumes (if any) do not have enough disk space "
+ "available for the currently selected features. You can either "
+ "remove some files from the highlighted volumes, or choose to "
+ "install less features onto local drive(s), or select different "
+ "destination drive(s).")
+ cost.control("VolumeList", "VolumeCostList", 20, 100, 330, 150, 393223,
+ None, "{120}{70}{70}{70}{70}", None, None)
+ cost.xbutton("OK", "Ok", None, 0.5).event("EndDialog", "Return")
+
+ #####################################################################
+ # WhichUsers Dialog. Only available on NT, and for privileged users.
+ # This must be run before FindRelatedProducts, because that will
+ # take into account whether the previous installation was per-user
+ # or per-machine. We currently don't support going back to this
+ # dialog after "Next" was selected; to support this, we would need to
+ # find how to reset the ALLUSERS property, and how to re-run
+ # FindRelatedProducts.
+ # On Windows9x, the ALLUSERS property is ignored on the command line
+ # and in the Property table, but installer fails according to the documentation
+ # if a dialog attempts to set ALLUSERS.
+ whichusers = PyDialog(db, "WhichUsersDlg", x, y, w, h, modal, title,
+ "AdminInstall", "Next", "Cancel")
+ whichusers.title("Select whether to install [ProductName] for all users of this computer.")
+ # A radio group with two options: allusers, justme
+ g = whichusers.radiogroup("AdminInstall", 15, 60, 260, 50, 3,
+ "WhichUsers", "", "Next")
+ g.add("ALL", 0, 5, 150, 20, "Install for all users")
+ g.add("JUSTME", 0, 25, 150, 20, "Install just for me")
+
+ whichusers.back("Back", None, active=0)
+
+ c = whichusers.next("Next >", "Cancel")
+ c.event("[ALLUSERS]", "1", 'WhichUsers="ALL"', 1)
+ c.event("EndDialog", "Return", ordering = 2)
+
+ c = whichusers.cancel("Cancel", "AdminInstall")
+ c.event("SpawnDialog", "CancelDlg")
+
+ #####################################################################
+ # Installation Progress dialog (modeless)
+ progress = PyDialog(db, "ProgressDlg", x, y, w, h, modeless, title,
+ "Cancel", "Cancel", "Cancel", bitmap=False)
+ progress.text("Title", 20, 15, 200, 15, 0x30003,
+ "{\DlgFontBold8}[Progress1] [ProductName]")
+ progress.text("Text", 35, 65, 300, 30, 3,
+ "Please wait while the Installer [Progress2] [ProductName]. "
+ "This may take several minutes.")
+ progress.text("StatusLabel", 35, 100, 35, 20, 3, "Status:")
+
+ c=progress.text("ActionText", 70, 100, w-70, 20, 3, "Pondering...")
+ c.mapping("ActionText", "Text")
+
+ #c=progress.text("ActionData", 35, 140, 300, 20, 3, None)
+ #c.mapping("ActionData", "Text")
+
+ c=progress.control("ProgressBar", "ProgressBar", 35, 120, 300, 10, 65537,
+ None, "Progress done", None, None)
+ c.mapping("SetProgress", "Progress")
+
+ progress.back("< Back", "Next", active=False)
+ progress.next("Next >", "Cancel", active=False)
+ progress.cancel("Cancel", "Back").event("SpawnDialog", "CancelDlg")
+
+ ###################################################################
+ # Maintenance type: repair/uninstall
+ maint = PyDialog(db, "MaintenanceTypeDlg", x, y, w, h, modal, title,
+ "Next", "Next", "Cancel")
+ maint.title("Welcome to the [ProductName] Setup Wizard")
+ maint.text("BodyText", 15, 63, 330, 42, 3,
+ "Select whether you want to repair or remove [ProductName].")
+ g=maint.radiogroup("RepairRadioGroup", 15, 108, 330, 60, 3,
+ "MaintenanceForm_Action", "", "Next")
+ #g.add("Change", 0, 0, 200, 17, "&Change [ProductName]")
+ g.add("Repair", 0, 18, 200, 17, "&Repair [ProductName]")
+ g.add("Remove", 0, 36, 200, 17, "Re&move [ProductName]")
+
+ maint.back("< Back", None, active=False)
+ c=maint.next("Finish", "Cancel")
+ # Change installation: Change progress dialog to "Change", then ask
+ # for feature selection
+ #c.event("[Progress1]", "Change", 'MaintenanceForm_Action="Change"', 1)
+ #c.event("[Progress2]", "changes", 'MaintenanceForm_Action="Change"', 2)
+
+ # Reinstall: Change progress dialog to "Repair", then invoke reinstall
+ # Also set list of reinstalled features to "ALL"
+ c.event("[REINSTALL]", "ALL", 'MaintenanceForm_Action="Repair"', 5)
+ c.event("[Progress1]", "Repairing", 'MaintenanceForm_Action="Repair"', 6)
+ c.event("[Progress2]", "repairs", 'MaintenanceForm_Action="Repair"', 7)
+ c.event("Reinstall", "ALL", 'MaintenanceForm_Action="Repair"', 8)
+
+ # Uninstall: Change progress to "Remove", then invoke uninstall
+ # Also set list of removed features to "ALL"
+ c.event("[REMOVE]", "ALL", 'MaintenanceForm_Action="Remove"', 11)
+ c.event("[Progress1]", "Removing", 'MaintenanceForm_Action="Remove"', 12)
+ c.event("[Progress2]", "removes", 'MaintenanceForm_Action="Remove"', 13)
+ c.event("Remove", "ALL", 'MaintenanceForm_Action="Remove"', 14)
+
+ # Close dialog when maintenance action scheduled
+ c.event("EndDialog", "Return", 'MaintenanceForm_Action<>"Change"', 20)
+ #c.event("NewDialog", "SelectFeaturesDlg", 'MaintenanceForm_Action="Change"', 21)
+
+ maint.cancel("Cancel", "RepairRadioGroup").event("SpawnDialog", "CancelDlg")
+
+ def get_installer_filename(self, fullname):
+ # Factored out to allow overriding in subclasses
+ if self.target_version:
+ base_name = "%s.%s-py%s.msi" % (fullname, self.plat_name,
+ self.target_version)
+ else:
+ base_name = "%s.%s.msi" % (fullname, self.plat_name)
+ installer_name = os.path.join(self.dist_dir, base_name)
+ return installer_name
diff --git a/Lib/packaging/command/bdist_wininst.py b/Lib/packaging/command/bdist_wininst.py
new file mode 100644
index 0000000..4e6b79e
--- /dev/null
+++ b/Lib/packaging/command/bdist_wininst.py
@@ -0,0 +1,345 @@
+"""Create an executable installer for Windows."""
+
+import sys
+import os
+
+from shutil import rmtree
+from sysconfig import get_python_version
+from packaging.command.cmd import Command
+from packaging.errors import PackagingOptionError, PackagingPlatformError
+from packaging import logger
+from packaging.util import get_platform
+
+
+class bdist_wininst(Command):
+
+ description = "create an executable installer for Windows"
+
+ user_options = [('bdist-dir=', None,
+ "temporary directory for creating the distribution"),
+ ('plat-name=', 'p',
+ "platform name to embed in generated filenames "
+ "(default: %s)" % get_platform()),
+ ('keep-temp', 'k',
+ "keep the pseudo-installation tree around after " +
+ "creating the distribution archive"),
+ ('target-version=', None,
+ "require a specific python version" +
+ " on the target system"),
+ ('no-target-compile', 'c',
+ "do not compile .py to .pyc on the target system"),
+ ('no-target-optimize', 'o',
+ "do not compile .py to .pyo (optimized)"
+ "on the target system"),
+ ('dist-dir=', 'd',
+ "directory to put final built distributions in"),
+ ('bitmap=', 'b',
+ "bitmap to use for the installer instead of python-powered logo"),
+ ('title=', 't',
+ "title to display on the installer background instead of default"),
+ ('skip-build', None,
+ "skip rebuilding everything (for testing/debugging)"),
+ ('install-script=', None,
+ "basename of installation script to be run after"
+ "installation or before deinstallation"),
+ ('pre-install-script=', None,
+ "Fully qualified filename of a script to be run before "
+ "any files are installed. This script need not be in the "
+ "distribution"),
+ ('user-access-control=', None,
+ "specify Vista's UAC handling - 'none'/default=no "
+ "handling, 'auto'=use UAC if target Python installed for "
+ "all users, 'force'=always use UAC"),
+ ]
+
+ boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize',
+ 'skip-build']
+
+ def initialize_options(self):
+ self.bdist_dir = None
+ self.plat_name = None
+ self.keep_temp = False
+ self.no_target_compile = False
+ self.no_target_optimize = False
+ self.target_version = None
+ self.dist_dir = None
+ self.bitmap = None
+ self.title = None
+ self.skip_build = None
+ self.install_script = None
+ self.pre_install_script = None
+ self.user_access_control = None
+
+
+ def finalize_options(self):
+ self.set_undefined_options('bdist', 'skip_build')
+
+ if self.bdist_dir is None:
+ if self.skip_build and self.plat_name:
+ # If build is skipped and plat_name is overridden, bdist will
+ # not see the correct 'plat_name' - so set that up manually.
+ bdist = self.distribution.get_command_obj('bdist')
+ bdist.plat_name = self.plat_name
+ # next the command will be initialized using that name
+ bdist_base = self.get_finalized_command('bdist').bdist_base
+ self.bdist_dir = os.path.join(bdist_base, 'wininst')
+
+ if not self.target_version:
+ self.target_version = ""
+
+ if not self.skip_build and self.distribution.has_ext_modules():
+ short_version = get_python_version()
+ if self.target_version and self.target_version != short_version:
+ raise PackagingOptionError("target version can only be %s, or the '--skip-build'" \
+ " option must be specified" % (short_version,))
+ self.target_version = short_version
+
+ self.set_undefined_options('bdist', 'dist_dir', 'plat_name')
+
+ if self.install_script:
+ for script in self.distribution.scripts:
+ if self.install_script == os.path.basename(script):
+ break
+ else:
+ raise PackagingOptionError("install_script '%s' not found in scripts" % \
+ self.install_script)
+
+ def run(self):
+ if (sys.platform != "win32" and
+ (self.distribution.has_ext_modules() or
+ self.distribution.has_c_libraries())):
+ raise PackagingPlatformError \
+ ("distribution contains extensions and/or C libraries; "
+ "must be compiled on a Windows 32 platform")
+
+ if not self.skip_build:
+ self.run_command('build')
+
+ install = self.reinitialize_command('install', reinit_subcommands=True)
+ install.root = self.bdist_dir
+ install.skip_build = self.skip_build
+ install.warn_dir = False
+ install.plat_name = self.plat_name
+
+ install_lib = self.reinitialize_command('install_lib')
+ # we do not want to include pyc or pyo files
+ install_lib.compile = False
+ install_lib.optimize = 0
+
+ if self.distribution.has_ext_modules():
+ # If we are building an installer for a Python version other
+ # than the one we are currently running, then we need to ensure
+ # our build_lib reflects the other Python version rather than ours.
+ # Note that for target_version!=sys.version, we must have skipped the
+ # build step, so there is no issue with enforcing the build of this
+ # version.
+ target_version = self.target_version
+ if not target_version:
+ assert self.skip_build, "Should have already checked this"
+ target_version = sys.version[0:3]
+ plat_specifier = ".%s-%s" % (self.plat_name, target_version)
+ build = self.get_finalized_command('build')
+ build.build_lib = os.path.join(build.build_base,
+ 'lib' + plat_specifier)
+
+ # Use a custom scheme for the zip-file, because we have to decide
+ # at installation time which scheme to use.
+ for key in ('purelib', 'platlib', 'headers', 'scripts', 'data'):
+ value = key.upper()
+ if key == 'headers':
+ value = value + '/Include/$dist_name'
+ setattr(install,
+ 'install_' + key,
+ value)
+
+ logger.info("installing to %s", self.bdist_dir)
+ install.ensure_finalized()
+
+ # avoid warning of 'install_lib' about installing
+ # into a directory not in sys.path
+ sys.path.insert(0, os.path.join(self.bdist_dir, 'PURELIB'))
+
+ install.run()
+
+ del sys.path[0]
+
+ # And make an archive relative to the root of the
+ # pseudo-installation tree.
+ from tempfile import NamedTemporaryFile
+ archive_basename = NamedTemporaryFile().name
+ fullname = self.distribution.get_fullname()
+ arcname = self.make_archive(archive_basename, "zip",
+ root_dir=self.bdist_dir)
+ # create an exe containing the zip-file
+ self.create_exe(arcname, fullname, self.bitmap)
+ if self.distribution.has_ext_modules():
+ pyversion = get_python_version()
+ else:
+ pyversion = 'any'
+ self.distribution.dist_files.append(('bdist_wininst', pyversion,
+ self.get_installer_filename(fullname)))
+ # remove the zip-file again
+ logger.debug("removing temporary file '%s'", arcname)
+ os.remove(arcname)
+
+ if not self.keep_temp:
+ logger.info('removing %s', self.bdist_dir)
+ if not self.dry_run:
+ rmtree(self.bdist_dir)
+
+ def get_inidata(self):
+ # Return data describing the installation.
+
+ lines = []
+ metadata = self.distribution.metadata
+
+ # Write the [metadata] section.
+ lines.append("[metadata]")
+
+ # 'info' will be displayed in the installer's dialog box,
+ # describing the items to be installed.
+ info = (metadata.long_description or '') + '\n'
+
+ # Escape newline characters
+ def escape(s):
+ return s.replace("\n", "\\n")
+
+ for name in ["author", "author_email", "description", "maintainer",
+ "maintainer_email", "name", "url", "version"]:
+ data = getattr(metadata, name, "")
+ if data:
+ info = info + ("\n %s: %s" % \
+ (name.capitalize(), escape(data)))
+ lines.append("%s=%s" % (name, escape(data)))
+
+ # The [setup] section contains entries controlling
+ # the installer runtime.
+ lines.append("\n[Setup]")
+ if self.install_script:
+ lines.append("install_script=%s" % self.install_script)
+ lines.append("info=%s" % escape(info))
+ lines.append("target_compile=%d" % (not self.no_target_compile))
+ lines.append("target_optimize=%d" % (not self.no_target_optimize))
+ if self.target_version:
+ lines.append("target_version=%s" % self.target_version)
+ if self.user_access_control:
+ lines.append("user_access_control=%s" % self.user_access_control)
+
+ title = self.title or self.distribution.get_fullname()
+ lines.append("title=%s" % escape(title))
+ import time
+ import packaging
+ build_info = "Built %s with packaging-%s" % \
+ (time.ctime(time.time()), packaging.__version__)
+ lines.append("build_info=%s" % build_info)
+ return "\n".join(lines)
+
+ def create_exe(self, arcname, fullname, bitmap=None):
+ import struct
+
+ self.mkpath(self.dist_dir)
+
+ cfgdata = self.get_inidata()
+
+ installer_name = self.get_installer_filename(fullname)
+ logger.info("creating %s", installer_name)
+
+ if bitmap:
+ with open(bitmap, "rb") as fp:
+ bitmapdata = fp.read()
+ bitmaplen = len(bitmapdata)
+ else:
+ bitmaplen = 0
+
+ with open(installer_name, "wb") as file:
+ file.write(self.get_exe_bytes())
+ if bitmap:
+ file.write(bitmapdata)
+
+ # Convert cfgdata from unicode to ascii, mbcs encoded
+ if isinstance(cfgdata, str):
+ cfgdata = cfgdata.encode("mbcs")
+
+ # Append the pre-install script
+ cfgdata = cfgdata + b"\0"
+ if self.pre_install_script:
+ # We need to normalize newlines, so we open in text mode and
+ # convert back to bytes. "latin-1" simply avoids any possible
+ # failures.
+ with open(self.pre_install_script, encoding="latin-1") as fp:
+ script_data = fp.read().encode("latin-1")
+ cfgdata = cfgdata + script_data + b"\n\0"
+ else:
+ # empty pre-install script
+ cfgdata = cfgdata + b"\0"
+ file.write(cfgdata)
+
+ # The 'magic number' 0x1234567B is used to make sure that the
+ # binary layout of 'cfgdata' is what the wininst.exe binary
+ # expects. If the layout changes, increment that number, make
+ # the corresponding changes to the wininst.exe sources, and
+ # recompile them.
+ header = struct.pack("<iii",
+ 0x1234567B, # tag
+ len(cfgdata), # length
+ bitmaplen, # number of bytes in bitmap
+ )
+ file.write(header)
+ with open(arcname, "rb") as fp:
+ file.write(fp.read())
+
+ def get_installer_filename(self, fullname):
+ # Factored out to allow overriding in subclasses
+ if self.target_version:
+ # if we create an installer for a specific python version,
+ # it's better to include this in the name
+ installer_name = os.path.join(self.dist_dir,
+ "%s.%s-py%s.exe" %
+ (fullname, self.plat_name, self.target_version))
+ else:
+ installer_name = os.path.join(self.dist_dir,
+ "%s.%s.exe" % (fullname, self.plat_name))
+ return installer_name
+
+ def get_exe_bytes(self):
+ from packaging.compiler.msvccompiler import get_build_version
+ # If a target-version other than the current version has been
+ # specified, then using the MSVC version from *this* build is no good.
+ # Without actually finding and executing the target version and parsing
+ # its sys.version, we just hard-code our knowledge of old versions.
+ # NOTE: Possible alternative is to allow "--target-version" to
+ # specify a Python executable rather than a simple version string.
+ # We can then execute this program to obtain any info we need, such
+ # as the real sys.version string for the build.
+ cur_version = get_python_version()
+ if self.target_version and self.target_version != cur_version:
+ # If the target version is *later* than us, then we assume they
+ # use what we use
+ # string compares seem wrong, but are what sysconfig.py itself uses
+ if self.target_version > cur_version:
+ bv = get_build_version()
+ else:
+ if self.target_version < "2.4":
+ bv = 6.0
+ else:
+ bv = 7.1
+ else:
+ # for current version - use authoritative check.
+ bv = get_build_version()
+
+ # wininst-x.y.exe is in the same directory as this file
+ directory = os.path.dirname(__file__)
+ # we must use a wininst-x.y.exe built with the same C compiler
+ # used for python. XXX What about mingw, borland, and so on?
+
+ # if plat_name starts with "win" but is not "win32"
+ # we want to strip "win" and leave the rest (e.g. -amd64)
+ # for all other cases, we don't want any suffix
+ if self.plat_name != 'win32' and self.plat_name[:3] == 'win':
+ sfix = self.plat_name[3:]
+ else:
+ sfix = ''
+
+ filename = os.path.join(directory, "wininst-%.1f%s.exe" % (bv, sfix))
+ with open(filename, "rb") as fp:
+ return fp.read()
diff --git a/Lib/packaging/command/build.py b/Lib/packaging/command/build.py
new file mode 100644
index 0000000..2e5eb8b
--- /dev/null
+++ b/Lib/packaging/command/build.py
@@ -0,0 +1,151 @@
+"""Main build command, which calls the other build_* commands."""
+
+import sys
+import os
+
+from packaging.util import get_platform
+from packaging.command.cmd import Command
+from packaging.errors import PackagingOptionError
+from packaging.compiler import show_compilers
+
+
+class build(Command):
+
+ description = "build everything needed to install"
+
+ user_options = [
+ ('build-base=', 'b',
+ "base directory for build library"),
+ ('build-purelib=', None,
+ "build directory for platform-neutral distributions"),
+ ('build-platlib=', None,
+ "build directory for platform-specific distributions"),
+ ('build-lib=', None,
+ "build directory for all distribution (defaults to either " +
+ "build-purelib or build-platlib"),
+ ('build-scripts=', None,
+ "build directory for scripts"),
+ ('build-temp=', 't',
+ "temporary build directory"),
+ ('plat-name=', 'p',
+ "platform name to build for, if supported "
+ "(default: %s)" % get_platform()),
+ ('compiler=', 'c',
+ "specify the compiler type"),
+ ('debug', 'g',
+ "compile extensions and libraries with debugging information"),
+ ('force', 'f',
+ "forcibly build everything (ignore file timestamps)"),
+ ('executable=', 'e',
+ "specify final destination interpreter path (build.py)"),
+ ('use-2to3', None,
+ "use 2to3 to make source python 3.x compatible"),
+ ('convert-2to3-doctests', None,
+ "use 2to3 to convert doctests in separate text files"),
+ ('use-2to3-fixers', None,
+ "list additional fixers opted for during 2to3 conversion"),
+ ]
+
+ boolean_options = ['debug', 'force']
+
+ help_options = [
+ ('help-compiler', None,
+ "list available compilers", show_compilers),
+ ]
+
+ def initialize_options(self):
+ self.build_base = 'build'
+ # these are decided only after 'build_base' has its final value
+ # (unless overridden by the user or client)
+ self.build_purelib = None
+ self.build_platlib = None
+ self.build_lib = None
+ self.build_temp = None
+ self.build_scripts = None
+ self.compiler = None
+ self.plat_name = None
+ self.debug = None
+ self.force = False
+ self.executable = None
+ self.use_2to3 = False
+ self.convert_2to3_doctests = None
+ self.use_2to3_fixers = None
+
+ def finalize_options(self):
+ if self.plat_name is None:
+ self.plat_name = get_platform()
+ else:
+ # plat-name only supported for windows (other platforms are
+ # supported via ./configure flags, if at all). Avoid misleading
+ # other platforms.
+ if os.name != 'nt':
+ raise PackagingOptionError(
+ "--plat-name only supported on Windows (try "
+ "using './configure --help' on your platform)")
+
+ plat_specifier = ".%s-%s" % (self.plat_name, sys.version[0:3])
+
+ # Make it so Python 2.x and Python 2.x with --with-pydebug don't
+ # share the same build directories. Doing so confuses the build
+ # process for C modules
+ if hasattr(sys, 'gettotalrefcount'):
+ plat_specifier += '-pydebug'
+
+ # 'build_purelib' and 'build_platlib' just default to 'lib' and
+ # 'lib.<plat>' under the base build directory. We only use one of
+ # them for a given distribution, though --
+ if self.build_purelib is None:
+ self.build_purelib = os.path.join(self.build_base, 'lib')
+ if self.build_platlib is None:
+ self.build_platlib = os.path.join(self.build_base,
+ 'lib' + plat_specifier)
+
+ # 'build_lib' is the actual directory that we will use for this
+ # particular module distribution -- if user didn't supply it, pick
+ # one of 'build_purelib' or 'build_platlib'.
+ if self.build_lib is None:
+ if self.distribution.ext_modules:
+ self.build_lib = self.build_platlib
+ else:
+ self.build_lib = self.build_purelib
+
+ # 'build_temp' -- temporary directory for compiler turds,
+ # "build/temp.<plat>"
+ if self.build_temp is None:
+ self.build_temp = os.path.join(self.build_base,
+ 'temp' + plat_specifier)
+ if self.build_scripts is None:
+ self.build_scripts = os.path.join(self.build_base,
+ 'scripts-' + sys.version[0:3])
+
+ if self.executable is None:
+ self.executable = os.path.normpath(sys.executable)
+
+ def run(self):
+ # Run all relevant sub-commands. This will be some subset of:
+ # - build_py - pure Python modules
+ # - build_clib - standalone C libraries
+ # - build_ext - Python extension modules
+ # - build_scripts - Python scripts
+ for cmd_name in self.get_sub_commands():
+ self.run_command(cmd_name)
+
+ # -- Predicates for the sub-command list ---------------------------
+
+ def has_pure_modules(self):
+ return self.distribution.has_pure_modules()
+
+ def has_c_libraries(self):
+ return self.distribution.has_c_libraries()
+
+ def has_ext_modules(self):
+ return self.distribution.has_ext_modules()
+
+ def has_scripts(self):
+ return self.distribution.has_scripts()
+
+ sub_commands = [('build_py', has_pure_modules),
+ ('build_clib', has_c_libraries),
+ ('build_ext', has_ext_modules),
+ ('build_scripts', has_scripts),
+ ]
diff --git a/Lib/packaging/command/build_clib.py b/Lib/packaging/command/build_clib.py
new file mode 100644
index 0000000..5388ccd
--- /dev/null
+++ b/Lib/packaging/command/build_clib.py
@@ -0,0 +1,197 @@
+"""Build C/C++ libraries.
+
+This command is useful to build libraries that are included in the
+distribution and needed by extension modules.
+"""
+
+# XXX this module has *lots* of code ripped-off quite transparently from
+# build_ext.py -- not surprisingly really, as the work required to build
+# a static library from a collection of C source files is not really all
+# that different from what's required to build a shared object file from
+# a collection of C source files. Nevertheless, I haven't done the
+# necessary refactoring to account for the overlap in code between the
+# two modules, mainly because a number of subtle details changed in the
+# cut 'n paste. Sigh.
+
+import os
+from packaging.command.cmd import Command
+from packaging.errors import PackagingSetupError
+from packaging.compiler import customize_compiler, new_compiler
+from packaging import logger
+
+
+def show_compilers():
+ from packaging.compiler import show_compilers
+ show_compilers()
+
+
+class build_clib(Command):
+
+ description = "build C/C++ libraries used by extension modules"
+
+ user_options = [
+ ('build-clib=', 'b',
+ "directory to build C/C++ libraries to"),
+ ('build-temp=', 't',
+ "directory to put temporary build by-products"),
+ ('debug', 'g',
+ "compile with debugging information"),
+ ('force', 'f',
+ "forcibly build everything (ignore file timestamps)"),
+ ('compiler=', 'c',
+ "specify the compiler type"),
+ ]
+
+ boolean_options = ['debug', 'force']
+
+ help_options = [
+ ('help-compiler', None,
+ "list available compilers", show_compilers),
+ ]
+
+ def initialize_options(self):
+ self.build_clib = None
+ self.build_temp = None
+
+ # List of libraries to build
+ self.libraries = None
+
+ # Compilation options for all libraries
+ self.include_dirs = None
+ self.define = None
+ self.undef = None
+ self.debug = None
+ self.force = False
+ self.compiler = None
+
+
+ def finalize_options(self):
+ # This might be confusing: both build-clib and build-temp default
+ # to build-temp as defined by the "build" command. This is because
+ # I think that C libraries are really just temporary build
+ # by-products, at least from the point of view of building Python
+ # extensions -- but I want to keep my options open.
+ self.set_undefined_options('build',
+ ('build_temp', 'build_clib'),
+ ('build_temp', 'build_temp'),
+ 'compiler', 'debug', 'force')
+
+ self.libraries = self.distribution.libraries
+ if self.libraries:
+ self.check_library_list(self.libraries)
+
+ if self.include_dirs is None:
+ self.include_dirs = self.distribution.include_dirs or []
+ if isinstance(self.include_dirs, str):
+ self.include_dirs = self.include_dirs.split(os.pathsep)
+
+ # XXX same as for build_ext -- what about 'self.define' and
+ # 'self.undef' ?
+
+ def run(self):
+ if not self.libraries:
+ return
+
+ # Yech -- this is cut 'n pasted from build_ext.py!
+ self.compiler = new_compiler(compiler=self.compiler,
+ dry_run=self.dry_run,
+ force=self.force)
+ customize_compiler(self.compiler)
+
+ if self.include_dirs is not None:
+ self.compiler.set_include_dirs(self.include_dirs)
+ if self.define is not None:
+ # 'define' option is a list of (name,value) tuples
+ for name, value in self.define:
+ self.compiler.define_macro(name, value)
+ if self.undef is not None:
+ for macro in self.undef:
+ self.compiler.undefine_macro(macro)
+
+ self.build_libraries(self.libraries)
+
+
+ def check_library_list(self, libraries):
+ """Ensure that the list of libraries is valid.
+
+ `library` is presumably provided as a command option 'libraries'.
+ This method checks that it is a list of 2-tuples, where the tuples
+ are (library_name, build_info_dict).
+
+ Raise PackagingSetupError if the structure is invalid anywhere;
+ just returns otherwise.
+ """
+ if not isinstance(libraries, list):
+ raise PackagingSetupError("'libraries' option must be a list of tuples")
+
+ for lib in libraries:
+ if not isinstance(lib, tuple) and len(lib) != 2:
+ raise PackagingSetupError("each element of 'libraries' must a 2-tuple")
+
+ name, build_info = lib
+
+ if not isinstance(name, str):
+ raise PackagingSetupError("first element of each tuple in 'libraries' " + \
+ "must be a string (the library name)")
+ if '/' in name or (os.sep != '/' and os.sep in name):
+ raise PackagingSetupError(("bad library name '%s': " +
+ "may not contain directory separators") % \
+ lib[0])
+
+ if not isinstance(build_info, dict):
+ raise PackagingSetupError("second element of each tuple in 'libraries' " + \
+ "must be a dictionary (build info)")
+
+ def get_library_names(self):
+ # Assume the library list is valid -- 'check_library_list()' is
+ # called from 'finalize_options()', so it should be!
+ if not self.libraries:
+ return None
+
+ lib_names = []
+ for lib_name, build_info in self.libraries:
+ lib_names.append(lib_name)
+ return lib_names
+
+
+ def get_source_files(self):
+ self.check_library_list(self.libraries)
+ filenames = []
+ for lib_name, build_info in self.libraries:
+ sources = build_info.get('sources')
+ if sources is None or not isinstance(sources, (list, tuple)):
+ raise PackagingSetupError(("in 'libraries' option (library '%s'), "
+ "'sources' must be present and must be "
+ "a list of source filenames") % lib_name)
+
+ filenames.extend(sources)
+ return filenames
+
+ def build_libraries(self, libraries):
+ for lib_name, build_info in libraries:
+ sources = build_info.get('sources')
+ if sources is None or not isinstance(sources, (list, tuple)):
+ raise PackagingSetupError(("in 'libraries' option (library '%s'), " +
+ "'sources' must be present and must be " +
+ "a list of source filenames") % lib_name)
+ sources = list(sources)
+
+ logger.info("building '%s' library", lib_name)
+
+ # First, compile the source code to object files in the library
+ # directory. (This should probably change to putting object
+ # files in a temporary build directory.)
+ macros = build_info.get('macros')
+ include_dirs = build_info.get('include_dirs')
+ objects = self.compiler.compile(sources,
+ output_dir=self.build_temp,
+ macros=macros,
+ include_dirs=include_dirs,
+ debug=self.debug)
+
+ # Now "link" the object files together into a static library.
+ # (On Unix at least, this isn't really linking -- it just
+ # builds an archive. Whatever.)
+ self.compiler.create_static_lib(objects, lib_name,
+ output_dir=self.build_clib,
+ debug=self.debug)
diff --git a/Lib/packaging/command/build_ext.py b/Lib/packaging/command/build_ext.py
new file mode 100644
index 0000000..4f375b6
--- /dev/null
+++ b/Lib/packaging/command/build_ext.py
@@ -0,0 +1,642 @@
+"""Build extension modules."""
+
+import os
+import re
+import sys
+import site
+import sysconfig
+
+from packaging.util import get_platform
+from packaging.command.cmd import Command
+from packaging.errors import (CCompilerError, CompileError, PackagingError,
+ PackagingPlatformError, PackagingSetupError)
+from packaging.compiler import customize_compiler, show_compilers
+from packaging.util import newer_group
+from packaging.compiler.extension import Extension
+from packaging import logger
+
+if os.name == 'nt':
+ from packaging.compiler.msvccompiler import get_build_version
+ MSVC_VERSION = int(get_build_version())
+
+# An extension name is just a dot-separated list of Python NAMEs (ie.
+# the same as a fully-qualified module name).
+extension_name_re = re.compile \
+ (r'^[a-zA-Z_][a-zA-Z_0-9]*(\.[a-zA-Z_][a-zA-Z_0-9]*)*$')
+
+
+class build_ext(Command):
+
+ description = "build C/C++ extension modules (compile/link to build directory)"
+
+ # XXX thoughts on how to deal with complex command-line options like
+ # these, i.e. how to make it so fancy_getopt can suck them off the
+ # command line and turn them into the appropriate
+ # lists of tuples of what-have-you.
+ # - each command needs a callback to process its command-line options
+ # - Command.__init__() needs access to its share of the whole
+ # command line (must ultimately come from
+ # Distribution.parse_command_line())
+ # - it then calls the current command class' option-parsing
+ # callback to deal with weird options like -D, which have to
+ # parse the option text and churn out some custom data
+ # structure
+ # - that data structure (in this case, a list of 2-tuples)
+ # will then be present in the command object by the time
+ # we get to finalize_options() (i.e. the constructor
+ # takes care of both command-line and client options
+ # in between initialize_options() and finalize_options())
+
+ sep_by = " (separated by '%s')" % os.pathsep
+ user_options = [
+ ('build-lib=', 'b',
+ "directory for compiled extension modules"),
+ ('build-temp=', 't',
+ "directory for temporary files (build by-products)"),
+ ('plat-name=', 'p',
+ "platform name to cross-compile for, if supported "
+ "(default: %s)" % get_platform()),
+ ('inplace', 'i',
+ "ignore build-lib and put compiled extensions into the source " +
+ "directory alongside your pure Python modules"),
+ ('user', None,
+ "add user include, library and rpath"),
+ ('include-dirs=', 'I',
+ "list of directories to search for header files" + sep_by),
+ ('define=', 'D',
+ "C preprocessor macros to define"),
+ ('undef=', 'U',
+ "C preprocessor macros to undefine"),
+ ('libraries=', 'l',
+ "external C libraries to link with"),
+ ('library-dirs=', 'L',
+ "directories to search for external C libraries" + sep_by),
+ ('rpath=', 'R',
+ "directories to search for shared C libraries at runtime"),
+ ('link-objects=', 'O',
+ "extra explicit link objects to include in the link"),
+ ('debug', 'g',
+ "compile/link with debugging information"),
+ ('force', 'f',
+ "forcibly build everything (ignore file timestamps)"),
+ ('compiler=', 'c',
+ "specify the compiler type"),
+ ('swig-opts=', None,
+ "list of SWIG command-line options"),
+ ('swig=', None,
+ "path to the SWIG executable"),
+ ]
+
+ boolean_options = ['inplace', 'debug', 'force', 'user']
+
+
+ help_options = [
+ ('help-compiler', None,
+ "list available compilers", show_compilers),
+ ]
+
+ def initialize_options(self):
+ self.extensions = None
+ self.build_lib = None
+ self.plat_name = None
+ self.build_temp = None
+ self.inplace = False
+ self.package = None
+
+ self.include_dirs = None
+ self.define = None
+ self.undef = None
+ self.libraries = None
+ self.library_dirs = None
+ self.rpath = None
+ self.link_objects = None
+ self.debug = None
+ self.force = None
+ self.compiler = None
+ self.swig = None
+ self.swig_opts = None
+ self.user = None
+
+ def finalize_options(self):
+ self.set_undefined_options('build',
+ 'build_lib', 'build_temp', 'compiler',
+ 'debug', 'force', 'plat_name')
+
+ if self.package is None:
+ self.package = self.distribution.ext_package
+
+ # Ensure that the list of extensions is valid, i.e. it is a list of
+ # Extension objects.
+ self.extensions = self.distribution.ext_modules
+ if self.extensions:
+ if not isinstance(self.extensions, (list, tuple)):
+ type_name = (self.extensions is None and 'None'
+ or type(self.extensions).__name__)
+ raise PackagingSetupError(
+ "'ext_modules' must be a sequence of Extension instances,"
+ " not %s" % (type_name,))
+ for i, ext in enumerate(self.extensions):
+ if isinstance(ext, Extension):
+ continue # OK! (assume type-checking done
+ # by Extension constructor)
+ type_name = (ext is None and 'None' or type(ext).__name__)
+ raise PackagingSetupError(
+ "'ext_modules' item %d must be an Extension instance,"
+ " not %s" % (i, type_name))
+
+ # Make sure Python's include directories (for Python.h, pyconfig.h,
+ # etc.) are in the include search path.
+ py_include = sysconfig.get_path('include')
+ plat_py_include = sysconfig.get_path('platinclude')
+ if self.include_dirs is None:
+ self.include_dirs = self.distribution.include_dirs or []
+ if isinstance(self.include_dirs, str):
+ self.include_dirs = self.include_dirs.split(os.pathsep)
+
+ # Put the Python "system" include dir at the end, so that
+ # any local include dirs take precedence.
+ self.include_dirs.append(py_include)
+ if plat_py_include != py_include:
+ self.include_dirs.append(plat_py_include)
+
+ if isinstance(self.libraries, str):
+ self.libraries = [self.libraries]
+
+ # Life is easier if we're not forever checking for None, so
+ # simplify these options to empty lists if unset
+ if self.libraries is None:
+ self.libraries = []
+ if self.library_dirs is None:
+ self.library_dirs = []
+ elif isinstance(self.library_dirs, str):
+ self.library_dirs = self.library_dirs.split(os.pathsep)
+
+ if self.rpath is None:
+ self.rpath = []
+ elif isinstance(self.rpath, str):
+ self.rpath = self.rpath.split(os.pathsep)
+
+ # for extensions under windows use different directories
+ # for Release and Debug builds.
+ # also Python's library directory must be appended to library_dirs
+ if os.name == 'nt':
+ # the 'libs' directory is for binary installs - we assume that
+ # must be the *native* platform. But we don't really support
+ # cross-compiling via a binary install anyway, so we let it go.
+ self.library_dirs.append(os.path.join(sys.exec_prefix, 'libs'))
+ if self.debug:
+ self.build_temp = os.path.join(self.build_temp, "Debug")
+ else:
+ self.build_temp = os.path.join(self.build_temp, "Release")
+
+ # Append the source distribution include and library directories,
+ # this allows distutils on windows to work in the source tree
+ self.include_dirs.append(os.path.join(sys.exec_prefix, 'PC'))
+ if MSVC_VERSION == 9:
+ # Use the .lib files for the correct architecture
+ if self.plat_name == 'win32':
+ suffix = ''
+ else:
+ # win-amd64 or win-ia64
+ suffix = self.plat_name[4:]
+ new_lib = os.path.join(sys.exec_prefix, 'PCbuild')
+ if suffix:
+ new_lib = os.path.join(new_lib, suffix)
+ self.library_dirs.append(new_lib)
+
+ elif MSVC_VERSION == 8:
+ self.library_dirs.append(os.path.join(sys.exec_prefix,
+ 'PC', 'VS8.0'))
+ elif MSVC_VERSION == 7:
+ self.library_dirs.append(os.path.join(sys.exec_prefix,
+ 'PC', 'VS7.1'))
+ else:
+ self.library_dirs.append(os.path.join(sys.exec_prefix,
+ 'PC', 'VC6'))
+
+ # OS/2 (EMX) doesn't support Debug vs Release builds, but has the
+ # import libraries in its "Config" subdirectory
+ if os.name == 'os2':
+ self.library_dirs.append(os.path.join(sys.exec_prefix, 'Config'))
+
+ # for extensions under Cygwin and AtheOS Python's library directory must be
+ # appended to library_dirs
+ if sys.platform[:6] == 'cygwin' or sys.platform[:6] == 'atheos':
+ if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")):
+ # building third party extensions
+ self.library_dirs.append(os.path.join(sys.prefix, "lib",
+ "python" + sysconfig.get_python_version(),
+ "config"))
+ else:
+ # building python standard extensions
+ self.library_dirs.append(os.curdir)
+
+ # for extensions under Linux or Solaris with a shared Python library,
+ # Python's library directory must be appended to library_dirs
+ sysconfig.get_config_var('Py_ENABLE_SHARED')
+ if (sys.platform.startswith(('linux', 'gnu', 'sunos'))
+ and sysconfig.get_config_var('Py_ENABLE_SHARED')):
+ if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")):
+ # building third party extensions
+ self.library_dirs.append(sysconfig.get_config_var('LIBDIR'))
+ else:
+ # building python standard extensions
+ self.library_dirs.append(os.curdir)
+
+ # The argument parsing will result in self.define being a string, but
+ # it has to be a list of 2-tuples. All the preprocessor symbols
+ # specified by the 'define' option will be set to '1'. Multiple
+ # symbols can be separated with commas.
+
+ if self.define:
+ defines = self.define.split(',')
+ self.define = [(symbol, '1') for symbol in defines]
+
+ # The option for macros to undefine is also a string from the
+ # option parsing, but has to be a list. Multiple symbols can also
+ # be separated with commas here.
+ if self.undef:
+ self.undef = self.undef.split(',')
+
+ if self.swig_opts is None:
+ self.swig_opts = []
+ else:
+ self.swig_opts = self.swig_opts.split(' ')
+
+ # Finally add the user include and library directories if requested
+ if self.user:
+ user_include = os.path.join(site.USER_BASE, "include")
+ user_lib = os.path.join(site.USER_BASE, "lib")
+ if os.path.isdir(user_include):
+ self.include_dirs.append(user_include)
+ if os.path.isdir(user_lib):
+ self.library_dirs.append(user_lib)
+ self.rpath.append(user_lib)
+
+ def run(self):
+ from packaging.compiler import new_compiler
+
+ if not self.extensions:
+ return
+
+ # If we were asked to build any C/C++ libraries, make sure that the
+ # directory where we put them is in the library search path for
+ # linking extensions.
+ if self.distribution.has_c_libraries():
+ build_clib = self.get_finalized_command('build_clib')
+ self.libraries.extend(build_clib.get_library_names() or [])
+ self.library_dirs.append(build_clib.build_clib)
+
+ # Setup the CCompiler object that we'll use to do all the
+ # compiling and linking
+ self.compiler_obj = new_compiler(compiler=self.compiler,
+ dry_run=self.dry_run,
+ force=self.force)
+
+ customize_compiler(self.compiler_obj)
+ # If we are cross-compiling, init the compiler now (if we are not
+ # cross-compiling, init would not hurt, but people may rely on
+ # late initialization of compiler even if they shouldn't...)
+ if os.name == 'nt' and self.plat_name != get_platform():
+ self.compiler_obj.initialize(self.plat_name)
+
+ # And make sure that any compile/link-related options (which might
+ # come from the command line or from the setup script) are set in
+ # that CCompiler object -- that way, they automatically apply to
+ # all compiling and linking done here.
+ if self.include_dirs is not None:
+ self.compiler_obj.set_include_dirs(self.include_dirs)
+ if self.define is not None:
+ # 'define' option is a list of (name,value) tuples
+ for name, value in self.define:
+ self.compiler_obj.define_macro(name, value)
+ if self.undef is not None:
+ for macro in self.undef:
+ self.compiler_obj.undefine_macro(macro)
+ if self.libraries is not None:
+ self.compiler_obj.set_libraries(self.libraries)
+ if self.library_dirs is not None:
+ self.compiler_obj.set_library_dirs(self.library_dirs)
+ if self.rpath is not None:
+ self.compiler_obj.set_runtime_library_dirs(self.rpath)
+ if self.link_objects is not None:
+ self.compiler_obj.set_link_objects(self.link_objects)
+
+ # Now actually compile and link everything.
+ self.build_extensions()
+
+ def get_source_files(self):
+ filenames = []
+
+ # Wouldn't it be neat if we knew the names of header files too...
+ for ext in self.extensions:
+ filenames.extend(ext.sources)
+
+ return filenames
+
+ def get_outputs(self):
+ # And build the list of output (built) filenames. Note that this
+ # ignores the 'inplace' flag, and assumes everything goes in the
+ # "build" tree.
+ outputs = []
+ for ext in self.extensions:
+ outputs.append(self.get_ext_fullpath(ext.name))
+ return outputs
+
+ def build_extensions(self):
+ for ext in self.extensions:
+ try:
+ self.build_extension(ext)
+ except (CCompilerError, PackagingError, CompileError) as e:
+ if not ext.optional:
+ raise
+ logger.warning('%s: building extension %r failed: %s',
+ self.get_command_name(), ext.name, e)
+
+ def build_extension(self, ext):
+ sources = ext.sources
+ if sources is None or not isinstance(sources, (list, tuple)):
+ raise PackagingSetupError(("in 'ext_modules' option (extension '%s'), " +
+ "'sources' must be present and must be " +
+ "a list of source filenames") % ext.name)
+ sources = list(sources)
+
+ ext_path = self.get_ext_fullpath(ext.name)
+ depends = sources + ext.depends
+ if not (self.force or newer_group(depends, ext_path, 'newer')):
+ logger.debug("skipping '%s' extension (up-to-date)", ext.name)
+ return
+ else:
+ logger.info("building '%s' extension", ext.name)
+
+ # First, scan the sources for SWIG definition files (.i), run
+ # SWIG on 'em to create .c files, and modify the sources list
+ # accordingly.
+ sources = self.swig_sources(sources, ext)
+
+ # Next, compile the source code to object files.
+
+ # XXX not honouring 'define_macros' or 'undef_macros' -- the
+ # CCompiler API needs to change to accommodate this, and I
+ # want to do one thing at a time!
+
+ # Two possible sources for extra compiler arguments:
+ # - 'extra_compile_args' in Extension object
+ # - CFLAGS environment variable (not particularly
+ # elegant, but people seem to expect it and I
+ # guess it's useful)
+ # The environment variable should take precedence, and
+ # any sensible compiler will give precedence to later
+ # command-line args. Hence we combine them in order:
+ extra_args = ext.extra_compile_args or []
+
+ macros = ext.define_macros[:]
+ for undef in ext.undef_macros:
+ macros.append((undef,))
+
+ objects = self.compiler_obj.compile(sources,
+ output_dir=self.build_temp,
+ macros=macros,
+ include_dirs=ext.include_dirs,
+ debug=self.debug,
+ extra_postargs=extra_args,
+ depends=ext.depends)
+
+ # XXX -- this is a Vile HACK!
+ #
+ # The setup.py script for Python on Unix needs to be able to
+ # get this list so it can perform all the clean up needed to
+ # avoid keeping object files around when cleaning out a failed
+ # build of an extension module. Since Packaging does not
+ # track dependencies, we have to get rid of intermediates to
+ # ensure all the intermediates will be properly re-built.
+ #
+ self._built_objects = objects[:]
+
+ # Now link the object files together into a "shared object" --
+ # of course, first we have to figure out all the other things
+ # that go into the mix.
+ if ext.extra_objects:
+ objects.extend(ext.extra_objects)
+ extra_args = ext.extra_link_args or []
+
+ # Detect target language, if not provided
+ language = ext.language or self.compiler_obj.detect_language(sources)
+
+ self.compiler_obj.link_shared_object(
+ objects, ext_path,
+ libraries=self.get_libraries(ext),
+ library_dirs=ext.library_dirs,
+ runtime_library_dirs=ext.runtime_library_dirs,
+ extra_postargs=extra_args,
+ export_symbols=self.get_export_symbols(ext),
+ debug=self.debug,
+ build_temp=self.build_temp,
+ target_lang=language)
+
+
+ def swig_sources(self, sources, extension):
+ """Walk the list of source files in 'sources', looking for SWIG
+ interface (.i) files. Run SWIG on all that are found, and
+ return a modified 'sources' list with SWIG source files replaced
+ by the generated C (or C++) files.
+ """
+ new_sources = []
+ swig_sources = []
+ swig_targets = {}
+
+ # XXX this drops generated C/C++ files into the source tree, which
+ # is fine for developers who want to distribute the generated
+ # source -- but there should be an option to put SWIG output in
+ # the temp dir.
+
+ if ('-c++' in self.swig_opts or '-c++' in extension.swig_opts):
+ target_ext = '.cpp'
+ else:
+ target_ext = '.c'
+
+ for source in sources:
+ base, ext = os.path.splitext(source)
+ if ext == ".i": # SWIG interface file
+ new_sources.append(base + '_wrap' + target_ext)
+ swig_sources.append(source)
+ swig_targets[source] = new_sources[-1]
+ else:
+ new_sources.append(source)
+
+ if not swig_sources:
+ return new_sources
+
+ swig = self.swig or self.find_swig()
+ swig_cmd = [swig, "-python"]
+ swig_cmd.extend(self.swig_opts)
+
+ # Do not override commandline arguments
+ if not self.swig_opts:
+ for o in extension.swig_opts:
+ swig_cmd.append(o)
+
+ for source in swig_sources:
+ target = swig_targets[source]
+ logger.info("swigging %s to %s", source, target)
+ self.spawn(swig_cmd + ["-o", target, source])
+
+ return new_sources
+
+ def find_swig(self):
+ """Return the name of the SWIG executable. On Unix, this is
+ just "swig" -- it should be in the PATH. Tries a bit harder on
+ Windows.
+ """
+
+ if os.name == "posix":
+ return "swig"
+ elif os.name == "nt":
+
+ # Look for SWIG in its standard installation directory on
+ # Windows (or so I presume!). If we find it there, great;
+ # if not, act like Unix and assume it's in the PATH.
+ for vers in ("1.3", "1.2", "1.1"):
+ fn = os.path.join("c:\\swig%s" % vers, "swig.exe")
+ if os.path.isfile(fn):
+ return fn
+ else:
+ return "swig.exe"
+
+ elif os.name == "os2":
+ # assume swig available in the PATH.
+ return "swig.exe"
+
+ else:
+ raise PackagingPlatformError(("I don't know how to find (much less run) SWIG "
+ "on platform '%s'") % os.name)
+
+ # -- Name generators -----------------------------------------------
+ # (extension names, filenames, whatever)
+ def get_ext_fullpath(self, ext_name):
+ """Returns the path of the filename for a given extension.
+
+ The file is located in `build_lib` or directly in the package
+ (inplace option).
+ """
+ fullname = self.get_ext_fullname(ext_name)
+ modpath = fullname.split('.')
+ filename = self.get_ext_filename(modpath[-1])
+
+ if not self.inplace:
+ # no further work needed
+ # returning :
+ # build_dir/package/path/filename
+ filename = os.path.join(*modpath[:-1]+[filename])
+ return os.path.join(self.build_lib, filename)
+
+ # the inplace option requires to find the package directory
+ # using the build_py command for that
+ package = '.'.join(modpath[0:-1])
+ build_py = self.get_finalized_command('build_py')
+ package_dir = os.path.abspath(build_py.get_package_dir(package))
+
+ # returning
+ # package_dir/filename
+ return os.path.join(package_dir, filename)
+
+ def get_ext_fullname(self, ext_name):
+ """Returns the fullname of a given extension name.
+
+ Adds the `package.` prefix"""
+ if self.package is None:
+ return ext_name
+ else:
+ return self.package + '.' + ext_name
+
+ def get_ext_filename(self, ext_name):
+ r"""Convert the name of an extension (eg. "foo.bar") into the name
+ of the file from which it will be loaded (eg. "foo/bar.so", or
+ "foo\bar.pyd").
+ """
+ ext_path = ext_name.split('.')
+ # OS/2 has an 8 character module (extension) limit :-(
+ if os.name == "os2":
+ ext_path[len(ext_path) - 1] = ext_path[len(ext_path) - 1][:8]
+ # extensions in debug_mode are named 'module_d.pyd' under windows
+ so_ext = sysconfig.get_config_var('SO')
+ if os.name == 'nt' and self.debug:
+ return os.path.join(*ext_path) + '_d' + so_ext
+ return os.path.join(*ext_path) + so_ext
+
+ def get_export_symbols(self, ext):
+ """Return the list of symbols that a shared extension has to
+ export. This either uses 'ext.export_symbols' or, if it's not
+ provided, "init" + module_name. Only relevant on Windows, where
+ the .pyd file (DLL) must export the module "init" function.
+ """
+ initfunc_name = "PyInit_" + ext.name.split('.')[-1]
+ if initfunc_name not in ext.export_symbols:
+ ext.export_symbols.append(initfunc_name)
+ return ext.export_symbols
+
+ def get_libraries(self, ext):
+ """Return the list of libraries to link against when building a
+ shared extension. On most platforms, this is just 'ext.libraries';
+ on Windows and OS/2, we add the Python library (eg. python20.dll).
+ """
+ # The python library is always needed on Windows. For MSVC, this
+ # is redundant, since the library is mentioned in a pragma in
+ # pyconfig.h that MSVC groks. The other Windows compilers all seem
+ # to need it mentioned explicitly, though, so that's what we do.
+ # Append '_d' to the python import library on debug builds.
+ if sys.platform == "win32":
+ from packaging.compiler.msvccompiler import MSVCCompiler
+ if not isinstance(self.compiler_obj, MSVCCompiler):
+ template = "python%d%d"
+ if self.debug:
+ template = template + '_d'
+ pythonlib = template % sys.version_info[:2]
+ # don't extend ext.libraries, it may be shared with other
+ # extensions, it is a reference to the original list
+ return ext.libraries + [pythonlib]
+ else:
+ return ext.libraries
+ elif sys.platform == "os2emx":
+ # EMX/GCC requires the python library explicitly, and I
+ # believe VACPP does as well (though not confirmed) - AIM Apr01
+ template = "python%d%d"
+ # debug versions of the main DLL aren't supported, at least
+ # not at this time - AIM Apr01
+ #if self.debug:
+ # template = template + '_d'
+ pythonlib = template % sys.version_info[:2]
+ # don't extend ext.libraries, it may be shared with other
+ # extensions, it is a reference to the original list
+ return ext.libraries + [pythonlib]
+ elif sys.platform[:6] == "cygwin":
+ template = "python%d.%d"
+ pythonlib = template % sys.version_info[:2]
+ # don't extend ext.libraries, it may be shared with other
+ # extensions, it is a reference to the original list
+ return ext.libraries + [pythonlib]
+ elif sys.platform[:6] == "atheos":
+ template = "python%d.%d"
+ pythonlib = template % sys.version_info[:2]
+ # Get SHLIBS from Makefile
+ extra = []
+ for lib in sysconfig.get_config_var('SHLIBS').split():
+ if lib.startswith('-l'):
+ extra.append(lib[2:])
+ else:
+ extra.append(lib)
+ # don't extend ext.libraries, it may be shared with other
+ # extensions, it is a reference to the original list
+ return ext.libraries + [pythonlib, "m"] + extra
+
+ elif sys.platform == 'darwin':
+ # Don't use the default code below
+ return ext.libraries
+
+ else:
+ if sysconfig.get_config_var('Py_ENABLE_SHARED'):
+ template = 'python%d.%d' + sys.abiflags
+ pythonlib = template % sys.version_info[:2]
+ return ext.libraries + [pythonlib]
+ else:
+ return ext.libraries
diff --git a/Lib/packaging/command/build_py.py b/Lib/packaging/command/build_py.py
new file mode 100644
index 0000000..0062140
--- /dev/null
+++ b/Lib/packaging/command/build_py.py
@@ -0,0 +1,392 @@
+"""Build pure Python modules (just copy to build directory)."""
+
+import os
+import imp
+from glob import glob
+
+from packaging import logger
+from packaging.command.cmd import Command
+from packaging.errors import PackagingOptionError, PackagingFileError
+from packaging.util import convert_path
+from packaging.compat import Mixin2to3
+
+# marking public APIs
+__all__ = ['build_py']
+
+
+class build_py(Command, Mixin2to3):
+
+ description = "build pure Python modules (copy to build directory)"
+
+ # The options for controlling byte compilation are two independent sets;
+ # more info in install_lib or the reST docs
+
+ user_options = [
+ ('build-lib=', 'd', "directory to build (copy) to"),
+ ('compile', 'c', "compile .py to .pyc"),
+ ('no-compile', None, "don't compile .py files [default]"),
+ ('optimize=', 'O',
+ "also compile with optimization: -O1 for \"python -O\", "
+ "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"),
+ ('force', 'f', "forcibly build everything (ignore file timestamps)"),
+ ('use-2to3', None,
+ "use 2to3 to make source python 3.x compatible"),
+ ('convert-2to3-doctests', None,
+ "use 2to3 to convert doctests in separate text files"),
+ ('use-2to3-fixers', None,
+ "list additional fixers opted for during 2to3 conversion"),
+ ]
+
+ boolean_options = ['compile', 'force']
+
+ negative_opt = {'no-compile': 'compile'}
+
+ def initialize_options(self):
+ self.build_lib = None
+ self.py_modules = None
+ self.package = None
+ self.package_data = None
+ self.package_dir = None
+ self.compile = False
+ self.optimize = 0
+ self.force = None
+ self._updated_files = []
+ self._doctests_2to3 = []
+ self.use_2to3 = False
+ self.convert_2to3_doctests = None
+ self.use_2to3_fixers = None
+
+ def finalize_options(self):
+ self.set_undefined_options('build',
+ 'use_2to3', 'use_2to3_fixers',
+ 'convert_2to3_doctests', 'build_lib',
+ 'force')
+
+ # Get the distribution options that are aliases for build_py
+ # options -- list of packages and list of modules.
+ self.packages = self.distribution.packages
+ self.py_modules = self.distribution.py_modules
+ self.package_data = self.distribution.package_data
+ self.package_dir = None
+ if self.distribution.package_dir is not None:
+ self.package_dir = convert_path(self.distribution.package_dir)
+ self.data_files = self.get_data_files()
+
+ # Ick, copied straight from install_lib.py (fancy_getopt needs a
+ # type system! Hell, *everything* needs a type system!!!)
+ if not isinstance(self.optimize, int):
+ try:
+ self.optimize = int(self.optimize)
+ assert 0 <= self.optimize <= 2
+ except (ValueError, AssertionError):
+ raise PackagingOptionError("optimize must be 0, 1, or 2")
+
+ def run(self):
+ # XXX copy_file by default preserves atime and mtime. IMHO this is
+ # the right thing to do, but perhaps it should be an option -- in
+ # particular, a site administrator might want installed files to
+ # reflect the time of installation rather than the last
+ # modification time before the installed release.
+
+ # XXX copy_file by default preserves mode, which appears to be the
+ # wrong thing to do: if a file is read-only in the working
+ # directory, we want it to be installed read/write so that the next
+ # installation of the same module distribution can overwrite it
+ # without problems. (This might be a Unix-specific issue.) Thus
+ # we turn off 'preserve_mode' when copying to the build directory,
+ # since the build directory is supposed to be exactly what the
+ # installation will look like (ie. we preserve mode when
+ # installing).
+
+ # Two options control which modules will be installed: 'packages'
+ # and 'py_modules'. The former lets us work with whole packages, not
+ # specifying individual modules at all; the latter is for
+ # specifying modules one-at-a-time.
+
+ if self.py_modules:
+ self.build_modules()
+ if self.packages:
+ self.build_packages()
+ self.build_package_data()
+
+ if self.use_2to3 and self._updated_files:
+ self.run_2to3(self._updated_files, self._doctests_2to3,
+ self.use_2to3_fixers)
+
+ self.byte_compile(self.get_outputs(include_bytecode=False),
+ prefix=self.build_lib)
+
+ # -- Top-level worker functions ------------------------------------
+
+ def get_data_files(self):
+ """Generate list of '(package,src_dir,build_dir,filenames)' tuples.
+
+ Helper function for finalize_options.
+ """
+ data = []
+ if not self.packages:
+ return data
+ for package in self.packages:
+ # Locate package source directory
+ src_dir = self.get_package_dir(package)
+
+ # Compute package build directory
+ build_dir = os.path.join(*([self.build_lib] + package.split('.')))
+
+ # Length of path to strip from found files
+ plen = 0
+ if src_dir:
+ plen = len(src_dir) + 1
+
+ # Strip directory from globbed filenames
+ filenames = [
+ file[plen:] for file in self.find_data_files(package, src_dir)
+ ]
+ data.append((package, src_dir, build_dir, filenames))
+ return data
+
+ def find_data_files(self, package, src_dir):
+ """Return filenames for package's data files in 'src_dir'.
+
+ Helper function for get_data_files.
+ """
+ globs = (self.package_data.get('', [])
+ + self.package_data.get(package, []))
+ files = []
+ for pattern in globs:
+ # Each pattern has to be converted to a platform-specific path
+ filelist = glob(os.path.join(src_dir, convert_path(pattern)))
+ # Files that match more than one pattern are only added once
+ files.extend(fn for fn in filelist if fn not in files)
+ return files
+
+ def build_package_data(self):
+ """Copy data files into build directory.
+
+ Helper function for run.
+ """
+ # FIXME add tests for this method
+ for package, src_dir, build_dir, filenames in self.data_files:
+ for filename in filenames:
+ target = os.path.join(build_dir, filename)
+ srcfile = os.path.join(src_dir, filename)
+ self.mkpath(os.path.dirname(target))
+ outf, copied = self.copy_file(srcfile,
+ target, preserve_mode=False)
+ doctests = self.distribution.convert_2to3_doctests
+ if copied and srcfile in doctests:
+ self._doctests_2to3.append(outf)
+
+ # XXX - this should be moved to the Distribution class as it is not
+ # only needed for build_py. It also has no dependencies on this class.
+ def get_package_dir(self, package):
+ """Return the directory, relative to the top of the source
+ distribution, where package 'package' should be found
+ (at least according to the 'package_dir' option, if any).
+ """
+ path = package.split('.')
+ if self.package_dir is not None:
+ path.insert(0, self.package_dir)
+
+ if len(path) > 0:
+ return os.path.join(*path)
+
+ return ''
+
+ def check_package(self, package, package_dir):
+ """Helper function for find_package_modules and find_modules."""
+ # Empty dir name means current directory, which we can probably
+ # assume exists. Also, os.path.exists and isdir don't know about
+ # my "empty string means current dir" convention, so we have to
+ # circumvent them.
+ if package_dir != "":
+ if not os.path.exists(package_dir):
+ raise PackagingFileError(
+ "package directory '%s' does not exist" % package_dir)
+ if not os.path.isdir(package_dir):
+ raise PackagingFileError(
+ "supposed package directory '%s' exists, "
+ "but is not a directory" % package_dir)
+
+ # Require __init__.py for all but the "root package"
+ if package:
+ init_py = os.path.join(package_dir, "__init__.py")
+ if os.path.isfile(init_py):
+ return init_py
+ else:
+ logger.warning("package init file %r not found "
+ "(or not a regular file)", init_py)
+
+ # Either not in a package at all (__init__.py not expected), or
+ # __init__.py doesn't exist -- so don't return the filename.
+ return None
+
+ def check_module(self, module, module_file):
+ if not os.path.isfile(module_file):
+ logger.warning("file %r (for module %r) not found",
+ module_file, module)
+ return False
+ else:
+ return True
+
+ def find_package_modules(self, package, package_dir):
+ self.check_package(package, package_dir)
+ module_files = glob(os.path.join(package_dir, "*.py"))
+ modules = []
+ if self.distribution.script_name is not None:
+ setup_script = os.path.abspath(self.distribution.script_name)
+ else:
+ setup_script = None
+
+ for f in module_files:
+ abs_f = os.path.abspath(f)
+ if abs_f != setup_script:
+ module = os.path.splitext(os.path.basename(f))[0]
+ modules.append((package, module, f))
+ else:
+ logger.debug("excluding %r", setup_script)
+ return modules
+
+ def find_modules(self):
+ """Finds individually-specified Python modules, ie. those listed by
+ module name in 'self.py_modules'. Returns a list of tuples (package,
+ module_base, filename): 'package' is a tuple of the path through
+ package-space to the module; 'module_base' is the bare (no
+ packages, no dots) module name, and 'filename' is the path to the
+ ".py" file (relative to the distribution root) that implements the
+ module.
+ """
+ # Map package names to tuples of useful info about the package:
+ # (package_dir, checked)
+ # package_dir - the directory where we'll find source files for
+ # this package
+ # checked - true if we have checked that the package directory
+ # is valid (exists, contains __init__.py, ... ?)
+ packages = {}
+
+ # List of (package, module, filename) tuples to return
+ modules = []
+
+ # We treat modules-in-packages almost the same as toplevel modules,
+ # just the "package" for a toplevel is empty (either an empty
+ # string or empty list, depending on context). Differences:
+ # - don't check for __init__.py in directory for empty package
+ for module in self.py_modules:
+ path = module.split('.')
+ package = '.'.join(path[0:-1])
+ module_base = path[-1]
+
+ try:
+ package_dir, checked = packages[package]
+ except KeyError:
+ package_dir = self.get_package_dir(package)
+ checked = False
+
+ if not checked:
+ init_py = self.check_package(package, package_dir)
+ packages[package] = (package_dir, 1)
+ if init_py:
+ modules.append((package, "__init__", init_py))
+
+ # XXX perhaps we should also check for just .pyc files
+ # (so greedy closed-source bastards can distribute Python
+ # modules too)
+ module_file = os.path.join(package_dir, module_base + ".py")
+ if not self.check_module(module, module_file):
+ continue
+
+ modules.append((package, module_base, module_file))
+
+ return modules
+
+ def find_all_modules(self):
+ """Compute the list of all modules that will be built, whether
+ they are specified one-module-at-a-time ('self.py_modules') or
+ by whole packages ('self.packages'). Return a list of tuples
+ (package, module, module_file), just like 'find_modules()' and
+ 'find_package_modules()' do."""
+ modules = []
+ if self.py_modules:
+ modules.extend(self.find_modules())
+ if self.packages:
+ for package in self.packages:
+ package_dir = self.get_package_dir(package)
+ m = self.find_package_modules(package, package_dir)
+ modules.extend(m)
+ return modules
+
+ def get_source_files(self):
+ sources = [module[-1] for module in self.find_all_modules()]
+ sources += [
+ os.path.join(src_dir, filename)
+ for package, src_dir, build_dir, filenames in self.data_files
+ for filename in filenames]
+ return sources
+
+ def get_module_outfile(self, build_dir, package, module):
+ outfile_path = [build_dir] + list(package) + [module + ".py"]
+ return os.path.join(*outfile_path)
+
+ def get_outputs(self, include_bytecode=True):
+ modules = self.find_all_modules()
+ outputs = []
+ for package, module, module_file in modules:
+ package = package.split('.')
+ filename = self.get_module_outfile(self.build_lib, package, module)
+ outputs.append(filename)
+ if include_bytecode:
+ if self.compile:
+ outputs.append(imp.cache_from_source(filename, True))
+ if self.optimize:
+ outputs.append(imp.cache_from_source(filename, False))
+
+ outputs += [
+ os.path.join(build_dir, filename)
+ for package, src_dir, build_dir, filenames in self.data_files
+ for filename in filenames]
+
+ return outputs
+
+ def build_module(self, module, module_file, package):
+ if isinstance(package, str):
+ package = package.split('.')
+ elif not isinstance(package, (list, tuple)):
+ raise TypeError(
+ "'package' must be a string (dot-separated), list, or tuple")
+
+ # Now put the module source file into the "build" area -- this is
+ # easy, we just copy it somewhere under self.build_lib (the build
+ # directory for Python source).
+ outfile = self.get_module_outfile(self.build_lib, package, module)
+ dir = os.path.dirname(outfile)
+ self.mkpath(dir)
+ return self.copy_file(module_file, outfile, preserve_mode=False)
+
+ def build_modules(self):
+ modules = self.find_modules()
+ for package, module, module_file in modules:
+ # Now "build" the module -- ie. copy the source file to
+ # self.build_lib (the build directory for Python source).
+ # (Actually, it gets copied to the directory for this package
+ # under self.build_lib.)
+ self.build_module(module, module_file, package)
+
+ def build_packages(self):
+ for package in self.packages:
+ # Get list of (package, module, module_file) tuples based on
+ # scanning the package directory. 'package' is only included
+ # in the tuple so that 'find_modules()' and
+ # 'find_package_tuples()' have a consistent interface; it's
+ # ignored here (apart from a sanity check). Also, 'module' is
+ # the *unqualified* module name (ie. no dots, no package -- we
+ # already know its package!), and 'module_file' is the path to
+ # the .py file, relative to the current directory
+ # (ie. including 'package_dir').
+ package_dir = self.get_package_dir(package)
+ modules = self.find_package_modules(package, package_dir)
+
+ # Now loop over the modules we found, "building" each one (just
+ # copy it to self.build_lib).
+ for package_, module, module_file in modules:
+ assert package == package_
+ self.build_module(module, module_file, package)
diff --git a/Lib/packaging/command/build_scripts.py b/Lib/packaging/command/build_scripts.py
new file mode 100644
index 0000000..d651ae0
--- /dev/null
+++ b/Lib/packaging/command/build_scripts.py
@@ -0,0 +1,154 @@
+"""Build scripts (copy to build dir and fix up shebang line)."""
+
+import os
+import re
+import sysconfig
+from tokenize import detect_encoding
+
+from packaging.command.cmd import Command
+from packaging.util import convert_path, newer
+from packaging import logger
+from packaging.compat import Mixin2to3
+
+
+# check if Python is called on the first line with this expression
+first_line_re = re.compile(b'^#!.*python[0-9.]*([ \t].*)?$')
+
+class build_scripts(Command, Mixin2to3):
+
+ description = "build scripts (copy and fix up shebang line)"
+
+ user_options = [
+ ('build-dir=', 'd', "directory to build (copy) to"),
+ ('force', 'f', "forcibly build everything (ignore file timestamps"),
+ ('executable=', 'e', "specify final destination interpreter path"),
+ ]
+
+ boolean_options = ['force']
+
+
+ def initialize_options(self):
+ self.build_dir = None
+ self.scripts = None
+ self.force = None
+ self.executable = None
+ self.outfiles = None
+ self.use_2to3 = False
+ self.convert_2to3_doctests = None
+ self.use_2to3_fixers = None
+
+ def finalize_options(self):
+ self.set_undefined_options('build',
+ ('build_scripts', 'build_dir'),
+ 'use_2to3', 'use_2to3_fixers',
+ 'convert_2to3_doctests', 'force',
+ 'executable')
+ self.scripts = self.distribution.scripts
+
+ def get_source_files(self):
+ return self.scripts
+
+ def run(self):
+ if not self.scripts:
+ return
+ copied_files = self.copy_scripts()
+ if self.use_2to3 and copied_files:
+ self._run_2to3(copied_files, fixers=self.use_2to3_fixers)
+
+ def copy_scripts(self):
+ """Copy each script listed in 'self.scripts'; if it's marked as a
+ Python script in the Unix way (first line matches 'first_line_re',
+ ie. starts with "\#!" and contains "python"), then adjust the first
+ line to refer to the current Python interpreter as we copy.
+ """
+ self.mkpath(self.build_dir)
+ outfiles = []
+ for script in self.scripts:
+ adjust = False
+ script = convert_path(script)
+ outfile = os.path.join(self.build_dir, os.path.basename(script))
+ outfiles.append(outfile)
+
+ if not self.force and not newer(script, outfile):
+ logger.debug("not copying %s (up-to-date)", script)
+ continue
+
+ # Always open the file, but ignore failures in dry-run mode --
+ # that way, we'll get accurate feedback if we can read the
+ # script.
+ try:
+ f = open(script, "rb")
+ except IOError:
+ if not self.dry_run:
+ raise
+ f = None
+ else:
+ encoding, lines = detect_encoding(f.readline)
+ f.seek(0)
+ first_line = f.readline()
+ if not first_line:
+ logger.warning('%s: %s is an empty file (skipping)',
+ self.get_command_name(), script)
+ continue
+
+ match = first_line_re.match(first_line)
+ if match:
+ adjust = True
+ post_interp = match.group(1) or b''
+
+ if adjust:
+ logger.info("copying and adjusting %s -> %s", script,
+ self.build_dir)
+ if not self.dry_run:
+ if not sysconfig.is_python_build():
+ executable = self.executable
+ else:
+ executable = os.path.join(
+ sysconfig.get_config_var("BINDIR"),
+ "python%s%s" % (sysconfig.get_config_var("VERSION"),
+ sysconfig.get_config_var("EXE")))
+ executable = os.fsencode(executable)
+ shebang = b"#!" + executable + post_interp + b"\n"
+ # Python parser starts to read a script using UTF-8 until
+ # it gets a #coding:xxx cookie. The shebang has to be the
+ # first line of a file, the #coding:xxx cookie cannot be
+ # written before. So the shebang has to be decodable from
+ # UTF-8.
+ try:
+ shebang.decode('utf-8')
+ except UnicodeDecodeError:
+ raise ValueError(
+ "The shebang ({!r}) is not decodable "
+ "from utf-8".format(shebang))
+ # If the script is encoded to a custom encoding (use a
+ # #coding:xxx cookie), the shebang has to be decodable from
+ # the script encoding too.
+ try:
+ shebang.decode(encoding)
+ except UnicodeDecodeError:
+ raise ValueError(
+ "The shebang ({!r}) is not decodable "
+ "from the script encoding ({})"
+ .format(shebang, encoding))
+ with open(outfile, "wb") as outf:
+ outf.write(shebang)
+ outf.writelines(f.readlines())
+ if f:
+ f.close()
+ else:
+ if f:
+ f.close()
+ self.copy_file(script, outfile)
+
+ if os.name == 'posix':
+ for file in outfiles:
+ if self.dry_run:
+ logger.info("changing mode of %s", file)
+ else:
+ oldmode = os.stat(file).st_mode & 0o7777
+ newmode = (oldmode | 0o555) & 0o7777
+ if newmode != oldmode:
+ logger.info("changing mode of %s from %o to %o",
+ file, oldmode, newmode)
+ os.chmod(file, newmode)
+ return outfiles
diff --git a/Lib/packaging/command/check.py b/Lib/packaging/command/check.py
new file mode 100644
index 0000000..6715db9
--- /dev/null
+++ b/Lib/packaging/command/check.py
@@ -0,0 +1,88 @@
+"""Check PEP compliance of metadata."""
+
+from packaging import logger
+from packaging.command.cmd import Command
+from packaging.errors import PackagingSetupError
+from packaging.util import resolve_name
+
+class check(Command):
+
+ description = "check PEP compliance of metadata"
+
+ user_options = [('metadata', 'm', 'Verify metadata'),
+ ('all', 'a',
+ ('runs extended set of checks')),
+ ('strict', 's',
+ 'Will exit with an error if a check fails')]
+
+ boolean_options = ['metadata', 'all', 'strict']
+
+ def initialize_options(self):
+ """Sets default values for options."""
+ self.all = False
+ self.metadata = True
+ self.strict = False
+ self._warnings = []
+
+ def finalize_options(self):
+ pass
+
+ def warn(self, msg, *args):
+ """Wrapper around logging that also remembers messages."""
+ # XXX we could use a special handler for this, but would need to test
+ # if it works even if the logger has a too high level
+ self._warnings.append((msg, args))
+ return logger.warning('%s: %s' % (self.get_command_name(), msg), *args)
+
+ def run(self):
+ """Runs the command."""
+ # perform the various tests
+ if self.metadata:
+ self.check_metadata()
+ if self.all:
+ self.check_restructuredtext()
+ self.check_hooks_resolvable()
+
+ # let's raise an error in strict mode, if we have at least
+ # one warning
+ if self.strict and len(self._warnings) > 0:
+ msg = '\n'.join(msg % args for msg, args in self._warnings)
+ raise PackagingSetupError(msg)
+
+ def check_metadata(self):
+ """Ensures that all required elements of metadata are supplied.
+
+ name, version, URL, author
+
+ Warns if any are missing.
+ """
+ missing, warnings = self.distribution.metadata.check(strict=True)
+ if missing != []:
+ self.warn('missing required metadata: %s', ', '.join(missing))
+ for warning in warnings:
+ self.warn(warning)
+
+ def check_restructuredtext(self):
+ """Checks if the long string fields are reST-compliant."""
+ missing, warnings = self.distribution.metadata.check(restructuredtext=True)
+ if self.distribution.metadata.docutils_support:
+ for warning in warnings:
+ line = warning[-1].get('line')
+ if line is None:
+ warning = warning[1]
+ else:
+ warning = '%s (line %s)' % (warning[1], line)
+ self.warn(warning)
+ elif self.strict:
+ raise PackagingSetupError('The docutils package is needed.')
+
+ def check_hooks_resolvable(self):
+ for options in self.distribution.command_options.values():
+ for hook_kind in ("pre_hook", "post_hook"):
+ if hook_kind not in options:
+ break
+ for hook_name in options[hook_kind][1].values():
+ try:
+ resolve_name(hook_name)
+ except ImportError:
+ self.warn('name %r cannot be resolved', hook_name)
diff --git a/Lib/packaging/command/clean.py b/Lib/packaging/command/clean.py
new file mode 100644
index 0000000..4f60f4e
--- /dev/null
+++ b/Lib/packaging/command/clean.py
@@ -0,0 +1,76 @@
+"""Clean up temporary files created by the build command."""
+
+# Contributed by Bastian Kleineidam <calvin@cs.uni-sb.de>
+
+import os
+from shutil import rmtree
+from packaging.command.cmd import Command
+from packaging import logger
+
+class clean(Command):
+
+ description = "clean up temporary files from 'build' command"
+ user_options = [
+ ('build-base=', 'b',
+ "base build directory (default: 'build.build-base')"),
+ ('build-lib=', None,
+ "build directory for all modules (default: 'build.build-lib')"),
+ ('build-temp=', 't',
+ "temporary build directory (default: 'build.build-temp')"),
+ ('build-scripts=', None,
+ "build directory for scripts (default: 'build.build-scripts')"),
+ ('bdist-base=', None,
+ "temporary directory for built distributions"),
+ ('all', 'a',
+ "remove all build output, not just temporary by-products")
+ ]
+
+ boolean_options = ['all']
+
+ def initialize_options(self):
+ self.build_base = None
+ self.build_lib = None
+ self.build_temp = None
+ self.build_scripts = None
+ self.bdist_base = None
+ self.all = None
+
+ def finalize_options(self):
+ self.set_undefined_options('build', 'build_base', 'build_lib',
+ 'build_scripts', 'build_temp')
+ self.set_undefined_options('bdist', 'bdist_base')
+
+ def run(self):
+ # remove the build/temp.<plat> directory (unless it's already
+ # gone)
+ if os.path.exists(self.build_temp):
+ if self.dry_run:
+ logger.info('removing %s', self.build_temp)
+ else:
+ rmtree(self.build_temp)
+ else:
+ logger.debug("'%s' does not exist -- can't clean it",
+ self.build_temp)
+
+ if self.all:
+ # remove build directories
+ for directory in (self.build_lib,
+ self.bdist_base,
+ self.build_scripts):
+ if os.path.exists(directory):
+ if self.dry_run:
+ logger.info('removing %s', directory)
+ else:
+ rmtree(directory)
+ else:
+ logger.warning("'%s' does not exist -- can't clean it",
+ directory)
+
+ # just for the heck of it, try to remove the base build directory:
+ # we might have emptied it right now, but if not we don't care
+ if not self.dry_run:
+ try:
+ os.rmdir(self.build_base)
+ logger.info("removing '%s'", self.build_base)
+ except OSError:
+ pass
diff --git a/Lib/packaging/command/cmd.py b/Lib/packaging/command/cmd.py
new file mode 100644
index 0000000..25e6a72
--- /dev/null
+++ b/Lib/packaging/command/cmd.py
@@ -0,0 +1,461 @@
+"""Base class for commands."""
+
+import os
+import re
+from shutil import copyfile, move, make_archive
+from packaging import util
+from packaging import logger
+from packaging.errors import PackagingOptionError
+
+
+class Command:
+ """Abstract base class for defining command classes, the "worker bees"
+ of Packaging. A useful analogy for command classes is to think of
+ them as subroutines with local variables called "options". The options
+ are "declared" in 'initialize_options()' and "defined" (given their
+ final values, aka "finalized") in 'finalize_options()', both of which
+ must be defined by every command class. The distinction between the
+ two is necessary because option values might come from the outside
+ world (command line, config file, ...), and any options dependent on
+ other options must be computed *after* these outside influences have
+ been processed -- hence 'finalize_options()'. The "body" of the
+ subroutine, where it does all its work based on the values of its
+ options, is the 'run()' method, which must also be implemented by every
+ command class.
+ """
+
+ # 'sub_commands' formalizes the notion of a "family" of commands,
+ # eg. "install_dist" as the parent with sub-commands "install_lib",
+ # "install_headers", etc. The parent of a family of commands
+ # defines 'sub_commands' as a class attribute; it's a list of
+ # (command_name : string, predicate : unbound_method | string | None)
+ # tuples, where 'predicate' is a method of the parent command that
+ # determines whether the corresponding command is applicable in the
+ # current situation. (Eg. we "install_headers" is only applicable if
+ # we have any C header files to install.) If 'predicate' is None,
+ # that command is always applicable.
+ #
+ # 'sub_commands' is usually defined at the *end* of a class, because
+ # predicates can be unbound methods, so they must already have been
+ # defined. The canonical example is the "install_dist" command.
+ sub_commands = []
+
+ # Pre and post command hooks are run just before or just after the command
+ # itself. They are simple functions that receive the command instance. They
+ # are specified as callable objects or dotted strings (for lazy loading).
+ pre_hook = None
+ post_hook = None
+
+ # -- Creation/initialization methods -------------------------------
+
+ def __init__(self, dist):
+ """Create and initialize a new Command object. Most importantly,
+ invokes the 'initialize_options()' method, which is the real
+ initializer and depends on the actual command being instantiated.
+ """
+ # late import because of mutual dependence between these classes
+ from packaging.dist import Distribution
+
+ if not isinstance(dist, Distribution):
+ raise TypeError("dist must be an instance of Distribution, not %r"
+ % type(dist))
+ if self.__class__ is Command:
+ raise RuntimeError("Command is an abstract class")
+
+ self.distribution = dist
+ self.initialize_options()
+
+ # Per-command versions of the global flags, so that the user can
+ # customize Packaging' behaviour command-by-command and let some
+ # commands fall back on the Distribution's behaviour. None means
+ # "not defined, check self.distribution's copy", while 0 or 1 mean
+ # false and true (duh). Note that this means figuring out the real
+ # value of each flag is a touch complicated -- hence "self._dry_run"
+ # will be handled by a property, below.
+ # XXX This needs to be fixed. [I changed it to a property--does that
+ # "fix" it?]
+ self._dry_run = None
+
+ # Some commands define a 'self.force' option to ignore file
+ # timestamps, but methods defined *here* assume that
+ # 'self.force' exists for all commands. So define it here
+ # just to be safe.
+ self.force = None
+
+ # The 'help' flag is just used for command line parsing, so
+ # none of that complicated bureaucracy is needed.
+ self.help = False
+
+ # 'finalized' records whether or not 'finalize_options()' has been
+ # called. 'finalize_options()' itself should not pay attention to
+ # this flag: it is the business of 'ensure_finalized()', which
+ # always calls 'finalize_options()', to respect/update it.
+ self.finalized = False
+
+ # XXX A more explicit way to customize dry_run would be better.
+ @property
+ def dry_run(self):
+ if self._dry_run is None:
+ return getattr(self.distribution, 'dry_run')
+ else:
+ return self._dry_run
+
+ def ensure_finalized(self):
+ if not self.finalized:
+ self.finalize_options()
+ self.finalized = True
+
+ # Subclasses must define:
+ # initialize_options()
+ # provide default values for all options; may be customized by
+ # setup script, by options from config file(s), or by command-line
+ # options
+ # finalize_options()
+ # decide on the final values for all options; this is called
+ # after all possible intervention from the outside world
+ # (command line, option file, etc.) has been processed
+ # run()
+ # run the command: do whatever it is we're here to do,
+ # controlled by the command's various option values
+
+ def initialize_options(self):
+ """Set default values for all the options that this command
+ supports. Note that these defaults may be overridden by other
+ commands, by the setup script, by config files, or by the
+ command line. Thus, this is not the place to code dependencies
+ between options; generally, 'initialize_options()' implementations
+ are just a bunch of "self.foo = None" assignments.
+
+ This method must be implemented by all command classes.
+ """
+ raise RuntimeError(
+ "abstract method -- subclass %s must override" % self.__class__)
+
+ def finalize_options(self):
+ """Set final values for all the options that this command supports.
+ This is always called as late as possible, ie. after any option
+ assignments from the command line or from other commands have been
+ done. Thus, this is the place to code option dependencies: if
+ 'foo' depends on 'bar', then it is safe to set 'foo' from 'bar' as
+ long as 'foo' still has the same value it was assigned in
+ 'initialize_options()'.
+
+ This method must be implemented by all command classes.
+ """
+ raise RuntimeError(
+ "abstract method -- subclass %s must override" % self.__class__)
+
+ def dump_options(self, header=None, indent=""):
+ if header is None:
+ header = "command options for '%s':" % self.get_command_name()
+ logger.info(indent + header)
+ indent = indent + " "
+ negative_opt = getattr(self, 'negative_opt', ())
+ for option, _, _ in self.user_options:
+ if option in negative_opt:
+ continue
+ option = option.replace('-', '_')
+ if option[-1] == "=":
+ option = option[:-1]
+ value = getattr(self, option)
+ logger.info(indent + "%s = %s", option, value)
+
+ def run(self):
+ """A command's raison d'etre: carry out the action it exists to
+ perform, controlled by the options initialized in
+ 'initialize_options()', customized by other commands, the setup
+ script, the command line and config files, and finalized in
+ 'finalize_options()'. All terminal output and filesystem
+ interaction should be done by 'run()'.
+
+ This method must be implemented by all command classes.
+ """
+ raise RuntimeError(
+ "abstract method -- subclass %s must override" % self.__class__)
+
+ # -- External interface --------------------------------------------
+ # (called by outsiders)
+
+ def get_source_files(self):
+ """Return the list of files that are used as inputs to this command,
+ i.e. the files used to generate the output files. The result is used
+ by the `sdist` command in determining the set of default files.
+
+ Command classes should implement this method if they operate on files
+ from the source tree.
+ """
+ return []
+
+ def get_outputs(self):
+ """Return the list of files that would be produced if this command
+ were actually run. Not affected by the "dry-run" flag or whether
+ any other commands have been run.
+
+ Command classes should implement this method if they produce any
+ output files that get consumed by another command. e.g., `build_ext`
+ returns the list of built extension modules, but not any temporary
+ files used in the compilation process.
+ """
+ return []
+
+ # -- Option validation methods -------------------------------------
+ # (these are very handy in writing the 'finalize_options()' method)
+ #
+ # NB. the general philosophy here is to ensure that a particular option
+ # value meets certain type and value constraints. If not, we try to
+ # force it into conformance (eg. if we expect a list but have a string,
+ # split the string on comma and/or whitespace). If we can't force the
+ # option into conformance, raise PackagingOptionError. Thus, command
+ # classes need do nothing more than (eg.)
+ # self.ensure_string_list('foo')
+ # and they can be guaranteed that thereafter, self.foo will be
+ # a list of strings.
+
+ def _ensure_stringlike(self, option, what, default=None):
+ val = getattr(self, option)
+ if val is None:
+ setattr(self, option, default)
+ return default
+ elif not isinstance(val, str):
+ raise PackagingOptionError("'%s' must be a %s (got `%s`)" %
+ (option, what, val))
+ return val
+
+ def ensure_string(self, option, default=None):
+ """Ensure that 'option' is a string; if not defined, set it to
+ 'default'.
+ """
+ self._ensure_stringlike(option, "string", default)
+
+ def ensure_string_list(self, option):
+ r"""Ensure that 'option' is a list of strings. If 'option' is
+ currently a string, we split it either on /,\s*/ or /\s+/, so
+ "foo bar baz", "foo,bar,baz", and "foo, bar baz" all become
+ ["foo", "bar", "baz"].
+ """
+ val = getattr(self, option)
+ if val is None:
+ return
+ elif isinstance(val, str):
+ setattr(self, option, re.split(r',\s*|\s+', val))
+ else:
+ if isinstance(val, list):
+ # checks if all elements are str
+ ok = True
+ for element in val:
+ if not isinstance(element, str):
+ ok = False
+ break
+ else:
+ ok = False
+
+ if not ok:
+ raise PackagingOptionError(
+ "'%s' must be a list of strings (got %r)" % (option, val))
+
+ def _ensure_tested_string(self, option, tester,
+ what, error_fmt, default=None):
+ val = self._ensure_stringlike(option, what, default)
+ if val is not None and not tester(val):
+ raise PackagingOptionError(
+ ("error in '%s' option: " + error_fmt) % (option, val))
+
+ def ensure_filename(self, option):
+ """Ensure that 'option' is the name of an existing file."""
+ self._ensure_tested_string(option, os.path.isfile,
+ "filename",
+ "'%s' does not exist or is not a file")
+
+ def ensure_dirname(self, option):
+ self._ensure_tested_string(option, os.path.isdir,
+ "directory name",
+ "'%s' does not exist or is not a directory")
+
+ # -- Convenience methods for commands ------------------------------
+
+ @classmethod
+ def get_command_name(cls):
+ if hasattr(cls, 'command_name'):
+ return cls.command_name
+ else:
+ return cls.__name__
+
+ def set_undefined_options(self, src_cmd, *options):
+ """Set values of undefined options from another command.
+
+ Undefined options are options set to None, which is the convention
+ used to indicate that an option has not been changed between
+ 'initialize_options()' and 'finalize_options()'. This method is
+ usually called from 'finalize_options()' for options that depend on
+ some other command rather than another option of the same command,
+ typically subcommands.
+
+ The 'src_cmd' argument is the other command from which option values
+ will be taken (a command object will be created for it if necessary);
+ the remaining positional arguments are strings that give the name of
+ the option to set. If the name is different on the source and target
+ command, you can pass a tuple with '(name_on_source, name_on_dest)' so
+ that 'self.name_on_dest' will be set from 'src_cmd.name_on_source'.
+ """
+ src_cmd_obj = self.distribution.get_command_obj(src_cmd)
+ src_cmd_obj.ensure_finalized()
+ for obj in options:
+ if isinstance(obj, tuple):
+ src_option, dst_option = obj
+ else:
+ src_option, dst_option = obj, obj
+ if getattr(self, dst_option) is None:
+ setattr(self, dst_option,
+ getattr(src_cmd_obj, src_option))
+
+ def get_finalized_command(self, command, create=True):
+ """Wrapper around Distribution's 'get_command_obj()' method: find
+ (create if necessary and 'create' is true) the command object for
+ 'command', call its 'ensure_finalized()' method, and return the
+ finalized command object.
+ """
+ cmd_obj = self.distribution.get_command_obj(command, create)
+ cmd_obj.ensure_finalized()
+ return cmd_obj
+
+ def reinitialize_command(self, command, reinit_subcommands=False):
+ return self.distribution.reinitialize_command(
+ command, reinit_subcommands)
+
+ def run_command(self, command):
+ """Run some other command: uses the 'run_command()' method of
+ Distribution, which creates and finalizes the command object if
+ necessary and then invokes its 'run()' method.
+ """
+ self.distribution.run_command(command)
+
+ def get_sub_commands(self):
+ """Determine the sub-commands that are relevant in the current
+ distribution (ie., that need to be run). This is based on the
+ 'sub_commands' class attribute: each tuple in that list may include
+ a method that we call to determine if the subcommand needs to be
+ run for the current distribution. Return a list of command names.
+ """
+ commands = []
+ for sub_command in self.sub_commands:
+ if len(sub_command) == 2:
+ cmd_name, method = sub_command
+ if method is None or method(self):
+ commands.append(cmd_name)
+ else:
+ commands.append(sub_command)
+ return commands
+
+ # -- External world manipulation -----------------------------------
+
+ def execute(self, func, args, msg=None, level=1):
+ util.execute(func, args, msg, dry_run=self.dry_run)
+
+ def mkpath(self, name, mode=0o777, dry_run=None):
+ if dry_run is None:
+ dry_run = self.dry_run
+ name = os.path.normpath(name)
+ if os.path.isdir(name) or name == '':
+ return
+ if dry_run:
+ head = ''
+ for part in name.split(os.sep):
+ logger.info("created directory %s%s", head, part)
+ head += part + os.sep
+ return
+ os.makedirs(name, mode)
+
+ def copy_file(self, infile, outfile,
+ preserve_mode=True, preserve_times=True, link=None, level=1):
+ """Copy a file respecting dry-run and force flags.
+
+ (dry-run defaults to whatever is in the Distribution object, and
+ force to false for commands that don't define it.)
+ """
+ if self.dry_run:
+ # XXX add a comment
+ return
+ if os.path.isdir(outfile):
+ outfile = os.path.join(outfile, os.path.split(infile)[-1])
+ copyfile(infile, outfile)
+ return outfile, None # XXX
+
+ def copy_tree(self, infile, outfile, preserve_mode=True,
+ preserve_times=True, preserve_symlinks=False, level=1):
+ """Copy an entire directory tree respecting dry-run
+ and force flags.
+ """
+ if self.dry_run:
+ # XXX should not return but let copy_tree log and decide to execute
+ # or not based on its dry_run argument
+ return
+
+ return util.copy_tree(infile, outfile, preserve_mode, preserve_times,
+ preserve_symlinks, not self.force, dry_run=self.dry_run)
+
+ def move_file(self, src, dst, level=1):
+ """Move a file respecting the dry-run flag."""
+ if self.dry_run:
+ return # XXX same thing
+ return move(src, dst)
+
+ def spawn(self, cmd, search_path=True, level=1):
+ """Spawn an external command respecting dry-run flag."""
+ from packaging.util import spawn
+ spawn(cmd, search_path, dry_run=self.dry_run)
+
+ def make_archive(self, base_name, format, root_dir=None, base_dir=None,
+ owner=None, group=None):
+ return make_archive(base_name, format, root_dir,
+ base_dir, dry_run=self.dry_run,
+ owner=owner, group=group)
+
+ def make_file(self, infiles, outfile, func, args,
+ exec_msg=None, skip_msg=None, level=1):
+ """Special case of 'execute()' for operations that process one or
+ more input files and generate one output file. Works just like
+ 'execute()', except the operation is skipped and a different
+ message printed if 'outfile' already exists and is newer than all
+ files listed in 'infiles'. If the command defined 'self.force',
+ and it is true, then the command is unconditionally run -- does no
+ timestamp checks.
+ """
+ if skip_msg is None:
+ skip_msg = "skipping %s (inputs unchanged)" % outfile
+
+ # Allow 'infiles' to be a single string
+ if isinstance(infiles, str):
+ infiles = (infiles,)
+ elif not isinstance(infiles, (list, tuple)):
+ raise TypeError(
+ "'infiles' must be a string, or a list or tuple of strings")
+
+ if exec_msg is None:
+ exec_msg = "generating %s from %s" % (outfile, ', '.join(infiles))
+
+ # If 'outfile' must be regenerated (either because it doesn't
+ # exist, is out-of-date, or the 'force' flag is true) then
+ # perform the action that presumably regenerates it
+ if self.force or util.newer_group(infiles, outfile):
+ self.execute(func, args, exec_msg, level)
+
+ # Otherwise, print the "skip" message
+ else:
+ logger.debug(skip_msg)
+
+ def byte_compile(self, files, prefix=None):
+ """Byte-compile files to pyc and/or pyo files.
+
+ This method requires that the calling class define compile and
+ optimize options, like build_py and install_lib. It also
+ automatically respects the force and dry-run options.
+
+ prefix, if given, is a string that will be stripped off the
+ filenames encoded in bytecode files.
+ """
+ if self.compile:
+ util.byte_compile(files, optimize=False, prefix=prefix,
+ force=self.force, dry_run=self.dry_run)
+ if self.optimize:
+ util.byte_compile(files, optimize=self.optimize, prefix=prefix,
+ force=self.force, dry_run=self.dry_run)
diff --git a/Lib/packaging/command/command_template b/Lib/packaging/command/command_template
new file mode 100644
index 0000000..a12d32b
--- /dev/null
+++ b/Lib/packaging/command/command_template
@@ -0,0 +1,35 @@
+"""Do X and Y."""
+
+from packaging import logger
+from packaging.command.cmd import Command
+
+
+class x(Command):
+
+ # Brief (40-50 characters) description of the command
+ description = ""
+
+ # List of option tuples: long name, short name (None if no short
+ # name), and help string.
+ user_options = [
+ ('', '', # long option, short option (one letter) or None
+ ""), # help text
+ ]
+
+ def initialize_options(self):
+ self. = None
+ self. = None
+ self. = None
+
+ def finalize_options(self):
+ if self.x is None:
+ self.x = ...
+
+ def run(self):
+ ...
+ logger.info(...)
+
+ if not self.dry_run:
+ ...
+
+ self.execute(..., dry_run=self.dry_run)
diff --git a/Lib/packaging/command/config.py b/Lib/packaging/command/config.py
new file mode 100644
index 0000000..264c139
--- /dev/null
+++ b/Lib/packaging/command/config.py
@@ -0,0 +1,349 @@
+"""Prepare the build.
+
+This module provides config, a (mostly) empty command class
+that exists mainly to be sub-classed by specific module distributions and
+applications. The idea is that while every "config" command is different,
+at least they're all named the same, and users always see "config" in the
+list of standard commands. Also, this is a good place to put common
+configure-like tasks: "try to compile this C code", or "figure out where
+this header file lives".
+"""
+
+import os
+import re
+
+from packaging.command.cmd import Command
+from packaging.errors import PackagingExecError
+from packaging.compiler import customize_compiler
+from packaging import logger
+
+LANG_EXT = {'c': '.c', 'c++': '.cxx'}
+
+class config(Command):
+
+ description = "prepare the build"
+
+ user_options = [
+ ('compiler=', None,
+ "specify the compiler type"),
+ ('cc=', None,
+ "specify the compiler executable"),
+ ('include-dirs=', 'I',
+ "list of directories to search for header files"),
+ ('define=', 'D',
+ "C preprocessor macros to define"),
+ ('undef=', 'U',
+ "C preprocessor macros to undefine"),
+ ('libraries=', 'l',
+ "external C libraries to link with"),
+ ('library-dirs=', 'L',
+ "directories to search for external C libraries"),
+
+ ('noisy', None,
+ "show every action (compile, link, run, ...) taken"),
+ ('dump-source', None,
+ "dump generated source files before attempting to compile them"),
+ ]
+
+
+ # The three standard command methods: since the "config" command
+ # does nothing by default, these are empty.
+
+ def initialize_options(self):
+ self.compiler = None
+ self.cc = None
+ self.include_dirs = None
+ self.libraries = None
+ self.library_dirs = None
+
+ # maximal output for now
+ self.noisy = True
+ self.dump_source = True
+
+ # list of temporary files generated along-the-way that we have
+ # to clean at some point
+ self.temp_files = []
+
+ def finalize_options(self):
+ if self.include_dirs is None:
+ self.include_dirs = self.distribution.include_dirs or []
+ elif isinstance(self.include_dirs, str):
+ self.include_dirs = self.include_dirs.split(os.pathsep)
+
+ if self.libraries is None:
+ self.libraries = []
+ elif isinstance(self.libraries, str):
+ self.libraries = [self.libraries]
+
+ if self.library_dirs is None:
+ self.library_dirs = []
+ elif isinstance(self.library_dirs, str):
+ self.library_dirs = self.library_dirs.split(os.pathsep)
+
+ def run(self):
+ pass
+
+
+ # Utility methods for actual "config" commands. The interfaces are
+ # loosely based on Autoconf macros of similar names. Sub-classes
+ # may use these freely.
+
+ def _check_compiler(self):
+ """Check that 'self.compiler' really is a CCompiler object;
+ if not, make it one.
+ """
+ # We do this late, and only on-demand, because this is an expensive
+ # import.
+ from packaging.compiler.ccompiler import CCompiler
+ from packaging.compiler import new_compiler
+ if not isinstance(self.compiler, CCompiler):
+ self.compiler = new_compiler(compiler=self.compiler,
+ dry_run=self.dry_run, force=True)
+ customize_compiler(self.compiler)
+ if self.include_dirs:
+ self.compiler.set_include_dirs(self.include_dirs)
+ if self.libraries:
+ self.compiler.set_libraries(self.libraries)
+ if self.library_dirs:
+ self.compiler.set_library_dirs(self.library_dirs)
+
+
+ def _gen_temp_sourcefile(self, body, headers, lang):
+ filename = "_configtest" + LANG_EXT[lang]
+ with open(filename, "w") as file:
+ if headers:
+ for header in headers:
+ file.write("#include <%s>\n" % header)
+ file.write("\n")
+ file.write(body)
+ if body[-1] != "\n":
+ file.write("\n")
+ return filename
+
+ def _preprocess(self, body, headers, include_dirs, lang):
+ src = self._gen_temp_sourcefile(body, headers, lang)
+ out = "_configtest.i"
+ self.temp_files.extend((src, out))
+ self.compiler.preprocess(src, out, include_dirs=include_dirs)
+ return src, out
+
+ def _compile(self, body, headers, include_dirs, lang):
+ src = self._gen_temp_sourcefile(body, headers, lang)
+ if self.dump_source:
+ dump_file(src, "compiling '%s':" % src)
+ obj = self.compiler.object_filenames([src])[0]
+ self.temp_files.extend((src, obj))
+ self.compiler.compile([src], include_dirs=include_dirs)
+ return src, obj
+
+ def _link(self, body, headers, include_dirs, libraries, library_dirs,
+ lang):
+ src, obj = self._compile(body, headers, include_dirs, lang)
+ prog = os.path.splitext(os.path.basename(src))[0]
+ self.compiler.link_executable([obj], prog,
+ libraries=libraries,
+ library_dirs=library_dirs,
+ target_lang=lang)
+
+ if self.compiler.exe_extension is not None:
+ prog = prog + self.compiler.exe_extension
+ self.temp_files.append(prog)
+
+ return src, obj, prog
+
+ def _clean(self, *filenames):
+ if not filenames:
+ filenames = self.temp_files
+ self.temp_files = []
+ logger.info("removing: %s", ' '.join(filenames))
+ for filename in filenames:
+ try:
+ os.remove(filename)
+ except OSError:
+ pass
+
+
+ # XXX these ignore the dry-run flag: what to do, what to do? even if
+ # you want a dry-run build, you still need some sort of configuration
+ # info. My inclination is to make it up to the real config command to
+ # consult 'dry_run', and assume a default (minimal) configuration if
+ # true. The problem with trying to do it here is that you'd have to
+ # return either true or false from all the 'try' methods, neither of
+ # which is correct.
+
+ # XXX need access to the header search path and maybe default macros.
+
+ def try_cpp(self, body=None, headers=None, include_dirs=None, lang="c"):
+ """Construct a source file from 'body' (a string containing lines
+ of C/C++ code) and 'headers' (a list of header files to include)
+ and run it through the preprocessor. Return true if the
+ preprocessor succeeded, false if there were any errors.
+ ('body' probably isn't of much use, but what the heck.)
+ """
+ from packaging.compiler.ccompiler import CompileError
+ self._check_compiler()
+ ok = True
+ try:
+ self._preprocess(body, headers, include_dirs, lang)
+ except CompileError:
+ ok = False
+
+ self._clean()
+ return ok
+
+ def search_cpp(self, pattern, body=None, headers=None, include_dirs=None,
+ lang="c"):
+ """Construct a source file (just like 'try_cpp()'), run it through
+ the preprocessor, and return true if any line of the output matches
+ 'pattern'. 'pattern' should either be a compiled regex object or a
+ string containing a regex. If both 'body' and 'headers' are None,
+ preprocesses an empty file -- which can be useful to determine the
+ symbols the preprocessor and compiler set by default.
+ """
+ self._check_compiler()
+ src, out = self._preprocess(body, headers, include_dirs, lang)
+
+ if isinstance(pattern, str):
+ pattern = re.compile(pattern)
+
+ with open(out) as file:
+ match = False
+ while True:
+ line = file.readline()
+ if line == '':
+ break
+ if pattern.search(line):
+ match = True
+ break
+
+ self._clean()
+ return match
+
+ def try_compile(self, body, headers=None, include_dirs=None, lang="c"):
+ """Try to compile a source file built from 'body' and 'headers'.
+ Return true on success, false otherwise.
+ """
+ from packaging.compiler.ccompiler import CompileError
+ self._check_compiler()
+ try:
+ self._compile(body, headers, include_dirs, lang)
+ ok = True
+ except CompileError:
+ ok = False
+
+ logger.info(ok and "success!" or "failure.")
+ self._clean()
+ return ok
+
+ def try_link(self, body, headers=None, include_dirs=None, libraries=None,
+ library_dirs=None, lang="c"):
+ """Try to compile and link a source file, built from 'body' and
+ 'headers', to executable form. Return true on success, false
+ otherwise.
+ """
+ from packaging.compiler.ccompiler import CompileError, LinkError
+ self._check_compiler()
+ try:
+ self._link(body, headers, include_dirs,
+ libraries, library_dirs, lang)
+ ok = True
+ except (CompileError, LinkError):
+ ok = False
+
+ logger.info(ok and "success!" or "failure.")
+ self._clean()
+ return ok
+
+ def try_run(self, body, headers=None, include_dirs=None, libraries=None,
+ library_dirs=None, lang="c"):
+ """Try to compile, link to an executable, and run a program
+ built from 'body' and 'headers'. Return true on success, false
+ otherwise.
+ """
+ from packaging.compiler.ccompiler import CompileError, LinkError
+ self._check_compiler()
+ try:
+ src, obj, exe = self._link(body, headers, include_dirs,
+ libraries, library_dirs, lang)
+ self.spawn([exe])
+ ok = True
+ except (CompileError, LinkError, PackagingExecError):
+ ok = False
+
+ logger.info(ok and "success!" or "failure.")
+ self._clean()
+ return ok
+
+
+ # -- High-level methods --------------------------------------------
+ # (these are the ones that are actually likely to be useful
+ # when implementing a real-world config command!)
+
+ def check_func(self, func, headers=None, include_dirs=None,
+ libraries=None, library_dirs=None, decl=False, call=False):
+
+ """Determine if function 'func' is available by constructing a
+ source file that refers to 'func', and compiles and links it.
+ If everything succeeds, returns true; otherwise returns false.
+
+ The constructed source file starts out by including the header
+ files listed in 'headers'. If 'decl' is true, it then declares
+ 'func' (as "int func()"); you probably shouldn't supply 'headers'
+ and set 'decl' true in the same call, or you might get errors about
+ a conflicting declarations for 'func'. Finally, the constructed
+ 'main()' function either references 'func' or (if 'call' is true)
+ calls it. 'libraries' and 'library_dirs' are used when
+ linking.
+ """
+
+ self._check_compiler()
+ body = []
+ if decl:
+ body.append("int %s ();" % func)
+ body.append("int main () {")
+ if call:
+ body.append(" %s();" % func)
+ else:
+ body.append(" %s;" % func)
+ body.append("}")
+ body = "\n".join(body) + "\n"
+
+ return self.try_link(body, headers, include_dirs,
+ libraries, library_dirs)
+
+ def check_lib(self, library, library_dirs=None, headers=None,
+ include_dirs=None, other_libraries=[]):
+ """Determine if 'library' is available to be linked against,
+ without actually checking that any particular symbols are provided
+ by it. 'headers' will be used in constructing the source file to
+ be compiled, but the only effect of this is to check if all the
+ header files listed are available. Any libraries listed in
+ 'other_libraries' will be included in the link, in case 'library'
+ has symbols that depend on other libraries.
+ """
+ self._check_compiler()
+ return self.try_link("int main (void) { }",
+ headers, include_dirs,
+ [library]+other_libraries, library_dirs)
+
+ def check_header(self, header, include_dirs=None, library_dirs=None,
+ lang="c"):
+ """Determine if the system header file named by 'header_file'
+ exists and can be found by the preprocessor; return true if so,
+ false otherwise.
+ """
+ return self.try_cpp(body="/* No body */", headers=[header],
+ include_dirs=include_dirs)
+
+
+def dump_file(filename, head=None):
+ """Dumps a file content into log.info.
+
+ If head is not None, will be dumped before the file content.
+ """
+ if head is None:
+ logger.info(filename)
+ else:
+ logger.info(head)
+ with open(filename) as file:
+ logger.info(file.read())
diff --git a/Lib/packaging/command/install_data.py b/Lib/packaging/command/install_data.py
new file mode 100644
index 0000000..9ca6279
--- /dev/null
+++ b/Lib/packaging/command/install_data.py
@@ -0,0 +1,79 @@
+"""Install platform-independent data files."""
+
+# Contributed by Bastian Kleineidam
+
+import os
+from shutil import Error
+from sysconfig import get_paths, format_value
+from packaging import logger
+from packaging.util import convert_path
+from packaging.command.cmd import Command
+
+
+class install_data(Command):
+
+ description = "install platform-independent data files"
+
+ user_options = [
+ ('install-dir=', 'd',
+ "base directory for installing data files "
+ "(default: installation base dir)"),
+ ('root=', None,
+ "install everything relative to this alternate root directory"),
+ ('force', 'f', "force installation (overwrite existing files)"),
+ ]
+
+ boolean_options = ['force']
+
+ def initialize_options(self):
+ self.install_dir = None
+ self.outfiles = []
+ self.data_files_out = []
+ self.root = None
+ self.force = False
+ self.data_files = self.distribution.data_files
+ self.warn_dir = True
+
+ def finalize_options(self):
+ self.set_undefined_options('install_dist',
+ ('install_data', 'install_dir'),
+ 'root', 'force')
+
+ def run(self):
+ self.mkpath(self.install_dir)
+ for _file in self.data_files.items():
+ destination = convert_path(self.expand_categories(_file[1]))
+ dir_dest = os.path.abspath(os.path.dirname(destination))
+
+ self.mkpath(dir_dest)
+ try:
+ out = self.copy_file(_file[0], dir_dest)[0]
+ except Error as e:
+ logger.warning('%s: %s', self.get_command_name(), e)
+ out = destination
+
+ self.outfiles.append(out)
+ self.data_files_out.append((_file[0], destination))
+
+ def expand_categories(self, path_with_categories):
+ local_vars = get_paths()
+ local_vars['distribution.name'] = self.distribution.metadata['Name']
+ expanded_path = format_value(path_with_categories, local_vars)
+ expanded_path = format_value(expanded_path, local_vars)
+ if '{' in expanded_path and '}' in expanded_path:
+ logger.warning(
+ '%s: unable to expand %s, some categories may be missing',
+ self.get_command_name(), path_with_categories)
+ return expanded_path
+
+ def get_source_files(self):
+ return list(self.data_files)
+
+ def get_inputs(self):
+ return list(self.data_files)
+
+ def get_outputs(self):
+ return self.outfiles
+
+ def get_resources_out(self):
+ return self.data_files_out
diff --git a/Lib/packaging/command/install_dist.py b/Lib/packaging/command/install_dist.py
new file mode 100644
index 0000000..c54da6f
--- /dev/null
+++ b/Lib/packaging/command/install_dist.py
@@ -0,0 +1,605 @@
+"""Main install command, which calls the other install_* commands."""
+
+import sys
+import os
+
+import sysconfig
+from sysconfig import get_config_vars, get_paths, get_path, get_config_var
+
+from packaging import logger
+from packaging.command.cmd import Command
+from packaging.errors import PackagingPlatformError
+from packaging.util import write_file
+from packaging.util import convert_path, change_root, get_platform
+from packaging.errors import PackagingOptionError
+
+
+class install_dist(Command):
+
+ description = "install everything from build directory"
+
+ user_options = [
+ # Select installation scheme and set base director(y|ies)
+ ('prefix=', None,
+ "installation prefix"),
+ ('exec-prefix=', None,
+ "(Unix only) prefix for platform-specific files"),
+ ('user', None,
+ "install in user site-packages directory [%s]" %
+ get_path('purelib', '%s_user' % os.name)),
+ ('home=', None,
+ "(Unix only) home directory to install under"),
+
+ # Or just set the base director(y|ies)
+ ('install-base=', None,
+ "base installation directory (instead of --prefix or --home)"),
+ ('install-platbase=', None,
+ "base installation directory for platform-specific files " +
+ "(instead of --exec-prefix or --home)"),
+ ('root=', None,
+ "install everything relative to this alternate root directory"),
+
+ # Or explicitly set the installation scheme
+ ('install-purelib=', None,
+ "installation directory for pure Python module distributions"),
+ ('install-platlib=', None,
+ "installation directory for non-pure module distributions"),
+ ('install-lib=', None,
+ "installation directory for all module distributions " +
+ "(overrides --install-purelib and --install-platlib)"),
+
+ ('install-headers=', None,
+ "installation directory for C/C++ headers"),
+ ('install-scripts=', None,
+ "installation directory for Python scripts"),
+ ('install-data=', None,
+ "installation directory for data files"),
+
+ # Byte-compilation options -- see install_lib for details
+ ('compile', 'c', "compile .py to .pyc [default]"),
+ ('no-compile', None, "don't compile .py files"),
+ ('optimize=', 'O',
+ 'also compile with optimization: -O1 for "python -O", '
+ '-O2 for "python -OO", and -O0 to disable [default: -O0]'),
+
+ # Miscellaneous control options
+ ('force', 'f',
+ "force installation (overwrite any existing files)"),
+ ('skip-build', None,
+ "skip rebuilding everything (for testing/debugging)"),
+
+ # Where to install documentation (eventually!)
+ #('doc-format=', None, "format of documentation to generate"),
+ #('install-man=', None, "directory for Unix man pages"),
+ #('install-html=', None, "directory for HTML documentation"),
+ #('install-info=', None, "directory for GNU info files"),
+
+ # XXX use a name that makes clear this is the old format
+ ('record=', None,
+ "filename in which to record a list of installed files "
+ "(not PEP 376-compliant)"),
+ ('resources=', None,
+ "data files mapping"),
+
+ # .dist-info related arguments, read by install_dist_info
+ ('no-distinfo', None,
+ "do not create a .dist-info directory"),
+ ('installer=', None,
+ "the name of the installer"),
+ ('requested', None,
+ "generate a REQUESTED file (i.e."),
+ ('no-requested', None,
+ "do not generate a REQUESTED file"),
+ ('no-record', None,
+ "do not generate a RECORD file"),
+ ]
+
+ boolean_options = ['compile', 'force', 'skip-build', 'no-distinfo',
+ 'requested', 'no-record', 'user']
+
+ negative_opt = {'no-compile': 'compile', 'no-requested': 'requested'}
+
+ def initialize_options(self):
+ # High-level options: these select both an installation base
+ # and scheme.
+ self.prefix = None
+ self.exec_prefix = None
+ self.home = None
+ self.user = False
+
+ # These select only the installation base; it's up to the user to
+ # specify the installation scheme (currently, that means supplying
+ # the --install-{platlib,purelib,scripts,data} options).
+ self.install_base = None
+ self.install_platbase = None
+ self.root = None
+
+ # These options are the actual installation directories; if not
+ # supplied by the user, they are filled in using the installation
+ # scheme implied by prefix/exec-prefix/home and the contents of
+ # that installation scheme.
+ self.install_purelib = None # for pure module distributions
+ self.install_platlib = None # non-pure (dists w/ extensions)
+ self.install_headers = None # for C/C++ headers
+ self.install_lib = None # set to either purelib or platlib
+ self.install_scripts = None
+ self.install_data = None
+ self.install_userbase = get_config_var('userbase')
+ self.install_usersite = get_path('purelib', '%s_user' % os.name)
+
+ self.compile = None
+ self.optimize = None
+
+ # These two are for putting non-packagized distributions into their
+ # own directory and creating a .pth file if it makes sense.
+ # 'extra_path' comes from the setup file; 'install_path_file' can
+ # be turned off if it makes no sense to install a .pth file. (But
+ # better to install it uselessly than to guess wrong and not
+ # install it when it's necessary and would be used!) Currently,
+ # 'install_path_file' is always true unless some outsider meddles
+ # with it.
+ self.extra_path = None
+ self.install_path_file = True
+
+ # 'force' forces installation, even if target files are not
+ # out-of-date. 'skip_build' skips running the "build" command,
+ # handy if you know it's not necessary. 'warn_dir' (which is *not*
+ # a user option, it's just there so the bdist_* commands can turn
+ # it off) determines whether we warn about installing to a
+ # directory not in sys.path.
+ self.force = False
+ self.skip_build = False
+ self.warn_dir = True
+
+ # These are only here as a conduit from the 'build' command to the
+ # 'install_*' commands that do the real work. ('build_base' isn't
+ # actually used anywhere, but it might be useful in future.) They
+ # are not user options, because if the user told the install
+ # command where the build directory is, that wouldn't affect the
+ # build command.
+ self.build_base = None
+ self.build_lib = None
+
+ # Not defined yet because we don't know anything about
+ # documentation yet.
+ #self.install_man = None
+ #self.install_html = None
+ #self.install_info = None
+
+ self.record = None
+ self.resources = None
+
+ # .dist-info related options
+ self.no_distinfo = None
+ self.installer = None
+ self.requested = None
+ self.no_record = None
+
+ # -- Option finalizing methods -------------------------------------
+ # (This is rather more involved than for most commands,
+ # because this is where the policy for installing third-
+ # party Python modules on various platforms given a wide
+ # array of user input is decided. Yes, it's quite complex!)
+
+ def finalize_options(self):
+ # This method (and its pliant slaves, like 'finalize_unix()',
+ # 'finalize_other()', and 'select_scheme()') is where the default
+ # installation directories for modules, extension modules, and
+ # anything else we care to install from a Python module
+ # distribution. Thus, this code makes a pretty important policy
+ # statement about how third-party stuff is added to a Python
+ # installation! Note that the actual work of installation is done
+ # by the relatively simple 'install_*' commands; they just take
+ # their orders from the installation directory options determined
+ # here.
+
+ # Check for errors/inconsistencies in the options; first, stuff
+ # that's wrong on any platform.
+
+ if ((self.prefix or self.exec_prefix or self.home) and
+ (self.install_base or self.install_platbase)):
+ raise PackagingOptionError(
+ "must supply either prefix/exec-prefix/home or "
+ "install-base/install-platbase -- not both")
+
+ if self.home and (self.prefix or self.exec_prefix):
+ raise PackagingOptionError(
+ "must supply either home or prefix/exec-prefix -- not both")
+
+ if self.user and (self.prefix or self.exec_prefix or self.home or
+ self.install_base or self.install_platbase):
+ raise PackagingOptionError(
+ "can't combine user with prefix/exec_prefix/home or "
+ "install_base/install_platbase")
+
+ # Next, stuff that's wrong (or dubious) only on certain platforms.
+ if os.name != "posix":
+ if self.exec_prefix:
+ logger.warning(
+ '%s: exec-prefix option ignored on this platform',
+ self.get_command_name())
+ self.exec_prefix = None
+
+ # Now the interesting logic -- so interesting that we farm it out
+ # to other methods. The goal of these methods is to set the final
+ # values for the install_{lib,scripts,data,...} options, using as
+ # input a heady brew of prefix, exec_prefix, home, install_base,
+ # install_platbase, user-supplied versions of
+ # install_{purelib,platlib,lib,scripts,data,...}, and the
+ # INSTALL_SCHEME dictionary above. Phew!
+
+ self.dump_dirs("pre-finalize_{unix,other}")
+
+ if os.name == 'posix':
+ self.finalize_unix()
+ else:
+ self.finalize_other()
+
+ self.dump_dirs("post-finalize_{unix,other}()")
+
+ # Expand configuration variables, tilde, etc. in self.install_base
+ # and self.install_platbase -- that way, we can use $base or
+ # $platbase in the other installation directories and not worry
+ # about needing recursive variable expansion (shudder).
+
+ py_version = sys.version.split()[0]
+ prefix, exec_prefix, srcdir, projectbase = get_config_vars(
+ 'prefix', 'exec_prefix', 'srcdir', 'projectbase')
+
+ metadata = self.distribution.metadata
+ self.config_vars = {
+ 'dist_name': metadata['Name'],
+ 'dist_version': metadata['Version'],
+ 'dist_fullname': metadata.get_fullname(),
+ 'py_version': py_version,
+ 'py_version_short': py_version[:3],
+ 'py_version_nodot': py_version[:3:2],
+ 'sys_prefix': prefix,
+ 'prefix': prefix,
+ 'sys_exec_prefix': exec_prefix,
+ 'exec_prefix': exec_prefix,
+ 'srcdir': srcdir,
+ 'projectbase': projectbase,
+ 'userbase': self.install_userbase,
+ 'usersite': self.install_usersite,
+ }
+
+ self.expand_basedirs()
+
+ self.dump_dirs("post-expand_basedirs()")
+
+ # Now define config vars for the base directories so we can expand
+ # everything else.
+ self.config_vars['base'] = self.install_base
+ self.config_vars['platbase'] = self.install_platbase
+
+ # Expand "~" and configuration variables in the installation
+ # directories.
+ self.expand_dirs()
+
+ self.dump_dirs("post-expand_dirs()")
+
+ # Create directories under USERBASE
+ if self.user:
+ self.create_user_dirs()
+
+ # Pick the actual directory to install all modules to: either
+ # install_purelib or install_platlib, depending on whether this
+ # module distribution is pure or not. Of course, if the user
+ # already specified install_lib, use their selection.
+ if self.install_lib is None:
+ if self.distribution.ext_modules: # has extensions: non-pure
+ self.install_lib = self.install_platlib
+ else:
+ self.install_lib = self.install_purelib
+
+ # Convert directories from Unix /-separated syntax to the local
+ # convention.
+ self.convert_paths('lib', 'purelib', 'platlib', 'scripts',
+ 'data', 'headers', 'userbase', 'usersite')
+
+ # Well, we're not actually fully completely finalized yet: we still
+ # have to deal with 'extra_path', which is the hack for allowing
+ # non-packagized module distributions (hello, Numerical Python!) to
+ # get their own directories.
+ self.handle_extra_path()
+ self.install_libbase = self.install_lib # needed for .pth file
+ self.install_lib = os.path.join(self.install_lib, self.extra_dirs)
+
+ # If a new root directory was supplied, make all the installation
+ # dirs relative to it.
+ if self.root is not None:
+ self.change_roots('libbase', 'lib', 'purelib', 'platlib',
+ 'scripts', 'data', 'headers')
+
+ self.dump_dirs("after prepending root")
+
+ # Find out the build directories, ie. where to install from.
+ self.set_undefined_options('build', 'build_base', 'build_lib')
+
+ # Punt on doc directories for now -- after all, we're punting on
+ # documentation completely!
+
+ if self.no_distinfo is None:
+ self.no_distinfo = False
+
+ def finalize_unix(self):
+ """Finalize options for posix platforms."""
+ if self.install_base is not None or self.install_platbase is not None:
+ if ((self.install_lib is None and
+ self.install_purelib is None and
+ self.install_platlib is None) or
+ self.install_headers is None or
+ self.install_scripts is None or
+ self.install_data is None):
+ raise PackagingOptionError(
+ "install-base or install-platbase supplied, but "
+ "installation scheme is incomplete")
+ return
+
+ if self.user:
+ if self.install_userbase is None:
+ raise PackagingPlatformError(
+ "user base directory is not specified")
+ self.install_base = self.install_platbase = self.install_userbase
+ self.select_scheme("posix_user")
+ elif self.home is not None:
+ self.install_base = self.install_platbase = self.home
+ self.select_scheme("posix_home")
+ else:
+ if self.prefix is None:
+ if self.exec_prefix is not None:
+ raise PackagingOptionError(
+ "must not supply exec-prefix without prefix")
+
+ self.prefix = os.path.normpath(sys.prefix)
+ self.exec_prefix = os.path.normpath(sys.exec_prefix)
+
+ else:
+ if self.exec_prefix is None:
+ self.exec_prefix = self.prefix
+
+ self.install_base = self.prefix
+ self.install_platbase = self.exec_prefix
+ self.select_scheme("posix_prefix")
+
+ def finalize_other(self):
+ """Finalize options for non-posix platforms"""
+ if self.user:
+ if self.install_userbase is None:
+ raise PackagingPlatformError(
+ "user base directory is not specified")
+ self.install_base = self.install_platbase = self.install_userbase
+ self.select_scheme(os.name + "_user")
+ elif self.home is not None:
+ self.install_base = self.install_platbase = self.home
+ self.select_scheme("posix_home")
+ else:
+ if self.prefix is None:
+ self.prefix = os.path.normpath(sys.prefix)
+
+ self.install_base = self.install_platbase = self.prefix
+ try:
+ self.select_scheme(os.name)
+ except KeyError:
+ raise PackagingPlatformError(
+ "no support for installation on '%s'" % os.name)
+
+ def dump_dirs(self, msg):
+ """Dump the list of user options."""
+ logger.debug(msg + ":")
+ for opt in self.user_options:
+ opt_name = opt[0]
+ if opt_name[-1] == "=":
+ opt_name = opt_name[0:-1]
+ if opt_name in self.negative_opt:
+ opt_name = self.negative_opt[opt_name]
+ opt_name = opt_name.replace('-', '_')
+ val = not getattr(self, opt_name)
+ else:
+ opt_name = opt_name.replace('-', '_')
+ val = getattr(self, opt_name)
+ logger.debug(" %s: %s", opt_name, val)
+
+ def select_scheme(self, name):
+ """Set the install directories by applying the install schemes."""
+ # it's the caller's problem if they supply a bad name!
+ scheme = get_paths(name, expand=False)
+ for key, value in scheme.items():
+ if key == 'platinclude':
+ key = 'headers'
+ value = os.path.join(value, self.distribution.metadata['Name'])
+ attrname = 'install_' + key
+ if hasattr(self, attrname):
+ if getattr(self, attrname) is None:
+ setattr(self, attrname, value)
+
+ def _expand_attrs(self, attrs):
+ for attr in attrs:
+ val = getattr(self, attr)
+ if val is not None:
+ if os.name == 'posix' or os.name == 'nt':
+ val = os.path.expanduser(val)
+ # see if we want to push this work in sysconfig XXX
+ val = sysconfig._subst_vars(val, self.config_vars)
+ setattr(self, attr, val)
+
+ def expand_basedirs(self):
+ """Call `os.path.expanduser` on install_{base,platbase} and root."""
+ self._expand_attrs(['install_base', 'install_platbase', 'root'])
+
+ def expand_dirs(self):
+ """Call `os.path.expanduser` on install dirs."""
+ self._expand_attrs(['install_purelib', 'install_platlib',
+ 'install_lib', 'install_headers',
+ 'install_scripts', 'install_data'])
+
+ def convert_paths(self, *names):
+ """Call `convert_path` over `names`."""
+ for name in names:
+ attr = "install_" + name
+ setattr(self, attr, convert_path(getattr(self, attr)))
+
+ def handle_extra_path(self):
+ """Set `path_file` and `extra_dirs` using `extra_path`."""
+ if self.extra_path is None:
+ self.extra_path = self.distribution.extra_path
+
+ if self.extra_path is not None:
+ if isinstance(self.extra_path, str):
+ self.extra_path = self.extra_path.split(',')
+
+ if len(self.extra_path) == 1:
+ path_file = extra_dirs = self.extra_path[0]
+ elif len(self.extra_path) == 2:
+ path_file, extra_dirs = self.extra_path
+ else:
+ raise PackagingOptionError(
+ "'extra_path' option must be a list, tuple, or "
+ "comma-separated string with 1 or 2 elements")
+
+ # convert to local form in case Unix notation used (as it
+ # should be in setup scripts)
+ extra_dirs = convert_path(extra_dirs)
+ else:
+ path_file = None
+ extra_dirs = ''
+
+ # XXX should we warn if path_file and not extra_dirs? (in which
+ # case the path file would be harmless but pointless)
+ self.path_file = path_file
+ self.extra_dirs = extra_dirs
+
+ def change_roots(self, *names):
+ """Change the install direcories pointed by name using root."""
+ for name in names:
+ attr = "install_" + name
+ setattr(self, attr, change_root(self.root, getattr(self, attr)))
+
+ def create_user_dirs(self):
+ """Create directories under USERBASE as needed."""
+ home = convert_path(os.path.expanduser("~"))
+ for name, path in self.config_vars.items():
+ if path.startswith(home) and not os.path.isdir(path):
+ os.makedirs(path, 0o700)
+
+ # -- Command execution methods -------------------------------------
+
+ def run(self):
+ """Runs the command."""
+ # Obviously have to build before we can install
+ if not self.skip_build:
+ self.run_command('build')
+ # If we built for any other platform, we can't install.
+ build_plat = self.distribution.get_command_obj('build').plat_name
+ # check warn_dir - it is a clue that the 'install_dist' is happening
+ # internally, and not to sys.path, so we don't check the platform
+ # matches what we are running.
+ if self.warn_dir and build_plat != get_platform():
+ raise PackagingPlatformError("Can't install when "
+ "cross-compiling")
+
+ # Run all sub-commands (at least those that need to be run)
+ for cmd_name in self.get_sub_commands():
+ self.run_command(cmd_name)
+
+ if self.path_file:
+ self.create_path_file()
+
+ # write list of installed files, if requested.
+ if self.record:
+ outputs = self.get_outputs()
+ if self.root: # strip any package prefix
+ root_len = len(self.root)
+ for counter in range(len(outputs)):
+ outputs[counter] = outputs[counter][root_len:]
+ self.execute(write_file,
+ (self.record, outputs),
+ "writing list of installed files to '%s'" %
+ self.record)
+
+ normpath, normcase = os.path.normpath, os.path.normcase
+ sys_path = [normcase(normpath(p)) for p in sys.path]
+ install_lib = normcase(normpath(self.install_lib))
+ if (self.warn_dir and
+ not (self.path_file and self.install_path_file) and
+ install_lib not in sys_path):
+ logger.debug(("modules installed to '%s', which is not in "
+ "Python's module search path (sys.path) -- "
+ "you'll have to change the search path yourself"),
+ self.install_lib)
+
+ def create_path_file(self):
+ """Creates the .pth file"""
+ filename = os.path.join(self.install_libbase,
+ self.path_file + ".pth")
+ if self.install_path_file:
+ self.execute(write_file,
+ (filename, [self.extra_dirs]),
+ "creating %s" % filename)
+ else:
+ logger.warning('%s: path file %r not created',
+ self.get_command_name(), filename)
+
+ # -- Reporting methods ---------------------------------------------
+
+ def get_outputs(self):
+ """Assembles the outputs of all the sub-commands."""
+ outputs = []
+ for cmd_name in self.get_sub_commands():
+ cmd = self.get_finalized_command(cmd_name)
+ # Add the contents of cmd.get_outputs(), ensuring
+ # that outputs doesn't contain duplicate entries
+ for filename in cmd.get_outputs():
+ if filename not in outputs:
+ outputs.append(filename)
+
+ if self.path_file and self.install_path_file:
+ outputs.append(os.path.join(self.install_libbase,
+ self.path_file + ".pth"))
+
+ return outputs
+
+ def get_inputs(self):
+ """Returns the inputs of all the sub-commands"""
+ # XXX gee, this looks familiar ;-(
+ inputs = []
+ for cmd_name in self.get_sub_commands():
+ cmd = self.get_finalized_command(cmd_name)
+ inputs.extend(cmd.get_inputs())
+
+ return inputs
+
+ # -- Predicates for sub-command list -------------------------------
+
+ def has_lib(self):
+ """Returns true if the current distribution has any Python
+ modules to install."""
+ return (self.distribution.has_pure_modules() or
+ self.distribution.has_ext_modules())
+
+ def has_headers(self):
+ """Returns true if the current distribution has any headers to
+ install."""
+ return self.distribution.has_headers()
+
+ def has_scripts(self):
+ """Returns true if the current distribution has any scripts to.
+ install."""
+ return self.distribution.has_scripts()
+
+ def has_data(self):
+ """Returns true if the current distribution has any data to.
+ install."""
+ return self.distribution.has_data_files()
+
+ # 'sub_commands': a list of commands this command might have to run to
+ # get its work done. See cmd.py for more info.
+ sub_commands = [('install_lib', has_lib),
+ ('install_headers', has_headers),
+ ('install_scripts', has_scripts),
+ ('install_data', has_data),
+ # keep install_distinfo last, as it needs the record
+ # with files to be completely generated
+ ('install_distinfo', lambda self: not self.no_distinfo),
+ ]
diff --git a/Lib/packaging/command/install_distinfo.py b/Lib/packaging/command/install_distinfo.py
new file mode 100644
index 0000000..b49729f
--- /dev/null
+++ b/Lib/packaging/command/install_distinfo.py
@@ -0,0 +1,143 @@
+"""Create the PEP 376-compliant .dist-info directory."""
+
+# Forked from the former install_egg_info command by Josip Djolonga
+
+import os
+import csv
+import hashlib
+from shutil import rmtree
+
+from packaging import logger
+from packaging.command.cmd import Command
+
+
+class install_distinfo(Command):
+
+ description = 'create a .dist-info directory for the distribution'
+
+ user_options = [
+ ('install-dir=', None,
+ "directory where the the .dist-info directory will be created"),
+ ('installer=', None,
+ "the name of the installer"),
+ ('requested', None,
+ "generate a REQUESTED file"),
+ ('no-requested', None,
+ "do not generate a REQUESTED file"),
+ ('no-record', None,
+ "do not generate a RECORD file"),
+ ('no-resources', None,
+ "do not generate a RESOURCES file"),
+ ]
+
+ boolean_options = ['requested', 'no-record', 'no-resources']
+
+ negative_opt = {'no-requested': 'requested'}
+
+ def initialize_options(self):
+ self.install_dir = None
+ self.installer = None
+ self.requested = None
+ self.no_record = None
+ self.no_resources = None
+ self.outfiles = []
+
+ def finalize_options(self):
+ self.set_undefined_options('install_dist',
+ 'installer', 'requested', 'no_record')
+
+ self.set_undefined_options('install_lib', 'install_dir')
+
+ if self.installer is None:
+ # FIXME distutils or packaging?
+ # + document default in the option help text above and in install
+ self.installer = 'distutils'
+ if self.requested is None:
+ self.requested = True
+ if self.no_record is None:
+ self.no_record = False
+ if self.no_resources is None:
+ self.no_resources = False
+
+ metadata = self.distribution.metadata
+
+ basename = metadata.get_fullname(filesafe=True) + ".dist-info"
+
+ self.install_dir = os.path.join(self.install_dir, basename)
+
+ def run(self):
+ target = self.install_dir
+
+ if os.path.isdir(target) and not os.path.islink(target):
+ if not self.dry_run:
+ rmtree(target)
+ elif os.path.exists(target):
+ self.execute(os.unlink, (self.install_dir,),
+ "removing " + target)
+
+ self.execute(os.makedirs, (target,), "creating " + target)
+
+ metadata_path = os.path.join(self.install_dir, 'METADATA')
+ self.execute(self.distribution.metadata.write, (metadata_path,),
+ "creating " + metadata_path)
+ self.outfiles.append(metadata_path)
+
+ installer_path = os.path.join(self.install_dir, 'INSTALLER')
+ logger.info('creating %s', installer_path)
+ if not self.dry_run:
+ with open(installer_path, 'w') as f:
+ f.write(self.installer)
+ self.outfiles.append(installer_path)
+
+ if self.requested:
+ requested_path = os.path.join(self.install_dir, 'REQUESTED')
+ logger.info('creating %s', requested_path)
+ if not self.dry_run:
+ open(requested_path, 'wb').close()
+ self.outfiles.append(requested_path)
+
+ if not self.no_resources:
+ install_data = self.get_finalized_command('install_data')
+ if install_data.get_resources_out() != []:
+ resources_path = os.path.join(self.install_dir,
+ 'RESOURCES')
+ logger.info('creating %s', resources_path)
+ if not self.dry_run:
+ with open(resources_path, 'w') as f:
+ writer = csv.writer(f, delimiter=',',
+ lineterminator='\n',
+ quotechar='"')
+ for row in install_data.get_resources_out():
+ writer.writerow(row)
+
+ self.outfiles.append(resources_path)
+
+ if not self.no_record:
+ record_path = os.path.join(self.install_dir, 'RECORD')
+ logger.info('creating %s', record_path)
+ if not self.dry_run:
+ with open(record_path, 'w', encoding='utf-8') as f:
+ writer = csv.writer(f, delimiter=',',
+ lineterminator='\n',
+ quotechar='"')
+
+ install = self.get_finalized_command('install_dist')
+
+ for fpath in install.get_outputs():
+ if fpath.endswith('.pyc') or fpath.endswith('.pyo'):
+ # do not put size and md5 hash, as in PEP-376
+ writer.writerow((fpath, '', ''))
+ else:
+ size = os.path.getsize(fpath)
+ with open(fpath, 'rb') as fp:
+ hash = hashlib.md5()
+ hash.update(fp.read())
+ md5sum = hash.hexdigest()
+ writer.writerow((fpath, md5sum, size))
+
+ # add the RECORD file itself
+ writer.writerow((record_path, '', ''))
+ self.outfiles.append(record_path)
+
+ def get_outputs(self):
+ return self.outfiles
diff --git a/Lib/packaging/command/install_headers.py b/Lib/packaging/command/install_headers.py
new file mode 100644
index 0000000..e043d6b
--- /dev/null
+++ b/Lib/packaging/command/install_headers.py
@@ -0,0 +1,43 @@
+"""Install C/C++ header files to the Python include directory."""
+
+from packaging.command.cmd import Command
+
+
+# XXX force is never used
+class install_headers(Command):
+
+ description = "install C/C++ header files"
+
+ user_options = [('install-dir=', 'd',
+ "directory to install header files to"),
+ ('force', 'f',
+ "force installation (overwrite existing files)"),
+ ]
+
+ boolean_options = ['force']
+
+ def initialize_options(self):
+ self.install_dir = None
+ self.force = False
+ self.outfiles = []
+
+ def finalize_options(self):
+ self.set_undefined_options('install_dist',
+ ('install_headers', 'install_dir'),
+ 'force')
+
+ def run(self):
+ headers = self.distribution.headers
+ if not headers:
+ return
+
+ self.mkpath(self.install_dir)
+ for header in headers:
+ out = self.copy_file(header, self.install_dir)[0]
+ self.outfiles.append(out)
+
+ def get_inputs(self):
+ return self.distribution.headers or []
+
+ def get_outputs(self):
+ return self.outfiles
diff --git a/Lib/packaging/command/install_lib.py b/Lib/packaging/command/install_lib.py
new file mode 100644
index 0000000..ffc5d45
--- /dev/null
+++ b/Lib/packaging/command/install_lib.py
@@ -0,0 +1,188 @@
+"""Install all modules (extensions and pure Python)."""
+
+import os
+import imp
+
+from packaging import logger
+from packaging.command.cmd import Command
+from packaging.errors import PackagingOptionError
+
+
+# Extension for Python source files.
+# XXX dead code? most of the codebase checks for literal '.py'
+if hasattr(os, 'extsep'):
+ PYTHON_SOURCE_EXTENSION = os.extsep + "py"
+else:
+ PYTHON_SOURCE_EXTENSION = ".py"
+
+
+class install_lib(Command):
+
+ description = "install all modules (extensions and pure Python)"
+
+ # The options for controlling byte compilation are two independent sets:
+ # 'compile' is strictly boolean, and only decides whether to
+ # generate .pyc files. 'optimize' is three-way (0, 1, or 2), and
+ # decides both whether to generate .pyo files and what level of
+ # optimization to use.
+
+ user_options = [
+ ('install-dir=', 'd', "directory to install to"),
+ ('build-dir=', 'b', "build directory (where to install from)"),
+ ('force', 'f', "force installation (overwrite existing files)"),
+ ('compile', 'c', "compile .py to .pyc [default]"),
+ ('no-compile', None, "don't compile .py files"),
+ ('optimize=', 'O',
+ "also compile with optimization: -O1 for \"python -O\", "
+ "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"),
+ ('skip-build', None, "skip the build steps"),
+ ]
+
+ boolean_options = ['force', 'compile', 'skip-build']
+
+ negative_opt = {'no-compile': 'compile'}
+
+ def initialize_options(self):
+ # let the 'install_dist' command dictate our installation directory
+ self.install_dir = None
+ self.build_dir = None
+ self.force = False
+ self.compile = None
+ self.optimize = None
+ self.skip_build = None
+
+ def finalize_options(self):
+ # Get all the information we need to install pure Python modules
+ # from the umbrella 'install_dist' command -- build (source) directory,
+ # install (target) directory, and whether to compile .py files.
+ self.set_undefined_options('install_dist',
+ ('build_lib', 'build_dir'),
+ ('install_lib', 'install_dir'),
+ 'force', 'compile', 'optimize',
+ 'skip_build')
+
+ if self.compile is None:
+ self.compile = True
+ if self.optimize is None:
+ self.optimize = 0
+
+ if not isinstance(self.optimize, int):
+ try:
+ self.optimize = int(self.optimize)
+ if self.optimize not in (0, 1, 2):
+ raise AssertionError
+ except (ValueError, AssertionError):
+ raise PackagingOptionError("optimize must be 0, 1, or 2")
+
+ def run(self):
+ # Make sure we have built everything we need first
+ self.build()
+
+ # Install everything: simply dump the entire contents of the build
+ # directory to the installation directory (that's the beauty of
+ # having a build directory!)
+ outfiles = self.install()
+
+ # (Optionally) compile .py to .pyc and/or .pyo
+ if outfiles is not None and self.distribution.has_pure_modules():
+ # XXX comment from distutils: "This [prefix stripping] is far from
+ # complete, but it should at least generate usable bytecode in RPM
+ # distributions." -> need to find exact requirements for
+ # byte-compiled files and fix it
+ install_root = self.get_finalized_command('install_dist').root
+ self.byte_compile(outfiles, prefix=install_root)
+
+ # -- Top-level worker functions ------------------------------------
+ # (called from 'run()')
+
+ def build(self):
+ if not self.skip_build:
+ if self.distribution.has_pure_modules():
+ self.run_command('build_py')
+ if self.distribution.has_ext_modules():
+ self.run_command('build_ext')
+
+ def install(self):
+ if os.path.isdir(self.build_dir):
+ outfiles = self.copy_tree(self.build_dir, self.install_dir)
+ else:
+ logger.warning(
+ '%s: %r does not exist -- no Python modules to install',
+ self.get_command_name(), self.build_dir)
+ return
+ return outfiles
+
+ # -- Utility methods -----------------------------------------------
+
+ def _mutate_outputs(self, has_any, build_cmd, cmd_option, output_dir):
+ if not has_any:
+ return []
+
+ build_cmd = self.get_finalized_command(build_cmd)
+ build_files = build_cmd.get_outputs()
+ build_dir = getattr(build_cmd, cmd_option)
+
+ prefix_len = len(build_dir) + len(os.sep)
+ outputs = []
+ for file in build_files:
+ outputs.append(os.path.join(output_dir, file[prefix_len:]))
+
+ return outputs
+
+ def _bytecode_filenames(self, py_filenames):
+ bytecode_files = []
+ for py_file in py_filenames:
+ # Since build_py handles package data installation, the
+ # list of outputs can contain more than just .py files.
+ # Make sure we only report bytecode for the .py files.
+ ext = os.path.splitext(os.path.normcase(py_file))[1]
+ if ext != PYTHON_SOURCE_EXTENSION:
+ continue
+ if self.compile:
+ bytecode_files.append(imp.cache_from_source(py_file, True))
+ if self.optimize:
+ bytecode_files.append(imp.cache_from_source(py_file, False))
+
+ return bytecode_files
+
+ # -- External interface --------------------------------------------
+ # (called by outsiders)
+
+ def get_outputs(self):
+ """Return the list of files that would be installed if this command
+ were actually run. Not affected by the "dry-run" flag or whether
+ modules have actually been built yet.
+ """
+ pure_outputs = \
+ self._mutate_outputs(self.distribution.has_pure_modules(),
+ 'build_py', 'build_lib',
+ self.install_dir)
+ if self.compile:
+ bytecode_outputs = self._bytecode_filenames(pure_outputs)
+ else:
+ bytecode_outputs = []
+
+ ext_outputs = \
+ self._mutate_outputs(self.distribution.has_ext_modules(),
+ 'build_ext', 'build_lib',
+ self.install_dir)
+
+ return pure_outputs + bytecode_outputs + ext_outputs
+
+ def get_inputs(self):
+ """Get the list of files that are input to this command, ie. the
+ files that get installed as they are named in the build tree.
+ The files in this list correspond one-to-one to the output
+ filenames returned by 'get_outputs()'.
+ """
+ inputs = []
+
+ if self.distribution.has_pure_modules():
+ build_py = self.get_finalized_command('build_py')
+ inputs.extend(build_py.get_outputs())
+
+ if self.distribution.has_ext_modules():
+ build_ext = self.get_finalized_command('build_ext')
+ inputs.extend(build_ext.get_outputs())
+
+ return inputs
diff --git a/Lib/packaging/command/install_scripts.py b/Lib/packaging/command/install_scripts.py
new file mode 100644
index 0000000..cfacbe2
--- /dev/null
+++ b/Lib/packaging/command/install_scripts.py
@@ -0,0 +1,59 @@
+"""Install scripts."""
+
+# Contributed by Bastian Kleineidam
+
+import os
+from packaging.command.cmd import Command
+from packaging import logger
+
+class install_scripts(Command):
+
+ description = "install scripts (Python or otherwise)"
+
+ user_options = [
+ ('install-dir=', 'd', "directory to install scripts to"),
+ ('build-dir=','b', "build directory (where to install from)"),
+ ('force', 'f', "force installation (overwrite existing files)"),
+ ('skip-build', None, "skip the build steps"),
+ ]
+
+ boolean_options = ['force', 'skip-build']
+
+
+ def initialize_options(self):
+ self.install_dir = None
+ self.force = False
+ self.build_dir = None
+ self.skip_build = None
+
+ def finalize_options(self):
+ self.set_undefined_options('build', ('build_scripts', 'build_dir'))
+ self.set_undefined_options('install_dist',
+ ('install_scripts', 'install_dir'),
+ 'force', 'skip_build')
+
+ def run(self):
+ if not self.skip_build:
+ self.run_command('build_scripts')
+
+ if not os.path.exists(self.build_dir):
+ self.outfiles = []
+ return
+
+ self.outfiles = self.copy_tree(self.build_dir, self.install_dir)
+ if os.name == 'posix':
+ # Set the executable bits (owner, group, and world) on
+ # all the scripts we just installed.
+ for file in self.get_outputs():
+ if self.dry_run:
+ logger.info("changing mode of %s", file)
+ else:
+ mode = (os.stat(file).st_mode | 0o555) & 0o7777
+ logger.info("changing mode of %s to %o", file, mode)
+ os.chmod(file, mode)
+
+ def get_inputs(self):
+ return self.distribution.scripts or []
+
+ def get_outputs(self):
+ return self.outfiles or []
diff --git a/Lib/packaging/command/register.py b/Lib/packaging/command/register.py
new file mode 100644
index 0000000..59805f7
--- /dev/null
+++ b/Lib/packaging/command/register.py
@@ -0,0 +1,263 @@
+"""Register a release with a project index."""
+
+# Contributed by Richard Jones
+
+import getpass
+import urllib.error
+import urllib.parse
+import urllib.request
+
+from packaging import logger
+from packaging.util import (read_pypirc, generate_pypirc, DEFAULT_REPOSITORY,
+ DEFAULT_REALM, get_pypirc_path, encode_multipart)
+from packaging.command.cmd import Command
+
+class register(Command):
+
+ description = "register a release with PyPI"
+ user_options = [
+ ('repository=', 'r',
+ "repository URL [default: %s]" % DEFAULT_REPOSITORY),
+ ('show-response', None,
+ "display full response text from server"),
+ ('list-classifiers', None,
+ "list valid Trove classifiers"),
+ ('strict', None ,
+ "stop the registration if the metadata is not fully compliant")
+ ]
+
+ boolean_options = ['show-response', 'list-classifiers', 'strict']
+
+ def initialize_options(self):
+ self.repository = None
+ self.realm = None
+ self.show_response = False
+ self.list_classifiers = False
+ self.strict = False
+
+ def finalize_options(self):
+ if self.repository is None:
+ self.repository = DEFAULT_REPOSITORY
+ if self.realm is None:
+ self.realm = DEFAULT_REALM
+
+ def run(self):
+ self._set_config()
+
+ # Check the package metadata
+ check = self.distribution.get_command_obj('check')
+ if check.strict != self.strict and not check.all:
+ # If check was already run but with different options,
+ # re-run it
+ check.strict = self.strict
+ check.all = True
+ self.distribution.have_run.pop('check', None)
+ self.run_command('check')
+
+ if self.dry_run:
+ self.verify_metadata()
+ elif self.list_classifiers:
+ self.classifiers()
+ else:
+ self.send_metadata()
+
+ def _set_config(self):
+ ''' Reads the configuration file and set attributes.
+ '''
+ config = read_pypirc(self.repository, self.realm)
+ if config != {}:
+ self.username = config['username']
+ self.password = config['password']
+ self.repository = config['repository']
+ self.realm = config['realm']
+ self.has_config = True
+ else:
+ if self.repository not in ('pypi', DEFAULT_REPOSITORY):
+ raise ValueError('%s not found in .pypirc' % self.repository)
+ if self.repository == 'pypi':
+ self.repository = DEFAULT_REPOSITORY
+ self.has_config = False
+
+ def classifiers(self):
+ ''' Fetch the list of classifiers from the server.
+ '''
+ response = urllib.request.urlopen(self.repository+'?:action=list_classifiers')
+ logger.info(response.read())
+
+ def verify_metadata(self):
+ ''' Send the metadata to the package index server to be checked.
+ '''
+ # send the info to the server and report the result
+ code, result = self.post_to_server(self.build_post_data('verify'))
+ logger.info('server response (%s): %s', code, result)
+
+
+ def send_metadata(self):
+ ''' Send the metadata to the package index server.
+
+ Well, do the following:
+ 1. figure who the user is, and then
+ 2. send the data as a Basic auth'ed POST.
+
+ First we try to read the username/password from $HOME/.pypirc,
+ which is a ConfigParser-formatted file with a section
+ [distutils] containing username and password entries (both
+ in clear text). Eg:
+
+ [distutils]
+ index-servers =
+ pypi
+
+ [pypi]
+ username: fred
+ password: sekrit
+
+ Otherwise, to figure who the user is, we offer the user three
+ choices:
+
+ 1. use existing login,
+ 2. register as a new user, or
+ 3. set the password to a random string and email the user.
+
+ '''
+ # TODO factor registration out into another method
+ # TODO use print to print, not logging
+
+ # see if we can short-cut and get the username/password from the
+ # config
+ if self.has_config:
+ choice = '1'
+ username = self.username
+ password = self.password
+ else:
+ choice = 'x'
+ username = password = ''
+
+ # get the user's login info
+ choices = '1 2 3 4'.split()
+ while choice not in choices:
+ logger.info('''\
+We need to know who you are, so please choose either:
+ 1. use your existing login,
+ 2. register as a new user,
+ 3. have the server generate a new password for you (and email it to you), or
+ 4. quit
+Your selection [default 1]: ''')
+
+ choice = input()
+ if not choice:
+ choice = '1'
+ elif choice not in choices:
+ print('Please choose one of the four options!')
+
+ if choice == '1':
+ # get the username and password
+ while not username:
+ username = input('Username: ')
+ while not password:
+ password = getpass.getpass('Password: ')
+
+ # set up the authentication
+ auth = urllib.request.HTTPPasswordMgr()
+ host = urllib.parse.urlparse(self.repository)[1]
+ auth.add_password(self.realm, host, username, password)
+ # send the info to the server and report the result
+ code, result = self.post_to_server(self.build_post_data('submit'),
+ auth)
+ logger.info('Server response (%s): %s', code, result)
+
+ # possibly save the login
+ if code == 200:
+ if self.has_config:
+ # sharing the password in the distribution instance
+ # so the upload command can reuse it
+ self.distribution.password = password
+ else:
+ logger.info(
+ 'I can store your PyPI login so future submissions '
+ 'will be faster.\n(the login will be stored in %s)',
+ get_pypirc_path())
+ choice = 'X'
+ while choice.lower() not in ('y', 'n'):
+ choice = input('Save your login (y/N)?')
+ if not choice:
+ choice = 'n'
+ if choice.lower() == 'y':
+ generate_pypirc(username, password)
+
+ elif choice == '2':
+ data = {':action': 'user'}
+ data['name'] = data['password'] = data['email'] = ''
+ data['confirm'] = None
+ while not data['name']:
+ data['name'] = input('Username: ')
+ while data['password'] != data['confirm']:
+ while not data['password']:
+ data['password'] = getpass.getpass('Password: ')
+ while not data['confirm']:
+ data['confirm'] = getpass.getpass(' Confirm: ')
+ if data['password'] != data['confirm']:
+ data['password'] = ''
+ data['confirm'] = None
+ print("Password and confirm don't match!")
+ while not data['email']:
+ data['email'] = input(' EMail: ')
+ code, result = self.post_to_server(data)
+ if code != 200:
+ logger.info('server response (%s): %s', code, result)
+ else:
+ logger.info('you will receive an email shortly; follow the '
+ 'instructions in it to complete registration.')
+ elif choice == '3':
+ data = {':action': 'password_reset'}
+ data['email'] = ''
+ while not data['email']:
+ data['email'] = input('Your email address: ')
+ code, result = self.post_to_server(data)
+ logger.info('server response (%s): %s', code, result)
+
+ def build_post_data(self, action):
+ # figure the data to send - the metadata plus some additional
+ # information used by the package server
+ data = self.distribution.metadata.todict()
+ data[':action'] = action
+ return data
+
+ # XXX to be refactored with upload.upload_file
+ def post_to_server(self, data, auth=None):
+ ''' Post a query to the server, and return a string response.
+ '''
+ if 'name' in data:
+ logger.info('Registering %s to %s', data['name'], self.repository)
+ # Build up the MIME payload for the urllib2 POST data
+ content_type, body = encode_multipart(data.items(), [])
+
+ # build the Request
+ headers = {
+ 'Content-type': content_type,
+ 'Content-length': str(len(body))
+ }
+ req = urllib.request.Request(self.repository, body, headers)
+
+ # handle HTTP and include the Basic Auth handler
+ opener = urllib.request.build_opener(
+ urllib.request.HTTPBasicAuthHandler(password_mgr=auth)
+ )
+ data = ''
+ try:
+ result = opener.open(req)
+ except urllib.error.HTTPError as e:
+ if self.show_response:
+ data = e.fp.read()
+ result = e.code, e.msg
+ except urllib.error.URLError as e:
+ result = 500, str(e)
+ else:
+ if self.show_response:
+ data = result.read()
+ result = 200, 'OK'
+ if self.show_response:
+ dashes = '-' * 75
+ logger.info('%s%s%s', dashes, data, dashes)
+
+ return result
diff --git a/Lib/packaging/command/sdist.py b/Lib/packaging/command/sdist.py
new file mode 100644
index 0000000..d399981
--- /dev/null
+++ b/Lib/packaging/command/sdist.py
@@ -0,0 +1,347 @@
+"""Create a source distribution."""
+
+import os
+import re
+import sys
+from io import StringIO
+from shutil import get_archive_formats, rmtree
+
+from packaging import logger
+from packaging.util import resolve_name
+from packaging.errors import (PackagingPlatformError, PackagingOptionError,
+ PackagingModuleError, PackagingFileError)
+from packaging.command import get_command_names
+from packaging.command.cmd import Command
+from packaging.manifest import Manifest
+
+
+def show_formats():
+ """Print all possible values for the 'formats' option (used by
+ the "--help-formats" command-line option).
+ """
+ from packaging.fancy_getopt import FancyGetopt
+ formats = sorted(('formats=' + name, None, desc)
+ for name, desc in get_archive_formats())
+ FancyGetopt(formats).print_help(
+ "List of available source distribution formats:")
+
+# a \ followed by some spaces + EOL
+_COLLAPSE_PATTERN = re.compile('\\\w\n', re.M)
+_COMMENTED_LINE = re.compile('^#.*\n$|^\w*\n$', re.M)
+
+
+class sdist(Command):
+
+ description = "create a source distribution (tarball, zip file, etc.)"
+
+ user_options = [
+ ('manifest=', 'm',
+ "name of manifest file [default: MANIFEST]"),
+ ('use-defaults', None,
+ "include the default file set in the manifest "
+ "[default; disable with --no-defaults]"),
+ ('no-defaults', None,
+ "don't include the default file set"),
+ ('prune', None,
+ "specifically exclude files/directories that should not be "
+ "distributed (build tree, RCS/CVS dirs, etc.) "
+ "[default; disable with --no-prune]"),
+ ('no-prune', None,
+ "don't automatically exclude anything"),
+ ('manifest-only', 'o',
+ "just regenerate the manifest and then stop "),
+ ('formats=', None,
+ "formats for source distribution (comma-separated list)"),
+ ('keep-temp', 'k',
+ "keep the distribution tree around after creating " +
+ "archive file(s)"),
+ ('dist-dir=', 'd',
+ "directory to put the source distribution archive(s) in "
+ "[default: dist]"),
+ ('check-metadata', None,
+ "Ensure that all required elements of metadata "
+ "are supplied. Warn if any missing. [default]"),
+ ('owner=', 'u',
+ "Owner name used when creating a tar file [default: current user]"),
+ ('group=', 'g',
+ "Group name used when creating a tar file [default: current group]"),
+ ('manifest-builders=', None,
+ "manifest builders (comma-separated list)"),
+ ]
+
+ boolean_options = ['use-defaults', 'prune',
+ 'manifest-only', 'keep-temp', 'check-metadata']
+
+ help_options = [
+ ('help-formats', None,
+ "list available distribution formats", show_formats),
+ ]
+
+ negative_opt = {'no-defaults': 'use-defaults',
+ 'no-prune': 'prune'}
+
+ default_format = {'posix': 'gztar',
+ 'nt': 'zip'}
+
+ def initialize_options(self):
+ self.manifest = None
+ # 'use_defaults': if true, we will include the default file set
+ # in the manifest
+ self.use_defaults = True
+ self.prune = True
+ self.manifest_only = False
+ self.formats = None
+ self.keep_temp = False
+ self.dist_dir = None
+
+ self.archive_files = None
+ self.metadata_check = True
+ self.owner = None
+ self.group = None
+ self.filelist = None
+ self.manifest_builders = None
+
+ def _check_archive_formats(self, formats):
+ supported_formats = [name for name, desc in get_archive_formats()]
+ for format in formats:
+ if format not in supported_formats:
+ return format
+ return None
+
+ def finalize_options(self):
+ if self.manifest is None:
+ self.manifest = "MANIFEST"
+
+ self.ensure_string_list('formats')
+ if self.formats is None:
+ try:
+ self.formats = [self.default_format[os.name]]
+ except KeyError:
+ raise PackagingPlatformError("don't know how to create source "
+ "distributions on platform %s" % os.name)
+
+ bad_format = self._check_archive_formats(self.formats)
+ if bad_format:
+ raise PackagingOptionError("unknown archive format '%s'" \
+ % bad_format)
+
+ if self.dist_dir is None:
+ self.dist_dir = "dist"
+
+ if self.filelist is None:
+ self.filelist = Manifest()
+
+ if self.manifest_builders is None:
+ self.manifest_builders = []
+ else:
+ if isinstance(self.manifest_builders, str):
+ self.manifest_builders = self.manifest_builders.split(',')
+ builders = []
+ for builder in self.manifest_builders:
+ builder = builder.strip()
+ if builder == '':
+ continue
+ try:
+ builder = resolve_name(builder)
+ except ImportError as e:
+ raise PackagingModuleError(e)
+
+ builders.append(builder)
+
+ self.manifest_builders = builders
+
+ def run(self):
+ # 'filelist' contains the list of files that will make up the
+ # manifest
+ self.filelist.clear()
+
+ # Check the package metadata
+ if self.metadata_check:
+ self.run_command('check')
+
+ # Do whatever it takes to get the list of files to process
+ # (process the manifest template, read an existing manifest,
+ # whatever). File list is accumulated in 'self.filelist'.
+ self.get_file_list()
+
+ # If user just wanted us to regenerate the manifest, stop now.
+ if self.manifest_only:
+ return
+
+ # Otherwise, go ahead and create the source distribution tarball,
+ # or zipfile, or whatever.
+ self.make_distribution()
+
+ def get_file_list(self):
+ """Figure out the list of files to include in the source
+ distribution, and put it in 'self.filelist'. This might involve
+ reading the manifest template (and writing the manifest), or just
+ reading the manifest, or just using the default file set -- it all
+ depends on the user's options.
+ """
+ template_exists = len(self.distribution.extra_files) > 0
+ if not template_exists:
+ logger.warning('%s: using default file list',
+ self.get_command_name())
+ self.filelist.findall()
+
+ if self.use_defaults:
+ self.add_defaults()
+ if template_exists:
+ template = '\n'.join(self.distribution.extra_files)
+ self.filelist.read_template(StringIO(template))
+
+ # call manifest builders, if any.
+ for builder in self.manifest_builders:
+ builder(self.distribution, self.filelist)
+
+ if self.prune:
+ self.prune_file_list()
+
+ self.filelist.write(self.manifest)
+
+ def add_defaults(self):
+ """Add all default files to self.filelist.
+
+ In addition to the setup.cfg file, this will include all files returned
+ by the get_source_files of every registered command. This will find
+ Python modules and packages, data files listed in package_data_,
+ data_files and extra_files, scripts, C sources of extension modules or
+ C libraries (headers are missing).
+ """
+ if os.path.exists('setup.cfg'):
+ self.filelist.append('setup.cfg')
+ else:
+ logger.warning("%s: standard 'setup.cfg' file not found",
+ self.get_command_name())
+
+ for cmd_name in get_command_names():
+ try:
+ cmd_obj = self.get_finalized_command(cmd_name)
+ except PackagingOptionError:
+ pass
+ else:
+ self.filelist.extend(cmd_obj.get_source_files())
+
+ def prune_file_list(self):
+ """Prune off branches that might slip into the file list as created
+ by 'read_template()', but really don't belong there:
+ * the build tree (typically "build")
+ * the release tree itself (only an issue if we ran "sdist"
+ previously with --keep-temp, or it aborted)
+ * any RCS, CVS, .svn, .hg, .git, .bzr, _darcs directories
+ """
+ build = self.get_finalized_command('build')
+ base_dir = self.distribution.get_fullname()
+
+ self.filelist.exclude_pattern(None, prefix=build.build_base)
+ self.filelist.exclude_pattern(None, prefix=base_dir)
+
+ # pruning out vcs directories
+ # both separators are used under win32
+ if sys.platform == 'win32':
+ seps = r'/|\\'
+ else:
+ seps = '/'
+
+ vcs_dirs = ['RCS', 'CVS', r'\.svn', r'\.hg', r'\.git', r'\.bzr',
+ '_darcs']
+ vcs_ptrn = r'(^|%s)(%s)(%s).*' % (seps, '|'.join(vcs_dirs), seps)
+ self.filelist.exclude_pattern(vcs_ptrn, is_regex=True)
+
+ def make_release_tree(self, base_dir, files):
+ """Create the directory tree that will become the source
+ distribution archive. All directories implied by the filenames in
+ 'files' are created under 'base_dir', and then we hard link or copy
+ (if hard linking is unavailable) those files into place.
+ Essentially, this duplicates the developer's source tree, but in a
+ directory named after the distribution, containing only the files
+ to be distributed.
+ """
+ # Create all the directories under 'base_dir' necessary to
+ # put 'files' there; the 'mkpath()' is just so we don't die
+ # if the manifest happens to be empty.
+ self.mkpath(base_dir)
+ self.create_tree(base_dir, files, dry_run=self.dry_run)
+
+ # And walk over the list of files, either making a hard link (if
+ # os.link exists) to each one that doesn't already exist in its
+ # corresponding location under 'base_dir', or copying each file
+ # that's out-of-date in 'base_dir'. (Usually, all files will be
+ # out-of-date, because by default we blow away 'base_dir' when
+ # we're done making the distribution archives.)
+
+ if hasattr(os, 'link'): # can make hard links on this system
+ link = 'hard'
+ msg = "making hard links in %s..." % base_dir
+ else: # nope, have to copy
+ link = None
+ msg = "copying files to %s..." % base_dir
+
+ if not files:
+ logger.warning("no files to distribute -- empty manifest?")
+ else:
+ logger.info(msg)
+
+ for file in self.distribution.metadata.requires_files:
+ if file not in files:
+ msg = "'%s' must be included explicitly in 'extra_files'" \
+ % file
+ raise PackagingFileError(msg)
+
+ for file in files:
+ if not os.path.isfile(file):
+ logger.warning("'%s' not a regular file -- skipping", file)
+ else:
+ dest = os.path.join(base_dir, file)
+ self.copy_file(file, dest, link=link)
+
+ self.distribution.metadata.write(os.path.join(base_dir, 'PKG-INFO'))
+
+ def make_distribution(self):
+ """Create the source distribution(s). First, we create the release
+ tree with 'make_release_tree()'; then, we create all required
+ archive files (according to 'self.formats') from the release tree.
+ Finally, we clean up by blowing away the release tree (unless
+ 'self.keep_temp' is true). The list of archive files created is
+ stored so it can be retrieved later by 'get_archive_files()'.
+ """
+ # Don't warn about missing metadata here -- should be (and is!)
+ # done elsewhere.
+ base_dir = self.distribution.get_fullname()
+ base_name = os.path.join(self.dist_dir, base_dir)
+
+ self.make_release_tree(base_dir, self.filelist.files)
+ archive_files = [] # remember names of files we create
+ # tar archive must be created last to avoid overwrite and remove
+ if 'tar' in self.formats:
+ self.formats.append(self.formats.pop(self.formats.index('tar')))
+
+ for fmt in self.formats:
+ file = self.make_archive(base_name, fmt, base_dir=base_dir,
+ owner=self.owner, group=self.group)
+ archive_files.append(file)
+ self.distribution.dist_files.append(('sdist', '', file))
+
+ self.archive_files = archive_files
+
+ if not self.keep_temp:
+ if self.dry_run:
+ logger.info('removing %s', base_dir)
+ else:
+ rmtree(base_dir)
+
+ def get_archive_files(self):
+ """Return the list of archive files created when the command
+ was run, or None if the command hasn't run yet.
+ """
+ return self.archive_files
+
+ def create_tree(self, base_dir, files, mode=0o777, dry_run=False):
+ need_dir = set()
+ for file in files:
+ need_dir.add(os.path.join(base_dir, os.path.dirname(file)))
+
+ # Now create them
+ for dir in sorted(need_dir):
+ self.mkpath(dir, mode, dry_run=dry_run)
diff --git a/Lib/packaging/command/test.py b/Lib/packaging/command/test.py
new file mode 100644
index 0000000..4d5348f
--- /dev/null
+++ b/Lib/packaging/command/test.py
@@ -0,0 +1,80 @@
+"""Run the project's test suite."""
+
+import os
+import sys
+import logging
+import unittest
+
+from packaging import logger
+from packaging.command.cmd import Command
+from packaging.database import get_distribution
+from packaging.errors import PackagingOptionError
+from packaging.util import resolve_name
+
+
+class test(Command):
+
+ description = "run the project's test suite"
+
+ user_options = [
+ ('suite=', 's',
+ "test suite to run (for example: 'some_module.test_suite')"),
+ ('runner=', None,
+ "test runner to be called."),
+ ('tests-require=', None,
+ "list of distributions required to run the test suite."),
+ ]
+
+ def initialize_options(self):
+ self.suite = None
+ self.runner = None
+ self.tests_require = []
+
+ def finalize_options(self):
+ self.build_lib = self.get_finalized_command("build").build_lib
+ for requirement in self.tests_require:
+ if get_distribution(requirement) is None:
+ logger.warning("test dependency %s is not installed, "
+ "tests may fail", requirement)
+ if (not self.suite and not self.runner and
+ self.get_ut_with_discovery() is None):
+ raise PackagingOptionError(
+ "no test discovery available, please give a 'suite' or "
+ "'runner' option or install unittest2")
+
+ def get_ut_with_discovery(self):
+ if hasattr(unittest.TestLoader, "discover"):
+ return unittest
+ else:
+ try:
+ import unittest2
+ return unittest2
+ except ImportError:
+ return None
+
+ def run(self):
+ prev_syspath = sys.path[:]
+ try:
+ # build release
+ build = self.reinitialize_command('build')
+ self.run_command('build')
+ sys.path.insert(0, build.build_lib)
+
+ # XXX maybe we could pass the verbose argument of pysetup here
+ logger = logging.getLogger('packaging')
+ verbose = logger.getEffectiveLevel() >= logging.DEBUG
+ verbosity = verbose + 1
+
+ # run the tests
+ if self.runner:
+ resolve_name(self.runner)()
+ elif self.suite:
+ runner = unittest.TextTestRunner(verbosity=verbosity)
+ runner.run(resolve_name(self.suite)())
+ elif self.get_ut_with_discovery():
+ ut = self.get_ut_with_discovery()
+ test_suite = ut.TestLoader().discover(os.curdir)
+ runner = ut.TextTestRunner(verbosity=verbosity)
+ runner.run(test_suite)
+ finally:
+ sys.path[:] = prev_syspath
diff --git a/Lib/packaging/command/upload.py b/Lib/packaging/command/upload.py
new file mode 100644
index 0000000..f56d2c6
--- /dev/null
+++ b/Lib/packaging/command/upload.py
@@ -0,0 +1,168 @@
+"""Upload a distribution to a project index."""
+
+import os
+import socket
+import logging
+import platform
+import urllib.parse
+from base64 import standard_b64encode
+from hashlib import md5
+from urllib.error import HTTPError
+from urllib.request import urlopen, Request
+
+from packaging import logger
+from packaging.errors import PackagingOptionError
+from packaging.util import (spawn, read_pypirc, DEFAULT_REPOSITORY,
+ DEFAULT_REALM, encode_multipart)
+from packaging.command.cmd import Command
+
+
+class upload(Command):
+
+ description = "upload distribution to PyPI"
+
+ user_options = [
+ ('repository=', 'r',
+ "repository URL [default: %s]" % DEFAULT_REPOSITORY),
+ ('show-response', None,
+ "display full response text from server"),
+ ('sign', 's',
+ "sign files to upload using gpg"),
+ ('identity=', 'i',
+ "GPG identity used to sign files"),
+ ('upload-docs', None,
+ "upload documentation too"),
+ ]
+
+ boolean_options = ['show-response', 'sign']
+
+ def initialize_options(self):
+ self.repository = None
+ self.realm = None
+ self.show_response = False
+ self.username = ''
+ self.password = ''
+ self.show_response = False
+ self.sign = False
+ self.identity = None
+ self.upload_docs = False
+
+ def finalize_options(self):
+ if self.repository is None:
+ self.repository = DEFAULT_REPOSITORY
+ if self.realm is None:
+ self.realm = DEFAULT_REALM
+ if self.identity and not self.sign:
+ raise PackagingOptionError(
+ "Must use --sign for --identity to have meaning")
+ config = read_pypirc(self.repository, self.realm)
+ if config != {}:
+ self.username = config['username']
+ self.password = config['password']
+ self.repository = config['repository']
+ self.realm = config['realm']
+
+ # getting the password from the distribution
+ # if previously set by the register command
+ if not self.password and self.distribution.password:
+ self.password = self.distribution.password
+
+ def run(self):
+ if not self.distribution.dist_files:
+ raise PackagingOptionError(
+ "No dist file created in earlier command")
+ for command, pyversion, filename in self.distribution.dist_files:
+ self.upload_file(command, pyversion, filename)
+ if self.upload_docs:
+ upload_docs = self.get_finalized_command("upload_docs")
+ upload_docs.repository = self.repository
+ upload_docs.username = self.username
+ upload_docs.password = self.password
+ upload_docs.run()
+
+ # XXX to be refactored with register.post_to_server
+ def upload_file(self, command, pyversion, filename):
+ # Makes sure the repository URL is compliant
+ scheme, netloc, url, params, query, fragments = \
+ urllib.parse.urlparse(self.repository)
+ if params or query or fragments:
+ raise AssertionError("Incompatible url %s" % self.repository)
+
+ if scheme not in ('http', 'https'):
+ raise AssertionError("unsupported scheme " + scheme)
+
+ # Sign if requested
+ if self.sign:
+ gpg_args = ["gpg", "--detach-sign", "-a", filename]
+ if self.identity:
+ gpg_args[2:2] = ["--local-user", self.identity]
+ spawn(gpg_args,
+ dry_run=self.dry_run)
+
+ # Fill in the data - send all the metadata in case we need to
+ # register a new release
+ with open(filename, 'rb') as f:
+ content = f.read()
+
+ data = self.distribution.metadata.todict()
+
+ # extra upload infos
+ data[':action'] = 'file_upload'
+ data['protcol_version'] = '1'
+ data['content'] = (os.path.basename(filename), content)
+ data['filetype'] = command
+ data['pyversion'] = pyversion
+ data['md5_digest'] = md5(content).hexdigest()
+
+ if command == 'bdist_dumb':
+ data['comment'] = 'built for %s' % platform.platform(terse=True)
+
+ if self.sign:
+ with open(filename + '.asc') as fp:
+ sig = fp.read()
+ data['gpg_signature'] = [
+ (os.path.basename(filename) + ".asc", sig)]
+
+ # set up the authentication
+ # The exact encoding of the authentication string is debated.
+ # Anyway PyPI only accepts ascii for both username or password.
+ user_pass = (self.username + ":" + self.password).encode('ascii')
+ auth = b"Basic " + standard_b64encode(user_pass)
+
+ # Build up the MIME payload for the POST data
+ files = []
+ for key in ('content', 'gpg_signature'):
+ if key in data:
+ filename_, value = data.pop(key)
+ files.append((key, filename_, value))
+
+ content_type, body = encode_multipart(data.items(), files)
+
+ logger.info("Submitting %s to %s", filename, self.repository)
+
+ # build the Request
+ headers = {'Content-type': content_type,
+ 'Content-length': str(len(body)),
+ 'Authorization': auth}
+
+ request = Request(self.repository, body, headers)
+ # send the data
+ try:
+ result = urlopen(request)
+ status = result.code
+ reason = result.msg
+ except socket.error as e:
+ logger.error(e)
+ return
+ except HTTPError as e:
+ status = e.code
+ reason = e.msg
+
+ if status == 200:
+ logger.info('Server response (%s): %s', status, reason)
+ else:
+ logger.error('Upload failed (%s): %s', status, reason)
+
+ if self.show_response and logger.isEnabledFor(logging.INFO):
+ sep = '-' * 75
+ logger.info('%s\n%s\n%s', sep, result.read().decode(), sep)
diff --git a/Lib/packaging/command/upload_docs.py b/Lib/packaging/command/upload_docs.py
new file mode 100644
index 0000000..30e37b5
--- /dev/null
+++ b/Lib/packaging/command/upload_docs.py
@@ -0,0 +1,131 @@
+"""Upload HTML documentation to a project index."""
+
+import os
+import base64
+import socket
+import zipfile
+import logging
+import http.client
+import urllib.parse
+from io import BytesIO
+
+from packaging import logger
+from packaging.util import (read_pypirc, DEFAULT_REPOSITORY, DEFAULT_REALM,
+ encode_multipart)
+from packaging.errors import PackagingFileError
+from packaging.command.cmd import Command
+
+
+def zip_dir(directory):
+ """Compresses recursively contents of directory into a BytesIO object"""
+ destination = BytesIO()
+ with zipfile.ZipFile(destination, "w") as zip_file:
+ for root, dirs, files in os.walk(directory):
+ for name in files:
+ full = os.path.join(root, name)
+ relative = root[len(directory):].lstrip(os.path.sep)
+ dest = os.path.join(relative, name)
+ zip_file.write(full, dest)
+ return destination
+
+
+class upload_docs(Command):
+
+ description = "upload HTML documentation to PyPI"
+
+ user_options = [
+ ('repository=', 'r',
+ "repository URL [default: %s]" % DEFAULT_REPOSITORY),
+ ('show-response', None,
+ "display full response text from server"),
+ ('upload-dir=', None,
+ "directory to upload"),
+ ]
+
+ def initialize_options(self):
+ self.repository = None
+ self.realm = None
+ self.show_response = False
+ self.upload_dir = None
+ self.username = ''
+ self.password = ''
+
+ def finalize_options(self):
+ if self.repository is None:
+ self.repository = DEFAULT_REPOSITORY
+ if self.realm is None:
+ self.realm = DEFAULT_REALM
+ if self.upload_dir is None:
+ build = self.get_finalized_command('build')
+ self.upload_dir = os.path.join(build.build_base, "docs")
+ if not os.path.isdir(self.upload_dir):
+ self.upload_dir = os.path.join(build.build_base, "doc")
+ logger.info('Using upload directory %s', self.upload_dir)
+ self.verify_upload_dir(self.upload_dir)
+ config = read_pypirc(self.repository, self.realm)
+ if config != {}:
+ self.username = config['username']
+ self.password = config['password']
+ self.repository = config['repository']
+ self.realm = config['realm']
+
+ def verify_upload_dir(self, upload_dir):
+ self.ensure_dirname('upload_dir')
+ index_location = os.path.join(upload_dir, "index.html")
+ if not os.path.exists(index_location):
+ mesg = "No 'index.html found in docs directory (%s)"
+ raise PackagingFileError(mesg % upload_dir)
+
+ def run(self):
+ name = self.distribution.metadata['Name']
+ version = self.distribution.metadata['Version']
+ zip_file = zip_dir(self.upload_dir)
+
+ fields = [(':action', 'doc_upload'),
+ ('name', name), ('version', version)]
+ files = [('content', name, zip_file.getvalue())]
+ content_type, body = encode_multipart(fields, files)
+
+ credentials = self.username + ':' + self.password
+ # FIXME should use explicit encoding
+ auth = b"Basic " + base64.encodebytes(credentials.encode()).strip()
+
+ logger.info("Submitting documentation to %s", self.repository)
+
+ scheme, netloc, url, params, query, fragments = urllib.parse.urlparse(
+ self.repository)
+ if scheme == "http":
+ conn = http.client.HTTPConnection(netloc)
+ elif scheme == "https":
+ conn = http.client.HTTPSConnection(netloc)
+ else:
+ raise AssertionError("unsupported scheme %r" % scheme)
+
+ try:
+ conn.connect()
+ conn.putrequest("POST", url)
+ conn.putheader('Content-type', content_type)
+ conn.putheader('Content-length', str(len(body)))
+ conn.putheader('Authorization', auth)
+ conn.endheaders()
+ conn.send(body)
+
+ except socket.error as e:
+ logger.error(e)
+ return
+
+ r = conn.getresponse()
+
+ if r.status == 200:
+ logger.info('Server response (%s): %s', r.status, r.reason)
+ elif r.status == 301:
+ location = r.getheader('Location')
+ if location is None:
+ location = 'http://packages.python.org/%s/' % name
+ logger.info('Upload successful. Visit %s', location)
+ else:
+ logger.error('Upload failed (%s): %s', r.status, r.reason)
+
+ if self.show_response and logger.isEnabledFor(logging.INFO):
+ sep = '-' * 75
+ logger.info('%s\n%s\n%s', sep, r.read().decode('utf-8'), sep)
diff --git a/Lib/packaging/command/wininst-10.0-amd64.exe b/Lib/packaging/command/wininst-10.0-amd64.exe
new file mode 100644
index 0000000..11f98cd
--- /dev/null
+++ b/Lib/packaging/command/wininst-10.0-amd64.exe
Binary files differ
diff --git a/Lib/packaging/command/wininst-10.0.exe b/Lib/packaging/command/wininst-10.0.exe
new file mode 100644
index 0000000..8ac6e19
--- /dev/null
+++ b/Lib/packaging/command/wininst-10.0.exe
Binary files differ
diff --git a/Lib/packaging/command/wininst-6.0.exe b/Lib/packaging/command/wininst-6.0.exe
new file mode 100644
index 0000000..f57c855
--- /dev/null
+++ b/Lib/packaging/command/wininst-6.0.exe
Binary files differ
diff --git a/Lib/packaging/command/wininst-7.1.exe b/Lib/packaging/command/wininst-7.1.exe
new file mode 100644
index 0000000..1433bc1
--- /dev/null
+++ b/Lib/packaging/command/wininst-7.1.exe
Binary files differ
diff --git a/Lib/packaging/command/wininst-8.0.exe b/Lib/packaging/command/wininst-8.0.exe
new file mode 100644
index 0000000..7403bfa
--- /dev/null
+++ b/Lib/packaging/command/wininst-8.0.exe
Binary files differ
diff --git a/Lib/packaging/command/wininst-9.0-amd64.exe b/Lib/packaging/command/wininst-9.0-amd64.exe
new file mode 100644
index 0000000..11d8011
--- /dev/null
+++ b/Lib/packaging/command/wininst-9.0-amd64.exe
Binary files differ
diff --git a/Lib/packaging/command/wininst-9.0.exe b/Lib/packaging/command/wininst-9.0.exe
new file mode 100644
index 0000000..dadb31d
--- /dev/null
+++ b/Lib/packaging/command/wininst-9.0.exe
Binary files differ
diff --git a/Lib/packaging/compat.py b/Lib/packaging/compat.py
new file mode 100644
index 0000000..dcb58f5
--- /dev/null
+++ b/Lib/packaging/compat.py
@@ -0,0 +1,51 @@
+"""Compatibility helpers."""
+
+from packaging import logger
+
+
+# XXX Having two classes with the same name is not a good thing.
+# XXX 2to3-related code should move from util to this module
+
+try:
+ from packaging.util import Mixin2to3 as _Mixin2to3
+ _CONVERT = True
+ _KLASS = _Mixin2to3
+except ImportError:
+ _CONVERT = False
+ _KLASS = object
+
+__all__ = ['Mixin2to3']
+
+
+class Mixin2to3(_KLASS):
+ """ The base class which can be used for refactoring. When run under
+ Python 3.0, the run_2to3 method provided by Mixin2to3 is overridden.
+ When run on Python 2.x, it merely creates a class which overrides run_2to3,
+ yet does nothing in particular with it.
+ """
+ if _CONVERT:
+
+ def _run_2to3(self, files, doctests=[], fixers=[]):
+ """ Takes a list of files and doctests, and performs conversion
+ on those.
+ - First, the files which contain the code(`files`) are converted.
+ - Second, the doctests in `files` are converted.
+ - Thirdly, the doctests in `doctests` are converted.
+ """
+ if fixers:
+ self.fixer_names = fixers
+
+ logger.info('converting Python code')
+ _KLASS.run_2to3(self, files)
+
+ logger.info('converting doctests in Python files')
+ _KLASS.run_2to3(self, files, doctests_only=True)
+
+ if doctests != []:
+ logger.info('converting doctest in text files')
+ _KLASS.run_2to3(self, doctests, doctests_only=True)
+ else:
+ # If run on Python 2.x, there is nothing to do.
+
+ def _run_2to3(self, files, doctests=[], fixers=[]):
+ pass
diff --git a/Lib/packaging/compiler/__init__.py b/Lib/packaging/compiler/__init__.py
new file mode 100644
index 0000000..d8e02ce
--- /dev/null
+++ b/Lib/packaging/compiler/__init__.py
@@ -0,0 +1,274 @@
+"""Compiler abstraction model used by packaging.
+
+An abstract base class is defined in the ccompiler submodule, and
+concrete implementations suitable for various platforms are defined in
+the other submodules. The extension module is also placed in this
+package.
+
+In general, code should not instantiate compiler classes directly but
+use the new_compiler and customize_compiler functions provided in this
+module.
+
+The compiler system has a registration API: get_default_compiler,
+set_compiler, show_compilers.
+"""
+
+import os
+import sys
+import re
+import sysconfig
+
+from packaging.util import resolve_name
+from packaging.errors import PackagingPlatformError
+from packaging import logger
+
+def customize_compiler(compiler):
+ """Do any platform-specific customization of a CCompiler instance.
+
+ Mainly needed on Unix, so we can plug in the information that
+ varies across Unices and is stored in Python's Makefile.
+ """
+ if compiler.name == "unix":
+ cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar, ar_flags = (
+ sysconfig.get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS',
+ 'CCSHARED', 'LDSHARED', 'SO', 'AR',
+ 'ARFLAGS'))
+
+ if 'CC' in os.environ:
+ cc = os.environ['CC']
+ if 'CXX' in os.environ:
+ cxx = os.environ['CXX']
+ if 'LDSHARED' in os.environ:
+ ldshared = os.environ['LDSHARED']
+ if 'CPP' in os.environ:
+ cpp = os.environ['CPP']
+ else:
+ cpp = cc + " -E" # not always
+ if 'LDFLAGS' in os.environ:
+ ldshared = ldshared + ' ' + os.environ['LDFLAGS']
+ if 'CFLAGS' in os.environ:
+ cflags = opt + ' ' + os.environ['CFLAGS']
+ ldshared = ldshared + ' ' + os.environ['CFLAGS']
+ if 'CPPFLAGS' in os.environ:
+ cpp = cpp + ' ' + os.environ['CPPFLAGS']
+ cflags = cflags + ' ' + os.environ['CPPFLAGS']
+ ldshared = ldshared + ' ' + os.environ['CPPFLAGS']
+ if 'AR' in os.environ:
+ ar = os.environ['AR']
+ if 'ARFLAGS' in os.environ:
+ archiver = ar + ' ' + os.environ['ARFLAGS']
+ else:
+ if ar_flags is not None:
+ archiver = ar + ' ' + ar_flags
+ else:
+ # see if its the proper default value
+ # mmm I don't want to backport the makefile
+ archiver = ar + ' rc'
+
+ cc_cmd = cc + ' ' + cflags
+ compiler.set_executables(
+ preprocessor=cpp,
+ compiler=cc_cmd,
+ compiler_so=cc_cmd + ' ' + ccshared,
+ compiler_cxx=cxx,
+ linker_so=ldshared,
+ linker_exe=cc,
+ archiver=archiver)
+
+ compiler.shared_lib_extension = so_ext
+
+
+# Map a sys.platform/os.name ('posix', 'nt') to the default compiler
+# type for that platform. Keys are interpreted as re match
+# patterns. Order is important; platform mappings are preferred over
+# OS names.
+_default_compilers = (
+ # Platform string mappings
+
+ # on a cygwin built python we can use gcc like an ordinary UNIXish
+ # compiler
+ ('cygwin.*', 'unix'),
+
+ # OS name mappings
+ ('posix', 'unix'),
+ ('nt', 'msvc'),
+)
+
+def get_default_compiler(osname=None, platform=None):
+ """ Determine the default compiler to use for the given platform.
+
+ osname should be one of the standard Python OS names (i.e. the
+ ones returned by os.name) and platform the common value
+ returned by sys.platform for the platform in question.
+
+ The default values are os.name and sys.platform in case the
+ parameters are not given.
+
+ """
+ if osname is None:
+ osname = os.name
+ if platform is None:
+ platform = sys.platform
+ for pattern, compiler in _default_compilers:
+ if re.match(pattern, platform) is not None or \
+ re.match(pattern, osname) is not None:
+ return compiler
+ # Defaults to Unix compiler
+ return 'unix'
+
+
+# compiler mapping
+# XXX useful to expose them? (i.e. get_compiler_names)
+_COMPILERS = {
+ 'unix': 'packaging.compiler.unixccompiler.UnixCCompiler',
+ 'msvc': 'packaging.compiler.msvccompiler.MSVCCompiler',
+ 'cygwin': 'packaging.compiler.cygwinccompiler.CygwinCCompiler',
+ 'mingw32': 'packaging.compiler.cygwinccompiler.Mingw32CCompiler',
+ 'bcpp': 'packaging.compiler.bcppcompiler.BCPPCompiler',
+}
+
+def set_compiler(location):
+ """Add or change a compiler"""
+ cls = resolve_name(location)
+ # XXX we want to check the class here
+ _COMPILERS[cls.name] = cls
+
+
+def show_compilers():
+ """Print list of available compilers (used by the "--help-compiler"
+ options to "build", "build_ext", "build_clib").
+ """
+ from packaging.fancy_getopt import FancyGetopt
+ compilers = []
+
+ for name, cls in _COMPILERS.items():
+ if isinstance(cls, str):
+ cls = resolve_name(cls)
+ _COMPILERS[name] = cls
+
+ compilers.append(("compiler=" + name, None, cls.description))
+
+ compilers.sort()
+ pretty_printer = FancyGetopt(compilers)
+ pretty_printer.print_help("List of available compilers:")
+
+
+def new_compiler(plat=None, compiler=None, dry_run=False, force=False):
+ """Generate an instance of some CCompiler subclass for the supplied
+ platform/compiler combination. 'plat' defaults to 'os.name'
+ (eg. 'posix', 'nt'), and 'compiler' defaults to the default compiler
+ for that platform. Currently only 'posix' and 'nt' are supported, and
+ the default compilers are "traditional Unix interface" (UnixCCompiler
+ class) and Visual C++ (MSVCCompiler class). Note that it's perfectly
+ possible to ask for a Unix compiler object under Windows, and a
+ Microsoft compiler object under Unix -- if you supply a value for
+ 'compiler', 'plat' is ignored.
+ """
+ if plat is None:
+ plat = os.name
+
+ try:
+ if compiler is None:
+ compiler = get_default_compiler(plat)
+
+ cls = _COMPILERS[compiler]
+ except KeyError:
+ msg = "don't know how to compile C/C++ code on platform '%s'" % plat
+ if compiler is not None:
+ msg = msg + " with '%s' compiler" % compiler
+ raise PackagingPlatformError(msg)
+
+ if isinstance(cls, str):
+ cls = resolve_name(cls)
+ _COMPILERS[compiler] = cls
+
+ return cls(dry_run, force)
+
+
+def gen_preprocess_options(macros, include_dirs):
+ """Generate C pre-processor options (-D, -U, -I) as used by at least
+ two types of compilers: the typical Unix compiler and Visual C++.
+ 'macros' is the usual thing, a list of 1- or 2-tuples, where (name,)
+ means undefine (-U) macro 'name', and (name,value) means define (-D)
+ macro 'name' to 'value'. 'include_dirs' is just a list of directory
+ names to be added to the header file search path (-I). Returns a list
+ of command-line options suitable for either Unix compilers or Visual
+ C++.
+ """
+ # XXX it would be nice (mainly aesthetic, and so we don't generate
+ # stupid-looking command lines) to go over 'macros' and eliminate
+ # redundant definitions/undefinitions (ie. ensure that only the
+ # latest mention of a particular macro winds up on the command
+ # line). I don't think it's essential, though, since most (all?)
+ # Unix C compilers only pay attention to the latest -D or -U
+ # mention of a macro on their command line. Similar situation for
+ # 'include_dirs'. I'm punting on both for now. Anyways, weeding out
+ # redundancies like this should probably be the province of
+ # CCompiler, since the data structures used are inherited from it
+ # and therefore common to all CCompiler classes.
+
+ pp_opts = []
+ for macro in macros:
+
+ if not isinstance(macro, tuple) and 1 <= len(macro) <= 2:
+ raise TypeError(
+ "bad macro definition '%s': each element of 'macros'"
+ "list must be a 1- or 2-tuple" % macro)
+
+ if len(macro) == 1: # undefine this macro
+ pp_opts.append("-U%s" % macro[0])
+ elif len(macro) == 2:
+ if macro[1] is None: # define with no explicit value
+ pp_opts.append("-D%s" % macro[0])
+ else:
+ # XXX *don't* need to be clever about quoting the
+ # macro value here, because we're going to avoid the
+ # shell at all costs when we spawn the command!
+ pp_opts.append("-D%s=%s" % macro)
+
+ for dir in include_dirs:
+ pp_opts.append("-I%s" % dir)
+
+ return pp_opts
+
+
+def gen_lib_options(compiler, library_dirs, runtime_library_dirs, libraries):
+ """Generate linker options for searching library directories and
+ linking with specific libraries.
+
+ 'libraries' and 'library_dirs' are, respectively, lists of library names
+ (not filenames!) and search directories. Returns a list of command-line
+ options suitable for use with some compiler (depending on the two format
+ strings passed in).
+ """
+ lib_opts = []
+
+ for dir in library_dirs:
+ lib_opts.append(compiler.library_dir_option(dir))
+
+ for dir in runtime_library_dirs:
+ opt = compiler.runtime_library_dir_option(dir)
+ if isinstance(opt, list):
+ lib_opts.extend(opt)
+ else:
+ lib_opts.append(opt)
+
+ # XXX it's important that we *not* remove redundant library mentions!
+ # sometimes you really do have to say "-lfoo -lbar -lfoo" in order to
+ # resolve all symbols. I just hope we never have to say "-lfoo obj.o
+ # -lbar" to get things to work -- that's certainly a possibility, but a
+ # pretty nasty way to arrange your C code.
+
+ for lib in libraries:
+ lib_dir, lib_name = os.path.split(lib)
+ if lib_dir != '':
+ lib_file = compiler.find_library_file([lib_dir], lib_name)
+ if lib_file is not None:
+ lib_opts.append(lib_file)
+ else:
+ logger.warning("no library file corresponding to "
+ "'%s' found (skipping)" % lib)
+ else:
+ lib_opts.append(compiler.library_option(lib))
+
+ return lib_opts
diff --git a/Lib/packaging/compiler/bcppcompiler.py b/Lib/packaging/compiler/bcppcompiler.py
new file mode 100644
index 0000000..06c758c
--- /dev/null
+++ b/Lib/packaging/compiler/bcppcompiler.py
@@ -0,0 +1,355 @@
+"""CCompiler implementation for the Borland C++ compiler."""
+
+# This implementation by Lyle Johnson, based on the original msvccompiler.py
+# module and using the directions originally published by Gordon Williams.
+
+# XXX looks like there's a LOT of overlap between these two classes:
+# someone should sit down and factor out the common code as
+# WindowsCCompiler! --GPW
+
+import os
+
+from packaging.errors import (PackagingExecError, CompileError, LibError,
+ LinkError, UnknownFileError)
+from packaging.compiler.ccompiler import CCompiler
+from packaging.compiler import gen_preprocess_options
+from packaging.file_util import write_file
+from packaging.dep_util import newer
+from packaging import logger
+
+
+class BCPPCompiler(CCompiler) :
+ """Concrete class that implements an interface to the Borland C/C++
+ compiler, as defined by the CCompiler abstract class.
+ """
+
+ name = 'bcpp'
+ description = 'Borland C++ Compiler'
+
+ # Just set this so CCompiler's constructor doesn't barf. We currently
+ # don't use the 'set_executables()' bureaucracy provided by CCompiler,
+ # as it really isn't necessary for this sort of single-compiler class.
+ # Would be nice to have a consistent interface with UnixCCompiler,
+ # though, so it's worth thinking about.
+ executables = {}
+
+ # Private class data (need to distinguish C from C++ source for compiler)
+ _c_extensions = ['.c']
+ _cpp_extensions = ['.cc', '.cpp', '.cxx']
+
+ # Needed for the filename generation methods provided by the
+ # base class, CCompiler.
+ src_extensions = _c_extensions + _cpp_extensions
+ obj_extension = '.obj'
+ static_lib_extension = '.lib'
+ shared_lib_extension = '.dll'
+ static_lib_format = shared_lib_format = '%s%s'
+ exe_extension = '.exe'
+
+
+ def __init__(self, dry_run=False, force=False):
+ super(BCPPCompiler, self).__init__(dry_run, force)
+
+ # These executables are assumed to all be in the path.
+ # Borland doesn't seem to use any special registry settings to
+ # indicate their installation locations.
+
+ self.cc = "bcc32.exe"
+ self.linker = "ilink32.exe"
+ self.lib = "tlib.exe"
+
+ self.preprocess_options = None
+ self.compile_options = ['/tWM', '/O2', '/q', '/g0']
+ self.compile_options_debug = ['/tWM', '/Od', '/q', '/g0']
+
+ self.ldflags_shared = ['/Tpd', '/Gn', '/q', '/x']
+ self.ldflags_shared_debug = ['/Tpd', '/Gn', '/q', '/x']
+ self.ldflags_static = []
+ self.ldflags_exe = ['/Gn', '/q', '/x']
+ self.ldflags_exe_debug = ['/Gn', '/q', '/x','/r']
+
+
+ # -- Worker methods ------------------------------------------------
+
+ def compile(self, sources,
+ output_dir=None, macros=None, include_dirs=None, debug=False,
+ extra_preargs=None, extra_postargs=None, depends=None):
+
+ macros, objects, extra_postargs, pp_opts, build = \
+ self._setup_compile(output_dir, macros, include_dirs, sources,
+ depends, extra_postargs)
+ compile_opts = extra_preargs or []
+ compile_opts.append('-c')
+ if debug:
+ compile_opts.extend(self.compile_options_debug)
+ else:
+ compile_opts.extend(self.compile_options)
+
+ for obj in objects:
+ try:
+ src, ext = build[obj]
+ except KeyError:
+ continue
+ # XXX why do the normpath here?
+ src = os.path.normpath(src)
+ obj = os.path.normpath(obj)
+ # XXX _setup_compile() did a mkpath() too but before the normpath.
+ # Is it possible to skip the normpath?
+ self.mkpath(os.path.dirname(obj))
+
+ if ext == '.res':
+ # This is already a binary file -- skip it.
+ continue # the 'for' loop
+ if ext == '.rc':
+ # This needs to be compiled to a .res file -- do it now.
+ try:
+ self.spawn(["brcc32", "-fo", obj, src])
+ except PackagingExecError as msg:
+ raise CompileError(msg)
+ continue # the 'for' loop
+
+ # The next two are both for the real compiler.
+ if ext in self._c_extensions:
+ input_opt = ""
+ elif ext in self._cpp_extensions:
+ input_opt = "-P"
+ else:
+ # Unknown file type -- no extra options. The compiler
+ # will probably fail, but let it just in case this is a
+ # file the compiler recognizes even if we don't.
+ input_opt = ""
+
+ output_opt = "-o" + obj
+
+ # Compiler command line syntax is: "bcc32 [options] file(s)".
+ # Note that the source file names must appear at the end of
+ # the command line.
+ try:
+ self.spawn([self.cc] + compile_opts + pp_opts +
+ [input_opt, output_opt] +
+ extra_postargs + [src])
+ except PackagingExecError as msg:
+ raise CompileError(msg)
+
+ return objects
+
+
+ def create_static_lib(self, objects, output_libname, output_dir=None,
+ debug=False, target_lang=None):
+ objects, output_dir = self._fix_object_args(objects, output_dir)
+ output_filename = \
+ self.library_filename(output_libname, output_dir=output_dir)
+
+ if self._need_link(objects, output_filename):
+ lib_args = [output_filename, '/u'] + objects
+ if debug:
+ pass # XXX what goes here?
+ try:
+ self.spawn([self.lib] + lib_args)
+ except PackagingExecError as msg:
+ raise LibError(msg)
+ else:
+ logger.debug("skipping %s (up-to-date)", output_filename)
+
+
+ def link(self, target_desc, objects, output_filename, output_dir=None,
+ libraries=None, library_dirs=None, runtime_library_dirs=None,
+ export_symbols=None, debug=False, extra_preargs=None,
+ extra_postargs=None, build_temp=None, target_lang=None):
+
+ # XXX this ignores 'build_temp'! should follow the lead of
+ # msvccompiler.py
+
+ objects, output_dir = self._fix_object_args(objects, output_dir)
+ libraries, library_dirs, runtime_library_dirs = \
+ self._fix_lib_args(libraries, library_dirs, runtime_library_dirs)
+
+ if runtime_library_dirs:
+ logger.warning("don't know what to do with "
+ "'runtime_library_dirs': %r", runtime_library_dirs)
+
+ if output_dir is not None:
+ output_filename = os.path.join(output_dir, output_filename)
+
+ if self._need_link(objects, output_filename):
+
+ # Figure out linker args based on type of target.
+ if target_desc == CCompiler.EXECUTABLE:
+ startup_obj = 'c0w32'
+ if debug:
+ ld_args = self.ldflags_exe_debug[:]
+ else:
+ ld_args = self.ldflags_exe[:]
+ else:
+ startup_obj = 'c0d32'
+ if debug:
+ ld_args = self.ldflags_shared_debug[:]
+ else:
+ ld_args = self.ldflags_shared[:]
+
+
+ # Create a temporary exports file for use by the linker
+ if export_symbols is None:
+ def_file = ''
+ else:
+ head, tail = os.path.split(output_filename)
+ modname, ext = os.path.splitext(tail)
+ temp_dir = os.path.dirname(objects[0]) # preserve tree structure
+ def_file = os.path.join(temp_dir, '%s.def' % modname)
+ contents = ['EXPORTS']
+ for sym in (export_symbols or []):
+ contents.append(' %s=_%s' % (sym, sym))
+ self.execute(write_file, (def_file, contents),
+ "writing %s" % def_file)
+
+ # Borland C++ has problems with '/' in paths
+ objects2 = [os.path.normpath(o) for o in objects]
+ # split objects in .obj and .res files
+ # Borland C++ needs them at different positions in the command line
+ objects = [startup_obj]
+ resources = []
+ for file in objects2:
+ base, ext = os.path.splitext(os.path.normcase(file))
+ if ext == '.res':
+ resources.append(file)
+ else:
+ objects.append(file)
+
+
+ for l in library_dirs:
+ ld_args.append("/L%s" % os.path.normpath(l))
+ ld_args.append("/L.") # we sometimes use relative paths
+
+ # list of object files
+ ld_args.extend(objects)
+
+ # XXX the command line syntax for Borland C++ is a bit wonky;
+ # certain filenames are jammed together in one big string, but
+ # comma-delimited. This doesn't mesh too well with the
+ # Unix-centric attitude (with a DOS/Windows quoting hack) of
+ # 'spawn()', so constructing the argument list is a bit
+ # awkward. Note that doing the obvious thing and jamming all
+ # the filenames and commas into one argument would be wrong,
+ # because 'spawn()' would quote any filenames with spaces in
+ # them. Arghghh!. Apparently it works fine as coded...
+
+ # name of dll/exe file
+ ld_args.extend((',',output_filename))
+ # no map file and start libraries
+ ld_args.append(',,')
+
+ for lib in libraries:
+ # see if we find it and if there is a bcpp specific lib
+ # (xxx_bcpp.lib)
+ libfile = self.find_library_file(library_dirs, lib, debug)
+ if libfile is None:
+ ld_args.append(lib)
+ # probably a BCPP internal library -- don't warn
+ else:
+ # full name which prefers bcpp_xxx.lib over xxx.lib
+ ld_args.append(libfile)
+
+ # some default libraries
+ ld_args.append('import32')
+ ld_args.append('cw32mt')
+
+ # def file for export symbols
+ ld_args.extend((',',def_file))
+ # add resource files
+ ld_args.append(',')
+ ld_args.extend(resources)
+
+
+ if extra_preargs:
+ ld_args[:0] = extra_preargs
+ if extra_postargs:
+ ld_args.extend(extra_postargs)
+
+ self.mkpath(os.path.dirname(output_filename))
+ try:
+ self.spawn([self.linker] + ld_args)
+ except PackagingExecError as msg:
+ raise LinkError(msg)
+
+ else:
+ logger.debug("skipping %s (up-to-date)", output_filename)
+
+ # -- Miscellaneous methods -----------------------------------------
+
+
+ def find_library_file(self, dirs, lib, debug=False):
+ # List of effective library names to try, in order of preference:
+ # xxx_bcpp.lib is better than xxx.lib
+ # and xxx_d.lib is better than xxx.lib if debug is set
+ #
+ # The "_bcpp" suffix is to handle a Python installation for people
+ # with multiple compilers (primarily Packaging hackers, I suspect
+ # ;-). The idea is they'd have one static library for each
+ # compiler they care about, since (almost?) every Windows compiler
+ # seems to have a different format for static libraries.
+ if debug:
+ dlib = (lib + "_d")
+ try_names = (dlib + "_bcpp", lib + "_bcpp", dlib, lib)
+ else:
+ try_names = (lib + "_bcpp", lib)
+
+ for dir in dirs:
+ for name in try_names:
+ libfile = os.path.join(dir, self.library_filename(name))
+ if os.path.exists(libfile):
+ return libfile
+ else:
+ # Oops, didn't find it in *any* of 'dirs'
+ return None
+
+ # overwrite the one from CCompiler to support rc and res-files
+ def object_filenames(self, source_filenames, strip_dir=False,
+ output_dir=''):
+ if output_dir is None:
+ output_dir = ''
+ obj_names = []
+ for src_name in source_filenames:
+ # use normcase to make sure '.rc' is really '.rc' and not '.RC'
+ base, ext = os.path.splitext(os.path.normcase(src_name))
+ if ext not in (self.src_extensions + ['.rc','.res']):
+ raise UnknownFileError("unknown file type '%s' (from '%s')" % \
+ (ext, src_name))
+ if strip_dir:
+ base = os.path.basename(base)
+ if ext == '.res':
+ # these can go unchanged
+ obj_names.append(os.path.join(output_dir, base + ext))
+ elif ext == '.rc':
+ # these need to be compiled to .res-files
+ obj_names.append(os.path.join(output_dir, base + '.res'))
+ else:
+ obj_names.append(os.path.join(output_dir,
+ base + self.obj_extension))
+ return obj_names
+
+
+ def preprocess(self, source, output_file=None, macros=None,
+ include_dirs=None, extra_preargs=None,
+ extra_postargs=None):
+ _, macros, include_dirs = \
+ self._fix_compile_args(None, macros, include_dirs)
+ pp_opts = gen_preprocess_options(macros, include_dirs)
+ pp_args = ['cpp32.exe'] + pp_opts
+ if output_file is not None:
+ pp_args.append('-o' + output_file)
+ if extra_preargs:
+ pp_args[:0] = extra_preargs
+ if extra_postargs:
+ pp_args.extend(extra_postargs)
+ pp_args.append(source)
+
+ # We need to preprocess: either we're being forced to, or the
+ # source file is newer than the target (or the target doesn't
+ # exist).
+ if self.force or output_file is None or newer(source, output_file):
+ if output_file:
+ self.mkpath(os.path.dirname(output_file))
+ try:
+ self.spawn(pp_args)
+ except PackagingExecError as msg:
+ raise CompileError(msg)
diff --git a/Lib/packaging/compiler/ccompiler.py b/Lib/packaging/compiler/ccompiler.py
new file mode 100644
index 0000000..98c4b68
--- /dev/null
+++ b/Lib/packaging/compiler/ccompiler.py
@@ -0,0 +1,863 @@
+"""Abstract base class for compilers.
+
+This modules contains CCompiler, an abstract base class that defines the
+interface for the compiler abstraction model used by packaging.
+"""
+
+import os
+from shutil import move
+from packaging import logger
+from packaging.util import split_quoted, execute, newer_group, spawn
+from packaging.errors import (CompileError, LinkError, UnknownFileError)
+from packaging.compiler import gen_preprocess_options
+
+
+class CCompiler:
+ """Abstract base class to define the interface that must be implemented
+ by real compiler classes. Also has some utility methods used by
+ several compiler classes.
+
+ The basic idea behind a compiler abstraction class is that each
+ instance can be used for all the compile/link steps in building a
+ single project. Thus, attributes common to all of those compile and
+ link steps -- include directories, macros to define, libraries to link
+ against, etc. -- are attributes of the compiler instance. To allow for
+ variability in how individual files are treated, most of those
+ attributes may be varied on a per-compilation or per-link basis.
+ """
+
+ # 'name' is a class attribute that identifies this class. It
+ # keeps code that wants to know what kind of compiler it's dealing with
+ # from having to import all possible compiler classes just to do an
+ # 'isinstance'.
+ name = None
+ description = None
+
+ # XXX things not handled by this compiler abstraction model:
+ # * client can't provide additional options for a compiler,
+ # e.g. warning, optimization, debugging flags. Perhaps this
+ # should be the domain of concrete compiler abstraction classes
+ # (UnixCCompiler, MSVCCompiler, etc.) -- or perhaps the base
+ # class should have methods for the common ones.
+ # * can't completely override the include or library searchg
+ # path, ie. no "cc -I -Idir1 -Idir2" or "cc -L -Ldir1 -Ldir2".
+ # I'm not sure how widely supported this is even by Unix
+ # compilers, much less on other platforms. And I'm even less
+ # sure how useful it is; maybe for cross-compiling, but
+ # support for that is a ways off. (And anyways, cross
+ # compilers probably have a dedicated binary with the
+ # right paths compiled in. I hope.)
+ # * can't do really freaky things with the library list/library
+ # dirs, e.g. "-Ldir1 -lfoo -Ldir2 -lfoo" to link against
+ # different versions of libfoo.a in different locations. I
+ # think this is useless without the ability to null out the
+ # library search path anyways.
+
+
+ # Subclasses that rely on the standard filename generation methods
+ # implemented below should override these; see the comment near
+ # those methods ('object_filenames()' et. al.) for details:
+ src_extensions = None # list of strings
+ obj_extension = None # string
+ static_lib_extension = None
+ shared_lib_extension = None # string
+ static_lib_format = None # format string
+ shared_lib_format = None # prob. same as static_lib_format
+ exe_extension = None # string
+
+ # Default language settings. language_map is used to detect a source
+ # file or Extension target language, checking source filenames.
+ # language_order is used to detect the language precedence, when deciding
+ # what language to use when mixing source types. For example, if some
+ # extension has two files with ".c" extension, and one with ".cpp", it
+ # is still linked as c++.
+ language_map = {".c": "c",
+ ".cc": "c++",
+ ".cpp": "c++",
+ ".cxx": "c++",
+ ".m": "objc",
+ }
+ language_order = ["c++", "objc", "c"]
+
+ def __init__(self, dry_run=False, force=False):
+ self.dry_run = dry_run
+ self.force = force
+
+ # 'output_dir': a common output directory for object, library,
+ # shared object, and shared library files
+ self.output_dir = None
+
+ # 'macros': a list of macro definitions (or undefinitions). A
+ # macro definition is a 2-tuple (name, value), where the value is
+ # either a string or None (no explicit value). A macro
+ # undefinition is a 1-tuple (name,).
+ self.macros = []
+
+ # 'include_dirs': a list of directories to search for include files
+ self.include_dirs = []
+
+ # 'libraries': a list of libraries to include in any link
+ # (library names, not filenames: eg. "foo" not "libfoo.a")
+ self.libraries = []
+
+ # 'library_dirs': a list of directories to search for libraries
+ self.library_dirs = []
+
+ # 'runtime_library_dirs': a list of directories to search for
+ # shared libraries/objects at runtime
+ self.runtime_library_dirs = []
+
+ # 'objects': a list of object files (or similar, such as explicitly
+ # named library files) to include on any link
+ self.objects = []
+
+ for key, value in self.executables.items():
+ self.set_executable(key, value)
+
+ def set_executables(self, **args):
+ """Define the executables (and options for them) that will be run
+ to perform the various stages of compilation. The exact set of
+ executables that may be specified here depends on the compiler
+ class (via the 'executables' class attribute), but most will have:
+ compiler the C/C++ compiler
+ linker_so linker used to create shared objects and libraries
+ linker_exe linker used to create binary executables
+ archiver static library creator
+
+ On platforms with a command line (Unix, DOS/Windows), each of these
+ is a string that will be split into executable name and (optional)
+ list of arguments. (Splitting the string is done similarly to how
+ Unix shells operate: words are delimited by spaces, but quotes and
+ backslashes can override this. See
+ 'distutils.util.split_quoted()'.)
+ """
+
+ # Note that some CCompiler implementation classes will define class
+ # attributes 'cpp', 'cc', etc. with hard-coded executable names;
+ # this is appropriate when a compiler class is for exactly one
+ # compiler/OS combination (eg. MSVCCompiler). Other compiler
+ # classes (UnixCCompiler, in particular) are driven by information
+ # discovered at run-time, since there are many different ways to do
+ # basically the same things with Unix C compilers.
+
+ for key, value in args.items():
+ if key not in self.executables:
+ raise ValueError("unknown executable '%s' for class %s" % \
+ (key, self.__class__.__name__))
+ self.set_executable(key, value)
+
+ def set_executable(self, key, value):
+ if isinstance(value, str):
+ setattr(self, key, split_quoted(value))
+ else:
+ setattr(self, key, value)
+
+ def _find_macro(self, name):
+ i = 0
+ for defn in self.macros:
+ if defn[0] == name:
+ return i
+ i = i + 1
+ return None
+
+ def _check_macro_definitions(self, definitions):
+ """Ensures that every element of 'definitions' is a valid macro
+ definition, ie. either (name,value) 2-tuple or a (name,) tuple. Do
+ nothing if all definitions are OK, raise TypeError otherwise.
+ """
+ for defn in definitions:
+ if not (isinstance(defn, tuple) and
+ (len(defn) == 1 or
+ (len(defn) == 2 and
+ (isinstance(defn[1], str) or defn[1] is None))) and
+ isinstance(defn[0], str)):
+ raise TypeError(("invalid macro definition '%s': " % defn) + \
+ "must be tuple (string,), (string, string), or " + \
+ "(string, None)")
+
+
+ # -- Bookkeeping methods -------------------------------------------
+
+ def define_macro(self, name, value=None):
+ """Define a preprocessor macro for all compilations driven by this
+ compiler object. The optional parameter 'value' should be a
+ string; if it is not supplied, then the macro will be defined
+ without an explicit value and the exact outcome depends on the
+ compiler used (XXX true? does ANSI say anything about this?)
+ """
+ # Delete from the list of macro definitions/undefinitions if
+ # already there (so that this one will take precedence).
+ i = self._find_macro(name)
+ if i is not None:
+ del self.macros[i]
+
+ defn = (name, value)
+ self.macros.append(defn)
+
+ def undefine_macro(self, name):
+ """Undefine a preprocessor macro for all compilations driven by
+ this compiler object. If the same macro is defined by
+ 'define_macro()' and undefined by 'undefine_macro()' the last call
+ takes precedence (including multiple redefinitions or
+ undefinitions). If the macro is redefined/undefined on a
+ per-compilation basis (ie. in the call to 'compile()'), then that
+ takes precedence.
+ """
+ # Delete from the list of macro definitions/undefinitions if
+ # already there (so that this one will take precedence).
+ i = self._find_macro(name)
+ if i is not None:
+ del self.macros[i]
+
+ undefn = (name,)
+ self.macros.append(undefn)
+
+ def add_include_dir(self, dir):
+ """Add 'dir' to the list of directories that will be searched for
+ header files. The compiler is instructed to search directories in
+ the order in which they are supplied by successive calls to
+ 'add_include_dir()'.
+ """
+ self.include_dirs.append(dir)
+
+ def set_include_dirs(self, dirs):
+ """Set the list of directories that will be searched to 'dirs' (a
+ list of strings). Overrides any preceding calls to
+ 'add_include_dir()'; subsequence calls to 'add_include_dir()' add
+ to the list passed to 'set_include_dirs()'. This does not affect
+ any list of standard include directories that the compiler may
+ search by default.
+ """
+ self.include_dirs = dirs[:]
+
+ def add_library(self, libname):
+ """Add 'libname' to the list of libraries that will be included in
+ all links driven by this compiler object. Note that 'libname'
+ should *not* be the name of a file containing a library, but the
+ name of the library itself: the actual filename will be inferred by
+ the linker, the compiler, or the compiler class (depending on the
+ platform).
+
+ The linker will be instructed to link against libraries in the
+ order they were supplied to 'add_library()' and/or
+ 'set_libraries()'. It is perfectly valid to duplicate library
+ names; the linker will be instructed to link against libraries as
+ many times as they are mentioned.
+ """
+ self.libraries.append(libname)
+
+ def set_libraries(self, libnames):
+ """Set the list of libraries to be included in all links driven by
+ this compiler object to 'libnames' (a list of strings). This does
+ not affect any standard system libraries that the linker may
+ include by default.
+ """
+ self.libraries = libnames[:]
+
+
+ def add_library_dir(self, dir):
+ """Add 'dir' to the list of directories that will be searched for
+ libraries specified to 'add_library()' and 'set_libraries()'. The
+ linker will be instructed to search for libraries in the order they
+ are supplied to 'add_library_dir()' and/or 'set_library_dirs()'.
+ """
+ self.library_dirs.append(dir)
+
+ def set_library_dirs(self, dirs):
+ """Set the list of library search directories to 'dirs' (a list of
+ strings). This does not affect any standard library search path
+ that the linker may search by default.
+ """
+ self.library_dirs = dirs[:]
+
+ def add_runtime_library_dir(self, dir):
+ """Add 'dir' to the list of directories that will be searched for
+ shared libraries at runtime.
+ """
+ self.runtime_library_dirs.append(dir)
+
+ def set_runtime_library_dirs(self, dirs):
+ """Set the list of directories to search for shared libraries at
+ runtime to 'dirs' (a list of strings). This does not affect any
+ standard search path that the runtime linker may search by
+ default.
+ """
+ self.runtime_library_dirs = dirs[:]
+
+ def add_link_object(self, object):
+ """Add 'object' to the list of object files (or analogues, such as
+ explicitly named library files or the output of "resource
+ compilers") to be included in every link driven by this compiler
+ object.
+ """
+ self.objects.append(object)
+
+ def set_link_objects(self, objects):
+ """Set the list of object files (or analogues) to be included in
+ every link to 'objects'. This does not affect any standard object
+ files that the linker may include by default (such as system
+ libraries).
+ """
+ self.objects = objects[:]
+
+
+ # -- Private utility methods --------------------------------------
+ # (here for the convenience of subclasses)
+
+ # Helper method to prep compiler in subclass compile() methods
+ def _setup_compile(self, outdir, macros, incdirs, sources, depends,
+ extra):
+ """Process arguments and decide which source files to compile."""
+ if outdir is None:
+ outdir = self.output_dir
+ elif not isinstance(outdir, str):
+ raise TypeError("'output_dir' must be a string or None")
+
+ if macros is None:
+ macros = self.macros
+ elif isinstance(macros, list):
+ macros = macros + (self.macros or [])
+ else:
+ raise TypeError("'macros' (if supplied) must be a list of tuples")
+
+ if incdirs is None:
+ incdirs = self.include_dirs
+ elif isinstance(incdirs, (list, tuple)):
+ incdirs = list(incdirs) + (self.include_dirs or [])
+ else:
+ raise TypeError(
+ "'include_dirs' (if supplied) must be a list of strings")
+
+ if extra is None:
+ extra = []
+
+ # Get the list of expected output (object) files
+ objects = self.object_filenames(sources,
+ strip_dir=False,
+ output_dir=outdir)
+ assert len(objects) == len(sources)
+
+ pp_opts = gen_preprocess_options(macros, incdirs)
+
+ build = {}
+ for i in range(len(sources)):
+ src = sources[i]
+ obj = objects[i]
+ ext = os.path.splitext(src)[1]
+ self.mkpath(os.path.dirname(obj))
+ build[obj] = (src, ext)
+
+ return macros, objects, extra, pp_opts, build
+
+ def _get_cc_args(self, pp_opts, debug, before):
+ # works for unixccompiler and cygwinccompiler
+ cc_args = pp_opts + ['-c']
+ if debug:
+ cc_args[:0] = ['-g']
+ if before:
+ cc_args[:0] = before
+ return cc_args
+
+ def _fix_compile_args(self, output_dir, macros, include_dirs):
+ """Typecheck and fix-up some of the arguments to the 'compile()'
+ method, and return fixed-up values. Specifically: if 'output_dir'
+ is None, replaces it with 'self.output_dir'; ensures that 'macros'
+ is a list, and augments it with 'self.macros'; ensures that
+ 'include_dirs' is a list, and augments it with 'self.include_dirs'.
+ Guarantees that the returned values are of the correct type,
+ i.e. for 'output_dir' either string or None, and for 'macros' and
+ 'include_dirs' either list or None.
+ """
+ if output_dir is None:
+ output_dir = self.output_dir
+ elif not isinstance(output_dir, str):
+ raise TypeError("'output_dir' must be a string or None")
+
+ if macros is None:
+ macros = self.macros
+ elif isinstance(macros, list):
+ macros = macros + (self.macros or [])
+ else:
+ raise TypeError("'macros' (if supplied) must be a list of tuples")
+
+ if include_dirs is None:
+ include_dirs = self.include_dirs
+ elif isinstance(include_dirs, (list, tuple)):
+ include_dirs = list(include_dirs) + (self.include_dirs or [])
+ else:
+ raise TypeError(
+ "'include_dirs' (if supplied) must be a list of strings")
+
+ return output_dir, macros, include_dirs
+
+ def _fix_object_args(self, objects, output_dir):
+ """Typecheck and fix up some arguments supplied to various methods.
+ Specifically: ensure that 'objects' is a list; if output_dir is
+ None, replace with self.output_dir. Return fixed versions of
+ 'objects' and 'output_dir'.
+ """
+ if not isinstance(objects, (list, tuple)):
+ raise TypeError("'objects' must be a list or tuple of strings")
+ objects = list(objects)
+
+ if output_dir is None:
+ output_dir = self.output_dir
+ elif not isinstance(output_dir, str):
+ raise TypeError("'output_dir' must be a string or None")
+
+ return objects, output_dir
+
+ def _fix_lib_args(self, libraries, library_dirs, runtime_library_dirs):
+ """Typecheck and fix up some of the arguments supplied to the
+ 'link_*' methods. Specifically: ensure that all arguments are
+ lists, and augment them with their permanent versions
+ (eg. 'self.libraries' augments 'libraries'). Return a tuple with
+ fixed versions of all arguments.
+ """
+ if libraries is None:
+ libraries = self.libraries
+ elif isinstance(libraries, (list, tuple)):
+ libraries = list(libraries) + (self.libraries or [])
+ else:
+ raise TypeError(
+ "'libraries' (if supplied) must be a list of strings")
+
+ if library_dirs is None:
+ library_dirs = self.library_dirs
+ elif isinstance(library_dirs, (list, tuple)):
+ library_dirs = list(library_dirs) + (self.library_dirs or [])
+ else:
+ raise TypeError(
+ "'library_dirs' (if supplied) must be a list of strings")
+
+ if runtime_library_dirs is None:
+ runtime_library_dirs = self.runtime_library_dirs
+ elif isinstance(runtime_library_dirs, (list, tuple)):
+ runtime_library_dirs = (list(runtime_library_dirs) +
+ (self.runtime_library_dirs or []))
+ else:
+ raise TypeError("'runtime_library_dirs' (if supplied) "
+ "must be a list of strings")
+
+ return libraries, library_dirs, runtime_library_dirs
+
+ def _need_link(self, objects, output_file):
+ """Return true if we need to relink the files listed in 'objects'
+ to recreate 'output_file'.
+ """
+ if self.force:
+ return True
+ else:
+ if self.dry_run:
+ newer = newer_group(objects, output_file, missing='newer')
+ else:
+ newer = newer_group(objects, output_file)
+ return newer
+
+ def detect_language(self, sources):
+ """Detect the language of a given file, or list of files. Uses
+ language_map, and language_order to do the job.
+ """
+ if not isinstance(sources, list):
+ sources = [sources]
+ lang = None
+ index = len(self.language_order)
+ for source in sources:
+ base, ext = os.path.splitext(source)
+ extlang = self.language_map.get(ext)
+ try:
+ extindex = self.language_order.index(extlang)
+ if extindex < index:
+ lang = extlang
+ index = extindex
+ except ValueError:
+ pass
+ return lang
+
+ # -- Worker methods ------------------------------------------------
+ # (must be implemented by subclasses)
+
+ def preprocess(self, source, output_file=None, macros=None,
+ include_dirs=None, extra_preargs=None, extra_postargs=None):
+ """Preprocess a single C/C++ source file, named in 'source'.
+ Output will be written to file named 'output_file', or stdout if
+ 'output_file' not supplied. 'macros' is a list of macro
+ definitions as for 'compile()', which will augment the macros set
+ with 'define_macro()' and 'undefine_macro()'. 'include_dirs' is a
+ list of directory names that will be added to the default list.
+
+ Raises PreprocessError on failure.
+ """
+ pass
+
+ def compile(self, sources, output_dir=None, macros=None,
+ include_dirs=None, debug=False, extra_preargs=None,
+ extra_postargs=None, depends=None):
+ """Compile one or more source files.
+
+ 'sources' must be a list of filenames, most likely C/C++
+ files, but in reality anything that can be handled by a
+ particular compiler and compiler class (eg. MSVCCompiler can
+ handle resource files in 'sources'). Return a list of object
+ filenames, one per source filename in 'sources'. Depending on
+ the implementation, not all source files will necessarily be
+ compiled, but all corresponding object filenames will be
+ returned.
+
+ If 'output_dir' is given, object files will be put under it, while
+ retaining their original path component. That is, "foo/bar.c"
+ normally compiles to "foo/bar.o" (for a Unix implementation); if
+ 'output_dir' is "build", then it would compile to
+ "build/foo/bar.o".
+
+ 'macros', if given, must be a list of macro definitions. A macro
+ definition is either a (name, value) 2-tuple or a (name,) 1-tuple.
+ The former defines a macro; if the value is None, the macro is
+ defined without an explicit value. The 1-tuple case undefines a
+ macro. Later definitions/redefinitions/ undefinitions take
+ precedence.
+
+ 'include_dirs', if given, must be a list of strings, the
+ directories to add to the default include file search path for this
+ compilation only.
+
+ 'debug' is a boolean; if true, the compiler will be instructed to
+ output debug symbols in (or alongside) the object file(s).
+
+ 'extra_preargs' and 'extra_postargs' are implementation- dependent.
+ On platforms that have the notion of a command line (e.g. Unix,
+ DOS/Windows), they are most likely lists of strings: extra
+ command-line arguments to prepand/append to the compiler command
+ line. On other platforms, consult the implementation class
+ documentation. In any event, they are intended as an escape hatch
+ for those occasions when the abstract compiler framework doesn't
+ cut the mustard.
+
+ 'depends', if given, is a list of filenames that all targets
+ depend on. If a source file is older than any file in
+ depends, then the source file will be recompiled. This
+ supports dependency tracking, but only at a coarse
+ granularity.
+
+ Raises CompileError on failure.
+ """
+ # A concrete compiler class can either override this method
+ # entirely or implement _compile().
+
+ macros, objects, extra_postargs, pp_opts, build = \
+ self._setup_compile(output_dir, macros, include_dirs, sources,
+ depends, extra_postargs)
+ cc_args = self._get_cc_args(pp_opts, debug, extra_preargs)
+
+ for obj in objects:
+ try:
+ src, ext = build[obj]
+ except KeyError:
+ continue
+ self._compile(obj, src, ext, cc_args, extra_postargs, pp_opts)
+
+ # Return *all* object filenames, not just the ones we just built.
+ return objects
+
+ def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
+ """Compile 'src' to product 'obj'."""
+
+ # A concrete compiler class that does not override compile()
+ # should implement _compile().
+ pass
+
+ def create_static_lib(self, objects, output_libname, output_dir=None,
+ debug=False, target_lang=None):
+ """Link a bunch of stuff together to create a static library file.
+ The "bunch of stuff" consists of the list of object files supplied
+ as 'objects', the extra object files supplied to
+ 'add_link_object()' and/or 'set_link_objects()', the libraries
+ supplied to 'add_library()' and/or 'set_libraries()', and the
+ libraries supplied as 'libraries' (if any).
+
+ 'output_libname' should be a library name, not a filename; the
+ filename will be inferred from the library name. 'output_dir' is
+ the directory where the library file will be put.
+
+ 'debug' is a boolean; if true, debugging information will be
+ included in the library (note that on most platforms, it is the
+ compile step where this matters: the 'debug' flag is included here
+ just for consistency).
+
+ 'target_lang' is the target language for which the given objects
+ are being compiled. This allows specific linkage time treatment of
+ certain languages.
+
+ Raises LibError on failure.
+ """
+ pass
+
+ # values for target_desc parameter in link()
+ SHARED_OBJECT = "shared_object"
+ SHARED_LIBRARY = "shared_library"
+ EXECUTABLE = "executable"
+
+ def link(self, target_desc, objects, output_filename, output_dir=None,
+ libraries=None, library_dirs=None, runtime_library_dirs=None,
+ export_symbols=None, debug=False, extra_preargs=None,
+ extra_postargs=None, build_temp=None, target_lang=None):
+ """Link a bunch of stuff together to create an executable or
+ shared library file.
+
+ The "bunch of stuff" consists of the list of object files supplied
+ as 'objects'. 'output_filename' should be a filename. If
+ 'output_dir' is supplied, 'output_filename' is relative to it
+ (i.e. 'output_filename' can provide directory components if
+ needed).
+
+ 'libraries' is a list of libraries to link against. These are
+ library names, not filenames, since they're translated into
+ filenames in a platform-specific way (eg. "foo" becomes "libfoo.a"
+ on Unix and "foo.lib" on DOS/Windows). However, they can include a
+ directory component, which means the linker will look in that
+ specific directory rather than searching all the normal locations.
+
+ 'library_dirs', if supplied, should be a list of directories to
+ search for libraries that were specified as bare library names
+ (ie. no directory component). These are on top of the system
+ default and those supplied to 'add_library_dir()' and/or
+ 'set_library_dirs()'. 'runtime_library_dirs' is a list of
+ directories that will be embedded into the shared library and used
+ to search for other shared libraries that *it* depends on at
+ run-time. (This may only be relevant on Unix.)
+
+ 'export_symbols' is a list of symbols that the shared library will
+ export. (This appears to be relevant only on Windows.)
+
+ 'debug' is as for 'compile()' and 'create_static_lib()', with the
+ slight distinction that it actually matters on most platforms (as
+ opposed to 'create_static_lib()', which includes a 'debug' flag
+ mostly for form's sake).
+
+ 'extra_preargs' and 'extra_postargs' are as for 'compile()' (except
+ of course that they supply command-line arguments for the
+ particular linker being used).
+
+ 'target_lang' is the target language for which the given objects
+ are being compiled. This allows specific linkage time treatment of
+ certain languages.
+
+ Raises LinkError on failure.
+ """
+ raise NotImplementedError
+
+
+ # Old 'link_*()' methods, rewritten to use the new 'link()' method.
+
+ def link_shared_lib(self, objects, output_libname, output_dir=None,
+ libraries=None, library_dirs=None,
+ runtime_library_dirs=None, export_symbols=None,
+ debug=False, extra_preargs=None, extra_postargs=None,
+ build_temp=None, target_lang=None):
+ self.link(CCompiler.SHARED_LIBRARY, objects,
+ self.library_filename(output_libname, lib_type='shared'),
+ output_dir,
+ libraries, library_dirs, runtime_library_dirs,
+ export_symbols, debug,
+ extra_preargs, extra_postargs, build_temp, target_lang)
+
+ def link_shared_object(self, objects, output_filename, output_dir=None,
+ libraries=None, library_dirs=None,
+ runtime_library_dirs=None, export_symbols=None,
+ debug=False, extra_preargs=None, extra_postargs=None,
+ build_temp=None, target_lang=None):
+ self.link(CCompiler.SHARED_OBJECT, objects,
+ output_filename, output_dir,
+ libraries, library_dirs, runtime_library_dirs,
+ export_symbols, debug,
+ extra_preargs, extra_postargs, build_temp, target_lang)
+
+ def link_executable(self, objects, output_progname, output_dir=None,
+ libraries=None, library_dirs=None,
+ runtime_library_dirs=None, debug=False,
+ extra_preargs=None, extra_postargs=None,
+ target_lang=None):
+ self.link(CCompiler.EXECUTABLE, objects,
+ self.executable_filename(output_progname), output_dir,
+ libraries, library_dirs, runtime_library_dirs, None,
+ debug, extra_preargs, extra_postargs, None, target_lang)
+
+
+ # -- Miscellaneous methods -----------------------------------------
+ # These are all used by the 'gen_lib_options() function; there is
+ # no appropriate default implementation so subclasses should
+ # implement all of these.
+
+ def library_dir_option(self, dir):
+ """Return the compiler option to add 'dir' to the list of
+ directories searched for libraries.
+ """
+ raise NotImplementedError
+
+ def runtime_library_dir_option(self, dir):
+ """Return the compiler option to add 'dir' to the list of
+ directories searched for runtime libraries.
+ """
+ raise NotImplementedError
+
+ def library_option(self, lib):
+ """Return the compiler option to add 'dir' to the list of libraries
+ linked into the shared library or executable.
+ """
+ raise NotImplementedError
+
+ def has_function(self, funcname, includes=None, include_dirs=None,
+ libraries=None, library_dirs=None):
+ """Return a boolean indicating whether funcname is supported on
+ the current platform. The optional arguments can be used to
+ augment the compilation environment.
+ """
+
+ # this can't be included at module scope because it tries to
+ # import math which might not be available at that point - maybe
+ # the necessary logic should just be inlined?
+ import tempfile
+ if includes is None:
+ includes = []
+ if include_dirs is None:
+ include_dirs = []
+ if libraries is None:
+ libraries = []
+ if library_dirs is None:
+ library_dirs = []
+ fd, fname = tempfile.mkstemp(".c", funcname, text=True)
+ with os.fdopen(fd, "w") as f:
+ for incl in includes:
+ f.write("""#include "%s"\n""" % incl)
+ f.write("""\
+main (int argc, char **argv) {
+ %s();
+}
+""" % funcname)
+ try:
+ objects = self.compile([fname], include_dirs=include_dirs)
+ except CompileError:
+ return False
+
+ try:
+ self.link_executable(objects, "a.out",
+ libraries=libraries,
+ library_dirs=library_dirs)
+ except (LinkError, TypeError):
+ return False
+ return True
+
+ def find_library_file(self, dirs, lib, debug=False):
+ """Search the specified list of directories for a static or shared
+ library file 'lib' and return the full path to that file. If
+ 'debug' is true, look for a debugging version (if that makes sense on
+ the current platform). Return None if 'lib' wasn't found in any of
+ the specified directories.
+ """
+ raise NotImplementedError
+
+ # -- Filename generation methods -----------------------------------
+
+ # The default implementation of the filename generating methods are
+ # prejudiced towards the Unix/DOS/Windows view of the world:
+ # * object files are named by replacing the source file extension
+ # (eg. .c/.cpp -> .o/.obj)
+ # * library files (shared or static) are named by plugging the
+ # library name and extension into a format string, eg.
+ # "lib%s.%s" % (lib_name, ".a") for Unix static libraries
+ # * executables are named by appending an extension (possibly
+ # empty) to the program name: eg. progname + ".exe" for
+ # Windows
+ #
+ # To reduce redundant code, these methods expect to find
+ # several attributes in the current object (presumably defined
+ # as class attributes):
+ # * src_extensions -
+ # list of C/C++ source file extensions, eg. ['.c', '.cpp']
+ # * obj_extension -
+ # object file extension, eg. '.o' or '.obj'
+ # * static_lib_extension -
+ # extension for static library files, eg. '.a' or '.lib'
+ # * shared_lib_extension -
+ # extension for shared library/object files, eg. '.so', '.dll'
+ # * static_lib_format -
+ # format string for generating static library filenames,
+ # eg. 'lib%s.%s' or '%s.%s'
+ # * shared_lib_format
+ # format string for generating shared library filenames
+ # (probably same as static_lib_format, since the extension
+ # is one of the intended parameters to the format string)
+ # * exe_extension -
+ # extension for executable files, eg. '' or '.exe'
+
+ def object_filenames(self, source_filenames, strip_dir=False, output_dir=''):
+ if output_dir is None:
+ output_dir = ''
+ obj_names = []
+ for src_name in source_filenames:
+ base, ext = os.path.splitext(src_name)
+ base = os.path.splitdrive(base)[1] # Chop off the drive
+ base = base[os.path.isabs(base):] # If abs, chop off leading /
+ if ext not in self.src_extensions:
+ raise UnknownFileError("unknown file type '%s' (from '%s')" %
+ (ext, src_name))
+ if strip_dir:
+ base = os.path.basename(base)
+ obj_names.append(os.path.join(output_dir,
+ base + self.obj_extension))
+ return obj_names
+
+ def shared_object_filename(self, basename, strip_dir=False, output_dir=''):
+ assert output_dir is not None
+ if strip_dir:
+ basename = os.path.basename(basename)
+ return os.path.join(output_dir, basename + self.shared_lib_extension)
+
+ def executable_filename(self, basename, strip_dir=False, output_dir=''):
+ assert output_dir is not None
+ if strip_dir:
+ basename = os.path.basename(basename)
+ return os.path.join(output_dir, basename + (self.exe_extension or ''))
+
+ def library_filename(self, libname, lib_type='static', # or 'shared'
+ strip_dir=False, output_dir=''):
+ assert output_dir is not None
+ if lib_type not in ("static", "shared", "dylib"):
+ raise ValueError(
+ "'lib_type' must be 'static', 'shared' or 'dylib'")
+ fmt = getattr(self, lib_type + "_lib_format")
+ ext = getattr(self, lib_type + "_lib_extension")
+
+ dir, base = os.path.split(libname)
+ filename = fmt % (base, ext)
+ if strip_dir:
+ dir = ''
+
+ return os.path.join(output_dir, dir, filename)
+
+
+ # -- Utility methods -----------------------------------------------
+
+ def execute(self, func, args, msg=None, level=1):
+ execute(func, args, msg, self.dry_run)
+
+ def spawn(self, cmd):
+ spawn(cmd, dry_run=self.dry_run)
+
+ def move_file(self, src, dst):
+ logger.info("moving %r to %r", src, dst)
+ if self.dry_run:
+ return
+ return move(src, dst)
+
+ def mkpath(self, name, mode=0o777):
+ name = os.path.normpath(name)
+ if os.path.isdir(name) or name == '':
+ return
+ if self.dry_run:
+ head = ''
+ for part in name.split(os.sep):
+ logger.info("created directory %s%s", head, part)
+ head += part + os.sep
+ return
+ os.makedirs(name, mode)
diff --git a/Lib/packaging/compiler/cygwinccompiler.py b/Lib/packaging/compiler/cygwinccompiler.py
new file mode 100644
index 0000000..3eec067
--- /dev/null
+++ b/Lib/packaging/compiler/cygwinccompiler.py
@@ -0,0 +1,351 @@
+"""CCompiler implementations for Cygwin and mingw32 versions of GCC.
+
+This module contains the CygwinCCompiler class, a subclass of
+UnixCCompiler that handles the Cygwin port of the GNU C compiler to
+Windows, and the Mingw32CCompiler class which handles the mingw32 port
+of GCC (same as cygwin in no-cygwin mode).
+"""
+
+# problems:
+#
+# * if you use a msvc compiled python version (1.5.2)
+# 1. you have to insert a __GNUC__ section in its config.h
+# 2. you have to generate a import library for its dll
+# - create a def-file for python??.dll
+# - create a import library using
+# dlltool --dllname python15.dll --def python15.def \
+# --output-lib libpython15.a
+#
+# see also http://starship.python.net/crew/kernr/mingw32/Notes.html
+#
+# * We put export_symbols in a def-file, and don't use
+# --export-all-symbols because it doesn't worked reliable in some
+# tested configurations. And because other windows compilers also
+# need their symbols specified this no serious problem.
+#
+# tested configurations:
+#
+# * cygwin gcc 2.91.57/ld 2.9.4/dllwrap 0.2.4 works
+# (after patching python's config.h and for C++ some other include files)
+# see also http://starship.python.net/crew/kernr/mingw32/Notes.html
+# * mingw32 gcc 2.95.2/ld 2.9.4/dllwrap 0.2.4 works
+# (ld doesn't support -shared, so we use dllwrap)
+# * cygwin gcc 2.95.2/ld 2.10.90/dllwrap 2.10.90 works now
+# - its dllwrap doesn't work, there is a bug in binutils 2.10.90
+# see also http://sources.redhat.com/ml/cygwin/2000-06/msg01274.html
+# - using gcc -mdll instead dllwrap doesn't work without -static because
+# it tries to link against dlls instead their import libraries. (If
+# it finds the dll first.)
+# By specifying -static we force ld to link against the import libraries,
+# this is windows standard and there are normally not the necessary symbols
+# in the dlls.
+# *** only the version of June 2000 shows these problems
+# * cygwin gcc 3.2/ld 2.13.90 works
+# (ld supports -shared)
+# * mingw gcc 3.2/ld 2.13 works
+# (ld supports -shared)
+
+
+import os
+import sys
+
+from packaging import logger
+from packaging.compiler.unixccompiler import UnixCCompiler
+from packaging.util import write_file
+from packaging.errors import PackagingExecError, CompileError, UnknownFileError
+from packaging.util import get_compiler_versions
+import sysconfig
+
+
+def get_msvcr():
+ """Include the appropriate MSVC runtime library if Python was built
+ with MSVC 7.0 or later.
+ """
+ msc_pos = sys.version.find('MSC v.')
+ if msc_pos != -1:
+ msc_ver = sys.version[msc_pos+6:msc_pos+10]
+ if msc_ver == '1300':
+ # MSVC 7.0
+ return ['msvcr70']
+ elif msc_ver == '1310':
+ # MSVC 7.1
+ return ['msvcr71']
+ elif msc_ver == '1400':
+ # VS2005 / MSVC 8.0
+ return ['msvcr80']
+ elif msc_ver == '1500':
+ # VS2008 / MSVC 9.0
+ return ['msvcr90']
+ else:
+ raise ValueError("Unknown MS Compiler version %s " % msc_ver)
+
+
+class CygwinCCompiler(UnixCCompiler):
+ """ Handles the Cygwin port of the GNU C compiler to Windows.
+ """
+ name = 'cygwin'
+ description = 'Cygwin port of GNU C Compiler for Win32'
+ obj_extension = ".o"
+ static_lib_extension = ".a"
+ shared_lib_extension = ".dll"
+ static_lib_format = "lib%s%s"
+ shared_lib_format = "%s%s"
+ exe_extension = ".exe"
+
+ def __init__(self, dry_run=False, force=False):
+ super(CygwinCCompiler, self).__init__(dry_run, force)
+
+ status, details = check_config_h()
+ logger.debug("Python's GCC status: %s (details: %s)", status, details)
+ if status is not CONFIG_H_OK:
+ self.warn(
+ "Python's pyconfig.h doesn't seem to support your compiler. "
+ "Reason: %s. "
+ "Compiling may fail because of undefined preprocessor macros."
+ % details)
+
+ self.gcc_version, self.ld_version, self.dllwrap_version = \
+ get_compiler_versions()
+ logger.debug(self.name + ": gcc %s, ld %s, dllwrap %s\n",
+ self.gcc_version,
+ self.ld_version,
+ self.dllwrap_version)
+
+ # ld_version >= "2.10.90" and < "2.13" should also be able to use
+ # gcc -mdll instead of dllwrap
+ # Older dllwraps had own version numbers, newer ones use the
+ # same as the rest of binutils ( also ld )
+ # dllwrap 2.10.90 is buggy
+ if self.ld_version >= "2.10.90":
+ self.linker_dll = "gcc"
+ else:
+ self.linker_dll = "dllwrap"
+
+ # ld_version >= "2.13" support -shared so use it instead of
+ # -mdll -static
+ if self.ld_version >= "2.13":
+ shared_option = "-shared"
+ else:
+ shared_option = "-mdll -static"
+
+ # Hard-code GCC because that's what this is all about.
+ # XXX optimization, warnings etc. should be customizable.
+ self.set_executables(compiler='gcc -mcygwin -O -Wall',
+ compiler_so='gcc -mcygwin -mdll -O -Wall',
+ compiler_cxx='g++ -mcygwin -O -Wall',
+ linker_exe='gcc -mcygwin',
+ linker_so=('%s -mcygwin %s' %
+ (self.linker_dll, shared_option)))
+
+ # cygwin and mingw32 need different sets of libraries
+ if self.gcc_version == "2.91.57":
+ # cygwin shouldn't need msvcrt, but without the dlls will crash
+ # (gcc version 2.91.57) -- perhaps something about initialization
+ self.dll_libraries=["msvcrt"]
+ self.warn(
+ "Consider upgrading to a newer version of gcc")
+ else:
+ # Include the appropriate MSVC runtime library if Python was built
+ # with MSVC 7.0 or later.
+ self.dll_libraries = get_msvcr()
+
+ def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
+ """Compile the source by spawning GCC and windres if needed."""
+ if ext == '.rc' or ext == '.res':
+ # gcc needs '.res' and '.rc' compiled to object files !!!
+ try:
+ self.spawn(["windres", "-i", src, "-o", obj])
+ except PackagingExecError as msg:
+ raise CompileError(msg)
+ else: # for other files use the C-compiler
+ try:
+ self.spawn(self.compiler_so + cc_args + [src, '-o', obj] +
+ extra_postargs)
+ except PackagingExecError as msg:
+ raise CompileError(msg)
+
+ def link(self, target_desc, objects, output_filename, output_dir=None,
+ libraries=None, library_dirs=None, runtime_library_dirs=None,
+ export_symbols=None, debug=False, extra_preargs=None,
+ extra_postargs=None, build_temp=None, target_lang=None):
+ """Link the objects."""
+ # use separate copies, so we can modify the lists
+ extra_preargs = list(extra_preargs or [])
+ libraries = list(libraries or [])
+ objects = list(objects or [])
+
+ # Additional libraries
+ libraries.extend(self.dll_libraries)
+
+ # handle export symbols by creating a def-file
+ # with executables this only works with gcc/ld as linker
+ if ((export_symbols is not None) and
+ (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")):
+ # (The linker doesn't do anything if output is up-to-date.
+ # So it would probably better to check if we really need this,
+ # but for this we had to insert some unchanged parts of
+ # UnixCCompiler, and this is not what we want.)
+
+ # we want to put some files in the same directory as the
+ # object files are, build_temp doesn't help much
+ # where are the object files
+ temp_dir = os.path.dirname(objects[0])
+ # name of dll to give the helper files the same base name
+ dll_name, dll_extension = os.path.splitext(
+ os.path.basename(output_filename))
+
+ # generate the filenames for these files
+ def_file = os.path.join(temp_dir, dll_name + ".def")
+ lib_file = os.path.join(temp_dir, 'lib' + dll_name + ".a")
+
+ # Generate .def file
+ contents = [
+ "LIBRARY %s" % os.path.basename(output_filename),
+ "EXPORTS"]
+ for sym in export_symbols:
+ contents.append(sym)
+ self.execute(write_file, (def_file, contents),
+ "writing %s" % def_file)
+
+ # next add options for def-file and to creating import libraries
+
+ # dllwrap uses different options than gcc/ld
+ if self.linker_dll == "dllwrap":
+ extra_preargs.extend(("--output-lib", lib_file))
+ # for dllwrap we have to use a special option
+ extra_preargs.extend(("--def", def_file))
+ # we use gcc/ld here and can be sure ld is >= 2.9.10
+ else:
+ # doesn't work: bfd_close build\...\libfoo.a: Invalid operation
+ #extra_preargs.extend(("-Wl,--out-implib,%s" % lib_file))
+ # for gcc/ld the def-file is specified as any object files
+ objects.append(def_file)
+
+ #end: if ((export_symbols is not None) and
+ # (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")):
+
+ # who wants symbols and a many times larger output file
+ # should explicitly switch the debug mode on
+ # otherwise we let dllwrap/ld strip the output file
+ # (On my machine: 10KB < stripped_file < ??100KB
+ # unstripped_file = stripped_file + XXX KB
+ # ( XXX=254 for a typical python extension))
+ if not debug:
+ extra_preargs.append("-s")
+
+ super(CygwinCCompiler, self).link(
+ target_desc, objects, output_filename, output_dir, libraries,
+ library_dirs, runtime_library_dirs,
+ None, # export_symbols, we do this in our def-file
+ debug, extra_preargs, extra_postargs, build_temp, target_lang)
+
+ # -- Miscellaneous methods -----------------------------------------
+
+ def object_filenames(self, source_filenames, strip_dir=False,
+ output_dir=''):
+ """Adds supports for rc and res files."""
+ if output_dir is None:
+ output_dir = ''
+ obj_names = []
+ for src_name in source_filenames:
+ # use normcase to make sure '.rc' is really '.rc' and not '.RC'
+ base, ext = os.path.splitext(os.path.normcase(src_name))
+ if ext not in (self.src_extensions + ['.rc','.res']):
+ raise UnknownFileError("unknown file type '%s' (from '%s')" % (ext, src_name))
+ if strip_dir:
+ base = os.path.basename(base)
+ if ext in ('.res', '.rc'):
+ # these need to be compiled to object files
+ obj_names.append(os.path.join(output_dir,
+ base + ext + self.obj_extension))
+ else:
+ obj_names.append(os.path.join(output_dir,
+ base + self.obj_extension))
+ return obj_names
+
+# the same as cygwin plus some additional parameters
+class Mingw32CCompiler(CygwinCCompiler):
+ """ Handles the Mingw32 port of the GNU C compiler to Windows.
+ """
+ name = 'mingw32'
+ description = 'MinGW32 compiler'
+
+ def __init__(self, dry_run=False, force=False):
+ super(Mingw32CCompiler, self).__init__(dry_run, force)
+
+ # ld_version >= "2.13" support -shared so use it instead of
+ # -mdll -static
+ if self.ld_version >= "2.13":
+ shared_option = "-shared"
+ else:
+ shared_option = "-mdll -static"
+
+ # A real mingw32 doesn't need to specify a different entry point,
+ # but cygwin 2.91.57 in no-cygwin-mode needs it.
+ if self.gcc_version <= "2.91.57":
+ entry_point = '--entry _DllMain@12'
+ else:
+ entry_point = ''
+
+ self.set_executables(compiler='gcc -mno-cygwin -O -Wall',
+ compiler_so='gcc -mno-cygwin -mdll -O -Wall',
+ compiler_cxx='g++ -mno-cygwin -O -Wall',
+ linker_exe='gcc -mno-cygwin',
+ linker_so='%s -mno-cygwin %s %s'
+ % (self.linker_dll, shared_option,
+ entry_point))
+ # Maybe we should also append -mthreads, but then the finished
+ # dlls need another dll (mingwm10.dll see Mingw32 docs)
+ # (-mthreads: Support thread-safe exception handling on `Mingw32')
+
+ # no additional libraries needed
+ self.dll_libraries=[]
+
+ # Include the appropriate MSVC runtime library if Python was built
+ # with MSVC 7.0 or later.
+ self.dll_libraries = get_msvcr()
+
+# Because these compilers aren't configured in Python's pyconfig.h file by
+# default, we should at least warn the user if he is using a unmodified
+# version.
+
+CONFIG_H_OK = "ok"
+CONFIG_H_NOTOK = "not ok"
+CONFIG_H_UNCERTAIN = "uncertain"
+
+def check_config_h():
+ """Check if the current Python installation appears amenable to building
+ extensions with GCC.
+
+ Returns a tuple (status, details), where 'status' is one of the following
+ constants:
+
+ - CONFIG_H_OK: all is well, go ahead and compile
+ - CONFIG_H_NOTOK: doesn't look good
+ - CONFIG_H_UNCERTAIN: not sure -- unable to read pyconfig.h
+
+ 'details' is a human-readable string explaining the situation.
+
+ Note there are two ways to conclude "OK": either 'sys.version' contains
+ the string "GCC" (implying that this Python was built with GCC), or the
+ installed "pyconfig.h" contains the string "__GNUC__".
+ """
+
+ # XXX since this function also checks sys.version, it's not strictly a
+ # "pyconfig.h" check -- should probably be renamed...
+ # if sys.version contains GCC then python was compiled with GCC, and the
+ # pyconfig.h file should be OK
+ if "GCC" in sys.version:
+ return CONFIG_H_OK, "sys.version mentions 'GCC'"
+
+ # let's see if __GNUC__ is mentioned in python.h
+ fn = sysconfig.get_config_h_filename()
+ try:
+ with open(fn) as config_h:
+ if "__GNUC__" in config_h.read():
+ return CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn
+ else:
+ return CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn
+ except IOError as exc:
+ return (CONFIG_H_UNCERTAIN,
+ "couldn't read '%s': %s" % (fn, exc.strerror))
diff --git a/Lib/packaging/compiler/extension.py b/Lib/packaging/compiler/extension.py
new file mode 100644
index 0000000..66f6e9a
--- /dev/null
+++ b/Lib/packaging/compiler/extension.py
@@ -0,0 +1,121 @@
+"""Class representing C/C++ extension modules."""
+
+from packaging import logger
+
+# This class is really only used by the "build_ext" command, so it might
+# make sense to put it in distutils.command.build_ext. However, that
+# module is already big enough, and I want to make this class a bit more
+# complex to simplify some common cases ("foo" module in "foo.c") and do
+# better error-checking ("foo.c" actually exists).
+#
+# Also, putting this in build_ext.py means every setup script would have to
+# import that large-ish module (indirectly, through distutils.core) in
+# order to do anything.
+
+
+class Extension:
+ """Just a collection of attributes that describes an extension
+ module and everything needed to build it (hopefully in a portable
+ way, but there are hooks that let you be as unportable as you need).
+
+ Instance attributes:
+ name : string
+ the full name of the extension, including any packages -- ie.
+ *not* a filename or pathname, but Python dotted name
+ sources : [string]
+ list of source filenames, relative to the distribution root
+ (where the setup script lives), in Unix form (slash-separated)
+ for portability. Source files may be C, C++, SWIG (.i),
+ platform-specific resource files, or whatever else is recognized
+ by the "build_ext" command as source for a Python extension.
+ include_dirs : [string]
+ list of directories to search for C/C++ header files (in Unix
+ form for portability)
+ define_macros : [(name : string, value : string|None)]
+ list of macros to define; each macro is defined using a 2-tuple,
+ where 'value' is either the string to define it to or None to
+ define it without a particular value (equivalent of "#define
+ FOO" in source or -DFOO on Unix C compiler command line)
+ undef_macros : [string]
+ list of macros to undefine explicitly
+ library_dirs : [string]
+ list of directories to search for C/C++ libraries at link time
+ libraries : [string]
+ list of library names (not filenames or paths) to link against
+ runtime_library_dirs : [string]
+ list of directories to search for C/C++ libraries at run time
+ (for shared extensions, this is when the extension is loaded)
+ extra_objects : [string]
+ list of extra files to link with (eg. object files not implied
+ by 'sources', static library that must be explicitly specified,
+ binary resource files, etc.)
+ extra_compile_args : [string]
+ any extra platform- and compiler-specific information to use
+ when compiling the source files in 'sources'. For platforms and
+ compilers where "command line" makes sense, this is typically a
+ list of command-line arguments, but for other platforms it could
+ be anything.
+ extra_link_args : [string]
+ any extra platform- and compiler-specific information to use
+ when linking object files together to create the extension (or
+ to create a new static Python interpreter). Similar
+ interpretation as for 'extra_compile_args'.
+ export_symbols : [string]
+ list of symbols to be exported from a shared extension. Not
+ used on all platforms, and not generally necessary for Python
+ extensions, which typically export exactly one symbol: "init" +
+ extension_name.
+ swig_opts : [string]
+ any extra options to pass to SWIG if a source file has the .i
+ extension.
+ depends : [string]
+ list of files that the extension depends on
+ language : string
+ extension language (i.e. "c", "c++", "objc"). Will be detected
+ from the source extensions if not provided.
+ optional : boolean
+ specifies that a build failure in the extension should not abort the
+ build process, but simply not install the failing extension.
+ """
+
+ # **kwargs are allowed so that a warning is emitted instead of an
+ # exception
+ def __init__(self, name, sources, include_dirs=None, define_macros=None,
+ undef_macros=None, library_dirs=None, libraries=None,
+ runtime_library_dirs=None, extra_objects=None,
+ extra_compile_args=None, extra_link_args=None,
+ export_symbols=None, swig_opts=None, depends=None,
+ language=None, optional=None, **kw):
+ if not isinstance(name, str):
+ raise AssertionError("'name' must be a string")
+
+ if not isinstance(sources, list):
+ raise AssertionError("'sources' must be a list of strings")
+
+ for v in sources:
+ if not isinstance(v, str):
+ raise AssertionError("'sources' must be a list of strings")
+
+ self.name = name
+ self.sources = sources
+ self.include_dirs = include_dirs or []
+ self.define_macros = define_macros or []
+ self.undef_macros = undef_macros or []
+ self.library_dirs = library_dirs or []
+ self.libraries = libraries or []
+ self.runtime_library_dirs = runtime_library_dirs or []
+ self.extra_objects = extra_objects or []
+ self.extra_compile_args = extra_compile_args or []
+ self.extra_link_args = extra_link_args or []
+ self.export_symbols = export_symbols or []
+ self.swig_opts = swig_opts or []
+ self.depends = depends or []
+ self.language = language
+ self.optional = optional
+
+ # If there are unknown keyword options, warn about them
+ if len(kw) > 0:
+ options = [repr(option) for option in kw]
+ options = ', '.join(sorted(options))
+ logger.warning(
+ 'unknown arguments given to Extension: %s', options)
diff --git a/Lib/packaging/compiler/msvc9compiler.py b/Lib/packaging/compiler/msvc9compiler.py
new file mode 100644
index 0000000..029aa77
--- /dev/null
+++ b/Lib/packaging/compiler/msvc9compiler.py
@@ -0,0 +1,720 @@
+"""CCompiler implementation for the Microsoft Visual Studio 2008 compiler.
+
+The MSVCCompiler class is compatible with VS 2005 and VS 2008. Legacy
+support for older versions of VS are in the msvccompiler module.
+"""
+
+# Written by Perry Stoll
+# hacked by Robin Becker and Thomas Heller to do a better job of
+# finding DevStudio (through the registry)
+# ported to VS2005 and VS 2008 by Christian Heimes
+import os
+import subprocess
+import sys
+import re
+
+from packaging.errors import (PackagingExecError, PackagingPlatformError,
+ CompileError, LibError, LinkError)
+from packaging.compiler.ccompiler import CCompiler
+from packaging.compiler import gen_lib_options
+from packaging import logger
+from packaging.util import get_platform
+
+import winreg
+
+RegOpenKeyEx = winreg.OpenKeyEx
+RegEnumKey = winreg.EnumKey
+RegEnumValue = winreg.EnumValue
+RegError = winreg.error
+
+HKEYS = (winreg.HKEY_USERS,
+ winreg.HKEY_CURRENT_USER,
+ winreg.HKEY_LOCAL_MACHINE,
+ winreg.HKEY_CLASSES_ROOT)
+
+VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f"
+WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows"
+NET_BASE = r"Software\Microsoft\.NETFramework"
+
+# A map keyed by get_platform() return values to values accepted by
+# 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is
+# the param to cross-compile on x86 targetting amd64.)
+PLAT_TO_VCVARS = {
+ 'win32' : 'x86',
+ 'win-amd64' : 'amd64',
+ 'win-ia64' : 'ia64',
+}
+
+
+class Reg:
+ """Helper class to read values from the registry
+ """
+
+ def get_value(cls, path, key):
+ for base in HKEYS:
+ d = cls.read_values(base, path)
+ if d and key in d:
+ return d[key]
+ raise KeyError(key)
+ get_value = classmethod(get_value)
+
+ def read_keys(cls, base, key):
+ """Return list of registry keys."""
+ try:
+ handle = RegOpenKeyEx(base, key)
+ except RegError:
+ return None
+ L = []
+ i = 0
+ while True:
+ try:
+ k = RegEnumKey(handle, i)
+ except RegError:
+ break
+ L.append(k)
+ i += 1
+ return L
+ read_keys = classmethod(read_keys)
+
+ def read_values(cls, base, key):
+ """Return dict of registry keys and values.
+
+ All names are converted to lowercase.
+ """
+ try:
+ handle = RegOpenKeyEx(base, key)
+ except RegError:
+ return None
+ d = {}
+ i = 0
+ while True:
+ try:
+ name, value, type = RegEnumValue(handle, i)
+ except RegError:
+ break
+ name = name.lower()
+ d[cls.convert_mbcs(name)] = cls.convert_mbcs(value)
+ i += 1
+ return d
+ read_values = classmethod(read_values)
+
+ def convert_mbcs(s):
+ dec = getattr(s, "decode", None)
+ if dec is not None:
+ try:
+ s = dec("mbcs")
+ except UnicodeError:
+ pass
+ return s
+ convert_mbcs = staticmethod(convert_mbcs)
+
+class MacroExpander:
+
+ def __init__(self, version):
+ self.macros = {}
+ self.vsbase = VS_BASE % version
+ self.load_macros(version)
+
+ def set_macro(self, macro, path, key):
+ self.macros["$(%s)" % macro] = Reg.get_value(path, key)
+
+ def load_macros(self, version):
+ self.set_macro("VCInstallDir", self.vsbase + r"\Setup\VC", "productdir")
+ self.set_macro("VSInstallDir", self.vsbase + r"\Setup\VS", "productdir")
+ self.set_macro("FrameworkDir", NET_BASE, "installroot")
+ try:
+ if version >= 8.0:
+ self.set_macro("FrameworkSDKDir", NET_BASE,
+ "sdkinstallrootv2.0")
+ else:
+ raise KeyError("sdkinstallrootv2.0")
+ except KeyError:
+ raise PackagingPlatformError(
+"""Python was built with Visual Studio 2008; extensions must be built with a
+compiler than can generate compatible binaries. Visual Studio 2008 was not
+found on this system. If you have Cygwin installed, you can try compiling
+with MingW32, by passing "-c mingw32" to pysetup.""")
+
+ if version >= 9.0:
+ self.set_macro("FrameworkVersion", self.vsbase, "clr version")
+ self.set_macro("WindowsSdkDir", WINSDK_BASE, "currentinstallfolder")
+ else:
+ p = r"Software\Microsoft\NET Framework Setup\Product"
+ for base in HKEYS:
+ try:
+ h = RegOpenKeyEx(base, p)
+ except RegError:
+ continue
+ key = RegEnumKey(h, 0)
+ d = Reg.get_value(base, r"%s\%s" % (p, key))
+ self.macros["$(FrameworkVersion)"] = d["version"]
+
+ def sub(self, s):
+ for k, v in self.macros.items():
+ s = s.replace(k, v)
+ return s
+
+def get_build_version():
+ """Return the version of MSVC that was used to build Python.
+
+ For Python 2.3 and up, the version number is included in
+ sys.version. For earlier versions, assume the compiler is MSVC 6.
+ """
+ prefix = "MSC v."
+ i = sys.version.find(prefix)
+ if i == -1:
+ return 6
+ i = i + len(prefix)
+ s, rest = sys.version[i:].split(" ", 1)
+ majorVersion = int(s[:-2]) - 6
+ minorVersion = int(s[2:3]) / 10.0
+ # I don't think paths are affected by minor version in version 6
+ if majorVersion == 6:
+ minorVersion = 0
+ if majorVersion >= 6:
+ return majorVersion + minorVersion
+ # else we don't know what version of the compiler this is
+ return None
+
+def normalize_and_reduce_paths(paths):
+ """Return a list of normalized paths with duplicates removed.
+
+ The current order of paths is maintained.
+ """
+ # Paths are normalized so things like: /a and /a/ aren't both preserved.
+ reduced_paths = []
+ for p in paths:
+ np = os.path.normpath(p)
+ # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set.
+ if np not in reduced_paths:
+ reduced_paths.append(np)
+ return reduced_paths
+
+def removeDuplicates(variable):
+ """Remove duplicate values of an environment variable.
+ """
+ oldList = variable.split(os.pathsep)
+ newList = []
+ for i in oldList:
+ if i not in newList:
+ newList.append(i)
+ newVariable = os.pathsep.join(newList)
+ return newVariable
+
+def find_vcvarsall(version):
+ """Find the vcvarsall.bat file
+
+ At first it tries to find the productdir of VS 2008 in the registry. If
+ that fails it falls back to the VS90COMNTOOLS env var.
+ """
+ vsbase = VS_BASE % version
+ try:
+ productdir = Reg.get_value(r"%s\Setup\VC" % vsbase,
+ "productdir")
+ except KeyError:
+ logger.debug("Unable to find productdir in registry")
+ productdir = None
+
+ if not productdir or not os.path.isdir(productdir):
+ toolskey = "VS%0.f0COMNTOOLS" % version
+ toolsdir = os.environ.get(toolskey, None)
+
+ if toolsdir and os.path.isdir(toolsdir):
+ productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC")
+ productdir = os.path.abspath(productdir)
+ if not os.path.isdir(productdir):
+ logger.debug("%s is not a valid directory", productdir)
+ return None
+ else:
+ logger.debug("env var %s is not set or invalid", toolskey)
+ if not productdir:
+ logger.debug("no productdir found")
+ return None
+ vcvarsall = os.path.join(productdir, "vcvarsall.bat")
+ if os.path.isfile(vcvarsall):
+ return vcvarsall
+ logger.debug("unable to find vcvarsall.bat")
+ return None
+
+def query_vcvarsall(version, arch="x86"):
+ """Launch vcvarsall.bat and read the settings from its environment
+ """
+ vcvarsall = find_vcvarsall(version)
+ interesting = set(("include", "lib", "libpath", "path"))
+ result = {}
+
+ if vcvarsall is None:
+ raise PackagingPlatformError("Unable to find vcvarsall.bat")
+ logger.debug("calling 'vcvarsall.bat %s' (version=%s)", arch, version)
+ popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch),
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+
+ stdout, stderr = popen.communicate()
+ if popen.wait() != 0:
+ raise PackagingPlatformError(stderr.decode("mbcs"))
+
+ stdout = stdout.decode("mbcs")
+ for line in stdout.split("\n"):
+ line = Reg.convert_mbcs(line)
+ if '=' not in line:
+ continue
+ line = line.strip()
+ key, value = line.split('=', 1)
+ key = key.lower()
+ if key in interesting:
+ if value.endswith(os.pathsep):
+ value = value[:-1]
+ result[key] = removeDuplicates(value)
+
+ if len(result) != len(interesting):
+ raise ValueError(str(list(result)))
+
+ return result
+
+# More globals
+VERSION = get_build_version()
+if VERSION < 8.0:
+ raise PackagingPlatformError("VC %0.1f is not supported by this module" % VERSION)
+# MACROS = MacroExpander(VERSION)
+
+class MSVCCompiler(CCompiler) :
+ """Concrete class that implements an interface to Microsoft Visual C++,
+ as defined by the CCompiler abstract class."""
+
+ name = 'msvc'
+ description = 'Microsoft Visual C++'
+
+ # Just set this so CCompiler's constructor doesn't barf. We currently
+ # don't use the 'set_executables()' bureaucracy provided by CCompiler,
+ # as it really isn't necessary for this sort of single-compiler class.
+ # Would be nice to have a consistent interface with UnixCCompiler,
+ # though, so it's worth thinking about.
+ executables = {}
+
+ # Private class data (need to distinguish C from C++ source for compiler)
+ _c_extensions = ['.c']
+ _cpp_extensions = ['.cc', '.cpp', '.cxx']
+ _rc_extensions = ['.rc']
+ _mc_extensions = ['.mc']
+
+ # Needed for the filename generation methods provided by the
+ # base class, CCompiler.
+ src_extensions = (_c_extensions + _cpp_extensions +
+ _rc_extensions + _mc_extensions)
+ res_extension = '.res'
+ obj_extension = '.obj'
+ static_lib_extension = '.lib'
+ shared_lib_extension = '.dll'
+ static_lib_format = shared_lib_format = '%s%s'
+ exe_extension = '.exe'
+
+ def __init__(self, dry_run=False, force=False):
+ super(MSVCCompiler, self).__init__(dry_run, force)
+ self.__version = VERSION
+ self.__root = r"Software\Microsoft\VisualStudio"
+ # self.__macros = MACROS
+ self.__paths = []
+ # target platform (.plat_name is consistent with 'bdist')
+ self.plat_name = None
+ self.__arch = None # deprecated name
+ self.initialized = False
+
+ def initialize(self, plat_name=None):
+ # multi-init means we would need to check platform same each time...
+ assert not self.initialized, "don't init multiple times"
+ if plat_name is None:
+ plat_name = get_platform()
+ # sanity check for platforms to prevent obscure errors later.
+ ok_plats = 'win32', 'win-amd64', 'win-ia64'
+ if plat_name not in ok_plats:
+ raise PackagingPlatformError("--plat-name must be one of %s" %
+ (ok_plats,))
+
+ if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"):
+ # Assume that the SDK set up everything alright; don't try to be
+ # smarter
+ self.cc = "cl.exe"
+ self.linker = "link.exe"
+ self.lib = "lib.exe"
+ self.rc = "rc.exe"
+ self.mc = "mc.exe"
+ else:
+ # On x86, 'vcvars32.bat amd64' creates an env that doesn't work;
+ # to cross compile, you use 'x86_amd64'.
+ # On AMD64, 'vcvars32.bat amd64' is a native build env; to cross
+ # compile use 'x86' (ie, it runs the x86 compiler directly)
+ # No idea how itanium handles this, if at all.
+ if plat_name == get_platform() or plat_name == 'win32':
+ # native build or cross-compile to win32
+ plat_spec = PLAT_TO_VCVARS[plat_name]
+ else:
+ # cross compile from win32 -> some 64bit
+ plat_spec = PLAT_TO_VCVARS[get_platform()] + '_' + \
+ PLAT_TO_VCVARS[plat_name]
+
+ vc_env = query_vcvarsall(VERSION, plat_spec)
+
+ # take care to only use strings in the environment.
+ self.__paths = vc_env['path'].split(os.pathsep)
+ os.environ['lib'] = vc_env['lib']
+ os.environ['include'] = vc_env['include']
+
+ if len(self.__paths) == 0:
+ raise PackagingPlatformError("Python was built with %s, "
+ "and extensions need to be built with the same "
+ "version of the compiler, but it isn't installed."
+ % self.__product)
+
+ self.cc = self.find_exe("cl.exe")
+ self.linker = self.find_exe("link.exe")
+ self.lib = self.find_exe("lib.exe")
+ self.rc = self.find_exe("rc.exe") # resource compiler
+ self.mc = self.find_exe("mc.exe") # message compiler
+ #self.set_path_env_var('lib')
+ #self.set_path_env_var('include')
+
+ # extend the MSVC path with the current path
+ try:
+ for p in os.environ['path'].split(';'):
+ self.__paths.append(p)
+ except KeyError:
+ pass
+ self.__paths = normalize_and_reduce_paths(self.__paths)
+ os.environ['path'] = ";".join(self.__paths)
+
+ self.preprocess_options = None
+ if self.__arch == "x86":
+ self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3',
+ '/DNDEBUG']
+ self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3',
+ '/Z7', '/D_DEBUG']
+ else:
+ # Win64
+ self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' ,
+ '/DNDEBUG']
+ self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-',
+ '/Z7', '/D_DEBUG']
+
+ self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO']
+ if self.__version >= 7:
+ self.ldflags_shared_debug = [
+ '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG', '/pdb:None'
+ ]
+ self.ldflags_static = [ '/nologo']
+
+ self.initialized = True
+
+ # -- Worker methods ------------------------------------------------
+
+ def object_filenames(self,
+ source_filenames,
+ strip_dir=False,
+ output_dir=''):
+ # Copied from ccompiler.py, extended to return .res as 'object'-file
+ # for .rc input file
+ if output_dir is None: output_dir = ''
+ obj_names = []
+ for src_name in source_filenames:
+ base, ext = os.path.splitext(src_name)
+ base = os.path.splitdrive(base)[1] # Chop off the drive
+ base = base[os.path.isabs(base):] # If abs, chop off leading /
+ if ext not in self.src_extensions:
+ # Better to raise an exception instead of silently continuing
+ # and later complain about sources and targets having
+ # different lengths
+ raise CompileError("Don't know how to compile %s" % src_name)
+ if strip_dir:
+ base = os.path.basename(base)
+ if ext in self._rc_extensions:
+ obj_names.append(os.path.join(output_dir,
+ base + self.res_extension))
+ elif ext in self._mc_extensions:
+ obj_names.append(os.path.join(output_dir,
+ base + self.res_extension))
+ else:
+ obj_names.append(os.path.join(output_dir,
+ base + self.obj_extension))
+ return obj_names
+
+
+ def compile(self, sources,
+ output_dir=None, macros=None, include_dirs=None, debug=False,
+ extra_preargs=None, extra_postargs=None, depends=None):
+
+ if not self.initialized:
+ self.initialize()
+ compile_info = self._setup_compile(output_dir, macros, include_dirs,
+ sources, depends, extra_postargs)
+ macros, objects, extra_postargs, pp_opts, build = compile_info
+
+ compile_opts = extra_preargs or []
+ compile_opts.append('/c')
+ if debug:
+ compile_opts.extend(self.compile_options_debug)
+ else:
+ compile_opts.extend(self.compile_options)
+
+ for obj in objects:
+ try:
+ src, ext = build[obj]
+ except KeyError:
+ continue
+ if debug:
+ # pass the full pathname to MSVC in debug mode,
+ # this allows the debugger to find the source file
+ # without asking the user to browse for it
+ src = os.path.abspath(src)
+
+ if ext in self._c_extensions:
+ input_opt = "/Tc" + src
+ elif ext in self._cpp_extensions:
+ input_opt = "/Tp" + src
+ elif ext in self._rc_extensions:
+ # compile .RC to .RES file
+ input_opt = src
+ output_opt = "/fo" + obj
+ try:
+ self.spawn([self.rc] + pp_opts +
+ [output_opt] + [input_opt])
+ except PackagingExecError as msg:
+ raise CompileError(msg)
+ continue
+ elif ext in self._mc_extensions:
+ # Compile .MC to .RC file to .RES file.
+ # * '-h dir' specifies the directory for the
+ # generated include file
+ # * '-r dir' specifies the target directory of the
+ # generated RC file and the binary message resource
+ # it includes
+ #
+ # For now (since there are no options to change this),
+ # we use the source-directory for the include file and
+ # the build directory for the RC file and message
+ # resources. This works at least for win32all.
+ h_dir = os.path.dirname(src)
+ rc_dir = os.path.dirname(obj)
+ try:
+ # first compile .MC to .RC and .H file
+ self.spawn([self.mc] +
+ ['-h', h_dir, '-r', rc_dir] + [src])
+ base, _ = os.path.splitext(os.path.basename(src))
+ rc_file = os.path.join(rc_dir, base + '.rc')
+ # then compile .RC to .RES file
+ self.spawn([self.rc] +
+ ["/fo" + obj] + [rc_file])
+
+ except PackagingExecError as msg:
+ raise CompileError(msg)
+ continue
+ else:
+ # how to handle this file?
+ raise CompileError("Don't know how to compile %s to %s"
+ % (src, obj))
+
+ output_opt = "/Fo" + obj
+ try:
+ self.spawn([self.cc] + compile_opts + pp_opts +
+ [input_opt, output_opt] +
+ extra_postargs)
+ except PackagingExecError as msg:
+ raise CompileError(msg)
+
+ return objects
+
+
+ def create_static_lib(self,
+ objects,
+ output_libname,
+ output_dir=None,
+ debug=False,
+ target_lang=None):
+
+ if not self.initialized:
+ self.initialize()
+ objects, output_dir = self._fix_object_args(objects, output_dir)
+ output_filename = self.library_filename(output_libname,
+ output_dir=output_dir)
+
+ if self._need_link(objects, output_filename):
+ lib_args = objects + ['/OUT:' + output_filename]
+ if debug:
+ pass # XXX what goes here?
+ try:
+ self.spawn([self.lib] + lib_args)
+ except PackagingExecError as msg:
+ raise LibError(msg)
+ else:
+ logger.debug("skipping %s (up-to-date)", output_filename)
+
+
+ def link(self, target_desc, objects, output_filename, output_dir=None,
+ libraries=None, library_dirs=None, runtime_library_dirs=None,
+ export_symbols=None, debug=False, extra_preargs=None,
+ extra_postargs=None, build_temp=None, target_lang=None):
+ if not self.initialized:
+ self.initialize()
+ objects, output_dir = self._fix_object_args(objects, output_dir)
+ fixed_args = self._fix_lib_args(libraries, library_dirs,
+ runtime_library_dirs)
+ libraries, library_dirs, runtime_library_dirs = fixed_args
+
+ if runtime_library_dirs:
+ self.warn("don't know what to do with 'runtime_library_dirs': "
+ + str(runtime_library_dirs))
+
+ lib_opts = gen_lib_options(self,
+ library_dirs, runtime_library_dirs,
+ libraries)
+ if output_dir is not None:
+ output_filename = os.path.join(output_dir, output_filename)
+
+ if self._need_link(objects, output_filename):
+ if target_desc == CCompiler.EXECUTABLE:
+ if debug:
+ ldflags = self.ldflags_shared_debug[1:]
+ else:
+ ldflags = self.ldflags_shared[1:]
+ else:
+ if debug:
+ ldflags = self.ldflags_shared_debug
+ else:
+ ldflags = self.ldflags_shared
+
+ export_opts = []
+ for sym in (export_symbols or []):
+ export_opts.append("/EXPORT:" + sym)
+
+ ld_args = (ldflags + lib_opts + export_opts +
+ objects + ['/OUT:' + output_filename])
+
+ # The MSVC linker generates .lib and .exp files, which cannot be
+ # suppressed by any linker switches. The .lib files may even be
+ # needed! Make sure they are generated in the temporary build
+ # directory. Since they have different names for debug and release
+ # builds, they can go into the same directory.
+ build_temp = os.path.dirname(objects[0])
+ if export_symbols is not None:
+ dll_name, dll_ext = os.path.splitext(
+ os.path.basename(output_filename))
+ implib_file = os.path.join(
+ build_temp,
+ self.library_filename(dll_name))
+ ld_args.append('/IMPLIB:' + implib_file)
+
+ # Embedded manifests are recommended - see MSDN article titled
+ # "How to: Embed a Manifest Inside a C/C++ Application"
+ # (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx)
+ # Ask the linker to generate the manifest in the temp dir, so
+ # we can embed it later.
+ temp_manifest = os.path.join(
+ build_temp,
+ os.path.basename(output_filename) + ".manifest")
+ ld_args.append('/MANIFESTFILE:' + temp_manifest)
+
+ if extra_preargs:
+ ld_args[:0] = extra_preargs
+ if extra_postargs:
+ ld_args.extend(extra_postargs)
+
+ self.mkpath(os.path.dirname(output_filename))
+ try:
+ self.spawn([self.linker] + ld_args)
+ except PackagingExecError as msg:
+ raise LinkError(msg)
+
+ # embed the manifest
+ # XXX - this is somewhat fragile - if mt.exe fails, distutils
+ # will still consider the DLL up-to-date, but it will not have a
+ # manifest. Maybe we should link to a temp file? OTOH, that
+ # implies a build environment error that shouldn't go undetected.
+ if target_desc == CCompiler.EXECUTABLE:
+ mfid = 1
+ else:
+ mfid = 2
+ self._remove_visual_c_ref(temp_manifest)
+ out_arg = '-outputresource:%s;%s' % (output_filename, mfid)
+ try:
+ self.spawn(['mt.exe', '-nologo', '-manifest',
+ temp_manifest, out_arg])
+ except PackagingExecError as msg:
+ raise LinkError(msg)
+ else:
+ logger.debug("skipping %s (up-to-date)", output_filename)
+
+ def _remove_visual_c_ref(self, manifest_file):
+ try:
+ # Remove references to the Visual C runtime, so they will
+ # fall through to the Visual C dependency of Python.exe.
+ # This way, when installed for a restricted user (e.g.
+ # runtimes are not in WinSxS folder, but in Python's own
+ # folder), the runtimes do not need to be in every folder
+ # with .pyd's.
+ with open(manifest_file) as manifest_f:
+ manifest_buf = manifest_f.read()
+ pattern = re.compile(
+ r"""<assemblyIdentity.*?name=("|')Microsoft\."""\
+ r"""VC\d{2}\.CRT("|').*?(/>|</assemblyIdentity>)""",
+ re.DOTALL)
+ manifest_buf = re.sub(pattern, "", manifest_buf)
+ pattern = "<dependentAssembly>\s*</dependentAssembly>"
+ manifest_buf = re.sub(pattern, "", manifest_buf)
+ with open(manifest_file, 'w') as manifest_f:
+ manifest_f.write(manifest_buf)
+ except IOError:
+ pass
+
+ # -- Miscellaneous methods -----------------------------------------
+ # These are all used by the 'gen_lib_options() function, in
+ # ccompiler.py.
+
+ def library_dir_option(self, dir):
+ return "/LIBPATH:" + dir
+
+ def runtime_library_dir_option(self, dir):
+ raise PackagingPlatformError(
+ "don't know how to set runtime library search path for MSVC++")
+
+ def library_option(self, lib):
+ return self.library_filename(lib)
+
+
+ def find_library_file(self, dirs, lib, debug=False):
+ # Prefer a debugging library if found (and requested), but deal
+ # with it if we don't have one.
+ if debug:
+ try_names = [lib + "_d", lib]
+ else:
+ try_names = [lib]
+ for dir in dirs:
+ for name in try_names:
+ libfile = os.path.join(dir, self.library_filename(name))
+ if os.path.exists(libfile):
+ return libfile
+ else:
+ # Oops, didn't find it in *any* of 'dirs'
+ return None
+
+ # Helper methods for using the MSVC registry settings
+
+ def find_exe(self, exe):
+ """Return path to an MSVC executable program.
+
+ Tries to find the program in several places: first, one of the
+ MSVC program search paths from the registry; next, the directories
+ in the PATH environment variable. If any of those work, return an
+ absolute path that is known to exist. If none of them work, just
+ return the original program name, 'exe'.
+ """
+ for p in self.__paths:
+ fn = os.path.join(os.path.abspath(p), exe)
+ if os.path.isfile(fn):
+ return fn
+
+ # didn't find it; try existing path
+ for p in os.environ['Path'].split(';'):
+ fn = os.path.join(os.path.abspath(p),exe)
+ if os.path.isfile(fn):
+ return fn
+
+ return exe
diff --git a/Lib/packaging/compiler/msvccompiler.py b/Lib/packaging/compiler/msvccompiler.py
new file mode 100644
index 0000000..39a10b2
--- /dev/null
+++ b/Lib/packaging/compiler/msvccompiler.py
@@ -0,0 +1,635 @@
+"""CCompiler implementation for old Microsoft Visual Studio compilers.
+
+For a compiler compatible with VS 2005 and 2008, use msvc9compiler.
+"""
+
+# Written by Perry Stoll
+# hacked by Robin Becker and Thomas Heller to do a better job of
+# finding DevStudio (through the registry)
+
+
+import sys
+import os
+
+from packaging.errors import (PackagingExecError, PackagingPlatformError,
+ CompileError, LibError, LinkError)
+from packaging.compiler.ccompiler import CCompiler
+from packaging.compiler import gen_lib_options
+from packaging import logger
+
+_can_read_reg = False
+try:
+ import winreg
+
+ _can_read_reg = True
+ hkey_mod = winreg
+
+ RegOpenKeyEx = winreg.OpenKeyEx
+ RegEnumKey = winreg.EnumKey
+ RegEnumValue = winreg.EnumValue
+ RegError = winreg.error
+
+except ImportError:
+ try:
+ import win32api
+ import win32con
+ _can_read_reg = True
+ hkey_mod = win32con
+
+ RegOpenKeyEx = win32api.RegOpenKeyEx
+ RegEnumKey = win32api.RegEnumKey
+ RegEnumValue = win32api.RegEnumValue
+ RegError = win32api.error
+
+ except ImportError:
+ logger.warning(
+ "can't read registry to find the necessary compiler setting;\n"
+ "make sure that Python modules _winreg, win32api or win32con "
+ "are installed.")
+
+if _can_read_reg:
+ HKEYS = (hkey_mod.HKEY_USERS,
+ hkey_mod.HKEY_CURRENT_USER,
+ hkey_mod.HKEY_LOCAL_MACHINE,
+ hkey_mod.HKEY_CLASSES_ROOT)
+
+
+def read_keys(base, key):
+ """Return list of registry keys."""
+
+ try:
+ handle = RegOpenKeyEx(base, key)
+ except RegError:
+ return None
+ L = []
+ i = 0
+ while True:
+ try:
+ k = RegEnumKey(handle, i)
+ except RegError:
+ break
+ L.append(k)
+ i = i + 1
+ return L
+
+
+def read_values(base, key):
+ """Return dict of registry keys and values.
+
+ All names are converted to lowercase.
+ """
+ try:
+ handle = RegOpenKeyEx(base, key)
+ except RegError:
+ return None
+ d = {}
+ i = 0
+ while True:
+ try:
+ name, value, type = RegEnumValue(handle, i)
+ except RegError:
+ break
+ name = name.lower()
+ d[convert_mbcs(name)] = convert_mbcs(value)
+ i = i + 1
+ return d
+
+
+def convert_mbcs(s):
+ enc = getattr(s, "encode", None)
+ if enc is not None:
+ try:
+ s = enc("mbcs")
+ except UnicodeError:
+ pass
+ return s
+
+
+class MacroExpander:
+
+ def __init__(self, version):
+ self.macros = {}
+ self.load_macros(version)
+
+ def set_macro(self, macro, path, key):
+ for base in HKEYS:
+ d = read_values(base, path)
+ if d:
+ self.macros["$(%s)" % macro] = d[key]
+ break
+
+ def load_macros(self, version):
+ vsbase = r"Software\Microsoft\VisualStudio\%0.1f" % version
+ self.set_macro("VCInstallDir", vsbase + r"\Setup\VC", "productdir")
+ self.set_macro("VSInstallDir", vsbase + r"\Setup\VS", "productdir")
+ net = r"Software\Microsoft\.NETFramework"
+ self.set_macro("FrameworkDir", net, "installroot")
+ try:
+ if version > 7.0:
+ self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1")
+ else:
+ self.set_macro("FrameworkSDKDir", net, "sdkinstallroot")
+ except KeyError:
+ raise PackagingPlatformError(
+"""Python was built with Visual Studio 2003; extensions must be built with
+a compiler than can generate compatible binaries. Visual Studio 2003 was
+not found on this system. If you have Cygwin installed, you can try
+compiling with MingW32, by passing "-c mingw32" to pysetup.""")
+
+ p = r"Software\Microsoft\NET Framework Setup\Product"
+ for base in HKEYS:
+ try:
+ h = RegOpenKeyEx(base, p)
+ except RegError:
+ continue
+ key = RegEnumKey(h, 0)
+ d = read_values(base, r"%s\%s" % (p, key))
+ self.macros["$(FrameworkVersion)"] = d["version"]
+
+ def sub(self, s):
+ for k, v in self.macros.items():
+ s = s.replace(k, v)
+ return s
+
+
+def get_build_version():
+ """Return the version of MSVC that was used to build Python.
+
+ For Python 2.3 and up, the version number is included in
+ sys.version. For earlier versions, assume the compiler is MSVC 6.
+ """
+
+ prefix = "MSC v."
+ i = sys.version.find(prefix)
+ if i == -1:
+ return 6
+ i = i + len(prefix)
+ s, rest = sys.version[i:].split(" ", 1)
+ majorVersion = int(s[:-2]) - 6
+ minorVersion = int(s[2:3]) / 10.0
+ # I don't think paths are affected by minor version in version 6
+ if majorVersion == 6:
+ minorVersion = 0
+ if majorVersion >= 6:
+ return majorVersion + minorVersion
+ # else we don't know what version of the compiler this is
+ return None
+
+
+def get_build_architecture():
+ """Return the processor architecture.
+
+ Possible results are "Intel", "Itanium", or "AMD64".
+ """
+
+ prefix = " bit ("
+ i = sys.version.find(prefix)
+ if i == -1:
+ return "Intel"
+ j = sys.version.find(")", i)
+ return sys.version[i+len(prefix):j]
+
+
+def normalize_and_reduce_paths(paths):
+ """Return a list of normalized paths with duplicates removed.
+
+ The current order of paths is maintained.
+ """
+ # Paths are normalized so things like: /a and /a/ aren't both preserved.
+ reduced_paths = []
+ for p in paths:
+ np = os.path.normpath(p)
+ # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set.
+ if np not in reduced_paths:
+ reduced_paths.append(np)
+ return reduced_paths
+
+
+class MSVCCompiler(CCompiler):
+ """Concrete class that implements an interface to Microsoft Visual C++,
+ as defined by the CCompiler abstract class."""
+
+ name = 'msvc'
+ description = "Microsoft Visual C++"
+
+ # Just set this so CCompiler's constructor doesn't barf. We currently
+ # don't use the 'set_executables()' bureaucracy provided by CCompiler,
+ # as it really isn't necessary for this sort of single-compiler class.
+ # Would be nice to have a consistent interface with UnixCCompiler,
+ # though, so it's worth thinking about.
+ executables = {}
+
+ # Private class data (need to distinguish C from C++ source for compiler)
+ _c_extensions = ['.c']
+ _cpp_extensions = ['.cc', '.cpp', '.cxx']
+ _rc_extensions = ['.rc']
+ _mc_extensions = ['.mc']
+
+ # Needed for the filename generation methods provided by the
+ # base class, CCompiler.
+ src_extensions = (_c_extensions + _cpp_extensions +
+ _rc_extensions + _mc_extensions)
+ res_extension = '.res'
+ obj_extension = '.obj'
+ static_lib_extension = '.lib'
+ shared_lib_extension = '.dll'
+ static_lib_format = shared_lib_format = '%s%s'
+ exe_extension = '.exe'
+
+ def __init__(self, dry_run=False, force=False):
+ super(MSVCCompiler, self).__init__(dry_run, force)
+ self.__version = get_build_version()
+ self.__arch = get_build_architecture()
+ if self.__arch == "Intel":
+ # x86
+ if self.__version >= 7:
+ self.__root = r"Software\Microsoft\VisualStudio"
+ self.__macros = MacroExpander(self.__version)
+ else:
+ self.__root = r"Software\Microsoft\Devstudio"
+ self.__product = "Visual Studio version %s" % self.__version
+ else:
+ # Win64. Assume this was built with the platform SDK
+ self.__product = "Microsoft SDK compiler %s" % (self.__version + 6)
+
+ self.initialized = False
+
+ def initialize(self):
+ self.__paths = []
+ if ("DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and
+ self.find_exe("cl.exe")):
+ # Assume that the SDK set up everything alright; don't try to be
+ # smarter
+ self.cc = "cl.exe"
+ self.linker = "link.exe"
+ self.lib = "lib.exe"
+ self.rc = "rc.exe"
+ self.mc = "mc.exe"
+ else:
+ self.__paths = self.get_msvc_paths("path")
+
+ if len(self.__paths) == 0:
+ raise PackagingPlatformError("Python was built with %s "
+ "and extensions need to be built with the same "
+ "version of the compiler, but it isn't installed." %
+ self.__product)
+
+ self.cc = self.find_exe("cl.exe")
+ self.linker = self.find_exe("link.exe")
+ self.lib = self.find_exe("lib.exe")
+ self.rc = self.find_exe("rc.exe") # resource compiler
+ self.mc = self.find_exe("mc.exe") # message compiler
+ self.set_path_env_var('lib')
+ self.set_path_env_var('include')
+
+ # extend the MSVC path with the current path
+ try:
+ for p in os.environ['path'].split(';'):
+ self.__paths.append(p)
+ except KeyError:
+ pass
+ self.__paths = normalize_and_reduce_paths(self.__paths)
+ os.environ['path'] = ';'.join(self.__paths)
+
+ self.preprocess_options = None
+ if self.__arch == "Intel":
+ self.compile_options = ['/nologo', '/Ox', '/MD', '/W3', '/GX',
+ '/DNDEBUG']
+ self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GX',
+ '/Z7', '/D_DEBUG']
+ else:
+ # Win64
+ self.compile_options = ['/nologo', '/Ox', '/MD', '/W3', '/GS-',
+ '/DNDEBUG']
+ self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-',
+ '/Z7', '/D_DEBUG']
+
+ self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO']
+ if self.__version >= 7:
+ self.ldflags_shared_debug = [
+ '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG'
+ ]
+ else:
+ self.ldflags_shared_debug = [
+ '/DLL', '/nologo', '/INCREMENTAL:no', '/pdb:None', '/DEBUG'
+ ]
+ self.ldflags_static = [ '/nologo']
+
+ self.initialized = True
+
+ # -- Worker methods ------------------------------------------------
+
+ def object_filenames(self, source_filenames, strip_dir=False, output_dir=''):
+ # Copied from ccompiler.py, extended to return .res as 'object'-file
+ # for .rc input file
+ if output_dir is None:
+ output_dir = ''
+ obj_names = []
+ for src_name in source_filenames:
+ base, ext = os.path.splitext(src_name)
+ base = os.path.splitdrive(base)[1] # Chop off the drive
+ base = base[os.path.isabs(base):] # If abs, chop off leading /
+ if ext not in self.src_extensions:
+ # Better to raise an exception instead of silently continuing
+ # and later complain about sources and targets having
+ # different lengths
+ raise CompileError("Don't know how to compile %s" % src_name)
+ if strip_dir:
+ base = os.path.basename(base)
+ if ext in self._rc_extensions:
+ obj_names.append(os.path.join(output_dir,
+ base + self.res_extension))
+ elif ext in self._mc_extensions:
+ obj_names.append(os.path.join(output_dir,
+ base + self.res_extension))
+ else:
+ obj_names.append(os.path.join(output_dir,
+ base + self.obj_extension))
+ return obj_names
+
+ def compile(self, sources,
+ output_dir=None, macros=None, include_dirs=None, debug=False,
+ extra_preargs=None, extra_postargs=None, depends=None):
+
+ if not self.initialized:
+ self.initialize()
+ macros, objects, extra_postargs, pp_opts, build = \
+ self._setup_compile(output_dir, macros, include_dirs, sources,
+ depends, extra_postargs)
+
+ compile_opts = extra_preargs or []
+ compile_opts.append('/c')
+ if debug:
+ compile_opts.extend(self.compile_options_debug)
+ else:
+ compile_opts.extend(self.compile_options)
+
+ for obj in objects:
+ try:
+ src, ext = build[obj]
+ except KeyError:
+ continue
+ if debug:
+ # pass the full pathname to MSVC in debug mode,
+ # this allows the debugger to find the source file
+ # without asking the user to browse for it
+ src = os.path.abspath(src)
+
+ if ext in self._c_extensions:
+ input_opt = "/Tc" + src
+ elif ext in self._cpp_extensions:
+ input_opt = "/Tp" + src
+ elif ext in self._rc_extensions:
+ # compile .RC to .RES file
+ input_opt = src
+ output_opt = "/fo" + obj
+ try:
+ self.spawn([self.rc] + pp_opts +
+ [output_opt] + [input_opt])
+ except PackagingExecError as msg:
+ raise CompileError(msg)
+ continue
+ elif ext in self._mc_extensions:
+
+ # Compile .MC to .RC file to .RES file.
+ # * '-h dir' specifies the directory for the
+ # generated include file
+ # * '-r dir' specifies the target directory of the
+ # generated RC file and the binary message resource
+ # it includes
+ #
+ # For now (since there are no options to change this),
+ # we use the source-directory for the include file and
+ # the build directory for the RC file and message
+ # resources. This works at least for win32all.
+
+ h_dir = os.path.dirname(src)
+ rc_dir = os.path.dirname(obj)
+ try:
+ # first compile .MC to .RC and .H file
+ self.spawn([self.mc] +
+ ['-h', h_dir, '-r', rc_dir] + [src])
+ base, _ = os.path.splitext(os.path.basename(src))
+ rc_file = os.path.join(rc_dir, base + '.rc')
+ # then compile .RC to .RES file
+ self.spawn([self.rc] +
+ ["/fo" + obj] + [rc_file])
+
+ except PackagingExecError as msg:
+ raise CompileError(msg)
+ continue
+ else:
+ # how to handle this file?
+ raise CompileError(
+ "Don't know how to compile %s to %s" %
+ (src, obj))
+
+ output_opt = "/Fo" + obj
+ try:
+ self.spawn([self.cc] + compile_opts + pp_opts +
+ [input_opt, output_opt] +
+ extra_postargs)
+ except PackagingExecError as msg:
+ raise CompileError(msg)
+
+ return objects
+
+ def create_static_lib(self, objects, output_libname, output_dir=None,
+ debug=False, target_lang=None):
+ if not self.initialized:
+ self.initialize()
+ objects, output_dir = self._fix_object_args(objects, output_dir)
+ output_filename = \
+ self.library_filename(output_libname, output_dir=output_dir)
+
+ if self._need_link(objects, output_filename):
+ lib_args = objects + ['/OUT:' + output_filename]
+ if debug:
+ pass # XXX what goes here?
+ try:
+ self.spawn([self.lib] + lib_args)
+ except PackagingExecError as msg:
+ raise LibError(msg)
+
+ else:
+ logger.debug("skipping %s (up-to-date)", output_filename)
+
+ def link(self, target_desc, objects, output_filename, output_dir=None,
+ libraries=None, library_dirs=None, runtime_library_dirs=None,
+ export_symbols=None, debug=False, extra_preargs=None,
+ extra_postargs=None, build_temp=None, target_lang=None):
+
+ if not self.initialized:
+ self.initialize()
+ objects, output_dir = self._fix_object_args(objects, output_dir)
+ libraries, library_dirs, runtime_library_dirs = \
+ self._fix_lib_args(libraries, library_dirs, runtime_library_dirs)
+
+ if runtime_library_dirs:
+ self.warn("don't know what to do with 'runtime_library_dirs': %s"
+ % (runtime_library_dirs,))
+
+ lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs,
+ libraries)
+ if output_dir is not None:
+ output_filename = os.path.join(output_dir, output_filename)
+
+ if self._need_link(objects, output_filename):
+
+ if target_desc == CCompiler.EXECUTABLE:
+ if debug:
+ ldflags = self.ldflags_shared_debug[1:]
+ else:
+ ldflags = self.ldflags_shared[1:]
+ else:
+ if debug:
+ ldflags = self.ldflags_shared_debug
+ else:
+ ldflags = self.ldflags_shared
+
+ export_opts = []
+ for sym in (export_symbols or []):
+ export_opts.append("/EXPORT:" + sym)
+
+ ld_args = (ldflags + lib_opts + export_opts +
+ objects + ['/OUT:' + output_filename])
+
+ # The MSVC linker generates .lib and .exp files, which cannot be
+ # suppressed by any linker switches. The .lib files may even be
+ # needed! Make sure they are generated in the temporary build
+ # directory. Since they have different names for debug and release
+ # builds, they can go into the same directory.
+ if export_symbols is not None:
+ dll_name, dll_ext = os.path.splitext(
+ os.path.basename(output_filename))
+ implib_file = os.path.join(
+ os.path.dirname(objects[0]),
+ self.library_filename(dll_name))
+ ld_args.append('/IMPLIB:' + implib_file)
+
+ if extra_preargs:
+ ld_args[:0] = extra_preargs
+ if extra_postargs:
+ ld_args.extend(extra_postargs)
+
+ self.mkpath(os.path.dirname(output_filename))
+ try:
+ self.spawn([self.linker] + ld_args)
+ except PackagingExecError as msg:
+ raise LinkError(msg)
+
+ else:
+ logger.debug("skipping %s (up-to-date)", output_filename)
+
+ # -- Miscellaneous methods -----------------------------------------
+ # These are all used by the 'gen_lib_options() function, in
+ # ccompiler.py.
+
+ def library_dir_option(self, dir):
+ return "/LIBPATH:" + dir
+
+ def runtime_library_dir_option(self, dir):
+ raise PackagingPlatformError("don't know how to set runtime library search path for MSVC++")
+
+ def library_option(self, lib):
+ return self.library_filename(lib)
+
+ def find_library_file(self, dirs, lib, debug=False):
+ # Prefer a debugging library if found (and requested), but deal
+ # with it if we don't have one.
+ if debug:
+ try_names = [lib + "_d", lib]
+ else:
+ try_names = [lib]
+ for dir in dirs:
+ for name in try_names:
+ libfile = os.path.join(dir, self.library_filename(name))
+ if os.path.exists(libfile):
+ return libfile
+ else:
+ # Oops, didn't find it in *any* of 'dirs'
+ return None
+
+ # Helper methods for using the MSVC registry settings
+
+ def find_exe(self, exe):
+ """Return path to an MSVC executable program.
+
+ Tries to find the program in several places: first, one of the
+ MSVC program search paths from the registry; next, the directories
+ in the PATH environment variable. If any of those work, return an
+ absolute path that is known to exist. If none of them work, just
+ return the original program name, 'exe'.
+ """
+
+ for p in self.__paths:
+ fn = os.path.join(os.path.abspath(p), exe)
+ if os.path.isfile(fn):
+ return fn
+
+ # didn't find it; try existing path
+ for p in os.environ['Path'].split(';'):
+ fn = os.path.join(os.path.abspath(p), exe)
+ if os.path.isfile(fn):
+ return fn
+
+ return exe
+
+ def get_msvc_paths(self, path, platform='x86'):
+ """Get a list of devstudio directories (include, lib or path).
+
+ Return a list of strings. The list will be empty if unable to
+ access the registry or appropriate registry keys not found.
+ """
+
+ if not _can_read_reg:
+ return []
+
+ path = path + " dirs"
+ if self.__version >= 7:
+ key = (r"%s\%0.1f\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories"
+ % (self.__root, self.__version))
+ else:
+ key = (r"%s\6.0\Build System\Components\Platforms"
+ r"\Win32 (%s)\Directories" % (self.__root, platform))
+
+ for base in HKEYS:
+ d = read_values(base, key)
+ if d:
+ if self.__version >= 7:
+ return self.__macros.sub(d[path]).split(";")
+ else:
+ return d[path].split(";")
+ # MSVC 6 seems to create the registry entries we need only when
+ # the GUI is run.
+ if self.__version == 6:
+ for base in HKEYS:
+ if read_values(base, r"%s\6.0" % self.__root) is not None:
+ self.warn("It seems you have Visual Studio 6 installed, "
+ "but the expected registry settings are not present.\n"
+ "You must at least run the Visual Studio GUI once "
+ "so that these entries are created.")
+ break
+ return []
+
+ def set_path_env_var(self, name):
+ """Set environment variable 'name' to an MSVC path type value.
+
+ This is equivalent to a SET command prior to execution of spawned
+ commands.
+ """
+
+ if name == "lib":
+ p = self.get_msvc_paths("library")
+ else:
+ p = self.get_msvc_paths(name)
+ if p:
+ os.environ[name] = ';'.join(p)
+
+
+if get_build_version() >= 8.0:
+ logger.debug("importing new compiler from distutils.msvc9compiler")
+ OldMSVCCompiler = MSVCCompiler
+ from packaging.compiler.msvc9compiler import MSVCCompiler
+ # get_build_architecture not really relevant now we support cross-compile
+ from packaging.compiler.msvc9compiler import MacroExpander
diff --git a/Lib/packaging/compiler/unixccompiler.py b/Lib/packaging/compiler/unixccompiler.py
new file mode 100644
index 0000000..3458faa
--- /dev/null
+++ b/Lib/packaging/compiler/unixccompiler.py
@@ -0,0 +1,339 @@
+"""CCompiler implementation for Unix compilers.
+
+This module contains the UnixCCompiler class, a subclass of CCompiler
+that handles the "typical" Unix-style command-line C compiler:
+ * macros defined with -Dname[=value]
+ * macros undefined with -Uname
+ * include search directories specified with -Idir
+ * libraries specified with -lllib
+ * library search directories specified with -Ldir
+ * compile handled by 'cc' (or similar) executable with -c option:
+ compiles .c to .o
+ * link static library handled by 'ar' command (possibly with 'ranlib')
+ * link shared library handled by 'cc -shared'
+"""
+
+import os, sys
+
+from packaging.util import newer
+from packaging.compiler.ccompiler import CCompiler
+from packaging.compiler import gen_preprocess_options, gen_lib_options
+from packaging.errors import (PackagingExecError, CompileError,
+ LibError, LinkError)
+from packaging import logger
+import sysconfig
+
+
+# XXX Things not currently handled:
+# * optimization/debug/warning flags; we just use whatever's in Python's
+# Makefile and live with it. Is this adequate? If not, we might
+# have to have a bunch of subclasses GNUCCompiler, SGICCompiler,
+# SunCCompiler, and I suspect down that road lies madness.
+# * even if we don't know a warning flag from an optimization flag,
+# we need some way for outsiders to feed preprocessor/compiler/linker
+# flags in to us -- eg. a sysadmin might want to mandate certain flags
+# via a site config file, or a user might want to set something for
+# compiling this module distribution only via the pysetup command
+# line, whatever. As long as these options come from something on the
+# current system, they can be as system-dependent as they like, and we
+# should just happily stuff them into the preprocessor/compiler/linker
+# options and carry on.
+
+def _darwin_compiler_fixup(compiler_so, cc_args):
+ """
+ This function will strip '-isysroot PATH' and '-arch ARCH' from the
+ compile flags if the user has specified one them in extra_compile_flags.
+
+ This is needed because '-arch ARCH' adds another architecture to the
+ build, without a way to remove an architecture. Furthermore GCC will
+ barf if multiple '-isysroot' arguments are present.
+ """
+ stripArch = stripSysroot = False
+
+ compiler_so = list(compiler_so)
+ kernel_version = os.uname()[2] # 8.4.3
+ major_version = int(kernel_version.split('.')[0])
+
+ if major_version < 8:
+ # OSX before 10.4.0, these don't support -arch and -isysroot at
+ # all.
+ stripArch = stripSysroot = True
+ else:
+ stripArch = '-arch' in cc_args
+ stripSysroot = '-isysroot' in cc_args
+
+ if stripArch or 'ARCHFLAGS' in os.environ:
+ while True:
+ try:
+ index = compiler_so.index('-arch')
+ # Strip this argument and the next one:
+ del compiler_so[index:index+2]
+ except ValueError:
+ break
+
+ if 'ARCHFLAGS' in os.environ and not stripArch:
+ # User specified different -arch flags in the environ,
+ # see also the sysconfig
+ compiler_so = compiler_so + os.environ['ARCHFLAGS'].split()
+
+ if stripSysroot:
+ try:
+ index = compiler_so.index('-isysroot')
+ # Strip this argument and the next one:
+ del compiler_so[index:index+2]
+ except ValueError:
+ pass
+
+ # Check if the SDK that is used during compilation actually exists,
+ # the universal build requires the usage of a universal SDK and not all
+ # users have that installed by default.
+ sysroot = None
+ if '-isysroot' in cc_args:
+ idx = cc_args.index('-isysroot')
+ sysroot = cc_args[idx+1]
+ elif '-isysroot' in compiler_so:
+ idx = compiler_so.index('-isysroot')
+ sysroot = compiler_so[idx+1]
+
+ if sysroot and not os.path.isdir(sysroot):
+ logger.warning(
+ "compiling with an SDK that doesn't seem to exist: %r;\n"
+ "please check your Xcode installation", sysroot)
+
+ return compiler_so
+
+class UnixCCompiler(CCompiler):
+
+ name = 'unix'
+ description = 'Standard UNIX-style compiler'
+
+ # These are used by CCompiler in two places: the constructor sets
+ # instance attributes 'preprocessor', 'compiler', etc. from them, and
+ # 'set_executable()' allows any of these to be set. The defaults here
+ # are pretty generic; they will probably have to be set by an outsider
+ # (eg. using information discovered by the sysconfig about building
+ # Python extensions).
+ executables = {'preprocessor' : None,
+ 'compiler' : ["cc"],
+ 'compiler_so' : ["cc"],
+ 'compiler_cxx' : ["cc"],
+ 'linker_so' : ["cc", "-shared"],
+ 'linker_exe' : ["cc"],
+ 'archiver' : ["ar", "-cr"],
+ 'ranlib' : None,
+ }
+
+ if sys.platform[:6] == "darwin":
+ executables['ranlib'] = ["ranlib"]
+
+ # Needed for the filename generation methods provided by the base
+ # class, CCompiler. XXX whoever instantiates/uses a particular
+ # UnixCCompiler instance should set 'shared_lib_ext' -- we set a
+ # reasonable common default here, but it's not necessarily used on all
+ # Unices!
+
+ src_extensions = [".c",".C",".cc",".cxx",".cpp",".m"]
+ obj_extension = ".o"
+ static_lib_extension = ".a"
+ shared_lib_extension = ".so"
+ dylib_lib_extension = ".dylib"
+ static_lib_format = shared_lib_format = dylib_lib_format = "lib%s%s"
+ if sys.platform == "cygwin":
+ exe_extension = ".exe"
+
+ def preprocess(self, source,
+ output_file=None, macros=None, include_dirs=None,
+ extra_preargs=None, extra_postargs=None):
+ ignore, macros, include_dirs = \
+ self._fix_compile_args(None, macros, include_dirs)
+ pp_opts = gen_preprocess_options(macros, include_dirs)
+ pp_args = self.preprocessor + pp_opts
+ if output_file:
+ pp_args.extend(('-o', output_file))
+ if extra_preargs:
+ pp_args[:0] = extra_preargs
+ if extra_postargs:
+ pp_args.extend(extra_postargs)
+ pp_args.append(source)
+
+ # We need to preprocess: either we're being forced to, or we're
+ # generating output to stdout, or there's a target output file and
+ # the source file is newer than the target (or the target doesn't
+ # exist).
+ if self.force or output_file is None or newer(source, output_file):
+ if output_file:
+ self.mkpath(os.path.dirname(output_file))
+ try:
+ self.spawn(pp_args)
+ except PackagingExecError as msg:
+ raise CompileError(msg)
+
+ def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
+ compiler_so = self.compiler_so
+ if sys.platform == 'darwin':
+ compiler_so = _darwin_compiler_fixup(compiler_so, cc_args + extra_postargs)
+ try:
+ self.spawn(compiler_so + cc_args + [src, '-o', obj] +
+ extra_postargs)
+ except PackagingExecError as msg:
+ raise CompileError(msg)
+
+ def create_static_lib(self, objects, output_libname,
+ output_dir=None, debug=False, target_lang=None):
+ objects, output_dir = self._fix_object_args(objects, output_dir)
+
+ output_filename = \
+ self.library_filename(output_libname, output_dir=output_dir)
+
+ if self._need_link(objects, output_filename):
+ self.mkpath(os.path.dirname(output_filename))
+ self.spawn(self.archiver +
+ [output_filename] +
+ objects + self.objects)
+
+ # Not many Unices required ranlib anymore -- SunOS 4.x is, I
+ # think the only major Unix that does. Maybe we need some
+ # platform intelligence here to skip ranlib if it's not
+ # needed -- or maybe Python's configure script took care of
+ # it for us, hence the check for leading colon.
+ if self.ranlib:
+ try:
+ self.spawn(self.ranlib + [output_filename])
+ except PackagingExecError as msg:
+ raise LibError(msg)
+ else:
+ logger.debug("skipping %s (up-to-date)", output_filename)
+
+ def link(self, target_desc, objects,
+ output_filename, output_dir=None, libraries=None,
+ library_dirs=None, runtime_library_dirs=None,
+ export_symbols=None, debug=False, extra_preargs=None,
+ extra_postargs=None, build_temp=None, target_lang=None):
+ objects, output_dir = self._fix_object_args(objects, output_dir)
+ libraries, library_dirs, runtime_library_dirs = \
+ self._fix_lib_args(libraries, library_dirs, runtime_library_dirs)
+
+ lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs,
+ libraries)
+ if type(output_dir) not in (str, type(None)):
+ raise TypeError("'output_dir' must be a string or None")
+ if output_dir is not None:
+ output_filename = os.path.join(output_dir, output_filename)
+
+ if self._need_link(objects, output_filename):
+ ld_args = (objects + self.objects +
+ lib_opts + ['-o', output_filename])
+ if debug:
+ ld_args[:0] = ['-g']
+ if extra_preargs:
+ ld_args[:0] = extra_preargs
+ if extra_postargs:
+ ld_args.extend(extra_postargs)
+ self.mkpath(os.path.dirname(output_filename))
+ try:
+ if target_desc == CCompiler.EXECUTABLE:
+ linker = self.linker_exe[:]
+ else:
+ linker = self.linker_so[:]
+ if target_lang == "c++" and self.compiler_cxx:
+ # skip over environment variable settings if /usr/bin/env
+ # is used to set up the linker's environment.
+ # This is needed on OSX. Note: this assumes that the
+ # normal and C++ compiler have the same environment
+ # settings.
+ i = 0
+ if os.path.basename(linker[0]) == "env":
+ i = 1
+ while '=' in linker[i]:
+ i = i + 1
+
+ linker[i] = self.compiler_cxx[i]
+
+ if sys.platform == 'darwin':
+ linker = _darwin_compiler_fixup(linker, ld_args)
+
+ self.spawn(linker + ld_args)
+ except PackagingExecError as msg:
+ raise LinkError(msg)
+ else:
+ logger.debug("skipping %s (up-to-date)", output_filename)
+
+ # -- Miscellaneous methods -----------------------------------------
+ # These are all used by the 'gen_lib_options() function, in
+ # ccompiler.py.
+
+ def library_dir_option(self, dir):
+ return "-L" + dir
+
+ def _is_gcc(self, compiler_name):
+ return "gcc" in compiler_name or "g++" in compiler_name
+
+ def runtime_library_dir_option(self, dir):
+ # XXX Hackish, at the very least. See Python bug #445902:
+ # http://sourceforge.net/tracker/index.php
+ # ?func=detail&aid=445902&group_id=5470&atid=105470
+ # Linkers on different platforms need different options to
+ # specify that directories need to be added to the list of
+ # directories searched for dependencies when a dynamic library
+ # is sought. GCC on GNU systems (Linux, FreeBSD, ...) has to
+ # be told to pass the -R option through to the linker, whereas
+ # other compilers and gcc on other systems just know this.
+ # Other compilers may need something slightly different. At
+ # this time, there's no way to determine this information from
+ # the configuration data stored in the Python installation, so
+ # we use this hack.
+
+ compiler = os.path.basename(sysconfig.get_config_var("CC"))
+ if sys.platform[:6] == "darwin":
+ # MacOSX's linker doesn't understand the -R flag at all
+ return "-L" + dir
+ elif sys.platform[:5] == "hp-ux":
+ if self._is_gcc(compiler):
+ return ["-Wl,+s", "-L" + dir]
+ return ["+s", "-L" + dir]
+ elif sys.platform[:7] == "irix646" or sys.platform[:6] == "osf1V5":
+ return ["-rpath", dir]
+ elif self._is_gcc(compiler):
+ # gcc on non-GNU systems does not need -Wl, but can
+ # use it anyway. Since distutils has always passed in
+ # -Wl whenever gcc was used in the past it is probably
+ # safest to keep doing so.
+ if sysconfig.get_config_var("GNULD") == "yes":
+ # GNU ld needs an extra option to get a RUNPATH
+ # instead of just an RPATH.
+ return "-Wl,--enable-new-dtags,-R" + dir
+ else:
+ return "-Wl,-R" + dir
+ elif sys.platform[:3] == "aix":
+ return "-blibpath:" + dir
+ else:
+ # No idea how --enable-new-dtags would be passed on to
+ # ld if this system was using GNU ld. Don't know if a
+ # system like this even exists.
+ return "-R" + dir
+
+ def library_option(self, lib):
+ return "-l" + lib
+
+ def find_library_file(self, dirs, lib, debug=False):
+ shared_f = self.library_filename(lib, lib_type='shared')
+ dylib_f = self.library_filename(lib, lib_type='dylib')
+ static_f = self.library_filename(lib, lib_type='static')
+
+ for dir in dirs:
+ shared = os.path.join(dir, shared_f)
+ dylib = os.path.join(dir, dylib_f)
+ static = os.path.join(dir, static_f)
+ # We're second-guessing the linker here, with not much hard
+ # data to go on: GCC seems to prefer the shared library, so I'm
+ # assuming that *all* Unix C compilers do. And of course I'm
+ # ignoring even GCC's "-static" option. So sue me.
+ if os.path.exists(dylib):
+ return dylib
+ elif os.path.exists(shared):
+ return shared
+ elif os.path.exists(static):
+ return static
+
+ # Oops, didn't find it in *any* of 'dirs'
+ return None
diff --git a/Lib/packaging/config.py b/Lib/packaging/config.py
new file mode 100644
index 0000000..366faea
--- /dev/null
+++ b/Lib/packaging/config.py
@@ -0,0 +1,380 @@
+"""Utilities to find and read config files used by packaging."""
+
+import os
+import sys
+import logging
+
+from shlex import split
+from configparser import RawConfigParser
+from packaging import logger
+from packaging.errors import PackagingOptionError
+from packaging.compiler.extension import Extension
+from packaging.util import (check_environ, iglob, resolve_name, strtobool,
+ split_multiline)
+from packaging.compiler import set_compiler
+from packaging.command import set_command
+from packaging.markers import interpret
+
+
+def _check_name(name, packages):
+ if '.' not in name:
+ return
+ parts = name.split('.')
+ modname = parts[-1]
+ parent = '.'.join(parts[:-1])
+ if parent not in packages:
+ # we could log a warning instead of raising, but what's the use
+ # of letting people build modules they can't import?
+ raise PackagingOptionError(
+ 'parent package for extension %r not found' % name)
+
+
+def _pop_values(values_dct, key):
+ """Remove values from the dictionary and convert them as a list"""
+ vals_str = values_dct.pop(key, '')
+ if not vals_str:
+ return
+ fields = []
+ # the line separator is \n for setup.cfg files
+ for field in vals_str.split('\n'):
+ tmp_vals = field.split('--')
+ if len(tmp_vals) == 2 and not interpret(tmp_vals[1]):
+ continue
+ fields.append(tmp_vals[0])
+ # Get bash options like `gcc -print-file-name=libgcc.a` XXX bash options?
+ vals = split(' '.join(fields))
+ if vals:
+ return vals
+
+
+def _rel_path(base, path):
+ # normalizes and returns a lstripped-/-separated path
+ base = base.replace(os.path.sep, '/')
+ path = path.replace(os.path.sep, '/')
+ assert path.startswith(base)
+ return path[len(base):].lstrip('/')
+
+
+def get_resources_dests(resources_root, rules):
+ """Find destinations for resources files"""
+ destinations = {}
+ for base, suffix, dest in rules:
+ prefix = os.path.join(resources_root, base)
+ for abs_base in iglob(prefix):
+ abs_glob = os.path.join(abs_base, suffix)
+ for abs_path in iglob(abs_glob):
+ resource_file = _rel_path(resources_root, abs_path)
+ if dest is None: # remove the entry if it was here
+ destinations.pop(resource_file, None)
+ else:
+ rel_path = _rel_path(abs_base, abs_path)
+ rel_dest = dest.replace(os.path.sep, '/').rstrip('/')
+ destinations[resource_file] = rel_dest + '/' + rel_path
+ return destinations
+
+
+class Config:
+ """Class used to work with configuration files"""
+ def __init__(self, dist):
+ self.dist = dist
+ self.setup_hooks = []
+
+ def run_hooks(self, config):
+ """Run setup hooks in the order defined in the spec."""
+ for hook in self.setup_hooks:
+ hook(config)
+
+ def find_config_files(self):
+ """Find as many configuration files as should be processed for this
+ platform, and return a list of filenames in the order in which they
+ should be parsed. The filenames returned are guaranteed to exist
+ (modulo nasty race conditions).
+
+ There are three possible config files: packaging.cfg in the
+ Packaging installation directory (ie. where the top-level
+ Packaging __inst__.py file lives), a file in the user's home
+ directory named .pydistutils.cfg on Unix and pydistutils.cfg
+ on Windows/Mac; and setup.cfg in the current directory.
+
+ The file in the user's home directory can be disabled with the
+ --no-user-cfg option.
+ """
+ files = []
+ check_environ()
+
+ # Where to look for the system-wide Packaging config file
+ sys_dir = os.path.dirname(sys.modules['packaging'].__file__)
+
+ # Look for the system config file
+ sys_file = os.path.join(sys_dir, "packaging.cfg")
+ if os.path.isfile(sys_file):
+ files.append(sys_file)
+
+ # What to call the per-user config file
+ if os.name == 'posix':
+ user_filename = ".pydistutils.cfg"
+ else:
+ user_filename = "pydistutils.cfg"
+
+ # And look for the user config file
+ if self.dist.want_user_cfg:
+ user_file = os.path.join(os.path.expanduser('~'), user_filename)
+ if os.path.isfile(user_file):
+ files.append(user_file)
+
+ # All platforms support local setup.cfg
+ local_file = "setup.cfg"
+ if os.path.isfile(local_file):
+ files.append(local_file)
+
+ if logger.isEnabledFor(logging.DEBUG):
+ logger.debug("using config files: %s", ', '.join(files))
+ return files
+
+ def _convert_metadata(self, name, value):
+ # converts a value found in setup.cfg into a valid metadata
+ # XXX
+ return value
+
+ def _read_setup_cfg(self, parser, cfg_filename):
+ cfg_directory = os.path.dirname(os.path.abspath(cfg_filename))
+ content = {}
+ for section in parser.sections():
+ content[section] = dict(parser.items(section))
+
+ # global setup hooks are called first
+ if 'global' in content:
+ if 'setup_hooks' in content['global']:
+ setup_hooks = split_multiline(content['global']['setup_hooks'])
+
+ # add project directory to sys.path, to allow hooks to be
+ # distributed with the project
+ sys.path.insert(0, cfg_directory)
+ try:
+ for line in setup_hooks:
+ try:
+ hook = resolve_name(line)
+ except ImportError as e:
+ logger.warning('cannot find setup hook: %s',
+ e.args[0])
+ else:
+ self.setup_hooks.append(hook)
+ self.run_hooks(content)
+ finally:
+ sys.path.pop(0)
+
+ metadata = self.dist.metadata
+
+ # setting the metadata values
+ if 'metadata' in content:
+ for key, value in content['metadata'].items():
+ key = key.replace('_', '-')
+ if metadata.is_multi_field(key):
+ value = split_multiline(value)
+
+ if key == 'project-url':
+ value = [(label.strip(), url.strip())
+ for label, url in
+ [v.split(',') for v in value]]
+
+ if key == 'description-file':
+ if 'description' in content['metadata']:
+ msg = ("description and description-file' are "
+ "mutually exclusive")
+ raise PackagingOptionError(msg)
+
+ filenames = value.split()
+
+ # concatenate all files
+ value = []
+ for filename in filenames:
+ # will raise if file not found
+ with open(filename) as description_file:
+ value.append(description_file.read().strip())
+ # add filename as a required file
+ if filename not in metadata.requires_files:
+ metadata.requires_files.append(filename)
+ value = '\n'.join(value).strip()
+ key = 'description'
+
+ if metadata.is_metadata_field(key):
+ metadata[key] = self._convert_metadata(key, value)
+
+ if 'files' in content:
+ files = content['files']
+ self.dist.package_dir = files.pop('packages_root', None)
+
+ files = dict((key, split_multiline(value)) for key, value in
+ files.items())
+
+ self.dist.packages = []
+
+ packages = files.get('packages', [])
+ if isinstance(packages, str):
+ packages = [packages]
+
+ for package in packages:
+ if ':' in package:
+ dir_, package = package.split(':')
+ self.dist.package_dir[package] = dir_
+ self.dist.packages.append(package)
+
+ self.dist.py_modules = files.get('modules', [])
+ if isinstance(self.dist.py_modules, str):
+ self.dist.py_modules = [self.dist.py_modules]
+ self.dist.scripts = files.get('scripts', [])
+ if isinstance(self.dist.scripts, str):
+ self.dist.scripts = [self.dist.scripts]
+
+ self.dist.package_data = {}
+ for line in files.get('package_data', []):
+ data = line.split('=')
+ if len(data) != 2:
+ raise ValueError('invalid line for package_data: %s '
+ '(misses "=")' % line)
+ key, value = data
+ self.dist.package_data[key.strip()] = value.strip()
+
+ self.dist.data_files = []
+ for data in files.get('data_files', []):
+ data = data.split('=')
+ if len(data) != 2:
+ continue
+ key, value = data
+ values = [v.strip() for v in value.split(',')]
+ self.dist.data_files.append((key, values))
+
+ # manifest template
+ self.dist.extra_files = files.get('extra_files', [])
+
+ resources = []
+ for rule in files.get('resources', []):
+ glob, destination = rule.split('=', 1)
+ rich_glob = glob.strip().split(' ', 1)
+ if len(rich_glob) == 2:
+ prefix, suffix = rich_glob
+ else:
+ assert len(rich_glob) == 1
+ prefix = ''
+ suffix = glob
+ if destination == '<exclude>':
+ destination = None
+ resources.append(
+ (prefix.strip(), suffix.strip(), destination.strip()))
+ self.dist.data_files = get_resources_dests(
+ cfg_directory, resources)
+
+ ext_modules = self.dist.ext_modules
+ for section_key in content:
+ # no str.partition in 2.4 :(
+ labels = section_key.split(':')
+ if len(labels) == 2 and labels[0] == 'extension':
+ values_dct = content[section_key]
+ if 'name' in values_dct:
+ raise PackagingOptionError(
+ 'extension name should be given as [extension: name], '
+ 'not as key')
+ name = labels[1].strip()
+ _check_name(name, self.dist.packages)
+ ext_modules.append(Extension(
+ name,
+ _pop_values(values_dct, 'sources'),
+ _pop_values(values_dct, 'include_dirs'),
+ _pop_values(values_dct, 'define_macros'),
+ _pop_values(values_dct, 'undef_macros'),
+ _pop_values(values_dct, 'library_dirs'),
+ _pop_values(values_dct, 'libraries'),
+ _pop_values(values_dct, 'runtime_library_dirs'),
+ _pop_values(values_dct, 'extra_objects'),
+ _pop_values(values_dct, 'extra_compile_args'),
+ _pop_values(values_dct, 'extra_link_args'),
+ _pop_values(values_dct, 'export_symbols'),
+ _pop_values(values_dct, 'swig_opts'),
+ _pop_values(values_dct, 'depends'),
+ values_dct.pop('language', None),
+ values_dct.pop('optional', None),
+ **values_dct))
+
+ def parse_config_files(self, filenames=None):
+ if filenames is None:
+ filenames = self.find_config_files()
+
+ logger.debug("Distribution.parse_config_files():")
+
+ parser = RawConfigParser()
+
+ for filename in filenames:
+ logger.debug(" reading %s", filename)
+ parser.read(filename, encoding='utf-8')
+
+ if os.path.split(filename)[-1] == 'setup.cfg':
+ self._read_setup_cfg(parser, filename)
+
+ for section in parser.sections():
+ if section == 'global':
+ if parser.has_option('global', 'compilers'):
+ self._load_compilers(parser.get('global', 'compilers'))
+
+ if parser.has_option('global', 'commands'):
+ self._load_commands(parser.get('global', 'commands'))
+
+ options = parser.options(section)
+ opt_dict = self.dist.get_option_dict(section)
+
+ for opt in options:
+ if opt == '__name__':
+ continue
+ val = parser.get(section, opt)
+ opt = opt.replace('-', '_')
+
+ if opt == 'sub_commands':
+ val = split_multiline(val)
+ if isinstance(val, str):
+ val = [val]
+
+ # Hooks use a suffix system to prevent being overriden
+ # by a config file processed later (i.e. a hook set in
+ # the user config file cannot be replaced by a hook
+ # set in a project config file, unless they have the
+ # same suffix).
+ if (opt.startswith("pre_hook.") or
+ opt.startswith("post_hook.")):
+ hook_type, alias = opt.split(".")
+ hook_dict = opt_dict.setdefault(
+ hook_type, (filename, {}))[1]
+ hook_dict[alias] = val
+ else:
+ opt_dict[opt] = filename, val
+
+ # Make the RawConfigParser forget everything (so we retain
+ # the original filenames that options come from)
+ parser.__init__()
+
+ # If there was a "global" section in the config file, use it
+ # to set Distribution options.
+ if 'global' in self.dist.command_options:
+ for opt, (src, val) in self.dist.command_options['global'].items():
+ alias = self.dist.negative_opt.get(opt)
+ try:
+ if alias:
+ setattr(self.dist, alias, not strtobool(val))
+ elif opt == 'dry_run': # FIXME ugh!
+ setattr(self.dist, opt, strtobool(val))
+ else:
+ setattr(self.dist, opt, val)
+ except ValueError as msg:
+ raise PackagingOptionError(msg)
+
+ def _load_compilers(self, compilers):
+ compilers = split_multiline(compilers)
+ if isinstance(compilers, str):
+ compilers = [compilers]
+ for compiler in compilers:
+ set_compiler(compiler.strip())
+
+ def _load_commands(self, commands):
+ commands = split_multiline(commands)
+ if isinstance(commands, str):
+ commands = [commands]
+ for command in commands:
+ set_command(command.strip())
diff --git a/Lib/packaging/create.py b/Lib/packaging/create.py
new file mode 100644
index 0000000..5abe47b
--- /dev/null
+++ b/Lib/packaging/create.py
@@ -0,0 +1,676 @@
+"""Interactive helper used to create a setup.cfg file.
+
+This script will generate a packaging configuration file by looking at
+the current directory and asking the user questions. It is intended to
+be called as *pysetup create*.
+"""
+
+# Original code by Sean Reifschneider <jafo@tummy.com>
+
+# Original TODO list:
+# Look for a license file and automatically add the category.
+# When a .c file is found during the walk, can we add it as an extension?
+# Ask if there is a maintainer different that the author
+# Ask for the platform (can we detect this via "import win32" or something?)
+# Ask for the dependencies.
+# Ask for the Requires-Dist
+# Ask for the Provides-Dist
+# Ask for a description
+# Detect scripts (not sure how. #! outside of package?)
+
+import os
+import re
+import imp
+import sys
+import glob
+import shutil
+import sysconfig
+from hashlib import md5
+from textwrap import dedent
+from tokenize import detect_encoding
+from configparser import RawConfigParser
+
+from packaging import logger
+# importing this with an underscore as it should be replaced by the
+# dict form or another structures for all purposes
+from packaging._trove import all_classifiers as _CLASSIFIERS_LIST
+from packaging.version import is_valid_version
+
+_FILENAME = 'setup.cfg'
+_DEFAULT_CFG = '.pypkgcreate' # FIXME use a section in user .pydistutils.cfg
+
+_helptext = {
+ 'name': '''
+The name of the project to be packaged, usually a single word composed
+of lower-case characters such as "zope.interface", "sqlalchemy" or
+"CherryPy".
+''',
+ 'version': '''
+Version number of the software, typically 2 or 3 numbers separated by
+dots such as "1.0", "0.6b3", or "3.2.1". "0.1.0" is recommended for
+initial development.
+''',
+ 'summary': '''
+A one-line summary of what this project is or does, typically a sentence
+80 characters or less in length.
+''',
+ 'author': '''
+The full name of the author (typically you).
+''',
+ 'author_email': '''
+Email address of the project author.
+''',
+ 'do_classifier': '''
+Trove classifiers are optional identifiers that allow you to specify the
+intended audience by saying things like "Beta software with a text UI
+for Linux under the PSF license". However, this can be a somewhat
+involved process.
+''',
+ 'packages': '''
+Python packages included in the project.
+''',
+ 'modules': '''
+Pure Python modules included in the project.
+''',
+ 'extra_files': '''
+You can provide extra files/dirs contained in your project.
+It has to follow the template syntax. XXX add help here.
+''',
+
+ 'home_page': '''
+The home page for the project, typically a public Web page.
+''',
+ 'trove_license': '''
+Optionally you can specify a license. Type a string that identifies a
+common license, and then you can select a list of license specifiers.
+''',
+ 'trove_generic': '''
+Optionally, you can set other trove identifiers for things such as the
+human language, programming language, user interface, etc.
+''',
+ 'setup.py found': '''
+The setup.py script will be executed to retrieve the metadata.
+An interactive helper will be run if you answer "n",
+''',
+}
+
+PROJECT_MATURITY = ['Development Status :: 1 - Planning',
+ 'Development Status :: 2 - Pre-Alpha',
+ 'Development Status :: 3 - Alpha',
+ 'Development Status :: 4 - Beta',
+ 'Development Status :: 5 - Production/Stable',
+ 'Development Status :: 6 - Mature',
+ 'Development Status :: 7 - Inactive']
+
+# XXX everything needs docstrings and tests (both low-level tests of various
+# methods and functional tests of running the script)
+
+
+def load_setup():
+ """run the setup script (i.e the setup.py file)
+
+ This function load the setup file in all cases (even if it have already
+ been loaded before, because we are monkey patching its setup function with
+ a particular one"""
+ with open("setup.py", "rb") as f:
+ encoding, lines = detect_encoding(f.readline)
+ with open("setup.py", encoding=encoding) as f:
+ imp.load_module("setup", f, "setup.py", (".py", "r", imp.PY_SOURCE))
+
+
+def ask_yn(question, default=None, helptext=None):
+ question += ' (y/n)'
+ while True:
+ answer = ask(question, default, helptext, required=True)
+ if answer and answer[0].lower() in ('y', 'n'):
+ return answer[0].lower()
+
+ logger.error('You must select "Y" or "N".')
+
+
+# XXX use util.ask
+# FIXME: if prompt ends with '?', don't add ':'
+
+
+def ask(question, default=None, helptext=None, required=True,
+ lengthy=False, multiline=False):
+ prompt = '%s: ' % (question,)
+ if default:
+ prompt = '%s [%s]: ' % (question, default)
+ if default and len(question) + len(default) > 70:
+ prompt = '%s\n [%s]: ' % (question, default)
+ if lengthy or multiline:
+ prompt += '\n > '
+
+ if not helptext:
+ helptext = 'No additional help available.'
+
+ helptext = helptext.strip("\n")
+
+ while True:
+ line = input(prompt).strip()
+ if line == '?':
+ print('=' * 70)
+ print(helptext)
+ print('=' * 70)
+ continue
+ if default and not line:
+ return default
+ if not line and required:
+ print('*' * 70)
+ print('This value cannot be empty.')
+ print('===========================')
+ if helptext:
+ print(helptext)
+ print('*' * 70)
+ continue
+ return line
+
+
+def convert_yn_to_bool(yn, yes=True, no=False):
+ """Convert a y/yes or n/no to a boolean value."""
+ if yn.lower().startswith('y'):
+ return yes
+ else:
+ return no
+
+
+def _build_classifiers_dict(classifiers):
+ d = {}
+ for key in classifiers:
+ subdict = d
+ for subkey in key.split(' :: '):
+ if subkey not in subdict:
+ subdict[subkey] = {}
+ subdict = subdict[subkey]
+ return d
+
+CLASSIFIERS = _build_classifiers_dict(_CLASSIFIERS_LIST)
+
+
+def _build_licences(classifiers):
+ res = []
+ for index, item in enumerate(classifiers):
+ if not item.startswith('License :: '):
+ continue
+ res.append((index, item.split(' :: ')[-1].lower()))
+ return res
+
+LICENCES = _build_licences(_CLASSIFIERS_LIST)
+
+
+class MainProgram:
+ """Make a project setup configuration file (setup.cfg)."""
+
+ def __init__(self):
+ self.configparser = None
+ self.classifiers = set()
+ self.data = {'name': '',
+ 'version': '1.0.0',
+ 'classifier': self.classifiers,
+ 'packages': [],
+ 'modules': [],
+ 'platform': [],
+ 'resources': [],
+ 'extra_files': [],
+ 'scripts': [],
+ }
+ self._load_defaults()
+
+ def __call__(self):
+ setupcfg_defined = False
+ if self.has_setup_py() and self._prompt_user_for_conversion():
+ setupcfg_defined = self.convert_py_to_cfg()
+ if not setupcfg_defined:
+ self.define_cfg_values()
+ self._write_cfg()
+
+ def has_setup_py(self):
+ """Test for the existence of a setup.py file."""
+ return os.path.exists('setup.py')
+
+ def define_cfg_values(self):
+ self.inspect()
+ self.query_user()
+
+ def _lookup_option(self, key):
+ if not self.configparser.has_option('DEFAULT', key):
+ return None
+ return self.configparser.get('DEFAULT', key)
+
+ def _load_defaults(self):
+ # Load default values from a user configuration file
+ self.configparser = RawConfigParser()
+ # TODO replace with section in distutils config file
+ default_cfg = os.path.expanduser(os.path.join('~', _DEFAULT_CFG))
+ self.configparser.read(default_cfg)
+ self.data['author'] = self._lookup_option('author')
+ self.data['author_email'] = self._lookup_option('author_email')
+
+ def _prompt_user_for_conversion(self):
+ # Prompt the user about whether they would like to use the setup.py
+ # conversion utility to generate a setup.cfg or generate the setup.cfg
+ # from scratch
+ answer = ask_yn(('A legacy setup.py has been found.\n'
+ 'Would you like to convert it to a setup.cfg?'),
+ default="y",
+ helptext=_helptext['setup.py found'])
+ return convert_yn_to_bool(answer)
+
+ def _dotted_packages(self, data):
+ packages = sorted(data)
+ modified_pkgs = []
+ for pkg in packages:
+ pkg = pkg.lstrip('./')
+ pkg = pkg.replace('/', '.')
+ modified_pkgs.append(pkg)
+ return modified_pkgs
+
+ def _write_cfg(self):
+ if os.path.exists(_FILENAME):
+ if os.path.exists('%s.old' % _FILENAME):
+ message = ("ERROR: %(name)s.old backup exists, please check "
+ "that current %(name)s is correct and remove "
+ "%(name)s.old" % {'name': _FILENAME})
+ logger.error(message)
+ return
+ shutil.move(_FILENAME, '%s.old' % _FILENAME)
+
+ with open(_FILENAME, 'w', encoding='utf-8') as fp:
+ fp.write('[metadata]\n')
+ # TODO use metadata module instead of hard-coding field-specific
+ # behavior here
+
+ # simple string entries
+ for name in ('name', 'version', 'summary', 'download_url'):
+ fp.write('%s = %s\n' % (name, self.data.get(name, 'UNKNOWN')))
+
+ # optional string entries
+ if 'keywords' in self.data and self.data['keywords']:
+ fp.write('keywords = %s\n' % ' '.join(self.data['keywords']))
+ for name in ('home_page', 'author', 'author_email',
+ 'maintainer', 'maintainer_email', 'description-file'):
+ if name in self.data and self.data[name]:
+ fp.write('%s = %s\n' % (name, self.data[name]))
+ if 'description' in self.data:
+ fp.write(
+ 'description = %s\n'
+ % '\n |'.join(self.data['description'].split('\n')))
+
+ # multiple use string entries
+ for name in ('platform', 'supported-platform', 'classifier',
+ 'requires-dist', 'provides-dist', 'obsoletes-dist',
+ 'requires-external'):
+ if not(name in self.data and self.data[name]):
+ continue
+ fp.write('%s = ' % name)
+ fp.write(''.join(' %s\n' % val
+ for val in self.data[name]).lstrip())
+ fp.write('\n[files]\n')
+ for name in ('packages', 'modules', 'scripts',
+ 'package_data', 'extra_files'):
+ if not(name in self.data and self.data[name]):
+ continue
+ fp.write('%s = %s\n'
+ % (name, '\n '.join(self.data[name]).strip()))
+ fp.write('\nresources =\n')
+ for src, dest in self.data['resources']:
+ fp.write(' %s = %s\n' % (src, dest))
+ fp.write('\n')
+
+ os.chmod(_FILENAME, 0o644)
+ logger.info('Wrote "%s".' % _FILENAME)
+
+ def convert_py_to_cfg(self):
+ """Generate a setup.cfg from an existing setup.py.
+
+ It only exports the distutils metadata (setuptools specific metadata
+ is not currently supported).
+ """
+ data = self.data
+
+ def setup_mock(**attrs):
+ """Mock the setup(**attrs) in order to retrieve metadata."""
+
+ # TODO use config and metadata instead of Distribution
+ from distutils.dist import Distribution
+ dist = Distribution(attrs)
+ dist.parse_config_files()
+
+ # 1. retrieve metadata fields that are quite similar in
+ # PEP 314 and PEP 345
+ labels = (('name',) * 2,
+ ('version',) * 2,
+ ('author',) * 2,
+ ('author_email',) * 2,
+ ('maintainer',) * 2,
+ ('maintainer_email',) * 2,
+ ('description', 'summary'),
+ ('long_description', 'description'),
+ ('url', 'home_page'),
+ ('platforms', 'platform'),
+ # backport only for 2.5+
+ ('provides', 'provides-dist'),
+ ('obsoletes', 'obsoletes-dist'),
+ ('requires', 'requires-dist'))
+
+ get = lambda lab: getattr(dist.metadata, lab.replace('-', '_'))
+ data.update((new, get(old)) for old, new in labels if get(old))
+
+ # 2. retrieve data that requires special processing
+ data['classifier'].update(dist.get_classifiers() or [])
+ data['scripts'].extend(dist.scripts or [])
+ data['packages'].extend(dist.packages or [])
+ data['modules'].extend(dist.py_modules or [])
+ # 2.1 data_files -> resources
+ if dist.data_files:
+ if (len(dist.data_files) < 2 or
+ isinstance(dist.data_files[1], str)):
+ dist.data_files = [('', dist.data_files)]
+ # add tokens in the destination paths
+ vars = {'distribution.name': data['name']}
+ path_tokens = sysconfig.get_paths(vars=vars).items()
+ # sort tokens to use the longest one first
+ path_tokens = sorted(path_tokens, key=lambda x: len(x[1]))
+ for dest, srcs in (dist.data_files or []):
+ dest = os.path.join(sys.prefix, dest)
+ dest = dest.replace(os.path.sep, '/')
+ for tok, path in path_tokens:
+ path = path.replace(os.path.sep, '/')
+ if not dest.startswith(path):
+ continue
+
+ dest = ('{%s}' % tok) + dest[len(path):]
+ files = [('/ '.join(src.rsplit('/', 1)), dest)
+ for src in srcs]
+ data['resources'].extend(files)
+
+ # 2.2 package_data -> extra_files
+ package_dirs = dist.package_dir or {}
+ for package, extras in dist.package_data.items() or []:
+ package_dir = package_dirs.get(package, package)
+ for file_ in extras:
+ if package_dir:
+ file_ = package_dir + '/' + file_
+ data['extra_files'].append(file_)
+
+ # Use README file if its content is the desciption
+ if "description" in data:
+ ref = md5(re.sub('\s', '',
+ self.data['description']).lower().encode())
+ ref = ref.digest()
+ for readme in glob.glob('README*'):
+ with open(readme, encoding='utf-8') as fp:
+ contents = fp.read()
+ contents = re.sub('\s', '', contents.lower()).encode()
+ val = md5(contents).digest()
+ if val == ref:
+ del data['description']
+ data['description-file'] = readme
+ break
+
+ # apply monkey patch to distutils (v1) and setuptools (if needed)
+ # (abort the feature if distutils v1 has been killed)
+ try:
+ from distutils import core
+ core.setup # make sure it's not d2 maskerading as d1
+ except (ImportError, AttributeError):
+ return
+ saved_setups = [(core, core.setup)]
+ core.setup = setup_mock
+ try:
+ import setuptools
+ except ImportError:
+ pass
+ else:
+ saved_setups.append((setuptools, setuptools.setup))
+ setuptools.setup = setup_mock
+ # get metadata by executing the setup.py with the patched setup(...)
+ success = False # for python < 2.4
+ try:
+ load_setup()
+ success = True
+ finally: # revert monkey patches
+ for patched_module, original_setup in saved_setups:
+ patched_module.setup = original_setup
+ if not self.data:
+ raise ValueError('Unable to load metadata from setup.py')
+ return success
+
+ def inspect(self):
+ """Inspect the current working diretory for a name and version.
+
+ This information is harvested in where the directory is named
+ like [name]-[version].
+ """
+ dir_name = os.path.basename(os.getcwd())
+ self.data['name'] = dir_name
+ match = re.match(r'(.*)-(\d.+)', dir_name)
+ if match:
+ self.data['name'] = match.group(1)
+ self.data['version'] = match.group(2)
+ # TODO needs testing!
+ if not is_valid_version(self.data['version']):
+ msg = "Invalid version discovered: %s" % self.data['version']
+ raise ValueError(msg)
+
+ def query_user(self):
+ self.data['name'] = ask('Project name', self.data['name'],
+ _helptext['name'])
+
+ self.data['version'] = ask('Current version number',
+ self.data.get('version'), _helptext['version'])
+ self.data['summary'] = ask('Project description summary',
+ self.data.get('summary'), _helptext['summary'],
+ lengthy=True)
+ self.data['author'] = ask('Author name',
+ self.data.get('author'), _helptext['author'])
+ self.data['author_email'] = ask('Author email address',
+ self.data.get('author_email'), _helptext['author_email'])
+ self.data['home_page'] = ask('Project home page',
+ self.data.get('home_page'), _helptext['home_page'],
+ required=False)
+
+ if ask_yn('Do you want me to automatically build the file list '
+ 'with everything I can find in the current directory? '
+ 'If you say no, you will have to define them manually.') == 'y':
+ self._find_files()
+ else:
+ while ask_yn('Do you want to add a single module?'
+ ' (you will be able to add full packages next)',
+ helptext=_helptext['modules']) == 'y':
+ self._set_multi('Module name', 'modules')
+
+ while ask_yn('Do you want to add a package?',
+ helptext=_helptext['packages']) == 'y':
+ self._set_multi('Package name', 'packages')
+
+ while ask_yn('Do you want to add an extra file?',
+ helptext=_helptext['extra_files']) == 'y':
+ self._set_multi('Extra file/dir name', 'extra_files')
+
+ if ask_yn('Do you want to set Trove classifiers?',
+ helptext=_helptext['do_classifier']) == 'y':
+ self.set_classifier()
+
+ def _find_files(self):
+ # we are looking for python modules and packages,
+ # other stuff are added as regular files
+ pkgs = self.data['packages']
+ modules = self.data['modules']
+ extra_files = self.data['extra_files']
+
+ def is_package(path):
+ return os.path.exists(os.path.join(path, '__init__.py'))
+
+ curdir = os.getcwd()
+ scanned = []
+ _pref = ['lib', 'include', 'dist', 'build', '.', '~']
+ _suf = ['.pyc']
+
+ def to_skip(path):
+ path = relative(path)
+
+ for pref in _pref:
+ if path.startswith(pref):
+ return True
+
+ for suf in _suf:
+ if path.endswith(suf):
+ return True
+
+ return False
+
+ def relative(path):
+ return path[len(curdir) + 1:]
+
+ def dotted(path):
+ res = relative(path).replace(os.path.sep, '.')
+ if res.endswith('.py'):
+ res = res[:-len('.py')]
+ return res
+
+ # first pass: packages
+ for root, dirs, files in os.walk(curdir):
+ if to_skip(root):
+ continue
+ for dir_ in sorted(dirs):
+ if to_skip(dir_):
+ continue
+ fullpath = os.path.join(root, dir_)
+ dotted_name = dotted(fullpath)
+ if is_package(fullpath) and dotted_name not in pkgs:
+ pkgs.append(dotted_name)
+ scanned.append(fullpath)
+
+ # modules and extra files
+ for root, dirs, files in os.walk(curdir):
+ if to_skip(root):
+ continue
+
+ if any(root.startswith(path) for path in scanned):
+ continue
+
+ for file in sorted(files):
+ fullpath = os.path.join(root, file)
+ if to_skip(fullpath):
+ continue
+ # single module?
+ if os.path.splitext(file)[-1] == '.py':
+ modules.append(dotted(fullpath))
+ else:
+ extra_files.append(relative(fullpath))
+
+ def _set_multi(self, question, name):
+ existing_values = self.data[name]
+ value = ask(question, helptext=_helptext[name]).strip()
+ if value not in existing_values:
+ existing_values.append(value)
+
+ def set_classifier(self):
+ self.set_maturity_status(self.classifiers)
+ self.set_license(self.classifiers)
+ self.set_other_classifier(self.classifiers)
+
+ def set_other_classifier(self, classifiers):
+ if ask_yn('Do you want to set other trove identifiers?', 'n',
+ _helptext['trove_generic']) != 'y':
+ return
+ self.walk_classifiers(classifiers, [CLASSIFIERS], '')
+
+ def walk_classifiers(self, classifiers, trovepath, desc):
+ trove = trovepath[-1]
+
+ if not trove:
+ return
+
+ for key in sorted(trove):
+ if len(trove[key]) == 0:
+ if ask_yn('Add "%s"' % desc[4:] + ' :: ' + key, 'n') == 'y':
+ classifiers.add(desc[4:] + ' :: ' + key)
+ continue
+
+ if ask_yn('Do you want to set items under\n "%s" (%d sub-items)?'
+ % (key, len(trove[key])), 'n',
+ _helptext['trove_generic']) == 'y':
+ self.walk_classifiers(classifiers, trovepath + [trove[key]],
+ desc + ' :: ' + key)
+
+ def set_license(self, classifiers):
+ while True:
+ license = ask('What license do you use?',
+ helptext=_helptext['trove_license'], required=False)
+ if not license:
+ return
+
+ license_words = license.lower().split(' ')
+ found_list = []
+
+ for index, licence in LICENCES:
+ for word in license_words:
+ if word in licence:
+ found_list.append(index)
+ break
+
+ if len(found_list) == 0:
+ logger.error('Could not find a matching license for "%s"' %
+ license)
+ continue
+
+ question = 'Matching licenses:\n\n'
+
+ for index, list_index in enumerate(found_list):
+ question += ' %s) %s\n' % (index + 1,
+ _CLASSIFIERS_LIST[list_index])
+
+ question += ('\nType the number of the license you wish to use or '
+ '? to try again:')
+ choice = ask(question, required=False)
+
+ if choice == '?':
+ continue
+ if choice == '':
+ return
+
+ try:
+ index = found_list[int(choice) - 1]
+ except ValueError:
+ logger.error(
+ "Invalid selection, type a number from the list above.")
+
+ classifiers.add(_CLASSIFIERS_LIST[index])
+
+ def set_maturity_status(self, classifiers):
+ maturity_name = lambda mat: mat.split('- ')[-1]
+ maturity_question = '''\
+ Please select the project status:
+
+ %s
+
+ Status''' % '\n'.join('%s - %s' % (i, maturity_name(n))
+ for i, n in enumerate(PROJECT_MATURITY))
+ while True:
+ choice = ask(dedent(maturity_question), required=False)
+
+ if choice:
+ try:
+ choice = int(choice) - 1
+ key = PROJECT_MATURITY[choice]
+ classifiers.add(key)
+ return
+ except (IndexError, ValueError):
+ logger.error(
+ "Invalid selection, type a single digit number.")
+
+
+def main():
+ """Main entry point."""
+ program = MainProgram()
+ # # uncomment when implemented
+ # if not program.load_existing_setup_script():
+ # program.inspect_directory()
+ # program.query_user()
+ # program.update_config_file()
+ # program.write_setup_script()
+ # packaging.util.cfg_to_args()
+ program()
diff --git a/Lib/packaging/database.py b/Lib/packaging/database.py
new file mode 100644
index 0000000..b2fcb97
--- /dev/null
+++ b/Lib/packaging/database.py
@@ -0,0 +1,650 @@
+"""PEP 376 implementation."""
+
+import os
+import re
+import csv
+import sys
+import zipimport
+from io import StringIO
+from hashlib import md5
+
+from packaging import logger
+from packaging.errors import PackagingError
+from packaging.version import suggest_normalized_version, VersionPredicate
+from packaging.metadata import Metadata
+
+
+__all__ = [
+ 'Distribution', 'EggInfoDistribution', 'distinfo_dirname',
+ 'get_distributions', 'get_distribution', 'get_file_users',
+ 'provides_distribution', 'obsoletes_distribution',
+ 'enable_cache', 'disable_cache', 'clear_cache',
+ 'get_file_path', 'get_file']
+
+
+# TODO update docs
+
+DIST_FILES = ('INSTALLER', 'METADATA', 'RECORD', 'REQUESTED', 'RESOURCES')
+
+# Cache
+_cache_name = {} # maps names to Distribution instances
+_cache_name_egg = {} # maps names to EggInfoDistribution instances
+_cache_path = {} # maps paths to Distribution instances
+_cache_path_egg = {} # maps paths to EggInfoDistribution instances
+_cache_generated = False # indicates if .dist-info distributions are cached
+_cache_generated_egg = False # indicates if .dist-info and .egg are cached
+_cache_enabled = True
+
+
+def enable_cache():
+ """
+ Enables the internal cache.
+
+ Note that this function will not clear the cache in any case, for that
+ functionality see :func:`clear_cache`.
+ """
+ global _cache_enabled
+
+ _cache_enabled = True
+
+
+def disable_cache():
+ """
+ Disables the internal cache.
+
+ Note that this function will not clear the cache in any case, for that
+ functionality see :func:`clear_cache`.
+ """
+ global _cache_enabled
+
+ _cache_enabled = False
+
+
+def clear_cache():
+ """ Clears the internal cache. """
+ global _cache_generated, _cache_generated_egg
+
+ _cache_name.clear()
+ _cache_name_egg.clear()
+ _cache_path.clear()
+ _cache_path_egg.clear()
+ _cache_generated = False
+ _cache_generated_egg = False
+
+
+def _yield_distributions(include_dist, include_egg, paths):
+ """
+ Yield .dist-info and .egg(-info) distributions, based on the arguments
+
+ :parameter include_dist: yield .dist-info distributions
+ :parameter include_egg: yield .egg(-info) distributions
+ """
+ for path in paths:
+ realpath = os.path.realpath(path)
+ if not os.path.isdir(realpath):
+ continue
+ for dir in os.listdir(realpath):
+ dist_path = os.path.join(realpath, dir)
+ if include_dist and dir.endswith('.dist-info'):
+ yield Distribution(dist_path)
+ elif include_egg and (dir.endswith('.egg-info') or
+ dir.endswith('.egg')):
+ yield EggInfoDistribution(dist_path)
+
+
+def _generate_cache(use_egg_info, paths):
+ global _cache_generated, _cache_generated_egg
+
+ if _cache_generated_egg or (_cache_generated and not use_egg_info):
+ return
+ else:
+ gen_dist = not _cache_generated
+ gen_egg = use_egg_info
+
+ for dist in _yield_distributions(gen_dist, gen_egg, paths):
+ if isinstance(dist, Distribution):
+ _cache_path[dist.path] = dist
+ if dist.name not in _cache_name:
+ _cache_name[dist.name] = []
+ _cache_name[dist.name].append(dist)
+ else:
+ _cache_path_egg[dist.path] = dist
+ if dist.name not in _cache_name_egg:
+ _cache_name_egg[dist.name] = []
+ _cache_name_egg[dist.name].append(dist)
+
+ if gen_dist:
+ _cache_generated = True
+ if gen_egg:
+ _cache_generated_egg = True
+
+
+class Distribution:
+ """Created with the *path* of the ``.dist-info`` directory provided to the
+ constructor. It reads the metadata contained in ``METADATA`` when it is
+ instantiated."""
+
+ name = ''
+ """The name of the distribution."""
+
+ version = ''
+ """The version of the distribution."""
+
+ metadata = None
+ """A :class:`packaging.metadata.Metadata` instance loaded with
+ the distribution's ``METADATA`` file."""
+
+ requested = False
+ """A boolean that indicates whether the ``REQUESTED`` metadata file is
+ present (in other words, whether the package was installed by user
+ request or it was installed as a dependency)."""
+
+ def __init__(self, path):
+ if _cache_enabled and path in _cache_path:
+ self.metadata = _cache_path[path].metadata
+ else:
+ metadata_path = os.path.join(path, 'METADATA')
+ self.metadata = Metadata(path=metadata_path)
+
+ self.name = self.metadata['Name']
+ self.version = self.metadata['Version']
+ self.path = path
+
+ if _cache_enabled and path not in _cache_path:
+ _cache_path[path] = self
+
+ def __repr__(self):
+ return '<Distribution %r %s at %r>' % (
+ self.name, self.version, self.path)
+
+ def _get_records(self, local=False):
+ results = []
+ with self.get_distinfo_file('RECORD') as record:
+ record_reader = csv.reader(record, delimiter=',',
+ lineterminator='\n')
+ for row in record_reader:
+ missing = [None for i in range(len(row), 3)]
+ path, checksum, size = row + missing
+ if local:
+ path = path.replace('/', os.sep)
+ path = os.path.join(sys.prefix, path)
+ results.append((path, checksum, size))
+ return results
+
+ def get_resource_path(self, relative_path):
+ with self.get_distinfo_file('RESOURCES') as resources_file:
+ resources_reader = csv.reader(resources_file, delimiter=',',
+ lineterminator='\n')
+ for relative, destination in resources_reader:
+ if relative == relative_path:
+ return destination
+ raise KeyError(
+ 'no resource file with relative path %r is installed' %
+ relative_path)
+
+ def list_installed_files(self, local=False):
+ """
+ Iterates over the ``RECORD`` entries and returns a tuple
+ ``(path, md5, size)`` for each line. If *local* is ``True``,
+ the returned path is transformed into a local absolute path.
+ Otherwise the raw value from RECORD is returned.
+
+ A local absolute path is an absolute path in which occurrences of
+ ``'/'`` have been replaced by the system separator given by ``os.sep``.
+
+ :parameter local: flag to say if the path should be returned as a local
+ absolute path
+
+ :type local: boolean
+ :returns: iterator of (path, md5, size)
+ """
+ for result in self._get_records(local):
+ yield result
+
+ def uses(self, path):
+ """
+ Returns ``True`` if path is listed in ``RECORD``. *path* can be a local
+ absolute path or a relative ``'/'``-separated path.
+
+ :rtype: boolean
+ """
+ for p, checksum, size in self._get_records():
+ local_absolute = os.path.join(sys.prefix, p)
+ if path == p or path == local_absolute:
+ return True
+ return False
+
+ def get_distinfo_file(self, path, binary=False):
+ """
+ Returns a file located under the ``.dist-info`` directory. Returns a
+ ``file`` instance for the file pointed by *path*.
+
+ :parameter path: a ``'/'``-separated path relative to the
+ ``.dist-info`` directory or an absolute path;
+ If *path* is an absolute path and doesn't start
+ with the ``.dist-info`` directory path,
+ a :class:`PackagingError` is raised
+ :type path: string
+ :parameter binary: If *binary* is ``True``, opens the file in read-only
+ binary mode (``rb``), otherwise opens it in
+ read-only mode (``r``).
+ :rtype: file object
+ """
+ open_flags = 'r'
+ if binary:
+ open_flags += 'b'
+
+ # Check if it is an absolute path # XXX use relpath, add tests
+ if path.find(os.sep) >= 0:
+ # it's an absolute path?
+ distinfo_dirname, path = path.split(os.sep)[-2:]
+ if distinfo_dirname != self.path.split(os.sep)[-1]:
+ raise PackagingError(
+ 'dist-info file %r does not belong to the %r %s '
+ 'distribution' % (path, self.name, self.version))
+
+ # The file must be relative
+ if path not in DIST_FILES:
+ raise PackagingError('invalid path for a dist-info file: %r' %
+ path)
+
+ path = os.path.join(self.path, path)
+ return open(path, open_flags)
+
+ def list_distinfo_files(self, local=False):
+ """
+ Iterates over the ``RECORD`` entries and returns paths for each line if
+ the path is pointing to a file located in the ``.dist-info`` directory
+ or one of its subdirectories.
+
+ :parameter local: If *local* is ``True``, each returned path is
+ transformed into a local absolute path. Otherwise the
+ raw value from ``RECORD`` is returned.
+ :type local: boolean
+ :returns: iterator of paths
+ """
+ for path, checksum, size in self._get_records(local):
+ # XXX add separator or use real relpath algo
+ if path.startswith(self.path):
+ yield path
+
+ def __eq__(self, other):
+ return isinstance(other, Distribution) and self.path == other.path
+
+ # See http://docs.python.org/reference/datamodel#object.__hash__
+ __hash__ = object.__hash__
+
+
+class EggInfoDistribution:
+ """Created with the *path* of the ``.egg-info`` directory or file provided
+ to the constructor. It reads the metadata contained in the file itself, or
+ if the given path happens to be a directory, the metadata is read from the
+ file ``PKG-INFO`` under that directory."""
+
+ name = ''
+ """The name of the distribution."""
+
+ version = ''
+ """The version of the distribution."""
+
+ metadata = None
+ """A :class:`packaging.metadata.Metadata` instance loaded with
+ the distribution's ``METADATA`` file."""
+
+ _REQUIREMENT = re.compile(
+ r'(?P<name>[-A-Za-z0-9_.]+)\s*'
+ r'(?P<first>(?:<|<=|!=|==|>=|>)[-A-Za-z0-9_.]+)?\s*'
+ r'(?P<rest>(?:\s*,\s*(?:<|<=|!=|==|>=|>)[-A-Za-z0-9_.]+)*)\s*'
+ r'(?P<extras>\[.*\])?')
+
+ def __init__(self, path):
+ self.path = path
+ if _cache_enabled and path in _cache_path_egg:
+ self.metadata = _cache_path_egg[path].metadata
+ self.name = self.metadata['Name']
+ self.version = self.metadata['Version']
+ return
+
+ # reused from Distribute's pkg_resources
+ def yield_lines(strs):
+ """Yield non-empty/non-comment lines of a ``basestring``
+ or sequence"""
+ if isinstance(strs, str):
+ for s in strs.splitlines():
+ s = s.strip()
+ # skip blank lines/comments
+ if s and not s.startswith('#'):
+ yield s
+ else:
+ for ss in strs:
+ for s in yield_lines(ss):
+ yield s
+
+ requires = None
+
+ if path.endswith('.egg'):
+ if os.path.isdir(path):
+ meta_path = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
+ self.metadata = Metadata(path=meta_path)
+ try:
+ req_path = os.path.join(path, 'EGG-INFO', 'requires.txt')
+ with open(req_path, 'r') as fp:
+ requires = fp.read()
+ except IOError:
+ requires = None
+ else:
+ # FIXME handle the case where zipfile is not available
+ zipf = zipimport.zipimporter(path)
+ fileobj = StringIO(
+ zipf.get_data('EGG-INFO/PKG-INFO').decode('utf8'))
+ self.metadata = Metadata(fileobj=fileobj)
+ try:
+ requires = zipf.get_data('EGG-INFO/requires.txt')
+ except IOError:
+ requires = None
+ self.name = self.metadata['Name']
+ self.version = self.metadata['Version']
+
+ elif path.endswith('.egg-info'):
+ if os.path.isdir(path):
+ path = os.path.join(path, 'PKG-INFO')
+ try:
+ with open(os.path.join(path, 'requires.txt'), 'r') as fp:
+ requires = fp.read()
+ except IOError:
+ requires = None
+ self.metadata = Metadata(path=path)
+ self.name = self.metadata['Name']
+ self.version = self.metadata['Version']
+
+ else:
+ raise ValueError('path must end with .egg-info or .egg, got %r' %
+ path)
+
+ if requires is not None:
+ if self.metadata['Metadata-Version'] == '1.1':
+ # we can't have 1.1 metadata *and* Setuptools requires
+ for field in ('Obsoletes', 'Requires', 'Provides'):
+ del self.metadata[field]
+
+ reqs = []
+
+ if requires is not None:
+ for line in yield_lines(requires):
+ if line.startswith('['):
+ logger.warning(
+ 'extensions in requires.txt are not supported '
+ '(used by %r %s)', self.name, self.version)
+ break
+ else:
+ match = self._REQUIREMENT.match(line.strip())
+ if not match:
+ # this happens when we encounter extras; since they
+ # are written at the end of the file we just exit
+ break
+ else:
+ if match.group('extras'):
+ msg = ('extra requirements are not supported '
+ '(used by %r %s)', self.name, self.version)
+ logger.warning(msg, self.name)
+ name = match.group('name')
+ version = None
+ if match.group('first'):
+ version = match.group('first')
+ if match.group('rest'):
+ version += match.group('rest')
+ version = version.replace(' ', '') # trim spaces
+ if version is None:
+ reqs.append(name)
+ else:
+ reqs.append('%s (%s)' % (name, version))
+
+ if len(reqs) > 0:
+ self.metadata['Requires-Dist'] += reqs
+
+ if _cache_enabled:
+ _cache_path_egg[self.path] = self
+
+ def __repr__(self):
+ return '<EggInfoDistribution %r %s at %r>' % (
+ self.name, self.version, self.path)
+
+ def list_installed_files(self, local=False):
+
+ def _md5(path):
+ with open(path, 'rb') as f:
+ content = f.read()
+ return md5(content).hexdigest()
+
+ def _size(path):
+ return os.stat(path).st_size
+
+ path = self.path
+ if local:
+ path = path.replace('/', os.sep)
+
+ # XXX What about scripts and data files ?
+ if os.path.isfile(path):
+ return [(path, _md5(path), _size(path))]
+ else:
+ files = []
+ for root, dir, files_ in os.walk(path):
+ for item in files_:
+ item = os.path.join(root, item)
+ files.append((item, _md5(item), _size(item)))
+ return files
+
+ return []
+
+ def uses(self, path):
+ return False
+
+ def __eq__(self, other):
+ return (isinstance(other, EggInfoDistribution) and
+ self.path == other.path)
+
+ # See http://docs.python.org/reference/datamodel#object.__hash__
+ __hash__ = object.__hash__
+
+
+def distinfo_dirname(name, version):
+ """
+ The *name* and *version* parameters are converted into their
+ filename-escaped form, i.e. any ``'-'`` characters are replaced
+ with ``'_'`` other than the one in ``'dist-info'`` and the one
+ separating the name from the version number.
+
+ :parameter name: is converted to a standard distribution name by replacing
+ any runs of non- alphanumeric characters with a single
+ ``'-'``.
+ :type name: string
+ :parameter version: is converted to a standard version string. Spaces
+ become dots, and all other non-alphanumeric characters
+ (except dots) become dashes, with runs of multiple
+ dashes condensed to a single dash.
+ :type version: string
+ :returns: directory name
+ :rtype: string"""
+ file_extension = '.dist-info'
+ name = name.replace('-', '_')
+ normalized_version = suggest_normalized_version(version)
+ # Because this is a lookup procedure, something will be returned even if
+ # it is a version that cannot be normalized
+ if normalized_version is None:
+ # Unable to achieve normality?
+ normalized_version = version
+ return '-'.join([name, normalized_version]) + file_extension
+
+
+def get_distributions(use_egg_info=False, paths=None):
+ """
+ Provides an iterator that looks for ``.dist-info`` directories in
+ ``sys.path`` and returns :class:`Distribution` instances for each one of
+ them. If the parameters *use_egg_info* is ``True``, then the ``.egg-info``
+ files and directores are iterated as well.
+
+ :rtype: iterator of :class:`Distribution` and :class:`EggInfoDistribution`
+ instances
+ """
+ if paths is None:
+ paths = sys.path
+
+ if not _cache_enabled:
+ for dist in _yield_distributions(True, use_egg_info, paths):
+ yield dist
+ else:
+ _generate_cache(use_egg_info, paths)
+
+ for dist in _cache_path.values():
+ yield dist
+
+ if use_egg_info:
+ for dist in _cache_path_egg.values():
+ yield dist
+
+
+def get_distribution(name, use_egg_info=False, paths=None):
+ """
+ Scans all elements in ``sys.path`` and looks for all directories
+ ending with ``.dist-info``. Returns a :class:`Distribution`
+ corresponding to the ``.dist-info`` directory that contains the
+ ``METADATA`` that matches *name* for the *name* metadata field.
+ If no distribution exists with the given *name* and the parameter
+ *use_egg_info* is set to ``True``, then all files and directories ending
+ with ``.egg-info`` are scanned. A :class:`EggInfoDistribution` instance is
+ returned if one is found that has metadata that matches *name* for the
+ *name* metadata field.
+
+ This function only returns the first result found, as no more than one
+ value is expected. If the directory is not found, ``None`` is returned.
+
+ :rtype: :class:`Distribution` or :class:`EggInfoDistribution` or None
+ """
+ if paths is None:
+ paths = sys.path
+
+ if not _cache_enabled:
+ for dist in _yield_distributions(True, use_egg_info, paths):
+ if dist.name == name:
+ return dist
+ else:
+ _generate_cache(use_egg_info, paths)
+
+ if name in _cache_name:
+ return _cache_name[name][0]
+ elif use_egg_info and name in _cache_name_egg:
+ return _cache_name_egg[name][0]
+ else:
+ return None
+
+
+def obsoletes_distribution(name, version=None, use_egg_info=False):
+ """
+ Iterates over all distributions to find which distributions obsolete
+ *name*.
+
+ If a *version* is provided, it will be used to filter the results.
+ If the argument *use_egg_info* is set to ``True``, then ``.egg-info``
+ distributions will be considered as well.
+
+ :type name: string
+ :type version: string
+ :parameter name:
+ """
+ for dist in get_distributions(use_egg_info):
+ obsoleted = (dist.metadata['Obsoletes-Dist'] +
+ dist.metadata['Obsoletes'])
+ for obs in obsoleted:
+ o_components = obs.split(' ', 1)
+ if len(o_components) == 1 or version is None:
+ if name == o_components[0]:
+ yield dist
+ break
+ else:
+ try:
+ predicate = VersionPredicate(obs)
+ except ValueError:
+ raise PackagingError(
+ 'distribution %r has ill-formed obsoletes field: '
+ '%r' % (dist.name, obs))
+ if name == o_components[0] and predicate.match(version):
+ yield dist
+ break
+
+
+def provides_distribution(name, version=None, use_egg_info=False):
+ """
+ Iterates over all distributions to find which distributions provide *name*.
+ If a *version* is provided, it will be used to filter the results. Scans
+ all elements in ``sys.path`` and looks for all directories ending with
+ ``.dist-info``. Returns a :class:`Distribution` corresponding to the
+ ``.dist-info`` directory that contains a ``METADATA`` that matches *name*
+ for the name metadata. If the argument *use_egg_info* is set to ``True``,
+ then all files and directories ending with ``.egg-info`` are considered
+ as well and returns an :class:`EggInfoDistribution` instance.
+
+ This function only returns the first result found, since no more than
+ one values are expected. If the directory is not found, returns ``None``.
+
+ :parameter version: a version specifier that indicates the version
+ required, conforming to the format in ``PEP-345``
+
+ :type name: string
+ :type version: string
+ """
+ predicate = None
+ if not version is None:
+ try:
+ predicate = VersionPredicate(name + ' (' + version + ')')
+ except ValueError:
+ raise PackagingError('invalid name or version: %r, %r' %
+ (name, version))
+
+ for dist in get_distributions(use_egg_info):
+ provided = dist.metadata['Provides-Dist'] + dist.metadata['Provides']
+
+ for p in provided:
+ p_components = p.rsplit(' ', 1)
+ if len(p_components) == 1 or predicate is None:
+ if name == p_components[0]:
+ yield dist
+ break
+ else:
+ p_name, p_ver = p_components
+ if len(p_ver) < 2 or p_ver[0] != '(' or p_ver[-1] != ')':
+ raise PackagingError(
+ 'distribution %r has invalid Provides field: %r' %
+ (dist.name, p))
+ p_ver = p_ver[1:-1] # trim off the parenthesis
+ if p_name == name and predicate.match(p_ver):
+ yield dist
+ break
+
+
+def get_file_users(path):
+ """
+ Iterates over all distributions to find out which distributions use
+ *path*.
+
+ :parameter path: can be a local absolute path or a relative
+ ``'/'``-separated path.
+ :type path: string
+ :rtype: iterator of :class:`Distribution` instances
+ """
+ for dist in get_distributions():
+ if dist.uses(path):
+ yield dist
+
+
+def get_file_path(distribution_name, relative_path):
+ """Return the path to a resource file."""
+ dist = get_distribution(distribution_name)
+ if dist is not None:
+ return dist.get_resource_path(relative_path)
+ raise LookupError('no distribution named %r found' % distribution_name)
+
+
+def get_file(distribution_name, relative_path, *args, **kwargs):
+ """Open and return a resource file."""
+ return open(get_file_path(distribution_name, relative_path),
+ *args, **kwargs)
diff --git a/Lib/packaging/depgraph.py b/Lib/packaging/depgraph.py
new file mode 100644
index 0000000..d633b63
--- /dev/null
+++ b/Lib/packaging/depgraph.py
@@ -0,0 +1,270 @@
+"""Class and functions dealing with dependencies between distributions.
+
+This module provides a DependencyGraph class to represent the
+dependencies between distributions. Auxiliary functions can generate a
+graph, find reverse dependencies, and print a graph in DOT format.
+"""
+
+import sys
+
+from io import StringIO
+from packaging.errors import PackagingError
+from packaging.version import VersionPredicate, IrrationalVersionError
+
+__all__ = ['DependencyGraph', 'generate_graph', 'dependent_dists',
+ 'graph_to_dot']
+
+
+class DependencyGraph:
+ """
+ Represents a dependency graph between distributions.
+
+ The dependency relationships are stored in an ``adjacency_list`` that maps
+ distributions to a list of ``(other, label)`` tuples where ``other``
+ is a distribution and the edge is labeled with ``label`` (i.e. the version
+ specifier, if such was provided). Also, for more efficient traversal, for
+ every distribution ``x``, a list of predecessors is kept in
+ ``reverse_list[x]``. An edge from distribution ``a`` to
+ distribution ``b`` means that ``a`` depends on ``b``. If any missing
+ dependencies are found, they are stored in ``missing``, which is a
+ dictionary that maps distributions to a list of requirements that were not
+ provided by any other distributions.
+ """
+
+ def __init__(self):
+ self.adjacency_list = {}
+ self.reverse_list = {}
+ self.missing = {}
+
+ def add_distribution(self, distribution):
+ """Add the *distribution* to the graph.
+
+ :type distribution: :class:`packaging.database.Distribution` or
+ :class:`packaging.database.EggInfoDistribution`
+ """
+ self.adjacency_list[distribution] = []
+ self.reverse_list[distribution] = []
+ self.missing[distribution] = []
+
+ def add_edge(self, x, y, label=None):
+ """Add an edge from distribution *x* to distribution *y* with the given
+ *label*.
+
+ :type x: :class:`packaging.database.Distribution` or
+ :class:`packaging.database.EggInfoDistribution`
+ :type y: :class:`packaging.database.Distribution` or
+ :class:`packaging.database.EggInfoDistribution`
+ :type label: ``str`` or ``None``
+ """
+ self.adjacency_list[x].append((y, label))
+ # multiple edges are allowed, so be careful
+ if x not in self.reverse_list[y]:
+ self.reverse_list[y].append(x)
+
+ def add_missing(self, distribution, requirement):
+ """
+ Add a missing *requirement* for the given *distribution*.
+
+ :type distribution: :class:`packaging.database.Distribution` or
+ :class:`packaging.database.EggInfoDistribution`
+ :type requirement: ``str``
+ """
+ self.missing[distribution].append(requirement)
+
+ def _repr_dist(self, dist):
+ return '%r %s' % (dist.name, dist.version)
+
+ def repr_node(self, dist, level=1):
+ """Prints only a subgraph"""
+ output = []
+ output.append(self._repr_dist(dist))
+ for other, label in self.adjacency_list[dist]:
+ dist = self._repr_dist(other)
+ if label is not None:
+ dist = '%s [%s]' % (dist, label)
+ output.append(' ' * level + str(dist))
+ suboutput = self.repr_node(other, level + 1)
+ subs = suboutput.split('\n')
+ output.extend(subs[1:])
+ return '\n'.join(output)
+
+ def __repr__(self):
+ """Representation of the graph"""
+ output = []
+ for dist, adjs in self.adjacency_list.items():
+ output.append(self.repr_node(dist))
+ return '\n'.join(output)
+
+
+def graph_to_dot(graph, f, skip_disconnected=True):
+ """Writes a DOT output for the graph to the provided file *f*.
+
+ If *skip_disconnected* is set to ``True``, then all distributions
+ that are not dependent on any other distribution are skipped.
+
+ :type f: has to support ``file``-like operations
+ :type skip_disconnected: ``bool``
+ """
+ disconnected = []
+
+ f.write("digraph dependencies {\n")
+ for dist, adjs in graph.adjacency_list.items():
+ if len(adjs) == 0 and not skip_disconnected:
+ disconnected.append(dist)
+ for other, label in adjs:
+ if not label is None:
+ f.write('"%s" -> "%s" [label="%s"]\n' %
+ (dist.name, other.name, label))
+ else:
+ f.write('"%s" -> "%s"\n' % (dist.name, other.name))
+ if not skip_disconnected and len(disconnected) > 0:
+ f.write('subgraph disconnected {\n')
+ f.write('label = "Disconnected"\n')
+ f.write('bgcolor = red\n')
+
+ for dist in disconnected:
+ f.write('"%s"' % dist.name)
+ f.write('\n')
+ f.write('}\n')
+ f.write('}\n')
+
+
+def generate_graph(dists):
+ """Generates a dependency graph from the given distributions.
+
+ :parameter dists: a list of distributions
+ :type dists: list of :class:`packaging.database.Distribution` and
+ :class:`packaging.database.EggInfoDistribution` instances
+ :rtype: a :class:`DependencyGraph` instance
+ """
+ graph = DependencyGraph()
+ provided = {} # maps names to lists of (version, dist) tuples
+
+ # first, build the graph and find out the provides
+ for dist in dists:
+ graph.add_distribution(dist)
+ provides = (dist.metadata['Provides-Dist'] +
+ dist.metadata['Provides'] +
+ ['%s (%s)' % (dist.name, dist.version)])
+
+ for p in provides:
+ comps = p.strip().rsplit(" ", 1)
+ name = comps[0]
+ version = None
+ if len(comps) == 2:
+ version = comps[1]
+ if len(version) < 3 or version[0] != '(' or version[-1] != ')':
+ raise PackagingError('distribution %r has ill-formed'
+ 'provides field: %r' % (dist.name, p))
+ version = version[1:-1] # trim off parenthesis
+ if name not in provided:
+ provided[name] = []
+ provided[name].append((version, dist))
+
+ # now make the edges
+ for dist in dists:
+ requires = dist.metadata['Requires-Dist'] + dist.metadata['Requires']
+ for req in requires:
+ try:
+ predicate = VersionPredicate(req)
+ except IrrationalVersionError:
+ # XXX compat-mode if cannot read the version
+ name = req.split()[0]
+ predicate = VersionPredicate(name)
+
+ name = predicate.name
+
+ if name not in provided:
+ graph.add_missing(dist, req)
+ else:
+ matched = False
+ for version, provider in provided[name]:
+ try:
+ match = predicate.match(version)
+ except IrrationalVersionError:
+ # XXX small compat-mode
+ if version.split(' ') == 1:
+ match = True
+ else:
+ match = False
+
+ if match:
+ graph.add_edge(dist, provider, req)
+ matched = True
+ break
+ if not matched:
+ graph.add_missing(dist, req)
+ return graph
+
+
+def dependent_dists(dists, dist):
+ """Recursively generate a list of distributions from *dists* that are
+ dependent on *dist*.
+
+ :param dists: a list of distributions
+ :param dist: a distribution, member of *dists* for which we are interested
+ """
+ if dist not in dists:
+ raise ValueError('given distribution %r is not a member of the list' %
+ dist.name)
+ graph = generate_graph(dists)
+
+ dep = [dist] # dependent distributions
+ fringe = graph.reverse_list[dist] # list of nodes we should inspect
+
+ while not len(fringe) == 0:
+ node = fringe.pop()
+ dep.append(node)
+ for prev in graph.reverse_list[node]:
+ if prev not in dep:
+ fringe.append(prev)
+
+ dep.pop(0) # remove dist from dep, was there to prevent infinite loops
+ return dep
+
+
+def main():
+ # XXX move to run._graph
+ from packaging.database import get_distributions
+ tempout = StringIO()
+ try:
+ old = sys.stderr
+ sys.stderr = tempout
+ try:
+ dists = list(get_distributions(use_egg_info=True))
+ graph = generate_graph(dists)
+ finally:
+ sys.stderr = old
+ except Exception as e:
+ tempout.seek(0)
+ tempout = tempout.read()
+ print('Could not generate the graph')
+ print(tempout)
+ print(e)
+ sys.exit(1)
+
+ for dist, reqs in graph.missing.items():
+ if len(reqs) > 0:
+ print("Warning: Missing dependencies for %r:" % dist.name,
+ ", ".join(reqs))
+ # XXX replace with argparse
+ if len(sys.argv) == 1:
+ print('Dependency graph:')
+ print(' ', repr(graph).replace('\n', '\n '))
+ sys.exit(0)
+ elif len(sys.argv) > 1 and sys.argv[1] in ('-d', '--dot'):
+ if len(sys.argv) > 2:
+ filename = sys.argv[2]
+ else:
+ filename = 'depgraph.dot'
+
+ with open(filename, 'w') as f:
+ graph_to_dot(graph, f, True)
+ tempout.seek(0)
+ tempout = tempout.read()
+ print(tempout)
+ print('Dot file written at %r' % filename)
+ sys.exit(0)
+ else:
+ print('Supported option: -d [filename]')
+ sys.exit(1)
diff --git a/Lib/packaging/dist.py b/Lib/packaging/dist.py
new file mode 100644
index 0000000..607767e
--- /dev/null
+++ b/Lib/packaging/dist.py
@@ -0,0 +1,769 @@
+"""Class representing the project being built/installed/etc."""
+
+import os
+import re
+
+from packaging import logger
+from packaging.util import strtobool, resolve_name
+from packaging.config import Config
+from packaging.errors import (PackagingOptionError, PackagingArgError,
+ PackagingModuleError, PackagingClassError)
+from packaging.command import get_command_class, STANDARD_COMMANDS
+from packaging.command.cmd import Command
+from packaging.metadata import Metadata
+from packaging.fancy_getopt import FancyGetopt
+
+# Regex to define acceptable Packaging command names. This is not *quite*
+# the same as a Python name -- leading underscores are not allowed. The fact
+# that they're very similar is no coincidence: the default naming scheme is
+# to look for a Python module named after the command.
+command_re = re.compile(r'^[a-zA-Z]([a-zA-Z0-9_]*)$')
+
+USAGE = """\
+usage: %(script)s [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
+ or: %(script)s --help [cmd1 cmd2 ...]
+ or: %(script)s --help-commands
+ or: %(script)s cmd --help
+"""
+
+
+def gen_usage(script_name):
+ script = os.path.basename(script_name)
+ return USAGE % {'script': script}
+
+
+class Distribution:
+ """Class used to represent a project and work with it.
+
+ Most of the work hiding behind 'pysetup run' is really done within a
+ Distribution instance, which farms the work out to the commands
+ specified on the command line.
+ """
+
+ # 'global_options' describes the command-line options that may be
+ # supplied to the setup script prior to any actual commands.
+ # Eg. "pysetup run -n" or "pysetup run --dry-run" both take advantage of
+ # these global options. This list should be kept to a bare minimum,
+ # since every global option is also valid as a command option -- and we
+ # don't want to pollute the commands with too many options that they
+ # have minimal control over.
+ global_options = [
+ ('dry-run', 'n', "don't actually do anything"),
+ ('help', 'h', "show detailed help message"),
+ ('no-user-cfg', None, 'ignore pydistutils.cfg in your home directory'),
+ ]
+
+ # 'common_usage' is a short (2-3 line) string describing the common
+ # usage of the setup script.
+ common_usage = """\
+Common commands: (see '--help-commands' for more)
+
+ pysetup run build will build the project underneath 'build/'
+ pysetup run install will install the project
+"""
+
+ # options that are not propagated to the commands
+ display_options = [
+ ('help-commands', None,
+ "list all available commands"),
+ ('use-2to3', None,
+ "use 2to3 to make source python 3.x compatible"),
+ ('convert-2to3-doctests', None,
+ "use 2to3 to convert doctests in separate text files"),
+ ]
+ display_option_names = [x[0].replace('-', '_') for x in display_options]
+
+ # negative options are options that exclude other options
+ negative_opt = {}
+
+ # -- Creation/initialization methods -------------------------------
+ def __init__(self, attrs=None):
+ """Construct a new Distribution instance: initialize all the
+ attributes of a Distribution, and then use 'attrs' (a dictionary
+ mapping attribute names to values) to assign some of those
+ attributes their "real" values. (Any attributes not mentioned in
+ 'attrs' will be assigned to some null value: 0, None, an empty list
+ or dictionary, etc.) Most importantly, initialize the
+ 'command_obj' attribute to the empty dictionary; this will be
+ filled in with real command objects by 'parse_command_line()'.
+ """
+
+ # Default values for our command-line options
+ self.dry_run = False
+ self.help = False
+ for attr in self.display_option_names:
+ setattr(self, attr, False)
+
+ # Store the configuration
+ self.config = Config(self)
+
+ # Store the distribution metadata (name, version, author, and so
+ # forth) in a separate object -- we're getting to have enough
+ # information here (and enough command-line options) that it's
+ # worth it.
+ self.metadata = Metadata()
+
+ # 'cmdclass' maps command names to class objects, so we
+ # can 1) quickly figure out which class to instantiate when
+ # we need to create a new command object, and 2) have a way
+ # for the setup script to override command classes
+ self.cmdclass = {}
+
+ # 'script_name' and 'script_args' are usually set to sys.argv[0]
+ # and sys.argv[1:], but they can be overridden when the caller is
+ # not necessarily a setup script run from the command line.
+ self.script_name = None
+ self.script_args = None
+
+ # 'command_options' is where we store command options between
+ # parsing them (from config files, the command line, etc.) and when
+ # they are actually needed -- ie. when the command in question is
+ # instantiated. It is a dictionary of dictionaries of 2-tuples:
+ # command_options = { command_name : { option : (source, value) } }
+ self.command_options = {}
+
+ # 'dist_files' is the list of (command, pyversion, file) that
+ # have been created by any dist commands run so far. This is
+ # filled regardless of whether the run is dry or not. pyversion
+ # gives sysconfig.get_python_version() if the dist file is
+ # specific to a Python version, 'any' if it is good for all
+ # Python versions on the target platform, and '' for a source
+ # file. pyversion should not be used to specify minimum or
+ # maximum required Python versions; use the metainfo for that
+ # instead.
+ self.dist_files = []
+
+ # These options are really the business of various commands, rather
+ # than of the Distribution itself. We provide aliases for them in
+ # Distribution as a convenience to the developer.
+ self.packages = []
+ self.package_data = {}
+ self.package_dir = None
+ self.py_modules = []
+ self.libraries = []
+ self.headers = []
+ self.ext_modules = []
+ self.ext_package = None
+ self.include_dirs = []
+ self.extra_path = None
+ self.scripts = []
+ self.data_files = {}
+ self.password = ''
+ self.use_2to3 = False
+ self.convert_2to3_doctests = []
+ self.extra_files = []
+
+ # And now initialize bookkeeping stuff that can't be supplied by
+ # the caller at all. 'command_obj' maps command names to
+ # Command instances -- that's how we enforce that every command
+ # class is a singleton.
+ self.command_obj = {}
+
+ # 'have_run' maps command names to boolean values; it keeps track
+ # of whether we have actually run a particular command, to make it
+ # cheap to "run" a command whenever we think we might need to -- if
+ # it's already been done, no need for expensive filesystem
+ # operations, we just check the 'have_run' dictionary and carry on.
+ # It's only safe to query 'have_run' for a command class that has
+ # been instantiated -- a false value will be inserted when the
+ # command object is created, and replaced with a true value when
+ # the command is successfully run. Thus it's probably best to use
+ # '.get()' rather than a straight lookup.
+ self.have_run = {}
+
+ # Now we'll use the attrs dictionary (ultimately, keyword args from
+ # the setup script) to possibly override any or all of these
+ # distribution options.
+
+ if attrs is not None:
+ # Pull out the set of command options and work on them
+ # specifically. Note that this order guarantees that aliased
+ # command options will override any supplied redundantly
+ # through the general options dictionary.
+ options = attrs.get('options')
+ if options is not None:
+ del attrs['options']
+ for command, cmd_options in options.items():
+ opt_dict = self.get_option_dict(command)
+ for opt, val in cmd_options.items():
+ opt_dict[opt] = ("setup script", val)
+
+ # Now work on the rest of the attributes. Any attribute that's
+ # not already defined is invalid!
+ for key, val in attrs.items():
+ if self.metadata.is_metadata_field(key):
+ self.metadata[key] = val
+ elif hasattr(self, key):
+ setattr(self, key, val)
+ else:
+ logger.warning(
+ 'unknown argument given to Distribution: %r', key)
+
+ # no-user-cfg is handled before other command line args
+ # because other args override the config files, and this
+ # one is needed before we can load the config files.
+ # If attrs['script_args'] wasn't passed, assume false.
+ #
+ # This also make sure we just look at the global options
+ self.want_user_cfg = True
+
+ if self.script_args is not None:
+ for arg in self.script_args:
+ if not arg.startswith('-'):
+ break
+ if arg == '--no-user-cfg':
+ self.want_user_cfg = False
+ break
+
+ self.finalize_options()
+
+ def get_option_dict(self, command):
+ """Get the option dictionary for a given command. If that
+ command's option dictionary hasn't been created yet, then create it
+ and return the new dictionary; otherwise, return the existing
+ option dictionary.
+ """
+ d = self.command_options.get(command)
+ if d is None:
+ d = self.command_options[command] = {}
+ return d
+
+ def get_fullname(self, filesafe=False):
+ return self.metadata.get_fullname(filesafe)
+
+ def dump_option_dicts(self, header=None, commands=None, indent=""):
+ from pprint import pformat
+
+ if commands is None: # dump all command option dicts
+ commands = sorted(self.command_options)
+
+ if header is not None:
+ logger.info(indent + header)
+ indent = indent + " "
+
+ if not commands:
+ logger.info(indent + "no commands known yet")
+ return
+
+ for cmd_name in commands:
+ opt_dict = self.command_options.get(cmd_name)
+ if opt_dict is None:
+ logger.info(indent + "no option dict for %r command",
+ cmd_name)
+ else:
+ logger.info(indent + "option dict for %r command:", cmd_name)
+ out = pformat(opt_dict)
+ for line in out.split('\n'):
+ logger.info(indent + " " + line)
+
+ # -- Config file finding/parsing methods ---------------------------
+ # XXX to be removed
+ def parse_config_files(self, filenames=None):
+ return self.config.parse_config_files(filenames)
+
+ def find_config_files(self):
+ return self.config.find_config_files()
+
+ # -- Command-line parsing methods ----------------------------------
+
+ def parse_command_line(self):
+ """Parse the setup script's command line, taken from the
+ 'script_args' instance attribute (which defaults to 'sys.argv[1:]'
+ -- see 'setup()' in run.py). This list is first processed for
+ "global options" -- options that set attributes of the Distribution
+ instance. Then, it is alternately scanned for Packaging commands
+ and options for that command. Each new command terminates the
+ options for the previous command. The allowed options for a
+ command are determined by the 'user_options' attribute of the
+ command class -- thus, we have to be able to load command classes
+ in order to parse the command line. Any error in that 'options'
+ attribute raises PackagingGetoptError; any error on the
+ command line raises PackagingArgError. If no Packaging commands
+ were found on the command line, raises PackagingArgError. Return
+ true if command line was successfully parsed and we should carry
+ on with executing commands; false if no errors but we shouldn't
+ execute commands (currently, this only happens if user asks for
+ help).
+ """
+ #
+ # We now have enough information to show the Macintosh dialog
+ # that allows the user to interactively specify the "command line".
+ #
+ toplevel_options = self._get_toplevel_options()
+
+ # We have to parse the command line a bit at a time -- global
+ # options, then the first command, then its options, and so on --
+ # because each command will be handled by a different class, and
+ # the options that are valid for a particular class aren't known
+ # until we have loaded the command class, which doesn't happen
+ # until we know what the command is.
+
+ self.commands = []
+ parser = FancyGetopt(toplevel_options + self.display_options)
+ parser.set_negative_aliases(self.negative_opt)
+ args = parser.getopt(args=self.script_args, object=self)
+ option_order = parser.get_option_order()
+
+ # for display options we return immediately
+ if self.handle_display_options(option_order):
+ return
+
+ while args:
+ args = self._parse_command_opts(parser, args)
+ if args is None: # user asked for help (and got it)
+ return
+
+ # Handle the cases of --help as a "global" option, ie.
+ # "pysetup run --help" and "pysetup run --help command ...". For the
+ # former, we show global options (--dry-run, etc.)
+ # and display-only options (--name, --version, etc.); for the
+ # latter, we omit the display-only options and show help for
+ # each command listed on the command line.
+ if self.help:
+ self._show_help(parser,
+ display_options=len(self.commands) == 0,
+ commands=self.commands)
+ return
+
+ return True
+
+ def _get_toplevel_options(self):
+ """Return the non-display options recognized at the top level.
+
+ This includes options that are recognized *only* at the top
+ level as well as options recognized for commands.
+ """
+ return self.global_options
+
+ def _parse_command_opts(self, parser, args):
+ """Parse the command-line options for a single command.
+ 'parser' must be a FancyGetopt instance; 'args' must be the list
+ of arguments, starting with the current command (whose options
+ we are about to parse). Returns a new version of 'args' with
+ the next command at the front of the list; will be the empty
+ list if there are no more commands on the command line. Returns
+ None if the user asked for help on this command.
+ """
+ # Pull the current command from the head of the command line
+ command = args[0]
+ if not command_re.match(command):
+ raise SystemExit("invalid command name %r" % command)
+ self.commands.append(command)
+
+ # Dig up the command class that implements this command, so we
+ # 1) know that it's a valid command, and 2) know which options
+ # it takes.
+ try:
+ cmd_class = get_command_class(command)
+ except PackagingModuleError as msg:
+ raise PackagingArgError(msg)
+
+ # XXX We want to push this in packaging.command
+ #
+ # Require that the command class be derived from Command -- want
+ # to be sure that the basic "command" interface is implemented.
+ for meth in ('initialize_options', 'finalize_options', 'run'):
+ if hasattr(cmd_class, meth):
+ continue
+ raise PackagingClassError(
+ 'command %r must implement %r' % (cmd_class, meth))
+
+ # Also make sure that the command object provides a list of its
+ # known options.
+ if not (hasattr(cmd_class, 'user_options') and
+ isinstance(cmd_class.user_options, list)):
+ raise PackagingClassError(
+ "command class %s must provide "
+ "'user_options' attribute (a list of tuples)" % cmd_class)
+
+ # If the command class has a list of negative alias options,
+ # merge it in with the global negative aliases.
+ negative_opt = self.negative_opt
+ if hasattr(cmd_class, 'negative_opt'):
+ negative_opt = negative_opt.copy()
+ negative_opt.update(cmd_class.negative_opt)
+
+ # Check for help_options in command class. They have a different
+ # format (tuple of four) so we need to preprocess them here.
+ if (hasattr(cmd_class, 'help_options') and
+ isinstance(cmd_class.help_options, list)):
+ help_options = cmd_class.help_options[:]
+ else:
+ help_options = []
+
+ # All commands support the global options too, just by adding
+ # in 'global_options'.
+ parser.set_option_table(self.global_options +
+ cmd_class.user_options +
+ help_options)
+ parser.set_negative_aliases(negative_opt)
+ args, opts = parser.getopt(args[1:])
+ if hasattr(opts, 'help') and opts.help:
+ self._show_help(parser, display_options=False,
+ commands=[cmd_class])
+ return
+
+ if (hasattr(cmd_class, 'help_options') and
+ isinstance(cmd_class.help_options, list)):
+ help_option_found = False
+ for help_option, short, desc, func in cmd_class.help_options:
+ if hasattr(opts, help_option.replace('-', '_')):
+ help_option_found = True
+ if callable(func):
+ func()
+ else:
+ raise PackagingClassError(
+ "invalid help function %r for help option %r: "
+ "must be a callable object (function, etc.)"
+ % (func, help_option))
+
+ if help_option_found:
+ return
+
+ # Put the options from the command line into their official
+ # holding pen, the 'command_options' dictionary.
+ opt_dict = self.get_option_dict(command)
+ for name, value in vars(opts).items():
+ opt_dict[name] = ("command line", value)
+
+ return args
+
+ def finalize_options(self):
+ """Set final values for all the options on the Distribution
+ instance, analogous to the .finalize_options() method of Command
+ objects.
+ """
+ if getattr(self, 'convert_2to3_doctests', None):
+ self.convert_2to3_doctests = [os.path.join(p)
+ for p in self.convert_2to3_doctests]
+ else:
+ self.convert_2to3_doctests = []
+
+ def _show_help(self, parser, global_options=True, display_options=True,
+ commands=[]):
+ """Show help for the setup script command line in the form of
+ several lists of command-line options. 'parser' should be a
+ FancyGetopt instance; do not expect it to be returned in the
+ same state, as its option table will be reset to make it
+ generate the correct help text.
+
+ If 'global_options' is true, lists the global options:
+ --dry-run, etc. If 'display_options' is true, lists
+ the "display-only" options: --help-commands. Finally,
+ lists per-command help for every command name or command class
+ in 'commands'.
+ """
+ if global_options:
+ if display_options:
+ options = self._get_toplevel_options()
+ else:
+ options = self.global_options
+ parser.set_option_table(options)
+ parser.print_help(self.common_usage + "\nGlobal options:")
+ print()
+
+ if display_options:
+ parser.set_option_table(self.display_options)
+ parser.print_help(
+ "Information display options (just display " +
+ "information, ignore any commands)")
+ print()
+
+ for command in self.commands:
+ if isinstance(command, type) and issubclass(command, Command):
+ cls = command
+ else:
+ cls = get_command_class(command)
+ if (hasattr(cls, 'help_options') and
+ isinstance(cls.help_options, list)):
+ parser.set_option_table(cls.user_options + cls.help_options)
+ else:
+ parser.set_option_table(cls.user_options)
+ parser.print_help("Options for %r command:" % cls.__name__)
+ print()
+
+ print(gen_usage(self.script_name))
+
+ def handle_display_options(self, option_order):
+ """If there were any non-global "display-only" options
+ (--help-commands) on the command line, display the requested info and
+ return true; else return false.
+ """
+ # User just wants a list of commands -- we'll print it out and stop
+ # processing now (ie. if they ran "setup --help-commands foo bar",
+ # we ignore "foo bar").
+ if self.help_commands:
+ self.print_commands()
+ print()
+ print(gen_usage(self.script_name))
+ return True
+
+ # If user supplied any of the "display metadata" options, then
+ # display that metadata in the order in which the user supplied the
+ # metadata options.
+ any_display_options = False
+ is_display_option = set()
+ for option in self.display_options:
+ is_display_option.add(option[0])
+
+ for opt, val in option_order:
+ if val and opt in is_display_option:
+ opt = opt.replace('-', '_')
+ value = self.metadata[opt]
+ if opt in ('keywords', 'platform'):
+ print(','.join(value))
+ elif opt in ('classifier', 'provides', 'requires',
+ 'obsoletes'):
+ print('\n'.join(value))
+ else:
+ print(value)
+ any_display_options = True
+
+ return any_display_options
+
+ def print_command_list(self, commands, header, max_length):
+ """Print a subset of the list of all commands -- used by
+ 'print_commands()'.
+ """
+ print(header + ":")
+
+ for cmd in commands:
+ cls = self.cmdclass.get(cmd) or get_command_class(cmd)
+ description = getattr(cls, 'description',
+ '(no description available)')
+
+ print(" %-*s %s" % (max_length, cmd, description))
+
+ def _get_command_groups(self):
+ """Helper function to retrieve all the command class names divided
+ into standard commands (listed in
+ packaging.command.STANDARD_COMMANDS) and extra commands (given in
+ self.cmdclass and not standard commands).
+ """
+ extra_commands = [cmd for cmd in self.cmdclass
+ if cmd not in STANDARD_COMMANDS]
+ return STANDARD_COMMANDS, extra_commands
+
+ def print_commands(self):
+ """Print out a help message listing all available commands with a
+ description of each. The list is divided into standard commands
+ (listed in packaging.command.STANDARD_COMMANDS) and extra commands
+ (given in self.cmdclass and not standard commands). The
+ descriptions come from the command class attribute
+ 'description'.
+ """
+ std_commands, extra_commands = self._get_command_groups()
+ max_length = 0
+ for cmd in (std_commands + extra_commands):
+ if len(cmd) > max_length:
+ max_length = len(cmd)
+
+ self.print_command_list(std_commands,
+ "Standard commands",
+ max_length)
+ if extra_commands:
+ print()
+ self.print_command_list(extra_commands,
+ "Extra commands",
+ max_length)
+
+ # -- Command class/object methods ----------------------------------
+
+ def get_command_obj(self, command, create=True):
+ """Return the command object for 'command'. Normally this object
+ is cached on a previous call to 'get_command_obj()'; if no command
+ object for 'command' is in the cache, then we either create and
+ return it (if 'create' is true) or return None.
+ """
+ cmd_obj = self.command_obj.get(command)
+ if not cmd_obj and create:
+ logger.debug("Distribution.get_command_obj(): "
+ "creating %r command object", command)
+
+ cls = get_command_class(command)
+ cmd_obj = self.command_obj[command] = cls(self)
+ self.have_run[command] = 0
+
+ # Set any options that were supplied in config files or on the
+ # command line. (XXX support for error reporting is suboptimal
+ # here: errors aren't reported until finalize_options is called,
+ # which means we won't report the source of the error.)
+ options = self.command_options.get(command)
+ if options:
+ self._set_command_options(cmd_obj, options)
+
+ return cmd_obj
+
+ def _set_command_options(self, command_obj, option_dict=None):
+ """Set the options for 'command_obj' from 'option_dict'. Basically
+ this means copying elements of a dictionary ('option_dict') to
+ attributes of an instance ('command').
+
+ 'command_obj' must be a Command instance. If 'option_dict' is not
+ supplied, uses the standard option dictionary for this command
+ (from 'self.command_options').
+ """
+ command_name = command_obj.get_command_name()
+ if option_dict is None:
+ option_dict = self.get_option_dict(command_name)
+
+ logger.debug(" setting options for %r command:", command_name)
+
+ for option, (source, value) in option_dict.items():
+ logger.debug(" %s = %s (from %s)", option, value, source)
+ try:
+ bool_opts = [x.replace('-', '_')
+ for x in command_obj.boolean_options]
+ except AttributeError:
+ bool_opts = []
+ try:
+ neg_opt = command_obj.negative_opt
+ except AttributeError:
+ neg_opt = {}
+
+ try:
+ is_string = isinstance(value, str)
+ if option in neg_opt and is_string:
+ setattr(command_obj, neg_opt[option], not strtobool(value))
+ elif option in bool_opts and is_string:
+ setattr(command_obj, option, strtobool(value))
+ elif hasattr(command_obj, option):
+ setattr(command_obj, option, value)
+ else:
+ raise PackagingOptionError(
+ "error in %s: command %r has no such option %r" %
+ (source, command_name, option))
+ except ValueError as msg:
+ raise PackagingOptionError(msg)
+
+ def reinitialize_command(self, command, reinit_subcommands=False):
+ """Reinitializes a command to the state it was in when first
+ returned by 'get_command_obj()': i.e., initialized but not yet
+ finalized. This provides the opportunity to sneak option
+ values in programmatically, overriding or supplementing
+ user-supplied values from the config files and command line.
+ You'll have to re-finalize the command object (by calling
+ 'finalize_options()' or 'ensure_finalized()') before using it for
+ real.
+
+ 'command' should be a command name (string) or command object. If
+ 'reinit_subcommands' is true, also reinitializes the command's
+ sub-commands, as declared by the 'sub_commands' class attribute (if
+ it has one). See the "install_dist" command for an example. Only
+ reinitializes the sub-commands that actually matter, i.e. those
+ whose test predicate return true.
+
+ Returns the reinitialized command object. It will be the same
+ object as the one stored in the self.command_obj attribute.
+ """
+ if not isinstance(command, Command):
+ command_name = command
+ command = self.get_command_obj(command_name)
+ else:
+ command_name = command.get_command_name()
+
+ if not command.finalized:
+ return command
+
+ command.initialize_options()
+ self.have_run[command_name] = 0
+ command.finalized = False
+ self._set_command_options(command)
+
+ if reinit_subcommands:
+ for sub in command.get_sub_commands():
+ self.reinitialize_command(sub, reinit_subcommands)
+
+ return command
+
+ # -- Methods that operate on the Distribution ----------------------
+
+ def run_commands(self):
+ """Run each command that was seen on the setup script command line.
+ Uses the list of commands found and cache of command objects
+ created by 'get_command_obj()'.
+ """
+ for cmd in self.commands:
+ self.run_command(cmd)
+
+ # -- Methods that operate on its Commands --------------------------
+
+ def run_command(self, command, options=None):
+ """Do whatever it takes to run a command (including nothing at all,
+ if the command has already been run). Specifically: if we have
+ already created and run the command named by 'command', return
+ silently without doing anything. If the command named by 'command'
+ doesn't even have a command object yet, create one. Then invoke
+ 'run()' on that command object (or an existing one).
+ """
+ # Already been here, done that? then return silently.
+ if self.have_run.get(command):
+ return
+
+ if options is not None:
+ self.command_options[command] = options
+
+ cmd_obj = self.get_command_obj(command)
+ cmd_obj.ensure_finalized()
+ self.run_command_hooks(cmd_obj, 'pre_hook')
+ logger.info("running %s", command)
+ cmd_obj.run()
+ self.run_command_hooks(cmd_obj, 'post_hook')
+ self.have_run[command] = 1
+
+ def run_command_hooks(self, cmd_obj, hook_kind):
+ """Run hooks registered for that command and phase.
+
+ *cmd_obj* is a finalized command object; *hook_kind* is either
+ 'pre_hook' or 'post_hook'.
+ """
+ if hook_kind not in ('pre_hook', 'post_hook'):
+ raise ValueError('invalid hook kind: %r' % hook_kind)
+
+ hooks = getattr(cmd_obj, hook_kind, None)
+
+ if hooks is None:
+ return
+
+ for hook in hooks.values():
+ if isinstance(hook, str):
+ try:
+ hook_obj = resolve_name(hook)
+ except ImportError as e:
+ raise PackagingModuleError(e)
+ else:
+ hook_obj = hook
+
+ if not callable(hook_obj):
+ raise PackagingOptionError('hook %r is not callable' % hook)
+
+ logger.info('running %s %s for command %s',
+ hook_kind, hook, cmd_obj.get_command_name())
+ hook_obj(cmd_obj)
+
+ # -- Distribution query methods ------------------------------------
+ def has_pure_modules(self):
+ return len(self.packages or self.py_modules or []) > 0
+
+ def has_ext_modules(self):
+ return self.ext_modules and len(self.ext_modules) > 0
+
+ def has_c_libraries(self):
+ return self.libraries and len(self.libraries) > 0
+
+ def has_modules(self):
+ return self.has_pure_modules() or self.has_ext_modules()
+
+ def has_headers(self):
+ return self.headers and len(self.headers) > 0
+
+ def has_scripts(self):
+ return self.scripts and len(self.scripts) > 0
+
+ def has_data_files(self):
+ return self.data_files and len(self.data_files) > 0
+
+ def is_pure(self):
+ return (self.has_pure_modules() and
+ not self.has_ext_modules() and
+ not self.has_c_libraries())
diff --git a/Lib/packaging/errors.py b/Lib/packaging/errors.py
new file mode 100644
index 0000000..8878129
--- /dev/null
+++ b/Lib/packaging/errors.py
@@ -0,0 +1,138 @@
+"""Exceptions used throughout the package.
+
+Submodules of packaging may raise exceptions defined in this module as
+well as standard exceptions; in particular, SystemExit is usually raised
+for errors that are obviously the end-user's fault (e.g. bad
+command-line arguments).
+"""
+
+
+class PackagingError(Exception):
+ """The root of all Packaging evil."""
+
+
+class PackagingModuleError(PackagingError):
+ """Unable to load an expected module, or to find an expected class
+ within some module (in particular, command modules and classes)."""
+
+
+class PackagingClassError(PackagingError):
+ """Some command class (or possibly distribution class, if anyone
+ feels a need to subclass Distribution) is found not to be holding
+ up its end of the bargain, ie. implementing some part of the
+ "command "interface."""
+
+
+class PackagingGetoptError(PackagingError):
+ """The option table provided to 'fancy_getopt()' is bogus."""
+
+
+class PackagingArgError(PackagingError):
+ """Raised by fancy_getopt in response to getopt.error -- ie. an
+ error in the command line usage."""
+
+
+class PackagingFileError(PackagingError):
+ """Any problems in the filesystem: expected file not found, etc.
+ Typically this is for problems that we detect before IOError or
+ OSError could be raised."""
+
+
+class PackagingOptionError(PackagingError):
+ """Syntactic/semantic errors in command options, such as use of
+ mutually conflicting options, or inconsistent options,
+ badly-spelled values, etc. No distinction is made between option
+ values originating in the setup script, the command line, config
+ files, or what-have-you -- but if we *know* something originated in
+ the setup script, we'll raise PackagingSetupError instead."""
+
+
+class PackagingSetupError(PackagingError):
+ """For errors that can be definitely blamed on the setup script,
+ such as invalid keyword arguments to 'setup()'."""
+
+
+class PackagingPlatformError(PackagingError):
+ """We don't know how to do something on the current platform (but
+ we do know how to do it on some platform) -- eg. trying to compile
+ C files on a platform not supported by a CCompiler subclass."""
+
+
+class PackagingExecError(PackagingError):
+ """Any problems executing an external program (such as the C
+ compiler, when compiling C files)."""
+
+
+class PackagingInternalError(PackagingError):
+ """Internal inconsistencies or impossibilities (obviously, this
+ should never be seen if the code is working!)."""
+
+
+class PackagingTemplateError(PackagingError):
+ """Syntax error in a file list template."""
+
+
+class PackagingPyPIError(PackagingError):
+ """Any problem occuring during using the indexes."""
+
+
+# Exception classes used by the CCompiler implementation classes
+class CCompilerError(Exception):
+ """Some compile/link operation failed."""
+
+
+class PreprocessError(CCompilerError):
+ """Failure to preprocess one or more C/C++ files."""
+
+
+class CompileError(CCompilerError):
+ """Failure to compile one or more C/C++ source files."""
+
+
+class LibError(CCompilerError):
+ """Failure to create a static library from one or more C/C++ object
+ files."""
+
+
+class LinkError(CCompilerError):
+ """Failure to link one or more C/C++ object files into an executable
+ or shared library file."""
+
+
+class UnknownFileError(CCompilerError):
+ """Attempt to process an unknown file type."""
+
+
+class MetadataMissingError(PackagingError):
+ """A required metadata is missing"""
+
+
+class MetadataConflictError(PackagingError):
+ """Attempt to read or write metadata fields that are conflictual."""
+
+
+class MetadataUnrecognizedVersionError(PackagingError):
+ """Unknown metadata version number."""
+
+
+class IrrationalVersionError(Exception):
+ """This is an irrational version."""
+ pass
+
+
+class HugeMajorVersionNumError(IrrationalVersionError):
+ """An irrational version because the major version number is huge
+ (often because a year or date was used).
+
+ See `error_on_huge_major_num` option in `NormalizedVersion` for details.
+ This guard can be disabled by setting that option False.
+ """
+ pass
+
+
+class InstallationException(Exception):
+ """Base exception for installation scripts"""
+
+
+class InstallationConflict(InstallationException):
+ """Raised when a conflict is detected"""
diff --git a/Lib/packaging/fancy_getopt.py b/Lib/packaging/fancy_getopt.py
new file mode 100644
index 0000000..61dd5fc
--- /dev/null
+++ b/Lib/packaging/fancy_getopt.py
@@ -0,0 +1,388 @@
+"""Command line parsing machinery.
+
+The FancyGetopt class is a Wrapper around the getopt module that
+provides the following additional features:
+ * short and long options are tied together
+ * options have help strings, so fancy_getopt could potentially
+ create a complete usage summary
+ * options set attributes of a passed-in object.
+
+It is used under the hood by the command classes. Do not use directly.
+"""
+
+import getopt
+import re
+import sys
+import textwrap
+
+from packaging.errors import PackagingGetoptError, PackagingArgError
+
+# Much like command_re in packaging.core, this is close to but not quite
+# the same as a Python NAME -- except, in the spirit of most GNU
+# utilities, we use '-' in place of '_'. (The spirit of LISP lives on!)
+# The similarities to NAME are again not a coincidence...
+longopt_pat = r'[a-zA-Z](?:[a-zA-Z0-9-]*)'
+longopt_re = re.compile(r'^%s$' % longopt_pat)
+
+# For recognizing "negative alias" options, eg. "quiet=!verbose"
+neg_alias_re = re.compile("^(%s)=!(%s)$" % (longopt_pat, longopt_pat))
+
+
+class FancyGetopt:
+ """Wrapper around the standard 'getopt()' module that provides some
+ handy extra functionality:
+ * short and long options are tied together
+ * options have help strings, and help text can be assembled
+ from them
+ * options set attributes of a passed-in object
+ * boolean options can have "negative aliases" -- eg. if
+ --quiet is the "negative alias" of --verbose, then "--quiet"
+ on the command line sets 'verbose' to false
+ """
+
+ def __init__(self, option_table=None):
+
+ # The option table is (currently) a list of tuples. The
+ # tuples may have 3 or four values:
+ # (long_option, short_option, help_string [, repeatable])
+ # if an option takes an argument, its long_option should have '='
+ # appended; short_option should just be a single character, no ':'
+ # in any case. If a long_option doesn't have a corresponding
+ # short_option, short_option should be None. All option tuples
+ # must have long options.
+ self.option_table = option_table
+
+ # 'option_index' maps long option names to entries in the option
+ # table (ie. those 3-tuples).
+ self.option_index = {}
+ if self.option_table:
+ self._build_index()
+
+ # 'alias' records (duh) alias options; {'foo': 'bar'} means
+ # --foo is an alias for --bar
+ self.alias = {}
+
+ # 'negative_alias' keeps track of options that are the boolean
+ # opposite of some other option
+ self.negative_alias = {}
+
+ # These keep track of the information in the option table. We
+ # don't actually populate these structures until we're ready to
+ # parse the command line, since the 'option_table' passed in here
+ # isn't necessarily the final word.
+ self.short_opts = []
+ self.long_opts = []
+ self.short2long = {}
+ self.attr_name = {}
+ self.takes_arg = {}
+
+ # And 'option_order' is filled up in 'getopt()'; it records the
+ # original order of options (and their values) on the command line,
+ # but expands short options, converts aliases, etc.
+ self.option_order = []
+
+ def _build_index(self):
+ self.option_index.clear()
+ for option in self.option_table:
+ self.option_index[option[0]] = option
+
+ def set_option_table(self, option_table):
+ self.option_table = option_table
+ self._build_index()
+
+ def add_option(self, long_option, short_option=None, help_string=None):
+ if long_option in self.option_index:
+ raise PackagingGetoptError(
+ "option conflict: already an option '%s'" % long_option)
+ else:
+ option = (long_option, short_option, help_string)
+ self.option_table.append(option)
+ self.option_index[long_option] = option
+
+ def has_option(self, long_option):
+ """Return true if the option table for this parser has an
+ option with long name 'long_option'."""
+ return long_option in self.option_index
+
+ def _check_alias_dict(self, aliases, what):
+ assert isinstance(aliases, dict)
+ for alias, opt in aliases.items():
+ if alias not in self.option_index:
+ raise PackagingGetoptError(
+ ("invalid %s '%s': "
+ "option '%s' not defined") % (what, alias, alias))
+ if opt not in self.option_index:
+ raise PackagingGetoptError(
+ ("invalid %s '%s': "
+ "aliased option '%s' not defined") % (what, alias, opt))
+
+ def set_aliases(self, alias):
+ """Set the aliases for this option parser."""
+ self._check_alias_dict(alias, "alias")
+ self.alias = alias
+
+ def set_negative_aliases(self, negative_alias):
+ """Set the negative aliases for this option parser.
+ 'negative_alias' should be a dictionary mapping option names to
+ option names, both the key and value must already be defined
+ in the option table."""
+ self._check_alias_dict(negative_alias, "negative alias")
+ self.negative_alias = negative_alias
+
+ def _grok_option_table(self):
+ """Populate the various data structures that keep tabs on the
+ option table. Called by 'getopt()' before it can do anything
+ worthwhile.
+ """
+ self.long_opts = []
+ self.short_opts = []
+ self.short2long.clear()
+ self.repeat = {}
+
+ for option in self.option_table:
+ if len(option) == 3:
+ longopt, short, help = option
+ repeat = 0
+ elif len(option) == 4:
+ longopt, short, help, repeat = option
+ else:
+ # the option table is part of the code, so simply
+ # assert that it is correct
+ raise ValueError("invalid option tuple: %r" % option)
+
+ # Type- and value-check the option names
+ if not isinstance(longopt, str) or len(longopt) < 2:
+ raise PackagingGetoptError(
+ ("invalid long option '%s': "
+ "must be a string of length >= 2") % longopt)
+
+ if (not ((short is None) or
+ (isinstance(short, str) and len(short) == 1))):
+ raise PackagingGetoptError(
+ ("invalid short option '%s': "
+ "must be a single character or None") % short)
+
+ self.repeat[longopt] = repeat
+ self.long_opts.append(longopt)
+
+ if longopt[-1] == '=': # option takes an argument?
+ if short:
+ short = short + ':'
+ longopt = longopt[0:-1]
+ self.takes_arg[longopt] = 1
+ else:
+
+ # Is option is a "negative alias" for some other option (eg.
+ # "quiet" == "!verbose")?
+ alias_to = self.negative_alias.get(longopt)
+ if alias_to is not None:
+ if self.takes_arg[alias_to]:
+ raise PackagingGetoptError(
+ ("invalid negative alias '%s': "
+ "aliased option '%s' takes a value") % \
+ (longopt, alias_to))
+
+ self.long_opts[-1] = longopt # XXX redundant?!
+ self.takes_arg[longopt] = 0
+
+ else:
+ self.takes_arg[longopt] = 0
+
+ # If this is an alias option, make sure its "takes arg" flag is
+ # the same as the option it's aliased to.
+ alias_to = self.alias.get(longopt)
+ if alias_to is not None:
+ if self.takes_arg[longopt] != self.takes_arg[alias_to]:
+ raise PackagingGetoptError(
+ ("invalid alias '%s': inconsistent with "
+ "aliased option '%s' (one of them takes a value, "
+ "the other doesn't") % (longopt, alias_to))
+
+ # Now enforce some bondage on the long option name, so we can
+ # later translate it to an attribute name on some object. Have
+ # to do this a bit late to make sure we've removed any trailing
+ # '='.
+ if not longopt_re.match(longopt):
+ raise PackagingGetoptError(
+ ("invalid long option name '%s' " +
+ "(must be letters, numbers, hyphens only") % longopt)
+
+ self.attr_name[longopt] = longopt.replace('-', '_')
+ if short:
+ self.short_opts.append(short)
+ self.short2long[short[0]] = longopt
+
+ def getopt(self, args=None, object=None):
+ """Parse command-line options in args. Store as attributes on object.
+
+ If 'args' is None or not supplied, uses 'sys.argv[1:]'. If
+ 'object' is None or not supplied, creates a new OptionDummy
+ object, stores option values there, and returns a tuple (args,
+ object). If 'object' is supplied, it is modified in place and
+ 'getopt()' just returns 'args'; in both cases, the returned
+ 'args' is a modified copy of the passed-in 'args' list, which
+ is left untouched.
+ """
+ if args is None:
+ args = sys.argv[1:]
+ if object is None:
+ object = OptionDummy()
+ created_object = 1
+ else:
+ created_object = 0
+
+ self._grok_option_table()
+
+ short_opts = ' '.join(self.short_opts)
+
+ try:
+ opts, args = getopt.getopt(args, short_opts, self.long_opts)
+ except getopt.error as msg:
+ raise PackagingArgError(msg)
+
+ for opt, val in opts:
+ if len(opt) == 2 and opt[0] == '-': # it's a short option
+ opt = self.short2long[opt[1]]
+ else:
+ assert len(opt) > 2 and opt[:2] == '--'
+ opt = opt[2:]
+
+ alias = self.alias.get(opt)
+ if alias:
+ opt = alias
+
+ if not self.takes_arg[opt]: # boolean option?
+ assert val == '', "boolean option can't have value"
+ alias = self.negative_alias.get(opt)
+ if alias:
+ opt = alias
+ val = 0
+ else:
+ val = 1
+
+ attr = self.attr_name[opt]
+ # The only repeating option at the moment is 'verbose'.
+ # It has a negative option -q quiet, which should set verbose = 0.
+ if val and self.repeat.get(attr) is not None:
+ val = getattr(object, attr, 0) + 1
+ setattr(object, attr, val)
+ self.option_order.append((opt, val))
+
+ # for opts
+ if created_object:
+ return args, object
+ else:
+ return args
+
+ def get_option_order(self):
+ """Returns the list of (option, value) tuples processed by the
+ previous run of 'getopt()'. Raises RuntimeError if
+ 'getopt()' hasn't been called yet.
+ """
+ if self.option_order is None:
+ raise RuntimeError("'getopt()' hasn't been called yet")
+ else:
+ return self.option_order
+
+ return self.option_order
+
+ def generate_help(self, header=None):
+ """Generate help text (a list of strings, one per suggested line of
+ output) from the option table for this FancyGetopt object.
+ """
+ # Blithely assume the option table is good: probably wouldn't call
+ # 'generate_help()' unless you've already called 'getopt()'.
+
+ # First pass: determine maximum length of long option names
+ max_opt = 0
+ for option in self.option_table:
+ longopt = option[0]
+ short = option[1]
+ l = len(longopt)
+ if longopt[-1] == '=':
+ l = l - 1
+ if short is not None:
+ l = l + 5 # " (-x)" where short == 'x'
+ if l > max_opt:
+ max_opt = l
+
+ opt_width = max_opt + 2 + 2 + 2 # room for indent + dashes + gutter
+
+ # Typical help block looks like this:
+ # --foo controls foonabulation
+ # Help block for longest option looks like this:
+ # --flimflam set the flim-flam level
+ # and with wrapped text:
+ # --flimflam set the flim-flam level (must be between
+ # 0 and 100, except on Tuesdays)
+ # Options with short names will have the short name shown (but
+ # it doesn't contribute to max_opt):
+ # --foo (-f) controls foonabulation
+ # If adding the short option would make the left column too wide,
+ # we push the explanation off to the next line
+ # --flimflam (-l)
+ # set the flim-flam level
+ # Important parameters:
+ # - 2 spaces before option block start lines
+ # - 2 dashes for each long option name
+ # - min. 2 spaces between option and explanation (gutter)
+ # - 5 characters (incl. space) for short option name
+
+ # Now generate lines of help text. (If 80 columns were good enough
+ # for Jesus, then 78 columns are good enough for me!)
+ line_width = 78
+ text_width = line_width - opt_width
+ big_indent = ' ' * opt_width
+ if header:
+ lines = [header]
+ else:
+ lines = ['Option summary:']
+
+ for option in self.option_table:
+ longopt, short, help = option[:3]
+ text = textwrap.wrap(help, text_width)
+
+ # Case 1: no short option at all (makes life easy)
+ if short is None:
+ if text:
+ lines.append(" --%-*s %s" % (max_opt, longopt, text[0]))
+ else:
+ lines.append(" --%-*s " % (max_opt, longopt))
+
+ # Case 2: we have a short option, so we have to include it
+ # just after the long option
+ else:
+ opt_names = "%s (-%s)" % (longopt, short)
+ if text:
+ lines.append(" --%-*s %s" %
+ (max_opt, opt_names, text[0]))
+ else:
+ lines.append(" --%-*s" % opt_names)
+
+ for l in text[1:]:
+ lines.append(big_indent + l)
+
+ return lines
+
+ def print_help(self, header=None, file=None):
+ if file is None:
+ file = sys.stdout
+ for line in self.generate_help(header):
+ file.write(line + "\n")
+
+
+def fancy_getopt(options, negative_opt, object, args):
+ parser = FancyGetopt(options)
+ parser.set_negative_aliases(negative_opt)
+ return parser.getopt(args, object)
+
+
+class OptionDummy:
+ """Dummy class just used as a place to hold command-line option
+ values as instance attributes."""
+
+ def __init__(self, options=[]):
+ """Create a new OptionDummy instance. The attributes listed in
+ 'options' will be initialized to None."""
+ for opt in options:
+ setattr(self, opt, None)
diff --git a/Lib/packaging/install.py b/Lib/packaging/install.py
new file mode 100644
index 0000000..776ba40
--- /dev/null
+++ b/Lib/packaging/install.py
@@ -0,0 +1,529 @@
+"""Building blocks for installers.
+
+When used as a script, this module installs a release thanks to info
+obtained from an index (e.g. PyPI), with dependencies.
+
+This is a higher-level module built on packaging.database and
+packaging.pypi.
+"""
+import os
+import sys
+import stat
+import errno
+import shutil
+import logging
+import tempfile
+from sysconfig import get_config_var, get_path, is_python_build
+
+from packaging import logger
+from packaging.dist import Distribution
+from packaging.util import (_is_archive_file, ask, get_install_method,
+ egginfo_to_distinfo)
+from packaging.pypi import wrapper
+from packaging.version import get_version_predicate
+from packaging.database import get_distributions, get_distribution
+from packaging.depgraph import generate_graph
+
+from packaging.errors import (PackagingError, InstallationException,
+ InstallationConflict, CCompilerError)
+from packaging.pypi.errors import ProjectNotFound, ReleaseNotFound
+from packaging import database
+
+
+__all__ = ['install_dists', 'install_from_infos', 'get_infos', 'remove',
+ 'install', 'install_local_project']
+
+
+def _move_files(files, destination):
+ """Move the list of files in the destination folder, keeping the same
+ structure.
+
+ Return a list of tuple (old, new) emplacement of files
+
+ :param files: a list of files to move.
+ :param destination: the destination directory to put on the files.
+ """
+
+ for old in files:
+ filename = os.path.split(old)[-1]
+ new = os.path.join(destination, filename)
+ # try to make the paths.
+ try:
+ os.makedirs(os.path.dirname(new))
+ except OSError as e:
+ if e.errno != errno.EEXIST:
+ raise
+ os.rename(old, new)
+ yield old, new
+
+
+def _run_distutils_install(path):
+ # backward compat: using setuptools or plain-distutils
+ cmd = '%s setup.py install --record=%s'
+ record_file = os.path.join(path, 'RECORD')
+ os.system(cmd % (sys.executable, record_file))
+ if not os.path.exists(record_file):
+ raise ValueError('failed to install')
+ else:
+ egginfo_to_distinfo(record_file, remove_egginfo=True)
+
+
+def _run_setuptools_install(path):
+ cmd = '%s setup.py install --record=%s --single-version-externally-managed'
+ record_file = os.path.join(path, 'RECORD')
+
+ os.system(cmd % (sys.executable, record_file))
+ if not os.path.exists(record_file):
+ raise ValueError('failed to install')
+ else:
+ egginfo_to_distinfo(record_file, remove_egginfo=True)
+
+
+def _run_packaging_install(path):
+ # XXX check for a valid setup.cfg?
+ dist = Distribution()
+ dist.parse_config_files()
+ try:
+ dist.run_command('install_dist')
+ name = dist.metadata['Name']
+ return database.get_distribution(name) is not None
+ except (IOError, os.error, PackagingError, CCompilerError) as msg:
+ raise ValueError("Failed to install, " + str(msg))
+
+
+def _install_dist(dist, path):
+ """Install a distribution into a path.
+
+ This:
+
+ * unpack the distribution
+ * copy the files in "path"
+ * determine if the distribution is packaging or distutils1.
+ """
+ where = dist.unpack()
+
+ if where is None:
+ raise ValueError('Cannot locate the unpacked archive')
+
+ return _run_install_from_archive(where)
+
+
+def install_local_project(path):
+ """Install a distribution from a source directory.
+
+ If the source directory contains a setup.py install using distutils1.
+ If a setup.cfg is found, install using the install_dist command.
+
+ Returns True on success, False on Failure.
+ """
+ path = os.path.abspath(path)
+ if os.path.isdir(path):
+ logger.info('Installing from source directory: %r', path)
+ return _run_install_from_dir(path)
+ elif _is_archive_file(path):
+ logger.info('Installing from archive: %r', path)
+ _unpacked_dir = tempfile.mkdtemp()
+ try:
+ shutil.unpack_archive(path, _unpacked_dir)
+ return _run_install_from_archive(_unpacked_dir)
+ finally:
+ shutil.rmtree(_unpacked_dir)
+ else:
+ logger.warning('No project to install.')
+ return False
+
+
+def _run_install_from_archive(source_dir):
+ # XXX need a better way
+ for item in os.listdir(source_dir):
+ fullpath = os.path.join(source_dir, item)
+ if os.path.isdir(fullpath):
+ source_dir = fullpath
+ break
+ return _run_install_from_dir(source_dir)
+
+
+install_methods = {
+ 'packaging': _run_packaging_install,
+ 'setuptools': _run_setuptools_install,
+ 'distutils': _run_distutils_install}
+
+
+def _run_install_from_dir(source_dir):
+ old_dir = os.getcwd()
+ os.chdir(source_dir)
+ install_method = get_install_method(source_dir)
+ func = install_methods[install_method]
+ try:
+ func = install_methods[install_method]
+ try:
+ func(source_dir)
+ return True
+ except ValueError as err:
+ # failed to install
+ logger.info(str(err))
+ return False
+ finally:
+ os.chdir(old_dir)
+
+
+def install_dists(dists, path, paths=None):
+ """Install all distributions provided in dists, with the given prefix.
+
+ If an error occurs while installing one of the distributions, uninstall all
+ the installed distribution (in the context if this function).
+
+ Return a list of installed dists.
+
+ :param dists: distributions to install
+ :param path: base path to install distribution in
+ :param paths: list of paths (defaults to sys.path) to look for info
+ """
+
+ installed_dists = []
+ for dist in dists:
+ logger.info('Installing %r %s...', dist.name, dist.version)
+ try:
+ _install_dist(dist, path)
+ installed_dists.append(dist)
+ except Exception as e:
+ logger.info('Failed: %s', e)
+
+ # reverting
+ for installed_dist in installed_dists:
+ logger.info('Reverting %r', installed_dist)
+ remove(installed_dist.name, paths)
+ raise e
+ return installed_dists
+
+
+def install_from_infos(install_path=None, install=[], remove=[], conflicts=[],
+ paths=None):
+ """Install and remove the given distributions.
+
+ The function signature is made to be compatible with the one of get_infos.
+ The aim of this script is to povide a way to install/remove what's asked,
+ and to rollback if needed.
+
+ So, it's not possible to be in an inconsistant state, it could be either
+ installed, either uninstalled, not half-installed.
+
+ The process follow those steps:
+
+ 1. Move all distributions that will be removed in a temporary location
+ 2. Install all the distributions that will be installed in a temp. loc.
+ 3. If the installation fails, rollback (eg. move back) those
+ distributions, or remove what have been installed.
+ 4. Else, move the distributions to the right locations, and remove for
+ real the distributions thats need to be removed.
+
+ :param install_path: the installation path where we want to install the
+ distributions.
+ :param install: list of distributions that will be installed; install_path
+ must be provided if this list is not empty.
+ :param remove: list of distributions that will be removed.
+ :param conflicts: list of conflicting distributions, eg. that will be in
+ conflict once the install and remove distribution will be
+ processed.
+ :param paths: list of paths (defaults to sys.path) to look for info
+ """
+ # first of all, if we have conflicts, stop here.
+ if conflicts:
+ raise InstallationConflict(conflicts)
+
+ if install and not install_path:
+ raise ValueError("Distributions are to be installed but `install_path`"
+ " is not provided.")
+
+ # before removing the files, we will start by moving them away
+ # then, if any error occurs, we could replace them in the good place.
+ temp_files = {} # contains lists of {dist: (old, new)} paths
+ temp_dir = None
+ if remove:
+ temp_dir = tempfile.mkdtemp()
+ for dist in remove:
+ files = dist.list_installed_files()
+ temp_files[dist] = _move_files(files, temp_dir)
+ try:
+ if install:
+ install_dists(install, install_path, paths)
+ except:
+ # if an error occurs, put back the files in the right place.
+ for files in temp_files.values():
+ for old, new in files:
+ shutil.move(new, old)
+ if temp_dir:
+ shutil.rmtree(temp_dir)
+ # now re-raising
+ raise
+
+ # we can remove them for good
+ for files in temp_files.values():
+ for old, new in files:
+ os.remove(new)
+ if temp_dir:
+ shutil.rmtree(temp_dir)
+
+
+def _get_setuptools_deps(release):
+ # NotImplementedError
+ pass
+
+
+def get_infos(requirements, index=None, installed=None, prefer_final=True):
+ """Return the informations on what's going to be installed and upgraded.
+
+ :param requirements: is a *string* containing the requirements for this
+ project (for instance "FooBar 1.1" or "BarBaz (<1.2)")
+ :param index: If an index is specified, use this one, otherwise, use
+ :class index.ClientWrapper: to get project metadatas.
+ :param installed: a list of already installed distributions.
+ :param prefer_final: when picking up the releases, prefer a "final" one
+ over a beta/alpha/etc one.
+
+ The results are returned in a dict, containing all the operations
+ needed to install the given requirements::
+
+ >>> get_install_info("FooBar (<=1.2)")
+ {'install': [<FooBar 1.1>], 'remove': [], 'conflict': []}
+
+ Conflict contains all the conflicting distributions, if there is a
+ conflict.
+ """
+ # this function does several things:
+ # 1. get a release specified by the requirements
+ # 2. gather its metadata, using setuptools compatibility if needed
+ # 3. compare this tree with what is currently installed on the system,
+ # return the requirements of what is missing
+ # 4. do that recursively and merge back the results
+ # 5. return a dict containing information about what is needed to install
+ # or remove
+
+ if not installed:
+ logger.debug('Reading installed distributions')
+ installed = list(get_distributions(use_egg_info=True))
+
+ infos = {'install': [], 'remove': [], 'conflict': []}
+ # Is a compatible version of the project already installed ?
+ predicate = get_version_predicate(requirements)
+ found = False
+
+ # check that the project isn't already installed
+ for installed_project in installed:
+ # is it a compatible project ?
+ if predicate.name.lower() != installed_project.name.lower():
+ continue
+ found = True
+ logger.info('Found %r %s', installed_project.name,
+ installed_project.version)
+
+ # if we already have something installed, check it matches the
+ # requirements
+ if predicate.match(installed_project.version):
+ return infos
+ break
+
+ if not found:
+ logger.debug('Project not installed')
+
+ if not index:
+ index = wrapper.ClientWrapper()
+
+ if not installed:
+ installed = get_distributions(use_egg_info=True)
+
+ # Get all the releases that match the requirements
+ try:
+ release = index.get_release(requirements)
+ except (ReleaseNotFound, ProjectNotFound):
+ raise InstallationException('Release not found: %r' % requirements)
+
+ if release is None:
+ logger.info('Could not find a matching project')
+ return infos
+
+ metadata = release.fetch_metadata()
+
+ # we need to build setuptools deps if any
+ if 'requires_dist' not in metadata:
+ metadata['requires_dist'] = _get_setuptools_deps(release)
+
+ # build the dependency graph with local and required dependencies
+ dists = list(installed)
+ dists.append(release)
+ depgraph = generate_graph(dists)
+
+ # Get what the missing deps are
+ dists = depgraph.missing[release]
+ if dists:
+ logger.info("Missing dependencies found, retrieving metadata")
+ # we have missing deps
+ for dist in dists:
+ _update_infos(infos, get_infos(dist, index, installed))
+
+ # Fill in the infos
+ existing = [d for d in installed if d.name == release.name]
+ if existing:
+ infos['remove'].append(existing[0])
+ infos['conflict'].extend(depgraph.reverse_list[existing[0]])
+ infos['install'].append(release)
+ return infos
+
+
+def _update_infos(infos, new_infos):
+ """extends the lists contained in the `info` dict with those contained
+ in the `new_info` one
+ """
+ for key, value in infos.items():
+ if key in new_infos:
+ infos[key].extend(new_infos[key])
+
+
+def remove(project_name, paths=None, auto_confirm=True):
+ """Removes a single project from the installation.
+
+ Returns True on success
+ """
+ dist = get_distribution(project_name, use_egg_info=True, paths=paths)
+ if dist is None:
+ raise PackagingError('Distribution %r not found' % project_name)
+ files = dist.list_installed_files(local=True)
+ rmdirs = []
+ rmfiles = []
+ tmp = tempfile.mkdtemp(prefix=project_name + '-uninstall')
+
+ def _move_file(source, target):
+ try:
+ os.rename(source, target)
+ except OSError as err:
+ return err
+ return None
+
+ success = True
+ error = None
+ try:
+ for file_, md5, size in files:
+ if os.path.isfile(file_):
+ dirname, filename = os.path.split(file_)
+ tmpfile = os.path.join(tmp, filename)
+ try:
+ error = _move_file(file_, tmpfile)
+ if error is not None:
+ success = False
+ break
+ finally:
+ if not os.path.isfile(file_):
+ os.rename(tmpfile, file_)
+ if file_ not in rmfiles:
+ rmfiles.append(file_)
+ if dirname not in rmdirs:
+ rmdirs.append(dirname)
+ finally:
+ shutil.rmtree(tmp)
+
+ if not success:
+ logger.info('%r cannot be removed.', project_name)
+ logger.info('Error: %s', error)
+ return False
+
+ logger.info('Removing %r: ', project_name)
+
+ for file_ in rmfiles:
+ logger.info(' %s', file_)
+
+ # Taken from the pip project
+ if auto_confirm:
+ response = 'y'
+ else:
+ response = ask('Proceed (y/n)? ', ('y', 'n'))
+
+ if response == 'y':
+ file_count = 0
+ for file_ in rmfiles:
+ os.remove(file_)
+ file_count += 1
+
+ dir_count = 0
+ for dirname in rmdirs:
+ if not os.path.exists(dirname):
+ # could
+ continue
+
+ files_count = 0
+ for root, dir, files in os.walk(dirname):
+ files_count += len(files)
+
+ if files_count > 0:
+ # XXX Warning
+ continue
+
+ # empty dirs with only empty dirs
+ if os.stat(dirname).st_mode & stat.S_IWUSR:
+ # XXX Add a callable in shutil.rmtree to count
+ # the number of deleted elements
+ shutil.rmtree(dirname)
+ dir_count += 1
+
+ # removing the top path
+ # XXX count it ?
+ if os.path.exists(dist.path):
+ shutil.rmtree(dist.path)
+
+ logger.info('Success: removed %d files and %d dirs',
+ file_count, dir_count)
+
+ return True
+
+
+def install(project):
+ """Installs a project.
+
+ Returns True on success, False on failure
+ """
+ if is_python_build():
+ # Python would try to install into the site-packages directory under
+ # $PREFIX, but when running from an uninstalled code checkout we don't
+ # want to create directories under the installation root
+ message = ('installing third-party projects from an uninstalled '
+ 'Python is not supported')
+ logger.error(message)
+ return False
+
+ logger.info('Checking the installation location...')
+ purelib_path = get_path('purelib')
+
+ # trying to write a file there
+ try:
+ with tempfile.NamedTemporaryFile(suffix=project,
+ dir=purelib_path) as testfile:
+ testfile.write(b'test')
+ except OSError:
+ # FIXME this should check the errno, or be removed altogether (race
+ # condition: the directory permissions could be changed between here
+ # and the actual install)
+ logger.info('Unable to write in "%s". Do you have the permissions ?'
+ % purelib_path)
+ return False
+
+ logger.info('Getting information about %r...', project)
+ try:
+ info = get_infos(project)
+ except InstallationException:
+ logger.info('Cound not find %r', project)
+ return False
+
+ if info['install'] == []:
+ logger.info('Nothing to install')
+ return False
+
+ install_path = get_config_var('base')
+ try:
+ install_from_infos(install_path,
+ info['install'], info['remove'], info['conflict'])
+
+ except InstallationConflict as e:
+ if logger.isEnabledFor(logging.INFO):
+ projects = ('%r %s' % (p.name, p.version) for p in e.args[0])
+ logger.info('%r conflicts with %s', project, ','.join(projects))
+
+ return True
diff --git a/Lib/packaging/manifest.py b/Lib/packaging/manifest.py
new file mode 100644
index 0000000..adf4633
--- /dev/null
+++ b/Lib/packaging/manifest.py
@@ -0,0 +1,377 @@
+"""Class representing the list of files in a distribution.
+
+The Manifest class can be used to:
+
+ - read or write a MANIFEST file
+ - read a template file and find out the file list
+"""
+# XXX todo: document + add tests
+import re
+import os
+import fnmatch
+
+from packaging import logger
+from packaging.util import write_file, convert_path
+from packaging.errors import (PackagingTemplateError,
+ PackagingInternalError)
+
+__all__ = ['Manifest']
+
+# a \ followed by some spaces + EOL
+_COLLAPSE_PATTERN = re.compile('\\\w*\n', re.M)
+_COMMENTED_LINE = re.compile('#.*?(?=\n)|\n(?=$)', re.M | re.S)
+
+
+class Manifest(object):
+ """A list of files built by on exploring the filesystem and filtered by
+ applying various patterns to what we find there.
+ """
+
+ def __init__(self):
+ self.allfiles = None
+ self.files = []
+
+ #
+ # Public API
+ #
+
+ def findall(self, dir=os.curdir):
+ self.allfiles = _findall(dir)
+
+ def append(self, item):
+ self.files.append(item)
+
+ def extend(self, items):
+ self.files.extend(items)
+
+ def sort(self):
+ # Not a strict lexical sort!
+ self.files = [os.path.join(*path_tuple) for path_tuple in
+ sorted(os.path.split(path) for path in self.files)]
+
+ def clear(self):
+ """Clear all collected files."""
+ self.files = []
+ if self.allfiles is not None:
+ self.allfiles = []
+
+ def remove_duplicates(self):
+ # Assumes list has been sorted!
+ for i in range(len(self.files) - 1, 0, -1):
+ if self.files[i] == self.files[i - 1]:
+ del self.files[i]
+
+ def read_template(self, path_or_file):
+ """Read and parse a manifest template file.
+ 'path' can be a path or a file-like object.
+
+ Updates the list accordingly.
+ """
+ if isinstance(path_or_file, str):
+ f = open(path_or_file)
+ else:
+ f = path_or_file
+
+ try:
+ content = f.read()
+ # first, let's unwrap collapsed lines
+ content = _COLLAPSE_PATTERN.sub('', content)
+ # next, let's remove commented lines and empty lines
+ content = _COMMENTED_LINE.sub('', content)
+
+ # now we have our cleaned up lines
+ lines = [line.strip() for line in content.split('\n')]
+ finally:
+ f.close()
+
+ for line in lines:
+ if line == '':
+ continue
+ try:
+ self._process_template_line(line)
+ except PackagingTemplateError as msg:
+ logger.warning("%s, %s", path_or_file, msg)
+
+ def write(self, path):
+ """Write the file list in 'self.filelist' (presumably as filled in
+ by 'add_defaults()' and 'read_template()') to the manifest file
+ named by 'self.manifest'.
+ """
+ if os.path.isfile(path):
+ with open(path) as fp:
+ first_line = fp.readline()
+
+ if first_line != '# file GENERATED by packaging, do NOT edit\n':
+ logger.info("not writing to manually maintained "
+ "manifest file %r", path)
+ return
+
+ self.sort()
+ self.remove_duplicates()
+ content = self.files[:]
+ content.insert(0, '# file GENERATED by packaging, do NOT edit')
+ logger.info("writing manifest file %r", path)
+ write_file(path, content)
+
+ def read(self, path):
+ """Read the manifest file (named by 'self.manifest') and use it to
+ fill in 'self.filelist', the list of files to include in the source
+ distribution.
+ """
+ logger.info("reading manifest file %r", path)
+ with open(path) as manifest:
+ for line in manifest.readlines():
+ self.append(line)
+
+ def exclude_pattern(self, pattern, anchor=True, prefix=None,
+ is_regex=False):
+ """Remove strings (presumably filenames) from 'files' that match
+ 'pattern'.
+
+ Other parameters are the same as for 'include_pattern()', above.
+ The list 'self.files' is modified in place. Return True if files are
+ found.
+ """
+ files_found = False
+ pattern_re = _translate_pattern(pattern, anchor, prefix, is_regex)
+ for i in range(len(self.files) - 1, -1, -1):
+ if pattern_re.search(self.files[i]):
+ del self.files[i]
+ files_found = True
+
+ return files_found
+
+ #
+ # Private API
+ #
+
+ def _parse_template_line(self, line):
+ words = line.split()
+ if len(words) == 1 and words[0] not in (
+ 'include', 'exclude', 'global-include', 'global-exclude',
+ 'recursive-include', 'recursive-exclude', 'graft', 'prune'):
+ # no action given, let's use the default 'include'
+ words.insert(0, 'include')
+
+ action = words[0]
+ patterns = dir = dir_pattern = None
+
+ if action in ('include', 'exclude',
+ 'global-include', 'global-exclude'):
+ if len(words) < 2:
+ raise PackagingTemplateError(
+ "%r expects <pattern1> <pattern2> ..." % action)
+
+ patterns = [convert_path(word) for word in words[1:]]
+
+ elif action in ('recursive-include', 'recursive-exclude'):
+ if len(words) < 3:
+ raise PackagingTemplateError(
+ "%r expects <dir> <pattern1> <pattern2> ..." % action)
+
+ dir = convert_path(words[1])
+ patterns = [convert_path(word) for word in words[2:]]
+
+ elif action in ('graft', 'prune'):
+ if len(words) != 2:
+ raise PackagingTemplateError(
+ "%r expects a single <dir_pattern>" % action)
+
+ dir_pattern = convert_path(words[1])
+
+ else:
+ raise PackagingTemplateError("unknown action %r" % action)
+
+ return action, patterns, dir, dir_pattern
+
+ def _process_template_line(self, line):
+ # Parse the line: split it up, make sure the right number of words
+ # is there, and return the relevant words. 'action' is always
+ # defined: it's the first word of the line. Which of the other
+ # three are defined depends on the action; it'll be either
+ # patterns, (dir and patterns), or (dir_pattern).
+ action, patterns, dir, dir_pattern = self._parse_template_line(line)
+
+ # OK, now we know that the action is valid and we have the
+ # right number of words on the line for that action -- so we
+ # can proceed with minimal error-checking.
+ if action == 'include':
+ for pattern in patterns:
+ if not self._include_pattern(pattern, anchor=True):
+ logger.warning("no files found matching %r", pattern)
+
+ elif action == 'exclude':
+ for pattern in patterns:
+ if not self.exclude_pattern(pattern, anchor=True):
+ logger.warning("no previously-included files "
+ "found matching %r", pattern)
+
+ elif action == 'global-include':
+ for pattern in patterns:
+ if not self._include_pattern(pattern, anchor=False):
+ logger.warning("no files found matching %r "
+ "anywhere in distribution", pattern)
+
+ elif action == 'global-exclude':
+ for pattern in patterns:
+ if not self.exclude_pattern(pattern, anchor=False):
+ logger.warning("no previously-included files "
+ "matching %r found anywhere in "
+ "distribution", pattern)
+
+ elif action == 'recursive-include':
+ for pattern in patterns:
+ if not self._include_pattern(pattern, prefix=dir):
+ logger.warning("no files found matching %r "
+ "under directory %r", pattern, dir)
+
+ elif action == 'recursive-exclude':
+ for pattern in patterns:
+ if not self.exclude_pattern(pattern, prefix=dir):
+ logger.warning("no previously-included files "
+ "matching %r found under directory %r",
+ pattern, dir)
+
+ elif action == 'graft':
+ if not self._include_pattern(None, prefix=dir_pattern):
+ logger.warning("no directories found matching %r",
+ dir_pattern)
+
+ elif action == 'prune':
+ if not self.exclude_pattern(None, prefix=dir_pattern):
+ logger.warning("no previously-included directories found "
+ "matching %r", dir_pattern)
+ else:
+ raise PackagingInternalError(
+ "this cannot happen: invalid action %r" % action)
+
+ def _include_pattern(self, pattern, anchor=True, prefix=None,
+ is_regex=False):
+ """Select strings (presumably filenames) from 'self.files' that
+ match 'pattern', a Unix-style wildcard (glob) pattern.
+
+ Patterns are not quite the same as implemented by the 'fnmatch'
+ module: '*' and '?' match non-special characters, where "special"
+ is platform-dependent: slash on Unix; colon, slash, and backslash on
+ DOS/Windows; and colon on Mac OS.
+
+ If 'anchor' is true (the default), then the pattern match is more
+ stringent: "*.py" will match "foo.py" but not "foo/bar.py". If
+ 'anchor' is false, both of these will match.
+
+ If 'prefix' is supplied, then only filenames starting with 'prefix'
+ (itself a pattern) and ending with 'pattern', with anything in between
+ them, will match. 'anchor' is ignored in this case.
+
+ If 'is_regex' is true, 'anchor' and 'prefix' are ignored, and
+ 'pattern' is assumed to be either a string containing a regex or a
+ regex object -- no translation is done, the regex is just compiled
+ and used as-is.
+
+ Selected strings will be added to self.files.
+
+ Return True if files are found.
+ """
+ files_found = False
+ pattern_re = _translate_pattern(pattern, anchor, prefix, is_regex)
+
+ # delayed loading of allfiles list
+ if self.allfiles is None:
+ self.findall()
+
+ for name in self.allfiles:
+ if pattern_re.search(name):
+ self.files.append(name)
+ files_found = True
+
+ return files_found
+
+
+#
+# Utility functions
+#
+def _findall(dir=os.curdir):
+ """Find all files under 'dir' and return the list of full filenames
+ (relative to 'dir').
+ """
+ from stat import S_ISREG, S_ISDIR, S_ISLNK
+
+ list = []
+ stack = [dir]
+ pop = stack.pop
+ push = stack.append
+
+ while stack:
+ dir = pop()
+ names = os.listdir(dir)
+
+ for name in names:
+ if dir != os.curdir: # avoid the dreaded "./" syndrome
+ fullname = os.path.join(dir, name)
+ else:
+ fullname = name
+
+ # Avoid excess stat calls -- just one will do, thank you!
+ stat = os.stat(fullname)
+ mode = stat.st_mode
+ if S_ISREG(mode):
+ list.append(fullname)
+ elif S_ISDIR(mode) and not S_ISLNK(mode):
+ push(fullname)
+
+ return list
+
+
+def _glob_to_re(pattern):
+ """Translate a shell-like glob pattern to a regular expression.
+
+ Return a string containing the regex. Differs from
+ 'fnmatch.translate()' in that '*' does not match "special characters"
+ (which are platform-specific).
+ """
+ pattern_re = fnmatch.translate(pattern)
+
+ # '?' and '*' in the glob pattern become '.' and '.*' in the RE, which
+ # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix,
+ # and by extension they shouldn't match such "special characters" under
+ # any OS. So change all non-escaped dots in the RE to match any
+ # character except the special characters.
+ # XXX currently the "special characters" are just slash -- i.e. this is
+ # Unix-only.
+ pattern_re = re.sub(r'((?<!\\)(\\\\)*)\.', r'\1[^/]', pattern_re)
+
+ return pattern_re
+
+
+def _translate_pattern(pattern, anchor=True, prefix=None, is_regex=False):
+ """Translate a shell-like wildcard pattern to a compiled regular
+ expression.
+
+ Return the compiled regex. If 'is_regex' true,
+ then 'pattern' is directly compiled to a regex (if it's a string)
+ or just returned as-is (assumes it's a regex object).
+ """
+ if is_regex:
+ if isinstance(pattern, str):
+ return re.compile(pattern)
+ else:
+ return pattern
+
+ if pattern:
+ pattern_re = _glob_to_re(pattern)
+ else:
+ pattern_re = ''
+
+ if prefix is not None:
+ # ditch end of pattern character
+ empty_pattern = _glob_to_re('')
+ prefix_re = _glob_to_re(prefix)[:-len(empty_pattern)]
+ # match both path separators, as in Postel's principle
+ sep_pat = "[" + re.escape(os.path.sep + os.path.altsep
+ if os.path.altsep else os.path.sep) + "]"
+ pattern_re = "^" + sep_pat.join([prefix_re, ".*" + pattern_re])
+ else: # no prefix -- respect anchor flag
+ if anchor:
+ pattern_re = "^" + pattern_re
+
+ return re.compile(pattern_re)
diff --git a/Lib/packaging/markers.py b/Lib/packaging/markers.py
new file mode 100644
index 0000000..4bbac7e
--- /dev/null
+++ b/Lib/packaging/markers.py
@@ -0,0 +1,187 @@
+"""Parser for the environment markers micro-language defined in PEP 345."""
+
+import sys
+import platform
+import os
+
+from tokenize import tokenize, NAME, OP, STRING, ENDMARKER, ENCODING
+from io import BytesIO
+
+__all__ = ['interpret']
+
+
+# allowed operators
+_OPERATORS = {'==': lambda x, y: x == y,
+ '!=': lambda x, y: x != y,
+ '>': lambda x, y: x > y,
+ '>=': lambda x, y: x >= y,
+ '<': lambda x, y: x < y,
+ '<=': lambda x, y: x <= y,
+ 'in': lambda x, y: x in y,
+ 'not in': lambda x, y: x not in y}
+
+
+def _operate(operation, x, y):
+ return _OPERATORS[operation](x, y)
+
+
+# restricted set of variables
+_VARS = {'sys.platform': sys.platform,
+ 'python_version': sys.version[:3],
+ 'python_full_version': sys.version.split(' ', 1)[0],
+ 'os.name': os.name,
+ 'platform.version': platform.version(),
+ 'platform.machine': platform.machine(),
+ 'platform.python_implementation': platform.python_implementation()}
+
+
+class _Operation:
+
+ def __init__(self, execution_context=None):
+ self.left = None
+ self.op = None
+ self.right = None
+ if execution_context is None:
+ execution_context = {}
+ self.execution_context = execution_context
+
+ def _get_var(self, name):
+ if name in self.execution_context:
+ return self.execution_context[name]
+ return _VARS[name]
+
+ def __repr__(self):
+ return '%s %s %s' % (self.left, self.op, self.right)
+
+ def _is_string(self, value):
+ if value is None or len(value) < 2:
+ return False
+ for delimiter in '"\'':
+ if value[0] == value[-1] == delimiter:
+ return True
+ return False
+
+ def _is_name(self, value):
+ return value in _VARS
+
+ def _convert(self, value):
+ if value in _VARS:
+ return self._get_var(value)
+ return value.strip('"\'')
+
+ def _check_name(self, value):
+ if value not in _VARS:
+ raise NameError(value)
+
+ def _nonsense_op(self):
+ msg = 'This operation is not supported : "%s"' % self
+ raise SyntaxError(msg)
+
+ def __call__(self):
+ # make sure we do something useful
+ if self._is_string(self.left):
+ if self._is_string(self.right):
+ self._nonsense_op()
+ self._check_name(self.right)
+ else:
+ if not self._is_string(self.right):
+ self._nonsense_op()
+ self._check_name(self.left)
+
+ if self.op not in _OPERATORS:
+ raise TypeError('Operator not supported "%s"' % self.op)
+
+ left = self._convert(self.left)
+ right = self._convert(self.right)
+ return _operate(self.op, left, right)
+
+
+class _OR:
+ def __init__(self, left, right=None):
+ self.left = left
+ self.right = right
+
+ def filled(self):
+ return self.right is not None
+
+ def __repr__(self):
+ return 'OR(%r, %r)' % (self.left, self.right)
+
+ def __call__(self):
+ return self.left() or self.right()
+
+
+class _AND:
+ def __init__(self, left, right=None):
+ self.left = left
+ self.right = right
+
+ def filled(self):
+ return self.right is not None
+
+ def __repr__(self):
+ return 'AND(%r, %r)' % (self.left, self.right)
+
+ def __call__(self):
+ return self.left() and self.right()
+
+
+def interpret(marker, execution_context=None):
+ """Interpret a marker and return a result depending on environment."""
+ marker = marker.strip().encode()
+ ops = []
+ op_starting = True
+ for token in tokenize(BytesIO(marker).readline):
+ # Unpack token
+ toktype, tokval, rowcol, line, logical_line = token
+ if toktype not in (NAME, OP, STRING, ENDMARKER, ENCODING):
+ raise SyntaxError('Type not supported "%s"' % tokval)
+
+ if op_starting:
+ op = _Operation(execution_context)
+ if len(ops) > 0:
+ last = ops[-1]
+ if isinstance(last, (_OR, _AND)) and not last.filled():
+ last.right = op
+ else:
+ ops.append(op)
+ else:
+ ops.append(op)
+ op_starting = False
+ else:
+ op = ops[-1]
+
+ if (toktype == ENDMARKER or
+ (toktype == NAME and tokval in ('and', 'or'))):
+ if toktype == NAME and tokval == 'and':
+ ops.append(_AND(ops.pop()))
+ elif toktype == NAME and tokval == 'or':
+ ops.append(_OR(ops.pop()))
+ op_starting = True
+ continue
+
+ if isinstance(op, (_OR, _AND)) and op.right is not None:
+ op = op.right
+
+ if ((toktype in (NAME, STRING) and tokval not in ('in', 'not'))
+ or (toktype == OP and tokval == '.')):
+ if op.op is None:
+ if op.left is None:
+ op.left = tokval
+ else:
+ op.left += tokval
+ else:
+ if op.right is None:
+ op.right = tokval
+ else:
+ op.right += tokval
+ elif toktype == OP or tokval in ('in', 'not'):
+ if tokval == 'in' and op.op == 'not':
+ op.op = 'not in'
+ else:
+ op.op = tokval
+
+ for op in ops:
+ if not op():
+ return False
+ return True
diff --git a/Lib/packaging/metadata.py b/Lib/packaging/metadata.py
new file mode 100644
index 0000000..2993ebb
--- /dev/null
+++ b/Lib/packaging/metadata.py
@@ -0,0 +1,570 @@
+"""Implementation of the Metadata for Python packages PEPs.
+
+Supports all metadata formats (1.0, 1.1, 1.2).
+"""
+
+import re
+import logging
+
+from io import StringIO
+from email import message_from_file
+from packaging import logger
+from packaging.markers import interpret
+from packaging.version import (is_valid_predicate, is_valid_version,
+ is_valid_versions)
+from packaging.errors import (MetadataMissingError,
+ MetadataConflictError,
+ MetadataUnrecognizedVersionError)
+
+try:
+ # docutils is installed
+ from docutils.utils import Reporter
+ from docutils.parsers.rst import Parser
+ from docutils import frontend
+ from docutils import nodes
+
+ class SilentReporter(Reporter):
+
+ def __init__(self, source, report_level, halt_level, stream=None,
+ debug=0, encoding='ascii', error_handler='replace'):
+ self.messages = []
+ super(SilentReporter, self).__init__(
+ source, report_level, halt_level, stream,
+ debug, encoding, error_handler)
+
+ def system_message(self, level, message, *children, **kwargs):
+ self.messages.append((level, message, children, kwargs))
+
+ _HAS_DOCUTILS = True
+except ImportError:
+ # docutils is not installed
+ _HAS_DOCUTILS = False
+
+# public API of this module
+__all__ = ['Metadata', 'PKG_INFO_ENCODING', 'PKG_INFO_PREFERRED_VERSION']
+
+# Encoding used for the PKG-INFO files
+PKG_INFO_ENCODING = 'utf-8'
+
+# preferred version. Hopefully will be changed
+# to 1.2 once PEP 345 is supported everywhere
+PKG_INFO_PREFERRED_VERSION = '1.0'
+
+_LINE_PREFIX = re.compile('\n \|')
+_241_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform',
+ 'Summary', 'Description',
+ 'Keywords', 'Home-page', 'Author', 'Author-email',
+ 'License')
+
+_314_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform',
+ 'Supported-Platform', 'Summary', 'Description',
+ 'Keywords', 'Home-page', 'Author', 'Author-email',
+ 'License', 'Classifier', 'Download-URL', 'Obsoletes',
+ 'Provides', 'Requires')
+
+_314_MARKERS = ('Obsoletes', 'Provides', 'Requires', 'Classifier',
+ 'Download-URL')
+
+_345_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform',
+ 'Supported-Platform', 'Summary', 'Description',
+ 'Keywords', 'Home-page', 'Author', 'Author-email',
+ 'Maintainer', 'Maintainer-email', 'License',
+ 'Classifier', 'Download-URL', 'Obsoletes-Dist',
+ 'Project-URL', 'Provides-Dist', 'Requires-Dist',
+ 'Requires-Python', 'Requires-External')
+
+_345_MARKERS = ('Provides-Dist', 'Requires-Dist', 'Requires-Python',
+ 'Obsoletes-Dist', 'Requires-External', 'Maintainer',
+ 'Maintainer-email', 'Project-URL')
+
+_ALL_FIELDS = set()
+_ALL_FIELDS.update(_241_FIELDS)
+_ALL_FIELDS.update(_314_FIELDS)
+_ALL_FIELDS.update(_345_FIELDS)
+
+
+def _version2fieldlist(version):
+ if version == '1.0':
+ return _241_FIELDS
+ elif version == '1.1':
+ return _314_FIELDS
+ elif version == '1.2':
+ return _345_FIELDS
+ raise MetadataUnrecognizedVersionError(version)
+
+
+def _best_version(fields):
+ """Detect the best version depending on the fields used."""
+ def _has_marker(keys, markers):
+ for marker in markers:
+ if marker in keys:
+ return True
+ return False
+
+ keys = list(fields)
+ possible_versions = ['1.0', '1.1', '1.2']
+
+ # first let's try to see if a field is not part of one of the version
+ for key in keys:
+ if key not in _241_FIELDS and '1.0' in possible_versions:
+ possible_versions.remove('1.0')
+ if key not in _314_FIELDS and '1.1' in possible_versions:
+ possible_versions.remove('1.1')
+ if key not in _345_FIELDS and '1.2' in possible_versions:
+ possible_versions.remove('1.2')
+
+ # possible_version contains qualified versions
+ if len(possible_versions) == 1:
+ return possible_versions[0] # found !
+ elif len(possible_versions) == 0:
+ raise MetadataConflictError('Unknown metadata set')
+
+ # let's see if one unique marker is found
+ is_1_1 = '1.1' in possible_versions and _has_marker(keys, _314_MARKERS)
+ is_1_2 = '1.2' in possible_versions and _has_marker(keys, _345_MARKERS)
+ if is_1_1 and is_1_2:
+ raise MetadataConflictError('You used incompatible 1.1 and 1.2 fields')
+
+ # we have the choice, either 1.0, or 1.2
+ # - 1.0 has a broken Summary field but works with all tools
+ # - 1.1 is to avoid
+ # - 1.2 fixes Summary but is not widespread yet
+ if not is_1_1 and not is_1_2:
+ # we couldn't find any specific marker
+ if PKG_INFO_PREFERRED_VERSION in possible_versions:
+ return PKG_INFO_PREFERRED_VERSION
+ if is_1_1:
+ return '1.1'
+
+ # default marker when 1.0 is disqualified
+ return '1.2'
+
+
+_ATTR2FIELD = {
+ 'metadata_version': 'Metadata-Version',
+ 'name': 'Name',
+ 'version': 'Version',
+ 'platform': 'Platform',
+ 'supported_platform': 'Supported-Platform',
+ 'summary': 'Summary',
+ 'description': 'Description',
+ 'keywords': 'Keywords',
+ 'home_page': 'Home-page',
+ 'author': 'Author',
+ 'author_email': 'Author-email',
+ 'maintainer': 'Maintainer',
+ 'maintainer_email': 'Maintainer-email',
+ 'license': 'License',
+ 'classifier': 'Classifier',
+ 'download_url': 'Download-URL',
+ 'obsoletes_dist': 'Obsoletes-Dist',
+ 'provides_dist': 'Provides-Dist',
+ 'requires_dist': 'Requires-Dist',
+ 'requires_python': 'Requires-Python',
+ 'requires_external': 'Requires-External',
+ 'requires': 'Requires',
+ 'provides': 'Provides',
+ 'obsoletes': 'Obsoletes',
+ 'project_url': 'Project-URL',
+}
+
+_PREDICATE_FIELDS = ('Requires-Dist', 'Obsoletes-Dist', 'Provides-Dist')
+_VERSIONS_FIELDS = ('Requires-Python',)
+_VERSION_FIELDS = ('Version',)
+_LISTFIELDS = ('Platform', 'Classifier', 'Obsoletes',
+ 'Requires', 'Provides', 'Obsoletes-Dist',
+ 'Provides-Dist', 'Requires-Dist', 'Requires-External',
+ 'Project-URL', 'Supported-Platform')
+_LISTTUPLEFIELDS = ('Project-URL',)
+
+_ELEMENTSFIELD = ('Keywords',)
+
+_UNICODEFIELDS = ('Author', 'Maintainer', 'Summary', 'Description')
+
+_MISSING = object()
+
+_FILESAFE = re.compile('[^A-Za-z0-9.]+')
+
+
+class Metadata:
+ """The metadata of a release.
+
+ Supports versions 1.0, 1.1 and 1.2 (auto-detected). You can
+ instantiate the class with one of these arguments (or none):
+ - *path*, the path to a METADATA file
+ - *fileobj* give a file-like object with METADATA as content
+ - *mapping* is a dict-like object
+ """
+ # TODO document that execution_context and platform_dependent are used
+ # to filter on query, not when setting a key
+ # also document the mapping API and UNKNOWN default key
+
+ def __init__(self, path=None, platform_dependent=False,
+ execution_context=None, fileobj=None, mapping=None):
+ self._fields = {}
+ self.requires_files = []
+ self.docutils_support = _HAS_DOCUTILS
+ self.platform_dependent = platform_dependent
+ self.execution_context = execution_context
+ if [path, fileobj, mapping].count(None) < 2:
+ raise TypeError('path, fileobj and mapping are exclusive')
+ if path is not None:
+ self.read(path)
+ elif fileobj is not None:
+ self.read_file(fileobj)
+ elif mapping is not None:
+ self.update(mapping)
+
+ def _set_best_version(self):
+ self._fields['Metadata-Version'] = _best_version(self._fields)
+
+ def _write_field(self, file, name, value):
+ file.write('%s: %s\n' % (name, value))
+
+ def __getitem__(self, name):
+ return self.get(name)
+
+ def __setitem__(self, name, value):
+ return self.set(name, value)
+
+ def __delitem__(self, name):
+ field_name = self._convert_name(name)
+ try:
+ del self._fields[field_name]
+ except KeyError:
+ raise KeyError(name)
+ self._set_best_version()
+
+ def __contains__(self, name):
+ return (name in self._fields or
+ self._convert_name(name) in self._fields)
+
+ def _convert_name(self, name):
+ if name in _ALL_FIELDS:
+ return name
+ name = name.replace('-', '_').lower()
+ return _ATTR2FIELD.get(name, name)
+
+ def _default_value(self, name):
+ if name in _LISTFIELDS or name in _ELEMENTSFIELD:
+ return []
+ return 'UNKNOWN'
+
+ def _check_rst_data(self, data):
+ """Return warnings when the provided data has syntax errors."""
+ source_path = StringIO()
+ parser = Parser()
+ settings = frontend.OptionParser().get_default_values()
+ settings.tab_width = 4
+ settings.pep_references = None
+ settings.rfc_references = None
+ reporter = SilentReporter(source_path,
+ settings.report_level,
+ settings.halt_level,
+ stream=settings.warning_stream,
+ debug=settings.debug,
+ encoding=settings.error_encoding,
+ error_handler=settings.error_encoding_error_handler)
+
+ document = nodes.document(settings, reporter, source=source_path)
+ document.note_source(source_path, -1)
+ try:
+ parser.parse(data, document)
+ except AttributeError:
+ reporter.messages.append((-1, 'Could not finish the parsing.',
+ '', {}))
+
+ return reporter.messages
+
+ def _platform(self, value):
+ if not self.platform_dependent or ';' not in value:
+ return True, value
+ value, marker = value.split(';')
+ return interpret(marker, self.execution_context), value
+
+ def _remove_line_prefix(self, value):
+ return _LINE_PREFIX.sub('\n', value)
+
+ #
+ # Public API
+ #
+ def get_fullname(self, filesafe=False):
+ """Return the distribution name with version.
+
+ If filesafe is true, return a filename-escaped form."""
+ name, version = self['Name'], self['Version']
+ if filesafe:
+ # For both name and version any runs of non-alphanumeric or '.'
+ # characters are replaced with a single '-'. Additionally any
+ # spaces in the version string become '.'
+ name = _FILESAFE.sub('-', name)
+ version = _FILESAFE.sub('-', version.replace(' ', '.'))
+ return '%s-%s' % (name, version)
+
+ def is_metadata_field(self, name):
+ """return True if name is a valid metadata key"""
+ name = self._convert_name(name)
+ return name in _ALL_FIELDS
+
+ def is_multi_field(self, name):
+ name = self._convert_name(name)
+ return name in _LISTFIELDS
+
+ def read(self, filepath):
+ """Read the metadata values from a file path."""
+ with open(filepath, 'r', encoding='utf-8') as fp:
+ self.read_file(fp)
+
+ def read_file(self, fileob):
+ """Read the metadata values from a file object."""
+ msg = message_from_file(fileob)
+ self._fields['Metadata-Version'] = msg['metadata-version']
+
+ for field in _version2fieldlist(self['Metadata-Version']):
+ if field in _LISTFIELDS:
+ # we can have multiple lines
+ values = msg.get_all(field)
+ if field in _LISTTUPLEFIELDS and values is not None:
+ values = [tuple(value.split(',')) for value in values]
+ self.set(field, values)
+ else:
+ # single line
+ value = msg[field]
+ if value is not None and value != 'UNKNOWN':
+ self.set(field, value)
+
+ def write(self, filepath):
+ """Write the metadata fields to filepath."""
+ with open(filepath, 'w', encoding='utf-8') as fp:
+ self.write_file(fp)
+
+ def write_file(self, fileobject):
+ """Write the PKG-INFO format data to a file object."""
+ self._set_best_version()
+ for field in _version2fieldlist(self['Metadata-Version']):
+ values = self.get(field)
+ if field in _ELEMENTSFIELD:
+ self._write_field(fileobject, field, ','.join(values))
+ continue
+ if field not in _LISTFIELDS:
+ if field == 'Description':
+ values = values.replace('\n', '\n |')
+ values = [values]
+
+ if field in _LISTTUPLEFIELDS:
+ values = [','.join(value) for value in values]
+
+ for value in values:
+ self._write_field(fileobject, field, value)
+
+ def update(self, other=None, **kwargs):
+ """Set metadata values from the given iterable `other` and kwargs.
+
+ Behavior is like `dict.update`: If `other` has a ``keys`` method,
+ they are looped over and ``self[key]`` is assigned ``other[key]``.
+ Else, ``other`` is an iterable of ``(key, value)`` iterables.
+
+ Keys that don't match a metadata field or that have an empty value are
+ dropped.
+ """
+ # XXX the code should just use self.set, which does tbe same checks and
+ # conversions already, but that would break packaging.pypi: it uses the
+ # update method, which does not call _set_best_version (which set
+ # does), and thus allows having a Metadata object (as long as you don't
+ # modify or write it) with extra fields from PyPI that are not fields
+ # defined in Metadata PEPs. to solve it, the best_version system
+ # should be reworked so that it's called only for writing, or in a new
+ # strict mode, or with a new, more lax Metadata subclass in p7g.pypi
+ def _set(key, value):
+ if key in _ATTR2FIELD and value:
+ self.set(self._convert_name(key), value)
+
+ if not other:
+ # other is None or empty container
+ pass
+ elif hasattr(other, 'keys'):
+ for k in other.keys():
+ _set(k, other[k])
+ else:
+ for k, v in other:
+ _set(k, v)
+
+ if kwargs:
+ for k, v in kwargs.items():
+ _set(k, v)
+
+ def set(self, name, value):
+ """Control then set a metadata field."""
+ name = self._convert_name(name)
+
+ if ((name in _ELEMENTSFIELD or name == 'Platform') and
+ not isinstance(value, (list, tuple))):
+ if isinstance(value, str):
+ value = [v.strip() for v in value.split(',')]
+ else:
+ value = []
+ elif (name in _LISTFIELDS and
+ not isinstance(value, (list, tuple))):
+ if isinstance(value, str):
+ value = [value]
+ else:
+ value = []
+
+ if logger.isEnabledFor(logging.WARNING):
+ project_name = self['Name']
+
+ if name in _PREDICATE_FIELDS and value is not None:
+ for v in value:
+ # check that the values are valid predicates
+ if not is_valid_predicate(v.split(';')[0]):
+ logger.warning(
+ '%r: %r is not a valid predicate (field %r)',
+ project_name, v, name)
+ # FIXME this rejects UNKNOWN, is that right?
+ elif name in _VERSIONS_FIELDS and value is not None:
+ if not is_valid_versions(value):
+ logger.warning('%r: %r is not a valid version (field %r)',
+ project_name, value, name)
+ elif name in _VERSION_FIELDS and value is not None:
+ if not is_valid_version(value):
+ logger.warning('%r: %r is not a valid version (field %r)',
+ project_name, value, name)
+
+ if name in _UNICODEFIELDS:
+ if name == 'Description':
+ value = self._remove_line_prefix(value)
+
+ self._fields[name] = value
+ self._set_best_version()
+
+ def get(self, name, default=_MISSING):
+ """Get a metadata field."""
+ name = self._convert_name(name)
+ if name not in self._fields:
+ if default is _MISSING:
+ default = self._default_value(name)
+ return default
+ if name in _UNICODEFIELDS:
+ value = self._fields[name]
+ return value
+ elif name in _LISTFIELDS:
+ value = self._fields[name]
+ if value is None:
+ return []
+ res = []
+ for val in value:
+ valid, val = self._platform(val)
+ if not valid:
+ continue
+ if name not in _LISTTUPLEFIELDS:
+ res.append(val)
+ else:
+ # That's for Project-URL
+ res.append((val[0], val[1]))
+ return res
+
+ elif name in _ELEMENTSFIELD:
+ valid, value = self._platform(self._fields[name])
+ if not valid:
+ return []
+ if isinstance(value, str):
+ return value.split(',')
+ valid, value = self._platform(self._fields[name])
+ if not valid:
+ return None
+ return value
+
+ def check(self, strict=False, restructuredtext=False):
+ """Check if the metadata is compliant. If strict is False then raise if
+ no Name or Version are provided"""
+ # XXX should check the versions (if the file was loaded)
+ missing, warnings = [], []
+
+ for attr in ('Name', 'Version'): # required by PEP 345
+ if attr not in self:
+ missing.append(attr)
+
+ if strict and missing != []:
+ msg = 'missing required metadata: %s' % ', '.join(missing)
+ raise MetadataMissingError(msg)
+
+ for attr in ('Home-page', 'Author'):
+ if attr not in self:
+ missing.append(attr)
+
+ if _HAS_DOCUTILS and restructuredtext:
+ warnings.extend(self._check_rst_data(self['Description']))
+
+ # checking metadata 1.2 (XXX needs to check 1.1, 1.0)
+ if self['Metadata-Version'] != '1.2':
+ return missing, warnings
+
+ def is_valid_predicates(value):
+ for v in value:
+ if not is_valid_predicate(v.split(';')[0]):
+ return False
+ return True
+
+ for fields, controller in ((_PREDICATE_FIELDS, is_valid_predicates),
+ (_VERSIONS_FIELDS, is_valid_versions),
+ (_VERSION_FIELDS, is_valid_version)):
+ for field in fields:
+ value = self.get(field, None)
+ if value is not None and not controller(value):
+ warnings.append('Wrong value for %r: %s' % (field, value))
+
+ return missing, warnings
+
+ def todict(self):
+ """Return fields as a dict.
+
+ Field names will be converted to use the underscore-lowercase style
+ instead of hyphen-mixed case (i.e. home_page instead of Home-page).
+ """
+ data = {
+ 'metadata_version': self['Metadata-Version'],
+ 'name': self['Name'],
+ 'version': self['Version'],
+ 'summary': self['Summary'],
+ 'home_page': self['Home-page'],
+ 'author': self['Author'],
+ 'author_email': self['Author-email'],
+ 'license': self['License'],
+ 'description': self['Description'],
+ 'keywords': self['Keywords'],
+ 'platform': self['Platform'],
+ 'classifier': self['Classifier'],
+ 'download_url': self['Download-URL'],
+ }
+
+ if self['Metadata-Version'] == '1.2':
+ data['requires_dist'] = self['Requires-Dist']
+ data['requires_python'] = self['Requires-Python']
+ data['requires_external'] = self['Requires-External']
+ data['provides_dist'] = self['Provides-Dist']
+ data['obsoletes_dist'] = self['Obsoletes-Dist']
+ data['project_url'] = [','.join(url) for url in
+ self['Project-URL']]
+
+ elif self['Metadata-Version'] == '1.1':
+ data['provides'] = self['Provides']
+ data['requires'] = self['Requires']
+ data['obsoletes'] = self['Obsoletes']
+
+ return data
+
+ # Mapping API
+ # XXX these methods should return views or sets in 3.x
+
+ def keys(self):
+ return list(_version2fieldlist(self['Metadata-Version']))
+
+ def __iter__(self):
+ for key in self.keys():
+ yield key
+
+ def values(self):
+ return [self[key] for key in self.keys()]
+
+ def items(self):
+ return [(key, self[key]) for key in self.keys()]
diff --git a/Lib/packaging/pypi/__init__.py b/Lib/packaging/pypi/__init__.py
new file mode 100644
index 0000000..5660c50
--- /dev/null
+++ b/Lib/packaging/pypi/__init__.py
@@ -0,0 +1,9 @@
+"""Low-level and high-level APIs to interact with project indexes."""
+
+__all__ = ['simple',
+ 'xmlrpc',
+ 'dist',
+ 'errors',
+ 'mirrors']
+
+from packaging.pypi.dist import ReleaseInfo, ReleasesList, DistInfo
diff --git a/Lib/packaging/pypi/base.py b/Lib/packaging/pypi/base.py
new file mode 100644
index 0000000..305fca9
--- /dev/null
+++ b/Lib/packaging/pypi/base.py
@@ -0,0 +1,48 @@
+"""Base class for index crawlers."""
+
+from packaging.pypi.dist import ReleasesList
+
+
+class BaseClient:
+ """Base class containing common methods for the index crawlers/clients"""
+
+ def __init__(self, prefer_final, prefer_source):
+ self._prefer_final = prefer_final
+ self._prefer_source = prefer_source
+ self._index = self
+
+ def _get_prefer_final(self, prefer_final=None):
+ """Return the prefer_final internal parameter or the specified one if
+ provided"""
+ if prefer_final:
+ return prefer_final
+ else:
+ return self._prefer_final
+
+ def _get_prefer_source(self, prefer_source=None):
+ """Return the prefer_source internal parameter or the specified one if
+ provided"""
+ if prefer_source:
+ return prefer_source
+ else:
+ return self._prefer_source
+
+ def _get_project(self, project_name):
+ """Return an project instance, create it if necessary"""
+ return self._projects.setdefault(project_name.lower(),
+ ReleasesList(project_name, index=self._index))
+
+ def download_distribution(self, requirements, temp_path=None,
+ prefer_source=None, prefer_final=None):
+ """Download a distribution from the last release according to the
+ requirements.
+
+ If temp_path is provided, download to this path, otherwise, create a
+ temporary location for the download and return it.
+ """
+ prefer_final = self._get_prefer_final(prefer_final)
+ prefer_source = self._get_prefer_source(prefer_source)
+ release = self.get_release(requirements, prefer_final)
+ if release:
+ dist = release.get_distribution(prefer_source=prefer_source)
+ return dist.download(temp_path)
diff --git a/Lib/packaging/pypi/dist.py b/Lib/packaging/pypi/dist.py
new file mode 100644
index 0000000..541465e
--- /dev/null
+++ b/Lib/packaging/pypi/dist.py
@@ -0,0 +1,544 @@
+"""Classes representing releases and distributions retrieved from indexes.
+
+A project (= unique name) can have several releases (= versions) and
+each release can have several distributions (= sdist and bdists).
+
+Release objects contain metadata-related information (see PEP 376);
+distribution objects contain download-related information.
+"""
+
+import re
+import hashlib
+import tempfile
+import urllib.request
+import urllib.parse
+import urllib.error
+import urllib.parse
+from shutil import unpack_archive
+
+from packaging.errors import IrrationalVersionError
+from packaging.version import (suggest_normalized_version, NormalizedVersion,
+ get_version_predicate)
+from packaging.metadata import Metadata
+from packaging.pypi.errors import (HashDoesNotMatch, UnsupportedHashName,
+ CantParseArchiveName)
+
+
+__all__ = ['ReleaseInfo', 'DistInfo', 'ReleasesList', 'get_infos_from_url']
+
+EXTENSIONS = ".tar.gz .tar.bz2 .tar .zip .tgz .egg".split()
+MD5_HASH = re.compile(r'^.*#md5=([a-f0-9]+)$')
+DIST_TYPES = ['bdist', 'sdist']
+
+
+class IndexReference:
+ """Mixin used to store the index reference"""
+ def set_index(self, index=None):
+ self._index = index
+
+
+class ReleaseInfo(IndexReference):
+ """Represent a release of a project (a project with a specific version).
+ The release contain the _metadata informations related to this specific
+ version, and is also a container for distribution related informations.
+
+ See the DistInfo class for more information about distributions.
+ """
+
+ def __init__(self, name, version, metadata=None, hidden=False,
+ index=None, **kwargs):
+ """
+ :param name: the name of the distribution
+ :param version: the version of the distribution
+ :param metadata: the metadata fields of the release.
+ :type metadata: dict
+ :param kwargs: optional arguments for a new distribution.
+ """
+ self.set_index(index)
+ self.name = name
+ self._version = None
+ self.version = version
+ if metadata:
+ self.metadata = Metadata(mapping=metadata)
+ else:
+ self.metadata = None
+ self.dists = {}
+ self.hidden = hidden
+
+ if 'dist_type' in kwargs:
+ dist_type = kwargs.pop('dist_type')
+ self.add_distribution(dist_type, **kwargs)
+
+ def set_version(self, version):
+ try:
+ self._version = NormalizedVersion(version)
+ except IrrationalVersionError:
+ suggestion = suggest_normalized_version(version)
+ if suggestion:
+ self.version = suggestion
+ else:
+ raise IrrationalVersionError(version)
+
+ def get_version(self):
+ return self._version
+
+ version = property(get_version, set_version)
+
+ def fetch_metadata(self):
+ """If the metadata is not set, use the indexes to get it"""
+ if not self.metadata:
+ self._index.get_metadata(self.name, str(self.version))
+ return self.metadata
+
+ @property
+ def is_final(self):
+ """proxy to version.is_final"""
+ return self.version.is_final
+
+ def fetch_distributions(self):
+ if self.dists is None:
+ self._index.get_distributions(self.name, str(self.version))
+ if self.dists is None:
+ self.dists = {}
+ return self.dists
+
+ def add_distribution(self, dist_type='sdist', python_version=None,
+ **params):
+ """Add distribution informations to this release.
+ If distribution information is already set for this distribution type,
+ add the given url paths to the distribution. This can be useful while
+ some of them fails to download.
+
+ :param dist_type: the distribution type (eg. "sdist", "bdist", etc.)
+ :param params: the fields to be passed to the distribution object
+ (see the :class:DistInfo constructor).
+ """
+ if dist_type not in DIST_TYPES:
+ raise ValueError(dist_type)
+ if dist_type in self.dists:
+ self.dists[dist_type].add_url(**params)
+ else:
+ self.dists[dist_type] = DistInfo(self, dist_type,
+ index=self._index, **params)
+ if python_version:
+ self.dists[dist_type].python_version = python_version
+
+ def get_distribution(self, dist_type=None, prefer_source=True):
+ """Return a distribution.
+
+ If dist_type is set, find first for this distribution type, and just
+ act as an alias of __get_item__.
+
+ If prefer_source is True, search first for source distribution, and if
+ not return one existing distribution.
+ """
+ if len(self.dists) == 0:
+ raise LookupError
+ if dist_type:
+ return self[dist_type]
+ if prefer_source:
+ if "sdist" in self.dists:
+ dist = self["sdist"]
+ else:
+ dist = next(self.dists.values())
+ return dist
+
+ def unpack(self, path=None, prefer_source=True):
+ """Unpack the distribution to the given path.
+
+ If not destination is given, creates a temporary location.
+
+ Returns the location of the extracted files (root).
+ """
+ return self.get_distribution(prefer_source=prefer_source)\
+ .unpack(path=path)
+
+ def download(self, temp_path=None, prefer_source=True):
+ """Download the distribution, using the requirements.
+
+ If more than one distribution match the requirements, use the last
+ version.
+ Download the distribution, and put it in the temp_path. If no temp_path
+ is given, creates and return one.
+
+ Returns the complete absolute path to the downloaded archive.
+ """
+ return self.get_distribution(prefer_source=prefer_source)\
+ .download(path=temp_path)
+
+ def set_metadata(self, metadata):
+ if not self.metadata:
+ self.metadata = Metadata()
+ self.metadata.update(metadata)
+
+ def __getitem__(self, item):
+ """distributions are available using release["sdist"]"""
+ return self.dists[item]
+
+ def _check_is_comparable(self, other):
+ if not isinstance(other, ReleaseInfo):
+ raise TypeError("cannot compare %s and %s"
+ % (type(self).__name__, type(other).__name__))
+ elif self.name != other.name:
+ raise TypeError("cannot compare %s and %s"
+ % (self.name, other.name))
+
+ def __repr__(self):
+ return "<%s %s>" % (self.name, self.version)
+
+ def __eq__(self, other):
+ self._check_is_comparable(other)
+ return self.version == other.version
+
+ def __lt__(self, other):
+ self._check_is_comparable(other)
+ return self.version < other.version
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __gt__(self, other):
+ return not (self.__lt__(other) or self.__eq__(other))
+
+ def __le__(self, other):
+ return self.__eq__(other) or self.__lt__(other)
+
+ def __ge__(self, other):
+ return self.__eq__(other) or self.__gt__(other)
+
+ # See http://docs.python.org/reference/datamodel#object.__hash__
+ __hash__ = object.__hash__
+
+
+class DistInfo(IndexReference):
+ """Represents a distribution retrieved from an index (sdist, bdist, ...)
+ """
+
+ def __init__(self, release, dist_type=None, url=None, hashname=None,
+ hashval=None, is_external=True, python_version=None,
+ index=None):
+ """Create a new instance of DistInfo.
+
+ :param release: a DistInfo class is relative to a release.
+ :param dist_type: the type of the dist (eg. source, bin-*, etc.)
+ :param url: URL where we found this distribution
+ :param hashname: the name of the hash we want to use. Refer to the
+ hashlib.new documentation for more information.
+ :param hashval: the hash value.
+ :param is_external: we need to know if the provided url comes from
+ an index browsing, or from an external resource.
+
+ """
+ self.set_index(index)
+ self.release = release
+ self.dist_type = dist_type
+ self.python_version = python_version
+ self._unpacked_dir = None
+ # set the downloaded path to None by default. The goal here
+ # is to not download distributions multiple times
+ self.downloaded_location = None
+ # We store urls in dict, because we need to have a bit more infos
+ # than the simple URL. It will be used later to find the good url to
+ # use.
+ # We have two _url* attributes: _url and urls. urls contains a list
+ # of dict for the different urls, and _url contains the choosen url, in
+ # order to dont make the selection process multiple times.
+ self.urls = []
+ self._url = None
+ self.add_url(url, hashname, hashval, is_external)
+
+ def add_url(self, url=None, hashname=None, hashval=None, is_external=True):
+ """Add a new url to the list of urls"""
+ if hashname is not None:
+ try:
+ hashlib.new(hashname)
+ except ValueError:
+ raise UnsupportedHashName(hashname)
+ if url not in [u['url'] for u in self.urls]:
+ self.urls.append({
+ 'url': url,
+ 'hashname': hashname,
+ 'hashval': hashval,
+ 'is_external': is_external,
+ })
+ # reset the url selection process
+ self._url = None
+
+ @property
+ def url(self):
+ """Pick up the right url for the list of urls in self.urls"""
+ # We return internal urls over externals.
+ # If there is more than one internal or external, return the first
+ # one.
+ if self._url is None:
+ if len(self.urls) > 1:
+ internals_urls = [u for u in self.urls \
+ if u['is_external'] == False]
+ if len(internals_urls) >= 1:
+ self._url = internals_urls[0]
+ if self._url is None:
+ self._url = self.urls[0]
+ return self._url
+
+ @property
+ def is_source(self):
+ """return if the distribution is a source one or not"""
+ return self.dist_type == 'sdist'
+
+ def download(self, path=None):
+ """Download the distribution to a path, and return it.
+
+ If the path is given in path, use this, otherwise, generates a new one
+ Return the download location.
+ """
+ if path is None:
+ path = tempfile.mkdtemp()
+
+ # if we do not have downloaded it yet, do it.
+ if self.downloaded_location is None:
+ url = self.url['url']
+ archive_name = urllib.parse.urlparse(url)[2].split('/')[-1]
+ filename, headers = urllib.request.urlretrieve(url,
+ path + "/" + archive_name)
+ self.downloaded_location = filename
+ self._check_md5(filename)
+ return self.downloaded_location
+
+ def unpack(self, path=None):
+ """Unpack the distribution to the given path.
+
+ If not destination is given, creates a temporary location.
+
+ Returns the location of the extracted files (root).
+ """
+ if not self._unpacked_dir:
+ if path is None:
+ path = tempfile.mkdtemp()
+
+ filename = self.download(path)
+ unpack_archive(filename, path)
+ self._unpacked_dir = path
+
+ return path
+
+ def _check_md5(self, filename):
+ """Check that the md5 checksum of the given file matches the one in
+ url param"""
+ hashname = self.url['hashname']
+ expected_hashval = self.url['hashval']
+ if None not in (expected_hashval, hashname):
+ with open(filename, 'rb') as f:
+ hashval = hashlib.new(hashname)
+ hashval.update(f.read())
+
+ if hashval.hexdigest() != expected_hashval:
+ raise HashDoesNotMatch("got %s instead of %s"
+ % (hashval.hexdigest(), expected_hashval))
+
+ def __repr__(self):
+ if self.release is None:
+ return "<? ? %s>" % self.dist_type
+
+ return "<%s %s %s>" % (
+ self.release.name, self.release.version, self.dist_type or "")
+
+
+class ReleasesList(IndexReference):
+ """A container of Release.
+
+ Provides useful methods and facilities to sort and filter releases.
+ """
+ def __init__(self, name, releases=None, contains_hidden=False, index=None):
+ self.set_index(index)
+ self.releases = []
+ self.name = name
+ self.contains_hidden = contains_hidden
+ if releases:
+ self.add_releases(releases)
+
+ def fetch_releases(self):
+ self._index.get_releases(self.name)
+ return self.releases
+
+ def filter(self, predicate):
+ """Filter and return a subset of releases matching the given predicate.
+ """
+ return ReleasesList(self.name, [release for release in self.releases
+ if predicate.match(release.version)],
+ index=self._index)
+
+ def get_last(self, requirements, prefer_final=None):
+ """Return the "last" release, that satisfy the given predicates.
+
+ "last" is defined by the version number of the releases, you also could
+ set prefer_final parameter to True or False to change the order results
+ """
+ predicate = get_version_predicate(requirements)
+ releases = self.filter(predicate)
+ if len(releases) == 0:
+ return None
+ releases.sort_releases(prefer_final, reverse=True)
+ return releases[0]
+
+ def add_releases(self, releases):
+ """Add releases in the release list.
+
+ :param: releases is a list of ReleaseInfo objects.
+ """
+ for r in releases:
+ self.add_release(release=r)
+
+ def add_release(self, version=None, dist_type='sdist', release=None,
+ **dist_args):
+ """Add a release to the list.
+
+ The release can be passed in the `release` parameter, and in this case,
+ it will be crawled to extract the useful informations if necessary, or
+ the release informations can be directly passed in the `version` and
+ `dist_type` arguments.
+
+ Other keywords arguments can be provided, and will be forwarded to the
+ distribution creation (eg. the arguments of the DistInfo constructor).
+ """
+ if release:
+ if release.name.lower() != self.name.lower():
+ raise ValueError("%s is not the same project as %s" %
+ (release.name, self.name))
+ version = str(release.version)
+
+ if version not in self.get_versions():
+ # append only if not already exists
+ self.releases.append(release)
+ for dist in release.dists.values():
+ for url in dist.urls:
+ self.add_release(version, dist.dist_type, **url)
+ else:
+ matches = [r for r in self.releases
+ if str(r.version) == version and r.name == self.name]
+ if not matches:
+ release = ReleaseInfo(self.name, version, index=self._index)
+ self.releases.append(release)
+ else:
+ release = matches[0]
+
+ release.add_distribution(dist_type=dist_type, **dist_args)
+
+ def sort_releases(self, prefer_final=False, reverse=True, *args, **kwargs):
+ """Sort the results with the given properties.
+
+ The `prefer_final` argument can be used to specify if final
+ distributions (eg. not dev, beta or alpha) would be preferred or not.
+
+ Results can be inverted by using `reverse`.
+
+ Any other parameter provided will be forwarded to the sorted call. You
+ cannot redefine the key argument of "sorted" here, as it is used
+ internally to sort the releases.
+ """
+
+ sort_by = []
+ if prefer_final:
+ sort_by.append("is_final")
+ sort_by.append("version")
+
+ self.releases.sort(
+ key=lambda i: tuple(getattr(i, arg) for arg in sort_by),
+ reverse=reverse, *args, **kwargs)
+
+ def get_release(self, version):
+ """Return a release from its version."""
+ matches = [r for r in self.releases if str(r.version) == version]
+ if len(matches) != 1:
+ raise KeyError(version)
+ return matches[0]
+
+ def get_versions(self):
+ """Return a list of releases versions contained"""
+ return [str(r.version) for r in self.releases]
+
+ def __getitem__(self, key):
+ return self.releases[key]
+
+ def __len__(self):
+ return len(self.releases)
+
+ def __repr__(self):
+ string = 'Project "%s"' % self.name
+ if self.get_versions():
+ string += ' versions: %s' % ', '.join(self.get_versions())
+ return '<%s>' % string
+
+
+def get_infos_from_url(url, probable_dist_name=None, is_external=True):
+ """Get useful informations from an URL.
+
+ Return a dict of (name, version, url, hashtype, hash, is_external)
+
+ :param url: complete url of the distribution
+ :param probable_dist_name: A probable name of the project.
+ :param is_external: Tell if the url commes from an index or from
+ an external URL.
+ """
+ # if the url contains a md5 hash, get it.
+ md5_hash = None
+ match = MD5_HASH.match(url)
+ if match is not None:
+ md5_hash = match.group(1)
+ # remove the hash
+ url = url.replace("#md5=%s" % md5_hash, "")
+
+ # parse the archive name to find dist name and version
+ archive_name = urllib.parse.urlparse(url)[2].split('/')[-1]
+ extension_matched = False
+ # remove the extension from the name
+ for ext in EXTENSIONS:
+ if archive_name.endswith(ext):
+ archive_name = archive_name[:-len(ext)]
+ extension_matched = True
+
+ name, version = split_archive_name(archive_name)
+ if extension_matched is True:
+ return {'name': name,
+ 'version': version,
+ 'url': url,
+ 'hashname': "md5",
+ 'hashval': md5_hash,
+ 'is_external': is_external,
+ 'dist_type': 'sdist'}
+
+
+def split_archive_name(archive_name, probable_name=None):
+ """Split an archive name into two parts: name and version.
+
+ Return the tuple (name, version)
+ """
+ # Try to determine wich part is the name and wich is the version using the
+ # "-" separator. Take the larger part to be the version number then reduce
+ # if this not works.
+ def eager_split(str, maxsplit=2):
+ # split using the "-" separator
+ splits = str.rsplit("-", maxsplit)
+ name = splits[0]
+ version = "-".join(splits[1:])
+ if version.startswith("-"):
+ version = version[1:]
+ if suggest_normalized_version(version) is None and maxsplit >= 0:
+ # we dont get a good version number: recurse !
+ return eager_split(str, maxsplit - 1)
+ else:
+ return name, version
+ if probable_name is not None:
+ probable_name = probable_name.lower()
+ name = None
+ if probable_name is not None and probable_name in archive_name:
+ # we get the name from probable_name, if given.
+ name = probable_name
+ version = archive_name.lstrip(name)
+ else:
+ name, version = eager_split(archive_name)
+
+ version = suggest_normalized_version(version)
+ if version is not None and name != "":
+ return name.lower(), version
+ else:
+ raise CantParseArchiveName(archive_name)
diff --git a/Lib/packaging/pypi/errors.py b/Lib/packaging/pypi/errors.py
new file mode 100644
index 0000000..2191ac1
--- /dev/null
+++ b/Lib/packaging/pypi/errors.py
@@ -0,0 +1,39 @@
+"""Exceptions raised by packaging.pypi code."""
+
+from packaging.errors import PackagingPyPIError
+
+
+class ProjectNotFound(PackagingPyPIError):
+ """Project has not been found"""
+
+
+class DistributionNotFound(PackagingPyPIError):
+ """The release has not been found"""
+
+
+class ReleaseNotFound(PackagingPyPIError):
+ """The release has not been found"""
+
+
+class CantParseArchiveName(PackagingPyPIError):
+ """An archive name can't be parsed to find distribution name and version"""
+
+
+class DownloadError(PackagingPyPIError):
+ """An error has occurs while downloading"""
+
+
+class HashDoesNotMatch(DownloadError):
+ """Compared hashes does not match"""
+
+
+class UnsupportedHashName(PackagingPyPIError):
+ """A unsupported hashname has been used"""
+
+
+class UnableToDownload(PackagingPyPIError):
+ """All mirrors have been tried, without success"""
+
+
+class InvalidSearchField(PackagingPyPIError):
+ """An invalid search field has been used"""
diff --git a/Lib/packaging/pypi/mirrors.py b/Lib/packaging/pypi/mirrors.py
new file mode 100644
index 0000000..a646acff
--- /dev/null
+++ b/Lib/packaging/pypi/mirrors.py
@@ -0,0 +1,52 @@
+"""Utilities related to the mirror infrastructure defined in PEP 381."""
+
+from string import ascii_lowercase
+import socket
+
+DEFAULT_MIRROR_URL = "last.pypi.python.org"
+
+
+def get_mirrors(hostname=None):
+ """Return the list of mirrors from the last record found on the DNS
+ entry::
+
+ >>> from packaging.pypi.mirrors import get_mirrors
+ >>> get_mirrors()
+ ['a.pypi.python.org', 'b.pypi.python.org', 'c.pypi.python.org',
+ 'd.pypi.python.org']
+
+ """
+ if hostname is None:
+ hostname = DEFAULT_MIRROR_URL
+
+ # return the last mirror registered on PyPI.
+ try:
+ hostname = socket.gethostbyname_ex(hostname)[0]
+ except socket.gaierror:
+ return []
+ end_letter = hostname.split(".", 1)
+
+ # determine the list from the last one.
+ return ["%s.%s" % (s, end_letter[1]) for s in string_range(end_letter[0])]
+
+
+def string_range(last):
+ """Compute the range of string between "a" and last.
+
+ This works for simple "a to z" lists, but also for "a to zz" lists.
+ """
+ for k in range(len(last)):
+ for x in product(ascii_lowercase, repeat=(k + 1)):
+ result = ''.join(x)
+ yield result
+ if result == last:
+ return
+
+
+def product(*args, **kwds):
+ pools = [tuple(arg) for arg in args] * kwds.get('repeat', 1)
+ result = [[]]
+ for pool in pools:
+ result = [x + [y] for x in result for y in pool]
+ for prod in result:
+ yield tuple(prod)
diff --git a/Lib/packaging/pypi/simple.py b/Lib/packaging/pypi/simple.py
new file mode 100644
index 0000000..44d98e1
--- /dev/null
+++ b/Lib/packaging/pypi/simple.py
@@ -0,0 +1,462 @@
+"""Spider using the screen-scraping "simple" PyPI API.
+
+This module contains the class Crawler, a simple spider that
+can be used to find and retrieve distributions from a project index
+(like the Python Package Index), using its so-called simple API (see
+reference implementation available at http://pypi.python.org/simple/).
+"""
+
+import http.client
+import re
+import socket
+import sys
+import urllib.request
+import urllib.parse
+import urllib.error
+import os
+
+from fnmatch import translate
+from functools import wraps
+from packaging import logger
+from packaging.metadata import Metadata
+from packaging.version import get_version_predicate
+from packaging import __version__ as packaging_version
+from packaging.pypi.base import BaseClient
+from packaging.pypi.dist import (ReleasesList, EXTENSIONS,
+ get_infos_from_url, MD5_HASH)
+from packaging.pypi.errors import (PackagingPyPIError, DownloadError,
+ UnableToDownload, CantParseArchiveName,
+ ReleaseNotFound, ProjectNotFound)
+from packaging.pypi.mirrors import get_mirrors
+
+__all__ = ['Crawler', 'DEFAULT_SIMPLE_INDEX_URL']
+
+# -- Constants -----------------------------------------------
+DEFAULT_SIMPLE_INDEX_URL = "http://a.pypi.python.org/simple/"
+DEFAULT_HOSTS = ("*",)
+SOCKET_TIMEOUT = 15
+USER_AGENT = "Python-urllib/%s packaging/%s" % (
+ sys.version[:3], packaging_version)
+
+# -- Regexps -------------------------------------------------
+EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.]+)$')
+HREF = re.compile("""href\\s*=\\s*['"]?([^'"> ]+)""", re.I)
+URL_SCHEME = re.compile('([-+.a-z0-9]{2,}):', re.I).match
+
+# This pattern matches a character entity reference (a decimal numeric
+# references, a hexadecimal numeric reference, or a named reference).
+ENTITY_SUB = re.compile(r'&(#(\d+|x[\da-fA-F]+)|[\w.:-]+);?').sub
+REL = re.compile("""<([^>]*\srel\s*=\s*['"]?([^'">]+)[^>]*)>""", re.I)
+
+
+def socket_timeout(timeout=SOCKET_TIMEOUT):
+ """Decorator to add a socket timeout when requesting pages on PyPI.
+ """
+ def wrapper(func):
+ @wraps(func)
+ def wrapped(self, *args, **kwargs):
+ old_timeout = socket.getdefaulttimeout()
+ if hasattr(self, "_timeout"):
+ timeout = self._timeout
+ socket.setdefaulttimeout(timeout)
+ try:
+ return func(self, *args, **kwargs)
+ finally:
+ socket.setdefaulttimeout(old_timeout)
+ return wrapped
+ return wrapper
+
+
+def with_mirror_support():
+ """Decorator that makes the mirroring support easier"""
+ def wrapper(func):
+ @wraps(func)
+ def wrapped(self, *args, **kwargs):
+ try:
+ return func(self, *args, **kwargs)
+ except DownloadError:
+ # if an error occurs, try with the next index_url
+ if self._mirrors_tries >= self._mirrors_max_tries:
+ try:
+ self._switch_to_next_mirror()
+ except KeyError:
+ raise UnableToDownload("Tried all mirrors")
+ else:
+ self._mirrors_tries += 1
+ self._projects.clear()
+ return wrapped(self, *args, **kwargs)
+ return wrapped
+ return wrapper
+
+
+class Crawler(BaseClient):
+ """Provides useful tools to request the Python Package Index simple API.
+
+ You can specify both mirrors and mirrors_url, but mirrors_url will only be
+ used if mirrors is set to None.
+
+ :param index_url: the url of the simple index to search on.
+ :param prefer_final: if the version is not mentioned, and the last
+ version is not a "final" one (alpha, beta, etc.),
+ pick up the last final version.
+ :param prefer_source: if the distribution type is not mentioned, pick up
+ the source one if available.
+ :param follow_externals: tell if following external links is needed or
+ not. Default is False.
+ :param hosts: a list of hosts allowed to be processed while using
+ follow_externals=True. Default behavior is to follow all
+ hosts.
+ :param follow_externals: tell if following external links is needed or
+ not. Default is False.
+ :param mirrors_url: the url to look on for DNS records giving mirror
+ addresses.
+ :param mirrors: a list of mirrors (see PEP 381).
+ :param timeout: time in seconds to consider a url has timeouted.
+ :param mirrors_max_tries": number of times to try requesting informations
+ on mirrors before switching.
+ """
+
+ def __init__(self, index_url=DEFAULT_SIMPLE_INDEX_URL, prefer_final=False,
+ prefer_source=True, hosts=DEFAULT_HOSTS,
+ follow_externals=False, mirrors_url=None, mirrors=None,
+ timeout=SOCKET_TIMEOUT, mirrors_max_tries=0):
+ super(Crawler, self).__init__(prefer_final, prefer_source)
+ self.follow_externals = follow_externals
+
+ # mirroring attributes.
+ parsed = urllib.parse.urlparse(index_url)
+ self.scheme = parsed[0]
+ if self.scheme == 'file':
+ ender = os.path.sep
+ else:
+ ender = '/'
+ if not index_url.endswith(ender):
+ index_url += ender
+ # if no mirrors are defined, use the method described in PEP 381.
+ if mirrors is None:
+ mirrors = get_mirrors(mirrors_url)
+ self._mirrors = set(mirrors)
+ self._mirrors_used = set()
+ self.index_url = index_url
+ self._mirrors_max_tries = mirrors_max_tries
+ self._mirrors_tries = 0
+ self._timeout = timeout
+
+ # create a regexp to match all given hosts
+ self._allowed_hosts = re.compile('|'.join(map(translate, hosts))).match
+
+ # we keep an index of pages we have processed, in order to avoid
+ # scanning them multple time (eg. if there is multiple pages pointing
+ # on one)
+ self._processed_urls = []
+ self._projects = {}
+
+ @with_mirror_support()
+ def search_projects(self, name=None, **kwargs):
+ """Search the index for projects containing the given name.
+
+ Return a list of names.
+ """
+ if '*' in name:
+ name.replace('*', '.*')
+ else:
+ name = "%s%s%s" % ('*.?', name, '*.?')
+ name = name.replace('*', '[^<]*') # avoid matching end tag
+ pattern = ('<a[^>]*>(%s)</a>' % name).encode('utf-8')
+ projectname = re.compile(pattern, re.I)
+ matching_projects = []
+
+ with self._open_url(self.index_url) as index:
+ index_content = index.read()
+
+ for match in projectname.finditer(index_content):
+ project_name = match.group(1).decode('utf-8')
+ matching_projects.append(self._get_project(project_name))
+ return matching_projects
+
+ def get_releases(self, requirements, prefer_final=None,
+ force_update=False):
+ """Search for releases and return a ReleasesList object containing
+ the results.
+ """
+ predicate = get_version_predicate(requirements)
+ if predicate.name.lower() in self._projects and not force_update:
+ return self._projects.get(predicate.name.lower())
+ prefer_final = self._get_prefer_final(prefer_final)
+ logger.debug('Reading info on PyPI about %s', predicate.name)
+ self._process_index_page(predicate.name)
+
+ if predicate.name.lower() not in self._projects:
+ raise ProjectNotFound
+
+ releases = self._projects.get(predicate.name.lower())
+ releases.sort_releases(prefer_final=prefer_final)
+ return releases
+
+ def get_release(self, requirements, prefer_final=None):
+ """Return only one release that fulfill the given requirements"""
+ predicate = get_version_predicate(requirements)
+ release = self.get_releases(predicate, prefer_final)\
+ .get_last(predicate)
+ if not release:
+ raise ReleaseNotFound("No release matches the given criterias")
+ return release
+
+ def get_distributions(self, project_name, version):
+ """Return the distributions found on the index for the specific given
+ release"""
+ # as the default behavior of get_release is to return a release
+ # containing the distributions, just alias it.
+ return self.get_release("%s (%s)" % (project_name, version))
+
+ def get_metadata(self, project_name, version):
+ """Return the metadatas from the simple index.
+
+ Currently, download one archive, extract it and use the PKG-INFO file.
+ """
+ release = self.get_distributions(project_name, version)
+ if not release.metadata:
+ location = release.get_distribution().unpack()
+ pkg_info = os.path.join(location, 'PKG-INFO')
+ release.metadata = Metadata(pkg_info)
+ return release
+
+ def _switch_to_next_mirror(self):
+ """Switch to the next mirror (eg. point self.index_url to the next
+ mirror url.
+
+ Raise a KeyError if all mirrors have been tried.
+ """
+ self._mirrors_used.add(self.index_url)
+ index_url = self._mirrors.pop()
+ # XXX use urllib.parse for a real check of missing scheme part
+ if not index_url.startswith(("http://", "https://", "file://")):
+ index_url = "http://%s" % index_url
+
+ if not index_url.endswith("/simple"):
+ index_url = "%s/simple/" % index_url
+
+ self.index_url = index_url
+
+ def _is_browsable(self, url):
+ """Tell if the given URL can be browsed or not.
+
+ It uses the follow_externals and the hosts list to tell if the given
+ url is browsable or not.
+ """
+ # if _index_url is contained in the given URL, we are browsing the
+ # index, and it's always "browsable".
+ # local files are always considered browable resources
+ if self.index_url in url or urllib.parse.urlparse(url)[0] == "file":
+ return True
+ elif self.follow_externals:
+ if self._allowed_hosts(urllib.parse.urlparse(url)[1]): # 1 is netloc
+ return True
+ else:
+ return False
+ return False
+
+ def _is_distribution(self, link):
+ """Tell if the given URL matches to a distribution name or not.
+ """
+ #XXX find a better way to check that links are distributions
+ # Using a regexp ?
+ for ext in EXTENSIONS:
+ if ext in link:
+ return True
+ return False
+
+ def _register_release(self, release=None, release_info={}):
+ """Register a new release.
+
+ Both a release or a dict of release_info can be provided, the preferred
+ way (eg. the quicker) is the dict one.
+
+ Return the list of existing releases for the given project.
+ """
+ # Check if the project already has a list of releases (refering to
+ # the project name). If not, create a new release list.
+ # Then, add the release to the list.
+ if release:
+ name = release.name
+ else:
+ name = release_info['name']
+ if name.lower() not in self._projects:
+ self._projects[name.lower()] = ReleasesList(name, index=self._index)
+
+ if release:
+ self._projects[name.lower()].add_release(release=release)
+ else:
+ name = release_info.pop('name')
+ version = release_info.pop('version')
+ dist_type = release_info.pop('dist_type')
+ self._projects[name.lower()].add_release(version, dist_type,
+ **release_info)
+ return self._projects[name.lower()]
+
+ def _process_url(self, url, project_name=None, follow_links=True):
+ """Process an url and search for distributions packages.
+
+ For each URL found, if it's a download, creates a PyPIdistribution
+ object. If it's a homepage and we can follow links, process it too.
+
+ :param url: the url to process
+ :param project_name: the project name we are searching for.
+ :param follow_links: Do not want to follow links more than from one
+ level. This parameter tells if we want to follow
+ the links we find (eg. run recursively this
+ method on it)
+ """
+ with self._open_url(url) as f:
+ base_url = f.url
+ if url not in self._processed_urls:
+ self._processed_urls.append(url)
+ link_matcher = self._get_link_matcher(url)
+ for link, is_download in link_matcher(f.read().decode(), base_url):
+ if link not in self._processed_urls:
+ if self._is_distribution(link) or is_download:
+ self._processed_urls.append(link)
+ # it's a distribution, so create a dist object
+ try:
+ infos = get_infos_from_url(link, project_name,
+ is_external=self.index_url not in url)
+ except CantParseArchiveName as e:
+ logger.warning(
+ "version has not been parsed: %s", e)
+ else:
+ self._register_release(release_info=infos)
+ else:
+ if self._is_browsable(link) and follow_links:
+ self._process_url(link, project_name,
+ follow_links=False)
+
+ def _get_link_matcher(self, url):
+ """Returns the right link matcher function of the given url
+ """
+ if self.index_url in url:
+ return self._simple_link_matcher
+ else:
+ return self._default_link_matcher
+
+ def _get_full_url(self, url, base_url):
+ return urllib.parse.urljoin(base_url, self._htmldecode(url))
+
+ def _simple_link_matcher(self, content, base_url):
+ """Yield all links with a rel="download" or rel="homepage".
+
+ This matches the simple index requirements for matching links.
+ If follow_externals is set to False, dont yeld the external
+ urls.
+
+ :param content: the content of the page we want to parse
+ :param base_url: the url of this page.
+ """
+ for match in HREF.finditer(content):
+ url = self._get_full_url(match.group(1), base_url)
+ if MD5_HASH.match(url):
+ yield (url, True)
+
+ for match in REL.finditer(content):
+ # search for rel links.
+ tag, rel = match.groups()
+ rels = [s.strip() for s in rel.lower().split(',')]
+ if 'homepage' in rels or 'download' in rels:
+ for match in HREF.finditer(tag):
+ url = self._get_full_url(match.group(1), base_url)
+ if 'download' in rels or self._is_browsable(url):
+ # yield a list of (url, is_download)
+ yield (url, 'download' in rels)
+
+ def _default_link_matcher(self, content, base_url):
+ """Yield all links found on the page.
+ """
+ for match in HREF.finditer(content):
+ url = self._get_full_url(match.group(1), base_url)
+ if self._is_browsable(url):
+ yield (url, False)
+
+ @with_mirror_support()
+ def _process_index_page(self, name):
+ """Find and process a PyPI page for the given project name.
+
+ :param name: the name of the project to find the page
+ """
+ # Browse and index the content of the given PyPI page.
+ if self.scheme == 'file':
+ ender = os.path.sep
+ else:
+ ender = '/'
+ url = self.index_url + name + ender
+ self._process_url(url, name)
+
+ @socket_timeout()
+ def _open_url(self, url):
+ """Open a urllib2 request, handling HTTP authentication, and local
+ files support.
+
+ """
+ scheme, netloc, path, params, query, frag = urllib.parse.urlparse(url)
+
+ # authentication stuff
+ if scheme in ('http', 'https'):
+ auth, host = urllib.parse.splituser(netloc)
+ else:
+ auth = None
+
+ # add index.html automatically for filesystem paths
+ if scheme == 'file':
+ if url.endswith(os.path.sep):
+ url += "index.html"
+
+ # add authorization headers if auth is provided
+ if auth:
+ auth = "Basic " + \
+ urllib.parse.unquote(auth).encode('base64').strip()
+ new_url = urllib.parse.urlunparse((
+ scheme, host, path, params, query, frag))
+ request = urllib.request.Request(new_url)
+ request.add_header("Authorization", auth)
+ else:
+ request = urllib.request.Request(url)
+ request.add_header('User-Agent', USER_AGENT)
+ try:
+ fp = urllib.request.urlopen(request)
+ except (ValueError, http.client.InvalidURL) as v:
+ msg = ' '.join([str(arg) for arg in v.args])
+ raise PackagingPyPIError('%s %s' % (url, msg))
+ except urllib.error.HTTPError as v:
+ return v
+ except urllib.error.URLError as v:
+ raise DownloadError("Download error for %s: %s" % (url, v.reason))
+ except http.client.BadStatusLine as v:
+ raise DownloadError('%s returned a bad status line. '
+ 'The server might be down, %s' % (url, v.line))
+ except http.client.HTTPException as v:
+ raise DownloadError("Download error for %s: %s" % (url, v))
+ except socket.timeout:
+ raise DownloadError("The server timeouted")
+
+ if auth:
+ # Put authentication info back into request URL if same host,
+ # so that links found on the page will work
+ s2, h2, path2, param2, query2, frag2 = \
+ urllib.parse.urlparse(fp.url)
+ if s2 == scheme and h2 == host:
+ fp.url = urllib.parse.urlunparse(
+ (s2, netloc, path2, param2, query2, frag2))
+ return fp
+
+ def _decode_entity(self, match):
+ what = match.group(1)
+ if what.startswith('#x'):
+ what = int(what[2:], 16)
+ elif what.startswith('#'):
+ what = int(what[1:])
+ else:
+ from html.entities import name2codepoint
+ what = name2codepoint.get(what, match.group(0))
+ return chr(what)
+
+ def _htmldecode(self, text):
+ """Decode HTML entities in the given text."""
+ return ENTITY_SUB(self._decode_entity, text)
diff --git a/Lib/packaging/pypi/wrapper.py b/Lib/packaging/pypi/wrapper.py
new file mode 100644
index 0000000..945d08a
--- /dev/null
+++ b/Lib/packaging/pypi/wrapper.py
@@ -0,0 +1,99 @@
+"""Convenient client for all PyPI APIs.
+
+This module provides a ClientWrapper class which will use the "simple"
+or XML-RPC API to request information or files from an index.
+"""
+
+from packaging.pypi import simple, xmlrpc
+
+_WRAPPER_MAPPINGS = {'get_release': 'simple',
+ 'get_releases': 'simple',
+ 'search_projects': 'simple',
+ 'get_metadata': 'xmlrpc',
+ 'get_distributions': 'simple'}
+
+_WRAPPER_INDEXES = {'xmlrpc': xmlrpc.Client,
+ 'simple': simple.Crawler}
+
+
+def switch_index_if_fails(func, wrapper):
+ """Decorator that switch of index (for instance from xmlrpc to simple)
+ if the first mirror return an empty list or raises an exception.
+ """
+ def decorator(*args, **kwargs):
+ retry = True
+ exception = None
+ methods = [func]
+ for f in wrapper._indexes.values():
+ if f != func.__self__ and hasattr(f, func.__name__):
+ methods.append(getattr(f, func.__name__))
+ for method in methods:
+ try:
+ response = method(*args, **kwargs)
+ retry = False
+ except Exception as e:
+ exception = e
+ if not retry:
+ break
+ if retry and exception:
+ raise exception
+ else:
+ return response
+ return decorator
+
+
+class ClientWrapper:
+ """Wrapper around simple and xmlrpc clients,
+
+ Choose the best implementation to use depending the needs, using the given
+ mappings.
+ If one of the indexes returns an error, tries to use others indexes.
+
+ :param index: tell which index to rely on by default.
+ :param index_classes: a dict of name:class to use as indexes.
+ :param indexes: a dict of name:index already instantiated
+ :param mappings: the mappings to use for this wrapper
+ """
+
+ def __init__(self, default_index='simple', index_classes=_WRAPPER_INDEXES,
+ indexes={}, mappings=_WRAPPER_MAPPINGS):
+ self._projects = {}
+ self._mappings = mappings
+ self._indexes = indexes
+ self._default_index = default_index
+
+ # instantiate the classes and set their _project attribute to the one
+ # of the wrapper.
+ for name, cls in index_classes.items():
+ obj = self._indexes.setdefault(name, cls())
+ obj._projects = self._projects
+ obj._index = self
+
+ def __getattr__(self, method_name):
+ """When asking for methods of the wrapper, return the implementation of
+ the wrapped classes, depending the mapping.
+
+ Decorate the methods to switch of implementation if an error occurs
+ """
+ real_method = None
+ if method_name in _WRAPPER_MAPPINGS:
+ obj = self._indexes[_WRAPPER_MAPPINGS[method_name]]
+ real_method = getattr(obj, method_name)
+ else:
+ # the method is not defined in the mappings, so we try first to get
+ # it via the default index, and rely on others if needed.
+ try:
+ real_method = getattr(self._indexes[self._default_index],
+ method_name)
+ except AttributeError:
+ other_indexes = [i for i in self._indexes
+ if i != self._default_index]
+ for index in other_indexes:
+ real_method = getattr(self._indexes[index], method_name,
+ None)
+ if real_method:
+ break
+ if real_method:
+ return switch_index_if_fails(real_method, self)
+ else:
+ raise AttributeError("No index have attribute '%s'" % method_name)
diff --git a/Lib/packaging/pypi/xmlrpc.py b/Lib/packaging/pypi/xmlrpc.py
new file mode 100644
index 0000000..befdf6d
--- /dev/null
+++ b/Lib/packaging/pypi/xmlrpc.py
@@ -0,0 +1,200 @@
+"""Spider using the XML-RPC PyPI API.
+
+This module contains the class Client, a spider that can be used to find
+and retrieve distributions from a project index (like the Python Package
+Index), using its XML-RPC API (see documentation of the reference
+implementation at http://wiki.python.org/moin/PyPiXmlRpc).
+"""
+
+import xmlrpc.client
+
+from packaging import logger
+from packaging.errors import IrrationalVersionError
+from packaging.version import get_version_predicate
+from packaging.pypi.base import BaseClient
+from packaging.pypi.errors import (ProjectNotFound, InvalidSearchField,
+ ReleaseNotFound)
+from packaging.pypi.dist import ReleaseInfo
+
+__all__ = ['Client', 'DEFAULT_XMLRPC_INDEX_URL']
+
+DEFAULT_XMLRPC_INDEX_URL = 'http://python.org/pypi'
+
+_SEARCH_FIELDS = ['name', 'version', 'author', 'author_email', 'maintainer',
+ 'maintainer_email', 'home_page', 'license', 'summary',
+ 'description', 'keywords', 'platform', 'download_url']
+
+
+class Client(BaseClient):
+ """Client to query indexes using XML-RPC method calls.
+
+ If no server_url is specified, use the default PyPI XML-RPC URL,
+ defined in the DEFAULT_XMLRPC_INDEX_URL constant::
+
+ >>> client = Client()
+ >>> client.server_url == DEFAULT_XMLRPC_INDEX_URL
+ True
+
+ >>> client = Client("http://someurl/")
+ >>> client.server_url
+ 'http://someurl/'
+ """
+
+ def __init__(self, server_url=DEFAULT_XMLRPC_INDEX_URL, prefer_final=False,
+ prefer_source=True):
+ super(Client, self).__init__(prefer_final, prefer_source)
+ self.server_url = server_url
+ self._projects = {}
+
+ def get_release(self, requirements, prefer_final=False):
+ """Return a release with all complete metadata and distribution
+ related informations.
+ """
+ prefer_final = self._get_prefer_final(prefer_final)
+ predicate = get_version_predicate(requirements)
+ releases = self.get_releases(predicate.name)
+ release = releases.get_last(predicate, prefer_final)
+ self.get_metadata(release.name, str(release.version))
+ self.get_distributions(release.name, str(release.version))
+ return release
+
+ def get_releases(self, requirements, prefer_final=None, show_hidden=True,
+ force_update=False):
+ """Return the list of existing releases for a specific project.
+
+ Cache the results from one call to another.
+
+ If show_hidden is True, return the hidden releases too.
+ If force_update is True, reprocess the index to update the
+ informations (eg. make a new XML-RPC call).
+ ::
+
+ >>> client = Client()
+ >>> client.get_releases('Foo')
+ ['1.1', '1.2', '1.3']
+
+ If no such project exists, raise a ProjectNotFound exception::
+
+ >>> client.get_project_versions('UnexistingProject')
+ ProjectNotFound: UnexistingProject
+
+ """
+ def get_versions(project_name, show_hidden):
+ return self.proxy.package_releases(project_name, show_hidden)
+
+ predicate = get_version_predicate(requirements)
+ prefer_final = self._get_prefer_final(prefer_final)
+ project_name = predicate.name
+ if not force_update and (project_name.lower() in self._projects):
+ project = self._projects[project_name.lower()]
+ if not project.contains_hidden and show_hidden:
+ # if hidden releases are requested, and have an existing
+ # list of releases that does not contains hidden ones
+ all_versions = get_versions(project_name, show_hidden)
+ existing_versions = project.get_versions()
+ hidden_versions = set(all_versions) - set(existing_versions)
+ for version in hidden_versions:
+ project.add_release(release=ReleaseInfo(project_name,
+ version, index=self._index))
+ else:
+ versions = get_versions(project_name, show_hidden)
+ if not versions:
+ raise ProjectNotFound(project_name)
+ project = self._get_project(project_name)
+ project.add_releases([ReleaseInfo(project_name, version,
+ index=self._index)
+ for version in versions])
+ project = project.filter(predicate)
+ if len(project) == 0:
+ raise ReleaseNotFound("%s" % predicate)
+ project.sort_releases(prefer_final)
+ return project
+
+
+ def get_distributions(self, project_name, version):
+ """Grab informations about distributions from XML-RPC.
+
+ Return a ReleaseInfo object, with distribution-related informations
+ filled in.
+ """
+ url_infos = self.proxy.release_urls(project_name, version)
+ project = self._get_project(project_name)
+ if version not in project.get_versions():
+ project.add_release(release=ReleaseInfo(project_name, version,
+ index=self._index))
+ release = project.get_release(version)
+ for info in url_infos:
+ packagetype = info['packagetype']
+ dist_infos = {'url': info['url'],
+ 'hashval': info['md5_digest'],
+ 'hashname': 'md5',
+ 'is_external': False,
+ 'python_version': info['python_version']}
+ release.add_distribution(packagetype, **dist_infos)
+ return release
+
+ def get_metadata(self, project_name, version):
+ """Retrieve project metadata.
+
+ Return a ReleaseInfo object, with metadata informations filled in.
+ """
+ # to be case-insensitive, get the informations from the XMLRPC API
+ projects = [d['name'] for d in
+ self.proxy.search({'name': project_name})
+ if d['name'].lower() == project_name]
+ if len(projects) > 0:
+ project_name = projects[0]
+
+ metadata = self.proxy.release_data(project_name, version)
+ project = self._get_project(project_name)
+ if version not in project.get_versions():
+ project.add_release(release=ReleaseInfo(project_name, version,
+ index=self._index))
+ release = project.get_release(version)
+ release.set_metadata(metadata)
+ return release
+
+ def search_projects(self, name=None, operator="or", **kwargs):
+ """Find using the keys provided in kwargs.
+
+ You can set operator to "and" or "or".
+ """
+ for key in kwargs:
+ if key not in _SEARCH_FIELDS:
+ raise InvalidSearchField(key)
+ if name:
+ kwargs["name"] = name
+ projects = self.proxy.search(kwargs, operator)
+ for p in projects:
+ project = self._get_project(p['name'])
+ try:
+ project.add_release(release=ReleaseInfo(p['name'],
+ p['version'], metadata={'summary': p['summary']},
+ index=self._index))
+ except IrrationalVersionError as e:
+ logger.warning("Irrational version error found: %s", e)
+ return [self._projects[p['name'].lower()] for p in projects]
+
+ def get_all_projects(self):
+ """Return the list of all projects registered in the package index"""
+ projects = self.proxy.list_packages()
+ for name in projects:
+ self.get_releases(name, show_hidden=True)
+
+ return [self._projects[name.lower()] for name in set(projects)]
+
+ @property
+ def proxy(self):
+ """Property used to return the XMLRPC server proxy.
+
+ If no server proxy is defined yet, creates a new one::
+
+ >>> client = Client()
+ >>> client.proxy()
+ <ServerProxy for python.org/pypi>
+
+ """
+ if not hasattr(self, '_server_proxy'):
+ self._server_proxy = xmlrpc.client.ServerProxy(self.server_url)
+
+ return self._server_proxy
diff --git a/Lib/packaging/run.py b/Lib/packaging/run.py
new file mode 100644
index 0000000..4756f7c
--- /dev/null
+++ b/Lib/packaging/run.py
@@ -0,0 +1,666 @@
+"""Main command line parser. Implements the pysetup script."""
+
+import os
+import re
+import sys
+import getopt
+import logging
+
+from packaging import logger
+from packaging.dist import Distribution
+from packaging.util import _is_archive_file, generate_setup_py
+from packaging.command import get_command_class, STANDARD_COMMANDS
+from packaging.install import install, install_local_project, remove
+from packaging.database import get_distribution, get_distributions
+from packaging.depgraph import generate_graph
+from packaging.fancy_getopt import FancyGetopt
+from packaging.errors import (PackagingArgError, PackagingError,
+ PackagingModuleError, PackagingClassError,
+ CCompilerError)
+
+
+command_re = re.compile(r'^[a-zA-Z]([a-zA-Z0-9_]*)$')
+
+common_usage = """\
+Actions:
+%(actions)s
+
+To get more help on an action, use:
+
+ pysetup action --help
+"""
+
+global_options = [
+ # The fourth entry for verbose means that it can be repeated.
+ ('verbose', 'v', "run verbosely (default)", True),
+ ('quiet', 'q', "run quietly (turns verbosity off)"),
+ ('dry-run', 'n', "don't actually do anything"),
+ ('help', 'h', "show detailed help message"),
+ ('no-user-cfg', None, 'ignore pydistutils.cfg in your home directory'),
+ ('version', None, 'Display the version'),
+]
+
+negative_opt = {'quiet': 'verbose'}
+
+display_options = [
+ ('help-commands', None, "list all available commands"),
+]
+
+display_option_names = [x[0].replace('-', '_') for x in display_options]
+
+
+def _parse_args(args, options, long_options):
+ """Transform sys.argv input into a dict.
+
+ :param args: the args to parse (i.e sys.argv)
+ :param options: the list of options to pass to getopt
+ :param long_options: the list of string with the names of the long options
+ to be passed to getopt.
+
+ The function returns a dict with options/long_options as keys and matching
+ values as values.
+ """
+ optlist, args = getopt.gnu_getopt(args, options, long_options)
+ optdict = {}
+ optdict['args'] = args
+ for k, v in optlist:
+ k = k.lstrip('-')
+ if k not in optdict:
+ optdict[k] = []
+ if v:
+ optdict[k].append(v)
+ else:
+ optdict[k].append(v)
+ return optdict
+
+
+class action_help:
+ """Prints a help message when the standard help flags: -h and --help
+ are used on the commandline.
+ """
+
+ def __init__(self, help_msg):
+ self.help_msg = help_msg
+
+ def __call__(self, f):
+ def wrapper(*args, **kwargs):
+ f_args = args[1]
+ if '--help' in f_args or '-h' in f_args:
+ print(self.help_msg)
+ return
+ return f(*args, **kwargs)
+ return wrapper
+
+
+@action_help("""\
+Usage: pysetup create
+ or: pysetup create --help
+
+Create a new Python project.
+""")
+def _create(distpatcher, args, **kw):
+ from packaging.create import main
+ return main()
+
+
+@action_help("""\
+Usage: pysetup generate-setup
+ or: pysetup generate-setup --help
+
+Generate a setup.py script for backward-compatibility purposes.
+""")
+def _generate(distpatcher, args, **kw):
+ generate_setup_py()
+ logger.info('The setup.py was generated')
+
+
+@action_help("""\
+Usage: pysetup graph dist
+ or: pysetup graph --help
+
+Print dependency graph for the distribution.
+
+positional arguments:
+ dist installed distribution name
+""")
+def _graph(dispatcher, args, **kw):
+ name = args[1]
+ dist = get_distribution(name, use_egg_info=True)
+ if dist is None:
+ logger.warning('Distribution not found.')
+ return 1
+ else:
+ dists = get_distributions(use_egg_info=True)
+ graph = generate_graph(dists)
+ print(graph.repr_node(dist))
+
+
+@action_help("""\
+Usage: pysetup install [dist]
+ or: pysetup install [archive]
+ or: pysetup install [src_dir]
+ or: pysetup install --help
+
+Install a Python distribution from the indexes, source directory, or sdist.
+
+positional arguments:
+ archive path to source distribution (zip, tar.gz)
+ dist distribution name to install from the indexes
+ scr_dir path to source directory
+""")
+def _install(dispatcher, args, **kw):
+ # first check if we are in a source directory
+ if len(args) < 2:
+ # are we inside a project dir?
+ if os.path.isfile('setup.cfg') or os.path.isfile('setup.py'):
+ args.insert(1, os.getcwd())
+ else:
+ logger.warning('No project to install.')
+ return 1
+
+ target = args[1]
+ # installing from a source dir or archive file?
+ if os.path.isdir(target) or _is_archive_file(target):
+ return not install_local_project(target)
+ else:
+ # download from PyPI
+ return not install(target)
+
+
+@action_help("""\
+Usage: pysetup metadata [dist]
+ or: pysetup metadata [dist] [-f field ...]
+ or: pysetup metadata --help
+
+Print metadata for the distribution.
+
+positional arguments:
+ dist installed distribution name
+
+optional arguments:
+ -f metadata field to print; omit to get all fields
+""")
+def _metadata(dispatcher, args, **kw):
+ opts = _parse_args(args[1:], 'f:', [])
+ if opts['args']:
+ name = opts['args'][0]
+ dist = get_distribution(name, use_egg_info=True)
+ if dist is None:
+ logger.warning('%r not installed', name)
+ return 1
+ elif os.path.isfile('setup.cfg'):
+ logger.info('searching local dir for metadata')
+ dist = Distribution() # XXX use config module
+ dist.parse_config_files()
+ else:
+ logger.warning('no argument given and no local setup.cfg found')
+ return 1
+
+ metadata = dist.metadata
+
+ if 'f' in opts:
+ keys = (k for k in opts['f'] if k in metadata)
+ else:
+ keys = metadata.keys()
+
+ for key in keys:
+ if key in metadata:
+ print(metadata._convert_name(key) + ':')
+ value = metadata[key]
+ if isinstance(value, list):
+ for v in value:
+ print(' ', v)
+ else:
+ print(' ', value.replace('\n', '\n '))
+
+
+@action_help("""\
+Usage: pysetup remove dist [-y]
+ or: pysetup remove --help
+
+Uninstall a Python distribution.
+
+positional arguments:
+ dist installed distribution name
+
+optional arguments:
+ -y auto confirm distribution removal
+""")
+def _remove(distpatcher, args, **kw):
+ opts = _parse_args(args[1:], 'y', [])
+ if 'y' in opts:
+ auto_confirm = True
+ else:
+ auto_confirm = False
+
+ retcode = 0
+ for dist in set(opts['args']):
+ try:
+ remove(dist, auto_confirm=auto_confirm)
+ except PackagingError:
+ logger.warning('%r not installed', dist)
+ retcode = 1
+
+ return retcode
+
+
+@action_help("""\
+Usage: pysetup run [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
+ or: pysetup run --help
+ or: pysetup run --list-commands
+ or: pysetup run cmd --help
+""")
+def _run(dispatcher, args, **kw):
+ parser = dispatcher.parser
+ args = args[1:]
+
+ commands = STANDARD_COMMANDS # + extra commands
+
+ if args == ['--list-commands']:
+ print('List of available commands:')
+ cmds = sorted(commands)
+
+ for cmd in cmds:
+ cls = dispatcher.cmdclass.get(cmd) or get_command_class(cmd)
+ desc = getattr(cls, 'description',
+ '(no description available)')
+ print(' %s: %s' % (cmd, desc))
+ return
+
+ while args:
+ args = dispatcher._parse_command_opts(parser, args)
+ if args is None:
+ return
+
+ # create the Distribution class
+ # need to feed setup.cfg here !
+ dist = Distribution()
+
+ # Find and parse the config file(s): they will override options from
+ # the setup script, but be overridden by the command line.
+
+ # XXX still need to be extracted from Distribution
+ dist.parse_config_files()
+
+ for cmd in dispatcher.commands:
+ # FIXME need to catch MetadataMissingError here (from the check command
+ # e.g.)--or catch any exception, print an error message and exit with 1
+ dist.run_command(cmd, dispatcher.command_options[cmd])
+
+ return 0
+
+
+@action_help("""\
+Usage: pysetup list [dist ...]
+ or: pysetup list --help
+
+Print name, version and location for the matching installed distributions.
+
+positional arguments:
+ dist installed distribution name; omit to get all distributions
+""")
+def _list(dispatcher, args, **kw):
+ opts = _parse_args(args[1:], '', [])
+ dists = get_distributions(use_egg_info=True)
+ if opts['args']:
+ results = (d for d in dists if d.name.lower() in opts['args'])
+ listall = False
+ else:
+ results = dists
+ listall = True
+
+ number = 0
+ for dist in results:
+ print('%r %s (from %r)' % (dist.name, dist.version, dist.path))
+ number += 1
+
+ if number == 0:
+ if listall:
+ logger.info('Nothing seems to be installed.')
+ else:
+ logger.warning('No matching distribution found.')
+ return 1
+ else:
+ logger.info('Found %d projects installed.', number)
+
+
+@action_help("""\
+Usage: pysetup search [project] [--simple [url]] [--xmlrpc [url] [--fieldname value ...] --operator or|and]
+ or: pysetup search --help
+
+Search the indexes for the matching projects.
+
+positional arguments:
+ project the project pattern to search for
+
+optional arguments:
+ --xmlrpc [url] whether to use the xmlrpc index or not. If an url is
+ specified, it will be used rather than the default one.
+
+ --simple [url] whether to use the simple index or not. If an url is
+ specified, it will be used rather than the default one.
+
+ --fieldname value Make a search on this field. Can only be used if
+ --xmlrpc has been selected or is the default index.
+
+ --operator or|and Defines what is the operator to use when doing xmlrpc
+ searchs with multiple fieldnames. Can only be used if
+ --xmlrpc has been selected or is the default index.
+""")
+def _search(dispatcher, args, **kw):
+ """The search action.
+
+ It is able to search for a specific index (specified with --index), using
+ the simple or xmlrpc index types (with --type xmlrpc / --type simple)
+ """
+ #opts = _parse_args(args[1:], '', ['simple', 'xmlrpc'])
+ # 1. what kind of index is requested ? (xmlrpc / simple)
+ logger.error('not implemented')
+ return 1
+
+
+actions = [
+ ('run', 'Run one or several commands', _run),
+ ('metadata', 'Display the metadata of a project', _metadata),
+ ('install', 'Install a project', _install),
+ ('remove', 'Remove a project', _remove),
+ ('search', 'Search for a project in the indexes', _search),
+ ('list', 'List installed projects', _list),
+ ('graph', 'Display a graph', _graph),
+ ('create', 'Create a project', _create),
+ ('generate-setup', 'Generate a backward-compatible setup.py', _generate),
+]
+
+
+class Dispatcher:
+ """Reads the command-line options
+ """
+ def __init__(self, args=None):
+ self.verbose = 1
+ self.dry_run = False
+ self.help = False
+ self.cmdclass = {}
+ self.commands = []
+ self.command_options = {}
+
+ for attr in display_option_names:
+ setattr(self, attr, False)
+
+ self.parser = FancyGetopt(global_options + display_options)
+ self.parser.set_negative_aliases(negative_opt)
+ # FIXME this parses everything, including command options (e.g. "run
+ # build -i" errors with "option -i not recognized")
+ args = self.parser.getopt(args=args, object=self)
+
+ # if first arg is "run", we have some commands
+ if len(args) == 0:
+ self.action = None
+ else:
+ self.action = args[0]
+
+ allowed = [action[0] for action in actions] + [None]
+ if self.action not in allowed:
+ msg = 'Unrecognized action "%s"' % self.action
+ raise PackagingArgError(msg)
+
+ self._set_logger()
+ self.args = args
+
+ # for display options we return immediately
+ if self.help or self.action is None:
+ self._show_help(self.parser, display_options_=False)
+
+ def _set_logger(self):
+ # setting up the logging level from the command-line options
+ # -q gets warning, error and critical
+ if self.verbose == 0:
+ level = logging.WARNING
+ # default level or -v gets info too
+ # XXX there's a bug somewhere: the help text says that -v is default
+ # (and verbose is set to 1 above), but when the user explicitly gives
+ # -v on the command line, self.verbose is incremented to 2! Here we
+ # compensate for that (I tested manually). On a related note, I think
+ # it's a good thing to use -q/nothing/-v/-vv on the command line
+ # instead of logging constants; it will be easy to add support for
+ # logging configuration in setup.cfg for advanced users. --merwok
+ elif self.verbose in (1, 2):
+ level = logging.INFO
+ else: # -vv and more for debug
+ level = logging.DEBUG
+
+ # setting up the stream handler
+ handler = logging.StreamHandler(sys.stderr)
+ handler.setLevel(level)
+ logger.addHandler(handler)
+ logger.setLevel(level)
+
+ def _parse_command_opts(self, parser, args):
+ # Pull the current command from the head of the command line
+ command = args[0]
+ if not command_re.match(command):
+ raise SystemExit("invalid command name %r" % (command,))
+ self.commands.append(command)
+
+ # Dig up the command class that implements this command, so we
+ # 1) know that it's a valid command, and 2) know which options
+ # it takes.
+ try:
+ cmd_class = get_command_class(command)
+ except PackagingModuleError as msg:
+ raise PackagingArgError(msg)
+
+ # XXX We want to push this in packaging.command
+ #
+ # Require that the command class be derived from Command -- want
+ # to be sure that the basic "command" interface is implemented.
+ for meth in ('initialize_options', 'finalize_options', 'run'):
+ if hasattr(cmd_class, meth):
+ continue
+ raise PackagingClassError(
+ 'command %r must implement %r' % (cmd_class, meth))
+
+ # Also make sure that the command object provides a list of its
+ # known options.
+ if not (hasattr(cmd_class, 'user_options') and
+ isinstance(cmd_class.user_options, list)):
+ raise PackagingClassError(
+ "command class %s must provide "
+ "'user_options' attribute (a list of tuples)" % cmd_class)
+
+ # If the command class has a list of negative alias options,
+ # merge it in with the global negative aliases.
+ _negative_opt = negative_opt.copy()
+
+ if hasattr(cmd_class, 'negative_opt'):
+ _negative_opt.update(cmd_class.negative_opt)
+
+ # Check for help_options in command class. They have a different
+ # format (tuple of four) so we need to preprocess them here.
+ if (hasattr(cmd_class, 'help_options') and
+ isinstance(cmd_class.help_options, list)):
+ help_options = cmd_class.help_options[:]
+ else:
+ help_options = []
+
+ # All commands support the global options too, just by adding
+ # in 'global_options'.
+ parser.set_option_table(global_options +
+ cmd_class.user_options +
+ help_options)
+ parser.set_negative_aliases(_negative_opt)
+ args, opts = parser.getopt(args[1:])
+
+ if hasattr(opts, 'help') and opts.help:
+ self._show_command_help(cmd_class)
+ return
+
+ if (hasattr(cmd_class, 'help_options') and
+ isinstance(cmd_class.help_options, list)):
+ help_option_found = False
+ for help_option, short, desc, func in cmd_class.help_options:
+ if hasattr(opts, help_option.replace('-', '_')):
+ help_option_found = True
+ if callable(func):
+ func()
+ else:
+ raise PackagingClassError(
+ "invalid help function %r for help option %r: "
+ "must be a callable object (function, etc.)"
+ % (func, help_option))
+
+ if help_option_found:
+ return
+
+ # Put the options from the command line into their official
+ # holding pen, the 'command_options' dictionary.
+ opt_dict = self.get_option_dict(command)
+ for name, value in vars(opts).items():
+ opt_dict[name] = ("command line", value)
+
+ return args
+
+ def get_option_dict(self, command):
+ """Get the option dictionary for a given command. If that
+ command's option dictionary hasn't been created yet, then create it
+ and return the new dictionary; otherwise, return the existing
+ option dictionary.
+ """
+ d = self.command_options.get(command)
+ if d is None:
+ d = self.command_options[command] = {}
+ return d
+
+ def show_help(self):
+ self._show_help(self.parser)
+
+ def print_usage(self, parser):
+ parser.set_option_table(global_options)
+
+ actions_ = [' %s: %s' % (name, desc) for name, desc, __ in actions]
+ usage = common_usage % {'actions': '\n'.join(actions_)}
+
+ parser.print_help(usage + "\nGlobal options:")
+
+ def _show_help(self, parser, global_options_=True, display_options_=True,
+ commands=[]):
+ # late import because of mutual dependence between these modules
+ from packaging.command.cmd import Command
+
+ print('Usage: pysetup [options] action [action_options]')
+ print()
+ if global_options_:
+ self.print_usage(self.parser)
+ print()
+
+ if display_options_:
+ parser.set_option_table(display_options)
+ parser.print_help(
+ "Information display options (just display " +
+ "information, ignore any commands)")
+ print()
+
+ for command in commands:
+ if isinstance(command, type) and issubclass(command, Command):
+ cls = command
+ else:
+ cls = get_command_class(command)
+ if (hasattr(cls, 'help_options') and
+ isinstance(cls.help_options, list)):
+ parser.set_option_table(cls.user_options + cls.help_options)
+ else:
+ parser.set_option_table(cls.user_options)
+
+ parser.print_help("Options for %r command:" % cls.__name__)
+ print()
+
+ def _show_command_help(self, command):
+ if isinstance(command, str):
+ command = get_command_class(command)
+
+ desc = getattr(command, 'description', '(no description available)')
+ print('Description:', desc)
+ print()
+
+ if (hasattr(command, 'help_options') and
+ isinstance(command.help_options, list)):
+ self.parser.set_option_table(command.user_options +
+ command.help_options)
+ else:
+ self.parser.set_option_table(command.user_options)
+
+ self.parser.print_help("Options:")
+ print()
+
+ def _get_command_groups(self):
+ """Helper function to retrieve all the command class names divided
+ into standard commands (listed in
+ packaging.command.STANDARD_COMMANDS) and extra commands (given in
+ self.cmdclass and not standard commands).
+ """
+ extra_commands = [cmd for cmd in self.cmdclass
+ if cmd not in STANDARD_COMMANDS]
+ return STANDARD_COMMANDS, extra_commands
+
+ def print_commands(self):
+ """Print out a help message listing all available commands with a
+ description of each. The list is divided into standard commands
+ (listed in packaging.command.STANDARD_COMMANDS) and extra commands
+ (given in self.cmdclass and not standard commands). The
+ descriptions come from the command class attribute
+ 'description'.
+ """
+ std_commands, extra_commands = self._get_command_groups()
+ max_length = max(len(command)
+ for commands in (std_commands, extra_commands)
+ for command in commands)
+
+ self.print_command_list(std_commands, "Standard commands", max_length)
+ if extra_commands:
+ print()
+ self.print_command_list(extra_commands, "Extra commands",
+ max_length)
+
+ def print_command_list(self, commands, header, max_length):
+ """Print a subset of the list of all commands -- used by
+ 'print_commands()'.
+ """
+ print(header + ":")
+
+ for cmd in commands:
+ cls = self.cmdclass.get(cmd) or get_command_class(cmd)
+ description = getattr(cls, 'description',
+ '(no description available)')
+
+ print(" %-*s %s" % (max_length, cmd, description))
+
+ def __call__(self):
+ if self.action is None:
+ return
+
+ for action, desc, func in actions:
+ if action == self.action:
+ return func(self, self.args)
+ return -1
+
+
+def main(args=None):
+ old_level = logger.level
+ old_handlers = list(logger.handlers)
+ try:
+ dispatcher = Dispatcher(args)
+ if dispatcher.action is None:
+ return
+ return dispatcher()
+ except KeyboardInterrupt:
+ logger.info('interrupted')
+ return 1
+ except (IOError, os.error, PackagingError, CCompilerError) as exc:
+ logger.exception(exc)
+ return 1
+ finally:
+ logger.setLevel(old_level)
+ logger.handlers[:] = old_handlers
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/Lib/packaging/tests/LONG_DESC.txt b/Lib/packaging/tests/LONG_DESC.txt
new file mode 100644
index 0000000..2b4358a
--- /dev/null
+++ b/Lib/packaging/tests/LONG_DESC.txt
@@ -0,0 +1,44 @@
+CLVault
+=======
+
+CLVault uses Keyring to provide a command-line utility to safely store
+and retrieve passwords.
+
+Install it using pip or the setup.py script::
+
+ $ python setup.py install
+
+ $ pip install clvault
+
+Once it's installed, you will have three scripts installed in your
+Python scripts folder, you can use to list, store and retrieve passwords::
+
+ $ clvault-set blog
+ Set your password:
+ Set the associated username (can be blank): tarek
+ Set a description (can be blank): My blog password
+ Password set.
+
+ $ clvault-get blog
+ The username is "tarek"
+ The password has been copied in your clipboard
+
+ $ clvault-list
+ Registered services:
+ blog My blog password
+
+
+*clvault-set* takes a service name then prompt you for a password, and some
+optional information about your service. The password is safely stored in
+a keyring while the description is saved in a ``.clvault`` file in your
+home directory. This file is created automatically the first time the command
+is used.
+
+*clvault-get* copies the password for a given service in your clipboard, and
+displays the associated user if any.
+
+*clvault-list* lists all registered services, with their description when
+given.
+
+
+Project page: http://bitbucket.org/tarek/clvault
diff --git a/Lib/packaging/tests/PKG-INFO b/Lib/packaging/tests/PKG-INFO
new file mode 100644
index 0000000..f48546e
--- /dev/null
+++ b/Lib/packaging/tests/PKG-INFO
@@ -0,0 +1,57 @@
+Metadata-Version: 1.2
+Name: CLVault
+Version: 0.5
+Summary: Command-Line utility to store and retrieve passwords
+Home-page: http://bitbucket.org/tarek/clvault
+Author: Tarek Ziade
+Author-email: tarek@ziade.org
+License: PSF
+Keywords: keyring,password,crypt
+Requires-Dist: foo; sys.platform == 'okook'
+Requires-Dist: bar; sys.platform == '%s'
+Platform: UNKNOWN
+Description: CLVault
+ |=======
+ |
+ |CLVault uses Keyring to provide a command-line utility to safely store
+ |and retrieve passwords.
+ |
+ |Install it using pip or the setup.py script::
+ |
+ | $ python setup.py install
+ |
+ | $ pip install clvault
+ |
+ |Once it's installed, you will have three scripts installed in your
+ |Python scripts folder, you can use to list, store and retrieve passwords::
+ |
+ | $ clvault-set blog
+ | Set your password:
+ | Set the associated username (can be blank): tarek
+ | Set a description (can be blank): My blog password
+ | Password set.
+ |
+ | $ clvault-get blog
+ | The username is "tarek"
+ | The password has been copied in your clipboard
+ |
+ | $ clvault-list
+ | Registered services:
+ | blog My blog password
+ |
+ |
+ |*clvault-set* takes a service name then prompt you for a password, and some
+ |optional information about your service. The password is safely stored in
+ |a keyring while the description is saved in a ``.clvault`` file in your
+ |home directory. This file is created automatically the first time the command
+ |is used.
+ |
+ |*clvault-get* copies the password for a given service in your clipboard, and
+ |displays the associated user if any.
+ |
+ |*clvault-list* lists all registered services, with their description when
+ |given.
+ |
+ |
+ |Project page: http://bitbucket.org/tarek/clvault
+ |
diff --git a/Lib/packaging/tests/SETUPTOOLS-PKG-INFO b/Lib/packaging/tests/SETUPTOOLS-PKG-INFO
new file mode 100644
index 0000000..dff8d00
--- /dev/null
+++ b/Lib/packaging/tests/SETUPTOOLS-PKG-INFO
@@ -0,0 +1,182 @@
+Metadata-Version: 1.0
+Name: setuptools
+Version: 0.6c9
+Summary: Download, build, install, upgrade, and uninstall Python packages -- easily!
+Home-page: http://pypi.python.org/pypi/setuptools
+Author: Phillip J. Eby
+Author-email: distutils-sig@python.org
+License: PSF or ZPL
+Description: ===============================
+ Installing and Using Setuptools
+ ===============================
+
+ .. contents:: **Table of Contents**
+
+
+ -------------------------
+ Installation Instructions
+ -------------------------
+
+ Windows
+ =======
+
+ Install setuptools using the provided ``.exe`` installer. If you've previously
+ installed older versions of setuptools, please delete all ``setuptools*.egg``
+ and ``setuptools.pth`` files from your system's ``site-packages`` directory
+ (and any other ``sys.path`` directories) FIRST.
+
+ If you are upgrading a previous version of setuptools that was installed using
+ an ``.exe`` installer, please be sure to also *uninstall that older version*
+ via your system's "Add/Remove Programs" feature, BEFORE installing the newer
+ version.
+
+ Once installation is complete, you will find an ``easy_install.exe`` program in
+ your Python ``Scripts`` subdirectory. Be sure to add this directory to your
+ ``PATH`` environment variable, if you haven't already done so.
+
+
+ RPM-Based Systems
+ =================
+
+ Install setuptools using the provided source RPM. The included ``.spec`` file
+ assumes you are installing using the default ``python`` executable, and is not
+ specific to a particular Python version. The ``easy_install`` executable will
+ be installed to a system ``bin`` directory such as ``/usr/bin``.
+
+ If you wish to install to a location other than the default Python
+ installation's default ``site-packages`` directory (and ``$prefix/bin`` for
+ scripts), please use the ``.egg``-based installation approach described in the
+ following section.
+
+
+ Cygwin, Mac OS X, Linux, Other
+ ==============================
+
+ 1. Download the appropriate egg for your version of Python (e.g.
+ ``setuptools-0.6c9-py2.4.egg``). Do NOT rename it.
+
+ 2. Run it as if it were a shell script, e.g. ``sh setuptools-0.6c9-py2.4.egg``.
+ Setuptools will install itself using the matching version of Python (e.g.
+ ``python2.4``), and will place the ``easy_install`` executable in the
+ default location for installing Python scripts (as determined by the
+ standard distutils configuration files, or by the Python installation).
+
+ If you want to install setuptools to somewhere other than ``site-packages`` or
+ your default distutils installation locations for libraries and scripts, you
+ may include EasyInstall command-line options such as ``--prefix``,
+ ``--install-dir``, and so on, following the ``.egg`` filename on the same
+ command line. For example::
+
+ sh setuptools-0.6c9-py2.4.egg --prefix=~
+
+ You can use ``--help`` to get a full options list, but we recommend consulting
+ the `EasyInstall manual`_ for detailed instructions, especially `the section
+ on custom installation locations`_.
+
+ .. _EasyInstall manual: http://peak.telecommunity.com/DevCenter/EasyInstall
+ .. _the section on custom installation locations: http://peak.telecommunity.com/DevCenter/EasyInstall#custom-installation-locations
+
+
+ Cygwin Note
+ -----------
+
+ If you are trying to install setuptools for the **Windows** version of Python
+ (as opposed to the Cygwin version that lives in ``/usr/bin``), you must make
+ sure that an appropriate executable (``python2.3``, ``python2.4``, or
+ ``python2.5``) is on your **Cygwin** ``PATH`` when invoking the egg. For
+ example, doing the following at a Cygwin bash prompt will install setuptools
+ for the **Windows** Python found at ``C:\\Python24``::
+
+ ln -s /cygdrive/c/Python24/python.exe python2.4
+ PATH=.:$PATH sh setuptools-0.6c9-py2.4.egg
+ rm python2.4
+
+
+ Downloads
+ =========
+
+ All setuptools downloads can be found at `the project's home page in the Python
+ Package Index`_. Scroll to the very bottom of the page to find the links.
+
+ .. _the project's home page in the Python Package Index: http://pypi.python.org/pypi/setuptools
+
+ In addition to the PyPI downloads, the development version of ``setuptools``
+ is available from the `Python SVN sandbox`_, and in-development versions of the
+ `0.6 branch`_ are available as well.
+
+ .. _0.6 branch: http://svn.python.org/projects/sandbox/branches/setuptools-0.6/#egg=setuptools-dev06
+
+ .. _Python SVN sandbox: http://svn.python.org/projects/sandbox/trunk/setuptools/#egg=setuptools-dev
+
+ --------------------------------
+ Using Setuptools and EasyInstall
+ --------------------------------
+
+ Here are some of the available manuals, tutorials, and other resources for
+ learning about Setuptools, Python Eggs, and EasyInstall:
+
+ * `The EasyInstall user's guide and reference manual`_
+ * `The setuptools Developer's Guide`_
+ * `The pkg_resources API reference`_
+ * `Package Compatibility Notes`_ (user-maintained)
+ * `The Internal Structure of Python Eggs`_
+
+ Questions, comments, and bug reports should be directed to the `distutils-sig
+ mailing list`_. If you have written (or know of) any tutorials, documentation,
+ plug-ins, or other resources for setuptools users, please let us know about
+ them there, so this reference list can be updated. If you have working,
+ *tested* patches to correct problems or add features, you may submit them to
+ the `setuptools bug tracker`_.
+
+ .. _setuptools bug tracker: http://bugs.python.org/setuptools/
+ .. _Package Compatibility Notes: http://peak.telecommunity.com/DevCenter/PackageNotes
+ .. _The Internal Structure of Python Eggs: http://peak.telecommunity.com/DevCenter/EggFormats
+ .. _The setuptools Developer's Guide: http://peak.telecommunity.com/DevCenter/setuptools
+ .. _The pkg_resources API reference: http://peak.telecommunity.com/DevCenter/PkgResources
+ .. _The EasyInstall user's guide and reference manual: http://peak.telecommunity.com/DevCenter/EasyInstall
+ .. _distutils-sig mailing list: http://mail.python.org/pipermail/distutils-sig/
+
+
+ -------
+ Credits
+ -------
+
+ * The original design for the ``.egg`` format and the ``pkg_resources`` API was
+ co-created by Phillip Eby and Bob Ippolito. Bob also implemented the first
+ version of ``pkg_resources``, and supplied the OS X operating system version
+ compatibility algorithm.
+
+ * Ian Bicking implemented many early "creature comfort" features of
+ easy_install, including support for downloading via Sourceforge and
+ Subversion repositories. Ian's comments on the Web-SIG about WSGI
+ application deployment also inspired the concept of "entry points" in eggs,
+ and he has given talks at PyCon and elsewhere to inform and educate the
+ community about eggs and setuptools.
+
+ * Jim Fulton contributed time and effort to build automated tests of various
+ aspects of ``easy_install``, and supplied the doctests for the command-line
+ ``.exe`` wrappers on Windows.
+
+ * Phillip J. Eby is the principal author and maintainer of setuptools, and
+ first proposed the idea of an importable binary distribution format for
+ Python application plug-ins.
+
+ * Significant parts of the implementation of setuptools were funded by the Open
+ Source Applications Foundation, to provide a plug-in infrastructure for the
+ Chandler PIM application. In addition, many OSAF staffers (such as Mike
+ "Code Bear" Taylor) contributed their time and stress as guinea pigs for the
+ use of eggs and setuptools, even before eggs were "cool". (Thanks, guys!)
+
+
+Keywords: CPAN PyPI distutils eggs package management
+Platform: UNKNOWN
+Classifier: Development Status :: 3 - Alpha
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Python Software Foundation License
+Classifier: License :: OSI Approved :: Zope Public License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: System :: Archiving :: Packaging
+Classifier: Topic :: System :: Systems Administration
+Classifier: Topic :: Utilities
diff --git a/Lib/packaging/tests/SETUPTOOLS-PKG-INFO2 b/Lib/packaging/tests/SETUPTOOLS-PKG-INFO2
new file mode 100644
index 0000000..4b3906a
--- /dev/null
+++ b/Lib/packaging/tests/SETUPTOOLS-PKG-INFO2
@@ -0,0 +1,183 @@
+Metadata-Version: 1.1
+Name: setuptools
+Version: 0.6c9
+Summary: Download, build, install, upgrade, and uninstall Python packages -- easily!
+Home-page: http://pypi.python.org/pypi/setuptools
+Author: Phillip J. Eby
+Author-email: distutils-sig@python.org
+License: PSF or ZPL
+Description: ===============================
+ Installing and Using Setuptools
+ ===============================
+
+ .. contents:: **Table of Contents**
+
+
+ -------------------------
+ Installation Instructions
+ -------------------------
+
+ Windows
+ =======
+
+ Install setuptools using the provided ``.exe`` installer. If you've previously
+ installed older versions of setuptools, please delete all ``setuptools*.egg``
+ and ``setuptools.pth`` files from your system's ``site-packages`` directory
+ (and any other ``sys.path`` directories) FIRST.
+
+ If you are upgrading a previous version of setuptools that was installed using
+ an ``.exe`` installer, please be sure to also *uninstall that older version*
+ via your system's "Add/Remove Programs" feature, BEFORE installing the newer
+ version.
+
+ Once installation is complete, you will find an ``easy_install.exe`` program in
+ your Python ``Scripts`` subdirectory. Be sure to add this directory to your
+ ``PATH`` environment variable, if you haven't already done so.
+
+
+ RPM-Based Systems
+ =================
+
+ Install setuptools using the provided source RPM. The included ``.spec`` file
+ assumes you are installing using the default ``python`` executable, and is not
+ specific to a particular Python version. The ``easy_install`` executable will
+ be installed to a system ``bin`` directory such as ``/usr/bin``.
+
+ If you wish to install to a location other than the default Python
+ installation's default ``site-packages`` directory (and ``$prefix/bin`` for
+ scripts), please use the ``.egg``-based installation approach described in the
+ following section.
+
+
+ Cygwin, Mac OS X, Linux, Other
+ ==============================
+
+ 1. Download the appropriate egg for your version of Python (e.g.
+ ``setuptools-0.6c9-py2.4.egg``). Do NOT rename it.
+
+ 2. Run it as if it were a shell script, e.g. ``sh setuptools-0.6c9-py2.4.egg``.
+ Setuptools will install itself using the matching version of Python (e.g.
+ ``python2.4``), and will place the ``easy_install`` executable in the
+ default location for installing Python scripts (as determined by the
+ standard distutils configuration files, or by the Python installation).
+
+ If you want to install setuptools to somewhere other than ``site-packages`` or
+ your default distutils installation locations for libraries and scripts, you
+ may include EasyInstall command-line options such as ``--prefix``,
+ ``--install-dir``, and so on, following the ``.egg`` filename on the same
+ command line. For example::
+
+ sh setuptools-0.6c9-py2.4.egg --prefix=~
+
+ You can use ``--help`` to get a full options list, but we recommend consulting
+ the `EasyInstall manual`_ for detailed instructions, especially `the section
+ on custom installation locations`_.
+
+ .. _EasyInstall manual: http://peak.telecommunity.com/DevCenter/EasyInstall
+ .. _the section on custom installation locations: http://peak.telecommunity.com/DevCenter/EasyInstall#custom-installation-locations
+
+
+ Cygwin Note
+ -----------
+
+ If you are trying to install setuptools for the **Windows** version of Python
+ (as opposed to the Cygwin version that lives in ``/usr/bin``), you must make
+ sure that an appropriate executable (``python2.3``, ``python2.4``, or
+ ``python2.5``) is on your **Cygwin** ``PATH`` when invoking the egg. For
+ example, doing the following at a Cygwin bash prompt will install setuptools
+ for the **Windows** Python found at ``C:\\Python24``::
+
+ ln -s /cygdrive/c/Python24/python.exe python2.4
+ PATH=.:$PATH sh setuptools-0.6c9-py2.4.egg
+ rm python2.4
+
+
+ Downloads
+ =========
+
+ All setuptools downloads can be found at `the project's home page in the Python
+ Package Index`_. Scroll to the very bottom of the page to find the links.
+
+ .. _the project's home page in the Python Package Index: http://pypi.python.org/pypi/setuptools
+
+ In addition to the PyPI downloads, the development version of ``setuptools``
+ is available from the `Python SVN sandbox`_, and in-development versions of the
+ `0.6 branch`_ are available as well.
+
+ .. _0.6 branch: http://svn.python.org/projects/sandbox/branches/setuptools-0.6/#egg=setuptools-dev06
+
+ .. _Python SVN sandbox: http://svn.python.org/projects/sandbox/trunk/setuptools/#egg=setuptools-dev
+
+ --------------------------------
+ Using Setuptools and EasyInstall
+ --------------------------------
+
+ Here are some of the available manuals, tutorials, and other resources for
+ learning about Setuptools, Python Eggs, and EasyInstall:
+
+ * `The EasyInstall user's guide and reference manual`_
+ * `The setuptools Developer's Guide`_
+ * `The pkg_resources API reference`_
+ * `Package Compatibility Notes`_ (user-maintained)
+ * `The Internal Structure of Python Eggs`_
+
+ Questions, comments, and bug reports should be directed to the `distutils-sig
+ mailing list`_. If you have written (or know of) any tutorials, documentation,
+ plug-ins, or other resources for setuptools users, please let us know about
+ them there, so this reference list can be updated. If you have working,
+ *tested* patches to correct problems or add features, you may submit them to
+ the `setuptools bug tracker`_.
+
+ .. _setuptools bug tracker: http://bugs.python.org/setuptools/
+ .. _Package Compatibility Notes: http://peak.telecommunity.com/DevCenter/PackageNotes
+ .. _The Internal Structure of Python Eggs: http://peak.telecommunity.com/DevCenter/EggFormats
+ .. _The setuptools Developer's Guide: http://peak.telecommunity.com/DevCenter/setuptools
+ .. _The pkg_resources API reference: http://peak.telecommunity.com/DevCenter/PkgResources
+ .. _The EasyInstall user's guide and reference manual: http://peak.telecommunity.com/DevCenter/EasyInstall
+ .. _distutils-sig mailing list: http://mail.python.org/pipermail/distutils-sig/
+
+
+ -------
+ Credits
+ -------
+
+ * The original design for the ``.egg`` format and the ``pkg_resources`` API was
+ co-created by Phillip Eby and Bob Ippolito. Bob also implemented the first
+ version of ``pkg_resources``, and supplied the OS X operating system version
+ compatibility algorithm.
+
+ * Ian Bicking implemented many early "creature comfort" features of
+ easy_install, including support for downloading via Sourceforge and
+ Subversion repositories. Ian's comments on the Web-SIG about WSGI
+ application deployment also inspired the concept of "entry points" in eggs,
+ and he has given talks at PyCon and elsewhere to inform and educate the
+ community about eggs and setuptools.
+
+ * Jim Fulton contributed time and effort to build automated tests of various
+ aspects of ``easy_install``, and supplied the doctests for the command-line
+ ``.exe`` wrappers on Windows.
+
+ * Phillip J. Eby is the principal author and maintainer of setuptools, and
+ first proposed the idea of an importable binary distribution format for
+ Python application plug-ins.
+
+ * Significant parts of the implementation of setuptools were funded by the Open
+ Source Applications Foundation, to provide a plug-in infrastructure for the
+ Chandler PIM application. In addition, many OSAF staffers (such as Mike
+ "Code Bear" Taylor) contributed their time and stress as guinea pigs for the
+ use of eggs and setuptools, even before eggs were "cool". (Thanks, guys!)
+
+
+Keywords: CPAN PyPI distutils eggs package management
+Platform: UNKNOWN
+Classifier: Development Status :: 3 - Alpha
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Python Software Foundation License
+Classifier: License :: OSI Approved :: Zope Public License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: System :: Archiving :: Packaging
+Classifier: Topic :: System :: Systems Administration
+Classifier: Topic :: Utilities
+Requires: Foo
diff --git a/Lib/packaging/tests/__init__.py b/Lib/packaging/tests/__init__.py
new file mode 100644
index 0000000..cb82004
--- /dev/null
+++ b/Lib/packaging/tests/__init__.py
@@ -0,0 +1,28 @@
+"""Test suite for packaging.
+
+This test suite consists of a collection of test modules in the
+packaging.tests package. Each test module has a name starting with
+'test' and contains a function test_suite(). The function is expected
+to return an initialized unittest.TestSuite instance.
+
+Utility code is included in packaging.tests.support.
+
+Always import unittest from this module: it will be unittest from the
+standard library for packaging tests and unittest2 for distutils2 tests.
+"""
+
+import os
+import sys
+import unittest
+
+
+def test_suite():
+ suite = unittest.TestSuite()
+ here = os.path.dirname(__file__) or os.curdir
+ for fn in os.listdir(here):
+ if fn.startswith("test") and fn.endswith(".py"):
+ modname = "packaging.tests." + fn[:-3]
+ __import__(modname)
+ module = sys.modules[modname]
+ suite.addTest(module.test_suite())
+ return suite
diff --git a/Lib/packaging/tests/__main__.py b/Lib/packaging/tests/__main__.py
new file mode 100644
index 0000000..00f323e
--- /dev/null
+++ b/Lib/packaging/tests/__main__.py
@@ -0,0 +1,24 @@
+"""Packaging test suite runner."""
+
+# Ripped from importlib tests, thanks Brett!
+
+import os
+import unittest
+from test.support import run_unittest, reap_children, reap_threads
+
+
+@reap_threads
+def test_main():
+ try:
+ start_dir = os.path.dirname(__file__)
+ top_dir = os.path.dirname(os.path.dirname(start_dir))
+ test_loader = unittest.TestLoader()
+ # XXX find out how to use unittest.main, to get command-line options
+ # (failfast, catch, etc.)
+ run_unittest(test_loader.discover(start_dir, top_level_dir=top_dir))
+ finally:
+ reap_children()
+
+
+if __name__ == '__main__':
+ test_main()
diff --git a/Lib/email/test/__init__.py b/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/INSTALLER
index e69de29..e69de29 100644
--- a/Lib/email/test/__init__.py
+++ b/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/INSTALLER
diff --git a/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/METADATA b/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/METADATA
new file mode 100644
index 0000000..65e839a
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/METADATA
@@ -0,0 +1,4 @@
+Metadata-version: 1.2
+Name: babar
+Version: 0.1
+Author: FELD Boris \ No newline at end of file
diff --git a/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/RECORD b/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/RECORD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/RECORD
diff --git a/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/REQUESTED b/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/REQUESTED
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/REQUESTED
diff --git a/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/RESOURCES b/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/RESOURCES
new file mode 100644
index 0000000..5d0da49
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/RESOURCES
@@ -0,0 +1,2 @@
+babar.png,babar.png
+babar.cfg,babar.cfg \ No newline at end of file
diff --git a/Lib/packaging/tests/fake_dists/babar.cfg b/Lib/packaging/tests/fake_dists/babar.cfg
new file mode 100644
index 0000000..ecd6efe
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/babar.cfg
@@ -0,0 +1 @@
+Config \ No newline at end of file
diff --git a/Lib/packaging/tests/fake_dists/babar.png b/Lib/packaging/tests/fake_dists/babar.png
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/babar.png
diff --git a/Lib/packaging/tests/fake_dists/bacon-0.1.egg-info/PKG-INFO b/Lib/packaging/tests/fake_dists/bacon-0.1.egg-info/PKG-INFO
new file mode 100644
index 0000000..a176dfd
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/bacon-0.1.egg-info/PKG-INFO
@@ -0,0 +1,6 @@
+Metadata-Version: 1.2
+Name: bacon
+Version: 0.1
+Provides-Dist: truffles (2.0)
+Provides-Dist: bacon (0.1)
+Obsoletes-Dist: truffles (>=0.9,<=1.5)
diff --git a/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/PKG-INFO b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/PKG-INFO
new file mode 100644
index 0000000..a7e118a
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/PKG-INFO
@@ -0,0 +1,18 @@
+Metadata-Version: 1.0
+Name: banana
+Version: 0.4
+Summary: A yellow fruit
+Home-page: http://en.wikipedia.org/wiki/Banana
+Author: Josip Djolonga
+Author-email: foo@nbar.com
+License: BSD
+Description: A fruit
+Keywords: foo bar
+Platform: UNKNOWN
+Classifier: Development Status :: 4 - Beta
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: Science/Research
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Topic :: Scientific/Engineering :: GIS
diff --git a/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/SOURCES.txt b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/SOURCES.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/SOURCES.txt
diff --git a/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/dependency_links.txt b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/entry_points.txt b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/entry_points.txt
new file mode 100644
index 0000000..5d3e5f6
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/entry_points.txt
@@ -0,0 +1,3 @@
+
+ # -*- Entry points: -*-
+ \ No newline at end of file
diff --git a/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/not-zip-safe b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/not-zip-safe
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/not-zip-safe
@@ -0,0 +1 @@
+
diff --git a/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/requires.txt b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/requires.txt
new file mode 100644
index 0000000..4354305
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/requires.txt
@@ -0,0 +1,6 @@
+# this should be ignored
+
+strawberry >=0.5
+
+[section ignored]
+foo ==0.5
diff --git a/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/top_level.txt b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/top_level.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/top_level.txt
diff --git a/Lib/packaging/tests/fake_dists/cheese-2.0.2.egg-info b/Lib/packaging/tests/fake_dists/cheese-2.0.2.egg-info
new file mode 100644
index 0000000..27cbe30
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/cheese-2.0.2.egg-info
@@ -0,0 +1,5 @@
+Metadata-Version: 1.2
+Name: cheese
+Version: 2.0.2
+Provides-Dist: truffles (1.0.2)
+Obsoletes-Dist: truffles (!=1.2,<=2.0)
diff --git a/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/INSTALLER b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/INSTALLER
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/INSTALLER
diff --git a/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/METADATA b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/METADATA
new file mode 100644
index 0000000..418929e
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/METADATA
@@ -0,0 +1,9 @@
+Metadata-Version: 1.2
+Name: choxie
+Version: 2.0.0.9
+Summary: Chocolate with a kick!
+Requires-Dist: towel-stuff (0.1)
+Requires-Dist: nut
+Provides-Dist: truffles (1.0)
+Obsoletes-Dist: truffles (<=0.8,>=0.5)
+Obsoletes-Dist: truffles (<=0.9,>=0.6)
diff --git a/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/RECORD b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/RECORD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/RECORD
diff --git a/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/REQUESTED b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/REQUESTED
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/REQUESTED
diff --git a/Lib/packaging/tests/fake_dists/choxie-2.0.0.9/choxie/__init__.py b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9/choxie/__init__.py
new file mode 100644
index 0000000..40a96af
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9/choxie/__init__.py
@@ -0,0 +1 @@
+# -*- coding: utf-8 -*-
diff --git a/Lib/packaging/tests/fake_dists/choxie-2.0.0.9/choxie/chocolate.py b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9/choxie/chocolate.py
new file mode 100644
index 0000000..c4027f3
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9/choxie/chocolate.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+from towel_stuff import Towel
+
+class Chocolate(object):
+ """A piece of chocolate."""
+
+ def wrap_with_towel(self):
+ towel = Towel()
+ towel.wrap(self)
+ return towel
diff --git a/Lib/packaging/tests/fake_dists/choxie-2.0.0.9/truffles.py b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9/truffles.py
new file mode 100644
index 0000000..342b8ea
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9/truffles.py
@@ -0,0 +1,5 @@
+# -*- coding: utf-8 -*-
+from choxie.chocolate import Chocolate
+
+class Truffle(Chocolate):
+ """A truffle."""
diff --git a/Lib/packaging/tests/fake_dists/coconuts-aster-10.3.egg-info/PKG-INFO b/Lib/packaging/tests/fake_dists/coconuts-aster-10.3.egg-info/PKG-INFO
new file mode 100644
index 0000000..499a083
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/coconuts-aster-10.3.egg-info/PKG-INFO
@@ -0,0 +1,5 @@
+Metadata-Version: 1.2
+Name: coconuts-aster
+Version: 10.3
+Provides-Dist: strawberry (0.6)
+Provides-Dist: banana (0.4)
diff --git a/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/INSTALLER b/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/INSTALLER
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/INSTALLER
diff --git a/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/METADATA b/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/METADATA
new file mode 100644
index 0000000..0b99f52
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/METADATA
@@ -0,0 +1,5 @@
+Metadata-Version: 1.2
+Name: grammar
+Version: 1.0a4
+Requires-Dist: truffles (>=1.2)
+Author: Sherlock Holmes
diff --git a/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/RECORD b/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/RECORD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/RECORD
diff --git a/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/REQUESTED b/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/REQUESTED
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/REQUESTED
diff --git a/Lib/packaging/tests/fake_dists/grammar-1.0a4/grammar/__init__.py b/Lib/packaging/tests/fake_dists/grammar-1.0a4/grammar/__init__.py
new file mode 100644
index 0000000..40a96af
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/grammar-1.0a4/grammar/__init__.py
@@ -0,0 +1 @@
+# -*- coding: utf-8 -*-
diff --git a/Lib/packaging/tests/fake_dists/grammar-1.0a4/grammar/utils.py b/Lib/packaging/tests/fake_dists/grammar-1.0a4/grammar/utils.py
new file mode 100644
index 0000000..66ba796
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/grammar-1.0a4/grammar/utils.py
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+from random import randint
+
+def is_valid_grammar(sentence):
+ if randint(0, 10) < 2:
+ return False
+ else:
+ return True
diff --git a/Lib/packaging/tests/fake_dists/nut-funkyversion.egg-info b/Lib/packaging/tests/fake_dists/nut-funkyversion.egg-info
new file mode 100644
index 0000000..0c58ec1
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/nut-funkyversion.egg-info
@@ -0,0 +1,3 @@
+Metadata-Version: 1.2
+Name: nut
+Version: funkyversion
diff --git a/Lib/packaging/tests/fake_dists/strawberry-0.6.egg b/Lib/packaging/tests/fake_dists/strawberry-0.6.egg
new file mode 100644
index 0000000..6d160e8
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/strawberry-0.6.egg
Binary files differ
diff --git a/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/INSTALLER b/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/INSTALLER
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/INSTALLER
diff --git a/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/METADATA b/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/METADATA
new file mode 100644
index 0000000..ca46d0a
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/METADATA
@@ -0,0 +1,7 @@
+Metadata-Version: 1.2
+Name: towel-stuff
+Version: 0.1
+Provides-Dist: truffles (1.1.2)
+Provides-Dist: towel-stuff (0.1)
+Obsoletes-Dist: truffles (!=0.8,<1.0)
+Requires-Dist: bacon (<=0.2)
diff --git a/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/RECORD b/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/RECORD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/RECORD
diff --git a/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/REQUESTED b/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/REQUESTED
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/REQUESTED
diff --git a/Lib/packaging/tests/fake_dists/towel_stuff-0.1/towel_stuff/__init__.py b/Lib/packaging/tests/fake_dists/towel_stuff-0.1/towel_stuff/__init__.py
new file mode 100644
index 0000000..191f895
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/towel_stuff-0.1/towel_stuff/__init__.py
@@ -0,0 +1,18 @@
+# -*- coding: utf-8 -*-
+
+class Towel(object):
+ """A towel, that one should never be without."""
+
+ def __init__(self, color='tie-dye'):
+ self.color = color
+ self.wrapped_obj = None
+
+ def wrap(self, obj):
+ """Wrap an object up in our towel."""
+ self.wrapped_obj = obj
+
+ def unwrap(self):
+ """Unwrap whatever is in our towel and return whatever it is."""
+ obj = self.wrapped_obj
+ self.wrapped_obj = None
+ return obj
diff --git a/Lib/packaging/tests/fake_dists/truffles-5.0.egg-info b/Lib/packaging/tests/fake_dists/truffles-5.0.egg-info
new file mode 100644
index 0000000..45f0cf8
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/truffles-5.0.egg-info
@@ -0,0 +1,3 @@
+Metadata-Version: 1.2
+Name: truffles
+Version: 5.0
diff --git a/Lib/packaging/tests/fixer/__init__.py b/Lib/packaging/tests/fixer/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fixer/__init__.py
diff --git a/Lib/packaging/tests/fixer/fix_idioms.py b/Lib/packaging/tests/fixer/fix_idioms.py
new file mode 100644
index 0000000..64f5ea0
--- /dev/null
+++ b/Lib/packaging/tests/fixer/fix_idioms.py
@@ -0,0 +1,134 @@
+"""Adjust some old Python 2 idioms to their modern counterparts.
+
+* Change some type comparisons to isinstance() calls:
+ type(x) == T -> isinstance(x, T)
+ type(x) is T -> isinstance(x, T)
+ type(x) != T -> not isinstance(x, T)
+ type(x) is not T -> not isinstance(x, T)
+
+* Change "while 1:" into "while True:".
+
+* Change both
+
+ v = list(EXPR)
+ v.sort()
+ foo(v)
+
+and the more general
+
+ v = EXPR
+ v.sort()
+ foo(v)
+
+into
+
+ v = sorted(EXPR)
+ foo(v)
+"""
+# Author: Jacques Frechet, Collin Winter
+
+# Local imports
+from lib2to3 import fixer_base
+from lib2to3.fixer_util import Call, Comma, Name, Node, syms
+
+CMP = "(n='!=' | '==' | 'is' | n=comp_op< 'is' 'not' >)"
+TYPE = "power< 'type' trailer< '(' x=any ')' > >"
+
+class FixIdioms(fixer_base.BaseFix):
+
+ explicit = False # The user must ask for this fixer
+
+ PATTERN = r"""
+ isinstance=comparison< %s %s T=any >
+ |
+ isinstance=comparison< T=any %s %s >
+ |
+ while_stmt< 'while' while='1' ':' any+ >
+ |
+ sorted=any<
+ any*
+ simple_stmt<
+ expr_stmt< id1=any '='
+ power< list='list' trailer< '(' (not arglist<any+>) any ')' > >
+ >
+ '\n'
+ >
+ sort=
+ simple_stmt<
+ power< id2=any
+ trailer< '.' 'sort' > trailer< '(' ')' >
+ >
+ '\n'
+ >
+ next=any*
+ >
+ |
+ sorted=any<
+ any*
+ simple_stmt< expr_stmt< id1=any '=' expr=any > '\n' >
+ sort=
+ simple_stmt<
+ power< id2=any
+ trailer< '.' 'sort' > trailer< '(' ')' >
+ >
+ '\n'
+ >
+ next=any*
+ >
+ """ % (TYPE, CMP, CMP, TYPE)
+
+ def match(self, node):
+ r = super(FixIdioms, self).match(node)
+ # If we've matched one of the sort/sorted subpatterns above, we
+ # want to reject matches where the initial assignment and the
+ # subsequent .sort() call involve different identifiers.
+ if r and "sorted" in r:
+ if r["id1"] == r["id2"]:
+ return r
+ return None
+ return r
+
+ def transform(self, node, results):
+ if "isinstance" in results:
+ return self.transform_isinstance(node, results)
+ elif "while" in results:
+ return self.transform_while(node, results)
+ elif "sorted" in results:
+ return self.transform_sort(node, results)
+ else:
+ raise RuntimeError("Invalid match")
+
+ def transform_isinstance(self, node, results):
+ x = results["x"].clone() # The thing inside of type()
+ T = results["T"].clone() # The type being compared against
+ x.prefix = ""
+ T.prefix = " "
+ test = Call(Name("isinstance"), [x, Comma(), T])
+ if "n" in results:
+ test.prefix = " "
+ test = Node(syms.not_test, [Name("not"), test])
+ test.prefix = node.prefix
+ return test
+
+ def transform_while(self, node, results):
+ one = results["while"]
+ one.replace(Name("True", prefix=one.prefix))
+
+ def transform_sort(self, node, results):
+ sort_stmt = results["sort"]
+ next_stmt = results["next"]
+ list_call = results.get("list")
+ simple_expr = results.get("expr")
+
+ if list_call:
+ list_call.replace(Name("sorted", prefix=list_call.prefix))
+ elif simple_expr:
+ new = simple_expr.clone()
+ new.prefix = ""
+ simple_expr.replace(Call(Name("sorted"), [new],
+ prefix=simple_expr.prefix))
+ else:
+ raise RuntimeError("should not have reached here")
+ sort_stmt.remove()
+ if next_stmt:
+ next_stmt[0].prefix = sort_stmt._prefix
diff --git a/Lib/packaging/tests/pypi_server.py b/Lib/packaging/tests/pypi_server.py
new file mode 100644
index 0000000..13c30cf
--- /dev/null
+++ b/Lib/packaging/tests/pypi_server.py
@@ -0,0 +1,449 @@
+"""Mock PyPI Server implementation, to use in tests.
+
+This module also provides a simple test case to extend if you need to use
+the PyPIServer all along your test case. Be sure to read the documentation
+before any use.
+
+XXX TODO:
+
+The mock server can handle simple HTTP request (to simulate a simple index) or
+XMLRPC requests, over HTTP. Both does not have the same intergface to deal
+with, and I think it's a pain.
+
+A good idea could be to re-think a bit the way dstributions are handled in the
+mock server. As it should return malformed HTML pages, we need to keep the
+static behavior.
+
+I think of something like that:
+
+ >>> server = PyPIMockServer()
+ >>> server.startHTTP()
+ >>> server.startXMLRPC()
+
+Then, the server must have only one port to rely on, eg.
+
+ >>> server.fulladdress()
+ "http://ip:port/"
+
+It could be simple to have one HTTP server, relaying the requests to the two
+implementations (static HTTP and XMLRPC over HTTP).
+"""
+
+import os
+import queue
+import select
+import threading
+from functools import wraps
+from http.server import HTTPServer, SimpleHTTPRequestHandler
+from xmlrpc.server import SimpleXMLRPCServer
+
+from packaging.tests import unittest
+
+
+PYPI_DEFAULT_STATIC_PATH = os.path.join(
+ os.path.dirname(os.path.abspath(__file__)), 'pypiserver')
+
+
+def use_xmlrpc_server(*server_args, **server_kwargs):
+ server_kwargs['serve_xmlrpc'] = True
+ return use_pypi_server(*server_args, **server_kwargs)
+
+
+def use_http_server(*server_args, **server_kwargs):
+ server_kwargs['serve_xmlrpc'] = False
+ return use_pypi_server(*server_args, **server_kwargs)
+
+
+def use_pypi_server(*server_args, **server_kwargs):
+ """Decorator to make use of the PyPIServer for test methods,
+ just when needed, and not for the entire duration of the testcase.
+ """
+ def wrapper(func):
+ @wraps(func)
+ def wrapped(*args, **kwargs):
+ server = PyPIServer(*server_args, **server_kwargs)
+ server.start()
+ try:
+ func(server=server, *args, **kwargs)
+ finally:
+ server.stop()
+ return wrapped
+ return wrapper
+
+
+class PyPIServerTestCase(unittest.TestCase):
+
+ def setUp(self):
+ super(PyPIServerTestCase, self).setUp()
+ self.pypi = PyPIServer()
+ self.pypi.start()
+ self.addCleanup(self.pypi.stop)
+
+
+class PyPIServer(threading.Thread):
+ """PyPI Mocked server.
+ Provides a mocked version of the PyPI API's, to ease tests.
+
+ Support serving static content and serving previously given text.
+ """
+
+ def __init__(self, test_static_path=None,
+ static_filesystem_paths=None,
+ static_uri_paths=["simple", "packages"], serve_xmlrpc=False):
+ """Initialize the server.
+
+ Default behavior is to start the HTTP server. You can either start the
+ xmlrpc server by setting xmlrpc to True. Caution: Only one server will
+ be started.
+
+ static_uri_paths and static_base_path are parameters used to provides
+ respectively the http_paths to serve statically, and where to find the
+ matching files on the filesystem.
+ """
+ # we want to launch the server in a new dedicated thread, to not freeze
+ # tests.
+ super(PyPIServer, self).__init__()
+ self._run = True
+ self._serve_xmlrpc = serve_xmlrpc
+ if static_filesystem_paths is None:
+ static_filesystem_paths = ["default"]
+
+ #TODO allow to serve XMLRPC and HTTP static files at the same time.
+ if not self._serve_xmlrpc:
+ self.server = HTTPServer(('127.0.0.1', 0), PyPIRequestHandler)
+ self.server.RequestHandlerClass.pypi_server = self
+
+ self.request_queue = queue.Queue()
+ self._requests = []
+ self.default_response_status = 404
+ self.default_response_headers = [('Content-type', 'text/plain')]
+ self.default_response_data = "The page does not exists"
+
+ # initialize static paths / filesystems
+ self.static_uri_paths = static_uri_paths
+
+ # append the static paths defined locally
+ if test_static_path is not None:
+ static_filesystem_paths.append(test_static_path)
+ self.static_filesystem_paths = [
+ PYPI_DEFAULT_STATIC_PATH + "/" + path
+ for path in static_filesystem_paths]
+ else:
+ # XMLRPC server
+ self.server = PyPIXMLRPCServer(('127.0.0.1', 0))
+ self.xmlrpc = XMLRPCMockIndex()
+ # register the xmlrpc methods
+ self.server.register_introspection_functions()
+ self.server.register_instance(self.xmlrpc)
+
+ self.address = ('127.0.0.1', self.server.server_port)
+ # to not have unwanted outputs.
+ self.server.RequestHandlerClass.log_request = lambda *_: None
+
+ def run(self):
+ # loop because we can't stop it otherwise, for python < 2.6
+ while self._run:
+ r, w, e = select.select([self.server], [], [], 0.5)
+ if r:
+ self.server.handle_request()
+
+ def stop(self):
+ """self shutdown is not supported for python < 2.6"""
+ self._run = False
+ if self.is_alive():
+ self.join()
+ self.server.server_close()
+
+ def get_next_response(self):
+ return (self.default_response_status,
+ self.default_response_headers,
+ self.default_response_data)
+
+ @property
+ def requests(self):
+ """Use this property to get all requests that have been made
+ to the server
+ """
+ while True:
+ try:
+ self._requests.append(self.request_queue.get_nowait())
+ except queue.Empty:
+ break
+ return self._requests
+
+ @property
+ def full_address(self):
+ return "http://%s:%s" % self.address
+
+
+class PyPIRequestHandler(SimpleHTTPRequestHandler):
+ # we need to access the pypi server while serving the content
+ pypi_server = None
+
+ def serve_request(self):
+ """Serve the content.
+
+ Also record the requests to be accessed later. If trying to access an
+ url matching a static uri, serve static content, otherwise serve
+ what is provided by the `get_next_response` method.
+
+ If nothing is defined there, return a 404 header.
+ """
+ # record the request. Read the input only on PUT or POST requests
+ if self.command in ("PUT", "POST"):
+ if 'content-length' in self.headers:
+ request_data = self.rfile.read(
+ int(self.headers['content-length']))
+ else:
+ request_data = self.rfile.read()
+
+ elif self.command in ("GET", "DELETE"):
+ request_data = ''
+
+ self.pypi_server.request_queue.put((self, request_data))
+
+ # serve the content from local disc if we request an URL beginning
+ # by a pattern defined in `static_paths`
+ url_parts = self.path.split("/")
+ if (len(url_parts) > 1 and
+ url_parts[1] in self.pypi_server.static_uri_paths):
+ data = None
+ # always take the last first.
+ fs_paths = []
+ fs_paths.extend(self.pypi_server.static_filesystem_paths)
+ fs_paths.reverse()
+ relative_path = self.path
+ for fs_path in fs_paths:
+ try:
+ if self.path.endswith("/"):
+ relative_path += "index.html"
+
+ if relative_path.endswith('.tar.gz'):
+ with open(fs_path + relative_path, 'rb') as file:
+ data = file.read()
+ headers = [('Content-type', 'application/x-gtar')]
+ else:
+ with open(fs_path + relative_path) as file:
+ data = file.read().encode()
+ headers = [('Content-type', 'text/html')]
+
+ headers.append(('Content-Length', len(data)))
+ self.make_response(data, headers=headers)
+
+ except IOError:
+ pass
+
+ if data is None:
+ self.make_response("Not found", 404)
+
+ # otherwise serve the content from get_next_response
+ else:
+ # send back a response
+ status, headers, data = self.pypi_server.get_next_response()
+ self.make_response(data, status, headers)
+
+ do_POST = do_GET = do_DELETE = do_PUT = serve_request
+
+ def make_response(self, data, status=200,
+ headers=[('Content-type', 'text/html')]):
+ """Send the response to the HTTP client"""
+ if not isinstance(status, int):
+ try:
+ status = int(status)
+ except ValueError:
+ # we probably got something like YYY Codename.
+ # Just get the first 3 digits
+ status = int(status[:3])
+
+ self.send_response(status)
+ for header, value in headers:
+ self.send_header(header, value)
+ self.end_headers()
+
+ if isinstance(data, str):
+ data = data.encode('utf-8')
+
+ self.wfile.write(data)
+
+
+class PyPIXMLRPCServer(SimpleXMLRPCServer):
+ def server_bind(self):
+ """Override server_bind to store the server name."""
+ super(PyPIXMLRPCServer, self).server_bind()
+ host, port = self.socket.getsockname()[:2]
+ self.server_port = port
+
+
+class MockDist:
+ """Fake distribution, used in the Mock PyPI Server"""
+
+ def __init__(self, name, version="1.0", hidden=False, url="http://url/",
+ type="sdist", filename="", size=10000,
+ digest="123456", downloads=7, has_sig=False,
+ python_version="source", comment="comment",
+ author="John Doe", author_email="john@doe.name",
+ maintainer="Main Tayner", maintainer_email="maintainer_mail",
+ project_url="http://project_url/", homepage="http://homepage/",
+ keywords="", platform="UNKNOWN", classifiers=[], licence="",
+ description="Description", summary="Summary", stable_version="",
+ ordering="", documentation_id="", code_kwalitee_id="",
+ installability_id="", obsoletes=[], obsoletes_dist=[],
+ provides=[], provides_dist=[], requires=[], requires_dist=[],
+ requires_external=[], requires_python=""):
+
+ # basic fields
+ self.name = name
+ self.version = version
+ self.hidden = hidden
+
+ # URL infos
+ self.url = url
+ self.digest = digest
+ self.downloads = downloads
+ self.has_sig = has_sig
+ self.python_version = python_version
+ self.comment = comment
+ self.type = type
+
+ # metadata
+ self.author = author
+ self.author_email = author_email
+ self.maintainer = maintainer
+ self.maintainer_email = maintainer_email
+ self.project_url = project_url
+ self.homepage = homepage
+ self.keywords = keywords
+ self.platform = platform
+ self.classifiers = classifiers
+ self.licence = licence
+ self.description = description
+ self.summary = summary
+ self.stable_version = stable_version
+ self.ordering = ordering
+ self.cheesecake_documentation_id = documentation_id
+ self.cheesecake_code_kwalitee_id = code_kwalitee_id
+ self.cheesecake_installability_id = installability_id
+
+ self.obsoletes = obsoletes
+ self.obsoletes_dist = obsoletes_dist
+ self.provides = provides
+ self.provides_dist = provides_dist
+ self.requires = requires
+ self.requires_dist = requires_dist
+ self.requires_external = requires_external
+ self.requires_python = requires_python
+
+ def url_infos(self):
+ return {
+ 'url': self.url,
+ 'packagetype': self.type,
+ 'filename': 'filename.tar.gz',
+ 'size': '6000',
+ 'md5_digest': self.digest,
+ 'downloads': self.downloads,
+ 'has_sig': self.has_sig,
+ 'python_version': self.python_version,
+ 'comment_text': self.comment,
+ }
+
+ def metadata(self):
+ return {
+ 'maintainer': self.maintainer,
+ 'project_url': [self.project_url],
+ 'maintainer_email': self.maintainer_email,
+ 'cheesecake_code_kwalitee_id': self.cheesecake_code_kwalitee_id,
+ 'keywords': self.keywords,
+ 'obsoletes_dist': self.obsoletes_dist,
+ 'requires_external': self.requires_external,
+ 'author': self.author,
+ 'author_email': self.author_email,
+ 'download_url': self.url,
+ 'platform': self.platform,
+ 'version': self.version,
+ 'obsoletes': self.obsoletes,
+ 'provides': self.provides,
+ 'cheesecake_documentation_id': self.cheesecake_documentation_id,
+ '_pypi_hidden': self.hidden,
+ 'description': self.description,
+ '_pypi_ordering': 19,
+ 'requires_dist': self.requires_dist,
+ 'requires_python': self.requires_python,
+ 'classifiers': [],
+ 'name': self.name,
+ 'licence': self.licence, # XXX licence or license?
+ 'summary': self.summary,
+ 'home_page': self.homepage,
+ 'stable_version': self.stable_version,
+ # FIXME doesn't that reproduce the bug from 6527d3106e9f?
+ 'provides_dist': (self.provides_dist or
+ "%s (%s)" % (self.name, self.version)),
+ 'requires': self.requires,
+ 'cheesecake_installability_id': self.cheesecake_installability_id,
+ }
+
+ def search_result(self):
+ return {
+ '_pypi_ordering': 0,
+ 'version': self.version,
+ 'name': self.name,
+ 'summary': self.summary,
+ }
+
+
+class XMLRPCMockIndex:
+ """Mock XMLRPC server"""
+
+ def __init__(self, dists=[]):
+ self._dists = dists
+ self._search_result = []
+
+ def add_distributions(self, dists):
+ for dist in dists:
+ self._dists.append(MockDist(**dist))
+
+ def set_distributions(self, dists):
+ self._dists = []
+ self.add_distributions(dists)
+
+ def set_search_result(self, result):
+ """set a predefined search result"""
+ self._search_result = result
+
+ def _get_search_results(self):
+ results = []
+ for name in self._search_result:
+ found_dist = [d for d in self._dists if d.name == name]
+ if found_dist:
+ results.append(found_dist[0])
+ else:
+ dist = MockDist(name)
+ results.append(dist)
+ self._dists.append(dist)
+ return [r.search_result() for r in results]
+
+ def list_packages(self):
+ return [d.name for d in self._dists]
+
+ def package_releases(self, package_name, show_hidden=False):
+ if show_hidden:
+ # return all
+ return [d.version for d in self._dists if d.name == package_name]
+ else:
+ # return only un-hidden
+ return [d.version for d in self._dists if d.name == package_name
+ and not d.hidden]
+
+ def release_urls(self, package_name, version):
+ return [d.url_infos() for d in self._dists
+ if d.name == package_name and d.version == version]
+
+ def release_data(self, package_name, version):
+ release = [d for d in self._dists
+ if d.name == package_name and d.version == version]
+ if release:
+ return release[0].metadata()
+ else:
+ return {}
+
+ def search(self, spec, operator="and"):
+ return self._get_search_results()
diff --git a/Lib/packaging/tests/pypi_test_server.py b/Lib/packaging/tests/pypi_test_server.py
new file mode 100644
index 0000000..8c8c641
--- /dev/null
+++ b/Lib/packaging/tests/pypi_test_server.py
@@ -0,0 +1,59 @@
+"""Test PyPI Server implementation at testpypi.python.org, to use in tests.
+
+This is a drop-in replacement for the mock pypi server for testing against a
+real pypi server hosted by python.org especially for testing against.
+"""
+
+import unittest
+
+PYPI_DEFAULT_STATIC_PATH = None
+
+
+def use_xmlrpc_server(*server_args, **server_kwargs):
+ server_kwargs['serve_xmlrpc'] = True
+ return use_pypi_server(*server_args, **server_kwargs)
+
+
+def use_http_server(*server_args, **server_kwargs):
+ server_kwargs['serve_xmlrpc'] = False
+ return use_pypi_server(*server_args, **server_kwargs)
+
+
+def use_pypi_server(*server_args, **server_kwargs):
+ """Decorator to make use of the PyPIServer for test methods,
+ just when needed, and not for the entire duration of the testcase.
+ """
+ def wrapper(func):
+ def wrapped(*args, **kwargs):
+ server = PyPIServer(*server_args, **server_kwargs)
+ func(server=server, *args, **kwargs)
+ return wrapped
+ return wrapper
+
+
+class PyPIServerTestCase(unittest.TestCase):
+
+ def setUp(self):
+ super(PyPIServerTestCase, self).setUp()
+ self.pypi = PyPIServer()
+ self.pypi.start()
+ self.addCleanup(self.pypi.stop)
+
+
+class PyPIServer:
+ """Shim to access testpypi.python.org, for testing a real server."""
+
+ def __init__(self, test_static_path=None,
+ static_filesystem_paths=["default"],
+ static_uri_paths=["simple"], serve_xmlrpc=False):
+ self.address = ('testpypi.python.org', '80')
+
+ def start(self):
+ pass
+
+ def stop(self):
+ pass
+
+ @property
+ def full_address(self):
+ return "http://%s:%s" % self.address
diff --git a/Lib/packaging/tests/pypiserver/downloads_with_md5/packages/source/f/foobar/foobar-0.1.tar.gz b/Lib/packaging/tests/pypiserver/downloads_with_md5/packages/source/f/foobar/foobar-0.1.tar.gz
new file mode 100644
index 0000000..333961e
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/downloads_with_md5/packages/source/f/foobar/foobar-0.1.tar.gz
Binary files differ
diff --git a/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/badmd5/badmd5-0.1.tar.gz b/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/badmd5/badmd5-0.1.tar.gz
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/badmd5/badmd5-0.1.tar.gz
diff --git a/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/badmd5/index.html b/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/badmd5/index.html
new file mode 100644
index 0000000..b89f1bd
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/badmd5/index.html
@@ -0,0 +1,3 @@
+<html><body>
+<a href="badmd5-0.1.tar.gz#md5=3e3d86693d6564c807272b11b3069dfe" rel="download">badmd5-0.1.tar.gz</a><br/>
+</body></html>
diff --git a/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/foobar/index.html b/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/foobar/index.html
new file mode 100644
index 0000000..9e42b16
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/foobar/index.html
@@ -0,0 +1,3 @@
+<html><body>
+<a href="foobar-0.1.tar.gz#md5=fe18804c5b722ff024cabdf514924fc4" rel="download">foobar-0.1.tar.gz</a><br/>
+</body></html>
diff --git a/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/index.html b/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/index.html
new file mode 100644
index 0000000..9baee04
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/index.html
@@ -0,0 +1,2 @@
+<a href="foobar/">foobar/</a>
+<a href="badmd5/">badmd5/</a>
diff --git a/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/bar/index.html b/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/bar/index.html
new file mode 100644
index 0000000..c3d42c5
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/bar/index.html
@@ -0,0 +1,6 @@
+<html><head><title>Links for bar</title></head><body><h1>Links for bar</h1>
+<a rel="download" href="../../packages/source/F/bar/bar-1.0.tar.gz">bar-1.0.tar.gz</a><br/>
+<a rel="download" href="../../packages/source/F/bar/bar-1.0.1.tar.gz">bar-1.0.1.tar.gz</a><br/>
+<a rel="download" href="../../packages/source/F/bar/bar-2.0.tar.gz">bar-2.0.tar.gz</a><br/>
+<a rel="download" href="../../packages/source/F/bar/bar-2.0.1.tar.gz">bar-2.0.1.tar.gz</a><br/>
+</body></html>
diff --git a/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/baz/index.html b/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/baz/index.html
new file mode 100644
index 0000000..4f34312
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/baz/index.html
@@ -0,0 +1,6 @@
+<html><head><title>Links for baz</title></head><body><h1>Links for baz</h1>
+<a rel="download" href="../../packages/source/F/baz/baz-1.0.tar.gz">baz-1.0.tar.gz</a><br/>
+<a rel="download" href="../../packages/source/F/baz/baz-1.0.1.tar.gz">baz-1.0.1.tar.gz</a><br/>
+<a rel="download" href="../../packages/source/F/baz/baz-2.0.tar.gz">baz-2.0.tar.gz</a><br/>
+<a rel="download" href="../../packages/source/F/baz/baz-2.0.1.tar.gz">baz-2.0.1.tar.gz</a><br/>
+</body></html>
diff --git a/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/foo/index.html b/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/foo/index.html
new file mode 100644
index 0000000..0565e11
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/foo/index.html
@@ -0,0 +1,6 @@
+<html><head><title>Links for foo</title></head><body><h1>Links for foo</h1>
+<a rel="download" href="../../packages/source/F/foo/foo-1.0.tar.gz">foo-1.0.tar.gz</a><br/>
+<a rel="download" href="../../packages/source/F/foo/foo-1.0.1.tar.gz">foo-1.0.1.tar.gz</a><br/>
+<a rel="download" href="../../packages/source/F/foo/foo-2.0.tar.gz">foo-2.0.tar.gz</a><br/>
+<a rel="download" href="../../packages/source/F/foo/foo-2.0.1.tar.gz">foo-2.0.1.tar.gz</a><br/>
+</body></html>
diff --git a/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/index.html b/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/index.html
new file mode 100644
index 0000000..a70cfd3
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/index.html
@@ -0,0 +1,3 @@
+<a href="foo/">foo/</a>
+<a href="bar/">bar/</a>
+<a href="baz/">baz/</a>
diff --git a/Lib/packaging/tests/pypiserver/project_list/simple/index.html b/Lib/packaging/tests/pypiserver/project_list/simple/index.html
new file mode 100644
index 0000000..b36d728
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/project_list/simple/index.html
@@ -0,0 +1,5 @@
+<a class="test" href="yeah">FooBar-bar</a>
+<a class="test" href="yeah">Foobar-baz</a>
+<a class="test" href="yeah">Baz-FooBar</a>
+<a class="test" href="yeah">Baz</a>
+<a class="test" href="yeah">Foo</a>
diff --git a/Lib/packaging/tests/pypiserver/test_found_links/simple/foobar/index.html b/Lib/packaging/tests/pypiserver/test_found_links/simple/foobar/index.html
new file mode 100644
index 0000000..a282a4e
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/test_found_links/simple/foobar/index.html
@@ -0,0 +1,6 @@
+<html><head><title>Links for Foobar</title></head><body><h1>Links for Foobar</h1>
+<a rel="download" href="../../packages/source/F/Foobar/Foobar-1.0.tar.gz#md5=98fa833fdabcdd78d00245aead66c174">Foobar-1.0.tar.gz</a><br/>
+<a rel="download" href="../../packages/source/F/Foobar/Foobar-1.0.1.tar.gz#md5=2351efb20f6b7b5d9ce80fa4cb1bd9ca">Foobar-1.0.1.tar.gz</a><br/>
+<a rel="download" href="../../packages/source/F/Foobar/Foobar-2.0.tar.gz#md5=98fa833fdabcdd78d00245aead66c274">Foobar-2.0.tar.gz</a><br/>
+<a rel="download" href="../../packages/source/F/Foobar/Foobar-2.0.1.tar.gz#md5=2352efb20f6b7b5d9ce80fa4cb2bd9ca">Foobar-2.0.1.tar.gz</a><br/>
+</body></html>
diff --git a/Lib/packaging/tests/pypiserver/test_found_links/simple/index.html b/Lib/packaging/tests/pypiserver/test_found_links/simple/index.html
new file mode 100644
index 0000000..a1a7bb7
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/test_found_links/simple/index.html
@@ -0,0 +1 @@
+<a href="foobar/">foobar/</a>
diff --git a/Lib/packaging/tests/pypiserver/test_pypi_server/external/index.html b/Lib/packaging/tests/pypiserver/test_pypi_server/external/index.html
new file mode 100644
index 0000000..265ee0a
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/test_pypi_server/external/index.html
@@ -0,0 +1 @@
+index.html from external server
diff --git a/Lib/packaging/tests/pypiserver/test_pypi_server/simple/index.html b/Lib/packaging/tests/pypiserver/test_pypi_server/simple/index.html
new file mode 100644
index 0000000..6f97667
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/test_pypi_server/simple/index.html
@@ -0,0 +1 @@
+Yeah
diff --git a/Lib/packaging/tests/pypiserver/with_externals/external/external.html b/Lib/packaging/tests/pypiserver/with_externals/external/external.html
new file mode 100644
index 0000000..92e4702
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/with_externals/external/external.html
@@ -0,0 +1,3 @@
+<html><body>
+<a href="/foobar-0.1.tar.gz#md5=1__bad_md5___">bad old link</a>
+</body></html>
diff --git a/Lib/packaging/tests/pypiserver/with_externals/simple/foobar/index.html b/Lib/packaging/tests/pypiserver/with_externals/simple/foobar/index.html
new file mode 100644
index 0000000..b100a26
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/with_externals/simple/foobar/index.html
@@ -0,0 +1,4 @@
+<html><body>
+<a rel ="download" href="/foobar-0.1.tar.gz#md5=12345678901234567">foobar-0.1.tar.gz</a><br/>
+<a href="../../external/external.html" rel="homepage">external homepage</a><br/>
+</body></html>
diff --git a/Lib/packaging/tests/pypiserver/with_externals/simple/index.html b/Lib/packaging/tests/pypiserver/with_externals/simple/index.html
new file mode 100644
index 0000000..a1a7bb7
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/with_externals/simple/index.html
@@ -0,0 +1 @@
+<a href="foobar/">foobar/</a>
diff --git a/Lib/packaging/tests/pypiserver/with_norel_links/external/homepage.html b/Lib/packaging/tests/pypiserver/with_norel_links/external/homepage.html
new file mode 100644
index 0000000..1cc0c32
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/with_norel_links/external/homepage.html
@@ -0,0 +1,7 @@
+<html>
+<body>
+<p>a rel=homepage HTML page</p>
+<a href="/foobar-2.0.tar.gz">foobar 2.0</a>
+</body>
+</html>
+
diff --git a/Lib/packaging/tests/pypiserver/with_norel_links/external/nonrel.html b/Lib/packaging/tests/pypiserver/with_norel_links/external/nonrel.html
new file mode 100644
index 0000000..f6ace22
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/with_norel_links/external/nonrel.html
@@ -0,0 +1 @@
+A page linked without rel="download" or rel="homepage" link.
diff --git a/Lib/packaging/tests/pypiserver/with_norel_links/simple/foobar/index.html b/Lib/packaging/tests/pypiserver/with_norel_links/simple/foobar/index.html
new file mode 100644
index 0000000..171df93
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/with_norel_links/simple/foobar/index.html
@@ -0,0 +1,6 @@
+<html><body>
+<a rel="download" href="/foobar-0.1.tar.gz" rel="download">foobar-0.1.tar.gz</a><br/>
+<a href="../../external/homepage.html" rel="homepage">external homepage</a><br/>
+<a href="../../external/nonrel.html">unrelated link</a><br/>
+<a href="/unrelated-0.2.tar.gz">unrelated download</a></br/>
+</body></html>
diff --git a/Lib/packaging/tests/pypiserver/with_norel_links/simple/index.html b/Lib/packaging/tests/pypiserver/with_norel_links/simple/index.html
new file mode 100644
index 0000000..a1a7bb7
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/with_norel_links/simple/index.html
@@ -0,0 +1 @@
+<a href="foobar/">foobar/</a>
diff --git a/Lib/packaging/tests/pypiserver/with_real_externals/simple/foobar/index.html b/Lib/packaging/tests/pypiserver/with_real_externals/simple/foobar/index.html
new file mode 100644
index 0000000..b2885ae
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/with_real_externals/simple/foobar/index.html
@@ -0,0 +1,4 @@
+<html><body>
+<a rel="download" href="/foobar-0.1.tar.gz#md5=0_correct_md5">foobar-0.1.tar.gz</a><br/>
+<a href="http://a-really-external-website/external/external.html" rel="homepage">external homepage</a><br/>
+</body></html>
diff --git a/Lib/packaging/tests/pypiserver/with_real_externals/simple/index.html b/Lib/packaging/tests/pypiserver/with_real_externals/simple/index.html
new file mode 100644
index 0000000..a1a7bb7
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/with_real_externals/simple/index.html
@@ -0,0 +1 @@
+<a href="foobar/">foobar/</a>
diff --git a/Lib/packaging/tests/support.py b/Lib/packaging/tests/support.py
new file mode 100644
index 0000000..441efc0
--- /dev/null
+++ b/Lib/packaging/tests/support.py
@@ -0,0 +1,392 @@
+"""Support code for packaging test cases.
+
+*This module should not be considered public: its content and API may
+change in incompatible ways.*
+
+A few helper classes are provided: LoggingCatcher, TempdirManager and
+EnvironRestorer. They are written to be used as mixins::
+
+ from packaging.tests import unittest
+ from packaging.tests.support import LoggingCatcher
+
+ class SomeTestCase(LoggingCatcher, unittest.TestCase):
+ ...
+
+If you need to define a setUp method on your test class, you have to
+call the mixin class' setUp method or it won't work (same thing for
+tearDown):
+
+ def setUp(self):
+ super(SomeTestCase, self).setUp()
+ ... # other setup code
+
+Also provided is a DummyCommand class, useful to mock commands in the
+tests of another command that needs them, for example to fake
+compilation in build_ext (this requires that the mock build_ext command
+be injected into the distribution object's command_obj dictionary).
+
+For tests that need to compile an extension module, use the
+copy_xxmodule_c and fixup_build_ext functions.
+
+Each class or function has a docstring to explain its purpose and usage.
+Existing tests should also be used as examples.
+"""
+
+import os
+import sys
+import shutil
+import logging
+import weakref
+import tempfile
+import sysconfig
+
+from packaging.dist import Distribution
+from packaging.util import resolve_name
+from packaging.command import set_command, _COMMANDS
+
+from packaging.tests import unittest
+from test.support import requires_zlib, unlink
+
+# define __all__ to make pydoc more useful
+__all__ = [
+ # TestCase mixins
+ 'LoggingCatcher', 'TempdirManager', 'EnvironRestorer',
+ # mocks
+ 'DummyCommand', 'TestDistribution', 'Inputs',
+ # misc. functions and decorators
+ 'fake_dec', 'create_distribution', 'use_command',
+ 'copy_xxmodule_c', 'fixup_build_ext',
+ # imported from this module for backport purposes
+ 'unittest', 'requires_zlib', 'skip_2to3_optimize', 'skip_unless_symlink',
+]
+
+
+logger = logging.getLogger('packaging')
+logger2to3 = logging.getLogger('RefactoringTool')
+
+
+class _TestHandler(logging.handlers.BufferingHandler):
+ # stolen and adapted from test.support
+
+ def __init__(self):
+ super(_TestHandler, self).__init__(0)
+ self.setLevel(logging.DEBUG)
+
+ def shouldFlush(self):
+ return False
+
+ def emit(self, record):
+ self.buffer.append(record)
+
+
+class LoggingCatcher:
+ """TestCase-compatible mixin to receive logging calls.
+
+ Upon setUp, instances of this classes get a BufferingHandler that's
+ configured to record all messages logged to the 'packaging' logger.
+
+ Use get_logs to retrieve messages and self.loghandler.flush to discard
+ them. get_logs automatically flushes the logs, unless you pass
+ *flush=False*, for example to make multiple calls to the method with
+ different level arguments. If your test calls some code that generates
+ logging message and then you don't call get_logs, you will need to flush
+ manually before testing other code in the same test_* method, otherwise
+ get_logs in the next lines will see messages from the previous lines.
+ See example in test_command_check.
+ """
+
+ def setUp(self):
+ super(LoggingCatcher, self).setUp()
+ self.loghandler = handler = _TestHandler()
+ self._old_levels = logger.level, logger2to3.level
+ logger.addHandler(handler)
+ logger.setLevel(logging.DEBUG) # we want all messages
+ logger2to3.setLevel(logging.CRITICAL) # we don't want 2to3 messages
+
+ def tearDown(self):
+ handler = self.loghandler
+ # All this is necessary to properly shut down the logging system and
+ # avoid a regrtest complaint. Thanks to Vinay Sajip for the help.
+ handler.close()
+ logger.removeHandler(handler)
+ for ref in weakref.getweakrefs(handler):
+ logging._removeHandlerRef(ref)
+ del self.loghandler
+ logger.setLevel(self._old_levels[0])
+ logger2to3.setLevel(self._old_levels[1])
+ super(LoggingCatcher, self).tearDown()
+
+ def get_logs(self, level=logging.WARNING, flush=True):
+ """Return all log messages with given level.
+
+ *level* defaults to logging.WARNING.
+
+ For log calls with arguments (i.e. logger.info('bla bla %r', arg)),
+ the messages will be formatted before being returned (e.g. "bla bla
+ 'thing'").
+
+ Returns a list. Automatically flushes the loghandler after being
+ called, unless *flush* is False (this is useful to get e.g. all
+ warnings then all info messages).
+ """
+ messages = [log.getMessage() for log in self.loghandler.buffer
+ if log.levelno == level]
+ if flush:
+ self.loghandler.flush()
+ return messages
+
+
+class TempdirManager:
+ """TestCase-compatible mixin to create temporary directories and files.
+
+ Directories and files created in a test_* method will be removed after it
+ has run.
+ """
+
+ def setUp(self):
+ super(TempdirManager, self).setUp()
+ self._olddir = os.getcwd()
+ self._basetempdir = tempfile.mkdtemp()
+ self._files = []
+
+ def tearDown(self):
+ for handle, name in self._files:
+ handle.close()
+ unlink(name)
+
+ os.chdir(self._olddir)
+ shutil.rmtree(self._basetempdir)
+ super(TempdirManager, self).tearDown()
+
+ def mktempfile(self):
+ """Create a read-write temporary file and return it."""
+ fd, fn = tempfile.mkstemp(dir=self._basetempdir)
+ os.close(fd)
+ fp = open(fn, 'w+')
+ self._files.append((fp, fn))
+ return fp
+
+ def mkdtemp(self):
+ """Create a temporary directory and return its path."""
+ d = tempfile.mkdtemp(dir=self._basetempdir)
+ return d
+
+ def write_file(self, path, content='xxx', encoding=None):
+ """Write a file at the given path.
+
+ path can be a string, a tuple or a list; if it's a tuple or list,
+ os.path.join will be used to produce a path.
+ """
+ if isinstance(path, (list, tuple)):
+ path = os.path.join(*path)
+ with open(path, 'w', encoding=encoding) as f:
+ f.write(content)
+
+ def create_dist(self, **kw):
+ """Create a stub distribution object and files.
+
+ This function creates a Distribution instance (use keyword arguments
+ to customize it) and a temporary directory with a project structure
+ (currently an empty directory).
+
+ It returns the path to the directory and the Distribution instance.
+ You can use self.write_file to write any file in that
+ directory, e.g. setup scripts or Python modules.
+ """
+ if 'name' not in kw:
+ kw['name'] = 'foo'
+ tmp_dir = self.mkdtemp()
+ project_dir = os.path.join(tmp_dir, kw['name'])
+ os.mkdir(project_dir)
+ dist = Distribution(attrs=kw)
+ return project_dir, dist
+
+ def assertIsFile(self, *args):
+ path = os.path.join(*args)
+ dirname = os.path.dirname(path)
+ file = os.path.basename(path)
+ if os.path.isdir(dirname):
+ files = os.listdir(dirname)
+ msg = "%s not found in %s: %s" % (file, dirname, files)
+ assert os.path.isfile(path), msg
+ else:
+ raise AssertionError(
+ '%s not found. %s does not exist' % (file, dirname))
+
+ def assertIsNotFile(self, *args):
+ path = os.path.join(*args)
+ self.assertFalse(os.path.isfile(path), "%r exists" % path)
+
+
+class EnvironRestorer:
+ """TestCase-compatible mixin to restore or delete environment variables.
+
+ The variables to restore (or delete if they were not originally present)
+ must be explicitly listed in self.restore_environ. It's better to be
+ aware of what we're modifying instead of saving and restoring the whole
+ environment.
+ """
+
+ def setUp(self):
+ super(EnvironRestorer, self).setUp()
+ self._saved = []
+ self._added = []
+ for key in self.restore_environ:
+ if key in os.environ:
+ self._saved.append((key, os.environ[key]))
+ else:
+ self._added.append(key)
+
+ def tearDown(self):
+ for key, value in self._saved:
+ os.environ[key] = value
+ for key in self._added:
+ os.environ.pop(key, None)
+ super(EnvironRestorer, self).tearDown()
+
+
+class DummyCommand:
+ """Class to store options for retrieval via set_undefined_options().
+
+ Useful for mocking one dependency command in the tests for another
+ command, see e.g. the dummy build command in test_build_scripts.
+ """
+ # XXX does not work with dist.reinitialize_command, which typechecks
+ # and wants a finalized attribute
+
+ def __init__(self, **kwargs):
+ for kw, val in kwargs.items():
+ setattr(self, kw, val)
+
+ def ensure_finalized(self):
+ pass
+
+
+class TestDistribution(Distribution):
+ """Distribution subclasses that avoids the default search for
+ configuration files.
+
+ The ._config_files attribute must be set before
+ .parse_config_files() is called.
+ """
+
+ def find_config_files(self):
+ return self._config_files
+
+
+class Inputs:
+ """Fakes user inputs."""
+ # TODO document usage
+ # TODO use context manager or something for auto cleanup
+
+ def __init__(self, *answers):
+ self.answers = answers
+ self.index = 0
+
+ def __call__(self, prompt=''):
+ try:
+ return self.answers[self.index]
+ finally:
+ self.index += 1
+
+
+def create_distribution(configfiles=()):
+ """Prepares a distribution with given config files parsed."""
+ d = TestDistribution()
+ d.config.find_config_files = d.find_config_files
+ d._config_files = configfiles
+ d.parse_config_files()
+ d.parse_command_line()
+ return d
+
+
+def use_command(testcase, fullname):
+ """Register command at *fullname* for the duration of a test."""
+ set_command(fullname)
+ # XXX maybe set_command should return the class object
+ name = resolve_name(fullname).get_command_name()
+ # XXX maybe we need a public API to remove commands
+ testcase.addCleanup(_COMMANDS.__delitem__, name)
+
+
+def fake_dec(*args, **kw):
+ """Fake decorator"""
+ def _wrap(func):
+ def __wrap(*args, **kw):
+ return func(*args, **kw)
+ return __wrap
+ return _wrap
+
+
+def copy_xxmodule_c(directory):
+ """Helper for tests that need the xxmodule.c source file.
+
+ Example use:
+
+ def test_compile(self):
+ copy_xxmodule_c(self.tmpdir)
+ self.assertIn('xxmodule.c', os.listdir(self.tmpdir))
+
+ If the source file can be found, it will be copied to *directory*. If not,
+ the test will be skipped. Errors during copy are not caught.
+ """
+ filename = _get_xxmodule_path()
+ if filename is None:
+ raise unittest.SkipTest('cannot find xxmodule.c (test must run in '
+ 'the python build dir)')
+ shutil.copy(filename, directory)
+
+
+def _get_xxmodule_path():
+ srcdir = sysconfig.get_config_var('srcdir')
+ candidates = [
+ # use installed copy if available
+ os.path.join(os.path.dirname(__file__), 'xxmodule.c'),
+ # otherwise try using copy from build directory
+ os.path.join(srcdir, 'Modules', 'xxmodule.c'),
+ ]
+ for path in candidates:
+ if os.path.exists(path):
+ return path
+
+
+def fixup_build_ext(cmd):
+ """Function needed to make build_ext tests pass.
+
+ When Python was built with --enable-shared on Unix, -L. is not enough to
+ find libpython<blah>.so, because regrtest runs in a tempdir, not in the
+ source directory where the .so lives.
+
+ When Python was built with in debug mode on Windows, build_ext commands
+ need their debug attribute set, and it is not done automatically for
+ some reason.
+
+ This function handles both of these things. Example use:
+
+ cmd = build_ext(dist)
+ support.fixup_build_ext(cmd)
+ cmd.ensure_finalized()
+ """
+ if os.name == 'nt':
+ cmd.debug = sys.executable.endswith('_d.exe')
+ elif sysconfig.get_config_var('Py_ENABLE_SHARED'):
+ # To further add to the shared builds fun on Unix, we can't just add
+ # library_dirs to the Extension() instance because that doesn't get
+ # plumbed through to the final compiler command.
+ runshared = sysconfig.get_config_var('RUNSHARED')
+ if runshared is None:
+ cmd.library_dirs = ['.']
+ else:
+ name, equals, value = runshared.partition('=')
+ cmd.library_dirs = value.split(os.pathsep)
+
+
+try:
+ from test.support import skip_unless_symlink
+except ImportError:
+ skip_unless_symlink = unittest.skip(
+ 'requires test.support.skip_unless_symlink')
+
+
+skip_2to3_optimize = unittest.skipIf(sys.flags.optimize,
+ "2to3 doesn't work under -O")
diff --git a/Lib/packaging/tests/test_ccompiler.py b/Lib/packaging/tests/test_ccompiler.py
new file mode 100644
index 0000000..dd4bdd9
--- /dev/null
+++ b/Lib/packaging/tests/test_ccompiler.py
@@ -0,0 +1,15 @@
+"""Tests for distutils.compiler.ccompiler."""
+
+from packaging.compiler import ccompiler
+from packaging.tests import unittest, support
+
+
+class CCompilerTestCase(unittest.TestCase):
+ pass # XXX need some tests on CCompiler
+
+
+def test_suite():
+ return unittest.makeSuite(CCompilerTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_bdist.py b/Lib/packaging/tests/test_command_bdist.py
new file mode 100644
index 0000000..7b2ea01
--- /dev/null
+++ b/Lib/packaging/tests/test_command_bdist.py
@@ -0,0 +1,61 @@
+"""Tests for distutils.command.bdist."""
+import os
+from test.support import captured_stdout
+from packaging.command.bdist import bdist, show_formats
+from packaging.tests import unittest, support
+
+
+class BuildTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ def test_formats(self):
+ # let's create a command and make sure
+ # we can set the format
+ dist = self.create_dist()[1]
+ cmd = bdist(dist)
+ cmd.formats = ['msi']
+ cmd.ensure_finalized()
+ self.assertEqual(cmd.formats, ['msi'])
+
+ # what formats does bdist offer?
+ # XXX hard-coded lists are not the best way to find available bdist_*
+ # commands; we should add a registry
+ formats = ['bztar', 'gztar', 'msi', 'tar', 'wininst', 'zip']
+ found = sorted(cmd.format_command)
+ self.assertEqual(found, formats)
+
+ def test_skip_build(self):
+ # bug #10946: bdist --skip-build should trickle down to subcommands
+ dist = self.create_dist()[1]
+ cmd = bdist(dist)
+ cmd.skip_build = True
+ cmd.ensure_finalized()
+ dist.command_obj['bdist'] = cmd
+
+ names = ['bdist_dumb', 'bdist_wininst']
+ if os.name == 'nt':
+ names.append('bdist_msi')
+
+ for name in names:
+ subcmd = cmd.get_finalized_command(name)
+ self.assertTrue(subcmd.skip_build,
+ '%s should take --skip-build from bdist' % name)
+
+ def test_show_formats(self):
+ with captured_stdout() as stdout:
+ show_formats()
+ stdout = stdout.getvalue()
+
+ # the output should be a header line + one line per format
+ num_formats = len(bdist.format_commands)
+ output = [line for line in stdout.split('\n')
+ if line.strip().startswith('--formats=')]
+ self.assertEqual(len(output), num_formats)
+
+
+def test_suite():
+ return unittest.makeSuite(BuildTestCase)
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_command_bdist_dumb.py b/Lib/packaging/tests/test_command_bdist_dumb.py
new file mode 100644
index 0000000..15cf658
--- /dev/null
+++ b/Lib/packaging/tests/test_command_bdist_dumb.py
@@ -0,0 +1,91 @@
+"""Tests for distutils.command.bdist_dumb."""
+
+import os
+import imp
+import sys
+import zipfile
+import packaging.util
+
+from packaging.dist import Distribution
+from packaging.command.bdist_dumb import bdist_dumb
+from packaging.tests import unittest, support
+from packaging.tests.support import requires_zlib
+
+
+class BuildDumbTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ def setUp(self):
+ super(BuildDumbTestCase, self).setUp()
+ self.old_location = os.getcwd()
+
+ def tearDown(self):
+ os.chdir(self.old_location)
+ packaging.util._path_created.clear()
+ super(BuildDumbTestCase, self).tearDown()
+
+ @requires_zlib
+ def test_simple_built(self):
+
+ # let's create a simple package
+ tmp_dir = self.mkdtemp()
+ pkg_dir = os.path.join(tmp_dir, 'foo')
+ os.mkdir(pkg_dir)
+ self.write_file((pkg_dir, 'foo.py'), '#')
+ self.write_file((pkg_dir, 'MANIFEST.in'), 'include foo.py')
+ self.write_file((pkg_dir, 'README'), '')
+
+ dist = Distribution({'name': 'foo', 'version': '0.1',
+ 'py_modules': ['foo'],
+ 'home_page': 'xxx', 'author': 'xxx',
+ 'author_email': 'xxx'})
+ os.chdir(pkg_dir)
+ cmd = bdist_dumb(dist)
+
+ # so the output is the same no matter
+ # what is the platform
+ cmd.format = 'zip'
+
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # see what we have
+ dist_created = os.listdir(os.path.join(pkg_dir, 'dist'))
+ base = "%s.%s.zip" % (dist.get_fullname(), cmd.plat_name)
+ if os.name == 'os2':
+ base = base.replace(':', '-')
+
+ self.assertEqual(dist_created, [base])
+
+ # now let's check what we have in the zip file
+ with zipfile.ZipFile(os.path.join('dist', base)) as fp:
+ contents = fp.namelist()
+
+ contents = sorted(os.path.basename(fn) for fn in contents)
+ wanted = ['foo.py',
+ 'foo.%s.pyc' % imp.get_tag(),
+ 'METADATA', 'INSTALLER', 'REQUESTED', 'RECORD']
+ self.assertEqual(contents, sorted(wanted))
+
+ def test_finalize_options(self):
+ pkg_dir, dist = self.create_dist()
+ os.chdir(pkg_dir)
+ cmd = bdist_dumb(dist)
+ self.assertEqual(cmd.bdist_dir, None)
+ cmd.finalize_options()
+
+ # bdist_dir is initialized to bdist_base/dumb if not set
+ base = cmd.get_finalized_command('bdist').bdist_base
+ self.assertEqual(cmd.bdist_dir, os.path.join(base, 'dumb'))
+
+ # the format is set to a default value depending on the os.name
+ default = cmd.default_format[os.name]
+ self.assertEqual(cmd.format, default)
+
+
+def test_suite():
+ return unittest.makeSuite(BuildDumbTestCase)
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_command_bdist_msi.py b/Lib/packaging/tests/test_command_bdist_msi.py
new file mode 100644
index 0000000..fded962
--- /dev/null
+++ b/Lib/packaging/tests/test_command_bdist_msi.py
@@ -0,0 +1,25 @@
+"""Tests for distutils.command.bdist_msi."""
+import sys
+
+from packaging.tests import unittest, support
+
+
+class BDistMSITestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ @unittest.skipUnless(sys.platform == "win32", "runs only on win32")
+ def test_minimal(self):
+ # minimal test XXX need more tests
+ from packaging.command.bdist_msi import bdist_msi
+ pkg_pth, dist = self.create_dist()
+ cmd = bdist_msi(dist)
+ cmd.ensure_finalized()
+
+
+def test_suite():
+ return unittest.makeSuite(BDistMSITestCase)
+
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_command_bdist_wininst.py b/Lib/packaging/tests/test_command_bdist_wininst.py
new file mode 100644
index 0000000..09bdaad
--- /dev/null
+++ b/Lib/packaging/tests/test_command_bdist_wininst.py
@@ -0,0 +1,32 @@
+"""Tests for distutils.command.bdist_wininst."""
+
+from packaging.command.bdist_wininst import bdist_wininst
+from packaging.tests import unittest, support
+
+
+class BuildWinInstTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ def test_get_exe_bytes(self):
+
+ # issue5731: command was broken on non-windows platforms
+ # this test makes sure it works now for every platform
+ # let's create a command
+ pkg_pth, dist = self.create_dist()
+ cmd = bdist_wininst(dist)
+ cmd.ensure_finalized()
+
+ # let's run the code that finds the right wininst*.exe file
+ # and make sure it finds it and returns its content
+ # no matter what platform we have
+ exe_file = cmd.get_exe_bytes()
+ self.assertGreater(len(exe_file), 10)
+
+
+def test_suite():
+ return unittest.makeSuite(BuildWinInstTestCase)
+
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_command_build.py b/Lib/packaging/tests/test_command_build.py
new file mode 100644
index 0000000..91fbe42
--- /dev/null
+++ b/Lib/packaging/tests/test_command_build.py
@@ -0,0 +1,55 @@
+"""Tests for distutils.command.build."""
+import os
+import sys
+
+from packaging.command.build import build
+from sysconfig import get_platform
+from packaging.tests import unittest, support
+
+
+class BuildTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ def test_finalize_options(self):
+ pkg_dir, dist = self.create_dist()
+ cmd = build(dist)
+ cmd.finalize_options()
+
+ # if not specified, plat_name gets the current platform
+ self.assertEqual(cmd.plat_name, get_platform())
+
+ # build_purelib is build + lib
+ wanted = os.path.join(cmd.build_base, 'lib')
+ self.assertEqual(cmd.build_purelib, wanted)
+
+ # build_platlib is 'build/lib.platform-x.x[-pydebug]'
+ # examples:
+ # build/lib.macosx-10.3-i386-2.7
+ plat_spec = '.%s-%s' % (cmd.plat_name, sys.version[0:3])
+ if hasattr(sys, 'gettotalrefcount'):
+ self.assertTrue(cmd.build_platlib.endswith('-pydebug'))
+ plat_spec += '-pydebug'
+ wanted = os.path.join(cmd.build_base, 'lib' + plat_spec)
+ self.assertEqual(cmd.build_platlib, wanted)
+
+ # by default, build_lib = build_purelib
+ self.assertEqual(cmd.build_lib, cmd.build_purelib)
+
+ # build_temp is build/temp.<plat>
+ wanted = os.path.join(cmd.build_base, 'temp' + plat_spec)
+ self.assertEqual(cmd.build_temp, wanted)
+
+ # build_scripts is build/scripts-x.x
+ wanted = os.path.join(cmd.build_base, 'scripts-' + sys.version[0:3])
+ self.assertEqual(cmd.build_scripts, wanted)
+
+ # executable is os.path.normpath(sys.executable)
+ self.assertEqual(cmd.executable, os.path.normpath(sys.executable))
+
+
+def test_suite():
+ return unittest.makeSuite(BuildTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_build_clib.py b/Lib/packaging/tests/test_command_build_clib.py
new file mode 100644
index 0000000..a2a8583
--- /dev/null
+++ b/Lib/packaging/tests/test_command_build_clib.py
@@ -0,0 +1,141 @@
+"""Tests for distutils.command.build_clib."""
+import os
+import sys
+
+from packaging.util import find_executable
+from packaging.command.build_clib import build_clib
+from packaging.errors import PackagingSetupError
+from packaging.tests import unittest, support
+
+
+class BuildCLibTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ def test_check_library_dist(self):
+ pkg_dir, dist = self.create_dist()
+ cmd = build_clib(dist)
+
+ # 'libraries' option must be a list
+ self.assertRaises(PackagingSetupError, cmd.check_library_list, 'foo')
+
+ # each element of 'libraries' must a 2-tuple
+ self.assertRaises(PackagingSetupError, cmd.check_library_list,
+ ['foo1', 'foo2'])
+
+ # first element of each tuple in 'libraries'
+ # must be a string (the library name)
+ self.assertRaises(PackagingSetupError, cmd.check_library_list,
+ [(1, 'foo1'), ('name', 'foo2')])
+
+ # library name may not contain directory separators
+ self.assertRaises(PackagingSetupError, cmd.check_library_list,
+ [('name', 'foo1'),
+ ('another/name', 'foo2')])
+
+ # second element of each tuple must be a dictionary (build info)
+ self.assertRaises(PackagingSetupError, cmd.check_library_list,
+ [('name', {}),
+ ('another', 'foo2')])
+
+ # those work
+ libs = [('name', {}), ('name', {'ok': 'good'})]
+ cmd.check_library_list(libs)
+
+ def test_get_source_files(self):
+ pkg_dir, dist = self.create_dist()
+ cmd = build_clib(dist)
+
+ # "in 'libraries' option 'sources' must be present and must be
+ # a list of source filenames
+ cmd.libraries = [('name', {})]
+ self.assertRaises(PackagingSetupError, cmd.get_source_files)
+
+ cmd.libraries = [('name', {'sources': 1})]
+ self.assertRaises(PackagingSetupError, cmd.get_source_files)
+
+ cmd.libraries = [('name', {'sources': ['a', 'b']})]
+ self.assertEqual(cmd.get_source_files(), ['a', 'b'])
+
+ cmd.libraries = [('name', {'sources': ('a', 'b')})]
+ self.assertEqual(cmd.get_source_files(), ['a', 'b'])
+
+ cmd.libraries = [('name', {'sources': ('a', 'b')}),
+ ('name2', {'sources': ['c', 'd']})]
+ self.assertEqual(cmd.get_source_files(), ['a', 'b', 'c', 'd'])
+
+ def test_build_libraries(self):
+ pkg_dir, dist = self.create_dist()
+ cmd = build_clib(dist)
+
+ class FakeCompiler:
+ def compile(*args, **kw):
+ pass
+ create_static_lib = compile
+
+ cmd.compiler = FakeCompiler()
+
+ # build_libraries is also doing a bit of type checking
+ lib = [('name', {'sources': 'notvalid'})]
+ self.assertRaises(PackagingSetupError, cmd.build_libraries, lib)
+
+ lib = [('name', {'sources': []})]
+ cmd.build_libraries(lib)
+
+ lib = [('name', {'sources': ()})]
+ cmd.build_libraries(lib)
+
+ def test_finalize_options(self):
+ pkg_dir, dist = self.create_dist()
+ cmd = build_clib(dist)
+
+ cmd.include_dirs = 'one-dir'
+ cmd.finalize_options()
+ self.assertEqual(cmd.include_dirs, ['one-dir'])
+
+ cmd.include_dirs = None
+ cmd.finalize_options()
+ self.assertEqual(cmd.include_dirs, [])
+
+ cmd.distribution.libraries = 'WONTWORK'
+ self.assertRaises(PackagingSetupError, cmd.finalize_options)
+
+ @unittest.skipIf(sys.platform == 'win32', 'disabled on win32')
+ def test_run(self):
+ pkg_dir, dist = self.create_dist()
+ cmd = build_clib(dist)
+
+ foo_c = os.path.join(pkg_dir, 'foo.c')
+ self.write_file(foo_c, 'int main(void) { return 1;}\n')
+ cmd.libraries = [('foo', {'sources': [foo_c]})]
+
+ build_temp = os.path.join(pkg_dir, 'build')
+ os.mkdir(build_temp)
+ cmd.build_temp = build_temp
+ cmd.build_clib = build_temp
+
+ # before we run the command, we want to make sure
+ # all commands are present on the system
+ # by creating a compiler and checking its executables
+ from packaging.compiler import new_compiler, customize_compiler
+
+ compiler = new_compiler()
+ customize_compiler(compiler)
+ for ccmd in compiler.executables.values():
+ if ccmd is None:
+ continue
+ if find_executable(ccmd[0]) is None:
+ raise unittest.SkipTest("can't test")
+
+ # this should work
+ cmd.run()
+
+ # let's check the result
+ self.assertIn('libfoo.a', os.listdir(build_temp))
+
+
+def test_suite():
+ return unittest.makeSuite(BuildCLibTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_build_ext.py b/Lib/packaging/tests/test_command_build_ext.py
new file mode 100644
index 0000000..4883f38
--- /dev/null
+++ b/Lib/packaging/tests/test_command_build_ext.py
@@ -0,0 +1,393 @@
+import os
+import sys
+import site
+import sysconfig
+import textwrap
+from packaging.dist import Distribution
+from packaging.errors import (UnknownFileError, CompileError,
+ PackagingPlatformError)
+from packaging.command.build_ext import build_ext
+from packaging.compiler.extension import Extension
+
+from test.script_helper import assert_python_ok
+from packaging.tests import support, unittest
+
+
+class BuildExtTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+ def setUp(self):
+ super(BuildExtTestCase, self).setUp()
+ self.tmp_dir = self.mkdtemp()
+ self.old_user_base = site.USER_BASE
+ site.USER_BASE = self.mkdtemp()
+
+ def tearDown(self):
+ site.USER_BASE = self.old_user_base
+ super(BuildExtTestCase, self).tearDown()
+
+ def test_build_ext(self):
+ support.copy_xxmodule_c(self.tmp_dir)
+ xx_c = os.path.join(self.tmp_dir, 'xxmodule.c')
+ xx_ext = Extension('xx', [xx_c])
+ dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]})
+ dist.package_dir = self.tmp_dir
+ cmd = build_ext(dist)
+ support.fixup_build_ext(cmd)
+ cmd.build_lib = self.tmp_dir
+ cmd.build_temp = self.tmp_dir
+ cmd.ensure_finalized()
+ cmd.run()
+
+ code = textwrap.dedent("""\
+ import sys
+ sys.path.insert(0, %r)
+
+ import xx
+
+ for attr in ('error', 'foo', 'new', 'roj'):
+ assert hasattr(xx, attr)
+
+ assert xx.foo(2, 5) == 7
+ assert xx.foo(13, 15) == 28
+ assert xx.new().demo() is None
+ doc = 'This is a template module just for instruction.'
+ assert xx.__doc__ == doc
+ assert isinstance(xx.Null(), xx.Null)
+ assert isinstance(xx.Str(), xx.Str)
+ """)
+ code = code % self.tmp_dir
+ assert_python_ok('-c', code)
+
+ def test_solaris_enable_shared(self):
+ dist = Distribution({'name': 'xx'})
+ cmd = build_ext(dist)
+ old = sys.platform
+
+ sys.platform = 'sunos' # fooling finalize_options
+
+ old_var = sysconfig.get_config_var('Py_ENABLE_SHARED')
+ sysconfig._CONFIG_VARS['Py_ENABLE_SHARED'] = 1
+ try:
+ cmd.ensure_finalized()
+ finally:
+ sys.platform = old
+ if old_var is None:
+ del sysconfig._CONFIG_VARS['Py_ENABLE_SHARED']
+ else:
+ sysconfig._CONFIG_VARS['Py_ENABLE_SHARED'] = old_var
+
+ # make sure we get some library dirs under solaris
+ self.assertGreater(len(cmd.library_dirs), 0)
+
+ def test_user_site(self):
+ dist = Distribution({'name': 'xx'})
+ cmd = build_ext(dist)
+
+ # making sure the user option is there
+ options = [name for name, short, label in
+ cmd.user_options]
+ self.assertIn('user', options)
+
+ # setting a value
+ cmd.user = True
+
+ # setting user based lib and include
+ lib = os.path.join(site.USER_BASE, 'lib')
+ incl = os.path.join(site.USER_BASE, 'include')
+ os.mkdir(lib)
+ os.mkdir(incl)
+
+ # let's run finalize
+ cmd.ensure_finalized()
+
+ # see if include_dirs and library_dirs
+ # were set
+ self.assertIn(lib, cmd.library_dirs)
+ self.assertIn(lib, cmd.rpath)
+ self.assertIn(incl, cmd.include_dirs)
+
+ def test_optional_extension(self):
+
+ # this extension will fail, but let's ignore this failure
+ # with the optional argument.
+ modules = [Extension('foo', ['xxx'], optional=False)]
+ dist = Distribution({'name': 'xx', 'ext_modules': modules})
+ cmd = build_ext(dist)
+ cmd.ensure_finalized()
+ self.assertRaises((UnknownFileError, CompileError),
+ cmd.run) # should raise an error
+
+ modules = [Extension('foo', ['xxx'], optional=True)]
+ dist = Distribution({'name': 'xx', 'ext_modules': modules})
+ cmd = build_ext(dist)
+ cmd.ensure_finalized()
+ cmd.run() # should pass
+
+ def test_finalize_options(self):
+ # Make sure Python's include directories (for Python.h, pyconfig.h,
+ # etc.) are in the include search path.
+ modules = [Extension('foo', ['xxx'], optional=False)]
+ dist = Distribution({'name': 'xx', 'ext_modules': modules})
+ cmd = build_ext(dist)
+ cmd.finalize_options()
+
+ py_include = sysconfig.get_path('include')
+ self.assertIn(py_include, cmd.include_dirs)
+
+ plat_py_include = sysconfig.get_path('platinclude')
+ self.assertIn(plat_py_include, cmd.include_dirs)
+
+ # make sure cmd.libraries is turned into a list
+ # if it's a string
+ cmd = build_ext(dist)
+ cmd.libraries = 'my_lib'
+ cmd.finalize_options()
+ self.assertEqual(cmd.libraries, ['my_lib'])
+
+ # make sure cmd.library_dirs is turned into a list
+ # if it's a string
+ cmd = build_ext(dist)
+ cmd.library_dirs = 'my_lib_dir'
+ cmd.finalize_options()
+ self.assertIn('my_lib_dir', cmd.library_dirs)
+
+ # make sure rpath is turned into a list
+ # if it's a list of os.pathsep's paths
+ cmd = build_ext(dist)
+ cmd.rpath = os.pathsep.join(['one', 'two'])
+ cmd.finalize_options()
+ self.assertEqual(cmd.rpath, ['one', 'two'])
+
+ # XXX more tests to perform for win32
+
+ # make sure define is turned into 2-tuples
+ # strings if they are ','-separated strings
+ cmd = build_ext(dist)
+ cmd.define = 'one,two'
+ cmd.finalize_options()
+ self.assertEqual(cmd.define, [('one', '1'), ('two', '1')])
+
+ # make sure undef is turned into a list of
+ # strings if they are ','-separated strings
+ cmd = build_ext(dist)
+ cmd.undef = 'one,two'
+ cmd.finalize_options()
+ self.assertEqual(cmd.undef, ['one', 'two'])
+
+ # make sure swig_opts is turned into a list
+ cmd = build_ext(dist)
+ cmd.swig_opts = None
+ cmd.finalize_options()
+ self.assertEqual(cmd.swig_opts, [])
+
+ cmd = build_ext(dist)
+ cmd.swig_opts = '1 2'
+ cmd.finalize_options()
+ self.assertEqual(cmd.swig_opts, ['1', '2'])
+
+ def test_get_source_files(self):
+ modules = [Extension('foo', ['xxx'], optional=False)]
+ dist = Distribution({'name': 'xx', 'ext_modules': modules})
+ cmd = build_ext(dist)
+ cmd.ensure_finalized()
+ self.assertEqual(cmd.get_source_files(), ['xxx'])
+
+ def test_compiler_option(self):
+ # cmd.compiler is an option and
+ # should not be overriden by a compiler instance
+ # when the command is run
+ dist = Distribution()
+ cmd = build_ext(dist)
+ cmd.compiler = 'unix'
+ cmd.ensure_finalized()
+ cmd.run()
+ self.assertEqual(cmd.compiler, 'unix')
+
+ def test_get_outputs(self):
+ tmp_dir = self.mkdtemp()
+ c_file = os.path.join(tmp_dir, 'foo.c')
+ self.write_file(c_file, 'void PyInit_foo(void) {}\n')
+ ext = Extension('foo', [c_file], optional=False)
+ dist = Distribution({'name': 'xx',
+ 'ext_modules': [ext]})
+ cmd = build_ext(dist)
+ support.fixup_build_ext(cmd)
+ cmd.ensure_finalized()
+ self.assertEqual(len(cmd.get_outputs()), 1)
+
+ cmd.build_lib = os.path.join(self.tmp_dir, 'build')
+ cmd.build_temp = os.path.join(self.tmp_dir, 'tempt')
+
+ # issue #5977 : distutils build_ext.get_outputs
+ # returns wrong result with --inplace
+ other_tmp_dir = os.path.realpath(self.mkdtemp())
+ old_wd = os.getcwd()
+ os.chdir(other_tmp_dir)
+ try:
+ cmd.inplace = True
+ cmd.run()
+ so_file = cmd.get_outputs()[0]
+ finally:
+ os.chdir(old_wd)
+ self.assertTrue(os.path.exists(so_file))
+ so_ext = sysconfig.get_config_var('SO')
+ self.assertTrue(so_file.endswith(so_ext))
+ so_dir = os.path.dirname(so_file)
+ self.assertEqual(so_dir, other_tmp_dir)
+
+ cmd.inplace = False
+ cmd.run()
+ so_file = cmd.get_outputs()[0]
+ self.assertTrue(os.path.exists(so_file))
+ self.assertTrue(so_file.endswith(so_ext))
+ so_dir = os.path.dirname(so_file)
+ self.assertEqual(so_dir, cmd.build_lib)
+
+ # inplace = False, cmd.package = 'bar'
+ build_py = cmd.get_finalized_command('build_py')
+ build_py.package_dir = 'bar'
+ path = cmd.get_ext_fullpath('foo')
+ # checking that the last directory is the build_dir
+ path = os.path.split(path)[0]
+ self.assertEqual(path, cmd.build_lib)
+
+ # inplace = True, cmd.package = 'bar'
+ cmd.inplace = True
+ other_tmp_dir = os.path.realpath(self.mkdtemp())
+ old_wd = os.getcwd()
+ os.chdir(other_tmp_dir)
+ try:
+ path = cmd.get_ext_fullpath('foo')
+ finally:
+ os.chdir(old_wd)
+ # checking that the last directory is bar
+ path = os.path.split(path)[0]
+ lastdir = os.path.split(path)[-1]
+ self.assertEqual(lastdir, 'bar')
+
+ def test_ext_fullpath(self):
+ ext = sysconfig.get_config_vars()['SO']
+ # building lxml.etree inplace
+ #etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c')
+ #etree_ext = Extension('lxml.etree', [etree_c])
+ #dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]})
+ dist = Distribution()
+ cmd = build_ext(dist)
+ cmd.inplace = True
+ cmd.distribution.package_dir = 'src'
+ cmd.distribution.packages = ['lxml', 'lxml.html']
+ curdir = os.getcwd()
+ wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext)
+ path = cmd.get_ext_fullpath('lxml.etree')
+ self.assertEqual(wanted, path)
+
+ # building lxml.etree not inplace
+ cmd.inplace = False
+ cmd.build_lib = os.path.join(curdir, 'tmpdir')
+ wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree' + ext)
+ path = cmd.get_ext_fullpath('lxml.etree')
+ self.assertEqual(wanted, path)
+
+ # building twisted.runner.portmap not inplace
+ build_py = cmd.get_finalized_command('build_py')
+ build_py.package_dir = None
+ cmd.distribution.packages = ['twisted', 'twisted.runner.portmap']
+ path = cmd.get_ext_fullpath('twisted.runner.portmap')
+ wanted = os.path.join(curdir, 'tmpdir', 'twisted', 'runner',
+ 'portmap' + ext)
+ self.assertEqual(wanted, path)
+
+ # building twisted.runner.portmap inplace
+ cmd.inplace = True
+ path = cmd.get_ext_fullpath('twisted.runner.portmap')
+ wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext)
+ self.assertEqual(wanted, path)
+
+ @unittest.skipUnless(sys.platform == 'darwin',
+ 'test only relevant for Mac OS X')
+ def test_deployment_target_default(self):
+ # Issue 9516: Test that, in the absence of the environment variable,
+ # an extension module is compiled with the same deployment target as
+ # the interpreter.
+ self._try_compile_deployment_target('==', None)
+
+ @unittest.skipUnless(sys.platform == 'darwin',
+ 'test only relevant for Mac OS X')
+ def test_deployment_target_too_low(self):
+ # Issue 9516: Test that an extension module is not allowed to be
+ # compiled with a deployment target less than that of the interpreter.
+ self.assertRaises(PackagingPlatformError,
+ self._try_compile_deployment_target, '>', '10.1')
+
+ @unittest.skipUnless(sys.platform == 'darwin',
+ 'test only relevant for Mac OS X')
+ def test_deployment_target_higher_ok(self):
+ # Issue 9516: Test that an extension module can be compiled with a
+ # deployment target higher than that of the interpreter: the ext
+ # module may depend on some newer OS feature.
+ deptarget = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET')
+ if deptarget:
+ # increment the minor version number (i.e. 10.6 -> 10.7)
+ deptarget = [int(x) for x in deptarget.split('.')]
+ deptarget[-1] += 1
+ deptarget = '.'.join(str(i) for i in deptarget)
+ self._try_compile_deployment_target('<', deptarget)
+
+ def _try_compile_deployment_target(self, operator, target):
+ orig_environ = os.environ
+ os.environ = orig_environ.copy()
+ self.addCleanup(setattr, os, 'environ', orig_environ)
+
+ if target is None:
+ if os.environ.get('MACOSX_DEPLOYMENT_TARGET'):
+ del os.environ['MACOSX_DEPLOYMENT_TARGET']
+ else:
+ os.environ['MACOSX_DEPLOYMENT_TARGET'] = target
+
+ deptarget_c = os.path.join(self.tmp_dir, 'deptargetmodule.c')
+
+ with open(deptarget_c, 'w') as fp:
+ fp.write(textwrap.dedent('''\
+ #include <AvailabilityMacros.h>
+
+ int dummy;
+
+ #if TARGET %s MAC_OS_X_VERSION_MIN_REQUIRED
+ #else
+ #error "Unexpected target"
+ #endif
+
+ ''' % operator))
+
+ # get the deployment target that the interpreter was built with
+ target = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET')
+ target = tuple(map(int, target.split('.')))
+ target = '%02d%01d0' % target
+
+ deptarget_ext = Extension(
+ 'deptarget',
+ [deptarget_c],
+ extra_compile_args=['-DTARGET=%s' % (target,)],
+ )
+ dist = Distribution({
+ 'name': 'deptarget',
+ 'ext_modules': [deptarget_ext],
+ })
+ dist.package_dir = self.tmp_dir
+ cmd = build_ext(dist)
+ cmd.build_lib = self.tmp_dir
+ cmd.build_temp = self.tmp_dir
+
+ try:
+ cmd.ensure_finalized()
+ cmd.run()
+ except CompileError:
+ self.fail("Wrong deployment target during compilation")
+
+
+def test_suite():
+ return unittest.makeSuite(BuildExtTestCase)
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_command_build_py.py b/Lib/packaging/tests/test_command_build_py.py
new file mode 100644
index 0000000..f7f26da
--- /dev/null
+++ b/Lib/packaging/tests/test_command_build_py.py
@@ -0,0 +1,136 @@
+"""Tests for distutils.command.build_py."""
+
+import os
+import sys
+import imp
+
+from packaging.command.build_py import build_py
+from packaging.dist import Distribution
+from packaging.errors import PackagingFileError
+
+from packaging.tests import unittest, support
+
+
+class BuildPyTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ def test_package_data(self):
+ sources = self.mkdtemp()
+ pkg_dir = os.path.join(sources, 'pkg')
+ os.mkdir(pkg_dir)
+ f = open(os.path.join(pkg_dir, "__init__.py"), "w")
+ try:
+ f.write("# Pretend this is a package.")
+ finally:
+ f.close()
+ f = open(os.path.join(pkg_dir, "README.txt"), "w")
+ try:
+ f.write("Info about this package")
+ finally:
+ f.close()
+
+ destination = self.mkdtemp()
+
+ dist = Distribution({"packages": ["pkg"],
+ "package_dir": sources})
+
+ dist.command_obj["build"] = support.DummyCommand(
+ force=False,
+ build_lib=destination,
+ use_2to3_fixers=None,
+ convert_2to3_doctests=None,
+ use_2to3=False)
+ dist.packages = ["pkg"]
+ dist.package_data = {"pkg": ["README.txt"]}
+ dist.package_dir = sources
+
+ cmd = build_py(dist)
+ cmd.compile = True
+ cmd.ensure_finalized()
+ self.assertEqual(cmd.package_data, dist.package_data)
+
+ cmd.run()
+
+ # This makes sure the list of outputs includes byte-compiled
+ # files for Python modules but not for package data files
+ # (there shouldn't *be* byte-code files for those!).
+ #
+ self.assertEqual(len(cmd.get_outputs()), 3)
+ pkgdest = os.path.join(destination, "pkg")
+ files = os.listdir(pkgdest)
+ pycache_dir = os.path.join(pkgdest, "__pycache__")
+ self.assertIn("__init__.py", files)
+ self.assertIn("README.txt", files)
+ pyc_files = os.listdir(pycache_dir)
+ self.assertIn("__init__.%s.pyc" % imp.get_tag(), pyc_files)
+
+ def test_empty_package_dir(self):
+ # See SF 1668596/1720897.
+ # create the distribution files.
+ sources = self.mkdtemp()
+ pkg = os.path.join(sources, 'pkg')
+ os.mkdir(pkg)
+ open(os.path.join(pkg, "__init__.py"), "wb").close()
+ testdir = os.path.join(pkg, "doc")
+ os.mkdir(testdir)
+ open(os.path.join(testdir, "testfile"), "wb").close()
+
+ os.chdir(sources)
+ dist = Distribution({"packages": ["pkg"],
+ "package_dir": sources,
+ "package_data": {"pkg": ["doc/*"]}})
+ dist.script_args = ["build"]
+ dist.parse_command_line()
+
+ try:
+ dist.run_commands()
+ except PackagingFileError:
+ self.fail("failed package_data test when package_dir is ''")
+
+ def test_byte_compile(self):
+ project_dir, dist = self.create_dist(py_modules=['boiledeggs'])
+ os.chdir(project_dir)
+ self.write_file('boiledeggs.py', 'import antigravity')
+ cmd = build_py(dist)
+ cmd.compile = True
+ cmd.build_lib = 'here'
+ cmd.finalize_options()
+ cmd.run()
+
+ found = os.listdir(cmd.build_lib)
+ self.assertEqual(sorted(found), ['__pycache__', 'boiledeggs.py'])
+ found = os.listdir(os.path.join(cmd.build_lib, '__pycache__'))
+ self.assertEqual(found, ['boiledeggs.%s.pyc' % imp.get_tag()])
+
+ def test_byte_compile_optimized(self):
+ project_dir, dist = self.create_dist(py_modules=['boiledeggs'])
+ os.chdir(project_dir)
+ self.write_file('boiledeggs.py', 'import antigravity')
+ cmd = build_py(dist)
+ cmd.compile = True
+ cmd.optimize = 1
+ cmd.build_lib = 'here'
+ cmd.finalize_options()
+ cmd.run()
+
+ found = os.listdir(cmd.build_lib)
+ self.assertEqual(sorted(found), ['__pycache__', 'boiledeggs.py'])
+ found = os.listdir(os.path.join(cmd.build_lib, '__pycache__'))
+ self.assertEqual(sorted(found), ['boiledeggs.%s.pyc' % imp.get_tag(),
+ 'boiledeggs.%s.pyo' % imp.get_tag()])
+
+ def test_byte_compile_under_B(self):
+ # make sure byte compilation works under -B (dont_write_bytecode)
+ self.addCleanup(setattr, sys, 'dont_write_bytecode',
+ sys.dont_write_bytecode)
+ sys.dont_write_bytecode = True
+ self.test_byte_compile()
+ self.test_byte_compile_optimized()
+
+
+def test_suite():
+ return unittest.makeSuite(BuildPyTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_build_scripts.py b/Lib/packaging/tests/test_command_build_scripts.py
new file mode 100644
index 0000000..fd3ac24
--- /dev/null
+++ b/Lib/packaging/tests/test_command_build_scripts.py
@@ -0,0 +1,109 @@
+"""Tests for distutils.command.build_scripts."""
+
+import os
+import sys
+import sysconfig
+from packaging.dist import Distribution
+from packaging.command.build_scripts import build_scripts
+
+from packaging.tests import unittest, support
+
+
+class BuildScriptsTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ def test_default_settings(self):
+ cmd = self.get_build_scripts_cmd("/foo/bar", [])
+ self.assertFalse(cmd.force)
+ self.assertIs(cmd.build_dir, None)
+
+ cmd.finalize_options()
+
+ self.assertTrue(cmd.force)
+ self.assertEqual(cmd.build_dir, "/foo/bar")
+
+ def test_build(self):
+ source = self.mkdtemp()
+ target = self.mkdtemp()
+ expected = self.write_sample_scripts(source)
+
+ cmd = self.get_build_scripts_cmd(target,
+ [os.path.join(source, fn)
+ for fn in expected])
+ cmd.finalize_options()
+ cmd.run()
+
+ built = os.listdir(target)
+ for name in expected:
+ self.assertIn(name, built)
+
+ def get_build_scripts_cmd(self, target, scripts):
+ dist = Distribution()
+ dist.scripts = scripts
+ dist.command_obj["build"] = support.DummyCommand(
+ build_scripts=target,
+ force=True,
+ executable=sys.executable,
+ use_2to3=False,
+ use_2to3_fixers=None,
+ convert_2to3_doctests=None
+ )
+ return build_scripts(dist)
+
+ def write_sample_scripts(self, dir):
+ expected = []
+ expected.append("script1.py")
+ self.write_script(dir, "script1.py",
+ ("#! /usr/bin/env python2.3\n"
+ "# bogus script w/ Python sh-bang\n"
+ "pass\n"))
+ expected.append("script2.py")
+ self.write_script(dir, "script2.py",
+ ("#!/usr/bin/python\n"
+ "# bogus script w/ Python sh-bang\n"
+ "pass\n"))
+ expected.append("shell.sh")
+ self.write_script(dir, "shell.sh",
+ ("#!/bin/sh\n"
+ "# bogus shell script w/ sh-bang\n"
+ "exit 0\n"))
+ return expected
+
+ def write_script(self, dir, name, text):
+ with open(os.path.join(dir, name), "w") as f:
+ f.write(text)
+
+ def test_version_int(self):
+ source = self.mkdtemp()
+ target = self.mkdtemp()
+ expected = self.write_sample_scripts(source)
+
+
+ cmd = self.get_build_scripts_cmd(target,
+ [os.path.join(source, fn)
+ for fn in expected])
+ cmd.finalize_options()
+
+ # http://bugs.python.org/issue4524
+ #
+ # On linux-g++-32 with command line `./configure --enable-ipv6
+ # --with-suffix=3`, python is compiled okay but the build scripts
+ # failed when writing the name of the executable
+ old = sysconfig.get_config_vars().get('VERSION')
+ sysconfig._CONFIG_VARS['VERSION'] = 4
+ try:
+ cmd.run()
+ finally:
+ if old is not None:
+ sysconfig._CONFIG_VARS['VERSION'] = old
+
+ built = os.listdir(target)
+ for name in expected:
+ self.assertIn(name, built)
+
+def test_suite():
+ return unittest.makeSuite(BuildScriptsTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_check.py b/Lib/packaging/tests/test_command_check.py
new file mode 100644
index 0000000..0b91050
--- /dev/null
+++ b/Lib/packaging/tests/test_command_check.py
@@ -0,0 +1,161 @@
+"""Tests for distutils.command.check."""
+
+from packaging.command.check import check
+from packaging.metadata import _HAS_DOCUTILS
+from packaging.errors import PackagingSetupError, MetadataMissingError
+from packaging.tests import unittest, support
+
+
+class CheckTestCase(support.LoggingCatcher,
+ support.TempdirManager,
+ unittest.TestCase):
+
+ def _run(self, metadata=None, **options):
+ if metadata is None:
+ metadata = {'name': 'xxx', 'version': '1.2'}
+ pkg_info, dist = self.create_dist(**metadata)
+ cmd = check(dist)
+ cmd.initialize_options()
+ for name, value in options.items():
+ setattr(cmd, name, value)
+ cmd.ensure_finalized()
+ cmd.run()
+ return cmd
+
+ def test_check_metadata(self):
+ # let's run the command with no metadata at all
+ # by default, check is checking the metadata
+ # should have some warnings
+ self._run()
+ # trick: using assertNotEqual with an empty list will give us a more
+ # useful error message than assertGreater(.., 0) when the code change
+ # and the test fails
+ self.assertNotEqual(self.get_logs(), [])
+
+ # now let's add the required fields
+ # and run it again, to make sure we don't get
+ # any warning anymore
+ metadata = {'home_page': 'xxx', 'author': 'xxx',
+ 'author_email': 'xxx',
+ 'name': 'xxx', 'version': '4.2',
+ }
+ self._run(metadata)
+ self.assertEqual(self.get_logs(), [])
+
+ # now with the strict mode, we should
+ # get an error if there are missing metadata
+ self.assertRaises(MetadataMissingError, self._run, {}, **{'strict': 1})
+ self.assertRaises(PackagingSetupError, self._run,
+ {'name': 'xxx', 'version': 'xxx'}, **{'strict': 1})
+
+ # clear warnings from the previous calls
+ self.loghandler.flush()
+
+ # and of course, no error when all metadata fields are present
+ self._run(metadata, strict=True)
+ self.assertEqual(self.get_logs(), [])
+
+ # now a test with non-ASCII characters
+ metadata = {'home_page': 'xxx', 'author': '\u00c9ric',
+ 'author_email': 'xxx', 'name': 'xxx',
+ 'version': '1.2',
+ 'summary': 'Something about esszet \u00df',
+ 'description': 'More things about esszet \u00df'}
+ self._run(metadata)
+ self.assertEqual(self.get_logs(), [])
+
+ def test_check_metadata_1_2(self):
+ # let's run the command with no metadata at all
+ # by default, check is checking the metadata
+ # should have some warnings
+ self._run()
+ self.assertNotEqual(self.get_logs(), [])
+
+ # now let's add the required fields and run it again, to make sure we
+ # don't get any warning anymore let's use requires_python as a marker
+ # to enforce Metadata-Version 1.2
+ metadata = {'home_page': 'xxx', 'author': 'xxx',
+ 'author_email': 'xxx',
+ 'name': 'xxx', 'version': '4.2',
+ 'requires_python': '2.4',
+ }
+ self._run(metadata)
+ self.assertEqual(self.get_logs(), [])
+
+ # now with the strict mode, we should
+ # get an error if there are missing metadata
+ self.assertRaises(MetadataMissingError, self._run, {}, **{'strict': 1})
+ self.assertRaises(PackagingSetupError, self._run,
+ {'name': 'xxx', 'version': 'xxx'}, **{'strict': 1})
+
+ # complain about version format
+ metadata['version'] = 'xxx'
+ self.assertRaises(PackagingSetupError, self._run, metadata,
+ **{'strict': 1})
+
+ # clear warnings from the previous calls
+ self.loghandler.flush()
+
+ # now with correct version format again
+ metadata['version'] = '4.2'
+ self._run(metadata, strict=True)
+ self.assertEqual(self.get_logs(), [])
+
+ @unittest.skipUnless(_HAS_DOCUTILS, "requires docutils")
+ def test_check_restructuredtext(self):
+ # let's see if it detects broken rest in description
+ broken_rest = 'title\n===\n\ntest'
+ pkg_info, dist = self.create_dist(description=broken_rest)
+ cmd = check(dist)
+ cmd.check_restructuredtext()
+ self.assertEqual(len(self.get_logs()), 1)
+
+ # let's see if we have an error with strict=1
+ metadata = {'home_page': 'xxx', 'author': 'xxx',
+ 'author_email': 'xxx',
+ 'name': 'xxx', 'version': '1.2',
+ 'description': broken_rest}
+ self.assertRaises(PackagingSetupError, self._run, metadata,
+ strict=True, all=True)
+ self.loghandler.flush()
+
+ # and non-broken rest, including a non-ASCII character to test #12114
+ dist = self.create_dist(description='title\n=====\n\ntest \u00df')[1]
+ cmd = check(dist)
+ cmd.check_restructuredtext()
+ self.assertEqual(self.get_logs(), [])
+
+ def test_check_all(self):
+ self.assertRaises(PackagingSetupError, self._run,
+ {'name': 'xxx', 'version': 'xxx'}, **{'strict': 1,
+ 'all': 1})
+ self.assertRaises(MetadataMissingError, self._run,
+ {}, **{'strict': 1,
+ 'all': 1})
+
+ def test_check_hooks(self):
+ pkg_info, dist = self.create_dist()
+ dist.command_options['install_dist'] = {
+ 'pre_hook': ('file', {"a": 'some.nonextistant.hook.ghrrraarrhll'}),
+ }
+ cmd = check(dist)
+ cmd.check_hooks_resolvable()
+ self.assertEqual(len(self.get_logs()), 1)
+
+ def test_warn(self):
+ _, dist = self.create_dist()
+ cmd = check(dist)
+ self.assertEqual(self.get_logs(), [])
+ cmd.warn('hello')
+ self.assertEqual(self.get_logs(), ['check: hello'])
+ cmd.warn('hello %s', 'world')
+ self.assertEqual(self.get_logs(), ['check: hello world'])
+ cmd.warn('hello %s %s', 'beautiful', 'world')
+ self.assertEqual(self.get_logs(), ['check: hello beautiful world'])
+
+
+def test_suite():
+ return unittest.makeSuite(CheckTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_clean.py b/Lib/packaging/tests/test_command_clean.py
new file mode 100644
index 0000000..a78c3a7
--- /dev/null
+++ b/Lib/packaging/tests/test_command_clean.py
@@ -0,0 +1,46 @@
+"""Tests for distutils.command.clean."""
+import os
+
+from packaging.command.clean import clean
+from packaging.tests import unittest, support
+
+
+class cleanTestCase(support.TempdirManager, support.LoggingCatcher,
+ unittest.TestCase):
+
+ def test_simple_run(self):
+ pkg_dir, dist = self.create_dist()
+ cmd = clean(dist)
+
+ # let's add some elements clean should remove
+ dirs = [(d, os.path.join(pkg_dir, d))
+ for d in ('build_temp', 'build_lib', 'bdist_base',
+ 'build_scripts', 'build_base')]
+
+ for name, path in dirs:
+ os.mkdir(path)
+ setattr(cmd, name, path)
+ if name == 'build_base':
+ continue
+ for f in ('one', 'two', 'three'):
+ self.write_file((path, f))
+
+ # let's run the command
+ cmd.all = True
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # make sure the files where removed
+ for name, path in dirs:
+ self.assertFalse(os.path.exists(path),
+ '%r was not removed' % path)
+
+ # let's run the command again (should spit warnings but succeed)
+ cmd.run()
+
+
+def test_suite():
+ return unittest.makeSuite(cleanTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_cmd.py b/Lib/packaging/tests/test_command_cmd.py
new file mode 100644
index 0000000..6d00ec3
--- /dev/null
+++ b/Lib/packaging/tests/test_command_cmd.py
@@ -0,0 +1,102 @@
+"""Tests for distutils.cmd."""
+import os
+import logging
+
+from packaging.command.cmd import Command
+from packaging.dist import Distribution
+from packaging.errors import PackagingOptionError
+from packaging.tests import support, unittest
+
+
+class MyCmd(Command):
+ def initialize_options(self):
+ pass
+
+
+class CommandTestCase(support.LoggingCatcher,
+ unittest.TestCase):
+
+ def setUp(self):
+ super(CommandTestCase, self).setUp()
+ dist = Distribution()
+ self.cmd = MyCmd(dist)
+
+ def test_make_file(self):
+ cmd = self.cmd
+
+ # making sure it raises when infiles is not a string or a list/tuple
+ self.assertRaises(TypeError, cmd.make_file,
+ infiles=1, outfile='', func='func', args=())
+
+ # making sure execute gets called properly
+ def _execute(func, args, exec_msg, level):
+ self.assertEqual(exec_msg, 'generating out from in')
+ cmd.force = True
+ cmd.execute = _execute
+ cmd.make_file(infiles='in', outfile='out', func='func', args=())
+
+ def test_dump_options(self):
+ cmd = self.cmd
+ cmd.option1 = 1
+ cmd.option2 = 1
+ cmd.user_options = [('option1', '', ''), ('option2', '', '')]
+ cmd.dump_options()
+
+ wanted = ["command options for 'MyCmd':", ' option1 = 1',
+ ' option2 = 1']
+ msgs = self.get_logs(logging.INFO)
+ self.assertEqual(msgs, wanted)
+
+ def test_ensure_string(self):
+ cmd = self.cmd
+ cmd.option1 = 'ok'
+ cmd.ensure_string('option1')
+
+ cmd.option2 = None
+ cmd.ensure_string('option2', 'xxx')
+ self.assertTrue(hasattr(cmd, 'option2'))
+
+ cmd.option3 = 1
+ self.assertRaises(PackagingOptionError, cmd.ensure_string, 'option3')
+
+ def test_ensure_string_list(self):
+ cmd = self.cmd
+ cmd.option1 = 'ok,dok'
+ cmd.ensure_string_list('option1')
+ self.assertEqual(cmd.option1, ['ok', 'dok'])
+
+ cmd.yes_string_list = ['one', 'two', 'three']
+ cmd.yes_string_list2 = 'ok'
+ cmd.ensure_string_list('yes_string_list')
+ cmd.ensure_string_list('yes_string_list2')
+ self.assertEqual(cmd.yes_string_list, ['one', 'two', 'three'])
+ self.assertEqual(cmd.yes_string_list2, ['ok'])
+
+ cmd.not_string_list = ['one', 2, 'three']
+ cmd.not_string_list2 = object()
+ self.assertRaises(PackagingOptionError,
+ cmd.ensure_string_list, 'not_string_list')
+
+ self.assertRaises(PackagingOptionError,
+ cmd.ensure_string_list, 'not_string_list2')
+
+ def test_ensure_filename(self):
+ cmd = self.cmd
+ cmd.option1 = __file__
+ cmd.ensure_filename('option1')
+ cmd.option2 = 'xxx'
+ self.assertRaises(PackagingOptionError, cmd.ensure_filename, 'option2')
+
+ def test_ensure_dirname(self):
+ cmd = self.cmd
+ cmd.option1 = os.path.dirname(__file__) or os.curdir
+ cmd.ensure_dirname('option1')
+ cmd.option2 = 'xxx'
+ self.assertRaises(PackagingOptionError, cmd.ensure_dirname, 'option2')
+
+
+def test_suite():
+ return unittest.makeSuite(CommandTestCase)
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_command_config.py b/Lib/packaging/tests/test_command_config.py
new file mode 100644
index 0000000..dae75b4
--- /dev/null
+++ b/Lib/packaging/tests/test_command_config.py
@@ -0,0 +1,76 @@
+"""Tests for distutils.command.config."""
+import os
+import sys
+import logging
+
+from packaging.command.config import dump_file, config
+from packaging.tests import unittest, support
+
+
+class ConfigTestCase(support.LoggingCatcher,
+ support.TempdirManager,
+ unittest.TestCase):
+
+ def test_dump_file(self):
+ this_file = __file__.rstrip('co')
+ with open(this_file) as f:
+ numlines = len(f.readlines())
+
+ dump_file(this_file, 'I am the header')
+
+ logs = []
+ for log in self.get_logs(logging.INFO):
+ logs.extend(line for line in log.split('\n'))
+ self.assertEqual(len(logs), numlines + 2)
+
+ @unittest.skipIf(sys.platform == 'win32', 'disabled on win32')
+ def test_search_cpp(self):
+ pkg_dir, dist = self.create_dist()
+ cmd = config(dist)
+
+ # simple pattern searches
+ match = cmd.search_cpp(pattern='xxx', body='/* xxx */')
+ self.assertEqual(match, 0)
+
+ match = cmd.search_cpp(pattern='_configtest', body='/* xxx */')
+ self.assertEqual(match, 1)
+
+ def test_finalize_options(self):
+ # finalize_options does a bit of transformation
+ # on options
+ pkg_dir, dist = self.create_dist()
+ cmd = config(dist)
+ cmd.include_dirs = 'one%stwo' % os.pathsep
+ cmd.libraries = 'one'
+ cmd.library_dirs = 'three%sfour' % os.pathsep
+ cmd.ensure_finalized()
+
+ self.assertEqual(cmd.include_dirs, ['one', 'two'])
+ self.assertEqual(cmd.libraries, ['one'])
+ self.assertEqual(cmd.library_dirs, ['three', 'four'])
+
+ def test_clean(self):
+ # _clean removes files
+ tmp_dir = self.mkdtemp()
+ f1 = os.path.join(tmp_dir, 'one')
+ f2 = os.path.join(tmp_dir, 'two')
+
+ self.write_file(f1, 'xxx')
+ self.write_file(f2, 'xxx')
+
+ for f in (f1, f2):
+ self.assertTrue(os.path.exists(f))
+
+ pkg_dir, dist = self.create_dist()
+ cmd = config(dist)
+ cmd._clean(f1, f2)
+
+ for f in (f1, f2):
+ self.assertFalse(os.path.exists(f))
+
+
+def test_suite():
+ return unittest.makeSuite(ConfigTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_install_data.py b/Lib/packaging/tests/test_command_install_data.py
new file mode 100644
index 0000000..8d4373d
--- /dev/null
+++ b/Lib/packaging/tests/test_command_install_data.py
@@ -0,0 +1,148 @@
+"""Tests for packaging.command.install_data."""
+import os
+import sys
+import sysconfig
+import packaging.database
+from sysconfig import _get_default_scheme
+from packaging.tests import unittest, support
+from packaging.command.install_data import install_data
+from packaging.command.install_dist import install_dist
+from packaging.command.install_distinfo import install_distinfo
+
+
+class InstallDataTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ def setUp(self):
+ super(InstallDataTestCase, self).setUp()
+ scheme = _get_default_scheme()
+ old_items = sysconfig._SCHEMES.items(scheme)
+
+ def restore():
+ sysconfig._SCHEMES.remove_section(scheme)
+ sysconfig._SCHEMES.add_section(scheme)
+ for option, value in old_items:
+ sysconfig._SCHEMES.set(scheme, option, value)
+
+ self.addCleanup(restore)
+
+ def test_simple_run(self):
+ pkg_dir, dist = self.create_dist()
+ cmd = install_data(dist)
+ cmd.install_dir = inst = os.path.join(pkg_dir, 'inst')
+ scheme = _get_default_scheme()
+
+ sysconfig._SCHEMES.set(scheme, 'inst',
+ os.path.join(pkg_dir, 'inst'))
+ sysconfig._SCHEMES.set(scheme, 'inst2',
+ os.path.join(pkg_dir, 'inst2'))
+
+ one = os.path.join(pkg_dir, 'one')
+ self.write_file(one, 'xxx')
+ inst2 = os.path.join(pkg_dir, 'inst2')
+ two = os.path.join(pkg_dir, 'two')
+ self.write_file(two, 'xxx')
+
+ # FIXME this creates a literal \{inst2\} directory!
+ cmd.data_files = {one: '{inst}/one', two: '{inst2}/two'}
+ self.assertCountEqual(cmd.get_inputs(), [one, two])
+
+ # let's run the command
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # let's check the result
+ self.assertEqual(len(cmd.get_outputs()), 2)
+ rtwo = os.path.split(two)[-1]
+ self.assertTrue(os.path.exists(os.path.join(inst2, rtwo)))
+ rone = os.path.split(one)[-1]
+ self.assertTrue(os.path.exists(os.path.join(inst, rone)))
+ cmd.outfiles = []
+
+ # let's try with warn_dir one
+ cmd.warn_dir = True
+ cmd.finalized = False
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # let's check the result
+ self.assertEqual(len(cmd.get_outputs()), 2)
+ self.assertTrue(os.path.exists(os.path.join(inst2, rtwo)))
+ self.assertTrue(os.path.exists(os.path.join(inst, rone)))
+ cmd.outfiles = []
+
+ # now using root and empty dir
+ cmd.root = os.path.join(pkg_dir, 'root')
+ three = os.path.join(cmd.install_dir, 'three')
+ self.write_file(three, 'xx')
+
+ sysconfig._SCHEMES.set(scheme, 'inst3', cmd.install_dir)
+
+ cmd.data_files = {one: '{inst}/one', two: '{inst2}/two',
+ three: '{inst3}/three'}
+ cmd.finalized = False
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # let's check the result
+ self.assertEqual(len(cmd.get_outputs()), 3)
+ self.assertTrue(os.path.exists(os.path.join(inst2, rtwo)))
+ self.assertTrue(os.path.exists(os.path.join(inst, rone)))
+
+ def test_resources(self):
+ install_dir = self.mkdtemp()
+ scripts_dir = self.mkdtemp()
+ project_dir, dist = self.create_dist(
+ name='Spamlib', version='0.1',
+ data_files={'spamd': '{scripts}/spamd'})
+
+ os.chdir(project_dir)
+ self.write_file('spamd', '# Python script')
+ sysconfig._SCHEMES.set(_get_default_scheme(), 'scripts', scripts_dir)
+ sys.path.insert(0, install_dir)
+ packaging.database.disable_cache()
+ self.addCleanup(sys.path.remove, install_dir)
+ self.addCleanup(packaging.database.enable_cache)
+
+ cmd = install_dist(dist)
+ cmd.outputs = ['spamd']
+ cmd.install_lib = install_dir
+ dist.command_obj['install_dist'] = cmd
+
+ cmd = install_data(dist)
+ cmd.install_dir = install_dir
+ cmd.ensure_finalized()
+ dist.command_obj['install_data'] = cmd
+ cmd.run()
+
+ cmd = install_distinfo(dist)
+ cmd.ensure_finalized()
+ dist.command_obj['install_distinfo'] = cmd
+ cmd.run()
+
+ # first a few sanity checks
+ self.assertEqual(os.listdir(scripts_dir), ['spamd'])
+ self.assertEqual(os.listdir(install_dir), ['Spamlib-0.1.dist-info'])
+
+ # now the real test
+ fn = os.path.join(install_dir, 'Spamlib-0.1.dist-info', 'RESOURCES')
+ with open(fn, encoding='utf-8') as fp:
+ content = fp.read().strip()
+
+ expected = 'spamd,%s' % os.path.join(scripts_dir, 'spamd')
+ self.assertEqual(content, expected)
+
+ # just to be sure, we also test that get_file works here, even though
+ # packaging.database has its own test file
+ with packaging.database.get_file('Spamlib', 'spamd') as fp:
+ content = fp.read()
+
+ self.assertEqual('# Python script', content)
+
+
+def test_suite():
+ return unittest.makeSuite(InstallDataTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_install_dist.py b/Lib/packaging/tests/test_command_install_dist.py
new file mode 100644
index 0000000..3345d2e
--- /dev/null
+++ b/Lib/packaging/tests/test_command_install_dist.py
@@ -0,0 +1,241 @@
+"""Tests for packaging.command.install."""
+
+import os
+import imp
+import sys
+from sysconfig import (get_scheme_names, get_config_vars,
+ _SCHEMES, get_config_var, get_path)
+
+from packaging.command.build_ext import build_ext
+from packaging.command.install_dist import install_dist
+from packaging.compiler.extension import Extension
+from packaging.dist import Distribution
+from packaging.errors import PackagingOptionError
+
+from packaging.tests import unittest, support
+
+
+_CONFIG_VARS = get_config_vars()
+
+
+def _make_ext_name(modname):
+ if os.name == 'nt' and sys.executable.endswith('_d.exe'):
+ modname += '_d'
+ return modname + get_config_var('SO')
+
+
+class InstallTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ def test_home_installation_scheme(self):
+ # This ensure two things:
+ # - that --home generates the desired set of directory names
+ # - test --home is supported on all platforms
+ builddir = self.mkdtemp()
+ destination = os.path.join(builddir, "installation")
+
+ dist = Distribution({"name": "foopkg"})
+ dist.command_obj["build"] = support.DummyCommand(
+ build_base=builddir,
+ build_lib=os.path.join(builddir, "lib"),
+ )
+
+ old_posix_prefix = _SCHEMES.get('posix_prefix', 'platinclude')
+ old_posix_home = _SCHEMES.get('posix_home', 'platinclude')
+
+ new_path = '{platbase}/include/python{py_version_short}'
+ _SCHEMES.set('posix_prefix', 'platinclude', new_path)
+ _SCHEMES.set('posix_home', 'platinclude', '{platbase}/include/python')
+
+ try:
+ cmd = install_dist(dist)
+ cmd.home = destination
+ cmd.ensure_finalized()
+ finally:
+ _SCHEMES.set('posix_prefix', 'platinclude', old_posix_prefix)
+ _SCHEMES.set('posix_home', 'platinclude', old_posix_home)
+
+ self.assertEqual(cmd.install_base, destination)
+ self.assertEqual(cmd.install_platbase, destination)
+
+ def check_path(got, expected):
+ got = os.path.normpath(got)
+ expected = os.path.normpath(expected)
+ self.assertEqual(got, expected)
+
+ libdir = os.path.join(destination, "lib", "python")
+ check_path(cmd.install_lib, libdir)
+ check_path(cmd.install_platlib, libdir)
+ check_path(cmd.install_purelib, libdir)
+ check_path(cmd.install_headers,
+ os.path.join(destination, "include", "python", "foopkg"))
+ check_path(cmd.install_scripts, os.path.join(destination, "bin"))
+ check_path(cmd.install_data, destination)
+
+ def test_user_site(self):
+ # test install with --user
+ # preparing the environment for the test
+ self.old_user_base = get_config_var('userbase')
+ self.old_user_site = get_path('purelib', '%s_user' % os.name)
+ self.tmpdir = self.mkdtemp()
+ self.user_base = os.path.join(self.tmpdir, 'B')
+ self.user_site = os.path.join(self.tmpdir, 'S')
+ _CONFIG_VARS['userbase'] = self.user_base
+ scheme = '%s_user' % os.name
+ _SCHEMES.set(scheme, 'purelib', self.user_site)
+
+ def _expanduser(path):
+ if path[0] == '~':
+ path = os.path.normpath(self.tmpdir) + path[1:]
+ return path
+
+ self.old_expand = os.path.expanduser
+ os.path.expanduser = _expanduser
+
+ def cleanup():
+ _CONFIG_VARS['userbase'] = self.old_user_base
+ _SCHEMES.set(scheme, 'purelib', self.old_user_site)
+ os.path.expanduser = self.old_expand
+
+ self.addCleanup(cleanup)
+
+ schemes = get_scheme_names()
+ for key in ('nt_user', 'posix_user', 'os2_home'):
+ self.assertIn(key, schemes)
+
+ dist = Distribution({'name': 'xx'})
+ cmd = install_dist(dist)
+
+ # making sure the user option is there
+ options = [name for name, short, lable in
+ cmd.user_options]
+ self.assertIn('user', options)
+
+ # setting a value
+ cmd.user = True
+
+ # user base and site shouldn't be created yet
+ self.assertFalse(os.path.exists(self.user_base))
+ self.assertFalse(os.path.exists(self.user_site))
+
+ # let's run finalize
+ cmd.ensure_finalized()
+
+ # now they should
+ self.assertTrue(os.path.exists(self.user_base))
+ self.assertTrue(os.path.exists(self.user_site))
+
+ self.assertIn('userbase', cmd.config_vars)
+ self.assertIn('usersite', cmd.config_vars)
+
+ def test_handle_extra_path(self):
+ dist = Distribution({'name': 'xx', 'extra_path': 'path,dirs'})
+ cmd = install_dist(dist)
+
+ # two elements
+ cmd.handle_extra_path()
+ self.assertEqual(cmd.extra_path, ['path', 'dirs'])
+ self.assertEqual(cmd.extra_dirs, 'dirs')
+ self.assertEqual(cmd.path_file, 'path')
+
+ # one element
+ cmd.extra_path = ['path']
+ cmd.handle_extra_path()
+ self.assertEqual(cmd.extra_path, ['path'])
+ self.assertEqual(cmd.extra_dirs, 'path')
+ self.assertEqual(cmd.path_file, 'path')
+
+ # none
+ dist.extra_path = cmd.extra_path = None
+ cmd.handle_extra_path()
+ self.assertEqual(cmd.extra_path, None)
+ self.assertEqual(cmd.extra_dirs, '')
+ self.assertEqual(cmd.path_file, None)
+
+ # three elements (no way !)
+ cmd.extra_path = 'path,dirs,again'
+ self.assertRaises(PackagingOptionError, cmd.handle_extra_path)
+
+ def test_finalize_options(self):
+ dist = Distribution({'name': 'xx'})
+ cmd = install_dist(dist)
+
+ # must supply either prefix/exec-prefix/home or
+ # install-base/install-platbase -- not both
+ cmd.prefix = 'prefix'
+ cmd.install_base = 'base'
+ self.assertRaises(PackagingOptionError, cmd.finalize_options)
+
+ # must supply either home or prefix/exec-prefix -- not both
+ cmd.install_base = None
+ cmd.home = 'home'
+ self.assertRaises(PackagingOptionError, cmd.finalize_options)
+
+ # can't combine user with with prefix/exec_prefix/home or
+ # install_(plat)base
+ cmd.prefix = None
+ cmd.user = 'user'
+ self.assertRaises(PackagingOptionError, cmd.finalize_options)
+
+ def test_old_record(self):
+ # test pre-PEP 376 --record option (outside dist-info dir)
+ install_dir = self.mkdtemp()
+ project_dir, dist = self.create_dist(py_modules=['hello'],
+ scripts=['sayhi'])
+ os.chdir(project_dir)
+ self.write_file('hello.py', "def main(): print('o hai')")
+ self.write_file('sayhi', 'from hello import main; main()')
+
+ cmd = install_dist(dist)
+ dist.command_obj['install_dist'] = cmd
+ cmd.root = install_dir
+ cmd.record = os.path.join(project_dir, 'filelist')
+ cmd.ensure_finalized()
+ cmd.run()
+
+ with open(cmd.record) as f:
+ content = f.read()
+
+ found = [os.path.basename(line) for line in content.splitlines()]
+ expected = ['hello.py', 'hello.%s.pyc' % imp.get_tag(), 'sayhi',
+ 'METADATA', 'INSTALLER', 'REQUESTED', 'RECORD']
+ self.assertEqual(sorted(found), sorted(expected))
+
+ # XXX test that fancy_getopt is okay with options named
+ # record and no-record but unrelated
+
+ def test_old_record_extensions(self):
+ # test pre-PEP 376 --record option with ext modules
+ install_dir = self.mkdtemp()
+ project_dir, dist = self.create_dist(ext_modules=[
+ Extension('xx', ['xxmodule.c'])])
+ os.chdir(project_dir)
+ support.copy_xxmodule_c(project_dir)
+
+ buildextcmd = build_ext(dist)
+ support.fixup_build_ext(buildextcmd)
+ buildextcmd.ensure_finalized()
+
+ cmd = install_dist(dist)
+ dist.command_obj['install_dist'] = cmd
+ dist.command_obj['build_ext'] = buildextcmd
+ cmd.root = install_dir
+ cmd.record = os.path.join(project_dir, 'filelist')
+ cmd.ensure_finalized()
+ cmd.run()
+
+ with open(cmd.record) as f:
+ content = f.read()
+
+ found = [os.path.basename(line) for line in content.splitlines()]
+ expected = [_make_ext_name('xx'),
+ 'METADATA', 'INSTALLER', 'REQUESTED', 'RECORD']
+ self.assertEqual(found, expected)
+
+
+def test_suite():
+ return unittest.makeSuite(InstallTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_install_distinfo.py b/Lib/packaging/tests/test_command_install_distinfo.py
new file mode 100644
index 0000000..33153e7
--- /dev/null
+++ b/Lib/packaging/tests/test_command_install_distinfo.py
@@ -0,0 +1,252 @@
+"""Tests for ``packaging.command.install_distinfo``.
+
+Writing of the RESOURCES file is tested in test_command_install_data.
+"""
+
+import os
+import csv
+import hashlib
+import sysconfig
+
+from packaging.command.install_distinfo import install_distinfo
+from packaging.command.cmd import Command
+from packaging.compiler.extension import Extension
+from packaging.metadata import Metadata
+from packaging.tests import unittest, support
+
+
+class DummyInstallCmd(Command):
+
+ def __init__(self, dist=None):
+ self.outputs = []
+ self.distribution = dist
+
+ def __getattr__(self, name):
+ return None
+
+ def ensure_finalized(self):
+ pass
+
+ def get_outputs(self):
+ return (self.outputs +
+ self.get_finalized_command('install_distinfo').get_outputs())
+
+
+class InstallDistinfoTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ checkLists = lambda self, x, y: self.assertListEqual(sorted(x), sorted(y))
+
+ def test_empty_install(self):
+ pkg_dir, dist = self.create_dist(name='foo',
+ version='1.0')
+ install_dir = self.mkdtemp()
+
+ install = DummyInstallCmd(dist)
+ dist.command_obj['install_dist'] = install
+
+ cmd = install_distinfo(dist)
+ dist.command_obj['install_distinfo'] = cmd
+
+ cmd.install_dir = install_dir
+ cmd.ensure_finalized()
+ cmd.run()
+
+ self.checkLists(os.listdir(install_dir), ['foo-1.0.dist-info'])
+
+ dist_info = os.path.join(install_dir, 'foo-1.0.dist-info')
+ self.checkLists(os.listdir(dist_info),
+ ['METADATA', 'RECORD', 'REQUESTED', 'INSTALLER'])
+ with open(os.path.join(dist_info, 'INSTALLER')) as fp:
+ self.assertEqual(fp.read(), 'distutils')
+ with open(os.path.join(dist_info, 'REQUESTED')) as fp:
+ self.assertEqual(fp.read(), '')
+ meta_path = os.path.join(dist_info, 'METADATA')
+ self.assertTrue(Metadata(path=meta_path).check())
+
+ def test_installer(self):
+ pkg_dir, dist = self.create_dist(name='foo',
+ version='1.0')
+ install_dir = self.mkdtemp()
+
+ install = DummyInstallCmd(dist)
+ dist.command_obj['install_dist'] = install
+
+ cmd = install_distinfo(dist)
+ dist.command_obj['install_distinfo'] = cmd
+
+ cmd.install_dir = install_dir
+ cmd.installer = 'bacon-python'
+ cmd.ensure_finalized()
+ cmd.run()
+
+ dist_info = os.path.join(install_dir, 'foo-1.0.dist-info')
+ with open(os.path.join(dist_info, 'INSTALLER')) as fp:
+ self.assertEqual(fp.read(), 'bacon-python')
+
+ def test_requested(self):
+ pkg_dir, dist = self.create_dist(name='foo',
+ version='1.0')
+ install_dir = self.mkdtemp()
+
+ install = DummyInstallCmd(dist)
+ dist.command_obj['install_dist'] = install
+
+ cmd = install_distinfo(dist)
+ dist.command_obj['install_distinfo'] = cmd
+
+ cmd.install_dir = install_dir
+ cmd.requested = False
+ cmd.ensure_finalized()
+ cmd.run()
+
+ dist_info = os.path.join(install_dir, 'foo-1.0.dist-info')
+ self.checkLists(os.listdir(dist_info),
+ ['METADATA', 'RECORD', 'INSTALLER'])
+
+ def test_no_record(self):
+ pkg_dir, dist = self.create_dist(name='foo',
+ version='1.0')
+ install_dir = self.mkdtemp()
+
+ install = DummyInstallCmd(dist)
+ dist.command_obj['install_dist'] = install
+
+ cmd = install_distinfo(dist)
+ dist.command_obj['install_distinfo'] = cmd
+
+ cmd.install_dir = install_dir
+ cmd.no_record = True
+ cmd.ensure_finalized()
+ cmd.run()
+
+ dist_info = os.path.join(install_dir, 'foo-1.0.dist-info')
+ self.checkLists(os.listdir(dist_info),
+ ['METADATA', 'REQUESTED', 'INSTALLER'])
+
+ def test_record_basic(self):
+ install_dir = self.mkdtemp()
+ modules_dest = os.path.join(install_dir, 'lib')
+ scripts_dest = os.path.join(install_dir, 'bin')
+ project_dir, dist = self.create_dist(
+ name='Spamlib', version='0.1',
+ py_modules=['spam'], scripts=['spamd'],
+ ext_modules=[Extension('_speedspam', ['_speedspam.c'])])
+
+ # using a real install_dist command is too painful, so we use a mock
+ # class that's only a holder for options to be used by install_distinfo
+ # and we create placeholder files manually instead of using build_*.
+ # the install_* commands will still be consulted by install_distinfo.
+ os.chdir(project_dir)
+ self.write_file('spam', '# Python module')
+ self.write_file('spamd', '# Python script')
+ extmod = '_speedspam' + sysconfig.get_config_var('SO')
+ self.write_file(extmod, '')
+
+ install = DummyInstallCmd(dist)
+ install.outputs = ['spam', 'spamd', extmod]
+ install.install_lib = modules_dest
+ install.install_scripts = scripts_dest
+ dist.command_obj['install_dist'] = install
+
+ cmd = install_distinfo(dist)
+ cmd.ensure_finalized()
+ dist.command_obj['install_distinfo'] = cmd
+ cmd.run()
+
+ # checksum and size are not hard-coded for METADATA as it is
+ # platform-dependent (line endings)
+ metadata = os.path.join(modules_dest, 'Spamlib-0.1.dist-info',
+ 'METADATA')
+ with open(metadata, 'rb') as fp:
+ content = fp.read()
+
+ metadata_size = str(len(content))
+ metadata_md5 = hashlib.md5(content).hexdigest()
+
+ record = os.path.join(modules_dest, 'Spamlib-0.1.dist-info', 'RECORD')
+ with open(record, encoding='utf-8') as fp:
+ content = fp.read()
+
+ found = []
+ for line in content.splitlines():
+ filename, checksum, size = line.split(',')
+ filename = os.path.basename(filename)
+ found.append((filename, checksum, size))
+
+ expected = [
+ ('spam', '6ab2f288ef2545868effe68757448b45', '15'),
+ ('spamd', 'd13e6156ce78919a981e424b2fdcd974', '15'),
+ (extmod, 'd41d8cd98f00b204e9800998ecf8427e', '0'),
+ ('METADATA', metadata_md5, metadata_size),
+ ('INSTALLER', '44e3fde05f3f537ed85831969acf396d', '9'),
+ ('REQUESTED', 'd41d8cd98f00b204e9800998ecf8427e', '0'),
+ ('RECORD', '', ''),
+ ]
+ self.assertEqual(found, expected)
+
+ def test_record(self):
+ pkg_dir, dist = self.create_dist(name='foo',
+ version='1.0')
+ install_dir = self.mkdtemp()
+
+ install = DummyInstallCmd(dist)
+ dist.command_obj['install_dist'] = install
+
+ fake_dists = os.path.join(os.path.dirname(__file__), 'fake_dists')
+ fake_dists = os.path.realpath(fake_dists)
+
+ # for testing, we simply add all files from _backport's fake_dists
+ dirs = []
+ for dir in os.listdir(fake_dists):
+ full_path = os.path.join(fake_dists, dir)
+ if (not dir.endswith('.egg') or dir.endswith('.egg-info') or
+ dir.endswith('.dist-info')) and os.path.isdir(full_path):
+ dirs.append(full_path)
+
+ for dir in dirs:
+ for path, subdirs, files in os.walk(dir):
+ install.outputs += [os.path.join(path, f) for f in files]
+ install.outputs += [os.path.join('path', f + 'c')
+ for f in files if f.endswith('.py')]
+
+ cmd = install_distinfo(dist)
+ dist.command_obj['install_distinfo'] = cmd
+
+ cmd.install_dir = install_dir
+ cmd.ensure_finalized()
+ cmd.run()
+
+ dist_info = os.path.join(install_dir, 'foo-1.0.dist-info')
+
+ expected = []
+ for f in install.get_outputs():
+ if (f.endswith(('.pyc', '.pyo')) or f == os.path.join(
+ install_dir, 'foo-1.0.dist-info', 'RECORD')):
+ expected.append([f, '', ''])
+ else:
+ size = os.path.getsize(f)
+ md5 = hashlib.md5()
+ with open(f, 'rb') as fp:
+ md5.update(fp.read())
+ hash = md5.hexdigest()
+ expected.append([f, hash, str(size)])
+
+ parsed = []
+ with open(os.path.join(dist_info, 'RECORD'), 'r') as f:
+ reader = csv.reader(f, delimiter=',',
+ lineterminator=os.linesep,
+ quotechar='"')
+ parsed = list(reader)
+
+ self.maxDiff = None
+ self.checkLists(parsed, expected)
+
+
+def test_suite():
+ return unittest.makeSuite(InstallDistinfoTestCase)
+
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_install_headers.py b/Lib/packaging/tests/test_command_install_headers.py
new file mode 100644
index 0000000..f2906a7
--- /dev/null
+++ b/Lib/packaging/tests/test_command_install_headers.py
@@ -0,0 +1,38 @@
+"""Tests for packaging.command.install_headers."""
+import os
+
+from packaging.command.install_headers import install_headers
+from packaging.tests import unittest, support
+
+
+class InstallHeadersTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ def test_simple_run(self):
+ # we have two headers
+ header_list = self.mkdtemp()
+ header1 = os.path.join(header_list, 'header1')
+ header2 = os.path.join(header_list, 'header2')
+ self.write_file(header1)
+ self.write_file(header2)
+ headers = [header1, header2]
+
+ pkg_dir, dist = self.create_dist(headers=headers)
+ cmd = install_headers(dist)
+ self.assertEqual(cmd.get_inputs(), headers)
+
+ # let's run the command
+ cmd.install_dir = os.path.join(pkg_dir, 'inst')
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # let's check the results
+ self.assertEqual(len(cmd.get_outputs()), 2)
+
+
+def test_suite():
+ return unittest.makeSuite(InstallHeadersTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_install_lib.py b/Lib/packaging/tests/test_command_install_lib.py
new file mode 100644
index 0000000..79e8fa8
--- /dev/null
+++ b/Lib/packaging/tests/test_command_install_lib.py
@@ -0,0 +1,110 @@
+"""Tests for packaging.command.install_data."""
+import os
+import sys
+import imp
+
+from packaging.tests import unittest, support
+from packaging.command.install_lib import install_lib
+from packaging.compiler.extension import Extension
+from packaging.errors import PackagingOptionError
+
+
+class InstallLibTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ support.EnvironRestorer,
+ unittest.TestCase):
+
+ restore_environ = ['PYTHONPATH']
+
+ def test_finalize_options(self):
+ dist = self.create_dist()[1]
+ cmd = install_lib(dist)
+
+ cmd.finalize_options()
+ self.assertTrue(cmd.compile)
+ self.assertEqual(cmd.optimize, 0)
+
+ # optimize must be 0, 1, or 2
+ cmd.optimize = 'foo'
+ self.assertRaises(PackagingOptionError, cmd.finalize_options)
+ cmd.optimize = '4'
+ self.assertRaises(PackagingOptionError, cmd.finalize_options)
+
+ cmd.optimize = '2'
+ cmd.finalize_options()
+ self.assertEqual(cmd.optimize, 2)
+
+ def test_byte_compile(self):
+ project_dir, dist = self.create_dist()
+ os.chdir(project_dir)
+ cmd = install_lib(dist)
+ cmd.compile = True
+ cmd.optimize = 1
+
+ f = os.path.join(project_dir, 'foo.py')
+ self.write_file(f, '# python file')
+ cmd.byte_compile([f])
+ pyc_file = imp.cache_from_source('foo.py', True)
+ pyo_file = imp.cache_from_source('foo.py', False)
+ self.assertTrue(os.path.exists(pyc_file))
+ self.assertTrue(os.path.exists(pyo_file))
+
+ def test_byte_compile_under_B(self):
+ # make sure byte compilation works under -B (dont_write_bytecode)
+ self.addCleanup(setattr, sys, 'dont_write_bytecode',
+ sys.dont_write_bytecode)
+ sys.dont_write_bytecode = True
+ self.test_byte_compile()
+
+ def test_get_outputs(self):
+ project_dir, dist = self.create_dist()
+ os.chdir(project_dir)
+ os.mkdir('spam')
+ cmd = install_lib(dist)
+
+ # setting up a dist environment
+ cmd.compile = True
+ cmd.optimize = 1
+ cmd.install_dir = self.mkdtemp()
+ f = os.path.join(project_dir, 'spam', '__init__.py')
+ self.write_file(f, '# python package')
+ cmd.distribution.ext_modules = [Extension('foo', ['xxx'])]
+ cmd.distribution.packages = ['spam']
+
+ # make sure the build_lib is set the temp dir # XXX what? this is not
+ # needed in the same distutils test and should work without manual
+ # intervention
+ build_dir = os.path.split(project_dir)[0]
+ cmd.get_finalized_command('build_py').build_lib = build_dir
+
+ # get_outputs should return 4 elements: spam/__init__.py, .pyc and
+ # .pyo, foo.import-tag-abiflags.so / foo.pyd
+ outputs = cmd.get_outputs()
+ self.assertEqual(len(outputs), 4, outputs)
+
+ def test_get_inputs(self):
+ project_dir, dist = self.create_dist()
+ os.chdir(project_dir)
+ os.mkdir('spam')
+ cmd = install_lib(dist)
+
+ # setting up a dist environment
+ cmd.compile = True
+ cmd.optimize = 1
+ cmd.install_dir = self.mkdtemp()
+ f = os.path.join(project_dir, 'spam', '__init__.py')
+ self.write_file(f, '# python package')
+ cmd.distribution.ext_modules = [Extension('foo', ['xxx'])]
+ cmd.distribution.packages = ['spam']
+
+ # get_inputs should return 2 elements: spam/__init__.py and
+ # foo.import-tag-abiflags.so / foo.pyd
+ inputs = cmd.get_inputs()
+ self.assertEqual(len(inputs), 2, inputs)
+
+
+def test_suite():
+ return unittest.makeSuite(InstallLibTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_install_scripts.py b/Lib/packaging/tests/test_command_install_scripts.py
new file mode 100644
index 0000000..6452a34
--- /dev/null
+++ b/Lib/packaging/tests/test_command_install_scripts.py
@@ -0,0 +1,75 @@
+"""Tests for packaging.command.install_scripts."""
+import os
+
+from packaging.tests import unittest, support
+from packaging.command.install_scripts import install_scripts
+from packaging.dist import Distribution
+
+
+class InstallScriptsTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ def test_default_settings(self):
+ dist = Distribution()
+ dist.command_obj["build"] = support.DummyCommand(
+ build_scripts="/foo/bar")
+ dist.command_obj["install_dist"] = support.DummyCommand(
+ install_scripts="/splat/funk",
+ force=True,
+ skip_build=True,
+ )
+ cmd = install_scripts(dist)
+ self.assertFalse(cmd.force)
+ self.assertFalse(cmd.skip_build)
+ self.assertIs(cmd.build_dir, None)
+ self.assertIs(cmd.install_dir, None)
+
+ cmd.finalize_options()
+
+ self.assertTrue(cmd.force)
+ self.assertTrue(cmd.skip_build)
+ self.assertEqual(cmd.build_dir, "/foo/bar")
+ self.assertEqual(cmd.install_dir, "/splat/funk")
+
+ def test_installation(self):
+ source = self.mkdtemp()
+ expected = []
+
+ def write_script(name, text):
+ expected.append(name)
+ with open(os.path.join(source, name), "w") as f:
+ f.write(text)
+
+ write_script("script1.py", ("#! /usr/bin/env python2.3\n"
+ "# bogus script w/ Python sh-bang\n"
+ "pass\n"))
+ write_script("script2.py", ("#!/usr/bin/python\n"
+ "# bogus script w/ Python sh-bang\n"
+ "pass\n"))
+ write_script("shell.sh", ("#!/bin/sh\n"
+ "# bogus shell script w/ sh-bang\n"
+ "exit 0\n"))
+
+ target = self.mkdtemp()
+ dist = Distribution()
+ dist.command_obj["build"] = support.DummyCommand(build_scripts=source)
+ dist.command_obj["install_dist"] = support.DummyCommand(
+ install_scripts=target,
+ force=True,
+ skip_build=True,
+ )
+ cmd = install_scripts(dist)
+ cmd.finalize_options()
+ cmd.run()
+
+ installed = os.listdir(target)
+ for name in expected:
+ self.assertIn(name, installed)
+
+
+def test_suite():
+ return unittest.makeSuite(InstallScriptsTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_register.py b/Lib/packaging/tests/test_command_register.py
new file mode 100644
index 0000000..07fad89
--- /dev/null
+++ b/Lib/packaging/tests/test_command_register.py
@@ -0,0 +1,260 @@
+"""Tests for packaging.command.register."""
+import os
+import getpass
+import urllib.request
+import urllib.error
+import urllib.parse
+
+try:
+ import docutils
+ DOCUTILS_SUPPORT = True
+except ImportError:
+ DOCUTILS_SUPPORT = False
+
+from packaging.tests import unittest, support
+from packaging.tests.support import Inputs
+from packaging.command import register as register_module
+from packaging.command.register import register
+from packaging.errors import PackagingSetupError
+
+
+PYPIRC_NOPASSWORD = """\
+[distutils]
+
+index-servers =
+ server1
+
+[server1]
+username:me
+"""
+
+WANTED_PYPIRC = """\
+[distutils]
+index-servers =
+ pypi
+
+[pypi]
+username:tarek
+password:password
+"""
+
+
+class FakeOpener:
+ """Fakes a PyPI server"""
+ def __init__(self):
+ self.reqs = []
+
+ def __call__(self, *args):
+ return self
+
+ def open(self, req):
+ self.reqs.append(req)
+ return self
+
+ def read(self):
+ return 'xxx'
+
+
+class RegisterTestCase(support.TempdirManager,
+ support.EnvironRestorer,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ restore_environ = ['HOME']
+
+ def setUp(self):
+ super(RegisterTestCase, self).setUp()
+ self.tmp_dir = self.mkdtemp()
+ self.rc = os.path.join(self.tmp_dir, '.pypirc')
+ os.environ['HOME'] = self.tmp_dir
+
+ # patching the password prompt
+ self._old_getpass = getpass.getpass
+
+ def _getpass(prompt):
+ return 'password'
+
+ getpass.getpass = _getpass
+ self.old_opener = urllib.request.build_opener
+ self.conn = urllib.request.build_opener = FakeOpener()
+
+ def tearDown(self):
+ getpass.getpass = self._old_getpass
+ urllib.request.build_opener = self.old_opener
+ if hasattr(register_module, 'input'):
+ del register_module.input
+ super(RegisterTestCase, self).tearDown()
+
+ def _get_cmd(self, metadata=None):
+ if metadata is None:
+ metadata = {'home_page': 'xxx', 'author': 'xxx',
+ 'author_email': 'xxx',
+ 'name': 'xxx', 'version': 'xxx'}
+ pkg_info, dist = self.create_dist(**metadata)
+ return register(dist)
+
+ def test_create_pypirc(self):
+ # this test makes sure a .pypirc file
+ # is created when requested.
+
+ # let's create a register instance
+ cmd = self._get_cmd()
+
+ # we shouldn't have a .pypirc file yet
+ self.assertFalse(os.path.exists(self.rc))
+
+ # patching input and getpass.getpass
+ # so register gets happy
+ # Here's what we are faking :
+ # use your existing login (choice 1.)
+ # Username : 'tarek'
+ # Password : 'password'
+ # Save your login (y/N)? : 'y'
+ inputs = Inputs('1', 'tarek', 'y')
+ register_module.input = inputs
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # we should have a brand new .pypirc file
+ self.assertTrue(os.path.exists(self.rc))
+
+ # with the content similar to WANTED_PYPIRC
+ with open(self.rc) as fp:
+ content = fp.read()
+ self.assertEqual(content, WANTED_PYPIRC)
+
+ # now let's make sure the .pypirc file generated
+ # really works : we shouldn't be asked anything
+ # if we run the command again
+ def _no_way(prompt=''):
+ raise AssertionError(prompt)
+
+ register_module.input = _no_way
+ cmd.show_response = True
+ cmd.finalized = False
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # let's see what the server received : we should
+ # have 2 similar requests
+ self.assertEqual(len(self.conn.reqs), 2)
+ req1 = dict(self.conn.reqs[0].headers)
+ req2 = dict(self.conn.reqs[1].headers)
+ self.assertEqual(req2['Content-length'], req1['Content-length'])
+ self.assertIn(b'xxx', self.conn.reqs[1].data)
+
+ def test_password_not_in_file(self):
+
+ self.write_file(self.rc, PYPIRC_NOPASSWORD)
+ cmd = self._get_cmd()
+ cmd.finalize_options()
+ cmd._set_config()
+ cmd.send_metadata()
+
+ # dist.password should be set
+ # therefore used afterwards by other commands
+ self.assertEqual(cmd.distribution.password, 'password')
+
+ def test_registration(self):
+ # this test runs choice 2
+ cmd = self._get_cmd()
+ inputs = Inputs('2', 'tarek', 'tarek@ziade.org')
+ register_module.input = inputs
+ # let's run the command
+ # FIXME does this send a real request? use a mock server
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # we should have send a request
+ self.assertEqual(len(self.conn.reqs), 1)
+ req = self.conn.reqs[0]
+ headers = dict(req.headers)
+ self.assertEqual(headers['Content-length'], '628')
+ self.assertIn(b'tarek', req.data)
+
+ def test_password_reset(self):
+ # this test runs choice 3
+ cmd = self._get_cmd()
+ inputs = Inputs('3', 'tarek@ziade.org')
+ register_module.input = inputs
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # we should have send a request
+ self.assertEqual(len(self.conn.reqs), 1)
+ req = self.conn.reqs[0]
+ headers = dict(req.headers)
+ self.assertEqual(headers['Content-length'], '298')
+ self.assertIn(b'tarek', req.data)
+
+ @unittest.skipUnless(DOCUTILS_SUPPORT, 'needs docutils')
+ def test_strict(self):
+ # testing the strict option: when on, the register command stops if the
+ # metadata is incomplete or if description contains bad reST
+
+ # empty metadata # XXX this is not really empty..
+ cmd = self._get_cmd({'name': 'xxx', 'version': 'xxx'})
+ cmd.ensure_finalized()
+ cmd.strict = True
+ inputs = Inputs('1', 'tarek', 'y')
+ register_module.input = inputs
+ self.assertRaises(PackagingSetupError, cmd.run)
+
+ # metadata is OK but description is broken
+ metadata = {'home_page': 'xxx', 'author': 'xxx',
+ 'author_email': 'éxéxé',
+ 'name': 'xxx', 'version': '4.2',
+ 'description': 'title\n==\n\ntext'}
+
+ cmd = self._get_cmd(metadata)
+ cmd.ensure_finalized()
+ cmd.strict = True
+ self.assertRaises(PackagingSetupError, cmd.run)
+
+ # now something that works
+ metadata['description'] = 'title\n=====\n\ntext'
+ cmd = self._get_cmd(metadata)
+ cmd.ensure_finalized()
+ cmd.strict = True
+ inputs = Inputs('1', 'tarek', 'y')
+ register_module.input = inputs
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # strict is not by default
+ cmd = self._get_cmd()
+ cmd.ensure_finalized()
+ inputs = Inputs('1', 'tarek', 'y')
+ register_module.input = inputs
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # and finally a Unicode test (bug #12114)
+ metadata = {'home_page': 'xxx', 'author': '\u00c9ric',
+ 'author_email': 'xxx', 'name': 'xxx',
+ 'version': 'xxx',
+ 'summary': 'Something about esszet \u00df',
+ 'description': 'More things about esszet \u00df'}
+
+ cmd = self._get_cmd(metadata)
+ cmd.ensure_finalized()
+ cmd.strict = True
+ inputs = Inputs('1', 'tarek', 'y')
+ register_module.input = inputs
+ cmd.ensure_finalized()
+ cmd.run()
+
+ def test_register_pep345(self):
+ cmd = self._get_cmd({})
+ cmd.ensure_finalized()
+ cmd.distribution.metadata['Requires-Dist'] = ['lxml']
+ data = cmd.build_post_data('submit')
+ self.assertEqual(data['metadata_version'], '1.2')
+ self.assertEqual(data['requires_dist'], ['lxml'])
+
+
+def test_suite():
+ return unittest.makeSuite(RegisterTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_sdist.py b/Lib/packaging/tests/test_command_sdist.py
new file mode 100644
index 0000000..47c455e
--- /dev/null
+++ b/Lib/packaging/tests/test_command_sdist.py
@@ -0,0 +1,395 @@
+"""Tests for packaging.command.sdist."""
+import os
+import tarfile
+import zipfile
+
+try:
+ import grp
+ import pwd
+ UID_GID_SUPPORT = True
+except ImportError:
+ UID_GID_SUPPORT = False
+
+from shutil import get_archive_formats
+from os.path import join
+from packaging.dist import Distribution
+from packaging.util import find_executable
+from packaging.errors import PackagingOptionError
+from packaging.command.sdist import sdist, show_formats
+
+from test.support import captured_stdout
+from packaging.tests import support, unittest
+from packaging.tests.support import requires_zlib
+
+
+MANIFEST = """\
+# file GENERATED by packaging, do NOT edit
+inroot.txt
+setup.cfg
+data%(sep)sdata.dt
+scripts%(sep)sscript.py
+some%(sep)sfile.txt
+some%(sep)sother_file.txt
+somecode%(sep)s__init__.py
+somecode%(sep)sdoc.dat
+somecode%(sep)sdoc.txt
+"""
+
+
+def builder(dist, filelist):
+ filelist.append('bah')
+
+
+class SDistTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ support.EnvironRestorer,
+ unittest.TestCase):
+
+ restore_environ = ['HOME']
+
+ def setUp(self):
+ super(SDistTestCase, self).setUp()
+ self.tmp_dir = self.mkdtemp()
+ os.environ['HOME'] = self.tmp_dir
+ # setting up an environment
+ self.old_path = os.getcwd()
+ os.mkdir(join(self.tmp_dir, 'somecode'))
+ os.mkdir(join(self.tmp_dir, 'dist'))
+ # a package, and a README
+ self.write_file((self.tmp_dir, 'README'), 'xxx')
+ self.write_file((self.tmp_dir, 'somecode', '__init__.py'), '#')
+ os.chdir(self.tmp_dir)
+
+ def tearDown(self):
+ # back to normal
+ os.chdir(self.old_path)
+ super(SDistTestCase, self).tearDown()
+
+ def get_cmd(self, metadata=None):
+ """Returns a cmd"""
+ if metadata is None:
+ metadata = {'name': 'fake', 'version': '1.0',
+ 'home_page': 'xxx', 'author': 'xxx',
+ 'author_email': 'xxx'}
+ dist = Distribution(metadata)
+ dist.packages = ['somecode']
+ dist.include_package_data = True
+ cmd = sdist(dist)
+ cmd.dist_dir = 'dist'
+ return dist, cmd
+
+ @requires_zlib
+ def test_prune_file_list(self):
+ # this test creates a package with some vcs dirs in it
+ # and launch sdist to make sure they get pruned
+ # on all systems
+
+ # creating VCS directories with some files in them
+ os.mkdir(join(self.tmp_dir, 'somecode', '.svn'))
+ self.write_file((self.tmp_dir, 'somecode', '.svn', 'ok.py'), 'xxx')
+
+ os.mkdir(join(self.tmp_dir, 'somecode', '.hg'))
+ self.write_file((self.tmp_dir, 'somecode', '.hg',
+ 'ok'), 'xxx')
+
+ os.mkdir(join(self.tmp_dir, 'somecode', '.git'))
+ self.write_file((self.tmp_dir, 'somecode', '.git',
+ 'ok'), 'xxx')
+
+ # now building a sdist
+ dist, cmd = self.get_cmd()
+
+ # zip is available universally
+ # (tar might not be installed under win32)
+ cmd.formats = ['zip']
+
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # now let's check what we have
+ dist_folder = join(self.tmp_dir, 'dist')
+ files = os.listdir(dist_folder)
+ self.assertEqual(files, ['fake-1.0.zip'])
+
+ with zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip')) as zip_file:
+ content = zip_file.namelist()
+
+ # making sure everything has been pruned correctly
+ self.assertEqual(len(content), 2)
+
+ @requires_zlib
+ @unittest.skipIf(find_executable('tar') is None or
+ find_executable('gzip') is None,
+ 'requires tar and gzip programs')
+ def test_make_distribution(self):
+ # building a sdist
+ dist, cmd = self.get_cmd()
+
+ # creating a gztar then a tar
+ cmd.formats = ['gztar', 'tar']
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # making sure we have two files
+ dist_folder = join(self.tmp_dir, 'dist')
+ result = sorted(os.listdir(dist_folder))
+ self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz'])
+
+ os.remove(join(dist_folder, 'fake-1.0.tar'))
+ os.remove(join(dist_folder, 'fake-1.0.tar.gz'))
+
+ # now trying a tar then a gztar
+ cmd.formats = ['tar', 'gztar']
+ cmd.finalized = False
+ cmd.ensure_finalized()
+ cmd.run()
+
+ result = sorted(os.listdir(dist_folder))
+ self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz'])
+
+ @requires_zlib
+ def test_add_defaults(self):
+
+ # http://bugs.python.org/issue2279
+
+ # add_default should also include
+ # data_files and package_data
+ dist, cmd = self.get_cmd()
+
+ # filling data_files by pointing files
+ # in package_data
+ dist.package_data = {'': ['*.cfg', '*.dat'],
+ 'somecode': ['*.txt']}
+ self.write_file((self.tmp_dir, 'setup.cfg'), '#')
+ self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#')
+ self.write_file((self.tmp_dir, 'somecode', 'doc.dat'), '#')
+
+ # adding some data in data_files
+ data_dir = join(self.tmp_dir, 'data')
+ os.mkdir(data_dir)
+ self.write_file((data_dir, 'data.dt'), '#')
+ some_dir = join(self.tmp_dir, 'some')
+ os.mkdir(some_dir)
+ self.write_file((self.tmp_dir, 'inroot.txt'), '#')
+ self.write_file((some_dir, 'file.txt'), '#')
+ self.write_file((some_dir, 'other_file.txt'), '#')
+
+ dist.data_files = {'data/data.dt': '{appdata}/data.dt',
+ 'inroot.txt': '{appdata}/inroot.txt',
+ 'some/file.txt': '{appdata}/file.txt',
+ 'some/other_file.txt': '{appdata}/other_file.txt'}
+
+ # adding a script
+ script_dir = join(self.tmp_dir, 'scripts')
+ os.mkdir(script_dir)
+ self.write_file((script_dir, 'script.py'), '#')
+ dist.scripts = [join('scripts', 'script.py')]
+
+ cmd.formats = ['zip']
+ cmd.use_defaults = True
+
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # now let's check what we have
+ dist_folder = join(self.tmp_dir, 'dist')
+ files = os.listdir(dist_folder)
+ self.assertEqual(files, ['fake-1.0.zip'])
+
+ with zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip')) as zip_file:
+ content = zip_file.namelist()
+
+ # Making sure everything was added. This includes 8 code and data
+ # files in addition to PKG-INFO and setup.cfg
+ self.assertEqual(len(content), 10)
+
+ # Checking the MANIFEST
+ with open(join(self.tmp_dir, 'MANIFEST')) as fp:
+ manifest = fp.read()
+ self.assertEqual(manifest, MANIFEST % {'sep': os.sep})
+
+ @requires_zlib
+ def test_metadata_check_option(self):
+ # testing the `check-metadata` option
+ dist, cmd = self.get_cmd(metadata={'name': 'xxx', 'version': 'xxx'})
+
+ # this should cause the check subcommand to log two warnings:
+ # version is invalid, home-page and author are missing
+ cmd.ensure_finalized()
+ cmd.run()
+ warnings = self.get_logs()
+ check_warnings = [msg for msg in warnings if
+ not msg.startswith('sdist:')]
+ self.assertEqual(len(check_warnings), 2, warnings)
+
+ # trying with a complete set of metadata
+ self.loghandler.flush()
+ dist, cmd = self.get_cmd()
+ cmd.ensure_finalized()
+ cmd.metadata_check = False
+ cmd.run()
+ warnings = self.get_logs()
+ self.assertEqual(len(warnings), 2)
+ self.assertIn('using default file list', warnings[0])
+ self.assertIn("'setup.cfg' file not found", warnings[1])
+
+ def test_show_formats(self):
+ with captured_stdout() as stdout:
+ show_formats()
+ stdout = stdout.getvalue()
+
+ # the output should be a header line + one line per format
+ num_formats = len(get_archive_formats())
+ output = [line for line in stdout.split('\n')
+ if line.strip().startswith('--formats=')]
+ self.assertEqual(len(output), num_formats)
+
+ def test_finalize_options(self):
+ dist, cmd = self.get_cmd()
+ cmd.finalize_options()
+
+ # default options set by finalize
+ self.assertEqual(cmd.manifest, 'MANIFEST')
+ self.assertEqual(cmd.dist_dir, 'dist')
+
+ # formats has to be a string splitable on (' ', ',') or
+ # a stringlist
+ cmd.formats = 1
+ self.assertRaises(PackagingOptionError, cmd.finalize_options)
+ cmd.formats = ['zip']
+ cmd.finalize_options()
+
+ # formats has to be known
+ cmd.formats = 'supazipa'
+ self.assertRaises(PackagingOptionError, cmd.finalize_options)
+
+ @requires_zlib
+ def test_template(self):
+ dist, cmd = self.get_cmd()
+ dist.extra_files = ['include yeah']
+ cmd.ensure_finalized()
+ self.write_file((self.tmp_dir, 'yeah'), 'xxx')
+ cmd.run()
+ with open(cmd.manifest) as f:
+ content = f.read()
+
+ self.assertIn('yeah', content)
+
+ @requires_zlib
+ @unittest.skipUnless(UID_GID_SUPPORT, "requires grp and pwd support")
+ @unittest.skipIf(find_executable('tar') is None or
+ find_executable('gzip') is None,
+ 'requires tar and gzip programs')
+ def test_make_distribution_owner_group(self):
+ # building a sdist
+ dist, cmd = self.get_cmd()
+
+ # creating a gztar and specifying the owner+group
+ cmd.formats = ['gztar']
+ cmd.owner = pwd.getpwuid(0)[0]
+ cmd.group = grp.getgrgid(0)[0]
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # making sure we have the good rights
+ archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz')
+ with tarfile.open(archive_name) as archive:
+ for member in archive.getmembers():
+ self.assertEqual(member.uid, 0)
+ self.assertEqual(member.gid, 0)
+
+ # building a sdist again
+ dist, cmd = self.get_cmd()
+
+ # creating a gztar
+ cmd.formats = ['gztar']
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # making sure we have the good rights
+ archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz')
+ with tarfile.open(archive_name) as archive:
+
+ # note that we are not testing the group ownership here
+ # because, depending on the platforms and the container
+ # rights (see #7408)
+ for member in archive.getmembers():
+ self.assertEqual(member.uid, os.getuid())
+
+ @requires_zlib
+ def test_get_file_list(self):
+ # make sure MANIFEST is recalculated
+ dist, cmd = self.get_cmd()
+ # filling data_files by pointing files in package_data
+ dist.package_data = {'somecode': ['*.txt']}
+ self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#')
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # Should produce four lines. Those lines are one comment, one default
+ # (README) and two package files.
+ with open(cmd.manifest) as f:
+ manifest = [line.strip() for line in f.read().split('\n')
+ if line.strip() != '']
+ self.assertEqual(len(manifest), 3)
+
+ # Adding a file
+ self.write_file((self.tmp_dir, 'somecode', 'doc2.txt'), '#')
+
+ # make sure build_py is reinitialized, like a fresh run
+ build_py = dist.get_command_obj('build_py')
+ build_py.finalized = False
+ build_py.ensure_finalized()
+
+ cmd.run()
+
+ with open(cmd.manifest) as f:
+ manifest2 = [line.strip() for line in f.read().split('\n')
+ if line.strip() != '']
+
+ # Do we have the new file in MANIFEST?
+ self.assertEqual(len(manifest2), 4)
+ self.assertIn('doc2.txt', manifest2[-1])
+
+ @requires_zlib
+ def test_manifest_marker(self):
+ # check that autogenerated MANIFESTs have a marker
+ dist, cmd = self.get_cmd()
+ cmd.ensure_finalized()
+ cmd.run()
+
+ with open(cmd.manifest) as f:
+ manifest = [line.strip() for line in f.read().split('\n')
+ if line.strip() != '']
+
+ self.assertEqual(manifest[0],
+ '# file GENERATED by packaging, do NOT edit')
+
+ @requires_zlib
+ def test_manual_manifest(self):
+ # check that a MANIFEST without a marker is left alone
+ dist, cmd = self.get_cmd()
+ cmd.ensure_finalized()
+ self.write_file((self.tmp_dir, cmd.manifest), 'README.manual')
+ cmd.run()
+
+ with open(cmd.manifest) as f:
+ manifest = [line.strip() for line in f.read().split('\n')
+ if line.strip() != '']
+
+ self.assertEqual(manifest, ['README.manual'])
+
+ @requires_zlib
+ def test_manifest_builder(self):
+ dist, cmd = self.get_cmd()
+ cmd.manifest_builders = 'packaging.tests.test_command_sdist.builder'
+ cmd.ensure_finalized()
+ cmd.run()
+ self.assertIn('bah', cmd.filelist.files)
+
+
+def test_suite():
+ return unittest.makeSuite(SDistTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_test.py b/Lib/packaging/tests/test_command_test.py
new file mode 100644