summaryrefslogtreecommitdiffstats
path: root/Python/structmember.c
diff options
context:
space:
mode:
Diffstat (limited to 'Python/structmember.c')
0 files changed, 0 insertions, 0 deletions
oss-git/cpython.git/diff/.hgeol?h=v3.5.0rc1&id=5b74badc8d9409ba45b14c8c469e7deed37cecc6&id2=ddf343855d787520d1ac72c7dcfdeb91e72731da'>.hgeol1
-rw-r--r--.hgignore18
-rw-r--r--.hgtags11
-rw-r--r--.hgtouch3
-rw-r--r--Doc/Makefile47
-rw-r--r--Doc/README.txt48
-rw-r--r--Doc/c-api/arg.rst2
-rw-r--r--Doc/c-api/concrete.rst22
-rw-r--r--Doc/c-api/dict.rst18
-rw-r--r--Doc/c-api/exceptions.rst71
-rw-r--r--Doc/c-api/file.rst2
-rw-r--r--Doc/c-api/import.rst11
-rw-r--r--Doc/c-api/init.rst50
-rw-r--r--Doc/c-api/long.rst2
-rw-r--r--Doc/c-api/mapping.rst6
-rw-r--r--Doc/c-api/memory.rst171
-rw-r--r--Doc/c-api/module.rst11
-rw-r--r--Doc/c-api/object.rst27
-rw-r--r--Doc/c-api/sequence.rst8
-rw-r--r--Doc/c-api/tuple.rst8
-rw-r--r--Doc/c-api/type.rst10
-rw-r--r--Doc/c-api/typeobj.rst70
-rw-r--r--Doc/c-api/unicode.rst11
-rw-r--r--Doc/c-api/veryhigh.rst52
-rw-r--r--Doc/conf.py6
-rw-r--r--Doc/contents.rst12
-rw-r--r--Doc/data/refcounts.dat33
-rw-r--r--Doc/distributing/index.rst143
-rw-r--r--Doc/distutils/apiref.rst13
-rw-r--r--Doc/distutils/builtdist.rst3
-rw-r--r--Doc/distutils/index.rst6
-rw-r--r--Doc/distutils/sourcedist.rst20
-rw-r--r--Doc/extending/embedding.rst8
-rw-r--r--Doc/extending/index.rst42
-rw-r--r--Doc/extending/newtypes.rst24
-rw-r--r--Doc/faq/extending.rst4
-rw-r--r--Doc/faq/gui.rst12
-rw-r--r--Doc/faq/library.rst8
-rw-r--r--Doc/faq/programming.rst14
-rw-r--r--Doc/glossary.rst36
-rw-r--r--Doc/howto/clinic.rst1686
-rw-r--r--Doc/howto/functional.rst144
-rw-r--r--Doc/howto/index.rst1
-rw-r--r--Doc/howto/logging-cookbook.rst8
-rw-r--r--Doc/howto/logging.rst4
-rw-r--r--Doc/howto/regex.rst24
-rw-r--r--Doc/howto/unicode.rst4
-rw-r--r--Doc/howto/urllib2.rst2
-rw-r--r--Doc/includes/email-alternative-new-api.py56
-rw-r--r--Doc/includes/email-dir.py86
-rw-r--r--Doc/includes/email-read-alternative-new-api.py74
-rw-r--r--Doc/includes/email-unpack.py45
-rw-r--r--Doc/includes/mp_benchmarks.py239
-rw-r--r--Doc/includes/mp_newtype.py14
-rw-r--r--Doc/includes/mp_pool.py337
-rw-r--r--Doc/includes/mp_synchronize.py278
-rw-r--r--Doc/includes/mp_webserver.py70
-rw-r--r--Doc/includes/mp_workers.py13
-rw-r--r--Doc/includes/typestruct.h7
-rw-r--r--Doc/install/index.rst6
-rw-r--r--Doc/installing/index.rst210
-rw-r--r--Doc/library/2to3.rst37
-rw-r--r--Doc/library/__main__.rst16
-rw-r--r--Doc/library/abc.rst38
-rw-r--r--Doc/library/aifc.rst19
-rw-r--r--Doc/library/argparse.rst23
-rw-r--r--Doc/library/asynchat.rst5
-rw-r--r--Doc/library/asyncio-dev.rst251
-rw-r--r--Doc/library/asyncio-eventloop.rst668
-rw-r--r--Doc/library/asyncio-protocol.rst502
-rw-r--r--Doc/library/asyncio-stream.rst278
-rw-r--r--Doc/library/asyncio-subprocess.rst270
-rw-r--r--Doc/library/asyncio-sync.rst410
-rw-r--r--Doc/library/asyncio-task.rst545
-rw-r--r--Doc/library/asyncio.rst59
-rw-r--r--Doc/library/asyncore.rst5
-rw-r--r--Doc/library/audioop.rst29
-rw-r--r--Doc/library/base64.rst104
-rw-r--r--Doc/library/bz2.rst15
-rw-r--r--Doc/library/cgi.rst13
-rw-r--r--Doc/library/code.rst5
-rw-r--r--Doc/library/codecs.rst156
-rw-r--r--Doc/library/collections.abc.rst2
-rw-r--r--Doc/library/collections.rst26
-rw-r--r--Doc/library/compileall.rst3
-rw-r--r--Doc/library/concurrency.rst1
-rw-r--r--Doc/library/concurrent.futures.rst3
-rw-r--r--Doc/library/contextlib.rst227
-rw-r--r--Doc/library/curses.rst2
-rw-r--r--Doc/library/datatypes.rst1
-rw-r--r--Doc/library/datetime.rst10
-rw-r--r--Doc/library/dbm.rst38
-rw-r--r--Doc/library/debug.rst1
-rw-r--r--Doc/library/development.rst1
-rw-r--r--Doc/library/difflib.rst12
-rw-r--r--Doc/library/dis.rst253
-rw-r--r--Doc/library/distribution.rst14
-rw-r--r--Doc/library/distutils.rst19
-rw-r--r--Doc/library/doctest.rst28
-rw-r--r--Doc/library/email-examples.rst30
-rw-r--r--Doc/library/email.contentmanager.rst430
-rw-r--r--Doc/library/email.generator.rst10
-rw-r--r--Doc/library/email.message.rst87
-rw-r--r--Doc/library/email.parser.rst41
-rw-r--r--Doc/library/email.policy.rst45
-rw-r--r--Doc/library/email.rst1
-rw-r--r--Doc/library/email.util.rst10
-rw-r--r--Doc/library/ensurepip.rst131
-rw-r--r--Doc/library/enum.rst749
-rw-r--r--Doc/library/exceptions.rst22
-rw-r--r--Doc/library/faulthandler.rst35
-rw-r--r--Doc/library/filecmp.rst32
-rw-r--r--Doc/library/fileinput.rst3
-rw-r--r--Doc/library/filesys.rst1
-rw-r--r--Doc/library/fnmatch.rst2
-rw-r--r--Doc/library/formatter.rst5
-rw-r--r--Doc/library/ftplib.rst55
-rw-r--r--Doc/library/functions.rst77
-rw-r--r--Doc/library/functools.rst179
-rw-r--r--Doc/library/gc.rst48
-rw-r--r--Doc/library/getpass.rst9
-rw-r--r--Doc/library/glob.rst15
-rw-r--r--Doc/library/gzip.rst15
-rw-r--r--Doc/library/hashlib.rst65
-rw-r--r--Doc/library/hmac.rst43
-rw-r--r--Doc/library/html.entities.rst1
-rw-r--r--Doc/library/html.parser.rst39
-rw-r--r--Doc/library/html.rst11
-rw-r--r--Doc/library/http.client.rst64
-rw-r--r--Doc/library/http.server.rst42
-rw-r--r--Doc/library/idle.rst345
-rw-r--r--Doc/library/imaplib.rst30
-rw-r--r--Doc/library/imp.rst65
-rw-r--r--Doc/library/importlib.rst695
-rw-r--r--Doc/library/index.rst1
-rw-r--r--Doc/library/inspect.rst73
-rw-r--r--Doc/library/io.rst57
-rw-r--r--Doc/library/ipaddress.rst30
-rw-r--r--Doc/library/ipc.rst5
-rw-r--r--Doc/library/itertools.rst13
-rw-r--r--Doc/library/json.rst72
-rw-r--r--Doc/library/logging.config.rst54
-rw-r--r--Doc/library/logging.handlers.rst14
-rw-r--r--Doc/library/logging.rst4
-rw-r--r--Doc/library/lzma.rst15
-rw-r--r--Doc/library/marshal.rst9
-rw-r--r--Doc/library/multiprocessing.rst320
-rw-r--r--Doc/library/netrc.rst2
-rw-r--r--Doc/library/nntplib.rst19
-rw-r--r--Doc/library/numeric.rst1
-rw-r--r--Doc/library/operator.rst11
-rw-r--r--Doc/library/optparse.rst4
-rw-r--r--Doc/library/os.path.rst38
-rw-r--r--Doc/library/os.rst278
-rw-r--r--Doc/library/pathlib-inheritance.pngbin0 -> 11280 bytes-rw-r--r--Doc/library/pathlib-inheritance.svg4
-rw-r--r--Doc/library/pathlib.rst919
-rw-r--r--Doc/library/pdb.rst14
-rw-r--r--Doc/library/pickle.rst171
-rw-r--r--Doc/library/pkgutil.rst17
-rw-r--r--Doc/library/plistlib.rst184
-rw-r--r--Doc/library/poplib.rst51
-rw-r--r--Doc/library/pprint.rst254
-rw-r--r--Doc/library/pty.rst3
-rw-r--r--Doc/library/py_compile.rst16
-rw-r--r--Doc/library/pydoc.rst5
-rw-r--r--Doc/library/pyexpat.rst9
-rw-r--r--Doc/library/python.rst1
-rw-r--r--Doc/library/re.rst48
-rw-r--r--Doc/library/readline.rst20
-rw-r--r--Doc/library/resource.rst95
-rw-r--r--Doc/library/rlcompleter.rst16
-rw-r--r--Doc/library/runpy.rst69
-rw-r--r--Doc/library/select.rst68
-rw-r--r--Doc/library/selectors.rst257
-rw-r--r--Doc/library/shelve.rst17
-rw-r--r--Doc/library/shutil.rst22
-rw-r--r--Doc/library/site.rst33
-rw-r--r--Doc/library/smtpd.rst19
-rw-r--r--Doc/library/smtplib.rst39
-rw-r--r--Doc/library/socket.rst102
-rw-r--r--Doc/library/spwd.rst10
-rw-r--r--Doc/library/sqlite3.rst111
-rw-r--r--Doc/library/ssl.rst485
-rw-r--r--Doc/library/stat.rst51
-rw-r--r--Doc/library/statistics.rst418
-rw-r--r--Doc/library/stdtypes.rst6
-rw-r--r--Doc/library/struct.rst20
-rw-r--r--Doc/library/subprocess.rst45
-rw-r--r--Doc/library/sunau.rst14
-rw-r--r--Doc/library/sys.rst52
-rw-r--r--Doc/library/sysconfig.rst2
-rw-r--r--Doc/library/tarfile.rst61
-rw-r--r--Doc/library/test.rst22
-rw-r--r--Doc/library/textwrap.rst73
-rw-r--r--Doc/library/threading.rst9
-rw-r--r--Doc/library/timeit.rst8
-rw-r--r--Doc/library/tkinter.rst28
-rw-r--r--Doc/library/traceback.rst11
-rw-r--r--Doc/library/tracemalloc.rst634
-rw-r--r--Doc/library/tulip_coro.diabin0 -> 4461 bytes-rw-r--r--Doc/library/tulip_coro.pngbin0 -> 45565 bytes-rw-r--r--Doc/library/types.rst53
-rw-r--r--Doc/library/undoc.rst2
-rw-r--r--Doc/library/unicodedata.rst8
-rw-r--r--Doc/library/unittest.mock-examples.rst18
-rw-r--r--Doc/library/unittest.mock.rst31
-rw-r--r--Doc/library/unittest.rst224
-rw-r--r--Doc/library/urllib.error.rst7
-rw-r--r--Doc/library/urllib.request.rst139
-rw-r--r--Doc/library/venv.rst24
-rw-r--r--Doc/library/wave.rst62
-rw-r--r--Doc/library/weakref.rst257
-rw-r--r--Doc/library/webbrowser.rst12
-rw-r--r--Doc/library/xml.etree.elementtree.rst199
-rw-r--r--Doc/library/zipfile.rst58
-rw-r--r--Doc/license.rst76
-rw-r--r--Doc/make.bat138
-rw-r--r--Doc/reference/compound_stmts.rst51
-rw-r--r--Doc/reference/datamodel.rst61
-rw-r--r--Doc/reference/import.rst587
-rw-r--r--Doc/reference/lexical_analysis.rst2
-rw-r--r--Doc/reference/simple_stmts.rst15
-rw-r--r--Doc/tools/sphinx-build.py28
-rw-r--r--Doc/tools/sphinxext/c_annotations.py5
-rw-r--r--Doc/tools/sphinxext/download.html4
-rw-r--r--Doc/tools/sphinxext/indexcontent.html8
-rw-r--r--Doc/tools/sphinxext/indexsidebar.html2
-rw-r--r--Doc/tools/sphinxext/layout.html2
-rw-r--r--Doc/tools/sphinxext/pyspecific.py2
-rw-r--r--Doc/tools/sphinxext/susp-ignored.csv38
-rw-r--r--Doc/tutorial/controlflow.rst2
-rw-r--r--Doc/tutorial/errors.rst4
-rw-r--r--Doc/tutorial/interactive.rst153
-rw-r--r--Doc/tutorial/interpreter.rst35
-rw-r--r--Doc/tutorial/modules.rst45
-rw-r--r--Doc/tutorial/stdlib.rst2
-rw-r--r--Doc/tutorial/stdlib2.rst2
-rw-r--r--Doc/tutorial/whatnow.rst4
-rw-r--r--Doc/using/cmdline.rst95
-rw-r--r--Doc/using/mac.rst6
-rw-r--r--Doc/using/unix.rst2
-rw-r--r--Doc/using/venv-create.inc49
-rw-r--r--Doc/using/windows.rst10
-rw-r--r--Doc/whatsnew/3.4.rst2506
-rw-r--r--Doc/whatsnew/index.rst1
-rw-r--r--Include/Python-ast.h31
-rw-r--r--Include/Python.h1
-rw-r--r--Include/abstract.h50
-rw-r--r--Include/asdl.h15
-rw-r--r--Include/ast.h5
-rw-r--r--Include/bytearrayobject.h11
-rw-r--r--Include/bytesobject.h10
-rw-r--r--Include/compile.h18
-rw-r--r--Include/dictobject.h8
-rw-r--r--Include/fileobject.h5
-rw-r--r--Include/fileutils.h19
-rw-r--r--Include/frameobject.h6
-rw-r--r--Include/genobject.h2
-rw-r--r--Include/grammar.h8
-rw-r--r--Include/import.h24
-rw-r--r--Include/longobject.h19
-rw-r--r--Include/marshal.h5
-rw-r--r--Include/modsupport.h3
-rw-r--r--Include/moduleobject.h1
-rw-r--r--Include/object.h116
-rw-r--r--Include/objimpl.h104
-rw-r--r--Include/opcode.h2
-rw-r--r--Include/osdefs.h8
-rw-r--r--Include/parsetok.h49
-rw-r--r--Include/patchlevel.h6
-rw-r--r--Include/pydebug.h1
-rw-r--r--Include/pyerrors.h44
-rw-r--r--Include/pyhash.h149
-rw-r--r--Include/pymacro.h6
-rw-r--r--Include/pymem.h99
-rw-r--r--Include/pyport.h71
-rw-r--r--Include/pystate.h50
-rw-r--r--Include/pythonrun.h47
-rw-r--r--Include/pytime.h17
-rw-r--r--Include/setobject.h9
-rw-r--r--Include/sliceobject.h3
-rw-r--r--Include/structseq.h2
-rw-r--r--Include/symtable.h10
-rw-r--r--Include/sysmodule.h5
-rw-r--r--Include/token.h2
-rw-r--r--Include/unicodeobject.h91
-rw-r--r--Include/warnings.h17
-rw-r--r--LICENSE4
-rw-r--r--Lib/_bootlocale.py34
-rw-r--r--Lib/_collections_abc.py734
-rw-r--r--Lib/_dummy_thread.py4
-rw-r--r--Lib/_osx_support.py6
-rw-r--r--Lib/_pyio.py209
-rw-r--r--Lib/_sitebuiltins.py103
-rw-r--r--Lib/_strptime.py4
-rw-r--r--Lib/abc.py20
-rw-r--r--Lib/aifc.py41
-rw-r--r--Lib/argparse.py31
-rw-r--r--Lib/ast.py6
-rw-r--r--Lib/asynchat.py9
-rw-r--r--Lib/asyncio/__init__.py45
-rw-r--r--Lib/asyncio/base_events.py828
-rw-r--r--Lib/asyncio/base_subprocess.py159
-rw-r--r--Lib/asyncio/constants.py7
-rw-r--r--Lib/asyncio/events.py485
-rw-r--r--Lib/asyncio/futures.py371
-rw-r--r--Lib/asyncio/locks.py466
-rw-r--r--Lib/asyncio/log.py7
-rw-r--r--Lib/asyncio/proactor_events.py455
-rw-r--r--Lib/asyncio/protocols.py129
-rw-r--r--Lib/asyncio/queues.py288
-rw-r--r--Lib/asyncio/selector_events.py853
-rw-r--r--Lib/asyncio/streams.py468
-rw-r--r--Lib/asyncio/subprocess.py195
-rw-r--r--Lib/asyncio/tasks.py720
-rw-r--r--Lib/asyncio/test_utils.py374
-rw-r--r--Lib/asyncio/transports.py295
-rw-r--r--Lib/asyncio/unix_events.py842
-rw-r--r--Lib/asyncio/windows_events.py489
-rw-r--r--Lib/asyncio/windows_utils.py205
-rw-r--r--Lib/asyncore.py29
-rwxr-xr-xLib/base64.py370
-rw-r--r--Lib/bdb.py36
-rw-r--r--Lib/bz2.py50
-rwxr-xr-xLib/cProfile.py53
-rwxr-xr-xLib/cgi.py15
-rw-r--r--Lib/chunk.py6
-rw-r--r--Lib/code.py2
-rw-r--r--Lib/collections/__init__.py28
-rw-r--r--Lib/collections/abc.py730
-rw-r--r--Lib/colorsys.py18
-rw-r--r--Lib/compileall.py20
-rw-r--r--Lib/concurrent/futures/_base.py9
-rw-r--r--Lib/concurrent/futures/process.py11
-rw-r--r--Lib/concurrent/futures/thread.py2
-rw-r--r--Lib/configparser.py2
-rw-r--r--Lib/contextlib.py71
-rw-r--r--Lib/copyreg.py6
-rw-r--r--Lib/ctypes/__init__.py26
-rw-r--r--Lib/ctypes/test/__init__.py2
-rw-r--r--Lib/ctypes/test/test_checkretval.py2
-rw-r--r--Lib/ctypes/test/test_internals.py19
-rw-r--r--Lib/ctypes/test/test_macholib.py63
-rw-r--r--Lib/ctypes/test/test_win32.py2
-rw-r--r--Lib/ctypes/util.py22
-rw-r--r--Lib/datetime.py395
-rw-r--r--Lib/dbm/__init__.py16
-rw-r--r--Lib/dbm/dumb.py28
-rw-r--r--Lib/decimal.py5
-rw-r--r--Lib/difflib.py36
-rw-r--r--Lib/dis.py396
-rw-r--r--Lib/distutils/__init__.py2
-rw-r--r--Lib/distutils/archive_util.py71
-rw-r--r--Lib/distutils/ccompiler.py5
-rw-r--r--Lib/distutils/cmd.py6
-rw-r--r--Lib/distutils/command/bdist.py16
-rw-r--r--Lib/distutils/command/bdist_dumb.py19
-rw-r--r--Lib/distutils/command/build_ext.py26
-rw-r--r--Lib/distutils/command/build_py.py10
-rw-r--r--Lib/distutils/command/build_scripts.py2
-rw-r--r--Lib/distutils/command/install.py15
-rw-r--r--Lib/distutils/command/install_lib.py6
-rw-r--r--Lib/distutils/command/sdist.py9
-rw-r--r--Lib/distutils/command/upload.py2
-rw-r--r--Lib/distutils/config.py9
-rw-r--r--Lib/distutils/core.py7
-rw-r--r--Lib/distutils/cygwinccompiler.py28
-rw-r--r--Lib/distutils/dir_util.py4
-rw-r--r--Lib/distutils/dist.py35
-rw-r--r--Lib/distutils/emxccompiler.py315
-rw-r--r--Lib/distutils/errors.py4
-rw-r--r--Lib/distutils/file_util.py16
-rw-r--r--Lib/distutils/msvc9compiler.py2
-rw-r--r--Lib/distutils/spawn.py28
-rw-r--r--Lib/distutils/sysconfig.py38
-rw-r--r--Lib/distutils/tests/test_archive_util.py61
-rw-r--r--Lib/distutils/tests/test_bdist_dumb.py5
-rw-r--r--Lib/distutils/tests/test_build_py.py10
-rw-r--r--Lib/distutils/tests/test_dist.py29
-rw-r--r--Lib/distutils/tests/test_install.py6
-rw-r--r--Lib/distutils/tests/test_install_lib.py8
-rw-r--r--Lib/distutils/tests/test_sdist.py54
-rw-r--r--Lib/distutils/tests/test_sysconfig.py48
-rw-r--r--Lib/distutils/tests/test_upload.py9
-rw-r--r--Lib/distutils/tests/test_util.py2
-rw-r--r--Lib/distutils/util.py13
-rw-r--r--Lib/doctest.py76
-rw-r--r--Lib/email/_header_value_parser.py39
-rw-r--r--Lib/email/contentmanager.py249
-rw-r--r--Lib/email/encoders.py17
-rw-r--r--Lib/email/feedparser.py52
-rw-r--r--Lib/email/generator.py13
-rw-r--r--Lib/email/header.py2
-rw-r--r--Lib/email/iterators.py6
-rw-r--r--Lib/email/message.py269
-rw-r--r--Lib/email/mime/text.py1
-rw-r--r--Lib/email/parser.py7
-rw-r--r--Lib/email/policy.py13
-rw-r--r--Lib/email/quoprimime.py1
-rw-r--r--Lib/email/utils.py30
-rw-r--r--Lib/encodings/aliases.py47
-rw-r--r--Lib/encodings/cp037.py1
-rw-r--r--Lib/encodings/cp1125.py698
-rw-r--r--Lib/encodings/cp273.py307
-rw-r--r--Lib/encodings/cp500.py1
-rw-r--r--Lib/encodings/iso8859_1.py1
-rwxr-xr-xLib/encodings/rot_13.py2
-rw-r--r--Lib/ensurepip/__init__.py210
-rw-r--r--Lib/ensurepip/__main__.py4
-rw-r--r--Lib/ensurepip/_bundled/pip-1.5.4-py2.py3-none-any.whlbin0 -> 1169272 bytes-rw-r--r--Lib/ensurepip/_bundled/setuptools-2.1-py2.py3-none-any.whlbin0 -> 540293 bytes-rw-r--r--Lib/ensurepip/_uninstall.py30
-rw-r--r--Lib/enum.py525
-rw-r--r--Lib/filecmp.py27
-rw-r--r--Lib/fileinput.py16
-rw-r--r--Lib/formatter.py3
-rw-r--r--Lib/fractions.py14
-rw-r--r--Lib/ftplib.py188
-rw-r--r--Lib/functools.py422
-rw-r--r--Lib/genericpath.py34
-rw-r--r--Lib/getpass.py8
-rw-r--r--Lib/gettext.py14
-rw-r--r--Lib/glob.py21
-rw-r--r--Lib/gzip.py115
-rw-r--r--Lib/hashlib.py97
-rw-r--r--Lib/hmac.py31
-rw-r--r--Lib/html/__init__.py123
-rw-r--r--Lib/html/parser.py111
-rw-r--r--Lib/http/client.py134
-rw-r--r--Lib/http/cookiejar.py31
-rw-r--r--Lib/http/server.py44
-rw-r--r--Lib/idlelib/Bindings.py32
-rw-r--r--Lib/idlelib/Debugger.py2
-rw-r--r--Lib/idlelib/EditorWindow.py27
-rw-r--r--Lib/idlelib/FileList.py2
-rw-r--r--Lib/idlelib/FormatParagraph.py13
-rw-r--r--Lib/idlelib/GrepDialog.py1
-rw-r--r--Lib/idlelib/IOBinding.py2
-rw-r--r--Lib/idlelib/MultiCall.py36
-rw-r--r--Lib/idlelib/NEWS.txt68
-rw-r--r--Lib/idlelib/PathBrowser.py2
-rwxr-xr-xLib/idlelib/PyShell.py16
-rw-r--r--Lib/idlelib/ScriptBinding.py4
-rw-r--r--Lib/idlelib/TreeWidget.py4
-rw-r--r--Lib/idlelib/ZoomHeight.py2
-rw-r--r--Lib/idlelib/config-main.def2
-rw-r--r--Lib/idlelib/configDialog.py2
-rw-r--r--Lib/idlelib/configHandler.py16
-rw-r--r--Lib/idlelib/help.txt556
-rw-r--r--Lib/idlelib/idle_test/test_calltips.py7
-rw-r--r--Lib/idlelib/idle_test/test_formatparagraph.py10
-rw-r--r--Lib/idlelib/idlever.py2
-rw-r--r--Lib/idlelib/keybindingDialog.py5
-rw-r--r--Lib/idlelib/macosxSupport.py136
-rw-r--r--Lib/idlelib/rpc.py8
-rw-r--r--Lib/idlelib/run.py4
-rw-r--r--Lib/imaplib.py30
-rw-r--r--Lib/imghdr.py2
-rw-r--r--Lib/imp.py224
-rw-r--r--Lib/importlib/__init__.py94
-rw-r--r--Lib/importlib/_bootstrap.py1748
-rw-r--r--Lib/importlib/abc.py367
-rw-r--r--Lib/importlib/machinery.py1
-rw-r--r--Lib/importlib/util.py187
-rw-r--r--Lib/inspect.py1081
-rw-r--r--Lib/io.py10
-rw-r--r--Lib/ipaddress.py93
-rw-r--r--Lib/json/__init__.py30
-rw-r--r--Lib/json/decoder.py28
-rw-r--r--Lib/json/encoder.py56
-rw-r--r--Lib/json/scanner.py4
-rw-r--r--Lib/json/tool.py3
-rwxr-xr-xLib/keyword.py23
-rw-r--r--Lib/lib2to3/Grammar.txt7
-rw-r--r--Lib/lib2to3/btm_utils.py6
-rw-r--r--Lib/lib2to3/fixer_util.py23
-rw-r--r--Lib/lib2to3/fixes/fix_asserts.py34
-rw-r--r--Lib/lib2to3/fixes/fix_intern.py21
-rw-r--r--Lib/lib2to3/fixes/fix_reload.py28
-rw-r--r--Lib/lib2to3/main.py4
-rw-r--r--Lib/lib2to3/pgen2/conv.py4
-rw-r--r--Lib/lib2to3/pgen2/driver.py2
-rw-r--r--Lib/lib2to3/pgen2/grammar.py11
-rwxr-xr-xLib/lib2to3/pgen2/token.py13
-rw-r--r--Lib/lib2to3/pgen2/tokenize.py2
-rw-r--r--Lib/lib2to3/pytree.py9
-rw-r--r--Lib/lib2to3/refactor.py6
-rwxr-xr-xLib/lib2to3/tests/pytree_idempotency.py2
-rw-r--r--Lib/lib2to3/tests/test_fixers.py109
-rw-r--r--Lib/lib2to3/tests/test_parser.py13
-rw-r--r--Lib/linecache.py8
-rw-r--r--Lib/locale.py487
-rw-r--r--Lib/logging/__init__.py118
-rw-r--r--Lib/logging/config.py183
-rw-r--r--Lib/logging/handlers.py127
-rw-r--r--Lib/lzma.py137
-rw-r--r--Lib/macpath.py2
-rw-r--r--Lib/mailbox.py84
-rw-r--r--Lib/mailcap.py2
-rw-r--r--Lib/mimetypes.py2
-rw-r--r--Lib/modulefinder.py19
-rw-r--r--Lib/multiprocessing/__init__.py251
-rw-r--r--Lib/multiprocessing/connection.py152
-rw-r--r--Lib/multiprocessing/context.py348
-rw-r--r--Lib/multiprocessing/dummy/__init__.py31
-rw-r--r--Lib/multiprocessing/dummy/connection.py27
-rw-r--r--Lib/multiprocessing/forking.py474
-rw-r--r--Lib/multiprocessing/forkserver.py267
-rw-r--r--Lib/multiprocessing/heap.py54
-rw-r--r--Lib/multiprocessing/managers.py62
-rw-r--r--Lib/multiprocessing/pool.py143
-rw-r--r--Lib/multiprocessing/popen_fork.py83
-rw-r--r--Lib/multiprocessing/popen_forkserver.py69
-rw-r--r--Lib/multiprocessing/popen_spawn_posix.py69
-rw-r--r--Lib/multiprocessing/popen_spawn_win32.py99
-rw-r--r--Lib/multiprocessing/process.py83
-rw-r--r--Lib/multiprocessing/queues.py106
-rw-r--r--Lib/multiprocessing/reduction.py362
-rw-r--r--Lib/multiprocessing/resource_sharer.py158
-rw-r--r--Lib/multiprocessing/semaphore_tracker.py143
-rw-r--r--Lib/multiprocessing/sharedctypes.py40
-rw-r--r--Lib/multiprocessing/spawn.py287
-rw-r--r--Lib/multiprocessing/synchronize.py111
-rw-r--r--Lib/multiprocessing/util.py54
-rw-r--r--Lib/netrc.py5
-rw-r--r--Lib/nntplib.py18
-rw-r--r--Lib/ntpath.py60
-rw-r--r--Lib/nturl2path.py4
-rw-r--r--Lib/opcode.py17
-rw-r--r--Lib/operator.py411
-rw-r--r--Lib/os.py99
-rw-r--r--Lib/os2emxpath.py158
-rw-r--r--Lib/pathlib.py1288
-rwxr-xr-xLib/pdb.py47
-rw-r--r--Lib/pickle.py862
-rw-r--r--Lib/pickletools.py871
-rw-r--r--Lib/pkgutil.py159
-rw-r--r--Lib/plat-os2emx/IN.py82
-rw-r--r--Lib/plat-os2emx/SOCKET.py106
-rw-r--r--Lib/plat-os2emx/_emx_link.py79
-rw-r--r--Lib/plat-os2emx/grp.py182
-rw-r--r--Lib/plat-os2emx/pwd.py208
-rwxr-xr-xLib/plat-os2emx/regen7
-rwxr-xr-xLib/platform.py311
-rw-r--r--Lib/plistlib.py1111
-rw-r--r--Lib/poplib.py102
-rw-r--r--Lib/posixpath.py57
-rw-r--r--Lib/pprint.py124
-rwxr-xr-xLib/profile.py63
-rw-r--r--Lib/pstats.py12
-rw-r--r--Lib/pty.py22
-rw-r--r--Lib/py_compile.py77
-rw-r--r--Lib/pyclbr.py11
-rwxr-xr-xLib/pydoc.py251
-rw-r--r--Lib/pydoc_data/topics.py20
-rwxr-xr-xLib/quopri.py2
-rw-r--r--Lib/random.py16
-rw-r--r--Lib/re.py29
-rw-r--r--Lib/rlcompleter.py5
-rw-r--r--Lib/runpy.py113
-rw-r--r--Lib/sched.py11
-rw-r--r--Lib/selectors.py525
-rw-r--r--Lib/shelve.py9
-rw-r--r--Lib/shutil.py39
-rw-r--r--Lib/site.py213
-rwxr-xr-xLib/smtpd.py21
-rwxr-xr-xLib/smtplib.py51
-rw-r--r--Lib/sndhdr.py17
-rw-r--r--Lib/socket.py116
-rw-r--r--Lib/socketserver.py9
-rw-r--r--Lib/sqlite3/test/dbapi.py18
-rw-r--r--Lib/sre_compile.py272
-rw-r--r--Lib/sre_constants.py8
-rw-r--r--Lib/sre_parse.py77
-rw-r--r--Lib/ssl.py401
-rw-r--r--Lib/stat.py6
-rw-r--r--Lib/statistics.py595
-rw-r--r--Lib/string.py23
-rw-r--r--Lib/struct.py1
-rw-r--r--Lib/subprocess.py422
-rw-r--r--Lib/sunau.py40
-rwxr-xr-xLib/symbol.py4
-rw-r--r--Lib/symtable.py3
-rw-r--r--Lib/sysconfig.py46
-rwxr-xr-xLib/tabnanny.py2
-rwxr-xr-xLib/tarfile.py149
-rw-r--r--Lib/telnetlib.py275
-rw-r--r--Lib/tempfile.py107
-rw-r--r--Lib/test/__main__.py14
-rw-r--r--Lib/test/_test_multiprocessing.py (renamed from Lib/test/test_multiprocessing.py)602
-rw-r--r--Lib/test/audiodata/pluck-pcm24.aubin0 -> 19866 bytes-rw-r--r--Lib/test/audiotests.py119
-rw-r--r--Lib/test/badsyntax_future10.py3
-rw-r--r--Lib/test/buffer_tests.py16
-rw-r--r--Lib/test/bytecode_helper.py41
-rw-r--r--Lib/test/coding20731.py8
-rw-r--r--Lib/test/datetimetester.py4
-rw-r--r--Lib/test/final_a.py19
-rw-r--r--Lib/test/final_b.py19
-rw-r--r--Lib/test/fork_wait.py2
-rw-r--r--Lib/test/keycert3.pem73
-rw-r--r--Lib/test/keycert4.pem73
-rw-r--r--Lib/test/leakers/test_gestalt.py14
-rw-r--r--Lib/test/lock_tests.py20
-rw-r--r--Lib/test/make_ssl_certs.py118
-rw-r--r--Lib/test/mock_socket.py8
-rw-r--r--Lib/test/mp_fork_bomb.py5
-rw-r--r--Lib/test/multibytecodec_support.py2
-rw-r--r--Lib/test/pickletester.py659
-rw-r--r--Lib/test/pycacert.pem78
-rw-r--r--Lib/test/pycakey.pem28
-rwxr-xr-xLib/test/regrtest.py1153
-rw-r--r--Lib/test/revocation.crl11
-rw-r--r--Lib/test/script_helper.py42
-rw-r--r--Lib/test/sortperf.py6
-rw-r--r--Lib/test/ssl_servers.py10
-rw-r--r--Lib/test/ssltests.py22
-rw-r--r--Lib/test/string_tests.py30
-rw-r--r--Lib/test/subprocessdata/fd_status.py26
-rw-r--r--Lib/test/support/__init__.py324
-rw-r--r--Lib/test/test___all__.py30
-rw-r--r--Lib/test/test__opcode.py23
-rw-r--r--Lib/test/test_abc.py16
-rw-r--r--Lib/test/test_aifc.py26
-rw-r--r--Lib/test/test_argparse.py39
-rw-r--r--Lib/test/test_array.py16
-rw-r--r--Lib/test/test_ast.py100
-rw-r--r--Lib/test/test_asynchat.py35
-rw-r--r--Lib/test/test_asyncio/__init__.py29
-rw-r--r--Lib/test/test_asyncio/__main__.py5
-rw-r--r--Lib/test/test_asyncio/echo.py8
-rw-r--r--Lib/test/test_asyncio/echo2.py6
-rw-r--r--Lib/test/test_asyncio/echo3.py11
-rw-r--r--Lib/test/test_asyncio/keycert3.pem73
-rw-r--r--Lib/test/test_asyncio/pycacert.pem78
-rw-r--r--Lib/test/test_asyncio/ssl_cert.pem15
-rw-r--r--Lib/test/test_asyncio/ssl_key.pem16
-rw-r--r--Lib/test/test_asyncio/test_base_events.py949
-rw-r--r--Lib/test/test_asyncio/test_events.py2061
-rw-r--r--Lib/test/test_asyncio/test_futures.py351
-rw-r--r--Lib/test/test_asyncio/test_locks.py870
-rw-r--r--Lib/test/test_asyncio/test_proactor_events.py490
-rw-r--r--Lib/test/test_asyncio/test_queues.py466
-rw-r--r--Lib/test/test_asyncio/test_selector_events.py1691
-rw-r--r--Lib/test/test_asyncio/test_streams.py588
-rw-r--r--Lib/test/test_asyncio/test_subprocess.py184
-rw-r--r--Lib/test/test_asyncio/test_tasks.py1775
-rw-r--r--Lib/test/test_asyncio/test_transports.py88
-rw-r--r--Lib/test/test_asyncio/test_unix_events.py1579
-rw-r--r--Lib/test/test_asyncio/test_windows_events.py134
-rw-r--r--Lib/test/test_asyncio/test_windows_utils.py160
-rw-r--r--Lib/test/test_asyncore.py25
-rw-r--r--Lib/test/test_atexit.py41
-rw-r--r--Lib/test/test_audioop.py243
-rw-r--r--Lib/test/test_base64.py362
-rw-r--r--Lib/test/test_bisect.py10
-rw-r--r--Lib/test/test_buffer.py6
-rw-r--r--Lib/test/test_builtin.py128
-rw-r--r--Lib/test/test_bytes.py34
-rw-r--r--Lib/test/test_bz2.py218
-rw-r--r--Lib/test/test_capi.py161
-rw-r--r--Lib/test/test_cmd_line.py100
-rw-r--r--Lib/test/test_cmd_line_script.py47
-rw-r--r--Lib/test/test_code_module.py14
-rw-r--r--Lib/test/test_codeccallbacks.py4
-rw-r--r--Lib/test/test_codecs.py389
-rw-r--r--Lib/test/test_coding.py71
-rw-r--r--Lib/test/test_collections.py128
-rw-r--r--Lib/test/test_colorsys.py32
-rw-r--r--Lib/test/test_compileall.py54
-rw-r--r--Lib/test/test_complex.py3
-rw-r--r--Lib/test/test_concurrent_futures.py22
-rw-r--r--Lib/test/test_contextlib.py141
-rw-r--r--Lib/test/test_cprofile.py3
-rw-r--r--Lib/test/test_crypt.py6
-rw-r--r--Lib/test/test_csv.py30
-rw-r--r--Lib/test/test_curses.py34
-rw-r--r--Lib/test/test_dbm.py2
-rw-r--r--Lib/test/test_dbm_dumb.py34
-rw-r--r--Lib/test/test_dbm_gnu.py11
-rw-r--r--Lib/test/test_dbm_ndbm.py13
-rw-r--r--Lib/test/test_decimal.py17
-rw-r--r--Lib/test/test_deque.py4
-rw-r--r--Lib/test/test_descr.py671
-rw-r--r--Lib/test/test_devpoll.py41
-rw-r--r--Lib/test/test_dis.py489
-rw-r--r--Lib/test/test_doctest.py317
-rw-r--r--Lib/test/test_dynamicclassattribute.py304
-rw-r--r--Lib/test/test_email/__init__.py26
-rw-r--r--Lib/test/test_email/test_contentmanager.py796
-rw-r--r--Lib/test/test_email/test_email.py50
-rw-r--r--Lib/test/test_email/test_headerregistry.py14
-rw-r--r--Lib/test/test_email/test_message.py758
-rw-r--r--Lib/test/test_email/test_policy.py10
-rw-r--r--Lib/test/test_email/torture_test.py2
-rw-r--r--Lib/test/test_ensurepip.py345
-rw-r--r--Lib/test/test_enum.py1594
-rw-r--r--Lib/test/test_enumerate.py10
-rw-r--r--Lib/test/test_epoll.py72
-rw-r--r--Lib/test/test_exceptions.py31
-rw-r--r--Lib/test/test_faulthandler.py77
-rw-r--r--Lib/test/test_fcntl.py39
-rw-r--r--Lib/test/test_file.py6
-rw-r--r--Lib/test/test_filecmp.py131
-rw-r--r--Lib/test/test_fileinput.py36
-rw-r--r--Lib/test/test_fileio.py20
-rw-r--r--Lib/test/test_finalization.py522
-rw-r--r--Lib/test/test_float.py1
-rw-r--r--Lib/test/test_fork1.py2
-rw-r--r--Lib/test/test_format.py36
-rw-r--r--Lib/test/test_fractions.py32
-rw-r--r--Lib/test/test_frame.py116
-rw-r--r--Lib/test/test_frozen.py79
-rw-r--r--Lib/test/test_ftplib.py91
-rw-r--r--Lib/test/test_funcattrs.py7
-rw-r--r--Lib/test/test_functools.py945
-rw-r--r--Lib/test/test_future.py8
-rw-r--r--Lib/test/test_gc.py94
-rw-r--r--Lib/test/test_gdb.py31
-rw-r--r--Lib/test/test_generators.py78
-rw-r--r--Lib/test/test_genericpath.py82
-rw-r--r--Lib/test/test_genexps.py4
-rw-r--r--Lib/test/test_getargs2.py112
-rw-r--r--Lib/test/test_getpass.py10
-rw-r--r--Lib/test/test_glob.py22
-rw-r--r--Lib/test/test_grammar.py7
-rw-r--r--Lib/test/test_gzip.py51
-rw-r--r--Lib/test/test_hash.py146
-rw-r--r--Lib/test/test_hashlib.py154
-rw-r--r--Lib/test/test_hmac.py99
-rw-r--r--Lib/test/test_html.py86
-rw-r--r--Lib/test/test_htmlparser.py98
-rw-r--r--Lib/test/test_httplib.py80
-rw-r--r--Lib/test/test_httpservers.py21
-rw-r--r--Lib/test/test_idle.py14
-rw-r--r--Lib/test/test_imaplib.py34
-rw-r--r--Lib/test/test_imp.py89
-rw-r--r--Lib/test/test_import.py102
-rw-r--r--Lib/test/test_importhooks.py250
-rw-r--r--Lib/test/test_importlib/__main__.py13
-rw-r--r--Lib/test/test_importlib/abc.py9
-rw-r--r--Lib/test/test_importlib/builtin/test_finder.py60
-rw-r--r--Lib/test/test_importlib/builtin/test_loader.py47
-rw-r--r--Lib/test/test_importlib/extension/test_case_sensitivity.py29
-rw-r--r--Lib/test/test_importlib/extension/test_finder.py25
-rw-r--r--Lib/test/test_importlib/extension/test_loader.py30
-rw-r--r--Lib/test/test_importlib/extension/test_path_hook.py20
-rw-r--r--Lib/test/test_importlib/extension/util.py1
-rw-r--r--Lib/test/test_importlib/frozen/test_finder.py49
-rw-r--r--Lib/test/test_importlib/frozen/test_loader.py156
-rw-r--r--Lib/test/test_importlib/import_/test___loader__.py70
-rw-r--r--Lib/test/test_importlib/import_/test___package__.py56
-rw-r--r--Lib/test/test_importlib/import_/test_api.py73
-rw-r--r--Lib/test/test_importlib/import_/test_caching.py32
-rw-r--r--Lib/test/test_importlib/import_/test_fromlist.py36
-rw-r--r--Lib/test/test_importlib/import_/test_meta_path.py66
-rw-r--r--Lib/test/test_importlib/import_/test_packages.py44
-rw-r--r--Lib/test/test_importlib/import_/test_path.py76
-rw-r--r--Lib/test/test_importlib/import_/test_relative_imports.py53
-rw-r--r--Lib/test/test_importlib/import_/util.py20
-rw-r--r--Lib/test/test_importlib/namespace_pkgs/both_portions/foo/one.py (renamed from Lib/test/namespace_pkgs/both_portions/foo/one.py)0
-rw-r--r--Lib/test/test_importlib/namespace_pkgs/both_portions/foo/two.py (renamed from Lib/test/namespace_pkgs/both_portions/foo/two.py)0
-rw-r--r--Lib/test/test_importlib/namespace_pkgs/missing_directory.zip (renamed from Lib/test/namespace_pkgs/missing_directory.zip)bin515 -> 515 bytes-rw-r--r--Lib/test/test_importlib/namespace_pkgs/module_and_namespace_package/a_test.py (renamed from Lib/test/namespace_pkgs/module_and_namespace_package/a_test.py)0
-rw-r--r--Lib/test/test_importlib/namespace_pkgs/module_and_namespace_package/a_test/empty (renamed from Lib/test/namespace_pkgs/module_and_namespace_package/a_test/empty)0
-rw-r--r--Lib/test/test_importlib/namespace_pkgs/nested_portion1.zip (renamed from Lib/test/namespace_pkgs/nested_portion1.zip)bin556 -> 556 bytes-rw-r--r--Lib/test/test_importlib/namespace_pkgs/not_a_namespace_pkg/foo/__init__.py (renamed from Lib/test/namespace_pkgs/not_a_namespace_pkg/foo/__init__.py)0
-rw-r--r--Lib/test/test_importlib/namespace_pkgs/not_a_namespace_pkg/foo/one.py (renamed from Lib/test/namespace_pkgs/not_a_namespace_pkg/foo/one.py)0
-rw-r--r--Lib/test/test_importlib/namespace_pkgs/portion1/foo/one.py (renamed from Lib/test/namespace_pkgs/portion1/foo/one.py)0
-rw-r--r--Lib/test/test_importlib/namespace_pkgs/portion2/foo/two.py (renamed from Lib/test/namespace_pkgs/portion2/foo/two.py)0
-rw-r--r--Lib/test/test_importlib/namespace_pkgs/project1/parent/child/one.py (renamed from Lib/test/namespace_pkgs/project1/parent/child/one.py)0
-rw-r--r--Lib/test/test_importlib/namespace_pkgs/project2/parent/child/two.py (renamed from Lib/test/namespace_pkgs/project2/parent/child/two.py)0
-rw-r--r--Lib/test/test_importlib/namespace_pkgs/project3/parent/child/three.py (renamed from Lib/test/namespace_pkgs/project3/parent/child/three.py)0
-rw-r--r--Lib/test/test_importlib/namespace_pkgs/top_level_portion1.zip (renamed from Lib/test/namespace_pkgs/top_level_portion1.zip)bin332 -> 332 bytes-rw-r--r--Lib/test/test_importlib/source/test_abc_loader.py906
-rw-r--r--Lib/test/test_importlib/source/test_case_sensitivity.py53
-rw-r--r--Lib/test/test_importlib/source/test_file_loader.py230
-rw-r--r--Lib/test/test_importlib/source/test_finder.py83
-rw-r--r--Lib/test/test_importlib/source/test_path_hook.py18
-rw-r--r--Lib/test/test_importlib/source/test_source_encoding.py66
-rw-r--r--Lib/test/test_importlib/source/util.py1
-rw-r--r--Lib/test/test_importlib/test_abc.py930
-rw-r--r--Lib/test/test_importlib/test_api.py330
-rw-r--r--Lib/test/test_importlib/test_locks.py71
-rw-r--r--Lib/test/test_importlib/test_namespace_pkgs.py (renamed from Lib/test/test_namespace_pkgs.py)13
-rw-r--r--Lib/test/test_importlib/test_spec.py957
-rw-r--r--Lib/test/test_importlib/test_util.py450
-rw-r--r--Lib/test/test_importlib/test_windows.py29
-rw-r--r--Lib/test/test_importlib/util.py105
-rw-r--r--Lib/test/test_index.py3
-rw-r--r--Lib/test/test_inspect.py941
-rw-r--r--Lib/test/test_int.py50
-rw-r--r--Lib/test/test_io.py170
-rw-r--r--Lib/test/test_ioctl.py6
-rw-r--r--Lib/test/test_ipaddress.py12
-rw-r--r--Lib/test/test_iterlen.py62
-rw-r--r--Lib/test/test_itertools.py79
-rw-r--r--Lib/test/test_json/test_decode.py23
-rw-r--r--Lib/test/test_json/test_enum.py120
-rw-r--r--Lib/test/test_json/test_fail.py89
-rw-r--r--Lib/test/test_json/test_indent.py4
-rw-r--r--Lib/test/test_keyword.py138
-rw-r--r--Lib/test/test_keywordonlyarg.py12
-rw-r--r--Lib/test/test_kqueue.py29
-rw-r--r--Lib/test/test_largefile.py2
-rw-r--r--Lib/test/test_locale.py42
-rw-r--r--Lib/test/test_logging.py472
-rw-r--r--Lib/test/test_long.py2
-rw-r--r--Lib/test/test_lzma.py46
-rw-r--r--Lib/test/test_mailbox.py2
-rw-r--r--Lib/test/test_marshal.py163
-rw-r--r--Lib/test/test_memoryio.py12
-rw-r--r--Lib/test/test_memoryview.py9
-rw-r--r--Lib/test/test_minidom.py69
-rw-r--r--Lib/test/test_mmap.py26
-rw-r--r--Lib/test/test_module.py64
-rw-r--r--Lib/test/test_multibytecodec.py73
-rw-r--r--Lib/test/test_multiprocessing_fork.py7
-rw-r--r--Lib/test/test_multiprocessing_forkserver.py7
-rw-r--r--Lib/test/test_multiprocessing_main_handling.py285
-rw-r--r--Lib/test/test_multiprocessing_spawn.py7
-rw-r--r--Lib/test/test_nntplib.py2
-rw-r--r--Lib/test/test_normalization.py2
-rw-r--r--Lib/test/test_ntpath.py34
-rw-r--r--Lib/test/test_openpty.py2
-rw-r--r--Lib/test/test_operator.py122
-rw-r--r--Lib/test/test_optparse.py9
-rw-r--r--Lib/test/test_os.py372
-rw-r--r--Lib/test/test_ossaudiodev.py4
-rw-r--r--Lib/test/test_pathlib.py1870
-rw-r--r--Lib/test/test_pdb.py348
-rw-r--r--Lib/test/test_peepholer.py295
-rw-r--r--Lib/test/test_pep247.py12
-rw-r--r--Lib/test/test_pep277.py4
-rw-r--r--Lib/test/test_pep3151.py2
-rw-r--r--Lib/test/test_pep352.py5
-rw-r--r--Lib/test/test_pep380.py19
-rw-r--r--Lib/test/test_pickle.py4
-rw-r--r--Lib/test/test_pkg.py20
-rw-r--r--Lib/test/test_pkgimport.py2
-rw-r--r--Lib/test/test_pkgutil.py81
-rw-r--r--Lib/test/test_plistlib.py533
-rw-r--r--Lib/test/test_poll.py20
-rw-r--r--Lib/test/test_poplib.py190
-rw-r--r--Lib/test/test_posix.py85
-rw-r--r--Lib/test/test_posixpath.py62
-rw-r--r--Lib/test/test_pprint.py50
-rw-r--r--Lib/test/test_print.py74
-rw-r--r--Lib/test/test_profile.py29
-rw-r--r--Lib/test/test_pty.py2
-rw-r--r--Lib/test/test_py_compile.py46
-rw-r--r--Lib/test/test_pyclbr.py4
-rw-r--r--Lib/test/test_pydoc.py327
-rw-r--r--Lib/test/test_pyexpat.py12
-rw-r--r--Lib/test/test_random.py207
-rw-r--r--Lib/test/test_range.py9
-rw-r--r--Lib/test/test_re.py240
-rw-r--r--Lib/test/test_regrtest.py275
-rw-r--r--Lib/test/test_reprlib.py6
-rw-r--r--Lib/test/test_resource.py32
-rw-r--r--Lib/test/test_runpy.py183
-rw-r--r--Lib/test/test_sched.py5
-rw-r--r--Lib/test/test_scope.py17
-rw-r--r--Lib/test/test_select.py4
-rw-r--r--Lib/test/test_selectors.py453
-rw-r--r--Lib/test/test_set.py2
-rw-r--r--Lib/test/test_shelve.py13
-rw-r--r--Lib/test/test_shutil.py43
-rw-r--r--Lib/test/test_signal.py60
-rw-r--r--Lib/test/test_site.py42
-rw-r--r--Lib/test/test_slice.py114
-rw-r--r--Lib/test/test_smtplib.py36
-rw-r--r--Lib/test/test_smtpnet.py30
-rw-r--r--Lib/test/test_sndhdr.py2
-rw-r--r--Lib/test/test_socket.py394
-rw-r--r--Lib/test/test_socketserver.py20
-rw-r--r--Lib/test/test_source_encoding.py (renamed from Lib/test/test_pep263.py)77
-rw-r--r--Lib/test/test_ssl.py840
-rw-r--r--Lib/test/test_stat.py61
-rw-r--r--Lib/test/test_statistics.py1574
-rw-r--r--Lib/test/test_strftime.py23
-rw-r--r--Lib/test/test_string.py17
-rw-r--r--Lib/test/test_strtod.py2
-rw-r--r--Lib/test/test_struct.py89
-rw-r--r--Lib/test/test_structseq.py2
-rw-r--r--Lib/test/test_subprocess.py259
-rw-r--r--Lib/test/test_sunau.py28
-rw-r--r--Lib/test/test_sundry.py37
-rw-r--r--Lib/test/test_super.py33
-rw-r--r--Lib/test/test_support.py1
-rw-r--r--Lib/test/test_syntax.py4
-rw-r--r--Lib/test/test_sys.py126
-rw-r--r--Lib/test/test_sysconfig.py66
-rw-r--r--Lib/test/test_syslog.py4
-rw-r--r--Lib/test/test_tarfile.py191
-rw-r--r--Lib/test/test_tcl.py34
-rw-r--r--Lib/test/test_telnetlib.py106
-rw-r--r--Lib/test/test_tempfile.py57
-rw-r--r--Lib/test/test_textwrap.py159
-rw-r--r--Lib/test/test_thread.py2
-rw-r--r--Lib/test/test_threaded_import.py14
-rw-r--r--Lib/test/test_threading.py454
-rw-r--r--Lib/test/test_threadsignals.py8
-rw-r--r--Lib/test/test_time.py148
-rw-r--r--Lib/test/test_timeout.py2
-rw-r--r--Lib/test/test_tk.py3
-rw-r--r--Lib/test/test_tools.py9
-rw-r--r--Lib/test/test_traceback.py82
-rw-r--r--Lib/test/test_tracemalloc.py820
-rw-r--r--Lib/test/test_ttk_guionly.py3
-rw-r--r--Lib/test/test_types.py37
-rw-r--r--Lib/test/test_ucn.py2
-rw-r--r--Lib/test/test_unicode.py424
-rw-r--r--Lib/test/test_unicodedata.py4
-rw-r--r--Lib/test/test_urllib.py103
-rw-r--r--Lib/test/test_urllib2.py173
-rw-r--r--Lib/test/test_urllib2_localnet.py49
-rw-r--r--Lib/test/test_urllib2net.py54
-rw-r--r--Lib/test/test_urllib_response.py65
-rw-r--r--Lib/test/test_urllibnet.py55
-rw-r--r--Lib/test/test_urlparse.py8
-rw-r--r--Lib/test/test_userdict.py9
-rw-r--r--Lib/test/test_venv.py218
-rw-r--r--Lib/test/test_wait3.py12
-rw-r--r--Lib/test/test_warnings.py32
-rw-r--r--Lib/test/test_wave.py21
-rw-r--r--Lib/test/test_weakref.py316
-rw-r--r--Lib/test/test_winreg.py28
-rw-r--r--Lib/test/test_winsound.py2
-rw-r--r--Lib/test/test_xml_etree.py200
-rw-r--r--Lib/test/test_xml_etree_c.py9
-rw-r--r--Lib/test/test_xmlrpc.py74
-rw-r--r--Lib/test/test_xmlrpc_net.py30
-rw-r--r--Lib/test/test_zipfile.py254
-rw-r--r--Lib/test/test_zipfile64.py4
-rw-r--r--Lib/test/test_zipimport.py37
-rw-r--r--Lib/test/test_zipimport_support.py6
-rw-r--r--Lib/test/tf_inherit_check.py2
-rw-r--r--Lib/textwrap.py73
-rw-r--r--Lib/threading.py191
-rwxr-xr-xLib/timeit.py46
-rw-r--r--Lib/tkinter/__init__.py47
-rw-r--r--Lib/tkinter/filedialog.py4
-rw-r--r--Lib/tkinter/test/support.py46
-rw-r--r--Lib/tkinter/test/test_tkinter/test_geometry_managers.py902
-rw-r--r--Lib/tkinter/test/test_tkinter/test_widgets.py4
-rw-r--r--Lib/tkinter/test/widget_tests.py11
-rw-r--r--Lib/tkinter/tix.py2
-rw-r--r--Lib/token.py6
-rw-r--r--Lib/tokenize.py2
-rwxr-xr-xLib/trace.py10
-rw-r--r--Lib/traceback.py259
-rw-r--r--Lib/tracemalloc.py487
-rw-r--r--Lib/turtle.py29
-rwxr-xr-xLib/turtledemo/__main__.py2
-rw-r--r--Lib/types.py57
-rw-r--r--Lib/unittest/__main__.py3
-rw-r--r--Lib/unittest/case.py396
-rw-r--r--Lib/unittest/loader.py76
-rw-r--r--Lib/unittest/main.py247
-rw-r--r--Lib/unittest/mock.py284
-rw-r--r--Lib/unittest/result.py27
-rw-r--r--Lib/unittest/suite.py31
-rw-r--r--Lib/unittest/test/__main__.py18
-rw-r--r--Lib/unittest/test/support.py38
-rw-r--r--Lib/unittest/test/test_assertions.py35
-rw-r--r--Lib/unittest/test/test_break.py4
-rw-r--r--Lib/unittest/test/test_case.py267
-rw-r--r--Lib/unittest/test/test_discovery.py169
-rw-r--r--Lib/unittest/test/test_functiontestcase.py4
-rw-r--r--Lib/unittest/test/test_loader.py4
-rw-r--r--Lib/unittest/test/test_program.py72
-rw-r--r--Lib/unittest/test/test_result.py92
-rw-r--r--Lib/unittest/test/test_runner.py16
-rw-r--r--Lib/unittest/test/test_setups.py7
-rw-r--r--Lib/unittest/test/test_skipping.py80
-rw-r--r--Lib/unittest/test/test_suite.py81
-rw-r--r--Lib/unittest/test/testmock/__main__.py18
-rw-r--r--Lib/unittest/test/testmock/testcallable.py4
-rw-r--r--Lib/unittest/test/testmock/testhelpers.py59
-rw-r--r--Lib/unittest/test/testmock/testmagicmethods.py25
-rw-r--r--Lib/unittest/test/testmock/testmock.py165
-rw-r--r--Lib/unittest/test/testmock/testpatch.py18
-rw-r--r--Lib/unittest/test/testmock/testwith.py83
-rw-r--r--Lib/unittest/util.py37
-rw-r--r--Lib/urllib/error.py14
-rw-r--r--Lib/urllib/parse.py18
-rw-r--r--Lib/urllib/request.py247
-rw-r--r--Lib/urllib/response.py61
-rw-r--r--Lib/uuid.py11
-rw-r--r--Lib/venv/__init__.py93
-rw-r--r--Lib/venv/scripts/nt/Activate.ps145
-rw-r--r--Lib/venv/scripts/nt/Deactivate.ps119
-rw-r--r--Lib/venv/scripts/nt/activate.bat2
-rw-r--r--Lib/venv/scripts/posix/activate4
-rw-r--r--Lib/venv/scripts/posix/activate.csh37
-rw-r--r--Lib/venv/scripts/posix/activate.fish74
-rw-r--r--Lib/warnings.py8
-rw-r--r--Lib/wave.py88
-rw-r--r--Lib/weakref.py198
-rwxr-xr-xLib/webbrowser.py95
-rw-r--r--Lib/wsgiref/__init__.py2
-rw-r--r--Lib/xml/dom/expatbuilder.py14
-rw-r--r--Lib/xml/etree/ElementInclude.py6
-rw-r--r--Lib/xml/etree/ElementPath.py15
-rw-r--r--Lib/xml/etree/ElementTree.py1431
-rw-r--r--Lib/xmlrpc/client.py10
-rw-r--r--Lib/xmlrpc/server.py7
-rw-r--r--Lib/zipfile.py202
-rw-r--r--Mac/BuildScript/README.txt50
-rwxr-xr-xMac/BuildScript/build-installer.py129
-rw-r--r--Mac/BuildScript/resources/ReadMe.txt30
-rw-r--r--Mac/BuildScript/resources/Welcome.rtf18
-rwxr-xr-xMac/BuildScript/scripts/postflight.ensurepip65
-rw-r--r--Mac/Makefile.in129
-rw-r--r--Mac/README10
-rw-r--r--Mac/Tools/Doc/HelpIndexingTool/Help_Indexing_Tool_Suite.py110
-rw-r--r--Mac/Tools/Doc/HelpIndexingTool/Miscellaneous_Standards.py49
-rw-r--r--Mac/Tools/Doc/HelpIndexingTool/Required_Suite.py32
-rw-r--r--Mac/Tools/Doc/HelpIndexingTool/Standard_Suite.py343
-rw-r--r--Mac/Tools/Doc/HelpIndexingTool/__init__.py78
-rw-r--r--Mac/Tools/Doc/HelpIndexingTool/odds_and_ends.py49
-rw-r--r--Mac/Tools/Doc/README35
-rw-r--r--Mac/Tools/Doc/setup.py213
-rwxr-xr-xMac/Tools/bundlebuilder.py2
-rwxr-xr-xMac/Tools/plistlib_generate_testdata.py103
-rw-r--r--Makefile.pre.in211
-rw-r--r--Misc/ACKS75
-rw-r--r--Misc/HISTORY4101
-rw-r--r--Misc/NEWS3721
-rw-r--r--Misc/README4
-rw-r--r--Misc/RPM/python-3.4.spec (renamed from Misc/RPM/python-3.3.spec)7
-rw-r--r--Misc/SpecialBuilds.txt12
-rw-r--r--Misc/TextMate/Python-Dev.tmbundle/Commands/2 to 3 - Module Deletion.tmCommand64
-rw-r--r--Misc/TextMate/Python-Dev.tmbundle/Commands/Build Docs.tmCommand23
-rw-r--r--Misc/TextMate/Python-Dev.tmbundle/Commands/Build.tmCommand25
-rw-r--r--Misc/TextMate/Python-Dev.tmbundle/Commands/Go to Issue.tmCommand20
-rw-r--r--Misc/TextMate/Python-Dev.tmbundle/Commands/Open Docs.tmCommand32
-rw-r--r--Misc/TextMate/Python-Dev.tmbundle/Commands/Open PEP.tmCommand23
-rw-r--r--Misc/TextMate/Python-Dev.tmbundle/Snippets/2 to 3 - Module Deletion (docs).tmSnippet16
-rw-r--r--Misc/TextMate/Python-Dev.tmbundle/info.plist37
-rw-r--r--Misc/Vim/python.vim148
-rw-r--r--Misc/Vim/syntax_test.py62
-rw-r--r--Misc/Vim/vim_syntax.py229
-rw-r--r--Misc/Vim/vimrc87
-rw-r--r--Misc/coverity_model.c132
-rw-r--r--Misc/python-config.in2
-rw-r--r--Misc/python-config.sh.in111
-rw-r--r--Misc/python-wing5.wpr18
-rw-r--r--Misc/python.man14
-rw-r--r--Modules/Setup.dist22
-rw-r--r--Modules/_bisectmodule.c5
-rw-r--r--Modules/_bz2module.c196
-rw-r--r--Modules/_codecsmodule.c6
-rw-r--r--Modules/_collectionsmodule.c412
-rw-r--r--Modules/_cryptmodule.c82
-rw-r--r--Modules/_csv.c18
-rw-r--r--Modules/_ctypes/_ctypes.c80
-rw-r--r--Modules/_ctypes/callbacks.c23
-rw-r--r--Modules/_ctypes/callproc.c105
-rw-r--r--Modules/_ctypes/libffi.diff36
-rw-r--r--Modules/_ctypes/libffi/src/dlmalloc.c8
-rw-r--r--Modules/_ctypes/libffi/testsuite/libffi.call/a.out0
-rw-r--r--Modules/_ctypes/libffi_osx/ffi.c11
-rw-r--r--Modules/_ctypes/libffi_osx/x86/x86-ffi64.c11
-rw-r--r--Modules/_ctypes/stgdict.c4
-rw-r--r--Modules/_curses_panel.c8
-rw-r--r--Modules/_cursesmodule.c258
-rw-r--r--Modules/_datetimemodule.c191
-rw-r--r--Modules/_dbmmodule.c196
-rw-r--r--Modules/_decimal/_decimal.c13
-rw-r--r--Modules/_elementtree.c426
-rw-r--r--Modules/_freeze_importlib.c6
-rw-r--r--Modules/_gdbmmodule.c38
-rw-r--r--Modules/_gestalt.c84
-rw-r--r--Modules/_hashopenssl.c254
-rw-r--r--Modules/_heapqmodule.c21
-rw-r--r--Modules/_io/_iomodule.c49
-rw-r--r--Modules/_io/_iomodule.h7
-rw-r--r--Modules/_io/bufferedio.c89
-rw-r--r--Modules/_io/fileio.c209
-rw-r--r--Modules/_io/iobase.c133
-rw-r--r--Modules/_io/textio.c114
-rw-r--r--Modules/_json.c169
-rw-r--r--Modules/_localemodule.c39
-rw-r--r--Modules/_lsprof.c32
-rw-r--r--Modules/_lzmamodule.c377
-rw-r--r--Modules/_multiprocessing/multiprocessing.c32
-rw-r--r--Modules/_multiprocessing/multiprocessing.h16
-rw-r--r--Modules/_multiprocessing/semaphore.c98
-rw-r--r--Modules/_opcode.c109
-rw-r--r--Modules/_operator.c (renamed from Modules/operator.c)125
-rw-r--r--Modules/_pickle.c3194
-rw-r--r--Modules/_posixsubprocess.c184
-rw-r--r--Modules/_randommodule.c109
-rw-r--r--Modules/_sqlite/cache.c1
-rw-r--r--Modules/_sqlite/connection.c109
-rw-r--r--Modules/_sqlite/cursor.c17
-rw-r--r--Modules/_sqlite/module.c16
-rw-r--r--Modules/_sqlite/prepare_protocol.c1
-rw-r--r--Modules/_sqlite/row.c14
-rw-r--r--Modules/_sqlite/sqlitecompat.h63
-rw-r--r--Modules/_sqlite/statement.c33
-rw-r--r--Modules/_sqlite/util.c2
-rw-r--r--Modules/_sre.c1911
-rw-r--r--Modules/_ssl.c1458
-rw-r--r--Modules/_stat.c563
-rw-r--r--Modules/_struct.c359
-rw-r--r--Modules/_testbuffer.c62
-rw-r--r--Modules/_testcapimodule.c486
-rw-r--r--Modules/_testembed.c94
-rw-r--r--Modules/_testimportmultiple.c57
-rw-r--r--Modules/_threadmodule.c98
-rw-r--r--Modules/_tkinter.c666
-rw-r--r--Modules/_tracemalloc.c1429
-rw-r--r--Modules/_weakref.c63
-rw-r--r--Modules/_winapi.c44
-rw-r--r--Modules/arraymodule.c122
-rw-r--r--Modules/atexitmodule.c121
-rw-r--r--Modules/audioop.c1299
-rw-r--r--Modules/binascii.c658
-rw-r--r--Modules/cjkcodecs/_codecs_cn.c293
-rw-r--r--Modules/cjkcodecs/_codecs_hk.c93
-rw-r--r--Modules/cjkcodecs/_codecs_iso2022.c475
-rw-r--r--Modules/cjkcodecs/_codecs_jp.c459
-rw-r--r--Modules/cjkcodecs/_codecs_kr.c204
-rw-r--r--Modules/cjkcodecs/_codecs_tw.c93
-rw-r--r--Modules/cjkcodecs/alg_jisx0201.h75
-rw-r--r--Modules/cjkcodecs/cjkcodecs.h203
-rw-r--r--Modules/cjkcodecs/emu_jisx0213_2000.h33
-rw-r--r--Modules/cjkcodecs/mappings_cn.h2
-rw-r--r--Modules/cjkcodecs/mappings_jisx0213_pair.h2
-rw-r--r--Modules/cjkcodecs/multibytecodec.c344
-rw-r--r--Modules/cjkcodecs/multibytecodec.h27
-rw-r--r--Modules/clinic/_bz2module.c.h162
-rw-r--r--Modules/clinic/_lzmamodule.c.h245
-rw-r--r--Modules/clinic/_pickle.c.h491
-rw-r--r--Modules/clinic/audioop.c.h889
-rw-r--r--Modules/clinic/binascii.c.h478
-rw-r--r--Modules/clinic/zlibmodule.c.h453
-rw-r--r--Modules/cmathmodule.c2
-rw-r--r--Modules/expat/xmlparse.c12
-rw-r--r--Modules/expat/xmlrole.c4
-rw-r--r--Modules/expat/xmltok.c4
-rw-r--r--Modules/faulthandler.c35
-rw-r--r--Modules/fcntlmodule.c178
-rw-r--r--Modules/fpectlmodule.c29
-rw-r--r--Modules/gcmodule.c289
-rw-r--r--Modules/getpath.c136
-rw-r--r--Modules/grpmodule.c16
-rw-r--r--Modules/hashlib.h33
-rw-r--r--Modules/hashtable.c518
-rw-r--r--Modules/hashtable.h128
-rw-r--r--Modules/itertoolsmodule.c80
-rw-r--r--Modules/main.c102
-rw-r--r--Modules/md5module.c21
-rw-r--r--Modules/mmapmodule.c22
-rw-r--r--Modules/ossaudiodev.c13
-rw-r--r--Modules/overlapped.c1380
-rw-r--r--Modules/parsermodule.c154
-rw-r--r--Modules/posixmodule.c2710
-rw-r--r--Modules/pwdmodule.c17
-rw-r--r--Modules/pyexpat.c69
-rw-r--r--Modules/python.c27
-rw-r--r--Modules/readline.c170
-rw-r--r--Modules/resource.c191
-rw-r--r--Modules/selectmodule.c271
-rw-r--r--Modules/sha1module.c21
-rw-r--r--Modules/sha256module.c44
-rw-r--r--Modules/sha512module.c43
-rw-r--r--Modules/signalmodule.c47
-rw-r--r--Modules/socketmodule.c1351
-rw-r--r--Modules/socketmodule.h6
-rw-r--r--Modules/spwdmodule.c22
-rw-r--r--Modules/sre.h10
-rw-r--r--Modules/sre_constants.h2
-rw-r--r--Modules/sre_lib.h1343
-rw-r--r--Modules/symtablemodule.c42
-rw-r--r--Modules/syslogmodule.c66
-rw-r--r--Modules/timemodule.c103
-rw-r--r--Modules/unicodedata.c77
-rw-r--r--Modules/unicodedata_db.h1988
-rw-r--r--Modules/unicodename_db.h33019
-rw-r--r--Modules/xxlimited.c2
-rw-r--r--Modules/zipimport.c68
-rw-r--r--Modules/zlib/ChangeLog266
-rw-r--r--Modules/zlib/FAQ12
-rw-r--r--Modules/zlib/INDEX13
-rw-r--r--Modules/zlib/Makefile.in111
-rw-r--r--Modules/zlib/README24
-rw-r--r--Modules/zlib/adler32.c68
-rw-r--r--Modules/zlib/algorithm.txt4
-rw-r--r--Modules/zlib/compress.c2
-rwxr-xr-xModules/zlib/configure521
-rw-r--r--Modules/zlib/crc32.c83
-rw-r--r--Modules/zlib/crc32.h2
-rw-r--r--Modules/zlib/deflate.c267
-rw-r--r--Modules/zlib/deflate.h12
-rw-r--r--Modules/zlib/example.c90
-rw-r--r--Modules/zlib/gzguts.h103
-rw-r--r--Modules/zlib/gzio.c1026
-rw-r--r--Modules/zlib/gzlib.c197
-rw-r--r--Modules/zlib/gzread.c431
-rw-r--r--Modules/zlib/gzwrite.c196
-rw-r--r--Modules/zlib/infback.c16
-rw-r--r--Modules/zlib/inffast.c6
-rw-r--r--Modules/zlib/inffixed.h6
-rw-r--r--Modules/zlib/inflate.c136
-rw-r--r--Modules/zlib/inftrees.c54
-rw-r--r--Modules/zlib/inftrees.h2
-rw-r--r--Modules/zlib/make_vms.com403
-rw-r--r--Modules/zlib/minigzip.c213
-rw-r--r--Modules/zlib/trees.c54
-rw-r--r--Modules/zlib/uncompr.c2
-rw-r--r--Modules/zlib/zconf.h201
-rw-r--r--Modules/zlib/zconf.h.cmakein201
-rw-r--r--Modules/zlib/zconf.h.in201
-rw-r--r--Modules/zlib/zconf.in.h332
-rw-r--r--Modules/zlib/zlib.318
-rw-r--r--Modules/zlib/zlib.h345
-rw-r--r--Modules/zlib/zlib.map15
-rw-r--r--Modules/zlib/zutil.c26
-rw-r--r--Modules/zlib/zutil.h103
-rw-r--r--Modules/zlibmodule.c710
-rw-r--r--Objects/abstract.c144
-rw-r--r--Objects/bytearrayobject.c440
-rw-r--r--Objects/bytes_methods.c30
-rw-r--r--Objects/bytesobject.c269
-rw-r--r--Objects/classobject.c34
-rw-r--r--Objects/codeobject.c5
-rw-r--r--Objects/complexobject.c28
-rw-r--r--Objects/descrobject.c98
-rw-r--r--Objects/dictobject.c418
-rw-r--r--Objects/exceptions.c288
-rw-r--r--Objects/fileobject.c6
-rw-r--r--Objects/floatobject.c37
-rw-r--r--Objects/frameobject.c136
-rw-r--r--Objects/funcobject.c101
-rw-r--r--Objects/genobject.c108
-rw-r--r--Objects/iterobject.c13
-rw-r--r--Objects/listobject.c106
-rw-r--r--Objects/longobject.c198
-rw-r--r--Objects/memoryobject.c4
-rw-r--r--Objects/methodobject.c91
-rw-r--r--Objects/moduleobject.c122
-rw-r--r--Objects/namespaceobject.c62
-rw-r--r--Objects/object.c350
-rw-r--r--Objects/obmalloc.c584
-rw-r--r--Objects/rangeobject.c205
-rw-r--r--Objects/setobject.c531
-rw-r--r--Objects/sliceobject.c204
-rw-r--r--Objects/stringlib/asciilib.h1
-rw-r--r--Objects/stringlib/codecs.h116
-rw-r--r--Objects/stringlib/eq.h4
-rw-r--r--Objects/stringlib/fastsearch.h8
-rw-r--r--Objects/stringlib/find_max_char.h4
-rw-r--r--Objects/stringlib/join.h133
-rw-r--r--Objects/stringlib/partition.h10
-rw-r--r--Objects/stringlib/replace.h53
-rw-r--r--Objects/stringlib/split.h4
-rw-r--r--Objects/stringlib/stringdefs.h1
-rw-r--r--Objects/stringlib/transmogrify.h8
-rw-r--r--Objects/stringlib/ucs1lib.h1
-rw-r--r--Objects/stringlib/ucs2lib.h1
-rw-r--r--Objects/stringlib/ucs4lib.h1
-rw-r--r--Objects/stringlib/undef.h1
-rw-r--r--Objects/stringlib/unicode_format.h141
-rw-r--r--Objects/stringlib/unicodedefs.h6
-rw-r--r--Objects/structseq.c86
-rw-r--r--Objects/tupleobject.c121
-rw-r--r--Objects/typeobject.c1125
-rw-r--r--Objects/unicodectype.c2
-rw-r--r--Objects/unicodeobject.c4824
-rw-r--r--Objects/unicodetype_db.h25
-rw-r--r--Objects/weakrefobject.c7
-rw-r--r--PC/VC6/_ctypes.dsp131
-rw-r--r--PC/VC6/_ctypes_test.dsp99
-rw-r--r--PC/VC6/_elementtree.dsp111
-rw-r--r--PC/VC6/_msi.dsp99
-rw-r--r--PC/VC6/_multiprocessing.dsp107
-rw-r--r--PC/VC6/_socket.dsp99
-rw-r--r--PC/VC6/_sqlite3.dsp131
-rw-r--r--PC/VC6/_ssl.dsp89
-rw-r--r--PC/VC6/_ssl.mak22
-rw-r--r--PC/VC6/_testcapi.dsp99
-rw-r--r--PC/VC6/_tkinter.dsp103
-rw-r--r--PC/VC6/build_ssl.py228
-rw-r--r--PC/VC6/build_tkinter.py81
-rw-r--r--PC/VC6/bz2.dsp99
-rw-r--r--PC/VC6/make_versioninfo.dsp108
-rw-r--r--PC/VC6/pcbuild.dsw306
-rw-r--r--PC/VC6/pyexpat.dsp111
-rw-r--r--PC/VC6/python.dsp100
-rw-r--r--PC/VC6/pythoncore.dsp780
-rw-r--r--PC/VC6/pythonw.dsp101
-rw-r--r--PC/VC6/readme.txt192
-rw-r--r--PC/VC6/rmpyc.py25
-rwxr-xr-xPC/VC6/rt.bat41
-rw-r--r--PC/VC6/select.dsp99
-rw-r--r--PC/VC6/tcl852.patch11
-rw-r--r--PC/VC6/unicodedata.dsp99
-rw-r--r--PC/VC6/w9xpopen.dsp97
-rw-r--r--PC/VC6/winsound.dsp99
-rw-r--r--PC/VS7.1/Uninstal.wse514
-rw-r--r--PC/VS7.1/_ctypes.vcproj311
-rw-r--r--PC/VS7.1/_ctypes_test.vcproj242
-rw-r--r--PC/VS7.1/_elementtree.vcproj264
-rw-r--r--PC/VS7.1/_msi.vcproj252
-rw-r--r--PC/VS7.1/_socket.vcproj254
-rw-r--r--PC/VS7.1/_sqlite3.vcproj283
-rw-r--r--PC/VS7.1/_ssl.mak38
-rw-r--r--PC/VS7.1/_ssl.vcproj84
-rw-r--r--PC/VS7.1/_testcapi.vcproj247
-rw-r--r--PC/VS7.1/_tkinter.vcproj261
-rw-r--r--PC/VS7.1/amd64_ml64.bat17
-rw-r--r--PC/VS7.1/build_ssl.bat12
-rw-r--r--PC/VS7.1/build_ssl.py181
-rw-r--r--PC/VS7.1/bz2.vcproj271
-rw-r--r--PC/VS7.1/db.build10
-rw-r--r--PC/VS7.1/field3.py35
-rw-r--r--PC/VS7.1/installer.bmpbin58806 -> 0 bytes-rw-r--r--PC/VS7.1/make_buildinfo.c92
-rw-r--r--PC/VS7.1/make_buildinfo.vcproj122
-rw-r--r--PC/VS7.1/make_versioninfo.vcproj142
-rw-r--r--PC/VS7.1/pcbuild.sln275
-rw-r--r--PC/VS7.1/pyexpat.vcproj263
-rw-r--r--PC/VS7.1/python.build20
-rw-r--r--PC/VS7.1/python.iss341
-rw-r--r--PC/VS7.1/python.vcproj274
-rw-r--r--PC/VS7.1/python20.wse3117
-rw-r--r--PC/VS7.1/pythoncore.vcproj826
-rw-r--r--PC/VS7.1/pythonw.vcproj261
-rw-r--r--PC/VS7.1/readme.txt337
-rw-r--r--PC/VS7.1/rmpyc.py25
-rwxr-xr-xPC/VS7.1/rt.bat52
-rw-r--r--PC/VS7.1/select.vcproj258
-rw-r--r--PC/VS7.1/unicodedata.vcproj247
-rw-r--r--PC/VS7.1/w9xpopen.vcproj121
-rw-r--r--PC/VS7.1/winsound.vcproj251
-rw-r--r--PC/VS8.0/_ctypes.vcproj705
-rw-r--r--PC/VS8.0/_ctypes_test.vcproj521
-rw-r--r--PC/VS8.0/_elementtree.vcproj613
-rw-r--r--PC/VS8.0/_hashlib.vcproj537
-rw-r--r--PC/VS8.0/_msi.vcproj529
-rw-r--r--PC/VS8.0/_multiprocessing.vcproj545
-rw-r--r--PC/VS8.0/_socket.vcproj537
-rw-r--r--PC/VS8.0/_sqlite3.vcproj613
-rw-r--r--PC/VS8.0/_ssl.vcproj537
-rw-r--r--PC/VS8.0/_tkinter.vcproj541
-rw-r--r--PC/VS8.0/bdist_wininst.vcproj270
-rw-r--r--PC/VS8.0/build.bat17
-rw-r--r--PC/VS8.0/build_env.bat1
-rw-r--r--PC/VS8.0/build_pgo.bat41
-rw-r--r--PC/VS8.0/build_ssl.bat12
-rw-r--r--PC/VS8.0/build_ssl.py277
-rw-r--r--PC/VS8.0/build_tkinter.py85
-rw-r--r--PC/VS8.0/bz2.vcproj581
-rw-r--r--PC/VS8.0/debug.vsprops15
-rw-r--r--PC/VS8.0/env.bat5
-rw-r--r--PC/VS8.0/field3.py35
-rw-r--r--PC/VS8.0/idle.bat15
-rw-r--r--PC/VS8.0/kill_python.c178
-rw-r--r--PC/VS8.0/kill_python.vcproj279
-rw-r--r--PC/VS8.0/make_buildinfo.c116
-rw-r--r--PC/VS8.0/make_buildinfo.vcproj101
-rw-r--r--PC/VS8.0/make_versioninfo.vcproj324
-rw-r--r--PC/VS8.0/pcbuild.sln560
-rw-r--r--PC/VS8.0/pginstrument.vsprops34
-rw-r--r--PC/VS8.0/pgupdate.vsprops14
-rw-r--r--PC/VS8.0/pyd.vsprops28
-rw-r--r--PC/VS8.0/pyd_d.vsprops36
-rw-r--r--PC/VS8.0/pyexpat.vcproj553
-rw-r--r--PC/VS8.0/pyproject.vsprops87
-rw-r--r--PC/VS8.0/python.vcproj637
-rw-r--r--PC/VS8.0/pythoncore.vcproj1921
-rw-r--r--PC/VS8.0/pythonw.vcproj618
-rw-r--r--PC/VS8.0/release.vsprops15
-rw-r--r--PC/VS8.0/rmpyc.py25
-rw-r--r--PC/VS8.0/rt.bat52
-rw-r--r--PC/VS8.0/select.vcproj537
-rw-r--r--PC/VS8.0/sqlite3.vcproj537
-rw-r--r--PC/VS8.0/sqlite3.vsprops14
-rw-r--r--PC/VS8.0/ssl.vcproj189
-rw-r--r--PC/VS8.0/unicodedata.vcproj533
-rw-r--r--PC/VS8.0/w9xpopen.vcproj576
-rw-r--r--PC/VS8.0/winsound.vcproj523
-rw-r--r--PC/VS8.0/x64.vsprops22
-rw-r--r--PC/VS9.0/_sqlite3.vcproj4
-rw-r--r--PC/VS9.0/_ssl.vcproj18
-rw-r--r--PC/VS9.0/_testimportmultiple.vcproj (renamed from PC/VS8.0/_testcapi.vcproj)10
-rw-r--r--PC/VS9.0/kill_python.c2
-rw-r--r--PC/VS9.0/pcbuild.sln42
-rw-r--r--PC/VS9.0/pyproject.vsprops8
-rw-r--r--PC/VS9.0/pythoncore.vcproj90
-rw-r--r--PC/VS9.0/w9xpopen.vcproj576
-rw-r--r--PC/_msi.c80
-rw-r--r--PC/bdist_wininst/install.c2
-rw-r--r--PC/config.c10
-rw-r--r--PC/example_nt/example.vcproj4
-rw-r--r--PC/getpathp.c43
-rw-r--r--PC/launcher.c254
-rw-r--r--PC/msvcrtmodule.c5
-rw-r--r--PC/os2emx/Makefile672
-rw-r--r--PC/os2emx/README.os2emx663
-rw-r--r--PC/os2emx/config.c164
-rw-r--r--PC/os2emx/dlfcn.c223
-rw-r--r--PC/os2emx/dlfcn.h51
-rw-r--r--PC/os2emx/dllentry.c42
-rw-r--r--PC/os2emx/getpathp.c418
-rw-r--r--PC/os2emx/pyconfig.h332
-rw-r--r--PC/os2emx/python33.def1314
-rw-r--r--PC/os2emx/pythonpm.c124
-rw-r--r--PC/os2vacpp/_tkinter.def8
-rw-r--r--PC/os2vacpp/config.c99
-rw-r--r--PC/os2vacpp/getpathp.c482
-rw-r--r--PC/os2vacpp/makefile1549
-rw-r--r--PC/os2vacpp/makefile.omk1047
-rw-r--r--PC/os2vacpp/pyconfig.h212
-rw-r--r--PC/os2vacpp/python.def479
-rw-r--r--PC/os2vacpp/readme.txt119
-rw-r--r--PC/pyconfig.h21
-rw-r--r--PC/python.mk5
-rw-r--r--PC/python3.def1396
-rw-r--r--PC/python3.mak12
-rw-r--r--PC/python34gen.py (renamed from PC/python33gen.py)8
-rw-r--r--PC/python34stub.def (renamed from PC/python33stub.def)4
-rw-r--r--PC/readme.txt13
-rw-r--r--PC/w9xpopen.c112
-rw-r--r--PC/winreg.c12
-rw-r--r--PCbuild/_overlapped.vcxproj234
-rw-r--r--PCbuild/_overlapped.vcxproj.filters (renamed from PCbuild/w9xpopen.vcxproj.filters)4
-rw-r--r--PCbuild/_sqlite3.vcxproj1
-rw-r--r--PCbuild/_sqlite3.vcxproj.filters3
-rw-r--r--PCbuild/_ssl.vcxproj16
-rw-r--r--PCbuild/_testembed.vcxproj161
-rw-r--r--PCbuild/_testembed.vcxproj.filters22
-rw-r--r--PCbuild/_testimportmultiple.vcxproj (renamed from PCbuild/w9xpopen.vcxproj)167
-rw-r--r--PCbuild/_testimportmultiple.vcxproj.filters13
-rw-r--r--PCbuild/build_ssl.bat4
-rw-r--r--PCbuild/build_tkinter.py12
-rw-r--r--PCbuild/kill_python.c2
-rw-r--r--PCbuild/pcbuild.sln70
-rw-r--r--PCbuild/pyproject.props21
-rw-r--r--PCbuild/python.vcxproj8
-rw-r--r--PCbuild/pythoncore.vcxproj31
-rw-r--r--PCbuild/pythoncore.vcxproj.filters35
-rw-r--r--PCbuild/readme.txt487
-rw-r--r--PCbuild/xxlimited.vcxproj11
-rw-r--r--Parser/Python.asdl12
-rw-r--r--Parser/asdl.py33
-rwxr-xr-xParser/asdl_c.py69
-rw-r--r--Parser/grammar.c10
-rw-r--r--Parser/grammar1.c8
-rw-r--r--Parser/myreadline.c48
-rw-r--r--Parser/node.c4
-rw-r--r--Parser/parser.c36
-rw-r--r--Parser/parsetok.c93
-rw-r--r--Parser/pgenmain.c3
-rw-r--r--Parser/tokenizer.c29
-rw-r--r--Parser/tokenizer.h6
-rw-r--r--Python/Python-ast.c370
-rw-r--r--Python/_warnings.c245
-rw-r--r--Python/asdl.c4
-rw-r--r--Python/ast.c239
-rw-r--r--Python/bltinmodule.c181
-rw-r--r--Python/ceval.c2352
-rw-r--r--Python/codecs.c201
-rw-r--r--Python/compile.c490
-rw-r--r--Python/condvar.h2
-rw-r--r--Python/dynload_aix.c12
-rw-r--r--Python/dynload_os2.c42
-rw-r--r--Python/dynload_shlib.c62
-rw-r--r--Python/errors.c285
-rw-r--r--Python/fileutils.c380
-rw-r--r--Python/formatter_unicode.c56
-rw-r--r--Python/frozen.c4
-rw-r--r--Python/frozenmain.c58
-rw-r--r--Python/future.c53
-rw-r--r--Python/getargs.c121
-rw-r--r--Python/getcwd.c83
-rw-r--r--Python/import.c991
-rw-r--r--Python/importdl.c2
-rw-r--r--Python/importdl.h5
-rw-r--r--Python/importlib.h8642
-rw-r--r--Python/marshal.c799
-rw-r--r--Python/modsupport.c12
-rw-r--r--Python/mystrtoul.c32
-rw-r--r--Python/opcode_targets.h4
-rw-r--r--Python/peephole.c55
-rw-r--r--Python/pyarena.c12
-rw-r--r--Python/pyhash.c427
-rw-r--r--Python/pystate.c152
-rw-r--r--Python/pystrtod.c6
-rw-r--r--Python/pythonrun.c546
-rw-r--r--Python/pytime.c35
-rw-r--r--Python/random.c167
-rw-r--r--Python/strdup.c2
-rw-r--r--Python/symtable.c187
-rw-r--r--Python/sysmodule.c268
-rw-r--r--Python/thread.c38
-rw-r--r--Python/thread_nt.h19
-rw-r--r--Python/thread_os2.h267
-rw-r--r--Python/thread_pth.h177
-rw-r--r--Python/thread_pthread.h15
-rw-r--r--Python/traceback.c35
-rw-r--r--README16
-rw-r--r--Tools/buildbot/external-amd64.bat18
-rw-r--r--Tools/buildbot/external-common.bat27
-rw-r--r--Tools/buildbot/external.bat16
-rw-r--r--Tools/buildbot/test-amd64.bat2
-rw-r--r--Tools/buildbot/test.bat2
-rwxr-xr-xTools/clinic/clinic.py4199
-rw-r--r--Tools/clinic/clinic_test.py801
-rw-r--r--Tools/clinic/cpp.py191
-rw-r--r--Tools/freeze/bkfile.py4
-rwxr-xr-xTools/freeze/freeze.py18
-rw-r--r--Tools/freeze/makeconfig.py2
-rw-r--r--Tools/freeze/test/Makefile11
-rw-r--r--Tools/freeze/test/ok.py2
-rwxr-xr-xTools/gdb/libpython.py44
-rwxr-xr-xTools/i18n/makelocalealias.py27
-rwxr-xr-xTools/i18n/msgfmt.py2
-rw-r--r--Tools/importbench/importbench.py31
-rw-r--r--Tools/iobench/iobench.py4
-rw-r--r--Tools/msi/msi.py67
-rw-r--r--Tools/parser/unparse.py15
-rw-r--r--Tools/scripts/README126
-rwxr-xr-xTools/scripts/byext.py2
-rwxr-xr-xTools/scripts/byteyears.py2
-rwxr-xr-xTools/scripts/checkpip.py32
-rwxr-xr-xTools/scripts/checkpyc.py12
-rwxr-xr-xTools/scripts/copytime.py4
-rwxr-xr-xTools/scripts/diff.py4
-rwxr-xr-xTools/scripts/finddiv.py2
-rwxr-xr-xTools/scripts/findlinksto.py2
-rwxr-xr-xTools/scripts/fixcid.py8
-rwxr-xr-xTools/scripts/ftpmirror.py20
-rwxr-xr-xTools/scripts/linktree.py6
-rwxr-xr-xTools/scripts/ndiff.py2
-rwxr-xr-xTools/scripts/parse_html5_entities.py105
-rwxr-xr-xTools/scripts/pathfix.py10
-rwxr-xr-xTools/scripts/pindent.py4
-rwxr-xr-x[-rw-r--r--]Tools/scripts/pydocgui.pyw0
-rwxr-xr-xTools/scripts/reindent.py2
-rwxr-xr-xTools/scripts/treesync.py2
-rwxr-xr-xTools/scripts/untabify.py4
-rwxr-xr-xTools/scripts/which.py2
-rw-r--r--Tools/scripts/win_add2path.py2
-rw-r--r--Tools/ssl/test_multiple_versions.py241
-rw-r--r--Tools/stringbench/stringbench.py2
-rw-r--r--Tools/unicode/gencodec.py18
-rw-r--r--Tools/unicode/makeunicodedata.py8
-rw-r--r--Tools/unicode/python-mappings/CP273.TXT258
-rwxr-xr-xconfigure646
-rw-r--r--configure.ac448
-rw-r--r--pyconfig.h.in37
-rw-r--r--setup.py45
1559 files changed, 171497 insertions, 108998 deletions
diff --git a/.gitignore b/.gitignore
index 49299ee..59d3832 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,11 +15,13 @@ Doc/tools/jinja2/
Doc/tools/pygments/
Doc/tools/sphinx/
Lib/lib2to3/*.pickle
+Lib/test/data/*
Lib/_sysconfigdata.py
Lib/plat-mac/errors.rsrc.df.rsrc
Makefile
Makefile.pre
Misc/python.pc
+Misc/python-config.sh
Modules/Setup
Modules/Setup.config
Modules/Setup.local
@@ -57,6 +59,8 @@ platform
pybuilddir.txt
pyconfig.h
python
+python-config
+python-config.py
python.exe
python-gdb.py
python.exe-gdb.py
diff --git a/.hgeol b/.hgeol
index 73a4770..2919f76 100644
--- a/.hgeol
+++ b/.hgeol
@@ -26,6 +26,7 @@
**.psd = BIN
**.tar = BIN
**.wav = BIN
+**.whl = BIN
**.xar = BIN
**.zip = BIN
diff --git a/.hgignore b/.hgignore
index c42dd03..c67ffb8 100644
--- a/.hgignore
+++ b/.hgignore
@@ -19,21 +19,20 @@ platform$
pyconfig.h$
python$
python.exe$
+python-config$
+python-config.py$
reflog.txt$
tags$
Lib/plat-mac/errors.rsrc.df.rsrc
-Doc/tools/sphinx/
-Doc/tools/docutils/
-Doc/tools/jinja/
-Doc/tools/jinja2/
-Doc/tools/pygments/
Misc/python.pc
+Misc/python-config.sh$
Modules/Setup$
Modules/Setup.config
Modules/Setup.local
Modules/config.c
Modules/ld_so_aix$
Parser/pgen$
+^lcov-report/
^core
^python-gdb.py
^python.exe-gdb.py
@@ -81,7 +80,12 @@ PCbuild/*.suo
PCbuild/*.*sdf
PCbuild/Win32-temp-*
PCbuild/x64-temp-*
+PCbuild/*-pgi
+PCbuild/*-pgo
PCbuild/amd64
+PCbuild/ipch
+Tools/unicode/build/
+Tools/unicode/MAPPINGS/
BuildLog.htm
__pycache__
Modules/_freeze_importlib
@@ -89,3 +93,7 @@ Modules/_testembed
.coverage
coverage/
htmlcov/
+*.gcda
+*.gcno
+*.gcov
+coverage.info
diff --git a/.hgtags b/.hgtags
index 3885d2c..9c3af09 100644
--- a/.hgtags
+++ b/.hgtags
@@ -123,3 +123,14 @@ fa92f5f940c6c0d839d7f0611e4b717606504a3c v3.3.4rc1
9ec811df548ed154a9bf9815383a916d6df31b98 v3.3.5rc1
ca5635efe090f78806188ac2758f9948596aa8b2 v3.3.5rc2
62cf4e77f78564714e7ea3d4bf1479ca1fbd0758 v3.3.5
+46535f65e7f3bcdcf176f36d34bc1fed719ffd2b v3.4.0a1
+9265a2168e2cb2a84785d8717792acc661e6b692 v3.4.0a2
+dd9cdf90a5073510877e9dd5112f8e6cf20d5e89 v3.4.0a3
+e245b0d7209bb6d0e19316e1e2af1aa9c2139104 v3.4.0a4
+3405dc9a6afaa0a06dd1f6f182ec5c998dce6f5f v3.4.0b1
+ba32913eb13ec545a46dd0ce18035b6c416f0d78 v3.4.0b2
+a97ce3ecc96af79bd2e1ac66ce48d9138e0ca749 v3.4.0b3
+5e088cea8660677969113741c1313d570d977e02 v3.4.0rc1
+a300712ed38c9a242b736c44e806caea25a6dc05 v3.4.0rc2
+8a81cdab3e9d521daaef989fade94b16455fc3b8 v3.4.0rc3
+04f714765c13824c3bc2835d7b008908862e083a v3.4.0
diff --git a/.hgtouch b/.hgtouch
index a629de9..7e3a5e7 100644
--- a/.hgtouch
+++ b/.hgtouch
@@ -4,8 +4,7 @@
Python/importlib.h: Lib/importlib/_bootstrap.py Modules/_freeze_importlib.c
-Include/ast.h: Parser/Python.asdl Parser/asdl.py Parser/asdl_c.py
-Include/Python-ast.h: Include/ast.h
+Include/Python-ast.h: Parser/Python.asdl Parser/asdl.py Parser/asdl_c.py
Python/Python-ast.c: Include/Python-ast.h
Python/opcode_targets.h: Python/makeopcodetargets.py Lib/opcode.py
diff --git a/Doc/Makefile b/Doc/Makefile
index 82f5bef..370c3fa 100644
--- a/Doc/Makefile
+++ b/Doc/Makefile
@@ -5,7 +5,7 @@
# You can set these variables from the command line.
PYTHON = python
-SVNROOT = http://svn.python.org/projects
+SPHINXBUILD = sphinx-build
SPHINXOPTS =
PAPER =
SOURCES =
@@ -14,15 +14,15 @@ DISTVERSION = $(shell $(PYTHON) tools/sphinxext/patchlevel.py)
ALLSPHINXOPTS = -b $(BUILDER) -d build/doctrees -D latex_paper_size=$(PAPER) \
$(SPHINXOPTS) . build/$(BUILDER) $(SOURCES)
-.PHONY: help checkout update build html htmlhelp latex text changes linkcheck \
+.PHONY: help build html htmlhelp latex text changes linkcheck \
suspicious coverage doctest pydoc-topics htmlview clean dist check serve \
autobuild-dev autobuild-stable
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " clean to remove build files"
- @echo " update to update build tools"
@echo " html to make standalone HTML files"
+ @echo " htmlview to open the index page built by the html target in your browser"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " text to make plain text files"
@@ -37,30 +37,8 @@ help:
@echo " check to run a check for frequent markup errors"
@echo " serve to serve the documentation on the localhost (8000)"
-# Note: if you update versions here, do the same in make.bat and README.txt
-checkout:
- @if [ ! -d tools/sphinx ]; then \
- echo "Checking out Sphinx..."; \
- svn checkout $(SVNROOT)/external/Sphinx-1.2/sphinx tools/sphinx; \
- fi
- @if [ ! -d tools/docutils ]; then \
- echo "Checking out Docutils..."; \
- svn checkout $(SVNROOT)/external/docutils-0.11/docutils tools/docutils; \
- fi
- @if [ ! -d tools/jinja2 ]; then \
- echo "Checking out Jinja..."; \
- svn checkout $(SVNROOT)/external/Jinja-2.3.1/jinja2 tools/jinja2; \
- fi
- @if [ ! -d tools/pygments ]; then \
- echo "Checking out Pygments..."; \
- svn checkout $(SVNROOT)/external/Pygments-1.6/pygments tools/pygments; \
- fi
-
-update: clean checkout
-
-build: checkout
- mkdir -p build/$(BUILDER) build/doctrees
- $(PYTHON) tools/sphinx-build.py $(ALLSPHINXOPTS)
+build:
+ $(SPHINXBUILD) $(ALLSPHINXOPTS)
@echo
html: BUILDER = html
@@ -120,10 +98,6 @@ htmlview: html
clean:
-rm -rf build/*
- -rm -rf tools/sphinx
- -rm -rf tools/pygments
- -rm -rf tools/jinja2
- -rm -rf tools/docutils
dist:
rm -rf dist
@@ -163,16 +137,10 @@ dist:
cp build/latex/docs-pdf.zip dist/python-$(DISTVERSION)-docs-pdf-letter.zip
cp build/latex/docs-pdf.tar.bz2 dist/python-$(DISTVERSION)-docs-pdf-letter.tar.bz2
- # archive the epub build
+ # copy the epub build
rm -rf build/epub
make epub
- mkdir -p dist/python-$(DISTVERSION)-docs-epub
- cp -pPR build/epub/*.epub dist/python-$(DISTVERSION)-docs-epub/
- tar -C dist -cf dist/python-$(DISTVERSION)-docs-epub.tar python-$(DISTVERSION)-docs-epub
- bzip2 -9 -k dist/python-$(DISTVERSION)-docs-epub.tar
- (cd dist; zip -q -r -9 python-$(DISTVERSION)-docs-epub.zip python-$(DISTVERSION)-docs-epub)
- rm -r dist/python-$(DISTVERSION)-docs-epub
- rm dist/python-$(DISTVERSION)-docs-epub.tar
+ cp -pPR build/epub/Python.epub dist/python-$(DISTVERSION)-docs.epub
check:
$(PYTHON) tools/rstlint.py -i tools
@@ -184,7 +152,6 @@ serve:
# for development releases: always build
autobuild-dev:
- make update
make dist SPHINXOPTS='-A daily=1 -A versionswitcher=1'
-make suspicious
diff --git a/Doc/README.txt b/Doc/README.txt
index a494c89..6df12ab 100644
--- a/Doc/README.txt
+++ b/Doc/README.txt
@@ -3,34 +3,30 @@ Python Documentation README
This directory contains the reStructuredText (reST) sources to the Python
documentation. You don't need to build them yourself, prebuilt versions are
-available at http://docs.python.org/download/.
+available at <https://docs.python.org/3.4/download.html>.
-Documentation on the authoring Python documentation, including information about
+Documentation on authoring Python documentation, including information about
both style and markup, is available in the "Documenting Python" chapter of the
-documentation.
+developers guide <http://docs.python.org/devguide/documenting.html>.
Building the docs
=================
-You need to have Python 2 installed; the toolset used to build the
-docs is written in Python. It is called *Sphinx*, it is not included in this
-tree, but maintained separately. Also needed are the docutils, supplying the
-base markup that Sphinx uses, Jinja, a templating engine, and optionally
-Pygments, a code highlighter.
+You need to have Sphinx <http://sphinx-doc.org/> installed; it is the toolset
+used to build the docs. It is not included in this tree, but maintained
+separately and available from PyPI <http://pypi.python.org/pypi/Sphinx>.
Using make
----------
-Luckily, a Makefile has been prepared so that on Unix, provided you have
-installed Python and Subversion, you can just run ::
+A Makefile has been prepared so that on Unix, provided you have installed
+Sphinx, you can just run ::
make html
-to check out the necessary toolset in the `tools/` subdirectory and build the
-HTML output files. To view the generated HTML, point your favorite browser at
-the top-level index `build/html/index.html` after running "make".
+to build the HTML output files.
On Windows, we try to emulate the Makefile as closely as possible with a
``make.bat`` file.
@@ -38,18 +34,29 @@ On Windows, we try to emulate the Makefile as closely as possible with a
To use a Python interpreter that's not called ``python``, use the standard
way to set Makefile variables, using e.g. ::
- make html PYTHON=/usr/bin/python2.5
+ make html PYTHON=python3
+
+On Windows, set the PYTHON environment variable instead.
+
+To use a specific sphinx-build (something other than ``sphinx-build``), set
+the SPHINXBUILD variable.
Available make targets are:
+ * "clean", which removes all build files.
+
* "html", which builds standalone HTML files for offline viewing.
+ * "htmlview", which re-uses the "html" builder, but then opens the main page
+ in your default web browser.
+
* "htmlhelp", which builds HTML files and a HTML Help project file usable to
convert them into a single Compiled HTML (.chm) file -- these are popular
under Microsoft Windows, but very handy on every platform.
- To create the CHM file, you need to run the Microsoft HTML Help Workshop over
- the generated project (.hhp) file.
+ To create the CHM file, you need to run the Microsoft HTML Help Workshop
+ over the generated project (.hhp) file. The make.bat script does this for
+ you on Windows.
* "latex", which builds LaTeX source files as input to "pdflatex" to produce
PDF documents.
@@ -78,7 +85,12 @@ Available make targets are:
* "suspicious", which checks the parsed markup for text that looks like
malformed and thus unconverted reST.
-A "make update" updates the Subversion checkouts in `tools/`.
+ * "check", which checks for frequent markup errors.
+
+ * "serve", which serves the build/html directory on port 8000.
+
+ * "dist", (Unix only) which creates distributable archives of HTML, text,
+ PDF, and EPUB builds.
Without make
@@ -86,7 +98,7 @@ Without make
Install the Sphinx package and its dependencies from PyPI.
-Then, from the ``Docs`` directory, run ::
+Then, from the ``Doc`` directory, run ::
sphinx-build -b<builder> . build/<builder>
diff --git a/Doc/c-api/arg.rst b/Doc/c-api/arg.rst
index 28a434e..2f02241 100644
--- a/Doc/c-api/arg.rst
+++ b/Doc/c-api/arg.rst
@@ -295,6 +295,8 @@ Other objects
the object pointer is stored. If the Python object does not have the required
type, :exc:`TypeError` is raised.
+.. _o_ampersand:
+
``O&`` (object) [*converter*, *anything*]
Convert a Python object to a C variable through a *converter* function. This
takes two arguments: the first is a function, the second is the address of a C
diff --git a/Doc/c-api/concrete.rst b/Doc/c-api/concrete.rst
index 65904ee..2d56386 100644
--- a/Doc/c-api/concrete.rst
+++ b/Doc/c-api/concrete.rst
@@ -74,26 +74,35 @@ intrinsic to the Python language.
.. _mapobjects:
-Mapping Objects
-===============
+Container Objects
+=================
.. index:: object: mapping
.. toctree::
dict.rst
+ set.rst
.. _otherobjects:
-Other Objects
-=============
+Function Objects
+================
.. toctree::
- set.rst
function.rst
method.rst
+ cell.rst
+ code.rst
+
+
+Other Objects
+=============
+
+.. toctree::
+
file.rst
module.rst
iterator.rst
@@ -102,7 +111,6 @@ Other Objects
memoryview.rst
weakref.rst
capsule.rst
- cell.rst
gen.rst
datetime.rst
- code.rst
+
diff --git a/Doc/c-api/dict.rst b/Doc/c-api/dict.rst
index 6bacc32..aeff640 100644
--- a/Doc/c-api/dict.rst
+++ b/Doc/c-api/dict.rst
@@ -84,7 +84,7 @@ Dictionary Objects
on failure.
-.. c:function:: int PyDict_DelItemString(PyObject *p, char *key)
+.. c:function:: int PyDict_DelItemString(PyObject *p, const char *key)
Remove the entry in dictionary *p* which has a key specified by the string
*key*. Return ``0`` on success or ``-1`` on failure.
@@ -110,6 +110,15 @@ Dictionary Objects
:c:type:`char\*`, rather than a :c:type:`PyObject\*`.
+.. c:function:: PyObject* PyDict_SetDefault(PyObject *p, PyObject *key, PyObject *default)
+
+ This is the same as the Python-level :meth:`dict.setdefault`. If present, it
+ returns the value corresponding to *key* from the dictionary *p*. If the key
+ is not in the dict, it is inserted with value *defaultobj* and *defaultobj*
+ is returned. This function evaluates the hash function of *key* only once,
+ instead of evaluating it independently for the lookup and the insertion.
+
+
.. c:function:: PyObject* PyDict_Items(PyObject *p)
Return a :c:type:`PyListObject` containing all the items from the dictionary.
@@ -192,8 +201,11 @@ Dictionary Objects
.. c:function:: int PyDict_Update(PyObject *a, PyObject *b)
- This is the same as ``PyDict_Merge(a, b, 1)`` in C, or ``a.update(b)`` in
- Python. Return ``0`` on success or ``-1`` if an exception was raised.
+ This is the same as ``PyDict_Merge(a, b, 1)`` in C, and is similar to
+ ``a.update(b)`` in Python except that :c:func:`PyDict_Update` doesn't fall
+ back to the iterating over a sequence of key value pairs if the second
+ argument has no "keys" attribute. Return ``0`` on success or ``-1`` if an
+ exception was raised.
.. c:function:: int PyDict_MergeFromSeq2(PyObject *a, PyObject *seq2, int override)
diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst
index 0aa892d..33b4439 100644
--- a/Doc/c-api/exceptions.rst
+++ b/Doc/c-api/exceptions.rst
@@ -90,6 +90,16 @@ in various ways. There is a separate error indicator for each thread.
the class in that case. If the values are already normalized, nothing happens.
The delayed normalization is implemented to improve performance.
+ .. note::
+
+ This function *does not* implicitly set the ``__traceback__``
+ attribute on the exception value. If setting the traceback
+ appropriately is desired, the following additional snippet is needed::
+
+ if (tb != NULL) {
+ PyException_SetTraceback(val, tb);
+ }
+
.. c:function:: void PyErr_Clear()
@@ -226,16 +236,25 @@ in various ways. There is a separate error indicator for each thread.
Similar to :c:func:`PyErr_SetFromErrno`, with the additional behavior that if
*filenameObject* is not *NULL*, it is passed to the constructor of *type* as
- a third parameter. In the case of exceptions such as :exc:`IOError` and
- :exc:`OSError`, this is used to define the :attr:`filename` attribute of the
+ a third parameter. In the case of :exc:`OSError` exception,
+ this is used to define the :attr:`filename` attribute of the
exception instance.
+.. c:function:: PyObject* PyErr_SetFromErrnoWithFilenameObjects(PyObject *type, PyObject *filenameObject, PyObject *filenameObject2)
+
+ Similar to :c:func:`PyErr_SetFromErrnoWithFilenameObject`, but takes a second
+ filename object, for raising errors when a function that takes two filenames
+ fails.
+
+ .. versionadded:: 3.4
+
+
.. c:function:: PyObject* PyErr_SetFromErrnoWithFilename(PyObject *type, const char *filename)
Similar to :c:func:`PyErr_SetFromErrnoWithFilenameObject`, but the filename
is given as a C string. *filename* is decoded from the filesystem encoding
- (:func:`sys.getfilesystemencoding`).
+ (:func:`os.fsdecode`).
.. c:function:: PyObject* PyErr_SetFromWindowsErr(int ierr)
@@ -256,18 +275,11 @@ in various ways. There is a separate error indicator for each thread.
specifying the exception type to be raised. Availability: Windows.
-.. c:function:: PyObject* PyErr_SetFromWindowsErrWithFilenameObject(int ierr, PyObject *filenameObject)
-
- Similar to :c:func:`PyErr_SetFromWindowsErr`, with the additional behavior
- that if *filenameObject* is not *NULL*, it is passed to the constructor of
- :exc:`WindowsError` as a third parameter. Availability: Windows.
-
-
.. c:function:: PyObject* PyErr_SetFromWindowsErrWithFilename(int ierr, const char *filename)
Similar to :c:func:`PyErr_SetFromWindowsErrWithFilenameObject`, but the
filename is given as a C string. *filename* is decoded from the filesystem
- encoding (:func:`sys.getfilesystemencoding`). Availability: Windows.
+ encoding (:func:`os.fsdecode`). Availability: Windows.
.. c:function:: PyObject* PyErr_SetExcFromWindowsErrWithFilenameObject(PyObject *type, int ierr, PyObject *filename)
@@ -277,6 +289,15 @@ in various ways. There is a separate error indicator for each thread.
Availability: Windows.
+.. c:function:: PyObject* PyErr_SetExcFromWindowsErrWithFilenameObjects(PyObject *type, int ierr, PyObject *filename, PyObject *filename2)
+
+ Similar to :c:func:`PyErr_SetExcFromWindowsErrWithFilenameObject`,
+ but accepts a second filename object.
+ Availability: Windows.
+
+ .. versionadded:: 3.4
+
+
.. c:function:: PyObject* PyErr_SetExcFromWindowsErrWithFilename(PyObject *type, int ierr, const char *filename)
Similar to :c:func:`PyErr_SetFromWindowsErrWithFilename`, with an additional
@@ -293,13 +314,20 @@ in various ways. There is a separate error indicator for each thread.
.. versionadded:: 3.3
-.. c:function:: void PyErr_SyntaxLocationEx(char *filename, int lineno, int col_offset)
+.. c:function:: void PyErr_SyntaxLocationObject(PyObject *filename, int lineno, int col_offset)
Set file, line, and offset information for the current exception. If the
current exception is not a :exc:`SyntaxError`, then it sets additional
attributes, which make the exception printing subsystem think the exception
- is a :exc:`SyntaxError`. *filename* is decoded from the filesystem encoding
- (:func:`sys.getfilesystemencoding`).
+ is a :exc:`SyntaxError`.
+
+ .. versionadded:: 3.4
+
+
+.. c:function:: void PyErr_SyntaxLocationEx(char *filename, int lineno, int col_offset)
+
+ Like :c:func:`PyErr_SyntaxLocationObject`, but *filename* is a byte string
+ decoded from the filesystem encoding (:func:`os.fsdecode`).
.. versionadded:: 3.2
@@ -355,15 +383,22 @@ in various ways. There is a separate error indicator for each thread.
documentation. There is no C API for warning control.
-.. c:function:: int PyErr_WarnExplicit(PyObject *category, const char *message, const char *filename, int lineno, const char *module, PyObject *registry)
+.. c:function:: int PyErr_WarnExplicitObject(PyObject *category, PyObject *message, PyObject *filename, int lineno, PyObject *module, PyObject *registry)
Issue a warning message with explicit control over all warning attributes. This
is a straightforward wrapper around the Python function
:func:`warnings.warn_explicit`, see there for more information. The *module*
and *registry* arguments may be set to *NULL* to get the default effect
- described there. *message* and *module* are UTF-8 encoded strings,
- *filename* is decoded from the filesystem encoding
- (:func:`sys.getfilesystemencoding`).
+ described there.
+
+ .. versionadded:: 3.4
+
+
+.. c:function:: int PyErr_WarnExplicit(PyObject *category, const char *message, const char *filename, int lineno, const char *module, PyObject *registry)
+
+ Similar to :c:func:`PyErr_WarnExplicitObject` except that *message* and
+ *module* are UTF-8 encoded strings, and *filename* is decoded from the
+ filesystem encoding (:func:`os.fsdecode`).
.. c:function:: int PyErr_WarnFormat(PyObject *category, Py_ssize_t stack_level, const char *format, ...)
diff --git a/Doc/c-api/file.rst b/Doc/c-api/file.rst
index cc190c9..6f2ecee 100644
--- a/Doc/c-api/file.rst
+++ b/Doc/c-api/file.rst
@@ -17,7 +17,7 @@ error reporting in the interpreter; third-party code is advised to access
the :mod:`io` APIs instead.
-.. c:function:: PyFile_FromFd(int fd, char *name, char *mode, int buffering, char *encoding, char *errors, char *newline, int closefd)
+.. c:function:: PyFile_FromFd(int fd, const char *name, const char *mode, int buffering, const char *encoding, const char *errors, const char *newline, int closefd)
Create a Python file object from the file descriptor of an already
opened file *fd*. The arguments *name*, *encoding*, *errors* and *newline*
diff --git a/Doc/c-api/import.rst b/Doc/c-api/import.rst
index 270152e..0dcac2d 100644
--- a/Doc/c-api/import.rst
+++ b/Doc/c-api/import.rst
@@ -118,7 +118,7 @@ Importing Modules
encoded string instead of a Unicode object.
-.. c:function:: PyObject* PyImport_ExecCodeModule(char *name, PyObject *co)
+.. c:function:: PyObject* PyImport_ExecCodeModule(const char *name, PyObject *co)
.. index:: builtin: compile
@@ -145,7 +145,7 @@ Importing Modules
:c:func:`PyImport_ExecCodeModuleWithPathnames`.
-.. c:function:: PyObject* PyImport_ExecCodeModuleEx(char *name, PyObject *co, char *pathname)
+.. c:function:: PyObject* PyImport_ExecCodeModuleEx(const char *name, PyObject *co, const char *pathname)
Like :c:func:`PyImport_ExecCodeModule`, but the :attr:`__file__` attribute of
the module object is set to *pathname* if it is non-``NULL``.
@@ -162,7 +162,7 @@ Importing Modules
.. versionadded:: 3.3
-.. c:function:: PyObject* PyImport_ExecCodeModuleWithPathnames(char *name, PyObject *co, char *pathname, char *cpathname)
+.. c:function:: PyObject* PyImport_ExecCodeModuleWithPathnames(const char *name, PyObject *co, const char *pathname, const char *cpathname)
Like :c:func:`PyImport_ExecCodeModuleObject`, but *name*, *pathname* and
*cpathname* are UTF-8 encoded strings. Attempts are also made to figure out
@@ -245,8 +245,11 @@ Importing Modules
.. versionadded:: 3.3
+ .. versionchanged:: 3.4
+ The ``__file__`` attribute is no longer set on the module.
-.. c:function:: int PyImport_ImportFrozenModule(char *name)
+
+.. c:function:: int PyImport_ImportFrozenModule(const char *name)
Similar to :c:func:`PyImport_ImportFrozenModuleObject`, but the name is a
UTF-8 encoded string instead of a Unicode object.
diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst
index 4dad2c8..0587e15 100644
--- a/Doc/c-api/init.rst
+++ b/Doc/c-api/init.rst
@@ -86,6 +86,36 @@ Process-wide parameters
=======================
+.. c:function:: int Py_SetStandardStreamEncoding(char *encoding, char *errors)
+
+ .. index::
+ single: Py_Initialize()
+ single: main()
+ triple: stdin; stdout; sdterr
+
+ This function should be called before :c:func:`Py_Initialize`, if it is
+ called at all. It specifies which encoding and error handling to use
+ with standard IO, with the same meanings as in :func:`str.encode`.
+
+ It overrides :envvar:`PYTHONIOENCODING` values, and allows embedding code
+ to control IO encoding when the environment variable does not work.
+
+ ``encoding`` and/or ``errors`` may be NULL to use
+ :envvar:`PYTHONIOENCODING` and/or default values (depending on other
+ settings).
+
+ Note that :data:`sys.stderr` always uses the "backslashreplace" error
+ handler, regardless of this (or any other) setting.
+
+ If :c:func:`Py_Finalize` is called, this function will need to be called
+ again in order to affect subsequent calls to :c:func:`Py_Initialize`.
+
+ Returns 0 if successful, a nonzero value on error (e.g. calling after the
+ interpreter has already been initialized).
+
+ .. versionadded:: 3.4
+
+
.. c:function:: void Py_SetProgramName(wchar_t *name)
.. index::
@@ -329,7 +359,11 @@ Process-wide parameters
.. c:function:: void PySys_SetArgv(int argc, wchar_t **argv)
- This function works like :c:func:`PySys_SetArgvEx` with *updatepath* set to 1.
+ This function works like :c:func:`PySys_SetArgvEx` with *updatepath* set
+ to 1 unless the :program:`python` interpreter was started with the
+ :option:`-I`.
+
+ .. versionchanged:: 3.4 The *updatepath* value depends on :option:`-I`.
.. c:function:: void Py_SetPythonHome(wchar_t *home)
@@ -658,6 +692,20 @@ with sub-interpreters:
made on the main thread. This is mainly a helper/diagnostic function.
+.. c:function:: int PyGILState_Check()
+
+ Return 1 if the current thread is holding the GIL and 0 otherwise.
+ This function can be called from any thread at any time.
+ Only if it has had its Python thread state initialized and currently is
+ holding the GIL will it return 1.
+ This is mainly a helper/diagnostic function. It can be useful
+ for example in callback contexts or memory allocation functions when
+ knowing that the GIL is locked can allow the caller to perform sensitive
+ actions or otherwise behave differently.
+
+ .. versionadded:: 3.4
+
+
The following macros are normally used without a trailing semicolon; look for
example usage in the Python source distribution.
diff --git a/Doc/c-api/long.rst b/Doc/c-api/long.rst
index d5430fd..b348015 100644
--- a/Doc/c-api/long.rst
+++ b/Doc/c-api/long.rst
@@ -80,7 +80,7 @@ All integers are implemented as "long" integer objects of arbitrary size.
*NULL* on failure.
-.. c:function:: PyObject* PyLong_FromString(char *str, char **pend, int base)
+.. c:function:: PyObject* PyLong_FromString(const char *str, char **pend, int base)
Return a new :c:type:`PyLongObject` based on the string value in *str*, which
is interpreted according to the radix in *base*. If *pend* is non-*NULL*,
diff --git a/Doc/c-api/mapping.rst b/Doc/c-api/mapping.rst
index 0ef2961..2803fd0 100644
--- a/Doc/c-api/mapping.rst
+++ b/Doc/c-api/mapping.rst
@@ -22,7 +22,7 @@ Mapping Protocol
expression ``len(o)``.
-.. c:function:: int PyMapping_DelItemString(PyObject *o, char *key)
+.. c:function:: int PyMapping_DelItemString(PyObject *o, const char *key)
Remove the mapping for object *key* from the object *o*. Return ``-1`` on
failure. This is equivalent to the Python statement ``del o[key]``.
@@ -67,13 +67,13 @@ Mapping Protocol
the Python expression ``list(o.items())``.
-.. c:function:: PyObject* PyMapping_GetItemString(PyObject *o, char *key)
+.. c:function:: PyObject* PyMapping_GetItemString(PyObject *o, const char *key)
Return element of *o* corresponding to the object *key* or *NULL* on failure.
This is the equivalent of the Python expression ``o[key]``.
-.. c:function:: int PyMapping_SetItemString(PyObject *o, char *key, PyObject *v)
+.. c:function:: int PyMapping_SetItemString(PyObject *o, const char *key, PyObject *v)
Map the object *key* to the value *v* in object *o*. Returns ``-1`` on failure.
This is the equivalent of the Python statement ``o[key] = v``.
diff --git a/Doc/c-api/memory.rst b/Doc/c-api/memory.rst
index 8afa56a..a82e1c2 100644
--- a/Doc/c-api/memory.rst
+++ b/Doc/c-api/memory.rst
@@ -84,6 +84,48 @@ the C library allocator as shown in the previous example, the allocated memory
for the I/O buffer escapes completely the Python memory manager.
+Raw Memory Interface
+====================
+
+The following function sets are wrappers to the system allocator. These
+functions are thread-safe, the :term:`GIL <global interpreter lock>` does not
+need to be held.
+
+The default raw memory block allocator uses the following functions:
+:c:func:`malloc`, :c:func:`realloc` and :c:func:`free`; call ``malloc(1)`` when
+requesting zero bytes.
+
+.. versionadded:: 3.4
+
+.. c:function:: void* PyMem_RawMalloc(size_t n)
+
+ Allocates *n* bytes and returns a pointer of type :c:type:`void\*` to the
+ allocated memory, or *NULL* if the request fails. Requesting zero bytes
+ returns a distinct non-*NULL* pointer if possible, as if
+ ``PyMem_RawMalloc(1)`` had been called instead. The memory will not have
+ been initialized in any way.
+
+
+.. c:function:: void* PyMem_RawRealloc(void *p, size_t n)
+
+ Resizes the memory block pointed to by *p* to *n* bytes. The contents will
+ be unchanged to the minimum of the old and the new sizes. If *p* is *NULL*,
+ the call is equivalent to ``PyMem_RawMalloc(n)``; else if *n* is equal to
+ zero, the memory block is resized but is not freed, and the returned pointer
+ is non-*NULL*. Unless *p* is *NULL*, it must have been returned by a
+ previous call to :c:func:`PyMem_RawMalloc` or :c:func:`PyMem_RawRealloc`. If
+ the request fails, :c:func:`PyMem_RawRealloc` returns *NULL* and *p* remains
+ a valid pointer to the previous memory area.
+
+
+.. c:function:: void PyMem_RawFree(void *p)
+
+ Frees the memory block pointed to by *p*, which must have been returned by a
+ previous call to :c:func:`PyMem_RawMalloc` or :c:func:`PyMem_RawRealloc`.
+ Otherwise, or if ``PyMem_Free(p)`` has been called before, undefined
+ behavior occurs. If *p* is *NULL*, no operation is performed.
+
+
.. _memoryinterface:
Memory Interface
@@ -91,8 +133,16 @@ Memory Interface
The following function sets, modeled after the ANSI C standard, but specifying
behavior when requesting zero bytes, are available for allocating and releasing
-memory from the Python heap:
+memory from the Python heap.
+
+The default memory block allocator uses the following functions:
+:c:func:`malloc`, :c:func:`realloc` and :c:func:`free`; call ``malloc(1)`` when
+requesting zero bytes.
+
+.. warning::
+ The :term:`GIL <global interpreter lock>` must be held when using these
+ functions.
.. c:function:: void* PyMem_Malloc(size_t n)
@@ -155,6 +205,125 @@ versions and is therefore deprecated in extension modules.
:c:func:`PyMem_NEW`, :c:func:`PyMem_RESIZE`, :c:func:`PyMem_DEL`.
+Customize Memory Allocators
+===========================
+
+.. versionadded:: 3.4
+
+.. c:type:: PyMemAllocator
+
+ Structure used to describe a memory block allocator. The structure has
+ four fields:
+
+ +----------------------------------------------------------+---------------------------------------+
+ | Field | Meaning |
+ +==========================================================+=======================================+
+ | ``void *ctx`` | user context passed as first argument |
+ +----------------------------------------------------------+---------------------------------------+
+ | ``void* malloc(void *ctx, size_t size)`` | allocate a memory block |
+ +----------------------------------------------------------+---------------------------------------+
+ | ``void* realloc(void *ctx, void *ptr, size_t new_size)`` | allocate or resize a memory block |
+ +----------------------------------------------------------+---------------------------------------+
+ | ``void free(void *ctx, void *ptr)`` | free a memory block |
+ +----------------------------------------------------------+---------------------------------------+
+
+.. c:type:: PyMemAllocatorDomain
+
+ Enum used to identify an allocator domain. Domains:
+
+ * :c:data:`PYMEM_DOMAIN_RAW`: functions :c:func:`PyMem_RawMalloc`,
+ :c:func:`PyMem_RawRealloc` and :c:func:`PyMem_RawFree`
+ * :c:data:`PYMEM_DOMAIN_MEM`: functions :c:func:`PyMem_Malloc`,
+ :c:func:`PyMem_Realloc` and :c:func:`PyMem_Free`
+ * :c:data:`PYMEM_DOMAIN_OBJ`: functions :c:func:`PyObject_Malloc`,
+ :c:func:`PyObject_Realloc` and :c:func:`PyObject_Free`
+
+
+.. c:function:: void PyMem_GetAllocator(PyMemAllocatorDomain domain, PyMemAllocator *allocator)
+
+ Get the memory block allocator of the specified domain.
+
+
+.. c:function:: void PyMem_SetAllocator(PyMemAllocatorDomain domain, PyMemAllocator *allocator)
+
+ Set the memory block allocator of the specified domain.
+
+ The new allocator must return a distinct non-NULL pointer when requesting
+ zero bytes.
+
+ For the :c:data:`PYMEM_DOMAIN_RAW` domain, the allocator must be
+ thread-safe: the :term:`GIL <global interpreter lock>` is not held when the
+ allocator is called.
+
+ If the new allocator is not a hook (does not call the previous allocator),
+ the :c:func:`PyMem_SetupDebugHooks` function must be called to reinstall the
+ debug hooks on top on the new allocator.
+
+
+.. c:function:: void PyMem_SetupDebugHooks(void)
+
+ Setup hooks to detect bugs in the following Python memory allocator
+ functions:
+
+ - :c:func:`PyMem_RawMalloc`, :c:func:`PyMem_RawRealloc`,
+ :c:func:`PyMem_RawFree`
+ - :c:func:`PyMem_Malloc`, :c:func:`PyMem_Realloc`, :c:func:`PyMem_Free`
+ - :c:func:`PyObject_Malloc`, :c:func:`PyObject_Realloc`,
+ :c:func:`PyObject_Free`
+
+ Newly allocated memory is filled with the byte ``0xCB``, freed memory is
+ filled with the byte ``0xDB``. Additionnal checks:
+
+ - detect API violations, ex: :c:func:`PyObject_Free` called on a buffer
+ allocated by :c:func:`PyMem_Malloc`
+ - detect write before the start of the buffer (buffer underflow)
+ - detect write after the end of the buffer (buffer overflow)
+
+ The function does nothing if Python is not compiled is debug mode.
+
+
+Customize PyObject Arena Allocator
+==================================
+
+Python has a *pymalloc* allocator for allocations smaller than 512 bytes. This
+allocator is optimized for small objects with a short lifetime. It uses memory
+mappings called "arenas" with a fixed size of 256 KB. It falls back to
+:c:func:`PyMem_RawMalloc` and :c:func:`PyMem_RawRealloc` for allocations larger
+than 512 bytes. *pymalloc* is the default allocator used by
+:c:func:`PyObject_Malloc`.
+
+The default arena allocator uses the following functions:
+
+* :c:func:`VirtualAlloc` and :c:func:`VirtualFree` on Windows,
+* :c:func:`mmap` and :c:func:`munmap` if available,
+* :c:func:`malloc` and :c:func:`free` otherwise.
+
+.. versionadded:: 3.4
+
+.. c:type:: PyObjectArenaAllocator
+
+ Structure used to describe an arena allocator. The structure has
+ three fields:
+
+ +--------------------------------------------------+---------------------------------------+
+ | Field | Meaning |
+ +==================================================+=======================================+
+ | ``void *ctx`` | user context passed as first argument |
+ +--------------------------------------------------+---------------------------------------+
+ | ``void* alloc(void *ctx, size_t size)`` | allocate an arena of size bytes |
+ +--------------------------------------------------+---------------------------------------+
+ | ``void free(void *ctx, size_t size, void *ptr)`` | free an arena |
+ +--------------------------------------------------+---------------------------------------+
+
+.. c:function:: PyObject_GetArenaAllocator(PyObjectArenaAllocator *allocator)
+
+ Get the arena allocator.
+
+.. c:function:: PyObject_SetArenaAllocator(PyObjectArenaAllocator *allocator)
+
+ Set the arena allocator.
+
+
.. _memoryexamples:
Examples
diff --git a/Doc/c-api/module.rst b/Doc/c-api/module.rst
index bd46170..985a347 100644
--- a/Doc/c-api/module.rst
+++ b/Doc/c-api/module.rst
@@ -35,13 +35,20 @@ There are only a few functions special to module objects.
single: __name__ (module attribute)
single: __doc__ (module attribute)
single: __file__ (module attribute)
+ single: __package__ (module attribute)
+ single: __loader__ (module attribute)
Return a new module object with the :attr:`__name__` attribute set to *name*.
- Only the module's :attr:`__doc__` and :attr:`__name__` attributes are filled in;
- the caller is responsible for providing a :attr:`__file__` attribute.
+ The module's :attr:`__name__`, :attr:`__doc__`, :attr:`__package__`, and
+ :attr:`__loader__` attributes are filled in (all but :attr:`__name__` are set
+ to ``None``); the caller is responsible for providing a :attr:`__file__`
+ attribute.
.. versionadded:: 3.3
+ .. versionchanged:: 3.4
+ :attr:`__package__` and :attr:`__loader__` are set to ``None``.
+
.. c:function:: PyObject* PyModule_New(const char *name)
diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst
index 0aba360..3e7c3b6 100644
--- a/Doc/c-api/object.rst
+++ b/Doc/c-api/object.rst
@@ -149,6 +149,9 @@ Object Protocol
representation on success, *NULL* on failure. This is the equivalent of the
Python expression ``repr(o)``. Called by the :func:`repr` built-in function.
+ .. versionchanged:: 3.4
+ This function now includes a debug assertion to help ensure that it
+ does not silently discard an active exception.
.. c:function:: PyObject* PyObject_ASCII(PyObject *o)
@@ -170,6 +173,10 @@ Object Protocol
Python expression ``str(o)``. Called by the :func:`str` built-in function
and, therefore, by the :func:`print` function.
+ .. versionchanged:: 3.4
+ This function now includes a debug assertion to help ensure that it
+ does not silently discard an active exception.
+
.. c:function:: PyObject* PyObject_Bytes(PyObject *o)
.. index:: builtin: bytes
@@ -240,7 +247,7 @@ attribute is considered sufficient for this determination.
of the Python expression ``callable_object(*args)``.
-.. c:function:: PyObject* PyObject_CallFunction(PyObject *callable, char *format, ...)
+.. c:function:: PyObject* PyObject_CallFunction(PyObject *callable, const char *format, ...)
Call a callable Python object *callable*, with a variable number of C arguments.
The C arguments are described using a :c:func:`Py_BuildValue` style format
@@ -250,8 +257,11 @@ attribute is considered sufficient for this determination.
pass :c:type:`PyObject \*` args, :c:func:`PyObject_CallFunctionObjArgs` is a
faster alternative.
+ .. versionchanged:: 3.4
+ The type of *format* was changed from ``char *``.
+
-.. c:function:: PyObject* PyObject_CallMethod(PyObject *o, char *method, char *format, ...)
+.. c:function:: PyObject* PyObject_CallMethod(PyObject *o, const char *method, const char *format, ...)
Call the method named *method* of object *o* with a variable number of C
arguments. The C arguments are described by a :c:func:`Py_BuildValue` format
@@ -261,6 +271,9 @@ attribute is considered sufficient for this determination.
Note that if you only pass :c:type:`PyObject \*` args,
:c:func:`PyObject_CallMethodObjArgs` is a faster alternative.
+ .. versionchanged:: 3.4
+ The types of *method* and *format* were changed from ``char *``.
+
.. c:function:: PyObject* PyObject_CallFunctionObjArgs(PyObject *callable, ..., NULL)
@@ -342,6 +355,16 @@ attribute is considered sufficient for this determination.
returned. This is the equivalent to the Python expression ``len(o)``.
+.. c:function:: Py_ssize_t PyObject_LengthHint(PyObject *o, Py_ssize_t default)
+
+ Return an estimated length for the object *o*. First try to return its
+ actual length, then an estimate using :meth:`~object.__length_hint__`, and
+ finally return the default value. On error return ``-1``. This is the
+ equivalent to the Python expression ``operator.length_hint(o, default)``.
+
+ .. versionadded:: 3.4
+
+
.. c:function:: PyObject* PyObject_GetItem(PyObject *o, PyObject *key)
Return element of *o* corresponding to the object *key* or *NULL* on failure.
diff --git a/Doc/c-api/sequence.rst b/Doc/c-api/sequence.rst
index 0297ba3..cf1e142 100644
--- a/Doc/c-api/sequence.rst
+++ b/Doc/c-api/sequence.rst
@@ -123,10 +123,10 @@ Sequence Protocol
.. c:function:: PyObject* PySequence_Fast(PyObject *o, const char *m)
- Returns the sequence *o* as a tuple, unless it is already a tuple or list, in
- which case *o* is returned. Use :c:func:`PySequence_Fast_GET_ITEM` to access the
- members of the result. Returns *NULL* on failure. If the object is not a
- sequence, raises :exc:`TypeError` with *m* as the message text.
+ Return the sequence *o* as a list, unless it is already a tuple or list, in
+ which case *o* is returned. Use :c:func:`PySequence_Fast_GET_ITEM` to access
+ the members of the result. Returns *NULL* on failure. If the object is not
+ a sequence, raises :exc:`TypeError` with *m* as the message text.
.. c:function:: PyObject* PySequence_Fast_GET_ITEM(PyObject *o, Py_ssize_t i)
diff --git a/Doc/c-api/tuple.rst b/Doc/c-api/tuple.rst
index 184affb..3922d50 100644
--- a/Doc/c-api/tuple.rst
+++ b/Doc/c-api/tuple.rst
@@ -129,6 +129,14 @@ type.
Initializes a struct sequence type *type* from *desc* in place.
+.. c:function:: int PyStructSequence_InitType2(PyTypeObject *type, PyStructSequence_Desc *desc)
+
+ The same as ``PyStructSequence_InitType``, but returns ``0`` on success and ``-1`` on
+ failure.
+
+ .. versionadded:: 3.4
+
+
.. c:type:: PyStructSequence_Desc
Contains the meta information of a struct sequence type to create.
diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst
index 5d83254..8dc040b 100644
--- a/Doc/c-api/type.rst
+++ b/Doc/c-api/type.rst
@@ -97,3 +97,13 @@ Type Objects
types. This allows the caller to reference other heap types as base types.
.. versionadded:: 3.3
+
+.. c:function:: void* PyType_GetSlot(PyTypeObject *type, int slot)
+
+ Return the function pointer stored in the given slot. If the
+ result is *NULL*, this indicates that either the slot is *NULL*,
+ or that the function was called with invalid parameters.
+ Callers will typically cast the result pointer into the appropriate
+ function type.
+
+ .. versionadded:: 3.4
diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst
index f3089d0..3a64724 100644
--- a/Doc/c-api/typeobj.rst
+++ b/Doc/c-api/typeobj.rst
@@ -464,6 +464,32 @@ type objects) *must* have the :attr:`ob_size` field.
:const:`Py_TPFLAGS_HAVE_VERSION_TAG`.
+ .. data:: Py_TPFLAGS_LONG_SUBCLASS
+ .. data:: Py_TPFLAGS_LIST_SUBCLASS
+ .. data:: Py_TPFLAGS_TUPLE_SUBCLASS
+ .. data:: Py_TPFLAGS_BYTES_SUBCLASS
+ .. data:: Py_TPFLAGS_UNICODE_SUBCLASS
+ .. data:: Py_TPFLAGS_DICT_SUBCLASS
+ .. data:: Py_TPFLAGS_BASE_EXC_SUBCLASS
+ .. data:: Py_TPFLAGS_TYPE_SUBCLASS
+
+ These flags are used by functions such as
+ :c:func:`PyLong_Check` to quickly determine if a type is a subclass
+ of a built-in type; such specific checks are faster than a generic
+ check, like :c:func:`PyObject_IsInstance`. Custom types that inherit
+ from built-ins should have their :c:member:`~PyTypeObject.tp_flags`
+ set appropriately, or the code that interacts with such types
+ will behave differently depending on what kind of check is used.
+
+
+ .. data:: Py_TPFLAGS_HAVE_FINALIZE
+
+ This bit is set when the :c:member:`~PyTypeObject.tp_finalize` slot is present in the
+ type structure.
+
+ .. versionadded:: 3.4
+
+
.. c:member:: char* PyTypeObject.tp_doc
An optional pointer to a NUL-terminated C string giving the docstring for this
@@ -967,6 +993,47 @@ type objects) *must* have the :attr:`ob_size` field.
This field is not inherited; it is calculated fresh by :c:func:`PyType_Ready`.
+.. c:member:: destructor PyTypeObject.tp_finalize
+
+ An optional pointer to an instance finalization function. Its signature is
+ :c:type:`destructor`::
+
+ void tp_finalize(PyObject *)
+
+ If :c:member:`~PyTypeObject.tp_finalize` is set, the interpreter calls it once when
+ finalizing an instance. It is called either from the garbage
+ collector (if the instance is part of an isolated reference cycle) or
+ just before the object is deallocated. Either way, it is guaranteed
+ to be called before attempting to break reference cycles, ensuring
+ that it finds the object in a sane state.
+
+ :c:member:`~PyTypeObject.tp_finalize` should not mutate the current exception status;
+ therefore, a recommended way to write a non-trivial finalizer is::
+
+ static void
+ local_finalize(PyObject *self)
+ {
+ PyObject *error_type, *error_value, *error_traceback;
+
+ /* Save the current exception, if any. */
+ PyErr_Fetch(&error_type, &error_value, &error_traceback);
+
+ /* ... */
+
+ /* Restore the saved exception. */
+ PyErr_Restore(error_type, error_value, error_traceback);
+ }
+
+ For this field to be taken into account (even through inheritance),
+ you must also set the :const:`Py_TPFLAGS_HAVE_FINALIZE` flags bit.
+
+ This field is inherited by subtypes.
+
+ .. versionadded:: 3.4
+
+ .. seealso:: "Safe object finalization" (:pep:`442`)
+
+
.. c:member:: PyObject* PyTypeObject.tp_cache
Unused. Not inherited. Internal use only.
@@ -1150,7 +1217,8 @@ Sequence Object Structures
This function is used by :c:func:`PySequence_Repeat` and has the same
signature. It is also used by the ``*`` operator, after trying numeric
- multiplication via the :c:member:`~PyTypeObject.tp_as_number.nb_mul` slot.
+ multiplication via the :c:member:`~PyTypeObject.tp_as_number.nb_multiply`
+ slot.
.. c:member:: ssizeargfunc PySequenceMethods.sq_item
diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst
index 3649cfb..c7ed5e5 100644
--- a/Doc/c-api/unicode.rst
+++ b/Doc/c-api/unicode.rst
@@ -526,12 +526,23 @@ APIs:
The `"%lld"` and `"%llu"` format specifiers are only available
when :const:`HAVE_LONG_LONG` is defined.
+ .. note::
+ The width formatter unit is number of characters rather than bytes.
+ The precision formatter unit is number of bytes for ``"%s"`` and
+ ``"%V"`` (if the ``PyObject*`` argument is NULL), and a number of
+ characters for ``"%A"``, ``"%U"``, ``"%S"``, ``"%R"`` and ``"%V"``
+ (if the ``PyObject*`` argument is not NULL).
+
.. versionchanged:: 3.2
Support for ``"%lld"`` and ``"%llu"`` added.
.. versionchanged:: 3.3
Support for ``"%li"``, ``"%lli"`` and ``"%zi"`` added.
+ .. versionchanged:: 3.4
+ Support width and precision formatter for ``"%s"``, ``"%A"``, ``"%U"``,
+ ``"%V"``, ``"%S"``, ``"%R"`` added.
+
.. c:function:: PyObject* PyUnicode_FromFormatV(const char *format, va_list vargs)
diff --git a/Doc/c-api/veryhigh.rst b/Doc/c-api/veryhigh.rst
index 14ef8df..f7ed4c7 100644
--- a/Doc/c-api/veryhigh.rst
+++ b/Doc/c-api/veryhigh.rst
@@ -144,6 +144,37 @@ the same library that the Python runtime is using.
(:func:`sys.getfilesystemencoding`). Returns ``0`` at EOF.
+.. c:var:: int (*PyOS_InputHook)(void)
+
+ Can be set to point to a function with the prototype
+ ``int func(void)``. The function will be called when Python's
+ interpreter prompt is about to become idle and wait for user input
+ from the terminal. The return value is ignored. Overriding this
+ hook can be used to integrate the interpreter's prompt with other
+ event loops, as done in the :file:`Modules/_tkinter.c` in the
+ Python source code.
+
+
+.. c:var:: char* (*PyOS_ReadlineFunctionPointer)(FILE *, FILE *, const char *)
+
+ Can be set to point to a function with the prototype
+ ``char *func(FILE *stdin, FILE *stdout, char *prompt)``,
+ overriding the default function used to read a single line of input
+ at the interpreter's prompt. The function is expected to output
+ the string *prompt* if it's not *NULL*, and then read a line of
+ input from the provided standard input file, returning the
+ resulting string. For example, The :mod:`readline` module sets
+ this hook to provide line-editing and tab-completion features.
+
+ The result must be a string allocated by :c:func:`PyMem_RawMalloc` or
+ :c:func:`PyMem_RawRealloc`, or *NULL* if an error occurred.
+
+ .. versionchanged:: 3.4
+ The result must be allocated by :c:func:`PyMem_RawMalloc` or
+ :c:func:`PyMem_RawRealloc`, instead of being allocated by
+ :c:func:`PyMem_Malloc` or :c:func:`PyMem_Realloc`.
+
+
.. c:function:: struct _node* PyParser_SimpleParseString(const char *str, int start)
This is a simplified interface to
@@ -235,16 +266,15 @@ the same library that the Python runtime is using.
*optimize* set to ``-1``.
-.. c:function:: PyObject* Py_CompileStringExFlags(const char *str, const char *filename, int start, PyCompilerFlags *flags, int optimize)
+.. c:function:: PyObject* Py_CompileStringObject(const char *str, PyObject *filename, int start, PyCompilerFlags *flags, int optimize)
Parse and compile the Python source code in *str*, returning the resulting code
object. The start token is given by *start*; this can be used to constrain the
code which can be compiled and should be :const:`Py_eval_input`,
:const:`Py_file_input`, or :const:`Py_single_input`. The filename specified by
*filename* is used to construct the code object and may appear in tracebacks or
- :exc:`SyntaxError` exception messages, it is decoded from the filesystem
- encoding (:func:`sys.getfilesystemencoding`). This returns *NULL* if the
- code cannot be parsed or compiled.
+ :exc:`SyntaxError` exception messages. This returns *NULL* if the code
+ cannot be parsed or compiled.
The integer *optimize* specifies the optimization level of the compiler; a
value of ``-1`` selects the optimization level of the interpreter as given by
@@ -252,8 +282,15 @@ the same library that the Python runtime is using.
``__debug__`` is true), ``1`` (asserts are removed, ``__debug__`` is false)
or ``2`` (docstrings are removed too).
- .. versionadded:: 3.2
+ .. versionadded:: 3.4
+
+
+.. c:function:: PyObject* Py_CompileStringExFlags(const char *str, const char *filename, int start, PyCompilerFlags *flags, int optimize)
+ Like :c:func:`Py_CompileStringExFlags`, but *filename* is a byte string
+ decoded from the filesystem encoding (:func:`os.fsdecode`).
+
+ .. versionadded:: 3.2
.. c:function:: PyObject* PyEval_EvalCode(PyObject *co, PyObject *globals, PyObject *locals)
@@ -285,6 +322,10 @@ the same library that the Python runtime is using.
it causes an exception to immediately be thrown; this is used for the
:meth:`~generator.throw` methods of generator objects.
+ .. versionchanged:: 3.4
+ This function now includes a debug assertion to help ensure that it
+ does not silently discard an active exception.
+
.. c:function:: int PyEval_MergeCompilerFlags(PyCompilerFlags *cf)
@@ -338,4 +379,3 @@ the same library that the Python runtime is using.
This bit can be set in *flags* to cause division operator ``/`` to be
interpreted as "true division" according to :pep:`238`.
-
diff --git a/Doc/conf.py b/Doc/conf.py
index 5b63cad..c0ed8c6 100644
--- a/Doc/conf.py
+++ b/Doc/conf.py
@@ -61,6 +61,8 @@ add_module_names = True
# By default, highlight as Python 3.
highlight_language = 'python3'
+needs_sphinx = '1.1'
+
# Options for HTML output
# -----------------------
@@ -118,11 +120,11 @@ _stdauthor = r'Guido van Rossum\\Fred L. Drake, Jr., editor'
latex_documents = [
('c-api/index', 'c-api.tex',
'The Python/C API', _stdauthor, 'manual'),
- ('distutils/index', 'distutils.tex',
+ ('distributing/index', 'distributing.tex',
'Distributing Python Modules', _stdauthor, 'manual'),
('extending/index', 'extending.tex',
'Extending and Embedding Python', _stdauthor, 'manual'),
- ('install/index', 'install.tex',
+ ('installing/index', 'installing.tex',
'Installing Python Modules', _stdauthor, 'manual'),
('library/index', 'library.tex',
'The Python Library Reference', _stdauthor, 'manual'),
diff --git a/Doc/contents.rst b/Doc/contents.rst
index c0c6af3..29b07db 100644
--- a/Doc/contents.rst
+++ b/Doc/contents.rst
@@ -11,8 +11,8 @@
library/index.rst
extending/index.rst
c-api/index.rst
- distutils/index.rst
- install/index.rst
+ distributing/index.rst
+ installing/index.rst
howto/index.rst
faq/index.rst
glossary.rst
@@ -21,3 +21,11 @@
bugs.rst
copyright.rst
license.rst
+
+.. include legacy packaging docs in build
+
+.. toctree::
+ :hidden:
+
+ distutils/index.rst
+ install/index.rst
diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat
index a42584c..6025617 100644
--- a/Doc/data/refcounts.dat
+++ b/Doc/data/refcounts.dat
@@ -29,7 +29,7 @@
# reference to the item argument!
# The parameter names are as they appear in the API manual, not the source
-# code.
+# code.
PyBool_FromLong:PyObject*::+1:
PyBool_FromLong:long:v:0:
@@ -220,6 +220,11 @@ PyDict_GetItemString:PyObject*::0:
PyDict_GetItemString:PyObject*:p:0:
PyDict_GetItemString:const char*:key::
+PyDict_SetDefault:PyObject*::0:
+PyDict_SetDefault:PyObject*:p:0:
+PyDict_SetDefault:PyObject*:key:0:conditionally +1 if inserted into the dict
+PyDict_SetDefault:PyObject*:default:0:conditionally +1 if inserted into the dict
+
PyDict_Items:PyObject*::+1:
PyDict_Items:PyObject*:p:0:
@@ -503,13 +508,13 @@ PyImport_AddModule:const char*:name::
PyImport_Cleanup:void:::
PyImport_ExecCodeModule:PyObject*::+1:
-PyImport_ExecCodeModule:char*:name::
+PyImport_ExecCodeModule:const char*:name::
PyImport_ExecCodeModule:PyObject*:co:0:
PyImport_ExecCodeModuleEx:PyObject*::+1:
-PyImport_ExecCodeModuleEx:char*:name::
+PyImport_ExecCodeModuleEx:const char*:name::
PyImport_ExecCodeModuleEx:PyObject*:co:0:
-PyImport_ExecCodeModuleEx:char*:pathname::
+PyImport_ExecCodeModuleEx:const char*:pathname::
PyImport_GetMagicNumber:long:::
@@ -519,7 +524,7 @@ PyImport_Import:PyObject*::+1:
PyImport_Import:PyObject*:name:0:
PyImport_ImportFrozenModule:int:::
-PyImport_ImportFrozenModule:char*:::
+PyImport_ImportFrozenModule:const char*:::
PyImport_ImportModule:PyObject*::+1:
PyImport_ImportModule:const char*:name::
@@ -668,7 +673,7 @@ PyLong_FromUnsignedLongLong:PyObject*::+1:
PyLong_FromUnsignedLongLong:unsigned long long:v::
PyLong_FromString:PyObject*::+1:
-PyLong_FromString:char*:str::
+PyLong_FromString:const char*:str::
PyLong_FromString:char**:pend::
PyLong_FromString:int:base::
@@ -696,7 +701,7 @@ PyMapping_DelItemString:const char*:key::
PyMapping_GetItemString:PyObject*::+1:
PyMapping_GetItemString:PyObject*:o:0:
-PyMapping_GetItemString:char*:key::
+PyMapping_GetItemString:const char*:key::
PyMapping_HasKey:int:::
PyMapping_HasKey:PyObject*:o:0:
@@ -704,7 +709,7 @@ PyMapping_HasKey:PyObject*:key::
PyMapping_HasKeyString:int:::
PyMapping_HasKeyString:PyObject*:o:0:
-PyMapping_HasKeyString:char*:key::
+PyMapping_HasKeyString:const char*:key::
PyMapping_Items:PyObject*::+1:
PyMapping_Items:PyObject*:o:0:
@@ -717,7 +722,7 @@ PyMapping_Length:PyObject*:o:0:
PyMapping_SetItemString:int:::
PyMapping_SetItemString:PyObject*:o:0:
-PyMapping_SetItemString:char*:key::
+PyMapping_SetItemString:const char*:key::
PyMapping_SetItemString:PyObject*:v:+1:
PyMapping_Values:PyObject*::+1:
@@ -730,7 +735,7 @@ PyMarshal_ReadObjectFromFile:PyObject*::+1:
PyMarshal_ReadObjectFromFile:FILE*:file::
PyMarshal_ReadObjectFromString:PyObject*::+1:
-PyMarshal_ReadObjectFromString:char*:string::
+PyMarshal_ReadObjectFromString:const char*:string::
PyMarshal_ReadObjectFromString:int:len::
PyMarshal_WriteObjectToString:PyObject*::+1:
@@ -902,7 +907,7 @@ PyNumber_Xor:PyObject*::+1:
PyNumber_Xor:PyObject*:o1:0:
PyNumber_Xor:PyObject*:o2:0:
-PyObject_AsFileDescriptor:int:::
+PyObject_AsFileDescriptor:int:::
PyObject_AsFileDescriptor:PyObject*:o:0:
PyObject_Call:PyObject*::+1:
@@ -912,7 +917,7 @@ PyObject_Call:PyObject*:kw:0:
PyObject_CallFunction:PyObject*::+1:
PyObject_CallFunction:PyObject*:callable_object:0:
-PyObject_CallFunction:char*:format::
+PyObject_CallFunction:const char*:format::
PyObject_CallFunction::...::
PyObject_CallFunctionObjArgs:PyObject*::+1:
@@ -921,8 +926,8 @@ PyObject_CallFunctionObjArgs::...::
PyObject_CallMethod:PyObject*::+1:
PyObject_CallMethod:PyObject*:o:0:
-PyObject_CallMethod:char*:m::
-PyObject_CallMethod:char*:format::
+PyObject_CallMethod:const char*:m::
+PyObject_CallMethod:const char*:format::
PyObject_CallMethod::...::
PyObject_CallMethodObjArgs:PyObject*::+1:
diff --git a/Doc/distributing/index.rst b/Doc/distributing/index.rst
new file mode 100644
index 0000000..d671041
--- /dev/null
+++ b/Doc/distributing/index.rst
@@ -0,0 +1,143 @@
+.. _distributing-index:
+
+###############################
+ Distributing Python Modules
+###############################
+
+:Email: distutils-sig@python.org
+
+
+As a popular open source development project, Python has an active
+supporting community of contributors and users that also make their software
+available for other Python developers to use under open source license terms.
+
+This allows Python users to share and collaborate effectively, benefiting
+from the solutions others have already created to common (and sometimes
+even rare!) problems, as well as potentially contributing their own
+solutions to the common pool.
+
+This guide covers the distribution part of the process. For a guide to
+installing other Python projects, refer to the
+:ref:`installation guide <installing-index>`.
+
+.. note::
+
+ For corporate and other institutional users, be aware that many
+ organisations have their own policies around using and contributing to
+ open source software. Please take such policies into account when making
+ use of the distribution and installation tools provided with Python.
+
+
+Key terms
+=========
+
+* the `Python Package Index <https://pypi.python.org/pypi>`__ is a public
+ repository of open source licensed packages made available for use by
+ other Python users
+* the `Python Packaging Authority
+ <http://packaging.python.org/en/latest/future.html>`__ are the group of
+ developers and documentation authors responsible for the maintenance and
+ evolution of the standard packaging tools and the associated metadata and
+ file format standards. They maintain a variety of tools, documentation
+ and issue trackers on both `GitHub <https://github.com/pypa>`__ and
+ `BitBucket <https://bitbucket.org/pypa/>`__.
+* ``distutils`` is the original build and distribution system first added to
+ the Python standard library in 1998. While direct use of ``distutils`` is
+ being phased out, it still laid the foundation for the current packaging
+ and distribution infrastructure, and it not only remains part of the
+ standard library, but its name lives on in other ways (such as the name
+ of the mailing list used to coordinate Python packaging standards
+ development).
+
+
+Open source licensing and collaboration
+=======================================
+
+In most parts of the world, software is automatically covered by copyright.
+This means that other developers require explicit permission to copy, use,
+modify and redistribute the software.
+
+Open source licensing is a way of explicitly granting such permission in a
+relatively consistent way, allowing developers to share and collaborate
+efficiently by making common solutions to various problems freely available.
+This leaves many developers free to spend more time focusing on the problems
+that are relatively unique to their specific situation.
+
+The distribution tools provided with Python are designed to make it
+reasonably straightforward for developers to make their own contributions
+back to that common pool of software if they choose to do so.
+
+The same distribution tools can also be used to distribute software within
+an organisation, regardless of whether that software is published as open
+source software or not.
+
+
+Installing the tools
+====================
+
+The standard library does not include build tools that support modern
+Python packaging standards, as the core development team has found that it
+is important to have standard tools that work consistently, even on older
+versions of Python.
+
+The currently recommended build and distribution tools can be installed
+using ``pip``::
+
+ pip install setuptools wheel twine
+
+
+Reading the guide
+=================
+
+The Python Packaging User Guide covers the various key steps and elements
+involved in creating a project
+
+* `Project structure`_
+* `Building and packaging the project`_
+* `Uploading the project to the Python Package Index`_
+
+.. _Project structure: \
+ http://packaging.python.org/en/latest/tutorial.html#creating-your-own-project
+.. _Building and packaging the project: \
+ http://packaging.python.org/en/latest/tutorial.html#building-packaging-your-project
+.. _Uploading the project to the Python Package Index: \
+ http://packaging.python.org/en/latest/tutorial.html#uploading-your-project-to-pypi
+
+
+How do I...?
+============
+
+These are quick answers or links for some common tasks.
+
+... choose a name for my project?
+---------------------------------
+
+This isn't an easy topic, but here are a few tips:
+
+* check the Python Package Index to see if the name is already in use
+* check popular hosting sites like GitHub, BitBucket, etc to see if there
+ is already a project with that name
+* check what comes up in a web search for the name you're considering
+* avoid particularly common words, especially ones with multiple meanings,
+ as they can make it difficult for users to find your software when
+ searching for it
+
+
+... create and distribute binary extensions?
+--------------------------------------------
+
+This is actually quite a complex topic, with a variety of alternatives
+available depending on exactly what you're aiming to achieve. See the
+Python Packaging User Guide for more information and recommendations.
+
+.. seealso::
+
+ `Python Packaging User Guide: Binary Extensions
+ <http://packaging.python.org/en/latest/extensions.html>`__
+
+.. other topics:
+
+ Once the Development & Deployment part of PPUG is fleshed out, some of
+ those sections should be linked from new questions here (most notably,
+ we should have a question about avoiding depending on PyPI that links to
+ http://packaging.python.org/en/latest/deployment.html#pypi-mirrors-and-caches)
diff --git a/Doc/distutils/apiref.rst b/Doc/distutils/apiref.rst
index 9853f02..e1357fa 100644
--- a/Doc/distutils/apiref.rst
+++ b/Doc/distutils/apiref.rst
@@ -853,17 +853,6 @@ Windows. It also contains the Mingw32CCompiler class which handles the mingw32
port of GCC (same as cygwin in no-cygwin mode).
-:mod:`distutils.emxccompiler` --- OS/2 EMX Compiler
-===================================================
-
-.. module:: distutils.emxccompiler
- :synopsis: OS/2 EMX Compiler support
-
-
-This module provides the EMXCCompiler class, a subclass of
-:class:`UnixCCompiler` that handles the EMX port of the GNU C compiler to OS/2.
-
-
:mod:`distutils.archive_util` --- Archiving utilities
======================================================
@@ -1004,7 +993,7 @@ directories.
Files in *src* that begin with :file:`.nfs` are skipped (more information on
these files is available in answer D2 of the `NFS FAQ page
- <http://nfs.sourceforge.net/#section_d>`_.
+ <http://nfs.sourceforge.net/#section_d>`_).
.. versionchanged:: 3.3.1
NFS files are ignored.
diff --git a/Doc/distutils/builtdist.rst b/Doc/distutils/builtdist.rst
index d1ab7db..83c68ae 100644
--- a/Doc/distutils/builtdist.rst
+++ b/Doc/distutils/builtdist.rst
@@ -239,7 +239,8 @@ tedious and error-prone, so it's usually best to put them in the setup
configuration file, :file:`setup.cfg`\ ---see section :ref:`setup-config`. If
you distribute or package many Python module distributions, you might want to
put options that apply to all of them in your personal Distutils configuration
-file (:file:`~/.pydistutils.cfg`).
+file (:file:`~/.pydistutils.cfg`). If you want to temporarily disable
+this file, you can pass the :option:`--no-user-cfg` option to :file:`setup.py`.
There are three steps to building a binary RPM package, all of which are
handled automatically by the Distutils:
diff --git a/Doc/distutils/index.rst b/Doc/distutils/index.rst
index 1a6f04c..90d1c1a 100644
--- a/Doc/distutils/index.rst
+++ b/Doc/distutils/index.rst
@@ -1,8 +1,8 @@
.. _distutils-index:
-###############################
- Distributing Python Modules
-###############################
+##############################################
+ Distributing Python Modules (Legacy version)
+##############################################
:Authors: Greg Ward, Anthony Baxter
:Email: distutils-sig@python.org
diff --git a/Doc/distutils/sourcedist.rst b/Doc/distutils/sourcedist.rst
index 1666436..427b7b1 100644
--- a/Doc/distutils/sourcedist.rst
+++ b/Doc/distutils/sourcedist.rst
@@ -26,16 +26,16 @@ to create a gzipped tarball and a zip file. The available formats are:
+===========+=========================+=========+
| ``zip`` | zip file (:file:`.zip`) | (1),(3) |
+-----------+-------------------------+---------+
-| ``gztar`` | gzip'ed tar file | (2),(4) |
+| ``gztar`` | gzip'ed tar file | \(2) |
| | (:file:`.tar.gz`) | |
+-----------+-------------------------+---------+
-| ``bztar`` | bzip2'ed tar file | \(4) |
+| ``bztar`` | bzip2'ed tar file | |
| | (:file:`.tar.bz2`) | |
+-----------+-------------------------+---------+
| ``ztar`` | compressed tar file | \(4) |
| | (:file:`.tar.Z`) | |
+-----------+-------------------------+---------+
-| ``tar`` | tar file (:file:`.tar`) | \(4) |
+| ``tar`` | tar file (:file:`.tar`) | |
+-----------+-------------------------+---------+
Notes:
@@ -51,8 +51,16 @@ Notes:
of the standard Python library since Python 1.6)
(4)
- requires external utilities: :program:`tar` and possibly one of :program:`gzip`,
- :program:`bzip2`, or :program:`compress`
+ requires the :program:`compress` program. Notice that this format is now
+ pending for deprecation and will be removed in the future versions of Python.
+
+When using any ``tar`` format (``gztar``, ``bztar``, ``ztar`` 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
.. _manifest:
@@ -68,7 +76,7 @@ source distribution:
:option:`packages` options
* all C source files mentioned in the :option:`ext_modules` or
- :option:`libraries` options (
+ :option:`libraries` options
.. XXX getting C library sources currently broken---no
:meth:`get_source_files` method in :file:`build_clib.py`!
diff --git a/Doc/extending/embedding.rst b/Doc/extending/embedding.rst
index bd66086..6cb686a 100644
--- a/Doc/extending/embedding.rst
+++ b/Doc/extending/embedding.rst
@@ -285,14 +285,14 @@ be directly useful to you:
* ``pythonX.Y-config --cflags`` will give you the recommended flags when
compiling::
- $ /opt/bin/python3.3-config --cflags
- -I/opt/include/python3.3m -I/opt/include/python3.3m -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes
+ $ /opt/bin/python3.4-config --cflags
+ -I/opt/include/python3.4m -I/opt/include/python3.4m -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes
* ``pythonX.Y-config --ldflags`` will give you the recommended flags when
linking::
- $ /opt/bin/python3.3-config --ldflags
- -L/opt/lib/python3.3/config-3.3m -lpthread -ldl -lutil -lm -lpython3.3m -Xlinker -export-dynamic
+ $ /opt/bin/python3.4-config --ldflags
+ -L/opt/lib/python3.4/config-3.4m -lpthread -ldl -lutil -lm -lpython3.4m -Xlinker -export-dynamic
.. note::
To avoid confusion between several Python installations (and especially
diff --git a/Doc/extending/index.rst b/Doc/extending/index.rst
index 44a7f92..dd43926 100644
--- a/Doc/extending/index.rst
+++ b/Doc/extending/index.rst
@@ -21,14 +21,31 @@ Python) that give the language its wide application range.
For a detailed description of the whole Python/C API, see the separate
:ref:`c-api-index`.
-.. note::
- This guide only covers the basic tools for creating extensions provided
- as part of this version of CPython. Third party tools may offer simpler
- alternatives. Refer to the `binary extensions section
- <https://python-packaging-user-guide.readthedocs.org/en/latest/extensions.html>`__
- in the Python Packaging User Guide for more information.
+Recommended third party tools
+=============================
+This guide only covers the basic tools for creating extensions provided
+as part of this version of CPython. Third party tools like Cython,
+``cffi``, SWIG and Numba offer both simpler and more sophisticated
+approaches to creating C and C++ extensions for Python.
+
+.. seealso::
+
+ `Python Packaging User Guide: Binary Extensions <https://packaging.python.org/en/latest/extensions.html>`_
+ The Python Packaging User Guide not only covers several available
+ tools that simplify the creation of binary extensions, but also
+ discusses the various reasons why creating an extension module may be
+ desirable in the first place.
+
+
+Creating extensions without third party tools
+=============================================
+
+This section of the guide covers creating C and C++ extensions without
+assistance from third party tools. It is intended primarily for creators
+of those tools, rather than being a recommended way to create your own
+C extensions.
.. toctree::
:maxdepth: 2
@@ -38,4 +55,17 @@ For a detailed description of the whole Python/C API, see the separate
newtypes.rst
building.rst
windows.rst
+
+Embedding the CPython runtime in a larger application
+=====================================================
+
+Sometimes, rather than creating an extension that runs inside the Python
+interpreter as the main application, it is desirable to instead embed
+the CPython runtime inside a larger application. This section covers
+some of the details involved in doing that successfully.
+
+.. toctree::
+ :maxdepth: 2
+ :numbered:
+
embedding.rst
diff --git a/Doc/extending/newtypes.rst b/Doc/extending/newtypes.rst
index f484ba4..45b5721 100644
--- a/Doc/extending/newtypes.rst
+++ b/Doc/extending/newtypes.rst
@@ -157,7 +157,8 @@ to :const:`Py_TPFLAGS_DEFAULT`. ::
Py_TPFLAGS_DEFAULT, /* tp_flags */
All types should include this constant in their flags. It enables all of the
-members defined by the current version of Python.
+members defined until at least Python 3.3. If you need further members,
+you will need to OR the corresponding flags.
We provide a doc string for the type in :c:member:`~PyTypeObject.tp_doc`. ::
@@ -928,8 +929,9 @@ Finalization and De-allocation
This function is called when the reference count of the instance of your type is
reduced to zero and the Python interpreter wants to reclaim it. If your type
-has memory to free or other clean-up to perform, put it here. The object itself
-needs to be freed here as well. Here is an example of this function::
+has memory to free or other clean-up to perform, you can put it here. The
+object itself needs to be freed here as well. Here is an example of this
+function::
static void
newdatatype_dealloc(newdatatypeobject * obj)
@@ -980,6 +982,22 @@ done. This can be done using the :c:func:`PyErr_Fetch` and
Py_TYPE(obj)->tp_free((PyObject*)self);
}
+.. note::
+ There are limitations to what you can safely do in a deallocator function.
+ First, if your type supports garbage collection (using :c:member:`~PyTypeObject.tp_traverse`
+ and/or :c:member:`~PyTypeObject.tp_clear`), some of the object's members can have been
+ cleared or finalized by the time :c:member:`~PyTypeObject.tp_dealloc` is called. Second, in
+ :c:member:`~PyTypeObject.tp_dealloc`, your object is in an unstable state: its reference
+ count is equal to zero. Any call to a non-trivial object or API (as in the
+ example above) might end up calling :c:member:`~PyTypeObject.tp_dealloc` again, causing a
+ double free and a crash.
+
+ Starting with Python 3.4, it is recommended not to put any complex
+ finalization code in :c:member:`~PyTypeObject.tp_dealloc`, and instead use the new
+ :c:member:`~PyTypeObject.tp_finalize` type method.
+
+ .. seealso::
+ :pep:`442` explains the new finalization scheme.
.. index::
single: string; object representation
diff --git a/Doc/faq/extending.rst b/Doc/faq/extending.rst
index a9a234b..d196e86 100644
--- a/Doc/faq/extending.rst
+++ b/Doc/faq/extending.rst
@@ -95,8 +95,8 @@ To test the type of an object, first make sure it isn't *NULL*, and then use
There is also a high-level API to Python objects which is provided by the
so-called 'abstract' interface -- read ``Include/abstract.h`` for further
details. It allows interfacing with any kind of Python sequence using calls
-like :c:func:`PySequence_Length`, :c:func:`PySequence_GetItem`, etc.) as well
-as many other useful protocols such as numbers (:c:func:`PyNumber_Index` et.
+like :c:func:`PySequence_Length`, :c:func:`PySequence_GetItem`, etc. as well
+as many other useful protocols such as numbers (:c:func:`PyNumber_Index` et
al.) and mappings in the PyMapping APIs.
diff --git a/Doc/faq/gui.rst b/Doc/faq/gui.rst
index 5827f28..3192163 100644
--- a/Doc/faq/gui.rst
+++ b/Doc/faq/gui.rst
@@ -32,14 +32,14 @@ install (since it comes included with most
`binary distributions <http://www.python.org/download/>`_ of Python) and use.
For more info about Tk, including pointers to the source, see the
`Tcl/Tk home page <http://www.tcl.tk>`_. Tcl/Tk is fully portable to the
-MacOS, Windows, and Unix platforms.
+Mac OS X, Windows, and Unix platforms.
wxWidgets
---------
wxWidgets (http://www.wxwidgets.org) is a free, portable GUI class
library written in C++ that provides a native look and feel on a
-number of platforms, with Windows, MacOS X, GTK, X11, all listed as
+number of platforms, with Windows, Mac OS X, GTK, X11, all listed as
current stable targets. Language bindings are available for a number
of languages including Python, Perl, Ruby, etc.
@@ -102,13 +102,9 @@ For OpenGL bindings, see `PyOpenGL <http://pyopengl.sourceforge.net>`_.
What platform-specific GUI toolkits exist for Python?
========================================================
-`The Mac port <http://python.org/download/mac>`_ by Jack Jansen has a rich and
-ever-growing set of modules that support the native Mac toolbox calls. The port
-supports MacOS X's Carbon libraries.
-
By installing the `PyObjc Objective-C bridge
-<http://pyobjc.sourceforge.net>`_, Python programs can use MacOS X's
-Cocoa libraries. See the documentation that comes with the Mac port.
+<http://pyobjc.sourceforge.net>`_, Python programs can use Mac OS X's
+Cocoa libraries.
:ref:`Pythonwin <windows-faq>` by Mark Hammond includes an interface to the
Microsoft Foundation Classes and a Python programming environment
diff --git a/Doc/faq/library.rst b/Doc/faq/library.rst
index 2566932..5f4ff17 100644
--- a/Doc/faq/library.rst
+++ b/Doc/faq/library.rst
@@ -211,7 +211,7 @@ using curses, but curses is a fairly large module to learn.
try:
c = sys.stdin.read(1)
print("Got character", repr(c))
- except IOError:
+ except OSError:
pass
finally:
termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
@@ -224,7 +224,11 @@ using curses, but curses is a fairly large module to learn.
:func:`termios.tcsetattr` turns off stdin's echoing and disables canonical
mode. :func:`fcntl.fnctl` is used to obtain stdin's file descriptor flags
and modify them for non-blocking mode. Since reading stdin when it is empty
- results in an :exc:`IOError`, this error is caught and ignored.
+ results in an :exc:`OSError`, this error is caught and ignored.
+
+ .. versionchanged:: 3.3
+ *sys.stdin.read* used to raise :exc:`IOError`. Starting from Python 3.3
+ :exc:`IOError` is alias for :exc:`OSError`.
Threads
diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst
index 280d5e1..8dcaa3a 100644
--- a/Doc/faq/programming.rst
+++ b/Doc/faq/programming.rst
@@ -711,7 +711,7 @@ By default, these interpret the number as decimal, so that ``int('0144') ==
144`` and ``int('0x144')`` raises :exc:`ValueError`. ``int(string, base)`` takes
the base to convert from as a second optional argument, so ``int('0x144', 16) ==
324``. If the base is specified as 0, the number is interpreted using Python's
-rules: a leading '0' indicates octal, and '0x' indicates a hex number.
+rules: a leading '0o' indicates octal, and '0x' indicates a hex number.
Do not use the built-in function :func:`eval` if all you need is to convert
strings to numbers. :func:`eval` will be significantly slower and it presents a
@@ -732,7 +732,7 @@ To convert, e.g., the number 144 to the string '144', use the built-in type
constructor :func:`str`. If you want a hexadecimal or octal representation, use
the built-in functions :func:`hex` or :func:`oct`. For fancy formatting, see
the :ref:`string-formatting` section, e.g. ``"{:04d}".format(144)`` yields
-``'0144'`` and ``"{:.3f}".format(1/3)`` yields ``'0.333'``.
+``'0144'`` and ``"{:.3f}".format(1.0/3.0)`` yields ``'0.333'``.
How do I modify a string in place?
@@ -1751,12 +1751,12 @@ When I edit an imported module and reimport it, the changes don't show up. Why
For reasons of efficiency as well as consistency, Python only reads the module
file on the first time a module is imported. If it didn't, in a program
consisting of many modules where each one imports the same basic module, the
-basic module would be parsed and re-parsed many times. To force rereading of a
+basic module would be parsed and re-parsed many times. To force re-reading of a
changed module, do this::
- import imp
+ import importlib
import modname
- imp.reload(modname)
+ importlib.reload(modname)
Warning: this technique is not 100% fool-proof. In particular, modules
containing statements like ::
@@ -1768,10 +1768,10 @@ module contains class definitions, existing class instances will *not* be
updated to use the new class definition. This can result in the following
paradoxical behaviour:
- >>> import imp
+ >>> import importlib
>>> import cls
>>> c = cls.C() # Create an instance of C
- >>> imp.reload(cls)
+ >>> importlib.reload(cls)
<module 'cls' from 'cls.py'>
>>> isinstance(c, cls.C) # isinstance is false?!?
False
diff --git a/Doc/glossary.rst b/Doc/glossary.rst
index b48eb1e..f71a1f7 100644
--- a/Doc/glossary.rst
+++ b/Doc/glossary.rst
@@ -310,6 +310,15 @@ Glossary
>>> sum(i*i for i in range(10)) # sum of squares 0, 1, 4, ... 81
285
+ generic function
+ A function composed of multiple functions implementing the same operation
+ for different types. Which implementation should be used during a call is
+ determined by the dispatch algorithm.
+
+ See also the :term:`single dispatch` glossary entry, the
+ :func:`functools.singledispatch` decorator, and :pep:`443`.
+
+
GIL
See :term:`global interpreter lock`.
@@ -530,6 +539,10 @@ Glossary
See also :term:`package`.
+ module spec
+ A namespace containing the import-related information used to load a
+ module.
+
MRO
See :term:`method resolution order`.
@@ -671,20 +684,27 @@ Glossary
positional argument
See :term:`argument`.
- provisional package
- A provisional package is one which has been deliberately excluded from
+ provisional API
+ A provisional API is one which has been deliberately excluded from
the standard library's backwards compatibility guarantees. While major
- changes to such packages are not expected, as long as they are marked
+ changes to such interfaces are not expected, as long as they are marked
provisional, backwards incompatible changes (up to and including removal
- of the package) may occur if deemed necessary by core developers. Such
+ of the interface) may occur if deemed necessary by core developers. Such
changes will not be made gratuitously -- they will occur only if serious
- flaws are uncovered that were missed prior to the inclusion of the
- package.
+ fundamental flaws are uncovered that were missed prior to the inclusion
+ of the API.
+
+ Even for provisional APIs, backwards incompatible changes are seen as
+ a "solution of last resort" - every attempt will still be made to find
+ a backwards compatible resolution to any identified problems.
This process allows the standard library to continue to evolve over
time, without locking in problematic design errors for extended periods
of time. See :pep:`411` for more details.
+ provisional package
+ See :term:`provisional API`.
+
Python 3000
Nickname for the Python 3.x release line (coined long ago when the
release of version 3 was something in the distant future.) This is also
@@ -771,6 +791,10 @@ Glossary
interface can be registered explicitly using
:func:`~abc.register`.
+ single dispatch
+ A form of :term:`generic function` dispatch where the implementation is
+ chosen based on the type of a single argument.
+
slice
An object usually containing a portion of a :term:`sequence`. A slice is
created using the subscript notation, ``[]`` with colons between numbers
diff --git a/Doc/howto/clinic.rst b/Doc/howto/clinic.rst
new file mode 100644
index 0000000..750ddbe
--- /dev/null
+++ b/Doc/howto/clinic.rst
@@ -0,0 +1,1686 @@
+**********************
+Argument Clinic How-To
+**********************
+
+:author: Larry Hastings
+
+
+.. topic:: Abstract
+
+ Argument Clinic is a preprocessor for CPython C files.
+ Its purpose is to automate all the boilerplate involved
+ with writing argument parsing code for "builtins".
+ This document shows you how to convert your first C
+ function to work with Argument Clinic, and then introduces
+ some advanced topics on Argument Clinic usage.
+
+ Currently Argument Clinic is considered internal-only
+ for CPython. Its use is not supported for files outside
+ CPython, and no guarantees are made regarding backwards
+ compatibility for future versions. In other words: if you
+ maintain an external C extension for CPython, you're welcome
+ to experiment with Argument Clinic in your own code. But the
+ version of Argument Clinic that ships with CPython 3.5 *could*
+ be totally incompatible and break all your code.
+
+The Goals Of Argument Clinic
+============================
+
+Argument Clinic's primary goal
+is to take over responsibility for all argument parsing code
+inside CPython. This means that, when you convert a function
+to work with Argument Clinic, that function should no longer
+do any of its own argument parsing--the code generated by
+Argument Clinic should be a "black box" to you, where CPython
+calls in at the top, and your code gets called at the bottom,
+with ``PyObject *args`` (and maybe ``PyObject *kwargs``)
+magically converted into the C variables and types you need.
+
+In order for Argument Clinic to accomplish its primary goal,
+it must be easy to use. Currently, working with CPython's
+argument parsing library is a chore, requiring maintaining
+redundant information in a surprising number of places.
+When you use Argument Clinic, you don't have to repeat yourself.
+
+Obviously, no one would want to use Argument Clinic unless
+it's solving their problem--and without creating new problems of
+its own.
+So it's paramount that Argument Clinic generate correct code.
+It'd be nice if the code was faster, too, but at the very least
+it should not introduce a major speed regression. (Eventually Argument
+Clinic *should* make a major speedup possible--we could
+rewrite its code generator to produce tailor-made argument
+parsing code, rather than calling the general-purpose CPython
+argument parsing library. That would make for the fastest
+argument parsing possible!)
+
+Additionally, Argument Clinic must be flexible enough to
+work with any approach to argument parsing. Python has
+some functions with some very strange parsing behaviors;
+Argument Clinic's goal is to support all of them.
+
+Finally, the original motivation for Argument Clinic was
+to provide introspection "signatures" for CPython builtins.
+It used to be, the introspection query functions would throw
+an exception if you passed in a builtin. With Argument
+Clinic, that's a thing of the past!
+
+One idea you should keep in mind, as you work with
+Argument Clinic: the more information you give it, the
+better job it'll be able to do.
+Argument Clinic is admittedly relatively simple right
+now. But as it evolves it will get more sophisticated,
+and it should be able to do many interesting and smart
+things with all the information you give it.
+
+
+Basic Concepts And Usage
+========================
+
+Argument Clinic ships with CPython; you'll find it in ``Tools/clinic/clinic.py``.
+If you run that script, specifying a C file as an argument::
+
+ % python3 Tools/clinic/clinic.py foo.c
+
+Argument Clinic will scan over the file looking for lines that
+look exactly like this::
+
+ /*[clinic input]
+
+When it finds one, it reads everything up to a line that looks
+exactly like this::
+
+ [clinic start generated code]*/
+
+Everything in between these two lines is input for Argument Clinic.
+All of these lines, including the beginning and ending comment
+lines, are collectively called an Argument Clinic "block".
+
+When Argument Clinic parses one of these blocks, it
+generates output. This output is rewritten into the C file
+immediately after the block, followed by a comment containing a checksum.
+The Argument Clinic block now looks like this::
+
+ /*[clinic input]
+ ... clinic input goes here ...
+ [clinic start generated code]*/
+ ... clinic output goes here ...
+ /*[clinic end generated code: checksum=...]*/
+
+If you run Argument Clinic on the same file a second time, Argument Clinic
+will discard the old output and write out the new output with a fresh checksum
+line. However, if the input hasn't changed, the output won't change either.
+
+You should never modify the output portion of an Argument Clinic block. Instead,
+change the input until it produces the output you want. (That's the purpose of the
+checksum--to detect if someone changed the output, as these edits would be lost
+the next time Argument Clinic writes out fresh output.)
+
+For the sake of clarity, here's the terminology we'll use with Argument Clinic:
+
+* The first line of the comment (``/*[clinic input]``) is the *start line*.
+* The last line of the initial comment (``[clinic start generated code]*/``) is the *end line*.
+* The last line (``/*[clinic end generated code: checksum=...]*/``) is the *checksum line*.
+* In between the start line and the end line is the *input*.
+* In between the end line and the checksum line is the *output*.
+* All the text collectively, from the start line to the checksum line inclusively,
+ is the *block*. (A block that hasn't been successfully processed by Argument
+ Clinic yet doesn't have output or a checksum line, but it's still considered
+ a block.)
+
+
+Converting Your First Function
+==============================
+
+The best way to get a sense of how Argument Clinic works is to
+convert a function to work with it. Here, then, are the bare
+minimum steps you'd need to follow to convert a function to
+work with Argument Clinic. Note that for code you plan to
+check in to CPython, you really should take the conversion farther,
+using some of the advanced concepts you'll see later on in
+the document (like "return converters" and "self converters").
+But we'll keep it simple for this walkthrough so you can learn.
+
+Let's dive in!
+
+0. Make sure you're working with a freshly updated checkout
+ of the CPython trunk.
+
+1. Find a Python builtin that calls either :c:func:`PyArg_ParseTuple`
+ or :c:func:`PyArg_ParseTupleAndKeywords`, and hasn't been converted
+ to work with Argument Clinic yet.
+ For my example I'm using ``_pickle.Pickler.dump()``.
+
+2. If the call to the ``PyArg_Parse`` function uses any of the
+ following format units::
+
+ O&
+ O!
+ es
+ es#
+ et
+ et#
+
+ or if it has multiple calls to :c:func:`PyArg_ParseTuple`,
+ you should choose a different function. Argument Clinic *does*
+ support all of these scenarios. But these are advanced
+ topics--let's do something simpler for your first function.
+
+ Also, if the function has multiple calls to :c:func:`PyArg_ParseTuple`
+ or :c:func:`PyArg_ParseTupleAndKeywords` where it supports different
+ types for the same argument, or if the function uses something besides
+ PyArg_Parse functions to parse its arguments, it probably
+ isn't suitable for conversion to Argument Clinic. Argument Clinic
+ doesn't support generic functions or polymorphic parameters.
+
+3. Add the following boilerplate above the function, creating our block::
+
+ /*[clinic input]
+ [clinic start generated code]*/
+
+4. Cut the docstring and paste it in between the ``[clinic]`` lines,
+ removing all the junk that makes it a properly quoted C string.
+ When you're done you should have just the text, based at the left
+ margin, with no line wider than 80 characters.
+ (Argument Clinic will preserve indents inside the docstring.)
+
+ If the old docstring had a first line that looked like a function
+ signature, throw that line away. (The docstring doesn't need it
+ anymore--when you use ``help()`` on your builtin in the future,
+ the first line will be built automatically based on the function's
+ signature.)
+
+ Sample::
+
+ /*[clinic input]
+ Write a pickled representation of obj to the open file.
+ [clinic start generated code]*/
+
+5. If your docstring doesn't have a "summary" line, Argument Clinic will
+ complain. So let's make sure it has one. The "summary" line should
+ be a paragraph consisting of a single 80-column line
+ at the beginning of the docstring.
+
+ (Our example docstring consists solely of a summary line, so the sample
+ code doesn't have to change for this step.)
+
+6. Above the docstring, enter the name of the function, followed
+ by a blank line. This should be the Python name of the function,
+ and should be the full dotted path
+ to the function--it should start with the name of the module,
+ include any sub-modules, and if the function is a method on
+ a class it should include the class name too.
+
+ Sample::
+
+ /*[clinic input]
+ _pickle.Pickler.dump
+
+ Write a pickled representation of obj to the open file.
+ [clinic start generated code]*/
+
+7. If this is the first time that module or class has been used with Argument
+ Clinic in this C file,
+ you must declare the module and/or class. Proper Argument Clinic hygiene
+ prefers declaring these in a separate block somewhere near the
+ top of the C file, in the same way that include files and statics go at
+ the top. (In our sample code we'll just show the two blocks next to
+ each other.)
+
+ The name of the class and module should be the same as the one
+ seen by Python. Check the name defined in the :c:type:`PyModuleDef`
+ or :c:type:`PyTypeObject` as appropriate.
+
+ When you declare a class, you must also specify two aspects of its type
+ in C: the type declaration you'd use for a pointer to an instance of
+ this class, and a pointer to the :c:type:`PyTypeObject` for this class.
+
+ Sample::
+
+ /*[clinic input]
+ module _pickle
+ class _pickle.Pickler "PicklerObject *" "&Pickler_Type"
+ [clinic start generated code]*/
+
+ /*[clinic input]
+ _pickle.Pickler.dump
+
+ Write a pickled representation of obj to the open file.
+ [clinic start generated code]*/
+
+
+
+
+8. Declare each of the parameters to the function. Each parameter
+ should get its own line. All the parameter lines should be
+ indented from the function name and the docstring.
+
+ The general form of these parameter lines is as follows::
+
+ name_of_parameter: converter
+
+ If the parameter has a default value, add that after the
+ converter::
+
+ name_of_parameter: converter = default_value
+
+ Argument Clinic's support for "default values" is quite sophisticated;
+ please see :ref:`the section below on default values <default_values>`
+ for more information.
+
+ Add a blank line below the parameters.
+
+ What's a "converter"? It establishes both the type
+ of the variable used in C, and the method to convert the Python
+ value into a C value at runtime.
+ For now you're going to use what's called a "legacy converter"--a
+ convenience syntax intended to make porting old code into Argument
+ Clinic easier.
+
+ For each parameter, copy the "format unit" for that
+ parameter from the ``PyArg_Parse()`` format argument and
+ specify *that* as its converter, as a quoted
+ string. ("format unit" is the formal name for the one-to-three
+ character substring of the ``format`` parameter that tells
+ the argument parsing function what the type of the variable
+ is and how to convert it. For more on format units please
+ see :ref:`arg-parsing`.)
+
+ For multicharacter format units like ``z#``, use the
+ entire two-or-three character string.
+
+ Sample::
+
+ /*[clinic input]
+ module _pickle
+ class _pickle.Pickler "PicklerObject *" "&Pickler_Type"
+ [clinic start generated code]*/
+
+ /*[clinic input]
+ _pickle.Pickler.dump
+
+ obj: 'O'
+
+ Write a pickled representation of obj to the open file.
+ [clinic start generated code]*/
+
+9. If your function has ``|`` in the format string, meaning some
+ parameters have default values, you can ignore it. Argument
+ Clinic infers which parameters are optional based on whether
+ or not they have default values.
+
+ If your function has ``$`` in the format string, meaning it
+ takes keyword-only arguments, specify ``*`` on a line by
+ itself before the first keyword-only argument, indented the
+ same as the parameter lines.
+
+ (``_pickle.Pickler.dump`` has neither, so our sample is unchanged.)
+
+
+10. If the existing C function calls :c:func:`PyArg_ParseTuple`
+ (as opposed to :c:func:`PyArg_ParseTupleAndKeywords`), then all its
+ arguments are positional-only.
+
+ To mark all parameters as positional-only in Argument Clinic,
+ add a ``/`` on a line by itself after the last parameter,
+ indented the same as the parameter lines.
+
+ Currently this is all-or-nothing; either all parameters are
+ positional-only, or none of them are. (In the future Argument
+ Clinic may relax this restriction.)
+
+ Sample::
+
+ /*[clinic input]
+ module _pickle
+ class _pickle.Pickler "PicklerObject *" "&Pickler_Type"
+ [clinic start generated code]*/
+
+ /*[clinic input]
+ _pickle.Pickler.dump
+
+ obj: 'O'
+ /
+
+ Write a pickled representation of obj to the open file.
+ [clinic start generated code]*/
+
+11. It's helpful to write a per-parameter docstring for each parameter.
+ But per-parameter docstrings are optional; you can skip this step
+ if you prefer.
+
+ Here's how to add a per-parameter docstring. The first line
+ of the per-parameter docstring must be indented further than the
+ parameter definition. The left margin of this first line establishes
+ the left margin for the whole per-parameter docstring; all the text
+ you write will be outdented by this amount. You can write as much
+ text as you like, across multiple lines if you wish.
+
+ Sample::
+
+ /*[clinic input]
+ module _pickle
+ class _pickle.Pickler "PicklerObject *" "&Pickler_Type"
+ [clinic start generated code]*/
+
+ /*[clinic input]
+ _pickle.Pickler.dump
+
+ obj: 'O'
+ The object to be pickled.
+ /
+
+ Write a pickled representation of obj to the open file.
+ [clinic start generated code]*/
+
+12. Save and close the file, then run ``Tools/clinic/clinic.py`` on it.
+ With luck everything worked and your block now has output! Reopen
+ the file in your text editor to see::
+
+ /*[clinic input]
+ module _pickle
+ class _pickle.Pickler "PicklerObject *" "&Pickler_Type"
+ [clinic start generated code]*/
+ /*[clinic end generated code: checksum=da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
+
+ /*[clinic input]
+ _pickle.Pickler.dump
+
+ obj: 'O'
+ The object to be pickled.
+ /
+
+ Write a pickled representation of obj to the open file.
+ [clinic start generated code]*/
+
+ PyDoc_STRVAR(_pickle_Pickler_dump__doc__,
+ "Write a pickled representation of obj to the open file.\n"
+ "\n"
+ ...
+ static PyObject *
+ _pickle_Pickler_dump_impl(PicklerObject *self, PyObject *obj)
+ /*[clinic end generated code: checksum=3bd30745bf206a48f8b576a1da3d90f55a0a4187]*/
+
+ Obviously, if Argument Clinic didn't produce any output, it's because
+ it found an error in your input. Keep fixing your errors and retrying
+ until Argument Clinic processes your file without complaint.
+
+13. Double-check that the argument-parsing code Argument Clinic generated
+ looks basically the same as the existing code.
+
+ First, ensure both places use the same argument-parsing function.
+ The existing code must call either
+ :c:func:`PyArg_ParseTuple` or :c:func:`PyArg_ParseTupleAndKeywords`;
+ ensure that the code generated by Argument Clinic calls the
+ *exact* same function.
+
+ Second, the format string passed in to :c:func:`PyArg_ParseTuple` or
+ :c:func:`PyArg_ParseTupleAndKeywords` should be *exactly* the same
+ as the hand-written one in the existing function, up to the colon
+ or semi-colon.
+
+ (Argument Clinic always generates its format strings
+ with a ``:`` followed by the name of the function. If the
+ existing code's format string ends with ``;``, to provide
+ usage help, this change is harmless--don't worry about it.)
+
+ Third, for parameters whose format units require two arguments
+ (like a length variable, or an encoding string, or a pointer
+ to a conversion function), ensure that the second argument is
+ *exactly* the same between the two invocations.
+
+ Fourth, inside the output portion of the block you'll find a preprocessor
+ macro defining the appropriate static :c:type:`PyMethodDef` structure for
+ this builtin::
+
+ #define __PICKLE_PICKLER_DUMP_METHODDEF \
+ {"dump", (PyCFunction)__pickle_Pickler_dump, METH_O, __pickle_Pickler_dump__doc__},
+
+ This static structure should be *exactly* the same as the existing static
+ :c:type:`PyMethodDef` structure for this builtin.
+
+ If any of these items differ in *any way*,
+ adjust your Argument Clinic function specification and rerun
+ ``Tools/clinic/clinic.py`` until they *are* the same.
+
+
+14. Notice that the last line of its output is the declaration
+ of your "impl" function. This is where the builtin's implementation goes.
+ Delete the existing prototype of the function you're modifying, but leave
+ the opening curly brace. Now delete its argument parsing code and the
+ declarations of all the variables it dumps the arguments into.
+ Notice how the Python arguments are now arguments to this impl function;
+ if the implementation used different names for these variables, fix it.
+
+ Let's reiterate, just because it's kind of weird. Your code should now
+ look like this::
+
+ static return_type
+ your_function_impl(...)
+ /*[clinic end generated code: checksum=...]*/
+ {
+ ...
+
+ Argument Clinic generated the checksum line and the function prototype just
+ above it. You should write the opening (and closing) curly braces for the
+ function, and the implementation inside.
+
+ Sample::
+
+ /*[clinic input]
+ module _pickle
+ class _pickle.Pickler "PicklerObject *" "&Pickler_Type"
+ [clinic start generated code]*/
+ /*[clinic end generated code: checksum=da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
+
+ /*[clinic input]
+ _pickle.Pickler.dump
+
+ obj: 'O'
+ The object to be pickled.
+ /
+
+ Write a pickled representation of obj to the open file.
+ [clinic start generated code]*/
+
+ PyDoc_STRVAR(__pickle_Pickler_dump__doc__,
+ "Write a pickled representation of obj to the open file.\n"
+ "\n"
+ ...
+ static PyObject *
+ _pickle_Pickler_dump_impl(PicklerObject *self, PyObject *obj)
+ /*[clinic end generated code: checksum=3bd30745bf206a48f8b576a1da3d90f55a0a4187]*/
+ {
+ /* Check whether the Pickler was initialized correctly (issue3664).
+ Developers often forget to call __init__() in their subclasses, which
+ would trigger a segfault without this check. */
+ if (self->write == NULL) {
+ PyErr_Format(PicklingError,
+ "Pickler.__init__() was not called by %s.__init__()",
+ Py_TYPE(self)->tp_name);
+ return NULL;
+ }
+
+ if (_Pickler_ClearBuffer(self) < 0)
+ return NULL;
+
+ ...
+
+15. Remember the macro with the :c:type:`PyMethodDef` structure for this
+ function? Find the existing :c:type:`PyMethodDef` structure for this
+ function and replace it with a reference to the macro. (If the builtin
+ is at module scope, this will probably be very near the end of the file;
+ if the builtin is a class method, this will probably be below but relatively
+ near to the implementation.)
+
+ Note that the body of the macro contains a trailing comma. So when you
+ replace the existing static :c:type:`PyMethodDef` structure with the macro,
+ *don't* add a comma to the end.
+
+ Sample::
+
+ static struct PyMethodDef Pickler_methods[] = {
+ __PICKLE_PICKLER_DUMP_METHODDEF
+ __PICKLE_PICKLER_CLEAR_MEMO_METHODDEF
+ {NULL, NULL} /* sentinel */
+ };
+
+
+16. Compile, then run the relevant portions of the regression-test suite.
+ This change should not introduce any new compile-time warnings or errors,
+ and there should be no externally-visible change to Python's behavior.
+
+ Well, except for one difference: ``inspect.signature()`` run on your function
+ should now provide a valid signature!
+
+ Congratulations, you've ported your first function to work with Argument Clinic!
+
+Advanced Topics
+===============
+
+Now that you've had some experience working with Argument Clinic, it's time
+for some advanced topics.
+
+
+Symbolic default values
+-----------------------
+
+The default value you provide for a parameter can't be any arbitrary
+expression. Currently the following are explicitly supported:
+
+* Numeric constants (integer and float)
+* String constants
+* ``True``, ``False``, and ``None``
+* Simple symbolic constants like ``sys.maxsize``, which must
+ start with the name of the module
+
+In case you're curious, this is implemented in ``from_builtin()``
+in ``Lib/inspect.py``.
+
+(In the future, this may need to get even more elaborate,
+to allow full expressions like ``CONSTANT - 1``.)
+
+
+Renaming the C functions and variables generated by Argument Clinic
+-------------------------------------------------------------------
+
+Argument Clinic automatically names the functions it generates for you.
+Occasionally this may cause a problem, if the generated name collides with
+the name of an existing C function. There's an easy solution: override the names
+used for the C functions. Just add the keyword ``"as"``
+to your function declaration line, followed by the function name you wish to use.
+Argument Clinic will use that function name for the base (generated) function,
+then add ``"_impl"`` to the end and use that for the name of the impl function.
+
+For example, if we wanted to rename the C function names generated for
+``pickle.Pickler.dump``, it'd look like this::
+
+ /*[clinic input]
+ pickle.Pickler.dump as pickler_dumper
+
+ ...
+
+The base function would now be named ``pickler_dumper()``,
+and the impl function would now be named ``pickler_dumper_impl()``.
+
+
+Similarly, you may have a problem where you want to give a parameter
+a specific Python name, but that name may be inconvenient in C. Argument
+Clinic allows you to give a parameter different names in Python and in C,
+using the same ``"as"`` syntax::
+
+ /*[clinic input]
+ pickle.Pickler.dump
+
+ obj: object
+ file as file_obj: object
+ protocol: object = NULL
+ *
+ fix_imports: bool = True
+
+Here, the name used in Python (in the signature and the ``keywords``
+array) would be ``file``, but the C variable would be named ``file_obj``.
+
+You can use this to rename the ``self`` parameter too!
+
+
+Converting functions using PyArg_UnpackTuple
+--------------------------------------------
+
+To convert a function parsing its arguments with :c:func:`PyArg_UnpackTuple`,
+simply write out all the arguments, specifying each as an ``object``. You
+may specify the ``type`` argument to cast the type as appropriate. All
+arguments should be marked positional-only (add a ``/`` on a line by itself
+after the last argument).
+
+Currently the generated code will use :c:func:`PyArg_ParseTuple`, but this
+will change soon.
+
+Optional Groups
+---------------
+
+Some legacy functions have a tricky approach to parsing their arguments:
+they count the number of positional arguments, then use a ``switch`` statement
+to call one of several different :c:func:`PyArg_ParseTuple` calls depending on
+how many positional arguments there are. (These functions cannot accept
+keyword-only arguments.) This approach was used to simulate optional
+arguments back before :c:func:`PyArg_ParseTupleAndKeywords` was created.
+
+While functions using this approach can often be converted to
+use :c:func:`PyArg_ParseTupleAndKeywords`, optional arguments, and default values,
+it's not always possible. Some of these legacy functions have
+behaviors :c:func:`PyArg_ParseTupleAndKeywords` doesn't directly support.
+The most obvious example is the builtin function ``range()``, which has
+an optional argument on the *left* side of its required argument!
+Another example is ``curses.window.addch()``, which has a group of two
+arguments that must always be specified together. (The arguments are
+called ``x`` and ``y``; if you call the function passing in ``x``,
+you must also pass in ``y``--and if you don't pass in ``x`` you may not
+pass in ``y`` either.)
+
+In any case, the goal of Argument Clinic is to support argument parsing
+for all existing CPython builtins without changing their semantics.
+Therefore Argument Clinic supports
+this alternate approach to parsing, using what are called *optional groups*.
+Optional groups are groups of arguments that must all be passed in together.
+They can be to the left or the right of the required arguments. They
+can *only* be used with positional-only parameters.
+
+.. note:: Optional groups are *only* intended for use when converting
+ functions that make multiple calls to :c:func:`PyArg_ParseTuple`!
+ Functions that use *any* other approach for parsing arguments
+ should *almost never* be converted to Argument Clinic using
+ optional groups. Functions using optional groups currently
+ cannot have accurate sigantures in Python, because Python just
+ doesn't understand the concept. Please avoid using optional
+ groups wherever possible.
+
+To specify an optional group, add a ``[`` on a line by itself before
+the parameters you wish to group together, and a ``]`` on a line by itself
+after these parameters. As an example, here's how ``curses.window.addch``
+uses optional groups to make the first two parameters and the last
+parameter optional::
+
+ /*[clinic input]
+
+ curses.window.addch
+
+ [
+ x: int
+ X-coordinate.
+ y: int
+ Y-coordinate.
+ ]
+
+ ch: object
+ Character to add.
+
+ [
+ attr: long
+ Attributes for the character.
+ ]
+ /
+
+ ...
+
+
+Notes:
+
+* For every optional group, one additional parameter will be passed into the
+ impl function representing the group. The parameter will be an int named
+ ``group_{direction}_{number}``,
+ where ``{direction}`` is either ``right`` or ``left`` depending on whether the group
+ is before or after the required parameters, and ``{number}`` is a monotonically
+ increasing number (starting at 1) indicating how far away the group is from
+ the required parameters. When the impl is called, this parameter will be set
+ to zero if this group was unused, and set to non-zero if this group was used.
+ (By used or unused, I mean whether or not the parameters received arguments
+ in this invocation.)
+
+* If there are no required arguments, the optional groups will behave
+ as if they're to the right of the required arguments.
+
+* In the case of ambiguity, the argument parsing code
+ favors parameters on the left (before the required parameters).
+
+* Optional groups can only contain positional-only parameters.
+
+* Optional groups are *only* intended for legacy code. Please do not
+ use optional groups for new code.
+
+
+Using real Argument Clinic converters, instead of "legacy converters"
+---------------------------------------------------------------------
+
+To save time, and to minimize how much you need to learn
+to achieve your first port to Argument Clinic, the walkthrough above tells
+you to use "legacy converters". "Legacy converters" are a convenience,
+designed explicitly to make porting existing code to Argument Clinic
+easier. And to be clear, their use is acceptable when porting code for
+Python 3.4.
+
+However, in the long term we probably want all our blocks to
+use Argument Clinic's real syntax for converters. Why? A couple
+reasons:
+
+* The proper converters are far easier to read and clearer in their intent.
+* There are some format units that are unsupported as "legacy converters",
+ because they require arguments, and the legacy converter syntax doesn't
+ support specifying arguments.
+* In the future we may have a new argument parsing library that isn't
+ restricted to what :c:func:`PyArg_ParseTuple` supports; this flexibility
+ won't be available to parameters using legacy converters.
+
+Therefore, if you don't mind a little extra effort, please use the normal
+converters instead of legacy converters.
+
+In a nutshell, the syntax for Argument Clinic (non-legacy) converters
+looks like a Python function call. However, if there are no explicit
+arguments to the function (all functions take their default values),
+you may omit the parentheses. Thus ``bool`` and ``bool()`` are exactly
+the same converters.
+
+All arguments to Argument Clinic converters are keyword-only.
+All Argument Clinic converters accept the following arguments:
+
+ ``c_default``
+ The default value for this parameter when defined in C.
+ Specifically, this will be the initializer for the variable declared
+ in the "parse function". See :ref:`the section on default values <default_values>`
+ for how to use this.
+ Specified as a string.
+
+ ``annotation``
+ The annotation value for this parameter. Not currently supported,
+ because PEP 8 mandates that the Python library may not use
+ annotations.
+
+In addition, some converters accept additional arguments. Here is a list
+of these arguments, along with their meanings:
+
+ ``bitwise``
+ Only supported for unsigned integers. The native integer value of this
+ Python argument will be written to the parameter without any range checking,
+ even for negative values.
+
+ ``converter``
+ Only supported by the ``object`` converter. Specifies the name of a
+ :ref:`C "converter function" <o_ampersand>`
+ to use to convert this object to a native type.
+
+ ``encoding``
+ Only supported for strings. Specifies the encoding to use when converting
+ this string from a Python str (Unicode) value into a C ``char *`` value.
+
+ ``length``
+ Only supported for strings. If true, requests that the length of the
+ string be passed in to the impl function, just after the string parameter,
+ in a parameter named ``<parameter_name>_length``.
+
+ ``nullable``
+ Only supported for strings. If true, this parameter may also be set to
+ ``None``, in which case the C parameter will be set to ``NULL``.
+
+ ``subclass_of``
+ Only supported for the ``object`` converter. Requires that the Python
+ value be a subclass of a Python type, as expressed in C.
+
+ ``types``
+ Only supported for the ``object`` (and ``self``) converter. Specifies
+ the C type that will be used to declare the variable. Default value is
+ ``"PyObject *"``.
+
+ ``types``
+ A string containing a list of Python types (and possibly pseudo-types);
+ this restricts the allowable Python argument to values of these types.
+ (This is not a general-purpose facility; as a rule it only supports
+ specific lists of types as shown in the legacy converter table.)
+
+ ``zeroes``
+ Only supported for strings. If true, embedded NUL bytes (``'\\0'``) are
+ permitted inside the value.
+
+Please note, not every possible combination of arguments will work.
+Often these arguments are implemented internally by specific ``PyArg_ParseTuple``
+*format units*, with specific behavior. For example, currently you cannot
+call ``str`` and pass in ``zeroes=True`` without also specifying an ``encoding``;
+although it's perfectly reasonable to think this would work, these semantics don't
+map to any existing format unit. So Argument Clinic doesn't support it. (Or, at
+least, not yet.)
+
+Below is a table showing the mapping of legacy converters into real
+Argument Clinic converters. On the left is the legacy converter,
+on the right is the text you'd replace it with.
+
+========= =================================================================================
+``'B'`` ``unsigned_char(bitwise=True)``
+``'b'`` ``unsigned_char``
+``'c'`` ``char``
+``'C'`` ``int(types='str')``
+``'d'`` ``double``
+``'D'`` ``Py_complex``
+``'es#'`` ``str(encoding='name_of_encoding', length=True, zeroes=True)``
+``'es'`` ``str(encoding='name_of_encoding')``
+``'et#'`` ``str(encoding='name_of_encoding', types='bytes bytearray str', length=True)``
+``'et'`` ``str(encoding='name_of_encoding', types='bytes bytearray str')``
+``'f'`` ``float``
+``'h'`` ``short``
+``'H'`` ``unsigned_short(bitwise=True)``
+``'i'`` ``int``
+``'I'`` ``unsigned_int(bitwise=True)``
+``'k'`` ``unsigned_long(bitwise=True)``
+``'K'`` ``unsigned_PY_LONG_LONG(bitwise=True)``
+``'L'`` ``PY_LONG_LONG``
+``'n'`` ``Py_ssize_t``
+``'O!'`` ``object(subclass_of='&PySomething_Type')``
+``'O&'`` ``object(converter='name_of_c_function')``
+``'O'`` ``object``
+``'p'`` ``bool``
+``'s#'`` ``str(length=True)``
+``'S'`` ``PyBytesObject``
+``'s'`` ``str``
+``'s*'`` ``Py_buffer(types='str bytes bytearray buffer')``
+``'u#'`` ``Py_UNICODE(length=True)``
+``'u'`` ``Py_UNICODE``
+``'U'`` ``unicode``
+``'w*'`` ``Py_buffer(types='bytearray rwbuffer')``
+``'y#'`` ``str(types='bytes', length=True)``
+``'Y'`` ``PyByteArrayObject``
+``'y'`` ``str(types='bytes')``
+``'y*'`` ``Py_buffer``
+``'Z#'`` ``Py_UNICODE(nullable=True, length=True)``
+``'z#'`` ``str(nullable=True, length=True)``
+``'Z'`` ``Py_UNICODE(nullable=True)``
+``'z'`` ``str(nullable=True)``
+``'z*'`` ``Py_buffer(types='str bytes bytearray buffer', nullable=True)``
+========= =================================================================================
+
+As an example, here's our sample ``pickle.Pickler.dump`` using the proper
+converter::
+
+ /*[clinic input]
+ pickle.Pickler.dump
+
+ obj: object
+ The object to be pickled.
+ /
+
+ Write a pickled representation of obj to the open file.
+ [clinic start generated code]*/
+
+Argument Clinic will show you all the converters it has
+available. For each converter it'll show you all the parameters
+it accepts, along with the default value for each parameter.
+Just run ``Tools/clinic/clinic.py --converters`` to see the full list.
+
+Py_buffer
+---------
+
+When using the ``Py_buffer`` converter
+(or the ``'s*'``, ``'w*'``, ``'*y'``, or ``'z*'`` legacy converters),
+you *must* not call :c:func:`PyBuffer_Release` on the provided buffer.
+Argument Clinic generates code that does it for you (in the parsing function).
+
+
+
+Advanced converters
+-------------------
+
+Remeber those format units you skipped for your first
+time because they were advanced? Here's how to handle those too.
+
+The trick is, all those format units take arguments--either
+conversion functions, or types, or strings specifying an encoding.
+(But "legacy converters" don't support arguments. That's why we
+skipped them for your first function.) The argument you specified
+to the format unit is now an argument to the converter; this
+argument is either ``converter`` (for ``O&``), ``subclass_of`` (for ``O!``),
+or ``encoding`` (for all the format units that start with ``e``).
+
+When using ``subclass_of``, you may also want to use the other
+custom argument for ``object()``: ``type``, which lets you set the type
+actually used for the parameter. For example, if you want to ensure
+that the object is a subclass of ``PyUnicode_Type``, you probably want
+to use the converter ``object(type='PyUnicodeObject *', subclass_of='&PyUnicode_Type')``.
+
+One possible problem with using Argument Clinic: it takes away some possible
+flexibility for the format units starting with ``e``. When writing a
+``PyArg_Parse`` call by hand, you could theoretically decide at runtime what
+encoding string to pass in to :c:func:`PyArg_ParseTuple`. But now this string must
+be hard-coded at Argument-Clinic-preprocessing-time. This limitation is deliberate;
+it made supporting this format unit much easier, and may allow for future optimizations.
+This restriction doesn't seem unreasonable; CPython itself always passes in static
+hard-coded encoding strings for parameters whose format units start with ``e``.
+
+
+.. _default_values:
+
+Parameter default values
+------------------------
+
+Default values for parameters can be any of a number of values.
+At their simplest, they can be string, int, or float literals::
+
+ foo: str = "abc"
+ bar: int = 123
+ bat: float = 45.6
+
+They can also use any of Python's built-in constants::
+
+ yep: bool = True
+ nope: bool = False
+ nada: object = None
+
+There's also special support for a default value of ``NULL``, and
+for simple expressions, documented in the following sections.
+
+
+The ``NULL`` default value
+--------------------------
+
+For string and object parameters, you can set them to ``None`` to indicate
+that there's no default. However, that means the C variable will be
+initialized to ``Py_None``. For convenience's sakes, there's a special
+value called ``NULL`` for just this reason: from Python's perspective it
+behaves like a default value of ``None``, but the C variable is initialized
+with ``NULL``.
+
+Expressions specified as default values
+---------------------------------------
+
+The default value for a parameter can be more than just a literal value.
+It can be an entire expression, using math operators and looking up attributes
+on objects. However, this support isn't exactly simple, because of some
+non-obvious semantics.
+
+Consider the following example::
+
+ foo: Py_ssize_t = sys.maxsize - 1
+
+``sys.maxsize`` can have different values on different platforms. Therefore
+Argument Clinic can't simply evaluate that expression locally and hard-code it
+in C. So it stores the default in such a way that it will get evaluated at
+runtime, when the user asks for the function's signature.
+
+What namespace is available when the expression is evaluated? It's evaluated
+in the context of the module the builtin came from. So, if your module has an
+attribute called "``max_widgets``", you may simply use it::
+
+ foo: Py_ssize_t = max_widgets
+
+If the symbol isn't found in the current module, it fails over to looking in
+``sys.modules``. That's how it can find ``sys.maxsize`` for example. (Since you
+don't know in advance what modules the user will load into their interpreter,
+it's best to restrict yourself to modules that are preloaded by Python itself.)
+
+Evaluating default values only at runtime means Argument Clinic can't compute
+the correct equivalent C default value. So you need to tell it explicitly.
+When you use an expression, you must also specify the equivalent expression
+in C, using the ``c_default`` parameter to the converter::
+
+ foo: Py_ssize_t(c_default="PY_SSIZE_T_MAX - 1") = sys.maxsize - 1
+
+Another complication: Argument Clinic can't know in advance whether or not the
+expression you supply is valid. It parses it to make sure it looks legal, but
+it can't *actually* know. You must be very careful when using expressions to
+specify values that are guaranteed to be valid at runtime!
+
+Finally, because expressions must be representable as static C values, there
+are many restrictions on legal expressions. Here's a list of Python features
+you're not permitted to use:
+
+* Function calls.
+* Inline if statements (``3 if foo else 5``).
+* Automatic sequence unpacking (``*[1, 2, 3]``).
+* List/set/dict comprehensions and generator expressions.
+* Tuple/list/set/dict literals.
+
+
+
+Using a return converter
+------------------------
+
+By default the impl function Argument Clinic generates for you returns ``PyObject *``.
+But your C function often computes some C type, then converts it into the ``PyObject *``
+at the last moment. Argument Clinic handles converting your inputs from Python types
+into native C types--why not have it convert your return value from a native C type
+into a Python type too?
+
+That's what a "return converter" does. It changes your impl function to return
+some C type, then adds code to the generated (non-impl) function to handle converting
+that value into the appropriate ``PyObject *``.
+
+The syntax for return converters is similar to that of parameter converters.
+You specify the return converter like it was a return annotation on the
+function itself. Return converters behave much the same as parameter converters;
+they take arguments, the arguments are all keyword-only, and if you're not changing
+any of the default arguments you can omit the parentheses.
+
+(If you use both ``"as"`` *and* a return converter for your function,
+the ``"as"`` should come before the return converter.)
+
+There's one additional complication when using return converters: how do you
+indicate an error has occured? Normally, a function returns a valid (non-``NULL``)
+pointer for success, and ``NULL`` for failure. But if you use an integer return converter,
+all integers are valid. How can Argument Clinic detect an error? Its solution: each return
+converter implicitly looks for a special value that indicates an error. If you return
+that value, and an error has been set (``PyErr_Occurred()`` returns a true
+value), then the generated code will propogate the error. Otherwise it will
+encode the value you return like normal.
+
+Currently Argument Clinic supports only a few return converters::
+
+ bool
+ int
+ unsigned int
+ long
+ unsigned int
+ size_t
+ Py_ssize_t
+ float
+ double
+ DecodeFSDefault
+
+None of these take parameters. For the first three, return -1 to indicate
+error. For ``DecodeFSDefault``, the return type is ``char *``; return a NULL
+pointer to indicate an error.
+
+(There's also an experimental ``NoneType`` converter, which lets you
+return ``Py_None`` on success or ``NULL`` on failure, without having
+to increment the reference count on ``Py_None``. I'm not sure it adds
+enough clarity to be worth using.)
+
+To see all the return converters Argument Clinic supports, along with
+their parameters (if any),
+just run ``Tools/clinic/clinic.py --converters`` for the full list.
+
+
+Cloning existing functions
+--------------------------
+
+If you have a number of functions that look similar, you may be able to
+use Clinic's "clone" feature. When you clone an existing function,
+you reuse:
+
+* its parameters, including
+
+ * their names,
+
+ * their converters, with all parameters,
+
+ * their default values,
+
+ * their per-parameter docstrings,
+
+ * their *kind* (whether they're positional only,
+ positional or keyword, or keyword only), and
+
+* its return converter.
+
+The only thing not copied from the original function is its docstring;
+the syntax allows you to specify a new docstring.
+
+Here's the syntax for cloning a function::
+
+ /*[clinic input]
+ module.class.new_function [as c_basename] = module.class.existing_function
+
+ Docstring for new_function goes here.
+ [clinic start generated code]*/
+
+(The functions can be in different modules or classes. I wrote
+``module.class`` in the sample just to illustrate that you must
+use the full path to *both* functions.)
+
+Sorry, there's no syntax for partially-cloning a function, or cloning a function
+then modifying it. Cloning is an all-or nothing proposition.
+
+Also, the function you are cloning from must have been previously defined
+in the current file.
+
+Calling Python code
+-------------------
+
+The rest of the advanced topics require you to write Python code
+which lives inside your C file and modifies Argument Clinic's
+runtime state. This is simple: you simply define a Python block.
+
+A Python block uses different delimiter lines than an Argument
+Clinic function block. It looks like this::
+
+ /*[python input]
+ # python code goes here
+ [python start generated code]*/
+
+All the code inside the Python block is executed at the
+time it's parsed. All text written to stdout inside the block
+is redirected into the "output" after the block.
+
+As an example, here's a Python block that adds a static integer
+variable to the C code::
+
+ /*[python input]
+ print('static int __ignored_unused_variable__ = 0;')
+ [python start generated code]*/
+ static int __ignored_unused_variable__ = 0;
+ /*[python checksum:...]*/
+
+
+Using a "self converter"
+------------------------
+
+Argument Clinic automatically adds a "self" parameter for you
+using a default converter. It automatically sets the ``type``
+of this parameter to the "pointer to an instance" you specified
+when you declared the type. However, you can override
+Argument Clinic's converter and specify one yourself.
+Just add your own ``self`` parameter as the first parameter in a
+block, and ensure that its converter is an instance of
+``self_converter`` or a subclass thereof.
+
+What's the point? This lets you override the type of ``self``,
+or give it a different default name.
+
+How do you specify the custom type you want to cast ``self`` to?
+If you only have one or two functions with the same type for ``self``,
+you can directly use Argument Clinic's existing ``self`` converter,
+passing in the type you want to use as the ``type`` parameter::
+
+ /*[clinic input]
+
+ _pickle.Pickler.dump
+
+ self: self(type="PicklerObject *")
+ obj: object
+ /
+
+ Write a pickled representation of the given object to the open file.
+ [clinic start generated code]*/
+
+On the other hand, if you have a lot of functions that will use the same
+type for ``self``, it's best to create your own converter, subclassing
+``self_converter`` but overwriting the ``type`` member::
+
+ /*[python input]
+ class PicklerObject_converter(self_converter):
+ type = "PicklerObject *"
+ [python start generated code]*/
+
+ /*[clinic input]
+
+ _pickle.Pickler.dump
+
+ self: PicklerObject
+ obj: object
+ /
+
+ Write a pickled representation of the given object to the open file.
+ [clinic start generated code]*/
+
+
+
+Writing a custom converter
+--------------------------
+
+As we hinted at in the previous section... you can write your own converters!
+A converter is simply a Python class that inherits from ``CConverter``.
+The main purpose of a custom converter is if you have a parameter using
+the ``O&`` format unit--parsing this parameter means calling
+a :c:func:`PyArg_ParseTuple` "converter function".
+
+Your converter class should be named ``*something*_converter``.
+If the name follows this convention, then your converter class
+will be automatically registered with Argument Clinic; its name
+will be the name of your class with the ``_converter`` suffix
+stripped off. (This is accomplished with a metaclass.)
+
+You shouldn't subclass ``CConverter.__init__``. Instead, you should
+write a ``converter_init()`` function. ``converter_init()``
+always accepts a ``self`` parameter; after that, all additional
+parameters *must* be keyword-only. Any arguments passed in to
+the converter in Argument Clinic will be passed along to your
+``converter_init()``.
+
+There are some additional members of ``CConverter`` you may wish
+to specify in your subclass. Here's the current list:
+
+``type``
+ The C type to use for this variable.
+ ``type`` should be a Python string specifying the type, e.g. ``int``.
+ If this is a pointer type, the type string should end with ``' *'``.
+
+``default``
+ The Python default value for this parameter, as a Python value.
+ Or the magic value ``unspecified`` if there is no default.
+
+``py_default``
+ ``default`` as it should appear in Python code,
+ as a string.
+ Or ``None`` if there is no default.
+
+``c_default``
+ ``default`` as it should appear in C code,
+ as a string.
+ Or ``None`` if there is no default.
+
+``c_ignored_default``
+ The default value used to initialize the C variable when
+ there is no default, but not specifying a default may
+ result in an "uninitialized variable" warning. This can
+ easily happen when using option groups--although
+ properly-written code will never actually use this value,
+ the variable does get passed in to the impl, and the
+ C compiler will complain about the "use" of the
+ uninitialized value. This value should always be a
+ non-empty string.
+
+``converter``
+ The name of the C converter function, as a string.
+
+``impl_by_reference``
+ A boolean value. If true,
+ Argument Clinic will add a ``&`` in front of the name of
+ the variable when passing it into the impl function.
+
+``parse_by_reference``
+ A boolean value. If true,
+ Argument Clinic will add a ``&`` in front of the name of
+ the variable when passing it into :c:func:`PyArg_ParseTuple`.
+
+
+Here's the simplest example of a custom converter, from ``Modules/zlibmodule.c``::
+
+ /*[python input]
+
+ class uint_converter(CConverter):
+ type = 'unsigned int'
+ converter = 'uint_converter'
+
+ [python start generated code]*/
+ /*[python end generated code: checksum=da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
+
+This block adds a converter to Argument Clinic named ``uint``. Parameters
+declared as ``uint`` will be declared as type ``unsigned int``, and will
+be parsed by the ``'O&'`` format unit, which will call the ``uint_converter``
+converter function.
+``uint`` variables automatically support default values.
+
+More sophisticated custom converters can insert custom C code to
+handle initialization and cleanup.
+You can see more examples of custom converters in the CPython
+source tree; grep the C files for the string ``CConverter``.
+
+Writing a custom return converter
+---------------------------------
+
+Writing a custom return converter is much like writing
+a custom converter. Except it's somewhat simpler, because return
+converters are themselves much simpler.
+
+Return converters must subclass ``CReturnConverter``.
+There are no examples yet of custom return converters,
+because they are not widely used yet. If you wish to
+write your own return converter, please read ``Tools/clinic/clinic.py``,
+specifically the implementation of ``CReturnConverter`` and
+all its subclasses.
+
+METH_O and METH_NOARGS
+----------------------------------------------
+
+To convert a function using ``METH_O``, make sure the function's
+single argument is using the ``object`` converter, and mark the
+arguments as positional-only::
+
+ /*[clinic input]
+ meth_o_sample
+
+ argument: object
+ /
+ [clinic start generated code]*/
+
+
+To convert a function using ``METH_NOARGS``, just don't specify
+any arguments.
+
+You can still use a self converter, a return converter, and specify
+a ``type`` argument to the object converter for ``METH_O``.
+
+tp_new and tp_init functions
+----------------------------------------------
+
+You can convert ``tp_new`` and ``tp_init`` functions. Just name
+them ``__new__`` or ``__init__`` as appropriate. Notes:
+
+* The function name generated for ``__new__`` doesn't end in ``__new__``
+ like it would by default. It's just the name of the class, converted
+ into a valid C identifier.
+
+* No ``PyMethodDef`` ``#define`` is generated for these functions.
+
+* ``__init__`` functions return ``int``, not ``PyObject *``.
+
+* Use the docstring as the class docstring.
+
+* Although ``__new__`` and ``__init__`` functions must always
+ accept both the ``args`` and ``kwargs`` objects, when converting
+ you may specify any signature for these functions that you like.
+ (If your function doesn't support keywords, the parsing function
+ generated will throw an exception if it receives any.)
+
+Changing and redirecting Clinic's output
+----------------------------------------
+
+It can be inconvenient to have Clinic's output interspersed with
+your conventional hand-edited C code. Luckily, Clinic is configurable:
+you can buffer up its output for printing later (or earlier!), or write
+its output to a separate file. You can also add a prefix or suffix to
+every line of Clinic's generated output.
+
+While changing Clinic's output in this manner can be a boon to readability,
+it may result in Clinic code using types before they are defined, or
+your code attempting to use Clinic-generated code befire it is defined.
+These problems can be easily solved by rearranging the declarations in your file,
+or moving where Clinic's generated code goes. (This is why the default behavior
+of Clinic is to output everything into the current block; while many people
+consider this hampers readability, it will never require rearranging your
+code to fix definition-before-use problems.)
+
+Let's start with defining some terminology:
+
+*field*
+ A field, in this context, is a subsection of Clinic's output.
+ For example, the ``#define`` for the ``PyMethodDef`` structure
+ is a field, called ``methoddef_define``. Clinic has seven
+ different fields it can output per function definition::
+
+ docstring_prototype
+ docstring_definition
+ methoddef_define
+ impl_prototype
+ parser_prototype
+ parser_definition
+ impl_definition
+
+ All the names are of the form ``"<a>_<b>"``,
+ where ``"<a>"`` is the semantic object represented (the parsing function,
+ the impl function, the docstring, or the methoddef structure) and ``"<b>"``
+ represents what kind of statement the field is. Field names that end in
+ ``"_prototype"``
+ represent forward declarations of that thing, without the actual body/data
+ of the thing; field names that end in ``"_definition"`` represent the actual
+ definition of the thing, with the body/data of the thing. (``"methoddef"``
+ is special, it's the only one that ends with ``"_define"``, representing that
+ it's a preprocessor #define.)
+
+*destination*
+ A destination is a place Clinic can write output to. There are
+ five built-in destinations:
+
+ ``block``
+ The default destination: printed in the output section of
+ the current Clinic block.
+
+ ``buffer``
+ A text buffer where you can save text for later. Text sent
+ here is appended to the end of any exsiting text. It's an
+ error to have any text left in the buffer when Clinic finishes
+ processing a file.
+
+ ``file``
+ A separate "clinic file" that will be created automatically by Clinic.
+ The filename chosen for the file is ``{basename}.clinic{extension}``,
+ where ``basename`` and ``extension`` were assigned the output
+ from ``os.path.splitext()`` run on the current file. (Example:
+ the ``file`` destination for ``_pickle.c`` would be written to
+ ``_pickle.clinic.c``.)
+
+ **Important: When using a** ``file`` **destination, you**
+ *must check in* **the generated file!**
+
+ ``two-pass``
+ A buffer like ``buffer``. However, a two-pass buffer can only
+ be written once, and it prints out all text sent to it during
+ all of processing, even from Clinic blocks *after* the
+
+ ``suppress``
+ The text is suppressed--thrown away.
+
+
+Clinic defines five new directives that let you reconfigure its output.
+
+The first new directive is ``dump``::
+
+ dump <destination>
+
+This dumps the current contents of the named destination into the output of
+the current block, and empties it. This only works with ``buffer`` and
+``two-pass`` destinations.
+
+The second new directive is ``output``. The most basic form of ``output``
+is like this::
+
+ output <field> <destination>
+
+This tells Clinic to output *field* to *destination*. ``output`` also
+supports a special meta-destination, called ``everything``, which tells
+Clinic to output *all* fields to that *destination*.
+
+``output`` has a number of other functions::
+
+ output push
+ output pop
+ output preset <preset>
+
+
+``output push`` and ``output pop`` allow you to push and pop
+configurations on an internal configuration stack, so that you
+can temporarily modify the output configuration, then easily restore
+the previous configuration. Simply push before your change to save
+the current configuration, then pop when you wish to restore the
+previous configuration.
+
+``output preset`` sets Clinic's output to one of several built-in
+preset configurations, as follows:
+
+ ``block``
+ Clinic's original starting configuration. Writes everything
+ immediately after the input block.
+
+ Suppress the ``parser_prototype``
+ and ``docstring_prototype``, write everything else to ``block``.
+
+ ``file``
+ Designed to write everything to the "clinic file" that it can.
+ You then ``#include`` this file near the top of your file.
+ You may need to rearrange your file to make this work, though
+ usually this just means creating forward declarations for various
+ ``typedef`` and ``PyTypeObject`` definitions.
+
+ Suppress the ``parser_prototype``
+ and ``docstring_prototype``, write the ``impl_definition`` to
+ ``block``, and write everything else to ``file``.
+
+ The default filename is ``"{dirname}/clinic/{basename}.h"``.
+
+ ``buffer``
+ Save up all most of the output from Clinic, to be written into
+ your file near the end. For Python files implementing modules
+ or builtin types, it's recommended that you dump the buffer
+ just above the static structures for your module or
+ builtin type; these are normally very near the end. Using
+ ``buffer`` may require even more editing than ``file``, if
+ your file has static ``PyMethodDef`` arrays defined in the
+ middle of the file.
+
+ Suppress the ``parser_prototype``, ``impl_prototype``,
+ and ``docstring_prototype``, write the ``impl_definition`` to
+ ``block``, and write everything else to ``file``.
+
+ ``two-pass``
+ Similar to the ``buffer`` preset, but writes forward declarations to
+ the ``two-pass`` buffer, and definitions to the ``buffer``.
+ This is similar to the ``buffer`` preset, but may require
+ less editing than ``buffer``. Dump the ``two-pass`` buffer
+ near the top of your file, and dump the ``buffer`` near
+ the end just like you would when using the ``buffer`` preset.
+
+ Suppresses the ``impl_prototype``, write the ``impl_definition``
+ to ``block``, write ``docstring_prototype``, ``methoddef_define``,
+ and ``parser_prototype`` to ``two-pass``, write everything else
+ to ``buffer``.
+
+ ``partial-buffer``
+ Similar to the ``buffer`` preset, but writes more things to ``block``,
+ only writing the really big chunks of generated code to ``buffer``.
+ This avoids the definition-before-use problem of ``buffer`` completely,
+ at the small cost of having slightly more stuff in the block's output.
+ Dump the ``buffer`` near the end, just like you would when using
+ the ``buffer`` preset.
+
+ Suppresses the ``impl_prototype``, write the ``docstring_definition``
+ and ``parser_defintion`` to ``buffer``, write everything else to ``block``.
+
+The third new directive is ``destination``::
+
+ destination <name> <command> [...]
+
+This performs an operation on the destination named ``name``.
+
+There are two defined subcommands: ``new`` and ``clear``.
+
+The ``new`` subcommand works like this::
+
+ destination <name> new <type>
+
+This creates a new destination with name ``<name>`` and type ``<type>``.
+
+There are five destination types:
+
+ ``suppress``
+ Throws the text away.
+
+ ``block``
+ Writes the text to the current block. This is what Clinic
+ originally did.
+
+ ``buffer``
+ A simple text buffer, like the "buffer" builtin destination above.
+
+ ``file``
+ A text file. The file destination takes an extra argument,
+ a template to use for building the filename, like so:
+
+ destination <name> new <type> <file_template>
+
+ The template can use three strings internally that will be replaced
+ by bits of the filename:
+
+ {path}
+ The full path to the file, including directory and full filename.
+ {dirname}
+ The name of the directory the file is in.
+ {basename}
+ Just the name of the file, not including the directory.
+ {basename_root}
+ Basename with the extension clipped off
+ (everything up to but not including the last '.').
+ {basename_extension}
+ The last '.' and everything after it. If the basename
+ does not contain a period, this will be the empty string.
+
+ If there are no periods in the filename, {basename} and {filename}
+ are the same, and {extension} is empty. "{basename}{extension}"
+ is always exactly the same as "{filename}"."
+
+ ``two-pass``
+ A two-pass buffer, like the "two-pass" builtin destination above.
+
+
+The ``clear`` subcommand works like this::
+
+ destination <name> clear
+
+It removes all the accumulated text up to this point in the destination.
+(I don't know what you'd need this for, but I thought maybe it'd be
+useful while someone's experimenting.)
+
+The fourth new directive is ``set``::
+
+ set line_prefix "string"
+ set line_suffix "string"
+
+``set`` lets you set two internal variables in Clinic.
+``line_prefix`` is a string that will be prepended to every line of Clinic's output;
+``line_suffix`` is a string that will be appended to every line of Clinic's output.
+
+Both of these suport two format strings:
+
+ ``{block comment start}``
+ Turns into the string ``/*``, the start-comment text sequence for C files.
+
+ ``{block comment end}``
+ Turns into the string ``*/``, the end-comment text sequence for C files.
+
+The final new directive is one you shouldn't need to use directly,
+called ``preserve``::
+
+ preserve
+
+This tells Clinic that the current contents of the output should be kept, unmodifed.
+This is used internally by Clinic when dumping output into ``file`` files; wrapping
+it in a Clinic block lets Clinic use its existing checksum functionality to ensure
+the file was not modified by hand before it gets overwritten.
+
+
+The #ifdef trick
+----------------------------------------------
+
+If you're converting a function that isn't available on all platforms,
+there's a trick you can use to make life a little easier. The existing
+code probably looks like this::
+
+ #ifdef HAVE_FUNCTIONNAME
+ static module_functionname(...)
+ {
+ ...
+ }
+ #endif /* HAVE_FUNCTIONNAME */
+
+And then in the ``PyMethodDef`` structure at the bottom the existing code
+will have::
+
+ #ifdef HAVE_FUNCTIONNAME
+ {'functionname', ... },
+ #endif /* HAVE_FUNCTIONNAME */
+
+In this scenario, you should enclose the body of your impl function inside the ``#ifdef``,
+like so::
+
+ #ifdef HAVE_FUNCTIONNAME
+ /*[clinic input]
+ module.functionname
+ ...
+ [clinic start generated code]*/
+ static module_functionname(...)
+ {
+ ...
+ }
+ #endif /* HAVE_FUNCTIONNAME */
+
+Then, remove those three lines from the ``PyMethodDef`` structure,
+replacing them with the macro Argument Clinic generated::
+
+ MODULE_FUNCTIONNAME_METHODDEF
+
+(You can find the real name for this macro inside the generated code.
+Or you can calculate it yourself: it's the name of your function as defined
+on the first line of your block, but with periods changed to underscores,
+uppercased, and ``"_METHODDEF"`` added to the end.)
+
+Perhaps you're wondering: what if ``HAVE_FUNCTIONNAME`` isn't defined?
+The ``MODULE_FUNCTIONNAME_METHODDEF`` macro won't be defined either!
+
+Here's where Argument Clinic gets very clever. It actually detects that the
+Argument Clinic block might be deactivated by the ``#ifdef``. When that
+happens, it generates a little extra code that looks like this::
+
+ #ifndef MODULE_FUNCTIONNAME_METHODDEF
+ #define MODULE_FUNCTIONNAME_METHODDEF
+ #endif /* !defined(MODULE_FUNCTIONNAME_METHODDEF) */
+
+That means the macro always works. If the function is defined, this turns
+into the correct structure, including the trailing comma. If the function is
+undefined, this turns into nothing.
+
+However, this causes one ticklish problem: where should Argument Clinic put this
+extra code when using the "block" output preset? It can't go in the output block,
+because that could be decativated by the ``#ifdef``. (That's the whole point!)
+
+In this situation, Argument Clinic writes the extra code to the "buffer" destination.
+This may mean that you get a complaint from Argument Clinic::
+
+ Warning in file "Modules/posixmodule.c" on line 12357:
+ Destination buffer 'buffer' not empty at end of file, emptying.
+
+When this happens, just open your file, find the ``dump buffer`` block that
+Argument Clinic added to your file (it'll be at the very bottom), then
+move it above the ``PyMethodDef`` structure where that macro is used.
+
+
+
+Using Argument Clinic in Python files
+-------------------------------------
+
+It's actually possible to use Argument Clinic to preprocess Python files.
+There's no point to using Argument Clinic blocks, of course, as the output
+wouldn't make any sense to the Python interpreter. But using Argument Clinic
+to run Python blocks lets you use Python as a Python preprocessor!
+
+Since Python comments are different from C comments, Argument Clinic
+blocks embedded in Python files look slightly different. They look like this::
+
+ #/*[python input]
+ #print("def foo(): pass")
+ #[python start generated code]*/
+ def foo(): pass
+ #/*[python checksum:...]*/
diff --git a/Doc/howto/functional.rst b/Doc/howto/functional.rst
index 7189c65..97c90c7 100644
--- a/Doc/howto/functional.rst
+++ b/Doc/howto/functional.rst
@@ -3,7 +3,7 @@
********************************
:Author: A. M. Kuchling
-:Release: 0.31
+:Release: 0.32
In this document, we'll take a tour of Python's features suitable for
implementing programs in a functional style. After an introduction to the
@@ -15,9 +15,9 @@ concepts of functional programming, we'll look at language features such as
Introduction
============
-This section explains the basic concept of functional programming; if you're
-just interested in learning about Python language features, skip to the next
-section.
+This section explains the basic concept of functional programming; if
+you're just interested in learning about Python language features,
+skip to the next section on :ref:`functional-howto-iterators`.
Programming languages support decomposing problems in several different ways:
@@ -173,6 +173,8 @@ new programs by arranging existing functions in a new configuration and writing
a few functions specialized for the current task.
+.. _functional-howto-iterators:
+
Iterators
=========
@@ -670,7 +672,7 @@ indexes at which certain conditions are met::
:func:`sorted(iterable, key=None, reverse=False) <sorted>` collects all the
elements of the iterable into a list, sorts the list, and returns the sorted
-result. The *key*, and *reverse* arguments are passed through to the
+result. The *key* and *reverse* arguments are passed through to the
constructed list's :meth:`~list.sort` method. ::
>>> import random
@@ -836,7 +838,8 @@ Another group of functions chooses a subset of an iterator's elements based on a
predicate.
:func:`itertools.filterfalse(predicate, iter) <itertools.filterfalse>` is the
-opposite, returning all elements for which the predicate returns false::
+opposite of :func:`filter`, returning all elements for which the predicate
+returns false::
itertools.filterfalse(is_even, itertools.count()) =>
1, 3, 5, 7, 9, 11, 13, 15, ...
@@ -864,6 +867,77 @@ iterable's results. ::
itertools.dropwhile(is_even, itertools.count()) =>
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...
+:func:`itertools.compress(data, selectors) <itertools.compress>` takes two
+iterators and returns only those elements of *data* for which the corresponding
+element of *selectors* is true, stopping whenever either one is exhausted::
+
+ itertools.compress([1,2,3,4,5], [True, True, False, False, True]) =>
+ 1, 2, 5
+
+
+Combinatoric functions
+----------------------
+
+The :func:`itertools.combinations(iterable, r) <itertools.combinations>`
+returns an iterator giving all possible *r*-tuple combinations of the
+elements contained in *iterable*. ::
+
+ itertools.combinations([1, 2, 3, 4, 5], 2) =>
+ (1, 2), (1, 3), (1, 4), (1, 5),
+ (2, 3), (2, 4), (2, 5),
+ (3, 4), (3, 5),
+ (4, 5)
+
+ itertools.combinations([1, 2, 3, 4, 5], 3) =>
+ (1, 2, 3), (1, 2, 4), (1, 2, 5), (1, 3, 4), (1, 3, 5), (1, 4, 5),
+ (2, 3, 4), (2, 3, 5), (2, 4, 5),
+ (3, 4, 5)
+
+The elements within each tuple remain in the same order as
+*iterable* returned them. For example, the number 1 is always before
+2, 3, 4, or 5 in the examples above. A similar function,
+:func:`itertools.permutations(iterable, r=None) <itertools.permutations>`,
+removes this constraint on the order, returning all possible
+arrangements of length *r*::
+
+ itertools.permutations([1, 2, 3, 4, 5], 2) =>
+ (1, 2), (1, 3), (1, 4), (1, 5),
+ (2, 1), (2, 3), (2, 4), (2, 5),
+ (3, 1), (3, 2), (3, 4), (3, 5),
+ (4, 1), (4, 2), (4, 3), (4, 5),
+ (5, 1), (5, 2), (5, 3), (5, 4)
+
+ itertools.permutations([1, 2, 3, 4, 5]) =>
+ (1, 2, 3, 4, 5), (1, 2, 3, 5, 4), (1, 2, 4, 3, 5),
+ ...
+ (5, 4, 3, 2, 1)
+
+If you don't supply a value for *r* the length of the iterable is used,
+meaning that all the elements are permuted.
+
+Note that these functions produce all of the possible combinations by
+position and don't require that the contents of *iterable* are unique::
+
+ itertools.permutations('aba', 3) =>
+ ('a', 'b', 'a'), ('a', 'a', 'b'), ('b', 'a', 'a'),
+ ('b', 'a', 'a'), ('a', 'a', 'b'), ('a', 'b', 'a')
+
+The identical tuple ``('a', 'a', 'b')`` occurs twice, but the two 'a'
+strings came from different positions.
+
+The :func:`itertools.combinations_with_replacement(iterable, r) <itertools.combinations_with_replacement>`
+function relaxes a different constraint: elements can be repeated
+within a single tuple. Conceptually an element is selected for the
+first position of each tuple and then is replaced before the second
+element is selected. ::
+
+ itertools.combinations_with_replacement([1, 2, 3, 4, 5], 2) =>
+ (1, 1), (1, 2), (1, 3), (1, 4), (1, 5),
+ (2, 2), (2, 3), (2, 4), (2, 5),
+ (3, 3), (3, 4), (3, 5),
+ (4, 4), (4, 5),
+ (5, 5)
+
Grouping elements
-----------------
@@ -986,6 +1060,17 @@ write the obvious :keyword:`for` loop::
for i in [1,2,3]:
product *= i
+A related function is `itertools.accumulate(iterable, func=operator.add) <itertools.accumulate`.
+It performs the same calculation, but instead of returning only the
+final result, :func:`accumulate` returns an iterator that also yields
+each partial result::
+
+ itertools.accumulate([1,2,3,4,5]) =>
+ 1, 3, 6, 10, 15
+
+ itertools.accumulate([1,2,3,4,5], operator.mul) =>
+ 1, 2, 6, 24, 120
+
The operator module
-------------------
@@ -1159,51 +1244,6 @@ features in Python 2.5.
.. comment
- Topics to place
- -----------------------------
-
- XXX os.walk()
-
- XXX Need a large example.
-
- But will an example add much? I'll post a first draft and see
- what the comments say.
-
-.. comment
-
- Original outline:
- Introduction
- Idea of FP
- Programs built out of functions
- Functions are strictly input-output, no internal state
- Opposed to OO programming, where objects have state
-
- Why FP?
- Formal provability
- Assignment is difficult to reason about
- Not very relevant to Python
- Modularity
- Small functions that do one thing
- Debuggability:
- Easy to test due to lack of state
- Easy to verify output from intermediate steps
- Composability
- You assemble a toolbox of functions that can be mixed
-
- Tackling a problem
- Need a significant example
-
- Iterators
- Generators
- The itertools module
- List comprehensions
- Small functions and the lambda statement
- Built-in functions
- map
- filter
-
-.. comment
-
Handy little function for printing part of an iterator -- used
while writing this document.
@@ -1214,5 +1254,3 @@ features in Python 2.5.
sys.stdout.write(str(elem))
sys.stdout.write(', ')
print(elem[-1])
-
-
diff --git a/Doc/howto/index.rst b/Doc/howto/index.rst
index 81a4f8b..2c9d699 100644
--- a/Doc/howto/index.rst
+++ b/Doc/howto/index.rst
@@ -28,4 +28,5 @@ Currently, the HOWTOs are:
webservers.rst
argparse.rst
ipaddress.rst
+ clinic.rst
diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst
index 563da9d..82c8f51 100644
--- a/Doc/howto/logging-cookbook.rst
+++ b/Doc/howto/logging-cookbook.rst
@@ -416,7 +416,7 @@ module. Here is a basic working example::
Simple TCP socket-based logging receiver suitable for testing.
"""
- allow_reuse_address = 1
+ allow_reuse_address = True
def __init__(self, host='localhost',
port=logging.handlers.DEFAULT_TCP_LOGGING_PORT,
@@ -704,9 +704,7 @@ the basis for code meeting your own specific requirements::
break
logger = logging.getLogger(record.name)
logger.handle(record) # No level or filter logic applied - just do it!
- except (KeyboardInterrupt, SystemExit):
- raise
- except:
+ except Exception:
import sys, traceback
print('Whoops! Problem:', file=sys.stderr)
traceback.print_exc(file=sys.stderr)
@@ -1310,7 +1308,7 @@ This dictionary is passed to :func:`~config.dictConfig` to put the configuration
}
For more information about this configuration, you can see the `relevant
-section <https://docs.djangoproject.com/en/1.3/topics/logging/#configuring-logging>`_
+section <https://docs.djangoproject.com/en/1.6/topics/logging/#configuring-logging>`_
of the Django documentation.
.. _cookbook-rotator-namer:
diff --git a/Doc/howto/logging.rst b/Doc/howto/logging.rst
index 55b1c86..dab58f4 100644
--- a/Doc/howto/logging.rst
+++ b/Doc/howto/logging.rst
@@ -901,10 +901,10 @@ provided:
disk files, rotating the log file at certain timed intervals.
#. :class:`~handlers.SocketHandler` instances send messages to TCP/IP
- sockets.
+ sockets. Since 3.4, Unix domain sockets are also supported.
#. :class:`~handlers.DatagramHandler` instances send messages to UDP
- sockets.
+ sockets. Since 3.4, Unix domain sockets are also supported.
#. :class:`~handlers.SMTPHandler` instances send messages to a designated
email address.
diff --git a/Doc/howto/regex.rst b/Doc/howto/regex.rst
index 5203e53..fbe763b 100644
--- a/Doc/howto/regex.rst
+++ b/Doc/howto/regex.rst
@@ -271,8 +271,8 @@ performing string substitutions. ::
>>> import re
>>> p = re.compile('ab*')
- >>> p #doctest: +ELLIPSIS
- <_sre.SRE_Pattern object at 0x...>
+ >>> p
+ re.compile('ab*')
:func:`re.compile` also accepts an optional *flags* argument, used to enable
various special features and syntax variations. We'll go over the available
@@ -383,8 +383,8 @@ Python interpreter, import the :mod:`re` module, and compile a RE::
>>> import re
>>> p = re.compile('[a-z]+')
- >>> p #doctest: +ELLIPSIS
- <_sre.SRE_Pattern object at 0x...>
+ >>> p
+ re.compile('[a-z]+')
Now, you can try matching various strings against the RE ``[a-z]+``. An empty
string shouldn't match at all, since ``+`` means 'one or more repetitions'.
@@ -402,7 +402,7 @@ should store the result in a variable for later use. ::
>>> m = p.match('tempo')
>>> m #doctest: +ELLIPSIS
- <_sre.SRE_Match object at 0x...>
+ <_sre.SRE_Match object; span=(0, 5), match='tempo'>
Now you can query the :ref:`match object <match-objects>` for information
about the matching string. :ref:`match object <match-objects>` instances
@@ -441,7 +441,7 @@ case. ::
>>> print(p.match('::: message'))
None
>>> m = p.search('::: message'); print(m) #doctest: +ELLIPSIS
- <_sre.SRE_Match object at 0x...>
+ <_sre.SRE_Match object; span=(4, 11), match='message'>
>>> m.group()
'message'
>>> m.span()
@@ -493,7 +493,7 @@ the RE string added as the first argument, and still return either ``None`` or a
>>> print(re.match(r'From\s+', 'Fromage amk'))
None
>>> re.match(r'From\s+', 'From amk Thu May 14 19:12:10 1998') #doctest: +ELLIPSIS
- <_sre.SRE_Match object at 0x...>
+ <_sre.SRE_Match object; span=(0, 5), match='From '>
Under the hood, these functions simply create a pattern object for you
and call the appropriate method on it. They also store the compiled
@@ -685,7 +685,7 @@ given location, they can obviously be matched an infinite number of times.
line, the RE to use is ``^From``. ::
>>> print(re.search('^From', 'From Here to Eternity')) #doctest: +ELLIPSIS
- <_sre.SRE_Match object at 0x...>
+ <_sre.SRE_Match object; span=(0, 4), match='From'>
>>> print(re.search('^From', 'Reciting From Memory'))
None
@@ -697,11 +697,11 @@ given location, they can obviously be matched an infinite number of times.
or any location followed by a newline character. ::
>>> print(re.search('}$', '{block}')) #doctest: +ELLIPSIS
- <_sre.SRE_Match object at 0x...>
+ <_sre.SRE_Match object; span=(6, 7), match='}'>
>>> print(re.search('}$', '{block} '))
None
>>> print(re.search('}$', '{block}\n')) #doctest: +ELLIPSIS
- <_sre.SRE_Match object at 0x...>
+ <_sre.SRE_Match object; span=(6, 7), match='}'>
To match a literal ``'$'``, use ``\$`` or enclose it inside a character class,
as in ``[$]``.
@@ -726,7 +726,7 @@ given location, they can obviously be matched an infinite number of times.
>>> p = re.compile(r'\bclass\b')
>>> print(p.search('no class at all')) #doctest: +ELLIPSIS
- <_sre.SRE_Match object at 0x...>
+ <_sre.SRE_Match object; span=(3, 8), match='class'>
>>> print(p.search('the declassified algorithm'))
None
>>> print(p.search('one subclass is'))
@@ -744,7 +744,7 @@ given location, they can obviously be matched an infinite number of times.
>>> print(p.search('no class at all'))
None
>>> print(p.search('\b' + 'class' + '\b')) #doctest: +ELLIPSIS
- <_sre.SRE_Match object at 0x...>
+ <_sre.SRE_Match object; span=(0, 7), match='\x08class\x08'>
Second, inside a character class, where there's no use for this assertion,
``\b`` represents the backspace character, for compatibility with Python's
diff --git a/Doc/howto/unicode.rst b/Doc/howto/unicode.rst
index 9d48a78..632e525 100644
--- a/Doc/howto/unicode.rst
+++ b/Doc/howto/unicode.rst
@@ -49,7 +49,7 @@ another and managed to catch on.
255 characters aren't very many. For example, you can't fit both the accented
characters used in Western Europe and the Cyrillic alphabet used for Russian
-into the 128--255 range because there are more than 127 such characters.
+into the 128--255 range because there are more than 128 such characters.
You could write files using different codes (all your Russian files in a coding
system called KOI8, all your French files in a different coding system called
@@ -246,7 +246,7 @@ include a Unicode character in a string literal::
try:
with open('/tmp/input.txt', 'r') as f:
...
- except IOError:
+ except OSError:
# 'File not found' error message.
print("Fichier non trouvé")
diff --git a/Doc/howto/urllib2.rst b/Doc/howto/urllib2.rst
index 5a22660..3d5de32 100644
--- a/Doc/howto/urllib2.rst
+++ b/Doc/howto/urllib2.rst
@@ -507,7 +507,7 @@ than the URL you pass to .add_password() will also match. ::
-- ``ProxyHandler`` (if a proxy setting such as an :envvar:`http_proxy`
environment variable is set), ``UnknownHandler``, ``HTTPHandler``,
``HTTPDefaultErrorHandler``, ``HTTPRedirectHandler``, ``FTPHandler``,
- ``FileHandler``, ``HTTPErrorProcessor``.
+ ``FileHandler``, ``DataHandler``, ``HTTPErrorProcessor``.
``top_level_url`` is in fact *either* a full URL (including the 'http:' scheme
component and the hostname and optionally the port number)
diff --git a/Doc/includes/email-alternative-new-api.py b/Doc/includes/email-alternative-new-api.py
new file mode 100644
index 0000000..c1255a6
--- /dev/null
+++ b/Doc/includes/email-alternative-new-api.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python3
+
+import smtplib
+
+from email.message import EmailMessage
+from email.headerregistry import Address
+from email.utils import make_msgid
+
+# Create the base text message.
+msg = EmailMessage()
+msg['Subject'] = "Ayons asperges pour le déjeuner"
+msg['From'] = Address("Pepé Le Pew", "pepe@example.com")
+msg['To'] = (Address("Penelope Pussycat", "penelope@example.com"),
+ Address("Fabrette Pussycat", "fabrette@example.com"))
+msg.set_content("""\
+Salut!
+
+Cela ressemble à un excellent recipie[1] déjeuner.
+
+[1] http://www.yummly.com/recipe/Roasted-Asparagus-Epicurious-203718
+
+--Pepé
+""")
+
+# Add the html version. This converts the message into a multipart/alternative
+# container, with the original text message as the first part and the new html
+# message as the second part.
+asparagus_cid = make_msgid()
+msg.add_alternative("""\
+<html>
+ <head></head>
+ <body>
+ <p>Salut!<\p>
+ <p>Cela ressemble à un excellent
+ <a href="http://www.yummly.com/recipe/Roasted-Asparagus-Epicurious-203718>
+ recipie
+ </a> déjeuner.
+ </p>
+ <img src="cid:{asparagus_cid}" \>
+ </body>
+</html>
+""".format(asparagus_cid=asparagus_cid[1:-1]), subtype='html')
+# note that we needed to peel the <> off the msgid for use in the html.
+
+# Now add the related image to the html part.
+with open("roasted-asparagus.jpg", 'rb') as img:
+ msg.get_payload()[1].add_related(img.read(), 'image', 'jpeg',
+ cid=asparagus_cid)
+
+# Make a local copy of what we are going to send.
+with open('outgoing.msg', 'wb') as f:
+ f.write(bytes(msg))
+
+# Send the message via local SMTP server.
+with smtplib.SMTP('localhost') as s:
+ s.send_message(msg)
diff --git a/Doc/includes/email-dir.py b/Doc/includes/email-dir.py
index cc5529e..3c7c770 100644
--- a/Doc/includes/email-dir.py
+++ b/Doc/includes/email-dir.py
@@ -8,7 +8,7 @@ import smtplib
# For guessing MIME type based on file name extension
import mimetypes
-from optparse import OptionParser
+from argparse import ArgumentParser
from email import encoders
from email.message import Message
@@ -22,44 +22,36 @@ COMMASPACE = ', '
def main():
- parser = OptionParser(usage="""\
+ parser = ArgumentParser(description="""\
Send the contents of a directory as a MIME message.
-
-Usage: %prog [options]
-
Unless the -o option is given, the email is sent by forwarding to your local
SMTP server, which then does the normal delivery process. Your local machine
must be running an SMTP server.
""")
- parser.add_option('-d', '--directory',
- type='string', action='store',
- help="""Mail the contents of the specified directory,
- otherwise use the current directory. Only the regular
- files in the directory are sent, and we don't recurse to
- subdirectories.""")
- parser.add_option('-o', '--output',
- type='string', action='store', metavar='FILE',
- help="""Print the composed message to FILE instead of
- sending the message to the SMTP server.""")
- parser.add_option('-s', '--sender',
- type='string', action='store', metavar='SENDER',
- help='The value of the From: header (required)')
- parser.add_option('-r', '--recipient',
- type='string', action='append', metavar='RECIPIENT',
- default=[], dest='recipients',
- help='A To: header value (at least one required)')
- opts, args = parser.parse_args()
- if not opts.sender or not opts.recipients:
- parser.print_help()
- sys.exit(1)
- directory = opts.directory
+ parser.add_argument('-d', '--directory',
+ help="""Mail the contents of the specified directory,
+ otherwise use the current directory. Only the regular
+ files in the directory are sent, and we don't recurse to
+ subdirectories.""")
+ parser.add_argument('-o', '--output',
+ metavar='FILE',
+ help="""Print the composed message to FILE instead of
+ sending the message to the SMTP server.""")
+ parser.add_argument('-s', '--sender', required=True,
+ help='The value of the From: header (required)')
+ parser.add_argument('-r', '--recipient', required=True,
+ action='append', metavar='RECIPIENT',
+ default=[], dest='recipients',
+ help='A To: header value (at least one required)')
+ args = parser.parse_args()
+ directory = args.directory
if not directory:
directory = '.'
# Create the enclosing (outer) message
outer = MIMEMultipart()
outer['Subject'] = 'Contents of directory %s' % os.path.abspath(directory)
- outer['To'] = COMMASPACE.join(opts.recipients)
- outer['From'] = opts.sender
+ outer['To'] = COMMASPACE.join(args.recipients)
+ outer['From'] = args.sender
outer.preamble = 'You will not see this in a MIME-aware mail reader.\n'
for filename in os.listdir(directory):
@@ -76,23 +68,19 @@ must be running an SMTP server.
ctype = 'application/octet-stream'
maintype, subtype = ctype.split('/', 1)
if maintype == 'text':
- fp = open(path)
- # Note: we should handle calculating the charset
- msg = MIMEText(fp.read(), _subtype=subtype)
- fp.close()
+ with open(path) as fp:
+ # Note: we should handle calculating the charset
+ msg = MIMEText(fp.read(), _subtype=subtype)
elif maintype == 'image':
- fp = open(path, 'rb')
- msg = MIMEImage(fp.read(), _subtype=subtype)
- fp.close()
+ with open(path, 'rb') as fp:
+ msg = MIMEImage(fp.read(), _subtype=subtype)
elif maintype == 'audio':
- fp = open(path, 'rb')
- msg = MIMEAudio(fp.read(), _subtype=subtype)
- fp.close()
+ with open(path, 'rb') as fp:
+ msg = MIMEAudio(fp.read(), _subtype=subtype)
else:
- fp = open(path, 'rb')
- msg = MIMEBase(maintype, subtype)
- msg.set_payload(fp.read())
- fp.close()
+ with open(path, 'rb') as fp:
+ msg = MIMEBase(maintype, subtype)
+ msg.set_payload(fp.read())
# Encode the payload using Base64
encoders.encode_base64(msg)
# Set the filename parameter
@@ -100,14 +88,12 @@ must be running an SMTP server.
outer.attach(msg)
# Now send or store the message
composed = outer.as_string()
- if opts.output:
- fp = open(opts.output, 'w')
- fp.write(composed)
- fp.close()
+ if args.output:
+ with open(args.output, 'w') as fp:
+ fp.write(composed)
else:
- s = smtplib.SMTP('localhost')
- s.sendmail(opts.sender, opts.recipients, composed)
- s.quit()
+ with smtplib.SMTP('localhost') as s:
+ s.sendmail(args.sender, args.recipients, composed)
if __name__ == '__main__':
diff --git a/Doc/includes/email-read-alternative-new-api.py b/Doc/includes/email-read-alternative-new-api.py
new file mode 100644
index 0000000..8ab4e9f
--- /dev/null
+++ b/Doc/includes/email-read-alternative-new-api.py
@@ -0,0 +1,74 @@
+import os
+import sys
+import tempfile
+import mimetypes
+import webbrowser
+
+# Import the email modules we'll need
+from email import policy
+from email.parser import BytesParser
+
+# An imaginary module that would make this work and be safe.
+from imaginary import magic_html_parser
+
+# In a real program you'd get the filename from the arguments.
+msg = BytesParser(policy=policy.default).parse(open('outgoing.msg', 'rb'))
+
+# Now the header items can be accessed as a dictionary, and any non-ASCII will
+# be converted to unicode:
+print('To:', msg['to'])
+print('From:', msg['from'])
+print('Subject:', msg['subject'])
+
+# If we want to print a priview of the message content, we can extract whatever
+# the least formatted payload is and print the first three lines. Of course,
+# if the message has no plain text part printing the first three lines of html
+# is probably useless, but this is just a conceptual example.
+simplest = msg.get_body(preferencelist=('plain', 'html'))
+print()
+print(''.join(simplest.get_content().splitlines(keepends=True)[:3]))
+
+ans = input("View full message?")
+if ans.lower()[0] == 'n':
+ sys.exit()
+
+# We can extract the richest alternative in order to display it:
+richest = msg.get_body()
+partfiles = {}
+if richest['content-type'].maintype == 'text':
+ if richest['content-type'].subtype == 'plain':
+ for line in richest.get_content().splitlines():
+ print(line)
+ sys.exit()
+ elif richest['content-type'].subtype == 'html':
+ body = richest
+ else:
+ print("Don't know how to display {}".format(richest.get_content_type()))
+ sys.exit()
+elif richest['content-type'].content_type == 'multipart/related':
+ body = richest.get_body(preferencelist=('html'))
+ for part in richest.iter_attachments():
+ fn = part.get_filename()
+ if fn:
+ extension = os.path.splitext(part.get_filename())[1]
+ else:
+ extension = mimetypes.guess_extension(part.get_content_type())
+ with tempfile.NamedTemporaryFile(suffix=extension, delete=False) as f:
+ f.write(part.get_content())
+ # again strip the <> to go from email form of cid to html form.
+ partfiles[part['content-id'][1:-1]] = f.name
+else:
+ print("Don't know how to display {}".format(richest.get_content_type()))
+ sys.exit()
+with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
+ # The magic_html_parser has to rewrite the href="cid:...." attributes to
+ # point to the filenames in partfiles. It also has to do a safety-sanitize
+ # of the html. It could be written using html.parser.
+ f.write(magic_html_parser(body.get_content(), partfiles))
+webbrowser.open(f.name)
+os.remove(f.name)
+for fn in partfiles.values():
+ os.remove(fn)
+
+# Of course, there are lots of email messages that could break this simple
+# minded program, but it will handle the most common ones.
diff --git a/Doc/includes/email-unpack.py b/Doc/includes/email-unpack.py
index 3653543..574a0b6 100644
--- a/Doc/includes/email-unpack.py
+++ b/Doc/includes/email-unpack.py
@@ -8,41 +8,27 @@ import email
import errno
import mimetypes
-from optparse import OptionParser
+from argparse import ArgumentParser
def main():
- parser = OptionParser(usage="""\
+ parser = ArgumentParser(description="""\
Unpack a MIME message into a directory of files.
-
-Usage: %prog [options] msgfile
""")
- parser.add_option('-d', '--directory',
- type='string', action='store',
- help="""Unpack the MIME message into the named
- directory, which will be created if it doesn't already
- exist.""")
- opts, args = parser.parse_args()
- if not opts.directory:
- parser.print_help()
- sys.exit(1)
+ parser.add_argument('-d', '--directory', required=True,
+ help="""Unpack the MIME message into the named
+ directory, which will be created if it doesn't already
+ exist.""")
+ parser.add_argument('msgfile')
+ args = parser.parse_args()
- try:
- msgfile = args[0]
- except IndexError:
- parser.print_help()
- sys.exit(1)
+ with open(args.msgfile) as fp:
+ msg = email.message_from_file(fp)
try:
- os.mkdir(opts.directory)
- except OSError as e:
- # Ignore directory exists error
- if e.errno != errno.EEXIST:
- raise
-
- fp = open(msgfile)
- msg = email.message_from_file(fp)
- fp.close()
+ os.mkdir(args.directory)
+ except FileExistsError:
+ pass
counter = 1
for part in msg.walk():
@@ -59,9 +45,8 @@ Usage: %prog [options] msgfile
ext = '.bin'
filename = 'part-%03d%s' % (counter, ext)
counter += 1
- fp = open(os.path.join(opts.directory, filename), 'wb')
- fp.write(part.get_payload(decode=True))
- fp.close()
+ with open(os.path.join(args.directory, filename), 'wb') as fp:
+ fp.write(part.get_payload(decode=True))
if __name__ == '__main__':
diff --git a/Doc/includes/mp_benchmarks.py b/Doc/includes/mp_benchmarks.py
deleted file mode 100644
index 3763ea9..0000000
--- a/Doc/includes/mp_benchmarks.py
+++ /dev/null
@@ -1,239 +0,0 @@
-#
-# Simple benchmarks for the multiprocessing package
-#
-# Copyright (c) 2006-2008, R Oudkerk
-# All rights reserved.
-#
-
-import time
-import multiprocessing
-import threading
-import queue
-import gc
-
-_timer = time.perf_counter
-
-delta = 1
-
-
-#### TEST_QUEUESPEED
-
-def queuespeed_func(q, c, iterations):
- a = '0' * 256
- c.acquire()
- c.notify()
- c.release()
-
- for i in range(iterations):
- q.put(a)
-
- q.put('STOP')
-
-def test_queuespeed(Process, q, c):
- elapsed = 0
- iterations = 1
-
- while elapsed < delta:
- iterations *= 2
-
- p = Process(target=queuespeed_func, args=(q, c, iterations))
- c.acquire()
- p.start()
- c.wait()
- c.release()
-
- result = None
- t = _timer()
-
- while result != 'STOP':
- result = q.get()
-
- elapsed = _timer() - t
-
- p.join()
-
- print(iterations, 'objects passed through the queue in', elapsed, 'seconds')
- print('average number/sec:', iterations/elapsed)
-
-
-#### TEST_PIPESPEED
-
-def pipe_func(c, cond, iterations):
- a = '0' * 256
- cond.acquire()
- cond.notify()
- cond.release()
-
- for i in range(iterations):
- c.send(a)
-
- c.send('STOP')
-
-def test_pipespeed():
- c, d = multiprocessing.Pipe()
- cond = multiprocessing.Condition()
- elapsed = 0
- iterations = 1
-
- while elapsed < delta:
- iterations *= 2
-
- p = multiprocessing.Process(target=pipe_func,
- args=(d, cond, iterations))
- cond.acquire()
- p.start()
- cond.wait()
- cond.release()
-
- result = None
- t = _timer()
-
- while result != 'STOP':
- result = c.recv()
-
- elapsed = _timer() - t
- p.join()
-
- print(iterations, 'objects passed through connection in',elapsed,'seconds')
- print('average number/sec:', iterations/elapsed)
-
-
-#### TEST_SEQSPEED
-
-def test_seqspeed(seq):
- elapsed = 0
- iterations = 1
-
- while elapsed < delta:
- iterations *= 2
-
- t = _timer()
-
- for i in range(iterations):
- a = seq[5]
-
- elapsed = _timer() - t
-
- print(iterations, 'iterations in', elapsed, 'seconds')
- print('average number/sec:', iterations/elapsed)
-
-
-#### TEST_LOCK
-
-def test_lockspeed(l):
- elapsed = 0
- iterations = 1
-
- while elapsed < delta:
- iterations *= 2
-
- t = _timer()
-
- for i in range(iterations):
- l.acquire()
- l.release()
-
- elapsed = _timer() - t
-
- print(iterations, 'iterations in', elapsed, 'seconds')
- print('average number/sec:', iterations/elapsed)
-
-
-#### TEST_CONDITION
-
-def conditionspeed_func(c, N):
- c.acquire()
- c.notify()
-
- for i in range(N):
- c.wait()
- c.notify()
-
- c.release()
-
-def test_conditionspeed(Process, c):
- elapsed = 0
- iterations = 1
-
- while elapsed < delta:
- iterations *= 2
-
- c.acquire()
- p = Process(target=conditionspeed_func, args=(c, iterations))
- p.start()
-
- c.wait()
-
- t = _timer()
-
- for i in range(iterations):
- c.notify()
- c.wait()
-
- elapsed = _timer() - t
-
- c.release()
- p.join()
-
- print(iterations * 2, 'waits in', elapsed, 'seconds')
- print('average number/sec:', iterations * 2 / elapsed)
-
-####
-
-def test():
- manager = multiprocessing.Manager()
-
- gc.disable()
-
- print('\n\t######## testing Queue.Queue\n')
- test_queuespeed(threading.Thread, queue.Queue(),
- threading.Condition())
- print('\n\t######## testing multiprocessing.Queue\n')
- test_queuespeed(multiprocessing.Process, multiprocessing.Queue(),
- multiprocessing.Condition())
- print('\n\t######## testing Queue managed by server process\n')
- test_queuespeed(multiprocessing.Process, manager.Queue(),
- manager.Condition())
- print('\n\t######## testing multiprocessing.Pipe\n')
- test_pipespeed()
-
- print()
-
- print('\n\t######## testing list\n')
- test_seqspeed(list(range(10)))
- print('\n\t######## testing list managed by server process\n')
- test_seqspeed(manager.list(list(range(10))))
- print('\n\t######## testing Array("i", ..., lock=False)\n')
- test_seqspeed(multiprocessing.Array('i', list(range(10)), lock=False))
- print('\n\t######## testing Array("i", ..., lock=True)\n')
- test_seqspeed(multiprocessing.Array('i', list(range(10)), lock=True))
-
- print()
-
- print('\n\t######## testing threading.Lock\n')
- test_lockspeed(threading.Lock())
- print('\n\t######## testing threading.RLock\n')
- test_lockspeed(threading.RLock())
- print('\n\t######## testing multiprocessing.Lock\n')
- test_lockspeed(multiprocessing.Lock())
- print('\n\t######## testing multiprocessing.RLock\n')
- test_lockspeed(multiprocessing.RLock())
- print('\n\t######## testing lock managed by server process\n')
- test_lockspeed(manager.Lock())
- print('\n\t######## testing rlock managed by server process\n')
- test_lockspeed(manager.RLock())
-
- print()
-
- print('\n\t######## testing threading.Condition\n')
- test_conditionspeed(threading.Thread, threading.Condition())
- print('\n\t######## testing multiprocessing.Condition\n')
- test_conditionspeed(multiprocessing.Process, multiprocessing.Condition())
- print('\n\t######## testing condition managed by a server process\n')
- test_conditionspeed(multiprocessing.Process, manager.Condition())
-
- gc.enable()
-
-if __name__ == '__main__':
- multiprocessing.freeze_support()
- test()
diff --git a/Doc/includes/mp_newtype.py b/Doc/includes/mp_newtype.py
index 7291743..1c796a5 100644
--- a/Doc/includes/mp_newtype.py
+++ b/Doc/includes/mp_newtype.py
@@ -1,11 +1,3 @@
-#
-# This module shows how to use arbitrary callables with a subclass of
-# `BaseManager`.
-#
-# Copyright (c) 2006-2008, R Oudkerk
-# All rights reserved.
-#
-
from multiprocessing import freeze_support
from multiprocessing.managers import BaseManager, BaseProxy
import operator
@@ -27,12 +19,10 @@ def baz():
# Proxy type for generator objects
class GeneratorProxy(BaseProxy):
- _exposed_ = ('next', '__next__')
+ _exposed_ = ['__next__']
def __iter__(self):
return self
def __next__(self):
- return self._callmethod('next')
- def __next__(self):
return self._callmethod('__next__')
# Function to return the operator module
@@ -90,8 +80,6 @@ def test():
op = manager.operator()
print('op.add(23, 45) =', op.add(23, 45))
print('op.pow(2, 94) =', op.pow(2, 94))
- print('op.getslice(range(10), 2, 6) =', op.getslice(list(range(10)), 2, 6))
- print('op.repeat(range(5), 3) =', op.repeat(list(range(5)), 3))
print('op._exposed_ =', op._exposed_)
##
diff --git a/Doc/includes/mp_pool.py b/Doc/includes/mp_pool.py
index 1578498..11101e1 100644
--- a/Doc/includes/mp_pool.py
+++ b/Doc/includes/mp_pool.py
@@ -1,10 +1,3 @@
-#
-# A test of `multiprocessing.Pool` class
-#
-# Copyright (c) 2006-2008, R Oudkerk
-# All rights reserved.
-#
-
import multiprocessing
import time
import random
@@ -46,269 +39,115 @@ def noop(x):
#
def test():
- print('cpu_count() = %d\n' % multiprocessing.cpu_count())
-
- #
- # Create pool
- #
-
PROCESSES = 4
print('Creating pool with %d processes\n' % PROCESSES)
- pool = multiprocessing.Pool(PROCESSES)
- print('pool = %s' % pool)
- print()
-
- #
- # Tests
- #
-
- TASKS = [(mul, (i, 7)) for i in range(10)] + \
- [(plus, (i, 8)) for i in range(10)]
-
- results = [pool.apply_async(calculate, t) for t in TASKS]
- imap_it = pool.imap(calculatestar, TASKS)
- imap_unordered_it = pool.imap_unordered(calculatestar, TASKS)
-
- print('Ordered results using pool.apply_async():')
- for r in results:
- print('\t', r.get())
- print()
-
- print('Ordered results using pool.imap():')
- for x in imap_it:
- print('\t', x)
- print()
-
- print('Unordered results using pool.imap_unordered():')
- for x in imap_unordered_it:
- print('\t', x)
- print()
-
- print('Ordered results using pool.map() --- will block till complete:')
- for x in pool.map(calculatestar, TASKS):
- print('\t', x)
- print()
-
- #
- # Simple benchmarks
- #
-
- N = 100000
- print('def pow3(x): return x**3')
- t = time.time()
- A = list(map(pow3, range(N)))
- print('\tmap(pow3, range(%d)):\n\t\t%s seconds' % \
- (N, time.time() - t))
+ with multiprocessing.Pool(PROCESSES) as pool:
+ #
+ # Tests
+ #
- t = time.time()
- B = pool.map(pow3, range(N))
- print('\tpool.map(pow3, range(%d)):\n\t\t%s seconds' % \
- (N, time.time() - t))
+ TASKS = [(mul, (i, 7)) for i in range(10)] + \
+ [(plus, (i, 8)) for i in range(10)]
- t = time.time()
- C = list(pool.imap(pow3, range(N), chunksize=N//8))
- print('\tlist(pool.imap(pow3, range(%d), chunksize=%d)):\n\t\t%s' \
- ' seconds' % (N, N//8, time.time() - t))
+ results = [pool.apply_async(calculate, t) for t in TASKS]
+ imap_it = pool.imap(calculatestar, TASKS)
+ imap_unordered_it = pool.imap_unordered(calculatestar, TASKS)
- assert A == B == C, (len(A), len(B), len(C))
- print()
+ print('Ordered results using pool.apply_async():')
+ for r in results:
+ print('\t', r.get())
+ print()
- L = [None] * 1000000
- print('def noop(x): pass')
- print('L = [None] * 1000000')
+ print('Ordered results using pool.imap():')
+ for x in imap_it:
+ print('\t', x)
+ print()
- t = time.time()
- A = list(map(noop, L))
- print('\tmap(noop, L):\n\t\t%s seconds' % \
- (time.time() - t))
+ print('Unordered results using pool.imap_unordered():')
+ for x in imap_unordered_it:
+ print('\t', x)
+ print()
- t = time.time()
- B = pool.map(noop, L)
- print('\tpool.map(noop, L):\n\t\t%s seconds' % \
- (time.time() - t))
+ print('Ordered results using pool.map() --- will block till complete:')
+ for x in pool.map(calculatestar, TASKS):
+ print('\t', x)
+ print()
- t = time.time()
- C = list(pool.imap(noop, L, chunksize=len(L)//8))
- print('\tlist(pool.imap(noop, L, chunksize=%d)):\n\t\t%s seconds' % \
- (len(L)//8, time.time() - t))
+ #
+ # Test error handling
+ #
- assert A == B == C, (len(A), len(B), len(C))
- print()
+ print('Testing error handling:')
- del A, B, C, L
-
- #
- # Test error handling
- #
-
- print('Testing error handling:')
-
- try:
- print(pool.apply(f, (5,)))
- except ZeroDivisionError:
- print('\tGot ZeroDivisionError as expected from pool.apply()')
- else:
- raise AssertionError('expected ZeroDivisionError')
-
- try:
- print(pool.map(f, list(range(10))))
- except ZeroDivisionError:
- print('\tGot ZeroDivisionError as expected from pool.map()')
- else:
- raise AssertionError('expected ZeroDivisionError')
-
- try:
- print(list(pool.imap(f, list(range(10)))))
- except ZeroDivisionError:
- print('\tGot ZeroDivisionError as expected from list(pool.imap())')
- else:
- raise AssertionError('expected ZeroDivisionError')
-
- it = pool.imap(f, list(range(10)))
- for i in range(10):
try:
- x = next(it)
+ print(pool.apply(f, (5,)))
except ZeroDivisionError:
- if i == 5:
- pass
- except StopIteration:
- break
+ print('\tGot ZeroDivisionError as expected from pool.apply()')
else:
- if i == 5:
- raise AssertionError('expected ZeroDivisionError')
-
- assert i == 9
- print('\tGot ZeroDivisionError as expected from IMapIterator.next()')
- print()
+ raise AssertionError('expected ZeroDivisionError')
- #
- # Testing timeouts
- #
-
- print('Testing ApplyResult.get() with timeout:', end=' ')
- res = pool.apply_async(calculate, TASKS[0])
- while 1:
- sys.stdout.flush()
try:
- sys.stdout.write('\n\t%s' % res.get(0.02))
- break
- except multiprocessing.TimeoutError:
- sys.stdout.write('.')
- print()
- print()
+ print(pool.map(f, list(range(10))))
+ except ZeroDivisionError:
+ print('\tGot ZeroDivisionError as expected from pool.map()')
+ else:
+ raise AssertionError('expected ZeroDivisionError')
- print('Testing IMapIterator.next() with timeout:', end=' ')
- it = pool.imap(calculatestar, TASKS)
- while 1:
- sys.stdout.flush()
try:
- sys.stdout.write('\n\t%s' % it.next(0.02))
- except StopIteration:
- break
- except multiprocessing.TimeoutError:
- sys.stdout.write('.')
- print()
- print()
-
- #
- # Testing callback
- #
-
- print('Testing callback:')
-
- A = []
- B = [56, 0, 1, 8, 27, 64, 125, 216, 343, 512, 729]
-
- r = pool.apply_async(mul, (7, 8), callback=A.append)
- r.wait()
-
- r = pool.map_async(pow3, list(range(10)), callback=A.extend)
- r.wait()
-
- if A == B:
- print('\tcallbacks succeeded\n')
- else:
- print('\t*** callbacks failed\n\t\t%s != %s\n' % (A, B))
-
- #
- # Check there are no outstanding tasks
- #
-
- assert not pool._cache, 'cache = %r' % pool._cache
-
- #
- # Check close() methods
- #
-
- print('Testing close():')
-
- for worker in pool._pool:
- assert worker.is_alive()
-
- result = pool.apply_async(time.sleep, [0.5])
- pool.close()
- pool.join()
-
- assert result.get() is None
-
- for worker in pool._pool:
- assert not worker.is_alive()
-
- print('\tclose() succeeded\n')
-
- #
- # Check terminate() method
- #
-
- print('Testing terminate():')
-
- pool = multiprocessing.Pool(2)
- DELTA = 0.1
- ignore = pool.apply(pow3, [2])
- results = [pool.apply_async(time.sleep, [DELTA]) for i in range(100)]
- pool.terminate()
- pool.join()
-
- for worker in pool._pool:
- assert not worker.is_alive()
-
- print('\tterminate() succeeded\n')
-
- #
- # Check garbage collection
- #
-
- print('Testing garbage collection:')
-
- pool = multiprocessing.Pool(2)
- DELTA = 0.1
- processes = pool._pool
- ignore = pool.apply(pow3, [2])
- results = [pool.apply_async(time.sleep, [DELTA]) for i in range(100)]
-
- results = pool = None
-
- time.sleep(DELTA * 2)
-
- for worker in processes:
- assert not worker.is_alive()
-
- print('\tgarbage collection succeeded\n')
+ print(list(pool.imap(f, list(range(10)))))
+ except ZeroDivisionError:
+ print('\tGot ZeroDivisionError as expected from list(pool.imap())')
+ else:
+ raise AssertionError('expected ZeroDivisionError')
+
+ it = pool.imap(f, list(range(10)))
+ for i in range(10):
+ try:
+ x = next(it)
+ except ZeroDivisionError:
+ if i == 5:
+ pass
+ except StopIteration:
+ break
+ else:
+ if i == 5:
+ raise AssertionError('expected ZeroDivisionError')
+
+ assert i == 9
+ print('\tGot ZeroDivisionError as expected from IMapIterator.next()')
+ print()
+
+ #
+ # Testing timeouts
+ #
+
+ print('Testing ApplyResult.get() with timeout:', end=' ')
+ res = pool.apply_async(calculate, TASKS[0])
+ while 1:
+ sys.stdout.flush()
+ try:
+ sys.stdout.write('\n\t%s' % res.get(0.02))
+ break
+ except multiprocessing.TimeoutError:
+ sys.stdout.write('.')
+ print()
+ print()
+
+ print('Testing IMapIterator.next() with timeout:', end=' ')
+ it = pool.imap(calculatestar, TASKS)
+ while 1:
+ sys.stdout.flush()
+ try:
+ sys.stdout.write('\n\t%s' % it.next(0.02))
+ except StopIteration:
+ break
+ except multiprocessing.TimeoutError:
+ sys.stdout.write('.')
+ print()
+ print()
if __name__ == '__main__':
multiprocessing.freeze_support()
-
- assert len(sys.argv) in (1, 2)
-
- if len(sys.argv) == 1 or sys.argv[1] == 'processes':
- print(' Using processes '.center(79, '-'))
- elif sys.argv[1] == 'threads':
- print(' Using threads '.center(79, '-'))
- import multiprocessing.dummy as multiprocessing
- else:
- print('Usage:\n\t%s [processes | threads]' % sys.argv[0])
- raise SystemExit(2)
-
test()
diff --git a/Doc/includes/mp_synchronize.py b/Doc/includes/mp_synchronize.py
deleted file mode 100644
index 81dbc38..0000000
--- a/Doc/includes/mp_synchronize.py
+++ /dev/null
@@ -1,278 +0,0 @@
-#
-# A test file for the `multiprocessing` package
-#
-# Copyright (c) 2006-2008, R Oudkerk
-# All rights reserved.
-#
-
-import time
-import sys
-import random
-from queue import Empty
-
-import multiprocessing # may get overwritten
-
-
-#### TEST_VALUE
-
-def value_func(running, mutex):
- random.seed()
- time.sleep(random.random()*4)
-
- mutex.acquire()
- print('\n\t\t\t' + str(multiprocessing.current_process()) + ' has finished')
- running.value -= 1
- mutex.release()
-
-def test_value():
- TASKS = 10
- running = multiprocessing.Value('i', TASKS)
- mutex = multiprocessing.Lock()
-
- for i in range(TASKS):
- p = multiprocessing.Process(target=value_func, args=(running, mutex))
- p.start()
-
- while running.value > 0:
- time.sleep(0.08)
- mutex.acquire()
- print(running.value, end=' ')
- sys.stdout.flush()
- mutex.release()
-
- print()
- print('No more running processes')
-
-
-#### TEST_QUEUE
-
-def queue_func(queue):
- for i in range(30):
- time.sleep(0.5 * random.random())
- queue.put(i*i)
- queue.put('STOP')
-
-def test_queue():
- q = multiprocessing.Queue()
-
- p = multiprocessing.Process(target=queue_func, args=(q,))
- p.start()
-
- o = None
- while o != 'STOP':
- try:
- o = q.get(timeout=0.3)
- print(o, end=' ')
- sys.stdout.flush()
- except Empty:
- print('TIMEOUT')
-
- print()
-
-
-#### TEST_CONDITION
-
-def condition_func(cond):
- cond.acquire()
- print('\t' + str(cond))
- time.sleep(2)
- print('\tchild is notifying')
- print('\t' + str(cond))
- cond.notify()
- cond.release()
-
-def test_condition():
- cond = multiprocessing.Condition()
-
- p = multiprocessing.Process(target=condition_func, args=(cond,))
- print(cond)
-
- cond.acquire()
- print(cond)
- cond.acquire()
- print(cond)
-
- p.start()
-
- print('main is waiting')
- cond.wait()
- print('main has woken up')
-
- print(cond)
- cond.release()
- print(cond)
- cond.release()
-
- p.join()
- print(cond)
-
-
-#### TEST_SEMAPHORE
-
-def semaphore_func(sema, mutex, running):
- sema.acquire()
-
- mutex.acquire()
- running.value += 1
- print(running.value, 'tasks are running')
- mutex.release()
-
- random.seed()
- time.sleep(random.random()*2)
-
- mutex.acquire()
- running.value -= 1
- print('%s has finished' % multiprocessing.current_process())
- mutex.release()
-
- sema.release()
-
-def test_semaphore():
- sema = multiprocessing.Semaphore(3)
- mutex = multiprocessing.RLock()
- running = multiprocessing.Value('i', 0)
-
- processes = [
- multiprocessing.Process(target=semaphore_func,
- args=(sema, mutex, running))
- for i in range(10)
- ]
-
- for p in processes:
- p.start()
-
- for p in processes:
- p.join()
-
-
-#### TEST_JOIN_TIMEOUT
-
-def join_timeout_func():
- print('\tchild sleeping')
- time.sleep(5.5)
- print('\n\tchild terminating')
-
-def test_join_timeout():
- p = multiprocessing.Process(target=join_timeout_func)
- p.start()
-
- print('waiting for process to finish')
-
- while 1:
- p.join(timeout=1)
- if not p.is_alive():
- break
- print('.', end=' ')
- sys.stdout.flush()
-
-
-#### TEST_EVENT
-
-def event_func(event):
- print('\t%r is waiting' % multiprocessing.current_process())
- event.wait()
- print('\t%r has woken up' % multiprocessing.current_process())
-
-def test_event():
- event = multiprocessing.Event()
-
- processes = [multiprocessing.Process(target=event_func, args=(event,))
- for i in range(5)]
-
- for p in processes:
- p.start()
-
- print('main is sleeping')
- time.sleep(2)
-
- print('main is setting event')
- event.set()
-
- for p in processes:
- p.join()
-
-
-#### TEST_SHAREDVALUES
-
-def sharedvalues_func(values, arrays, shared_values, shared_arrays):
- for i in range(len(values)):
- v = values[i][1]
- sv = shared_values[i].value
- assert v == sv
-
- for i in range(len(values)):
- a = arrays[i][1]
- sa = list(shared_arrays[i][:])
- assert a == sa
-
- print('Tests passed')
-
-def test_sharedvalues():
- values = [
- ('i', 10),
- ('h', -2),
- ('d', 1.25)
- ]
- arrays = [
- ('i', list(range(100))),
- ('d', [0.25 * i for i in range(100)]),
- ('H', list(range(1000)))
- ]
-
- shared_values = [multiprocessing.Value(id, v) for id, v in values]
- shared_arrays = [multiprocessing.Array(id, a) for id, a in arrays]
-
- p = multiprocessing.Process(
- target=sharedvalues_func,
- args=(values, arrays, shared_values, shared_arrays)
- )
- p.start()
- p.join()
-
- assert p.exitcode == 0
-
-
-####
-
-def test(namespace=multiprocessing):
- global multiprocessing
-
- multiprocessing = namespace
-
- for func in [test_value, test_queue, test_condition,
- test_semaphore, test_join_timeout, test_event,
- test_sharedvalues]:
-
- print('\n\t######## %s\n' % func.__name__)
- func()
-
- ignore = multiprocessing.active_children() # cleanup any old processes
- if hasattr(multiprocessing, '_debug_info'):
- info = multiprocessing._debug_info()
- if info:
- print(info)
- raise ValueError('there should be no positive refcounts left')
-
-
-if __name__ == '__main__':
- multiprocessing.freeze_support()
-
- assert len(sys.argv) in (1, 2)
-
- if len(sys.argv) == 1 or sys.argv[1] == 'processes':
- print(' Using processes '.center(79, '-'))
- namespace = multiprocessing
- elif sys.argv[1] == 'manager':
- print(' Using processes and a manager '.center(79, '-'))
- namespace = multiprocessing.Manager()
- namespace.Process = multiprocessing.Process
- namespace.current_process = multiprocessing.current_process
- namespace.active_children = multiprocessing.active_children
- elif sys.argv[1] == 'threads':
- print(' Using threads '.center(79, '-'))
- import multiprocessing.dummy as namespace
- else:
- print('Usage:\n\t%s [processes | manager | threads]' % sys.argv[0])
- raise SystemExit(2)
-
- test(namespace)
diff --git a/Doc/includes/mp_webserver.py b/Doc/includes/mp_webserver.py
deleted file mode 100644
index 651024d..0000000
--- a/Doc/includes/mp_webserver.py
+++ /dev/null
@@ -1,70 +0,0 @@
-#
-# Example where a pool of http servers share a single listening socket
-#
-# On Windows this module depends on the ability to pickle a socket
-# object so that the worker processes can inherit a copy of the server
-# object. (We import `multiprocessing.reduction` to enable this pickling.)
-#
-# Not sure if we should synchronize access to `socket.accept()` method by
-# using a process-shared lock -- does not seem to be necessary.
-#
-# Copyright (c) 2006-2008, R Oudkerk
-# All rights reserved.
-#
-
-import os
-import sys
-
-from multiprocessing import Process, current_process, freeze_support
-from http.server import HTTPServer
-from http.server import SimpleHTTPRequestHandler
-
-if sys.platform == 'win32':
- import multiprocessing.reduction # make sockets pickable/inheritable
-
-
-def note(format, *args):
- sys.stderr.write('[%s]\t%s\n' % (current_process().name, format % args))
-
-
-class RequestHandler(SimpleHTTPRequestHandler):
- # we override log_message() to show which process is handling the request
- def log_message(self, format, *args):
- note(format, *args)
-
-def serve_forever(server):
- note('starting server')
- try:
- server.serve_forever()
- except KeyboardInterrupt:
- pass
-
-
-def runpool(address, number_of_processes):
- # create a single server object -- children will each inherit a copy
- server = HTTPServer(address, RequestHandler)
-
- # create child processes to act as workers
- for i in range(number_of_processes - 1):
- Process(target=serve_forever, args=(server,)).start()
-
- # main process also acts as a worker
- serve_forever(server)
-
-
-def test():
- DIR = os.path.join(os.path.dirname(__file__), '..')
- ADDRESS = ('localhost', 8000)
- NUMBER_OF_PROCESSES = 4
-
- print('Serving at http://%s:%d using %d worker processes' % \
- (ADDRESS[0], ADDRESS[1], NUMBER_OF_PROCESSES))
- print('To exit press Ctrl-' + ['C', 'Break'][sys.platform=='win32'])
-
- os.chdir(DIR)
- runpool(ADDRESS, NUMBER_OF_PROCESSES)
-
-
-if __name__ == '__main__':
- freeze_support()
- test()
diff --git a/Doc/includes/mp_workers.py b/Doc/includes/mp_workers.py
index e66d97b..3b92269 100644
--- a/Doc/includes/mp_workers.py
+++ b/Doc/includes/mp_workers.py
@@ -1,16 +1,3 @@
-#
-# Simple example which uses a pool of workers to carry out some tasks.
-#
-# Notice that the results will probably not come out of the output
-# queue in the same in the same order as the corresponding tasks were
-# put on the input queue. If it is important to get the results back
-# in the original order then consider using `Pool.map()` or
-# `Pool.imap()` (which will save on the amount of code needed anyway).
-#
-# Copyright (c) 2006-2008, R Oudkerk
-# All rights reserved.
-#
-
import time
import random
diff --git a/Doc/includes/typestruct.h b/Doc/includes/typestruct.h
index 32647c0..fcb846a 100644
--- a/Doc/includes/typestruct.h
+++ b/Doc/includes/typestruct.h
@@ -70,4 +70,11 @@ typedef struct _typeobject {
PyObject *tp_subclasses;
PyObject *tp_weaklist;
+ destructor tp_del;
+
+ /* Type attribute cache version tag. Added in version 2.6 */
+ unsigned int tp_version_tag;
+
+ destructor tp_finalize;
+
} PyTypeObject;
diff --git a/Doc/install/index.rst b/Doc/install/index.rst
index 0c9a9f2..d0fea3d 100644
--- a/Doc/install/index.rst
+++ b/Doc/install/index.rst
@@ -2,9 +2,9 @@
.. _install-index:
-*****************************
- Installing Python Modules
-*****************************
+********************************************
+ Installing Python Modules (Legacy version)
+********************************************
:Author: Greg Ward
diff --git a/Doc/installing/index.rst b/Doc/installing/index.rst
new file mode 100644
index 0000000..1284613
--- /dev/null
+++ b/Doc/installing/index.rst
@@ -0,0 +1,210 @@
+.. highlightlang:: none
+
+.. _installing-index:
+
+*****************************
+ Installing Python Modules
+*****************************
+
+:Email: distutils-sig@python.org
+
+As a popular open source development project, Python has an active
+supporting community of contributors and users that also make their software
+available for other Python developers to use under open source license terms.
+
+This allows Python users to share and collaborate effectively, benefiting
+from the solutions others have already created to common (and sometimes
+even rare!) problems, as well as potentially contributing their own
+solutions to the common pool.
+
+This guide covers the installation part of the process. For a guide to
+creating and sharing your own Python projects, refer to the
+:ref:`distribution guide <distributing-index>`.
+
+.. note::
+
+ For corporate and other institutional users, be aware that many
+ organisations have their own policies around using and contributing to
+ open source software. Please take such policies into account when making
+ use of the distribution and installation tools provided with Python.
+
+
+Key terms
+=========
+
+* ``pip`` is the preferred installer program. Starting with Python 3.4, it
+ is included by default with the Python binary installers.
+* a virtual environment is a semi-isolated Python environment that allows
+ packages to be installed for use by a particular application, rather than
+ being installed system wide
+* ``pyvenv`` is the standard tool for creating virtual environments, and has
+ been part of Python since Python 3.3. Starting with Python 3.4, it
+ defaults to installing ``pip`` into all created virtual environments
+* the `Python Package Index <https://pypi.python.org/pypi>`__ is a public
+ repository of open source licensed packages made available for use by
+ other Python users
+* the `Python Packaging Authority
+ <http://packaging.python.org/en/latest/future.html>`__ are the group of
+ developers and documentation authors responsible for the maintenance and
+ evolution of the standard packaging tools and the associated metadata and
+ file format standards. They maintain a variety of tools, documentation
+ and issue trackers on both `GitHub <https://github.com/pypa>`__ and
+ `BitBucket <https://bitbucket.org/pypa/>`__.
+* ``distutils`` is the original build and distribution system first added to
+ the Python standard library in 1998. While direct use of ``distutils`` is
+ being phased out, it still laid the foundation for the current packaging
+ and distribution infrastructure, and it not only remains part of the
+ standard library, but its name lives on in other ways (such as the name
+ of the mailing list used to coordinate Python packaging standards
+ development).
+
+
+Basic usage
+===========
+
+The standard packaging tools are all designed to be used from the command
+line. For Windows users, the examples below assume that the option to
+adjust the system PATH environment variable was selected when installing
+Python. For Linux users, the command to install into the system version of
+Python 3 is likely to be ``pip3`` rather than ``pip``.
+
+The following command will install the latest version of a module and its
+dependencies from the Python Package Index::
+
+ pip install SomePackage
+
+It's also possible to specify an exact or minimum version directly on the
+command line::
+
+ pip install SomePackage==1.0.4 # specific version
+ pip install 'SomePackage>=1.0.4' # minimum version
+
+Normally, if a suitable module is already installed, attempting to install
+it again will have no effect. Upgrading existing modules must be requested
+explicitly::
+
+ pip install --upgrade SomePackage
+
+More information and resources regarding ``pip`` and its capabilities can be
+found in the `Python Packaging User Guide <http://packaging.python.org>`__.
+
+``pyvenv`` has its own documentation at :ref:`scripts-pyvenv`. Installing
+into an active virtual environment uses the commands shown above.
+
+.. seealso::
+
+ `Python Packaging User Guide: Installing Python packages
+ <http://packaging.python.org/en/latest/tutorial.html#installing-python-packages>`__
+
+
+How do I ...?
+=============
+
+These are quick answers or links for some common tasks.
+
+... install ``pip`` in versions of Python prior to Python 3.4?
+--------------------------------------------------------------
+
+Python only started bundling ``pip`` with Python 3.4. For earlier versions,
+``pip`` needs to be "bootstrapped" as described in the Python Packaging
+User Guide.
+
+.. seealso::
+
+ `Python Packaging User Guide: Installing the Tools
+ <http://packaging.python.org/en/latest/tutorial.html#installing-the-tools>`__
+
+
+.. installing-per-user-installation:
+
+... install packages just for the current user?
+-----------------------------------------------
+
+Passing the ``--user`` option to ``pip install`` will install a package
+just for the current user, rather than for all users of the system.
+
+
+... install scientific Python packages?
+---------------------------------------
+
+A number of scientific Python packages have complex binary dependencies, and
+aren't currently easy to install using ``pip`` directly. At this point in
+time, it will often be easier for users to install these packages by
+`other means
+<http://packaging.python.org/en/latest/platforms.html#installing-scientific-packages>`__
+rather than attempting to install them with ``pip``.
+
+.. seealso::
+
+ `Python Packaging User Guide: Installing Scientific Packages
+ <http://packaging.python.org/en/latest/platforms.html#installing-scientific-packages>`__
+
+
+... work with multiple versions of Python installed in parallel?
+----------------------------------------------------------------
+
+On Linux, Mac OS X and other POSIX systems, use the versioned Python commands
+in combination with the ``-m`` switch to run the appropriate copy of
+``pip``::
+
+ python2 -m pip install SomePackage # default Python 2
+ python2.7 -m pip install SomePackage # specifically Python 2.7
+ python3 -m pip install SomePackage # default Python 3
+ python3.4 -m pip install SomePackage # specifically Python 3.4
+
+(appropriately versioned ``pip`` commands may also be available)
+
+On Windows, use the ``py`` Python launcher in combination with the ``-m``
+switch::
+
+ py -2 -m pip install SomePackage # default Python 2
+ py -2.7 -m pip install SomePackage # specifically Python 2.7
+ py -3 -m pip install SomePackage # default Python 3
+ py -3.4 -m pip install SomePackage # specifically Python 3.4
+
+.. other questions:
+
+ Once the Development & Deployment part of PPUG is fleshed out, some of
+ those sections should be linked from new questions here (most notably,
+ we should have a question about avoiding depending on PyPI that links to
+ http://packaging.python.org/en/latest/deployment.html#pypi-mirrors-and-caches)
+
+
+Common installation issues
+==========================
+
+Installing into the system Python on Linux
+------------------------------------------
+
+On Linux systems, a Python installation will typically be included as part
+of the distribution. Installing into this Python installation requires
+root access to the system, and may interfere with the operation of the
+system package manager and other components of the system if a component
+is unexpectedly upgraded using ``pip``.
+
+On such systems, it is often better to use a virtual environment or a
+per-user installation when installing packages with ``pip``.
+
+
+Installing binary extensions
+----------------------------
+
+Python has typically relied heavily on source based distribution, with end
+users being expected to compile extension modules from source as part of
+the installation process.
+
+With the introduction of support for the binary ``wheel`` format, and the
+ability to publish wheels for at least Windows and Mac OS X through the
+Python Package Index, this problem is expected to diminish over time,
+as users are more regularly able to install pre-built extensions rather
+than needing to build them themselves.
+
+Some of the solutions for installing `scientific software
+<http://packaging.python.org/en/latest/platforms.html#installing-scientific-packages>`__
+that is not yet available as pre-built ``wheel`` files may also help with
+obtaining other binary extensions without needing to build them locally.
+
+.. seealso::
+
+ `Python Packaging User Guide: Binary Extensions
+ <http://packaging.python.org/en/latest/extensions.html>`__
diff --git a/Doc/library/2to3.rst b/Doc/library/2to3.rst
index 1e5f42d..1092e6b 100644
--- a/Doc/library/2to3.rst
+++ b/Doc/library/2to3.rst
@@ -142,6 +142,39 @@ and off individually. They are described here in more detail.
Removes usage of :func:`apply`. For example ``apply(function, *args,
**kwargs)`` is converted to ``function(*args, **kwargs)``.
+.. 2to3fixer:: asserts
+
+ Replaces deprecated :mod:`unittest` method names with the correct ones.
+
+ ================================ ==========================================
+ From To
+ ================================ ==========================================
+ ``failUnlessEqual(a, b)`` :meth:`assertEqual(a, b)
+ <unittest.TestCase.assertEqual>`
+ ``assertEquals(a, b)`` :meth:`assertEqual(a, b)
+ <unittest.TestCase.assertEqual>`
+ ``failIfEqual(a, b)`` :meth:`assertNotEqual(a, b)
+ <unittest.TestCase.assertNotEqual>`
+ ``assertNotEquals(a, b)`` :meth:`assertNotEqual(a, b)
+ <unittest.TestCase.assertNotEqual>`
+ ``failUnless(a)`` :meth:`assertTrue(a)
+ <unittest.TestCase.assertTrue>`
+ ``assert_(a)`` :meth:`assertTrue(a)
+ <unittest.TestCase.assertTrue>`
+ ``failIf(a)`` :meth:`assertFalse(a)
+ <unittest.TestCase.assertFalse>`
+ ``failUnlessRaises(exc, cal)`` :meth:`assertRaises(exc, cal)
+ <unittest.TestCase.assertRaises>`
+ ``failUnlessAlmostEqual(a, b)`` :meth:`assertAlmostEqual(a, b)
+ <unittest.TestCase.assertAlmostEqual>`
+ ``assertAlmostEquals(a, b)`` :meth:`assertAlmostEqual(a, b)
+ <unittest.TestCase.assertAlmostEqual>`
+ ``failIfAlmostEqual(a, b)`` :meth:`assertNotAlmostEqual(a, b)
+ <unittest.TestCase.assertNotAlmostEqual>`
+ ``assertNotAlmostEquals(a, b)`` :meth:`assertNotAlmostEqual(a, b)
+ <unittest.TestCase.assertNotAlmostEqual>`
+ ================================ ==========================================
+
.. 2to3fixer:: basestring
Converts :class:`basestring` to :class:`str`.
@@ -342,6 +375,10 @@ and off individually. They are described here in more detail.
Handles the move of :func:`reduce` to :func:`functools.reduce`.
+.. 2to3fixer:: reload
+
+ Converts :func:`reload` to :func:`imp.reload`.
+
.. 2to3fixer:: renames
Changes :data:`sys.maxint` to :data:`sys.maxsize`.
diff --git a/Doc/library/__main__.rst b/Doc/library/__main__.rst
index a1d3c24..fcbe53d 100644
--- a/Doc/library/__main__.rst
+++ b/Doc/library/__main__.rst
@@ -5,13 +5,19 @@
.. module:: __main__
:synopsis: The environment where the top-level script is run.
+``'__main__'`` is the name of the scope in which top-level code executes.
+A module's __name__ is set equal to ``'__main__'`` when read from
+standard input, a script, or from an interactive prompt.
-This module represents the (otherwise anonymous) scope in which the
-interpreter's main program executes --- commands read either from standard
-input, from a script file, or from an interactive prompt. It is this
-environment in which the idiomatic "conditional script" stanza causes a script
-to run::
+A module can discover whether or not it is running in the main scope by
+checking its own ``__name__``, which allows a common idiom for conditionally
+executing code in a module when it is run as a script or with ``python
+-m`` but not when it is imported:
if __name__ == "__main__":
+ # execute only if run as a script
main()
+For a package, the same effect can be achieved by including a
+``__main__.py`` module, the contents of which will be executed when the
+module is run with ``-m``.
diff --git a/Doc/library/abc.rst b/Doc/library/abc.rst
index 9299124..7a73704 100644
--- a/Doc/library/abc.rst
+++ b/Doc/library/abc.rst
@@ -12,9 +12,9 @@
--------------
This module provides the infrastructure for defining :term:`abstract base
-classes <abstract base class>` (ABCs) in Python, as outlined in :pep:`3119`; see the PEP for why this
-was added to Python. (See also :pep:`3141` and the :mod:`numbers` module
-regarding a type hierarchy for numbers based on ABCs.)
+classes <abstract base class>` (ABCs) in Python, as outlined in :pep:`3119`;
+see the PEP for why this was added to Python. (See also :pep:`3141` and the
+:mod:`numbers` module 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
@@ -23,7 +23,7 @@ a class or instance provides a particular interface, for example, is it
hashable or a mapping.
-This module provides the following class:
+This module provides the following classes:
.. class:: ABCMeta
@@ -58,6 +58,10 @@ This module provides the following class:
.. versionchanged:: 3.3
Returns the registered subclass, to allow usage as a class decorator.
+ .. versionchanged:: 3.4
+ To detect calls to :meth:`register`, you can use the
+ :func:`get_cache_token` function.
+
You can also override this method in an abstract base class:
.. method:: __subclasshook__(subclass)
@@ -127,6 +131,19 @@ This module provides the following class:
available as a method of ``Foo``, so it is provided separately.
+.. class:: ABC
+
+ A helper class that has :class:`ABCMeta` as its metaclass. With this class,
+ an abstract base class can be created by simply deriving from :class:`ABC`,
+ avoiding sometimes confusing metaclass usage.
+
+ Note that the type of :class:`ABC` is still :class:`ABCMeta`, therefore
+ inheriting from :class:`ABC` requires the usual precautions regarding metaclass
+ usage, as multiple inheritance may lead to metaclass conflicts.
+
+ .. versionadded:: 3.4
+
+
The :mod:`abc` module also provides the following decorators:
.. decorator:: abstractmethod
@@ -295,6 +312,19 @@ The :mod:`abc` module also provides the following decorators:
:func:`abstractmethod`, making this decorator redundant.
+The :mod:`abc` module also provides the following functions:
+
+.. function:: get_cache_token()
+
+ Returns the current abstract base class cache token.
+
+ The token is an opaque object (that supports equality testing) identifying
+ the current version of the abstract base class cache for virtual subclasses.
+ The token changes with every call to :meth:`ABCMeta.register` on any ABC.
+
+ .. versionadded:: 3.4
+
+
.. rubric:: Footnotes
.. [#] C++ programmers should note that Python's virtual base class
diff --git a/Doc/library/aifc.rst b/Doc/library/aifc.rst
index 8a3541b..6fbcf28 100644
--- a/Doc/library/aifc.rst
+++ b/Doc/library/aifc.rst
@@ -30,8 +30,8 @@ sampling rate or frame rate is the number of times per second the sound is
sampled. The number of channels indicate if the audio is mono, stereo, or
quadro. Each frame consists of one sample per channel. The sample size is the
size in bytes of each sample. Thus a frame consists of
-*nchannels*\*\ *samplesize* bytes, and a second's worth of audio consists of
-*nchannels*\*\ *samplesize*\*\ *framerate* bytes.
+``nchannels * samplesize`` bytes, and a second's worth of audio consists of
+``nchannels * samplesize * framerate`` bytes.
For example, CD quality audio has a sample size of two bytes (16 bits), uses two
channels (stereo) and has a frame rate of 44,100 frames/second. This gives a
@@ -51,6 +51,11 @@ Module :mod:`aifc` defines the following function:
used for writing, the file object should be seekable, unless you know ahead of
time how many samples you are going to write in total and use
:meth:`writeframesraw` and :meth:`setnframes`.
+ The :func:`.open` function may be used in a :keyword:`with` statement. When
+ the :keyword:`with` block completes, the :meth:`~aifc.close` method is called.
+
+ .. versionchanged:: 3.4
+ Support for the :keyword:`with` statement was added.
Objects returned by :func:`.open` when a file is opened for reading have the
following methods:
@@ -92,7 +97,9 @@ following methods:
.. method:: aifc.getparams()
- Return a tuple consisting of all of the above values in the above order.
+ Returns a :func:`~collections.namedtuple` ``(nchannels, sampwidth,
+ framerate, nframes, comptype, compname)``, equivalent to output of the
+ :meth:`get\*` methods.
.. method:: aifc.getmarkers()
@@ -218,12 +225,18 @@ number of frames must be filled in.
Write data to the output file. This method can only be called after the audio
file parameters have been set.
+ .. versionchanged:: 3.4
+ Any :term:`bytes-like object` is now accepted.
+
.. method:: aifc.writeframesraw(data)
Like :meth:`writeframes`, except that the header of the audio file is not
updated.
+ .. versionchanged:: 3.4
+ Any :term:`bytes-like object` is now accepted.
+
.. method:: aifc.close()
diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst
index b686c44..b1b5135 100644
--- a/Doc/library/argparse.rst
+++ b/Doc/library/argparse.rst
@@ -977,9 +977,9 @@ See the section on the default_ keyword argument for information on when the
``type`` argument is applied to default arguments.
To ease the use of various types of files, the argparse module provides the
-factory FileType which takes the ``mode=`` and ``bufsize=`` arguments of the
-:func:`open` function. For example, ``FileType('w')`` can be used to create a
-writable file::
+factory FileType which takes the ``mode=``, ``bufsize=``, ``encoding=`` and
+``errors=`` arguments of the :func:`open` function. For example,
+``FileType('w')`` can be used to create a writable file::
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('bar', type=argparse.FileType('w'))
@@ -1648,17 +1648,19 @@ Sub-commands
FileType objects
^^^^^^^^^^^^^^^^
-.. class:: FileType(mode='r', bufsize=None)
+.. class:: FileType(mode='r', bufsize=-1, encoding=None, errors=None)
The :class:`FileType` factory creates objects that can be passed to the type
argument of :meth:`ArgumentParser.add_argument`. Arguments that have
- :class:`FileType` objects as their type will open command-line arguments as files
- with the requested modes and buffer sizes::
+ :class:`FileType` objects as their type will open command-line arguments as
+ files with the requested modes, buffer sizes, encodings and error handling
+ (see the :func:`open` function for more details)::
>>> parser = argparse.ArgumentParser()
- >>> parser.add_argument('--output', type=argparse.FileType('wb', 0))
- >>> parser.parse_args(['--output', 'out'])
- Namespace(output=<_io.BufferedWriter name='out'>)
+ >>> parser.add_argument('--raw', type=argparse.FileType('wb', 0))
+ >>> parser.add_argument('out', type=argparse.FileType('w', encoding='UTF-8'))
+ >>> parser.parse_args(['--raw', 'raw.dat', 'file.txt'])
+ Namespace(out=<_io.TextIOWrapper name='file.txt' mode='w' encoding='UTF-8'>, raw=<_io.FileIO name='raw.dat' mode='wb'>)
FileType objects understand the pseudo-argument ``'-'`` and automatically
convert this into ``sys.stdin`` for readable :class:`FileType` objects and
@@ -1669,6 +1671,9 @@ FileType objects
>>> parser.parse_args(['-'])
Namespace(infile=<_io.TextIOWrapper name='<stdin>' encoding='UTF-8'>)
+ .. versionadded:: 3.4
+ The *encodings* and *errors* keyword arguments.
+
Argument groups
^^^^^^^^^^^^^^^
diff --git a/Doc/library/asynchat.rst b/Doc/library/asynchat.rst
index adbd3be..c6fa061 100644
--- a/Doc/library/asynchat.rst
+++ b/Doc/library/asynchat.rst
@@ -10,6 +10,11 @@
--------------
+.. note::
+
+ This module exists for backwards compatibility only. For new code we
+ recommend using :mod:`asyncio`.
+
This module builds on the :mod:`asyncore` infrastructure, simplifying
asynchronous clients and servers and making it easier to handle protocols
whose elements are terminated by arbitrary strings, or are of variable length.
diff --git a/Doc/library/asyncio-dev.rst b/Doc/library/asyncio-dev.rst
new file mode 100644
index 0000000..9d6f054
--- /dev/null
+++ b/Doc/library/asyncio-dev.rst
@@ -0,0 +1,251 @@
+.. currentmodule:: asyncio
+
+.. _asyncio-dev:
+
+Develop with asyncio
+====================
+
+Asynchronous programming is different than classical "sequential" programming.
+This page lists common traps and explains how to avoid them.
+
+
+.. _asyncio-multithreading:
+
+Concurrency and multithreading
+------------------------------
+
+An event loop runs in a thread and executes all callbacks and tasks in the same
+thread. While a task is running in the event loop, no other task is running in
+the same thread. But when the task uses ``yield from``, the task is suspended
+and the event loop executes the next task.
+
+To schedule a callback from a different thread, the
+:meth:`BaseEventLoop.call_soon_threadsafe` method should be used. Example to
+schedule a coroutine from a different thread::
+
+ loop.call_soon_threadsafe(asyncio.async, coro_func())
+
+Most asyncio objects are not thread safe. You should only worry if you access
+objects outside the event loop. For example, to cancel a future, don't call
+directly its :meth:`Future.cancel` method, but::
+
+ loop.call_soon_threadsafe(fut.cancel)
+
+To handle signals and to execute subprocesses, the event loop must be run in
+the main thread.
+
+The :meth:`BaseEventLoop.run_in_executor` method can be used with a thread pool
+executor to execute a callback in different thread to not block the thread of
+the event loop.
+
+.. seealso::
+
+ See the :ref:`Synchronization primitives <asyncio-sync>` section to
+ synchronize tasks.
+
+
+.. _asyncio-handle-blocking:
+
+Handle blocking functions correctly
+-----------------------------------
+
+Blocking functions should not be called directly. For example, if a function
+blocks for 1 second, other tasks are delayed by 1 second which can have an
+important impact on reactivity.
+
+For networking and subprocesses, the :mod:`asyncio` module provides high-level
+APIs like :ref:`protocols <asyncio-protocol>`.
+
+An executor can be used to run a task in a different thread or even in a
+different process, to not block the thread of the event loop. See the
+:meth:`BaseEventLoop.run_in_executor` method.
+
+.. seealso::
+
+ The :ref:`Delayed calls <asyncio-delayed-calls>` section details how the
+ event loop handles time.
+
+
+.. _asyncio-logger:
+
+Logging
+-------
+
+The :mod:`asyncio` module logs information with the :mod:`logging` module in
+the logger ``'asyncio'``.
+
+
+.. _asyncio-coroutine-not-scheduled:
+
+Detect coroutine objects never scheduled
+----------------------------------------
+
+When a coroutine function is called but not passed to :func:`async` or to the
+:class:`Task` constructor, it is not scheduled and it is probably a bug.
+
+To detect such bug, set the environment variable :envvar:`PYTHONASYNCIODEBUG`
+to ``1``. When the coroutine object is destroyed by the garbage collector, a
+log will be emitted with the traceback where the coroutine function was called.
+See the :ref:`asyncio logger <asyncio-logger>`.
+
+The debug flag changes the behaviour of the :func:`coroutine` decorator. The
+debug flag value is only used when then coroutine function is defined, not when
+it is called. Coroutine functions defined before the debug flag is set to
+``True`` will not be tracked. For example, it is not possible to debug
+coroutines defined in the :mod:`asyncio` module, because the module must be
+imported before the flag value can be changed.
+
+Example with the bug::
+
+ import asyncio
+
+ @asyncio.coroutine
+ def test():
+ print("never scheduled")
+
+ test()
+
+Output in debug mode::
+
+ Coroutine 'test' defined at test.py:4 was never yielded from
+
+The fix is to call the :func:`async` function or create a :class:`Task` object
+with this coroutine object.
+
+
+Detect exceptions not consumed
+------------------------------
+
+Python usually calls :func:`sys.displayhook` on unhandled exceptions. If
+:meth:`Future.set_exception` is called, but the exception is not consumed,
+:func:`sys.displayhook` is not called. Instead, a log is emitted when the
+future is deleted by the garbage collector, with the traceback where the
+exception was raised. See the :ref:`asyncio logger <asyncio-logger>`.
+
+Example of unhandled exception::
+
+ import asyncio
+
+ @asyncio.coroutine
+ def bug():
+ raise Exception("not consumed")
+
+ loop = asyncio.get_event_loop()
+ asyncio.async(bug())
+ loop.run_forever()
+
+Output::
+
+ Future/Task exception was never retrieved:
+ Traceback (most recent call last):
+ File "/usr/lib/python3.4/asyncio/tasks.py", line 279, in _step
+ result = next(coro)
+ File "/usr/lib/python3.4/asyncio/tasks.py", line 80, in coro
+ res = func(*args, **kw)
+ File "test.py", line 5, in bug
+ raise Exception("not consumed")
+ Exception: not consumed
+
+There are different options to fix this issue. The first option is to chain to
+coroutine in another coroutine and use classic try/except::
+
+ @asyncio.coroutine
+ def handle_exception():
+ try:
+ yield from bug()
+ except Exception:
+ print("exception consumed")
+
+ loop = asyncio.get_event_loop()
+ asyncio.async(handle_exception())
+ loop.run_forever()
+
+Another option is to use the :meth:`BaseEventLoop.run_until_complete`
+function::
+
+ task = asyncio.async(bug())
+ try:
+ loop.run_until_complete(task)
+ except Exception:
+ print("exception consumed")
+
+See also the :meth:`Future.exception` method.
+
+
+Chain coroutines correctly
+--------------------------
+
+When a coroutine function calls other coroutine functions and tasks, they
+should be chained explicitly with ``yield from``. Otherwise, the execution is
+not guaranteed to be sequential.
+
+Example with different bugs using :func:`asyncio.sleep` to simulate slow
+operations::
+
+ import asyncio
+
+ @asyncio.coroutine
+ def create():
+ yield from asyncio.sleep(3.0)
+ print("(1) create file")
+
+ @asyncio.coroutine
+ def write():
+ yield from asyncio.sleep(1.0)
+ print("(2) write into file")
+
+ @asyncio.coroutine
+ def close():
+ print("(3) close file")
+
+ @asyncio.coroutine
+ def test():
+ asyncio.async(create())
+ asyncio.async(write())
+ asyncio.async(close())
+ yield from asyncio.sleep(2.0)
+ loop.stop()
+
+ loop = asyncio.get_event_loop()
+ asyncio.async(test())
+ loop.run_forever()
+ print("Pending tasks at exit: %s" % asyncio.Task.all_tasks(loop))
+ loop.close()
+
+Expected output::
+
+ (1) create file
+ (2) write into file
+ (3) close file
+ Pending tasks at exit: set()
+
+Actual output::
+
+ (3) close file
+ (2) write into file
+ Pending tasks at exit: {Task(<create>)<PENDING>}
+
+The loop stopped before the ``create()`` finished, ``close()`` has been called
+before ``write()``, whereas coroutine functions were called in this order:
+``create()``, ``write()``, ``close()``.
+
+To fix the example, tasks must be marked with ``yield from``::
+
+ @asyncio.coroutine
+ def test():
+ yield from asyncio.async(create())
+ yield from asyncio.async(write())
+ yield from asyncio.async(close())
+ yield from asyncio.sleep(2.0)
+ loop.stop()
+
+Or without ``asyncio.async()``::
+
+ @asyncio.coroutine
+ def test():
+ yield from create()
+ yield from write()
+ yield from close()
+ yield from asyncio.sleep(2.0)
+ loop.stop()
+
diff --git a/Doc/library/asyncio-eventloop.rst b/Doc/library/asyncio-eventloop.rst
new file mode 100644
index 0000000..835266f
--- /dev/null
+++ b/Doc/library/asyncio-eventloop.rst
@@ -0,0 +1,668 @@
+.. currentmodule:: asyncio
+
+.. _asyncio-event-loop:
+
+Event loops
+===========
+
+The event loop is the central execution device provided by :mod:`asyncio`.
+It provides multiple facilities, amongst which:
+
+* Registering, executing and cancelling delayed calls (timeouts).
+
+* Creating client and server :ref:`transports <asyncio-transport>` for various
+ kinds of communication.
+
+* Launching subprocesses and the associated :ref:`transports
+ <asyncio-transport>` for communication with an external program.
+
+* Delegating costly function calls to a pool of threads.
+
+Event loop policies and the default policy
+------------------------------------------
+
+Event loop management is abstracted with a *policy* pattern, to provide maximal
+flexibility for custom platforms and frameworks. Throughout the execution of a
+process, a single global policy object manages the event loops available to the
+process based on the calling context. A policy is an object implementing the
+:class:`AbstractEventLoopPolicy` interface.
+
+For most users of :mod:`asyncio`, policies never have to be dealt with
+explicitly, since the default global policy is sufficient.
+
+The default policy defines context as the current thread, and manages an event
+loop per thread that interacts with :mod:`asyncio`. The module-level functions
+:func:`get_event_loop` and :func:`set_event_loop` provide convenient access to
+event loops managed by the default policy.
+
+Event loop functions
+--------------------
+
+The following functions are convenient shortcuts to accessing the methods of the
+global policy. Note that this provides access to the default policy, unless an
+alternative policy was set by calling :func:`set_event_loop_policy` earlier in
+the execution of the process.
+
+.. function:: get_event_loop()
+
+ Equivalent to calling ``get_event_loop_policy().get_event_loop()``.
+
+.. function:: set_event_loop(loop)
+
+ Equivalent to calling ``get_event_loop_policy().set_event_loop(loop)``.
+
+.. function:: new_event_loop()
+
+ Equivalent to calling ``get_event_loop_policy().new_event_loop()``.
+
+Event loop policy interface
+---------------------------
+
+An event loop policy must implement the following interface:
+
+.. class:: AbstractEventLoopPolicy
+
+ .. method:: get_event_loop()
+
+ Get the event loop for current context. Returns an event loop object
+ implementing :class:`BaseEventLoop` interface, or raises an exception in case
+ no event loop has been set for the current context and the current policy
+ does not specify to create one. It should never return ``None``.
+
+ .. method:: set_event_loop(loop)
+
+ Set the event loop of the current context to *loop*.
+
+ .. method:: new_event_loop()
+
+ Create and return a new event loop object according to this policy's rules.
+ If there's need to set this loop as the event loop of the current context,
+ :meth:`set_event_loop` must be called explicitly.
+
+Access to the global loop policy
+--------------------------------
+
+.. function:: get_event_loop_policy()
+
+ Get the current event loop policy.
+
+.. function:: set_event_loop_policy(policy)
+
+ Set the current event loop policy. If *policy* is ``None``, the default
+ policy is restored.
+
+Run an event loop
+-----------------
+
+.. method:: BaseEventLoop.run_forever()
+
+ Run until :meth:`stop` is called.
+
+.. method:: BaseEventLoop.run_until_complete(future)
+
+ Run until the :class:`Future` is done.
+
+ If the argument is a :ref:`coroutine <coroutine>`, it is wrapped
+ in a :class:`Task`.
+
+ Return the Future's result, or raise its exception.
+
+.. method:: BaseEventLoop.is_running()
+
+ Returns running status of event loop.
+
+.. method:: BaseEventLoop.stop()
+
+ Stop running the event loop.
+
+ Every callback scheduled before :meth:`stop` is called will run.
+ Callback scheduled after :meth:`stop` is called won't. However, those
+ callbacks will run if :meth:`run_forever` is called again later.
+
+.. method:: BaseEventLoop.close()
+
+ Close the event loop. The loop should not be running.
+
+ This clears the queues and shuts down the executor, but does not wait for
+ the executor to finish.
+
+ This is idempotent and irreversible. No other methods should be called after
+ this one.
+
+
+Calls
+-----
+
+.. method:: BaseEventLoop.call_soon(callback, \*args)
+
+ Arrange for a callback to be called as soon as possible.
+
+ This operates as a FIFO queue, callbacks are called in the order in
+ which they are registered. Each callback will be called exactly once.
+
+ Any positional arguments after the callback will be passed to the
+ callback when it is called.
+
+ An instance of :class:`asyncio.Handle` is returned.
+
+.. method:: BaseEventLoop.call_soon_threadsafe(callback, \*args)
+
+ Like :meth:`call_soon`, but thread safe.
+
+
+.. _asyncio-delayed-calls:
+
+Delayed calls
+-------------
+
+The event loop has its own internal clock for computing timeouts.
+Which clock is used depends on the (platform-specific) event loop
+implementation; ideally it is a monotonic clock. This will generally be
+a different clock than :func:`time.time`.
+
+.. note::
+
+ Timeouts (relative *delay* or absolute *when*) should not exceed one day.
+
+
+.. method:: BaseEventLoop.call_later(delay, callback, *args)
+
+ Arrange for the *callback* to be called after the given *delay*
+ seconds (either an int or float).
+
+ An instance of :class:`asyncio.Handle` is returned.
+
+ *callback* will be called exactly once per call to :meth:`call_later`.
+ If two callbacks are scheduled for exactly the same time, it is
+ undefined which will be called first.
+
+ The optional positional *args* will be passed to the callback when it
+ is called. If you want the callback to be called with some named
+ arguments, use a closure or :func:`functools.partial`.
+
+.. method:: BaseEventLoop.call_at(when, callback, *args)
+
+ Arrange for the *callback* to be called at the given absolute timestamp
+ *when* (an int or float), using the same time reference as :meth:`time`.
+
+ This method's behavior is the same as :meth:`call_later`.
+
+.. method:: BaseEventLoop.time()
+
+ Return the current time, as a :class:`float` value, according to the
+ event loop's internal clock.
+
+.. seealso::
+
+ The :func:`asyncio.sleep` function.
+
+
+Creating connections
+--------------------
+
+.. method:: BaseEventLoop.create_connection(protocol_factory, host=None, port=None, \*, ssl=None, family=0, proto=0, flags=0, sock=None, local_addr=None, server_hostname=None)
+
+ Create a streaming transport connection to a given Internet *host* and
+ *port*: socket family :py:data:`~socket.AF_INET` or
+ :py:data:`~socket.AF_INET6` depending on *host* (or *family* if specified),
+ socket type :py:data:`~socket.SOCK_STREAM`. *protocol_factory* must be a
+ callable returning a :ref:`protocol <asyncio-protocol>` instance.
+
+ This method is a :ref:`coroutine <coroutine>` which will try to
+ establish the connection in the background. When successful, the
+ coroutine returns a ``(transport, protocol)`` pair.
+
+ The chronological synopsis of the underlying operation is as follows:
+
+ #. The connection is established, and a :ref:`transport <asyncio-transport>`
+ is created to represent it.
+
+ #. *protocol_factory* is called without arguments and must return a
+ :ref:`protocol <asyncio-protocol>` instance.
+
+ #. The protocol instance is tied to the transport, and its
+ :meth:`connection_made` method is called.
+
+ #. The coroutine returns successfully with the ``(transport, protocol)``
+ pair.
+
+ The created transport is an implementation-dependent bidirectional stream.
+
+ .. note::
+ *protocol_factory* can be any kind of callable, not necessarily
+ a class. For example, if you want to use a pre-created
+ protocol instance, you can pass ``lambda: my_protocol``.
+
+ Options allowing to change how the connection is created:
+
+ * *ssl*: if given and not false, a SSL/TLS transport is created
+ (by default a plain TCP transport is created). If *ssl* is
+ a :class:`ssl.SSLContext` object, this context is used to create
+ the transport; if *ssl* is :const:`True`, a context with some
+ unspecified default settings is used.
+
+ .. seealso:: :ref:`SSL/TLS security considerations <ssl-security>`
+
+ * *server_hostname*, is only for use together with *ssl*,
+ and sets or overrides the hostname that the target server's certificate
+ will be matched against. By default the value of the *host* argument
+ is used. If *host* is empty, there is no default and you must pass a
+ value for *server_hostname*. If *server_hostname* is an empty
+ string, hostname matching is disabled (which is a serious security
+ risk, allowing for man-in-the-middle-attacks).
+
+ * *family*, *proto*, *flags* are the optional address family, protocol
+ and flags to be passed through to getaddrinfo() for *host* resolution.
+ If given, these should all be integers from the corresponding
+ :mod:`socket` module constants.
+
+ * *sock*, if given, should be an existing, already connected
+ :class:`socket.socket` object to be used by the transport.
+ If *sock* is given, none of *host*, *port*, *family*, *proto*, *flags*
+ and *local_addr* should be specified.
+
+ * *local_addr*, if given, is a ``(local_host, local_port)`` tuple used
+ to bind the socket to locally. The *local_host* and *local_port*
+ are looked up using getaddrinfo(), similarly to *host* and *port*.
+
+ .. seealso::
+
+ The :func:`open_connection` function can be used to get a pair of
+ (:class:`StreamReader`, :class:`StreamWriter`) instead of a protocol.
+
+
+.. method:: BaseEventLoop.create_datagram_endpoint(protocol_factory, local_addr=None, remote_addr=None, \*, family=0, proto=0, flags=0)
+
+ Create datagram connection: socket family :py:data:`~socket.AF_INET` or
+ :py:data:`~socket.AF_INET6` depending on *host* (or *family* if specified),
+ socket type :py:data:`~socket.SOCK_DGRAM`.
+
+ This method is a :ref:`coroutine <coroutine>` which will try to
+ establish the connection in the background. When successful, the
+ coroutine returns a ``(transport, protocol)`` pair.
+
+ See the :meth:`BaseEventLoop.create_connection` method for parameters.
+
+
+.. method:: BaseEventLoop.create_unix_connection(protocol_factory, path, \*, ssl=None, sock=None, server_hostname=None)
+
+ Create UNIX connection: socket family :py:data:`~socket.AF_UNIX`, socket
+ type :py:data:`~socket.SOCK_STREAM`. The :py:data:`~socket.AF_UNIX` socket
+ family is used to communicate between processes on the same machine
+ efficiently.
+
+ This method is a :ref:`coroutine <coroutine>` which will try to
+ establish the connection in the background. When successful, the
+ coroutine returns a ``(transport, protocol)`` pair.
+
+ See the :meth:`BaseEventLoop.create_connection` method for parameters.
+
+ Availability: UNIX.
+
+
+Creating listening connections
+------------------------------
+
+.. method:: BaseEventLoop.create_server(protocol_factory, host=None, port=None, \*, family=socket.AF_UNSPEC, flags=socket.AI_PASSIVE, sock=None, backlog=100, ssl=None, reuse_address=None)
+
+ A :ref:`coroutine <coroutine>` method which creates a TCP server bound to
+ host and port.
+
+ The return value is a :class:`AbstractServer` object which can be used to stop
+ the service.
+
+ If *host* is an empty string or None all interfaces are assumed
+ and a list of multiple sockets will be returned (most likely
+ one for IPv4 and another one for IPv6).
+
+ *family* can be set to either :data:`~socket.AF_INET` or
+ :data:`~socket.AF_INET6` to force the socket to use IPv4 or IPv6. If not set
+ it will be determined from host (defaults to :data:`~socket.AF_UNSPEC`).
+
+ *flags* is a bitmask for :meth:`getaddrinfo`.
+
+ *sock* can optionally be specified in order to use a preexisting
+ socket object.
+
+ *backlog* is the maximum number of queued connections passed to
+ :meth:`~socket.socket.listen` (defaults to 100).
+
+ ssl can be set to an :class:`~ssl.SSLContext` to enable SSL over the
+ accepted connections.
+
+ *reuse_address* tells the kernel to reuse a local socket in
+ TIME_WAIT state, without waiting for its natural timeout to
+ expire. If not specified will automatically be set to True on
+ UNIX.
+
+ .. seealso::
+
+ The function :func:`start_server` creates a (:class:`StreamReader`,
+ :class:`StreamWriter`) pair and calls back a function with this pair.
+
+
+.. method:: BaseEventLoop.create_unix_server(protocol_factory, path=None, \*, sock=None, backlog=100, ssl=None)
+
+ Similar to :meth:`BaseEventLoop.create_server`, but specific to the
+ socket family :py:data:`~socket.AF_UNIX`.
+
+ Availability: UNIX.
+
+
+
+Watch file descriptors
+----------------------
+
+.. method:: BaseEventLoop.add_reader(fd, callback, \*args)
+
+ Start watching the file descriptor for read availability and then call the
+ *callback* with specified arguments.
+
+.. method:: BaseEventLoop.remove_reader(fd)
+
+ Stop watching the file descriptor for read availability.
+
+.. method:: BaseEventLoop.add_writer(fd, callback, \*args)
+
+ Start watching the file descriptor for write availability and then call the
+ *callback* with specified arguments.
+
+.. method:: BaseEventLoop.remove_writer(fd)
+
+ Stop watching the file descriptor for write availability.
+
+
+Low-level socket operations
+---------------------------
+
+.. method:: BaseEventLoop.sock_recv(sock, nbytes)
+
+ Receive data from the socket. The return value is a bytes object
+ representing the data received. The maximum amount of data to be received
+ at once is specified by *nbytes*.
+
+ This method is a :ref:`coroutine <coroutine>`.
+
+ .. seealso::
+
+ The :meth:`socket.socket.recv` method.
+
+.. method:: BaseEventLoop.sock_sendall(sock, data)
+
+ Send data to the socket. The socket must be connected to a remote socket.
+ This method continues to send data from *data* until either all data has
+ been sent or an error occurs. ``None`` is returned on success. On error,
+ an exception is raised, and there is no way to determine how much data, if
+ any, was successfully processed by the receiving end of the connection.
+
+ This method is a :ref:`coroutine <coroutine>`.
+
+ .. seealso::
+
+ The :meth:`socket.socket.sendall` method.
+
+.. method:: BaseEventLoop.sock_connect(sock, address)
+
+ Connect to a remote socket at *address*.
+
+ The *address* must be already resolved to avoid the trap of hanging the
+ entire event loop when the address requires doing a DNS lookup. For
+ example, it must be an IP address, not an hostname, for
+ :py:data:`~socket.AF_INET` and :py:data:`~socket.AF_INET6` address families.
+ Use :meth:`getaddrinfo` to resolve the hostname asynchronously.
+
+ This method is a :ref:`coroutine <coroutine>`.
+
+ .. seealso::
+
+ The :meth:`BaseEventLoop.create_connection` method, the
+ :func:`open_connection` function and the :meth:`socket.socket.connect`
+ method.
+
+
+.. method:: BaseEventLoop.sock_accept(sock)
+
+ Accept a connection. The socket must be bound to an address and listening
+ for connections. The return value is a pair ``(conn, address)`` where *conn*
+ is a *new* socket object usable to send and receive data on the connection,
+ and *address* is the address bound to the socket on the other end of the
+ connection.
+
+ This method is a :ref:`coroutine <coroutine>`.
+
+ .. seealso::
+
+ The :meth:`BaseEventLoop.create_server` method, the :func:`start_server`
+ function and the :meth:`socket.socket.accept` method.
+
+
+Resolve host name
+-----------------
+
+.. method:: BaseEventLoop.getaddrinfo(host, port, \*, family=0, type=0, proto=0, flags=0)
+
+ This method is a :ref:`coroutine <coroutine>`, similar to
+ :meth:`socket.getaddrinfo` function but non-blocking.
+
+.. method:: BaseEventLoop.getnameinfo(sockaddr, flags=0)
+
+ This method is a :ref:`coroutine <coroutine>`, similar to
+ :meth:`socket.getnameinfo` function but non-blocking.
+
+
+Connect pipes
+-------------
+
+.. method:: BaseEventLoop.connect_read_pipe(protocol_factory, pipe)
+
+ Register read pipe in eventloop.
+
+ *protocol_factory* should instantiate object with :class:`Protocol`
+ interface. pipe is file-like object already switched to nonblocking.
+ Return pair (transport, protocol), where transport support
+ :class:`ReadTransport` interface.
+
+ This method is a :ref:`coroutine <coroutine>`.
+
+.. method:: BaseEventLoop.connect_write_pipe(protocol_factory, pipe)
+
+ Register write pipe in eventloop.
+
+ *protocol_factory* should instantiate object with :class:`BaseProtocol`
+ interface. Pipe is file-like object already switched to nonblocking.
+ Return pair (transport, protocol), where transport support
+ :class:`WriteTransport` interface.
+
+ This method is a :ref:`coroutine <coroutine>`.
+
+.. seealso::
+
+ The :meth:`BaseEventLoop.subprocess_exec` and
+ :meth:`BaseEventLoop.subprocess_shell` methods.
+
+
+UNIX signals
+------------
+
+Availability: UNIX only.
+
+.. method:: BaseEventLoop.add_signal_handler(signum, callback, \*args)
+
+ Add a handler for a signal.
+
+ Raise :exc:`ValueError` if the signal number is invalid or uncatchable.
+ Raise :exc:`RuntimeError` if there is a problem setting up the handler.
+
+.. method:: BaseEventLoop.remove_signal_handler(sig)
+
+ Remove a handler for a signal.
+
+ Return ``True`` if a signal handler was removed, ``False`` if not.
+
+.. seealso::
+
+ The :mod:`signal` module.
+
+
+Executor
+--------
+
+Call a function in an :class:`~concurrent.futures.Executor` (pool of threads or
+pool of processes). By default, an event loop uses a thread pool executor
+(:class:`~concurrent.futures.ThreadPoolExecutor`).
+
+.. method:: BaseEventLoop.run_in_executor(executor, callback, \*args)
+
+ Arrange for a callback to be called in the specified executor.
+
+ The *executor* argument should be an :class:`~concurrent.futures.Executor`
+ instance. The default executor is used if *executor* is ``None``.
+
+ This method is a :ref:`coroutine <coroutine>`.
+
+.. method:: BaseEventLoop.set_default_executor(executor)
+
+ Set the default executor used by :meth:`run_in_executor`.
+
+
+Error Handling API
+------------------
+
+Allows to customize how exceptions are handled in the event loop.
+
+.. method:: BaseEventLoop.set_exception_handler(handler)
+
+ Set *handler* as the new event loop exception handler.
+
+ If *handler* is ``None``, the default exception handler will
+ be set.
+
+ If *handler* is a callable object, it should have a
+ matching signature to ``(loop, context)``, where ``loop``
+ will be a reference to the active event loop, ``context``
+ will be a ``dict`` object (see :meth:`call_exception_handler`
+ documentation for details about context).
+
+.. method:: BaseEventLoop.default_exception_handler(context)
+
+ Default exception handler.
+
+ This is called when an exception occurs and no exception
+ handler is set, and can be called by a custom exception
+ handler that wants to defer to the default behavior.
+
+ *context* parameter has the same meaning as in
+ :meth:`call_exception_handler`.
+
+.. method:: BaseEventLoop.call_exception_handler(context)
+
+ Call the current event loop exception handler.
+
+ *context* is a ``dict`` object containing the following keys
+ (new keys may be introduced later):
+
+ * 'message': Error message;
+ * 'exception' (optional): Exception object;
+ * 'future' (optional): :class:`asyncio.Future` instance;
+ * 'handle' (optional): :class:`asyncio.Handle` instance;
+ * 'protocol' (optional): :ref:`Protocol <asyncio-protocol>` instance;
+ * 'transport' (optional): :ref:`Transport <asyncio-transport>` instance;
+ * 'socket' (optional): :class:`socket.socket` instance.
+
+ .. note::
+
+ Note: this method should not be overloaded in subclassed
+ event loops. For any custom exception handling, use
+ :meth:`set_exception_handler()` method.
+
+Debug mode
+----------
+
+.. method:: BaseEventLoop.get_debug()
+
+ Get the debug mode (:class:`bool`) of the event loop, ``False`` by default.
+
+.. method:: BaseEventLoop.set_debug(enabled: bool)
+
+ Set the debug mode of the event loop.
+
+.. seealso::
+
+ The :ref:`Develop with asyncio <asyncio-dev>` section.
+
+
+Server
+------
+
+.. class:: AbstractServer
+
+ Abstract server returned by :func:`BaseEventLoop.create_server`.
+
+ .. method:: close()
+
+ Stop serving. This leaves existing connections open.
+
+ .. method:: wait_closed()
+
+ A :ref:`coroutine <coroutine>` to wait until service is closed.
+
+
+Handle
+------
+
+.. class:: Handle
+
+ A callback wrapper object returned by :func:`BaseEventLoop.call_soon`,
+ :func:`BaseEventLoop.call_soon_threadsafe`, :func:`BaseEventLoop.call_later`,
+ and :func:`BaseEventLoop.call_at`.
+
+ .. method:: cancel()
+
+ Cancel the call.
+
+
+.. _asyncio-hello-world-callback:
+
+Example: Hello World (callback)
+-------------------------------
+
+Print ``Hello World`` every two seconds, using a callback::
+
+ import asyncio
+
+ def print_and_repeat(loop):
+ print('Hello World')
+ loop.call_later(2, print_and_repeat, loop)
+
+ loop = asyncio.get_event_loop()
+ loop.call_soon(print_and_repeat, loop)
+ loop.run_forever()
+
+.. seealso::
+
+ :ref:`Hello World example using a coroutine <asyncio-hello-world-coroutine>`.
+
+
+Example: Set signal handlers for SIGINT and SIGTERM
+---------------------------------------------------
+
+Register handlers for signals :py:data:`SIGINT` and :py:data:`SIGTERM`::
+
+ import asyncio
+ import functools
+ import os
+ import signal
+
+ def ask_exit(signame):
+ print("got signal %s: exit" % signame)
+ loop.stop()
+
+ loop = asyncio.get_event_loop()
+ for signame in ('SIGINT', 'SIGTERM'):
+ loop.add_signal_handler(getattr(signal, signame),
+ functools.partial(ask_exit, signame))
+
+ print("Event loop running forever, press CTRL+c to interrupt.")
+ print("pid %s: send SIGINT or SIGTERM to exit." % os.getpid())
+ loop.run_forever()
+
diff --git a/Doc/library/asyncio-protocol.rst b/Doc/library/asyncio-protocol.rst
new file mode 100644
index 0000000..58c94ae
--- /dev/null
+++ b/Doc/library/asyncio-protocol.rst
@@ -0,0 +1,502 @@
+.. currentmodule:: asyncio
+
++++++++++++++++++++++++++++++++++++++++++
+Transports and protocols (low-level API)
++++++++++++++++++++++++++++++++++++++++++
+
+.. _asyncio-transport:
+
+Transports
+==========
+
+Transports are classes provided by :mod:`asyncio` in order to abstract
+various kinds of communication channels. You generally won't instantiate
+a transport yourself; instead, you will call a :class:`BaseEventLoop` method
+which will create the transport and try to initiate the underlying
+communication channel, calling you back when it succeeds.
+
+Once the communication channel is established, a transport is always
+paired with a :ref:`protocol <asyncio-protocol>` instance. The protocol can
+then call the transport's methods for various purposes.
+
+:mod:`asyncio` currently implements transports for TCP, UDP, SSL, and
+subprocess pipes. The methods available on a transport depend on
+the transport's kind.
+
+
+BaseTransport
+-------------
+
+.. class:: BaseTransport
+
+ Base class for transports.
+
+ .. method:: close(self)
+
+ Close the transport. If the transport has a buffer for outgoing
+ data, buffered data will be flushed asynchronously. No more data
+ will be received. After all buffered data is flushed, the
+ protocol's :meth:`connection_lost` method will be called with
+ :const:`None` as its argument.
+
+
+ .. method:: get_extra_info(name, default=None)
+
+ Return optional transport information. *name* is a string representing
+ the piece of transport-specific information to get, *default* is the
+ value to return if the information doesn't exist.
+
+ This method allows transport implementations to easily expose
+ channel-specific information.
+
+ * socket:
+
+ - ``'peername'``: the remote address to which the socket is connected,
+ result of :meth:`socket.socket.getpeername` (``None`` on error)
+ - ``'socket'``: :class:`socket.socket` instance
+ - ``'sockname'``: the socket's own address,
+ result of :meth:`socket.socket.getsockname`
+
+ * SSL socket:
+
+ - ``'compression'``: the compression algorithm being used as a string,
+ or ``None`` if the connection isn't compressed; result of
+ :meth:`ssl.SSLSocket.compression`
+ - ``'cipher'``: a three-value tuple containing the name of the cipher
+ being used, the version of the SSL protocol that defines its use, and
+ the number of secret bits being used; result of
+ :meth:`ssl.SSLSocket.cipher`
+ - ``'peercert'``: peer certificate; result of
+ :meth:`ssl.SSLSocket.getpeercert`
+ - ``'sslcontext'``: :class:`ssl.SSLContext` instance
+
+ * pipe:
+
+ - ``'pipe'``: pipe object
+
+ * subprocess:
+
+ - ``'subprocess'``: :class:`subprocess.Popen` instance
+
+
+ReadTransport
+-------------
+
+.. class:: ReadTransport
+
+ Interface for read-only transports.
+
+ .. method:: pause_reading()
+
+ Pause the receiving end of the transport. No data will be passed to
+ the protocol's :meth:`data_received` method until :meth:`resume_reading`
+ is called.
+
+ .. method:: resume_reading()
+
+ Resume the receiving end. The protocol's :meth:`data_received` method
+ will be called once again if some data is available for reading.
+
+
+WriteTransport
+--------------
+
+.. class:: WriteTransport
+
+ Interface for write-only transports.
+
+ .. method:: abort()
+
+ Close the transport immediately, without waiting for pending operations
+ to complete. Buffered data will be lost. No more data will be received.
+ The protocol's :meth:`connection_lost` method will eventually be
+ called with :const:`None` as its argument.
+
+ .. method:: can_write_eof()
+
+ Return :const:`True` if the transport supports :meth:`write_eof`,
+ :const:`False` if not.
+
+ .. method:: get_write_buffer_size()
+
+ Return the current size of the output buffer used by the transport.
+
+ .. method:: set_write_buffer_limits(high=None, low=None)
+
+ Set the *high*- and *low*-water limits for write flow control.
+
+ These two values control when call the protocol's
+ :meth:`pause_writing` and :meth:`resume_writing` methods are called.
+ If specified, the low-water limit must be less than or equal to the
+ high-water limit. Neither *high* nor *low* can be negative.
+
+ The defaults are implementation-specific. If only the
+ high-water limit is given, the low-water limit defaults to a
+ implementation-specific value less than or equal to the
+ high-water limit. Setting *high* to zero forces *low* to zero as
+ well, and causes :meth:`pause_writing` to be called whenever the
+ buffer becomes non-empty. Setting *low* to zero causes
+ :meth:`resume_writing` to be called only once the buffer is empty.
+ Use of zero for either limit is generally sub-optimal as it
+ reduces opportunities for doing I/O and computation
+ concurrently.
+
+ .. method:: write(data)
+
+ Write some *data* bytes to the transport.
+
+ This method does not block; it buffers the data and arranges for it
+ to be sent out asynchronously.
+
+ .. method:: writelines(list_of_data)
+
+ Write a list (or any iterable) of data bytes to the transport.
+ This is functionally equivalent to calling :meth:`write` on each
+ element yielded by the iterable, but may be implemented more efficiently.
+
+ .. method:: write_eof()
+
+ Close the write end of the transport after flushing buffered data.
+ Data may still be received.
+
+ This method can raise :exc:`NotImplementedError` if the transport
+ (e.g. SSL) doesn't support half-closes.
+
+
+DatagramTransport
+-----------------
+
+.. method:: DatagramTransport.sendto(data, addr=None)
+
+ Send the *data* bytes to the remote peer given by *addr* (a
+ transport-dependent target address). If *addr* is :const:`None`, the
+ data is sent to the target address given on transport creation.
+
+ This method does not block; it buffers the data and arranges for it
+ to be sent out asynchronously.
+
+.. method:: DatagramTransport.abort()
+
+ Close the transport immediately, without waiting for pending operations
+ to complete. Buffered data will be lost. No more data will be received.
+ The protocol's :meth:`connection_lost` method will eventually be
+ called with :const:`None` as its argument.
+
+
+BaseSubprocessTransport
+-----------------------
+
+.. class:: BaseSubprocessTransport
+
+ .. method:: get_pid()
+
+ Return the subprocess process id as an integer.
+
+ .. method:: get_pipe_transport(fd)
+
+ Return the transport for the communication pipe corresponding to the
+ integer file descriptor *fd*. The return value can be a readable or
+ writable streaming transport, depending on the *fd*. If *fd* doesn't
+ correspond to a pipe belonging to this transport, :const:`None` is
+ returned.
+
+ .. method:: get_returncode()
+
+ Return the subprocess returncode as an integer or :const:`None`
+ if it hasn't returned, similarly to the
+ :attr:`subprocess.Popen.returncode` attribute.
+
+ .. method:: kill(self)
+
+ Kill the subprocess, as in :meth:`subprocess.Popen.kill`
+
+ On POSIX systems, the function sends SIGKILL to the subprocess.
+ On Windows, this method is an alias for :meth:`terminate`.
+
+ .. method:: send_signal(signal)
+
+ Send the *signal* number to the subprocess, as in
+ :meth:`subprocess.Popen.send_signal`.
+
+ .. method:: terminate()
+
+ Ask the subprocess to stop, as in :meth:`subprocess.Popen.terminate`.
+ This method is an alias for the :meth:`close` method.
+
+ On POSIX systems, this method sends SIGTERM to the subprocess.
+ On Windows, the Windows API function TerminateProcess() is called to
+ stop the subprocess.
+
+
+.. _asyncio-protocol:
+
+Protocols
+=========
+
+:mod:`asyncio` provides base classes that you can subclass to implement
+your network protocols. Those classes are used in conjunction with
+:ref:`transports <asyncio-transport>` (see below): the protocol parses incoming
+data and asks for the writing of outgoing data, while the transport is
+responsible for the actual I/O and buffering.
+
+When subclassing a protocol class, it is recommended you override certain
+methods. Those methods are callbacks: they will be called by the transport
+on certain events (for example when some data is received); you shouldn't
+call them yourself, unless you are implementing a transport.
+
+.. note::
+ All callbacks have default implementations, which are empty. Therefore,
+ you only need to implement the callbacks for the events in which you
+ are interested.
+
+
+Protocol classes
+----------------
+
+.. class:: Protocol
+
+ The base class for implementing streaming protocols (for use with
+ e.g. TCP and SSL transports).
+
+.. class:: DatagramProtocol
+
+ The base class for implementing datagram protocols (for use with
+ e.g. UDP transports).
+
+.. class:: SubprocessProtocol
+
+ The base class for implementing protocols communicating with child
+ processes (through a set of unidirectional pipes).
+
+
+Connection callbacks
+--------------------
+
+These callbacks may be called on :class:`Protocol` and
+:class:`SubprocessProtocol` instances:
+
+.. method:: BaseProtocol.connection_made(transport)
+
+ Called when a connection is made.
+
+ The *transport* argument is the transport representing the
+ connection. You are responsible for storing it somewhere
+ (e.g. as an attribute) if you need to.
+
+.. method:: BaseProtocol.connection_lost(exc)
+
+ Called when the connection is lost or closed.
+
+ The argument is either an exception object or :const:`None`.
+ The latter means a regular EOF is received, or the connection was
+ aborted or closed by this side of the connection.
+
+:meth:`connection_made` and :meth:`connection_lost` are called exactly once
+per successful connection. All other callbacks will be called between those
+two methods, which allows for easier resource management in your protocol
+implementation.
+
+The following callbacks may be called only on :class:`SubprocessProtocol`
+instances:
+
+.. method:: SubprocessProtocol.pipe_data_received(fd, data)
+
+ Called when the child process writes data into its stdout or stderr pipe.
+ *fd* is the integer file descriptor of the pipe. *data* is a non-empty
+ bytes object containing the data.
+
+.. method:: SubprocessProtocol.pipe_connection_lost(fd, exc)
+
+ Called when one of the pipes communicating with the child process
+ is closed. *fd* is the integer file descriptor that was closed.
+
+.. method:: SubprocessProtocol.process_exited()
+
+ Called when the child process has exited.
+
+
+Streaming protocols
+-------------------
+
+The following callbacks are called on :class:`Protocol` instances:
+
+.. method:: Protocol.data_received(data)
+
+ Called when some data is received. *data* is a non-empty bytes object
+ containing the incoming data.
+
+ .. note::
+ Whether the data is buffered, chunked or reassembled depends on
+ the transport. In general, you shouldn't rely on specific semantics
+ and instead make your parsing generic and flexible enough. However,
+ data is always received in the correct order.
+
+.. method:: Protocol.eof_received()
+
+ Calls when the other end signals it won't send any more data
+ (for example by calling :meth:`write_eof`, if the other end also uses
+ asyncio).
+
+ This method may return a false value (including None), in which case
+ the transport will close itself. Conversely, if this method returns a
+ true value, closing the transport is up to the protocol. Since the
+ default implementation returns None, it implicitly closes the connection.
+
+ .. note::
+ Some transports such as SSL don't support half-closed connections,
+ in which case returning true from this method will not prevent closing
+ the connection.
+
+:meth:`data_received` can be called an arbitrary number of times during
+a connection. However, :meth:`eof_received` is called at most once
+and, if called, :meth:`data_received` won't be called after it.
+
+Datagram protocols
+------------------
+
+The following callbacks are called on :class:`DatagramProtocol` instances.
+
+.. method:: DatagramProtocol.datagram_received(data, addr)
+
+ Called when a datagram is received. *data* is a bytes object containing
+ the incoming data. *addr* is the address of the peer sending the data;
+ the exact format depends on the transport.
+
+.. method:: DatagramProtocol.error_received(exc)
+
+ Called when a previous send or receive operation raises an
+ :class:`OSError`. *exc* is the :class:`OSError` instance.
+
+ This method is called in rare conditions, when the transport (e.g. UDP)
+ detects that a datagram couldn't be delivered to its recipient.
+ In many conditions though, undeliverable datagrams will be silently
+ dropped.
+
+
+Flow control callbacks
+----------------------
+
+These callbacks may be called on :class:`Protocol`,
+:class:`DatagramProtocol` and :class:`SubprocessProtocol` instances:
+
+.. method:: BaseProtocol.pause_writing()
+
+ Called when the transport's buffer goes over the high-water mark.
+
+.. method:: BaseProtocol.resume_writing()
+
+ Called when the transport's buffer drains below the low-water mark.
+
+
+:meth:`pause_writing` and :meth:`resume_writing` calls are paired --
+:meth:`pause_writing` is called once when the buffer goes strictly over
+the high-water mark (even if subsequent writes increases the buffer size
+even more), and eventually :meth:`resume_writing` is called once when the
+buffer size reaches the low-water mark.
+
+.. note::
+ If the buffer size equals the high-water mark,
+ :meth:`pause_writing` is not called -- it must go strictly over.
+ Conversely, :meth:`resume_writing` is called when the buffer size is
+ equal or lower than the low-water mark. These end conditions
+ are important to ensure that things go as expected when either
+ mark is zero.
+
+.. note::
+ On BSD systems (OS X, FreeBSD, etc.) flow control is not supported
+ for :class:`DatagramProtocol`, because send failures caused by
+ writing too many packets cannot be detected easily. The socket
+ always appears 'ready' and excess packets are dropped; an
+ :class:`OSError` with errno set to :const:`errno.ENOBUFS` may or
+ may not be raised; if it is raised, it will be reported to
+ :meth:`DatagramProtocol.error_received` but otherwise ignored.
+
+
+Coroutines and protocols
+------------------------
+
+Coroutines can be scheduled in a protocol method using :func:`async`, but there
+is not guarantee on the execution order. Protocols are not aware of coroutines
+created in protocol methods and so will not wait for them.
+
+To have a reliable execution order, use :ref:`stream objects <asyncio-streams>` in a
+coroutine with ``yield from``. For example, the :meth:`StreamWriter.drain`
+coroutine can be used to wait until the write buffer is flushed.
+
+
+Protocol example: TCP echo server and client
+============================================
+
+Echo client
+-----------
+
+TCP echo client example, send data and wait until the connection is closed::
+
+ import asyncio
+
+ class EchoClient(asyncio.Protocol):
+ message = 'This is the message. It will be echoed.'
+
+ def connection_made(self, transport):
+ transport.write(self.message.encode())
+ print('data sent: {}'.format(self.message))
+
+ def data_received(self, data):
+ print('data received: {}'.format(data.decode()))
+
+ def connection_lost(self, exc):
+ print('server closed the connection')
+ asyncio.get_event_loop().stop()
+
+ loop = asyncio.get_event_loop()
+ coro = loop.create_connection(EchoClient, '127.0.0.1', 8888)
+ loop.run_until_complete(coro)
+ loop.run_forever()
+ loop.close()
+
+The event loop is running twice. The
+:meth:`~BaseEventLoop.run_until_complete` method is preferred in this short
+example to raise an exception if the server is not listening, instead of
+having to write a short coroutine to handle the exception and stop the
+running loop. At :meth:`~BaseEventLoop.run_until_complete` exit, the loop is
+no more running, so there is no need to stop the loop in case of an error.
+
+Echo server
+-----------
+
+TCP echo server example, send back received data and close the connection::
+
+ import asyncio
+
+ class EchoServer(asyncio.Protocol):
+ def connection_made(self, transport):
+ peername = transport.get_extra_info('peername')
+ print('connection from {}'.format(peername))
+ self.transport = transport
+
+ def data_received(self, data):
+ print('data received: {}'.format(data.decode()))
+ self.transport.write(data)
+
+ # close the socket
+ self.transport.close()
+
+ loop = asyncio.get_event_loop()
+ coro = loop.create_server(EchoServer, '127.0.0.1', 8888)
+ server = loop.run_until_complete(coro)
+ print('serving on {}'.format(server.sockets[0].getsockname()))
+
+ try:
+ loop.run_forever()
+ except KeyboardInterrupt:
+ print("exit")
+ finally:
+ server.close()
+ loop.close()
+
+:meth:`Transport.close` can be called immediately after
+:meth:`WriteTransport.write` even if data are not sent yet on the socket: both
+methods are asynchronous. ``yield from`` is not needed because these transport
+methods are not coroutines.
+
+
diff --git a/Doc/library/asyncio-stream.rst b/Doc/library/asyncio-stream.rst
new file mode 100644
index 0000000..4543af4
--- /dev/null
+++ b/Doc/library/asyncio-stream.rst
@@ -0,0 +1,278 @@
+.. currentmodule:: asyncio
+
+.. _asyncio-streams:
+
+++++++++++++++++++++++++
+Streams (high-level API)
+++++++++++++++++++++++++
+
+Stream functions
+================
+
+.. function:: open_connection(host=None, port=None, \*, loop=None, limit=None, **kwds)
+
+ A wrapper for :meth:`~BaseEventLoop.create_connection()` returning a (reader,
+ writer) pair.
+
+ The reader returned is a :class:`StreamReader` instance; the writer is
+ a :class:`StreamWriter` instance.
+
+ The arguments are all the usual arguments to
+ :meth:`BaseEventLoop.create_connection` except *protocol_factory*; most
+ common are positional host and port, with various optional keyword arguments
+ following.
+
+ Additional optional keyword arguments are *loop* (to set the event loop
+ instance to use) and *limit* (to set the buffer limit passed to the
+ :class:`StreamReader`).
+
+ (If you want to customize the :class:`StreamReader` and/or
+ :class:`StreamReaderProtocol` classes, just copy the code -- there's really
+ nothing special here except some convenience.)
+
+ This function is a :ref:`coroutine <coroutine>`.
+
+.. function:: start_server(client_connected_cb, host=None, port=None, \*, loop=None, limit=None, **kwds)
+
+ Start a socket server, with a callback for each client connected.
+
+ The first parameter, *client_connected_cb*, takes two parameters:
+ *client_reader*, *client_writer*. *client_reader* is a
+ :class:`StreamReader` object, while *client_writer* is a
+ :class:`StreamWriter` object. This parameter can either be a plain callback
+ function or a :ref:`coroutine function <coroutine>`; if it is a coroutine
+ function, it will be automatically converted into a :class:`Task`.
+
+ The rest of the arguments are all the usual arguments to
+ :meth:`~BaseEventLoop.create_server()` except *protocol_factory*; most
+ common are positional host and port, with various optional keyword arguments
+ following. The return value is the same as
+ :meth:`~BaseEventLoop.create_server()`.
+
+ Additional optional keyword arguments are *loop* (to set the event loop
+ instance to use) and *limit* (to set the buffer limit passed to the
+ :class:`StreamReader`).
+
+ The return value is the same as :meth:`~BaseEventLoop.create_server()`, i.e.
+ a :class:`AbstractServer` object which can be used to stop the service.
+
+ This function is a :ref:`coroutine <coroutine>`.
+
+.. function:: open_unix_connection(path=None, \*, loop=None, limit=None, **kwds)
+
+ A wrapper for :meth:`~BaseEventLoop.create_unix_connection()` returning
+ a (reader, writer) pair.
+
+ See :func:`open_connection` for information about return value and other
+ details.
+
+ This function is a :ref:`coroutine <coroutine>`.
+
+ Availability: UNIX.
+
+.. function:: start_unix_server(client_connected_cb, path=None, \*, loop=None, limit=None, **kwds)
+
+ Start a UNIX Domain Socket server, with a callback for each client connected.
+
+ See :func:`start_server` for information about return value and other
+ details.
+
+ This function is a :ref:`coroutine <coroutine>`.
+
+ Availability: UNIX.
+
+
+StreamReader
+============
+
+.. class:: StreamReader(limit=None, loop=None)
+
+ .. method:: exception()
+
+ Get the exception.
+
+ .. method:: feed_eof()
+
+ Acknowledge the EOF.
+
+ .. method:: feed_data(data)
+
+ Feed *data* bytes in the internal buffer. Any operations waiting
+ for the data will be resumed.
+
+ .. method:: set_exception(exc)
+
+ Set the exception.
+
+ .. method:: set_transport(transport)
+
+ Set the transport.
+
+ .. method:: read(n=-1)
+
+ Read up to *n* bytes. If *n* is not provided, or set to ``-1``,
+ read until EOF and return all read bytes.
+
+ If the EOF was received and the internal buffer is empty,
+ return an empty ``bytes`` object.
+
+ This method is a :ref:`coroutine <coroutine>`.
+
+ .. method:: readline()
+
+ Read one line, where "line" is a sequence of bytes ending with ``\n``.
+
+ If EOF is received, and ``\n`` was not found, the method will
+ return the partial read bytes.
+
+ If the EOF was received and the internal buffer is empty,
+ return an empty ``bytes`` object.
+
+ This method is a :ref:`coroutine <coroutine>`.
+
+ .. method:: readexactly(n)
+
+ Read exactly *n* bytes. Raise an :exc:`IncompleteReadError` if the end of
+ the stream is reached before *n* can be read, the
+ :attr:`IncompleteReadError.partial` attribute of the exception contains
+ the partial read bytes.
+
+ This method is a :ref:`coroutine <coroutine>`.
+
+ .. method:: at_eof()
+
+ Return ``True`` if the buffer is empty and :meth:`feed_eof` was called.
+
+
+StreamWriter
+============
+
+.. class:: StreamWriter(transport, protocol, reader, loop)
+
+ Wraps a Transport.
+
+ This exposes :meth:`write`, :meth:`writelines`, :meth:`can_write_eof()`,
+ :meth:`write_eof`, :meth:`get_extra_info` and :meth:`close`. It adds
+ :meth:`drain` which returns an optional :class:`Future` on which you can
+ wait for flow control. It also adds a transport attribute which references
+ the :class:`Transport` directly.
+
+ .. attribute:: transport
+
+ Transport.
+
+ .. method:: can_write_eof()
+
+ Return :const:`True` if the transport supports :meth:`write_eof`,
+ :const:`False` if not. See :meth:`WriteTransport.can_write_eof`.
+
+ .. method:: close()
+
+ Close the transport: see :meth:`BaseTransport.close`.
+
+ .. method:: drain()
+
+ Wait until the write buffer of the underlying transport is flushed.
+
+ This method has an unusual return value. The intended use is to write::
+
+ w.write(data)
+ yield from w.drain()
+
+ When there's nothing to wait for, :meth:`drain()` returns ``()``, and the
+ yield-from continues immediately. When the transport buffer is full (the
+ protocol is paused), :meth:`drain` creates and returns a
+ :class:`Future` and the yield-from will block until
+ that Future is completed, which will happen when the buffer is
+ (partially) drained and the protocol is resumed.
+
+ .. method:: get_extra_info(name, default=None)
+
+ Return optional transport information: see
+ :meth:`BaseTransport.get_extra_info`.
+
+ .. method:: write(data)
+
+ Write some *data* bytes to the transport: see
+ :meth:`WriteTransport.write`.
+
+ .. method:: writelines(data)
+
+ Write a list (or any iterable) of data bytes to the transport:
+ see :meth:`WriteTransport.writelines`.
+
+ .. method:: write_eof()
+
+ Close the write end of the transport after flushing buffered data:
+ see :meth:`WriteTransport.write_eof`.
+
+
+StreamReaderProtocol
+====================
+
+.. class:: StreamReaderProtocol(stream_reader, client_connected_cb=None, loop=None)
+
+ Trivial helper class to adapt between :class:`Protocol` and
+ :class:`StreamReader`. Sublclass of :class:`Protocol`.
+
+ *stream_reader* is a :class:`StreamReader` instance, *client_connected_cb*
+ is an optional function called with (stream_reader, stream_writer) when a
+ connection is made, *loop* is the event loop instance to use.
+
+ (This is a helper class instead of making :class:`StreamReader` itself a
+ :class:`Protocol` subclass, because the :class:`StreamReader` has other
+ potential uses, and to prevent the user of the :class:`StreamReader` to
+ accidentally call inappropriate methods of the protocol.)
+
+
+IncompleteReadError
+===================
+
+.. exception:: IncompleteReadError
+
+ Incomplete read error, subclass of :exc:`EOFError`.
+
+ .. attribute:: expected
+
+ Total number of expected bytes (:class:`int`).
+
+ .. attribute:: partial
+
+ Read bytes string before the end of stream was reached (:class:`bytes`).
+
+
+Example
+=======
+
+Simple example querying HTTP headers of the URL passed on the command line::
+
+ import asyncio
+ import urllib.parse
+ import sys
+
+ @asyncio.coroutine
+ def print_http_headers(url):
+ url = urllib.parse.urlsplit(url)
+ reader, writer = yield from asyncio.open_connection(url.hostname, 80)
+ query = ('HEAD {url.path} HTTP/1.0\r\n'
+ 'Host: {url.hostname}\r\n'
+ '\r\n').format(url=url)
+ writer.write(query.encode('latin-1'))
+ while True:
+ line = yield from reader.readline()
+ if not line:
+ break
+ line = line.decode('latin1').rstrip()
+ if line:
+ print('HTTP header> %s' % line)
+
+ url = sys.argv[1]
+ loop = asyncio.get_event_loop()
+ task = asyncio.async(print_http_headers(url))
+ loop.run_until_complete(task)
+ loop.close()
+
+Usage::
+
+ python example.py http://example.com/path/page.html
+
diff --git a/Doc/library/asyncio-subprocess.rst b/Doc/library/asyncio-subprocess.rst
new file mode 100644
index 0000000..5b4ede6
--- /dev/null
+++ b/Doc/library/asyncio-subprocess.rst
@@ -0,0 +1,270 @@
+.. currentmodule:: asyncio
+
+Subprocess
+==========
+
+Operating system support
+------------------------
+
+On Windows, the default event loop uses :class:`selectors.SelectSelector`
+which only supports sockets. The :class:`ProactorEventLoop` should be used to
+support subprocesses. However, the latter does not support SSL.
+
+On Mac OS X older than 10.9 (Mavericks), :class:`selectors.KqueueSelector`
+does not support character devices like PTY, whereas it is used by the
+default event loop. The :class:`SelectorEventLoop` can be used with
+:class:`SelectSelector` or :class:`PollSelector` to handle character
+devices on Mac OS X 10.6 (Snow Leopard) and later.
+
+
+Create a subprocess: high-level API using Process
+-------------------------------------------------
+
+.. function:: create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, loop=None, limit=None, \*\*kwds)
+
+ Run the shell command *cmd* given as a string. Return a :class:`~asyncio.subprocess.Process`
+ instance.
+
+ The optional *limit* parameter sets the buffer limit passed to the
+ :class:`StreamReader`.
+
+ This function is a :ref:`coroutine <coroutine>`.
+
+.. function:: create_subprocess_exec(\*args, stdin=None, stdout=None, stderr=None, loop=None, limit=None, \*\*kwds)
+
+ Create a subprocess. Return a :class:`~asyncio.subprocess.Process` instance.
+
+ The optional *limit* parameter sets the buffer limit passed to the
+ :class:`StreamReader`.
+
+ This function is a :ref:`coroutine <coroutine>`.
+
+Use the :meth:`BaseEventLoop.connect_read_pipe` and
+:meth:`BaseEventLoop.connect_write_pipe` methods to connect pipes.
+
+
+Create a subprocess: low-level API using subprocess.Popen
+---------------------------------------------------------
+
+Run subprocesses asynchronously using the :mod:`subprocess` module.
+
+.. method:: BaseEventLoop.subprocess_exec(protocol_factory, \*args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, \*\*kwargs)
+
+ Create a subprocess from one or more string arguments, where the first string
+ specifies the program to execute, and the remaining strings specify the
+ program's arguments. (Thus, together the string arguments form the
+ ``sys.argv`` value of the program, assuming it is a Python script.) This is
+ similar to the standard library :class:`subprocess.Popen` class called with
+ shell=False and the list of strings passed as the first argument;
+ however, where :class:`~subprocess.Popen` takes a single argument which is
+ list of strings, :func:`subprocess_exec` takes multiple string arguments.
+
+ Other parameters:
+
+ * *stdin*: Either a file-like object representing the pipe to be connected
+ to the subprocess's standard input stream using
+ :meth:`~BaseEventLoop.connect_write_pipe`, or the constant
+ :const:`subprocess.PIPE` (the default). By default a new pipe will be
+ created and connected.
+
+ * *stdout*: Either a file-like object representing the pipe to be connected
+ to the subprocess's standard output stream using
+ :meth:`~BaseEventLoop.connect_read_pipe`, or the constant
+ :const:`subprocess.PIPE` (the default). By default a new pipe will be
+ created and connected.
+
+ * *stderr*: Either a file-like object representing the pipe to be connected
+ to the subprocess's standard error stream using
+ :meth:`~BaseEventLoop.connect_read_pipe`, or one of the constants
+ :const:`subprocess.PIPE` (the default) or :const:`subprocess.STDOUT`.
+ By default a new pipe will be created and connected. When
+ :const:`subprocess.STDOUT` is specified, the subprocess's standard error
+ stream will be connected to the same pipe as the standard output stream.
+
+ * All other keyword arguments are passed to :class:`subprocess.Popen`
+ without interpretation, except for *bufsize*, *universal_newlines* and
+ *shell*, which should not be specified at all.
+
+ Returns a pair of ``(transport, protocol)``, where *transport* is an
+ instance of :class:`BaseSubprocessTransport`.
+
+ This method is a :ref:`coroutine <coroutine>`.
+
+ See the constructor of the :class:`subprocess.Popen` class for parameters.
+
+.. method:: BaseEventLoop.subprocess_shell(protocol_factory, cmd, \*, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, \*\*kwargs)
+
+ Create a subprocess from *cmd*, which is a string using the platform's
+ "shell" syntax. This is similar to the standard library
+ :class:`subprocess.Popen` class called with ``shell=True``.
+
+ See :meth:`~BaseEventLoop.subprocess_exec` for more details about
+ the remaining arguments.
+
+ Returns a pair of ``(transport, protocol)``, where *transport* is an
+ instance of :class:`BaseSubprocessTransport`.
+
+ This method is a :ref:`coroutine <coroutine>`.
+
+ See the constructor of the :class:`subprocess.Popen` class for parameters.
+
+.. seealso::
+
+ The :meth:`BaseEventLoop.connect_read_pipe` and
+ :meth:`BaseEventLoop.connect_write_pipe` methods.
+
+
+Constants
+---------
+
+.. data:: asyncio.subprocess.PIPE
+
+ Special value that can be used as the *stdin*, *stdout* or *stderr* argument
+ to :func:`create_subprocess_shell` and :func:`create_subprocess_exec` and
+ indicates that a pipe to the standard stream should be opened.
+
+.. data:: asyncio.subprocess.STDOUT
+
+ Special value that can be used as the *stderr* argument to
+ :func:`create_subprocess_shell` and :func:`create_subprocess_exec` and
+ indicates that standard error should go into the same handle as standard
+ output.
+
+.. data:: asyncio.subprocess.DEVNULL
+
+ Special value that can be used as the *stdin*, *stdout* or *stderr* argument
+ to :func:`create_subprocess_shell` and :func:`create_subprocess_exec` and
+ indicates that the special file :data:`os.devnull` will be used.
+
+
+Process
+-------
+
+.. class:: asyncio.subprocess.Process
+
+ .. attribute:: pid
+
+ The identifier of the process.
+
+ Note that if you set the *shell* argument to ``True``, this is the
+ process identifier of the spawned shell.
+
+ .. attribute:: returncode
+
+ Return code of the process when it exited. A ``None`` value indicates
+ that the process has not terminated yet.
+
+ A negative value ``-N`` indicates that the child was terminated by signal
+ ``N`` (Unix only).
+
+ .. attribute:: stdin
+
+ Standard input stream (write), ``None`` if the process was created with
+ ``stdin=None``.
+
+ .. attribute:: stdout
+
+ Standard output stream (read), ``None`` if the process was created with
+ ``stdout=None``.
+
+ .. attribute:: stderr
+
+ Standard error stream (read), ``None`` if the process was created with
+ ``stderr=None``.
+
+ .. method:: communicate(input=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 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.
+
+ :meth:`communicate` returns a tuple ``(stdoutdata, stderrdata)``.
+
+ Note that if you want to send data to the process's stdin, you need to
+ create the Process object with ``stdin=PIPE``. Similarly, to get anything
+ other than ``None`` in the result tuple, you need to give ``stdout=PIPE``
+ and/or ``stderr=PIPE`` too.
+
+ .. note::
+
+ The data read is buffered in memory, so do not use this method if the
+ data size is large or unlimited.
+
+ This method is a :ref:`coroutine <coroutine>`.
+
+ .. method:: kill()
+
+ Kills the child. On Posix OSs the function sends :py:data:`SIGKILL` to
+ the child. On Windows :meth:`kill` is an alias for :meth:`terminate`.
+
+ .. method:: send_signal(signal)
+
+ Sends the signal *signal* to the child process.
+
+ .. note::
+
+ On Windows, :py:data:`SIGTERM` is an alias for :meth:`terminate`.
+ ``CTRL_C_EVENT`` and ``CTRL_BREAK_EVENT`` can be sent to processes
+ started with a *creationflags* parameter which includes
+ ``CREATE_NEW_PROCESS_GROUP``.
+
+ .. method:: terminate()
+
+ Stop the child. On Posix OSs the method sends :py:data:`signal.SIGTERM`
+ to the child. On Windows the Win32 API function
+ :c:func:`TerminateProcess` is called to stop the child.
+
+ .. method:: wait():
+
+ Wait for child process to terminate. Set and return :attr:`returncode`
+ attribute.
+
+ This method is a :ref:`coroutine <coroutine>`.
+
+
+Example
+-------
+
+Implement a function similar to :func:`subprocess.getstatusoutput`, except that
+it does not use a shell. Get the output of the "python -m platform" command and
+display the output::
+
+ import asyncio
+ import os
+ import sys
+ from asyncio import subprocess
+
+ @asyncio.coroutine
+ def getstatusoutput(*args):
+ proc = yield from asyncio.create_subprocess_exec(
+ *args,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ try:
+ stdout, _ = yield from proc.communicate()
+ except:
+ proc.kill()
+ yield from proc.wait()
+ raise
+ exitcode = yield from proc.wait()
+ return (exitcode, stdout)
+
+ if os.name == 'nt':
+ loop = asyncio.ProactorEventLoop()
+ asyncio.set_event_loop(loop)
+ else:
+ loop = asyncio.get_event_loop()
+ coro = getstatusoutput(sys.executable, '-m', 'platform')
+ exitcode, stdout = loop.run_until_complete(coro)
+ if not exitcode:
+ stdout = stdout.decode('ascii').rstrip()
+ print("Platform: %s" % stdout)
+ else:
+ print("Python failed with exit code %s:" % exitcode)
+ sys.stdout.flush()
+ sys.stdout.buffer.flush()
+ sys.stdout.buffer.write(stdout)
+ sys.stdout.buffer.flush()
+ loop.close()
diff --git a/Doc/library/asyncio-sync.rst b/Doc/library/asyncio-sync.rst
new file mode 100644
index 0000000..a299f09
--- /dev/null
+++ b/Doc/library/asyncio-sync.rst
@@ -0,0 +1,410 @@
+.. currentmodule:: asyncio
+.. _asyncio-sync:
+
+Synchronization primitives
+==========================
+
+Locks
+-----
+
+Lock
+^^^^
+
+.. class:: Lock(\*, loop=None)
+
+ Primitive lock objects.
+
+ A primitive lock is a synchronization primitive that is not owned by a
+ particular coroutine when locked. A primitive lock is in one of two states,
+ 'locked' or 'unlocked'.
+
+ It is created in the unlocked state. It has two basic methods, :meth:`acquire`
+ and :meth:`release`. When the state is unlocked, acquire() changes the state to
+ locked and returns immediately. When the state is locked, acquire() blocks
+ until a call to release() in another coroutine changes it to unlocked, then
+ the acquire() call resets it to locked and returns. The release() method
+ should only be called in the locked state; it changes the state to unlocked
+ and returns immediately. If an attempt is made to release an unlocked lock,
+ a :exc:`RuntimeError` will be raised.
+
+ When more than one coroutine is blocked in acquire() waiting for the state
+ to turn to unlocked, only one coroutine proceeds when a release() call
+ resets the state to unlocked; first coroutine which is blocked in acquire()
+ is being processed.
+
+ :meth:`acquire` is a coroutine and should be called with ``yield from``.
+
+ Locks also support the context manager protocol. ``(yield from lock)``
+ should be used as context manager expression.
+
+ Usage::
+
+ lock = Lock()
+ ...
+ yield from lock
+ try:
+ ...
+ finally:
+ lock.release()
+
+ Context manager usage::
+
+ lock = Lock()
+ ...
+ with (yield from lock):
+ ...
+
+ Lock objects can be tested for locking state::
+
+ if not lock.locked():
+ yield from lock
+ else:
+ # lock is acquired
+ ...
+
+ .. method:: locked()
+
+ Return ``True`` if the lock is acquired.
+
+ .. method:: acquire()
+
+ Acquire a lock.
+
+ This method blocks until the lock is unlocked, then sets it to locked and
+ returns ``True``.
+
+ This method is a :ref:`coroutine <coroutine>`.
+
+ .. method:: release()
+
+ Release a lock.
+
+ When the lock is locked, reset it to unlocked, and return. If any other
+ coroutines are blocked waiting for the lock to become unlocked, allow
+ exactly one of them to proceed.
+
+ When invoked on an unlocked lock, a :exc:`RuntimeError` is raised.
+
+ There is no return value.
+
+
+Event
+^^^^^
+
+.. class:: Event(\*, loop=None)
+
+ An Event implementation, asynchronous equivalent to :class:`threading.Event`.
+
+ Class implementing event objects. An event manages a flag that can be set to
+ true with the :meth:`set` method and reset to false with the :meth:`clear`
+ method. The :meth:`wait` method blocks until the flag is true. The flag is
+ initially false.
+
+ .. method:: clear()
+
+ Reset the internal flag to false. Subsequently, coroutines calling
+ :meth:`wait` will block until :meth:`set` is called to set the internal
+ flag to true again.
+
+ .. method:: is_set()
+
+ Return ``True`` if and only if the internal flag is true.
+
+ .. method:: set()
+
+ Set the internal flag to true. All coroutines waiting for it to become
+ true are awakened. Coroutine that call :meth:`wait` once the flag is true
+ will not block at all.
+
+ .. method:: wait()
+
+ Block until the internal flag is true.
+
+ If the internal flag is true on entry, return ``True`` immediately.
+ Otherwise, block until another coroutine calls :meth:`set` to set the
+ flag to true, then return ``True``.
+
+ This method is a :ref:`coroutine <coroutine>`.
+
+
+Condition
+^^^^^^^^^
+
+.. class:: Condition(\*, loop=None)
+
+ A Condition implementation, asynchronous equivalent to
+ :class:`threading.Condition`.
+
+ This class implements condition variable objects. A condition variable
+ allows one or more coroutines to wait until they are notified by another
+ coroutine.
+
+ A new :class:`Lock` object is created and used as the underlying lock.
+
+ .. method:: acquire()
+
+ Acquire the underlying lock.
+
+ This method blocks until the lock is unlocked, then sets it to locked and
+ returns ``True``.
+
+ This method is a :ref:`coroutine <coroutine>`.
+
+ .. method:: notify(n=1)
+
+ By default, wake up one coroutine waiting on this condition, if any.
+ If the calling coroutine has not acquired the lock when this method is
+ called, a :exc:`RuntimeError` is raised.
+
+ This method wakes up at most *n* of the coroutines waiting for the
+ condition variable; it is a no-op if no coroutines are waiting.
+
+ .. note::
+
+ An awakened coroutine does not actually return from its :meth:`wait`
+ call until it can reacquire the lock. Since :meth:`notify` does not
+ release the lock, its caller should.
+
+ .. method:: locked()
+
+ Return ``True`` if the underlying lock is acquired.
+
+ .. method:: notify_all()
+
+ Wake up all threads waiting on this condition. This method acts like
+ :meth:`notify`, but wakes up all waiting threads instead of one. If the
+ calling thread has not acquired the lock when this method is called, a
+ :exc:`RuntimeError` is raised.
+
+ .. method:: release()
+
+ Release the underlying lock.
+
+ When the lock is locked, reset it to unlocked, and return. If any other
+ coroutines are blocked waiting for the lock to become unlocked, allow
+ exactly one of them to proceed.
+
+ When invoked on an unlocked lock, a :exc:`RuntimeError` is raised.
+
+ There is no return value.
+
+ .. method:: wait()
+
+ Wait until notified.
+
+ If the calling coroutine has not acquired the lock when this method is
+ called, a :exc:`RuntimeError` is raised.
+
+ This method releases the underlying lock, and then blocks until it is
+ awakened by a :meth:`notify` or :meth:`notify_all` call for the same
+ condition variable in another coroutine. Once awakened, it re-acquires
+ the lock and returns ``True``.
+
+ This method is a :ref:`coroutine <coroutine>`.
+
+ .. method:: wait_for(predicate)
+
+ Wait until a predicate becomes true.
+
+ The predicate should be a callable which result will be interpreted as a
+ boolean value. The final predicate value is the return value.
+
+ This method is a :ref:`coroutine <coroutine>`.
+
+
+Semaphores
+----------
+
+Semaphore
+^^^^^^^^^
+
+.. class:: Semaphore(value=1, \*, loop=None)
+
+ A Semaphore implementation.
+
+ A semaphore manages an internal counter which is decremented by each
+ :meth:`acquire` call and incremented by each :meth:`release` call. The
+ counter can never go below zero; when :meth:`acquire` finds that it is zero,
+ it blocks, waiting until some other thread calls :meth:`release`.
+
+ Semaphores also support the context manager protocol.
+
+ The optional argument gives the initial value for the internal counter; it
+ defaults to ``1``. If the value given is less than ``0``, :exc:`ValueError`
+ is raised.
+
+ .. method:: acquire()
+
+ Acquire a semaphore.
+
+ If the internal counter is larger than zero on entry, decrement it by one
+ and return ``True`` immediately. If it is zero on entry, block, waiting
+ until some other coroutine has called :meth:`release` to make it larger
+ than ``0``, and then return ``True``.
+
+ This method is a :ref:`coroutine <coroutine>`.
+
+ .. method:: locked()
+
+ Returns ``True`` if semaphore can not be acquired immediately.
+
+ .. method:: release()
+
+ Release a semaphore, incrementing the internal counter by one. When it
+ was zero on entry and another coroutine is waiting for it to become
+ larger than zero again, wake up that coroutine.
+
+
+BoundedSemaphore
+^^^^^^^^^^^^^^^^
+
+.. class:: BoundedSemaphore(value=1, \*, loop=None)
+
+ A bounded semaphore implementation. Inherit from :class:`Semaphore`.
+
+ This raises :exc:`ValueError` in :meth:`~Semaphore.release` if it would
+ increase the value above the initial value.
+
+
+Queues
+------
+
+Queue
+^^^^^
+
+.. class:: Queue(maxsize=0, \*, loop=None)
+
+ A queue, useful for coordinating producer and consumer coroutines.
+
+ If *maxsize* is less than or equal to zero, the queue size is infinite. If
+ it is an integer greater than ``0``, then ``yield from put()`` will block
+ when the queue reaches *maxsize*, until an item is removed by :meth:`get`.
+
+ Unlike the standard library :mod:`queue`, you can reliably know this Queue's
+ size with :meth:`qsize`, since your single-threaded asyncio application won't
+ be interrupted between calling :meth:`qsize` and doing an operation on the
+ Queue.
+
+ .. method:: empty()
+
+ Return ``True`` if the queue is empty, ``False`` otherwise.
+
+ .. method:: full()
+
+ Return ``True`` if there are maxsize items in the queue.
+
+ .. note::
+
+ If the Queue was initialized with ``maxsize=0`` (the default), then
+ :meth:`full()` is never ``True``.
+
+ .. method:: get()
+
+ Remove and return an item from the queue.
+
+ If you yield from :meth:`get()`, wait until a item is available.
+
+ This method is a :ref:`coroutine <coroutine>`.
+
+ .. method:: get_nowait()
+
+ Remove and return an item from the queue.
+
+ Return an item if one is immediately available, else raise
+ :exc:`QueueEmpty`.
+
+ .. method:: put(item)
+
+ Put an item into the queue.
+
+ If you yield from ``put()``, wait until a free slot is available before
+ adding item.
+
+ This method is a :ref:`coroutine <coroutine>`.
+
+ .. method:: put_nowait(item)
+
+ Put an item into the queue without blocking.
+
+ If no free slot is immediately available, raise :exc:`QueueFull`.
+
+ .. method:: qsize()
+
+ Number of items in the queue.
+
+ .. attribute:: maxsize
+
+ Number of items allowed in the queue.
+
+
+PriorityQueue
+^^^^^^^^^^^^^
+
+.. class:: PriorityQueue
+
+ A subclass of :class:`Queue`; retrieves entries in priority order (lowest
+ first).
+
+ Entries are typically tuples of the form: (priority number, data).
+
+
+LifoQueue
+^^^^^^^^^
+
+.. class:: LifoQueue
+
+ A subclass of :class:`Queue` that retrieves most recently added entries
+ first.
+
+
+JoinableQueue
+^^^^^^^^^^^^^
+
+.. class:: JoinableQueue
+
+ A subclass of :class:`Queue` with :meth:`task_done` and :meth:`join`
+ methods.
+
+ .. method:: join()
+
+ Block until all items in the queue have been gotten and processed.
+
+ The count of unfinished tasks goes up whenever an item is added to the
+ queue. The count goes down whenever a consumer thread calls
+ :meth:`task_done` to indicate that the item was retrieved and all work on
+ it is complete. When the count of unfinished tasks drops to zero,
+ :meth:`join` unblocks.
+
+ This method is a :ref:`coroutine <coroutine>`.
+
+ .. method:: task_done()
+
+ Indicate that a formerly enqueued task is complete.
+
+ Used by queue consumers. For each :meth:`~Queue.get` used to fetch a task, a
+ subsequent call to :meth:`task_done` tells the queue that the processing
+ on the task is complete.
+
+ If a :meth:`join` is currently blocking, it will resume when all items
+ have been processed (meaning that a :meth:`task_done` call was received
+ for every item that had been :meth:`~Queue.put` into the queue).
+
+ Raises :exc:`ValueError` if called more times than there were items
+ placed in the queue.
+
+
+Exceptions
+^^^^^^^^^^
+
+.. exception:: QueueEmpty
+
+ Exception raised when non-blocking :meth:`~Queue.get` (or
+ :meth:`~Queue.get_nowait`) is called
+ on a :class:`Queue` object which is empty.
+
+
+.. exception:: QueueFull
+
+ Exception raised when non-blocking :meth:`~Queue.put` (or
+ :meth:`~Queue.put_nowait`) is called
+ on a :class:`Queue` object which is full.
diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst
new file mode 100644
index 0000000..4e5526e
--- /dev/null
+++ b/Doc/library/asyncio-task.rst
@@ -0,0 +1,545 @@
+.. currentmodule:: asyncio
+
+Tasks and coroutines
+====================
+
+.. _coroutine:
+
+Coroutines
+----------
+
+A coroutine is a generator that follows certain conventions. For
+documentation purposes, all coroutines should be decorated with
+``@asyncio.coroutine``, but this cannot be strictly enforced.
+
+Coroutines use the ``yield from`` syntax introduced in :pep:`380`,
+instead of the original ``yield`` syntax.
+
+The word "coroutine", like the word "generator", is used for two
+different (though related) concepts:
+
+- The function that defines a coroutine (a function definition
+ decorated with ``@asyncio.coroutine``). If disambiguation is needed
+ we will call this a *coroutine function* (:func:`iscoroutinefunction`
+ returns ``True``).
+
+- The object obtained by calling a coroutine function. This object
+ represents a computation or an I/O operation (usually a combination)
+ that will complete eventually. If disambiguation is needed we will
+ call it a *coroutine object* (:func:`iscoroutine` returns ``True``).
+
+Things a coroutine can do:
+
+- ``result = yield from future`` -- suspends the coroutine until the
+ future is done, then returns the future's result, or raises an
+ exception, which will be propagated. (If the future is cancelled,
+ it will raise a ``CancelledError`` exception.) Note that tasks are
+ futures, and everything said about futures also applies to tasks.
+
+- ``result = yield from coroutine`` -- wait for another coroutine to
+ produce a result (or raise an exception, which will be propagated).
+ The ``coroutine`` expression must be a *call* to another coroutine.
+
+- ``return expression`` -- produce a result to the coroutine that is
+ waiting for this one using ``yield from``.
+
+- ``raise exception`` -- raise an exception in the coroutine that is
+ waiting for this one using ``yield from``.
+
+Calling a coroutine does not start its code running -- it is just a
+generator, and the coroutine object returned by the call is really a
+generator object, which doesn't do anything until you iterate over it.
+In the case of a coroutine object, there are two basic ways to start
+it running: call ``yield from coroutine`` from another coroutine
+(assuming the other coroutine is already running!), or convert it to a
+:class:`Task`.
+
+Coroutines (and tasks) can only run when the event loop is running.
+
+.. decorator:: coroutine
+
+ Decorator to mark coroutines.
+
+ If the coroutine is not yielded from before it is destroyed, an error
+ message is logged. See :ref:`Detect coroutines never scheduled
+ <asyncio-coroutine-not-scheduled>`.
+
+.. note::
+
+ In this documentation, some methods are documented as coroutines,
+ even if they are plain Python functions returning a :class:`Future`.
+ This is intentional to have a freedom of tweaking the implementation
+ of these functions in the future. If such a function is needed to be
+ used in a callback-style code, wrap its result with :func:`async`.
+
+
+.. _asyncio-hello-world-coroutine:
+
+Example: "Hello World" coroutine
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Print ``"Hello World"`` every two seconds using a coroutine::
+
+ import asyncio
+
+ @asyncio.coroutine
+ def greet_every_two_seconds():
+ while True:
+ print('Hello World')
+ yield from asyncio.sleep(2)
+
+ loop = asyncio.get_event_loop()
+ loop.run_until_complete(greet_every_two_seconds())
+
+.. seealso::
+
+ :ref:`Hello World example using a callback <asyncio-hello-world-callback>`.
+
+
+Example: Chain coroutines
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Example chaining coroutines::
+
+ import asyncio
+
+ @asyncio.coroutine
+ def compute(x, y):
+ print("Compute %s + %s ..." % (x, y))
+ yield from asyncio.sleep(1.0)
+ return x + y
+
+ @asyncio.coroutine
+ def print_sum(x, y):
+ result = yield from compute(x, y)
+ print("%s + %s = %s" % (x, y, result))
+
+ loop = asyncio.get_event_loop()
+ loop.run_until_complete(print_sum(1, 2))
+ loop.close()
+
+``compute()`` is chained to ``print_sum()``: ``print_sum()`` coroutine waits
+until ``compute()`` is completed before returning its result.
+
+Sequence diagram of the example:
+
+.. image:: tulip_coro.png
+ :align: center
+
+The "Task" is created by the :meth:`BaseEventLoop.run_until_complete` method
+when it gets a coroutine object instead of a task.
+
+The diagram shows the control flow, it does not describe exactly how things
+work internally. For example, the sleep coroutine creates an internal future
+which uses :meth:`BaseEventLoop.call_later` to wake up the task in 1 second.
+
+
+InvalidStateError
+-----------------
+
+.. exception:: InvalidStateError
+
+ The operation is not allowed in this state.
+
+
+Future
+------
+
+.. class:: Future(\*, loop=None)
+
+ This class is *almost* compatible with :class:`concurrent.futures.Future`.
+
+ Differences:
+
+ - :meth:`result` and :meth:`exception` do not take a timeout argument and
+ raise an exception when the future isn't done yet.
+
+ - Callbacks registered with :meth:`add_done_callback` are always called
+ via the event loop's :meth:`~BaseEventLoop.call_soon_threadsafe`.
+
+ - This class is not compatible with the :func:`~concurrent.futures.wait` and
+ :func:`~concurrent.futures.as_completed` functions in the
+ :mod:`concurrent.futures` package.
+
+ .. method:: cancel()
+
+ Cancel the future and schedule callbacks.
+
+ If the future is already done or cancelled, return ``False``. Otherwise,
+ change the future's state to cancelled, schedule the callbacks and return
+ ``True``.
+
+ .. method:: cancelled()
+
+ Return ``True`` if the future was cancelled.
+
+ .. method:: done()
+
+ Return True if the future is done.
+
+ Done means either that a result / exception are available, or that the
+ future was cancelled.
+
+ .. method:: result()
+
+ Return the result this future represents.
+
+ If the future has been cancelled, raises :exc:`CancelledError`. If the
+ future's result isn't yet available, raises :exc:`InvalidStateError`. If
+ the future is done and has an exception set, this exception is raised.
+
+ .. method:: exception()
+
+ Return the exception that was set on this future.
+
+ The exception (or ``None`` if no exception was set) is returned only if
+ the future is done. If the future has been cancelled, raises
+ :exc:`CancelledError`. If the future isn't done yet, raises
+ :exc:`InvalidStateError`.
+
+ .. method:: add_done_callback(fn)
+
+ Add a callback to be run when the future becomes done.
+
+ The callback is called with a single argument - the future object. If the
+ future is already done when this is called, the callback is scheduled
+ with :meth:`~BaseEventLoop.call_soon`.
+
+ .. method:: remove_done_callback(fn)
+
+ Remove all instances of a callback from the "call when done" list.
+
+ Returns the number of callbacks removed.
+
+ .. method:: set_result(result)
+
+ Mark the future done and set its result.
+
+ If the future is already done when this method is called, raises
+ :exc:`InvalidStateError`.
+
+ .. method:: set_exception(exception)
+
+ Mark the future done and set an exception.
+
+ If the future is already done when this method is called, raises
+ :exc:`InvalidStateError`.
+
+
+Example: Future with run_until_complete()
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Example combining a :class:`Future` and a :ref:`coroutine function
+<coroutine>`::
+
+ import asyncio
+
+ @asyncio.coroutine
+ def slow_operation(future):
+ yield from asyncio.sleep(1)
+ future.set_result('Future is done!')
+
+ loop = asyncio.get_event_loop()
+ future = asyncio.Future()
+ asyncio.Task(slow_operation(future))
+ loop.run_until_complete(future)
+ print(future.result())
+ loop.close()
+
+The coroutine function is responsible of the computation (which takes 1 second)
+and it stores the result into the future. The
+:meth:`~BaseEventLoop.run_until_complete` method waits for the completion of
+the future.
+
+.. note::
+ The :meth:`~BaseEventLoop.run_until_complete` method uses internally the
+ :meth:`~Future.add_done_callback` method to be notified when the future is
+ done.
+
+
+Example: Future with run_forever()
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The previous example can be written differently using the
+:meth:`Future.add_done_callback` method to describe explicitly the control
+flow::
+
+ import asyncio
+
+ @asyncio.coroutine
+ def slow_operation(future):
+ yield from asyncio.sleep(1)
+ future.set_result('Future is done!')
+
+ def got_result(future):
+ print(future.result())
+ loop.stop()
+
+ loop = asyncio.get_event_loop()
+ future = asyncio.Future()
+ asyncio.Task(slow_operation(future))
+ future.add_done_callback(got_result)
+ try:
+ loop.run_forever()
+ finally:
+ loop.close()
+
+In this example, the future is responsible to display the result and to stop
+the loop.
+
+.. note::
+ The "slow_operation" coroutine object is only executed when the event loop
+ starts running, so it is possible to add a "done callback" to the future
+ after creating the task scheduling the coroutine object.
+
+
+
+Task
+----
+
+.. class:: Task(coro, \*, loop=None)
+
+ A coroutine object wrapped in a :class:`Future`. Subclass of :class:`Future`.
+
+ .. classmethod:: all_tasks(loop=None)
+
+ Return a set of all tasks for an event loop.
+
+ By default all tasks for the current event loop are returned.
+
+ .. classmethod:: current_task(loop=None)
+
+ Return the currently running task in an event loop or ``None``.
+
+ By default the current task for the current event loop is returned.
+
+ ``None`` is returned when called not in the context of a :class:`Task`.
+
+ .. method:: get_stack(self, \*, limit=None)
+
+ Return the list of stack frames for this task's coroutine.
+
+ If the coroutine is active, this returns the stack where it is suspended.
+ If the coroutine has completed successfully or was cancelled, this
+ returns an empty list. If the coroutine was terminated by an exception,
+ this returns the list of traceback frames.
+
+ The frames are always ordered from oldest to newest.
+
+ The optional limit gives the maximum number of frames to return; by
+ default all available frames are returned. Its meaning differs depending
+ on whether a stack or a traceback is returned: the newest frames of a
+ stack are returned, but the oldest frames of a traceback are returned.
+ (This matches the behavior of the traceback module.)
+
+ For reasons beyond our control, only one stack frame is returned for a
+ suspended coroutine.
+
+ .. method:: print_stack(\*, limit=None, file=None)
+
+ Print the stack or traceback for this task's coroutine.
+
+ This produces output similar to that of the traceback module, for the
+ frames retrieved by get_stack(). The limit argument is passed to
+ get_stack(). The file argument is an I/O stream to which the output
+ goes; by default it goes to sys.stderr.
+
+
+Example: Parallel execution of tasks
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Example executing 3 tasks (A, B, C) in parallel::
+
+ import asyncio
+
+ @asyncio.coroutine
+ def factorial(name, number):
+ f = 1
+ for i in range(2, number+1):
+ print("Task %s: Compute factorial(%s)..." % (name, i))
+ yield from asyncio.sleep(1)
+ f *= i
+ print("Task %s: factorial(%s) = %s" % (name, number, f))
+
+ tasks = [
+ asyncio.Task(factorial("A", 2)),
+ asyncio.Task(factorial("B", 3)),
+ asyncio.Task(factorial("C", 4))]
+
+ loop = asyncio.get_event_loop()
+ loop.run_until_complete(asyncio.wait(tasks))
+ loop.close()
+
+Output::
+
+ Task A: Compute factorial(2)...
+ Task B: Compute factorial(2)...
+ Task C: Compute factorial(2)...
+ Task A: factorial(2) = 2
+ Task B: Compute factorial(3)...
+ Task C: Compute factorial(3)...
+ Task B: factorial(3) = 6
+ Task C: Compute factorial(4)...
+ Task C: factorial(4) = 24
+
+A task is automatically scheduled for execution when it is created. The event
+loop stops when all tasks are done.
+
+
+Task functions
+--------------
+
+.. note::
+
+ In the functions below, the optional *loop* argument allows to explicitly set
+ the event loop object used by the underlying task or coroutine. If it's
+ not provided, the default event loop is used.
+
+.. function:: as_completed(fs, \*, loop=None, timeout=None)
+
+ Return an iterator whose values, when waited for, are :class:`Future`
+ instances.
+
+ Raises :exc:`TimeoutError` if the timeout occurs before all Futures are done.
+
+ Example::
+
+ for f in as_completed(fs):
+ result = yield from f # The 'yield from' may raise
+ # Use result
+
+ .. note::
+
+ The futures ``f`` are not necessarily members of fs.
+
+.. function:: async(coro_or_future, \*, loop=None)
+
+ Wrap a :ref:`coroutine object <coroutine>` in a future.
+
+ If the argument is a :class:`Future`, it is returned directly.
+
+.. function:: gather(\*coros_or_futures, loop=None, return_exceptions=False)
+
+ Return a future aggregating results from the given coroutine objects or
+ futures.
+
+ All futures must share the same event loop. If all the tasks are done
+ successfully, the returned future's result is the list of results (in the
+ order of the original sequence, not necessarily the order of results
+ arrival). If *return_exceptions* is True, exceptions in the tasks are
+ treated the same as successful results, and gathered in the result list;
+ otherwise, the first raised exception will be immediately propagated to the
+ returned future.
+
+ Cancellation: if the outer Future is cancelled, all children (that have not
+ completed yet) are also cancelled. If any child is cancelled, this is
+ treated as if it raised :exc:`~concurrent.futures.CancelledError` -- the
+ outer Future is *not* cancelled in this case. (This is to prevent the
+ cancellation of one child to cause other children to be cancelled.)
+
+.. function:: iscoroutine(obj)
+
+ Return ``True`` if *obj* is a :ref:`coroutine object <coroutine>`.
+
+.. function:: iscoroutinefunction(obj)
+
+ Return ``True`` if *func* is a decorated :ref:`coroutine function
+ <coroutine>`.
+
+.. function:: sleep(delay, result=None, \*, loop=None)
+
+ Create a :ref:`coroutine <coroutine>` that completes after a given
+ time (in seconds). If *result* is provided, it is produced to the caller
+ when the coroutine completes.
+
+ The resolution of the sleep depends on the :ref:`granularity of the event
+ loop <asyncio-delayed-calls>`.
+
+.. function:: shield(arg, \*, loop=None)
+
+ Wait for a future, shielding it from cancellation.
+
+ The statement::
+
+ res = yield from shield(something())
+
+ is exactly equivalent to the statement::
+
+ res = yield from something()
+
+ *except* that if the coroutine containing it is cancelled, the task running
+ in ``something()`` is not cancelled. From the point of view of
+ ``something()``, the cancellation did not happen. But its caller is still
+ cancelled, so the yield-from expression still raises
+ :exc:`~concurrent.futures.CancelledError`. Note: If ``something()`` is
+ cancelled by other means this will still cancel ``shield()``.
+
+ If you want to completely ignore cancellation (not recommended) you can
+ combine ``shield()`` with a try/except clause, as follows::
+
+ try:
+ res = yield from shield(something())
+ except CancelledError:
+ res = None
+
+.. function:: wait(futures, \*, loop=None, timeout=None, return_when=ALL_COMPLETED)
+
+ Wait for the Futures and coroutine objects given by the sequence *futures*
+ to complete. Coroutines will be wrapped in Tasks. Returns two sets of
+ :class:`Future`: (done, pending).
+
+ *timeout* can be used to control the maximum number of seconds to wait before
+ returning. *timeout* can be an int or float. If *timeout* is not specified
+ or ``None``, there is no limit to the wait time.
+
+ *return_when* indicates when this function should return. It must be one of
+ the following constants of the :mod:`concurrent.futures` module:
+
+ .. tabularcolumns:: |l|L|
+
+ +-----------------------------+----------------------------------------+
+ | Constant | Description |
+ +=============================+========================================+
+ | :const:`FIRST_COMPLETED` | The function will return when any |
+ | | future finishes or is cancelled. |
+ +-----------------------------+----------------------------------------+
+ | :const:`FIRST_EXCEPTION` | The function will return when any |
+ | | future finishes by raising an |
+ | | exception. If no future raises an |
+ | | exception then it is equivalent to |
+ | | :const:`ALL_COMPLETED`. |
+ +-----------------------------+----------------------------------------+
+ | :const:`ALL_COMPLETED` | The function will return when all |
+ | | futures finish or are cancelled. |
+ +-----------------------------+----------------------------------------+
+
+ This function is a :ref:`coroutine <coroutine>`.
+
+ Usage::
+
+ done, pending = yield from asyncio.wait(fs)
+
+ .. note::
+
+ This does not raise :exc:`TimeoutError`! Futures that aren't done when
+ the timeout occurs are returned in the second set.
+
+
+.. function:: wait_for(fut, timeout, \*, loop=None)
+
+ Wait for the single :class:`Future` or :ref:`coroutine object <coroutine>`
+ to complete, with timeout. If *timeout* is ``None``, block until the future
+ completes.
+
+ Coroutine will be wrapped in :class:`Task`.
+
+ Returns result of the Future or coroutine. When a timeout occurs, it
+ cancels the task and raises :exc:`TimeoutError`. To avoid the task
+ cancellation, wrap it in :func:`shield`.
+
+ This function is a :ref:`coroutine <coroutine>`.
+
+ Usage::
+
+ result = yield from asyncio.wait_for(fut, 60.0)
+
diff --git a/Doc/library/asyncio.rst b/Doc/library/asyncio.rst
new file mode 100644
index 0000000..23731b1
--- /dev/null
+++ b/Doc/library/asyncio.rst
@@ -0,0 +1,59 @@
+:mod:`asyncio` -- Asynchronous I/O, event loop, coroutines and tasks
+====================================================================
+
+.. module:: asyncio
+ :synopsis: Asynchronous I/O, event loop, coroutines and tasks.
+
+.. versionadded:: 3.4
+
+**Source code:** :source:`Lib/asyncio/`
+
+--------------
+
+This module provides infrastructure for writing single-threaded concurrent
+code using coroutines, multiplexing I/O access over sockets and other
+resources, running network clients and servers, and other related primitives.
+Here is a more detailed list of the package contents:
+
+* a pluggable :ref:`event loop <asyncio-event-loop>` with various system-specific
+ implementations;
+
+* :ref:`transport <asyncio-transport>` and :ref:`protocol <asyncio-protocol>` abstractions
+ (similar to those in `Twisted <http://twistedmatrix.com/>`_);
+
+* concrete support for TCP, UDP, SSL, subprocess pipes, delayed calls, and
+ others (some may be system-dependent);
+
+* a :class:`Future` class that mimics the one in the :mod:`concurrent.futures`
+ module, but adapted for use with the event loop;
+
+* coroutines and tasks based on ``yield from`` (:PEP:`380`), to help write
+ concurrent code in a sequential fashion;
+
+* cancellation support for :class:`Future`\s and coroutines;
+
+* :ref:`synchronization primitives <asyncio-sync>` for use between coroutines in
+ a single thread, mimicking those in the :mod:`threading` module;
+
+* an interface for passing work off to a threadpool, for times when
+ you absolutely, positively have to use a library that makes blocking
+ I/O calls.
+
+Table of content:
+
+.. toctree::
+ :maxdepth: 3
+
+ asyncio-eventloop.rst
+ asyncio-task.rst
+ asyncio-protocol.rst
+ asyncio-stream.rst
+ asyncio-subprocess.rst
+ asyncio-sync.rst
+ asyncio-dev.rst
+
+.. seealso::
+
+ The :mod:`asyncio` module was designed in the :PEP:`3156`. For a
+ motivational primer on transports and protocols, see :PEP:`3153`.
+
diff --git a/Doc/library/asyncore.rst b/Doc/library/asyncore.rst
index 1521e72..0adf8d9 100644
--- a/Doc/library/asyncore.rst
+++ b/Doc/library/asyncore.rst
@@ -13,6 +13,11 @@
--------------
+.. note::
+
+ This module exists for backwards compatibility only. For new code we
+ recommend using :mod:`asyncio`.
+
This module provides the basic infrastructure for writing asynchronous socket
service clients and servers.
diff --git a/Doc/library/audioop.rst b/Doc/library/audioop.rst
index edb3870..ce127aa 100644
--- a/Doc/library/audioop.rst
+++ b/Doc/library/audioop.rst
@@ -6,9 +6,14 @@
The :mod:`audioop` module contains some useful operations on sound fragments.
-It operates on sound fragments consisting of signed integer samples 8, 16 or 32
-bits wide, stored in bytes objects. All scalar items are integers, unless
-specified otherwise.
+It operates on sound fragments consisting of signed integer samples 8, 16, 24
+or 32 bits wide, stored in :term:`bytes-like object`\ s. All scalar items are
+integers, unless specified otherwise.
+
+.. versionchanged:: 3.4
+ Support for 24-bit samples was added.
+ All functions now accept any :term:`bytes-like object`.
+ String input now results in an immediate error.
.. index::
single: Intel/DVI ADPCM
@@ -35,7 +40,7 @@ The module defines the following variables and functions:
.. function:: add(fragment1, fragment2, width)
Return a fragment which is the addition of the two samples passed as parameters.
- *width* is the sample width in bytes, either ``1``, ``2`` or ``4``. Both
+ *width* is the sample width in bytes, either ``1``, ``2``, ``3`` or ``4``. Both
fragments should have the same length. Samples are truncated in case of overflow.
@@ -70,6 +75,14 @@ The module defines the following variables and functions:
sample. Samples wrap around in case of overflow.
+.. function:: byteswap(fragment, width)
+
+ "Byteswap" all samples in a fragment and returns the modified fragment.
+ Converts big-endian samples to little-endian and vice versa.
+
+ .. versionadded:: 3.4
+
+
.. function:: cross(fragment, width)
Return the number of zero crossings in the fragment passed as an argument.
@@ -133,19 +146,19 @@ The module defines the following variables and functions:
.. function:: lin2lin(fragment, width, newwidth)
- Convert samples between 1-, 2- and 4-byte formats.
+ Convert samples between 1-, 2-, 3- and 4-byte formats.
.. note::
- In some audio formats, such as .WAV files, 16 and 32 bit samples are
+ In some audio formats, such as .WAV files, 16, 24 and 32 bit samples are
signed, but 8 bit samples are unsigned. So when converting to 8 bit wide
samples for these formats, you need to also add 128 to the result::
new_frames = audioop.lin2lin(frames, old_width, 1)
new_frames = audioop.bias(new_frames, 1, 128)
- The same, in reverse, has to be applied when converting from 8 to 16 or 32
- bit width samples.
+ The same, in reverse, has to be applied when converting from 8 to 16, 24
+ or 32 bit width samples.
.. function:: lin2ulaw(fragment, width)
diff --git a/Doc/library/base64.rst b/Doc/library/base64.rst
index f0d11b0..02b4d7b 100644
--- a/Doc/library/base64.rst
+++ b/Doc/library/base64.rst
@@ -1,32 +1,42 @@
-:mod:`base64` --- RFC 3548: Base16, Base32, Base64 Data Encodings
-=================================================================
+:mod:`base64` --- Base16, Base32, Base64, Base85 Data Encodings
+===============================================================
.. module:: base64
- :synopsis: RFC 3548: Base16, Base32, Base64 Data Encodings
+ :synopsis: RFC 3548: Base16, Base32, Base64 Data Encodings;
+ Base85 and Ascii85
.. index::
pair: base64; encoding
single: MIME; base64 encoding
-This module provides data encoding and decoding as specified in :rfc:`3548`.
-This standard defines the Base16, Base32, and Base64 algorithms for encoding
-and decoding arbitrary binary strings into ASCII-only byte strings that can be
+This module provides functions for encoding binary data to printable
+ASCII characters and decoding such encodings back to binary data.
+It provides encoding and decoding functions for the encodings specified in
+in :rfc:`3548`, which defines the Base16, Base32, and Base64 algorithms,
+and for the de-facto standard Ascii85 and Base85 encodings.
+
+The :rfc:`3548` encodings are suitable for encoding binary data so that it can
safely sent by email, used as parts of URLs, or included as part of an HTTP
POST request. The encoding algorithm is not the same as the
:program:`uuencode` program.
-There are two interfaces provided by this module. The modern interface
-supports encoding and decoding ASCII byte string objects using all three
-alphabets. Additionally, the decoding functions of the modern interface also
-accept Unicode strings containing only ASCII characters. The legacy interface
-provides for encoding and decoding to and from file-like objects as well as
-byte strings, but only using the Base64 standard alphabet.
+There are two :rfc:`3548` interfaces provided by this module. The modern
+interface supports encoding and decoding ASCII byte string objects using all
+three :rfc:`3548` defined alphabets (normal, URL-safe, and filesystem-safe).
+Additionally, the decoding functions of the modern interface also accept
+Unicode strings containing only ASCII characters. The legacy interface provides
+for encoding and decoding to and from file-like objects as well as byte
+strings, but only using the Base64 standard alphabet.
.. versionchanged:: 3.3
ASCII-only Unicode strings are now accepted by the decoding functions of
the modern interface.
+.. versionchanged:: 3.4
+ Any :term:`bytes-like object`\ s are now accepted by all
+ encoding and decoding functions in this module. Ascii85/Base85 support added.
+
The modern interface provides:
.. function:: b64encode(s, altchars=None)
@@ -128,6 +138,76 @@ The modern interface provides:
string.
+.. function:: a85encode(s, *, foldspaces=False, wrapcol=0, pad=False, adobe=False)
+
+ Encode a byte string using Ascii85.
+
+ *s* is the string to encode. The encoded byte string is returned.
+
+ *foldspaces* is an optional flag that uses the special short sequence 'y'
+ instead of 4 consecutive spaces (ASCII 0x20) as supported by 'btoa'. This
+ feature is not supported by the "standard" Ascii85 encoding.
+
+ *wrapcol* controls whether the output should have newline ('\n')
+ characters added to it. If this is non-zero, each output line will be
+ at most this many characters long.
+
+ *pad* controls whether the input string is padded to a multiple of 4
+ before encoding. Note that the ``btoa`` implementation always pads.
+
+ *adobe* controls whether the encoded byte sequence is framed with ``<~``
+ and ``~>``, which is used by the Adobe implementation.
+
+ .. versionadded:: 3.4
+
+
+.. function:: a85decode(s, *, foldspaces=False, adobe=False, ignorechars=b' \t\n\r\v')
+
+ Decode an Ascii85 encoded byte string.
+
+ *s* is the byte string to decode.
+
+ *foldspaces* is a flag that specifies whether the 'y' short sequence
+ should be accepted as shorthand for 4 consecutive spaces (ASCII 0x20).
+ This feature is not supported by the "standard" Ascii85 encoding.
+
+ *adobe* controls whether the input sequence is in Adobe Ascii85 format
+ (i.e. is framed with <~ and ~>).
+
+ *ignorechars* should be a byte string containing characters to ignore
+ from the input. This should only contain whitespace characters, and by
+ default contains all whitespace characters in ASCII.
+
+ .. versionadded:: 3.4
+
+
+.. function:: b85encode(s, pad=False)
+
+ Encode a byte string using base85, as used in e.g. git-style binary
+ diffs.
+
+ If *pad* is true, the input is padded with "\\0" so its length is a
+ multiple of 4 characters before encoding.
+
+ .. versionadded:: 3.4
+
+
+.. function:: b85decode(b)
+
+ Decode base85-encoded byte string. Padding is implicitly removed, if
+ necessary.
+
+ .. versionadded:: 3.4
+
+
+.. note::
+ Both Base85 and Ascii85 have an expansion factor of 5 to 4 (5 Base85 or
+ Ascii85 characters can encode 4 binary bytes), while the better-known
+ Base64 has an expansion factor of 6 to 4. They are therefore more
+ efficient when space expensive. They differ by details such as the
+ character map used for encoding.
+
+
The legacy interface:
.. function:: decode(input, output)
diff --git a/Doc/library/bz2.rst b/Doc/library/bz2.rst
index b79bccd..488cda5 100644
--- a/Doc/library/bz2.rst
+++ b/Doc/library/bz2.rst
@@ -37,8 +37,8 @@ All of the classes in this module may safely be accessed from multiple threads.
file object to read from or write to.
The *mode* argument can be any of ``'r'``, ``'rb'``, ``'w'``, ``'wb'``,
- ``'a'``, or ``'ab'`` for binary mode, or ``'rt'``, ``'wt'``, or ``'at'`` for
- text mode. The default is ``'rb'``.
+ ``'x'``, ``'xb'``, ``'a'`` or ``'ab'`` for binary mode, or ``'rt'``,
+ ``'wt'``, ``'xt'``, or ``'at'`` for text mode. The default is ``'rb'``.
The *compresslevel* argument is an integer from 1 to 9, as for the
:class:`BZ2File` constructor.
@@ -54,6 +54,9 @@ All of the classes in this module may safely be accessed from multiple threads.
.. versionadded:: 3.3
+ .. versionchanged:: 3.4
+ The ``'x'`` (exclusive creation) mode was added.
+
.. class:: BZ2File(filename, mode='r', buffering=None, compresslevel=9)
@@ -64,8 +67,9 @@ All of the classes in this module may safely be accessed from multiple threads.
be used to read or write the compressed data.
The *mode* argument can be either ``'r'`` for reading (default), ``'w'`` for
- overwriting, or ``'a'`` for appending. These can equivalently be given as
- ``'rb'``, ``'wb'``, and ``'ab'`` respectively.
+ overwriting, ``'x'`` for exclusive creation, or ``'a'`` for appending. These
+ can equivalently be given as ``'rb'``, ``'wb'``, ``'xb'`` and ``'ab'``
+ respectively.
If *filename* is a file object (rather than an actual file name), a mode of
``'w'`` does not truncate the file, and is instead equivalent to ``'a'``.
@@ -113,6 +117,9 @@ All of the classes in this module may safely be accessed from multiple threads.
The ``'a'`` (append) mode was added, along with support for reading
multi-stream files.
+ .. versionchanged:: 3.4
+ The ``'x'`` (exclusive creation) mode was added.
+
Incremental (de)compression
---------------------------
diff --git a/Doc/library/cgi.rst b/Doc/library/cgi.rst
index c4e7c60..fa13145 100644
--- a/Doc/library/cgi.rst
+++ b/Doc/library/cgi.rst
@@ -142,9 +142,11 @@ If a field represents an uploaded file, accessing the value via the
method reads the entire file in memory as bytes. This may not be what you
want. You can test for an uploaded file by testing either the
:attr:`~FieldStorage.filename` attribute or the :attr:`~FieldStorage.file`
-attribute. You can then read the data at leisure from the :attr:`!file`
-attribute (the :func:`~io.RawIOBase.read` and :func:`~io.IOBase.readline`
-methods will return bytes)::
+attribute. You can then read the data from the :attr:`!file`
+attribute before it is automatically closed as part of the garbage collection of
+the :class:`FieldStorage` instance
+(the :func:`~io.RawIOBase.read` and :func:`~io.IOBase.readline` methods will
+return bytes)::
fileitem = form["userfile"]
if fileitem.file:
@@ -176,6 +178,11 @@ actually be instances of the class :class:`MiniFieldStorage`. In this case, the
A form submitted via POST that also has a query string will contain both
:class:`FieldStorage` and :class:`MiniFieldStorage` items.
+.. versionchanged:: 3.4
+ The :attr:`~FieldStorage.file` attribute is automatically closed upon the
+ garbage collection of the creating :class:`FieldStorage` instance.
+
+
Higher Level Interface
----------------------
diff --git a/Doc/library/code.rst b/Doc/library/code.rst
index e869004..5b5d7cc 100644
--- a/Doc/library/code.rst
+++ b/Doc/library/code.rst
@@ -132,12 +132,15 @@ interpreter objects as well as the following additions.
.. method:: InteractiveConsole.interact(banner=None)
- Closely emulate the interactive Python console. The optional banner argument
+ Closely emulate the interactive Python console. The optional *banner* argument
specify the banner to print before the first interaction; by default it prints a
banner similar to the one printed by the standard Python interpreter, followed
by the class name of the console object in parentheses (so as not to confuse
this with the real interpreter -- since it's so close!).
+ .. versionchanged:: 3.4
+ To suppress printing any banner, pass an empty string.
+
.. method:: InteractiveConsole.push(line)
diff --git a/Doc/library/codecs.rst b/Doc/library/codecs.rst
index 009ae26..3729dac 100644
--- a/Doc/library/codecs.rst
+++ b/Doc/library/codecs.rst
@@ -365,18 +365,23 @@ and implemented by all standard Python codecs:
| | in :pep:`383`. |
+-------------------------+-----------------------------------------------+
-In addition, the following error handlers are specific to a single codec:
+In addition, the following error handlers are specific to Unicode encoding
+schemes:
-+-------------------+---------+-------------------------------------------+
-| Value | Codec | Meaning |
-+===================+=========+===========================================+
-|``'surrogatepass'``| utf-8 | Allow encoding and decoding of surrogate |
-| | | codes in UTF-8. |
-+-------------------+---------+-------------------------------------------+
++-------------------+------------------------+-------------------------------------------+
+| Value | Codec | Meaning |
++===================+========================+===========================================+
+|``'surrogatepass'``| utf-8, utf-16, utf-32, | Allow encoding and decoding of surrogate |
+| | utf-16-be, utf-16-le, | codes in all the Unicode encoding schemes.|
+| | utf-32-be, utf-32-le | |
++-------------------+------------------------+-------------------------------------------+
.. versionadded:: 3.1
The ``'surrogateescape'`` and ``'surrogatepass'`` error handlers.
+.. versionchanged:: 3.4
+ The ``'surrogatepass'`` error handlers now works with utf-16\* and utf-32\* codecs.
+
The set of allowed values can be extended via :meth:`register_error`.
@@ -966,6 +971,10 @@ particular, the following variants typically exist:
+-----------------+--------------------------------+--------------------------------+
| cp037 | IBM037, IBM039 | English |
+-----------------+--------------------------------+--------------------------------+
+| cp273 | 273, IBM273, csIBM273 | German |
+| | | |
+| | | .. versionadded:: 3.4 |
++-----------------+--------------------------------+--------------------------------+
| cp424 | EBCDIC-CP-HE, IBM424 | Hebrew |
+-----------------+--------------------------------+--------------------------------+
| cp437 | 437, IBM437 | English |
@@ -1022,6 +1031,10 @@ particular, the following variants typically exist:
+-----------------+--------------------------------+--------------------------------+
| cp1026 | ibm1026 | Turkish |
+-----------------+--------------------------------+--------------------------------+
+| cp1125 | 1125, ibm1125, cp866u, ruscii | Ukrainian |
+| | | |
+| | | .. versionadded:: 3.4 |
++-----------------+--------------------------------+--------------------------------+
| cp1140 | ibm1140 | Western Europe |
+-----------------+--------------------------------+--------------------------------+
| cp1250 | windows-1250 | Central and Eastern Europe |
@@ -1167,6 +1180,12 @@ particular, the following variants typically exist:
| utf_8_sig | | all languages |
+-----------------+--------------------------------+--------------------------------+
+.. versionchanged:: 3.4
+ The utf-16\* and utf-32\* encoders no longer allow surrogate code points
+ (U+D800--U+DFFF) to be encoded. The utf-32\* decoders no longer decode
+ byte sequences that correspond to surrogate code points.
+
+
Python Specific Encodings
-------------------------
@@ -1177,6 +1196,9 @@ common use case for codecs, the underlying codec infrastructure supports
arbitrary data transforms rather than just text encodings). For asymmetric
codecs, the stated purpose describes the encoding direction.
+Text Encodings
+^^^^^^^^^^^^^^
+
The following codecs provide :class:`str` to :class:`bytes` encoding and
:term:`bytes-like object` to :class:`str` decoding, similar to the Unicode text
encodings.
@@ -1223,57 +1245,83 @@ encodings.
| | | .. deprecated:: 3.3 |
+--------------------+---------+---------------------------+
-The following codecs provide :term:`bytes-like object` to :class:`bytes`
-mappings.
-
-
-.. tabularcolumns:: |l|L|L|
-
-+----------------------+---------------------------+------------------------------+
-| Codec | Purpose | Encoder/decoder |
-+======================+===========================+==============================+
-| base64_codec [#b64]_ | Convert operand to MIME | :meth:`base64.b64encode`, |
-| | base64 (the result always | :meth:`base64.b64decode` |
-| | includes a trailing | |
-| | ``'\n'``) | |
-+----------------------+---------------------------+------------------------------+
-| bz2_codec | Compress the operand | :meth:`bz2.compress`, |
-| | using bz2 | :meth:`bz2.decompress` |
-+----------------------+---------------------------+------------------------------+
-| hex_codec | Convert operand to | :meth:`base64.b16encode`, |
-| | hexadecimal | :meth:`base64.b16decode` |
-| | representation, with two | |
-| | digits per byte | |
-+----------------------+---------------------------+------------------------------+
-| quopri_codec | Convert operand to MIME | :meth:`quopri.encodestring`, |
-| | quoted printable | :meth:`quopri.decodestring` |
-+----------------------+---------------------------+------------------------------+
-| uu_codec | Convert the operand using | :meth:`uu.encode`, |
-| | uuencode | :meth:`uu.decode` |
-+----------------------+---------------------------+------------------------------+
-| zlib_codec | Compress the operand | :meth:`zlib.compress`, |
-| | using gzip | :meth:`zlib.decompress` |
-+----------------------+---------------------------+------------------------------+
-
-.. [#b64] Rather than accepting any :term:`bytes-like object`,
- ``'base64_codec'`` accepts only :class:`bytes` and :class:`bytearray` for
- encoding and only :class:`bytes`, :class:`bytearray`, and ASCII-only
- instances of :class:`str` for decoding
-
-
-The following codecs provide :class:`str` to :class:`str` mappings.
+.. _binary-transforms:
+
+Binary Transforms
+^^^^^^^^^^^^^^^^^
+
+The following codecs provide binary transforms: :term:`bytes-like object`
+to :class:`bytes` mappings.
+
+
+.. tabularcolumns:: |l|L|L|L|
+
++----------------------+------------------+------------------------------+------------------------------+
+| Codec | Aliases | Purpose | Encoder / decoder |
++======================+==================+==============================+==============================+
+| base64_codec [#b64]_ | base64, base_64 | Convert operand to MIME | :meth:`base64.b64encode` / |
+| | | base64 (the result always | :meth:`base64.b64decode` |
+| | | includes a trailing | |
+| | | ``'\n'``) | |
+| | | | |
+| | | .. versionchanged:: 3.4 | |
+| | | accepts any | |
+| | | :term:`bytes-like object` | |
+| | | as input for encoding and | |
+| | | decoding | |
++----------------------+------------------+------------------------------+------------------------------+
+| bz2_codec | bz2 | Compress the operand | :meth:`bz2.compress` / |
+| | | using bz2 | :meth:`bz2.decompress` |
++----------------------+------------------+------------------------------+------------------------------+
+| hex_codec | hex | Convert operand to | :meth:`base64.b16encode` / |
+| | | hexadecimal | :meth:`base64.b16decode` |
+| | | representation, with two | |
+| | | digits per byte | |
++----------------------+------------------+------------------------------+------------------------------+
+| quopri_codec | quopri, | Convert operand to MIME | :meth:`quopri.encodestring` /|
+| | quotedprintable, | quoted printable | :meth:`quopri.decodestring` |
+| | quoted_printable | | |
++----------------------+------------------+------------------------------+------------------------------+
+| uu_codec | uu | Convert the operand using | :meth:`uu.encode` / |
+| | | uuencode | :meth:`uu.decode` |
++----------------------+------------------+------------------------------+------------------------------+
+| zlib_codec | zip, zlib | Compress the operand | :meth:`zlib.compress` / |
+| | | using gzip | :meth:`zlib.decompress` |
++----------------------+------------------+------------------------------+------------------------------+
+
+.. [#b64] In addition to :term:`bytes-like objects <bytes-like object>`,
+ ``'base64_codec'`` also accepts ASCII-only instances of :class:`str` for
+ decoding
-.. tabularcolumns:: |l|L|
+.. versionadded:: 3.2
+ Restoration of the binary transforms.
+
+.. versionchanged:: 3.4
+ Restoration of the aliases for the binary transforms.
-+--------------------+---------------------------+
-| Codec | Purpose |
-+====================+===========================+
-| rot_13 | Returns the Caesar-cypher |
-| | encryption of the operand |
-+--------------------+---------------------------+
+
+.. _text-transforms:
+
+Text Transforms
+^^^^^^^^^^^^^^^
+
+The following codec provides a text transform: a :class:`str` to :class:`str`
+mapping.
+
+.. tabularcolumns:: |l|l|L|
+
++--------------------+---------+---------------------------+
+| Codec | Aliases | Purpose |
++====================+=========+===========================+
+| rot_13 | rot13 | Returns the Caesar-cypher |
+| | | encryption of the operand |
++--------------------+---------+---------------------------+
.. versionadded:: 3.2
- bytes-to-bytes and str-to-str codecs.
+ Restoration of the ``rot_13`` text transform.
+
+.. versionchanged:: 3.4
+ Restoration of the ``rot13`` alias.
:mod:`encodings.idna` --- Internationalized Domain Names in Applications
diff --git a/Doc/library/collections.abc.rst b/Doc/library/collections.abc.rst
index 4b21932..356f473 100644
--- a/Doc/library/collections.abc.rst
+++ b/Doc/library/collections.abc.rst
@@ -15,7 +15,7 @@
import itertools
__name__ = '<doctest>'
-**Source code:** :source:`Lib/collections/abc.py`
+**Source code:** :source:`Lib/_collections_abc.py`
--------------
diff --git a/Doc/library/collections.rst b/Doc/library/collections.rst
index 4def1dc..f5fe12a 100644
--- a/Doc/library/collections.rst
+++ b/Doc/library/collections.rst
@@ -76,14 +76,19 @@ The class can be used to simulate nested scopes and is useful in templating.
be modified to change which mappings are searched. The list should
always contain at least one mapping.
- .. method:: new_child()
+ .. method:: new_child(m=None)
- 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
+ Returns a new :class:`ChainMap` containing a new map followed by
+ all of the maps in the current instance. If ``m`` is specified,
+ it becomes the new map at the front of the list of mappings; if not
+ specified, an empty dict is used, so that 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.
+ .. versionchanged:: 3.4
+ The optional ``m`` parameter was added.
+
.. attribute:: parents
Property returning a new :class:`ChainMap` containing all of the maps in
@@ -369,10 +374,6 @@ or subtracting from an empty counter.
.. seealso::
- * `Counter class <http://code.activestate.com/recipes/576611/>`_
- adapted for Python 2.5 and an early `Bag recipe
- <http://code.activestate.com/recipes/259174/>`_ for Python 2.4.
-
* `Bag class <http://www.gnu.org/software/smalltalk/manual-base/html_node/Bag.html>`_
in Smalltalk.
@@ -915,11 +916,6 @@ and more efficient to use a simple class declaration:
>>> class Status:
open, pending, closed = range(3)
-.. seealso::
-
- * `Named tuple recipe <http://code.activestate.com/recipes/500261/>`_
- adapted for Python 2.4.
-
* `Recipe for named tuple abstract base class with a metaclass mix-in
<http://code.activestate.com/recipes/577629-namedtupleabc-abstract-base-class-mix-in-for-named/>`_
by Jan Kaliszewski. Besides providing an :term:`abstract base class` for
@@ -982,10 +978,6 @@ The :class:`OrderedDict` constructor and :meth:`update` method both accept
keyword arguments, but their order is lost because Python's function call
semantics pass-in keyword arguments using a regular unordered dictionary.
-.. seealso::
-
- `Equivalent OrderedDict recipe <http://code.activestate.com/recipes/576693/>`_
- that runs on Python 2.4 or later.
:class:`OrderedDict` Examples and Recipes
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/Doc/library/compileall.rst b/Doc/library/compileall.rst
index b12c217..41e9e1b 100644
--- a/Doc/library/compileall.rst
+++ b/Doc/library/compileall.rst
@@ -20,7 +20,8 @@ compile Python sources.
.. program:: compileall
-.. cmdoption:: [directory|file]...
+.. cmdoption:: directory ...
+ file ...
Positional arguments are files to compile or directories that contain
source files, traversed recursively. If no argument is given, behave as if
diff --git a/Doc/library/concurrency.rst b/Doc/library/concurrency.rst
index f2a512f..0de281b 100644
--- a/Doc/library/concurrency.rst
+++ b/Doc/library/concurrency.rst
@@ -20,7 +20,6 @@ multitasking). Here's an overview:
subprocess.rst
sched.rst
queue.rst
- select.rst
The following are support modules for some of the above services:
diff --git a/Doc/library/concurrent.futures.rst b/Doc/library/concurrent.futures.rst
index 575b1ea..0495737 100644
--- a/Doc/library/concurrent.futures.rst
+++ b/Doc/library/concurrent.futures.rst
@@ -164,6 +164,9 @@ uses a pool of processes to execute calls asynchronously.
allows it to side-step the :term:`Global Interpreter Lock` but also means that
only picklable objects can be executed and returned.
+The ``__main__`` module must be importable by worker subprocesses. This means
+that :class:`ProcessPoolExecutor` will not work in the interactive interpreter.
+
Calling :class:`Executor` or :class:`Future` methods from a callable submitted
to a :class:`ProcessPoolExecutor` will result in deadlock.
diff --git a/Doc/library/contextlib.rst b/Doc/library/contextlib.rst
index fba48f4..82efd0c 100644
--- a/Doc/library/contextlib.rst
+++ b/Doc/library/contextlib.rst
@@ -95,6 +95,83 @@ Functions and classes provided:
``page.close()`` will be called when the :keyword:`with` block is exited.
+.. function:: suppress(*exceptions)
+
+ Return a context manager that suppresses any of the specified exceptions
+ if they occur in the body of a with statement and then resumes execution
+ with the first statement following the end of the with statement.
+
+ As with any other mechanism that completely suppresses exceptions, this
+ context manager should be used only to cover very specific errors where
+ silently continuing with program execution is known to be the right
+ thing to do.
+
+ For example::
+
+ from contextlib import suppress
+
+ with suppress(FileNotFoundError):
+ os.remove('somefile.tmp')
+
+ with suppress(FileNotFoundError):
+ os.remove('someotherfile.tmp')
+
+ This code is equivalent to::
+
+ try:
+ os.remove('somefile.tmp')
+ except FileNotFoundError:
+ pass
+
+ try:
+ os.remove('someotherfile.tmp')
+ except FileNotFoundError:
+ pass
+
+ This context manager is :ref:`reentrant <reentrant-cms>`.
+
+ .. versionadded:: 3.4
+
+
+.. function:: redirect_stdout(new_target)
+
+ Context manager for temporarily redirecting :data:`sys.stdout` to
+ another file or file-like object.
+
+ This tool adds flexibility to existing functions or classes whose output
+ is hardwired to stdout.
+
+ For example, the output of :func:`help` normally is sent to *sys.stdout*.
+ You can capture that output in a string by redirecting the output to a
+ :class:`io.StringIO` object::
+
+ f = io.StringIO()
+ with redirect_stdout(f):
+ help(pow)
+ s = f.getvalue()
+
+ To send the output of :func:`help` to a file on disk, redirect the output
+ to a regular file::
+
+ with open('help.txt', 'w') as f:
+ with redirect_stdout(f):
+ help(pow)
+
+ To send the output of :func:`help` to *sys.stderr*::
+
+ with redirect_stdout(sys.stderr):
+ help(pow)
+
+ Note that the global side effect on :data:`sys.stdout` means that this
+ context manager is not suitable for use in library code and most threaded
+ applications. It also has no effect on the output of subprocesses.
+ However, it is still a useful approach for many utility scripts.
+
+ This context manager is :ref:`reusable but not reentrant <reusable-cms>`.
+
+ .. versionadded:: 3.4
+
+
.. class:: ContextDecorator()
A base class that enables a context manager to also be used as a decorator.
@@ -520,3 +597,153 @@ an explicit ``with`` statement.
The specification, background, and examples for the Python :keyword:`with`
statement.
+.. _single-use-reusable-and-reentrant-cms:
+
+Single use, reusable and reentrant context managers
+---------------------------------------------------
+
+Most context managers are written in a way that means they can only be
+used effectively in a :keyword:`with` statement once. These single use
+context managers must be created afresh each time they're used -
+attempting to use them a second time will trigger an exception or
+otherwise not work correctly.
+
+This common limitation means that it is generally advisable to create
+context managers directly in the header of the :keyword:`with` statement
+where they are used (as shown in all of the usage examples above).
+
+Files are an example of effectively single use context managers, since
+the first :keyword:`with` statement will close the file, preventing any
+further IO operations using that file object.
+
+Context managers created using :func:`contextmanager` are also single use
+context managers, and will complain about the underlying generator failing
+to yield if an attempt is made to use them a second time::
+
+ >>> from contextlib import contextmanager
+ >>> @contextmanager
+ ... def singleuse():
+ ... print("Before")
+ ... yield
+ ... print("After")
+ ...
+ >>> cm = singleuse()
+ >>> with cm:
+ ... pass
+ ...
+ Before
+ After
+ >>> with cm:
+ ... pass
+ ...
+ Traceback (most recent call last):
+ ...
+ RuntimeError: generator didn't yield
+
+
+.. _reentrant-cms:
+
+Reentrant context managers
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+More sophisticated context managers may be "reentrant". These context
+managers can not only be used in multiple :keyword:`with` statements,
+but may also be used *inside* a :keyword:`with` statement that is already
+using the same context manager.
+
+:class:`threading.RLock` is an example of a reentrant context manager, as are
+:func:`suppress` and :func:`redirect_stdout`. Here's a very simple example of
+reentrant use::
+
+ >>> from contextlib import redirect_stdout
+ >>> from io import StringIO
+ >>> stream = StringIO()
+ >>> write_to_stream = redirect_stdout(stream)
+ >>> with write_to_stream:
+ ... print("This is written to the stream rather than stdout")
+ ... with write_to_stream:
+ ... print("This is also written to the stream")
+ ...
+ >>> print("This is written directly to stdout")
+ This is written directly to stdout
+ >>> print(stream.getvalue())
+ This is written to the stream rather than stdout
+ This is also written to the stream
+
+Real world examples of reentrancy are more likely to involve multiple
+functions calling each other and hence be far more complicated than this
+example.
+
+Note also that being reentrant is *not* the same thing as being thread safe.
+:func:`redirect_stdout`, for example, is definitely not thread safe, as it
+makes a global modification to the system state by binding :data:`sys.stdout`
+to a different stream.
+
+
+.. _reusable-cms:
+
+Reusable context managers
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Distinct from both single use and reentrant context managers are "reusable"
+context managers (or, to be completely explicit, "reusable, but not
+reentrant" context managers, since reentrant context managers are also
+reusable). These context managers support being used multiple times, but
+will fail (or otherwise not work correctly) if the specific context manager
+instance has already been used in a containing with statement.
+
+:class:`threading.Lock` is an example of a reusable, but not reentrant,
+context manager (for a reentrant lock, it is necessary to use
+:class:`threading.RLock` instead).
+
+Another example of a reusable, but not reentrant, context manager is
+:class:`ExitStack`, as it invokes *all* currently registered callbacks
+when leaving any with statement, regardless of where those callbacks
+were added::
+
+ >>> from contextlib import ExitStack
+ >>> stack = ExitStack()
+ >>> with stack:
+ ... stack.callback(print, "Callback: from first context")
+ ... print("Leaving first context")
+ ...
+ Leaving first context
+ Callback: from first context
+ >>> with stack:
+ ... stack.callback(print, "Callback: from second context")
+ ... print("Leaving second context")
+ ...
+ Leaving second context
+ Callback: from second context
+ >>> with stack:
+ ... stack.callback(print, "Callback: from outer context")
+ ... with stack:
+ ... stack.callback(print, "Callback: from inner context")
+ ... print("Leaving inner context")
+ ... print("Leaving outer context")
+ ...
+ Leaving inner context
+ Callback: from inner context
+ Callback: from outer context
+ Leaving outer context
+
+As the output from the example shows, reusing a single stack object across
+multiple with statements works correctly, but attempting to nest them
+will cause the stack to be cleared at the end of the innermost with
+statement, which is unlikely to be desirable behaviour.
+
+Using separate :class:`ExitStack` instances instead of reusing a single
+instance avoids that problem::
+
+ >>> from contextlib import ExitStack
+ >>> with ExitStack() as outer_stack:
+ ... outer_stack.callback(print, "Callback: from outer context")
+ ... with ExitStack() as inner_stack:
+ ... inner_stack.callback(print, "Callback: from inner context")
+ ... print("Leaving inner context")
+ ... print("Leaving outer context")
+ ...
+ Leaving inner context
+ Callback: from inner context
+ Leaving outer context
+ Callback: from outer context
diff --git a/Doc/library/curses.rst b/Doc/library/curses.rst
index 314636e..f3e60b4 100644
--- a/Doc/library/curses.rst
+++ b/Doc/library/curses.rst
@@ -12,7 +12,7 @@ The :mod:`curses` module provides an interface to the curses library, the
de-facto standard for portable advanced terminal handling.
While curses is most widely used in the Unix environment, versions are available
-for DOS, OS/2, and possibly other systems as well. This extension module is
+for Windows, DOS, and possibly other systems as well. This extension module is
designed to match the API of ncurses, an open-source curses library hosted on
Linux and the BSD variants of Unix.
diff --git a/Doc/library/datatypes.rst b/Doc/library/datatypes.rst
index d0382e0..48af082 100644
--- a/Doc/library/datatypes.rst
+++ b/Doc/library/datatypes.rst
@@ -30,3 +30,4 @@ The following modules are documented in this chapter:
copy.rst
pprint.rst
reprlib.rst
+ enum.rst
diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst
index 065e850..e4f1eb2 100644
--- a/Doc/library/datetime.rst
+++ b/Doc/library/datetime.rst
@@ -170,10 +170,12 @@ dates or times.
* ``0 <= seconds < 3600*24`` (the number of seconds in one day)
* ``-999999999 <= days <= 999999999``
- If any argument is a float and there are fractional microseconds, the fractional
- microseconds left over from all arguments are combined and their sum is rounded
- to the nearest microsecond. If no argument is a float, the conversion and
- normalization processes are exact (no information is lost).
+ If any argument is a float and there are fractional microseconds,
+ the fractional microseconds left over from all arguments are
+ combined and their sum is rounded to the nearest microsecond using
+ round-half-to-even tiebreaker. If no argument is a float, the
+ conversion and normalization processes are exact (no information is
+ lost).
If the normalized value of days lies outside the indicated range,
:exc:`OverflowError` is raised.
diff --git a/Doc/library/dbm.rst b/Doc/library/dbm.rst
index 81a05c7..f5496d5 100644
--- a/Doc/library/dbm.rst
+++ b/Doc/library/dbm.rst
@@ -73,33 +73,39 @@ Key and values are always stored as bytes. This means that when
strings are used they are implicitly converted to the default encoding before
being stored.
+These objects also support being used in a :keyword:`with` statement, which
+will automatically close them when done.
+
+.. versionchanged:: 3.4
+ Added native support for the context management protocol to the objects
+ returned by :func:`.open`.
+
The following example records some hostnames and a corresponding title, and
then prints out the contents of the database::
import dbm
# Open database, creating it if necessary.
- db = dbm.open('cache', 'c')
+ with dbm.open('cache', 'c') as db:
- # Record some values
- db[b'hello'] = b'there'
- db['www.python.org'] = 'Python Website'
- db['www.cnn.com'] = 'Cable News Network'
+ # Record some values
+ db[b'hello'] = b'there'
+ db['www.python.org'] = 'Python Website'
+ db['www.cnn.com'] = 'Cable News Network'
- # Note that the keys are considered bytes now.
- assert db[b'www.python.org'] == b'Python Website'
- # Notice how the value is now in bytes.
- assert db['www.cnn.com'] == b'Cable News Network'
+ # Note that the keys are considered bytes now.
+ assert db[b'www.python.org'] == b'Python Website'
+ # Notice how the value is now in bytes.
+ assert db['www.cnn.com'] == b'Cable News Network'
- # Often-used methods of the dict interface work too.
- print(db.get('python.org', b'not present'))
+ # Often-used methods of the dict interface work too.
+ print(db.get('python.org', b'not present'))
- # Storing a non-string key or value will raise an exception (most
- # likely a TypeError).
- db['www.yahoo.com'] = 4
+ # Storing a non-string key or value will raise an exception (most
+ # likely a TypeError).
+ db['www.yahoo.com'] = 4
- # Close when done.
- db.close()
+ # db is automatically closed when leaving the with statement.
.. seealso::
diff --git a/Doc/library/debug.rst b/Doc/library/debug.rst
index c69fb1c..88a2fa6 100644
--- a/Doc/library/debug.rst
+++ b/Doc/library/debug.rst
@@ -15,3 +15,4 @@ allowing you to identify bottlenecks in your programs.
profile.rst
timeit.rst
trace.rst
+ tracemalloc.rst
diff --git a/Doc/library/development.rst b/Doc/library/development.rst
index 2368769..06e7048 100644
--- a/Doc/library/development.rst
+++ b/Doc/library/development.rst
@@ -23,4 +23,3 @@ The list of modules described in this chapter is:
unittest.mock-examples.rst
2to3.rst
test.rst
- venv.rst
diff --git a/Doc/library/difflib.rst b/Doc/library/difflib.rst
index 81dc0f1..2a75d2c 100644
--- a/Doc/library/difflib.rst
+++ b/Doc/library/difflib.rst
@@ -226,8 +226,8 @@ diffs. For comparing directories and files, see also, the :mod:`filecmp` module.
:file:`Tools/scripts/ndiff.py` is a command-line front-end to this function.
- >>> 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
? ^
@@ -250,8 +250,8 @@ diffs. For comparing directories and files, see also, the :mod:`filecmp` module.
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))
>>> diff = list(diff) # materialize the generated delta into a list
>>> print(''.join(restore(diff, 1)), end="")
one
@@ -650,7 +650,7 @@ obtained from the :meth:`~io.BaseIO.readlines` method of file-like objects):
... 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]
@@ -659,7 +659,7 @@ obtained from the :meth:`~io.BaseIO.readlines` method of file-like objects):
... 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:
diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst
index ec7112d..d86550f 100644
--- a/Doc/library/dis.rst
+++ b/Doc/library/dis.rst
@@ -26,7 +26,8 @@ Example: Given the function :func:`myfunc`::
def myfunc(alist):
return len(alist)
-the following command can be used to get the disassembly of :func:`myfunc`::
+the following command can be used to display the disassembly of
+:func:`myfunc`::
>>> dis.dis(myfunc)
2 0 LOAD_GLOBAL 0 (len)
@@ -36,8 +37,77 @@ the following command can be used to get the disassembly of :func:`myfunc`::
(The "2" is a line number).
-The :mod:`dis` module defines the following functions and constants:
+Bytecode analysis
+-----------------
+.. versionadded:: 3.4
+
+The bytecode analysis API allows pieces of Python code to be wrapped in a
+:class:`Bytecode` object that provides easy access to details of the
+compiled code.
+
+.. class:: Bytecode(x, *, first_line=None, current_offset=None)
+
+ Analyse the bytecode corresponding to a function, method, string of
+ source code, or a code object (as returned by :func:`compile`).
+
+ This is a convenience wrapper around many of the functions listed below,
+ most notably :func:`get_instructions`, as iterating over a
+ :class:`Bytecode` instance yields the bytecode operations as
+ :class:`Instruction` instances.
+
+ If *first_line* is not None, it indicates the line number that should
+ be reported for the first source line in the disassembled code.
+ Otherwise, the source line information (if any) is taken directly from
+ the disassembled code object.
+
+ If *current_offset* is not None, it refers to an instruction offset
+ in the disassembled code. Setting this means :meth:`dis` will display
+ a "current instruction" marker against the specified opcode.
+
+ .. classmethod:: from_traceback(tb)
+
+ Construct a :class:`Bytecode` instance from the given traceback,
+ setting *current_offset* to the instruction responsible for the
+ exception.
+
+ .. data:: codeobj
+
+ The compiled code object.
+
+ .. data:: first_line
+
+ The first source line of the code object (if available)
+
+ .. method:: dis()
+
+ Return a formatted view of the bytecode operations (the same as
+ printed by :func:`dis`, but returned as a multi-line string).
+
+ .. method:: info()
+
+ Return a formatted multi-line string with detailed information about the
+ code object, like :func:`code_info`.
+
+Example::
+
+ >>> bytecode = dis.Bytecode(myfunc)
+ >>> for instr in bytecode:
+ ... print(instr.opname)
+ ...
+ LOAD_GLOBAL
+ LOAD_FAST
+ CALL_FUNCTION
+ RETURN_VALUE
+
+
+Analysis functions
+------------------
+
+The :mod:`dis` module also defines the following analysis functions that
+convert the input directly to the desired output. They can be useful if
+only a single operation is being performed, so the intermediate analysis
+object isn't useful:
.. function:: code_info(x)
@@ -51,17 +121,22 @@ The :mod:`dis` module defines the following functions and constants:
.. versionadded:: 3.2
-.. function:: show_code(x)
+.. function:: show_code(x, *, file=None)
Print detailed code object information for the supplied function, method,
- source code string or code object to stdout.
+ source code string or code object to *file* (or ``sys.stdout`` if *file*
+ is not specified).
- This is a convenient shorthand for ``print(code_info(x))``, intended for
- interactive exploration at the interpreter prompt.
+ This is a convenient shorthand for ``print(code_info(x), file=file)``,
+ intended for interactive exploration at the interpreter prompt.
.. versionadded:: 3.2
-.. function:: dis(x=None)
+ .. versionchanged:: 3.4
+ Added ``file`` parameter
+
+
+.. function:: dis(x=None, *, file=None)
Disassemble the *x* object. *x* can denote either a module, a class, a
method, a function, a code object, a string of source code or a byte sequence
@@ -72,16 +147,28 @@ The :mod:`dis` module defines the following functions and constants:
disassembled. If no object is provided, this function disassembles the last
traceback.
+ The disassembly is written as text to the supplied ``file`` argument if
+ provided and to ``sys.stdout`` otherwise.
-.. function:: distb(tb=None)
+ .. versionchanged:: 3.4
+ Added ``file`` parameter
+
+
+.. function:: distb(tb=None, *, file=None)
Disassemble the top-of-stack function of a traceback, using the last
traceback if none was passed. The instruction causing the exception is
indicated.
+ The disassembly is written as text to the supplied ``file`` argument if
+ provided and to ``sys.stdout`` otherwise.
+
+ .. versionchanged:: 3.4
+ Added ``file`` parameter
+
-.. function:: disassemble(code, lasti=-1)
- disco(code, lasti=-1)
+.. function:: disassemble(code, lasti=-1, *, file=None)
+ disco(code, lasti=-1, *, file=None)
Disassemble a code object, indicating the last instruction if *lasti* was
provided. The output is divided in the following columns:
@@ -97,6 +184,28 @@ The :mod:`dis` module defines the following functions and constants:
The parameter interpretation recognizes local and global variable names,
constant values, branch targets, and compare operators.
+ The disassembly is written as text to the supplied ``file`` argument if
+ provided and to ``sys.stdout`` otherwise.
+
+ .. versionchanged:: 3.4
+ Added ``file`` parameter
+
+
+.. function:: get_instructions(x, *, first_line=None)
+
+ Return an iterator over the instructions in the supplied function, method,
+ source code string or code object.
+
+ The iterator generates a series of :class:`Instruction` named tuples
+ giving the details of each operation in the supplied code.
+
+ If *first_line* is not None, it indicates the line number that should
+ be reported for the first source line in the disassembled code.
+ Otherwise, the source line information (if any) is taken directly from
+ the disassembled code object.
+
+ .. versionadded:: 3.4
+
.. function:: findlinestarts(code)
@@ -111,60 +220,66 @@ The :mod:`dis` module defines the following functions and constants:
return a list of these offsets.
-.. data:: opname
+.. function:: stack_effect(opcode, [oparg])
- Sequence of operation names, indexable using the bytecode.
+ Compute the stack effect of *opcode* with argument *oparg*.
+ .. versionadded:: 3.4
-.. data:: opmap
+.. _bytecodes:
- Dictionary mapping operation names to bytecodes.
+Python Bytecode Instructions
+----------------------------
+The :func:`get_instructions` function and :class:`Bytecode` class provide
+details of bytecode instructions as :class:`Instruction` instances:
-.. data:: cmp_op
+.. class:: Instruction
- Sequence of all compare operation names.
+ Details for a bytecode operation
+ .. data:: opcode
-.. data:: hasconst
+ numeric code for operation, corresponding to the opcode values listed
+ below and the bytecode values in the :ref:`opcode_collections`.
- Sequence of bytecodes that have a constant parameter.
+ .. data:: opname
-.. data:: hasfree
+ human readable name for operation
- Sequence of bytecodes that access a free variable.
+ .. data:: arg
-.. data:: hasname
+ numeric argument to operation (if any), otherwise None
- Sequence of bytecodes that access an attribute by name.
+ .. data:: argval
-.. data:: hasjrel
+ resolved arg value (if known), otherwise same as arg
- Sequence of bytecodes that have a relative jump target.
+ .. data:: argrepr
-.. data:: hasjabs
+ human readable description of operation argument
- Sequence of bytecodes that have an absolute jump target.
+ .. data:: offset
-.. data:: haslocal
+ start index of operation within bytecode sequence
- Sequence of bytecodes that access a local variable.
+ .. data:: starts_line
-.. data:: hascompare
+ line started by this opcode (if any), otherwise None
- Sequence of bytecodes of Boolean operations.
+ .. data:: is_jump_target
-.. _bytecodes:
+ ``True`` if other code jumps to here, otherwise ``False``
+
+ .. versionadded:: 3.4
-Python Bytecode Instructions
-----------------------------
The Python compiler currently generates the following bytecode instructions.
@@ -506,12 +621,6 @@ the stack so that it is available for further iterations of the loop.
.. XXX explain the WHY stuff!
-.. opcode:: STORE_LOCALS
-
- Pops TOS from the stack and stores it as the current frame's ``f_locals``.
- This is used in class construction.
-
-
All of the following opcodes expect arguments. An argument is two bytes, with
the more significant byte last.
@@ -722,6 +831,13 @@ the more significant byte last.
Pushes a reference to the object the cell contains on the stack.
+.. opcode:: LOAD_CLASSDEREF (i)
+
+ Much like :opcode:`LOAD_DEREF` but first checks the locals dictionary before
+ consulting the cell. This is used for loading free variables in class
+ bodies.
+
+
.. opcode:: STORE_DEREF (i)
Stores TOS into the cell contained in slot *i* of the cell and free variable
@@ -820,3 +936,62 @@ the more significant byte last.
which don't take arguments ``< HAVE_ARGUMENT`` and those which do ``>=
HAVE_ARGUMENT``.
+.. _opcode_collections:
+
+Opcode collections
+------------------
+
+These collections are provided for automatic introspection of bytecode
+instructions:
+
+.. data:: opname
+
+ Sequence of operation names, indexable using the bytecode.
+
+
+.. data:: opmap
+
+ Dictionary mapping operation names to bytecodes.
+
+
+.. data:: cmp_op
+
+ Sequence of all compare operation names.
+
+
+.. data:: hasconst
+
+ Sequence of bytecodes that have a constant parameter.
+
+
+.. data:: hasfree
+
+ Sequence of bytecodes that access a free variable (note that 'free' in
+ this context refers to names in the current scope that are referenced by
+ inner scopes or names in outer scopes that are referenced from this scope.
+ It does *not* include references to global or builtin scopes).
+
+
+.. data:: hasname
+
+ Sequence of bytecodes that access an attribute by name.
+
+
+.. data:: hasjrel
+
+ Sequence of bytecodes that have a relative jump target.
+
+
+.. data:: hasjabs
+
+ Sequence of bytecodes that have an absolute jump target.
+
+
+.. data:: haslocal
+
+ Sequence of bytecodes that access a local variable.
+
+
+.. data:: hascompare
+
+ Sequence of bytecodes of Boolean operations.
diff --git a/Doc/library/distribution.rst b/Doc/library/distribution.rst
new file mode 100644
index 0000000..fb3f5df
--- /dev/null
+++ b/Doc/library/distribution.rst
@@ -0,0 +1,14 @@
+***********************************
+Software Packaging and Distribution
+***********************************
+
+These libraries help you with publishing and installing Python software.
+While these modules are designed to work in conjunction with the
+`Python Package Index <https://pypi.python.org>`__, they can also be used
+with a local index server, or without any index server at all.
+
+.. toctree::
+
+ distutils.rst
+ ensurepip.rst
+ venv.rst
diff --git a/Doc/library/distutils.rst b/Doc/library/distutils.rst
index 6666a9b..8e3732b 100644
--- a/Doc/library/distutils.rst
+++ b/Doc/library/distutils.rst
@@ -12,14 +12,15 @@ 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.
+Most Python users will *not* want to use this module directly, but instead
+use the cross-version tools maintained by the Python Packaging Authority.
+Refer to the `Python Packaging User Guide <http://packaging.python.org>`_
+for more information.
-User documentation and API reference are provided in another document:
+For the benefits of packaging tool authors and users seeking a deeper
+understanding of the details of the current packaging and distribution
+system, the legacy :mod:`distutils` based user documentation and API
+reference remain available:
-.. 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. It also contains
- instructions for end-users wanting to install a distutils-based package,
- :ref:`install-index`.
+* :ref:`install-index`
+* :ref:`distutils-index`
diff --git a/Doc/library/doctest.rst b/Doc/library/doctest.rst
index 222c719..50626e9 100644
--- a/Doc/library/doctest.rst
+++ b/Doc/library/doctest.rst
@@ -278,6 +278,10 @@ strings are treated as if they were docstrings. In output, a key ``K`` in
Any classes found are recursively searched similarly, to test docstrings in
their contained methods and nested classes.
+.. impl-detail::
+ Prior to version 3.4, extension modules written in C were not fully
+ searched by doctest.
+
.. _doctest-finding-examples:
@@ -495,7 +499,11 @@ Option Flags
A number of option flags control various aspects of doctest's behavior.
Symbolic names for the flags are supplied as module constants, which can be
or'ed together and passed to various functions. The names can also be used in
-:ref:`doctest directives <doctest-directives>`.
+:ref:`doctest directives <doctest-directives>`, and may be passed to the
+doctest command line interface via the ``-o`` option.
+
+.. versionadded:: 3.4
+ The ``-o`` command line option.
The first group of options define test semantics, controlling aspects of how
doctest decides whether actual output matches an example's expected output:
@@ -633,6 +641,19 @@ The second group of options controls how test failures are reported:
the output is suppressed.
+.. data:: FAIL_FAST
+
+ When specified, exit after the first failing example and don't attempt to run
+ the remaining examples. Thus, the number of failures reported will be at most
+ 1. This flag may be useful during debugging, since examples after the first
+ failure won't even produce debugging output.
+
+ The doctest command line accepts the option ``-f`` as a shorthand for ``-o
+ FAIL_FAST``.
+
+ .. versionadded:: 3.4
+
+
.. data:: REPORTING_FLAGS
A bitmask or'ing together all the reporting flags above.
@@ -1269,9 +1290,8 @@ DocTestFinder objects
A processing class used to extract the :class:`DocTest`\ s that are relevant to
a given object, from its docstring and the docstrings of its contained objects.
- :class:`DocTest`\ s can currently be extracted from the following object types:
- modules, functions, classes, methods, staticmethods, classmethods, and
- properties.
+ :class:`DocTest`\ s can be extracted from modules, classes, functions,
+ methods, staticmethods, classmethods, and properties.
The optional argument *verbose* can be used to display the objects searched by
the finder. It defaults to ``False`` (no output).
diff --git a/Doc/library/email-examples.rst b/Doc/library/email-examples.rst
index 32cecf3..cbbcb78 100644
--- a/Doc/library/email-examples.rst
+++ b/Doc/library/email-examples.rst
@@ -40,6 +40,36 @@ text version: [2]_
.. literalinclude:: ../includes/email-alternative.py
+.. _email-contentmanager-api-examples:
+
+Examples using the Provisional API
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Here is a reworking of the last example using the provisional API. To make
+things a bit more interesting, we include a related image in the html part, and
+we save a copy of what we are going to send to disk, as well as sending it.
+
+This example also shows how easy it is to include non-ASCII, and simplifies the
+sending of the message using the :meth:`.send_message` method of the
+:mod:`smtplib` module.
+
+.. literalinclude:: ../includes/email-alternative-new-api.py
+
+If we were instead sent the message from the last example, here is one
+way we could process it:
+
+.. literalinclude:: ../includes/email-read-alternative-new-api.py
+
+Up to the prompt, the output from the above is::
+
+ To: Penelope Pussycat <"penelope@example.com">, Fabrette Pussycat <"fabrette@example.com">
+ From: Pepé Le Pew <pepe@example.com>
+ Subject: Ayons asperges pour le déjeuner
+
+ Salut!
+
+ Cela ressemble à un excellent recipie[1] déjeuner.
+
+
.. rubric:: Footnotes
.. [1] Thanks to Matthew Dixon Cowles for the original inspiration and examples.
diff --git a/Doc/library/email.contentmanager.rst b/Doc/library/email.contentmanager.rst
new file mode 100644
index 0000000..8f0bfdb
--- /dev/null
+++ b/Doc/library/email.contentmanager.rst
@@ -0,0 +1,430 @@
+:mod:`email.contentmanager`: Managing MIME Content
+--------------------------------------------------
+
+.. module:: email.contentmanager
+ :synopsis: Storing and Retrieving Content from MIME Parts
+
+.. moduleauthor:: R. David Murray <rdmurray@bitdance.com>
+.. sectionauthor:: R. David Murray <rdmurray@bitdance.com>
+
+
+.. note::
+
+ The contentmanager module has been included in the standard library on a
+ :term:`provisional basis <provisional package>`. Backwards incompatible
+ changes (up to and including removal of the module) may occur if deemed
+ necessary by the core developers.
+
+.. versionadded:: 3.4
+ as a :term:`provisional module <provisional package>`.
+
+The :mod:`~email.message` module provides a class that can represent an
+arbitrary email message. That basic message model has a useful and flexible
+API, but it provides only a lower-level API for interacting with the generic
+parts of a message (the headers, generic header parameters, and the payload,
+which may be a list of sub-parts). This module provides classes and tools
+that provide an enhanced and extensible API for dealing with various specific
+types of content, including the ability to retrieve the content of the message
+as a specialized object type rather than as a simple bytes object. The module
+automatically takes care of the RFC-specified MIME details (required headers
+and parameters, etc.) for the certain common content types content properties,
+and support for additional types can be added by an application using the
+extension mechanisms.
+
+This module defines the eponymous "Content Manager" classes. The base
+:class:`.ContentManager` class defines an API for registering content
+management functions which extract data from ``Message`` objects or insert data
+and headers into ``Message`` objects, thus providing a way of converting
+between ``Message`` objects containing data and other representations of that
+data (Python data types, specialized Python objects, external files, etc). The
+module also defines one concrete content manager: :data:`raw_data_manager`
+converts between MIME content types and ``str`` or ``bytes`` data. It also
+provides a convenient API for managing the MIME parameters when inserting
+content into ``Message``\ s. It also handles inserting and extracting
+``Message`` objects when dealing with the ``message/rfc822`` content type.
+
+Another part of the enhanced interface is subclasses of
+:class:`~email.message.Message` that provide new convenience API functions,
+including convenience methods for calling the Content Managers derived from
+this module.
+
+.. note::
+
+ Although :class:`.EmailMessage` and :class:`.MIMEPart` are currently
+ documented in this module because of the provisional nature of the code, the
+ implementation lives in the :mod:`email.message` module.
+
+.. currentmodule:: email.message
+
+.. class:: EmailMessage(policy=default)
+
+ If *policy* is specified (it must be an instance of a :mod:`~email.policy`
+ class) use the rules it specifies to udpate and serialize the representation
+ of the message. If *policy* is not set, use the
+ :class:`~email.policy.default` policy, which follows the rules of the email
+ RFCs except for line endings (instead of the RFC mandated ``\r\n``, it uses
+ the Python standard ``\n`` line endings). For more information see the
+ :mod:`~email.policy` documentation.
+
+ This class is a subclass of :class:`~email.message.Message`. It adds
+ the following methods:
+
+
+ .. attribute:: is_attachment
+
+ Set to ``True`` if there is a :mailheader:`Content-Disposition` header
+ and its (case insensitive) value is ``attachment``, ``False`` otherwise.
+
+
+ .. method:: get_body(preferencelist=('related', 'html', 'plain'))
+
+ Return the MIME part that is the best candidate to be the "body" of the
+ message.
+
+ *preferencelist* must be a sequence of strings from the set ``related``,
+ ``html``, and ``plain``, and indicates the order of preference for the
+ content type of the part returned.
+
+ Start looking for candidate matches with the object on which the
+ ``get_body`` method is called.
+
+ If ``related`` is not included in *preferencelist*, consider the root
+ part (or subpart of the root part) of any related encountered as a
+ candidate if the (sub-)part matches a preference.
+
+ When encountering a ``multipart/related``, check the ``start`` parameter
+ and if a part with a matching :mailheader:`Content-ID` is found, consider
+ only it when looking for candidate matches. Otherwise consider only the
+ first (default root) part of the ``multipart/related``.
+
+ If a part has a :mailheader:`Content-Disposition` header, only consider
+ the part a candidate match if the value of the header is ``inline``.
+
+ If none of the candidates matches any of the preferences in
+ *preferneclist*, return ``None``.
+
+ Notes: (1) For most applications the only *preferencelist* combinations
+ that really make sense are ``('plain',)``, ``('html', 'plain')``, and the
+ default, ``('related', 'html', 'plain')``. (2) Because matching starts
+ with the object on which ``get_body`` is called, calling ``get_body`` on
+ a ``multipart/related`` will return the object itself unless
+ *preferencelist* has a non-default value. (3) Messages (or message parts)
+ that do not specify a :mailheader:`Content-Type` or whose
+ :mailheader:`Content-Type` header is invalid will be treated as if they
+ are of type ``text/plain``, which may occasionally cause ``get_body`` to
+ return unexpected results.
+
+
+ .. method:: iter_attachments()
+
+ Return an iterator over all of the parts of the message that are not
+ candidate "body" parts. That is, skip the first occurrence of each of
+ ``text/plain``, ``text/html``, ``multipart/related``, or
+ ``multipart/alternative`` (unless they are explicitly marked as
+ attachments via :mailheader:`Content-Disposition: attachment`), and
+ return all remaining parts. When applied directly to a
+ ``multipart/related``, return an iterator over the all the related parts
+ except the root part (ie: the part pointed to by the ``start`` parameter,
+ or the first part if there is no ``start`` parameter or the ``start``
+ parameter doesn't match the :mailheader:`Content-ID` of any of the
+ parts). When applied directly to a ``multipart/alternative`` or a
+ non-``multipart``, return an empty iterator.
+
+
+ .. method:: iter_parts()
+
+ Return an iterator over all of the immediate sub-parts of the message,
+ which will be empty for a non-``multipart``. (See also
+ :meth:`~email.message.walk`.)
+
+
+ .. method:: get_content(*args, content_manager=None, **kw)
+
+ Call the ``get_content`` method of the *content_manager*, passing self
+ as the message object, and passing along any other arguments or keywords
+ as additional arguments. If *content_manager* is not specified, use
+ the ``content_manager`` specified by the current :mod:`~email.policy`.
+
+
+ .. method:: set_content(*args, content_manager=None, **kw)
+
+ Call the ``set_content`` method of the *content_manager*, passing self
+ as the message object, and passing along any other arguments or keywords
+ as additional arguments. If *content_manager* is not specified, use
+ the ``content_manager`` specified by the current :mod:`~email.policy`.
+
+
+ .. method:: make_related(boundary=None)
+
+ Convert a non-``multipart`` message into a ``multipart/related`` message,
+ moving any existing :mailheader:`Content-` headers and payload into a
+ (new) first part of the ``multipart``. If *boundary* is specified, use
+ it as the boundary string in the multipart, otherwise leave the boundary
+ to be automatically created when it is needed (for example, when the
+ message is serialized).
+
+
+ .. method:: make_alternative(boundary=None)
+
+ Convert a non-``multipart`` or a ``multipart/related`` into a
+ ``multipart/alternative``, moving any existing :mailheader:`Content-`
+ headers and payload into a (new) first part of the ``multipart``. If
+ *boundary* is specified, use it as the boundary string in the multipart,
+ otherwise leave the boundary to be automatically created when it is
+ needed (for example, when the message is serialized).
+
+
+ .. method:: make_mixed(boundary=None)
+
+ Convert a non-``multipart``, a ``multipart/related``, or a
+ ``multipart-alternative`` into a ``multipart/mixed``, moving any existing
+ :mailheader:`Content-` headers and payload into a (new) first part of the
+ ``multipart``. If *boundary* is specified, use it as the boundary string
+ in the multipart, otherwise leave the boundary to be automatically
+ created when it is needed (for example, when the message is serialized).
+
+
+ .. method:: add_related(*args, content_manager=None, **kw)
+
+ If the message is a ``multipart/related``, create a new message
+ object, pass all of the arguments to its :meth:`set_content` method,
+ and :meth:`~email.message.Message.attach` it to the ``multipart``. If
+ the message is a non-``multipart``, call :meth:`make_related` and then
+ proceed as above. If the message is any other type of ``multipart``,
+ raise a :exc:`TypeError`. If *content_manager* is not specified, use
+ the ``content_manager`` specified by the current :mod:`~email.policy`.
+ If the added part has no :mailheader:`Content-Disposition` header,
+ add one with the value ``inline``.
+
+
+ .. method:: add_alternative(*args, content_manager=None, **kw)
+
+ If the message is a ``multipart/alternative``, create a new message
+ object, pass all of the arguments to its :meth:`set_content` method, and
+ :meth:`~email.message.Message.attach` it to the ``multipart``. If the
+ message is a non-``multipart`` or ``multipart/related``, call
+ :meth:`make_alternative` and then proceed as above. If the message is
+ any other type of ``multipart``, raise a :exc:`TypeError`. If
+ *content_manager* is not specified, use the ``content_manager`` specified
+ by the current :mod:`~email.policy`.
+
+
+ .. method:: add_attachment(*args, content_manager=None, **kw)
+
+ If the message is a ``multipart/mixed``, create a new message object,
+ pass all of the arguments to its :meth:`set_content` method, and
+ :meth:`~email.message.Message.attach` it to the ``multipart``. If the
+ message is a non-``multipart``, ``multipart/related``, or
+ ``multipart/alternative``, call :meth:`make_mixed` and then proceed as
+ above. If *content_manager* is not specified, use the ``content_manager``
+ specified by the current :mod:`~email.policy`. If the added part
+ has no :mailheader:`Content-Disposition` header, add one with the value
+ ``attachment``. This method can be used both for explicit attachments
+ (:mailheader:`Content-Disposition: attachment` and ``inline`` attachments
+ (:mailheader:`Content-Disposition: inline`), by passing appropriate
+ options to the ``content_manager``.
+
+
+ .. method:: clear()
+
+ Remove the payload and all of the headers.
+
+
+ .. method:: clear_content()
+
+ Remove the payload and all of the :exc:`Content-` headers, leaving
+ all other headers intact and in their original order.
+
+
+.. class:: MIMEPart(policy=default)
+
+ This class represents a subpart of a MIME message. It is identical to
+ :class:`EmailMessage`, except that no :mailheader:`MIME-Version` headers are
+ added when :meth:`~EmailMessage.set_content` is called, since sub-parts do
+ not need their own :mailheader:`MIME-Version` headers.
+
+
+.. currentmodule:: email.contentmanager
+
+.. class:: ContentManager()
+
+ Base class for content managers. Provides the standard registry mechanisms
+ to register converters between MIME content and other representations, as
+ well as the ``get_content`` and ``set_content`` dispatch methods.
+
+
+ .. method:: get_content(msg, *args, **kw)
+
+ Look up a handler function based on the ``mimetype`` of *msg* (see next
+ paragraph), call it, passing through all arguments, and return the result
+ of the call. The expectation is that the handler will extract the
+ payload from *msg* and return an object that encodes information about
+ the extracted data.
+
+ To find the handler, look for the following keys in the registry,
+ stopping with the first one found:
+
+ * the string representing the full MIME type (``maintype/subtype``)
+ * the string representing the ``maintype``
+ * the empty string
+
+ If none of these keys produce a handler, raise a :exc:`KeyError` for the
+ full MIME type.
+
+
+ .. method:: set_content(msg, obj, *args, **kw)
+
+ If the ``maintype`` is ``multipart``, raise a :exc:`TypeError`; otherwise
+ look up a handler function based on the type of *obj* (see next
+ paragraph), call :meth:`~email.message.EmailMessage.clear_content` on the
+ *msg*, and call the handler function, passing through all arguments. The
+ expectation is that the handler will transform and store *obj* into
+ *msg*, possibly making other changes to *msg* as well, such as adding
+ various MIME headers to encode information needed to interpret the stored
+ data.
+
+ To find the handler, obtain the type of *obj* (``typ = type(obj)``), and
+ look for the following keys in the registry, stopping with the first one
+ found:
+
+ * the type itself (``typ``)
+ * the type's fully qualified name (``typ.__module__ + '.' +
+ typ.__qualname__``).
+ * the type's qualname (``typ.__qualname__``)
+ * the type's name (``typ.__name__``).
+
+ If none of the above match, repeat all of the checks above for each of
+ the types in the :term:`MRO` (``typ.__mro__``). Finally, if no other key
+ yields a handler, check for a handler for the key ``None``. If there is
+ no handler for ``None``, raise a :exc:`KeyError` for the fully
+ qualified name of the type.
+
+ Also add a :mailheader:`MIME-Version` header if one is not present (see
+ also :class:`.MIMEPart`).
+
+
+ .. method:: add_get_handler(key, handler)
+
+ Record the function *handler* as the handler for *key*. For the possible
+ values of *key*, see :meth:`get_content`.
+
+
+ .. method:: add_set_handler(typekey, handler)
+
+ Record *handler* as the function to call when an object of a type
+ matching *typekey* is passed to :meth:`set_content`. For the possible
+ values of *typekey*, see :meth:`set_content`.
+
+
+Content Manager Instances
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Currently the email package provides only one concrete content manager,
+:data:`raw_data_manager`, although more may be added in the future.
+:data:`raw_data_manager` is the
+:attr:`~email.policy.EmailPolicy.content_manager` provided by
+:attr:`~email.policy.EmailPolicy` and its derivatives.
+
+
+.. data:: raw_data_manager
+
+ This content manager provides only a minimum interface beyond that provided
+ by :class:`~email.message.Message` itself: it deals only with text, raw
+ byte strings, and :class:`~email.message.Message` objects. Nevertheless, it
+ provides significant advantages compared to the base API: ``get_content`` on
+ a text part will return a unicode string without the application needing to
+ manually decode it, ``set_content`` provides a rich set of options for
+ controlling the headers added to a part and controlling the content transfer
+ encoding, and it enables the use of the various ``add_`` methods, thereby
+ simplifying the creation of multipart messages.
+
+ .. method:: get_content(msg, errors='replace')
+
+ Return the payload of the part as either a string (for ``text`` parts), a
+ :class:`~email.message.EmailMessage` object (for ``message/rfc822``
+ parts), or a ``bytes`` object (for all other non-multipart types). Raise
+ a :exc:`KeyError` if called on a ``multipart``. If the part is a
+ ``text`` part and *errors* is specified, use it as the error handler when
+ decoding the payload to unicode. The default error handler is
+ ``replace``.
+
+ .. method:: set_content(msg, <'str'>, subtype="plain", charset='utf-8' \
+ cte=None, \
+ disposition=None, filename=None, cid=None, \
+ params=None, headers=None)
+ set_content(msg, <'bytes'>, maintype, subtype, cte="base64", \
+ disposition=None, filename=None, cid=None, \
+ params=None, headers=None)
+ set_content(msg, <'Message'>, cte=None, \
+ disposition=None, filename=None, cid=None, \
+ params=None, headers=None)
+ set_content(msg, <'list'>, subtype='mixed', \
+ disposition=None, filename=None, cid=None, \
+ params=None, headers=None)
+
+ Add headers and payload to *msg*:
+
+ Add a :mailheader:`Content-Type` header with a ``maintype/subtype``
+ value.
+
+ * For ``str``, set the MIME ``maintype`` to ``text``, and set the
+ subtype to *subtype* if it is specified, or ``plain`` if it is not.
+ * For ``bytes``, use the specified *maintype* and *subtype*, or
+ raise a :exc:`TypeError` if they are not specified.
+ * For :class:`~email.message.Message` objects, set the maintype to
+ ``message``, and set the subtype to *subtype* if it is specified
+ or ``rfc822`` if it is not. If *subtype* is ``partial``, raise an
+ error (``bytes`` objects must be used to construct
+ ``message/partial`` parts).
+ * For *<'list'>*, which should be a list of
+ :class:`~email.message.Message` objects, set the ``maintype`` to
+ ``multipart``, and the ``subtype`` to *subtype* if it is
+ specified, and ``mixed`` if it is not. If the message parts in
+ the *<'list'>* have :mailheader:`MIME-Version` headers, remove
+ them.
+
+ If *charset* is provided (which is valid only for ``str``), encode the
+ string to bytes using the specified character set. The default is
+ ``utf-8``. If the specified *charset* is a known alias for a standard
+ MIME charset name, use the standard charset instead.
+
+ If *cte* is set, encode the payload using the specified content transfer
+ encoding, and set the :mailheader:`Content-Transfer-Endcoding` header to
+ that value. For ``str`` objects, if it is not set use heuristics to
+ determine the most compact encoding. Possible values for *cte* are
+ ``quoted-printable``, ``base64``, ``7bit``, ``8bit``, and ``binary``.
+ If the input cannot be encoded in the specified encoding (eg: ``7bit``),
+ raise a :exc:`ValueError`. For :class:`~email.message.Message`, per
+ :rfc:`2046`, raise an error if a *cte* of ``quoted-printable`` or
+ ``base64`` is requested for *subtype* ``rfc822``, and for any *cte*
+ other than ``7bit`` for *subtype* ``external-body``. For
+ ``message/rfc822``, use ``8bit`` if *cte* is not specified. For all
+ other values of *subtype*, use ``7bit``.
+
+ .. note:: A *cte* of ``binary`` does not actually work correctly yet.
+ The ``Message`` object as modified by ``set_content`` is correct, but
+ :class:`~email.generator.BytesGenerator` does not serialize it
+ correctly.
+
+ If *disposition* is set, use it as the value of the
+ :mailheader:`Content-Disposition` header. If not specified, and
+ *filename* is specified, add the header with the value ``attachment``.
+ If it is not specified and *filename* is also not specified, do not add
+ the header. The only valid values for *disposition* are ``attachment``
+ and ``inline``.
+
+ If *filename* is specified, use it as the value of the ``filename``
+ parameter of the :mailheader:`Content-Disposition` header. There is no
+ default.
+
+ If *cid* is specified, add a :mailheader:`Content-ID` header with
+ *cid* as its value.
+
+ If *params* is specified, iterate its ``items`` method and use the
+ resulting ``(key, value)`` pairs to set additional paramters on the
+ :mailheader:`Content-Type` header.
+
+ If *headers* is specified and is a list of strings of the form
+ ``headername: headervalue`` or a list of ``header`` objects
+ (distinguised from strings by having a ``name`` attribute), add the
+ headers to *msg*.
diff --git a/Doc/library/email.generator.rst b/Doc/library/email.generator.rst
index c172acb..48d41e1 100644
--- a/Doc/library/email.generator.rst
+++ b/Doc/library/email.generator.rst
@@ -112,7 +112,7 @@ formatted string representation of a message object. For more detail, see
:mod:`email.message`.
.. class:: BytesGenerator(outfp, mangle_from_=True, maxheaderlen=78, *, \
- policy=policy.default)
+ policy=None)
The constructor for the :class:`BytesGenerator` class takes a binary
:term:`file-like object` called *outfp* for an argument. *outfp* must
@@ -134,9 +134,11 @@ 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.
+ number of aspects of the generator's operation. If no *policy* is specified,
+ then the *policy* attached to the message object passed to :attr:`flatten`
+ is used.
.. versionchanged:: 3.3 Added the *policy* keyword.
@@ -174,7 +176,7 @@ formatted string representation of a message object. For more detail, see
Optional *linesep* specifies the line separator character used to
terminate lines in the output. If specified it overrides the value
- specified by the ``Generator``\ 's ``policy``.
+ specified by the ``Generator``\ or *msg*\ 's ``policy``.
.. method:: clone(fp)
diff --git a/Doc/library/email.message.rst b/Doc/library/email.message.rst
index 3075e86..58f708c 100644
--- a/Doc/library/email.message.rst
+++ b/Doc/library/email.message.rst
@@ -33,23 +33,31 @@ Here are the methods of the :class:`Message` class:
.. class:: Message(policy=compat32)
- The *policy* argument determiens the :mod:`~email.policy` that will be used
- to update the message model. The default value, :class:`compat32
- <email.policy.Compat32>` maintains backward compatibility with the
- Python 3.2 version of the email package. For more information see the
+ If *policy* is specified (it must be an instance of a :mod:`~email.policy`
+ class) use the rules it specifies to udpate and serialize the representation
+ of the message. If *policy* is not set, use the :class:`compat32
+ <email.policy.Compat32>` policy, which maintains backward compatibility with
+ the Python 3.2 version of the email package. For more information see the
:mod:`~email.policy` documentation.
.. versionchanged:: 3.3 The *policy* keyword argument was added.
- .. method:: as_string(unixfrom=False, maxheaderlen=0)
+ .. method:: as_string(unixfrom=False, maxheaderlen=0, policy=None)
Return the entire message flattened as a string. When optional *unixfrom*
- is ``True``, the envelope header is included in the returned string.
- *unixfrom* defaults to ``False``. Flattening the message may trigger
- changes to the :class:`Message` if defaults need to be filled in to
- complete the transformation to a string (for example, MIME boundaries may
- be generated or modified).
+ is true, the envelope header is included in the returned string.
+ *unixfrom* defaults to ``False``. For backward compabitility reasons,
+ *maxheaderlen* defaults to ``0``, so if you want a different value you
+ must override it explicitly (the value specified for *max_line_length* in
+ the policy will be ignored by this method). The *policy* argument may be
+ used to override the default policy obtained from the message instance.
+ This can be used to control some of the formatting produced by the
+ method, since the specified *policy* will be passed to the ``Generator``.
+
+ Flattening the message may trigger changes to the :class:`Message` if
+ defaults need to be filled in to complete the transformation to a string
+ (for example, MIME boundaries may be generated or modified).
Note that this method is provided as a convenience and may not always
format the message the way you want. For example, by default it does
@@ -65,10 +73,58 @@ Here are the methods of the :class:`Message` class:
g.flatten(msg)
text = fp.getvalue()
+ If the message object contains binary data that is not encoded according
+ to RFC standards, the non-compliant data will be replaced by unicode
+ "unknown character" code points. (See also :meth:`.as_bytes` and
+ :class:`~email.generator.BytesGenerator`.)
+
+ .. versionchanged:: 3.4 the *policy* keyword argument was added.
+
.. method:: __str__()
- Equivalent to ``as_string(unixfrom=True)``.
+ Equivalent to :meth:`.as_string()`. Allows ``str(msg)`` to produce a
+ string containing the formatted message.
+
+
+ .. method:: as_bytes(unixfrom=False, policy=None)
+
+ Return the entire message flattened as a bytes object. When optional
+ *unixfrom* is true, the envelope header is included in the returned
+ string. *unixfrom* defaults to ``False``. The *policy* argument may be
+ used to override the default policy obtained from the message instance.
+ This can be used to control some of the formatting produced by the
+ method, since the specified *policy* will be passed to the
+ ``BytesGenerator``.
+
+ Flattening the message may trigger changes to the :class:`Message` if
+ defaults need to be filled in to complete the transformation to a string
+ (for example, MIME boundaries may be generated or modified).
+
+ Note that this method is provided as a convenience and may not always
+ format the message the way you want. For example, by default it does
+ not do the mangling of lines that begin with ``From`` that is
+ required by the unix mbox format. For more flexibility, instantiate a
+ :class:`~email.generator.BytesGenerator` instance and use its
+ :meth:`~email.generator.BytesGenerator.flatten` method directly.
+ For example::
+
+ from io import BytesIO
+ from email.generator import BytesGenerator
+ fp = BytesIO()
+ g = BytesGenerator(fp, mangle_from_=True, maxheaderlen=60)
+ g.flatten(msg)
+ text = fp.getvalue()
+
+ .. versionadded:: 3.4
+
+
+ .. method:: __bytes__()
+
+ Equivalent to :meth:`.as_bytes()`. Allows ``bytes(msg)`` to produce a
+ bytes object containing the formatted message.
+
+ .. versionadded:: 3.4
.. method:: is_multipart()
@@ -410,7 +466,8 @@ Here are the methods of the :class:`Message` class:
to ``False``.
- .. method:: set_param(param, value, header='Content-Type', requote=True, charset=None, language='')
+ .. method:: set_param(param, value, header='Content-Type', requote=True, \
+ charset=None, language='', replace=False)
Set a parameter in the :mailheader:`Content-Type` header. If the
parameter already exists in the header, its value will be replaced with
@@ -427,6 +484,12 @@ Here are the methods of the :class:`Message` class:
language, defaulting to the empty string. Both *charset* and *language*
should be strings.
+ If *replace* is ``False`` (the default) the header is moved to the
+ end of the list of headers. If *replace* is ``True``, the header
+ will be updated in place.
+
+ .. versionchanged:: 3.4 ``replace`` keyword was added.
+
.. method:: del_param(param, header='content-type', requote=True)
diff --git a/Doc/library/email.parser.rst b/Doc/library/email.parser.rst
index ee6af3f..90d2c54 100644
--- a/Doc/library/email.parser.rst
+++ b/Doc/library/email.parser.rst
@@ -60,15 +60,18 @@ list of defects that it can find.
Here is the API for the :class:`FeedParser`:
-.. class:: FeedParser(_factory=email.message.Message, *, policy=policy.default)
+.. class:: FeedParser(_factory=email.message.Message, *, policy=policy.compat32)
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.
+ If *policy* is specified (it must be an instance of a :mod:`~email.policy`
+ class) use the rules it specifies to udpate the representation of the
+ message. If *policy* is not set, use the :class:`compat32
+ <email.policy.Compat32>` policy, which maintains backward compatibility with
+ the Python 3.2 version of the email package. For more information see the
+ :mod:`~email.policy` documentation.
.. versionchanged:: 3.3 Added the *policy* keyword.
@@ -113,7 +116,7 @@ have the same API as the :class:`Parser` and :class:`BytesParser` classes.
The BytesHeaderParser class.
-.. class:: Parser(_class=email.message.Message, *, policy=policy.default)
+.. class:: Parser(_class=email.message.Message, *, policy=policy.compat32)
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
@@ -121,9 +124,12 @@ have the same API as the :class:`Parser` and :class:`BytesParser` classes.
:class:`~email.message.Message` (see :mod:`email.message`). The factory will
be called without arguments.
- 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.
+ If *policy* is specified (it must be an instance of a :mod:`~email.policy`
+ class) use the rules it specifies to udpate the representation of the
+ message. If *policy* is not set, use the :class:`compat32
+ <email.policy.Compat32>` policy, which maintains backward compatibility with
+ the Python 3.2 version of the email package. For more information see the
+ :mod:`~email.policy` documentation.
.. versionchanged:: 3.3
Removed the *strict* argument that was deprecated in 2.4. Added the
@@ -159,15 +165,18 @@ have the same API as the :class:`Parser` and :class:`BytesParser` classes.
Optional *headersonly* is as with the :meth:`parse` method.
-.. class:: BytesParser(_class=email.message.Message, *, policy=policy.default)
+.. class:: BytesParser(_class=email.message.Message, *, policy=policy.compat32)
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.
- 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.
+ If *policy* is specified (it must be an instance of a :mod:`~email.policy`
+ class) use the rules it specifies to udpate the representation of the
+ message. If *policy* is not set, use the :class:`compat32
+ <email.policy.Compat32>` policy, which maintains backward compatibility with
+ the Python 3.2 version of the email package. For more information see the
+ :mod:`~email.policy` documentation.
.. versionchanged:: 3.3
Removed the *strict* argument. Added the *policy* keyword.
@@ -209,7 +218,7 @@ in the top-level :mod:`email` package namespace.
.. currentmodule:: email
.. function:: message_from_string(s, _class=email.message.Message, *, \
- policy=policy.default)
+ policy=policy.compat32)
Return a message object structure from a string. This is exactly equivalent to
``Parser().parsestr(s)``. *_class* and *policy* are interpreted as
@@ -219,7 +228,7 @@ in the top-level :mod:`email` package namespace.
Removed the *strict* argument. Added the *policy* keyword.
.. function:: message_from_bytes(s, _class=email.message.Message, *, \
- policy=policy.default)
+ policy=policy.compat32)
Return a message object structure from a byte string. This is exactly
equivalent to ``BytesParser().parsebytes(s)``. Optional *_class* and
@@ -231,7 +240,7 @@ in the top-level :mod:`email` package namespace.
Removed the *strict* argument. Added the *policy* keyword.
.. function:: message_from_file(fp, _class=email.message.Message, *, \
- policy=policy.default)
+ policy=policy.compat32)
Return a message object structure tree from an open :term:`file object`.
This is exactly equivalent to ``Parser().parse(fp)``. *_class*
@@ -242,7 +251,7 @@ in the top-level :mod:`email` package namespace.
Removed the *strict* argument. Added the *policy* keyword.
.. function:: message_from_binary_file(fp, _class=email.message.Message, *, \
- policy=policy.default)
+ policy=policy.compat32)
Return a message object structure tree from an open binary :term:`file
object`. This is exactly equivalent to ``BytesParser().parse(fp)``.
diff --git a/Doc/library/email.policy.rst b/Doc/library/email.policy.rst
index cb2023c..97358f0 100644
--- a/Doc/library/email.policy.rst
+++ b/Doc/library/email.policy.rst
@@ -97,6 +97,17 @@ correct line separator characters when creating the binary string to feed into
``sendmail's`` ``stdin``, where the default policy would use ``\n`` line
separators.
+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 writes the message to a file using the native line
+separators for the platform on which it is running::
+
+ >>> import os
+ >>> with open('converted.txt', 'wb') as f:
+ ... f.write(msg.as_bytes(policy=msg.policy.clone(linesep=os.linesep)))
+ 17
+
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::
@@ -360,7 +371,7 @@ added matters. To illustrate::
to) :rfc:`5322`, :rfc:`2047`, and the current MIME RFCs.
This policy adds new header parsing and folding algorithms. Instead of
- simple strings, headers are custom objects with custom attributes depending
+ simple strings, headers are ``str`` subclasses with attributes that depend
on the type of the field. The parsing and folding algorithm fully implement
:rfc:`2047` and :rfc:`5322`.
@@ -397,6 +408,20 @@ added matters. To illustrate::
fields are treated as unstructured. This list will be completed before
the extension is marked stable.)
+ .. attribute:: content_manager
+
+ An object with at least two methods: get_content and set_content. When
+ the :meth:`~email.message.Message.get_content` or
+ :meth:`~email.message.Message.set_content` method of a
+ :class:`~email.message.Message` object is called, it calls the
+ corresponding method of this object, passing it the message object as its
+ first argument, and any arguments or keywords that were passed to it as
+ additional arguments. By default ``content_manager`` is set to
+ :data:`~email.contentmanager.raw_data_manager`.
+
+ .. versionadded:: 3.4
+
+
The class provides the following concrete implementations of the abstract
methods of :class:`Policy`:
@@ -416,7 +441,7 @@ added matters. To illustrate::
The name is returned unchanged. If the input value has a ``name``
attribute and it matches *name* ignoring case, the value is returned
unchanged. Otherwise the *name* and *value* are passed to
- ``header_factory``, and the resulting custom header object is returned as
+ ``header_factory``, and the resulting header object is returned as
the value. In this case a ``ValueError`` is raised if the input value
contains CR or LF characters.
@@ -424,7 +449,7 @@ added matters. To illustrate::
If the value has a ``name`` attribute, it is returned to unmodified.
Otherwise the *name*, and the *value* with any CR or LF characters
- removed, are passed to the ``header_factory``, and the resulting custom
+ removed, are passed to the ``header_factory``, and the resulting
header object is returned. Any surrogateescaped bytes get turned into
the unicode unknown-character glyph.
@@ -434,9 +459,9 @@ added matters. To illustrate::
A value is considered to be a 'source value' if and only if it does not
have a ``name`` attribute (having a ``name`` attribute means it is a
header object of some sort). If a source value needs to be refolded
- according to the policy, it is converted into a custom header object by
+ according to the policy, it is converted into a header object by
passing the *name* and the *value* with any CR and LF characters removed
- to the ``header_factory``. Folding of a custom header object is done by
+ to the ``header_factory``. Folding of a header object is done by
calling its ``fold`` method with the current policy.
Source values are split into lines using :meth:`~str.splitlines`. If
@@ -491,23 +516,23 @@ With all of these :class:`EmailPolicies <.EmailPolicy>`, the effective API of
the email package is changed from the Python 3.2 API in the following ways:
* Setting a header on a :class:`~email.message.Message` results in that
- header being parsed and a custom header object created.
+ header being parsed and a header object created.
* Fetching a header value from a :class:`~email.message.Message` results
- in that header being parsed and a custom header object created and
+ in that header being parsed and a header object created and
returned.
- * Any custom header object, or any header that is refolded due to the
+ * Any header object, or any header that is refolded due to the
policy settings, is folded using an algorithm that fully implements the
RFC folding algorithms, including knowing where encoded words are required
and allowed.
From the application view, this means that any header obtained through the
-:class:`~email.message.Message` is a custom header object with custom
+:class:`~email.message.Message` is a header object with extra
attributes, whose string value is the fully decoded unicode value of the
header. Likewise, a header may be assigned a new value, or a new header
created, using a unicode string, and the policy will take care of converting
the unicode string into the correct RFC encoded form.
-The custom header objects and their attributes are described in
+The header objects and their attributes are described in
:mod:`~email.headerregistry`.
diff --git a/Doc/library/email.rst b/Doc/library/email.rst
index a6cbbce..331d2ef 100644
--- a/Doc/library/email.rst
+++ b/Doc/library/email.rst
@@ -53,6 +53,7 @@ Contents of the :mod:`email` package documentation:
email.generator.rst
email.policy.rst
email.headerregistry.rst
+ email.contentmanager.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 f75975e..219e284 100644
--- a/Doc/library/email.util.rst
+++ b/Doc/library/email.util.rst
@@ -98,12 +98,9 @@ There are several useful utilities provided in the :mod:`email.utils` module:
.. function:: mktime_tz(tuple)
- Turn a 10-tuple as returned by :func:`parsedate_tz` into a UTC timestamp. It
- the timezone item in the tuple is ``None``, assume local time. Minor
- deficiency: :func:`mktime_tz` interprets the first 8 elements of *tuple* as a
- local time and then compensates for the timezone difference. This may yield a
- slight error around changes in daylight savings time, though not worth worrying
- about for common use.
+ Turn a 10-tuple as returned by :func:`parsedate_tz` into a UTC
+ timestamp (seconds since the Epoch). If the timezone item in the
+ tuple is ``None``, assume local time.
.. function:: formatdate(timeval=None, localtime=False, usegmt=False)
@@ -210,4 +207,3 @@ There are several useful utilities provided in the :mod:`email.utils` module:
.. [#] Note that the sign of the timezone offset is the opposite of the sign of the
``time.timezone`` variable for the same timezone; the latter variable follows
the POSIX standard while this module follows :rfc:`2822`.
-
diff --git a/Doc/library/ensurepip.rst b/Doc/library/ensurepip.rst
new file mode 100644
index 0000000..8012f51
--- /dev/null
+++ b/Doc/library/ensurepip.rst
@@ -0,0 +1,131 @@
+:mod:`ensurepip` --- Bootstrapping the ``pip`` installer
+========================================================
+
+.. module:: ensurepip
+ :synopsis: Bootstrapping the ``pip`` installer into an existing Python
+ installation or virtual environment.
+
+.. versionadded:: 3.4
+
+The :mod:`ensurepip` package provides support for bootstrapping the ``pip``
+installer into an existing Python installation or virtual environment. This
+bootstrapping approach reflects the fact that ``pip`` is an independent
+project with its own release cycle, and the latest available stable version
+is bundled with maintenance and feature releases of the CPython reference
+interpreter.
+
+In most cases, end users of Python shouldn't need to invoke this module
+directly (as ``pip`` should be bootstrapped by default), but it may be
+needed if installing ``pip`` was skipped when installing Python (or
+when creating a virtual environment) or after explicitly uninstalling
+``pip``.
+
+.. note::
+
+ This module *does not* access the internet. All of the components
+ needed to bootstrap ``pip`` are included as internal parts of the
+ package.
+
+.. seealso::
+
+ :ref:`installing-index`
+ The end user guide for installing Python packages
+
+ :pep:`453`: Explicit bootstrapping of pip in Python installations
+ The original rationale and specification for this module.
+
+
+Command line interface
+----------------------
+
+The command line interface is invoked using the interpreter's ``-m`` switch.
+
+The simplest possible invocation is::
+
+ python -m ensurepip
+
+This invocation will install ``pip`` if it is not already installed,
+but otherwise does nothing. To ensure the installed version of ``pip``
+is at least as recent as the one bundled with ``ensurepip``, pass the
+``--upgrade`` option::
+
+ python -m ensurepip --upgrade
+
+By default, ``pip`` is installed into the current virtual environment
+(if one is active) or into the system site packages (if there is no
+active virtual environment). The installation location can be controlled
+through two additional command line options:
+
+* ``--root <dir>``: Installs ``pip`` relative to the given root directory
+ rather than the root of the currently active virtual environment (if any)
+ or the default root for the current Python installation.
+* ``--user``: Installs ``pip`` into the user site packages directory rather
+ than globally for the current Python installation (this option is not
+ permitted inside an active virtual environment).
+
+By default, the scripts ``pipX`` and ``pipX.Y`` will be installed (where
+X.Y stands for the version of Python used to invoke ``ensurepip``). The
+scripts installed can be controlled through two additional command line
+options:
+
+* ``--altinstall``: if an alternate installation is requested, the ``pipX``
+ script will *not* be installed.
+
+* ``--default-pip``: if a "default pip" installation is requested, the
+ ``pip`` script will be installed in addition to the two regular scripts.
+
+Providing both of the script selection options will trigger an exception.
+
+
+Module API
+----------
+
+:mod:`ensurepip` exposes two functions for programmatic use:
+
+.. function:: version()
+
+ Returns a string specifying the bundled version of pip that will be
+ installed when bootstrapping an environment.
+
+.. function:: bootstrap(root=None, upgrade=False, user=False, \
+ altinstall=False, default_pip=False, \
+ verbosity=0)
+
+ Bootstraps ``pip`` into the current or designated environment.
+
+ *root* specifies an alternative root directory to install relative to.
+ If *root* is None, then installation uses the default install location
+ for the current environment.
+
+ *upgrade* indicates whether or not to upgrade an existing installation
+ of an earlier version of ``pip`` to the bundled version.
+
+ *user* indicates whether to use the user scheme rather than installing
+ globally.
+
+ By default, the scripts ``pipX`` and ``pipX.Y`` will be installed (where
+ X.Y stands for the current version of Python).
+
+ If *altinstall* is set, then ``pipX`` will *not* be installed.
+
+ If *default_pip* is set, then ``pip`` will be installed in addition to
+ the two regular scripts.
+
+ Setting both *altinstall* and *default_pip* will trigger
+ :exc:`ValueError`.
+
+ *verbosity* controls the level of output to :data:`sys.stdout` from the
+ bootstrapping operation.
+
+ .. note::
+
+ The bootstrapping process has side effects on both ``sys.path`` and
+ ``os.environ``. Invoking the command line interface in a subprocess
+ instead allows these side effects to be avoided.
+
+ .. note::
+
+ The bootstrapping process may install additional modules required by
+ ``pip``, but other software should not assume those dependencies will
+ always be present by default (as the dependencies may be removed in a
+ future version of ``pip``).
diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst
new file mode 100644
index 0000000..acdcf7f
--- /dev/null
+++ b/Doc/library/enum.rst
@@ -0,0 +1,749 @@
+:mod:`enum` --- Support for enumerations
+========================================
+
+.. module:: enum
+ :synopsis: Implementation of an enumeration class.
+
+.. :moduleauthor:: Ethan Furman <ethan@stoneleaf.us>
+.. :sectionauthor:: Barry Warsaw <barry@python.org>,
+.. :sectionauthor:: Eli Bendersky <eliben@gmail.com>,
+.. :sectionauthor:: Ethan Furman <ethan@stoneleaf.us>
+
+.. versionadded:: 3.4
+
+**Source code:** :source:`Lib/enum.py`
+
+----------------
+
+An enumeration is a set of symbolic names (members) bound to unique,
+constant values. Within an enumeration, the members can be compared
+by identity, and the enumeration itself can be iterated over.
+
+
+Module Contents
+---------------
+
+This module defines two enumeration classes that can be used to define unique
+sets of names and values: :class:`Enum` and :class:`IntEnum`. It also defines
+one decorator, :func:`unique`.
+
+.. class:: Enum
+
+ Base class for creating enumerated constants. See section
+ `Functional API`_ for an alternate construction syntax.
+
+.. class:: IntEnum
+
+ Base class for creating enumerated constants that are also
+ subclasses of :class:`int`.
+
+.. function:: unique
+
+ Enum class decorator that ensures only one name is bound to any one value.
+
+
+Creating an Enum
+----------------
+
+Enumerations are created using the :keyword:`class` syntax, which makes them
+easy to read and write. An alternative creation method is described in
+`Functional API`_. To define an enumeration, subclass :class:`Enum` as
+follows::
+
+ >>> from enum import Enum
+ >>> class Color(Enum):
+ ... red = 1
+ ... green = 2
+ ... blue = 3
+ ...
+
+.. note:: Nomenclature
+
+ - The class :class:`Color` is an *enumeration* (or *enum*)
+ - The attributes :attr:`Color.red`, :attr:`Color.green`, etc., are
+ *enumeration members* (or *enum members*).
+ - The enum members have *names* and *values* (the name of
+ :attr:`Color.red` is ``red``, the value of :attr:`Color.blue` is
+ ``3``, etc.)
+
+.. note::
+
+ Even though we use the :keyword:`class` syntax to create Enums, Enums
+ are not normal Python classes. See `How are Enums different?`_ for
+ more details.
+
+Enumeration members have human readable string representations::
+
+ >>> print(Color.red)
+ Color.red
+
+...while their ``repr`` has more information::
+
+ >>> print(repr(Color.red))
+ <Color.red: 1>
+
+The *type* of an enumeration member is the enumeration it belongs to::
+
+ >>> type(Color.red)
+ <enum 'Color'>
+ >>> isinstance(Color.green, Color)
+ True
+ >>>
+
+Enum members also have a property that contains just their item name::
+
+ >>> print(Color.red.name)
+ red
+
+Enumerations support iteration, in definition order::
+
+ >>> class Shake(Enum):
+ ... vanilla = 7
+ ... chocolate = 4
+ ... cookies = 9
+ ... mint = 3
+ ...
+ >>> for shake in Shake:
+ ... print(shake)
+ ...
+ Shake.vanilla
+ Shake.chocolate
+ Shake.cookies
+ Shake.mint
+
+Enumeration members are hashable, so they can be used in dictionaries and sets::
+
+ >>> apples = {}
+ >>> apples[Color.red] = 'red delicious'
+ >>> apples[Color.green] = 'granny smith'
+ >>> apples == {Color.red: 'red delicious', Color.green: 'granny smith'}
+ True
+
+
+Programmatic access to enumeration members and their attributes
+---------------------------------------------------------------
+
+Sometimes it's useful to access members in enumerations programmatically (i.e.
+situations where ``Color.red`` won't do because the exact color is not known
+at program-writing time). ``Enum`` allows such access::
+
+ >>> Color(1)
+ <Color.red: 1>
+ >>> Color(3)
+ <Color.blue: 3>
+
+If you want to access enum members by *name*, use item access::
+
+ >>> Color['red']
+ <Color.red: 1>
+ >>> Color['green']
+ <Color.green: 2>
+
+If you have an enum member and need its :attr:`name` or :attr:`value`::
+
+ >>> member = Color.red
+ >>> member.name
+ 'red'
+ >>> member.value
+ 1
+
+
+Duplicating enum members and values
+-----------------------------------
+
+Having two enum members with the same name is invalid::
+
+ >>> class Shape(Enum):
+ ... square = 2
+ ... square = 3
+ ...
+ Traceback (most recent call last):
+ ...
+ TypeError: Attempted to reuse key: 'square'
+
+However, two enum members are allowed to have the same value. Given two members
+A and B with the same value (and A defined first), B is an alias to A. By-value
+lookup of the value of A and B will return A. By-name lookup of B will also
+return A::
+
+ >>> class Shape(Enum):
+ ... square = 2
+ ... diamond = 1
+ ... circle = 3
+ ... alias_for_square = 2
+ ...
+ >>> Shape.square
+ <Shape.square: 2>
+ >>> Shape.alias_for_square
+ <Shape.square: 2>
+ >>> Shape(2)
+ <Shape.square: 2>
+
+.. note::
+
+ Attempting to create a member with the same name as an already
+ defined attribute (another member, a method, etc.) or attempting to create
+ an attribute with the same name as a member is not allowed.
+
+
+Ensuring unique enumeration values
+----------------------------------
+
+By default, enumerations allow multiple names as aliases for the same value.
+When this behavior isn't desired, the following decorator can be used to
+ensure each value is used only once in the enumeration:
+
+.. decorator:: unique
+
+A :keyword:`class` decorator specifically for enumerations. It searches an
+enumeration's :attr:`__members__` gathering any aliases it finds; if any are
+found :exc:`ValueError` is raised with the details::
+
+ >>> from enum import Enum, unique
+ >>> @unique
+ ... class Mistake(Enum):
+ ... one = 1
+ ... two = 2
+ ... three = 3
+ ... four = 3
+ ...
+ Traceback (most recent call last):
+ ...
+ ValueError: duplicate values found in <enum 'Mistake'>: four -> three
+
+
+Iteration
+---------
+
+Iterating over the members of an enum does not provide the aliases::
+
+ >>> list(Shape)
+ [<Shape.square: 2>, <Shape.diamond: 1>, <Shape.circle: 3>]
+
+The special attribute ``__members__`` is an ordered dictionary mapping names
+to members. It includes all names defined in the enumeration, including the
+aliases::
+
+ >>> for name, member in Shape.__members__.items():
+ ... name, member
+ ...
+ ('square', <Shape.square: 2>)
+ ('diamond', <Shape.diamond: 1>)
+ ('circle', <Shape.circle: 3>)
+ ('alias_for_square', <Shape.square: 2>)
+
+The ``__members__`` attribute can be used for detailed programmatic access to
+the enumeration members. For example, finding all the aliases::
+
+ >>> [name for name, member in Shape.__members__.items() if member.name != name]
+ ['alias_for_square']
+
+
+Comparisons
+-----------
+
+Enumeration members are compared by identity::
+
+ >>> Color.red is Color.red
+ True
+ >>> Color.red is Color.blue
+ False
+ >>> Color.red is not Color.blue
+ True
+
+Ordered comparisons between enumeration values are *not* supported. Enum
+members are not integers (but see `IntEnum`_ below)::
+
+ >>> Color.red < Color.blue
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+ TypeError: unorderable types: Color() < Color()
+
+Equality comparisons are defined though::
+
+ >>> Color.blue == Color.red
+ False
+ >>> Color.blue != Color.red
+ True
+ >>> Color.blue == Color.blue
+ True
+
+Comparisons against non-enumeration values will always compare not equal
+(again, :class:`IntEnum` was explicitly designed to behave differently, see
+below)::
+
+ >>> Color.blue == 2
+ False
+
+
+Allowed members and attributes of enumerations
+----------------------------------------------
+
+The examples above use integers for enumeration values. Using integers is
+short and handy (and provided by default by the `Functional API`_), but not
+strictly enforced. In the vast majority of use-cases, one doesn't care what
+the actual value of an enumeration is. But if the value *is* important,
+enumerations can have arbitrary values.
+
+Enumerations are Python classes, and can have methods and special methods as
+usual. If we have this enumeration::
+
+ >>> class Mood(Enum):
+ ... funky = 1
+ ... happy = 3
+ ...
+ ... def describe(self):
+ ... # self is the member here
+ ... return self.name, self.value
+ ...
+ ... def __str__(self):
+ ... return 'my custom str! {0}'.format(self.value)
+ ...
+ ... @classmethod
+ ... def favorite_mood(cls):
+ ... # cls here is the enumeration
+ ... return cls.happy
+ ...
+
+Then::
+
+ >>> Mood.favorite_mood()
+ <Mood.happy: 3>
+ >>> Mood.happy.describe()
+ ('happy', 3)
+ >>> str(Mood.funky)
+ 'my custom str! 1'
+
+The rules for what is allowed are as follows: _sunder_ names (starting and
+ending with a single underscore) are reserved by enum and cannot be used;
+all other attributes defined within an enumeration will become members of this
+enumeration, with the exception of *__dunder__* names and descriptors (methods
+are also descriptors).
+
+Note: if your enumeration defines :meth:`__new__` and/or :meth:`__init__` then
+whatever value(s) were given to the enum member will be passed into those
+methods. See `Planet`_ for an example.
+
+
+Restricted subclassing of enumerations
+--------------------------------------
+
+Subclassing an enumeration is allowed only if the enumeration does not define
+any members. So this is forbidden::
+
+ >>> class MoreColor(Color):
+ ... pink = 17
+ ...
+ Traceback (most recent call last):
+ ...
+ TypeError: Cannot extend enumerations
+
+But this is allowed::
+
+ >>> class Foo(Enum):
+ ... def some_behavior(self):
+ ... pass
+ ...
+ >>> class Bar(Foo):
+ ... happy = 1
+ ... sad = 2
+ ...
+
+Allowing subclassing of enums that define members would lead to a violation of
+some important invariants of types and instances. On the other hand, it makes
+sense to allow sharing some common behavior between a group of enumerations.
+(See `OrderedEnum`_ for an example.)
+
+
+Pickling
+--------
+
+Enumerations can be pickled and unpickled::
+
+ >>> from test.test_enum import Fruit
+ >>> from pickle import dumps, loads
+ >>> Fruit.tomato is loads(dumps(Fruit.tomato))
+ True
+
+The usual restrictions for pickling apply: picklable enums must be defined in
+the top level of a module, since unpickling requires them to be importable
+from that module.
+
+.. note::
+
+ With pickle protocol version 4 it is possible to easily pickle enums
+ nested in other classes.
+
+It is possible to modify how Enum members are pickled/unpickled by defining
+:meth:`__reduce_ex__` in the enumeration class.
+
+
+Functional API
+--------------
+
+The :class:`Enum` class is callable, providing the following functional API::
+
+ >>> Animal = Enum('Animal', 'ant bee cat dog')
+ >>> Animal
+ <enum 'Animal'>
+ >>> Animal.ant
+ <Animal.ant: 1>
+ >>> Animal.ant.value
+ 1
+ >>> list(Animal)
+ [<Animal.ant: 1>, <Animal.bee: 2>, <Animal.cat: 3>, <Animal.dog: 4>]
+
+The semantics of this API resemble :class:`~collections.namedtuple`. The first
+argument of the call to :class:`Enum` is the name of the enumeration.
+
+The second argument is the *source* of enumeration member names. It can be a
+whitespace-separated string of names, a sequence of names, a sequence of
+2-tuples with key/value pairs, or a mapping (e.g. dictionary) of names to
+values. The last two options enable assigning arbitrary values to
+enumerations; the others auto-assign increasing integers starting with 1. A
+new class derived from :class:`Enum` is returned. In other words, the above
+assignment to :class:`Animal` is equivalent to::
+
+ >>> class Animals(Enum):
+ ... ant = 1
+ ... bee = 2
+ ... cat = 3
+ ... dog = 4
+ ...
+
+The reason for defaulting to ``1`` as the starting number and not ``0`` is
+that ``0`` is ``False`` in a boolean sense, but enum members all evaluate
+to ``True``.
+
+Pickling enums created with the functional API can be tricky as frame stack
+implementation details are used to try and figure out which module the
+enumeration is being created in (e.g. it will fail if you use a utility
+function in separate module, and also may not work on IronPython or Jython).
+The solution is to specify the module name explicitly as follows::
+
+ >>> Animals = Enum('Animals', 'ant bee cat dog', module=__name__)
+
+.. warning::
+
+ If ``module`` is not supplied, and Enum cannot determine what it is,
+ the new Enum members will not be unpicklable; to keep errors closer to
+ the source, pickling will be disabled.
+
+The new pickle protocol 4 also, in some circumstances, relies on
+:attr:`__qualname__` being set to the location where pickle will be able
+to find the class. For example, if the class was made available in class
+SomeData in the global scope::
+
+ >>> Animals = Enum('Animals', 'ant bee cat dog', qualname='SomeData.Animals')
+
+The complete signature is::
+
+ Enum(value='NewEnumName', names=<...>, *, module='...', qualname='...', type=<mixed-in class>)
+
+:value: What the new Enum class will record as its name.
+
+:names: The Enum members. This can be a whitespace or comma separated string
+ (values will start at 1)::
+
+ 'red green blue' | 'red,green,blue' | 'red, green, blue'
+
+ or an iterator of (name, value) pairs::
+
+ [('cyan', 4), ('magenta', 5), ('yellow', 6)]
+
+ or a mapping::
+
+ {'chartreuse': 7, 'sea_green': 11, 'rosemary': 42}
+
+:module: name of module where new Enum class can be found.
+
+:qualname: where in module new Enum class can be found.
+
+:type: type to mix in to new Enum class.
+
+
+Derived Enumerations
+--------------------
+
+IntEnum
+^^^^^^^
+
+A variation of :class:`Enum` is provided which is also a subclass of
+:class:`int`. Members of an :class:`IntEnum` can be compared to integers;
+by extension, integer enumerations of different types can also be compared
+to each other::
+
+ >>> from enum import IntEnum
+ >>> class Shape(IntEnum):
+ ... circle = 1
+ ... square = 2
+ ...
+ >>> class Request(IntEnum):
+ ... post = 1
+ ... get = 2
+ ...
+ >>> Shape == 1
+ False
+ >>> Shape.circle == 1
+ True
+ >>> Shape.circle == Request.post
+ True
+
+However, they still can't be compared to standard :class:`Enum` enumerations::
+
+ >>> class Shape(IntEnum):
+ ... circle = 1
+ ... square = 2
+ ...
+ >>> class Color(Enum):
+ ... red = 1
+ ... green = 2
+ ...
+ >>> Shape.circle == Color.red
+ False
+
+:class:`IntEnum` values behave like integers in other ways you'd expect::
+
+ >>> int(Shape.circle)
+ 1
+ >>> ['a', 'b', 'c'][Shape.circle]
+ 'b'
+ >>> [i for i in range(Shape.square)]
+ [0, 1]
+
+For the vast majority of code, :class:`Enum` is strongly recommended,
+since :class:`IntEnum` breaks some semantic promises of an enumeration (by
+being comparable to integers, and thus by transitivity to other
+unrelated enumerations). It should be used only in special cases where
+there's no other choice; for example, when integer constants are
+replaced with enumerations and backwards compatibility is required with code
+that still expects integers.
+
+
+Others
+^^^^^^
+
+While :class:`IntEnum` is part of the :mod:`enum` module, it would be very
+simple to implement independently::
+
+ class IntEnum(int, Enum):
+ pass
+
+This demonstrates how similar derived enumerations can be defined; for example
+a :class:`StrEnum` that mixes in :class:`str` instead of :class:`int`.
+
+Some rules:
+
+1. When subclassing :class:`Enum`, mix-in types must appear before
+ :class:`Enum` itself in the sequence of bases, as in the :class:`IntEnum`
+ example above.
+2. While :class:`Enum` can have members of any type, once you mix in an
+ additional type, all the members must have values of that type, e.g.
+ :class:`int` above. This restriction does not apply to mix-ins which only
+ add methods and don't specify another data type such as :class:`int` or
+ :class:`str`.
+3. When another data type is mixed in, the :attr:`value` attribute is *not the
+ same* as the enum member itself, although it is equivalent and will compare
+ equal.
+4. %-style formatting: `%s` and `%r` call :class:`Enum`'s :meth:`__str__` and
+ :meth:`__repr__` respectively; other codes (such as `%i` or `%h` for
+ IntEnum) treat the enum member as its mixed-in type.
+5. :meth:`str.__format__` (or :func:`format`) will use the mixed-in
+ type's :meth:`__format__`. If the :class:`Enum`'s :func:`str` or
+ :func:`repr` is desired use the `!s` or `!r` :class:`str` format codes.
+
+
+Interesting examples
+--------------------
+
+While :class:`Enum` and :class:`IntEnum` are expected to cover the majority of
+use-cases, they cannot cover them all. Here are recipes for some different
+types of enumerations that can be used directly, or as examples for creating
+one's own.
+
+
+AutoNumber
+^^^^^^^^^^
+
+Avoids having to specify the value for each enumeration member::
+
+ >>> class AutoNumber(Enum):
+ ... def __new__(cls):
+ ... value = len(cls.__members__) + 1
+ ... obj = object.__new__(cls)
+ ... obj._value_ = value
+ ... return obj
+ ...
+ >>> class Color(AutoNumber):
+ ... red = ()
+ ... green = ()
+ ... blue = ()
+ ...
+ >>> Color.green.value == 2
+ True
+
+.. note::
+
+ The :meth:`__new__` method, if defined, is used during creation of the Enum
+ members; it is then replaced by Enum's :meth:`__new__` which is used after
+ class creation for lookup of existing members. Due to the way Enums are
+ supposed to behave, there is no way to customize Enum's :meth:`__new__`.
+
+
+OrderedEnum
+^^^^^^^^^^^
+
+An ordered enumeration that is not based on :class:`IntEnum` and so maintains
+the normal :class:`Enum` invariants (such as not being comparable to other
+enumerations)::
+
+ >>> class OrderedEnum(Enum):
+ ... def __ge__(self, other):
+ ... if self.__class__ is other.__class__:
+ ... return self.value >= other.value
+ ... return NotImplemented
+ ... def __gt__(self, other):
+ ... if self.__class__ is other.__class__:
+ ... return self.value > other.value
+ ... return NotImplemented
+ ... def __le__(self, other):
+ ... if self.__class__ is other.__class__:
+ ... return self.value <= other.value
+ ... return NotImplemented
+ ... def __lt__(self, other):
+ ... if self.__class__ is other.__class__:
+ ... return self.value < other.value
+ ... return NotImplemented
+ ...
+ >>> class Grade(OrderedEnum):
+ ... A = 5
+ ... B = 4
+ ... C = 3
+ ... D = 2
+ ... F = 1
+ ...
+ >>> Grade.C < Grade.A
+ True
+
+
+DuplicateFreeEnum
+^^^^^^^^^^^^^^^^^
+
+Raises an error if a duplicate member name is found instead of creating an
+alias::
+
+ >>> class DuplicateFreeEnum(Enum):
+ ... def __init__(self, *args):
+ ... cls = self.__class__
+ ... if any(self.value == e.value for e in cls):
+ ... a = self.name
+ ... e = cls(self.value).name
+ ... raise ValueError(
+ ... "aliases not allowed in DuplicateFreeEnum: %r --> %r"
+ ... % (a, e))
+ ...
+ >>> class Color(DuplicateFreeEnum):
+ ... red = 1
+ ... green = 2
+ ... blue = 3
+ ... grene = 2
+ ...
+ Traceback (most recent call last):
+ ...
+ ValueError: aliases not allowed in DuplicateFreeEnum: 'grene' --> 'green'
+
+.. note::
+
+ This is a useful example for subclassing Enum to add or change other
+ behaviors as well as disallowing aliases. If the only desired change is
+ disallowing aliases, the :func:`unique` decorator can be used instead.
+
+
+Planet
+^^^^^^
+
+If :meth:`__new__` or :meth:`__init__` is defined the value of the enum member
+will be passed to those methods::
+
+ >>> class Planet(Enum):
+ ... MERCURY = (3.303e+23, 2.4397e6)
+ ... VENUS = (4.869e+24, 6.0518e6)
+ ... EARTH = (5.976e+24, 6.37814e6)
+ ... MARS = (6.421e+23, 3.3972e6)
+ ... JUPITER = (1.9e+27, 7.1492e7)
+ ... SATURN = (5.688e+26, 6.0268e7)
+ ... URANUS = (8.686e+25, 2.5559e7)
+ ... NEPTUNE = (1.024e+26, 2.4746e7)
+ ... def __init__(self, mass, radius):
+ ... self.mass = mass # in kilograms
+ ... self.radius = radius # in meters
+ ... @property
+ ... def surface_gravity(self):
+ ... # universal gravitational constant (m3 kg-1 s-2)
+ ... G = 6.67300E-11
+ ... return G * self.mass / (self.radius * self.radius)
+ ...
+ >>> Planet.EARTH.value
+ (5.976e+24, 6378140.0)
+ >>> Planet.EARTH.surface_gravity
+ 9.802652743337129
+
+
+How are Enums different?
+------------------------
+
+Enums have a custom metaclass that affects many aspects of both derived Enum
+classes and their instances (members).
+
+
+Enum Classes
+^^^^^^^^^^^^
+
+The :class:`EnumMeta` metaclass is responsible for providing the
+:meth:`__contains__`, :meth:`__dir__`, :meth:`__iter__` and other methods that
+allow one to do things with an :class:`Enum` class that fail on a typical
+class, such as `list(Color)` or `some_var in Color`. :class:`EnumMeta` is
+responsible for ensuring that various other methods on the final :class:`Enum`
+class are correct (such as :meth:`__new__`, :meth:`__getnewargs__`,
+:meth:`__str__` and :meth:`__repr__`)
+
+
+Enum Members (aka instances)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The most interesting thing about Enum members is that they are singletons.
+:class:`EnumMeta` creates them all while it is creating the :class:`Enum`
+class itself, and then puts a custom :meth:`__new__` in place to ensure
+that no new ones are ever instantiated by returning only the existing
+member instances.
+
+
+Finer Points
+^^^^^^^^^^^^
+
+Enum members are instances of an Enum class, and even though they are
+accessible as `EnumClass.member`, they are not accessible directly from
+the member::
+
+ >>> Color.red
+ <Color.red: 1>
+ >>> Color.red.blue
+ Traceback (most recent call last):
+ ...
+ AttributeError: 'Color' object has no attribute 'blue'
+
+Likewise, the :attr:`__members__` is only available on the class.
+
+If you give your :class:`Enum` subclass extra methods, like the `Planet`_
+class above, those methods will show up in a :func:`dir` of the member,
+but not of the class::
+
+ >>> dir(Planet)
+ ['EARTH', 'JUPITER', 'MARS', 'MERCURY', 'NEPTUNE', 'SATURN', 'URANUS', 'VENUS', '__class__', '__doc__', '__members__', '__module__']
+ >>> dir(Planet.EARTH)
+ ['__class__', '__doc__', '__module__', 'name', 'surface_gravity', 'value']
+
+A :meth:`__new__` method will only be used for the creation of the
+:class:`Enum` members -- after that it is replaced. This means if you wish to
+change how :class:`Enum` members are looked up you either have to write a
+helper function or a :func:`classmethod`.
diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst
index 6e4559c..45a6b1b 100644
--- a/Doc/library/exceptions.rst
+++ b/Doc/library/exceptions.rst
@@ -28,10 +28,10 @@ handler or to report an error condition "just like" the situation in which the
interpreter raises the same exception; but beware that there is nothing to
prevent user code from raising an inappropriate error.
-The built-in exception classes can be sub-classed to define new exceptions;
-programmers are encouraged to at least derive new exceptions from the
-:exc:`Exception` class and not :exc:`BaseException`. More information on
-defining exceptions is available in the Python Tutorial under
+The built-in exception classes can be subclassed to define new exceptions;
+programmers are encouraged to derive new exceptions from the :exc:`Exception`
+class or one of its subclasses, and not from :exc:`BaseException`. More
+information on defining exceptions is available in the Python Tutorial under
:ref:`tut-userexceptions`.
When raising (or re-raising) an exception in an :keyword:`except` clause
@@ -82,7 +82,7 @@ The following exceptions are used mostly as base classes for other exceptions.
.. attribute:: args
The tuple of arguments given to the exception constructor. Some built-in
- exceptions (like :exc:`IOError`) expect a certain number of arguments and
+ exceptions (like :exc:`OSError`) expect a certain number of arguments and
assign a special meaning to the elements of this tuple, while others are
usually called only with a single string giving an error message.
@@ -161,7 +161,7 @@ The following exceptions are the exceptions that are usually raised.
.. exception:: GeneratorExit
- Raise when a :term:`generator`\'s :meth:`close` method is called. It
+ Raised when a :term:`generator`\'s :meth:`close` method is called. It
directly inherits from :exc:`BaseException` instead of :exc:`Exception` since
it is technically not an error.
@@ -253,12 +253,22 @@ The following exceptions are the exceptions that are usually raised.
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.
+ For functions that involve two file system paths (such as
+ :func:`os.rename`), the exception instance will contain a second
+ :attr:`filename2` attribute corresponding to the second 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`.
+ .. versionchanged:: 3.4
+ The :attr:`filename` attribute is now the original file name passed to
+ the function, instead of the name encoded to or decoded from the
+ filesystem encoding. Also, the :attr:`filename2` attribute was added.
+
.. exception:: OverflowError
diff --git a/Doc/library/faulthandler.rst b/Doc/library/faulthandler.rst
index 3c33621..eb2016a 100644
--- a/Doc/library/faulthandler.rst
+++ b/Doc/library/faulthandler.rst
@@ -4,12 +4,14 @@
.. module:: faulthandler
:synopsis: Dump the Python traceback.
+.. versionadded:: 3.3
+
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.
+variable or by using the :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
@@ -27,6 +29,7 @@ tracebacks:
* Only the filename, the function name and the line number are
displayed. (no source code)
* It is limited to 100 frames and 100 threads.
+* The order is reversed: the most recent call is shown first.
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
@@ -35,11 +38,9 @@ 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
-------------------
+Dumping the traceback
+---------------------
.. function:: dump_traceback(file=sys.stderr, all_threads=True)
@@ -68,8 +69,8 @@ Fault handler state
Check if the fault handler is enabled.
-Dump the tracebacks after a timeout
------------------------------------
+Dumping the tracebacks after a timeout
+--------------------------------------
.. function:: dump_traceback_later(timeout, repeat=False, file=sys.stderr, exit=False)
@@ -89,8 +90,8 @@ Dump the tracebacks after a timeout
Cancel the last call to :func:`dump_traceback_later`.
-Dump the traceback on a user signal
------------------------------------
+Dumping the traceback on a user signal
+--------------------------------------
.. function:: register(signum, file=sys.stderr, all_threads=True, chain=False)
@@ -109,8 +110,8 @@ Dump the traceback on a user signal
Not available on Windows.
-File descriptor issue
----------------------
+Issue with file descriptors
+---------------------------
:func:`enable`, :func:`dump_traceback_later` and :func:`register` keep the
file descriptor of their *file* argument. If the file is closed and its file
@@ -122,14 +123,20 @@ these functions again each time that the file is replaced.
Example
-------
-Example of a segmentation fault on Linux: ::
+.. highlight:: sh
+
+Example of a segmentation fault on Linux with and without enabling the fault
+handler::
+
+ $ python3 -c "import ctypes; ctypes.string_at(0)"
+ Segmentation fault
- $ python -q -X faulthandler
+ $ python3 -q -X faulthandler
>>> import ctypes
>>> ctypes.string_at(0)
Fatal Python error: Segmentation fault
- Current thread 0x00007fb899f39700:
+ Current thread 0x00007fb899f39700 (most recent call first):
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/filecmp.rst b/Doc/library/filecmp.rst
index 8a88f8c..06d3f21 100644
--- a/Doc/library/filecmp.rst
+++ b/Doc/library/filecmp.rst
@@ -27,6 +27,10 @@ The :mod:`filecmp` module defines the following functions:
Note that no external programs are called from this function, giving it
portability and efficiency.
+ This function uses a cache for past comparisons and the results,
+ with cache entries invalidated if the :func:`os.stat` information for the
+ file changes. The entire cache may be cleared using :func:`clear_cache`.
+
.. function:: cmpfiles(dir1, dir2, common, shallow=True)
@@ -48,6 +52,15 @@ The :mod:`filecmp` module defines the following functions:
one of the three returned lists.
+.. function:: clear_cache()
+
+ Clear the filecmp cache. This may be useful if a file is compared so quickly
+ after it is modified that it is within the mtime resolution of
+ the underlying filesystem.
+
+ .. versionadded:: 3.4
+
+
.. _dircmp-objects:
The :class:`dircmp` class
@@ -55,28 +68,25 @@ The :class:`dircmp` class
.. class:: dircmp(a, b, ignore=None, hide=None)
- Construct a new directory comparison object, to compare the directories *a* and
- *b*. *ignore* is a list of names to ignore, and defaults to ``['RCS', 'CVS',
- 'tags']``. *hide* is a list of names to hide, and defaults to ``[os.curdir,
- os.pardir]``.
+ Construct a new directory comparison object, to compare the directories *a*
+ and *b*. *ignore* is a list of names to ignore, and defaults to
+ :attr:`filecmp.DEFAULT_IGNORES`. *hide* is a list of names to hide, and
+ defaults to ``[os.curdir, os.pardir]``.
The :class:`dircmp` class compares files by doing *shallow* comparisons
as described for :func:`filecmp.cmp`.
The :class:`dircmp` class provides the following methods:
-
.. method:: report()
Print (to :data:`sys.stdout`) a comparison between *a* and *b*.
-
.. method:: report_partial_closure()
Print a comparison between *a* and *b* and common immediate
subdirectories.
-
.. method:: report_full_closure()
Print a comparison between *a* and *b* and common subdirectories
@@ -133,7 +143,7 @@ The :class:`dircmp` class
.. attribute:: common_files
- Files in both *a* and *b*
+ Files in both *a* and *b*.
.. attribute:: common_funny
@@ -164,6 +174,12 @@ The :class:`dircmp` class
A dictionary mapping names in :attr:`common_dirs` to :class:`dircmp`
objects.
+.. attribute:: DEFAULT_IGNORES
+
+ .. versionadded:: 3.4
+
+ List of directories ignored by :class:`dircmp` by default.
+
Here is a simplified example of using the ``subdirs`` attribute to search
recursively through two directories to show common different files::
diff --git a/Doc/library/fileinput.rst b/Doc/library/fileinput.rst
index d5a4875..ee06830 100644
--- a/Doc/library/fileinput.rst
+++ b/Doc/library/fileinput.rst
@@ -160,6 +160,9 @@ available for subclassing as well:
.. versionchanged:: 3.2
Can be used as a context manager.
+ .. deprecated:: 3.4
+ The ``'rU'`` and ``'U'`` modes.
+
**Optional in-place filtering:** if the keyword argument ``inplace=True`` is
passed to :func:`fileinput.input` or to the :class:`FileInput` constructor, the
diff --git a/Doc/library/filesys.rst b/Doc/library/filesys.rst
index 58998a8..03e085b 100644
--- a/Doc/library/filesys.rst
+++ b/Doc/library/filesys.rst
@@ -12,6 +12,7 @@ in this chapter is:
.. toctree::
+ pathlib.rst
os.path.rst
fileinput.rst
stat.rst
diff --git a/Doc/library/fnmatch.rst b/Doc/library/fnmatch.rst
index e0434b0..ef93f05 100644
--- a/Doc/library/fnmatch.rst
+++ b/Doc/library/fnmatch.rst
@@ -86,7 +86,7 @@ patterns.
'.*\\.txt$'
>>> reobj = re.compile(regex)
>>> reobj.match('foobar.txt')
- <_sre.SRE_Match object at 0x...>
+ <_sre.SRE_Match object; span=(0, 10), match='foobar.txt'>
.. seealso::
diff --git a/Doc/library/formatter.rst b/Doc/library/formatter.rst
index 88be11c..1847a80 100644
--- a/Doc/library/formatter.rst
+++ b/Doc/library/formatter.rst
@@ -3,6 +3,11 @@
.. module:: formatter
:synopsis: Generic output formatter and device interface.
+ :deprecated:
+
+.. deprecated:: 3.4
+ Due to lack of usage, the formatter module has been deprecated and is slated
+ for removal in Python 3.6.
This module supports two interface definitions, each with multiple
diff --git a/Doc/library/ftplib.rst b/Doc/library/ftplib.rst
index dcb2ac4..bfec9b0 100644
--- a/Doc/library/ftplib.rst
+++ b/Doc/library/ftplib.rst
@@ -80,40 +80,34 @@ The module defines the following items:
:rfc:`4217`.
Connect as usual to port 21 implicitly securing the FTP control connection
before authenticating. Securing the data connection requires the user to
- explicitly ask for it by calling the :meth:`prot_p` method.
- *keyfile* and *certfile* are optional -- they can contain a PEM formatted
- 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. *source_address* is a 2-tuple
- ``(host, port)`` for the socket to bind to as its source address before
- connecting.
+ explicitly ask for it by calling the :meth:`prot_p` method. *context*
+ is a :class:`ssl.SSLContext` object which allows bundling SSL configuration
+ options, certificates and private keys into a single (potentially
+ long-lived) structure. Please read :ref:`ssl-security` for best practices.
+
+ *keyfile* and *certfile* are a legacy alternative to *context* -- they
+ can point to PEM-formatted private key and certificate chain files
+ (respectively) for the SSL connection.
.. 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
- >>> ftps = FTP_TLS('ftp.python.org')
- >>> ftps.login() # login anonymously before securing control channel
- >>> ftps.prot_p() # switch to secure data connection
- >>> ftps.retrlines('LIST') # list directory content securely
- total 9
- drwxr-xr-x 8 root wheel 1024 Jan 3 1994 .
- drwxr-xr-x 8 root wheel 1024 Jan 3 1994 ..
- drwxr-xr-x 2 root wheel 1024 Jan 3 1994 bin
- drwxr-xr-x 2 root wheel 1024 Jan 3 1994 etc
- d-wxrwxr-x 2 ftp wheel 1024 Sep 5 13:43 incoming
- drwxr-xr-x 2 root wheel 1024 Nov 17 1993 lib
- drwxr-xr-x 6 1094 wheel 1024 Sep 13 19:07 pub
- drwxr-xr-x 3 root wheel 1024 Jan 3 1994 usr
- -rw-r--r-- 1 root root 312 Aug 1 1994 welcome.msg
- '226 Transfer complete.'
- >>> ftps.quit()
- >>>
+ .. versionchanged:: 3.4
+ The class now supports hostname check with
+ :attr:`ssl.SSLContext.check_hostname` and *Server Name Indication* (see
+ :data:`ssl.HAS_SNI`).
+
+ Here's a sample session using the :class:`FTP_TLS` class::
+
+ >>> ftps = FTP_TLS('ftp.pureftpd.org')
+ >>> ftps.login()
+ '230 Anonymous user logged in'
+ >>> ftps.prot_p()
+ '200 Data protection level set to "private"'
+ >>> ftps.nlst()
+ ['6jack', 'OpenBSD', 'antilink', 'blogbench', 'bsdcam', 'clockspeed', 'djbdns-jedi', 'docs', 'eaccelerator-jedi', 'favicon.ico', 'francotone', 'fugu', 'ignore', 'libpuzzle', 'metalog', 'minidentd', 'misc', 'mysql-udf-global-user-variables', 'php-jenkins-hash', 'php-skein-hash', 'php-webdav', 'phpaudit', 'phpbench', 'pincaster', 'ping', 'posto', 'pub', 'public', 'public_keys', 'pure-ftpd', 'qscan', 'qtc', 'sharedance', 'skycache', 'sound', 'tmp', 'ucarp']
.. exception:: error_reply
@@ -427,6 +421,11 @@ FTP_TLS Objects
Set up secure control connection by using TLS or SSL, depending on what
specified in :meth:`ssl_version` attribute.
+ .. versionchanged:: 3.4
+ The method now supports hostname check with
+ :attr:`ssl.SSLContext.check_hostname` and *Server Name Indication* (see
+ :data:`ssl.HAS_SNI`).
+
.. method:: FTP_TLS.ccc()
Revert control channel back to plaintext. This can be useful to take
diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst
index 7ed25c1..02e6374 100644
--- a/Doc/library/functions.rst
+++ b/Doc/library/functions.rst
@@ -540,8 +540,13 @@ are always available. They are listed here in alphabetical order.
A call to ``format(value, format_spec)`` is translated to
``type(value).__format__(format_spec)`` which bypasses the instance
dictionary when searching for the value's :meth:`__format__` method. A
- :exc:`TypeError` exception is raised if the method is not found or if either
- the *format_spec* or the return value are not strings.
+ :exc:`TypeError` exception is raised if the method search reaches
+ :mod:`object` and the *format_spec* is non-empty, or if either the
+ *format_spec* or the return value are not strings.
+
+ .. versionchanged:: 3.4
+ ``object().__format__(format_spec)`` raises :exc:`TypeError`
+ if *format_spec* is not an empty string.
.. _func-frozenset:
@@ -605,6 +610,10 @@ are always available. They are listed here in alphabetical order.
This function is added to the built-in namespace by the :mod:`site` module.
+ .. versionchanged:: 3.4
+ Changes to :mod:`pydoc` and :mod:`inspect` mean that the reported
+ signatures for callables are now more comprehensive and consistent.
+
.. function:: hex(x)
@@ -676,6 +685,12 @@ are always available. They are listed here in alphabetical order.
The integer type is described in :ref:`typesnumeric`.
+ .. versionchanged:: 3.4
+ If *base* is not an instance of :class:`int` and the *base* object has a
+ :meth:`base.__index__ <object.__index__>` method, that method is called
+ to obtain an integer for the base. Previous versions used
+ :meth:`base.__int__ <object.__int__>` instead of :meth:`base.__index__
+ <object.__index__>`.
.. function:: isinstance(object, classinfo)
@@ -758,25 +773,31 @@ are always available. They are listed here in alphabetical order.
already arranged into argument tuples, see :func:`itertools.starmap`\.
-.. function:: max(iterable, *[, key])
+.. function:: max(iterable, *[, key, default])
max(arg1, arg2, *args[, key])
Return the largest item in an iterable or the largest of two or more
arguments.
- If one positional argument is provided, *iterable* must be a non-empty
- iterable (such as a non-empty string, tuple or list). The largest item
- in the iterable is returned. If two or more positional arguments are
- provided, the largest of the positional arguments is returned.
+ If one positional argument is provided, it should be an :term:`iterable`.
+ The largest item in the iterable is returned. If two or more positional
+ arguments are provided, the largest of the positional arguments is
+ returned.
- The optional keyword-only *key* argument specifies a one-argument ordering
- function like that used for :meth:`list.sort`.
+ There are two optional keyword-only arguments. The *key* argument specifies
+ a one-argument ordering function like that used for :meth:`list.sort`. The
+ *default* argument specifies an object to return if the provided iterable is
+ empty. If the iterable is empty and *default* is not provided, a
+ :exc:`ValueError` is raised.
If multiple items are maximal, the function returns the first one
encountered. This is consistent with other sort-stability preserving tools
such as ``sorted(iterable, key=keyfunc, reverse=True)[0]`` and
``heapq.nlargest(1, iterable, key=keyfunc)``.
+ .. versionadded:: 3.4
+ The *default* keyword-only argument.
+
.. _func-memoryview:
.. function:: memoryview(obj)
@@ -786,25 +807,32 @@ are always available. They are listed here in alphabetical order.
:ref:`typememoryview` for more information.
-.. function:: min(iterable, *[, key])
+.. function:: min(iterable, *[, key, default])
min(arg1, arg2, *args[, key])
Return the smallest item in an iterable or the smallest of two or more
arguments.
- If one positional argument is provided, *iterable* must be a non-empty
- iterable (such as a non-empty string, tuple or list). The smallest item
- in the iterable is returned. If two or more positional arguments are
- provided, the smallest of the positional arguments is returned.
+ If one positional argument is provided, it should be an :term:`iterable`.
+ The smallest item in the iterable is returned. If two or more positional
+ arguments are provided, the smallest of the positional arguments is
+ returned.
- The optional keyword-only *key* argument specifies a one-argument ordering
- function like that used for :meth:`list.sort`.
+ There are two optional keyword-only arguments. The *key* argument specifies
+ a one-argument ordering function like that used for :meth:`list.sort`. The
+ *default* argument specifies an object to return if the provided iterable is
+ empty. If the iterable is empty and *default* is not provided, a
+ :exc:`ValueError` is raised.
If multiple items are minimal, the function returns the first one
encountered. This is consistent with other sort-stability preserving tools
such as ``sorted(iterable, key=keyfunc)[0]`` and ``heapq.nsmallest(1,
iterable, key=keyfunc)``.
+ .. versionadded:: 3.4
+ The *default* keyword-only argument.
+
+
.. function:: next(iterator[, default])
Retrieve the next item from the *iterator* by calling its
@@ -866,8 +894,7 @@ are always available. They are listed here in alphabetical order.
``'b'`` binary mode
``'t'`` text mode (default)
``'+'`` open a disk file for updating (reading and writing)
- ``'U'`` universal newlines mode (for backwards compatibility; should
- not be used in new code)
+ ``'U'`` :term:`universal newlines` mode (deprecated)
========= ===============================================================
The default mode is ``'r'`` (open for reading text, synonym of ``'rt'``).
@@ -973,6 +1000,8 @@ are always available. They are listed here in alphabetical order.
:mod:`os.open` as *opener* results in functionality similar to passing
``None``).
+ The newly created file is :ref:`non-inheritable <fd_inheritance>`.
+
The following example uses the :ref:`dir_fd <dir_fd>` parameter of the
:func:`os.open` function to open a file relative to a given directory::
@@ -986,10 +1015,6 @@ are always available. They are listed here in alphabetical order.
...
>>> os.close(dir_fd) # don't leak a file descriptor
- .. versionchanged:: 3.3
- The *opener* parameter was added.
- The ``'x'`` mode was added.
-
The type of :term:`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
@@ -1016,10 +1041,18 @@ are always available. They are listed here in alphabetical order.
and :mod:`shutil`.
.. versionchanged:: 3.3
+ The *opener* parameter was added.
+ The ``'x'`` mode was added.
:exc:`IOError` used to be raised, it is now an alias of :exc:`OSError`.
:exc:`FileExistsError` is now raised if the file opened in exclusive
creation mode (``'x'``) already exists.
+ .. versionchanged:: 3.4
+ The file is now non-inheritable.
+
+ .. deprecated-removed:: 3.4 4.0
+ The ``'U'`` mode.
+
.. XXX works for bytes too, but should it?
.. function:: ord(c)
diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst
index 3c946e3..c46b799 100644
--- a/Doc/library/functools.rst
+++ b/Doc/library/functools.rst
@@ -6,6 +6,7 @@
.. moduleauthor:: Peter Harris <scav@blueyonder.co.uk>
.. moduleauthor:: Raymond Hettinger <python@rcn.com>
.. moduleauthor:: Nick Coghlan <ncoghlan@gmail.com>
+.. moduleauthor:: Łukasz Langa <lukasz@langa.pl>
.. sectionauthor:: Peter Harris <scav@blueyonder.co.uk>
**Source code:** :source:`Lib/functools.py`
@@ -133,15 +134,34 @@ The :mod:`functools` module defines the following functions:
@total_ordering
class Student:
+ def _is_valid_operand(self, other):
+ return (hasattr(other, "lastname") and
+ hasattr(other, "firstname"))
def __eq__(self, other):
+ if not self._is_valid_operand(other):
+ return NotImplemented
return ((self.lastname.lower(), self.firstname.lower()) ==
(other.lastname.lower(), other.firstname.lower()))
def __lt__(self, other):
+ if not self._is_valid_operand(other):
+ return NotImplemented
return ((self.lastname.lower(), self.firstname.lower()) <
(other.lastname.lower(), other.firstname.lower()))
+ .. note::
+
+ While this decorator makes it easy to create well behaved totally
+ ordered types, it *does* come at the cost of slower execution and
+ more complex stack traces for the derived comparison methods. If
+ performance benchmarking indicates this is a bottleneck for a given
+ application, implementing all six rich comparison methods instead is
+ likely to provide an easy speed boost.
+
.. versionadded:: 3.2
+ .. versionchanged:: 3.4
+ Returning NotImplemented from the underlying comparison function for
+ unrecognised types is now supported.
.. function:: partial(func, *args, **keywords)
@@ -174,6 +194,50 @@ The :mod:`functools` module defines the following functions:
18
+.. class:: partialmethod(func, *args, **keywords)
+
+ Return a new :class:`partialmethod` descriptor which behaves
+ like :class:`partial` except that it is designed to be used as a method
+ definition rather than being directly callable.
+
+ *func* must be a :term:`descriptor` or a callable (objects which are both,
+ like normal functions, are handled as descriptors).
+
+ When *func* is a descriptor (such as a normal Python function,
+ :func:`classmethod`, :func:`staticmethod`, :func:`abstractmethod` or
+ another instance of :class:`partialmethod`), calls to ``__get__`` are
+ delegated to the underlying descriptor, and an appropriate
+ :class:`partial` object returned as the result.
+
+ When *func* is a non-descriptor callable, an appropriate bound method is
+ created dynamically. This behaves like a normal Python function when
+ used as a method: the *self* argument will be inserted as the first
+ positional argument, even before the *args* and *keywords* supplied to
+ the :class:`partialmethod` constructor.
+
+ Example::
+
+ >>> class Cell(object):
+ ... def __init__(self):
+ ... self._alive = False
+ ... @property
+ ... def alive(self):
+ ... return self._alive
+ ... def set_state(self, state):
+ ... self._alive = bool(state)
+ ... set_alive = partialmethod(set_state, True)
+ ... set_dead = partialmethod(set_state, False)
+ ...
+ >>> c = Cell()
+ >>> c.alive
+ False
+ >>> c.set_alive()
+ >>> c.alive
+ True
+
+ .. versionadded:: 3.4
+
+
.. function:: reduce(function, iterable[, initializer])
Apply *function* of two arguments cumulatively to the items of *sequence*, from
@@ -198,6 +262,111 @@ The :mod:`functools` module defines the following functions:
return value
+.. decorator:: singledispatch(default)
+
+ Transforms a function into a :term:`single-dispatch <single
+ dispatch>` :term:`generic function`.
+
+ To define a generic function, decorate it with the ``@singledispatch``
+ decorator. Note that the dispatch happens on the type of the first argument,
+ create your function accordingly::
+
+ >>> from functools import singledispatch
+ >>> @singledispatch
+ ... def fun(arg, verbose=False):
+ ... if verbose:
+ ... print("Let me just say,", end=" ")
+ ... print(arg)
+
+ To add overloaded implementations to the function, use the :func:`register`
+ attribute of the generic function. It is a decorator, taking a type
+ parameter and decorating a function implementing the operation for that
+ type::
+
+ >>> @fun.register(int)
+ ... def _(arg, verbose=False):
+ ... if verbose:
+ ... print("Strength in numbers, eh?", end=" ")
+ ... print(arg)
+ ...
+ >>> @fun.register(list)
+ ... def _(arg, verbose=False):
+ ... if verbose:
+ ... print("Enumerate this:")
+ ... for i, elem in enumerate(arg):
+ ... print(i, elem)
+
+ To enable registering lambdas and pre-existing functions, the
+ :func:`register` attribute can be used in a functional form::
+
+ >>> def nothing(arg, verbose=False):
+ ... print("Nothing.")
+ ...
+ >>> fun.register(type(None), nothing)
+
+ The :func:`register` attribute returns the undecorated function which
+ enables decorator stacking, pickling, as well as creating unit tests for
+ each variant independently::
+
+ >>> @fun.register(float)
+ ... @fun.register(Decimal)
+ ... def fun_num(arg, verbose=False):
+ ... if verbose:
+ ... print("Half of your number:", end=" ")
+ ... print(arg / 2)
+ ...
+ >>> fun_num is fun
+ False
+
+ When called, the generic function dispatches on the type of the first
+ argument::
+
+ >>> fun("Hello, world.")
+ Hello, world.
+ >>> fun("test.", verbose=True)
+ Let me just say, test.
+ >>> fun(42, verbose=True)
+ Strength in numbers, eh? 42
+ >>> fun(['spam', 'spam', 'eggs', 'spam'], verbose=True)
+ Enumerate this:
+ 0 spam
+ 1 spam
+ 2 eggs
+ 3 spam
+ >>> fun(None)
+ Nothing.
+ >>> fun(1.23)
+ 0.615
+
+ Where there is no registered implementation for a specific type, its
+ method resolution order is used to find a more generic implementation.
+ The original function decorated with ``@singledispatch`` is registered
+ for the base ``object`` type, which means it is used if no better
+ implementation is found.
+
+ To check which implementation will the generic function choose for
+ a given type, use the ``dispatch()`` attribute::
+
+ >>> fun.dispatch(float)
+ <function fun_num at 0x1035a2840>
+ >>> fun.dispatch(dict) # note: default implementation
+ <function fun at 0x103fe0000>
+
+ To access all registered implementations, use the read-only ``registry``
+ attribute::
+
+ >>> fun.registry.keys()
+ dict_keys([<class 'NoneType'>, <class 'int'>, <class 'object'>,
+ <class 'decimal.Decimal'>, <class 'list'>,
+ <class 'float'>])
+ >>> fun.registry[float]
+ <function fun_num at 0x1035a2840>
+ >>> fun.registry[object]
+ <function fun at 0x103fe0000>
+
+ .. versionadded:: 3.4
+
+
.. function:: update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)
Update a *wrapper* function to look like the *wrapped* function. The optional
@@ -212,8 +381,8 @@ The :mod:`functools` module defines the following functions:
To allow access to the original function for introspection and other purposes
(e.g. bypassing a caching decorator such as :func:`lru_cache`), this function
- automatically adds a __wrapped__ attribute to the wrapper that refers to
- the original function.
+ automatically adds a ``__wrapped__`` attribute to the wrapper that refers to
+ the function being wrapped.
The main intended use for this function is in :term:`decorator` functions which
wrap the decorated function and return the wrapper. If the wrapper function is
@@ -236,6 +405,11 @@ The :mod:`functools` module defines the following functions:
.. versionchanged:: 3.2
Missing attributes no longer trigger an :exc:`AttributeError`.
+ .. versionchanged:: 3.4
+ The ``__wrapped__`` attribute now always refers to the wrapped
+ function, even if that function defined a ``__wrapped__`` attribute.
+ (see :issue:`17482`)
+
.. decorator:: wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)
@@ -301,4 +475,3 @@ differences. For instance, the :attr:`__name__` and :attr:`__doc__` attributes
are not created automatically. Also, :class:`partial` objects defined in
classes behave like static methods and do not transform into bound methods
during instance attribute look-up.
-
diff --git a/Doc/library/gc.rst b/Doc/library/gc.rst
index 71449a3..8135542 100644
--- a/Doc/library/gc.rst
+++ b/Doc/library/gc.rst
@@ -67,6 +67,25 @@ The :mod:`gc` module provides the following functions:
returned.
+.. function:: get_stats()
+
+ Return a list of three per-generation dictionaries containing collection
+ statistics since interpreter start. The number of keys may change
+ in the future, but currently each dictionary will contain the following
+ items:
+
+ * ``collections`` is the number of times this generation was collected;
+
+ * ``collected`` is the total number of objects collected inside this
+ generation;
+
+ * ``uncollectable`` is the total number of objects which were found
+ to be uncollectable (and were therefore moved to the :data:`garbage`
+ list) inside this generation.
+
+ .. versionadded:: 3.4
+
+
.. function:: set_threshold(threshold0[, threshold1[, threshold2]])
Set the garbage collection thresholds (the collection frequency). Setting
@@ -158,24 +177,13 @@ values but should not rebind them):
.. data:: garbage
- A list of objects which the collector found to be unreachable but could not be
- freed (uncollectable objects). By default, this list contains only objects with
- :meth:`__del__` methods. Objects that have :meth:`__del__` methods and are
- part of a reference cycle cause the entire reference cycle to be uncollectable,
- including objects not necessarily in the cycle but reachable only from it.
- Python doesn't collect such cycles automatically because, in general, it isn't
- possible for Python to guess a safe order in which to run the :meth:`__del__`
- methods. If you know a safe order, you can force the issue by examining the
- *garbage* list, and explicitly breaking cycles due to your objects within the
- list. Note that these objects are kept alive even so by virtue of being in the
- *garbage* list, so they should be removed from *garbage* too. For example,
- after breaking cycles, do ``del gc.garbage[:]`` to empty the list. It's
- generally better to avoid the issue by not creating cycles containing objects
- with :meth:`__del__` methods, and *garbage* can be examined in that case to
- verify that no such cycles are being created.
-
- If :const:`DEBUG_SAVEALL` is set, then all unreachable objects will be added
- to this list rather than freed.
+ A list of objects which the collector found to be unreachable but could
+ not be freed (uncollectable objects). Starting with Python 3.4, this
+ list should be empty most of the time, except when using instances of
+ C extension types with a non-NULL ``tp_del`` slot.
+
+ If :const:`DEBUG_SAVEALL` is set, then all unreachable objects will be
+ added to this list rather than freed.
.. versionchanged:: 3.2
If this list is non-empty at interpreter shutdown, a
@@ -183,6 +191,10 @@ values but should not rebind them):
:const:`DEBUG_UNCOLLECTABLE` is set, in addition all uncollectable objects
are printed.
+ .. versionchanged:: 3.4
+ Following :pep:`442`, objects with a :meth:`__del__` method don't end
+ up in :attr:`gc.garbage` anymore.
+
.. data:: callbacks
A list of callbacks that will be invoked by the garbage collector before and
diff --git a/Doc/library/getpass.rst b/Doc/library/getpass.rst
index ffe2b12..211563e 100644
--- a/Doc/library/getpass.rst
+++ b/Doc/library/getpass.rst
@@ -13,10 +13,11 @@ The :mod:`getpass` module provides two functions:
.. function:: getpass(prompt='Password: ', stream=None)
Prompt the user for a password without echoing. The user is prompted using
- the string *prompt*, which defaults to ``'Password: '``. On Unix, the prompt
- is written to the file-like object *stream*. *stream* defaults to the
- controlling terminal (:file:`/dev/tty`) or if that is unavailable to
- ``sys.stderr`` (this argument is ignored on Windows).
+ the string *prompt*, which defaults to ``'Password: '``. On Unix, the
+ prompt is written to the file-like object *stream* using the replace error
+ handler if needed. *stream* defaults to the controlling terminal
+ (:file:`/dev/tty`) or if that is unavailable to ``sys.stderr`` (this
+ argument is ignored on Windows).
If echo free input is unavailable getpass() falls back to printing
a warning message to *stream* and reading from ``sys.stdin`` and
diff --git a/Doc/library/glob.rst b/Doc/library/glob.rst
index eff138b..abcbf38 100644
--- a/Doc/library/glob.rst
+++ b/Doc/library/glob.rst
@@ -25,6 +25,10 @@ For a literal match, wrap the meta-characters in brackets.
For example, ``'[?]'`` matches the character ``'?'``.
+.. seealso::
+ The :mod:`pathlib` module offers high-level path objects.
+
+
.. function:: glob(pathname)
Return a possibly-empty list of path names that match *pathname*, which must be
@@ -40,6 +44,17 @@ For example, ``'[?]'`` matches the character ``'?'``.
without actually storing them all simultaneously.
+.. function:: escape(pathname)
+
+ Escape all special characters (``'?'``, ``'*'`` and ``'['``).
+ This is useful if you want to match an arbitrary literal string that may
+ have special characters in it. Special characters in drive/UNC
+ sharepoints are not escaped, e.g. on Windows
+ ``escape('//?/c:/Quo vadis?.txt')`` returns ``'//?/c:/Quo vadis[?].txt'``.
+
+ .. versionadded:: 3.4
+
+
For example, consider a directory containing only the following files:
:file:`1.gif`, :file:`2.txt`, and :file:`card.gif`. :func:`glob` will produce
the following results. Notice how any leading components of the path are
diff --git a/Doc/library/gzip.rst b/Doc/library/gzip.rst
index 354deed..78536fa 100644
--- a/Doc/library/gzip.rst
+++ b/Doc/library/gzip.rst
@@ -35,8 +35,8 @@ The module defines the following items:
:class:`bytes` object), or an existing file object to read from or write to.
The *mode* argument can be any of ``'r'``, ``'rb'``, ``'a'``, ``'ab'``,
- ``'w'``, or ``'wb'`` for binary mode, or ``'rt'``, ``'at'``, or ``'wt'`` for
- text mode. The default is ``'rb'``.
+ ``'w'``, ``'wb'``, ``'x'`` or ``'xb'`` for binary mode, or ``'rt'``,
+ ``'at'``, ``'wt'``, or ``'xt'`` for text mode. The default is ``'rb'``.
The *compresslevel* argument is an integer from 0 to 9, as for the
:class:`GzipFile` constructor.
@@ -53,6 +53,9 @@ The module defines the following items:
Added support for *filename* being a file object, support for text mode,
and the *encoding*, *errors* and *newline* arguments.
+ .. versionchanged:: 3.4
+ Added support for the ``'x'``, ``'xb'`` and ``'xt'`` modes.
+
.. class:: GzipFile(filename=None, mode=None, compresslevel=9, fileobj=None, mtime=None)
@@ -73,8 +76,9 @@ The module defines the following items:
original filename is not included in the header.
The *mode* argument can be any of ``'r'``, ``'rb'``, ``'a'``, ``'ab'``, ``'w'``,
- or ``'wb'``, depending on whether the file will be read or written. The default
- is the mode of *fileobj* if discernible; otherwise, the default is ``'rb'``.
+ ``'wb'``, ``'x'``, or ``'xb'``, depending on whether the file will be read or
+ written. The default is the mode of *fileobj* if discernible; otherwise, the
+ default is ``'rb'``.
Note that the file is always opened in binary mode. To open a compressed file
in text mode, use :func:`.open` (or wrap your :class:`GzipFile` with an
@@ -130,6 +134,9 @@ The module defines the following items:
.. versionchanged:: 3.3
The :meth:`io.BufferedIOBase.read1` method is now implemented.
+ .. versionchanged:: 3.4
+ Added support for the ``'x'`` and ``'xb'`` modes.
+
.. function:: compress(data, compresslevel=9)
diff --git a/Doc/library/hashlib.rst b/Doc/library/hashlib.rst
index a693f7e..d27902e 100644
--- a/Doc/library/hashlib.rst
+++ b/Doc/library/hashlib.rst
@@ -32,6 +32,12 @@ digests. The modern term is secure hash.
Some algorithms have known hash collision weaknesses, refer to the "See
also" section at the end.
+
+.. _hash-algorithms:
+
+Hash algorithms
+---------------
+
There is one constructor method named for each type of :dfn:`hash`. All return
a hash object with the same simple interface. For example: use :func:`sha1` to
create a SHA1 hash object. You can now feed this object with :term:`bytes-like
@@ -53,9 +59,9 @@ concatenation of the data fed to it so far using the :meth:`digest` or
.. index:: single: OpenSSL; (use in module hashlib)
Constructors for hash algorithms that are always present in this module are
-:func:`md5`, :func:`sha1`, :func:`sha224`, :func:`sha256`, :func:`sha384`, and
-:func:`sha512`. Additional algorithms may also be available depending upon the
-OpenSSL library that Python uses on your platform.
+:func:`md5`, :func:`sha1`, :func:`sha224`, :func:`sha256`, :func:`sha384`,
+and :func:`sha512`. Additional algorithms may also be available depending upon
+the OpenSSL library that Python uses on your platform.
For example, to obtain the digest of the byte string ``b'Nobody inspects the
spammish repetition'``::
@@ -122,6 +128,18 @@ returned by the constructors:
The internal block size of the hash algorithm in bytes.
+A hash object has the following attributes:
+
+.. attribute:: hash.name
+
+ The canonical name of this hash, always lowercase and always suitable as a
+ parameter to :func:`new` to create another hash of this type.
+
+ .. versionchanged:: 3.4
+ The name attribute has been present in CPython since its inception, but
+ until Python 3.4 was not formally specified, so may not exist on some
+ platforms.
+
A hash object has the following methods:
@@ -158,6 +176,45 @@ A hash object has the following methods:
compute the digests of data sharing a common initial substring.
+Key Derivation Function
+-----------------------
+
+Key derivation and key stretching algorithms are designed for secure password
+hashing. Naive algorithms such as ``sha1(password)`` are not resistant
+against brute-force attacks. A good password hashing function must be tunable,
+slow and include a salt.
+
+
+.. function:: pbkdf2_hmac(name, password, salt, rounds, dklen=None)
+
+ The function provides PKCS#5 password-based key derivation function 2. It
+ uses HMAC as pseudorandom function.
+
+ The string *name* is the desired name of the hash digest algorithm for
+ HMAC, e.g. 'sha1' or 'sha256'. *password* and *salt* are interpreted as
+ buffers of bytes. Applications and libraries should limit *password* to
+ a sensible value (e.g. 1024). *salt* should be about 16 or more bytes from
+ a proper source, e.g. :func:`os.urandom`.
+
+ The number of *rounds* should be chosen based on the hash algorithm and
+ computing power. As of 2013 a value of at least 100,000 rounds of SHA-256
+ have been suggested.
+
+ *dklen* is the length of the derived key. If *dklen* is ``None`` then the
+ digest size of the hash algorithm *name* is used, e.g. 64 for SHA-512.
+
+ >>> import hashlib, binascii
+ >>> dk = hashlib.pbkdf2_hmac('sha256', b'password', b'salt', 100000)
+ >>> binascii.hexlify(dk)
+ b'0394a2ede332c9a13eb82e9b24631604c31df978b4e2f0fbd2c549944f9d79a5'
+
+ .. versionadded:: 3.4
+
+ .. note:: A fast implementation of *pbkdf2_hmac* is available with OpenSSL.
+ The Python implementation uses an inline version of :mod:`hmac`. It is
+ about three times slower and doesn't release the GIL.
+
+
.. seealso::
Module :mod:`hmac`
@@ -173,3 +230,5 @@ A hash object has the following methods:
Wikipedia article with information on which algorithms have known issues and
what that means regarding their use.
+ http://www.ietf.org/rfc/rfc2898.txt
+ PKCS #5: Password-Based Cryptography Specification Version 2.0
diff --git a/Doc/library/hmac.rst b/Doc/library/hmac.rst
index 9575693..4858235 100644
--- a/Doc/library/hmac.rst
+++ b/Doc/library/hmac.rst
@@ -16,20 +16,32 @@ This module implements the HMAC algorithm as described by :rfc:`2104`.
.. function:: new(key, msg=None, digestmod=None)
- Return a new hmac object. *key* is a bytes object giving the secret key. If
- *msg* is present, the method call ``update(msg)`` is made. *digestmod* is
- the digest constructor or module for the HMAC object to use. It defaults to
- the :data:`hashlib.md5` constructor.
+ Return a new hmac object. *key* is a bytes or bytearray object giving the
+ secret key. If *msg* is present, the method call ``update(msg)`` is made.
+ *digestmod* is the digest name, digest constructor or module for the HMAC
+ object to use. It supports any name suitable to :func:`hashlib.new` and
+ defaults to the :data:`hashlib.md5` constructor.
+
+ .. versionchanged:: 3.4
+ Parameter *key* can be a bytes or bytearray object.
+ Parameter *msg* can be of any type supported by :mod:`hashlib`.
+ Paramter *digestmod* can be the name of a hash algorithm.
+
+ .. deprecated:: 3.4
+ MD5 as implicit default digest for *digestmod* is deprecated.
An HMAC object has the following methods:
.. method:: HMAC.update(msg)
- Update the hmac object with the bytes object *msg*. Repeated calls are
- equivalent to a single call with the concatenation of all the arguments:
+ Update the hmac object with *msg*. Repeated calls are equivalent to a
+ single call with the concatenation of all the arguments:
``m.update(a); m.update(b)`` is equivalent to ``m.update(a + b)``.
+ .. versionchanged:: 3.4
+ Parameter *msg* can be of any type supported by :mod:`hashlib`.
+
.. method:: HMAC.digest()
@@ -66,6 +78,25 @@ An HMAC object has the following methods:
compute the digests of strings that share a common initial substring.
+A hash object has the following attributes:
+
+.. attribute:: HMAC.digest_size
+
+ The size of the resulting HMAC digest in bytes.
+
+.. attribute:: HMAC.block_size
+
+ The internal block size of the hash algorithm in bytes.
+
+ .. versionadded:: 3.4
+
+.. attribute:: HMAC.name
+
+ The canonical name of this HMAC, always lowercase, e.g. ``hmac-md5``.
+
+ .. versionadded:: 3.4
+
+
This module also provides the following helper function:
.. function:: compare_digest(a, b)
diff --git a/Doc/library/html.entities.rst b/Doc/library/html.entities.rst
index 0aabbad..09b0abc 100644
--- a/Doc/library/html.entities.rst
+++ b/Doc/library/html.entities.rst
@@ -20,6 +20,7 @@ This module defines four dictionaries, :data:`html5`,
Note that the trailing semicolon is included in the name (e.g. ``'gt;'``),
however some of the names are accepted by the standard even without the
semicolon: in this case the name is present with and without the ``';'``.
+ See also :func:`html.unescape`.
.. versionadded:: 3.3
diff --git a/Doc/library/html.parser.rst b/Doc/library/html.parser.rst
index e4154ef..44b7d6e 100644
--- a/Doc/library/html.parser.rst
+++ b/Doc/library/html.parser.rst
@@ -16,14 +16,21 @@
This module defines a class :class:`HTMLParser` which serves as the basis for
parsing text files formatted in HTML (HyperText Mark-up Language) and XHTML.
-.. class:: HTMLParser(strict=False)
+.. class:: HTMLParser(strict=False, *, convert_charrefs=False)
- Create a parser instance. If *strict* is ``False`` (the default), the parser
- will accept and parse invalid markup. If *strict* is ``True`` the parser
- will raise an :exc:`~html.parser.HTMLParseError` exception instead [#]_ when
- it's not able to parse the markup.
- The use of ``strict=True`` is discouraged and the *strict* argument is
- deprecated.
+ Create a parser instance.
+
+ If *convert_charrefs* is ``True`` (default: ``False``), all character
+ references (except the ones in ``script``/``style`` elements) are
+ automatically converted to the corresponding Unicode characters.
+ The use of ``convert_charrefs=True`` is encouraged and will become
+ the default in Python 3.5.
+
+ If *strict* is ``False`` (the default), the parser will accept and parse
+ invalid markup. If *strict* is ``True`` the parser will raise an
+ :exc:`~html.parser.HTMLParseError` exception instead [#]_ when it's not
+ able to parse the markup. The use of ``strict=True`` is discouraged and
+ the *strict* argument is deprecated.
An :class:`.HTMLParser` instance is fed HTML data and calls handler methods
when start tags, end tags, text, comments, and other markup elements are
@@ -34,12 +41,15 @@ parsing text files formatted in HTML (HyperText Mark-up Language) and XHTML.
handler for elements which are closed implicitly by closing an outer element.
.. versionchanged:: 3.2
- *strict* keyword added.
+ *strict* argument added.
.. deprecated-removed:: 3.3 3.5
The *strict* argument and the strict mode have been deprecated.
The parser is now able to accept and parse invalid markup too.
+ .. versionchanged:: 3.4
+ *convert_charrefs* keyword argument added.
+
An exception is defined as well:
@@ -74,7 +84,7 @@ as they are encountered::
def handle_data(self, data):
print("Encountered some data :", data)
- parser = MyHTMLParser(strict=False)
+ parser = MyHTMLParser()
parser.feed('<html><head><title>Test</title></head>'
'<body><h1>Parse me!</h1></body></html>')
@@ -181,7 +191,8 @@ implementations do nothing (except for :meth:`~HTMLParser.handle_startendtag`):
This method is called to process a named character reference of the form
``&name;`` (e.g. ``&gt;``), where *name* is a general entity reference
- (e.g. ``'gt'``).
+ (e.g. ``'gt'``). This method is never called if *convert_charrefs* is
+ ``True``.
.. method:: HTMLParser.handle_charref(name)
@@ -189,7 +200,8 @@ implementations do nothing (except for :meth:`~HTMLParser.handle_startendtag`):
This method is called to process decimal and hexadecimal numeric character
references of the form ``&#NNN;`` and ``&#xNNN;``. For example, the decimal
equivalent for ``&gt;`` is ``&#62;``, whereas the hexadecimal is ``&#x3E;``;
- in this case the method will receive ``'62'`` or ``'x3E'``.
+ in this case the method will receive ``'62'`` or ``'x3E'``. This method
+ is never called if *convert_charrefs* is ``True``.
.. method:: HTMLParser.handle_comment(data)
@@ -272,7 +284,7 @@ examples::
def handle_decl(self, data):
print("Decl :", data)
- parser = MyHTMLParser(strict=False)
+ parser = MyHTMLParser()
Parsing a doctype::
@@ -324,7 +336,8 @@ correct char (note: these 3 references are all equivalent to ``'>'``)::
Num ent : >
Feeding incomplete chunks to :meth:`~HTMLParser.feed` works, but
-:meth:`~HTMLParser.handle_data` might be called more than once::
+:meth:`~HTMLParser.handle_data` might be called more than once
+(unless *convert_charrefs* is set to ``True``)::
>>> for chunk in ['<sp', 'an>buff', 'ered ', 'text</s', 'pan>']:
... parser.feed(chunk)
diff --git a/Doc/library/html.rst b/Doc/library/html.rst
index 1107ca9..d0706bc 100644
--- a/Doc/library/html.rst
+++ b/Doc/library/html.rst
@@ -20,6 +20,17 @@ This module defines utilities to manipulate HTML.
.. versionadded:: 3.2
+
+.. function:: unescape(s)
+
+ Convert all named and numeric character references (e.g. ``&gt;``,
+ ``&#62;``, ``&x3e;``) in the string *s* to the corresponding unicode
+ characters. This function uses the rules defined by the HTML 5 standard
+ for both valid and invalid character references, and the :data:`list of
+ HTML 5 named character references <html.entities.html5>`.
+
+ .. versionadded:: 3.4
+
--------------
Submodules in the ``html`` package are:
diff --git a/Doc/library/http.client.rst b/Doc/library/http.client.rst
index bb30f24..9f6bcd1 100644
--- a/Doc/library/http.client.rst
+++ b/Doc/library/http.client.rst
@@ -27,7 +27,7 @@ HTTPS protocols. It is normally not used directly --- the module
The module provides the following classes:
-.. class:: HTTPConnection(host, port=None[, strict][, timeout], \
+.. class:: HTTPConnection(host, port=None[, timeout], \
source_address=None)
An :class:`HTTPConnection` instance represents one transaction with an HTTP
@@ -43,44 +43,48 @@ The module provides the following classes:
For example, the following calls all create instances that connect to the server
at the same host and port::
- >>> h1 = http.client.HTTPConnection('www.cwi.nl')
- >>> h2 = http.client.HTTPConnection('www.cwi.nl:80')
- >>> h3 = http.client.HTTPConnection('www.cwi.nl', 80)
- >>> h3 = http.client.HTTPConnection('www.cwi.nl', 80, timeout=10)
+ >>> h1 = http.client.HTTPConnection('www.python.org')
+ >>> h2 = http.client.HTTPConnection('www.python.org:80')
+ >>> h3 = http.client.HTTPConnection('www.python.org', 80)
+ >>> h4 = http.client.HTTPConnection('www.python.org', 80, timeout=10)
.. versionchanged:: 3.2
*source_address* was added.
- .. deprecated-removed:: 3.2 3.4
- The *strict* parameter is deprecated. HTTP 0.9-style "Simple Responses"
- are not supported anymore.
+ .. versionchanged:: 3.4
+ The *strict* parameter was removed. HTTP 0.9-style "Simple Responses" are
+ not longer supported.
.. class:: HTTPSConnection(host, port=None, key_file=None, \
- cert_file=None[, strict][, timeout], \
+ cert_file=None[, timeout], \
source_address=None, *, context=None, \
check_hostname=None)
A subclass of :class:`HTTPConnection` that uses SSL for communication with
secure servers. Default port is ``443``. If *context* is specified, it
must be a :class:`ssl.SSLContext` instance describing the various SSL
- options. If *context* is specified and has a :attr:`~ssl.SSLContext.verify_mode`
- of either :data:`~ssl.CERT_OPTIONAL` or :data:`~ssl.CERT_REQUIRED`, then
- by default *host* is matched against the host name(s) allowed by the
- server's certificate. If you want to change that behaviour, you can
- explicitly set *check_hostname* to False.
+ options.
*key_file* and *cert_file* are deprecated, please use
- :meth:`ssl.SSLContext.load_cert_chain` instead.
+ :meth:`ssl.SSLContext.load_cert_chain` instead, or let
+ :func:`ssl.create_default_context` select the system's trusted CA
+ certificates for you.
- If you access arbitrary hosts on the Internet, it is recommended to
- require certificate checking and feed the *context* with a set of
- trusted CA certificates::
+ The recommended way to connect to HTTPS hosts on the Internet is as
+ follows::
- context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
- context.verify_mode = ssl.CERT_REQUIRED
- context.load_verify_locations('/etc/pki/tls/certs/ca-bundle.crt')
- h = client.HTTPSConnection('svn.python.org', 443, context=context)
+ context = ssl.create_default_context()
+ h = client.HTTPSConnection('www.python.org', 443, context=context)
+
+ Please read :ref:`ssl-security` for more information on best practices.
+
+ .. note::
+ If *context* is specified and has a :attr:`~ssl.SSLContext.verify_mode`
+ of either :data:`~ssl.CERT_OPTIONAL` or :data:`~ssl.CERT_REQUIRED`, then
+ by default *host* is matched against the host name(s) allowed by the
+ server's certificate. If you want to change that behaviour, you can
+ explicitly set *check_hostname* to False.
.. versionchanged:: 3.2
*source_address*, *context* and *check_hostname* were added.
@@ -89,19 +93,19 @@ The module provides the following classes:
This class now supports HTTPS virtual hosts if possible (that is,
if :data:`ssl.HAS_SNI` is true).
- .. deprecated-removed:: 3.2 3.4
- The *strict* parameter is deprecated. HTTP 0.9-style "Simple Responses"
- are not supported anymore.
+ .. versionchanged:: 3.4
+ The *strict* parameter was removed. HTTP 0.9-style "Simple Responses" are
+ no longer supported.
-.. class:: HTTPResponse(sock, debuglevel=0[, strict], method=None, url=None)
+.. class:: HTTPResponse(sock, debuglevel=0, method=None, url=None)
Class whose instances are returned upon successful connection. Not
instantiated directly by user.
- .. deprecated-removed:: 3.2 3.4
- The *strict* parameter is deprecated. HTTP 0.9-style "Simple Responses"
- are not supported anymore.
+ .. versionchanged:: 3.4
+ The *strict* parameter was removed. HTTP 0.9 style "Simple Responses" are
+ no longer supported.
The following exceptions are raised as appropriate:
@@ -656,7 +660,7 @@ request using http.client::
>>> # This creates an HTTP message
>>> # with the content of BODY as the enclosed representation
- >>> # for the resource http://localhost:8080/foobar
+ >>> # for the resource http://localhost:8080/file
...
>>> import http.client
>>> BODY = "***filecontents***"
diff --git a/Doc/library/http.server.rst b/Doc/library/http.server.rst
index 5aeb719..0d8e7fe 100644
--- a/Doc/library/http.server.rst
+++ b/Doc/library/http.server.rst
@@ -81,7 +81,10 @@ of which this module provides three different variants:
Holds an instance of the class specified by the :attr:`MessageClass` class
variable. This instance parses and manages the headers in the HTTP
- request.
+ request. The :func:`~http.client.parse_headers` function from
+ :mod:`http.client` is used to parse the headers and it requires that the
+ HTTP request provide a valid :rfc:`2822` style header.
+
.. attribute:: rfile
@@ -116,7 +119,7 @@ of which this module provides three different variants:
HTTP error code value. *message* should be a string containing a
(detailed) error message of what occurred, and *explain* should be an
explanation of the error code number. Default *message* and *explain*
- values can found in the *responses* class variable.
+ values can found in the :attr:`responses` class variable.
.. attribute:: error_content_type
@@ -170,12 +173,22 @@ of which this module provides three different variants:
.. versionadded:: 3.2
- .. method:: send_error(code, message=None)
+ .. method:: send_error(code, message=None, explain=None)
Sends and logs a complete error reply to the client. The numeric *code*
- specifies the HTTP error code, with *message* as optional, more specific text. A
- complete set of headers is sent, followed by text composed using the
- :attr:`error_message_format` class variable.
+ specifies the HTTP error code, with *message* as an optional, short, human
+ readable description of the error. The *explain* argument can be used to
+ provide more detailed information about the error; it will be formatted
+ using the :attr:`error_message_format` class variable and emitted, after
+ a complete set of headers, as the response body. The :attr:`responses`
+ class variable holds the default values for *message* and *explain* that
+ will be used if no value is provided; for unknown codes the default value
+ for both is the string ``???``.
+
+ .. versionchanged:: 3.4
+ The error response includes a Content-Length header.
+ Added the *explain* argument.
+
.. method:: send_response(code, message=None)
@@ -341,7 +354,7 @@ of which this module provides three different variants:
The :class:`SimpleHTTPRequestHandler` class can be used in the following
manner in order to create a very basic webserver serving files relative to
-the current directory. ::
+the current directory::
import http.server
import socketserver
@@ -355,12 +368,23 @@ the current directory. ::
print("serving at port", PORT)
httpd.serve_forever()
+.. _http-server-cli:
+
:mod:`http.server` can also be invoked directly using the :option:`-m`
switch of the interpreter with a ``port number`` argument. Similar to
-the previous example, this serves files relative to the current directory. ::
+the previous example, this serves files relative to the current directory::
python -m http.server 8000
+By default, server binds itself to all interfaces. The option ``-b/--bind``
+specifies a specific address to which it should bind. For example, the
+following command causes the server to bind to localhost only::
+
+ python -m http.server 8000 --bind 127.0.0.1
+
+.. versionadded:: 3.4
+ ``--bind`` argument was introduced.
+
.. class:: CGIHTTPRequestHandler(request, client_address, server)
@@ -403,7 +427,7 @@ the previous example, this serves files relative to the current directory. ::
reasons. Problems with the CGI script will be translated to error 403.
:class:`CGIHTTPRequestHandler` can be enabled in the command line by passing
-the ``--cgi`` option.::
+the ``--cgi`` option::
python -m http.server --cgi 8000
diff --git a/Doc/library/idle.rst b/Doc/library/idle.rst
index 36d78b0..2718cef 100644
--- a/Doc/library/idle.rst
+++ b/Doc/library/idle.rst
@@ -16,70 +16,82 @@ IDLE has the following features:
* coded in 100% pure Python, using the :mod:`tkinter` GUI toolkit
-* cross-platform: works on Windows and Unix
+* cross-platform: works on Windows, Unix, and Mac OS X
-* multi-window text editor with multiple undo, Python colorizing and many other
- features, e.g. smart indent and call tips
+* multi-window text editor with multiple undo, Python colorizing,
+ smart indent, call tips, and many other features
* Python shell window (a.k.a. interactive interpreter)
-* debugger (not complete, but you can set breakpoints, view and step)
+* debugger (not complete, but you can set breakpoints, view and step)
Menus
-----
+IDLE has two window types, the Shell window and the Editor window. It is
+possible to have multiple editor windows simultaneously. IDLE's
+menus dynamically change based on which window is currently selected. Each menu
+documented below indicates which window type it is associated with. Click on
+the dotted line at the top of a menu to "tear it off": a separate window
+containing the menu is created (for Unix and Windows only).
-File menu
-^^^^^^^^^
+File menu (Shell and Editor)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
New file
- create a new file editing window
+ Create a new file editing window
Open...
- open an existing file
+ Open an existing file
Open module...
- open an existing module (searches sys.path)
+ Open an existing module (searches sys.path)
+
+Recent Files
+ Open a list of recent files
Class browser
- show classes and methods in current file
+ Show classes and methods in current file
Path browser
- show sys.path directories, modules, classes and methods
+ Show sys.path directories, modules, classes and methods
.. index::
single: Class browser
single: Path browser
Save
- save current window to the associated file (unsaved windows have a \* before and
- after the window title)
+ Save current window to the associated file (unsaved windows have a
+ \* before and after the window title)
Save As...
- save current window to new file, which becomes the associated file
+ Save current window to new file, which becomes the associated file
Save Copy As...
- save current window to different file without changing the associated file
+ Save current window to different file without changing the associated file
+
+Print Window
+ Print the current window
Close
- close current window (asks to save if unsaved)
+ Close current window (asks to save if unsaved)
Exit
- close all windows and quit IDLE (asks to save if unsaved)
+ Close all windows and quit IDLE (asks to save if unsaved)
-Edit menu
-^^^^^^^^^
+Edit menu (Shell and Editor)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Undo
- Undo last change to current window (max 1000 changes)
+ Undo last change to current window (a maximum of 1000 changes may be undone)
Redo
Redo last undone change to current window
Cut
- Copy selection into system-wide clipboard; then delete selection
+ Copy selection into system-wide clipboard; then delete the selection
Copy
Copy selection into system-wide clipboard
@@ -108,11 +120,30 @@ Replace...
Go to line
Ask for a line number and show that line
+Expand word
+ Expand the word you have typed to match another word in the same buffer;
+ repeat to get a different expansion
+
+Show call tip
+ After an unclosed parenthesis for a function, open a small window with
+ function parameter hints
+
+Show surrounding parens
+ Highlight the surrounding parenthesis
+
+Show Completions
+ Open a scroll window allowing selection keywords and attributes. See
+ Completions below.
+
+
+Format menu (Editor window only)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
Indent region
- Shift selected lines right 4 spaces
+ Shift selected lines right by the indent width (default 4 spaces)
Dedent region
- Shift selected lines left 4 spaces
+ Shift selected lines left by the indent width (default 4 spaces)
Comment out region
Insert ## in front of selected lines
@@ -121,67 +152,121 @@ Uncomment region
Remove leading # or ## from selected lines
Tabify region
- Turns *leading* stretches of spaces into tabs
+ Turns *leading* stretches of spaces into tabs. (Note: We recommend using
+ 4 space blocks to indent Python code.)
Untabify region
- Turn *all* tabs into the right number of spaces
+ Turn *all* tabs into the correct number of spaces
-Expand word
- Expand the word you have typed to match another word in the same buffer; repeat
- to get a different expansion
+Toggle tabs
+ Open a dialog to switch between indenting with spaces and tabs.
-Format Paragraph
- Reformat the current blank-line-separated paragraph
+New Indent Width
+ Open a dialog to change indent width. The accepted default by the Python
+ community is 4 spaces.
-Import module
- Import or reload the current module
+Format Paragraph
+ Reformat the current blank-line-separated paragraph. All lines in the
+ paragraph will be formatted to less than 80 columns.
-Run script
- Execute the current file in the __main__ namespace
+Strip trailing whitespace
+ Removes any space characters after the end of the last non-space character
.. index::
single: Import module
single: Run script
-Windows menu
-^^^^^^^^^^^^
+Run menu (Editor window only)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-Zoom Height
- toggles the window between normal size (24x80) and maximum height.
+Python Shell
+ Open or wake up the Python Shell window
-The rest of this menu lists the names of all open windows; select one to bring
-it to the foreground (deiconifying it if necessary).
+Check module
+ Check the syntax of the module currently open in the Editor window. If the
+ module has not been saved IDLE will prompt the user to save the code.
+
+Run module
+ Restart the shell to clean the environment, then execute the currently
+ open module. If the module has not been saved IDLE will prompt the user
+ to save the code.
+Shell menu (Shell window only)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-Debug menu
-^^^^^^^^^^
+View Last Restart
+ Scroll the shell window to the last Shell restart
-* in the Python Shell window only
+Restart Shell
+ Restart the shell to clean the environment
+
+Debug menu (Shell window only)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Go to file/line
Look around the insert point for a filename and line number, open the file,
and show the line. Useful to view the source lines referenced in an
- exception traceback.
+ exception traceback. Available in the context menu of the Shell window.
-Debugger
- Run commands in the shell under the debugger.
+Debugger (toggle)
+ This feature is not complete and considered experimental. Run commands in
+ the shell under the debugger
Stack viewer
- Show the stack traceback of the last exception.
+ Show the stack traceback of the last exception
Auto-open Stack Viewer
- Open stack viewer on traceback.
+ Toggle automatically opening the stack viewer on unhandled exception
.. index::
single: stack viewer
single: debugger
+Options menu (Shell and Editor)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Configure IDLE
+ Open a configuration dialog. Fonts, indentation, keybindings, and color
+ themes may be altered. Startup Preferences may be set, and additional
+ help sources can be specified.
+
+Code Context (toggle)(Editor Window only)
+ Open a pane at the top of the edit window which shows the block context
+ of the section of code which is scrolling off the top of the window.
+
+Windows menu (Shell and Editor)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Zoom Height
+ Toggles the window between normal size (40x80 initial setting) and maximum
+ height. The initial size is in the Configure IDLE dialog under the general
+ tab.
+
+The rest of this menu lists the names of all open windows; select one to bring
+it to the foreground (deiconifying it if necessary).
+
+Help menu (Shell and Editor)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+About IDLE
+ Version, copyright, license, credits
+
+IDLE Help
+ Display a help file for IDLE detailing the menu options, basic editing and
+ navigation, and other tips.
+
+Python Docs
+ Access local Python documentation, if installed. Or will start a web browser
+ and open docs.python.org showing the latest Python documentation.
-Edit context menu
-^^^^^^^^^^^^^^^^^
+Additional help sources may be added here with the Configure IDLE dialog under
+the General tab.
-* Right-click in Edit window (Control-click on OS X)
+Editor Window context menu
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Right-click in Editor window (Control-click on OS X)
Cut
Copy selection into system-wide clipboard; then delete selection
@@ -207,8 +292,8 @@ Clear Breakpoint
single: breakpoints
-Shell context menu
-^^^^^^^^^^^^^^^^^^
+Shell Window context menu
+^^^^^^^^^^^^^^^^^^^^^^^^^
* Right-click in Python Shell window (Control-click on OS X)
@@ -225,19 +310,44 @@ Go to file/line
Same as in Debug menu.
-Basic editing and navigation
-----------------------------
+Editing and navigation
+----------------------
* :kbd:`Backspace` deletes to the left; :kbd:`Del` deletes to the right
+* :kbd:`C-Backspace` delete word left; :kbd:`C-Del` delete word to the right
+
* Arrow keys and :kbd:`Page Up`/:kbd:`Page Down` to move around
+* :kbd:`C-LeftArrow` and :kbd:`C-RightArrow` moves by words
+
* :kbd:`Home`/:kbd:`End` go to begin/end of line
* :kbd:`C-Home`/:kbd:`C-End` go to begin/end of file
-* Some :program:`Emacs` bindings may also work, including :kbd:`C-B`,
- :kbd:`C-P`, :kbd:`C-A`, :kbd:`C-E`, :kbd:`C-D`, :kbd:`C-L`
+* Some useful Emacs bindings are inherited from Tcl/Tk:
+
+ * :kbd:`C-a` beginning of line
+
+ * :kbd:`C-e` end of line
+
+ * :kbd:`C-k` kill line (but doesn't put it in clipboard)
+
+ * :kbd:`C-l` center window around the insertion point
+
+ * :kbd:`C-b` go backwards one character without deleting (usually you can
+ also use the cursor key for this)
+
+ * :kbd:`C-f` go forward one character without deleting (usually you can
+ also use the cursor key for this)
+
+ * :kbd:`C-p` go up one line (usually you can also use the cursor key for
+ this)
+
+ * :kbd:`C-d` delete next character
+
+Standard keybindings (like :kbd:`C-c` to copy and :kbd:`C-v` to paste)
+may work. Keybindings are selected in the Configure IDLE dialog.
Automatic indentation
@@ -246,27 +356,76 @@ Automatic indentation
After a block-opening statement, the next line is indented by 4 spaces (in the
Python Shell window by one tab). After certain keywords (break, return etc.)
the next line is dedented. In leading indentation, :kbd:`Backspace` deletes up
-to 4 spaces if they are there. :kbd:`Tab` inserts 1-4 spaces (in the Python
-Shell window one tab). See also the indent/dedent region commands in the edit
-menu.
-
+to 4 spaces if they are there. :kbd:`Tab` inserts spaces (in the Python
+Shell window one tab), number depends on Indent width. Currently tabs
+are restricted to four spaces due to Tcl/Tk limitations.
+
+See also the indent/dedent region commands in the edit menu.
+
+Completions
+^^^^^^^^^^^
+
+Completions are supplied for functions, classes, and attributes of classes,
+both built-in and user-defined. Completions are also provided for
+filenames.
+
+The AutoCompleteWindow (ACW) will open after a predefined delay (default is
+two seconds) after a '.' or (in a string) an os.sep is typed. If after one
+of those characters (plus zero or more other characters) a tab is typed
+the ACW will open immediately if a possible continuation is found.
+
+If there is only one possible completion for the characters entered, a
+:kbd:`Tab` will supply that completion without opening the ACW.
+
+'Show Completions' will force open a completions window, by default the
+:kbd:`C-space` will open a completions window. In an empty
+string, this will contain the files in the current directory. On a
+blank line, it will contain the built-in and user-defined functions and
+classes in the current name spaces, plus any modules imported. If some
+characters have been entered, the ACW will attempt to be more specific.
+
+If a string of characters is typed, the ACW selection will jump to the
+entry most closely matching those characters. Entering a :kbd:`tab` will
+cause the longest non-ambiguous match to be entered in the Editor window or
+Shell. Two :kbd:`tab` in a row will supply the current ACW selection, as
+will return or a double click. Cursor keys, Page Up/Down, mouse selection,
+and the scroll wheel all operate on the ACW.
+
+"Hidden" attributes can be accessed by typing the beginning of hidden
+name after a '.', e.g. '_'. This allows access to modules with
+``__all__`` set, or to class-private attributes.
+
+Completions and the 'Expand Word' facility can save a lot of typing!
+
+Completions are currently limited to those in the namespaces. Names in
+an Editor window which are not via ``__main__`` and :data:`sys.modules` will
+not be found. Run the module once with your imports to correct this situation.
+Note that IDLE itself places quite a few modules in sys.modules, so
+much can be found by default, e.g. the re module.
+
+If you don't like the ACW popping up unbidden, simply make the delay
+longer or disable the extension. Or another option is the delay could
+be set to zero. Another alternative to preventing ACW popups is to
+disable the call tips extension.
Python Shell window
^^^^^^^^^^^^^^^^^^^
-* :kbd:`C-C` interrupts executing command
+* :kbd:`C-c` interrupts executing command
-* :kbd:`C-D` sends end-of-file; closes window if typed at a ``>>>`` prompt
+* :kbd:`C-d` sends end-of-file; closes window if typed at a ``>>>`` prompt
+ (this is :kbd:`C-z` on Windows).
-* :kbd:`Alt-p` retrieves previous command matching what you have typed
+* :kbd:`Alt-/` (Expand word) is also useful to reduce typing
-* :kbd:`Alt-n` retrieves next
+ Command history
-* :kbd:`Return` while on any previous command retrieves that command
+ * :kbd:`Alt-p` retrieves previous command matching what you have typed. On
+ OS X use :kbd:`C-p`.
-* :kbd:`Alt-/` (Expand word) is also useful here
+ * :kbd:`Alt-n` retrieves next. On OS X use :kbd:`C-n`.
-.. index:: single: indentation
+ * :kbd:`Return` while on any previous command retrieves that command
Syntax colors
@@ -308,17 +467,17 @@ Startup
Upon startup with the ``-s`` option, IDLE will execute the file referenced by
the environment variables :envvar:`IDLESTARTUP` or :envvar:`PYTHONSTARTUP`.
-Idle first checks for ``IDLESTARTUP``; if ``IDLESTARTUP`` is present the file
-referenced is run. If ``IDLESTARTUP`` is not present, Idle checks for
+IDLE first checks for ``IDLESTARTUP``; if ``IDLESTARTUP`` is present the file
+referenced is run. If ``IDLESTARTUP`` is not present, IDLE checks for
``PYTHONSTARTUP``. Files referenced by these environment variables are
-convenient places to store functions that are used frequently from the Idle
+convenient places to store functions that are used frequently from the IDLE
shell, or for executing import statements to import common modules.
In addition, ``Tk`` also loads a startup file if it is present. Note that the
Tk file is loaded unconditionally. This additional file is ``.Idle.py`` and is
looked for in the user's home directory. Statements in this file will be
executed in the Tk namespace, so this file is not useful for importing functions
-to be used from Idle's Python shell.
+to be used from IDLE's Python shell.
Command line usage
@@ -349,3 +508,45 @@ If there are arguments:
the arguments are still available in ``sys.argv``.
+Additional help sources
+-----------------------
+
+IDLE includes a help menu entry called "Python Docs" that will open the
+extensive sources of help, including tutorials, available at docs.python.org.
+Selected URLs can be added or removed from the help menu at any time using the
+Configure IDLE dialog. See the IDLE help option in the help menu of IDLE for
+more information.
+
+
+Other preferences
+-----------------
+
+The font preferences, highlighting, keys, and general preferences can be
+changed via the Configure IDLE menu option. Be sure to note that
+keys can be user defined, IDLE ships with four built in key sets. In
+addition a user can create a custom key set in the Configure IDLE dialog
+under the keys tab.
+
+Extensions
+----------
+
+IDLE contains an extension facility. See the beginning of
+config-extensions.def in the idlelib directory for further information. The
+default extensions are currently:
+
+* FormatParagraph
+
+* AutoExpand
+
+* ZoomHeight
+
+* ScriptBinding
+
+* CallTips
+
+* ParenMatch
+
+* AutoComplete
+
+* CodeContext
+
diff --git a/Doc/library/imaplib.rst b/Doc/library/imaplib.rst
index 01236fb..fa736fe 100644
--- a/Doc/library/imaplib.rst
+++ b/Doc/library/imaplib.rst
@@ -69,17 +69,25 @@ There's also a subclass for secure connections:
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. *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 raised if *keyfile*/*certfile* is provided along with *ssl_context*.
+ If *port* is omitted, the standard IMAP4-over-SSL port (993) is used.
+ *ssl_context* is a :class:`ssl.SSLContext` object which allows bundling
+ SSL configuration options, certificates and private keys into a single
+ (potentially long-lived) structure. Please read :ref:`ssl-security` for
+ best practices.
+
+ *keyfile* and *certfile* are a legacy alternative to *ssl_context* - they
+ can point to PEM-formatted private key and certificate chain files for
+ the SSL connection. Note that the *keyfile*/*certfile* parameters are
+ mutually exclusive with *ssl_context*, a :class:`ValueError` is raised
+ if *keyfile*/*certfile* is provided along with *ssl_context*.
.. versionchanged:: 3.3
*ssl_context* parameter added.
+ .. versionchanged:: 3.4
+ The class now supports hostname check with
+ :attr:`ssl.SSLContext.check_hostname` and *Server Name Indication* (see
+ :data:`ssl.HAS_SNI`).
The second subclass allows for connections created by a child process:
@@ -433,10 +441,16 @@ An :class:`IMAP4` instance has the following methods:
Send a ``STARTTLS`` command. The *ssl_context* argument is optional
and should be a :class:`ssl.SSLContext` object. This will enable
- encryption on the IMAP connection.
+ encryption on the IMAP connection. Please read :ref:`ssl-security` for
+ best practices.
.. versionadded:: 3.2
+ .. versionchanged:: 3.4
+ The method now supports hostname check with
+ :attr:`ssl.SSLContext.check_hostname` and *Server Name Indication* (see
+ :data:`ssl.HAS_SNI`).
+
.. method:: IMAP4.status(mailbox, names)
diff --git a/Doc/library/imp.rst b/Doc/library/imp.rst
index 364d81e..ed4820e 100644
--- a/Doc/library/imp.rst
+++ b/Doc/library/imp.rst
@@ -3,7 +3,10 @@
.. module:: imp
:synopsis: Access the implementation of the import statement.
+ :deprecated:
+.. deprecated:: 3.4
+ The :mod:`imp` package is pending deprecation in favor of :mod:`importlib`.
.. index:: statement: import
@@ -11,10 +14,6 @@ This module provides an interface to the mechanisms used to implement the
:keyword:`import` statement. It defines the following constants and functions:
-.. note::
- New programs should use :mod:`importlib` rather than this module.
-
-
.. function:: get_magic()
.. index:: pair: file; byte-code
@@ -22,6 +21,9 @@ This module provides an interface to the mechanisms used to implement the
Return the magic string value used to recognize byte-compiled code files
(:file:`.pyc` files). (This value may be different for each Python version.)
+ .. deprecated:: 3.4
+ Use :attr:`importlib.util.MAGIC_NUMBER` instead.
+
.. function:: get_suffixes()
@@ -101,8 +103,10 @@ This module provides an interface to the mechanisms used to implement the
using a :keyword:`try` ... :keyword:`finally` statement.
.. deprecated:: 3.3
- Unneeded as loaders should be used to load modules and
- :func:`find_module` is deprecated.
+ If previously used in conjunction with :func:`imp.find_module` then
+ call ``load_module()`` on the returned loader. If you wish to load a
+ module from a specific file, then use one of the file-based loaders found
+ in :mod:`importlib.machinery`.
.. function:: new_module(name)
@@ -110,6 +114,9 @@ This module provides an interface to the mechanisms used to implement the
Return a new empty module object called *name*. This object is *not* inserted
in ``sys.modules``.
+ .. deprecated:: 3.4
+ Use :class:`types.ModuleType` instead.
+
.. function:: reload(module)
@@ -176,6 +183,9 @@ This module provides an interface to the mechanisms used to implement the
Relies on both ``__name__`` and ``__loader__`` being defined on the module
being reloaded instead of just ``__name__``.
+ .. deprecated:: 3.4
+ Use :func:`importlib.reload` instead.
+
The following functions are conveniences for handling :pep:`3147` byte-compiled
file paths.
@@ -201,6 +211,9 @@ file paths.
If :attr:`sys.implementation.cache_tag` is ``None``, then
:exc:`NotImplementedError` is raised.
+ .. deprecated:: 3.4
+ Use :func:`importlib.util.cache_from_source` instead.
+
.. function:: source_from_cache(path)
@@ -216,14 +229,17 @@ file paths.
Raise :exc:`NotImplementedError` when
:attr:`sys.implementation.cache_tag` is not defined.
+ .. deprecated:: 3.4
+ Use :func:`importlib.util.source_from_cache` instead.
+
.. function:: get_tag()
Return the :pep:`3147` magic tag string matching this version of Python's
magic number, as returned by :func:`get_magic`.
- .. note::
- You may use :attr:`sys.implementation.cache_tag` directly starting
+ .. deprecated:: 3.4
+ Use :attr:`sys.implementation.cache_tag` directly starting
in Python 3.3.
@@ -246,10 +262,12 @@ that circular imports work without any deadlocks.
exception is made for circular imports, which by construction have to
expose an incomplete module object at some point.
-.. versionchanged:: 3.3
- The locking scheme has changed to per-module locks for
- the most part. A global import lock is kept for some critical tasks,
- such as initializing the per-module locks.
+ .. versionchanged:: 3.3
+ The locking scheme has changed to per-module locks for
+ the most part. A global import lock is kept for some critical tasks,
+ such as initializing the per-module locks.
+
+ .. deprecated:: 3.4
.. function:: acquire_lock()
@@ -264,10 +282,12 @@ that circular imports work without any deadlocks.
On platforms without threads, this function does nothing.
-.. versionchanged:: 3.3
- The locking scheme has changed to per-module locks for
- the most part. A global import lock is kept for some critical tasks,
- such as initializing the per-module locks.
+ .. versionchanged:: 3.3
+ The locking scheme has changed to per-module locks for
+ the most part. A global import lock is kept for some critical tasks,
+ such as initializing the per-module locks.
+
+ .. deprecated:: 3.4
.. function:: release_lock()
@@ -275,10 +295,12 @@ that circular imports work without any deadlocks.
Release the interpreter's global import lock. On platforms without
threads, this function does nothing.
-.. versionchanged:: 3.3
- The locking scheme has changed to per-module locks for
- the most part. A global import lock is kept for some critical tasks,
- such as initializing the per-module locks.
+ .. versionchanged:: 3.3
+ The locking scheme has changed to per-module locks for
+ the most part. A global import lock is kept for some critical tasks,
+ such as initializing the per-module locks.
+
+ .. deprecated:: 3.4
The following constants with integer values, defined in this module, are used
@@ -345,6 +367,9 @@ to indicate the search result of :func:`find_module`.
``None`` is inserted into ``sys.path_importer_cache`` instead of an
instance of :class:`NullImporter`.
+ .. deprecated:: 3.4
+ Insert ``None`` into ``sys.path_importer_cache`` instead.
+
.. _examples-imp:
diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst
index 5f740a2..afdae9e 100644
--- a/Doc/library/importlib.rst
+++ b/Doc/library/importlib.rst
@@ -95,7 +95,7 @@ Functions
Find the loader for a module, optionally within the specified *path*. If the
module is in :attr:`sys.modules`, then ``sys.modules[name].__loader__`` is
- returned (unless the loader would be ``None``, in which case
+ returned (unless the loader would be ``None`` or is not set, in which case
:exc:`ValueError` is raised). Otherwise a search using :attr:`sys.meta_path`
is done. ``None`` is returned if no loader is found.
@@ -104,6 +104,15 @@ Functions
will need to import all parent packages of the submodule and use the correct
argument to *path*.
+ .. versionadded:: 3.3
+
+ .. versionchanged:: 3.4
+ If ``__loader__`` is not set, raise :exc:`ValueError`, just like when the
+ attribute is set to ``None``.
+
+ .. deprecated:: 3.4
+ Use :func:`importlib.util.find_spec` instead.
+
.. function:: invalidate_caches()
Invalidate the internal caches of finders stored at
@@ -114,6 +123,74 @@ Functions
.. versionadded:: 3.3
+.. function:: reload(module)
+
+ Reload a previously imported *module*. The argument must be a module object,
+ so it must have been successfully imported before. This is useful if you
+ have edited the module source file using an external editor and want to try
+ out the new version without leaving the Python interpreter. The return value
+ is the module object (which can be different if re-importing causes a
+ different object to be placed in :data:`sys.modules`).
+
+ When :func:`reload` is executed:
+
+ * Python module's code is recompiled and the module-level code re-executed,
+ defining a new set of objects which are bound to names in the module's
+ dictionary by reusing the :term:`loader` which originally loaded the
+ module. The ``init`` function of extension modules is not called a second
+ time.
+
+ * As with all other objects in Python the old objects are only reclaimed
+ after their reference counts drop to zero.
+
+ * The names in the module namespace are updated to point to any new or
+ changed objects.
+
+ * Other references to the old objects (such as names external to the module) are
+ not rebound to refer to the new objects and must be updated in each namespace
+ where they occur if that is desired.
+
+ There are a number of other caveats:
+
+ If a module is syntactically correct but its initialization fails, the first
+ :keyword:`import` statement for it does not bind its name locally, but does
+ store a (partially initialized) module object in ``sys.modules``. To reload
+ the module you must first :keyword:`import` it again (this will bind the name
+ to the partially initialized module object) before you can :func:`reload` it.
+
+ When a module is reloaded, its dictionary (containing the module's global
+ variables) is retained. Redefinitions of names will override the old
+ definitions, so this is generally not a problem. If the new version of a
+ module does not define a name that was defined by the old version, the old
+ definition remains. This feature can be used to the module's advantage if it
+ maintains a global table or cache of objects --- with a :keyword:`try`
+ statement it can test for the table's presence and skip its initialization if
+ desired::
+
+ try:
+ cache
+ except NameError:
+ cache = {}
+
+ It is legal though generally not very useful to reload built-in or
+ dynamically loaded modules (this is not true for e.g. :mod:`sys`,
+ :mod:`__main__`, :mod:`builtins` and other key modules where reloading is
+ frowned upon). In many cases, however, extension modules are not designed to
+ be initialized more than once, and may fail in arbitrary ways when reloaded.
+
+ If a module imports objects from another module using :keyword:`from` ...
+ :keyword:`import` ..., calling :func:`reload` for the other module does not
+ redefine the objects imported from it --- one way around this is to
+ re-execute the :keyword:`from` statement, another is to use :keyword:`import`
+ and qualified names (*module.name*) instead.
+
+ If a module instantiates instances of a class, reloading the module that
+ defines the class does not affect the method definitions of the instances ---
+ they continue to use the old class definition. The same is true for derived
+ classes.
+
+ .. versionadded:: 3.4
+
:mod:`importlib.abc` -- Abstract base classes related to import
---------------------------------------------------------------
@@ -137,8 +214,6 @@ ABC hierarchy::
+-- ExecutionLoader --+
+-- FileLoader
+-- SourceLoader
- +-- PyLoader (deprecated)
- +-- PyPycLoader (deprecated)
.. class:: Finder
@@ -154,6 +229,10 @@ ABC hierarchy::
module. Originally specified in :pep:`302`, this method was meant
for use in :data:`sys.meta_path` and in the path-based import subsystem.
+ .. versionchanged:: 3.4
+ Returns ``None`` when called instead of raising
+ :exc:`NotImplementedError`.
+
.. class:: MetaPathFinder
@@ -162,20 +241,46 @@ ABC hierarchy::
.. versionadded:: 3.3
+ .. method:: find_spec(fullname, path, target=None)
+
+ An abstract method for finding a :term:`spec <module spec>` for
+ the specified module. If this is a top-level import, *path* will
+ be ``None``. Otherwise, this is a search for a subpackage or
+ module and *path* will be the value of :attr:`__path__` from the
+ parent package. If a spec cannot be found, ``None`` is returned.
+ When passed in, ``target`` is a module object that the finder may
+ use to make a more educated about what spec to return.
+
+ .. versionadded:: 3.4
+
.. method:: find_module(fullname, path)
- An abstract method for finding a :term:`loader` for the specified
+ A legacy method for finding a :term:`loader` for the specified
module. If this is a top-level import, *path* will be ``None``.
Otherwise, this is a search for a subpackage or module and *path*
will be the value of :attr:`__path__` from the parent
package. If a loader cannot be found, ``None`` is returned.
+ If :meth:`find_spec` is defined, backwards-compatible functionality is
+ provided.
+
+ .. versionchanged:: 3.4
+ Returns ``None`` when called instead of raising
+ :exc:`NotImplementedError`. Can use :meth:`find_spec` to provide
+ functionality.
+
+ .. deprecated:: 3.4
+ Use :meth:`find_spec` instead.
+
.. method:: invalidate_caches()
An optional method which, when called, should invalidate any internal
cache used by the finder. Used by :func:`importlib.invalidate_caches`
when invalidating the caches of all finders on :data:`sys.meta_path`.
+ .. versionchanged:: 3.4
+ Returns ``None`` when called instead of ``NotImplemented``.
+
.. class:: PathEntryFinder
@@ -183,27 +288,51 @@ ABC hierarchy::
it bears some similarities to :class:`MetaPathFinder`, ``PathEntryFinder``
is meant for use only within the path-based import subsystem provided
by :class:`PathFinder`. This ABC is a subclass of :class:`Finder` for
- compatibility.
+ compatibility reasons only.
.. versionadded:: 3.3
+ .. method:: find_spec(fullname, target=None)
+
+ An abstract method for finding a :term:`spec <module spec>` for
+ the specified module. The finder will search for the module only
+ within the :term:`path entry` to which it is assigned. If a spec
+ cannot be found, ``None`` is returned. When passed in, ``target``
+ is a module object that the finder may use to make a more educated
+ about what spec to return.
+
+ .. versionadded:: 3.4
+
.. method:: find_loader(fullname)
- An abstract method for finding a :term:`loader` for the specified
+ A legacy method for finding a :term:`loader` for the specified
module. Returns a 2-tuple of ``(loader, portion)`` where ``portion``
is a sequence of file system locations contributing to part of a namespace
package. The loader may be ``None`` while specifying ``portion`` to
signify the contribution of the file system locations to a namespace
package. An empty list can be used for ``portion`` to signify the loader
- is not part of a package. If ``loader`` is ``None`` and ``portion`` is
- the empty list then no loader or location for a namespace package were
- found (i.e. failure to find anything for the module).
+ is not part of a namespace package. If ``loader`` is ``None`` and
+ ``portion`` is the empty list then no loader or location for a namespace
+ package were found (i.e. failure to find anything for the module).
+
+ If :meth:`find_spec` is defined then backwards-compatible functionality is
+ provided.
+
+ .. versionchanged:: 3.4
+ Returns ``(None, [])`` instead of raising :exc:`NotImplementedError`.
+ Uses :meth:`find_spec` when available to provide functionality.
+
+ .. deprecated:: 3.4
+ Use :meth:`find_spec` instead.
.. method:: find_module(fullname)
A concrete implementation of :meth:`Finder.find_module` which is
equivalent to ``self.find_loader(fullname)[0]``.
+ .. deprecated:: 3.4
+ Use :meth:`find_spec` instead.
+
.. method:: invalidate_caches()
An optional method which, when called, should invalidate any internal
@@ -216,9 +345,26 @@ ABC hierarchy::
An abstract base class for a :term:`loader`.
See :pep:`302` for the exact definition for a loader.
+ .. method:: create_module(spec)
+
+ An optional method that returns the module object to use when
+ importing a module. create_module() may also return ``None``,
+ indicating that the default module creation should take place
+ instead.
+
+ .. versionadded:: 3.4
+
+ .. method:: exec_module(module)
+
+ An abstract method that executes the module in its own namespace
+ when a module is imported or reloaded. The module should already
+ be initialized when exec_module() is called.
+
+ .. versionadded:: 3.4
+
.. method:: load_module(fullname)
- An abstract method for loading a module. If the module cannot be
+ A legacy method for loading a module. If the module cannot be
loaded, :exc:`ImportError` is raised, otherwise the loaded module is
returned.
@@ -229,12 +375,11 @@ ABC hierarchy::
from the import. If the loader inserted a module and the load fails, it
must be removed by the loader from :data:`sys.modules`; modules already
in :data:`sys.modules` before the loader began execution should be left
- alone. The :func:`importlib.util.module_for_loader` decorator handles
- all of these details.
+ alone (see :func:`importlib.util.module_for_loader`).
The loader should set several attributes on the module.
(Note that some of these attributes can change when a module is
- reloaded.)
+ reloaded):
- :attr:`__name__`
The name of the module.
@@ -254,20 +399,42 @@ ABC hierarchy::
- :attr:`__package__`
The parent package for the module/package. If the module is
top-level then it has a value of the empty string. The
- :func:`importlib.util.set_package` decorator can handle the details
- for :attr:`__package__`.
+ :func:`importlib.util.module_for_loader` decorator can handle the
+ details for :attr:`__package__`.
- :attr:`__loader__`
- The loader used to load the module.
- (This is not set by the built-in import machinery,
- but it should be set whenever a :term:`loader` is used.)
+ The loader used to load the module. The
+ :func:`importlib.util.module_for_loader` decorator can handle the
+ details for :attr:`__package__`.
+
+ When :meth:`exec_module` is available then backwards-compatible
+ functionality is provided.
+
+ .. versionchanged:: 3.4
+ Raise :exc:`ImportError` when called instead of
+ :exc:`NotImplementedError`. Functionality provided when
+ :meth:`exec_module` is available.
+
+ .. deprecated:: 3.4
+ The recommended API for loading a module is :meth:`exec_module`
+ (and optionally :meth:`create_module`). Loaders should implement
+ it instead of load_module(). The import machinery takes care of
+ all the other responsibilities of load_module() when exec_module()
+ is implemented.
.. method:: module_repr(module)
- An abstract method which when implemented calculates and returns the
- given module's repr, as a string.
+ A legacy method which when implemented calculates and returns the
+ given module's repr, as a string. The module type's default repr() will
+ use the result of this method as appropriate.
+
+ .. versionadded:: 3.3
+
+ .. versionchanged:: 3.4
+ Made optional instead of an abstractmethod.
- .. versionadded: 3.3
+ .. deprecated:: 3.4
+ The import machinery now takes care of this automatically.
.. class:: ResourceLoader
@@ -282,10 +449,13 @@ ABC hierarchy::
Loaders that have a file-like storage back-end
that allows storing arbitrary data
can implement this abstract method to give direct access
- to the data stored. :exc:`IOError` is to be raised if the *path* cannot
+ to the data stored. :exc:`OSError` is to be raised if the *path* cannot
be found. The *path* is expected to be constructed using a module's
:attr:`__file__` attribute or an item from a package's :attr:`__path__`.
+ .. versionchanged:: 3.4
+ Raises :exc:`OSError` instead of :exc:`NotImplementedError`.
+
.. class:: InspectLoader
@@ -294,14 +464,21 @@ ABC hierarchy::
.. method:: get_code(fullname)
- An abstract method to return the :class:`code` object for a module.
- ``None`` is returned if the module does not have a code object
- (e.g. built-in module). :exc:`ImportError` is raised if loader cannot
- find the requested module.
+ Return the code object for a module, or ``None`` if the module does not
+ have a code object (as would be the case, for example, for a built-in
+ module). Raise an :exc:`ImportError` if loader cannot find the
+ requested module.
+
+ .. note::
+ While the method has a default implementation, it is suggested that
+ it be overridden if possible for performance.
.. index::
single: universal newlines; importlib.abc.InspectLoader.get_source method
+ .. versionchanged:: 3.4
+ No longer abstract and a concrete implementation is provided.
+
.. method:: get_source(fullname)
An abstract method to return the source of a module. It is returned as
@@ -310,12 +487,42 @@ ABC hierarchy::
if no source is available (e.g. a built-in module). Raises
:exc:`ImportError` if the loader cannot find the module specified.
+ .. versionchanged:: 3.4
+ Raises :exc:`ImportError` instead of :exc:`NotImplementedError`.
+
.. method:: is_package(fullname)
An abstract method to return a true value if the module is a package, a
false value otherwise. :exc:`ImportError` is raised if the
:term:`loader` cannot find the module.
+ .. versionchanged:: 3.4
+ Raises :exc:`ImportError` instead of :exc:`NotImplementedError`.
+
+ .. method:: source_to_code(data, path='<string>')
+
+ Create a code object from Python source.
+
+ The *data* argument can be whatever the :func:`compile` function
+ supports (i.e. string or bytes). The *path* argument should be
+ the "path" to where the source code originated from, which can be an
+ abstract concept (e.g. location in a zip file).
+
+ .. versionadded:: 3.4
+
+ .. method:: exec_module(module)
+
+ Implementation of :meth:`Loader.exec_module`.
+
+ .. versionadded:: 3.4
+
+ .. method:: load_module(fullname)
+
+ Implementation of :meth:`Loader.load_module`.
+
+ .. deprecated:: 3.4
+ use :meth:`exec_module` instead.
+
.. class:: ExecutionLoader
@@ -333,6 +540,9 @@ ABC hierarchy::
the source file, regardless of whether a bytecode was used to load the
module.
+ .. versionchanged:: 3.4
+ Raises :exc:`ImportError` instead of :exc:`NotImplementedError`.
+
.. class:: FileLoader(fullname, path)
@@ -357,13 +567,16 @@ ABC hierarchy::
Calls super's ``load_module()``.
+ .. deprecated:: 3.4
+ Use :meth:`Loader.exec_module` instead.
+
.. method:: get_filename(fullname)
Returns :attr:`path`.
.. method:: get_data(path)
- Returns the open, binary file for *path*.
+ Reads *path* as a binary file and returns the bytes from it.
.. class:: SourceLoader
@@ -378,7 +591,8 @@ ABC hierarchy::
loading is not supported.
The abstract methods defined by this class are to add optional bytecode
- file support. Not implementing these optional methods causes the loader to
+ file support. Not implementing these optional methods (or causing them to
+ raise :exc:`NotImplementedError`) causes the loader to
only work with source code. Implementing the methods allows the loader to
work with source *and* bytecode files; it does not allow for *sourceless*
loading where only bytecode is provided. Bytecode files are an
@@ -395,10 +609,13 @@ ABC hierarchy::
- ``'size'`` (optional): the size in bytes of the source code.
Any other keys in the dictionary are ignored, to allow for future
- extensions.
+ extensions. If the path cannot be handled, :exc:`OSError` is raised.
.. versionadded:: 3.3
+ .. versionchanged:: 3.4
+ Raise :exc:`OSError` instead of :exc:`NotImplementedError`.
+
.. method:: path_mtime(path)
Optional abstract method which returns the modification time for the
@@ -407,7 +624,10 @@ ABC hierarchy::
.. deprecated:: 3.3
This method is deprecated in favour of :meth:`path_stats`. You don't
have to implement it, but it is still available for compatibility
- purposes.
+ purposes. Raise :exc:`OSError` if the path cannot be handled.
+
+ .. versionchanged:: 3.4
+ Raise :exc:`OSError` instead of :exc:`NotImplementedError`.
.. method:: set_data(path, data)
@@ -419,13 +639,25 @@ ABC hierarchy::
(:attr:`errno.EACCES`/:exc:`PermissionError`), do not propagate the
exception.
+ .. versionchanged:: 3.4
+ No longer raises :exc:`NotImplementedError` when called.
+
.. method:: get_code(fullname)
Concrete implementation of :meth:`InspectLoader.get_code`.
+ .. method:: exec_module(module)
+
+ Concrete implementation of :meth:`Loader.exec_module`.
+
+ .. versionadded:: 3.4
+
.. method:: load_module(fullname)
- Concrete implementation of :meth:`Loader.load_module`.
+ Concrete implementation of :meth:`Loader.load_module`.
+
+ .. deprecated:: 3.4
+ Use :meth:`exec_module` instead.
.. method:: get_source(fullname)
@@ -440,142 +672,6 @@ ABC hierarchy::
itself does not end in ``__init__``.
-.. class:: PyLoader
-
- An abstract base class inheriting from
- :class:`ExecutionLoader` and
- :class:`ResourceLoader` designed to ease the loading of
- Python source modules (bytecode is not handled; see
- :class:`SourceLoader` for a source/bytecode ABC). A subclass
- implementing this ABC will only need to worry about exposing how the source
- code is stored; all other details for loading Python source code will be
- handled by the concrete implementations of key methods.
-
- .. deprecated:: 3.2
- This class has been deprecated in favor of :class:`SourceLoader` and is
- slated for removal in Python 3.4. See below for how to create a
- subclass that is compatible with Python 3.1 onwards.
-
- If compatibility with Python 3.1 is required, then use the following idiom
- to implement a subclass that will work with Python 3.1 onwards (make sure
- to implement :meth:`ExecutionLoader.get_filename`)::
-
- try:
- from importlib.abc import SourceLoader
- except ImportError:
- from importlib.abc import PyLoader as SourceLoader
-
-
- class CustomLoader(SourceLoader):
- def get_filename(self, fullname):
- """Return the path to the source file."""
- # Implement ...
-
- def source_path(self, fullname):
- """Implement source_path in terms of get_filename."""
- try:
- return self.get_filename(fullname)
- except ImportError:
- return None
-
- def is_package(self, fullname):
- """Implement is_package by looking for an __init__ file
- name as returned by get_filename."""
- filename = os.path.basename(self.get_filename(fullname))
- return os.path.splitext(filename)[0] == '__init__'
-
-
- .. method:: source_path(fullname)
-
- An abstract method that returns the path to the source code for a
- module. Should return ``None`` if there is no source code.
- Raises :exc:`ImportError` if the loader knows it cannot handle the
- module.
-
- .. method:: get_filename(fullname)
-
- A concrete implementation of
- :meth:`importlib.abc.ExecutionLoader.get_filename` that
- relies on :meth:`source_path`. If :meth:`source_path` returns
- ``None``, then :exc:`ImportError` is raised.
-
- .. method:: load_module(fullname)
-
- A concrete implementation of :meth:`importlib.abc.Loader.load_module`
- that loads Python source code. All needed information comes from the
- abstract methods required by this ABC. The only pertinent assumption
- made by this method is that when loading a package
- :attr:`__path__` is set to ``[os.path.dirname(__file__)]``.
-
- .. method:: get_code(fullname)
-
- A concrete implementation of
- :meth:`importlib.abc.InspectLoader.get_code` that creates code objects
- from Python source code, by requesting the source code (using
- :meth:`source_path` and :meth:`get_data`) and compiling it with the
- built-in :func:`compile` function.
-
- .. method:: get_source(fullname)
-
- A concrete implementation of
- :meth:`importlib.abc.InspectLoader.get_source`. Uses
- :meth:`importlib.abc.ResourceLoader.get_data` and :meth:`source_path`
- to get the source code. It tries to guess the source encoding using
- :func:`tokenize.detect_encoding`.
-
-
-.. class:: PyPycLoader
-
- An abstract base class inheriting from :class:`PyLoader`.
- This ABC is meant to help in creating loaders that support both Python
- source and bytecode.
-
- .. deprecated:: 3.2
- This class has been deprecated in favor of :class:`SourceLoader` and to
- properly support :pep:`3147`. If compatibility is required with
- Python 3.1, implement both :class:`SourceLoader` and :class:`PyLoader`;
- instructions on how to do so are included in the documentation for
- :class:`PyLoader`. Do note that this solution will not support
- sourceless/bytecode-only loading; only source *and* bytecode loading.
-
- .. versionchanged:: 3.3
- Updated to parse (but not use) the new source size field in bytecode
- files when reading and to write out the field properly when writing.
-
- .. method:: source_mtime(fullname)
-
- An abstract method which returns the modification time for the source
- code of the specified module. The modification time should be an
- integer. If there is no source code, return ``None``. If the
- module cannot be found then :exc:`ImportError` is raised.
-
- .. method:: bytecode_path(fullname)
-
- An abstract method which returns the path to the bytecode for the
- specified module, if it exists. It returns ``None``
- if no bytecode exists (yet).
- Raises :exc:`ImportError` if the loader knows it cannot handle the
- module.
-
- .. method:: get_filename(fullname)
-
- A concrete implementation of
- :meth:`ExecutionLoader.get_filename` that relies on
- :meth:`PyLoader.source_path` and :meth:`bytecode_path`.
- If :meth:`source_path` returns a path, then that value is returned.
- Else if :meth:`bytecode_path` returns a path, that path will be
- returned. If a path is not available from both methods,
- :exc:`ImportError` is raised.
-
- .. method:: write_bytecode(fullname, bytecode)
-
- An abstract method which has the loader write *bytecode* for future
- use. If the bytecode is written, return ``True``. Return
- ``False`` if the bytecode could not be written. This method
- should not be called if :data:`sys.dont_write_bytecode` is true.
- The *bytecode* argument should be a bytes string or bytes array.
-
-
:mod:`importlib.machinery` -- Importers and path hooks
------------------------------------------------------
@@ -642,6 +738,10 @@ find and load modules.
Only class methods are defined by this class to alleviate the need for
instantiation.
+ .. note::
+ Due to limitations in the extension module C-API, for now
+ BuiltinImporter does not implement :meth:`Loader.exec_module`.
+
.. class:: FrozenImporter
@@ -672,24 +772,37 @@ find and load modules.
Only class methods are defined by this class to alleviate the need for
instantiation.
+ .. classmethod:: find_spec(fullname, path=None, target=None)
+
+ Class method that attempts to find a :term:`spec <module spec>`
+ for the module specified by *fullname* on :data:`sys.path` or, if
+ defined, on *path*. For each path entry that is searched,
+ :data:`sys.path_importer_cache` is checked. If a non-false object
+ is found then it is used as the :term:`path entry finder` to look
+ for the module being searched for. If no entry is found in
+ :data:`sys.path_importer_cache`, then :data:`sys.path_hooks` is
+ searched for a finder for the path entry and, if found, is stored
+ in :data:`sys.path_importer_cache` along with being queried about
+ the module. If no finder is ever found then ``None`` is both
+ stored in the cache and returned.
+
+ .. versionadded:: 3.4
+
.. classmethod:: find_module(fullname, path=None)
- Class method that attempts to find a :term:`loader` for the module
- specified by *fullname* on :data:`sys.path` or, if defined, on
- *path*. For each path entry that is searched,
- :data:`sys.path_importer_cache` is checked. If a non-false object is
- found then it is used as the :term:`path entry finder` to look for the
- module being searched for. If no entry is found in
- :data:`sys.path_importer_cache`, then :data:`sys.path_hooks` is
- searched for a finder for the path entry and, if found, is stored in
- :data:`sys.path_importer_cache` along with being queried about the
- module. If no finder is ever found then ``None`` is both stored in
- the cache and returned.
+ A legacy wrapper around :meth:`find_spec`.
+
+ .. deprecated:: 3.4
+ Use :meth:`find_spec` instead.
.. classmethod:: invalidate_caches()
- Calls :meth:`importlib.abc.PathEntryFinder.invalidate_caches` on all
- finders stored in :attr:`sys.path_importer_cache`.
+ Calls :meth:`importlib.abc.PathEntryFinder.invalidate_caches` on all
+ finders stored in :attr:`sys.path_importer_cache`.
+
+ .. versionchanged:: 3.4
+ Calls objects in :data:`sys.path_hooks` with the current working
+ directory for ``''`` (i.e. the empty string).
.. class:: FileFinder(path, \*loader_details)
@@ -721,6 +834,12 @@ find and load modules.
The path the finder will search in.
+ .. method:: find_spec(fullname, target=None)
+
+ Attempt to find the spec to handle *fullname* within :attr:`path`.
+
+ .. versionadded:: 3.4
+
.. method:: find_loader(fullname)
Attempt to find the loader to handle *fullname* within :attr:`path`.
@@ -805,7 +924,7 @@ find and load modules.
.. class:: ExtensionFileLoader(fullname, path)
- A concrete implementation of :class:`importlib.abc.InspectLoader` for
+ A concrete implementation of :class:`importlib.abc.ExecutionLoader` for
extension modules.
The *fullname* argument specifies the name of the module the loader is to
@@ -826,6 +945,10 @@ find and load modules.
Loads the extension module if and only if *fullname* is the same as
:attr:`name` or is ``None``.
+ .. note::
+ Due to limitations in the extension module C-API, for now
+ ExtensionFileLoader does not implement :meth:`Loader.exec_module`.
+
.. method:: is_package(fullname)
Returns ``True`` if the file path points to a package's ``__init__``
@@ -839,6 +962,70 @@ find and load modules.
Returns ``None`` as extension modules do not have source code.
+ .. method:: get_filename(fullname)
+
+ Returns :attr:`path`.
+
+ .. versionadded:: 3.4
+
+
+.. class:: ModuleSpec(name, loader, *, origin=None, loader_state=None, is_package=None)
+
+ A specification for a module's import-system-related state.
+
+ .. versionadded:: 3.4
+
+ .. attribute:: name
+
+ (``__name__``)
+
+ A string for the fully-qualified name of the module.
+
+ .. attribute:: loader
+
+ (``__loader__``)
+
+ The loader to use for loading. For namespace packages this should be
+ set to None.
+
+ .. attribute:: origin
+
+ (``__file__``)
+
+ Name of the place from which the module is loaded, e.g. "builtin" for
+ built-in modules and the filename for modules loaded from source.
+ Normally "origin" should be set, but it may be None (the default)
+ which indicates it is unspecified.
+
+ .. attribute:: submodule_search_locations
+
+ (``__path__``)
+
+ List of strings for where to find submodules, if a package (None
+ otherwise).
+
+ .. attribute:: loader_state
+
+ Container of extra module-specific data for use during loading (or
+ None).
+
+ .. attribute:: cached
+
+ (``__cached__``)
+
+ String for where the compiled module should be stored (or None).
+
+ .. attribute:: parent
+
+ (``__package__``)
+
+ (Read-only) Fully-qualified name of the package to which the module
+ belongs as a submodule (or None).
+
+ .. attribute:: has_location
+
+ Boolean indicating whether or not the module's "origin"
+ attribute refers to a loadable location.
:mod:`importlib.util` -- Utility code for importers
---------------------------------------------------
@@ -849,6 +1036,51 @@ find and load modules.
This module contains the various objects that help in the construction of
an :term:`importer`.
+.. attribute:: MAGIC_NUMBER
+
+ The bytes which represent the bytecode version number. If you need help with
+ loading/writing bytecode then consider :class:`importlib.abc.SourceLoader`.
+
+ .. versionadded:: 3.4
+
+.. function:: cache_from_source(path, debug_override=None)
+
+ Return the :pep:`3147` path to the byte-compiled file associated with the
+ source *path*. For example, if *path* is ``/foo/bar/baz.py`` the return
+ value would be ``/foo/bar/__pycache__/baz.cpython-32.pyc`` for Python 3.2.
+ The ``cpython-32`` string comes from the current magic tag (see
+ :func:`get_tag`; if :attr:`sys.implementation.cache_tag` is not defined then
+ :exc:`NotImplementedError` will be raised). The returned path will end in
+ ``.pyc`` when ``__debug__`` is ``True`` or ``.pyo`` for an optimized Python
+ (i.e. ``__debug__`` is ``False``). By passing in ``True`` or ``False`` for
+ *debug_override* you can override the system's value for ``__debug__`` for
+ extension selection.
+
+ *path* need not exist.
+
+ .. versionadded:: 3.4
+
+
+.. function:: source_from_cache(path)
+
+ Given the *path* to a :pep:`3147` file name, return the associated source code
+ file path. For example, if *path* is
+ ``/foo/bar/__pycache__/baz.cpython-32.pyc`` the returned path would be
+ ``/foo/bar/baz.py``. *path* need not exist, however if it does not conform
+ to :pep:`3147` format, a ``ValueError`` is raised. If
+ :attr:`sys.implementation.cache_tag` is not defined,
+ :exc:`NotImplementedError` is raised.
+
+ .. versionadded:: 3.4
+
+.. function:: decode_source(source_bytes)
+
+ Decode the given bytes representing source code and return it as a string
+ with universal newlines (as required by
+ :meth:`importlib.abc.InspectLoader.get_source`).
+
+ .. versionadded:: 3.4
+
.. function:: resolve_name(name, package)
Resolve a relative module name to an absolute one.
@@ -865,9 +1097,25 @@ an :term:`importer`.
.. versionadded:: 3.3
+.. function:: find_spec(name, package=None)
+
+ Find the :term:`spec <module spec>` for a module, optionally relative to
+ the specified **package** name. If the module is in :attr:`sys.modules`,
+ then ``sys.modules[name].__spec__`` is returned (unless the spec would be
+ ``None`` or is not set, in which case :exc:`ValueError` is raised).
+ Otherwise a search using :attr:`sys.meta_path` is done. ``None`` is
+ returned if no spec is found.
+
+ If **name** is for a submodule (contains a dot), the parent module is
+ automatically imported.
+
+ **name** and **package** work the same as for :func:`import_module`.
+
+ .. versionadded:: 3.4
+
.. decorator:: module_for_loader
- A :term:`decorator` for a :term:`loader` method,
+ A :term:`decorator` for :meth:`importlib.abc.Loader.load_module`
to handle selecting the proper
module object to load with. The decorated method is expected to have a call
signature taking two positional arguments
@@ -878,55 +1126,68 @@ an :term:`importer`.
The decorated method will take in the **name** of the module to be loaded
as expected for a :term:`loader`. If the module is not found in
- :data:`sys.modules` then a new one is constructed with its
- :attr:`__name__` attribute set to **name**, :attr:`__loader__` set to
- **self**, and :attr:`__package__` set if
- :meth:`importlib.abc.InspectLoader.is_package` is defined for **self** and
- does not raise :exc:`ImportError` for **name**. If a new module is not
- needed then the module found in :data:`sys.modules` will be passed into the
- method.
+ :data:`sys.modules` then a new one is constructed. Regardless of where the
+ module came from, :attr:`__loader__` set to **self** and :attr:`__package__`
+ is set based on what :meth:`importlib.abc.InspectLoader.is_package` returns
+ (if available). These attributes are set unconditionally to support
+ reloading.
If an exception is raised by the decorated method and a module was added to
- :data:`sys.modules` it will be removed to prevent a partially initialized
- module from being in left in :data:`sys.modules`. If the module was already
- in :data:`sys.modules` then it is left alone.
-
- Use of this decorator handles all the details of which module object a
- loader should initialize as specified by :pep:`302` as best as possible.
+ :data:`sys.modules`, then the module will be removed to prevent a partially
+ initialized module from being in left in :data:`sys.modules`. If the module
+ was already in :data:`sys.modules` then it is left alone.
.. versionchanged:: 3.3
:attr:`__loader__` and :attr:`__package__` are automatically set
(when possible).
-.. decorator:: set_loader
+ .. versionchanged:: 3.4
+ Set :attr:`__name__`, :attr:`__loader__` :attr:`__package__`
+ unconditionally to support reloading.
- A :term:`decorator` for a :term:`loader` method,
- to set the :attr:`__loader__`
- attribute on loaded modules. If the attribute is already set the decorator
- does nothing. It is assumed that the first positional argument to the
- wrapped method (i.e. ``self``) is what :attr:`__loader__` should be set to.
+ .. deprecated:: 3.4
+ The import machinery now directly performs all the functionality
+ provided by this function.
- .. note::
+.. decorator:: set_loader
- It is recommended that :func:`module_for_loader` be used over this
- decorator as it subsumes this functionality.
+ A :term:`decorator` for :meth:`importlib.abc.Loader.load_module`
+ to set the :attr:`__loader__`
+ attribute on the returned module. If the attribute is already set the
+ decorator does nothing. It is assumed that the first positional argument to
+ the wrapped method (i.e. ``self``) is what :attr:`__loader__` should be set
+ to.
+ .. versionchanged:: 3.4
+ Set ``__loader__`` if set to ``None``, as if the attribute does not
+ exist.
+
+ .. deprecated:: 3.4
+ The import machinery takes care of this automatically.
.. decorator:: set_package
- A :term:`decorator` for a :term:`loader` to set the :attr:`__package__`
- attribute on the module returned by the loader. If :attr:`__package__` is
- set and has a value other than ``None`` it will not be changed.
- Note that the module returned by the loader is what has the attribute
- set on and not the module found in :data:`sys.modules`.
+ A :term:`decorator` for :meth:`importlib.abc.Loader.load_module` to set the :attr:`__package__` attribute on the returned module. If :attr:`__package__`
+ is set and has a value other than ``None`` it will not be changed.
+
+ .. deprecated:: 3.4
+ The import machinery takes care of this automatically.
+
+.. function:: spec_from_loader(name, loader, *, origin=None, is_package=None)
+
+ A factory function for creating a :class:`ModuleSpec` instance based
+ on a loader. The parameters have the same meaning as they do for
+ ModuleSpec. The function uses available :term:`loader` APIs, such as
+ :meth:`InspectLoader.is_package`, to fill in any missing
+ information on the spec.
+
+ .. versionadded:: 3.4
- Reliance on this decorator is discouraged when it is possible to set
- :attr:`__package__` before importing. By
- setting it beforehand the code for the module is executed with the
- attribute set and thus can be used by global level code during
- initialization.
+.. function:: spec_from_file_location(name, location, *, loader=None, submodule_search_locations=None)
- .. note::
+ A factory function for creating a :class:`ModuleSpec` instance based
+ on the path to a file. Missing information will be filled in on the
+ spec by making use of loader APIs and by the implication that the
+ module will be file-based.
- It is recommended that :func:`module_for_loader` be used over this
- decorator as it subsumes this functionality.
+ .. versionadded:: 3.4
diff --git a/Doc/library/index.rst b/Doc/library/index.rst
index 1b25c6e..81289a5 100644
--- a/Doc/library/index.rst
+++ b/Doc/library/index.rst
@@ -65,6 +65,7 @@ the `Python Package Index <http://pypi.python.org/pypi>`_.
tk.rst
development.rst
debug.rst
+ distribution.rst
python.rst
custominterp.rst
modules.rst
diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst
index d4cf905..0c08712 100644
--- a/Doc/library/inspect.rst
+++ b/Doc/library/inspect.rst
@@ -69,7 +69,12 @@ attributes:
| | | :term:`bytecode` |
+-----------+-----------------+---------------------------+
| | __defaults__ | tuple of any default |
-| | | values for arguments |
+| | | values for positional or |
+| | | keyword parameters |
++-----------+-----------------+---------------------------+
+| | __kwdefaults__ | mapping of any default |
+| | | values for keyword-only |
+| | | parameters |
+-----------+-----------------+---------------------------+
| | __globals__ | global namespace in which |
| | | this function was defined |
@@ -173,8 +178,9 @@ attributes:
.. note::
- :func:`getmembers` does not return metaclass attributes when the argument
- is a class (this behavior is inherited from the :func:`dir` function).
+ :func:`getmembers` will only return class attributes defined in the
+ metaclass when the argument is a class and those attributes have been
+ listed in the metaclass' custom :meth:`__dir__`.
.. function:: getmoduleinfo(path)
@@ -428,11 +434,14 @@ function.
Accepts a wide range of python callables, from plain functions and classes to
:func:`functools.partial` objects.
+ Raises :exc:`ValueError` if no signature can be provided, and
+ :exc:`TypeError` if that type of object is not supported.
+
.. note::
Some callables may not be introspectable in certain implementations of
- Python. For example, in CPython, built-in functions defined in C provide
- no metadata about their arguments.
+ Python. For example, in CPython, some built-in functions defined in
+ C provide no metadata about their arguments.
.. class:: Signature(parameters=None, \*, return_annotation=Signature.empty)
@@ -510,9 +519,8 @@ function.
.. attribute:: Parameter.name
- The name of the parameter as a string. Must be a valid python identifier
- name (with the exception of ``POSITIONAL_ONLY`` parameters, which can have
- it set to ``None``).
+ The name of the parameter as a string. The name must be a valid
+ Python identifier.
.. attribute:: Parameter.default
@@ -596,6 +604,10 @@ function.
>>> str(param.replace(default=Parameter.empty, annotation='spam'))
"foo:'spam'"
+ .. versionchanged:: 3.4
+ In Python 3.3 Parameter objects were allowed to have ``name`` set
+ to ``None`` if their ``kind`` was set to ``POSITIONAL_ONLY``.
+ This is no longer permitted.
.. class:: BoundArguments
@@ -717,6 +729,11 @@ Classes and functions
Consider using the new :ref:`Signature Object <inspect-signature-object>`
interface, which provides a better way of introspecting functions.
+ .. versionchanged:: 3.4
+ This function is now based on :func:`signature`, but still ignores
+ ``__wrapped__`` attributes and includes the already bound first
+ parameter in the signature output for bound methods.
+
.. function:: getargvalues(frame)
@@ -806,6 +823,23 @@ Classes and functions
.. versionadded:: 3.3
+.. function:: unwrap(func, *, stop=None)
+
+ Get the object wrapped by *func*. It follows the chain of :attr:`__wrapped__`
+ attributes returning the last object in the chain.
+
+ *stop* is an optional callback accepting an object in the wrapper chain
+ as its sole argument that allows the unwrapping to be terminated early if
+ the callback returns a true value. If the callback never returns a true
+ value, the last object in the chain is returned as usual. For example,
+ :func:`signature` uses this to stop unwrapping if any object in the
+ chain has a ``__signature__`` attribute defined.
+
+ :exc:`ValueError` is raised if a cycle is encountered.
+
+ .. versionadded:: 3.4
+
+
.. _inspect-stack:
The interpreter stack
@@ -838,6 +872,10 @@ index of the current line within that list.
finally:
del frame
+ If you want to keep the frame around (for example to print a traceback
+ later), you can also break reference cycles by using the
+ :meth:`frame.clear` method.
+
The optional *context* argument supported by most of these functions specifies
the number of lines of context to return, which are centered around the current
line.
@@ -994,3 +1032,22 @@ updated as expected:
return an empty dictionary.
.. versionadded:: 3.3
+
+
+.. _inspect-module-cli:
+
+Command Line Interface
+----------------------
+
+The :mod:`inspect` module also provides a basic introspection capability
+from the command line.
+
+.. program:: inspect
+
+By default, accepts the name of a module and prints the source of that
+module. A class or function within the module can be printed instead by
+appended a colon and the qualified name of the target object.
+
+.. cmdoption:: --details
+
+ Print information about the specified object rather than the source code
diff --git a/Doc/library/io.rst b/Doc/library/io.rst
index 716767f..79f65e0 100644
--- a/Doc/library/io.rst
+++ b/Doc/library/io.rst
@@ -283,10 +283,10 @@ I/O Base Classes
Return ``True`` if the stream can be read from. If ``False``, :meth:`read`
will raise :exc:`OSError`.
- .. method:: readline(limit=-1)
+ .. method:: readline(size=-1)
- Read and return one line from the stream. If *limit* is specified, at
- most *limit* bytes will be read.
+ Read and return one line from the stream. If *size* is specified, at
+ most *size* bytes will be read.
The line terminator is always ``b'\n'`` for binary files; for text files,
the *newlines* argument to :func:`open` can be used to select the line
@@ -366,14 +366,14 @@ I/O Base Classes
In addition to the attributes and methods from :class:`IOBase`,
:class:`RawIOBase` provides the following methods:
- .. method:: read(n=-1)
+ .. method:: read(size=-1)
- Read up to *n* bytes from the object and return them. As a convenience,
- if *n* is unspecified or -1, :meth:`readall` is called. Otherwise,
- only one system call is ever made. Fewer than *n* bytes may be
- returned if the operating system call returns fewer than *n* bytes.
+ Read up to *size* bytes from the object and return them. As a convenience,
+ if *size* is unspecified or -1, :meth:`readall` is called. Otherwise,
+ only one system call is ever made. Fewer than *size* bytes may be
+ returned if the operating system call returns fewer than *size* bytes.
- If 0 bytes are returned, and *n* was not 0, this indicates end of file.
+ If 0 bytes are returned, and *size* was not 0, this indicates end of file.
If the object is in non-blocking mode and no bytes are available,
``None`` is returned.
@@ -442,10 +442,10 @@ I/O Base Classes
.. versionadded:: 3.1
- .. method:: read(n=-1)
+ .. method:: read(size=-1)
- Read and return up to *n* bytes. If the argument is omitted, ``None``, or
- negative, data is read and returned until EOF is reached. An empty
+ Read and return up to *size* bytes. If the argument is omitted, ``None``,
+ or negative, data is read and returned until EOF is reached. An empty
:class:`bytes` object is returned if the stream is already at EOF.
If the argument is positive, and the underlying raw stream is not
@@ -457,9 +457,9 @@ I/O Base Classes
A :exc:`BlockingIOError` is raised if the underlying raw stream is in
non blocking-mode, and has no data available at the moment.
- .. method:: read1(n=-1)
+ .. method:: read1(size=-1)
- Read and return up to *n* bytes, with at most one call to the underlying
+ Read and return up to *size* bytes, with at most one call to the underlying
raw stream's :meth:`~RawIOBase.read` method. This can be useful if you
are implementing your own buffering on top of a :class:`BufferedIOBase`
object.
@@ -522,6 +522,8 @@ Raw File I/O
:mod:`os.open` as *opener* results in functionality similar to passing
``None``).
+ The newly created file is :ref:`non-inheritable <fd_inheritance>`.
+
See the :func:`open` built-in function for examples on using the *opener*
parameter.
@@ -529,6 +531,9 @@ Raw File I/O
The *opener* parameter was added.
The ``'x'`` mode was added.
+ .. versionchanged:: 3.4
+ The file is now non-inheritable.
+
In addition to the attributes and methods from :class:`IOBase` and
:class:`RawIOBase`, :class:`FileIO` provides the following data
attributes:
@@ -601,21 +606,21 @@ than raw I/O does.
:class:`BufferedReader` provides or overrides these methods in addition to
those from :class:`BufferedIOBase` and :class:`IOBase`:
- .. method:: peek([n])
+ .. method:: peek([size])
Return bytes from the stream without advancing the position. At most one
single read on the raw stream is done to satisfy the call. The number of
bytes returned may be less or more than requested.
- .. method:: read([n])
+ .. method:: read([size])
- Read and return *n* bytes, or if *n* is not given or negative, until EOF
- or if the read call would block in non-blocking mode.
+ Read and return *size* bytes, or if *size* is not given or negative, until
+ EOF or if the read call would block in non-blocking mode.
- .. method:: read1(n)
+ .. method:: read1(size)
- Read and return up to *n* bytes with only one call on the raw stream. If
- at least one byte is buffered, only buffered bytes are returned.
+ Read and return up to *size* bytes with only one call on the raw stream.
+ If at least one byte is buffered, only buffered bytes are returned.
Otherwise, one raw stream read call is made.
@@ -735,17 +740,17 @@ Text I/O
.. versionadded:: 3.1
- .. method:: read(n)
+ .. method:: read(size)
- Read and return at most *n* characters from the stream as a single
- :class:`str`. If *n* is negative or ``None``, reads until EOF.
+ Read and return at most *size* characters from the stream as a single
+ :class:`str`. If *size* is negative or ``None``, reads until EOF.
- .. method:: readline(limit=-1)
+ .. method:: readline(size=-1)
Read until newline or EOF and return a single ``str``. If the stream is
already at EOF, an empty string is returned.
- If *limit* is specified, at most *limit* characters will be read.
+ If *size* is specified, at most *size* characters will be read.
.. method:: seek(offset, whence=SEEK_SET)
diff --git a/Doc/library/ipaddress.rst b/Doc/library/ipaddress.rst
index 8eac92f..9625e71 100644
--- a/Doc/library/ipaddress.rst
+++ b/Doc/library/ipaddress.rst
@@ -9,13 +9,6 @@
--------------
-.. note::
-
- The ``ipaddress`` module has been included in the standard library on a
- :term:`provisional basis <provisional package>`. Backwards incompatible
- changes (up to and including removal of the package) may occur if deemed
- necessary by the core developers.
-
:mod:`ipaddress` provides the capabilities to create, manipulate and
operate on IPv4 and IPv6 addresses and networks.
@@ -161,7 +154,16 @@ write code that handles both IP versions correctly.
.. attribute:: is_private
``True`` if the address is allocated for private networks. See
- :RFC:`1918` (for IPv4) or :RFC:`4193` (for IPv6).
+ iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
+ (for IPv6).
+
+ .. attribute:: is_global
+
+ ``True`` if the address is allocated for public networks. See
+ iana-ipv4-special-registry_ (for IPv4) or iana-ipv6-special-registry_
+ (for IPv6).
+
+ .. versionadded:: 3.4
.. attribute:: is_unspecified
@@ -182,6 +184,9 @@ write code that handles both IP versions correctly.
``True`` if the address is reserved for link-local usage. See
:RFC:`3927`.
+.. _iana-ipv4-special-registry: http://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
+.. _iana-ipv6-special-registry: http://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
+
.. class:: IPv6Address(address)
@@ -216,18 +221,23 @@ write code that handles both IP versions correctly.
The long form of the address representation, with all leading zeroes and
groups consisting entirely of zeroes included.
+
+ For the following attributes, see the corresponding documention of the
+ :class:`IPv4Address` class:
+
.. attribute:: packed
.. attribute:: version
.. attribute:: max_prefixlen
.. attribute:: is_multicast
.. attribute:: is_private
+ .. attribute:: is_global
.. attribute:: is_unspecified
.. attribute:: is_reserved
.. attribute:: is_loopback
.. attribute:: is_link_local
- Refer to the corresponding attribute documentation in
- :class:`IPv4Address`
+ .. versionadded:: 3.4
+ is_global
.. attribute:: is_site_local
diff --git a/Doc/library/ipc.rst b/Doc/library/ipc.rst
index 91ec693..6b17563 100644
--- a/Doc/library/ipc.rst
+++ b/Doc/library/ipc.rst
@@ -9,7 +9,7 @@ to communicate.
Some modules only work for two processes that are on the same machine, e.g.
:mod:`signal` and :mod:`mmap`. Other modules support networking protocols
-that two or more processes can used to communicate across machines.
+that two or more processes can use to communicate across machines.
The list of modules described in this chapter is:
@@ -18,6 +18,9 @@ The list of modules described in this chapter is:
socket.rst
ssl.rst
+ select.rst
+ selectors.rst
+ asyncio.rst
asyncore.rst
asynchat.rst
signal.rst
diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst
index 5d3e50a..f489535 100644
--- a/Doc/library/itertools.rst
+++ b/Doc/library/itertools.rst
@@ -784,6 +784,19 @@ which incur interpreter overhead.
except exception:
pass
+ def first_true(iterable, default=False, pred=None):
+ """Returns the first true value in the iterable.
+
+ If no true value is found, returns *default*
+
+ If *pred* is not None, returns the first item
+ for which pred(item) is true.
+
+ """
+ # first_true([a,b,c], x) --> a or b or c or x
+ # first_true([a,b], x, f) --> a if f(a) else b if f(b) else x
+ return next(filter(pred, iterable), default)
+
def random_product(*args, repeat=1):
"Random selection from itertools.product(*args, **kwds)"
pools = [tuple(pool) for pool in args] * repeat
diff --git a/Doc/library/json.rst b/Doc/library/json.rst
index f652039..5d97ee8 100644
--- a/Doc/library/json.rst
+++ b/Doc/library/json.rst
@@ -42,8 +42,7 @@ Compact encoding::
Pretty printing::
>>> import json
- >>> print(json.dumps({'4': 5, '6': 7}, sort_keys=True,
- ... indent=4, separators=(',', ': ')))
+ >>> print(json.dumps({'4': 5, '6': 7}, sort_keys=True, indent=4))
{
"4": 5,
"6": 7
@@ -158,15 +157,13 @@ Basic Usage
.. versionchanged:: 3.2
Allow strings for *indent* in addition to integers.
- .. note::
-
- Since the default item separator is ``', '``, the output might include
- trailing whitespace when *indent* is specified. You can use
- ``separators=(',', ': ')`` to avoid this.
+ If specified, *separators* should be an ``(item_separator, key_separator)``
+ tuple. The default is ``(', ', ': ')`` if *indent* is ``None`` and
+ ``(',', ': ')`` otherwise. To get the most compact JSON representation,
+ you should specify ``(',', ':')`` to eliminate whitespace.
- If *separators* is an ``(item_separator, dict_separator)`` tuple, then it
- will be used instead of the default ``(', ', ': ')`` separators. ``(',',
- ':')`` is the most compact JSON representation.
+ .. versionchanged:: 3.4
+ Use ``(',', ': ')`` as default if *indent* is not ``None``.
*default(obj)* is a function that should return a serializable version of
*obj* or raise :exc:`TypeError`. The default simply raises :exc:`TypeError`.
@@ -248,6 +245,8 @@ Basic Usage
kwarg; otherwise :class:`JSONDecoder` is used. Additional keyword arguments
will be passed to the constructor of the class.
+ If the data being deserialized is not a valid JSON document, a
+ :exc:`ValueError` will be raised.
.. function:: loads(s, encoding=None, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)
@@ -257,6 +256,8 @@ Basic Usage
The other arguments have the same meaning as in :func:`load`, except
*encoding* which is ignored and deprecated.
+ If the data being deserialized is not a valid JSON document, a
+ :exc:`ValueError` will be raised.
Encoders and Decoders
---------------------
@@ -354,23 +355,26 @@ Encoders and Decoders
.. _py-to-json-table:
- +-------------------+---------------+
- | Python | JSON |
- +===================+===============+
- | dict | object |
- +-------------------+---------------+
- | list, tuple | array |
- +-------------------+---------------+
- | str | string |
- +-------------------+---------------+
- | int, float | number |
- +-------------------+---------------+
- | True | true |
- +-------------------+---------------+
- | False | false |
- +-------------------+---------------+
- | None | null |
- +-------------------+---------------+
+ +----------------------------------------+---------------+
+ | Python | JSON |
+ +========================================+===============+
+ | dict | object |
+ +----------------------------------------+---------------+
+ | list, tuple | array |
+ +----------------------------------------+---------------+
+ | str | string |
+ +----------------------------------------+---------------+
+ | int, float, int- & float-derived Enums | number |
+ +----------------------------------------+---------------+
+ | True | true |
+ +----------------------------------------+---------------+
+ | False | false |
+ +----------------------------------------+---------------+
+ | None | null |
+ +----------------------------------------+---------------+
+
+ .. versionchanged:: 3.4
+ Added support for int- and float-derived Enum classes.
To extend this to recognize other objects, subclass and implement a
:meth:`default` method with another method that returns a serializable object
@@ -410,15 +414,13 @@ Encoders and Decoders
.. versionchanged:: 3.2
Allow strings for *indent* in addition to integers.
- .. note::
-
- Since the default item separator is ``', '``, the output might include
- trailing whitespace when *indent* is specified. You can use
- ``separators=(',', ': ')`` to avoid this.
-
If specified, *separators* should be an ``(item_separator, key_separator)``
- tuple. The default is ``(', ', ': ')``. To get the most compact JSON
- representation, you should specify ``(',', ':')`` to eliminate whitespace.
+ tuple. The default is ``(', ', ': ')`` if *indent* is ``None`` and
+ ``(',', ': ')`` otherwise. To get the most compact JSON representation,
+ you should specify ``(',', ':')`` to eliminate whitespace.
+
+ .. versionchanged:: 3.4
+ Use ``(',', ': ')`` as default if *indent* is not ``None``.
If specified, *default* is a function that gets called for objects that can't
otherwise be serialized. It should return a JSON encodable version of the
diff --git a/Doc/library/logging.config.rst b/Doc/library/logging.config.rst
index 303b4d8..171b0a3 100644
--- a/Doc/library/logging.config.rst
+++ b/Doc/library/logging.config.rst
@@ -80,12 +80,25 @@ in :mod:`logging` itself) and defining handlers which are declared either in
.. function:: fileConfig(fname, defaults=None, disable_existing_loggers=True)
- Reads the logging configuration from a :mod:`configparser`\-format file
- named *fname*. The format of the file should be as described in
- :ref:`logging-config-fileformat`. This function can be called several times
- from an application, allowing an end user to select from various pre-canned
- configurations (if the developer provides a mechanism to present the choices
- and load the chosen configuration).
+ Reads the logging configuration from a :mod:`configparser`\-format file. The
+ format of the file should be as described in
+ :ref:`logging-config-fileformat`.
+ This function can be called several times from an application, allowing an
+ end user to select from various pre-canned configurations (if the developer
+ provides a mechanism to present the choices and load the chosen
+ configuration).
+
+ :param fname: A filename, or a file-like object, or an instance derived
+ from :class:`~configparser.RawConfigParser`. If a
+ ``RawConfigParser``-derived instance is passed, it is used as
+ is. Otherwise, a :class:`~configparser.Configparser` is
+ instantiated, and the configuration read by it from the
+ object passed in ``fname``. If that has a :meth:`readline`
+ method, it is assumed to be a file-like object and read using
+ :meth:`~configparser.ConfigParser.read_file`; otherwise,
+ it is assumed to be a filename and passed to
+ :meth:`~configparser.ConfigParser.read`.
+
:param defaults: Defaults to be passed to the ConfigParser can be specified
in this argument.
@@ -99,8 +112,17 @@ in :mod:`logging` itself) and defining handlers which are declared either in
their ancestors are explicitly named in the
logging configuration.
+ .. versionchanged:: 3.4
+ An instance of a subclass of :class:`~configparser.RawConfigParser` is
+ now accepted as a value for ``fname``. This facilitates:
+
+ * Use of a configuration file where logging configuration is just part
+ of the overall application configuration.
+ * Use of a configuration read from a file, and then modified by the using
+ application (e.g. based on command-line parameters or other aspects
+ of the runtime environment) before being passed to ``fileConfig``.
-.. function:: listen(port=DEFAULT_LOGGING_CONFIG_PORT)
+.. function:: listen(port=DEFAULT_LOGGING_CONFIG_PORT, verify=None)
Starts up a socket server on the specified port, and listens for new
configurations. If no port is specified, the module's default
@@ -111,6 +133,17 @@ in :mod:`logging` itself) and defining handlers which are declared either in
:meth:`~threading.Thread.join` when appropriate. To stop the server,
call :func:`stopListening`.
+ The ``verify`` argument, if specified, should be a callable which should
+ verify whether bytes received across the socket are valid and should be
+ processed. This could be done by encrypting and/or signing what is sent
+ across the socket, such that the ``verify`` callable can perform
+ signature verification and/or decryption. The ``verify`` callable is called
+ with a single argument - the bytes received across the socket - and should
+ return the bytes to be processed, or None to indicate that the bytes should
+ be discarded. The returned bytes could be the same as the passed in bytes
+ (e.g. when only verification is done), or they could be completely different
+ (perhaps if decryption were performed).
+
To send a configuration to the socket, read in the configuration file and
send it to the socket as a string of bytes preceded by a four-byte length
string packed in binary using ``struct.pack('>L', n)``.
@@ -129,7 +162,12 @@ in :mod:`logging` itself) and defining handlers which are declared either in
:func:`listen` socket and sending a configuration which runs whatever
code the attacker wants to have executed in the victim's process. This is
especially easy to do if the default port is used, but not hard even if a
- different port is used).
+ different port is used). To avoid the risk of this happening, use the
+ ``verify`` argument to :func:`listen` to prevent unrecognised
+ configurations from being applied.
+
+ .. versionchanged:: 3.4.
+ The ``verify`` argument was added.
.. function:: stopListening()
diff --git a/Doc/library/logging.handlers.rst b/Doc/library/logging.handlers.rst
index 415e397..315c168 100644
--- a/Doc/library/logging.handlers.rst
+++ b/Doc/library/logging.handlers.rst
@@ -300,7 +300,7 @@ The :class:`TimedRotatingFileHandler` class, located in the
timed intervals.
-.. class:: TimedRotatingFileHandler(filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False)
+.. class:: TimedRotatingFileHandler(filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False, atTime=None)
Returns a new instance of the :class:`TimedRotatingFileHandler` class. The
specified file is opened and used as the stream for logging. On rotating it also
@@ -350,6 +350,12 @@ timed intervals.
If *delay* is true, then file opening is deferred until the first call to
:meth:`emit`.
+ If *atTime* is not ``None``, it must be a ``datetime.time`` instance which
+ specifies the time of day when rollover occurs, for the cases where rollover
+ is set to happen "at midnight" or "on a particular weekday".
+
+ .. versionchanged:: 3.4
+ *atTime* parameter was added.
.. method:: doRollover()
@@ -375,6 +381,9 @@ sends logging output to a network socket. The base class uses a TCP socket.
Returns a new instance of the :class:`SocketHandler` class intended to
communicate with a remote machine whose address is given by *host* and *port*.
+ .. versionchanged:: 3.4
+ If ``port`` is specified as ``None``, a Unix domain socket is created
+ using the value in ``host`` - otherwise, a TCP socket is created.
.. method:: close()
@@ -460,6 +469,9 @@ over UDP sockets.
Returns a new instance of the :class:`DatagramHandler` class intended to
communicate with a remote machine whose address is given by *host* and *port*.
+ .. versionchanged:: 3.4
+ If ``port`` is specified as ``None``, a Unix domain socket is created
+ using the value in ``host`` - otherwise, a TCP socket is created.
.. method:: emit()
diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst
index 8d093e1..c0a479e 100644
--- a/Doc/library/logging.rst
+++ b/Doc/library/logging.rst
@@ -250,7 +250,7 @@ is the module's name in the Python package namespace.
interpreted as for :meth:`debug`.
-.. method:: Logger.exception(msg, *args)
+.. method:: Logger.exception(msg, *args, **kwargs)
Logs a message with level :const:`ERROR` on this logger. The arguments are
interpreted as for :meth:`debug`. Exception info is added to the logging
@@ -992,7 +992,7 @@ functions.
are interpreted as for :func:`debug`.
-.. function:: exception(msg, *args)
+.. function:: exception(msg, *args, **kwargs)
Logs a message with level :const:`ERROR` on the root logger. The arguments are
interpreted as for :func:`debug`. Exception info is added to the logging
diff --git a/Doc/library/lzma.rst b/Doc/library/lzma.rst
index 07b69eb..b71051d 100644
--- a/Doc/library/lzma.rst
+++ b/Doc/library/lzma.rst
@@ -39,8 +39,8 @@ Reading and writing compressed files
opened, or it can be an existing file object to read from or write to.
The *mode* argument can be any of ``"r"``, ``"rb"``, ``"w"``, ``"wb"``,
- ``"a"`` or ``"ab"`` for binary mode, or ``"rt"``, ``"wt"``, or ``"at"`` for
- text mode. The default is ``"rb"``.
+ ``"x"``, ``"xb"``, ``"a"`` or ``"ab"`` for binary mode, or ``"rt"``,
+ ``"wt"``, ``"xt"``, or ``"at"`` for text mode. The default is ``"rb"``.
When opening a file for reading, the *format* and *filters* arguments have
the same meanings as for :class:`LZMADecompressor`. In this case, the *check*
@@ -57,6 +57,9 @@ Reading and writing compressed files
:class:`io.TextIOWrapper` instance with the specified encoding, error
handling behavior, and line ending(s).
+ .. versionchanged:: 3.4
+ Added support for the ``"x"``, ``"xb"`` and ``"xt"`` modes.
+
.. class:: LZMAFile(filename=None, mode="r", \*, format=None, check=-1, preset=None, filters=None)
@@ -69,8 +72,9 @@ Reading and writing compressed files
file will not be closed when the :class:`LZMAFile` is closed.
The *mode* argument can be either ``"r"`` for reading (default), ``"w"`` for
- overwriting, or ``"a"`` for appending. These can equivalently be given as
- ``"rb"``, ``"wb"``, and ``"ab"`` respectively.
+ overwriting, ``"x"`` for exclusive creation, or ``"a"`` for appending. These
+ can equivalently be given as ``"rb"``, ``"wb"``, ``"xb"`` and ``"ab"``
+ respectively.
If *filename* is a file object (rather than an actual file name), a mode of
``"w"`` does not truncate the file, and is instead equivalent to ``"a"``.
@@ -103,6 +107,9 @@ Reading and writing compressed files
file object (e.g. if the :class:`LZMAFile` was constructed by passing a
file object for *filename*).
+ .. versionchanged:: 3.4
+ Added support for the ``"x"`` and ``"xb"`` modes.
+
Compressing and decompressing data in memory
--------------------------------------------
diff --git a/Doc/library/marshal.rst b/Doc/library/marshal.rst
index 3b9e3d2..124eb61 100644
--- a/Doc/library/marshal.rst
+++ b/Doc/library/marshal.rst
@@ -40,10 +40,11 @@ this module. The following types are supported: booleans, integers, floating
point numbers, complex numbers, strings, bytes, bytearrays, tuples, lists, sets,
frozensets, dictionaries, and code objects, where it should be understood that
tuples, lists, sets, frozensets and dictionaries are only supported as long as
-the values contained therein are themselves supported; and recursive lists, sets
-and dictionaries should not be written (they will cause infinite loops). The
+the values contained therein are themselves supported.
singletons :const:`None`, :const:`Ellipsis` and :exc:`StopIteration` can also be
marshalled and unmarshalled.
+For format *version* lower than 3, recursive lists, sets and dictionaries cannot
+be written (see below).
There are functions that read/write files as well as functions operating on
strings.
@@ -103,7 +104,9 @@ In addition, the following constants are defined:
Indicates the format that the module uses. Version 0 is the historical
format, version 1 shares interned strings and version 2 uses a binary format
- for floating point numbers. The current version is 2.
+ for floating point numbers.
+ Version 3 adds support for object instancing and recursion.
+ The current version is 3.
.. rubric:: Footnotes
diff --git a/Doc/library/multiprocessing.rst b/Doc/library/multiprocessing.rst
index 5f50cf1..5fac730 100644
--- a/Doc/library/multiprocessing.rst
+++ b/Doc/library/multiprocessing.rst
@@ -93,11 +93,112 @@ To show the individual process IDs involved, here is an expanded example::
p.start()
p.join()
-For an explanation of why (on Windows) the ``if __name__ == '__main__'`` part is
+For an explanation of why the ``if __name__ == '__main__'`` part is
necessary, see :ref:`multiprocessing-programming`.
+Contexts and start methods
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. _multiprocessing-start-methods:
+
+Depending on the platform, :mod:`multiprocessing` supports three ways
+to start a process. These *start methods* are
+
+ *spawn*
+ The parent process starts a fresh python interpreter process. The
+ child process will only inherit those resources necessary to run
+ the process objects :meth:`~Process.run` method. In particular,
+ unnecessary file descriptors and handles from the parent process
+ will not be inherited. Starting a process using this method is
+ rather slow compared to using *fork* or *forkserver*.
+
+ Available on Unix and Windows. The default on Windows.
+
+ *fork*
+ The parent process uses :func:`os.fork` to fork the Python
+ interpreter. The child process, when it begins, is effectively
+ identical to the parent process. All resources of the parent are
+ inherited by the child process. Note that safely forking a
+ multithreaded process is problematic.
+
+ Available on Unix only. The default on Unix.
+
+ *forkserver*
+ When the program starts and selects the *forkserver* start method,
+ a server process is started. From then on, whenever a new process
+ is needed, the parent process connects to the server and requests
+ that it fork a new process. The fork server process is single
+ threaded so it is safe for it to use :func:`os.fork`. No
+ unnecessary resources are inherited.
+
+ Available on Unix platforms which support passing file descriptors
+ over Unix pipes.
+
+.. versionchanged:: 3.4
+ *spawn* added on all unix platforms, and *forkserver* added for
+ some unix platforms.
+ Child processes no longer inherit all of the parents inheritable
+ handles on Windows.
+
+On Unix using the *spawn* or *forkserver* start methods will also
+start a *semaphore tracker* process which tracks the unlinked named
+semaphores created by processes of the program. When all processes
+have exited the semaphore tracker unlinks any remaining semaphores.
+Usually there should be none, but if a process was killed by a signal
+there may some "leaked" semaphores. (Unlinking the named semaphores
+is a serious matter since the system allows only a limited number, and
+they will not be automatically unlinked until the next reboot.)
+
+To select a start method you use the :func:`set_start_method` in
+the ``if __name__ == '__main__'`` clause of the main module. For
+example::
+
+ import multiprocessing as mp
+
+ def foo(q):
+ q.put('hello')
+
+ if __name__ == '__main__':
+ mp.set_start_method('spawn')
+ q = mp.Queue()
+ p = mp.Process(target=foo, args=(q,))
+ p.start()
+ print(q.get())
+ p.join()
+
+:func:`set_start_method` should not be used more than once in the
+program.
+
+Alternatively, you can use :func:`get_context` to obtain a context
+object. Context objects have the same API as the multiprocessing
+module, and allow one to use multiple start methods in the same
+program. ::
+
+ import multiprocessing as mp
+
+ def foo(q):
+ q.put('hello')
+
+ if __name__ == '__main__':
+ ctx = mp.get_context('spawn')
+ q = ctx.Queue()
+ p = ctx.Process(target=foo, args=(q,))
+ p.start()
+ print(q.get())
+ p.join()
+
+Note that objects related to one context may not be compatible with
+processes for a different context. In particular, locks created using
+the *fork* context cannot be passed to a processes started using the
+*spawn* or *forkserver* start methods.
+
+A library which wants to use a particular start method should probably
+use :func:`get_context` to avoid interfering with the choice of the
+library user.
+
+
Exchanging objects between processes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -274,15 +375,31 @@ processes in a few different ways.
For example::
from multiprocessing import Pool
+ from time import sleep
def f(x):
return x*x
if __name__ == '__main__':
- with Pool(processes=4) as pool: # start 4 worker processes
- result = pool.apply_async(f, [10]) # evaluate "f(10)" asynchronously
- print(result.get(timeout=1)) # prints "100" unless your computer is *very* slow
- print(pool.map(f, range(10))) # prints "[0, 1, 4,..., 81]"
+ # start 4 worker processes
+ with Pool(processes=4) as pool:
+
+ # print "[0, 1, 4,..., 81]"
+ print(pool.map(f, range(10)))
+
+ # print same numbers in arbitrary order
+ for i in pool.imap_unordered(f, range(10)):
+ print(i)
+
+ # evaluate "f(10)" asynchronously
+ res = pool.apply_async(f, [10])
+ print(res.get(timeout=1)) # prints "100"
+
+ # make worker sleep for 10 secs
+ res = pool.apply_async(sleep, 10)
+ print(res.get(timeout=1)) # raises multiprocessing.TimeoutError
+
+ # exiting the 'with'-block has stopped the pool
Note that the methods of a pool should only ever be used by the
process which created it.
@@ -731,6 +848,9 @@ Miscellaneous
Return the number of CPUs in the system. May raise
:exc:`NotImplementedError`.
+ .. seealso::
+ :func:`os.cpu_count`
+
.. function:: current_process()
Return the :class:`Process` object corresponding to the current process.
@@ -761,6 +881,43 @@ Miscellaneous
If the module is being run normally by the Python interpreter then
:func:`freeze_support` has no effect.
+.. function:: get_all_start_methods()
+
+ Returns a list of the supported start methods, the first of which
+ is the default. The possible start methods are ``'fork'``,
+ ``'spawn'`` and ``'forkserver'``. On Windows only ``'spawn'`` is
+ available. On Unix ``'fork'`` and ``'spawn'`` are always
+ supported, with ``'fork'`` being the default.
+
+ .. versionadded:: 3.4
+
+.. function:: get_context(method=None)
+
+ Return a context object which has the same attributes as the
+ :mod:`multiprocessing` module.
+
+ If *method* is *None* then the default context is returned.
+ Otherwise *method* should be ``'fork'``, ``'spawn'``,
+ ``'forkserver'``. :exc:`ValueError` is raised if the specified
+ start method is not available.
+
+ .. versionadded:: 3.4
+
+.. function:: get_start_method(allow_none=False)
+
+ Return the name of start method used for starting processes.
+
+ If the start method has not been fixed and *allow_none* is false,
+ then the start method is fixed to the default and the name is
+ returned. If the start method has not been fixed and *allow_none*
+ is true then *None* is returned.
+
+ The return value can be ``'fork'``, ``'spawn'``, ``'forkserver'``
+ or *None*. ``'fork'`` is the default on Unix, while ``'spawn'`` is
+ the default on Windows.
+
+ .. versionadded:: 3.4
+
.. function:: set_executable()
Sets the path of the Python interpreter to use when starting a child process.
@@ -769,8 +926,21 @@ Miscellaneous
set_executable(os.path.join(sys.exec_prefix, 'pythonw.exe'))
- before they can create child processes. (Windows only)
+ before they can create child processes.
+
+ .. versionchanged:: 3.4
+ Now supported on Unix when the ``'spawn'`` start method is used.
+.. function:: set_start_method(method)
+
+ Set the method which should be used to start child processes.
+ *method* can be ``'fork'``, ``'spawn'`` or ``'forkserver'``.
+
+ Note that this should be called at most once, and it should be
+ protected inside the ``if __name__ == '__main__'`` clause of the
+ main module.
+
+ .. versionadded:: 3.4
.. note::
@@ -1678,25 +1848,37 @@ Process Pools
One can create a pool of processes which will carry out tasks submitted to it
with the :class:`Pool` class.
-.. class:: Pool([processes[, initializer[, initargs[, maxtasksperchild]]]])
+.. class:: Pool([processes[, initializer[, initargs[, maxtasksperchild [, context]]]]])
A process pool object which controls a pool of worker processes to which jobs
can be submitted. It supports asynchronous results with timeouts and
callbacks and has a parallel map implementation.
*processes* is the number of worker processes to use. If *processes* is
- ``None`` then the number returned by :func:`cpu_count` is used. If
- *initializer* is not ``None`` then each worker process will call
+ ``None`` then the number returned by :func:`os.cpu_count` is used.
+
+ If *initializer* is not ``None`` then each worker process will call
``initializer(*initargs)`` when it starts.
+ *maxtasksperchild* is the number of tasks a worker process can complete
+ before it will exit and be replaced with a fresh worker process, to enable
+ unused resources to be freed. The default *maxtasksperchild* is None, which
+ means worker processes will live as long as the pool.
+
+ *context* can be used to specify the context used for starting
+ the worker processes. Usually a pool is created using the
+ function :func:`multiprocessing.Pool` or the :meth:`Pool` method
+ of a context object. In both cases *context* is set
+ appropriately.
+
Note that the methods of the pool object should only be called by
the process which created the pool.
.. versionadded:: 3.2
- *maxtasksperchild* is the number of tasks a worker process can complete
- before it will exit and be replaced with a fresh worker process, to enable
- unused resources to be freed. The default *maxtasksperchild* is None, which
- means worker processes will live as long as the pool.
+ *maxtasksperchild*
+
+ .. versionadded:: 3.4
+ *context*
.. note::
@@ -2189,43 +2371,8 @@ Below is an example session with logging turned on::
[INFO/MainProcess] sending shutdown message to manager
[INFO/SyncManager-...] manager exiting with exitcode 0
-In addition to having these two logging functions, the multiprocessing also
-exposes two additional logging level attributes. These are :const:`SUBWARNING`
-and :const:`SUBDEBUG`. The table below illustrates where theses fit in the
-normal level hierarchy.
-
-+----------------+----------------+
-| Level | Numeric value |
-+================+================+
-| ``SUBWARNING`` | 25 |
-+----------------+----------------+
-| ``SUBDEBUG`` | 5 |
-+----------------+----------------+
-
For a full table of logging levels, see the :mod:`logging` module.
-These additional logging levels are used primarily for certain debug messages
-within the multiprocessing module. Below is the same example as above, except
-with :const:`SUBDEBUG` enabled::
-
- >>> import multiprocessing, logging
- >>> logger = multiprocessing.log_to_stderr()
- >>> logger.setLevel(multiprocessing.SUBDEBUG)
- >>> logger.warning('doomed')
- [WARNING/MainProcess] doomed
- >>> m = multiprocessing.Manager()
- [INFO/SyncManager-...] child process calling self.run()
- [INFO/SyncManager-...] created temp directory /.../pymp-...
- [INFO/SyncManager-...] manager serving at '/.../pymp-djGBXN/listener-...'
- >>> del m
- [SUBDEBUG/MainProcess] finalizer calling ...
- [INFO/MainProcess] sending shutdown message to manager
- [DEBUG/SyncManager-...] manager received shutdown message
- [SUBDEBUG/SyncManager-...] calling <Finalize object, callback=unlink, ...
- [SUBDEBUG/SyncManager-...] finalizer calling <built-in function unlink> ...
- [SUBDEBUG/SyncManager-...] calling <Finalize object, dead>
- [SUBDEBUG/SyncManager-...] finalizer calling <function rmtree at 0x5aa730> ...
- [INFO/SyncManager-...] manager exiting with exitcode 0
The :mod:`multiprocessing.dummy` module
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -2246,8 +2393,10 @@ There are certain guidelines and idioms which should be adhered to when using
:mod:`multiprocessing`.
-All platforms
-~~~~~~~~~~~~~
+All start methods
+~~~~~~~~~~~~~~~~~
+
+The following applies to all start methods.
Avoid shared state
@@ -2281,11 +2430,13 @@ Joining zombie processes
Better to inherit than pickle/unpickle
- On Windows many types from :mod:`multiprocessing` need to be picklable so
- that child processes can use them. However, one should generally avoid
- sending shared objects to other processes using pipes or queues. Instead
- you should arrange the program so that a process which needs access to a
- shared resource created elsewhere can inherit it from an ancestor process.
+ When using the *spawn* or *forkserver* start methods many types
+ from :mod:`multiprocessing` need to be picklable so that child
+ processes can use them. However, one should generally avoid
+ sending shared objects to other processes using pipes or queues.
+ Instead you should arrange the program so that a process which
+ needs access to a shared resource created elsewhere can inherit it
+ from an ancestor process.
Avoid terminating processes
@@ -2332,15 +2483,17 @@ Joining processes that use queues
Explicitly pass resources to child processes
- On Unix a child process can make use of a shared resource created in a
- parent process using a global resource. However, it is better to pass the
- object as an argument to the constructor for the child process.
+ On Unix using the *fork* start method, a child process can make
+ use of a shared resource created in a parent process using a
+ global resource. However, it is better to pass the object as an
+ argument to the constructor for the child process.
- Apart from making the code (potentially) compatible with Windows this also
- ensures that as long as the child process is still alive the object will not
- be garbage collected in the parent process. This might be important if some
- resource is freed when the object is garbage collected in the parent
- process.
+ Apart from making the code (potentially) compatible with Windows
+ and the other start methods this also ensures that as long as the
+ child process is still alive the object will not be garbage
+ collected in the parent process. This might be important if some
+ resource is freed when the object is garbage collected in the
+ parent process.
So for instance ::
@@ -2399,17 +2552,19 @@ Beware of replacing :data:`sys.stdin` with a "file like object"
For more information, see :issue:`5155`, :issue:`5313` and :issue:`5331`
-Windows
-~~~~~~~
+The *spawn* and *forkserver* start methods
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Since Windows lacks :func:`os.fork` it has a few extra restrictions:
+There are a few extra restriction which don't apply to the *fork*
+start method.
More picklability
- Ensure that all arguments to :meth:`Process.__init__` are picklable. This
- means, in particular, that bound or unbound methods cannot be used directly
- as the ``target`` argument on Windows --- just define a function and use
- that instead.
+ Ensure that all arguments to :meth:`Process.__init__` are
+ picklable. This means, in particular, that bound or unbound
+ methods cannot be used directly as the ``target`` (unless you use
+ the *fork* start method) --- just define a function and use that
+ instead.
Also, if you subclass :class:`~multiprocessing.Process` then make sure that
instances will be picklable when the :meth:`Process.start
@@ -2431,7 +2586,8 @@ Safe importing of main module
interpreter without causing unintended side effects (such a starting a new
process).
- For example, under Windows running the following module would fail with a
+ For example, using the *spawn* or *forkserver* start method
+ running the following module would fail with a
:exc:`RuntimeError`::
from multiprocessing import Process
@@ -2445,13 +2601,14 @@ Safe importing of main module
Instead one should protect the "entry point" of the program by using ``if
__name__ == '__main__':`` as follows::
- from multiprocessing import Process, freeze_support
+ from multiprocessing import Process, freeze_support, set_start_method
def foo():
print('hello')
if __name__ == '__main__':
freeze_support()
+ set_start_method('spawn')
p = Process(target=foo)
p.start()
@@ -2482,26 +2639,7 @@ Using :class:`~multiprocessing.pool.Pool`:
:language: python3
-Synchronization types like locks, conditions and queues:
-
-.. literalinclude:: ../includes/mp_synchronize.py
- :language: python3
-
-
An example showing how to use queues to feed tasks to a collection of worker
processes and collect the results:
.. literalinclude:: ../includes/mp_workers.py
-
-
-An example of how a pool of worker processes can each run a
-:class:`~http.server.SimpleHTTPRequestHandler` instance while sharing a single
-listening socket.
-
-.. literalinclude:: ../includes/mp_webserver.py
-
-
-Some simple benchmarks comparing :mod:`multiprocessing` with :mod:`threading`:
-
-.. literalinclude:: ../includes/mp_benchmarks.py
-
diff --git a/Doc/library/netrc.rst b/Doc/library/netrc.rst
index 564f101..23ffed6 100644
--- a/Doc/library/netrc.rst
+++ b/Doc/library/netrc.rst
@@ -29,7 +29,7 @@ the Unix :program:`ftp` program and other FTP clients.
This implements security behavior equivalent to that of ftp and other
programs that use :file:`.netrc`.
- .. versionchanged:: 3.3.3 Added the POSIX permission check.
+ .. versionchanged:: 3.4 Added the POSIX permission check.
.. exception:: NetrcParseError
diff --git a/Doc/library/nntplib.rst b/Doc/library/nntplib.rst
index a15d7f7..3943f2c 100644
--- a/Doc/library/nntplib.rst
+++ b/Doc/library/nntplib.rst
@@ -1,4 +1,3 @@
-
:mod:`nntplib` --- NNTP protocol client
=======================================
@@ -71,7 +70,7 @@ The module itself defines the following classes:
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
+ unconditionally consume :exc:`OSError` exceptions and to close the NNTP
connection when done. Here is a sample on how using it:
>>> from nntplib import NNTP
@@ -95,6 +94,7 @@ The module itself defines the following classes:
port *port*. :class:`NNTP_SSL` objects have the same methods as
:class:`NNTP` objects. If *port* is omitted, port 563 (NNTPS) is used.
*ssl_context* is also optional, and is a :class:`~ssl.SSLContext` object.
+ Please read :ref:`ssl-security` for best practices.
All other parameters behave the same as for :class:`NNTP`.
Note that SSL-on-563 is discouraged per :rfc:`4642`, in favor of
@@ -103,6 +103,10 @@ The module itself defines the following classes:
.. versionadded:: 3.2
+ .. versionchanged:: 3.4
+ The class now supports hostname check with
+ :attr:`ssl.SSLContext.check_hostname` and *Server Name Indication* (see
+ :data:`ssl.HAS_SNI`).
.. exception:: NNTPError
@@ -231,9 +235,10 @@ tuples or objects that the method normally returns will be empty.
.. method:: NNTP.starttls(ssl_context=None)
- Send a ``STARTTLS`` command. The *ssl_context* argument is optional
- and should be a :class:`ssl.SSLContext` object. This will enable
- encryption on the NNTP connection.
+ Send a ``STARTTLS`` command. This will enable encryption on the NNTP
+ connection. The *ssl_context* argument is optional and should be a
+ :class:`ssl.SSLContext` object. Please read :ref:`ssl-security` for best
+ practices.
Note that this may not be done after authentication information has
been transmitted, and authentication occurs by default if possible during a
@@ -242,6 +247,10 @@ tuples or objects that the method normally returns will be empty.
.. versionadded:: 3.2
+ .. versionchanged:: 3.4
+ The method now supports hostname check with
+ :attr:`ssl.SSLContext.check_hostname` and *Server Name Indication* (see
+ :data:`ssl.HAS_SNI`).
.. method:: NNTP.newgroups(date, *, file=None)
diff --git a/Doc/library/numeric.rst b/Doc/library/numeric.rst
index 2732a84..7c76a47 100644
--- a/Doc/library/numeric.rst
+++ b/Doc/library/numeric.rst
@@ -23,3 +23,4 @@ The following modules are documented in this chapter:
decimal.rst
fractions.rst
random.rst
+ statistics.rst
diff --git a/Doc/library/operator.rst b/Doc/library/operator.rst
index 24becf9..3bcbaa4 100644
--- a/Doc/library/operator.rst
+++ b/Doc/library/operator.rst
@@ -11,6 +11,9 @@
import operator
from operator import itemgetter, iadd
+**Source code:** :source:`Lib/operator.py`
+
+--------------
The :mod:`operator` module exports a set of efficient functions corresponding to
the intrinsic operators of Python. For example, ``operator.add(x, y)`` is
@@ -235,6 +238,14 @@ their character equivalents.
.. XXX: find a better, readable, example
+.. function:: length_hint(obj, default=0)
+
+ Return an estimated length for the object *o*. First try to return its
+ actual length, then an estimate using :meth:`object.__length_hint__`, and
+ finally return the default value.
+
+ .. versionadded:: 3.4
+
The :mod:`operator` module also defines tools for generalized attribute and item
lookups. These are useful for making fast field extractors as arguments for
:func:`map`, :func:`sorted`, :meth:`itertools.groupby`, or other functions that
diff --git a/Doc/library/optparse.rst b/Doc/library/optparse.rst
index 13395b6..72145aa 100644
--- a/Doc/library/optparse.rst
+++ b/Doc/library/optparse.rst
@@ -8,8 +8,8 @@
.. sectionauthor:: Greg Ward <gward@python.net>
.. deprecated:: 3.2
- The :mod:`optparse` module is deprecated and will not be developed further;
- development will continue with the :mod:`argparse` module.
+ The :mod:`optparse` module is deprecated and will not be developed further;
+ development will continue with the :mod:`argparse` module.
**Source code:** :source:`Lib/optparse.py`
diff --git a/Doc/library/os.path.rst b/Doc/library/os.path.rst
index 57a6bca..6b3a3b6 100644
--- a/Doc/library/os.path.rst
+++ b/Doc/library/os.path.rst
@@ -22,6 +22,11 @@ Functions such as :func:`expanduser` and :func:`expandvars` can be invoked
explicitly when an application desires shell-like path expansion. (See also
the :mod:`glob` module.)
+
+.. seealso::
+ The :mod:`pathlib` module offers high-level path objects.
+
+
.. note::
All of these functions accept either only bytes or only string objects as
@@ -42,7 +47,6 @@ the :mod:`glob` module.)
* :mod:`posixpath` for UNIX-style paths
* :mod:`ntpath` for Windows paths
* :mod:`macpath` for old-style MacOS paths
- * :mod:`os2emxpath` for OS/2 EMX paths
.. function:: abspath(path)
@@ -189,11 +193,17 @@ the :mod:`glob` module.)
.. function:: ismount(path)
- Return ``True`` if pathname *path* is a :dfn:`mount point`: a point in a file
- system where a different file system has been mounted. The function checks
- whether *path*'s parent, :file:`path/..`, is on a different device than *path*,
- or whether :file:`path/..` and *path* point to the same i-node on the same
- device --- this should detect mount points for all Unix and POSIX variants.
+ Return ``True`` if pathname *path* is a :dfn:`mount point`: a point in a
+ file system where a different file system has been mounted. On POSIX, the
+ function checks whether *path*'s parent, :file:`path/..`, is on a different
+ device than *path*, or whether :file:`path/..` and *path* point to the same
+ i-node on the same device --- this should detect mount points for all Unix
+ and POSIX variants. On Windows, a drive letter root and a share UNC are
+ always mount points, and for any other path ``GetVolumePathName`` is called
+ to see if it is different from the input path.
+
+ .. versionadded:: 3.4
+ Support for detecting non-root mount points on Windows.
.. function:: join(path1[, path2[, ...]])
@@ -232,7 +242,7 @@ the :mod:`glob` module.)
links encountered in the path (if they are supported by the operating system).
-.. function:: relpath(path, start=None)
+.. function:: relpath(path, start=os.curdir)
Return a relative filepath to *path* either from the current directory or
from an optional *start* directory. This is a path computation: the
@@ -247,18 +257,17 @@ the :mod:`glob` module.)
.. function:: samefile(path1, path2)
Return ``True`` if both pathname arguments refer to the same file or directory.
- On Unix, this is determined by the device number and i-node number and raises an
+ This is determined by the device number and i-node number and raises an
exception if a :func:`os.stat` call on either pathname fails.
- On Windows, two files are the same if they resolve to the same final path
- name using the Windows API call GetFinalPathNameByHandle. This function
- raises an exception if handles cannot be obtained to either file.
-
Availability: Unix, Windows.
.. versionchanged:: 3.2
Added Windows support.
+ .. versionchanged:: 3.4
+ Windows now uses the same implementation as all other platforms.
+
.. function:: sameopenfile(fp1, fp2)
@@ -277,7 +286,10 @@ the :mod:`glob` module.)
:func:`os.lstat`, or :func:`os.stat`. This function implements the
underlying comparison used by :func:`samefile` and :func:`sameopenfile`.
- Availability: Unix.
+ Availability: Unix, Windows.
+
+ .. versionchanged:: 3.4
+ Added Windows support.
.. function:: split(path)
diff --git a/Doc/library/os.rst b/Doc/library/os.rst
index d7b9829..3d492ba 100644
--- a/Doc/library/os.rst
+++ b/Doc/library/os.rst
@@ -54,7 +54,7 @@ Notes on the availability of these functions:
The name of the operating system dependent module imported. The following
names have currently been registered: ``'posix'``, ``'nt'``, ``'mac'``,
- ``'os2'``, ``'ce'``, ``'java'``.
+ ``'ce'``, ``'java'``.
.. seealso::
:attr:`sys.platform` has a finer granularity. :func:`os.uname` gives
@@ -687,17 +687,30 @@ as internal buffering of data.
.. function:: dup(fd)
- Return a duplicate of file descriptor *fd*.
+ Return a duplicate of file descriptor *fd*. The new file descriptor is
+ :ref:`non-inheritable <fd_inheritance>`.
+
+ On Windows, when duplicating a standard stream (0: stdin, 1: stdout,
+ 2: stderr), the new file descriptor is :ref:`inheritable
+ <fd_inheritance>`.
Availability: Unix, Windows.
+ .. versionchanged:: 3.4
+ The new file descriptor is now non-inheritable.
+
-.. function:: dup2(fd, fd2)
+.. function:: dup2(fd, fd2, inheritable=True)
Duplicate file descriptor *fd* to *fd2*, closing the latter first if necessary.
+ The file descriptor *fd2* is :ref:`inheritable <fd_inheritance>` by default,
+ or non-inheritable if *inheritable* is ``False``.
Availability: Unix, Windows.
+ .. versionchanged:: 3.4
+ Add the optional *inheritable* parameter.
+
.. function:: fchmod(fd, mode)
@@ -848,17 +861,21 @@ as internal buffering of data.
Open the file *file* and set various flags according to *flags* and possibly
its mode according to *mode*. When computing *mode*, the current umask value
is first masked out. Return the file descriptor for the newly opened file.
+ The new file descriptor is :ref:`non-inheritable <fd_inheritance>`.
For a description of the flag and mode values, see the C run-time documentation;
flag constants (like :const:`O_RDONLY` and :const:`O_WRONLY`) are defined in
- this module too (see :ref:`open-constants`). In particular, on Windows adding
+ the :mod:`os` module. In particular, on Windows adding
:const:`O_BINARY` is needed to open files in binary mode.
This function can support :ref:`paths relative to directory descriptors
- <dir_fd>`.
+ <dir_fd>` with the *dir_fd* parameter.
Availability: Unix, Windows.
+ .. versionchanged:: 3.4
+ The new file descriptor is now non-inheritable.
+
.. note::
This function is intended for low-level I/O. For normal usage, use the
@@ -869,25 +886,93 @@ as internal buffering of data.
.. versionadded:: 3.3
The *dir_fd* argument.
+The following constants are options for the *flags* parameter to the
+:func:`~os.open` function. They can be combined using the bitwise OR operator
+``|``. Some of them are not available on all platforms. For descriptions of
+their availability and use, consult the :manpage:`open(2)` manual page on Unix
+or `the MSDN <http://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windows.
+
+
+.. data:: O_RDONLY
+ O_WRONLY
+ O_RDWR
+ O_APPEND
+ O_CREAT
+ O_EXCL
+ O_TRUNC
+
+ These constants are available on Unix and Windows.
+
+
+.. data:: O_DSYNC
+ O_RSYNC
+ O_SYNC
+ O_NDELAY
+ O_NONBLOCK
+ 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
+ O_SHORT_LIVED
+ O_TEMPORARY
+ O_RANDOM
+ O_SEQUENTIAL
+ O_TEXT
+
+ These constants are only available on Windows.
+
+
+.. data:: O_ASYNC
+ O_DIRECT
+ O_DIRECTORY
+ O_NOFOLLOW
+ O_NOATIME
+ O_PATH
+ O_TMPFILE
+
+ These constants are GNU extensions and not present if they are not defined by
+ the C library.
+
+ .. versionchanged:: 3.4
+ Add :data:`O_PATH` on systems that support it.
+ Add :data:`O_TMPFILE`, only available on Linux Kernel 3.11
+ or newer.
+
.. function:: openpty()
.. index:: module: pty
- Open a new pseudo-terminal pair. Return a pair of file descriptors ``(master,
- slave)`` for the pty and the tty, respectively. For a (slightly) more portable
- approach, use the :mod:`pty` module.
+ Open a new pseudo-terminal pair. Return a pair of file descriptors
+ ``(master, slave)`` for the pty and the tty, respectively. The new file
+ descriptors are :ref:`non-inheritable <fd_inheritance>`. For a (slightly) more
+ portable approach, use the :mod:`pty` module.
Availability: some flavors of Unix.
+ .. versionchanged:: 3.4
+ The new file descriptors are now non-inheritable.
+
.. function:: pipe()
- Create a pipe. Return a pair of file descriptors ``(r, w)`` usable for reading
- and writing, respectively.
+ Create a pipe. Return a pair of file descriptors ``(r, w)`` usable for
+ reading and writing, respectively. The new file descriptor is
+ :ref:`non-inheritable <fd_inheritance>`.
Availability: Unix, Windows.
+ .. versionchanged:: 3.4
+ The new file descriptors are now non-inheritable.
+
.. function:: pipe2(flags)
@@ -1027,7 +1112,7 @@ as internal buffering of data.
sequence to hold the rest of the data. :func:`~os.readv` returns the total
number of bytes read (which may be less than the total capacity of all the
objects).
-
+
Availability: Unix.
.. versionadded:: 3.3
@@ -1080,80 +1165,8 @@ as internal buffering of data.
sequence of :term:`bytes-like objects <bytes-like object>`.
:func:`~os.writev` writes the contents of each object to the file descriptor
and returns the total number of bytes written.
-
- Availability: Unix.
-
- .. versionadded:: 3.3
-
-
-.. _open-constants:
-
-``open()`` flag constants
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The following constants are options for the *flags* parameter to the
-:func:`~os.open` function. They can be combined using the bitwise OR operator
-``|``. Some of them are not available on all platforms. For descriptions of
-their availability and use, consult the :manpage:`open(2)` manual page on Unix
-or `the MSDN <http://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windows.
-
-
-.. data:: O_RDONLY
- O_WRONLY
- O_RDWR
- O_APPEND
- O_CREAT
- O_EXCL
- O_TRUNC
-
- These constants are available on Unix and Windows.
-
-
-.. data:: O_DSYNC
- O_RSYNC
- O_SYNC
- O_NDELAY
- O_NONBLOCK
- 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
- O_SHORT_LIVED
- O_TEMPORARY
- O_RANDOM
- O_SEQUENTIAL
- O_TEXT
-
- These constants are only available on Windows.
-
-
-.. data:: O_ASYNC
- O_DIRECT
- O_DIRECTORY
- O_NOFOLLOW
- O_NOATIME
-
- These constants are GNU extensions and not present if they are not defined by
- 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)`.
+ Availability: Unix.
.. versionadded:: 3.3
@@ -1195,6 +1208,49 @@ Querying the size of a terminal
Height of the terminal window in characters.
+.. _fd_inheritance:
+
+Inheritance of File Descriptors
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. versionadded:: 3.4
+
+A file descriptor has an "inheritable" flag which indicates if the file descriptor
+can be inherited by child processes. Since Python 3.4, file descriptors
+created by Python are non-inheritable by default.
+
+On UNIX, non-inheritable file descriptors are closed in child processes at the
+execution of a new program, other file descriptors are inherited.
+
+On Windows, non-inheritable handles and file descriptors are closed in child
+processes, except for standard streams (file descriptors 0, 1 and 2: stdin, stdout
+and stderr), which are always inherited. Using :func:`spawn\* <spawnl>` functions,
+all inheritable handles and all inheritable file descriptors are inherited.
+Using the :mod:`subprocess` module, all file descriptors except standard
+streams are closed, and inheritable handles are only inherited if the
+*close_fds* parameter is ``False``.
+
+.. function:: get_inheritable(fd)
+
+ Get the "inheritable" flag of the specified file descriptor (a boolean).
+
+.. function:: set_inheritable(fd, inheritable)
+
+ Set the "inheritable" flag of the specified file descriptor.
+
+.. function:: get_handle_inheritable(handle)
+
+ Get the "inheritable" flag of the specified handle (a boolean).
+
+ Availability: Windows.
+
+.. function:: set_handle_inheritable(handle, inheritable)
+
+ Set the "inheritable" flag of the specified handle.
+
+ Availability: Windows.
+
+
.. _os-file-dir:
Files and Directories
@@ -1551,7 +1607,7 @@ features:
The *dir_fd* argument.
-.. function:: makedirs(path, mode=0o777, exist_ok=False)
+.. function:: makedirs(name, mode=0o777, exist_ok=False)
.. index::
single: directory; creating
@@ -1576,12 +1632,12 @@ features:
.. versionadded:: 3.2
The *exist_ok* parameter.
- .. versionchanged:: 3.3.6
+ .. versionchanged:: 3.4.1
- Before Python 3.3.6, if *exist_ok* was ``True`` and the directory existed,
+ Before Python 3.4.1, if *exist_ok* was ``True`` and the directory existed,
:func:`makedirs` would still raise an error if *mode* did not match the
mode of the existing directory. Since this behavior was impossible to
- implement safely, it was removed in Python 3.3.6. See :issue:`21082`.
+ implement safely, it was removed in Python 3.4.1. See :issue:`21082`.
.. function:: mkfifo(path, mode=0o666, *, dir_fd=None)
@@ -1711,7 +1767,7 @@ features:
The *dir_fd* argument.
-.. function:: removedirs(path)
+.. function:: removedirs(name)
.. index:: single: directory; deleting
@@ -1935,11 +1991,26 @@ features:
read-only, and if :const:`ST_NOSUID` is set, the semantics of
setuid/setgid bits are disabled or not supported.
+ Additional module-level constants are defined for GNU/glibc based systems.
+ These are :const:`ST_NODEV` (disallow access to device special files),
+ :const:`ST_NOEXEC` (disallow program execution), :const:`ST_SYNCHRONOUS`
+ (writes are synced at once), :const:`ST_MANDLOCK` (allow mandatory locks on an FS),
+ :const:`ST_WRITE` (write on file/directory/symlink), :const:`ST_APPEND`
+ (append-only file), :const:`ST_IMMUTABLE` (immutable file), :const:`ST_NOATIME`
+ (do not update access times), :const:`ST_NODIRATIME` (do not update directory access
+ times), :const:`ST_RELATIME` (update atime relative to mtime/ctime).
+
This function can support :ref:`specifying a file descriptor <path_fd>`.
.. versionchanged:: 3.2
The :const:`ST_RDONLY` and :const:`ST_NOSUID` constants were added.
+ .. versionchanged:: 3.4
+ The :const:`ST_NODEV`, :const:`ST_NOEXEC`, :const:`ST_SYNCHRONOUS`,
+ :const:`ST_MANDLOCK`, :const:`ST_WRITE`, :const:`ST_APPEND`,
+ :const:`ST_IMMUTABLE`, :const:`ST_NOATIME`, :const:`ST_NODIRATIME`,
+ and :const:`ST_RELATIME` constants were added.
+
Availability: Unix.
.. versionadded:: 3.3
@@ -2589,7 +2660,7 @@ written in Python, such as a mail server's external command delivery program.
Fork a child process. Return ``0`` in the child and the child's process id in the
parent. If an error occurs :exc:`OSError` is raised.
- Note that some platforms including FreeBSD <= 6.3, Cygwin and OS/2 EMX have
+ Note that some platforms including FreeBSD <= 6.3 and Cygwin have
known issues when using fork() from a thread.
.. warning::
@@ -2835,7 +2906,6 @@ written in Python, such as a mail server's external command delivery program.
:manpage:`times(2)` or the corresponding Windows Platform API documentation.
On Windows, only :attr:`user` and :attr:`system` are known; the other
attributes are zero.
- On OS/2, only :attr:`elapsed` is known; the other attributes are zero.
Availability: Unix, Windows.
@@ -3057,7 +3127,7 @@ information, consult your Unix manpages.
.. versionadded:: 3.3
-The following scheduling policies are exposed if they are a supported by the
+The following scheduling policies are exposed if they are supported by the
operating system.
.. data:: SCHED_OTHER
@@ -3166,10 +3236,6 @@ operating system.
Return the set of CPUs the process with PID *pid* (or the current process
if zero) is restricted to.
- .. seealso::
- :func:`multiprocessing.cpu_count` returns the number of CPUs in the
- system.
-
.. _os-path:
@@ -3207,6 +3273,13 @@ Miscellaneous System Information
Availability: Unix.
+.. function:: cpu_count()
+
+ Return the number of CPUs in the system. Returns None if undetermined.
+
+ .. versionadded:: 3.4
+
+
.. function:: getloadavg()
Return the number of processes in the system run queue averaged over the last
@@ -3305,6 +3378,19 @@ Higher-level operations on pathnames are defined in the :mod:`os.path` module.
The file path of the null device. For example: ``'/dev/null'`` for
POSIX, ``'nul'`` for Windows. Also available via :mod:`os.path`.
+.. data:: RTLD_LAZY
+ RTLD_NOW
+ RTLD_GLOBAL
+ RTLD_LOCAL
+ RTLD_NODELETE
+ RTLD_NOLOAD
+ RTLD_DEEPBIND
+
+ Flags for use with the :func:`~sys.setdlopenflags` and
+ :func:`~sys.getdlopenflags` functions. See the Unix manual page
+ :manpage:`dlopen(3)` for what the different flags mean.
+
+ .. versionadded:: 3.3
.. _os-miscfunc:
diff --git a/Doc/library/pathlib-inheritance.png b/Doc/library/pathlib-inheritance.png
new file mode 100644
index 0000000..b81c3de
--- /dev/null
+++ b/Doc/library/pathlib-inheritance.png
Binary files differ
diff --git a/Doc/library/pathlib-inheritance.svg b/Doc/library/pathlib-inheritance.svg
new file mode 100644
index 0000000..9f42005
--- /dev/null
+++ b/Doc/library/pathlib-inheritance.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" standalone="yes"?>
+
+<svg version="1.1" viewBox="0.0 0.0 538.0 496.0" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><clipPath id="p.0"><path d="m0 0l538.0 0l0 496.0l-538.0 0l0 -496.0z" clip-rule="nonzero"></path></clipPath><g clip-path="url(#p.0)"><path fill="#000000" fill-opacity="0.0" d="m0 0l538.916 0l0 496.08398l-538.916 0z" fill-rule="nonzero"></path><path fill="#ffffff" d="m176.0 24.0l187.43307 0l0 80.53543l-187.43307 0z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m176.0 24.0l187.43307 0l0 80.53543l-187.43307 0z" fill-rule="nonzero"></path><path fill="#000000" d="m235.18527 66.877716l0 1.640625l1.625 0q0.578125 0 0.8125 0.234375q0.25 0.21875 0.25 0.578125q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-3.46875 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.34375 0.25 -0.5625q0.25 -0.234375 0.828125 -0.234375l0.25 0l0 -6.265625l-0.25 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.21875 -0.25 -0.578125q0 -0.359375 0.25 -0.578125q0.25 -0.234375 0.828125 -0.234375l3.6875 0.015625q1.625 0 2.5625 0.890625q0.953125 0.875 0.953125 2.15625q0 0.703125 -0.3125 1.328125q-0.25 0.46875 -0.8125 0.921875q-0.5625 0.453125 -1.15625 0.6875q-0.59375 0.234375 -1.5625 0.234375l-1.515625 0zm0 -1.609375l1.484375 0q1.046875 0 1.65625 -0.46875q0.625 -0.46875 0.625 -1.140625q0 -0.5625 -0.5 -0.984375q-0.5 -0.421875 -1.421875 -0.421875l-1.84375 0l0 3.015625zm13.9296875 -2.234375l0 5.484375q0.515625 0 0.75 0.234375q0.25 0.21875 0.25 0.578125q0 0.34375 -0.25 0.578125q-0.25 0.21875 -0.828125 0.21875l-1.515625 0l0 -0.375q-0.6875 0.3125 -1.3125 0.46875q-0.625 0.171875 -1.1875 0.171875q-0.796875 0 -1.375 -0.328125q-0.578125 -0.34375 -0.90625 -0.9375q-0.25 -0.421875 -0.25 -1.046875l0 -3.453125l-0.265625 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.21875 -0.25 -0.578125q0 -0.359375 0.25 -0.578125q0.25 -0.21875 0.828125 -0.21875l1.875 0l0 4.765625q0 0.5 0.234375 0.75q0.25 0.234375 0.765625 0.234375q0.484375 0 1.03125 -0.1875q0.5625 -0.203125 1.390625 -0.703125l0 -3.265625l-0.578125 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.21875 -0.25 -0.578125q0 -0.359375 0.25 -0.578125q0.25 -0.21875 0.828125 -0.21875l2.171875 0zm5.8671875 0l0 1.0q1.015625 -0.734375 1.59375 -0.96875q0.578125 -0.25 1.09375 -0.25q0.78125 0 1.515625 0.578125q0.5 0.390625 0.5 0.796875q0 0.34375 -0.25 0.59375q-0.234375 0.234375 -0.5625 0.234375q-0.296875 0 -0.625 -0.296875q-0.328125 -0.296875 -0.59375 -0.296875q-0.328125 0 -1.0 0.421875q-0.671875 0.421875 -1.671875 1.265625l0 2.40625l2.28125 0q0.578125 0 0.828125 0.234375q0.25 0.21875 0.25 0.578125q0 0.34375 -0.25 0.578125q-0.25 0.21875 -0.828125 0.21875l-4.828125 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.34375 0.25 -0.5625q0.25 -0.234375 0.828125 -0.234375l0.953125 0l0 -3.890625l-0.578125 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.21875 -0.25 -0.578125q0 -0.359375 0.25 -0.578125q0.25 -0.21875 0.828125 -0.21875l2.171875 0zm13.9609375 4.359375l-6.578125 0q0.25 0.625 0.890625 1.015625q0.640625 0.375 1.71875 0.375q0.890625 0 2.375 -0.390625q0.609375 -0.15625 0.84375 -0.15625q0.3125 0 0.53125 0.234375q0.21875 0.21875 0.21875 0.5625q0 0.3125 -0.234375 0.53125q-0.3125 0.296875 -1.53125 0.5625q-1.203125 0.265625 -2.3125 0.265625q-1.921875 0 -3.078125 -1.09375q-1.15625 -1.09375 -1.15625 -2.671875q0 -1.6875 1.25 -2.75q1.25 -1.0625 2.875 -1.0625q0.96875 0 1.78125 0.34375q0.828125 0.34375 1.21875 0.75q0.5625 0.578125 0.9375 1.421875q0.25 0.59375 0.25 1.375l0 0.6875zm-1.78125 -1.609375q-0.359375 -0.6875 -0.953125 -1.015625q-0.59375 -0.34375 -1.421875 -0.34375q-0.8125 0 -1.40625 0.34375q-0.59375 0.328125 -0.96875 1.015625l4.75 0zm6.4296875 1.09375l0 1.640625l1.625 0q0.578125 0 0.8125 0.234375q0.25 0.21875 0.25 0.578125q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-3.46875 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.34375 0.25 -0.5625q0.25 -0.234375 0.828125 -0.234375l0.25 0l0 -6.265625l-0.25 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.21875 -0.25 -0.578125q0 -0.359375 0.25 -0.578125q0.25 -0.234375 0.828125 -0.234375l3.6875 0.015625q1.625 0 2.5625 0.890625q0.953125 0.875 0.953125 2.15625q0 0.703125 -0.3125 1.328125q-0.25 0.46875 -0.8125 0.921875q-0.5625 0.453125 -1.15625 0.6875q-0.59375 0.234375 -1.5625 0.234375l-1.515625 0zm0 -1.609375l1.484375 0q1.046875 0 1.65625 -0.46875q0.625 -0.46875 0.625 -1.140625q0 -0.5625 -0.5 -0.984375q-0.5 -0.421875 -1.421875 -0.421875l-1.84375 0l0 3.015625zm11.9765625 4.859375l0 -0.375q-0.609375 0.3125 -1.34375 0.46875q-0.71875 0.171875 -1.3125 0.171875q-1.28125 0 -2.09375 -0.6875q-0.796875 -0.6875 -0.796875 -1.515625q0 -1.0 1.015625 -1.859375q1.03125 -0.875 2.84375 -0.875q0.734375 0 1.6875 0.15625l0 -0.375q0 -0.359375 -0.3125 -0.578125q-0.3125 -0.234375 -1.171875 -0.234375q-0.71875 0 -1.84375 0.28125q-0.421875 0.09375 -0.65625 0.09375q-0.328125 0 -0.546875 -0.21875q-0.21875 -0.234375 -0.21875 -0.59375q0 -0.203125 0.078125 -0.34375q0.078125 -0.15625 0.21875 -0.25q0.140625 -0.09375 0.578125 -0.21875q0.59375 -0.15625 1.203125 -0.25q0.625 -0.109375 1.125 -0.109375q1.5 0 2.3125 0.65625q0.828125 0.640625 0.828125 1.75l0 3.296875l0.28125 0q0.578125 0 0.8125 0.234375q0.25 0.21875 0.25 0.578125q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-1.875 0zm0 -2.875q-0.96875 -0.1875 -1.78125 -0.1875q-0.96875 0 -1.671875 0.484375q-0.4375 0.296875 -0.4375 0.609375q0 0.234375 0.203125 0.375q0.390625 0.25 1.078125 0.25q0.578125 0 1.296875 -0.21875q0.734375 -0.234375 1.3125 -0.625l0 -0.6875zm7.7578125 -2.625l0 3.21875q0 0.515625 0.21875 0.671875q0.328125 0.265625 1.171875 0.265625q1.21875 0 2.265625 -0.53125q0.390625 -0.203125 0.625 -0.203125q0.3125 0 0.53125 0.234375q0.234375 0.234375 0.234375 0.578125q0 0.3125 -0.25 0.53125q-0.375 0.375 -1.515625 0.6875q-1.125 0.3125 -1.890625 0.3125q-1.5 0 -2.25 -0.640625q-0.734375 -0.65625 -0.734375 -1.59375l0 -3.53125l-0.578125 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.21875 -0.25 -0.578125q0 -0.359375 0.25 -0.578125q0.25 -0.21875 0.828125 -0.21875l0.578125 0l0 -1.453125q0 -0.578125 0.21875 -0.8125q0.21875 -0.25 0.578125 -0.25q0.359375 0 0.578125 0.25q0.21875 0.234375 0.21875 0.8125l0 1.453125l2.96875 0q0.578125 0 0.8125 0.21875q0.25 0.21875 0.25 0.578125q0 0.359375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-2.96875 0zm8.3515625 -4.640625l0 3.421875q0.5 -0.296875 1.0 -0.4375q0.515625 -0.15625 1.046875 -0.15625q0.828125 0 1.46875 0.28125q0.65625 0.28125 1.078125 0.890625q0.421875 0.609375 0.421875 1.53125l0 3.0q0.609375 0 0.796875 0.125q0.375 0.234375 0.375 0.6875q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-1.828125 0q-0.5625 0 -0.8125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.4375 0.375 -0.671875q0.203125 -0.125 0.796875 -0.125l0 -2.890625q0 -0.625 -0.28125 -0.875q-0.359375 -0.328125 -1.078125 -0.328125q-0.53125 0 -0.953125 0.203125q-0.40625 0.203125 -1.09375 0.890625l0 3.0q0.609375 0 0.796875 0.125q0.375 0.234375 0.375 0.6875q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-1.828125 0q-0.578125 0 -0.828125 -0.21875q-0.234375 -0.234375 -0.234375 -0.59375q0 -0.4375 0.375 -0.671875q0.1875 -0.125 0.796875 -0.125l0 -6.921875l-0.265625 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.34375 0.25 -0.5625q0.25 -0.234375 0.828125 -0.234375l1.875 0z" fill-rule="nonzero"></path><path fill="#ffffff" d="m16.0 144.0l187.43306 0l0 80.53543l-187.43306 0z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m16.0 144.0l187.43306 0l0 80.53543l-187.43306 0z" fill-rule="nonzero"></path><path fill="#000000" d="m51.181374 186.87772l0 1.640625l1.625 0q0.578125 0 0.8125 0.234375q0.25 0.21875 0.25 0.578125q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-3.46875 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.34375 0.25 -0.5625q0.25 -0.234375 0.828125 -0.234375l0.25 0l0 -6.265625l-0.25 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.21875 -0.25 -0.578125q0 -0.359375 0.25 -0.578125q0.25 -0.234375 0.828125 -0.234375l3.6875 0.015625q1.625 0 2.5625 0.890625q0.953125 0.875 0.953125 2.15625q0 0.703125 -0.3125 1.328125q-0.25 0.46875 -0.8125 0.921875q-0.5625 0.453125 -1.15625 0.6875q-0.59375 0.234375 -1.5625 0.234375l-1.515625 0zm0 -1.609375l1.484375 0q1.046875 0 1.65625 -0.46875q0.625 -0.46875 0.625 -1.140625q0 -0.5625 -0.5 -0.984375q-0.5 -0.421875 -1.421875 -0.421875l-1.84375 0l0 3.015625zm13.9296875 -2.234375l0 5.484375q0.515625 0 0.75 0.234375q0.25 0.21875 0.25 0.578125q0 0.34375 -0.25 0.578125q-0.25 0.21875 -0.828125 0.21875l-1.515625 0l0 -0.375q-0.6875 0.3125 -1.3125 0.46875q-0.625 0.171875 -1.1875 0.171875q-0.796875 0 -1.375 -0.328125q-0.578125 -0.34375 -0.90625 -0.9375q-0.25 -0.421875 -0.25 -1.046875l0 -3.453125l-0.265625 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.21875 -0.25 -0.578125q0 -0.359375 0.25 -0.578125q0.25 -0.21875 0.828125 -0.21875l1.875 0l0 4.765625q0 0.5 0.234375 0.75q0.25 0.234375 0.765625 0.234375q0.484375 0 1.03125 -0.1875q0.5625 -0.203125 1.390625 -0.703125l0 -3.265625l-0.578125 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.21875 -0.25 -0.578125q0 -0.359375 0.25 -0.578125q0.25 -0.21875 0.828125 -0.21875l2.171875 0zm5.8671875 0l0 1.0q1.015625 -0.734375 1.59375 -0.96875q0.578125 -0.25 1.09375 -0.25q0.78125 0 1.515625 0.578125q0.5 0.390625 0.5 0.796875q0 0.34375 -0.25 0.59375q-0.234375 0.234375 -0.5625 0.234375q-0.296875 0 -0.625 -0.296875q-0.328125 -0.296875 -0.59375 -0.296875q-0.328125 0 -1.0 0.421875q-0.671875 0.421875 -1.671875 1.265625l0 2.40625l2.28125 0q0.578125 0 0.828125 0.234375q0.25 0.21875 0.25 0.578125q0 0.34375 -0.25 0.578125q-0.25 0.21875 -0.828125 0.21875l-4.828125 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.34375 0.25 -0.5625q0.25 -0.234375 0.828125 -0.234375l0.953125 0l0 -3.890625l-0.578125 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.21875 -0.25 -0.578125q0 -0.359375 0.25 -0.578125q0.25 -0.21875 0.828125 -0.21875l2.171875 0zm13.9609375 4.359375l-6.578125 0q0.25 0.625 0.890625 1.015625q0.640625 0.375 1.71875 0.375q0.890625 0 2.375 -0.390625q0.609375 -0.15625 0.84375 -0.15625q0.3125 0 0.53125 0.234375q0.21875 0.21875 0.21875 0.5625q0 0.3125 -0.234375 0.53125q-0.3125 0.296875 -1.53125 0.5625q-1.203125 0.265625 -2.3125 0.265625q-1.921875 0 -3.078125 -1.09375q-1.15625 -1.09375 -1.15625 -2.671875q0 -1.6875 1.25 -2.75q1.25 -1.0625 2.875 -1.0625q0.96875 0 1.78125 0.34375q0.828125 0.34375 1.21875 0.75q0.5625 0.578125 0.9375 1.421875q0.25 0.59375 0.25 1.375l0 0.6875zm-1.78125 -1.609375q-0.359375 -0.6875 -0.953125 -1.015625q-0.59375 -0.34375 -1.421875 -0.34375q-0.8125 0 -1.40625 0.34375q-0.59375 0.328125 -0.96875 1.015625l4.75 0zm6.4296875 1.09375l0 1.640625l1.625 0q0.578125 0 0.8125 0.234375q0.25 0.21875 0.25 0.578125q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-3.46875 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.34375 0.25 -0.5625q0.25 -0.234375 0.828125 -0.234375l0.25 0l0 -6.265625l-0.25 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.21875 -0.25 -0.578125q0 -0.359375 0.25 -0.578125q0.25 -0.234375 0.828125 -0.234375l3.6875 0.015625q1.625 0 2.5625 0.890625q0.953125 0.875 0.953125 2.15625q0 0.703125 -0.3125 1.328125q-0.25 0.46875 -0.8125 0.921875q-0.5625 0.453125 -1.15625 0.6875q-0.59375 0.234375 -1.5625 0.234375l-1.515625 0zm0 -1.609375l1.484375 0q1.046875 0 1.65625 -0.46875q0.625 -0.46875 0.625 -1.140625q0 -0.5625 -0.5 -0.984375q-0.5 -0.421875 -1.421875 -0.421875l-1.84375 0l0 3.015625zm14.6953125 1.4375q0 0.921875 -0.515625 1.796875q-0.515625 0.859375 -1.53125 1.375q-1.0 0.515625 -2.109375 0.515625q-1.09375 0 -2.09375 -0.5q-1.0 -0.515625 -1.53125 -1.375q-0.515625 -0.875 -0.515625 -1.828125q0 -0.953125 0.53125 -1.875q0.53125 -0.9375 1.53125 -1.46875q1.0 -0.53125 2.078125 -0.53125q1.09375 0 2.109375 0.546875q1.015625 0.546875 1.53125 1.46875q0.515625 0.90625 0.515625 1.875zm-1.609375 0.015625q0 -0.78125 -0.546875 -1.421875q-0.765625 -0.875 -2.0 -0.875q-1.078125 0 -1.8125 0.703125q-0.71875 0.6875 -0.71875 1.59375q0 0.75 0.734375 1.40625q0.734375 0.65625 1.796875 0.65625q1.078125 0 1.8125 -0.65625q0.734375 -0.65625 0.734375 -1.40625zm8.7734375 -1.8125q-0.390625 -0.25 -0.828125 -0.359375q-0.421875 -0.125 -0.890625 -0.125q-0.921875 0 -1.46875 0.296875q-0.25 0.140625 -0.25 0.296875q0 0.171875 0.328125 0.34375q0.25 0.125 1.125 0.25q1.59375 0.21875 2.21875 0.4375q0.8125 0.28125 1.25 0.859375q0.453125 0.5625 0.453125 1.203125q0 0.859375 -0.75 1.4375q-1.09375 0.84375 -2.828125 0.84375q-0.6875 0 -1.28125 -0.125q-0.59375 -0.125 -1.078125 -0.359375q-0.125 0.09375 -0.265625 0.15625q-0.125 0.046875 -0.265625 0.046875q-0.375 0 -0.59375 -0.234375q-0.21875 -0.25 -0.21875 -0.828125l0 -0.546875q0 -0.578125 0.21875 -0.8125q0.21875 -0.25 0.578125 -0.25q0.296875 0 0.484375 0.15625q0.203125 0.15625 0.3125 0.546875q0.359375 0.3125 0.875 0.484375q0.515625 0.15625 1.1875 0.15625q1.109375 0 1.71875 -0.34375q0.28125 -0.171875 0.28125 -0.359375q0 -0.3125 -0.40625 -0.515625q-0.421875 -0.203125 -1.71875 -0.34375q-1.921875 -0.203125 -2.578125 -0.78125q-0.640625 -0.578125 -0.640625 -1.40625q0 -0.859375 0.71875 -1.4375q0.984375 -0.78125 2.578125 -0.78125q0.5625 0 1.0625 0.109375q0.515625 0.109375 0.984375 0.328125q0.15625 -0.109375 0.28125 -0.15625q0.125 -0.0625 0.234375 -0.0625q0.328125 0 0.546875 0.25q0.21875 0.234375 0.21875 0.8125l0 0.390625q0 0.53125 -0.125 0.71875q-0.25 0.359375 -0.671875 0.359375q-0.296875 0 -0.515625 -0.171875q-0.21875 -0.1875 -0.28125 -0.484375zm8.4453125 -4.921875l0 1.703125l-1.90625 0l0 -1.703125l1.90625 0zm0.21875 3.046875l0 5.484375l1.921875 0q0.578125 0 0.828125 0.234375q0.25 0.21875 0.25 0.578125q0 0.34375 -0.25 0.578125q-0.25 0.21875 -0.828125 0.21875l-5.4375 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.34375 0.25 -0.5625q0.25 -0.234375 0.828125 -0.234375l1.921875 0l0 -3.890625l-1.296875 0q-0.5625 0 -0.8125 -0.21875q-0.25 -0.21875 -0.25 -0.578125q0 -0.359375 0.234375 -0.578125q0.25 -0.21875 0.828125 -0.21875l2.890625 0zm10.007805 3.40625l2.4375 2.078125q0.4375 0.03125 0.65625 0.25q0.21875 0.21875 0.21875 0.5625q0 0.34375 -0.25 0.578125q-0.25 0.21875 -0.828125 0.21875l-1.8125 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.28125 0.171875 -0.5q0.1875 -0.21875 0.484375 -0.296875l-1.1875 -1.03125l-1.2187424 1.03125q0.35936737 0.09375 0.5156174 0.296875q0.171875 0.1875 0.171875 0.5q0 0.359375 -0.25 0.59375q-0.23436737 0.21875 -0.8124924 0.21875l-1.8125 0q-0.578125 0 -0.828125 -0.21875q-0.234375 -0.234375 -0.234375 -0.59375q0 -0.328125 0.21875 -0.546875q0.21875 -0.21875 0.65625 -0.25l2.375 -2.09375l-2.109375 -1.796875q-0.40625 -0.03125 -0.625 -0.25q-0.21875 -0.21875 -0.21875 -0.546875q0 -0.359375 0.25 -0.578125q0.25 -0.21875 0.828125 -0.21875l1.5 0q0.578125 0 0.8124924 0.21875q0.25 0.21875 0.25 0.5625q0 0.46875 -0.43749237 0.75l0.9687424 0.8125l0.953125 -0.828125q-0.421875 -0.296875 -0.421875 -0.703125q0 -0.375 0.234375 -0.59375q0.25 -0.21875 0.828125 -0.21875l1.484375 0q0.578125 0 0.828125 0.21875q0.25 0.21875 0.25 0.578125q0 0.328125 -0.21875 0.546875q-0.21875 0.21875 -0.640625 0.25l-2.109375 1.8125zm7.4765625 0.4375l0 1.640625l1.625 0q0.578125 0 0.8125 0.234375q0.25 0.21875 0.25 0.578125q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-3.46875 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.34375 0.25 -0.5625q0.25 -0.234375 0.828125 -0.234375l0.25 0l0 -6.265625l-0.25 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.21875 -0.25 -0.578125q0 -0.359375 0.25 -0.578125q0.25 -0.234375 0.828125 -0.234375l3.6875 0.015625q1.625 0 2.5625 0.890625q0.953125 0.875 0.953125 2.15625q0 0.703125 -0.3125 1.328125q-0.25 0.46875 -0.8125 0.921875q-0.5625 0.453125 -1.15625 0.6875q-0.59375 0.234375 -1.5625 0.234375l-1.515625 0zm0 -1.609375l1.484375 0q1.046875 0 1.65625 -0.46875q0.625 -0.46875 0.625 -1.140625q0 -0.5625 -0.5 -0.984375q-0.5 -0.421875 -1.421875 -0.421875l-1.84375 0l0 3.015625zm11.9765625 4.859375l0 -0.375q-0.609375 0.3125 -1.34375 0.46875q-0.71875 0.171875 -1.3125 0.171875q-1.28125 0 -2.09375 -0.6875q-0.796875 -0.6875 -0.796875 -1.515625q0 -1.0 1.015625 -1.859375q1.03125 -0.875 2.84375 -0.875q0.734375 0 1.6875 0.15625l0 -0.375q0 -0.359375 -0.3125 -0.578125q-0.3125 -0.234375 -1.171875 -0.234375q-0.71875 0 -1.84375 0.28125q-0.421875 0.09375 -0.65625 0.09375q-0.328125 0 -0.546875 -0.21875q-0.21875 -0.234375 -0.21875 -0.59375q0 -0.203125 0.078125 -0.34375q0.078125 -0.15625 0.21875 -0.25q0.140625 -0.09375 0.578125 -0.21875q0.59375 -0.15625 1.203125 -0.25q0.625 -0.109375 1.125 -0.109375q1.5 0 2.3125 0.65625q0.828125 0.640625 0.828125 1.75l0 3.296875l0.28125 0q0.578125 0 0.8125 0.234375q0.25 0.21875 0.25 0.578125q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-1.875 0zm0 -2.875q-0.96875 -0.1875 -1.78125 -0.1875q-0.96875 0 -1.671875 0.484375q-0.4375 0.296875 -0.4375 0.609375q0 0.234375 0.203125 0.375q0.390625 0.25 1.078125 0.25q0.578125 0 1.296875 -0.21875q0.734375 -0.234375 1.3125 -0.625l0 -0.6875zm7.7578125 -2.625l0 3.21875q0 0.515625 0.21875 0.671875q0.328125 0.265625 1.171875 0.265625q1.21875 0 2.265625 -0.53125q0.390625 -0.203125 0.625 -0.203125q0.3125 0 0.53125 0.234375q0.234375 0.234375 0.234375 0.578125q0 0.3125 -0.25 0.53125q-0.375 0.375 -1.515625 0.6875q-1.125 0.3125 -1.890625 0.3125q-1.5 0 -2.25 -0.640625q-0.734375 -0.65625 -0.734375 -1.59375l0 -3.53125l-0.578125 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.21875 -0.25 -0.578125q0 -0.359375 0.25 -0.578125q0.25 -0.21875 0.828125 -0.21875l0.578125 0l0 -1.453125q0 -0.578125 0.21875 -0.8125q0.21875 -0.25 0.578125 -0.25q0.359375 0 0.578125 0.25q0.21875 0.234375 0.21875 0.8125l0 1.453125l2.96875 0q0.578125 0 0.8125 0.21875q0.25 0.21875 0.25 0.578125q0 0.359375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-2.96875 0zm8.3515625 -4.640625l0 3.421875q0.5 -0.296875 1.0 -0.4375q0.515625 -0.15625 1.046875 -0.15625q0.828125 0 1.46875 0.28125q0.65625 0.28125 1.078125 0.890625q0.421875 0.609375 0.421875 1.53125l0 3.0q0.609375 0 0.796875 0.125q0.375 0.234375 0.375 0.6875q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-1.828125 0q-0.5625 0 -0.8125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.4375 0.375 -0.671875q0.203125 -0.125 0.796875 -0.125l0 -2.890625q0 -0.625 -0.28125 -0.875q-0.359375 -0.328125 -1.078125 -0.328125q-0.53125 0 -0.953125 0.203125q-0.40625 0.203125 -1.09375 0.890625l0 3.0q0.609375 0 0.796875 0.125q0.375 0.234375 0.375 0.6875q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-1.828125 0q-0.578125 0 -0.828125 -0.21875q-0.234375 -0.234375 -0.234375 -0.59375q0 -0.4375 0.375 -0.671875q0.1875 -0.125 0.796875 -0.125l0 -6.921875l-0.265625 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.34375 0.25 -0.5625q0.25 -0.234375 0.828125 -0.234375l1.875 0z" fill-rule="nonzero"></path><path fill="#ffffff" d="m336.0 143.46457l187.43304 0l0 80.53543l-187.43304 0z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m336.0 143.46457l187.43304 0l0 80.53543l-187.43304 0z" fill-rule="nonzero"></path><path fill="#000000" d="m361.5798 186.34229l0 1.640625l1.625 0q0.578125 0 0.8125 0.234375q0.25 0.21875 0.25 0.578125q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-3.46875 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.34375 0.25 -0.5625q0.25 -0.234375 0.828125 -0.234375l0.25 0l0 -6.265625l-0.25 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.21875 -0.25 -0.578125q0 -0.359375 0.25 -0.578125q0.25 -0.234375 0.828125 -0.234375l3.6875 0.015625q1.625 0 2.5625 0.890625q0.953125 0.875 0.953125 2.15625q0 0.703125 -0.3125 1.328125q-0.25 0.46875 -0.8125 0.921875q-0.5625 0.453125 -1.15625 0.6875q-0.59375 0.234375 -1.5625 0.234375l-1.515625 0zm0 -1.609375l1.484375 0q1.046875 0 1.65625 -0.46875q0.625 -0.46875 0.625 -1.140625q0 -0.5625 -0.5 -0.984375q-0.5 -0.421875 -1.421875 -0.421875l-1.84375 0l0 3.015625zm13.9296875 -2.234375l0 5.484375q0.515625 0 0.75 0.234375q0.25 0.21875 0.25 0.578125q0 0.34375 -0.25 0.578125q-0.25 0.21875 -0.828125 0.21875l-1.515625 0l0 -0.375q-0.6875 0.3125 -1.3125 0.46875q-0.625 0.171875 -1.1875 0.171875q-0.796875 0 -1.375 -0.328125q-0.578125 -0.34375 -0.90625 -0.9375q-0.25 -0.421875 -0.25 -1.046875l0 -3.453125l-0.265625 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.21875 -0.25 -0.578125q0 -0.359375 0.25 -0.578125q0.25 -0.21875 0.828125 -0.21875l1.875 0l0 4.765625q0 0.5 0.234375 0.75q0.25 0.234375 0.765625 0.234375q0.484375 0 1.03125 -0.1875q0.5625 -0.203125 1.390625 -0.703125l0 -3.265625l-0.578125 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.21875 -0.25 -0.578125q0 -0.359375 0.25 -0.578125q0.25 -0.21875 0.828125 -0.21875l2.171875 0zm5.8671875 0l0 1.0q1.015625 -0.734375 1.59375 -0.96875q0.578125 -0.25 1.09375 -0.25q0.78125 0 1.515625 0.578125q0.5 0.390625 0.5 0.796875q0 0.34375 -0.25 0.59375q-0.234375 0.234375 -0.5625 0.234375q-0.296875 0 -0.625 -0.296875q-0.328125 -0.296875 -0.59375 -0.296875q-0.328125 0 -1.0 0.421875q-0.671875 0.421875 -1.671875 1.265625l0 2.40625l2.28125 0q0.578125 0 0.828125 0.234375q0.25 0.21875 0.25 0.578125q0 0.34375 -0.25 0.578125q-0.25 0.21875 -0.828125 0.21875l-4.828125 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.34375 0.25 -0.5625q0.25 -0.234375 0.828125 -0.234375l0.953125 0l0 -3.890625l-0.578125 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.21875 -0.25 -0.578125q0 -0.359375 0.25 -0.578125q0.25 -0.21875 0.828125 -0.21875l2.171875 0zm13.9609375 4.359375l-6.578125 0q0.25 0.625 0.890625 1.015625q0.640625 0.375 1.71875 0.375q0.890625 0 2.375 -0.390625q0.609375 -0.15625 0.84375 -0.15625q0.3125 0 0.53125 0.234375q0.21875 0.21875 0.21875 0.5625q0 0.3125 -0.234375 0.53125q-0.3125 0.296875 -1.53125 0.5625q-1.203125 0.265625 -2.3125 0.265625q-1.921875 0 -3.078125 -1.09375q-1.15625 -1.09375 -1.15625 -2.671875q0 -1.6875 1.25 -2.75q1.25 -1.0625 2.875 -1.0625q0.96875 0 1.78125 0.34375q0.828125 0.34375 1.21875 0.75q0.5625 0.578125 0.9375 1.421875q0.25 0.59375 0.25 1.375l0 0.6875zm-1.78125 -1.609375q-0.359375 -0.6875 -0.953125 -1.015625q-0.59375 -0.34375 -1.421875 -0.34375q-0.8125 0 -1.40625 0.34375q-0.59375 0.328125 -0.96875 1.015625l4.75 0zm7.3671875 -0.203125l-1.484375 4.546875l-1.796875 0l-0.953125 -7.875q-0.375 -0.046875 -0.5625 -0.25q-0.1875 -0.203125 -0.1875 -0.53125q0 -0.375 0.25 -0.59375q0.25 -0.234375 0.828125 -0.234375l2.125 0.015625q0.578125 0 0.828125 0.21875q0.25 0.21875 0.25 0.578125q0 0.359375 -0.25 0.578125q-0.25 0.21875 -0.828125 0.21875l-0.828125 0l0.53125 4.484375l1.25 -3.734375l1.671875 0l1.25 3.734375l0.53125 -4.484375l-0.84375 0q-0.578125 0 -0.828125 -0.21875q-0.234375 -0.21875 -0.234375 -0.578125q0 -0.359375 0.234375 -0.578125q0.25 -0.234375 0.828125 -0.234375l2.125 0.015625q0.578125 0 0.828125 0.21875q0.25 0.21875 0.25 0.578125q0 0.296875 -0.203125 0.515625q-0.1875 0.21875 -0.5625 0.28125l-0.921875 7.875l-1.765625 0l-1.53125 -4.546875zm10.1640625 -5.59375l0 1.703125l-1.90625 0l0 -1.703125l1.90625 0zm0.21875 3.046875l0 5.484375l1.921875 0q0.578125 0 0.828125 0.234375q0.25 0.21875 0.25 0.578125q0 0.34375 -0.25 0.578125q-0.25 0.21875 -0.828125 0.21875l-5.4375 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.34375 0.25 -0.5625q0.25 -0.234375 0.828125 -0.234375l1.921875 0l0 -3.890625l-1.296875 0q-0.5625 0 -0.8125 -0.21875q-0.25 -0.21875 -0.25 -0.578125q0 -0.359375 0.234375 -0.578125q0.25 -0.21875 0.828125 -0.21875l2.890625 0zm7.1953125 0l0 0.53125q0.4375 -0.375 0.953125 -0.5625q0.53125 -0.1875 1.15625 -0.1875q1.421875 0 2.25 0.890625q0.65625 0.703125 0.65625 1.84375l0 2.96875q0.5 0 0.734375 0.234375q0.25 0.21875 0.25 0.578125q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-1.453125 0q-0.578125 0 -0.828125 -0.21875q-0.234375 -0.234375 -0.234375 -0.59375q0 -0.34375 0.234375 -0.5625q0.25 -0.234375 0.75 -0.234375l0 -3.015625q0 -0.53125 -0.28125 -0.765625q-0.359375 -0.3125 -1.09375 -0.3125q-0.5625 0 -0.984375 0.21875q-0.40625 0.203125 -1.046875 0.90625l0 2.96875q0.609375 0 0.796875 0.125q0.375 0.234375 0.375 0.6875q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-1.828125 0q-0.578125 0 -0.828125 -0.21875q-0.234375 -0.234375 -0.234375 -0.59375q0 -0.4375 0.375 -0.671875q0.1875 -0.125 0.796875 -0.125l0 -3.890625q-0.5 0 -0.75 -0.21875q-0.234375 -0.234375 -0.234375 -0.578125q0 -0.359375 0.234375 -0.578125q0.25 -0.21875 0.828125 -0.21875l1.53125 0zm14.8984375 -3.046875l0 8.53125l0.265625 0q0.578125 0 0.828125 0.234375q0.25 0.21875 0.25 0.578125q0 0.34375 -0.25 0.578125q-0.25 0.21875 -0.828125 0.21875l-1.875 0l0 -0.390625q-0.546875 0.3125 -1.140625 0.484375q-0.59375 0.171875 -1.234375 0.171875q-1.8125 0 -2.921875 -1.046875q-1.09375 -1.046875 -1.09375 -2.609375q0 -1.625 1.15625 -2.765625q1.15625 -1.15625 2.828125 -1.15625q0.625 0 1.21875 0.203125q0.609375 0.1875 1.1875 0.5625l0 -1.984375l-0.265625 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.234375 -0.25 -0.578125q0 -0.359375 0.25 -0.578125q0.25 -0.234375 0.828125 -0.234375l1.875 0zm-1.609375 6.796875q0 -0.984375 -0.703125 -1.671875q-0.6875 -0.6875 -1.6875 -0.6875q-1.0 0 -1.703125 0.6875q-0.6875 0.6875 -0.6875 1.65625q0 0.875 0.625 1.453125q0.625 0.5625 1.765625 0.5625q1.125 0 1.75 -0.5625q0.640625 -0.578125 0.640625 -1.4375zm11.6953125 -0.078125q0 0.921875 -0.515625 1.796875q-0.515625 0.859375 -1.53125 1.375q-1.0 0.515625 -2.109375 0.515625q-1.09375 0 -2.09375 -0.5q-1.0 -0.515625 -1.53125 -1.375q-0.515625 -0.875 -0.515625 -1.828125q0 -0.953125 0.53125 -1.875q0.53125 -0.9375 1.53125 -1.46875q1.0 -0.53125 2.078125 -0.53125q1.09375 0 2.109375 0.546875q1.015625 0.546875 1.53125 1.46875q0.515625 0.90625 0.515625 1.875zm-1.609375 0.015625q0 -0.78125 -0.546875 -1.421875q-0.765625 -0.875 -2.0 -0.875q-1.078125 0 -1.8125 0.703125q-0.71875 0.6875 -0.71875 1.59375q0 0.75 0.734375 1.40625q0.734375 0.65625 1.796875 0.65625q1.078125 0 1.8125 -0.65625q0.734375 -0.65625 0.734375 -1.40625zm7.0546875 0.453125l-1.109375 2.953125l-1.5 0l-1.34375 -5.5q-0.4375 -0.015625 -0.671875 -0.234375q-0.21875 -0.234375 -0.21875 -0.5625q0 -0.359375 0.25 -0.578125q0.25 -0.21875 0.828125 -0.21875l1.484375 0q0.578125 0 0.828125 0.21875q0.25 0.21875 0.25 0.578125q0 0.359375 -0.28125 0.609375q-0.21875 0.1875 -0.828125 0.1875l0.609375 2.546875l0.984375 -2.609375l1.421875 0l1.0 2.609375l0.625 -2.546875q-0.59375 0 -0.78125 -0.109375q-0.375 -0.25 -0.375 -0.6875q0 -0.359375 0.25 -0.578125q0.25 -0.21875 0.828125 -0.21875l1.5 0q0.578125 0 0.828125 0.21875q0.25 0.21875 0.25 0.578125q0 0.328125 -0.21875 0.546875q-0.21875 0.21875 -0.640625 0.25l-1.3125 5.5l-1.484375 0l-1.171875 -2.953125zm11.3203125 -2.265625q-0.390625 -0.25 -0.828125 -0.359375q-0.421875 -0.125 -0.890625 -0.125q-0.921875 0 -1.46875 0.296875q-0.25 0.140625 -0.25 0.296875q0 0.171875 0.328125 0.34375q0.25 0.125 1.125 0.25q1.59375 0.21875 2.21875 0.4375q0.8125 0.28125 1.25 0.859375q0.453125 0.5625 0.453125 1.203125q0 0.859375 -0.75 1.4375q-1.09375 0.84375 -2.828125 0.84375q-0.6875 0 -1.28125 -0.125q-0.59375 -0.125 -1.078125 -0.359375q-0.125 0.09375 -0.265625 0.15625q-0.125 0.046875 -0.265625 0.046875q-0.375 0 -0.59375 -0.234375q-0.21875 -0.25 -0.21875 -0.828125l0 -0.546875q0 -0.578125 0.21875 -0.8125q0.21875 -0.25 0.578125 -0.25q0.296875 0 0.484375 0.15625q0.203125 0.15625 0.3125 0.546875q0.359375 0.3125 0.875 0.484375q0.515625 0.15625 1.1875 0.15625q1.109375 0 1.71875 -0.34375q0.28125 -0.171875 0.28125 -0.359375q0 -0.3125 -0.40625 -0.515625q-0.421875 -0.203125 -1.71875 -0.34375q-1.921875 -0.203125 -2.578125 -0.78125q-0.640625 -0.578125 -0.640625 -1.40625q0 -0.859375 0.71875 -1.4375q0.984375 -0.78125 2.578125 -0.78125q0.5625 0 1.0625 0.109375q0.515625 0.109375 0.984375 0.328125q0.15625 -0.109375 0.28125 -0.15625q0.125 -0.0625 0.234375 -0.0625q0.328125 0 0.546875 0.25q0.21875 0.234375 0.21875 0.8125l0 0.390625q0 0.53125 -0.125 0.71875q-0.25 0.359375 -0.671875 0.359375q-0.296875 0 -0.515625 -0.171875q-0.21875 -0.1875 -0.28125 -0.484375zm6.9453125 1.96875l0 1.640625l1.625 0q0.578125 0 0.8125 0.234375q0.25 0.21875 0.25 0.578125q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-3.46875 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.34375 0.25 -0.5625q0.25 -0.234375 0.828125 -0.234375l0.25 0l0 -6.265625l-0.25 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.21875 -0.25 -0.578125q0 -0.359375 0.25 -0.578125q0.25 -0.234375 0.828125 -0.234375l3.6875 0.015625q1.625 0 2.5625 0.890625q0.953125 0.875 0.953125 2.15625q0 0.703125 -0.3125 1.328125q-0.25 0.46875 -0.8125 0.921875q-0.5625 0.453125 -1.15625 0.6875q-0.59375 0.234375 -1.5625 0.234375l-1.515625 0zm0 -1.609375l1.484375 0q1.046875 0 1.65625 -0.46875q0.625 -0.46875 0.625 -1.140625q0 -0.5625 -0.5 -0.984375q-0.5 -0.421875 -1.421875 -0.421875l-1.84375 0l0 3.015625zm11.9765625 4.859375l0 -0.375q-0.609375 0.3125 -1.34375 0.46875q-0.71875 0.171875 -1.3125 0.171875q-1.28125 0 -2.09375 -0.6875q-0.796875 -0.6875 -0.796875 -1.515625q0 -1.0 1.015625 -1.859375q1.03125 -0.875 2.84375 -0.875q0.734375 0 1.6875 0.15625l0 -0.375q0 -0.359375 -0.3125 -0.578125q-0.3125 -0.234375 -1.171875 -0.234375q-0.71875 0 -1.84375 0.28125q-0.421875 0.09375 -0.65625 0.09375q-0.328125 0 -0.546875 -0.21875q-0.21875 -0.234375 -0.21875 -0.59375q0 -0.203125 0.078125 -0.34375q0.078125 -0.15625 0.21875 -0.25q0.140625 -0.09375 0.578125 -0.21875q0.59375 -0.15625 1.203125 -0.25q0.625 -0.109375 1.125 -0.109375q1.5 0 2.3125 0.65625q0.828125 0.640625 0.828125 1.75l0 3.296875l0.28125 0q0.578125 0 0.8125 0.234375q0.25 0.21875 0.25 0.578125q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-1.875 0zm0 -2.875q-0.96875 -0.1875 -1.78125 -0.1875q-0.96875 0 -1.671875 0.484375q-0.4375 0.296875 -0.4375 0.609375q0 0.234375 0.203125 0.375q0.390625 0.25 1.078125 0.25q0.578125 0 1.296875 -0.21875q0.734375 -0.234375 1.3125 -0.625l0 -0.6875zm7.7578125 -2.625l0 3.21875q0 0.515625 0.21875 0.671875q0.328125 0.265625 1.171875 0.265625q1.21875 0 2.265625 -0.53125q0.390625 -0.203125 0.625 -0.203125q0.3125 0 0.53125 0.234375q0.234375 0.234375 0.234375 0.578125q0 0.3125 -0.25 0.53125q-0.375 0.375 -1.515625 0.6875q-1.125 0.3125 -1.890625 0.3125q-1.5 0 -2.25 -0.640625q-0.734375 -0.65625 -0.734375 -1.59375l0 -3.53125l-0.578125 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.21875 -0.25 -0.578125q0 -0.359375 0.25 -0.578125q0.25 -0.21875 0.828125 -0.21875l0.578125 0l0 -1.453125q0 -0.578125 0.21875 -0.8125q0.21875 -0.25 0.578125 -0.25q0.359375 0 0.578125 0.25q0.21875 0.234375 0.21875 0.8125l0 1.453125l2.96875 0q0.578125 0 0.8125 0.21875q0.25 0.21875 0.25 0.578125q0 0.359375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-2.96875 0zm8.3515625 -4.640625l0 3.421875q0.5 -0.296875 1.0 -0.4375q0.515625 -0.15625 1.046875 -0.15625q0.828125 0 1.46875 0.28125q0.65625 0.28125 1.078125 0.890625q0.421875 0.609375 0.421875 1.53125l0 3.0q0.609375 0 0.796875 0.125q0.375 0.234375 0.375 0.6875q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-1.828125 0q-0.5625 0 -0.8125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.4375 0.375 -0.671875q0.203125 -0.125 0.796875 -0.125l0 -2.890625q0 -0.625 -0.28125 -0.875q-0.359375 -0.328125 -1.078125 -0.328125q-0.53125 0 -0.953125 0.203125q-0.40625 0.203125 -1.09375 0.890625l0 3.0q0.609375 0 0.796875 0.125q0.375 0.234375 0.375 0.6875q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-1.828125 0q-0.578125 0 -0.828125 -0.21875q-0.234375 -0.234375 -0.234375 -0.59375q0 -0.4375 0.375 -0.671875q0.1875 -0.125 0.796875 -0.125l0 -6.921875l-0.265625 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.34375 0.25 -0.5625q0.25 -0.234375 0.828125 -0.234375l1.875 0z" fill-rule="nonzero"></path><path fill="#ffffff" d="m176.0 271.46457l187.43307 0l0 80.53543l-187.43307 0z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m176.0 271.46457l187.43307 0l0 80.53543l-187.43307 0z" fill-rule="nonzero"></path><path fill="#000000" d="m254.3884 314.3423l0 1.640625l1.625 0q0.578125 0 0.8125 0.234375q0.25 0.21875 0.25 0.578125q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-3.46875 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.34375 0.25 -0.5625q0.25 -0.234375 0.828125 -0.234375l0.25 0l0 -6.265625l-0.25 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.21875 -0.25 -0.578125q0 -0.359375 0.25 -0.578125q0.25 -0.234375 0.828125 -0.234375l3.6875 0.015625q1.625 0 2.5625 0.890625q0.953125 0.875 0.953125 2.15625q0 0.703125 -0.3125 1.328125q-0.25 0.46875 -0.8125 0.921875q-0.5625 0.453125 -1.15625 0.6875q-0.59375 0.234375 -1.5625 0.234375l-1.515625 0zm0 -1.609375l1.484375 0q1.046875 0 1.65625 -0.46875q0.625 -0.46875 0.625 -1.140625q0 -0.5625 -0.5 -0.984375q-0.5 -0.421875 -1.421875 -0.421875l-1.84375 0l0 3.015625zm11.9765625 4.859375l0 -0.375q-0.609375 0.3125 -1.34375 0.46875q-0.71875 0.171875 -1.3125 0.171875q-1.28125 0 -2.09375 -0.6875q-0.796875 -0.6875 -0.796875 -1.515625q0 -1.0 1.015625 -1.859375q1.03125 -0.875 2.84375 -0.875q0.734375 0 1.6875 0.15625l0 -0.375q0 -0.359375 -0.3125 -0.578125q-0.3125 -0.234375 -1.171875 -0.234375q-0.71875 0 -1.84375 0.28125q-0.421875 0.09375 -0.65625 0.09375q-0.328125 0 -0.546875 -0.21875q-0.21875 -0.234375 -0.21875 -0.59375q0 -0.203125 0.078125 -0.34375q0.078125 -0.15625 0.21875 -0.25q0.140625 -0.09375 0.578125 -0.21875q0.59375 -0.15625 1.203125 -0.25q0.625 -0.109375 1.125 -0.109375q1.5 0 2.3125 0.65625q0.828125 0.640625 0.828125 1.75l0 3.296875l0.28125 0q0.578125 0 0.8125 0.234375q0.25 0.21875 0.25 0.578125q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-1.875 0zm0 -2.875q-0.96875 -0.1875 -1.78125 -0.1875q-0.96875 0 -1.671875 0.484375q-0.4375 0.296875 -0.4375 0.609375q0 0.234375 0.203125 0.375q0.390625 0.25 1.078125 0.25q0.578125 0 1.296875 -0.21875q0.734375 -0.234375 1.3125 -0.625l0 -0.6875zm7.7578125 -2.625l0 3.21875q0 0.515625 0.21875 0.671875q0.328125 0.265625 1.171875 0.265625q1.21875 0 2.265625 -0.53125q0.390625 -0.203125 0.625 -0.203125q0.3125 0 0.53125 0.234375q0.234375 0.234375 0.234375 0.578125q0 0.3125 -0.25 0.53125q-0.375 0.375 -1.515625 0.6875q-1.125 0.3125 -1.890625 0.3125q-1.5 0 -2.25 -0.640625q-0.734375 -0.65625 -0.734375 -1.59375l0 -3.53125l-0.578125 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.21875 -0.25 -0.578125q0 -0.359375 0.25 -0.578125q0.25 -0.21875 0.828125 -0.21875l0.578125 0l0 -1.453125q0 -0.578125 0.21875 -0.8125q0.21875 -0.25 0.578125 -0.25q0.359375 0 0.578125 0.25q0.21875 0.234375 0.21875 0.8125l0 1.453125l2.96875 0q0.578125 0 0.8125 0.21875q0.25 0.21875 0.25 0.578125q0 0.359375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-2.96875 0zm8.3515625 -4.640625l0 3.421875q0.5 -0.296875 1.0 -0.4375q0.515625 -0.15625 1.046875 -0.15625q0.828125 0 1.46875 0.28125q0.65625 0.28125 1.078125 0.890625q0.421875 0.609375 0.421875 1.53125l0 3.0q0.609375 0 0.796875 0.125q0.375 0.234375 0.375 0.6875q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-1.828125 0q-0.5625 0 -0.8125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.4375 0.375 -0.671875q0.203125 -0.125 0.796875 -0.125l0 -2.890625q0 -0.625 -0.28125 -0.875q-0.359375 -0.328125 -1.078125 -0.328125q-0.53125 0 -0.953125 0.203125q-0.40625 0.203125 -1.09375 0.890625l0 3.0q0.609375 0 0.796875 0.125q0.375 0.234375 0.375 0.6875q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-1.828125 0q-0.578125 0 -0.828125 -0.21875q-0.234375 -0.234375 -0.234375 -0.59375q0 -0.4375 0.375 -0.671875q0.1875 -0.125 0.796875 -0.125l0 -6.921875l-0.265625 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.34375 0.25 -0.5625q0.25 -0.234375 0.828125 -0.234375l1.875 0z" fill-rule="nonzero"></path><path fill="#ffffff" d="m16.0 400.0l187.43306 0l0 80.53543l-187.43306 0z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m16.0 400.0l187.43306 0l0 80.53543l-187.43306 0z" fill-rule="nonzero"></path><path fill="#000000" d="m70.3845 442.87772l0 1.640625l1.625 0q0.578125 0 0.8125 0.234375q0.25 0.21875 0.25 0.578125q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-3.46875 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.34375 0.25 -0.5625q0.25 -0.234375 0.828125 -0.234375l0.25 0l0 -6.265625l-0.25 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.21875 -0.25 -0.578125q0 -0.359375 0.25 -0.578125q0.25 -0.234375 0.828125 -0.234375l3.6875 0.015625q1.625 0 2.5625 0.890625q0.953125 0.875 0.953125 2.15625q0 0.703125 -0.3125 1.328125q-0.25 0.46875 -0.8125 0.921875q-0.5625 0.453125 -1.15625 0.6875q-0.59375 0.234375 -1.5625 0.234375l-1.515625 0zm0 -1.609375l1.484375 0q1.046875 0 1.65625 -0.46875q0.625 -0.46875 0.625 -1.140625q0 -0.5625 -0.5 -0.984375q-0.5 -0.421875 -1.421875 -0.421875l-1.84375 0l0 3.015625zm14.6953125 1.4375q0 0.921875 -0.515625 1.796875q-0.515625 0.859375 -1.53125 1.375q-1.0 0.515625 -2.109375 0.515625q-1.09375 0 -2.09375 -0.5q-1.0 -0.515625 -1.53125 -1.375q-0.515625 -0.875 -0.515625 -1.828125q0 -0.953125 0.53125 -1.875q0.53125 -0.9375 1.53125 -1.46875q1.0 -0.53125 2.078125 -0.53125q1.09375 0 2.109375 0.546875q1.015625 0.546875 1.53125 1.46875q0.515625 0.90625 0.515625 1.875zm-1.609375 0.015625q0 -0.78125 -0.546875 -1.421875q-0.765625 -0.875 -2.0 -0.875q-1.078125 0 -1.8125 0.703125q-0.71875 0.6875 -0.71875 1.59375q0 0.75 0.734375 1.40625q0.734375 0.65625 1.796875 0.65625q1.078125 0 1.8125 -0.65625q0.734375 -0.65625 0.734375 -1.40625zm8.7734375 -1.8125q-0.390625 -0.25 -0.828125 -0.359375q-0.421875 -0.125 -0.890625 -0.125q-0.921875 0 -1.46875 0.296875q-0.25 0.140625 -0.25 0.296875q0 0.171875 0.328125 0.34375q0.25 0.125 1.125 0.25q1.59375 0.21875 2.21875 0.4375q0.8125 0.28125 1.25 0.859375q0.453125 0.5625 0.453125 1.203125q0 0.859375 -0.75 1.4375q-1.09375 0.84375 -2.828125 0.84375q-0.6875 0 -1.28125 -0.125q-0.59375 -0.125 -1.078125 -0.359375q-0.125 0.09375 -0.265625 0.15625q-0.125 0.046875 -0.265625 0.046875q-0.375 0 -0.59375 -0.234375q-0.21875 -0.25 -0.21875 -0.828125l0 -0.546875q0 -0.578125 0.21875 -0.8125q0.21875 -0.25 0.578125 -0.25q0.296875 0 0.484375 0.15625q0.203125 0.15625 0.3125 0.546875q0.359375 0.3125 0.875 0.484375q0.515625 0.15625 1.1875 0.15625q1.109375 0 1.71875 -0.34375q0.28125 -0.171875 0.28125 -0.359375q0 -0.3125 -0.40625 -0.515625q-0.421875 -0.203125 -1.71875 -0.34375q-1.921875 -0.203125 -2.578125 -0.78125q-0.640625 -0.578125 -0.640625 -1.40625q0 -0.859375 0.71875 -1.4375q0.984375 -0.78125 2.578125 -0.78125q0.5625 0 1.0625 0.109375q0.515625 0.109375 0.984375 0.328125q0.15625 -0.109375 0.28125 -0.15625q0.125 -0.0625 0.234375 -0.0625q0.328125 0 0.546875 0.25q0.21875 0.234375 0.21875 0.8125l0 0.390625q0 0.53125 -0.125 0.71875q-0.25 0.359375 -0.671875 0.359375q-0.296875 0 -0.515625 -0.171875q-0.21875 -0.1875 -0.28125 -0.484375zm8.4453125 -4.921875l0 1.703125l-1.90625 0l0 -1.703125l1.90625 0zm0.21875 3.046875l0 5.484375l1.921875 0q0.578125 0 0.828125 0.234375q0.25 0.21875 0.25 0.578125q0 0.34375 -0.25 0.578125q-0.25 0.21875 -0.828125 0.21875l-5.4375 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.34375 0.25 -0.5625q0.25 -0.234375 0.828125 -0.234375l1.921875 0l0 -3.890625l-1.296875 0q-0.5625 0 -0.8125 -0.21875q-0.25 -0.21875 -0.25 -0.578125q0 -0.359375 0.234375 -0.578125q0.25 -0.21875 0.828125 -0.21875l2.890625 0zm10.0078125 3.40625l2.4375 2.078125q0.4375 0.03125 0.65625 0.25q0.21875 0.21875 0.21875 0.5625q0 0.34375 -0.25 0.578125q-0.25 0.21875 -0.828125 0.21875l-1.8125 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.28125 0.171875 -0.5q0.1875 -0.21875 0.484375 -0.296875l-1.1875 -1.03125l-1.21875 1.03125q0.359375 0.09375 0.515625 0.296875q0.171875 0.1875 0.171875 0.5q0 0.359375 -0.25 0.59375q-0.234375 0.21875 -0.8125 0.21875l-1.8125 0q-0.578125 0 -0.828125 -0.21875q-0.234375 -0.234375 -0.234375 -0.59375q0 -0.328125 0.21875 -0.546875q0.21875 -0.21875 0.65625 -0.25l2.375 -2.09375l-2.109375 -1.796875q-0.40625 -0.03125 -0.625 -0.25q-0.21875 -0.21875 -0.21875 -0.546875q0 -0.359375 0.25 -0.578125q0.25 -0.21875 0.828125 -0.21875l1.5 0q0.578125 0 0.8125 0.21875q0.25 0.21875 0.25 0.5625q0 0.46875 -0.4375 0.75l0.96875 0.8125l0.953125 -0.828125q-0.421875 -0.296875 -0.421875 -0.703125q0 -0.375 0.234375 -0.59375q0.25 -0.21875 0.828125 -0.21875l1.484375 0q0.578125 0 0.828125 0.21875q0.25 0.21875 0.25 0.578125q0 0.328125 -0.21875 0.546875q-0.21875 0.21875 -0.640625 0.25l-2.109375 1.8125zm7.4765625 0.4375l0 1.640625l1.625 0q0.578125 0 0.8125 0.234375q0.25 0.21875 0.25 0.578125q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-3.46875 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.34375 0.25 -0.5625q0.25 -0.234375 0.828125 -0.234375l0.25 0l0 -6.265625l-0.25 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.21875 -0.25 -0.578125q0 -0.359375 0.25 -0.578125q0.25 -0.234375 0.828125 -0.234375l3.6875 0.015625q1.625 0 2.5625 0.890625q0.953125 0.875 0.953125 2.15625q0 0.703125 -0.3125 1.328125q-0.25 0.46875 -0.8125 0.921875q-0.5625 0.453125 -1.15625 0.6875q-0.59375 0.234375 -1.5625 0.234375l-1.515625 0zm0 -1.609375l1.484375 0q1.046875 0 1.65625 -0.46875q0.625 -0.46875 0.625 -1.140625q0 -0.5625 -0.5 -0.984375q-0.5 -0.421875 -1.421875 -0.421875l-1.84375 0l0 3.015625zm11.976555 4.859375l0 -0.375q-0.609375 0.3125 -1.34375 0.46875q-0.71875 0.171875 -1.3124924 0.171875q-1.28125 0 -2.09375 -0.6875q-0.796875 -0.6875 -0.796875 -1.515625q0 -1.0 1.015625 -1.859375q1.03125 -0.875 2.8437424 -0.875q0.734375 0 1.6875 0.15625l0 -0.375q0 -0.359375 -0.3125 -0.578125q-0.3125 -0.234375 -1.171875 -0.234375q-0.71875 0 -1.8437424 0.28125q-0.421875 0.09375 -0.65625 0.09375q-0.328125 0 -0.546875 -0.21875q-0.21875 -0.234375 -0.21875 -0.59375q0 -0.203125 0.078125 -0.34375q0.078125 -0.15625 0.21875 -0.25q0.140625 -0.09375 0.578125 -0.21875q0.59375 -0.15625 1.203125 -0.25q0.6249924 -0.109375 1.1249924 -0.109375q1.5 0 2.3125 0.65625q0.828125 0.640625 0.828125 1.75l0 3.296875l0.28125 0q0.578125 0 0.8125 0.234375q0.25 0.21875 0.25 0.578125q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-1.875 0zm0 -2.875q-0.96875 -0.1875 -1.78125 -0.1875q-0.9687424 0 -1.6718674 0.484375q-0.4375 0.296875 -0.4375 0.609375q0 0.234375 0.203125 0.375q0.390625 0.25 1.078125 0.25q0.5781174 0 1.2968674 -0.21875q0.734375 -0.234375 1.3125 -0.625l0 -0.6875zm7.7578125 -2.625l0 3.21875q0 0.515625 0.21875 0.671875q0.328125 0.265625 1.171875 0.265625q1.21875 0 2.265625 -0.53125q0.390625 -0.203125 0.625 -0.203125q0.3125 0 0.53125 0.234375q0.234375 0.234375 0.234375 0.578125q0 0.3125 -0.25 0.53125q-0.375 0.375 -1.515625 0.6875q-1.125 0.3125 -1.890625 0.3125q-1.5 0 -2.25 -0.640625q-0.734375 -0.65625 -0.734375 -1.59375l0 -3.53125l-0.578125 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.21875 -0.25 -0.578125q0 -0.359375 0.25 -0.578125q0.25 -0.21875 0.828125 -0.21875l0.578125 0l0 -1.453125q0 -0.578125 0.21875 -0.8125q0.21875 -0.25 0.578125 -0.25q0.359375 0 0.578125 0.25q0.21875 0.234375 0.21875 0.8125l0 1.453125l2.96875 0q0.578125 0 0.8125 0.21875q0.25 0.21875 0.25 0.578125q0 0.359375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-2.96875 0zm8.3515625 -4.640625l0 3.421875q0.5 -0.296875 1.0 -0.4375q0.515625 -0.15625 1.046875 -0.15625q0.828125 0 1.46875 0.28125q0.65625 0.28125 1.078125 0.890625q0.421875 0.609375 0.421875 1.53125l0 3.0q0.609375 0 0.796875 0.125q0.375 0.234375 0.375 0.6875q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-1.828125 0q-0.5625 0 -0.8125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.4375 0.375 -0.671875q0.203125 -0.125 0.796875 -0.125l0 -2.890625q0 -0.625 -0.28125 -0.875q-0.359375 -0.328125 -1.078125 -0.328125q-0.53125 0 -0.953125 0.203125q-0.40625 0.203125 -1.09375 0.890625l0 3.0q0.609375 0 0.796875 0.125q0.375 0.234375 0.375 0.6875q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-1.828125 0q-0.578125 0 -0.828125 -0.21875q-0.234375 -0.234375 -0.234375 -0.59375q0 -0.4375 0.375 -0.671875q0.1875 -0.125 0.796875 -0.125l0 -6.921875l-0.265625 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.34375 0.25 -0.5625q0.25 -0.234375 0.828125 -0.234375l1.875 0z" fill-rule="nonzero"></path><path fill="#ffffff" d="m336.0 400.0l187.43304 0l0 80.53543l-187.43304 0z" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m336.0 400.0l187.43304 0l0 80.53543l-187.43304 0z" fill-rule="nonzero"></path><path fill="#000000" d="m381.72043 441.58084l-1.484375 4.546875l-1.796875 0l-0.953125 -7.875q-0.375 -0.046875 -0.5625 -0.25q-0.1875 -0.203125 -0.1875 -0.53125q0 -0.375 0.25 -0.59375q0.25 -0.234375 0.828125 -0.234375l2.125 0.015625q0.578125 0 0.828125 0.21875q0.25 0.21875 0.25 0.578125q0 0.359375 -0.25 0.578125q-0.25 0.21875 -0.828125 0.21875l-0.828125 0l0.53125 4.484375l1.25 -3.734375l1.671875 0l1.25 3.734375l0.53125 -4.484375l-0.84375 0q-0.578125 0 -0.828125 -0.21875q-0.234375 -0.21875 -0.234375 -0.578125q0 -0.359375 0.234375 -0.578125q0.25 -0.234375 0.828125 -0.234375l2.125 0.015625q0.578125 0 0.828125 0.21875q0.25 0.21875 0.25 0.578125q0 0.296875 -0.203125 0.515625q-0.1875 0.21875 -0.5625 0.28125l-0.921875 7.875l-1.765625 0l-1.53125 -4.546875zm10.1640625 -5.59375l0 1.703125l-1.90625 0l0 -1.703125l1.90625 0zm0.21875 3.046875l0 5.484375l1.921875 0q0.578125 0 0.828125 0.234375q0.25 0.21875 0.25 0.578125q0 0.34375 -0.25 0.578125q-0.25 0.21875 -0.828125 0.21875l-5.4375 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.34375 0.25 -0.5625q0.25 -0.234375 0.828125 -0.234375l1.921875 0l0 -3.890625l-1.296875 0q-0.5625 0 -0.8125 -0.21875q-0.25 -0.21875 -0.25 -0.578125q0 -0.359375 0.234375 -0.578125q0.25 -0.21875 0.828125 -0.21875l2.890625 0zm7.1953125 0l0 0.53125q0.4375 -0.375 0.953125 -0.5625q0.53125 -0.1875 1.15625 -0.1875q1.421875 0 2.25 0.890625q0.65625 0.703125 0.65625 1.84375l0 2.96875q0.5 0 0.734375 0.234375q0.25 0.21875 0.25 0.578125q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-1.453125 0q-0.578125 0 -0.828125 -0.21875q-0.234375 -0.234375 -0.234375 -0.59375q0 -0.34375 0.234375 -0.5625q0.25 -0.234375 0.75 -0.234375l0 -3.015625q0 -0.53125 -0.28125 -0.765625q-0.359375 -0.3125 -1.09375 -0.3125q-0.5625 0 -0.984375 0.21875q-0.40625 0.203125 -1.046875 0.90625l0 2.96875q0.609375 0 0.796875 0.125q0.375 0.234375 0.375 0.6875q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-1.828125 0q-0.578125 0 -0.828125 -0.21875q-0.234375 -0.234375 -0.234375 -0.59375q0 -0.4375 0.375 -0.671875q0.1875 -0.125 0.796875 -0.125l0 -3.890625q-0.5 0 -0.75 -0.21875q-0.234375 -0.234375 -0.234375 -0.578125q0 -0.359375 0.234375 -0.578125q0.25 -0.21875 0.828125 -0.21875l1.53125 0zm14.8984375 -3.046875l0 8.53125l0.265625 0q0.578125 0 0.828125 0.234375q0.25 0.21875 0.25 0.578125q0 0.34375 -0.25 0.578125q-0.25 0.21875 -0.828125 0.21875l-1.875 0l0 -0.390625q-0.546875 0.3125 -1.140625 0.484375q-0.59375 0.171875 -1.234375 0.171875q-1.8125 0 -2.921875 -1.046875q-1.09375 -1.046875 -1.09375 -2.609375q0 -1.625 1.15625 -2.765625q1.15625 -1.15625 2.828125 -1.15625q0.625 0 1.21875 0.203125q0.609375 0.1875 1.1875 0.5625l0 -1.984375l-0.265625 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.234375 -0.25 -0.578125q0 -0.359375 0.25 -0.578125q0.25 -0.234375 0.828125 -0.234375l1.875 0zm-1.609375 6.796875q0 -0.984375 -0.703125 -1.671875q-0.6875 -0.6875 -1.6875 -0.6875q-1.0 0 -1.703125 0.6875q-0.6875 0.6875 -0.6875 1.65625q0 0.875 0.625 1.453125q0.625 0.5625 1.765625 0.5625q1.125 0 1.75 -0.5625q0.640625 -0.578125 0.640625 -1.4375zm11.6953125 -0.078125q0 0.921875 -0.515625 1.796875q-0.515625 0.859375 -1.53125 1.375q-1.0 0.515625 -2.109375 0.515625q-1.09375 0 -2.09375 -0.5q-1.0 -0.515625 -1.53125 -1.375q-0.515625 -0.875 -0.515625 -1.828125q0 -0.953125 0.53125 -1.875q0.53125 -0.9375 1.53125 -1.46875q1.0 -0.53125 2.078125 -0.53125q1.09375 0 2.109375 0.546875q1.015625 0.546875 1.53125 1.46875q0.515625 0.90625 0.515625 1.875zm-1.609375 0.015625q0 -0.78125 -0.546875 -1.421875q-0.765625 -0.875 -2.0 -0.875q-1.078125 0 -1.8125 0.703125q-0.71875 0.6875 -0.71875 1.59375q0 0.75 0.734375 1.40625q0.734375 0.65625 1.796875 0.65625q1.078125 0 1.8125 -0.65625q0.734375 -0.65625 0.734375 -1.40625zm7.0546875 0.453125l-1.109375 2.953125l-1.5 0l-1.34375 -5.5q-0.4375 -0.015625 -0.671875 -0.234375q-0.21875 -0.234375 -0.21875 -0.5625q0 -0.359375 0.25 -0.578125q0.25 -0.21875 0.828125 -0.21875l1.484375 0q0.578125 0 0.828125 0.21875q0.25 0.21875 0.25 0.578125q0 0.359375 -0.28125 0.609375q-0.21875 0.1875 -0.828125 0.1875l0.609375 2.546875l0.984375 -2.609375l1.421875 0l1.0 2.609375l0.625 -2.546875q-0.59375 0 -0.78125 -0.109375q-0.375 -0.25 -0.375 -0.6875q0 -0.359375 0.25 -0.578125q0.25 -0.21875 0.828125 -0.21875l1.5 0q0.578125 0 0.828125 0.21875q0.25 0.21875 0.25 0.578125q0 0.328125 -0.21875 0.546875q-0.21875 0.21875 -0.640625 0.25l-1.3125 5.5l-1.484375 0l-1.171875 -2.953125zm11.3203125 -2.265625q-0.390625 -0.25 -0.828125 -0.359375q-0.421875 -0.125 -0.890625 -0.125q-0.921875 0 -1.46875 0.296875q-0.25 0.140625 -0.25 0.296875q0 0.171875 0.328125 0.34375q0.25 0.125 1.125 0.25q1.59375 0.21875 2.21875 0.4375q0.8125 0.28125 1.25 0.859375q0.453125 0.5625 0.453125 1.203125q0 0.859375 -0.75 1.4375q-1.09375 0.84375 -2.828125 0.84375q-0.6875 0 -1.28125 -0.125q-0.59375 -0.125 -1.078125 -0.359375q-0.125 0.09375 -0.265625 0.15625q-0.125 0.046875 -0.265625 0.046875q-0.375 0 -0.59375 -0.234375q-0.21875 -0.25 -0.21875 -0.828125l0 -0.546875q0 -0.578125 0.21875 -0.8125q0.21875 -0.25 0.578125 -0.25q0.296875 0 0.484375 0.15625q0.203125 0.15625 0.3125 0.546875q0.359375 0.3125 0.875 0.484375q0.515625 0.15625 1.1875 0.15625q1.109375 0 1.71875 -0.34375q0.28125 -0.171875 0.28125 -0.359375q0 -0.3125 -0.40625 -0.515625q-0.421875 -0.203125 -1.71875 -0.34375q-1.921875 -0.203125 -2.578125 -0.78125q-0.640625 -0.578125 -0.640625 -1.40625q0 -0.859375 0.71875 -1.4375q0.984375 -0.78125 2.578125 -0.78125q0.5625 0 1.0625 0.109375q0.515625 0.109375 0.984375 0.328125q0.15625 -0.109375 0.28125 -0.15625q0.125 -0.0625 0.234375 -0.0625q0.328125 0 0.546875 0.25q0.21875 0.234375 0.21875 0.8125l0 0.390625q0 0.53125 -0.125 0.71875q-0.25 0.359375 -0.671875 0.359375q-0.296875 0 -0.515625 -0.171875q-0.21875 -0.1875 -0.28125 -0.484375zm6.9453125 1.96875l0 1.640625l1.625 0q0.578125 0 0.8125 0.234375q0.25 0.21875 0.25 0.578125q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-3.46875 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.34375 0.25 -0.5625q0.25 -0.234375 0.828125 -0.234375l0.25 0l0 -6.265625l-0.25 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.21875 -0.25 -0.578125q0 -0.359375 0.25 -0.578125q0.25 -0.234375 0.828125 -0.234375l3.6875 0.015625q1.625 0 2.5625 0.890625q0.953125 0.875 0.953125 2.15625q0 0.703125 -0.3125 1.328125q-0.25 0.46875 -0.8125 0.921875q-0.5625 0.453125 -1.15625 0.6875q-0.59375 0.234375 -1.5625 0.234375l-1.515625 0zm0 -1.609375l1.484375 0q1.046875 0 1.65625 -0.46875q0.625 -0.46875 0.625 -1.140625q0 -0.5625 -0.5 -0.984375q-0.5 -0.421875 -1.421875 -0.421875l-1.84375 0l0 3.015625zm11.9765625 4.859375l0 -0.375q-0.609375 0.3125 -1.34375 0.46875q-0.71875 0.171875 -1.3125 0.171875q-1.28125 0 -2.09375 -0.6875q-0.796875 -0.6875 -0.796875 -1.515625q0 -1.0 1.015625 -1.859375q1.03125 -0.875 2.84375 -0.875q0.734375 0 1.6875 0.15625l0 -0.375q0 -0.359375 -0.3125 -0.578125q-0.3125 -0.234375 -1.171875 -0.234375q-0.71875 0 -1.84375 0.28125q-0.421875 0.09375 -0.65625 0.09375q-0.328125 0 -0.546875 -0.21875q-0.21875 -0.234375 -0.21875 -0.59375q0 -0.203125 0.078125 -0.34375q0.078125 -0.15625 0.21875 -0.25q0.140625 -0.09375 0.578125 -0.21875q0.59375 -0.15625 1.203125 -0.25q0.625 -0.109375 1.125 -0.109375q1.5 0 2.3125 0.65625q0.828125 0.640625 0.828125 1.75l0 3.296875l0.28125 0q0.578125 0 0.8125 0.234375q0.25 0.21875 0.25 0.578125q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-1.875 0zm0 -2.875q-0.96875 -0.1875 -1.78125 -0.1875q-0.96875 0 -1.671875 0.484375q-0.4375 0.296875 -0.4375 0.609375q0 0.234375 0.203125 0.375q0.390625 0.25 1.078125 0.25q0.578125 0 1.296875 -0.21875q0.734375 -0.234375 1.3125 -0.625l0 -0.6875zm7.7578125 -2.625l0 3.21875q0 0.515625 0.21875 0.671875q0.328125 0.265625 1.171875 0.265625q1.21875 0 2.265625 -0.53125q0.390625 -0.203125 0.625 -0.203125q0.3125 0 0.53125 0.234375q0.234375 0.234375 0.234375 0.578125q0 0.3125 -0.25 0.53125q-0.375 0.375 -1.515625 0.6875q-1.125 0.3125 -1.890625 0.3125q-1.5 0 -2.25 -0.640625q-0.734375 -0.65625 -0.734375 -1.59375l0 -3.53125l-0.578125 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.21875 -0.25 -0.578125q0 -0.359375 0.25 -0.578125q0.25 -0.21875 0.828125 -0.21875l0.578125 0l0 -1.453125q0 -0.578125 0.21875 -0.8125q0.21875 -0.25 0.578125 -0.25q0.359375 0 0.578125 0.25q0.21875 0.234375 0.21875 0.8125l0 1.453125l2.96875 0q0.578125 0 0.8125 0.21875q0.25 0.21875 0.25 0.578125q0 0.359375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-2.96875 0zm8.3515625 -4.640625l0 3.421875q0.5 -0.296875 1.0 -0.4375q0.515625 -0.15625 1.046875 -0.15625q0.828125 0 1.46875 0.28125q0.65625 0.28125 1.078125 0.890625q0.421875 0.609375 0.421875 1.53125l0 3.0q0.609375 0 0.796875 0.125q0.375 0.234375 0.375 0.6875q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-1.828125 0q-0.5625 0 -0.8125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.4375 0.375 -0.671875q0.203125 -0.125 0.796875 -0.125l0 -2.890625q0 -0.625 -0.28125 -0.875q-0.359375 -0.328125 -1.078125 -0.328125q-0.53125 0 -0.953125 0.203125q-0.40625 0.203125 -1.09375 0.890625l0 3.0q0.609375 0 0.796875 0.125q0.375 0.234375 0.375 0.6875q0 0.34375 -0.25 0.578125q-0.234375 0.21875 -0.8125 0.21875l-1.828125 0q-0.578125 0 -0.828125 -0.21875q-0.234375 -0.234375 -0.234375 -0.59375q0 -0.4375 0.375 -0.671875q0.1875 -0.125 0.796875 -0.125l0 -6.921875l-0.265625 0q-0.578125 0 -0.828125 -0.21875q-0.25 -0.234375 -0.25 -0.59375q0 -0.34375 0.25 -0.5625q0.25 -0.234375 0.828125 -0.234375l1.875 0z" fill-rule="nonzero"></path><path fill="#000000" fill-opacity="0.0" d="m96.0 144.0l0 -80.0l80.0 0" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="2.0" stroke-linejoin="round" stroke-linecap="butt" d="m96.0 144.0l0 -80.0l68.0 0" fill-rule="evenodd"></path><path stroke="#000000" stroke-width="2.0" stroke-linecap="butt" d="m164.0 67.30347l9.076202 -3.3034668l-9.076202 -3.303463z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m431.5013 143.08398l0 -78.99999l-68.0 0" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="2.0" stroke-linejoin="round" stroke-linecap="butt" d="m431.5013 143.08398l0 -78.99999l-56.0 0" fill-rule="evenodd"></path><path stroke="#000000" stroke-width="2.0" stroke-linecap="butt" d="m375.5013 60.780525l-9.076202 3.3034668l9.076202 3.3034592z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m269.71652 104.53543l0 166.92914" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="2.0" stroke-linejoin="round" stroke-linecap="butt" d="m269.71652 116.53543l0 154.92914" fill-rule="evenodd"></path><path stroke="#000000" stroke-width="2.0" stroke-linecap="butt" d="m273.02 116.53543l-3.3034668 -9.076195l-3.3034668 9.076195z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m65.0 223.08398l-1.0078735 176.91339" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="2.0" stroke-linejoin="round" stroke-linecap="butt" d="m64.93163 235.08379l-0.93950653 164.91359" fill-rule="evenodd"></path><path stroke="#000000" stroke-width="2.0" stroke-linecap="butt" d="m68.23505 235.10262l-3.2517014 -9.094864l-3.3551178 9.05722z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m471.0 224.38058l-1.0078735 176.91336" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="2.0" stroke-linejoin="round" stroke-linecap="butt" d="m470.93164 236.38037l-0.93951416 164.9136" fill-rule="evenodd"></path><path stroke="#000000" stroke-width="2.0" stroke-linecap="butt" d="m474.23505 236.3992l-3.251709 -9.094864l-3.3551025 9.05722z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m128.50131 400.00266l0 -86.168l47.496002 0" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="2.0" stroke-linejoin="round" stroke-linecap="butt" d="m128.50131 400.00266l0 -86.168l35.496002 0" fill-rule="evenodd"></path><path stroke="#000000" stroke-width="2.0" stroke-linecap="butt" d="m163.99731 317.13812l9.076202 -3.3034668l-9.076202 -3.3034668z" fill-rule="evenodd"></path><path fill="#000000" fill-opacity="0.0" d="m414.71902 400.00266l0 -86.168l-51.216003 0" fill-rule="nonzero"></path><path stroke="#000000" stroke-width="2.0" stroke-linejoin="round" stroke-linecap="butt" d="m414.71902 400.00266l0 -86.168l-39.216003 0" fill-rule="evenodd"></path><path stroke="#000000" stroke-width="2.0" stroke-linecap="butt" d="m375.50302 310.5312l-9.076172 3.3034668l9.076172 3.3034668z" fill-rule="evenodd"></path></g></svg>
+
diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst
new file mode 100644
index 0000000..ec1dc4f
--- /dev/null
+++ b/Doc/library/pathlib.rst
@@ -0,0 +1,919 @@
+
+:mod:`pathlib` --- Object-oriented filesystem paths
+===================================================
+
+.. module:: pathlib
+ :synopsis: Object-oriented filesystem paths
+
+.. index:: single: path; operations
+
+.. versionadded:: 3.4
+
+This module offers classes representing filesystem paths with semantics
+appropriate for different operating systems. Path classes are divided
+between :ref:`pure paths <pure-paths>`, which provide purely computational
+operations without I/O, and :ref:`concrete paths <concrete-paths>`, which
+inherit from pure paths but also provide I/O operations.
+
+.. image:: pathlib-inheritance.png
+ :align: center
+
+If you've never used this module before or just aren't sure which class is
+right for your task, :class:`Path` is most likely what you need. It instantiates
+a :ref:`concrete path <concrete-paths>` for the platform the code is running on.
+
+Pure paths are useful in some special cases; for example:
+
+#. If you want to manipulate Windows paths on a Unix machine (or vice versa).
+ You cannot instantiate a :class:`WindowsPath` when running on Unix, but you
+ can instantiate :class:`PureWindowsPath`.
+#. You want to make sure that your code only manipulates paths without actually
+ accessing the OS. In this case, instantiating one of the pure classes may be
+ useful since those simply don't have any OS-accessing operations.
+
+.. note::
+ This module has been included in the standard library on a
+ :term:`provisional basis <provisional package>`. Backwards incompatible
+ changes (up to and including removal of the package) may occur if deemed
+ necessary by the core developers.
+
+.. seealso::
+ :pep:`428`: The pathlib module -- object-oriented filesystem paths.
+
+.. seealso::
+ For low-level path manipulation on strings, you can also use the
+ :mod:`os.path` module.
+
+
+Basic use
+---------
+
+Importing the main class::
+
+ >>> from pathlib import Path
+
+Listing subdirectories::
+
+ >>> p = Path('.')
+ >>> [x for x in p.iterdir() if x.is_dir()]
+ [PosixPath('.hg'), PosixPath('docs'), PosixPath('dist'),
+ PosixPath('__pycache__'), PosixPath('build')]
+
+Listing Python source files in this directory tree::
+
+ >>> list(p.glob('**/*.py'))
+ [PosixPath('test_pathlib.py'), PosixPath('setup.py'),
+ PosixPath('pathlib.py'), PosixPath('docs/conf.py'),
+ PosixPath('build/lib/pathlib.py')]
+
+Navigating inside a directory tree::
+
+ >>> p = Path('/etc')
+ >>> q = p / 'init.d' / 'reboot'
+ >>> q
+ PosixPath('/etc/init.d/reboot')
+ >>> q.resolve()
+ PosixPath('/etc/rc.d/init.d/halt')
+
+Querying path properties::
+
+ >>> q.exists()
+ True
+ >>> q.is_dir()
+ False
+
+Opening a file::
+
+ >>> with q.open() as f: f.readline()
+ ...
+ '#!/bin/bash\n'
+
+
+.. _pure-paths:
+
+Pure paths
+----------
+
+Pure path objects provide path-handling operations which don't actually
+access a filesystem. There are three ways to access these classes, which
+we also call *flavours*:
+
+.. class:: PurePath(*pathsegments)
+
+ A generic class that represents the system's path flavour (instantiating
+ it creates either a :class:`PurePosixPath` or a :class:`PureWindowsPath`)::
+
+ >>> PurePath('setup.py') # Running on a Unix machine
+ PurePosixPath('setup.py')
+
+ Each element of *pathsegments* can be either a string or bytes object
+ representing a path segment; it can also be another path object::
+
+ >>> PurePath('foo', 'some/path', 'bar')
+ PurePosixPath('foo/some/path/bar')
+ >>> PurePath(Path('foo'), Path('bar'))
+ PurePosixPath('foo/bar')
+
+ When *pathsegments* is empty, the current directory is assumed::
+
+ >>> PurePath()
+ PurePosixPath('.')
+
+ When several absolute paths are given, the last is taken as an anchor
+ (mimicking :func:`os.path.join`'s behaviour)::
+
+ >>> PurePath('/etc', '/usr', 'lib64')
+ PurePosixPath('/usr/lib64')
+ >>> PureWindowsPath('c:/Windows', 'd:bar')
+ PureWindowsPath('d:bar')
+
+ However, in a Windows path, changing the local root doesn't discard the
+ previous drive setting::
+
+ >>> PureWindowsPath('c:/Windows', '/Program Files')
+ PureWindowsPath('c:/Program Files')
+
+ Spurious slashes and single dots are collapsed, but double dots (``'..'``)
+ are not, since this would change the meaning of a path in the face of
+ symbolic links::
+
+ >>> PurePath('foo//bar')
+ PurePosixPath('foo/bar')
+ >>> PurePath('foo/./bar')
+ PurePosixPath('foo/bar')
+ >>> PurePath('foo/../bar')
+ PurePosixPath('foo/../bar')
+
+ (a naïve approach would make ``PurePosixPath('foo/../bar')`` equivalent
+ to ``PurePosixPath('bar')``, which is wrong if ``foo`` is a symbolic link
+ to another directory)
+
+.. class:: PurePosixPath(*pathsegments)
+
+ A subclass of :class:`PurePath`, this path flavour represents non-Windows
+ filesystem paths::
+
+ >>> PurePosixPath('/etc')
+ PurePosixPath('/etc')
+
+ *pathsegments* is specified similarly to :class:`PurePath`.
+
+.. class:: PureWindowsPath(*pathsegments)
+
+ A subclass of :class:`PurePath`, this path flavour represents Windows
+ filesystem paths::
+
+ >>> PureWindowsPath('c:/Program Files/')
+ PureWindowsPath('c:/Program Files')
+
+ *pathsegments* is specified similarly to :class:`PurePath`.
+
+Regardless of the system you're running on, you can instantiate all of
+these classes, since they don't provide any operation that does system calls.
+
+
+General properties
+^^^^^^^^^^^^^^^^^^
+
+Paths are immutable and hashable. Paths of a same flavour are comparable
+and orderable. These properties respect the flavour's case-folding
+semantics::
+
+ >>> PurePosixPath('foo') == PurePosixPath('FOO')
+ False
+ >>> PureWindowsPath('foo') == PureWindowsPath('FOO')
+ True
+ >>> PureWindowsPath('FOO') in { PureWindowsPath('foo') }
+ True
+ >>> PureWindowsPath('C:') < PureWindowsPath('d:')
+ True
+
+Paths of a different flavour compare unequal and cannot be ordered::
+
+ >>> PureWindowsPath('foo') == PurePosixPath('foo')
+ False
+ >>> PureWindowsPath('foo') < PurePosixPath('foo')
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+ TypeError: unorderable types: PureWindowsPath() < PurePosixPath()
+
+
+Operators
+^^^^^^^^^
+
+The slash operator helps create child paths, similarly to :func:`os.path.join`::
+
+ >>> p = PurePath('/etc')
+ >>> p
+ PurePosixPath('/etc')
+ >>> p / 'init.d' / 'apache2'
+ PurePosixPath('/etc/init.d/apache2')
+ >>> q = PurePath('bin')
+ >>> '/usr' / q
+ PurePosixPath('/usr/bin')
+
+The string representation of a path is the raw filesystem path itself
+(in native form, e.g. with backslashes under Windows), which you can
+pass to any function taking a file path as a string::
+
+ >>> p = PurePath('/etc')
+ >>> str(p)
+ '/etc'
+ >>> p = PureWindowsPath('c:/Program Files')
+ >>> str(p)
+ 'c:\\Program Files'
+
+Similarly, calling :class:`bytes` on a path gives the raw filesystem path as a
+bytes object, as encoded by :func:`os.fsencode`::
+
+ >>> bytes(p)
+ b'/etc'
+
+.. note::
+ Calling :class:`bytes` is only recommended under Unix. Under Windows,
+ the unicode form is the canonical representation of filesystem paths.
+
+
+Accessing individual parts
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+To access the individual "parts" (components) of a path, use the following
+property:
+
+.. data:: PurePath.parts
+
+ A tuple giving access to the path's various components::
+
+ >>> p = PurePath('/usr/bin/python3')
+ >>> p.parts
+ ('/', 'usr', 'bin', 'python3')
+
+ >>> p = PureWindowsPath('c:/Program Files/PSF')
+ >>> p.parts
+ ('c:\\', 'Program Files', 'PSF')
+
+ (note how the drive and local root are regrouped in a single part)
+
+
+Methods and properties
+^^^^^^^^^^^^^^^^^^^^^^
+
+Pure paths provide the following methods and properties:
+
+.. data:: PurePath.drive
+
+ A string representing the drive letter or name, if any::
+
+ >>> PureWindowsPath('c:/Program Files/').drive
+ 'c:'
+ >>> PureWindowsPath('/Program Files/').drive
+ ''
+ >>> PurePosixPath('/etc').drive
+ ''
+
+ UNC shares are also considered drives::
+
+ >>> PureWindowsPath('//host/share/foo.txt').drive
+ '\\\\host\\share'
+
+.. data:: PurePath.root
+
+ A string representing the (local or global) root, if any::
+
+ >>> PureWindowsPath('c:/Program Files/').root
+ '\\'
+ >>> PureWindowsPath('c:Program Files/').root
+ ''
+ >>> PurePosixPath('/etc').root
+ '/'
+
+ UNC shares always have a root::
+
+ >>> PureWindowsPath('//host/share').root
+ '\\'
+
+.. data:: PurePath.anchor
+
+ The concatenation of the drive and root::
+
+ >>> PureWindowsPath('c:/Program Files/').anchor
+ 'c:\\'
+ >>> PureWindowsPath('c:Program Files/').anchor
+ 'c:'
+ >>> PurePosixPath('/etc').anchor
+ '/'
+ >>> PureWindowsPath('//host/share').anchor
+ '\\\\host\\share\\'
+
+
+.. data:: PurePath.parents
+
+ An immutable sequence providing access to the logical ancestors of
+ the path::
+
+ >>> p = PureWindowsPath('c:/foo/bar/setup.py')
+ >>> p.parents[0]
+ PureWindowsPath('c:/foo/bar')
+ >>> p.parents[1]
+ PureWindowsPath('c:/foo')
+ >>> p.parents[2]
+ PureWindowsPath('c:/')
+
+
+.. data:: PurePath.parent
+
+ The logical parent of the path::
+
+ >>> p = PurePosixPath('/a/b/c/d')
+ >>> p.parent
+ PurePosixPath('/a/b/c')
+
+ You cannot go past an anchor, or empty path::
+
+ >>> p = PurePosixPath('/')
+ >>> p.parent
+ PurePosixPath('/')
+ >>> p = PurePosixPath('.')
+ >>> p.parent
+ PurePosixPath('.')
+
+ .. note::
+ This is a purely lexical operation, hence the following behaviour::
+
+ >>> p = PurePosixPath('foo/..')
+ >>> p.parent
+ PurePosixPath('foo')
+
+ If you want to walk an arbitrary filesystem path upwards, it is
+ recommended to first call :meth:`Path.resolve` so as to resolve
+ symlinks and eliminate `".."` components.
+
+
+.. data:: PurePath.name
+
+ A string representing the final path component, excluding the drive and
+ root, if any::
+
+ >>> PurePosixPath('my/library/setup.py').name
+ 'setup.py'
+
+ UNC drive names are not considered::
+
+ >>> PureWindowsPath('//some/share/setup.py').name
+ 'setup.py'
+ >>> PureWindowsPath('//some/share').name
+ ''
+
+
+.. data:: PurePath.suffix
+
+ The file extension of the final component, if any::
+
+ >>> PurePosixPath('my/library/setup.py').suffix
+ '.py'
+ >>> PurePosixPath('my/library.tar.gz').suffix
+ '.gz'
+ >>> PurePosixPath('my/library').suffix
+ ''
+
+
+.. data:: PurePath.suffixes
+
+ A list of the path's file extensions::
+
+ >>> PurePosixPath('my/library.tar.gar').suffixes
+ ['.tar', '.gar']
+ >>> PurePosixPath('my/library.tar.gz').suffixes
+ ['.tar', '.gz']
+ >>> PurePosixPath('my/library').suffixes
+ []
+
+
+.. data:: PurePath.stem
+
+ The final path component, without its suffix::
+
+ >>> PurePosixPath('my/library.tar.gz').stem
+ 'library.tar'
+ >>> PurePosixPath('my/library.tar').stem
+ 'library'
+ >>> PurePosixPath('my/library').stem
+ 'library'
+
+
+.. method:: PurePath.as_posix()
+
+ Return a string representation of the path with forward slashes (``/``)::
+
+ >>> p = PureWindowsPath('c:\\windows')
+ >>> str(p)
+ 'c:\\windows'
+ >>> p.as_posix()
+ 'c:/windows'
+
+
+.. method:: PurePath.as_uri()
+
+ Represent the path as a ``file`` URI. :exc:`ValueError` is raised if
+ the path isn't absolute.
+
+ >>> p = PurePosixPath('/etc/passwd')
+ >>> p.as_uri()
+ 'file:///etc/passwd'
+ >>> p = PureWindowsPath('c:/Windows')
+ >>> p.as_uri()
+ 'file:///c:/Windows'
+
+
+.. method:: PurePath.is_absolute()
+
+ Return whether the path is absolute or not. A path is considered absolute
+ if it has both a root and (if the flavour allows) a drive::
+
+ >>> PurePosixPath('/a/b').is_absolute()
+ True
+ >>> PurePosixPath('a/b').is_absolute()
+ False
+
+ >>> PureWindowsPath('c:/a/b').is_absolute()
+ True
+ >>> PureWindowsPath('/a/b').is_absolute()
+ False
+ >>> PureWindowsPath('c:').is_absolute()
+ False
+ >>> PureWindowsPath('//some/share').is_absolute()
+ True
+
+
+.. method:: PurePath.is_reserved()
+
+ With :class:`PureWindowsPath`, return ``True`` if the path is considered
+ reserved under Windows, ``False`` otherwise. With :class:`PurePosixPath`,
+ ``False`` is always returned.
+
+ >>> PureWindowsPath('nul').is_reserved()
+ True
+ >>> PurePosixPath('nul').is_reserved()
+ False
+
+ File system calls on reserved paths can fail mysteriously or have
+ unintended effects.
+
+
+.. method:: PurePath.joinpath(*other)
+
+ Calling this method is equivalent to combining the path with each of
+ the *other* arguments in turn::
+
+ >>> PurePosixPath('/etc').joinpath('passwd')
+ PurePosixPath('/etc/passwd')
+ >>> PurePosixPath('/etc').joinpath(PurePosixPath('passwd'))
+ PurePosixPath('/etc/passwd')
+ >>> PurePosixPath('/etc').joinpath('init.d', 'apache2')
+ PurePosixPath('/etc/init.d/apache2')
+ >>> PureWindowsPath('c:').joinpath('/Program Files')
+ PureWindowsPath('c:/Program Files')
+
+
+.. method:: PurePath.match(pattern)
+
+ Match this path against the provided glob-style pattern. Return ``True``
+ if matching is successful, ``False`` otherwise.
+
+ If *pattern* is relative, the path can be either relative or absolute,
+ and matching is done from the right::
+
+ >>> PurePath('a/b.py').match('*.py')
+ True
+ >>> PurePath('/a/b/c.py').match('b/*.py')
+ True
+ >>> PurePath('/a/b/c.py').match('a/*.py')
+ False
+
+ If *pattern* is absolute, the path must be absolute, and the whole path
+ must match::
+
+ >>> PurePath('/a.py').match('/*.py')
+ True
+ >>> PurePath('a/b.py').match('/*.py')
+ False
+
+ As with other methods, case-sensitivity is observed::
+
+ >>> PureWindowsPath('b.py').match('*.PY')
+ True
+
+
+.. method:: PurePath.relative_to(*other)
+
+ Compute a version of this path relative to the path represented by
+ *other*. If it's impossible, ValueError is raised::
+
+ >>> p = PurePosixPath('/etc/passwd')
+ >>> p.relative_to('/')
+ PurePosixPath('etc/passwd')
+ >>> p.relative_to('/etc')
+ PurePosixPath('passwd')
+ >>> p.relative_to('/usr')
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+ File "pathlib.py", line 694, in relative_to
+ .format(str(self), str(formatted)))
+ ValueError: '/etc/passwd' does not start with '/usr'
+
+
+.. method:: PurePath.with_name(name)
+
+ Return a new path with the :attr:`name` changed. If the original path
+ doesn't have a name, ValueError is raised::
+
+ >>> p = PureWindowsPath('c:/Downloads/pathlib.tar.gz')
+ >>> p.with_name('setup.py')
+ PureWindowsPath('c:/Downloads/setup.py')
+ >>> p = PureWindowsPath('c:/')
+ >>> p.with_name('setup.py')
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+ File "/home/antoine/cpython/default/Lib/pathlib.py", line 751, in with_name
+ raise ValueError("%r has an empty name" % (self,))
+ ValueError: PureWindowsPath('c:/') has an empty name
+
+
+.. method:: PurePath.with_suffix(suffix)
+
+ Return a new path with the :attr:`suffix` changed. If the original path
+ doesn't have a suffix, the new *suffix* is appended instead::
+
+ >>> p = PureWindowsPath('c:/Downloads/pathlib.tar.gz')
+ >>> p.with_suffix('.bz2')
+ PureWindowsPath('c:/Downloads/pathlib.tar.bz2')
+ >>> p = PureWindowsPath('README')
+ >>> p.with_suffix('.txt')
+ PureWindowsPath('README.txt')
+
+
+.. _concrete-paths:
+
+
+Concrete paths
+--------------
+
+Concrete paths are subclasses of the pure path classes. In addition to
+operations provided by the latter, they also provide methods to do system
+calls on path objects. There are three ways to instantiate concrete paths:
+
+.. class:: Path(*pathsegments)
+
+ A subclass of :class:`PurePath`, this class represents concrete paths of
+ the system's path flavour (instantiating it creates either a
+ :class:`PosixPath` or a :class:`WindowsPath`)::
+
+ >>> Path('setup.py')
+ PosixPath('setup.py')
+
+ *pathsegments* is specified similarly to :class:`PurePath`.
+
+.. class:: PosixPath(*pathsegments)
+
+ A subclass of :class:`Path` and :class:`PurePosixPath`, this class
+ represents concrete non-Windows filesystem paths::
+
+ >>> PosixPath('/etc')
+ PosixPath('/etc')
+
+ *pathsegments* is specified similarly to :class:`PurePath`.
+
+.. class:: WindowsPath(*pathsegments)
+
+ A subclass of :class:`Path` and :class:`PureWindowsPath`, this class
+ represents concrete Windows filesystem paths::
+
+ >>> WindowsPath('c:/Program Files/')
+ WindowsPath('c:/Program Files')
+
+ *pathsegments* is specified similarly to :class:`PurePath`.
+
+You can only instantiate the class flavour that corresponds to your system
+(allowing system calls on non-compatible path flavours could lead to
+bugs or failures in your application)::
+
+ >>> import os
+ >>> os.name
+ 'posix'
+ >>> Path('setup.py')
+ PosixPath('setup.py')
+ >>> PosixPath('setup.py')
+ PosixPath('setup.py')
+ >>> WindowsPath('setup.py')
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+ File "pathlib.py", line 798, in __new__
+ % (cls.__name__,))
+ NotImplementedError: cannot instantiate 'WindowsPath' on your system
+
+
+Methods
+^^^^^^^
+
+Concrete paths provide the following methods in addition to pure paths
+methods. Many of these methods can raise an :exc:`OSError` if a system
+call fails (for example because the path doesn't exist):
+
+.. classmethod:: Path.cwd()
+
+ Return a new path object representing the current directory (as returned
+ by :func:`os.getcwd`)::
+
+ >>> Path.cwd()
+ PosixPath('/home/antoine/pathlib')
+
+
+.. method:: Path.stat()
+
+ Return information about this path (similarly to :func:`os.stat`).
+ The result is looked up at each call to this method.
+
+ >>> p = Path('setup.py')
+ >>> p.stat().st_size
+ 956
+ >>> p.stat().st_mtime
+ 1327883547.852554
+
+
+.. method:: Path.chmod(mode)
+
+ Change the file mode and permissions, like :func:`os.chmod`::
+
+ >>> p = Path('setup.py')
+ >>> p.stat().st_mode
+ 33277
+ >>> p.chmod(0o444)
+ >>> p.stat().st_mode
+ 33060
+
+
+.. method:: Path.exists()
+
+ Whether the path points to an existing file or directory::
+
+ >>> Path('.').exists()
+ True
+ >>> Path('setup.py').exists()
+ True
+ >>> Path('/etc').exists()
+ True
+ >>> Path('nonexistentfile').exists()
+ False
+
+ .. note::
+ If the path points to a symlink, :meth:`exists` returns whether the
+ symlink *points to* an existing file or directory.
+
+
+.. method:: Path.glob(pattern)
+
+ Glob the given *pattern* in the directory represented by this path,
+ yielding all matching files (of any kind)::
+
+ >>> sorted(Path('.').glob('*.py'))
+ [PosixPath('pathlib.py'), PosixPath('setup.py'), PosixPath('test_pathlib.py')]
+ >>> sorted(Path('.').glob('*/*.py'))
+ [PosixPath('docs/conf.py')]
+
+ The "``**``" pattern means "this directory and all subdirectories,
+ recursively". In other words, it enables recursive globbing::
+
+ >>> sorted(Path('.').glob('**/*.py'))
+ [PosixPath('build/lib/pathlib.py'),
+ PosixPath('docs/conf.py'),
+ PosixPath('pathlib.py'),
+ PosixPath('setup.py'),
+ PosixPath('test_pathlib.py')]
+
+ .. note::
+ Using the "``**``" pattern in large directory trees may consume
+ an inordinate amount of time.
+
+
+.. method:: Path.group()
+
+ Return the name of the group owning the file. :exc:`KeyError` is raised
+ if the file's gid isn't found in the system database.
+
+
+.. method:: Path.is_dir()
+
+ Return ``True`` if the path points to a directory (or a symbolic link
+ pointing to a directory), ``False`` if it points to another kind of file.
+
+ ``False`` is also returned if the path doesn't exist or is a broken symlink;
+ other errors (such as permission errors) are propagated.
+
+
+.. method:: Path.is_file()
+
+ Return ``True`` if the path points to a regular file (or a symbolic link
+ pointing to a regular file), ``False`` if it points to another kind of file.
+
+ ``False`` is also returned if the path doesn't exist or is a broken symlink;
+ other errors (such as permission errors) are propagated.
+
+
+.. method:: Path.is_symlink()
+
+ Return ``True`` if the path points to a symbolic link, ``False`` otherwise.
+
+ ``False`` is also returned if the path doesn't exist; other errors (such
+ as permission errors) are propagated.
+
+
+.. method:: Path.is_socket()
+
+ Return ``True`` if the path points to a Unix socket (or a symbolic link
+ pointing to a Unix socket), ``False`` if it points to another kind of file.
+
+ ``False`` is also returned if the path doesn't exist or is a broken symlink;
+ other errors (such as permission errors) are propagated.
+
+
+.. method:: Path.is_fifo()
+
+ Return ``True`` if the path points to a FIFO (or a symbolic link
+ pointing to a FIFO), ``False`` if it points to another kind of file.
+
+ ``False`` is also returned if the path doesn't exist or is a broken symlink;
+ other errors (such as permission errors) are propagated.
+
+
+.. method:: Path.is_block_device()
+
+ Return ``True`` if the path points to a block device (or a symbolic link
+ pointing to a block device), ``False`` if it points to another kind of file.
+
+ ``False`` is also returned if the path doesn't exist or is a broken symlink;
+ other errors (such as permission errors) are propagated.
+
+
+.. method:: Path.is_char_device()
+
+ Return ``True`` if the path points to a character device (or a symbolic link
+ pointing to a character device), ``False`` if it points to another kind of file.
+
+ ``False`` is also returned if the path doesn't exist or is a broken symlink;
+ other errors (such as permission errors) are propagated.
+
+
+.. method:: Path.iterdir()
+
+ When the path points to a directory, yield path objects of the directory
+ contents::
+
+ >>> p = Path('docs')
+ >>> for child in p.iterdir(): child
+ ...
+ PosixPath('docs/conf.py')
+ PosixPath('docs/_templates')
+ PosixPath('docs/make.bat')
+ PosixPath('docs/index.rst')
+ PosixPath('docs/_build')
+ PosixPath('docs/_static')
+ PosixPath('docs/Makefile')
+
+.. method:: Path.lchmod(mode)
+
+ Like :meth:`Path.chmod` but, if the path points to a symbolic link, the
+ symbolic link's mode is changed rather than its target's.
+
+
+.. method:: Path.lstat()
+
+ Like :meth:`Path.stat` but, if the path points to a symbolic link, return
+ the symbolic link's information rather than its target's.
+
+
+.. method:: Path.mkdir(mode=0o777, parents=False)
+
+ Create a new directory at this given path. If *mode* is given, it is
+ combined with the process' ``umask`` value to determine the file mode
+ and access flags. If the path already exists, :exc:`FileExistsError`
+ is raised.
+
+ If *parents* is true, any missing parents of this path are created
+ as needed; they are created with the default permissions without taking
+ *mode* into account (mimicking the POSIX ``mkdir -p`` command).
+
+ If *parents* is false (the default), a missing parent raises
+ :exc:`FileNotFoundError`.
+
+
+.. method:: Path.open(mode='r', buffering=-1, encoding=None, errors=None, newline=None)
+
+ Open the file pointed to by the path, like the built-in :func:`open`
+ function does::
+
+ >>> p = Path('setup.py')
+ >>> with p.open() as f:
+ ... f.readline()
+ ...
+ '#!/usr/bin/env python3\n'
+
+
+.. method:: Path.owner()
+
+ Return the name of the user owning the file. :exc:`KeyError` is raised
+ if the file's uid isn't found in the system database.
+
+
+.. method:: Path.rename(target)
+
+ Rename this file or directory to the given *target*. *target* can be
+ either a string or another path object::
+
+ >>> p = Path('foo')
+ >>> p.open('w').write('some text')
+ 9
+ >>> target = Path('bar')
+ >>> p.rename(target)
+ >>> target.open().read()
+ 'some text'
+
+
+.. method:: Path.replace(target)
+
+ Rename this file or directory to the given *target*. If *target* points
+ to an existing file or directory, it will be unconditionally replaced.
+
+
+.. method:: Path.resolve()
+
+ Make the path absolute, resolving any symlinks. A new path object is
+ returned::
+
+ >>> p = Path()
+ >>> p
+ PosixPath('.')
+ >>> p.resolve()
+ PosixPath('/home/antoine/pathlib')
+
+ `".."` components are also eliminated (this is the only method to do so)::
+
+ >>> p = Path('docs/../setup.py')
+ >>> p.resolve()
+ PosixPath('/home/antoine/pathlib/setup.py')
+
+ If the path doesn't exist, :exc:`FileNotFoundError` is raised. If an
+ infinite loop is encountered along the resolution path,
+ :exc:`RuntimeError` is raised.
+
+
+.. method:: Path.rglob(pattern)
+
+ This is like calling :meth:`glob` with "``**``" added in front of the
+ given *pattern*:
+
+ >>> sorted(Path().rglob("*.py"))
+ [PosixPath('build/lib/pathlib.py'),
+ PosixPath('docs/conf.py'),
+ PosixPath('pathlib.py'),
+ PosixPath('setup.py'),
+ PosixPath('test_pathlib.py')]
+
+
+.. method:: Path.rmdir()
+
+ Remove this directory. The directory must be empty.
+
+
+.. method:: Path.symlink_to(target, target_is_directory=False)
+
+ Make this path a symbolic link to *target*. Under Windows,
+ *target_is_directory* must be true (default ``False``) if the link's target
+ is a directory. Under POSIX, *target_is_directory*'s value is ignored.
+
+ >>> p = Path('mylink')
+ >>> p.symlink_to('setup.py')
+ >>> p.resolve()
+ PosixPath('/home/antoine/pathlib/setup.py')
+ >>> p.stat().st_size
+ 956
+ >>> p.lstat().st_size
+ 8
+
+ .. note::
+ The order of arguments (link, target) is the reverse
+ of :func:`os.symlink`'s.
+
+
+.. method:: Path.touch(mode=0o777, exist_ok=True)
+
+ Create a file at this given path. If *mode* is given, it is combined
+ with the process' ``umask`` value to determine the file mode and access
+ flags. If the file already exists, the function succeeds if *exist_ok*
+ is true (and its modification time is updated to the current time),
+ otherwise :exc:`FileExistsError` is raised.
+
+
+.. method:: Path.unlink()
+
+ Remove this file or symbolic link. If the path points to a directory,
+ use :func:`Path.rmdir` instead.
diff --git a/Doc/library/pdb.rst b/Doc/library/pdb.rst
index f4e37ac..48a8a6b 100644
--- a/Doc/library/pdb.rst
+++ b/Doc/library/pdb.rst
@@ -41,7 +41,7 @@ of the debugger is::
.. versionchanged:: 3.3
Tab-completion via the :mod:`readline` module is available for commands and
command arguments, e.g. the current global and local names are offered as
- arguments of the ``print`` command.
+ arguments of the ``p`` command.
:file:`pdb.py` can also be invoked as a script to debug other scripts. For
example::
@@ -309,7 +309,7 @@ by the local file.
``end`` to terminate the commands. An example::
(Pdb) commands 1
- (com) print some_variable
+ (com) p some_variable
(com) end
(Pdb)
@@ -403,13 +403,19 @@ by the local file.
Print the argument list of the current function.
-.. pdbcommand:: p(rint) expression
+.. pdbcommand:: p expression
Evaluate the *expression* in the current context and print its value.
+ .. note::
+
+ ``print()`` can also be used, but is not a debugger command --- this executes the
+ Python :func:`print` function.
+
+
.. pdbcommand:: pp expression
- Like the :pdbcmd:`print` command, except the value of the expression is
+ Like the :pdbcmd:`p` command, except the value of the expression is
pretty-printed using the :mod:`pprint` module.
.. pdbcommand:: whatis expression
diff --git a/Doc/library/pickle.rst b/Doc/library/pickle.rst
index 273fb34..ce5467f 100644
--- a/Doc/library/pickle.rst
+++ b/Doc/library/pickle.rst
@@ -94,6 +94,9 @@ There are fundamental differences between the pickle protocols and
The :mod:`json` module: a standard library module allowing JSON
serialization and deserialization.
+
+.. _pickle-protocols:
+
Data stream format
------------------
@@ -113,7 +116,9 @@ The module :mod:`pickletools` contains tools for analyzing data streams
generated by :mod:`pickle`. :mod:`pickletools` source code has extensive
comments about opcodes used by pickle protocols.
-There are currently 4 different protocols which can be used for pickling.
+There are currently 5 different protocols which can be used for pickling.
+The higher the protocol used, the more recent the version of Python needed
+to read the pickle produced.
* Protocol version 0 is the original "human-readable" protocol and is
backwards compatible with earlier versions of Python.
@@ -125,10 +130,15 @@ There are currently 4 different protocols which can be used for pickling.
efficient pickling of :term:`new-style class`\es. Refer to :pep:`307` for
information about improvements brought by protocol 2.
-* Protocol version 3 was added in Python 3. It has explicit support for
+* Protocol version 3 was added in Python 3.0. It has explicit support for
:class:`bytes` objects and cannot be unpickled by Python 2.x. This is
- the default as well as the current recommended protocol; use it whenever
- possible.
+ the default protocol, and the recommended protocol when compatibility with
+ other Python 3 versions is required.
+
+* Protocol version 4 was added in Python 3.4. It adds support for very large
+ objects, pickling more kinds of objects, and some data format
+ optimizations. Refer to :pep:`3154` for information about improvements
+ brought by protocol 4.
.. note::
Serialization is a more primitive notion than persistence; although
@@ -156,13 +166,16 @@ The :mod:`pickle` module provides the following constants:
.. data:: HIGHEST_PROTOCOL
- The highest protocol version available. This value can be passed as a
- *protocol* value.
+ An integer, the highest :ref:`protocol version <pickle-protocols>`
+ available. This value can be passed as a *protocol* value to functions
+ :func:`dump` and :func:`dumps` as well as the :class:`Pickler`
+ constructor.
.. data:: DEFAULT_PROTOCOL
- The default protocol used for pickling. May be less than HIGHEST_PROTOCOL.
- Currently the default protocol is 3, a new protocol designed for Python 3.0.
+ An integer, the default :ref:`protocol version <pickle-protocols>` used
+ for pickling. May be less than :data:`HIGHEST_PROTOCOL`. Currently the
+ default protocol is 3, a new protocol designed for Python 3.
The :mod:`pickle` module provides the following functions to make the pickling
@@ -173,13 +186,10 @@ process more convenient:
Write a pickled representation of *obj* to the open :term:`file object` *file*.
This is equivalent to ``Pickler(file, protocol).dump(obj)``.
- The optional *protocol* argument tells the pickler to use the given protocol;
- supported protocols are 0, 1, 2, 3. The default protocol is 3; a
- backward-incompatible protocol designed for Python 3.0.
-
- Specifying a negative protocol version selects the highest protocol version
- supported. The higher the protocol used, the more recent the version of
- Python needed to read the pickle produced.
+ The optional *protocol* argument, an integer, tells the pickler to use
+ the given protocol; supported protocols are 0 to :data:`HIGHEST_PROTOCOL`.
+ If not specified, the default is :data:`DEFAULT_PROTOCOL`. If a negative
+ number is specified, :data:`HIGHEST_PROTOCOL` is selected.
The *file* argument must have a write() method that accepts a single bytes
argument. It can thus be an on-disk file opened for binary writing, a
@@ -187,64 +197,57 @@ process more convenient:
interface.
If *fix_imports* is true and *protocol* is less than 3, pickle will try to
- map the new Python 3.x names to the old module names used in Python 2.x,
- so that the pickle data stream is readable with Python 2.x.
+ map the new Python 3 names to the old module names used in Python 2, so
+ that the pickle data stream is readable with Python 2.
.. function:: dumps(obj, protocol=None, \*, fix_imports=True)
- Return the pickled representation of the object as a :class:`bytes`
- object, instead of writing it to a file.
-
- The optional *protocol* argument tells the pickler to use the given protocol;
- supported protocols are 0, 1, 2, 3. The default protocol is 3; a
- backward-incompatible protocol designed for Python 3.0.
+ Return the pickled representation of the object as a :class:`bytes` object,
+ instead of writing it to a file.
- Specifying a negative protocol version selects the highest protocol version
- supported. The higher the protocol used, the more recent the version of
- Python needed to read the pickle produced.
-
- If *fix_imports* is true and *protocol* is less than 3, pickle will try to
- map the new Python 3.x names to the old module names used in Python 2.x,
- so that the pickle data stream is readable with Python 2.x.
+ Arguments *protocol* and *fix_imports* have the same meaning as in
+ :func:`dump`.
.. function:: load(file, \*, fix_imports=True, encoding="ASCII", errors="strict")
- Read a pickled object representation from the open :term:`file object` *file*
- and return the reconstituted object hierarchy specified therein. This is
- equivalent to ``Unpickler(file).load()``.
+ Read a pickled object representation from the open :term:`file object`
+ *file* and return the reconstituted object hierarchy specified therein.
+ This is equivalent to ``Unpickler(file).load()``.
- The protocol version of the pickle is detected automatically, so no protocol
- argument is needed. Bytes past the pickled object's representation are
- ignored.
+ The protocol version of the pickle is detected automatically, so no
+ protocol argument is needed. Bytes past the pickled object's
+ representation are ignored.
The argument *file* must have two methods, a read() method that takes an
integer argument, and a readline() method that requires no arguments. Both
- methods should return bytes. Thus *file* can be an on-disk file opened
- for binary reading, a :class:`io.BytesIO` object, or any other custom object
+ methods should return bytes. Thus *file* can be an on-disk file opened for
+ binary reading, a :class:`io.BytesIO` object, or any other custom object
that meets this interface.
Optional keyword arguments are *fix_imports*, *encoding* and *errors*,
which are used to control compatibility support for pickle stream generated
- by Python 2.x. If *fix_imports* is true, pickle will try to map the old
- Python 2.x names to the new names used in Python 3.x. The *encoding* and
+ by Python 2. If *fix_imports* is true, pickle will try to map the old
+ Python 2 names to the new names used in Python 3. The *encoding* and
*errors* tell pickle how to decode 8-bit string instances pickled by Python
- 2.x; these default to 'ASCII' and 'strict', respectively.
+ 2; these default to 'ASCII' and 'strict', respectively. The *encoding* can
+ be 'bytes' to read these 8-bit string instances as bytes objects.
.. function:: loads(bytes_object, \*, fix_imports=True, encoding="ASCII", errors="strict")
Read a pickled object hierarchy from a :class:`bytes` object and return the
reconstituted object hierarchy specified therein
- The protocol version of the pickle is detected automatically, so no protocol
- argument is needed. Bytes past the pickled object's representation are
- ignored.
+ The protocol version of the pickle is detected automatically, so no
+ protocol argument is needed. Bytes past the pickled object's
+ representation are ignored.
Optional keyword arguments are *fix_imports*, *encoding* and *errors*,
which are used to control compatibility support for pickle stream generated
- by Python 2.x. If *fix_imports* is true, pickle will try to map the old
- Python 2.x names to the new names used in Python 3.x. The *encoding* and
+ by Python 2. If *fix_imports* is true, pickle will try to map the old
+ Python 2 names to the new names used in Python 3. The *encoding* and
*errors* tell pickle how to decode 8-bit string instances pickled by Python
- 2.x; these default to 'ASCII' and 'strict', respectively.
+ 2; these default to 'ASCII' and 'strict', respectively. The *encoding* can
+ be 'bytes' to read these 8-bit string instances as bytes objects.
The :mod:`pickle` module defines three exceptions:
@@ -279,21 +282,19 @@ The :mod:`pickle` module exports two classes, :class:`Pickler` and
This takes a binary file for writing a pickle data stream.
- The optional *protocol* argument tells the pickler to use the given protocol;
- supported protocols are 0, 1, 2, 3. The default protocol is 3; a
- backward-incompatible protocol designed for Python 3.0.
-
- Specifying a negative protocol version selects the highest protocol version
- supported. The higher the protocol used, the more recent the version of
- Python needed to read the pickle produced.
+ The optional *protocol* argument, an integer, tells the pickler to use
+ the given protocol; supported protocols are 0 to :data:`HIGHEST_PROTOCOL`.
+ If not specified, the default is :data:`DEFAULT_PROTOCOL`. If a negative
+ number is specified, :data:`HIGHEST_PROTOCOL` is selected.
The *file* argument must have a write() method that accepts a single bytes
argument. It can thus be an on-disk file opened for binary writing, a
- :class:`io.BytesIO` instance, or any other custom object that meets this interface.
+ :class:`io.BytesIO` instance, or any other custom object that meets this
+ interface.
If *fix_imports* is true and *protocol* is less than 3, pickle will try to
- map the new Python 3.x names to the old module names used in Python 2.x,
- so that the pickle data stream is readable with Python 2.x.
+ map the new Python 3 names to the old module names used in Python 2, so
+ that the pickle data stream is readable with Python 2.
.. method:: dump(obj)
@@ -355,16 +356,17 @@ The :mod:`pickle` module exports two classes, :class:`Pickler` and
The argument *file* must have two methods, a read() method that takes an
integer argument, and a readline() method that requires no arguments. Both
- methods should return bytes. Thus *file* can be an on-disk file object opened
- for binary reading, a :class:`io.BytesIO` object, or any other custom object
- that meets this interface.
+ methods should return bytes. Thus *file* can be an on-disk file object
+ opened for binary reading, a :class:`io.BytesIO` object, or any other
+ custom object that meets this interface.
Optional keyword arguments are *fix_imports*, *encoding* and *errors*,
which are used to control compatibility support for pickle stream generated
- by Python 2.x. If *fix_imports* is true, pickle will try to map the old
- Python 2.x names to the new names used in Python 3.x. The *encoding* and
+ by Python 2. If *fix_imports* is true, pickle will try to map the old
+ Python 2 names to the new names used in Python 3. The *encoding* and
*errors* tell pickle how to decode 8-bit string instances pickled by Python
- 2.x; these default to 'ASCII' and 'strict', respectively.
+ 2; these default to 'ASCII' and 'strict', respectively. The *encoding* can
+ be 'bytes' to read these ß8-bit string instances as bytes objects.
.. method:: load()
@@ -409,7 +411,8 @@ The following types can be pickled:
* tuples, lists, sets, and dictionaries containing only picklable objects
-* functions defined at the top level of a module
+* functions defined at the top level of a module (using :keyword:`def`, not
+ :keyword:`lambda`)
* built-in functions defined at the top level of a module
@@ -427,7 +430,7 @@ raised in this case. You can carefully raise this limit with
:func:`sys.setrecursionlimit`.
Note that functions (built-in and user-defined) are pickled by "fully qualified"
-name reference, not by value. This means that only the function name is
+name reference, not by value. [#]_ This means that only the function name is
pickled, along with the name of the module the function is defined in. Neither
the function's code, nor any of its function attributes are pickled. Thus the
defining module must be importable in the unpickling environment, and the module
@@ -483,12 +486,29 @@ implementation of this behaviour::
Classes can alter the default behaviour by providing one or several special
methods:
+.. method:: object.__getnewargs_ex__()
+
+ In protocols 4 and newer, classes that implements the
+ :meth:`__getnewargs_ex__` method can dictate the values passed to the
+ :meth:`__new__` method upon unpickling. The method must return a pair
+ ``(args, kwargs)`` where *args* is a tuple of positional arguments
+ and *kwargs* a dictionary of named arguments for constructing the
+ object. Those will be passed to the :meth:`__new__` method upon
+ unpickling.
+
+ You should implement this method if the :meth:`__new__` method of your
+ class requires keyword-only arguments. Otherwise, it is recommended for
+ compatibility to implement :meth:`__getnewargs__`.
+
+
.. method:: object.__getnewargs__()
- In protocol 2 and newer, classes that implements the :meth:`__getnewargs__`
- method can dictate the values passed to the :meth:`__new__` method upon
- unpickling. This is often needed for classes whose :meth:`__new__` method
- requires arguments.
+ This method serve a similar purpose as :meth:`__getnewargs_ex__` but
+ for protocols 2 and newer. It must return a tuple of arguments `args`
+ which will be passed to the :meth:`__new__` method upon unpickling.
+
+ In protocols 4 and newer, :meth:`__getnewargs__` will not be called if
+ :meth:`__getnewargs_ex__` is defined.
.. method:: object.__getstate__()
@@ -520,10 +540,10 @@ the methods :meth:`__getstate__` and :meth:`__setstate__`.
At unpickling time, some methods like :meth:`__getattr__`,
:meth:`__getattribute__`, or :meth:`__setattr__` may be called upon the
- instance. In case those methods rely on some internal invariant being true,
- the type should implement :meth:`__getnewargs__` to establish such an
- invariant; otherwise, neither :meth:`__new__` nor :meth:`__init__` will be
- called.
+ instance. In case those methods rely on some internal invariant being
+ true, the type should implement :meth:`__getnewargs__` or
+ :meth:`__getnewargs_ex__` to establish such an invariant; otherwise,
+ neither :meth:`__new__` nor :meth:`__init__` will be called.
.. index:: pair: copy; protocol
@@ -535,7 +555,7 @@ objects. [#]_
Although powerful, implementing :meth:`__reduce__` directly in your classes is
error prone. For this reason, class designers should use the high-level
-interface (i.e., :meth:`__getnewargs__`, :meth:`__getstate__` and
+interface (i.e., :meth:`__getnewargs_ex__`, :meth:`__getstate__` and
:meth:`__setstate__`) whenever possible. We will show, however, cases where
using :meth:`__reduce__` is the only option or leads to more efficient pickling
or both.
@@ -883,6 +903,9 @@ The following example reads the resulting pickled data. ::
.. [#] Don't confuse this with the :mod:`marshal` module
+.. [#] This is why :keyword:`lambda` functions cannot be pickled: all
+ :keyword:`lambda` functions share the same name: ``<lambda>``.
+
.. [#] The exception raised will likely be an :exc:`ImportError` or an
:exc:`AttributeError` but it could be something else.
diff --git a/Doc/library/pkgutil.rst b/Doc/library/pkgutil.rst
index 7c4f912..13ea7b9 100644
--- a/Doc/library/pkgutil.rst
+++ b/Doc/library/pkgutil.rst
@@ -74,15 +74,17 @@ support.
Retrieve a :pep:`302` module loader for the given *fullname*.
- This is a convenience wrapper around :func:`importlib.find_loader` that
- sets the *path* argument correctly when searching for submodules, and
- also ensures parent packages (if any) are imported before searching for
- submodules.
+ This is a backwards compatibility wrapper around
+ :func:`importlib.util.find_spec` that converts most failures to
+ :exc:`ImportError` and only returns the loader rather than the full
+ :class:`ModuleSpec`.
.. versionchanged:: 3.3
Updated to be based directly on :mod:`importlib` rather than relying
on the package internal PEP 302 import emulation.
+ .. versionchanged:: 3.4
+ Updated to be based on :pep:`451`
.. function:: get_importer(path_item)
@@ -109,14 +111,13 @@ support.
not already imported, its containing package (if any) is imported, in order
to establish the package ``__path__``.
- This function uses :func:`iter_importers`, and is thus subject to the same
- limitations regarding platform-specific special import locations such as the
- Windows registry.
-
.. versionchanged:: 3.3
Updated to be based directly on :mod:`importlib` rather than relying
on the package internal PEP 302 import emulation.
+ .. versionchanged:: 3.4
+ Updated to be based on :pep:`451`
+
.. function:: iter_importers(fullname='')
diff --git a/Doc/library/plistlib.rst b/Doc/library/plistlib.rst
index a267368..6a2d6b4 100644
--- a/Doc/library/plistlib.rst
+++ b/Doc/library/plistlib.rst
@@ -16,26 +16,24 @@
--------------
This module provides an interface for reading and writing the "property list"
-XML files used mainly by Mac OS X.
+files used mainly by Mac OS X and supports both binary and XML plist files.
-The property list (``.plist``) file format is a simple XML pickle supporting
+The property list (``.plist``) file format is a simple serialization supporting
basic object types, like dictionaries, lists, numbers and strings. Usually the
top level object is a dictionary.
-To write out and to parse a plist file, use the :func:`writePlist` and
-:func:`readPlist` functions.
+To write out and to parse a plist file, use the :func:`dump` and
+:func:`load` functions.
-To work with plist data in bytes objects, use :func:`writePlistToBytes`
-and :func:`readPlistFromBytes`.
+To work with plist data in bytes objects, use :func:`dumps`
+and :func:`loads`.
Values can be strings, integers, floats, booleans, tuples, lists, dictionaries
-(but only with string keys), :class:`Data` or :class:`datetime.datetime`
-objects. String values (including dictionary keys) have to be unicode strings --
-they will be written out as UTF-8.
+(but only with string keys), :class:`Data`, :class:`bytes`, :class:`bytesarray`
+or :class:`datetime.datetime` objects.
-The ``<data>`` plist type is supported through the :class:`Data` class. This is
-a thin wrapper around a Python bytes object. Use :class:`Data` if your strings
-contain control characters.
+.. versionchanged:: 3.4
+ New API, old API deprecated. Support for binary format plists added.
.. seealso::
@@ -45,37 +43,147 @@ contain control characters.
This module defines the following functions:
-.. function:: readPlist(pathOrFile)
+.. function:: load(fp, \*, fmt=None, use_builtin_types=True, dict_type=dict)
- Read a plist file. *pathOrFile* may either be a file name or a (readable and
- binary) file object. Return the unpacked root object (which usually is a
+ Read a plist file. *fp* should be a readable and binary file object.
+ Return the unpacked root object (which usually is a
dictionary).
- The XML data is parsed using the Expat parser from :mod:`xml.parsers.expat`
- -- see its documentation for possible exceptions on ill-formed XML.
- Unknown elements will simply be ignored by the plist parser.
+ The *fmt* is the format of the file and the following values are valid:
+
+ * :data:`None`: Autodetect the file format
+
+ * :data:`FMT_XML`: XML file format
+
+ * :data:`FMT_BINARY`: Binary plist format
+
+ If *use_builtin_types* is true (the default) binary data will be returned
+ as instances of :class:`bytes`, otherwise it is returned as instances of
+ :class:`Data`.
+
+ The *dict_type* is the type used for dictionaries that are read from the
+ plist file. The exact structure of the plist can be recovered by using
+ :class:`collections.OrderedDict` (although the order of keys shouldn't be
+ important in plist files).
+
+ XML data for the :data:`FMT_XML` format is parsed using the Expat parser
+ from :mod:`xml.parsers.expat` -- see its documentation for possible
+ exceptions on ill-formed XML. Unknown elements will simply be ignored
+ by the plist parser.
+
+ The parser for the binary format raises :exc:`InvalidFileException`
+ when the file cannot be parsed.
+
+ .. versionadded:: 3.4
+
+
+.. function:: loads(data, \*, fmt=None, use_builtin_types=True, dict_type=dict)
+
+ Load a plist from a bytes object. See :func:`load` for an explanation of
+ the keyword arguments.
+
+ .. versionadded:: 3.4
+
+
+.. function:: dump(value, fp, \*, fmt=FMT_XML, sort_keys=True, skipkeys=False)
+
+ Write *value* to a plist file. *Fp* should be a writable, binary
+ file object.
+
+ The *fmt* argument specifies the format of the plist file and can be
+ one of the following values:
+
+ * :data:`FMT_XML`: XML formatted plist file
+
+ * :data:`FMT_BINARY`: Binary formatted plist file
+
+ When *sort_keys* is true (the default) the keys for dictionaries will be
+ written to the plist in sorted order, otherwise they will be written in
+ the iteration order of the dictionary.
+
+ When *skipkeys* is false (the default) the function raises :exc:`TypeError`
+ when a key of a dictionary is not a string, otherwise such keys are skipped.
+
+ A :exc:`TypeError` will be raised if the object is of an unsupported type or
+ a container that contains objects of unsupported types.
+
+ An :exc:`OverflowError` will be raised for integer values that cannot
+ be represented in (binary) plist files.
+
+ .. versionadded:: 3.4
+
+
+.. function:: dumps(value, \*, fmt=FMT_XML, sort_keys=True, skipkeys=False)
+
+ Return *value* as a plist-formatted bytes object. See
+ the documentation for :func:`dump` for an explanation of the keyword
+ arguments of this function.
+
+ .. versionadded:: 3.4
+
+The following functions are deprecated:
+
+.. function:: readPlist(pathOrFile)
+
+ Read a plist file. *pathOrFile* may be either a file name or a (readable
+ and binary) file object. Returns the unpacked root object (which usually
+ is a dictionary).
+
+ This function calls :func:`load` to do the actual work, the the documentation
+ of :func:`that function <load>` for an explanation of the keyword arguments.
+
+ .. note::
+
+ Dict values in the result have a ``__getattr__`` method that defers
+ to ``__getitem_``. This means that you can use attribute access to
+ access items of these dictionaries.
+
+ .. deprecated:: 3.4 Use :func:`load` instead.
.. function:: writePlist(rootObject, pathOrFile)
- Write *rootObject* to a plist file. *pathOrFile* may either be a file name
- or a (writable and binary) file object.
+ Write *rootObject* to an XML plist file. *pathOrFile* may be either a file name
+ or a (writable and binary) file object
- A :exc:`TypeError` will be raised if the object is of an unsupported type or
- a container that contains objects of unsupported types.
+ .. deprecated:: 3.4 Use :func:`dump` instead.
.. function:: readPlistFromBytes(data)
Read a plist data from a bytes object. Return the root object.
+ See :func:`load` for a description of the keyword arguments.
+
+ .. note::
+
+ Dict values in the result have a ``__getattr__`` method that defers
+ to ``__getitem_``. This means that you can use attribute access to
+ access items of these dictionaries.
+
+ .. deprecated:: 3.4 Use :func:`loads` instead.
+
.. function:: writePlistToBytes(rootObject)
- Return *rootObject* as a plist-formatted bytes object.
+ Return *rootObject* as an XML plist-formatted bytes object.
+
+ .. deprecated:: 3.4 Use :func:`dumps` instead.
+
+The following classes are available:
+
+.. class:: Dict([dict]):
+
+ Return an extended mapping object with the same value as dictionary
+ *dict*.
+
+ This class is a subclass of :class:`dict` where attribute access can
+ be used to access items. That is, ``aDict.key`` is the same as
+ ``aDict['key']`` for getting, setting and deleting items in the mapping.
+
+ .. deprecated:: 3.0
-The following class is available:
.. class:: Data(data)
@@ -86,6 +194,24 @@ The following class is available:
It has one attribute, :attr:`data`, that can be used to retrieve the Python
bytes object stored in it.
+ .. deprecated:: 3.4 Use a :class:`bytes` object instead
+
+
+The following constants are available:
+
+.. data:: FMT_XML
+
+ The XML format for plist files.
+
+ .. versionadded:: 3.4
+
+
+.. data:: FMT_BINARY
+
+ The binary format for plist files
+
+ .. versionadded:: 3.4
+
Examples
--------
@@ -103,13 +229,15 @@ Generating a plist::
aTrueValue = True,
aFalseValue = False,
),
- someData = Data(b"<binary gunk>"),
- someMoreData = Data(b"<lots of binary gunk>" * 10),
+ someData = b"<binary gunk>",
+ someMoreData = b"<lots of binary gunk>" * 10,
aDate = datetime.datetime.fromtimestamp(time.mktime(time.gmtime())),
)
- writePlist(pl, fileName)
+ with open(fileName, 'wb') as fp:
+ dump(pl, fp)
Parsing a plist::
- pl = readPlist(pathOrFile)
+ with open(fileName, 'rb') as fp:
+ pl = load(fp)
print(pl["aKey"])
diff --git a/Doc/library/poplib.rst b/Doc/library/poplib.rst
index b4080d6..bc7b3e7 100644
--- a/Doc/library/poplib.rst
+++ b/Doc/library/poplib.rst
@@ -13,8 +13,11 @@
--------------
This module defines a class, :class:`POP3`, which encapsulates a connection to a
-POP3 server and implements the protocol as defined in :rfc:`1725`. The
-:class:`POP3` class supports both the minimal and optional command sets.
+POP3 server and implements the protocol as defined in :rfc:`1939`. The
+:class:`POP3` class supports both the minimal and optional command sets from
+:rfc:`1939`. The :class:`POP3` class also supports the `STLS` command introduced
+in :rfc:`2595` to enable encrypted communication on an already established connection.
+
Additionally, this module provides a class :class:`POP3_SSL`, which provides
support for connecting to POP3 servers that use SSL as an underlying protocol
layer.
@@ -40,16 +43,23 @@ The :mod:`poplib` module provides two classes:
This is a subclass of :class:`POP3` that connects to the server over an SSL
encrypted socket. If *port* is not specified, 995, the standard POP3-over-SSL
- port is used. *keyfile* and *certfile* are also optional - they can contain a
- PEM formatted private key and certificate chain file for the SSL connection.
- *timeout* works as in the :class:`POP3` constructor. *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.
+ port is used. *timeout* works as in the :class:`POP3` constructor.
+ *context* is an optional :class:`ssl.SSLContext` object which allows
+ bundling SSL configuration options, certificates and private keys into a
+ single (potentially long-lived) structure. Please read :ref:`ssl-security`
+ for best practices.
+
+ *keyfile* and *certfile* are a legacy alternative to *context* - they can
+ point to PEM-formatted private key and certificate chain files,
+ respectively, for the SSL connection.
.. versionchanged:: 3.2
*context* parameter added.
+ .. versionchanged:: 3.4
+ The class now supports hostname check with
+ :attr:`ssl.SSLContext.check_hostname` and *Server Name Indication* (see
+ :data:`ssl.HAS_SNI`).
One exception is defined as an attribute of the :mod:`poplib` module:
@@ -97,6 +107,14 @@ An :class:`POP3` instance has the following methods:
Returns the greeting string sent by the POP3 server.
+.. method:: POP3.capa()
+
+ Query the server's capabilities as specified in :rfc:`2449`.
+ Returns a dictionary in the form ``{'name': ['param'...]}``.
+
+ .. versionadded:: 3.4
+
+
.. method:: POP3.user(username)
Send user command, response should indicate that a password is required.
@@ -176,6 +194,23 @@ An :class:`POP3` instance has the following methods:
the unique id for that message in the form ``'response mesgnum uid``, otherwise
result is list ``(response, ['mesgnum uid', ...], octets)``.
+.. method:: POP3.stls(context=None)
+
+ Start a TLS session on the active connection as specified in :rfc:`2595`.
+ This is only allowed before user authentication
+
+ *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. Please read :ref:`ssl-security`
+ for best practices.
+
+ This method supports hostname checking via
+ :attr:`ssl.SSLContext.check_hostname` and *Server Name Indication* (see
+ :data:`ssl.HAS_SNI`).
+
+ .. versionadded:: 3.4
+
+
Instances of :class:`POP3_SSL` have no additional methods. The interface of this
subclass is identical to its parent.
diff --git a/Doc/library/pprint.rst b/Doc/library/pprint.rst
index 3a86331..447f8f7 100644
--- a/Doc/library/pprint.rst
+++ b/Doc/library/pprint.rst
@@ -14,8 +14,8 @@ The :mod:`pprint` module provides a capability to "pretty-print" arbitrary
Python data structures in a form which can be used as input to the interpreter.
If the formatted structures include objects which are not fundamental Python
types, the representation may not be loadable. This may be the case if objects
-such as files, sockets, classes, or instances are included, as well as many
-other built-in objects which are not representable as Python constants.
+such as files, sockets or classes are included, as well as many other
+objects which are not representable as Python literals.
The formatted representation keeps objects on a single line if it can, and
breaks them onto multiple lines if they don't fit within the allowed width.
@@ -29,14 +29,14 @@ The :mod:`pprint` module defines one class:
.. First the implementation class:
-.. class:: PrettyPrinter(indent=1, width=80, depth=None, stream=None)
+.. class:: PrettyPrinter(indent=1, width=80, depth=None, stream=None, *, \
+ compact=False)
Construct a :class:`PrettyPrinter` instance. This constructor understands
several keyword parameters. An output stream may be set using the *stream*
keyword; the only method used on the stream object is the file protocol's
:meth:`write` method. If not specified, the :class:`PrettyPrinter` adopts
- ``sys.stdout``. Three additional parameters may be used to control the
- formatted representation. The keywords are *indent*, *depth*, and *width*. The
+ ``sys.stdout``. The
amount of indentation added for each recursive level is specified by *indent*;
the default is one. Other values can cause output to look a little odd, but can
make nesting easier to spot. The number of levels which may be printed is
@@ -45,7 +45,12 @@ The :mod:`pprint` module defines one class:
the depth of the objects being formatted. The desired output width is
constrained using the *width* parameter; the default is 80 characters. If a
structure cannot be formatted within the constrained width, a best effort will
- be made.
+ be made. If *compact* is false (the default) each item of a long sequence
+ will be formatted on a separate line. If *compact* is true, as many items
+ as will fit within the *width* will be formatted on each output line.
+
+ .. versionchanged:: 3.4
+ Added the *compact* parameter.
>>> import pprint
>>> stuff = ['spam', 'eggs', 'lumberjack', 'knights', 'ni']
@@ -58,6 +63,12 @@ The :mod:`pprint` module defines one class:
'lumberjack',
'knights',
'ni']
+ >>> pp = pprint.PrettyPrinter(width=41, compact=True)
+ >>> pp.pprint(stuff)
+ [['spam', 'eggs', 'lumberjack',
+ 'knights', 'ni'],
+ 'spam', 'eggs', 'lumberjack', 'knights',
+ 'ni']
>>> tup = ('spam', ('eggs', ('lumberjack', ('knights', ('ni', ('dead',
... ('parrot', ('fresh fruit',))))))))
>>> pp = pprint.PrettyPrinter(depth=6)
@@ -65,23 +76,30 @@ The :mod:`pprint` module defines one class:
('spam', ('eggs', ('lumberjack', ('knights', ('ni', ('dead', (...)))))))
-The :class:`PrettyPrinter` class supports several derivative functions:
+The :mod:`pprint` module also provides several shortcut functions:
-.. function:: pformat(object, indent=1, width=80, depth=None)
+.. function:: pformat(object, indent=1, width=80, depth=None, *, compact=False)
- Return the formatted representation of *object* as a string. *indent*, *width*
- and *depth* will be passed to the :class:`PrettyPrinter` constructor as
- formatting parameters.
+ Return the formatted representation of *object* as a string. *indent*,
+ *width*, *depth* and *compact* will be passed to the :class:`PrettyPrinter`
+ constructor as formatting parameters.
+ .. versionchanged:: 3.4
+ Added the *compact* parameter.
-.. function:: pprint(object, stream=None, indent=1, width=80, depth=None)
+
+.. function:: pprint(object, stream=None, indent=1, width=80, depth=None, *, \
+ compact=False)
Prints the formatted representation of *object* on *stream*, followed by a
newline. If *stream* is ``None``, ``sys.stdout`` is used. This may be used
in the interactive interpreter instead of the :func:`print` function for
inspecting values (you can even reassign ``print = pprint.pprint`` for use
- within a scope). *indent*, *width* and *depth* will be passed to the
- :class:`PrettyPrinter` constructor as formatting parameters.
+ within a scope). *indent*, *width*, *depth* and *compact* will be passed
+ to the :class:`PrettyPrinter` constructor as formatting parameters.
+
+ .. versionchanged:: 3.4
+ Added the *compact* parameter.
>>> import pprint
>>> stuff = ['spam', 'eggs', 'lumberjack', 'knights', 'ni']
@@ -193,101 +211,141 @@ Example
-------
To demonstrate several uses of the :func:`pprint` function and its parameters,
-let's fetch information about a project from PyPI::
+let's fetch information about a project from `PyPI <https://pypi.python.org>`_::
>>> import json
>>> import pprint
>>> from urllib.request import urlopen
- >>> with urlopen('http://pypi.python.org/pypi/configparser/json') as url:
+ >>> with urlopen('http://pypi.python.org/pypi/Twisted/json') as url:
... http_info = url.info()
... raw_data = url.read().decode(http_info.get_content_charset())
>>> project_info = json.loads(raw_data)
- >>> result = {'headers': http_info.items(), 'body': project_info}
In its basic form, :func:`pprint` shows the whole object::
- >>> pprint.pprint(result)
- {'body': {'info': {'_pypi_hidden': False,
- '_pypi_ordering': 12,
- 'classifiers': ['Development Status :: 4 - Beta',
- 'Intended Audience :: Developers',
- 'License :: OSI Approved :: MIT License',
- 'Natural Language :: English',
- 'Operating System :: OS Independent',
- 'Programming Language :: Python',
- 'Programming Language :: Python :: 2',
- 'Programming Language :: Python :: 2.6',
- 'Programming Language :: Python :: 2.7',
- 'Topic :: Software Development :: Libraries',
- 'Topic :: Software Development :: Libraries :: Python Modules'],
- 'download_url': 'UNKNOWN',
- 'home_page': 'http://docs.python.org/py3k/library/configparser.html',
- 'keywords': 'configparser ini parsing conf cfg configuration file',
- 'license': 'MIT',
- 'name': 'configparser',
- 'package_url': 'http://pypi.python.org/pypi/configparser',
- 'platform': 'any',
- 'release_url': 'http://pypi.python.org/pypi/configparser/3.2.0r3',
- 'requires_python': None,
- 'stable_version': None,
- 'summary': 'This library brings the updated configparser from Python 3.2+ to Python 2.6-2.7.',
- 'version': '3.2.0r3'},
- 'urls': [{'comment_text': '',
- 'downloads': 47,
- 'filename': 'configparser-3.2.0r3.tar.gz',
- 'has_sig': False,
- 'md5_digest': '8500fd87c61ac0de328fc996fce69b96',
- 'packagetype': 'sdist',
- 'python_version': 'source',
- 'size': 32281,
- 'upload_time': '2011-05-10T16:28:50',
- 'url': 'http://pypi.python.org/packages/source/c/configparser/configparser-3.2.0r3.tar.gz'}]},
- 'headers': [('Date', 'Sat, 14 May 2011 12:48:52 GMT'),
- ('Server', 'Apache/2.2.16 (Debian)'),
- ('Content-Disposition', 'inline'),
- ('Connection', 'close'),
- ('Transfer-Encoding', 'chunked'),
- ('Content-Type', 'application/json; charset="UTF-8"')]}
+ >>> pprint.pprint(project_info)
+ {'info': {'_pypi_hidden': False,
+ '_pypi_ordering': 125,
+ 'author': 'Glyph Lefkowitz',
+ 'author_email': 'glyph@twistedmatrix.com',
+ 'bugtrack_url': '',
+ 'cheesecake_code_kwalitee_id': None,
+ 'cheesecake_documentation_id': None,
+ 'cheesecake_installability_id': None,
+ 'classifiers': ['Programming Language :: Python :: 2.6',
+ 'Programming Language :: Python :: 2.7',
+ 'Programming Language :: Python :: 2 :: Only'],
+ 'description': 'An extensible framework for Python programming, '
+ 'with special focus\r\n'
+ 'on event-based network programming and '
+ 'multiprotocol integration.',
+ 'docs_url': '',
+ 'download_url': 'UNKNOWN',
+ 'home_page': 'http://twistedmatrix.com/',
+ 'keywords': '',
+ 'license': 'MIT',
+ 'maintainer': '',
+ 'maintainer_email': '',
+ 'name': 'Twisted',
+ 'package_url': 'http://pypi.python.org/pypi/Twisted',
+ 'platform': 'UNKNOWN',
+ 'release_url': 'http://pypi.python.org/pypi/Twisted/12.3.0',
+ 'requires_python': None,
+ 'stable_version': None,
+ 'summary': 'An asynchronous networking framework written in Python',
+ 'version': '12.3.0'},
+ 'urls': [{'comment_text': '',
+ 'downloads': 71844,
+ 'filename': 'Twisted-12.3.0.tar.bz2',
+ 'has_sig': False,
+ 'md5_digest': '6e289825f3bf5591cfd670874cc0862d',
+ 'packagetype': 'sdist',
+ 'python_version': 'source',
+ 'size': 2615733,
+ 'upload_time': '2012-12-26T12:47:03',
+ 'url': 'https://pypi.python.org/packages/source/T/Twisted/Twisted-12.3.0.tar.bz2'},
+ {'comment_text': '',
+ 'downloads': 5224,
+ 'filename': 'Twisted-12.3.0.win32-py2.7.msi',
+ 'has_sig': False,
+ 'md5_digest': '6b778f5201b622a5519a2aca1a2fe512',
+ 'packagetype': 'bdist_msi',
+ 'python_version': '2.7',
+ 'size': 2916352,
+ 'upload_time': '2012-12-26T12:48:15',
+ 'url': 'https://pypi.python.org/packages/2.7/T/Twisted/Twisted-12.3.0.win32-py2.7.msi'}]}
The result can be limited to a certain *depth* (ellipsis is used for deeper
contents)::
- >>> pprint.pprint(result, depth=3)
- {'body': {'info': {'_pypi_hidden': False,
- '_pypi_ordering': 12,
- 'classifiers': [...],
- 'download_url': 'UNKNOWN',
- 'home_page': 'http://docs.python.org/py3k/library/configparser.html',
- 'keywords': 'configparser ini parsing conf cfg configuration file',
- 'license': 'MIT',
- 'name': 'configparser',
- 'package_url': 'http://pypi.python.org/pypi/configparser',
- 'platform': 'any',
- 'release_url': 'http://pypi.python.org/pypi/configparser/3.2.0r3',
- 'requires_python': None,
- 'stable_version': None,
- 'summary': 'This library brings the updated configparser from Python 3.2+ to Python 2.6-2.7.',
- 'version': '3.2.0r3'},
- 'urls': [{...}]},
- 'headers': [('Date', 'Sat, 14 May 2011 12:48:52 GMT'),
- ('Server', 'Apache/2.2.16 (Debian)'),
- ('Content-Disposition', 'inline'),
- ('Connection', 'close'),
- ('Transfer-Encoding', 'chunked'),
- ('Content-Type', 'application/json; charset="UTF-8"')]}
-
-Additionally, maximum *width* can be suggested. If a long object cannot be
-split, the specified width will be exceeded::
-
- >>> pprint.pprint(result['headers'], width=30)
- [('Date',
- 'Sat, 14 May 2011 12:48:52 GMT'),
- ('Server',
- 'Apache/2.2.16 (Debian)'),
- ('Content-Disposition',
- 'inline'),
- ('Connection', 'close'),
- ('Transfer-Encoding',
- 'chunked'),
- ('Content-Type',
- 'application/json; charset="UTF-8"')]
+ >>> pprint.pprint(project_info, depth=2)
+ {'info': {'_pypi_hidden': False,
+ '_pypi_ordering': 125,
+ 'author': 'Glyph Lefkowitz',
+ 'author_email': 'glyph@twistedmatrix.com',
+ 'bugtrack_url': '',
+ 'cheesecake_code_kwalitee_id': None,
+ 'cheesecake_documentation_id': None,
+ 'cheesecake_installability_id': None,
+ 'classifiers': [...],
+ 'description': 'An extensible framework for Python programming, '
+ 'with special focus\r\n'
+ 'on event-based network programming and '
+ 'multiprotocol integration.',
+ 'docs_url': '',
+ 'download_url': 'UNKNOWN',
+ 'home_page': 'http://twistedmatrix.com/',
+ 'keywords': '',
+ 'license': 'MIT',
+ 'maintainer': '',
+ 'maintainer_email': '',
+ 'name': 'Twisted',
+ 'package_url': 'http://pypi.python.org/pypi/Twisted',
+ 'platform': 'UNKNOWN',
+ 'release_url': 'http://pypi.python.org/pypi/Twisted/12.3.0',
+ 'requires_python': None,
+ 'stable_version': None,
+ 'summary': 'An asynchronous networking framework written in Python',
+ 'version': '12.3.0'},
+ 'urls': [{...}, {...}]}
+
+Additionally, maximum character *width* can be suggested. If a long object
+cannot be split, the specified width will be exceeded::
+
+ >>> pprint.pprint(project_info, depth=2, width=50)
+ {'info': {'_pypi_hidden': False,
+ '_pypi_ordering': 125,
+ 'author': 'Glyph Lefkowitz',
+ 'author_email': 'glyph@twistedmatrix.com',
+ 'bugtrack_url': '',
+ 'cheesecake_code_kwalitee_id': None,
+ 'cheesecake_documentation_id': None,
+ 'cheesecake_installability_id': None,
+ 'classifiers': [...],
+ 'description': 'An extensible '
+ 'framework for '
+ 'Python programming, '
+ 'with special '
+ 'focus\r\n'
+ 'on event-based '
+ 'network programming '
+ 'and multiprotocol '
+ 'integration.',
+ 'docs_url': '',
+ 'download_url': 'UNKNOWN',
+ 'home_page': 'http://twistedmatrix.com/',
+ 'keywords': '',
+ 'license': 'MIT',
+ 'maintainer': '',
+ 'maintainer_email': '',
+ 'name': 'Twisted',
+ 'package_url': 'http://pypi.python.org/pypi/Twisted',
+ 'platform': 'UNKNOWN',
+ 'release_url': 'http://pypi.python.org/pypi/Twisted/12.3.0',
+ 'requires_python': None,
+ 'stable_version': None,
+ 'summary': 'An asynchronous '
+ 'networking framework '
+ 'written in Python',
+ 'version': '12.3.0'},
+ 'urls': [{...}, {...}]}
diff --git a/Doc/library/pty.rst b/Doc/library/pty.rst
index 2b9385b..90baec5 100644
--- a/Doc/library/pty.rst
+++ b/Doc/library/pty.rst
@@ -45,6 +45,9 @@ The :mod:`pty` module defines the following functions:
a file descriptor. The defaults try to read 1024 bytes each time they are
called.
+ .. versionchanged:: 3.4
+ :func:`spawn` now returns the status value from :func:`os.waitpid`
+ on the child process.
Example
-------
diff --git a/Doc/library/py_compile.rst b/Doc/library/py_compile.rst
index 07ddc25..bae8450 100644
--- a/Doc/library/py_compile.rst
+++ b/Doc/library/py_compile.rst
@@ -28,7 +28,7 @@ byte-code cache files in the directory containing the source code.
.. function:: compile(file, cfile=None, dfile=None, doraise=False, optimize=-1)
- Compile a source file to byte-code and write out the byte-code cache file.
+ Compile a source file to byte-code and write out the byte-code cache file.
The source code is loaded from the file name *file*. The byte-code is
written to *cfile*, which defaults to the :PEP:`3147` path, ending in
``.pyc`` (``.pyo`` if optimization is enabled in the current interpreter).
@@ -41,6 +41,13 @@ byte-code cache files in the directory containing the source code.
is raised. This function returns the path to byte-compiled file, i.e.
whatever *cfile* value was used.
+ If the path that *cfile* becomes (either explicitly specified or computed)
+ is a symlink or non-regular file, :exc:`FileExistsError` will be raised.
+ This is to act as a warning that import will turn those paths into regular
+ files if it is allowed to write byte-compiled files to those paths. This is
+ a side-effect of import using file renaming to place the final byte-compiled
+ file into place to prevent concurrent file writing issues.
+
*optimize* controls the optimization level and is passed to the built-in
:func:`compile` function. The default of ``-1`` selects the optimization
level of the current interpreter.
@@ -50,6 +57,13 @@ byte-code cache files in the directory containing the source code.
default was *file* + ``'c'`` (``'o'`` if optimization was enabled).
Also added the *optimize* parameter.
+ .. versionchanged:: 3.4
+ Changed code to use :mod:`importlib` for the byte-code cache file writing.
+ This means file creation/writing semantics now match what :mod:`importlib`
+ does, e.g. permissions, write-and-move semantics, etc. Also added the
+ caveat that :exc:`FileExistsError` is raised if *cfile* is a symlink or
+ non-regular file.
+
.. function:: main(args=None)
diff --git a/Doc/library/pydoc.rst b/Doc/library/pydoc.rst
index e100865..3f520e8 100644
--- a/Doc/library/pydoc.rst
+++ b/Doc/library/pydoc.rst
@@ -84,3 +84,8 @@ Reference Manual pages.
.. versionchanged:: 3.2
Added the ``-b`` option, deprecated the ``-g`` option.
+
+.. versionchanged:: 3.4
+ :mod:`pydoc` now uses :func:`inspect.signature` rather than
+ :func:`inspect.getfullargspec` to extract signature information from
+ callables.
diff --git a/Doc/library/pyexpat.rst b/Doc/library/pyexpat.rst
index 3d88d85..cb8ab65 100644
--- a/Doc/library/pyexpat.rst
+++ b/Doc/library/pyexpat.rst
@@ -100,6 +100,11 @@ The :mod:`xml.parsers.expat` module contains two functions:
http://www.python.org/ns/ elem1
elem2
+ Due to limitations in the ``Expat`` library used by :mod:`pyexpat`,
+ the :class:`xmlparser` instance returned can only be used to parse a single
+ XML document. Call ``ParserCreate`` for each document to provide unique
+ parser instances.
+
.. seealso::
@@ -119,7 +124,9 @@ XMLParser Objects
Parses the contents of the string *data*, calling the appropriate handler
functions to process the parsed data. *isfinal* must be true on the final call
- to this method. *data* can be the empty string at any time.
+ to this method; it allows the parsing of a single file in fragments,
+ not the submission of multiple files.
+ *data* can be the empty string at any time.
.. method:: xmlparser.ParseFile(file)
diff --git a/Doc/library/python.rst b/Doc/library/python.rst
index b67fbfc..f307d7d 100644
--- a/Doc/library/python.rst
+++ b/Doc/library/python.rst
@@ -25,4 +25,3 @@ overview:
inspect.rst
site.rst
fpectl.rst
- distutils.rst
diff --git a/Doc/library/re.rst b/Doc/library/re.rst
index dd790e7..1fa1cc0 100644
--- a/Doc/library/re.rst
+++ b/Doc/library/re.rst
@@ -481,7 +481,7 @@ form.
.. note::
The compiled versions of the most recent patterns passed to
- :func:`re.match`, :func:`re.search` or :func:`re.compile` are cached, so
+ :func:`re.compile` and the module-level matching functions are cached, so
programs that use only a few regular expressions at a time needn't worry
about compiling regular expressions.
@@ -584,6 +584,16 @@ form.
instead (see also :ref:`search-vs-match`).
+.. function:: fullmatch(pattern, string, flags=0)
+
+ If the whole *string* matches the regular expression *pattern*, return a
+ corresponding :ref:`match object <match-objects>`. Return ``None`` if the
+ string does not match the pattern; note that this is different from a
+ zero-length match.
+
+ .. versionadded:: 3.4
+
+
.. function:: split(pattern, string, maxsplit=0, flags=0)
Split *string* by the occurrences of *pattern*. If capturing parentheses are
@@ -755,7 +765,7 @@ attributes:
>>> pattern = re.compile("d")
>>> pattern.search("dog") # Match at index 0
- <_sre.SRE_Match object at ...>
+ <_sre.SRE_Match object; span=(0, 1), match='d'>
>>> pattern.search("dog", 1) # No match; search doesn't include the "d"
@@ -772,12 +782,30 @@ attributes:
>>> pattern = re.compile("o")
>>> pattern.match("dog") # No match as "o" is not at the start of "dog".
>>> pattern.match("dog", 1) # Match as "o" is the 2nd character of "dog".
- <_sre.SRE_Match object at ...>
+ <_sre.SRE_Match object; span=(1, 2), match='o'>
If you want to locate a match anywhere in *string*, use
:meth:`~regex.search` instead (see also :ref:`search-vs-match`).
+.. method:: regex.fullmatch(string[, pos[, endpos]])
+
+ If the whole *string* matches this regular expression, return a corresponding
+ :ref:`match object <match-objects>`. Return ``None`` if the string does not
+ match the pattern; note that this is different from a zero-length match.
+
+ The optional *pos* and *endpos* parameters have the same meaning as for the
+ :meth:`~regex.search` method.
+
+ >>> pattern = re.compile("o[gh]")
+ >>> pattern.fullmatch("dog") # No match as "o" is not at the start of "dog".
+ >>> pattern.fullmatch("ogre") # No match as not the full string matches.
+ >>> pattern.fullmatch("doggie", 1, 3) # Matches within given limits.
+ <_sre.SRE_Match object; span=(1, 3), match='og'>
+
+ .. versionadded:: 3.4
+
+
.. method:: regex.split(string, maxsplit=0)
Identical to the :func:`split` function, using the compiled pattern.
@@ -1139,7 +1167,7 @@ For example::
>>> re.match("c", "abcdef") # No match
>>> re.search("c", "abcdef") # Match
- <_sre.SRE_Match object at ...>
+ <_sre.SRE_Match object; span=(2, 3), match='c'>
Regular expressions beginning with ``'^'`` can be used with :func:`search` to
restrict the match at the beginning of the string::
@@ -1147,7 +1175,7 @@ restrict the match at the beginning of the string::
>>> re.match("c", "abcdef") # No match
>>> re.search("^c", "abcdef") # No match
>>> re.search("^a", "abcdef") # Match
- <_sre.SRE_Match object at ...>
+ <_sre.SRE_Match object; span=(0, 1), match='a'>
Note however that in :const:`MULTILINE` mode :func:`match` only matches at the
beginning of the string, whereas using :func:`search` with a regular expression
@@ -1155,7 +1183,7 @@ beginning with ``'^'`` will match at the beginning of each line.
>>> re.match('X', 'A\nB\nX', re.MULTILINE) # No match
>>> re.search('^X', 'A\nB\nX', re.MULTILINE) # Match
- <_sre.SRE_Match object at ...>
+ <_sre.SRE_Match object; span=(4, 5), match='X'>
Making a Phonebook
@@ -1274,9 +1302,9 @@ another one to escape it. For example, the two following lines of code are
functionally identical:
>>> re.match(r"\W(.)\1\W", " ff ")
- <_sre.SRE_Match object at ...>
+ <_sre.SRE_Match object; span=(0, 4), match=' ff '>
>>> re.match("\\W(.)\\1\\W", " ff ")
- <_sre.SRE_Match object at ...>
+ <_sre.SRE_Match object; span=(0, 4), match=' ff '>
When one wants to match a literal backslash, it must be escaped in the regular
expression. With raw string notation, this means ``r"\\"``. Without raw string
@@ -1284,9 +1312,9 @@ notation, one must use ``"\\\\"``, making the following lines of code
functionally identical:
>>> re.match(r"\\", r"\\")
- <_sre.SRE_Match object at ...>
+ <_sre.SRE_Match object; span=(0, 1), match='\\'>
>>> re.match("\\\\", r"\\")
- <_sre.SRE_Match object at ...>
+ <_sre.SRE_Match object; span=(0, 1), match='\\'>
Writing a Tokenizer
diff --git a/Doc/library/readline.rst b/Doc/library/readline.rst
index 1134619..692310b 100644
--- a/Doc/library/readline.rst
+++ b/Doc/library/readline.rst
@@ -190,28 +190,32 @@ Example
The following example demonstrates how to use the :mod:`readline` module's
history reading and writing functions to automatically load and save a history
-file named :file:`.pyhist` from the user's home directory. The code below would
-normally be executed automatically during interactive sessions from the user's
-:envvar:`PYTHONSTARTUP` file. ::
+file named :file:`.python_history` from the user's home directory. The code
+below would normally be executed automatically during interactive sessions
+from the user's :envvar:`PYTHONSTARTUP` file. ::
+ import atexit
import os
import readline
- histfile = os.path.join(os.path.expanduser("~"), ".pyhist")
+
+ histfile = os.path.join(os.path.expanduser("~"), ".python_history")
try:
readline.read_history_file(histfile)
except FileNotFoundError:
pass
- import atexit
+
atexit.register(readline.write_history_file, histfile)
- del os, histfile
+
+This code is actually automatically run when Python is run in
+:ref:`interactive mode <tut-interactive>` (see :ref:`rlcompleter-config`).
The following example extends the :class:`code.InteractiveConsole` class to
support history save/restore. ::
- import code
- import readline
import atexit
+ import code
import os
+ import readline
class HistoryConsole(code.InteractiveConsole):
def __init__(self, locals=None, filename="<console>",
diff --git a/Doc/library/resource.rst b/Doc/library/resource.rst
index ed85850..f8112cc 100644
--- a/Doc/library/resource.rst
+++ b/Doc/library/resource.rst
@@ -74,6 +74,27 @@ this module for those platforms.
``setrlimit`` may also raise :exc:`error` if the underlying system call
fails.
+.. function:: prlimit(pid, resource[, limits])
+
+ Combines :func:`setrlimit` and :func:`getrlimit` in one function and
+ supports to get and set the resources limits of an arbitrary process. If
+ *pid* is 0, then the call applies to the current process. *resource* and
+ *limits* have the same meaning as in :func:`setrlimit`, except that
+ *limits* is optional.
+
+ When *limits* is not given the function returns the *resource* limit of the
+ process *pid*. When *limits* is given the *resource* limit of the process is
+ set and the former resource limit is returned.
+
+ Raises :exc:`ProcessLookupError` when *pid* can't be found and
+ :exc:`PermissionError` when the user doesn't have ``CAP_SYS_RESOURCE`` for
+ the process.
+
+ Availability: Linux 2.6.36 or later with glibc 2.13 or later
+
+ .. versionadded:: 3.4
+
+
These symbols define resources whose consumption can be controlled using the
:func:`setrlimit` and :func:`getrlimit` functions described below. The values of
these symbols are exactly the constants used by C programs.
@@ -151,6 +172,80 @@ platform.
The maximum area (in bytes) of address space which may be taken by the process.
+.. data:: RLIMIT_MSGQUEUE
+
+ The number of bytes that can be allocated for POSIX message queues.
+
+ Availability: Linux 2.6.8 or later.
+
+ .. versionadded:: 3.4
+
+
+.. data:: RLIMIT_NICE
+
+ The ceiling for the process's nice level (calculated as 20 - rlim_cur).
+
+ Availability: Linux 2.6.12 or later.
+
+ .. versionadded:: 3.4
+
+
+.. data:: RLIMIT_RTPRIO
+
+ The ceiling of the real-time priority.
+
+ Availability: Linux 2.6.12 or later.
+
+ .. versionadded:: 3.4
+
+
+.. data:: RLIMIT_RTTIME
+
+ The time limit (in microseconds) on CPU time that a process can spend
+ under real-time scheduling without making a blocking syscall.
+
+ Availability: Linux 2.6.25 or later.
+
+ .. versionadded:: 3.4
+
+
+.. data:: RLIMIT_SIGPENDING
+
+ The number of signals which the process may queue.
+
+ Availability: Linux 2.6.8 or later.
+
+ .. versionadded:: 3.4
+
+.. data:: RLIMIT_SBSIZE
+
+ The maximum size (in bytes) of socket buffer usage for this user.
+ This limits the amount of network memory, and hence the amount of mbufs,
+ that this user may hold at any time.
+
+ Availability: FreeBSD 9 or later.
+
+ .. versionadded:: 3.4
+
+.. data:: RLIMIT_SWAP
+
+ The maximum size (in bytes) of the swap space that may be reserved or
+ used by all of this user id's processes.
+ This limit is enforced only if bit 1 of the vm.overcommit sysctl is set.
+ Please see :manpage:`tuning(7)` for a complete description of this sysctl.
+
+ Availability: FreeBSD 9 or later.
+
+ .. versionadded:: 3.4
+
+.. data:: RLIMIT_NPTS
+
+ The maximum number of pseudo-terminals created by this user id.
+
+ Availability: FreeBSD 9 or later.
+
+ .. versionadded:: 3.4
+
Resource Usage
--------------
diff --git a/Doc/library/rlcompleter.rst b/Doc/library/rlcompleter.rst
index 633088d..9ed01c7 100644
--- a/Doc/library/rlcompleter.rst
+++ b/Doc/library/rlcompleter.rst
@@ -27,18 +27,10 @@ Example::
readline.__name__ readline.parse_and_bind(
>>> readline.
-The :mod:`rlcompleter` module is designed for use with Python's interactive
-mode. A user can add the following lines to his or her initialization file
-(identified by the :envvar:`PYTHONSTARTUP` environment variable) to get
-automatic :kbd:`Tab` completion::
-
- try:
- import readline
- except ImportError:
- print("Module readline not available.")
- else:
- import rlcompleter
- readline.parse_and_bind("tab: complete")
+The :mod:`rlcompleter` module is designed for use with Python's
+:ref:`interactive mode <tut-interactive>`. Unless Python is run with the
+:option:`-S` option, the module is automatically imported and configured
+(see :ref:`rlcompleter-config`).
On platforms without :mod:`readline`, the :class:`Completer` class defined by
this module can still be used for custom purposes.
diff --git a/Doc/library/runpy.rst b/Doc/library/runpy.rst
index 6919bc0..ee9fbcf 100644
--- a/Doc/library/runpy.rst
+++ b/Doc/library/runpy.rst
@@ -44,28 +44,22 @@ The :mod:`runpy` module provides two functions:
below are defined in the supplied dictionary, those definitions are
overridden by :func:`run_module`.
- The special global variables ``__name__``, ``__file__``, ``__cached__``,
- ``__loader__``
- and ``__package__`` are set in the globals dictionary before the module
- code is executed (Note that this is a minimal set of variables - other
- variables may be set implicitly as an interpreter implementation detail).
+ The special global variables ``__name__``, ``__spec__``, ``__file__``,
+ ``__cached__``, ``__loader__`` and ``__package__`` are set in the globals
+ dictionary before the module code is executed (Note that this is a
+ minimal set of variables - other variables may be set implicitly as an
+ interpreter implementation detail).
``__name__`` is set to *run_name* if this optional argument is not
:const:`None`, to ``mod_name + '.__main__'`` if the named module is a
package and to the *mod_name* argument otherwise.
- ``__file__`` is set to the name provided by the module loader. If the
- loader does not make filename information available, this variable is set
- to :const:`None`.
+ ``__spec__`` will be set appropriately for the *actually* imported
+ module (that is, ``__spec__.name`` will always be *mod_name* or
+ ``mod_name + '.__main__``, never *run_name*).
- ``__cached__`` will be set to ``None``.
-
- ``__loader__`` is set to the :pep:`302` module loader used to retrieve the
- code for the module (This loader may be a wrapper around the standard
- import mechanism).
-
- ``__package__`` is set to *mod_name* if the named module is a package and
- to ``mod_name.rpartition('.')[0]`` otherwise.
+ ``__file__``, ``__cached__``, ``__loader__`` and ``__package__`` are
+ :ref:`set as normal <import-mod-attrs>` based on the module spec.
If the argument *alter_sys* is supplied and evaluates to :const:`True`,
then ``sys.argv[0]`` is updated with the value of ``__file__`` and
@@ -83,8 +77,13 @@ The :mod:`runpy` module provides two functions:
Added ability to execute packages by looking for a ``__main__`` submodule.
.. versionchanged:: 3.2
- Added ``__cached__`` global variable (see :PEP:`3147`).
+ Added ``__cached__`` global variable (see :pep:`3147`).
+ .. versionchanged:: 3.4
+ Updated to take advantage of the module spec feature added by
+ :pep:`451`. This allows ``__cached__`` to be set correctly for modules
+ run this way, as well as ensuring the real module name is always
+ accessible as ``__spec__.name``.
.. function:: run_path(file_path, init_globals=None, run_name=None)
@@ -108,23 +107,28 @@ The :mod:`runpy` module provides two functions:
below are defined in the supplied dictionary, those definitions are
overridden by :func:`run_path`.
- The special global variables ``__name__``, ``__file__``, ``__loader__``
- and ``__package__`` are set in the globals dictionary before the module
- code is executed (Note that this is a minimal set of variables - other
- variables may be set implicitly as an interpreter implementation detail).
+ The special global variables ``__name__``, ``__spec__``, ``__file__``,
+ ``__cached__``, ``__loader__`` and ``__package__`` are set in the globals
+ dictionary before the module code is executed (Note that this is a
+ minimal set of variables - other variables may be set implicitly as an
+ interpreter implementation detail).
``__name__`` is set to *run_name* if this optional argument is not
:const:`None` and to ``'<run_path>'`` otherwise.
- ``__file__`` is set to the name provided by the module loader. If the
- loader does not make filename information available, this variable is set
- to :const:`None`. For a simple script, this will be set to ``file_path``.
+ If the supplied path directly references a script file (whether as source
+ or as precompiled byte code), then ``__file__`` will be set to the
+ supplied path, and ``__spec__``, ``__cached__``, ``__loader__`` and
+ ``__package__`` will all be set to :const:`None`.
- ``__loader__`` is set to the :pep:`302` module loader used to retrieve the
- code for the module (This loader may be a wrapper around the standard
- import mechanism). For a simple script, this will be set to :const:`None`.
+ ``__spec__`` will be set to :const:`None` if the supplied path is a
+ direct path to a script (as source or as precompiled bytecode).
- ``__package__`` is set to ``__name__.rpartition('.')[0]``.
+ If the supplied path is a reference to a valid sys.path entry, then
+ ``__spec__`` will be set appropriately for the imported ``__main__``
+ module (that is, ``__spec__.name`` will always be ``__main__``).
+ ``__file__``, ``__cached__``, ``__loader__`` and ``__package__`` will be
+ :ref:`set as normal <import-mod-attrs>` based on the module spec.
A number of alterations are also made to the :mod:`sys` module. Firstly,
``sys.path`` may be altered as described above. ``sys.argv[0]`` is updated
@@ -141,6 +145,12 @@ The :mod:`runpy` module provides two functions:
.. versionadded:: 3.2
+ .. versionchanged:: 3.4
+ Updated to take advantage of the module spec feature added by
+ :pep:`451`. This allows ``__cached__`` to be set correctly in the
+ case where ``__main__`` is imported from a valid sys.path entry rather
+ than being executed directly.
+
.. seealso::
:pep:`338` - Executing modules as scripts
@@ -149,6 +159,9 @@ The :mod:`runpy` module provides two functions:
:pep:`366` - Main module explicit relative imports
PEP written and implemented by Nick Coghlan.
+ :pep:`451` - A ModuleSpec Type for the Import System
+ PEP written and implemented by Eric Snow
+
:ref:`using-on-general` - CPython command line details
The :func:`importlib.import_module` function
diff --git a/Doc/library/select.rst b/Doc/library/select.rst
index b1fd3a8..973a0cc 100644
--- a/Doc/library/select.rst
+++ b/Doc/library/select.rst
@@ -14,6 +14,14 @@ it also works for other file types (in particular, on Unix, it works on pipes).
It cannot be used on regular files to determine whether a file has grown since
it was last read.
+.. note::
+
+ The :mod:`selectors` module allows high-level and efficient I/O
+ multiplexing, built upon the :mod:`select` module primitives. Users are
+ encouraged to use the :mod:`selectors` module instead, unless they want
+ precise control over the OS-level primitives used.
+
+
The module defines the following:
@@ -37,21 +45,37 @@ The module defines the following:
increases this value, :c:func:`devpoll` may return an
incomplete list of active file descriptors.
+ The new file descriptor is :ref:`non-inheritable <fd_inheritance>`.
+
.. versionadded:: 3.3
+ .. versionchanged:: 3.4
+ The new file descriptor is now non-inheritable.
+
.. function:: epoll(sizehint=-1, flags=0)
(Only supported on Linux 2.5.44 and newer.) Return an edge polling object,
which can be used as Edge or Level Triggered interface for I/O
events. *sizehint* is deprecated and completely ignored. *flags* can be set
to :const:`EPOLL_CLOEXEC`, which causes the epoll descriptor to be closed
- automatically when :func:`os.execve` is called. See section
- :ref:`epoll-objects` below for the methods supported by epolling objects.
+ automatically when :func:`os.execve` is called.
+
+ See the :ref:`epoll-objects` section below for the methods supported by
+ epolling objects.
+
+ ``epoll`` objects support the context management protocol: when used in a
+ :keyword:`with` statement, the new file descriptor is automatically closed
+ at the end of the block.
+ The new file descriptor is :ref:`non-inheritable <fd_inheritance>`.
.. versionchanged:: 3.3
Added the *flags* parameter.
+ .. versionchanged:: 3.4
+ Support for the :keyword:`with` statement was added.
+ The new file descriptor is now non-inheritable.
+
.. function:: poll()
@@ -66,6 +90,11 @@ The module defines the following:
(Only supported on BSD.) Returns a kernel queue object; see section
:ref:`kqueue-objects` below for the methods supported by kqueue objects.
+ The new file descriptor is :ref:`non-inheritable <fd_inheritance>`.
+
+ .. versionchanged:: 3.4
+ The new file descriptor is now non-inheritable.
+
.. function:: kevent(ident, filter=KQ_FILTER_READ, flags=KQ_EV_ADD, fflags=0, data=0, udata=0)
@@ -144,6 +173,27 @@ descriptors), ``/dev/poll`` is O(active file descriptors).
object.
+.. method:: devpoll.close()
+
+ Close the file descriptor of the polling object.
+
+ .. versionadded:: 3.4
+
+
+.. attribute:: devpoll.closed
+
+ ``True`` if the polling object is closed.
+
+ .. versionadded:: 3.4
+
+
+.. method:: devpoll.fileno()
+
+ Return the file descriptor number of the polling object.
+
+ .. versionadded:: 3.4
+
+
.. method:: devpoll.register(fd[, eventmask])
Register a file descriptor with the polling object. Future calls to the
@@ -241,6 +291,11 @@ Edge and Level Trigger Polling (epoll) Objects
Close the control file descriptor of the epoll object.
+.. attribute:: epoll.closed
+
+ ``True`` if the epoll object is closed.
+
+
.. method:: epoll.fileno()
Return the file descriptor number of the control fd.
@@ -258,7 +313,7 @@ Edge and Level Trigger Polling (epoll) Objects
.. method:: epoll.modify(fd, eventmask)
- Modify a register file descriptor.
+ Modify a registered file descriptor.
.. method:: epoll.unregister(fd)
@@ -322,7 +377,7 @@ linearly scanned again. :c:func:`select` is O(highest file descriptor), while
Modifies an already registered fd. This has the same effect as
``register(fd, eventmask)``. Attempting to modify a file descriptor
- that was never registered causes an :exc:`IOError` exception with errno
+ that was never registered causes an :exc:`OSError` exception with errno
:const:`ENOENT` to be raised.
@@ -360,6 +415,11 @@ Kqueue Objects
Close the control file descriptor of the kqueue object.
+.. attribute:: kqueue.closed
+
+ ``True`` if the kqueue object is closed.
+
+
.. method:: kqueue.fileno()
Return the file descriptor number of the control fd.
diff --git a/Doc/library/selectors.rst b/Doc/library/selectors.rst
new file mode 100644
index 0000000..98377c8
--- /dev/null
+++ b/Doc/library/selectors.rst
@@ -0,0 +1,257 @@
+:mod:`selectors` -- High-level I/O multiplexing
+===============================================
+
+.. module:: selectors
+ :synopsis: High-level I/O multiplexing.
+
+.. versionadded:: 3.4
+
+
+Introduction
+------------
+
+This module allows high-level and efficient I/O multiplexing, built upon the
+:mod:`select` module primitives. Users are encouraged to use this module
+instead, unless they want precise control over the OS-level primitives used.
+
+It defines a :class:`BaseSelector` abstract base class, along with several
+concrete implementations (:class:`KqueueSelector`, :class:`EpollSelector`...),
+that can be used to wait for I/O readiness notification on multiple file
+objects. In the following, "file object" refers to any object with a
+:meth:`fileno()` method, or a raw file descriptor. See :term:`file object`.
+
+:class:`DefaultSelector` is an alias to the most efficient implementation
+available on the current platform: this should be the default choice for most
+users.
+
+.. note::
+ The type of file objects supported depends on the platform: on Windows,
+ sockets are supported, but not pipes, whereas on Unix, both are supported
+ (some other types may be supported as well, such as fifos or special file
+ devices).
+
+.. seealso::
+
+ :mod:`select`
+ Low-level I/O multiplexing module.
+
+
+Classes
+-------
+
+Classes hierarchy::
+
+ BaseSelector
+ +-- SelectSelector
+ +-- PollSelector
+ +-- EpollSelector
+ +-- KqueueSelector
+
+
+In the following, *events* is a bitwise mask indicating which I/O events should
+be waited for on a given file object. It can be a combination of the constants
+below:
+
+ +-----------------------+-----------------------------------------------+
+ | Constant | Meaning |
+ +=======================+===============================================+
+ | :const:`EVENT_READ` | Available for read |
+ +-----------------------+-----------------------------------------------+
+ | :const:`EVENT_WRITE` | Available for write |
+ +-----------------------+-----------------------------------------------+
+
+
+.. class:: SelectorKey
+
+ A :class:`SelectorKey` is a :class:`~collections.namedtuple` used to
+ associate a file object to its underlying file decriptor, selected event
+ mask and attached data. It is returned by several :class:`BaseSelector`
+ methods.
+
+ .. attribute:: fileobj
+
+ File object registered.
+
+ .. attribute:: fd
+
+ Underlying file descriptor.
+
+ .. attribute:: events
+
+ Events that must be waited for on this file object.
+
+ .. attribute:: data
+
+ Optional opaque data associated to this file object: for example, this
+ could be used to store a per-client session ID.
+
+
+.. class:: BaseSelector
+
+ A :class:`BaseSelector` is used to wait for I/O event readiness on multiple
+ file objects. It supports file stream registration, unregistration, and a
+ method to wait for I/O events on those streams, with an optional timeout.
+ It's an abstract base class, so cannot be instantiated. Use
+ :class:`DefaultSelector` instead, or one of :class:`SelectSelector`,
+ :class:`KqueueSelector` etc. if you want to specifically use an
+ implementation, and your platform supports it.
+ :class:`BaseSelector` and its concrete implementations support the
+ :term:`context manager` protocol.
+
+ .. method:: register(fileobj, events, data=None)
+
+ Register a file object for selection, monitoring it for I/O events.
+
+ *fileobj* is the file object to monitor. It may either be an integer
+ file descriptor or an object with a ``fileno()`` method.
+ *events* is a bitwise mask of events to monitor.
+ *data* is an opaque object.
+
+ This returns a new :class:`SelectorKey` instance, or raises a
+ :exc:`ValueError` in case of invalid event mask or file descriptor, or
+ :exc:`KeyError` if the file object is already registered.
+
+ .. method:: unregister(fileobj)
+
+ Unregister a file object from selection, removing it from monitoring. A
+ file object shall be unregistered prior to being closed.
+
+ *fileobj* must be a file object previously registered.
+
+ This returns the associated :class:`SelectorKey` instance, or raises a
+ :exc:`KeyError` if *fileobj* is not registered. It will raise
+ :exc:`ValueError` if *fileobj* is invalid (e.g. it has no ``fileno()``
+ method or its ``fileno()`` method has an invalid return value).
+
+ .. method:: modify(fileobj, events, data=None)
+
+ Change a registered file object's monitored events or attached data.
+
+ This is equivalent to :meth:`BaseSelector.unregister(fileobj)` followed
+ by :meth:`BaseSelector.register(fileobj, events, data)`, except that it
+ can be implemented more efficiently.
+
+ This returns a new :class:`SelectorKey` instance, or raises a
+ :exc:`ValueError` in case of invalid event mask or file descriptor, or
+ :exc:`KeyError` if the file object is not registered.
+
+ .. method:: select(timeout=None)
+
+ Wait until some registered file objects become ready, or the timeout
+ expires.
+
+ If ``timeout > 0``, this specifies the maximum wait time, in seconds.
+ If ``timeout <= 0``, the call won't block, and will report the currently
+ ready file objects.
+ If *timeout* is ``None``, the call will block until a monitored file object
+ becomes ready.
+
+ This returns a list of ``(key, events)`` tuples, one for each ready file
+ object.
+
+ *key* is the :class:`SelectorKey` instance corresponding to a ready file
+ object.
+ *events* is a bitmask of events ready on this file object.
+
+ .. note::
+ This method can return before any file object becomes ready or the
+ timeout has elapsed if the current process receives a signal: in this
+ case, an empty list will be returned.
+
+ .. method:: close()
+
+ Close the selector.
+
+ This must be called to make sure that any underlying resource is freed.
+ The selector shall not be used once it has been closed.
+
+ .. method:: get_key(fileobj)
+
+ Return the key associated with a registered file object.
+
+ This returns the :class:`SelectorKey` instance associated to this file
+ object, or raises :exc:`KeyError` if the file object is not registered.
+
+ .. method:: get_map()
+
+ Return a mapping of file objects to selector keys.
+
+ This returns a :class:`~collections.abc.Mapping` instance mapping
+ registered file objects to their associated :class:`SelectorKey`
+ instance.
+
+
+.. class:: DefaultSelector()
+
+ The default selector class, using the most efficient implementation
+ available on the current platform. This should be the default choice for
+ most users.
+
+
+.. class:: SelectSelector()
+
+ :func:`select.select`-based selector.
+
+
+.. class:: PollSelector()
+
+ :func:`select.poll`-based selector.
+
+
+.. class:: EpollSelector()
+
+ :func:`select.epoll`-based selector.
+
+ .. method:: fileno()
+
+ This returns the file descriptor used by the underlying
+ :func:`select.epoll` object.
+
+
+.. class:: KqueueSelector()
+
+ :func:`select.kqueue`-based selector.
+
+ .. method:: fileno()
+
+ This returns the file descriptor used by the underlying
+ :func:`select.kqueue` object.
+
+
+Examples
+--------
+
+Here is a simple echo server implementation::
+
+ import selectors
+ import socket
+
+ sel = selectors.DefaultSelector()
+
+ def accept(sock, mask):
+ conn, addr = sock.accept() # Should be ready
+ print('accepted', conn, 'from', addr)
+ conn.setblocking(False)
+ sel.register(conn, selectors.EVENT_READ, read)
+
+ def read(conn, mask):
+ data = conn.recv(1000) # Should be ready
+ if data:
+ print('echoing', repr(data), 'to', conn)
+ conn.send(data) # Hope it won't block
+ else:
+ print('closing', conn)
+ sel.unregister(conn)
+ conn.close()
+
+ sock = socket.socket()
+ sock.bind(('localhost', 1234))
+ sock.listen(100)
+ sock.setblocking(False)
+ sel.register(sock, selectors.EVENT_READ, accept)
+
+ while True:
+ events = sel.select()
+ for key, mask in events:
+ callback = key.data
+ callback(key.fileobj, mask)
diff --git a/Doc/library/shelve.rst b/Doc/library/shelve.rst
index 048f80a..22e202d 100644
--- a/Doc/library/shelve.rst
+++ b/Doc/library/shelve.rst
@@ -44,8 +44,11 @@ lots of shared sub-objects. The keys are ordinary strings.
.. note::
Do not rely on the shelf being closed automatically; always call
- :meth:`close` explicitly when you don't need it any more, or use a
- :keyword:`with` statement with :func:`contextlib.closing`.
+ :meth:`~Shelf.close` explicitly when you don't need it any more, or
+ use :func:`shelve.open` as a context manager::
+
+ with shelve.open('spam') as db:
+ db['eggs'] = 'eggs'
.. warning::
@@ -118,10 +121,16 @@ Restrictions
The *keyencoding* parameter is the encoding used to encode keys before they
are used with the underlying dict.
- .. versionadded:: 3.2
- The *keyencoding* parameter; previously, keys were always encoded in
+ A :class:`Shelf` object can also be used as a context manager, in which
+ case it will be automatically closed when the :keyword:`with` block ends.
+
+ .. versionchanged:: 3.2
+ Added the *keyencoding* parameter; previously, keys were always encoded in
UTF-8.
+ .. versionchanged:: 3.4
+ Added context manager support.
+
.. class:: BsdDbShelf(dict, protocol=None, writeback=False, keyencoding='utf-8')
diff --git a/Doc/library/shutil.rst b/Doc/library/shutil.rst
index a1457d0..e4f348c 100644
--- a/Doc/library/shutil.rst
+++ b/Doc/library/shutil.rst
@@ -53,7 +53,7 @@ Directory and files operations
*dst* and return *dst*. *src* and *dst* are path names given as strings.
*dst* must be the complete target file name; look at :func:`shutil.copy`
for a copy that accepts a target directory path. If *src* and *dst*
- specify the same file, :exc:`Error` is raised.
+ specify the same file, :exc:`SameFileError` is raised.
The destination location must be writable; otherwise, an :exc:`OSError`
exception will be raised. If *dst* already exists, it will be replaced.
@@ -69,6 +69,19 @@ Directory and files operations
Added *follow_symlinks* argument.
Now returns *dst*.
+ .. versionchanged:: 3.4
+ Raise :exc:`SameFileError` instead of :exc:`Error`. Since the former is
+ a subclass of the latter, this change is backward compatible.
+
+
+.. exception:: SameFileError
+
+ This exception is raised if source and destination in :func:`copyfile`
+ are the same file.
+
+ .. versionadded:: 3.4
+
+
.. function:: copymode(src, dst, *, follow_symlinks=True)
Copy the permission bits from *src* to *dst*. The file contents, owner, and
@@ -380,11 +393,10 @@ provided by this module. ::
errors.extend(err.args[0])
try:
copystat(src, dst)
- except WindowsError:
- # can't copy file access times on Windows
- pass
except OSError as why:
- errors.extend((src, dst, str(why)))
+ # can't copy file access times on Windows
+ if why.winerror is None:
+ errors.extend((src, dst, str(why)))
if errors:
raise Error(errors)
diff --git a/Doc/library/site.rst b/Doc/library/site.rst
index 36b80c3..e57b8cc 100644
--- a/Doc/library/site.rst
+++ b/Doc/library/site.rst
@@ -38,6 +38,9 @@ Unix and Macintosh). For each of the distinct head-tail combinations, it sees
if it refers to an existing directory, and if so, adds it to ``sys.path`` and
also inspects the newly added path for configuration files.
+.. deprecated:: 3.4
+ Support for the "site-python" directory will be removed in 3.5.
+
If a file named "pyvenv.cfg" exists one directory above sys.executable,
sys.prefix and sys.exec_prefix are set to that directory and
it is also checked for site-packages and site-python (sys.base_prefix and
@@ -96,7 +99,11 @@ After these path manipulations, an attempt is made to import a module named
:mod:`sitecustomize`, which can perform arbitrary site-specific customizations.
It is typically created by a system administrator in the site-packages
directory. If this import fails with an :exc:`ImportError` exception, it is
-silently ignored.
+silently ignored. If Python is started without output streams available, as
+with :file:`pythonw.exe` on Windows (which is used by default to start IDLE),
+attempted output from :mod:`sitecustomize` is ignored. Any exception other
+than :exc:`ImportError` causes a silent and perhaps mysterious failure of the
+process.
.. index:: module: usercustomize
@@ -111,6 +118,27 @@ empty, and the path manipulations are skipped; however the import of
:mod:`sitecustomize` and :mod:`usercustomize` is still attempted.
+.. _rlcompleter-config:
+
+Readline configuration
+----------------------
+
+On systems that support :mod:`readline`, this module will also import and
+configure the :mod:`rlcompleter` module, if Python is started in
+:ref:`interactive mode <tut-interactive>` and without the :option:`-S` option.
+The default behavior is enable tab-completion and to use
+:file:`~/.python_history` as the history save file. To disable it, delete (or
+override) the :data:`sys.__interactivehook__` attribute in your
+:mod:`sitecustomize` or :mod:`usercustomize` module or your
+:envvar:`PYTHONSTARTUP` file.
+
+.. versionchanged:: 3.4
+ Activation of rlcompleter and history was made automatic.
+
+
+Module contents
+---------------
+
.. data:: PREFIXES
A list of prefixes for site-packages directories.
@@ -153,8 +181,7 @@ empty, and the path manipulations are skipped; however the import of
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.
+ unless the Python interpreter was started with the :option:`-S` flag.
.. versionchanged:: 3.3
This function used to be called unconditionnally.
diff --git a/Doc/library/smtpd.rst b/Doc/library/smtpd.rst
index f305818..3ebed06 100644
--- a/Doc/library/smtpd.rst
+++ b/Doc/library/smtpd.rst
@@ -27,7 +27,8 @@ SMTPServer Objects
------------------
-.. class:: SMTPServer(localaddr, remoteaddr, data_size_limit=33554432)
+.. class:: SMTPServer(localaddr, remoteaddr, data_size_limit=33554432,\
+ map=None)
Create a new :class:`SMTPServer` object, which binds to local address
*localaddr*. It will treat *remoteaddr* as an upstream SMTP relayer. It
@@ -38,6 +39,8 @@ SMTPServer Objects
accepted in a ``DATA`` command. A value of ``None`` or ``0`` means no
limit.
+ A dictionary can be specified in *map* to avoid using a global socket map.
+
.. method:: process_message(peer, mailfrom, rcpttos, data)
Raise :exc:`NotImplementedError` exception. Override this in subclasses to
@@ -53,6 +56,9 @@ SMTPServer Objects
Override this in subclasses to use a custom :class:`SMTPChannel` for
managing SMTP clients.
+ .. versionchanged:: 3.4
+ The *map* argument was added.
+
DebuggingServer Objects
-----------------------
@@ -90,11 +96,20 @@ MailmanProxy Objects
SMTPChannel Objects
-------------------
-.. class:: SMTPChannel(server, conn, addr)
+.. class:: SMTPChannel(server, conn, addr, data_size_limit=33554432,\
+ map=None))
Create a new :class:`SMTPChannel` object which manages the communication
between the server and a single SMTP client.
+ *conn* and *addr* are as per the instance variables described below.
+
+ *data_size_limit* specifies the maximum number of bytes that will be
+ accepted in a ``DATA`` command. A value of ``None`` or ``0`` means no
+ limit.
+
+ A dictionary can be specified in *map* to avoid using a global socket map.
+
To use a custom SMTPChannel implementation you need to override the
:attr:`SMTPServer.channel_class` of your :class:`SMTPServer`.
diff --git a/Doc/library/smtplib.rst b/Doc/library/smtplib.rst
index addc6be..46cfa36 100644
--- a/Doc/library/smtplib.rst
+++ b/Doc/library/smtplib.rst
@@ -69,20 +69,15 @@ Protocol) and :rfc:`1869` (SMTP Service Extensions).
required from the beginning of the connection and using :meth:`starttls` is
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. The optional
- arguments *local_hostname* and *source_address* have the same meaning as
- they do in the :class:`SMTP` class. *keyfile* and *certfile* are also
- optional, and can contain a PEM formatted private key 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). 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.
+ arguments *local_hostname*, *timeout* and *source_address* have the same
+ meaning as they do in the :class:`SMTP` class. *context*, also optional,
+ can contain a :class:`~ssl.SSLContext` and allows to configure various
+ aspects of the secure connection. Please read :ref:`ssl-security` for
+ best practices.
+
+ *keyfile* and *certfile* are a legacy alternative to *context*, and can
+ point to a PEM formatted private key and certificate chain file for the
+ SSL connection.
.. versionchanged:: 3.3
*context* was added.
@@ -90,6 +85,10 @@ Protocol) and :rfc:`1869` (SMTP Service Extensions).
.. versionchanged:: 3.3
source_address argument was added.
+ .. versionchanged:: 3.4
+ The class now supports hostname check with
+ :attr:`ssl.SSLContext.check_hostname` and *Server Name Indication* (see
+ :data:`ssl.HAS_SNI`).
.. class:: LMTP(host='', port=LMTP_PORT, local_hostname=None, source_address=None)
@@ -110,8 +109,11 @@ A nice selection of exceptions is defined as well:
.. exception:: SMTPException
- The base exception class for all the other exceptions provided by this
- module.
+ Subclass of :exc:`OSError` that is the base exception class for all
+ the other exceptions provided by this module.
+
+ .. versionchanged:: 3.4
+ SMTPException became subclass of :exc:`OSError`
.. exception:: SMTPServerDisconnected
@@ -316,6 +318,11 @@ An :class:`SMTP` instance has the following methods:
.. versionchanged:: 3.3
*context* was added.
+ .. versionchanged:: 3.4
+ The method now supports hostname check with
+ :attr:`SSLContext.check_hostname` and *Server Name Indicator* (see
+ :data:`~ssl.HAS_SNI`).
+
.. 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 3a49eed..54c6bad 100644
--- a/Doc/library/socket.rst
+++ b/Doc/library/socket.rst
@@ -6,8 +6,7 @@
This module provides access to the BSD *socket* interface. It is available on
-all modern Unix systems, Windows, MacOS, OS/2, and probably additional
-platforms.
+all modern Unix systems, Windows, MacOS, and probably additional platforms.
.. note::
@@ -107,8 +106,8 @@ created. Socket addresses are represented as follows:
.. versionadded:: 3.3
-- Certain other address families (:const:`AF_BLUETOOTH`, :const:`AF_PACKET`)
- support specific representations.
+- Certain other address families (:const:`AF_BLUETOOTH`, :const:`AF_PACKET`,
+ :const:`AF_CAN`) support specific representations.
.. XXX document them!
@@ -264,6 +263,16 @@ Constants
.. versionadded:: 3.3
+.. data:: CAN_BCM
+ CAN_BCM_*
+
+ CAN_BCM, in the CAN protocol family, is the broadcast manager (BCM) protocol.
+ Broadcast manager constants, documented in the Linux documentation, are also
+ defined in the socket module.
+
+ Availability: Linux >= 2.6.25.
+
+ .. versionadded:: 3.4
.. data:: AF_RDS
PF_RDS
@@ -290,6 +299,11 @@ Constants
TIPC related constants, matching the ones exported by the C socket API. See
the TIPC documentation for more information.
+.. data:: AF_LINK
+
+ Availability: BSD, OSX.
+
+ .. versionadded:: 3.4
.. data:: has_ipv6
@@ -306,20 +320,29 @@ Creating sockets
The following functions all create :ref:`socket objects <socket-objects>`.
-.. function:: socket([family[, type[, proto]]])
+.. function:: socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
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`, :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`.
+ constants. The protocol number is usually zero and may be omitted or in the
+ case where the address family is :const:`AF_CAN` the protocol should be one
+ of :const:`CAN_RAW` or :const:`CAN_BCM`.
+
+ The newly created socket is :ref:`non-inheritable <fd_inheritance>`.
.. versionchanged:: 3.3
The AF_CAN family was added.
The AF_RDS family was added.
+ .. versionchanged:: 3.4
+ The CAN_BCM protocol was added.
+
+ .. versionchanged:: 3.4
+ The returned socket is now non-inheritable.
+
.. function:: socketpair([family[, type[, proto]]])
@@ -329,10 +352,15 @@ The following functions all create :ref:`socket objects <socket-objects>`.
if defined on the platform; otherwise, the default is :const:`AF_INET`.
Availability: Unix.
+ The newly created sockets are :ref:`non-inheritable <fd_inheritance>`.
+
.. versionchanged:: 3.2
The returned socket objects now support the whole socket API, rather
than a subset.
+ .. versionchanged:: 3.4
+ The returned sockets are now non-inheritable.
+
.. function:: create_connection(address[, timeout[, source_address]])
@@ -360,7 +388,7 @@ The following functions all create :ref:`socket objects <socket-objects>`.
support for the :keyword:`with` statement was added.
-.. function:: fromfd(fd, family, type[, proto])
+.. function:: fromfd(fd, family, type, proto=0)
Duplicate the file descriptor *fd* (an integer as returned by a file object's
:meth:`fileno` method) and build a socket object from the result. Address
@@ -371,6 +399,11 @@ The following functions all create :ref:`socket objects <socket-objects>`.
a socket passed to a program as standard input or output (such as a server
started by the Unix inet daemon). The socket is assumed to be in blocking mode.
+ The newly created socket is :ref:`non-inheritable <fd_inheritance>`.
+
+ .. versionchanged:: 3.4
+ The returned socket is now non-inheritable.
+
.. function:: fromshare(data)
@@ -598,7 +631,10 @@ The :mod:`socket` module also offers various network-related services:
both the value of *address_family* and the underlying implementation of
:c:func:`inet_pton`.
- Availability: Unix (maybe not all platforms).
+ Availability: Unix (maybe not all platforms), Windows.
+
+ .. versionchanged:: 3.4
+ Windows support added
.. function:: inet_ntop(address_family, packed_ip)
@@ -614,7 +650,10 @@ The :mod:`socket` module also offers various network-related services:
specified address family, :exc:`ValueError` will be raised. A
:exc:`OSError` is raised for errors from the call to :func:`inet_ntop`.
- Availability: Unix (maybe not all platforms).
+ Availability: Unix (maybe not all platforms), Windows.
+
+ .. versionchanged:: 3.4
+ Windows support added
..
@@ -735,6 +774,11 @@ to sockets.
*new* socket object usable to send and receive data on the connection, and
*address* is the address bound to the socket on the other end of the connection.
+ The newly created socket is :ref:`non-inheritable <fd_inheritance>`.
+
+ .. versionchanged:: 3.4
+ The socket is now non-inheritable.
+
.. method:: socket.bind(address)
@@ -787,6 +831,16 @@ to sockets.
.. versionadded:: 3.2
+.. method:: socket.dup()
+
+ Duplicate the socket.
+
+ The newly created socket is :ref:`non-inheritable <fd_inheritance>`.
+
+ .. versionchanged:: 3.4
+ The socket is now non-inheritable.
+
+
.. method:: socket.fileno()
Return the socket's file descriptor (a small integer). This is useful with
@@ -797,6 +851,15 @@ to sockets.
this limitation.
+.. method:: socket.get_inheritable()
+
+ Get the :ref:`inheritable flag <fd_inheritance>` of the socket's file
+ descriptor or socket's handle: ``True`` if the socket can be inherited in
+ child processes, ``False`` if it cannot.
+
+ .. versionadded:: 3.4
+
+
.. method:: socket.getpeername()
Return the remote address to which the socket is connected. This is useful to
@@ -1083,6 +1146,14 @@ to sockets.
.. versionadded:: 3.3
+.. method:: socket.set_inheritable(inheritable)
+
+ Set the :ref:`inheritable flag <fd_inheritance>` of the socket's file
+ descriptor or socket's handle.
+
+ .. versionadded:: 3.4
+
+
.. method:: socket.setblocking(flag)
Set blocking or non-blocking mode of the socket: if *flag* is false, the
@@ -1364,7 +1435,16 @@ the interface::
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::
+network using the raw socket protocol. To use CAN with the broadcast
+manager protocol instead, open a socket with::
+
+ socket.socket(socket.AF_CAN, socket.SOCK_DGRAM, socket.CAN_BCM)
+
+After binding (:const:`CAN_RAW`) or connecting (:const:`CAN_BCM`) the socket, you
+can use the :meth:`socket.send`, and the :meth:`socket.recv` operations (and
+their counterparts) on the socket object as usual.
+
+This example might require special priviledge::
import socket
import struct
diff --git a/Doc/library/spwd.rst b/Doc/library/spwd.rst
index add611f..58be78f 100644
--- a/Doc/library/spwd.rst
+++ b/Doc/library/spwd.rst
@@ -19,9 +19,9 @@ below, see ``<shadow.h>``):
+-------+---------------+---------------------------------+
| Index | Attribute | Meaning |
+=======+===============+=================================+
-| 0 | ``sp_nam`` | Login name |
+| 0 | ``sp_namp`` | Login name |
+-------+---------------+---------------------------------+
-| 1 | ``sp_pwd`` | Encrypted password |
+| 1 | ``sp_pwdp`` | Encrypted password |
+-------+---------------+---------------------------------+
| 2 | ``sp_lstchg`` | Date of last change |
+-------+---------------+---------------------------------+
@@ -36,15 +36,15 @@ below, see ``<shadow.h>``):
+-------+---------------+---------------------------------+
| 6 | ``sp_inact`` | Number of days after password |
| | | expires until account is |
-| | | blocked |
+| | | disabled |
+-------+---------------+---------------------------------+
| 7 | ``sp_expire`` | Number of days since 1970-01-01 |
-| | | until account is disabled |
+| | | when account expires |
+-------+---------------+---------------------------------+
| 8 | ``sp_flag`` | Reserved |
+-------+---------------+---------------------------------+
-The sp_nam and sp_pwd items are strings, all others are integers.
+The sp_namp and sp_pwdp items are strings, all others are integers.
:exc:`KeyError` is raised if the entry asked for cannot be found.
The following functions are defined:
diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst
index 8a50f87..c2e6080 100644
--- a/Doc/library/sqlite3.rst
+++ b/Doc/library/sqlite3.rst
@@ -13,8 +13,8 @@ SQLite for internal data storage. It's also possible to prototype an
application using SQLite and then port the code to a larger database such as
PostgreSQL or Oracle.
-sqlite3 was written by Gerhard Häring and provides a SQL interface compliant
-with the DB-API 2.0 specification described by :pep:`249`.
+The sqlite3 module was written by Gerhard Häring. It provides a SQL interface
+compliant with the DB-API 2.0 specification described by :pep:`249`.
To use the module, you must first create a :class:`Connection` object that
represents the database. Here the data will be stored in the
@@ -31,23 +31,29 @@ and call its :meth:`~Cursor.execute` method to perform SQL commands::
c = conn.cursor()
# Create table
- c.execute('''create table stocks
- (date text, trans text, symbol text,
- qty real, price real)''')
+ c.execute('''CREATE TABLE stocks
+ (date text, trans text, symbol text, qty real, price real)''')
# Insert a row of data
- c.execute("""insert into stocks
- values ('2006-01-05','BUY','RHAT',100,35.14)""")
+ c.execute("INSERT INTO stocks VALUES ('2006-01-05','BUY','RHAT',100,35.14)")
# Save (commit) the changes
conn.commit()
- # We can also close the cursor if we are done with it
- c.close()
+ # We can also close the connection if we are done with it.
+ # Just be sure any changes have been committed or they will be lost.
+ conn.close()
+
+The data you've saved is persistent and is available in subsequent sessions::
+
+ import sqlite3
+ conn = sqlite3.connect('example.db')
+ c = conn.cursor()
Usually your SQL operations will need to use values from Python variables. You
shouldn't assemble your query using Python's string operations because doing so
-is insecure; it makes your program vulnerable to an SQL injection attack.
+is insecure; it makes your program vulnerable to an SQL injection attack
+(see http://xkcd.com/327/ for humorous example of what can go wrong).
Instead, use the DB-API's parameter substitution. Put ``?`` as a placeholder
wherever you want to use a value, and then provide a tuple of values as the
@@ -56,19 +62,20 @@ modules may use a different placeholder, such as ``%s`` or ``:1``.) For
example::
# Never do this -- insecure!
- symbol = 'IBM'
- c.execute("select * from stocks where symbol = '%s'" % symbol)
+ symbol = 'RHAT'
+ c.execute("SELECT * FROM stocks WHERE symbol = '%s'" % symbol)
# Do this instead
- t = ('IBM',)
- c.execute('select * from stocks where symbol=?', t)
+ t = ('RHAT',)
+ c.execute('SELECT * FROM stocks WHERE symbol=?', t)
+ print(c.fetchone())
- # Larger example
- for t in [('2006-03-28', 'BUY', 'IBM', 1000, 45.00),
- ('2006-04-05', 'BUY', 'MSFT', 1000, 72.00),
- ('2006-04-06', 'SELL', 'IBM', 500, 53.00),
- ]:
- c.execute('insert into stocks values (?,?,?,?,?)', t)
+ # Larger example that inserts many records at a time
+ purchases = [('2006-03-28', 'BUY', 'IBM', 1000, 45.00),
+ ('2006-04-05', 'BUY', 'MSFT', 1000, 72.00),
+ ('2006-04-06', 'SELL', 'IBM', 500, 53.00),
+ ]
+ c.executemany('INSERT INTO stocks VALUES (?,?,?,?,?)', purchases)
To retrieve data after executing a SELECT statement, you can either treat the
cursor as an :term:`iterator`, call the cursor's :meth:`~Cursor.fetchone` method to
@@ -77,16 +84,13 @@ matching rows.
This example uses the iterator form::
- >>> c = conn.cursor()
- >>> c.execute('select * from stocks order by price')
- >>> for row in c:
- ... print(row)
- ...
+ >>> for row in c.execute('SELECT * FROM stocks ORDER BY price'):
+ print(row)
+
('2006-01-05', 'BUY', 'RHAT', 100, 35.14)
('2006-03-28', 'BUY', 'IBM', 1000, 45.0)
('2006-04-06', 'SELL', 'IBM', 500, 53.0)
- ('2006-04-05', 'BUY', 'MSOFT', 1000, 72.0)
- >>>
+ ('2006-04-05', 'BUY', 'MSFT', 1000, 72.0)
.. seealso::
@@ -99,6 +103,9 @@ This example uses the iterator form::
The SQLite web page; the documentation describes the syntax and the
available data types for the supported SQL dialect.
+ http://www.w3schools.com/sql/
+ Tutorial, reference and examples for learning SQL syntax.
+
:pep:`249` - Database API Specification 2.0
PEP written by Marc-André Lemburg.
@@ -159,7 +166,7 @@ Module functions and constants
first blank for the column name: the column name would simply be "x".
-.. function:: connect(database[, timeout, detect_types, isolation_level, check_same_thread, factory, cached_statements])
+.. function:: connect(database[, timeout, detect_types, isolation_level, check_same_thread, factory, cached_statements, uri])
Opens a connection to the SQLite database file *database*. You can use
``":memory:"`` to open a database connection to a database that resides in RAM
@@ -195,6 +202,18 @@ Module functions and constants
for the connection, you can set the *cached_statements* parameter. The currently
implemented default is to cache 100 statements.
+ If *uri* is true, *database* is interpreted as a URI. This allows you
+ to specify options. For example, to open a database in read-only mode
+ you can use::
+
+ db = sqlite3.connect('file:path/to/database?mode=ro', uri=True)
+
+ More information about this feature, including a list of recognized options, can
+ be found in the `SQLite URI documentation <http://www.sqlite.org/uri.html>`_.
+
+ .. versionchanged:: 3.4
+ Added the *uri* parameter.
+
.. function:: register_converter(typename, callable)
@@ -505,7 +524,7 @@ Cursor Objects
.. method:: execute(sql, [parameters])
- Executes an SQL statement. The SQL statement may be parametrized (i. e.
+ Executes an SQL statement. The SQL statement may be parameterized (i. e.
placeholders instead of SQL literals). The :mod:`sqlite3` module supports two
kinds of placeholders: question marks (qmark style) and named placeholders
(named style).
@@ -702,19 +721,20 @@ The following Python types can thus be sent to SQLite without any problem:
This is how SQLite types are converted to Python types by default:
-+-------------+---------------------------------------------+
-| SQLite type | Python type |
-+=============+=============================================+
-| ``NULL`` | :const:`None` |
-+-------------+---------------------------------------------+
-| ``INTEGER`` | :class:`int` |
-+-------------+---------------------------------------------+
-| ``REAL`` | :class:`float` |
-+-------------+---------------------------------------------+
-| ``TEXT`` | depends on text_factory, str by default |
-+-------------+---------------------------------------------+
-| ``BLOB`` | :class:`bytes` |
-+-------------+---------------------------------------------+
++-------------+----------------------------------------------+
+| SQLite type | Python type |
++=============+==============================================+
+| ``NULL`` | :const:`None` |
++-------------+----------------------------------------------+
+| ``INTEGER`` | :class:`int` |
++-------------+----------------------------------------------+
+| ``REAL`` | :class:`float` |
++-------------+----------------------------------------------+
+| ``TEXT`` | depends on :attr:`~Connection.text_factory`, |
+| | :class:`str` by default |
++-------------+----------------------------------------------+
+| ``BLOB`` | :class:`bytes` |
++-------------+----------------------------------------------+
The type system of the :mod:`sqlite3` module is extensible in two ways: you can
store additional Python types in a SQLite database via object adaptation, and
@@ -730,9 +750,6 @@ use other Python types with SQLite, you must **adapt** them to one of the
sqlite3 module's supported types for SQLite: one of NoneType, int, float,
str, bytes.
-The :mod:`sqlite3` module uses Python object adaptation, as described in
-:pep:`246` for this. The protocol to use is :class:`PrepareProtocol`.
-
There are two ways to enable the :mod:`sqlite3` module to adapt a custom Python
type to one of the supported ones.
@@ -788,8 +805,8 @@ and constructs a :class:`Point` object from it.
.. note::
- Converter functions **always** get called with a string, no matter under which
- data type you sent the value to SQLite.
+ Converter functions **always** get called with a :class:`bytes` object, no
+ matter under which data type you sent the value to SQLite.
::
diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst
index aa5a52f..94a0c81 100644
--- a/Doc/library/ssl.rst
+++ b/Doc/library/ssl.rst
@@ -26,7 +26,8 @@ probably additional platforms, as long as OpenSSL is installed on that platform.
Some behavior may be platform dependent, since calls are made to the
operating system socket APIs. The installed version of OpenSSL may also
- cause variations in behavior.
+ cause variations in behavior. For example, TLSv1.1 and TLSv1.2 come with
+ openssl version 1.0.1.
.. warning::
Don't use this module without reading the :ref:`ssl-security`. Doing so
@@ -186,14 +187,16 @@ instead.
.. table::
- ======================== ========= ========= ========== =========
- *client* / **server** **SSLv2** **SSLv3** **SSLv23** **TLSv1**
- ------------------------ --------- --------- ---------- ---------
- *SSLv2* yes no yes no
- *SSLv3* no yes yes no
- *SSLv23* yes no yes no
- *TLSv1* no no yes yes
- ======================== ========= ========= ========== =========
+ ======================== ========= ========= ========== ========= =========== ===========
+ *client* / **server** **SSLv2** **SSLv3** **SSLv23** **TLSv1** **TLSv1.1** **TLSv1.2**
+ ------------------------ --------- --------- ---------- --------- ----------- -----------
+ *SSLv2* yes no yes no no no
+ *SSLv3* no yes yes no no no
+ *SSLv23* yes no yes no no no
+ *TLSv1* no no yes yes no no
+ *TLSv1.1* no no yes no yes no
+ *TLSv1.2* no no yes no no yes
+ ======================== ========= ========= ========== ========= =========== ===========
.. note::
@@ -227,6 +230,58 @@ instead.
.. versionchanged:: 3.2
New optional argument *ciphers*.
+
+Context creation
+^^^^^^^^^^^^^^^^
+
+A convenience function helps create :class:`SSLContext` objects for common
+purposes.
+
+.. function:: create_default_context(purpose=Purpose.SERVER_AUTH, cafile=None, capath=None, cadata=None)
+
+ Return a new :class:`SSLContext` object with default settings for
+ the given *purpose*. The settings are chosen by the :mod:`ssl` module,
+ and usually represent a higher security level than when calling the
+ :class:`SSLContext` constructor directly.
+
+ *cafile*, *capath*, *cadata* represent optional CA certificates to
+ trust for certificate verification, as in
+ :meth:`SSLContext.load_verify_locations`. If all three are
+ :const:`None`, this function can choose to trust the system's default
+ CA certificates instead.
+
+ The settings in Python 3.4 are: :data:`PROTOCOL_SSLv23`, :data:`OP_NO_SSLv2`,
+ and :data:`OP_NO_SSLv3` with high encryption cipher suites without RC4 and
+ without unauthenticated cipher suites. Passing :data:`~Purpose.SERVER_AUTH`
+ as *purpose* sets :data:`~SSLContext.verify_mode` to :data:`CERT_REQUIRED`
+ and either loads CA certificates (when at least one of *cafile*, *capath* or
+ *cadata* is given) or uses :meth:`SSLContext.load_default_certs` to load
+ default CA certificates.
+
+ .. note::
+ The protocol, options, cipher and other settings may change to more
+ restrictive values anytime without prior deprecation. The values
+ represent a fair balance between compatibility and security.
+
+ If your application needs specific settings, you should create a
+ :class:`SSLContext` and apply the settings yourself.
+
+ .. note::
+ If you find that when certain older clients or servers attempt to connect
+ with a :class:`SSLContext` created by this function that they get an
+ error stating "Protocol or cipher suite mismatch", it may be that they
+ only support SSL3.0 which this function excludes using the
+ :data:`OP_NO_SSLv3`. SSL3.0 has problematic security due to a number of
+ poor implementations and it's reliance on MD5 within the protocol. If you
+ wish to continue to use this function but still allow SSL 3.0 connections
+ you can re-enable them using::
+
+ ctx = ssl.create_default_context(Purpose.CLIENT_AUTH)
+ ctx.options &= ~ssl.OP_NO_SSLv3
+
+ .. versionadded:: 3.4
+
+
Random generation
^^^^^^^^^^^^^^^^^
@@ -356,6 +411,61 @@ Certificate handling
Given a certificate as an ASCII PEM string, returns a DER-encoded sequence of
bytes for that same certificate.
+.. function:: get_default_verify_paths()
+
+ Returns a named tuple with paths to OpenSSL's default cafile and capath.
+ The paths are the same as used by
+ :meth:`SSLContext.set_default_verify_paths`. The return value is a
+ :term:`named tuple` ``DefaultVerifyPaths``:
+
+ * :attr:`cafile` - resolved path to cafile or None if the file doesn't exist,
+ * :attr:`capath` - resolved path to capath or None if the directory doesn't exist,
+ * :attr:`openssl_cafile_env` - OpenSSL's environment key that points to a cafile,
+ * :attr:`openssl_cafile` - hard coded path to a cafile,
+ * :attr:`openssl_capath_env` - OpenSSL's environment key that points to a capath,
+ * :attr:`openssl_capath` - hard coded path to a capath directory
+
+ .. versionadded:: 3.4
+
+.. function:: enum_certificates(store_name)
+
+ Retrieve certificates from Windows' system cert store. *store_name* may be
+ one of ``CA``, ``ROOT`` or ``MY``. Windows may provide additional cert
+ stores, too.
+
+ The function returns a list of (cert_bytes, encoding_type, trust) tuples.
+ The encoding_type specifies the encoding of cert_bytes. It is either
+ :const:`x509_asn` for X.509 ASN.1 data or :const:`pkcs_7_asn` for
+ PKCS#7 ASN.1 data. Trust specifies the purpose of the certificate as a set
+ of OIDS or exactly ``True`` if the certificate is trustworthy for all
+ purposes.
+
+ Example::
+
+ >>> ssl.enum_certificates("CA")
+ [(b'data...', 'x509_asn', {'1.3.6.1.5.5.7.3.1', '1.3.6.1.5.5.7.3.2'}),
+ (b'data...', 'x509_asn', True)]
+
+ Availability: Windows.
+
+ .. versionadded:: 3.4
+
+.. function:: enum_crls(store_name)
+
+ Retrieve CRLs from Windows' system cert store. *store_name* may be
+ one of ``CA``, ``ROOT`` or ``MY``. Windows may provide additional cert
+ stores, too.
+
+ The function returns a list of (cert_bytes, encoding_type, trust) tuples.
+ The encoding_type specifies the encoding of cert_bytes. It is either
+ :const:`x509_asn` for X.509 ASN.1 data or :const:`pkcs_7_asn` for
+ PKCS#7 ASN.1 data.
+
+ Availability: Windows.
+
+ .. versionadded:: 3.4
+
+
Constants
^^^^^^^^^
@@ -392,6 +502,38 @@ Constants
be passed, either to :meth:`SSLContext.load_verify_locations` or as a
value of the ``ca_certs`` parameter to :func:`wrap_socket`.
+.. data:: VERIFY_DEFAULT
+
+ Possible value for :attr:`SSLContext.verify_flags`. In this mode,
+ certificate revocation lists (CRLs) are not checked. By default OpenSSL
+ does neither require nor verify CRLs.
+
+ .. versionadded:: 3.4
+
+.. data:: VERIFY_CRL_CHECK_LEAF
+
+ Possible value for :attr:`SSLContext.verify_flags`. In this mode, only the
+ peer cert is check but non of the intermediate CA certificates. The mode
+ requires a valid CRL that is signed by the peer cert's issuer (its direct
+ ancestor CA). If no proper has been loaded
+ :attr:`SSLContext.load_verify_locations`, validation will fail.
+
+ .. versionadded:: 3.4
+
+.. data:: VERIFY_CRL_CHECK_CHAIN
+
+ Possible value for :attr:`SSLContext.verify_flags`. In this mode, CRLs of
+ all certificates in the peer cert chain are checked.
+
+ .. versionadded:: 3.4
+
+.. data:: VERIFY_X509_STRICT
+
+ Possible value for :attr:`SSLContext.verify_flags` to disable workarounds
+ for broken X.509 certificates.
+
+ .. versionadded:: 3.4
+
.. data:: PROTOCOL_SSLv2
Selects SSL version 2 as the channel encryption protocol.
@@ -417,9 +559,22 @@ Constants
.. data:: PROTOCOL_TLSv1
- Selects TLS version 1 as the channel encryption protocol. This is the most
+ Selects TLS version 1.0 as the channel encryption protocol.
+
+.. data:: PROTOCOL_TLSv1_1
+
+ Selects TLS version 1.1 as the channel encryption protocol.
+ Available only with openssl version 1.0.1+.
+
+ .. versionadded:: 3.4
+
+.. data:: PROTOCOL_TLSv1_2
+
+ Selects TLS version 1.2 as the channel encryption protocol. This is the most
modern version, and probably the best choice for maximum protection, if both
- sides can speak it.
+ sides can speak it. Available only with openssl version 1.0.1+.
+
+ .. versionadded:: 3.4
.. data:: OP_ALL
@@ -453,6 +608,22 @@ Constants
.. versionadded:: 3.2
+.. data:: OP_NO_TLSv1_1
+
+ Prevents a TLSv1.1 connection. This option is only applicable in conjunction
+ with :const:`PROTOCOL_SSLv23`. It prevents the peers from choosing TLSv1.1 as
+ the protocol version. Available only with openssl version 1.0.1+.
+
+ .. versionadded:: 3.4
+
+.. data:: OP_NO_TLSv1_2
+
+ Prevents a TLSv1.2 connection. This option is only applicable in conjunction
+ with :const:`PROTOCOL_SSLv23`. It prevents the peers from choosing TLSv1.2 as
+ the protocol version. Available only with openssl version 1.0.1+.
+
+ .. versionadded:: 3.4
+
.. data:: OP_CIPHER_SERVER_PREFERENCE
Use the server's cipher ordering preference, rather than the client's.
@@ -549,6 +720,37 @@ Constants
.. versionadded:: 3.2
+.. data:: ALERT_DESCRIPTION_HANDSHAKE_FAILURE
+ ALERT_DESCRIPTION_INTERNAL_ERROR
+ ALERT_DESCRIPTION_*
+
+ Alert Descriptions from :rfc:`5246` and others. The `IANA TLS Alert Registry
+ <http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-6>`_
+ contains this list and references to the RFCs where their meaning is defined.
+
+ Used as the return value of the callback function in
+ :meth:`SSLContext.set_servername_callback`.
+
+ .. versionadded:: 3.4
+
+.. data:: Purpose.SERVER_AUTH
+
+ Option for :func:`create_default_context` and
+ :meth:`SSLContext.load_default_certs`. This value indicates that the
+ context may be used to authenticate Web servers (therefore, it will
+ be used to create client-side sockets).
+
+ .. versionadded:: 3.4
+
+.. data:: Purpose.CLIENT_AUTH
+
+ Option for :func:`create_default_context` and
+ :meth:`SSLContext.load_default_certs`. This value indicates that the
+ context may be used to authenticate Web clients (therefore, it will
+ be used to create server-side sockets).
+
+ .. versionadded:: 3.4
+
SSL Sockets
-----------
@@ -584,10 +786,16 @@ SSL sockets also have the following additional methods and attributes:
Perform the SSL setup handshake.
+ .. versionchanged:: 3.4
+ The handshake method also performce :func:`match_hostname` when the
+ :attr:`~SSLContext.check_hostname` attribute of the socket's
+ :attr:`~SSLSocket.context` is true.
+
.. method:: SSLSocket.getpeercert(binary_form=False)
If there is no certificate for the peer on the other end of the connection,
- returns ``None``.
+ return ``None``. If the SSL handshake hasn't been done yet, raise
+ :exc:`ValueError`.
If the ``binary_form`` parameter is :const:`False`, and a certificate was
received from the peer, this method returns a :class:`dict` instance. If the
@@ -645,6 +853,11 @@ SSL sockets also have the following additional methods and attributes:
The returned dictionary includes additional items such as ``issuer``
and ``notBefore``.
+ .. versionchanged:: 3.4
+ :exc:`ValueError` is raised when the handshake isn't done.
+ The returned dictionary includes additional X509v3 extension items
+ such as ``crlDistributionPoints``, ``caIssuers`` and ``OCSP`` URIs.
+
.. method:: SSLSocket.cipher()
Returns a three-value tuple containing the name of the cipher being used, the
@@ -715,11 +928,30 @@ to speed up repeated connections from the same clients.
Create a new SSL context. You must pass *protocol* which must be one
of the ``PROTOCOL_*`` constants defined in this module.
- :data:`PROTOCOL_SSLv23` is recommended for maximum interoperability.
+ :data:`PROTOCOL_SSLv23` is currently recommended for maximum
+ interoperability.
+
+ .. seealso::
+ :func:`create_default_context` lets the :mod:`ssl` module choose
+ security settings for a given purpose.
:class:`SSLContext` objects have the following methods and attributes:
+.. method:: SSLContext.cert_store_stats()
+
+ Get statistics about quantities of loaded X.509 certificates, count of
+ X.509 certificates flagged as CA certificates and certificate revocation
+ lists as dictionary.
+
+ Example for a context with one CA cert and one other cert::
+
+ >>> context.cert_store_stats()
+ {'crl': 0, 'x509_ca': 1, 'x509': 2}
+
+ .. versionadded:: 3.4
+
+
.. method:: SSLContext.load_cert_chain(certfile, keyfile=None, password=None)
Load a private key and the corresponding certificate. The *certfile*
@@ -750,12 +982,32 @@ to speed up repeated connections from the same clients.
.. versionchanged:: 3.3
New optional argument *password*.
-.. method:: SSLContext.load_verify_locations(cafile=None, capath=None)
+.. method:: SSLContext.load_default_certs(purpose=Purpose.SERVER_AUTH)
+
+ Load a set of default "certification authority" (CA) certificates from
+ default locations. On Windows it loads CA certs from the ``CA`` and
+ ``ROOT`` system stores. On other systems it calls
+ :meth:`SSLContext.set_default_verify_paths`. In the future the method may
+ load CA certificates from other locations, too.
+
+ The *purpose* flag specifies what kind of CA certificates are loaded. The
+ default settings :data:`Purpose.SERVER_AUTH` loads certificates, that are
+ flagged and trusted for TLS web server authentication (client side
+ sockets). :data:`Purpose.CLIENT_AUTH` loads CA certificates for client
+ certificate verification on the server side.
+
+ .. versionadded:: 3.4
+
+.. method:: SSLContext.load_verify_locations(cafile=None, capath=None, cadata=None)
Load a set of "certification authority" (CA) certificates used to validate
other peers' certificates when :data:`verify_mode` is other than
:data:`CERT_NONE`. At least one of *cafile* or *capath* must be specified.
+ This method can also load certification revocation lists (CRLs) in PEM or
+ or DER format. In order to make use of CRLs, :attr:`SSLContext.verify_flags`
+ must be configured properly.
+
The *cafile* string, if present, is the path to a file of concatenated
CA certificates in PEM format. See the discussion of
:ref:`ssl-certificates` for more information about how to arrange the
@@ -766,6 +1018,25 @@ to speed up repeated connections from the same clients.
following an `OpenSSL specific layout
<http://www.openssl.org/docs/ssl/SSL_CTX_load_verify_locations.html>`_.
+ The *cadata* object, if present, is either an ASCII string of one or more
+ PEM-encoded certificates or a bytes-like object of DER-encoded
+ certificates. Like with *capath* extra lines around PEM-encoded
+ certificates are ignored but at least one certificate must be present.
+
+ .. versionchanged:: 3.4
+ New optional argument *cadata*
+
+.. method:: SSLContext.get_ca_certs(binary_form=False)
+
+ Get a list of loaded "certification authority" (CA) certificates. If the
+ ``binary_form`` parameter is :const:`False` each list
+ entry is a dict like the output of :meth:`SSLSocket.getpeercert`. Otherwise
+ the method returns a list of DER-encoded certificates. The returned list
+ does not contain certificates from *capath* unless a certificate was
+ requested and loaded by a SSL connection.
+
+ .. versionadded:: 3.4
+
.. method:: SSLContext.set_default_verify_paths()
Load a set of default "certification authority" (CA) certificates from
@@ -803,6 +1074,56 @@ to speed up repeated connections from the same clients.
.. versionadded:: 3.3
+.. method:: SSLContext.set_servername_callback(server_name_callback)
+
+ Register a callback function that will be called after the TLS Client Hello
+ handshake message has been received by the SSL/TLS server when the TLS client
+ specifies a server name indication. The server name indication mechanism
+ is specified in :rfc:`6066` section 3 - Server Name Indication.
+
+ Only one callback can be set per ``SSLContext``. If *server_name_callback*
+ is ``None`` then the callback is disabled. Calling this function a
+ subsequent time will disable the previously registered callback.
+
+ The callback function, *server_name_callback*, will be called with three
+ arguments; the first being the :class:`ssl.SSLSocket`, the second is a string
+ that represents the server name that the client is intending to communicate
+ (or :const:`None` if the TLS Client Hello does not contain a server name)
+ and the third argument is the original :class:`SSLContext`. The server name
+ argument is the IDNA decoded server name.
+
+ A typical use of this callback is to change the :class:`ssl.SSLSocket`'s
+ :attr:`SSLSocket.context` attribute to a new object of type
+ :class:`SSLContext` representing a certificate chain that matches the server
+ name.
+
+ Due to the early negotiation phase of the TLS connection, only limited
+ methods and attributes are usable like
+ :meth:`SSLSocket.selected_npn_protocol` and :attr:`SSLSocket.context`.
+ :meth:`SSLSocket.getpeercert`, :meth:`SSLSocket.getpeercert`,
+ :meth:`SSLSocket.cipher` and :meth:`SSLSocket.compress` methods require that
+ the TLS connection has progressed beyond the TLS Client Hello and therefore
+ will not contain return meaningful values nor can they be called safely.
+
+ The *server_name_callback* function must return ``None`` to allow the
+ TLS negotiation to continue. If a TLS failure is required, a constant
+ :const:`ALERT_DESCRIPTION_* <ALERT_DESCRIPTION_INTERNAL_ERROR>` can be
+ returned. Other return values will result in a TLS fatal error with
+ :const:`ALERT_DESCRIPTION_INTERNAL_ERROR`.
+
+ If there is a IDNA decoding error on the server name, the TLS connection
+ will terminate with an :const:`ALERT_DESCRIPTION_INTERNAL_ERROR` fatal TLS
+ alert message to the client.
+
+ If an exception is raised from the *server_name_callback* function the TLS
+ connection will terminate with a fatal TLS alert message
+ :const:`ALERT_DESCRIPTION_HANDSHAKE_FAILURE`.
+
+ This method will raise :exc:`NotImplementedError` if the OpenSSL library
+ had OPENSSL_NO_TLSEXT defined when it was built.
+
+ .. versionadded:: 3.4
+
.. method:: SSLContext.load_dh_params(dhfile)
Load the key generation parameters for Diffie-Helman (DH) key exchange.
@@ -869,6 +1190,45 @@ to speed up repeated connections from the same clients.
>>> stats['hits'], stats['misses']
(0, 0)
+.. method:: SSLContext.get_ca_certs(binary_form=False)
+
+ Returns a list of dicts with information of loaded CA certs. If the
+ optional argument is true, returns a DER-encoded copy of the CA
+ certificate.
+
+ .. note::
+ Certificates in a capath directory aren't loaded unless they have
+ been used at least once.
+
+ .. versionadded:: 3.4
+
+.. attribute:: SSLContext.check_hostname
+
+ Wether to match the peer cert's hostname with :func:`match_hostname` in
+ :meth:`SSLSocket.do_handshake`. The context's
+ :attr:`~SSLContext.verify_mode` must be set to :data:`CERT_OPTIONAL` or
+ :data:`CERT_REQUIRED`, and you must pass *server_hostname* to
+ :meth:`~SSLContext.wrap_socket` in order to match the hostname.
+
+ Example::
+
+ import socket, ssl
+
+ context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
+ context.verify_mode = ssl.CERT_REQUIRED
+ context.check_hostname = True
+ context.load_default_certs()
+
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ ssl_sock = context.wrap_socket(s, server_hostname='www.verisign.com'):
+ ssl_sock.connect(('www.verisign.com', 443))
+
+ .. versionadded:: 3.4
+
+ .. note::
+
+ This features requires OpenSSL 0.9.8f or newer.
+
.. attribute:: SSLContext.options
An integer representing the set of SSL options enabled on this context.
@@ -885,6 +1245,15 @@ to speed up repeated connections from the same clients.
The protocol version chosen when constructing the context. This attribute
is read-only.
+.. attribute:: SSLContext.verify_flags
+
+ The flags for certificate verification operations. You can set flags like
+ :data:`VERIFY_CRL_CHECK_LEAF` by ORing them together. By default OpenSSL
+ does neither require nor verify certificate revocation lists (CRLs).
+ Available only with openssl version 0.9.8+.
+
+ .. versionadded:: 3.4
+
.. attribute:: SSLContext.verify_mode
Whether to try to verify other peers' certificates and how to behave
@@ -970,20 +1339,9 @@ If you are going to require validation of the other side of the connection's
certificate, you need to provide a "CA certs" file, filled with the certificate
chains for each issuer you are willing to trust. Again, this file just contains
these chains concatenated together. For validation, Python will use the first
-chain it finds in the file which matches. Some "standard" root certificates are
-available from various certification authorities: `CACert.org
-<http://www.cacert.org/index.php?id=3>`_, `Thawte
-<http://www.thawte.com/roots/>`_, `Verisign
-<http://www.verisign.com/support/roots.html>`_, `Positive SSL
-<http://www.PositiveSSL.com/ssl-certificate-support/cert_installation/UTN-USERFirst-Hardware.crt>`_
-(used by python.org), `Equifax and GeoTrust
-<http://www.geotrust.com/resources/root_certificates/index.asp>`_.
-
-In general, if you are using SSL3 or TLS1, you don't need to put the full chain
-in your "CA certs" file; you only need the root certificates, and the remote
-peer is supposed to furnish the other certificates necessary to chain from its
-certificate to a root certificate. See :rfc:`4158` for more discussion of the
-way in which certification chains can be built.
+chain it finds in the file which matches. The platform's certificates file can
+be used by calling :meth:`SSLContext.load_default_certs`, this is done
+automatically with :func:`.create_default_context`.
Combined key and certificate
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -1186,7 +1544,7 @@ waiting for clients to connect::
import socket, ssl
- context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
+ context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
context.load_cert_chain(certfile="mycertfile", keyfile="mykeyfile")
bindsocket = socket.socket()
@@ -1263,9 +1621,40 @@ to be aware of:
Security considerations
-----------------------
+Best defaults
+^^^^^^^^^^^^^
+
+For **client use**, if you don't have any special requirements for your
+security policy, it is highly recommended that you use the
+:func:`create_default_context` function to create your SSL context.
+It will load the system's trusted CA certificates, enable certificate
+validation and hostname checking, and try to choose reasonably secure
+protocol and cipher settings.
+
+For example, here is how you would use the :class:`smtplib.SMTP` class to
+create a trusted, secure connection to a SMTP server::
+
+ >>> import ssl, smtplib
+ >>> smtp = smtplib.SMTP("mail.python.org", port=587)
+ >>> context = ssl.create_default_context()
+ >>> smtp.starttls(context=context)
+ (220, b'2.0.0 Ready to start TLS')
+
+If a client certificate is needed for the connection, it can be added with
+:meth:`SSLContext.load_cert_chain`.
+
+By contrast, if you create the SSL context by calling the :class:`SSLContext`
+constructor yourself, it will not have certificate validation nor hostname
+checking enabled by default. If you do so, please read the paragraphs below
+to achieve a good security level.
+
+Manual settings
+^^^^^^^^^^^^^^^
+
Verifying certificates
-^^^^^^^^^^^^^^^^^^^^^^
+''''''''''''''''''''''
+When calling the the :class:`SSLContext` constructor directly,
:const:`CERT_NONE` is the default. Since it does not authenticate the other
peer, it can be insecure, especially in client mode where most of time you
would like to ensure the authenticity of the server you're talking to.
@@ -1274,7 +1663,9 @@ Therefore, when in client mode, it is highly recommended to use
have to check that the server certificate, which can be obtained by calling
:meth:`SSLSocket.getpeercert`, matches the desired service. For many
protocols and applications, the service can be identified by the hostname;
-in this case, the :func:`match_hostname` function can be used.
+in this case, the :func:`match_hostname` function can be used. This common
+check is automatically performed when :attr:`SSLContext.check_hostname` is
+enabled.
In server mode, if you want to authenticate your clients using the SSL layer
(rather than using a higher-level authentication mechanism), you'll also have
@@ -1287,7 +1678,7 @@ to specify :const:`CERT_REQUIRED` and similarly check the client certificate.
by default).
Protocol versions
-^^^^^^^^^^^^^^^^^
+'''''''''''''''''
SSL version 2 is considered insecure and is therefore dangerous to use. If
you want maximum compatibility between clients and servers, it is recommended
@@ -1297,27 +1688,20 @@ SSLv2 explicitly using the :data:`SSLContext.options` attribute::
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
context.options |= ssl.OP_NO_SSLv2
-The SSL context created above will allow SSLv3 and TLSv1 connections, but
-not SSLv2.
+The SSL context created above will allow SSLv3 and TLSv1 (and later, if
+supported by your system) connections, but not SSLv2.
Cipher selection
-^^^^^^^^^^^^^^^^
+''''''''''''''''
If you have advanced security requirements, fine-tuning of the ciphers
enabled when negotiating a SSL session is possible through the
:meth:`SSLContext.set_ciphers` method. Starting from Python 3.2.3, the
ssl module disables certain weak ciphers by default, but you may want
-to further restrict the cipher choice. For example::
-
- context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
- context.set_ciphers('HIGH:!aNULL:!eNULL')
-
-The ``!aNULL:!eNULL`` part of the cipher spec is necessary to disable ciphers
-which don't provide both encryption and authentication. Be sure to read
-OpenSSL's documentation about the `cipher list
-format <http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT>`_.
-If you want to check which ciphers are enabled by a given cipher list,
-use the ``openssl ciphers`` command on your system.
+to further restrict the cipher choice. Be sure to read OpenSSL's documentation
+about the `cipher list format <http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT>`_.
+If you want to check which ciphers are enabled by a given cipher list, use the
+``openssl ciphers`` command on your system.
Multi-processing
^^^^^^^^^^^^^^^^
@@ -1350,3 +1734,12 @@ successful call of :func:`~ssl.RAND_add`, :func:`~ssl.RAND_bytes` or
`RFC 4366: Transport Layer Security (TLS) Extensions <http://www.ietf.org/rfc/rfc4366>`_
Blake-Wilson et. al.
+
+ `RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2 <http://www.ietf.org/rfc/rfc5246>`_
+ T. Dierks et. al.
+
+ `RFC 6066: Transport Layer Security (TLS) Extensions <http://www.ietf.org/rfc/rfc6066>`_
+ D. Eastlake
+
+ `IANA TLS: Transport Layer Security (TLS) Parameters <http://www.iana.org/assignments/tls-parameters/tls-parameters.xml>`_
+ IANA
diff --git a/Doc/library/stat.rst b/Doc/library/stat.rst
index 6c20aa2..24769f6 100644
--- a/Doc/library/stat.rst
+++ b/Doc/library/stat.rst
@@ -6,7 +6,8 @@
os.lstat() and os.fstat().
.. sectionauthor:: Skip Montanaro <skip@automatrix.com>
-**Source code:** :source:`Lib/stat.py`
+**Source code:** :source:`Modules/_stat.c`
+ :source:`Lib/stat.py`
--------------
@@ -15,6 +16,9 @@ results of :func:`os.stat`, :func:`os.fstat` and :func:`os.lstat` (if they
exist). For complete details about the :c:func:`stat`, :c:func:`fstat` and
:c:func:`lstat` calls, consult the documentation for your system.
+.. versionchanged:: 3.4
+ The stat module is backed by a C implementation.
+
The :mod:`stat` module defines the following functions to test for specific file
types:
@@ -53,6 +57,24 @@ types:
Return non-zero if the mode is from a socket.
+.. function:: S_ISDOOR(mode)
+
+ Return non-zero if the mode is from a door.
+
+ .. versionadded:: 3.4
+
+.. function:: S_ISPORT(mode)
+
+ Return non-zero if the mode is from an event port.
+
+ .. versionadded:: 3.4
+
+.. function:: S_ISWHT(mode)
+
+ Return non-zero if the mode is from a whiteout.
+
+ .. versionadded:: 3.4
+
Two additional functions are defined for more general manipulation of the file's
mode:
@@ -113,6 +135,10 @@ readable string:
.. versionadded:: 3.3
+ .. versionchanged:: 3.4
+ The function supports :data:`S_IFDOOR`, :data:`S_IFPORT` and
+ :data:`S_IFWHT`.
+
All the variables below are simply symbolic indexes into the 10-tuple returned
by :func:`os.stat`, :func:`os.fstat` or :func:`os.lstat`.
@@ -210,6 +236,29 @@ Use of the functions above is more portable than use of the first set of flags:
FIFO.
+.. data:: S_IFDOOR
+
+ Door.
+
+ .. versionadded:: 3.4
+
+.. data:: S_IFPORT
+
+ Event port.
+
+ .. versionadded:: 3.4
+
+.. data:: S_IFWHT
+
+ Whiteout.
+
+ .. versionadded:: 3.4
+
+.. note::
+
+ :data:`S_IFDOOR`, :data:`S_IFPORT` or :data:`S_IFWHT` are defined as
+ 0 when the platform does not have support for the file types.
+
The following flags can also be used in the *mode* argument of :func:`os.chmod`:
.. data:: S_ISUID
diff --git a/Doc/library/statistics.rst b/Doc/library/statistics.rst
new file mode 100644
index 0000000..4e77838
--- /dev/null
+++ b/Doc/library/statistics.rst
@@ -0,0 +1,418 @@
+:mod:`statistics` --- Mathematical statistics functions
+=======================================================
+
+.. module:: statistics
+ :synopsis: mathematical statistics functions
+.. moduleauthor:: Steven D'Aprano <steve+python@pearwood.info>
+.. sectionauthor:: Steven D'Aprano <steve+python@pearwood.info>
+
+.. versionadded:: 3.4
+
+.. testsetup:: *
+
+ from statistics import *
+ __name__ = '<doctest>'
+
+**Source code:** :source:`Lib/statistics.py`
+
+--------------
+
+This module provides functions for calculating mathematical statistics of
+numeric (:class:`Real`-valued) data.
+
+.. note::
+
+ Unless explicitly noted otherwise, these functions support :class:`int`,
+ :class:`float`, :class:`decimal.Decimal` and :class:`fractions.Fraction`.
+ Behaviour with other types (whether in the numeric tower or not) is
+ currently unsupported. Mixed types are also undefined and
+ implementation-dependent. If your input data consists of mixed types,
+ you may be able to use :func:`map` to ensure a consistent result, e.g.
+ ``map(float, input_data)``.
+
+Averages and measures of central location
+-----------------------------------------
+
+These functions calculate an average or typical value from a population
+or sample.
+
+======================= =============================================
+:func:`mean` Arithmetic mean ("average") of data.
+:func:`median` Median (middle value) of data.
+:func:`median_low` Low median of data.
+:func:`median_high` High median of data.
+:func:`median_grouped` Median, or 50th percentile, of grouped data.
+:func:`mode` Mode (most common value) of discrete data.
+======================= =============================================
+
+Measures of spread
+------------------
+
+These functions calculate a measure of how much the population or sample
+tends to deviate from the typical or average values.
+
+======================= =============================================
+:func:`pstdev` Population standard deviation of data.
+:func:`pvariance` Population variance of data.
+:func:`stdev` Sample standard deviation of data.
+:func:`variance` Sample variance of data.
+======================= =============================================
+
+
+Function details
+----------------
+
+Note: The functions do not require the data given to them to be sorted.
+However, for reading convenience, most of the examples show sorted sequences.
+
+.. function:: mean(data)
+
+ Return the sample arithmetic mean of *data*, a sequence or iterator of
+ real-valued numbers.
+
+ The arithmetic mean is the sum of the data divided by the number of data
+ points. It is commonly called "the average", although it is only one of many
+ different mathematical averages. It is a measure of the central location of
+ the data.
+
+ If *data* is empty, :exc:`StatisticsError` will be raised.
+
+ Some examples of use:
+
+ .. doctest::
+
+ >>> mean([1, 2, 3, 4, 4])
+ 2.8
+ >>> mean([-1.0, 2.5, 3.25, 5.75])
+ 2.625
+
+ >>> from fractions import Fraction as F
+ >>> mean([F(3, 7), F(1, 21), F(5, 3), F(1, 3)])
+ Fraction(13, 21)
+
+ >>> from decimal import Decimal as D
+ >>> mean([D("0.5"), D("0.75"), D("0.625"), D("0.375")])
+ Decimal('0.5625')
+
+ .. note::
+
+ The mean is strongly affected by outliers and is not a robust estimator
+ for central location: the mean is not necessarily a typical example of the
+ data points. For more robust, although less efficient, measures of
+ central location, see :func:`median` and :func:`mode`. (In this case,
+ "efficient" refers to statistical efficiency rather than computational
+ efficiency.)
+
+ The sample mean gives an unbiased estimate of the true population mean,
+ which means that, taken on average over all the possible samples,
+ ``mean(sample)`` converges on the true mean of the entire population. If
+ *data* represents the entire population rather than a sample, then
+ ``mean(data)`` is equivalent to calculating the true population mean μ.
+
+
+.. function:: median(data)
+
+ Return the median (middle value) of numeric data, using the common "mean of
+ middle two" method. If *data* is empty, :exc:`StatisticsError` is raised.
+
+ The median is a robust measure of central location, and is less affected by
+ the presence of outliers in your data. When the number of data points is
+ odd, the middle data point is returned:
+
+ .. doctest::
+
+ >>> median([1, 3, 5])
+ 3
+
+ When the number of data points is even, the median is interpolated by taking
+ the average of the two middle values:
+
+ .. doctest::
+
+ >>> median([1, 3, 5, 7])
+ 4.0
+
+ This is suited for when your data is discrete, and you don't mind that the
+ median may not be an actual data point.
+
+ .. seealso:: :func:`median_low`, :func:`median_high`, :func:`median_grouped`
+
+
+.. function:: median_low(data)
+
+ Return the low median of numeric data. If *data* is empty,
+ :exc:`StatisticsError` is raised.
+
+ The low median is always a member of the data set. When the number of data
+ points is odd, the middle value is returned. When it is even, the smaller of
+ the two middle values is returned.
+
+ .. doctest::
+
+ >>> median_low([1, 3, 5])
+ 3
+ >>> median_low([1, 3, 5, 7])
+ 3
+
+ Use the low median when your data are discrete and you prefer the median to
+ be an actual data point rather than interpolated.
+
+
+.. function:: median_high(data)
+
+ Return the high median of data. If *data* is empty, :exc:`StatisticsError`
+ is raised.
+
+ The high median is always a member of the data set. When the number of data
+ points is odd, the middle value is returned. When it is even, the larger of
+ the two middle values is returned.
+
+ .. doctest::
+
+ >>> median_high([1, 3, 5])
+ 3
+ >>> median_high([1, 3, 5, 7])
+ 5
+
+ Use the high median when your data are discrete and you prefer the median to
+ be an actual data point rather than interpolated.
+
+
+.. function:: median_grouped(data, interval=1)
+
+ Return the median of grouped continuous data, calculated as the 50th
+ percentile, using interpolation. If *data* is empty, :exc:`StatisticsError`
+ is raised.
+
+ .. doctest::
+
+ >>> median_grouped([52, 52, 53, 54])
+ 52.5
+
+ In the following example, the data are rounded, so that each value represents
+ the midpoint of data classes, e.g. 1 is the midpoint of the class 0.5-1.5, 2
+ is the midpoint of 1.5-2.5, 3 is the midpoint of 2.5-3.5, etc. With the data
+ given, the middle value falls somewhere in the class 3.5-4.5, and
+ interpolation is used to estimate it:
+
+ .. doctest::
+
+ >>> median_grouped([1, 2, 2, 3, 4, 4, 4, 4, 4, 5])
+ 3.7
+
+ Optional argument *interval* represents the class interval, and defaults
+ to 1. Changing the class interval naturally will change the interpolation:
+
+ .. doctest::
+
+ >>> median_grouped([1, 3, 3, 5, 7], interval=1)
+ 3.25
+ >>> median_grouped([1, 3, 3, 5, 7], interval=2)
+ 3.5
+
+ This function does not check whether the data points are at least
+ *interval* apart.
+
+ .. impl-detail::
+
+ Under some circumstances, :func:`median_grouped` may coerce data points to
+ floats. This behaviour is likely to change in the future.
+
+ .. seealso::
+
+ * "Statistics for the Behavioral Sciences", Frederick J Gravetter and
+ Larry B Wallnau (8th Edition).
+
+ * Calculating the `median <http://www.ualberta.ca/~opscan/median.html>`_.
+
+ * The `SSMEDIAN
+ <https://projects.gnome.org/gnumeric/doc/gnumeric-function-SSMEDIAN.shtml>`_
+ function in the Gnome Gnumeric spreadsheet, including `this discussion
+ <https://mail.gnome.org/archives/gnumeric-list/2011-April/msg00018.html>`_.
+
+
+.. function:: mode(data)
+
+ Return the most common data point from discrete or nominal *data*. The mode
+ (when it exists) is the most typical value, and is a robust measure of
+ central location.
+
+ If *data* is empty, or if there is not exactly one most common value,
+ :exc:`StatisticsError` is raised.
+
+ ``mode`` assumes discrete data, and returns a single value. This is the
+ standard treatment of the mode as commonly taught in schools:
+
+ .. doctest::
+
+ >>> mode([1, 1, 2, 3, 3, 3, 3, 4])
+ 3
+
+ The mode is unique in that it is the only statistic which also applies
+ to nominal (non-numeric) data:
+
+ .. doctest::
+
+ >>> mode(["red", "blue", "blue", "red", "green", "red", "red"])
+ 'red'
+
+
+.. function:: pstdev(data, mu=None)
+
+ Return the population standard deviation (the square root of the population
+ variance). See :func:`pvariance` for arguments and other details.
+
+ .. doctest::
+
+ >>> pstdev([1.5, 2.5, 2.5, 2.75, 3.25, 4.75])
+ 0.986893273527251
+
+
+.. function:: pvariance(data, mu=None)
+
+ Return the population variance of *data*, a non-empty iterable of real-valued
+ numbers. Variance, or second moment about the mean, is a measure of the
+ variability (spread or dispersion) of data. A large variance indicates that
+ the data is spread out; a small variance indicates it is clustered closely
+ around the mean.
+
+ If the optional second argument *mu* is given, it should be the mean of
+ *data*. If it is missing or ``None`` (the default), the mean is
+ automatically calculated.
+
+ Use this function to calculate the variance from the entire population. To
+ estimate the variance from a sample, the :func:`variance` function is usually
+ a better choice.
+
+ Raises :exc:`StatisticsError` if *data* is empty.
+
+ Examples:
+
+ .. doctest::
+
+ >>> data = [0.0, 0.25, 0.25, 1.25, 1.5, 1.75, 2.75, 3.25]
+ >>> pvariance(data)
+ 1.25
+
+ If you have already calculated the mean of your data, you can pass it as the
+ optional second argument *mu* to avoid recalculation:
+
+ .. doctest::
+
+ >>> mu = mean(data)
+ >>> pvariance(data, mu)
+ 1.25
+
+ This function does not attempt to verify that you have passed the actual mean
+ as *mu*. Using arbitrary values for *mu* may lead to invalid or impossible
+ results.
+
+ Decimals and Fractions are supported:
+
+ .. doctest::
+
+ >>> from decimal import Decimal as D
+ >>> pvariance([D("27.5"), D("30.25"), D("30.25"), D("34.5"), D("41.75")])
+ Decimal('24.815')
+
+ >>> from fractions import Fraction as F
+ >>> pvariance([F(1, 4), F(5, 4), F(1, 2)])
+ Fraction(13, 72)
+
+ .. note::
+
+ When called with the entire population, this gives the population variance
+ σ². When called on a sample instead, this is the biased sample variance
+ s², also known as variance with N degrees of freedom.
+
+ If you somehow know the true population mean μ, you may use this function
+ to calculate the variance of a sample, giving the known population mean as
+ the second argument. Provided the data points are representative
+ (e.g. independent and identically distributed), the result will be an
+ unbiased estimate of the population variance.
+
+
+.. function:: stdev(data, xbar=None)
+
+ Return the sample standard deviation (the square root of the sample
+ variance). See :func:`variance` for arguments and other details.
+
+ .. doctest::
+
+ >>> stdev([1.5, 2.5, 2.5, 2.75, 3.25, 4.75])
+ 1.0810874155219827
+
+
+.. function:: variance(data, xbar=None)
+
+ Return the sample variance of *data*, an iterable of at least two real-valued
+ numbers. Variance, or second moment about the mean, is a measure of the
+ variability (spread or dispersion) of data. A large variance indicates that
+ the data is spread out; a small variance indicates it is clustered closely
+ around the mean.
+
+ If the optional second argument *xbar* is given, it should be the mean of
+ *data*. If it is missing or ``None`` (the default), the mean is
+ automatically calculated.
+
+ Use this function when your data is a sample from a population. To calculate
+ the variance from the entire population, see :func:`pvariance`.
+
+ Raises :exc:`StatisticsError` if *data* has fewer than two values.
+
+ Examples:
+
+ .. doctest::
+
+ >>> data = [2.75, 1.75, 1.25, 0.25, 0.5, 1.25, 3.5]
+ >>> variance(data)
+ 1.3720238095238095
+
+ If you have already calculated the mean of your data, you can pass it as the
+ optional second argument *xbar* to avoid recalculation:
+
+ .. doctest::
+
+ >>> m = mean(data)
+ >>> variance(data, m)
+ 1.3720238095238095
+
+ This function does not attempt to verify that you have passed the actual mean
+ as *xbar*. Using arbitrary values for *xbar* can lead to invalid or
+ impossible results.
+
+ Decimal and Fraction values are supported:
+
+ .. doctest::
+
+ >>> from decimal import Decimal as D
+ >>> variance([D("27.5"), D("30.25"), D("30.25"), D("34.5"), D("41.75")])
+ Decimal('31.01875')
+
+ >>> from fractions import Fraction as F
+ >>> variance([F(1, 6), F(1, 2), F(5, 3)])
+ Fraction(67, 108)
+
+ .. note::
+
+ This is the sample variance s² with Bessel's correction, also known as
+ variance with N-1 degrees of freedom. Provided that the data points are
+ representative (e.g. independent and identically distributed), the result
+ should be an unbiased estimate of the true population variance.
+
+ If you somehow know the actual population mean μ you should pass it to the
+ :func:`pvariance` function as the *mu* parameter to get the variance of a
+ sample.
+
+Exceptions
+----------
+
+A single exception is defined:
+
+.. exception:: StatisticsError
+
+ Subclass of :exc:`ValueError` for statistics-related exceptions.
+
+..
+ # This modelines must appear within the last ten lines of the file.
+ kate: indent-width 3; remove-trailing-space on; replace-tabs on; encoding utf-8;
diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst
index ab78cdc..bb421af 100644
--- a/Doc/library/stdtypes.rst
+++ b/Doc/library/stdtypes.rst
@@ -1526,7 +1526,7 @@ expression support in the :mod:`re` module).
at that position.
-.. method:: str.expandtabs([tabsize])
+.. method:: str.expandtabs(tabsize=8)
Return a copy of the string where all tab characters are replaced by one or
more spaces, depending on the current column and the given tab size. Tab
@@ -2462,6 +2462,10 @@ copying.
.. versionchanged:: 3.3
One-dimensional memoryviews with formats 'B', 'b' or 'c' are now hashable.
+ .. versionchanged:: 3.4
+ memoryview is now registered automatically with
+ :class:`collections.abc.Sequence`
+
:class:`memoryview` has several methods:
.. method:: __eq__(exporter)
diff --git a/Doc/library/struct.rst b/Doc/library/struct.rst
index dde32b9..ec2e1be 100644
--- a/Doc/library/struct.rst
+++ b/Doc/library/struct.rst
@@ -66,6 +66,19 @@ The module defines the following exception and functions:
format (``len(buffer[offset:])`` must be at least ``calcsize(fmt)``).
+.. function:: iter_unpack(fmt, buffer)
+
+ Iteratively unpack from the buffer *buffer* according to the format
+ string *fmt*. This function returns an iterator which will read
+ equally-sized chunks from the buffer until all its contents have been
+ consumed. The buffer's size in bytes must be a multiple of the amount
+ of data required by the format, as reflected by :func:`calcsize`.
+
+ Each iteration yields a tuple as specified by the format string.
+
+ .. versionadded:: 3.4
+
+
.. function:: calcsize(fmt)
Return the size of the struct (and hence of the bytes object produced by
@@ -388,6 +401,13 @@ The :mod:`struct` module also defines the following type:
(``len(buffer[offset:])`` must be at least :attr:`self.size`).
+ .. method:: iter_unpack(buffer)
+
+ Identical to the :func:`iter_unpack` function, using the compiled format.
+ (``len(buffer)`` must be a multiple of :attr:`self.size`).
+
+ .. versionadded:: 3.4
+
.. attribute:: format
The format string used to construct this Struct object.
diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst
index a9ee970..4b18396 100644
--- a/Doc/library/subprocess.rst
+++ b/Doc/library/subprocess.rst
@@ -116,7 +116,7 @@ use cases, the underlying :class:`Popen` interface can be used directly.
*timeout* was added.
-.. function:: check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False, timeout=None)
+.. function:: check_output(args, *, input=None, stdin=None, stderr=None, shell=False, universal_newlines=False, timeout=None)
Run command with arguments and return its output.
@@ -129,15 +129,21 @@ use cases, the underlying :class:`Popen` interface can be used directly.
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.
+ supplied arguments other than *input* and *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.
+ The *input* argument is passed to :meth:`Popen.communicate` and thus to the
+ subprocess's stdin. If used it must be a byte sequence, or a string if
+ ``universal_newlines=True``. When used, the internal :class:`Popen` object
+ is automatically created with ``stdin=PIPE``, and the *stdin* argument may
+ not be used as well.
+
Examples::
>>> subprocess.check_output(["echo", "Hello World!"])
@@ -146,6 +152,10 @@ use cases, the underlying :class:`Popen` interface can be used directly.
>>> subprocess.check_output(["echo", "Hello World!"], universal_newlines=True)
'Hello World!\n'
+ >>> subprocess.check_output(["sed", "-e", "s/foo/bar/"],
+ ... input=b"when in the course of fooman events\n")
+ b'when in the course of barman events\n'
+
>>> subprocess.check_output("exit 1", shell=True)
Traceback (most recent call last):
...
@@ -167,10 +177,6 @@ use cases, the underlying :class:`Popen` interface can be used directly.
... shell=True)
'ls: non_existent_file: No such file or directory\n'
- .. versionadded:: 3.1
-
- ..
-
.. warning::
Invoking the system shell with ``shell=True`` can be a security hazard
@@ -183,9 +189,13 @@ 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.
+ .. versionadded:: 3.1
+
.. versionchanged:: 3.3
*timeout* was added.
+ .. versionchanged:: 3.4
+ *input* was added.
.. data:: DEVNULL
@@ -619,6 +629,12 @@ Instances of the :class:`Popen` class have the following methods:
:exc:`TimeoutExpired` exception. It is safe to catch this exception and
retry the wait.
+ .. note::
+
+ The function is implemented using a busy loop (non-blocking call and
+ short sleeps). Use the :mod:`asyncio` module for an asynchronous wait:
+ see :class:`asyncio.create_subprocess_exec`.
+
.. warning::
This will deadlock when using ``stdout=PIPE`` and/or
@@ -629,6 +645,11 @@ Instances of the :class:`Popen` class have the following methods:
.. versionchanged:: 3.3
*timeout* was added.
+ .. deprecated:: 3.4
+
+ Do not use the undocumented *endtime* parameter. It is was
+ unintentionally exposed in 3.3 but was intended to be private
+ for internal use. Use *timeout* instead.
.. method:: Popen.communicate(input=None, timeout=None)
@@ -847,8 +868,6 @@ The :mod:`subprocess` module exposes the following constants.
The new process has a new console, instead of inheriting its parent's
console (the default).
- This flag is always set when :class:`Popen` is created with ``shell=True``.
-
.. data:: CREATE_NEW_PROCESS_GROUP
A :class:`Popen` ``creationflags`` parameter to specify that a new process
@@ -1063,9 +1082,9 @@ handling consistency are valid for these functions.
Return ``(status, output)`` of executing *cmd* in a shell.
- Execute the string *cmd* in a shell with :class:`Popen` and return a 2-tuple
- ``(status, output)`` via :func:`Popen.communicate`. Universal newlines mode
- is used; see the notes on :ref:`frequently-used-arguments` for more details.
+ Execute the string *cmd* in a shell with :meth:`Popen.check_output` and
+ return a 2-tuple ``(status, output)``. Universal newlines mode is used;
+ see the notes on :ref:`frequently-used-arguments` for more details.
A trailing newline is stripped from the output.
The exit status for the command can be interpreted
diff --git a/Doc/library/sunau.rst b/Doc/library/sunau.rst
index 4bdb99b..a94ae08 100644
--- a/Doc/library/sunau.rst
+++ b/Doc/library/sunau.rst
@@ -150,8 +150,9 @@ AU_read objects, as returned by :func:`.open` above, have the following methods:
.. method:: AU_read.getparams()
- Returns a tuple ``(nchannels, sampwidth, framerate, nframes, comptype,
- compname)``, equivalent to output of the :meth:`get\*` methods.
+ Returns a :func:`~collections.namedtuple` ``(nchannels, sampwidth,
+ framerate, nframes, comptype, compname)``, equivalent to output of the
+ :meth:`get\*` methods.
.. method:: AU_read.readframes(n)
@@ -211,6 +212,9 @@ AU_write objects, as returned by :func:`.open` above, have the following methods
Set the sample width (in bytes.)
+ .. versionchanged:: 3.4
+ Added support for 24-bit samples.
+
.. method:: AU_write.setframerate(n)
@@ -246,11 +250,17 @@ AU_write objects, as returned by :func:`.open` above, have the following methods
Write audio frames, without correcting *nframes*.
+ .. versionchanged:: 3.4
+ Any :term:`bytes-like object` is now accepted.
+
.. method:: AU_write.writeframes(data)
Write audio frames and make sure *nframes* is correct.
+ .. versionchanged:: 3.4
+ Any :term:`bytes-like object` is now accepted.
+
.. method:: AU_write.close()
diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst
index 36c608c..2328a10 100644
--- a/Doc/library/sys.rst
+++ b/Doc/library/sys.rst
@@ -12,11 +12,12 @@ always available.
.. data:: abiflags
- On POSIX systems where Python is build with the standard ``configure``
+ On POSIX systems where Python was built with the standard ``configure``
script, this contains the ABI flags as specified by :pep:`3149`.
.. versionadded:: 3.2
+
.. data:: argv
The list of command line arguments passed to a Python script. ``argv[0]`` is the
@@ -382,6 +383,21 @@ always available.
.. versionadded:: 3.1
+.. function:: getallocatedblocks()
+
+ Return the number of memory blocks currently allocated by the interpreter,
+ regardless of their size. This function is mainly useful for tracking
+ and debugging memory leaks. Because of the interpreter's internal
+ caches, the result can vary from call to call; you may have to call
+ :func:`_clear_type_cache()` and :func:`gc.collect()` to get more
+ predictable results.
+
+ If a Python build or implementation cannot reasonably compute this
+ information, :func:`getallocatedblocks()` is allowed to return 0 instead.
+
+ .. versionadded:: 3.4
+
+
.. function:: getcheckinterval()
Return the interpreter's "check interval"; see :func:`setcheckinterval`.
@@ -398,9 +414,10 @@ always available.
.. function:: getdlopenflags()
- Return the current value of the flags that are used for :c:func:`dlopen` calls.
- The flag constants are defined in the :mod:`ctypes` and :mod:`DLFCN` modules.
- Availability: Unix.
+ Return the current value of the flags that are used for
+ :c:func:`dlopen` calls. Symbolic names for the flag values can be
+ found in the :mod:`os` module (``RTLD_xxx`` constants, e.g.
+ :data:`os.RTLD_LAZY`). Availability: Unix.
.. function:: getfilesystemencoding()
@@ -579,9 +596,20 @@ always available.
| :const:`imag` | multiplier used for the imaginary part of a |
| | complex number |
+---------------------+--------------------------------------------------+
+ | :const:`algorithm` | name of the algorithm for hashing of str, bytes, |
+ | | and memoryview |
+ +---------------------+--------------------------------------------------+
+ | :const:`hash_bits` | internal output size of the hash algorithm |
+ +---------------------+--------------------------------------------------+
+ | :const:`seed_bits` | size of the seed key of the hash algorithm |
+ +---------------------+--------------------------------------------------+
+
.. versionadded:: 3.2
+ .. versionchanged:: 3.4
+ Added *algorithm*, *hash_bits* and *seed_bits*
+
.. data:: hexversion
@@ -665,6 +693,17 @@ always available.
.. versionadded:: 3.1
+.. data:: __interactivehook__
+
+ When this attribute exists, its value is automatically called (with no
+ arguments) when the interpreter is launched in :ref:`interactive mode
+ <tut-interactive>`. This is done after the :envvar:`PYTHONSTARTUP` file is
+ read, so that you can set this hook there. The :mod:`site` module
+ :ref:`sets this <rlcompleter-config>`.
+
+ .. versionadded:: 3.4
+
+
.. function:: intern(string)
Enter *string* in the table of "interned" strings and return the interned string
@@ -812,8 +851,6 @@ always available.
Windows ``'win32'``
Windows/Cygwin ``'cygwin'``
Mac OS X ``'darwin'``
- OS/2 ``'os2'``
- OS/2 EMX ``'os2emx'``
================ ===========================
.. versionchanged:: 3.3
@@ -884,7 +921,7 @@ 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(os.RTLD_GLOBAL)``. Symbolic names for the flag modules
+ ``sys.setdlopenflags(os.RTLD_GLOBAL)``. Symbolic names for the flag values
can be found in the :mod:`os` module (``RTLD_xxx`` constants, e.g.
:data:`os.RTLD_LAZY`).
@@ -1094,7 +1131,6 @@ always available.
| :const:`name` | Name of the thread implementation: |
| | |
| | * ``'nt'``: Windows threads |
- | | * ``'os2'``: OS/2 threads |
| | * ``'pthread'``: POSIX threads |
| | * ``'solaris'``: Solaris threads |
+------------------+---------------------------------------------------------+
diff --git a/Doc/library/sysconfig.rst b/Doc/library/sysconfig.rst
index c47dcce..535ac54 100644
--- a/Doc/library/sysconfig.rst
+++ b/Doc/library/sysconfig.rst
@@ -83,8 +83,6 @@ Python currently supports seven schemes:
located under the user home directory.
- *nt*: scheme for NT platforms like Windows.
- *nt_user*: scheme for NT platforms, when the *user* option is used.
-- *os2*: scheme for OS/2 platforms.
-- *os2_home*: scheme for OS/2 patforms, when the *user* option is used.
Each scheme is itself composed of a series of paths and each path has a unique
identifier. Python currently uses eight paths:
diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst
index 1ead71c..59336c9 100644
--- a/Doc/library/tarfile.rst
+++ b/Doc/library/tarfile.rst
@@ -591,6 +591,67 @@ A :class:`TarInfo` object also provides some convenient query methods:
Return :const:`True` if it is one of character device, block device or FIFO.
+.. _tarfile-commandline:
+
+Command Line Interface
+----------------------
+
+.. versionadded:: 3.4
+
+The :mod:`tarfile` module provides a simple command line interface to interact
+with tar archives.
+
+If you want to create a new tar archive, specify its name after the :option:`-c`
+option and then list the filename(s) that should be included::
+
+ $ python -m tarfile -c monty.tar spam.txt eggs.txt
+
+Passing a directory is also acceptable::
+
+ $ python -m tarfile -c monty.tar life-of-brian_1979/
+
+If you want to extract a tar archive into the current directory, use
+the :option:`-e` option::
+
+ $ python -m tarfile -e monty.tar
+
+You can also extract a tar archive into a different directory by passing the
+directory's name::
+
+ $ python -m tarfile -e monty.tar other-dir/
+
+For a list of the files in a tar archive, use the :option:`-l` option::
+
+ $ python -m tarfile -l monty.tar
+
+
+Command line options
+~~~~~~~~~~~~~~~~~~~~
+
+.. cmdoption:: -l <tarfile>
+ --list <tarfile>
+
+ List files in a tarfile.
+
+.. cmdoption:: -c <tarfile> <source1> <sourceN>
+ --create <tarfile> <source1> <sourceN>
+
+ Create tarfile from source files.
+
+.. cmdoption:: -e <tarfile> [<output_dir>]
+ --extract <tarfile> [<output_dir>]
+
+ Extract tarfile into the current directory if *output_dir* is not specified.
+
+.. cmdoption:: -t <tarfile>
+ --test <tarfile>
+
+ Test whether the tarfile is valid or not.
+
+.. cmdoption:: -v, --verbose
+
+ Verbose output
+
.. _tar-examples:
Examples
diff --git a/Doc/library/test.rst b/Doc/library/test.rst
index c28536d..83026d8 100644
--- a/Doc/library/test.rst
+++ b/Doc/library/test.rst
@@ -443,13 +443,6 @@ The :mod:`test.support` module defines the following functions:
A decorator for running tests that require support for symbolic links.
-.. function:: suppress_crash_popup()
-
- A context manager that disables Windows Error Reporting dialogs using
- `SetErrorMode <http://msdn.microsoft.com/en-us/library/windows/desktop/ms680621%28v=vs.85%29.aspx>`_.
- On other platforms it's a no-op.
-
-
.. decorator:: anticipate_failure(condition)
A decorator to conditionally mark tests with
@@ -594,6 +587,21 @@ The :mod:`test.support` module defines the following classes:
Temporarily unset the environment variable ``envvar``.
+.. class:: SuppressCrashReport()
+
+ A context manager used to try to prevent crash dialog popups on tests that
+ are expected to crash a subprocess.
+
+ On Windows, it disables Windows Error Reporting dialogs using
+ `SetErrorMode <http://msdn.microsoft.com/en-us/library/windows/desktop/ms680621.aspx>`_.
+
+ On UNIX, :func:`resource.setrlimit` is used to set
+ :attr:`resource.RLIMIT_CORE`'s soft limit to 0 to prevent coredump file
+ creation.
+
+ On both platforms, the old value is restored by :meth:`__exit__`.
+
+
.. class:: WarningsRecorder()
Class used to record warnings for unit tests. See documentation of
diff --git a/Doc/library/textwrap.rst b/Doc/library/textwrap.rst
index c625254..edf1fd6 100644
--- a/Doc/library/textwrap.rst
+++ b/Doc/library/textwrap.rst
@@ -10,11 +10,11 @@
--------------
-The :mod:`textwrap` module provides two convenience functions, :func:`wrap` and
-:func:`fill`, as well as :class:`TextWrapper`, the class that does all the work,
-and two utility functions, :func:`dedent` and :func:`indent`. If you're just wrapping or filling one
-or two text strings, the convenience functions should be good enough;
-otherwise, you should use an instance of :class:`TextWrapper` for efficiency.
+The :mod:`textwrap` module provides some convenience functions,
+as well as :class:`TextWrapper`, the class that does all the work.
+If you're just wrapping or filling one or two text strings, the convenience
+functions should be good enough; otherwise, you should use an instance of
+:class:`TextWrapper` for efficiency.
.. function:: wrap(text, width=70, **kwargs)
@@ -39,19 +39,31 @@ otherwise, you should use an instance of :class:`TextWrapper` for efficiency.
In particular, :func:`fill` accepts exactly the same keyword arguments as
:func:`wrap`.
-Both :func:`wrap` and :func:`fill` work by creating a :class:`TextWrapper`
-instance and calling a single method on it. That instance is not reused, so for
-applications that wrap/fill many text strings, it will be more efficient for you
-to create your own :class:`TextWrapper` object.
-Text is preferably wrapped on whitespaces and right after the hyphens in
-hyphenated words; only then will long words be broken if necessary, unless
-:attr:`TextWrapper.break_long_words` is set to false.
+.. function:: shorten(text, width, **kwargs)
+
+ Collapse and truncate the given *text* to fit in the given *width*.
+
+ First the whitespace in *text* is collapsed (all whitespace is replaced by
+ single spaces). If the result fits in the *width*, it is returned.
+ Otherwise, enough words are dropped from the end so that the remaining words
+ plus the :attr:`placeholder` fit within :attr:`width`::
+
+ >>> textwrap.shorten("Hello world!", width=12)
+ 'Hello world!'
+ >>> textwrap.shorten("Hello world!", width=11)
+ 'Hello [...]'
+ >>> textwrap.shorten("Hello world", width=10, placeholder="...")
+ 'Hello...'
+
+ Optional keyword arguments correspond to the instance attributes of
+ :class:`TextWrapper`, documented below. Note that the whitespace is
+ collapsed before the text is passed to the :class:`TextWrapper` :meth:`fill`
+ function, so changing the value of :attr:`.tabsize`, :attr:`.expand_tabs`,
+ :attr:`.drop_whitespace`, and :attr:`.replace_whitespace` will have no effect.
+
+ .. versionadded:: 3.4
-Two additional utility function, :func:`dedent` and :func:`indent`, are
-provided to remove indentation from strings that have unwanted whitespace
-to the left of the text and to add an arbitrary prefix to selected lines
-in a block of text.
.. function:: dedent(text)
@@ -102,6 +114,16 @@ in a block of text.
+ world
+:func:`wrap`, :func:`fill` and :func:`shorten` work by creating a
+:class:`TextWrapper` instance and calling a single method on it. That
+instance is not reused, so for applications that process many text
+strings using :func:`wrap` and/or :func:`fill`, it may be more efficient to
+create your own :class:`TextWrapper` object.
+
+Text is preferably wrapped on whitespaces and right after the hyphens in
+hyphenated words; only then will long words be broken if necessary, unless
+:attr:`TextWrapper.break_long_words` is set to false.
+
.. class:: TextWrapper(**kwargs)
The :class:`TextWrapper` constructor accepts a number of optional keyword
@@ -235,7 +257,23 @@ in a block of text.
was to always allow breaking hyphenated words.
- :class:`TextWrapper` also provides two public methods, analogous to the
+ .. attribute:: max_lines
+
+ (default: ``None``) If not ``None``, then the output will contain at most
+ *max_lines* lines, with *placeholder* appearing at the end of the output.
+
+ .. versionadded:: 3.4
+
+
+ .. attribute:: placeholder
+
+ (default: ``' [...]'``) String that will appear at the end of the output
+ text if it has been truncated.
+
+ .. versionadded:: 3.4
+
+
+ :class:`TextWrapper` also provides some public methods, analogous to the
module-level convenience functions:
.. method:: wrap(text)
@@ -251,4 +289,3 @@ in a block of text.
Wraps the single paragraph in *text*, and returns a single string
containing the wrapped paragraph.
-
diff --git a/Doc/library/threading.rst b/Doc/library/threading.rst
index 7c326d1..4a3b3ea 100644
--- a/Doc/library/threading.rst
+++ b/Doc/library/threading.rst
@@ -57,6 +57,15 @@ This module defines the following functions:
and threads that have not yet been started.
+.. function:: main_thread()
+
+ Return the main :class:`Thread` object. In normal conditions, the
+ main thread is the thread from which the Python interpreter was
+ started.
+
+ .. versionadded:: 3.4
+
+
.. function:: settrace(func)
.. index:: single: trace function
diff --git a/Doc/library/timeit.rst b/Doc/library/timeit.rst
index a487917..824a8a3 100644
--- a/Doc/library/timeit.rst
+++ b/Doc/library/timeit.rst
@@ -63,6 +63,12 @@ The module defines three convenience functions and a public class:
Create a :class:`Timer` instance with the given statement, *setup* code and
*timer* function and run its :meth:`.timeit` method with *number* executions.
+ .. note::
+
+ Because :meth:`.timeit` is executing *stmt*, placing a return statement
+ in *stmt* will prevent :meth:`.timeit` from returning execution time.
+ It will instead return the data specified by your return statement.
+
.. function:: repeat(stmt='pass', setup='pass', timer=<default timer>, repeat=3, number=1000000)
@@ -151,7 +157,7 @@ The module defines three convenience functions and a public class:
t = Timer(...) # outside the try/except
try:
t.timeit(...) # or t.repeat(...)
- except:
+ except Exception:
t.print_exc()
The advantage over the standard traceback is that source lines in the
diff --git a/Doc/library/tkinter.rst b/Doc/library/tkinter.rst
index eeb1f80..0715a43 100644
--- a/Doc/library/tkinter.rst
+++ b/Doc/library/tkinter.rst
@@ -15,7 +15,7 @@ this should open a window demonstrating a simple Tk interface.
.. seealso::
- `Python Tkinter Resources <http://www.python.org/topics/tkinter/>`_
+ `Python Tkinter Resources <https://wiki.python.org/moin/TkInter>`_
The Python Tkinter Topic Guide provides a great deal of information on using Tk
from Python and links to other sources of information on Tk.
@@ -747,32 +747,6 @@ Entry widget indexes (index, view index, etc.)
displayed. You can use these :mod:`tkinter` functions to access these special
points in text widgets:
-.. function:: AtEnd()
- refers to the last position in the text
-
- .. deprecated:: 3.3
-
-.. function:: AtInsert()
- refers to the point where the text cursor is
-
- .. deprecated:: 3.3
-
-.. function:: AtSelFirst()
- indicates the beginning point of the selected text
-
- .. deprecated:: 3.3
-
-.. function:: AtSelLast()
- denotes the last point of the selected text and finally
-
- .. deprecated:: 3.3
-
-.. function:: At(x[, y])
- refers to the character at pixel location *x*, *y* (with *y* not used in the
- case of a text entry widget, which contains a single line of text).
-
- .. deprecated:: 3.3
-
Text widget indexes
The index notation for Text widgets is very rich and is best described in the Tk
man pages.
diff --git a/Doc/library/traceback.rst b/Doc/library/traceback.rst
index 32e5733..15fbedc 100644
--- a/Doc/library/traceback.rst
+++ b/Doc/library/traceback.rst
@@ -72,7 +72,7 @@ The module defines the following functions:
Return a list of up to *limit* "pre-processed" stack trace entries extracted
from the traceback object *traceback*. It is useful for alternate formatting of
stack traces. If *limit* is omitted or ``None``, all entries are extracted. A
- "pre-processed" stack trace entry is a quadruple (*filename*, *line number*,
+ "pre-processed" stack trace entry is a 4-tuple (*filename*, *line number*,
*function name*, *text*) representing the information that is usually printed
for a stack trace. The *text* is a string with leading and trailing whitespace
stripped; if the source is not available it is ``None``.
@@ -129,6 +129,13 @@ The module defines the following functions:
A shorthand for ``format_list(extract_stack(f, limit))``.
+.. function:: clear_frames(tb)
+
+ Clears the local variables of all the stack frames in a traceback *tb*
+ by calling the :meth:`clear` method of each frame object.
+
+ .. versionadded:: 3.4
+
.. _traceback-example:
@@ -146,7 +153,7 @@ module. ::
source = input(">>> ")
try:
exec(source, envdir)
- except:
+ except Exception:
print("Exception in user code:")
print("-"*60)
traceback.print_exc(file=sys.stdout)
diff --git a/Doc/library/tracemalloc.rst b/Doc/library/tracemalloc.rst
new file mode 100644
index 0000000..3405518
--- /dev/null
+++ b/Doc/library/tracemalloc.rst
@@ -0,0 +1,634 @@
+:mod:`tracemalloc` --- Trace memory allocations
+===============================================
+
+.. module:: tracemalloc
+ :synopsis: Trace memory allocations.
+
+.. versionadded:: 3.4
+
+The tracemalloc module is a debug tool to trace memory blocks allocated by
+Python. It provides the following information:
+
+* Traceback where an object was allocated
+* Statistics on allocated memory blocks per filename and per line number:
+ total size, number and average size of allocated memory blocks
+* Compute the differences between two snapshots to detect memory leaks
+
+To trace most memory blocks allocated by Python, the module should be started
+as early as possible by setting the :envvar:`PYTHONTRACEMALLOC` environment
+variable to ``1``, or by using :option:`-X` ``tracemalloc`` command line
+option. The :func:`tracemalloc.start` function can be called at runtime to
+start tracing Python memory allocations.
+
+By default, a trace of an allocated memory block only stores the most recent
+frame (1 frame). To store 25 frames at startup: set the
+:envvar:`PYTHONTRACEMALLOC` environment variable to ``25``, or use the
+:option:`-X` ``tracemalloc=25`` command line option.
+
+
+Examples
+========
+
+Display the top 10
+------------------
+
+Display the 10 files allocating the most memory::
+
+ import tracemalloc
+
+ tracemalloc.start()
+
+ # ... run your application ...
+
+ snapshot = tracemalloc.take_snapshot()
+ top_stats = snapshot.statistics('lineno')
+
+ print("[ Top 10 ]")
+ for stat in top_stats[:10]:
+ print(stat)
+
+
+Example of output of the Python test suite::
+
+ [ Top 10 ]
+ <frozen importlib._bootstrap>:716: size=4855 KiB, count=39328, average=126 B
+ <frozen importlib._bootstrap>:284: size=521 KiB, count=3199, average=167 B
+ /usr/lib/python3.4/collections/__init__.py:368: size=244 KiB, count=2315, average=108 B
+ /usr/lib/python3.4/unittest/case.py:381: size=185 KiB, count=779, average=243 B
+ /usr/lib/python3.4/unittest/case.py:402: size=154 KiB, count=378, average=416 B
+ /usr/lib/python3.4/abc.py:133: size=88.7 KiB, count=347, average=262 B
+ <frozen importlib._bootstrap>:1446: size=70.4 KiB, count=911, average=79 B
+ <frozen importlib._bootstrap>:1454: size=52.0 KiB, count=25, average=2131 B
+ <string>:5: size=49.7 KiB, count=148, average=344 B
+ /usr/lib/python3.4/sysconfig.py:411: size=48.0 KiB, count=1, average=48.0 KiB
+
+We can see that Python loaded ``4.8 MiB`` data (bytecode and constants) from
+modules and that the :mod:`collections` module allocated ``244 KiB`` to build
+:class:`~collections.namedtuple` types.
+
+See :meth:`Snapshot.statistics` for more options.
+
+
+Compute differences
+-------------------
+
+Take two snapshots and display the differences::
+
+ import tracemalloc
+ tracemalloc.start()
+ # ... start your application ...
+
+ snapshot1 = tracemalloc.take_snapshot()
+ # ... call the function leaking memory ...
+ snapshot2 = tracemalloc.take_snapshot()
+
+ top_stats = snapshot2.compare_to(snapshot1, 'lineno')
+
+ print("[ Top 10 differences ]")
+ for stat in top_stats[:10]:
+ print(stat)
+
+Example of output before/after running some tests of the Python test suite::
+
+ [ Top 10 differences ]
+ <frozen importlib._bootstrap>:716: size=8173 KiB (+4428 KiB), count=71332 (+39369), average=117 B
+ /usr/lib/python3.4/linecache.py:127: size=940 KiB (+940 KiB), count=8106 (+8106), average=119 B
+ /usr/lib/python3.4/unittest/case.py:571: size=298 KiB (+298 KiB), count=589 (+589), average=519 B
+ <frozen importlib._bootstrap>:284: size=1005 KiB (+166 KiB), count=7423 (+1526), average=139 B
+ /usr/lib/python3.4/mimetypes.py:217: size=112 KiB (+112 KiB), count=1334 (+1334), average=86 B
+ /usr/lib/python3.4/http/server.py:848: size=96.0 KiB (+96.0 KiB), count=1 (+1), average=96.0 KiB
+ /usr/lib/python3.4/inspect.py:1465: size=83.5 KiB (+83.5 KiB), count=109 (+109), average=784 B
+ /usr/lib/python3.4/unittest/mock.py:491: size=77.7 KiB (+77.7 KiB), count=143 (+143), average=557 B
+ /usr/lib/python3.4/urllib/parse.py:476: size=71.8 KiB (+71.8 KiB), count=969 (+969), average=76 B
+ /usr/lib/python3.4/contextlib.py:38: size=67.2 KiB (+67.2 KiB), count=126 (+126), average=546 B
+
+We can see that Python has loaded ``8.2 MiB`` of module data (bytecode and
+constants), and that this is ``4.4 MiB`` more than had been loaded before the
+tests, when the previous snapshot was taken. Similarly, the :mod:`linecache`
+module has cached ``940 KiB`` of Python source code to format tracebacks, all
+of it since the previous snapshot.
+
+If the system has little free memory, snapshots can be written on disk using
+the :meth:`Snapshot.dump` method to analyze the snapshot offline. Then use the
+:meth:`Snapshot.load` method reload the snapshot.
+
+
+Get the traceback of a memory block
+-----------------------------------
+
+Code to display the traceback of the biggest memory block::
+
+ import tracemalloc
+
+ # Store 25 frames
+ tracemalloc.start(25)
+
+ # ... run your application ...
+
+ snapshot = tracemalloc.take_snapshot()
+ top_stats = snapshot.statistics('traceback')
+
+ # pick the biggest memory block
+ stat = top_stats[0]
+ print("%s memory blocks: %.1f KiB" % (stat.count, stat.size / 1024))
+ for line in stat.traceback.format():
+ print(line)
+
+Example of output of the Python test suite (traceback limited to 25 frames)::
+
+ 903 memory blocks: 870.1 KiB
+ File "<frozen importlib._bootstrap>", line 716
+ File "<frozen importlib._bootstrap>", line 1036
+ File "<frozen importlib._bootstrap>", line 934
+ File "<frozen importlib._bootstrap>", line 1068
+ File "<frozen importlib._bootstrap>", line 619
+ File "<frozen importlib._bootstrap>", line 1581
+ File "<frozen importlib._bootstrap>", line 1614
+ File "/usr/lib/python3.4/doctest.py", line 101
+ import pdb
+ File "<frozen importlib._bootstrap>", line 284
+ File "<frozen importlib._bootstrap>", line 938
+ File "<frozen importlib._bootstrap>", line 1068
+ File "<frozen importlib._bootstrap>", line 619
+ File "<frozen importlib._bootstrap>", line 1581
+ File "<frozen importlib._bootstrap>", line 1614
+ File "/usr/lib/python3.4/test/support/__init__.py", line 1728
+ import doctest
+ File "/usr/lib/python3.4/test/test_pickletools.py", line 21
+ support.run_doctest(pickletools)
+ File "/usr/lib/python3.4/test/regrtest.py", line 1276
+ test_runner()
+ File "/usr/lib/python3.4/test/regrtest.py", line 976
+ display_failure=not verbose)
+ File "/usr/lib/python3.4/test/regrtest.py", line 761
+ match_tests=ns.match_tests)
+ File "/usr/lib/python3.4/test/regrtest.py", line 1563
+ main()
+ File "/usr/lib/python3.4/test/__main__.py", line 3
+ regrtest.main_in_temp_cwd()
+ File "/usr/lib/python3.4/runpy.py", line 73
+ exec(code, run_globals)
+ File "/usr/lib/python3.4/runpy.py", line 160
+ "__main__", fname, loader, pkg_name)
+
+We can see that the most memory was allocated in the :mod:`importlib` module to
+load data (bytecode and constants) from modules: ``870 KiB``. The traceback is
+where the :mod:`importlib` loaded data most recently: on the ``import pdb``
+line of the :mod:`doctest` module. The traceback may change if a new module is
+loaded.
+
+
+Pretty top
+----------
+
+Code to display the 10 lines allocating the most memory with a pretty output,
+ignoring ``<frozen importlib._bootstrap>`` and ``<unknown>`` files::
+
+ import linecache
+ import os
+ import tracemalloc
+
+ def display_top(snapshot, group_by='lineno', limit=10):
+ snapshot = snapshot.filter_traces((
+ tracemalloc.Filter(False, "<frozen importlib._bootstrap>"),
+ tracemalloc.Filter(False, "<unknown>"),
+ ))
+ top_stats = snapshot.statistics(group_by)
+
+ print("Top %s lines" % limit)
+ for index, stat in enumerate(top_stats[:limit], 1):
+ frame = stat.traceback[0]
+ # replace "/path/to/module/file.py" with "module/file.py"
+ filename = os.sep.join(frame.filename.split(os.sep)[-2:])
+ print("#%s: %s:%s: %.1f KiB"
+ % (index, filename, frame.lineno, stat.size / 1024))
+ line = linecache.getline(frame.filename, frame.lineno).strip()
+ if line:
+ print(' %s' % line)
+
+ other = top_stats[limit:]
+ if other:
+ size = sum(stat.size for stat in other)
+ print("%s other: %.1f KiB" % (len(other), size / 1024))
+ total = sum(stat.size for stat in top_stats)
+ print("Total allocated size: %.1f KiB" % (total / 1024))
+
+ tracemalloc.start()
+
+ # ... run your application ...
+
+ snapshot = tracemalloc.take_snapshot()
+ display_top(snapshot)
+
+Example of output of the Python test suite::
+
+ Top 10 lines
+ #1: Lib/base64.py:414: 419.8 KiB
+ _b85chars2 = [(a + b) for a in _b85chars for b in _b85chars]
+ #2: Lib/base64.py:306: 419.8 KiB
+ _a85chars2 = [(a + b) for a in _a85chars for b in _a85chars]
+ #3: collections/__init__.py:368: 293.6 KiB
+ exec(class_definition, namespace)
+ #4: Lib/abc.py:133: 115.2 KiB
+ cls = super().__new__(mcls, name, bases, namespace)
+ #5: unittest/case.py:574: 103.1 KiB
+ testMethod()
+ #6: Lib/linecache.py:127: 95.4 KiB
+ lines = fp.readlines()
+ #7: urllib/parse.py:476: 71.8 KiB
+ for a in _hexdig for b in _hexdig}
+ #8: <string>:5: 62.0 KiB
+ #9: Lib/_weakrefset.py:37: 60.0 KiB
+ self.data = set()
+ #10: Lib/base64.py:142: 59.8 KiB
+ _b32tab2 = [a + b for a in _b32tab for b in _b32tab]
+ 6220 other: 3602.8 KiB
+ Total allocated size: 5303.1 KiB
+
+See :meth:`Snapshot.statistics` for more options.
+
+
+API
+===
+
+Functions
+---------
+
+.. function:: clear_traces()
+
+ Clear traces of memory blocks allocated by Python.
+
+ See also :func:`stop`.
+
+
+.. function:: get_object_traceback(obj)
+
+ Get the traceback where the Python object *obj* was allocated.
+ Return a :class:`Traceback` instance, or ``None`` if the :mod:`tracemalloc`
+ module is not tracing memory allocations or did not trace the allocation of
+ the object.
+
+ See also :func:`gc.get_referrers` and :func:`sys.getsizeof` functions.
+
+
+.. function:: get_traceback_limit()
+
+ Get the maximum number of frames stored in the traceback of a trace.
+
+ The :mod:`tracemalloc` module must be tracing memory allocations to
+ get the limit, otherwise an exception is raised.
+
+ The limit is set by the :func:`start` function.
+
+
+.. function:: get_traced_memory()
+
+ Get the current size and peak size of memory blocks traced by the
+ :mod:`tracemalloc` module as a tuple: ``(current: int, peak: int)``.
+
+
+.. function:: get_tracemalloc_memory()
+
+ Get the memory usage in bytes of the :mod:`tracemalloc` module used to store
+ traces of memory blocks.
+ Return an :class:`int`.
+
+
+.. function:: is_tracing()
+
+ ``True`` if the :mod:`tracemalloc` module is tracing Python memory
+ allocations, ``False`` otherwise.
+
+ See also :func:`start` and :func:`stop` functions.
+
+
+.. function:: start(nframe: int=1)
+
+ Start tracing Python memory allocations: install hooks on Python memory
+ allocators. Collected tracebacks of traces will be limited to *nframe*
+ frames. By default, a trace of a memory block only stores the most recent
+ frame: the limit is ``1``. *nframe* must be greater or equal to ``1``.
+
+ Storing more than ``1`` frame is only useful to compute statistics grouped
+ by ``'traceback'`` or to compute cumulative statistics: see the
+ :meth:`Snapshot.compare_to` and :meth:`Snapshot.statistics` methods.
+
+ Storing more frames increases the memory and CPU overhead of the
+ :mod:`tracemalloc` module. Use the :func:`get_tracemalloc_memory` function
+ to measure how much memory is used by the :mod:`tracemalloc` module.
+
+ The :envvar:`PYTHONTRACEMALLOC` environment variable
+ (``PYTHONTRACEMALLOC=NFRAME``) and the :option:`-X` ``tracemalloc=NFRAME``
+ command line option can be used to start tracing at startup.
+
+ See also :func:`stop`, :func:`is_tracing` and :func:`get_traceback_limit`
+ functions.
+
+
+.. function:: stop()
+
+ Stop tracing Python memory allocations: uninstall hooks on Python memory
+ allocators. Also clears all previously collected traces of memory blocks
+ allocated by Python.
+
+ Call :func:`take_snapshot` function to take a snapshot of traces before
+ clearing them.
+
+ See also :func:`start`, :func:`is_tracing` and :func:`clear_traces`
+ functions.
+
+
+.. function:: take_snapshot()
+
+ Take a snapshot of traces of memory blocks allocated by Python. Return a new
+ :class:`Snapshot` instance.
+
+ The snapshot does not include memory blocks allocated before the
+ :mod:`tracemalloc` module started to trace memory allocations.
+
+ Tracebacks of traces are limited to :func:`get_traceback_limit` frames. Use
+ the *nframe* parameter of the :func:`start` function to store more frames.
+
+ The :mod:`tracemalloc` module must be tracing memory allocations to take a
+ snapshot, see the the :func:`start` function.
+
+ See also the :func:`get_object_traceback` function.
+
+
+Filter
+------
+
+.. class:: Filter(inclusive: bool, filename_pattern: str, lineno: int=None, all_frames: bool=False)
+
+ Filter on traces of memory blocks.
+
+ See the :func:`fnmatch.fnmatch` function for the syntax of
+ *filename_pattern*. The ``'.pyc'`` and ``'.pyo'`` file extensions are
+ replaced with ``'.py'``.
+
+ Examples:
+
+ * ``Filter(True, subprocess.__file__)`` only includes traces of the
+ :mod:`subprocess` module
+ * ``Filter(False, tracemalloc.__file__)`` excludes traces of the
+ :mod:`tracemalloc` module
+ * ``Filter(False, "<unknown>")`` excludes empty tracebacks
+
+ .. attribute:: inclusive
+
+ If *inclusive* is ``True`` (include), only trace memory blocks allocated
+ in a file with a name matching :attr:`filename_pattern` at line number
+ :attr:`lineno`.
+
+ If *inclusive* is ``False`` (exclude), ignore memory blocks allocated in
+ a file with a name matching :attr:`filename_pattern` at line number
+ :attr:`lineno`.
+
+ .. attribute:: lineno
+
+ Line number (``int``) of the filter. If *lineno* is ``None``, the filter
+ matches any line number.
+
+ .. attribute:: filename_pattern
+
+ Filename pattern of the filter (``str``).
+
+ .. attribute:: all_frames
+
+ If *all_frames* is ``True``, all frames of the traceback are checked. If
+ *all_frames* is ``False``, only the most recent frame is checked.
+
+ This attribute has no effect if the traceback limit is ``1``. See the
+ :func:`get_traceback_limit` function and :attr:`Snapshot.traceback_limit`
+ attribute.
+
+
+Frame
+-----
+
+.. class:: Frame
+
+ Frame of a traceback.
+
+ The :class:`Traceback` class is a sequence of :class:`Frame` instances.
+
+ .. attribute:: filename
+
+ Filename (``str``).
+
+ .. attribute:: lineno
+
+ Line number (``int``).
+
+
+Snapshot
+--------
+
+.. class:: Snapshot
+
+ Snapshot of traces of memory blocks allocated by Python.
+
+ The :func:`take_snapshot` function creates a snapshot instance.
+
+ .. method:: compare_to(old_snapshot: Snapshot, group_by: str, cumulative: bool=False)
+
+ Compute the differences with an old snapshot. Get statistics as a sorted
+ list of :class:`StatisticDiff` instances grouped by *group_by*.
+
+ See the :meth:`statistics` method for *group_by* and *cumulative*
+ parameters.
+
+ The result is sorted from the biggest to the smallest by: absolute value
+ of :attr:`StatisticDiff.size_diff`, :attr:`StatisticDiff.size`, absolute
+ value of :attr:`StatisticDiff.count_diff`, :attr:`Statistic.count` and
+ then by :attr:`StatisticDiff.traceback`.
+
+
+ .. method:: dump(filename)
+
+ Write the snapshot into a file.
+
+ Use :meth:`load` to reload the snapshot.
+
+
+ .. method:: filter_traces(filters)
+
+ Create a new :class:`Snapshot` instance with a filtered :attr:`traces`
+ sequence, *filters* is a list of :class:`Filter` instances. If *filters*
+ is an empty list, return a new :class:`Snapshot` instance with a copy of
+ the traces.
+
+ All inclusive filters are applied at once, a trace is ignored if no
+ inclusive filters match it. A trace is ignored if at least one exclusive
+ filter matchs it.
+
+
+ .. classmethod:: load(filename)
+
+ Load a snapshot from a file.
+
+ See also :meth:`dump`.
+
+
+ .. method:: statistics(group_by: str, cumulative: bool=False)
+
+ Get statistics as a sorted list of :class:`Statistic` instances grouped
+ by *group_by*:
+
+ ===================== ========================
+ group_by description
+ ===================== ========================
+ ``'filename'`` filename
+ ``'lineno'`` filename and line number
+ ``'traceback'`` traceback
+ ===================== ========================
+
+ If *cumulative* is ``True``, cumulate size and count of memory blocks of
+ all frames of the traceback of a trace, not only the most recent frame.
+ The cumulative mode can only be used with *group_by* equals to
+ ``'filename'`` and ``'lineno'``.
+
+ The result is sorted from the biggest to the smallest by:
+ :attr:`Statistic.size`, :attr:`Statistic.count` and then by
+ :attr:`Statistic.traceback`.
+
+
+ .. attribute:: traceback_limit
+
+ Maximum number of frames stored in the traceback of :attr:`traces`:
+ result of the :func:`get_traceback_limit` when the snapshot was taken.
+
+ .. attribute:: traces
+
+ Traces of all memory blocks allocated by Python: sequence of
+ :class:`Trace` instances.
+
+ The sequence has an undefined order. Use the :meth:`Snapshot.statistics`
+ method to get a sorted list of statistics.
+
+
+Statistic
+---------
+
+.. class:: Statistic
+
+ Statistic on memory allocations.
+
+ :func:`Snapshot.statistics` returns a list of :class:`Statistic` instances.
+
+ See also the :class:`StatisticDiff` class.
+
+ .. attribute:: count
+
+ Number of memory blocks (``int``).
+
+ .. attribute:: size
+
+ Total size of memory blocks in bytes (``int``).
+
+ .. attribute:: traceback
+
+ Traceback where the memory block was allocated, :class:`Traceback`
+ instance.
+
+
+StatisticDiff
+-------------
+
+.. class:: StatisticDiff
+
+ Statistic difference on memory allocations between an old and a new
+ :class:`Snapshot` instance.
+
+ :func:`Snapshot.compare_to` returns a list of :class:`StatisticDiff`
+ instances. See also the :class:`Statistic` class.
+
+ .. attribute:: count
+
+ Number of memory blocks in the new snapshot (``int``): ``0`` if
+ the memory blocks have been released in the new snapshot.
+
+ .. attribute:: count_diff
+
+ Difference of number of memory blocks between the old and the new
+ snapshots (``int``): ``0`` if the memory blocks have been allocated in
+ the new snapshot.
+
+ .. attribute:: size
+
+ Total size of memory blocks in bytes in the new snapshot (``int``):
+ ``0`` if the memory blocks have been released in the new snapshot.
+
+ .. attribute:: size_diff
+
+ Difference of total size of memory blocks in bytes between the old and
+ the new snapshots (``int``): ``0`` if the memory blocks have been
+ allocated in the new snapshot.
+
+ .. attribute:: traceback
+
+ Traceback where the memory blocks were allocated, :class:`Traceback`
+ instance.
+
+
+Trace
+-----
+
+.. class:: Trace
+
+ Trace of a memory block.
+
+ The :attr:`Snapshot.traces` attribute is a sequence of :class:`Trace`
+ instances.
+
+ .. attribute:: size
+
+ Size of the memory block in bytes (``int``).
+
+ .. attribute:: traceback
+
+ Traceback where the memory block was allocated, :class:`Traceback`
+ instance.
+
+
+Traceback
+---------
+
+.. class:: Traceback
+
+ Sequence of :class:`Frame` instances sorted from the most recent frame to
+ the oldest frame.
+
+ A traceback contains at least ``1`` frame. If the ``tracemalloc`` module
+ failed to get a frame, the filename ``"<unknown>"`` at line number ``0`` is
+ used.
+
+ When a snapshot is taken, tracebacks of traces are limited to
+ :func:`get_traceback_limit` frames. See the :func:`take_snapshot` function.
+
+ The :attr:`Trace.traceback` attribute is an instance of :class:`Traceback`
+ instance.
+
+ .. method:: format(limit=None)
+
+ Format the traceback as a list of lines with newlines. Use the
+ :mod:`linecache` module to retrieve lines from the source code. If
+ *limit* is set, only format the *limit* most recent frames.
+
+ Similar to the :func:`traceback.format_tb` function, except that
+ :meth:`format` does not include newlines.
+
+ Example::
+
+ print("Traceback (most recent call first):")
+ for line in traceback:
+ print(line)
+
+ Output::
+
+ Traceback (most recent call first):
+ File "test.py", line 9
+ obj = Object()
+ File "test.py", line 12
+ tb = tracemalloc.get_object_traceback(f())
+
diff --git a/Doc/library/tulip_coro.dia b/Doc/library/tulip_coro.dia
new file mode 100644
index 0000000..eccf4fa
--- /dev/null
+++ b/Doc/library/tulip_coro.dia
Binary files differ
diff --git a/Doc/library/tulip_coro.png b/Doc/library/tulip_coro.png
new file mode 100644
index 0000000..65b6951
--- /dev/null
+++ b/Doc/library/tulip_coro.png
Binary files differ
diff --git a/Doc/library/types.rst b/Doc/library/types.rst
index 695480f..abdb939 100644
--- a/Doc/library/types.rst
+++ b/Doc/library/types.rst
@@ -15,6 +15,9 @@ It also defines names for some object types that are used by the standard
Python interpreter, but not exposed as builtins like :class:`int` or
:class:`str` are.
+Finally, it provides some additional type-related utility classes and functions
+that are not fundamental enough to be builtins.
+
Dynamic Type Creation
---------------------
@@ -107,9 +110,35 @@ Standard names are defined for the following types:
C".)
-.. data:: ModuleType
+.. class:: ModuleType(name, doc=None)
+
+ The type of :term:`modules <module>`. Constructor takes the name of the
+ module to be created and optionally its :term:`docstring`.
+
+ .. attribute:: __doc__
+
+ The :term:`docstring` of the module. Defaults to ``None``.
+
+ .. attribute:: __loader__
+
+ The :term:`loader` which loaded the module. Defaults to ``None``.
+
+ .. versionchanged:: 3.4
+ Defaults to ``None``. Previously the attribute was optional.
+
+ .. attribute:: __name__
+
+ The name of the module.
- The type of modules.
+ .. attribute:: __package__
+
+ Which :term:`package` a module belongs to. If the module is top-level
+ (i.e. not a part of any specific package) then the attribute should be set
+ to ``''``, else it should be set to the name of the package (which can be
+ :attr:`__name__` if the module is a package itself). Defaults to ``None``.
+
+ .. versionchanged:: 3.4
+ Defaults to ``None``. Previously the attribute was optional.
.. data:: TracebackType
@@ -194,6 +223,9 @@ Standard names are defined for the following types:
Return a new view of the underlying mapping's values.
+Additional Utility Classes and Functions
+----------------------------------------
+
.. class:: SimpleNamespace
A simple :class:`object` subclass that provides attribute access to its
@@ -212,9 +244,26 @@ Standard names are defined for the following types:
keys = sorted(self.__dict__)
items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys)
return "{}({})".format(type(self).__name__, ", ".join(items))
+ def __eq__(self, other):
+ return self.__dict__ == other.__dict__
``SimpleNamespace`` may be useful as a replacement for ``class NS: pass``.
However, for a structured record type use :func:`~collections.namedtuple`
instead.
.. versionadded:: 3.3
+
+
+.. function:: DynamicClassAttribute(fget=None, fset=None, fdel=None, doc=None)
+
+ Route attribute access on a class to __getattr__.
+
+ This is a descriptor, used to define attributes that act differently when
+ accessed through an instance and through a class. Instance access remains
+ normal, but access to an attribute through a class will be routed to the
+ class's __getattr__ method; this is done by raising AttributeError.
+
+ This allows one to have properties active on an instance, and have virtual
+ attributes on the class with the same name (see Enum for an example).
+
+ .. versionadded:: 3.4
diff --git a/Doc/library/undoc.rst b/Doc/library/undoc.rst
index 80386d2..20830e2 100644
--- a/Doc/library/undoc.rst
+++ b/Doc/library/undoc.rst
@@ -20,7 +20,7 @@ These modules are used to implement the :mod:`os.path` module, and are not
documented beyond this mention. There's little need to document these.
:mod:`ntpath`
- --- Implementation of :mod:`os.path` on Win32, Win64, WinCE, and OS/2 platforms.
+ --- Implementation of :mod:`os.path` on Win32, Win64, and WinCE platforms.
:mod:`posixpath`
--- Implementation of :mod:`os.path` on POSIX.
diff --git a/Doc/library/unicodedata.rst b/Doc/library/unicodedata.rst
index ad70dd9..3b3d3a0 100644
--- a/Doc/library/unicodedata.rst
+++ b/Doc/library/unicodedata.rst
@@ -15,8 +15,8 @@
This module provides access to the Unicode Character Database (UCD) which
defines character properties for all Unicode characters. The data contained in
-this database is compiled from the `UCD version 6.1.0
-<http://www.unicode.org/Public/6.1.0/ucd>`_.
+this database is compiled from the `UCD version 6.3.0
+<http://www.unicode.org/Public/6.3.0/ucd>`_.
The module uses the same names and symbols as defined by Unicode
Standard Annex #44, `"Unicode Character Database"
@@ -166,6 +166,6 @@ Examples:
.. rubric:: Footnotes
-.. [#] http://www.unicode.org/Public/6.1.0/ucd/NameAliases.txt
+.. [#] http://www.unicode.org/Public/6.3.0/ucd/NameAliases.txt
-.. [#] http://www.unicode.org/Public/6.1.0/ucd/NamedSequences.txt
+.. [#] http://www.unicode.org/Public/6.3.0/ucd/NamedSequences.txt
diff --git a/Doc/library/unittest.mock-examples.rst b/Doc/library/unittest.mock-examples.rst
index 554ef11..1c43bfe 100644
--- a/Doc/library/unittest.mock-examples.rst
+++ b/Doc/library/unittest.mock-examples.rst
@@ -277,6 +277,20 @@ instantiate the class in those tests.
...
AttributeError: object has no attribute 'old_method'
+Using a specification also enables a smarter matching of calls made to the
+mock, regardless of whether some parameters were passed as positional or
+named arguments::
+
+ >>> def f(a, b, c): pass
+ ...
+ >>> mock = Mock(spec=f)
+ >>> mock(1, 2, 3)
+ <Mock name='mock()' id='140161580456576'>
+ >>> mock.assert_called_with(a=1, b=2, c=3)
+
+If you want this smarter matching to also work with method calls on the mock,
+you can use :ref:`auto-speccing <auto-speccing>`.
+
If you want a stronger form of specification that prevents the setting
of arbitrary attributes as well as the getting of them then you can use
`spec_set` instead of `spec`.
@@ -920,8 +934,8 @@ After the `MagicMock` has been used we can use attributes like
the magic methods you specifically want:
>>> mock = Mock()
- >>> mock.__setitem__ = Mock(side_effect=getitem)
- >>> mock.__getitem__ = Mock(side_effect=setitem)
+ >>> mock.__getitem__ = Mock(side_effect=getitem)
+ >>> mock.__setitem__ = Mock(side_effect=setitem)
A *third* option is to use `MagicMock` but passing in `dict` as the `spec`
(or `spec_set`) argument so that the `MagicMock` created only has
diff --git a/Doc/library/unittest.mock.rst b/Doc/library/unittest.mock.rst
index f805df3..cb72a68 100644
--- a/Doc/library/unittest.mock.rst
+++ b/Doc/library/unittest.mock.rst
@@ -264,7 +264,6 @@ the `new_callable` argument to `patch`.
<Mock name='mock.method()' id='...'>
>>> mock.method.assert_called_with(1, 2, 3, test='wow')
-
.. method:: assert_called_once_with(*args, **kwargs)
Assert that the mock was called exactly once and with the specified
@@ -685,6 +684,27 @@ have to create a dictionary and unpack it using `**`:
...
KeyError
+A callable mock which was created with a *spec* (or a *spec_set*) will
+introspect the specification object's signature when matching calls to
+the mock. Therefore, it can match the actual call's arguments regardless
+of whether they were passed positionally or by name::
+
+ >>> def f(a, b, c): pass
+ ...
+ >>> mock = Mock(spec=f)
+ >>> mock(1, 2, c=3)
+ <Mock name='mock()' id='140161580456576'>
+ >>> mock.assert_called_with(1, 2, 3)
+ >>> mock.assert_called_with(a=1, b=2, c=3)
+
+This applies to :meth:`~Mock.assert_called_with`,
+:meth:`~Mock.assert_called_once_with`, :meth:`~Mock.assert_has_calls` and
+:meth:`~Mock.assert_any_call`. When :ref:`auto-speccing`, it will also
+apply to method calls on the mock object.
+
+ .. versionchanged:: 3.4
+ Added signature introspection on specced and autospecced mock objects.
+
.. class:: PropertyMock(*args, **kwargs)
@@ -1969,8 +1989,13 @@ mock_open
default) then a `MagicMock` will be created for you, with the API limited
to methods or attributes available on standard file handles.
- `read_data` is a string for the `~io.IOBase.read` method of the file handle
- to return. This is an empty string by default.
+ `read_data` is a string for the :meth:`~io.IOBase.read`,
+ :meth:`~io.IOBase.readline`, and :meth:`~io.IOBase.readlines` methods
+ of the file handle to return. Calls to those methods will take data from
+ `read_data` until it is depleted. The mock of these methods is pretty
+ simplistic. If you need more control over the data that you are feeding to
+ the tested code you will need to customize this mock for yourself.
+ `read_data` is an empty string by default.
Using `open` as a context manager is a great way to ensure your file handles
are closed properly and is becoming common::
diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst
index fdc9409..52f6e3f 100644
--- a/Doc/library/unittest.rst
+++ b/Doc/library/unittest.rst
@@ -239,9 +239,10 @@ Test Discovery
Unittest supports simple test discovery. In order to be compatible with test
discovery, all of the test files must be :ref:`modules <tut-modules>` or
-:ref:`packages <tut-packages>` importable from the top-level directory of
-the project (this means that their filenames must be valid
-:ref:`identifiers <identifiers>`).
+:ref:`packages <tut-packages>` (including :term:`namespace packages
+<namespace package>`) importable from the top-level directory of
+the project (this means that their filenames must be valid :ref:`identifiers
+<identifiers>`).
Test discovery is implemented in :meth:`TestLoader.discover`, but can also be
used from the command line. The basic command-line usage is::
@@ -306,6 +307,9 @@ as the start directory.
Test modules and packages can customize test loading and discovery by through
the `load_tests protocol`_.
+.. versionchanged:: 3.4
+ Test discovery supports :term:`namespace packages <namespace package>`.
+
.. _organizing-tests:
@@ -559,8 +563,71 @@ The following decorators implement test skipping and expected failures:
Usually you can use :meth:`TestCase.skipTest` or one of the skipping
decorators instead of raising this directly.
-Skipped tests will not have :meth:`setUp` or :meth:`tearDown` run around them.
-Skipped classes will not have :meth:`setUpClass` or :meth:`tearDownClass` run.
+Skipped tests will not have :meth:`~TestCase.setUp` or :meth:`~TestCase.tearDown` run around them.
+Skipped classes will not have :meth:`~TestCase.setUpClass` or :meth:`~TestCase.tearDownClass` run.
+Skipped modules will not have :func:`setUpModule` or :func:`tearDownModule` run.
+
+
+.. _subtests:
+
+Distinguishing test iterations using subtests
+---------------------------------------------
+
+.. versionadded:: 3.4
+
+When some of your tests differ only by a some very small differences, for
+instance some parameters, unittest allows you to distinguish them inside
+the body of a test method using the :meth:`~TestCase.subTest` context manager.
+
+For example, the following test::
+
+ class NumbersTest(unittest.TestCase):
+
+ def test_even(self):
+ """
+ Test that numbers between 0 and 5 are all even.
+ """
+ for i in range(0, 6):
+ with self.subTest(i=i):
+ self.assertEqual(i % 2, 0)
+
+will produce the following output::
+
+ ======================================================================
+ FAIL: test_even (__main__.NumbersTest) (i=1)
+ ----------------------------------------------------------------------
+ Traceback (most recent call last):
+ File "subtests.py", line 32, in test_even
+ self.assertEqual(i % 2, 0)
+ AssertionError: 1 != 0
+
+ ======================================================================
+ FAIL: test_even (__main__.NumbersTest) (i=3)
+ ----------------------------------------------------------------------
+ Traceback (most recent call last):
+ File "subtests.py", line 32, in test_even
+ self.assertEqual(i % 2, 0)
+ AssertionError: 1 != 0
+
+ ======================================================================
+ FAIL: test_even (__main__.NumbersTest) (i=5)
+ ----------------------------------------------------------------------
+ Traceback (most recent call last):
+ File "subtests.py", line 32, in test_even
+ self.assertEqual(i % 2, 0)
+ AssertionError: 1 != 0
+
+Without using a subtest, execution would stop after the first failure,
+and the error would be less easy to diagnose because the value of ``i``
+wouldn't be displayed::
+
+ ======================================================================
+ FAIL: test_even (__main__.NumbersTest)
+ ----------------------------------------------------------------------
+ Traceback (most recent call last):
+ File "subtests.py", line 32, in test_even
+ self.assertEqual(i % 2, 0)
+ AssertionError: 1 != 0
.. _unittest-contents:
@@ -607,9 +674,9 @@ Test cases
.. method:: setUp()
Method called to prepare the test fixture. This is called immediately
- before calling the test method; any exception raised by this method will
- be considered an error rather than a test failure. The default
- implementation does nothing.
+ before calling the test method; other than :exc:`AssertionError` or :exc:`SkipTest`,
+ any exception raised by this method will be considered an error rather than
+ a test failure. The default implementation does nothing.
.. method:: tearDown()
@@ -617,10 +684,10 @@ Test cases
Method called immediately after the test method has been called and the
result recorded. This is called even if the test method raised an
exception, so the implementation in subclasses may need to be particularly
- careful about checking internal state. Any exception raised by this
- method will be considered an error rather than a test failure. This
- method will only be called if the :meth:`setUp` succeeds, regardless of
- the outcome of the test method. The default implementation does nothing.
+ careful about checking internal state. Any exception, other than :exc:`AssertionError`
+ or :exc:`SkipTest`, raised by this method will be considered an error rather than a
+ test failure. This method will only be called if the :meth:`setUp` succeeds,
+ regardless of the outcome of the test method. The default implementation does nothing.
.. method:: setUpClass()
@@ -676,6 +743,21 @@ Test cases
.. versionadded:: 3.1
+ .. method:: subTest(msg=None, **params)
+
+ Return a context manager which executes the enclosed code block as a
+ subtest. *msg* and *params* are optional, arbitrary values which are
+ displayed whenever a subtest fails, allowing you to identify them
+ clearly.
+
+ A test case can contain any number of subtest declarations, and
+ they can be arbitrarily nested.
+
+ See :ref:`subtests` for more information.
+
+ .. versionadded:: 3.4
+
+
.. method:: debug()
Run the test without collecting the result. This allows exceptions raised
@@ -806,8 +888,8 @@ Test cases
- It is also possible to check that exceptions and warnings are raised using
- the following methods:
+ It is also possible to check the production of exceptions, warnings and
+ log messages using the following methods:
+---------------------------------------------------------+--------------------------------------+------------+
| Method | Checks that | New in |
@@ -824,6 +906,9 @@ Test cases
| :meth:`assertWarnsRegex(warn, r, fun, *args, **kwds) | ``fun(*args, **kwds)`` raises *warn* | 3.2 |
| <TestCase.assertWarnsRegex>` | and the message matches regex *r* | |
+---------------------------------------------------------+--------------------------------------+------------+
+ | :meth:`assertLogs(logger, level) | The ``with`` block logs on *logger* | 3.4 |
+ | <TestCase.assertLogs>` | with minimum *level* | |
+ +---------------------------------------------------------+--------------------------------------+------------+
.. method:: assertRaises(exception, callable, *args, **kwds)
assertRaises(exception, msg=None)
@@ -954,6 +1039,47 @@ Test cases
.. versionchanged:: 3.3
Added the *msg* keyword argument when used as a context manager.
+ .. method:: assertLogs(logger=None, level=None)
+
+ A context manager to test that at least one message is logged on
+ the *logger* or one of its children, with at least the given
+ *level*.
+
+ If given, *logger* should be a :class:`logging.Logger` object or a
+ :class:`str` giving the name of a logger. The default is the root
+ logger, which will catch all messages.
+
+ If given, *level* should be either a numeric logging level or
+ its string equivalent (for example either ``"ERROR"`` or
+ :attr:`logging.ERROR`). The default is :attr:`logging.INFO`.
+
+ The test passes if at least one message emitted inside the ``with``
+ block matches the *logger* and *level* conditions, otherwise it fails.
+
+ The object returned by the context manager is a recording helper
+ which keeps tracks of the matching log messages. It has two
+ attributes:
+
+ .. attribute:: records
+
+ A list of :class:`logging.LogRecord` objects of the matching
+ log messages.
+
+ .. attribute:: output
+
+ A list of :class:`str` objects with the formatted output of
+ matching messages.
+
+ Example::
+
+ with self.assertLogs('foo', level='INFO') as cm:
+ logging.getLogger('foo').info('first message')
+ logging.getLogger('foo.bar').error('second message')
+ self.assertEqual(cm.output, ['INFO:foo:first message',
+ 'ERROR:foo.bar:second message'])
+
+ .. versionadded:: 3.4
+
There are also other methods used to perform more specific checks, such as:
@@ -1393,15 +1519,24 @@ Grouping tests
Tests grouped by a :class:`TestSuite` are always accessed by iteration.
Subclasses can lazily provide tests by overriding :meth:`__iter__`. Note
- that this method maybe called several times on a single suite
- (for example when counting tests or comparing for equality)
- so the tests returned must be the same for repeated iterations.
+ that this method may be called several times on a single suite (for
+ example when counting tests or comparing for equality) so the tests
+ returned by repeated iterations before :meth:`TestSuite.run` must be the
+ same for each call iteration. After :meth:`TestSuite.run`, callers should
+ not rely on the tests returned by this method unless the caller uses a
+ subclass that overrides :meth:`TestSuite._removeTestAtIndex` to preserve
+ test references.
.. versionchanged:: 3.2
In earlier versions the :class:`TestSuite` accessed tests directly rather
than through iteration, so overriding :meth:`__iter__` wasn't sufficient
for providing tests.
+ .. versionchanged:: 3.4
+ In earlier versions the :class:`TestSuite` held references to each
+ :class:`TestCase` after :meth:`TestSuite.run`. Subclasses can restore
+ that behavior by overriding :meth:`TestSuite._removeTestAtIndex`.
+
In the typical usage of a :class:`TestSuite` object, the :meth:`run` method
is invoked by a :class:`TestRunner` rather than by the end-user test harness.
@@ -1500,7 +1635,9 @@ Loading and running tests
directory must be specified separately.
If importing a module fails, for example due to a syntax error, then this
- will be recorded as a single error and discovery will continue.
+ will be recorded as a single error and discovery will continue. If the
+ import failure is due to :exc:`SkipTest` being raised, it will be recorded
+ as a skip instead of an error.
If a test package name (directory with :file:`__init__.py`) matches the
pattern then the package will be checked for a ``load_tests``
@@ -1519,6 +1656,14 @@ Loading and running tests
.. versionadded:: 3.2
+ .. versionchanged:: 3.4
+ Modules that raise :exc:`SkipTest` on import are recorded as skips,
+ not errors.
+ Discovery works for :term:`namespace packages <namespace package>`.
+ Paths are sorted before being imported so that execution order is
+ the same even if the underlying file system's ordering is not
+ dependent on file name.
+
The following attributes of a :class:`TestLoader` can be configured either by
subclassing or assignment on an instance:
@@ -1630,6 +1775,10 @@ Loading and running tests
Return ``True`` if all tests run so far have passed, otherwise returns
``False``.
+ .. versionchanged:: 3.4
+ Returns ``False`` if there were any :attr:`unexpectedSuccesses`
+ from tests marked with the :func:`expectedFailure` decorator.
+
.. method:: stop()
@@ -1658,14 +1807,14 @@ Loading and running tests
Called after the test case *test* has been executed, regardless of the
outcome.
- .. method:: startTestRun(test)
+ .. method:: startTestRun()
Called once before any tests are executed.
.. versionadded:: 3.1
- .. method:: stopTestRun(test)
+ .. method:: stopTestRun()
Called once after all tests are executed.
@@ -1728,6 +1877,22 @@ Loading and running tests
:attr:`unexpectedSuccesses` attribute.
+ .. method:: addSubTest(test, subtest, outcome)
+
+ Called when a subtest finishes. *test* is the test case
+ corresponding to the test method. *subtest* is a custom
+ :class:`TestCase` instance describing the subtest.
+
+ If *outcome* is :const:`None`, the subtest succeeded. Otherwise,
+ it failed with an exception where *outcome* is a tuple of the form
+ returned by :func:`sys.exc_info`: ``(type, value, traceback)``.
+
+ The default implementation does nothing when the outcome is a
+ success, and records subtest failures as normal failures.
+
+ .. versionadded:: 3.4
+
+
.. class:: TextTestResult(stream, descriptions, verbosity)
A concrete implementation of :class:`TestResult` used by the
@@ -1783,6 +1948,14 @@ Loading and running tests
stream, descriptions, verbosity
+ .. method:: run(test)
+
+ This method is the main public interface to the `TextTestRunner`. This
+ method takes a :class:`TestSuite` or :class:`TestCase` instance. A
+ :class:`TestResult` is created by calling
+ :func:`_makeResult` and the test(s) are run and the
+ results printed to stdout.
+
.. function:: main(module='__main__', defaultTest=None, argv=None, testRunner=None, \
testLoader=unittest.defaultTestLoader, exit=True, verbosity=1, \
@@ -1802,9 +1975,10 @@ Loading and running tests
if __name__ == '__main__':
unittest.main(verbosity=2)
- The *defaultTest* argument is the name of the test to run if no test names
- are specified via *argv*. If not specified or ``None`` and no test names are
- provided via *argv*, all tests found in *module* are run.
+ The *defaultTest* argument is either the name of a single test or an
+ iterable of test names to run if no test names are specified via *argv*. If
+ not specified or ``None`` and no test names are provided via *argv*, all
+ tests found in *module* are run.
The *argv* argument can be a list of options passed to the program, with the
first element being the program name. If not specified or ``None``,
@@ -1842,6 +2016,10 @@ Loading and running tests
The *verbosity*, *failfast*, *catchbreak*, *buffer*
and *warnings* parameters were added.
+ .. versionchanged:: 3.4
+ The *defaultTest* parameter was changed to also accept an iterable of
+ test names.
+
load_tests Protocol
###################
diff --git a/Doc/library/urllib.error.rst b/Doc/library/urllib.error.rst
index 25c13bd..f7f0c97 100644
--- a/Doc/library/urllib.error.rst
+++ b/Doc/library/urllib.error.rst
@@ -46,6 +46,13 @@ The following exceptions are raised by :mod:`urllib.error` as appropriate:
This is usually a string explaining the reason for this error.
+ .. attribute:: headers
+
+ The HTTP response headers for the HTTP request that caused the
+ :exc:`HTTPError`.
+
+ .. versionadded:: 3.4
+
.. exception:: ContentTooShortError(msg, content)
This exception is raised when the :func:`~urllib.request.urlretrieve`
diff --git a/Doc/library/urllib.request.rst b/Doc/library/urllib.request.rst
index 6493a29..019f59c 100644
--- a/Doc/library/urllib.request.rst
+++ b/Doc/library/urllib.request.rst
@@ -218,12 +218,17 @@ The following classes are provided:
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
+ will be used (e.g. ``'HEAD'``). If provided, its value is stored in the
:attr:`~Request.method` attribute and is used by :meth:`get_method()`.
+ Subclasses may indicate a default method by setting the
+ :attr:`~Request.method` attribute in the class itself.
.. versionchanged:: 3.3
:attr:`Request.method` argument is added to the Request class.
+ .. versionchanged:: 3.4
+ Default :attr:`Request.method` may be indicated at the class level.
+
.. class:: OpenerDirector()
@@ -356,6 +361,11 @@ The following classes are provided:
Open local files.
+.. class:: DataHandler()
+
+ Open data URLs.
+
+ .. versionadded:: 3.4
.. class:: FTPHandler()
@@ -391,6 +401,12 @@ request.
The original URL passed to the constructor.
+ .. versionchanged:: 3.4
+
+ Request.full_url is a property with setter, getter and a deleter. Getting
+ :attr:`~Request.full_url` returns the original request URL with the
+ fragment, if it was present.
+
.. attribute:: Request.type
The URI scheme.
@@ -413,6 +429,10 @@ request.
The entity body for the request, or None if not specified.
+ .. versionchanged:: 3.4
+ Changing value of :attr:`Request.data` now deletes "Content-Length"
+ header if it was previously set or calculated.
+
.. attribute:: Request.unverifiable
boolean, indicates whether the request is unverifiable as defined
@@ -420,13 +440,20 @@ request.
.. 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.
+ The HTTP request method to use. By default its value is :const:`None`,
+ which means that :meth:`~Request.get_method` will do its normal computation
+ of the method to be used. Its value can be set (thus overriding the default
+ computation in :meth:`~Request.get_method`) either by providing a default
+ value by setting it at the class level in a :class:`Request` subclass, or by
+ passing a value in to the :class:`Request` constructor via the *method*
+ argument.
.. versionadded:: 3.3
+ .. versionchanged:: 3.4
+ A default value can now be set in subclasses; previously it could only
+ be set via the constructor argument.
+
.. method:: Request.get_method()
@@ -461,65 +488,29 @@ request.
unredirected).
-.. method:: Request.get_full_url()
-
- Return the URL given in the constructor.
-
-
-.. method:: Request.set_proxy(host, type)
-
- Prepare the request by connecting to a proxy server. The *host* and *type* will
- replace those of the instance, and the instance's selector will be the original
- URL given in the constructor.
-
-
-.. method:: Request.add_data(data)
-
- Set the :class:`Request` data to *data*. This is ignored by all handlers except
- HTTP handlers --- and there it should be a byte string, and will change the
- request to be ``POST`` rather than ``GET``. Deprecated in 3.3, use
- :attr:`Request.data`.
-
- .. deprecated-removed:: 3.3 3.4
-
-
-.. method:: Request.has_data()
-
- Return whether the instance has a non-\ ``None`` data. Deprecated in 3.3,
- use :attr:`Request.data`.
-
- .. deprecated-removed:: 3.3 3.4
-
+.. method:: Request.remove_header(header)
-.. method:: Request.get_data()
+ Remove named header from the request instance (both from regular and
+ unredirected headers).
- Return the instance's data. Deprecated in 3.3, use :attr:`Request.data`.
+ .. versionadded:: 3.4
- .. deprecated-removed:: 3.3 3.4
-
-
-.. method:: Request.get_type()
-
- Return the type of the URL --- also known as the scheme. Deprecated in 3.3,
- use :attr:`Request.type`.
-
- .. deprecated-removed:: 3.3 3.4
+.. method:: Request.get_full_url()
-.. method:: Request.get_host()
+ Return the URL given in the constructor.
- Return the host to which a connection will be made. Deprecated in 3.3, use
- :attr:`Request.host`.
+ .. versionchanged:: 3.4
- .. deprecated-removed:: 3.3 3.4
+ Returns :attr:`Request.full_url`
-.. method:: Request.get_selector()
+.. method:: Request.set_proxy(host, type)
- Return the selector --- the part of the URL that is sent to the server.
- Deprecated in 3.3, use :attr:`Request.selector`.
+ Prepare the request by connecting to a proxy server. The *host* and *type* will
+ replace those of the instance, and the instance's selector will be the original
+ URL given in the constructor.
- .. deprecated-removed:: 3.3 3.4
.. method:: Request.get_header(header_name, default=None)
@@ -531,25 +522,10 @@ request.
Return a list of tuples (header_name, header_value) of the Request headers.
-
-.. method:: Request.set_proxy(host, type)
-
-.. method:: Request.get_origin_req_host()
-
- Return the request-host of the origin transaction, as defined by
- :rfc:`2965`. See the documentation for the :class:`Request` constructor.
- Deprecated in 3.3, use :attr:`Request.origin_req_host`.
-
- .. deprecated-removed:: 3.3 3.4
-
-
-.. method:: Request.is_unverifiable()
-
- Return whether the request is unverifiable, as defined by RFC 2965. See the
- documentation for the :class:`Request` constructor. Deprecated in 3.3, use
- :attr:`Request.unverifiable`.
-
- .. deprecated-removed:: 3.3 3.4
+.. versionchanged:: 3.4
+ The request methods add_data, has_data, get_data, get_type, get_host,
+ get_selector, get_origin_req_host and is_unverifiable that were deprecated
+ since 3.3 have been removed.
.. _opener-director-objects:
@@ -983,6 +959,21 @@ FileHandler Objects
hostname is given, an :exc:`~urllib.error.URLError` is raised.
+.. _data-handler-objects:
+
+DataHandler Objects
+-------------------
+
+.. method:: DataHandler.data_open(req)
+
+ Read a data URL. This kind of URL contains the content encoded in the URL
+ itself. The data URL syntax is specified in :rfc:`2397`. This implementation
+ ignores white spaces in base64 encoded data URLs so the URL may be wrapped
+ in whatever source file it comes from. But even though some browsers don't
+ mind about a missing padding at the end of a base64 encoded data URL, this
+ implementation will raise an :exc:`ValueError` in that case.
+
+
.. _ftp-handler-objects:
FTPHandler Objects
@@ -1398,7 +1389,9 @@ some point in the future.
pair: FTP; protocol
* Currently, only the following protocols are supported: HTTP (versions 0.9 and
- 1.0), FTP, and local files.
+ 1.0), FTP, local files, and data URLs.
+
+ .. versionchanged:: 3.4 Added support for data URLs.
* The caching feature of :func:`urlretrieve` has been disabled until someone
finds the time to hack proper processing of Expiration time headers.
diff --git a/Doc/library/venv.rst b/Doc/library/venv.rst
index 96ca2f5..e93f48e 100644
--- a/Doc/library/venv.rst
+++ b/Doc/library/venv.rst
@@ -76,6 +76,8 @@ Creating virtual environments
without there needing to be any reference to its venv in ``PATH``.
+.. _venv-api:
+
API
---
@@ -85,7 +87,8 @@ The high-level method described above makes use of a simple API which provides
mechanisms for third-party virtual environment creators to customize environment
creation according to their needs, the :class:`EnvBuilder` class.
-.. class:: EnvBuilder(system_site_packages=False, clear=False, symlinks=False, upgrade=False)
+.. class:: EnvBuilder(system_site_packages=False, clear=False, \
+ symlinks=False, upgrade=False, with_pip=False)
The :class:`EnvBuilder` class accepts the following keyword arguments on
instantiation:
@@ -93,8 +96,8 @@ creation according to their needs, the :class:`EnvBuilder` class.
* ``system_site_packages`` -- a Boolean value indicating that the system Python
site-packages should be available to the environment (defaults to ``False``).
- * ``clear`` -- a Boolean value which, if true, will delete any existing target
- directory instead of raising an exception (defaults to ``False``).
+ * ``clear`` -- a Boolean value which, if true, will delete the contents of
+ any existing target directory, before creating the environment.
* ``symlinks`` -- a Boolean value indicating whether to attempt to symlink the
Python binary (and any necessary DLLs or other binaries,
@@ -105,6 +108,12 @@ creation according to their needs, the :class:`EnvBuilder` class.
environment with the running Python - for use when that Python has been
upgraded in-place (defaults to ``False``).
+ * ``with_pip`` -- a Boolean value which, if true, ensures pip is
+ installed in the virtual environment. This uses :mod:`ensurepip` with
+ the ``--default-pip`` option.
+
+ .. versionchanged:: 3.4
+ Added the ``with_pip`` parameter
Creators of third-party virtual environment tools will be free to use the
@@ -188,6 +197,9 @@ creation according to their needs, the :class:`EnvBuilder` class.
* ``__VENV_NAME__`` is replaced with the environment name (final path
segment of environment directory).
+ * ``__VENV_PROMPT__`` is replaced with the prompt (the environment
+ name surrounded by parentheses and with a following space)
+
* ``__VENV_BIN_NAME__`` is replaced with the name of the bin directory
(either ``bin`` or ``Scripts``).
@@ -199,11 +211,15 @@ creation according to their needs, the :class:`EnvBuilder` class.
There is also a module-level convenience function:
-.. function:: create(env_dir, system_site_packages=False, clear=False, symlinks=False)
+.. function:: create(env_dir, system_site_packages=False, clear=False, \
+ symlinks=False, with_pip=False)
Create an :class:`EnvBuilder` with the given keyword arguments, and call its
:meth:`~EnvBuilder.create` method with the *env_dir* argument.
+ .. versionchanged:: 3.4
+ Added the ``with_pip`` parameter
+
An example of extending ``EnvBuilder``
--------------------------------------
diff --git a/Doc/library/wave.rst b/Doc/library/wave.rst
index afafb45..ab64978 100644
--- a/Doc/library/wave.rst
+++ b/Doc/library/wave.rst
@@ -19,26 +19,32 @@ The :mod:`wave` module defines the following function and exception:
.. function:: open(file, mode=None)
If *file* is a string, open the file by that name, otherwise treat it as a
- seekable file-like object. *mode* can be any of
+ file-like object. *mode* can be:
- ``'r'``, ``'rb'``
+ ``'rb'``
Read only mode.
- ``'w'``, ``'wb'``
+ ``'wb'``
Write only mode.
Note that it does not allow read/write WAV files.
- A *mode* of ``'r'`` or ``'rb'`` returns a :class:`Wave_read` object, while a
- *mode* of ``'w'`` or ``'wb'`` returns a :class:`Wave_write` object. If
- *mode* is omitted and a file-like object is passed as *file*, ``file.mode``
- is used as the default value for *mode* (the ``'b'`` flag is still added if
- necessary).
+ A *mode* of ``'rb'`` returns a :class:`Wave_read` object, while a *mode* of
+ ``'wb'`` returns a :class:`Wave_write` object. If *mode* is omitted and a
+ file-like object is passed as *file*, ``file.mode`` is used as the default
+ value for *mode*.
If you pass in a file-like object, the wave object will not close it when its
:meth:`close` method is called; it is the caller's responsibility to close
the file object.
+ The :func:`.open` function may be used in a :keyword:`with` statement. When
+ the :keyword:`with` block completes, the :meth:`Wave_read.close()
+ <wave.Wave_read.close>` or :meth:`Wave_write.close()
+ <wave.Wave_write.close()>` method is called.
+
+ .. versionchanged:: 3.4
+ Added support for unseekable files.
.. function:: openfp(file, mode)
@@ -98,8 +104,9 @@ Wave_read objects, as returned by :func:`.open`, have the following methods:
.. method:: Wave_read.getparams()
- Returns a tuple ``(nchannels, sampwidth, framerate, nframes, comptype,
- compname)``, equivalent to output of the :meth:`get\*` methods.
+ Returns a :func:`~collections.namedtuple` ``(nchannels, sampwidth,
+ framerate, nframes, comptype, compname)``, equivalent to output of the
+ :meth:`get\*` methods.
.. method:: Wave_read.readframes(n)
@@ -143,13 +150,30 @@ them, and is otherwise implementation dependent.
Wave_write Objects
------------------
+For seekable output streams, the ``wave`` header will automatically be updated
+to reflect the number of frames actually written. For unseekable streams, the
+*nframes* value must be accurate when the first frame data is written. An
+accurate *nframes* value can be achieved either by calling
+:meth:`~Wave_write.setnframes` or :meth:`~Wave_write.setparams` with the number
+of frames that will be written before :meth:`~Wave_write.close` is called and
+then using :meth:`~Wave_write.writeframesraw` to write the frame data, or by
+calling :meth:`~Wave_write.writeframes` with all of the frame data to be
+written. In the latter case :meth:`~Wave_write.writeframes` will calculate
+the number of frames in the data and set *nframes* accordingly before writing
+the frame data.
+
Wave_write objects, as returned by :func:`.open`, have the following methods:
+.. versionchanged:: 3.4
+ Added support for unseekable files.
+
.. method:: Wave_write.close()
Make sure *nframes* is correct, and close the file if it was opened by
- :mod:`wave`. This method is called upon object collection.
+ :mod:`wave`. This method is called upon object collection. It will raise
+ an exception if the output stream is not seekable and *nframes* does not
+ match the number of frames actually written.
.. method:: Wave_write.setnchannels(n)
@@ -173,8 +197,9 @@ Wave_write objects, as returned by :func:`.open`, have the following methods:
.. method:: Wave_write.setnframes(n)
- Set the number of frames to *n*. This will be changed later if more frames are
- written.
+ Set the number of frames to *n*. This will be changed later if the number
+ of frames actually written is different (this update attempt will
+ raise an error if the output stream is not seekable).
.. method:: Wave_write.setcomptype(type, name)
@@ -200,10 +225,19 @@ Wave_write objects, as returned by :func:`.open`, have the following methods:
Write audio frames, without correcting *nframes*.
+ .. versionchanged:: 3.4
+ Any :term:`bytes-like object` is now accepted.
+
.. method:: Wave_write.writeframes(data)
- Write audio frames and make sure *nframes* is correct.
+ Write audio frames and make sure *nframes* is correct. It will raise an
+ error if the output stream is not seekable and the total number of frames
+ that have been written after *data* has been written does not match the
+ previously set value for *nframes*.
+
+ .. versionchanged:: 3.4
+ Any :term:`bytes-like object` is now accepted.
Note that it is invalid to set any parameters after calling :meth:`writeframes`
diff --git a/Doc/library/weakref.rst b/Doc/library/weakref.rst
index 224f442..9ca60a9 100644
--- a/Doc/library/weakref.rst
+++ b/Doc/library/weakref.rst
@@ -51,10 +51,16 @@ garbage collection. :class:`WeakSet` implements the :class:`set` interface,
but keeps weak references to its elements, just like a
:class:`WeakKeyDictionary` does.
-Most programs should find that using one of these weak container types is all
-they need -- it's not usually necessary to create your own weak references
-directly. The low-level machinery used by the weak dictionary implementations
-is exposed by the :mod:`weakref` module for the benefit of advanced uses.
+:class:`finalize` provides a straight forward way to register a
+cleanup function to be called when an object is garbage collected.
+This is simpler to use than setting up a callback function on a raw
+weak reference, since the module automatically ensures that the finalizer
+remains alive until the object is collected.
+
+Most programs should find that using one of these weak container types
+or :class:`finalize` is all they need -- it's not usually necessary to
+create your own weak references directly. The low-level machinery is
+exposed by the :mod:`weakref` module for the benefit of advanced uses.
Not all objects can be weakly referenced; those objects which can include class
instances, functions written in Python (but not in C), instance methods, sets,
@@ -111,6 +117,15 @@ Extension types can easily be made to support weak references; see
This is a subclassable type rather than a factory function.
+ .. attribute:: __callback__
+
+ This read-only attribute returns the callback currently associated to the
+ weakref. If there is no callback or if the referent of the weakref is
+ no longer alive then this attribute will have value ``None``.
+
+ .. versionchanged:: 3.4
+ Added the :attr:`__callback__` attribute.
+
.. function:: proxy(object[, callback])
@@ -192,6 +207,98 @@ These method have the same issues as the and :meth:`keyrefs` method of
discarded when no strong reference to it exists any more.
+.. class:: WeakMethod(method)
+
+ A custom :class:`ref` subclass which simulates a weak reference to a bound
+ method (i.e., a method defined on a class and looked up on an instance).
+ Since a bound method is ephemeral, a standard weak reference cannot keep
+ hold of it. :class:`WeakMethod` has special code to recreate the bound
+ method until either the object or the original function dies::
+
+ >>> class C:
+ ... def method(self):
+ ... print("method called!")
+ ...
+ >>> c = C()
+ >>> r = weakref.ref(c.method)
+ >>> r()
+ >>> r = weakref.WeakMethod(c.method)
+ >>> r()
+ <bound method C.method of <__main__.C object at 0x7fc859830220>>
+ >>> r()()
+ method called!
+ >>> del c
+ >>> gc.collect()
+ 0
+ >>> r()
+ >>>
+
+ .. versionadded:: 3.4
+
+.. class:: finalize(obj, func, *args, **kwargs)
+
+ Return a callable finalizer object which will be called when *obj*
+ is garbage collected. Unlike an ordinary weak reference, a finalizer
+ will always survive until the reference object is collected, greatly
+ simplifying lifecycle management.
+
+ A finalizer is considered *alive* until it is called (either explicitly
+ or at garbage collection), and after that it is *dead*. Calling a live
+ finalizer returns the result of evaluating ``func(*arg, **kwargs)``,
+ whereas calling a dead finalizer returns :const:`None`.
+
+ Exceptions raised by finalizer callbacks during garbage collection
+ will be shown on the standard error output, but cannot be
+ propagated. They are handled in the same way as exceptions raised
+ from an object's :meth:`__del__` method or a weak reference's
+ callback.
+
+ When the program exits, each remaining live finalizer is called
+ unless its :attr:`atexit` attribute has been set to false. They
+ are called in reverse order of creation.
+
+ A finalizer will never invoke its callback during the later part of
+ the interpreter shutdown when module globals are liable to have
+ been replaced by :const:`None`.
+
+ .. method:: __call__()
+
+ If *self* is alive then mark it as dead and return the result of
+ calling ``func(*args, **kwargs)``. If *self* is dead then return
+ :const:`None`.
+
+ .. method:: detach()
+
+ If *self* is alive then mark it as dead and return the tuple
+ ``(obj, func, args, kwargs)``. If *self* is dead then return
+ :const:`None`.
+
+ .. method:: peek()
+
+ If *self* is alive then return the tuple ``(obj, func, args,
+ kwargs)``. If *self* is dead then return :const:`None`.
+
+ .. attribute:: alive
+
+ Property which is true if the finalizer is alive, false otherwise.
+
+ .. attribute:: atexit
+
+ A writable boolean property which by default is true. When the
+ program exits, it calls all remaining live finalizers for which
+ :attr:`.atexit` is true. They are called in reverse order of
+ creation.
+
+ .. note::
+
+ It is important to ensure that *func*, *args* and *kwargs* do
+ not own any references to *obj*, either directly or indirectly,
+ since otherwise *obj* will never be garbage collected. In
+ particular, *func* should not be a bound method of *obj*.
+
+ .. versionadded:: 3.4
+
+
.. data:: ReferenceType
The type object for weak references objects.
@@ -232,8 +339,9 @@ These method have the same issues as the and :meth:`keyrefs` method of
Weak Reference Objects
----------------------
-Weak reference objects have no attributes or methods, but do allow the referent
-to be obtained, if it still exists, by calling it:
+Weak reference objects have no methods and no attributes besides
+:attr:`ref.__callback__`. A weak reference object allows the referent to be
+obtained, if it still exists, by calling it:
>>> import weakref
>>> class Object:
@@ -326,3 +434,140 @@ objects can still be retrieved by ID if they do.
def id2obj(oid):
return _id2obj_dict[oid]
+
+.. _finalize-examples:
+
+Finalizer Objects
+-----------------
+
+The main benefit of using :class:`finalize` is that it makes it simple
+to register a callback without needing to preserve the returned finalizer
+object. For instance
+
+ >>> import weakref
+ >>> class Object:
+ ... pass
+ ...
+ >>> kenny = Object()
+ >>> weakref.finalize(kenny, print, "You killed Kenny!") #doctest:+ELLIPSIS
+ <finalize object at ...; for 'Object' at ...>
+ >>> del kenny
+ You killed Kenny!
+
+The finalizer can be called directly as well. However the finalizer
+will invoke the callback at most once.
+
+ >>> def callback(x, y, z):
+ ... print("CALLBACK")
+ ... return x + y + z
+ ...
+ >>> obj = Object()
+ >>> f = weakref.finalize(obj, callback, 1, 2, z=3)
+ >>> assert f.alive
+ >>> assert f() == 6
+ CALLBACK
+ >>> assert not f.alive
+ >>> f() # callback not called because finalizer dead
+ >>> del obj # callback not called because finalizer dead
+
+You can unregister a finalizer using its :meth:`~finalize.detach`
+method. This kills the finalizer and returns the arguments passed to
+the constructor when it was created.
+
+ >>> obj = Object()
+ >>> f = weakref.finalize(obj, callback, 1, 2, z=3)
+ >>> f.detach() #doctest:+ELLIPSIS
+ (<__main__.Object object ...>, <function callback ...>, (1, 2), {'z': 3})
+ >>> newobj, func, args, kwargs = _
+ >>> assert not f.alive
+ >>> assert newobj is obj
+ >>> assert func(*args, **kwargs) == 6
+ CALLBACK
+
+Unless you set the :attr:`~finalize.atexit` attribute to
+:const:`False`, a finalizer will be called when the program exits if it
+is still alive. For instance
+
+ >>> obj = Object()
+ >>> weakref.finalize(obj, print, "obj dead or exiting") #doctest:+ELLIPSIS
+ <finalize object at ...; for 'Object' at ...>
+ >>> exit() #doctest:+SKIP
+ obj dead or exiting
+
+
+Comparing finalizers with :meth:`__del__` methods
+-------------------------------------------------
+
+Suppose we want to create a class whose instances represent temporary
+directories. The directories should be deleted with their contents
+when the first of the following events occurs:
+
+* the object is garbage collected,
+* the object's :meth:`remove` method is called, or
+* the program exits.
+
+We might try to implement the class using a :meth:`__del__` method as
+follows::
+
+ class TempDir:
+ def __init__(self):
+ self.name = tempfile.mkdtemp()
+
+ def remove(self):
+ if self.name is not None:
+ shutil.rmtree(self.name)
+ self.name = None
+
+ @property
+ def removed(self):
+ return self.name is None
+
+ def __del__(self):
+ self.remove()
+
+Starting with Python 3.4, :meth:`__del__` methods no longer prevent
+reference cycles from being garbage collected, and module globals are
+no longer forced to :const:`None` during interpreter shutdown. So this
+code should work without any issues on CPython.
+
+However, handling of :meth:`__del__` methods is notoriously implementation
+specific, since it depends on internal details of the interpreter's garbage
+collector implementation.
+
+A more robust alternative can be to define a finalizer which only references
+the specific functions and objects that it needs, rather than having access
+to the full state of the object::
+
+ class TempDir:
+ def __init__(self):
+ self.name = tempfile.mkdtemp()
+ self._finalizer = weakref.finalize(self, shutil.rmtree, self.name)
+
+ def remove(self):
+ self._finalizer()
+
+ @property
+ def removed(self):
+ return not self._finalizer.alive
+
+Defined like this, our finalizer only receives a reference to the details
+it needs to clean up the directory appropriately. If the object never gets
+garbage collected the finalizer will still be called at exit.
+
+The other advantage of weakref based finalizers is that they can be used to
+register finalizers for classes where the definition is controlled by a
+third party, such as running code when a module is unloaded::
+
+ import weakref, sys
+ def unloading_module():
+ # implicit reference to the module globals from the function body
+ weakref.finalize(sys.modules[__name__], unloading_module)
+
+
+.. note::
+
+ If you create a finalizer object in a daemonic thread just as the
+ the program exits then there is the possibility that the finalizer
+ does not get called at exit. However, in a daemonic thread
+ :func:`atexit.register`, ``try: ... finally: ...`` and ``with: ...``
+ do not guarantee that cleanup occurs either.
diff --git a/Doc/library/webbrowser.rst b/Doc/library/webbrowser.rst
index 9c2b3ab..ef63769 100644
--- a/Doc/library/webbrowser.rst
+++ b/Doc/library/webbrowser.rst
@@ -19,12 +19,12 @@ will be used if graphical browsers are not available or an X11 display isn't
available. If text-mode browsers are used, the calling process will block until
the user exits the browser.
-If the environment variable :envvar:`BROWSER` exists, it is interpreted to
-override the platform default list of browsers, as a :data:`os.pathsep`-separated
-list of browsers to try in order. When the value of a list part contains the
-string ``%s``, then it is interpreted as a literal browser command line to be
-used with the argument URL substituted for ``%s``; if the part does not contain
-``%s``, it is simply interpreted as the name of the browser to launch. [1]_
+If the environment variable :envvar:`BROWSER` exists, it is interpreted as the
+:data:`os.pathsep`-separated list of browsers to try ahead of the the platform
+defaults. When the value of a list part contains the string ``%s``, then it is
+interpreted as a literal browser command line to be used with the argument URL
+substituted for ``%s``; if the part does not contain ``%s``, it is simply
+interpreted as the name of the browser to launch. [1]_
For non-Unix platforms, or when a remote browser is available on Unix, the
controlling process will not wait for the user to finish with the browser, but
diff --git a/Doc/library/xml.etree.elementtree.rst b/Doc/library/xml.etree.elementtree.rst
index 3f2dcbb..fc0b79e 100644
--- a/Doc/library/xml.etree.elementtree.rst
+++ b/Doc/library/xml.etree.elementtree.rst
@@ -105,6 +105,59 @@ Children are nested, and we can access specific child nodes by index::
>>> root[0][1].text
'2008'
+
+.. note::
+
+ Not all elements of the XML input will end up as elements of the
+ parsed tree. Currently, this module skips over any XML comments,
+ processing instructions, and document type declarations in the
+ input. Nevertheless, trees built using this module's API rather
+ than parsing from XML text can have comments and processing
+ instructions in them; they will be included when generating XML
+ output. A document type declaration may be accessed by passing a
+ custom :class:`TreeBuilder` instance to the :class:`XMLParser`
+ constructor.
+
+
+.. _elementtree-pull-parsing:
+
+Pull API for non-blocking parsing
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Most parsing functions provided by this module require the whole document
+to be read at once before returning any result. It is possible to use an
+:class:`XMLParser` and feed data into it incrementally, but it is a push API that
+calls methods on a callback target, which is too low-level and inconvenient for
+most needs. Sometimes what the user really wants is to be able to parse XML
+incrementally, without blocking operations, while enjoying the convenience of
+fully constructed :class:`Element` objects.
+
+The most powerful tool for doing this is :class:`XMLPullParser`. It does not
+require a blocking read to obtain the XML data, and is instead fed with data
+incrementally with :meth:`XMLPullParser.feed` calls. To get the parsed XML
+elements, call :meth:`XMLPullParser.read_events`. Here is an example::
+
+ >>> parser = ET.XMLPullParser(['start', 'end'])
+ >>> parser.feed('<mytag>sometext')
+ >>> list(parser.read_events())
+ [('start', <Element 'mytag' at 0x7fa66db2be58>)]
+ >>> parser.feed(' more text</mytag>')
+ >>> for event, elem in parser.read_events():
+ ... print(event)
+ ... print(elem.tag, 'text=', elem.text)
+ ...
+ end
+
+The obvious use case is applications that operate in a non-blocking fashion
+where the XML data is being received from a socket or read incrementally from
+some storage device. In such cases, blocking reads are unacceptable.
+
+Because it's so flexible, :class:`XMLPullParser` can be inconvenient to use for
+simpler use-cases. If you don't mind your application blocking on reading XML
+data but would still like to have incremental parsing capabilities, take a look
+at :func:`iterparse`. It can be useful when you're reading a large XML document
+and don't want to hold it wholly in memory.
+
Finding interesting elements
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -285,7 +338,7 @@ Supported XPath syntax
+=======================+======================================================+
| ``tag`` | Selects all child elements with the given tag. |
| | For example, ``spam`` selects all child elements |
-| | named ``spam``, ``spam/egg`` selects all |
+| | named ``spam``, and ``spam/egg`` selects all |
| | grandchildren named ``egg`` in all children named |
| | ``spam``. |
+-----------------------+------------------------------------------------------+
@@ -341,6 +394,10 @@ Functions
string containing the comment string. Returns an element instance
representing a comment.
+ Note that :class:`XMLParser` skips over comments in the input
+ instead of creating comment objects for them. An :class:`ElementTree` will
+ only contain comment nodes if they have been inserted into to
+ the tree using one of the :class:`Element` methods.
.. function:: dump(elem)
@@ -379,25 +436,32 @@ Functions
Parses an XML section into an element tree incrementally, and reports what's
going on to the user. *source* is a filename or :term:`file object`
- containing XML data. *events* is a tuple of events to report back. The
- supported events are the strings ``"start"``, ``"end"``, ``"start-ns"``
- and ``"end-ns"`` (the "ns" events are used to get detailed namespace
+ containing XML data. *events* is a sequence of events to report back. The
+ supported events are the strings ``"start"``, ``"end"``, ``"start-ns"`` and
+ ``"end-ns"`` (the "ns" events are used to get detailed namespace
information). If *events* is omitted, only ``"end"`` events are reported.
*parser* is an optional parser instance. If not given, the standard
- :class:`XMLParser` parser is used. *parser* can only use the default
- :class:`TreeBuilder` as a target. Returns an :term:`iterator` providing
- ``(event, elem)`` pairs.
+ :class:`XMLParser` parser is used. *parser* must be a subclass of
+ :class:`XMLParser` and can only use the default :class:`TreeBuilder` as a
+ target. Returns an :term:`iterator` providing ``(event, elem)`` pairs.
+
+ Note that while :func:`iterparse` builds the tree incrementally, it issues
+ blocking reads on *source* (or the file it names). As such, it's unsuitable
+ for applications where blocking reads can't be made. For fully non-blocking
+ parsing, see :class:`XMLPullParser`.
.. note::
- :func:`iterparse` only guarantees that it has seen the ">"
- character of a starting tag when it emits a "start" event, so the
- attributes are defined, but the contents of the text and tail attributes
- are undefined at that point. The same applies to the element children;
- they may or may not be present.
+ :func:`iterparse` only guarantees that it has seen the ">" character of a
+ starting tag when it emits a "start" event, so the attributes are defined,
+ but the contents of the text and tail attributes are undefined at that
+ point. The same applies to the element children; they may or may not be
+ present.
If you need a fully populated element, look for "end" events instead.
+ .. deprecated:: 3.4
+ The *parser* argument.
.. function:: parse(source, parser=None)
@@ -414,6 +478,11 @@ Functions
containing the PI target. *text* is a string containing the PI contents, if
given. Returns an element instance, representing a processing instruction.
+ Note that :class:`XMLParser` skips over processing instructions
+ in the input instead of creating comment objects for them. An
+ :class:`ElementTree` will only contain processing instruction nodes if
+ they have been inserted into to the tree using one of the
+ :class:`Element` methods.
.. function:: register_namespace(prefix, uri)
@@ -438,29 +507,39 @@ Functions
arguments. Returns an element instance.
-.. function:: tostring(element, encoding="us-ascii", method="xml")
+.. function:: tostring(element, encoding="us-ascii", method="xml", *, \
+ short_empty_elements=True)
Generates a string representation of an XML element, including all
subelements. *element* is an :class:`Element` instance. *encoding* [1]_ is
the output encoding (default is US-ASCII). Use ``encoding="unicode"`` to
generate a Unicode string (otherwise, a bytestring is generated). *method*
is either ``"xml"``, ``"html"`` or ``"text"`` (default is ``"xml"``).
+ *short_empty_elements* has the same meaning as in :meth:`ElementTree.write`.
Returns an (optionally) encoded string containing the XML data.
+ .. versionadded:: 3.4
+ The *short_empty_elements* parameter.
-.. function:: tostringlist(element, encoding="us-ascii", method="xml")
+
+.. function:: tostringlist(element, encoding="us-ascii", method="xml", *, \
+ short_empty_elements=True)
Generates a string representation of an XML element, including all
subelements. *element* is an :class:`Element` instance. *encoding* [1]_ is
the output encoding (default is US-ASCII). Use ``encoding="unicode"`` to
generate a Unicode string (otherwise, a bytestring is generated). *method*
is either ``"xml"``, ``"html"`` or ``"text"`` (default is ``"xml"``).
+ *short_empty_elements* has the same meaning as in :meth:`ElementTree.write`.
Returns a list of (optionally) encoded strings containing the XML data.
It does not guarantee any specific sequence, except that
``b"".join(tostringlist(element)) == tostring(element)``.
.. versionadded:: 3.2
+ .. versionadded:: 3.4
+ The *short_empty_elements* parameter.
+
.. function:: XML(text, parser=None)
@@ -753,7 +832,8 @@ ElementTree Objects
.. method:: write(file, encoding="us-ascii", xml_declaration=None, \
- default_namespace=None, method="xml")
+ default_namespace=None, method="xml", *, \
+ short_empty_elements=True)
Writes the element tree to a file, as XML. *file* is a file name, or a
:term:`file object` opened for writing. *encoding* [1]_ is the output
@@ -764,6 +844,10 @@ ElementTree Objects
*default_namespace* sets the default XML namespace (for "xmlns").
*method* is either ``"xml"``, ``"html"`` or ``"text"`` (default is
``"xml"``).
+ The keyword-only *short_empty_elements* parameter controls the formatting
+ of elements that contain no content. If *True* (the default), they are
+ emitted as a single self-closed tag, otherwise they are emitted as a pair
+ of start/end tags.
The output is either a string (:class:`str`) or binary (:class:`bytes`).
This is controlled by the *encoding* argument. If *encoding* is
@@ -772,6 +856,9 @@ ElementTree Objects
:term:`file object`; make sure you do not try to write a string to a
binary stream and vice versa.
+ .. versionadded:: 3.4
+ The *short_empty_elements* parameter.
+
This is the XML file that is going to be manipulated::
@@ -817,6 +904,7 @@ QName Objects
:class:`QName` instances are opaque.
+
.. _elementtree-treebuilder-objects:
TreeBuilder Objects
@@ -876,13 +964,18 @@ XMLParser Objects
.. class:: XMLParser(html=0, target=None, encoding=None)
- :class:`Element` structure builder for XML source data, based on the expat
- parser. *html* are predefined HTML entities. This flag is not supported by
- the current implementation. *target* is the target object. If omitted, the
- builder uses an instance of the standard :class:`TreeBuilder` class.
- *encoding* [1]_ is optional. If given, the value overrides the encoding
+ This class is the low-level building block of the module. It uses
+ :mod:`xml.parsers.expat` for efficient, event-based parsing of XML. It can
+ be fed XML data incrementall with the :meth:`feed` method, and parsing events
+ are translated to a push API - by invoking callbacks on the *target* object.
+ If *target* is omitted, the standard :class:`TreeBuilder` is used. The
+ *html* argument was historically used for backwards compatibility and is now
+ deprecated. If *encoding* [1]_ is given, the value overrides the encoding
specified in the XML file.
+ .. deprecated:: 3.4
+ The *html* argument. The remaining arguments should be passed via
+ keywword to prepare for the removal of the *html* argument.
.. method:: close()
@@ -902,12 +995,12 @@ XMLParser Objects
Feeds data to the parser. *data* is encoded data.
- :meth:`XMLParser.feed` calls *target*\'s ``start()`` method
- for each opening tag, its ``end()`` method for each closing tag,
- and data is processed by method ``data()``. :meth:`XMLParser.close`
- calls *target*\'s method ``close()``.
- :class:`XMLParser` can be used not only for building a tree structure.
- This is an example of counting the maximum depth of an XML file::
+ :meth:`XMLParser.feed` calls *target*\'s ``start(tag, attrs_dict)`` method
+ for each opening tag, its ``end(tag)`` method for each closing tag, and data
+ is processed by method ``data(data)``. :meth:`XMLParser.close` calls
+ *target*\'s method ``close()``. :class:`XMLParser` can be used not only for
+ building a tree structure. This is an example of counting the maximum depth
+ of an XML file::
>>> from xml.etree.ElementTree import XMLParser
>>> class MaxDepth: # The target object of the parser
@@ -941,6 +1034,60 @@ XMLParser Objects
>>> parser.close()
4
+
+.. _elementtree-xmlpullparser-objects:
+
+XMLPullParser Objects
+^^^^^^^^^^^^^^^^^^^^^
+
+.. class:: XMLPullParser(events=None)
+
+ A pull parser suitable for non-blocking applications. Its input-side API is
+ similar to that of :class:`XMLParser`, but instead of pushing calls to a
+ callback target, :class:`XMLPullParser` collects an internal list of parsing
+ events and lets the user read from it. *events* is a sequence of events to
+ report back. The supported events are the strings ``"start"``, ``"end"``,
+ ``"start-ns"`` and ``"end-ns"`` (the "ns" events are used to get detailed
+ namespace information). If *events* is omitted, only ``"end"`` events are
+ reported.
+
+ .. method:: feed(data)
+
+ Feed the given bytes data to the parser.
+
+ .. method:: close()
+
+ Signal the parser that the data stream is terminated. Unlike
+ :meth:`XMLParser.close`, this method always returns :const:`None`.
+ Any events not yet retrieved when the parser is closed can still be
+ read with :meth:`read_events`.
+
+ .. method:: read_events()
+
+ Return an iterator over the events which have been encountered in the
+ data fed to the
+ parser. The iterator yields ``(event, elem)`` pairs, where *event* is a
+ string representing the type of event (e.g. ``"end"``) and *elem* is the
+ encountered :class:`Element` object.
+
+ Events provided in a previous call to :meth:`read_events` will not be
+ yielded again. Events are consumed from the internal queue only when
+ they are retrieved from the iterator, so multiple readers iterating in
+ parallel over iterators obtained from :meth:`read_events` will have
+ unpredictable results.
+
+ .. note::
+
+ :class:`XMLPullParser` only guarantees that it has seen the ">"
+ character of a starting tag when it emits a "start" event, so the
+ attributes are defined, but the contents of the text and tail attributes
+ are undefined at that point. The same applies to the element children;
+ they may or may not be present.
+
+ If you need a fully populated element, look for "end" events instead.
+
+ .. versionadded:: 3.4
+
Exceptions
^^^^^^^^^^
diff --git a/Doc/library/zipfile.rst b/Doc/library/zipfile.rst
index 7a6482b..1d23a7c 100644
--- a/Doc/library/zipfile.rst
+++ b/Doc/library/zipfile.rst
@@ -130,7 +130,7 @@ ZipFile Objects
---------------
-.. class:: ZipFile(file, mode='r', compression=ZIP_STORED, allowZip64=False)
+.. class:: ZipFile(file, mode='r', compression=ZIP_STORED, allowZip64=True)
Open a ZIP file, where *file* can be either a path to a file (a string) or a
file-like object. The *mode* parameter should be ``'r'`` to read an existing
@@ -144,15 +144,12 @@ ZipFile Objects
and should be :const:`ZIP_STORED`, :const:`ZIP_DEFLATED`,
:const:`ZIP_BZIP2` or :const:`ZIP_LZMA`; unrecognized
values will cause :exc:`RuntimeError` to be raised. If :const:`ZIP_DEFLATED`,
- :const:`ZIP_BZIP2` or :const:`ZIP_LZMA` is specified but the corresponded module
+ :const:`ZIP_BZIP2` or :const:`ZIP_LZMA` is specified but the corresponding module
(:mod:`zlib`, :mod:`bz2` or :mod:`lzma`) is not available, :exc:`RuntimeError`
is also raised. The default is :const:`ZIP_STORED`. If *allowZip64* is
- ``True`` zipfile will create ZIP files that use the ZIP64 extensions when
- the zipfile is larger than 2 GiB. If it is false (the default) :mod:`zipfile`
+ ``True`` (the default) zipfile will create ZIP files that use the ZIP64
+ extensions when the zipfile is larger than 2 GiB. If it is false :mod:`zipfile`
will raise an exception when the ZIP file would require ZIP64 extensions.
- ZIP64 extensions are disabled by default because the default :program:`zip`
- and :program:`unzip` commands on Unix (the InfoZIP utilities) don't support
- these extensions.
If the file is created with mode ``'a'`` or ``'w'`` and then
:meth:`closed <close>` without adding any files to the archive, the appropriate
@@ -171,6 +168,9 @@ ZipFile Objects
.. versionchanged:: 3.3
Added support for :mod:`bzip2 <bz2>` and :mod:`lzma` compression.
+ .. versionchanged:: 3.4
+ ZIP64 extensions are enabled by default.
+
.. method:: ZipFile.close()
@@ -234,6 +234,9 @@ ZipFile Objects
or a :class:`ZipInfo` object. You will appreciate this when trying to read a
ZIP file that contains members with duplicate names.
+ .. deprecated-removed:: 3.4 3.6
+ The ``'U'`` or ``'rU'`` mode. Use :class:`io.TextIOWrapper` for reading
+ compressed text files in :term:`universal newlines` mode.
.. method:: ZipFile.extract(member, path=None, pwd=None)
@@ -266,10 +269,8 @@ ZipFile Objects
Never extract archives from untrusted sources without prior inspection.
It is possible that files are created outside of *path*, e.g. members
that have absolute filenames starting with ``"/"`` or filenames with two
- dots ``".."``.
-
- .. versionchanged:: 3.3.1
- The zipfile module attempts to prevent that. See :meth:`extract` note.
+ dots ``".."``. This module attempts to prevent that.
+ See :meth:`extract` note.
.. method:: ZipFile.printdir()
@@ -376,15 +377,18 @@ PyZipFile Objects
The :class:`PyZipFile` constructor takes the same parameters as the
:class:`ZipFile` constructor, and one additional parameter, *optimize*.
-.. class:: PyZipFile(file, mode='r', compression=ZIP_STORED, allowZip64=False, \
+.. class:: PyZipFile(file, mode='r', compression=ZIP_STORED, allowZip64=True, \
optimize=-1)
.. versionadded:: 3.2
The *optimize* parameter.
+ .. versionchanged:: 3.4
+ ZIP64 extensions are enabled by default.
+
Instances have one method in addition to those of :class:`ZipFile` objects:
- .. method:: PyZipFile.writepy(pathname, basename='')
+ .. method:: PyZipFile.writepy(pathname, basename='', filterfunc=None)
Search for files :file:`\*.py` and add the corresponding file to the
archive.
@@ -397,16 +401,33 @@ The :class:`PyZipFile` constructor takes the same parameters as the
``2``, only files with that optimization level (see :func:`compile`) are
added to the archive, compiling if necessary.
- If the pathname is a file, the filename must end with :file:`.py`, and
+ If *pathname* is a file, the filename must end with :file:`.py`, and
just the (corresponding :file:`\*.py[co]`) file is added at the top level
- (no path information). If the pathname is a file that does not end with
+ (no path information). If *pathname* is a file that does not end with
:file:`.py`, a :exc:`RuntimeError` will be raised. If it is a directory,
and the directory is not a package directory, then all the files
:file:`\*.py[co]` are added at the top level. If the directory is a
package directory, then all :file:`\*.py[co]` are added under the package
name as a file path, and if any subdirectories are package directories,
- all of these are added recursively. *basename* is intended for internal
- use only. The :meth:`writepy` method makes archives with file names like
+ all of these are added recursively.
+
+ *basename* is intended for internal use only.
+
+ *filterfunc*, if given, must be a function taking a single string
+ argument. It will be passed each path (including each individual full
+ file path) before it is added to the archive. If *filterfunc* returns a
+ false value, the path will not be added, and if it is a directory its
+ contents will be ignored. For example, if our test files are all either
+ in ``test`` directories or start with the string ``test_``, we can use a
+ *filterfunc* to exclude them::
+
+ >>> zf = PyZipFile('myprog.zip')
+ >>> def notests(s):
+ ... fn = os.path.basename(s)
+ ... return (not (fn == 'test' or fn.startswith('test_')))
+ >>> zf.writepy('myprog', filterfunc=notests)
+
+ The :meth:`writepy` method makes archives with file names like
this::
string.pyc # Top level name
@@ -415,6 +436,9 @@ The :class:`PyZipFile` constructor takes the same parameters as the
test/bogus/__init__.pyc # Subpackage directory
test/bogus/myfile.pyc # Submodule test.bogus.myfile
+ .. versionadded:: 3.4
+ The *filterfunc* parameter.
+
.. _zipinfo-objects:
diff --git a/Doc/license.rst b/Doc/license.rst
index 4440c7b..6190fe0 100644
--- a/Doc/license.rst
+++ b/Doc/license.rst
@@ -84,9 +84,9 @@ Terms and conditions for accessing or otherwise using Python
analyze, test, perform and/or display publicly, prepare derivative works,
distribute, and otherwise use Python |release| alone or in any derivative
version, provided, however, that PSF's License Agreement and PSF's notice of
- copyright, i.e., "Copyright © 2001-2014 Python Software Foundation; All Rights
- Reserved" are retained in Python |release| alone or in any derivative version
- prepared by Licensee.
+ copyright, i.e., "Copyright © 2001-2014 Python Software Foundation; All
+ Rights Reserved" are retained in Python |release| alone or in any derivative
+ version prepared by Licensee.
#. In the event Licensee prepares a derivative work that is based on or
incorporates Python |release| or any part thereof, and wants to make the
@@ -590,6 +590,35 @@ The :mod:`select` and contains the following notice for the kqueue interface::
SUCH DAMAGE.
+SipHash24
+---------
+
+The file :file:`Python/pyhash.c` contains Marek Majkowski' implementation of
+Dan Bernstein's SipHash24 algorithm. The contains the following note::
+
+ <MIT License>
+ Copyright (c) 2013 Marek Majkowski <marek@popcount.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+ </MIT License>
+
+ Original location:
+ https://github.com/majek/csiphash/
+
+ Solution inspired by code from:
+ Samuel Neves (supercop/crypto_auth/siphash24/little)
+ djb (supercop/crypto_auth/siphash24/little2)
+ Jean-Philippe Aumasson (https://131002.net/siphash/siphash24.c)
+
+
strtod and dtoa
---------------
@@ -846,6 +875,47 @@ used for the build::
jloup@gzip.org madler@alumni.caltech.edu
+cfuhash
+-------
+
+The implementation of the hash table used by the :mod:`tracemalloc` is based
+on the cfuhash project::
+
+ Copyright (c) 2005 Don Owens
+ All rights reserved.
+
+ This code is released under the BSD license:
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the name of the author nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
libmpdec
--------
diff --git a/Doc/make.bat b/Doc/make.bat
index d6f7074..6f74cb0 100644
--- a/Doc/make.bat
+++ b/Doc/make.bat
@@ -1,59 +1,115 @@
-@@echo off
+@echo off
setlocal
-set SVNROOT=http://svn.python.org/projects
-if "%PYTHON%" EQU "" set PYTHON=py -2
-if "%HTMLHELP%" EQU "" set HTMLHELP=%ProgramFiles%\HTML Help Workshop\hhc.exe
+pushd %~dp0
+
+set this=%~n0
+
+if "%SPHINXBUILD%" EQU "" set SPHINXBUILD=sphinx-build
+if "%PYTHON%" EQU "" set PYTHON=py
+
+if DEFINED ProgramFiles(x86) set _PRGMFLS=%ProgramFiles(x86)%
+if NOT DEFINED ProgramFiles(x86) set _PRGMFLS=%ProgramFiles%
+if "%HTMLHELP%" EQU "" set HTMLHELP=%_PRGMFLS%\HTML Help Workshop\hhc.exe
+
if "%DISTVERSION%" EQU "" for /f "usebackq" %%v in (`%PYTHON% tools/sphinxext/patchlevel.py`) do set DISTVERSION=%%v
+if "%BUILDDIR%" EQU "" set BUILDDIR=build
+
+rem Targets that don't require sphinx-build
if "%1" EQU "" goto help
-if "%1" EQU "html" goto build
-if "%1" EQU "htmlhelp" goto build
-if "%1" EQU "latex" goto build
-if "%1" EQU "text" goto build
-if "%1" EQU "suspicious" goto build
-if "%1" EQU "linkcheck" goto build
-if "%1" EQU "changes" goto build
-if "%1" EQU "checkout" goto checkout
-if "%1" EQU "update" goto update
+if "%1" EQU "help" goto help
+if "%1" EQU "check" goto check
+if "%1" EQU "serve" goto serve
+if "%1" == "clean" (
+ rmdir /q /s %BUILDDIR%
+ goto end
+)
+
+%SPHINXBUILD% 2> nul
+if errorlevel 9009 (
+ echo.
+ echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+ echo.installed, then set the SPHINXBUILD environment variable to point
+ echo.to the full path of the 'sphinx-build' executable. Alternatively you
+ echo.may add the Sphinx directory to PATH.
+ echo.
+ echo.If you don't have Sphinx installed, grab it from
+ echo.http://sphinx-doc.org/
+ goto end
+)
+
+rem Targets that do require sphinx-build and have their own label
+if "%1" EQU "htmlview" goto htmlview
+
+rem Everything else
+goto build
:help
-set this=%~n0
-echo HELP
+echo.usage: %this% BUILDER [filename ...]
+echo.
+echo.Call %this% with the desired Sphinx builder as the first argument, e.g.
+echo.``%this% html`` or ``%this% doctest``. Interesting targets that are
+echo.always available include:
echo.
-echo %this% checkout
-echo %this% update
-echo %this% html
-echo %this% htmlhelp
-echo %this% latex
-echo %this% text
-echo %this% suspicious
-echo %this% linkcheck
-echo %this% changes
+echo. Provided by Sphinx:
+echo. html, htmlhelp, latex, text
+echo. suspicious, linkcheck, changes, doctest
+echo. Provided by this script:
+echo. clean, check, serve, htmlview
echo.
+echo.All arguments past the first one are passed through to sphinx-build as
+echo.filenames to build or are ignored. See README.txt in this directory or
+echo.the documentation for your version of Sphinx for more exhaustive lists
+echo.of available targets and descriptions of each.
+echo.
+echo.This script assumes that the SPHINXBUILD environment variable contains
+echo.a legitimate command for calling sphinx-build, or that sphinx-build is
+echo.on your PATH if SPHINXBUILD is not set. Options for sphinx-build can
+echo.be passed by setting the SPHINXOPTS environment variable.
+goto end
+
+:build
+if NOT "%PAPER%" == "" (
+ set SPHINXOPTS=-D latex_paper_size=%PAPER% %SPHINXOPTS%
+)
+cmd /C %SPHINXBUILD% %SPHINXOPTS% -b%1 -dbuild\doctrees . %BUILDDIR%\%*
+
+if "%1" EQU "htmlhelp" (
+ cmd /C "%HTMLHELP%" build\htmlhelp\python%DISTVERSION:.=%.hhp
+ rem hhc.exe seems to always exit with code 1, reset to 0 for less than 2
+ if not errorlevel 2 cmd /C exit /b 0
+)
+
+echo.
+if errorlevel 1 (
+ echo.Build failed (exit code %ERRORLEVEL%^), check for error messages
+ echo.above. Any output will be found in %BUILDDIR%\%1
+) else (
+ echo.Build succeeded. All output should be in %BUILDDIR%\%1
+)
goto end
-:checkout
-svn co %SVNROOT%/external/Sphinx-1.2/sphinx tools/sphinx
-svn co %SVNROOT%/external/docutils-0.11/docutils tools/docutils
-svn co %SVNROOT%/external/Jinja-2.3.1/jinja2 tools/jinja2
-svn co %SVNROOT%/external/Pygments-1.6/pygments tools/pygments
+:htmlview
+if NOT "%2" EQU "" (
+ echo.Can't specify filenames to build with htmlview target, ignoring.
+)
+cmd /C %this% html
+
+if EXIST %BUILDDIR%\html\index.html (
+ echo.Opening %BUILDDIR%\html\index.html in the default web browser...
+ start %BUILDDIR%\html\index.html
+)
+
goto end
-:update
-svn update tools/sphinx
-svn update tools/docutils
-svn update tools/jinja2
-svn update tools/pygments
+:check
+cmd /C %PYTHON% tools\rstlint.py -i tools
goto end
-:build
-if not exist build mkdir build
-if not exist build\%1 mkdir build\%1
-if not exist build\doctrees mkdir build\doctrees
-cmd /C %PYTHON% --version
-cmd /C %PYTHON% tools\sphinx-build.py -b%1 -dbuild\doctrees . build\%*
-if "%1" EQU "htmlhelp" "%HTMLHELP%" build\htmlhelp\python%DISTVERSION:.=%.hhp
+:serve
+cmd /C %PYTHON% ..\Tools\scripts\serve.py %BUILDDIR%\html
goto end
:end
+popd
diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst
index 8afc69e..5e093cc 100644
--- a/Doc/reference/compound_stmts.rst
+++ b/Doc/reference/compound_stmts.rst
@@ -313,14 +313,14 @@ exception, the saved exception is set as the context of the new exception.
If the :keyword:`finally` clause executes a :keyword:`return` or :keyword:`break`
statement, the saved exception is discarded::
- def f():
- try:
- 1/0
- finally:
- return 42
-
- >>> f()
- 42
+ >>> def f():
+ ... try:
+ ... 1/0
+ ... finally:
+ ... return 42
+ ...
+ >>> f()
+ 42
The exception information is not available to the program during execution of
the :keyword:`finally` clause.
@@ -337,6 +337,20 @@ statement, the :keyword:`finally` clause is also executed 'on the way out.' A
reason is a problem with the current implementation --- this restriction may be
lifted in the future).
+The return value of a function is determined by the last :keyword:`return`
+statement executed. Since the :keyword:`finally` clause always executes, a
+:keyword:`return` statement executed in the :keyword:`finally` clause will
+always be the last one executed::
+
+ >>> def foo():
+ ... try:
+ ... return 'try'
+ ... finally:
+ ... return 'finally'
+ ...
+ >>> foo()
+ 'finally'
+
Additional information on exceptions can be found in section :ref:`exceptions`,
and information on using the :keyword:`raise` statement to generate exceptions
may be found in section :ref:`raise`.
@@ -348,7 +362,9 @@ may be found in section :ref:`raise`.
The :keyword:`with` statement
=============================
-.. index:: statement: with
+.. index::
+ statement: with
+ single: as; with statement
The :keyword:`with` statement is used to wrap the execution of a block with
methods defined by a context manager (see section :ref:`context-managers`).
@@ -493,14 +509,15 @@ case the parameter's default value is substituted. If a parameter has a default
value, all following parameters up until the "``*``" must also have a default
value --- this is a syntactic restriction that is not expressed by the grammar.
-**Default parameter values are evaluated when the function definition is
-executed.** This means that the expression is evaluated once, when the function
-is defined, and that the same "pre-computed" value is used for each call. This
-is especially important to understand when a default parameter is a mutable
-object, such as a list or a dictionary: if the function modifies the object
-(e.g. by appending an item to a list), the default value is in effect modified.
-This is generally not what was intended. A way around this is to use ``None``
-as the default, and explicitly test for it in the body of the function, e.g.::
+**Default parameter values are evaluated from left to right when the function
+definition is executed.** This means that the expression is evaluated once, when
+the function is defined, and that the same "pre-computed" value is used for each
+call. This is especially important to understand when a default parameter is a
+mutable object, such as a list or a dictionary: if the function modifies the
+object (e.g. by appending an item to a list), the default value is in effect
+modified. This is generally not what was intended. A way around this is to use
+``None`` as the default, and explicitly test for it in the body of the function,
+e.g.::
def whats_on_the_telly(penguin=None):
if penguin is None:
diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst
index 160af30..ccaa4f7 100644
--- a/Doc/reference/datamodel.rst
+++ b/Doc/reference/datamodel.rst
@@ -934,6 +934,20 @@ Internal types
frame). A debugger can implement a Jump command (aka Set Next Statement)
by writing to f_lineno.
+ Frame objects support one method:
+
+ .. method:: frame.clear()
+
+ This method clears all references to local variables held by the
+ frame. Also, if the frame belonged to a generator, the generator
+ is finalized. This helps break reference cycles involving frame
+ objects (for example when catching an exception and storing its
+ traceback for later use).
+
+ :exc:`RuntimeError` is raised if the frame is currently executing.
+
+ .. versionadded:: 3.4
+
Traceback objects
.. index::
object: traceback
@@ -1121,12 +1135,10 @@ Basic customization
``sys.last_traceback`` keeps the stack frame alive). The first situation
can only be remedied by explicitly breaking the cycles; the latter two
situations can be resolved by storing ``None`` in ``sys.last_traceback``.
- Circular references which are garbage are detected when the option cycle
- detector is enabled (it's on by default), but can only be cleaned up if
- there are no Python- level :meth:`__del__` methods involved. Refer to the
- documentation for the :mod:`gc` module for more information about how
- :meth:`__del__` methods are handled by the cycle detector, particularly
- the description of the ``garbage`` value.
+ Circular references which are garbage are detected and cleaned up when
+ the cyclic garbage collector is enabled (it's on by default). Refer to the
+ documentation for the :mod:`gc` module for more information about this
+ topic.
.. warning::
@@ -1214,6 +1226,10 @@ Basic customization
The return value must be a string object.
+ .. versionchanged:: 3.4
+ The __format__ method of ``object`` itself raises a :exc:`TypeError`
+ if passed any non-empty string.
+
.. _richcmpfuncs:
.. method:: object.__lt__(self, other)
@@ -1451,6 +1467,14 @@ class' :attr:`__dict__`.
Called to delete the attribute on an instance *instance* of the owner class.
+The attribute :attr:`__objclass__` is interpreted by the :mod:`inspect` module
+as specifying the class where this object was defined (setting this
+appropriately can assist in runtime introspection of dynamic class attributes).
+For callables, it may indicate that an instance of the given type (or a
+subclass) is expected or required as the first positional argument (for example,
+CPython sets this attribute for unbound methods that are implemented in C).
+
+
.. _descriptor-invocation:
Invoking Descriptors
@@ -1631,6 +1655,8 @@ of these candidate metaclasses. If none of the candidate metaclasses meets
that criterion, then the class definition will fail with ``TypeError``.
+.. _prepare:
+
Preparing the class namespace
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -1830,6 +1856,15 @@ through the container; for mappings, :meth:`__iter__` should be the same as
considered to be false in a Boolean context.
+.. method:: object.__length_hint__(self)
+
+ Called to implement :func:`operator.length_hint`. Should return an estimated
+ length for the object (which may be greater or less than the actual length).
+ The length must be an integer ``>=`` 0. This method is purely an
+ optimization and is never required for correctness.
+
+ .. versionadded:: 3.4
+
.. note::
Slicing is done exclusively with the following three methods. A call like ::
@@ -2061,9 +2096,17 @@ left undefined.
.. method:: object.__index__(self)
- Called to implement :func:`operator.index`. Also called whenever Python needs
- an integer object (such as in slicing, or in the built-in :func:`bin`,
- :func:`hex` and :func:`oct` functions). Must return an integer.
+ Called to implement :func:`operator.index`, and whenever Python needs to
+ losslessly convert the numeric object to an integer object (such as in
+ slicing, or in the built-in :func:`bin`, :func:`hex` and :func:`oct`
+ functions). Presence of this method indicates that the numeric object is
+ an integer type. Must return an integer.
+
+ .. note::
+
+ When :meth:`__index__` is defined, :meth:`__int__` should also be defined,
+ and both shuld return the same value, in order to have a coherent integer
+ type class.
.. _context-managers:
diff --git a/Doc/reference/import.rst b/Doc/reference/import.rst
index eb497a1..645802d 100644
--- a/Doc/reference/import.rst
+++ b/Doc/reference/import.rst
@@ -210,6 +210,7 @@ Finders and loaders
.. index::
single: finder
single: loader
+ single: module spec
If the named module is not found in :data:`sys.modules`, then Python's import
protocol is invoked to find and load the module. This protocol consists of
@@ -230,13 +231,17 @@ The import machinery is extensible, so new finders can be added to extend the
range and scope of module searching.
Finders do not actually load modules. If they can find the named module, they
-return a :term:`loader`, which the import machinery then invokes to load the
-module and create the corresponding module object.
+return a :dfn:`module spec`, an encapsulation of the module's import-related
+information, which the import machinery then uses when loading the module.
The following sections describe the protocol for finders and loaders in more
detail, including how you can create and register new ones to extend the
import machinery.
+.. versionchanged:: 3.4
+ In previous versions of Python, finders returned :term:`loaders <loader>`
+ directly, whereas now they return module specs which *contain* loaders.
+ Loaders are still used during import but have fewer responsibilities.
Import hooks
------------
@@ -270,41 +275,43 @@ The meta path
.. index::
single: sys.meta_path
- pair: finder; find_module
- pair: finder; find_loader
+ pair: finder; find_spec
When the named module is not found in :data:`sys.modules`, Python next
searches :data:`sys.meta_path`, which contains a list of meta path finder
objects. These finders are queried in order to see if they know how to handle
the named module. Meta path finders must implement a method called
-:meth:`find_module()` which takes two arguments, a name and an import path.
-The meta path finder can use any strategy it wants to determine whether it can
-handle the named module or not.
+:meth:`~importlib.abc.MetaPathFinder.find_spec()` which takes three arguments:
+a name, an import path, and (optionally) a target module. The meta path
+finder can use any strategy it wants to determine whether it can handle
+the named module or not.
If the meta path finder knows how to handle the named module, it returns a
-loader object. If it cannot handle the named module, it returns ``None``. If
+spec object. If it cannot handle the named module, it returns ``None``. If
:data:`sys.meta_path` processing reaches the end of its list without returning
-a loader, then an :exc:`ImportError` is raised. Any other exceptions raised
+a spec, then an :exc:`ImportError` is raised. Any other exceptions raised
are simply propagated up, aborting the import process.
-The :meth:`find_module()` method of meta path finders is called with two
-arguments. The first is the fully qualified name of the module being
-imported, for example ``foo.bar.baz``. The second argument is the path
-entries to use for the module search. For top-level modules, the second
-argument is ``None``, but for submodules or subpackages, the second
-argument is the value of the parent package's ``__path__`` attribute. If
-the appropriate ``__path__`` attribute cannot be accessed, an
-:exc:`ImportError` is raised.
+The :meth:`~importlib.abc.MetaPathFinder.find_spec()` method of meta path
+finders is called with two or three arguments. The first is the fully
+qualified name of the module being imported, for example ``foo.bar.baz``.
+The second argument is the path entries to use for the module search. For
+top-level modules, the second argument is ``None``, but for submodules or
+subpackages, the second argument is the value of the parent package's
+``__path__`` attribute. If the appropriate ``__path__`` attribute cannot
+be accessed, an :exc:`ImportError` is raised. The third argument is an
+existing module object that will be the target of loading later. The
+import system passes in a target module only during reload.
The meta path may be traversed multiple times for a single import request.
For example, assuming none of the modules involved has already been cached,
importing ``foo.bar.baz`` will first perform a top level import, calling
-``mpf.find_module("foo", None)`` on each meta path finder (``mpf``). After
+``mpf.find_spec("foo", None, None)`` on each meta path finder (``mpf``). After
``foo`` has been imported, ``foo.bar`` will be imported by traversing the
meta path a second time, calling
-``mpf.find_module("foo.bar", foo.__path__)``. Once ``foo.bar`` has been
+``mpf.find_spec("foo.bar", foo.__path__, None)``. Once ``foo.bar`` has been
imported, the final traversal will call
-``mpf.find_module("foo.bar.baz", foo.bar.__path__)``.
+``mpf.find_spec("foo.bar.baz", foo.bar.__path__, None)``.
Some meta path finders only support top level imports. These importers will
always return ``None`` when anything other than ``None`` is passed as the
@@ -315,129 +322,239 @@ knows how to import built-in modules, one that knows how to import frozen
modules, and one that knows how to import modules from an :term:`import path`
(i.e. the :term:`path based finder`).
+.. versionchanged:: 3.4
+ The :meth:`~importlib.abc.MetaPathFinder.find_spec` method of meta path
+ finders replaced :meth:`~importlib.abc.MetaPathFinder.find_module`, which
+ is now deprecated. While it will continue to work without change, the
+ import machinery will try it only if the finder does not implement
+ ``find_spec()``.
-Loaders
-=======
-
-If and when a module loader is found its
-:meth:`~importlib.abc.Loader.load_module` method is called, with a single
-argument, the fully qualified name of the module being imported. This method
-has several responsibilities, and should return the module object it has
-loaded [#fnlo]_. If it cannot load the module, it should raise an
-:exc:`ImportError`, although any other exception raised during
-:meth:`load_module()` will be propagated.
-In many cases, the finder and loader can be the same object; in such cases the
-:meth:`finder.find_module()` would just return ``self``.
+Loading
+=======
-Loaders must satisfy the following requirements:
+If and when a module spec is found, the import machinery will use it (and
+the loader it contains) when loading the module. Here is an approximation
+of what happens during the loading portion of import::
+
+ module = None
+ if spec.loader is not None and hasattr(spec.loader, 'create_module'):
+ module = spec.loader.create_module(spec)
+ if module is None:
+ module = ModuleType(spec.name)
+ # The import-related module attributes get set here:
+ _init_module_attrs(spec, module)
+
+ if spec.loader is None:
+ if spec.submodule_search_locations is not None:
+ # namespace package
+ sys.modules[spec.name] = module
+ else:
+ # unsupported
+ raise ImportError
+ elif not hasattr(spec.loader, 'exec_module'):
+ module = spec.loader.load_module(spec.name)
+ # Set __loader__ and __package__ if missing.
+ else:
+ sys.modules[spec.name] = module
+ try:
+ spec.loader.exec_module(module)
+ except BaseException:
+ try:
+ del sys.modules[spec.name]
+ except KeyError:
+ pass
+ raise
+ return sys.modules[spec.name]
+
+Note the following details:
* If there is an existing module object with the given name in
- :data:`sys.modules`, the loader must use that existing module. (Otherwise,
- :func:`imp.reload` will not work correctly.) If the named module does
- not exist in :data:`sys.modules`, the loader must create a new module
- object and add it to :data:`sys.modules`.
+ :data:`sys.modules`, import will have already returned it.
- Note that the module *must* exist in :data:`sys.modules` before the loader
+ * The module will exist in :data:`sys.modules` before the loader
executes the module code. This is crucial because the module code may
(directly or indirectly) import itself; adding it to :data:`sys.modules`
beforehand prevents unbounded recursion in the worst case and multiple
loading in the best.
- If loading fails, the loader must remove any modules it has inserted into
- :data:`sys.modules`, but it must remove **only** the failing module, and
- only if the loader itself has loaded it explicitly. Any module already in
- the :data:`sys.modules` cache, and any module that was successfully loaded
- as a side-effect, must remain in the cache.
-
- * The loader may set the ``__file__`` attribute of the module. If set, this
- attribute's value must be a string. The loader may opt to leave
- ``__file__`` unset if it has no semantic meaning (e.g. a module loaded from
- a database). If ``__file__`` is set, it may also be appropriate to set the
- ``__cached__`` attribute which is the path to any compiled version of the
- code (e.g. byte-compiled file). The file does not need to exist to set this
- attribute; the path can simply point to whether the compiled file would
- exist (see :pep:`3147`).
-
- * The loader may set the ``__name__`` attribute of the module. While not
- required, setting this attribute is highly recommended so that the
- :meth:`repr()` of the module is more informative.
-
- * If the module is a package (either regular or namespace), the loader must
- set the module object's ``__path__`` attribute. The value must be
- iterable, but may be empty if ``__path__`` has no further significance
- to the loader. If ``__path__`` is not empty, it must produce strings
- when iterated over. More details on the semantics of ``__path__`` are
- given :ref:`below <package-path-rules>`.
-
- * The ``__loader__`` attribute must be set to the loader object that loaded
- the module. This is mostly for introspection and reloading, but can be
- used for additional loader-specific functionality, for example getting
- data associated with a loader.
-
- * The module's ``__package__`` attribute should be set. Its value must be a
- string, but it can be the same value as its ``__name__``. If the attribute
- is set to ``None`` or is missing, the import system will fill it in with a
- more appropriate value. When the module is a package, its ``__package__``
- value should be set to its ``__name__``. When the module is not a package,
- ``__package__`` should be set to the empty string for top-level modules, or
- for submodules, to the parent package's name. See :pep:`366` for further
- details.
+ * If loading fails, the failing module -- and only the failing module --
+ gets removed from :data:`sys.modules`. Any module already in the
+ :data:`sys.modules` cache, and any module that was successfully loaded
+ as a side-effect, must remain in the cache. This contrasts with
+ reloading where even the failing module is left in :data:`sys.modules`.
- This attribute is used instead of ``__name__`` to calculate explicit
- relative imports for main modules, as defined in :pep:`366`.
+ * After the module is created but before execution, the import machinery
+ sets the import-related module attributes ("_init_module_attrs" in
+ the pseudo-code example above), as summarized in a
+ :ref:`later section <import-mod-attrs>`.
+
+ * Module execution is the key moment of loading in which the module's
+ namespace gets populated. Execution is entirely delegated to the
+ loader, which gets to decide what gets populated and how.
+
+ * The module created during loading and passed to exec_module() may
+ not be the one returned at the end of import [#fnlo]_.
+
+.. versionchanged:: 3.4
+ The import system has taken over the boilerplate responsibilities of
+ loaders. These were previously performed by the
+ :meth:`importlib.abc.Loader.load_module` method.
+
+Loaders
+-------
+
+Module loaders provide the critical function of loading: module execution.
+The import machinery calls the :meth:`importlib.abc.Loader.exec_module`
+method with a single argument, the module object to execute. Any value
+returned from :meth:`~importlib.abc.Loader.exec_module` is ignored.
+
+Loaders must satisfy the following requirements:
* If the module is a Python module (as opposed to a built-in module or a
dynamically loaded extension), the loader should execute the module's code
in the module's global name space (``module.__dict__``).
+ * If the loader cannot execute the module, it should raise an
+ :exc:`ImportError`, although any other exception raised during
+ :meth:`~importlib.abc.Loader.exec_module` will be propagated.
-Module reprs
-------------
+In many cases, the finder and loader can be the same object; in such cases the
+:meth:`~importlib.abc.MetaPathFinder.find_spec` method would just return a
+spec with the loader set to ``self``.
-By default, all modules have a usable repr, however depending on the
-attributes set above, and hooks in the loader, you can more explicitly control
-the repr of module objects.
-
-Loaders may implement a :meth:`module_repr()` method which takes a single
-argument, the module object. When ``repr(module)`` is called for a module
-with a loader supporting this protocol, whatever is returned from
-``module.__loader__.module_repr(module)`` is returned as the module's repr
-without further processing. This return value must be a string.
-
-If the module has no ``__loader__`` attribute, or the loader has no
-:meth:`module_repr()` method, then the module object implementation itself
-will craft a default repr using whatever information is available. It will
-try to use the ``module.__name__``, ``module.__file__``, and
-``module.__loader__`` as input into the repr, with defaults for whatever
-information is missing.
+Module loaders may opt in to creating the module object during loading
+by implementing a :meth:`~importlib.abc.Loader.create_module` method.
+It takes one argument, the module spec, and returns the new module object
+to use during loading. ``create_module()`` does not need to set any attributes
+on the module object. If the loader does not define ``create_module()``, the
+import machinery will create the new module itself.
-Here are the exact rules used:
+.. versionadded:: 3.4
+ The create_module() method of loaders.
- * If the module has a ``__loader__`` and that loader has a
- :meth:`module_repr()` method, call it with a single argument, which is the
- module object. The value returned is used as the module's repr.
+.. versionchanged:: 3.4
+ The :meth:`~importlib.abc.Loader.load_module` method was replaced by
+ :meth:`~importlib.abc.Loader.exec_module` and the import
+ machinery assumed all the boilerplate responsibilities of loading.
- * If an exception occurs in :meth:`module_repr()`, the exception is caught
- and discarded, and the calculation of the module's repr continues as if
- :meth:`module_repr()` did not exist.
+ For compatibility with existing loaders, the import machinery will use
+ the ``load_module()`` method of loaders if it exists and the loader does
+ not also implement ``exec_module()``. However, ``load_module()`` has been
+ deprecated and loaders should implement ``exec_module()`` instead.
- * If the module has a ``__file__`` attribute, this is used as part of the
- module's repr.
+ The ``load_module()`` method must implement all the boilerplate loading
+ functionality described above in addition to executing the module. All
+ the same constraints apply, with some additional clarification:
- * If the module has no ``__file__`` but does have a ``__loader__``, then the
- loader's repr is used as part of the module's repr.
+ * If there is an existing module object with the given name in
+ :data:`sys.modules`, the loader must use that existing module.
+ (Otherwise, :func:`importlib.reload` will not work correctly.) If the
+ named module does not exist in :data:`sys.modules`, the loader
+ must create a new module object and add it to :data:`sys.modules`.
- * Otherwise, just use the module's ``__name__`` in the repr.
+ * The module *must* exist in :data:`sys.modules` before the loader
+ executes the module code, to prevent unbounded recursion or multiple
+ loading.
+
+ * If loading fails, the loader must remove any modules it has inserted
+ into :data:`sys.modules`, but it must remove **only** the failing
+ module, and only if the loader itself has loaded it explicitly.
+
+Module spec
+-----------
+
+The import machinery uses a variety of information about each module
+during import, especially before loading. Most of the information is
+common to all modules. The purpose of a module's spec is to encapsulate
+this import-related information on a per-module basis.
+
+Using a spec during import allows state to be transferred between import
+system components, e.g. between the finder that creates the module spec
+and the loader that executes it. Most importantly, it allows the
+import machinery to perform the boilerplate operations of loading,
+whereas without a module spec the loader had that responsibility.
+
+See :class:`~importlib.machinery.ModuleSpec` for more specifics on what
+information a module's spec may hold.
+
+.. versionadded:: 3.4
+
+.. _import-mod-attrs:
+
+Import-related module attributes
+--------------------------------
+
+The import machinery fills in these attributes on each module object
+during loading, based on the module's spec, before the loader executes
+the module.
+
+.. attribute:: __name__
+
+ The ``__name__`` attribute must be set to the fully-qualified name of
+ the module. This name is used to uniquely identify the module in
+ the import system.
+
+.. attribute:: __loader__
+
+ The ``__loader__`` attribute must be set to the loader object that
+ the import machinery used when loading the module. This is mostly
+ for introspection, but can be used for additional loader-specific
+ functionality, for example getting data associated with a loader.
+
+.. attribute:: __package__
+
+ The module's ``__package__`` attribute must be set. Its value must
+ be a string, but it can be the same value as its ``__name__``. When
+ the module is a package, its ``__package__`` value should be set to
+ its ``__name__``. When the module is not a package, ``__package__``
+ should be set to the empty string for top-level modules, or for
+ submodules, to the parent package's name. See :pep:`366` for further
+ details.
-This example, from :pep:`420` shows how a loader can craft its own module
-repr::
+ This attribute is used instead of ``__name__`` to calculate explicit
+ relative imports for main modules, as defined in :pep:`366`.
+
+.. attribute:: __spec__
+
+ The ``__spec__`` attribute must be set to the module spec that was
+ used when importing the module. This is used primarily for
+ introspection and during reloading. Setting ``__spec__``
+ appropriately applies equally to :ref:`modules initialized during
+ interpreter startup <programs>`. The one exception is ``__main__``,
+ where ``__spec__`` is :ref:`set to None in some cases <main_spec>`.
+
+ .. versionadded:: 3.4
+
+.. attribute:: __path__
- class NamespaceLoader:
- @classmethod
- def module_repr(cls, module):
- return "<module '{}' (namespace)>".format(module.__name__)
+ If the module is a package (either regular or namespace), the module
+ object's ``__path__`` attribute must be set. The value must be
+ iterable, but may be empty if ``__path__`` has no further significance.
+ If ``__path__`` is not empty, it must produce strings when iterated
+ over. More details on the semantics of ``__path__`` are given
+ :ref:`below <package-path-rules>`.
+ Non-package modules should not have a ``__path__`` attribute.
+
+.. attribute:: __file__
+.. attribute:: __cached__
+
+ ``__file__`` is optional. If set, this attribute's value must be a
+ string. The import system may opt to leave ``__file__`` unset if it
+ has no semantic meaning (e.g. a module loaded from a database).
+
+ If ``__file__`` is set, it may also be appropriate to set the
+ ``__cached__`` attribute which is the path to any compiled version of
+ the code (e.g. byte-compiled file). The file does not need to exist
+ to set this attribute; the path can simply point to where the
+ compiled file would exist (see :pep:`3147`).
+
+ It is also appropriate to set ``__cached__`` when ``__file__`` is not
+ set. However, that scenario is quite atypical. Ultimately, the
+ loader is what makes use of ``__file__`` and/or ``__cached__``. So
+ if a loader can load from a cached module but otherwise does not load
+ from a file, that atypical scenario may be appropriate.
.. _package-path-rules:
@@ -462,9 +579,47 @@ A package's ``__init__.py`` file may set or alter the package's ``__path__``
attribute, and this was typically the way namespace packages were implemented
prior to :pep:`420`. With the adoption of :pep:`420`, namespace packages no
longer need to supply ``__init__.py`` files containing only ``__path__``
-manipulation code; the namespace loader automatically sets ``__path__``
+manipulation code; the import machinery automatically sets ``__path__``
correctly for the namespace package.
+Module reprs
+------------
+
+By default, all modules have a usable repr, however depending on the
+attributes set above, and in the module's spec, you can more explicitly
+control the repr of module objects.
+
+If the module has a spec (``__spec__``), the import machinery will try
+to generate a repr from it. If that fails or there is no spec, the import
+system will craft a default repr using whatever information is available
+on the module. It will try to use the ``module.__name__``,
+``module.__file__``, and ``module.__loader__`` as input into the repr,
+with defaults for whatever information is missing.
+
+Here are the exact rules used:
+
+ * If the module has a ``__spec__`` attribute, the information in the spec
+ is used to generate the repr. The "name", "loader", "origin", and
+ "has_location" attributes are consulted.
+
+ * If the module has a ``__file__`` attribute, this is used as part of the
+ module's repr.
+
+ * If the module has no ``__file__`` but does have a ``__loader__`` that is not
+ ``None``, then the loader's repr is used as part of the module's repr.
+
+ * Otherwise, just use the module's ``__name__`` in the repr.
+
+.. versionchanged:: 3.4
+ Use of :meth:`loader.module_repr() <importlib.abc.Loader.module_repr>`
+ has been deprecated and the module spec is now used by the import
+ machinery to generate a module repr.
+
+ For backward compatibility with Python 3.3, the module repr will be
+ generated by calling the loader's
+ :meth:`~importlib.abc.Loader.module_repr` method, if defined, before
+ trying either approach described above. However, the method is deprecated.
+
The Path Based Finder
=====================
@@ -473,8 +628,9 @@ The Path Based Finder
single: path based finder
As mentioned previously, Python comes with several default meta path finders.
-One of these, called the :term:`path based finder`, searches an :term:`import
-path`, which contains a list of :term:`path entries <path entry>`. Each path
+One of these, called the :term:`path based finder`
+(:class:`~importlib.machinery.PathFinder`), searches an :term:`import path`,
+which contains a list of :term:`path entries <path entry>`. Each path
entry names a location to search for modules.
The path based finder itself doesn't know how to import anything. Instead, it
@@ -523,15 +679,15 @@ Path entry finders
single: sys.path_importer_cache
single: PYTHONPATH
-The :term:`path based finder` is responsible for finding and loading Python
-modules and packages whose location is specified with a string :term:`path
-entry`. Most path entries name locations in the file system, but they need
-not be limited to this.
+The :term:`path based finder` is responsible for finding and loading
+Python modules and packages whose location is specified with a string
+:term:`path entry`. Most path entries name locations in the file system,
+but they need not be limited to this.
As a meta path finder, the :term:`path based finder` implements the
-:meth:`find_module()` protocol previously described, however it exposes
-additional hooks that can be used to customize how modules are found and
-loaded from the :term:`import path`.
+:meth:`~importlib.abc.MetaPathFinder.find_spec` protocol previously
+described, however it exposes additional hooks that can be used to
+customize how modules are found and loaded from the :term:`import path`.
Three variables are used by the :term:`path based finder`, :data:`sys.path`,
:data:`sys.path_hooks` and :data:`sys.path_importer_cache`. The ``__path__``
@@ -551,14 +707,16 @@ finder>`.
The :term:`path based finder` is a :term:`meta path finder`, so the import
machinery begins the :term:`import path` search by calling the path
-based finder's :meth:`find_module()` method as described previously. When
-the ``path`` argument to :meth:`find_module()` is given, it will be a
+based finder's :meth:`~importlib.machinery.PathFinder.find_spec` method as
+described previously. When the ``path`` argument to
+:meth:`~importlib.machinery.PathFinder.find_spec` is given, it will be a
list of string paths to traverse - typically a package's ``__path__``
-attribute for an import within that package. If the ``path`` argument
-is ``None``, this indicates a top level import and :data:`sys.path` is used.
+attribute for an import within that package. If the ``path`` argument is
+``None``, this indicates a top level import and :data:`sys.path` is used.
The path based finder iterates over every entry in the search path, and
-for each of these, looks for an appropriate :term:`path entry finder` for the
+for each of these, looks for an appropriate :term:`path entry finder`
+(:class:`~importlib.abc.PathEntryFinder`) for the
path entry. Because this can be an expensive operation (e.g. there may be
`stat()` call overheads for this search), the path based finder maintains
a cache mapping path entries to path entry finders. This cache is maintained
@@ -575,59 +733,82 @@ hooks <path entry hook>` in this list is called with a single argument, the
path entry to be searched. This callable may either return a :term:`path
entry finder` that can handle the path entry, or it may raise
:exc:`ImportError`. An :exc:`ImportError` is used by the path based finder to
-signal that the hook cannot find a :term:`path entry finder` for that
-:term:`path entry`. The exception is ignored and :term:`import path`
-iteration continues. The hook should expect either a string or bytes object;
-the encoding of bytes objects is up to the hook (e.g. it may be a file system
-encoding, UTF-8, or something else), and if the hook cannot decode the
-argument, it should raise :exc:`ImportError`.
+signal that the hook cannot find a :term:`path entry finder`.
+for that :term:`path entry`. The
+exception is ignored and :term:`import path` iteration continues. The hook
+should expect either a string or bytes object; the encoding of bytes objects
+is up to the hook (e.g. it may be a file system encoding, UTF-8, or something
+else), and if the hook cannot decode the argument, it should raise
+:exc:`ImportError`.
If :data:`sys.path_hooks` iteration ends with no :term:`path entry finder`
-being returned, then the path based finder's :meth:`find_module()` method
-will store ``None`` in :data:`sys.path_importer_cache` (to indicate that
-there is no finder for this path entry) and return ``None``, indicating that
-this :term:`meta path finder` could not find the module.
+being returned, then the path based finder's
+:meth:`~importlib.machinery.PathFinder.find_spec` method will store ``None``
+in :data:`sys.path_importer_cache` (to indicate that there is no finder for
+this path entry) and return ``None``, indicating that this
+:term:`meta path finder` could not find the module.
If a :term:`path entry finder` *is* returned by one of the :term:`path entry
hook` callables on :data:`sys.path_hooks`, then the following protocol is used
-to ask the finder for a module loader, which is then used to load the module.
-
+to ask the finder for a module spec, which is then used when loading the
+module.
Path entry finder protocol
--------------------------
In order to support imports of modules and initialized packages and also to
contribute portions to namespace packages, path entry finders must implement
-the :meth:`find_loader()` method.
-
-:meth:`find_loader()` takes one argument, the fully qualified name of the
-module being imported. :meth:`find_loader()` returns a 2-tuple where the
-first item is the loader and the second item is a namespace :term:`portion`.
-When the first item (i.e. the loader) is ``None``, this means that while the
-path entry finder does not have a loader for the named module, it knows that the
-path entry contributes to a namespace portion for the named module. This will
-almost always be the case where Python is asked to import a namespace package
-that has no physical presence on the file system. When a path entry finder
-returns ``None`` for the loader, the second item of the 2-tuple return value
-must be a sequence, although it can be empty.
-
-If :meth:`find_loader()` returns a non-``None`` loader value, the portion is
-ignored and the loader is returned from the path based finder, terminating
-the search through the path entries.
-
-For backwards compatibility with other implementations of the import
-protocol, many path entry finders also support the same,
-traditional :meth:`find_module()` method that meta path finders support.
-However path entry finder :meth:`find_module()` methods are never called
-with a ``path`` argument (they are expected to record the appropriate
-path information from the initial call to the path hook).
-
-The :meth:`find_module()` method on path entry finders is deprecated,
-as it does not allow the path entry finder to contribute portions to
-namespace packages. Instead path entry finders should implement the
-:meth:`find_loader()` method as described above. If it exists on the path
-entry finder, the import system will always call :meth:`find_loader()`
-in preference to :meth:`find_module()`.
+the :meth:`~importlib.abc.PathEntryFinder.find_spec` method.
+
+:meth:`~importlib.abc.PathEntryFinder.find_spec` takes two argument, the
+fully qualified name of the module being imported, and the (optional) target
+module. ``find_spec()`` returns a fully populated spec for the module.
+This spec will always have "loader" set (with one exception).
+
+To indicate to the import machinery that the spec represents a namespace
+:term:`portion`. the path entry finder sets "loader" on the spec to
+``None`` and "submodule_search_locations" to a list containing the
+portion.
+
+.. versionchanged:: 3.4
+ :meth:`~importlib.abc.PathEntryFinder.find_spec` replaced
+ :meth:`~importlib.abc.PathEntryFinder.find_loader` and
+ :meth:`~importlib.abc.PathEntryFinder.find_module`, both of which
+ are now deprecated, but will be used if ``find_spec()`` is not defined.
+
+ Older path entry finders may implement one of these two deprecated methods
+ instead of ``find_spec()``. The methods are still respected for the
+ sake of backward compatibility. Howevever, if ``find_spec()`` is
+ implemented on the path entry finder, the legacy methods are ignored.
+
+ :meth:`~importlib.abc.PathEntryFinder.find_loader` takes one argument, the
+ fully qualified name of the module being imported. ``find_loader()``
+ returns a 2-tuple where the first item is the loader and the second item
+ is a namespace :term:`portion`. When the first item (i.e. the loader) is
+ ``None``, this means that while the path entry finder does not have a
+ loader for the named module, it knows that the path entry contributes to
+ a namespace portion for the named module. This will almost always be the
+ case where Python is asked to import a namespace package that has no
+ physical presence on the file system. When a path entry finder returns
+ ``None`` for the loader, the second item of the 2-tuple return value must
+ be a sequence, although it can be empty.
+
+ If ``find_loader()`` returns a non-``None`` loader value, the portion is
+ ignored and the loader is returned from the path based finder, terminating
+ the search through the path entries.
+
+ For backwards compatibility with other implementations of the import
+ protocol, many path entry finders also support the same,
+ traditional ``find_module()`` method that meta path finders support.
+ However path entry finder ``find_module()`` methods are never called
+ with a ``path`` argument (they are expected to record the appropriate
+ path information from the initial call to the path hook).
+
+ The ``find_module()`` method on path entry finders is deprecated,
+ as it does not allow the path entry finder to contribute portions to
+ namespace packages. If both ``find_loader()`` and ``find_module()``
+ exist on a path entry finder, the import system will always call
+ ``find_loader()`` in preference to ``find_module()``.
Replacing the standard import system
@@ -646,9 +827,54 @@ import statements within that module.
To selectively prevent import of some modules from a hook early on the
meta path (rather than disabling the standard import system entirely),
it is sufficient to raise :exc:`ImportError` directly from
-:meth:`find_module` instead of returning ``None``. The latter indicates
-that the meta path search should continue. while raising an exception
-terminates it immediately.
+:meth:`~importlib.abc.MetaPathFinder.find_spec` instead of returning
+``None``. The latter indicates that the meta path search should continue,
+while raising an exception terminates it immediately.
+
+
+Special considerations for __main__
+===================================
+
+The :mod:`__main__` module is a special case relative to Python's import
+system. As noted :ref:`elsewhere <programs>`, the ``__main__`` module
+is directly initialized at interpreter startup, much like :mod:`sys` and
+:mod:`builtins`. However, unlike those two, it doesn't strictly
+qualify as a built-in module. This is because the manner in which
+``__main__`` is initialized depends on the flags and other options with
+which the interpreter is invoked.
+
+.. _main_spec:
+
+__main__.__spec__
+-----------------
+
+Depending on how :mod:`__main__` is initialized, ``__main__.__spec__``
+gets set appropriately or to ``None``.
+
+When Python is started with the :option:`-m` option, ``__spec__`` is set
+to the module spec of the corresponding module or package. ``__spec__`` is
+also populated when the ``__main__`` module is loaded as part of executing a
+directory, zipfile or other :data:`sys.path` entry.
+
+In :ref:`the remaining cases <using-on-interface-options>`
+``__main__.__spec__`` is set to ``None``, as the code used to populate the
+:mod:`__main__` does not correspond directly with an importable module:
+
+- interactive prompt
+- -c switch
+- running from stdin
+- running directly from a source or bytecode file
+
+Note that ``__main__.__spec__`` is always ``None`` in the last case,
+*even if* the file could technically be imported directly as a module
+instead. Use the :option:`-m` switch if valid module metadata is desired
+in :mod:`__main__`.
+
+Note also that even when ``__main__`` corresponds with an importable module
+and ``__main__.__spec__`` is set accordingly, they're still considered
+*distinct* modules. This is due to the fact that blocks guarded by
+``if __name__ == "__main__":`` checks only execute when the module is used
+to populate the ``__main__`` namespace, and not during normal import.
Open issues
@@ -663,6 +889,12 @@ related entries in the data model reference page?
XXX runpy, pkgutil, et al in the library manual should all get "See Also"
links at the top pointing to the new import system section.
+XXX Add more explanation regarding the different ways in which
+``__main__`` is initialized?
+
+XXX Add more info on ``__main__`` quirks/pitfalls (i.e. copy from
+:pep:`395`).
+
References
==========
@@ -688,6 +920,11 @@ proposed ``__name__`` for semantics :pep:`366` would eventually specify for
:pep:`338` defines executing modules as scripts.
+:pep:`451` adds the encapsulation of per-module import state in spec
+objects. It also off-loads most of the boilerplate responsibilities of
+loaders back onto the import machinery. These changes allow the
+deprecation of several APIs in the import system and also addition of new
+methods to finders and loaders.
.. rubric:: Footnotes
diff --git a/Doc/reference/lexical_analysis.rst b/Doc/reference/lexical_analysis.rst
index 0ed3d3b..6617c3b 100644
--- a/Doc/reference/lexical_analysis.rst
+++ b/Doc/reference/lexical_analysis.rst
@@ -76,7 +76,7 @@ are ignored by the syntax; they are not tokens.
Encoding declarations
---------------------
-.. index:: source character set, encodings
+.. index:: source character set, encoding declarations (source file)
If a comment in the first or second line of the Python script matches the
regular expression ``coding[=:]\s*([-\w.]+)``, this comment is processed as an
diff --git a/Doc/reference/simple_stmts.rst b/Doc/reference/simple_stmts.rst
index 40bbc39..66c234c 100644
--- a/Doc/reference/simple_stmts.rst
+++ b/Doc/reference/simple_stmts.rst
@@ -70,6 +70,7 @@ Assignment statements
=====================
.. index::
+ single: =; assignment statement
pair: assignment; statement
pair: binding; name
pair: rebinding; name
@@ -260,6 +261,18 @@ Augmented assignment statements
.. index::
pair: augmented; assignment
single: statement; assignment, augmented
+ single: +=; augmented assignment
+ single: -=; augmented assignment
+ single: *=; augmented assignment
+ single: /=; augmented assignment
+ single: %=; augmented assignment
+ single: &=; augmented assignment
+ single: ^=; augmented assignment
+ single: |=; augmented assignment
+ single: **=; augmented assignment
+ single: //=; augmented assignment
+ single: >>=; augmented assignment
+ single: <<=; augmented assignment
Augmented assignment is the combination, in a single statement, of a binary
operation and an assignment statement:
@@ -656,6 +669,8 @@ initializing the module, which includes execution of the module's code.
If the requested module is retrieved successfully, it will be made
available in the local namespace in one of three ways:
+.. index:: single: as; import statement
+
* If the module name is followed by :keyword:`as`, then the name
following :keyword:`as` is bound directly to the imported module.
* If no other name is specified, and the module being imported is a top
diff --git a/Doc/tools/sphinx-build.py b/Doc/tools/sphinx-build.py
deleted file mode 100644
index d3fe702..0000000
--- a/Doc/tools/sphinx-build.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- Sphinx - Python documentation toolchain
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- :copyright: 2007-2010 by Georg Brandl.
- :license: Python license.
-"""
-
-import sys
-import warnings
-
-# Get rid of UserWarnings reported by pkg_resources.
-warnings.filterwarnings('ignore', category=UserWarning, module='jinja2')
-
-if __name__ == '__main__':
-
- if sys.version_info[:3] < (2, 4, 0) or sys.version_info[:3] > (3, 0, 0):
- sys.stderr.write("""\
-Error: Sphinx needs to be executed with Python 2.4 or newer (not 3.x though).
-(If you run this from the Makefile, you can set the PYTHON variable
-to the path of an alternative interpreter executable, e.g.,
-``make html PYTHON=python2.5``).
-""")
- sys.exit(1)
-
- from sphinx import main
- sys.exit(main(sys.argv))
diff --git a/Doc/tools/sphinxext/c_annotations.py b/Doc/tools/sphinxext/c_annotations.py
index 8b5167a..cf9ad9e 100644
--- a/Doc/tools/sphinxext/c_annotations.py
+++ b/Doc/tools/sphinxext/c_annotations.py
@@ -81,7 +81,10 @@ class Annotations(dict):
continue
if not par[0].has_key('names') or not par[0]['names']:
continue
- entry = self.get(par[0]['names'][0])
+ name = par[0]['names'][0]
+ if name.startswith("c."):
+ name = name[2:]
+ entry = self.get(name)
if not entry:
continue
elif entry.result_type not in ("PyObject*", "PyVarObject*"):
diff --git a/Doc/tools/sphinxext/download.html b/Doc/tools/sphinxext/download.html
index 31a53cf..0550111 100644
--- a/Doc/tools/sphinxext/download.html
+++ b/Doc/tools/sphinxext/download.html
@@ -34,8 +34,8 @@ in the table are the size of the download files in megabytes.</p>
<td><a href="{{ dlbase }}/python-{{ release }}-docs-text.tar.bz2">Download</a> (ca. 1.5 MB)</td>
</tr>
<tr><td>EPUB</td>
- <td><a href="{{ dlbase }}/python-{{ release }}-docs-epub.zip">Download</a> (ca. 3.5 MB)</td>
- <td><a href="{{ dlbase }}/python-{{ release }}-docs-epub.tar.bz2">Download</a> (ca. 3.5 MB)</td>
+ <td><a href="{{ dlbase }}/python-{{ release }}-docs.epub">Download</a> (ca. 4.5 MB)</td>
+ <td></td>
</tr>
</table>
diff --git a/Doc/tools/sphinxext/indexcontent.html b/Doc/tools/sphinxext/indexcontent.html
index 7f85470..969099a 100644
--- a/Doc/tools/sphinxext/indexcontent.html
+++ b/Doc/tools/sphinxext/indexcontent.html
@@ -16,14 +16,14 @@
<p class="biglink"><a class="biglink" href="{{ pathto("howto/index") }}">Python HOWTOs</a><br/>
<span class="linkdescr">in-depth documents on specific topics</span></p>
</td><td width="50%">
+ <p class="biglink"><a class="biglink" href="{{ pathto("installing/index") }}">Installing Python Modules</a><br/>
+ <span class="linkdescr">installing from the Python Package Index &amp; other sources</span></p>
+ <p class="biglink"><a class="biglink" href="{{ pathto("distributing/index") }}">Distributing Python Modules</a><br/>
+ <span class="linkdescr">publishing modules for installation by others</span></p>
<p class="biglink"><a class="biglink" href="{{ pathto("extending/index") }}">Extending and Embedding</a><br/>
<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("faq/index") }}">FAQs</a><br/>
<span class="linkdescr">frequently asked questions (with answers!)</span></p>
</td></tr>
diff --git a/Doc/tools/sphinxext/indexsidebar.html b/Doc/tools/sphinxext/indexsidebar.html
index a0ec32f..ed5da0c 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.4/">Python 3.4 (in development)</a></li>
+ <li><a href="http://docs.python.org/3.3/">Python 3.3 (stable)</a></li>
<li><a href="http://www.python.org/doc/versions/">Old versions</a></li>
</ul>
diff --git a/Doc/tools/sphinxext/layout.html b/Doc/tools/sphinxext/layout.html
index 16a9212..d3b2801 100644
--- a/Doc/tools/sphinxext/layout.html
+++ b/Doc/tools/sphinxext/layout.html
@@ -12,6 +12,8 @@
{%- endif %}
</li>
{% endblock %}
+{% block relbar1 %} {% if builder != 'qthelp' %} {{ relbar() }} {% endif %} {% endblock %}
+{% block relbar2 %} {% if builder != 'qthelp' %} {{ relbar() }} {% endif %} {% endblock %}
{% block extrahead %}
<link rel="shortcut icon" type="image/png" href="{{ pathto('_static/py.png', 1) }}" />
{% if not embedded %}<script type="text/javascript" src="{{ pathto('_static/copybutton.js', 1) }}"></script>{% endif %}
diff --git a/Doc/tools/sphinxext/pyspecific.py b/Doc/tools/sphinxext/pyspecific.py
index f2d3ca1..31d8c06 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.3/%s'
+SOURCE_URI = 'http://hg.python.org/cpython/file/3.4/%s'
from docutils import nodes, utils
diff --git a/Doc/tools/sphinxext/susp-ignored.csv b/Doc/tools/sphinxext/susp-ignored.csv
index abc3ca8..1769023 100644
--- a/Doc/tools/sphinxext/susp-ignored.csv
+++ b/Doc/tools/sphinxext/susp-ignored.csv
@@ -79,9 +79,8 @@ howto/logging,,:Started,INFO:root:Started
howto/logging,,:This,DEBUG:root:This message should go to the log file
howto/logging,,:This,DEBUG:This message should appear on the console
howto/logging,,:Watch,WARNING:root:Watch out!
-howto/pyporting,75,::,# make sure to use :: Python *and* :: Python :: 3 so
-howto/pyporting,75,::,"'Programming Language :: Python',"
-howto/pyporting,75,::,'Programming Language :: Python :: 3'
+howto/pyporting,,::,Programming Language :: Python :: 2
+howto/pyporting,,::,Programming Language :: Python :: 3
howto/regex,,::,
howto/regex,,:foo,(?:foo)
howto/urllib2,,:example,"for example ""joe@password:example.com"""
@@ -141,15 +140,8 @@ library/linecache,,:sys,"sys:x:3:3:sys:/dev:/bin/sh"
library/logging.handlers,,:port,host:port
library/mmap,,:i2,obj[i1:i2]
library/multiprocessing,,`,# Add more tasks using `put()`
-library/multiprocessing,,`,# A test file for the `multiprocessing` package
-library/multiprocessing,,`,# A test of `multiprocessing.Pool` class
-library/multiprocessing,,`,# `BaseManager`.
-library/multiprocessing,,`,# in the original order then consider using `Pool.map()` or
library/multiprocessing,,`,">>> l._callmethod('__getitem__', (20,)) # equiv to `l[20]`"
library/multiprocessing,,`,">>> l._callmethod('__getslice__', (2, 7)) # equiv to `l[2:7]`"
-library/multiprocessing,,`,# Not sure if we should synchronize access to `socket.accept()` method by
-library/multiprocessing,,`,# object. (We import `multiprocessing.reduction` to enable this pickling.)
-library/multiprocessing,,`,# `Pool.imap()` (which will save on the amount of code needed anyway).
library/multiprocessing,,:queue,">>> QueueManager.register('get_queue', callable=lambda:queue)"
library/multiprocessing,,`,# register the Foo class; make `f()` and `g()` accessible via proxy
library/multiprocessing,,`,# register the Foo class; make `g()` and `_h()` accessible via proxy
@@ -158,20 +150,16 @@ library/nntplib,,:bytes,:bytes
library/nntplib,,:lines,:lines
library/optparse,,:len,"del parser.rargs[:len(value)]"
library/os.path,,:foo,c:foo
+library/pathlib,,:bar,">>> PureWindowsPath('c:/Windows', 'd:bar')"
+library/pathlib,,:bar,PureWindowsPath('d:bar')
+library/pathlib,,:Program,>>> PureWindowsPath('c:Program Files/').root
+library/pathlib,,:Program,>>> PureWindowsPath('c:Program Files/').anchor
library/pdb,,:lineno,filename:lineno
library/pickle,,:memory,"conn = sqlite3.connect("":memory:"")"
library/posix,,`,"CFLAGS=""`getconf LFS_CFLAGS`"" OPT=""-g -O2 $CFLAGS"""
-library/pprint,209,::,"'classifiers': ['Development Status :: 4 - Beta',"
-library/pprint,209,::,"'Intended Audience :: Developers',"
-library/pprint,209,::,"'License :: OSI Approved :: MIT License',"
-library/pprint,209,::,"'Natural Language :: English',"
-library/pprint,209,::,"'Operating System :: OS Independent',"
-library/pprint,209,::,"'Programming Language :: Python',"
-library/pprint,209,::,"'Programming Language :: Python :: 2',"
-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'],"
+library/pprint,,::,"'Programming Language :: Python :: 2 :: Only'],"
+library/pprint,,::,"'Programming Language :: Python :: 2.6',"
+library/pprint,,::,"'Programming Language :: Python :: 2.7',"
library/profile,,:lineno,filename:lineno(function)
library/pyexpat,,:elem1,<py:elem1 />
library/pyexpat,,:py,"xmlns:py = ""http://www.python.org/ns/"">"
@@ -183,6 +171,7 @@ library/socket,,:len,fds.fromstring(cmsg_data[:len(cmsg_data) - (len(cmsg_data)
library/sqlite3,,:age,"cur.execute(""select * from people where name_last=:who and age=:age"", {""who"": who, ""age"": age})"
library/sqlite3,,:memory,
library/sqlite3,,:who,"cur.execute(""select * from people where name_last=:who and age=:age"", {""who"": who, ""age"": age})"
+library/sqlite3,,:path,"db = sqlite3.connect('file:path/to/database?mode=ro', uri=True)"
library/ssl,,:My,"Organizational Unit Name (eg, section) []:My Group"
library/ssl,,:My,"Organization Name (eg, company) [Internet Widgits Pty Ltd]:My Organization, Inc."
library/ssl,,:myserver,"Common Name (eg, YOUR name) []:myserver.mygroup.myorganization.com"
@@ -207,7 +196,12 @@ library/tarfile,,:xz,'r:xz'
library/tarfile,,:xz,'w:xz'
library/time,,:mm,
library/time,,:ss,
+library/tracemalloc,,:limit,"for index, stat in enumerate(top_stats[:limit], 1):"
library/turtle,,::,Example::
+library/unittest,1412,:foo,"self.assertEqual(cm.output, ['INFO:foo:first message',"
+library/unittest,1412,:first,"self.assertEqual(cm.output, ['INFO:foo:first message',"
+library/unittest,1412,:foo,'ERROR:foo.bar:second message'])
+library/unittest,1412,:second,'ERROR:foo.bar:second message'])
library/urllib.request,,:close,Connection:close
library/urllib.request,,:lang,"xmlns=""http://www.w3.org/1999/xhtml"" xml:lang=""en"" lang=""en"">\n\n<head>\n"
library/urllib.request,,:password,"""joe:password@python.org"""
@@ -283,8 +277,8 @@ whatsnew/3.2,,:gz,">>> with tarfile.open(name='myarchive.tar.gz', mode='w:gz') a
whatsnew/3.2,,:location,zope9-location = ${zope9:location}
whatsnew/3.2,,:prefix,zope-conf = ${custom:prefix}/etc/zope.conf
whatsnew/changelog,,:platform,:platform:
+whatsnew/changelog,,:gz,": TarFile opened with external fileobj and ""w:gz"" mode didn't"
whatsnew/changelog,,:PythonCmd,"With Tk < 8.5 _tkinter.c:PythonCmd() raised UnicodeDecodeError, caused"
whatsnew/changelog,,::,": Fix FTP tests for IPv6, bind to ""::1"" instead of ""localhost""."
whatsnew/changelog,,::,": Use ""127.0.0.1"" or ""::1"" instead of ""localhost"" as much as"
whatsnew/changelog,,:password,user:password
-whatsnew/changelog,,:gz,w:gz
diff --git a/Doc/tutorial/controlflow.rst b/Doc/tutorial/controlflow.rst
index 97aea4f..ef50731 100644
--- a/Doc/tutorial/controlflow.rst
+++ b/Doc/tutorial/controlflow.rst
@@ -370,7 +370,7 @@ defined to allow. For example::
return False
retries = retries - 1
if retries < 0:
- raise IOError('uncooperative user')
+ raise OSError('uncooperative user')
print(complaint)
This function can be called in several ways:
diff --git a/Doc/tutorial/errors.rst b/Doc/tutorial/errors.rst
index 4282151..d048ae9 100644
--- a/Doc/tutorial/errors.rst
+++ b/Doc/tutorial/errors.rst
@@ -131,8 +131,8 @@ the exception (allowing a caller to handle the exception as well)::
f = open('myfile.txt')
s = f.readline()
i = int(s.strip())
- except IOError as err:
- print("I/O error: {0}".format(err))
+ except OSError as err:
+ print("OS error: {0}".format(err))
except ValueError:
print("Could not convert data to an integer.")
except:
diff --git a/Doc/tutorial/interactive.rst b/Doc/tutorial/interactive.rst
index 36acb06..abf30f0 100644
--- a/Doc/tutorial/interactive.rst
+++ b/Doc/tutorial/interactive.rst
@@ -7,140 +7,27 @@ Interactive Input Editing and History Substitution
Some versions of the Python interpreter support editing of the current input
line and history substitution, similar to facilities found in the Korn shell and
the GNU Bash shell. This is implemented using the `GNU Readline`_ library,
-which supports Emacs-style and vi-style editing. This library has its own
-documentation which I won't duplicate here; however, the basics are easily
-explained. The interactive editing and history described here are optionally
-available in the Unix and Cygwin versions of the interpreter.
-
-This chapter does *not* document the editing facilities of Mark Hammond's
-PythonWin package or the Tk-based environment, IDLE, distributed with Python.
-The command line history recall which operates within DOS boxes on NT and some
-other DOS and Windows flavors is yet another beast.
-
-
-.. _tut-lineediting:
-
-Line Editing
-============
-
-If supported, input line editing is active whenever the interpreter prints a
-primary or secondary prompt. The current line can be edited using the
-conventional Emacs control characters. The most important of these are:
-:kbd:`C-A` (Control-A) moves the cursor to the beginning of the line, :kbd:`C-E`
-to the end, :kbd:`C-B` moves it one position to the left, :kbd:`C-F` to the
-right. Backspace erases the character to the left of the cursor, :kbd:`C-D` the
-character to its right. :kbd:`C-K` kills (erases) the rest of the line to the
-right of the cursor, :kbd:`C-Y` yanks back the last killed string.
-:kbd:`C-underscore` undoes the last change you made; it can be repeated for
-cumulative effect.
-
-
-.. _tut-history:
-
-History Substitution
-====================
-
-History substitution works as follows. All non-empty input lines issued are
-saved in a history buffer, and when a new prompt is given you are positioned on
-a new line at the bottom of this buffer. :kbd:`C-P` moves one line up (back) in
-the history buffer, :kbd:`C-N` moves one down. Any line in the history buffer
-can be edited; an asterisk appears in front of the prompt to mark a line as
-modified. Pressing the :kbd:`Return` key passes the current line to the
-interpreter. :kbd:`C-R` starts an incremental reverse search; :kbd:`C-S` starts
-a forward search.
+which supports various styles of editing. This library has its own
+documentation which we won't duplicate here.
.. _tut-keybindings:
-Key Bindings
-============
-
-The key bindings and some other parameters of the Readline library can be
-customized by placing commands in an initialization file called
-:file:`~/.inputrc`. Key bindings have the form ::
-
- key-name: function-name
-
-or ::
-
- "string": function-name
-
-and options can be set with ::
-
- set option-name value
-
-For example::
-
- # I prefer vi-style editing:
- set editing-mode vi
-
- # Edit using a single line:
- set horizontal-scroll-mode On
+Tab Completion and History Editing
+==================================
- # Rebind some keys:
- Meta-h: backward-kill-word
- "\C-u": universal-argument
- "\C-x\C-r": re-read-init-file
-
-Note that the default binding for :kbd:`Tab` in Python is to insert a :kbd:`Tab`
-character instead of Readline's default filename completion function. If you
-insist, you can override this by putting ::
-
- Tab: complete
-
-in your :file:`~/.inputrc`. (Of course, this makes it harder to type indented
-continuation lines if you're accustomed to using :kbd:`Tab` for that purpose.)
-
-.. index::
- module: rlcompleter
- module: readline
-
-Automatic completion of variable and module names is optionally available. To
-enable it in the interpreter's interactive mode, add the following to your
-startup file: [#]_ ::
-
- import rlcompleter, readline
- readline.parse_and_bind('tab: complete')
-
-This binds the :kbd:`Tab` key to the completion function, so hitting the
-:kbd:`Tab` key twice suggests completions; it looks at Python statement names,
-the current local variables, and the available module names. For dotted
-expressions such as ``string.a``, it will evaluate the expression up to the
-final ``'.'`` and then suggest completions from the attributes of the resulting
-object. Note that this may execute application-defined code if an object with a
-:meth:`__getattr__` method is part of the expression.
-
-A more capable startup file might look like this example. Note that this
-deletes the names it creates once they are no longer needed; this is done since
-the startup file is executed in the same namespace as the interactive commands,
-and removing the names avoids creating side effects in the interactive
-environment. You may find it convenient to keep some of the imported modules,
-such as :mod:`os`, which turn out to be needed in most sessions with the
-interpreter. ::
-
- # Add auto-completion and a stored history file of commands to your Python
- # interactive interpreter. Requires Python 2.0+, readline. Autocomplete is
- # bound to the Esc key by default (you can change it - see readline docs).
- #
- # Store the file in ~/.pystartup, and set an environment variable to point
- # to it: "export PYTHONSTARTUP=~/.pystartup" in bash.
-
- import atexit
- import os
- import readline
- import rlcompleter
-
- historyPath = os.path.expanduser("~/.pyhistory")
-
- def save_history(historyPath=historyPath):
- import readline
- readline.write_history_file(historyPath)
-
- if os.path.exists(historyPath):
- readline.read_history_file(historyPath)
-
- atexit.register(save_history)
- del os, atexit, readline, rlcompleter, save_history, historyPath
+Completion of variable and module names is
+:ref:`automatically enabled <rlcompleter-config>` at interpreter startup so
+that the :kbd:`Tab` key invokes the completion function; it looks at
+Python statement names, the current local variables, and the available
+module names. For dotted expressions such as ``string.a``, it will evaluate
+the expression up to the final ``'.'`` and then suggest completions from
+the attributes of the resulting object. Note that this may execute
+application-defined code if an object with a :meth:`__getattr__` method
+is part of the expression. The default configuration also saves your
+history into a file named :file:`.python_history` in your user directory.
+The history will be available again during the next interactive interpreter
+session.
.. _tut-commentary:
@@ -162,14 +49,6 @@ into other applications. Another similar enhanced interactive environment is
bpython_.
-.. rubric:: Footnotes
-
-.. [#] Python will execute the contents of a file identified by the
- :envvar:`PYTHONSTARTUP` environment variable when you start an interactive
- interpreter. To customize Python even for non-interactive mode, see
- :ref:`tut-customize`.
-
-
.. _GNU Readline: http://tiswww.case.edu/php/chet/readline/rltop.html
.. _IPython: http://ipython.scipy.org/
.. _bpython: http://www.bpython-interpreter.org/
diff --git a/Doc/tutorial/interpreter.rst b/Doc/tutorial/interpreter.rst
index cdc2bf2..44dc6d1 100644
--- a/Doc/tutorial/interpreter.rst
+++ b/Doc/tutorial/interpreter.rst
@@ -10,13 +10,13 @@ Using the Python Interpreter
Invoking the Interpreter
========================
-The Python interpreter is usually installed as :file:`/usr/local/bin/python3.3`
+The Python interpreter is usually installed as :file:`/usr/local/bin/python3.4`
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:
.. code-block:: text
- python3.3
+ python3.4
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
@@ -24,26 +24,25 @@ 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:\\Python33`, though you can change this when you're running the
+:file:`C:\\Python34`, 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:\python33
+ set path=%path%;C:\python34
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
status. If that doesn't work, you can exit the interpreter by typing the
following command: ``quit()``.
-The interpreter's line-editing features usually aren't very sophisticated. On
-Unix, whoever installed the interpreter may have enabled support for the GNU
-readline library, which adds more elaborate interactive editing and history
-features. Perhaps the quickest check to see whether command line editing is
-supported is typing Control-P to the first Python prompt you get. If it beeps,
-you have command line editing; see Appendix :ref:`tut-interacting` for an
-introduction to the keys. If nothing appears to happen, or if ``^P`` is echoed,
-command line editing isn't available; you'll only be able to use backspace to
-remove characters from the current line.
+The interpreter's line-editing features include interactive editing, history
+substitution and code completion on systems that support readline. Perhaps the
+quickest check to see whether command line editing is supported is typing
+Control-P to the first Python prompt you get. If it beeps, you have command
+line editing; see Appendix :ref:`tut-interacting` for an introduction to the
+keys. If nothing appears to happen, or if ``^P`` is echoed, command line
+editing isn't available; you'll only be able to use backspace to remove
+characters from the current line.
The interpreter operates somewhat like the Unix shell: when called with standard
input connected to a tty device, it reads and executes commands interactively;
@@ -95,9 +94,9 @@ 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.3
- Python 3.3 (default, Sep 24 2012, 09:25:04)
- [GCC 4.6.3] on linux2
+ $ python3.4
+ Python 3.4 (default, Mar 16 2014, 09:25:04)
+ [GCC 4.8.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
@@ -106,7 +105,7 @@ before printing the first prompt::
Continuation lines are needed when entering a multi-line construct. As an
example, take a look at this :keyword:`if` statement::
- >>> the_world_is_flat = 1
+ >>> the_world_is_flat = True
>>> if the_world_is_flat:
... print("Be careful not to fall off!")
...
@@ -149,7 +148,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.3
+ #! /usr/bin/env python3.4
(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/modules.rst b/Doc/tutorial/modules.rst
index 1902964..fd361ae 100644
--- a/Doc/tutorial/modules.rst
+++ b/Doc/tutorial/modules.rst
@@ -165,10 +165,16 @@ a built-in module with that name. If not found, it then searches for a file
named :file:`spam.py` in a list of directories given by the variable
:data:`sys.path`. :data:`sys.path` is initialized from these locations:
-* the directory containing the input script (or the current directory).
+* The directory containing the input script (or the current directory when no
+ file is specified).
* :envvar:`PYTHONPATH` (a list of directory names, with the same syntax as the
shell variable :envvar:`PATH`).
-* the installation-dependent default.
+* The installation-dependent default.
+
+.. note::
+ On file systems which support symlinks, the directory containing the input
+ script is calculated after the symlink is followed. In other words the
+ directory containing the symlink is **not** added to the module search path.
After initialization, Python programs can modify :data:`sys.path`. The
directory containing the script being run is placed at the beginning of the
@@ -278,24 +284,23 @@ defines. It returns a sorted list of strings::
>>> dir(fibo)
['__name__', 'fib', 'fib2']
>>> dir(sys) # doctest: +NORMALIZE_WHITESPACE
- ['__displayhook__', '__doc__', '__egginsert', '__excepthook__',
- '__loader__', '__name__', '__package__', '__plen', '__stderr__',
- '__stdin__', '__stdout__', '_clear_type_cache', '_current_frames',
- '_debugmallocstats', '_getframe', '_home', '_mercurial', '_xoptions',
- 'abiflags', 'api_version', 'argv', 'base_exec_prefix', 'base_prefix',
- 'builtin_module_names', 'byteorder', 'call_tracing', 'callstats',
- 'copyright', 'displayhook', 'dont_write_bytecode', 'exc_info',
- 'excepthook', 'exec_prefix', 'executable', 'exit', 'flags', 'float_info',
- 'float_repr_style', 'getcheckinterval', 'getdefaultencoding',
- 'getdlopenflags', 'getfilesystemencoding', 'getobjects', 'getprofile',
- 'getrecursionlimit', 'getrefcount', 'getsizeof', 'getswitchinterval',
- 'gettotalrefcount', 'gettrace', 'hash_info', 'hexversion',
- 'implementation', 'int_info', 'intern', 'maxsize', 'maxunicode',
- 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache',
- 'platform', 'prefix', 'ps1', 'setcheckinterval', 'setdlopenflags',
- 'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace',
- 'stderr', 'stdin', 'stdout', 'thread_info', 'version', 'version_info',
- 'warnoptions']
+ ['__displayhook__', '__doc__', '__excepthook__', '__loader__', '__name__',
+ '__package__', '__stderr__', '__stdin__', '__stdout__',
+ '_clear_type_cache', '_current_frames', '_debugmallocstats', '_getframe',
+ '_home', '_mercurial', '_xoptions', 'abiflags', 'api_version', 'argv',
+ 'base_exec_prefix', 'base_prefix', 'builtin_module_names', 'byteorder',
+ 'call_tracing', 'callstats', 'copyright', 'displayhook',
+ 'dont_write_bytecode', 'exc_info', 'excepthook', 'exec_prefix',
+ 'executable', 'exit', 'flags', 'float_info', 'float_repr_style',
+ 'getcheckinterval', 'getdefaultencoding', 'getdlopenflags',
+ 'getfilesystemencoding', 'getobjects', 'getprofile', 'getrecursionlimit',
+ 'getrefcount', 'getsizeof', 'getswitchinterval', 'gettotalrefcount',
+ 'gettrace', 'hash_info', 'hexversion', 'implementation', 'int_info',
+ 'intern', 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path',
+ 'path_hooks', 'path_importer_cache', 'platform', 'prefix', 'ps1',
+ 'setcheckinterval', 'setdlopenflags', 'setprofile', 'setrecursionlimit',
+ 'setswitchinterval', 'settrace', 'stderr', 'stdin', 'stdout',
+ 'thread_info', 'version', 'version_info', 'warnoptions']
Without arguments, :func:`dir` lists the names you have defined currently::
diff --git a/Doc/tutorial/stdlib.rst b/Doc/tutorial/stdlib.rst
index 7e7a154..2e3ed18 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:\\Python33'
+ 'C:\\Python34'
>>> 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 c1dd69a..c0197ea 100644
--- a/Doc/tutorial/stdlib2.rst
+++ b/Doc/tutorial/stdlib2.rst
@@ -277,7 +277,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:/python33/lib/weakref.py", line 46, in __getitem__
+ File "C:/python34/lib/weakref.py", line 46, in __getitem__
o = self.data[key]()
KeyError: 'primary'
diff --git a/Doc/tutorial/whatnow.rst b/Doc/tutorial/whatnow.rst
index 7fcbdc3f..979f587 100644
--- a/Doc/tutorial/whatnow.rst
+++ b/Doc/tutorial/whatnow.rst
@@ -21,8 +21,8 @@ the set are:
and many other tasks. Skimming through the Library Reference will give you an
idea of what's available.
-* :ref:`install-index` explains how to install external modules written by other
- Python users.
+* :ref:`installing-index` explains how to install additional modules written
+ by other Python users.
* :ref:`reference-index`: A detailed explanation of Python's syntax and
semantics. It's heavy reading, but is useful as a complete guide to the
diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst
index 4e7168f..0c3c203 100644
--- a/Doc/using/cmdline.rst
+++ b/Doc/using/cmdline.rst
@@ -24,7 +24,7 @@ Command line
When invoking Python, you may specify any of these options::
- python [-bBdEhiOqsSuvVWx?] [-c command | -m module-name | script | - ] [args]
+ python [-bBdEhiIOqsSuvVWx?] [-c command | -m module-name | script | - ] [args]
The most common use case is, of course, a simple invocation of a script::
@@ -81,7 +81,8 @@ source.
the implementation may not always enforce this (e.g. it may allow you to
use a name that includes a hyphen).
- Package names are also permitted. When a package name is supplied instead
+ Package names (including namespace packages) are also permitted. When a
+ package name is supplied instead
of a normal module, the interpreter will execute ``<pkg>.__main__`` as
the main module. This behaviour is deliberately similar to the handling
of directories and zipfiles that are passed to the interpreter as the
@@ -115,6 +116,9 @@ source.
.. versionchanged:: 3.1
Supply the package name to run a ``__main__`` submodule.
+ .. versionchanged:: 3.4
+ namespace packages are also supported
+
.. describe:: -
@@ -147,7 +151,12 @@ source.
If no interface option is given, :option:`-i` is implied, ``sys.argv[0]`` is
an empty string (``""``) and the current directory will be added to the
-start of :data:`sys.path`.
+start of :data:`sys.path`. Also, tab-completion and history editing is
+automatically enabled, if available on your platform (see
+:ref:`rlcompleter-config`).
+
+.. versionchanged:: 3.4
+ Automatic enabling of tab-completion and history editing.
.. seealso:: :ref:`tut-invoking`
@@ -170,6 +179,8 @@ Generic options
Python 3.0
+.. _using-on-misc-options:
+
Miscellaneous options
~~~~~~~~~~~~~~~~~~~~~
@@ -208,6 +219,17 @@ Miscellaneous options
raises an exception. See also :envvar:`PYTHONINSPECT`.
+.. cmdoption:: -I
+
+ Run Python in isolated mode. This also implies -E and -s.
+ In isolated mode :data:`sys.path` contains neither the script's directory nor
+ the user's site-packages directory. All :envvar:`PYTHON*` environment
+ variables are ignored, too. Further restrictions may be imposed to prevent
+ the user from injecting malicious code.
+
+ .. versionadded:: 3.4
+
+
.. cmdoption:: -O
Turn on basic optimizations. This changes the filename extension for
@@ -358,9 +380,19 @@ Miscellaneous options
.. cmdoption:: -X
Reserved for various implementation-specific options. CPython currently
- defines just one, you can use ``-X faulthandler`` to enable
- :mod:`faulthandler`. It also allows to pass arbitrary values and retrieve
- them through the :data:`sys._xoptions` dictionary.
+ defines the following possible values:
+
+ * ``-X faulthandler`` to enable :mod:`faulthandler`;
+ * ``-X showrefcount`` to enable the output of the total reference count
+ and memory blocks (only works on debug builds);
+ * ``-X tracemalloc`` to start tracing Python memory allocations using the
+ :mod:`tracemalloc` module. By default, only the most recent frame is
+ stored in a traceback of a trace. Use ``-X tracemalloc=NFRAME`` to start
+ tracing with a traceback limit of *NFRAME* frames. See the
+ :func:`tracemalloc.start` for more information.
+
+ It also allows to pass arbitrary values and retrieve them through the
+ :data:`sys._xoptions` dictionary.
.. versionchanged:: 3.2
It is now allowed to pass :option:`-X` with CPython.
@@ -368,6 +400,9 @@ Miscellaneous options
.. versionadded:: 3.3
The ``-X faulthandler`` option.
+ .. versionadded:: 3.4
+ The ``-X showrefcount`` and ``-X tracemalloc`` options.
+
Options you shouldn't use
~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -385,7 +420,7 @@ Environment variables
---------------------
These environment variables influence Python's behavior, they are processed
-before the command-line switches other than -E. It is customary that
+before the command-line switches other than -E or -I. It is customary that
command-line switches override environmental variables where there is a
conflict.
@@ -430,7 +465,7 @@ conflict.
is executed in the same namespace where interactive commands are executed so
that objects defined or imported in it can be used without qualification in
the interactive session. You can also change the prompts :data:`sys.ps1` and
- :data:`sys.ps2` in this file.
+ :data:`sys.ps2` and the hook :data:`sys.__interactivehook__` in this file.
.. envvar:: PYTHONY2K
@@ -485,9 +520,9 @@ conflict.
.. envvar:: PYTHONDONTWRITEBYTECODE
- If this is set, Python won't try to write ``.pyc`` or ``.pyo`` files on the
- import of source modules. This is equivalent to specifying the :option:`-B`
- option.
+ If this is set to a non-empty string, Python won't try to write ``.pyc`` or
+ ``.pyo`` files on the import of source modules. This is equivalent to
+ specifying the :option:`-B` option.
.. envvar:: PYTHONHASHSEED
@@ -512,13 +547,16 @@ conflict.
.. envvar:: PYTHONIOENCODING
If this is set before running the interpreter, it overrides the encoding used
- for stdin/stdout/stderr, in the syntax ``encodingname:errorhandler``. The
- ``:errorhandler`` part is optional and has the same meaning as in
- :func:`str.encode`.
+ for stdin/stdout/stderr, in the syntax ``encodingname:errorhandler``. Both
+ the ``encodingname`` and the ``:errorhandler`` parts are optional and have
+ the same meaning as in :func:`str.encode`.
For stderr, the ``:errorhandler`` part is ignored; the handler will always be
``'backslashreplace'``.
+ .. versionchanged:: 3.4
+ The ``encodingname`` part is now optional.
+
.. envvar:: PYTHONNOUSERSITE
@@ -556,15 +594,34 @@ conflict.
.. 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.
+ If this environment variable is set to a non-empty string,
+ :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.
.. versionadded:: 3.3
+.. envvar:: PYTHONTRACEMALLOC
+
+ If this environment variable is set to a non-empty string, start tracing
+ Python memory allocations using the :mod:`tracemalloc` module. The value of
+ the variable is the maximum number of frames stored in a traceback of a
+ trace. For example, ``PYTHONTRACEMALLOC=1`` stores only the most recent
+ frame. See the :func:`tracemalloc.start` for more information.
+
+ .. versionadded:: 3.4
+
+
+.. envvar:: PYTHONASYNCIODEBUG
+
+ If this environment variable is set to a non-empty string, enable the debug
+ mode of the :mod:`asyncio` module.
+
+ .. versionadded:: 3.4
+
+
Debug-mode variables
~~~~~~~~~~~~~~~~~~~~
diff --git a/Doc/using/mac.rst b/Doc/using/mac.rst
index 3e1b74d..5be439f 100644
--- a/Doc/using/mac.rst
+++ b/Doc/using/mac.rst
@@ -25,7 +25,7 @@ there.
What you get after installing is a number of things:
-* A :file:`MacPython 3.3` folder in your :file:`Applications` folder. In here
+* A :file:`MacPython 3.4` folder in your :file:`Applications` folder. In here
you find IDLE, the development environment that is a standard part of official
Python distributions; PythonLauncher, which handles double-clicking Python
scripts from the Finder; and the "Build Applet" tool, which allows you to
@@ -93,7 +93,7 @@ aware of: programs that talk to the Aqua window manager (in other words,
anything that has a GUI) need to be run in a special way. Use :program:`pythonw`
instead of :program:`python` to start such scripts.
-With Python 3.3, you can use either :program:`python` or :program:`pythonw`.
+With Python 3.4, you can use either :program:`python` or :program:`pythonw`.
Configuration
@@ -158,7 +158,7 @@ http://www.riverbankcomputing.co.uk/software/pyqt/intro.
Distributing Python Applications on the Mac
===========================================
-The "Build Applet" tool that is placed in the MacPython 3.3 folder is fine for
+The "Build Applet" tool that is placed in the MacPython 3.4 folder is fine for
packaging small Python scripts on your own machine to run as a standard Mac
application. This tool, however, is not robust enough to distribute Python
applications to other users.
diff --git a/Doc/using/unix.rst b/Doc/using/unix.rst
index 40635c6..3c7fc41 100644
--- a/Doc/using/unix.rst
+++ b/Doc/using/unix.rst
@@ -60,6 +60,8 @@ To install the newest Python versions on OpenSolaris, install `blastwave
prompt.
+.. _building-python-on-unix:
+
Building Python
===============
diff --git a/Doc/using/venv-create.inc b/Doc/using/venv-create.inc
index 5fdbc9b..52cdda0 100644
--- a/Doc/using/venv-create.inc
+++ b/Doc/using/venv-create.inc
@@ -11,6 +11,11 @@ containing a copy of the ``python`` binary (or binaries, in the case of
Windows). It also creates an (initially empty) ``lib/pythonX.Y/site-packages``
subdirectory (on Windows, this is ``Lib\site-packages``).
+.. seealso::
+
+ `Python Packaging User Guide: Creating and using virtual environments
+ <http://packaging.python.org/en/latest/tutorial.html#creating-and-using-virtual-environments>`__
+
.. highlight:: none
On Windows, you may have to invoke the ``pyvenv`` script as follows, if you
@@ -25,7 +30,7 @@ or equivalently::
The command, if run with ``-h``, will show the available options::
usage: pyvenv [-h] [--system-site-packages] [--symlinks] [--clear]
- [--upgrade] ENV_DIR [ENV_DIR ...]
+ [--upgrade] [--without-pip] ENV_DIR [ENV_DIR ...]
Creates virtual Python environments in one or more target directories.
@@ -38,11 +43,19 @@ The command, if run with ``-h``, will show the available options::
virtual environment.
--symlinks Try to use symlinks rather than copies, when symlinks
are not the default for the platform.
+ --copies Try to use copies rather than symlinks, even when
+ symlinks are the default for the platform.
--clear Delete the environment directory if it already exists.
If not specified and the directory exists, an error is
raised.
--upgrade Upgrade the environment directory to use this version
of Python, assuming Python has been upgraded in-place.
+ --without-pip Skips installing or upgrading pip in the virtual
+ environment (pip is bootstrapped by default)
+
+.. versionchanged:: 3.4
+ Installs pip by default, added the ``--without-pip`` and ``--copies``
+ options
If the target directory already exists an error will be raised, unless
the ``--clear`` or ``--upgrade`` option was provided.
@@ -51,25 +64,29 @@ The created ``pyvenv.cfg`` file also includes the
``include-system-site-packages`` key, set to ``true`` if ``venv`` is
run with the ``--system-site-packages`` option, ``false`` otherwise.
+Unless the ``--without-pip`` option is given, :mod:`ensurepip` will be
+invoked to bootstrap ``pip`` into the virtual environment.
+
Multiple paths can be given to ``pyvenv``, in which case an identical
virtualenv will be created, according to the given options, at each
provided path.
Once a venv has been created, it can be "activated" using a script in the
-venv's binary directory. The invocation of the script is platform-specific: on
-a Posix platform, you would typically do::
-
- $ source <venv>/bin/activate
-
-whereas on Windows, you might do::
-
- C:\> <venv>/Scripts/activate
-
-if you are using the ``cmd.exe`` shell, or perhaps::
-
- PS C:\> <venv>/Scripts/Activate.ps1
-
-if you use PowerShell.
+venv's binary directory. The invocation of the script is platform-specific:
+
++-------------+-----------------+-----------------------------------------+
+| Platform | Shell | Command to activate virtual environment |
++=============+=================+=========================================+
+| Posix | bash/zsh | $ source <venv>/bin/activate |
++-------------+-----------------+-----------------------------------------+
+| | fish | $ . <venv>/bin/activate.fish |
++-------------+-----------------+-----------------------------------------+
+| | csh/tcsh | $ source <venv>/bin/activate.csh |
++-------------+-----------------+-----------------------------------------+
+| Windows | cmd.exe | C:\> <venv>/Scripts/activate.bat |
++-------------+-----------------+-----------------------------------------+
+| | PowerShell | PS C:\> <venv>/Scripts/Activate.ps1 |
++-------------+-----------------+-----------------------------------------+
You don't specifically *need* to activate an environment; activation just
prepends the venv's binary directory to your path, so that "python" invokes the
@@ -83,3 +100,5 @@ a "deactivate" function, whereas on Windows there are separate scripts called
``deactivate.bat`` and ``Deactivate.ps1`` which are installed when the venv is
created.
+.. versionadded:: 3.4
+ ``fish`` and ``csh`` activation scripts.
diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst
index 6c4d16e..42a92af 100644
--- a/Doc/using/windows.rst
+++ b/Doc/using/windows.rst
@@ -11,6 +11,10 @@
This document aims to give an overview of Windows-specific behaviour you should
know about when using Python on Microsoft Windows.
+.. XXX (ncoghlan)
+
+ This looks rather stale to me...
+
Installing Python
=================
@@ -560,12 +564,6 @@ View the :file:`readme.txt` in their respective directories:
+--------------------+--------------+-----------------------+
| Directory | MSVC version | Visual Studio version |
+====================+==============+=======================+
-| :file:`PC/VC6/` | 6.0 | 97 |
-+--------------------+--------------+-----------------------+
-| :file:`PC/VS7.1/` | 7.1 | 2003 |
-+--------------------+--------------+-----------------------+
-| :file:`PC/VS8.0/` | 8.0 | 2005 |
-+--------------------+--------------+-----------------------+
| :file:`PC/VS9.0/` | 9.0 | 2008 |
+--------------------+--------------+-----------------------+
| :file:`PCbuild/` | 10.0 | 2010 |
diff --git a/Doc/whatsnew/3.4.rst b/Doc/whatsnew/3.4.rst
new file mode 100644
index 0000000..177208a
--- /dev/null
+++ b/Doc/whatsnew/3.4.rst
@@ -0,0 +1,2506 @@
+****************************
+ What's New In Python 3.4
+****************************
+
+:Author: R. David Murray <rdmurray@bitdance.com> (Editor)
+
+.. Rules for maintenance:
+
+ * Anyone can add text to this document, but the maintainer reserves the
+ right to rewrite any additions. In particular, for obscure or esoteric
+ features, the maintainer may reduce any addition to a simple reference to
+ the new documentation rather than explaining the feature inline.
+
+ * While the maintainer will periodically go through Misc/NEWS
+ and add changes, it's best not to rely on this. We know from experience
+ that any changes that aren't in the What's New documentation around the
+ time of the original release will remain largely unknown to the community
+ for years, even if they're added later. We also know from experience that
+ other priorities can arise, and the maintainer will run out of time to do
+ updates -- in such cases, end users will be much better served by partial
+ notifications that at least give a hint about new features to
+ investigate.
+
+ * This is not a complete list of every single change; completeness
+ is the purpose of Misc/NEWS. The What's New should focus on changes that
+ are visible to Python *users* and that *require* a feature release (i.e.
+ most bug fixes should only be recorded in Misc/NEWS)
+
+ * PEPs should not be marked Final until they have an entry in What's New.
+ A placeholder entry that is just a section header and a link to the PEP
+ (e.g ":pep:`397` has been implemented") is acceptable. If a PEP has been
+ implemented and noted in What's New, don't forget to mark it as Final!
+
+ * 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 add just a very brief note about a change. For
+ example: "The :ref:`~socket.transmogrify()` function was added to the
+ :mod:`socket` module." The maintainer will research the change and
+ write the necessary text (if appropriate). The advantage of doing this
+ is that even if no more descriptive text is ever added, readers will at
+ least have a notification that the new feature exists and a link to the
+ relevant documentation.
+
+ * 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:
+
+ The :ref:`~socket.transmogrify()` function was added to the
+ :mod:`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.
+
+ * Cross referencing tip: :ref:`mod.attr` will display as ``mod.attr``,
+ while :ref:`~mod.attr` will display as ``attr``.
+
+This article explains the new features in Python 3.4, compared to 3.3.
+Python 3.4 was released on March 16, 2014. For full details, see the
+`changelog <http://docs.python.org/3.4/whatsnew/changelog.html>`_.
+
+
+.. seealso::
+
+ :pep:`429` -- Python 3.4 Release Schedule
+
+
+
+Summary -- Release Highlights
+=============================
+
+.. This section singles out the most important changes in Python 3.4.
+ Brevity is key.
+
+New syntax features:
+
+* No new syntax features were added in Python 3.4.
+
+Other new features:
+
+* :ref:`pip should always be available <whatsnew-pep-453>` (:pep:`453`).
+* :ref:`Newly created file descriptors are non-inheritable <whatsnew-pep-446>`
+ (:pep:`446`).
+* command line option for :ref:`isolated mode <whatsnew-isolated-mode>`
+ (:issue:`16499`).
+* :ref:`improvements in the handling of codecs <codec-handling-improvements>`
+ that are not text encodings (multiple issues).
+* :ref:`A ModuleSpec Type <whatsnew-pep-451>` for the Import System
+ (:pep:`451`). (Affects importer authors.)
+* The :mod:`marshal` format has been made :ref:`more compact and efficient
+ <whatsnew-marshal-3>` (:issue:`16475`).
+
+New library modules:
+
+* :mod:`asyncio`: :ref:`New provisional API for asynchronous IO
+ <whatsnew-asyncio>` (:pep:`3156`).
+* :mod:`ensurepip`: :ref:`Bootstrapping the pip installer <whatsnew-ensurepip>`
+ (:pep:`453`).
+* :mod:`enum`: :ref:`Support for enumeration types <whatsnew-enum>`
+ (:pep:`435`).
+* :mod:`pathlib`: :ref:`Object-oriented filesystem paths <whatsnew-pathlib>`
+ (:pep:`428`).
+* :mod:`selectors`: :ref:`High-level and efficient I/O multiplexing
+ <whatsnew-selectors>`, built upon the :mod:`select` module primitives (part
+ of :pep:`3156`).
+* :mod:`statistics`: A basic :ref:`numerically stable statistics library
+ <whatsnew-statistics>` (:pep:`450`).
+* :mod:`tracemalloc`: :ref:`Trace Python memory allocations
+ <whatsnew-tracemalloc>` (:pep:`454`).
+
+Significantly improved library modules:
+
+* :ref:`Single-dispatch generic functions <whatsnew-singledispatch>` in
+ :mod:`functools` (:pep:`443`).
+* New :mod:`pickle` :ref:`protocol 4 <whatsnew-protocol-4>` (:pep:`3154`).
+* :mod:`multiprocessing` now has :ref:`an option to avoid using os.fork
+ on Unix <whatsnew-multiprocessing-no-fork>` (:issue:`8713`).
+* :mod:`email` has a new submodule, :mod:`~email.contentmanager`, and
+ a new :mod:`~email.message.Message` subclass
+ (:class:`~email.contentmanager.EmailMessage`) that :ref:`simplify MIME
+ handling <whatsnew_email_contentmanager>` (:issue:`18891`).
+* The :mod:`inspect` and :mod:`pydoc` modules are now capable of
+ correct introspection of a much wider variety of callable objects,
+ which improves the output of the Python :func:`help` system.
+* The :mod:`ipaddress` module API has been declared stable
+
+Security improvements:
+
+* :ref:`Secure and interchangeable hash algorithm <whatsnew-pep-456>`
+ (:pep:`456`).
+* :ref:`Make newly created file descriptors non-inheritable <whatsnew-pep-446>`
+ (:pep:`446`) to avoid leaking file descriptors to child processes.
+* New command line option for :ref:`isolated mode <whatsnew-isolated-mode>`,
+ (:issue:`16499`).
+* :mod:`multiprocessing` now has :ref:`an option to avoid using os.fork
+ on Unix <whatsnew-multiprocessing-no-fork>`. *spawn* and *forkserver* are
+ more secure because they avoid sharing data with child processes.
+* :mod:`multiprocessing` child processes on Windows no longer inherit
+ all of the parent's inheritable handles, only the necessary ones.
+* A new :func:`hashlib.pbkdf2_hmac` function provides
+ the `PKCS#5 password-based key derivation function 2
+ <http://en.wikipedia.org/wiki/PBKDF2>`_.
+* :ref:`TLSv1.1 and TLSv1.2 support <whatsnew-tls-11-12>` for :mod:`ssl`.
+* :ref:`Retrieving certificates from the Windows system cert store support
+ <whatsnew34-win-cert-store>` for :mod:`ssl`.
+* :ref:`Server-side SNI (Server Name Indication) support
+ <whatsnew34-sni>` for :mod:`ssl`.
+* The :class:`ssl.SSLContext` class has a :ref:`lot of improvements
+ <whatsnew34-sslcontext>`.
+* All modules in the standard library that support SSL now support server
+ certificate verification, including hostname matching
+ (:func:`ssl.match_hostname`) and CRLs (Certificate Revocation lists, see
+ :func:`ssl.SSLContext.load_verify_locations`).
+
+CPython implementation improvements:
+
+* :ref:`Safe object finalization <whatsnew-pep-442>` (:pep:`442`).
+* Leveraging :pep:`442`, in most cases :ref:`module globals are no longer set
+ to None during finalization <whatsnew-pep-442>` (:issue:`18214`).
+* :ref:`Configurable memory allocators <whatsnew-pep-445>` (:pep:`445`).
+* :ref:`Argument Clinic <whatsnew-pep-436>` (:pep:`436`).
+
+Please read on for a comprehensive list of user-facing changes, including many
+other smaller improvements, CPython optimizations, deprecations, and potential
+porting issues.
+
+
+
+New Features
+============
+
+.. _whatsnew-pep-453:
+
+PEP 453: Explicit Bootstrapping of PIP in Python Installations
+--------------------------------------------------------------
+
+Bootstrapping pip By Default
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The new :mod:`ensurepip` module (defined in :pep:`453`) provides a standard
+cross-platform mechanism to bootstrap the pip installer into Python
+installations and virtual environments. The version of ``pip`` included
+with Python 3.4.0 is ``pip`` 1.5.4, and future 3.4.x maintenance releases
+will update the bundled version to the latest version of ``pip`` that is
+available at the time of creating the release candidate.
+
+By default, the commands ``pipX`` and ``pipX.Y`` will be installed on all
+platforms (where X.Y stands for the version of the Python installation),
+along with the ``pip`` Python package and its dependencies. On Windows and
+in virtual environments on all platforms, the unversioned ``pip`` command
+will also be installed. On other platforms, the system wide unversioned
+``pip`` command typically refers to the separately installed Python 2
+version.
+
+The :ref:`pyvenv <scripts-pyvenv>` command line utility and the :mod:`venv`
+module make use of the :mod:`ensurepip` module to make ``pip`` readily
+available in virtual environments. When using the command line utility,
+``pip`` is installed by default, while when using the :mod:`venv` module
+:ref:`venv-api` installation of ``pip`` must be requested explicitly.
+
+For CPython :ref:`source builds on POSIX systems <building-python-on-unix>`,
+the ``make install`` and ``make altinstall`` commands bootstrap ``pip`` by
+default. This behaviour can be controlled through configure options, and
+overridden through Makefile options.
+
+On Windows and Mac OS X, the CPython installers now default to installing
+``pip`` along with CPython itself (users may opt out of installing it
+during the installation process). Window users will need to opt in to the
+automatic ``PATH`` modifications to have ``pip`` available from the command
+line by default, otherwise it can still be accessed through the Python
+launcher for Windows as ``py -m pip``.
+
+As `discussed in the PEP`__, platform packagers may choose not to install
+these commands by default, as long as, when invoked, they provide clear and
+simple directions on how to install them on that platform (usually using
+the system package manager).
+
+__ http://www.python.org/dev/peps/pep-0453/#recommendations-for-downstream-distributors
+
+.. note::
+
+ To avoid conflicts between parallel Python 2 and Python 3 installations,
+ only the versioned ``pip3`` and ``pip3.4`` commands are bootstrapped by
+ default when ``ensurepip`` is invoked directly - the ``--default-pip``
+ option is needed to also request the unversioned ``pip`` command.
+ ``pyvenv`` and the Windows installer ensure that the unqualified ``pip``
+ command is made available in those environments, and ``pip`` can always be
+ invoked via the ``-m`` switch rather than directly to avoid ambiguity on
+ systems with multiple Python installations.
+
+
+Documentation Changes
+~~~~~~~~~~~~~~~~~~~~~
+
+As part of this change, the :ref:`installing-index` and
+:ref:`distributing-index` sections of the documentation have been
+completely redesigned as short getting started and FAQ documents. Most
+packaging documentation has now been moved out to the Python Packaging
+Authority maintained `Python Packaging User Guide
+<http://packaging.python.org>`__ and the documentation of the individual
+projects.
+
+However, as this migration is currently still incomplete, the legacy
+versions of those guides remaining available as :ref:`install-index`
+and :ref:`distutils-index`.
+
+.. seealso::
+
+ :pep:`453` -- Explicit bootstrapping of pip in Python installations
+ PEP written by Donald Stufft and Nick Coghlan, implemented by
+ Donald Stufft, Nick Coghlan, Martin von Löwis and Ned Deily.
+
+
+.. _whatsnew-pep-446:
+
+PEP 446: Newly Created File Descriptors Are Non-Inheritable
+-----------------------------------------------------------
+
+:pep:`446` makes newly created file descriptors :ref:`non-inheritable
+<fd_inheritance>`. In general, this is the behavior an application will
+want: when launching a new process, having currently open files also
+open in the new process can lead to all sorts of hard to find bugs,
+and potentially to security issues.
+
+However, there are occasions when inheritance is desired. To support
+these cases, the following new functions and methods are available:
+
+* :func:`os.get_inheritable`, :func:`os.set_inheritable`
+* :func:`os.get_handle_inheritable`, :func:`os.set_handle_inheritable`
+* :meth:`socket.socket.get_inheritable`, :meth:`socket.socket.set_inheritable`
+
+.. seealso::
+
+ :pep:`446` -- Make newly created file descriptors non-inheritable
+ PEP written and implemented by Victor Stinner.
+
+
+.. _codec-handling-improvements:
+
+Improvements to Codec Handling
+------------------------------
+
+Since it was first introduced, the :mod:`codecs` module has always been
+intended to operate as a type-neutral dynamic encoding and decoding
+system. However, its close coupling with the Python text model, especially
+the type restricted convenience methods on the builtin :class:`str`,
+:class:`bytes` and :class:`bytearray` types, has historically obscured that
+fact.
+
+As a key step in clarifying the situation, the :meth:`codecs.encode` and
+:meth:`codecs.decode` convenience functions are now properly documented in
+Python 2.7, 3.3 and 3.4. These functions have existed in the :mod:`codecs`
+module (and have been covered by the regression test suite) since Python 2.4,
+but were previously only discoverable through runtime introspection.
+
+Unlike the convenience methods on :class:`str`, :class:`bytes` and
+:class:`bytearray`, the :mod:`codecs` convenience functions support arbitrary
+codecs in both Python 2 and Python 3, rather than being limited to Unicode text
+encodings (in Python 3) or ``basestring`` <-> ``basestring`` conversions (in
+Python 2).
+
+In Python 3.4, the interpreter is able to identify the known non-text
+encodings provided in the standard library and direct users towards these
+general purpose convenience functions when appropriate::
+
+ >>> b"abcdef".decode("hex")
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+ LookupError: 'hex' is not a text encoding; use codecs.decode() to handle arbitrary codecs
+
+ >>> "hello".encode("rot13")
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+ LookupError: 'rot13' is not a text encoding; use codecs.encode() to handle arbitrary codecs
+
+ >>> open("foo.txt", encoding="hex")
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+ LookupError: 'hex' is not a text encoding; use codecs.open() to handle arbitrary codecs
+
+In a related change, whenever it is feasible without breaking backwards
+compatibility, exceptions raised during encoding and decoding operations
+are wrapped in a chained exception of the same type that mentions the
+name of the codec responsible for producing the error::
+
+ >>> import codecs
+
+ >>> codecs.decode(b"abcdefgh", "hex")
+ Traceback (most recent call last):
+ File "/usr/lib/python3.4/encodings/hex_codec.py", line 20, in hex_decode
+ return (binascii.a2b_hex(input), len(input))
+ binascii.Error: Non-hexadecimal digit found
+
+ The above exception was the direct cause of the following exception:
+
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+ binascii.Error: decoding with 'hex' codec failed (Error: Non-hexadecimal digit found)
+
+ >>> codecs.encode("hello", "bz2")
+ Traceback (most recent call last):
+ File "/usr/lib/python3.4/encodings/bz2_codec.py", line 17, in bz2_encode
+ return (bz2.compress(input), len(input))
+ File "/usr/lib/python3.4/bz2.py", line 498, in compress
+ return comp.compress(data) + comp.flush()
+ TypeError: 'str' does not support the buffer interface
+
+ The above exception was the direct cause of the following exception:
+
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+ TypeError: encoding with 'bz2' codec failed (TypeError: 'str' does not support the buffer interface)
+
+Finally, as the examples above show, these improvements have permitted
+the restoration of the convenience aliases for the non-Unicode codecs that
+were themselves restored in Python 3.2. This means that encoding binary data
+to and from its hexadecimal representation (for example) can now be written
+as::
+
+ >>> from codecs import encode, decode
+ >>> encode(b"hello", "hex")
+ b'68656c6c6f'
+ >>> decode(b"68656c6c6f", "hex")
+ b'hello'
+
+The binary and text transforms provided in the standard library are detailed
+in :ref:`binary-transforms` and :ref:`text-transforms`.
+
+(Contributed by Nick Coghlan in :issue:`7475`, :issue:`17827`,
+:issue:`17828` and :issue:`19619`)
+
+
+.. _whatsnew-pep-451:
+
+PEP 451: A ModuleSpec Type for the Import System
+------------------------------------------------
+
+:pep:`451` provides an encapsulation of the information about a module that the
+import machinery will use to load it (that is, a module specification). This
+helps simplify both the import implementation and several import-related APIs.
+The change is also a stepping stone for `several future import-related
+improvements`__.
+
+__ https://mail.python.org/pipermail/python-dev/2013-November/130111.html
+
+The public-facing changes from the PEP are entirely backward-compatible.
+Furthermore, they should be transparent to everyone but importer authors. Key
+finder and loader methods have been deprecated, but they will continue working.
+New importers should use the new methods described in the PEP. Existing
+importers should be updated to implement the new methods. See the
+:ref:`deprecated-3.4` section for a list of methods that should be replaced and
+their replacements.
+
+
+Other Language Changes
+----------------------
+
+Some smaller changes made to the core Python language are:
+
+* Unicode database updated to UCD version 6.3.
+
+* :func:`min` and :func:`max` now accept a *default* keyword-only argument that
+ can be used to specify the value they return if the iterable they are
+ evaluating has no elements. (Contributed by Julian Berman in
+ :issue:`18111`.)
+
+* Module objects are now :mod:`weakref`'able.
+
+* Module ``__file__`` attributes (and related values) should now always
+ contain absolute paths by default, with the sole exception of
+ ``__main__.__file__`` when a script has been executed directly using
+ a relative path (Contributed by Brett Cannon in :issue:`18416`).
+
+* All the UTF-\* codecs (except UTF-7) now reject surrogates during both
+ encoding and decoding unless the ``surrogatepass`` error handler is used,
+ with the exception of the UTF-16 decoder (which accepts valid surrogate pairs)
+ and the UTF-16 encoder (which produces them while encoding non-BMP characters).
+ Contributed by Victor Stinner, Kang-Hao (Kenny) Lu and Serhiy Storchaka in
+ :issue:`12892`.
+
+* New German EBCDIC :ref:`codec <standard-encodings>` ``cp273``. (Contributed
+ by Michael Bierenfeld and Andrew Kuchling in :issue:`1097797`.)
+
+* New Ukrainian :ref:`codec <standard-encodings>` ``cp1125``. (Contributed by
+ Serhiy Storchaka in :issue:`19668`.)
+
+* :class:`bytes`.join() and :class:`bytearray`.join() now accept arbitrary
+ buffer objects as arguments. (Contributed by Antoine Pitrou in
+ :issue:`15958`.)
+
+* The :class:`int` constructor now accepts any object that has an ``__index__``
+ method for its *base* argument. (Contributed by Mark Dickinson in
+ :issue:`16772`.)
+
+* Frame objects now have a :func:`~frame.clear` method that clears all
+ references to local variables from the frame. (Contributed by Antoine Pitrou
+ in :issue:`17934`.)
+
+* :class:`memoryview` is now registered as a :class:`Sequence <collections.abc>`,
+ and supports the :func:`reversed` builtin. (Contributed by Nick Coghlan
+ and Claudiu Popa in :issue:`18690` and :issue:`19078`.)
+
+* Signatures reported by :func:`help` have been modified and improved in
+ several cases as a result of the introduction of Argument Clinic and other
+ changes to the :mod:`inspect` and :mod:`pydoc` modules.
+
+* :meth:`~object.__length_hint__` is now part of the formal language
+ specification (see :pep:`424`). (Contributed by Armin Ronacher in
+ :issue:`16148`.)
+
+
+New Modules
+===========
+
+
+.. _whatsnew-asyncio:
+
+asyncio
+-------
+
+The new :mod:`asyncio` module (defined in :pep:`3156`) provides a standard
+pluggable event loop model for Python, providing solid asynchronous IO
+support in the standard library, and making it easier for other event loop
+implementations to interoperate with the standard library and each other.
+
+For Python 3.4, this module is considered a :term:`provisional API`.
+
+.. seealso::
+
+ :pep:`3156` -- Asynchronous IO Support Rebooted: the "asyncio" Module
+ PEP written and implementation led by Guido van Rossum.
+
+
+.. _whatsnew-ensurepip:
+
+ensurepip
+---------
+
+The new :mod:`ensurepip` module is the primary infrastructure for the
+:pep:`453` implementation. In the normal course of events end users will not
+need to interact with this module, but it can be used to manually bootstrap
+``pip`` if the automated bootstrapping into an installation or virtual
+environment was declined.
+
+:mod:`ensurepip` includes a bundled copy of ``pip``, up-to-date as of the first
+release candidate of the release of CPython with which it ships (this applies
+to both maintenance releases and feature releases). ``ensurepip`` does not
+access the internet. If the installation has Internet access, after
+``ensurepip`` is run the bundled ``pip`` can be used to upgrade ``pip`` to a
+more recent release than the bundled one. (Note that such an upgraded version
+of ``pip`` is considered to be a separately installed package and will not be
+removed if Python is uninstalled.)
+
+The module is named *ensure*\ pip because if called when ``pip`` is already
+installed, it does nothing. It also has an ``--upgrade`` option that will
+cause it to install the bundled copy of ``pip`` if the existing installed
+version of ``pip`` is older than the bundled copy.
+
+
+.. _whatsnew-enum:
+
+enum
+----
+
+The new :mod:`enum` module (defined in :pep:`435`) provides a standard
+implementation of enumeration types, allowing other modules (such as
+:mod:`socket`) to provide more informative error messages and better
+debugging support by replacing opaque integer constants with backwards
+compatible enumeration values.
+
+.. seealso::
+
+ :pep:`435` -- Adding an Enum type to the Python standard library
+ PEP written by Barry Warsaw, Eli Bendersky and Ethan Furman,
+ implemented by Ethan Furman.
+
+
+.. _whatsnew-pathlib:
+
+pathlib
+-------
+
+The new :mod:`pathlib` module offers classes representing filesystem paths
+with semantics appropriate for different operating systems. Path classes are
+divided between *pure paths*, which provide purely computational operations
+without I/O, and *concrete paths*, which inherit from pure paths but also
+provide I/O operations.
+
+For Python 3.4, this module is considered a :term:`provisional API`.
+
+.. seealso::
+
+ :pep:`428` -- The pathlib module -- object-oriented filesystem paths
+ PEP written and implemented by Antoine Pitrou.
+
+
+.. _whatsnew-selectors:
+
+selectors
+---------
+
+The new :mod:`selectors` module (created as part of implementing :pep:`3156`)
+allows high-level and efficient I/O multiplexing, built upon the
+:mod:`select` module primitives.
+
+
+.. _whatsnew-statistics:
+
+statistics
+----------
+
+The new :mod:`statistics` module (defined in :pep:`450`) offers some core
+statistics functionality directly in the standard library. This module
+supports calculation of the mean, median, mode, variance and standard
+deviation of a data series.
+
+.. seealso::
+
+ :pep:`450` -- Adding A Statistics Module To The Standard Library
+ PEP written and implemented by Steven D'Aprano
+
+.. _whatsnew-tracemalloc:
+
+
+tracemalloc
+-----------
+
+The new :mod:`tracemalloc` module (defined in :pep:`454`) is a debug tool to
+trace memory blocks allocated by Python. It provides the following information:
+
+* Trace where an object was allocated
+* Statistics on allocated memory blocks per filename and per line number:
+ total size, number and average size of allocated memory blocks
+* Compute the differences between two snapshots to detect memory leaks
+
+.. seealso::
+
+ :pep:`454` -- Add a new tracemalloc module to trace Python memory allocations
+ PEP written and implemented by Victor Stinner
+
+
+
+Improved Modules
+================
+
+
+abc
+---
+
+New function :func:`abc.get_cache_token` can be used to know when to invalidate
+caches that are affected by changes in the object graph. (Contributed
+by Łukasz Langa in :issue:`16832`.)
+
+New class :class:`~abc.ABC` has :class:`~abc.ABCMeta` as its meta class.
+Using ``ABC`` as a base class has essentially the same effect as specifying
+``metaclass=abc.ABCMeta``, but is simpler to type and easier to read.
+(Contributed by Bruno Dupuis in :issue:`16049`.)
+
+
+aifc
+----
+
+The :meth:`~aifc.aifc.getparams` method now returns a namedtuple rather than a
+plain tuple. (Contributed by Claudiu Popa in :issue:`17818`.)
+
+:func:`aifc.open` now supports the context manager protocol: when used in a
+:keyword:`with` block, the :meth:`~aifc.aifc.close` method of the returned
+object will be called automatically at the end of the block. (Contributed by
+Serhiy Storchacha in :issue:`16486`.)
+
+The :meth:`~aifc.aifc.writeframesraw` and :meth:`~aifc.aifc.writeframes`
+methods now accept any :term:`bytes-like object`. (Contributed by Serhiy
+Storchaka in :issue:`8311`.)
+
+
+argparse
+--------
+
+The :class:`~argparse.FileType` class now accepts *encoding* and
+*errors* arguments, which are passed through to :func:`open`. (Contributed
+by Lucas Maystre in :issue:`11175`.)
+
+
+audioop
+-------
+
+:mod:`audioop` now supports 24-bit samples. (Contributed by Serhiy Storchaka
+in :issue:`12866`.)
+
+New :func:`~audioop.byteswap` function converts big-endian samples to
+little-endian and vice versa (Contributed by Serhiy Storchaka in
+:issue:`19641`).
+
+All :mod:`audioop` functions now accept any :term:`bytes-like object`. Strings
+are not accepted: they didn't work before, now they raise an error right away.
+(Contributed by Serhiy Storchaka in :issue:`16685`.)
+
+
+base64
+------
+
+The encoding and decoding functions in :mod:`base64` now accept any
+:term:`bytes-like object` in cases where it previously required a
+:class:`bytes` or :class:`bytearray` instance. (Contributed by Nick Coghlan in
+:issue:`17839`.)
+
+New functions :func:`~base64.a85encode`, :func:`~base64.a85decode`,
+:func:`~base64.b85encode`, and :func:`~base64.b85decode` provide the ability to
+encode and decode binary data from and to ``Ascii85`` and the git/mercurial
+``Base85`` formats, respectively. The ``a85`` functions have options that can
+be used to make them compatible with the variants of the ``Ascii85`` encoding,
+including the Adobe variant. (Contributed by Martin Morrison, the Mercurial
+project, Serhiy Storchaka, and Antoine Pitrou in :issue:`17618`.)
+
+
+collections
+-----------
+
+The :meth:`.ChainMap.new_child` method now accepts an *m* argument specifying
+the child map to add to the chain. This allows an existing mapping and/or a
+custom mapping type to be used for the child. (Contributed by Vinay Sajip in
+:issue:`16613`.)
+
+
+colorsys
+--------
+
+The number of digits in the coefficients for the RGB --- YIQ conversions have
+been expanded so that they match the FCC NTSC versions. The change in
+results should be less than 1% and may better match results found elsewhere.
+(Contributed by Brian Landers and Serhiy Storchaka in :issue:`14323`.)
+
+
+contextlib
+----------
+
+The new :class:`contextlib.suppress` context manager helps to clarify the
+intent of code that deliberately suppresses exceptions from a single
+statement. (Contributed by Raymond Hettinger in :issue:`15806` and
+Zero Piraeus in :issue:`19266`)
+
+The new :func:`contextlib.redirect_stdout` context manager makes it easier
+for utility scripts to handle inflexible APIs that write their output to
+:data:`sys.stdout` and don't provide any options to redirect it. Using the
+context manager, the :data:`sys.stdout` output can be redirected to any
+other stream or, in conjunction with :class:`io.StringIO`, to a string.
+The latter can be especially useful, for example, to capture output
+from a function that was written to implement a command line interface.
+It is recommended only for utility scripts because it affects the
+global state of :data:`sys.stdout`. (Contributed by Raymond Hettinger
+in :issue:`15805`)
+
+The :mod:`contextlib` documentation has also been updated to include a
+:ref:`discussion <single-use-reusable-and-reentrant-cms>` of the
+differences between single use, reusable and reentrant context managers.
+
+
+dbm
+---
+
+:func:`dbm.open` objects now support the context management protocol. When
+used in a :keyword:`with` statement, the ``close`` method of the database
+object will be called automatically at the end of the block. (Contributed by
+Claudiu Popa and Nick Coghlan in :issue:`19282`.)
+
+
+dis
+---
+
+Functions :func:`~dis.show_code`, :func:`~dis.dis`, :func:`~dis.distb`, and
+:func:`~dis.disassemble` now accept a keyword-only *file* argument that
+controls where they write their output.
+
+The :mod:`dis` module is now built around an :class:`~dis.Instruction` class
+that provides object oriented access to the details of each individual bytecode
+operation.
+
+A new method, :func:`~dis.get_instructions`, provides an iterator that emits
+the Instruction stream for a given piece of Python code. Thus it is now
+possible to write a program that inspects and manipulates a bytecode
+object in ways different from those provided by the :mod:`~dis` module
+itself. For example::
+
+ >>> import dis
+ >>> for instr in dis.get_instructions(lambda x: x + 1):
+ ... print(instr.opname)
+ LOAD_FAST
+ LOAD_CONST
+ BINARY_ADD
+ RETURN_VALUE
+
+The various display tools in the :mod:`dis` module have been rewritten to use
+these new components.
+
+In addition, a new application-friendly class :class:`~dis.Bytecode` provides
+an object-oriented API for inspecting bytecode in both in human-readable form
+and for iterating over instructions. The :class:`~dis.Bytecode` constructor
+takes the same arguments that :func:`~dis.get_instruction` does (plus an
+optional *current_offset*), and the resulting object can be iterated to produce
+:class:`~dis.Instruction` objects. But it also has a :mod:`~dis.Bytecode.dis`
+method, equivalent to calling :mod:`~dis.dis` on the constructor argument, but
+returned as a multi-line string::
+
+ >>> bytecode = dis.Bytecode(lambda x: x +1, current_offset=3)
+ >>> for instr in bytecode:
+ ... print('{} ({})'.format(instr.opname, instr.opcode))
+ LOAD_FAST (124)
+ LOAD_CONST (100)
+ BINARY_ADD (23)
+ RETURN_VALUE (83)
+ >>> bytecode.dis().splitlines() # doctest: +NORMALIZE_WHITESPACE
+ [' 1 0 LOAD_FAST 0 (x)',
+ ' --> 3 LOAD_CONST 1 (1)',
+ ' 6 BINARY_ADD',
+ ' 7 RETURN_VALUE']
+
+:class:`~dis.Bytecode` also has a class method,
+:meth:`~dis.Bytecode.from_traceback`, that provides the ability to manipulate a
+traceback (that is, ``print(Bytecode.from_traceback(tb).dis())`` is equivalent
+to ``distb(tb)``).
+
+(Contributed by Nick Coghlan, Ryan Kelly and Thomas Kluyver in :issue:`11816`
+and Claudiu Popa in :issue:`17916`)
+
+New function :func:`~dis.stack_effect` computes the effect on the Python stack
+of a given opcode and argument, information that is not otherwise available.
+(Contributed by Larry Hastings in :issue:`19722`.)
+
+
+doctest
+-------
+
+A new :ref:`option flag <doctest-options>`, :data:`~doctest.FAIL_FAST`, halts
+test running as soon as the first failure is detected. (Contributed by R.
+David Murray and Daniel Urban in :issue:`16522`.)
+
+The :mod:`doctest` command line interface now uses :mod:`argparse`, and has two
+new options, ``-o`` and ``-f``. ``-o`` allows :ref:`doctest options
+<doctest-options>` to be specified on the command line, and ``-f`` is a
+shorthand for ``-o FAIL_FAST`` (to parallel the similar option supported by the
+:mod:`unittest` CLI). (Contributed by R. David Murray in :issue:`11390`.)
+
+:mod:`doctest` will now find doctests in extension module ``__doc__`` strings.
+(Contributed by Zachary Ware in :issue:`3158`.)
+
+
+email
+-----
+
+:meth:`~email.message.Message.as_string` now accepts a *policy* argument to
+override the default policy of the message when generating a string
+representation of it. This means that ``as_string`` can now be used in more
+circumstances, instead of having to create and use a :mod:`~email.generator` in
+order to pass formatting parameters to its ``flatten`` method. (Contributed by
+R. David Murray in :issue:`18600`.)
+
+New method :meth:`~email.message.Message.as_bytes` added to produce a bytes
+representation of the message in a fashion similar to how ``as_string``
+produces a string representation. It does not accept the *maxheaderlen*
+argument, but does accept the *unixfrom* and *policy* arguments. The
+:class:`~email.message.Message` :meth:`~email.message.Message.__bytes__` method
+calls it, meaning that ``bytes(mymsg)`` will now produce the intuitive
+result: a bytes object containing the fully formatted message. (Contributed
+by R. David Murray in :issue:`18600`.)
+
+The :meth:`.Message.set_param` message now accepts a *replace* keyword argument.
+When specified, the associated header will be updated without changing
+its location in the list of headers. For backward compatibility, the default
+is ``False``. (Contributed by R. David Murray in :issue:`18891`.)
+
+
+.. _whatsnew_email_contentmanager:
+
+A pair of new subclasses of :class:`~email.message.Message` have been added
+(:class:`.EmailMessage` and :class:`.MIMEPart`), along with a new sub-module,
+:mod:`~email.contentmanager` and a new :mod:`~email.policy` attribute
+:attr:`~email.policy.EmailPolicy.content_manager`. All documentation is
+currently in the new module, which is being added as part of email's new
+:term:`provisional API`. These classes provide a number of new methods that
+make extracting content from and inserting content into email messages much
+easier. For details, see the :mod:`~email.contentmanager` documentation and
+the :ref:`email-contentmanager-api-examples`. These API additions complete the
+bulk of the work that was planned as part of the email6 project. The currently
+provisional API is scheduled to become final in Python 3.5 (possibly with a few
+minor additions in the area of error handling). (Contributed by R. David
+Murray in :issue:`18891`.)
+
+
+filecmp
+-------
+
+A new :func:`~filecmp.clear_cache` function provides the ability to clear the
+:mod:`filecmp` comparison cache, which uses :func:`os.stat` information to
+determine if the file has changed since the last compare. This can be used,
+for example, if the file might have been changed and re-checked in less time
+than the resolution of a particular filesystem's file modification time field.
+(Contributed by Mark Levitt in :issue:`18149`.)
+
+New module attribute :data:`~filecmp.DEFAULT_IGNORES` provides the list of
+directories that are used as the default value for the *ignore* parameter of
+the :func:`~filecmp.dircmp` function. (Contributed by Eli Bendersky in
+:issue:`15442`.)
+
+
+functools
+---------
+
+The new :func:`~functools.partialmethod` descriptor brings partial argument
+application to descriptors, just as :func:`~functools.partial` provides
+for normal callables. The new descriptor also makes it easier to get
+arbitrary callables (including :func:`~functools.partial` instances)
+to behave like normal instance methods when included in a class definition.
+(Contributed by Alon Horev and Nick Coghlan in :issue:`4331`)
+
+.. _whatsnew-singledispatch:
+
+The new :func:`~functools.singledispatch` decorator brings support for
+single-dispatch generic functions to the Python standard library. Where
+object oriented programming focuses on grouping multiple operations on a
+common set of data into a class, a generic function focuses on grouping
+multiple implementations of an operation that allows it to work with
+*different* kinds of data.
+
+.. seealso::
+
+ :pep:`443` -- Single-dispatch generic functions
+ PEP written and implemented by Łukasz Langa.
+
+:func:`~functools.total_ordering` now supports a return value of
+:const:`NotImplemented` from the underlying comparison function. (Contributed
+by Katie Miller in :issue:`10042`.)
+
+A pure-python version of the :func:`~functools.partial` function is now in the
+stdlib; in CPython it is overridden by the C accelerated version, but it is
+available for other implementations to use. (Contributed by Brian Thorne in
+:issue:`12428`.)
+
+
+gc
+--
+
+New function :func:`~gc.get_stats` returns a list of three per-generation
+dictionaries containing the collections statistics since interpreter startup.
+(Contributed by Antoine Pitrou in :issue:`16351`.)
+
+
+glob
+----
+
+A new function :func:`~glob.escape` provides a way to escape special characters
+in a filename so that they do not become part of the globbing expansion but are
+instead matched literally. (Contributed by Serhiy Storchaka in :issue:`8402`.)
+
+
+hashlib
+-------
+
+A new :func:`hashlib.pbkdf2_hmac` function provides
+the `PKCS#5 password-based key derivation function 2
+<http://en.wikipedia.org/wiki/PBKDF2>`_. (Contributed by Christian
+Heimes in :issue:`18582`)
+
+The :attr:`~hashlib.hash.name` attribute of :mod:`hashlib` hash objects is now
+a formally supported interface. It has always existed in CPython's
+:mod:`hashlib` (although it did not return lower case names for all supported
+hashes), but it was not a public interface and so some other Python
+implementations have not previously supported it. (Contributed by Jason R.
+Coombs in :issue:`18532`.)
+
+
+hmac
+----
+
+:mod:`hmac` now accepts ``bytearray`` as well as ``bytes`` for the *key*
+argument to the :func:`~hmac.new` function, and the *msg* parameter to both the
+:func:`~hmac.new` function and the :meth:`~hmac.HMAC.update` method now
+accepts any type supported by the :mod:`hashlib` module. (Contributed
+by Jonas Borgström in :issue:`18240`.)
+
+The *digestmod* argument to the :func:`hmac.new` function may now be any hash
+digest name recognized by :mod:`hashlib`. In addition, the current behavior in
+which the value of *digestmod* defaults to ``MD5`` is deprecated: in a
+future version of Python there will be no default value. (Contributed by
+Christian Heimes in :issue:`17276`.)
+
+With the addition of :attr:`~hmac.HMAC.block_size` and :attr:`~hmac.HMAC.name`
+attributes (and the formal documentation of the :attr:`~hmac.HMAC.digest_size`
+attribute), the :mod:`hmac` module now conforms fully to the :pep:`247` API.
+(Contributed by Christian Heimes in :issue:`18775`.)
+
+
+html
+----
+
+New function :func:`~html.unescape` function converts HTML5 character references to
+the corresponding Unicode characters. (Contributed by Ezio Melotti in
+:issue:`2927`)
+
+:class:`~html.parser.HTMLParser` accepts a new keyword argument
+*convert_charrefs* that, when ``True``, automatically converts all character
+references. For backward-compatibility, its value defaults to ``False``, but
+it will change to ``True`` in a future version of Python, so you are invited to
+set it explicitly and update your code to use this new feature. (Contributed
+by Ezio Melotti in :issue:`13633`)
+
+The *strict* argument of :class:`~html.parser.HTMLParser` is now deprecated.
+(Contributed by Ezio Melotti in :issue:`15114`)
+
+
+http
+----
+
+:meth:`~http.server.BaseHTTPRequestHandler.send_error` now accepts an
+optional additional *explain* parameter which can be used to provide an
+extended error description, overriding the hardcoded default if there is one.
+This extended error description will be formatted using the
+:attr:`~http.server.HTTP.error_message_format` attribute and sent as the body
+of the error response. (Contributed by Karl Cow in :issue:`12921`.)
+
+The :mod:`http.server` :ref:`command line interface <http-server-cli>` now has
+a ``-b/--bind`` option that causes the server to listen on a specific address.
+(Contributed by Malte Swart in :issue:`17764`.)
+
+
+importlib
+---------
+
+The :class:`~importlib.abc.InspectLoader` ABC defines a new method,
+:meth:`~importlib.abc.InspectLoader.source_to_code` that accepts source
+data and a path and returns a code object. The default implementation
+is equivalent to ``compile(data, path, 'exec', dont_inherit=True)``.
+(Contributed by Eric Snow and Brett Cannon in :issue:`15627`.)
+
+:class:`~importlib.abc.InspectLoader` also now has a default implementation
+for the :meth:`~importlib.abc.InspectLoader.get_code` method. However,
+it will normally be desirable to override the default implementation
+for performance reasons. (Contributed by Brett Cannon in :issue:`18072`.)
+
+The :func:`~importlib.reload` function has been moved from :mod:`imp` to
+:mod:`importlib` as part of the :mod:`imp` module deprecation. (Contributed by
+Berker Peksag in :issue:`18193`.)
+
+:mod:`importlib.util` now has a :data:`~importlib.util.MAGIC_NUMBER` attribute
+providing access to the bytecode version number. This replaces the
+:func:`~imp.get_magic` function in the deprecated :mod:`imp` module.
+(Contributed by Brett Cannon in :issue:`18192`.)
+
+New :mod:`importlib.util` functions :func:`~importlib.util.cache_from_source`
+and :func:`~importlib.util.source_from_cache` replace the same-named functions
+in the deprecated :mod:`imp` module. (Contributed by Brett Cannon in
+:issue:`18194`.)
+
+The :mod:`importlib` bootstrap :class:`.NamespaceLoader` now conforms to
+the :class:`.InspectLoader` ABC, which means that ``runpy`` and
+``python -m`` can now be used with namespace packages. (Contributed
+by Brett Cannon in :issue:`18058`.)
+
+:mod:`importlib.util` has a new function :func:`~importlib.util.decode_source`
+that decodes source from bytes using universal newline processing. This is
+useful for implementing :meth:`.InspectLoader.get_source` methods.
+
+:class:`importlib.machinery.ExtensionFileLoader` now has a
+:meth:`~importlib.machinery.ExtensionFileLoader.get_filename` method. This was
+inadvertently omitted in the original implementation. (Contributed by Eric
+Snow in :issue:`19152`.)
+
+
+inspect
+-------
+
+The :mod:`inspect` module now offers a basic :ref:`command line interface
+<inspect-module-cli>` to quickly display source code and other
+information for modules, classes and functions. (Contributed by Claudiu Popa
+and Nick Coghlan in :issue:`18626`)
+
+:func:`~inspect.unwrap` makes it easy to unravel wrapper function chains
+created by :func:`functools.wraps` (and any other API that sets the
+``__wrapped__`` attribute on a wrapper function). (Contributed by
+Daniel Urban, Aaron Iles and Nick Coghlan in :issue:`13266`)
+
+As part of the implementation of the new :mod:`enum` module, the
+:mod:`inspect` module now has substantially better support for custom
+``__dir__`` methods and dynamic class attributes provided through
+metaclasses (Contributed by Ethan Furman in :issue:`18929` and
+:issue:`19030`)
+
+:func:`~inspect.getfullargspec` and :func:`~inspect.getargspec`
+now use the :func:`~inspect.signature` API. This allows them to
+support a much broader range of callables, including those with
+``__signature__`` attributes, those with metadata provided by argument
+clinic, :func:`functools.partial` objects and more. Note that, unlike
+:func:`~inspect.signature`, these functions still ignore ``__wrapped__``
+attributes, and report the already bound first argument for bound methods,
+so it is still necessary to update your code to use
+:func:`~inspect.signature` directly if those features are desired.
+(Contributed by Yury Selivanov in :issue:`17481`)
+
+:func:`~inspect.signature` now supports duck types of CPython functions,
+which adds support for functions compiled with Cython. (Contributed
+by Stefan Behnel and Yury Selivanov in :issue:`17159`)
+
+
+ipaddress
+---------
+
+:mod:`ipaddress` was added to the standard library in Python 3.3 as a
+:term:`provisional API`. With the release of Python 3.4, this qualification
+has been removed: :mod:`ipaddress` is now considered a stable API, covered
+by the normal standard library requirements to maintain backwards
+compatibility.
+
+A new :attr:`~ipaddress.IPv4Address.is_global` property is ``True`` if
+an address is globally routeable. (Contributed by Peter Moody in
+:issue:`17400`.)
+
+
+logging
+-------
+
+The :class:`~logging.handlers.TimedRotatingFileHandler` has a new *atTime*
+parameter that can be used to specify the time of day when rollover should
+happen. (Contributed by Ronald Oussoren in :issue:`9556`.)
+
+:class:`~logging.handlers.SocketHandler` and
+:class:`~logging.handlers.DatagramHandler` now support Unix domain sockets (by
+setting *port* to ``None``). (Contributed by Vinay Sajip in commit
+ce46195b56a9.)
+
+:func:`~logging.config.fileConfig` now accepts a
+:class:`configparser.RawConfigParser` subclass instance for the *fname*
+parameter. This facilitates using a configuration file when logging
+configuration is just a part of the overall application configuration, or where
+the application modifies the configuration before passing it to
+:func:`~logging.config.fileConfig`. (Contributed by Vinay Sajip in
+:issue:`16110`.)
+
+Logging configuration data received from a socket via the
+:func:`logging.config.listen` function can now be validated before being
+processed by supplying a verification function as the argument to the new
+*verify* keyword argument. (Contributed by Vinay Sajip in :issue:`15452`.)
+
+
+.. _whatsnew-marshal-3:
+
+marshal
+-------
+
+The default :mod:`marshal` version has been bumped to 3. The code implementing
+the new version restores the Python2 behavior of recording only one copy of
+interned strings and preserving the interning on deserialization, and extends
+this "one copy" ability to any object type (including handling recursive
+references). This reduces both the size of ``.pyc`` files and the amount of
+memory a module occupies in memory when it is loaded from a ``.pyc`` (or
+``.pyo``) file. (Contributed by Kristján Valur Jónsson in :issue:`16475`,
+with additional speedups by Antoine Pitrou in :issue:`19219`.)
+
+
+mmap
+----
+
+mmap objects can now be :mod:`weakref`\ ed. (Contributed by Valerie Lambert in
+:issue:`4885`.)
+
+
+multiprocessing
+---------------
+
+.. _whatsnew-multiprocessing-no-fork:
+
+On Unix two new :ref:`start methods <multiprocessing-start-methods>`,
+(``spawn`` and ``forkserver``, have been added for starting processes using
+:mod:`multiprocessing`. These make the mixing of processes with threads more
+robust, and the ``spawn`` method matches the semantics that multiprocessing has
+always used on Windows. New function
+:func:`~multiprocessing.get_all_start_methods` reports all start methods
+available on the platform, :func:`~multiprocessing.get_start_method` reports
+the current start method, and :func:`~multiprocessing.set_start_method` sets
+the start method. (Contributed by Richard Oudkerk in :issue:`8713`).
+
+:mod:`multiprocessing` also now has the concept of a ``context``, which
+determines how child processes are created. New function
+:func:`~multiprocessing.get_context` returns a context that uses a specified
+start method. It has the same API as the :mod:`multiprocessing` module itself,
+so you can use it to create :class:`~multiprocessing.pool.Pool`\ s and other
+objects that will operate within that context. This allows a framework and an
+application or different parts of the same application to use multiprocessing
+without interfering with each other. (Contributed by Richard Oudkerk in
+:issue:`18999`.)
+
+Except when using the old *fork* start method, child processes no longer
+inherit unneeded handles/file descriptors from their parents (part of
+:issue:`8713`).
+
+:mod:`multiprocessing` now relies on :mod:`runpy` (which implements the
+``-m`` switch) to initialise ``__main__`` appropriately in child processes
+when using the ``spawn`` or ``forkserver`` start methods. This resolves some
+edge cases where combining multiprocessing, the ``-m`` command line switch,
+and explicit relative imports could cause obscure failures in child
+processes. (Contributed by Nick Coghlan in :issue:`19946`)
+
+
+operator
+--------
+
+New function :func:`~operator.length_hint` provides an implementation of the
+specification for how the :meth:`~object.__length_hint__` special method should
+be used, as part of the :pep:`424` formal specification of this language
+feature. (Contributed by Armin Ronacher in :issue:`16148`.)
+
+There is now a pure-python version of the :mod:`operator` module available for
+reference and for use by alternate implementations of Python. (Contributed by
+Zachary Ware in :issue:`16694`.)
+
+
+os
+--
+
+There are new functions to get and set the :ref:`inheritable flag
+<fd_inheritance>` of a file descriptor (:func:`os.get_inheritable`,
+:func:`os.set_inheritable`) or a Windows handle
+(:func:`os.get_handle_inheritable`, :func:`os.set_handle_inheritable`).
+
+New function :func:`~os.cpu_count` reports the number of CPUs available on the
+platform on which Python is running (or ``None`` if the count can't be
+determined). The :func:`multiprocessing.cpu_count` function is now implemented
+in terms of this function). (Contributed by Trent Nelson, Yogesh Chaudhari,
+Victor Stinner, and Charles-François Natali in :issue:`17914`.)
+
+:func:`os.path.samestat` is now available on the Windows platform (and the
+:func:`os.path.samefile` implementation is now shared between Unix and
+Windows). (Contributed by Brian Curtin in :issue:`11939`.)
+
+:func:`os.path.ismount` now recognizes volumes mounted below a drive
+root on Windows. (Contributed by Tim Golden in :issue:`9035`.)
+
+:func:`os.open` supports two new flags on platforms that provide them,
+:data:`~os.O_PATH` (un-opened file descriptor), and :data:`~os.O_TMPFILE`
+(unnamed temporary file; as of 3.4.0 release available only on Linux systems
+with a kernel version of 3.11 or newer that have uapi headers). (Contributed
+by Christian Heimes in :issue:`18673` and Benjamin Peterson, respectively.)
+
+
+pdb
+---
+
+:mod:`pdb` has been enhanced to handle generators, :keyword:`yield`, and
+``yield from`` in a more useful fashion. This is especially helpful when
+debugging :mod:`asyncio` based programs. (Contributed by Andrew Svetlov and
+Xavier de Gaye in :issue:`16596`.)
+
+The ``print`` command has been removed from :mod:`pdb`, restoring access to the
+Python :func:`print` function from the pdb command line. Python2's ``pdb`` did
+not have a ``print`` command; instead, entering ``print`` executed the
+``print`` statement. In Python3 ``print`` was mistakenly made an alias for the
+pdb :pdbcmd:`p` command. ``p``, however, prints the ``repr`` of its argument,
+not the ``str`` like the Python2 ``print`` command did. Worse, the Python3
+``pdb print`` command shadowed the Python3 ``print`` function, making it
+inaccessible at the ``pdb`` prompt. (Contributed by Connor Osborn in
+:issue:`18764`.)
+
+
+.. _whatsnew-protocol-4:
+
+pickle
+------
+
+:mod:`pickle` now supports (but does not use by default) a new pickle protocol,
+protocol 4. This new protocol addresses a number of issues that were present
+in previous protocols, such as the serialization of nested classes, very large
+strings and containers, and classes whose :meth:`__new__` method takes
+keyword-only arguments. It also provides some efficiency improvements.
+
+.. seealso::
+
+ :pep:`3154` -- Pickle protocol 4
+ PEP written by Antoine Pitrou and implemented by Alexandre Vassalotti.
+
+
+plistlib
+--------
+
+:mod:`plistlib` now has an API that is similar to the standard pattern for
+stdlib serialization protocols, with new :func:`~plistlib.load`,
+:func:`~plistlib.dump`, :func:`~plistlib.loads`, and :func:`~plistlib.dumps`
+functions. (The older API is now deprecated.) In addition to the already
+supported XML plist format (:data:`~plistlib.FMT_XML`), it also now supports
+the binary plist format (:data:`~plistlib.FMT_BINARY`). (Contributed by Ronald
+Oussoren and others in :issue:`14455`).
+
+
+poplib
+------
+
+Two new methods have been added to :mod:`poplib`: :meth:`~poplib.POP3.capa`,
+which returns the list of capabilities advertised by the POP server, and
+:meth:`~poplib.POP3.stls`, which switches a clear-text POP3 session into an
+encrypted POP3 session if the POP server supports it. (Contributed by Lorenzo
+Catucci in :issue:`4473`.)
+
+
+pprint
+------
+
+The :mod:`pprint` module's :class:`~pprint.PrettyPrinter` class and its
+:func:`~pprint.pformat`, and :func:`~pprint.pprint` functions have a new
+option, *compact*, that controls how the output is formatted. Currently
+setting *compact* to ``True`` means that sequences will be printed with as many
+sequence elements as will fit within *width* on each (indented) line.
+(Contributed by Serhiy Storchaka in :issue:`19132`.)
+
+Long strings are now wrapped using Python's normal line continuation
+syntax. (Contributed by Antoine Pitrou in :issue:`17150`).
+
+
+pty
+---
+
+:func:`pty.spawn` now returns the status value from :func:`os.waitpid` on
+the child process, instead of ``None``. (Contributed by Gregory P. Smith.)
+
+
+pydoc
+-----
+
+The :mod:`pydoc` module is now based directly on the :func:`inspect.signature`
+introspection API, allowing it to provide signature information for a wider
+variety of callable objects. This change also means that ``__wrapped__``
+attributes are now taken into account when displaying help information
+(Contributed by Larry Hastings in :issue:`19674`)
+
+The :mod:`pydoc` module no longer displays the ``self`` parameter for
+already bound methods. Instead, it aims to always display the exact current
+signature of the supplied callable (Contributed by Larry Hastings in
+:issue:`20710`)
+
+In addition to the changes that have been made to :mod:`pydoc` directly,
+its handling of custom ``__dir__`` methods and various descriptor
+behaviours has also been improved substantially by the underlying changes in
+the :mod:`inspect` module.
+
+As the :func:`help` builtin is based on :mod:`pydoc`, the above changes also
+affect the behaviour of :func:`help`.
+
+
+re
+--
+
+New :func:`~re.fullmatch` function and :meth:`.regex.fullmatch` method anchor
+the pattern at both ends of the string to match. This provides a way to be
+explicit about the goal of the match, which avoids a class of subtle bugs where
+``$`` characters get lost during code changes or the addition of alternatives
+to an existing regular expression. (Contributed by Matthew Barnett in
+:issue:`16203`.)
+
+The repr of :ref:`regex objects <re-objects>` now includes the pattern
+and the flags; the repr of :ref:`match objects <match-objects>` now
+includes the start, end, and the part of the string that matched. (Contributed
+by Hugo Lopes Tavares and Serhiy Storchaka in :issue:`13592` and
+:issue:`17087`.)
+
+
+resource
+--------
+
+New :func:`~resource.prlimit` function, available on Linux platforms with a
+kernel version of 2.6.36 or later and glibc of 2.13 or later, provides the
+ability to query or set the resource limits for processes other than the one
+making the call. (Contributed by Christian Heimes in :issue:`16595`.)
+
+On Linux kernel version 2.6.36 or later, there are there are also some new
+Linux specific constants: :attr:`~resource.RLIMIT_MSGQUEUE`,
+:attr:`~resource.RLIMIT_NICE`, :attr:`~resource.RLIMIT_RTPRIO`,
+:attr:`~resource.RLIMIT_RTTIME`, and :attr:`~resource.RLIMIT_SIGPENDING`.
+(Contributed by Christian Heimes in :issue:`19324`.)
+
+On FreeBSD version 9 and later, there some new FreeBSD specific constants:
+:attr:`~resource.RLIMIT_SBSIZE`, :attr:`~resource.RLIMIT_SWAP`, and
+:attr:`~resource.RLIMIT_NPTS`. (Contributed by Claudiu Popa in
+:issue:`19343`.)
+
+
+select
+------
+
+:class:`~select.epoll` objects now support the context management protocol.
+When used in a :keyword:`with` statement, the :meth:`~select.epoll.close`
+method will be called automatically at the end of the block. (Contributed
+by Serhiy Storchaka in :issue:`16488`.)
+
+:class:`~select.devpoll` objects now have :meth:`~select.devpoll.fileno` and
+:meth:`~select.devpoll.close` methods, as well as a new attribute
+:attr:`~select.devpoll.closed`. (Contributed by Victor Stinner in
+:issue:`18794`.)
+
+
+shelve
+------
+
+:class:`~shelve.Shelf` instances may now be used in :keyword:`with` statements,
+and will be automatically closed at the end of the :keyword:`with` block.
+(Contributed by Filip Gruszczyński in :issue:`13896`.)
+
+
+shutil
+------
+
+:func:`~shutil.copyfile` now raises a specific :exc:`~shutil.Error` subclass,
+:exc:`~shutil.SameFileError`, when the source and destination are the same
+file, which allows an application to take appropriate action on this specific
+error. (Contributed by Atsuo Ishimoto and Hynek Schlawack in
+:issue:`1492704`.)
+
+
+smtpd
+-----
+
+The :class:`~smtpd.SMTPServer` and :class:`~smtpd.SMTPChannel` classes now
+accept a *map* keyword argument which, if specified, is passed in to
+:class:`asynchat.async_chat` as its *map* argument. This allows an application
+to avoid affecting the global socket map. (Contributed by Vinay Sajip in
+:issue:`11959`.)
+
+
+smtplib
+-------
+
+:exc:`~smtplib.SMTPException` is now a subclass of :exc:`OSError`, which allows
+both socket level errors and SMTP protocol level errors to be caught in one
+try/except statement by code that only cares whether or not an error occurred.
+(Contributed by Ned Jackson Lovely in :issue:`2118`).
+
+
+socket
+------
+
+The socket module now supports the :data:`~socket.CAN_BCM` protocol on
+platforms that support it. (Contributed by Brian Thorne in :issue:`15359`.)
+
+Socket objects have new methods to get or set their :ref:`inheritable flag
+<fd_inheritance>`, :meth:`~socket.socket.get_inheritable` and
+:meth:`~socket.socket.set_inheritable`.
+
+The ``socket.AF_*`` and ``socket.SOCK_*`` constants are now enumeration values
+using the new :mod:`enum` module. This allows meaningful names to be printed
+during debugging, instead of integer "magic numbers".
+
+The :data:`~socket.AF_LINK` constant is now available on BSD and OSX.
+
+:func:`~socket.inet_pton` and :func:`~socket.inet_ntop` are now supported
+on Windows. (Contributed by Atsuo Ishimoto in :issue:`7171`.)
+
+
+sqlite3
+-------
+
+A new boolean parameter to the :func:`~sqlite3.connect` function, *uri*, can be
+used to indicate that the *database* parameter is a ``uri`` (see the `SQLite
+URI documentation <http://www.sqlite.org/uri.html>`_). (Contributed by poq in
+:issue:`13773`.)
+
+
+ssl
+---
+
+.. _whatsnew-tls-11-12:
+
+:data:`~ssl.PROTOCOL_TLSv1_1` and :data:`~ssl.PROTOCOL_TLSv1_2` (TLSv1.1 and
+TLSv1.2 support) have been added; support for these protocols is only available if
+Python is linked with OpenSSL 1.0.1 or later. (Contributed by Michele Orrù and
+Antoine Pitrou in :issue:`16692`)
+
+.. _whatsnew34-sslcontext:
+
+New function :func:`~ssl.create_default_context` provides a standard way to
+obtain an :class:`~ssl.SSLContext` whose settings are intended to be a
+reasonable balance between compatibility and security. These settings are
+more stringent than the defaults provided by the :class:`~ssl.SSLContext`
+constructor, and may be adjusted in the future, without prior deprecation, if
+best-practice security requirements change. The new recommended best
+practice for using stdlib libraries that support SSL is to use
+:func:`~ssl.create_default_context` to obtain an :class:`~ssl.SSLContext`
+object, modify it if needed, and then pass it as the *context* argument
+of the appropriate stdlib API. (Contributed by Christian Heimes
+in :issue:`19689`.)
+
+:class:`~ssl.SSLContext` method :meth:`~ssl.SSLContext.load_verify_locations`
+accepts a new optional argument *cadata*, which can be used to provide PEM or
+DER encoded certificates directly via strings or bytes, respectively.
+(Contributed by Christian Heimes in :issue:`18138`.)
+
+New function :func:`~ssl.get_default_verify_paths` returns
+a named tuple of the paths and environment variables that the
+:meth:`~ssl.SSLContext.set_default_verify_paths` method uses to set
+OpenSSL's default ``cafile`` and ``capath``. This can be an aid in
+debugging default verification issues. (Contributed by Christian Heimes
+in :issue:`18143`.)
+
+:class:`~ssl.SSLContext` has a new method,
+:meth:`~ssl.SSLContext.cert_store_stats`, that reports the number of loaded
+``X.509`` certs, ``X.509 CA`` certs, and certificate revocation lists (``crl``\
+s), as well as a :meth:`~ssl.SSLContext.get_ca_certs` method that returns a
+list of the loaded ``CA`` certificates. (Contributed by Christian Heimes in
+:issue:`18147`.)
+
+If OpenSSL 0.9.8 or later is available, :class:`~ssl.SSLContext` has an new
+attribute :attr:`~ssl.SSLContext.verify_flags` that can be used to control the
+certificate verification process by setting it to some combination of the new
+constants :data:`~ssl.VERIFY_DEFAULT`, :data:`~ssl.VERIFY_CRL_CHECK_LEAF`,
+:data:`~ssl.VERIFY_CRL_CHECK_CHAIN`, or :data:`~ssl.VERIFY_X509_STRICT`.
+OpenSSL does not do any CRL verification by default. (Contributed by
+Christien Heimes in :issue:`8813`.)
+
+New :class:`~ssl.SSLContext` method :meth:`~ssl.SSLContext.load_default_certs`
+loads a set of default "certificate authority" (CA) certificates from default
+locations, which vary according to the platform. It can be used to load both
+TLS web server authentication certificates
+(``purpose=``:data:`~ssl.Purpose.SERVER_AUTH`) for a client to use to verify a
+server, and certificates for a server to use in verifying client certificates
+(``purpose=``:data:`~ssl.Purpose.CLIENT_AUTH`). (Contributed by Christian
+Heimes in :issue:`19292`.)
+
+.. _whatsnew34-win-cert-store:
+
+Two new windows-only functions, :func:`~ssl.enum_certificates` and
+:func:`~ssl.enum_crls` provide the ability to retrieve certificates,
+certificate information, and CRLs from the Windows cert store. (Contributed
+by Christian Heimes in :issue:`17134`.)
+
+.. _whatsnew34-sni:
+
+Support for server-side SNI (Server Name Indication) using the new
+:meth:`ssl.SSLContext.set_servername_callback` method.
+(Contributed by Daniel Black in :issue:`8109`.)
+
+The dictionary returned by :meth:`.SSLSocket.getpeercert` contains additional
+``X509v3`` extension items: ``crlDistributionPoints``, ``calIssuers``, and
+``OCSP`` URIs. (Contributed by Christian Heimes in :issue:`18379`.)
+
+
+stat
+----
+
+The :mod:`stat` module is now backed by a C implementation in :mod:`_stat`. A C
+implementation is required as most of the values aren't standardized and
+are platform-dependent. (Contributed by Christian Heimes in :issue:`11016`.)
+
+The module supports new :mod:`~stat.ST_MODE` flags, :mod:`~stat.S_IFDOOR`,
+:attr:`~stat.S_IFPORT`, and :attr:`~stat.S_IFWHT`. (Contributed by
+Christian Hiemes in :issue:`11016`.)
+
+
+struct
+------
+
+New function :mod:`~struct.iter_unpack` and a new
+:meth:`struct.Struct.iter_unpack` method on compiled formats provide streamed
+unpacking of a buffer containing repeated instances of a given format of data.
+(Contributed by Antoine Pitrou in :issue:`17804`.)
+
+
+subprocess
+----------
+
+:func:`~subprocess.check_output` now accepts an *input* argument that can
+be used to provide the contents of ``stdin`` for the command that is run.
+(Contributed by Zack Weinberg in :issue:`16624`.)
+
+:func:`~subprocess.getstatus` and :func:`~subprocess.getstatusoutput` now
+work on Windows. This change was actually inadvertently made in 3.3.4.
+(Contributed by Tim Golden in :issue:`10197`.)
+
+
+sunau
+-----
+
+The :meth:`~sunau.getparams` method now returns a namedtuple rather than a
+plain tuple. (Contributed by Claudiu Popa in :issue:`18901`.)
+
+:meth:`sunau.open` now supports the context manager protocol: when used in a
+:keyword:`with` block, the ``close`` method of the returned object will be
+called automatically at the end of the block. (Contributed by Serhiy Storchaka
+in :issue:`18878`.)
+
+:meth:`.AU_write.setsampwidth` now supports 24 bit samples, thus adding
+support for writing 24 sample using the module. (Contributed by
+Serhiy Storchaka in :issue:`19261`.)
+
+The :meth:`~sunau.AU_write.writeframesraw` and
+:meth:`~sunau.AU_write.writeframes` methods now accept any :term:`bytes-like
+object`. (Contributed by Serhiy Storchaka in :issue:`8311`.)
+
+
+sys
+---
+
+New function :func:`sys.getallocatedblocks` returns the current number of
+blocks allocated by the interpreter. (In CPython with the default
+``--with-pymalloc`` setting, this is allocations made through the
+:c:func:`PyObject_Malloc` API.) This can be useful for tracking memory leaks,
+especially if automated via a test suite. (Contributed by Antoine Pitrou
+in :issue:`13390`.)
+
+When the Python interpreter starts in :ref:`interactive mode
+<tut-interactive>`, it checks for an :data:`~sys.__interactivehook__` attribute
+on the :mod:`sys` module. If the attribute exists, its value is called with no
+arguments just before interactive mode is started. The check is made after the
+:envvar:`PYTHONSTARTUP` file is read, so it can be set there. The :mod:`site`
+module :ref:`sets it <rlcompleter-config>` to a function that enables tab
+completion and history saving (in :file:`~/.python-history`) if the platform
+supports :mod:`readline`. If you do not want this (new) behavior, you can
+override it in :envvar:`PYTHONSTARTUP`, :mod:`sitecustomize`, or
+:mod:`usercustomize` by deleting this attribute from :mod:`sys` (or setting it
+to some other callable). (Contributed by Éric Araujo and Antoine Pitrou in
+:issue:`5845`.)
+
+
+tarfile
+-------
+
+The :mod:`tarfile` module now supports a simple :ref:`tarfile-commandline` when
+called as a script directly or via ``-m``. This can be used to create and
+extract tarfile archives. (Contributed by Berker Peksag in :issue:`13477`.)
+
+
+textwrap
+--------
+
+The :class:`~textwrap.TextWrapper` class has two new attributes/constructor
+arguments: :attr:`~textwrap.TextWrapper.max_lines`, which limits the number of
+lines in the output, and :attr:`~textwrap.TextWrapper.placeholder`, which is a
+string that will appear at the end of the output if it has been truncated
+because of *max_lines*. Building on these capabilities, a new convenience
+function :func:`~textwrap.shorten` collapses all of the whitespace in the input
+to single spaces and produces a single line of a given *width* that ends with
+the *placeholder* (by default, ``[...]``). (Contributed by Antoine Pitrou and
+Serhiy Storchaka in :issue:`18585` and :issue:`18725`.)
+
+
+threading
+---------
+
+The :class:`~threading.Thread` object representing the main thread can be
+obtained from the new :func:`~threading.main_thread` function. In normal
+conditions this will be the thread from which the Python interpreter was
+started. (Contributed by Andrew Svetlov in :issue:`18882`.)
+
+
+traceback
+---------
+
+A new :func:`traceback.clear_frames` function takes a traceback object
+and clears the local variables in all of the frames it references,
+reducing the amount of memory consumed. (Contributed by Andrew Kuchling in
+:issue:`1565525`).
+
+
+types
+-----
+
+A new :func:`~types.DynamicClassAttribute` descriptor provides a way to define
+an attribute that acts normally when looked up through an instance object, but
+which is routed to the *class* ``__getattr__`` when looked up through the
+class. This allows one to have properties active on a class, and have virtual
+attributes on the class with the same name (see :mod:`Enum` for an example).
+(Contributed by Ethan Furman in :issue:`19030`.)
+
+
+urllib
+------
+
+:mod:`urllib.request` now supports ``data:`` URLs via the
+:class:`~urllib.request.DataHandler` class. (Contributed by Mathias Panzenböck
+in :issue:`16423`.)
+
+The http method that will be used by a :class:`~urllib.request.Request` class
+can now be specified by setting a :class:`~urllib.request.Request.method`
+class attribute on the subclass. (Contributed by Jason R Coombs in
+:issue:`18978`.)
+
+:class:`~urllib.request.Request` objects are now reusable: if the
+:attr:`~urllib.request.Request.full_url` or :attr:`~urllib.request.Request.data`
+attributes are modified, all relevant internal properties are updated. This
+means, for example, that it is now possible to use the same
+:class:`~urllib.request.Request` object in more than one
+:meth:`.OpenerDirector.open` call with different *data* arguments, or to
+modify a :class:`~urllib.request.Request`\ 's ``url`` rather than recomputing it
+from scratch. There is also a new
+:meth:`~urllib.request.Request.remove_header` method that can be used to remove
+headers from a :class:`~urllib.request.Request`. (Contributed by Alexey
+Kachayev in :issue:`16464`, Daniel Wozniak in :issue:`17485`, and Damien Brecht
+and Senthil Kumaran in :issue:`17272`.)
+
+:class:`~urllib.error.HTTPError` objects now have a
+:attr:`~urllib.error.HTTPError.headers` attribute that provides access to the
+HTTP response headers associated with the error. (Contributed by
+Berker Peksag in :issue:`15701`.)
+
+
+unittest
+--------
+
+The :class:`~unittest.TestCase` class has a new method,
+:meth:`~unittest.TestCase.subTest`, that produces a context manager whose
+:keyword:`with` block becomes a "sub-test". This context manager allows a test
+method to dynamically generate subtests by, say, calling the ``subTest``
+context manager inside a loop. A single test method can thereby produce an
+indefinite number of separately-identified and separately-counted tests, all of
+which will run even if one or more of them fail. For example::
+
+ class NumbersTest(unittest.TestCase):
+ def test_even(self):
+ for i in range(6):
+ with self.subTest(i=i):
+ self.assertEqual(i % 2, 0)
+
+will result in six subtests, each identified in the unittest verbose output
+with a label consisting of the variable name ``i`` and a particular value for
+that variable (``i=0``, ``i=1``, etc). See :ref:`subtests` for the full
+version of this example. (Contributed by Antoine Pitrou in :issue:`16997`.)
+
+:func:`unittest.main` now accepts an iterable of test names for
+*defaultTest*, where previously it only accepted a single test name as a
+string. (Contributed by Jyrki Pulliainen in :issue:`15132`.)
+
+If :class:`~unittest.SkipTest` is raised during test discovery (that is, at the
+module level in the test file), it is now reported as a skip instead of an
+error. (Contributed by Zach Ware in :issue:`16935`.)
+
+:meth:`~unittest.TestLoader.discover` now sorts the discovered files to provide
+consistent test ordering. (Contributed by Martin Melin and Jeff Ramnani in
+:issue:`16709`.)
+
+:class:`~unittest.TestSuite` now drops references to tests as soon as the test
+has been run, if the test is successful. On Python interpreters that do
+garbage collection, this allows the tests to be garbage collected if nothing
+else is holding a reference to the test. It is possible to override this
+behavior by creating a :class:`~unittest.TestSuite` subclass that defines a
+custom ``_removeTestAtIndex`` method. (Contributed by Tom Wardill, Matt
+McClure, and Andrew Svetlov in :issue:`11798`.)
+
+A new test assertion context-manager, :meth:`~unittest.TestCase.assertLogs`,
+will ensure that a given block of code emits a log message using the
+:mod:`logging` module. By default the message can come from any logger and
+have a priority of ``INFO`` or higher, but both the logger name and an
+alternative minimum logging level may be specified. The object returned by the
+context manager can be queried for the :class:`~logging.LogRecord`\ s and/or
+formatted messages that were logged. (Contributed by Antoine Pitrou in
+:issue:`18937`.)
+
+Test discovery now works with namespace packages (Contributed by Claudiu Popa
+in :issue:`17457`.)
+
+:mod:`unittest.mock` objects now inspect their specification signatures when
+matching calls, which means an argument can now be matched by either position
+or name, instead of only by position. (Contributed by Antoine Pitrou in
+:issue:`17015`.)
+
+:func:`~mock.mock_open` objects now have ``readline`` and ``readlines``
+methods. (Contributed by Toshio Kuratomi in :issue:`17467`.)
+
+
+venv
+----
+
+:mod:`venv` now includes activation scripts for the ``csh`` and ``fish``
+shells (Contributed by Andrew Svetlov in :issue:`15417`.)
+
+:class:`~venv.EnvBuilder` and the :func:`~venv.create` convenience function
+take a new keyword argument *with_pip*, which defaults to ``False``, that
+controls whether or not :class:`~venv.EnvBuilder` ensures that ``pip`` is
+installed in the virtual environment. (Contributed by Nick Coghlan in
+:issue:`19552` as part of the :pep:`453` implementation.)
+
+
+wave
+----
+
+The :meth:`~wave.getparams` method now returns a namedtuple rather than a
+plain tuple. (Contributed by Claudiu Popa in :issue:`17487`.)
+
+:meth:`wave.open` now supports the context manager protocol. (Contributed
+by Claudiu Popa in :issue:`17616`.)
+
+:mod:`wave` can now :ref:`write output to unseekable files
+<wave-write-objects>`. (Contributed by David Jones, Guilherme Polo, and Serhiy
+Storchaka in :issue:`5202`.)
+
+The :meth:`~wave.Wave_write.writeframesraw` and
+:meth:`~wave.Wave_write.writeframes` methods now accept any :term:`bytes-like
+object`. (Contributed by Serhiy Storchaka in :issue:`8311`.)
+
+
+weakref
+-------
+
+New :class:`~weakref.WeakMethod` class simulates weak references to bound
+methods. (Contributed by Antoine Pitrou in :issue:`14631`.)
+
+New :class:`~weakref.finalize` class makes it possible to register a callback
+to be invoked when an object is garbage collected, without needing to
+carefully manage the lifecycle of the weak reference itself. (Contributed by
+Richard Oudkerk in :issue:`15528`)
+
+The callback, if any, associated with a :class:`~weakref.ref` is now
+exposed via the :attr:`~weakref.ref.__callback__` attribute. (Contributed
+by Mark Dickinson in :issue:`17643`.)
+
+
+xml.etree
+---------
+
+A new parser, :class:`~xml.etree.ElementTree.XMLPullParser`, allows a
+non-blocking applications to parse XML documents. An example can be
+seen at :ref:`elementtree-pull-parsing`. (Contributed by Antoine
+Pitrou in :issue:`17741`.)
+
+The :mod:`xml.etree.ElementTree` :func:`~xml.etree.ElementTree.tostring` and
+:func:`~xml.etree.ElementTree.tostringlist` functions, and the
+:class:`~xml.etree.ElementTree.ElementTree`
+:meth:`~xml.etree.ElementTree.ElementTree.write` method, now have a
+*short_empty_elements* :ref:`keyword-only parameter <keyword-only_parameter>`
+providing control over whether elements with no content are written in
+abbreviated (``<tag />``) or expanded (``<tag></tag>``) form. (Contributed by
+Ariel Poliak and Serhiy Storchaka in :issue:`14377`.)
+
+
+zipfile
+-------
+
+The :meth:`~zipfile.PyZipFile.writepy` method of the
+:class:`~zipfile.PyZipFile` class has a new *filterfunc* option that can be
+used to control which directories and files are added to the archive. For
+example, this could be used to exclude test files from the archive.
+(Contributed by Christian Tismer in :issue:`19274`.)
+
+The *allowZip64* parameter to :class:`~zipfile.ZipFile` and
+:class:`~zipfile.PyZipfile` is now ``True`` by default. (Contributed by
+William Mallard in :issue:`17201`.)
+
+
+
+CPython Implementation Changes
+==============================
+
+
+.. _whatsnew-pep-445:
+
+PEP 445: Customization of CPython Memory Allocators
+---------------------------------------------------
+
+:pep:`445` adds new C level interfaces to customize memory allocation in
+the CPython interpreter.
+
+.. seealso::
+
+ :pep:`445` -- Add new APIs to customize Python memory allocators
+ PEP written and implemented by Victor Stinner.
+
+
+.. _whatsnew-pep-442:
+
+PEP 442: Safe Object Finalization
+---------------------------------
+
+:pep:`442` removes the current limitations and quirks of object finalization
+in CPython. With it, objects with :meth:`__del__` methods, as well as
+generators with :keyword:`finally` clauses, can be finalized when they are
+part of a reference cycle.
+
+As part of this change, module globals are no longer forcibly set to
+:const:`None` during interpreter shutdown in most cases, instead relying
+on the normal operation of the cyclic garbage collector. This avoids a
+whole class of interpreter-shutdown-time errors, usually involving
+``__del__`` methods, that have plagued Python since the cyclic GC
+was first introduced.
+
+.. seealso::
+
+ :pep:`442` -- Safe object finalization
+ PEP written and implemented by Antoine Pitrou.
+
+
+.. _whatsnew-pep-456:
+
+PEP 456: Secure and Interchangeable Hash Algorithm
+--------------------------------------------------
+
+:pep:`456` follows up on earlier security fix work done on Python's hash
+algorithm to address certain DOS attacks to which public facing APIs backed by
+dictionary lookups may be subject. (See :issue:`14621` for the start of the
+current round of improvements.) The PEP unifies CPython's hash code to make it
+easier for a packager to substitute a different hash algorithm, and switches
+Python's default implementation to a SipHash implementation on platforms that
+have a 64 bit data type. Any performance differences in comparison with the
+older FNV algorithm are trivial.
+
+The PEP adds additional fields to the :attr:`sys.hash_info` struct sequence to
+describe the hash algorithm in use by the currently executing binary. Otherwise,
+the PEP does not alter any existing CPython APIs.
+
+
+.. _whatsnew-pep-436:
+
+PEP 436: Argument Clinic
+------------------------
+
+"Argument Clinic" (:pep:`436`) is now part of the CPython build process
+and can be used to simplify the process of defining and maintaining
+accurate signatures for builtins and standard library extension modules
+implemented in C.
+
+Some standard library extension modules have been converted to use Argument
+Clinic in Python 3.4, and :mod:`pydoc` and :mod:`inspect` have been updated
+accordingly.
+
+It is expected that signature metadata for programmatic introspection will
+be added to additional callables implemented in C as part of Python 3.4
+maintenance releases.
+
+.. note::
+ The Argument Clinic PEP is not fully up to date with the state of the
+ implementation. This has been deemed acceptable by the release manager
+ and core development team in this case, as Argument Clinic will not
+ be made available as a public API for third party use in Python 3.4.
+
+.. seealso::
+
+ :pep:`436` -- The Argument Clinic DSL
+ PEP written and implemented by Larry Hastings.
+
+
+Other Build and C API Changes
+-----------------------------
+
+* The new :c:func:`PyType_GetSlot` function has been added to the stable ABI,
+ allowing retrieval of function pointers from named type slots when using
+ the limited API. (Contributed by Martin von Löwis in :issue:`17162`)
+
+* The new :c:func:`Py_SetStandardStreamEncoding` pre-initialization API
+ allows applications embedding the CPython interpreter to reliably force
+ a particular encoding and error handler for the standard streams
+ (Contributed by Bastien Montagne and Nick Coghlan in :issue:`16129`)
+
+* Most Python C APIs that don't mutate string arguments are now correctly
+ marked as accepting ``const char *`` rather than ``char *`` (Contributed
+ by Serhiy Storchaka in :issue:`1772673`).
+
+* A new shell version of ``python-config`` can be used even when a python
+ interpreter is not available (for example, in cross compilation scenarios).
+
+* :c:func:`PyUnicode_FromFormat` now supports width and precision
+ specifications for ``%s``, ``%A``, ``%U``, ``%V``, ``%S``, and ``%R``.
+ (Contributed by Ysj Ray and Victor Stinner in :issue:`7330`.)
+
+* New function :c:func:`PyStructSequence_InitType2` supplements the
+ existing :c:func:`PyStructSequence_InitType` function. The difference
+ is that it returns ``0`` on success and ``-1`` on failure.
+
+* The CPython source can now be compiled using the address sanity checking
+ features of recent versions of GCC and clang: the false alarms in the small
+ object allocator have been silenced. (Contributed by Dhiru Kholia in
+ :issue:`18596`.)
+
+* The Windows build now uses `Address Space Layout Randomization
+ <http://en.wikipedia.org/wiki/ASLR>`_ and `Data Execution Prevention
+ <http://en.wikipedia.org/wiki/Data_Execution_Prevention>`_. (Contributed by
+ Christian Heimes in :issue:`16632`.)
+
+* New function :c:func:`PyObject_LengthHint` is the C API equivalent
+ of :func:`operator.length_hint`. (Contributed by Armin Ronacher in
+ :issue:`16148`.)
+
+
+.. _other-improvements-3.4:
+
+Other Improvements
+------------------
+
+.. _whatsnew-isolated-mode:
+
+* The :ref:`python <using-on-cmdline>` command has a new :ref:`option
+ <using-on-misc-options>`, ``-I``, which causes it to run in "isolated mode",
+ which means that :data:`sys.path` contains neither the script's directory nor
+ the user's ``site-packages`` directory, and all :envvar:`PYTHON*` environment
+ variables are ignored (it implies both ``-s`` and ``-E``). Other
+ restrictions may also be applied in the future, with the goal being to
+ isolate the execution of a script from the user's environment. This is
+ appropriate, for example, when Python is used to run a system script. On
+ most POSIX systems it can and should be used in the ``#!`` line of system
+ scripts. (Contributed by Christian Heimes in :issue:`16499`.)
+
+* Tab-completion is now enabled by default in the interactive interpreter
+ on systems that support :mod:`readline`. History is also enabled by default,
+ and is written to (and read from) the file :file:`~/.python-history`.
+ (Contributed by Antoine Pitrou and Éric Araujo in :issue:`5845`.)
+
+* Invoking the Python interpreter with ``--version`` now outputs the version to
+ standard output instead of standard error (:issue:`18338`). Similar changes
+ were made to :mod:`argparse` (:issue:`18920`) and other modules that have
+ script-like invocation capabilities (:issue:`18922`).
+
+* The CPython Windows installer now adds ``.py`` to the :envvar:`PATHEXT`
+ variable when extensions are registered, allowing users to run a python
+ script at the windows command prompt by just typing its name without the
+ ``.py`` extension. (Contributed by Paul Moore in :issue:`18569`.)
+
+* A new ``make`` target `coverage-report
+ <http://docs.python.org/devguide/coverage.html#measuring-coverage-of-c-code-with-gcov-and-lcov>`_
+ will build python, run the test suite, and generate an HTML coverage report
+ for the C codebase using ``gcov`` and `lcov
+ <http://ltp.sourceforge.net/coverage/lcov.php>`_.
+
+* The ``-R`` option to the :ref:`python regression test suite <regrtest>` now
+ also checks for memory allocation leaks, using
+ :func:`sys.getallocatedblocks()`. (Contributed by Antoine Pitrou in
+ :issue:`13390`).
+
+* ``python -m`` now works with namespace packages.
+
+* The :mod:`stat` module is now implemented in C, which means it gets the
+ values for its constants from the C header files, instead of having the
+ values hard-coded in the python module as was previously the case.
+
+* Loading multiple python modules from a single OS module (``.so``, ``.dll``)
+ now works correctly (previously it silently returned the first python
+ module in the file). (Contributed by Václav Šmilauer in :issue:`16421`.)
+
+* A new opcode, :opcode:`LOAD_CLASSDEREF`, has been added to fix a bug in the
+ loading of free variables in class bodies that could be triggered by certain
+ uses of :ref:`__prepare__ <prepare>`. (Contributed by Benjamin Peterson in
+ :issue:`17853`.)
+
+* A number of MemoryError-related crashes were identified and fixed by Victor
+ Stinner using his :pep:`445`-based ``pyfailmalloc`` tool (:issue:`18408`,
+ :issue:`18520`).
+
+* The :ref:`pyvenv <scripts-pyvenv>` command now accepts a ``--copies`` option
+ to use copies rather than symlinks even on systems where symlinks are the
+ default. (Contributed by Vinay Sajip in :issue:`18807`.)
+
+* The :ref:`pyvenv <scripts-pyvenv>` command also accepts a ``--without-pip``
+ option to suppress the otherwise-automatic bootstrapping of pip into
+ the virtual environment. (Contributed by Nick Coghlan in :issue:`19552`
+ as part of the :pep:`453` implementation.)
+
+* The encoding name is now optional in the value set for the
+ :envvar:`PYTHONIOENCODING` environment variable. This makes it possible to
+ set just the error handler, without changing the default encoding.
+ (Contributed by Serhiy Storchaka in :issue:`18818`.)
+
+* The :mod:`bz2`, :mod:`lzma`, and :mod:`gzip` module ``open`` functions now
+ support ``x`` (exclusive creation) mode. (Contributed by Tim Heaney and
+ Vajrasky Kok in :issue:`19201`, :issue:`19222`, and :issue:`19223`.)
+
+
+Significant Optimizations
+-------------------------
+
+* The UTF-32 decoder is now 3x to 4x faster. (Contributed by Serhiy Storchaka
+ in :issue:`14625`.)
+
+* The cost of hash collisions for sets is now reduced. Each hash table
+ probe now checks a series of consecutive, adjacent key/hash pairs before
+ continuing to make random probes through the hash table. This exploits
+ cache locality to make collision resolution less expensive.
+ The collision resolution scheme can be described as a hybrid of linear
+ probing and open addressing. The number of additional linear probes
+ defaults to nine. This can be changed at compile-time by defining
+ LINEAR_PROBES to be any value. Set LINEAR_PROBES=0 to turn-off
+ linear probing entirely. (Contributed by Raymond Hettinger in
+ :issue:`18771`.)
+
+* The interpreter starts about 30% faster. A couple of measures lead to the
+ speedup. The interpreter loads fewer modules on startup, e.g. the :mod:`re`,
+ :mod:`collections` and :mod:`locale` modules and their dependencies are no
+ longer imported by default. The marshal module has been improved to load
+ compiled Python code faster. (Contributed by Antoine Pitrou, Christian
+ Heimes and Victor Stinner in :issue:`19219`, :issue:`19218`, :issue:`19209`,
+ :issue:`19205` and :issue:`9548`)
+
+* :class:`bz2.BZ2File` is now as fast or faster than the Python2 version for
+ most cases. :class:`lzma.LZMAFile` has also been optimized. (Contributed by
+ Serhiy Storchaka and Nadeem Vawda in :issue:`16034`.)
+
+* :func:`random.getrandbits` is 20%-40% faster for small integers (the most
+ common use case). (Contributed by Serhiy Storchaka in :issue:`16674`).
+
+* By taking advantage of the new storage format for strings, pickling of
+ strings is now significantly faster. (Contributed by Victor Stinner and
+ Antoine Pitrou in :issue:`15596`.)
+
+* A performance issue in :meth:`io.FileIO.readall` has been solved. This
+ particularly affects Windows, and significantly speeds up the case of piping
+ significant amounts of data through :mod:`subprocess`. (Contributed
+ by Richard Oudkerk in :issue:`15758`.)
+
+* :func:`html.escape` is now 10x faster. (Contributed by Matt Bryant in
+ :issue:`18020`.)
+
+* On Windows, the native ``VirtualAlloc`` is now used instead of the CRT
+ ``malloc`` in ``obmalloc``. Artificial benchmarks show about a 3% memory
+ savings.
+
+* :func:`os.urandom` now uses a lazily-opened persistent file descriptor
+ so as to avoid using many file descriptors when run in parallel from
+ multiple threads. (Contributed by Antoine Pitrou in :issue:`18756`.)
+
+
+.. _deprecated-3.4:
+
+Deprecated
+==========
+
+This section covers various APIs and other features that have been deprecated
+in Python 3.4, and will be removed in Python 3.5 or later. In most (but not
+all) cases, using the deprecated APIs will produce a :exc:`DeprecationWarning`
+when the interpreter is run with deprecation warnings enabled (for example, by
+using ``-Wd``).
+
+
+Deprecations in the Python API
+------------------------------
+
+* As mentioned in :ref:`whatsnew-pep-451`, a number of :mod:`importilb`
+ methods and functions are deprecated: :meth:`importlib.find_loader` is
+ replaced by :func:`importlib.util.find_spec`;
+ :meth:`importlib.machinery.PathFinder.find_module` is replaced by
+ :meth:`importlib.machinery.PathFinder.find_spec`;
+ :meth:`importlib.abc.MetaPathFinder.find_module` is replaced by
+ :meth:`importlib.abc.MetaPathFinder.find_spec`;
+ :meth:`importlib.abc.PathEntryFinder.find_loader` and
+ :meth:`~importlib.abc.PathEntryFinder.find_module` are replaced by
+ :meth:`importlib.abc.PathEntryFinder.find_spec`; all of the ``xxxLoader`` ABC
+ ``load_module`` methods (:meth:`importlib.abc.Loader.load_module`,
+ :meth:`importlib.abc.InspectLoader.load_module`,
+ :meth:`importlib.abc.FileLoader.load_module`,
+ :meth:`importlib.abc.SourceLoader.load_module`) should no longer be
+ implemented, instead loaders should implement an
+ ``exec_module`` method
+ (:meth:`importlib.abc.Loader.exec_module`,
+ :meth:`importlib.abc.InspectLoader.exec_module`
+ :meth:`importlib.abc.SourceLoader.exec_module`) and let the import system
+ take care of the rest; and
+ :meth:`importlib.abc.Loader.module_repr`,
+ :meth:`importlib.util.module_for_loader`, :meth:`importlib.util.set_loader`,
+ and :meth:`importlib.util.set_package` are no longer needed because their
+ functions are now handled automatically by the import system.
+
+* The :mod:`imp` module is pending deprecation. To keep compatibility with
+ Python 2/3 code bases, the module's removal is currently not scheduled.
+
+* The :mod:`formatter` module is pending deprecation and is slated for removal
+ in Python 3.6.
+
+* ``MD5`` as the default *digestmod* for the :func:`hmac.new` function is
+ deprecated. Python 3.6 will require an explicit digest name or constructor as
+ *digestmod* argument.
+
+* The internal ``Netrc`` class in the :mod:`ftplib` module has been documented
+ as deprecated in its docstring for quite some time. It now emits a
+ :exc:`DeprecationWarning` and will be removed completely in Python 3.5.
+
+* The undocumented *endtime* argument to :meth:`subprocess.Popen.wait` should
+ not have been exposed and is hopefully not in use; it is deprecated and
+ will mostly likely be removed in Python 3.5.
+
+* The *strict* argument of :class:`~html.parser.HTMLParser` is deprecated.
+
+* The :mod:`plistlib` :func:`~plistlib.readPlist`,
+ :func:`~plistlib.writePlist`, :func:`~plistlib.readPlistFromBytes`, and
+ :func:`~plistlib.writePlistToBytes` functions are deprecated in favor of the
+ corresponding new functions :func:`~plistlib.load`, :func:`~plistlib.dump`,
+ :func:`~plistlib.loads`, and :func:`~plistlib.dumps`. :func:`~plistlib.Data`
+ is deprecated in favor of just using the :class:`bytes` constructor.
+
+* The :mod:`sysconfig` key ``SO`` is deprecated, it has been replaced by
+ ``EXT_SUFFIX``.
+
+* The ``U`` mode accepted by various ``open`` functions is deprecated.
+ In Python3 it does not do anything useful, and should be replaced by
+ appropriate uses of :class:`io.TextIOWrapper` (if needed) and its *newline*
+ argument.
+
+* The *parser* argument of :func:`xml.etree.ElementTree.iterparse` has
+ been deprecated, as has the *html* argument of
+ :func:`~xml.etree.ElementTree.XMLParser`. To prepare for the removal of the
+ latter, all arguments to ``XMLParser`` should be passed by keyword.
+
+
+Deprecated Features
+-------------------
+
+* Running :ref:`idle` with the ``-n`` flag (no subprocess) is deprecated.
+ However, the feature will not be removed until :issue:`18823` is resolved.
+
+* The site module adding a "site-python" directory to sys.path, if it
+ exists, is deprecated (:issue:`19375`).
+
+
+
+Removed
+=======
+
+
+Operating Systems No Longer Supported
+-------------------------------------
+
+Support for the following operating systems has been removed from the source
+and build tools:
+
+* OS/2 (:issue:`16135`).
+* Windows 2000 (changeset e52df05b496a).
+* Windows systems where ``COMSPEC`` points to ``command.com`` (:issue:`14470`).
+* VMS (:issue:`16136`).
+
+
+API and Feature Removals
+------------------------
+
+The following obsolete and previously deprecated APIs and features have been
+removed:
+
+* The unmaintained ``Misc/TextMate`` and ``Misc/vim`` directories have been
+ removed (see the `devguide <http://docs.python.org/devguide>`_
+ for suggestions on what to use instead).
+
+* The ``SO`` makefile macro is removed (it was replaced by the
+ ``SHLIB_SUFFIX`` and ``EXT_SUFFIX`` macros) (:issue:`16754`).
+
+* The ``PyThreadState.tick_counter`` field has been removed; its value has
+ been meaningless since Python 3.2, when the "new GIL" was introduced
+ (:issue:`19199`).
+
+* ``PyLoader`` and ``PyPycLoader`` have been removed from :mod:`importlib`.
+ (Contributed by Taras Lyapun in :issue:`15641`.)
+
+* The *strict* argument to :class:`~http.client.HTTPConnection` and
+ :class:`~http.client.HTTPSConnection` has been removed. HTTP 0.9-style
+ "Simple Responses" are no longer supported.
+
+* The deprecated :mod:`urllib.request.Request` getter and setter methods
+ ``add_data``, ``has_data``, ``get_data``, ``get_type``, ``get_host``,
+ ``get_selector``, ``set_proxy``, ``get_origin_req_host``, and
+ ``is_unverifiable`` have been removed (use direct attribute access instead).
+
+* Support for loading the deprecated ``TYPE_INT64`` has been removed from
+ :mod:`marshal`. (Contributed by Dan Riti in :issue:`15480`.)
+
+* :class:`inspect.Signature`: positional-only parameters are now required
+ to have a valid name.
+
+* :meth:`object.__format__` no longer accepts non-empty format strings, it now
+ raises a :exc:`TypeError` instead. Using a non-empty string has been
+ deprecated since Python 3.2. This change has been made to prevent a
+ situation where previously working (but incorrect) code would start failing
+ if an object gained a __format__ method, which means that your code may now
+ raise a :exc:`TypeError` if you are using an ``'s'`` format code with objects
+ that do not have a __format__ method that handles it. See :issue:`7994` for
+ background.
+
+* :meth:`difflib.SequenceMatcher.isbjunk` and
+ :meth:`difflib.SequenceMatcher.isbpopular` were deprecated in 3.2, and have
+ now been removed: use ``x in sm.bjunk`` and
+ ``x in sm.bpopular``, where *sm* is a :class:`~difflib.SequenceMatcher` object
+ (:issue:`13248`).
+
+
+Code Cleanups
+-------------
+
+* The unused and undocumented internal ``Scanner`` class has been removed from
+ the :mod:`pydoc` module.
+
+* The private and effectively unused ``_gestalt`` module has been removed,
+ along with the private :mod:`platform` functions ``_mac_ver_lookup``,
+ ``_mac_ver_gstalt``, and ``_bcd2str``, which would only have ever been called
+ on badly broken OSX systems (see :issue:`18393`).
+
+* The hardcoded copies of certain :mod:`stat` constants that were included in
+ the :mod:`tarfile` module namespace have been removed.
+
+
+
+Porting to Python 3.4
+=====================
+
+This section lists previously described changes and other bugfixes
+that may require changes to your code.
+
+
+Changes in 'python' Command Behavior
+------------------------------------
+
+* In a posix shell, setting the :envvar:`PATH` environment variable to
+ an empty value is equivalent to not setting it at all. However, setting
+ :envvar:`PYTHONPATH` to an empty value was *not* equivalent to not setting it
+ at all: setting :envvar:`PYTHONPATH` to an empty value was equivalent to
+ setting it to ``.``, which leads to confusion when reasoning by analogy to
+ how :envvar:`PATH` works. The behavior now conforms to the posix convention
+ for :envvar:`PATH`.
+
+* The [X refs, Y blocks] output of a debug (``--with-pydebug``) build of the
+ CPython interpreter is now off by default. It can be re-enabled using the
+ ``-X showrefcount`` option. (Contributed by Ezio Melotti in :issue:`17323`.)
+
+* The python command and most stdlib scripts (as well as :mod:`argparse`) now
+ output ``--version`` information to ``stdout`` instead of ``stderr`` (for
+ issue list see :ref:`other-improvements-3.4` above).
+
+
+Changes in the Python API
+-------------------------
+
+* The ABCs defined in :mod:`importlib.abc` now either raise the appropriate
+ exception or return a default value instead of raising
+ :exc:`NotImplementedError` blindly. This will only affect code calling
+ :func:`super` and falling through all the way to the ABCs. For compatibility,
+ catch both :exc:`NotImplementedError` or the appropriate exception as needed.
+
+* The module type now initializes the :attr:`__package__` and :attr:`__loader__`
+ attributes to ``None`` by default. To determine if these attributes were set
+ in a backwards-compatible fashion, use e.g.
+ ``getattr(module, '__loader__', None) is not None``. (:issue:`17115`.)
+
+* :meth:`importlib.util.module_for_loader` now sets ``__loader__`` and
+ ``__package__`` unconditionally to properly support reloading. If this is not
+ desired then you will need to set these attributes manually. You can use
+ :func:`importlib.util.module_to_load` for module management.
+
+* Import now resets relevant attributes (e.g. ``__name__``, ``__loader__``,
+ ``__package__``, ``__file__``, ``__cached__``) unconditionally when reloading.
+ Note that this restores a pre-3.3 behavior in that it means a module is
+ re-found when re-loaded (:issue:`19413`).
+
+* Frozen packages no longer set ``__path__`` to a list containing the package
+ name, they now set it to an empty list. The previous behavior could cause
+ the import system to do the wrong thing on submodule imports if there was
+ also a directory with the same name as the frozen package. The correct way
+ to determine if a module is a package or not is to use ``hasattr(module,
+ '__path__')`` (:issue:`18065`).
+
+* Frozen modules no longer define a ``__file__`` attribute. It's semantically
+ incorrect for frozen modules to set the attribute as they are not loaded from
+ any explicit location. If you must know that a module comes from frozen code
+ then you can see if the module's ``__spec__.location`` is set to ``'frozen'``,
+ check if the loader is a subclass of
+ :class:`importlib.machinery.FrozenImporter`,
+ or if Python 2 compatibility is necessary you can use :func:`imp.is_frozen`.
+
+* :func:`py_compile.compile` now raises :exc:`FileExistsError` if the file path
+ it would write to is a symlink or a non-regular file. This is to act as a
+ warning that import will overwrite those files with a regular file regardless
+ of what type of file path they were originally.
+
+* :meth:`importlib.abc.SourceLoader.get_source` no longer raises
+ :exc:`ImportError` when the source code being loaded triggers a
+ :exc:`SyntaxError` or :exc:`UnicodeDecodeError`. As :exc:`ImportError` is
+ meant to be raised only when source code cannot be found but it should, it was
+ felt to be over-reaching/overloading of that meaning when the source code is
+ found but improperly structured. If you were catching ImportError before and
+ wish to continue to ignore syntax or decoding issues, catch all three
+ exceptions now.
+
+* :func:`functools.update_wrapper` and :func:`functools.wraps` now correctly
+ set the ``__wrapped__`` attribute to the function being wrapped, even if
+ that function also had its ``__wrapped__`` attribute set. This means
+ ``__wrapped__`` attributes now correctly link a stack of decorated
+ functions rather than every ``__wrapped__`` attribute in the chain
+ referring to the innermost function. Introspection libraries that
+ assumed the previous behaviour was intentional can use
+ :func:`inspect.unwrap` to access the first function in the chain that has
+ no ``__wrapped__`` attribute.
+
+* :func:`inspect.getfullargspec` has been reimplemented on top of
+ :func:`inspect.signature` and hence handles a much wider variety of callable
+ objects than it did in the past. It is expected that additional builtin and
+ extension module callables will gain signature metadata over the course of
+ the Python 3.4 series. Code that assumes that
+ :func:`inspect.getfullargspec` will fail on non-Python callables may need
+ to be adjusted accordingly.
+
+* :class:`importlib.machinery.PathFinder` now passes on the current working
+ directory to objects in :data:`sys.path_hooks` for the empty string. This
+ results in :data:`sys.path_importer_cache` never containing ``''``, thus
+ iterating through :data:`sys.path_importer_cache` based on :data:`sys.path`
+ will not find all keys. A module's ``__file__`` when imported in the current
+ working directory will also now have an absolute path, including when using
+ ``-m`` with the interpreter (except for ``__main__.__file__`` when a script
+ has been executed directly using a relative path) (Contributed by Brett
+ Cannon in :issue:`18416`). is specified on the command-line)
+ (:issue:`18416`).
+
+* The removal of the *strict* argument to :class:`~http.client.HTTPConnection`
+ and :class:`~http.client.HTTPSConnection` changes the meaning of the
+ remaining arguments if you are specifying them positionally rather than by
+ keyword. If you've been paying attention to deprecation warnings your code
+ should already be specifying any additional arguments via keywords.
+
+* Strings between ``from __future__ import ...`` statements now *always* raise
+ a :exc:`SyntaxError`. Previously if there was no leading docstring, an
+ interstitial string would sometimes be ignored. This brings CPython into
+ compliance with the language spec; Jython and PyPy already were.
+ (:issue:`17434`).
+
+* :meth:`ssl.SSLSocket.getpeercert` and :meth:`ssl.SSLSocket.do_handshake`
+ now raise an :exc:`OSError` with ``ENOTCONN`` when the ``SSLSocket`` is not
+ connected, instead of the previous behavior of raising an
+ :exc:`AttributeError`. In addition, :meth:`~ssl.SSLSocket.getpeercert`
+ will raise a :exc:`ValueError` if the handshake has not yet been done.
+
+* :func:`base64.b32decode` now raises a :exc:`binascii.Error` when the
+ input string contains non-b32-alphabet characters, instead of a
+ :exc:`TypeError`. This particular :exc:`TypeError` was missed when the other
+ :exc:`TypeError`\ s were converted. (Contributed by Serhiy Storchaka in
+ :issue:`18011`.) Note: this change was also inadvertently applied in Python
+ 3.3.3.
+
+* The :attr:`~cgi.FieldStorage.file` attribute is now automatically closed when
+ the creating :class:`cgi.FieldStorage` instance is garbage collected. If you
+ were pulling the file object out separately from the :class:`cgi.FieldStorage`
+ instance and not keeping the instance alive, then you should either store the
+ entire :class:`cgi.FieldStorage` instance or read the contents of the file
+ before the :class:`cgi.FieldStorage` instance is garbage collected.
+
+* Calling ``read`` or ``write`` on a closed SSL socket now raises an
+ informative :exc:`ValueError` rather than the previous more mysterious
+ :exc:`AttributeError` (:issue:`9177`).
+
+* :meth:`slice.indices` no longer produces an :exc:`OverflowError` for huge
+ values. As a consequence of this fix, :meth:`slice.indices` now raises a
+ :exc:`ValueError` if given a negative length; previously it returned nonsense
+ values (:issue:`14794`).
+
+* The :class:`complex` constructor, unlike the :mod:`cmath` functions, was
+ incorrectly accepting :class:`float` values if an object's ``__complex__``
+ special method returned one. This now raises a :exc:`TypeError`.
+ (:issue:`16290`.)
+
+* The :class:`int` constructor in 3.2 and 3.3 erroneously accepts :class:`float`
+ values for the *base* parameter. It is unlikely anyone was doing this, but
+ if so, it will now raise a :exc:`TypeError` (:issue:`16772`).
+
+* Defaults for keyword-only arguments are now evaluated *after* defaults for
+ regular keyword arguments, instead of before. Hopefully no one wrote any
+ code that depends on the previous buggy behavior (:issue:`16967`).
+
+* Stale thread states are now cleared after :func:`~os.fork`. This may cause
+ some system resources to be released that previously were incorrectly kept
+ perpetually alive (for example, database connections kept in thread-local
+ storage). (:issue:`17094`.)
+
+* Parameter names in ``__annotations__`` dicts are now mangled properly,
+ similarly to ``__kwdefaults__``. (Contributed by Yury Selivanov in
+ :issue:`20625`).
+
+* :attr:`hashlib.hash.name` now always returns the identifier in lower case.
+ Previously some builtin hashes had uppercase names, but now that it is a
+ formal public interface the naming has been made consistent (:issue:`18532`).
+
+* Because :mod:`unittest.TestSuite` now drops references to tests after they
+ are run, test harnesses that re-use a :class:`~unittest.TestSuite` to re-run
+ a set of tests may fail. Test suites should not be re-used in this fashion
+ since it means state is retained between test runs, breaking the test
+ isolation that :mod:`unittest` is designed to provide. However, if the lack
+ of isolation is considered acceptable, the old behavior can be restored by
+ creating a :mod:`~unittest.TestSuite` subclass that defines a
+ ``_removeTestAtIndex`` method that does nothing (see
+ :meth:`.TestSuite.__iter__`) (:issue:`11798`).
+
+* :mod:`unittest` now uses :mod:`argparse` for command line parsing. There are
+ certain invalid command forms that used to work that are no longer allowed;
+ in theory this should not cause backward compatibility issues since the
+ disallowed command forms didn't make any sense and are unlikely to be in use.
+
+* The :func:`re.split`, :func:`re.findall`, and :func:`re.sub` functions, and
+ the :meth:`~re.match.group` and :meth:`~re.match.groups` methods of
+ ``match`` objects now always return a *bytes* object when the string
+ to be matched is a :term:`bytes-like object`. Previously the return type
+ matched the input type, so if your code was depending on the return value
+ being, say, a ``bytearray``, you will need to change your code.
+
+* :mod:`audioop` functions now raise an error immediately if passed string
+ input, instead of failing randomly later on (:issue:`16685`).
+
+* The new *convert_charrefs* argument to :class:`~html.parser.HTMLParser`
+ currently defaults to ``False`` for backward compatibility, but will
+ eventually be changed to default to ``True``. It is recommended that you add
+ this keyword, with the appropriate value, to any
+ :class:`~html.parser.HTMLParser` calls in your code (:issue:`13633`).
+
+* Since the *digestmod* argument to the :func:`hmac.new` function will in the
+ future have no default, all calls to :func:`hmac.new` should be changed to
+ explicitly specify a *digestmod* (:issue:`17276`).
+
+* Calling :func:`sysconfig.get_config_var` with the ``SO`` key, or looking
+ ``SO`` up in the results of a call to :func:`sysconfig.get_config_vars`
+ is deprecated. This key should be replaced by ``EXT_SUFFIX`` or
+ ``SHLIB_SUFFIX``, depending on the context (:issue:`19555`).
+
+* Any calls to ``open`` functions that specify ``U`` should be modified.
+ ``U`` is ineffective in Python3 and will eventually raise an error if used.
+ Depending on the function, the equivalent of its old Python2 behavior can be
+ achieved using either a *newline* argument, or if necessary by wrapping the
+ stream in :mod:`~io.TextIOWrapper` to use its *newline* argument
+ (:issue:`15204`).
+
+* If you use :ref:`pyvenv <scripts-pyvenv>` in a script and desire that pip
+ *not* be installed, you must add ``--without-pip`` to your command
+ invocation.
+
+* The default behavior of :func:`json.dump` and :func:`json.dumps` when
+ an indent is specified has changed: it no longer produces trailing
+ spaces after the item separating commas at the ends of lines. This
+ will matter only if you have tests that are doing white-space-sensitive
+ comparisons of such output (:issue:`16333`).
+
+* :mod:`doctest` now looks for doctests in extension module ``__doc__``
+ strings, so if your doctest test discovery includes extension modules that
+ have things that look like doctests in them you may see test failures you've
+ never seen before when running your tests (:issue:`3158`).
+
+* The :mod:`collections.abc` module has been slightly refactored as
+ part of the Python startup improvements. As a consequence of this, it is no
+ longer the case that importing :mod:`collections` automatically imports
+ :mod:`collections.abc`. If your program depended on the (undocumented)
+ implicit import, you will need to add an explicit ``import collections.abc``
+ (:issue:`20784`).
+
+
+Changes in the C API
+--------------------
+
+* :c:func:`PyEval_EvalFrameEx`, :c:func:`PyObject_Repr`, and
+ :c:func:`PyObject_Str`, along with some other internal C APIs, now include
+ a debugging assertion that ensures they are not used in situations where
+ they may silently discard a currently active exception. In cases where
+ discarding the active exception is expected and desired (for example,
+ because it has already been saved locally with :c:func:`PyErr_Fetch` or
+ is being deliberately replaced with a different exception), an explicit
+ :c:func:`PyErr_Clear` call will be needed to avoid triggering the
+ assertion when invoking these operations (directly or indirectly) and
+ running against a version of Python that is compiled with assertions
+ enabled.
+
+* :c:func:`PyErr_SetImportError` now sets :exc:`TypeError` when its **msg**
+ argument is not set. Previously only ``NULL`` was returned with no exception
+ set.
+
+* The result of the :c:data:`PyOS_ReadlineFunctionPointer` callback must
+ now be a string allocated by :c:func:`PyMem_RawMalloc` or
+ :c:func:`PyMem_RawRealloc`, or *NULL* if an error occurred, instead of a
+ string allocated by :c:func:`PyMem_Malloc` or :c:func:`PyMem_Realloc`
+ (:issue:`16742`)
+
+* :c:func:`PyThread_set_key_value` now always set the value. In Python
+ 3.3, the function did nothing if the key already exists (if the current
+ value is a non-NULL pointer).
+
+* The ``f_tstate`` (thread state) field of the :c:type:`PyFrameObject`
+ structure has been removed to fix a bug: see :issue:`14432` for the
+ rationale.
diff --git a/Doc/whatsnew/index.rst b/Doc/whatsnew/index.rst
index bc1206b..29902e4 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.4.rst
3.3.rst
3.2.rst
3.1.rst
diff --git a/Include/Python-ast.h b/Include/Python-ast.h
index 00e92d0..67d677b 100644
--- a/Include/Python-ast.h
+++ b/Include/Python-ast.h
@@ -182,8 +182,9 @@ enum _expr_kind {BoolOp_kind=1, BinOp_kind=2, UnaryOp_kind=3, Lambda_kind=4,
SetComp_kind=9, DictComp_kind=10, GeneratorExp_kind=11,
Yield_kind=12, YieldFrom_kind=13, Compare_kind=14,
Call_kind=15, Num_kind=16, Str_kind=17, Bytes_kind=18,
- Ellipsis_kind=19, Attribute_kind=20, Subscript_kind=21,
- Starred_kind=22, Name_kind=23, List_kind=24, Tuple_kind=25};
+ NameConstant_kind=19, Ellipsis_kind=20, Attribute_kind=21,
+ Subscript_kind=22, Starred_kind=23, Name_kind=24,
+ List_kind=25, Tuple_kind=26};
struct _expr {
enum _expr_kind kind;
union {
@@ -279,6 +280,10 @@ struct _expr {
} Bytes;
struct {
+ singleton value;
+ } NameConstant;
+
+ struct {
expr_ty value;
identifier attr;
expr_context_ty ctx;
@@ -359,18 +364,18 @@ struct _excepthandler {
struct _arguments {
asdl_seq *args;
- identifier vararg;
- expr_ty varargannotation;
+ arg_ty vararg;
asdl_seq *kwonlyargs;
- identifier kwarg;
- expr_ty kwargannotation;
- asdl_seq *defaults;
asdl_seq *kw_defaults;
+ arg_ty kwarg;
+ asdl_seq *defaults;
};
struct _arg {
identifier arg;
expr_ty annotation;
+ int lineno;
+ int col_offset;
};
struct _keyword {
@@ -509,6 +514,9 @@ expr_ty _Py_Num(object n, int lineno, int col_offset, PyArena *arena);
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(bytes s, int lineno, int col_offset, PyArena *arena);
+#define NameConstant(a0, a1, a2, a3) _Py_NameConstant(a0, a1, a2, a3)
+expr_ty _Py_NameConstant(singleton value, 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)
@@ -542,11 +550,10 @@ comprehension_ty _Py_comprehension(expr_ty target, expr_ty iter, asdl_seq *
excepthandler_ty _Py_ExceptHandler(expr_ty type, identifier name, asdl_seq *
body, int lineno, int col_offset, PyArena
*arena);
-#define arguments(a0, a1, a2, a3, a4, a5, a6, a7, a8) _Py_arguments(a0, a1, a2, a3, a4, a5, a6, a7, a8)
-arguments_ty _Py_arguments(asdl_seq * args, identifier vararg, expr_ty
- varargannotation, asdl_seq * kwonlyargs, identifier
- kwarg, expr_ty kwargannotation, asdl_seq * defaults,
- asdl_seq * kw_defaults, PyArena *arena);
+#define arguments(a0, a1, a2, a3, a4, a5, a6) _Py_arguments(a0, a1, a2, a3, a4, a5, a6)
+arguments_ty _Py_arguments(asdl_seq * args, arg_ty vararg, asdl_seq *
+ kwonlyargs, asdl_seq * kw_defaults, arg_ty kwarg,
+ asdl_seq * defaults, PyArena *arena);
#define arg(a0, a1, a2) _Py_arg(a0, a1, a2)
arg_ty _Py_arg(identifier arg, expr_ty annotation, PyArena *arena);
#define keyword(a0, a1, a2) _Py_keyword(a0, a1, a2)
diff --git a/Include/Python.h b/Include/Python.h
index a78a721..2dd8290 100644
--- a/Include/Python.h
+++ b/Include/Python.h
@@ -68,6 +68,7 @@
#include "object.h"
#include "objimpl.h"
#include "typeslots.h"
+#include "pyhash.h"
#include "pydebug.h"
diff --git a/Include/abstract.h b/Include/abstract.h
index c9624f3..6e850b8 100644
--- a/Include/abstract.h
+++ b/Include/abstract.h
@@ -144,7 +144,7 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
/* Implemented elsewhere:
- int PyObject_HasAttrString(PyObject *o, char *attr_name);
+ int PyObject_HasAttrString(PyObject *o, const char *attr_name);
Returns 1 if o has the attribute attr_name, and 0 otherwise.
This is equivalent to the Python expression:
@@ -156,7 +156,7 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
/* Implemented elsewhere:
- PyObject* PyObject_GetAttrString(PyObject *o, char *attr_name);
+ PyObject* PyObject_GetAttrString(PyObject *o, const char *attr_name);
Retrieve an attributed named attr_name form object o.
Returns the attribute value on success, or NULL on failure.
@@ -189,7 +189,7 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
/* Implemented elsewhere:
- int PyObject_SetAttrString(PyObject *o, char *attr_name, PyObject *v);
+ int PyObject_SetAttrString(PyObject *o, const char *attr_name, PyObject *v);
Set the value of the attribute named attr_name, for object o,
to the value, v. Returns -1 on failure. This is
@@ -209,7 +209,7 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
/* implemented as a macro:
- int PyObject_DelAttrString(PyObject *o, char *attr_name);
+ int PyObject_DelAttrString(PyObject *o, const char *attr_name);
Delete attribute named attr_name, for object o. Returns
-1 on failure. This is the equivalent of the Python
@@ -284,7 +284,7 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
*/
PyAPI_FUNC(PyObject *) PyObject_CallFunction(PyObject *callable_object,
- char *format, ...);
+ const char *format, ...);
/*
Call a callable Python object, callable_object, with a
@@ -296,8 +296,9 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
*/
- PyAPI_FUNC(PyObject *) PyObject_CallMethod(PyObject *o, char *method,
- char *format, ...);
+ PyAPI_FUNC(PyObject *) PyObject_CallMethod(PyObject *o,
+ const char *method,
+ const char *format, ...);
/*
Call the method named m of object o with a variable number of
@@ -308,8 +309,9 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
Python expression: o.method(args).
*/
- PyAPI_FUNC(PyObject *) _PyObject_CallMethodId(PyObject *o, _Py_Identifier *method,
- char *format, ...);
+ PyAPI_FUNC(PyObject *) _PyObject_CallMethodId(PyObject *o,
+ _Py_Identifier *method,
+ const char *format, ...);
/*
Like PyObject_CallMethod, but expect a _Py_Identifier* as the
@@ -317,13 +319,16 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
*/
PyAPI_FUNC(PyObject *) _PyObject_CallFunction_SizeT(PyObject *callable,
- char *format, ...);
+ const char *format,
+ ...);
PyAPI_FUNC(PyObject *) _PyObject_CallMethod_SizeT(PyObject *o,
- char *name,
- char *format, ...);
+ const char *name,
+ const char *format,
+ ...);
PyAPI_FUNC(PyObject *) _PyObject_CallMethodId_SizeT(PyObject *o,
_Py_Identifier *name,
- char *format, ...);
+ const char *format,
+ ...);
PyAPI_FUNC(PyObject *) PyObject_CallFunctionObjArgs(PyObject *callable,
...);
@@ -339,11 +344,10 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
PyAPI_FUNC(PyObject *) PyObject_CallMethodObjArgs(PyObject *o,
PyObject *method, ...);
- PyAPI_FUNC(PyObject *) _PyObject_CallMethodObjIdArgs(PyObject *o,
+ PyAPI_FUNC(PyObject *) _PyObject_CallMethodIdObjArgs(PyObject *o,
struct _Py_Identifier *method,
...);
-
/*
Call the method named m of object o with a variable number of
C arguments. The C arguments are provided as PyObject *
@@ -404,7 +408,8 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
#define PyObject_Length PyObject_Size
#ifndef Py_LIMITED_API
- PyAPI_FUNC(Py_ssize_t) _PyObject_LengthHint(PyObject *o, Py_ssize_t);
+ PyAPI_FUNC(int) _PyObject_HasLen(PyObject *o);
+ PyAPI_FUNC(Py_ssize_t) PyObject_LengthHint(PyObject *o, Py_ssize_t);
#endif
/*
@@ -429,7 +434,7 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
statement: o[key]=v.
*/
- PyAPI_FUNC(int) PyObject_DelItemString(PyObject *o, char *key);
+ PyAPI_FUNC(int) PyObject_DelItemString(PyObject *o, const char *key);
/*
Remove the mapping for object, key, from the object *o.
@@ -1016,7 +1021,7 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
PyAPI_FUNC(PyObject *) PySequence_Fast(PyObject *o, const char* m);
/*
- Returns the sequence, o, as a list, unless it's already a
+ Return the sequence, o, as a list, unless it's already a
tuple or list. Use PySequence_Fast_GET_ITEM to access the
members of this list, and PySequence_Fast_GET_SIZE to get its length.
@@ -1151,7 +1156,7 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
/* implemented as a macro:
- int PyMapping_DelItemString(PyObject *o, char *key);
+ int PyMapping_DelItemString(PyObject *o, const char *key);
Remove the mapping for object, key, from the object *o.
Returns -1 on failure. This is equivalent to
@@ -1169,7 +1174,7 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
*/
#define PyMapping_DelItem(O,K) PyObject_DelItem((O),(K))
- PyAPI_FUNC(int) PyMapping_HasKeyString(PyObject *o, char *key);
+ PyAPI_FUNC(int) PyMapping_HasKeyString(PyObject *o, const char *key);
/*
On success, return 1 if the mapping object has the key, key,
@@ -1213,7 +1218,8 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
*/
- PyAPI_FUNC(PyObject *) PyMapping_GetItemString(PyObject *o, char *key);
+ PyAPI_FUNC(PyObject *) PyMapping_GetItemString(PyObject *o,
+ const char *key);
/*
Return element of o corresponding to the object, key, or NULL
@@ -1221,7 +1227,7 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
o[key].
*/
- PyAPI_FUNC(int) PyMapping_SetItemString(PyObject *o, char *key,
+ PyAPI_FUNC(int) PyMapping_SetItemString(PyObject *o, const char *key,
PyObject *value);
/*
diff --git a/Include/asdl.h b/Include/asdl.h
index 6bf618f..495153c 100644
--- a/Include/asdl.h
+++ b/Include/asdl.h
@@ -5,6 +5,7 @@ typedef PyObject * identifier;
typedef PyObject * string;
typedef PyObject * bytes;
typedef PyObject * object;
+typedef PyObject * singleton;
/* It would be nice if the code generated by asdl_c.py was completely
independent of Python, but it is a goal the requires too much work
@@ -24,17 +25,19 @@ typedef struct {
int elements[1];
} asdl_int_seq;
-asdl_seq *asdl_seq_new(Py_ssize_t size, PyArena *arena);
-asdl_int_seq *asdl_int_seq_new(Py_ssize_t size, PyArena *arena);
+asdl_seq *_Py_asdl_seq_new(Py_ssize_t size, PyArena *arena);
+asdl_int_seq *_Py_asdl_int_seq_new(Py_ssize_t size, PyArena *arena);
#define asdl_seq_GET(S, I) (S)->elements[(I)]
#define asdl_seq_LEN(S) ((S) == NULL ? 0 : (S)->size)
#ifdef Py_DEBUG
-#define asdl_seq_SET(S, I, V) { \
- int _asdl_i = (I); \
- assert((S) && _asdl_i < (S)->size); \
+#define asdl_seq_SET(S, I, V) \
+ do { \
+ Py_ssize_t _asdl_i = (I); \
+ assert((S) != NULL); \
+ assert(_asdl_i < (S)->size); \
(S)->elements[_asdl_i] = (V); \
-}
+ } while (0)
#else
#define asdl_seq_SET(S, I, V) (S)->elements[I] = (V)
#endif
diff --git a/Include/ast.h b/Include/ast.h
index 055e8dc..6a8c816 100644
--- a/Include/ast.h
+++ b/Include/ast.h
@@ -10,6 +10,11 @@ PyAPI_FUNC(mod_ty) PyAST_FromNode(
PyCompilerFlags *flags,
const char *filename, /* decoded from the filesystem encoding */
PyArena *arena);
+PyAPI_FUNC(mod_ty) PyAST_FromNodeObject(
+ const node *n,
+ PyCompilerFlags *flags,
+ PyObject *filename,
+ PyArena *arena);
#ifdef __cplusplus
}
diff --git a/Include/bytearrayobject.h b/Include/bytearrayobject.h
index eccd44c..a757b88 100644
--- a/Include/bytearrayobject.h
+++ b/Include/bytearrayobject.h
@@ -22,10 +22,11 @@ extern "C" {
#ifndef Py_LIMITED_API
typedef struct {
PyObject_VAR_HEAD
+ Py_ssize_t ob_alloc; /* How many bytes allocated in ob_bytes */
+ char *ob_bytes; /* Physical backing buffer */
+ char *ob_start; /* Logical start inside ob_bytes */
/* XXX(nnorwitz): should ob_exports be Py_ssize_t? */
- int ob_exports; /* how many buffer exports */
- Py_ssize_t ob_alloc; /* How many bytes allocated */
- char *ob_bytes;
+ int ob_exports; /* How many buffer exports */
} PyByteArrayObject;
#endif
@@ -49,8 +50,8 @@ PyAPI_FUNC(int) PyByteArray_Resize(PyObject *, Py_ssize_t);
#ifndef Py_LIMITED_API
#define PyByteArray_AS_STRING(self) \
(assert(PyByteArray_Check(self)), \
- Py_SIZE(self) ? ((PyByteArrayObject *)(self))->ob_bytes : _PyByteArray_empty_string)
-#define PyByteArray_GET_SIZE(self) (assert(PyByteArray_Check(self)),Py_SIZE(self))
+ Py_SIZE(self) ? ((PyByteArrayObject *)(self))->ob_start : _PyByteArray_empty_string)
+#define PyByteArray_GET_SIZE(self) (assert(PyByteArray_Check(self)), Py_SIZE(self))
PyAPI_DATA(char) _PyByteArray_empty_string[];
#endif
diff --git a/Include/bytesobject.h b/Include/bytesobject.h
index d7c7ffd..0ee8d36 100644
--- a/Include/bytesobject.h
+++ b/Include/bytesobject.h
@@ -86,11 +86,11 @@ PyAPI_FUNC(PyObject *) _PyBytes_Join(PyObject *sep, PyObject *x);
0-terminated (passing a string with embedded NULL characters will
cause an exception). */
PyAPI_FUNC(int) PyBytes_AsStringAndSize(
- register PyObject *obj, /* string or Unicode object */
- register char **s, /* pointer to buffer variable */
- register Py_ssize_t *len /* pointer to length variable or NULL
- (only possible for 0-terminated
- strings) */
+ PyObject *obj, /* string or Unicode object */
+ char **s, /* pointer to buffer variable */
+ Py_ssize_t *len /* pointer to length variable or NULL
+ (only possible for 0-terminated
+ strings) */
);
/* Using the current locale, insert the thousands grouping
diff --git a/Include/compile.h b/Include/compile.h
index ac2636d..c6650d7f 100644
--- a/Include/compile.h
+++ b/Include/compile.h
@@ -36,11 +36,27 @@ PyAPI_FUNC(PyCodeObject *) PyAST_CompileEx(
PyCompilerFlags *flags,
int optimize,
PyArena *arena);
-PyAPI_FUNC(PyFutureFeatures *) PyFuture_FromAST(struct _mod *, const char *);
+PyAPI_FUNC(PyCodeObject *) PyAST_CompileObject(
+ struct _mod *mod,
+ PyObject *filename,
+ PyCompilerFlags *flags,
+ int optimize,
+ PyArena *arena);
+PyAPI_FUNC(PyFutureFeatures *) PyFuture_FromAST(
+ struct _mod * mod,
+ const char *filename /* decoded from the filesystem encoding */
+ );
+PyAPI_FUNC(PyFutureFeatures *) PyFuture_FromASTObject(
+ struct _mod * mod,
+ PyObject *filename
+ );
/* _Py_Mangle is defined in compile.c */
PyAPI_FUNC(PyObject*) _Py_Mangle(PyObject *p, PyObject *name);
+#define PY_INVALID_STACK_EFFECT INT_MAX
+PyAPI_FUNC(int) PyCompile_OpcodeStackEffect(int opcode, int oparg);
+
#ifdef __cplusplus
}
#endif
diff --git a/Include/dictobject.h b/Include/dictobject.h
index d89aac8..ef122bd 100644
--- a/Include/dictobject.h
+++ b/Include/dictobject.h
@@ -53,6 +53,10 @@ PyAPI_FUNC(PyObject *) PyDict_GetItem(PyObject *mp, PyObject *key);
PyAPI_FUNC(PyObject *) PyDict_GetItemWithError(PyObject *mp, PyObject *key);
PyAPI_FUNC(PyObject *) _PyDict_GetItemIdWithError(PyObject *dp,
struct _Py_Identifier *key);
+#ifndef Py_LIMITED_API
+PyAPI_FUNC(PyObject *) PyDict_SetDefault(
+ PyObject *mp, PyObject *key, PyObject *defaultobj);
+#endif
PyAPI_FUNC(int) PyDict_SetItem(PyObject *mp, PyObject *key, PyObject *item);
PyAPI_FUNC(int) PyDict_DelItem(PyObject *mp, PyObject *key);
PyAPI_FUNC(void) PyDict_Clear(PyObject *mp);
@@ -109,9 +113,11 @@ PyAPI_FUNC(int) _PyDict_SetItemId(PyObject *dp, struct _Py_Identifier *key, PyOb
PyAPI_FUNC(int) PyDict_DelItemString(PyObject *dp, const char *key);
#ifndef Py_LIMITED_API
+PyAPI_FUNC(int) _PyDict_DelItemId(PyObject *mp, struct _Py_Identifier *key);
+PyAPI_FUNC(void) _PyDict_DebugMallocStats(FILE *out);
+
int _PyObjectDict_SetItem(PyTypeObject *tp, PyObject **dictptr, PyObject *name, PyObject *value);
PyObject *_PyDict_LoadGlobal(PyDictObject *, PyDictObject *, PyObject *);
-PyAPI_FUNC(void) _PyDict_DebugMallocStats(FILE *out);
#endif
#ifdef __cplusplus
diff --git a/Include/fileobject.h b/Include/fileobject.h
index a99c94d..0939744 100644
--- a/Include/fileobject.h
+++ b/Include/fileobject.h
@@ -8,8 +8,9 @@ extern "C" {
#define PY_STDIOTEXTMODE "b"
-PyAPI_FUNC(PyObject *) PyFile_FromFd(int, char *, char *, int, char *, char *,
- char *, int);
+PyAPI_FUNC(PyObject *) PyFile_FromFd(int, const char *, const char *, int,
+ const char *, const char *,
+ const char *, int);
PyAPI_FUNC(PyObject *) PyFile_GetLine(PyObject *, int);
PyAPI_FUNC(int) PyFile_WriteObject(PyObject *, PyObject *, int);
PyAPI_FUNC(int) PyFile_WriteString(const char *, PyObject *);
diff --git a/Include/fileutils.h b/Include/fileutils.h
index 7c18cf2..e9bad80 100644
--- a/Include/fileutils.h
+++ b/Include/fileutils.h
@@ -27,11 +27,21 @@ PyAPI_FUNC(int) _Py_stat(
struct stat *statbuf);
#endif
+#ifndef Py_LIMITED_API
+PyAPI_FUNC(int) _Py_open(
+ const char *pathname,
+ int flags);
+#endif
+
PyAPI_FUNC(FILE *) _Py_wfopen(
const wchar_t *path,
const wchar_t *mode);
PyAPI_FUNC(FILE*) _Py_fopen(
+ const char *pathname,
+ const char *mode);
+
+PyAPI_FUNC(FILE*) _Py_fopen_obj(
PyObject *path,
const char *mode);
@@ -53,6 +63,15 @@ PyAPI_FUNC(wchar_t*) _Py_wgetcwd(
wchar_t *buf,
size_t size);
+#ifndef Py_LIMITED_API
+PyAPI_FUNC(int) _Py_get_inheritable(int fd);
+
+PyAPI_FUNC(int) _Py_set_inheritable(int fd, int inheritable,
+ int *atomic_flag_works);
+
+PyAPI_FUNC(int) _Py_dup(int fd);
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/Include/frameobject.h b/Include/frameobject.h
index 33f73af..966ff1f 100644
--- a/Include/frameobject.h
+++ b/Include/frameobject.h
@@ -36,8 +36,9 @@ typedef struct _frame {
non-generator frames. See the save_exc_state and swap_exc_state
functions in ceval.c for details of their use. */
PyObject *f_exc_type, *f_exc_value, *f_exc_traceback;
+ /* Borrowed reference to a generator, or NULL */
+ PyObject *f_gen;
- PyThreadState *f_tstate;
int f_lasti; /* Last instruction if called */
/* Call PyFrame_GetLineNumber() instead of reading this field
directly. As of 2.3 f_lineno is only valid when tracing is
@@ -46,6 +47,7 @@ typedef struct _frame {
bytecode index. */
int f_lineno; /* Current line number */
int f_iblock; /* index in f_blockstack */
+ char f_executing; /* whether the frame is still executing */
PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */
PyObject *f_localsplus[1]; /* locals+stack, dynamically sized */
} PyFrameObject;
@@ -75,6 +77,8 @@ PyAPI_FUNC(PyObject **) PyFrame_ExtendStack(PyFrameObject *, int, int);
/* Conversions between "fast locals" and locals in dictionary */
PyAPI_FUNC(void) PyFrame_LocalsToFast(PyFrameObject *, int);
+
+PyAPI_FUNC(int) PyFrame_FastToLocalsWithError(PyFrameObject *f);
PyAPI_FUNC(void) PyFrame_FastToLocals(PyFrameObject *);
PyAPI_FUNC(int) PyFrame_ClearFreeList(void);
diff --git a/Include/genobject.h b/Include/genobject.h
index ed451ba..65f1ecf 100644
--- a/Include/genobject.h
+++ b/Include/genobject.h
@@ -36,6 +36,8 @@ PyAPI_FUNC(PyObject *) PyGen_New(struct _frame *);
PyAPI_FUNC(int) PyGen_NeedsFinalizing(PyGenObject *);
PyAPI_FUNC(int) _PyGen_FetchStopIterationValue(PyObject **);
PyObject *_PyGen_Send(PyGenObject *, PyObject *);
+PyAPI_FUNC(void) _PyGen_Finalize(PyObject *self);
+
#ifdef __cplusplus
}
diff --git a/Include/grammar.h b/Include/grammar.h
index 8426da3..ba7d19d 100644
--- a/Include/grammar.h
+++ b/Include/grammar.h
@@ -69,14 +69,14 @@ typedef struct {
/* FUNCTIONS */
grammar *newgrammar(int start);
-dfa *adddfa(grammar *g, int type, char *name);
+dfa *adddfa(grammar *g, int type, const char *name);
int addstate(dfa *d);
void addarc(dfa *d, int from, int to, int lbl);
dfa *PyGrammar_FindDFA(grammar *g, int type);
-int addlabel(labellist *ll, int type, char *str);
-int findlabel(labellist *ll, int type, char *str);
-char *PyGrammar_LabelRepr(label *lb);
+int addlabel(labellist *ll, int type, const char *str);
+int findlabel(labellist *ll, int type, const char *str);
+const char *PyGrammar_LabelRepr(label *lb);
void translatelabels(grammar *g);
void addfirstsets(grammar *g);
diff --git a/Include/import.h b/Include/import.h
index 73c86ee..afdfac2 100644
--- a/Include/import.h
+++ b/Include/import.h
@@ -13,19 +13,19 @@ PyMODINIT_FUNC PyInit_imp(void);
PyAPI_FUNC(long) PyImport_GetMagicNumber(void);
PyAPI_FUNC(const char *) PyImport_GetMagicTag(void);
PyAPI_FUNC(PyObject *) PyImport_ExecCodeModule(
- char *name, /* UTF-8 encoded string */
+ const char *name, /* UTF-8 encoded string */
PyObject *co
);
PyAPI_FUNC(PyObject *) PyImport_ExecCodeModuleEx(
- char *name, /* UTF-8 encoded string */
+ const char *name, /* UTF-8 encoded string */
PyObject *co,
- char *pathname /* decoded from the filesystem encoding */
+ const char *pathname /* decoded from the filesystem encoding */
);
PyAPI_FUNC(PyObject *) PyImport_ExecCodeModuleWithPathnames(
- char *name, /* UTF-8 encoded string */
+ const char *name, /* UTF-8 encoded string */
PyObject *co,
- char *pathname, /* decoded from the filesystem encoding */
- char *cpathname /* decoded from the filesystem encoding */
+ const char *pathname, /* decoded from the filesystem encoding */
+ const char *cpathname /* decoded from the filesystem encoding */
);
PyAPI_FUNC(PyObject *) PyImport_ExecCodeModuleObject(
PyObject *name,
@@ -72,7 +72,7 @@ PyAPI_FUNC(int) PyImport_ImportFrozenModuleObject(
PyObject *name
);
PyAPI_FUNC(int) PyImport_ImportFrozenModule(
- char *name /* UTF-8 encoded string */
+ const char *name /* UTF-8 encoded string */
);
#ifndef Py_LIMITED_API
@@ -92,12 +92,12 @@ PyAPI_FUNC(PyObject *) _PyImport_FindBuiltin(
PyAPI_FUNC(PyObject *) _PyImport_FindExtensionObject(PyObject *, PyObject *);
PyAPI_FUNC(int) _PyImport_FixupBuiltin(
PyObject *mod,
- char *name /* UTF-8 encoded string */
+ const char *name /* UTF-8 encoded string */
);
PyAPI_FUNC(int) _PyImport_FixupExtensionObject(PyObject*, PyObject *, PyObject *);
struct _inittab {
- char *name; /* ASCII encoded string */
+ const char *name; /* ASCII encoded string */
PyObject* (*initfunc)(void);
};
PyAPI_DATA(struct _inittab *) PyImport_Inittab;
@@ -113,15 +113,15 @@ PyAPI_FUNC(int) PyImport_AppendInittab(
#ifndef Py_LIMITED_API
struct _frozen {
- char *name; /* ASCII encoded string */
- unsigned char *code;
+ const char *name; /* ASCII encoded string */
+ const unsigned char *code;
int size;
};
/* Embedding apps may change this pointer to point to their favorite
collection of frozen modules: */
-PyAPI_DATA(struct _frozen *) PyImport_FrozenModules;
+PyAPI_DATA(const struct _frozen *) PyImport_FrozenModules;
#endif
#ifdef __cplusplus
diff --git a/Include/longobject.h b/Include/longobject.h
index 1c1c5de..ff43309 100644
--- a/Include/longobject.h
+++ b/Include/longobject.h
@@ -52,6 +52,19 @@ PyAPI_FUNC(PyObject *) PyLong_GetInfo(void);
#error "sizeof(pid_t) is neither sizeof(int), sizeof(long) or sizeof(long long)"
#endif /* SIZEOF_PID_T */
+#if SIZEOF_VOID_P == SIZEOF_INT
+# define _Py_PARSE_INTPTR "i"
+# define _Py_PARSE_UINTPTR "I"
+#elif SIZEOF_VOID_P == SIZEOF_LONG
+# define _Py_PARSE_INTPTR "l"
+# define _Py_PARSE_UINTPTR "k"
+#elif defined(SIZEOF_LONG_LONG) && SIZEOF_VOID_P == SIZEOF_LONG_LONG
+# define _Py_PARSE_INTPTR "L"
+# define _Py_PARSE_UINTPTR "K"
+#else
+# error "void* different in size from int, long and long long"
+#endif /* SIZEOF_VOID_P */
+
/* Used by Python/mystrtoul.c. */
#ifndef Py_LIMITED_API
PyAPI_DATA(unsigned char) _PyLong_DigitValue[256];
@@ -80,7 +93,7 @@ PyAPI_FUNC(unsigned PY_LONG_LONG) PyLong_AsUnsignedLongLongMask(PyObject *);
PyAPI_FUNC(PY_LONG_LONG) PyLong_AsLongLongAndOverflow(PyObject *, int *);
#endif /* HAVE_LONG_LONG */
-PyAPI_FUNC(PyObject *) PyLong_FromString(char *, char **, int);
+PyAPI_FUNC(PyObject *) PyLong_FromString(const 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);
@@ -182,8 +195,8 @@ PyAPI_FUNC(int) _PyLong_FormatAdvancedWriter(
/* These aren't really part of the int object, but they're handy. The
functions are in Python/mystrtoul.c.
*/
-PyAPI_FUNC(unsigned long) PyOS_strtoul(char *, char **, int);
-PyAPI_FUNC(long) PyOS_strtol(char *, char **, int);
+PyAPI_FUNC(unsigned long) PyOS_strtoul(const char *, char **, int);
+PyAPI_FUNC(long) PyOS_strtol(const char *, char **, int);
#ifdef __cplusplus
}
diff --git a/Include/marshal.h b/Include/marshal.h
index e96d062..09d9337 100644
--- a/Include/marshal.h
+++ b/Include/marshal.h
@@ -7,7 +7,7 @@
extern "C" {
#endif
-#define Py_MARSHAL_VERSION 2
+#define Py_MARSHAL_VERSION 4
PyAPI_FUNC(void) PyMarshal_WriteLongToFile(long, FILE *, int);
PyAPI_FUNC(void) PyMarshal_WriteObjectToFile(PyObject *, FILE *, int);
@@ -19,7 +19,8 @@ PyAPI_FUNC(int) PyMarshal_ReadShortFromFile(FILE *);
PyAPI_FUNC(PyObject *) PyMarshal_ReadObjectFromFile(FILE *);
PyAPI_FUNC(PyObject *) PyMarshal_ReadLastObjectFromFile(FILE *);
#endif
-PyAPI_FUNC(PyObject *) PyMarshal_ReadObjectFromString(char *, Py_ssize_t);
+PyAPI_FUNC(PyObject *) PyMarshal_ReadObjectFromString(const char *,
+ Py_ssize_t);
#ifdef __cplusplus
}
diff --git a/Include/modsupport.h b/Include/modsupport.h
index ecf1dcc..5de0458 100644
--- a/Include/modsupport.h
+++ b/Include/modsupport.h
@@ -26,7 +26,7 @@ PyAPI_FUNC(PyObject *) _Py_VaBuildValue_SizeT(const char *, va_list);
/* Due to a glitch in 3.2, the _SizeT versions weren't exported from the DLL. */
#if !defined(PY_SSIZE_T_CLEAN) || !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000
PyAPI_FUNC(int) PyArg_Parse(PyObject *, const char *, ...);
-PyAPI_FUNC(int) PyArg_ParseTuple(PyObject *, const char *, ...) Py_FORMAT_PARSETUPLE(PyArg_ParseTuple, 2, 3);
+PyAPI_FUNC(int) PyArg_ParseTuple(PyObject *, const char *, ...);
PyAPI_FUNC(int) PyArg_ParseTupleAndKeywords(PyObject *, PyObject *,
const char *, char **, ...);
PyAPI_FUNC(int) PyArg_ValidateKeywordArguments(PyObject *);
@@ -36,6 +36,7 @@ PyAPI_FUNC(PyObject *) _Py_BuildValue_SizeT(const char *, ...);
#endif
#ifndef Py_LIMITED_API
PyAPI_FUNC(int) _PyArg_NoKeywords(const char *funcname, PyObject *kw);
+PyAPI_FUNC(int) _PyArg_NoPositional(const char *funcname, PyObject *args);
PyAPI_FUNC(int) PyArg_VaParse(PyObject *, const char *, va_list);
PyAPI_FUNC(int) PyArg_VaParseTupleAndKeywords(PyObject *, PyObject *,
diff --git a/Include/moduleobject.h b/Include/moduleobject.h
index 8013dd9..f119364 100644
--- a/Include/moduleobject.h
+++ b/Include/moduleobject.h
@@ -25,6 +25,7 @@ PyAPI_FUNC(const char *) PyModule_GetFilename(PyObject *);
PyAPI_FUNC(PyObject *) PyModule_GetFilenameObject(PyObject *);
#ifndef Py_LIMITED_API
PyAPI_FUNC(void) _PyModule_Clear(PyObject *);
+PyAPI_FUNC(void) _PyModule_ClearDict(PyObject *);
#endif
PyAPI_FUNC(struct PyModuleDef*) PyModule_GetDef(PyObject*);
PyAPI_FUNC(void*) PyModule_GetState(PyObject*);
diff --git a/Include/object.h b/Include/object.h
index cdeb06c..7584d4c 100644
--- a/Include/object.h
+++ b/Include/object.h
@@ -143,7 +143,8 @@ typedef struct _Py_Identifier {
PyObject *object;
} _Py_Identifier;
-#define _Py_static_string(varname, value) static _Py_Identifier varname = { 0, value, 0 }
+#define _Py_static_string_init(value) { 0, value, 0 }
+#define _Py_static_string(varname, value) static _Py_Identifier varname = _Py_static_string_init(value)
#define _Py_IDENTIFIER(varname) _Py_static_string(PyId_##varname, #varname)
/*
@@ -362,7 +363,7 @@ typedef struct _typeobject {
PyBufferProcs *tp_as_buffer;
/* Flags to define presence of optional/expanded features */
- long tp_flags;
+ unsigned long tp_flags;
const char *tp_doc; /* Documentation string */
@@ -408,6 +409,8 @@ typedef struct _typeobject {
/* Type attribute cache version tag. Added in version 2.6 */
unsigned int tp_version_tag;
+ destructor tp_finalize;
+
#ifdef COUNT_ALLOCS
/* these must be last and never explicitly initialized */
Py_ssize_t tp_allocs;
@@ -428,7 +431,7 @@ typedef struct{
const char* name;
int basicsize;
int itemsize;
- int flags;
+ unsigned int flags;
PyType_Slot *slots; /* terminated by slot==0. */
} PyType_Spec;
@@ -436,6 +439,9 @@ PyAPI_FUNC(PyObject*) PyType_FromSpec(PyType_Spec*);
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000
PyAPI_FUNC(PyObject*) PyType_FromSpecWithBases(PyType_Spec*, PyObject*);
#endif
+#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03040000
+PyAPI_FUNC(void*) PyType_GetSlot(PyTypeObject*, int);
+#endif
#ifndef Py_LIMITED_API
/* The *real* layout of a type object when allocated on the heap */
@@ -470,7 +476,7 @@ PyAPI_DATA(PyTypeObject) PyType_Type; /* built-in 'type' */
PyAPI_DATA(PyTypeObject) PyBaseObject_Type; /* built-in 'object' */
PyAPI_DATA(PyTypeObject) PySuper_Type; /* built-in 'super' */
-PyAPI_FUNC(long) PyType_GetFlags(PyTypeObject*);
+PyAPI_FUNC(unsigned long) PyType_GetFlags(PyTypeObject*);
#define PyType_Check(op) \
PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_TYPE_SUBCLASS)
@@ -489,6 +495,11 @@ PyAPI_FUNC(PyTypeObject *) _PyType_CalculateMetaclass(PyTypeObject *, PyObject *
PyAPI_FUNC(unsigned int) PyType_ClearCache(void);
PyAPI_FUNC(void) PyType_Modified(PyTypeObject *);
+#ifndef Py_LIMITED_API
+PyAPI_FUNC(PyObject *) _PyType_GetDocFromInternalDoc(const char *, const char *);
+PyAPI_FUNC(PyObject *) _PyType_GetTextSignatureFromInternalDoc(const char *, const char *);
+#endif
+
/* Generic operations on objects */
struct _Py_Identifier;
#ifndef Py_LIMITED_API
@@ -530,6 +541,10 @@ PyAPI_FUNC(int) PyObject_Not(PyObject *);
PyAPI_FUNC(int) PyCallable_Check(PyObject *);
PyAPI_FUNC(void) PyObject_ClearWeakRefs(PyObject *);
+#ifndef Py_LIMITED_API
+PyAPI_FUNC(void) PyObject_CallFinalizer(PyObject *);
+PyAPI_FUNC(int) PyObject_CallFinalizerFromDealloc(PyObject *);
+#endif
/* Same as PyObject_Generic{Get,Set}Attr, but passing the attributes
dict as the last parameter. */
@@ -557,23 +572,6 @@ PyAPI_FUNC(PyObject *) PyObject_Dir(PyObject *);
PyAPI_FUNC(int) Py_ReprEnter(PyObject *);
PyAPI_FUNC(void) Py_ReprLeave(PyObject *);
-/* Helpers for hash functions */
-#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
-
-typedef struct {
- Py_hash_t prefix;
- Py_hash_t suffix;
-} _Py_HashSecret_t;
-PyAPI_DATA(_Py_HashSecret_t) _Py_HashSecret;
-
-#ifdef Py_DEBUG
-PyAPI_DATA(int) _Py_HashSecret_Initialized;
-#endif
-
/* Helper for passing objects to printf and the like */
#define PyObject_REPR(obj) _PyUnicode_AsString(PyObject_Repr(obj))
@@ -604,50 +602,55 @@ given type object has a specified feature.
*/
/* Set if the type object is dynamically allocated */
-#define Py_TPFLAGS_HEAPTYPE (1L<<9)
+#define Py_TPFLAGS_HEAPTYPE (1UL << 9)
/* Set if the type allows subclassing */
-#define Py_TPFLAGS_BASETYPE (1L<<10)
+#define Py_TPFLAGS_BASETYPE (1UL << 10)
/* Set if the type is 'ready' -- fully initialized */
-#define Py_TPFLAGS_READY (1L<<12)
+#define Py_TPFLAGS_READY (1UL << 12)
/* Set while the type is being 'readied', to prevent recursive ready calls */
-#define Py_TPFLAGS_READYING (1L<<13)
+#define Py_TPFLAGS_READYING (1UL << 13)
/* Objects support garbage collection (see objimp.h) */
-#define Py_TPFLAGS_HAVE_GC (1L<<14)
+#define Py_TPFLAGS_HAVE_GC (1UL << 14)
/* These two bits are preserved for Stackless Python, next after this is 17 */
#ifdef STACKLESS
-#define Py_TPFLAGS_HAVE_STACKLESS_EXTENSION (3L<<15)
+#define Py_TPFLAGS_HAVE_STACKLESS_EXTENSION (3UL << 15)
#else
#define Py_TPFLAGS_HAVE_STACKLESS_EXTENSION 0
#endif
/* Objects support type attribute cache */
-#define Py_TPFLAGS_HAVE_VERSION_TAG (1L<<18)
-#define Py_TPFLAGS_VALID_VERSION_TAG (1L<<19)
+#define Py_TPFLAGS_HAVE_VERSION_TAG (1UL << 18)
+#define Py_TPFLAGS_VALID_VERSION_TAG (1UL << 19)
/* Type is abstract and cannot be instantiated */
-#define Py_TPFLAGS_IS_ABSTRACT (1L<<20)
+#define Py_TPFLAGS_IS_ABSTRACT (1UL << 20)
/* These flags are used to determine if a type is a subclass. */
-#define Py_TPFLAGS_INT_SUBCLASS (1L<<23)
-#define Py_TPFLAGS_LONG_SUBCLASS (1L<<24)
-#define Py_TPFLAGS_LIST_SUBCLASS (1L<<25)
-#define Py_TPFLAGS_TUPLE_SUBCLASS (1L<<26)
-#define Py_TPFLAGS_BYTES_SUBCLASS (1L<<27)
-#define Py_TPFLAGS_UNICODE_SUBCLASS (1L<<28)
-#define Py_TPFLAGS_DICT_SUBCLASS (1L<<29)
-#define Py_TPFLAGS_BASE_EXC_SUBCLASS (1L<<30)
-#define Py_TPFLAGS_TYPE_SUBCLASS (1L<<31)
+#define Py_TPFLAGS_LONG_SUBCLASS (1UL << 24)
+#define Py_TPFLAGS_LIST_SUBCLASS (1UL << 25)
+#define Py_TPFLAGS_TUPLE_SUBCLASS (1UL << 26)
+#define Py_TPFLAGS_BYTES_SUBCLASS (1UL << 27)
+#define Py_TPFLAGS_UNICODE_SUBCLASS (1UL << 28)
+#define Py_TPFLAGS_DICT_SUBCLASS (1UL << 29)
+#define Py_TPFLAGS_BASE_EXC_SUBCLASS (1UL << 30)
+#define Py_TPFLAGS_TYPE_SUBCLASS (1UL << 31)
#define Py_TPFLAGS_DEFAULT ( \
Py_TPFLAGS_HAVE_STACKLESS_EXTENSION | \
Py_TPFLAGS_HAVE_VERSION_TAG | \
0)
+/* NOTE: The following flags reuse lower bits (removed as part of the
+ * Python 3.0 transition). */
+
+/* Type structure has tp_finalize member (3.4) */
+#define Py_TPFLAGS_HAVE_FINALIZE (1UL << 0)
+
#ifdef Py_LIMITED_API
#define PyType_HasFeature(t,f) ((PyType_GetFlags(t) & (f)) != 0)
#else
@@ -682,12 +685,6 @@ is not considered to be a reference to the type object, to save
complications in the deallocation function. (This is actually a
decision that's up to the implementer of each new type so if you want,
you can count such references to the type object.)
-
-*** WARNING*** The Py_DECREF macro must have a side-effect-free argument
-since it may evaluate its argument multiple times. (The alternative
-would be to mace it a proper function or assign it to a global temporary
-variable first, both of which are slower; and in a multi-threaded
-environment the global variable trick is not safe.)
*/
/* First define a pile of simple helper macros, one set per special
@@ -704,7 +701,6 @@ PyAPI_DATA(Py_ssize_t) _Py_RefTotal;
PyAPI_FUNC(void) _Py_NegativeRefcount(const char *fname,
int lineno, PyObject *op);
PyAPI_FUNC(PyObject *) _PyDict_Dummy(void);
-PyAPI_FUNC(PyObject *) _PySet_Dummy(void);
PyAPI_FUNC(Py_ssize_t) _Py_GetRefTotal(void);
#define _Py_INC_REFTOTAL _Py_RefTotal++
#define _Py_DEC_REFTOTAL _Py_RefTotal--
@@ -766,15 +762,16 @@ PyAPI_FUNC(void) _Py_Dealloc(PyObject *);
#define Py_INCREF(op) ( \
_Py_INC_REFTOTAL _Py_REF_DEBUG_COMMA \
- ((PyObject*)(op))->ob_refcnt++)
+ ((PyObject *)(op))->ob_refcnt++)
#define Py_DECREF(op) \
do { \
+ PyObject *_py_decref_tmp = (PyObject *)(op); \
if (_Py_DEC_REFTOTAL _Py_REF_DEBUG_COMMA \
- --((PyObject*)(op))->ob_refcnt != 0) \
- _Py_CHECK_REFCNT(op) \
+ --(_py_decref_tmp)->ob_refcnt != 0) \
+ _Py_CHECK_REFCNT(_py_decref_tmp) \
else \
- _Py_Dealloc((PyObject *)(op)); \
+ _Py_Dealloc(_py_decref_tmp); \
} while (0)
/* Safely decref `op` and set `op` to NULL, especially useful in tp_clear
@@ -813,16 +810,27 @@ PyAPI_FUNC(void) _Py_Dealloc(PyObject *);
*/
#define Py_CLEAR(op) \
do { \
- if (op) { \
- PyObject *_py_tmp = (PyObject *)(op); \
+ PyObject *_py_tmp = (PyObject *)(op); \
+ if (_py_tmp != NULL) { \
(op) = NULL; \
Py_DECREF(_py_tmp); \
} \
} while (0)
/* Macros to use in case the object pointer may be NULL: */
-#define Py_XINCREF(op) do { if ((op) == NULL) ; else Py_INCREF(op); } while (0)
-#define Py_XDECREF(op) do { if ((op) == NULL) ; else Py_DECREF(op); } while (0)
+#define Py_XINCREF(op) \
+ do { \
+ PyObject *_py_xincref_tmp = (PyObject *)(op); \
+ if (_py_xincref_tmp != NULL) \
+ Py_INCREF(_py_xincref_tmp); \
+ } while (0)
+
+#define Py_XDECREF(op) \
+ do { \
+ PyObject *_py_xdecref_tmp = (PyObject *)(op); \
+ if (_py_xdecref_tmp != NULL) \
+ Py_DECREF(_py_xdecref_tmp); \
+ } while (0)
/*
These are provided as conveniences to Python runtime embedders, so that
diff --git a/Include/objimpl.h b/Include/objimpl.h
index c6b7df4..3f21b70 100644
--- a/Include/objimpl.h
+++ b/Include/objimpl.h
@@ -94,51 +94,27 @@ PyObject_{New, NewVar, Del}.
the object gets initialized via PyObject_{Init, InitVar} after obtaining
the raw memory.
*/
-PyAPI_FUNC(void *) PyObject_Malloc(size_t);
-PyAPI_FUNC(void *) PyObject_Realloc(void *, size_t);
-PyAPI_FUNC(void) PyObject_Free(void *);
+PyAPI_FUNC(void *) PyObject_Malloc(size_t size);
+PyAPI_FUNC(void *) PyObject_Realloc(void *ptr, size_t new_size);
+PyAPI_FUNC(void) PyObject_Free(void *ptr);
+/* This function returns the number of allocated memory blocks, regardless of size */
+PyAPI_FUNC(Py_ssize_t) _Py_GetAllocatedBlocks(void);
/* Macros */
#ifdef WITH_PYMALLOC
#ifndef Py_LIMITED_API
PyAPI_FUNC(void) _PyObject_DebugMallocStats(FILE *out);
#endif /* #ifndef Py_LIMITED_API */
-#ifdef PYMALLOC_DEBUG /* WITH_PYMALLOC && PYMALLOC_DEBUG */
-PyAPI_FUNC(void *) _PyObject_DebugMalloc(size_t nbytes);
-PyAPI_FUNC(void *) _PyObject_DebugRealloc(void *p, size_t nbytes);
-PyAPI_FUNC(void) _PyObject_DebugFree(void *p);
-PyAPI_FUNC(void) _PyObject_DebugDumpAddress(const void *p);
-PyAPI_FUNC(void) _PyObject_DebugCheckAddress(const void *p);
-PyAPI_FUNC(void *) _PyObject_DebugMallocApi(char api, size_t nbytes);
-PyAPI_FUNC(void *) _PyObject_DebugReallocApi(char api, void *p, size_t nbytes);
-PyAPI_FUNC(void) _PyObject_DebugFreeApi(char api, void *p);
-PyAPI_FUNC(void) _PyObject_DebugCheckAddressApi(char api, const void *p);
-PyAPI_FUNC(void *) _PyMem_DebugMalloc(size_t nbytes);
-PyAPI_FUNC(void *) _PyMem_DebugRealloc(void *p, size_t nbytes);
-PyAPI_FUNC(void) _PyMem_DebugFree(void *p);
-#define PyObject_MALLOC _PyObject_DebugMalloc
-#define PyObject_Malloc _PyObject_DebugMalloc
-#define PyObject_REALLOC _PyObject_DebugRealloc
-#define PyObject_Realloc _PyObject_DebugRealloc
-#define PyObject_FREE _PyObject_DebugFree
-#define PyObject_Free _PyObject_DebugFree
-
-#else /* WITH_PYMALLOC && ! PYMALLOC_DEBUG */
+#endif
+
+/* Macros */
#define PyObject_MALLOC PyObject_Malloc
#define PyObject_REALLOC PyObject_Realloc
#define PyObject_FREE PyObject_Free
-#endif
-
-#else /* ! WITH_PYMALLOC */
-#define PyObject_MALLOC PyMem_MALLOC
-#define PyObject_REALLOC PyMem_REALLOC
-#define PyObject_FREE PyMem_FREE
-
-#endif /* WITH_PYMALLOC */
-
#define PyObject_Del PyObject_Free
-#define PyObject_DEL PyObject_FREE
+#define PyObject_DEL PyObject_Free
+
/*
* Generic object allocator interface
@@ -222,6 +198,26 @@ PyAPI_FUNC(PyVarObject *) _PyObject_NewVar(PyTypeObject *, Py_ssize_t);
constructor you would start directly with PyObject_Init/InitVar
*/
+#ifndef Py_LIMITED_API
+typedef struct {
+ /* user context passed as the first argument to the 2 functions */
+ void *ctx;
+
+ /* allocate an arena of size bytes */
+ void* (*alloc) (void *ctx, size_t size);
+
+ /* free an arena */
+ void (*free) (void *ctx, void *ptr, size_t size);
+} PyObjectArenaAllocator;
+
+/* Get the arena allocator. */
+PyAPI_FUNC(void) PyObject_GetArenaAllocator(PyObjectArenaAllocator *allocator);
+
+/* Set the arena allocator. */
+PyAPI_FUNC(void) PyObject_SetArenaAllocator(PyObjectArenaAllocator *allocator);
+#endif
+
+
/*
* Garbage Collection Support
* ==========================
@@ -230,6 +226,10 @@ PyAPI_FUNC(PyVarObject *) _PyObject_NewVar(PyTypeObject *, Py_ssize_t);
/* C equivalent of gc.collect(). */
PyAPI_FUNC(Py_ssize_t) PyGC_Collect(void);
+#ifndef Py_LIMITED_API
+PyAPI_FUNC(Py_ssize_t) _PyGC_CollectNoFail(void);
+#endif
+
/* Test if a type has a GC head */
#define PyType_IS_GC(t) PyType_HasFeature((t), Py_TPFLAGS_HAVE_GC)
@@ -249,13 +249,37 @@ typedef union _gc_head {
union _gc_head *gc_prev;
Py_ssize_t gc_refs;
} gc;
- long double dummy; /* force worst-case alignment */
+ double dummy; /* force worst-case alignment */
} PyGC_Head;
extern PyGC_Head *_PyGC_generation0;
#define _Py_AS_GC(o) ((PyGC_Head *)(o)-1)
+/* Bit 0 is set when tp_finalize is called */
+#define _PyGC_REFS_MASK_FINALIZED (1 << 0)
+/* The (N-1) most significant bits contain the gc state / refcount */
+#define _PyGC_REFS_SHIFT (1)
+#define _PyGC_REFS_MASK (((size_t) -1) << _PyGC_REFS_SHIFT)
+
+#define _PyGCHead_REFS(g) ((g)->gc.gc_refs >> _PyGC_REFS_SHIFT)
+#define _PyGCHead_SET_REFS(g, v) do { \
+ (g)->gc.gc_refs = ((g)->gc.gc_refs & ~_PyGC_REFS_MASK) \
+ | (((size_t)(v)) << _PyGC_REFS_SHIFT); \
+ } while (0)
+#define _PyGCHead_DECREF(g) ((g)->gc.gc_refs -= 1 << _PyGC_REFS_SHIFT)
+
+#define _PyGCHead_FINALIZED(g) (((g)->gc.gc_refs & _PyGC_REFS_MASK_FINALIZED) != 0)
+#define _PyGCHead_SET_FINALIZED(g, v) do { \
+ (g)->gc.gc_refs = ((g)->gc.gc_refs & ~_PyGC_REFS_MASK_FINALIZED) \
+ | (v != 0); \
+ } while (0)
+
+#define _PyGC_FINALIZED(o) _PyGCHead_FINALIZED(_Py_AS_GC(o))
+#define _PyGC_SET_FINALIZED(o, v) _PyGCHead_SET_FINALIZED(_Py_AS_GC(o), v)
+
+#define _PyGC_REFS(o) _PyGCHead_REFS(_Py_AS_GC(o))
+
#define _PyGC_REFS_UNTRACKED (-2)
#define _PyGC_REFS_REACHABLE (-3)
#define _PyGC_REFS_TENTATIVELY_UNREACHABLE (-4)
@@ -264,9 +288,9 @@ extern PyGC_Head *_PyGC_generation0;
* collector it must be safe to call the ob_traverse method. */
#define _PyObject_GC_TRACK(o) do { \
PyGC_Head *g = _Py_AS_GC(o); \
- if (g->gc.gc_refs != _PyGC_REFS_UNTRACKED) \
+ if (_PyGCHead_REFS(g) != _PyGC_REFS_UNTRACKED) \
Py_FatalError("GC object already tracked"); \
- g->gc.gc_refs = _PyGC_REFS_REACHABLE; \
+ _PyGCHead_SET_REFS(g, _PyGC_REFS_REACHABLE); \
g->gc.gc_next = _PyGC_generation0; \
g->gc.gc_prev = _PyGC_generation0->gc.gc_prev; \
g->gc.gc_prev->gc.gc_next = g; \
@@ -279,8 +303,8 @@ extern PyGC_Head *_PyGC_generation0;
*/
#define _PyObject_GC_UNTRACK(o) do { \
PyGC_Head *g = _Py_AS_GC(o); \
- assert(g->gc.gc_refs != _PyGC_REFS_UNTRACKED); \
- g->gc.gc_refs = _PyGC_REFS_UNTRACKED; \
+ assert(_PyGCHead_REFS(g) != _PyGC_REFS_UNTRACKED); \
+ _PyGCHead_SET_REFS(g, _PyGC_REFS_UNTRACKED); \
g->gc.gc_prev->gc.gc_next = g->gc.gc_next; \
g->gc.gc_next->gc.gc_prev = g->gc.gc_prev; \
g->gc.gc_next = NULL; \
@@ -288,7 +312,7 @@ extern PyGC_Head *_PyGC_generation0;
/* True if the object is currently tracked by the GC. */
#define _PyObject_GC_IS_TRACKED(o) \
- ((_Py_AS_GC(o))->gc.gc_refs != _PyGC_REFS_UNTRACKED)
+ (_PyGC_REFS(o) != _PyGC_REFS_UNTRACKED)
/* True if the object may be tracked by the GC in the future, or already is.
This can be useful to implement some optimizations. */
diff --git a/Include/opcode.h b/Include/opcode.h
index a90184d..0936f2d 100644
--- a/Include/opcode.h
+++ b/Include/opcode.h
@@ -49,7 +49,6 @@ extern "C" {
#define BINARY_OR 66
#define INPLACE_POWER 67
#define GET_ITER 68
-#define STORE_LOCALS 69
#define PRINT_EXPR 70
#define LOAD_BUILD_CLASS 71
#define YIELD_FROM 72
@@ -140,6 +139,7 @@ extern "C" {
#define SET_ADD 146
#define MAP_ADD 147
+#define LOAD_CLASSDEREF 148
/* EXCEPT_HANDLER is a special, implicit block type which is created when
entering an except handler. It is not an opcode but we define it here
diff --git a/Include/osdefs.h b/Include/osdefs.h
index 05c0c8e..0c2e34b 100644
--- a/Include/osdefs.h
+++ b/Include/osdefs.h
@@ -9,16 +9,10 @@ extern "C" {
/* Mod by chrish: QNX has WATCOM, but isn't DOS */
#if !defined(__QNX__)
-#if defined(MS_WINDOWS) || defined(__BORLANDC__) || defined(__WATCOMC__) || defined(__DJGPP__) || defined(PYOS_OS2)
-#if defined(PYOS_OS2) && defined(PYCC_GCC)
-#define MAXPATHLEN 260
-#define SEP L'/'
-#define ALTSEP L'\\'
-#else
+#if defined(MS_WINDOWS) || defined(__BORLANDC__) || defined(__WATCOMC__) || defined(__DJGPP__)
#define SEP L'\\'
#define ALTSEP L'/'
#define MAXPATHLEN 256
-#endif
#define DELIM L';'
#endif
#endif
diff --git a/Include/parsetok.h b/Include/parsetok.h
index 911dfc1..2acb854 100644
--- a/Include/parsetok.h
+++ b/Include/parsetok.h
@@ -38,29 +38,49 @@ typedef struct {
PyAPI_FUNC(node *) PyParser_ParseString(const char *, grammar *, int,
perrdetail *);
PyAPI_FUNC(node *) PyParser_ParseFile (FILE *, const char *, grammar *, int,
- char *, char *, perrdetail *);
+ const char *, const char *,
+ perrdetail *);
PyAPI_FUNC(node *) PyParser_ParseStringFlags(const char *, grammar *, int,
perrdetail *, int);
-PyAPI_FUNC(node *) PyParser_ParseFileFlags(FILE *, const char *,
- const char*, grammar *,
- int, char *, char *,
- perrdetail *, int);
+PyAPI_FUNC(node *) PyParser_ParseFileFlags(
+ FILE *fp,
+ const char *filename, /* decoded from the filesystem encoding */
+ const char *enc,
+ grammar *g,
+ int start,
+ const char *ps1,
+ const char *ps2,
+ perrdetail *err_ret,
+ int flags);
PyAPI_FUNC(node *) PyParser_ParseFileFlagsEx(
FILE *fp,
const char *filename, /* decoded from the filesystem encoding */
const char *enc,
grammar *g,
int start,
- char *ps1,
- char *ps2,
+ const char *ps1,
+ const char *ps2,
+ perrdetail *err_ret,
+ int *flags);
+PyAPI_FUNC(node *) PyParser_ParseFileObject(
+ FILE *fp,
+ PyObject *filename,
+ const char *enc,
+ grammar *g,
+ int start,
+ const char *ps1,
+ const char *ps2,
perrdetail *err_ret,
int *flags);
-PyAPI_FUNC(node *) PyParser_ParseStringFlagsFilename(const char *,
- const char *,
- grammar *, int,
- perrdetail *, int);
+PyAPI_FUNC(node *) PyParser_ParseStringFlagsFilename(
+ const char *s,
+ const char *filename, /* decoded from the filesystem encoding */
+ grammar *g,
+ int start,
+ perrdetail *err_ret,
+ int flags);
PyAPI_FUNC(node *) PyParser_ParseStringFlagsFilenameEx(
const char *s,
const char *filename, /* decoded from the filesystem encoding */
@@ -68,6 +88,13 @@ PyAPI_FUNC(node *) PyParser_ParseStringFlagsFilenameEx(
int start,
perrdetail *err_ret,
int *flags);
+PyAPI_FUNC(node *) PyParser_ParseStringObject(
+ const char *s,
+ PyObject *filename,
+ grammar *g,
+ int start,
+ perrdetail *err_ret,
+ int *flags);
/* Note that the following functions are defined in pythonrun.c,
not in parsetok.c */
diff --git a/Include/patchlevel.h b/Include/patchlevel.h
index 61a3ba0..72f540d 100644
--- a/Include/patchlevel.h
+++ b/Include/patchlevel.h
@@ -17,13 +17,13 @@
/* Version parsed out into numeric values */
/*--start constants--*/
#define PY_MAJOR_VERSION 3
-#define PY_MINOR_VERSION 3
-#define PY_MICRO_VERSION 5
+#define PY_MINOR_VERSION 4
+#define PY_MICRO_VERSION 0
#define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_FINAL
#define PY_RELEASE_SERIAL 0
/* Version as a string */
-#define PY_VERSION "3.3.5+"
+#define PY_VERSION "3.4.0+"
/*--end constants--*/
/* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2.
diff --git a/Include/pydebug.h b/Include/pydebug.h
index 97c2f8c..8fe9818 100644
--- a/Include/pydebug.h
+++ b/Include/pydebug.h
@@ -20,6 +20,7 @@ PyAPI_DATA(int) Py_DontWriteBytecodeFlag;
PyAPI_DATA(int) Py_NoUserSiteDirectory;
PyAPI_DATA(int) Py_UnbufferedStdioFlag;
PyAPI_DATA(int) Py_HashRandomizationFlag;
+PyAPI_DATA(int) Py_IsolatedFlag;
/* this is a wrapper around getenv() that pays attention to
Py_IgnoreEnvironmentFlag. It should be used for getting variables like
diff --git a/Include/pyerrors.h b/Include/pyerrors.h
index e385123..e44fb5f 100644
--- a/Include/pyerrors.h
+++ b/Include/pyerrors.h
@@ -53,6 +53,7 @@ typedef struct {
PyObject *myerrno;
PyObject *strerror;
PyObject *filename;
+ PyObject *filename2;
#ifdef MS_WINDOWS
PyObject *winerror;
#endif
@@ -75,6 +76,9 @@ typedef PyOSErrorObject PyWindowsErrorObject;
PyAPI_FUNC(void) PyErr_SetNone(PyObject *);
PyAPI_FUNC(void) PyErr_SetObject(PyObject *, PyObject *);
+#ifndef Py_LIMITED_API
+PyAPI_FUNC(void) _PyErr_SetKeyError(PyObject *);
+#endif
PyAPI_FUNC(void) PyErr_SetString(
PyObject *exception,
const char *string /* decoded from utf-8 */
@@ -198,9 +202,6 @@ PyAPI_DATA(PyObject *) PyExc_IOError;
#ifdef MS_WINDOWS
PyAPI_DATA(PyObject *) PyExc_WindowsError;
#endif
-#ifdef __VMS
-PyAPI_DATA(PyObject *) PyExc_VMSError;
-#endif
PyAPI_DATA(PyObject *) PyExc_RecursionErrorInst;
@@ -225,6 +226,8 @@ PyAPI_FUNC(PyObject *) PyErr_NoMemory(void);
PyAPI_FUNC(PyObject *) PyErr_SetFromErrno(PyObject *);
PyAPI_FUNC(PyObject *) PyErr_SetFromErrnoWithFilenameObject(
PyObject *, PyObject *);
+PyAPI_FUNC(PyObject *) PyErr_SetFromErrnoWithFilenameObjects(
+ PyObject *, PyObject *, PyObject *);
PyAPI_FUNC(PyObject *) PyErr_SetFromErrnoWithFilename(
PyObject *exc,
const char *filename /* decoded from the filesystem encoding */
@@ -253,6 +256,8 @@ PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErrWithUnicodeFilename(
PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErr(int);
PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErrWithFilenameObject(
PyObject *,int, PyObject *);
+PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErrWithFilenameObjects(
+ PyObject *,int, PyObject *, PyObject *);
PyAPI_FUNC(PyObject *) PyErr_SetExcFromWindowsErrWithFilename(
PyObject *exc,
int ierr,
@@ -284,6 +289,28 @@ PyAPI_FUNC(PyObject *) PyErr_NewExceptionWithDoc(
const char *name, const char *doc, PyObject *base, PyObject *dict);
PyAPI_FUNC(void) PyErr_WriteUnraisable(PyObject *);
+/* In exceptions.c */
+#ifndef Py_LIMITED_API
+/* Helper that attempts to replace the current exception with one of the
+ * same type but with a prefix added to the exception text. The resulting
+ * exception description looks like:
+ *
+ * prefix (exc_type: original_exc_str)
+ *
+ * Only some exceptions can be safely replaced. If the function determines
+ * it isn't safe to perform the replacement, it will leave the original
+ * unmodified exception in place.
+ *
+ * Returns a borrowed reference to the new exception (if any), NULL if the
+ * existing exception was left in place.
+ */
+PyAPI_FUNC(PyObject *) _PyErr_TrySetFromCause(
+ const char *prefix_format, /* ASCII-encoded string */
+ ...
+ );
+#endif
+
+
/* In sigcheck.c or signalmodule.c */
PyAPI_FUNC(int) PyErr_CheckSignals(void);
PyAPI_FUNC(void) PyErr_SetInterrupt(void);
@@ -301,9 +328,20 @@ PyAPI_FUNC(void) PyErr_SyntaxLocationEx(
const char *filename, /* decoded from the filesystem encoding */
int lineno,
int col_offset);
+#ifndef Py_LIMITED_API
+PyAPI_FUNC(void) PyErr_SyntaxLocationObject(
+ PyObject *filename,
+ int lineno,
+ int col_offset);
+#endif
PyAPI_FUNC(PyObject *) PyErr_ProgramText(
const char *filename, /* decoded from the filesystem encoding */
int lineno);
+#ifndef Py_LIMITED_API
+PyAPI_FUNC(PyObject *) PyErr_ProgramTextObject(
+ PyObject *filename,
+ int lineno);
+#endif
/* The following functions are used to create and modify unicode
exceptions from C */
diff --git a/Include/pyhash.h b/Include/pyhash.h
new file mode 100644
index 0000000..a7ca937
--- /dev/null
+++ b/Include/pyhash.h
@@ -0,0 +1,149 @@
+#ifndef Py_HASH_H
+
+#define Py_HASH_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Helpers for hash functions */
+#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(const void*, Py_ssize_t);
+#endif
+
+/* Prime multiplier used in string and various other hashes. */
+#define _PyHASH_MULTIPLIER 1000003UL /* 0xf4243 */
+
+/* Parameters used for the numeric hash implementation. See notes for
+ _Py_HashDouble in Objects/object.c. Numeric hashes are based on
+ reduction modulo the prime 2**_PyHASH_BITS - 1. */
+
+#if SIZEOF_VOID_P >= 8
+# define _PyHASH_BITS 61
+#else
+# define _PyHASH_BITS 31
+#endif
+
+#define _PyHASH_MODULUS (((size_t)1 << _PyHASH_BITS) - 1)
+#define _PyHASH_INF 314159
+#define _PyHASH_NAN 0
+#define _PyHASH_IMAG _PyHASH_MULTIPLIER
+
+
+/* hash secret
+ *
+ * memory layout on 64 bit systems
+ * cccccccc cccccccc cccccccc uc -- unsigned char[24]
+ * pppppppp ssssssss ........ fnv -- two Py_hash_t
+ * k0k0k0k0 k1k1k1k1 ........ siphash -- two PY_UINT64_T
+ * ........ ........ ssssssss djbx33a -- 16 bytes padding + one Py_hash_t
+ * ........ ........ eeeeeeee pyexpat XML hash salt
+ *
+ * memory layout on 32 bit systems
+ * cccccccc cccccccc cccccccc uc
+ * ppppssss ........ ........ fnv -- two Py_hash_t
+ * k0k0k0k0 k1k1k1k1 ........ siphash -- two PY_UINT64_T (*)
+ * ........ ........ ssss.... djbx33a -- 16 bytes padding + one Py_hash_t
+ * ........ ........ eeee.... pyexpat XML hash salt
+ *
+ * (*) The siphash member may not be available on 32 bit platforms without
+ * an unsigned int64 data type.
+ */
+#ifndef Py_LIMITED_API
+typedef union {
+ /* ensure 24 bytes */
+ unsigned char uc[24];
+ /* two Py_hash_t for FNV */
+ struct {
+ Py_hash_t prefix;
+ Py_hash_t suffix;
+ } fnv;
+#ifdef PY_UINT64_T
+ /* two uint64 for SipHash24 */
+ struct {
+ PY_UINT64_T k0;
+ PY_UINT64_T k1;
+ } siphash;
+#endif
+ /* a different (!) Py_hash_t for small string optimization */
+ struct {
+ unsigned char padding[16];
+ Py_hash_t suffix;
+ } djbx33a;
+ struct {
+ unsigned char padding[16];
+ Py_hash_t hashsalt;
+ } expat;
+} _Py_HashSecret_t;
+PyAPI_DATA(_Py_HashSecret_t) _Py_HashSecret;
+#endif
+
+#ifdef Py_DEBUG
+PyAPI_DATA(int) _Py_HashSecret_Initialized;
+#endif
+
+
+/* hash function definition */
+#ifndef Py_LIMITED_API
+typedef struct {
+ Py_hash_t (*const hash)(const void *, Py_ssize_t);
+ const char *name;
+ const int hash_bits;
+ const int seed_bits;
+} PyHash_FuncDef;
+
+PyAPI_FUNC(PyHash_FuncDef*) PyHash_GetFuncDef(void);
+#endif
+
+
+/* cutoff for small string DJBX33A optimization in range [1, cutoff).
+ *
+ * About 50% of the strings in a typical Python application are smaller than
+ * 6 to 7 chars. However DJBX33A is vulnerable to hash collision attacks.
+ * NEVER use DJBX33A for long strings!
+ *
+ * A Py_HASH_CUTOFF of 0 disables small string optimization. 32 bit platforms
+ * should use a smaller cutoff because it is easier to create colliding
+ * strings. A cutoff of 7 on 64bit platforms and 5 on 32bit platforms should
+ * provide a decent safety margin.
+ */
+#ifndef Py_HASH_CUTOFF
+# define Py_HASH_CUTOFF 0
+#elif (Py_HASH_CUTOFF > 7 || Py_HASH_CUTOFF < 0)
+# error Py_HASH_CUTOFF must in range 0...7.
+#endif /* Py_HASH_CUTOFF */
+
+
+/* hash algorithm selection
+ *
+ * The values for Py_HASH_SIPHASH24 and Py_HASH_FNV are hard-coded in the
+ * configure script.
+ *
+ * - FNV is available on all platforms and architectures.
+ * - SIPHASH24 only works on plaforms that provide PY_UINT64_T and doesn't
+ * require aligned memory for integers.
+ * - With EXTERNAL embedders can provide an alternative implementation with::
+ *
+ * PyHash_FuncDef PyHash_Func = {...};
+ *
+ * XXX: Figure out __declspec() for extern PyHash_FuncDef.
+ */
+#define Py_HASH_EXTERNAL 0
+#define Py_HASH_SIPHASH24 1
+#define Py_HASH_FNV 2
+
+#ifndef Py_HASH_ALGORITHM
+# if (defined(PY_UINT64_T) && defined(PY_UINT32_T) \
+ && !defined(HAVE_ALIGNED_REQUIRED))
+# define Py_HASH_ALGORITHM Py_HASH_SIPHASH24
+# else
+# define Py_HASH_ALGORITHM Py_HASH_FNV
+# endif /* uint64_t && uint32_t && aligned */
+#endif /* Py_HASH_ALGORITHM */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !Py_HASH_H */
diff --git a/Include/pymacro.h b/Include/pymacro.h
index 793f67d..7997c55 100644
--- a/Include/pymacro.h
+++ b/Include/pymacro.h
@@ -69,4 +69,10 @@
/* Check if pointer "p" is aligned to "a"-bytes boundary. */
#define _Py_IS_ALIGNED(p, a) (!((Py_uintptr_t)(p) & (Py_uintptr_t)((a) - 1)))
+#ifdef __GNUC__
+#define Py_UNUSED(name) _unused_ ## name __attribute__((unused))
+#else
+#define Py_UNUSED(name) _unused_ ## name
+#endif
+
#endif /* Py_PYMACRO_H */
diff --git a/Include/pymem.h b/Include/pymem.h
index 10b5bea..2372b86 100644
--- a/Include/pymem.h
+++ b/Include/pymem.h
@@ -11,6 +11,13 @@
extern "C" {
#endif
+#ifndef Py_LIMITED_API
+PyAPI_FUNC(void *) PyMem_RawMalloc(size_t size);
+PyAPI_FUNC(void *) PyMem_RawRealloc(void *ptr, size_t new_size);
+PyAPI_FUNC(void) PyMem_RawFree(void *ptr);
+#endif
+
+
/* BEWARE:
Each interface exports both functions and macros. Extension modules should
@@ -49,21 +56,16 @@ extern "C" {
performed on failure (no exception is set, no warning is printed, etc).
*/
-PyAPI_FUNC(void *) PyMem_Malloc(size_t);
-PyAPI_FUNC(void *) PyMem_Realloc(void *, size_t);
-PyAPI_FUNC(void) PyMem_Free(void *);
+PyAPI_FUNC(void *) PyMem_Malloc(size_t size);
+PyAPI_FUNC(void *) PyMem_Realloc(void *ptr, size_t new_size);
+PyAPI_FUNC(void) PyMem_Free(void *ptr);
-/* Starting from Python 1.6, the wrappers Py_{Malloc,Realloc,Free} are
- no longer supported. They used to call PyErr_NoMemory() on failure. */
+#ifndef Py_LIMITED_API
+PyAPI_FUNC(char *) _PyMem_RawStrdup(const char *str);
+PyAPI_FUNC(char *) _PyMem_Strdup(const char *str);
+#endif
/* Macros. */
-#ifdef PYMALLOC_DEBUG
-/* Redirect all memory operations to Python's debugging allocator. */
-#define PyMem_MALLOC _PyMem_DebugMalloc
-#define PyMem_REALLOC _PyMem_DebugRealloc
-#define PyMem_FREE _PyMem_DebugFree
-
-#else /* ! PYMALLOC_DEBUG */
/* PyMem_MALLOC(0) means malloc(1). Some systems would return NULL
for malloc(0), which would be treated as an error. Some platforms
@@ -71,13 +73,9 @@ PyAPI_FUNC(void) PyMem_Free(void *);
pymalloc. To solve these problems, allocate an extra byte. */
/* Returns NULL to indicate error if a negative size or size larger than
Py_ssize_t can represent is supplied. Helps prevents security holes. */
-#define PyMem_MALLOC(n) ((size_t)(n) > (size_t)PY_SSIZE_T_MAX ? NULL \
- : malloc((n) ? (n) : 1))
-#define PyMem_REALLOC(p, n) ((size_t)(n) > (size_t)PY_SSIZE_T_MAX ? NULL \
- : realloc((p), (n) ? (n) : 1))
-#define PyMem_FREE free
-
-#endif /* PYMALLOC_DEBUG */
+#define PyMem_MALLOC(n) PyMem_Malloc(n)
+#define PyMem_REALLOC(p, n) PyMem_Realloc(p, n)
+#define PyMem_FREE(p) PyMem_Free(p)
/*
* Type-oriented memory interface
@@ -115,6 +113,69 @@ PyAPI_FUNC(void) PyMem_Free(void *);
#define PyMem_Del PyMem_Free
#define PyMem_DEL PyMem_FREE
+#ifndef Py_LIMITED_API
+typedef enum {
+ /* PyMem_RawMalloc(), PyMem_RawRealloc() and PyMem_RawFree() */
+ PYMEM_DOMAIN_RAW,
+
+ /* PyMem_Malloc(), PyMem_Realloc() and PyMem_Free() */
+ PYMEM_DOMAIN_MEM,
+
+ /* PyObject_Malloc(), PyObject_Realloc() and PyObject_Free() */
+ PYMEM_DOMAIN_OBJ
+} PyMemAllocatorDomain;
+
+typedef struct {
+ /* user context passed as the first argument to the 3 functions */
+ void *ctx;
+
+ /* allocate a memory block */
+ void* (*malloc) (void *ctx, size_t size);
+
+ /* allocate or resize a memory block */
+ void* (*realloc) (void *ctx, void *ptr, size_t new_size);
+
+ /* release a memory block */
+ void (*free) (void *ctx, void *ptr);
+} PyMemAllocator;
+
+/* Get the memory block allocator of the specified domain. */
+PyAPI_FUNC(void) PyMem_GetAllocator(PyMemAllocatorDomain domain,
+ PyMemAllocator *allocator);
+
+/* Set the memory block allocator of the specified domain.
+
+ The new allocator must return a distinct non-NULL pointer when requesting
+ zero bytes.
+
+ For the PYMEM_DOMAIN_RAW domain, the allocator must be thread-safe: the GIL
+ is not held when the allocator is called.
+
+ If the new allocator is not a hook (don't call the previous allocator), the
+ PyMem_SetupDebugHooks() function must be called to reinstall the debug hooks
+ on top on the new allocator. */
+PyAPI_FUNC(void) PyMem_SetAllocator(PyMemAllocatorDomain domain,
+ PyMemAllocator *allocator);
+
+/* Setup hooks to detect bugs in the following Python memory allocator
+ functions:
+
+ - PyMem_RawMalloc(), PyMem_RawRealloc(), PyMem_RawFree()
+ - PyMem_Malloc(), PyMem_Realloc(), PyMem_Free()
+ - PyObject_Malloc(), PyObject_Realloc() and PyObject_Free()
+
+ Newly allocated memory is filled with the byte 0xCB, freed memory is filled
+ with the byte 0xDB. Additionnal checks:
+
+ - detect API violations, ex: PyObject_Free() called on a buffer allocated
+ by PyMem_Malloc()
+ - detect write before the start of the buffer (buffer underflow)
+ - detect write after the end of the buffer (buffer overflow)
+
+ The function does nothing if Python is not compiled is debug mode. */
+PyAPI_FUNC(void) PyMem_SetupDebugHooks(void);
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/Include/pyport.h b/Include/pyport.h
index a5edea9..c706213 100644
--- a/Include/pyport.h
+++ b/Include/pyport.h
@@ -144,23 +144,6 @@ Used in: PY_LONG_LONG
#endif
#endif
-/* Prime multiplier used in string and various other hashes. */
-#define _PyHASH_MULTIPLIER 1000003UL /* 0xf4243 */
-
-/* Parameters used for the numeric hash implementation. See notes for
- _Py_HashDouble in Objects/object.c. Numeric hashes are based on
- reduction modulo the prime 2**_PyHASH_BITS - 1. */
-
-#if SIZEOF_VOID_P >= 8
-#define _PyHASH_BITS 61
-#else
-#define _PyHASH_BITS 31
-#endif
-#define _PyHASH_MODULUS (((size_t)1 << _PyHASH_BITS) - 1)
-#define _PyHASH_INF 314159
-#define _PyHASH_NAN 0
-#define _PyHASH_IMAG _PyHASH_MULTIPLIER
-
/* uintptr_t is the C9X name for an unsigned integral type such that a
* legitimate void* can be cast to uintptr_t and then back to void* again
* without loss of information. Similarly for intptr_t, wrt a signed
@@ -199,10 +182,19 @@ typedef Py_intptr_t Py_ssize_t;
#endif
/* Py_hash_t is the same size as a pointer. */
+#define SIZEOF_PY_HASH_T SIZEOF_SIZE_T
typedef Py_ssize_t Py_hash_t;
/* Py_uhash_t is the unsigned equivalent needed to calculate numeric hash. */
+#define SIZEOF_PY_UHASH_T SIZEOF_SIZE_T
typedef size_t Py_uhash_t;
+/* Only used for compatibility with code that may not be PY_SSIZE_T_CLEAN. */
+#ifdef PY_SSIZE_T_CLEAN
+typedef Py_ssize_t Py_ssize_clean_t;
+#else
+typedef int Py_ssize_clean_t;
+#endif
+
/* Largest possible value of size_t.
SIZE_MAX is part of C99, so it might be defined on some
platforms. If it is not defined, (size_t)-1 is a portable
@@ -219,10 +211,6 @@ typedef size_t Py_uhash_t;
/* Smallest negative value of type Py_ssize_t. */
#define PY_SSIZE_T_MIN (-PY_SSIZE_T_MAX-1)
-#if SIZEOF_PID_T > SIZEOF_LONG
-# error "Python doesn't support sizeof(pid_t) > sizeof(long)"
-#endif
-
/* PY_FORMAT_SIZE_T is a platform-specific modifier for use in a printf
* format to convert an argument with the width of a size_t or Py_ssize_t.
* C99 introduced "z" for this purpose, but not all platforms support that;
@@ -267,7 +255,7 @@ typedef size_t Py_uhash_t;
*/
#ifdef HAVE_LONG_LONG
# ifndef PY_FORMAT_LONG_LONG
-# if defined(MS_WIN64) || defined(MS_WINDOWS)
+# ifdef MS_WINDOWS
# define PY_FORMAT_LONG_LONG "I64"
# else
# error "This platform's pyconfig.h needs to define PY_FORMAT_LONG_LONG"
@@ -392,17 +380,20 @@ typedef size_t Py_uhash_t;
#endif
#ifdef HAVE_SYS_STAT_H
-#if defined(PYOS_OS2) && defined(PYCC_GCC)
-#include <sys/types.h>
-#endif
#include <sys/stat.h>
#elif defined(HAVE_STAT_H)
#include <stat.h>
#endif
-#if defined(PYCC_VACPP)
+#ifndef S_IFMT
/* VisualAge C/C++ Failed to Define MountType Field in sys/stat.h */
-#define S_IFMT (S_IFDIR|S_IFCHR|S_IFREG)
+#define S_IFMT 0170000
+#endif
+
+#ifndef S_IFLNK
+/* Windows doesn't define S_IFLNK but posixmodule.c maps
+ * IO_REPARSE_TAG_SYMLINK to S_IFLNK */
+# define S_IFLNK 0120000
#endif
#ifndef S_ISREG
@@ -413,6 +404,9 @@ typedef size_t Py_uhash_t;
#define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR)
#endif
+#ifndef S_ISCHR
+#define S_ISCHR(x) (((x) & S_IFMT) == S_IFCHR)
+#endif
#ifdef __cplusplus
/* Move this down here since some C++ #include's don't like to be included
@@ -835,15 +829,6 @@ extern pid_t forkpty(int *, char *, struct termios *, struct winsize *);
#endif
/*
- * Add PyArg_ParseTuple format where available.
- */
-#ifdef HAVE_ATTRIBUTE_FORMAT_PARSETUPLE
-#define Py_FORMAT_PARSETUPLE(func,p1,p2) __attribute__((format(func,p1,p2)))
-#else
-#define Py_FORMAT_PARSETUPLE(func,p1,p2)
-#endif
-
-/*
* Specify alignment on compilers that support it.
*/
#if defined(__GNUC__) && __GNUC__ >= 3
@@ -881,4 +866,18 @@ extern pid_t forkpty(int *, char *, struct termios *, struct winsize *);
#endif
#endif
+/*
+ * Convenient macros to deal with endianness of the platform. WORDS_BIGENDIAN is
+ * detected by configure and defined in pyconfig.h. The code in pyconfig.h
+ * also takes care of Apple's universal builds.
+ */
+
+#ifdef WORDS_BIGENDIAN
+#define PY_BIG_ENDIAN 1
+#define PY_LITTLE_ENDIAN 0
+#else
+#define PY_BIG_ENDIAN 0
+#define PY_LITTLE_ENDIAN 1
+#endif
+
#endif /* Py_PYPORT_H */
diff --git a/Include/pystate.h b/Include/pystate.h
index 2017b02..4992c22 100644
--- a/Include/pystate.h
+++ b/Include/pystate.h
@@ -33,7 +33,6 @@ typedef struct _is {
int codecs_initialized;
int fscodec_initialized;
-
#ifdef HAVE_DLOPEN
int dlopenflags;
#endif
@@ -41,6 +40,7 @@ typedef struct _is {
int tscdump;
#endif
+ PyObject *builtins_copy;
} PyInterpreterState;
#endif
@@ -69,6 +69,7 @@ typedef struct _ts PyThreadState;
typedef struct _ts {
/* See Python/ceval.c for comments explaining most fields */
+ struct _ts *prev;
struct _ts *next;
PyInterpreterState *interp;
@@ -99,16 +100,6 @@ typedef struct _ts {
PyObject *dict; /* Stores per-thread state */
- /* XXX doesn't mean anything anymore (the comment below is obsolete)
- => deprecate or remove? */
- /* tick_counter is incremented whenever the check_interval ticker
- * reaches zero. The purpose is to give a useful measure of the number
- * of interpreted bytecode instructions in a given thread. This
- * extremely lightweight statistic collector may be of interest to
- * profilers (like psyco.jit()), although nothing in the core uses it.
- */
- int tick_counter;
-
int gilstate_counter;
PyObject *async_exc; /* Asynchronous exception to raise */
@@ -117,6 +108,32 @@ typedef struct _ts {
int trash_delete_nesting;
PyObject *trash_delete_later;
+ /* Called when a thread state is deleted normally, but not when it
+ * is destroyed after fork().
+ * Pain: to prevent rare but fatal shutdown errors (issue 18808),
+ * Thread.join() must wait for the join'ed thread's tstate to be unlinked
+ * from the tstate chain. That happens at the end of a thread's life,
+ * in pystate.c.
+ * The obvious way doesn't quite work: create a lock which the tstate
+ * unlinking code releases, and have Thread.join() wait to acquire that
+ * lock. The problem is that we _are_ at the end of the thread's life:
+ * if the thread holds the last reference to the lock, decref'ing the
+ * lock will delete the lock, and that may trigger arbitrary Python code
+ * if there's a weakref, with a callback, to the lock. But by this time
+ * _PyThreadState_Current is already NULL, so only the simplest of C code
+ * can be allowed to run (in particular it must not be possible to
+ * release the GIL).
+ * So instead of holding the lock directly, the tstate holds a weakref to
+ * the lock: that's the value of on_delete_data below. Decref'ing a
+ * weakref is harmless.
+ * on_delete points to _threadmodule.c's static release_sentinel() function.
+ * After the tstate is unlinked, release_sentinel is called with the
+ * weakref-to-lock (on_delete_data) argument, and release_sentinel releases
+ * the indirectly held lock.
+ */
+ void (*on_delete)(void *);
+ void *on_delete_data;
+
/* XXX signal handlers should also be here */
} PyThreadState;
@@ -133,12 +150,16 @@ PyAPI_FUNC(int) PyState_AddModule(PyObject*, struct PyModuleDef*);
PyAPI_FUNC(int) PyState_RemoveModule(struct PyModuleDef*);
#endif
PyAPI_FUNC(PyObject*) PyState_FindModule(struct PyModuleDef*);
+#ifndef Py_LIMITED_API
+PyAPI_FUNC(void) _PyState_ClearModules(void);
+#endif
PyAPI_FUNC(PyThreadState *) PyThreadState_New(PyInterpreterState *);
PyAPI_FUNC(PyThreadState *) _PyThreadState_Prealloc(PyInterpreterState *);
PyAPI_FUNC(void) _PyThreadState_Init(PyThreadState *);
PyAPI_FUNC(void) PyThreadState_Clear(PyThreadState *);
PyAPI_FUNC(void) PyThreadState_Delete(PyThreadState *);
+PyAPI_FUNC(void) _PyThreadState_DeleteExcept(PyThreadState *tstate);
#ifdef WITH_THREAD
PyAPI_FUNC(void) PyThreadState_DeleteCurrent(void);
PyAPI_FUNC(void) _PyGILState_Reinit(void);
@@ -212,6 +233,13 @@ PyAPI_FUNC(void) PyGILState_Release(PyGILState_STATE);
*/
PyAPI_FUNC(PyThreadState *) PyGILState_GetThisThreadState(void);
+/* Helper/diagnostic function - return 1 if the current thread
+ * currently holds the GIL, 0 otherwise
+ */
+#ifndef Py_LIMITED_API
+PyAPI_FUNC(int) PyGILState_Check(void);
+#endif
+
#endif /* #ifdef WITH_THREAD */
/* The implementation of sys._current_frames() Returns a dict mapping
diff --git a/Include/pythonrun.h b/Include/pythonrun.h
index e8a582d..2fc5578 100644
--- a/Include/pythonrun.h
+++ b/Include/pythonrun.h
@@ -28,6 +28,14 @@ PyAPI_FUNC(wchar_t *) Py_GetProgramName(void);
PyAPI_FUNC(void) Py_SetPythonHome(wchar_t *);
PyAPI_FUNC(wchar_t *) Py_GetPythonHome(void);
+#ifndef Py_LIMITED_API
+/* Only used by applications that embed the interpreter and need to
+ * override the standard encoding determination mechanism
+ */
+PyAPI_FUNC(int) Py_SetStandardStreamEncoding(const char *encoding,
+ const char *errors);
+#endif
+
PyAPI_FUNC(void) Py_Initialize(void);
PyAPI_FUNC(void) Py_InitializeEx(int);
#ifndef Py_LIMITED_API
@@ -55,6 +63,10 @@ PyAPI_FUNC(int) PyRun_InteractiveOneFlags(
FILE *fp,
const char *filename, /* decoded from the filesystem encoding */
PyCompilerFlags *flags);
+PyAPI_FUNC(int) PyRun_InteractiveOneObject(
+ FILE *fp,
+ PyObject *filename,
+ PyCompilerFlags *flags);
PyAPI_FUNC(int) PyRun_InteractiveLoopFlags(
FILE *fp,
const char *filename, /* decoded from the filesystem encoding */
@@ -66,6 +78,12 @@ PyAPI_FUNC(struct _mod *) PyParser_ASTFromString(
int start,
PyCompilerFlags *flags,
PyArena *arena);
+PyAPI_FUNC(struct _mod *) PyParser_ASTFromStringObject(
+ const char *s,
+ PyObject *filename,
+ int start,
+ PyCompilerFlags *flags,
+ PyArena *arena);
PyAPI_FUNC(struct _mod *) PyParser_ASTFromFile(
FILE *fp,
const char *filename, /* decoded from the filesystem encoding */
@@ -76,6 +94,16 @@ PyAPI_FUNC(struct _mod *) PyParser_ASTFromFile(
PyCompilerFlags *flags,
int *errcode,
PyArena *arena);
+PyAPI_FUNC(struct _mod *) PyParser_ASTFromFileObject(
+ FILE *fp,
+ PyObject *filename,
+ const char* enc,
+ int start,
+ char *ps1,
+ char *ps2,
+ PyCompilerFlags *flags,
+ int *errcode,
+ PyArena *arena);
#endif
#ifndef PyParser_SimpleParseString
@@ -117,11 +145,22 @@ PyAPI_FUNC(PyObject *) Py_CompileStringExFlags(
int start,
PyCompilerFlags *flags,
int optimize);
+PyAPI_FUNC(PyObject *) Py_CompileStringObject(
+ const char *str,
+ PyObject *filename, int start,
+ PyCompilerFlags *flags,
+ int optimize);
#endif
PyAPI_FUNC(struct symtable *) Py_SymtableString(
const char *str,
const char *filename, /* decoded from the filesystem encoding */
int start);
+#ifndef Py_LIMITED_API
+PyAPI_FUNC(struct symtable *) Py_SymtableStringObject(
+ const char *str,
+ PyObject *filename,
+ int start);
+#endif
PyAPI_FUNC(void) PyErr_Print(void);
PyAPI_FUNC(void) PyErr_PrintEx(int);
@@ -197,7 +236,7 @@ PyAPI_FUNC(void) _PyImport_Init(void);
PyAPI_FUNC(void) _PyExc_Init(PyObject * bltinmod);
PyAPI_FUNC(void) _PyImportHooks_Init(void);
PyAPI_FUNC(int) _PyFrame_Init(void);
-PyAPI_FUNC(void) _PyFloat_Init(void);
+PyAPI_FUNC(int) _PyFloat_Init(void);
PyAPI_FUNC(int) PyByteArray_Init(void);
PyAPI_FUNC(void) _PyRandom_Init(void);
#endif
@@ -217,19 +256,21 @@ PyAPI_FUNC(void) PyBytes_Fini(void);
PyAPI_FUNC(void) PyByteArray_Fini(void);
PyAPI_FUNC(void) PyFloat_Fini(void);
PyAPI_FUNC(void) PyOS_FiniInterrupts(void);
+PyAPI_FUNC(void) _PyGC_DumpShutdownStats(void);
PyAPI_FUNC(void) _PyGC_Fini(void);
PyAPI_FUNC(void) PySlice_Fini(void);
PyAPI_FUNC(void) _PyType_Fini(void);
+PyAPI_FUNC(void) _PyRandom_Fini(void);
PyAPI_DATA(PyThreadState *) _Py_Finalizing;
#endif
/* Stuff with no proper home (yet) */
#ifndef Py_LIMITED_API
-PyAPI_FUNC(char *) PyOS_Readline(FILE *, FILE *, char *);
+PyAPI_FUNC(char *) PyOS_Readline(FILE *, FILE *, const char *);
#endif
PyAPI_DATA(int) (*PyOS_InputHook)(void);
-PyAPI_DATA(char) *(*PyOS_ReadlineFunctionPointer)(FILE *, FILE *, char *);
+PyAPI_DATA(char) *(*PyOS_ReadlineFunctionPointer)(FILE *, FILE *, const char *);
#ifndef Py_LIMITED_API
PyAPI_DATA(PyThreadState*) _PyOS_ReadlineTState;
#endif
diff --git a/Include/pytime.h b/Include/pytime.h
index 52902f5..b0fc6d0 100644
--- a/Include/pytime.h
+++ b/Include/pytime.h
@@ -53,10 +53,19 @@ do { \
(tv_end.tv_usec - tv_start.tv_usec) * 0.000001)
#ifndef Py_LIMITED_API
+
+typedef enum {
+ /* Round towards zero. */
+ _PyTime_ROUND_DOWN=0,
+ /* Round away from zero. */
+ _PyTime_ROUND_UP
+} _PyTime_round_t;
+
/* Convert a number of seconds, int or float, to time_t. */
PyAPI_FUNC(int) _PyTime_ObjectToTime_t(
PyObject *obj,
- time_t *sec);
+ time_t *sec,
+ _PyTime_round_t);
/* Convert a time_t to a PyLong. */
PyAPI_FUNC(PyObject *) _PyLong_FromTime_t(
@@ -72,7 +81,8 @@ PyAPI_FUNC(time_t) _PyLong_AsTime_t(
PyAPI_FUNC(int) _PyTime_ObjectToTimeval(
PyObject *obj,
time_t *sec,
- long *usec);
+ long *usec,
+ _PyTime_round_t);
/* Convert a number of seconds, int or float, to a timespec structure.
nsec is in the range [0; 999999999] and rounded towards zero.
@@ -80,7 +90,8 @@ PyAPI_FUNC(int) _PyTime_ObjectToTimeval(
PyAPI_FUNC(int) _PyTime_ObjectToTimespec(
PyObject *obj,
time_t *sec,
- long *nsec);
+ long *nsec,
+ _PyTime_round_t);
#endif
/* Dummy to force linking. */
diff --git a/Include/setobject.h b/Include/setobject.h
index a14874b..ae3f556 100644
--- a/Include/setobject.h
+++ b/Include/setobject.h
@@ -23,8 +23,8 @@ no meaning otherwise.
typedef struct {
/* Cached hash code of the key. */
- Py_hash_t hash;
PyObject *key;
+ Py_hash_t hash;
} setentry;
@@ -51,9 +51,9 @@ struct _setobject {
*/
setentry *table;
setentry *(*lookup)(PySetObject *so, PyObject *key, Py_hash_t hash);
+ Py_hash_t hash; /* only used by frozenset objects */
setentry smalltable[PySet_MINSIZE];
- Py_hash_t hash; /* only used by frozenset objects */
PyObject *weakreflist; /* List of weak references */
};
#endif /* Py_LIMITED_API */
@@ -61,6 +61,10 @@ struct _setobject {
PyAPI_DATA(PyTypeObject) PySet_Type;
PyAPI_DATA(PyTypeObject) PyFrozenSet_Type;
PyAPI_DATA(PyTypeObject) PySetIter_Type;
+#ifndef Py_LIMITED_API
+PyAPI_DATA(PyObject *) _PySet_Dummy;
+#endif
+
/* Invariants for frozensets:
* data is immutable.
@@ -101,7 +105,6 @@ PyAPI_FUNC(PyObject *) PySet_Pop(PyObject *set);
PyAPI_FUNC(int) _PySet_Update(PyObject *set, PyObject *iterable);
PyAPI_FUNC(int) PySet_ClearFreeList(void);
-PyAPI_FUNC(void) _PySet_DebugMallocStats(FILE *out);
#endif
#ifdef __cplusplus
diff --git a/Include/sliceobject.h b/Include/sliceobject.h
index 8bec179..f7ee90c 100644
--- a/Include/sliceobject.h
+++ b/Include/sliceobject.h
@@ -34,6 +34,9 @@ PyAPI_FUNC(PyObject *) PySlice_New(PyObject* start, PyObject* stop,
PyObject* step);
#ifndef Py_LIMITED_API
PyAPI_FUNC(PyObject *) _PySlice_FromIndices(Py_ssize_t start, Py_ssize_t stop);
+PyAPI_FUNC(int) _PySlice_GetLongIndices(PySliceObject *self, PyObject *length,
+ PyObject **start_ptr, PyObject **stop_ptr,
+ PyObject **step_ptr);
#endif
PyAPI_FUNC(int) PySlice_GetIndices(PyObject *r, Py_ssize_t length,
Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t *step);
diff --git a/Include/structseq.h b/Include/structseq.h
index 30c52ac..af22716 100644
--- a/Include/structseq.h
+++ b/Include/structseq.h
@@ -24,6 +24,8 @@ extern char* PyStructSequence_UnnamedField;
#ifndef Py_LIMITED_API
PyAPI_FUNC(void) PyStructSequence_InitType(PyTypeObject *type,
PyStructSequence_Desc *desc);
+PyAPI_FUNC(int) PyStructSequence_InitType2(PyTypeObject *type,
+ PyStructSequence_Desc *desc);
#endif
PyAPI_FUNC(PyTypeObject*) PyStructSequence_NewType(PyStructSequence_Desc *desc);
diff --git a/Include/symtable.h b/Include/symtable.h
index 6ed3a2b..1cfd884 100644
--- a/Include/symtable.h
+++ b/Include/symtable.h
@@ -16,7 +16,7 @@ typedef enum _block_type { FunctionBlock, ClassBlock, ModuleBlock }
struct _symtable_entry;
struct symtable {
- const char *st_filename; /* name of file being compiled,
+ PyObject *st_filename; /* name of file being compiled,
decoded from the filesystem encoding */
struct _symtable_entry *st_cur; /* current symbol table entry */
struct _symtable_entry *st_top; /* symbol table entry for module */
@@ -41,6 +41,7 @@ typedef struct _symtable_entry {
PyObject *ste_name; /* string: name of current block */
PyObject *ste_varnames; /* list of function parameters */
PyObject *ste_children; /* list of child blocks */
+ PyObject *ste_directives;/* locations of global and nonlocal statements */
_Py_block_ty ste_type; /* module, class, or function */
int ste_unoptimized; /* false if namespace is optimized */
int ste_nested; /* true if block is nested */
@@ -52,6 +53,9 @@ typedef struct _symtable_entry {
unsigned ste_varkeywords : 1; /* true if block has varkeywords */
unsigned ste_returns_value : 1; /* true if namespace uses return with
an argument */
+ unsigned ste_needs_class_closure : 1; /* for class scopes, true if a
+ closure over __class__
+ should be created */
int ste_lineno; /* first line of block */
int ste_col_offset; /* offset of first line of block */
int ste_opt_lineno; /* lineno of last exec or import * */
@@ -70,6 +74,10 @@ PyAPI_FUNC(struct symtable *) PySymtable_Build(
mod_ty mod,
const char *filename, /* decoded from the filesystem encoding */
PyFutureFeatures *future);
+PyAPI_FUNC(struct symtable *) PySymtable_BuildObject(
+ mod_ty mod,
+ PyObject *filename,
+ PyFutureFeatures *future);
PyAPI_FUNC(PySTEntryObject *) PySymtable_Lookup(struct symtable *, void *);
PyAPI_FUNC(void) PySymtable_Free(struct symtable *);
diff --git a/Include/sysmodule.h b/Include/sysmodule.h
index 0cabf6f..79e52a3 100644
--- a/Include/sysmodule.h
+++ b/Include/sysmodule.h
@@ -8,7 +8,12 @@ extern "C" {
#endif
PyAPI_FUNC(PyObject *) PySys_GetObject(const char *);
+#ifndef Py_LIMITED_API
+PyAPI_FUNC(PyObject *) _PySys_GetObjectId(_Py_Identifier *key);
+#endif
PyAPI_FUNC(int) PySys_SetObject(const char *, PyObject *);
+PyAPI_FUNC(int) _PySys_SetObjectId(_Py_Identifier *key, PyObject *);
+
PyAPI_FUNC(void) PySys_SetArgv(int, wchar_t **);
PyAPI_FUNC(void) PySys_SetArgvEx(int, wchar_t **, int);
PyAPI_FUNC(void) PySys_SetPath(const wchar_t *);
diff --git a/Include/token.h b/Include/token.h
index f7f6504..905022b 100644
--- a/Include/token.h
+++ b/Include/token.h
@@ -75,7 +75,7 @@ extern "C" {
#define ISEOF(x) ((x) == ENDMARKER)
-PyAPI_DATA(char *) _PyParser_TokenNames[]; /* Token names */
+PyAPI_DATA(const char *) _PyParser_TokenNames[]; /* Token names */
PyAPI_FUNC(int) PyToken_OneChar(int);
PyAPI_FUNC(int) PyToken_TwoChars(int, int);
PyAPI_FUNC(int) PyToken_ThreeChars(int, int, int);
diff --git a/Include/unicodeobject.h b/Include/unicodeobject.h
index a8f5b5d..faa53d6 100644
--- a/Include/unicodeobject.h
+++ b/Include/unicodeobject.h
@@ -180,9 +180,9 @@ typedef unsigned char Py_UCS1;
} 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)
+#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) | \
@@ -343,6 +343,9 @@ typedef struct {
the data pointer is filled out. The bit is redundant, and helps
to minimize the test in PyUnicode_IS_READY(). */
unsigned int ready:1;
+ /* Padding to ensure that PyUnicode_DATA() is always aligned to
+ 4 bytes (see issue #19537 on m68k). */
+ unsigned int :24;
} state;
wchar_t *wstr; /* wchar_t representation (null-terminated) */
} PyASCIIObject;
@@ -859,7 +862,7 @@ PyAPI_FUNC(int) PyUnicode_Resize(
*/
PyAPI_FUNC(PyObject*) PyUnicode_FromEncodedObject(
- register PyObject *obj, /* Object */
+ PyObject *obj, /* Object */
const char *encoding, /* encoding */
const char *errors /* error handling */
);
@@ -878,7 +881,7 @@ PyAPI_FUNC(PyObject*) PyUnicode_FromEncodedObject(
*/
PyAPI_FUNC(PyObject*) PyUnicode_FromObject(
- register PyObject *obj /* Object */
+ PyObject *obj /* Object */
);
PyAPI_FUNC(PyObject *) PyUnicode_FromFormatV(
@@ -898,22 +901,28 @@ typedef struct {
Py_UCS4 maxchar;
Py_ssize_t size;
Py_ssize_t pos;
- /* minimum length of the buffer when overallocation is enabled,
- see _PyUnicodeWriter_Init() */
+
+ /* minimum number of allocated characters (default: 0) */
Py_ssize_t min_length;
+
+ /* minimum character (default: 127, ASCII) */
+ Py_UCS4 min_char;
+
+ /* If non-zero, overallocate the buffer by 25% (default: 0). */
unsigned char overallocate;
+
/* If readonly is 1, buffer is a shared string (cannot be modified)
and size is set to 0. */
unsigned char readonly;
} _PyUnicodeWriter ;
/* Initialize a Unicode writer.
-
- If min_length is greater than zero, _PyUnicodeWriter_Prepare()
- overallocates the buffer and min_length is the minimum length in characters
- of the buffer. */
+ *
+ * By default, the minimum buffer size is 0 character and overallocation is
+ * disabled. Set min_length, min_char and overallocate attributes to control
+ * the allocation of the buffer. */
PyAPI_FUNC(void)
-_PyUnicodeWriter_Init(_PyUnicodeWriter *writer, Py_ssize_t min_length);
+_PyUnicodeWriter_Init(_PyUnicodeWriter *writer);
/* Prepare the buffer to write 'length' characters
with the specified maximum character.
@@ -933,12 +942,52 @@ PyAPI_FUNC(int)
_PyUnicodeWriter_PrepareInternal(_PyUnicodeWriter *writer,
Py_ssize_t length, Py_UCS4 maxchar);
+/* Append a Unicode character.
+ Return 0 on success, raise an exception and return -1 on error. */
+PyAPI_FUNC(int)
+_PyUnicodeWriter_WriteChar(_PyUnicodeWriter *writer,
+ Py_UCS4 ch
+ );
+
+/* Append a Unicode string.
+ Return 0 on success, raise an exception and return -1 on error. */
PyAPI_FUNC(int)
-_PyUnicodeWriter_WriteStr(_PyUnicodeWriter *writer, PyObject *str);
+_PyUnicodeWriter_WriteStr(_PyUnicodeWriter *writer,
+ PyObject *str /* Unicode string */
+ );
+/* Append a substring of a Unicode string.
+ Return 0 on success, raise an exception and return -1 on error. */
+PyAPI_FUNC(int)
+_PyUnicodeWriter_WriteSubstring(_PyUnicodeWriter *writer,
+ PyObject *str, /* Unicode string */
+ Py_ssize_t start,
+ Py_ssize_t end
+ );
+
+/* Append a ASCII-encoded byte string.
+ Return 0 on success, raise an exception and return -1 on error. */
+PyAPI_FUNC(int)
+_PyUnicodeWriter_WriteASCIIString(_PyUnicodeWriter *writer,
+ const char *str, /* ASCII-encoded byte string */
+ Py_ssize_t len /* number of bytes, or -1 if unknown */
+ );
+
+/* Append a latin1-encoded byte string.
+ Return 0 on success, raise an exception and return -1 on error. */
+PyAPI_FUNC(int)
+_PyUnicodeWriter_WriteLatin1String(_PyUnicodeWriter *writer,
+ const char *str, /* latin1-encoded byte string */
+ Py_ssize_t len /* length in bytes */
+ );
+
+/* Get the value of the writer as an Unicode string. Clear the
+ buffer of the writer. Raise an exception and return NULL
+ on error. */
PyAPI_FUNC(PyObject *)
_PyUnicodeWriter_Finish(_PyUnicodeWriter *writer);
+/* Deallocate memory of a writer (clear its internal buffer). */
PyAPI_FUNC(void)
_PyUnicodeWriter_Dealloc(_PyUnicodeWriter *writer);
#endif
@@ -977,7 +1026,7 @@ PyAPI_FUNC(void) _Py_ReleaseInternedUnicodeStrings(void);
The buffer is copied into the new object. */
PyAPI_FUNC(PyObject*) PyUnicode_FromWideChar(
- register const wchar_t *w, /* wchar_t buffer */
+ const wchar_t *w, /* wchar_t buffer */
Py_ssize_t size /* size of buffer */
);
@@ -995,7 +1044,7 @@ PyAPI_FUNC(PyObject*) PyUnicode_FromWideChar(
PyAPI_FUNC(Py_ssize_t) PyUnicode_AsWideChar(
PyObject *unicode, /* Unicode object */
- register wchar_t *w, /* wchar_t buffer */
+ wchar_t *w, /* wchar_t buffer */
Py_ssize_t size /* size of buffer */
);
@@ -1726,7 +1775,7 @@ PyAPI_FUNC(PyObject*) PyUnicode_DecodeLocale(
/* 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.. */
+ cannot contain embedded null characters. */
PyAPI_FUNC(PyObject*) PyUnicode_EncodeLocale(
PyObject *unicode,
@@ -1950,13 +1999,21 @@ PyAPI_FUNC(PyObject *) PyUnicode_Replace(
);
/* Compare two strings and return -1, 0, 1 for less than, equal,
- greater than resp. */
+ greater than resp.
+ Raise an exception and return -1 on error. */
PyAPI_FUNC(int) PyUnicode_Compare(
PyObject *left, /* Left string */
PyObject *right /* Right string */
);
+#ifndef Py_LIMITED_API
+PyAPI_FUNC(int) _PyUnicode_CompareWithId(
+ PyObject *left, /* Left string */
+ _Py_Identifier *right /* Right identifier */
+ );
+#endif
+
PyAPI_FUNC(int) PyUnicode_CompareWithASCIIString(
PyObject *left,
const char *right /* ASCII-encoded string */
diff --git a/Include/warnings.h b/Include/warnings.h
index b7db681..effb9fad 100644
--- a/Include/warnings.h
+++ b/Include/warnings.h
@@ -17,6 +17,15 @@ PyAPI_FUNC(int) PyErr_WarnFormat(
Py_ssize_t stack_level,
const char *format, /* ASCII-encoded string */
...);
+#ifndef Py_LIMITED_API
+PyAPI_FUNC(int) PyErr_WarnExplicitObject(
+ PyObject *category,
+ PyObject *message,
+ PyObject *filename,
+ int lineno,
+ PyObject *module,
+ PyObject *registry);
+#endif
PyAPI_FUNC(int) PyErr_WarnExplicit(
PyObject *category,
const char *message, /* UTF-8 encoded string */
@@ -25,6 +34,14 @@ PyAPI_FUNC(int) PyErr_WarnExplicit(
const char *module, /* UTF-8 encoded string */
PyObject *registry);
+#ifndef Py_LIMITED_API
+PyAPI_FUNC(int)
+PyErr_WarnExplicitFormat(PyObject *category,
+ const char *filename, int lineno,
+ const char *module, PyObject *registry,
+ const char *format, ...);
+#endif
+
/* DEPRECATED: Use PyErr_WarnEx() instead. */
#ifndef Py_LIMITED_API
#define PyErr_Warn(category, msg) PyErr_WarnEx(category, msg, 1)
diff --git a/LICENSE b/LICENSE
index f9ca2c9..583f9f6 100644
--- a/LICENSE
+++ b/LICENSE
@@ -74,8 +74,8 @@ analyze, test, perform and/or display publicly, prepare derivative works,
distribute, and otherwise use Python alone or in any derivative version,
provided, however, that PSF's License Agreement and PSF's notice of copyright,
i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
-2011, 2012, 2013, 2014 Python Software Foundation; All Rights Reserved" are retained
-in Python alone or in any derivative version prepared by Licensee.
+2011, 2012, 2013, 2014 Python Software Foundation; All Rights Reserved" are
+retained in Python alone or in any derivative version prepared by Licensee.
3. In the event Licensee prepares a derivative work that is based on
or incorporates Python or any part thereof, and wants to make
diff --git a/Lib/_bootlocale.py b/Lib/_bootlocale.py
new file mode 100644
index 0000000..4bccac1
--- /dev/null
+++ b/Lib/_bootlocale.py
@@ -0,0 +1,34 @@
+"""A minimal subset of the locale module used at interpreter startup
+(imported by the _io module), in order to reduce startup time.
+
+Don't import directly from third-party code; use the `locale` module instead!
+"""
+
+import sys
+import _locale
+
+if sys.platform.startswith("win"):
+ def getpreferredencoding(do_setlocale=True):
+ return _locale._getdefaultlocale()[1]
+else:
+ try:
+ _locale.CODESET
+ except AttributeError:
+ def getpreferredencoding(do_setlocale=True):
+ # This path for legacy systems needs the more complex
+ # getdefaultlocale() function, import the full locale module.
+ import locale
+ return locale.getpreferredencoding(do_setlocale)
+ else:
+ def getpreferredencoding(do_setlocale=True):
+ assert not do_setlocale
+ result = _locale.nl_langinfo(_locale.CODESET)
+ if not result and sys.platform == 'darwin':
+ # nl_langinfo can return an empty string
+ # when the setting has an invalid value.
+ # Default to UTF-8 in that case because
+ # UTF-8 is the default charset on OSX and
+ # returning nothing will crash the
+ # interpreter.
+ result = 'UTF-8'
+ return result
diff --git a/Lib/_collections_abc.py b/Lib/_collections_abc.py
new file mode 100644
index 0000000..faa1ff2
--- /dev/null
+++ b/Lib/_collections_abc.py
@@ -0,0 +1,734 @@
+# Copyright 2007 Google, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+"""Abstract Base Classes (ABCs) for collections, according to PEP 3119.
+
+Unit tests are in test_collections.
+"""
+
+from abc import ABCMeta, abstractmethod
+import sys
+
+__all__ = ["Hashable", "Iterable", "Iterator",
+ "Sized", "Container", "Callable",
+ "Set", "MutableSet",
+ "Mapping", "MutableMapping",
+ "MappingView", "KeysView", "ItemsView", "ValuesView",
+ "Sequence", "MutableSequence",
+ "ByteString",
+ ]
+
+# This module has been renamed from collections.abc to _collections_abc to
+# speed up interpreter startup. Some of the types such as MutableMapping are
+# required early but collections module imports a lot of other modules.
+# See issue #19218
+__name__ = "collections.abc"
+
+# Private list of types that we want to register with the various ABCs
+# so that they will pass tests like:
+# it = iter(somebytearray)
+# assert isinstance(it, Iterable)
+# Note: in other implementations, these types many not be distinct
+# and they make have their own implementation specific types that
+# are not included on this list.
+bytes_iterator = type(iter(b''))
+bytearray_iterator = type(iter(bytearray()))
+#callable_iterator = ???
+dict_keyiterator = type(iter({}.keys()))
+dict_valueiterator = type(iter({}.values()))
+dict_itemiterator = type(iter({}.items()))
+list_iterator = type(iter([]))
+list_reverseiterator = type(iter(reversed([])))
+range_iterator = type(iter(range(0)))
+set_iterator = type(iter(set()))
+str_iterator = type(iter(""))
+tuple_iterator = type(iter(()))
+zip_iterator = type(iter(zip()))
+## views ##
+dict_keys = type({}.keys())
+dict_values = type({}.values())
+dict_items = type({}.items())
+## misc ##
+mappingproxy = type(type.__dict__)
+
+
+### ONE-TRICK PONIES ###
+
+class Hashable(metaclass=ABCMeta):
+
+ __slots__ = ()
+
+ @abstractmethod
+ def __hash__(self):
+ return 0
+
+ @classmethod
+ def __subclasshook__(cls, C):
+ if cls is Hashable:
+ for B in C.__mro__:
+ if "__hash__" in B.__dict__:
+ if B.__dict__["__hash__"]:
+ return True
+ break
+ return NotImplemented
+
+
+class Iterable(metaclass=ABCMeta):
+
+ __slots__ = ()
+
+ @abstractmethod
+ def __iter__(self):
+ while False:
+ yield None
+
+ @classmethod
+ def __subclasshook__(cls, C):
+ if cls is Iterable:
+ if any("__iter__" in B.__dict__ for B in C.__mro__):
+ return True
+ return NotImplemented
+
+
+class Iterator(Iterable):
+
+ __slots__ = ()
+
+ @abstractmethod
+ def __next__(self):
+ 'Return the next item from the iterator. When exhausted, raise StopIteration'
+ raise StopIteration
+
+ def __iter__(self):
+ return self
+
+ @classmethod
+ def __subclasshook__(cls, C):
+ if cls is Iterator:
+ if (any("__next__" in B.__dict__ for B in C.__mro__) and
+ any("__iter__" in B.__dict__ for B in C.__mro__)):
+ return True
+ return NotImplemented
+
+Iterator.register(bytes_iterator)
+Iterator.register(bytearray_iterator)
+#Iterator.register(callable_iterator)
+Iterator.register(dict_keyiterator)
+Iterator.register(dict_valueiterator)
+Iterator.register(dict_itemiterator)
+Iterator.register(list_iterator)
+Iterator.register(list_reverseiterator)
+Iterator.register(range_iterator)
+Iterator.register(set_iterator)
+Iterator.register(str_iterator)
+Iterator.register(tuple_iterator)
+Iterator.register(zip_iterator)
+
+class Sized(metaclass=ABCMeta):
+
+ __slots__ = ()
+
+ @abstractmethod
+ def __len__(self):
+ return 0
+
+ @classmethod
+ def __subclasshook__(cls, C):
+ if cls is Sized:
+ if any("__len__" in B.__dict__ for B in C.__mro__):
+ return True
+ return NotImplemented
+
+
+class Container(metaclass=ABCMeta):
+
+ __slots__ = ()
+
+ @abstractmethod
+ def __contains__(self, x):
+ return False
+
+ @classmethod
+ def __subclasshook__(cls, C):
+ if cls is Container:
+ if any("__contains__" in B.__dict__ for B in C.__mro__):
+ return True
+ return NotImplemented
+
+
+class Callable(metaclass=ABCMeta):
+
+ __slots__ = ()
+
+ @abstractmethod
+ def __call__(self, *args, **kwds):
+ return False
+
+ @classmethod
+ def __subclasshook__(cls, C):
+ if cls is Callable:
+ if any("__call__" in B.__dict__ for B in C.__mro__):
+ return True
+ return NotImplemented
+
+
+### SETS ###
+
+
+class Set(Sized, Iterable, Container):
+
+ """A set is a finite, iterable container.
+
+ This class provides concrete generic implementations of all
+ methods except for __contains__, __iter__ and __len__.
+
+ To override the comparisons (presumably for speed, as the
+ semantics are fixed), all you have to do is redefine __le__ and
+ then the other operations will automatically follow suit.
+ """
+
+ __slots__ = ()
+
+ def __le__(self, other):
+ if not isinstance(other, Set):
+ return NotImplemented
+ if len(self) > len(other):
+ return False
+ for elem in self:
+ if elem not in other:
+ return False
+ return True
+
+ def __lt__(self, other):
+ if not isinstance(other, Set):
+ return NotImplemented
+ return len(self) < len(other) and self.__le__(other)
+
+ def __gt__(self, other):
+ if not isinstance(other, Set):
+ return NotImplemented
+ return other.__lt__(self)
+
+ def __ge__(self, other):
+ if not isinstance(other, Set):
+ return NotImplemented
+ return other.__le__(self)
+
+ def __eq__(self, other):
+ if not isinstance(other, Set):
+ return NotImplemented
+ return len(self) == len(other) and self.__le__(other)
+
+ def __ne__(self, other):
+ return not (self == other)
+
+ @classmethod
+ def _from_iterable(cls, it):
+ '''Construct an instance of the class from any iterable input.
+
+ Must override this method if the class constructor signature
+ does not accept an iterable for an input.
+ '''
+ return cls(it)
+
+ def __and__(self, other):
+ if not isinstance(other, Iterable):
+ return NotImplemented
+ return self._from_iterable(value for value in other if value in self)
+
+ def isdisjoint(self, other):
+ 'Return True if two sets have a null intersection.'
+ for value in other:
+ if value in self:
+ return False
+ return True
+
+ def __or__(self, other):
+ if not isinstance(other, Iterable):
+ return NotImplemented
+ chain = (e for s in (self, other) for e in s)
+ return self._from_iterable(chain)
+
+ def __sub__(self, other):
+ if not isinstance(other, Set):
+ if not isinstance(other, Iterable):
+ return NotImplemented
+ other = self._from_iterable(other)
+ return self._from_iterable(value for value in self
+ if value not in other)
+
+ def __xor__(self, other):
+ if not isinstance(other, Set):
+ if not isinstance(other, Iterable):
+ return NotImplemented
+ other = self._from_iterable(other)
+ return (self - other) | (other - self)
+
+ def _hash(self):
+ """Compute the hash value of a set.
+
+ Note that we don't define __hash__: not all sets are hashable.
+ But if you define a hashable set type, its __hash__ should
+ call this function.
+
+ This must be compatible __eq__.
+
+ All sets ought to compare equal if they contain the same
+ elements, regardless of how they are implemented, and
+ regardless of the order of the elements; so there's not much
+ freedom for __eq__ or __hash__. We match the algorithm used
+ by the built-in frozenset type.
+ """
+ MAX = sys.maxsize
+ MASK = 2 * MAX + 1
+ n = len(self)
+ h = 1927868237 * (n + 1)
+ h &= MASK
+ for x in self:
+ hx = hash(x)
+ h ^= (hx ^ (hx << 16) ^ 89869747) * 3644798167
+ h &= MASK
+ h = h * 69069 + 907133923
+ h &= MASK
+ if h > MAX:
+ h -= MASK + 1
+ if h == -1:
+ h = 590923713
+ return h
+
+Set.register(frozenset)
+
+
+class MutableSet(Set):
+ """A mutable set is a finite, iterable container.
+
+ This class provides concrete generic implementations of all
+ methods except for __contains__, __iter__, __len__,
+ add(), and discard().
+
+ To override the comparisons (presumably for speed, as the
+ semantics are fixed), all you have to do is redefine __le__ and
+ then the other operations will automatically follow suit.
+ """
+
+ __slots__ = ()
+
+ @abstractmethod
+ def add(self, value):
+ """Add an element."""
+ raise NotImplementedError
+
+ @abstractmethod
+ def discard(self, value):
+ """Remove an element. Do not raise an exception if absent."""
+ raise NotImplementedError
+
+ def remove(self, value):
+ """Remove an element. If not a member, raise a KeyError."""
+ if value not in self:
+ raise KeyError(value)
+ self.discard(value)
+
+ def pop(self):
+ """Return the popped value. Raise KeyError if empty."""
+ it = iter(self)
+ try:
+ value = next(it)
+ except StopIteration:
+ raise KeyError
+ self.discard(value)
+ return value
+
+ def clear(self):
+ """This is slow (creates N new iterators!) but effective."""
+ try:
+ while True:
+ self.pop()
+ except KeyError:
+ pass
+
+ def __ior__(self, it):
+ for value in it:
+ self.add(value)
+ return self
+
+ def __iand__(self, it):
+ for value in (self - it):
+ self.discard(value)
+ return self
+
+ def __ixor__(self, it):
+ if it is self:
+ self.clear()
+ else:
+ if not isinstance(it, Set):
+ it = self._from_iterable(it)
+ for value in it:
+ if value in self:
+ self.discard(value)
+ else:
+ self.add(value)
+ return self
+
+ def __isub__(self, it):
+ if it is self:
+ self.clear()
+ else:
+ for value in it:
+ self.discard(value)
+ return self
+
+MutableSet.register(set)
+
+
+### MAPPINGS ###
+
+
+class Mapping(Sized, Iterable, Container):
+
+ __slots__ = ()
+
+ """A Mapping is a generic container for associating key/value
+ pairs.
+
+ This class provides concrete generic implementations of all
+ methods except for __getitem__, __iter__, and __len__.
+
+ """
+
+ @abstractmethod
+ def __getitem__(self, key):
+ raise KeyError
+
+ def get(self, key, default=None):
+ 'D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None.'
+ try:
+ return self[key]
+ except KeyError:
+ return default
+
+ def __contains__(self, key):
+ try:
+ self[key]
+ except KeyError:
+ return False
+ else:
+ return True
+
+ def keys(self):
+ "D.keys() -> a set-like object providing a view on D's keys"
+ return KeysView(self)
+
+ def items(self):
+ "D.items() -> a set-like object providing a view on D's items"
+ return ItemsView(self)
+
+ def values(self):
+ "D.values() -> an object providing a view on D's values"
+ return ValuesView(self)
+
+ def __eq__(self, other):
+ if not isinstance(other, Mapping):
+ return NotImplemented
+ return dict(self.items()) == dict(other.items())
+
+ def __ne__(self, other):
+ return not (self == other)
+
+Mapping.register(mappingproxy)
+
+
+class MappingView(Sized):
+
+ def __init__(self, mapping):
+ self._mapping = mapping
+
+ def __len__(self):
+ return len(self._mapping)
+
+ def __repr__(self):
+ return '{0.__class__.__name__}({0._mapping!r})'.format(self)
+
+
+class KeysView(MappingView, Set):
+
+ @classmethod
+ def _from_iterable(self, it):
+ return set(it)
+
+ def __contains__(self, key):
+ return key in self._mapping
+
+ def __iter__(self):
+ yield from self._mapping
+
+KeysView.register(dict_keys)
+
+
+class ItemsView(MappingView, Set):
+
+ @classmethod
+ def _from_iterable(self, it):
+ return set(it)
+
+ def __contains__(self, item):
+ key, value = item
+ try:
+ v = self._mapping[key]
+ except KeyError:
+ return False
+ else:
+ return v == value
+
+ def __iter__(self):
+ for key in self._mapping:
+ yield (key, self._mapping[key])
+
+ItemsView.register(dict_items)
+
+
+class ValuesView(MappingView):
+
+ def __contains__(self, value):
+ for key in self._mapping:
+ if value == self._mapping[key]:
+ return True
+ return False
+
+ def __iter__(self):
+ for key in self._mapping:
+ yield self._mapping[key]
+
+ValuesView.register(dict_values)
+
+
+class MutableMapping(Mapping):
+
+ __slots__ = ()
+
+ """A MutableMapping is a generic container for associating
+ key/value pairs.
+
+ This class provides concrete generic implementations of all
+ methods except for __getitem__, __setitem__, __delitem__,
+ __iter__, and __len__.
+
+ """
+
+ @abstractmethod
+ def __setitem__(self, key, value):
+ raise KeyError
+
+ @abstractmethod
+ def __delitem__(self, key):
+ raise KeyError
+
+ __marker = object()
+
+ def pop(self, key, default=__marker):
+ '''D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
+ If key is not found, d is returned if given, otherwise KeyError is raised.
+ '''
+ try:
+ value = self[key]
+ except KeyError:
+ if default is self.__marker:
+ raise
+ return default
+ else:
+ del self[key]
+ return value
+
+ def popitem(self):
+ '''D.popitem() -> (k, v), remove and return some (key, value) pair
+ as a 2-tuple; but raise KeyError if D is empty.
+ '''
+ try:
+ key = next(iter(self))
+ except StopIteration:
+ raise KeyError
+ value = self[key]
+ del self[key]
+ return key, value
+
+ def clear(self):
+ 'D.clear() -> None. Remove all items from D.'
+ try:
+ while True:
+ self.popitem()
+ except KeyError:
+ pass
+
+ def update(*args, **kwds):
+ ''' D.update([E, ]**F) -> None. Update D from mapping/iterable E and F.
+ If E present and has a .keys() method, does: for k in E: D[k] = E[k]
+ If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v
+ In either case, this is followed by: for k, v in F.items(): D[k] = v
+ '''
+ if len(args) > 2:
+ raise TypeError("update() takes at most 2 positional "
+ "arguments ({} given)".format(len(args)))
+ elif not args:
+ raise TypeError("update() takes at least 1 argument (0 given)")
+ self = args[0]
+ other = args[1] if len(args) >= 2 else ()
+
+ if isinstance(other, Mapping):
+ for key in other:
+ self[key] = other[key]
+ elif hasattr(other, "keys"):
+ for key in other.keys():
+ self[key] = other[key]
+ else:
+ for key, value in other:
+ self[key] = value
+ for key, value in kwds.items():
+ self[key] = value
+
+ def setdefault(self, key, default=None):
+ 'D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D'
+ try:
+ return self[key]
+ except KeyError:
+ self[key] = default
+ return default
+
+MutableMapping.register(dict)
+
+
+### SEQUENCES ###
+
+
+class Sequence(Sized, Iterable, Container):
+
+ """All the operations on a read-only sequence.
+
+ Concrete subclasses must override __new__ or __init__,
+ __getitem__, and __len__.
+ """
+
+ __slots__ = ()
+
+ @abstractmethod
+ def __getitem__(self, index):
+ raise IndexError
+
+ def __iter__(self):
+ i = 0
+ try:
+ while True:
+ v = self[i]
+ yield v
+ i += 1
+ except IndexError:
+ return
+
+ def __contains__(self, value):
+ for v in self:
+ if v == value:
+ return True
+ return False
+
+ def __reversed__(self):
+ for i in reversed(range(len(self))):
+ yield self[i]
+
+ def index(self, value):
+ '''S.index(value) -> integer -- return first index of value.
+ Raises ValueError if the value is not present.
+ '''
+ for i, v in enumerate(self):
+ if v == value:
+ return i
+ raise ValueError
+
+ def count(self, value):
+ 'S.count(value) -> integer -- return number of occurrences of value'
+ return sum(1 for v in self if v == value)
+
+Sequence.register(tuple)
+Sequence.register(str)
+Sequence.register(range)
+Sequence.register(memoryview)
+
+
+class ByteString(Sequence):
+
+ """This unifies bytes and bytearray.
+
+ XXX Should add all their methods.
+ """
+
+ __slots__ = ()
+
+ByteString.register(bytes)
+ByteString.register(bytearray)
+
+
+class MutableSequence(Sequence):
+
+ __slots__ = ()
+
+ """All the operations on a read-write sequence.
+
+ Concrete subclasses must provide __new__ or __init__,
+ __getitem__, __setitem__, __delitem__, __len__, and insert().
+
+ """
+
+ @abstractmethod
+ def __setitem__(self, index, value):
+ raise IndexError
+
+ @abstractmethod
+ def __delitem__(self, index):
+ raise IndexError
+
+ @abstractmethod
+ def insert(self, index, value):
+ 'S.insert(index, value) -- insert value before index'
+ raise IndexError
+
+ def append(self, value):
+ 'S.append(value) -- append value to the end of the sequence'
+ self.insert(len(self), value)
+
+ def clear(self):
+ 'S.clear() -> None -- remove all items from S'
+ try:
+ while True:
+ self.pop()
+ except IndexError:
+ pass
+
+ def reverse(self):
+ 'S.reverse() -- reverse *IN PLACE*'
+ n = len(self)
+ for i in range(n//2):
+ self[i], self[n-i-1] = self[n-i-1], self[i]
+
+ def extend(self, values):
+ 'S.extend(iterable) -- extend sequence by appending elements from the iterable'
+ for v in values:
+ self.append(v)
+
+ def pop(self, index=-1):
+ '''S.pop([index]) -> item -- remove and return item at index (default last).
+ Raise IndexError if list is empty or index is out of range.
+ '''
+ v = self[index]
+ del self[index]
+ return v
+
+ def remove(self, value):
+ '''S.remove(value) -- remove first occurrence of value.
+ Raise ValueError if the value is not present.
+ '''
+ del self[self.index(value)]
+
+ def __iadd__(self, values):
+ self.extend(values)
+ return self
+
+MutableSequence.register(list)
+MutableSequence.register(bytearray) # Multiply inheriting, see ByteString
diff --git a/Lib/_dummy_thread.py b/Lib/_dummy_thread.py
index 13b1f26..b67cfb9 100644
--- a/Lib/_dummy_thread.py
+++ b/Lib/_dummy_thread.py
@@ -81,6 +81,10 @@ def stack_size(size=None):
raise error("setting thread stack size not supported")
return 0
+def _set_sentinel():
+ """Dummy implementation of _thread._set_sentinel()."""
+ return LockType()
+
class LockType(object):
"""Class implementing dummy implementation of _thread.LockType.
diff --git a/Lib/_osx_support.py b/Lib/_osx_support.py
index 50b2d17..b6eac5f 100644
--- a/Lib/_osx_support.py
+++ b/Lib/_osx_support.py
@@ -38,7 +38,7 @@ def _find_executable(executable, path=None):
paths = path.split(os.pathsep)
base, ext = os.path.splitext(executable)
- if (sys.platform == 'win32' or os.name == 'os2') and (ext != '.exe'):
+ if (sys.platform == 'win32') and (ext != '.exe'):
executable = executable + '.exe'
if not os.path.isfile(executable):
@@ -94,7 +94,7 @@ def _get_system_version():
_SYSTEM_VERSION = ''
try:
f = open('/System/Library/CoreServices/SystemVersion.plist')
- except IOError:
+ except OSError:
# We're on a plain darwin box, fall back to the default
# behaviour.
pass
@@ -182,7 +182,7 @@ def _find_appropriate_compiler(_config_vars):
# Compiler is GCC, check if it is LLVM-GCC
data = _read_output("'%s' --version"
% (cc.replace("'", "'\"'\"'"),))
- if 'llvm-gcc' in data:
+ if data and 'llvm-gcc' in data:
# Found LLVM-GCC, fall back to clang
cc = _find_build_tool('clang')
diff --git a/Lib/_pyio.py b/Lib/_pyio.py
index a0c4b25..b04d23a 100644
--- a/Lib/_pyio.py
+++ b/Lib/_pyio.py
@@ -34,7 +34,7 @@ BlockingIOError = BlockingIOError
def open(file, mode="r", buffering=-1, encoding=None, errors=None,
newline=None, closefd=True, opener=None):
- r"""Open file and return a stream. Raise IOError upon failure.
+ r"""Open file and return a stream. Raise OSError upon failure.
file is either a text or byte string giving the name (and the path
if the file isn't in the current working directory) of the file to
@@ -62,8 +62,7 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
'b' binary mode
't' text mode (default)
'+' open a disk file for updating (reading and writing)
- 'U' universal newline mode (for backwards compatibility; unneeded
- for new code)
+ 'U' universal newline mode (deprecated)
========= ===============================================================
The default mode is 'rt' (open for reading text). For binary random
@@ -79,6 +78,10 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
returned as strings, the bytes having been first decoded using a
platform-dependent encoding or using the specified encoding if given.
+ 'U' mode is deprecated and will raise an exception in future versions
+ of Python. It has no effect in Python 3. Use newline to control
+ universal newlines mode.
+
buffering is an optional integer used to set the buffering policy.
Pass 0 to switch buffering off (only allowed in binary mode), 1 to select
line buffering (only usable in text mode), and an integer > 1 to indicate
@@ -129,6 +132,8 @@ 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.
+ The newly created file is non-inheritable.
+
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
@@ -172,6 +177,9 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
if "U" in modes:
if creating or writing or appending:
raise ValueError("can't use U and writing mode at once")
+ import warnings
+ warnings.warn("'U' mode is deprecated",
+ DeprecationWarning, 2)
reading = True
if text and binary:
raise ValueError("can't have text and binary mode at once")
@@ -200,7 +208,7 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
buffering = DEFAULT_BUFFER_SIZE
try:
bs = os.fstat(raw.fileno()).st_blksize
- except (os.error, AttributeError):
+ except (OSError, AttributeError):
pass
else:
if bs > 1:
@@ -254,7 +262,7 @@ class OpenWrapper:
try:
UnsupportedOperation = io.UnsupportedOperation
except AttributeError:
- class UnsupportedOperation(ValueError, IOError):
+ class UnsupportedOperation(ValueError, OSError):
pass
@@ -278,7 +286,7 @@ class IOBase(metaclass=abc.ABCMeta):
readinto) needed. Text I/O classes work with str data.
Note that calling any method (even inquiries) on a closed stream is
- undefined. Implementations may raise IOError in this case.
+ undefined. Implementations may raise OSError in this case.
IOBase (and its subclasses) support the iterator protocol, meaning
that an IOBase object can be iterated over yielding the lines in a
@@ -294,7 +302,7 @@ class IOBase(metaclass=abc.ABCMeta):
### Internal ###
def _unsupported(self, name):
- """Internal: raise an IOError exception for unsupported operations."""
+ """Internal: raise an OSError exception for unsupported operations."""
raise UnsupportedOperation("%s.%s() not supported" %
(self.__class__.__name__, name))
@@ -441,7 +449,7 @@ class IOBase(metaclass=abc.ABCMeta):
def fileno(self):
"""Returns underlying file descriptor (an int) if one exists.
- An IOError is raised if the IO object does not use a file descriptor.
+ An OSError is raised if the IO object does not use a file descriptor.
"""
self._unsupported("fileno")
@@ -455,11 +463,11 @@ class IOBase(metaclass=abc.ABCMeta):
### Readline[s] and writelines ###
- def readline(self, limit=-1):
+ def readline(self, size=-1):
r"""Read and return a line of bytes from the stream.
- If limit is specified, at most limit bytes will be read.
- Limit should be an int.
+ If size is specified, at most size bytes will be read.
+ Size should be an int.
The line terminator is always b'\n' for binary files; for text
files, the newlines argument to open can be used to select the line
@@ -472,18 +480,18 @@ class IOBase(metaclass=abc.ABCMeta):
if not readahead:
return 1
n = (readahead.find(b"\n") + 1) or len(readahead)
- if limit >= 0:
- n = min(n, limit)
+ if size >= 0:
+ n = min(n, size)
return n
else:
def nreadahead():
return 1
- if limit is None:
- limit = -1
- elif not isinstance(limit, int):
- raise TypeError("limit must be an integer")
+ if size is None:
+ size = -1
+ elif not isinstance(size, int):
+ raise TypeError("size must be an integer")
res = bytearray()
- while limit < 0 or len(res) < limit:
+ while size < 0 or len(res) < size:
b = self.read(nreadahead())
if not b:
break
@@ -542,17 +550,17 @@ class RawIOBase(IOBase):
# primitive operation, but that would lead to nasty recursion in case
# a subclass doesn't implement either.)
- def read(self, n=-1):
- """Read and return up to n bytes, where n is an int.
+ def read(self, size=-1):
+ """Read and return up to size bytes, where size is an int.
Returns an empty bytes object on EOF, or None if the object is
set not to block and has no data to read.
"""
- if n is None:
- n = -1
- if n < 0:
+ if size is None:
+ size = -1
+ if size < 0:
return self.readall()
- b = bytearray(n.__index__())
+ b = bytearray(size.__index__())
n = self.readinto(b)
if n is None:
return None
@@ -610,8 +618,8 @@ class BufferedIOBase(IOBase):
implementation, but wrap one.
"""
- def read(self, n=None):
- """Read and return up to n bytes, where n is an int.
+ def read(self, size=None):
+ """Read and return up to size bytes, where size is an int.
If the argument is omitted, None, or negative, reads and
returns all data until EOF.
@@ -630,9 +638,9 @@ class BufferedIOBase(IOBase):
"""
self._unsupported("read")
- def read1(self, n=None):
- """Read up to n bytes with at most one read() system call,
- where n is an int.
+ def read1(self, size=None):
+ """Read up to size bytes with at most one read() system call,
+ where size is an int.
"""
self._unsupported("read1")
@@ -699,13 +707,13 @@ class _BufferedIOMixin(BufferedIOBase):
def seek(self, pos, whence=0):
new_position = self.raw.seek(pos, whence)
if new_position < 0:
- raise IOError("seek() returned an invalid position")
+ raise OSError("seek() returned an invalid position")
return new_position
def tell(self):
pos = self.raw.tell()
if pos < 0:
- raise IOError("tell() returned an invalid position")
+ raise OSError("tell() returned an invalid position")
return pos
def truncate(self, pos=None):
@@ -820,24 +828,24 @@ class BytesIO(BufferedIOBase):
"""
return memoryview(self._buffer)
- def read(self, n=None):
+ def read(self, size=None):
if self.closed:
raise ValueError("read from closed file")
- if n is None:
- n = -1
- if n < 0:
- n = len(self._buffer)
+ if size is None:
+ size = -1
+ if size < 0:
+ size = len(self._buffer)
if len(self._buffer) <= self._pos:
return b""
- newpos = min(len(self._buffer), self._pos + n)
+ newpos = min(len(self._buffer), self._pos + size)
b = self._buffer[self._pos : newpos]
self._pos = newpos
return bytes(b)
- def read1(self, n):
+ def read1(self, size):
"""This is the same as read.
"""
- return self.read(n)
+ return self.read(size)
def write(self, b):
if self.closed:
@@ -927,7 +935,7 @@ class BufferedReader(_BufferedIOMixin):
"""Create a new buffered reader using the given readable raw IO object.
"""
if not raw.readable():
- raise IOError('"raw" argument must be readable.')
+ raise OSError('"raw" argument must be readable.')
_BufferedIOMixin.__init__(self, raw)
if buffer_size <= 0:
@@ -940,18 +948,18 @@ class BufferedReader(_BufferedIOMixin):
self._read_buf = b""
self._read_pos = 0
- def read(self, n=None):
- """Read n bytes.
+ def read(self, size=None):
+ """Read size bytes.
- Returns exactly n bytes of data unless the underlying raw IO
+ Returns exactly size bytes of data unless the underlying raw IO
stream reaches EOF or if the call would block in non-blocking
- mode. If n is negative, read until EOF or until read() would
+ mode. If size is negative, read until EOF or until read() would
block.
"""
- if n is not None and n < -1:
+ if size is not None and size < -1:
raise ValueError("invalid number of bytes to read")
with self._read_lock:
- return self._read_unlocked(n)
+ return self._read_unlocked(size)
def _read_unlocked(self, n=None):
nodata_val = b""
@@ -1011,7 +1019,7 @@ class BufferedReader(_BufferedIOMixin):
self._read_pos = 0
return out[:n] if out else nodata_val
- def peek(self, n=0):
+ def peek(self, size=0):
"""Returns buffered bytes without advancing the position.
The argument indicates a desired minimal number of bytes; we
@@ -1019,7 +1027,7 @@ class BufferedReader(_BufferedIOMixin):
than self.buffer_size.
"""
with self._read_lock:
- return self._peek_unlocked(n)
+ return self._peek_unlocked(size)
def _peek_unlocked(self, n=0):
want = min(n, self.buffer_size)
@@ -1037,18 +1045,18 @@ class BufferedReader(_BufferedIOMixin):
self._read_pos = 0
return self._read_buf[self._read_pos:]
- def read1(self, n):
- """Reads up to n bytes, with at most one read() system call."""
- # Returns up to n bytes. If at least one byte is buffered, we
+ def read1(self, size):
+ """Reads up to size bytes, with at most one read() system call."""
+ # Returns up to size bytes. If at least one byte is buffered, we
# only return buffered bytes. Otherwise, we do one raw read.
- if n < 0:
+ if size < 0:
raise ValueError("number of bytes to read must be positive")
- if n == 0:
+ if size == 0:
return b""
with self._read_lock:
self._peek_unlocked(1)
return self._read_unlocked(
- min(n, len(self._read_buf) - self._read_pos))
+ min(size, len(self._read_buf) - self._read_pos))
def tell(self):
return _BufferedIOMixin.tell(self) - len(self._read_buf) + self._read_pos
@@ -1074,7 +1082,7 @@ class BufferedWriter(_BufferedIOMixin):
def __init__(self, raw, buffer_size=DEFAULT_BUFFER_SIZE):
if not raw.writable():
- raise IOError('"raw" argument must be writable.')
+ raise OSError('"raw" argument must be writable.')
_BufferedIOMixin.__init__(self, raw)
if buffer_size <= 0:
@@ -1138,7 +1146,7 @@ class BufferedWriter(_BufferedIOMixin):
errno.EAGAIN,
"write could not complete without blocking", 0)
if n > len(self._write_buf) or n < 0:
- raise IOError("write() returned incorrect number of bytes")
+ raise OSError("write() returned incorrect number of bytes")
del self._write_buf[:n]
def tell(self):
@@ -1174,18 +1182,18 @@ class BufferedRWPair(BufferedIOBase):
The arguments are two RawIO instances.
"""
if not reader.readable():
- raise IOError('"reader" argument must be readable.')
+ raise OSError('"reader" argument must be readable.')
if not writer.writable():
- raise IOError('"writer" argument must be writable.')
+ raise OSError('"writer" argument must be writable.')
self.reader = BufferedReader(reader, buffer_size)
self.writer = BufferedWriter(writer, buffer_size)
- def read(self, n=None):
- if n is None:
- n = -1
- return self.reader.read(n)
+ def read(self, size=None):
+ if size is None:
+ size = -1
+ return self.reader.read(size)
def readinto(self, b):
return self.reader.readinto(b)
@@ -1193,11 +1201,11 @@ class BufferedRWPair(BufferedIOBase):
def write(self, b):
return self.writer.write(b)
- def peek(self, n=0):
- return self.reader.peek(n)
+ def peek(self, size=0):
+ return self.reader.peek(size)
- def read1(self, n):
- return self.reader.read1(n)
+ def read1(self, size):
+ return self.reader.read1(size)
def readable(self):
return self.reader.readable()
@@ -1248,7 +1256,7 @@ class BufferedRandom(BufferedWriter, BufferedReader):
with self._read_lock:
self._reset_read_buf()
if pos < 0:
- raise IOError("seek() returned invalid position")
+ raise OSError("seek() returned invalid position")
return pos
def tell(self):
@@ -1263,23 +1271,23 @@ class BufferedRandom(BufferedWriter, BufferedReader):
# Use seek to flush the read buffer.
return BufferedWriter.truncate(self, pos)
- def read(self, n=None):
- if n is None:
- n = -1
+ def read(self, size=None):
+ if size is None:
+ size = -1
self.flush()
- return BufferedReader.read(self, n)
+ return BufferedReader.read(self, size)
def readinto(self, b):
self.flush()
return BufferedReader.readinto(self, b)
- def peek(self, n=0):
+ def peek(self, size=0):
self.flush()
- return BufferedReader.peek(self, n)
+ return BufferedReader.peek(self, size)
- def read1(self, n):
+ def read1(self, size):
self.flush()
- return BufferedReader.read1(self, n)
+ return BufferedReader.read1(self, size)
def write(self, b):
if self._read_buf:
@@ -1299,11 +1307,11 @@ class TextIOBase(IOBase):
are immutable. There is no public constructor.
"""
- def read(self, n=-1):
- """Read at most n characters from stream, where n is an int.
+ def read(self, size=-1):
+ """Read at most size characters from stream, where size is an int.
- Read from underlying buffer until we have n characters or we hit EOF.
- If n is negative or omitted, read until EOF.
+ Read from underlying buffer until we have size characters or we hit EOF.
+ If size is negative or omitted, read until EOF.
Returns a string.
"""
@@ -1732,7 +1740,7 @@ class TextIOWrapper(TextIOBase):
if not self._seekable:
raise UnsupportedOperation("underlying stream is not seekable")
if not self._telling:
- raise IOError("telling position disabled by next() call")
+ raise OSError("telling position disabled by next() call")
self.flush()
position = self.buffer.tell()
decoder = self._decoder
@@ -1819,7 +1827,7 @@ class TextIOWrapper(TextIOBase):
chars_decoded += len(decoder.decode(b'', final=True))
need_eof = 1
if chars_decoded < chars_to_skip:
- raise IOError("can't reconstruct logical file position")
+ raise OSError("can't reconstruct logical file position")
# The returned cookie corresponds to the last safe start point.
return self._pack_cookie(
@@ -1896,7 +1904,7 @@ class TextIOWrapper(TextIOBase):
# Skip chars_to_skip of the decoded characters.
if len(self._decoded_chars) < chars_to_skip:
- raise IOError("can't restore logical file position")
+ raise OSError("can't restore logical file position")
self._decoded_chars_used = chars_to_skip
# Finally, reset the encoder (merely useful for proper BOM handling)
@@ -1912,16 +1920,16 @@ class TextIOWrapper(TextIOBase):
encoder.reset()
return cookie
- def read(self, n=None):
+ def read(self, size=None):
self._checkReadable()
- if n is None:
- n = -1
+ if size is None:
+ size = -1
decoder = self._decoder or self._get_decoder()
try:
- n.__index__
+ size.__index__
except AttributeError as err:
raise TypeError("an integer is required") from err
- if n < 0:
+ if size < 0:
# Read everything.
result = (self._get_decoded_chars() +
decoder.decode(self.buffer.read(), final=True))
@@ -1929,12 +1937,12 @@ class TextIOWrapper(TextIOBase):
self._snapshot = None
return result
else:
- # Keep reading chunks until we have n characters to return.
+ # Keep reading chunks until we have size characters to return.
eof = False
- result = self._get_decoded_chars(n)
- while len(result) < n and not eof:
+ result = self._get_decoded_chars(size)
+ while len(result) < size and not eof:
eof = not self._read_chunk()
- result += self._get_decoded_chars(n - len(result))
+ result += self._get_decoded_chars(size - len(result))
return result
def __next__(self):
@@ -1946,13 +1954,13 @@ class TextIOWrapper(TextIOBase):
raise StopIteration
return line
- def readline(self, limit=None):
+ def readline(self, size=None):
if self.closed:
raise ValueError("read from closed file")
- if limit is None:
- limit = -1
- elif not isinstance(limit, int):
- raise TypeError("limit must be an integer")
+ if size is None:
+ size = -1
+ elif not isinstance(size, int):
+ raise TypeError("size must be an integer")
# Grab all the decoded text (we will rewind any extra bits later).
line = self._get_decoded_chars()
@@ -2011,8 +2019,8 @@ class TextIOWrapper(TextIOBase):
endpos = pos + len(self._readnl)
break
- if limit >= 0 and len(line) >= limit:
- endpos = limit # reached length limit
+ if size >= 0 and len(line) >= size:
+ endpos = size # reached length size
break
# No line ending seen yet - get more data'
@@ -2027,8 +2035,8 @@ class TextIOWrapper(TextIOBase):
self._snapshot = None
return line
- if limit >= 0 and endpos > limit:
- endpos = limit # don't exceed limit
+ if size >= 0 and endpos > size:
+ endpos = size # don't exceed size
# Rewind _decoded_chars to just after the line ending we found.
self._rewind_decoded_chars(len(line) - endpos)
@@ -2059,7 +2067,6 @@ class StringIO(TextIOWrapper):
if not isinstance(initial_value, str):
raise TypeError("initial_value must be str or None, not {0}"
.format(type(initial_value).__name__))
- initial_value = str(initial_value)
self.write(initial_value)
self.seek(0)
diff --git a/Lib/_sitebuiltins.py b/Lib/_sitebuiltins.py
new file mode 100644
index 0000000..c29cf4b
--- /dev/null
+++ b/Lib/_sitebuiltins.py
@@ -0,0 +1,103 @@
+"""
+The objects used by the site module to add custom builtins.
+"""
+
+# Those objects are almost immortal and they keep a reference to their module
+# globals. Defining them in the site module would keep too many references
+# alive.
+# Note this means this module should also avoid keep things alive in its
+# globals.
+
+import sys
+
+class Quitter(object):
+ def __init__(self, name, eof):
+ self.name = name
+ self.eof = eof
+ def __repr__(self):
+ return 'Use %s() or %s to exit' % (self.name, self.eof)
+ def __call__(self, code=None):
+ # Shells like IDLE catch the SystemExit, but listen when their
+ # stdin wrapper is closed.
+ try:
+ sys.stdin.close()
+ except:
+ pass
+ raise SystemExit(code)
+
+
+class _Printer(object):
+ """interactive prompt objects for printing the license text, a list of
+ contributors and the copyright notice."""
+
+ MAXLINES = 23
+
+ def __init__(self, name, data, files=(), dirs=()):
+ import os
+ self.__name = name
+ self.__data = data
+ self.__lines = None
+ self.__filenames = [os.path.join(dir, filename)
+ for dir in dirs
+ for filename in files]
+
+ def __setup(self):
+ if self.__lines:
+ return
+ data = None
+ for filename in self.__filenames:
+ try:
+ with open(filename, "r") as fp:
+ data = fp.read()
+ break
+ except OSError:
+ pass
+ if not data:
+ data = self.__data
+ self.__lines = data.split('\n')
+ self.__linecnt = len(self.__lines)
+
+ def __repr__(self):
+ self.__setup()
+ if len(self.__lines) <= self.MAXLINES:
+ return "\n".join(self.__lines)
+ else:
+ return "Type %s() to see the full %s text" % ((self.__name,)*2)
+
+ def __call__(self):
+ self.__setup()
+ prompt = 'Hit Return for more, or q (and Return) to quit: '
+ lineno = 0
+ while 1:
+ try:
+ for i in range(lineno, lineno + self.MAXLINES):
+ print(self.__lines[i])
+ except IndexError:
+ break
+ else:
+ lineno += self.MAXLINES
+ key = None
+ while key is None:
+ key = input(prompt)
+ if key not in ('', 'q'):
+ key = None
+ if key == 'q':
+ break
+
+
+class _Helper(object):
+ """Define the builtin 'help'.
+
+ This is a wrapper around pydoc.help that provides a helpful message
+ when 'help' is typed at the Python interactive prompt.
+
+ Calling help() at the Python prompt starts an interactive help session.
+ Calling help(thing) prints help for the python object 'thing'.
+ """
+
+ def __repr__(self):
+ return "Type help() for interactive help, " \
+ "or help(object) for help about object."
+ def __call__(self, *args, **kwds):
+ import pydoc
+ return pydoc.help(*args, **kwds)
diff --git a/Lib/_strptime.py b/Lib/_strptime.py
index 9058a69..53bd34b 100644
--- a/Lib/_strptime.py
+++ b/Lib/_strptime.py
@@ -14,14 +14,14 @@ import time
import locale
import calendar
from re import compile as re_compile
-from re import IGNORECASE, ASCII
+from re import IGNORECASE
from re import escape as re_escape
from datetime import (date as datetime_date,
timedelta as datetime_timedelta,
timezone as datetime_timezone)
try:
from _thread import allocate_lock as _thread_allocate_lock
-except:
+except ImportError:
from _dummy_thread import allocate_lock as _thread_allocate_lock
__all__ = []
diff --git a/Lib/abc.py b/Lib/abc.py
index 09778e8..0358a46 100644
--- a/Lib/abc.py
+++ b/Lib/abc.py
@@ -5,6 +5,7 @@
from _weakrefset import WeakSet
+
def abstractmethod(funcobj):
"""A decorator indicating abstract methods.
@@ -124,6 +125,8 @@ class ABCMeta(type):
# A global counter that is incremented each time a class is
# registered as a virtual subclass of anything. It forces the
# negative cache to be cleared before its next use.
+ # Note: this counter is private. Use `abc.get_cache_token()` for
+ # external code.
_abc_invalidation_counter = 0
def __new__(mcls, name, bases, namespace):
@@ -226,3 +229,20 @@ class ABCMeta(type):
# No dice; update negative cache
cls._abc_negative_cache.add(subclass)
return False
+
+
+class ABC(metaclass=ABCMeta):
+ """Helper class that provides a standard way to create an ABC using
+ inheritance.
+ """
+ pass
+
+
+def get_cache_token():
+ """Returns the current ABC cache token.
+
+ The token is an opaque object (supporting equality testing) identifying the
+ current version of the ABC cache for virtual subclasses. The token changes
+ with every call to ``register()`` on any ABC.
+ """
+ return ABCMeta._abc_invalidation_counter
diff --git a/Lib/aifc.py b/Lib/aifc.py
index db0924a..9e64de9 100644
--- a/Lib/aifc.py
+++ b/Lib/aifc.py
@@ -69,7 +69,7 @@ This returns an instance of a class with the following public methods:
getcomptype() -- returns compression type ('NONE' for AIFF files)
getcompname() -- returns human-readable version of
compression type ('not compressed' for AIFF files)
- getparams() -- returns a tuple consisting of all of the
+ getparams() -- returns a namedtuple consisting of all of the
above in the above order
getmarkers() -- get the list of marks in the audio file or None
if there are no marks
@@ -252,6 +252,11 @@ def _write_float(f, x):
_write_ulong(f, lomant)
from chunk import Chunk
+from collections import namedtuple
+
+_aifc_params = namedtuple('_aifc_params',
+ 'nchannels sampwidth framerate nframes comptype compname')
+
class Aifc_read:
# Variables used in this class:
@@ -334,6 +339,12 @@ class Aifc_read:
# else, assume it is an open file object already
self.initfp(f)
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *args):
+ self.close()
+
#
# User visible methods.
#
@@ -372,9 +383,9 @@ class Aifc_read:
## return self._version
def getparams(self):
- return self.getnchannels(), self.getsampwidth(), \
- self.getframerate(), self.getnframes(), \
- self.getcomptype(), self.getcompname()
+ return _aifc_params(self.getnchannels(), self.getsampwidth(),
+ self.getframerate(), self.getnframes(),
+ self.getcomptype(), self.getcompname())
def getmarkers(self):
if len(self._markers) == 0:
@@ -551,6 +562,12 @@ class Aifc_write:
def __del__(self):
self.close()
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *args):
+ self.close()
+
#
# User visible methods.
#
@@ -644,8 +661,8 @@ class Aifc_write:
def getparams(self):
if not self._nchannels or not self._sampwidth or not self._framerate:
raise Error('not all parameters set')
- return self._nchannels, self._sampwidth, self._framerate, \
- self._nframes, self._comptype, self._compname
+ return _aifc_params(self._nchannels, self._sampwidth, self._framerate,
+ self._nframes, self._comptype, self._compname)
def setmark(self, id, pos, name):
if id <= 0:
@@ -675,6 +692,8 @@ class Aifc_write:
return self._nframeswritten
def writeframesraw(self, data):
+ if not isinstance(data, (bytes, bytearray)):
+ data = memoryview(data).cast('B')
self._ensure_header_written(len(data))
nframes = len(data) // (self._sampwidth * self._nchannels)
if self._convert:
@@ -878,8 +897,7 @@ if __name__ == '__main__':
if not sys.argv[1:]:
sys.argv.append('/usr/demos/data/audio/bach.aiff')
fn = sys.argv[1]
- f = open(fn, 'r')
- try:
+ with open(fn, 'r') as f:
print("Reading", fn)
print("nchannels =", f.getnchannels())
print("nframes =", f.getnframes())
@@ -890,16 +908,11 @@ if __name__ == '__main__':
if sys.argv[2:]:
gn = sys.argv[2]
print("Writing", gn)
- g = open(gn, 'w')
- try:
+ with open(gn, 'w') as g:
g.setparams(f.getparams())
while 1:
data = f.readframes(1024)
if not data:
break
g.writeframes(data)
- finally:
- g.close()
print("Done.")
- finally:
- f.close()
diff --git a/Lib/argparse.py b/Lib/argparse.py
index bc2ba13..5ad7e13 100644
--- a/Lib/argparse.py
+++ b/Lib/argparse.py
@@ -608,8 +608,7 @@ class HelpFormatter(object):
pass
else:
self._indent()
- for subaction in get_subactions():
- yield subaction
+ yield from get_subactions()
self._dedent()
def _split_lines(self, text, width):
@@ -1040,7 +1039,8 @@ class _VersionAction(Action):
version = parser.version
formatter = parser._get_formatter()
formatter.add_text(version)
- parser.exit(message=formatter.format_help())
+ parser._print_message(formatter.format_help(), _sys.stdout)
+ parser.exit()
class _SubParsersAction(Action):
@@ -1143,11 +1143,17 @@ class FileType(object):
same values as the builtin open() function.
- bufsize -- The file's desired buffer size. Accepts the same values as
the builtin open() function.
+ - encoding -- The file's encoding. Accepts the same values as the
+ builtin open() function.
+ - errors -- A string indicating how encoding and decoding errors are to
+ be handled. Accepts the same value as the builtin open() function.
"""
- def __init__(self, mode='r', bufsize=-1):
+ def __init__(self, mode='r', bufsize=-1, encoding=None, errors=None):
self._mode = mode
self._bufsize = bufsize
+ self._encoding = encoding
+ self._errors = errors
def __call__(self, string):
# the special argument "-" means sys.std{in,out}
@@ -1162,14 +1168,18 @@ class FileType(object):
# all other arguments are used as file names
try:
- return open(string, self._mode, self._bufsize)
- except IOError as e:
+ return open(string, self._mode, self._bufsize, self._encoding,
+ self._errors)
+ except OSError as e:
message = _("can't open '%s': %s")
raise ArgumentTypeError(message % (string, e))
def __repr__(self):
args = self._mode, self._bufsize
- args_str = ', '.join(repr(arg) for arg in args if arg != -1)
+ kwargs = [('encoding', self._encoding), ('errors', self._errors)]
+ args_str = ', '.join([repr(arg) for arg in args if arg != -1] +
+ ['%s=%r' % (kw, arg) for kw, arg in kwargs
+ if arg is not None])
return '%s(%s)' % (type(self).__name__, args_str)
# ===========================
@@ -2003,17 +2013,14 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
# replace arguments referencing files with the file content
else:
try:
- args_file = open(arg_string[1:])
- try:
+ with open(arg_string[1:]) as args_file:
arg_strings = []
for arg_line in args_file.read().splitlines():
for arg in self.convert_arg_line_to_args(arg_line):
arg_strings.append(arg)
arg_strings = self._read_args_from_files(arg_strings)
new_arg_strings.extend(arg_strings)
- finally:
- args_file.close()
- except IOError:
+ except OSError:
err = _sys.exc_info()[1]
self.error(str(err))
diff --git a/Lib/ast.py b/Lib/ast.py
index 13f59f9..02c3b28 100644
--- a/Lib/ast.py
+++ b/Lib/ast.py
@@ -42,7 +42,6 @@ def literal_eval(node_or_string):
Python literal structures: strings, bytes, numbers, tuples, lists, dicts,
sets, booleans, and None.
"""
- _safe_names = {'None': None, 'True': True, 'False': False}
if isinstance(node_or_string, str):
node_or_string = parse(node_or_string, mode='eval')
if isinstance(node_or_string, Expression):
@@ -61,9 +60,8 @@ def literal_eval(node_or_string):
elif isinstance(node, Dict):
return dict((_convert(k), _convert(v)) for k, v
in zip(node.keys, node.values))
- elif isinstance(node, Name):
- if node.id in _safe_names:
- return _safe_names[node.id]
+ elif isinstance(node, NameConstant):
+ return node.value
elif isinstance(node, UnaryOp) and \
isinstance(node.op, (UAdd, USub)) and \
isinstance(node.operand, (Num, UnaryOp, BinOp)):
diff --git a/Lib/asynchat.py b/Lib/asynchat.py
index eea3418..f1a5731 100644
--- a/Lib/asynchat.py
+++ b/Lib/asynchat.py
@@ -45,7 +45,6 @@ command will be accumulated (using your own 'collect_incoming_data'
method) up to the terminator, and then control will be returned to
you - by calling your self.found_terminator() method.
"""
-import socket
import asyncore
from collections import deque
@@ -56,8 +55,8 @@ class async_chat (asyncore.dispatcher):
# these are overridable defaults
- ac_in_buffer_size = 4096
- ac_out_buffer_size = 4096
+ ac_in_buffer_size = 65536
+ ac_out_buffer_size = 65536
# we don't want to enable the use of encoding by default, because that is a
# sign of an application bug that we don't want to pass silently
@@ -111,7 +110,7 @@ class async_chat (asyncore.dispatcher):
try:
data = self.recv (self.ac_in_buffer_size)
- except socket.error as why:
+ except OSError as why:
self.handle_error()
return
@@ -240,7 +239,7 @@ class async_chat (asyncore.dispatcher):
# send the data
try:
num_sent = self.send(data)
- except socket.error:
+ except OSError:
self.handle_error()
return
diff --git a/Lib/asyncio/__init__.py b/Lib/asyncio/__init__.py
new file mode 100644
index 0000000..3df2f80
--- /dev/null
+++ b/Lib/asyncio/__init__.py
@@ -0,0 +1,45 @@
+"""The asyncio package, tracking PEP 3156."""
+
+import sys
+
+# The selectors module is in the stdlib in Python 3.4 but not in 3.3.
+# Do this first, so the other submodules can use "from . import selectors".
+# Prefer asyncio/selectors.py over the stdlib one, as ours may be newer.
+try:
+ from . import selectors
+except ImportError:
+ import selectors # Will also be exported.
+
+if sys.platform == 'win32':
+ # Similar thing for _overlapped.
+ try:
+ from . import _overlapped
+ except ImportError:
+ import _overlapped # Will also be exported.
+
+# This relies on each of the submodules having an __all__ variable.
+from .events import *
+from .futures import *
+from .locks import *
+from .protocols import *
+from .queues import *
+from .streams import *
+from .subprocess import *
+from .tasks import *
+from .transports import *
+
+if sys.platform == 'win32': # pragma: no cover
+ from .windows_events import *
+else:
+ from .unix_events import * # pragma: no cover
+
+
+__all__ = (events.__all__ +
+ futures.__all__ +
+ locks.__all__ +
+ protocols.__all__ +
+ queues.__all__ +
+ streams.__all__ +
+ subprocess.__all__ +
+ tasks.__all__ +
+ transports.__all__)
diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py
new file mode 100644
index 0000000..d2bdc07
--- /dev/null
+++ b/Lib/asyncio/base_events.py
@@ -0,0 +1,828 @@
+"""Base implementation of event loop.
+
+The event loop can be broken up into a multiplexer (the part
+responsible for notifying us of IO events) and the event loop proper,
+which wraps a multiplexer with functionality for scheduling callbacks,
+immediately or at a given time in the future.
+
+Whenever a public API takes a callback, subsequent positional
+arguments will be passed to the callback if/when it is called. This
+avoids the proliferation of trivial lambdas implementing closures.
+Keyword arguments for the callback are not supported; this is a
+conscious design decision, leaving the door open for keyword arguments
+to modify the meaning of the API call itself.
+"""
+
+
+import collections
+import concurrent.futures
+import heapq
+import logging
+import socket
+import subprocess
+import time
+import os
+import sys
+
+from . import events
+from . import futures
+from . import tasks
+from .log import logger
+
+
+__all__ = ['BaseEventLoop', 'Server']
+
+
+# Argument for default thread pool executor creation.
+_MAX_WORKERS = 5
+
+
+class _StopError(BaseException):
+ """Raised to stop the event loop."""
+
+
+def _check_resolved_address(sock, address):
+ # Ensure that the address is already resolved to avoid the trap of hanging
+ # the entire event loop when the address requires doing a DNS lookup.
+ family = sock.family
+ if family == socket.AF_INET:
+ host, port = address
+ elif family == socket.AF_INET6:
+ host, port = address[:2]
+ else:
+ return
+
+ type_mask = 0
+ if hasattr(socket, 'SOCK_NONBLOCK'):
+ type_mask |= socket.SOCK_NONBLOCK
+ if hasattr(socket, 'SOCK_CLOEXEC'):
+ type_mask |= socket.SOCK_CLOEXEC
+ # Use getaddrinfo(AI_NUMERICHOST) to ensure that the address is
+ # already resolved.
+ try:
+ socket.getaddrinfo(host, port,
+ family=family,
+ type=(sock.type & ~type_mask),
+ proto=sock.proto,
+ flags=socket.AI_NUMERICHOST)
+ except socket.gaierror as err:
+ raise ValueError("address must be resolved (IP address), got %r: %s"
+ % (address, err))
+
+def _raise_stop_error(*args):
+ raise _StopError
+
+
+class Server(events.AbstractServer):
+
+ def __init__(self, loop, sockets):
+ self.loop = loop
+ self.sockets = sockets
+ self.active_count = 0
+ self.waiters = []
+
+ def attach(self, transport):
+ assert self.sockets is not None
+ self.active_count += 1
+
+ def detach(self, transport):
+ assert self.active_count > 0
+ self.active_count -= 1
+ if self.active_count == 0 and self.sockets is None:
+ self._wakeup()
+
+ def close(self):
+ sockets = self.sockets
+ if sockets is not None:
+ self.sockets = None
+ for sock in sockets:
+ self.loop._stop_serving(sock)
+ if self.active_count == 0:
+ self._wakeup()
+
+ def _wakeup(self):
+ waiters = self.waiters
+ self.waiters = None
+ for waiter in waiters:
+ if not waiter.done():
+ waiter.set_result(waiter)
+
+ @tasks.coroutine
+ def wait_closed(self):
+ if self.sockets is None or self.waiters is None:
+ return
+ waiter = futures.Future(loop=self.loop)
+ self.waiters.append(waiter)
+ yield from waiter
+
+
+class BaseEventLoop(events.AbstractEventLoop):
+
+ def __init__(self):
+ self._ready = collections.deque()
+ self._scheduled = []
+ self._default_executor = None
+ self._internal_fds = 0
+ self._running = False
+ self._clock_resolution = time.get_clock_info('monotonic').resolution
+ self._exception_handler = None
+ self._debug = False
+
+ def _make_socket_transport(self, sock, protocol, waiter=None, *,
+ extra=None, server=None):
+ """Create socket transport."""
+ raise NotImplementedError
+
+ def _make_ssl_transport(self, rawsock, protocol, sslcontext, waiter, *,
+ server_side=False, server_hostname=None,
+ extra=None, server=None):
+ """Create SSL transport."""
+ raise NotImplementedError
+
+ def _make_datagram_transport(self, sock, protocol,
+ address=None, extra=None):
+ """Create datagram transport."""
+ raise NotImplementedError
+
+ def _make_read_pipe_transport(self, pipe, protocol, waiter=None,
+ extra=None):
+ """Create read pipe transport."""
+ raise NotImplementedError
+
+ def _make_write_pipe_transport(self, pipe, protocol, waiter=None,
+ extra=None):
+ """Create write pipe transport."""
+ raise NotImplementedError
+
+ @tasks.coroutine
+ def _make_subprocess_transport(self, protocol, args, shell,
+ stdin, stdout, stderr, bufsize,
+ extra=None, **kwargs):
+ """Create subprocess transport."""
+ raise NotImplementedError
+
+ def _read_from_self(self):
+ """XXX"""
+ raise NotImplementedError
+
+ def _write_to_self(self):
+ """XXX"""
+ raise NotImplementedError
+
+ def _process_events(self, event_list):
+ """Process selector events."""
+ raise NotImplementedError
+
+ def run_forever(self):
+ """Run until stop() is called."""
+ if self._running:
+ raise RuntimeError('Event loop is running.')
+ self._running = True
+ try:
+ while True:
+ try:
+ self._run_once()
+ except _StopError:
+ break
+ finally:
+ self._running = False
+
+ def run_until_complete(self, future):
+ """Run until the Future is done.
+
+ If the argument is a coroutine, it is wrapped in a Task.
+
+ XXX TBD: It would be disastrous to call run_until_complete()
+ with the same coroutine twice -- it would wrap it in two
+ different Tasks and that can't be good.
+
+ Return the Future's result, or raise its exception.
+ """
+ future = tasks.async(future, loop=self)
+ future.add_done_callback(_raise_stop_error)
+ self.run_forever()
+ future.remove_done_callback(_raise_stop_error)
+ if not future.done():
+ raise RuntimeError('Event loop stopped before Future completed.')
+
+ return future.result()
+
+ def stop(self):
+ """Stop running the event loop.
+
+ Every callback scheduled before stop() is called will run.
+ Callback scheduled after stop() is called won't. However,
+ those callbacks will run if run() is called again later.
+ """
+ self.call_soon(_raise_stop_error)
+
+ def close(self):
+ """Close the event loop.
+
+ This clears the queues and shuts down the executor,
+ but does not wait for the executor to finish.
+ """
+ self._ready.clear()
+ self._scheduled.clear()
+ executor = self._default_executor
+ if executor is not None:
+ self._default_executor = None
+ executor.shutdown(wait=False)
+
+ def is_running(self):
+ """Returns running status of event loop."""
+ return self._running
+
+ def time(self):
+ """Return the time according to the event loop's clock."""
+ return time.monotonic()
+
+ def call_later(self, delay, callback, *args):
+ """Arrange for a callback to be called at a given time.
+
+ Return a Handle: an opaque object with a cancel() method that
+ can be used to cancel the call.
+
+ The delay can be an int or float, expressed in seconds. It is
+ always a relative time.
+
+ Each callback will be called exactly once. If two callbacks
+ are scheduled for exactly the same time, it undefined which
+ will be called first.
+
+ Any positional arguments after the callback will be passed to
+ the callback when it is called.
+ """
+ return self.call_at(self.time() + delay, callback, *args)
+
+ def call_at(self, when, callback, *args):
+ """Like call_later(), but uses an absolute time."""
+ if tasks.iscoroutinefunction(callback):
+ raise TypeError("coroutines cannot be used with call_at()")
+ if self._debug:
+ self._assert_is_current_event_loop()
+ timer = events.TimerHandle(when, callback, args, self)
+ heapq.heappush(self._scheduled, timer)
+ return timer
+
+ def call_soon(self, callback, *args):
+ """Arrange for a callback to be called as soon as possible.
+
+ This operates as a FIFO queue, callbacks are called in the
+ order in which they are registered. Each callback will be
+ called exactly once.
+
+ Any positional arguments after the callback will be passed to
+ the callback when it is called.
+ """
+ return self._call_soon(callback, args, check_loop=True)
+
+ def _call_soon(self, callback, args, check_loop):
+ if tasks.iscoroutinefunction(callback):
+ raise TypeError("coroutines cannot be used with call_soon()")
+ if self._debug and check_loop:
+ self._assert_is_current_event_loop()
+ handle = events.Handle(callback, args, self)
+ self._ready.append(handle)
+ return handle
+
+ def _assert_is_current_event_loop(self):
+ """Asserts that this event loop is the current event loop.
+
+ Non-threadsafe methods of this class make this assumption and will
+ likely behave incorrectly when the assumption is violated.
+
+ Should only be called when (self._debug == True). The caller is
+ responsible for checking this condition for performance reasons.
+ """
+ if events.get_event_loop() is not self:
+ raise RuntimeError(
+ "non-threadsafe operation invoked on an event loop other "
+ "than the current one")
+
+ def call_soon_threadsafe(self, callback, *args):
+ """XXX"""
+ handle = self._call_soon(callback, args, check_loop=False)
+ self._write_to_self()
+ return handle
+
+ def run_in_executor(self, executor, callback, *args):
+ if tasks.iscoroutinefunction(callback):
+ raise TypeError("coroutines cannot be used with run_in_executor()")
+ if isinstance(callback, events.Handle):
+ assert not args
+ assert not isinstance(callback, events.TimerHandle)
+ if callback._cancelled:
+ f = futures.Future(loop=self)
+ f.set_result(None)
+ return f
+ callback, args = callback._callback, callback._args
+ if executor is None:
+ executor = self._default_executor
+ if executor is None:
+ executor = concurrent.futures.ThreadPoolExecutor(_MAX_WORKERS)
+ self._default_executor = executor
+ return futures.wrap_future(executor.submit(callback, *args), loop=self)
+
+ def set_default_executor(self, executor):
+ self._default_executor = executor
+
+ def getaddrinfo(self, host, port, *,
+ family=0, type=0, proto=0, flags=0):
+ return self.run_in_executor(None, socket.getaddrinfo,
+ host, port, family, type, proto, flags)
+
+ def getnameinfo(self, sockaddr, flags=0):
+ return self.run_in_executor(None, socket.getnameinfo, sockaddr, flags)
+
+ @tasks.coroutine
+ def create_connection(self, protocol_factory, host=None, port=None, *,
+ ssl=None, family=0, proto=0, flags=0, sock=None,
+ local_addr=None, server_hostname=None):
+ """XXX"""
+ if server_hostname is not None and not ssl:
+ raise ValueError('server_hostname is only meaningful with ssl')
+
+ if server_hostname is None and ssl:
+ # Use host as default for server_hostname. It is an error
+ # if host is empty or not set, e.g. when an
+ # already-connected socket was passed or when only a port
+ # is given. To avoid this error, you can pass
+ # server_hostname='' -- this will bypass the hostname
+ # check. (This also means that if host is a numeric
+ # IP/IPv6 address, we will attempt to verify that exact
+ # address; this will probably fail, but it is possible to
+ # create a certificate for a specific IP address, so we
+ # don't judge it here.)
+ if not host:
+ raise ValueError('You must set server_hostname '
+ 'when using ssl without a host')
+ server_hostname = host
+
+ if host is not None or port is not None:
+ if sock is not None:
+ raise ValueError(
+ 'host/port and sock can not be specified at the same time')
+
+ f1 = self.getaddrinfo(
+ host, port, family=family,
+ type=socket.SOCK_STREAM, proto=proto, flags=flags)
+ fs = [f1]
+ if local_addr is not None:
+ f2 = self.getaddrinfo(
+ *local_addr, family=family,
+ type=socket.SOCK_STREAM, proto=proto, flags=flags)
+ fs.append(f2)
+ else:
+ f2 = None
+
+ yield from tasks.wait(fs, loop=self)
+
+ infos = f1.result()
+ if not infos:
+ raise OSError('getaddrinfo() returned empty list')
+ if f2 is not None:
+ laddr_infos = f2.result()
+ if not laddr_infos:
+ raise OSError('getaddrinfo() returned empty list')
+
+ exceptions = []
+ for family, type, proto, cname, address in infos:
+ try:
+ sock = socket.socket(family=family, type=type, proto=proto)
+ sock.setblocking(False)
+ if f2 is not None:
+ for _, _, _, _, laddr in laddr_infos:
+ try:
+ sock.bind(laddr)
+ break
+ except OSError as exc:
+ exc = OSError(
+ exc.errno, 'error while '
+ 'attempting to bind on address '
+ '{!r}: {}'.format(
+ laddr, exc.strerror.lower()))
+ exceptions.append(exc)
+ else:
+ sock.close()
+ sock = None
+ continue
+ yield from self.sock_connect(sock, address)
+ except OSError as exc:
+ if sock is not None:
+ sock.close()
+ exceptions.append(exc)
+ else:
+ break
+ else:
+ if len(exceptions) == 1:
+ raise exceptions[0]
+ else:
+ # If they all have the same str(), raise one.
+ model = str(exceptions[0])
+ if all(str(exc) == model for exc in exceptions):
+ raise exceptions[0]
+ # Raise a combined exception so the user can see all
+ # the various error messages.
+ raise OSError('Multiple exceptions: {}'.format(
+ ', '.join(str(exc) for exc in exceptions)))
+
+ elif sock is None:
+ raise ValueError(
+ 'host and port was not specified and no sock specified')
+
+ sock.setblocking(False)
+
+ transport, protocol = yield from self._create_connection_transport(
+ sock, protocol_factory, ssl, server_hostname)
+ return transport, protocol
+
+ @tasks.coroutine
+ def _create_connection_transport(self, sock, protocol_factory, ssl,
+ server_hostname):
+ protocol = protocol_factory()
+ waiter = futures.Future(loop=self)
+ if ssl:
+ sslcontext = None if isinstance(ssl, bool) else ssl
+ transport = self._make_ssl_transport(
+ sock, protocol, sslcontext, waiter,
+ server_side=False, server_hostname=server_hostname)
+ else:
+ transport = self._make_socket_transport(sock, protocol, waiter)
+
+ yield from waiter
+ return transport, protocol
+
+ @tasks.coroutine
+ def create_datagram_endpoint(self, protocol_factory,
+ local_addr=None, remote_addr=None, *,
+ family=0, proto=0, flags=0):
+ """Create datagram connection."""
+ if not (local_addr or remote_addr):
+ if family == 0:
+ raise ValueError('unexpected address family')
+ addr_pairs_info = (((family, proto), (None, None)),)
+ else:
+ # join addresss by (family, protocol)
+ addr_infos = collections.OrderedDict()
+ for idx, addr in ((0, local_addr), (1, remote_addr)):
+ if addr is not None:
+ assert isinstance(addr, tuple) and len(addr) == 2, (
+ '2-tuple is expected')
+
+ infos = yield from self.getaddrinfo(
+ *addr, family=family, type=socket.SOCK_DGRAM,
+ proto=proto, flags=flags)
+ if not infos:
+ raise OSError('getaddrinfo() returned empty list')
+
+ for fam, _, pro, _, address in infos:
+ key = (fam, pro)
+ if key not in addr_infos:
+ addr_infos[key] = [None, None]
+ addr_infos[key][idx] = address
+
+ # each addr has to have info for each (family, proto) pair
+ addr_pairs_info = [
+ (key, addr_pair) for key, addr_pair in addr_infos.items()
+ if not ((local_addr and addr_pair[0] is None) or
+ (remote_addr and addr_pair[1] is None))]
+
+ if not addr_pairs_info:
+ raise ValueError('can not get address information')
+
+ exceptions = []
+
+ for ((family, proto),
+ (local_address, remote_address)) in addr_pairs_info:
+ sock = None
+ r_addr = None
+ try:
+ sock = socket.socket(
+ family=family, type=socket.SOCK_DGRAM, proto=proto)
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ sock.setblocking(False)
+
+ if local_addr:
+ sock.bind(local_address)
+ if remote_addr:
+ yield from self.sock_connect(sock, remote_address)
+ r_addr = remote_address
+ except OSError as exc:
+ if sock is not None:
+ sock.close()
+ exceptions.append(exc)
+ else:
+ break
+ else:
+ raise exceptions[0]
+
+ protocol = protocol_factory()
+ transport = self._make_datagram_transport(sock, protocol, r_addr)
+ return transport, protocol
+
+ @tasks.coroutine
+ def create_server(self, protocol_factory, host=None, port=None,
+ *,
+ family=socket.AF_UNSPEC,
+ flags=socket.AI_PASSIVE,
+ sock=None,
+ backlog=100,
+ ssl=None,
+ reuse_address=None):
+ """XXX"""
+ if isinstance(ssl, bool):
+ raise TypeError('ssl argument must be an SSLContext or None')
+ if host is not None or port is not None:
+ if sock is not None:
+ raise ValueError(
+ 'host/port and sock can not be specified at the same time')
+
+ AF_INET6 = getattr(socket, 'AF_INET6', 0)
+ if reuse_address is None:
+ reuse_address = os.name == 'posix' and sys.platform != 'cygwin'
+ sockets = []
+ if host == '':
+ host = None
+
+ infos = yield from self.getaddrinfo(
+ host, port, family=family,
+ type=socket.SOCK_STREAM, proto=0, flags=flags)
+ if not infos:
+ raise OSError('getaddrinfo() returned empty list')
+
+ completed = False
+ try:
+ for res in infos:
+ af, socktype, proto, canonname, sa = res
+ try:
+ sock = socket.socket(af, socktype, proto)
+ except socket.error:
+ # Assume it's a bad family/type/protocol combination.
+ continue
+ sockets.append(sock)
+ if reuse_address:
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
+ True)
+ # Disable IPv4/IPv6 dual stack support (enabled by
+ # default on Linux) which makes a single socket
+ # listen on both address families.
+ if af == AF_INET6 and hasattr(socket, 'IPPROTO_IPV6'):
+ sock.setsockopt(socket.IPPROTO_IPV6,
+ socket.IPV6_V6ONLY,
+ True)
+ try:
+ sock.bind(sa)
+ except OSError as err:
+ raise OSError(err.errno, 'error while attempting '
+ 'to bind on address %r: %s'
+ % (sa, err.strerror.lower()))
+ completed = True
+ finally:
+ if not completed:
+ for sock in sockets:
+ sock.close()
+ else:
+ if sock is None:
+ raise ValueError(
+ 'host and port was not specified and no sock specified')
+ sockets = [sock]
+
+ server = Server(self, sockets)
+ for sock in sockets:
+ sock.listen(backlog)
+ sock.setblocking(False)
+ self._start_serving(protocol_factory, sock, ssl, server)
+ return server
+
+ @tasks.coroutine
+ def connect_read_pipe(self, protocol_factory, pipe):
+ protocol = protocol_factory()
+ waiter = futures.Future(loop=self)
+ transport = self._make_read_pipe_transport(pipe, protocol, waiter)
+ yield from waiter
+ return transport, protocol
+
+ @tasks.coroutine
+ def connect_write_pipe(self, protocol_factory, pipe):
+ protocol = protocol_factory()
+ waiter = futures.Future(loop=self)
+ transport = self._make_write_pipe_transport(pipe, protocol, waiter)
+ yield from waiter
+ return transport, protocol
+
+ @tasks.coroutine
+ def subprocess_shell(self, protocol_factory, cmd, *, stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ universal_newlines=False, shell=True, bufsize=0,
+ **kwargs):
+ if not isinstance(cmd, (bytes, str)):
+ raise ValueError("cmd must be a string")
+ if universal_newlines:
+ raise ValueError("universal_newlines must be False")
+ if not shell:
+ raise ValueError("shell must be True")
+ if bufsize != 0:
+ raise ValueError("bufsize must be 0")
+ protocol = protocol_factory()
+ transport = yield from self._make_subprocess_transport(
+ protocol, cmd, True, stdin, stdout, stderr, bufsize, **kwargs)
+ return transport, protocol
+
+ @tasks.coroutine
+ def subprocess_exec(self, protocol_factory, program, *args,
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE, universal_newlines=False,
+ shell=False, bufsize=0, **kwargs):
+ if universal_newlines:
+ raise ValueError("universal_newlines must be False")
+ if shell:
+ raise ValueError("shell must be False")
+ if bufsize != 0:
+ raise ValueError("bufsize must be 0")
+ popen_args = (program,) + args
+ for arg in popen_args:
+ if not isinstance(arg, (str, bytes)):
+ raise TypeError("program arguments must be "
+ "a bytes or text string, not %s"
+ % type(arg).__name__)
+ protocol = protocol_factory()
+ transport = yield from self._make_subprocess_transport(
+ protocol, popen_args, False, stdin, stdout, stderr,
+ bufsize, **kwargs)
+ return transport, protocol
+
+ def set_exception_handler(self, handler):
+ """Set handler as the new event loop exception handler.
+
+ If handler is None, the default exception handler will
+ be set.
+
+ If handler is a callable object, it should have a
+ matching signature to '(loop, context)', where 'loop'
+ will be a reference to the active event loop, 'context'
+ will be a dict object (see `call_exception_handler()`
+ documentation for details about context).
+ """
+ if handler is not None and not callable(handler):
+ raise TypeError('A callable object or None is expected, '
+ 'got {!r}'.format(handler))
+ self._exception_handler = handler
+
+ def default_exception_handler(self, context):
+ """Default exception handler.
+
+ This is called when an exception occurs and no exception
+ handler is set, and can be called by a custom exception
+ handler that wants to defer to the default behavior.
+
+ context parameter has the same meaning as in
+ `call_exception_handler()`.
+ """
+ message = context.get('message')
+ if not message:
+ message = 'Unhandled exception in event loop'
+
+ exception = context.get('exception')
+ if exception is not None:
+ exc_info = (type(exception), exception, exception.__traceback__)
+ else:
+ exc_info = False
+
+ log_lines = [message]
+ for key in sorted(context):
+ if key in {'message', 'exception'}:
+ continue
+ log_lines.append('{}: {!r}'.format(key, context[key]))
+
+ logger.error('\n'.join(log_lines), exc_info=exc_info)
+
+ def call_exception_handler(self, context):
+ """Call the current event loop exception handler.
+
+ context is a dict object containing the following keys
+ (new keys maybe introduced later):
+ - 'message': Error message;
+ - 'exception' (optional): Exception object;
+ - 'future' (optional): Future instance;
+ - 'handle' (optional): Handle instance;
+ - 'protocol' (optional): Protocol instance;
+ - 'transport' (optional): Transport instance;
+ - 'socket' (optional): Socket instance.
+
+ Note: this method should not be overloaded in subclassed
+ event loops. For any custom exception handling, use
+ `set_exception_handler()` method.
+ """
+ if self._exception_handler is None:
+ try:
+ self.default_exception_handler(context)
+ except Exception:
+ # Second protection layer for unexpected errors
+ # in the default implementation, as well as for subclassed
+ # event loops with overloaded "default_exception_handler".
+ logger.error('Exception in default exception handler',
+ exc_info=True)
+ else:
+ try:
+ self._exception_handler(self, context)
+ except Exception as exc:
+ # Exception in the user set custom exception handler.
+ try:
+ # Let's try default handler.
+ self.default_exception_handler({
+ 'message': 'Unhandled error in exception handler',
+ 'exception': exc,
+ 'context': context,
+ })
+ except Exception:
+ # Guard 'default_exception_handler' in case it's
+ # overloaded.
+ logger.error('Exception in default exception handler '
+ 'while handling an unexpected error '
+ 'in custom exception handler',
+ exc_info=True)
+
+ def _add_callback(self, handle):
+ """Add a Handle to ready or scheduled."""
+ assert isinstance(handle, events.Handle), 'A Handle is required here'
+ if handle._cancelled:
+ return
+ if isinstance(handle, events.TimerHandle):
+ heapq.heappush(self._scheduled, handle)
+ else:
+ self._ready.append(handle)
+
+ def _add_callback_signalsafe(self, handle):
+ """Like _add_callback() but called from a signal handler."""
+ self._add_callback(handle)
+ self._write_to_self()
+
+ def _run_once(self):
+ """Run one full iteration of the event loop.
+
+ This calls all currently ready callbacks, polls for I/O,
+ schedules the resulting callbacks, and finally schedules
+ 'call_later' callbacks.
+ """
+ # Remove delayed calls that were cancelled from head of queue.
+ while self._scheduled and self._scheduled[0]._cancelled:
+ heapq.heappop(self._scheduled)
+
+ timeout = None
+ if self._ready:
+ timeout = 0
+ elif self._scheduled:
+ # Compute the desired timeout.
+ when = self._scheduled[0]._when
+ deadline = max(0, when - self.time())
+ if timeout is None:
+ timeout = deadline
+ else:
+ timeout = min(timeout, deadline)
+
+ # TODO: Instrumentation only in debug mode?
+ if logger.isEnabledFor(logging.INFO):
+ t0 = self.time()
+ event_list = self._selector.select(timeout)
+ t1 = self.time()
+ if t1-t0 >= 1:
+ level = logging.INFO
+ else:
+ level = logging.DEBUG
+ if timeout is not None:
+ logger.log(level, 'poll %.3f took %.3f seconds',
+ timeout, t1-t0)
+ else:
+ logger.log(level, 'poll took %.3f seconds', t1-t0)
+ else:
+ event_list = self._selector.select(timeout)
+ self._process_events(event_list)
+
+ # Handle 'later' callbacks that are ready.
+ end_time = self.time() + self._clock_resolution
+ while self._scheduled:
+ handle = self._scheduled[0]
+ if handle._when >= end_time:
+ break
+ handle = heapq.heappop(self._scheduled)
+ self._ready.append(handle)
+
+ # This is the only place where callbacks are actually *called*.
+ # All other places just add them to ready.
+ # Note: We run all currently scheduled callbacks, but not any
+ # callbacks scheduled by callbacks run this time around --
+ # they will be run the next time (after another I/O poll).
+ # Use an idiom that is threadsafe without using locks.
+ ntodo = len(self._ready)
+ for i in range(ntodo):
+ handle = self._ready.popleft()
+ if not handle._cancelled:
+ handle._run()
+ handle = None # Needed to break cycles when an exception occurs.
+
+ def get_debug(self):
+ return self._debug
+
+ def set_debug(self, enabled):
+ self._debug = enabled
diff --git a/Lib/asyncio/base_subprocess.py b/Lib/asyncio/base_subprocess.py
new file mode 100644
index 0000000..b78f816
--- /dev/null
+++ b/Lib/asyncio/base_subprocess.py
@@ -0,0 +1,159 @@
+import collections
+import subprocess
+
+from . import protocols
+from . import tasks
+from . import transports
+
+
+class BaseSubprocessTransport(transports.SubprocessTransport):
+
+ def __init__(self, loop, protocol, args, shell,
+ stdin, stdout, stderr, bufsize,
+ extra=None, **kwargs):
+ super().__init__(extra)
+ self._protocol = protocol
+ self._loop = loop
+
+ self._pipes = {}
+ if stdin == subprocess.PIPE:
+ self._pipes[0] = None
+ if stdout == subprocess.PIPE:
+ self._pipes[1] = None
+ if stderr == subprocess.PIPE:
+ self._pipes[2] = None
+ self._pending_calls = collections.deque()
+ self._finished = False
+ self._returncode = None
+ self._start(args=args, shell=shell, stdin=stdin, stdout=stdout,
+ stderr=stderr, bufsize=bufsize, **kwargs)
+ self._extra['subprocess'] = self._proc
+
+ def _start(self, args, shell, stdin, stdout, stderr, bufsize, **kwargs):
+ raise NotImplementedError
+
+ def _make_write_subprocess_pipe_proto(self, fd):
+ raise NotImplementedError
+
+ def _make_read_subprocess_pipe_proto(self, fd):
+ raise NotImplementedError
+
+ def close(self):
+ for proto in self._pipes.values():
+ proto.pipe.close()
+ if self._returncode is None:
+ self.terminate()
+
+ def get_pid(self):
+ return self._proc.pid
+
+ def get_returncode(self):
+ return self._returncode
+
+ def get_pipe_transport(self, fd):
+ if fd in self._pipes:
+ return self._pipes[fd].pipe
+ else:
+ return None
+
+ def send_signal(self, signal):
+ self._proc.send_signal(signal)
+
+ def terminate(self):
+ self._proc.terminate()
+
+ def kill(self):
+ self._proc.kill()
+
+ @tasks.coroutine
+ def _post_init(self):
+ proc = self._proc
+ loop = self._loop
+ if proc.stdin is not None:
+ _, pipe = yield from loop.connect_write_pipe(
+ lambda: WriteSubprocessPipeProto(self, 0),
+ proc.stdin)
+ self._pipes[0] = pipe
+ if proc.stdout is not None:
+ _, pipe = yield from loop.connect_read_pipe(
+ lambda: ReadSubprocessPipeProto(self, 1),
+ proc.stdout)
+ self._pipes[1] = pipe
+ if proc.stderr is not None:
+ _, pipe = yield from loop.connect_read_pipe(
+ lambda: ReadSubprocessPipeProto(self, 2),
+ proc.stderr)
+ self._pipes[2] = pipe
+
+ assert self._pending_calls is not None
+
+ self._loop.call_soon(self._protocol.connection_made, self)
+ for callback, data in self._pending_calls:
+ self._loop.call_soon(callback, *data)
+ self._pending_calls = None
+
+ def _call(self, cb, *data):
+ if self._pending_calls is not None:
+ self._pending_calls.append((cb, data))
+ else:
+ self._loop.call_soon(cb, *data)
+
+ def _pipe_connection_lost(self, fd, exc):
+ self._call(self._protocol.pipe_connection_lost, fd, exc)
+ self._try_finish()
+
+ def _pipe_data_received(self, fd, data):
+ self._call(self._protocol.pipe_data_received, fd, data)
+
+ def _process_exited(self, returncode):
+ assert returncode is not None, returncode
+ assert self._returncode is None, self._returncode
+ self._returncode = returncode
+ self._call(self._protocol.process_exited)
+ self._try_finish()
+
+ def _try_finish(self):
+ assert not self._finished
+ if self._returncode is None:
+ return
+ if all(p is not None and p.disconnected
+ for p in self._pipes.values()):
+ self._finished = True
+ self._loop.call_soon(self._call_connection_lost, None)
+
+ def _call_connection_lost(self, exc):
+ try:
+ self._protocol.connection_lost(exc)
+ finally:
+ self._proc = None
+ self._protocol = None
+ self._loop = None
+
+
+class WriteSubprocessPipeProto(protocols.BaseProtocol):
+
+ def __init__(self, proc, fd):
+ self.proc = proc
+ self.fd = fd
+ self.pipe = None
+ self.disconnected = False
+
+ def connection_made(self, transport):
+ self.pipe = transport
+
+ def connection_lost(self, exc):
+ self.disconnected = True
+ self.proc._pipe_connection_lost(self.fd, exc)
+
+ def pause_writing(self):
+ self.proc._protocol.pause_writing()
+
+ def resume_writing(self):
+ self.proc._protocol.resume_writing()
+
+
+class ReadSubprocessPipeProto(WriteSubprocessPipeProto,
+ protocols.Protocol):
+
+ def data_received(self, data):
+ self.proc._pipe_data_received(self.fd, data)
diff --git a/Lib/asyncio/constants.py b/Lib/asyncio/constants.py
new file mode 100644
index 0000000..f9e1232
--- /dev/null
+++ b/Lib/asyncio/constants.py
@@ -0,0 +1,7 @@
+"""Constants."""
+
+# After the connection is lost, log warnings after this many write()s.
+LOG_THRESHOLD_FOR_CONNLOST_WRITES = 5
+
+# Seconds to wait before retrying accept().
+ACCEPT_RETRY_DELAY = 1
diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py
new file mode 100644
index 0000000..31592d1
--- /dev/null
+++ b/Lib/asyncio/events.py
@@ -0,0 +1,485 @@
+"""Event loop and event loop policy."""
+
+__all__ = ['AbstractEventLoopPolicy',
+ 'AbstractEventLoop', 'AbstractServer',
+ 'Handle', 'TimerHandle',
+ 'get_event_loop_policy', 'set_event_loop_policy',
+ 'get_event_loop', 'set_event_loop', 'new_event_loop',
+ 'get_child_watcher', 'set_child_watcher',
+ ]
+
+import subprocess
+import threading
+import socket
+
+
+class Handle:
+ """Object returned by callback registration methods."""
+
+ __slots__ = ['_callback', '_args', '_cancelled', '_loop', '__weakref__']
+
+ def __init__(self, callback, args, loop):
+ assert not isinstance(callback, Handle), 'A Handle is not a callback'
+ self._loop = loop
+ self._callback = callback
+ self._args = args
+ self._cancelled = False
+
+ def __repr__(self):
+ res = 'Handle({}, {})'.format(self._callback, self._args)
+ if self._cancelled:
+ res += '<cancelled>'
+ return res
+
+ def cancel(self):
+ self._cancelled = True
+
+ def _run(self):
+ try:
+ self._callback(*self._args)
+ except Exception as exc:
+ msg = 'Exception in callback {}{!r}'.format(self._callback,
+ self._args)
+ self._loop.call_exception_handler({
+ 'message': msg,
+ 'exception': exc,
+ 'handle': self,
+ })
+ self = None # Needed to break cycles when an exception occurs.
+
+
+class TimerHandle(Handle):
+ """Object returned by timed callback registration methods."""
+
+ __slots__ = ['_when']
+
+ def __init__(self, when, callback, args, loop):
+ assert when is not None
+ super().__init__(callback, args, loop)
+
+ self._when = when
+
+ def __repr__(self):
+ res = 'TimerHandle({}, {}, {})'.format(self._when,
+ self._callback,
+ self._args)
+ if self._cancelled:
+ res += '<cancelled>'
+
+ return res
+
+ def __hash__(self):
+ return hash(self._when)
+
+ def __lt__(self, other):
+ return self._when < other._when
+
+ def __le__(self, other):
+ if self._when < other._when:
+ return True
+ return self.__eq__(other)
+
+ def __gt__(self, other):
+ return self._when > other._when
+
+ def __ge__(self, other):
+ if self._when > other._when:
+ return True
+ return self.__eq__(other)
+
+ def __eq__(self, other):
+ if isinstance(other, TimerHandle):
+ return (self._when == other._when and
+ self._callback == other._callback and
+ self._args == other._args and
+ self._cancelled == other._cancelled)
+ return NotImplemented
+
+ def __ne__(self, other):
+ equal = self.__eq__(other)
+ return NotImplemented if equal is NotImplemented else not equal
+
+
+class AbstractServer:
+ """Abstract server returned by create_server()."""
+
+ def close(self):
+ """Stop serving. This leaves existing connections open."""
+ return NotImplemented
+
+ def wait_closed(self):
+ """Coroutine to wait until service is closed."""
+ return NotImplemented
+
+
+class AbstractEventLoop:
+ """Abstract event loop."""
+
+ # Running and stopping the event loop.
+
+ def run_forever(self):
+ """Run the event loop until stop() is called."""
+ raise NotImplementedError
+
+ def run_until_complete(self, future):
+ """Run the event loop until a Future is done.
+
+ Return the Future's result, or raise its exception.
+ """
+ raise NotImplementedError
+
+ def stop(self):
+ """Stop the event loop as soon as reasonable.
+
+ Exactly how soon that is may depend on the implementation, but
+ no more I/O callbacks should be scheduled.
+ """
+ raise NotImplementedError
+
+ def is_running(self):
+ """Return whether the event loop is currently running."""
+ raise NotImplementedError
+
+ def close(self):
+ """Close the loop.
+
+ The loop should not be running.
+
+ This is idempotent and irreversible.
+
+ No other methods should be called after this one.
+ """
+ raise NotImplementedError
+
+ # Methods scheduling callbacks. All these return Handles.
+
+ def call_soon(self, callback, *args):
+ return self.call_later(0, callback, *args)
+
+ def call_later(self, delay, callback, *args):
+ raise NotImplementedError
+
+ def call_at(self, when, callback, *args):
+ raise NotImplementedError
+
+ def time(self):
+ raise NotImplementedError
+
+ # Methods for interacting with threads.
+
+ def call_soon_threadsafe(self, callback, *args):
+ raise NotImplementedError
+
+ def run_in_executor(self, executor, callback, *args):
+ raise NotImplementedError
+
+ def set_default_executor(self, executor):
+ raise NotImplementedError
+
+ # Network I/O methods returning Futures.
+
+ def getaddrinfo(self, host, port, *, family=0, type=0, proto=0, flags=0):
+ raise NotImplementedError
+
+ def getnameinfo(self, sockaddr, flags=0):
+ raise NotImplementedError
+
+ def create_connection(self, protocol_factory, host=None, port=None, *,
+ ssl=None, family=0, proto=0, flags=0, sock=None,
+ local_addr=None, server_hostname=None):
+ raise NotImplementedError
+
+ def create_server(self, protocol_factory, host=None, port=None, *,
+ family=socket.AF_UNSPEC, flags=socket.AI_PASSIVE,
+ sock=None, backlog=100, ssl=None, reuse_address=None):
+ """A coroutine which creates a TCP server bound to host and port.
+
+ The return value is a Server object which can be used to stop
+ the service.
+
+ If host is an empty string or None all interfaces are assumed
+ and a list of multiple sockets will be returned (most likely
+ one for IPv4 and another one for IPv6).
+
+ family can be set to either AF_INET or AF_INET6 to force the
+ socket to use IPv4 or IPv6. If not set it will be determined
+ from host (defaults to AF_UNSPEC).
+
+ flags is a bitmask for getaddrinfo().
+
+ sock can optionally be specified in order to use a preexisting
+ socket object.
+
+ backlog is the maximum number of queued connections passed to
+ listen() (defaults to 100).
+
+ ssl can be set to an SSLContext to enable SSL over the
+ accepted connections.
+
+ reuse_address tells the kernel to reuse a local socket in
+ TIME_WAIT state, without waiting for its natural timeout to
+ expire. If not specified will automatically be set to True on
+ UNIX.
+ """
+ raise NotImplementedError
+
+ def create_unix_connection(self, protocol_factory, path, *,
+ ssl=None, sock=None,
+ server_hostname=None):
+ raise NotImplementedError
+
+ def create_unix_server(self, protocol_factory, path, *,
+ sock=None, backlog=100, ssl=None):
+ """A coroutine which creates a UNIX Domain Socket server.
+
+ The return value is a Server object, which can be used to stop
+ the service.
+
+ path is a str, representing a file systsem path to bind the
+ server socket to.
+
+ sock can optionally be specified in order to use a preexisting
+ socket object.
+
+ backlog is the maximum number of queued connections passed to
+ listen() (defaults to 100).
+
+ ssl can be set to an SSLContext to enable SSL over the
+ accepted connections.
+ """
+ raise NotImplementedError
+
+ def create_datagram_endpoint(self, protocol_factory,
+ local_addr=None, remote_addr=None, *,
+ family=0, proto=0, flags=0):
+ raise NotImplementedError
+
+ # Pipes and subprocesses.
+
+ def connect_read_pipe(self, protocol_factory, pipe):
+ """Register read pipe in event loop.
+
+ protocol_factory should instantiate object with Protocol interface.
+ pipe is file-like object already switched to nonblocking.
+ Return pair (transport, protocol), where transport support
+ ReadTransport interface."""
+ # The reason to accept file-like object instead of just file descriptor
+ # is: we need to own pipe and close it at transport finishing
+ # Can got complicated errors if pass f.fileno(),
+ # close fd in pipe transport then close f and vise versa.
+ raise NotImplementedError
+
+ def connect_write_pipe(self, protocol_factory, pipe):
+ """Register write pipe in event loop.
+
+ protocol_factory should instantiate object with BaseProtocol interface.
+ Pipe is file-like object already switched to nonblocking.
+ Return pair (transport, protocol), where transport support
+ WriteTransport interface."""
+ # The reason to accept file-like object instead of just file descriptor
+ # is: we need to own pipe and close it at transport finishing
+ # Can got complicated errors if pass f.fileno(),
+ # close fd in pipe transport then close f and vise versa.
+ raise NotImplementedError
+
+ def subprocess_shell(self, protocol_factory, cmd, *, stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ **kwargs):
+ raise NotImplementedError
+
+ def subprocess_exec(self, protocol_factory, *args, stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ **kwargs):
+ raise NotImplementedError
+
+ # Ready-based callback registration methods.
+ # The add_*() methods return None.
+ # The remove_*() methods return True if something was removed,
+ # False if there was nothing to delete.
+
+ def add_reader(self, fd, callback, *args):
+ raise NotImplementedError
+
+ def remove_reader(self, fd):
+ raise NotImplementedError
+
+ def add_writer(self, fd, callback, *args):
+ raise NotImplementedError
+
+ def remove_writer(self, fd):
+ raise NotImplementedError
+
+ # Completion based I/O methods returning Futures.
+
+ def sock_recv(self, sock, nbytes):
+ raise NotImplementedError
+
+ def sock_sendall(self, sock, data):
+ raise NotImplementedError
+
+ def sock_connect(self, sock, address):
+ raise NotImplementedError
+
+ def sock_accept(self, sock):
+ raise NotImplementedError
+
+ # Signal handling.
+
+ def add_signal_handler(self, sig, callback, *args):
+ raise NotImplementedError
+
+ def remove_signal_handler(self, sig):
+ raise NotImplementedError
+
+ # Error handlers.
+
+ def set_exception_handler(self, handler):
+ raise NotImplementedError
+
+ def default_exception_handler(self, context):
+ raise NotImplementedError
+
+ def call_exception_handler(self, context):
+ raise NotImplementedError
+
+ # Debug flag management.
+
+ def get_debug(self):
+ raise NotImplementedError
+
+ def set_debug(self, enabled):
+ raise NotImplementedError
+
+
+class AbstractEventLoopPolicy:
+ """Abstract policy for accessing the event loop."""
+
+ def get_event_loop(self):
+ """XXX"""
+ raise NotImplementedError
+
+ def set_event_loop(self, loop):
+ """XXX"""
+ raise NotImplementedError
+
+ def new_event_loop(self):
+ """XXX"""
+ raise NotImplementedError
+
+ # Child processes handling (Unix only).
+
+ def get_child_watcher(self):
+ """XXX"""
+ raise NotImplementedError
+
+ def set_child_watcher(self, watcher):
+ """XXX"""
+ raise NotImplementedError
+
+
+class BaseDefaultEventLoopPolicy(AbstractEventLoopPolicy):
+ """Default policy implementation for accessing the event loop.
+
+ In this policy, each thread has its own event loop. However, we
+ only automatically create an event loop by default for the main
+ thread; other threads by default have no event loop.
+
+ Other policies may have different rules (e.g. a single global
+ event loop, or automatically creating an event loop per thread, or
+ using some other notion of context to which an event loop is
+ associated).
+ """
+
+ _loop_factory = None
+
+ class _Local(threading.local):
+ _loop = None
+ _set_called = False
+
+ def __init__(self):
+ self._local = self._Local()
+
+ def get_event_loop(self):
+ """Get the event loop.
+
+ This may be None or an instance of EventLoop.
+ """
+ if (self._local._loop is None and
+ not self._local._set_called and
+ isinstance(threading.current_thread(), threading._MainThread)):
+ self.set_event_loop(self.new_event_loop())
+ assert self._local._loop is not None, \
+ ('There is no current event loop in thread %r.' %
+ threading.current_thread().name)
+ return self._local._loop
+
+ def set_event_loop(self, loop):
+ """Set the event loop."""
+ self._local._set_called = True
+ assert loop is None or isinstance(loop, AbstractEventLoop)
+ self._local._loop = loop
+
+ def new_event_loop(self):
+ """Create a new event loop.
+
+ You must call set_event_loop() to make this the current event
+ loop.
+ """
+ return self._loop_factory()
+
+
+# Event loop policy. The policy itself is always global, even if the
+# policy's rules say that there is an event loop per thread (or other
+# notion of context). The default policy is installed by the first
+# call to get_event_loop_policy().
+_event_loop_policy = None
+
+# Lock for protecting the on-the-fly creation of the event loop policy.
+_lock = threading.Lock()
+
+
+def _init_event_loop_policy():
+ global _event_loop_policy
+ with _lock:
+ if _event_loop_policy is None: # pragma: no branch
+ from . import DefaultEventLoopPolicy
+ _event_loop_policy = DefaultEventLoopPolicy()
+
+
+def get_event_loop_policy():
+ """XXX"""
+ if _event_loop_policy is None:
+ _init_event_loop_policy()
+ return _event_loop_policy
+
+
+def set_event_loop_policy(policy):
+ """XXX"""
+ global _event_loop_policy
+ assert policy is None or isinstance(policy, AbstractEventLoopPolicy)
+ _event_loop_policy = policy
+
+
+def get_event_loop():
+ """XXX"""
+ return get_event_loop_policy().get_event_loop()
+
+
+def set_event_loop(loop):
+ """XXX"""
+ get_event_loop_policy().set_event_loop(loop)
+
+
+def new_event_loop():
+ """XXX"""
+ return get_event_loop_policy().new_event_loop()
+
+
+def get_child_watcher():
+ """XXX"""
+ return get_event_loop_policy().get_child_watcher()
+
+
+def set_child_watcher(watcher):
+ """XXX"""
+ return get_event_loop_policy().set_child_watcher(watcher)
diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py
new file mode 100644
index 0000000..91ea170
--- /dev/null
+++ b/Lib/asyncio/futures.py
@@ -0,0 +1,371 @@
+"""A Future class similar to the one in PEP 3148."""
+
+__all__ = ['CancelledError', 'TimeoutError',
+ 'InvalidStateError',
+ 'Future', 'wrap_future',
+ ]
+
+import concurrent.futures._base
+import logging
+import sys
+import traceback
+
+from . import events
+
+# States for Future.
+_PENDING = 'PENDING'
+_CANCELLED = 'CANCELLED'
+_FINISHED = 'FINISHED'
+
+_PY34 = sys.version_info >= (3, 4)
+
+# TODO: Do we really want to depend on concurrent.futures internals?
+Error = concurrent.futures._base.Error
+CancelledError = concurrent.futures.CancelledError
+TimeoutError = concurrent.futures.TimeoutError
+
+STACK_DEBUG = logging.DEBUG - 1 # heavy-duty debugging
+
+
+class InvalidStateError(Error):
+ """The operation is not allowed in this state."""
+ # TODO: Show the future, its state, the method, and the required state.
+
+
+class _TracebackLogger:
+ """Helper to log a traceback upon destruction if not cleared.
+
+ This solves a nasty problem with Futures and Tasks that have an
+ exception set: if nobody asks for the exception, the exception is
+ never logged. This violates the Zen of Python: 'Errors should
+ never pass silently. Unless explicitly silenced.'
+
+ However, we don't want to log the exception as soon as
+ set_exception() is called: if the calling code is written
+ properly, it will get the exception and handle it properly. But
+ we *do* want to log it if result() or exception() was never called
+ -- otherwise developers waste a lot of time wondering why their
+ buggy code fails silently.
+
+ An earlier attempt added a __del__() method to the Future class
+ itself, but this backfired because the presence of __del__()
+ prevents garbage collection from breaking cycles. A way out of
+ this catch-22 is to avoid having a __del__() method on the Future
+ class itself, but instead to have a reference to a helper object
+ with a __del__() method that logs the traceback, where we ensure
+ that the helper object doesn't participate in cycles, and only the
+ Future has a reference to it.
+
+ The helper object is added when set_exception() is called. When
+ the Future is collected, and the helper is present, the helper
+ object is also collected, and its __del__() method will log the
+ traceback. When the Future's result() or exception() method is
+ called (and a helper object is present), it removes the the helper
+ object, after calling its clear() method to prevent it from
+ logging.
+
+ One downside is that we do a fair amount of work to extract the
+ traceback from the exception, even when it is never logged. It
+ would seem cheaper to just store the exception object, but that
+ references the traceback, which references stack frames, which may
+ reference the Future, which references the _TracebackLogger, and
+ then the _TracebackLogger would be included in a cycle, which is
+ what we're trying to avoid! As an optimization, we don't
+ immediately format the exception; we only do the work when
+ activate() is called, which call is delayed until after all the
+ Future's callbacks have run. Since usually a Future has at least
+ one callback (typically set by 'yield from') and usually that
+ callback extracts the callback, thereby removing the need to
+ format the exception.
+
+ PS. I don't claim credit for this solution. I first heard of it
+ in a discussion about closing files when they are collected.
+ """
+
+ __slots__ = ['exc', 'tb', 'loop']
+
+ def __init__(self, exc, loop):
+ self.loop = loop
+ self.exc = exc
+ self.tb = None
+
+ def activate(self):
+ exc = self.exc
+ if exc is not None:
+ self.exc = None
+ self.tb = traceback.format_exception(exc.__class__, exc,
+ exc.__traceback__)
+
+ def clear(self):
+ self.exc = None
+ self.tb = None
+
+ def __del__(self):
+ if self.tb:
+ msg = 'Future/Task exception was never retrieved:\n{tb}'
+ context = {
+ 'message': msg.format(tb=''.join(self.tb)),
+ }
+ self.loop.call_exception_handler(context)
+
+
+class Future:
+ """This class is *almost* compatible with concurrent.futures.Future.
+
+ Differences:
+
+ - result() and exception() do not take a timeout argument and
+ raise an exception when the future isn't done yet.
+
+ - Callbacks registered with add_done_callback() are always called
+ via the event loop's call_soon_threadsafe().
+
+ - This class is not compatible with the wait() and as_completed()
+ methods in the concurrent.futures package.
+
+ (In Python 3.4 or later we may be able to unify the implementations.)
+ """
+
+ # Class variables serving as defaults for instance variables.
+ _state = _PENDING
+ _result = None
+ _exception = None
+ _loop = None
+
+ _blocking = False # proper use of future (yield vs yield from)
+
+ _log_traceback = False # Used for Python 3.4 and later
+ _tb_logger = None # Used for Python 3.3 only
+
+ def __init__(self, *, loop=None):
+ """Initialize the future.
+
+ The optional event_loop argument allows to explicitly set the event
+ loop object used by the future. If it's not provided, the future uses
+ the default event loop.
+ """
+ if loop is None:
+ self._loop = events.get_event_loop()
+ else:
+ self._loop = loop
+ self._callbacks = []
+
+ def __repr__(self):
+ res = self.__class__.__name__
+ if self._state == _FINISHED:
+ if self._exception is not None:
+ res += '<exception={!r}>'.format(self._exception)
+ else:
+ res += '<result={!r}>'.format(self._result)
+ elif self._callbacks:
+ size = len(self._callbacks)
+ if size > 2:
+ res += '<{}, [{}, <{} more>, {}]>'.format(
+ self._state, self._callbacks[0],
+ size-2, self._callbacks[-1])
+ else:
+ res += '<{}, {}>'.format(self._state, self._callbacks)
+ else:
+ res += '<{}>'.format(self._state)
+ return res
+
+ if _PY34:
+ def __del__(self):
+ if not self._log_traceback:
+ # set_exception() was not called, or result() or exception()
+ # has consumed the exception
+ return
+ exc = self._exception
+ context = {
+ 'message': 'Future/Task exception was never retrieved',
+ 'exception': exc,
+ 'future': self,
+ }
+ self._loop.call_exception_handler(context)
+
+ def cancel(self):
+ """Cancel the future and schedule callbacks.
+
+ If the future is already done or cancelled, return False. Otherwise,
+ change the future's state to cancelled, schedule the callbacks and
+ return True.
+ """
+ if self._state != _PENDING:
+ return False
+ self._state = _CANCELLED
+ self._schedule_callbacks()
+ return True
+
+ def _schedule_callbacks(self):
+ """Internal: Ask the event loop to call all callbacks.
+
+ The callbacks are scheduled to be called as soon as possible. Also
+ clears the callback list.
+ """
+ callbacks = self._callbacks[:]
+ if not callbacks:
+ return
+
+ self._callbacks[:] = []
+ for callback in callbacks:
+ self._loop.call_soon(callback, self)
+
+ def cancelled(self):
+ """Return True if the future was cancelled."""
+ return self._state == _CANCELLED
+
+ # Don't implement running(); see http://bugs.python.org/issue18699
+
+ def done(self):
+ """Return True if the future is done.
+
+ Done means either that a result / exception are available, or that the
+ future was cancelled.
+ """
+ return self._state != _PENDING
+
+ def result(self):
+ """Return the result this future represents.
+
+ If the future has been cancelled, raises CancelledError. If the
+ future's result isn't yet available, raises InvalidStateError. If
+ the future is done and has an exception set, this exception is raised.
+ """
+ if self._state == _CANCELLED:
+ raise CancelledError
+ if self._state != _FINISHED:
+ raise InvalidStateError('Result is not ready.')
+ self._log_traceback = False
+ if self._tb_logger is not None:
+ self._tb_logger.clear()
+ self._tb_logger = None
+ if self._exception is not None:
+ raise self._exception
+ return self._result
+
+ def exception(self):
+ """Return the exception that was set on this future.
+
+ The exception (or None if no exception was set) is returned only if
+ the future is done. If the future has been cancelled, raises
+ CancelledError. If the future isn't done yet, raises
+ InvalidStateError.
+ """
+ if self._state == _CANCELLED:
+ raise CancelledError
+ if self._state != _FINISHED:
+ raise InvalidStateError('Exception is not set.')
+ self._log_traceback = False
+ if self._tb_logger is not None:
+ self._tb_logger.clear()
+ self._tb_logger = None
+ return self._exception
+
+ def add_done_callback(self, fn):
+ """Add a callback to be run when the future becomes done.
+
+ The callback is called with a single argument - the future object. If
+ the future is already done when this is called, the callback is
+ scheduled with call_soon.
+ """
+ if self._state != _PENDING:
+ self._loop.call_soon(fn, self)
+ else:
+ self._callbacks.append(fn)
+
+ # New method not in PEP 3148.
+
+ def remove_done_callback(self, fn):
+ """Remove all instances of a callback from the "call when done" list.
+
+ Returns the number of callbacks removed.
+ """
+ filtered_callbacks = [f for f in self._callbacks if f != fn]
+ removed_count = len(self._callbacks) - len(filtered_callbacks)
+ if removed_count:
+ self._callbacks[:] = filtered_callbacks
+ return removed_count
+
+ # So-called internal methods (note: no set_running_or_notify_cancel()).
+
+ def set_result(self, result):
+ """Mark the future done and set its result.
+
+ If the future is already done when this method is called, raises
+ InvalidStateError.
+ """
+ if self._state != _PENDING:
+ raise InvalidStateError('{}: {!r}'.format(self._state, self))
+ self._result = result
+ self._state = _FINISHED
+ self._schedule_callbacks()
+
+ def set_exception(self, exception):
+ """Mark the future done and set an exception.
+
+ If the future is already done when this method is called, raises
+ InvalidStateError.
+ """
+ if self._state != _PENDING:
+ raise InvalidStateError('{}: {!r}'.format(self._state, self))
+ if isinstance(exception, type):
+ exception = exception()
+ self._exception = exception
+ self._state = _FINISHED
+ self._schedule_callbacks()
+ if _PY34:
+ self._log_traceback = True
+ else:
+ self._tb_logger = _TracebackLogger(exception, self._loop)
+ # Arrange for the logger to be activated after all callbacks
+ # have had a chance to call result() or exception().
+ self._loop.call_soon(self._tb_logger.activate)
+
+ # Truly internal methods.
+
+ def _copy_state(self, other):
+ """Internal helper to copy state from another Future.
+
+ The other Future may be a concurrent.futures.Future.
+ """
+ assert other.done()
+ if self.cancelled():
+ return
+ assert not self.done()
+ if other.cancelled():
+ self.cancel()
+ else:
+ exception = other.exception()
+ if exception is not None:
+ self.set_exception(exception)
+ else:
+ result = other.result()
+ self.set_result(result)
+
+ def __iter__(self):
+ if not self.done():
+ self._blocking = True
+ yield self # This tells Task to wait for completion.
+ assert self.done(), "yield from wasn't used with future"
+ return self.result() # May raise too.
+
+
+def wrap_future(fut, *, loop=None):
+ """Wrap concurrent.futures.Future object."""
+ if isinstance(fut, Future):
+ return fut
+ assert isinstance(fut, concurrent.futures.Future), \
+ 'concurrent.futures.Future is expected, got {!r}'.format(fut)
+ if loop is None:
+ loop = events.get_event_loop()
+ new_future = Future(loop=loop)
+
+ def _check_cancel_other(f):
+ if f.cancelled():
+ fut.cancel()
+
+ new_future.add_done_callback(_check_cancel_other)
+ fut.add_done_callback(
+ lambda future: loop.call_soon_threadsafe(
+ new_future._copy_state, fut))
+ return new_future
diff --git a/Lib/asyncio/locks.py b/Lib/asyncio/locks.py
new file mode 100644
index 0000000..29c4434
--- /dev/null
+++ b/Lib/asyncio/locks.py
@@ -0,0 +1,466 @@
+"""Synchronization primitives."""
+
+__all__ = ['Lock', 'Event', 'Condition', 'Semaphore', 'BoundedSemaphore']
+
+import collections
+
+from . import events
+from . import futures
+from . import tasks
+
+
+class _ContextManager:
+ """Context manager.
+
+ This enables the following idiom for acquiring and releasing a
+ lock around a block:
+
+ with (yield from lock):
+ <block>
+
+ while failing loudly when accidentally using:
+
+ with lock:
+ <block>
+ """
+
+ def __init__(self, lock):
+ self._lock = lock
+
+ def __enter__(self):
+ # We have no use for the "as ..." clause in the with
+ # statement for locks.
+ return None
+
+ def __exit__(self, *args):
+ try:
+ self._lock.release()
+ finally:
+ self._lock = None # Crudely prevent reuse.
+
+
+class Lock:
+ """Primitive lock objects.
+
+ A primitive lock is a synchronization primitive that is not owned
+ by a particular coroutine when locked. A primitive lock is in one
+ of two states, 'locked' or 'unlocked'.
+
+ It is created in the unlocked state. It has two basic methods,
+ acquire() and release(). When the state is unlocked, acquire()
+ changes the state to locked and returns immediately. When the
+ state is locked, acquire() blocks until a call to release() in
+ another coroutine changes it to unlocked, then the acquire() call
+ resets it to locked and returns. The release() method should only
+ be called in the locked state; it changes the state to unlocked
+ and returns immediately. If an attempt is made to release an
+ unlocked lock, a RuntimeError will be raised.
+
+ When more than one coroutine is blocked in acquire() waiting for
+ the state to turn to unlocked, only one coroutine proceeds when a
+ release() call resets the state to unlocked; first coroutine which
+ is blocked in acquire() is being processed.
+
+ acquire() is a coroutine and should be called with 'yield from'.
+
+ Locks also support the context manager protocol. '(yield from lock)'
+ should be used as context manager expression.
+
+ Usage:
+
+ lock = Lock()
+ ...
+ yield from lock
+ try:
+ ...
+ finally:
+ lock.release()
+
+ Context manager usage:
+
+ lock = Lock()
+ ...
+ with (yield from lock):
+ ...
+
+ Lock objects can be tested for locking state:
+
+ if not lock.locked():
+ yield from lock
+ else:
+ # lock is acquired
+ ...
+
+ """
+
+ def __init__(self, *, loop=None):
+ self._waiters = collections.deque()
+ self._locked = False
+ if loop is not None:
+ self._loop = loop
+ else:
+ self._loop = events.get_event_loop()
+
+ def __repr__(self):
+ res = super().__repr__()
+ extra = 'locked' if self._locked else 'unlocked'
+ if self._waiters:
+ extra = '{},waiters:{}'.format(extra, len(self._waiters))
+ return '<{} [{}]>'.format(res[1:-1], extra)
+
+ def locked(self):
+ """Return True if lock is acquired."""
+ return self._locked
+
+ @tasks.coroutine
+ def acquire(self):
+ """Acquire a lock.
+
+ This method blocks until the lock is unlocked, then sets it to
+ locked and returns True.
+ """
+ if not self._waiters and not self._locked:
+ self._locked = True
+ return True
+
+ fut = futures.Future(loop=self._loop)
+ self._waiters.append(fut)
+ try:
+ yield from fut
+ self._locked = True
+ return True
+ finally:
+ self._waiters.remove(fut)
+
+ def release(self):
+ """Release a lock.
+
+ When the lock is locked, reset it to unlocked, and return.
+ If any other coroutines are blocked waiting for the lock to become
+ unlocked, allow exactly one of them to proceed.
+
+ When invoked on an unlocked lock, a RuntimeError is raised.
+
+ There is no return value.
+ """
+ if self._locked:
+ self._locked = False
+ # Wake up the first waiter who isn't cancelled.
+ for fut in self._waiters:
+ if not fut.done():
+ fut.set_result(True)
+ break
+ else:
+ raise RuntimeError('Lock is not acquired.')
+
+ def __enter__(self):
+ raise RuntimeError(
+ '"yield from" should be used as context manager expression')
+
+ def __exit__(self, *args):
+ # This must exist because __enter__ exists, even though that
+ # always raises; that's how the with-statement works.
+ pass
+
+ def __iter__(self):
+ # This is not a coroutine. It is meant to enable the idiom:
+ #
+ # with (yield from lock):
+ # <block>
+ #
+ # as an alternative to:
+ #
+ # yield from lock.acquire()
+ # try:
+ # <block>
+ # finally:
+ # lock.release()
+ yield from self.acquire()
+ return _ContextManager(self)
+
+
+class Event:
+ """Asynchronous equivalent to threading.Event.
+
+ Class implementing event objects. An event manages a flag that can be set
+ to true with the set() method and reset to false with the clear() method.
+ The wait() method blocks until the flag is true. The flag is initially
+ false.
+ """
+
+ def __init__(self, *, loop=None):
+ self._waiters = collections.deque()
+ self._value = False
+ if loop is not None:
+ self._loop = loop
+ else:
+ self._loop = events.get_event_loop()
+
+ def __repr__(self):
+ res = super().__repr__()
+ extra = 'set' if self._value else 'unset'
+ if self._waiters:
+ extra = '{},waiters:{}'.format(extra, len(self._waiters))
+ return '<{} [{}]>'.format(res[1:-1], extra)
+
+ def is_set(self):
+ """Return True if and only if the internal flag is true."""
+ return self._value
+
+ def set(self):
+ """Set the internal flag to true. All coroutines waiting for it to
+ become true are awakened. Coroutine that call wait() once the flag is
+ true will not block at all.
+ """
+ if not self._value:
+ self._value = True
+
+ for fut in self._waiters:
+ if not fut.done():
+ fut.set_result(True)
+
+ def clear(self):
+ """Reset the internal flag to false. Subsequently, coroutines calling
+ wait() will block until set() is called to set the internal flag
+ to true again."""
+ self._value = False
+
+ @tasks.coroutine
+ def wait(self):
+ """Block until the internal flag is true.
+
+ If the internal flag is true on entry, return True
+ immediately. Otherwise, block until another coroutine calls
+ set() to set the flag to true, then return True.
+ """
+ if self._value:
+ return True
+
+ fut = futures.Future(loop=self._loop)
+ self._waiters.append(fut)
+ try:
+ yield from fut
+ return True
+ finally:
+ self._waiters.remove(fut)
+
+
+class Condition:
+ """Asynchronous equivalent to threading.Condition.
+
+ This class implements condition variable objects. A condition variable
+ allows one or more coroutines to wait until they are notified by another
+ coroutine.
+
+ A new Lock object is created and used as the underlying lock.
+ """
+
+ def __init__(self, *, loop=None):
+ if loop is not None:
+ self._loop = loop
+ else:
+ self._loop = events.get_event_loop()
+
+ # Lock as an attribute as in threading.Condition.
+ lock = Lock(loop=self._loop)
+ self._lock = lock
+ # Export the lock's locked(), acquire() and release() methods.
+ self.locked = lock.locked
+ self.acquire = lock.acquire
+ self.release = lock.release
+
+ self._waiters = collections.deque()
+
+ def __repr__(self):
+ res = super().__repr__()
+ extra = 'locked' if self.locked() else 'unlocked'
+ if self._waiters:
+ extra = '{},waiters:{}'.format(extra, len(self._waiters))
+ return '<{} [{}]>'.format(res[1:-1], extra)
+
+ @tasks.coroutine
+ def wait(self):
+ """Wait until notified.
+
+ If the calling coroutine has not acquired the lock when this
+ method is called, a RuntimeError is raised.
+
+ This method releases the underlying lock, and then blocks
+ until it is awakened by a notify() or notify_all() call for
+ the same condition variable in another coroutine. Once
+ awakened, it re-acquires the lock and returns True.
+ """
+ if not self.locked():
+ raise RuntimeError('cannot wait on un-acquired lock')
+
+ self.release()
+ try:
+ fut = futures.Future(loop=self._loop)
+ self._waiters.append(fut)
+ try:
+ yield from fut
+ return True
+ finally:
+ self._waiters.remove(fut)
+
+ finally:
+ yield from self.acquire()
+
+ @tasks.coroutine
+ def wait_for(self, predicate):
+ """Wait until a predicate becomes true.
+
+ The predicate should be a callable which result will be
+ interpreted as a boolean value. The final predicate value is
+ the return value.
+ """
+ result = predicate()
+ while not result:
+ yield from self.wait()
+ result = predicate()
+ return result
+
+ def notify(self, n=1):
+ """By default, wake up one coroutine waiting on this condition, if any.
+ If the calling coroutine has not acquired the lock when this method
+ is called, a RuntimeError is raised.
+
+ This method wakes up at most n of the coroutines waiting for the
+ condition variable; it is a no-op if no coroutines are waiting.
+
+ Note: an awakened coroutine does not actually return from its
+ wait() call until it can reacquire the lock. Since notify() does
+ not release the lock, its caller should.
+ """
+ if not self.locked():
+ raise RuntimeError('cannot notify on un-acquired lock')
+
+ idx = 0
+ for fut in self._waiters:
+ if idx >= n:
+ break
+
+ if not fut.done():
+ idx += 1
+ fut.set_result(False)
+
+ def notify_all(self):
+ """Wake up all threads waiting on this condition. This method acts
+ like notify(), but wakes up all waiting threads instead of one. If the
+ calling thread has not acquired the lock when this method is called,
+ a RuntimeError is raised.
+ """
+ self.notify(len(self._waiters))
+
+ def __enter__(self):
+ raise RuntimeError(
+ '"yield from" should be used as context manager expression')
+
+ def __exit__(self, *args):
+ pass
+
+ def __iter__(self):
+ # See comment in Lock.__iter__().
+ yield from self.acquire()
+ return _ContextManager(self)
+
+
+class Semaphore:
+ """A Semaphore implementation.
+
+ A semaphore manages an internal counter which is decremented by each
+ acquire() call and incremented by each release() call. The counter
+ can never go below zero; when acquire() finds that it is zero, it blocks,
+ waiting until some other thread calls release().
+
+ Semaphores also support the context manager protocol.
+
+ The optional argument gives the initial value for the internal
+ counter; it defaults to 1. If the value given is less than 0,
+ ValueError is raised.
+ """
+
+ def __init__(self, value=1, *, loop=None):
+ if value < 0:
+ raise ValueError("Semaphore initial value must be >= 0")
+ self._value = value
+ self._waiters = collections.deque()
+ if loop is not None:
+ self._loop = loop
+ else:
+ self._loop = events.get_event_loop()
+
+ def __repr__(self):
+ res = super().__repr__()
+ extra = 'locked' if self.locked() else 'unlocked,value:{}'.format(
+ self._value)
+ if self._waiters:
+ extra = '{},waiters:{}'.format(extra, len(self._waiters))
+ return '<{} [{}]>'.format(res[1:-1], extra)
+
+ def locked(self):
+ """Returns True if semaphore can not be acquired immediately."""
+ return self._value == 0
+
+ @tasks.coroutine
+ def acquire(self):
+ """Acquire a semaphore.
+
+ If the internal counter is larger than zero on entry,
+ decrement it by one and return True immediately. If it is
+ zero on entry, block, waiting until some other coroutine has
+ called release() to make it larger than 0, and then return
+ True.
+ """
+ if not self._waiters and self._value > 0:
+ self._value -= 1
+ return True
+
+ fut = futures.Future(loop=self._loop)
+ self._waiters.append(fut)
+ try:
+ yield from fut
+ self._value -= 1
+ return True
+ finally:
+ self._waiters.remove(fut)
+
+ def release(self):
+ """Release a semaphore, incrementing the internal counter by one.
+ When it was zero on entry and another coroutine is waiting for it to
+ become larger than zero again, wake up that coroutine.
+ """
+ self._value += 1
+ for waiter in self._waiters:
+ if not waiter.done():
+ waiter.set_result(True)
+ break
+
+ def __enter__(self):
+ raise RuntimeError(
+ '"yield from" should be used as context manager expression')
+
+ def __exit__(self, *args):
+ pass
+
+ def __iter__(self):
+ # See comment in Lock.__iter__().
+ yield from self.acquire()
+ return _ContextManager(self)
+
+
+class BoundedSemaphore(Semaphore):
+ """A bounded semaphore implementation.
+
+ This raises ValueError in release() if it would increase the value
+ above the initial value.
+ """
+
+ def __init__(self, value=1, *, loop=None):
+ self._bound_value = value
+ super().__init__(value, loop=loop)
+
+ def release(self):
+ if self._value >= self._bound_value:
+ raise ValueError('BoundedSemaphore released too many times')
+ super().release()
diff --git a/Lib/asyncio/log.py b/Lib/asyncio/log.py
new file mode 100644
index 0000000..23a7074
--- /dev/null
+++ b/Lib/asyncio/log.py
@@ -0,0 +1,7 @@
+"""Logging configuration."""
+
+import logging
+
+
+# Name the logger after the package.
+logger = logging.getLogger(__package__)
diff --git a/Lib/asyncio/proactor_events.py b/Lib/asyncio/proactor_events.py
new file mode 100644
index 0000000..d99e8ce
--- /dev/null
+++ b/Lib/asyncio/proactor_events.py
@@ -0,0 +1,455 @@
+"""Event loop using a proactor and related classes.
+
+A proactor is a "notify-on-completion" multiplexer. Currently a
+proactor is only implemented on Windows with IOCP.
+"""
+
+__all__ = ['BaseProactorEventLoop']
+
+import socket
+
+from . import base_events
+from . import constants
+from . import futures
+from . import transports
+from .log import logger
+
+
+class _ProactorBasePipeTransport(transports._FlowControlMixin,
+ transports.BaseTransport):
+ """Base class for pipe and socket transports."""
+
+ def __init__(self, loop, sock, protocol, waiter=None,
+ extra=None, server=None):
+ super().__init__(extra)
+ self._set_extra(sock)
+ self._loop = loop
+ self._sock = sock
+ self._protocol = protocol
+ self._server = server
+ self._buffer = None # None or bytearray.
+ self._read_fut = None
+ self._write_fut = None
+ self._pending_write = 0
+ self._conn_lost = 0
+ self._closing = False # Set when close() called.
+ self._eof_written = False
+ if self._server is not None:
+ self._server.attach(self)
+ self._loop.call_soon(self._protocol.connection_made, self)
+ if waiter is not None:
+ self._loop.call_soon(waiter.set_result, None)
+
+ def _set_extra(self, sock):
+ self._extra['pipe'] = sock
+
+ def close(self):
+ if self._closing:
+ return
+ self._closing = True
+ self._conn_lost += 1
+ if not self._buffer and self._write_fut is None:
+ self._loop.call_soon(self._call_connection_lost, None)
+ if self._read_fut is not None:
+ self._read_fut.cancel()
+
+ def _fatal_error(self, exc, message='Fatal error on pipe transport'):
+ if not isinstance(exc, (BrokenPipeError, ConnectionResetError)):
+ self._loop.call_exception_handler({
+ 'message': message,
+ 'exception': exc,
+ 'transport': self,
+ 'protocol': self._protocol,
+ })
+ self._force_close(exc)
+
+ def _force_close(self, exc):
+ if self._closing:
+ return
+ self._closing = True
+ self._conn_lost += 1
+ if self._write_fut:
+ self._write_fut.cancel()
+ if self._read_fut:
+ self._read_fut.cancel()
+ self._write_fut = self._read_fut = None
+ self._pending_write = 0
+ self._buffer = None
+ self._loop.call_soon(self._call_connection_lost, exc)
+
+ def _call_connection_lost(self, exc):
+ try:
+ self._protocol.connection_lost(exc)
+ finally:
+ # XXX If there is a pending overlapped read on the other
+ # end then it may fail with ERROR_NETNAME_DELETED if we
+ # just close our end. First calling shutdown() seems to
+ # cure it, but maybe using DisconnectEx() would be better.
+ if hasattr(self._sock, 'shutdown'):
+ self._sock.shutdown(socket.SHUT_RDWR)
+ self._sock.close()
+ server = self._server
+ if server is not None:
+ server.detach(self)
+ self._server = None
+
+ def get_write_buffer_size(self):
+ size = self._pending_write
+ if self._buffer is not None:
+ size += len(self._buffer)
+ return size
+
+
+class _ProactorReadPipeTransport(_ProactorBasePipeTransport,
+ transports.ReadTransport):
+ """Transport for read pipes."""
+
+ def __init__(self, loop, sock, protocol, waiter=None,
+ extra=None, server=None):
+ super().__init__(loop, sock, protocol, waiter, extra, server)
+ self._read_fut = None
+ self._paused = False
+ self._loop.call_soon(self._loop_reading)
+
+ def pause_reading(self):
+ if self._closing:
+ raise RuntimeError('Cannot pause_reading() when closing')
+ if self._paused:
+ raise RuntimeError('Already paused')
+ self._paused = True
+
+ def resume_reading(self):
+ if not self._paused:
+ raise RuntimeError('Not paused')
+ self._paused = False
+ if self._closing:
+ return
+ self._loop.call_soon(self._loop_reading, self._read_fut)
+
+ def _loop_reading(self, fut=None):
+ if self._paused:
+ return
+ data = None
+
+ try:
+ if fut is not None:
+ assert self._read_fut is fut or (self._read_fut is None and
+ self._closing)
+ self._read_fut = None
+ data = fut.result() # deliver data later in "finally" clause
+
+ if self._closing:
+ # since close() has been called we ignore any read data
+ data = None
+ return
+
+ if data == b'':
+ # we got end-of-file so no need to reschedule a new read
+ return
+
+ # reschedule a new read
+ self._read_fut = self._loop._proactor.recv(self._sock, 4096)
+ except ConnectionAbortedError as exc:
+ if not self._closing:
+ self._fatal_error(exc, 'Fatal read error on pipe transport')
+ except ConnectionResetError as exc:
+ self._force_close(exc)
+ except OSError as exc:
+ self._fatal_error(exc, 'Fatal read error on pipe transport')
+ except futures.CancelledError:
+ if not self._closing:
+ raise
+ else:
+ self._read_fut.add_done_callback(self._loop_reading)
+ finally:
+ if data:
+ self._protocol.data_received(data)
+ elif data is not None:
+ keep_open = self._protocol.eof_received()
+ if not keep_open:
+ self.close()
+
+
+class _ProactorBaseWritePipeTransport(_ProactorBasePipeTransport,
+ transports.WriteTransport):
+ """Transport for write pipes."""
+
+ def write(self, data):
+ if not isinstance(data, (bytes, bytearray, memoryview)):
+ raise TypeError('data argument must be byte-ish (%r)',
+ type(data))
+ if self._eof_written:
+ raise RuntimeError('write_eof() already called')
+
+ if not data:
+ return
+
+ if self._conn_lost:
+ if self._conn_lost >= constants.LOG_THRESHOLD_FOR_CONNLOST_WRITES:
+ logger.warning('socket.send() raised exception.')
+ self._conn_lost += 1
+ return
+
+ # Observable states:
+ # 1. IDLE: _write_fut and _buffer both None
+ # 2. WRITING: _write_fut set; _buffer None
+ # 3. BACKED UP: _write_fut set; _buffer a bytearray
+ # We always copy the data, so the caller can't modify it
+ # while we're still waiting for the I/O to happen.
+ if self._write_fut is None: # IDLE -> WRITING
+ assert self._buffer is None
+ # Pass a copy, except if it's already immutable.
+ self._loop_writing(data=bytes(data))
+ # XXX Should we pause the protocol at this point
+ # if len(data) > self._high_water? (That would
+ # require keeping track of the number of bytes passed
+ # to a send() that hasn't finished yet.)
+ elif not self._buffer: # WRITING -> BACKED UP
+ # Make a mutable copy which we can extend.
+ self._buffer = bytearray(data)
+ self._maybe_pause_protocol()
+ else: # BACKED UP
+ # Append to buffer (also copies).
+ self._buffer.extend(data)
+ self._maybe_pause_protocol()
+
+ def _loop_writing(self, f=None, data=None):
+ try:
+ assert f is self._write_fut
+ self._write_fut = None
+ self._pending_write = 0
+ if f:
+ f.result()
+ if data is None:
+ data = self._buffer
+ self._buffer = None
+ if not data:
+ if self._closing:
+ self._loop.call_soon(self._call_connection_lost, None)
+ if self._eof_written:
+ self._sock.shutdown(socket.SHUT_WR)
+ # Now that we've reduced the buffer size, tell the
+ # protocol to resume writing if it was paused. Note that
+ # we do this last since the callback is called immediately
+ # and it may add more data to the buffer (even causing the
+ # protocol to be paused again).
+ self._maybe_resume_protocol()
+ else:
+ self._write_fut = self._loop._proactor.send(self._sock, data)
+ if not self._write_fut.done():
+ assert self._pending_write == 0
+ self._pending_write = len(data)
+ self._write_fut.add_done_callback(self._loop_writing)
+ self._maybe_pause_protocol()
+ else:
+ self._write_fut.add_done_callback(self._loop_writing)
+ except ConnectionResetError as exc:
+ self._force_close(exc)
+ except OSError as exc:
+ self._fatal_error(exc, 'Fatal write error on pipe transport')
+
+ def can_write_eof(self):
+ return True
+
+ def write_eof(self):
+ self.close()
+
+ def abort(self):
+ self._force_close(None)
+
+
+class _ProactorWritePipeTransport(_ProactorBaseWritePipeTransport):
+ def __init__(self, *args, **kw):
+ super().__init__(*args, **kw)
+ self._read_fut = self._loop._proactor.recv(self._sock, 16)
+ self._read_fut.add_done_callback(self._pipe_closed)
+
+ def _pipe_closed(self, fut):
+ if fut.cancelled():
+ # the transport has been closed
+ return
+ assert fut.result() == b''
+ if self._closing:
+ assert self._read_fut is None
+ return
+ assert fut is self._read_fut, (fut, self._read_fut)
+ self._read_fut = None
+ if self._write_fut is not None:
+ self._force_close(BrokenPipeError())
+ else:
+ self.close()
+
+
+class _ProactorDuplexPipeTransport(_ProactorReadPipeTransport,
+ _ProactorBaseWritePipeTransport,
+ transports.Transport):
+ """Transport for duplex pipes."""
+
+ def can_write_eof(self):
+ return False
+
+ def write_eof(self):
+ raise NotImplementedError
+
+
+class _ProactorSocketTransport(_ProactorReadPipeTransport,
+ _ProactorBaseWritePipeTransport,
+ transports.Transport):
+ """Transport for connected sockets."""
+
+ def _set_extra(self, sock):
+ self._extra['socket'] = sock
+ try:
+ self._extra['sockname'] = sock.getsockname()
+ except (socket.error, AttributeError):
+ pass
+ if 'peername' not in self._extra:
+ try:
+ self._extra['peername'] = sock.getpeername()
+ except (socket.error, AttributeError):
+ pass
+
+ def can_write_eof(self):
+ return True
+
+ def write_eof(self):
+ if self._closing or self._eof_written:
+ return
+ self._eof_written = True
+ if self._write_fut is None:
+ self._sock.shutdown(socket.SHUT_WR)
+
+
+class BaseProactorEventLoop(base_events.BaseEventLoop):
+
+ def __init__(self, proactor):
+ super().__init__()
+ logger.debug('Using proactor: %s', proactor.__class__.__name__)
+ self._proactor = proactor
+ self._selector = proactor # convenient alias
+ self._self_reading_future = None
+ self._accept_futures = {} # socket file descriptor => Future
+ proactor.set_loop(self)
+ self._make_self_pipe()
+
+ def _make_socket_transport(self, sock, protocol, waiter=None,
+ extra=None, server=None):
+ return _ProactorSocketTransport(self, sock, protocol, waiter,
+ extra, server)
+
+ def _make_duplex_pipe_transport(self, sock, protocol, waiter=None,
+ extra=None):
+ return _ProactorDuplexPipeTransport(self,
+ sock, protocol, waiter, extra)
+
+ def _make_read_pipe_transport(self, sock, protocol, waiter=None,
+ extra=None):
+ return _ProactorReadPipeTransport(self, sock, protocol, waiter, extra)
+
+ def _make_write_pipe_transport(self, sock, protocol, waiter=None,
+ extra=None):
+ # We want connection_lost() to be called when other end closes
+ return _ProactorWritePipeTransport(self,
+ sock, protocol, waiter, extra)
+
+ def close(self):
+ if self._proactor is not None:
+ self._close_self_pipe()
+ self._proactor.close()
+ self._proactor = None
+ self._selector = None
+ super().close()
+ self._accept_futures.clear()
+
+ def sock_recv(self, sock, n):
+ return self._proactor.recv(sock, n)
+
+ def sock_sendall(self, sock, data):
+ return self._proactor.send(sock, data)
+
+ def sock_connect(self, sock, address):
+ try:
+ base_events._check_resolved_address(sock, address)
+ except ValueError as err:
+ fut = futures.Future(loop=self)
+ fut.set_exception(err)
+ return fut
+ else:
+ return self._proactor.connect(sock, address)
+
+ def sock_accept(self, sock):
+ return self._proactor.accept(sock)
+
+ def _socketpair(self):
+ raise NotImplementedError
+
+ def _close_self_pipe(self):
+ if self._self_reading_future is not None:
+ self._self_reading_future.cancel()
+ self._self_reading_future = None
+ self._ssock.close()
+ self._ssock = None
+ self._csock.close()
+ self._csock = None
+ self._internal_fds -= 1
+
+ def _make_self_pipe(self):
+ # A self-socket, really. :-)
+ self._ssock, self._csock = self._socketpair()
+ self._ssock.setblocking(False)
+ self._csock.setblocking(False)
+ self._internal_fds += 1
+ self.call_soon(self._loop_self_reading)
+
+ def _loop_self_reading(self, f=None):
+ try:
+ if f is not None:
+ f.result() # may raise
+ f = self._proactor.recv(self._ssock, 4096)
+ except:
+ self.close()
+ raise
+ else:
+ self._self_reading_future = f
+ f.add_done_callback(self._loop_self_reading)
+
+ def _write_to_self(self):
+ self._csock.send(b'x')
+
+ def _start_serving(self, protocol_factory, sock, ssl=None, server=None):
+ if ssl:
+ raise ValueError('IocpEventLoop is incompatible with SSL.')
+
+ def loop(f=None):
+ try:
+ if f is not None:
+ conn, addr = f.result()
+ protocol = protocol_factory()
+ self._make_socket_transport(
+ conn, protocol,
+ extra={'peername': addr}, server=server)
+ f = self._proactor.accept(sock)
+ except OSError as exc:
+ if sock.fileno() != -1:
+ self.call_exception_handler({
+ 'message': 'Accept failed',
+ 'exception': exc,
+ 'socket': sock,
+ })
+ sock.close()
+ except futures.CancelledError:
+ sock.close()
+ else:
+ self._accept_futures[sock.fileno()] = f
+ f.add_done_callback(loop)
+
+ self.call_soon(loop)
+
+ def _process_events(self, event_list):
+ pass # XXX hard work currently done in poll
+
+ def _stop_serving(self, sock):
+ for future in self._accept_futures.values():
+ future.cancel()
+ self._proactor._stop_serving(sock)
+ sock.close()
diff --git a/Lib/asyncio/protocols.py b/Lib/asyncio/protocols.py
new file mode 100644
index 0000000..52fc25c
--- /dev/null
+++ b/Lib/asyncio/protocols.py
@@ -0,0 +1,129 @@
+"""Abstract Protocol class."""
+
+__all__ = ['BaseProtocol', 'Protocol', 'DatagramProtocol',
+ 'SubprocessProtocol']
+
+
+class BaseProtocol:
+ """Common base class for protocol interfaces.
+
+ Usually user implements protocols that derived from BaseProtocol
+ like Protocol or ProcessProtocol.
+
+ The only case when BaseProtocol should be implemented directly is
+ write-only transport like write pipe
+ """
+
+ def connection_made(self, transport):
+ """Called when a connection is made.
+
+ The argument is the transport representing the pipe connection.
+ To receive data, wait for data_received() calls.
+ When the connection is closed, connection_lost() is called.
+ """
+
+ def connection_lost(self, exc):
+ """Called when the connection is lost or closed.
+
+ The argument is an exception object or None (the latter
+ meaning a regular EOF is received or the connection was
+ aborted or closed).
+ """
+
+ def pause_writing(self):
+ """Called when the transport's buffer goes over the high-water mark.
+
+ Pause and resume calls are paired -- pause_writing() is called
+ once when the buffer goes strictly over the high-water mark
+ (even if subsequent writes increases the buffer size even
+ more), and eventually resume_writing() is called once when the
+ buffer size reaches the low-water mark.
+
+ Note that if the buffer size equals the high-water mark,
+ pause_writing() is not called -- it must go strictly over.
+ Conversely, resume_writing() is called when the buffer size is
+ equal or lower than the low-water mark. These end conditions
+ are important to ensure that things go as expected when either
+ mark is zero.
+
+ NOTE: This is the only Protocol callback that is not called
+ through EventLoop.call_soon() -- if it were, it would have no
+ effect when it's most needed (when the app keeps writing
+ without yielding until pause_writing() is called).
+ """
+
+ def resume_writing(self):
+ """Called when the transport's buffer drains below the low-water mark.
+
+ See pause_writing() for details.
+ """
+
+
+class Protocol(BaseProtocol):
+ """Interface for stream protocol.
+
+ The user should implement this interface. They can inherit from
+ this class but don't need to. The implementations here do
+ nothing (they don't raise exceptions).
+
+ When the user wants to requests a transport, they pass a protocol
+ factory to a utility function (e.g., EventLoop.create_connection()).
+
+ When the connection is made successfully, connection_made() is
+ called with a suitable transport object. Then data_received()
+ will be called 0 or more times with data (bytes) received from the
+ transport; finally, connection_lost() will be called exactly once
+ with either an exception object or None as an argument.
+
+ State machine of calls:
+
+ start -> CM [-> DR*] [-> ER?] -> CL -> end
+ """
+
+ def data_received(self, data):
+ """Called when some data is received.
+
+ The argument is a bytes object.
+ """
+
+ def eof_received(self):
+ """Called when the other end calls write_eof() or equivalent.
+
+ If this returns a false value (including None), the transport
+ will close itself. If it returns a true value, closing the
+ transport is up to the protocol.
+ """
+
+
+class DatagramProtocol(BaseProtocol):
+ """Interface for datagram protocol."""
+
+ def datagram_received(self, data, addr):
+ """Called when some datagram is received."""
+
+ def error_received(self, exc):
+ """Called when a send or receive operation raises an OSError.
+
+ (Other than BlockingIOError or InterruptedError.)
+ """
+
+
+class SubprocessProtocol(BaseProtocol):
+ """Interface for protocol for subprocess calls."""
+
+ def pipe_data_received(self, fd, data):
+ """Called when the subprocess writes data into stdout/stderr pipe.
+
+ fd is int file descriptor.
+ data is bytes object.
+ """
+
+ def pipe_connection_lost(self, fd, exc):
+ """Called when a file descriptor associated with the child process is
+ closed.
+
+ fd is the int file descriptor that was closed.
+ """
+
+ def process_exited(self):
+ """Called when subprocess has exited."""
diff --git a/Lib/asyncio/queues.py b/Lib/asyncio/queues.py
new file mode 100644
index 0000000..6283db3
--- /dev/null
+++ b/Lib/asyncio/queues.py
@@ -0,0 +1,288 @@
+"""Queues"""
+
+__all__ = ['Queue', 'PriorityQueue', 'LifoQueue', 'JoinableQueue',
+ 'QueueFull', 'QueueEmpty']
+
+import collections
+import heapq
+
+from . import events
+from . import futures
+from . import locks
+from .tasks import coroutine
+
+
+class QueueEmpty(Exception):
+ 'Exception raised by Queue.get(block=0)/get_nowait().'
+ pass
+
+
+class QueueFull(Exception):
+ 'Exception raised by Queue.put(block=0)/put_nowait().'
+ pass
+
+
+class Queue:
+ """A queue, useful for coordinating producer and consumer coroutines.
+
+ If maxsize is less than or equal to zero, the queue size is infinite. If it
+ is an integer greater than 0, then "yield from put()" will block when the
+ queue reaches maxsize, until an item is removed by get().
+
+ Unlike the standard library Queue, you can reliably know this Queue's size
+ with qsize(), since your single-threaded asyncio application won't be
+ interrupted between calling qsize() and doing an operation on the Queue.
+ """
+
+ def __init__(self, maxsize=0, *, loop=None):
+ if loop is None:
+ self._loop = events.get_event_loop()
+ else:
+ self._loop = loop
+ self._maxsize = maxsize
+
+ # Futures.
+ self._getters = collections.deque()
+ # Pairs of (item, Future).
+ self._putters = collections.deque()
+ self._init(maxsize)
+
+ def _init(self, maxsize):
+ self._queue = collections.deque()
+
+ def _get(self):
+ return self._queue.popleft()
+
+ def _put(self, item):
+ self._queue.append(item)
+
+ def __repr__(self):
+ return '<{} at {:#x} {}>'.format(
+ type(self).__name__, id(self), self._format())
+
+ def __str__(self):
+ return '<{} {}>'.format(type(self).__name__, self._format())
+
+ def _format(self):
+ result = 'maxsize={!r}'.format(self._maxsize)
+ if getattr(self, '_queue', None):
+ result += ' _queue={!r}'.format(list(self._queue))
+ if self._getters:
+ result += ' _getters[{}]'.format(len(self._getters))
+ if self._putters:
+ result += ' _putters[{}]'.format(len(self._putters))
+ return result
+
+ def _consume_done_getters(self):
+ # Delete waiters at the head of the get() queue who've timed out.
+ while self._getters and self._getters[0].done():
+ self._getters.popleft()
+
+ def _consume_done_putters(self):
+ # Delete waiters at the head of the put() queue who've timed out.
+ while self._putters and self._putters[0][1].done():
+ self._putters.popleft()
+
+ def qsize(self):
+ """Number of items in the queue."""
+ return len(self._queue)
+
+ @property
+ def maxsize(self):
+ """Number of items allowed in the queue."""
+ return self._maxsize
+
+ def empty(self):
+ """Return True if the queue is empty, False otherwise."""
+ return not self._queue
+
+ def full(self):
+ """Return True if there are maxsize items in the queue.
+
+ Note: if the Queue was initialized with maxsize=0 (the default),
+ then full() is never True.
+ """
+ if self._maxsize <= 0:
+ return False
+ else:
+ return self.qsize() == self._maxsize
+
+ @coroutine
+ def put(self, item):
+ """Put an item into the queue.
+
+ If you yield from put(), wait until a free slot is available
+ before adding item.
+ """
+ self._consume_done_getters()
+ if self._getters:
+ assert not self._queue, (
+ 'queue non-empty, why are getters waiting?')
+
+ getter = self._getters.popleft()
+
+ # Use _put and _get instead of passing item straight to getter, in
+ # case a subclass has logic that must run (e.g. JoinableQueue).
+ self._put(item)
+ getter.set_result(self._get())
+
+ elif self._maxsize > 0 and self._maxsize == self.qsize():
+ waiter = futures.Future(loop=self._loop)
+
+ self._putters.append((item, waiter))
+ yield from waiter
+
+ else:
+ self._put(item)
+
+ def put_nowait(self, item):
+ """Put an item into the queue without blocking.
+
+ If no free slot is immediately available, raise QueueFull.
+ """
+ self._consume_done_getters()
+ if self._getters:
+ assert not self._queue, (
+ 'queue non-empty, why are getters waiting?')
+
+ getter = self._getters.popleft()
+
+ # Use _put and _get instead of passing item straight to getter, in
+ # case a subclass has logic that must run (e.g. JoinableQueue).
+ self._put(item)
+ getter.set_result(self._get())
+
+ elif self._maxsize > 0 and self._maxsize == self.qsize():
+ raise QueueFull
+ else:
+ self._put(item)
+
+ @coroutine
+ def get(self):
+ """Remove and return an item from the queue.
+
+ If you yield from get(), wait until a item is available.
+ """
+ self._consume_done_putters()
+ if self._putters:
+ assert self.full(), 'queue not full, why are putters waiting?'
+ item, putter = self._putters.popleft()
+ self._put(item)
+
+ # When a getter runs and frees up a slot so this putter can
+ # run, we need to defer the put for a tick to ensure that
+ # getters and putters alternate perfectly. See
+ # ChannelTest.test_wait.
+ self._loop.call_soon(putter.set_result, None)
+
+ return self._get()
+
+ elif self.qsize():
+ return self._get()
+ else:
+ waiter = futures.Future(loop=self._loop)
+
+ self._getters.append(waiter)
+ return (yield from waiter)
+
+ def get_nowait(self):
+ """Remove and return an item from the queue.
+
+ Return an item if one is immediately available, else raise QueueEmpty.
+ """
+ self._consume_done_putters()
+ if self._putters:
+ assert self.full(), 'queue not full, why are putters waiting?'
+ item, putter = self._putters.popleft()
+ self._put(item)
+ # Wake putter on next tick.
+ putter.set_result(None)
+
+ return self._get()
+
+ elif self.qsize():
+ return self._get()
+ else:
+ raise QueueEmpty
+
+
+class PriorityQueue(Queue):
+ """A subclass of Queue; retrieves entries in priority order (lowest first).
+
+ Entries are typically tuples of the form: (priority number, data).
+ """
+
+ def _init(self, maxsize):
+ self._queue = []
+
+ def _put(self, item, heappush=heapq.heappush):
+ heappush(self._queue, item)
+
+ def _get(self, heappop=heapq.heappop):
+ return heappop(self._queue)
+
+
+class LifoQueue(Queue):
+ """A subclass of Queue that retrieves most recently added entries first."""
+
+ def _init(self, maxsize):
+ self._queue = []
+
+ def _put(self, item):
+ self._queue.append(item)
+
+ def _get(self):
+ return self._queue.pop()
+
+
+class JoinableQueue(Queue):
+ """A subclass of Queue with task_done() and join() methods."""
+
+ def __init__(self, maxsize=0, *, loop=None):
+ super().__init__(maxsize=maxsize, loop=loop)
+ self._unfinished_tasks = 0
+ self._finished = locks.Event(loop=self._loop)
+ self._finished.set()
+
+ def _format(self):
+ result = Queue._format(self)
+ if self._unfinished_tasks:
+ result += ' tasks={}'.format(self._unfinished_tasks)
+ return result
+
+ def _put(self, item):
+ super()._put(item)
+ self._unfinished_tasks += 1
+ self._finished.clear()
+
+ def task_done(self):
+ """Indicate that a formerly enqueued task is complete.
+
+ Used by queue consumers. For each get() used to fetch a task,
+ a subsequent call to task_done() tells the queue that the processing
+ on the task is complete.
+
+ If a join() is currently blocking, it will resume when all items have
+ been processed (meaning that a task_done() call was received for every
+ item that had been put() into the queue).
+
+ Raises ValueError if called more times than there were items placed in
+ the queue.
+ """
+ if self._unfinished_tasks <= 0:
+ raise ValueError('task_done() called too many times')
+ self._unfinished_tasks -= 1
+ if self._unfinished_tasks == 0:
+ self._finished.set()
+
+ @coroutine
+ def join(self):
+ """Block until all items in the queue have been gotten and processed.
+
+ The count of unfinished tasks goes up whenever an item is added to the
+ queue. The count goes down whenever a consumer thread calls task_done()
+ to indicate that the item was retrieved and all work on it is complete.
+ When the count of unfinished tasks drops to zero, join() unblocks.
+ """
+ if self._unfinished_tasks > 0:
+ yield from self._finished.wait()
diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py
new file mode 100644
index 0000000..c7df8d8
--- /dev/null
+++ b/Lib/asyncio/selector_events.py
@@ -0,0 +1,853 @@
+"""Event loop using a selector and related classes.
+
+A selector is a "notify-when-ready" multiplexer. For a subclass which
+also includes support for signal handling, see the unix_events sub-module.
+"""
+
+__all__ = ['BaseSelectorEventLoop']
+
+import collections
+import errno
+import socket
+try:
+ import ssl
+except ImportError: # pragma: no cover
+ ssl = None
+
+from . import base_events
+from . import constants
+from . import events
+from . import futures
+from . import selectors
+from . import transports
+from .log import logger
+
+
+class BaseSelectorEventLoop(base_events.BaseEventLoop):
+ """Selector event loop.
+
+ See events.EventLoop for API specification.
+ """
+
+ def __init__(self, selector=None):
+ super().__init__()
+
+ if selector is None:
+ selector = selectors.DefaultSelector()
+ logger.debug('Using selector: %s', selector.__class__.__name__)
+ self._selector = selector
+ self._make_self_pipe()
+
+ def _make_socket_transport(self, sock, protocol, waiter=None, *,
+ extra=None, server=None):
+ return _SelectorSocketTransport(self, sock, protocol, waiter,
+ extra, server)
+
+ def _make_ssl_transport(self, rawsock, protocol, sslcontext, waiter, *,
+ server_side=False, server_hostname=None,
+ extra=None, server=None):
+ return _SelectorSslTransport(
+ self, rawsock, protocol, sslcontext, waiter,
+ server_side, server_hostname, extra, server)
+
+ def _make_datagram_transport(self, sock, protocol,
+ address=None, extra=None):
+ return _SelectorDatagramTransport(self, sock, protocol, address, extra)
+
+ def close(self):
+ if self._selector is not None:
+ self._close_self_pipe()
+ self._selector.close()
+ self._selector = None
+ super().close()
+
+ def _socketpair(self):
+ raise NotImplementedError
+
+ def _close_self_pipe(self):
+ self.remove_reader(self._ssock.fileno())
+ self._ssock.close()
+ self._ssock = None
+ self._csock.close()
+ self._csock = None
+ self._internal_fds -= 1
+
+ def _make_self_pipe(self):
+ # A self-socket, really. :-)
+ self._ssock, self._csock = self._socketpair()
+ self._ssock.setblocking(False)
+ self._csock.setblocking(False)
+ self._internal_fds += 1
+ self.add_reader(self._ssock.fileno(), self._read_from_self)
+
+ def _read_from_self(self):
+ try:
+ self._ssock.recv(1)
+ except (BlockingIOError, InterruptedError):
+ pass
+
+ def _write_to_self(self):
+ # This may be called from a different thread, possibly after
+ # _close_self_pipe() has been called or even while it is
+ # running. Guard for self._csock being None or closed. When
+ # a socket is closed, send() raises OSError (with errno set to
+ # EBADF, but let's not rely on the exact error code).
+ csock = self._csock
+ if csock is not None:
+ try:
+ csock.send(b'x')
+ except OSError:
+ pass
+
+ def _start_serving(self, protocol_factory, sock,
+ sslcontext=None, server=None):
+ self.add_reader(sock.fileno(), self._accept_connection,
+ protocol_factory, sock, sslcontext, server)
+
+ def _accept_connection(self, protocol_factory, sock,
+ sslcontext=None, server=None):
+ try:
+ conn, addr = sock.accept()
+ conn.setblocking(False)
+ except (BlockingIOError, InterruptedError, ConnectionAbortedError):
+ pass # False alarm.
+ except OSError as exc:
+ # There's nowhere to send the error, so just log it.
+ # TODO: Someone will want an error handler for this.
+ if exc.errno in (errno.EMFILE, errno.ENFILE,
+ errno.ENOBUFS, errno.ENOMEM):
+ # Some platforms (e.g. Linux keep reporting the FD as
+ # ready, so we remove the read handler temporarily.
+ # We'll try again in a while.
+ self.call_exception_handler({
+ 'message': 'socket.accept() out of system resource',
+ 'exception': exc,
+ 'socket': sock,
+ })
+ self.remove_reader(sock.fileno())
+ self.call_later(constants.ACCEPT_RETRY_DELAY,
+ self._start_serving,
+ protocol_factory, sock, sslcontext, server)
+ else:
+ raise # The event loop will catch, log and ignore it.
+ else:
+ if sslcontext:
+ self._make_ssl_transport(
+ conn, protocol_factory(), sslcontext, None,
+ server_side=True, extra={'peername': addr}, server=server)
+ else:
+ self._make_socket_transport(
+ conn, protocol_factory(), extra={'peername': addr},
+ server=server)
+ # It's now up to the protocol to handle the connection.
+
+ def add_reader(self, fd, callback, *args):
+ """Add a reader callback."""
+ if self._selector is None:
+ raise RuntimeError('Event loop is closed')
+ handle = events.Handle(callback, args, self)
+ try:
+ key = self._selector.get_key(fd)
+ except KeyError:
+ self._selector.register(fd, selectors.EVENT_READ,
+ (handle, None))
+ else:
+ mask, (reader, writer) = key.events, key.data
+ self._selector.modify(fd, mask | selectors.EVENT_READ,
+ (handle, writer))
+ if reader is not None:
+ reader.cancel()
+
+ def remove_reader(self, fd):
+ """Remove a reader callback."""
+ if self._selector is None:
+ return False
+ try:
+ key = self._selector.get_key(fd)
+ except KeyError:
+ return False
+ else:
+ mask, (reader, writer) = key.events, key.data
+ mask &= ~selectors.EVENT_READ
+ if not mask:
+ self._selector.unregister(fd)
+ else:
+ self._selector.modify(fd, mask, (None, writer))
+
+ if reader is not None:
+ reader.cancel()
+ return True
+ else:
+ return False
+
+ def add_writer(self, fd, callback, *args):
+ """Add a writer callback.."""
+ if self._selector is None:
+ raise RuntimeError('Event loop is closed')
+ handle = events.Handle(callback, args, self)
+ try:
+ key = self._selector.get_key(fd)
+ except KeyError:
+ self._selector.register(fd, selectors.EVENT_WRITE,
+ (None, handle))
+ else:
+ mask, (reader, writer) = key.events, key.data
+ self._selector.modify(fd, mask | selectors.EVENT_WRITE,
+ (reader, handle))
+ if writer is not None:
+ writer.cancel()
+
+ def remove_writer(self, fd):
+ """Remove a writer callback."""
+ if self._selector is None:
+ return False
+ try:
+ key = self._selector.get_key(fd)
+ except KeyError:
+ return False
+ else:
+ mask, (reader, writer) = key.events, key.data
+ # Remove both writer and connector.
+ mask &= ~selectors.EVENT_WRITE
+ if not mask:
+ self._selector.unregister(fd)
+ else:
+ self._selector.modify(fd, mask, (reader, None))
+
+ if writer is not None:
+ writer.cancel()
+ return True
+ else:
+ return False
+
+ def sock_recv(self, sock, n):
+ """XXX"""
+ fut = futures.Future(loop=self)
+ self._sock_recv(fut, False, sock, n)
+ return fut
+
+ def _sock_recv(self, fut, registered, sock, n):
+ # _sock_recv() can add itself as an I/O callback if the operation can't
+ # be done immediately. Don't use it directly, call sock_recv().
+ fd = sock.fileno()
+ if registered:
+ # Remove the callback early. It should be rare that the
+ # selector says the fd is ready but the call still returns
+ # EAGAIN, and I am willing to take a hit in that case in
+ # order to simplify the common case.
+ self.remove_reader(fd)
+ if fut.cancelled():
+ return
+ try:
+ data = sock.recv(n)
+ except (BlockingIOError, InterruptedError):
+ self.add_reader(fd, self._sock_recv, fut, True, sock, n)
+ except Exception as exc:
+ fut.set_exception(exc)
+ else:
+ fut.set_result(data)
+
+ def sock_sendall(self, sock, data):
+ """XXX"""
+ fut = futures.Future(loop=self)
+ if data:
+ self._sock_sendall(fut, False, sock, data)
+ else:
+ fut.set_result(None)
+ return fut
+
+ def _sock_sendall(self, fut, registered, sock, data):
+ fd = sock.fileno()
+
+ if registered:
+ self.remove_writer(fd)
+ if fut.cancelled():
+ return
+
+ try:
+ n = sock.send(data)
+ except (BlockingIOError, InterruptedError):
+ n = 0
+ except Exception as exc:
+ fut.set_exception(exc)
+ return
+
+ if n == len(data):
+ fut.set_result(None)
+ else:
+ if n:
+ data = data[n:]
+ self.add_writer(fd, self._sock_sendall, fut, True, sock, data)
+
+ def sock_connect(self, sock, address):
+ """XXX"""
+ fut = futures.Future(loop=self)
+ try:
+ base_events._check_resolved_address(sock, address)
+ except ValueError as err:
+ fut.set_exception(err)
+ else:
+ self._sock_connect(fut, False, sock, address)
+ return fut
+
+ def _sock_connect(self, fut, registered, sock, address):
+ fd = sock.fileno()
+ if registered:
+ self.remove_writer(fd)
+ if fut.cancelled():
+ return
+ try:
+ if not registered:
+ # First time around.
+ sock.connect(address)
+ else:
+ err = sock.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
+ if err != 0:
+ # Jump to the except clause below.
+ raise OSError(err, 'Connect call failed %s' % (address,))
+ except (BlockingIOError, InterruptedError):
+ self.add_writer(fd, self._sock_connect, fut, True, sock, address)
+ except Exception as exc:
+ fut.set_exception(exc)
+ else:
+ fut.set_result(None)
+
+ def sock_accept(self, sock):
+ """XXX"""
+ fut = futures.Future(loop=self)
+ self._sock_accept(fut, False, sock)
+ return fut
+
+ def _sock_accept(self, fut, registered, sock):
+ fd = sock.fileno()
+ if registered:
+ self.remove_reader(fd)
+ if fut.cancelled():
+ return
+ try:
+ conn, address = sock.accept()
+ conn.setblocking(False)
+ except (BlockingIOError, InterruptedError):
+ self.add_reader(fd, self._sock_accept, fut, True, sock)
+ except Exception as exc:
+ fut.set_exception(exc)
+ else:
+ fut.set_result((conn, address))
+
+ def _process_events(self, event_list):
+ for key, mask in event_list:
+ fileobj, (reader, writer) = key.fileobj, key.data
+ if mask & selectors.EVENT_READ and reader is not None:
+ if reader._cancelled:
+ self.remove_reader(fileobj)
+ else:
+ self._add_callback(reader)
+ if mask & selectors.EVENT_WRITE and writer is not None:
+ if writer._cancelled:
+ self.remove_writer(fileobj)
+ else:
+ self._add_callback(writer)
+
+ def _stop_serving(self, sock):
+ self.remove_reader(sock.fileno())
+ sock.close()
+
+
+class _SelectorTransport(transports._FlowControlMixin,
+ transports.Transport):
+
+ max_size = 256 * 1024 # Buffer size passed to recv().
+
+ _buffer_factory = bytearray # Constructs initial value for self._buffer.
+
+ def __init__(self, loop, sock, protocol, extra, server=None):
+ super().__init__(extra)
+ self._extra['socket'] = sock
+ self._extra['sockname'] = sock.getsockname()
+ if 'peername' not in self._extra:
+ try:
+ self._extra['peername'] = sock.getpeername()
+ except socket.error:
+ self._extra['peername'] = None
+ self._loop = loop
+ self._sock = sock
+ self._sock_fd = sock.fileno()
+ self._protocol = protocol
+ self._server = server
+ self._buffer = self._buffer_factory()
+ self._conn_lost = 0 # Set when call to connection_lost scheduled.
+ self._closing = False # Set when close() called.
+ if self._server is not None:
+ self._server.attach(self)
+
+ def abort(self):
+ self._force_close(None)
+
+ def close(self):
+ if self._closing:
+ return
+ self._closing = True
+ self._loop.remove_reader(self._sock_fd)
+ if not self._buffer:
+ self._conn_lost += 1
+ self._loop.call_soon(self._call_connection_lost, None)
+
+ def _fatal_error(self, exc, message='Fatal error on transport'):
+ # Should be called from exception handler only.
+ if not isinstance(exc, (BrokenPipeError, ConnectionResetError)):
+ self._loop.call_exception_handler({
+ 'message': message,
+ 'exception': exc,
+ 'transport': self,
+ 'protocol': self._protocol,
+ })
+ self._force_close(exc)
+
+ def _force_close(self, exc):
+ if self._conn_lost:
+ return
+ if self._buffer:
+ self._buffer.clear()
+ self._loop.remove_writer(self._sock_fd)
+ if not self._closing:
+ self._closing = True
+ self._loop.remove_reader(self._sock_fd)
+ self._conn_lost += 1
+ self._loop.call_soon(self._call_connection_lost, exc)
+
+ def _call_connection_lost(self, exc):
+ try:
+ self._protocol.connection_lost(exc)
+ finally:
+ self._sock.close()
+ self._sock = None
+ self._protocol = None
+ self._loop = None
+ server = self._server
+ if server is not None:
+ server.detach(self)
+ self._server = None
+
+ def get_write_buffer_size(self):
+ return len(self._buffer)
+
+
+class _SelectorSocketTransport(_SelectorTransport):
+
+ def __init__(self, loop, sock, protocol, waiter=None,
+ extra=None, server=None):
+ super().__init__(loop, sock, protocol, extra, server)
+ self._eof = False
+ self._paused = False
+
+ self._loop.add_reader(self._sock_fd, self._read_ready)
+ self._loop.call_soon(self._protocol.connection_made, self)
+ if waiter is not None:
+ self._loop.call_soon(waiter.set_result, None)
+
+ def pause_reading(self):
+ if self._closing:
+ raise RuntimeError('Cannot pause_reading() when closing')
+ if self._paused:
+ raise RuntimeError('Already paused')
+ self._paused = True
+ self._loop.remove_reader(self._sock_fd)
+
+ def resume_reading(self):
+ if not self._paused:
+ raise RuntimeError('Not paused')
+ self._paused = False
+ if self._closing:
+ return
+ self._loop.add_reader(self._sock_fd, self._read_ready)
+
+ def _read_ready(self):
+ try:
+ data = self._sock.recv(self.max_size)
+ except (BlockingIOError, InterruptedError):
+ pass
+ except Exception as exc:
+ self._fatal_error(exc, 'Fatal read error on socket transport')
+ else:
+ if data:
+ self._protocol.data_received(data)
+ else:
+ keep_open = self._protocol.eof_received()
+ if keep_open:
+ # We're keeping the connection open so the
+ # protocol can write more, but we still can't
+ # receive more, so remove the reader callback.
+ self._loop.remove_reader(self._sock_fd)
+ else:
+ self.close()
+
+ def write(self, data):
+ if not isinstance(data, (bytes, bytearray, memoryview)):
+ raise TypeError('data argument must be byte-ish (%r)',
+ type(data))
+ if self._eof:
+ raise RuntimeError('Cannot call write() after write_eof()')
+ if not data:
+ return
+
+ if self._conn_lost:
+ if self._conn_lost >= constants.LOG_THRESHOLD_FOR_CONNLOST_WRITES:
+ logger.warning('socket.send() raised exception.')
+ self._conn_lost += 1
+ return
+
+ if not self._buffer:
+ # Optimization: try to send now.
+ try:
+ n = self._sock.send(data)
+ except (BlockingIOError, InterruptedError):
+ pass
+ except Exception as exc:
+ self._fatal_error(exc, 'Fatal write error on socket transport')
+ return
+ else:
+ data = data[n:]
+ if not data:
+ return
+ # Not all was written; register write handler.
+ self._loop.add_writer(self._sock_fd, self._write_ready)
+
+ # Add it to the buffer.
+ self._buffer.extend(data)
+ self._maybe_pause_protocol()
+
+ def _write_ready(self):
+ assert self._buffer, 'Data should not be empty'
+
+ try:
+ n = self._sock.send(self._buffer)
+ except (BlockingIOError, InterruptedError):
+ pass
+ except Exception as exc:
+ self._loop.remove_writer(self._sock_fd)
+ self._buffer.clear()
+ self._fatal_error(exc, 'Fatal write error on socket transport')
+ else:
+ if n:
+ del self._buffer[:n]
+ self._maybe_resume_protocol() # May append to buffer.
+ if not self._buffer:
+ self._loop.remove_writer(self._sock_fd)
+ if self._closing:
+ self._call_connection_lost(None)
+ elif self._eof:
+ self._sock.shutdown(socket.SHUT_WR)
+
+ def write_eof(self):
+ if self._eof:
+ return
+ self._eof = True
+ if not self._buffer:
+ self._sock.shutdown(socket.SHUT_WR)
+
+ def can_write_eof(self):
+ return True
+
+
+class _SelectorSslTransport(_SelectorTransport):
+
+ _buffer_factory = bytearray
+
+ def __init__(self, loop, rawsock, protocol, sslcontext, waiter=None,
+ server_side=False, server_hostname=None,
+ extra=None, server=None):
+ if ssl is None:
+ raise RuntimeError('stdlib ssl module not available')
+
+ if server_side:
+ if not sslcontext:
+ raise ValueError('Server side ssl needs a valid SSLContext')
+ else:
+ if not sslcontext:
+ # Client side may pass ssl=True to use a default
+ # context; in that case the sslcontext passed is None.
+ # The default is the same as used by urllib with
+ # cadefault=True.
+ if hasattr(ssl, '_create_stdlib_context'):
+ sslcontext = ssl._create_stdlib_context(
+ cert_reqs=ssl.CERT_REQUIRED,
+ check_hostname=bool(server_hostname))
+ else:
+ # Fallback for Python 3.3.
+ sslcontext = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
+ sslcontext.options |= ssl.OP_NO_SSLv2
+ sslcontext.set_default_verify_paths()
+ sslcontext.verify_mode = ssl.CERT_REQUIRED
+
+ wrap_kwargs = {
+ 'server_side': server_side,
+ 'do_handshake_on_connect': False,
+ }
+ if server_hostname and not server_side and ssl.HAS_SNI:
+ wrap_kwargs['server_hostname'] = server_hostname
+ sslsock = sslcontext.wrap_socket(rawsock, **wrap_kwargs)
+
+ super().__init__(loop, sslsock, protocol, extra, server)
+
+ self._server_hostname = server_hostname
+ self._waiter = waiter
+ self._rawsock = rawsock
+ self._sslcontext = sslcontext
+ self._paused = False
+
+ # SSL-specific extra info. (peercert is set later)
+ self._extra.update(sslcontext=sslcontext)
+
+ self._on_handshake()
+
+ def _on_handshake(self):
+ try:
+ self._sock.do_handshake()
+ except ssl.SSLWantReadError:
+ self._loop.add_reader(self._sock_fd, self._on_handshake)
+ return
+ except ssl.SSLWantWriteError:
+ self._loop.add_writer(self._sock_fd, self._on_handshake)
+ return
+ except Exception as exc:
+ self._loop.remove_reader(self._sock_fd)
+ self._loop.remove_writer(self._sock_fd)
+ self._sock.close()
+ if self._waiter is not None:
+ self._waiter.set_exception(exc)
+ return
+ except BaseException as exc:
+ self._loop.remove_reader(self._sock_fd)
+ self._loop.remove_writer(self._sock_fd)
+ self._sock.close()
+ if self._waiter is not None:
+ self._waiter.set_exception(exc)
+ raise
+
+ self._loop.remove_reader(self._sock_fd)
+ self._loop.remove_writer(self._sock_fd)
+
+ peercert = self._sock.getpeercert()
+ if not hasattr(self._sslcontext, 'check_hostname'):
+ # Verify hostname if requested, Python 3.4+ uses check_hostname
+ # and checks the hostname in do_handshake()
+ if (self._server_hostname and
+ self._sslcontext.verify_mode != ssl.CERT_NONE):
+ try:
+ ssl.match_hostname(peercert, self._server_hostname)
+ except Exception as exc:
+ self._sock.close()
+ if self._waiter is not None:
+ self._waiter.set_exception(exc)
+ return
+
+ # Add extra info that becomes available after handshake.
+ self._extra.update(peercert=peercert,
+ cipher=self._sock.cipher(),
+ compression=self._sock.compression(),
+ )
+
+ self._read_wants_write = False
+ self._write_wants_read = False
+ self._loop.add_reader(self._sock_fd, self._read_ready)
+ self._loop.call_soon(self._protocol.connection_made, self)
+ if self._waiter is not None:
+ self._loop.call_soon(self._waiter.set_result, None)
+
+ def pause_reading(self):
+ # XXX This is a bit icky, given the comment at the top of
+ # _read_ready(). Is it possible to evoke a deadlock? I don't
+ # know, although it doesn't look like it; write() will still
+ # accept more data for the buffer and eventually the app will
+ # call resume_reading() again, and things will flow again.
+
+ if self._closing:
+ raise RuntimeError('Cannot pause_reading() when closing')
+ if self._paused:
+ raise RuntimeError('Already paused')
+ self._paused = True
+ self._loop.remove_reader(self._sock_fd)
+
+ def resume_reading(self):
+ if not self._paused:
+ raise ('Not paused')
+ self._paused = False
+ if self._closing:
+ return
+ self._loop.add_reader(self._sock_fd, self._read_ready)
+
+ def _read_ready(self):
+ if self._write_wants_read:
+ self._write_wants_read = False
+ self._write_ready()
+
+ if self._buffer:
+ self._loop.add_writer(self._sock_fd, self._write_ready)
+
+ try:
+ data = self._sock.recv(self.max_size)
+ except (BlockingIOError, InterruptedError, ssl.SSLWantReadError):
+ pass
+ except ssl.SSLWantWriteError:
+ self._read_wants_write = True
+ self._loop.remove_reader(self._sock_fd)
+ self._loop.add_writer(self._sock_fd, self._write_ready)
+ except Exception as exc:
+ self._fatal_error(exc, 'Fatal read error on SSL transport')
+ else:
+ if data:
+ self._protocol.data_received(data)
+ else:
+ try:
+ keep_open = self._protocol.eof_received()
+ if keep_open:
+ logger.warning('returning true from eof_received() '
+ 'has no effect when using ssl')
+ finally:
+ self.close()
+
+ def _write_ready(self):
+ if self._read_wants_write:
+ self._read_wants_write = False
+ self._read_ready()
+
+ if not (self._paused or self._closing):
+ self._loop.add_reader(self._sock_fd, self._read_ready)
+
+ if self._buffer:
+ try:
+ n = self._sock.send(self._buffer)
+ except (BlockingIOError, InterruptedError, ssl.SSLWantWriteError):
+ n = 0
+ except ssl.SSLWantReadError:
+ n = 0
+ self._loop.remove_writer(self._sock_fd)
+ self._write_wants_read = True
+ except Exception as exc:
+ self._loop.remove_writer(self._sock_fd)
+ self._buffer.clear()
+ self._fatal_error(exc, 'Fatal write error on SSL transport')
+ return
+
+ if n:
+ del self._buffer[:n]
+
+ self._maybe_resume_protocol() # May append to buffer.
+
+ if not self._buffer:
+ self._loop.remove_writer(self._sock_fd)
+ if self._closing:
+ self._call_connection_lost(None)
+
+ def write(self, data):
+ if not isinstance(data, (bytes, bytearray, memoryview)):
+ raise TypeError('data argument must be byte-ish (%r)',
+ type(data))
+ if not data:
+ return
+
+ if self._conn_lost:
+ if self._conn_lost >= constants.LOG_THRESHOLD_FOR_CONNLOST_WRITES:
+ logger.warning('socket.send() raised exception.')
+ self._conn_lost += 1
+ return
+
+ if not self._buffer:
+ self._loop.add_writer(self._sock_fd, self._write_ready)
+
+ # Add it to the buffer.
+ self._buffer.extend(data)
+ self._maybe_pause_protocol()
+
+ def can_write_eof(self):
+ return False
+
+
+class _SelectorDatagramTransport(_SelectorTransport):
+
+ _buffer_factory = collections.deque
+
+ def __init__(self, loop, sock, protocol, address=None, extra=None):
+ super().__init__(loop, sock, protocol, extra)
+ self._address = address
+ self._loop.add_reader(self._sock_fd, self._read_ready)
+ self._loop.call_soon(self._protocol.connection_made, self)
+
+ def get_write_buffer_size(self):
+ return sum(len(data) for data, _ in self._buffer)
+
+ def _read_ready(self):
+ try:
+ data, addr = self._sock.recvfrom(self.max_size)
+ except (BlockingIOError, InterruptedError):
+ pass
+ except OSError as exc:
+ self._protocol.error_received(exc)
+ except Exception as exc:
+ self._fatal_error(exc, 'Fatal read error on datagram transport')
+ else:
+ self._protocol.datagram_received(data, addr)
+
+ def sendto(self, data, addr=None):
+ if not isinstance(data, (bytes, bytearray, memoryview)):
+ raise TypeError('data argument must be byte-ish (%r)',
+ type(data))
+ if not data:
+ return
+
+ if self._address and addr not in (None, self._address):
+ raise ValueError('Invalid address: must be None or %s' %
+ (self._address,))
+
+ if self._conn_lost and self._address:
+ if self._conn_lost >= constants.LOG_THRESHOLD_FOR_CONNLOST_WRITES:
+ logger.warning('socket.send() raised exception.')
+ self._conn_lost += 1
+ return
+
+ if not self._buffer:
+ # Attempt to send it right away first.
+ try:
+ if self._address:
+ self._sock.send(data)
+ else:
+ self._sock.sendto(data, addr)
+ return
+ except (BlockingIOError, InterruptedError):
+ self._loop.add_writer(self._sock_fd, self._sendto_ready)
+ except OSError as exc:
+ self._protocol.error_received(exc)
+ return
+ except Exception as exc:
+ self._fatal_error(exc,
+ 'Fatal write error on datagram transport')
+ return
+
+ # Ensure that what we buffer is immutable.
+ self._buffer.append((bytes(data), addr))
+ self._maybe_pause_protocol()
+
+ def _sendto_ready(self):
+ while self._buffer:
+ data, addr = self._buffer.popleft()
+ try:
+ if self._address:
+ self._sock.send(data)
+ else:
+ self._sock.sendto(data, addr)
+ except (BlockingIOError, InterruptedError):
+ self._buffer.appendleft((data, addr)) # Try again later.
+ break
+ except OSError as exc:
+ self._protocol.error_received(exc)
+ return
+ except Exception as exc:
+ self._fatal_error(exc,
+ 'Fatal write error on datagram transport')
+ return
+
+ self._maybe_resume_protocol() # May append to buffer.
+ if not self._buffer:
+ self._loop.remove_writer(self._sock_fd)
+ if self._closing:
+ self._call_connection_lost(None)
diff --git a/Lib/asyncio/streams.py b/Lib/asyncio/streams.py
new file mode 100644
index 0000000..27d595f
--- /dev/null
+++ b/Lib/asyncio/streams.py
@@ -0,0 +1,468 @@
+"""Stream-related things."""
+
+__all__ = ['StreamReader', 'StreamWriter', 'StreamReaderProtocol',
+ 'open_connection', 'start_server',
+ 'IncompleteReadError',
+ ]
+
+import socket
+
+if hasattr(socket, 'AF_UNIX'):
+ __all__.extend(['open_unix_connection', 'start_unix_server'])
+
+from . import events
+from . import futures
+from . import protocols
+from . import tasks
+
+
+_DEFAULT_LIMIT = 2**16
+
+
+class IncompleteReadError(EOFError):
+ """
+ Incomplete read error. Attributes:
+
+ - partial: read bytes string before the end of stream was reached
+ - expected: total number of expected bytes
+ """
+ def __init__(self, partial, expected):
+ EOFError.__init__(self, "%s bytes read on a total of %s expected bytes"
+ % (len(partial), expected))
+ self.partial = partial
+ self.expected = expected
+
+
+@tasks.coroutine
+def open_connection(host=None, port=None, *,
+ loop=None, limit=_DEFAULT_LIMIT, **kwds):
+ """A wrapper for create_connection() returning a (reader, writer) pair.
+
+ The reader returned is a StreamReader instance; the writer is a
+ StreamWriter instance.
+
+ The arguments are all the usual arguments to create_connection()
+ except protocol_factory; most common are positional host and port,
+ with various optional keyword arguments following.
+
+ Additional optional keyword arguments are loop (to set the event loop
+ instance to use) and limit (to set the buffer limit passed to the
+ StreamReader).
+
+ (If you want to customize the StreamReader and/or
+ StreamReaderProtocol classes, just copy the code -- there's
+ really nothing special here except some convenience.)
+ """
+ if loop is None:
+ loop = events.get_event_loop()
+ reader = StreamReader(limit=limit, loop=loop)
+ protocol = StreamReaderProtocol(reader, loop=loop)
+ transport, _ = yield from loop.create_connection(
+ lambda: protocol, host, port, **kwds)
+ writer = StreamWriter(transport, protocol, reader, loop)
+ return reader, writer
+
+
+@tasks.coroutine
+def start_server(client_connected_cb, host=None, port=None, *,
+ loop=None, limit=_DEFAULT_LIMIT, **kwds):
+ """Start a socket server, call back for each client connected.
+
+ The first parameter, `client_connected_cb`, takes two parameters:
+ client_reader, client_writer. client_reader is a StreamReader
+ object, while client_writer is a StreamWriter object. This
+ parameter can either be a plain callback function or a coroutine;
+ if it is a coroutine, it will be automatically converted into a
+ Task.
+
+ The rest of the arguments are all the usual arguments to
+ loop.create_server() except protocol_factory; most common are
+ positional host and port, with various optional keyword arguments
+ following. The return value is the same as loop.create_server().
+
+ Additional optional keyword arguments are loop (to set the event loop
+ instance to use) and limit (to set the buffer limit passed to the
+ StreamReader).
+
+ The return value is the same as loop.create_server(), i.e. a
+ Server object which can be used to stop the service.
+ """
+ if loop is None:
+ loop = events.get_event_loop()
+
+ def factory():
+ reader = StreamReader(limit=limit, loop=loop)
+ protocol = StreamReaderProtocol(reader, client_connected_cb,
+ loop=loop)
+ return protocol
+
+ return (yield from loop.create_server(factory, host, port, **kwds))
+
+
+if hasattr(socket, 'AF_UNIX'):
+ # UNIX Domain Sockets are supported on this platform
+
+ @tasks.coroutine
+ def open_unix_connection(path=None, *,
+ loop=None, limit=_DEFAULT_LIMIT, **kwds):
+ """Similar to `open_connection` but works with UNIX Domain Sockets."""
+ if loop is None:
+ loop = events.get_event_loop()
+ reader = StreamReader(limit=limit, loop=loop)
+ protocol = StreamReaderProtocol(reader, loop=loop)
+ transport, _ = yield from loop.create_unix_connection(
+ lambda: protocol, path, **kwds)
+ writer = StreamWriter(transport, protocol, reader, loop)
+ return reader, writer
+
+
+ @tasks.coroutine
+ def start_unix_server(client_connected_cb, path=None, *,
+ loop=None, limit=_DEFAULT_LIMIT, **kwds):
+ """Similar to `start_server` but works with UNIX Domain Sockets."""
+ if loop is None:
+ loop = events.get_event_loop()
+
+ def factory():
+ reader = StreamReader(limit=limit, loop=loop)
+ protocol = StreamReaderProtocol(reader, client_connected_cb,
+ loop=loop)
+ return protocol
+
+ return (yield from loop.create_unix_server(factory, path, **kwds))
+
+
+class FlowControlMixin(protocols.Protocol):
+ """Reusable flow control logic for StreamWriter.drain().
+
+ This implements the protocol methods pause_writing(),
+ resume_reading() and connection_lost(). If the subclass overrides
+ these it must call the super methods.
+
+ StreamWriter.drain() must check for error conditions and then call
+ _make_drain_waiter(), which will return either () or a Future
+ depending on the paused state.
+ """
+
+ def __init__(self, loop=None):
+ self._loop = loop # May be None; we may never need it.
+ self._paused = False
+ self._drain_waiter = None
+
+ def pause_writing(self):
+ assert not self._paused
+ self._paused = True
+
+ def resume_writing(self):
+ assert self._paused
+ self._paused = False
+ waiter = self._drain_waiter
+ if waiter is not None:
+ self._drain_waiter = None
+ if not waiter.done():
+ waiter.set_result(None)
+
+ def connection_lost(self, exc):
+ # Wake up the writer if currently paused.
+ if not self._paused:
+ return
+ waiter = self._drain_waiter
+ if waiter is None:
+ return
+ self._drain_waiter = None
+ if waiter.done():
+ return
+ if exc is None:
+ waiter.set_result(None)
+ else:
+ waiter.set_exception(exc)
+
+ def _make_drain_waiter(self):
+ if not self._paused:
+ return ()
+ waiter = self._drain_waiter
+ assert waiter is None or waiter.cancelled()
+ waiter = futures.Future(loop=self._loop)
+ self._drain_waiter = waiter
+ return waiter
+
+
+class StreamReaderProtocol(FlowControlMixin, protocols.Protocol):
+ """Helper class to adapt between Protocol and StreamReader.
+
+ (This is a helper class instead of making StreamReader itself a
+ Protocol subclass, because the StreamReader has other potential
+ uses, and to prevent the user of the StreamReader to accidentally
+ call inappropriate methods of the protocol.)
+ """
+
+ def __init__(self, stream_reader, client_connected_cb=None, loop=None):
+ super().__init__(loop=loop)
+ self._stream_reader = stream_reader
+ self._stream_writer = None
+ self._client_connected_cb = client_connected_cb
+
+ def connection_made(self, transport):
+ self._stream_reader.set_transport(transport)
+ if self._client_connected_cb is not None:
+ self._stream_writer = StreamWriter(transport, self,
+ self._stream_reader,
+ self._loop)
+ res = self._client_connected_cb(self._stream_reader,
+ self._stream_writer)
+ if tasks.iscoroutine(res):
+ tasks.Task(res, loop=self._loop)
+
+ def connection_lost(self, exc):
+ if exc is None:
+ self._stream_reader.feed_eof()
+ else:
+ self._stream_reader.set_exception(exc)
+ super().connection_lost(exc)
+
+ def data_received(self, data):
+ self._stream_reader.feed_data(data)
+
+ def eof_received(self):
+ self._stream_reader.feed_eof()
+
+
+class StreamWriter:
+ """Wraps a Transport.
+
+ This exposes write(), writelines(), [can_]write_eof(),
+ get_extra_info() and close(). It adds drain() which returns an
+ optional Future on which you can wait for flow control. It also
+ adds a transport property which references the Transport
+ directly.
+ """
+
+ def __init__(self, transport, protocol, reader, loop):
+ self._transport = transport
+ self._protocol = protocol
+ self._reader = reader
+ self._loop = loop
+
+ @property
+ def transport(self):
+ return self._transport
+
+ def write(self, data):
+ self._transport.write(data)
+
+ def writelines(self, data):
+ self._transport.writelines(data)
+
+ def write_eof(self):
+ return self._transport.write_eof()
+
+ def can_write_eof(self):
+ return self._transport.can_write_eof()
+
+ def close(self):
+ return self._transport.close()
+
+ def get_extra_info(self, name, default=None):
+ return self._transport.get_extra_info(name, default)
+
+ def drain(self):
+ """This method has an unusual return value.
+
+ The intended use is to write
+
+ w.write(data)
+ yield from w.drain()
+
+ When there's nothing to wait for, drain() returns (), and the
+ yield-from continues immediately. When the transport buffer
+ is full (the protocol is paused), drain() creates and returns
+ a Future and the yield-from will block until that Future is
+ completed, which will happen when the buffer is (partially)
+ drained and the protocol is resumed.
+ """
+ if self._reader is not None and self._reader._exception is not None:
+ raise self._reader._exception
+ if self._transport._conn_lost: # Uses private variable.
+ raise ConnectionResetError('Connection lost')
+ return self._protocol._make_drain_waiter()
+
+
+class StreamReader:
+
+ def __init__(self, limit=_DEFAULT_LIMIT, loop=None):
+ # The line length limit is a security feature;
+ # it also doubles as half the buffer limit.
+ self._limit = limit
+ if loop is None:
+ loop = events.get_event_loop()
+ self._loop = loop
+ self._buffer = bytearray()
+ self._eof = False # Whether we're done.
+ self._waiter = None # A future.
+ self._exception = None
+ self._transport = None
+ self._paused = False
+
+ def exception(self):
+ return self._exception
+
+ def set_exception(self, exc):
+ self._exception = exc
+
+ waiter = self._waiter
+ if waiter is not None:
+ self._waiter = None
+ if not waiter.cancelled():
+ waiter.set_exception(exc)
+
+ def set_transport(self, transport):
+ assert self._transport is None, 'Transport already set'
+ self._transport = transport
+
+ def _maybe_resume_transport(self):
+ if self._paused and len(self._buffer) <= self._limit:
+ self._paused = False
+ self._transport.resume_reading()
+
+ def feed_eof(self):
+ self._eof = True
+ waiter = self._waiter
+ if waiter is not None:
+ self._waiter = None
+ if not waiter.cancelled():
+ waiter.set_result(True)
+
+ def at_eof(self):
+ """Return True if the buffer is empty and 'feed_eof' was called."""
+ return self._eof and not self._buffer
+
+ def feed_data(self, data):
+ assert not self._eof, 'feed_data after feed_eof'
+
+ if not data:
+ return
+
+ self._buffer.extend(data)
+
+ waiter = self._waiter
+ if waiter is not None:
+ self._waiter = None
+ if not waiter.cancelled():
+ waiter.set_result(False)
+
+ if (self._transport is not None and
+ not self._paused and
+ len(self._buffer) > 2*self._limit):
+ try:
+ self._transport.pause_reading()
+ except NotImplementedError:
+ # The transport can't be paused.
+ # We'll just have to buffer all data.
+ # Forget the transport so we don't keep trying.
+ self._transport = None
+ else:
+ self._paused = True
+
+ def _create_waiter(self, func_name):
+ # StreamReader uses a future to link the protocol feed_data() method
+ # to a read coroutine. Running two read coroutines at the same time
+ # would have an unexpected behaviour. It would not possible to know
+ # which coroutine would get the next data.
+ if self._waiter is not None:
+ raise RuntimeError('%s() called while another coroutine is '
+ 'already waiting for incoming data' % func_name)
+ return futures.Future(loop=self._loop)
+
+ @tasks.coroutine
+ def readline(self):
+ if self._exception is not None:
+ raise self._exception
+
+ line = bytearray()
+ not_enough = True
+
+ while not_enough:
+ while self._buffer and not_enough:
+ ichar = self._buffer.find(b'\n')
+ if ichar < 0:
+ line.extend(self._buffer)
+ self._buffer.clear()
+ else:
+ ichar += 1
+ line.extend(self._buffer[:ichar])
+ del self._buffer[:ichar]
+ not_enough = False
+
+ if len(line) > self._limit:
+ self._maybe_resume_transport()
+ raise ValueError('Line is too long')
+
+ if self._eof:
+ break
+
+ if not_enough:
+ self._waiter = self._create_waiter('readline')
+ try:
+ yield from self._waiter
+ finally:
+ self._waiter = None
+
+ self._maybe_resume_transport()
+ return bytes(line)
+
+ @tasks.coroutine
+ def read(self, n=-1):
+ if self._exception is not None:
+ raise self._exception
+
+ if not n:
+ return b''
+
+ if n < 0:
+ while not self._eof:
+ self._waiter = self._create_waiter('read')
+ try:
+ yield from self._waiter
+ finally:
+ self._waiter = None
+ else:
+ if not self._buffer and not self._eof:
+ self._waiter = self._create_waiter('read')
+ try:
+ yield from self._waiter
+ finally:
+ self._waiter = None
+
+ if n < 0 or len(self._buffer) <= n:
+ data = bytes(self._buffer)
+ self._buffer.clear()
+ else:
+ # n > 0 and len(self._buffer) > n
+ data = bytes(self._buffer[:n])
+ del self._buffer[:n]
+
+ self._maybe_resume_transport()
+ return data
+
+ @tasks.coroutine
+ def readexactly(self, n):
+ if self._exception is not None:
+ raise self._exception
+
+ # There used to be "optimized" code here. It created its own
+ # Future and waited until self._buffer had at least the n
+ # bytes, then called read(n). Unfortunately, this could pause
+ # the transport if the argument was larger than the pause
+ # limit (which is twice self._limit). So now we just read()
+ # into a local buffer.
+
+ blocks = []
+ while n > 0:
+ block = yield from self.read(n)
+ if not block:
+ partial = b''.join(blocks)
+ raise IncompleteReadError(partial, len(partial) + n)
+ blocks.append(block)
+ n -= len(block)
+
+ return b''.join(blocks)
diff --git a/Lib/asyncio/subprocess.py b/Lib/asyncio/subprocess.py
new file mode 100644
index 0000000..414e023
--- /dev/null
+++ b/Lib/asyncio/subprocess.py
@@ -0,0 +1,195 @@
+__all__ = ['create_subprocess_exec', 'create_subprocess_shell']
+
+import collections
+import subprocess
+
+from . import events
+from . import futures
+from . import protocols
+from . import streams
+from . import tasks
+
+
+PIPE = subprocess.PIPE
+STDOUT = subprocess.STDOUT
+DEVNULL = subprocess.DEVNULL
+
+
+class SubprocessStreamProtocol(streams.FlowControlMixin,
+ protocols.SubprocessProtocol):
+ """Like StreamReaderProtocol, but for a subprocess."""
+
+ def __init__(self, limit, loop):
+ super().__init__(loop=loop)
+ self._limit = limit
+ self.stdin = self.stdout = self.stderr = None
+ self.waiter = futures.Future(loop=loop)
+ self._waiters = collections.deque()
+ self._transport = None
+
+ def connection_made(self, transport):
+ self._transport = transport
+ if transport.get_pipe_transport(1):
+ self.stdout = streams.StreamReader(limit=self._limit,
+ loop=self._loop)
+ if transport.get_pipe_transport(2):
+ self.stderr = streams.StreamReader(limit=self._limit,
+ loop=self._loop)
+ stdin = transport.get_pipe_transport(0)
+ if stdin is not None:
+ self.stdin = streams.StreamWriter(stdin,
+ protocol=self,
+ reader=None,
+ loop=self._loop)
+ self.waiter.set_result(None)
+
+ def pipe_data_received(self, fd, data):
+ if fd == 1:
+ reader = self.stdout
+ elif fd == 2:
+ reader = self.stderr
+ else:
+ reader = None
+ if reader is not None:
+ reader.feed_data(data)
+
+ def pipe_connection_lost(self, fd, exc):
+ if fd == 0:
+ pipe = self.stdin
+ if pipe is not None:
+ pipe.close()
+ self.connection_lost(exc)
+ return
+ if fd == 1:
+ reader = self.stdout
+ elif fd == 2:
+ reader = self.stderr
+ else:
+ reader = None
+ if reader != None:
+ if exc is None:
+ reader.feed_eof()
+ else:
+ reader.set_exception(exc)
+
+ def process_exited(self):
+ # wake up futures waiting for wait()
+ returncode = self._transport.get_returncode()
+ while self._waiters:
+ waiter = self._waiters.popleft()
+ waiter.set_result(returncode)
+
+
+class Process:
+ def __init__(self, transport, protocol, loop):
+ self._transport = transport
+ self._protocol = protocol
+ self._loop = loop
+ self.stdin = protocol.stdin
+ self.stdout = protocol.stdout
+ self.stderr = protocol.stderr
+ self.pid = transport.get_pid()
+
+ @property
+ def returncode(self):
+ return self._transport.get_returncode()
+
+ @tasks.coroutine
+ def wait(self):
+ """Wait until the process exit and return the process return code."""
+ returncode = self._transport.get_returncode()
+ if returncode is not None:
+ return returncode
+
+ waiter = futures.Future(loop=self._loop)
+ self._protocol._waiters.append(waiter)
+ yield from waiter
+ return waiter.result()
+
+ def _check_alive(self):
+ if self._transport.get_returncode() is not None:
+ raise ProcessLookupError()
+
+ def send_signal(self, signal):
+ self._check_alive()
+ self._transport.send_signal(signal)
+
+ def terminate(self):
+ self._check_alive()
+ self._transport.terminate()
+
+ def kill(self):
+ self._check_alive()
+ self._transport.kill()
+
+ @tasks.coroutine
+ def _feed_stdin(self, input):
+ self.stdin.write(input)
+ yield from self.stdin.drain()
+ self.stdin.close()
+
+ @tasks.coroutine
+ def _noop(self):
+ return None
+
+ @tasks.coroutine
+ def _read_stream(self, fd):
+ transport = self._transport.get_pipe_transport(fd)
+ if fd == 2:
+ stream = self.stderr
+ else:
+ assert fd == 1
+ stream = self.stdout
+ output = yield from stream.read()
+ transport.close()
+ return output
+
+ @tasks.coroutine
+ def communicate(self, input=None):
+ if input:
+ stdin = self._feed_stdin(input)
+ else:
+ stdin = self._noop()
+ if self.stdout is not None:
+ stdout = self._read_stream(1)
+ else:
+ stdout = self._noop()
+ if self.stderr is not None:
+ stderr = self._read_stream(2)
+ else:
+ stderr = self._noop()
+ stdin, stdout, stderr = yield from tasks.gather(stdin, stdout, stderr,
+ loop=self._loop)
+ yield from self.wait()
+ return (stdout, stderr)
+
+
+@tasks.coroutine
+def create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None,
+ loop=None, limit=streams._DEFAULT_LIMIT, **kwds):
+ if loop is None:
+ loop = events.get_event_loop()
+ protocol_factory = lambda: SubprocessStreamProtocol(limit=limit,
+ loop=loop)
+ transport, protocol = yield from loop.subprocess_shell(
+ protocol_factory,
+ cmd, stdin=stdin, stdout=stdout,
+ stderr=stderr, **kwds)
+ yield from protocol.waiter
+ return Process(transport, protocol, loop)
+
+@tasks.coroutine
+def create_subprocess_exec(program, *args, stdin=None, stdout=None,
+ stderr=None, loop=None,
+ limit=streams._DEFAULT_LIMIT, **kwds):
+ if loop is None:
+ loop = events.get_event_loop()
+ protocol_factory = lambda: SubprocessStreamProtocol(limit=limit,
+ loop=loop)
+ transport, protocol = yield from loop.subprocess_exec(
+ protocol_factory,
+ program, *args,
+ stdin=stdin, stdout=stdout,
+ stderr=stderr, **kwds)
+ yield from protocol.waiter
+ return Process(transport, protocol, loop)
diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py
new file mode 100644
index 0000000..45a6342
--- /dev/null
+++ b/Lib/asyncio/tasks.py
@@ -0,0 +1,720 @@
+"""Support for tasks, coroutines and the scheduler."""
+
+__all__ = ['coroutine', 'Task',
+ 'iscoroutinefunction', 'iscoroutine',
+ 'FIRST_COMPLETED', 'FIRST_EXCEPTION', 'ALL_COMPLETED',
+ 'wait', 'wait_for', 'as_completed', 'sleep', 'async',
+ 'gather', 'shield',
+ ]
+
+import concurrent.futures
+import functools
+import inspect
+import linecache
+import os
+import sys
+import traceback
+import weakref
+
+from . import events
+from . import futures
+from .log import logger
+
+# If you set _DEBUG to true, @coroutine will wrap the resulting
+# generator objects in a CoroWrapper instance (defined below). That
+# instance will log a message when the generator is never iterated
+# over, which may happen when you forget to use "yield from" with a
+# coroutine call. Note that the value of the _DEBUG flag is taken
+# when the decorator is used, so to be of any use it must be set
+# before you define your coroutines. A downside of using this feature
+# is that tracebacks show entries for the CoroWrapper.__next__ method
+# when _DEBUG is true.
+_DEBUG = (not sys.flags.ignore_environment
+ and bool(os.environ.get('PYTHONASYNCIODEBUG')))
+
+
+class CoroWrapper:
+ # Wrapper for coroutine in _DEBUG mode.
+
+ __slots__ = ['gen', 'func', '__name__', '__doc__', '__weakref__']
+
+ def __init__(self, gen, func):
+ assert inspect.isgenerator(gen), gen
+ self.gen = gen
+ self.func = func
+
+ def __iter__(self):
+ return self
+
+ def __next__(self):
+ return next(self.gen)
+
+ def send(self, *value):
+ # We use `*value` because of a bug in CPythons prior
+ # to 3.4.1. See issue #21209 and test_yield_from_corowrapper
+ # for details. This workaround should be removed in 3.5.0.
+ if len(value) == 1:
+ value = value[0]
+ return self.gen.send(value)
+
+ def throw(self, exc):
+ return self.gen.throw(exc)
+
+ def close(self):
+ return self.gen.close()
+
+ @property
+ def gi_frame(self):
+ return self.gen.gi_frame
+
+ @property
+ def gi_running(self):
+ return self.gen.gi_running
+
+ @property
+ def gi_code(self):
+ return self.gen.gi_code
+
+ def __del__(self):
+ # Be careful accessing self.gen.frame -- self.gen might not exist.
+ gen = getattr(self, 'gen', None)
+ frame = getattr(gen, 'gi_frame', None)
+ if frame is not None and frame.f_lasti == -1:
+ func = self.func
+ code = func.__code__
+ filename = code.co_filename
+ lineno = code.co_firstlineno
+ logger.error(
+ 'Coroutine %r defined at %s:%s was never yielded from',
+ func.__name__, filename, lineno)
+
+
+def coroutine(func):
+ """Decorator to mark coroutines.
+
+ If the coroutine is not yielded from before it is destroyed,
+ an error message is logged.
+ """
+ if inspect.isgeneratorfunction(func):
+ coro = func
+ else:
+ @functools.wraps(func)
+ def coro(*args, **kw):
+ res = func(*args, **kw)
+ if isinstance(res, futures.Future) or inspect.isgenerator(res):
+ res = yield from res
+ return res
+
+ if not _DEBUG:
+ wrapper = coro
+ else:
+ @functools.wraps(func)
+ def wrapper(*args, **kwds):
+ w = CoroWrapper(coro(*args, **kwds), func)
+ w.__name__ = coro.__name__
+ w.__doc__ = coro.__doc__
+ return w
+
+ wrapper._is_coroutine = True # For iscoroutinefunction().
+ return wrapper
+
+
+def iscoroutinefunction(func):
+ """Return True if func is a decorated coroutine function."""
+ return getattr(func, '_is_coroutine', False)
+
+
+def iscoroutine(obj):
+ """Return True if obj is a coroutine object."""
+ return isinstance(obj, CoroWrapper) or inspect.isgenerator(obj)
+
+
+class Task(futures.Future):
+ """A coroutine wrapped in a Future."""
+
+ # An important invariant maintained while a Task not done:
+ #
+ # - Either _fut_waiter is None, and _step() is scheduled;
+ # - or _fut_waiter is some Future, and _step() is *not* scheduled.
+ #
+ # The only transition from the latter to the former is through
+ # _wakeup(). When _fut_waiter is not None, one of its callbacks
+ # must be _wakeup().
+
+ # Weak set containing all tasks alive.
+ _all_tasks = weakref.WeakSet()
+
+ # Dictionary containing tasks that are currently active in
+ # all running event loops. {EventLoop: Task}
+ _current_tasks = {}
+
+ @classmethod
+ def current_task(cls, loop=None):
+ """Return the currently running task in an event loop or None.
+
+ By default the current task for the current event loop is returned.
+
+ None is returned when called not in the context of a Task.
+ """
+ if loop is None:
+ loop = events.get_event_loop()
+ return cls._current_tasks.get(loop)
+
+ @classmethod
+ def all_tasks(cls, loop=None):
+ """Return a set of all tasks for an event loop.
+
+ By default all tasks for the current event loop are returned.
+ """
+ if loop is None:
+ loop = events.get_event_loop()
+ return {t for t in cls._all_tasks if t._loop is loop}
+
+ def __init__(self, coro, *, loop=None):
+ assert iscoroutine(coro), repr(coro) # Not a coroutine function!
+ super().__init__(loop=loop)
+ self._coro = iter(coro) # Use the iterator just in case.
+ self._fut_waiter = None
+ self._must_cancel = False
+ self._loop.call_soon(self._step)
+ self.__class__._all_tasks.add(self)
+
+ def __repr__(self):
+ res = super().__repr__()
+ if (self._must_cancel and
+ self._state == futures._PENDING and
+ '<PENDING' in res):
+ res = res.replace('<PENDING', '<CANCELLING', 1)
+ i = res.find('<')
+ if i < 0:
+ i = len(res)
+ res = res[:i] + '(<{}>)'.format(self._coro.__name__) + res[i:]
+ return res
+
+ def get_stack(self, *, limit=None):
+ """Return the list of stack frames for this task's coroutine.
+
+ If the coroutine is active, this returns the stack where it is
+ suspended. If the coroutine has completed successfully or was
+ cancelled, this returns an empty list. If the coroutine was
+ terminated by an exception, this returns the list of traceback
+ frames.
+
+ The frames are always ordered from oldest to newest.
+
+ The optional limit gives the maximum number of frames to
+ return; by default all available frames are returned. Its
+ meaning differs depending on whether a stack or a traceback is
+ returned: the newest frames of a stack are returned, but the
+ oldest frames of a traceback are returned. (This matches the
+ behavior of the traceback module.)
+
+ For reasons beyond our control, only one stack frame is
+ returned for a suspended coroutine.
+ """
+ frames = []
+ f = self._coro.gi_frame
+ if f is not None:
+ while f is not None:
+ if limit is not None:
+ if limit <= 0:
+ break
+ limit -= 1
+ frames.append(f)
+ f = f.f_back
+ frames.reverse()
+ elif self._exception is not None:
+ tb = self._exception.__traceback__
+ while tb is not None:
+ if limit is not None:
+ if limit <= 0:
+ break
+ limit -= 1
+ frames.append(tb.tb_frame)
+ tb = tb.tb_next
+ return frames
+
+ def print_stack(self, *, limit=None, file=None):
+ """Print the stack or traceback for this task's coroutine.
+
+ This produces output similar to that of the traceback module,
+ for the frames retrieved by get_stack(). The limit argument
+ is passed to get_stack(). The file argument is an I/O stream
+ to which the output goes; by default it goes to sys.stderr.
+ """
+ extracted_list = []
+ checked = set()
+ for f in self.get_stack(limit=limit):
+ lineno = f.f_lineno
+ co = f.f_code
+ filename = co.co_filename
+ name = co.co_name
+ if filename not in checked:
+ checked.add(filename)
+ linecache.checkcache(filename)
+ line = linecache.getline(filename, lineno, f.f_globals)
+ extracted_list.append((filename, lineno, name, line))
+ exc = self._exception
+ if not extracted_list:
+ print('No stack for %r' % self, file=file)
+ elif exc is not None:
+ print('Traceback for %r (most recent call last):' % self,
+ file=file)
+ else:
+ print('Stack for %r (most recent call last):' % self,
+ file=file)
+ traceback.print_list(extracted_list, file=file)
+ if exc is not None:
+ for line in traceback.format_exception_only(exc.__class__, exc):
+ print(line, file=file, end='')
+
+ def cancel(self):
+ """Request that a task to cancel itself.
+
+ This arranges for a CancellationError to be thrown into the
+ wrapped coroutine on the next cycle through the event loop.
+ The coroutine then has a chance to clean up or even deny
+ the request using try/except/finally.
+
+ Contrary to Future.cancel(), this does not guarantee that the
+ task will be cancelled: the exception might be caught and
+ acted upon, delaying cancellation of the task or preventing it
+ completely. The task may also return a value or raise a
+ different exception.
+
+ Immediately after this method is called, Task.cancelled() will
+ not return True (unless the task was already cancelled). A
+ task will be marked as cancelled when the wrapped coroutine
+ terminates with a CancelledError exception (even if cancel()
+ was not called).
+ """
+ if self.done():
+ return False
+ if self._fut_waiter is not None:
+ if self._fut_waiter.cancel():
+ # Leave self._fut_waiter; it may be a Task that
+ # catches and ignores the cancellation so we may have
+ # to cancel it again later.
+ return True
+ # It must be the case that self._step is already scheduled.
+ self._must_cancel = True
+ return True
+
+ def _step(self, value=None, exc=None):
+ assert not self.done(), \
+ '_step(): already done: {!r}, {!r}, {!r}'.format(self, value, exc)
+ if self._must_cancel:
+ if not isinstance(exc, futures.CancelledError):
+ exc = futures.CancelledError()
+ self._must_cancel = False
+ coro = self._coro
+ self._fut_waiter = None
+
+ self.__class__._current_tasks[self._loop] = self
+ # Call either coro.throw(exc) or coro.send(value).
+ try:
+ if exc is not None:
+ result = coro.throw(exc)
+ elif value is not None:
+ result = coro.send(value)
+ else:
+ result = next(coro)
+ except StopIteration as exc:
+ self.set_result(exc.value)
+ except futures.CancelledError as exc:
+ super().cancel() # I.e., Future.cancel(self).
+ except Exception as exc:
+ self.set_exception(exc)
+ except BaseException as exc:
+ self.set_exception(exc)
+ raise
+ else:
+ if isinstance(result, futures.Future):
+ # Yielded Future must come from Future.__iter__().
+ if result._blocking:
+ result._blocking = False
+ result.add_done_callback(self._wakeup)
+ self._fut_waiter = result
+ if self._must_cancel:
+ if self._fut_waiter.cancel():
+ self._must_cancel = False
+ else:
+ self._loop.call_soon(
+ self._step, None,
+ RuntimeError(
+ 'yield was used instead of yield from '
+ 'in task {!r} with {!r}'.format(self, result)))
+ elif result is None:
+ # Bare yield relinquishes control for one event loop iteration.
+ self._loop.call_soon(self._step)
+ elif inspect.isgenerator(result):
+ # Yielding a generator is just wrong.
+ self._loop.call_soon(
+ self._step, None,
+ RuntimeError(
+ 'yield was used instead of yield from for '
+ 'generator in task {!r} with {}'.format(
+ self, result)))
+ else:
+ # Yielding something else is an error.
+ self._loop.call_soon(
+ self._step, None,
+ RuntimeError(
+ 'Task got bad yield: {!r}'.format(result)))
+ finally:
+ self.__class__._current_tasks.pop(self._loop)
+ self = None # Needed to break cycles when an exception occurs.
+
+ def _wakeup(self, future):
+ try:
+ value = future.result()
+ except Exception as exc:
+ # This may also be a cancellation.
+ self._step(None, exc)
+ else:
+ self._step(value, None)
+ self = None # Needed to break cycles when an exception occurs.
+
+
+# wait() and as_completed() similar to those in PEP 3148.
+
+FIRST_COMPLETED = concurrent.futures.FIRST_COMPLETED
+FIRST_EXCEPTION = concurrent.futures.FIRST_EXCEPTION
+ALL_COMPLETED = concurrent.futures.ALL_COMPLETED
+
+
+@coroutine
+def wait(fs, *, loop=None, timeout=None, return_when=ALL_COMPLETED):
+ """Wait for the Futures and coroutines given by fs to complete.
+
+ Coroutines will be wrapped in Tasks.
+
+ Returns two sets of Future: (done, pending).
+
+ Usage:
+
+ done, pending = yield from asyncio.wait(fs)
+
+ Note: This does not raise TimeoutError! Futures that aren't done
+ when the timeout occurs are returned in the second set.
+ """
+ if isinstance(fs, futures.Future) or iscoroutine(fs):
+ raise TypeError("expect a list of futures, not %s" % type(fs).__name__)
+ if not fs:
+ raise ValueError('Set of coroutines/Futures is empty.')
+
+ if loop is None:
+ loop = events.get_event_loop()
+
+ fs = {async(f, loop=loop) for f in set(fs)}
+
+ if return_when not in (FIRST_COMPLETED, FIRST_EXCEPTION, ALL_COMPLETED):
+ raise ValueError('Invalid return_when value: {}'.format(return_when))
+ return (yield from _wait(fs, timeout, return_when, loop))
+
+
+def _release_waiter(waiter, value=True, *args):
+ if not waiter.done():
+ waiter.set_result(value)
+
+
+@coroutine
+def wait_for(fut, timeout, *, loop=None):
+ """Wait for the single Future or coroutine to complete, with timeout.
+
+ Coroutine will be wrapped in Task.
+
+ Returns result of the Future or coroutine. When a timeout occurs,
+ it cancels the task and raises TimeoutError. To avoid the task
+ cancellation, wrap it in shield().
+
+ Usage:
+
+ result = yield from asyncio.wait_for(fut, 10.0)
+
+ """
+ if loop is None:
+ loop = events.get_event_loop()
+
+ if timeout is None:
+ return (yield from fut)
+
+ waiter = futures.Future(loop=loop)
+ timeout_handle = loop.call_later(timeout, _release_waiter, waiter, False)
+ cb = functools.partial(_release_waiter, waiter, True)
+
+ fut = async(fut, loop=loop)
+ fut.add_done_callback(cb)
+
+ try:
+ if (yield from waiter):
+ return fut.result()
+ else:
+ fut.remove_done_callback(cb)
+ fut.cancel()
+ raise futures.TimeoutError()
+ finally:
+ timeout_handle.cancel()
+
+
+@coroutine
+def _wait(fs, timeout, return_when, loop):
+ """Internal helper for wait() and _wait_for().
+
+ The fs argument must be a collection of Futures.
+ """
+ assert fs, 'Set of Futures is empty.'
+ waiter = futures.Future(loop=loop)
+ timeout_handle = None
+ if timeout is not None:
+ timeout_handle = loop.call_later(timeout, _release_waiter, waiter)
+ counter = len(fs)
+
+ def _on_completion(f):
+ nonlocal counter
+ counter -= 1
+ if (counter <= 0 or
+ return_when == FIRST_COMPLETED or
+ return_when == FIRST_EXCEPTION and (not f.cancelled() and
+ f.exception() is not None)):
+ if timeout_handle is not None:
+ timeout_handle.cancel()
+ if not waiter.done():
+ waiter.set_result(False)
+
+ for f in fs:
+ f.add_done_callback(_on_completion)
+
+ try:
+ yield from waiter
+ finally:
+ if timeout_handle is not None:
+ timeout_handle.cancel()
+
+ done, pending = set(), set()
+ for f in fs:
+ f.remove_done_callback(_on_completion)
+ if f.done():
+ done.add(f)
+ else:
+ pending.add(f)
+ return done, pending
+
+
+# This is *not* a @coroutine! It is just an iterator (yielding Futures).
+def as_completed(fs, *, loop=None, timeout=None):
+ """Return an iterator whose values are coroutines.
+
+ When waiting for the yielded coroutines you'll get the results (or
+ exceptions!) of the original Futures (or coroutines), in the order
+ in which and as soon as they complete.
+
+ This differs from PEP 3148; the proper way to use this is:
+
+ for f in as_completed(fs):
+ result = yield from f # The 'yield from' may raise.
+ # Use result.
+
+ If a timeout is specified, the 'yield from' will raise
+ TimeoutError when the timeout occurs before all Futures are done.
+
+ Note: The futures 'f' are not necessarily members of fs.
+ """
+ if isinstance(fs, futures.Future) or iscoroutine(fs):
+ raise TypeError("expect a list of futures, not %s" % type(fs).__name__)
+ loop = loop if loop is not None else events.get_event_loop()
+ todo = {async(f, loop=loop) for f in set(fs)}
+ from .queues import Queue # Import here to avoid circular import problem.
+ done = Queue(loop=loop)
+ timeout_handle = None
+
+ def _on_timeout():
+ for f in todo:
+ f.remove_done_callback(_on_completion)
+ done.put_nowait(None) # Queue a dummy value for _wait_for_one().
+ todo.clear() # Can't do todo.remove(f) in the loop.
+
+ def _on_completion(f):
+ if not todo:
+ return # _on_timeout() was here first.
+ todo.remove(f)
+ done.put_nowait(f)
+ if not todo and timeout_handle is not None:
+ timeout_handle.cancel()
+
+ @coroutine
+ def _wait_for_one():
+ f = yield from done.get()
+ if f is None:
+ # Dummy value from _on_timeout().
+ raise futures.TimeoutError
+ return f.result() # May raise f.exception().
+
+ for f in todo:
+ f.add_done_callback(_on_completion)
+ if todo and timeout is not None:
+ timeout_handle = loop.call_later(timeout, _on_timeout)
+ for _ in range(len(todo)):
+ yield _wait_for_one()
+
+
+@coroutine
+def sleep(delay, result=None, *, loop=None):
+ """Coroutine that completes after a given time (in seconds)."""
+ future = futures.Future(loop=loop)
+ h = future._loop.call_later(delay, future.set_result, result)
+ try:
+ return (yield from future)
+ finally:
+ h.cancel()
+
+
+def async(coro_or_future, *, loop=None):
+ """Wrap a coroutine in a future.
+
+ If the argument is a Future, it is returned directly.
+ """
+ if isinstance(coro_or_future, futures.Future):
+ if loop is not None and loop is not coro_or_future._loop:
+ raise ValueError('loop argument must agree with Future')
+ return coro_or_future
+ elif iscoroutine(coro_or_future):
+ return Task(coro_or_future, loop=loop)
+ else:
+ raise TypeError('A Future or coroutine is required')
+
+
+class _GatheringFuture(futures.Future):
+ """Helper for gather().
+
+ This overrides cancel() to cancel all the children and act more
+ like Task.cancel(), which doesn't immediately mark itself as
+ cancelled.
+ """
+
+ def __init__(self, children, *, loop=None):
+ super().__init__(loop=loop)
+ self._children = children
+
+ def cancel(self):
+ if self.done():
+ return False
+ for child in self._children:
+ child.cancel()
+ return True
+
+
+def gather(*coros_or_futures, loop=None, return_exceptions=False):
+ """Return a future aggregating results from the given coroutines
+ or futures.
+
+ All futures must share the same event loop. If all the tasks are
+ done successfully, the returned future's result is the list of
+ results (in the order of the original sequence, not necessarily
+ the order of results arrival). If *return_exceptions* is True,
+ exceptions in the tasks are treated the same as successful
+ results, and gathered in the result list; otherwise, the first
+ raised exception will be immediately propagated to the returned
+ future.
+
+ Cancellation: if the outer Future is cancelled, all children (that
+ have not completed yet) are also cancelled. If any child is
+ cancelled, this is treated as if it raised CancelledError --
+ the outer Future is *not* cancelled in this case. (This is to
+ prevent the cancellation of one child to cause other children to
+ be cancelled.)
+ """
+ arg_to_fut = {arg: async(arg, loop=loop) for arg in set(coros_or_futures)}
+ children = [arg_to_fut[arg] for arg in coros_or_futures]
+ n = len(children)
+ if n == 0:
+ outer = futures.Future(loop=loop)
+ outer.set_result([])
+ return outer
+ if loop is None:
+ loop = children[0]._loop
+ for fut in children:
+ if fut._loop is not loop:
+ raise ValueError("futures are tied to different event loops")
+ outer = _GatheringFuture(children, loop=loop)
+ nfinished = 0
+ results = [None] * n
+
+ def _done_callback(i, fut):
+ nonlocal nfinished
+ if outer._state != futures._PENDING:
+ if fut._exception is not None:
+ # Mark exception retrieved.
+ fut.exception()
+ return
+ if fut._state == futures._CANCELLED:
+ res = futures.CancelledError()
+ if not return_exceptions:
+ outer.set_exception(res)
+ return
+ elif fut._exception is not None:
+ res = fut.exception() # Mark exception retrieved.
+ if not return_exceptions:
+ outer.set_exception(res)
+ return
+ else:
+ res = fut._result
+ results[i] = res
+ nfinished += 1
+ if nfinished == n:
+ outer.set_result(results)
+
+ for i, fut in enumerate(children):
+ fut.add_done_callback(functools.partial(_done_callback, i))
+ return outer
+
+
+def shield(arg, *, loop=None):
+ """Wait for a future, shielding it from cancellation.
+
+ The statement
+
+ res = yield from shield(something())
+
+ is exactly equivalent to the statement
+
+ res = yield from something()
+
+ *except* that if the coroutine containing it is cancelled, the
+ task running in something() is not cancelled. From the POV of
+ something(), the cancellation did not happen. But its caller is
+ still cancelled, so the yield-from expression still raises
+ CancelledError. Note: If something() is cancelled by other means
+ this will still cancel shield().
+
+ If you want to completely ignore cancellation (not recommended)
+ you can combine shield() with a try/except clause, as follows:
+
+ try:
+ res = yield from shield(something())
+ except CancelledError:
+ res = None
+ """
+ inner = async(arg, loop=loop)
+ if inner.done():
+ # Shortcut.
+ return inner
+ loop = inner._loop
+ outer = futures.Future(loop=loop)
+
+ def _done_callback(inner):
+ if outer.cancelled():
+ # Mark inner's result as retrieved.
+ inner.cancelled() or inner.exception()
+ return
+ if inner.cancelled():
+ outer.cancel()
+ else:
+ exc = inner.exception()
+ if exc is not None:
+ outer.set_exception(exc)
+ else:
+ outer.set_result(inner.result())
+
+ inner.add_done_callback(_done_callback)
+ return outer
diff --git a/Lib/asyncio/test_utils.py b/Lib/asyncio/test_utils.py
new file mode 100644
index 0000000..9c3656a
--- /dev/null
+++ b/Lib/asyncio/test_utils.py
@@ -0,0 +1,374 @@
+"""Utilities shared by tests."""
+
+import collections
+import contextlib
+import io
+import os
+import re
+import socket
+import socketserver
+import sys
+import tempfile
+import threading
+import time
+from unittest import mock
+
+from http.server import HTTPServer
+from wsgiref.simple_server import WSGIRequestHandler, WSGIServer
+
+try:
+ import ssl
+except ImportError: # pragma: no cover
+ ssl = None
+
+from . import base_events
+from . import events
+from . import futures
+from . import selectors
+from . import tasks
+
+
+if sys.platform == 'win32': # pragma: no cover
+ from .windows_utils import socketpair
+else:
+ from socket import socketpair # pragma: no cover
+
+
+def dummy_ssl_context():
+ if ssl is None:
+ return None
+ else:
+ return ssl.SSLContext(ssl.PROTOCOL_SSLv23)
+
+
+def run_briefly(loop):
+ @tasks.coroutine
+ def once():
+ pass
+ gen = once()
+ t = tasks.Task(gen, loop=loop)
+ try:
+ loop.run_until_complete(t)
+ finally:
+ gen.close()
+
+
+def run_until(loop, pred, timeout=30):
+ deadline = time.time() + timeout
+ while not pred():
+ if timeout is not None:
+ timeout = deadline - time.time()
+ if timeout <= 0:
+ raise futures.TimeoutError()
+ loop.run_until_complete(tasks.sleep(0.001, loop=loop))
+
+
+def run_once(loop):
+ """loop.stop() schedules _raise_stop_error()
+ and run_forever() runs until _raise_stop_error() callback.
+ this wont work if test waits for some IO events, because
+ _raise_stop_error() runs before any of io events callbacks.
+ """
+ loop.stop()
+ loop.run_forever()
+
+
+class SilentWSGIRequestHandler(WSGIRequestHandler):
+
+ def get_stderr(self):
+ return io.StringIO()
+
+ def log_message(self, format, *args):
+ pass
+
+
+class SilentWSGIServer(WSGIServer):
+
+ def handle_error(self, request, client_address):
+ pass
+
+
+class SSLWSGIServerMixin:
+
+ def finish_request(self, request, client_address):
+ # The relative location of our test directory (which
+ # contains the ssl key and certificate files) differs
+ # between the stdlib and stand-alone asyncio.
+ # Prefer our own if we can find it.
+ here = os.path.join(os.path.dirname(__file__), '..', 'tests')
+ if not os.path.isdir(here):
+ here = os.path.join(os.path.dirname(os.__file__),
+ 'test', 'test_asyncio')
+ keyfile = os.path.join(here, 'ssl_key.pem')
+ certfile = os.path.join(here, 'ssl_cert.pem')
+ ssock = ssl.wrap_socket(request,
+ keyfile=keyfile,
+ certfile=certfile,
+ server_side=True)
+ try:
+ self.RequestHandlerClass(ssock, client_address, self)
+ ssock.close()
+ except OSError:
+ # maybe socket has been closed by peer
+ pass
+
+
+class SSLWSGIServer(SSLWSGIServerMixin, SilentWSGIServer):
+ pass
+
+
+def _run_test_server(*, address, use_ssl=False, server_cls, server_ssl_cls):
+
+ def app(environ, start_response):
+ status = '200 OK'
+ headers = [('Content-type', 'text/plain')]
+ start_response(status, headers)
+ return [b'Test message']
+
+ # Run the test WSGI server in a separate thread in order not to
+ # interfere with event handling in the main thread
+ server_class = server_ssl_cls if use_ssl else server_cls
+ httpd = server_class(address, SilentWSGIRequestHandler)
+ httpd.set_app(app)
+ httpd.address = httpd.server_address
+ server_thread = threading.Thread(target=httpd.serve_forever)
+ server_thread.start()
+ try:
+ yield httpd
+ finally:
+ httpd.shutdown()
+ httpd.server_close()
+ server_thread.join()
+
+
+if hasattr(socket, 'AF_UNIX'):
+
+ class UnixHTTPServer(socketserver.UnixStreamServer, HTTPServer):
+
+ def server_bind(self):
+ socketserver.UnixStreamServer.server_bind(self)
+ self.server_name = '127.0.0.1'
+ self.server_port = 80
+
+
+ class UnixWSGIServer(UnixHTTPServer, WSGIServer):
+
+ def server_bind(self):
+ UnixHTTPServer.server_bind(self)
+ self.setup_environ()
+
+ def get_request(self):
+ request, client_addr = super().get_request()
+ # Code in the stdlib expects that get_request
+ # will return a socket and a tuple (host, port).
+ # However, this isn't true for UNIX sockets,
+ # as the second return value will be a path;
+ # hence we return some fake data sufficient
+ # to get the tests going
+ return request, ('127.0.0.1', '')
+
+
+ class SilentUnixWSGIServer(UnixWSGIServer):
+
+ def handle_error(self, request, client_address):
+ pass
+
+
+ class UnixSSLWSGIServer(SSLWSGIServerMixin, SilentUnixWSGIServer):
+ pass
+
+
+ def gen_unix_socket_path():
+ with tempfile.NamedTemporaryFile() as file:
+ return file.name
+
+
+ @contextlib.contextmanager
+ def unix_socket_path():
+ path = gen_unix_socket_path()
+ try:
+ yield path
+ finally:
+ try:
+ os.unlink(path)
+ except OSError:
+ pass
+
+
+ @contextlib.contextmanager
+ def run_test_unix_server(*, use_ssl=False):
+ with unix_socket_path() as path:
+ yield from _run_test_server(address=path, use_ssl=use_ssl,
+ server_cls=SilentUnixWSGIServer,
+ server_ssl_cls=UnixSSLWSGIServer)
+
+
+@contextlib.contextmanager
+def run_test_server(*, host='127.0.0.1', port=0, use_ssl=False):
+ yield from _run_test_server(address=(host, port), use_ssl=use_ssl,
+ server_cls=SilentWSGIServer,
+ server_ssl_cls=SSLWSGIServer)
+
+
+def make_test_protocol(base):
+ dct = {}
+ for name in dir(base):
+ if name.startswith('__') and name.endswith('__'):
+ # skip magic names
+ continue
+ dct[name] = MockCallback(return_value=None)
+ return type('TestProtocol', (base,) + base.__bases__, dct)()
+
+
+class TestSelector(selectors.BaseSelector):
+
+ def __init__(self):
+ self.keys = {}
+
+ def register(self, fileobj, events, data=None):
+ key = selectors.SelectorKey(fileobj, 0, events, data)
+ self.keys[fileobj] = key
+ return key
+
+ def unregister(self, fileobj):
+ return self.keys.pop(fileobj)
+
+ def select(self, timeout):
+ return []
+
+ def get_map(self):
+ return self.keys
+
+
+class TestLoop(base_events.BaseEventLoop):
+ """Loop for unittests.
+
+ It manages self time directly.
+ If something scheduled to be executed later then
+ on next loop iteration after all ready handlers done
+ generator passed to __init__ is calling.
+
+ Generator should be like this:
+
+ def gen():
+ ...
+ when = yield ...
+ ... = yield time_advance
+
+ Value returned by yield is absolute time of next scheduled handler.
+ Value passed to yield is time advance to move loop's time forward.
+ """
+
+ def __init__(self, gen=None):
+ super().__init__()
+
+ if gen is None:
+ def gen():
+ yield
+ self._check_on_close = False
+ else:
+ self._check_on_close = True
+
+ self._gen = gen()
+ next(self._gen)
+ self._time = 0
+ self._clock_resolution = 1e-9
+ self._timers = []
+ self._selector = TestSelector()
+
+ self.readers = {}
+ self.writers = {}
+ self.reset_counters()
+
+ def time(self):
+ return self._time
+
+ def advance_time(self, advance):
+ """Move test time forward."""
+ if advance:
+ self._time += advance
+
+ def close(self):
+ if self._check_on_close:
+ try:
+ self._gen.send(0)
+ except StopIteration:
+ pass
+ else: # pragma: no cover
+ raise AssertionError("Time generator is not finished")
+
+ def add_reader(self, fd, callback, *args):
+ self.readers[fd] = events.Handle(callback, args, self)
+
+ def remove_reader(self, fd):
+ self.remove_reader_count[fd] += 1
+ if fd in self.readers:
+ del self.readers[fd]
+ return True
+ else:
+ return False
+
+ def assert_reader(self, fd, callback, *args):
+ assert fd in self.readers, 'fd {} is not registered'.format(fd)
+ handle = self.readers[fd]
+ assert handle._callback == callback, '{!r} != {!r}'.format(
+ handle._callback, callback)
+ assert handle._args == args, '{!r} != {!r}'.format(
+ handle._args, args)
+
+ def add_writer(self, fd, callback, *args):
+ self.writers[fd] = events.Handle(callback, args, self)
+
+ def remove_writer(self, fd):
+ self.remove_writer_count[fd] += 1
+ if fd in self.writers:
+ del self.writers[fd]
+ return True
+ else:
+ return False
+
+ def assert_writer(self, fd, callback, *args):
+ assert fd in self.writers, 'fd {} is not registered'.format(fd)
+ handle = self.writers[fd]
+ assert handle._callback == callback, '{!r} != {!r}'.format(
+ handle._callback, callback)
+ assert handle._args == args, '{!r} != {!r}'.format(
+ handle._args, args)
+
+ def reset_counters(self):
+ self.remove_reader_count = collections.defaultdict(int)
+ self.remove_writer_count = collections.defaultdict(int)
+
+ def _run_once(self):
+ super()._run_once()
+ for when in self._timers:
+ advance = self._gen.send(when)
+ self.advance_time(advance)
+ self._timers = []
+
+ def call_at(self, when, callback, *args):
+ self._timers.append(when)
+ return super().call_at(when, callback, *args)
+
+ def _process_events(self, event_list):
+ return
+
+ def _write_to_self(self):
+ pass
+
+
+def MockCallback(**kwargs):
+ return mock.Mock(spec=['__call__'], **kwargs)
+
+
+class MockPattern(str):
+ """A regex based str with a fuzzy __eq__.
+
+ Use this helper with 'mock.assert_called_with', or anywhere
+ where a regex comparison between strings is needed.
+
+ For instance:
+ mock_call.assert_called_with(MockPattern('spam.*ham'))
+ """
+ def __eq__(self, other):
+ return bool(re.search(str(self), other, re.S))
diff --git a/Lib/asyncio/transports.py b/Lib/asyncio/transports.py
new file mode 100644
index 0000000..5f674f9
--- /dev/null
+++ b/Lib/asyncio/transports.py
@@ -0,0 +1,295 @@
+"""Abstract Transport class."""
+
+import sys
+
+_PY34 = sys.version_info >= (3, 4)
+
+__all__ = ['BaseTransport', 'ReadTransport', 'WriteTransport',
+ 'Transport', 'DatagramTransport', 'SubprocessTransport',
+ ]
+
+
+class BaseTransport:
+ """Base class for transports."""
+
+ def __init__(self, extra=None):
+ if extra is None:
+ extra = {}
+ self._extra = extra
+
+ def get_extra_info(self, name, default=None):
+ """Get optional transport information."""
+ return self._extra.get(name, default)
+
+ def close(self):
+ """Close the transport.
+
+ Buffered data will be flushed asynchronously. No more data
+ will be received. After all buffered data is flushed, the
+ protocol's connection_lost() method will (eventually) called
+ with None as its argument.
+ """
+ raise NotImplementedError
+
+
+class ReadTransport(BaseTransport):
+ """Interface for read-only transports."""
+
+ def pause_reading(self):
+ """Pause the receiving end.
+
+ No data will be passed to the protocol's data_received()
+ method until resume_reading() is called.
+ """
+ raise NotImplementedError
+
+ def resume_reading(self):
+ """Resume the receiving end.
+
+ Data received will once again be passed to the protocol's
+ data_received() method.
+ """
+ raise NotImplementedError
+
+
+class WriteTransport(BaseTransport):
+ """Interface for write-only transports."""
+
+ def set_write_buffer_limits(self, high=None, low=None):
+ """Set the high- and low-water limits for write flow control.
+
+ These two values control when to call the protocol's
+ pause_writing() and resume_writing() methods. If specified,
+ the low-water limit must be less than or equal to the
+ high-water limit. Neither value can be negative.
+
+ The defaults are implementation-specific. If only the
+ high-water limit is given, the low-water limit defaults to a
+ implementation-specific value less than or equal to the
+ high-water limit. Setting high to zero forces low to zero as
+ well, and causes pause_writing() to be called whenever the
+ buffer becomes non-empty. Setting low to zero causes
+ resume_writing() to be called only once the buffer is empty.
+ Use of zero for either limit is generally sub-optimal as it
+ reduces opportunities for doing I/O and computation
+ concurrently.
+ """
+ raise NotImplementedError
+
+ def get_write_buffer_size(self):
+ """Return the current size of the write buffer."""
+ raise NotImplementedError
+
+ def write(self, data):
+ """Write some data bytes to the transport.
+
+ This does not block; it buffers the data and arranges for it
+ to be sent out asynchronously.
+ """
+ raise NotImplementedError
+
+ def writelines(self, list_of_data):
+ """Write a list (or any iterable) of data bytes to the transport.
+
+ The default implementation concatenates the arguments and
+ calls write() on the result.
+ """
+ if not _PY34:
+ # In Python 3.3, bytes.join() doesn't handle memoryview.
+ list_of_data = (
+ bytes(data) if isinstance(data, memoryview) else data
+ for data in list_of_data)
+ self.write(b''.join(list_of_data))
+
+ def write_eof(self):
+ """Close the write end after flushing buffered data.
+
+ (This is like typing ^D into a UNIX program reading from stdin.)
+
+ Data may still be received.
+ """
+ raise NotImplementedError
+
+ def can_write_eof(self):
+ """Return True if this transport supports write_eof(), False if not."""
+ raise NotImplementedError
+
+ def abort(self):
+ """Close the transport immediately.
+
+ Buffered data will be lost. No more data will be received.
+ The protocol's connection_lost() method will (eventually) be
+ called with None as its argument.
+ """
+ raise NotImplementedError
+
+
+class Transport(ReadTransport, WriteTransport):
+ """Interface representing a bidirectional transport.
+
+ There may be several implementations, but typically, the user does
+ not implement new transports; rather, the platform provides some
+ useful transports that are implemented using the platform's best
+ practices.
+
+ The user never instantiates a transport directly; they call a
+ utility function, passing it a protocol factory and other
+ information necessary to create the transport and protocol. (E.g.
+ EventLoop.create_connection() or EventLoop.create_server().)
+
+ The utility function will asynchronously create a transport and a
+ protocol and hook them up by calling the protocol's
+ connection_made() method, passing it the transport.
+
+ The implementation here raises NotImplemented for every method
+ except writelines(), which calls write() in a loop.
+ """
+
+
+class DatagramTransport(BaseTransport):
+ """Interface for datagram (UDP) transports."""
+
+ def sendto(self, data, addr=None):
+ """Send data to the transport.
+
+ This does not block; it buffers the data and arranges for it
+ to be sent out asynchronously.
+ addr is target socket address.
+ If addr is None use target address pointed on transport creation.
+ """
+ raise NotImplementedError
+
+ def abort(self):
+ """Close the transport immediately.
+
+ Buffered data will be lost. No more data will be received.
+ The protocol's connection_lost() method will (eventually) be
+ called with None as its argument.
+ """
+ raise NotImplementedError
+
+
+class SubprocessTransport(BaseTransport):
+
+ def get_pid(self):
+ """Get subprocess id."""
+ raise NotImplementedError
+
+ def get_returncode(self):
+ """Get subprocess returncode.
+
+ See also
+ http://docs.python.org/3/library/subprocess#subprocess.Popen.returncode
+ """
+ raise NotImplementedError
+
+ def get_pipe_transport(self, fd):
+ """Get transport for pipe with number fd."""
+ raise NotImplementedError
+
+ def send_signal(self, signal):
+ """Send signal to subprocess.
+
+ See also:
+ docs.python.org/3/library/subprocess#subprocess.Popen.send_signal
+ """
+ raise NotImplementedError
+
+ def terminate(self):
+ """Stop the subprocess.
+
+ Alias for close() method.
+
+ On Posix OSs the method sends SIGTERM to the subprocess.
+ On Windows the Win32 API function TerminateProcess()
+ is called to stop the subprocess.
+
+ See also:
+ http://docs.python.org/3/library/subprocess#subprocess.Popen.terminate
+ """
+ raise NotImplementedError
+
+ def kill(self):
+ """Kill the subprocess.
+
+ On Posix OSs the function sends SIGKILL to the subprocess.
+ On Windows kill() is an alias for terminate().
+
+ See also:
+ http://docs.python.org/3/library/subprocess#subprocess.Popen.kill
+ """
+ raise NotImplementedError
+
+
+class _FlowControlMixin(Transport):
+ """All the logic for (write) flow control in a mix-in base class.
+
+ The subclass must implement get_write_buffer_size(). It must call
+ _maybe_pause_protocol() whenever the write buffer size increases,
+ and _maybe_resume_protocol() whenever it decreases. It may also
+ override set_write_buffer_limits() (e.g. to specify different
+ defaults).
+
+ The subclass constructor must call super().__init__(extra). This
+ will call set_write_buffer_limits().
+
+ The user may call set_write_buffer_limits() and
+ get_write_buffer_size(), and their protocol's pause_writing() and
+ resume_writing() may be called.
+ """
+
+ def __init__(self, extra=None):
+ super().__init__(extra)
+ self._protocol_paused = False
+ self._set_write_buffer_limits()
+
+ def _maybe_pause_protocol(self):
+ size = self.get_write_buffer_size()
+ if size <= self._high_water:
+ return
+ if not self._protocol_paused:
+ self._protocol_paused = True
+ try:
+ self._protocol.pause_writing()
+ except Exception as exc:
+ self._loop.call_exception_handler({
+ 'message': 'protocol.pause_writing() failed',
+ 'exception': exc,
+ 'transport': self,
+ 'protocol': self._protocol,
+ })
+
+ def _maybe_resume_protocol(self):
+ if (self._protocol_paused and
+ self.get_write_buffer_size() <= self._low_water):
+ self._protocol_paused = False
+ try:
+ self._protocol.resume_writing()
+ except Exception as exc:
+ self._loop.call_exception_handler({
+ 'message': 'protocol.resume_writing() failed',
+ 'exception': exc,
+ 'transport': self,
+ 'protocol': self._protocol,
+ })
+
+ def _set_write_buffer_limits(self, high=None, low=None):
+ if high is None:
+ if low is None:
+ high = 64*1024
+ else:
+ high = 4*low
+ if low is None:
+ low = high // 4
+ if not high >= low >= 0:
+ raise ValueError('high (%r) must be >= low (%r) must be >= 0' %
+ (high, low))
+ self._high_water = high
+ self._low_water = low
+
+ def set_write_buffer_limits(self, high=None, low=None):
+ self._set_write_buffer_limits(high=high, low=low)
+ self._maybe_pause_protocol()
+
+ def get_write_buffer_size(self):
+ raise NotImplementedError
diff --git a/Lib/asyncio/unix_events.py b/Lib/asyncio/unix_events.py
new file mode 100644
index 0000000..1fbdd31
--- /dev/null
+++ b/Lib/asyncio/unix_events.py
@@ -0,0 +1,842 @@
+"""Selector event loop for Unix with signal handling."""
+
+import errno
+import fcntl
+import os
+import signal
+import socket
+import stat
+import subprocess
+import sys
+import threading
+
+
+from . import base_events
+from . import base_subprocess
+from . import constants
+from . import events
+from . import selector_events
+from . import tasks
+from . import transports
+from .log import logger
+
+
+__all__ = ['SelectorEventLoop',
+ 'AbstractChildWatcher', 'SafeChildWatcher',
+ 'FastChildWatcher', 'DefaultEventLoopPolicy',
+ ]
+
+if sys.platform == 'win32': # pragma: no cover
+ raise ImportError('Signals are not really supported on Windows')
+
+
+class _UnixSelectorEventLoop(selector_events.BaseSelectorEventLoop):
+ """Unix event loop.
+
+ Adds signal handling and UNIX Domain Socket support to SelectorEventLoop.
+ """
+
+ def __init__(self, selector=None):
+ super().__init__(selector)
+ self._signal_handlers = {}
+
+ def _socketpair(self):
+ return socket.socketpair()
+
+ def close(self):
+ for sig in list(self._signal_handlers):
+ self.remove_signal_handler(sig)
+ super().close()
+
+ def add_signal_handler(self, sig, callback, *args):
+ """Add a handler for a signal. UNIX only.
+
+ Raise ValueError if the signal number is invalid or uncatchable.
+ Raise RuntimeError if there is a problem setting up the handler.
+ """
+ self._check_signal(sig)
+ try:
+ # set_wakeup_fd() raises ValueError if this is not the
+ # main thread. By calling it early we ensure that an
+ # event loop running in another thread cannot add a signal
+ # handler.
+ signal.set_wakeup_fd(self._csock.fileno())
+ except ValueError as exc:
+ raise RuntimeError(str(exc))
+
+ handle = events.Handle(callback, args, self)
+ self._signal_handlers[sig] = handle
+
+ try:
+ signal.signal(sig, self._handle_signal)
+ # Set SA_RESTART to limit EINTR occurrences.
+ signal.siginterrupt(sig, False)
+ except OSError as exc:
+ del self._signal_handlers[sig]
+ if not self._signal_handlers:
+ try:
+ signal.set_wakeup_fd(-1)
+ except ValueError as nexc:
+ logger.info('set_wakeup_fd(-1) failed: %s', nexc)
+
+ if exc.errno == errno.EINVAL:
+ raise RuntimeError('sig {} cannot be caught'.format(sig))
+ else:
+ raise
+
+ def _handle_signal(self, sig, arg):
+ """Internal helper that is the actual signal handler."""
+ handle = self._signal_handlers.get(sig)
+ if handle is None:
+ return # Assume it's some race condition.
+ if handle._cancelled:
+ self.remove_signal_handler(sig) # Remove it properly.
+ else:
+ self._add_callback_signalsafe(handle)
+
+ def remove_signal_handler(self, sig):
+ """Remove a handler for a signal. UNIX only.
+
+ Return True if a signal handler was removed, False if not.
+ """
+ self._check_signal(sig)
+ try:
+ del self._signal_handlers[sig]
+ except KeyError:
+ return False
+
+ if sig == signal.SIGINT:
+ handler = signal.default_int_handler
+ else:
+ handler = signal.SIG_DFL
+
+ try:
+ signal.signal(sig, handler)
+ except OSError as exc:
+ if exc.errno == errno.EINVAL:
+ raise RuntimeError('sig {} cannot be caught'.format(sig))
+ else:
+ raise
+
+ if not self._signal_handlers:
+ try:
+ signal.set_wakeup_fd(-1)
+ except ValueError as exc:
+ logger.info('set_wakeup_fd(-1) failed: %s', exc)
+
+ return True
+
+ def _check_signal(self, sig):
+ """Internal helper to validate a signal.
+
+ Raise ValueError if the signal number is invalid or uncatchable.
+ Raise RuntimeError if there is a problem setting up the handler.
+ """
+ if not isinstance(sig, int):
+ raise TypeError('sig must be an int, not {!r}'.format(sig))
+
+ if not (1 <= sig < signal.NSIG):
+ raise ValueError(
+ 'sig {} out of range(1, {})'.format(sig, signal.NSIG))
+
+ def _make_read_pipe_transport(self, pipe, protocol, waiter=None,
+ extra=None):
+ return _UnixReadPipeTransport(self, pipe, protocol, waiter, extra)
+
+ def _make_write_pipe_transport(self, pipe, protocol, waiter=None,
+ extra=None):
+ return _UnixWritePipeTransport(self, pipe, protocol, waiter, extra)
+
+ @tasks.coroutine
+ def _make_subprocess_transport(self, protocol, args, shell,
+ stdin, stdout, stderr, bufsize,
+ extra=None, **kwargs):
+ with events.get_child_watcher() as watcher:
+ transp = _UnixSubprocessTransport(self, protocol, args, shell,
+ stdin, stdout, stderr, bufsize,
+ extra=extra, **kwargs)
+ yield from transp._post_init()
+ watcher.add_child_handler(transp.get_pid(),
+ self._child_watcher_callback, transp)
+
+ return transp
+
+ def _child_watcher_callback(self, pid, returncode, transp):
+ self.call_soon_threadsafe(transp._process_exited, returncode)
+
+ @tasks.coroutine
+ def create_unix_connection(self, protocol_factory, path, *,
+ ssl=None, sock=None,
+ server_hostname=None):
+ assert server_hostname is None or isinstance(server_hostname, str)
+ if ssl:
+ if server_hostname is None:
+ raise ValueError(
+ 'you have to pass server_hostname when using ssl')
+ else:
+ if server_hostname is not None:
+ raise ValueError('server_hostname is only meaningful with ssl')
+
+ if path is not None:
+ if sock is not None:
+ raise ValueError(
+ 'path and sock can not be specified at the same time')
+
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0)
+ try:
+ sock.setblocking(False)
+ yield from self.sock_connect(sock, path)
+ except:
+ sock.close()
+ raise
+
+ else:
+ if sock is None:
+ raise ValueError('no path and sock were specified')
+ sock.setblocking(False)
+
+ transport, protocol = yield from self._create_connection_transport(
+ sock, protocol_factory, ssl, server_hostname)
+ return transport, protocol
+
+ @tasks.coroutine
+ def create_unix_server(self, protocol_factory, path=None, *,
+ sock=None, backlog=100, ssl=None):
+ if isinstance(ssl, bool):
+ raise TypeError('ssl argument must be an SSLContext or None')
+
+ if path is not None:
+ if sock is not None:
+ raise ValueError(
+ 'path and sock can not be specified at the same time')
+
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+
+ try:
+ sock.bind(path)
+ except OSError as exc:
+ sock.close()
+ if exc.errno == errno.EADDRINUSE:
+ # Let's improve the error message by adding
+ # with what exact address it occurs.
+ msg = 'Address {!r} is already in use'.format(path)
+ raise OSError(errno.EADDRINUSE, msg) from None
+ else:
+ raise
+ else:
+ if sock is None:
+ raise ValueError(
+ 'path was not specified, and no sock specified')
+
+ if sock.family != socket.AF_UNIX:
+ raise ValueError(
+ 'A UNIX Domain Socket was expected, got {!r}'.format(sock))
+
+ server = base_events.Server(self, [sock])
+ sock.listen(backlog)
+ sock.setblocking(False)
+ self._start_serving(protocol_factory, sock, ssl, server)
+ return server
+
+
+def _set_nonblocking(fd):
+ flags = fcntl.fcntl(fd, fcntl.F_GETFL)
+ flags = flags | os.O_NONBLOCK
+ fcntl.fcntl(fd, fcntl.F_SETFL, flags)
+
+
+class _UnixReadPipeTransport(transports.ReadTransport):
+
+ max_size = 256 * 1024 # max bytes we read in one event loop iteration
+
+ def __init__(self, loop, pipe, protocol, waiter=None, extra=None):
+ super().__init__(extra)
+ self._extra['pipe'] = pipe
+ self._loop = loop
+ self._pipe = pipe
+ self._fileno = pipe.fileno()
+ mode = os.fstat(self._fileno).st_mode
+ if not (stat.S_ISFIFO(mode) or
+ stat.S_ISSOCK(mode) or
+ stat.S_ISCHR(mode)):
+ raise ValueError("Pipe transport is for pipes/sockets only.")
+ _set_nonblocking(self._fileno)
+ self._protocol = protocol
+ self._closing = False
+ self._loop.add_reader(self._fileno, self._read_ready)
+ self._loop.call_soon(self._protocol.connection_made, self)
+ if waiter is not None:
+ self._loop.call_soon(waiter.set_result, None)
+
+ def _read_ready(self):
+ try:
+ data = os.read(self._fileno, self.max_size)
+ except (BlockingIOError, InterruptedError):
+ pass
+ except OSError as exc:
+ self._fatal_error(exc, 'Fatal read error on pipe transport')
+ else:
+ if data:
+ self._protocol.data_received(data)
+ else:
+ self._closing = True
+ self._loop.remove_reader(self._fileno)
+ self._loop.call_soon(self._protocol.eof_received)
+ self._loop.call_soon(self._call_connection_lost, None)
+
+ def pause_reading(self):
+ self._loop.remove_reader(self._fileno)
+
+ def resume_reading(self):
+ self._loop.add_reader(self._fileno, self._read_ready)
+
+ def close(self):
+ if not self._closing:
+ self._close(None)
+
+ def _fatal_error(self, exc, message='Fatal error on pipe transport'):
+ # should be called by exception handler only
+ if not (isinstance(exc, OSError) and exc.errno == errno.EIO):
+ self._loop.call_exception_handler({
+ 'message': message,
+ 'exception': exc,
+ 'transport': self,
+ 'protocol': self._protocol,
+ })
+ self._close(exc)
+
+ def _close(self, exc):
+ self._closing = True
+ self._loop.remove_reader(self._fileno)
+ self._loop.call_soon(self._call_connection_lost, exc)
+
+ def _call_connection_lost(self, exc):
+ try:
+ self._protocol.connection_lost(exc)
+ finally:
+ self._pipe.close()
+ self._pipe = None
+ self._protocol = None
+ self._loop = None
+
+
+class _UnixWritePipeTransport(transports._FlowControlMixin,
+ transports.WriteTransport):
+
+ def __init__(self, loop, pipe, protocol, waiter=None, extra=None):
+ super().__init__(extra)
+ self._extra['pipe'] = pipe
+ self._loop = loop
+ self._pipe = pipe
+ self._fileno = pipe.fileno()
+ mode = os.fstat(self._fileno).st_mode
+ is_socket = stat.S_ISSOCK(mode)
+ if not (is_socket or
+ stat.S_ISFIFO(mode) or
+ stat.S_ISCHR(mode)):
+ raise ValueError("Pipe transport is only for "
+ "pipes, sockets and character devices")
+ _set_nonblocking(self._fileno)
+ self._protocol = protocol
+ self._buffer = []
+ self._conn_lost = 0
+ self._closing = False # Set when close() or write_eof() called.
+
+ # On AIX, the reader trick only works for sockets.
+ # On other platforms it works for pipes and sockets.
+ # (Exception: OS X 10.4? Issue #19294.)
+ if is_socket or not sys.platform.startswith("aix"):
+ self._loop.add_reader(self._fileno, self._read_ready)
+
+ self._loop.call_soon(self._protocol.connection_made, self)
+ if waiter is not None:
+ self._loop.call_soon(waiter.set_result, None)
+
+ def get_write_buffer_size(self):
+ return sum(len(data) for data in self._buffer)
+
+ def _read_ready(self):
+ # Pipe was closed by peer.
+ if self._buffer:
+ self._close(BrokenPipeError())
+ else:
+ self._close()
+
+ def write(self, data):
+ assert isinstance(data, (bytes, bytearray, memoryview)), repr(data)
+ if isinstance(data, bytearray):
+ data = memoryview(data)
+ if not data:
+ return
+
+ if self._conn_lost or self._closing:
+ if self._conn_lost >= constants.LOG_THRESHOLD_FOR_CONNLOST_WRITES:
+ logger.warning('pipe closed by peer or '
+ 'os.write(pipe, data) raised exception.')
+ self._conn_lost += 1
+ return
+
+ if not self._buffer:
+ # Attempt to send it right away first.
+ try:
+ n = os.write(self._fileno, data)
+ except (BlockingIOError, InterruptedError):
+ n = 0
+ except Exception as exc:
+ self._conn_lost += 1
+ self._fatal_error(exc, 'Fatal write error on pipe transport')
+ return
+ if n == len(data):
+ return
+ elif n > 0:
+ data = data[n:]
+ self._loop.add_writer(self._fileno, self._write_ready)
+
+ self._buffer.append(data)
+ self._maybe_pause_protocol()
+
+ def _write_ready(self):
+ data = b''.join(self._buffer)
+ assert data, 'Data should not be empty'
+
+ self._buffer.clear()
+ try:
+ n = os.write(self._fileno, data)
+ except (BlockingIOError, InterruptedError):
+ self._buffer.append(data)
+ except Exception as exc:
+ self._conn_lost += 1
+ # Remove writer here, _fatal_error() doesn't it
+ # because _buffer is empty.
+ self._loop.remove_writer(self._fileno)
+ self._fatal_error(exc, 'Fatal write error on pipe transport')
+ else:
+ if n == len(data):
+ self._loop.remove_writer(self._fileno)
+ self._maybe_resume_protocol() # May append to buffer.
+ if not self._buffer and self._closing:
+ self._loop.remove_reader(self._fileno)
+ self._call_connection_lost(None)
+ return
+ elif n > 0:
+ data = data[n:]
+
+ self._buffer.append(data) # Try again later.
+
+ def can_write_eof(self):
+ return True
+
+ # TODO: Make the relationships between write_eof(), close(),
+ # abort(), _fatal_error() and _close() more straightforward.
+
+ def write_eof(self):
+ if self._closing:
+ return
+ assert self._pipe
+ self._closing = True
+ if not self._buffer:
+ self._loop.remove_reader(self._fileno)
+ self._loop.call_soon(self._call_connection_lost, None)
+
+ def close(self):
+ if not self._closing:
+ # write_eof is all what we needed to close the write pipe
+ self.write_eof()
+
+ def abort(self):
+ self._close(None)
+
+ def _fatal_error(self, exc, message='Fatal error on pipe transport'):
+ # should be called by exception handler only
+ if not isinstance(exc, (BrokenPipeError, ConnectionResetError)):
+ self._loop.call_exception_handler({
+ 'message': message,
+ 'exception': exc,
+ 'transport': self,
+ 'protocol': self._protocol,
+ })
+ self._close(exc)
+
+ def _close(self, exc=None):
+ self._closing = True
+ if self._buffer:
+ self._loop.remove_writer(self._fileno)
+ self._buffer.clear()
+ self._loop.remove_reader(self._fileno)
+ self._loop.call_soon(self._call_connection_lost, exc)
+
+ def _call_connection_lost(self, exc):
+ try:
+ self._protocol.connection_lost(exc)
+ finally:
+ self._pipe.close()
+ self._pipe = None
+ self._protocol = None
+ self._loop = None
+
+
+class _UnixSubprocessTransport(base_subprocess.BaseSubprocessTransport):
+
+ def _start(self, args, shell, stdin, stdout, stderr, bufsize, **kwargs):
+ stdin_w = None
+ if stdin == subprocess.PIPE:
+ # Use a socket pair for stdin, since not all platforms
+ # support selecting read events on the write end of a
+ # socket (which we use in order to detect closing of the
+ # other end). Notably this is needed on AIX, and works
+ # just fine on other platforms.
+ stdin, stdin_w = self._loop._socketpair()
+ self._proc = subprocess.Popen(
+ args, shell=shell, stdin=stdin, stdout=stdout, stderr=stderr,
+ universal_newlines=False, bufsize=bufsize, **kwargs)
+ if stdin_w is not None:
+ stdin.close()
+ self._proc.stdin = open(stdin_w.detach(), 'rb', buffering=bufsize)
+
+
+class AbstractChildWatcher:
+ """Abstract base class for monitoring child processes.
+
+ Objects derived from this class monitor a collection of subprocesses and
+ report their termination or interruption by a signal.
+
+ New callbacks are registered with .add_child_handler(). Starting a new
+ process must be done within a 'with' block to allow the watcher to suspend
+ its activity until the new process if fully registered (this is needed to
+ prevent a race condition in some implementations).
+
+ Example:
+ with watcher:
+ proc = subprocess.Popen("sleep 1")
+ watcher.add_child_handler(proc.pid, callback)
+
+ Notes:
+ Implementations of this class must be thread-safe.
+
+ Since child watcher objects may catch the SIGCHLD signal and call
+ waitpid(-1), there should be only one active object per process.
+ """
+
+ def add_child_handler(self, pid, callback, *args):
+ """Register a new child handler.
+
+ Arrange for callback(pid, returncode, *args) to be called when
+ process 'pid' terminates. Specifying another callback for the same
+ process replaces the previous handler.
+
+ Note: callback() must be thread-safe
+ """
+ raise NotImplementedError()
+
+ def remove_child_handler(self, pid):
+ """Removes the handler for process 'pid'.
+
+ The function returns True if the handler was successfully removed,
+ False if there was nothing to remove."""
+
+ raise NotImplementedError()
+
+ def attach_loop(self, loop):
+ """Attach the watcher to an event loop.
+
+ If the watcher was previously attached to an event loop, then it is
+ first detached before attaching to the new loop.
+
+ Note: loop may be None.
+ """
+ raise NotImplementedError()
+
+ def close(self):
+ """Close the watcher.
+
+ This must be called to make sure that any underlying resource is freed.
+ """
+ raise NotImplementedError()
+
+ def __enter__(self):
+ """Enter the watcher's context and allow starting new processes
+
+ This function must return self"""
+ raise NotImplementedError()
+
+ def __exit__(self, a, b, c):
+ """Exit the watcher's context"""
+ raise NotImplementedError()
+
+
+class BaseChildWatcher(AbstractChildWatcher):
+
+ def __init__(self):
+ self._loop = None
+
+ def close(self):
+ self.attach_loop(None)
+
+ def _do_waitpid(self, expected_pid):
+ raise NotImplementedError()
+
+ def _do_waitpid_all(self):
+ raise NotImplementedError()
+
+ def attach_loop(self, loop):
+ assert loop is None or isinstance(loop, events.AbstractEventLoop)
+
+ if self._loop is not None:
+ self._loop.remove_signal_handler(signal.SIGCHLD)
+
+ self._loop = loop
+ if loop is not None:
+ loop.add_signal_handler(signal.SIGCHLD, self._sig_chld)
+
+ # Prevent a race condition in case a child terminated
+ # during the switch.
+ self._do_waitpid_all()
+
+ def _sig_chld(self):
+ try:
+ self._do_waitpid_all()
+ except Exception as exc:
+ # self._loop should always be available here
+ # as '_sig_chld' is added as a signal handler
+ # in 'attach_loop'
+ self._loop.call_exception_handler({
+ 'message': 'Unknown exception in SIGCHLD handler',
+ 'exception': exc,
+ })
+
+ def _compute_returncode(self, status):
+ if os.WIFSIGNALED(status):
+ # The child process died because of a signal.
+ return -os.WTERMSIG(status)
+ elif os.WIFEXITED(status):
+ # The child process exited (e.g sys.exit()).
+ return os.WEXITSTATUS(status)
+ else:
+ # The child exited, but we don't understand its status.
+ # This shouldn't happen, but if it does, let's just
+ # return that status; perhaps that helps debug it.
+ return status
+
+
+class SafeChildWatcher(BaseChildWatcher):
+ """'Safe' child watcher implementation.
+
+ This implementation avoids disrupting other code spawning processes by
+ polling explicitly each process in the SIGCHLD handler instead of calling
+ os.waitpid(-1).
+
+ This is a safe solution but it has a significant overhead when handling a
+ big number of children (O(n) each time SIGCHLD is raised)
+ """
+
+ def __init__(self):
+ super().__init__()
+ self._callbacks = {}
+
+ def close(self):
+ self._callbacks.clear()
+ super().close()
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, a, b, c):
+ pass
+
+ def add_child_handler(self, pid, callback, *args):
+ self._callbacks[pid] = callback, args
+
+ # Prevent a race condition in case the child is already terminated.
+ self._do_waitpid(pid)
+
+ def remove_child_handler(self, pid):
+ try:
+ del self._callbacks[pid]
+ return True
+ except KeyError:
+ return False
+
+ def _do_waitpid_all(self):
+
+ for pid in list(self._callbacks):
+ self._do_waitpid(pid)
+
+ def _do_waitpid(self, expected_pid):
+ assert expected_pid > 0
+
+ try:
+ pid, status = os.waitpid(expected_pid, os.WNOHANG)
+ except ChildProcessError:
+ # The child process is already reaped
+ # (may happen if waitpid() is called elsewhere).
+ pid = expected_pid
+ returncode = 255
+ logger.warning(
+ "Unknown child process pid %d, will report returncode 255",
+ pid)
+ else:
+ if pid == 0:
+ # The child process is still alive.
+ return
+
+ returncode = self._compute_returncode(status)
+
+ try:
+ callback, args = self._callbacks.pop(pid)
+ except KeyError: # pragma: no cover
+ # May happen if .remove_child_handler() is called
+ # after os.waitpid() returns.
+ pass
+ else:
+ callback(pid, returncode, *args)
+
+
+class FastChildWatcher(BaseChildWatcher):
+ """'Fast' child watcher implementation.
+
+ This implementation reaps every terminated processes by calling
+ os.waitpid(-1) directly, possibly breaking other code spawning processes
+ and waiting for their termination.
+
+ There is no noticeable overhead when handling a big number of children
+ (O(1) each time a child terminates).
+ """
+ def __init__(self):
+ super().__init__()
+ self._callbacks = {}
+ self._lock = threading.Lock()
+ self._zombies = {}
+ self._forks = 0
+
+ def close(self):
+ self._callbacks.clear()
+ self._zombies.clear()
+ super().close()
+
+ def __enter__(self):
+ with self._lock:
+ self._forks += 1
+
+ return self
+
+ def __exit__(self, a, b, c):
+ with self._lock:
+ self._forks -= 1
+
+ if self._forks or not self._zombies:
+ return
+
+ collateral_victims = str(self._zombies)
+ self._zombies.clear()
+
+ logger.warning(
+ "Caught subprocesses termination from unknown pids: %s",
+ collateral_victims)
+
+ def add_child_handler(self, pid, callback, *args):
+ assert self._forks, "Must use the context manager"
+ with self._lock:
+ try:
+ returncode = self._zombies.pop(pid)
+ except KeyError:
+ # The child is running.
+ self._callbacks[pid] = callback, args
+ return
+
+ # The child is dead already. We can fire the callback.
+ callback(pid, returncode, *args)
+
+ def remove_child_handler(self, pid):
+ try:
+ del self._callbacks[pid]
+ return True
+ except KeyError:
+ return False
+
+ def _do_waitpid_all(self):
+ # Because of signal coalescing, we must keep calling waitpid() as
+ # long as we're able to reap a child.
+ while True:
+ try:
+ pid, status = os.waitpid(-1, os.WNOHANG)
+ except ChildProcessError:
+ # No more child processes exist.
+ return
+ else:
+ if pid == 0:
+ # A child process is still alive.
+ return
+
+ returncode = self._compute_returncode(status)
+
+ with self._lock:
+ try:
+ callback, args = self._callbacks.pop(pid)
+ except KeyError:
+ # unknown child
+ if self._forks:
+ # It may not be registered yet.
+ self._zombies[pid] = returncode
+ continue
+ callback = None
+
+ if callback is None:
+ logger.warning(
+ "Caught subprocess termination from unknown pid: "
+ "%d -> %d", pid, returncode)
+ else:
+ callback(pid, returncode, *args)
+
+
+class _UnixDefaultEventLoopPolicy(events.BaseDefaultEventLoopPolicy):
+ """XXX"""
+ _loop_factory = _UnixSelectorEventLoop
+
+ def __init__(self):
+ super().__init__()
+ self._watcher = None
+
+ def _init_watcher(self):
+ with events._lock:
+ if self._watcher is None: # pragma: no branch
+ self._watcher = SafeChildWatcher()
+ if isinstance(threading.current_thread(),
+ threading._MainThread):
+ self._watcher.attach_loop(self._local._loop)
+
+ def set_event_loop(self, loop):
+ """Set the event loop.
+
+ As a side effect, if a child watcher was set before, then calling
+ .set_event_loop() from the main thread will call .attach_loop(loop) on
+ the child watcher.
+ """
+
+ super().set_event_loop(loop)
+
+ if self._watcher is not None and \
+ isinstance(threading.current_thread(), threading._MainThread):
+ self._watcher.attach_loop(loop)
+
+ def get_child_watcher(self):
+ """Get the child watcher
+
+ If not yet set, a SafeChildWatcher object is automatically created.
+ """
+ if self._watcher is None:
+ self._init_watcher()
+
+ return self._watcher
+
+ def set_child_watcher(self, watcher):
+ """Set the child watcher"""
+
+ assert watcher is None or isinstance(watcher, AbstractChildWatcher)
+
+ if self._watcher is not None:
+ self._watcher.close()
+
+ self._watcher = watcher
+
+SelectorEventLoop = _UnixSelectorEventLoop
+DefaultEventLoopPolicy = _UnixDefaultEventLoopPolicy
diff --git a/Lib/asyncio/windows_events.py b/Lib/asyncio/windows_events.py
new file mode 100644
index 0000000..19f2588
--- /dev/null
+++ b/Lib/asyncio/windows_events.py
@@ -0,0 +1,489 @@
+"""Selector and proactor event loops for Windows."""
+
+import _winapi
+import errno
+import math
+import socket
+import struct
+import weakref
+
+from . import events
+from . import base_subprocess
+from . import futures
+from . import proactor_events
+from . import selector_events
+from . import tasks
+from . import windows_utils
+from .log import logger
+from . import _overlapped
+
+
+__all__ = ['SelectorEventLoop', 'ProactorEventLoop', 'IocpProactor',
+ 'DefaultEventLoopPolicy',
+ ]
+
+
+NULL = 0
+INFINITE = 0xffffffff
+ERROR_CONNECTION_REFUSED = 1225
+ERROR_CONNECTION_ABORTED = 1236
+
+
+class _OverlappedFuture(futures.Future):
+ """Subclass of Future which represents an overlapped operation.
+
+ Cancelling it will immediately cancel the overlapped operation.
+ """
+
+ def __init__(self, ov, *, loop=None):
+ super().__init__(loop=loop)
+ self.ov = ov
+
+ def cancel(self):
+ try:
+ self.ov.cancel()
+ except OSError:
+ pass
+ return super().cancel()
+
+
+class _WaitHandleFuture(futures.Future):
+ """Subclass of Future which represents a wait handle."""
+
+ def __init__(self, wait_handle, *, loop=None):
+ super().__init__(loop=loop)
+ self._wait_handle = wait_handle
+
+ def cancel(self):
+ super().cancel()
+ try:
+ _overlapped.UnregisterWait(self._wait_handle)
+ except OSError as e:
+ if e.winerror != _overlapped.ERROR_IO_PENDING:
+ raise
+
+
+class PipeServer(object):
+ """Class representing a pipe server.
+
+ This is much like a bound, listening socket.
+ """
+ def __init__(self, address):
+ self._address = address
+ self._free_instances = weakref.WeakSet()
+ self._pipe = self._server_pipe_handle(True)
+
+ def _get_unconnected_pipe(self):
+ # Create new instance and return previous one. This ensures
+ # that (until the server is closed) there is always at least
+ # one pipe handle for address. Therefore if a client attempt
+ # to connect it will not fail with FileNotFoundError.
+ tmp, self._pipe = self._pipe, self._server_pipe_handle(False)
+ return tmp
+
+ def _server_pipe_handle(self, first):
+ # Return a wrapper for a new pipe handle.
+ if self._address is None:
+ return None
+ flags = _winapi.PIPE_ACCESS_DUPLEX | _winapi.FILE_FLAG_OVERLAPPED
+ if first:
+ flags |= _winapi.FILE_FLAG_FIRST_PIPE_INSTANCE
+ h = _winapi.CreateNamedPipe(
+ self._address, flags,
+ _winapi.PIPE_TYPE_MESSAGE | _winapi.PIPE_READMODE_MESSAGE |
+ _winapi.PIPE_WAIT,
+ _winapi.PIPE_UNLIMITED_INSTANCES,
+ windows_utils.BUFSIZE, windows_utils.BUFSIZE,
+ _winapi.NMPWAIT_WAIT_FOREVER, _winapi.NULL)
+ pipe = windows_utils.PipeHandle(h)
+ self._free_instances.add(pipe)
+ return pipe
+
+ def close(self):
+ # Close all instances which have not been connected to by a client.
+ if self._address is not None:
+ for pipe in self._free_instances:
+ pipe.close()
+ self._pipe = None
+ self._address = None
+ self._free_instances.clear()
+
+ __del__ = close
+
+
+class _WindowsSelectorEventLoop(selector_events.BaseSelectorEventLoop):
+ """Windows version of selector event loop."""
+
+ def _socketpair(self):
+ return windows_utils.socketpair()
+
+
+class ProactorEventLoop(proactor_events.BaseProactorEventLoop):
+ """Windows version of proactor event loop using IOCP."""
+
+ def __init__(self, proactor=None):
+ if proactor is None:
+ proactor = IocpProactor()
+ super().__init__(proactor)
+
+ def _socketpair(self):
+ return windows_utils.socketpair()
+
+ @tasks.coroutine
+ def create_pipe_connection(self, protocol_factory, address):
+ f = self._proactor.connect_pipe(address)
+ pipe = yield from f
+ protocol = protocol_factory()
+ trans = self._make_duplex_pipe_transport(pipe, protocol,
+ extra={'addr': address})
+ return trans, protocol
+
+ @tasks.coroutine
+ def start_serving_pipe(self, protocol_factory, address):
+ server = PipeServer(address)
+
+ def loop(f=None):
+ pipe = None
+ try:
+ if f:
+ pipe = f.result()
+ server._free_instances.discard(pipe)
+ protocol = protocol_factory()
+ self._make_duplex_pipe_transport(
+ pipe, protocol, extra={'addr': address})
+ pipe = server._get_unconnected_pipe()
+ if pipe is None:
+ return
+ f = self._proactor.accept_pipe(pipe)
+ except OSError as exc:
+ if pipe and pipe.fileno() != -1:
+ self.call_exception_handler({
+ 'message': 'Pipe accept failed',
+ 'exception': exc,
+ 'pipe': pipe,
+ })
+ pipe.close()
+ except futures.CancelledError:
+ if pipe:
+ pipe.close()
+ else:
+ f.add_done_callback(loop)
+
+ self.call_soon(loop)
+ return [server]
+
+ @tasks.coroutine
+ def _make_subprocess_transport(self, protocol, args, shell,
+ stdin, stdout, stderr, bufsize,
+ extra=None, **kwargs):
+ transp = _WindowsSubprocessTransport(self, protocol, args, shell,
+ stdin, stdout, stderr, bufsize,
+ extra=extra, **kwargs)
+ yield from transp._post_init()
+ return transp
+
+
+class IocpProactor:
+ """Proactor implementation using IOCP."""
+
+ def __init__(self, concurrency=0xffffffff):
+ self._loop = None
+ self._results = []
+ self._iocp = _overlapped.CreateIoCompletionPort(
+ _overlapped.INVALID_HANDLE_VALUE, NULL, 0, concurrency)
+ self._cache = {}
+ self._registered = weakref.WeakSet()
+ self._stopped_serving = weakref.WeakSet()
+
+ def set_loop(self, loop):
+ self._loop = loop
+
+ def select(self, timeout=None):
+ if not self._results:
+ self._poll(timeout)
+ tmp = self._results
+ self._results = []
+ return tmp
+
+ def recv(self, conn, nbytes, flags=0):
+ self._register_with_iocp(conn)
+ ov = _overlapped.Overlapped(NULL)
+ if isinstance(conn, socket.socket):
+ ov.WSARecv(conn.fileno(), nbytes, flags)
+ else:
+ ov.ReadFile(conn.fileno(), nbytes)
+
+ def finish_recv(trans, key, ov):
+ try:
+ return ov.getresult()
+ except OSError as exc:
+ if exc.winerror == _overlapped.ERROR_NETNAME_DELETED:
+ raise ConnectionResetError(*exc.args)
+ else:
+ raise
+
+ return self._register(ov, conn, finish_recv)
+
+ def send(self, conn, buf, flags=0):
+ self._register_with_iocp(conn)
+ ov = _overlapped.Overlapped(NULL)
+ if isinstance(conn, socket.socket):
+ ov.WSASend(conn.fileno(), buf, flags)
+ else:
+ ov.WriteFile(conn.fileno(), buf)
+
+ def finish_send(trans, key, ov):
+ try:
+ return ov.getresult()
+ except OSError as exc:
+ if exc.winerror == _overlapped.ERROR_NETNAME_DELETED:
+ raise ConnectionResetError(*exc.args)
+ else:
+ raise
+
+ return self._register(ov, conn, finish_send)
+
+ def accept(self, listener):
+ self._register_with_iocp(listener)
+ conn = self._get_accept_socket(listener.family)
+ ov = _overlapped.Overlapped(NULL)
+ ov.AcceptEx(listener.fileno(), conn.fileno())
+
+ def finish_accept(trans, key, ov):
+ ov.getresult()
+ # Use SO_UPDATE_ACCEPT_CONTEXT so getsockname() etc work.
+ buf = struct.pack('@P', listener.fileno())
+ conn.setsockopt(socket.SOL_SOCKET,
+ _overlapped.SO_UPDATE_ACCEPT_CONTEXT, buf)
+ conn.settimeout(listener.gettimeout())
+ return conn, conn.getpeername()
+
+ @tasks.coroutine
+ def accept_coro(future, conn):
+ # Coroutine closing the accept socket if the future is cancelled
+ try:
+ yield from future
+ except futures.CancelledError:
+ conn.close()
+ raise
+
+ future = self._register(ov, listener, finish_accept)
+ coro = accept_coro(future, conn)
+ tasks.async(coro, loop=self._loop)
+ return future
+
+ def connect(self, conn, address):
+ self._register_with_iocp(conn)
+ # The socket needs to be locally bound before we call ConnectEx().
+ try:
+ _overlapped.BindLocal(conn.fileno(), conn.family)
+ except OSError as e:
+ if e.winerror != errno.WSAEINVAL:
+ raise
+ # Probably already locally bound; check using getsockname().
+ if conn.getsockname()[1] == 0:
+ raise
+ ov = _overlapped.Overlapped(NULL)
+ ov.ConnectEx(conn.fileno(), address)
+
+ def finish_connect(trans, key, ov):
+ ov.getresult()
+ # Use SO_UPDATE_CONNECT_CONTEXT so getsockname() etc work.
+ conn.setsockopt(socket.SOL_SOCKET,
+ _overlapped.SO_UPDATE_CONNECT_CONTEXT, 0)
+ return conn
+
+ return self._register(ov, conn, finish_connect)
+
+ def accept_pipe(self, pipe):
+ self._register_with_iocp(pipe)
+ ov = _overlapped.Overlapped(NULL)
+ ov.ConnectNamedPipe(pipe.fileno())
+
+ def finish_accept_pipe(trans, key, ov):
+ ov.getresult()
+ return pipe
+
+ return self._register(ov, pipe, finish_accept_pipe)
+
+ def connect_pipe(self, address):
+ ov = _overlapped.Overlapped(NULL)
+ ov.WaitNamedPipeAndConnect(address, self._iocp, ov.address)
+
+ def finish_connect_pipe(err, handle, ov):
+ # err, handle were arguments passed to PostQueuedCompletionStatus()
+ # in a function run in a thread pool.
+ if err == _overlapped.ERROR_SEM_TIMEOUT:
+ # Connection did not succeed within time limit.
+ msg = _overlapped.FormatMessage(err)
+ raise ConnectionRefusedError(0, msg, None, err)
+ elif err != 0:
+ msg = _overlapped.FormatMessage(err)
+ raise OSError(0, msg, None, err)
+ else:
+ return windows_utils.PipeHandle(handle)
+
+ return self._register(ov, None, finish_connect_pipe, wait_for_post=True)
+
+ def wait_for_handle(self, handle, timeout=None):
+ if timeout is None:
+ ms = _winapi.INFINITE
+ else:
+ # RegisterWaitForSingleObject() has a resolution of 1 millisecond,
+ # round away from zero to wait *at least* timeout seconds.
+ ms = math.ceil(timeout * 1e3)
+
+ # We only create ov so we can use ov.address as a key for the cache.
+ ov = _overlapped.Overlapped(NULL)
+ wh = _overlapped.RegisterWaitWithQueue(
+ handle, self._iocp, ov.address, ms)
+ f = _WaitHandleFuture(wh, loop=self._loop)
+
+ def finish_wait_for_handle(trans, key, ov):
+ if not f.cancelled():
+ try:
+ _overlapped.UnregisterWait(wh)
+ except OSError as e:
+ if e.winerror != _overlapped.ERROR_IO_PENDING:
+ raise
+ # Note that this second wait means that we should only use
+ # this with handles types where a successful wait has no
+ # effect. So events or processes are all right, but locks
+ # or semaphores are not. Also note if the handle is
+ # signalled and then quickly reset, then we may return
+ # False even though we have not timed out.
+ return (_winapi.WaitForSingleObject(handle, 0) ==
+ _winapi.WAIT_OBJECT_0)
+
+ self._cache[ov.address] = (f, ov, None, finish_wait_for_handle)
+ return f
+
+ def _register_with_iocp(self, obj):
+ # To get notifications of finished ops on this objects sent to the
+ # completion port, were must register the handle.
+ if obj not in self._registered:
+ self._registered.add(obj)
+ _overlapped.CreateIoCompletionPort(obj.fileno(), self._iocp, 0, 0)
+ # XXX We could also use SetFileCompletionNotificationModes()
+ # to avoid sending notifications to completion port of ops
+ # that succeed immediately.
+
+ def _register(self, ov, obj, callback, wait_for_post=False):
+ # Return a future which will be set with the result of the
+ # operation when it completes. The future's value is actually
+ # the value returned by callback().
+ f = _OverlappedFuture(ov, loop=self._loop)
+ if ov.pending or wait_for_post:
+ # Register the overlapped operation for later. Note that
+ # we only store obj to prevent it from being garbage
+ # collected too early.
+ self._cache[ov.address] = (f, ov, obj, callback)
+ else:
+ # The operation has completed, so no need to postpone the
+ # work. We cannot take this short cut if we need the
+ # NumberOfBytes, CompletionKey values returned by
+ # PostQueuedCompletionStatus().
+ try:
+ value = callback(None, None, ov)
+ except OSError as e:
+ f.set_exception(e)
+ else:
+ f.set_result(value)
+ return f
+
+ def _get_accept_socket(self, family):
+ s = socket.socket(family)
+ s.settimeout(0)
+ return s
+
+ def _poll(self, timeout=None):
+ if timeout is None:
+ ms = INFINITE
+ elif timeout < 0:
+ raise ValueError("negative timeout")
+ else:
+ # GetQueuedCompletionStatus() has a resolution of 1 millisecond,
+ # round away from zero to wait *at least* timeout seconds.
+ ms = math.ceil(timeout * 1e3)
+ if ms >= INFINITE:
+ raise ValueError("timeout too big")
+ while True:
+ status = _overlapped.GetQueuedCompletionStatus(self._iocp, ms)
+ if status is None:
+ return
+ err, transferred, key, address = status
+ try:
+ f, ov, obj, callback = self._cache.pop(address)
+ except KeyError:
+ # key is either zero, or it is used to return a pipe
+ # handle which should be closed to avoid a leak.
+ if key not in (0, _overlapped.INVALID_HANDLE_VALUE):
+ _winapi.CloseHandle(key)
+ ms = 0
+ continue
+ if obj in self._stopped_serving:
+ f.cancel()
+ elif not f.cancelled():
+ try:
+ value = callback(transferred, key, ov)
+ except OSError as e:
+ f.set_exception(e)
+ self._results.append(f)
+ else:
+ f.set_result(value)
+ self._results.append(f)
+ ms = 0
+
+ def _stop_serving(self, obj):
+ # obj is a socket or pipe handle. It will be closed in
+ # BaseProactorEventLoop._stop_serving() which will make any
+ # pending operations fail quickly.
+ self._stopped_serving.add(obj)
+
+ def close(self):
+ # Cancel remaining registered operations.
+ for address, (f, ov, obj, callback) in list(self._cache.items()):
+ if obj is None:
+ # The operation was started with connect_pipe() which
+ # queues a task to Windows' thread pool. This cannot
+ # be cancelled, so just forget it.
+ del self._cache[address]
+ else:
+ try:
+ ov.cancel()
+ except OSError:
+ pass
+
+ while self._cache:
+ if not self._poll(1):
+ logger.debug('taking long time to close proactor')
+
+ self._results = []
+ if self._iocp is not None:
+ _winapi.CloseHandle(self._iocp)
+ self._iocp = None
+
+
+class _WindowsSubprocessTransport(base_subprocess.BaseSubprocessTransport):
+
+ def _start(self, args, shell, stdin, stdout, stderr, bufsize, **kwargs):
+ self._proc = windows_utils.Popen(
+ args, shell=shell, stdin=stdin, stdout=stdout, stderr=stderr,
+ bufsize=bufsize, **kwargs)
+
+ def callback(f):
+ returncode = self._proc.poll()
+ self._process_exited(returncode)
+
+ f = self._loop._proactor.wait_for_handle(int(self._proc._handle))
+ f.add_done_callback(callback)
+
+
+SelectorEventLoop = _WindowsSelectorEventLoop
+
+
+class _WindowsDefaultEventLoopPolicy(events.BaseDefaultEventLoopPolicy):
+ _loop_factory = SelectorEventLoop
+
+
+DefaultEventLoopPolicy = _WindowsDefaultEventLoopPolicy
diff --git a/Lib/asyncio/windows_utils.py b/Lib/asyncio/windows_utils.py
new file mode 100644
index 0000000..2a196cc
--- /dev/null
+++ b/Lib/asyncio/windows_utils.py
@@ -0,0 +1,205 @@
+"""
+Various Windows specific bits and pieces
+"""
+
+import sys
+
+if sys.platform != 'win32': # pragma: no cover
+ raise ImportError('win32 only')
+
+import socket
+import itertools
+import msvcrt
+import os
+import subprocess
+import tempfile
+import _winapi
+
+
+__all__ = ['socketpair', 'pipe', 'Popen', 'PIPE', 'PipeHandle']
+
+
+# Constants/globals
+
+
+BUFSIZE = 8192
+PIPE = subprocess.PIPE
+STDOUT = subprocess.STDOUT
+_mmap_counter = itertools.count()
+
+
+# Replacement for socket.socketpair()
+
+
+def socketpair(family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0):
+ """A socket pair usable as a self-pipe, for Windows.
+
+ Origin: https://gist.github.com/4325783, by Geert Jansen. Public domain.
+ """
+ if family == socket.AF_INET:
+ host = '127.0.0.1'
+ elif family == socket.AF_INET6:
+ host = '::1'
+ else:
+ raise ValueError("Ony AF_INET and AF_INET6 socket address families "
+ "are supported")
+ if type != socket.SOCK_STREAM:
+ raise ValueError("Only SOCK_STREAM socket type is supported")
+ if proto != 0:
+ raise ValueError("Only protocol zero is supported")
+
+ # We create a connected TCP socket. Note the trick with setblocking(0)
+ # that prevents us from having to create a thread.
+ lsock = socket.socket(family, type, proto)
+ lsock.bind((host, 0))
+ lsock.listen(1)
+ # On IPv6, ignore flow_info and scope_id
+ addr, port = lsock.getsockname()[:2]
+ csock = socket.socket(family, type, proto)
+ csock.setblocking(False)
+ try:
+ csock.connect((addr, port))
+ except (BlockingIOError, InterruptedError):
+ pass
+ except Exception:
+ lsock.close()
+ csock.close()
+ raise
+ ssock, _ = lsock.accept()
+ csock.setblocking(True)
+ lsock.close()
+ return (ssock, csock)
+
+
+# Replacement for os.pipe() using handles instead of fds
+
+
+def pipe(*, duplex=False, overlapped=(True, True), bufsize=BUFSIZE):
+ """Like os.pipe() but with overlapped support and using handles not fds."""
+ address = tempfile.mktemp(prefix=r'\\.\pipe\python-pipe-%d-%d-' %
+ (os.getpid(), next(_mmap_counter)))
+
+ if duplex:
+ openmode = _winapi.PIPE_ACCESS_DUPLEX
+ access = _winapi.GENERIC_READ | _winapi.GENERIC_WRITE
+ obsize, ibsize = bufsize, bufsize
+ else:
+ openmode = _winapi.PIPE_ACCESS_INBOUND
+ access = _winapi.GENERIC_WRITE
+ obsize, ibsize = 0, bufsize
+
+ openmode |= _winapi.FILE_FLAG_FIRST_PIPE_INSTANCE
+
+ if overlapped[0]:
+ openmode |= _winapi.FILE_FLAG_OVERLAPPED
+
+ if overlapped[1]:
+ flags_and_attribs = _winapi.FILE_FLAG_OVERLAPPED
+ else:
+ flags_and_attribs = 0
+
+ h1 = h2 = None
+ try:
+ h1 = _winapi.CreateNamedPipe(
+ address, openmode, _winapi.PIPE_WAIT,
+ 1, obsize, ibsize, _winapi.NMPWAIT_WAIT_FOREVER, _winapi.NULL)
+
+ h2 = _winapi.CreateFile(
+ address, access, 0, _winapi.NULL, _winapi.OPEN_EXISTING,
+ flags_and_attribs, _winapi.NULL)
+
+ ov = _winapi.ConnectNamedPipe(h1, overlapped=True)
+ ov.GetOverlappedResult(True)
+ return h1, h2
+ except:
+ if h1 is not None:
+ _winapi.CloseHandle(h1)
+ if h2 is not None:
+ _winapi.CloseHandle(h2)
+ raise
+
+
+# Wrapper for a pipe handle
+
+
+class PipeHandle:
+ """Wrapper for an overlapped pipe handle which is vaguely file-object like.
+
+ The IOCP event loop can use these instead of socket objects.
+ """
+ def __init__(self, handle):
+ self._handle = handle
+
+ @property
+ def handle(self):
+ return self._handle
+
+ def fileno(self):
+ return self._handle
+
+ def close(self, *, CloseHandle=_winapi.CloseHandle):
+ if self._handle != -1:
+ CloseHandle(self._handle)
+ self._handle = -1
+
+ __del__ = close
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, t, v, tb):
+ self.close()
+
+
+# Replacement for subprocess.Popen using overlapped pipe handles
+
+
+class Popen(subprocess.Popen):
+ """Replacement for subprocess.Popen using overlapped pipe handles.
+
+ The stdin, stdout, stderr are None or instances of PipeHandle.
+ """
+ def __init__(self, args, stdin=None, stdout=None, stderr=None, **kwds):
+ assert not kwds.get('universal_newlines')
+ assert kwds.get('bufsize', 0) == 0
+ stdin_rfd = stdout_wfd = stderr_wfd = None
+ stdin_wh = stdout_rh = stderr_rh = None
+ if stdin == PIPE:
+ stdin_rh, stdin_wh = pipe(overlapped=(False, True), duplex=True)
+ stdin_rfd = msvcrt.open_osfhandle(stdin_rh, os.O_RDONLY)
+ else:
+ stdin_rfd = stdin
+ if stdout == PIPE:
+ stdout_rh, stdout_wh = pipe(overlapped=(True, False))
+ stdout_wfd = msvcrt.open_osfhandle(stdout_wh, 0)
+ else:
+ stdout_wfd = stdout
+ if stderr == PIPE:
+ stderr_rh, stderr_wh = pipe(overlapped=(True, False))
+ stderr_wfd = msvcrt.open_osfhandle(stderr_wh, 0)
+ elif stderr == STDOUT:
+ stderr_wfd = stdout_wfd
+ else:
+ stderr_wfd = stderr
+ try:
+ super().__init__(args, stdin=stdin_rfd, stdout=stdout_wfd,
+ stderr=stderr_wfd, **kwds)
+ except:
+ for h in (stdin_wh, stdout_rh, stderr_rh):
+ if h is not None:
+ _winapi.CloseHandle(h)
+ raise
+ else:
+ if stdin_wh is not None:
+ self.stdin = PipeHandle(stdin_wh)
+ if stdout_rh is not None:
+ self.stdout = PipeHandle(stdout_rh)
+ if stderr_rh is not None:
+ self.stderr = PipeHandle(stderr_rh)
+ finally:
+ if stdin == PIPE:
+ os.close(stdin_rfd)
+ if stdout == PIPE:
+ os.close(stdout_wfd)
+ if stderr == PIPE:
+ os.close(stderr_wfd)
diff --git a/Lib/asyncore.py b/Lib/asyncore.py
index 909d9f6..75481dd 100644
--- a/Lib/asyncore.py
+++ b/Lib/asyncore.py
@@ -112,7 +112,7 @@ def readwrite(obj, flags):
obj.handle_expt_event()
if flags & (select.POLLHUP | select.POLLERR | select.POLLNVAL):
obj.handle_close()
- except socket.error as e:
+ except OSError as e:
if e.args[0] not in _DISCONNECTED:
obj.handle_error()
else:
@@ -240,7 +240,7 @@ class dispatcher:
# passed be connected.
try:
self.addr = sock.getpeername()
- except socket.error as err:
+ except OSError as err:
if err.args[0] in (ENOTCONN, EINVAL):
# To handle the case where we got an unconnected
# socket.
@@ -304,7 +304,7 @@ class dispatcher:
self.socket.getsockopt(socket.SOL_SOCKET,
socket.SO_REUSEADDR) | 1
)
- except socket.error:
+ except OSError:
pass
# ==================================================
@@ -345,7 +345,7 @@ class dispatcher:
self.addr = address
self.handle_connect_event()
else:
- raise socket.error(err, errorcode[err])
+ raise OSError(err, errorcode[err])
def accept(self):
# XXX can return either an address pair or None
@@ -353,7 +353,7 @@ class dispatcher:
conn, addr = self.socket.accept()
except TypeError:
return None
- except socket.error as why:
+ except OSError as why:
if why.args[0] in (EWOULDBLOCK, ECONNABORTED, EAGAIN):
return None
else:
@@ -365,7 +365,7 @@ class dispatcher:
try:
result = self.socket.send(data)
return result
- except socket.error as why:
+ except OSError as why:
if why.args[0] == EWOULDBLOCK:
return 0
elif why.args[0] in _DISCONNECTED:
@@ -384,7 +384,7 @@ class dispatcher:
return b''
else:
return data
- except socket.error as why:
+ except OSError as why:
# winsock sometimes raises ENOTCONN
if why.args[0] in _DISCONNECTED:
self.handle_close()
@@ -397,11 +397,12 @@ class dispatcher:
self.accepting = False
self.connecting = False
self.del_channel()
- try:
- self.socket.close()
- except socket.error as why:
- if why.args[0] not in (ENOTCONN, EBADF):
- raise
+ if self.socket is not None:
+ try:
+ self.socket.close()
+ except OSError as why:
+ if why.args[0] not in (ENOTCONN, EBADF):
+ raise
# cheap inheritance, used to pass all other attribute
# references to the underlying socket object.
@@ -443,7 +444,7 @@ class dispatcher:
def handle_connect_event(self):
err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
if err != 0:
- raise socket.error(err, _strerror(err))
+ raise OSError(err, _strerror(err))
self.handle_connect()
self.connected = True
self.connecting = False
@@ -532,7 +533,7 @@ class dispatcher_with_send(dispatcher):
def initiate_send(self):
num_sent = 0
- num_sent = dispatcher.send(self, self.out_buffer[:512])
+ num_sent = dispatcher.send(self, self.out_buffer[:65536])
self.out_buffer = self.out_buffer[num_sent:]
def handle_write(self):
diff --git a/Lib/base64.py b/Lib/base64.py
index b6e82b6..36c68a6 100755
--- a/Lib/base64.py
+++ b/Lib/base64.py
@@ -1,6 +1,6 @@
#! /usr/bin/env python3
-"""RFC 3548: Base16, Base32, Base64 Data Encodings"""
+"""Base16, Base32, Base64 (RFC 3548), Base85 and Ascii85 data encodings"""
# Modified 04-Oct-1995 by Jack Jansen to use binascii module
# Modified 30-Dec-2003 by Barry Warsaw to add full RFC 3548 support
@@ -17,6 +17,8 @@ __all__ = [
# Generalized interface for other encodings
'b64encode', 'b64decode', 'b32encode', 'b32decode',
'b16encode', 'b16decode',
+ # Base85 and Ascii85 encodings
+ 'b85encode', 'b85decode', 'a85encode', 'a85decode',
# Standard Base64 encoding
'standard_b64encode', 'standard_b64decode',
# Some common Base64 alternatives. As referenced by RFC 3458, see thread
@@ -35,11 +37,13 @@ def _bytes_from_decode_data(s):
return s.encode('ascii')
except UnicodeEncodeError:
raise ValueError('string argument should contain only ASCII characters')
- elif isinstance(s, bytes_types):
+ if isinstance(s, bytes_types):
return s
- else:
- raise TypeError("argument should be bytes or ASCII string, not %s" % s.__class__.__name__)
-
+ try:
+ return memoryview(s).tobytes()
+ except TypeError:
+ raise TypeError("argument should be a bytes-like object or ASCII "
+ "string, not %r" % s.__class__.__name__) from None
# Base64 encoding/decoding uses binascii
@@ -54,14 +58,9 @@ def b64encode(s, altchars=None):
The encoded byte string is returned.
"""
- if not isinstance(s, bytes_types):
- raise TypeError("expected bytes, not %s" % s.__class__.__name__)
# Strip off the trailing newline
encoded = binascii.b2a_base64(s)[:-1]
if altchars is not None:
- if not isinstance(altchars, bytes_types):
- raise TypeError("expected bytes, not %s"
- % altchars.__class__.__name__)
assert len(altchars) == 2, repr(altchars)
return encoded.translate(bytes.maketrans(b'+/', altchars))
return encoded
@@ -138,53 +137,39 @@ def urlsafe_b64decode(s):
# Base32 encoding/decoding must be done in Python
-_b32alphabet = {
- 0: b'A', 9: b'J', 18: b'S', 27: b'3',
- 1: b'B', 10: b'K', 19: b'T', 28: b'4',
- 2: b'C', 11: b'L', 20: b'U', 29: b'5',
- 3: b'D', 12: b'M', 21: b'V', 30: b'6',
- 4: b'E', 13: b'N', 22: b'W', 31: b'7',
- 5: b'F', 14: b'O', 23: b'X',
- 6: b'G', 15: b'P', 24: b'Y',
- 7: b'H', 16: b'Q', 25: b'Z',
- 8: b'I', 17: b'R', 26: b'2',
- }
-
-_b32tab = [v[0] for k, v in sorted(_b32alphabet.items())]
-_b32rev = dict([(v[0], k) for k, v in _b32alphabet.items()])
-
+_b32alphabet = b'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'
+_b32tab2 = None
+_b32rev = None
def b32encode(s):
"""Encode a byte string using Base32.
s is the byte string to encode. The encoded byte string is returned.
"""
+ global _b32tab2
+ # Delay the initialization of the table to not waste memory
+ # if the function is never called
+ if _b32tab2 is None:
+ b32tab = [bytes((i,)) for i in _b32alphabet]
+ _b32tab2 = [a + b for a in b32tab for b in b32tab]
+ b32tab = None
+
if not isinstance(s, bytes_types):
- raise TypeError("expected bytes, not %s" % s.__class__.__name__)
- quanta, leftover = divmod(len(s), 5)
+ s = memoryview(s).tobytes()
+ leftover = len(s) % 5
# Pad the last quantum with zero bits if necessary
if leftover:
s = s + bytes(5 - leftover) # Don't use += !
- quanta += 1
encoded = bytearray()
- for i in range(quanta):
- # c1 and c2 are 16 bits wide, c3 is 8 bits wide. The intent of this
- # code is to process the 40 bits in units of 5 bits. So we take the 1
- # leftover bit of c1 and tack it onto c2. Then we take the 2 leftover
- # bits of c2 and tack them onto c3. The shifts and masks are intended
- # to give us values of exactly 5 bits in width.
- c1, c2, c3 = struct.unpack('!HHB', s[i*5:(i+1)*5])
- c2 += (c1 & 1) << 16 # 17 bits wide
- c3 += (c2 & 3) << 8 # 10 bits wide
- encoded += bytes([_b32tab[c1 >> 11], # bits 1 - 5
- _b32tab[(c1 >> 6) & 0x1f], # bits 6 - 10
- _b32tab[(c1 >> 1) & 0x1f], # bits 11 - 15
- _b32tab[c2 >> 12], # bits 16 - 20 (1 - 5)
- _b32tab[(c2 >> 7) & 0x1f], # bits 21 - 25 (6 - 10)
- _b32tab[(c2 >> 2) & 0x1f], # bits 26 - 30 (11 - 15)
- _b32tab[c3 >> 5], # bits 31 - 35 (1 - 5)
- _b32tab[c3 & 0x1f], # bits 36 - 40 (1 - 5)
- ])
+ from_bytes = int.from_bytes
+ b32tab2 = _b32tab2
+ for i in range(0, len(s), 5):
+ c = from_bytes(s[i: i + 5], 'big')
+ encoded += (b32tab2[c >> 30] + # bits 1 - 10
+ b32tab2[(c >> 20) & 0x3ff] + # bits 11 - 20
+ b32tab2[(c >> 10) & 0x3ff] + # bits 21 - 30
+ b32tab2[c & 0x3ff] # bits 31 - 40
+ )
# Adjust for any leftover partial quanta
if leftover == 1:
encoded[-6:] = b'======'
@@ -196,7 +181,6 @@ def b32encode(s):
encoded[-1:] = b'='
return bytes(encoded)
-
def b32decode(s, casefold=False, map01=None):
"""Decode a Base32 encoded byte string.
@@ -216,9 +200,13 @@ def b32decode(s, casefold=False, map01=None):
the input is incorrectly padded or if there are non-alphabet
characters present in the input.
"""
+ global _b32rev
+ # Delay the initialization of the table to not waste memory
+ # if the function is never called
+ if _b32rev is None:
+ _b32rev = {v: k for k, v in enumerate(_b32alphabet)}
s = _bytes_from_decode_data(s)
- quanta, leftover = divmod(len(s), 8)
- if leftover:
+ if len(s) % 8:
raise binascii.Error('Incorrect padding')
# Handle section 2.4 zero and one mapping. The flag map01 will be either
# False, or the character to map the digit 1 (one) to. It should be
@@ -232,42 +220,36 @@ def b32decode(s, casefold=False, map01=None):
# Strip off pad characters from the right. We need to count the pad
# characters because this will tell us how many null bytes to remove from
# the end of the decoded string.
- padchars = 0
- mo = re.search(b'(?P<pad>[=]*)$', s)
- if mo:
- padchars = len(mo.group('pad'))
- if padchars > 0:
- s = s[:-padchars]
+ l = len(s)
+ s = s.rstrip(b'=')
+ padchars = l - len(s)
# Now decode the full quanta
- parts = []
- acc = 0
- shift = 35
- for c in s:
- val = _b32rev.get(c)
- if val is None:
- raise binascii.Error('Non-base32 digit found')
- acc += _b32rev[c] << shift
- shift -= 5
- if shift < 0:
- parts.append(binascii.unhexlify(bytes('%010x' % acc, "ascii")))
- acc = 0
- shift = 35
+ decoded = bytearray()
+ b32rev = _b32rev
+ for i in range(0, len(s), 8):
+ quanta = s[i: i + 8]
+ acc = 0
+ try:
+ for c in quanta:
+ acc = (acc << 5) + b32rev[c]
+ except KeyError:
+ raise binascii.Error('Non-base32 digit found') from None
+ decoded += acc.to_bytes(5, 'big')
# Process the last, partial quanta
- last = binascii.unhexlify(bytes('%010x' % acc, "ascii"))
- if padchars == 0:
- last = b'' # No characters
- elif padchars == 1:
- last = last[:-1]
- elif padchars == 3:
- last = last[:-2]
- elif padchars == 4:
- last = last[:-3]
- elif padchars == 6:
- last = last[:-4]
- else:
- raise binascii.Error('Incorrect padding')
- parts.append(last)
- return b''.join(parts)
+ if padchars:
+ acc <<= 5 * padchars
+ last = acc.to_bytes(5, 'big')
+ if padchars == 1:
+ decoded[-5:] = last[:-1]
+ elif padchars == 3:
+ decoded[-5:] = last[:-2]
+ elif padchars == 4:
+ decoded[-5:] = last[:-3]
+ elif padchars == 6:
+ decoded[-5:] = last[:-4]
+ else:
+ raise binascii.Error('Incorrect padding')
+ return bytes(decoded)
@@ -279,8 +261,6 @@ def b16encode(s):
s is the byte string to encode. The encoded byte string is returned.
"""
- if not isinstance(s, bytes_types):
- raise TypeError("expected bytes, not %s" % s.__class__.__name__)
return binascii.hexlify(s).upper()
@@ -302,7 +282,206 @@ def b16decode(s, casefold=False):
raise binascii.Error('Non-base16 digit found')
return binascii.unhexlify(s)
+#
+# Ascii85 encoding/decoding
+#
+
+_a85chars = None
+_a85chars2 = None
+_A85START = b"<~"
+_A85END = b"~>"
+
+def _85encode(b, chars, chars2, pad=False, foldnuls=False, foldspaces=False):
+ # Helper function for a85encode and b85encode
+ if not isinstance(b, bytes_types):
+ b = memoryview(b).tobytes()
+
+ padding = (-len(b)) % 4
+ if padding:
+ b = b + b'\0' * padding
+ words = struct.Struct('!%dI' % (len(b) // 4)).unpack(b)
+
+ chunks = [b'z' if foldnuls and not word else
+ b'y' if foldspaces and word == 0x20202020 else
+ (chars2[word // 614125] +
+ chars2[word // 85 % 7225] +
+ chars[word % 85])
+ for word in words]
+
+ if padding and not pad:
+ if chunks[-1] == b'z':
+ chunks[-1] = chars[0] * 5
+ chunks[-1] = chunks[-1][:-padding]
+
+ return b''.join(chunks)
+def a85encode(b, *, foldspaces=False, wrapcol=0, pad=False, adobe=False):
+ """Encode a byte string using Ascii85.
+
+ b is the byte string to encode. The encoded byte string is returned.
+
+ foldspaces is an optional flag that uses the special short sequence 'y'
+ instead of 4 consecutive spaces (ASCII 0x20) as supported by 'btoa'. This
+ feature is not supported by the "standard" Adobe encoding.
+
+ wrapcol controls whether the output should have newline ('\n') characters
+ added to it. If this is non-zero, each output line will be at most this
+ many characters long.
+
+ pad controls whether the input string is padded to a multiple of 4 before
+ encoding. Note that the btoa implementation always pads.
+
+ adobe controls whether the encoded byte sequence is framed with <~ and ~>,
+ which is used by the Adobe implementation.
+ """
+ global _a85chars, _a85chars2
+ # Delay the initialization of tables to not waste memory
+ # if the function is never called
+ if _a85chars is None:
+ _a85chars = [bytes((i,)) for i in range(33, 118)]
+ _a85chars2 = [(a + b) for a in _a85chars for b in _a85chars]
+
+ result = _85encode(b, _a85chars, _a85chars2, pad, True, foldspaces)
+
+ if adobe:
+ result = _A85START + result
+ if wrapcol:
+ wrapcol = max(2 if adobe else 1, wrapcol)
+ chunks = [result[i: i + wrapcol]
+ for i in range(0, len(result), wrapcol)]
+ if adobe:
+ if len(chunks[-1]) + 2 > wrapcol:
+ chunks.append(b'')
+ result = b'\n'.join(chunks)
+ if adobe:
+ result += _A85END
+
+ return result
+
+def a85decode(b, *, foldspaces=False, adobe=False, ignorechars=b' \t\n\r\v'):
+ """Decode an Ascii85 encoded byte string.
+
+ s is the byte string to decode.
+
+ foldspaces is a flag that specifies whether the 'y' short sequence should be
+ accepted as shorthand for 4 consecutive spaces (ASCII 0x20). This feature is
+ not supported by the "standard" Adobe encoding.
+
+ adobe controls whether the input sequence is in Adobe Ascii85 format (i.e.
+ is framed with <~ and ~>).
+
+ ignorechars should be a byte string containing characters to ignore from the
+ input. This should only contain whitespace characters, and by default
+ contains all whitespace characters in ASCII.
+ """
+ b = _bytes_from_decode_data(b)
+ if adobe:
+ if not (b.startswith(_A85START) and b.endswith(_A85END)):
+ raise ValueError("Ascii85 encoded byte sequences must be bracketed "
+ "by {!r} and {!r}".format(_A85START, _A85END))
+ b = b[2:-2] # Strip off start/end markers
+ #
+ # We have to go through this stepwise, so as to ignore spaces and handle
+ # special short sequences
+ #
+ packI = struct.Struct('!I').pack
+ decoded = []
+ decoded_append = decoded.append
+ curr = []
+ curr_append = curr.append
+ curr_clear = curr.clear
+ for x in b + b'u' * 4:
+ if b'!'[0] <= x <= b'u'[0]:
+ curr_append(x)
+ if len(curr) == 5:
+ acc = 0
+ for x in curr:
+ acc = 85 * acc + (x - 33)
+ try:
+ decoded_append(packI(acc))
+ except struct.error:
+ raise ValueError('Ascii85 overflow') from None
+ curr_clear()
+ elif x == b'z'[0]:
+ if curr:
+ raise ValueError('z inside Ascii85 5-tuple')
+ decoded_append(b'\0\0\0\0')
+ elif foldspaces and x == b'y'[0]:
+ if curr:
+ raise ValueError('y inside Ascii85 5-tuple')
+ decoded_append(b'\x20\x20\x20\x20')
+ elif x in ignorechars:
+ # Skip whitespace
+ continue
+ else:
+ raise ValueError('Non-Ascii85 digit found: %c' % x)
+
+ result = b''.join(decoded)
+ padding = 4 - len(curr)
+ if padding:
+ # Throw away the extra padding
+ result = result[:-padding]
+ return result
+
+# The following code is originally taken (with permission) from Mercurial
+
+_b85alphabet = (b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ b"abcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~")
+_b85chars = None
+_b85chars2 = None
+_b85dec = None
+
+def b85encode(b, pad=False):
+ """Encode an ASCII-encoded byte array in base85 format.
+
+ If pad is true, the input is padded with "\0" so its length is a multiple of
+ 4 characters before encoding.
+ """
+ global _b85chars, _b85chars2
+ # Delay the initialization of tables to not waste memory
+ # if the function is never called
+ if _b85chars is None:
+ _b85chars = [bytes((i,)) for i in _b85alphabet]
+ _b85chars2 = [(a + b) for a in _b85chars for b in _b85chars]
+ return _85encode(b, _b85chars, _b85chars2, pad)
+
+def b85decode(b):
+ """Decode base85-encoded byte array"""
+ global _b85dec
+ # Delay the initialization of tables to not waste memory
+ # if the function is never called
+ if _b85dec is None:
+ _b85dec = [None] * 256
+ for i, c in enumerate(_b85alphabet):
+ _b85dec[c] = i
+
+ b = _bytes_from_decode_data(b)
+ padding = (-len(b)) % 5
+ b = b + b'~' * padding
+ out = []
+ packI = struct.Struct('!I').pack
+ for i in range(0, len(b), 5):
+ chunk = b[i:i + 5]
+ acc = 0
+ try:
+ for c in chunk:
+ acc = acc * 85 + _b85dec[c]
+ except TypeError:
+ for j, c in enumerate(chunk):
+ if _b85dec[c] is None:
+ raise ValueError('bad base85 character at position %d'
+ % (i + j)) from None
+ raise
+ try:
+ out.append(packI(acc))
+ except struct.error:
+ raise ValueError('base85 overflow in hunk starting at byte %d'
+ % i) from None
+
+ result = b''.join(out)
+ if padding:
+ result = result[:-padding]
+ return result
# Legacy interface. This code could be cleaned up since I don't believe
# binascii has any line length limitations. It just doesn't seem worth it
@@ -335,12 +514,26 @@ def decode(input, output):
s = binascii.a2b_base64(line)
output.write(s)
+def _input_type_check(s):
+ try:
+ m = memoryview(s)
+ except TypeError as err:
+ msg = "expected bytes-like object, not %s" % s.__class__.__name__
+ raise TypeError(msg) from err
+ if m.format not in ('c', 'b', 'B'):
+ msg = ("expected single byte elements, not %r from %s" %
+ (m.format, s.__class__.__name__))
+ raise TypeError(msg)
+ if m.ndim != 1:
+ msg = ("expected 1-D data, not %d-D data from %s" %
+ (m.ndim, s.__class__.__name__))
+ raise TypeError(msg)
+
def encodebytes(s):
"""Encode a bytestring into a bytestring containing multiple lines
of base-64 data."""
- if not isinstance(s, bytes_types):
- raise TypeError("expected bytes, not %s" % s.__class__.__name__)
+ _input_type_check(s)
pieces = []
for i in range(0, len(s), MAXBINSIZE):
chunk = s[i : i + MAXBINSIZE]
@@ -357,8 +550,7 @@ def encodestring(s):
def decodebytes(s):
"""Decode a bytestring of base-64 data into a bytestring."""
- if not isinstance(s, bytes_types):
- raise TypeError("expected bytes, not %s" % s.__class__.__name__)
+ _input_type_check(s)
return binascii.a2b_base64(s)
def decodestring(s):
diff --git a/Lib/bdb.py b/Lib/bdb.py
index dd1f4287..67a0846 100644
--- a/Lib/bdb.py
+++ b/Lib/bdb.py
@@ -3,6 +3,7 @@
import fnmatch
import sys
import os
+from inspect import CO_GENERATOR
__all__ = ["BdbQuit", "Bdb", "Breakpoint"]
@@ -75,24 +76,48 @@ class Bdb:
if not (self.stop_here(frame) or self.break_anywhere(frame)):
# No need to trace this function
return # None
+ # Ignore call events in generator except when stepping.
+ if self.stopframe and frame.f_code.co_flags & CO_GENERATOR:
+ return self.trace_dispatch
self.user_call(frame, arg)
if self.quitting: raise BdbQuit
return self.trace_dispatch
def dispatch_return(self, frame, arg):
if self.stop_here(frame) or frame == self.returnframe:
+ # Ignore return events in generator except when stepping.
+ if self.stopframe and frame.f_code.co_flags & CO_GENERATOR:
+ return self.trace_dispatch
try:
self.frame_returning = frame
self.user_return(frame, arg)
finally:
self.frame_returning = None
if self.quitting: raise BdbQuit
+ # The user issued a 'next' or 'until' command.
+ if self.stopframe is frame and self.stoplineno != -1:
+ self._set_stopinfo(None, None)
return self.trace_dispatch
def dispatch_exception(self, frame, arg):
if self.stop_here(frame):
+ # When stepping with next/until/return in a generator frame, skip
+ # the internal StopIteration exception (with no traceback)
+ # triggered by a subiterator run with the 'yield from' statement.
+ if not (frame.f_code.co_flags & CO_GENERATOR
+ and arg[0] is StopIteration and arg[2] is None):
+ self.user_exception(frame, arg)
+ if self.quitting: raise BdbQuit
+ # Stop at the StopIteration or GeneratorExit exception when the user
+ # has set stopframe in a generator by issuing a return command, or a
+ # next/until command at the last statement in the generator before the
+ # exception.
+ elif (self.stopframe and frame is not self.stopframe
+ and self.stopframe.f_code.co_flags & CO_GENERATOR
+ and arg[0] in (StopIteration, GeneratorExit)):
self.user_exception(frame, arg)
if self.quitting: raise BdbQuit
+
return self.trace_dispatch
# Normally derived classes don't override the following
@@ -115,10 +140,8 @@ class Bdb:
if self.stoplineno == -1:
return False
return frame.f_lineno >= self.stoplineno
- while frame is not None and frame is not self.stopframe:
- if frame is self.botframe:
- return True
- frame = frame.f_back
+ if not self.stopframe:
+ return True
return False
def break_here(self, frame):
@@ -207,7 +230,10 @@ class Bdb:
def set_return(self, frame):
"""Stop when returning from the given frame."""
- self._set_stopinfo(frame.f_back, frame)
+ if frame.f_code.co_flags & CO_GENERATOR:
+ self._set_stopinfo(frame, None, -1)
+ else:
+ self._set_stopinfo(frame.f_back, frame)
def set_trace(self, frame=None):
"""Start debugging from `frame`.
diff --git a/Lib/bz2.py b/Lib/bz2.py
index 1de8f3c..6f47bfa 100644
--- a/Lib/bz2.py
+++ b/Lib/bz2.py
@@ -9,7 +9,6 @@ __all__ = ["BZ2File", "BZ2Compressor", "BZ2Decompressor",
__author__ = "Nadeem Vawda <nadeem.vawda@gmail.com>"
-import builtins
import io
import warnings
@@ -28,6 +27,8 @@ _MODE_WRITE = 3
_BUFFER_SIZE = 8192
+_builtin_open = open
+
class BZ2File(io.BufferedIOBase):
@@ -43,16 +44,17 @@ class BZ2File(io.BufferedIOBase):
def __init__(self, filename, mode="r", buffering=None, compresslevel=9):
"""Open a bzip2-compressed file.
- If filename is a str or bytes object, is gives the name of the file to
- be opened. Otherwise, it should be a file object, which will be used to
- read or write the compressed data.
+ If filename is a str or bytes object, it gives the name
+ of the file to be opened. Otherwise, it should be a file object,
+ which will be used to read or write the compressed data.
- mode can be 'r' for reading (default), 'w' for (over)writing, or 'a' for
- appending. These can equivalently be given as 'rb', 'wb', and 'ab'.
+ mode can be 'r' for reading (default), 'w' for (over)writing,
+ 'x' for creating exclusively, or 'a' for appending. These can
+ equivalently be given as 'rb', 'wb', 'xb', and 'ab'.
buffering is ignored. Its use is deprecated.
- If mode is 'w' or 'a', compresslevel can be a number between 1
+ If mode is 'w', 'x' 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.
@@ -85,15 +87,19 @@ class BZ2File(io.BufferedIOBase):
mode = "wb"
mode_code = _MODE_WRITE
self._compressor = BZ2Compressor(compresslevel)
+ elif mode in ("x", "xb"):
+ mode = "xb"
+ 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))
+ raise ValueError("Invalid mode: %r" % (mode,))
if isinstance(filename, (str, bytes)):
- self._fp = builtins.open(filename, mode)
+ self._fp = _builtin_open(filename, mode)
self._closefp = True
self._mode = mode_code
elif hasattr(filename, "read") or hasattr(filename, "write"):
@@ -189,15 +195,17 @@ class BZ2File(io.BufferedIOBase):
if not rawblock:
if self._decompressor.eof:
+ # End-of-stream marker and end of file. We're good.
self._mode = _MODE_READ_EOF
self._size = self._pos
return False
else:
+ # Problem - we were expecting more compressed data.
raise EOFError("Compressed file ended before the "
"end-of-stream marker was reached")
- # Continue to next stream.
if self._decompressor.eof:
+ # Continue to next stream.
self._decompressor = BZ2Decompressor()
try:
self._buffer = self._decompressor.decompress(rawblock)
@@ -419,7 +427,7 @@ class BZ2File(io.BufferedIOBase):
self._read_all(return_data=False)
offset = self._size + offset
else:
- raise ValueError("Invalid value for whence: {}".format(whence))
+ raise ValueError("Invalid value for whence: %s" % (whence,))
# Make it so that offset is the number of bytes to skip forward.
if offset < self._pos:
@@ -443,20 +451,20 @@ def open(filename, mode="rb", compresslevel=9,
encoding=None, errors=None, newline=None):
"""Open a bzip2-compressed file in binary or text mode.
- The filename argument can be an actual filename (a str or bytes object), or
- an existing file object to read from or write to.
+ The filename argument can be an actual filename (a str or bytes
+ object), or an existing file object to read from or write to.
- The mode argument can be "r", "rb", "w", "wb", "a" or "ab" for binary mode,
- or "rt", "wt" or "at" for text mode. The default mode is "rb", and the
- default compresslevel is 9.
+ The mode argument can be "r", "rb", "w", "wb", "x", "xb", "a" or
+ "ab" for binary mode, or "rt", "wt", "xt" or "at" for text mode.
+ The default mode is "rb", and the default compresslevel is 9.
- For binary mode, this function is equivalent to the BZ2File constructor:
- BZ2File(filename, mode, compresslevel). In this case, the encoding, errors
- and newline arguments must not be provided.
+ For binary mode, this function is equivalent to the BZ2File
+ constructor: BZ2File(filename, mode, compresslevel). In this case,
+ the encoding, errors and newline arguments must not be provided.
For text mode, a BZ2File object is created, and wrapped in an
- io.TextIOWrapper instance with the specified encoding, error handling
- behavior, and line ending(s).
+ io.TextIOWrapper instance with the specified encoding, error
+ handling behavior, and line ending(s).
"""
if "t" in mode:
diff --git a/Lib/cProfile.py b/Lib/cProfile.py
index c24d45b..1184385 100755
--- a/Lib/cProfile.py
+++ b/Lib/cProfile.py
@@ -7,54 +7,20 @@
__all__ = ["run", "runctx", "Profile"]
import _lsprof
+import profile as _pyprofile
# ____________________________________________________________
# Simple interface
def run(statement, filename=None, sort=-1):
- """Run statement under profiler optionally saving results in filename
-
- This function takes a single argument that can be passed to the
- "exec" statement, and an optional file name. In all cases this
- routine attempts to "exec" its first argument and gather profiling
- statistics from the execution. If no file name is present, then this
- function automatically prints a simple profiling report, sorted by the
- standard name string (file/line/function-name) that is presented in
- each line.
- """
- prof = Profile()
- result = None
- try:
- try:
- prof = prof.run(statement)
- except SystemExit:
- pass
- finally:
- if filename is not None:
- prof.dump_stats(filename)
- else:
- result = prof.print_stats(sort)
- return result
+ return _pyprofile._Utils(Profile).run(statement, filename, sort)
def runctx(statement, globals, locals, filename=None, sort=-1):
- """Run statement under profiler, supplying your own globals and locals,
- optionally saving results in filename.
+ return _pyprofile._Utils(Profile).runctx(statement, globals, locals,
+ filename, sort)
- statement and filename have the same semantics as profile.run
- """
- prof = Profile()
- result = None
- try:
- try:
- prof = prof.runctx(statement, globals, locals)
- except SystemExit:
- pass
- finally:
- if filename is not None:
- prof.dump_stats(filename)
- else:
- result = prof.print_stats(sort)
- return result
+run.__doc__ = _pyprofile.run.__doc__
+runctx.__doc__ = _pyprofile.runctx.__doc__
# ____________________________________________________________
@@ -77,10 +43,9 @@ class Profile(_lsprof.Profiler):
def dump_stats(self, file):
import marshal
- f = open(file, 'wb')
- self.create_stats()
- marshal.dump(self.stats, f)
- f.close()
+ with open(file, 'wb') as f:
+ self.create_stats()
+ marshal.dump(self.stats, f)
def create_stats(self):
self.disable()
diff --git a/Lib/cgi.py b/Lib/cgi.py
index 0f50d0e..1ef780c 100755
--- a/Lib/cgi.py
+++ b/Lib/cgi.py
@@ -82,7 +82,7 @@ def initlog(*allargs):
if logfile and not logfp:
try:
logfp = open(logfile, "a")
- except IOError:
+ except OSError:
pass
if not logfp:
log = nolog
@@ -560,6 +560,12 @@ class FieldStorage:
else:
self.read_single()
+ def __del__(self):
+ try:
+ self.file.close()
+ except AttributeError:
+ pass
+
def __repr__(self):
"""Return a printable representation."""
return "FieldStorage(%r, %r, %r)" % (
@@ -680,7 +686,6 @@ class FieldStorage:
encoding=self.encoding, errors=self.errors)
for key, value in query:
self.list.append(MiniFieldStorage(key, value))
- FieldStorageClass = None
klass = self.FieldStorageClass or self.__class__
first_line = self.fp.readline() # bytes
@@ -968,8 +973,8 @@ def print_directory():
print("<H3>Current Working Directory:</H3>")
try:
pwd = os.getcwd()
- except os.error as msg:
- print("os.error:", html.escape(str(msg)))
+ except OSError as msg:
+ print("OSError:", html.escape(str(msg)))
else:
print(html.escape(pwd))
print()
@@ -1040,7 +1045,7 @@ def escape(s, quote=None):
return s
-def valid_boundary(s, _vb_pattern=None):
+def valid_boundary(s):
import re
if isinstance(s, bytes):
_vb_pattern = b"^[ -~]{0,200}[!-~]$"
diff --git a/Lib/chunk.py b/Lib/chunk.py
index 5863ed0..dc90a75 100644
--- a/Lib/chunk.py
+++ b/Lib/chunk.py
@@ -70,7 +70,7 @@ class Chunk:
self.size_read = 0
try:
self.offset = self.file.tell()
- except (AttributeError, IOError):
+ except (AttributeError, OSError):
self.seekable = False
else:
self.seekable = True
@@ -102,7 +102,7 @@ class Chunk:
if self.closed:
raise ValueError("I/O operation on closed file")
if not self.seekable:
- raise IOError("cannot seek")
+ raise OSError("cannot seek")
if whence == 1:
pos = pos + self.size_read
elif whence == 2:
@@ -158,7 +158,7 @@ class Chunk:
self.file.seek(n, 1)
self.size_read = self.size_read + n
return
- except IOError:
+ except OSError:
pass
while self.size_read < self.chunksize:
n = min(8192, self.chunksize - self.size_read)
diff --git a/Lib/code.py b/Lib/code.py
index 9020aab..f8184b6 100644
--- a/Lib/code.py
+++ b/Lib/code.py
@@ -216,7 +216,7 @@ class InteractiveConsole(InteractiveInterpreter):
self.write("Python %s on %s\n%s\n(%s)\n" %
(sys.version, sys.platform, cprt,
self.__class__.__name__))
- else:
+ elif banner:
self.write("%s\n" % str(banner))
more = 0
while 1:
diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py
index d737295..d6deb6a 100644
--- a/Lib/collections/__init__.py
+++ b/Lib/collections/__init__.py
@@ -3,16 +3,16 @@ __all__ = ['deque', 'defaultdict', 'namedtuple', 'UserDict', 'UserList',
# 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_abc import *
+import _collections_abc
+__all__ += _collections_abc.__all__
from _collections import deque, defaultdict
from operator import itemgetter as _itemgetter, eq as _eq
from keyword import iskeyword as _iskeyword
import sys as _sys
import heapq as _heapq
-from weakref import proxy as _proxy
+from _weakref import proxy as _proxy
from itertools import repeat as _repeat, chain as _chain, starmap as _starmap
from reprlib import recursive_repr as _recursive_repr
@@ -199,13 +199,10 @@ class OrderedDict(dict):
def __reduce__(self):
'Return state information for pickling'
- items = [[k, self[k]] for k in self]
inst_dict = vars(self).copy()
for k in vars(OrderedDict()):
inst_dict.pop(k, None)
- if inst_dict:
- return (self.__class__, (items,), inst_dict)
- return self.__class__, (items,)
+ return self.__class__, (), inst_dict or None, None, iter(self.items())
def copy(self):
'od.copy() -> a shallow copy of od'
@@ -277,9 +274,7 @@ class {typename}(tuple):
return OrderedDict(zip(self._fields, self))
def _asdict(self):
- '''Return a new OrderedDict which maps field names to their values.
- This method is obsolete. Use vars(nt) or nt.__dict__ instead.
- '''
+ 'Return a new OrderedDict which maps field names to their values.'
return self.__dict__
def __getnewargs__(self):
@@ -822,9 +817,14 @@ class ChainMap(MutableMapping):
__copy__ = copy
- def new_child(self): # like Django's Context.push()
- 'New ChainMap with a new dict followed by all previous maps.'
- return self.__class__({}, *self.maps)
+ def new_child(self, m=None): # like Django's Context.push()
+ '''
+ New ChainMap with a new map followed by all previous maps. If no
+ map is provided, an empty dict is used.
+ '''
+ if m is None:
+ m = {}
+ return self.__class__(m, *self.maps)
@property
def parents(self): # like Django's Context.pop()
diff --git a/Lib/collections/abc.py b/Lib/collections/abc.py
index 7939268..891600d 100644
--- a/Lib/collections/abc.py
+++ b/Lib/collections/abc.py
@@ -1,728 +1,2 @@
-# Copyright 2007 Google, Inc. All Rights Reserved.
-# Licensed to PSF under a Contributor Agreement.
-
-"""Abstract Base Classes (ABCs) for collections, according to PEP 3119.
-
-Unit tests are in test_collections.
-"""
-
-from abc import ABCMeta, abstractmethod
-import sys
-
-__all__ = ["Hashable", "Iterable", "Iterator",
- "Sized", "Container", "Callable",
- "Set", "MutableSet",
- "Mapping", "MutableMapping",
- "MappingView", "KeysView", "ItemsView", "ValuesView",
- "Sequence", "MutableSequence",
- "ByteString",
- ]
-
-# Private list of types that we want to register with the various ABCs
-# so that they will pass tests like:
-# it = iter(somebytearray)
-# assert isinstance(it, Iterable)
-# Note: in other implementations, these types many not be distinct
-# and they make have their own implementation specific types that
-# are not included on this list.
-bytes_iterator = type(iter(b''))
-bytearray_iterator = type(iter(bytearray()))
-#callable_iterator = ???
-dict_keyiterator = type(iter({}.keys()))
-dict_valueiterator = type(iter({}.values()))
-dict_itemiterator = type(iter({}.items()))
-list_iterator = type(iter([]))
-list_reverseiterator = type(iter(reversed([])))
-range_iterator = type(iter(range(0)))
-set_iterator = type(iter(set()))
-str_iterator = type(iter(""))
-tuple_iterator = type(iter(()))
-zip_iterator = type(iter(zip()))
-## views ##
-dict_keys = type({}.keys())
-dict_values = type({}.values())
-dict_items = type({}.items())
-## misc ##
-mappingproxy = type(type.__dict__)
-
-
-### ONE-TRICK PONIES ###
-
-class Hashable(metaclass=ABCMeta):
-
- __slots__ = ()
-
- @abstractmethod
- def __hash__(self):
- return 0
-
- @classmethod
- def __subclasshook__(cls, C):
- if cls is Hashable:
- for B in C.__mro__:
- if "__hash__" in B.__dict__:
- if B.__dict__["__hash__"]:
- return True
- break
- return NotImplemented
-
-
-class Iterable(metaclass=ABCMeta):
-
- __slots__ = ()
-
- @abstractmethod
- def __iter__(self):
- while False:
- yield None
-
- @classmethod
- def __subclasshook__(cls, C):
- if cls is Iterable:
- if any("__iter__" in B.__dict__ for B in C.__mro__):
- return True
- return NotImplemented
-
-
-class Iterator(Iterable):
-
- __slots__ = ()
-
- @abstractmethod
- def __next__(self):
- 'Return the next item from the iterator. When exhausted, raise StopIteration'
- raise StopIteration
-
- def __iter__(self):
- return self
-
- @classmethod
- def __subclasshook__(cls, C):
- if cls is Iterator:
- if (any("__next__" in B.__dict__ for B in C.__mro__) and
- any("__iter__" in B.__dict__ for B in C.__mro__)):
- return True
- return NotImplemented
-
-Iterator.register(bytes_iterator)
-Iterator.register(bytearray_iterator)
-#Iterator.register(callable_iterator)
-Iterator.register(dict_keyiterator)
-Iterator.register(dict_valueiterator)
-Iterator.register(dict_itemiterator)
-Iterator.register(list_iterator)
-Iterator.register(list_reverseiterator)
-Iterator.register(range_iterator)
-Iterator.register(set_iterator)
-Iterator.register(str_iterator)
-Iterator.register(tuple_iterator)
-Iterator.register(zip_iterator)
-
-class Sized(metaclass=ABCMeta):
-
- __slots__ = ()
-
- @abstractmethod
- def __len__(self):
- return 0
-
- @classmethod
- def __subclasshook__(cls, C):
- if cls is Sized:
- if any("__len__" in B.__dict__ for B in C.__mro__):
- return True
- return NotImplemented
-
-
-class Container(metaclass=ABCMeta):
-
- __slots__ = ()
-
- @abstractmethod
- def __contains__(self, x):
- return False
-
- @classmethod
- def __subclasshook__(cls, C):
- if cls is Container:
- if any("__contains__" in B.__dict__ for B in C.__mro__):
- return True
- return NotImplemented
-
-
-class Callable(metaclass=ABCMeta):
-
- __slots__ = ()
-
- @abstractmethod
- def __call__(self, *args, **kwds):
- return False
-
- @classmethod
- def __subclasshook__(cls, C):
- if cls is Callable:
- if any("__call__" in B.__dict__ for B in C.__mro__):
- return True
- return NotImplemented
-
-
-### SETS ###
-
-
-class Set(Sized, Iterable, Container):
-
- """A set is a finite, iterable container.
-
- This class provides concrete generic implementations of all
- methods except for __contains__, __iter__ and __len__.
-
- To override the comparisons (presumably for speed, as the
- semantics are fixed), all you have to do is redefine __le__ and
- then the other operations will automatically follow suit.
- """
-
- __slots__ = ()
-
- def __le__(self, other):
- if not isinstance(other, Set):
- return NotImplemented
- if len(self) > len(other):
- return False
- for elem in self:
- if elem not in other:
- return False
- return True
-
- def __lt__(self, other):
- if not isinstance(other, Set):
- return NotImplemented
- return len(self) < len(other) and self.__le__(other)
-
- def __gt__(self, other):
- if not isinstance(other, Set):
- return NotImplemented
- return other.__lt__(self)
-
- def __ge__(self, other):
- if not isinstance(other, Set):
- return NotImplemented
- return other.__le__(self)
-
- def __eq__(self, other):
- if not isinstance(other, Set):
- return NotImplemented
- return len(self) == len(other) and self.__le__(other)
-
- def __ne__(self, other):
- return not (self == other)
-
- @classmethod
- def _from_iterable(cls, it):
- '''Construct an instance of the class from any iterable input.
-
- Must override this method if the class constructor signature
- does not accept an iterable for an input.
- '''
- return cls(it)
-
- def __and__(self, other):
- if not isinstance(other, Iterable):
- return NotImplemented
- return self._from_iterable(value for value in other if value in self)
-
- def isdisjoint(self, other):
- 'Return True if two sets have a null intersection.'
- for value in other:
- if value in self:
- return False
- return True
-
- def __or__(self, other):
- if not isinstance(other, Iterable):
- return NotImplemented
- chain = (e for s in (self, other) for e in s)
- return self._from_iterable(chain)
-
- def __sub__(self, other):
- if not isinstance(other, Set):
- if not isinstance(other, Iterable):
- return NotImplemented
- other = self._from_iterable(other)
- return self._from_iterable(value for value in self
- if value not in other)
-
- def __xor__(self, other):
- if not isinstance(other, Set):
- if not isinstance(other, Iterable):
- return NotImplemented
- other = self._from_iterable(other)
- return (self - other) | (other - self)
-
- def _hash(self):
- """Compute the hash value of a set.
-
- Note that we don't define __hash__: not all sets are hashable.
- But if you define a hashable set type, its __hash__ should
- call this function.
-
- This must be compatible __eq__.
-
- All sets ought to compare equal if they contain the same
- elements, regardless of how they are implemented, and
- regardless of the order of the elements; so there's not much
- freedom for __eq__ or __hash__. We match the algorithm used
- by the built-in frozenset type.
- """
- MAX = sys.maxsize
- MASK = 2 * MAX + 1
- n = len(self)
- h = 1927868237 * (n + 1)
- h &= MASK
- for x in self:
- hx = hash(x)
- h ^= (hx ^ (hx << 16) ^ 89869747) * 3644798167
- h &= MASK
- h = h * 69069 + 907133923
- h &= MASK
- if h > MAX:
- h -= MASK + 1
- if h == -1:
- h = 590923713
- return h
-
-Set.register(frozenset)
-
-
-class MutableSet(Set):
- """A mutable set is a finite, iterable container.
-
- This class provides concrete generic implementations of all
- methods except for __contains__, __iter__, __len__,
- add(), and discard().
-
- To override the comparisons (presumably for speed, as the
- semantics are fixed), all you have to do is redefine __le__ and
- then the other operations will automatically follow suit.
- """
-
- __slots__ = ()
-
- @abstractmethod
- def add(self, value):
- """Add an element."""
- raise NotImplementedError
-
- @abstractmethod
- def discard(self, value):
- """Remove an element. Do not raise an exception if absent."""
- raise NotImplementedError
-
- def remove(self, value):
- """Remove an element. If not a member, raise a KeyError."""
- if value not in self:
- raise KeyError(value)
- self.discard(value)
-
- def pop(self):
- """Return the popped value. Raise KeyError if empty."""
- it = iter(self)
- try:
- value = next(it)
- except StopIteration:
- raise KeyError
- self.discard(value)
- return value
-
- def clear(self):
- """This is slow (creates N new iterators!) but effective."""
- try:
- while True:
- self.pop()
- except KeyError:
- pass
-
- def __ior__(self, it):
- for value in it:
- self.add(value)
- return self
-
- def __iand__(self, it):
- for value in (self - it):
- self.discard(value)
- return self
-
- def __ixor__(self, it):
- if it is self:
- self.clear()
- else:
- if not isinstance(it, Set):
- it = self._from_iterable(it)
- for value in it:
- if value in self:
- self.discard(value)
- else:
- self.add(value)
- return self
-
- def __isub__(self, it):
- if it is self:
- self.clear()
- else:
- for value in it:
- self.discard(value)
- return self
-
-MutableSet.register(set)
-
-
-### MAPPINGS ###
-
-
-class Mapping(Sized, Iterable, Container):
-
- __slots__ = ()
-
- """A Mapping is a generic container for associating key/value
- pairs.
-
- This class provides concrete generic implementations of all
- methods except for __getitem__, __iter__, and __len__.
-
- """
-
- @abstractmethod
- def __getitem__(self, key):
- raise KeyError
-
- def get(self, key, default=None):
- 'D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None.'
- try:
- return self[key]
- except KeyError:
- return default
-
- def __contains__(self, key):
- try:
- self[key]
- except KeyError:
- return False
- else:
- return True
-
- def keys(self):
- "D.keys() -> a set-like object providing a view on D's keys"
- return KeysView(self)
-
- def items(self):
- "D.items() -> a set-like object providing a view on D's items"
- return ItemsView(self)
-
- def values(self):
- "D.values() -> an object providing a view on D's values"
- return ValuesView(self)
-
- def __eq__(self, other):
- if not isinstance(other, Mapping):
- return NotImplemented
- return dict(self.items()) == dict(other.items())
-
- def __ne__(self, other):
- return not (self == other)
-
-Mapping.register(mappingproxy)
-
-
-class MappingView(Sized):
-
- def __init__(self, mapping):
- self._mapping = mapping
-
- def __len__(self):
- return len(self._mapping)
-
- def __repr__(self):
- return '{0.__class__.__name__}({0._mapping!r})'.format(self)
-
-
-class KeysView(MappingView, Set):
-
- @classmethod
- def _from_iterable(self, it):
- return set(it)
-
- def __contains__(self, key):
- return key in self._mapping
-
- def __iter__(self):
- for key in self._mapping:
- yield key
-
-KeysView.register(dict_keys)
-
-
-class ItemsView(MappingView, Set):
-
- @classmethod
- def _from_iterable(self, it):
- return set(it)
-
- def __contains__(self, item):
- key, value = item
- try:
- v = self._mapping[key]
- except KeyError:
- return False
- else:
- return v == value
-
- def __iter__(self):
- for key in self._mapping:
- yield (key, self._mapping[key])
-
-ItemsView.register(dict_items)
-
-
-class ValuesView(MappingView):
-
- def __contains__(self, value):
- for key in self._mapping:
- if value == self._mapping[key]:
- return True
- return False
-
- def __iter__(self):
- for key in self._mapping:
- yield self._mapping[key]
-
-ValuesView.register(dict_values)
-
-
-class MutableMapping(Mapping):
-
- __slots__ = ()
-
- """A MutableMapping is a generic container for associating
- key/value pairs.
-
- This class provides concrete generic implementations of all
- methods except for __getitem__, __setitem__, __delitem__,
- __iter__, and __len__.
-
- """
-
- @abstractmethod
- def __setitem__(self, key, value):
- raise KeyError
-
- @abstractmethod
- def __delitem__(self, key):
- raise KeyError
-
- __marker = object()
-
- def pop(self, key, default=__marker):
- '''D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
- If key is not found, d is returned if given, otherwise KeyError is raised.
- '''
- try:
- value = self[key]
- except KeyError:
- if default is self.__marker:
- raise
- return default
- else:
- del self[key]
- return value
-
- def popitem(self):
- '''D.popitem() -> (k, v), remove and return some (key, value) pair
- as a 2-tuple; but raise KeyError if D is empty.
- '''
- try:
- key = next(iter(self))
- except StopIteration:
- raise KeyError
- value = self[key]
- del self[key]
- return key, value
-
- def clear(self):
- 'D.clear() -> None. Remove all items from D.'
- try:
- while True:
- self.popitem()
- except KeyError:
- pass
-
- def update(*args, **kwds):
- ''' D.update([E, ]**F) -> None. Update D from mapping/iterable E and F.
- If E present and has a .keys() method, does: for k in E: D[k] = E[k]
- If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v
- In either case, this is followed by: for k, v in F.items(): D[k] = v
- '''
- if len(args) > 2:
- raise TypeError("update() takes at most 2 positional "
- "arguments ({} given)".format(len(args)))
- elif not args:
- raise TypeError("update() takes at least 1 argument (0 given)")
- self = args[0]
- other = args[1] if len(args) >= 2 else ()
-
- if isinstance(other, Mapping):
- for key in other:
- self[key] = other[key]
- elif hasattr(other, "keys"):
- for key in other.keys():
- self[key] = other[key]
- else:
- for key, value in other:
- self[key] = value
- for key, value in kwds.items():
- self[key] = value
-
- def setdefault(self, key, default=None):
- 'D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D'
- try:
- return self[key]
- except KeyError:
- self[key] = default
- return default
-
-MutableMapping.register(dict)
-
-
-### SEQUENCES ###
-
-
-class Sequence(Sized, Iterable, Container):
-
- """All the operations on a read-only sequence.
-
- Concrete subclasses must override __new__ or __init__,
- __getitem__, and __len__.
- """
-
- __slots__ = ()
-
- @abstractmethod
- def __getitem__(self, index):
- raise IndexError
-
- def __iter__(self):
- i = 0
- try:
- while True:
- v = self[i]
- yield v
- i += 1
- except IndexError:
- return
-
- def __contains__(self, value):
- for v in self:
- if v == value:
- return True
- return False
-
- def __reversed__(self):
- for i in reversed(range(len(self))):
- yield self[i]
-
- def index(self, value):
- '''S.index(value) -> integer -- return first index of value.
- Raises ValueError if the value is not present.
- '''
- for i, v in enumerate(self):
- if v == value:
- return i
- raise ValueError
-
- def count(self, value):
- 'S.count(value) -> integer -- return number of occurrences of value'
- return sum(1 for v in self if v == value)
-
-Sequence.register(tuple)
-Sequence.register(str)
-Sequence.register(range)
-
-
-class ByteString(Sequence):
-
- """This unifies bytes and bytearray.
-
- XXX Should add all their methods.
- """
-
- __slots__ = ()
-
-ByteString.register(bytes)
-ByteString.register(bytearray)
-
-
-class MutableSequence(Sequence):
-
- __slots__ = ()
-
- """All the operations on a read-only sequence.
-
- Concrete subclasses must provide __new__ or __init__,
- __getitem__, __setitem__, __delitem__, __len__, and insert().
-
- """
-
- @abstractmethod
- def __setitem__(self, index, value):
- raise IndexError
-
- @abstractmethod
- def __delitem__(self, index):
- raise IndexError
-
- @abstractmethod
- def insert(self, index, value):
- 'S.insert(index, value) -- insert value before index'
- raise IndexError
-
- def append(self, value):
- 'S.append(value) -- append value to the end of the sequence'
- self.insert(len(self), value)
-
- def clear(self):
- 'S.clear() -> None -- remove all items from S'
- try:
- while True:
- self.pop()
- except IndexError:
- pass
-
- def reverse(self):
- 'S.reverse() -- reverse *IN PLACE*'
- n = len(self)
- for i in range(n//2):
- self[i], self[n-i-1] = self[n-i-1], self[i]
-
- def extend(self, values):
- 'S.extend(iterable) -- extend sequence by appending elements from the iterable'
- for v in values:
- self.append(v)
-
- def pop(self, index=-1):
- '''S.pop([index]) -> item -- remove and return item at index (default last).
- Raise IndexError if list is empty or index is out of range.
- '''
- v = self[index]
- del self[index]
- return v
-
- def remove(self, value):
- '''S.remove(value) -- remove first occurrence of value.
- Raise ValueError if the value is not present.
- '''
- del self[self.index(value)]
-
- def __iadd__(self, values):
- self.extend(values)
- return self
-
-MutableSequence.register(list)
-MutableSequence.register(bytearray) # Multiply inheriting, see ByteString
+from _collections_abc import *
+from _collections_abc import __all__
diff --git a/Lib/colorsys.py b/Lib/colorsys.py
index a6c0cf6..b93e384 100644
--- a/Lib/colorsys.py
+++ b/Lib/colorsys.py
@@ -33,17 +33,25 @@ TWO_THIRD = 2.0/3.0
# YIQ: used by composite video signals (linear combinations of RGB)
# Y: perceived grey level (0.0 == black, 1.0 == white)
# I, Q: color components
+#
+# There are a great many versions of the constants used in these formulae.
+# The ones in this library uses constants from the FCC version of NTSC.
def rgb_to_yiq(r, g, b):
y = 0.30*r + 0.59*g + 0.11*b
- i = 0.60*r - 0.28*g - 0.32*b
- q = 0.21*r - 0.52*g + 0.31*b
+ i = 0.74*(r-y) - 0.27*(b-y)
+ q = 0.48*(r-y) + 0.41*(b-y)
return (y, i, q)
def yiq_to_rgb(y, i, q):
- r = y + 0.948262*i + 0.624013*q
- g = y - 0.276066*i - 0.639810*q
- b = y - 1.105450*i + 1.729860*q
+ # r = y + (0.27*q + 0.41*i) / (0.74*0.41 + 0.27*0.48)
+ # b = y + (0.74*q - 0.48*i) / (0.74*0.41 + 0.27*0.48)
+ # g = y - (0.30*(r-y) + 0.11*(b-y)) / 0.59
+
+ r = y + 0.9468822170900693*i + 0.6235565819861433*q
+ g = y - 0.27478764629897834*i - 0.6356910791873801*q
+ b = y - 1.1085450346420322*i + 1.7090069284064666*q
+
if r < 0.0:
r = 0.0
if g < 0.0:
diff --git a/Lib/compileall.py b/Lib/compileall.py
index 693eda9..d957ee5 100644
--- a/Lib/compileall.py
+++ b/Lib/compileall.py
@@ -12,8 +12,7 @@ See module py_compile for details of the actual byte-compilation.
"""
import os
import sys
-import errno
-import imp
+import importlib.util
import py_compile
import struct
@@ -38,7 +37,7 @@ def compile_dir(dir, maxlevels=10, ddir=None, force=False, rx=None,
print('Listing {!r}...'.format(dir))
try:
names = os.listdir(dir)
- except os.error:
+ except OSError:
print("Can't list {!r}".format(dir))
names = []
names.sort()
@@ -91,22 +90,23 @@ def compile_file(fullname, ddir=None, force=False, rx=None, quiet=False,
cfile = fullname + ('c' if __debug__ else 'o')
else:
if optimize >= 0:
- cfile = imp.cache_from_source(fullname,
- debug_override=not optimize)
+ cfile = importlib.util.cache_from_source(
+ fullname, debug_override=not optimize)
else:
- cfile = imp.cache_from_source(fullname)
+ cfile = importlib.util.cache_from_source(fullname)
cache_dir = os.path.dirname(cfile)
head, tail = name[:-3], name[-3:]
if tail == '.py':
if not force:
try:
mtime = int(os.stat(fullname).st_mtime)
- expect = struct.pack('<4sl', imp.get_magic(), mtime)
+ expect = struct.pack('<4sl', importlib.util.MAGIC_NUMBER,
+ mtime)
with open(cfile, 'rb') as chandle:
actual = chandle.read(8)
if expect == actual:
return success
- except IOError:
+ except OSError:
pass
if not quiet:
print('Compiling {!r}...'.format(fullname))
@@ -124,7 +124,7 @@ def compile_file(fullname, ddir=None, force=False, rx=None, quiet=False,
msg = msg.decode(sys.stdout.encoding)
print(msg)
success = 0
- except (SyntaxError, UnicodeError, IOError) as e:
+ except (SyntaxError, UnicodeError, OSError) as e:
if quiet:
print('*** Error compiling {!r}...'.format(fullname))
else:
@@ -209,7 +209,7 @@ def main():
with (sys.stdin if args.flist=='-' else open(args.flist)) as f:
for line in f:
compile_dests.append(line.strip())
- except EnvironmentError:
+ except OSError:
print("Error reading file list {}".format(args.flist))
return False
diff --git a/Lib/concurrent/futures/_base.py b/Lib/concurrent/futures/_base.py
index d45a404..acd05d0 100644
--- a/Lib/concurrent/futures/_base.py
+++ b/Lib/concurrent/futures/_base.py
@@ -200,8 +200,7 @@ def as_completed(fs, timeout=None):
waiter = _create_and_install_waiters(fs, _AS_COMPLETED)
try:
- for future in finished:
- yield future
+ yield from finished
while pending:
if timeout is None:
@@ -226,7 +225,8 @@ def as_completed(fs, timeout=None):
finally:
for f in fs:
- f._waiters.remove(waiter)
+ with f._condition:
+ f._waiters.remove(waiter)
DoneAndNotDoneFutures = collections.namedtuple(
'DoneAndNotDoneFutures', 'done not_done')
@@ -273,7 +273,8 @@ def wait(fs, timeout=None, return_when=ALL_COMPLETED):
waiter.event.wait(timeout)
for f in fs:
- f._waiters.remove(waiter)
+ with f._condition:
+ f._waiters.remove(waiter)
done.update(waiter.finished_futures)
return DoneAndNotDoneFutures(done, set(fs) - done)
diff --git a/Lib/concurrent/futures/process.py b/Lib/concurrent/futures/process.py
index adf2ab4..07b5225 100644
--- a/Lib/concurrent/futures/process.py
+++ b/Lib/concurrent/futures/process.py
@@ -40,7 +40,7 @@ Local worker thread:
Process #1..n:
- reads _CallItems from "Call Q", executes the calls, and puts the resulting
- _ResultItems in "Request Q"
+ _ResultItems in "Result Q"
"""
__author__ = 'Brian Quinlan (brian@sweetapp.com)'
@@ -49,8 +49,9 @@ import atexit
import os
from concurrent.futures import _base
import queue
+from queue import Full
import multiprocessing
-from multiprocessing.queues import SimpleQueue, Full
+from multiprocessing import SimpleQueue
from multiprocessing.connection import wait
import threading
import weakref
@@ -240,6 +241,8 @@ def _queue_management_worker(executor_reference,
"terminated abruptly while the future was "
"running or pending."
))
+ # Delete references to object. See issue16284
+ del work_item
pending_work_items.clear()
# Terminate remaining workers forcibly: the queues or their
# locks may be in a dirty state and block forever.
@@ -264,6 +267,8 @@ def _queue_management_worker(executor_reference,
work_item.future.set_exception(result_item.exception)
else:
work_item.future.set_result(result_item.result)
+ # Delete references to object. See issue16284
+ del work_item
# Check whether we should start shutting down.
executor = executor_reference()
# No more work items can be added if:
@@ -327,7 +332,7 @@ class ProcessPoolExecutor(_base.Executor):
_check_system_limits()
if max_workers is None:
- self._max_workers = multiprocessing.cpu_count()
+ self._max_workers = os.cpu_count() or 1
else:
self._max_workers = max_workers
diff --git a/Lib/concurrent/futures/thread.py b/Lib/concurrent/futures/thread.py
index 95bb682..f9beb0f 100644
--- a/Lib/concurrent/futures/thread.py
+++ b/Lib/concurrent/futures/thread.py
@@ -63,6 +63,8 @@ def _worker(executor_reference, work_queue):
work_item = work_queue.get(block=True)
if work_item is not None:
work_item.run()
+ # Delete references to object. See issue16284
+ del work_item
continue
executor = executor_reference()
# Exit if:
diff --git a/Lib/configparser.py b/Lib/configparser.py
index aebf8a0..4ee8307 100644
--- a/Lib/configparser.py
+++ b/Lib/configparser.py
@@ -670,7 +670,7 @@ class RawConfigParser(MutableMapping):
try:
with open(filename, encoding=encoding) as fp:
self._read(fp, filename)
- except IOError:
+ except OSError:
continue
read_ok.append(filename)
return read_ok
diff --git a/Lib/contextlib.py b/Lib/contextlib.py
index b03f828..82ee955 100644
--- a/Lib/contextlib.py
+++ b/Lib/contextlib.py
@@ -4,7 +4,8 @@ import sys
from collections import deque
from functools import wraps
-__all__ = ["contextmanager", "closing", "ContextDecorator", "ExitStack"]
+__all__ = ["contextmanager", "closing", "ContextDecorator", "ExitStack",
+ "redirect_stdout", "suppress"]
class ContextDecorator(object):
@@ -36,6 +37,16 @@ class _GeneratorContextManager(ContextDecorator):
def __init__(self, func, *args, **kwds):
self.gen = func(*args, **kwds)
self.func, self.args, self.kwds = func, args, kwds
+ # Issue 19330: ensure context manager instances have good docstrings
+ doc = getattr(func, "__doc__", None)
+ if doc is None:
+ doc = type(self).__doc__
+ self.__doc__ = doc
+ # Unfortunately, this still doesn't provide good help output when
+ # inspecting the created context manager instances, since pydoc
+ # currently bypasses the instance docstring and shows the docstring
+ # for the class instead.
+ # See http://bugs.python.org/issue19404 for more details.
def _recreate_cm(self):
# _GCM instances are one-shot context managers, so the
@@ -47,7 +58,7 @@ class _GeneratorContextManager(ContextDecorator):
try:
return next(self.gen)
except StopIteration:
- raise RuntimeError("generator didn't yield")
+ raise RuntimeError("generator didn't yield") from None
def __exit__(self, type, value, traceback):
if type is None:
@@ -140,6 +151,62 @@ class closing(object):
def __exit__(self, *exc_info):
self.thing.close()
+class redirect_stdout:
+ """Context manager for temporarily redirecting stdout to another file
+
+ # How to send help() to stderr
+ with redirect_stdout(sys.stderr):
+ help(dir)
+
+ # How to write help() to a file
+ with open('help.txt', 'w') as f:
+ with redirect_stdout(f):
+ help(pow)
+ """
+
+ def __init__(self, new_target):
+ self._new_target = new_target
+ # We use a list of old targets to make this CM re-entrant
+ self._old_targets = []
+
+ def __enter__(self):
+ self._old_targets.append(sys.stdout)
+ sys.stdout = self._new_target
+ return self._new_target
+
+ def __exit__(self, exctype, excinst, exctb):
+ sys.stdout = self._old_targets.pop()
+
+
+class suppress:
+ """Context manager to suppress specified exceptions
+
+ After the exception is suppressed, execution proceeds with the next
+ statement following the with statement.
+
+ with suppress(FileNotFoundError):
+ os.remove(somefile)
+ # Execution still resumes here if the file was already removed
+ """
+
+ def __init__(self, *exceptions):
+ self._exceptions = exceptions
+
+ def __enter__(self):
+ pass
+
+ def __exit__(self, exctype, excinst, exctb):
+ # Unlike isinstance and issubclass, CPython exception handling
+ # currently only looks at the concrete type hierarchy (ignoring
+ # the instance and subclass checking hooks). While Guido considers
+ # that a bug rather than a feature, it's a fairly hard one to fix
+ # due to various internal implementation details. suppress provides
+ # the simpler issubclass based semantics, rather than trying to
+ # exactly reproduce the limitations of the CPython interpreter.
+ #
+ # See http://bugs.python.org/issue12029 for more details
+ return exctype is not None and issubclass(exctype, self._exceptions)
+
# Inspired by discussions on http://bugs.python.org/issue13585
class ExitStack(object):
diff --git a/Lib/copyreg.py b/Lib/copyreg.py
index 66c0f8a..67f5bb0 100644
--- a/Lib/copyreg.py
+++ b/Lib/copyreg.py
@@ -87,6 +87,12 @@ def _reduce_ex(self, proto):
def __newobj__(cls, *args):
return cls.__new__(cls, *args)
+def __newobj_ex__(cls, args, kwargs):
+ """Used by pickle protocol 4, instead of __newobj__ to allow classes with
+ keyword-only arguments to be pickled correctly.
+ """
+ return cls.__new__(cls, *args, **kwargs)
+
def _slotnames(cls):
"""Return a list of slot names for a given class.
diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py
index c92e130..e34c646 100644
--- a/Lib/ctypes/__init__.py
+++ b/Lib/ctypes/__init__.py
@@ -34,17 +34,15 @@ from _ctypes import FUNCFLAG_CDECL as _FUNCFLAG_CDECL, \
FUNCFLAG_USE_ERRNO as _FUNCFLAG_USE_ERRNO, \
FUNCFLAG_USE_LASTERROR as _FUNCFLAG_USE_LASTERROR
-"""
-WINOLEAPI -> HRESULT
-WINOLEAPI_(type)
-
-STDMETHODCALLTYPE
-
-STDMETHOD(name)
-STDMETHOD_(type, name)
-
-STDAPICALLTYPE
-"""
+# WINOLEAPI -> HRESULT
+# WINOLEAPI_(type)
+#
+# STDMETHODCALLTYPE
+#
+# STDMETHOD(name)
+# STDMETHOD_(type, name)
+#
+# STDAPICALLTYPE
def create_string_buffer(init, size=None):
"""create_string_buffer(aBytes) -> character array
@@ -395,7 +393,7 @@ if _os.name in ("nt", "ce"):
_type_ = "l"
# _check_retval_ is called with the function's result when it
# is used as restype. It checks for the FAILED bit, and
- # raises a WindowsError if it is set.
+ # raises an OSError if it is set.
#
# The _check_retval_ method is implemented in C, so that the
# method definition itself is not included in the traceback
@@ -407,7 +405,7 @@ if _os.name in ("nt", "ce"):
class OleDLL(CDLL):
"""This class represents a dll exporting functions using the
Windows stdcall calling convention, and returning HRESULT.
- HRESULT error values are automatically raised as WindowsError
+ HRESULT error values are automatically raised as OSError
exceptions.
"""
_func_flags_ = _FUNCFLAG_STDCALL
@@ -456,7 +454,7 @@ if _os.name in ("nt", "ce"):
code = GetLastError()
if descr is None:
descr = FormatError(code).strip()
- return WindowsError(None, descr, None, code)
+ return OSError(None, descr, None, code)
if sizeof(c_uint) == sizeof(c_void_p):
c_size_t = c_uint
diff --git a/Lib/ctypes/test/__init__.py b/Lib/ctypes/test/__init__.py
index cc5fe02..7c72210 100644
--- a/Lib/ctypes/test/__init__.py
+++ b/Lib/ctypes/test/__init__.py
@@ -37,7 +37,7 @@ def requires(resource, msg=None):
def find_package_modules(package, mask):
import fnmatch
- if (hasattr(package, "__loader__") and
+ if (package.__loader__ is not None and
hasattr(package.__loader__, '_files')):
path = package.__name__.replace(".", os.path.sep)
mask = os.path.join(path, mask)
diff --git a/Lib/ctypes/test/test_checkretval.py b/Lib/ctypes/test/test_checkretval.py
index 01ccc57..19bb813 100644
--- a/Lib/ctypes/test/test_checkretval.py
+++ b/Lib/ctypes/test/test_checkretval.py
@@ -31,7 +31,7 @@ class Test(unittest.TestCase):
pass
else:
def test_oledll(self):
- self.assertRaises(WindowsError,
+ self.assertRaises(OSError,
oledll.oleaut32.CreateTypeLib2,
0, None, None)
diff --git a/Lib/ctypes/test/test_internals.py b/Lib/ctypes/test/test_internals.py
index cbf2e05..271e3f5 100644
--- a/Lib/ctypes/test/test_internals.py
+++ b/Lib/ctypes/test/test_internals.py
@@ -5,17 +5,14 @@ from sys import getrefcount as grc
# XXX This test must be reviewed for correctness!!!
-"""
-ctypes' types are container types.
-
-They have an internal memory block, which only consists of some bytes,
-but it has to keep references to other objects as well. This is not
-really needed for trivial C types like int or char, but it is important
-for aggregate types like strings or pointers in particular.
-
-What about pointers?
-
-"""
+# ctypes' types are container types.
+#
+# They have an internal memory block, which only consists of some bytes,
+# but it has to keep references to other objects as well. This is not
+# really needed for trivial C types like int or char, but it is important
+# for aggregate types like strings or pointers in particular.
+#
+# What about pointers?
class ObjectsTestCase(unittest.TestCase):
def assertSame(self, a, b):
diff --git a/Lib/ctypes/test/test_macholib.py b/Lib/ctypes/test/test_macholib.py
index eda846d..8a2b2c3 100644
--- a/Lib/ctypes/test/test_macholib.py
+++ b/Lib/ctypes/test/test_macholib.py
@@ -3,35 +3,33 @@ import sys
import unittest
# Bob Ippolito:
-"""
-Ok.. the code to find the filename for __getattr__ should look
-something like:
-
-import os
-from macholib.dyld import dyld_find
-
-def find_lib(name):
- possible = ['lib'+name+'.dylib', name+'.dylib',
- name+'.framework/'+name]
- for dylib in possible:
- try:
- return os.path.realpath(dyld_find(dylib))
- except ValueError:
- pass
- raise ValueError, "%s not found" % (name,)
-
-It'll have output like this:
-
- >>> find_lib('pthread')
-'/usr/lib/libSystem.B.dylib'
- >>> find_lib('z')
-'/usr/lib/libz.1.dylib'
- >>> find_lib('IOKit')
-'/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit'
-
--bob
-
-"""
+#
+# Ok.. the code to find the filename for __getattr__ should look
+# something like:
+#
+# import os
+# from macholib.dyld import dyld_find
+#
+# def find_lib(name):
+# possible = ['lib'+name+'.dylib', name+'.dylib',
+# name+'.framework/'+name]
+# for dylib in possible:
+# try:
+# return os.path.realpath(dyld_find(dylib))
+# except ValueError:
+# pass
+# raise ValueError, "%s not found" % (name,)
+#
+# It'll have output like this:
+#
+# >>> find_lib('pthread')
+# '/usr/lib/libSystem.B.dylib'
+# >>> find_lib('z')
+# '/usr/lib/libz.1.dylib'
+# >>> find_lib('IOKit')
+# '/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit'
+#
+# -bob
from ctypes.macholib.dyld import dyld_find
@@ -52,8 +50,11 @@ class MachOTest(unittest.TestCase):
'/usr/lib/libSystem.B.dylib')
result = find_lib('z')
- self.assertTrue(result.startswith('/usr/lib/libz.1'))
- self.assertTrue(result.endswith('.dylib'))
+ # Issue #21093: dyld default search path includes $HOME/lib and
+ # /usr/local/lib before /usr/lib, which caused test failures if
+ # a local copy of libz exists in one of them. Now ignore the head
+ # of the path.
+ self.assertRegex(result, r".*/lib/libz\..*.*\.dylib")
self.assertEqual(find_lib('IOKit'),
'/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit')
diff --git a/Lib/ctypes/test/test_win32.py b/Lib/ctypes/test/test_win32.py
index da21336..91ad314 100644
--- a/Lib/ctypes/test/test_win32.py
+++ b/Lib/ctypes/test/test_win32.py
@@ -41,7 +41,7 @@ if sys.platform == "win32":
# Call functions with invalid arguments, and make sure
# that access violations are trapped and raise an
# exception.
- self.assertRaises(WindowsError, windll.kernel32.GetModuleHandleA, 32)
+ self.assertRaises(OSError, windll.kernel32.GetModuleHandleA, 32)
def test_noargs(self):
# This is a special case on win32 x64
diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py
index 054c511..595113b 100644
--- a/Lib/ctypes/util.py
+++ b/Lib/ctypes/util.py
@@ -85,7 +85,7 @@ if os.name == "posix" and sys.platform == "darwin":
elif os.name == "posix":
# Andreas Degert's find functions, using gcc, /sbin/ldconfig, objdump
- import re, tempfile, errno
+ import re, tempfile
def _findLib_gcc(name):
expr = r'[^\(\)\s]*lib%s\.[^\(\)\s]*' % re.escape(name)
@@ -102,9 +102,8 @@ elif os.name == "posix":
finally:
try:
os.unlink(ccout)
- except OSError as e:
- if e.errno != errno.ENOENT:
- raise
+ except FileNotFoundError:
+ pass
if rv == 10:
raise OSError('gcc or cc command not found')
res = re.search(expr, trace)
@@ -133,8 +132,10 @@ elif os.name == "posix":
cmd = 'if ! type objdump >/dev/null 2>&1; then exit 10; fi;' \
"objdump -p -j .dynamic 2>/dev/null " + f
f = os.popen(cmd)
- dump = f.read()
- rv = f.close()
+ try:
+ dump = f.read()
+ finally:
+ rv = f.close()
if rv == 10:
raise OSError('objdump command not found')
res = re.search(r'\sSONAME\s+([^\s]+)', dump)
@@ -177,10 +178,11 @@ elif os.name == "posix":
else:
cmd = 'env LC_ALL=C /usr/bin/crle 2>/dev/null'
- for line in os.popen(cmd).readlines():
- line = line.strip()
- if line.startswith('Default Library Path (ELF):'):
- paths = line.split()[4]
+ with contextlib.closing(os.popen(cmd)) as f:
+ for line in f.readlines():
+ line = line.strip()
+ if line.startswith('Default Library Path (ELF):'):
+ paths = line.split()[4]
if not paths:
return None
diff --git a/Lib/datetime.py b/Lib/datetime.py
index d1f353b..1789714 100644
--- a/Lib/datetime.py
+++ b/Lib/datetime.py
@@ -23,9 +23,10 @@ _MAXORDINAL = 3652059 # date.max.toordinal()
# for all computations. See the book for algorithms for converting between
# proleptic Gregorian ordinals and many other calendar systems.
-_DAYS_IN_MONTH = [None, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
+# -1 is a placeholder for indexing purposes.
+_DAYS_IN_MONTH = [-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
-_DAYS_BEFORE_MONTH = [None]
+_DAYS_BEFORE_MONTH = [-1] # -1 is a placeholder for indexing purposes.
dbm = 0
for dim in _DAYS_IN_MONTH[1:]:
_DAYS_BEFORE_MONTH.append(dbm)
@@ -1917,203 +1918,203 @@ timezone.utc = timezone._create(timedelta(0))
timezone.min = timezone._create(timezone._minoffset)
timezone.max = timezone._create(timezone._maxoffset)
_EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc)
-"""
-Some time zone algebra. For a datetime x, let
- x.n = x stripped of its timezone -- its naive time.
- x.o = x.utcoffset(), and assuming that doesn't raise an exception or
- return None
- x.d = x.dst(), and assuming that doesn't raise an exception or
- return None
- x.s = x's standard offset, x.o - x.d
-
-Now some derived rules, where k is a duration (timedelta).
-
-1. x.o = x.s + x.d
- This follows from the definition of x.s.
-
-2. If x and y have the same tzinfo member, x.s = y.s.
- This is actually a requirement, an assumption we need to make about
- sane tzinfo classes.
-
-3. The naive UTC time corresponding to x is x.n - x.o.
- This is again a requirement for a sane tzinfo class.
-
-4. (x+k).s = x.s
- This follows from #2, and that datimetimetz+timedelta preserves tzinfo.
-
-5. (x+k).n = x.n + k
- Again follows from how arithmetic is defined.
-
-Now we can explain tz.fromutc(x). Let's assume it's an interesting case
-(meaning that the various tzinfo methods exist, and don't blow up or return
-None when called).
-
-The function wants to return a datetime y with timezone tz, equivalent to x.
-x is already in UTC.
-
-By #3, we want
-
- y.n - y.o = x.n [1]
-
-The algorithm starts by attaching tz to x.n, and calling that y. So
-x.n = y.n at the start. Then it wants to add a duration k to y, so that [1]
-becomes true; in effect, we want to solve [2] for k:
-
- (y+k).n - (y+k).o = x.n [2]
-
-By #1, this is the same as
-
- (y+k).n - ((y+k).s + (y+k).d) = x.n [3]
-
-By #5, (y+k).n = y.n + k, which equals x.n + k because x.n=y.n at the start.
-Substituting that into [3],
-
- x.n + k - (y+k).s - (y+k).d = x.n; the x.n terms cancel, leaving
- k - (y+k).s - (y+k).d = 0; rearranging,
- k = (y+k).s - (y+k).d; by #4, (y+k).s == y.s, so
- k = y.s - (y+k).d
-
-On the RHS, (y+k).d can't be computed directly, but y.s can be, and we
-approximate k by ignoring the (y+k).d term at first. Note that k can't be
-very large, since all offset-returning methods return a duration of magnitude
-less than 24 hours. For that reason, if y is firmly in std time, (y+k).d must
-be 0, so ignoring it has no consequence then.
-
-In any case, the new value is
- z = y + y.s [4]
+# Some time zone algebra. For a datetime x, let
+# x.n = x stripped of its timezone -- its naive time.
+# x.o = x.utcoffset(), and assuming that doesn't raise an exception or
+# return None
+# x.d = x.dst(), and assuming that doesn't raise an exception or
+# return None
+# x.s = x's standard offset, x.o - x.d
+#
+# Now some derived rules, where k is a duration (timedelta).
+#
+# 1. x.o = x.s + x.d
+# This follows from the definition of x.s.
+#
+# 2. If x and y have the same tzinfo member, x.s = y.s.
+# This is actually a requirement, an assumption we need to make about
+# sane tzinfo classes.
+#
+# 3. The naive UTC time corresponding to x is x.n - x.o.
+# This is again a requirement for a sane tzinfo class.
+#
+# 4. (x+k).s = x.s
+# This follows from #2, and that datimetimetz+timedelta preserves tzinfo.
+#
+# 5. (x+k).n = x.n + k
+# Again follows from how arithmetic is defined.
+#
+# Now we can explain tz.fromutc(x). Let's assume it's an interesting case
+# (meaning that the various tzinfo methods exist, and don't blow up or return
+# None when called).
+#
+# The function wants to return a datetime y with timezone tz, equivalent to x.
+# x is already in UTC.
+#
+# By #3, we want
+#
+# y.n - y.o = x.n [1]
+#
+# The algorithm starts by attaching tz to x.n, and calling that y. So
+# x.n = y.n at the start. Then it wants to add a duration k to y, so that [1]
+# becomes true; in effect, we want to solve [2] for k:
+#
+# (y+k).n - (y+k).o = x.n [2]
+#
+# By #1, this is the same as
+#
+# (y+k).n - ((y+k).s + (y+k).d) = x.n [3]
+#
+# By #5, (y+k).n = y.n + k, which equals x.n + k because x.n=y.n at the start.
+# Substituting that into [3],
+#
+# x.n + k - (y+k).s - (y+k).d = x.n; the x.n terms cancel, leaving
+# k - (y+k).s - (y+k).d = 0; rearranging,
+# k = (y+k).s - (y+k).d; by #4, (y+k).s == y.s, so
+# k = y.s - (y+k).d
+#
+# On the RHS, (y+k).d can't be computed directly, but y.s can be, and we
+# approximate k by ignoring the (y+k).d term at first. Note that k can't be
+# very large, since all offset-returning methods return a duration of magnitude
+# less than 24 hours. For that reason, if y is firmly in std time, (y+k).d must
+# be 0, so ignoring it has no consequence then.
+#
+# In any case, the new value is
+#
+# z = y + y.s [4]
+#
+# It's helpful to step back at look at [4] from a higher level: it's simply
+# mapping from UTC to tz's standard time.
+#
+# At this point, if
+#
+# z.n - z.o = x.n [5]
+#
+# we have an equivalent time, and are almost done. The insecurity here is
+# at the start of daylight time. Picture US Eastern for concreteness. The wall
+# time jumps from 1:59 to 3:00, and wall hours of the form 2:MM don't make good
+# sense then. The docs ask that an Eastern tzinfo class consider such a time to
+# be EDT (because it's "after 2"), which is a redundant spelling of 1:MM EST
+# on the day DST starts. We want to return the 1:MM EST spelling because that's
+# the only spelling that makes sense on the local wall clock.
+#
+# In fact, if [5] holds at this point, we do have the standard-time spelling,
+# but that takes a bit of proof. We first prove a stronger result. What's the
+# difference between the LHS and RHS of [5]? Let
+#
+# diff = x.n - (z.n - z.o) [6]
+#
+# Now
+# z.n = by [4]
+# (y + y.s).n = by #5
+# y.n + y.s = since y.n = x.n
+# x.n + y.s = since z and y are have the same tzinfo member,
+# y.s = z.s by #2
+# x.n + z.s
+#
+# Plugging that back into [6] gives
+#
+# diff =
+# x.n - ((x.n + z.s) - z.o) = expanding
+# x.n - x.n - z.s + z.o = cancelling
+# - z.s + z.o = by #2
+# z.d
+#
+# So diff = z.d.
+#
+# If [5] is true now, diff = 0, so z.d = 0 too, and we have the standard-time
+# spelling we wanted in the endcase described above. We're done. Contrarily,
+# if z.d = 0, then we have a UTC equivalent, and are also done.
+#
+# If [5] is not true now, diff = z.d != 0, and z.d is the offset we need to
+# add to z (in effect, z is in tz's standard time, and we need to shift the
+# local clock into tz's daylight time).
+#
+# Let
+#
+# z' = z + z.d = z + diff [7]
+#
+# and we can again ask whether
+#
+# z'.n - z'.o = x.n [8]
+#
+# If so, we're done. If not, the tzinfo class is insane, according to the
+# assumptions we've made. This also requires a bit of proof. As before, let's
+# compute the difference between the LHS and RHS of [8] (and skipping some of
+# the justifications for the kinds of substitutions we've done several times
+# already):
+#
+# diff' = x.n - (z'.n - z'.o) = replacing z'.n via [7]
+# x.n - (z.n + diff - z'.o) = replacing diff via [6]
+# x.n - (z.n + x.n - (z.n - z.o) - z'.o) =
+# x.n - z.n - x.n + z.n - z.o + z'.o = cancel x.n
+# - z.n + z.n - z.o + z'.o = cancel z.n
+# - z.o + z'.o = #1 twice
+# -z.s - z.d + z'.s + z'.d = z and z' have same tzinfo
+# z'.d - z.d
+#
+# So z' is UTC-equivalent to x iff z'.d = z.d at this point. If they are equal,
+# we've found the UTC-equivalent so are done. In fact, we stop with [7] and
+# return z', not bothering to compute z'.d.
+#
+# How could z.d and z'd differ? z' = z + z.d [7], so merely moving z' by
+# a dst() offset, and starting *from* a time already in DST (we know z.d != 0),
+# would have to change the result dst() returns: we start in DST, and moving
+# a little further into it takes us out of DST.
+#
+# There isn't a sane case where this can happen. The closest it gets is at
+# the end of DST, where there's an hour in UTC with no spelling in a hybrid
+# tzinfo class. In US Eastern, that's 5:MM UTC = 0:MM EST = 1:MM EDT. During
+# that hour, on an Eastern clock 1:MM is taken as being in standard time (6:MM
+# UTC) because the docs insist on that, but 0:MM is taken as being in daylight
+# time (4:MM UTC). There is no local time mapping to 5:MM UTC. The local
+# clock jumps from 1:59 back to 1:00 again, and repeats the 1:MM hour in
+# standard time. Since that's what the local clock *does*, we want to map both
+# UTC hours 5:MM and 6:MM to 1:MM Eastern. The result is ambiguous
+# in local time, but so it goes -- it's the way the local clock works.
+#
+# When x = 5:MM UTC is the input to this algorithm, x.o=0, y.o=-5 and y.d=0,
+# so z=0:MM. z.d=60 (minutes) then, so [5] doesn't hold and we keep going.
+# z' = z + z.d = 1:MM then, and z'.d=0, and z'.d - z.d = -60 != 0 so [8]
+# (correctly) concludes that z' is not UTC-equivalent to x.
+#
+# Because we know z.d said z was in daylight time (else [5] would have held and
+# we would have stopped then), and we know z.d != z'.d (else [8] would have held
+# and we have stopped then), and there are only 2 possible values dst() can
+# return in Eastern, it follows that z'.d must be 0 (which it is in the example,
+# but the reasoning doesn't depend on the example -- it depends on there being
+# two possible dst() outcomes, one zero and the other non-zero). Therefore
+# z' must be in standard time, and is the spelling we want in this case.
+#
+# Note again that z' is not UTC-equivalent as far as the hybrid tzinfo class is
+# concerned (because it takes z' as being in standard time rather than the
+# daylight time we intend here), but returning it gives the real-life "local
+# clock repeats an hour" behavior when mapping the "unspellable" UTC hour into
+# tz.
+#
+# When the input is 6:MM, z=1:MM and z.d=0, and we stop at once, again with
+# the 1:MM standard time spelling we want.
+#
+# So how can this break? One of the assumptions must be violated. Two
+# possibilities:
+#
+# 1) [2] effectively says that y.s is invariant across all y belong to a given
+# time zone. This isn't true if, for political reasons or continental drift,
+# a region decides to change its base offset from UTC.
+#
+# 2) There may be versions of "double daylight" time where the tail end of
+# the analysis gives up a step too early. I haven't thought about that
+# enough to say.
+#
+# In any case, it's clear that the default fromutc() is strong enough to handle
+# "almost all" time zones: so long as the standard offset is invariant, it
+# doesn't matter if daylight time transition points change from year to year, or
+# if daylight time is skipped in some years; it doesn't matter how large or
+# small dst() may get within its bounds; and it doesn't even matter if some
+# perverse time zone returns a negative dst()). So a breaking case must be
+# pretty bizarre, and a tzinfo subclass can override fromutc() if it is.
-It's helpful to step back at look at [4] from a higher level: it's simply
-mapping from UTC to tz's standard time.
-
-At this point, if
-
- z.n - z.o = x.n [5]
-
-we have an equivalent time, and are almost done. The insecurity here is
-at the start of daylight time. Picture US Eastern for concreteness. The wall
-time jumps from 1:59 to 3:00, and wall hours of the form 2:MM don't make good
-sense then. The docs ask that an Eastern tzinfo class consider such a time to
-be EDT (because it's "after 2"), which is a redundant spelling of 1:MM EST
-on the day DST starts. We want to return the 1:MM EST spelling because that's
-the only spelling that makes sense on the local wall clock.
-
-In fact, if [5] holds at this point, we do have the standard-time spelling,
-but that takes a bit of proof. We first prove a stronger result. What's the
-difference between the LHS and RHS of [5]? Let
-
- diff = x.n - (z.n - z.o) [6]
-
-Now
- z.n = by [4]
- (y + y.s).n = by #5
- y.n + y.s = since y.n = x.n
- x.n + y.s = since z and y are have the same tzinfo member,
- y.s = z.s by #2
- x.n + z.s
-
-Plugging that back into [6] gives
-
- diff =
- x.n - ((x.n + z.s) - z.o) = expanding
- x.n - x.n - z.s + z.o = cancelling
- - z.s + z.o = by #2
- z.d
-
-So diff = z.d.
-
-If [5] is true now, diff = 0, so z.d = 0 too, and we have the standard-time
-spelling we wanted in the endcase described above. We're done. Contrarily,
-if z.d = 0, then we have a UTC equivalent, and are also done.
-
-If [5] is not true now, diff = z.d != 0, and z.d is the offset we need to
-add to z (in effect, z is in tz's standard time, and we need to shift the
-local clock into tz's daylight time).
-
-Let
-
- z' = z + z.d = z + diff [7]
-
-and we can again ask whether
-
- z'.n - z'.o = x.n [8]
-
-If so, we're done. If not, the tzinfo class is insane, according to the
-assumptions we've made. This also requires a bit of proof. As before, let's
-compute the difference between the LHS and RHS of [8] (and skipping some of
-the justifications for the kinds of substitutions we've done several times
-already):
-
- diff' = x.n - (z'.n - z'.o) = replacing z'.n via [7]
- x.n - (z.n + diff - z'.o) = replacing diff via [6]
- x.n - (z.n + x.n - (z.n - z.o) - z'.o) =
- x.n - z.n - x.n + z.n - z.o + z'.o = cancel x.n
- - z.n + z.n - z.o + z'.o = cancel z.n
- - z.o + z'.o = #1 twice
- -z.s - z.d + z'.s + z'.d = z and z' have same tzinfo
- z'.d - z.d
-
-So z' is UTC-equivalent to x iff z'.d = z.d at this point. If they are equal,
-we've found the UTC-equivalent so are done. In fact, we stop with [7] and
-return z', not bothering to compute z'.d.
-
-How could z.d and z'd differ? z' = z + z.d [7], so merely moving z' by
-a dst() offset, and starting *from* a time already in DST (we know z.d != 0),
-would have to change the result dst() returns: we start in DST, and moving
-a little further into it takes us out of DST.
-
-There isn't a sane case where this can happen. The closest it gets is at
-the end of DST, where there's an hour in UTC with no spelling in a hybrid
-tzinfo class. In US Eastern, that's 5:MM UTC = 0:MM EST = 1:MM EDT. During
-that hour, on an Eastern clock 1:MM is taken as being in standard time (6:MM
-UTC) because the docs insist on that, but 0:MM is taken as being in daylight
-time (4:MM UTC). There is no local time mapping to 5:MM UTC. The local
-clock jumps from 1:59 back to 1:00 again, and repeats the 1:MM hour in
-standard time. Since that's what the local clock *does*, we want to map both
-UTC hours 5:MM and 6:MM to 1:MM Eastern. The result is ambiguous
-in local time, but so it goes -- it's the way the local clock works.
-
-When x = 5:MM UTC is the input to this algorithm, x.o=0, y.o=-5 and y.d=0,
-so z=0:MM. z.d=60 (minutes) then, so [5] doesn't hold and we keep going.
-z' = z + z.d = 1:MM then, and z'.d=0, and z'.d - z.d = -60 != 0 so [8]
-(correctly) concludes that z' is not UTC-equivalent to x.
-
-Because we know z.d said z was in daylight time (else [5] would have held and
-we would have stopped then), and we know z.d != z'.d (else [8] would have held
-and we have stopped then), and there are only 2 possible values dst() can
-return in Eastern, it follows that z'.d must be 0 (which it is in the example,
-but the reasoning doesn't depend on the example -- it depends on there being
-two possible dst() outcomes, one zero and the other non-zero). Therefore
-z' must be in standard time, and is the spelling we want in this case.
-
-Note again that z' is not UTC-equivalent as far as the hybrid tzinfo class is
-concerned (because it takes z' as being in standard time rather than the
-daylight time we intend here), but returning it gives the real-life "local
-clock repeats an hour" behavior when mapping the "unspellable" UTC hour into
-tz.
-
-When the input is 6:MM, z=1:MM and z.d=0, and we stop at once, again with
-the 1:MM standard time spelling we want.
-
-So how can this break? One of the assumptions must be violated. Two
-possibilities:
-
-1) [2] effectively says that y.s is invariant across all y belong to a given
- time zone. This isn't true if, for political reasons or continental drift,
- a region decides to change its base offset from UTC.
-
-2) There may be versions of "double daylight" time where the tail end of
- the analysis gives up a step too early. I haven't thought about that
- enough to say.
-
-In any case, it's clear that the default fromutc() is strong enough to handle
-"almost all" time zones: so long as the standard offset is invariant, it
-doesn't matter if daylight time transition points change from year to year, or
-if daylight time is skipped in some years; it doesn't matter how large or
-small dst() may get within its bounds; and it doesn't even matter if some
-perverse time zone returns a negative dst()). So a breaking case must be
-pretty bizarre, and a tzinfo subclass can override fromutc() if it is.
-"""
try:
from _datetime import *
except ImportError:
diff --git a/Lib/dbm/__init__.py b/Lib/dbm/__init__.py
index a783fde..5f4664a 100644
--- a/Lib/dbm/__init__.py
+++ b/Lib/dbm/__init__.py
@@ -42,7 +42,7 @@ _names = ['dbm.gnu', 'dbm.ndbm', 'dbm.dumb']
_defaultmod = None
_modules = {}
-error = (error, IOError)
+error = (error, OSError)
try:
from dbm import ndbm
@@ -111,12 +111,10 @@ def whichdb(filename):
try:
f = io.open(filename + ".pag", "rb")
f.close()
- # dbm linked with gdbm on OS/2 doesn't have .dir file
- if not (ndbm.library == "GNU gdbm" and sys.platform == "os2emx"):
- f = io.open(filename + ".dir", "rb")
- f.close()
+ f = io.open(filename + ".dir", "rb")
+ f.close()
return "dbm.ndbm"
- except IOError:
+ except OSError:
# some dbm emulations based on Berkeley DB generate a .db file
# some do not, but they should be caught by the bsd checks
try:
@@ -129,7 +127,7 @@ def whichdb(filename):
d = ndbm.open(filename)
d.close()
return "dbm.ndbm"
- except IOError:
+ except OSError:
pass
# Check for dumbdbm next -- this has a .dir and a .dat file
@@ -146,13 +144,13 @@ def whichdb(filename):
return "dbm.dumb"
finally:
f.close()
- except (OSError, IOError):
+ except OSError:
pass
# See if the file exists, return None if not
try:
f = io.open(filename, "rb")
- except IOError:
+ except OSError:
return None
# Read the start of the file -- the magic number
diff --git a/Lib/dbm/dumb.py b/Lib/dbm/dumb.py
index cfb9123..15dad9c 100644
--- a/Lib/dbm/dumb.py
+++ b/Lib/dbm/dumb.py
@@ -29,7 +29,7 @@ __all__ = ["error", "open"]
_BLOCKSIZE = 512
-error = IOError
+error = OSError
class _Database(collections.MutableMapping):
@@ -67,7 +67,7 @@ class _Database(collections.MutableMapping):
# Mod by Jack: create data file if needed
try:
f = _io.open(self._datfile, 'r', encoding="Latin-1")
- except IOError:
+ except OSError:
f = _io.open(self._datfile, 'w', encoding="Latin-1")
self._chmod(self._datfile)
f.close()
@@ -78,7 +78,7 @@ class _Database(collections.MutableMapping):
self._index = {}
try:
f = _io.open(self._dirfile, 'r', encoding="Latin-1")
- except IOError:
+ except OSError:
pass
else:
for line in f:
@@ -100,12 +100,12 @@ class _Database(collections.MutableMapping):
try:
self._os.unlink(self._bakfile)
- except self._os.error:
+ except OSError:
pass
try:
self._os.rename(self._dirfile, self._bakfile)
- except self._os.error:
+ except OSError:
pass
f = self._io.open(self._dirfile, 'w', encoding="Latin-1")
@@ -118,9 +118,14 @@ class _Database(collections.MutableMapping):
sync = _commit
+ def _verify_open(self):
+ if self._index is None:
+ raise error('DBM object has already been closed')
+
def __getitem__(self, key):
if isinstance(key, str):
key = key.encode('utf-8')
+ self._verify_open()
pos, siz = self._index[key] # may raise KeyError
f = _io.open(self._datfile, 'rb')
f.seek(pos)
@@ -173,6 +178,7 @@ class _Database(collections.MutableMapping):
val = val.encode('utf-8')
elif not isinstance(val, (bytes, bytearray)):
raise TypeError("values must be bytes or strings")
+ self._verify_open()
if key not in self._index:
self._addkey(key, self._addval(val))
else:
@@ -200,6 +206,7 @@ class _Database(collections.MutableMapping):
def __delitem__(self, key):
if isinstance(key, str):
key = key.encode('utf-8')
+ self._verify_open()
# The blocks used by the associated value are lost.
del self._index[key]
# XXX It's unclear why we do a _commit() here (the code always
@@ -209,21 +216,26 @@ class _Database(collections.MutableMapping):
self._commit()
def keys(self):
+ self._verify_open()
return list(self._index.keys())
def items(self):
+ self._verify_open()
return [(key, self[key]) for key in self._index.keys()]
def __contains__(self, key):
if isinstance(key, str):
key = key.encode('utf-8')
+ self._verify_open()
return key in self._index
def iterkeys(self):
+ self._verify_open()
return iter(self._index.keys())
__iter__ = iterkeys
def __len__(self):
+ self._verify_open()
return len(self._index)
def close(self):
@@ -236,6 +248,12 @@ class _Database(collections.MutableMapping):
if hasattr(self._os, 'chmod'):
self._os.chmod(file, self._mode)
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *args):
+ self.close()
+
def open(file, flag=None, mode=0o666):
"""Open the database file, filename, and return corresponding object.
diff --git a/Lib/decimal.py b/Lib/decimal.py
index 9f37e4f..5b98473 100644
--- a/Lib/decimal.py
+++ b/Lib/decimal.py
@@ -142,7 +142,6 @@ __version__ = '1.70' # Highest version of the spec this complies with
# See http://speleotrove.com/decimal/
__libmpdec_version__ = "2.4.0" # compatible libmpdec version
-import copy as _copy
import math as _math
import numbers as _numbers
import sys
@@ -704,8 +703,7 @@ class Decimal(object):
raise TypeError("Cannot convert %r to Decimal" % value)
- # @classmethod, but @decorator is not valid Python 2.3 syntax, so
- # don't use it (see notes on Py2.3 compatibility at top of file)
+ @classmethod
def from_float(cls, f):
"""Converts a float to a decimal number, exactly.
@@ -744,7 +742,6 @@ class Decimal(object):
return result
else:
return cls(result)
- from_float = classmethod(from_float)
def _isnan(self):
"""Returns whether the number is not actually one.
diff --git a/Lib/difflib.py b/Lib/difflib.py
index f0bfcc5..38dfef4 100644
--- a/Lib/difflib.py
+++ b/Lib/difflib.py
@@ -30,7 +30,6 @@ __all__ = ['get_close_matches', 'ndiff', 'restore', 'SequenceMatcher',
'Differ','IS_CHARACTER_JUNK', 'IS_LINE_JUNK', 'context_diff',
'unified_diff', 'HtmlDiff', 'Match']
-import warnings
import heapq
from collections import namedtuple as _namedtuple
@@ -334,20 +333,6 @@ class SequenceMatcher:
for elt in popular: # ditto; as fast for 1% deletion
del b2j[elt]
- def isbjunk(self, item):
- "Deprecated; use 'item in SequenceMatcher().bjunk'."
- warnings.warn("'SequenceMatcher().isbjunk(item)' is deprecated;\n"
- "use 'item in SMinstance.bjunk' instead.",
- DeprecationWarning, 2)
- return item in self.bjunk
-
- def isbpopular(self, item):
- "Deprecated; use 'item in SequenceMatcher().bpopular'."
- warnings.warn("'SequenceMatcher().isbpopular(item)' is deprecated;\n"
- "use 'item in SMinstance.bpopular' instead.",
- DeprecationWarning, 2)
- return item in self.bpopular
-
def find_longest_match(self, alo, ahi, blo, bhi):
"""Find longest matching block in a[alo:ahi] and b[blo:bhi].
@@ -920,8 +905,7 @@ class Differ:
else:
raise ValueError('unknown tag %r' % (tag,))
- for line in g:
- yield line
+ yield from g
def _dump(self, tag, x, lo, hi):
"""Generate comparison results for a same-tagged range."""
@@ -940,8 +924,7 @@ class Differ:
second = self._dump('+', b, blo, bhi)
for g in first, second:
- for line in g:
- yield line
+ yield from g
def _fancy_replace(self, a, alo, ahi, b, blo, bhi):
r"""
@@ -995,8 +978,7 @@ class Differ:
# no non-identical "pretty close" pair
if eqi is None:
# no identical pair either -- treat it as a straight replace
- for line in self._plain_replace(a, alo, ahi, b, blo, bhi):
- yield line
+ yield from self._plain_replace(a, alo, ahi, b, blo, bhi)
return
# no close pair, but an identical pair -- synch up on that
best_i, best_j, best_ratio = eqi, eqj, 1.0
@@ -1008,8 +990,7 @@ class Differ:
# identical
# pump out diffs from before the synch point
- for line in self._fancy_helper(a, alo, best_i, b, blo, best_j):
- yield line
+ yield from self._fancy_helper(a, alo, best_i, b, blo, best_j)
# do intraline marking on the synch pair
aelt, belt = a[best_i], b[best_j]
@@ -1031,15 +1012,13 @@ class Differ:
btags += ' ' * lb
else:
raise ValueError('unknown tag %r' % (tag,))
- for line in self._qformat(aelt, belt, atags, btags):
- yield line
+ yield from self._qformat(aelt, belt, atags, btags)
else:
# the synch pair is identical
yield ' ' + aelt
# pump out diffs from after the synch point
- for line in self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi):
- yield line
+ yield from self._fancy_helper(a, best_i+1, ahi, b, best_j+1, bhi)
def _fancy_helper(self, a, alo, ahi, b, blo, bhi):
g = []
@@ -1051,8 +1030,7 @@ class Differ:
elif blo < bhi:
g = self._dump('+', b, blo, bhi)
- for line in g:
- yield line
+ yield from g
def _qformat(self, aline, bline, atags, btags):
r"""
diff --git a/Lib/dis.py b/Lib/dis.py
index 543fdc7..81cbe7f 100644
--- a/Lib/dis.py
+++ b/Lib/dis.py
@@ -2,12 +2,15 @@
import sys
import types
+import collections
+import io
from opcode import *
from opcode import __all__ as _opcodes_all
__all__ = ["code_info", "dis", "disassemble", "distb", "disco",
- "findlinestarts", "findlabels", "show_code"] + _opcodes_all
+ "findlinestarts", "findlabels", "show_code",
+ "get_instructions", "Instruction", "Bytecode"] + _opcodes_all
del _opcodes_all
_have_code = (types.MethodType, types.FunctionType, types.CodeType, type)
@@ -25,14 +28,14 @@ def _try_compile(source, name):
c = compile(source, name, 'exec')
return c
-def dis(x=None):
+def dis(x=None, *, file=None):
"""Disassemble classes, methods, functions, or code.
With no argument, disassemble the last traceback.
"""
if x is None:
- distb()
+ distb(file=file)
return
if hasattr(x, '__func__'): # Method
x = x.__func__
@@ -42,23 +45,23 @@ def dis(x=None):
items = sorted(x.__dict__.items())
for name, x1 in items:
if isinstance(x1, _have_code):
- print("Disassembly of %s:" % name)
+ print("Disassembly of %s:" % name, file=file)
try:
- dis(x1)
+ dis(x1, file=file)
except TypeError as msg:
- print("Sorry:", msg)
- print()
+ print("Sorry:", msg, file=file)
+ print(file=file)
elif hasattr(x, 'co_code'): # Code object
- disassemble(x)
+ disassemble(x, file=file)
elif isinstance(x, (bytes, bytearray)): # Raw bytecode
- _disassemble_bytes(x)
+ _disassemble_bytes(x, file=file)
elif isinstance(x, str): # Source code
- _disassemble_str(x)
+ _disassemble_str(x, file=file)
else:
raise TypeError("don't know how to disassemble %s objects" %
type(x).__name__)
-def distb(tb=None):
+def distb(tb=None, *, file=None):
"""Disassemble a traceback (default: last traceback)."""
if tb is None:
try:
@@ -66,7 +69,7 @@ def distb(tb=None):
except AttributeError:
raise RuntimeError("no last traceback to disassemble")
while tb.tb_next: tb = tb.tb_next
- disassemble(tb.tb_frame.f_code, tb.tb_lasti)
+ disassemble(tb.tb_frame.f_code, tb.tb_lasti, file=file)
# The inspect module interrogates this dictionary to build its
# list of CO_* constants. It is also used by pretty_flags to
@@ -95,19 +98,22 @@ def pretty_flags(flags):
names.append(hex(flags))
return ", ".join(names)
-def code_info(x):
- """Formatted details of methods, functions, or code."""
+def _get_code_object(x):
+ """Helper to handle methods, functions, strings and raw code objects"""
if hasattr(x, '__func__'): # Method
x = x.__func__
if hasattr(x, '__code__'): # Function
x = x.__code__
if isinstance(x, str): # Source code
- x = _try_compile(x, "<code_info>")
+ x = _try_compile(x, "<disassembly>")
if hasattr(x, 'co_code'): # Code object
- return _format_code_info(x)
- else:
- raise TypeError("don't know how to disassemble %s objects" %
- type(x).__name__)
+ return x
+ raise TypeError("don't know how to disassemble %s objects" %
+ type(x).__name__)
+
+def code_info(x):
+ """Formatted details of methods, functions, or code."""
+ return _format_code_info(_get_code_object(x))
def _format_code_info(co):
lines = []
@@ -140,106 +146,206 @@ def _format_code_info(co):
lines.append("%4d: %s" % i_n)
return "\n".join(lines)
-def show_code(co):
- """Print details of methods, functions, or code to stdout."""
- print(code_info(co))
+def show_code(co, *, file=None):
+ """Print details of methods, functions, or code to *file*.
-def disassemble(co, lasti=-1):
- """Disassemble a code object."""
- code = co.co_code
- labels = findlabels(code)
+ If *file* is not provided, the output is printed on stdout.
+ """
+ print(code_info(co), file=file)
+
+_Instruction = collections.namedtuple("_Instruction",
+ "opname opcode arg argval argrepr offset starts_line is_jump_target")
+
+class Instruction(_Instruction):
+ """Details for a bytecode operation
+
+ Defined fields:
+ opname - human readable name for operation
+ opcode - numeric code for operation
+ arg - numeric argument to operation (if any), otherwise None
+ argval - resolved arg value (if known), otherwise same as arg
+ argrepr - human readable description of operation argument
+ offset - start index of operation within bytecode sequence
+ starts_line - line started by this opcode (if any), otherwise None
+ is_jump_target - True if other code jumps to here, otherwise False
+ """
+
+ def _disassemble(self, lineno_width=3, mark_as_current=False):
+ """Format instruction details for inclusion in disassembly output
+
+ *lineno_width* sets the width of the line number field (0 omits it)
+ *mark_as_current* inserts a '-->' marker arrow as part of the line
+ """
+ fields = []
+ # Column: Source code line number
+ if lineno_width:
+ if self.starts_line is not None:
+ lineno_fmt = "%%%dd" % lineno_width
+ fields.append(lineno_fmt % self.starts_line)
+ else:
+ fields.append(' ' * lineno_width)
+ # Column: Current instruction indicator
+ if mark_as_current:
+ fields.append('-->')
+ else:
+ fields.append(' ')
+ # Column: Jump target marker
+ if self.is_jump_target:
+ fields.append('>>')
+ else:
+ fields.append(' ')
+ # Column: Instruction offset from start of code sequence
+ fields.append(repr(self.offset).rjust(4))
+ # Column: Opcode name
+ fields.append(self.opname.ljust(20))
+ # Column: Opcode argument
+ if self.arg is not None:
+ fields.append(repr(self.arg).rjust(5))
+ # Column: Opcode argument details
+ if self.argrepr:
+ fields.append('(' + self.argrepr + ')')
+ return ' '.join(fields).rstrip()
+
+
+def get_instructions(x, *, first_line=None):
+ """Iterator for the opcodes in methods, functions or code
+
+ Generates a series of Instruction named tuples giving the details of
+ each operations in the supplied code.
+
+ If *first_line* is not None, it indicates the line number that should
+ be reported for the first source line in the disassembled code.
+ Otherwise, the source line information (if any) is taken directly from
+ the disassembled code object.
+ """
+ co = _get_code_object(x)
+ cell_names = co.co_cellvars + co.co_freevars
linestarts = dict(findlinestarts(co))
- n = len(code)
- i = 0
+ if first_line is not None:
+ line_offset = first_line - co.co_firstlineno
+ else:
+ line_offset = 0
+ return _get_instructions_bytes(co.co_code, co.co_varnames, co.co_names,
+ co.co_consts, cell_names, linestarts,
+ line_offset)
+
+def _get_const_info(const_index, const_list):
+ """Helper to get optional details about const references
+
+ Returns the dereferenced constant and its repr if the constant
+ list is defined.
+ Otherwise returns the constant index and its repr().
+ """
+ argval = const_index
+ if const_list is not None:
+ argval = const_list[const_index]
+ return argval, repr(argval)
+
+def _get_name_info(name_index, name_list):
+ """Helper to get optional details about named references
+
+ Returns the dereferenced name as both value and repr if the name
+ list is defined.
+ Otherwise returns the name index and its repr().
+ """
+ argval = name_index
+ if name_list is not None:
+ argval = name_list[name_index]
+ argrepr = argval
+ else:
+ argrepr = repr(argval)
+ return argval, argrepr
+
+
+def _get_instructions_bytes(code, varnames=None, names=None, constants=None,
+ cells=None, linestarts=None, line_offset=0):
+ """Iterate over the instructions in a bytecode string.
+
+ Generates a sequence of Instruction namedtuples giving the details of each
+ opcode. Additional information about the code's runtime environment
+ (e.g. variable names, constants) can be specified using optional
+ arguments.
+
+ """
+ labels = findlabels(code)
extended_arg = 0
+ starts_line = None
free = None
+ # enumerate() is not an option, since we sometimes process
+ # multiple elements on a single pass through the loop
+ n = len(code)
+ i = 0
while i < n:
op = code[i]
- if i in linestarts:
- if i > 0:
- print()
- print("%3d" % linestarts[i], end=' ')
- else:
- print(' ', end=' ')
-
- if i == lasti: print('-->', end=' ')
- else: print(' ', end=' ')
- if i in labels: print('>>', end=' ')
- else: print(' ', end=' ')
- print(repr(i).rjust(4), end=' ')
- print(opname[op].ljust(20), end=' ')
+ offset = i
+ if linestarts is not None:
+ starts_line = linestarts.get(i, None)
+ if starts_line is not None:
+ starts_line += line_offset
+ is_jump_target = i in labels
i = i+1
+ arg = None
+ argval = None
+ argrepr = ''
if op >= HAVE_ARGUMENT:
- oparg = code[i] + code[i+1]*256 + extended_arg
+ arg = code[i] + code[i+1]*256 + extended_arg
extended_arg = 0
i = i+2
if op == EXTENDED_ARG:
- extended_arg = oparg*65536
- print(repr(oparg).rjust(5), end=' ')
+ extended_arg = arg*65536
+ # Set argval to the dereferenced value of the argument when
+ # availabe, and argrepr to the string representation of argval.
+ # _disassemble_bytes needs the string repr of the
+ # raw name index for LOAD_GLOBAL, LOAD_CONST, etc.
+ argval = arg
if op in hasconst:
- print('(' + repr(co.co_consts[oparg]) + ')', end=' ')
+ argval, argrepr = _get_const_info(arg, constants)
elif op in hasname:
- print('(' + co.co_names[oparg] + ')', end=' ')
+ argval, argrepr = _get_name_info(arg, names)
elif op in hasjrel:
- print('(to ' + repr(i + oparg) + ')', end=' ')
+ argval = i + arg
+ argrepr = "to " + repr(argval)
elif op in haslocal:
- print('(' + co.co_varnames[oparg] + ')', end=' ')
+ argval, argrepr = _get_name_info(arg, varnames)
elif op in hascompare:
- print('(' + cmp_op[oparg] + ')', end=' ')
+ argval = cmp_op[arg]
+ argrepr = argval
elif op in hasfree:
- if free is None:
- free = co.co_cellvars + co.co_freevars
- print('(' + free[oparg] + ')', end=' ')
+ argval, argrepr = _get_name_info(arg, cells)
elif op in hasnargs:
- print('(%d positional, %d keyword pair)'
- % (code[i-2], code[i-1]), end=' ')
- print()
+ argrepr = "%d positional, %d keyword pair" % (code[i-2], code[i-1])
+ yield Instruction(opname[op], op,
+ arg, argval, argrepr,
+ offset, starts_line, is_jump_target)
+
+def disassemble(co, lasti=-1, *, file=None):
+ """Disassemble a code object."""
+ cell_names = co.co_cellvars + co.co_freevars
+ linestarts = dict(findlinestarts(co))
+ _disassemble_bytes(co.co_code, lasti, co.co_varnames, co.co_names,
+ co.co_consts, cell_names, linestarts, file=file)
def _disassemble_bytes(code, lasti=-1, varnames=None, names=None,
- constants=None):
- labels = findlabels(code)
- n = len(code)
- i = 0
- while i < n:
- op = code[i]
- if i == lasti: print('-->', end=' ')
- else: print(' ', end=' ')
- if i in labels: print('>>', end=' ')
- else: print(' ', end=' ')
- print(repr(i).rjust(4), end=' ')
- print(opname[op].ljust(15), end=' ')
- i = i+1
- if op >= HAVE_ARGUMENT:
- oparg = code[i] + code[i+1]*256
- i = i+2
- print(repr(oparg).rjust(5), end=' ')
- if op in hasconst:
- if constants:
- print('(' + repr(constants[oparg]) + ')', end=' ')
- else:
- print('(%d)'%oparg, end=' ')
- elif op in hasname:
- if names is not None:
- print('(' + names[oparg] + ')', end=' ')
- else:
- print('(%d)'%oparg, end=' ')
- elif op in hasjrel:
- print('(to ' + repr(i + oparg) + ')', end=' ')
- elif op in haslocal:
- if varnames:
- print('(' + varnames[oparg] + ')', end=' ')
- else:
- print('(%d)' % oparg, end=' ')
- elif op in hascompare:
- print('(' + cmp_op[oparg] + ')', end=' ')
- elif op in hasnargs:
- print('(%d positional, %d keyword pair)'
- % (code[i-2], code[i-1]), end=' ')
- print()
+ constants=None, cells=None, linestarts=None,
+ *, file=None, line_offset=0):
+ # Omit the line number column entirely if we have no line number info
+ show_lineno = linestarts is not None
+ # TODO?: Adjust width upwards if max(linestarts.values()) >= 1000?
+ lineno_width = 3 if show_lineno else 0
+ for instr in _get_instructions_bytes(code, varnames, names,
+ constants, cells, linestarts,
+ line_offset=line_offset):
+ new_source_line = (show_lineno and
+ instr.starts_line is not None and
+ instr.offset > 0)
+ if new_source_line:
+ print(file=file)
+ is_current_instr = instr.offset == lasti
+ print(instr._disassemble(lineno_width, is_current_instr), file=file)
-def _disassemble_str(source):
+def _disassemble_str(source, *, file=None):
"""Compile the source string, then disassemble the code object."""
- disassemble(_try_compile(source, '<dis>'))
+ disassemble(_try_compile(source, '<dis>'), file=file)
disco = disassemble # XXX For backwards compatibility
@@ -250,19 +356,21 @@ def findlabels(code):
"""
labels = []
+ # enumerate() is not an option, since we sometimes process
+ # multiple elements on a single pass through the loop
n = len(code)
i = 0
while i < n:
op = code[i]
i = i+1
if op >= HAVE_ARGUMENT:
- oparg = code[i] + code[i+1]*256
+ arg = code[i] + code[i+1]*256
i = i+2
label = -1
if op in hasjrel:
- label = i+oparg
+ label = i+arg
elif op in hasjabs:
- label = oparg
+ label = arg
if label >= 0:
if label not in labels:
labels.append(label)
@@ -290,27 +398,77 @@ def findlinestarts(code):
if lineno != lastlineno:
yield (addr, lineno)
+class Bytecode:
+ """The bytecode operations of a piece of code
+
+ Instantiate this with a function, method, string of code, or a code object
+ (as returned by compile()).
+
+ Iterating over this yields the bytecode operations as Instruction instances.
+ """
+ def __init__(self, x, *, first_line=None, current_offset=None):
+ self.codeobj = co = _get_code_object(x)
+ if first_line is None:
+ self.first_line = co.co_firstlineno
+ self._line_offset = 0
+ else:
+ self.first_line = first_line
+ self._line_offset = first_line - co.co_firstlineno
+ self._cell_names = co.co_cellvars + co.co_freevars
+ self._linestarts = dict(findlinestarts(co))
+ self._original_object = x
+ self.current_offset = current_offset
+
+ def __iter__(self):
+ co = self.codeobj
+ return _get_instructions_bytes(co.co_code, co.co_varnames, co.co_names,
+ co.co_consts, self._cell_names,
+ self._linestarts,
+ line_offset=self._line_offset)
+
+ def __repr__(self):
+ return "{}({!r})".format(self.__class__.__name__,
+ self._original_object)
+
+ @classmethod
+ def from_traceback(cls, tb):
+ """ Construct a Bytecode from the given traceback """
+ while tb.tb_next:
+ tb = tb.tb_next
+ return cls(tb.tb_frame.f_code, current_offset=tb.tb_lasti)
+
+ def info(self):
+ """Return formatted information about the code object."""
+ return _format_code_info(self.codeobj)
+
+ def dis(self):
+ """Return a formatted view of the bytecode operations."""
+ co = self.codeobj
+ if self.current_offset is not None:
+ offset = self.current_offset
+ else:
+ offset = -1
+ with io.StringIO() as output:
+ _disassemble_bytes(co.co_code, varnames=co.co_varnames,
+ names=co.co_names, constants=co.co_consts,
+ cells=self._cell_names,
+ linestarts=self._linestarts,
+ line_offset=self._line_offset,
+ file=output,
+ lasti=offset)
+ return output.getvalue()
+
+
def _test():
"""Simple test program to disassemble a file."""
- if sys.argv[1:]:
- if sys.argv[2:]:
- sys.stderr.write("usage: python dis.py [-|file]\n")
- sys.exit(2)
- fn = sys.argv[1]
- if not fn or fn == "-":
- fn = None
- else:
- fn = None
- if fn is None:
- f = sys.stdin
- else:
- f = open(fn)
- source = f.read()
- if fn is not None:
- f.close()
- else:
- fn = "<stdin>"
- code = compile(source, fn, "exec")
+ import argparse
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument('infile', type=argparse.FileType(), nargs='?', default='-')
+ args = parser.parse_args()
+ with args.infile as infile:
+ source = infile.read()
+ code = compile(source, args.infile.name, "exec")
dis(code)
if __name__ == "__main__":
diff --git a/Lib/distutils/__init__.py b/Lib/distutils/__init__.py
index 2e8719f..04804c1 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.3.5"
+__version__ = "3.4.0"
#--end constants--
diff --git a/Lib/distutils/archive_util.py b/Lib/distutils/archive_util.py
index fcda08e..4470bb0 100644
--- a/Lib/distutils/archive_util.py
+++ b/Lib/distutils/archive_util.py
@@ -18,15 +18,55 @@ from distutils.spawn import spawn
from distutils.dir_util import mkpath
from distutils import log
-def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0):
+try:
+ from pwd import getpwnam
+except ImportError:
+ getpwnam = None
+
+try:
+ from grp import getgrnam
+except ImportError:
+ getgrnam = None
+
+def _get_gid(name):
+ """Returns a gid, given a group name."""
+ if getgrnam is None or name is None:
+ return None
+ try:
+ result = getgrnam(name)
+ except KeyError:
+ result = None
+ if result is not None:
+ return result[2]
+ return None
+
+def _get_uid(name):
+ """Returns an uid, given a user name."""
+ if getpwnam is None or name is None:
+ return None
+ try:
+ result = getpwnam(name)
+ except KeyError:
+ result = None
+ if result is not None:
+ return result[2]
+ return None
+
+def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0,
+ owner=None, group=None):
"""Create a (possibly compressed) tar file from all the files under
'base_dir'.
'compress' must be "gzip" (the default), "compress", "bzip2", or None.
- Both "tar" and the compression utility named by 'compress' must be on
- the default program search path, so this is probably Unix-specific.
+ (compress will be deprecated in Python 3.2)
+
+ 'owner' and 'group' can be used to define an owner and a group for the
+ archive that is being built. If not provided, the current owner and group
+ will be used.
+
The output tar file will be named 'base_dir' + ".tar", possibly plus
the appropriate compression extension (".gz", ".bz2" or ".Z").
+
Returns the output filename.
"""
tar_compression = {'gzip': 'gz', 'bzip2': 'bz2', None: '', 'compress': ''}
@@ -48,10 +88,23 @@ def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0):
import tarfile # late import so Python build itself doesn't break
log.info('Creating tar archive')
+
+ uid = _get_uid(owner)
+ gid = _get_gid(group)
+
+ def _set_uid_gid(tarinfo):
+ if gid is not None:
+ tarinfo.gid = gid
+ tarinfo.gname = group
+ if uid is not None:
+ tarinfo.uid = uid
+ tarinfo.uname = owner
+ return tarinfo
+
if not dry_run:
tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress])
try:
- tar.add(base_dir)
+ tar.add(base_dir, filter=_set_uid_gid)
finally:
tar.close()
@@ -140,7 +193,7 @@ def check_archive_formats(formats):
return None
def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0,
- dry_run=0):
+ dry_run=0, owner=None, group=None):
"""Create an archive file (eg. zip or tar).
'base_name' is the name of the file to create, minus any format-specific
@@ -153,6 +206,9 @@ def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0,
ie. 'base_dir' will be the common prefix of all files and
directories in the archive. 'root_dir' and 'base_dir' both default
to the current directory. Returns the name of the archive file.
+
+ 'owner' and 'group' are used when creating a tar archive. By default,
+ uses the current owner and group.
"""
save_cwd = os.getcwd()
if root_dir is not None:
@@ -174,6 +230,11 @@ def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0,
func = format_info[0]
for arg, val in format_info[1]:
kwargs[arg] = val
+
+ if format != 'zip':
+ kwargs['owner'] = owner
+ kwargs['group'] = group
+
try:
filename = func(base_name, base_dir, **kwargs)
finally:
diff --git a/Lib/distutils/ccompiler.py b/Lib/distutils/ccompiler.py
index c795c95..911e84d 100644
--- a/Lib/distutils/ccompiler.py
+++ b/Lib/distutils/ccompiler.py
@@ -351,7 +351,7 @@ class CCompiler:
return macros, objects, extra, pp_opts, build
def _get_cc_args(self, pp_opts, debug, before):
- # works for unixccompiler, emxccompiler, cygwinccompiler
+ # works for unixccompiler, cygwinccompiler
cc_args = pp_opts + ['-c']
if debug:
cc_args[:0] = ['-g']
@@ -926,7 +926,6 @@ _default_compilers = (
# on a cygwin built python we can use gcc like an ordinary UNIXish
# compiler
('cygwin.*', 'unix'),
- ('os2emx', 'emx'),
# OS name mappings
('posix', 'unix'),
@@ -968,8 +967,6 @@ compiler_class = { 'unix': ('unixccompiler', 'UnixCCompiler',
"Mingw32 port of GNU C Compiler for Win32"),
'bcpp': ('bcppcompiler', 'BCPPCompiler',
"Borland C++ Compiler"),
- 'emx': ('emxccompiler', 'EMXCCompiler',
- "EMX port of GNU C Compiler for OS/2"),
}
def show_compilers():
diff --git a/Lib/distutils/cmd.py b/Lib/distutils/cmd.py
index 3ea0810..c89d5ef 100644
--- a/Lib/distutils/cmd.py
+++ b/Lib/distutils/cmd.py
@@ -365,9 +365,11 @@ class Command:
from distutils.spawn import spawn
spawn(cmd, search_path, dry_run=self.dry_run)
- def make_archive(self, base_name, format, root_dir=None, base_dir=None):
+ def make_archive(self, base_name, format, root_dir=None, base_dir=None,
+ owner=None, group=None):
return archive_util.make_archive(base_name, format, root_dir, base_dir,
- dry_run=self.dry_run)
+ 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):
diff --git a/Lib/distutils/command/bdist.py b/Lib/distutils/command/bdist.py
index c5188eb..6814a1c 100644
--- a/Lib/distutils/command/bdist.py
+++ b/Lib/distutils/command/bdist.py
@@ -37,6 +37,12 @@ class bdist(Command):
"[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']
@@ -52,8 +58,7 @@ class bdist(Command):
# This won't do in reality: will need to distinguish RPM-ish Linux,
# Debian-ish Linux, Solaris, FreeBSD, ..., Windows, Mac OS.
default_format = {'posix': 'gztar',
- 'nt': 'zip',
- 'os2': 'zip'}
+ 'nt': 'zip'}
# Establish the preferred order (for the --help-formats option).
format_commands = ['rpm', 'gztar', 'bztar', 'ztar', 'tar',
@@ -78,6 +83,8 @@ class bdist(Command):
self.formats = None
self.dist_dir = None
self.skip_build = 0
+ self.group = None
+ self.owner = None
def finalize_options(self):
# have to finalize 'plat_name' before 'bdist_base'
@@ -123,6 +130,11 @@ class bdist(Command):
if cmd_name not in self.no_format_option:
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:]:
diff --git a/Lib/distutils/command/bdist_dumb.py b/Lib/distutils/command/bdist_dumb.py
index 1ab09d1..4405d12 100644
--- a/Lib/distutils/command/bdist_dumb.py
+++ b/Lib/distutils/command/bdist_dumb.py
@@ -33,13 +33,18 @@ class bdist_dumb(Command):
('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' }
+ 'nt': 'zip' }
def initialize_options(self):
self.bdist_dir = None
@@ -49,6 +54,8 @@ class bdist_dumb(Command):
self.dist_dir = None
self.skip_build = None
self.relative = 0
+ self.owner = None
+ self.group = None
def finalize_options(self):
if self.bdist_dir is None:
@@ -85,11 +92,6 @@ class bdist_dumb(Command):
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
@@ -107,7 +109,8 @@ class bdist_dumb(Command):
# Make the archive
filename = self.make_archive(pseudoinstall_root,
- self.format, root_dir=archive_root)
+ self.format, root_dir=archive_root,
+ owner=self.owner, group=self.group)
if self.distribution.has_ext_modules():
pyversion = get_python_version()
else:
diff --git a/Lib/distutils/command/build_ext.py b/Lib/distutils/command/build_ext.py
index bc6a23f..80689b6 100644
--- a/Lib/distutils/command/build_ext.py
+++ b/Lib/distutils/command/build_ext.py
@@ -230,11 +230,6 @@ class build_ext(Command):
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':
@@ -619,9 +614,6 @@ class build_ext(Command):
return fn
else:
return "swig.exe"
- elif os.name == "os2":
- # assume swig available in the PATH.
- return "swig.exe"
else:
raise DistutilsPlatformError(
"I don't know how to find (much less run) SWIG "
@@ -672,9 +664,6 @@ class build_ext(Command):
"""
from distutils.sysconfig import get_config_var
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
ext_suffix = get_config_var('EXT_SUFFIX')
if os.name == 'nt' and self.debug:
@@ -695,7 +684,7 @@ class build_ext(Command):
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).
+ on Windows, 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
@@ -715,19 +704,6 @@ class build_ext(Command):
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.hexversion >> 24, (sys.hexversion >> 16) & 0xff))
- # 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 %
diff --git a/Lib/distutils/command/build_py.py b/Lib/distutils/command/build_py.py
index d48eb69..9100b96 100644
--- a/Lib/distutils/command/build_py.py
+++ b/Lib/distutils/command/build_py.py
@@ -3,7 +3,7 @@
Implements the Distutils 'build_py' command."""
import os
-import imp
+import importlib.util
import sys
from glob import glob
@@ -313,11 +313,11 @@ class build_py (Command):
outputs.append(filename)
if include_bytecode:
if self.compile:
- outputs.append(imp.cache_from_source(filename,
- debug_override=True))
+ outputs.append(importlib.util.cache_from_source(
+ filename, debug_override=True))
if self.optimize > 0:
- outputs.append(imp.cache_from_source(filename,
- debug_override=False))
+ outputs.append(importlib.util.cache_from_source(
+ filename, debug_override=False))
outputs += [
os.path.join(build_dir, filename)
diff --git a/Lib/distutils/command/build_scripts.py b/Lib/distutils/command/build_scripts.py
index 4b5b22e..90a8380 100644
--- a/Lib/distutils/command/build_scripts.py
+++ b/Lib/distutils/command/build_scripts.py
@@ -74,7 +74,7 @@ class build_scripts(Command):
# script.
try:
f = open(script, "rb")
- except IOError:
+ except OSError:
if not self.dry_run:
raise
f = None
diff --git a/Lib/distutils/command/install.py b/Lib/distutils/command/install.py
index 3c675d1..456511c 100644
--- a/Lib/distutils/command/install.py
+++ b/Lib/distutils/command/install.py
@@ -58,13 +58,6 @@ INSTALL_SCHEMES = {
'data' : '$base',
},
'nt': WINDOWS_SCHEME,
- 'os2': {
- 'purelib': '$base/Lib/site-packages',
- 'platlib': '$base/Lib/site-packages',
- 'headers': '$base/Include/$dist_name',
- 'scripts': '$base/Scripts',
- 'data' : '$base',
- },
}
# user site schemes
@@ -86,14 +79,6 @@ if HAS_USER_SITE:
'data' : '$userbase',
}
- INSTALL_SCHEMES['os2_home'] = {
- 'purelib': '$usersite',
- 'platlib': '$usersite',
- 'headers': '$userbase/include/python$py_version_short/$dist_name',
- 'scripts': '$userbase/bin',
- 'data' : '$userbase',
- }
-
# The keys to an installation scheme; if any new types of files are to be
# installed, be sure to add an entry to every installation scheme above,
# and to SCHEME_KEYS here.
diff --git a/Lib/distutils/command/install_lib.py b/Lib/distutils/command/install_lib.py
index 15c08f1..215813b 100644
--- a/Lib/distutils/command/install_lib.py
+++ b/Lib/distutils/command/install_lib.py
@@ -4,7 +4,7 @@ Implements the Distutils 'install_lib' command
(install all Python modules)."""
import os
-import imp
+import importlib.util
import sys
from distutils.core import Command
@@ -165,10 +165,10 @@ class install_lib(Command):
if ext != PYTHON_SOURCE_EXTENSION:
continue
if self.compile:
- bytecode_files.append(imp.cache_from_source(
+ bytecode_files.append(importlib.util.cache_from_source(
py_file, debug_override=True))
if self.optimize > 0:
- bytecode_files.append(imp.cache_from_source(
+ bytecode_files.append(importlib.util.cache_from_source(
py_file, debug_override=False))
return bytecode_files
diff --git a/Lib/distutils/command/sdist.py b/Lib/distutils/command/sdist.py
index 116f67e..7ea3d5f 100644
--- a/Lib/distutils/command/sdist.py
+++ b/Lib/distutils/command/sdist.py
@@ -74,6 +74,10 @@ class sdist(Command):
('metadata-check', None,
"Ensure that all required elements of meta-data "
"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]"),
]
boolean_options = ['use-defaults', 'prune',
@@ -113,6 +117,8 @@ class sdist(Command):
self.archive_files = None
self.metadata_check = 1
+ self.owner = None
+ self.group = None
def finalize_options(self):
if self.manifest is None:
@@ -444,7 +450,8 @@ class sdist(Command):
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)
+ 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))
diff --git a/Lib/distutils/command/upload.py b/Lib/distutils/command/upload.py
index e30c189..d6762e4 100644
--- a/Lib/distutils/command/upload.py
+++ b/Lib/distutils/command/upload.py
@@ -182,7 +182,7 @@ class upload(PyPIRCCommand):
result = urlopen(request)
status = result.getcode()
reason = result.msg
- except socket.error as e:
+ except OSError as e:
self.announce(str(e), log.ERROR)
return
except HTTPError as e:
diff --git a/Lib/distutils/config.py b/Lib/distutils/config.py
index 106e146..382aca8 100644
--- a/Lib/distutils/config.py
+++ b/Lib/distutils/config.py
@@ -83,6 +83,15 @@ class PyPIRCCommand(Command):
current[key] = config.get(server, key)
else:
current[key] = default
+
+ # work around people having "repository" for the "pypi"
+ # section of their config set to the HTTP (rather than
+ # HTTPS) URL
+ if (server == 'pypi' and
+ repository in (self.DEFAULT_REPOSITORY, 'pypi')):
+ current['repository'] = self.DEFAULT_REPOSITORY
+ return current
+
if (current['server'] == repository or
current['repository'] == repository):
return current
diff --git a/Lib/distutils/core.py b/Lib/distutils/core.py
index 25d91ba..2bfe66a 100644
--- a/Lib/distutils/core.py
+++ b/Lib/distutils/core.py
@@ -127,8 +127,9 @@ def setup (**attrs):
if _setup_stop_after == "config":
return dist
- # Parse the command line; any command-line errors are the end user's
- # fault, so turn them into SystemExit to suppress tracebacks.
+ # Parse the command line and override config files; any
+ # command-line errors are the end user's fault, so turn them into
+ # SystemExit to suppress tracebacks.
try:
ok = dist.parse_command_line()
except DistutilsArgError as msg:
@@ -147,7 +148,7 @@ def setup (**attrs):
dist.run_commands()
except KeyboardInterrupt:
raise SystemExit("interrupted")
- except (IOError, os.error) as exc:
+ except OSError as exc:
if DEBUG:
sys.stderr.write("error: %s\n" % (exc,))
raise
diff --git a/Lib/distutils/cygwinccompiler.py b/Lib/distutils/cygwinccompiler.py
index e0074a1..d28b1b3 100644
--- a/Lib/distutils/cygwinccompiler.py
+++ b/Lib/distutils/cygwinccompiler.py
@@ -54,7 +54,8 @@ import re
from distutils.ccompiler import gen_preprocess_options, gen_lib_options
from distutils.unixccompiler import UnixCCompiler
from distutils.file_util import write_file
-from distutils.errors import DistutilsExecError, CompileError, UnknownFileError
+from distutils.errors import (DistutilsExecError, CCompilerError,
+ CompileError, UnknownFileError)
from distutils import log
from distutils.version import LooseVersion
from distutils.spawn import find_executable
@@ -294,18 +295,17 @@ class Mingw32CCompiler(CygwinCCompiler):
else:
entry_point = ''
- if self.gcc_version < '4' or is_cygwingcc():
- no_cygwin = ' -mno-cygwin'
- else:
- no_cygwin = ''
-
- self.set_executables(compiler='gcc%s -O -Wall' % no_cygwin,
- compiler_so='gcc%s -mdll -O -Wall' % no_cygwin,
- compiler_cxx='g++%s -O -Wall' % no_cygwin,
- linker_exe='gcc%s' % no_cygwin,
- linker_so='%s%s %s %s'
- % (self.linker_dll, no_cygwin,
- shared_option, entry_point))
+ if is_cygwingcc():
+ raise CCompilerError(
+ 'Cygwin gcc cannot be used with --compiler=mingw32')
+
+ self.set_executables(compiler='gcc -O -Wall',
+ compiler_so='gcc -mdll -O -Wall',
+ compiler_cxx='g++ -O -Wall',
+ linker_exe='gcc',
+ linker_so='%s %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')
@@ -364,7 +364,7 @@ def check_config_h():
return CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn
finally:
config_h.close()
- except IOError as exc:
+ except OSError as exc:
return (CONFIG_H_UNCERTAIN,
"couldn't read '%s': %s" % (fn, exc.strerror))
diff --git a/Lib/distutils/dir_util.py b/Lib/distutils/dir_util.py
index 6a72bdd..9879b0d 100644
--- a/Lib/distutils/dir_util.py
+++ b/Lib/distutils/dir_util.py
@@ -124,7 +124,7 @@ def copy_tree(src, dst, preserve_mode=1, preserve_times=1,
"cannot copy tree '%s': not a directory" % src)
try:
names = os.listdir(src)
- except os.error as e:
+ except OSError as e:
(errno, errstr) = e
if dry_run:
names = []
@@ -197,7 +197,7 @@ def remove_tree(directory, verbose=1, dry_run=0):
abspath = os.path.abspath(cmd[1])
if abspath in _path_created:
del _path_created[abspath]
- except (IOError, OSError) as exc:
+ except OSError as exc:
log.warn("error removing %s: %s", directory, exc)
def ensure_relative(path):
diff --git a/Lib/distutils/dist.py b/Lib/distutils/dist.py
index 11a2102..7eb04bc 100644
--- a/Lib/distutils/dist.py
+++ b/Lib/distutils/dist.py
@@ -52,7 +52,9 @@ class Distribution:
('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'),
+ ]
# 'common_usage' is a short (2-3 line) string describing the common
# usage of the setup script.
@@ -259,6 +261,22 @@ Common commands: (see '--help-commands' for more)
else:
sys.stderr.write(msg + "\n")
+ # 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):
@@ -310,7 +328,10 @@ Common commands: (see '--help-commands' for more)
Distutils installation directory (ie. where the top-level
Distutils __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.
+ 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()
@@ -330,15 +351,19 @@ Common commands: (see '--help-commands' for more)
user_filename = "pydistutils.cfg"
# And look for the user config file
- user_file = os.path.join(os.path.expanduser('~'), user_filename)
- if os.path.isfile(user_file):
- files.append(user_file)
+ if self.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 DEBUG:
+ self.announce("using config files: %s" % ', '.join(files))
+
return files
def parse_config_files(self, filenames=None):
diff --git a/Lib/distutils/emxccompiler.py b/Lib/distutils/emxccompiler.py
deleted file mode 100644
index 3675f8d..0000000
--- a/Lib/distutils/emxccompiler.py
+++ /dev/null
@@ -1,315 +0,0 @@
-"""distutils.emxccompiler
-
-Provides the EMXCCompiler class, a subclass of UnixCCompiler that
-handles the EMX port of the GNU C compiler to OS/2.
-"""
-
-# issues:
-#
-# * OS/2 insists that DLLs can have names no longer than 8 characters
-# We put export_symbols in a def-file, as though the DLL can have
-# an arbitrary length name, but truncate the output filename.
-#
-# * only use OMF objects and use LINK386 as the linker (-Zomf)
-#
-# * always build for multithreading (-Zmt) as the accompanying OS/2 port
-# of Python is only distributed with threads enabled.
-#
-# tested configurations:
-#
-# * EMX gcc 2.81/EMX 0.9d fix03
-
-import os,sys,copy
-from distutils.ccompiler import gen_preprocess_options, gen_lib_options
-from distutils.unixccompiler import UnixCCompiler
-from distutils.file_util import write_file
-from distutils.errors import DistutilsExecError, CompileError, UnknownFileError
-from distutils import log
-
-class EMXCCompiler (UnixCCompiler):
-
- compiler_type = 'emx'
- obj_extension = ".obj"
- static_lib_extension = ".lib"
- shared_lib_extension = ".dll"
- static_lib_format = "%s%s"
- shared_lib_format = "%s%s"
- res_extension = ".res" # compiled resource file
- exe_extension = ".exe"
-
- def __init__ (self,
- verbose=0,
- dry_run=0,
- force=0):
-
- UnixCCompiler.__init__ (self, verbose, dry_run, force)
-
- (status, details) = check_config_h()
- self.debug_print("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." % details) +
- "Compiling may fail because of undefined preprocessor macros.")
-
- (self.gcc_version, self.ld_version) = \
- get_versions()
- self.debug_print(self.compiler_type + ": gcc %s, ld %s\n" %
- (self.gcc_version,
- self.ld_version) )
-
- # Hard-code GCC because that's what this is all about.
- # XXX optimization, warnings etc. should be customizable.
- self.set_executables(compiler='gcc -Zomf -Zmt -O3 -fomit-frame-pointer -mprobe -Wall',
- compiler_so='gcc -Zomf -Zmt -O3 -fomit-frame-pointer -mprobe -Wall',
- linker_exe='gcc -Zomf -Zmt -Zcrtdll',
- linker_so='gcc -Zomf -Zmt -Zcrtdll -Zdll')
-
- # want the gcc library statically linked (so that we don't have
- # to distribute a version dependent on the compiler we have)
- self.dll_libraries=["gcc"]
-
- # __init__ ()
-
- def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
- if ext == '.rc':
- # gcc requires '.rc' compiled to binary ('.res') files !!!
- try:
- self.spawn(["rc", "-r", src])
- except DistutilsExecError 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 DistutilsExecError 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=0,
- extra_preargs=None,
- extra_postargs=None,
- build_temp=None,
- target_lang=None):
-
- # use separate copies, so we can modify the lists
- extra_preargs = copy.copy(extra_preargs or [])
- libraries = copy.copy(libraries or [])
- objects = copy.copy(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)):
- # (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")
-
- # Generate .def file
- contents = [
- "LIBRARY %s INITINSTANCE TERMINSTANCE" % \
- os.path.splitext(os.path.basename(output_filename))[0],
- "DATA MULTIPLE NONSHARED",
- "EXPORTS"]
- for sym in export_symbols:
- contents.append(' "%s"' % sym)
- self.execute(write_file, (def_file, contents),
- "writing %s" % def_file)
-
- # next add options for def-file and to creating import libraries
- # for gcc/ld the def-file is specified as any other 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")
-
- UnixCCompiler.link(self,
- 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)
-
- # link ()
-
- # -- Miscellaneous methods -----------------------------------------
-
- # override the object_filenames method from CCompiler to
- # support rc and res-files
- def object_filenames (self,
- source_filenames,
- strip_dir=0,
- 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']):
- raise UnknownFileError("unknown file type '%s' (from '%s')" % \
- (ext, src_name))
- if strip_dir:
- base = os.path.basename (base)
- if ext == '.rc':
- # these need to be compiled to object files
- 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
-
- # object_filenames ()
-
- # override the find_library_file method from UnixCCompiler
- # to deal with file naming/searching differences
- def find_library_file(self, dirs, lib, debug=0):
- shortlib = '%s.lib' % lib
- longlib = 'lib%s.lib' % lib # this form very rare
-
- # get EMX's default library directory search path
- try:
- emx_dirs = os.environ['LIBRARY_PATH'].split(';')
- except KeyError:
- emx_dirs = []
-
- for dir in dirs + emx_dirs:
- shortlibp = os.path.join(dir, shortlib)
- longlibp = os.path.join(dir, longlib)
- if os.path.exists(shortlibp):
- return shortlibp
- elif os.path.exists(longlibp):
- return longlibp
-
- # Oops, didn't find it in *any* of 'dirs'
- return None
-
-# class EMXCCompiler
-
-
-# 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 (specifically, pyconfig.h)
- 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...
-
- from distutils import sysconfig
- # if sys.version contains GCC then python was compiled with
- # GCC, and the pyconfig.h file should be OK
- if sys.version.find("GCC") >= 0:
- return (CONFIG_H_OK, "sys.version mentions 'GCC'")
-
- fn = sysconfig.get_config_h_filename()
- try:
- # It would probably better to read single lines to search.
- # But we do this only once, and it is fast enough
- f = open(fn)
- try:
- s = f.read()
- finally:
- f.close()
-
- except IOError as exc:
- # if we can't read this file, we cannot say it is wrong
- # the compiler will complain later about this file as missing
- return (CONFIG_H_UNCERTAIN,
- "couldn't read '%s': %s" % (fn, exc.strerror))
-
- else:
- # "pyconfig.h" contains an "#ifdef __GNUC__" or something similar
- if s.find("__GNUC__") >= 0:
- return (CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn)
- else:
- return (CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn)
-
-
-def get_versions():
- """ Try to find out the versions of gcc and ld.
- If not possible it returns None for it.
- """
- from distutils.version import StrictVersion
- from distutils.spawn import find_executable
- import re
-
- gcc_exe = find_executable('gcc')
- if gcc_exe:
- out = os.popen(gcc_exe + ' -dumpversion','r')
- try:
- out_string = out.read()
- finally:
- out.close()
- result = re.search('(\d+\.\d+\.\d+)', out_string, re.ASCII)
- if result:
- gcc_version = StrictVersion(result.group(1))
- else:
- gcc_version = None
- else:
- gcc_version = None
- # EMX ld has no way of reporting version number, and we use GCC
- # anyway - so we can link OMF DLLs
- ld_version = None
- return (gcc_version, ld_version)
diff --git a/Lib/distutils/errors.py b/Lib/distutils/errors.py
index eb13c98..8b93059 100644
--- a/Lib/distutils/errors.py
+++ b/Lib/distutils/errors.py
@@ -35,8 +35,8 @@ class DistutilsArgError (DistutilsError):
class DistutilsFileError (DistutilsError):
"""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."""
+ Typically this is for problems that we detect before OSError
+ could be raised."""
pass
class DistutilsOptionError (DistutilsError):
diff --git a/Lib/distutils/file_util.py b/Lib/distutils/file_util.py
index 9bdd14e..f6ed290 100644
--- a/Lib/distutils/file_util.py
+++ b/Lib/distutils/file_util.py
@@ -27,26 +27,26 @@ def _copy_file_contents(src, dst, buffer_size=16*1024):
try:
try:
fsrc = open(src, 'rb')
- except os.error as e:
+ except OSError as e:
raise DistutilsFileError("could not open '%s': %s" % (src, e.strerror))
if os.path.exists(dst):
try:
os.unlink(dst)
- except os.error as e:
+ except OSError as e:
raise DistutilsFileError(
"could not delete '%s': %s" % (dst, e.strerror))
try:
fdst = open(dst, 'wb')
- except os.error as e:
+ except OSError as e:
raise DistutilsFileError(
"could not create '%s': %s" % (dst, e.strerror))
while True:
try:
buf = fsrc.read(buffer_size)
- except os.error as e:
+ except OSError as e:
raise DistutilsFileError(
"could not read from '%s': %s" % (src, e.strerror))
@@ -55,7 +55,7 @@ def _copy_file_contents(src, dst, buffer_size=16*1024):
try:
fdst.write(buf)
- except os.error as e:
+ except OSError as e:
raise DistutilsFileError(
"could not write to '%s': %s" % (dst, e.strerror))
finally:
@@ -193,7 +193,7 @@ def move_file (src, dst,
copy_it = False
try:
os.rename(src, dst)
- except os.error as e:
+ except OSError as e:
(num, msg) = e
if num == errno.EXDEV:
copy_it = True
@@ -205,11 +205,11 @@ def move_file (src, dst,
copy_file(src, dst, verbose=verbose)
try:
os.unlink(src)
- except os.error as e:
+ except OSError as e:
(num, msg) = e
try:
os.unlink(dst)
- except os.error:
+ except OSError:
pass
raise DistutilsFileError(
"couldn't move '%s' to '%s' by copy/delete: "
diff --git a/Lib/distutils/msvc9compiler.py b/Lib/distutils/msvc9compiler.py
index b3f6ce1..9688f20 100644
--- a/Lib/distutils/msvc9compiler.py
+++ b/Lib/distutils/msvc9compiler.py
@@ -729,7 +729,7 @@ class MSVCCompiler(CCompiler) :
return manifest_file
finally:
manifest_f.close()
- except IOError:
+ except OSError:
pass
# -- Miscellaneous methods -----------------------------------------
diff --git a/Lib/distutils/spawn.py b/Lib/distutils/spawn.py
index f66ff93..22e87e8 100644
--- a/Lib/distutils/spawn.py
+++ b/Lib/distutils/spawn.py
@@ -36,8 +36,6 @@ def spawn(cmd, search_path=1, verbose=0, dry_run=0):
_spawn_posix(cmd, search_path, dry_run=dry_run)
elif os.name == 'nt':
_spawn_nt(cmd, search_path, dry_run=dry_run)
- elif os.name == 'os2':
- _spawn_os2(cmd, search_path, dry_run=dry_run)
else:
raise DistutilsPlatformError(
"don't know how to spawn programs on platform '%s'" % os.name)
@@ -82,30 +80,6 @@ def _spawn_nt(cmd, search_path=1, verbose=0, dry_run=0):
raise DistutilsExecError(
"command %r failed with exit status %d" % (cmd, rc))
-def _spawn_os2(cmd, search_path=1, verbose=0, dry_run=0):
- executable = cmd[0]
- if search_path:
- # either we find one or it stays the same
- executable = find_executable(executable) or executable
- log.info(' '.join([executable] + cmd[1:]))
- if not dry_run:
- # spawnv for OS/2 EMX requires a full path to the .exe
- try:
- rc = os.spawnv(os.P_WAIT, executable, cmd)
- except OSError as exc:
- # this seems to happen when the command isn't found
- if not DEBUG:
- cmd = executable
- raise DistutilsExecError(
- "command %r failed: %s" % (cmd, exc.args[-1]))
- if rc != 0:
- # and this reflects the command running but failing
- if not DEBUG:
- cmd = executable
- log.debug("command %r failed with exit status %d" % (cmd, rc))
- raise DistutilsExecError(
- "command %r failed with exit status %d" % (cmd, rc))
-
if sys.platform == 'darwin':
from distutils import sysconfig
_cfg_target = None
@@ -207,7 +181,7 @@ def find_executable(executable, path=None):
paths = path.split(os.pathsep)
base, ext = os.path.splitext(executable)
- if (sys.platform == 'win32' or os.name == 'os2') and (ext != '.exe'):
+ if (sys.platform == 'win32') and (ext != '.exe'):
executable = executable + '.exe'
if not os.path.isfile(executable):
diff --git a/Lib/distutils/sysconfig.py b/Lib/distutils/sysconfig.py
index b947988..75537db 100644
--- a/Lib/distutils/sysconfig.py
+++ b/Lib/distutils/sysconfig.py
@@ -114,8 +114,6 @@ def get_python_inc(plat_specific=0, prefix=None):
return os.path.join(prefix, "include", python_dir)
elif os.name == "nt":
return os.path.join(prefix, "include")
- elif os.name == "os2":
- return os.path.join(prefix, "Include")
else:
raise DistutilsPlatformError(
"I don't know where Python installs its C header files "
@@ -157,11 +155,6 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None):
return prefix
else:
return os.path.join(prefix, "Lib", "site-packages")
- elif os.name == "os2":
- if standard_lib:
- return os.path.join(prefix, "Lib")
- else:
- return os.path.join(prefix, "Lib", "site-packages")
else:
raise DistutilsPlatformError(
"I don't know where Python installs its library "
@@ -442,7 +435,7 @@ def _init_posix():
try:
filename = get_makefile_filename()
parse_makefile(filename, g)
- except IOError as msg:
+ except OSError as msg:
my_msg = "invalid Python installation: unable to open %s" % filename
if hasattr(msg, "strerror"):
my_msg = my_msg + " (%s)" % msg.strerror
@@ -454,7 +447,7 @@ def _init_posix():
filename = get_config_h_filename()
with open(filename) as file:
parse_config_h(file, g)
- except IOError as msg:
+ except OSError as msg:
my_msg = "invalid Python installation: unable to open %s" % filename
if hasattr(msg, "strerror"):
my_msg = my_msg + " (%s)" % msg.strerror
@@ -492,7 +485,6 @@ def _init_nt():
# XXX hmmm.. a normal install puts include files here
g['INCLUDEPY'] = get_python_inc(plat_specific=0)
- g['SO'] = '.pyd'
g['EXT_SUFFIX'] = '.pyd'
g['EXE'] = ".exe"
g['VERSION'] = get_python_version().replace(".", "")
@@ -502,24 +494,6 @@ def _init_nt():
_config_vars = g
-def _init_os2():
- """Initialize the module as appropriate for OS/2"""
- g = {}
- # set basic install directories
- g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1)
- g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1)
-
- # XXX hmmm.. a normal install puts include files here
- g['INCLUDEPY'] = get_python_inc(plat_specific=0)
-
- g['SO'] = '.pyd'
- g['EXT_SUFFIX'] = '.pyd'
- g['EXE'] = ".exe"
-
- global _config_vars
- _config_vars = g
-
-
def get_config_vars(*args):
"""With no arguments, return a dictionary of all configuration
variables relevant for the current platform. Generally this includes
@@ -544,6 +518,11 @@ def get_config_vars(*args):
_config_vars['prefix'] = PREFIX
_config_vars['exec_prefix'] = EXEC_PREFIX
+ # For backward compatibility, see issue19555
+ SO = _config_vars.get('EXT_SUFFIX')
+ if SO is not None:
+ _config_vars['SO'] = SO
+
# Always convert srcdir to an absolute path
srcdir = _config_vars.get('srcdir', project_base)
if os.name == 'posix':
@@ -594,4 +573,7 @@ def get_config_var(name):
returned by 'get_config_vars()'. Equivalent to
get_config_vars().get(name)
"""
+ if name == 'SO':
+ import warnings
+ warnings.warn('SO is deprecated, use EXT_SUFFIX', DeprecationWarning, 2)
return get_config_vars().get(name)
diff --git a/Lib/distutils/tests/test_archive_util.py b/Lib/distutils/tests/test_archive_util.py
index d3fb24a..2d72af4 100644
--- a/Lib/distutils/tests/test_archive_util.py
+++ b/Lib/distutils/tests/test_archive_util.py
@@ -16,6 +16,13 @@ from distutils.tests import support
from test.support import check_warnings, run_unittest, patch
try:
+ import grp
+ import pwd
+ UID_GID_SUPPORT = True
+except ImportError:
+ UID_GID_SUPPORT = False
+
+try:
import zipfile
ZIP_SUPPORT = True
except ImportError:
@@ -77,7 +84,7 @@ class ArchiveUtilTestCase(support.TempdirManager,
tmpdir2 = self.mkdtemp()
unittest.skipUnless(splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0],
- "Source and target should be on same drive")
+ "source and target should be on same drive")
base_name = os.path.join(tmpdir2, target_name)
@@ -275,6 +282,58 @@ class ArchiveUtilTestCase(support.TempdirManager,
finally:
del ARCHIVE_FORMATS['xxx']
+ def test_make_archive_owner_group(self):
+ # testing make_archive with owner and group, with various combinations
+ # this works even if there's not gid/uid support
+ if UID_GID_SUPPORT:
+ group = grp.getgrgid(0)[0]
+ owner = pwd.getpwuid(0)[0]
+ else:
+ group = owner = 'root'
+
+ base_dir, root_dir, base_name = self._create_files()
+ base_name = os.path.join(self.mkdtemp() , 'archive')
+ res = make_archive(base_name, 'zip', root_dir, base_dir, owner=owner,
+ group=group)
+ self.assertTrue(os.path.exists(res))
+
+ res = make_archive(base_name, 'zip', root_dir, base_dir)
+ self.assertTrue(os.path.exists(res))
+
+ res = make_archive(base_name, 'tar', root_dir, base_dir,
+ owner=owner, group=group)
+ self.assertTrue(os.path.exists(res))
+
+ res = make_archive(base_name, 'tar', root_dir, base_dir,
+ owner='kjhkjhkjg', group='oihohoh')
+ self.assertTrue(os.path.exists(res))
+
+ @unittest.skipUnless(ZLIB_SUPPORT, "Requires zlib")
+ @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support")
+ def test_tarfile_root_owner(self):
+ tmpdir, tmpdir2, base_name = self._create_files()
+ old_dir = os.getcwd()
+ os.chdir(tmpdir)
+ group = grp.getgrgid(0)[0]
+ owner = pwd.getpwuid(0)[0]
+ try:
+ archive_name = make_tarball(base_name, 'dist', compress=None,
+ owner=owner, group=group)
+ finally:
+ os.chdir(old_dir)
+
+ # check if the compressed tarball was created
+ self.assertTrue(os.path.exists(archive_name))
+
+ # now checks the rights
+ archive = tarfile.open(archive_name)
+ try:
+ for member in archive.getmembers():
+ self.assertEqual(member.uid, 0)
+ self.assertEqual(member.gid, 0)
+ finally:
+ archive.close()
+
def test_suite():
return unittest.makeSuite(ArchiveUtilTestCase)
diff --git a/Lib/distutils/tests/test_bdist_dumb.py b/Lib/distutils/tests/test_bdist_dumb.py
index 0ad32d4..c8ccdc2 100644
--- a/Lib/distutils/tests/test_bdist_dumb.py
+++ b/Lib/distutils/tests/test_bdist_dumb.py
@@ -1,7 +1,6 @@
"""Tests for distutils.command.bdist_dumb."""
import os
-import imp
import sys
import zipfile
import unittest
@@ -75,8 +74,6 @@ class BuildDumbTestCase(support.TempdirManager,
# 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])
@@ -90,7 +87,7 @@ class BuildDumbTestCase(support.TempdirManager,
contents = sorted(os.path.basename(fn) for fn in contents)
wanted = ['foo-0.1-py%s.%s.egg-info' % sys.version_info[:2], 'foo.py']
if not sys.dont_write_bytecode:
- wanted.append('foo.%s.pyc' % imp.get_tag())
+ wanted.append('foo.%s.pyc' % sys.implementation.cache_tag)
self.assertEqual(contents, sorted(wanted))
def test_suite():
diff --git a/Lib/distutils/tests/test_build_py.py b/Lib/distutils/tests/test_build_py.py
index 2ce9d44..c8f6b89 100644
--- a/Lib/distutils/tests/test_build_py.py
+++ b/Lib/distutils/tests/test_build_py.py
@@ -2,7 +2,6 @@
import os
import sys
-import imp
import unittest
from distutils.command.build_py import build_py
@@ -63,7 +62,8 @@ class BuildPyTestCase(support.TempdirManager,
self.assertFalse(os.path.exists(pycache_dir))
else:
pyc_files = os.listdir(pycache_dir)
- self.assertIn("__init__.%s.pyc" % imp.get_tag(), pyc_files)
+ self.assertIn("__init__.%s.pyc" % sys.implementation.cache_tag,
+ pyc_files)
def test_empty_package_dir(self):
# See bugs #1668596/#1720897
@@ -102,7 +102,8 @@ class BuildPyTestCase(support.TempdirManager,
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()])
+ self.assertEqual(found,
+ ['boiledeggs.%s.pyc' % sys.implementation.cache_tag])
@unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled')
def test_byte_compile_optimized(self):
@@ -119,7 +120,8 @@ class BuildPyTestCase(support.TempdirManager,
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.pyo' % imp.get_tag()])
+ self.assertEqual(sorted(found),
+ ['boiledeggs.%s.pyo' % sys.implementation.cache_tag])
def test_dir_in_package_data(self):
"""
diff --git a/Lib/distutils/tests/test_dist.py b/Lib/distutils/tests/test_dist.py
index 61ac57d..b7fd3fb 100644
--- a/Lib/distutils/tests/test_dist.py
+++ b/Lib/distutils/tests/test_dist.py
@@ -39,6 +39,7 @@ class TestDistribution(Distribution):
class DistributionTestCase(support.LoggingSilencer,
+ support.TempdirManager,
support.EnvironGuard,
unittest.TestCase):
@@ -213,6 +214,34 @@ class DistributionTestCase(support.LoggingSilencer,
self.assertRaises(ValueError, dist.announce, args, kwargs)
+ def test_find_config_files_disable(self):
+ # Ticket #1180: Allow user to disable their home config file.
+ temp_home = self.mkdtemp()
+ if os.name == 'posix':
+ user_filename = os.path.join(temp_home, ".pydistutils.cfg")
+ else:
+ user_filename = os.path.join(temp_home, "pydistutils.cfg")
+
+ with open(user_filename, 'w') as f:
+ f.write('[distutils]\n')
+
+ def _expander(path):
+ return temp_home
+
+ old_expander = os.path.expanduser
+ os.path.expanduser = _expander
+ try:
+ d = Distribution()
+ all_files = d.find_config_files()
+
+ d = Distribution(attrs={'script_args': ['--no-user-cfg']})
+ files = d.find_config_files()
+ finally:
+ os.path.expanduser = old_expander
+
+ # make sure --no-user-cfg disables the user cfg file
+ self.assertEqual(len(all_files)-1, len(files))
+
class MetadataTestCase(support.TempdirManager, support.EnvironGuard,
unittest.TestCase):
diff --git a/Lib/distutils/tests/test_install.py b/Lib/distutils/tests/test_install.py
index ede88e5..18e1e57 100644
--- a/Lib/distutils/tests/test_install.py
+++ b/Lib/distutils/tests/test_install.py
@@ -1,7 +1,6 @@
"""Tests for distutils.command.install."""
import os
-import imp
import sys
import unittest
import site
@@ -94,7 +93,7 @@ class InstallTestCase(support.TempdirManager,
self.addCleanup(cleanup)
- for key in ('nt_user', 'unix_user', 'os2_home'):
+ for key in ('nt_user', 'unix_user'):
self.assertIn(key, INSTALL_SCHEMES)
dist = Distribution({'name': 'xx'})
@@ -193,7 +192,8 @@ class InstallTestCase(support.TempdirManager,
f.close()
found = [os.path.basename(line) for line in content.splitlines()]
- expected = ['hello.py', 'hello.%s.pyc' % imp.get_tag(), 'sayhi',
+ expected = ['hello.py', 'hello.%s.pyc' % sys.implementation.cache_tag,
+ 'sayhi',
'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]]
self.assertEqual(found, expected)
diff --git a/Lib/distutils/tests/test_install_lib.py b/Lib/distutils/tests/test_install_lib.py
index d0dfca0..40dd1a9 100644
--- a/Lib/distutils/tests/test_install_lib.py
+++ b/Lib/distutils/tests/test_install_lib.py
@@ -1,7 +1,7 @@
"""Tests for distutils.command.install_data."""
import sys
import os
-import imp
+import importlib.util
import unittest
from distutils.command.install_lib import install_lib
@@ -44,8 +44,10 @@ class InstallLibTestCase(support.TempdirManager,
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', debug_override=True)
- pyo_file = imp.cache_from_source('foo.py', debug_override=False)
+ pyc_file = importlib.util.cache_from_source('foo.py',
+ debug_override=True)
+ pyo_file = importlib.util.cache_from_source('foo.py',
+ debug_override=False)
self.assertTrue(os.path.exists(pyc_file))
self.assertTrue(os.path.exists(pyo_file))
diff --git a/Lib/distutils/tests/test_sdist.py b/Lib/distutils/tests/test_sdist.py
index c952406..5a04e0d 100644
--- a/Lib/distutils/tests/test_sdist.py
+++ b/Lib/distutils/tests/test_sdist.py
@@ -14,6 +14,12 @@ try:
except ImportError:
ZLIB_SUPPORT = False
+try:
+ import grp
+ import pwd
+ UID_GID_SUPPORT = True
+except ImportError:
+ UID_GID_SUPPORT = False
from distutils.command.sdist import sdist, show_formats
from distutils.core import Distribution
@@ -423,6 +429,54 @@ class SDistTestCase(PyPIRCCommandTestCase):
self.assertEqual(sorted(filenames), ['fake-1.0', 'fake-1.0/PKG-INFO',
'fake-1.0/README.manual'])
+ @unittest.skipUnless(ZLIB_SUPPORT, "requires zlib")
+ @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support")
+ @unittest.skipIf(find_executable('tar') is None,
+ "The tar command is not found")
+ @unittest.skipIf(find_executable('gzip') is None,
+ "The gzip command is not found")
+ def test_make_distribution_owner_group(self):
+ # now 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')
+ archive = tarfile.open(archive_name)
+ try:
+ for member in archive.getmembers():
+ self.assertEqual(member.uid, 0)
+ self.assertEqual(member.gid, 0)
+ finally:
+ archive.close()
+
+ # 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')
+ archive = tarfile.open(archive_name)
+
+ # note that we are not testing the group ownership here
+ # because, depending on the platforms and the container
+ # rights (see #7408)
+ try:
+ for member in archive.getmembers():
+ self.assertEqual(member.uid, os.getuid())
+ finally:
+ archive.close()
+
def test_suite():
return unittest.makeSuite(SDistTestCase)
diff --git a/Lib/distutils/tests/test_sysconfig.py b/Lib/distutils/tests/test_sysconfig.py
index a1cb47d..95fa9dc 100644
--- a/Lib/distutils/tests/test_sysconfig.py
+++ b/Lib/distutils/tests/test_sysconfig.py
@@ -1,16 +1,14 @@
"""Tests for distutils.sysconfig."""
import os
import shutil
-import test
import unittest
from distutils import sysconfig
from distutils.ccompiler import get_default_compiler
from distutils.tests import support
-from test.support import TESTFN, run_unittest
+from test.support import TESTFN, run_unittest, check_warnings
-class SysconfigTestCase(support.EnvironGuard,
- unittest.TestCase):
+class SysconfigTestCase(support.EnvironGuard, unittest.TestCase):
def setUp(self):
super(SysconfigTestCase, self).setUp()
self.makefile = None
@@ -32,7 +30,6 @@ class SysconfigTestCase(support.EnvironGuard,
self.assertTrue(os.path.isfile(config_h), config_h)
def test_get_python_lib(self):
- lib_dir = sysconfig.get_python_lib()
# XXX doesn't work on Linux when Python was never installed before
#self.assertTrue(os.path.isdir(lib_dir), lib_dir)
# test for pythonxx.lib?
@@ -67,8 +64,9 @@ class SysconfigTestCase(support.EnvironGuard,
self.assertTrue(os.path.exists(Python_h), Python_h)
self.assertTrue(sysconfig._is_python_source_dir(srcdir))
elif os.name == 'posix':
- self.assertEqual(os.path.dirname(sysconfig.get_makefile_filename()),
- srcdir)
+ self.assertEqual(
+ os.path.dirname(sysconfig.get_makefile_filename()),
+ srcdir)
def test_srcdir_independent_of_cwd(self):
# srcdir should be independent of the current working directory
@@ -126,10 +124,13 @@ class SysconfigTestCase(support.EnvironGuard,
def test_sysconfig_module(self):
import sysconfig as global_sysconfig
- self.assertEqual(global_sysconfig.get_config_var('CFLAGS'), sysconfig.get_config_var('CFLAGS'))
- self.assertEqual(global_sysconfig.get_config_var('LDFLAGS'), sysconfig.get_config_var('LDFLAGS'))
+ self.assertEqual(global_sysconfig.get_config_var('CFLAGS'),
+ sysconfig.get_config_var('CFLAGS'))
+ self.assertEqual(global_sysconfig.get_config_var('LDFLAGS'),
+ sysconfig.get_config_var('LDFLAGS'))
- @unittest.skipIf(sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'),'compiler flags customized')
+ @unittest.skipIf(sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'),
+ 'compiler flags customized')
def test_sysconfig_compiler_vars(self):
# On OS X, binary installers support extension module building on
# various levels of the operating system with differing Xcode
@@ -148,9 +149,30 @@ class SysconfigTestCase(support.EnvironGuard,
import sysconfig as global_sysconfig
if sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'):
self.skipTest('compiler flags customized')
- self.assertEqual(global_sysconfig.get_config_var('LDSHARED'), sysconfig.get_config_var('LDSHARED'))
- self.assertEqual(global_sysconfig.get_config_var('CC'), sysconfig.get_config_var('CC'))
-
+ self.assertEqual(global_sysconfig.get_config_var('LDSHARED'),
+ sysconfig.get_config_var('LDSHARED'))
+ self.assertEqual(global_sysconfig.get_config_var('CC'),
+ sysconfig.get_config_var('CC'))
+
+ @unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None,
+ 'EXT_SUFFIX required for this test')
+ def test_SO_deprecation(self):
+ self.assertWarns(DeprecationWarning,
+ sysconfig.get_config_var, 'SO')
+
+ @unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None,
+ 'EXT_SUFFIX required for this test')
+ def test_SO_value(self):
+ with check_warnings(('', DeprecationWarning)):
+ self.assertEqual(sysconfig.get_config_var('SO'),
+ sysconfig.get_config_var('EXT_SUFFIX'))
+
+ @unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None,
+ 'EXT_SUFFIX required for this test')
+ def test_SO_in_vars(self):
+ vars = sysconfig.get_config_vars()
+ self.assertIsNotNone(vars['SO'])
+ self.assertEqual(vars['SO'], vars['EXT_SUFFIX'])
def test_suite():
diff --git a/Lib/distutils/tests/test_upload.py b/Lib/distutils/tests/test_upload.py
index 8532369..f53eb26 100644
--- a/Lib/distutils/tests/test_upload.py
+++ b/Lib/distutils/tests/test_upload.py
@@ -124,11 +124,12 @@ class uploadTestCase(PyPIRCCommandTestCase):
# what did we send ?
headers = dict(self.last_open.req.headers)
self.assertEqual(headers['Content-length'], '2087')
- self.assertTrue(headers['Content-type'].startswith('multipart/form-data'))
+ content_type = headers['Content-type']
+ self.assertTrue(content_type.startswith('multipart/form-data'))
self.assertEqual(self.last_open.req.get_method(), 'POST')
- self.assertEqual(self.last_open.req.get_full_url(),
- 'https://pypi.python.org/pypi')
- self.assertIn(b'xxx', self.last_open.req.data)
+ expected_url = 'https://pypi.python.org/pypi'
+ self.assertEqual(self.last_open.req.get_full_url(), expected_url)
+ self.assertTrue(b'xxx' in self.last_open.req.data)
# The PyPI response body was echoed
results = self.get_logs(INFO)
diff --git a/Lib/distutils/tests/test_util.py b/Lib/distutils/tests/test_util.py
index a1abf8f..4e9d79b 100644
--- a/Lib/distutils/tests/test_util.py
+++ b/Lib/distutils/tests/test_util.py
@@ -237,7 +237,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase):
self.assertRaises(DistutilsPlatformError,
change_root, 'c:\\root', 'its\\here')
- # XXX platforms to be covered: os2, mac
+ # XXX platforms to be covered: mac
def test_check_environ(self):
util._environ_checked = 0
diff --git a/Lib/distutils/util.py b/Lib/distutils/util.py
index b558957..5adcac5 100644
--- a/Lib/distutils/util.py
+++ b/Lib/distutils/util.py
@@ -6,7 +6,7 @@ one of the other *util.py modules.
import os
import re
-import imp
+import importlib.util
import sys
import string
from distutils.errors import DistutilsPlatformError
@@ -154,12 +154,6 @@ def change_root (new_root, pathname):
path = path[1:]
return os.path.join(new_root, path)
- elif os.name == 'os2':
- (drive, path) = os.path.splitdrive(pathname)
- if path[0] == os.sep:
- path = path[1:]
- return os.path.join(new_root, path)
-
else:
raise DistutilsPlatformError("nothing known about platform '%s'" % os.name)
@@ -444,9 +438,10 @@ byte_compile(files, optimize=%r, force=%r,
# cfile - byte-compiled file
# dfile - purported source filename (same as 'file' by default)
if optimize >= 0:
- cfile = imp.cache_from_source(file, debug_override=not optimize)
+ cfile = importlib.util.cache_from_source(
+ file, debug_override=not optimize)
else:
- cfile = imp.cache_from_source(file)
+ cfile = importlib.util.cache_from_source(file)
dfile = file
if prefix:
if file[:len(prefix)] != prefix:
diff --git a/Lib/doctest.py b/Lib/doctest.py
index 3f0d9d9..d212ad6 100644
--- a/Lib/doctest.py
+++ b/Lib/doctest.py
@@ -62,6 +62,7 @@ __all__ = [
'REPORT_NDIFF',
'REPORT_ONLY_FIRST_FAILURE',
'REPORTING_FLAGS',
+ 'FAIL_FAST',
# 1. Utility Functions
# 2. Example & DocTest
'Example',
@@ -92,6 +93,7 @@ __all__ = [
]
import __future__
+import argparse
import difflib
import inspect
import linecache
@@ -150,11 +152,13 @@ REPORT_UDIFF = register_optionflag('REPORT_UDIFF')
REPORT_CDIFF = register_optionflag('REPORT_CDIFF')
REPORT_NDIFF = register_optionflag('REPORT_NDIFF')
REPORT_ONLY_FIRST_FAILURE = register_optionflag('REPORT_ONLY_FIRST_FAILURE')
+FAIL_FAST = register_optionflag('FAIL_FAST')
REPORTING_FLAGS = (REPORT_UDIFF |
REPORT_CDIFF |
REPORT_NDIFF |
- REPORT_ONLY_FIRST_FAILURE)
+ REPORT_ONLY_FIRST_FAILURE |
+ FAIL_FAST)
# Special string markers for use in `want` strings:
BLANKLINE_MARKER = '<BLANKLINE>'
@@ -212,7 +216,7 @@ def _load_testfile(filename, package, module_relative, encoding):
if module_relative:
package = _normalize_module(package, 3)
filename = _module_relative_path(package, filename)
- if hasattr(package, '__loader__'):
+ if getattr(package, '__loader__', None) is not None:
if hasattr(package.__loader__, 'get_data'):
file_contents = package.__loader__.get_data(filename)
file_contents = file_contents.decode(encoding)
@@ -940,6 +944,14 @@ class DocTestFinder:
return module is inspect.getmodule(object)
elif inspect.isfunction(object):
return module.__dict__ is object.__globals__
+ elif inspect.ismethoddescriptor(object):
+ if hasattr(object, '__objclass__'):
+ obj_mod = object.__objclass__.__module__
+ elif hasattr(object, '__module__'):
+ obj_mod = object.__module__
+ else:
+ return True # [XX] no easy way to tell otherwise
+ return module.__name__ == obj_mod
elif inspect.isclass(object):
return module.__name__ == object.__module__
elif hasattr(object, '__module__'):
@@ -972,7 +984,7 @@ class DocTestFinder:
for valname, val in obj.__dict__.items():
valname = '%s.%s' % (name, valname)
# Recurse to functions & classes.
- if ((inspect.isfunction(val) or inspect.isclass(val)) and
+ if ((inspect.isroutine(val) or inspect.isclass(val)) and
self._from_module(module, val)):
self._find(tests, val, valname, module, source_lines,
globs, seen)
@@ -984,9 +996,8 @@ class DocTestFinder:
raise ValueError("DocTestFinder.find: __test__ keys "
"must be strings: %r" %
(type(valname),))
- if not (inspect.isfunction(val) or inspect.isclass(val) or
- inspect.ismethod(val) or inspect.ismodule(val) or
- isinstance(val, str)):
+ if not (inspect.isroutine(val) or inspect.isclass(val) or
+ inspect.ismodule(val) or isinstance(val, str)):
raise ValueError("DocTestFinder.find: __test__ values "
"must be strings, functions, methods, "
"classes, or modules: %r" %
@@ -1005,7 +1016,7 @@ class DocTestFinder:
val = getattr(obj, valname).__func__
# Recurse to methods, properties, and nested classes.
- if ((inspect.isfunction(val) or inspect.isclass(val) or
+ if ((inspect.isroutine(val) or inspect.isclass(val) or
isinstance(val, property)) and
self._from_module(module, val)):
valname = '%s.%s' % (name, valname)
@@ -1367,6 +1378,9 @@ class DocTestRunner:
else:
assert False, ("unknown outcome", outcome)
+ if failures and self.optionflags & FAIL_FAST:
+ break
+
# Restore the option flags (in case they were modified)
self.optionflags = original_optionflags
@@ -2308,6 +2322,12 @@ class SkipDocTestCase(DocTestCase):
__str__ = shortDescription
+class _DocTestSuite(unittest.TestSuite):
+
+ def _removeTestAtIndex(self, index):
+ pass
+
+
def DocTestSuite(module=None, globs=None, extraglobs=None, test_finder=None,
**options):
"""
@@ -2353,7 +2373,7 @@ def DocTestSuite(module=None, globs=None, extraglobs=None, test_finder=None,
if not tests and sys.flags.optimize >=2:
# Skip doctests when running with -O2
- suite = unittest.TestSuite()
+ suite = _DocTestSuite()
suite.addTest(SkipDocTestCase(module))
return suite
elif not tests:
@@ -2367,7 +2387,7 @@ def DocTestSuite(module=None, globs=None, extraglobs=None, test_finder=None,
raise ValueError(module, "has no docstrings")
tests.sort()
- suite = unittest.TestSuite()
+ suite = _DocTestSuite()
for test in tests:
if len(test.examples) == 0:
@@ -2477,7 +2497,7 @@ def DocFileSuite(*paths, **kw):
encoding
An encoding that will be used to convert the files to unicode.
"""
- suite = unittest.TestSuite()
+ suite = _DocTestSuite()
# We do this here so that _normalize_module is called at the right
# level. If it were called in DocFileTest, then this function
@@ -2727,13 +2747,30 @@ __test__ = {"_TestClass": _TestClass,
def _test():
- testfiles = [arg for arg in sys.argv[1:] if arg and arg[0] != '-']
- if not testfiles:
- name = os.path.basename(sys.argv[0])
- if '__loader__' in globals(): # python -m
- name, _ = os.path.splitext(name)
- print("usage: {0} [-v] file ...".format(name))
- return 2
+ parser = argparse.ArgumentParser(description="doctest runner")
+ parser.add_argument('-v', '--verbose', action='store_true', default=False,
+ help='print very verbose output for all tests')
+ parser.add_argument('-o', '--option', action='append',
+ choices=OPTIONFLAGS_BY_NAME.keys(), default=[],
+ help=('specify a doctest option flag to apply'
+ ' to the test run; may be specified more'
+ ' than once to apply multiple options'))
+ parser.add_argument('-f', '--fail-fast', action='store_true',
+ help=('stop running tests after first failure (this'
+ ' is a shorthand for -o FAIL_FAST, and is'
+ ' in addition to any other -o options)'))
+ parser.add_argument('file', nargs='+',
+ help='file containing the tests to run')
+ args = parser.parse_args()
+ testfiles = args.file
+ # Verbose used to be handled by the "inspect argv" magic in DocTestRunner,
+ # but since we are using argparse we are passing it manually now.
+ verbose = args.verbose
+ options = 0
+ for option in args.option:
+ options |= OPTIONFLAGS_BY_NAME[option]
+ if args.fail_fast:
+ options |= FAIL_FAST
for filename in testfiles:
if filename.endswith(".py"):
# It is a module -- insert its dir into sys.path and try to
@@ -2743,9 +2780,10 @@ def _test():
sys.path.insert(0, dirname)
m = __import__(filename[:-3])
del sys.path[0]
- failures, _ = testmod(m)
+ failures, _ = testmod(m, verbose=verbose, optionflags=options)
else:
- failures, _ = testfile(filename, module_relative=False)
+ failures, _ = testfile(filename, module_relative=False,
+ verbose=verbose, optionflags=options)
if failures:
return 1
return 0
diff --git a/Lib/email/_header_value_parser.py b/Lib/email/_header_value_parser.py
index 0369e01..3dc5502 100644
--- a/Lib/email/_header_value_parser.py
+++ b/Lib/email/_header_value_parser.py
@@ -70,7 +70,7 @@ XXX: provide complete list of token types.
import re
import urllib # For urllib.parse.unquote
from string import hexdigits
-from collections import namedtuple, OrderedDict
+from collections import OrderedDict
from email import _encoded_words as _ew
from email import errors
from email import utils
@@ -368,8 +368,7 @@ class TokenList(list):
yield (indent + ' !! invalid element in token '
'list: {!r}'.format(token))
else:
- for line in token._pp(indent+' '):
- yield line
+ yield from token._pp(indent+' ')
if self.defects:
extra = ' Defects: {}'.format(self.defects)
else:
@@ -1315,24 +1314,22 @@ RouteComponentMarker = ValueTerminal('@', 'route-component-marker')
# Parser
#
-"""Parse strings according to RFC822/2047/2822/5322 rules.
-
-This is a stateless parser. Each get_XXX function accepts a string and
-returns either a Terminal or a TokenList representing the RFC object named
-by the method and a string containing the remaining unparsed characters
-from the input. Thus a parser method consumes the next syntactic construct
-of a given type and returns a token representing the construct plus the
-unparsed remainder of the input string.
-
-For example, if the first element of a structured header is a 'phrase',
-then:
-
- phrase, value = get_phrase(value)
-
-returns the complete phrase from the start of the string value, plus any
-characters left in the string after the phrase is removed.
-
-"""
+# Parse strings according to RFC822/2047/2822/5322 rules.
+#
+# This is a stateless parser. Each get_XXX function accepts a string and
+# returns either a Terminal or a TokenList representing the RFC object named
+# by the method and a string containing the remaining unparsed characters
+# from the input. Thus a parser method consumes the next syntactic construct
+# of a given type and returns a token representing the construct plus the
+# unparsed remainder of the input string.
+#
+# For example, if the first element of a structured header is a 'phrase',
+# then:
+#
+# phrase, value = get_phrase(value)
+#
+# returns the complete phrase from the start of the string value, plus any
+# characters left in the string after the phrase is removed.
_wsp_splitter = re.compile(r'([{}]+)'.format(''.join(WSP))).split
_non_atom_end_matcher = re.compile(r"[^{}]+".format(
diff --git a/Lib/email/contentmanager.py b/Lib/email/contentmanager.py
new file mode 100644
index 0000000..d363652
--- /dev/null
+++ b/Lib/email/contentmanager.py
@@ -0,0 +1,249 @@
+import binascii
+import email.charset
+import email.message
+import email.errors
+from email import quoprimime
+
+class ContentManager:
+
+ def __init__(self):
+ self.get_handlers = {}
+ self.set_handlers = {}
+
+ def add_get_handler(self, key, handler):
+ self.get_handlers[key] = handler
+
+ def get_content(self, msg, *args, **kw):
+ content_type = msg.get_content_type()
+ if content_type in self.get_handlers:
+ return self.get_handlers[content_type](msg, *args, **kw)
+ maintype = msg.get_content_maintype()
+ if maintype in self.get_handlers:
+ return self.get_handlers[maintype](msg, *args, **kw)
+ if '' in self.get_handlers:
+ return self.get_handlers[''](msg, *args, **kw)
+ raise KeyError(content_type)
+
+ def add_set_handler(self, typekey, handler):
+ self.set_handlers[typekey] = handler
+
+ def set_content(self, msg, obj, *args, **kw):
+ if msg.get_content_maintype() == 'multipart':
+ # XXX: is this error a good idea or not? We can remove it later,
+ # but we can't add it later, so do it for now.
+ raise TypeError("set_content not valid on multipart")
+ handler = self._find_set_handler(msg, obj)
+ msg.clear_content()
+ handler(msg, obj, *args, **kw)
+
+ def _find_set_handler(self, msg, obj):
+ full_path_for_error = None
+ for typ in type(obj).__mro__:
+ if typ in self.set_handlers:
+ return self.set_handlers[typ]
+ qname = typ.__qualname__
+ modname = getattr(typ, '__module__', '')
+ full_path = '.'.join((modname, qname)) if modname else qname
+ if full_path_for_error is None:
+ full_path_for_error = full_path
+ if full_path in self.set_handlers:
+ return self.set_handlers[full_path]
+ if qname in self.set_handlers:
+ return self.set_handlers[qname]
+ name = typ.__name__
+ if name in self.set_handlers:
+ return self.set_handlers[name]
+ if None in self.set_handlers:
+ return self.set_handlers[None]
+ raise KeyError(full_path_for_error)
+
+
+raw_data_manager = ContentManager()
+
+
+def get_text_content(msg, errors='replace'):
+ content = msg.get_payload(decode=True)
+ charset = msg.get_param('charset', 'ASCII')
+ return content.decode(charset, errors=errors)
+raw_data_manager.add_get_handler('text', get_text_content)
+
+
+def get_non_text_content(msg):
+ return msg.get_payload(decode=True)
+for maintype in 'audio image video application'.split():
+ raw_data_manager.add_get_handler(maintype, get_non_text_content)
+
+
+def get_message_content(msg):
+ return msg.get_payload(0)
+for subtype in 'rfc822 external-body'.split():
+ raw_data_manager.add_get_handler('message/'+subtype, get_message_content)
+
+
+def get_and_fixup_unknown_message_content(msg):
+ # If we don't understand a message subtype, we are supposed to treat it as
+ # if it were application/octet-stream, per
+ # tools.ietf.org/html/rfc2046#section-5.2.4. Feedparser doesn't do that,
+ # so do our best to fix things up. Note that it is *not* appropriate to
+ # model message/partial content as Message objects, so they are handled
+ # here as well. (How to reassemble them is out of scope for this comment :)
+ return bytes(msg.get_payload(0))
+raw_data_manager.add_get_handler('message',
+ get_and_fixup_unknown_message_content)
+
+
+def _prepare_set(msg, maintype, subtype, headers):
+ msg['Content-Type'] = '/'.join((maintype, subtype))
+ if headers:
+ if not hasattr(headers[0], 'name'):
+ mp = msg.policy
+ headers = [mp.header_factory(*mp.header_source_parse([header]))
+ for header in headers]
+ try:
+ for header in headers:
+ if header.defects:
+ raise header.defects[0]
+ msg[header.name] = header
+ except email.errors.HeaderDefect as exc:
+ raise ValueError("Invalid header: {}".format(
+ header.fold(policy=msg.policy))) from exc
+
+
+def _finalize_set(msg, disposition, filename, cid, params):
+ if disposition is None and filename is not None:
+ disposition = 'attachment'
+ if disposition is not None:
+ msg['Content-Disposition'] = disposition
+ if filename is not None:
+ msg.set_param('filename',
+ filename,
+ header='Content-Disposition',
+ replace=True)
+ if cid is not None:
+ msg['Content-ID'] = cid
+ if params is not None:
+ for key, value in params.items():
+ msg.set_param(key, value)
+
+
+# XXX: This is a cleaned-up version of base64mime.body_encode. It would
+# be nice to drop both this and quoprimime.body_encode in favor of
+# enhanced binascii routines that accepted a max_line_length parameter.
+def _encode_base64(data, max_line_length):
+ encoded_lines = []
+ unencoded_bytes_per_line = max_line_length * 3 // 4
+ for i in range(0, len(data), unencoded_bytes_per_line):
+ thisline = data[i:i+unencoded_bytes_per_line]
+ encoded_lines.append(binascii.b2a_base64(thisline).decode('ascii'))
+ return ''.join(encoded_lines)
+
+
+def _encode_text(string, charset, cte, policy):
+ lines = string.encode(charset).splitlines()
+ linesep = policy.linesep.encode('ascii')
+ def embeded_body(lines): return linesep.join(lines) + linesep
+ def normal_body(lines): return b'\n'.join(lines) + b'\n'
+ if cte==None:
+ # Use heuristics to decide on the "best" encoding.
+ try:
+ return '7bit', normal_body(lines).decode('ascii')
+ except UnicodeDecodeError:
+ pass
+ if (policy.cte_type == '8bit' and
+ max(len(x) for x in lines) <= policy.max_line_length):
+ return '8bit', normal_body(lines).decode('ascii', 'surrogateescape')
+ sniff = embeded_body(lines[:10])
+ sniff_qp = quoprimime.body_encode(sniff.decode('latin-1'),
+ policy.max_line_length)
+ sniff_base64 = binascii.b2a_base64(sniff)
+ # This is a little unfair to qp; it includes lineseps, base64 doesn't.
+ if len(sniff_qp) > len(sniff_base64):
+ cte = 'base64'
+ else:
+ cte = 'quoted-printable'
+ if len(lines) <= 10:
+ return cte, sniff_qp
+ if cte == '7bit':
+ data = normal_body(lines).decode('ascii')
+ elif cte == '8bit':
+ data = normal_body(lines).decode('ascii', 'surrogateescape')
+ elif cte == 'quoted-printable':
+ data = quoprimime.body_encode(normal_body(lines).decode('latin-1'),
+ policy.max_line_length)
+ elif cte == 'base64':
+ data = _encode_base64(embeded_body(lines), policy.max_line_length)
+ else:
+ raise ValueError("Unknown content transfer encoding {}".format(cte))
+ return cte, data
+
+
+def set_text_content(msg, string, subtype="plain", charset='utf-8', cte=None,
+ disposition=None, filename=None, cid=None,
+ params=None, headers=None):
+ _prepare_set(msg, 'text', subtype, headers)
+ cte, payload = _encode_text(string, charset, cte, msg.policy)
+ msg.set_payload(payload)
+ msg.set_param('charset',
+ email.charset.ALIASES.get(charset, charset),
+ replace=True)
+ msg['Content-Transfer-Encoding'] = cte
+ _finalize_set(msg, disposition, filename, cid, params)
+raw_data_manager.add_set_handler(str, set_text_content)
+
+
+def set_message_content(msg, message, subtype="rfc822", cte=None,
+ disposition=None, filename=None, cid=None,
+ params=None, headers=None):
+ if subtype == 'partial':
+ raise ValueError("message/partial is not supported for Message objects")
+ if subtype == 'rfc822':
+ if cte not in (None, '7bit', '8bit', 'binary'):
+ # http://tools.ietf.org/html/rfc2046#section-5.2.1 mandate.
+ raise ValueError(
+ "message/rfc822 parts do not support cte={}".format(cte))
+ # 8bit will get coerced on serialization if policy.cte_type='7bit'. We
+ # may end up claiming 8bit when it isn't needed, but the only negative
+ # result of that should be a gateway that needs to coerce to 7bit
+ # having to look through the whole embedded message to discover whether
+ # or not it actually has to do anything.
+ cte = '8bit' if cte is None else cte
+ elif subtype == 'external-body':
+ if cte not in (None, '7bit'):
+ # http://tools.ietf.org/html/rfc2046#section-5.2.3 mandate.
+ raise ValueError(
+ "message/external-body parts do not support cte={}".format(cte))
+ cte = '7bit'
+ elif cte is None:
+ # http://tools.ietf.org/html/rfc2046#section-5.2.4 says all future
+ # subtypes should be restricted to 7bit, so assume that.
+ cte = '7bit'
+ _prepare_set(msg, 'message', subtype, headers)
+ msg.set_payload([message])
+ msg['Content-Transfer-Encoding'] = cte
+ _finalize_set(msg, disposition, filename, cid, params)
+raw_data_manager.add_set_handler(email.message.Message, set_message_content)
+
+
+def set_bytes_content(msg, data, maintype, subtype, cte='base64',
+ disposition=None, filename=None, cid=None,
+ params=None, headers=None):
+ _prepare_set(msg, maintype, subtype, headers)
+ if cte == 'base64':
+ data = _encode_base64(data, max_line_length=msg.policy.max_line_length)
+ elif cte == 'quoted-printable':
+ # XXX: quoprimime.body_encode won't encode newline characters in data,
+ # so we can't use it. This means max_line_length is ignored. Another
+ # bug to fix later. (Note: encoders.quopri is broken on line ends.)
+ data = binascii.b2a_qp(data, istext=False, header=False, quotetabs=True)
+ data = data.decode('ascii')
+ elif cte == '7bit':
+ # Make sure it really is only ASCII. The early warning here seems
+ # worth the overhead...if you care write your own content manager :).
+ data.encode('ascii')
+ elif cte in ('8bit', 'binary'):
+ data = data.decode('ascii', 'surrogateescape')
+ msg.set_payload(data)
+ msg['Content-Transfer-Encoding'] = cte
+ _finalize_set(msg, disposition, filename, cid, params)
+for typ in (bytes, bytearray, memoryview):
+ raw_data_manager.add_set_handler(typ, set_bytes_content)
diff --git a/Lib/email/encoders.py b/Lib/email/encoders.py
index f9657f0..0a66acb 100644
--- a/Lib/email/encoders.py
+++ b/Lib/email/encoders.py
@@ -54,21 +54,12 @@ def encode_7or8bit(msg):
# There's no payload. For backwards compatibility we use 7bit
msg['Content-Transfer-Encoding'] = '7bit'
return
- # We play a trick to make this go fast. If encoding/decode to ASCII
- # succeeds, we know the data must be 7bit, otherwise treat it as 8bit.
+ # We play a trick to make this go fast. If decoding from ASCII succeeds,
+ # we know the data must be 7bit, otherwise treat it as 8bit.
try:
- if isinstance(orig, str):
- orig.encode('ascii')
- else:
- orig.decode('ascii')
+ orig.decode('ascii')
except UnicodeError:
- charset = msg.get_charset()
- output_cset = charset and charset.output_charset
- # iso-2022-* is non-ASCII but encodes to a 7-bit representation
- if output_cset and output_cset.lower().startswith('iso-2022-'):
- msg['Content-Transfer-Encoding'] = '7bit'
- else:
- msg['Content-Transfer-Encoding'] = '8bit'
+ msg['Content-Transfer-Encoding'] = '8bit'
else:
msg['Content-Transfer-Encoding'] = '7bit'
diff --git a/Lib/email/feedparser.py b/Lib/email/feedparser.py
index ea41e95..6cf9b91 100644
--- a/Lib/email/feedparser.py
+++ b/Lib/email/feedparser.py
@@ -98,24 +98,15 @@ class BufferedSubFile(object):
"""Push some new data into this object."""
# Handle any previous leftovers
data, self._partial = self._partial + data, ''
- # Crack into lines, but preserve the newlines on the end of each
- parts = NLCRE_crack.split(data)
- # The *ahem* interesting behaviour of re.split when supplied grouping
- # parentheses is that the last element of the resulting list is the
- # data after the final RE. In the case of a NL/CR terminated string,
- # this is the empty string.
- self._partial = parts.pop()
- #GAN 29Mar09 bugs 1555570, 1721862 Confusion at 8K boundary ending with \r:
- # is there a \n to follow later?
- if not self._partial and parts and parts[-1].endswith('\r'):
- self._partial = parts.pop(-2)+parts.pop()
- # parts is a list of strings, alternating between the line contents
- # and the eol character(s). Gather up a list of lines after
- # re-attaching the newlines.
- lines = []
- for i in range(len(parts) // 2):
- lines.append(parts[i*2] + parts[i*2+1])
- self.pushlines(lines)
+ # Crack into lines, but preserve the linesep characters on the end of each
+ parts = data.splitlines(True)
+ # If the last element of the list does not end in a newline, then treat
+ # it as a partial line. We only check for '\n' here because a line
+ # ending with '\r' might be a line that was split in the middle of a
+ # '\r\n' sequence (see bugs 1555570 and 1721862).
+ if parts and not parts[-1].endswith('\n'):
+ self._partial = parts.pop()
+ self.pushlines(parts)
def pushlines(self, lines):
# Reverse and insert at the front of the lines.
@@ -135,7 +126,7 @@ class BufferedSubFile(object):
class FeedParser:
"""A feed-style parser of email."""
- def __init__(self, _factory=message.Message, *, policy=compat32):
+ def __init__(self, _factory=None, *, policy=compat32):
"""_factory is called with no arguments to create a new message obj
The policy keyword specifies a policy object that controls a number of
@@ -143,14 +134,23 @@ class FeedParser:
backward compatibility.
"""
- self._factory = _factory
self.policy = policy
- try:
- _factory(policy=self.policy)
- self._factory_kwds = lambda: {'policy': self.policy}
- except TypeError:
- # Assume this is an old-style factory
- self._factory_kwds = lambda: {}
+ self._factory_kwds = lambda: {'policy': self.policy}
+ if _factory is None:
+ # What this should be:
+ #self._factory = policy.default_message_factory
+ # but, because we are post 3.4 feature freeze, fix with temp hack:
+ if self.policy is compat32:
+ self._factory = message.Message
+ else:
+ self._factory = message.EmailMessage
+ else:
+ self._factory = _factory
+ try:
+ _factory(policy=self.policy)
+ except TypeError:
+ # Assume this is an old-style factory
+ self._factory_kwds = lambda: {}
self._input = BufferedSubFile()
self._msgstack = []
self._parse = self._parsegen().__next__
diff --git a/Lib/email/generator.py b/Lib/email/generator.py
index e4a86d4..4735721 100644
--- a/Lib/email/generator.py
+++ b/Lib/email/generator.py
@@ -10,14 +10,10 @@ import re
import sys
import time
import random
-import warnings
from copy import deepcopy
from io import StringIO, BytesIO
-from email._policybase import compat32
-from email.header import Header
from email.utils import _has_surrogates
-import email.charset as _charset
UNDERSCORE = '_'
NL = '\n' # XXX: no longer used by the code below.
@@ -55,8 +51,9 @@ class Generator:
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.
+ aspects of the generator's operation. If no policy is specified,
+ the policy associated with the Message object passed to the
+ flatten method is used.
"""
self._fp = outfp
@@ -80,7 +77,9 @@ 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 determined by the policy.
+ the output. The default value is determined by the policy specified
+ when the Generator instance was created or, if none was specified,
+ from the policy associated with the msg.
"""
# We use the _XXX constants for operating on data that comes directly
diff --git a/Lib/email/header.py b/Lib/email/header.py
index 5bd0638..9c89589 100644
--- a/Lib/email/header.py
+++ b/Lib/email/header.py
@@ -100,7 +100,6 @@ def decode_header(header):
words.append((encoded, encoding, charset))
# Now loop over words and remove words that consist of whitespace
# between two encoded strings.
- import sys
droplist = []
for n, w in enumerate(words):
if n>1 and w[1] and words[n-2][1] and words[n-1][0].isspace():
@@ -362,7 +361,6 @@ class Header:
for string, charset in self._chunks:
if hasspace is not None:
hasspace = string and self._nonctext(string[0])
- import sys
if lastcs not in (None, 'us-ascii'):
if not hasspace or charset not in (None, 'us-ascii'):
formatter.add_transition()
diff --git a/Lib/email/iterators.py b/Lib/email/iterators.py
index 3adc4a0..b5502ee 100644
--- a/Lib/email/iterators.py
+++ b/Lib/email/iterators.py
@@ -26,8 +26,7 @@ def walk(self):
yield self
if self.is_multipart():
for subpart in self.get_payload():
- for subsubpart in subpart.walk():
- yield subsubpart
+ yield from subpart.walk()
@@ -40,8 +39,7 @@ def body_line_iterator(msg, decode=False):
for subpart in msg.walk():
payload = subpart.get_payload(decode=decode)
if isinstance(payload, str):
- for line in StringIO(payload):
- yield line
+ yield from StringIO(payload)
def typed_subpart_iterator(msg, maintype='text', subtype=None):
diff --git a/Lib/email/message.py b/Lib/email/message.py
index afe350c..aa46deb 100644
--- a/Lib/email/message.py
+++ b/Lib/email/message.py
@@ -8,8 +8,7 @@ __all__ = ['Message']
import re
import uu
-import base64
-import binascii
+import quopri
from io import BytesIO, StringIO
# Intrapackage imports
@@ -132,22 +131,50 @@ class Message:
def __str__(self):
"""Return the entire formatted message as a string.
- This includes the headers, body, and envelope header.
"""
return self.as_string()
- def as_string(self, unixfrom=False, maxheaderlen=0):
+ def as_string(self, unixfrom=False, maxheaderlen=0, policy=None):
"""Return the entire formatted message as a string.
- Optional `unixfrom' when True, means include the Unix From_ envelope
- header.
- This is a convenience method and may not generate the message exactly
- as you intend. For more flexibility, use the flatten() method of a
- Generator instance.
+ Optional 'unixfrom', when true, means include the Unix From_ envelope
+ header. For backward compatibility reasons, if maxheaderlen is
+ not specified it defaults to 0, so you must override it explicitly
+ if you want a different maxheaderlen. 'policy' is passed to the
+ Generator instance used to serialize the mesasge; if it is not
+ specified the policy associated with the message instance is used.
+
+ If the message object contains binary data that is not encoded
+ according to RFC standards, the non-compliant data will be replaced by
+ unicode "unknown character" code points.
"""
from email.generator import Generator
+ policy = self.policy if policy is None else policy
fp = StringIO()
- g = Generator(fp, mangle_from_=False, maxheaderlen=maxheaderlen)
+ g = Generator(fp,
+ mangle_from_=False,
+ maxheaderlen=maxheaderlen,
+ policy=policy)
+ g.flatten(self, unixfrom=unixfrom)
+ return fp.getvalue()
+
+ def __bytes__(self):
+ """Return the entire formatted message as a bytes object.
+ """
+ return self.as_bytes()
+
+ def as_bytes(self, unixfrom=False, policy=None):
+ """Return the entire formatted message as a bytes object.
+
+ Optional 'unixfrom', when true, means include the Unix From_ envelope
+ header. 'policy' is passed to the BytesGenerator instance used to
+ serialize the message; if not specified the policy associated with
+ the message instance is used.
+ """
+ from email.generator import BytesGenerator
+ policy = self.policy if policy is None else policy
+ fp = BytesIO()
+ g = BytesGenerator(fp, mangle_from_=False, policy=policy)
g.flatten(self, unixfrom=unixfrom)
return fp.getvalue()
@@ -177,7 +204,11 @@ class Message:
if self._payload is None:
self._payload = [payload]
else:
- self._payload.append(payload)
+ try:
+ self._payload.append(payload)
+ except AttributeError:
+ raise TypeError("Attach is not valid on a message with a"
+ " non-multipart payload")
def get_payload(self, i=None, decode=False):
"""Return a reference to the payload.
@@ -248,7 +279,7 @@ class Message:
if not decode:
return payload
if cte == 'quoted-printable':
- return utils._qdecode(bpayload)
+ return quopri.decodestring(bpayload)
elif cte == 'base64':
# XXX: this is a bit of a hack; decode_b should probably be factored
# out somewhere, but I haven't figured out where yet.
@@ -668,7 +699,7 @@ class Message:
return failobj
def set_param(self, param, value, header='Content-Type', requote=True,
- charset=None, language=''):
+ charset=None, language='', replace=False):
"""Set a parameter in the Content-Type header.
If the parameter already exists in the header, its value will be
@@ -712,8 +743,11 @@ class Message:
else:
ctype = SEMISPACE.join([ctype, append_param])
if ctype != self.get(header):
- del self[header]
- self[header] = ctype
+ if replace:
+ self.replace_header(header, ctype)
+ else:
+ del self[header]
+ self[header] = ctype
def del_param(self, param, header='content-type', requote=True):
"""Remove the given parameter completely from the Content-Type header.
@@ -894,3 +928,208 @@ class Message:
# I.e. def walk(self): ...
from email.iterators import walk
+
+
+class MIMEPart(Message):
+
+ def __init__(self, policy=None):
+ if policy is None:
+ from email.policy import default
+ policy = default
+ Message.__init__(self, policy)
+
+ @property
+ def is_attachment(self):
+ c_d = self.get('content-disposition')
+ if c_d is None:
+ return False
+ return c_d.lower() == 'attachment'
+
+ def _find_body(self, part, preferencelist):
+ if part.is_attachment:
+ return
+ maintype, subtype = part.get_content_type().split('/')
+ if maintype == 'text':
+ if subtype in preferencelist:
+ yield (preferencelist.index(subtype), part)
+ return
+ if maintype != 'multipart':
+ return
+ if subtype != 'related':
+ for subpart in part.iter_parts():
+ yield from self._find_body(subpart, preferencelist)
+ return
+ if 'related' in preferencelist:
+ yield (preferencelist.index('related'), part)
+ candidate = None
+ start = part.get_param('start')
+ if start:
+ for subpart in part.iter_parts():
+ if subpart['content-id'] == start:
+ candidate = subpart
+ break
+ if candidate is None:
+ subparts = part.get_payload()
+ candidate = subparts[0] if subparts else None
+ if candidate is not None:
+ yield from self._find_body(candidate, preferencelist)
+
+ def get_body(self, preferencelist=('related', 'html', 'plain')):
+ """Return best candidate mime part for display as 'body' of message.
+
+ Do a depth first search, starting with self, looking for the first part
+ matching each of the items in preferencelist, and return the part
+ corresponding to the first item that has a match, or None if no items
+ have a match. If 'related' is not included in preferencelist, consider
+ the root part of any multipart/related encountered as a candidate
+ match. Ignore parts with 'Content-Disposition: attachment'.
+ """
+ best_prio = len(preferencelist)
+ body = None
+ for prio, part in self._find_body(self, preferencelist):
+ if prio < best_prio:
+ best_prio = prio
+ body = part
+ if prio == 0:
+ break
+ return body
+
+ _body_types = {('text', 'plain'),
+ ('text', 'html'),
+ ('multipart', 'related'),
+ ('multipart', 'alternative')}
+ def iter_attachments(self):
+ """Return an iterator over the non-main parts of a multipart.
+
+ Skip the first of each occurrence of text/plain, text/html,
+ multipart/related, or multipart/alternative in the multipart (unless
+ they have a 'Content-Disposition: attachment' header) and include all
+ remaining subparts in the returned iterator. When applied to a
+ multipart/related, return all parts except the root part. Return an
+ empty iterator when applied to a multipart/alternative or a
+ non-multipart.
+ """
+ maintype, subtype = self.get_content_type().split('/')
+ if maintype != 'multipart' or subtype == 'alternative':
+ return
+ parts = self.get_payload()
+ if maintype == 'multipart' and subtype == 'related':
+ # For related, we treat everything but the root as an attachment.
+ # The root may be indicated by 'start'; if there's no start or we
+ # can't find the named start, treat the first subpart as the root.
+ start = self.get_param('start')
+ if start:
+ found = False
+ attachments = []
+ for part in parts:
+ if part.get('content-id') == start:
+ found = True
+ else:
+ attachments.append(part)
+ if found:
+ yield from attachments
+ return
+ parts.pop(0)
+ yield from parts
+ return
+ # Otherwise we more or less invert the remaining logic in get_body.
+ # This only really works in edge cases (ex: non-text relateds or
+ # alternatives) if the sending agent sets content-disposition.
+ seen = [] # Only skip the first example of each candidate type.
+ for part in parts:
+ maintype, subtype = part.get_content_type().split('/')
+ if ((maintype, subtype) in self._body_types and
+ not part.is_attachment and subtype not in seen):
+ seen.append(subtype)
+ continue
+ yield part
+
+ def iter_parts(self):
+ """Return an iterator over all immediate subparts of a multipart.
+
+ Return an empty iterator for a non-multipart.
+ """
+ if self.get_content_maintype() == 'multipart':
+ yield from self.get_payload()
+
+ def get_content(self, *args, content_manager=None, **kw):
+ if content_manager is None:
+ content_manager = self.policy.content_manager
+ return content_manager.get_content(self, *args, **kw)
+
+ def set_content(self, *args, content_manager=None, **kw):
+ if content_manager is None:
+ content_manager = self.policy.content_manager
+ content_manager.set_content(self, *args, **kw)
+
+ def _make_multipart(self, subtype, disallowed_subtypes, boundary):
+ if self.get_content_maintype() == 'multipart':
+ existing_subtype = self.get_content_subtype()
+ disallowed_subtypes = disallowed_subtypes + (subtype,)
+ if existing_subtype in disallowed_subtypes:
+ raise ValueError("Cannot convert {} to {}".format(
+ existing_subtype, subtype))
+ keep_headers = []
+ part_headers = []
+ for name, value in self._headers:
+ if name.lower().startswith('content-'):
+ part_headers.append((name, value))
+ else:
+ keep_headers.append((name, value))
+ if part_headers:
+ # There is existing content, move it to the first subpart.
+ part = type(self)(policy=self.policy)
+ part._headers = part_headers
+ part._payload = self._payload
+ self._payload = [part]
+ else:
+ self._payload = []
+ self._headers = keep_headers
+ self['Content-Type'] = 'multipart/' + subtype
+ if boundary is not None:
+ self.set_param('boundary', boundary)
+
+ def make_related(self, boundary=None):
+ self._make_multipart('related', ('alternative', 'mixed'), boundary)
+
+ def make_alternative(self, boundary=None):
+ self._make_multipart('alternative', ('mixed',), boundary)
+
+ def make_mixed(self, boundary=None):
+ self._make_multipart('mixed', (), boundary)
+
+ def _add_multipart(self, _subtype, *args, _disp=None, **kw):
+ if (self.get_content_maintype() != 'multipart' or
+ self.get_content_subtype() != _subtype):
+ getattr(self, 'make_' + _subtype)()
+ part = type(self)(policy=self.policy)
+ part.set_content(*args, **kw)
+ if _disp and 'content-disposition' not in part:
+ part['Content-Disposition'] = _disp
+ self.attach(part)
+
+ def add_related(self, *args, **kw):
+ self._add_multipart('related', *args, _disp='inline', **kw)
+
+ def add_alternative(self, *args, **kw):
+ self._add_multipart('alternative', *args, **kw)
+
+ def add_attachment(self, *args, **kw):
+ self._add_multipart('mixed', *args, _disp='attachment', **kw)
+
+ def clear(self):
+ self._headers = []
+ self._payload = None
+
+ def clear_content(self):
+ self._headers = [(n, v) for n, v in self._headers
+ if not n.lower().startswith('content-')]
+ self._payload = None
+
+
+class EmailMessage(MIMEPart):
+
+ def set_content(self, *args, **kw):
+ super().set_content(*args, **kw)
+ if 'MIME-Version' not in self:
+ self['MIME-Version'] = '1.0'
diff --git a/Lib/email/mime/text.py b/Lib/email/mime/text.py
index 3b5b09f..ec18b85 100644
--- a/Lib/email/mime/text.py
+++ b/Lib/email/mime/text.py
@@ -6,7 +6,6 @@
__all__ = ['MIMEText']
-from email.encoders import encode_7or8bit
from email.mime.nonmultipart import MIMENonMultipart
diff --git a/Lib/email/parser.py b/Lib/email/parser.py
index 752bf35..9f5f95d 100644
--- a/Lib/email/parser.py
+++ b/Lib/email/parser.py
@@ -4,19 +4,18 @@
"""A parser of RFC 2822 and MIME email messages."""
-__all__ = ['Parser', 'HeaderParser', 'BytesParser', 'BytesHeaderParser']
+__all__ = ['Parser', 'HeaderParser', 'BytesParser', 'BytesHeaderParser',
+ 'FeedParser', 'BytesFeedParser']
-import warnings
from io import StringIO, TextIOWrapper
from email.feedparser import FeedParser, BytesFeedParser
-from email.message import Message
from email._policybase import compat32
class Parser:
- def __init__(self, _class=Message, *, policy=compat32):
+ def __init__(self, _class=None, *, policy=compat32):
"""Parser of RFC 2822 and MIME email messages.
Creates an in-memory object tree representing the email message, which
diff --git a/Lib/email/policy.py b/Lib/email/policy.py
index 38e88af..f0b20f4 100644
--- a/Lib/email/policy.py
+++ b/Lib/email/policy.py
@@ -5,6 +5,7 @@ code that adds all the email6 features.
from email._policybase import Policy, Compat32, compat32, _extend_docstrings
from email.utils import _has_surrogates
from email.headerregistry import HeaderRegistry as HeaderRegistry
+from email.contentmanager import raw_data_manager
__all__ = [
'Compat32',
@@ -58,10 +59,22 @@ class EmailPolicy(Policy):
special treatment, while all other fields are
treated as unstructured. This list will be
completed before the extension is marked stable.)
+
+ content_manager -- an object with at least two methods: get_content
+ and set_content. When the get_content or
+ set_content method of a Message object is called,
+ it calls the corresponding method of this object,
+ passing it the message object as its first argument,
+ and any arguments or keywords that were passed to
+ it as additional arguments. The default
+ content_manager is
+ :data:`~email.contentmanager.raw_data_manager`.
+
"""
refold_source = 'long'
header_factory = HeaderRegistry()
+ content_manager = raw_data_manager
def __init__(self, **kw):
# Ensure that each new instance gets a unique header factory
diff --git a/Lib/email/quoprimime.py b/Lib/email/quoprimime.py
index 30bf916..c1fe2b4 100644
--- a/Lib/email/quoprimime.py
+++ b/Lib/email/quoprimime.py
@@ -40,7 +40,6 @@ __all__ = [
]
import re
-import io
from string import ascii_letters, digits, hexdigits
diff --git a/Lib/email/utils.py b/Lib/email/utils.py
index f76c21e..cacb9b1 100644
--- a/Lib/email/utils.py
+++ b/Lib/email/utils.py
@@ -25,13 +25,10 @@ __all__ = [
import os
import re
import time
-import base64
import random
import socket
import datetime
import urllib.parse
-import warnings
-from io import StringIO
from email._parseaddr import quote
from email._parseaddr import AddressList as _AddressList
@@ -39,10 +36,7 @@ from email._parseaddr import mktime_tz
from email._parseaddr import parsedate, parsedate_tz, _parsedate_tz
-from quopri import decodestring as _qdecode
-
# Intrapackage imports
-from email.encoders import _bencode, _qencode
from email.charset import Charset
COMMASPACE = ', '
@@ -54,17 +48,27 @@ TICK = "'"
specialsre = re.compile(r'[][\\()<>@,:;".]')
escapesre = re.compile(r'[\\"]')
-# How to figure out if we are processing strings that come from a byte
-# source with undecodable characters.
-_has_surrogates = re.compile(
- '([^\ud800-\udbff]|\A)[\udc00-\udfff]([^\udc00-\udfff]|\Z)').search
+def _has_surrogates(s):
+ """Return True if s contains surrogate-escaped binary data."""
+ # This check is based on the fact that unless there are surrogates, utf8
+ # (Python's default encoding) can encode any string. This is the fastest
+ # way to check for surrogates, see issue 11454 for timings.
+ try:
+ s.encode()
+ return False
+ except UnicodeEncodeError:
+ return True
# How to deal with a string containing bytes before handing it to the
# application through the 'normal' interface.
def _sanitize(string):
- # Turn any escaped bytes into unicode 'unknown' char.
- original_bytes = string.encode('ascii', 'surrogateescape')
- return original_bytes.decode('ascii', 'replace')
+ # Turn any escaped bytes into unicode 'unknown' char. If the escaped
+ # bytes happen to be utf-8 they will instead get decoded, even if they
+ # were invalid in the charset the source was supposed to be in. This
+ # seems like it is not a bad thing; a defect was still registered.
+ original_bytes = string.encode('utf-8', 'surrogateescape')
+ return original_bytes.decode('utf-8', 'replace')
+
# Helpers
diff --git a/Lib/encodings/aliases.py b/Lib/encodings/aliases.py
index 235deb5..4cbaade 100644
--- a/Lib/encodings/aliases.py
+++ b/Lib/encodings/aliases.py
@@ -33,9 +33,9 @@ aliases = {
'us' : 'ascii',
'us_ascii' : 'ascii',
- ## base64_codec codec
- #'base64' : 'base64_codec',
- #'base_64' : 'base64_codec',
+ # base64_codec codec
+ 'base64' : 'base64_codec',
+ 'base_64' : 'base64_codec',
# big5 codec
'big5_tw' : 'big5',
@@ -45,8 +45,8 @@ aliases = {
'big5_hkscs' : 'big5hkscs',
'hkscs' : 'big5hkscs',
- ## bz2_codec codec
- #'bz2' : 'bz2_codec',
+ # bz2_codec codec
+ 'bz2' : 'bz2_codec',
# cp037 codec
'037' : 'cp037',
@@ -63,6 +63,12 @@ aliases = {
'csibm1026' : 'cp1026',
'ibm1026' : 'cp1026',
+ # cp1125 codec
+ '1125' : 'cp1125',
+ 'ibm1125' : 'cp1125',
+ 'cp866u' : 'cp1125',
+ 'ruscii' : 'cp1125',
+
# cp1140 codec
'1140' : 'cp1140',
'ibm1140' : 'cp1140',
@@ -103,6 +109,11 @@ aliases = {
'1258' : 'cp1258',
'windows_1258' : 'cp1258',
+ # cp273 codec
+ '273' : 'cp273',
+ 'ibm273' : 'cp273',
+ 'csibm273' : 'cp273',
+
# cp424 codec
'424' : 'cp424',
'csibm424' : 'cp424',
@@ -248,8 +259,8 @@ aliases = {
'cp936' : 'gbk',
'ms936' : 'gbk',
- ## hex_codec codec
- #'hex' : 'hex_codec',
+ # hex_codec codec
+ 'hex' : 'hex_codec',
# hp_roman8 codec
'roman8' : 'hp_roman8',
@@ -450,13 +461,13 @@ aliases = {
'cp154' : 'ptcp154',
'cyrillic_asian' : 'ptcp154',
- ## quopri_codec codec
- #'quopri' : 'quopri_codec',
- #'quoted_printable' : 'quopri_codec',
- #'quotedprintable' : 'quopri_codec',
+ # quopri_codec codec
+ 'quopri' : 'quopri_codec',
+ 'quoted_printable' : 'quopri_codec',
+ 'quotedprintable' : 'quopri_codec',
- ## rot_13 codec
- #'rot13' : 'rot_13',
+ # rot_13 codec
+ 'rot13' : 'rot_13',
# shift_jis codec
'csshiftjis' : 'shift_jis',
@@ -518,12 +529,12 @@ aliases = {
'utf8_ucs2' : 'utf_8',
'utf8_ucs4' : 'utf_8',
- ## uu_codec codec
- #'uu' : 'uu_codec',
+ # uu_codec codec
+ 'uu' : 'uu_codec',
- ## zlib_codec codec
- #'zip' : 'zlib_codec',
- #'zlib' : 'zlib_codec',
+ # zlib_codec codec
+ 'zip' : 'zlib_codec',
+ 'zlib' : 'zlib_codec',
# temporary mac CJK aliases, will be replaced by proper codecs in 3.1
'x_mac_japanese' : 'shift_jis',
diff --git a/Lib/encodings/cp037.py b/Lib/encodings/cp037.py
index bfe2c1e..4edd708 100644
--- a/Lib/encodings/cp037.py
+++ b/Lib/encodings/cp037.py
@@ -301,7 +301,6 @@ decoding_table = (
'\xd9' # 0xFD -> LATIN CAPITAL LETTER U WITH GRAVE
'\xda' # 0xFE -> LATIN CAPITAL LETTER U WITH ACUTE
'\x9f' # 0xFF -> CONTROL
- '\ufffe' ## Widen to UCS2 for optimization
)
### Encoding table
diff --git a/Lib/encodings/cp1125.py b/Lib/encodings/cp1125.py
new file mode 100644
index 0000000..b1fd69d
--- /dev/null
+++ b/Lib/encodings/cp1125.py
@@ -0,0 +1,698 @@
+""" Python Character Mapping Codec for CP1125
+
+"""#"
+
+import codecs
+
+### Codec APIs
+
+class Codec(codecs.Codec):
+
+ def encode(self,input,errors='strict'):
+ return codecs.charmap_encode(input,errors,encoding_map)
+
+ def decode(self,input,errors='strict'):
+ return codecs.charmap_decode(input,errors,decoding_table)
+
+class IncrementalEncoder(codecs.IncrementalEncoder):
+ def encode(self, input, final=False):
+ return codecs.charmap_encode(input,self.errors,encoding_map)[0]
+
+class IncrementalDecoder(codecs.IncrementalDecoder):
+ def decode(self, input, final=False):
+ return codecs.charmap_decode(input,self.errors,decoding_table)[0]
+
+class StreamWriter(Codec,codecs.StreamWriter):
+ pass
+
+class StreamReader(Codec,codecs.StreamReader):
+ pass
+
+### encodings module API
+
+def getregentry():
+ return codecs.CodecInfo(
+ name='cp1125',
+ encode=Codec().encode,
+ decode=Codec().decode,
+ incrementalencoder=IncrementalEncoder,
+ incrementaldecoder=IncrementalDecoder,
+ streamreader=StreamReader,
+ streamwriter=StreamWriter,
+ )
+
+### Decoding Map
+
+decoding_map = codecs.make_identity_dict(range(256))
+decoding_map.update({
+ 0x0080: 0x0410, # CYRILLIC CAPITAL LETTER A
+ 0x0081: 0x0411, # CYRILLIC CAPITAL LETTER BE
+ 0x0082: 0x0412, # CYRILLIC CAPITAL LETTER VE
+ 0x0083: 0x0413, # CYRILLIC CAPITAL LETTER GHE
+ 0x0084: 0x0414, # CYRILLIC CAPITAL LETTER DE
+ 0x0085: 0x0415, # CYRILLIC CAPITAL LETTER IE
+ 0x0086: 0x0416, # CYRILLIC CAPITAL LETTER ZHE
+ 0x0087: 0x0417, # CYRILLIC CAPITAL LETTER ZE
+ 0x0088: 0x0418, # CYRILLIC CAPITAL LETTER I
+ 0x0089: 0x0419, # CYRILLIC CAPITAL LETTER SHORT I
+ 0x008a: 0x041a, # CYRILLIC CAPITAL LETTER KA
+ 0x008b: 0x041b, # CYRILLIC CAPITAL LETTER EL
+ 0x008c: 0x041c, # CYRILLIC CAPITAL LETTER EM
+ 0x008d: 0x041d, # CYRILLIC CAPITAL LETTER EN
+ 0x008e: 0x041e, # CYRILLIC CAPITAL LETTER O
+ 0x008f: 0x041f, # CYRILLIC CAPITAL LETTER PE
+ 0x0090: 0x0420, # CYRILLIC CAPITAL LETTER ER
+ 0x0091: 0x0421, # CYRILLIC CAPITAL LETTER ES
+ 0x0092: 0x0422, # CYRILLIC CAPITAL LETTER TE
+ 0x0093: 0x0423, # CYRILLIC CAPITAL LETTER U
+ 0x0094: 0x0424, # CYRILLIC CAPITAL LETTER EF
+ 0x0095: 0x0425, # CYRILLIC CAPITAL LETTER HA
+ 0x0096: 0x0426, # CYRILLIC CAPITAL LETTER TSE
+ 0x0097: 0x0427, # CYRILLIC CAPITAL LETTER CHE
+ 0x0098: 0x0428, # CYRILLIC CAPITAL LETTER SHA
+ 0x0099: 0x0429, # CYRILLIC CAPITAL LETTER SHCHA
+ 0x009a: 0x042a, # CYRILLIC CAPITAL LETTER HARD SIGN
+ 0x009b: 0x042b, # CYRILLIC CAPITAL LETTER YERU
+ 0x009c: 0x042c, # CYRILLIC CAPITAL LETTER SOFT SIGN
+ 0x009d: 0x042d, # CYRILLIC CAPITAL LETTER E
+ 0x009e: 0x042e, # CYRILLIC CAPITAL LETTER YU
+ 0x009f: 0x042f, # CYRILLIC CAPITAL LETTER YA
+ 0x00a0: 0x0430, # CYRILLIC SMALL LETTER A
+ 0x00a1: 0x0431, # CYRILLIC SMALL LETTER BE
+ 0x00a2: 0x0432, # CYRILLIC SMALL LETTER VE
+ 0x00a3: 0x0433, # CYRILLIC SMALL LETTER GHE
+ 0x00a4: 0x0434, # CYRILLIC SMALL LETTER DE
+ 0x00a5: 0x0435, # CYRILLIC SMALL LETTER IE
+ 0x00a6: 0x0436, # CYRILLIC SMALL LETTER ZHE
+ 0x00a7: 0x0437, # CYRILLIC SMALL LETTER ZE
+ 0x00a8: 0x0438, # CYRILLIC SMALL LETTER I
+ 0x00a9: 0x0439, # CYRILLIC SMALL LETTER SHORT I
+ 0x00aa: 0x043a, # CYRILLIC SMALL LETTER KA
+ 0x00ab: 0x043b, # CYRILLIC SMALL LETTER EL
+ 0x00ac: 0x043c, # CYRILLIC SMALL LETTER EM
+ 0x00ad: 0x043d, # CYRILLIC SMALL LETTER EN
+ 0x00ae: 0x043e, # CYRILLIC SMALL LETTER O
+ 0x00af: 0x043f, # CYRILLIC SMALL LETTER PE
+ 0x00b0: 0x2591, # LIGHT SHADE
+ 0x00b1: 0x2592, # MEDIUM SHADE
+ 0x00b2: 0x2593, # DARK SHADE
+ 0x00b3: 0x2502, # BOX DRAWINGS LIGHT VERTICAL
+ 0x00b4: 0x2524, # BOX DRAWINGS LIGHT VERTICAL AND LEFT
+ 0x00b5: 0x2561, # BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
+ 0x00b6: 0x2562, # BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE
+ 0x00b7: 0x2556, # BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE
+ 0x00b8: 0x2555, # BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE
+ 0x00b9: 0x2563, # BOX DRAWINGS DOUBLE VERTICAL AND LEFT
+ 0x00ba: 0x2551, # BOX DRAWINGS DOUBLE VERTICAL
+ 0x00bb: 0x2557, # BOX DRAWINGS DOUBLE DOWN AND LEFT
+ 0x00bc: 0x255d, # BOX DRAWINGS DOUBLE UP AND LEFT
+ 0x00bd: 0x255c, # BOX DRAWINGS UP DOUBLE AND LEFT SINGLE
+ 0x00be: 0x255b, # BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
+ 0x00bf: 0x2510, # BOX DRAWINGS LIGHT DOWN AND LEFT
+ 0x00c0: 0x2514, # BOX DRAWINGS LIGHT UP AND RIGHT
+ 0x00c1: 0x2534, # BOX DRAWINGS LIGHT UP AND HORIZONTAL
+ 0x00c2: 0x252c, # BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+ 0x00c3: 0x251c, # BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+ 0x00c4: 0x2500, # BOX DRAWINGS LIGHT HORIZONTAL
+ 0x00c5: 0x253c, # BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
+ 0x00c6: 0x255e, # BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
+ 0x00c7: 0x255f, # BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE
+ 0x00c8: 0x255a, # BOX DRAWINGS DOUBLE UP AND RIGHT
+ 0x00c9: 0x2554, # BOX DRAWINGS DOUBLE DOWN AND RIGHT
+ 0x00ca: 0x2569, # BOX DRAWINGS DOUBLE UP AND HORIZONTAL
+ 0x00cb: 0x2566, # BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
+ 0x00cc: 0x2560, # BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
+ 0x00cd: 0x2550, # BOX DRAWINGS DOUBLE HORIZONTAL
+ 0x00ce: 0x256c, # BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
+ 0x00cf: 0x2567, # BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
+ 0x00d0: 0x2568, # BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE
+ 0x00d1: 0x2564, # BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE
+ 0x00d2: 0x2565, # BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE
+ 0x00d3: 0x2559, # BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE
+ 0x00d4: 0x2558, # BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
+ 0x00d5: 0x2552, # BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE
+ 0x00d6: 0x2553, # BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE
+ 0x00d7: 0x256b, # BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE
+ 0x00d8: 0x256a, # BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
+ 0x00d9: 0x2518, # BOX DRAWINGS LIGHT UP AND LEFT
+ 0x00da: 0x250c, # BOX DRAWINGS LIGHT DOWN AND RIGHT
+ 0x00db: 0x2588, # FULL BLOCK
+ 0x00dc: 0x2584, # LOWER HALF BLOCK
+ 0x00dd: 0x258c, # LEFT HALF BLOCK
+ 0x00de: 0x2590, # RIGHT HALF BLOCK
+ 0x00df: 0x2580, # UPPER HALF BLOCK
+ 0x00e0: 0x0440, # CYRILLIC SMALL LETTER ER
+ 0x00e1: 0x0441, # CYRILLIC SMALL LETTER ES
+ 0x00e2: 0x0442, # CYRILLIC SMALL LETTER TE
+ 0x00e3: 0x0443, # CYRILLIC SMALL LETTER U
+ 0x00e4: 0x0444, # CYRILLIC SMALL LETTER EF
+ 0x00e5: 0x0445, # CYRILLIC SMALL LETTER HA
+ 0x00e6: 0x0446, # CYRILLIC SMALL LETTER TSE
+ 0x00e7: 0x0447, # CYRILLIC SMALL LETTER CHE
+ 0x00e8: 0x0448, # CYRILLIC SMALL LETTER SHA
+ 0x00e9: 0x0449, # CYRILLIC SMALL LETTER SHCHA
+ 0x00ea: 0x044a, # CYRILLIC SMALL LETTER HARD SIGN
+ 0x00eb: 0x044b, # CYRILLIC SMALL LETTER YERU
+ 0x00ec: 0x044c, # CYRILLIC SMALL LETTER SOFT SIGN
+ 0x00ed: 0x044d, # CYRILLIC SMALL LETTER E
+ 0x00ee: 0x044e, # CYRILLIC SMALL LETTER YU
+ 0x00ef: 0x044f, # CYRILLIC SMALL LETTER YA
+ 0x00f0: 0x0401, # CYRILLIC CAPITAL LETTER IO
+ 0x00f1: 0x0451, # CYRILLIC SMALL LETTER IO
+ 0x00f2: 0x0490, # CYRILLIC CAPITAL LETTER GHE WITH UPTURN
+ 0x00f3: 0x0491, # CYRILLIC SMALL LETTER GHE WITH UPTURN
+ 0x00f4: 0x0404, # CYRILLIC CAPITAL LETTER UKRAINIAN IE
+ 0x00f5: 0x0454, # CYRILLIC SMALL LETTER UKRAINIAN IE
+ 0x00f6: 0x0406, # CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I
+ 0x00f7: 0x0456, # CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
+ 0x00f8: 0x0407, # CYRILLIC CAPITAL LETTER YI
+ 0x00f9: 0x0457, # CYRILLIC SMALL LETTER YI
+ 0x00fa: 0x00b7, # MIDDLE DOT
+ 0x00fb: 0x221a, # SQUARE ROOT
+ 0x00fc: 0x2116, # NUMERO SIGN
+ 0x00fd: 0x00a4, # CURRENCY SIGN
+ 0x00fe: 0x25a0, # BLACK SQUARE
+ 0x00ff: 0x00a0, # NO-BREAK SPACE
+})
+
+### Decoding Table
+
+decoding_table = (
+ '\x00' # 0x0000 -> NULL
+ '\x01' # 0x0001 -> START OF HEADING
+ '\x02' # 0x0002 -> START OF TEXT
+ '\x03' # 0x0003 -> END OF TEXT
+ '\x04' # 0x0004 -> END OF TRANSMISSION
+ '\x05' # 0x0005 -> ENQUIRY
+ '\x06' # 0x0006 -> ACKNOWLEDGE
+ '\x07' # 0x0007 -> BELL
+ '\x08' # 0x0008 -> BACKSPACE
+ '\t' # 0x0009 -> HORIZONTAL TABULATION
+ '\n' # 0x000a -> LINE FEED
+ '\x0b' # 0x000b -> VERTICAL TABULATION
+ '\x0c' # 0x000c -> FORM FEED
+ '\r' # 0x000d -> CARRIAGE RETURN
+ '\x0e' # 0x000e -> SHIFT OUT
+ '\x0f' # 0x000f -> SHIFT IN
+ '\x10' # 0x0010 -> DATA LINK ESCAPE
+ '\x11' # 0x0011 -> DEVICE CONTROL ONE
+ '\x12' # 0x0012 -> DEVICE CONTROL TWO
+ '\x13' # 0x0013 -> DEVICE CONTROL THREE
+ '\x14' # 0x0014 -> DEVICE CONTROL FOUR
+ '\x15' # 0x0015 -> NEGATIVE ACKNOWLEDGE
+ '\x16' # 0x0016 -> SYNCHRONOUS IDLE
+ '\x17' # 0x0017 -> END OF TRANSMISSION BLOCK
+ '\x18' # 0x0018 -> CANCEL
+ '\x19' # 0x0019 -> END OF MEDIUM
+ '\x1a' # 0x001a -> SUBSTITUTE
+ '\x1b' # 0x001b -> ESCAPE
+ '\x1c' # 0x001c -> FILE SEPARATOR
+ '\x1d' # 0x001d -> GROUP SEPARATOR
+ '\x1e' # 0x001e -> RECORD SEPARATOR
+ '\x1f' # 0x001f -> UNIT SEPARATOR
+ ' ' # 0x0020 -> SPACE
+ '!' # 0x0021 -> EXCLAMATION MARK
+ '"' # 0x0022 -> QUOTATION MARK
+ '#' # 0x0023 -> NUMBER SIGN
+ '$' # 0x0024 -> DOLLAR SIGN
+ '%' # 0x0025 -> PERCENT SIGN
+ '&' # 0x0026 -> AMPERSAND
+ "'" # 0x0027 -> APOSTROPHE
+ '(' # 0x0028 -> LEFT PARENTHESIS
+ ')' # 0x0029 -> RIGHT PARENTHESIS
+ '*' # 0x002a -> ASTERISK
+ '+' # 0x002b -> PLUS SIGN
+ ',' # 0x002c -> COMMA
+ '-' # 0x002d -> HYPHEN-MINUS
+ '.' # 0x002e -> FULL STOP
+ '/' # 0x002f -> SOLIDUS
+ '0' # 0x0030 -> DIGIT ZERO
+ '1' # 0x0031 -> DIGIT ONE
+ '2' # 0x0032 -> DIGIT TWO
+ '3' # 0x0033 -> DIGIT THREE
+ '4' # 0x0034 -> DIGIT FOUR
+ '5' # 0x0035 -> DIGIT FIVE
+ '6' # 0x0036 -> DIGIT SIX
+ '7' # 0x0037 -> DIGIT SEVEN
+ '8' # 0x0038 -> DIGIT EIGHT
+ '9' # 0x0039 -> DIGIT NINE
+ ':' # 0x003a -> COLON
+ ';' # 0x003b -> SEMICOLON
+ '<' # 0x003c -> LESS-THAN SIGN
+ '=' # 0x003d -> EQUALS SIGN
+ '>' # 0x003e -> GREATER-THAN SIGN
+ '?' # 0x003f -> QUESTION MARK
+ '@' # 0x0040 -> COMMERCIAL AT
+ 'A' # 0x0041 -> LATIN CAPITAL LETTER A
+ 'B' # 0x0042 -> LATIN CAPITAL LETTER B
+ 'C' # 0x0043 -> LATIN CAPITAL LETTER C
+ 'D' # 0x0044 -> LATIN CAPITAL LETTER D
+ 'E' # 0x0045 -> LATIN CAPITAL LETTER E
+ 'F' # 0x0046 -> LATIN CAPITAL LETTER F
+ 'G' # 0x0047 -> LATIN CAPITAL LETTER G
+ 'H' # 0x0048 -> LATIN CAPITAL LETTER H
+ 'I' # 0x0049 -> LATIN CAPITAL LETTER I
+ 'J' # 0x004a -> LATIN CAPITAL LETTER J
+ 'K' # 0x004b -> LATIN CAPITAL LETTER K
+ 'L' # 0x004c -> LATIN CAPITAL LETTER L
+ 'M' # 0x004d -> LATIN CAPITAL LETTER M
+ 'N' # 0x004e -> LATIN CAPITAL LETTER N
+ 'O' # 0x004f -> LATIN CAPITAL LETTER O
+ 'P' # 0x0050 -> LATIN CAPITAL LETTER P
+ 'Q' # 0x0051 -> LATIN CAPITAL LETTER Q
+ 'R' # 0x0052 -> LATIN CAPITAL LETTER R
+ 'S' # 0x0053 -> LATIN CAPITAL LETTER S
+ 'T' # 0x0054 -> LATIN CAPITAL LETTER T
+ 'U' # 0x0055 -> LATIN CAPITAL LETTER U
+ 'V' # 0x0056 -> LATIN CAPITAL LETTER V
+ 'W' # 0x0057 -> LATIN CAPITAL LETTER W
+ 'X' # 0x0058 -> LATIN CAPITAL LETTER X
+ 'Y' # 0x0059 -> LATIN CAPITAL LETTER Y
+ 'Z' # 0x005a -> LATIN CAPITAL LETTER Z
+ '[' # 0x005b -> LEFT SQUARE BRACKET
+ '\\' # 0x005c -> REVERSE SOLIDUS
+ ']' # 0x005d -> RIGHT SQUARE BRACKET
+ '^' # 0x005e -> CIRCUMFLEX ACCENT
+ '_' # 0x005f -> LOW LINE
+ '`' # 0x0060 -> GRAVE ACCENT
+ 'a' # 0x0061 -> LATIN SMALL LETTER A
+ 'b' # 0x0062 -> LATIN SMALL LETTER B
+ 'c' # 0x0063 -> LATIN SMALL LETTER C
+ 'd' # 0x0064 -> LATIN SMALL LETTER D
+ 'e' # 0x0065 -> LATIN SMALL LETTER E
+ 'f' # 0x0066 -> LATIN SMALL LETTER F
+ 'g' # 0x0067 -> LATIN SMALL LETTER G
+ 'h' # 0x0068 -> LATIN SMALL LETTER H
+ 'i' # 0x0069 -> LATIN SMALL LETTER I
+ 'j' # 0x006a -> LATIN SMALL LETTER J
+ 'k' # 0x006b -> LATIN SMALL LETTER K
+ 'l' # 0x006c -> LATIN SMALL LETTER L
+ 'm' # 0x006d -> LATIN SMALL LETTER M
+ 'n' # 0x006e -> LATIN SMALL LETTER N
+ 'o' # 0x006f -> LATIN SMALL LETTER O
+ 'p' # 0x0070 -> LATIN SMALL LETTER P
+ 'q' # 0x0071 -> LATIN SMALL LETTER Q
+ 'r' # 0x0072 -> LATIN SMALL LETTER R
+ 's' # 0x0073 -> LATIN SMALL LETTER S
+ 't' # 0x0074 -> LATIN SMALL LETTER T
+ 'u' # 0x0075 -> LATIN SMALL LETTER U
+ 'v' # 0x0076 -> LATIN SMALL LETTER V
+ 'w' # 0x0077 -> LATIN SMALL LETTER W
+ 'x' # 0x0078 -> LATIN SMALL LETTER X
+ 'y' # 0x0079 -> LATIN SMALL LETTER Y
+ 'z' # 0x007a -> LATIN SMALL LETTER Z
+ '{' # 0x007b -> LEFT CURLY BRACKET
+ '|' # 0x007c -> VERTICAL LINE
+ '}' # 0x007d -> RIGHT CURLY BRACKET
+ '~' # 0x007e -> TILDE
+ '\x7f' # 0x007f -> DELETE
+ '\u0410' # 0x0080 -> CYRILLIC CAPITAL LETTER A
+ '\u0411' # 0x0081 -> CYRILLIC CAPITAL LETTER BE
+ '\u0412' # 0x0082 -> CYRILLIC CAPITAL LETTER VE
+ '\u0413' # 0x0083 -> CYRILLIC CAPITAL LETTER GHE
+ '\u0414' # 0x0084 -> CYRILLIC CAPITAL LETTER DE
+ '\u0415' # 0x0085 -> CYRILLIC CAPITAL LETTER IE
+ '\u0416' # 0x0086 -> CYRILLIC CAPITAL LETTER ZHE
+ '\u0417' # 0x0087 -> CYRILLIC CAPITAL LETTER ZE
+ '\u0418' # 0x0088 -> CYRILLIC CAPITAL LETTER I
+ '\u0419' # 0x0089 -> CYRILLIC CAPITAL LETTER SHORT I
+ '\u041a' # 0x008a -> CYRILLIC CAPITAL LETTER KA
+ '\u041b' # 0x008b -> CYRILLIC CAPITAL LETTER EL
+ '\u041c' # 0x008c -> CYRILLIC CAPITAL LETTER EM
+ '\u041d' # 0x008d -> CYRILLIC CAPITAL LETTER EN
+ '\u041e' # 0x008e -> CYRILLIC CAPITAL LETTER O
+ '\u041f' # 0x008f -> CYRILLIC CAPITAL LETTER PE
+ '\u0420' # 0x0090 -> CYRILLIC CAPITAL LETTER ER
+ '\u0421' # 0x0091 -> CYRILLIC CAPITAL LETTER ES
+ '\u0422' # 0x0092 -> CYRILLIC CAPITAL LETTER TE
+ '\u0423' # 0x0093 -> CYRILLIC CAPITAL LETTER U
+ '\u0424' # 0x0094 -> CYRILLIC CAPITAL LETTER EF
+ '\u0425' # 0x0095 -> CYRILLIC CAPITAL LETTER HA
+ '\u0426' # 0x0096 -> CYRILLIC CAPITAL LETTER TSE
+ '\u0427' # 0x0097 -> CYRILLIC CAPITAL LETTER CHE
+ '\u0428' # 0x0098 -> CYRILLIC CAPITAL LETTER SHA
+ '\u0429' # 0x0099 -> CYRILLIC CAPITAL LETTER SHCHA
+ '\u042a' # 0x009a -> CYRILLIC CAPITAL LETTER HARD SIGN
+ '\u042b' # 0x009b -> CYRILLIC CAPITAL LETTER YERU
+ '\u042c' # 0x009c -> CYRILLIC CAPITAL LETTER SOFT SIGN
+ '\u042d' # 0x009d -> CYRILLIC CAPITAL LETTER E
+ '\u042e' # 0x009e -> CYRILLIC CAPITAL LETTER YU
+ '\u042f' # 0x009f -> CYRILLIC CAPITAL LETTER YA
+ '\u0430' # 0x00a0 -> CYRILLIC SMALL LETTER A
+ '\u0431' # 0x00a1 -> CYRILLIC SMALL LETTER BE
+ '\u0432' # 0x00a2 -> CYRILLIC SMALL LETTER VE
+ '\u0433' # 0x00a3 -> CYRILLIC SMALL LETTER GHE
+ '\u0434' # 0x00a4 -> CYRILLIC SMALL LETTER DE
+ '\u0435' # 0x00a5 -> CYRILLIC SMALL LETTER IE
+ '\u0436' # 0x00a6 -> CYRILLIC SMALL LETTER ZHE
+ '\u0437' # 0x00a7 -> CYRILLIC SMALL LETTER ZE
+ '\u0438' # 0x00a8 -> CYRILLIC SMALL LETTER I
+ '\u0439' # 0x00a9 -> CYRILLIC SMALL LETTER SHORT I
+ '\u043a' # 0x00aa -> CYRILLIC SMALL LETTER KA
+ '\u043b' # 0x00ab -> CYRILLIC SMALL LETTER EL
+ '\u043c' # 0x00ac -> CYRILLIC SMALL LETTER EM
+ '\u043d' # 0x00ad -> CYRILLIC SMALL LETTER EN
+ '\u043e' # 0x00ae -> CYRILLIC SMALL LETTER O
+ '\u043f' # 0x00af -> CYRILLIC SMALL LETTER PE
+ '\u2591' # 0x00b0 -> LIGHT SHADE
+ '\u2592' # 0x00b1 -> MEDIUM SHADE
+ '\u2593' # 0x00b2 -> DARK SHADE
+ '\u2502' # 0x00b3 -> BOX DRAWINGS LIGHT VERTICAL
+ '\u2524' # 0x00b4 -> BOX DRAWINGS LIGHT VERTICAL AND LEFT
+ '\u2561' # 0x00b5 -> BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
+ '\u2562' # 0x00b6 -> BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE
+ '\u2556' # 0x00b7 -> BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE
+ '\u2555' # 0x00b8 -> BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE
+ '\u2563' # 0x00b9 -> BOX DRAWINGS DOUBLE VERTICAL AND LEFT
+ '\u2551' # 0x00ba -> BOX DRAWINGS DOUBLE VERTICAL
+ '\u2557' # 0x00bb -> BOX DRAWINGS DOUBLE DOWN AND LEFT
+ '\u255d' # 0x00bc -> BOX DRAWINGS DOUBLE UP AND LEFT
+ '\u255c' # 0x00bd -> BOX DRAWINGS UP DOUBLE AND LEFT SINGLE
+ '\u255b' # 0x00be -> BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
+ '\u2510' # 0x00bf -> BOX DRAWINGS LIGHT DOWN AND LEFT
+ '\u2514' # 0x00c0 -> BOX DRAWINGS LIGHT UP AND RIGHT
+ '\u2534' # 0x00c1 -> BOX DRAWINGS LIGHT UP AND HORIZONTAL
+ '\u252c' # 0x00c2 -> BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+ '\u251c' # 0x00c3 -> BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+ '\u2500' # 0x00c4 -> BOX DRAWINGS LIGHT HORIZONTAL
+ '\u253c' # 0x00c5 -> BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
+ '\u255e' # 0x00c6 -> BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
+ '\u255f' # 0x00c7 -> BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE
+ '\u255a' # 0x00c8 -> BOX DRAWINGS DOUBLE UP AND RIGHT
+ '\u2554' # 0x00c9 -> BOX DRAWINGS DOUBLE DOWN AND RIGHT
+ '\u2569' # 0x00ca -> BOX DRAWINGS DOUBLE UP AND HORIZONTAL
+ '\u2566' # 0x00cb -> BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
+ '\u2560' # 0x00cc -> BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
+ '\u2550' # 0x00cd -> BOX DRAWINGS DOUBLE HORIZONTAL
+ '\u256c' # 0x00ce -> BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
+ '\u2567' # 0x00cf -> BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
+ '\u2568' # 0x00d0 -> BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE
+ '\u2564' # 0x00d1 -> BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE
+ '\u2565' # 0x00d2 -> BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE
+ '\u2559' # 0x00d3 -> BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE
+ '\u2558' # 0x00d4 -> BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
+ '\u2552' # 0x00d5 -> BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE
+ '\u2553' # 0x00d6 -> BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE
+ '\u256b' # 0x00d7 -> BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE
+ '\u256a' # 0x00d8 -> BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
+ '\u2518' # 0x00d9 -> BOX DRAWINGS LIGHT UP AND LEFT
+ '\u250c' # 0x00da -> BOX DRAWINGS LIGHT DOWN AND RIGHT
+ '\u2588' # 0x00db -> FULL BLOCK
+ '\u2584' # 0x00dc -> LOWER HALF BLOCK
+ '\u258c' # 0x00dd -> LEFT HALF BLOCK
+ '\u2590' # 0x00de -> RIGHT HALF BLOCK
+ '\u2580' # 0x00df -> UPPER HALF BLOCK
+ '\u0440' # 0x00e0 -> CYRILLIC SMALL LETTER ER
+ '\u0441' # 0x00e1 -> CYRILLIC SMALL LETTER ES
+ '\u0442' # 0x00e2 -> CYRILLIC SMALL LETTER TE
+ '\u0443' # 0x00e3 -> CYRILLIC SMALL LETTER U
+ '\u0444' # 0x00e4 -> CYRILLIC SMALL LETTER EF
+ '\u0445' # 0x00e5 -> CYRILLIC SMALL LETTER HA
+ '\u0446' # 0x00e6 -> CYRILLIC SMALL LETTER TSE
+ '\u0447' # 0x00e7 -> CYRILLIC SMALL LETTER CHE
+ '\u0448' # 0x00e8 -> CYRILLIC SMALL LETTER SHA
+ '\u0449' # 0x00e9 -> CYRILLIC SMALL LETTER SHCHA
+ '\u044a' # 0x00ea -> CYRILLIC SMALL LETTER HARD SIGN
+ '\u044b' # 0x00eb -> CYRILLIC SMALL LETTER YERU
+ '\u044c' # 0x00ec -> CYRILLIC SMALL LETTER SOFT SIGN
+ '\u044d' # 0x00ed -> CYRILLIC SMALL LETTER E
+ '\u044e' # 0x00ee -> CYRILLIC SMALL LETTER YU
+ '\u044f' # 0x00ef -> CYRILLIC SMALL LETTER YA
+ '\u0401' # 0x00f0 -> CYRILLIC CAPITAL LETTER IO
+ '\u0451' # 0x00f1 -> CYRILLIC SMALL LETTER IO
+ '\u0490' # 0x00f2 -> CYRILLIC CAPITAL LETTER GHE WITH UPTURN
+ '\u0491' # 0x00f3 -> CYRILLIC SMALL LETTER GHE WITH UPTURN
+ '\u0404' # 0x00f4 -> CYRILLIC CAPITAL LETTER UKRAINIAN IE
+ '\u0454' # 0x00f5 -> CYRILLIC SMALL LETTER UKRAINIAN IE
+ '\u0406' # 0x00f6 -> CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I
+ '\u0456' # 0x00f7 -> CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
+ '\u0407' # 0x00f8 -> CYRILLIC CAPITAL LETTER YI
+ '\u0457' # 0x00f9 -> CYRILLIC SMALL LETTER YI
+ '\xb7' # 0x00fa -> MIDDLE DOT
+ '\u221a' # 0x00fb -> SQUARE ROOT
+ '\u2116' # 0x00fc -> NUMERO SIGN
+ '\xa4' # 0x00fd -> CURRENCY SIGN
+ '\u25a0' # 0x00fe -> BLACK SQUARE
+ '\xa0' # 0x00ff -> NO-BREAK SPACE
+)
+
+### Encoding Map
+
+encoding_map = {
+ 0x0000: 0x0000, # NULL
+ 0x0001: 0x0001, # START OF HEADING
+ 0x0002: 0x0002, # START OF TEXT
+ 0x0003: 0x0003, # END OF TEXT
+ 0x0004: 0x0004, # END OF TRANSMISSION
+ 0x0005: 0x0005, # ENQUIRY
+ 0x0006: 0x0006, # ACKNOWLEDGE
+ 0x0007: 0x0007, # BELL
+ 0x0008: 0x0008, # BACKSPACE
+ 0x0009: 0x0009, # HORIZONTAL TABULATION
+ 0x000a: 0x000a, # LINE FEED
+ 0x000b: 0x000b, # VERTICAL TABULATION
+ 0x000c: 0x000c, # FORM FEED
+ 0x000d: 0x000d, # CARRIAGE RETURN
+ 0x000e: 0x000e, # SHIFT OUT
+ 0x000f: 0x000f, # SHIFT IN
+ 0x0010: 0x0010, # DATA LINK ESCAPE
+ 0x0011: 0x0011, # DEVICE CONTROL ONE
+ 0x0012: 0x0012, # DEVICE CONTROL TWO
+ 0x0013: 0x0013, # DEVICE CONTROL THREE
+ 0x0014: 0x0014, # DEVICE CONTROL FOUR
+ 0x0015: 0x0015, # NEGATIVE ACKNOWLEDGE
+ 0x0016: 0x0016, # SYNCHRONOUS IDLE
+ 0x0017: 0x0017, # END OF TRANSMISSION BLOCK
+ 0x0018: 0x0018, # CANCEL
+ 0x0019: 0x0019, # END OF MEDIUM
+ 0x001a: 0x001a, # SUBSTITUTE
+ 0x001b: 0x001b, # ESCAPE
+ 0x001c: 0x001c, # FILE SEPARATOR
+ 0x001d: 0x001d, # GROUP SEPARATOR
+ 0x001e: 0x001e, # RECORD SEPARATOR
+ 0x001f: 0x001f, # UNIT SEPARATOR
+ 0x0020: 0x0020, # SPACE
+ 0x0021: 0x0021, # EXCLAMATION MARK
+ 0x0022: 0x0022, # QUOTATION MARK
+ 0x0023: 0x0023, # NUMBER SIGN
+ 0x0024: 0x0024, # DOLLAR SIGN
+ 0x0025: 0x0025, # PERCENT SIGN
+ 0x0026: 0x0026, # AMPERSAND
+ 0x0027: 0x0027, # APOSTROPHE
+ 0x0028: 0x0028, # LEFT PARENTHESIS
+ 0x0029: 0x0029, # RIGHT PARENTHESIS
+ 0x002a: 0x002a, # ASTERISK
+ 0x002b: 0x002b, # PLUS SIGN
+ 0x002c: 0x002c, # COMMA
+ 0x002d: 0x002d, # HYPHEN-MINUS
+ 0x002e: 0x002e, # FULL STOP
+ 0x002f: 0x002f, # SOLIDUS
+ 0x0030: 0x0030, # DIGIT ZERO
+ 0x0031: 0x0031, # DIGIT ONE
+ 0x0032: 0x0032, # DIGIT TWO
+ 0x0033: 0x0033, # DIGIT THREE
+ 0x0034: 0x0034, # DIGIT FOUR
+ 0x0035: 0x0035, # DIGIT FIVE
+ 0x0036: 0x0036, # DIGIT SIX
+ 0x0037: 0x0037, # DIGIT SEVEN
+ 0x0038: 0x0038, # DIGIT EIGHT
+ 0x0039: 0x0039, # DIGIT NINE
+ 0x003a: 0x003a, # COLON
+ 0x003b: 0x003b, # SEMICOLON
+ 0x003c: 0x003c, # LESS-THAN SIGN
+ 0x003d: 0x003d, # EQUALS SIGN
+ 0x003e: 0x003e, # GREATER-THAN SIGN
+ 0x003f: 0x003f, # QUESTION MARK
+ 0x0040: 0x0040, # COMMERCIAL AT
+ 0x0041: 0x0041, # LATIN CAPITAL LETTER A
+ 0x0042: 0x0042, # LATIN CAPITAL LETTER B
+ 0x0043: 0x0043, # LATIN CAPITAL LETTER C
+ 0x0044: 0x0044, # LATIN CAPITAL LETTER D
+ 0x0045: 0x0045, # LATIN CAPITAL LETTER E
+ 0x0046: 0x0046, # LATIN CAPITAL LETTER F
+ 0x0047: 0x0047, # LATIN CAPITAL LETTER G
+ 0x0048: 0x0048, # LATIN CAPITAL LETTER H
+ 0x0049: 0x0049, # LATIN CAPITAL LETTER I
+ 0x004a: 0x004a, # LATIN CAPITAL LETTER J
+ 0x004b: 0x004b, # LATIN CAPITAL LETTER K
+ 0x004c: 0x004c, # LATIN CAPITAL LETTER L
+ 0x004d: 0x004d, # LATIN CAPITAL LETTER M
+ 0x004e: 0x004e, # LATIN CAPITAL LETTER N
+ 0x004f: 0x004f, # LATIN CAPITAL LETTER O
+ 0x0050: 0x0050, # LATIN CAPITAL LETTER P
+ 0x0051: 0x0051, # LATIN CAPITAL LETTER Q
+ 0x0052: 0x0052, # LATIN CAPITAL LETTER R
+ 0x0053: 0x0053, # LATIN CAPITAL LETTER S
+ 0x0054: 0x0054, # LATIN CAPITAL LETTER T
+ 0x0055: 0x0055, # LATIN CAPITAL LETTER U
+ 0x0056: 0x0056, # LATIN CAPITAL LETTER V
+ 0x0057: 0x0057, # LATIN CAPITAL LETTER W
+ 0x0058: 0x0058, # LATIN CAPITAL LETTER X
+ 0x0059: 0x0059, # LATIN CAPITAL LETTER Y
+ 0x005a: 0x005a, # LATIN CAPITAL LETTER Z
+ 0x005b: 0x005b, # LEFT SQUARE BRACKET
+ 0x005c: 0x005c, # REVERSE SOLIDUS
+ 0x005d: 0x005d, # RIGHT SQUARE BRACKET
+ 0x005e: 0x005e, # CIRCUMFLEX ACCENT
+ 0x005f: 0x005f, # LOW LINE
+ 0x0060: 0x0060, # GRAVE ACCENT
+ 0x0061: 0x0061, # LATIN SMALL LETTER A
+ 0x0062: 0x0062, # LATIN SMALL LETTER B
+ 0x0063: 0x0063, # LATIN SMALL LETTER C
+ 0x0064: 0x0064, # LATIN SMALL LETTER D
+ 0x0065: 0x0065, # LATIN SMALL LETTER E
+ 0x0066: 0x0066, # LATIN SMALL LETTER F
+ 0x0067: 0x0067, # LATIN SMALL LETTER G
+ 0x0068: 0x0068, # LATIN SMALL LETTER H
+ 0x0069: 0x0069, # LATIN SMALL LETTER I
+ 0x006a: 0x006a, # LATIN SMALL LETTER J
+ 0x006b: 0x006b, # LATIN SMALL LETTER K
+ 0x006c: 0x006c, # LATIN SMALL LETTER L
+ 0x006d: 0x006d, # LATIN SMALL LETTER M
+ 0x006e: 0x006e, # LATIN SMALL LETTER N
+ 0x006f: 0x006f, # LATIN SMALL LETTER O
+ 0x0070: 0x0070, # LATIN SMALL LETTER P
+ 0x0071: 0x0071, # LATIN SMALL LETTER Q
+ 0x0072: 0x0072, # LATIN SMALL LETTER R
+ 0x0073: 0x0073, # LATIN SMALL LETTER S
+ 0x0074: 0x0074, # LATIN SMALL LETTER T
+ 0x0075: 0x0075, # LATIN SMALL LETTER U
+ 0x0076: 0x0076, # LATIN SMALL LETTER V
+ 0x0077: 0x0077, # LATIN SMALL LETTER W
+ 0x0078: 0x0078, # LATIN SMALL LETTER X
+ 0x0079: 0x0079, # LATIN SMALL LETTER Y
+ 0x007a: 0x007a, # LATIN SMALL LETTER Z
+ 0x007b: 0x007b, # LEFT CURLY BRACKET
+ 0x007c: 0x007c, # VERTICAL LINE
+ 0x007d: 0x007d, # RIGHT CURLY BRACKET
+ 0x007e: 0x007e, # TILDE
+ 0x007f: 0x007f, # DELETE
+ 0x00a0: 0x00ff, # NO-BREAK SPACE
+ 0x00a4: 0x00fd, # CURRENCY SIGN
+ 0x00b7: 0x00fa, # MIDDLE DOT
+ 0x0401: 0x00f0, # CYRILLIC CAPITAL LETTER IO
+ 0x0404: 0x00f4, # CYRILLIC CAPITAL LETTER UKRAINIAN IE
+ 0x0406: 0x00f6, # CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I
+ 0x0407: 0x00f8, # CYRILLIC CAPITAL LETTER YI
+ 0x0410: 0x0080, # CYRILLIC CAPITAL LETTER A
+ 0x0411: 0x0081, # CYRILLIC CAPITAL LETTER BE
+ 0x0412: 0x0082, # CYRILLIC CAPITAL LETTER VE
+ 0x0413: 0x0083, # CYRILLIC CAPITAL LETTER GHE
+ 0x0414: 0x0084, # CYRILLIC CAPITAL LETTER DE
+ 0x0415: 0x0085, # CYRILLIC CAPITAL LETTER IE
+ 0x0416: 0x0086, # CYRILLIC CAPITAL LETTER ZHE
+ 0x0417: 0x0087, # CYRILLIC CAPITAL LETTER ZE
+ 0x0418: 0x0088, # CYRILLIC CAPITAL LETTER I
+ 0x0419: 0x0089, # CYRILLIC CAPITAL LETTER SHORT I
+ 0x041a: 0x008a, # CYRILLIC CAPITAL LETTER KA
+ 0x041b: 0x008b, # CYRILLIC CAPITAL LETTER EL
+ 0x041c: 0x008c, # CYRILLIC CAPITAL LETTER EM
+ 0x041d: 0x008d, # CYRILLIC CAPITAL LETTER EN
+ 0x041e: 0x008e, # CYRILLIC CAPITAL LETTER O
+ 0x041f: 0x008f, # CYRILLIC CAPITAL LETTER PE
+ 0x0420: 0x0090, # CYRILLIC CAPITAL LETTER ER
+ 0x0421: 0x0091, # CYRILLIC CAPITAL LETTER ES
+ 0x0422: 0x0092, # CYRILLIC CAPITAL LETTER TE
+ 0x0423: 0x0093, # CYRILLIC CAPITAL LETTER U
+ 0x0424: 0x0094, # CYRILLIC CAPITAL LETTER EF
+ 0x0425: 0x0095, # CYRILLIC CAPITAL LETTER HA
+ 0x0426: 0x0096, # CYRILLIC CAPITAL LETTER TSE
+ 0x0427: 0x0097, # CYRILLIC CAPITAL LETTER CHE
+ 0x0428: 0x0098, # CYRILLIC CAPITAL LETTER SHA
+ 0x0429: 0x0099, # CYRILLIC CAPITAL LETTER SHCHA
+ 0x042a: 0x009a, # CYRILLIC CAPITAL LETTER HARD SIGN
+ 0x042b: 0x009b, # CYRILLIC CAPITAL LETTER YERU
+ 0x042c: 0x009c, # CYRILLIC CAPITAL LETTER SOFT SIGN
+ 0x042d: 0x009d, # CYRILLIC CAPITAL LETTER E
+ 0x042e: 0x009e, # CYRILLIC CAPITAL LETTER YU
+ 0x042f: 0x009f, # CYRILLIC CAPITAL LETTER YA
+ 0x0430: 0x00a0, # CYRILLIC SMALL LETTER A
+ 0x0431: 0x00a1, # CYRILLIC SMALL LETTER BE
+ 0x0432: 0x00a2, # CYRILLIC SMALL LETTER VE
+ 0x0433: 0x00a3, # CYRILLIC SMALL LETTER GHE
+ 0x0434: 0x00a4, # CYRILLIC SMALL LETTER DE
+ 0x0435: 0x00a5, # CYRILLIC SMALL LETTER IE
+ 0x0436: 0x00a6, # CYRILLIC SMALL LETTER ZHE
+ 0x0437: 0x00a7, # CYRILLIC SMALL LETTER ZE
+ 0x0438: 0x00a8, # CYRILLIC SMALL LETTER I
+ 0x0439: 0x00a9, # CYRILLIC SMALL LETTER SHORT I
+ 0x043a: 0x00aa, # CYRILLIC SMALL LETTER KA
+ 0x043b: 0x00ab, # CYRILLIC SMALL LETTER EL
+ 0x043c: 0x00ac, # CYRILLIC SMALL LETTER EM
+ 0x043d: 0x00ad, # CYRILLIC SMALL LETTER EN
+ 0x043e: 0x00ae, # CYRILLIC SMALL LETTER O
+ 0x043f: 0x00af, # CYRILLIC SMALL LETTER PE
+ 0x0440: 0x00e0, # CYRILLIC SMALL LETTER ER
+ 0x0441: 0x00e1, # CYRILLIC SMALL LETTER ES
+ 0x0442: 0x00e2, # CYRILLIC SMALL LETTER TE
+ 0x0443: 0x00e3, # CYRILLIC SMALL LETTER U
+ 0x0444: 0x00e4, # CYRILLIC SMALL LETTER EF
+ 0x0445: 0x00e5, # CYRILLIC SMALL LETTER HA
+ 0x0446: 0x00e6, # CYRILLIC SMALL LETTER TSE
+ 0x0447: 0x00e7, # CYRILLIC SMALL LETTER CHE
+ 0x0448: 0x00e8, # CYRILLIC SMALL LETTER SHA
+ 0x0449: 0x00e9, # CYRILLIC SMALL LETTER SHCHA
+ 0x044a: 0x00ea, # CYRILLIC SMALL LETTER HARD SIGN
+ 0x044b: 0x00eb, # CYRILLIC SMALL LETTER YERU
+ 0x044c: 0x00ec, # CYRILLIC SMALL LETTER SOFT SIGN
+ 0x044d: 0x00ed, # CYRILLIC SMALL LETTER E
+ 0x044e: 0x00ee, # CYRILLIC SMALL LETTER YU
+ 0x044f: 0x00ef, # CYRILLIC SMALL LETTER YA
+ 0x0451: 0x00f1, # CYRILLIC SMALL LETTER IO
+ 0x0454: 0x00f5, # CYRILLIC SMALL LETTER UKRAINIAN IE
+ 0x0456: 0x00f7, # CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
+ 0x0457: 0x00f9, # CYRILLIC SMALL LETTER YI
+ 0x0490: 0x00f2, # CYRILLIC CAPITAL LETTER GHE WITH UPTURN
+ 0x0491: 0x00f3, # CYRILLIC SMALL LETTER GHE WITH UPTURN
+ 0x2116: 0x00fc, # NUMERO SIGN
+ 0x221a: 0x00fb, # SQUARE ROOT
+ 0x2500: 0x00c4, # BOX DRAWINGS LIGHT HORIZONTAL
+ 0x2502: 0x00b3, # BOX DRAWINGS LIGHT VERTICAL
+ 0x250c: 0x00da, # BOX DRAWINGS LIGHT DOWN AND RIGHT
+ 0x2510: 0x00bf, # BOX DRAWINGS LIGHT DOWN AND LEFT
+ 0x2514: 0x00c0, # BOX DRAWINGS LIGHT UP AND RIGHT
+ 0x2518: 0x00d9, # BOX DRAWINGS LIGHT UP AND LEFT
+ 0x251c: 0x00c3, # BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+ 0x2524: 0x00b4, # BOX DRAWINGS LIGHT VERTICAL AND LEFT
+ 0x252c: 0x00c2, # BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+ 0x2534: 0x00c1, # BOX DRAWINGS LIGHT UP AND HORIZONTAL
+ 0x253c: 0x00c5, # BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
+ 0x2550: 0x00cd, # BOX DRAWINGS DOUBLE HORIZONTAL
+ 0x2551: 0x00ba, # BOX DRAWINGS DOUBLE VERTICAL
+ 0x2552: 0x00d5, # BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE
+ 0x2553: 0x00d6, # BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE
+ 0x2554: 0x00c9, # BOX DRAWINGS DOUBLE DOWN AND RIGHT
+ 0x2555: 0x00b8, # BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE
+ 0x2556: 0x00b7, # BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE
+ 0x2557: 0x00bb, # BOX DRAWINGS DOUBLE DOWN AND LEFT
+ 0x2558: 0x00d4, # BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
+ 0x2559: 0x00d3, # BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE
+ 0x255a: 0x00c8, # BOX DRAWINGS DOUBLE UP AND RIGHT
+ 0x255b: 0x00be, # BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
+ 0x255c: 0x00bd, # BOX DRAWINGS UP DOUBLE AND LEFT SINGLE
+ 0x255d: 0x00bc, # BOX DRAWINGS DOUBLE UP AND LEFT
+ 0x255e: 0x00c6, # BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
+ 0x255f: 0x00c7, # BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE
+ 0x2560: 0x00cc, # BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
+ 0x2561: 0x00b5, # BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
+ 0x2562: 0x00b6, # BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE
+ 0x2563: 0x00b9, # BOX DRAWINGS DOUBLE VERTICAL AND LEFT
+ 0x2564: 0x00d1, # BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE
+ 0x2565: 0x00d2, # BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE
+ 0x2566: 0x00cb, # BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
+ 0x2567: 0x00cf, # BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
+ 0x2568: 0x00d0, # BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE
+ 0x2569: 0x00ca, # BOX DRAWINGS DOUBLE UP AND HORIZONTAL
+ 0x256a: 0x00d8, # BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
+ 0x256b: 0x00d7, # BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE
+ 0x256c: 0x00ce, # BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
+ 0x2580: 0x00df, # UPPER HALF BLOCK
+ 0x2584: 0x00dc, # LOWER HALF BLOCK
+ 0x2588: 0x00db, # FULL BLOCK
+ 0x258c: 0x00dd, # LEFT HALF BLOCK
+ 0x2590: 0x00de, # RIGHT HALF BLOCK
+ 0x2591: 0x00b0, # LIGHT SHADE
+ 0x2592: 0x00b1, # MEDIUM SHADE
+ 0x2593: 0x00b2, # DARK SHADE
+ 0x25a0: 0x00fe, # BLACK SQUARE
+}
diff --git a/Lib/encodings/cp273.py b/Lib/encodings/cp273.py
new file mode 100644
index 0000000..69c6d77
--- /dev/null
+++ b/Lib/encodings/cp273.py
@@ -0,0 +1,307 @@
+""" Python Character Mapping Codec cp273 generated from 'python-mappings/CP273.TXT' with gencodec.py.
+
+"""#"
+
+import codecs
+
+### Codec APIs
+
+class Codec(codecs.Codec):
+
+ def encode(self,input,errors='strict'):
+ return codecs.charmap_encode(input,errors,encoding_table)
+
+ def decode(self,input,errors='strict'):
+ return codecs.charmap_decode(input,errors,decoding_table)
+
+class IncrementalEncoder(codecs.IncrementalEncoder):
+ def encode(self, input, final=False):
+ return codecs.charmap_encode(input,self.errors,encoding_table)[0]
+
+class IncrementalDecoder(codecs.IncrementalDecoder):
+ def decode(self, input, final=False):
+ return codecs.charmap_decode(input,self.errors,decoding_table)[0]
+
+class StreamWriter(Codec,codecs.StreamWriter):
+ pass
+
+class StreamReader(Codec,codecs.StreamReader):
+ pass
+
+### encodings module API
+
+def getregentry():
+ return codecs.CodecInfo(
+ name='cp273',
+ encode=Codec().encode,
+ decode=Codec().decode,
+ incrementalencoder=IncrementalEncoder,
+ incrementaldecoder=IncrementalDecoder,
+ streamreader=StreamReader,
+ streamwriter=StreamWriter,
+ )
+
+
+### Decoding Table
+
+decoding_table = (
+ '\x00' # 0x00 -> NULL (NUL)
+ '\x01' # 0x01 -> START OF HEADING (SOH)
+ '\x02' # 0x02 -> START OF TEXT (STX)
+ '\x03' # 0x03 -> END OF TEXT (ETX)
+ '\x9c' # 0x04 -> STRING TERMINATOR (ST)
+ '\t' # 0x05 -> CHARACTER TABULATION (HT)
+ '\x86' # 0x06 -> START OF SELECTED AREA (SSA)
+ '\x7f' # 0x07 -> DELETE (DEL)
+ '\x97' # 0x08 -> END OF GUARDED AREA (EPA)
+ '\x8d' # 0x09 -> REVERSE LINE FEED (RI)
+ '\x8e' # 0x0A -> SINGLE-SHIFT TWO (SS2)
+ '\x0b' # 0x0B -> LINE TABULATION (VT)
+ '\x0c' # 0x0C -> FORM FEED (FF)
+ '\r' # 0x0D -> CARRIAGE RETURN (CR)
+ '\x0e' # 0x0E -> SHIFT OUT (SO)
+ '\x0f' # 0x0F -> SHIFT IN (SI)
+ '\x10' # 0x10 -> DATALINK ESCAPE (DLE)
+ '\x11' # 0x11 -> DEVICE CONTROL ONE (DC1)
+ '\x12' # 0x12 -> DEVICE CONTROL TWO (DC2)
+ '\x13' # 0x13 -> DEVICE CONTROL THREE (DC3)
+ '\x9d' # 0x14 -> OPERATING SYSTEM COMMAND (OSC)
+ '\x85' # 0x15 -> NEXT LINE (NEL)
+ '\x08' # 0x16 -> BACKSPACE (BS)
+ '\x87' # 0x17 -> END OF SELECTED AREA (ESA)
+ '\x18' # 0x18 -> CANCEL (CAN)
+ '\x19' # 0x19 -> END OF MEDIUM (EM)
+ '\x92' # 0x1A -> PRIVATE USE TWO (PU2)
+ '\x8f' # 0x1B -> SINGLE-SHIFT THREE (SS3)
+ '\x1c' # 0x1C -> FILE SEPARATOR (IS4)
+ '\x1d' # 0x1D -> GROUP SEPARATOR (IS3)
+ '\x1e' # 0x1E -> RECORD SEPARATOR (IS2)
+ '\x1f' # 0x1F -> UNIT SEPARATOR (IS1)
+ '\x80' # 0x20 -> PADDING CHARACTER (PAD)
+ '\x81' # 0x21 -> HIGH OCTET PRESET (HOP)
+ '\x82' # 0x22 -> BREAK PERMITTED HERE (BPH)
+ '\x83' # 0x23 -> NO BREAK HERE (NBH)
+ '\x84' # 0x24 -> INDEX (IND)
+ '\n' # 0x25 -> LINE FEED (LF)
+ '\x17' # 0x26 -> END OF TRANSMISSION BLOCK (ETB)
+ '\x1b' # 0x27 -> ESCAPE (ESC)
+ '\x88' # 0x28 -> CHARACTER TABULATION SET (HTS)
+ '\x89' # 0x29 -> CHARACTER TABULATION WITH JUSTIFICATION (HTJ)
+ '\x8a' # 0x2A -> LINE TABULATION SET (VTS)
+ '\x8b' # 0x2B -> PARTIAL LINE FORWARD (PLD)
+ '\x8c' # 0x2C -> PARTIAL LINE BACKWARD (PLU)
+ '\x05' # 0x2D -> ENQUIRY (ENQ)
+ '\x06' # 0x2E -> ACKNOWLEDGE (ACK)
+ '\x07' # 0x2F -> BELL (BEL)
+ '\x90' # 0x30 -> DEVICE CONTROL STRING (DCS)
+ '\x91' # 0x31 -> PRIVATE USE ONE (PU1)
+ '\x16' # 0x32 -> SYNCHRONOUS IDLE (SYN)
+ '\x93' # 0x33 -> SET TRANSMIT STATE (STS)
+ '\x94' # 0x34 -> CANCEL CHARACTER (CCH)
+ '\x95' # 0x35 -> MESSAGE WAITING (MW)
+ '\x96' # 0x36 -> START OF GUARDED AREA (SPA)
+ '\x04' # 0x37 -> END OF TRANSMISSION (EOT)
+ '\x98' # 0x38 -> START OF STRING (SOS)
+ '\x99' # 0x39 -> SINGLE GRAPHIC CHARACTER INTRODUCER (SGCI)
+ '\x9a' # 0x3A -> SINGLE CHARACTER INTRODUCER (SCI)
+ '\x9b' # 0x3B -> CONTROL SEQUENCE INTRODUCER (CSI)
+ '\x14' # 0x3C -> DEVICE CONTROL FOUR (DC4)
+ '\x15' # 0x3D -> NEGATIVE ACKNOWLEDGE (NAK)
+ '\x9e' # 0x3E -> PRIVACY MESSAGE (PM)
+ '\x1a' # 0x3F -> SUBSTITUTE (SUB)
+ ' ' # 0x40 -> SPACE
+ '\xa0' # 0x41 -> NO-BREAK SPACE
+ '\xe2' # 0x42 -> LATIN SMALL LETTER A WITH CIRCUMFLEX
+ '{' # 0x43 -> LEFT CURLY BRACKET
+ '\xe0' # 0x44 -> LATIN SMALL LETTER A WITH GRAVE
+ '\xe1' # 0x45 -> LATIN SMALL LETTER A WITH ACUTE
+ '\xe3' # 0x46 -> LATIN SMALL LETTER A WITH TILDE
+ '\xe5' # 0x47 -> LATIN SMALL LETTER A WITH RING ABOVE
+ '\xe7' # 0x48 -> LATIN SMALL LETTER C WITH CEDILLA
+ '\xf1' # 0x49 -> LATIN SMALL LETTER N WITH TILDE
+ '\xc4' # 0x4A -> LATIN CAPITAL LETTER A WITH DIAERESIS
+ '.' # 0x4B -> FULL STOP
+ '<' # 0x4C -> LESS-THAN SIGN
+ '(' # 0x4D -> LEFT PARENTHESIS
+ '+' # 0x4E -> PLUS SIGN
+ '!' # 0x4F -> EXCLAMATION MARK
+ '&' # 0x50 -> AMPERSAND
+ '\xe9' # 0x51 -> LATIN SMALL LETTER E WITH ACUTE
+ '\xea' # 0x52 -> LATIN SMALL LETTER E WITH CIRCUMFLEX
+ '\xeb' # 0x53 -> LATIN SMALL LETTER E WITH DIAERESIS
+ '\xe8' # 0x54 -> LATIN SMALL LETTER E WITH GRAVE
+ '\xed' # 0x55 -> LATIN SMALL LETTER I WITH ACUTE
+ '\xee' # 0x56 -> LATIN SMALL LETTER I WITH CIRCUMFLEX
+ '\xef' # 0x57 -> LATIN SMALL LETTER I WITH DIAERESIS
+ '\xec' # 0x58 -> LATIN SMALL LETTER I WITH GRAVE
+ '~' # 0x59 -> TILDE
+ '\xdc' # 0x5A -> LATIN CAPITAL LETTER U WITH DIAERESIS
+ '$' # 0x5B -> DOLLAR SIGN
+ '*' # 0x5C -> ASTERISK
+ ')' # 0x5D -> RIGHT PARENTHESIS
+ ';' # 0x5E -> SEMICOLON
+ '^' # 0x5F -> CIRCUMFLEX ACCENT
+ '-' # 0x60 -> HYPHEN-MINUS
+ '/' # 0x61 -> SOLIDUS
+ '\xc2' # 0x62 -> LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+ '[' # 0x63 -> LEFT SQUARE BRACKET
+ '\xc0' # 0x64 -> LATIN CAPITAL LETTER A WITH GRAVE
+ '\xc1' # 0x65 -> LATIN CAPITAL LETTER A WITH ACUTE
+ '\xc3' # 0x66 -> LATIN CAPITAL LETTER A WITH TILDE
+ '\xc5' # 0x67 -> LATIN CAPITAL LETTER A WITH RING ABOVE
+ '\xc7' # 0x68 -> LATIN CAPITAL LETTER C WITH CEDILLA
+ '\xd1' # 0x69 -> LATIN CAPITAL LETTER N WITH TILDE
+ '\xf6' # 0x6A -> LATIN SMALL LETTER O WITH DIAERESIS
+ ',' # 0x6B -> COMMA
+ '%' # 0x6C -> PERCENT SIGN
+ '_' # 0x6D -> LOW LINE
+ '>' # 0x6E -> GREATER-THAN SIGN
+ '?' # 0x6F -> QUESTION MARK
+ '\xf8' # 0x70 -> LATIN SMALL LETTER O WITH STROKE
+ '\xc9' # 0x71 -> LATIN CAPITAL LETTER E WITH ACUTE
+ '\xca' # 0x72 -> LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+ '\xcb' # 0x73 -> LATIN CAPITAL LETTER E WITH DIAERESIS
+ '\xc8' # 0x74 -> LATIN CAPITAL LETTER E WITH GRAVE
+ '\xcd' # 0x75 -> LATIN CAPITAL LETTER I WITH ACUTE
+ '\xce' # 0x76 -> LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+ '\xcf' # 0x77 -> LATIN CAPITAL LETTER I WITH DIAERESIS
+ '\xcc' # 0x78 -> LATIN CAPITAL LETTER I WITH GRAVE
+ '`' # 0x79 -> GRAVE ACCENT
+ ':' # 0x7A -> COLON
+ '#' # 0x7B -> NUMBER SIGN
+ '\xa7' # 0x7C -> SECTION SIGN
+ "'" # 0x7D -> APOSTROPHE
+ '=' # 0x7E -> EQUALS SIGN
+ '"' # 0x7F -> QUOTATION MARK
+ '\xd8' # 0x80 -> LATIN CAPITAL LETTER O WITH STROKE
+ 'a' # 0x81 -> LATIN SMALL LETTER A
+ 'b' # 0x82 -> LATIN SMALL LETTER B
+ 'c' # 0x83 -> LATIN SMALL LETTER C
+ 'd' # 0x84 -> LATIN SMALL LETTER D
+ 'e' # 0x85 -> LATIN SMALL LETTER E
+ 'f' # 0x86 -> LATIN SMALL LETTER F
+ 'g' # 0x87 -> LATIN SMALL LETTER G
+ 'h' # 0x88 -> LATIN SMALL LETTER H
+ 'i' # 0x89 -> LATIN SMALL LETTER I
+ '\xab' # 0x8A -> LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+ '\xbb' # 0x8B -> RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+ '\xf0' # 0x8C -> LATIN SMALL LETTER ETH (Icelandic)
+ '\xfd' # 0x8D -> LATIN SMALL LETTER Y WITH ACUTE
+ '\xfe' # 0x8E -> LATIN SMALL LETTER THORN (Icelandic)
+ '\xb1' # 0x8F -> PLUS-MINUS SIGN
+ '\xb0' # 0x90 -> DEGREE SIGN
+ 'j' # 0x91 -> LATIN SMALL LETTER J
+ 'k' # 0x92 -> LATIN SMALL LETTER K
+ 'l' # 0x93 -> LATIN SMALL LETTER L
+ 'm' # 0x94 -> LATIN SMALL LETTER M
+ 'n' # 0x95 -> LATIN SMALL LETTER N
+ 'o' # 0x96 -> LATIN SMALL LETTER O
+ 'p' # 0x97 -> LATIN SMALL LETTER P
+ 'q' # 0x98 -> LATIN SMALL LETTER Q
+ 'r' # 0x99 -> LATIN SMALL LETTER R
+ '\xaa' # 0x9A -> FEMININE ORDINAL INDICATOR
+ '\xba' # 0x9B -> MASCULINE ORDINAL INDICATOR
+ '\xe6' # 0x9C -> LATIN SMALL LETTER AE
+ '\xb8' # 0x9D -> CEDILLA
+ '\xc6' # 0x9E -> LATIN CAPITAL LETTER AE
+ '\xa4' # 0x9F -> CURRENCY SIGN
+ '\xb5' # 0xA0 -> MICRO SIGN
+ '\xdf' # 0xA1 -> LATIN SMALL LETTER SHARP S (German)
+ 's' # 0xA2 -> LATIN SMALL LETTER S
+ 't' # 0xA3 -> LATIN SMALL LETTER T
+ 'u' # 0xA4 -> LATIN SMALL LETTER U
+ 'v' # 0xA5 -> LATIN SMALL LETTER V
+ 'w' # 0xA6 -> LATIN SMALL LETTER W
+ 'x' # 0xA7 -> LATIN SMALL LETTER X
+ 'y' # 0xA8 -> LATIN SMALL LETTER Y
+ 'z' # 0xA9 -> LATIN SMALL LETTER Z
+ '\xa1' # 0xAA -> INVERTED EXCLAMATION MARK
+ '\xbf' # 0xAB -> INVERTED QUESTION MARK
+ '\xd0' # 0xAC -> LATIN CAPITAL LETTER ETH (Icelandic)
+ '\xdd' # 0xAD -> LATIN CAPITAL LETTER Y WITH ACUTE
+ '\xde' # 0xAE -> LATIN CAPITAL LETTER THORN (Icelandic)
+ '\xae' # 0xAF -> REGISTERED SIGN
+ '\xa2' # 0xB0 -> CENT SIGN
+ '\xa3' # 0xB1 -> POUND SIGN
+ '\xa5' # 0xB2 -> YEN SIGN
+ '\xb7' # 0xB3 -> MIDDLE DOT
+ '\xa9' # 0xB4 -> COPYRIGHT SIGN
+ '@' # 0xB5 -> COMMERCIAL AT
+ '\xb6' # 0xB6 -> PILCROW SIGN
+ '\xbc' # 0xB7 -> VULGAR FRACTION ONE QUARTER
+ '\xbd' # 0xB8 -> VULGAR FRACTION ONE HALF
+ '\xbe' # 0xB9 -> VULGAR FRACTION THREE QUARTERS
+ '\xac' # 0xBA -> NOT SIGN
+ '|' # 0xBB -> VERTICAL LINE
+ '\u203e' # 0xBC -> OVERLINE
+ '\xa8' # 0xBD -> DIAERESIS
+ '\xb4' # 0xBE -> ACUTE ACCENT
+ '\xd7' # 0xBF -> MULTIPLICATION SIGN
+ '\xe4' # 0xC0 -> LATIN SMALL LETTER A WITH DIAERESIS
+ 'A' # 0xC1 -> LATIN CAPITAL LETTER A
+ 'B' # 0xC2 -> LATIN CAPITAL LETTER B
+ 'C' # 0xC3 -> LATIN CAPITAL LETTER C
+ 'D' # 0xC4 -> LATIN CAPITAL LETTER D
+ 'E' # 0xC5 -> LATIN CAPITAL LETTER E
+ 'F' # 0xC6 -> LATIN CAPITAL LETTER F
+ 'G' # 0xC7 -> LATIN CAPITAL LETTER G
+ 'H' # 0xC8 -> LATIN CAPITAL LETTER H
+ 'I' # 0xC9 -> LATIN CAPITAL LETTER I
+ '\xad' # 0xCA -> SOFT HYPHEN
+ '\xf4' # 0xCB -> LATIN SMALL LETTER O WITH CIRCUMFLEX
+ '\xa6' # 0xCC -> BROKEN BAR
+ '\xf2' # 0xCD -> LATIN SMALL LETTER O WITH GRAVE
+ '\xf3' # 0xCE -> LATIN SMALL LETTER O WITH ACUTE
+ '\xf5' # 0xCF -> LATIN SMALL LETTER O WITH TILDE
+ '\xfc' # 0xD0 -> LATIN SMALL LETTER U WITH DIAERESIS
+ 'J' # 0xD1 -> LATIN CAPITAL LETTER J
+ 'K' # 0xD2 -> LATIN CAPITAL LETTER K
+ 'L' # 0xD3 -> LATIN CAPITAL LETTER L
+ 'M' # 0xD4 -> LATIN CAPITAL LETTER M
+ 'N' # 0xD5 -> LATIN CAPITAL LETTER N
+ 'O' # 0xD6 -> LATIN CAPITAL LETTER O
+ 'P' # 0xD7 -> LATIN CAPITAL LETTER P
+ 'Q' # 0xD8 -> LATIN CAPITAL LETTER Q
+ 'R' # 0xD9 -> LATIN CAPITAL LETTER R
+ '\xb9' # 0xDA -> SUPERSCRIPT ONE
+ '\xfb' # 0xDB -> LATIN SMALL LETTER U WITH CIRCUMFLEX
+ '}' # 0xDC -> RIGHT CURLY BRACKET
+ '\xf9' # 0xDD -> LATIN SMALL LETTER U WITH GRAVE
+ '\xfa' # 0xDE -> LATIN SMALL LETTER U WITH ACUTE
+ '\xff' # 0xDF -> LATIN SMALL LETTER Y WITH DIAERESIS
+ '\xd6' # 0xE0 -> LATIN CAPITAL LETTER O WITH DIAERESIS
+ '\xf7' # 0xE1 -> DIVISION SIGN
+ 'S' # 0xE2 -> LATIN CAPITAL LETTER S
+ 'T' # 0xE3 -> LATIN CAPITAL LETTER T
+ 'U' # 0xE4 -> LATIN CAPITAL LETTER U
+ 'V' # 0xE5 -> LATIN CAPITAL LETTER V
+ 'W' # 0xE6 -> LATIN CAPITAL LETTER W
+ 'X' # 0xE7 -> LATIN CAPITAL LETTER X
+ 'Y' # 0xE8 -> LATIN CAPITAL LETTER Y
+ 'Z' # 0xE9 -> LATIN CAPITAL LETTER Z
+ '\xb2' # 0xEA -> SUPERSCRIPT TWO
+ '\xd4' # 0xEB -> LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+ '\\' # 0xEC -> REVERSE SOLIDUS
+ '\xd2' # 0xED -> LATIN CAPITAL LETTER O WITH GRAVE
+ '\xd3' # 0xEE -> LATIN CAPITAL LETTER O WITH ACUTE
+ '\xd5' # 0xEF -> LATIN CAPITAL LETTER O WITH TILDE
+ '0' # 0xF0 -> DIGIT ZERO
+ '1' # 0xF1 -> DIGIT ONE
+ '2' # 0xF2 -> DIGIT TWO
+ '3' # 0xF3 -> DIGIT THREE
+ '4' # 0xF4 -> DIGIT FOUR
+ '5' # 0xF5 -> DIGIT FIVE
+ '6' # 0xF6 -> DIGIT SIX
+ '7' # 0xF7 -> DIGIT SEVEN
+ '8' # 0xF8 -> DIGIT EIGHT
+ '9' # 0xF9 -> DIGIT NINE
+ '\xb3' # 0xFA -> SUPERSCRIPT THREE
+ '\xdb' # 0xFB -> LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+ ']' # 0xFC -> RIGHT SQUARE BRACKET
+ '\xd9' # 0xFD -> LATIN CAPITAL LETTER U WITH GRAVE
+ '\xda' # 0xFE -> LATIN CAPITAL LETTER U WITH ACUTE
+ '\x9f' # 0xFF -> APPLICATION PROGRAM COMMAND (APC)
+)
+
+### Encoding table
+encoding_table=codecs.charmap_build(decoding_table)
diff --git a/Lib/encodings/cp500.py b/Lib/encodings/cp500.py
index a975be7..5f61535 100644
--- a/Lib/encodings/cp500.py
+++ b/Lib/encodings/cp500.py
@@ -301,7 +301,6 @@ decoding_table = (
'\xd9' # 0xFD -> LATIN CAPITAL LETTER U WITH GRAVE
'\xda' # 0xFE -> LATIN CAPITAL LETTER U WITH ACUTE
'\x9f' # 0xFF -> CONTROL
- '\ufffe' ## Widen to UCS2 for optimization
)
### Encoding table
diff --git a/Lib/encodings/iso8859_1.py b/Lib/encodings/iso8859_1.py
index d9cc516..8cfc01f 100644
--- a/Lib/encodings/iso8859_1.py
+++ b/Lib/encodings/iso8859_1.py
@@ -301,7 +301,6 @@ decoding_table = (
'\xfd' # 0xFD -> LATIN SMALL LETTER Y WITH ACUTE
'\xfe' # 0xFE -> LATIN SMALL LETTER THORN (Icelandic)
'\xff' # 0xFF -> LATIN SMALL LETTER Y WITH DIAERESIS
- '\ufffe' ## Widen to UCS2 for optimization
)
### Encoding table
diff --git a/Lib/encodings/rot_13.py b/Lib/encodings/rot_13.py
index 1f2f47b..f0b4186 100755
--- a/Lib/encodings/rot_13.py
+++ b/Lib/encodings/rot_13.py
@@ -106,7 +106,7 @@ rot13_map.update({
### Filter API
def rot13(infile, outfile):
- outfile.write(infile.read().encode('rot-13'))
+ outfile.write(codecs.encode(infile.read(), 'rot-13'))
if __name__ == '__main__':
import sys
diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py
new file mode 100644
index 0000000..7cf6a4b
--- /dev/null
+++ b/Lib/ensurepip/__init__.py
@@ -0,0 +1,210 @@
+import os
+import os.path
+import pkgutil
+import sys
+import tempfile
+
+
+__all__ = ["version", "bootstrap"]
+
+
+_SETUPTOOLS_VERSION = "2.1"
+
+_PIP_VERSION = "1.5.4"
+
+# pip currently requires ssl support, so we try to provide a nicer
+# error message when that is missing (http://bugs.python.org/issue19744)
+_MISSING_SSL_MESSAGE = ("pip {} requires SSL/TLS".format(_PIP_VERSION))
+try:
+ import ssl
+except ImportError:
+ ssl = None
+ def _require_ssl_for_pip():
+ raise RuntimeError(_MISSING_SSL_MESSAGE)
+else:
+ def _require_ssl_for_pip():
+ pass
+
+_PROJECTS = [
+ ("setuptools", _SETUPTOOLS_VERSION),
+ ("pip", _PIP_VERSION),
+]
+
+
+def _run_pip(args, additional_paths=None):
+ # Add our bundled software to the sys.path so we can import it
+ if additional_paths is not None:
+ sys.path = additional_paths + sys.path
+
+ # Install the bundled software
+ import pip
+ pip.main(args)
+
+
+def version():
+ """
+ Returns a string specifying the bundled version of pip.
+ """
+ return _PIP_VERSION
+
+def _disable_pip_configuration_settings():
+ # We deliberately ignore all pip environment variables
+ # when invoking pip
+ # See http://bugs.python.org/issue19734 for details
+ keys_to_remove = [k for k in os.environ if k.startswith("PIP_")]
+ for k in keys_to_remove:
+ del os.environ[k]
+ # We also ignore the settings in the default pip configuration file
+ # See http://bugs.python.org/issue20053 for details
+ os.environ['PIP_CONFIG_FILE'] = os.devnull
+
+
+def bootstrap(*, root=None, upgrade=False, user=False,
+ altinstall=False, default_pip=False,
+ verbosity=0):
+ """
+ Bootstrap pip into the current Python installation (or the given root
+ directory).
+
+ Note that calling this function will alter both sys.path and os.environ.
+ """
+ if altinstall and default_pip:
+ raise ValueError("Cannot use altinstall and default_pip together")
+
+ _require_ssl_for_pip()
+ _disable_pip_configuration_settings()
+
+ # By default, installing pip and setuptools installs all of the
+ # following scripts (X.Y == running Python version):
+ #
+ # pip, pipX, pipX.Y, easy_install, easy_install-X.Y
+ #
+ # pip 1.5+ allows ensurepip to request that some of those be left out
+ if altinstall:
+ # omit pip, pipX and easy_install
+ os.environ["ENSUREPIP_OPTIONS"] = "altinstall"
+ elif not default_pip:
+ # omit pip and easy_install
+ os.environ["ENSUREPIP_OPTIONS"] = "install"
+
+ with tempfile.TemporaryDirectory() as tmpdir:
+ # Put our bundled wheels into a temporary directory and construct the
+ # additional paths that need added to sys.path
+ additional_paths = []
+ for project, version in _PROJECTS:
+ wheel_name = "{}-{}-py2.py3-none-any.whl".format(project, version)
+ whl = pkgutil.get_data(
+ "ensurepip",
+ "_bundled/{}".format(wheel_name),
+ )
+ with open(os.path.join(tmpdir, wheel_name), "wb") as fp:
+ fp.write(whl)
+
+ additional_paths.append(os.path.join(tmpdir, wheel_name))
+
+ # Construct the arguments to be passed to the pip command
+ args = ["install", "--no-index", "--find-links", tmpdir]
+ if root:
+ args += ["--root", root]
+ if upgrade:
+ args += ["--upgrade"]
+ if user:
+ args += ["--user"]
+ if verbosity:
+ args += ["-" + "v" * verbosity]
+
+ _run_pip(args + [p[0] for p in _PROJECTS], additional_paths)
+
+def _uninstall_helper(*, verbosity=0):
+ """Helper to support a clean default uninstall process on Windows
+
+ Note that calling this function may alter os.environ.
+ """
+ # Nothing to do if pip was never installed, or has been removed
+ try:
+ import pip
+ except ImportError:
+ return
+
+ # If the pip version doesn't match the bundled one, leave it alone
+ if pip.__version__ != _PIP_VERSION:
+ msg = ("ensurepip will only uninstall a matching version "
+ "({!r} installed, {!r} bundled)")
+ print(msg.format(pip.__version__, _PIP_VERSION), file=sys.stderr)
+ return
+
+ _require_ssl_for_pip()
+ _disable_pip_configuration_settings()
+
+ # Construct the arguments to be passed to the pip command
+ args = ["uninstall", "-y"]
+ if verbosity:
+ args += ["-" + "v" * verbosity]
+
+ _run_pip(args + [p[0] for p in reversed(_PROJECTS)])
+
+
+def _main(argv=None):
+ if ssl is None:
+ print("Ignoring ensurepip failure: {}".format(_MISSING_SSL_MESSAGE),
+ file=sys.stderr)
+ return
+
+ import argparse
+ parser = argparse.ArgumentParser(prog="python -m ensurepip")
+ parser.add_argument(
+ "--version",
+ action="version",
+ version="pip {}".format(version()),
+ help="Show the version of pip that is bundled with this Python.",
+ )
+ parser.add_argument(
+ "-v", "--verbose",
+ action="count",
+ default=0,
+ dest="verbosity",
+ help=("Give more output. Option is additive, and can be used up to 3 "
+ "times."),
+ )
+ parser.add_argument(
+ "-U", "--upgrade",
+ action="store_true",
+ default=False,
+ help="Upgrade pip and dependencies, even if already installed.",
+ )
+ parser.add_argument(
+ "--user",
+ action="store_true",
+ default=False,
+ help="Install using the user scheme.",
+ )
+ parser.add_argument(
+ "--root",
+ default=None,
+ help="Install everything relative to this alternate root directory.",
+ )
+ parser.add_argument(
+ "--altinstall",
+ action="store_true",
+ default=False,
+ help=("Make an alternate install, installing only the X.Y versioned"
+ "scripts (Default: pipX, pipX.Y, easy_install-X.Y)"),
+ )
+ parser.add_argument(
+ "--default-pip",
+ action="store_true",
+ default=False,
+ help=("Make a default pip install, installing the unqualified pip "
+ "and easy_install in addition to the versioned scripts"),
+ )
+
+ args = parser.parse_args(argv)
+
+ bootstrap(
+ root=args.root,
+ upgrade=args.upgrade,
+ user=args.user,
+ verbosity=args.verbosity,
+ altinstall=args.altinstall,
+ default_pip=args.default_pip,
+ )
diff --git a/Lib/ensurepip/__main__.py b/Lib/ensurepip/__main__.py
new file mode 100644
index 0000000..77527d7
--- /dev/null
+++ b/Lib/ensurepip/__main__.py
@@ -0,0 +1,4 @@
+import ensurepip
+
+if __name__ == "__main__":
+ ensurepip._main()
diff --git a/Lib/ensurepip/_bundled/pip-1.5.4-py2.py3-none-any.whl b/Lib/ensurepip/_bundled/pip-1.5.4-py2.py3-none-any.whl
new file mode 100644
index 0000000..e07d476
--- /dev/null
+++ b/Lib/ensurepip/_bundled/pip-1.5.4-py2.py3-none-any.whl
Binary files differ
diff --git a/Lib/ensurepip/_bundled/setuptools-2.1-py2.py3-none-any.whl b/Lib/ensurepip/_bundled/setuptools-2.1-py2.py3-none-any.whl
new file mode 100644
index 0000000..ed77b59
--- /dev/null
+++ b/Lib/ensurepip/_bundled/setuptools-2.1-py2.py3-none-any.whl
Binary files differ
diff --git a/Lib/ensurepip/_uninstall.py b/Lib/ensurepip/_uninstall.py
new file mode 100644
index 0000000..750365e
--- /dev/null
+++ b/Lib/ensurepip/_uninstall.py
@@ -0,0 +1,30 @@
+"""Basic pip uninstallation support, helper for the Windows uninstaller"""
+
+import argparse
+import ensurepip
+
+
+def _main(argv=None):
+ parser = argparse.ArgumentParser(prog="python -m ensurepip._uninstall")
+ parser.add_argument(
+ "--version",
+ action="version",
+ version="pip {}".format(ensurepip.version()),
+ help="Show the version of pip this will attempt to uninstall.",
+ )
+ parser.add_argument(
+ "-v", "--verbose",
+ action="count",
+ default=0,
+ dest="verbosity",
+ help=("Give more output. Option is additive, and can be used up to 3 "
+ "times."),
+ )
+
+ args = parser.parse_args(argv)
+
+ ensurepip._uninstall_helper(verbosity=args.verbosity)
+
+
+if __name__ == "__main__":
+ _main()
diff --git a/Lib/enum.py b/Lib/enum.py
new file mode 100644
index 0000000..844a956
--- /dev/null
+++ b/Lib/enum.py
@@ -0,0 +1,525 @@
+import sys
+from collections import OrderedDict
+from types import MappingProxyType, DynamicClassAttribute
+
+__all__ = ['Enum', 'IntEnum', 'unique']
+
+
+def _is_descriptor(obj):
+ """Returns True if obj is a descriptor, False otherwise."""
+ return (
+ hasattr(obj, '__get__') or
+ hasattr(obj, '__set__') or
+ hasattr(obj, '__delete__'))
+
+
+def _is_dunder(name):
+ """Returns True if a __dunder__ name, False otherwise."""
+ return (name[:2] == name[-2:] == '__' and
+ name[2:3] != '_' and
+ name[-3:-2] != '_' and
+ len(name) > 4)
+
+
+def _is_sunder(name):
+ """Returns True if a _sunder_ name, False otherwise."""
+ return (name[0] == name[-1] == '_' and
+ name[1:2] != '_' and
+ name[-2:-1] != '_' and
+ len(name) > 2)
+
+
+def _make_class_unpicklable(cls):
+ """Make the given class un-picklable."""
+ def _break_on_call_reduce(self, proto):
+ raise TypeError('%r cannot be pickled' % self)
+ cls.__reduce_ex__ = _break_on_call_reduce
+ cls.__module__ = '<unknown>'
+
+
+class _EnumDict(dict):
+ """Track enum member order and ensure member names are not reused.
+
+ EnumMeta will use the names found in self._member_names as the
+ enumeration member names.
+
+ """
+ def __init__(self):
+ super().__init__()
+ self._member_names = []
+
+ def __setitem__(self, key, value):
+ """Changes anything not dundered or not a descriptor.
+
+ If an enum member name is used twice, an error is raised; duplicate
+ values are not checked for.
+
+ Single underscore (sunder) names are reserved.
+
+ """
+ if _is_sunder(key):
+ raise ValueError('_names_ are reserved for future Enum use')
+ elif _is_dunder(key):
+ pass
+ elif key in self._member_names:
+ # descriptor overwriting an enum?
+ raise TypeError('Attempted to reuse key: %r' % key)
+ elif not _is_descriptor(value):
+ if key in self:
+ # enum overwriting a descriptor?
+ raise TypeError('Key already defined as: %r' % self[key])
+ self._member_names.append(key)
+ super().__setitem__(key, value)
+
+
+
+# Dummy value for Enum as EnumMeta explicitly checks for it, but of course
+# until EnumMeta finishes running the first time the Enum class doesn't exist.
+# This is also why there are checks in EnumMeta like `if Enum is not None`
+Enum = None
+
+
+class EnumMeta(type):
+ """Metaclass for Enum"""
+ @classmethod
+ def __prepare__(metacls, cls, bases):
+ return _EnumDict()
+
+ def __new__(metacls, cls, bases, classdict):
+ # an Enum class is final once enumeration items have been defined; it
+ # cannot be mixed with other types (int, float, etc.) if it has an
+ # inherited __new__ unless a new __new__ is defined (or the resulting
+ # class will fail).
+ member_type, first_enum = metacls._get_mixins_(bases)
+ __new__, save_new, use_args = metacls._find_new_(classdict, member_type,
+ first_enum)
+
+ # save enum items into separate mapping so they don't get baked into
+ # the new class
+ members = {k: classdict[k] for k in classdict._member_names}
+ for name in classdict._member_names:
+ del classdict[name]
+
+ # check for illegal enum names (any others?)
+ invalid_names = set(members) & {'mro', }
+ if invalid_names:
+ raise ValueError('Invalid enum member name: {0}'.format(
+ ','.join(invalid_names)))
+
+ # create our new Enum type
+ enum_class = super().__new__(metacls, cls, bases, classdict)
+ enum_class._member_names_ = [] # names in definition order
+ enum_class._member_map_ = OrderedDict() # name->value map
+ enum_class._member_type_ = member_type
+
+ # Reverse value->name map for hashable values.
+ enum_class._value2member_map_ = {}
+
+ # If a custom type is mixed into the Enum, and it does not know how
+ # to pickle itself, pickle.dumps will succeed but pickle.loads will
+ # fail. Rather than have the error show up later and possibly far
+ # from the source, sabotage the pickle protocol for this class so
+ # that pickle.dumps also fails.
+ #
+ # However, if the new class implements its own __reduce_ex__, do not
+ # sabotage -- it's on them to make sure it works correctly. We use
+ # __reduce_ex__ instead of any of the others as it is preferred by
+ # pickle over __reduce__, and it handles all pickle protocols.
+ if '__reduce_ex__' not in classdict:
+ if member_type is not object:
+ methods = ('__getnewargs_ex__', '__getnewargs__',
+ '__reduce_ex__', '__reduce__')
+ if not any(m in member_type.__dict__ for m in methods):
+ _make_class_unpicklable(enum_class)
+
+ # instantiate them, checking for duplicates as we go
+ # we instantiate first instead of checking for duplicates first in case
+ # a custom __new__ is doing something funky with the values -- such as
+ # auto-numbering ;)
+ for member_name in classdict._member_names:
+ value = members[member_name]
+ if not isinstance(value, tuple):
+ args = (value, )
+ else:
+ args = value
+ if member_type is tuple: # special case for tuple enums
+ args = (args, ) # wrap it one more time
+ if not use_args:
+ enum_member = __new__(enum_class)
+ if not hasattr(enum_member, '_value_'):
+ enum_member._value_ = value
+ else:
+ enum_member = __new__(enum_class, *args)
+ if not hasattr(enum_member, '_value_'):
+ enum_member._value_ = member_type(*args)
+ value = enum_member._value_
+ enum_member._name_ = member_name
+ enum_member.__objclass__ = enum_class
+ enum_member.__init__(*args)
+ # If another member with the same value was already defined, the
+ # new member becomes an alias to the existing one.
+ for name, canonical_member in enum_class._member_map_.items():
+ if canonical_member.value == enum_member._value_:
+ enum_member = canonical_member
+ break
+ else:
+ # Aliases don't appear in member names (only in __members__).
+ enum_class._member_names_.append(member_name)
+ enum_class._member_map_[member_name] = enum_member
+ try:
+ # This may fail if value is not hashable. We can't add the value
+ # to the map, and by-value lookups for this value will be
+ # linear.
+ enum_class._value2member_map_[value] = enum_member
+ except TypeError:
+ pass
+
+ # double check that repr and friends are not the mixin's or various
+ # things break (such as pickle)
+ for name in ('__repr__', '__str__', '__format__', '__reduce_ex__'):
+ class_method = getattr(enum_class, name)
+ obj_method = getattr(member_type, name, None)
+ enum_method = getattr(first_enum, name, None)
+ if obj_method is not None and obj_method is class_method:
+ setattr(enum_class, name, enum_method)
+
+ # replace any other __new__ with our own (as long as Enum is not None,
+ # anyway) -- again, this is to support pickle
+ if Enum is not None:
+ # if the user defined their own __new__, save it before it gets
+ # clobbered in case they subclass later
+ if save_new:
+ enum_class.__new_member__ = __new__
+ enum_class.__new__ = Enum.__new__
+ return enum_class
+
+ def __call__(cls, value, names=None, *, module=None, qualname=None, type=None):
+ """Either returns an existing member, or creates a new enum class.
+
+ This method is used both when an enum class is given a value to match
+ to an enumeration member (i.e. Color(3)) and for the functional API
+ (i.e. Color = Enum('Color', names='red green blue')).
+
+ When used for the functional API:
+
+ `value` will be the name of the new class.
+
+ `names` should be either a string of white-space/comma delimited names
+ (values will start at 1), or an iterator/mapping of name, value pairs.
+
+ `module` should be set to the module this class is being created in;
+ if it is not set, an attempt to find that module will be made, but if
+ it fails the class will not be picklable.
+
+ `qualname` should be set to the actual location this class can be found
+ at in its module; by default it is set to the global scope. If this is
+ not correct, unpickling will fail in some circumstances.
+
+ `type`, if set, will be mixed in as the first base class.
+
+ """
+ if names is None: # simple value lookup
+ return cls.__new__(cls, value)
+ # otherwise, functional API: we're creating a new Enum type
+ return cls._create_(value, names, module=module, qualname=qualname, type=type)
+
+ def __contains__(cls, member):
+ return isinstance(member, cls) and member.name in cls._member_map_
+
+ def __delattr__(cls, attr):
+ # nicer error message when someone tries to delete an attribute
+ # (see issue19025).
+ if attr in cls._member_map_:
+ raise AttributeError(
+ "%s: cannot delete Enum member." % cls.__name__)
+ super().__delattr__(attr)
+
+ def __dir__(self):
+ return (['__class__', '__doc__', '__members__', '__module__'] +
+ self._member_names_)
+
+ def __getattr__(cls, name):
+ """Return the enum member matching `name`
+
+ We use __getattr__ instead of descriptors or inserting into the enum
+ class' __dict__ in order to support `name` and `value` being both
+ properties for enum members (which live in the class' __dict__) and
+ enum members themselves.
+
+ """
+ if _is_dunder(name):
+ raise AttributeError(name)
+ try:
+ return cls._member_map_[name]
+ except KeyError:
+ raise AttributeError(name) from None
+
+ def __getitem__(cls, name):
+ return cls._member_map_[name]
+
+ def __iter__(cls):
+ return (cls._member_map_[name] for name in cls._member_names_)
+
+ def __len__(cls):
+ return len(cls._member_names_)
+
+ @property
+ def __members__(cls):
+ """Returns a mapping of member name->value.
+
+ This mapping lists all enum members, including aliases. Note that this
+ is a read-only view of the internal mapping.
+
+ """
+ return MappingProxyType(cls._member_map_)
+
+ def __repr__(cls):
+ return "<enum %r>" % cls.__name__
+
+ def __reversed__(cls):
+ return (cls._member_map_[name] for name in reversed(cls._member_names_))
+
+ def __setattr__(cls, name, value):
+ """Block attempts to reassign Enum members.
+
+ A simple assignment to the class namespace only changes one of the
+ several possible ways to get an Enum member from the Enum class,
+ resulting in an inconsistent Enumeration.
+
+ """
+ member_map = cls.__dict__.get('_member_map_', {})
+ if name in member_map:
+ raise AttributeError('Cannot reassign members.')
+ super().__setattr__(name, value)
+
+ def _create_(cls, class_name, names=None, *, module=None, qualname=None, type=None):
+ """Convenience method to create a new Enum class.
+
+ `names` can be:
+
+ * A string containing member names, separated either with spaces or
+ commas. Values are auto-numbered from 1.
+ * An iterable of member names. Values are auto-numbered from 1.
+ * An iterable of (member name, value) pairs.
+ * A mapping of member name -> value.
+
+ """
+ metacls = cls.__class__
+ bases = (cls, ) if type is None else (type, cls)
+ classdict = metacls.__prepare__(class_name, bases)
+
+ # special processing needed for names?
+ if isinstance(names, str):
+ names = names.replace(',', ' ').split()
+ if isinstance(names, (tuple, list)) and isinstance(names[0], str):
+ names = [(e, i) for (i, e) in enumerate(names, 1)]
+
+ # Here, names is either an iterable of (name, value) or a mapping.
+ for item in names:
+ if isinstance(item, str):
+ member_name, member_value = item, names[item]
+ else:
+ member_name, member_value = item
+ classdict[member_name] = member_value
+ enum_class = metacls.__new__(metacls, class_name, bases, classdict)
+
+ # TODO: replace the frame hack if a blessed way to know the calling
+ # module is ever developed
+ if module is None:
+ try:
+ module = sys._getframe(2).f_globals['__name__']
+ except (AttributeError, ValueError) as exc:
+ pass
+ if module is None:
+ _make_class_unpicklable(enum_class)
+ else:
+ enum_class.__module__ = module
+ if qualname is not None:
+ enum_class.__qualname__ = qualname
+
+ return enum_class
+
+ @staticmethod
+ def _get_mixins_(bases):
+ """Returns the type for creating enum members, and the first inherited
+ enum class.
+
+ bases: the tuple of bases that was given to __new__
+
+ """
+ if not bases:
+ return object, Enum
+
+ # double check that we are not subclassing a class with existing
+ # enumeration members; while we're at it, see if any other data
+ # type has been mixed in so we can use the correct __new__
+ member_type = first_enum = None
+ for base in bases:
+ if (base is not Enum and
+ issubclass(base, Enum) and
+ base._member_names_):
+ raise TypeError("Cannot extend enumerations")
+ # base is now the last base in bases
+ if not issubclass(base, Enum):
+ raise TypeError("new enumerations must be created as "
+ "`ClassName([mixin_type,] enum_type)`")
+
+ # get correct mix-in type (either mix-in type of Enum subclass, or
+ # first base if last base is Enum)
+ if not issubclass(bases[0], Enum):
+ member_type = bases[0] # first data type
+ first_enum = bases[-1] # enum type
+ else:
+ for base in bases[0].__mro__:
+ # most common: (IntEnum, int, Enum, object)
+ # possible: (<Enum 'AutoIntEnum'>, <Enum 'IntEnum'>,
+ # <class 'int'>, <Enum 'Enum'>,
+ # <class 'object'>)
+ if issubclass(base, Enum):
+ if first_enum is None:
+ first_enum = base
+ else:
+ if member_type is None:
+ member_type = base
+
+ return member_type, first_enum
+
+ @staticmethod
+ def _find_new_(classdict, member_type, first_enum):
+ """Returns the __new__ to be used for creating the enum members.
+
+ classdict: the class dictionary given to __new__
+ member_type: the data type whose __new__ will be used by default
+ first_enum: enumeration to check for an overriding __new__
+
+ """
+ # now find the correct __new__, checking to see of one was defined
+ # by the user; also check earlier enum classes in case a __new__ was
+ # saved as __new_member__
+ __new__ = classdict.get('__new__', None)
+
+ # should __new__ be saved as __new_member__ later?
+ save_new = __new__ is not None
+
+ if __new__ is None:
+ # check all possibles for __new_member__ before falling back to
+ # __new__
+ for method in ('__new_member__', '__new__'):
+ for possible in (member_type, first_enum):
+ target = getattr(possible, method, None)
+ if target not in {
+ None,
+ None.__new__,
+ object.__new__,
+ Enum.__new__,
+ }:
+ __new__ = target
+ break
+ if __new__ is not None:
+ break
+ else:
+ __new__ = object.__new__
+
+ # if a non-object.__new__ is used then whatever value/tuple was
+ # assigned to the enum member name will be passed to __new__ and to the
+ # new enum member's __init__
+ if __new__ is object.__new__:
+ use_args = False
+ else:
+ use_args = True
+
+ return __new__, save_new, use_args
+
+
+class Enum(metaclass=EnumMeta):
+ """Generic enumeration.
+
+ Derive from this class to define new enumerations.
+
+ """
+ def __new__(cls, value):
+ # all enum instances are actually created during class construction
+ # without calling this method; this method is called by the metaclass'
+ # __call__ (i.e. Color(3) ), and by pickle
+ if type(value) is cls:
+ # For lookups like Color(Color.red)
+ return value
+ # by-value search for a matching enum member
+ # see if it's in the reverse mapping (for hashable values)
+ try:
+ if value in cls._value2member_map_:
+ return cls._value2member_map_[value]
+ except TypeError:
+ # not there, now do long search -- O(n) behavior
+ for member in cls._member_map_.values():
+ if member.value == value:
+ return member
+ raise ValueError("%s is not a valid %s" % (value, cls.__name__))
+
+ def __repr__(self):
+ return "<%s.%s: %r>" % (
+ self.__class__.__name__, self._name_, self._value_)
+
+ def __str__(self):
+ return "%s.%s" % (self.__class__.__name__, self._name_)
+
+ def __dir__(self):
+ added_behavior = [m for m in self.__class__.__dict__ if m[0] != '_']
+ return (['__class__', '__doc__', '__module__', 'name', 'value'] +
+ added_behavior)
+
+ def __format__(self, format_spec):
+ # mixed-in Enums should use the mixed-in type's __format__, otherwise
+ # we can get strange results with the Enum name showing up instead of
+ # the value
+
+ # pure Enum branch
+ if self._member_type_ is object:
+ cls = str
+ val = str(self)
+ # mix-in branch
+ else:
+ cls = self._member_type_
+ val = self.value
+ return cls.__format__(val, format_spec)
+
+ def __hash__(self):
+ return hash(self._name_)
+
+ def __reduce_ex__(self, proto):
+ return self.__class__, (self._value_, )
+
+ # DynamicClassAttribute is used to provide access to the `name` and
+ # `value` properties of enum members while keeping some measure of
+ # protection from modification, while still allowing for an enumeration
+ # to have members named `name` and `value`. This works because enumeration
+ # members are not set directly on the enum class -- __getattr__ is
+ # used to look them up.
+
+ @DynamicClassAttribute
+ def name(self):
+ """The name of the Enum member."""
+ return self._name_
+
+ @DynamicClassAttribute
+ def value(self):
+ """The value of the Enum member."""
+ return self._value_
+
+
+class IntEnum(int, Enum):
+ """Enum where members are also (and must be) ints"""
+
+
+def unique(enumeration):
+ """Class decorator for enumerations ensuring unique member values."""
+ duplicates = []
+ for name, member in enumeration.__members__.items():
+ if name != member.name:
+ duplicates.append((name, member.name))
+ if duplicates:
+ alias_details = ', '.join(
+ ["%s -> %s" % (alias, name) for (alias, name) in duplicates])
+ raise ValueError('duplicate values found in %r: %s' %
+ (enumeration, alias_details))
+ return enumeration
diff --git a/Lib/filecmp.py b/Lib/filecmp.py
index f5cea1d..e5ad839 100644
--- a/Lib/filecmp.py
+++ b/Lib/filecmp.py
@@ -6,6 +6,7 @@ Classes:
Functions:
cmp(f1, f2, shallow=True) -> int
cmpfiles(a, b, common) -> ([], [], [])
+ clear_cache()
"""
@@ -13,11 +14,18 @@ import os
import stat
from itertools import filterfalse
-__all__ = ["cmp", "dircmp", "cmpfiles"]
+__all__ = ['clear_cache', 'cmp', 'dircmp', 'cmpfiles', 'DEFAULT_IGNORES']
_cache = {}
BUFSIZE = 8*1024
+DEFAULT_IGNORES = [
+ 'RCS', 'CVS', 'tags', '.git', '.hg', '.bzr', '_darcs', '__pycache__']
+
+def clear_cache():
+ """Clear the filecmp cache."""
+ _cache.clear()
+
def cmp(f1, f2, shallow=True):
"""Compare two files.
@@ -28,14 +36,15 @@ def cmp(f1, f2, shallow=True):
f2 -- Second file name
shallow -- Just check stat signature (do not read the files).
- defaults to 1.
+ defaults to True.
Return value:
True if the files are the same, False otherwise.
This function uses a cache for past comparisons and the results,
- with a cache invalidation mechanism relying on stale signatures.
+ with cache entries invalidated if their stat information
+ changes. The cache may be cleared by calling clear_cache().
"""
@@ -52,7 +61,7 @@ def cmp(f1, f2, shallow=True):
if outcome is None:
outcome = _do_cmp(f1, f2)
if len(_cache) > 100: # limit the maximum size of the cache
- _cache.clear()
+ clear_cache()
_cache[f1, f2, s1, s2] = outcome
return outcome
@@ -80,7 +89,7 @@ class dircmp:
dircmp(a, b, ignore=None, hide=None)
A and B are directories.
IGNORE is a list of names to ignore,
- defaults to ['RCS', 'CVS', 'tags'].
+ defaults to DEFAULT_IGNORES.
HIDE is a list of names to hide,
defaults to [os.curdir, os.pardir].
@@ -116,7 +125,7 @@ class dircmp:
else:
self.hide = hide
if ignore is None:
- self.ignore = ['RCS', 'CVS', 'tags'] # Names ignored in comparison
+ self.ignore = DEFAULT_IGNORES
else:
self.ignore = ignore
@@ -147,12 +156,12 @@ class dircmp:
ok = 1
try:
a_stat = os.stat(a_path)
- except os.error as why:
+ except OSError as why:
# print('Can\'t stat', a_path, ':', why.args[1])
ok = 0
try:
b_stat = os.stat(b_path)
- except os.error as why:
+ except OSError as why:
# print('Can\'t stat', b_path, ':', why.args[1])
ok = 0
@@ -268,7 +277,7 @@ def cmpfiles(a, b, common, shallow=True):
def _cmp(a, b, sh, abs=abs, cmp=cmp):
try:
return not abs(cmp(a, b, sh))
- except os.error:
+ except OSError:
return 2
diff --git a/Lib/fileinput.py b/Lib/fileinput.py
index 879a0fd..de29518 100644
--- a/Lib/fileinput.py
+++ b/Lib/fileinput.py
@@ -30,7 +30,7 @@ pertaining to the last line read; nextfile() has no effect.
All files are opened in text mode by default, you can override this by
setting the mode parameter to input() or FileInput.__init__().
-If an I/O error occurs during opening or reading a file, the IOError
+If an I/O error occurs during opening or reading a file, the OSError
exception is raised.
If sys.stdin is used more than once, the second and further use will
@@ -222,6 +222,10 @@ class FileInput:
if mode not in ('r', 'rU', 'U', 'rb'):
raise ValueError("FileInput opening mode must be one of "
"'r', 'rU', 'U' and 'rb'")
+ if 'U' in mode:
+ import warnings
+ warnings.warn("'U' mode is deprecated",
+ DeprecationWarning, 2)
self._mode = mode
if openhook:
if inplace:
@@ -322,9 +326,11 @@ class FileInput:
if self._inplace:
self._backupfilename = (
self._filename + (self._backup or ".bak"))
- try: os.unlink(self._backupfilename)
- except os.error: pass
- # The next few lines may raise IOError
+ try:
+ os.unlink(self._backupfilename)
+ except OSError:
+ pass
+ # The next few lines may raise OSError
os.rename(self._filename, self._backupfilename)
self._file = open(self._backupfilename, self._mode)
try:
@@ -346,7 +352,7 @@ class FileInput:
self._savestdout = sys.stdout
sys.stdout = self._output
else:
- # This may raise IOError
+ # This may raise OSError
if self._openhook:
self._file = self._openhook(self._filename, self._mode)
else:
diff --git a/Lib/formatter.py b/Lib/formatter.py
index 60e60f1..d8cca52 100644
--- a/Lib/formatter.py
+++ b/Lib/formatter.py
@@ -19,6 +19,9 @@ manage and inserting data into the output.
"""
import sys
+import warnings
+warnings.warn('the formatter module is deprecated and will be removed in '
+ 'Python 3.6', PendingDeprecationWarning)
AS_IS = None
diff --git a/Lib/fractions.py b/Lib/fractions.py
index 8be52d2..79e83ff 100644
--- a/Lib/fractions.py
+++ b/Lib/fractions.py
@@ -182,8 +182,10 @@ class Fraction(numbers.Rational):
elif not isinstance(f, float):
raise TypeError("%s.from_float() only takes floats, not %r (%s)" %
(cls.__name__, f, type(f).__name__))
- if math.isnan(f) or math.isinf(f):
- raise TypeError("Cannot convert %r to %s." % (f, cls.__name__))
+ if math.isnan(f):
+ raise ValueError("Cannot convert %r to %s." % (f, cls.__name__))
+ if math.isinf(f):
+ raise OverflowError("Cannot convert %r to %s." % (f, cls.__name__))
return cls(*f.as_integer_ratio())
@classmethod
@@ -196,9 +198,11 @@ class Fraction(numbers.Rational):
raise TypeError(
"%s.from_decimal() only takes Decimals, not %r (%s)" %
(cls.__name__, dec, type(dec).__name__))
- if not dec.is_finite():
- # Catches infinities and nans.
- raise TypeError("Cannot convert %s to %s." % (dec, cls.__name__))
+ if dec.is_infinite():
+ raise OverflowError(
+ "Cannot convert %s to %s." % (dec, cls.__name__))
+ if dec.is_nan():
+ raise ValueError("Cannot convert %s to %s." % (dec, cls.__name__))
sign, digits, exp = dec.as_tuple()
digits = int(''.join(map(str, digits)))
if sign:
diff --git a/Lib/ftplib.py b/Lib/ftplib.py
index 5e75e6d..c83be2b 100644
--- a/Lib/ftplib.py
+++ b/Lib/ftplib.py
@@ -39,9 +39,10 @@ python ftplib.py -d localhost -l -p -l
import os
import sys
import socket
+import warnings
from socket import _GLOBAL_DEFAULT_TIMEOUT
-__all__ = ["FTP","Netrc"]
+__all__ = ["FTP", "Netrc"]
# Magic number from <socket.h>
MSG_OOB = 0x1 # Process data out of band
@@ -63,7 +64,7 @@ class error_proto(Error): pass # response does not begin with [1-5]
# All exceptions (hopefully) that may be raised here and that aren't
# (always) programming errors on our side
-all_errors = (Error, IOError, EOFError)
+all_errors = (Error, OSError, EOFError)
# Line terminators (we always output CRLF, but accept any of CRLF, CR, LF)
@@ -126,7 +127,7 @@ class FTP:
if self.sock is not None:
try:
self.quit()
- except (socket.error, EOFError):
+ except (OSError, EOFError):
pass
finally:
if self.sock is not None:
@@ -136,6 +137,7 @@ class FTP:
'''Connect to host. Arguments are:
- host: hostname to connect to (string, default previous host)
- port: port to connect to (integer, default previous port)
+ - timeout: the timeout to set against the ftp socket(s)
- source_address: a 2-tuple (host, port) for the socket to bind
to as its source address before connecting.
'''
@@ -186,7 +188,8 @@ class FTP:
# Internal: send one line to the server, appending CRLF
def putline(self, line):
line = line + CRLF
- if self.debugging > 1: print('*put*', self.sanitize(line))
+ if self.debugging > 1:
+ print('*put*', self.sanitize(line))
self.sock.sendall(line.encode(self.encoding))
# Internal: send one command to the server (through putline())
@@ -202,9 +205,12 @@ class FTP:
raise Error("got more than %d bytes" % self.maxline)
if self.debugging > 1:
print('*get*', self.sanitize(line))
- if not line: raise EOFError
- if line[-2:] == CRLF: line = line[:-2]
- elif line[-1:] in CRLF: line = line[:-1]
+ if not line:
+ raise EOFError
+ if line[-2:] == CRLF:
+ line = line[:-2]
+ elif line[-1:] in CRLF:
+ line = line[:-1]
return line
# Internal: get a response from the server, which may possibly
@@ -227,7 +233,8 @@ class FTP:
# Raise various errors if the response indicates an error
def getresp(self):
resp = self.getmultiline()
- if self.debugging: print('*resp*', self.sanitize(resp))
+ if self.debugging:
+ print('*resp*', self.sanitize(resp))
self.lastresp = resp[:3]
c = resp[:1]
if c in {'1', '2', '3'}:
@@ -251,7 +258,8 @@ class FTP:
IP and Synch; that doesn't seem to work with the servers I've
tried. Instead, just send the ABOR command as OOB data.'''
line = b'ABOR' + B_CRLF
- if self.debugging > 1: print('*put urgent*', self.sanitize(line))
+ if self.debugging > 1:
+ print('*put urgent*', self.sanitize(line))
self.sock.sendall(line, MSG_OOB)
resp = self.getmultiline()
if resp[:3] not in {'426', '225', '226'}:
@@ -300,7 +308,7 @@ class FTP:
try:
sock = socket.socket(af, socktype, proto)
sock.bind(sa)
- except socket.error as _:
+ except OSError as _:
err = _
if sock:
sock.close()
@@ -311,8 +319,7 @@ class FTP:
if err is not None:
raise err
else:
- raise socket.error("getaddrinfo returns an empty list")
- raise socket.error(msg)
+ raise OSError("getaddrinfo returns an empty list")
sock.listen(1)
port = sock.getsockname()[1] # Get proper port
host = self.sock.getsockname()[0] # Get proper host
@@ -392,9 +399,12 @@ class FTP:
def login(self, user = '', passwd = '', acct = ''):
'''Login, default anonymous.'''
- if not user: user = 'anonymous'
- if not passwd: passwd = ''
- if not acct: acct = ''
+ if not user:
+ user = 'anonymous'
+ if not passwd:
+ passwd = ''
+ if not acct:
+ acct = ''
if user == 'anonymous' and passwd in {'', '-'}:
# If there is no anonymous ftp password specified
# then we'll just use anonymous@
@@ -405,8 +415,10 @@ class FTP:
# host or country.
passwd = passwd + 'anonymous@'
resp = self.sendcmd('USER ' + user)
- if resp[0] == '3': resp = self.sendcmd('PASS ' + passwd)
- if resp[0] == '3': resp = self.sendcmd('ACCT ' + acct)
+ if resp[0] == '3':
+ resp = self.sendcmd('PASS ' + passwd)
+ if resp[0] == '3':
+ resp = self.sendcmd('ACCT ' + acct)
if resp[0] != '2':
raise error_reply(resp)
return resp
@@ -432,6 +444,9 @@ class FTP:
if not data:
break
callback(data)
+ # shutdown ssl layer
+ if _SSLSocket is not None and isinstance(conn, _SSLSocket):
+ conn.unwrap()
return self.voidresp()
def retrlines(self, cmd, callback = None):
@@ -446,7 +461,8 @@ class FTP:
Returns:
The response code.
"""
- if callback is None: callback = print_line
+ if callback is None:
+ callback = print_line
resp = self.sendcmd('TYPE A')
with self.transfercmd(cmd) as conn, \
conn.makefile('r', encoding=self.encoding) as fp:
@@ -454,7 +470,8 @@ class FTP:
line = fp.readline(self.maxline + 1)
if len(line) > self.maxline:
raise Error("got more than %d bytes" % self.maxline)
- if self.debugging > 2: print('*retr*', repr(line))
+ if self.debugging > 2:
+ print('*retr*', repr(line))
if not line:
break
if line[-2:] == CRLF:
@@ -462,6 +479,9 @@ class FTP:
elif line[-1:] == '\n':
line = line[:-1]
callback(line)
+ # shutdown ssl layer
+ if _SSLSocket is not None and isinstance(conn, _SSLSocket):
+ conn.unwrap()
return self.voidresp()
def storbinary(self, cmd, fp, blocksize=8192, callback=None, rest=None):
@@ -483,9 +503,14 @@ class FTP:
with self.transfercmd(cmd, rest) as conn:
while 1:
buf = fp.read(blocksize)
- if not buf: break
+ if not buf:
+ break
conn.sendall(buf)
- if callback: callback(buf)
+ if callback:
+ callback(buf)
+ # shutdown ssl layer
+ if _SSLSocket is not None and isinstance(conn, _SSLSocket):
+ conn.unwrap()
return self.voidresp()
def storlines(self, cmd, fp, callback=None):
@@ -506,12 +531,17 @@ class FTP:
buf = fp.readline(self.maxline + 1)
if len(buf) > self.maxline:
raise Error("got more than %d bytes" % self.maxline)
- if not buf: break
+ if not buf:
+ break
if buf[-2:] != B_CRLF:
if buf[-1] in B_CRLF: buf = buf[:-1]
buf = buf + B_CRLF
conn.sendall(buf)
- if callback: callback(buf)
+ if callback:
+ callback(buf)
+ # shutdown ssl layer
+ if _SSLSocket is not None and isinstance(conn, _SSLSocket):
+ conn.unwrap()
return self.voidresp()
def acct(self, password):
@@ -646,8 +676,10 @@ class FTP:
try:
import ssl
except ImportError:
- pass
+ _SSLSocket = None
else:
+ _SSLSocket = ssl.SSLSocket
+
class FTP_TLS(FTP):
'''A FTP subclass which adds TLS support to FTP as described
in RFC-4217.
@@ -694,6 +726,10 @@ else:
"exclusive")
self.keyfile = keyfile
self.certfile = certfile
+ if context is None:
+ context = ssl._create_stdlib_context(self.ssl_version,
+ certfile=certfile,
+ keyfile=keyfile)
self.context = context
self._prot_p = False
FTP.__init__(self, host, user, passwd, acct, timeout, source_address)
@@ -711,12 +747,9 @@ else:
resp = self.voidcmd('AUTH TLS')
else:
resp = self.voidcmd('AUTH SSL')
- if self.context is not None:
- self.sock = self.context.wrap_socket(self.sock)
- else:
- self.sock = ssl.wrap_socket(self.sock, self.keyfile,
- self.certfile,
- ssl_version=self.ssl_version)
+ server_hostname = self.host if ssl.HAS_SNI else None
+ self.sock = self.context.wrap_socket(self.sock,
+ server_hostname=server_hostname)
self.file = self.sock.makefile(mode='r', encoding=self.encoding)
return resp
@@ -755,80 +788,11 @@ else:
def ntransfercmd(self, cmd, rest=None):
conn, size = FTP.ntransfercmd(self, cmd, rest)
if self._prot_p:
- if self.context is not None:
- conn = self.context.wrap_socket(conn)
- else:
- conn = ssl.wrap_socket(conn, self.keyfile, self.certfile,
- ssl_version=self.ssl_version)
+ server_hostname = self.host if ssl.HAS_SNI else None
+ conn = self.context.wrap_socket(conn,
+ server_hostname=server_hostname)
return conn, size
- def retrbinary(self, cmd, callback, blocksize=8192, rest=None):
- self.voidcmd('TYPE I')
- with self.transfercmd(cmd, rest) as conn:
- while 1:
- data = conn.recv(blocksize)
- if not data:
- break
- callback(data)
- # shutdown ssl layer
- if isinstance(conn, ssl.SSLSocket):
- conn.unwrap()
- return self.voidresp()
-
- def retrlines(self, cmd, callback = None):
- if callback is None: callback = print_line
- resp = self.sendcmd('TYPE A')
- conn = self.transfercmd(cmd)
- fp = conn.makefile('r', encoding=self.encoding)
- with fp, conn:
- while 1:
- line = fp.readline(self.maxline + 1)
- if len(line) > self.maxline:
- raise Error("got more than %d bytes" % self.maxline)
- if self.debugging > 2: print('*retr*', repr(line))
- if not line:
- break
- if line[-2:] == CRLF:
- line = line[:-2]
- elif line[-1:] == '\n':
- line = line[:-1]
- callback(line)
- # shutdown ssl layer
- if isinstance(conn, ssl.SSLSocket):
- conn.unwrap()
- return self.voidresp()
-
- def storbinary(self, cmd, fp, blocksize=8192, callback=None, rest=None):
- self.voidcmd('TYPE I')
- with self.transfercmd(cmd, rest) as conn:
- while 1:
- buf = fp.read(blocksize)
- if not buf: break
- conn.sendall(buf)
- if callback: callback(buf)
- # shutdown ssl layer
- if isinstance(conn, ssl.SSLSocket):
- conn.unwrap()
- return self.voidresp()
-
- def storlines(self, cmd, fp, callback=None):
- self.voidcmd('TYPE A')
- with self.transfercmd(cmd) as conn:
- while 1:
- buf = fp.readline(self.maxline + 1)
- if len(buf) > self.maxline:
- raise Error("got more than %d bytes" % self.maxline)
- if not buf: break
- if buf[-2:] != B_CRLF:
- if buf[-1] in B_CRLF: buf = buf[:-1]
- buf = buf + B_CRLF
- conn.sendall(buf)
- if callback: callback(buf)
- # shutdown ssl layer
- if isinstance(conn, ssl.SSLSocket):
- conn.unwrap()
- return self.voidresp()
-
def abort(self):
# overridden as we can't pass MSG_OOB flag to sendall()
line = b'ABOR' + B_CRLF
@@ -839,7 +803,7 @@ else:
return resp
__all__.append('FTP_TLS')
- all_errors = (Error, IOError, EOFError, ssl.SSLError)
+ all_errors = (Error, OSError, EOFError, ssl.SSLError)
_150_re = None
@@ -936,7 +900,8 @@ def print_line(line):
def ftpcp(source, sourcename, target, targetname = '', type = 'I'):
'''Copy file from one FTP-instance to another.'''
- if not targetname: targetname = sourcename
+ if not targetname:
+ targetname = sourcename
type = 'TYPE ' + type
source.voidcmd(type)
target.voidcmd(type)
@@ -946,9 +911,11 @@ def ftpcp(source, sourcename, target, targetname = '', type = 'I'):
# transfer request.
# So: STOR before RETR, because here the target is a "user".
treply = target.sendcmd('STOR ' + targetname)
- if treply[:3] not in {'125', '150'}: raise error_proto # RFC 959
+ if treply[:3] not in {'125', '150'}:
+ raise error_proto # RFC 959
sreply = source.sendcmd('RETR ' + sourcename)
- if sreply[:3] not in {'125', '150'}: raise error_proto # RFC 959
+ if sreply[:3] not in {'125', '150'}:
+ raise error_proto # RFC 959
source.voidresp()
target.voidresp()
@@ -966,19 +933,22 @@ class Netrc:
__defacct = None
def __init__(self, filename=None):
+ warnings.warn("This class is deprecated, use the netrc module instead",
+ DeprecationWarning, 2)
if filename is None:
if "HOME" in os.environ:
filename = os.path.join(os.environ["HOME"],
".netrc")
else:
- raise IOError("specify file to load or set $HOME")
+ raise OSError("specify file to load or set $HOME")
self.__hosts = {}
self.__macros = {}
fp = open(filename, "r")
in_macro = 0
while 1:
line = fp.readline()
- if not line: break
+ if not line:
+ break
if in_macro and line.strip():
macro_lines.append(line)
continue
@@ -1087,7 +1057,7 @@ def test():
userid = passwd = acct = ''
try:
netrc = Netrc(rcfile)
- except IOError:
+ except OSError:
if rcfile is not None:
sys.stderr.write("Could not open account file"
" -- using anonymous login.")
diff --git a/Lib/functools.py b/Lib/functools.py
index 053e44e..b8463ad 100644
--- a/Lib/functools.py
+++ b/Lib/functools.py
@@ -3,16 +3,24 @@
# Python module wrapper for _functools C module
# to allow utilities written in Python to be added
# to the functools module.
-# Written by Nick Coghlan <ncoghlan at gmail.com>
-# and Raymond Hettinger <python at rcn.com>
-# Copyright (C) 2006-2010 Python Software Foundation.
+# Written by Nick Coghlan <ncoghlan at gmail.com>,
+# Raymond Hettinger <python at rcn.com>,
+# and Łukasz Langa <lukasz at langa.pl>.
+# Copyright (C) 2006-2013 Python Software Foundation.
# See C source code for _functools credits/copyright
__all__ = ['update_wrapper', 'wraps', 'WRAPPER_ASSIGNMENTS', 'WRAPPER_UPDATES',
- 'total_ordering', 'cmp_to_key', 'lru_cache', 'reduce', 'partial']
+ 'total_ordering', 'cmp_to_key', 'lru_cache', 'reduce', 'partial',
+ 'partialmethod', 'singledispatch']
-from _functools import partial, reduce
+try:
+ from _functools import reduce
+except ImportError:
+ pass
+from abc import get_cache_token
from collections import namedtuple
+from types import MappingProxyType
+from weakref import WeakKeyDictionary
try:
from _thread import RLock
except:
@@ -47,7 +55,6 @@ def update_wrapper(wrapper,
are updated with the corresponding attribute from the wrapped
function (defaults to functools.WRAPPER_UPDATES)
"""
- wrapper.__wrapped__ = wrapped
for attr in assigned:
try:
value = getattr(wrapped, attr)
@@ -57,6 +64,9 @@ def update_wrapper(wrapper,
setattr(wrapper, attr, value)
for attr in updated:
getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
+ # Issue #17482: set __wrapped__ last so we don't inadvertently copy it
+ # from the wrapped function when updating __dict__
+ wrapper.__wrapped__ = wrapped
# Return the wrapper so this can be used as a decorator via partial()
return wrapper
@@ -79,21 +89,91 @@ def wraps(wrapped,
### total_ordering class decorator
################################################################################
+# The correct way to indicate that a comparison operation doesn't
+# recognise the other type is to return NotImplemented and let the
+# interpreter handle raising TypeError if both operands return
+# NotImplemented from their respective comparison methods
+#
+# This makes the implementation of total_ordering more complicated, since
+# we need to be careful not to trigger infinite recursion when two
+# different types that both use this decorator encounter each other.
+#
+# For example, if a type implements __lt__, it's natural to define
+# __gt__ as something like:
+#
+# lambda self, other: not self < other and not self == other
+#
+# However, using the operator syntax like that ends up invoking the full
+# type checking machinery again and means we can end up bouncing back and
+# forth between the two operands until we run out of stack space.
+#
+# The solution is to define helper functions that invoke the appropriate
+# magic methods directly, ensuring we only try each operand once, and
+# return NotImplemented immediately if it is returned from the
+# underlying user provided method. Using this scheme, the __gt__ derived
+# from a user provided __lt__ becomes:
+#
+# lambda self, other: _not_op_and_not_eq(self.__lt__, self, other))
+
+def _not_op(op, other):
+ # "not a < b" handles "a >= b"
+ # "not a <= b" handles "a > b"
+ # "not a >= b" handles "a < b"
+ # "not a > b" handles "a <= b"
+ op_result = op(other)
+ if op_result is NotImplemented:
+ return NotImplemented
+ return not op_result
+
+def _op_or_eq(op, self, other):
+ # "a < b or a == b" handles "a <= b"
+ # "a > b or a == b" handles "a >= b"
+ op_result = op(other)
+ if op_result is NotImplemented:
+ return NotImplemented
+ return op_result or self == other
+
+def _not_op_and_not_eq(op, self, other):
+ # "not (a < b or a == b)" handles "a > b"
+ # "not a < b and a != b" is equivalent
+ # "not (a > b or a == b)" handles "a < b"
+ # "not a > b and a != b" is equivalent
+ op_result = op(other)
+ if op_result is NotImplemented:
+ return NotImplemented
+ return not op_result and self != other
+
+def _not_op_or_eq(op, self, other):
+ # "not a <= b or a == b" handles "a >= b"
+ # "not a >= b or a == b" handles "a <= b"
+ op_result = op(other)
+ if op_result is NotImplemented:
+ return NotImplemented
+ return not op_result or self == other
+
+def _op_and_not_eq(op, self, other):
+ # "a <= b and not a == b" handles "a < b"
+ # "a >= b and not a == b" handles "a > b"
+ op_result = op(other)
+ if op_result is NotImplemented:
+ return NotImplemented
+ return op_result and self != other
+
def total_ordering(cls):
"""Class decorator that fills in missing ordering methods"""
convert = {
- '__lt__': [('__gt__', lambda self, other: not (self < other or self == other)),
- ('__le__', lambda self, other: self < other or self == other),
- ('__ge__', lambda self, other: not self < other)],
- '__le__': [('__ge__', lambda self, other: not self <= other or self == other),
- ('__lt__', lambda self, other: self <= other and not self == other),
- ('__gt__', lambda self, other: not self <= other)],
- '__gt__': [('__lt__', lambda self, other: not (self > other or self == other)),
- ('__ge__', lambda self, other: self > other or self == other),
- ('__le__', lambda self, other: not self > other)],
- '__ge__': [('__le__', lambda self, other: (not self >= other) or self == other),
- ('__gt__', lambda self, other: self >= other and not self == other),
- ('__lt__', lambda self, other: not self >= other)]
+ '__lt__': [('__gt__', lambda self, other: _not_op_and_not_eq(self.__lt__, self, other)),
+ ('__le__', lambda self, other: _op_or_eq(self.__lt__, self, other)),
+ ('__ge__', lambda self, other: _not_op(self.__lt__, other))],
+ '__le__': [('__ge__', lambda self, other: _not_op_or_eq(self.__le__, self, other)),
+ ('__lt__', lambda self, other: _op_and_not_eq(self.__le__, self, other)),
+ ('__gt__', lambda self, other: _not_op(self.__le__, other))],
+ '__gt__': [('__lt__', lambda self, other: _not_op_and_not_eq(self.__gt__, self, other)),
+ ('__ge__', lambda self, other: _op_or_eq(self.__gt__, self, other)),
+ ('__le__', lambda self, other: _not_op(self.__gt__, other))],
+ '__ge__': [('__le__', lambda self, other: _not_op_or_eq(self.__ge__, self, other)),
+ ('__gt__', lambda self, other: _op_and_not_eq(self.__ge__, self, other)),
+ ('__lt__', lambda self, other: _not_op(self.__ge__, other))]
}
# Find user-defined comparisons (not those inherited from object).
roots = [op for op in convert if getattr(cls, op, None) is not getattr(object, op, None)]
@@ -140,6 +220,104 @@ except ImportError:
################################################################################
+### partial() argument application
+################################################################################
+
+# Purely functional, no descriptor behaviour
+def partial(func, *args, **keywords):
+ """New function with partial application of the given arguments
+ and keywords.
+ """
+ def newfunc(*fargs, **fkeywords):
+ newkeywords = keywords.copy()
+ newkeywords.update(fkeywords)
+ return func(*(args + fargs), **newkeywords)
+ newfunc.func = func
+ newfunc.args = args
+ newfunc.keywords = keywords
+ return newfunc
+
+try:
+ from _functools import partial
+except ImportError:
+ pass
+
+# Descriptor version
+class partialmethod(object):
+ """Method descriptor with partial application of the given arguments
+ and keywords.
+
+ Supports wrapping existing descriptors and handles non-descriptor
+ callables as instance methods.
+ """
+
+ def __init__(self, func, *args, **keywords):
+ if not callable(func) and not hasattr(func, "__get__"):
+ raise TypeError("{!r} is not callable or a descriptor"
+ .format(func))
+
+ # func could be a descriptor like classmethod which isn't callable,
+ # so we can't inherit from partial (it verifies func is callable)
+ if isinstance(func, partialmethod):
+ # flattening is mandatory in order to place cls/self before all
+ # other arguments
+ # it's also more efficient since only one function will be called
+ self.func = func.func
+ self.args = func.args + args
+ self.keywords = func.keywords.copy()
+ self.keywords.update(keywords)
+ else:
+ self.func = func
+ self.args = args
+ self.keywords = keywords
+
+ def __repr__(self):
+ args = ", ".join(map(repr, self.args))
+ keywords = ", ".join("{}={!r}".format(k, v)
+ for k, v in self.keywords.items())
+ format_string = "{module}.{cls}({func}, {args}, {keywords})"
+ return format_string.format(module=self.__class__.__module__,
+ cls=self.__class__.__name__,
+ func=self.func,
+ args=args,
+ keywords=keywords)
+
+ def _make_unbound_method(self):
+ def _method(*args, **keywords):
+ call_keywords = self.keywords.copy()
+ call_keywords.update(keywords)
+ cls_or_self, *rest = args
+ call_args = (cls_or_self,) + self.args + tuple(rest)
+ return self.func(*call_args, **call_keywords)
+ _method.__isabstractmethod__ = self.__isabstractmethod__
+ _method._partialmethod = self
+ return _method
+
+ def __get__(self, obj, cls):
+ get = getattr(self.func, "__get__", None)
+ result = None
+ if get is not None:
+ new_func = get(obj, cls)
+ if new_func is not self.func:
+ # Assume __get__ returning something new indicates the
+ # creation of an appropriate callable
+ result = partial(new_func, *self.args, **self.keywords)
+ try:
+ result.__self__ = new_func.__self__
+ except AttributeError:
+ pass
+ if result is None:
+ # If the underlying descriptor didn't do anything, treat this
+ # like an instance method
+ result = self._make_unbound_method().__get__(obj, cls)
+ return result
+
+ @property
+ def __isabstractmethod__(self):
+ return getattr(self.func, "__isabstractmethod__", False)
+
+
+################################################################################
### LRU Cache function decorator
################################################################################
@@ -220,7 +398,6 @@ def lru_cache(maxsize=128, typed=False):
PREV, NEXT, KEY, RESULT = 0, 1, 2, 3 # names for the link fields
def decorating_function(user_function):
-
cache = {}
hits = misses = 0
full = False
@@ -329,3 +506,210 @@ def lru_cache(maxsize=128, typed=False):
return update_wrapper(wrapper, user_function)
return decorating_function
+
+
+################################################################################
+### singledispatch() - single-dispatch generic function decorator
+################################################################################
+
+def _c3_merge(sequences):
+ """Merges MROs in *sequences* to a single MRO using the C3 algorithm.
+
+ Adapted from http://www.python.org/download/releases/2.3/mro/.
+
+ """
+ result = []
+ while True:
+ sequences = [s for s in sequences if s] # purge empty sequences
+ if not sequences:
+ return result
+ for s1 in sequences: # find merge candidates among seq heads
+ candidate = s1[0]
+ for s2 in sequences:
+ if candidate in s2[1:]:
+ candidate = None
+ break # reject the current head, it appears later
+ else:
+ break
+ if not candidate:
+ raise RuntimeError("Inconsistent hierarchy")
+ result.append(candidate)
+ # remove the chosen candidate
+ for seq in sequences:
+ if seq[0] == candidate:
+ del seq[0]
+
+def _c3_mro(cls, abcs=None):
+ """Computes the method resolution order using extended C3 linearization.
+
+ If no *abcs* are given, the algorithm works exactly like the built-in C3
+ linearization used for method resolution.
+
+ If given, *abcs* is a list of abstract base classes that should be inserted
+ into the resulting MRO. Unrelated ABCs are ignored and don't end up in the
+ result. The algorithm inserts ABCs where their functionality is introduced,
+ i.e. issubclass(cls, abc) returns True for the class itself but returns
+ False for all its direct base classes. Implicit ABCs for a given class
+ (either registered or inferred from the presence of a special method like
+ __len__) are inserted directly after the last ABC explicitly listed in the
+ MRO of said class. If two implicit ABCs end up next to each other in the
+ resulting MRO, their ordering depends on the order of types in *abcs*.
+
+ """
+ for i, base in enumerate(reversed(cls.__bases__)):
+ if hasattr(base, '__abstractmethods__'):
+ boundary = len(cls.__bases__) - i
+ break # Bases up to the last explicit ABC are considered first.
+ else:
+ boundary = 0
+ abcs = list(abcs) if abcs else []
+ explicit_bases = list(cls.__bases__[:boundary])
+ abstract_bases = []
+ other_bases = list(cls.__bases__[boundary:])
+ for base in abcs:
+ if issubclass(cls, base) and not any(
+ issubclass(b, base) for b in cls.__bases__
+ ):
+ # If *cls* is the class that introduces behaviour described by
+ # an ABC *base*, insert said ABC to its MRO.
+ abstract_bases.append(base)
+ for base in abstract_bases:
+ abcs.remove(base)
+ explicit_c3_mros = [_c3_mro(base, abcs=abcs) for base in explicit_bases]
+ abstract_c3_mros = [_c3_mro(base, abcs=abcs) for base in abstract_bases]
+ other_c3_mros = [_c3_mro(base, abcs=abcs) for base in other_bases]
+ return _c3_merge(
+ [[cls]] +
+ explicit_c3_mros + abstract_c3_mros + other_c3_mros +
+ [explicit_bases] + [abstract_bases] + [other_bases]
+ )
+
+def _compose_mro(cls, types):
+ """Calculates the method resolution order for a given class *cls*.
+
+ Includes relevant abstract base classes (with their respective bases) from
+ the *types* iterable. Uses a modified C3 linearization algorithm.
+
+ """
+ bases = set(cls.__mro__)
+ # Remove entries which are already present in the __mro__ or unrelated.
+ def is_related(typ):
+ return (typ not in bases and hasattr(typ, '__mro__')
+ and issubclass(cls, typ))
+ types = [n for n in types if is_related(n)]
+ # Remove entries which are strict bases of other entries (they will end up
+ # in the MRO anyway.
+ def is_strict_base(typ):
+ for other in types:
+ if typ != other and typ in other.__mro__:
+ return True
+ return False
+ types = [n for n in types if not is_strict_base(n)]
+ # Subclasses of the ABCs in *types* which are also implemented by
+ # *cls* can be used to stabilize ABC ordering.
+ type_set = set(types)
+ mro = []
+ for typ in types:
+ found = []
+ for sub in typ.__subclasses__():
+ if sub not in bases and issubclass(cls, sub):
+ found.append([s for s in sub.__mro__ if s in type_set])
+ if not found:
+ mro.append(typ)
+ continue
+ # Favor subclasses with the biggest number of useful bases
+ found.sort(key=len, reverse=True)
+ for sub in found:
+ for subcls in sub:
+ if subcls not in mro:
+ mro.append(subcls)
+ return _c3_mro(cls, abcs=mro)
+
+def _find_impl(cls, registry):
+ """Returns the best matching implementation from *registry* for type *cls*.
+
+ Where there is no registered implementation for a specific type, its method
+ resolution order is used to find a more generic implementation.
+
+ Note: if *registry* does not contain an implementation for the base
+ *object* type, this function may return None.
+
+ """
+ mro = _compose_mro(cls, registry.keys())
+ match = None
+ for t in mro:
+ if match is not None:
+ # If *match* is an implicit ABC but there is another unrelated,
+ # equally matching implicit ABC, refuse the temptation to guess.
+ if (t in registry and t not in cls.__mro__
+ and match not in cls.__mro__
+ and not issubclass(match, t)):
+ raise RuntimeError("Ambiguous dispatch: {} or {}".format(
+ match, t))
+ break
+ if t in registry:
+ match = t
+ return registry.get(match)
+
+def singledispatch(func):
+ """Single-dispatch generic function decorator.
+
+ Transforms a function into a generic function, which can have different
+ behaviours depending upon the type of its first argument. The decorated
+ function acts as the default implementation, and additional
+ implementations can be registered using the register() attribute of the
+ generic function.
+
+ """
+ registry = {}
+ dispatch_cache = WeakKeyDictionary()
+ cache_token = None
+
+ def dispatch(cls):
+ """generic_func.dispatch(cls) -> <function implementation>
+
+ Runs the dispatch algorithm to return the best available implementation
+ for the given *cls* registered on *generic_func*.
+
+ """
+ nonlocal cache_token
+ if cache_token is not None:
+ current_token = get_cache_token()
+ if cache_token != current_token:
+ dispatch_cache.clear()
+ cache_token = current_token
+ try:
+ impl = dispatch_cache[cls]
+ except KeyError:
+ try:
+ impl = registry[cls]
+ except KeyError:
+ impl = _find_impl(cls, registry)
+ dispatch_cache[cls] = impl
+ return impl
+
+ def register(cls, func=None):
+ """generic_func.register(cls, func) -> func
+
+ Registers a new implementation for the given *cls* on a *generic_func*.
+
+ """
+ nonlocal cache_token
+ if func is None:
+ return lambda f: register(cls, f)
+ registry[cls] = func
+ if cache_token is None and hasattr(cls, '__abstractmethods__'):
+ cache_token = get_cache_token()
+ dispatch_cache.clear()
+ return func
+
+ def wrapper(*args, **kw):
+ return dispatch(args[0].__class__)(*args, **kw)
+
+ registry[object] = func
+ wrapper.register = register
+ wrapper.dispatch = dispatch
+ wrapper.registry = MappingProxyType(registry)
+ wrapper._clear_cache = dispatch_cache.clear
+ update_wrapper(wrapper, func)
+ return wrapper
diff --git a/Lib/genericpath.py b/Lib/genericpath.py
index 340c004..ca4a510 100644
--- a/Lib/genericpath.py
+++ b/Lib/genericpath.py
@@ -7,7 +7,8 @@ import os
import stat
__all__ = ['commonprefix', 'exists', 'getatime', 'getctime', 'getmtime',
- 'getsize', 'isdir', 'isfile']
+ 'getsize', 'isdir', 'isfile', 'samefile', 'sameopenfile',
+ 'samestat']
# Does a path exist?
@@ -16,7 +17,7 @@ def exists(path):
"""Test whether a path exists. Returns False for broken symbolic links"""
try:
os.stat(path)
- except os.error:
+ except OSError:
return False
return True
@@ -27,7 +28,7 @@ def isfile(path):
"""Test whether a path is a regular file"""
try:
st = os.stat(path)
- except os.error:
+ except OSError:
return False
return stat.S_ISREG(st.st_mode)
@@ -39,7 +40,7 @@ def isdir(s):
"""Return true if the pathname refers to an existing directory."""
try:
st = os.stat(s)
- except os.error:
+ except OSError:
return False
return stat.S_ISDIR(st.st_mode)
@@ -75,6 +76,31 @@ def commonprefix(m):
return s1[:i]
return s1
+# Are two stat buffers (obtained from stat, fstat or lstat)
+# describing the same file?
+def samestat(s1, s2):
+ """Test whether two stat buffers reference the same file"""
+ return (s1.st_ino == s2.st_ino and
+ s1.st_dev == s2.st_dev)
+
+
+# Are two filenames really pointing to the same file?
+def samefile(f1, f2):
+ """Test whether two pathnames reference the same actual file"""
+ s1 = os.stat(f1)
+ s2 = os.stat(f2)
+ return samestat(s1, s2)
+
+
+# Are two open files really referencing the same file?
+# (Not necessarily the same file descriptor!)
+def sameopenfile(fp1, fp2):
+ """Test whether two open file objects reference the same file"""
+ s1 = os.fstat(fp1)
+ s2 = os.fstat(fp2)
+ return samestat(s1, s2)
+
+
# Split a path in root and extension.
# The extension is everything starting at the last dot in the last
# pathname component; the root is everything before that.
diff --git a/Lib/getpass.py b/Lib/getpass.py
index 53c38b8..7c4e976 100644
--- a/Lib/getpass.py
+++ b/Lib/getpass.py
@@ -135,7 +135,13 @@ def _raw_input(prompt="", stream=None, input=None):
input = sys.stdin
prompt = str(prompt)
if prompt:
- stream.write(prompt)
+ try:
+ stream.write(prompt)
+ except UnicodeEncodeError:
+ # Use replace error handler to get as much as possible printed.
+ prompt = prompt.encode(stream.encoding, 'replace')
+ prompt = prompt.decode(stream.encoding)
+ stream.write(prompt)
stream.flush()
# NOTE: The Python C API calls flockfile() (and unlock) during readline.
line = input.readline()
diff --git a/Lib/gettext.py b/Lib/gettext.py
index e43f044..05d9c1e 100644
--- a/Lib/gettext.py
+++ b/Lib/gettext.py
@@ -244,7 +244,7 @@ class GNUTranslations(NullTranslations):
version, msgcount, masteridx, transidx = unpack('>4I', buf[4:20])
ii = '>II'
else:
- raise IOError(0, 'Bad magic number', filename)
+ raise OSError(0, 'Bad magic number', filename)
# Now put all messages from the .mo file buffer into the catalog
# dictionary.
for i in range(0, msgcount):
@@ -256,7 +256,7 @@ class GNUTranslations(NullTranslations):
msg = buf[moff:mend]
tmsg = buf[toff:tend]
else:
- raise IOError(0, 'File is corrupt', filename)
+ raise OSError(0, 'File is corrupt', filename)
# See if we're looking at GNU .mo conventions for metadata
if mlen == 0:
# Catalog description
@@ -398,7 +398,7 @@ def translation(domain, localedir=None, languages=None,
if not mofiles:
if fallback:
return NullTranslations()
- raise IOError(ENOENT, 'No translation file found for domain', domain)
+ raise OSError(ENOENT, 'No translation file found for domain', domain)
# Avoid opening, reading, and parsing the .mo file after it's been done
# once.
result = None
@@ -460,7 +460,7 @@ def dgettext(domain, message):
try:
t = translation(domain, _localedirs.get(domain, None),
codeset=_localecodesets.get(domain))
- except IOError:
+ except OSError:
return message
return t.gettext(message)
@@ -468,7 +468,7 @@ def ldgettext(domain, message):
try:
t = translation(domain, _localedirs.get(domain, None),
codeset=_localecodesets.get(domain))
- except IOError:
+ except OSError:
return message
return t.lgettext(message)
@@ -476,7 +476,7 @@ def dngettext(domain, msgid1, msgid2, n):
try:
t = translation(domain, _localedirs.get(domain, None),
codeset=_localecodesets.get(domain))
- except IOError:
+ except OSError:
if n == 1:
return msgid1
else:
@@ -487,7 +487,7 @@ def ldngettext(domain, msgid1, msgid2, n):
try:
t = translation(domain, _localedirs.get(domain, None),
codeset=_localecodesets.get(domain))
- except IOError:
+ except OSError:
if n == 1:
return msgid1
else:
diff --git a/Lib/glob.py b/Lib/glob.py
index 1f60265..e388b5f 100644
--- a/Lib/glob.py
+++ b/Lib/glob.py
@@ -32,8 +32,7 @@ def iglob(pathname):
return
dirname, basename = os.path.split(pathname)
if not dirname:
- for name in glob1(None, basename):
- yield name
+ yield from glob1(None, basename)
return
# `os.path.split()` returns the argument itself as a dirname if it is a
# drive or UNC path. Prevent an infinite recursion if a drive or UNC path
@@ -62,7 +61,7 @@ def glob1(dirname, pattern):
dirname = os.curdir
try:
names = os.listdir(dirname)
- except os.error:
+ except OSError:
return []
if not _ishidden(pattern):
names = [x for x in names if not _ishidden(x)]
@@ -80,8 +79,8 @@ def glob0(dirname, basename):
return []
-magic_check = re.compile('[*?[]')
-magic_check_bytes = re.compile(b'[*?[]')
+magic_check = re.compile('([*?[])')
+magic_check_bytes = re.compile(b'([*?[])')
def has_magic(s):
if isinstance(s, bytes):
@@ -92,3 +91,15 @@ def has_magic(s):
def _ishidden(path):
return path[0] in ('.', b'.'[0])
+
+def escape(pathname):
+ """Escape all special characters.
+ """
+ # Escaping is done by wrapping any of "*?[" between square brackets.
+ # Metacharacters do not work in the drive part and shouldn't be escaped.
+ drive, pathname = os.path.splitdrive(pathname)
+ if isinstance(pathname, bytes):
+ pathname = magic_check_bytes.sub(br'[\1]', pathname)
+ else:
+ pathname = magic_check.sub(r'[\1]', pathname)
+ return drive + pathname
diff --git a/Lib/gzip.py b/Lib/gzip.py
index 4ff9820..f934d4f 100644
--- a/Lib/gzip.py
+++ b/Lib/gzip.py
@@ -23,9 +23,9 @@ def open(filename, mode="rb", compresslevel=9,
The filename argument can be an actual filename (a str or bytes object), or
an existing file object to read from or write to.
- The mode argument can be "r", "rb", "w", "wb", "a" or "ab" for binary mode,
- or "rt", "wt" or "at" for text mode. The default mode is "rb", and the
- default compresslevel is 9.
+ The mode argument can be "r", "rb", "w", "wb", "x", "xb", "a" or "ab" for
+ binary mode, or "rt", "wt", "xt" or "at" for text mode. The default mode is
+ "rb", and the default compresslevel is 9.
For binary mode, this function is equivalent to the GzipFile constructor:
GzipFile(filename, mode, compresslevel). In this case, the encoding, errors
@@ -65,9 +65,6 @@ def write32u(output, value):
# or unsigned.
output.write(struct.pack("<L", value))
-def read32(input):
- return struct.unpack("<I", input.read(4))[0]
-
class _PaddedFile:
"""Minimal read-only file object that prepends a string to the contents
of an actual file. Shouldn't be used outside of gzip.py, as it lacks
@@ -154,11 +151,11 @@ class GzipFile(io.BufferedIOBase):
fileobj, if discernible; otherwise, it defaults to the empty string,
and in this case the original filename is not included in the header.
- The mode argument can be any of 'r', 'rb', 'a', 'ab', 'w', or 'wb',
- depending on whether the file will be read or written. The default
+ The mode argument can be any of 'r', 'rb', 'a', 'ab', 'w', 'wb', 'x', or
+ 'xb' depending on whether the file will be read or written. The default
is the mode of fileobj if discernible; otherwise, the default is 'rb'.
A mode of 'r' is equivalent to one of 'rb', and similarly for 'w' and
- 'wb', and 'a' and 'ab'.
+ 'wb', 'a' and 'ab', and 'x' and 'xb'.
The compresslevel argument is an integer from 0 to 9 controlling the
level of compression; 1 is fastest and produces the least compression,
@@ -204,7 +201,7 @@ class GzipFile(io.BufferedIOBase):
self.min_readsize = 100
fileobj = _PaddedFile(fileobj)
- elif mode.startswith(('w', 'a')):
+ elif mode.startswith(('w', 'a', 'x')):
self.mode = WRITE
self._init_write(filename)
self.compress = zlib.compressobj(compresslevel,
@@ -281,28 +278,32 @@ class GzipFile(io.BufferedIOBase):
self.crc = zlib.crc32(b"") & 0xffffffff
self.size = 0
+ def _read_exact(self, n):
+ data = self.fileobj.read(n)
+ while len(data) < n:
+ b = self.fileobj.read(n - len(data))
+ if not b:
+ raise EOFError("Compressed file ended before the "
+ "end-of-stream marker was reached")
+ data += b
+ return data
+
def _read_gzip_header(self):
magic = self.fileobj.read(2)
if magic == b'':
- raise EOFError("Reached EOF")
+ return False
if magic != b'\037\213':
- raise IOError('Not a gzipped file')
+ raise OSError('Not a gzipped file')
- method = ord( self.fileobj.read(1) )
+ method, flag, self.mtime = struct.unpack("<BBIxx", self._read_exact(8))
if method != 8:
- raise IOError('Unknown compression method')
- flag = ord( self.fileobj.read(1) )
- self.mtime = read32(self.fileobj)
- # extraflag = self.fileobj.read(1)
- # os = self.fileobj.read(1)
- self.fileobj.read(2)
+ raise OSError('Unknown compression method')
if flag & FEXTRA:
# Read & discard the extra field, if present
- xlen = ord(self.fileobj.read(1))
- xlen = xlen + 256*ord(self.fileobj.read(1))
- self.fileobj.read(xlen)
+ extra_len, = struct.unpack("<H", self._read_exact(2))
+ self._read_exact(extra_len)
if flag & FNAME:
# Read and discard a null-terminated string containing the filename
while True:
@@ -316,18 +317,19 @@ class GzipFile(io.BufferedIOBase):
if not s or s==b'\000':
break
if flag & FHCRC:
- self.fileobj.read(2) # Read & discard the 16-bit header CRC
+ self._read_exact(2) # Read & discard the 16-bit header CRC
unused = self.fileobj.unused()
if unused:
uncompress = self.decompress.decompress(unused)
self._add_read_data(uncompress)
+ return True
def write(self,data):
self._check_closed()
if self.mode != WRITE:
import errno
- raise IOError(errno.EBADF, "write() on read-only GzipFile object")
+ raise OSError(errno.EBADF, "write() on read-only GzipFile object")
if self.fileobj is None:
raise ValueError("write() on closed GzipFile object")
@@ -348,27 +350,23 @@ class GzipFile(io.BufferedIOBase):
self._check_closed()
if self.mode != READ:
import errno
- raise IOError(errno.EBADF, "read() on write-only GzipFile object")
+ raise OSError(errno.EBADF, "read() on write-only GzipFile object")
if self.extrasize <= 0 and self.fileobj is None:
return b''
readsize = 1024
if size < 0: # get the whole thing
- try:
- while True:
- self._read(readsize)
- readsize = min(self.max_read_chunk, readsize * 2)
- except EOFError:
- size = self.extrasize
+ while self._read(readsize):
+ readsize = min(self.max_read_chunk, readsize * 2)
+ size = self.extrasize
else: # just get some more of it
- try:
- while size > self.extrasize:
- self._read(readsize)
- readsize = min(self.max_read_chunk, readsize * 2)
- except EOFError:
- if size > self.extrasize:
- size = self.extrasize
+ while size > self.extrasize:
+ if not self._read(readsize):
+ if size > self.extrasize:
+ size = self.extrasize
+ break
+ readsize = min(self.max_read_chunk, readsize * 2)
offset = self.offset - self.extrastart
chunk = self.extrabuf[offset: offset + size]
@@ -381,17 +379,14 @@ class GzipFile(io.BufferedIOBase):
self._check_closed()
if self.mode != READ:
import errno
- raise IOError(errno.EBADF, "read1() on write-only GzipFile object")
+ raise OSError(errno.EBADF, "read1() on write-only GzipFile object")
if self.extrasize <= 0 and self.fileobj is None:
return b''
- try:
- # For certain input data, a single call to _read() may not return
- # any data. In this case, retry until we get some data or reach EOF.
- while self.extrasize <= 0:
- self._read()
- except EOFError:
+ # For certain input data, a single call to _read() may not return
+ # any data. In this case, retry until we get some data or reach EOF.
+ while self.extrasize <= 0 and self._read():
pass
if size < 0 or size > self.extrasize:
size = self.extrasize
@@ -405,7 +400,7 @@ class GzipFile(io.BufferedIOBase):
def peek(self, n):
if self.mode != READ:
import errno
- raise IOError(errno.EBADF, "peek() on write-only GzipFile object")
+ raise OSError(errno.EBADF, "peek() on write-only GzipFile object")
# Do not return ridiculously small buffers, for one common idiom
# is to call peek(1) and expect more bytes in return.
@@ -414,12 +409,9 @@ class GzipFile(io.BufferedIOBase):
if self.extrasize == 0:
if self.fileobj is None:
return b''
- try:
- # Ensure that we don't return b"" if we haven't reached EOF.
- while self.extrasize == 0:
- # 1024 is the same buffering heuristic used in read()
- self._read(max(n, 1024))
- except EOFError:
+ # Ensure that we don't return b"" if we haven't reached EOF.
+ # 1024 is the same buffering heuristic used in read()
+ while self.extrasize == 0 and self._read(max(n, 1024)):
pass
offset = self.offset - self.extrastart
remaining = self.extrasize
@@ -432,13 +424,14 @@ class GzipFile(io.BufferedIOBase):
def _read(self, size=1024):
if self.fileobj is None:
- raise EOFError("Reached EOF")
+ return False
if self._new_member:
# If the _new_member flag is set, we have to
# jump to the next member, if there is one.
self._init_read()
- self._read_gzip_header()
+ if not self._read_gzip_header():
+ return False
self.decompress = zlib.decompressobj(-zlib.MAX_WBITS)
self._new_member = False
@@ -455,7 +448,7 @@ class GzipFile(io.BufferedIOBase):
self.fileobj.prepend(self.decompress.unused_data, True)
self._read_eof()
self._add_read_data( uncompress )
- raise EOFError('Reached EOF')
+ return False
uncompress = self.decompress.decompress(buf)
self._add_read_data( uncompress )
@@ -471,6 +464,7 @@ class GzipFile(io.BufferedIOBase):
# a new member on the next call
self._read_eof()
self._new_member = True
+ return True
def _add_read_data(self, data):
self.crc = zlib.crc32(data, self.crc) & 0xffffffff
@@ -485,13 +479,12 @@ class GzipFile(io.BufferedIOBase):
# We check the that the computed CRC and size of the
# uncompressed data matches the stored values. Note that the size
# stored is the true file size mod 2**32.
- crc32 = read32(self.fileobj)
- isize = read32(self.fileobj) # may exceed 2GB
+ crc32, isize = struct.unpack("<II", self._read_exact(8))
if crc32 != self.crc:
- raise IOError("CRC check failed %s != %s" % (hex(crc32),
+ raise OSError("CRC check failed %s != %s" % (hex(crc32),
hex(self.crc)))
elif isize != (self.size & 0xffffffff):
- raise IOError("Incorrect length of data produced")
+ raise OSError("Incorrect length of data produced")
# Gzip files can be padded with zeroes and still have archives.
# Consume all zero bytes and set the file position to the first
@@ -540,7 +533,7 @@ class GzipFile(io.BufferedIOBase):
'''Return the uncompressed stream file position indicator to the
beginning of the file'''
if self.mode != READ:
- raise IOError("Can't rewind in write mode")
+ raise OSError("Can't rewind in write mode")
self.fileobj.seek(0)
self._new_member = True
self.extrabuf = b""
@@ -565,7 +558,7 @@ class GzipFile(io.BufferedIOBase):
raise ValueError('Seek from end not supported')
if self.mode == WRITE:
if offset < self.offset:
- raise IOError('Negative seek in write mode')
+ raise OSError('Negative seek in write mode')
count = offset - self.offset
chunk = bytes(1024)
for i in range(count // 1024):
diff --git a/Lib/hashlib.py b/Lib/hashlib.py
index 21454c7..e652fc6 100644
--- a/Lib/hashlib.py
+++ b/Lib/hashlib.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2005-2010 Gregory P. Smith (greg@krypto.org)
+#. Copyright (C) 2005-2010 Gregory P. Smith (greg@krypto.org)
# Licensed to PSF under a Contributor Agreement.
#
@@ -60,34 +60,38 @@ algorithms_guaranteed = set(__always_supported)
algorithms_available = set(__always_supported)
__all__ = __always_supported + ('new', 'algorithms_guaranteed',
- 'algorithms_available')
+ 'algorithms_available', 'pbkdf2_hmac')
+__builtin_constructor_cache = {}
+
def __get_builtin_constructor(name):
+ cache = __builtin_constructor_cache
+ constructor = cache.get(name)
+ if constructor is not None:
+ return constructor
try:
if name in ('SHA1', 'sha1'):
import _sha1
- return _sha1.sha1
+ cache['SHA1'] = cache['sha1'] = _sha1.sha1
elif name in ('MD5', 'md5'):
import _md5
- return _md5.md5
+ cache['MD5'] = cache['md5'] = _md5.md5
elif name in ('SHA256', 'sha256', 'SHA224', 'sha224'):
import _sha256
- bs = name[3:]
- if bs == '256':
- return _sha256.sha256
- elif bs == '224':
- return _sha256.sha224
+ cache['SHA224'] = cache['sha224'] = _sha256.sha224
+ cache['SHA256'] = cache['sha256'] = _sha256.sha256
elif name in ('SHA512', 'sha512', 'SHA384', 'sha384'):
import _sha512
- bs = name[3:]
- if bs == '512':
- return _sha512.sha512
- elif bs == '384':
- return _sha512.sha384
+ cache['SHA384'] = cache['sha384'] = _sha512.sha384
+ cache['SHA512'] = cache['sha512'] = _sha512.sha512
except ImportError:
pass # no extension module, this hash is unsupported.
+ constructor = cache.get(name)
+ if constructor is not None:
+ return constructor
+
raise ValueError('unsupported hash type ' + name)
@@ -134,6 +138,71 @@ except ImportError:
new = __py_new
__get_hash = __get_builtin_constructor
+try:
+ # OpenSSL's PKCS5_PBKDF2_HMAC requires OpenSSL 1.0+ with HMAC and SHA
+ from _hashlib import pbkdf2_hmac
+except ImportError:
+ _trans_5C = bytes((x ^ 0x5C) for x in range(256))
+ _trans_36 = bytes((x ^ 0x36) for x in range(256))
+
+ def pbkdf2_hmac(hash_name, password, salt, iterations, dklen=None):
+ """Password based key derivation function 2 (PKCS #5 v2.0)
+
+ This Python implementations based on the hmac module about as fast
+ as OpenSSL's PKCS5_PBKDF2_HMAC for short passwords and much faster
+ for long passwords.
+ """
+ if not isinstance(hash_name, str):
+ raise TypeError(hash_name)
+
+ if not isinstance(password, (bytes, bytearray)):
+ password = bytes(memoryview(password))
+ if not isinstance(salt, (bytes, bytearray)):
+ salt = bytes(memoryview(salt))
+
+ # Fast inline HMAC implementation
+ inner = new(hash_name)
+ outer = new(hash_name)
+ blocksize = getattr(inner, 'block_size', 64)
+ if len(password) > blocksize:
+ password = new(hash_name, password).digest()
+ password = password + b'\x00' * (blocksize - len(password))
+ inner.update(password.translate(_trans_36))
+ outer.update(password.translate(_trans_5C))
+
+ def prf(msg, inner=inner, outer=outer):
+ # PBKDF2_HMAC uses the password as key. We can re-use the same
+ # digest objects and and just update copies to skip initialization.
+ icpy = inner.copy()
+ ocpy = outer.copy()
+ icpy.update(msg)
+ ocpy.update(icpy.digest())
+ return ocpy.digest()
+
+ if iterations < 1:
+ raise ValueError(iterations)
+ if dklen is None:
+ dklen = outer.digest_size
+ if dklen < 1:
+ raise ValueError(dklen)
+
+ dkey = b''
+ loop = 1
+ from_bytes = int.from_bytes
+ while len(dkey) < dklen:
+ prev = prf(salt + loop.to_bytes(4, 'big'))
+ # endianess doesn't matter here as long to / from use the same
+ rkey = int.from_bytes(prev, 'big')
+ for i in range(iterations - 1):
+ prev = prf(prev)
+ # rkey = rkey ^ prev
+ rkey ^= from_bytes(prev, 'big')
+ loop += 1
+ dkey += rkey.to_bytes(inner.digest_size, 'big')
+
+ return dkey[:dklen]
+
+
for __func_name in __always_supported:
# try them all, some may not work due to the OpenSSL
# version not supporting that algorithm.
diff --git a/Lib/hmac.py b/Lib/hmac.py
index 4297a71..77785a2 100644
--- a/Lib/hmac.py
+++ b/Lib/hmac.py
@@ -4,7 +4,8 @@ Implements the HMAC algorithm as described by RFC 2104.
"""
import warnings as _warnings
-from operator import _compare_digest as compare_digest
+from _operator import _compare_digest as compare_digest
+import hashlib as _hashlib
trans_5C = bytes((x ^ 0x5C) for x in range(256))
trans_36 = bytes((x ^ 0x36) for x in range(256))
@@ -28,21 +29,27 @@ class HMAC:
key: key for the keyed hash object.
msg: Initial input for the hash, if provided.
digestmod: A module supporting PEP 247. *OR*
- A hashlib constructor returning a new hash object.
+ A hashlib constructor returning a new hash object. *OR*
+ A hash name suitable for hashlib.new().
Defaults to hashlib.md5.
+ Implicit default to hashlib.md5 is deprecated and will be
+ removed in Python 3.6.
- Note: key and msg must be bytes objects.
+ Note: key and msg must be a bytes or bytearray objects.
"""
- if not isinstance(key, bytes):
- raise TypeError("key: expected bytes, but got %r" % type(key).__name__)
+ if not isinstance(key, (bytes, bytearray)):
+ raise TypeError("key: expected bytes or bytearray, but got %r" % type(key).__name__)
if digestmod is None:
- import hashlib
- digestmod = hashlib.md5
+ _warnings.warn("HMAC() without an explicit digestmod argument "
+ "is deprecated.", PendingDeprecationWarning, 2)
+ digestmod = _hashlib.md5
if callable(digestmod):
self.digest_cons = digestmod
+ elif isinstance(digestmod, str):
+ self.digest_cons = lambda d=b'': _hashlib.new(digestmod, d)
else:
self.digest_cons = lambda d=b'': digestmod.new(d)
@@ -63,6 +70,10 @@ class HMAC:
RuntimeWarning, 2)
blocksize = self.blocksize
+ # self.blocksize is the default blocksize. self.block_size is
+ # effective block size as well as the public API attribute.
+ self.block_size = blocksize
+
if len(key) > blocksize:
key = self.digest_cons(key).digest()
@@ -72,11 +83,13 @@ class HMAC:
if msg is not None:
self.update(msg)
+ @property
+ def name(self):
+ return "hmac-" + self.inner.name
+
def update(self, msg):
"""Update this hashing object with the string msg.
"""
- if not isinstance(msg, bytes):
- raise TypeError("expected bytes, but got %r" % type(msg).__name__)
self.inner.update(msg)
def copy(self):
diff --git a/Lib/html/__init__.py b/Lib/html/__init__.py
index 02652ef..da0a0a3 100644
--- a/Lib/html/__init__.py
+++ b/Lib/html/__init__.py
@@ -2,12 +2,12 @@
General functions for HTML manipulation.
"""
+import re as _re
+from html.entities import html5 as _html5
-_escape_map = {ord('&'): '&amp;', ord('<'): '&lt;', ord('>'): '&gt;'}
-_escape_map_full = {ord('&'): '&amp;', ord('<'): '&lt;', ord('>'): '&gt;',
- ord('"'): '&quot;', ord('\''): '&#x27;'}
-# NB: this is a candidate for a bytes/string polymorphic interface
+__all__ = ['escape', 'unescape']
+
def escape(s, quote=True):
"""
@@ -16,6 +16,117 @@ def escape(s, quote=True):
characters, both double quote (") and single quote (') characters are also
translated.
"""
+ s = s.replace("&", "&amp;") # Must be done first!
+ s = s.replace("<", "&lt;")
+ s = s.replace(">", "&gt;")
if quote:
- return s.translate(_escape_map_full)
- return s.translate(_escape_map)
+ s = s.replace('"', "&quot;")
+ s = s.replace('\'', "&#x27;")
+ return s
+
+
+# see http://www.w3.org/TR/html5/syntax.html#tokenizing-character-references
+
+_invalid_charrefs = {
+ 0x00: '\ufffd', # REPLACEMENT CHARACTER
+ 0x0d: '\r', # CARRIAGE RETURN
+ 0x80: '\u20ac', # EURO SIGN
+ 0x81: '\x81', # <control>
+ 0x82: '\u201a', # SINGLE LOW-9 QUOTATION MARK
+ 0x83: '\u0192', # LATIN SMALL LETTER F WITH HOOK
+ 0x84: '\u201e', # DOUBLE LOW-9 QUOTATION MARK
+ 0x85: '\u2026', # HORIZONTAL ELLIPSIS
+ 0x86: '\u2020', # DAGGER
+ 0x87: '\u2021', # DOUBLE DAGGER
+ 0x88: '\u02c6', # MODIFIER LETTER CIRCUMFLEX ACCENT
+ 0x89: '\u2030', # PER MILLE SIGN
+ 0x8a: '\u0160', # LATIN CAPITAL LETTER S WITH CARON
+ 0x8b: '\u2039', # SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+ 0x8c: '\u0152', # LATIN CAPITAL LIGATURE OE
+ 0x8d: '\x8d', # <control>
+ 0x8e: '\u017d', # LATIN CAPITAL LETTER Z WITH CARON
+ 0x8f: '\x8f', # <control>
+ 0x90: '\x90', # <control>
+ 0x91: '\u2018', # LEFT SINGLE QUOTATION MARK
+ 0x92: '\u2019', # RIGHT SINGLE QUOTATION MARK
+ 0x93: '\u201c', # LEFT DOUBLE QUOTATION MARK
+ 0x94: '\u201d', # RIGHT DOUBLE QUOTATION MARK
+ 0x95: '\u2022', # BULLET
+ 0x96: '\u2013', # EN DASH
+ 0x97: '\u2014', # EM DASH
+ 0x98: '\u02dc', # SMALL TILDE
+ 0x99: '\u2122', # TRADE MARK SIGN
+ 0x9a: '\u0161', # LATIN SMALL LETTER S WITH CARON
+ 0x9b: '\u203a', # SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+ 0x9c: '\u0153', # LATIN SMALL LIGATURE OE
+ 0x9d: '\x9d', # <control>
+ 0x9e: '\u017e', # LATIN SMALL LETTER Z WITH CARON
+ 0x9f: '\u0178', # LATIN CAPITAL LETTER Y WITH DIAERESIS
+}
+
+_invalid_codepoints = {
+ # 0x0001 to 0x0008
+ 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
+ # 0x000E to 0x001F
+ 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
+ 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ # 0x007F to 0x009F
+ 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
+ 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96,
+ 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+ # 0xFDD0 to 0xFDEF
+ 0xfdd0, 0xfdd1, 0xfdd2, 0xfdd3, 0xfdd4, 0xfdd5, 0xfdd6, 0xfdd7, 0xfdd8,
+ 0xfdd9, 0xfdda, 0xfddb, 0xfddc, 0xfddd, 0xfdde, 0xfddf, 0xfde0, 0xfde1,
+ 0xfde2, 0xfde3, 0xfde4, 0xfde5, 0xfde6, 0xfde7, 0xfde8, 0xfde9, 0xfdea,
+ 0xfdeb, 0xfdec, 0xfded, 0xfdee, 0xfdef,
+ # others
+ 0xb, 0xfffe, 0xffff, 0x1fffe, 0x1ffff, 0x2fffe, 0x2ffff, 0x3fffe, 0x3ffff,
+ 0x4fffe, 0x4ffff, 0x5fffe, 0x5ffff, 0x6fffe, 0x6ffff, 0x7fffe, 0x7ffff,
+ 0x8fffe, 0x8ffff, 0x9fffe, 0x9ffff, 0xafffe, 0xaffff, 0xbfffe, 0xbffff,
+ 0xcfffe, 0xcffff, 0xdfffe, 0xdffff, 0xefffe, 0xeffff, 0xffffe, 0xfffff,
+ 0x10fffe, 0x10ffff
+}
+
+
+def _replace_charref(s):
+ s = s.group(1)
+ if s[0] == '#':
+ # numeric charref
+ if s[1] in 'xX':
+ num = int(s[2:].rstrip(';'), 16)
+ else:
+ num = int(s[1:].rstrip(';'))
+ if num in _invalid_charrefs:
+ return _invalid_charrefs[num]
+ if 0xD800 <= num <= 0xDFFF or num > 0x10FFFF:
+ return '\uFFFD'
+ if num in _invalid_codepoints:
+ return ''
+ return chr(num)
+ else:
+ # named charref
+ if s in _html5:
+ return _html5[s]
+ # find the longest matching name (as defined by the standard)
+ for x in range(len(s)-1, 1, -1):
+ if s[:x] in _html5:
+ return _html5[s[:x]] + s[x:]
+ else:
+ return '&' + s
+
+
+_charref = _re.compile(r'&(#[0-9]+;?'
+ r'|#[xX][0-9a-fA-F]+;?'
+ r'|[^\t\n\f <&#;]{1,32};?)')
+
+def unescape(s):
+ """
+ Convert all named and numeric character references (e.g. &gt;, &#62;,
+ &x3e;) in the string s to the corresponding unicode characters.
+ This function uses the rules defined by the HTML 5 standard
+ for both valid and invalid character references, and the list of
+ HTML 5 named character references defined in html.entities.html5.
+ """
+ if '&' not in s:
+ return s
+ return _charref.sub(_replace_charref, s)
diff --git a/Lib/html/parser.py b/Lib/html/parser.py
index 63fe774..a650d5e 100644
--- a/Lib/html/parser.py
+++ b/Lib/html/parser.py
@@ -8,9 +8,14 @@
# and CDATA (character data -- only end tags are special).
-import _markupbase
import re
import warnings
+import _markupbase
+
+from html import unescape
+
+
+__all__ = ['HTMLParser']
# Regular expressions used for parsing
@@ -92,6 +97,8 @@ class HTMLParseError(Exception):
return result
+_default_sentinel = object()
+
class HTMLParser(_markupbase.ParserBase):
"""Find tags and other markup and call handler functions.
@@ -105,26 +112,39 @@ class HTMLParser(_markupbase.ParserBase):
self.handle_startendtag(); end tags by self.handle_endtag(). The
data between tags is passed from the parser to the derived class
by calling self.handle_data() with the data as argument (the data
- may be split up in arbitrary chunks). Entity references are
- passed by calling self.handle_entityref() with the entity
- reference as the argument. Numeric character references are
- passed to self.handle_charref() with the string containing the
- reference as the argument.
+ may be split up in arbitrary chunks). If convert_charrefs is
+ True the character references are converted automatically to the
+ corresponding Unicode character (and self.handle_data() is no
+ longer split in chunks), otherwise they are passed by calling
+ self.handle_entityref() or self.handle_charref() with the string
+ containing respectively the named or numeric reference as the
+ argument.
"""
CDATA_CONTENT_ELEMENTS = ("script", "style")
- def __init__(self, strict=False):
+ def __init__(self, strict=_default_sentinel, *,
+ convert_charrefs=_default_sentinel):
"""Initialize and reset this instance.
+ If convert_charrefs is True (default: False), all character references
+ are automatically converted to the corresponding Unicode characters.
If strict is set to False (the default) the parser will parse invalid
markup, otherwise it will raise an error. Note that the strict mode
- is deprecated.
+ and argument are deprecated.
"""
- if strict:
- warnings.warn("The strict mode is deprecated.",
+ if strict is not _default_sentinel:
+ warnings.warn("The strict argument and mode are deprecated.",
DeprecationWarning, stacklevel=2)
+ else:
+ strict = False # default
self.strict = strict
+ if convert_charrefs is _default_sentinel:
+ convert_charrefs = False # default
+ warnings.warn("The value of convert_charrefs will become True in "
+ "3.5. You are encouraged to set the value explicitly.",
+ DeprecationWarning, stacklevel=2)
+ self.convert_charrefs = convert_charrefs
self.reset()
def reset(self):
@@ -149,6 +169,8 @@ class HTMLParser(_markupbase.ParserBase):
self.goahead(1)
def error(self, message):
+ warnings.warn("The 'error' method is deprecated.",
+ DeprecationWarning, stacklevel=2)
raise HTMLParseError(message, self.getpos())
__starttag_text = None
@@ -173,14 +195,25 @@ class HTMLParser(_markupbase.ParserBase):
i = 0
n = len(rawdata)
while i < n:
- match = self.interesting.search(rawdata, i) # < or &
- if match:
- j = match.start()
+ if self.convert_charrefs and not self.cdata_elem:
+ j = rawdata.find('<', i)
+ if j < 0:
+ if not end:
+ break # wait till we get all the text
+ j = n
else:
- if self.cdata_elem:
- break
- j = n
- if i < j: self.handle_data(rawdata[i:j])
+ match = self.interesting.search(rawdata, i) # < or &
+ if match:
+ j = match.start()
+ else:
+ if self.cdata_elem:
+ break
+ j = n
+ if i < j:
+ if self.convert_charrefs and not self.cdata_elem:
+ self.handle_data(unescape(rawdata[i:j]))
+ else:
+ self.handle_data(rawdata[i:j])
i = self.updatepos(i, j)
if i == n: break
startswith = rawdata.startswith
@@ -215,7 +248,10 @@ class HTMLParser(_markupbase.ParserBase):
k = i + 1
else:
k += 1
- self.handle_data(rawdata[i:k])
+ if self.convert_charrefs and not self.cdata_elem:
+ self.handle_data(unescape(rawdata[i:k]))
+ else:
+ self.handle_data(rawdata[i:k])
i = self.updatepos(i, k)
elif startswith("&#", i):
match = charref.match(rawdata, i)
@@ -266,7 +302,10 @@ class HTMLParser(_markupbase.ParserBase):
assert 0, "interesting.search() lied"
# end while
if end and i < n and not self.cdata_elem:
- self.handle_data(rawdata[i:n])
+ if self.convert_charrefs and not self.cdata_elem:
+ self.handle_data(unescape(rawdata[i:n]))
+ else:
+ self.handle_data(rawdata[i:n])
i = self.updatepos(i, n)
self.rawdata = rawdata[i:]
@@ -349,7 +388,7 @@ class HTMLParser(_markupbase.ParserBase):
attrvalue[:1] == '"' == attrvalue[-1:]:
attrvalue = attrvalue[1:-1]
if attrvalue:
- attrvalue = self.unescape(attrvalue)
+ attrvalue = unescape(attrvalue)
attrs.append((attrname.lower(), attrvalue))
k = m.end()
@@ -505,31 +544,7 @@ class HTMLParser(_markupbase.ParserBase):
# Internal -- helper to remove special character quoting
def unescape(self, s):
- if '&' not in s:
- return s
- def replaceEntities(s):
- s = s.groups()[0]
- try:
- if s[0] == "#":
- s = s[1:]
- if s[0] in ['x','X']:
- c = int(s[1:].rstrip(';'), 16)
- else:
- c = int(s.rstrip(';'))
- return chr(c)
- except ValueError:
- return '&#' + s
- else:
- from html.entities import html5
- if s in html5:
- return html5[s]
- elif s.endswith(';'):
- return '&' + s
- for x in range(2, len(s)):
- if s[:x] in html5:
- return html5[s[:x]] + s[x:]
- else:
- return '&' + s
-
- return re.sub(r"&(#?[xX]?(?:[0-9a-fA-F]+;|\w{1,32};?))",
- replaceEntities, s, flags=re.ASCII)
+ warnings.warn('The unescape method is deprecated and will be removed '
+ 'in 3.5, use html.unescape() instead.',
+ DeprecationWarning, stacklevel=2)
+ return unescape(s)
diff --git a/Lib/http/client.py b/Lib/http/client.py
index e05c84d..d2013f2 100644
--- a/Lib/http/client.py
+++ b/Lib/http/client.py
@@ -73,7 +73,6 @@ import os
import socket
import collections
from urllib.parse import urlsplit
-import warnings
__all__ = ["HTTPResponse", "HTTPConnection",
"HTTPException", "NotConnected", "UnknownProtocol",
@@ -271,8 +270,6 @@ def parse_headers(fp, _class=HTTPMessage):
return email.parser.Parser(_class=_class).parsestr(hstring)
-_strict_sentinel = object()
-
class HTTPResponse(io.RawIOBase):
# See RFC 2616 sec 19.6 and RFC 1945 sec 6 for details.
@@ -282,7 +279,7 @@ class HTTPResponse(io.RawIOBase):
# text following RFC 2047. The basic status line parsing only
# accepts iso-8859-1.
- def __init__(self, sock, debuglevel=0, strict=_strict_sentinel, method=None, url=None):
+ def __init__(self, sock, debuglevel=0, method=None, url=None):
# If the response includes a content-length header, we need to
# make sure that the client doesn't read more than the
# specified number of bytes. If it does, it will block until
@@ -292,10 +289,6 @@ class HTTPResponse(io.RawIOBase):
# clients unless they know what they are doing.
self.fp = sock.makefile("rb")
self.debuglevel = debuglevel
- if strict is not _strict_sentinel:
- warnings.warn("the 'strict' argument isn't supported anymore; "
- "http.client now always assumes HTTP/1.x compliant servers.",
- DeprecationWarning, 2)
self._method = method
# The HTTPResponse object is returned via urllib. The clients
@@ -732,13 +725,17 @@ class HTTPConnection:
default_port = HTTP_PORT
auto_open = 1
debuglevel = 0
-
- def __init__(self, host, port=None, strict=_strict_sentinel,
- timeout=socket._GLOBAL_DEFAULT_TIMEOUT, source_address=None):
- if strict is not _strict_sentinel:
- warnings.warn("the 'strict' argument isn't supported anymore; "
- "http.client now always assumes HTTP/1.x compliant servers.",
- DeprecationWarning, 2)
+ # TCP Maximum Segment Size (MSS) is determined by the TCP stack on
+ # a per-connection basis. There is no simple and efficient
+ # platform independent mechanism for determining the MSS, so
+ # instead a reasonable estimate is chosen. The getsockopt()
+ # interface using the TCP_MAXSEG parameter may be a suitable
+ # approach on some operating systems. A value of 16KiB is chosen
+ # as a reasonable estimate of the maximum MSS.
+ mss = 16384
+
+ def __init__(self, host, port=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
+ source_address=None):
self.timeout = timeout
self.source_address = source_address
self.sock = None
@@ -750,14 +747,30 @@ class HTTPConnection:
self._tunnel_port = None
self._tunnel_headers = {}
- self._set_hostport(host, port)
+ (self.host, self.port) = self._get_hostport(host, port)
+
+ # This is stored as an instance variable to allow unit
+ # tests to replace it with a suitable mockup
+ self._create_connection = socket.create_connection
def set_tunnel(self, host, port=None, headers=None):
- """ Sets up the host and the port for the HTTP CONNECT Tunnelling.
+ """Set up host and port for HTTP CONNECT tunnelling.
+
+ In a connection that uses HTTP CONNECT tunneling, the host passed to the
+ constructor is used as a proxy server that relays all communication to
+ the endpoint passed to `set_tunnel`. This done by sending an HTTP
+ CONNECT request to the proxy server when the connection is established.
+
+ This method must be called before the HTML connection has been
+ established.
- The headers argument should be a mapping of extra HTTP headers
- to send with the CONNECT request.
+ The headers argument should be a mapping of extra HTTP headers to send
+ with the CONNECT request.
"""
+
+ if self.sock:
+ raise RuntimeError("Can't set up tunnel for established connection")
+
self._tunnel_host = host
self._tunnel_port = port
if headers:
@@ -765,7 +778,7 @@ class HTTPConnection:
else:
self._tunnel_headers.clear()
- def _set_hostport(self, host, port):
+ def _get_hostport(self, host, port):
if port is None:
i = host.rfind(':')
j = host.rfind(']') # ipv6 addresses have [...]
@@ -782,15 +795,16 @@ class HTTPConnection:
port = self.default_port
if host and host[0] == '[' and host[-1] == ']':
host = host[1:-1]
- self.host = host
- self.port = port
+
+ return (host, port)
def set_debuglevel(self, level):
self.debuglevel = level
def _tunnel(self):
- self._set_hostport(self._tunnel_host, self._tunnel_port)
- connect_str = "CONNECT %s:%d HTTP/1.0\r\n" % (self.host, self.port)
+ (host, port) = self._get_hostport(self._tunnel_host,
+ self._tunnel_port)
+ connect_str = "CONNECT %s:%d HTTP/1.0\r\n" % (host, port)
connect_bytes = connect_str.encode("ascii")
self.send(connect_bytes)
for header, value in self._tunnel_headers.items():
@@ -804,8 +818,8 @@ class HTTPConnection:
if code != 200:
self.close()
- raise socket.error("Tunnel connection failed: %d %s" % (code,
- message.strip()))
+ raise OSError("Tunnel connection failed: %d %s" % (code,
+ message.strip()))
while True:
line = response.fp.readline(_MAXLINE + 1)
if len(line) > _MAXLINE:
@@ -818,8 +832,9 @@ class HTTPConnection:
def connect(self):
"""Connect to the host and port specified in __init__."""
- self.sock = socket.create_connection((self.host,self.port),
- self.timeout, self.source_address)
+ self.sock = self._create_connection((self.host,self.port),
+ self.timeout, self.source_address)
+
if self._tunnel_host:
self._tunnel()
@@ -899,8 +914,11 @@ class HTTPConnection:
del self._buffer[:]
# If msg and message_body are sent in a single send() call,
# it will avoid performance problems caused by the interaction
- # between delayed ack and the Nagle algorithm.
- if isinstance(message_body, bytes):
+ # between delayed ack and the Nagle algorithm. However,
+ # there is no performance gain if the message is larger
+ # than MSS (and there is a memory penalty for the message
+ # copy).
+ if isinstance(message_body, bytes) and len(message_body) < self.mss:
msg += message_body
message_body = None
self.send(msg)
@@ -985,22 +1003,29 @@ class HTTPConnection:
netloc_enc = netloc.encode("idna")
self.putheader('Host', netloc_enc)
else:
+ if self._tunnel_host:
+ host = self._tunnel_host
+ port = self._tunnel_port
+ else:
+ host = self.host
+ port = self.port
+
try:
- host_enc = self.host.encode("ascii")
+ host_enc = host.encode("ascii")
except UnicodeEncodeError:
- host_enc = self.host.encode("idna")
+ host_enc = host.encode("idna")
# As per RFC 273, IPv6 address should be wrapped with []
# when used as Host header
- if self.host.find(':') >= 0:
+ if host.find(':') >= 0:
host_enc = b'[' + host_enc + b']'
- if self.port == self.default_port:
+ if port == self.default_port:
self.putheader('Host', host_enc)
else:
host_enc = host_enc.decode("ascii")
- self.putheader('Host', "%s:%s" % (host_enc, self.port))
+ self.putheader('Host', "%s:%s" % (host_enc, port))
# note: we are assuming that clients will not attempt to set these
# headers since *this* library must deal with the
@@ -1170,16 +1195,15 @@ else:
# XXX Should key_file and cert_file be deprecated in favour of context?
def __init__(self, host, port=None, key_file=None, cert_file=None,
- strict=_strict_sentinel, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
- source_address=None, *, context=None, check_hostname=None):
- super(HTTPSConnection, self).__init__(host, port, strict, timeout,
+ timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
+ source_address=None, *, context=None,
+ check_hostname=None):
+ super(HTTPSConnection, self).__init__(host, port, timeout,
source_address)
self.key_file = key_file
self.cert_file = cert_file
if context is None:
- # Some reasonable defaults
- context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
- context.options |= ssl.OP_NO_SSLv2
+ context = ssl._create_stdlib_context()
will_verify = context.verify_mode != ssl.CERT_NONE
if check_hostname is None:
check_hostname = will_verify
@@ -1194,23 +1218,23 @@ else:
def connect(self):
"Connect to a host on a given (SSL) port."
- sock = socket.create_connection((self.host, self.port),
- self.timeout, self.source_address)
+ super().connect()
if self._tunnel_host:
- self.sock = sock
- self._tunnel()
+ server_hostname = self._tunnel_host
+ else:
+ server_hostname = self.host
+ sni_hostname = server_hostname if ssl.HAS_SNI else None
- server_hostname = self.host if ssl.HAS_SNI else None
- self.sock = self._context.wrap_socket(sock,
- server_hostname=server_hostname)
- try:
- if self._check_hostname:
- ssl.match_hostname(self.sock.getpeercert(), self.host)
- except Exception:
- self.sock.shutdown(socket.SHUT_RDWR)
- self.sock.close()
- raise
+ self.sock = self._context.wrap_socket(self.sock,
+ server_hostname=sni_hostname)
+ if not self._context.check_hostname and self._check_hostname:
+ try:
+ ssl.match_hostname(self.sock.getpeercert(), server_hostname)
+ except Exception:
+ self.sock.shutdown(socket.SHUT_RDWR)
+ self.sock.close()
+ raise
__all__.append("HTTPSConnection")
diff --git a/Lib/http/cookiejar.py b/Lib/http/cookiejar.py
index 9fcd4c6..4dc468b 100644
--- a/Lib/http/cookiejar.py
+++ b/Lib/http/cookiejar.py
@@ -1193,8 +1193,7 @@ def deepvalues(mapping):
pass
else:
mapping = True
- for subobj in deepvalues(obj):
- yield subobj
+ yield from deepvalues(obj)
if not mapping:
yield obj
@@ -1731,8 +1730,8 @@ class CookieJar:
return "<%s[%s]>" % (self.__class__, ", ".join(r))
-# derives from IOError for backwards-compatibility with Python 2.4.0
-class LoadError(IOError): pass
+# derives from OSError for backwards-compatibility with Python 2.4.0
+class LoadError(OSError): pass
class FileCookieJar(CookieJar):
"""CookieJar that can be loaded from and saved to a file."""
@@ -1762,17 +1761,14 @@ class FileCookieJar(CookieJar):
if self.filename is not None: filename = self.filename
else: raise ValueError(MISSING_FILENAME_TEXT)
- f = open(filename)
- try:
+ with open(filename) as f:
self._really_load(f, filename, ignore_discard, ignore_expires)
- finally:
- f.close()
def revert(self, filename=None,
ignore_discard=False, ignore_expires=False):
"""Clear all cookies and reload cookies from a saved file.
- Raises LoadError (or IOError) if reversion is not successful; the
+ Raises LoadError (or OSError) if reversion is not successful; the
object's state will not be altered if this happens.
"""
@@ -1787,7 +1783,7 @@ class FileCookieJar(CookieJar):
self._cookies = {}
try:
self.load(filename, ignore_discard, ignore_expires)
- except (LoadError, IOError):
+ except OSError:
self._cookies = old_state
raise
@@ -1857,15 +1853,12 @@ class LWPCookieJar(FileCookieJar):
if self.filename is not None: filename = self.filename
else: raise ValueError(MISSING_FILENAME_TEXT)
- f = open(filename, "w")
- try:
+ with open(filename, "w") as f:
# There really isn't an LWP Cookies 2.0 format, but this indicates
# that there is extra information in here (domain_dot and
# port_spec) while still being compatible with libwww-perl, I hope.
f.write("#LWP-Cookies-2.0\n")
f.write(self.as_lwp_str(ignore_discard, ignore_expires))
- finally:
- f.close()
def _really_load(self, f, filename, ignore_discard, ignore_expires):
magic = f.readline()
@@ -1938,8 +1931,7 @@ class LWPCookieJar(FileCookieJar):
if not ignore_expires and c.is_expired(now):
continue
self.set_cookie(c)
-
- except IOError:
+ except OSError:
raise
except Exception:
_warn_unhandled_exception()
@@ -2045,7 +2037,7 @@ class MozillaCookieJar(FileCookieJar):
continue
self.set_cookie(c)
- except IOError:
+ except OSError:
raise
except Exception:
_warn_unhandled_exception()
@@ -2057,8 +2049,7 @@ class MozillaCookieJar(FileCookieJar):
if self.filename is not None: filename = self.filename
else: raise ValueError(MISSING_FILENAME_TEXT)
- f = open(filename, "w")
- try:
+ with open(filename, "w") as f:
f.write(self.header)
now = time.time()
for cookie in self:
@@ -2087,5 +2078,3 @@ class MozillaCookieJar(FileCookieJar):
"\t".join([cookie.domain, initial_dot, cookie.path,
secure, expires, name, value])+
"\n")
- finally:
- f.close()
diff --git a/Lib/http/server.py b/Lib/http/server.py
index dab1eb6..6ce6bda 100644
--- a/Lib/http/server.py
+++ b/Lib/http/server.py
@@ -85,8 +85,6 @@ __version__ = "0.6"
__all__ = ["HTTPServer", "BaseHTTPRequestHandler"]
import html
-import email.message
-import email.parser
import http.client
import io
import mimetypes
@@ -401,12 +399,17 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
while not self.close_connection:
self.handle_one_request()
- def send_error(self, code, message=None):
+ def send_error(self, code, message=None, explain=None):
"""Send and log an error reply.
- Arguments are the error code, and a detailed message.
- The detailed message defaults to the short entry matching the
- response code.
+ Arguments are
+ * code: an HTTP error code
+ 3 digits
+ * message: a simple optional 1 line reason phrase.
+ *( HTAB / SP / VCHAR / %x80-FF )
+ defaults to short entry matching the response code
+ * explain: a detailed message defaults to the long entry
+ matching the response code.
This sends an error response (so it must be called before any
output has been generated), logs the error, and finally sends
@@ -420,17 +423,20 @@ class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
shortmsg, longmsg = '???', '???'
if message is None:
message = shortmsg
- explain = longmsg
+ if explain is None:
+ explain = longmsg
self.log_error("code %d, message %s", code, message)
# using _quote_html to prevent Cross Site Scripting attacks (see bug #1100201)
content = (self.error_message_format %
- {'code': code, 'message': _quote_html(message), 'explain': explain})
+ {'code': code, 'message': _quote_html(message), 'explain': _quote_html(explain)})
+ body = content.encode('UTF-8', 'replace')
self.send_response(code, message)
self.send_header("Content-Type", self.error_content_type)
self.send_header('Connection', 'close')
+ self.send_header('Content-Length', int(len(body)))
self.end_headers()
if self.command != 'HEAD' and code >= 200 and code not in (204, 304):
- self.wfile.write(content.encode('UTF-8', 'replace'))
+ self.wfile.write(body)
def send_response(self, code, message=None):
"""Add the response header to the headers buffer and log the
@@ -711,7 +717,7 @@ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
ctype = self.guess_type(path)
try:
f = open(path, 'rb')
- except IOError:
+ except OSError:
self.send_error(404, "File not found")
return None
try:
@@ -736,7 +742,7 @@ class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
"""
try:
list = os.listdir(path)
- except os.error:
+ except OSError:
self.send_error(404, "No permission to list directory")
return None
list.sort(key=lambda a: a.lower())
@@ -1130,7 +1136,7 @@ class CGIHTTPRequestHandler(SimpleHTTPRequestHandler):
try:
try:
os.setuid(nobody)
- except os.error:
+ except OSError:
pass
os.dup2(self.rfile.fileno(), 0)
os.dup2(self.wfile.fileno(), 1)
@@ -1183,15 +1189,15 @@ class CGIHTTPRequestHandler(SimpleHTTPRequestHandler):
self.log_message("CGI script exited OK")
-def test(HandlerClass = BaseHTTPRequestHandler,
- ServerClass = HTTPServer, protocol="HTTP/1.0", port=8000):
+def test(HandlerClass=BaseHTTPRequestHandler,
+ ServerClass=HTTPServer, protocol="HTTP/1.0", port=8000, bind=""):
"""Test the HTTP request handler class.
This runs an HTTP server on port 8000 (or the first command line
argument).
"""
- server_address = ('', port)
+ server_address = (bind, port)
HandlerClass.protocol_version = protocol
httpd = ServerClass(server_address, HandlerClass)
@@ -1209,12 +1215,16 @@ if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--cgi', action='store_true',
help='Run as CGI Server')
+ parser.add_argument('--bind', '-b', default='', metavar='ADDRESS',
+ help='Specify alternate bind address '
+ '[default: all interfaces]')
parser.add_argument('port', action='store',
default=8000, type=int,
nargs='?',
help='Specify alternate port [default: 8000]')
args = parser.parse_args()
if args.cgi:
- test(HandlerClass=CGIHTTPRequestHandler, port=args.port)
+ handler_class = CGIHTTPRequestHandler
else:
- test(HandlerClass=SimpleHTTPRequestHandler, port=args.port)
+ handler_class = SimpleHTTPRequestHandler
+ test(HandlerClass=handler_class, port=args.port, bind=args.bind)
diff --git a/Lib/idlelib/Bindings.py b/Lib/idlelib/Bindings.py
index 65c0317..df2b251 100644
--- a/Lib/idlelib/Bindings.py
+++ b/Lib/idlelib/Bindings.py
@@ -8,9 +8,14 @@ the PythonShell window, and a Format menu which is only present in the Editor
windows.
"""
-import sys
from idlelib.configHandler import idleConf
-from idlelib import macosxSupport
+
+# Warning: menudefs is altered in macosxSupport.overrideRootMenu()
+# after it is determined that an OS X Aqua Tk is in use,
+# which cannot be done until after Tk() is first called.
+# Do not alter the 'file', 'options', or 'help' cascades here
+# without altering overrideRootMenu() as well.
+# TODO: Make this more robust
menudefs = [
# underscore prefixes character to underscore
@@ -81,27 +86,4 @@ menudefs = [
]),
]
-if macosxSupport.runningAsOSXApp():
- # Running as a proper MacOS application bundle. This block restructures
- # the menus a little to make them conform better to the HIG.
-
- quitItem = menudefs[0][1][-1]
- closeItem = menudefs[0][1][-2]
-
- # Remove the last 3 items of the file menu: a separator, close window and
- # quit. Close window will be reinserted just above the save item, where
- # it should be according to the HIG. Quit is in the application menu.
- del menudefs[0][1][-3:]
- menudefs[0][1].insert(6, closeItem)
-
- # Remove the 'About' entry from the help menu, it is in the application
- # menu
- del menudefs[-1][1][0:2]
-
- # Remove the 'Configure' entry from the options menu, it is in the
- # application menu as 'Preferences'
- del menudefs[-2][1][0:2]
-
default_keydefs = idleConf.GetCurrentKeySet()
-
-del sys
diff --git a/Lib/idlelib/Debugger.py b/Lib/idlelib/Debugger.py
index d4872ed..ca98b10 100644
--- a/Lib/idlelib/Debugger.py
+++ b/Lib/idlelib/Debugger.py
@@ -322,7 +322,7 @@ class Debugger:
class StackViewer(ScrolledList):
def __init__(self, master, flist, gui):
- if macosxSupport.runningAsOSXApp():
+ if macosxSupport.isAquaTk():
# At least on with the stock AquaTk version on OSX 10.4 you'll
# get an shaking GUI that eventually kills IDLE if the width
# argument is specified.
diff --git a/Lib/idlelib/EditorWindow.py b/Lib/idlelib/EditorWindow.py
index 4bf1111..cdb6775 100644
--- a/Lib/idlelib/EditorWindow.py
+++ b/Lib/idlelib/EditorWindow.py
@@ -1,5 +1,6 @@
import importlib
import importlib.abc
+import importlib.util
import os
from platform import python_version
import re
@@ -108,8 +109,8 @@ class EditorWindow(object):
'Python%s.chm' % _sphinx_version())
if os.path.isfile(chmfile):
dochome = chmfile
- elif macosxSupport.runningAsOSXApp():
- # documentation is stored inside the python framework
+ elif sys.platform == 'darwin':
+ # documentation may be stored inside a python framework
dochome = os.path.join(sys.base_prefix,
'Resources/English.lproj/Documentation/index.html')
dochome = os.path.normpath(dochome)
@@ -165,7 +166,7 @@ class EditorWindow(object):
self.top.protocol("WM_DELETE_WINDOW", self.close)
self.top.bind("<<close-window>>", self.close_event)
- if macosxSupport.runningAsOSXApp():
+ if macosxSupport.isAquaTk():
# Command-W on editorwindows doesn't work without this.
text.bind('<<close-window>>', self.close_event)
# Some OS X systems have only one mouse button,
@@ -408,7 +409,7 @@ class EditorWindow(object):
def set_status_bar(self):
self.status_bar = self.MultiStatusBar(self.top)
- if macosxSupport.runningAsOSXApp():
+ if sys.platform == "darwin":
# Insert some padding to avoid obscuring some of the statusbar
# by the resize widget.
self.status_bar.set_label('_padding1', ' ', side=RIGHT)
@@ -435,7 +436,7 @@ class EditorWindow(object):
("help", "_Help"),
]
- if macosxSupport.runningAsOSXApp():
+ if sys.platform == "darwin":
menu_specs[-2] = ("windows", "_Window")
@@ -446,7 +447,7 @@ class EditorWindow(object):
underline, label = prepstr(label)
menudict[name] = menu = Menu(mbar, name=name)
mbar.add_cascade(label=label, menu=menu, underline=underline)
- if macosxSupport.isCarbonAquaTk(self.root):
+ if macosxSupport.isCarbonTk():
# Insert the application menu
menudict['application'] = menu = Menu(mbar, name='apple')
mbar.add_cascade(label='IDLE', menu=menu)
@@ -549,7 +550,7 @@ class EditorWindow(object):
if sys.platform[:3] == 'win':
try:
os.startfile(self.help_url)
- except WindowsError as why:
+ except OSError as why:
tkMessageBox.showerror(title='Document Start Failure',
message=str(why), parent=self.text)
else:
@@ -660,20 +661,20 @@ class EditorWindow(object):
return
# XXX Ought to insert current file's directory in front of path
try:
- loader = importlib.find_loader(name)
+ spec = importlib.util.find_spec(name)
except (ValueError, ImportError) as msg:
tkMessageBox.showerror("Import error", str(msg), parent=self.text)
return
- if loader is None:
+ if spec is None:
tkMessageBox.showerror("Import error", "module not found",
parent=self.text)
return
- if not isinstance(loader, importlib.abc.SourceLoader):
+ if not isinstance(spec.loader, importlib.abc.SourceLoader):
tkMessageBox.showerror("Import error", "not a source-based module",
parent=self.text)
return
try:
- file_path = loader.get_filename(name)
+ file_path = spec.loader.get_filename(name)
except AttributeError:
tkMessageBox.showerror("Import error",
"loader does not support get_filename",
@@ -872,7 +873,7 @@ class EditorWindow(object):
if sys.platform[:3] == 'win':
try:
os.startfile(helpfile)
- except WindowsError as why:
+ except OSError as why:
tkMessageBox.showerror(title='Document Start Failure',
message=str(why), parent=self.text)
else:
@@ -1672,7 +1673,7 @@ def get_accelerator(keydefs, eventname):
keylist = keydefs.get(eventname)
# issue10940: temporary workaround to prevent hang with OS X Cocoa Tk 8.5
# if not keylist:
- if (not keylist) or (macosxSupport.runningAsOSXApp() and eventname in {
+ if (not keylist) or (macosxSupport.isCocoaTk() and eventname in {
"<<open-module>>",
"<<goto-line>>",
"<<change-indentwidth>>"}):
diff --git a/Lib/idlelib/FileList.py b/Lib/idlelib/FileList.py
index 37a337e..a9989a8 100644
--- a/Lib/idlelib/FileList.py
+++ b/Lib/idlelib/FileList.py
@@ -103,7 +103,7 @@ class FileList:
if not os.path.isabs(filename):
try:
pwd = os.getcwd()
- except os.error:
+ except OSError:
pass
else:
filename = os.path.join(pwd, filename)
diff --git a/Lib/idlelib/FormatParagraph.py b/Lib/idlelib/FormatParagraph.py
index ae4e6e7..2ac87e4 100644
--- a/Lib/idlelib/FormatParagraph.py
+++ b/Lib/idlelib/FormatParagraph.py
@@ -32,7 +32,7 @@ class FormatParagraph:
def close(self):
self.editwin = None
- def format_paragraph_event(self, event):
+ def format_paragraph_event(self, event, limit=None):
"""Formats paragraph to a max width specified in idleConf.
If text is selected, format_paragraph_event will start breaking lines
@@ -41,9 +41,12 @@ class FormatParagraph:
If no text is selected, format_paragraph_event uses the current
cursor location to determine the paragraph (lines of text surrounded
by blank lines) and formats it.
+
+ The length limit parameter is for testing with a known value.
"""
- maxformatwidth = idleConf.GetOption(
- 'main', 'FormatParagraph', 'paragraph', type='int')
+ if limit == None:
+ limit = idleConf.GetOption(
+ 'main', 'FormatParagraph', 'paragraph', type='int')
text = self.editwin.text
first, last = self.editwin.get_selection_indices()
if first and last:
@@ -53,9 +56,9 @@ class FormatParagraph:
first, last, comment_header, data = \
find_paragraph(text, text.index("insert"))
if comment_header:
- newdata = reformat_comment(data, maxformatwidth, comment_header)
+ newdata = reformat_comment(data, limit, comment_header)
else:
- newdata = reformat_paragraph(data, maxformatwidth)
+ newdata = reformat_paragraph(data, limit)
text.tag_remove("sel", "1.0", "end")
if newdata != data:
diff --git a/Lib/idlelib/GrepDialog.py b/Lib/idlelib/GrepDialog.py
index c359074..f73d70a 100644
--- a/Lib/idlelib/GrepDialog.py
+++ b/Lib/idlelib/GrepDialog.py
@@ -125,4 +125,3 @@ if __name__ == "__main__":
# Hence Idle must be restarted after editing this file for a live test.
import unittest
unittest.main('idlelib.idle_test.test_grep', verbosity=2, exit=False)
-
diff --git a/Lib/idlelib/IOBinding.py b/Lib/idlelib/IOBinding.py
index f008b46..3cd7a4c 100644
--- a/Lib/idlelib/IOBinding.py
+++ b/Lib/idlelib/IOBinding.py
@@ -506,7 +506,7 @@ class IOBinding:
else:
try:
pwd = os.getcwd()
- except os.error:
+ except OSError:
pwd = ""
return pwd, ""
diff --git a/Lib/idlelib/MultiCall.py b/Lib/idlelib/MultiCall.py
index 64729ea..cc6bffd 100644
--- a/Lib/idlelib/MultiCall.py
+++ b/Lib/idlelib/MultiCall.py
@@ -32,7 +32,6 @@ Each function will be called at most once for each event.
import sys
import re
import tkinter
-from idlelib import macosxSupport
# the event type constants, which define the meaning of mc_type
MC_KEYPRESS=0; MC_KEYRELEASE=1; MC_BUTTONPRESS=2; MC_BUTTONRELEASE=3;
@@ -45,7 +44,7 @@ MC_SHIFT = 1<<0; MC_CONTROL = 1<<2; MC_ALT = 1<<3; MC_META = 1<<5
MC_OPTION = 1<<6; MC_COMMAND = 1<<7
# define the list of modifiers, to be used in complex event types.
-if macosxSupport.runningAsOSXApp():
+if sys.platform == "darwin":
_modifiers = (("Shift",), ("Control",), ("Option",), ("Command",))
_modifier_masks = (MC_SHIFT, MC_CONTROL, MC_OPTION, MC_COMMAND)
else:
@@ -57,6 +56,13 @@ _modifier_names = dict([(name, number)
for number in range(len(_modifiers))
for name in _modifiers[number]])
+# In 3.4, if no shell window is ever open, the underlying Tk widget is
+# destroyed before .__del__ methods here are called. The following
+# is used to selectively ignore shutdown exceptions to avoid
+# 'Exception ignored' messages. See http://bugs.python.org/issue20167
+APPLICATION_GONE = '''\
+can't invoke "bind" command: application has been destroyed'''
+
# A binder is a class which binds functions to one type of event. It has two
# methods: bind and unbind, which get a function and a parsed sequence, as
# returned by _parse_sequence(). There are two types of binders:
@@ -98,7 +104,14 @@ class _SimpleBinder:
def __del__(self):
if self.handlerid:
- self.widget.unbind(self.widgetinst, self.sequence, self.handlerid)
+ try:
+ self.widget.unbind(self.widgetinst, self.sequence,
+ self.handlerid)
+ except tkinter.TclError as e:
+ if e.args[0] == APPLICATION_GONE:
+ pass
+ else:
+ raise
# An int in range(1 << len(_modifiers)) represents a combination of modifiers
# (if the least significent bit is on, _modifiers[0] is on, and so on).
@@ -227,7 +240,13 @@ class _ComplexBinder:
def __del__(self):
for seq, id in self.handlerids:
- self.widget.unbind(self.widgetinst, seq, id)
+ try:
+ self.widget.unbind(self.widgetinst, seq, id)
+ except tkinter.TclError as e:
+ if e.args[0] == APPLICATION_GONE:
+ break
+ else:
+ raise
# define the list of event types to be handled by MultiEvent. the order is
# compatible with the definition of event type constants.
@@ -390,8 +409,13 @@ def MultiCallCreator(widget):
func, triplets = self.__eventinfo[virtual]
if func:
for triplet in triplets:
- self.__binders[triplet[1]].unbind(triplet, func)
-
+ try:
+ self.__binders[triplet[1]].unbind(triplet, func)
+ except tkinter.TclError as e:
+ if e.args[0] == APPLICATION_GONE:
+ break
+ else:
+ raise
_multicall_dict[widget] = MultiCall
return MultiCall
diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt
index 6388d0d..953a38d 100644
--- a/Lib/idlelib/NEWS.txt
+++ b/Lib/idlelib/NEWS.txt
@@ -1,74 +1,10 @@
-What's New in IDLE 3.3.4?
-=========================
-
-- Issue #17390: Add Python version to Idle editor window title bar.
- Original patches by Edmond Burnett and Kent Johnson.
-
-- Issue #18960: IDLE now ignores the source encoding declaration on the second
- line if the first line contains anything except a comment.
-
-- Issue #20058: sys.stdin.readline() in IDLE now always returns only one line.
-
-- Issue #19481: print() of string subclass instance in IDLE no longer hangs.
-
-- Issue #18270: Prevent possible IDLE AttributeError on OS X when no initial
- shell window is present.
-
-
-What's New in IDLE 3.3.3?
-=========================
-
-- Issue #18873: IDLE now detects Python source code encoding only in comment
- lines.
-
-- Issue #18988: The "Tab" key now works when a word is already autocompleted.
-
-- Issue #18489: Add tests for SearchEngine. Original patch by Phil Webster.
-
-- Issue #18429: Format / Format Paragraph, now works when comment blocks
- are selected. As with text blocks, this works best when the selection
- only includes complete lines.
-
-- Issue #18226: Add docstrings and unittests for FormatParagraph.py.
- Original patches by Todd Rovito and Phil Webster.
-
-- Issue #18279: Format - Strip trailing whitespace no longer marks a file as
- changed when it has not been changed. This fix followed the addition of a
- test file originally written by Phil Webster (the issue's main goal).
-
-- Issue #7136: In the Idle File menu, "New Window" is renamed "New File".
- Patch by Tal Einat, Roget Serwy, and Todd Rovito.
-
-- Remove dead imports of imp.
-
-- Issue #18196: Avoid displaying spurious SystemExit tracebacks.
-
-- Issue #5492: Avoid traceback when exiting IDLE caused by a race condition.
-
-- Issue #17511: Keep IDLE find dialog open after clicking "Find Next".
- Original patch by Sarah K.
-
-- Issue #18055: Move IDLE off of imp and on to importlib.
-
-- Issue #15392: Create a unittest framework for IDLE.
- Initial patch by Rajagopalasarma Jayakrishnan.
- See Lib/idlelib/idle_test/README.txt for how to run Idle tests.
-
-- Issue #14146: Highlight source line while debugging on Windows.
-
-- Issue #17532: Always include Options menu for IDLE on OS X.
- Patch by Guilherme Simões.
-
-
-What's New in IDLE 3.3.2?
+What's New in IDLE 3.4.0?
=========================
- Issue #17390: Display Python version on Idle title bar.
Initial patch by Edmond Burnett.
-
-What's New in IDLE 3.3.1?
-=========================
+- Issue #5066: Update IDLE docs. Patch by Todd Rovito.
- Issue #17625: Close the replace dialog after it is used.
diff --git a/Lib/idlelib/PathBrowser.py b/Lib/idlelib/PathBrowser.py
index ba40719..5e5c6be 100644
--- a/Lib/idlelib/PathBrowser.py
+++ b/Lib/idlelib/PathBrowser.py
@@ -44,7 +44,7 @@ class DirBrowserTreeItem(TreeItem):
def GetSubList(self):
try:
names = os.listdir(self.dir or os.curdir)
- except os.error:
+ except OSError:
return []
packages = []
for name in names:
diff --git a/Lib/idlelib/PyShell.py b/Lib/idlelib/PyShell.py
index 2e5ebb2..c23b62a 100755
--- a/Lib/idlelib/PyShell.py
+++ b/Lib/idlelib/PyShell.py
@@ -419,7 +419,7 @@ class ModifiedInterpreter(InteractiveInterpreter):
try:
self.rpcclt = MyRPCClient(addr)
break
- except socket.error as err:
+ except OSError as err:
pass
else:
self.display_port_binding_error()
@@ -844,7 +844,7 @@ class PyShell(OutputWindow):
("help", "_Help"),
]
- if macosxSupport.runningAsOSXApp():
+ if sys.platform == "darwin":
menu_specs[-2] = ("windows", "_Window")
@@ -1034,7 +1034,10 @@ class PyShell(OutputWindow):
self.close()
return False
else:
- nosub = "==== No Subprocess ===="
+ nosub = ("==== No Subprocess ====\n\n" +
+ "WARNING: Running IDLE without a Subprocess is deprecated\n" +
+ "and will be removed in a later version. See Help/IDLE Help\n" +
+ "for details.\n\n")
sys.displayhook = rpc.displayhook
self.write("Python %s on %s\n%s\n%s" %
@@ -1398,7 +1401,8 @@ USAGE: idle [-deins] [-t title] [file]*
idle [-dns] [-t title] - [arg]*
-h print this help message and exit
- -n run IDLE without a subprocess (see Help/IDLE Help for details)
+ -n run IDLE without a subprocess (DEPRECATED,
+ see Help/IDLE Help for details)
The following options will override the IDLE 'settings' configuration:
@@ -1476,6 +1480,8 @@ def main():
if o == '-i':
enable_shell = True
if o == '-n':
+ print(" Warning: running IDLE without a subprocess is deprecated.",
+ file=sys.stderr)
use_subprocess = False
if o == '-r':
script = a
@@ -1554,7 +1560,7 @@ def main():
shell = flist.open_shell()
if not shell:
return # couldn't open shell
- if macosxSupport.runningAsOSXApp() and flist.dict:
+ if macosxSupport.isAquaTk() and flist.dict:
# On OSX: when the user has double-clicked on a file that causes
# IDLE to be launched the shell window will open just in front of
# the file she wants to see. Lower the interpreter window when
diff --git a/Lib/idlelib/ScriptBinding.py b/Lib/idlelib/ScriptBinding.py
index 6bfe128..b783637 100644
--- a/Lib/idlelib/ScriptBinding.py
+++ b/Lib/idlelib/ScriptBinding.py
@@ -53,7 +53,7 @@ class ScriptBinding:
self.flist = self.editwin.flist
self.root = self.editwin.root
- if macosxSupport.runningAsOSXApp():
+ if macosxSupport.isCocoaTk():
self.editwin.text_frame.bind('<<run-module-event-2>>', self._run_module_event)
def check_module_event(self, event):
@@ -114,7 +114,7 @@ class ScriptBinding:
shell.set_warning_stream(saved_stream)
def run_module_event(self, event):
- if macosxSupport.runningAsOSXApp():
+ if macosxSupport.isCocoaTk():
# Tk-Cocoa in MacOSX is broken until at least
# Tk 8.5.9, and without this rather
# crude workaround IDLE would hang when a user
diff --git a/Lib/idlelib/TreeWidget.py b/Lib/idlelib/TreeWidget.py
index 25bae48..1f4854d 100644
--- a/Lib/idlelib/TreeWidget.py
+++ b/Lib/idlelib/TreeWidget.py
@@ -381,7 +381,7 @@ class FileTreeItem(TreeItem):
try:
os.rename(self.path, newpath)
self.path = newpath
- except os.error:
+ except OSError:
pass
def GetIconName(self):
@@ -394,7 +394,7 @@ class FileTreeItem(TreeItem):
def GetSubList(self):
try:
names = os.listdir(self.path)
- except os.error:
+ except OSError:
return []
names.sort(key = os.path.normcase)
sublist = []
diff --git a/Lib/idlelib/ZoomHeight.py b/Lib/idlelib/ZoomHeight.py
index e8d1710..a5d679e 100644
--- a/Lib/idlelib/ZoomHeight.py
+++ b/Lib/idlelib/ZoomHeight.py
@@ -32,7 +32,7 @@ def zoom_height(top):
newy = 0
newheight = newheight - 72
- elif macosxSupport.runningAsOSXApp():
+ elif macosxSupport.isAquaTk():
# The '88' below is a magic number that avoids placing the bottom
# of the window below the panel on my machine. I don't know how
# to calculate the correct value for this with tkinter.
diff --git a/Lib/idlelib/config-main.def b/Lib/idlelib/config-main.def
index 9546e2b..8f0fe76 100644
--- a/Lib/idlelib/config-main.def
+++ b/Lib/idlelib/config-main.def
@@ -59,7 +59,7 @@ font-bold= 0
encoding= none
[FormatParagraph]
-paragraph=70
+paragraph=72
[Indent]
use-spaces= 1
diff --git a/Lib/idlelib/configDialog.py b/Lib/idlelib/configDialog.py
index efe5c43..fefe42b 100644
--- a/Lib/idlelib/configDialog.py
+++ b/Lib/idlelib/configDialog.py
@@ -74,7 +74,7 @@ class ConfigDialog(Toplevel):
frameActionButtons = Frame(self,pady=2)
#action buttons
- if macosxSupport.runningAsOSXApp():
+ if macosxSupport.isAquaTk():
# Surpress the padx and pady arguments when
# running as IDLE.app, otherwise the text
# on these buttons will not be readable.
diff --git a/Lib/idlelib/configHandler.py b/Lib/idlelib/configHandler.py
index a974d54..8608f7c 100644
--- a/Lib/idlelib/configHandler.py
+++ b/Lib/idlelib/configHandler.py
@@ -20,7 +20,6 @@ configuration problem notification and resolution.
import os
import sys
-from idlelib import macosxSupport
from configparser import ConfigParser, NoOptionError, NoSectionError
class InvalidConfigType(Exception): pass
@@ -271,8 +270,10 @@ class IdleConf:
except OSError:
pass
return default
+
def SetOption(self, configType, section, option, value):
"""In user's config file, set section's option to value.
+
"""
self.userCfg[configType].SetOption(section, option, value)
@@ -525,10 +526,13 @@ class IdleConf:
def GetCurrentKeySet(self):
result = self.GetKeySet(self.CurrentKeys())
- if macosxSupport.runningAsOSXApp():
- # We're using AquaTk, replace all keybingings that use the
- # Alt key by ones that use the Option key because the former
- # don't work reliably.
+ if sys.platform == "darwin":
+ # OS X Tk variants do not support the "Alt" keyboard modifier.
+ # So replace all keybingings that use "Alt" with ones that
+ # use the "Option" keyboard modifier.
+ # TO DO: the "Option" modifier does not work properly for
+ # Cocoa Tk and XQuartz Tk so we should not use it
+ # in default OS X KeySets.
for k, v in result.items():
v2 = [ x.replace('<Alt-', '<Option-') for x in v ]
if v != v2:
@@ -638,8 +642,10 @@ class IdleConf:
except OSError:
pass
return keyBindings
+
def GetExtraHelpSourceList(self,configSet):
"""Fetch list of extra help sources from a given configSet.
+
Valid configSets are 'user' or 'default'. Return a list of tuples of
the form (menu_item , path_to_help_file , option), or return the empty
list. 'option' is the sequence number of the help resource. 'option'
diff --git a/Lib/idlelib/help.txt b/Lib/idlelib/help.txt
index ff786c5..6378a2e 100644
--- a/Lib/idlelib/help.txt
+++ b/Lib/idlelib/help.txt
@@ -1,142 +1,185 @@
[See the end of this file for ** TIPS ** on using IDLE !!]
-Click on the dotted line at the top of a menu to "tear it off": a
-separate window containing the menu is created.
-
-File Menu:
-
- New File -- Create a new file editing window
- Open... -- Open an existing file
- Recent Files... -- Open a list of recent files
- Open Module... -- Open an existing module (searches sys.path)
- Class Browser -- Show classes and methods in current file
- Path Browser -- Show sys.path directories, modules, classes
+IDLE is the Python IDE built with the tkinter GUI toolkit.
+
+IDLE has the following features:
+-coded in 100% pure Python, using the tkinter GUI toolkit
+-cross-platform: works on Windows, Unix, and OS X
+-multi-window text editor with multiple undo, Python colorizing, smart indent,
+call tips, and many other features
+-Python shell window (a.k.a interactive interpreter)
+-debugger (not complete, but you can set breakpoints, view and step)
+
+Menus:
+
+IDLE has two window types the Shell window and the Editor window. It is
+possible to have multiple editor windows simultaneously. IDLE's
+menus dynamically change based on which window is currently selected. Each menu
+documented below indicates which window type it is associated with. Click on
+the dotted line at the top of a menu to "tear it off": a separate window
+containing the menu is created (for Unix and Windows only).
+
+File Menu (Shell and Editor):
+
+ New File -- Create a new file editing window
+ Open... -- Open an existing file
+ Open Module... -- Open an existing module (searches sys.path)
+ Recent Files... -- Open a list of recent files
+ Class Browser -- Show classes and methods in current file
+ Path Browser -- Show sys.path directories, modules, classes,
and methods
- ---
- Save -- Save current window to the associated file (unsaved
- windows have a * before and after the window title)
-
- Save As... -- Save current window to new file, which becomes
- the associated file
- Save Copy As... -- Save current window to different file
- without changing the associated file
- ---
- Print Window -- Print the current window
- ---
- Close -- Close current window (asks to save if unsaved)
- Exit -- Close all windows, quit (asks to save if unsaved)
-
-Edit Menu:
-
- Undo -- Undo last change to current window
- (A maximum of 1000 changes may be undone)
- Redo -- Redo last undone change to current window
- ---
- Cut -- Copy a selection into system-wide clipboard,
+ ---
+ Save -- Save current window to the associated file (unsaved
+ windows have a * before and after the window title)
+
+ Save As... -- Save current window to new file, which becomes
+ the associated file
+ Save Copy As... -- Save current window to different file
+ without changing the associated file
+ ---
+ Print Window -- Print the current window
+ ---
+ Close -- Close current window (asks to save if unsaved)
+ Exit -- Close all windows, quit (asks to save if unsaved)
+
+Edit Menu (Shell and Editor):
+
+ Undo -- Undo last change to current window
+ (a maximum of 1000 changes may be undone)
+ Redo -- Redo last undone change to current window
+ ---
+ Cut -- Copy a selection into system-wide clipboard,
then delete the selection
- Copy -- Copy selection into system-wide clipboard
- Paste -- Insert system-wide clipboard into window
- Select All -- Select the entire contents of the edit buffer
- ---
- Find... -- Open a search dialog box with many options
- Find Again -- Repeat last search
- Find Selection -- Search for the string in the selection
- Find in Files... -- Open a search dialog box for searching files
- Replace... -- Open a search-and-replace dialog box
- Go to Line -- Ask for a line number and show that line
- Show Calltip -- Open a small window with function param hints
- Show Completions -- Open a scroll window allowing selection keywords
- and attributes. (see '*TIPS*', below)
- Show Parens -- Highlight the surrounding parenthesis
- Expand Word -- Expand the word you have typed to match another
- word in the same buffer; repeat to get a
+ Copy -- Copy selection into system-wide clipboard
+ Paste -- Insert system-wide clipboard into window
+ Select All -- Select the entire contents of the edit buffer
+ ---
+ Find... -- Open a search dialog box with many options
+ Find Again -- Repeat last search
+ Find Selection -- Search for the string in the selection
+ Find in Files... -- Open a search dialog box for searching files
+ Replace... -- Open a search-and-replace dialog box
+ Go to Line -- Ask for a line number and show that line
+ Expand Word -- Expand the word you have typed to match another
+ word in the same buffer; repeat to get a
different expansion
-
-Format Menu (only in Edit window):
-
- Indent Region -- Shift selected lines right 4 spaces
- Dedent Region -- Shift selected lines left 4 spaces
- Comment Out Region -- Insert ## in front of selected lines
- Uncomment Region -- Remove leading # or ## from selected lines
- Tabify Region -- Turns *leading* stretches of spaces into tabs
- (Note: We recommend using 4 space blocks to indent Python code.)
- Untabify Region -- Turn *all* tabs into the right number of spaces
- New Indent Width... -- Open dialog to change indent width
- Format Paragraph -- Reformat the current blank-line-separated
- paragraph
-
-Run Menu (only in Edit window):
-
- Python Shell -- Open or wake up the Python shell window
- ---
- Check Module -- Run a syntax check on the module
- Run Module -- Execute the current file in the __main__ namespace
-
-Shell Menu (only in Shell window):
-
- View Last Restart -- Scroll the shell window to the last restart
- Restart Shell -- Restart the interpreter with a fresh environment
-
-Debug Menu (only in Shell window):
-
- Go to File/Line -- look around the insert point for a filename
- and line number, open the file, and show the line
- Debugger (toggle) -- Run commands in the shell under the debugger
- Stack Viewer -- Show the stack traceback of the last exception
- Auto-open Stack Viewer (toggle) -- Open stack viewer on traceback
-
-Options Menu:
-
- Configure IDLE -- Open a configuration dialog. Fonts, indentation,
+ Show Calltip -- After an unclosed parenthesis for a function, open
+ a small window with function parameter hints
+ Show Parens -- Highlight the surrounding parenthesis
+ Show Completions -- Open a scroll window allowing selection keywords
+ and attributes. (see '*TIPS*', below)
+
+Format Menu (Editor window only):
+
+ Indent Region -- Shift selected lines right by the indent width
+ (default 4 spaces)
+ Dedent Region -- Shift selected lines left by the indent width
+ (default 4 spaces)
+ Comment Out Region -- Insert ## in front of selected lines
+ Uncomment Region -- Remove leading # or ## from selected lines
+ Tabify Region -- Turns *leading* stretches of spaces into tabs.
+ (Note: We recommend using 4 space blocks to indent Python code.)
+ Untabify Region -- Turn *all* tabs into the corrent number of spaces
+ Toggle tabs -- Open a dialog to switch between indenting with
+ spaces and tabs.
+ New Indent Width... -- Open a dialog to change indent width. The
+ accepted default by the Python community is 4
+ spaces.
+ Format Paragraph -- Reformat the current blank-line-separated
+ paragraph. All lines in the paragraph will be
+ formatted to less than 80 columns.
+ ---
+ Strip trailing whitespace -- Removed any space characters after the end
+ of the last non-space character
+
+Run Menu (Editor window only):
+
+ Python Shell -- Open or wake up the Python shell window
+ ---
+ Check Module -- Check the syntax of the module currently open in the
+ Editor window. If the module has not been saved IDLE
+ will prompt the user to save the code.
+ Run Module -- Restart the shell to clean the environment, then
+ execute the currently open module. If the module has
+ not been saved IDLE will prompt the user to save the
+ code.
+
+Shell Menu (Shell window only):
+
+ View Last Restart -- Scroll the shell window to the last Shell restart
+ Restart Shell -- Restart the shell to clean the environment
+
+Debug Menu (Shell window only):
+
+ Go to File/Line -- Look around the insert point for a filename
+ and line number, open the file, and show the line.
+ Useful to view the source lines referenced in an
+ exception traceback. Available in the context
+ menu of the Shell window.
+ Debugger (toggle) -- This feature is not complete and considered
+ experimental. Run commands in the shell under the
+ debugger.
+ Stack Viewer -- Show the stack traceback of the last exception
+ Auto-open Stack Viewer (toggle) -- Toggle automatically opening the
+ stack viewer on unhandled
+ exception
+
+Options Menu (Shell and Editor):
+
+ Configure IDLE -- Open a configuration dialog. Fonts, indentation,
keybindings, and color themes may be altered.
- Startup Preferences may be set, and Additional Help
- Sources can be specified.
-
- On OS X this menu is not present, use
- menu 'IDLE -> Preferences...' instead.
- ---
- Code Context -- Open a pane at the top of the edit window which
- shows the block context of the section of code
- which is scrolling off the top or the window.
- (Not present in Shell window.)
-
-Windows Menu:
-
- Zoom Height -- toggles the window between configured size
- and maximum height.
- ---
- The rest of this menu lists the names of all open windows;
- select one to bring it to the foreground (deiconifying it if
- necessary).
+ Startup Preferences may be set, and additional Help
+ sources can be specified.
+
+ ---
+ Code Context (toggle) -- Open a pane at the top of the edit window
+ which shows the block context of the section
+ of code which is scrolling off the top or the
+ window. This is not present in the Shell
+ window only the Editor window.
+
+Windows Menu (Shell and Editor):
+
+ Zoom Height -- Toggles the window between normal size (40x80 initial
+ setting) and maximum height. The initial size is in the Configure
+ IDLE dialog under the general tab.
+ ---
+ The rest of this menu lists the names of all open windows;
+ select one to bring it to the foreground (deiconifying it if
+ necessary).
Help Menu:
- About IDLE -- Version, copyright, license, credits
- IDLE Readme -- Background discussion and change details
- ---
- IDLE Help -- Display this file
- Python Docs -- Access local Python documentation, if
- installed. Otherwise, access www.python.org.
- ---
- (Additional Help Sources may be added here)
-
-Edit context menu (Right-click / Control-click on OS X in Edit window):
-
- Cut -- Copy a selection into system-wide clipboard,
+ About IDLE -- Version, copyright, license, credits
+ ---
+ IDLE Help -- Display this file which is a help file for IDLE
+ detailing the menu options, basic editing and navigation,
+ and other tips.
+ Python Docs -- Access local Python documentation, if
+ installed. Or will start a web browser and open
+ docs.python.org showing the latest Python documentation.
+ ---
+ Additional help sources may be added here with the Configure IDLE
+ dialog under the General tab.
+
+Editor context menu (Right-click / Control-click on OS X in Edit window):
+
+ Cut -- Copy a selection into system-wide clipboard,
then delete the selection
- Copy -- Copy selection into system-wide clipboard
- Paste -- Insert system-wide clipboard into window
- Set Breakpoint -- Sets a breakpoint (when debugger open)
- Clear Breakpoint -- Clears the breakpoint on that line
+ Copy -- Copy selection into system-wide clipboard
+ Paste -- Insert system-wide clipboard into window
+ Set Breakpoint -- Sets a breakpoint. Breakpoints are only enabled
+ when the debugger is open.
+ Clear Breakpoint -- Clears the breakpoint on that line
Shell context menu (Right-click / Control-click on OS X in Shell window):
- Cut -- Copy a selection into system-wide clipboard,
+ Cut -- Copy a selection into system-wide clipboard,
then delete the selection
- Copy -- Copy selection into system-wide clipboard
- Paste -- Insert system-wide clipboard into window
- ---
- Go to file/line -- Same as in Debug menu
+ Copy -- Copy selection into system-wide clipboard
+ Paste -- Insert system-wide clipboard into window
+ ---
+ Go to file/line -- Same as in Debug menu
** TIPS **
@@ -144,159 +187,182 @@ Shell context menu (Right-click / Control-click on OS X in Shell window):
Additional Help Sources:
- Windows users can Google on zopeshelf.chm to access Zope help files in
- the Windows help format. The Additional Help Sources feature of the
- configuration GUI supports .chm, along with any other filetypes
- supported by your browser. Supply a Menu Item title, and enter the
- location in the Help File Path slot of the New Help Source dialog. Use
- http:// and/or www. to identify external URLs, or download the file and
- browse for its path on your machine using the Browse button.
+ Windows users can Google on zopeshelf.chm to access Zope help files in
+ the Windows help format. The Additional Help Sources feature of the
+ configuration GUI supports .chm, along with any other filetypes
+ supported by your browser. Supply a Menu Item title, and enter the
+ location in the Help File Path slot of the New Help Source dialog. Use
+ http:// and/or www. to identify external URLs, or download the file and
+ browse for its path on your machine using the Browse button.
- All users can access the extensive sources of help, including
- tutorials, available at www.python.org/doc. Selected URLs can be added
- or removed from the Help menu at any time using Configure IDLE.
+ All users can access the extensive sources of help, including
+ tutorials, available at docs.python.org. Selected URLs can be added
+ or removed from the Help menu at any time using Configure IDLE.
Basic editing and navigation:
- Backspace deletes char to the left; DEL deletes char to the right.
- Control-backspace deletes word left, Control-DEL deletes word right.
- Arrow keys and Page Up/Down move around.
- Control-left/right Arrow moves by words in a strange but useful way.
- Home/End go to begin/end of line.
- Control-Home/End go to begin/end of file.
- Some useful Emacs bindings are inherited from Tcl/Tk:
- Control-a beginning of line
- Control-e end of line
- Control-k kill line (but doesn't put it in clipboard)
- Control-l center window around the insertion point
- Standard Windows bindings may work on that platform.
- Keybindings are selected in the Settings Dialog, look there.
+ Backspace deletes char to the left; DEL deletes char to the right.
+ Control-backspace deletes word left, Control-DEL deletes word right.
+ Arrow keys and Page Up/Down move around.
+ Control-left/right Arrow moves by words in a strange but useful way.
+ Home/End go to begin/end of line.
+ Control-Home/End go to begin/end of file.
+ Some useful Emacs bindings are inherited from Tcl/Tk:
+ Control-a beginning of line
+ Control-e end of line
+ Control-k kill line (but doesn't put it in clipboard)
+ Control-l center window around the insertion point
+ Standard keybindings (like Control-c to copy and Control-v to
+ paste) may work. Keybindings are selected in the Configure IDLE
+ dialog.
Automatic indentation:
- After a block-opening statement, the next line is indented by 4 spaces
- (in the Python Shell window by one tab). After certain keywords
- (break, return etc.) the next line is dedented. In leading
- indentation, Backspace deletes up to 4 spaces if they are there. Tab
- inserts spaces (in the Python Shell window one tab), number depends on
- Indent Width. (N.B. Currently tabs are restricted to four spaces due
- to Tcl/Tk issues.)
+ After a block-opening statement, the next line is indented by 4 spaces
+ (in the Python Shell window by one tab). After certain keywords
+ (break, return etc.) the next line is dedented. In leading
+ indentation, Backspace deletes up to 4 spaces if they are there. Tab
+ inserts spaces (in the Python Shell window one tab), number depends on
+ Indent Width. Currently tabs are restricted to four spaces due
+ to Tcl/Tk limitations.
See also the indent/dedent region commands in the edit menu.
Completions:
- Completions are supplied for functions, classes, and attributes of
- classes, both built-in and user-defined. Completions are also provided
- for filenames.
-
- The AutoCompleteWindow (ACW) will open after a predefined delay
- (default is two seconds) after a '.' or (in a string) an os.sep is
- typed. If after one of those characters (plus zero or more other
- characters) you type a Tab the ACW will open immediately if a possible
- continuation is found.
-
- If there is only one possible completion for the characters entered, a
- Tab will supply that completion without opening the ACW.
-
- 'Show Completions' will force open a completions window. In an empty
- string, this will contain the files in the current directory. On a
- blank line, it will contain the built-in and user-defined functions and
- classes in the current name spaces, plus any modules imported. If some
- characters have been entered, the ACW will attempt to be more specific.
-
- If string of characters is typed, the ACW selection will jump to the
- entry most closely matching those characters. Entering a Tab will cause
- the longest non-ambiguous match to be entered in the Edit window or
- Shell. Two Tabs in a row will supply the current ACW selection, as
- will Return or a double click. Cursor keys, Page Up/Down, mouse
- selection, and the scrollwheel all operate on the ACW.
-
- 'Hidden' attributes can be accessed by typing the beginning of hidden
- name after a '.'. e.g. '_'. This allows access to modules with
- '__all__' set, or to class-private attributes.
-
- Completions and the 'Expand Word' facility can save a lot of typing!
-
- Completions are currently limited to those in the namespaces. Names in
- an Edit window which are not via __main__ or sys.modules will not be
- found. Run the module once with your imports to correct this
- situation. Note that IDLE itself places quite a few modules in
- sys.modules, so much can be found by default, e.g. the re module.
-
- If you don't like the ACW popping up unbidden, simply make the delay
- longer or disable the extension. OTOH, you could make the delay zero.
-
- You could also switch off the CallTips extension. (We will be adding
- a delay to the call tip window.)
+ Completions are supplied for functions, classes, and attributes of
+ classes, both built-in and user-defined. Completions are also provided
+ for filenames.
+
+ The AutoCompleteWindow (ACW) will open after a predefined delay
+ (default is two seconds) after a '.' or (in a string) an os.sep is
+ typed. If after one of those characters (plus zero or more other
+ characters) a tab is typed the ACW will open immediately if a possible
+ continuation is found.
+
+ If there is only one possible completion for the characters entered, a
+ tab will supply that completion without opening the ACW.
+
+ 'Show Completions' will force open a completions window, by default the
+ Control-space keys will open a completions window. In an empty
+ string, this will contain the files in the current directory. On a
+ blank line, it will contain the built-in and user-defined functions and
+ classes in the current name spaces, plus any modules imported. If some
+ characters have been entered, the ACW will attempt to be more specific.
+
+ If string of characters is typed, the ACW selection will jump to the
+ entry most closely matching those characters. Entering a tab will cause
+ the longest non-ambiguous match to be entered in the Edit window or
+ Shell. Two tabs in a row will supply the current ACW selection, as
+ will return or a double click. Cursor keys, Page Up/Down, mouse
+ selection, and the scroll wheel all operate on the ACW.
+
+ "Hidden" attributes can be accessed by typing the beginning of hidden
+ name after a '.', e.g. '_'. This allows access to modules with
+ '__all__' set, or to class-private attributes.
+
+ Completions and the 'Expand Word' facility can save a lot of typing!
+
+ Completions are currently limited to those in the namespaces. Names in
+ an Editor window which are not via __main__ or sys.modules will not be
+ found. Run the module once with your imports to correct this
+ situation. Note that IDLE itself places quite a few modules in
+ sys.modules, so much can be found by default, e.g. the re module.
+
+ If you don't like the ACW popping up unbidden, simply make the delay
+ longer or disable the extension. Or another option is the delay could
+ be set to zero. Another alternative to preventing ACW popups is to
+ disable the call tips extension.
Python Shell window:
- Control-c interrupts executing command.
- Control-d sends end-of-file; closes window if typed at >>> prompt.
+ Control-c interrupts executing command.
+ Control-d sends end-of-file; closes window if typed at >>> prompt.
+ Alt-/ expand word is also useful to reduce typing.
Command history:
- Alt-p retrieves previous command matching what you have typed.
- Alt-n retrieves next.
- (These are Control-p, Control-n on OS X)
- Return while cursor is on a previous command retrieves that command.
- Expand word is also useful to reduce typing.
+ Alt-p retrieves previous command matching what you have typed. On OS X
+ use Control-p.
+ Alt-n retrieves next. On OS X use Control-n.
+ Return while cursor is on a previous command retrieves that command.
Syntax colors:
- The coloring is applied in a background "thread", so you may
- occasionally see uncolorized text. To change the color
- scheme, use the Configure IDLE / Highlighting dialog.
+ The coloring is applied in a background "thread", so you may
+ occasionally see uncolorized text. To change the color
+ scheme, use the Configure IDLE / Highlighting dialog.
Python default syntax colors:
- Keywords orange
- Builtins royal purple
- Strings green
- Comments red
- Definitions blue
+ Keywords orange
+ Builtins royal purple
+ Strings green
+ Comments red
+ Definitions blue
Shell default colors:
- Console output brown
- stdout blue
- stderr red
- stdin black
+ Console output brown
+ stdout blue
+ stderr red
+ stdin black
Other preferences:
- The font preferences, keybinding, and startup preferences can
- be changed using the Settings dialog.
+ The font preferences, highlighting, keys, and general preferences can
+ be changed via the Configure IDLE menu option. Be sure to note that
+ keys can be user defined, IDLE ships with four built in key sets. In
+ addition a user can create a custom key set in the Configure IDLE
+ dialog under the keys tab.
Command line usage:
- Enter idle -h at the command prompt to get a usage message.
-
-Running without a subprocess:
-
- If IDLE is started with the -n command line switch it will run in a
- single process and will not create the subprocess which runs the RPC
- Python execution server. This can be useful if Python cannot create
- the subprocess or the RPC socket interface on your platform. However,
- in this mode user code is not isolated from IDLE itself. Also, the
- environment is not restarted when Run/Run Module (F5) is selected. If
- your code has been modified, you must reload() the affected modules and
- re-import any specific items (e.g. from foo import baz) if the changes
- are to take effect. For these reasons, it is preferable to run IDLE
- with the default subprocess if at all possible.
+ Enter idle -h at the command prompt to get a usage message.
+
+ idle.py [-c command] [-d] [-e] [-s] [-t title] [arg] ...
+
+ -c command run this command
+ -d enable debugger
+ -e edit mode; arguments are files to be edited
+ -s run $IDLESTARTUP or $PYTHONSTARTUP first
+ -t title set title of shell window
+
+ If there are arguments:
+ 1. If -e is used, arguments are files opened for editing and sys.argv
+ reflects the arguments passed to IDLE itself.
+ 2. Otherwise, if -c is used, all arguments are placed in
+ sys.argv[1:...], with sys.argv[0] set to -c.
+ 3. Otherwise, if neither -e nor -c is used, the first argument is a
+ script which is executed with the remaining arguments in
+ sys.argv[1:...] and sys.argv[0] set to the script name. If the
+ script name is -, no script is executed but an interactive Python
+ session is started; the arguments are still available in sys.argv.
+
+Running without a subprocess: (DEPRECATED in Python 3.4 see Issue 16123)
+
+ If IDLE is started with the -n command line switch it will run in a
+ single process and will not create the subprocess which runs the RPC
+ Python execution server. This can be useful if Python cannot create
+ the subprocess or the RPC socket interface on your platform. However,
+ in this mode user code is not isolated from IDLE itself. Also, the
+ environment is not restarted when Run/Run Module (F5) is selected. If
+ your code has been modified, you must reload() the affected modules and
+ re-import any specific items (e.g. from foo import baz) if the changes
+ are to take effect. For these reasons, it is preferable to run IDLE
+ with the default subprocess if at all possible.
Extensions:
- IDLE contains an extension facility. See the beginning of
- config-extensions.def in the idlelib directory for further information.
- The default extensions are currently:
-
- FormatParagraph
- AutoExpand
- ZoomHeight
- ScriptBinding
- CallTips
- ParenMatch
- AutoComplete
- CodeContext
+ IDLE contains an extension facility. See the beginning of
+ config-extensions.def in the idlelib directory for further information.
+ The default extensions are currently:
+
+ FormatParagraph
+ AutoExpand
+ ZoomHeight
+ ScriptBinding
+ CallTips
+ ParenMatch
+ AutoComplete
+ CodeContext
diff --git a/Lib/idlelib/idle_test/test_calltips.py b/Lib/idlelib/idle_test/test_calltips.py
index f363764..4ee15ae 100644
--- a/Lib/idlelib/idle_test/test_calltips.py
+++ b/Lib/idlelib/idle_test/test_calltips.py
@@ -54,9 +54,9 @@ class Get_signatureTest(unittest.TestCase):
gtest(List, List.__doc__)
gtest(list.__new__,
- 'T.__new__(S, ...) -> a new object with type S, a subtype of T')
+ 'Create and return a new object. See help(type) for accurate signature.')
gtest(list.__init__,
- 'x.__init__(...) initializes x; see help(type(x)) for signature')
+ 'Initialize self. See help(type(self)) for accurate signature.')
append_doc = "L.append(object) -> None -- append object to end"
gtest(list.append, append_doc)
gtest([].append, append_doc)
@@ -69,7 +69,8 @@ class Get_signatureTest(unittest.TestCase):
self.assertEqual(signature(textwrap.TextWrapper), '''\
(width=70, initial_indent='', subsequent_indent='', expand_tabs=True,
replace_whitespace=True, fix_sentence_endings=False, break_long_words=True,
- drop_whitespace=True, break_on_hyphens=True, tabsize=8)''')
+ drop_whitespace=True, break_on_hyphens=True, tabsize=8, *, max_lines=None,
+ placeholder=' [...]')''')
def test_docline_truncation(self):
def f(): pass
diff --git a/Lib/idlelib/idle_test/test_formatparagraph.py b/Lib/idlelib/idle_test/test_formatparagraph.py
index f4a7c2d..690c936 100644
--- a/Lib/idlelib/idle_test/test_formatparagraph.py
+++ b/Lib/idlelib/idle_test/test_formatparagraph.py
@@ -293,7 +293,7 @@ class FormatEventTest(unittest.TestCase):
# Set cursor ('insert' mark) to '1.0', within text.
text.insert('1.0', self.test_string)
text.mark_set('insert', '1.0')
- self.formatter('ParameterDoesNothing')
+ self.formatter('ParameterDoesNothing', limit=70)
result = text.get('1.0', 'insert')
# find function includes \n
expected = (
@@ -305,7 +305,7 @@ class FormatEventTest(unittest.TestCase):
# Select from 1.11 to line end.
text.insert('1.0', self.test_string)
text.tag_add('sel', '1.11', '1.end')
- self.formatter('ParameterDoesNothing')
+ self.formatter('ParameterDoesNothing', limit=70)
result = text.get('1.0', 'insert')
# selection excludes \n
expected = (
@@ -319,7 +319,7 @@ class FormatEventTest(unittest.TestCase):
# Select 2 long lines.
text.insert('1.0', self.multiline_test_string)
text.tag_add('sel', '2.0', '4.0')
- self.formatter('ParameterDoesNothing')
+ self.formatter('ParameterDoesNothing', limit=70)
result = text.get('2.0', 'insert')
expected = (
" The second line's length is way over the max width. It goes on and\n"
@@ -334,7 +334,7 @@ class FormatEventTest(unittest.TestCase):
# Set cursor ('insert') to '1.0', within block.
text.insert('1.0', self.multiline_test_comment)
- self.formatter('ParameterDoesNothing')
+ self.formatter('ParameterDoesNothing', limit=70)
result = text.get('1.0', 'insert')
expected = (
"# The first line is under the max width. The second line's length is\n"
@@ -348,7 +348,7 @@ class FormatEventTest(unittest.TestCase):
# Select line 2, verify line 1 unaffected.
text.insert('1.0', self.multiline_test_comment)
text.tag_add('sel', '2.0', '3.0')
- self.formatter('ParameterDoesNothing')
+ self.formatter('ParameterDoesNothing', limit=70)
result = text.get('1.0', 'insert')
expected = (
"# The first line is under the max width.\n"
diff --git a/Lib/idlelib/idlever.py b/Lib/idlelib/idlever.py
index 044bef9..efe37a1 100644
--- a/Lib/idlelib/idlever.py
+++ b/Lib/idlelib/idlever.py
@@ -1 +1 @@
-IDLE_VERSION = "3.3.5"
+IDLE_VERSION = "3.4.0"
diff --git a/Lib/idlelib/keybindingDialog.py b/Lib/idlelib/keybindingDialog.py
index 0f0da8c..db88cb4 100644
--- a/Lib/idlelib/keybindingDialog.py
+++ b/Lib/idlelib/keybindingDialog.py
@@ -4,7 +4,7 @@ Dialog for building Tkinter accelerator key bindings
from tkinter import *
import tkinter.messagebox as tkMessageBox
import string
-from idlelib import macosxSupport
+import sys
class GetKeysDialog(Toplevel):
def __init__(self,parent,title,action,currentKeySequences):
@@ -133,8 +133,7 @@ class GetKeysDialog(Toplevel):
order is also important: key binding equality depends on it, so
config-keys.def must use the same ordering.
"""
- import sys
- if macosxSupport.runningAsOSXApp():
+ if sys.platform == "darwin":
self.modifiers = ['Shift', 'Control', 'Option', 'Command']
else:
self.modifiers = ['Control', 'Alt', 'Shift']
diff --git a/Lib/idlelib/macosxSupport.py b/Lib/idlelib/macosxSupport.py
index 67069fa..b6488f8 100644
--- a/Lib/idlelib/macosxSupport.py
+++ b/Lib/idlelib/macosxSupport.py
@@ -1,48 +1,70 @@
"""
-A number of function that enhance IDLE on MacOSX when it used as a normal
-GUI application (as opposed to an X11 application).
+A number of functions that enhance IDLE on Mac OSX.
"""
import sys
import tkinter
from os import path
+import warnings
+def runningAsOSXApp():
+ warnings.warn("runningAsOSXApp() is deprecated, use isAquaTk()",
+ DeprecationWarning, stacklevel=2)
+ return isAquaTk()
-_appbundle = None
+def isCarbonAquaTk(root):
+ warnings.warn("isCarbonAquaTk(root) is deprecated, use isCarbonTk()",
+ DeprecationWarning, stacklevel=2)
+ return isCarbonTk()
-def runningAsOSXApp():
+_tk_type = None
+
+def _initializeTkVariantTests(root):
+ """
+ Initializes OS X Tk variant values for
+ isAquaTk(), isCarbonTk(), isCocoaTk(), and isXQuartz().
"""
- Returns True if Python is running from within an app on OSX.
- If so, the various OS X customizations will be triggered later (menu
- fixup, et al). (Originally, this test was supposed to condition
- behavior on whether IDLE was running under Aqua Tk rather than
- under X11 Tk but that does not work since a framework build
- could be linked with X11. For several releases, this test actually
- differentiates between whether IDLE is running from a framework or
- not. As a future enhancement, it should be considered whether there
- should be a difference based on framework and any needed X11 adaptions
- should be made dependent on a new function that actually tests for X11.)
- """
- global _appbundle
- if _appbundle is None:
- _appbundle = sys.platform == 'darwin'
- if _appbundle:
- import sysconfig
- _appbundle = bool(sysconfig.get_config_var('PYTHONFRAMEWORK'))
- return _appbundle
-
-_carbonaquatk = None
+ global _tk_type
+ if sys.platform == 'darwin':
+ ws = root.tk.call('tk', 'windowingsystem')
+ if 'x11' in ws:
+ _tk_type = "xquartz"
+ elif 'aqua' not in ws:
+ _tk_type = "other"
+ elif 'AppKit' in root.tk.call('winfo', 'server', '.'):
+ _tk_type = "cocoa"
+ else:
+ _tk_type = "carbon"
+ else:
+ _tk_type = "other"
-def isCarbonAquaTk(root):
+def isAquaTk():
+ """
+ Returns True if IDLE is using a native OS X Tk (Cocoa or Carbon).
+ """
+ assert _tk_type is not None
+ return _tk_type == "cocoa" or _tk_type == "carbon"
+
+def isCarbonTk():
"""
Returns True if IDLE is using a Carbon Aqua Tk (instead of the
newer Cocoa Aqua Tk).
"""
- global _carbonaquatk
- if _carbonaquatk is None:
- _carbonaquatk = (runningAsOSXApp() and
- 'aqua' in root.tk.call('tk', 'windowingsystem') and
- 'AppKit' not in root.tk.call('winfo', 'server', '.'))
- return _carbonaquatk
+ assert _tk_type is not None
+ return _tk_type == "carbon"
+
+def isCocoaTk():
+ """
+ Returns True if IDLE is using a Cocoa Aqua Tk.
+ """
+ assert _tk_type is not None
+ return _tk_type == "cocoa"
+
+def isXQuartz():
+ """
+ Returns True if IDLE is using an OS X X11 Tk.
+ """
+ assert _tk_type is not None
+ return _tk_type == "xquartz"
def tkVersionWarning(root):
"""
@@ -53,8 +75,7 @@ def tkVersionWarning(root):
can still crash unexpectedly.
"""
- if (runningAsOSXApp() and
- ('AppKit' in root.tk.call('winfo', 'server', '.')) ):
+ if isCocoaTk():
patchlevel = root.tk.call('info', 'patchlevel')
if patchlevel not in ('8.5.7', '8.5.9'):
return False
@@ -88,8 +109,8 @@ def hideTkConsole(root):
def overrideRootMenu(root, flist):
"""
- Replace the Tk root menu by something that's more appropriate for
- IDLE.
+ Replace the Tk root menu by something that is more appropriate for
+ IDLE with an Aqua Tk.
"""
# The menu that is attached to the Tk root (".") is also used by AquaTk for
# all windows that don't specify a menu of their own. The default menubar
@@ -108,6 +129,22 @@ def overrideRootMenu(root, flist):
from idlelib import WindowList
from idlelib.MultiCall import MultiCallCreator
+ closeItem = Bindings.menudefs[0][1][-2]
+
+ # Remove the last 3 items of the file menu: a separator, close window and
+ # quit. Close window will be reinserted just above the save item, where
+ # it should be according to the HIG. Quit is in the application menu.
+ del Bindings.menudefs[0][1][-3:]
+ Bindings.menudefs[0][1].insert(6, closeItem)
+
+ # Remove the 'About' entry from the help menu, it is in the application
+ # menu
+ del Bindings.menudefs[-1][1][0:2]
+
+ # Remove the 'Configure' entry from the options menu, it is in the
+ # application menu as 'Preferences'
+ del Bindings.menudefs[-2][1][0:2]
+
menubar = Menu(root)
root.configure(menu=menubar)
menudict = {}
@@ -156,7 +193,7 @@ def overrideRootMenu(root, flist):
# right thing for now.
root.createcommand('exit', flist.close_all_callback)
- if isCarbonAquaTk(root):
+ if isCarbonTk():
# for Carbon AquaTk, replace the default Tk apple menu
menudict['application'] = menu = Menu(menubar, name='apple')
menubar.add_cascade(label='IDLE', menu=menu)
@@ -171,8 +208,7 @@ def overrideRootMenu(root, flist):
Bindings.menudefs[0][1].append(
('_Preferences....', '<<open-config-dialog>>'),
)
- else:
- # assume Cocoa AquaTk
+ if isCocoaTk():
# replace default About dialog with About IDLE one
root.createcommand('tkAboutDialog', about_dialog)
# replace default "Help" item in Help menu
@@ -182,10 +218,22 @@ def overrideRootMenu(root, flist):
def setupApp(root, flist):
"""
- Perform setup for the OSX application bundle.
+ Perform initial OS X customizations if needed.
+ Called from PyShell.main() after initial calls to Tk()
+
+ There are currently three major versions of Tk in use on OS X:
+ 1. Aqua Cocoa Tk (native default since OS X 10.6)
+ 2. Aqua Carbon Tk (original native, 32-bit only, deprecated)
+ 3. X11 (supported by some third-party distributors, deprecated)
+ There are various differences among the three that affect IDLE
+ behavior, primarily with menus, mouse key events, and accelerators.
+ Some one-time customizations are performed here.
+ Others are dynamically tested throughout idlelib by calls to the
+ isAquaTk(), isCarbonTk(), isCocoaTk(), isXQuartz() functions which
+ are initialized here as well.
"""
- if not runningAsOSXApp(): return
-
- hideTkConsole(root)
- overrideRootMenu(root, flist)
- addOpenEventSupport(root, flist)
+ _initializeTkVariantTests(root)
+ if isAquaTk():
+ hideTkConsole(root)
+ overrideRootMenu(root, flist)
+ addOpenEventSupport(root, flist)
diff --git a/Lib/idlelib/rpc.py b/Lib/idlelib/rpc.py
index ddce6e9..9c51b8f 100644
--- a/Lib/idlelib/rpc.py
+++ b/Lib/idlelib/rpc.py
@@ -199,7 +199,7 @@ class SocketIO(object):
raise
except KeyboardInterrupt:
raise
- except socket.error:
+ except OSError:
raise
except Exception as ex:
return ("CALLEXC", ex)
@@ -340,7 +340,7 @@ class SocketIO(object):
n = self.sock.send(s[:BUFSIZE])
except (AttributeError, TypeError):
raise OSError("socket no longer exists")
- except socket.error:
+ except OSError:
raise
else:
s = s[n:]
@@ -357,7 +357,7 @@ class SocketIO(object):
return None
try:
s = self.sock.recv(BUFSIZE)
- except socket.error:
+ except OSError:
raise EOFError
if len(s) == 0:
raise EOFError
@@ -537,7 +537,7 @@ class RPCClient(SocketIO):
SocketIO.__init__(self, working_sock)
else:
print("** Invalid host: ", address, file=sys.__stderr__)
- raise socket.error
+ raise OSError
def get_remote_proxy(self, oid):
return RPCProxy(self, oid)
diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py
index c1859b6..13cec62 100644
--- a/Lib/idlelib/run.py
+++ b/Lib/idlelib/run.py
@@ -150,8 +150,8 @@ def manage_socket(address):
try:
server = MyRPCServer(address, MyHandler)
break
- except socket.error as err:
- print("IDLE Subprocess: socket error: " + err.args[1] +
+ except OSError as err:
+ print("IDLE Subprocess: OSError: " + err.args[1] +
", retrying....", file=sys.__stderr__)
socket_error = err
else:
diff --git a/Lib/imaplib.py b/Lib/imaplib.py
index ad5f4e9..ad104fe 100644
--- a/Lib/imaplib.py
+++ b/Lib/imaplib.py
@@ -185,7 +185,7 @@ class IMAP4:
except Exception:
try:
self.shutdown()
- except socket.error:
+ except OSError:
pass
raise
@@ -281,7 +281,7 @@ class IMAP4:
self.file.close()
try:
self.sock.shutdown(socket.SHUT_RDWR)
- except socket.error as e:
+ except OSError as e:
# The server might already have closed the connection
if e.errno != errno.ENOTCONN:
raise
@@ -554,7 +554,7 @@ class IMAP4:
import hmac
pwd = (self.password.encode('ASCII') if isinstance(self.password, str)
else self.password)
- return self.user + " " + hmac.HMAC(pwd, challenge).hexdigest()
+ return self.user + " " + hmac.HMAC(pwd, challenge, 'md5').hexdigest()
def logout(self):
@@ -742,12 +742,12 @@ class IMAP4:
raise self.abort('TLS not supported by server')
# Generate a default SSL context if none was passed.
if ssl_context is None:
- ssl_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
- # SSLv2 considered harmful.
- ssl_context.options |= ssl.OP_NO_SSLv2
+ ssl_context = ssl._create_stdlib_context()
typ, dat = self._simple_command(name)
if typ == 'OK':
- self.sock = ssl_context.wrap_socket(self.sock)
+ server_hostname = self.host if ssl.HAS_SNI else None
+ self.sock = ssl_context.wrap_socket(self.sock,
+ server_hostname=server_hostname)
self.file = self.sock.makefile('rb')
self._tls_established = True
self._get_capabilities()
@@ -915,7 +915,7 @@ class IMAP4:
try:
self.send(data + CRLF)
- except (socket.error, OSError) as val:
+ except OSError as val:
raise self.abort('socket error: %s' % val)
if literal is None:
@@ -940,7 +940,7 @@ class IMAP4:
try:
self.send(literal)
self.send(CRLF)
- except (socket.error, OSError) as val:
+ except OSError as val:
raise self.abort('socket error: %s' % val)
if not literator:
@@ -1090,7 +1090,7 @@ class IMAP4:
# Protocol mandates all lines terminated by CRLF
if not line.endswith(b'\r\n'):
- raise self.abort('socket error: unterminated line')
+ raise self.abort('socket error: unterminated line: %r' % line)
line = line[:-2]
if __debug__:
@@ -1215,15 +1215,17 @@ if HAVE_SSL:
self.keyfile = keyfile
self.certfile = certfile
+ if ssl_context is None:
+ ssl_context = ssl._create_stdlib_context(certfile=certfile,
+ keyfile=keyfile)
self.ssl_context = ssl_context
IMAP4.__init__(self, host, port)
def _create_socket(self):
sock = IMAP4._create_socket(self)
- if self.ssl_context:
- return self.ssl_context.wrap_socket(sock)
- else:
- return ssl.wrap_socket(sock, self.keyfile, self.certfile)
+ server_hostname = self.host if ssl.HAS_SNI else None
+ return self.ssl_context.wrap_socket(sock,
+ server_hostname=server_hostname)
def open(self, host='', port=IMAP4_SSL_PORT):
"""Setup connection to remote server on "host:port".
diff --git a/Lib/imghdr.py b/Lib/imghdr.py
index bdd47ee..add2ea8 100644
--- a/Lib/imghdr.py
+++ b/Lib/imghdr.py
@@ -147,7 +147,7 @@ def testall(list, recursive, toplevel):
sys.stdout.flush()
try:
print(what(filename))
- except IOError:
+ except OSError:
print('*** not found ***')
if __name__ == '__main__':
diff --git a/Lib/imp.py b/Lib/imp.py
index 4088383..c8449c6 100644
--- a/Lib/imp.py
+++ b/Lib/imp.py
@@ -16,18 +16,20 @@ except ImportError:
# Platform doesn't support dynamic loading.
load_dynamic = None
-# Directly exposed by this module
-from importlib._bootstrap import new_module
-from importlib._bootstrap import cache_from_source, source_from_cache
+from importlib._bootstrap import SourcelessFileLoader, _ERR_MSG, _SpecMethods
-
-from importlib import _bootstrap
from importlib import machinery
+from importlib import util
+import importlib
import os
import sys
import tokenize
+import types
import warnings
+warnings.warn("the imp module is deprecated in favour of importlib; "
+ "see the module's documentation for alternative uses",
+ PendingDeprecationWarning)
# DEPRECATED
SEARCH_ERROR = 0
@@ -42,9 +44,23 @@ PY_CODERESOURCE = 8
IMP_HOOK = 9
+def new_module(name):
+ """**DEPRECATED**
+
+ Create a new module.
+
+ The module is not entered into sys.modules.
+
+ """
+ return types.ModuleType(name)
+
+
def get_magic():
- """Return the magic number for .pyc or .pyo files."""
- return _bootstrap._MAGIC_BYTES
+ """**DEPRECATED**
+
+ Return the magic number for .pyc or .pyo files.
+ """
+ return util.MAGIC_NUMBER
def get_tag():
@@ -52,12 +68,42 @@ def get_tag():
return sys.implementation.cache_tag
+def cache_from_source(path, debug_override=None):
+ """**DEPRECATED**
+
+ Given the path to a .py file, return the path to its .pyc/.pyo file.
+
+ The .py file does not need to exist; this simply returns the path to the
+ .pyc/.pyo file calculated as if the .py file were imported. The extension
+ will be .pyc unless sys.flags.optimize is non-zero, then it will be .pyo.
+
+ If debug_override is not None, then it must be a boolean and is used in
+ place of sys.flags.optimize.
+
+ If sys.implementation.cache_tag is None then NotImplementedError is raised.
+
+ """
+ return util.cache_from_source(path, debug_override)
+
+
+def source_from_cache(path):
+ """**DEPRECATED**
+
+ Given the path to a .pyc./.pyo file, return the path to its .py file.
+
+ The .pyc/.pyo file does not need to exist; this simply returns the path to
+ the .py file calculated to correspond to the .pyc/.pyo file. If path does
+ not conform to PEP 3147 format, ValueError will be raised. If
+ sys.implementation.cache_tag is None then NotImplementedError is raised.
+
+ """
+ return util.source_from_cache(path)
+
+
def get_suffixes():
- warnings.warn('imp.get_suffixes() is deprecated; use the constants '
- 'defined on importlib.machinery instead',
- DeprecationWarning, 2)
+ """**DEPRECATED**"""
extensions = [(s, 'rb', C_EXTENSION) for s in machinery.EXTENSION_SUFFIXES]
- source = [(s, 'U', PY_SOURCE) for s in machinery.SOURCE_SUFFIXES]
+ source = [(s, 'r', PY_SOURCE) for s in machinery.SOURCE_SUFFIXES]
bytecode = [(s, 'rb', PY_COMPILED) for s in machinery.BYTECODE_SUFFIXES]
return extensions + source + bytecode
@@ -65,7 +111,11 @@ def get_suffixes():
class NullImporter:
- """Null import object."""
+ """**DEPRECATED**
+
+ Null import object.
+
+ """
def __init__(self, path):
if path == '':
@@ -106,48 +156,49 @@ class _HackedGetData:
return super().get_data(path)
-class _LoadSourceCompatibility(_HackedGetData, _bootstrap.SourceFileLoader):
+class _LoadSourceCompatibility(_HackedGetData, machinery.SourceFileLoader):
"""Compatibility support for implementing load_source()."""
def load_source(name, pathname, file=None):
- msg = ('imp.load_source() is deprecated; use '
- 'importlib.machinery.SourceFileLoader(name, pathname).load_module()'
- ' instead')
- warnings.warn(msg, DeprecationWarning, 2)
- _LoadSourceCompatibility(name, pathname, file).load_module(name)
- module = sys.modules[name]
+ loader = _LoadSourceCompatibility(name, pathname, file)
+ spec = util.spec_from_file_location(name, pathname, loader=loader)
+ methods = _SpecMethods(spec)
+ if name in sys.modules:
+ module = methods.exec(sys.modules[name])
+ else:
+ module = methods.load()
# To allow reloading to potentially work, use a non-hacked loader which
# won't rely on a now-closed file object.
- module.__loader__ = _bootstrap.SourceFileLoader(name, pathname)
+ module.__loader__ = machinery.SourceFileLoader(name, pathname)
+ module.__spec__.loader = module.__loader__
return module
-class _LoadCompiledCompatibility(_HackedGetData,
- _bootstrap.SourcelessFileLoader):
+class _LoadCompiledCompatibility(_HackedGetData, SourcelessFileLoader):
"""Compatibility support for implementing load_compiled()."""
def load_compiled(name, pathname, file=None):
- msg = ('imp.load_compiled() is deprecated; use '
- 'importlib.machinery.SourcelessFileLoader(name, pathname).'
- 'load_module() instead ')
- warnings.warn(msg, DeprecationWarning, 2)
- _LoadCompiledCompatibility(name, pathname, file).load_module(name)
- module = sys.modules[name]
+ """**DEPRECATED**"""
+ loader = _LoadCompiledCompatibility(name, pathname, file)
+ spec = util.spec_from_file_location(name, pathname, loader=loader)
+ methods = _SpecMethods(spec)
+ if name in sys.modules:
+ module = methods.exec(sys.modules[name])
+ else:
+ module = methods.load()
# To allow reloading to potentially work, use a non-hacked loader which
# won't rely on a now-closed file object.
- module.__loader__ = _bootstrap.SourcelessFileLoader(name, pathname)
+ module.__loader__ = SourcelessFileLoader(name, pathname)
+ module.__spec__.loader = module.__loader__
return module
def load_package(name, path):
- msg = ('imp.load_package() is deprecated; use either '
- 'importlib.machinery.SourceFileLoader() or '
- 'importlib.machinery.SourcelessFileLoader() instead')
- warnings.warn(msg, DeprecationWarning, 2)
+ """**DEPRECATED**"""
if os.path.isdir(path):
extensions = (machinery.SOURCE_SUFFIXES[:] +
machinery.BYTECODE_SUFFIXES[:])
@@ -157,7 +208,13 @@ def load_package(name, path):
break
else:
raise ValueError('{!r} is not a package'.format(path))
- return _bootstrap.SourceFileLoader(name, path).load_module(name)
+ spec = util.spec_from_file_location(name, path,
+ submodule_search_locations=[])
+ methods = _SpecMethods(spec)
+ if name in sys.modules:
+ return methods.exec(sys.modules[name])
+ else:
+ return methods.load()
def load_module(name, file, filename, details):
@@ -169,32 +226,30 @@ def load_module(name, file, filename, details):
"""
suffix, mode, type_ = details
- with warnings.catch_warnings():
- warnings.simplefilter('ignore')
- if mode and (not mode.startswith(('r', 'U')) or '+' in mode):
- raise ValueError('invalid file open mode {!r}'.format(mode))
- elif file is None and type_ in {PY_SOURCE, PY_COMPILED}:
- msg = 'file object required for import (type code {})'.format(type_)
- raise ValueError(msg)
- elif type_ == PY_SOURCE:
- return load_source(name, filename, file)
- elif type_ == PY_COMPILED:
- return load_compiled(name, filename, file)
- elif type_ == C_EXTENSION and load_dynamic is not None:
- if file is None:
- with open(filename, 'rb') as opened_file:
- return load_dynamic(name, filename, opened_file)
- else:
- return load_dynamic(name, filename, file)
- elif type_ == PKG_DIRECTORY:
- return load_package(name, filename)
- elif type_ == C_BUILTIN:
- return init_builtin(name)
- elif type_ == PY_FROZEN:
- return init_frozen(name)
+ if mode and (not mode.startswith(('r', 'U')) or '+' in mode):
+ raise ValueError('invalid file open mode {!r}'.format(mode))
+ elif file is None and type_ in {PY_SOURCE, PY_COMPILED}:
+ msg = 'file object required for import (type code {})'.format(type_)
+ raise ValueError(msg)
+ elif type_ == PY_SOURCE:
+ return load_source(name, filename, file)
+ elif type_ == PY_COMPILED:
+ return load_compiled(name, filename, file)
+ elif type_ == C_EXTENSION and load_dynamic is not None:
+ if file is None:
+ with open(filename, 'rb') as opened_file:
+ return load_dynamic(name, filename, opened_file)
else:
- msg = "Don't know how to import {} (type code {})".format(name, type_)
- raise ImportError(msg, name=name)
+ return load_dynamic(name, filename, file)
+ elif type_ == PKG_DIRECTORY:
+ return load_package(name, filename)
+ elif type_ == C_BUILTIN:
+ return init_builtin(name)
+ elif type_ == PY_FROZEN:
+ return init_frozen(name)
+ else:
+ msg = "Don't know how to import {} (type code {})".format(name, type_)
+ raise ImportError(msg, name=name)
def find_module(name, path=None):
@@ -230,54 +285,31 @@ def find_module(name, path=None):
file_path = os.path.join(package_directory, package_file_name)
if os.path.isfile(file_path):
return None, package_directory, ('', '', PKG_DIRECTORY)
- with warnings.catch_warnings():
- warnings.simplefilter('ignore')
- for suffix, mode, type_ in get_suffixes():
- file_name = name + suffix
- file_path = os.path.join(entry, file_name)
- if os.path.isfile(file_path):
- break
- else:
- continue
- break # Break out of outer loop when breaking out of inner loop.
+ for suffix, mode, type_ in get_suffixes():
+ file_name = name + suffix
+ file_path = os.path.join(entry, file_name)
+ if os.path.isfile(file_path):
+ break
+ else:
+ continue
+ break # Break out of outer loop when breaking out of inner loop.
else:
- raise ImportError(_bootstrap._ERR_MSG.format(name), name=name)
+ raise ImportError(_ERR_MSG.format(name), name=name)
encoding = None
- if mode == 'U':
+ if 'b' not in mode:
with open(file_path, 'rb') as file:
encoding = tokenize.detect_encoding(file.readline)[0]
file = open(file_path, mode, encoding=encoding)
return file, file_path, (suffix, mode, type_)
-_RELOADING = {}
-
def reload(module):
- """Reload the module and return it.
+ """**DEPRECATED**
+
+ Reload the module and return it.
The module must have been successfully imported before.
"""
- if not module or type(module) != type(sys):
- raise TypeError("reload() argument must be module")
- name = module.__name__
- if name not in sys.modules:
- msg = "module {} not in sys.modules"
- raise ImportError(msg.format(name), name=name)
- if name in _RELOADING:
- return _RELOADING[name]
- _RELOADING[name] = module
- try:
- parent_name = name.rpartition('.')[0]
- if parent_name and parent_name not in sys.modules:
- msg = "parent {!r} not in sys.modules"
- raise ImportError(msg.format(parent_name), name=parent_name)
- module.__loader__.load_module(name)
- # The module may have replaced itself in sys.modules!
- return sys.modules[module.__name__]
- finally:
- try:
- del _RELOADING[name]
- except KeyError:
- pass
+ return importlib.reload(module)
diff --git a/Lib/importlib/__init__.py b/Lib/importlib/__init__.py
index 22c90f2..1bc9947 100644
--- a/Lib/importlib/__init__.py
+++ b/Lib/importlib/__init__.py
@@ -1,5 +1,5 @@
"""A pure Python implementation of import."""
-__all__ = ['__import__', 'import_module', 'invalidate_caches']
+__all__ = ['__import__', 'import_module', 'invalidate_caches', 'reload']
# Bootstrap help #####################################################
@@ -22,7 +22,12 @@ else:
# a second copy of the module.
_bootstrap.__name__ = 'importlib._bootstrap'
_bootstrap.__package__ = 'importlib'
- _bootstrap.__file__ = __file__.replace('__init__.py', '_bootstrap.py')
+ try:
+ _bootstrap.__file__ = __file__.replace('__init__.py', '_bootstrap.py')
+ except NameError:
+ # __file__ is not guaranteed to be defined, e.g. if this code gets
+ # frozen by a tool like cx_Freeze.
+ pass
sys.modules['importlib._bootstrap'] = _bootstrap
# To simplify imports in test code
@@ -32,6 +37,10 @@ _r_long = _bootstrap._r_long
# Fully bootstrapped at this point, import whatever you like, circular
# dependencies and startup overhead minimisation permitting :)
+import types
+import warnings
+
+
# Public API #########################################################
from ._bootstrap import __import__
@@ -46,20 +55,15 @@ def invalidate_caches():
def find_loader(name, path=None):
- """Find the loader for the specified module.
+ """Return the loader for the specified module.
- First, sys.modules is checked to see if the module was already imported. If
- so, then sys.modules[name].__loader__ is returned. If that happens to be
- set to None, then ValueError is raised. If the module is not in
- sys.modules, then sys.meta_path is searched for a suitable loader with the
- value of 'path' given to the finders. None is returned if no loader could
- be found.
+ This is a backward-compatible wrapper around find_spec().
- Dotted names do not have their parent packages implicitly imported. You will
- most likely need to explicitly import all parent packages in the proper
- order for a submodule to get the correct loader.
+ This function is deprecated in favor of importlib.util.find_spec().
"""
+ warnings.warn('Use importlib.util.find_spec() instead.',
+ DeprecationWarning, stacklevel=2)
try:
loader = sys.modules[name].__loader__
if loader is None:
@@ -68,7 +72,20 @@ def find_loader(name, path=None):
return loader
except KeyError:
pass
- return _bootstrap._find_module(name, path)
+ except AttributeError:
+ raise ValueError('{}.__loader__ is not set'.format(name))
+
+ spec = _bootstrap._find_spec(name, path)
+ # We won't worry about malformed specs (missing attributes).
+ if spec is None:
+ return None
+ if spec.loader is None:
+ if spec.submodule_search_locations is None:
+ raise ImportError('spec for {} missing loader'.format(name),
+ name=name)
+ raise ImportError('namespace packages do not have loaders',
+ name=name)
+ return spec.loader
def import_module(name, package=None):
@@ -82,9 +99,58 @@ def import_module(name, package=None):
level = 0
if name.startswith('.'):
if not package:
- raise TypeError("relative imports require the 'package' argument")
+ msg = ("the 'package' argument is required to perform a relative "
+ "import for {!r}")
+ raise TypeError(msg.format(name))
for character in name:
if character != '.':
break
level += 1
return _bootstrap._gcd_import(name[level:], package, level)
+
+
+_RELOADING = {}
+
+
+def reload(module):
+ """Reload the module and return it.
+
+ The module must have been successfully imported before.
+
+ """
+ if not module or not isinstance(module, types.ModuleType):
+ raise TypeError("reload() argument must be module")
+ try:
+ name = module.__spec__.name
+ except AttributeError:
+ name = module.__name__
+
+ if sys.modules.get(name) is not module:
+ msg = "module {} not in sys.modules"
+ raise ImportError(msg.format(name), name=name)
+ if name in _RELOADING:
+ return _RELOADING[name]
+ _RELOADING[name] = module
+ try:
+ parent_name = name.rpartition('.')[0]
+ if parent_name:
+ try:
+ parent = sys.modules[parent_name]
+ except KeyError:
+ msg = "parent {!r} not in sys.modules"
+ raise ImportError(msg.format(parent_name), name=parent_name)
+ else:
+ pkgpath = parent.__path__
+ else:
+ pkgpath = None
+ target = module
+ spec = module.__spec__ = _bootstrap._find_spec(name, pkgpath, target)
+ methods = _bootstrap._SpecMethods(spec)
+ methods.exec(module)
+ # The module may have replaced itself in sys.modules!
+ return sys.modules[name]
+ finally:
+ try:
+ del _RELOADING[name]
+ except KeyError:
+ pass
diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py
index e40ec92..beaa9b3 100644
--- a/Lib/importlib/_bootstrap.py
+++ b/Lib/importlib/_bootstrap.py
@@ -9,7 +9,7 @@ work. One should use importlib as the public-facing version of this module.
#
# IMPORTANT: Whenever making changes to this module, be sure to run
# a top-level make in order to get the frozen version of the module
-# update. Not doing so, will result in the Makefile to fail for
+# update. Not doing so will result in the Makefile to fail for
# all others who don't have a ./python around to freeze the module
# in the early stages of compilation.
#
@@ -20,10 +20,6 @@ work. One should use importlib as the public-facing version of this module.
# reference any injected objects! This includes not only global code but also
# anything specified at the class level.
-# XXX Make sure all public names have no single leading underscore and all
-# others do.
-
-
# Bootstrap-related code ######################################################
_CASE_INSENSITIVE_PLATFORMS = 'win', 'cygwin', 'darwin'
@@ -41,76 +37,58 @@ def _make_relax_case():
return _relax_case
-# TODO: Expose from marshal
def _w_long(x):
- """Convert a 32-bit integer to little-endian.
+ """Convert a 32-bit integer to little-endian."""
+ return (int(x) & 0xFFFFFFFF).to_bytes(4, 'little')
- XXX Temporary until marshal's long functions are exposed.
-
- """
- x = int(x)
- int_bytes = []
- int_bytes.append(x & 0xFF)
- int_bytes.append((x >> 8) & 0xFF)
- int_bytes.append((x >> 16) & 0xFF)
- int_bytes.append((x >> 24) & 0xFF)
- return bytearray(int_bytes)
-
-# TODO: Expose from marshal
def _r_long(int_bytes):
- """Convert 4 bytes in little-endian to an integer.
-
- XXX Temporary until marshal's long function are exposed.
-
- """
- x = int_bytes[0]
- x |= int_bytes[1] << 8
- x |= int_bytes[2] << 16
- x |= int_bytes[3] << 24
- return x
+ """Convert 4 bytes in little-endian to an integer."""
+ return int.from_bytes(int_bytes, 'little')
def _path_join(*path_parts):
"""Replacement for os.path.join()."""
- new_parts = []
- for part in path_parts:
- if not part:
- continue
- new_parts.append(part)
- if part[-1] not in path_separators:
- new_parts.append(path_sep)
- return ''.join(new_parts[:-1]) # Drop superfluous path separator.
+ return path_sep.join([part.rstrip(path_separators)
+ for part in path_parts if part])
def _path_split(path):
"""Replacement for os.path.split()."""
+ if len(path_separators) == 1:
+ front, _, tail = path.rpartition(path_sep)
+ return front, tail
for x in reversed(path):
if x in path_separators:
- sep = x
- break
- else:
- sep = path_sep
- front, _, tail = path.rpartition(sep)
- return front, tail
+ front, tail = path.rsplit(x, maxsplit=1)
+ return front, tail
+ return '', path
+
+
+def _path_stat(path):
+ """Stat the path.
+
+ Made a separate function to make it easier to override in experiments
+ (e.g. cache stat results).
+
+ """
+ return _os.stat(path)
def _path_is_mode_type(path, mode):
"""Test whether the path is the specified mode type."""
try:
- stat_info = _os.stat(path)
+ stat_info = _path_stat(path)
except OSError:
return False
return (stat_info.st_mode & 0o170000) == mode
-# XXX Could also expose Modules/getpath.c:isfile()
def _path_isfile(path):
"""Replacement for os.path.isfile."""
return _path_is_mode_type(path, 0o100000)
-# XXX Could also expose Modules/getpath.c:isdir()
def _path_isdir(path):
"""Replacement for os.path.isdir."""
if not path:
@@ -148,17 +126,30 @@ def _wrap(new, old):
new.__dict__.update(old.__dict__)
+def _new_module(name):
+ return type(sys)(name)
+
+
_code_type = type(_wrap.__code__)
-def new_module(name):
- """Create a new module.
- The module is not entered into sys.modules.
+class _ManageReload:
- """
- return type(_io)(name)
+ """Manages the possible clean-up of sys.modules for load_module()."""
+
+ def __init__(self, name):
+ self._name = name
+ def __enter__(self):
+ self._is_reload = self._name in sys.modules
+
+ def __exit__(self, *args):
+ if any(arg is not None for arg in args) and not self._is_reload:
+ try:
+ del sys.modules[self._name]
+ except KeyError:
+ pass
# Module-level locking ########################################################
@@ -214,7 +205,7 @@ class _ModuleLock:
self.count += 1
return True
if self.has_deadlock():
- raise _DeadlockError("deadlock detected by %r" % self)
+ raise _DeadlockError('deadlock detected by %r' % self)
if self.wakeup.acquire(False):
self.waiters += 1
# Wait for a release() call
@@ -227,7 +218,7 @@ class _ModuleLock:
tid = _thread.get_ident()
with self.lock:
if self.owner != tid:
- raise RuntimeError("cannot release un-acquired lock")
+ raise RuntimeError('cannot release un-acquired lock')
assert self.count > 0
self.count -= 1
if self.count == 0:
@@ -237,7 +228,7 @@ class _ModuleLock:
self.wakeup.release()
def __repr__(self):
- return "_ModuleLock(%r) at %d" % (self.name, id(self))
+ return '_ModuleLock({!r}) at {}'.format(self.name, id(self))
class _DummyModuleLock:
@@ -254,11 +245,28 @@ class _DummyModuleLock:
def release(self):
if self.count == 0:
- raise RuntimeError("cannot release un-acquired lock")
+ raise RuntimeError('cannot release un-acquired lock')
self.count -= 1
def __repr__(self):
- return "_DummyModuleLock(%r) at %d" % (self.name, id(self))
+ return '_DummyModuleLock({!r}) at {}'.format(self.name, id(self))
+
+
+class _ModuleLockManager:
+
+ def __init__(self, name):
+ self._name = name
+ self._lock = None
+
+ def __enter__(self):
+ try:
+ self._lock = _get_module_lock(self._name)
+ finally:
+ _imp.release_lock()
+ self._lock.acquire()
+
+ def __exit__(self, *args, **kwargs):
+ self._lock.release()
# The following two functions are for consumption by Python/import.c.
@@ -315,95 +323,109 @@ def _call_with_frames_removed(f, *args, **kwds):
# Finder/loader utility code ###############################################
-"""Magic word to reject .pyc files generated by other Python versions.
-It should change for each incompatible change to the bytecode.
-
-The value of CR and LF is incorporated so if you ever read or write
-a .pyc file in text mode the magic number will be wrong; also, the
-Apple MPW compiler swaps their values, botching string constants.
-
-The magic numbers must be spaced apart at least 2 values, as the
--U interpeter flag will cause MAGIC+1 being used. They have been
-odd numbers for some time now.
-
-There were a variety of old schemes for setting the magic number.
-The current working scheme is to increment the previous value by
-10.
-
-Starting with the adoption of PEP 3147 in Python 3.2, every bump in magic
-number also includes a new "magic tag", i.e. a human readable string used
-to represent the magic number in __pycache__ directories. When you change
-the magic number, you must also set a new unique magic tag. Generally this
-can be named after the Python major version of the magic number bump, but
-it can really be anything, as long as it's different than anything else
-that's come before. The tags are included in the following table, starting
-with Python 3.2a0.
-
-Known values:
- Python 1.5: 20121
- Python 1.5.1: 20121
- Python 1.5.2: 20121
- Python 1.6: 50428
- Python 2.0: 50823
- Python 2.0.1: 50823
- Python 2.1: 60202
- Python 2.1.1: 60202
- Python 2.1.2: 60202
- Python 2.2: 60717
- Python 2.3a0: 62011
- Python 2.3a0: 62021
- Python 2.3a0: 62011 (!)
- Python 2.4a0: 62041
- Python 2.4a3: 62051
- Python 2.4b1: 62061
- Python 2.5a0: 62071
- Python 2.5a0: 62081 (ast-branch)
- Python 2.5a0: 62091 (with)
- Python 2.5a0: 62092 (changed WITH_CLEANUP opcode)
- Python 2.5b3: 62101 (fix wrong code: for x, in ...)
- Python 2.5b3: 62111 (fix wrong code: x += yield)
- Python 2.5c1: 62121 (fix wrong lnotab with for loops and
- storing constants that should have been removed)
- Python 2.5c2: 62131 (fix wrong code: for x, in ... in listcomp/genexp)
- Python 2.6a0: 62151 (peephole optimizations and STORE_MAP opcode)
- Python 2.6a1: 62161 (WITH_CLEANUP optimization)
- Python 3000: 3000
- 3010 (removed UNARY_CONVERT)
- 3020 (added BUILD_SET)
- 3030 (added keyword-only parameters)
- 3040 (added signature annotations)
- 3050 (print becomes a function)
- 3060 (PEP 3115 metaclass syntax)
- 3061 (string literals become unicode)
- 3071 (PEP 3109 raise changes)
- 3081 (PEP 3137 make __file__ and __name__ unicode)
- 3091 (kill str8 interning)
- 3101 (merge from 2.6a0, see 62151)
- 3103 (__file__ points to source file)
- Python 3.0a4: 3111 (WITH_CLEANUP optimization).
- Python 3.0a5: 3131 (lexical exception stacking, including POP_EXCEPT)
- Python 3.1a0: 3141 (optimize list, set and dict comprehensions:
- change LIST_APPEND and SET_ADD, add MAP_ADD)
- Python 3.1a0: 3151 (optimize conditional branches:
- introduce POP_JUMP_IF_FALSE and POP_JUMP_IF_TRUE)
- Python 3.2a0: 3160 (add SETUP_WITH)
- tag: cpython-32
- Python 3.2a1: 3170 (add DUP_TOP_TWO, remove DUP_TOPX and ROT_FOUR)
- tag: cpython-32
- Python 3.2a2 3180 (add DELETE_DEREF)
- Python 3.3a0 3190 __class__ super closure changed
- Python 3.3a0 3200 (__qualname__ added)
- 3210 (added size modulo 2**32 to the pyc header)
- Python 3.3a1 3220 (changed PEP 380 implementation)
- Python 3.3a4 3230 (revert changes to implicit __class__ closure)
-
-MAGIC must change whenever the bytecode emitted by the compiler may no
-longer be understood by older implementations of the eval loop (usually
-due to the addition of new opcodes).
+# Magic word to reject .pyc files generated by other Python versions.
+# It should change for each incompatible change to the bytecode.
+#
+# The value of CR and LF is incorporated so if you ever read or write
+# a .pyc file in text mode the magic number will be wrong; also, the
+# Apple MPW compiler swaps their values, botching string constants.
+#
+# The magic numbers must be spaced apart at least 2 values, as the
+# -U interpeter flag will cause MAGIC+1 being used. They have been
+# odd numbers for some time now.
+#
+# There were a variety of old schemes for setting the magic number.
+# The current working scheme is to increment the previous value by
+# 10.
+#
+# Starting with the adoption of PEP 3147 in Python 3.2, every bump in magic
+# number also includes a new "magic tag", i.e. a human readable string used
+# to represent the magic number in __pycache__ directories. When you change
+# the magic number, you must also set a new unique magic tag. Generally this
+# can be named after the Python major version of the magic number bump, but
+# it can really be anything, as long as it's different than anything else
+# that's come before. The tags are included in the following table, starting
+# with Python 3.2a0.
+#
+# Known values:
+# Python 1.5: 20121
+# Python 1.5.1: 20121
+# Python 1.5.2: 20121
+# Python 1.6: 50428
+# Python 2.0: 50823
+# Python 2.0.1: 50823
+# Python 2.1: 60202
+# Python 2.1.1: 60202
+# Python 2.1.2: 60202
+# Python 2.2: 60717
+# Python 2.3a0: 62011
+# Python 2.3a0: 62021
+# Python 2.3a0: 62011 (!)
+# Python 2.4a0: 62041
+# Python 2.4a3: 62051
+# Python 2.4b1: 62061
+# Python 2.5a0: 62071
+# Python 2.5a0: 62081 (ast-branch)
+# Python 2.5a0: 62091 (with)
+# Python 2.5a0: 62092 (changed WITH_CLEANUP opcode)
+# Python 2.5b3: 62101 (fix wrong code: for x, in ...)
+# Python 2.5b3: 62111 (fix wrong code: x += yield)
+# Python 2.5c1: 62121 (fix wrong lnotab with for loops and
+# storing constants that should have been removed)
+# Python 2.5c2: 62131 (fix wrong code: for x, in ... in listcomp/genexp)
+# Python 2.6a0: 62151 (peephole optimizations and STORE_MAP opcode)
+# Python 2.6a1: 62161 (WITH_CLEANUP optimization)
+# Python 2.7a0: 62171 (optimize list comprehensions/change LIST_APPEND)
+# Python 2.7a0: 62181 (optimize conditional branches:
+# introduce POP_JUMP_IF_FALSE and POP_JUMP_IF_TRUE)
+# Python 2.7a0 62191 (introduce SETUP_WITH)
+# Python 2.7a0 62201 (introduce BUILD_SET)
+# Python 2.7a0 62211 (introduce MAP_ADD and SET_ADD)
+# Python 3000: 3000
+# 3010 (removed UNARY_CONVERT)
+# 3020 (added BUILD_SET)
+# 3030 (added keyword-only parameters)
+# 3040 (added signature annotations)
+# 3050 (print becomes a function)
+# 3060 (PEP 3115 metaclass syntax)
+# 3061 (string literals become unicode)
+# 3071 (PEP 3109 raise changes)
+# 3081 (PEP 3137 make __file__ and __name__ unicode)
+# 3091 (kill str8 interning)
+# 3101 (merge from 2.6a0, see 62151)
+# 3103 (__file__ points to source file)
+# Python 3.0a4: 3111 (WITH_CLEANUP optimization).
+# Python 3.0a5: 3131 (lexical exception stacking, including POP_EXCEPT)
+# Python 3.1a0: 3141 (optimize list, set and dict comprehensions:
+# change LIST_APPEND and SET_ADD, add MAP_ADD)
+# Python 3.1a0: 3151 (optimize conditional branches:
+# introduce POP_JUMP_IF_FALSE and POP_JUMP_IF_TRUE)
+# Python 3.2a0: 3160 (add SETUP_WITH)
+# tag: cpython-32
+# Python 3.2a1: 3170 (add DUP_TOP_TWO, remove DUP_TOPX and ROT_FOUR)
+# tag: cpython-32
+# Python 3.2a2 3180 (add DELETE_DEREF)
+# Python 3.3a0 3190 __class__ super closure changed
+# Python 3.3a0 3200 (__qualname__ added)
+# 3210 (added size modulo 2**32 to the pyc header)
+# Python 3.3a1 3220 (changed PEP 380 implementation)
+# Python 3.3a4 3230 (revert changes to implicit __class__ closure)
+# Python 3.4a1 3250 (evaluate positional default arguments before
+# keyword-only defaults)
+# Python 3.4a1 3260 (add LOAD_CLASSDEREF; allow locals of class to override
+# free vars)
+# Python 3.4a1 3270 (various tweaks to the __class__ closure)
+# Python 3.4a1 3280 (remove implicit class argument)
+# Python 3.4a4 3290 (changes to __qualname__ computation)
+# Python 3.4a4 3300 (more changes to __qualname__ computation)
+# Python 3.4rc2 3310 (alter __qualname__ computation)
+#
+# MAGIC must change whenever the bytecode emitted by the compiler may no
+# longer be understood by older implementations of the eval loop (usually
+# due to the addition of new opcodes).
-"""
-_RAW_MAGIC_NUMBER = 3230 | ord('\r') << 16 | ord('\n') << 24
-_MAGIC_BYTES = bytes(_RAW_MAGIC_NUMBER >> n & 0xff for n in range(0, 25, 8))
+MAGIC_NUMBER = (3310).to_bytes(2, 'little') + b'\r\n'
+_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
_PYCACHE = '__pycache__'
@@ -481,6 +503,18 @@ def _get_sourcefile(bytecode_path):
return source_path if _path_isfile(source_path) else bytecode_path
+def _calc_mode(path):
+ """Calculate the mode permissions for a bytecode file."""
+ try:
+ mode = _path_stat(path).st_mode
+ except OSError:
+ mode = 0o666
+ # We always ensure write access so we can update cached files
+ # later even when the source files are read-only on Windows (#6074)
+ mode |= 0o200
+ return mode
+
+
def _verbose_message(message, *args, verbosity=1):
"""Print the message to stderr if -v/PYTHONVERBOSE is turned on."""
if sys.flags.verbose >= verbosity:
@@ -489,85 +523,6 @@ def _verbose_message(message, *args, verbosity=1):
print(message.format(*args), file=sys.stderr)
-def set_package(fxn):
- """Set __package__ on the returned module."""
- def set_package_wrapper(*args, **kwargs):
- module = fxn(*args, **kwargs)
- if getattr(module, '__package__', None) is None:
- module.__package__ = module.__name__
- if not hasattr(module, '__path__'):
- module.__package__ = module.__package__.rpartition('.')[0]
- return module
- _wrap(set_package_wrapper, fxn)
- return set_package_wrapper
-
-
-def set_loader(fxn):
- """Set __loader__ on the returned module."""
- def set_loader_wrapper(self, *args, **kwargs):
- module = fxn(self, *args, **kwargs)
- if not hasattr(module, '__loader__'):
- module.__loader__ = self
- return module
- _wrap(set_loader_wrapper, fxn)
- return set_loader_wrapper
-
-
-def module_for_loader(fxn):
- """Decorator to handle selecting the proper module for loaders.
-
- The decorated function is passed the module to use instead of the module
- name. The module passed in to the function is either from sys.modules if
- it already exists or is a new module. If the module is new, then __name__
- is set the first argument to the method, __loader__ is set to self, and
- __package__ is set accordingly (if self.is_package() is defined) will be set
- before it is passed to the decorated function (if self.is_package() does
- not work for the module it will be set post-load).
-
- If an exception is raised and the decorator created the module it is
- subsequently removed from sys.modules.
-
- The decorator assumes that the decorated function takes the module name as
- the second argument.
-
- """
- def module_for_loader_wrapper(self, fullname, *args, **kwargs):
- module = sys.modules.get(fullname)
- is_reload = module is not None
- if not is_reload:
- # This must be done before open() is called as the 'io' module
- # implicitly imports 'locale' and would otherwise trigger an
- # infinite loop.
- module = new_module(fullname)
- # This must be done before putting the module in sys.modules
- # (otherwise an optimization shortcut in import.c becomes wrong)
- module.__initializing__ = True
- sys.modules[fullname] = module
- module.__loader__ = self
- try:
- is_package = self.is_package(fullname)
- except (ImportError, AttributeError):
- pass
- else:
- if is_package:
- module.__package__ = fullname
- else:
- module.__package__ = fullname.rpartition('.')[0]
- else:
- module.__initializing__ = True
- try:
- # If __package__ was not set above, __import__() will do it later.
- return fxn(self, module, *args, **kwargs)
- except:
- if not is_reload:
- del sys.modules[fullname]
- raise
- finally:
- module.__initializing__ = False
- _wrap(module_for_loader_wrapper, fxn)
- return module_for_loader_wrapper
-
-
def _check_name(method):
"""Decorator to verify that the module being requested matches the one the
loader can handle.
@@ -580,7 +535,7 @@ def _check_name(method):
if name is None:
name = self.name
elif self.name != name:
- raise ImportError("loader cannot handle %s" % name, name=name)
+ raise ImportError('loader cannot handle %s' % name, name=name)
return method(self, name, *args, **kwargs)
_wrap(_check_name_wrapper, method)
return _check_name_wrapper
@@ -590,7 +545,7 @@ def _requires_builtin(fxn):
"""Decorator to verify the named module is built-in."""
def _requires_builtin_wrapper(self, fullname):
if fullname not in sys.builtin_module_names:
- raise ImportError("{} is not a built-in module".format(fullname),
+ raise ImportError('{!r} is not a built-in module'.format(fullname),
name=fullname)
return fxn(self, fullname)
_wrap(_requires_builtin_wrapper, fxn)
@@ -601,7 +556,7 @@ def _requires_frozen(fxn):
"""Decorator to verify the named module is frozen."""
def _requires_frozen_wrapper(self, fullname):
if not _imp.is_frozen(fullname):
- raise ImportError("{} is not a frozen module".format(fullname),
+ raise ImportError('{!r} is not a frozen module'.format(fullname),
name=fullname)
return fxn(self, fullname)
_wrap(_requires_frozen_wrapper, fxn)
@@ -610,17 +565,659 @@ def _requires_frozen(fxn):
def _find_module_shim(self, fullname):
"""Try to find a loader for the specified module by delegating to
- self.find_loader()."""
+ self.find_loader().
+
+ This method is deprecated in favor of finder.find_spec().
+
+ """
# Call find_loader(). If it returns a string (indicating this
# is a namespace package portion), generate a warning and
# return None.
loader, portions = self.find_loader(fullname)
if loader is None and len(portions):
- msg = "Not importing directory {}: missing __init__"
+ msg = 'Not importing directory {}: missing __init__'
_warnings.warn(msg.format(portions[0]), ImportWarning)
return loader
+def _load_module_shim(self, fullname):
+ """Load the specified module into sys.modules and return it.
+
+ This method is deprecated. Use loader.exec_module instead.
+
+ """
+ spec = spec_from_loader(fullname, self)
+ methods = _SpecMethods(spec)
+ if fullname in sys.modules:
+ module = sys.modules[fullname]
+ methods.exec(module)
+ return sys.modules[fullname]
+ else:
+ return methods.load()
+
+
+def _validate_bytecode_header(data, source_stats=None, name=None, path=None):
+ """Validate the header of the passed-in bytecode against source_stats (if
+ given) and returning the bytecode that can be compiled by compile().
+
+ All other arguments are used to enhance error reporting.
+
+ ImportError is raised when the magic number is incorrect or the bytecode is
+ found to be stale. EOFError is raised when the data is found to be
+ truncated.
+
+ """
+ exc_details = {}
+ if name is not None:
+ exc_details['name'] = name
+ else:
+ # To prevent having to make all messages have a conditional name.
+ name = '<bytecode>'
+ if path is not None:
+ exc_details['path'] = path
+ magic = data[:4]
+ raw_timestamp = data[4:8]
+ raw_size = data[8:12]
+ if magic != MAGIC_NUMBER:
+ message = 'bad magic number in {!r}: {!r}'.format(name, magic)
+ _verbose_message(message)
+ raise ImportError(message, **exc_details)
+ elif len(raw_timestamp) != 4:
+ message = 'reached EOF while reading timestamp in {!r}'.format(name)
+ _verbose_message(message)
+ raise EOFError(message)
+ elif len(raw_size) != 4:
+ message = 'reached EOF while reading size of source in {!r}'.format(name)
+ _verbose_message(message)
+ raise EOFError(message)
+ if source_stats is not None:
+ try:
+ source_mtime = int(source_stats['mtime'])
+ except KeyError:
+ pass
+ else:
+ if _r_long(raw_timestamp) != source_mtime:
+ message = 'bytecode is stale for {!r}'.format(name)
+ _verbose_message(message)
+ raise ImportError(message, **exc_details)
+ try:
+ source_size = source_stats['size'] & 0xFFFFFFFF
+ except KeyError:
+ pass
+ else:
+ if _r_long(raw_size) != source_size:
+ raise ImportError('bytecode is stale for {!r}'.format(name),
+ **exc_details)
+ return data[12:]
+
+
+def _compile_bytecode(data, name=None, bytecode_path=None, source_path=None):
+ """Compile bytecode as returned by _validate_bytecode_header()."""
+ code = marshal.loads(data)
+ if isinstance(code, _code_type):
+ _verbose_message('code object from {!r}', bytecode_path)
+ if source_path is not None:
+ _imp._fix_co_filename(code, source_path)
+ return code
+ else:
+ raise ImportError('Non-code object in {!r}'.format(bytecode_path),
+ name=name, path=bytecode_path)
+
+def _code_to_bytecode(code, mtime=0, source_size=0):
+ """Compile a code object into bytecode for writing out to a byte-compiled
+ file."""
+ data = bytearray(MAGIC_NUMBER)
+ data.extend(_w_long(mtime))
+ data.extend(_w_long(source_size))
+ data.extend(marshal.dumps(code))
+ return data
+
+
+def decode_source(source_bytes):
+ """Decode bytes representing source code and return the string.
+
+ Universal newline support is used in the decoding.
+ """
+ import tokenize # To avoid bootstrap issues.
+ source_bytes_readline = _io.BytesIO(source_bytes).readline
+ encoding = tokenize.detect_encoding(source_bytes_readline)
+ newline_decoder = _io.IncrementalNewlineDecoder(None, True)
+ return newline_decoder.decode(source_bytes.decode(encoding[0]))
+
+
+# Module specifications #######################################################
+
+def _module_repr(module):
+ # The implementation of ModuleType__repr__().
+ loader = getattr(module, '__loader__', None)
+ if hasattr(loader, 'module_repr'):
+ # As soon as BuiltinImporter, FrozenImporter, and NamespaceLoader
+ # drop their implementations for module_repr. we can add a
+ # deprecation warning here.
+ try:
+ return loader.module_repr(module)
+ except Exception:
+ pass
+ try:
+ spec = module.__spec__
+ except AttributeError:
+ pass
+ else:
+ if spec is not None:
+ return _SpecMethods(spec).module_repr()
+
+ # We could use module.__class__.__name__ instead of 'module' in the
+ # various repr permutations.
+ try:
+ name = module.__name__
+ except AttributeError:
+ name = '?'
+ try:
+ filename = module.__file__
+ except AttributeError:
+ if loader is None:
+ return '<module {!r}>'.format(name)
+ else:
+ return '<module {!r} ({!r})>'.format(name, loader)
+ else:
+ return '<module {!r} from {!r}>'.format(name, filename)
+
+
+class _installed_safely:
+
+ def __init__(self, module):
+ self._module = module
+ self._spec = module.__spec__
+
+ def __enter__(self):
+ # This must be done before putting the module in sys.modules
+ # (otherwise an optimization shortcut in import.c becomes
+ # wrong)
+ self._spec._initializing = True
+ sys.modules[self._spec.name] = self._module
+
+ def __exit__(self, *args):
+ try:
+ spec = self._spec
+ if any(arg is not None for arg in args):
+ try:
+ del sys.modules[spec.name]
+ except KeyError:
+ pass
+ else:
+ _verbose_message('import {!r} # {!r}', spec.name, spec.loader)
+ finally:
+ self._spec._initializing = False
+
+
+class ModuleSpec:
+ """The specification for a module, used for loading.
+
+ A module's spec is the source for information about the module. For
+ data associated with the module, including source, use the spec's
+ loader.
+
+ `name` is the absolute name of the module. `loader` is the loader
+ to use when loading the module. `parent` is the name of the
+ package the module is in. The parent is derived from the name.
+
+ `is_package` determines if the module is considered a package or
+ not. On modules this is reflected by the `__path__` attribute.
+
+ `origin` is the specific location used by the loader from which to
+ load the module, if that information is available. When filename is
+ set, origin will match.
+
+ `has_location` indicates that a spec's "origin" reflects a location.
+ When this is True, `__file__` attribute of the module is set.
+
+ `cached` is the location of the cached bytecode file, if any. It
+ corresponds to the `__cached__` attribute.
+
+ `submodule_search_locations` is the sequence of path entries to
+ search when importing submodules. If set, is_package should be
+ True--and False otherwise.
+
+ Packages are simply modules that (may) have submodules. If a spec
+ has a non-None value in `submodule_search_locations`, the import
+ system will consider modules loaded from the spec as packages.
+
+ Only finders (see importlib.abc.MetaPathFinder and
+ importlib.abc.PathEntryFinder) should modify ModuleSpec instances.
+
+ """
+
+ def __init__(self, name, loader, *, origin=None, loader_state=None,
+ is_package=None):
+ self.name = name
+ self.loader = loader
+ self.origin = origin
+ self.loader_state = loader_state
+ self.submodule_search_locations = [] if is_package else None
+
+ # file-location attributes
+ self._set_fileattr = False
+ self._cached = None
+
+ def __repr__(self):
+ args = ['name={!r}'.format(self.name),
+ 'loader={!r}'.format(self.loader)]
+ if self.origin is not None:
+ args.append('origin={!r}'.format(self.origin))
+ if self.submodule_search_locations is not None:
+ args.append('submodule_search_locations={}'
+ .format(self.submodule_search_locations))
+ return '{}({})'.format(self.__class__.__name__, ', '.join(args))
+
+ def __eq__(self, other):
+ smsl = self.submodule_search_locations
+ try:
+ return (self.name == other.name and
+ self.loader == other.loader and
+ self.origin == other.origin and
+ smsl == other.submodule_search_locations and
+ self.cached == other.cached and
+ self.has_location == other.has_location)
+ except AttributeError:
+ return False
+
+ @property
+ def cached(self):
+ if self._cached is None:
+ if self.origin is not None and self._set_fileattr:
+ filename = self.origin
+ if filename.endswith(tuple(SOURCE_SUFFIXES)):
+ try:
+ self._cached = cache_from_source(filename)
+ except NotImplementedError:
+ pass
+ elif filename.endswith(tuple(BYTECODE_SUFFIXES)):
+ self._cached = filename
+ return self._cached
+
+ @cached.setter
+ def cached(self, cached):
+ self._cached = cached
+
+ @property
+ def parent(self):
+ """The name of the module's parent."""
+ if self.submodule_search_locations is None:
+ return self.name.rpartition('.')[0]
+ else:
+ return self.name
+
+ @property
+ def has_location(self):
+ return self._set_fileattr
+
+ @has_location.setter
+ def has_location(self, value):
+ self._set_fileattr = bool(value)
+
+
+def spec_from_loader(name, loader, *, origin=None, is_package=None):
+ """Return a module spec based on various loader methods."""
+ if hasattr(loader, 'get_filename'):
+ if is_package is None:
+ return spec_from_file_location(name, loader=loader)
+ search = [] if is_package else None
+ return spec_from_file_location(name, loader=loader,
+ submodule_search_locations=search)
+
+ if is_package is None:
+ if hasattr(loader, 'is_package'):
+ try:
+ is_package = loader.is_package(name)
+ except ImportError:
+ is_package = None # aka, undefined
+ else:
+ # the default
+ is_package = False
+
+ return ModuleSpec(name, loader, origin=origin, is_package=is_package)
+
+
+_POPULATE = object()
+
+
+def spec_from_file_location(name, location=None, *, loader=None,
+ submodule_search_locations=_POPULATE):
+ """Return a module spec based on a file location.
+
+ To indicate that the module is a package, set
+ submodule_search_locations to a list of directory paths. An
+ empty list is sufficient, though its not otherwise useful to the
+ import system.
+
+ The loader must take a spec as its only __init__() arg.
+
+ """
+ if location is None:
+ # The caller may simply want a partially populated location-
+ # oriented spec. So we set the location to a bogus value and
+ # fill in as much as we can.
+ location = '<unknown>'
+ if hasattr(loader, 'get_filename'):
+ # ExecutionLoader
+ try:
+ location = loader.get_filename(name)
+ except ImportError:
+ pass
+
+ # If the location is on the filesystem, but doesn't actually exist,
+ # we could return None here, indicating that the location is not
+ # valid. However, we don't have a good way of testing since an
+ # indirect location (e.g. a zip file or URL) will look like a
+ # non-existent file relative to the filesystem.
+
+ spec = ModuleSpec(name, loader, origin=location)
+ spec._set_fileattr = True
+
+ # Pick a loader if one wasn't provided.
+ if loader is None:
+ for loader_class, suffixes in _get_supported_file_loaders():
+ if location.endswith(tuple(suffixes)):
+ loader = loader_class(name, location)
+ spec.loader = loader
+ break
+ else:
+ return None
+
+ # Set submodule_search_paths appropriately.
+ if submodule_search_locations is _POPULATE:
+ # Check the loader.
+ if hasattr(loader, 'is_package'):
+ try:
+ is_package = loader.is_package(name)
+ except ImportError:
+ pass
+ else:
+ if is_package:
+ spec.submodule_search_locations = []
+ else:
+ spec.submodule_search_locations = submodule_search_locations
+ if spec.submodule_search_locations == []:
+ if location:
+ dirname = _path_split(location)[0]
+ spec.submodule_search_locations.append(dirname)
+
+ return spec
+
+
+def _spec_from_module(module, loader=None, origin=None):
+ # This function is meant for use in _setup().
+ try:
+ spec = module.__spec__
+ except AttributeError:
+ pass
+ else:
+ if spec is not None:
+ return spec
+
+ name = module.__name__
+ if loader is None:
+ try:
+ loader = module.__loader__
+ except AttributeError:
+ # loader will stay None.
+ pass
+ try:
+ location = module.__file__
+ except AttributeError:
+ location = None
+ if origin is None:
+ if location is None:
+ try:
+ origin = loader._ORIGIN
+ except AttributeError:
+ origin = None
+ else:
+ origin = location
+ try:
+ cached = module.__cached__
+ except AttributeError:
+ cached = None
+ try:
+ submodule_search_locations = list(module.__path__)
+ except AttributeError:
+ submodule_search_locations = None
+
+ spec = ModuleSpec(name, loader, origin=origin)
+ spec._set_fileattr = False if location is None else True
+ spec.cached = cached
+ spec.submodule_search_locations = submodule_search_locations
+ return spec
+
+
+class _SpecMethods:
+
+ """Convenience wrapper around spec objects to provide spec-specific
+ methods."""
+
+ # The various spec_from_* functions could be made factory methods here.
+
+ def __init__(self, spec):
+ self.spec = spec
+
+ def module_repr(self):
+ """Return the repr to use for the module."""
+ # We mostly replicate _module_repr() using the spec attributes.
+ spec = self.spec
+ name = '?' if spec.name is None else spec.name
+ if spec.origin is None:
+ if spec.loader is None:
+ return '<module {!r}>'.format(name)
+ else:
+ return '<module {!r} ({!r})>'.format(name, spec.loader)
+ else:
+ if spec.has_location:
+ return '<module {!r} from {!r}>'.format(name, spec.origin)
+ else:
+ return '<module {!r} ({})>'.format(spec.name, spec.origin)
+
+ def init_module_attrs(self, module, *, _override=False, _force_name=True):
+ """Set the module's attributes.
+
+ All missing import-related module attributes will be set. Here
+ is how the spec attributes map onto the module:
+
+ spec.name -> module.__name__
+ spec.loader -> module.__loader__
+ spec.parent -> module.__package__
+ spec -> module.__spec__
+
+ Optional:
+ spec.origin -> module.__file__ (if spec.set_fileattr is true)
+ spec.cached -> module.__cached__ (if __file__ also set)
+ spec.submodule_search_locations -> module.__path__ (if set)
+
+ """
+ spec = self.spec
+
+ # The passed in module may be not support attribute assignment,
+ # in which case we simply don't set the attributes.
+
+ # __name__
+ if (_override or _force_name or
+ getattr(module, '__name__', None) is None):
+ try:
+ module.__name__ = spec.name
+ except AttributeError:
+ pass
+
+ # __loader__
+ if _override or getattr(module, '__loader__', None) is None:
+ loader = spec.loader
+ if loader is None:
+ # A backward compatibility hack.
+ if spec.submodule_search_locations is not None:
+ loader = _NamespaceLoader.__new__(_NamespaceLoader)
+ loader._path = spec.submodule_search_locations
+ try:
+ module.__loader__ = loader
+ except AttributeError:
+ pass
+
+ # __package__
+ if _override or getattr(module, '__package__', None) is None:
+ try:
+ module.__package__ = spec.parent
+ except AttributeError:
+ pass
+
+ # __spec__
+ try:
+ module.__spec__ = spec
+ except AttributeError:
+ pass
+
+ # __path__
+ if _override or getattr(module, '__path__', None) is None:
+ if spec.submodule_search_locations is not None:
+ try:
+ module.__path__ = spec.submodule_search_locations
+ except AttributeError:
+ pass
+
+ if spec.has_location:
+ # __file__
+ if _override or getattr(module, '__file__', None) is None:
+ try:
+ module.__file__ = spec.origin
+ except AttributeError:
+ pass
+
+ # __cached__
+ if _override or getattr(module, '__cached__', None) is None:
+ if spec.cached is not None:
+ try:
+ module.__cached__ = spec.cached
+ except AttributeError:
+ pass
+
+ def create(self):
+ """Return a new module to be loaded.
+
+ The import-related module attributes are also set with the
+ appropriate values from the spec.
+
+ """
+ spec = self.spec
+ # Typically loaders will not implement create_module().
+ if hasattr(spec.loader, 'create_module'):
+ # If create_module() returns `None` it means the default
+ # module creation should be used.
+ module = spec.loader.create_module(spec)
+ else:
+ module = None
+ if module is None:
+ # This must be done before open() is ever called as the 'io'
+ # module implicitly imports 'locale' and would otherwise
+ # trigger an infinite loop.
+ module = _new_module(spec.name)
+ self.init_module_attrs(module)
+ return module
+
+ def _exec(self, module):
+ """Do everything necessary to execute the module.
+
+ The namespace of `module` is used as the target of execution.
+ This method uses the loader's `exec_module()` method.
+
+ """
+ self.spec.loader.exec_module(module)
+
+ # Used by importlib.reload() and _load_module_shim().
+ def exec(self, module):
+ """Execute the spec in an existing module's namespace."""
+ name = self.spec.name
+ _imp.acquire_lock()
+ with _ModuleLockManager(name):
+ if sys.modules.get(name) is not module:
+ msg = 'module {!r} not in sys.modules'.format(name)
+ raise ImportError(msg, name=name)
+ if self.spec.loader is None:
+ if self.spec.submodule_search_locations is None:
+ raise ImportError('missing loader', name=self.spec.name)
+ # namespace package
+ self.init_module_attrs(module, _override=True)
+ return module
+ self.init_module_attrs(module, _override=True)
+ if not hasattr(self.spec.loader, 'exec_module'):
+ # (issue19713) Once BuiltinImporter and ExtensionFileLoader
+ # have exec_module() implemented, we can add a deprecation
+ # warning here.
+ self.spec.loader.load_module(name)
+ else:
+ self._exec(module)
+ return sys.modules[name]
+
+ def _load_backward_compatible(self):
+ # (issue19713) Once BuiltinImporter and ExtensionFileLoader
+ # have exec_module() implemented, we can add a deprecation
+ # warning here.
+ spec = self.spec
+ spec.loader.load_module(spec.name)
+ # The module must be in sys.modules at this point!
+ module = sys.modules[spec.name]
+ if getattr(module, '__loader__', None) is None:
+ try:
+ module.__loader__ = spec.loader
+ except AttributeError:
+ pass
+ if getattr(module, '__package__', None) is None:
+ try:
+ # Since module.__path__ may not line up with
+ # spec.submodule_search_paths, we can't necessarily rely
+ # on spec.parent here.
+ module.__package__ = module.__name__
+ if not hasattr(module, '__path__'):
+ module.__package__ = spec.name.rpartition('.')[0]
+ except AttributeError:
+ pass
+ if getattr(module, '__spec__', None) is None:
+ try:
+ module.__spec__ = spec
+ except AttributeError:
+ pass
+ return module
+
+ def _load_unlocked(self):
+ # A helper for direct use by the import system.
+ if self.spec.loader is not None:
+ # not a namespace package
+ if not hasattr(self.spec.loader, 'exec_module'):
+ return self._load_backward_compatible()
+
+ module = self.create()
+ with _installed_safely(module):
+ if self.spec.loader is None:
+ if self.spec.submodule_search_locations is None:
+ raise ImportError('missing loader', name=self.spec.name)
+ # A namespace package so do nothing.
+ else:
+ self._exec(module)
+
+ # We don't ensure that the import-related module attributes get
+ # set in the sys.modules replacement case. Such modules are on
+ # their own.
+ return sys.modules[self.spec.name]
+
+ # A method used during testing of _load_unlocked() and by
+ # _load_module_shim().
+ def load(self):
+ """Return a new module object, loaded by the spec's loader.
+
+ The module is not added to its parent.
+
+ If a module is already in sys.modules, that existing module gets
+ clobbered.
+
+ """
+ _imp.acquire_lock()
+ with _ModuleLockManager(self.spec.name):
+ return self._load_unlocked()
# Loaders #####################################################################
@@ -634,9 +1231,23 @@ class BuiltinImporter:
"""
+ @staticmethod
+ def module_repr(module):
+ """Return repr for the module.
+
+ The method is deprecated. The import machinery does the job itself.
+
+ """
+ return '<module {!r} (built-in)>'.format(module.__name__)
+
@classmethod
- def module_repr(cls, module):
- return "<module '{}' (built-in)>".format(module.__name__)
+ def find_spec(cls, fullname, path=None, target=None):
+ if path is not None:
+ return None
+ if _imp.is_builtin(fullname):
+ return spec_from_loader(fullname, cls, origin='built-in')
+ else:
+ return None
@classmethod
def find_module(cls, fullname, path=None):
@@ -644,24 +1255,23 @@ class BuiltinImporter:
If 'path' is ever specified then the search is considered a failure.
+ This method is deprecated. Use find_spec() instead.
+
"""
- if path is not None:
- return None
- return cls if _imp.is_builtin(fullname) else None
+ spec = cls.find_spec(fullname, path)
+ return spec.loader if spec is not None else None
@classmethod
- @set_package
- @set_loader
@_requires_builtin
def load_module(cls, fullname):
"""Load a built-in module."""
- is_reload = fullname in sys.modules
- try:
- return _call_with_frames_removed(_imp.init_builtin, fullname)
- except:
- if not is_reload and fullname in sys.modules:
- del sys.modules[fullname]
- raise
+ # Once an exec_module() implementation is added we can also
+ # add a deprecation warning here.
+ with _ManageReload(fullname):
+ module = _call_with_frames_removed(_imp.init_builtin, fullname)
+ module.__loader__ = cls
+ module.__package__ = ''
+ return module
@classmethod
@_requires_builtin
@@ -691,31 +1301,48 @@ class FrozenImporter:
"""
+ @staticmethod
+ def module_repr(m):
+ """Return repr for the module.
+
+ The method is deprecated. The import machinery does the job itself.
+
+ """
+ return '<module {!r} (frozen)>'.format(m.__name__)
+
@classmethod
- def module_repr(cls, m):
- return "<module '{}' (frozen)>".format(m.__name__)
+ def find_spec(cls, fullname, path=None, target=None):
+ if _imp.is_frozen(fullname):
+ return spec_from_loader(fullname, cls, origin='frozen')
+ else:
+ return None
@classmethod
def find_module(cls, fullname, path=None):
- """Find a frozen module."""
+ """Find a frozen module.
+
+ This method is deprecated. Use find_spec() instead.
+
+ """
return cls if _imp.is_frozen(fullname) else None
+ @staticmethod
+ def exec_module(module):
+ name = module.__spec__.name
+ if not _imp.is_frozen(name):
+ raise ImportError('{!r} is not a frozen module'.format(name),
+ name=name)
+ code = _call_with_frames_removed(_imp.get_frozen_object, name)
+ exec(code, module.__dict__)
+
@classmethod
- @set_package
- @set_loader
- @_requires_frozen
def load_module(cls, fullname):
- """Load a frozen module."""
- is_reload = fullname in sys.modules
- try:
- m = _call_with_frames_removed(_imp.init_frozen, fullname)
- # Let our own module_repr() method produce a suitable repr.
- del m.__file__
- return m
- except:
- if not is_reload and fullname in sys.modules:
- del sys.modules[fullname]
- raise
+ """Load a frozen module.
+
+ This method is deprecated. Use exec_module() instead.
+
+ """
+ return _load_module_shim(cls, fullname)
@classmethod
@_requires_frozen
@@ -738,22 +1365,21 @@ class FrozenImporter:
class WindowsRegistryFinder:
- """Meta path finder for modules declared in the Windows registry.
- """
+ """Meta path finder for modules declared in the Windows registry."""
REGISTRY_KEY = (
- "Software\\Python\\PythonCore\\{sys_version}"
- "\\Modules\\{fullname}")
+ 'Software\\Python\\PythonCore\\{sys_version}'
+ '\\Modules\\{fullname}')
REGISTRY_KEY_DEBUG = (
- "Software\\Python\\PythonCore\\{sys_version}"
- "\\Modules\\{fullname}\\Debug")
+ 'Software\\Python\\PythonCore\\{sys_version}'
+ '\\Modules\\{fullname}\\Debug')
DEBUG_BUILD = False # Changed in _setup()
@classmethod
def _open_registry(cls, key):
try:
return _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, key)
- except WindowsError:
+ except OSError:
return _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, key)
@classmethod
@@ -766,24 +1392,38 @@ class WindowsRegistryFinder:
sys_version=sys.version[:3])
try:
with cls._open_registry(key) as hkey:
- filepath = _winreg.QueryValue(hkey, "")
- except WindowsError:
+ filepath = _winreg.QueryValue(hkey, '')
+ except OSError:
return None
return filepath
@classmethod
- def find_module(cls, fullname, path=None):
- """Find module named in the registry."""
+ def find_spec(cls, fullname, path=None, target=None):
filepath = cls._search_registry(fullname)
if filepath is None:
return None
try:
- _os.stat(filepath)
+ _path_stat(filepath)
except OSError:
return None
for loader, suffixes in _get_supported_file_loaders():
if filepath.endswith(tuple(suffixes)):
- return loader(fullname, filepath)
+ spec = spec_from_loader(fullname, loader(fullname, filepath),
+ origin=filepath)
+ return spec
+
+ @classmethod
+ def find_module(cls, fullname, path=None):
+ """Find module named in the registry.
+
+ This method is deprecated. Use exec_module() instead.
+
+ """
+ spec = cls.find_spec(fullname, path)
+ if spec is not None:
+ return spec.loader
+ else:
+ return None
class _LoaderBasics:
@@ -799,74 +1439,15 @@ class _LoaderBasics:
tail_name = fullname.rpartition('.')[2]
return filename_base == '__init__' and tail_name != '__init__'
- def _bytes_from_bytecode(self, fullname, data, bytecode_path, source_stats):
- """Return the marshalled bytes from bytecode, verifying the magic
- number, timestamp and source size along the way.
+ def exec_module(self, module):
+ """Execute the module."""
+ code = self.get_code(module.__name__)
+ if code is None:
+ raise ImportError('cannot load module {!r} when get_code() '
+ 'returns None'.format(module.__name__))
+ _call_with_frames_removed(exec, code, module.__dict__)
- If source_stats is None then skip the timestamp check.
-
- """
- magic = data[:4]
- raw_timestamp = data[4:8]
- raw_size = data[8:12]
- if magic != _MAGIC_BYTES:
- msg = 'bad magic number in {!r}: {!r}'.format(fullname, magic)
- _verbose_message(msg)
- raise ImportError(msg, name=fullname, path=bytecode_path)
- elif len(raw_timestamp) != 4:
- message = 'bad timestamp in {}'.format(fullname)
- _verbose_message(message)
- raise EOFError(message)
- elif len(raw_size) != 4:
- message = 'bad size in {}'.format(fullname)
- _verbose_message(message)
- raise EOFError(message)
- if source_stats is not None:
- try:
- source_mtime = int(source_stats['mtime'])
- except KeyError:
- pass
- else:
- if _r_long(raw_timestamp) != source_mtime:
- message = 'bytecode is stale for {}'.format(fullname)
- _verbose_message(message)
- raise ImportError(message, name=fullname,
- path=bytecode_path)
- try:
- source_size = source_stats['size'] & 0xFFFFFFFF
- except KeyError:
- pass
- else:
- if _r_long(raw_size) != source_size:
- raise ImportError(
- "bytecode is stale for {}".format(fullname),
- name=fullname, path=bytecode_path)
- # Can't return the code object as errors from marshal loading need to
- # propagate even when source is available.
- return data[12:]
-
- @module_for_loader
- def _load_module(self, module, *, sourceless=False):
- """Helper for load_module able to handle either source or sourceless
- loading."""
- name = module.__name__
- code_object = self.get_code(name)
- module.__file__ = self.get_filename(name)
- if not sourceless:
- try:
- module.__cached__ = cache_from_source(module.__file__)
- except NotImplementedError:
- module.__cached__ = module.__file__
- else:
- module.__cached__ = module.__file__
- module.__package__ = name
- if self.is_package(name):
- module.__path__ = [_path_split(module.__file__)[0]]
- else:
- module.__package__ = module.__package__.rpartition('.')[0]
- module.__loader__ = self
- _call_with_frames_removed(exec, code_object, module.__dict__)
- return module
+ load_module = _load_module_shim
class SourceLoader(_LoaderBasics):
@@ -874,8 +1455,10 @@ class SourceLoader(_LoaderBasics):
def path_mtime(self, path):
"""Optional method that returns the modification time (an int) for the
specified path, where path is a str.
+
+ Raises IOError when the path cannot be handled.
"""
- raise NotImplementedError
+ raise IOError
def path_stats(self, path):
"""Optional method returning a metadata dict for the specified path
@@ -886,6 +1469,7 @@ class SourceLoader(_LoaderBasics):
- 'size' (optional) is the size in bytes of the source code.
Implementing this method allows the loader to read bytecode files.
+ Raises IOError when the path cannot be handled.
"""
return {'mtime': self.path_mtime(path)}
@@ -903,32 +1487,26 @@ class SourceLoader(_LoaderBasics):
"""Optional method which writes data (bytes) to a file path (a str).
Implementing this method allows for the writing of bytecode files.
-
"""
- raise NotImplementedError
def get_source(self, fullname):
"""Concrete implementation of InspectLoader.get_source."""
- import tokenize
path = self.get_filename(fullname)
try:
source_bytes = self.get_data(path)
- except IOError as exc:
- raise ImportError("source not available through get_data()",
- name=fullname) from exc
- readsource = _io.BytesIO(source_bytes).readline
- try:
- encoding = tokenize.detect_encoding(readsource)
- except SyntaxError as exc:
- raise ImportError("Failed to detect encoding",
- name=fullname) from exc
- newline_decoder = _io.IncrementalNewlineDecoder(None, True)
- try:
- return newline_decoder.decode(source_bytes.decode(encoding[0]))
- except UnicodeDecodeError as exc:
- raise ImportError("Failed to decode source file",
+ except OSError as exc:
+ raise ImportError('source not available through get_data()',
name=fullname) from exc
+ return decode_source(source_bytes)
+
+ def source_to_code(self, data, path, *, _optimize=-1):
+ """Return the code object compiled from source.
+
+ The 'data' argument can be any object type that compile() supports.
+ """
+ return _call_with_frames_removed(compile, data, path, 'exec',
+ dont_inherit=True, optimize=_optimize)
def get_code(self, fullname):
"""Concrete implementation of InspectLoader.get_code.
@@ -946,45 +1524,34 @@ class SourceLoader(_LoaderBasics):
else:
try:
st = self.path_stats(source_path)
- except NotImplementedError:
+ except IOError:
pass
else:
source_mtime = int(st['mtime'])
try:
data = self.get_data(bytecode_path)
- except IOError:
+ except OSError:
pass
else:
try:
- bytes_data = self._bytes_from_bytecode(fullname, data,
- bytecode_path,
- st)
+ bytes_data = _validate_bytecode_header(data,
+ source_stats=st, name=fullname,
+ path=bytecode_path)
except (ImportError, EOFError):
pass
else:
_verbose_message('{} matches {}', bytecode_path,
source_path)
- found = marshal.loads(bytes_data)
- if isinstance(found, _code_type):
- _imp._fix_co_filename(found, source_path)
- _verbose_message('code object from {}',
- bytecode_path)
- return found
- else:
- msg = "Non-code object in {}"
- raise ImportError(msg.format(bytecode_path),
- name=fullname, path=bytecode_path)
+ return _compile_bytecode(bytes_data, name=fullname,
+ bytecode_path=bytecode_path,
+ source_path=source_path)
source_bytes = self.get_data(source_path)
- code_object = _call_with_frames_removed(compile,
- source_bytes, source_path, 'exec',
- dont_inherit=True)
+ code_object = self.source_to_code(source_bytes, source_path)
_verbose_message('code object from {}', source_path)
if (not sys.dont_write_bytecode and bytecode_path is not None and
- source_mtime is not None):
- data = bytearray(_MAGIC_BYTES)
- data.extend(_w_long(source_mtime))
- data.extend(_w_long(len(source_bytes)))
- data.extend(marshal.dumps(code_object))
+ source_mtime is not None):
+ data = _code_to_bytecode(code_object, source_mtime,
+ len(source_bytes))
try:
self._cache_bytecode(source_path, bytecode_path, data)
_verbose_message('wrote {!r}', bytecode_path)
@@ -992,16 +1559,6 @@ class SourceLoader(_LoaderBasics):
pass
return code_object
- def load_module(self, fullname):
- """Concrete implementation of Loader.load_module.
-
- Requires ExecutionLoader.get_filename and ResourceLoader.get_data to be
- implemented to load source code. Use of bytecode is dictated by whether
- get_code uses/writes bytecode.
-
- """
- return self._load_module(fullname)
-
class FileLoader:
@@ -1014,10 +1571,22 @@ class FileLoader:
self.name = fullname
self.path = path
+ def __eq__(self, other):
+ return (self.__class__ == other.__class__ and
+ self.__dict__ == other.__dict__)
+
+ def __hash__(self):
+ return hash(self.name) ^ hash(self.path)
+
@_check_name
def load_module(self, fullname):
- """Load a module from a file."""
- # Issue #14857: Avoid the zero-argument form so the implementation
+ """Load a module from a file.
+
+ This method is deprecated. Use exec_module() instead.
+
+ """
+ # The only reason for this method is for the name check.
+ # Issue #14857: Avoid the zero-argument form of super so the implementation
# of that form can be updated without breaking the frozen module
return super(FileLoader, self).load_module(fullname)
@@ -1038,18 +1607,12 @@ class SourceFileLoader(FileLoader, SourceLoader):
def path_stats(self, path):
"""Return the metadata for the path."""
- st = _os.stat(path)
+ st = _path_stat(path)
return {'mtime': st.st_mtime, 'size': st.st_size}
def _cache_bytecode(self, source_path, bytecode_path, data):
# Adapt between the two APIs
- try:
- mode = _os.stat(source_path).st_mode
- except OSError:
- mode = 0o666
- # We always ensure write access so we can update cached files
- # later even when the source files are read-only on Windows (#6074)
- mode |= 0o200
+ mode = _calc_mode(source_path)
return self.set_data(bytecode_path, data, _mode=mode)
def set_data(self, path, data, *, _mode=0o666):
@@ -1085,20 +1648,11 @@ class SourcelessFileLoader(FileLoader, _LoaderBasics):
"""Loader which handles sourceless file imports."""
- def load_module(self, fullname):
- return self._load_module(fullname, sourceless=True)
-
def get_code(self, fullname):
path = self.get_filename(fullname)
data = self.get_data(path)
- bytes_data = self._bytes_from_bytecode(fullname, data, path, None)
- found = marshal.loads(bytes_data)
- if isinstance(found, _code_type):
- _verbose_message('code object from {!r}', path)
- return found
- else:
- raise ImportError("Non-code object in {}".format(path),
- name=fullname, path=path)
+ bytes_data = _validate_bytecode_header(data, name=fullname, path=path)
+ return _compile_bytecode(bytes_data, name=fullname, bytecode_path=path)
def get_source(self, fullname):
"""Return None as there is no source code."""
@@ -1121,23 +1675,30 @@ class ExtensionFileLoader:
self.name = name
self.path = path
+ def __eq__(self, other):
+ return (self.__class__ == other.__class__ and
+ self.__dict__ == other.__dict__)
+
+ def __hash__(self):
+ return hash(self.name) ^ hash(self.path)
+
@_check_name
- @set_package
- @set_loader
def load_module(self, fullname):
"""Load an extension module."""
- is_reload = fullname in sys.modules
- try:
+ # Once an exec_module() implementation is added we can also
+ # add a deprecation warning here.
+ with _ManageReload(fullname):
module = _call_with_frames_removed(_imp.load_dynamic,
fullname, self.path)
- _verbose_message('extension module loaded from {!r}', self.path)
- if self.is_package(fullname) and not hasattr(module, '__path__'):
- module.__path__ = [_path_split(self.path)[0]]
- return module
- except:
- if not is_reload and fullname in sys.modules:
- del sys.modules[fullname]
- raise
+ _verbose_message('extension module loaded from {!r}', self.path)
+ is_package = self.is_package(fullname)
+ if is_package and not hasattr(module, '__path__'):
+ module.__path__ = [_path_split(self.path)[0]]
+ module.__loader__ = self
+ module.__package__ = module.__name__
+ if not is_package:
+ module.__package__ = module.__package__.rpartition('.')[0]
+ return module
def is_package(self, fullname):
"""Return True if the extension module is a package."""
@@ -1153,6 +1714,11 @@ class ExtensionFileLoader:
"""Return None as extension modules have no source code."""
return None
+ @_check_name
+ def get_filename(self, fullname):
+ """Return the path to the source file as found by the finder."""
+ return self.path
+
class _NamespacePath:
"""Represents a namespace package's path. It uses the module name
@@ -1185,11 +1751,12 @@ class _NamespacePath:
# If the parent's path has changed, recalculate _path
parent_path = tuple(self._get_parent_path()) # Make a copy
if parent_path != self._last_parent_path:
- loader, new_path = self._path_finder(self._name, parent_path)
+ spec = self._path_finder(self._name, parent_path)
# Note that no changes are made if a loader is returned, but we
# do remember the new parent path
- if loader is None:
- self._path = new_path
+ if spec is not None and spec.loader is None:
+ if spec.submodule_search_locations:
+ self._path = spec.submodule_search_locations
self._last_parent_path = parent_path # Save the copy
return self._path
@@ -1200,7 +1767,7 @@ class _NamespacePath:
return len(self._recalculate())
def __repr__(self):
- return "_NamespacePath({!r})".format(self._path)
+ return '_NamespacePath({!r})'.format(self._path)
def __contains__(self, item):
return item in self._recalculate()
@@ -1209,20 +1776,41 @@ class _NamespacePath:
self._path.append(item)
-class NamespaceLoader:
+# We use this exclusively in init_module_attrs() for backward-compatibility.
+class _NamespaceLoader:
def __init__(self, name, path, path_finder):
self._path = _NamespacePath(name, path, path_finder)
@classmethod
def module_repr(cls, module):
- return "<module '{}' (namespace)>".format(module.__name__)
+ """Return repr for the module.
+
+ The method is deprecated. The import machinery does the job itself.
+
+ """
+ return '<module {!r} (namespace)>'.format(module.__name__)
+
+ def is_package(self, fullname):
+ return True
+
+ def get_source(self, fullname):
+ return ''
+
+ def get_code(self, fullname):
+ return compile('', '<string>', 'exec', dont_inherit=True)
- @module_for_loader
- def load_module(self, module):
- """Load a namespace module."""
+ def exec_module(self, module):
+ pass
+
+ def load_module(self, fullname):
+ """Load a namespace module.
+
+ This method is deprecated. Use exec_module() instead.
+
+ """
+ # The import system never calls this method.
_verbose_message('namespace module loaded with path {!r}', self._path)
- module.__path__ = self._path
- return module
+ return _load_module_shim(self, fullname)
# Finders #####################################################################
@@ -1265,7 +1853,7 @@ class PathFinder:
"""
if path == '':
- path = '.'
+ path = _os.getcwd()
try:
finder = sys.path_importer_cache[path]
except KeyError:
@@ -1274,7 +1862,22 @@ class PathFinder:
return finder
@classmethod
- def _get_loader(cls, fullname, path):
+ def _legacy_get_spec(cls, fullname, finder):
+ # This would be a good place for a DeprecationWarning if
+ # we ended up going that route.
+ if hasattr(finder, 'find_loader'):
+ loader, portions = finder.find_loader(fullname)
+ else:
+ loader = finder.find_module(fullname)
+ portions = []
+ if loader is not None:
+ return spec_from_loader(fullname, loader)
+ spec = ModuleSpec(fullname, None)
+ spec.submodule_search_locations = portions
+ return spec
+
+ @classmethod
+ def _get_spec(cls, fullname, path, target=None):
"""Find the loader or namespace_path for this module/package name."""
# If this ends up being a namespace package, namespace_path is
# the list of paths that will become its __path__
@@ -1284,38 +1887,61 @@ class PathFinder:
continue
finder = cls._path_importer_cache(entry)
if finder is not None:
- if hasattr(finder, 'find_loader'):
- loader, portions = finder.find_loader(fullname)
+ if hasattr(finder, 'find_spec'):
+ spec = finder.find_spec(fullname, target)
else:
- loader = finder.find_module(fullname)
- portions = []
- if loader is not None:
- # We found a loader: return it immediately.
- return loader, namespace_path
+ spec = cls._legacy_get_spec(fullname, finder)
+ if spec is None:
+ continue
+ if spec.loader is not None:
+ return spec
+ portions = spec.submodule_search_locations
+ if portions is None:
+ raise ImportError('spec missing loader')
# This is possibly part of a namespace package.
# Remember these path entries (if any) for when we
# create a namespace package, and continue iterating
# on path.
namespace_path.extend(portions)
else:
- return None, namespace_path
+ spec = ModuleSpec(fullname, None)
+ spec.submodule_search_locations = namespace_path
+ return spec
@classmethod
- def find_module(cls, fullname, path=None):
- """Find the module on sys.path or 'path' based on sys.path_hooks and
+ def find_spec(cls, fullname, path=None, target=None):
+ """find the module on sys.path or 'path' based on sys.path_hooks and
sys.path_importer_cache."""
if path is None:
path = sys.path
- loader, namespace_path = cls._get_loader(fullname, path)
- if loader is not None:
- return loader
- else:
+ spec = cls._get_spec(fullname, path, target)
+ if spec is None:
+ return None
+ elif spec.loader is None:
+ namespace_path = spec.submodule_search_locations
if namespace_path:
# We found at least one namespace path. Return a
- # loader which can create the namespace package.
- return NamespaceLoader(fullname, namespace_path, cls._get_loader)
+ # spec which can create the namespace package.
+ spec.origin = 'namespace'
+ spec.submodule_search_locations = _NamespacePath(fullname, namespace_path, cls._get_spec)
+ return spec
else:
return None
+ else:
+ return spec
+
+ @classmethod
+ def find_module(cls, fullname, path=None):
+ """find the module on sys.path or 'path' based on sys.path_hooks and
+ sys.path_importer_cache.
+
+ This method is deprecated. Use find_spec() instead.
+
+ """
+ spec = cls.find_spec(fullname, path)
+ if spec is None:
+ return None
+ return spec.loader
class FileFinder:
@@ -1349,11 +1975,28 @@ class FileFinder:
def find_loader(self, fullname):
"""Try to find a loader for the specified module, or the namespace
+ package portions. Returns (loader, list-of-portions).
+
+ This method is deprecated. Use find_spec() instead.
+
+ """
+ spec = self.find_spec(fullname)
+ if spec is None:
+ return None, []
+ return spec.loader, spec.submodule_search_locations or []
+
+ def _get_spec(self, loader_class, fullname, path, smsl, target):
+ loader = loader_class(fullname, path)
+ return spec_from_file_location(fullname, path, loader=loader,
+ submodule_search_locations=smsl)
+
+ def find_spec(self, fullname, target=None):
+ """Try to find a loader for the specified module, or the namespace
package portions. Returns (loader, list-of-portions)."""
is_namespace = False
tail_module = fullname.rpartition('.')[2]
try:
- mtime = _os.stat(self.path).st_mtime
+ mtime = _path_stat(self.path or _os.getcwd()).st_mtime
except OSError:
mtime = -1
if mtime != self._path_mtime:
@@ -1369,33 +2012,34 @@ class FileFinder:
# Check if the module is the name of a directory (and thus a package).
if cache_module in cache:
base_path = _path_join(self.path, tail_module)
- if _path_isdir(base_path):
- for suffix, loader in self._loaders:
- init_filename = '__init__' + suffix
- full_path = _path_join(base_path, init_filename)
- if _path_isfile(full_path):
- return (loader(fullname, full_path), [base_path])
- else:
- # A namespace package, return the path if we don't also
- # find a module in the next section.
- is_namespace = True
+ for suffix, loader_class in self._loaders:
+ init_filename = '__init__' + suffix
+ full_path = _path_join(base_path, init_filename)
+ if _path_isfile(full_path):
+ return self._get_spec(loader_class, fullname, full_path, [base_path], target)
+ else:
+ # If a namespace package, return the path if we don't
+ # find a module in the next section.
+ is_namespace = _path_isdir(base_path)
# Check for a file w/ a proper suffix exists.
- for suffix, loader in self._loaders:
+ for suffix, loader_class in self._loaders:
full_path = _path_join(self.path, tail_module + suffix)
_verbose_message('trying {}'.format(full_path), verbosity=2)
if cache_module + suffix in cache:
if _path_isfile(full_path):
- return (loader(fullname, full_path), [])
+ return self._get_spec(loader_class, fullname, full_path, None, target)
if is_namespace:
_verbose_message('possible namespace for {}'.format(base_path))
- return (None, [base_path])
- return (None, [])
+ spec = ModuleSpec(fullname, None)
+ spec.submodule_search_locations = [base_path]
+ return spec
+ return None
def _fill_cache(self):
"""Fill the cache of potential modules and packages for this directory."""
path = self.path
try:
- contents = _os.listdir(path)
+ contents = _os.listdir(path or _os.getcwd())
except (FileNotFoundError, PermissionError, NotADirectoryError):
# Directory has either been removed, turned into a file, or made
# unreadable.
@@ -1420,7 +2064,7 @@ class FileFinder:
lower_suffix_contents.add(new_name)
self._path_cache = lower_suffix_contents
if sys.platform.startswith(_CASE_INSENSITIVE_PLATFORMS):
- self._relaxed_path_cache = set(fn.lower() for fn in contents)
+ self._relaxed_path_cache = {fn.lower() for fn in contents}
@classmethod
def path_hook(cls, *loader_details):
@@ -1435,13 +2079,13 @@ class FileFinder:
def path_hook_for_FileFinder(path):
"""Path hook for importlib.machinery.FileFinder."""
if not _path_isdir(path):
- raise ImportError("only directories are supported", path=path)
+ raise ImportError('only directories are supported', path=path)
return cls(path, *loader_details)
return path_hook_for_FileFinder
def __repr__(self):
- return "FileFinder(%r)" % (self.path,)
+ return 'FileFinder({!r})'.format(self.path)
# Import itself ###############################################################
@@ -1468,19 +2112,51 @@ def _resolve_name(name, package, level):
return '{}.{}'.format(base, name) if name else base
-def _find_module(name, path):
+def _find_spec_legacy(finder, name, path):
+ # This would be a good place for a DeprecationWarning if
+ # we ended up going that route.
+ loader = finder.find_module(name, path)
+ if loader is None:
+ return None
+ return spec_from_loader(name, loader)
+
+
+def _find_spec(name, path, target=None):
"""Find a module's loader."""
if not sys.meta_path:
_warnings.warn('sys.meta_path is empty', ImportWarning)
+ # We check sys.modules here for the reload case. While a passed-in
+ # target will usually indicate a reload there is no guarantee, whereas
+ # sys.modules provides one.
+ is_reload = name in sys.modules
for finder in sys.meta_path:
with _ImportLockContext():
- loader = finder.find_module(name, path)
- if loader is not None:
+ try:
+ find_spec = finder.find_spec
+ except AttributeError:
+ spec = _find_spec_legacy(finder, name, path)
+ if spec is None:
+ continue
+ else:
+ spec = find_spec(name, path, target)
+ if spec is not None:
# The parent import may have already imported this module.
- if name not in sys.modules:
- return loader
+ if not is_reload and name in sys.modules:
+ module = sys.modules[name]
+ try:
+ __spec__ = module.__spec__
+ except AttributeError:
+ # We use the found spec since that is the one that
+ # we would have used if the parent module hadn't
+ # beaten us to the punch.
+ return spec
+ else:
+ if __spec__ is None:
+ return spec
+ else:
+ return __spec__
else:
- return sys.modules[name].__loader__
+ return spec
else:
return None
@@ -1488,21 +2164,22 @@ def _find_module(name, path):
def _sanity_check(name, package, level):
"""Verify arguments are "sane"."""
if not isinstance(name, str):
- raise TypeError("module name must be str, not {}".format(type(name)))
+ raise TypeError('module name must be str, not {}'.format(type(name)))
if level < 0:
raise ValueError('level must be >= 0')
if package:
if not isinstance(package, str):
- raise TypeError("__package__ not set to a string")
+ raise TypeError('__package__ not set to a string')
elif package not in sys.modules:
- msg = ("Parent module {!r} not loaded, cannot perform relative "
- "import")
+ msg = ('Parent module {!r} not loaded, cannot perform relative '
+ 'import')
raise SystemError(msg.format(package))
if not name and level == 0:
- raise ValueError("Empty module name")
+ raise ValueError('Empty module name')
-_ERR_MSG = 'No module named {!r}'
+_ERR_MSG_PREFIX = 'No module named '
+_ERR_MSG = _ERR_MSG_PREFIX + '{!r}'
def _find_and_load_unlocked(name, import_):
path = None
@@ -1513,58 +2190,28 @@ def _find_and_load_unlocked(name, import_):
# Crazy side-effects!
if name in sys.modules:
return sys.modules[name]
- # Backwards-compatibility; be nicer to skip the dict lookup.
parent_module = sys.modules[parent]
try:
path = parent_module.__path__
except AttributeError:
- msg = (_ERR_MSG + '; {} is not a package').format(name, parent)
+ msg = (_ERR_MSG + '; {!r} is not a package').format(name, parent)
raise ImportError(msg, name=name)
- loader = _find_module(name, path)
- if loader is None:
- exc = ImportError(_ERR_MSG.format(name), name=name)
- # TODO(brett): switch to a proper ModuleNotFound exception in Python
- # 3.4.
- exc._not_found = True
- raise exc
- elif name not in sys.modules:
- # The parent import may have already imported this module.
- loader.load_module(name)
- _verbose_message('import {!r} # {!r}', name, loader)
- # Backwards-compatibility; be nicer to skip the dict lookup.
- module = sys.modules[name]
+ spec = _find_spec(name, path)
+ if spec is None:
+ raise ImportError(_ERR_MSG.format(name), name=name)
+ else:
+ module = _SpecMethods(spec)._load_unlocked()
if parent:
# Set the module as an attribute on its parent.
parent_module = sys.modules[parent]
setattr(parent_module, name.rpartition('.')[2], module)
- # Set __package__ if the loader did not.
- if getattr(module, '__package__', None) is None:
- try:
- module.__package__ = module.__name__
- if not hasattr(module, '__path__'):
- module.__package__ = module.__package__.rpartition('.')[0]
- except AttributeError:
- pass
- # Set loader if need be.
- if not hasattr(module, '__loader__'):
- try:
- module.__loader__ = loader
- except AttributeError:
- pass
return module
def _find_and_load(name, import_):
"""Find and load the module, and release the import lock."""
- try:
- lock = _get_module_lock(name)
- finally:
- _imp.release_lock()
- lock.acquire()
- try:
+ with _ModuleLockManager(name):
return _find_and_load_unlocked(name, import_)
- finally:
- lock.release()
def _gcd_import(name, package=None, level=0):
@@ -1585,8 +2232,8 @@ def _gcd_import(name, package=None, level=0):
module = sys.modules[name]
if module is None:
_imp.release_lock()
- message = ("import of {} halted; "
- "None in sys.modules".format(name))
+ message = ('import of {} halted; '
+ 'None in sys.modules'.format(name))
raise ImportError(message, name=name)
_lock_unlock_module(name)
return module
@@ -1616,9 +2263,7 @@ def _handle_fromlist(module, fromlist, import_):
# Backwards-compatibility dictates we ignore failed
# imports triggered by fromlist for modules that don't
# exist.
- # TODO(brett): In Python 3.4, have import raise
- # ModuleNotFound and catch that.
- if getattr(exc, '_not_found', False):
+ if str(exc).startswith(_ERR_MSG_PREFIX):
if exc.name == from_name:
continue
raise
@@ -1686,6 +2331,13 @@ def __import__(name, globals=None, locals=None, fromlist=(), level=0):
return _handle_fromlist(module, fromlist, _gcd_import)
+def _builtin_from_name(name):
+ spec = BuiltinImporter.find_spec(name)
+ if spec is None:
+ raise ImportError('no built-in module named ' + name)
+ methods = _SpecMethods(spec)
+ return methods._load_unlocked()
+
def _setup(sys_module, _imp_module):
"""Setup importlib by importing needed built-in modules and injecting them
@@ -1704,24 +2356,31 @@ def _setup(sys_module, _imp_module):
else:
BYTECODE_SUFFIXES = DEBUG_BYTECODE_SUFFIXES
+ # Set up the spec for existing builtin/frozen modules.
module_type = type(sys)
for name, module in sys.modules.items():
if isinstance(module, module_type):
- if not hasattr(module, '__loader__'):
- if name in sys.builtin_module_names:
- module.__loader__ = BuiltinImporter
- elif _imp.is_frozen(name):
- module.__loader__ = FrozenImporter
+ if name in sys.builtin_module_names:
+ loader = BuiltinImporter
+ elif _imp.is_frozen(name):
+ loader = FrozenImporter
+ else:
+ continue
+ spec = _spec_from_module(module, loader)
+ methods = _SpecMethods(spec)
+ methods.init_module_attrs(module)
+ # Directly load built-in modules needed during bootstrap.
self_module = sys.modules[__name__]
for builtin_name in ('_io', '_warnings', 'builtins', 'marshal'):
if builtin_name not in sys.modules:
- builtin_module = BuiltinImporter.load_module(builtin_name)
+ builtin_module = _builtin_from_name(builtin_name)
else:
builtin_module = sys.modules[builtin_name]
setattr(self_module, builtin_name, builtin_module)
- os_details = ('posix', ['/']), ('nt', ['\\', '/']), ('os2', ['\\', '/'])
+ # Directly load the os module (needed during bootstrap).
+ os_details = ('posix', ['/']), ('nt', ['\\', '/'])
for builtin_os, path_separators in os_details:
# Assumption made in _path_join()
assert all(len(sep) == 1 for sep in path_separators)
@@ -1731,32 +2390,33 @@ def _setup(sys_module, _imp_module):
break
else:
try:
- os_module = BuiltinImporter.load_module(builtin_os)
- # TODO: rip out os2 code after 3.3 is released as per PEP 11
- if builtin_os == 'os2' and 'EMX GCC' in sys.version:
- path_sep = path_separators[1]
+ os_module = _builtin_from_name(builtin_os)
break
except ImportError:
continue
else:
raise ImportError('importlib requires posix or nt')
+ setattr(self_module, '_os', os_module)
+ setattr(self_module, 'path_sep', path_sep)
+ setattr(self_module, 'path_separators', ''.join(path_separators))
+ # Directly load the _thread module (needed during bootstrap).
try:
- thread_module = BuiltinImporter.load_module('_thread')
+ thread_module = _builtin_from_name('_thread')
except ImportError:
# Python was built without threads
thread_module = None
- weakref_module = BuiltinImporter.load_module('_weakref')
+ setattr(self_module, '_thread', thread_module)
+ # Directly load the _weakref module (needed during bootstrap).
+ weakref_module = _builtin_from_name('_weakref')
+ setattr(self_module, '_weakref', weakref_module)
+
+ # Directly load the winreg module (needed during bootstrap).
if builtin_os == 'nt':
- winreg_module = BuiltinImporter.load_module('winreg')
+ winreg_module = _builtin_from_name('winreg')
setattr(self_module, '_winreg', winreg_module)
- setattr(self_module, '_os', os_module)
- setattr(self_module, '_thread', thread_module)
- setattr(self_module, '_weakref', weakref_module)
- setattr(self_module, 'path_sep', path_sep)
- setattr(self_module, 'path_separators', set(path_separators))
# Constants
setattr(self_module, '_relax_case', _make_relax_case())
EXTENSION_SUFFIXES.extend(_imp.extension_suffixes())
diff --git a/Lib/importlib/abc.py b/Lib/importlib/abc.py
index 387567a..558abd3 100644
--- a/Lib/importlib/abc.py
+++ b/Lib/importlib/abc.py
@@ -8,11 +8,6 @@ except ImportError as exc:
raise
_frozen_importlib = None
import abc
-import imp
-import marshal
-import sys
-import tokenize
-import warnings
def _register(abstract_cls, *classes):
@@ -37,28 +32,37 @@ class Finder(metaclass=abc.ABCMeta):
def find_module(self, fullname, path=None):
"""An abstract method that should find a module.
The fullname is a str and the optional path is a str or None.
- Returns a Loader object.
+ Returns a Loader object or None.
"""
- raise NotImplementedError
class MetaPathFinder(Finder):
"""Abstract base class for import finders on sys.meta_path."""
- @abc.abstractmethod
+ # We don't define find_spec() here since that would break
+ # hasattr checks we do to support backward compatibility.
+
def find_module(self, fullname, path):
- """Abstract method which, when implemented, should find a module.
- The fullname is a str and the path is a str or None.
- Returns a Loader object.
+ """Return a loader for the module.
+
+ If no module is found, return None. The fullname is a str and
+ the path is a list of strings or None.
+
+ This method is deprecated in favor of finder.find_spec(). If find_spec()
+ exists then backwards-compatible functionality is provided for this
+ method.
+
"""
- raise NotImplementedError
+ if not hasattr(self, 'find_spec'):
+ return None
+ found = self.find_spec(fullname, path)
+ return found.loader if found is not None else None
def invalidate_caches(self):
"""An optional method for clearing the finder's cache, if any.
This method is used by importlib.invalidate_caches().
"""
- return NotImplemented
_register(MetaPathFinder, machinery.BuiltinImporter, machinery.FrozenImporter,
machinery.PathFinder, machinery.WindowsRegistryFinder)
@@ -68,15 +72,35 @@ class PathEntryFinder(Finder):
"""Abstract base class for path entry finders used by PathFinder."""
- @abc.abstractmethod
+ # We don't define find_spec() here since that would break
+ # hasattr checks we do to support backward compatibility.
+
def find_loader(self, fullname):
- """Abstract method which, when implemented, returns a module loader.
- The fullname is a str. Returns a 2-tuple of (Loader, portion) where
- portion is a sequence of file system locations contributing to part of
- a namespace package. The sequence may be empty and the loader may be
- None.
+ """Return (loader, namespace portion) for the path entry.
+
+ The fullname is a str. The namespace portion is a sequence of
+ path entries contributing to part of a namespace package. The
+ sequence may be empty. If loader is not None, the portion will
+ be ignored.
+
+ The portion will be discarded if another path entry finder
+ locates the module as a normal module or package.
+
+ This method is deprecated in favor of finder.find_spec(). If find_spec()
+ is provided than backwards-compatible functionality is provided.
+
"""
- raise NotImplementedError
+ if not hasattr(self, 'find_spec'):
+ return None, []
+ found = self.find_spec(fullname)
+ if found is not None:
+ if not found.submodule_search_locations:
+ portions = []
+ else:
+ portions = found.submodule_search_locations
+ return found.loader, portions
+ else:
+ return None, []
find_module = _bootstrap._find_module_shim
@@ -84,7 +108,6 @@ class PathEntryFinder(Finder):
"""An optional method for clearing the finder's cache, if any.
This method is used by PathFinder.invalidate_caches().
"""
- return NotImplemented
_register(PathEntryFinder, machinery.FileFinder)
@@ -93,16 +116,49 @@ class Loader(metaclass=abc.ABCMeta):
"""Abstract base class for import loaders."""
- @abc.abstractmethod
+ def create_module(self, spec):
+ """Return a module to initialize and into which to load.
+
+ This method should raise ImportError if anything prevents it
+ from creating a new module. It may return None to indicate
+ that the spec should create the new module.
+
+ create_module() is optional.
+
+ """
+ # By default, defer to _SpecMethods.create() for the new module.
+ return None
+
+ # We don't define exec_module() here since that would break
+ # hasattr checks we do to support backward compatibility.
+
def load_module(self, fullname):
- """Abstract method which when implemented should load a module.
- The fullname is a str."""
- raise NotImplementedError
+ """Return the loaded module.
+
+ The module must be added to sys.modules and have import-related
+ attributes set properly. The fullname is a str.
+
+ ImportError is raised on failure.
+
+ This method is deprecated in favor of loader.exec_module(). If
+ exec_module() exists then it is used to provide a backwards-compatible
+ functionality for this method.
+
+ """
+ if not hasattr(self, 'exec_module'):
+ raise ImportError
+ return _bootstrap._load_module_shim(self, fullname)
- @abc.abstractmethod
def module_repr(self, module):
- """Abstract method which when implemented calculates and returns the
- given module's repr."""
+ """Return a module's repr.
+
+ Used by the module type when the method does not raise
+ NotImplementedError.
+
+ This method is deprecated.
+
+ """
+ # The exception will cause ModuleType.__repr__ to ignore this method.
raise NotImplementedError
@@ -119,7 +175,7 @@ class ResourceLoader(Loader):
def get_data(self, path):
"""Abstract method which when implemented should return the bytes for
the specified path. The path must be a str."""
- raise NotImplementedError
+ raise IOError
class InspectLoader(Loader):
@@ -131,26 +187,47 @@ class InspectLoader(Loader):
"""
- @abc.abstractmethod
def is_package(self, fullname):
- """Abstract method which when implemented should return whether the
- module is a package. The fullname is a str. Returns a bool."""
- raise NotImplementedError
+ """Optional method which when implemented should return whether the
+ module is a package. The fullname is a str. Returns a bool.
+
+ Raises ImportError if the module cannot be found.
+ """
+ raise ImportError
- @abc.abstractmethod
def get_code(self, fullname):
- """Abstract method which when implemented should return the code object
- for the module. The fullname is a str. Returns a types.CodeType."""
- raise NotImplementedError
+ """Method which returns the code object for the module.
+
+ The fullname is a str. Returns a types.CodeType if possible, else
+ returns None if a code object does not make sense
+ (e.g. built-in module). Raises ImportError if the module cannot be
+ found.
+ """
+ source = self.get_source(fullname)
+ if source is None:
+ return None
+ return self.source_to_code(source)
@abc.abstractmethod
def get_source(self, fullname):
"""Abstract method which should return the source code for the
- module. The fullname is a str. Returns a str."""
- raise NotImplementedError
+ module. The fullname is a str. Returns a str.
+
+ Raises ImportError if the module cannot be found.
+ """
+ raise ImportError
+
+ def source_to_code(self, data, path='<string>'):
+ """Compile 'data' into a code object.
+
+ The 'data' argument can be anything that compile() can handle. The'path'
+ argument should be where the data was retrieved (when applicable)."""
+ return compile(data, path, 'exec', dont_inherit=True)
-_register(InspectLoader, machinery.BuiltinImporter, machinery.FrozenImporter,
- machinery.ExtensionFileLoader)
+ exec_module = _bootstrap._LoaderBasics.exec_module
+ load_module = _bootstrap._LoaderBasics.load_module
+
+_register(InspectLoader, machinery.BuiltinImporter, machinery.FrozenImporter)
class ExecutionLoader(InspectLoader):
@@ -165,8 +242,29 @@ class ExecutionLoader(InspectLoader):
@abc.abstractmethod
def get_filename(self, fullname):
"""Abstract method which should return the value that __file__ is to be
- set to."""
- raise NotImplementedError
+ set to.
+
+ Raises ImportError if the module cannot be found.
+ """
+ raise ImportError
+
+ def get_code(self, fullname):
+ """Method to return the code object for fullname.
+
+ Should return None if not applicable (e.g. built-in module).
+ Raise ImportError if the module cannot be found.
+ """
+ source = self.get_source(fullname)
+ if source is None:
+ return None
+ try:
+ path = self.get_filename(fullname)
+ except ImportError:
+ return self.source_to_code(source)
+ else:
+ return self.source_to_code(source, path)
+
+_register(ExecutionLoader, machinery.ExtensionFileLoader)
class FileLoader(_bootstrap.FileLoader, ResourceLoader, ExecutionLoader):
@@ -198,7 +296,7 @@ class SourceLoader(_bootstrap.SourceLoader, ResourceLoader, ExecutionLoader):
def path_mtime(self, path):
"""Return the (int) modification time for the path (str)."""
if self.path_stats.__func__ is SourceLoader.path_stats:
- raise NotImplementedError
+ raise IOError
return int(self.path_stats(path)['mtime'])
def path_stats(self, path):
@@ -209,7 +307,7 @@ class SourceLoader(_bootstrap.SourceLoader, ResourceLoader, ExecutionLoader):
- 'size' (optional) is the size in bytes of the source code.
"""
if self.path_mtime.__func__ is SourceLoader.path_mtime:
- raise NotImplementedError
+ raise IOError
return {'mtime': self.path_mtime(path)}
def set_data(self, path, data):
@@ -220,185 +318,6 @@ class SourceLoader(_bootstrap.SourceLoader, ResourceLoader, ExecutionLoader):
Any needed intermediary directories are to be created. If for some
reason the file cannot be written because of permissions, fail
silently.
-
"""
- raise NotImplementedError
_register(SourceLoader, machinery.SourceFileLoader)
-
-class PyLoader(SourceLoader):
-
- """Implement the deprecated PyLoader ABC in terms of SourceLoader.
-
- This class has been deprecated! It is slated for removal in Python 3.4.
- If compatibility with Python 3.1 is not needed then implement the
- SourceLoader ABC instead of this class. If Python 3.1 compatibility is
- needed, then use the following idiom to have a single class that is
- compatible with Python 3.1 onwards::
-
- try:
- from importlib.abc import SourceLoader
- except ImportError:
- from importlib.abc import PyLoader as SourceLoader
-
-
- class CustomLoader(SourceLoader):
- def get_filename(self, fullname):
- # Implement ...
-
- def source_path(self, fullname):
- '''Implement source_path in terms of get_filename.'''
- try:
- return self.get_filename(fullname)
- except ImportError:
- return None
-
- def is_package(self, fullname):
- filename = os.path.basename(self.get_filename(fullname))
- return os.path.splitext(filename)[0] == '__init__'
-
- """
-
- @abc.abstractmethod
- def is_package(self, fullname):
- raise NotImplementedError
-
- @abc.abstractmethod
- def source_path(self, fullname):
- """Abstract method. Accepts a str module name and returns the path to
- the source code for the module."""
- raise NotImplementedError
-
- def get_filename(self, fullname):
- """Implement get_filename in terms of source_path.
-
- As get_filename should only return a source file path there is no
- chance of the path not existing but loading still being possible, so
- ImportError should propagate instead of being turned into returning
- None.
-
- """
- warnings.warn("importlib.abc.PyLoader is deprecated and is "
- "slated for removal in Python 3.4; "
- "use SourceLoader instead. "
- "See the importlib documentation on how to be "
- "compatible with Python 3.1 onwards.",
- DeprecationWarning)
- path = self.source_path(fullname)
- if path is None:
- raise ImportError(name=fullname)
- else:
- return path
-
-
-class PyPycLoader(PyLoader):
-
- """Abstract base class to assist in loading source and bytecode by
- requiring only back-end storage methods to be implemented.
-
- This class has been deprecated! Removal is slated for Python 3.4. Implement
- the SourceLoader ABC instead. If Python 3.1 compatibility is needed, see
- PyLoader.
-
- The methods get_code, get_source, and load_module are implemented for the
- user.
-
- """
-
- def get_filename(self, fullname):
- """Return the source or bytecode file path."""
- path = self.source_path(fullname)
- if path is not None:
- return path
- path = self.bytecode_path(fullname)
- if path is not None:
- return path
- raise ImportError("no source or bytecode path available for "
- "{0!r}".format(fullname), name=fullname)
-
- def get_code(self, fullname):
- """Get a code object from source or bytecode."""
- warnings.warn("importlib.abc.PyPycLoader is deprecated and slated for "
- "removal in Python 3.4; use SourceLoader instead. "
- "If Python 3.1 compatibility is required, see the "
- "latest documentation for PyLoader.",
- DeprecationWarning)
- source_timestamp = self.source_mtime(fullname)
- # Try to use bytecode if it is available.
- bytecode_path = self.bytecode_path(fullname)
- if bytecode_path:
- data = self.get_data(bytecode_path)
- try:
- magic = data[:4]
- if len(magic) < 4:
- raise ImportError(
- "bad magic number in {}".format(fullname),
- name=fullname, path=bytecode_path)
- raw_timestamp = data[4:8]
- if len(raw_timestamp) < 4:
- raise EOFError("bad timestamp in {}".format(fullname))
- pyc_timestamp = _bootstrap._r_long(raw_timestamp)
- raw_source_size = data[8:12]
- if len(raw_source_size) != 4:
- raise EOFError("bad file size in {}".format(fullname))
- # Source size is unused as the ABC does not provide a way to
- # get the size of the source ahead of reading it.
- bytecode = data[12:]
- # Verify that the magic number is valid.
- if imp.get_magic() != magic:
- raise ImportError(
- "bad magic number in {}".format(fullname),
- name=fullname, path=bytecode_path)
- # Verify that the bytecode is not stale (only matters when
- # there is source to fall back on.
- if source_timestamp:
- if pyc_timestamp < source_timestamp:
- raise ImportError("bytecode is stale", name=fullname,
- path=bytecode_path)
- except (ImportError, EOFError):
- # If source is available give it a shot.
- if source_timestamp is not None:
- pass
- else:
- raise
- else:
- # Bytecode seems fine, so try to use it.
- return marshal.loads(bytecode)
- elif source_timestamp is None:
- raise ImportError("no source or bytecode available to create code "
- "object for {0!r}".format(fullname),
- name=fullname)
- # Use the source.
- source_path = self.source_path(fullname)
- if source_path is None:
- message = "a source path must exist to load {0}".format(fullname)
- raise ImportError(message, name=fullname)
- source = self.get_data(source_path)
- code_object = compile(source, source_path, 'exec', dont_inherit=True)
- # Generate bytecode and write it out.
- if not sys.dont_write_bytecode:
- data = bytearray(imp.get_magic())
- data.extend(_bootstrap._w_long(source_timestamp))
- data.extend(_bootstrap._w_long(len(source) & 0xFFFFFFFF))
- data.extend(marshal.dumps(code_object))
- self.write_bytecode(fullname, data)
- return code_object
-
- @abc.abstractmethod
- def source_mtime(self, fullname):
- """Abstract method. Accepts a str filename and returns an int
- modification time for the source of the module."""
- raise NotImplementedError
-
- @abc.abstractmethod
- def bytecode_path(self, fullname):
- """Abstract method. Accepts a str filename and returns the str pathname
- to the bytecode for the module."""
- raise NotImplementedError
-
- @abc.abstractmethod
- def write_bytecode(self, fullname, bytecode):
- """Abstract method. Accepts a str filename and bytes object
- representing the bytecode for the module. Returns a boolean
- representing whether the bytecode was written or not."""
- raise NotImplementedError
diff --git a/Lib/importlib/machinery.py b/Lib/importlib/machinery.py
index ff826e4..2e1b2d7 100644
--- a/Lib/importlib/machinery.py
+++ b/Lib/importlib/machinery.py
@@ -5,6 +5,7 @@ import _imp
from ._bootstrap import (SOURCE_SUFFIXES, DEBUG_BYTECODE_SUFFIXES,
OPTIMIZED_BYTECODE_SUFFIXES, BYTECODE_SUFFIXES,
EXTENSION_SUFFIXES)
+from ._bootstrap import ModuleSpec
from ._bootstrap import BuiltinImporter
from ._bootstrap import FrozenImporter
from ._bootstrap import WindowsRegistryFinder
diff --git a/Lib/importlib/util.py b/Lib/importlib/util.py
index 1316437..6d73b1d 100644
--- a/Lib/importlib/util.py
+++ b/Lib/importlib/util.py
@@ -1,9 +1,18 @@
"""Utility code for constructing importers, etc."""
-from ._bootstrap import module_for_loader
-from ._bootstrap import set_loader
-from ._bootstrap import set_package
+from ._bootstrap import MAGIC_NUMBER
+from ._bootstrap import cache_from_source
+from ._bootstrap import decode_source
+from ._bootstrap import source_from_cache
+from ._bootstrap import spec_from_loader
+from ._bootstrap import spec_from_file_location
from ._bootstrap import _resolve_name
+from ._bootstrap import _find_spec
+
+from contextlib import contextmanager
+import functools
+import sys
+import warnings
def resolve_name(name, package):
@@ -19,3 +28,175 @@ def resolve_name(name, package):
break
level += 1
return _resolve_name(name[level:], package, level)
+
+
+def _find_spec_from_path(name, path=None):
+ """Return the spec for the specified module.
+
+ First, sys.modules is checked to see if the module was already imported. If
+ so, then sys.modules[name].__spec__ is returned. If that happens to be
+ set to None, then ValueError is raised. If the module is not in
+ sys.modules, then sys.meta_path is searched for a suitable spec with the
+ value of 'path' given to the finders. None is returned if no spec could
+ be found.
+
+ Dotted names do not have their parent packages implicitly imported. You will
+ most likely need to explicitly import all parent packages in the proper
+ order for a submodule to get the correct spec.
+
+ """
+ if name not in sys.modules:
+ return _find_spec(name, path)
+ else:
+ module = sys.modules[name]
+ if module is None:
+ return None
+ try:
+ spec = module.__spec__
+ except AttributeError:
+ raise ValueError('{}.__spec__ is not set'.format(name))
+ else:
+ if spec is None:
+ raise ValueError('{}.__spec__ is None'.format(name))
+ return spec
+
+
+def find_spec(name, package=None):
+ """Return the spec for the specified module.
+
+ First, sys.modules is checked to see if the module was already imported. If
+ so, then sys.modules[name].__spec__ is returned. If that happens to be
+ set to None, then ValueError is raised. If the module is not in
+ sys.modules, then sys.meta_path is searched for a suitable spec with the
+ value of 'path' given to the finders. None is returned if no spec could
+ be found.
+
+ If the name is for submodule (contains a dot), the parent module is
+ automatically imported.
+
+ The name and package arguments work the same as importlib.import_module().
+ In other words, relative module names (with leading dots) work.
+
+ """
+ fullname = resolve_name(name, package) if name.startswith('.') else name
+ if fullname not in sys.modules:
+ parent_name = fullname.rpartition('.')[0]
+ if parent_name:
+ # Use builtins.__import__() in case someone replaced it.
+ parent = __import__(parent_name, fromlist=['__path__'])
+ return _find_spec(fullname, parent.__path__)
+ else:
+ return _find_spec(fullname, None)
+ else:
+ module = sys.modules[fullname]
+ if module is None:
+ return None
+ try:
+ spec = module.__spec__
+ except AttributeError:
+ raise ValueError('{}.__spec__ is not set'.format(name))
+ else:
+ if spec is None:
+ raise ValueError('{}.__spec__ is None'.format(name))
+ return spec
+
+
+@contextmanager
+def _module_to_load(name):
+ is_reload = name in sys.modules
+
+ module = sys.modules.get(name)
+ if not is_reload:
+ # This must be done before open() is called as the 'io' module
+ # implicitly imports 'locale' and would otherwise trigger an
+ # infinite loop.
+ module = type(sys)(name)
+ # This must be done before putting the module in sys.modules
+ # (otherwise an optimization shortcut in import.c becomes wrong)
+ module.__initializing__ = True
+ sys.modules[name] = module
+ try:
+ yield module
+ except Exception:
+ if not is_reload:
+ try:
+ del sys.modules[name]
+ except KeyError:
+ pass
+ finally:
+ module.__initializing__ = False
+
+
+def set_package(fxn):
+ """Set __package__ on the returned module.
+
+ This function is deprecated.
+
+ """
+ @functools.wraps(fxn)
+ def set_package_wrapper(*args, **kwargs):
+ warnings.warn('The import system now takes care of this automatically.',
+ DeprecationWarning, stacklevel=2)
+ module = fxn(*args, **kwargs)
+ if getattr(module, '__package__', None) is None:
+ module.__package__ = module.__name__
+ if not hasattr(module, '__path__'):
+ module.__package__ = module.__package__.rpartition('.')[0]
+ return module
+ return set_package_wrapper
+
+
+def set_loader(fxn):
+ """Set __loader__ on the returned module.
+
+ This function is deprecated.
+
+ """
+ @functools.wraps(fxn)
+ def set_loader_wrapper(self, *args, **kwargs):
+ warnings.warn('The import system now takes care of this automatically.',
+ DeprecationWarning, stacklevel=2)
+ module = fxn(self, *args, **kwargs)
+ if getattr(module, '__loader__', None) is None:
+ module.__loader__ = self
+ return module
+ return set_loader_wrapper
+
+
+def module_for_loader(fxn):
+ """Decorator to handle selecting the proper module for loaders.
+
+ The decorated function is passed the module to use instead of the module
+ name. The module passed in to the function is either from sys.modules if
+ it already exists or is a new module. If the module is new, then __name__
+ is set the first argument to the method, __loader__ is set to self, and
+ __package__ is set accordingly (if self.is_package() is defined) will be set
+ before it is passed to the decorated function (if self.is_package() does
+ not work for the module it will be set post-load).
+
+ If an exception is raised and the decorator created the module it is
+ subsequently removed from sys.modules.
+
+ The decorator assumes that the decorated function takes the module name as
+ the second argument.
+
+ """
+ warnings.warn('The import system now takes care of this automatically.',
+ DeprecationWarning, stacklevel=2)
+ @functools.wraps(fxn)
+ def module_for_loader_wrapper(self, fullname, *args, **kwargs):
+ with _module_to_load(fullname) as module:
+ module.__loader__ = self
+ try:
+ is_package = self.is_package(fullname)
+ except (ImportError, AttributeError):
+ pass
+ else:
+ if is_package:
+ module.__package__ = fullname
+ else:
+ module.__package__ = fullname.rpartition('.')[0]
+ # If __package__ was not set above, __import__() will do it later.
+ return fxn(self, module, *args, **kwargs)
+
+ return module_for_loader_wrapper
diff --git a/Lib/inspect.py b/Lib/inspect.py
index 680623d..4c3e33d 100644
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -31,7 +31,7 @@ Here are some of the useful functions provided by this module:
__author__ = ('Ka-Ping Yee <ping@lfw.org>',
'Yury Selivanov <yselivanov@sprymix.com>')
-import imp
+import ast
import importlib.machinery
import itertools
import linecache
@@ -39,6 +39,7 @@ import os
import re
import sys
import tokenize
+import token
import types
import warnings
import functools
@@ -268,21 +269,40 @@ def getmembers(object, predicate=None):
else:
mro = ()
results = []
- for key in dir(object):
- # First try to get the value via __dict__. Some descriptors don't
- # like calling their __get__ (see bug #1785).
- for base in mro:
- if key in base.__dict__:
- value = base.__dict__[key]
- break
- else:
- try:
- value = getattr(object, key)
- except AttributeError:
+ processed = set()
+ names = dir(object)
+ # :dd any DynamicClassAttributes to the list of names if object is a class;
+ # this may result in duplicate entries if, for example, a virtual
+ # attribute with the same name as a DynamicClassAttribute exists
+ try:
+ for base in object.__bases__:
+ for k, v in base.__dict__.items():
+ if isinstance(v, types.DynamicClassAttribute):
+ names.append(k)
+ except AttributeError:
+ pass
+ for key in names:
+ # First try to get the value via getattr. Some descriptors don't
+ # like calling their __get__ (see bug #1785), so fall back to
+ # looking in the __dict__.
+ try:
+ value = getattr(object, key)
+ # handle the duplicate key
+ if key in processed:
+ raise AttributeError
+ except AttributeError:
+ for base in mro:
+ if key in base.__dict__:
+ value = base.__dict__[key]
+ break
+ else:
+ # could be a (currently) missing slot member, or a buggy
+ # __dir__; discard and move on
continue
if not predicate or predicate(value):
results.append((key, value))
- results.sort()
+ processed.add(key)
+ results.sort(key=lambda pair: pair[0])
return results
Attribute = namedtuple('Attribute', 'name kind defining_class object')
@@ -299,60 +319,106 @@ def classify_class_attrs(cls):
'class method' created via classmethod()
'static method' created via staticmethod()
'property' created via property()
- 'method' any other flavor of method
+ 'method' any other flavor of method or descriptor
'data' not a method
2. The class which defined this attribute (a class).
- 3. The object as obtained directly from the defining class's
- __dict__, not via getattr. This is especially important for
- data attributes: C.data is just a data object, but
- C.__dict__['data'] may be a data descriptor with additional
- info, like a __doc__ string.
+ 3. The object as obtained by calling getattr; if this fails, or if the
+ resulting object does not live anywhere in the class' mro (including
+ metaclasses) then the object is looked up in the defining class's
+ dict (found by walking the mro).
+
+ If one of the items in dir(cls) is stored in the metaclass it will now
+ be discovered and not have None be listed as the class in which it was
+ defined. Any items whose home class cannot be discovered are skipped.
"""
mro = getmro(cls)
+ metamro = getmro(type(cls)) # for attributes stored in the metaclass
+ metamro = tuple([cls for cls in metamro if cls not in (type, object)])
+ class_bases = (cls,) + mro
+ all_bases = class_bases + metamro
names = dir(cls)
+ # :dd any DynamicClassAttributes to the list of names;
+ # this may result in duplicate entries if, for example, a virtual
+ # attribute with the same name as a DynamicClassAttribute exists.
+ for base in mro:
+ for k, v in base.__dict__.items():
+ if isinstance(v, types.DynamicClassAttribute):
+ names.append(k)
result = []
+ processed = set()
+
for name in names:
# Get the object associated with the name, and where it was defined.
+ # Normal objects will be looked up with both getattr and directly in
+ # its class' dict (in case getattr fails [bug #1785], and also to look
+ # for a docstring).
+ # For DynamicClassAttributes on the second pass we only look in the
+ # class's dict.
+ #
# Getting an obj from the __dict__ sometimes reveals more than
# using getattr. Static and class methods are dramatic examples.
- # Furthermore, some objects may raise an Exception when fetched with
- # getattr(). This is the case with some descriptors (bug #1785).
- # Thus, we only use getattr() as a last resort.
homecls = None
- for base in (cls,) + mro:
+ get_obj = None
+ dict_obj = None
+ if name not in processed:
+ try:
+ if name == '__dict__':
+ raise Exception("__dict__ is special, don't want the proxy")
+ get_obj = getattr(cls, name)
+ except Exception as exc:
+ pass
+ else:
+ homecls = getattr(get_obj, "__objclass__", homecls)
+ if homecls not in class_bases:
+ # if the resulting object does not live somewhere in the
+ # mro, drop it and search the mro manually
+ homecls = None
+ last_cls = None
+ # first look in the classes
+ for srch_cls in class_bases:
+ srch_obj = getattr(srch_cls, name, None)
+ if srch_obj == get_obj:
+ last_cls = srch_cls
+ # then check the metaclasses
+ for srch_cls in metamro:
+ try:
+ srch_obj = srch_cls.__getattr__(cls, name)
+ except AttributeError:
+ continue
+ if srch_obj == get_obj:
+ last_cls = srch_cls
+ if last_cls is not None:
+ homecls = last_cls
+ for base in all_bases:
if name in base.__dict__:
- obj = base.__dict__[name]
- homecls = base
+ dict_obj = base.__dict__[name]
+ if homecls not in metamro:
+ homecls = base
break
- else:
- obj = getattr(cls, name)
- homecls = getattr(obj, "__objclass__", homecls)
-
- # Classify the object.
- if isinstance(obj, staticmethod):
+ if homecls is None:
+ # unable to locate the attribute anywhere, most likely due to
+ # buggy custom __dir__; discard and move on
+ continue
+ obj = get_obj or dict_obj
+ # Classify the object or its descriptor.
+ if isinstance(dict_obj, staticmethod):
kind = "static method"
- elif isinstance(obj, classmethod):
+ obj = dict_obj
+ elif isinstance(dict_obj, classmethod):
kind = "class method"
- elif isinstance(obj, property):
+ obj = dict_obj
+ elif isinstance(dict_obj, property):
kind = "property"
- elif ismethoddescriptor(obj):
+ obj = dict_obj
+ elif isroutine(obj):
kind = "method"
- elif isdatadescriptor(obj):
- kind = "data"
else:
- obj_via_getattr = getattr(cls, name)
- if (isfunction(obj_via_getattr) or
- ismethoddescriptor(obj_via_getattr)):
- kind = "method"
- else:
- kind = "data"
- obj = obj_via_getattr
-
+ kind = "data"
result.append(Attribute(name, kind, homecls, obj))
-
+ processed.add(name)
return result
# ----------------------------------------------------------- class helpers
@@ -361,6 +427,40 @@ def getmro(cls):
"Return tuple of base classes (including cls) in method resolution order."
return cls.__mro__
+# -------------------------------------------------------- function helpers
+
+def unwrap(func, *, stop=None):
+ """Get the object wrapped by *func*.
+
+ Follows the chain of :attr:`__wrapped__` attributes returning the last
+ object in the chain.
+
+ *stop* is an optional callback accepting an object in the wrapper chain
+ as its sole argument that allows the unwrapping to be terminated early if
+ the callback returns a true value. If the callback never returns a true
+ value, the last object in the chain is returned as usual. For example,
+ :func:`signature` uses this to stop unwrapping if any object in the
+ chain has a ``__signature__`` attribute defined.
+
+ :exc:`ValueError` is raised if a cycle is encountered.
+
+ """
+ if stop is None:
+ def _is_wrapper(f):
+ return hasattr(f, '__wrapped__')
+ else:
+ def _is_wrapper(f):
+ return hasattr(f, '__wrapped__') and not stop(f)
+ f = func # remember the original func for error reporting
+ memo = {id(f)} # Memoise by id to tolerate non-hashable objects
+ while _is_wrapper(func):
+ func = func.__wrapped__
+ id_func = id(func)
+ if id_func in memo:
+ raise ValueError('wrapper loop when unwrapping {!r}'.format(f))
+ memo.add(id_func)
+ return func
+
# -------------------------------------------------- source code extraction
def indentsize(line):
"""Return the indent size, in spaces, at the start of a line of text."""
@@ -417,9 +517,10 @@ def getfile(object):
return object.__file__
raise TypeError('{!r} is a built-in module'.format(object))
if isclass(object):
- object = sys.modules.get(object.__module__)
- if hasattr(object, '__file__'):
- return object.__file__
+ if hasattr(object, '__module__'):
+ object = sys.modules.get(object.__module__)
+ if hasattr(object, '__file__'):
+ return object.__file__
raise TypeError('{!r} is a built-in class'.format(object))
if ismethod(object):
object = object.__func__
@@ -440,6 +541,9 @@ def getmoduleinfo(path):
"""Get the module name, suffix, mode, and module type for a given file."""
warnings.warn('inspect.getmoduleinfo() is deprecated', DeprecationWarning,
2)
+ with warnings.catch_warnings():
+ warnings.simplefilter('ignore', PendingDeprecationWarning)
+ import imp
filename = os.path.basename(path)
suffixes = [(-len(suffix), suffix, mode, mtype)
for suffix, mode, mtype in imp.get_suffixes()]
@@ -476,7 +580,7 @@ def getsourcefile(object):
if os.path.exists(filename):
return filename
# only return a non-existent filename if the module has a PEP 302 loader
- if hasattr(getmodule(object, filename), '__loader__'):
+ if getattr(getmodule(object, filename), '__loader__', None) is not None:
return filename
# or it is in the linecache
if filename in linecache.cache:
@@ -545,13 +649,13 @@ def findsource(object):
The argument may be a module, class, method, function, traceback, frame,
or code object. The source code is returned as a list of all the lines
- in the file and the line number indexes a line in that list. An IOError
+ in the file and the line number indexes a line in that list. An OSError
is raised if the source code cannot be retrieved."""
file = getfile(object)
sourcefile = getsourcefile(object)
if not sourcefile and file[:1] + file[-1:] != '<>':
- raise IOError('source code not available')
+ raise OSError('source code not available')
file = sourcefile if sourcefile else file
module = getmodule(object, file)
@@ -560,7 +664,7 @@ def findsource(object):
else:
lines = linecache.getlines(file)
if not lines:
- raise IOError('could not get source code')
+ raise OSError('could not get source code')
if ismodule(object):
return lines, 0
@@ -586,7 +690,7 @@ def findsource(object):
candidates.sort()
return lines, candidates[0][1]
else:
- raise IOError('could not find class definition')
+ raise OSError('could not find class definition')
if ismethod(object):
object = object.__func__
@@ -598,14 +702,14 @@ def findsource(object):
object = object.f_code
if iscode(object):
if not hasattr(object, 'co_firstlineno'):
- raise IOError('could not find function definition')
+ raise OSError('could not find function definition')
lnum = object.co_firstlineno - 1
pat = re.compile(r'^(\s*def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)')
while lnum > 0:
if pat.match(lines[lnum]): break
lnum = lnum - 1
return lines, lnum
- raise IOError('could not find code object')
+ raise OSError('could not find code object')
def getcomments(object):
"""Get lines of comments immediately preceding an object's source code.
@@ -614,7 +718,7 @@ def getcomments(object):
"""
try:
lines, lnum = findsource(object)
- except (IOError, TypeError):
+ except (OSError, TypeError):
return None
if ismodule(object):
@@ -710,7 +814,7 @@ def getsourcelines(object):
The 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 IOError is
+ original source file the first line of code was found. An OSError is
raised if the source code cannot be retrieved."""
lines, lnum = findsource(object)
@@ -722,7 +826,7 @@ def getsource(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
- IOError is raised if the source code cannot be retrieved."""
+ OSError is raised if the source code cannot be retrieved."""
lines, lnum = getsourcelines(object)
return ''.join(lines)
@@ -831,7 +935,7 @@ FullArgSpec = namedtuple('FullArgSpec',
'args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, annotations')
def getfullargspec(func):
- """Get the names and default values of a function's arguments.
+ """Get the names and default values of a callable object's arguments.
A tuple of seven things is returned:
(args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults annotations).
@@ -845,13 +949,78 @@ def getfullargspec(func):
The first four items in the tuple correspond to getargspec().
"""
- if ismethod(func):
- func = func.__func__
- if not isfunction(func):
- raise TypeError('{!r} is not a Python function'.format(func))
- args, varargs, kwonlyargs, varkw = _getfullargs(func.__code__)
- return FullArgSpec(args, varargs, varkw, func.__defaults__,
- kwonlyargs, func.__kwdefaults__, func.__annotations__)
+ try:
+ # Re: `skip_bound_arg=False`
+ #
+ # There is a notable difference in behaviour between getfullargspec
+ # and Signature: the former always returns 'self' parameter for bound
+ # methods, whereas the Signature always shows the actual calling
+ # signature of the passed object.
+ #
+ # To simulate this behaviour, we "unbind" bound methods, to trick
+ # inspect.signature to always return their first parameter ("self",
+ # usually)
+
+ # Re: `follow_wrapper_chains=False`
+ #
+ # getfullargspec() historically ignored __wrapped__ attributes,
+ # so we ensure that remains the case in 3.3+
+
+ sig = _signature_internal(func,
+ follow_wrapper_chains=False,
+ skip_bound_arg=False)
+ except Exception as ex:
+ # Most of the times 'signature' will raise ValueError.
+ # But, it can also raise AttributeError, and, maybe something
+ # else. So to be fully backwards compatible, we catch all
+ # possible exceptions here, and reraise a TypeError.
+ raise TypeError('unsupported callable') from ex
+
+ args = []
+ varargs = None
+ varkw = None
+ kwonlyargs = []
+ defaults = ()
+ annotations = {}
+ defaults = ()
+ kwdefaults = {}
+
+ if sig.return_annotation is not sig.empty:
+ annotations['return'] = sig.return_annotation
+
+ for param in sig.parameters.values():
+ kind = param.kind
+ name = param.name
+
+ if kind is _POSITIONAL_ONLY:
+ args.append(name)
+ elif kind is _POSITIONAL_OR_KEYWORD:
+ args.append(name)
+ if param.default is not param.empty:
+ defaults += (param.default,)
+ elif kind is _VAR_POSITIONAL:
+ varargs = name
+ elif kind is _KEYWORD_ONLY:
+ kwonlyargs.append(name)
+ if param.default is not param.empty:
+ kwdefaults[name] = param.default
+ elif kind is _VAR_KEYWORD:
+ varkw = name
+
+ if param.annotation is not param.empty:
+ annotations[name] = param.annotation
+
+ if not kwdefaults:
+ # compatibility with 'func.__kwdefaults__'
+ kwdefaults = None
+
+ if not defaults:
+ # compatibility with 'func.__defaults__'
+ defaults = None
+
+ return FullArgSpec(args, varargs, varkw, defaults,
+ kwonlyargs, kwdefaults, annotations)
+
ArgInfo = namedtuple('ArgInfo', 'args varargs keywords locals')
@@ -956,7 +1125,7 @@ def _missing_arguments(f_name, argnames, pos, values):
elif missing == 2:
s = "{} and {}".format(*names)
else:
- tail = ", {} and {}".format(names[-2:])
+ tail = ", {} and {}".format(*names[-2:])
del names[-2:]
s = ", ".join(names) + tail
raise TypeError("%s() missing %i required %s argument%s: %s" %
@@ -1039,7 +1208,7 @@ def getcallargs(*func_and_positional, **named):
missing = 0
for kwarg in kwonlyargs:
if kwarg not in arg2value:
- if kwarg in kwonlydefaults:
+ if kwonlydefaults and kwarg in kwonlydefaults:
arg2value[kwarg] = kwonlydefaults[kwarg]
else:
missing += 1
@@ -1125,7 +1294,7 @@ def getframeinfo(frame, context=1):
start = lineno - 1 - context//2
try:
lines, lnum = findsource(frame)
- except IOError:
+ except OSError:
lines = index = None
else:
start = max(start, 1)
@@ -1317,13 +1486,15 @@ def getgeneratorlocals(generator):
_WrapperDescriptor = type(type.__call__)
_MethodWrapper = type(all.__call__)
+_ClassMethodWrapper = type(int.__dict__['from_bytes'])
_NonUserDefinedCallables = (_WrapperDescriptor,
_MethodWrapper,
+ _ClassMethodWrapper,
types.BuiltinFunctionType)
-def _get_user_defined_method(cls, method_name):
+def _signature_get_user_defined_method(cls, method_name):
try:
meth = getattr(cls, method_name)
except AttributeError:
@@ -1335,8 +1506,387 @@ def _get_user_defined_method(cls, method_name):
return meth
-def signature(obj):
- '''Get a signature object for the passed callable.'''
+def _signature_get_partial(wrapped_sig, partial, extra_args=()):
+ # Internal helper to calculate how 'wrapped_sig' signature will
+ # look like after applying a 'functools.partial' object (or alike)
+ # on it.
+
+ old_params = wrapped_sig.parameters
+ new_params = OrderedDict(old_params.items())
+
+ partial_args = partial.args or ()
+ partial_keywords = partial.keywords or {}
+
+ if extra_args:
+ partial_args = extra_args + partial_args
+
+ try:
+ ba = wrapped_sig.bind_partial(*partial_args, **partial_keywords)
+ except TypeError as ex:
+ msg = 'partial object {!r} has incorrect arguments'.format(partial)
+ raise ValueError(msg) from ex
+
+
+ transform_to_kwonly = False
+ for param_name, param in old_params.items():
+ try:
+ arg_value = ba.arguments[param_name]
+ except KeyError:
+ pass
+ else:
+ if param.kind is _POSITIONAL_ONLY:
+ # If positional-only parameter is bound by partial,
+ # it effectively disappears from the signature
+ new_params.pop(param_name)
+ continue
+
+ if param.kind is _POSITIONAL_OR_KEYWORD:
+ if param_name in partial_keywords:
+ # This means that this parameter, and all parameters
+ # after it should be keyword-only (and var-positional
+ # should be removed). Here's why. Consider the following
+ # function:
+ # foo(a, b, *args, c):
+ # pass
+ #
+ # "partial(foo, a='spam')" will have the following
+ # signature: "(*, a='spam', b, c)". Because attempting
+ # to call that partial with "(10, 20)" arguments will
+ # raise a TypeError, saying that "a" argument received
+ # multiple values.
+ transform_to_kwonly = True
+ # Set the new default value
+ new_params[param_name] = param.replace(default=arg_value)
+ else:
+ # was passed as a positional argument
+ new_params.pop(param.name)
+ continue
+
+ if param.kind is _KEYWORD_ONLY:
+ # Set the new default value
+ new_params[param_name] = param.replace(default=arg_value)
+
+ if transform_to_kwonly:
+ assert param.kind is not _POSITIONAL_ONLY
+
+ if param.kind is _POSITIONAL_OR_KEYWORD:
+ new_param = new_params[param_name].replace(kind=_KEYWORD_ONLY)
+ new_params[param_name] = new_param
+ new_params.move_to_end(param_name)
+ elif param.kind in (_KEYWORD_ONLY, _VAR_KEYWORD):
+ new_params.move_to_end(param_name)
+ elif param.kind is _VAR_POSITIONAL:
+ new_params.pop(param.name)
+
+ return wrapped_sig.replace(parameters=new_params.values())
+
+
+def _signature_bound_method(sig):
+ # Internal helper to transform signatures for unbound
+ # functions to bound methods
+
+ params = tuple(sig.parameters.values())
+
+ if not params or params[0].kind in (_VAR_KEYWORD, _KEYWORD_ONLY):
+ raise ValueError('invalid method signature')
+
+ kind = params[0].kind
+ if kind in (_POSITIONAL_OR_KEYWORD, _POSITIONAL_ONLY):
+ # Drop first parameter:
+ # '(p1, p2[, ...])' -> '(p2[, ...])'
+ params = params[1:]
+ else:
+ if kind is not _VAR_POSITIONAL:
+ # Unless we add a new parameter type we never
+ # get here
+ raise ValueError('invalid argument type')
+ # It's a var-positional parameter.
+ # Do nothing. '(*args[, ...])' -> '(*args[, ...])'
+
+ return sig.replace(parameters=params)
+
+
+def _signature_is_builtin(obj):
+ # Internal helper to test if `obj` is a callable that might
+ # support Argument Clinic's __text_signature__ protocol.
+ return (isbuiltin(obj) or
+ ismethoddescriptor(obj) or
+ isinstance(obj, _NonUserDefinedCallables) or
+ # Can't test 'isinstance(type)' here, as it would
+ # also be True for regular python classes
+ obj in (type, object))
+
+
+def _signature_is_functionlike(obj):
+ # Internal helper to test if `obj` is a duck type of FunctionType.
+ # A good example of such objects are functions compiled with
+ # Cython, which have all attributes that a pure Python function
+ # would have, but have their code statically compiled.
+
+ if not callable(obj) or isclass(obj):
+ # All function-like objects are obviously callables,
+ # and not classes.
+ return False
+
+ name = getattr(obj, '__name__', None)
+ code = getattr(obj, '__code__', None)
+ defaults = getattr(obj, '__defaults__', _void) # Important to use _void ...
+ kwdefaults = getattr(obj, '__kwdefaults__', _void) # ... and not None here
+ annotations = getattr(obj, '__annotations__', None)
+
+ return (isinstance(code, types.CodeType) and
+ isinstance(name, str) and
+ (defaults is None or isinstance(defaults, tuple)) and
+ (kwdefaults is None or isinstance(kwdefaults, dict)) and
+ isinstance(annotations, dict))
+
+
+def _signature_get_bound_param(spec):
+ # Internal helper to get first parameter name from a
+ # __text_signature__ of a builtin method, which should
+ # be in the following format: '($param1, ...)'.
+ # Assumptions are that the first argument won't have
+ # a default value or an annotation.
+
+ assert spec.startswith('($')
+
+ pos = spec.find(',')
+ if pos == -1:
+ pos = spec.find(')')
+
+ cpos = spec.find(':')
+ assert cpos == -1 or cpos > pos
+
+ cpos = spec.find('=')
+ assert cpos == -1 or cpos > pos
+
+ return spec[2:pos]
+
+
+def _signature_strip_non_python_syntax(signature):
+ """
+ Takes a signature in Argument Clinic's extended signature format.
+ Returns a tuple of three things:
+ * that signature re-rendered in standard Python syntax,
+ * the index of the "self" parameter (generally 0), or None if
+ the function does not have a "self" parameter, and
+ * the index of the last "positional only" parameter,
+ or None if the signature has no positional-only parameters.
+ """
+
+ if not signature:
+ return signature, None, None
+
+ self_parameter = None
+ last_positional_only = None
+
+ lines = [l.encode('ascii') for l in signature.split('\n')]
+ generator = iter(lines).__next__
+ token_stream = tokenize.tokenize(generator)
+
+ delayed_comma = False
+ skip_next_comma = False
+ text = []
+ add = text.append
+
+ current_parameter = 0
+ OP = token.OP
+ ERRORTOKEN = token.ERRORTOKEN
+
+ # token stream always starts with ENCODING token, skip it
+ t = next(token_stream)
+ assert t.type == tokenize.ENCODING
+
+ for t in token_stream:
+ type, string = t.type, t.string
+
+ if type == OP:
+ if string == ',':
+ if skip_next_comma:
+ skip_next_comma = False
+ else:
+ assert not delayed_comma
+ delayed_comma = True
+ current_parameter += 1
+ continue
+
+ if string == '/':
+ assert not skip_next_comma
+ assert last_positional_only is None
+ skip_next_comma = True
+ last_positional_only = current_parameter - 1
+ continue
+
+ if (type == ERRORTOKEN) and (string == '$'):
+ assert self_parameter is None
+ self_parameter = current_parameter
+ continue
+
+ if delayed_comma:
+ delayed_comma = False
+ if not ((type == OP) and (string == ')')):
+ add(', ')
+ add(string)
+ if (string == ','):
+ add(' ')
+ clean_signature = ''.join(text)
+ return clean_signature, self_parameter, last_positional_only
+
+
+def _signature_fromstr(cls, obj, s, skip_bound_arg=True):
+ # Internal helper to parse content of '__text_signature__'
+ # and return a Signature based on it
+ Parameter = cls._parameter_cls
+
+ clean_signature, self_parameter, last_positional_only = \
+ _signature_strip_non_python_syntax(s)
+
+ program = "def foo" + clean_signature + ": pass"
+
+ try:
+ module = ast.parse(program)
+ except SyntaxError:
+ module = None
+
+ if not isinstance(module, ast.Module):
+ raise ValueError("{!r} builtin has invalid signature".format(obj))
+
+ f = module.body[0]
+
+ parameters = []
+ empty = Parameter.empty
+ invalid = object()
+
+ module = None
+ module_dict = {}
+ module_name = getattr(obj, '__module__', None)
+ if module_name:
+ module = sys.modules.get(module_name, None)
+ if module:
+ module_dict = module.__dict__
+ sys_module_dict = sys.modules
+
+ def parse_name(node):
+ assert isinstance(node, ast.arg)
+ if node.annotation != None:
+ raise ValueError("Annotations are not currently supported")
+ return node.arg
+
+ def wrap_value(s):
+ try:
+ value = eval(s, module_dict)
+ except NameError:
+ try:
+ value = eval(s, sys_module_dict)
+ except NameError:
+ raise RuntimeError()
+
+ if isinstance(value, str):
+ return ast.Str(value)
+ if isinstance(value, (int, float)):
+ return ast.Num(value)
+ if isinstance(value, bytes):
+ return ast.Bytes(value)
+ if value in (True, False, None):
+ return ast.NameConstant(value)
+ raise RuntimeError()
+
+ class RewriteSymbolics(ast.NodeTransformer):
+ def visit_Attribute(self, node):
+ a = []
+ n = node
+ while isinstance(n, ast.Attribute):
+ a.append(n.attr)
+ n = n.value
+ if not isinstance(n, ast.Name):
+ raise RuntimeError()
+ a.append(n.id)
+ value = ".".join(reversed(a))
+ return wrap_value(value)
+
+ def visit_Name(self, node):
+ if not isinstance(node.ctx, ast.Load):
+ raise ValueError()
+ return wrap_value(node.id)
+
+ def p(name_node, default_node, default=empty):
+ name = parse_name(name_node)
+ if name is invalid:
+ return None
+ if default_node and default_node is not _empty:
+ try:
+ default_node = RewriteSymbolics().visit(default_node)
+ o = ast.literal_eval(default_node)
+ except ValueError:
+ o = invalid
+ if o is invalid:
+ return None
+ default = o if o is not invalid else default
+ parameters.append(Parameter(name, kind, default=default, annotation=empty))
+
+ # non-keyword-only parameters
+ args = reversed(f.args.args)
+ defaults = reversed(f.args.defaults)
+ iter = itertools.zip_longest(args, defaults, fillvalue=None)
+ if last_positional_only is not None:
+ kind = Parameter.POSITIONAL_ONLY
+ else:
+ kind = Parameter.POSITIONAL_OR_KEYWORD
+ for i, (name, default) in enumerate(reversed(list(iter))):
+ p(name, default)
+ if i == last_positional_only:
+ kind = Parameter.POSITIONAL_OR_KEYWORD
+
+ # *args
+ if f.args.vararg:
+ kind = Parameter.VAR_POSITIONAL
+ p(f.args.vararg, empty)
+
+ # keyword-only arguments
+ kind = Parameter.KEYWORD_ONLY
+ for name, default in zip(f.args.kwonlyargs, f.args.kw_defaults):
+ p(name, default)
+
+ # **kwargs
+ if f.args.kwarg:
+ kind = Parameter.VAR_KEYWORD
+ p(f.args.kwarg, empty)
+
+ if self_parameter is not None:
+ # Possibly strip the bound argument:
+ # - We *always* strip first bound argument if
+ # it is a module.
+ # - We don't strip first bound argument if
+ # skip_bound_arg is False.
+ assert parameters
+ _self = getattr(obj, '__self__', None)
+ self_isbound = _self is not None
+ self_ismodule = ismodule(_self)
+ if self_isbound and (self_ismodule or skip_bound_arg):
+ parameters.pop(0)
+ else:
+ # for builtins, self parameter is always positional-only!
+ p = parameters[0].replace(kind=Parameter.POSITIONAL_ONLY)
+ parameters[0] = p
+
+ return cls(parameters, return_annotation=cls.empty)
+
+
+def _signature_from_builtin(cls, func, skip_bound_arg=True):
+ # Internal helper function to get signature for
+ # builtin callables
+ if not _signature_is_builtin(func):
+ raise TypeError("{!r} is not a Python builtin "
+ "function".format(func))
+
+ s = getattr(func, "__text_signature__", None)
+ if not s:
+ raise ValueError("no signature found for builtin {!r}".format(func))
+
+ return _signature_fromstr(cls, func, s, skip_bound_arg)
+
+
+def _signature_internal(obj, follow_wrapper_chains=True, skip_bound_arg=True):
if not callable(obj):
raise TypeError('{!r} is not a callable object'.format(obj))
@@ -1344,8 +1894,17 @@ def signature(obj):
if isinstance(obj, types.MethodType):
# In this case we skip the first parameter of the underlying
# function (usually `self` or `cls`).
- sig = signature(obj.__func__)
- return sig.replace(parameters=tuple(sig.parameters.values())[1:])
+ sig = _signature_internal(obj.__func__,
+ follow_wrapper_chains,
+ skip_bound_arg)
+ if skip_bound_arg:
+ return _signature_bound_method(sig)
+ else:
+ return sig
+
+ # Was this function wrapped by a decorator?
+ if follow_wrapper_chains:
+ obj = unwrap(obj, stop=(lambda f: hasattr(f, "__signature__")))
try:
sig = obj.__signature__
@@ -1356,57 +1915,42 @@ def signature(obj):
return sig
try:
- # Was this function wrapped by a decorator?
- wrapped = obj.__wrapped__
+ partialmethod = obj._partialmethod
except AttributeError:
pass
else:
- return signature(wrapped)
-
- if isinstance(obj, types.FunctionType):
+ if isinstance(partialmethod, functools.partialmethod):
+ # Unbound partialmethod (see functools.partialmethod)
+ # This means, that we need to calculate the signature
+ # as if it's a regular partial object, but taking into
+ # account that the first positional argument
+ # (usually `self`, or `cls`) will not be passed
+ # automatically (as for boundmethods)
+
+ wrapped_sig = _signature_internal(partialmethod.func,
+ follow_wrapper_chains,
+ skip_bound_arg)
+ sig = _signature_get_partial(wrapped_sig, partialmethod, (None,))
+
+ first_wrapped_param = tuple(wrapped_sig.parameters.values())[0]
+ new_params = (first_wrapped_param,) + tuple(sig.parameters.values())
+
+ return sig.replace(parameters=new_params)
+
+ if isfunction(obj) or _signature_is_functionlike(obj):
+ # If it's a pure Python function, or an object that is duck type
+ # of a Python function (Cython functions, for instance), then:
return Signature.from_function(obj)
- if isinstance(obj, functools.partial):
- sig = signature(obj.func)
-
- new_params = OrderedDict(sig.parameters.items())
+ if _signature_is_builtin(obj):
+ return _signature_from_builtin(Signature, obj,
+ skip_bound_arg=skip_bound_arg)
- partial_args = obj.args or ()
- partial_keywords = obj.keywords or {}
- try:
- ba = sig.bind_partial(*partial_args, **partial_keywords)
- except TypeError as ex:
- msg = 'partial object {!r} has incorrect arguments'.format(obj)
- raise ValueError(msg) from ex
-
- for arg_name, arg_value in ba.arguments.items():
- param = new_params[arg_name]
- if arg_name in partial_keywords:
- # We set a new default value, because the following code
- # is correct:
- #
- # >>> def foo(a): print(a)
- # >>> print(partial(partial(foo, a=10), a=20)())
- # 20
- # >>> print(partial(partial(foo, a=10), a=20)(a=30))
- # 30
- #
- # So, with 'partial' objects, passing a keyword argument is
- # like setting a new default value for the corresponding
- # parameter
- #
- # We also mark this parameter with '_partial_kwarg'
- # flag. Later, in '_bind', the 'default' value of this
- # parameter will be added to 'kwargs', to simulate
- # the 'functools.partial' real call.
- new_params[arg_name] = param.replace(default=arg_value,
- _partial_kwarg=True)
-
- elif (param.kind not in (_VAR_KEYWORD, _VAR_POSITIONAL) and
- not param._partial_kwarg):
- new_params.pop(arg_name)
-
- return sig.replace(parameters=new_params.values())
+ if isinstance(obj, functools.partial):
+ wrapped_sig = _signature_internal(obj.func,
+ follow_wrapper_chains,
+ skip_bound_arg)
+ return _signature_get_partial(wrapped_sig, obj)
sig = None
if isinstance(obj, type):
@@ -1414,32 +1958,80 @@ def signature(obj):
# First, let's see if it has an overloaded __call__ defined
# in its metaclass
- call = _get_user_defined_method(type(obj), '__call__')
+ call = _signature_get_user_defined_method(type(obj), '__call__')
if call is not None:
- sig = signature(call)
+ sig = _signature_internal(call,
+ follow_wrapper_chains,
+ skip_bound_arg)
else:
# Now we check if the 'obj' class has a '__new__' method
- new = _get_user_defined_method(obj, '__new__')
+ new = _signature_get_user_defined_method(obj, '__new__')
if new is not None:
- sig = signature(new)
+ sig = _signature_internal(new,
+ follow_wrapper_chains,
+ skip_bound_arg)
else:
# Finally, we should have at least __init__ implemented
- init = _get_user_defined_method(obj, '__init__')
+ init = _signature_get_user_defined_method(obj, '__init__')
if init is not None:
- sig = signature(init)
+ sig = _signature_internal(init,
+ follow_wrapper_chains,
+ skip_bound_arg)
+
+ if sig is None:
+ # At this point we know, that `obj` is a class, with no user-
+ # defined '__init__', '__new__', or class-level '__call__'
+
+ for base in obj.__mro__[:-1]:
+ # Since '__text_signature__' is implemented as a
+ # descriptor that extracts text signature from the
+ # class docstring, if 'obj' is derived from a builtin
+ # class, its own '__text_signature__' may be 'None'.
+ # Therefore, we go through the MRO (except the last
+ # class in there, which is 'object') to find the first
+ # class with non-empty text signature.
+ try:
+ text_sig = base.__text_signature__
+ except AttributeError:
+ pass
+ else:
+ if text_sig:
+ # If 'obj' class has a __text_signature__ attribute:
+ # return a signature based on it
+ return _signature_fromstr(Signature, obj, text_sig)
+
+ # No '__text_signature__' was found for the 'obj' class.
+ # Last option is to check if its '__init__' is
+ # object.__init__ or type.__init__.
+ if type not in obj.__mro__:
+ # We have a class (not metaclass), but no user-defined
+ # __init__ or __new__ for it
+ if obj.__init__ is object.__init__:
+ # Return a signature of 'object' builtin.
+ return signature(object)
+
elif not isinstance(obj, _NonUserDefinedCallables):
# An object with __call__
# We also check that the 'obj' is not an instance of
# _WrapperDescriptor or _MethodWrapper to avoid
# infinite recursion (and even potential segfault)
- call = _get_user_defined_method(type(obj), '__call__')
+ call = _signature_get_user_defined_method(type(obj), '__call__')
if call is not None:
- sig = signature(call)
+ try:
+ sig = _signature_internal(call,
+ follow_wrapper_chains,
+ skip_bound_arg)
+ except ValueError as ex:
+ msg = 'no signature found for {!r}'.format(obj)
+ raise ValueError(msg) from ex
if sig is not None:
# For classes and objects we skip the first parameter of their
# __call__, __new__, or __init__ methods
- return sig.replace(parameters=tuple(sig.parameters.values())[1:])
+ if skip_bound_arg:
+ return _signature_bound_method(sig)
+ else:
+ return sig
if isinstance(obj, types.BuiltinFunctionType):
# Raise a nicer error message for builtins
@@ -1448,6 +2040,10 @@ def signature(obj):
raise ValueError('callable {!r} is not supported by signature'.format(obj))
+def signature(obj):
+ '''Get a signature object for the passed callable.'''
+ return _signature_internal(obj)
+
class _void:
'''A private marker - used in Parameter & Signature'''
@@ -1486,10 +2082,12 @@ class Parameter:
The name of the parameter as a string.
* default : object
The default value for the parameter if specified. If the
- parameter has no default value, this attribute is not set.
+ parameter has no default value, this attribute is set to
+ `Parameter.empty`.
* annotation
The annotation for the parameter if specified. If the
- parameter has no annotation, this attribute is not set.
+ parameter has no annotation, this attribute is set to
+ `Parameter.empty`.
* kind : str
Describes how argument values are bound to the parameter.
Possible values: `Parameter.POSITIONAL_ONLY`,
@@ -1497,7 +2095,7 @@ class Parameter:
`Parameter.KEYWORD_ONLY`, `Parameter.VAR_KEYWORD`.
'''
- __slots__ = ('_name', '_kind', '_default', '_annotation', '_partial_kwarg')
+ __slots__ = ('_name', '_kind', '_default', '_annotation')
POSITIONAL_ONLY = _POSITIONAL_ONLY
POSITIONAL_OR_KEYWORD = _POSITIONAL_OR_KEYWORD
@@ -1507,8 +2105,7 @@ class Parameter:
empty = _empty
- def __init__(self, name, kind, *, default=_empty, annotation=_empty,
- _partial_kwarg=False):
+ def __init__(self, name, kind, *, default=_empty, annotation=_empty):
if kind not in (_POSITIONAL_ONLY, _POSITIONAL_OR_KEYWORD,
_VAR_POSITIONAL, _KEYWORD_ONLY, _VAR_KEYWORD):
@@ -1522,19 +2119,16 @@ class Parameter:
self._default = default
self._annotation = annotation
- if name is None:
- if kind != _POSITIONAL_ONLY:
- raise ValueError("None is not a valid name for a "
- "non-positional-only parameter")
- self._name = name
- else:
- name = str(name)
- if kind != _POSITIONAL_ONLY and not name.isidentifier():
- msg = '{!r} is not a valid parameter name'.format(name)
- raise ValueError(msg)
- self._name = name
+ if name is _empty:
+ raise ValueError('name is a required attribute for Parameter')
+
+ if not isinstance(name, str):
+ raise TypeError("name must be a str, not a {!r}".format(name))
+
+ if not name.isidentifier():
+ raise ValueError('{!r} is not a valid parameter name'.format(name))
- self._partial_kwarg = _partial_kwarg
+ self._name = name
@property
def name(self):
@@ -1552,8 +2146,8 @@ class Parameter:
def kind(self):
return self._kind
- def replace(self, *, name=_void, kind=_void, annotation=_void,
- default=_void, _partial_kwarg=_void):
+ def replace(self, *, name=_void, kind=_void,
+ annotation=_void, default=_void):
'''Creates a customized copy of the Parameter.'''
if name is _void:
@@ -1568,20 +2162,11 @@ class Parameter:
if default is _void:
default = self._default
- if _partial_kwarg is _void:
- _partial_kwarg = self._partial_kwarg
-
- return type(self)(name, kind, default=default, annotation=annotation,
- _partial_kwarg=_partial_kwarg)
+ return type(self)(name, kind, default=default, annotation=annotation)
def __str__(self):
kind = self.kind
-
formatted = self._name
- if kind == _POSITIONAL_ONLY:
- if formatted is None:
- formatted = ''
- formatted = '<{}>'.format(formatted)
# Add annotation and default value
if self._annotation is not _empty:
@@ -1642,12 +2227,7 @@ class BoundArguments:
def args(self):
args = []
for param_name, param in self._signature.parameters.items():
- if (param.kind in (_VAR_KEYWORD, _KEYWORD_ONLY) or
- param._partial_kwarg):
- # Keyword arguments mapped by 'functools.partial'
- # (Parameter._partial_kwarg is True) are mapped
- # in 'BoundArguments.kwargs', along with VAR_KEYWORD &
- # KEYWORD_ONLY
+ if param.kind in (_VAR_KEYWORD, _KEYWORD_ONLY):
break
try:
@@ -1672,8 +2252,7 @@ class BoundArguments:
kwargs_started = False
for param_name, param in self._signature.parameters.items():
if not kwargs_started:
- if (param.kind in (_VAR_KEYWORD, _KEYWORD_ONLY) or
- param._partial_kwarg):
+ if param.kind in (_VAR_KEYWORD, _KEYWORD_ONLY):
kwargs_started = True
else:
if param_name not in self.arguments:
@@ -1720,7 +2299,7 @@ class Signature:
* return_annotation : object
The annotation for the return type of the function if specified.
If the function has no annotation for its return type, this
- attribute is not set.
+ attribute is set to `Signature.empty`.
* bind(*args, **kwargs) -> BoundArguments
Creates a mapping from positional and keyword arguments to
parameters.
@@ -1748,24 +2327,37 @@ class Signature:
if __validate_parameters__:
params = OrderedDict()
top_kind = _POSITIONAL_ONLY
+ kind_defaults = False
for idx, param in enumerate(parameters):
kind = param.kind
+ name = param.name
+
if kind < top_kind:
- msg = 'wrong parameter order: {} before {}'
- msg = msg.format(top_kind, param.kind)
+ msg = 'wrong parameter order: {!r} before {!r}'
+ msg = msg.format(top_kind, kind)
raise ValueError(msg)
- else:
+ elif kind > top_kind:
+ kind_defaults = False
top_kind = kind
- name = param.name
- if name is None:
- name = str(idx)
- param = param.replace(name=name)
+ if kind in (_POSITIONAL_ONLY, _POSITIONAL_OR_KEYWORD):
+ if param.default is _empty:
+ if kind_defaults:
+ # No default for this parameter, but the
+ # previous parameter of the same kind had
+ # a default
+ msg = 'non-default argument follows default ' \
+ 'argument'
+ raise ValueError(msg)
+ else:
+ # There is a default for this parameter.
+ kind_defaults = True
if name in params:
msg = 'duplicate parameter name: {!r}'.format(name)
raise ValueError(msg)
+
params[name] = param
else:
params = OrderedDict(((param.name, param)
@@ -1778,8 +2370,14 @@ class Signature:
def from_function(cls, func):
'''Constructs Signature for the given python function'''
- if not isinstance(func, types.FunctionType):
- raise TypeError('{!r} is not a Python function'.format(func))
+ is_duck_function = False
+ if not isfunction(func):
+ if _signature_is_functionlike(func):
+ is_duck_function = True
+ else:
+ # If it's not a pure Python function, and not a duck type
+ # of pure function:
+ raise TypeError('{!r} is not a Python function'.format(func))
Parameter = cls._parameter_cls
@@ -1816,7 +2414,7 @@ class Signature:
default=defaults[offset]))
# *args
- if func_code.co_flags & 0x04:
+ if func_code.co_flags & CO_VARARGS:
name = arg_names[pos_count + keyword_only_count]
annotation = annotations.get(name, _empty)
parameters.append(Parameter(name, annotation=annotation,
@@ -1833,9 +2431,9 @@ class Signature:
kind=_KEYWORD_ONLY,
default=default))
# **kwargs
- if func_code.co_flags & 0x08:
+ if func_code.co_flags & CO_VARKEYWORDS:
index = pos_count + keyword_only_count
- if func_code.co_flags & 0x04:
+ if func_code.co_flags & CO_VARARGS:
index += 1
name = arg_names[index]
@@ -1843,9 +2441,15 @@ class Signature:
parameters.append(Parameter(name, annotation=annotation,
kind=_VAR_KEYWORD))
+ # Is 'func' is a pure Python function - don't validate the
+ # parameters list (for correct order and defaults), it should be OK.
return cls(parameters,
return_annotation=annotations.get('return', _empty),
- __validate_parameters__=False)
+ __validate_parameters__=is_duck_function)
+
+ @classmethod
+ def from_builtin(cls, func):
+ return _signature_from_builtin(cls, func)
@property
def parameters(self):
@@ -1912,15 +2516,6 @@ class Signature:
parameters_ex = ()
arg_vals = iter(args)
- if partial:
- # Support for binding arguments to 'functools.partial' objects.
- # See 'functools.partial' case in 'signature()' implementation
- # for details.
- for param_name, param in self.parameters.items():
- if (param._partial_kwarg and param_name not in kwargs):
- # Simulating 'functools.partial' behavior
- kwargs[param_name] = param.default
-
while True:
# Let's iterate through the positional arguments and corresponding
# parameters
@@ -1955,6 +2550,8 @@ class Signature:
parameters_ex = (param,)
break
else:
+ # No default, not VAR_KEYWORD, not VAR_POSITIONAL,
+ # not in `kwargs`
if partial:
parameters_ex = (param,)
break
@@ -1993,19 +2590,17 @@ class Signature:
# keyword arguments
kwargs_param = None
for param in itertools.chain(parameters_ex, parameters):
- if param.kind == _POSITIONAL_ONLY:
- # This should never happen in case of a properly built
- # Signature object (but let's have this check here
- # to ensure correct behaviour just in case)
- raise TypeError('{arg!r} parameter is positional only, '
- 'but was passed as a keyword'. \
- format(arg=param.name))
-
if param.kind == _VAR_KEYWORD:
# Memorize that we have a '**kwargs'-like parameter
kwargs_param = param
continue
+ if param.kind == _VAR_POSITIONAL:
+ # Named arguments don't refer to '*args'-like parameters.
+ # We only arrive here if the positional arguments ended
+ # before reaching the last parameter before *args.
+ continue
+
param_name = param.name
try:
arg_val = kwargs.pop(param_name)
@@ -2020,6 +2615,14 @@ class Signature:
format(arg=param_name)) from None
else:
+ if param.kind == _POSITIONAL_ONLY:
+ # This should never happen in case of a properly built
+ # Signature object (but let's have this check here
+ # to ensure correct behaviour just in case)
+ raise TypeError('{arg!r} parameter is positional only, '
+ 'but was passed as a keyword'. \
+ format(arg=param.name))
+
arguments[param_name] = arg_val
if kwargs:
@@ -2031,27 +2634,37 @@ class Signature:
return self._bound_arguments_cls(self, arguments)
- def bind(__bind_self, *args, **kwargs):
+ def bind(*args, **kwargs):
'''Get a BoundArguments object, that maps the passed `args`
and `kwargs` to the function's signature. Raises `TypeError`
if the passed arguments can not be bound.
'''
- return __bind_self._bind(args, kwargs)
+ return args[0]._bind(args[1:], kwargs)
- def bind_partial(__bind_self, *args, **kwargs):
+ def bind_partial(*args, **kwargs):
'''Get a BoundArguments object, that partially maps the
passed `args` and `kwargs` to the function's signature.
Raises `TypeError` if the passed arguments can not be bound.
'''
- return __bind_self._bind(args, kwargs, partial=True)
+ return args[0]._bind(args[1:], kwargs, partial=True)
def __str__(self):
result = []
+ render_pos_only_separator = False
render_kw_only_separator = True
- for idx, param in enumerate(self.parameters.values()):
+ for param in self.parameters.values():
formatted = str(param)
kind = param.kind
+
+ if kind == _POSITIONAL_ONLY:
+ render_pos_only_separator = True
+ elif render_pos_only_separator:
+ # It's not a positional-only parameter, and the flag
+ # is set to 'True' (there were pos-only params before.)
+ result.append('/')
+ render_pos_only_separator = False
+
if kind == _VAR_POSITIONAL:
# OK, we have an '*args'-like parameter, so we won't need
# a '*' to separate keyword-only arguments
@@ -2067,6 +2680,11 @@ class Signature:
result.append(formatted)
+ if render_pos_only_separator:
+ # There were only positional-only parameters, hence the
+ # flag was not reset to 'False'
+ result.append('/')
+
rendered = '({})'.format(', '.join(result))
if self.return_annotation is not _empty:
@@ -2074,3 +2692,64 @@ class Signature:
rendered += ' -> {}'.format(anno)
return rendered
+
+def _main():
+ """ Logic for inspecting an object given at command line """
+ import argparse
+ import importlib
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ 'object',
+ help="The object to be analysed. "
+ "It supports the 'module:qualname' syntax")
+ parser.add_argument(
+ '-d', '--details', action='store_true',
+ help='Display info about the module rather than its source code')
+
+ args = parser.parse_args()
+
+ target = args.object
+ mod_name, has_attrs, attrs = target.partition(":")
+ try:
+ obj = module = importlib.import_module(mod_name)
+ except Exception as exc:
+ msg = "Failed to import {} ({}: {})".format(mod_name,
+ type(exc).__name__,
+ exc)
+ print(msg, file=sys.stderr)
+ exit(2)
+
+ if has_attrs:
+ parts = attrs.split(".")
+ obj = module
+ for part in parts:
+ obj = getattr(obj, part)
+
+ if module.__name__ in sys.builtin_module_names:
+ print("Can't get info for builtin modules.", file=sys.stderr)
+ exit(1)
+
+ if args.details:
+ print('Target: {}'.format(target))
+ print('Origin: {}'.format(getsourcefile(module)))
+ print('Cached: {}'.format(module.__cached__))
+ if obj is module:
+ print('Loader: {}'.format(repr(module.__loader__)))
+ if hasattr(module, '__path__'):
+ print('Submodule search path: {}'.format(module.__path__))
+ else:
+ try:
+ __, lineno = findsource(obj)
+ except Exception:
+ pass
+ else:
+ print('Line: {}'.format(lineno))
+
+ print('\n')
+ else:
+ print(getsource(obj))
+
+
+if __name__ == "__main__":
+ _main()
diff --git a/Lib/io.py b/Lib/io.py
index bda4def..8d68f1e 100644
--- a/Lib/io.py
+++ b/Lib/io.py
@@ -4,7 +4,7 @@ builtin open function is defined in this module.
At the top of the I/O hierarchy is the abstract base class IOBase. It
defines the basic interface to a stream. Note, however, that there is no
separation between reading and writing to streams; implementations are
-allowed to raise an IOError if they do not support a given operation.
+allowed to raise an OSError if they do not support a given operation.
Extending IOBase is RawIOBase which deals simply with the reading and
writing of raw bytes to a stream. FileIO subclasses RawIOBase to provide
@@ -70,16 +70,16 @@ SEEK_END = 2
# Method descriptions and default implementations are inherited from the C
# version however.
class IOBase(_io._IOBase, metaclass=abc.ABCMeta):
- pass
+ __doc__ = _io._IOBase.__doc__
class RawIOBase(_io._RawIOBase, IOBase):
- pass
+ __doc__ = _io._RawIOBase.__doc__
class BufferedIOBase(_io._BufferedIOBase, IOBase):
- pass
+ __doc__ = _io._BufferedIOBase.__doc__
class TextIOBase(_io._TextIOBase, IOBase):
- pass
+ __doc__ = _io._TextIOBase.__doc__
RawIOBase.register(FileIO)
diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py
index ecf3f44..54df39a 100644
--- a/Lib/ipaddress.py
+++ b/Lib/ipaddress.py
@@ -1030,13 +1030,25 @@ class _BaseNetwork(_IPAddressBase):
"""Test if this address is allocated for private networks.
Returns:
- A boolean, True if the address is reserved per RFC 4193.
+ A boolean, True if the address is reserved per
+ iana-ipv4-special-registry or iana-ipv6-special-registry.
"""
return (self.network_address.is_private and
self.broadcast_address.is_private)
@property
+ def is_global(self):
+ """Test if this address is allocated for public networks.
+
+ Returns:
+ A boolean, True if the address is not reserved per
+ iana-ipv4-special-registry or iana-ipv6-special-registry.
+
+ """
+ return not self.is_private
+
+ @property
def is_unspecified(self):
"""Test if the address is unspecified.
@@ -1276,19 +1288,30 @@ class IPv4Address(_BaseV4, _BaseAddress):
return self in reserved_network
@property
+ @functools.lru_cache()
def is_private(self):
"""Test if this address is allocated for private networks.
Returns:
- A boolean, True if the address is reserved per RFC 1918.
+ A boolean, True if the address is reserved per
+ iana-ipv4-special-registry.
"""
- private_10 = IPv4Network('10.0.0.0/8')
- private_172 = IPv4Network('172.16.0.0/12')
- private_192 = IPv4Network('192.168.0.0/16')
- return (self in private_10 or
- self in private_172 or
- self in private_192)
+ return (self in IPv4Network('0.0.0.0/8') or
+ self in IPv4Network('10.0.0.0/8') or
+ self in IPv4Network('127.0.0.0/8') or
+ self in IPv4Network('169.254.0.0/16') or
+ self in IPv4Network('172.16.0.0/12') or
+ self in IPv4Network('192.0.0.0/29') or
+ self in IPv4Network('192.0.0.170/31') or
+ self in IPv4Network('192.0.2.0/24') or
+ self in IPv4Network('192.168.0.0/16') or
+ self in IPv4Network('198.18.0.0/15') or
+ self in IPv4Network('198.51.100.0/24') or
+ self in IPv4Network('203.0.113.0/24') or
+ self in IPv4Network('240.0.0.0/4') or
+ self in IPv4Network('255.255.255.255/32'))
+
@property
def is_multicast(self):
@@ -1504,6 +1527,21 @@ class IPv4Network(_BaseV4, _BaseNetwork):
if self._prefixlen == (self._max_prefixlen - 1):
self.hosts = self.__iter__
+ @property
+ @functools.lru_cache()
+ def is_global(self):
+ """Test if this address is allocated for public networks.
+
+ Returns:
+ A boolean, True if the address is not reserved per
+ iana-ipv4-special-registry.
+
+ """
+ return (not (self.network_address in IPv4Network('100.64.0.0/10') and
+ self.broadcast_address in IPv4Network('100.64.0.0/10')) and
+ not self.is_private)
+
+
class _BaseV6:
@@ -1860,15 +1898,36 @@ class IPv6Address(_BaseV6, _BaseAddress):
return self in sitelocal_network
@property
+ @functools.lru_cache()
def is_private(self):
"""Test if this address is allocated for private networks.
Returns:
- A boolean, True if the address is reserved per RFC 4193.
+ A boolean, True if the address is reserved per
+ iana-ipv6-special-registry.
"""
- private_network = IPv6Network('fc00::/7')
- return self in private_network
+ return (self in IPv6Network('::1/128') or
+ self in IPv6Network('::/128') or
+ self in IPv6Network('::ffff:0:0/96') or
+ self in IPv6Network('100::/64') or
+ self in IPv6Network('2001::/23') or
+ self in IPv6Network('2001:2::/48') or
+ self in IPv6Network('2001:db8::/32') or
+ self in IPv6Network('2001:10::/28') or
+ self in IPv6Network('fc00::/7') or
+ self in IPv6Network('fe80::/10'))
+
+ @property
+ def is_global(self):
+ """Test if this address is allocated for public networks.
+
+ Returns:
+ A boolean, true if the address is not reserved per
+ iana-ipv6-special-registry.
+
+ """
+ return not self.is_private
@property
def is_unspecified(self):
@@ -2096,6 +2155,18 @@ class IPv6Network(_BaseV6, _BaseNetwork):
if self._prefixlen == (self._max_prefixlen - 1):
self.hosts = self.__iter__
+ def hosts(self):
+ """Generate Iterator over usable hosts in a network.
+
+ This is like __iter__ except it doesn't return the
+ Subnet-Router anycast address.
+
+ """
+ network = int(self.network_address)
+ broadcast = int(self.broadcast_address)
+ for x in range(network + 1, broadcast + 1):
+ yield self._address_class(x)
+
@property
def is_site_local(self):
"""Test if the address is reserved for site-local.
diff --git a/Lib/json/__init__.py b/Lib/json/__init__.py
index 6d9b30d..94f7d8c 100644
--- a/Lib/json/__init__.py
+++ b/Lib/json/__init__.py
@@ -36,8 +36,7 @@ Compact encoding::
Pretty printing::
>>> import json
- >>> print(json.dumps({'4': 5, '6': 7}, sort_keys=True,
- ... indent=4, separators=(',', ': ')))
+ >>> print(json.dumps({'4': 5, '6': 7}, sort_keys=True, indent=4))
{
"4": 5,
"6": 7
@@ -143,13 +142,12 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
If ``indent`` is a non-negative integer, then JSON array elements and
object members will be pretty-printed with that indent level. An indent
level of 0 will only insert newlines. ``None`` is the most compact
- representation. Since the default item separator is ``', '``, the
- output might include trailing whitespace when ``indent`` is specified.
- You can use ``separators=(',', ': ')`` to avoid this.
+ representation.
- If ``separators`` is an ``(item_separator, dict_separator)`` tuple
- then it will be used instead of the default ``(', ', ': ')`` separators.
- ``(',', ':')`` is the most compact JSON representation.
+ If specified, ``separators`` should be an ``(item_separator, key_separator)``
+ tuple. The default is ``(', ', ': ')`` if *indent* is ``None`` and
+ ``(',', ': ')`` otherwise. To get the most compact JSON representation,
+ you should specify ``(',', ':')`` to eliminate whitespace.
``default(obj)`` is a function that should return a serializable version
of obj or raise TypeError. The default simply raises TypeError.
@@ -206,13 +204,12 @@ def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
If ``indent`` is a non-negative integer, then JSON array elements and
object members will be pretty-printed with that indent level. An indent
level of 0 will only insert newlines. ``None`` is the most compact
- representation. Since the default item separator is ``', '``, the
- output might include trailing whitespace when ``indent`` is specified.
- You can use ``separators=(',', ': ')`` to avoid this.
+ representation.
- If ``separators`` is an ``(item_separator, dict_separator)`` tuple
- then it will be used instead of the default ``(', ', ': ')`` separators.
- ``(',', ':')`` is the most compact JSON representation.
+ If specified, ``separators`` should be an ``(item_separator, key_separator)``
+ tuple. The default is ``(', ', ': ')`` if *indent* is ``None`` and
+ ``(',', ': ')`` otherwise. To get the most compact JSON representation,
+ you should specify ``(',', ':')`` to eliminate whitespace.
``default(obj)`` is a function that should return a serializable version
of obj or raise TypeError. The default simply raises TypeError.
@@ -310,6 +307,11 @@ def loads(s, encoding=None, cls=None, object_hook=None, parse_float=None,
The ``encoding`` argument is ignored and deprecated.
"""
+ if not isinstance(s, str):
+ raise TypeError('the JSON object must be str, not {!r}'.format(
+ s.__class__.__name__))
+ if s.startswith(u'\ufeff'):
+ raise ValueError("Unexpected UTF-8 BOM (decode using utf-8-sig)")
if (cls is None and object_hook is None and
parse_int is None and parse_float is None and
parse_constant is None and object_pairs_hook is None and not kw):
diff --git a/Lib/json/decoder.py b/Lib/json/decoder.py
index 80d3420..59e5f41 100644
--- a/Lib/json/decoder.py
+++ b/Lib/json/decoder.py
@@ -1,9 +1,6 @@
"""Implementation of JSONDecoder
"""
-import binascii
import re
-import sys
-import struct
from json import scanner
try:
@@ -15,14 +12,9 @@ __all__ = ['JSONDecoder']
FLAGS = re.VERBOSE | re.MULTILINE | re.DOTALL
-def _floatconstants():
- _BYTES = binascii.unhexlify(b'7FF80000000000007FF0000000000000')
- if sys.byteorder != 'big':
- _BYTES = _BYTES[:8][::-1] + _BYTES[8:][::-1]
- nan, inf = struct.unpack('dd', _BYTES)
- return nan, inf, -inf
-
-NaN, PosInf, NegInf = _floatconstants()
+NaN = float('nan')
+PosInf = float('inf')
+NegInf = float('-inf')
def linecol(doc, pos):
@@ -195,8 +187,8 @@ def JSONObject(s_and_end, strict, scan_once, object_hook, object_pairs_hook,
try:
value, end = scan_once(s, end)
- except StopIteration:
- raise ValueError(errmsg("Expecting object", s, end))
+ except StopIteration as err:
+ raise ValueError(errmsg("Expecting value", s, err.value)) from None
pairs_append((key, value))
try:
nextchar = s[end]
@@ -239,8 +231,8 @@ def JSONArray(s_and_end, scan_once, _w=WHITESPACE.match, _ws=WHITESPACE_STR):
while True:
try:
value, end = scan_once(s, end)
- except StopIteration:
- raise ValueError(errmsg("Expecting object", s, end))
+ except StopIteration as err:
+ raise ValueError(errmsg("Expecting value", s, err.value)) from None
_append(value)
nextchar = s[end:end + 1]
if nextchar in _ws:
@@ -250,7 +242,7 @@ def JSONArray(s_and_end, scan_once, _w=WHITESPACE.match, _ws=WHITESPACE_STR):
if nextchar == ']':
break
elif nextchar != ',':
- raise ValueError(errmsg("Expecting ',' delimiter", s, end))
+ raise ValueError(errmsg("Expecting ',' delimiter", s, end - 1))
try:
if s[end] in _ws:
end += 1
@@ -365,6 +357,6 @@ class JSONDecoder(object):
"""
try:
obj, end = self.scan_once(s, idx)
- except StopIteration:
- raise ValueError("No JSON object could be decoded")
+ except StopIteration as err:
+ raise ValueError(errmsg("Expecting value", s, err.value)) from None
return obj, end
diff --git a/Lib/json/encoder.py b/Lib/json/encoder.py
index 1d8b20c..0513838 100644
--- a/Lib/json/encoder.py
+++ b/Lib/json/encoder.py
@@ -125,14 +125,12 @@ class JSONEncoder(object):
If indent is a non-negative integer, then JSON array
elements and object members will be pretty-printed with that
indent level. An indent level of 0 will only insert newlines.
- None is the most compact representation. Since the default
- item separator is ', ', the output might include trailing
- whitespace when indent is specified. You can use
- separators=(',', ': ') to avoid this.
+ None is the most compact representation.
- If specified, separators should be a (item_separator, key_separator)
- tuple. The default is (', ', ': '). To get the most compact JSON
- representation you should specify (',', ':') to eliminate whitespace.
+ If specified, separators should be an (item_separator, key_separator)
+ tuple. The default is (', ', ': ') if *indent* is ``None`` and
+ (',', ': ') otherwise. To get the most compact JSON representation,
+ you should specify (',', ':') to eliminate whitespace.
If specified, default is a function that gets called for objects
that can't otherwise be serialized. It should return a JSON encodable
@@ -148,6 +146,8 @@ class JSONEncoder(object):
self.indent = indent
if separators is not None:
self.item_separator, self.key_separator = separators
+ elif indent is not None:
+ self.item_separator = ','
if default is not None:
self.default = default
@@ -175,6 +175,7 @@ class JSONEncoder(object):
def encode(self, o):
"""Return a JSON string representation of a Python data structure.
+ >>> from json.encoder import JSONEncoder
>>> JSONEncoder().encode({"foo": ["bar", "baz"]})
'{"foo": ["bar", "baz"]}'
@@ -298,9 +299,13 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
elif value is False:
yield buf + 'false'
elif isinstance(value, int):
- yield buf + str(value)
+ # Subclasses of int/float may override __str__, but we still
+ # want to encode them as integers/floats in JSON. One example
+ # within the standard library is IntEnum.
+ yield buf + str(int(value))
elif isinstance(value, float):
- yield buf + _floatstr(value)
+ # see comment above for int
+ yield buf + _floatstr(float(value))
else:
yield buf
if isinstance(value, (list, tuple)):
@@ -309,8 +314,7 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
chunks = _iterencode_dict(value, _current_indent_level)
else:
chunks = _iterencode(value, _current_indent_level)
- for chunk in chunks:
- yield chunk
+ yield from chunks
if newline_indent is not None:
_current_indent_level -= 1
yield '\n' + _indent * _current_indent_level
@@ -347,7 +351,8 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
# JavaScript is weakly typed for these, so it makes sense to
# also allow them. Many encoders seem to do something like this.
elif isinstance(key, float):
- key = _floatstr(key)
+ # see comment for int/float in _make_iterencode
+ key = _floatstr(float(key))
elif key is True:
key = 'true'
elif key is False:
@@ -355,7 +360,8 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
elif key is None:
key = 'null'
elif isinstance(key, int):
- key = str(key)
+ # see comment for int/float in _make_iterencode
+ key = str(int(key))
elif _skipkeys:
continue
else:
@@ -375,9 +381,11 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
elif value is False:
yield 'false'
elif isinstance(value, int):
- yield str(value)
+ # see comment for int/float in _make_iterencode
+ yield str(int(value))
elif isinstance(value, float):
- yield _floatstr(value)
+ # see comment for int/float in _make_iterencode
+ yield _floatstr(float(value))
else:
if isinstance(value, (list, tuple)):
chunks = _iterencode_list(value, _current_indent_level)
@@ -385,8 +393,7 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
chunks = _iterencode_dict(value, _current_indent_level)
else:
chunks = _iterencode(value, _current_indent_level)
- for chunk in chunks:
- yield chunk
+ yield from chunks
if newline_indent is not None:
_current_indent_level -= 1
yield '\n' + _indent * _current_indent_level
@@ -404,15 +411,15 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
elif o is False:
yield 'false'
elif isinstance(o, int):
- yield str(o)
+ # see comment for int/float in _make_iterencode
+ yield str(int(o))
elif isinstance(o, float):
- yield _floatstr(o)
+ # see comment for int/float in _make_iterencode
+ yield _floatstr(float(o))
elif isinstance(o, (list, tuple)):
- for chunk in _iterencode_list(o, _current_indent_level):
- yield chunk
+ yield from _iterencode_list(o, _current_indent_level)
elif isinstance(o, dict):
- for chunk in _iterencode_dict(o, _current_indent_level):
- yield chunk
+ yield from _iterencode_dict(o, _current_indent_level)
else:
if markers is not None:
markerid = id(o)
@@ -420,8 +427,7 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
raise ValueError("Circular reference detected")
markers[markerid] = o
o = _default(o)
- for chunk in _iterencode(o, _current_indent_level):
- yield chunk
+ yield from _iterencode(o, _current_indent_level)
if markers is not None:
del markers[markerid]
return _iterencode
diff --git a/Lib/json/scanner.py b/Lib/json/scanner.py
index 23eef61..86426cd 100644
--- a/Lib/json/scanner.py
+++ b/Lib/json/scanner.py
@@ -29,7 +29,7 @@ def py_make_scanner(context):
try:
nextchar = string[idx]
except IndexError:
- raise StopIteration
+ raise StopIteration(idx)
if nextchar == '"':
return parse_string(string, idx + 1, strict)
@@ -60,7 +60,7 @@ def py_make_scanner(context):
elif nextchar == '-' and string[idx:idx + 9] == '-Infinity':
return parse_constant('-Infinity'), idx + 9
else:
- raise StopIteration
+ raise StopIteration(idx)
def scan_once(string, idx):
try:
diff --git a/Lib/json/tool.py b/Lib/json/tool.py
index ecf9c47..7db4528 100644
--- a/Lib/json/tool.py
+++ b/Lib/json/tool.py
@@ -31,8 +31,7 @@ def main():
except ValueError as e:
raise SystemExit(e)
with outfile:
- json.dump(obj, outfile, sort_keys=True,
- indent=4, separators=(',', ': '))
+ json.dump(obj, outfile, sort_keys=True, indent=4)
outfile.write('\n')
diff --git a/Lib/keyword.py b/Lib/keyword.py
index dad39cc..6e1e882 100755
--- a/Lib/keyword.py
+++ b/Lib/keyword.py
@@ -60,6 +60,12 @@ def main():
if len(args) > 1: optfile = args[1]
else: optfile = "Lib/keyword.py"
+ # load the output skeleton from the target, taking care to preserve its
+ # newline convention.
+ with open(optfile, newline='') as fp:
+ format = fp.readlines()
+ nl = format[0][len(format[0].strip()):] if format else '\n'
+
# scan the source file for keywords
with open(iptfile) as fp:
strprog = re.compile('"([^"]+)"')
@@ -68,26 +74,21 @@ def main():
if '{1, "' in line:
match = strprog.search(line)
if match:
- lines.append(" '" + match.group(1) + "',\n")
+ lines.append(" '" + match.group(1) + "'," + nl)
lines.sort()
- # load the output skeleton from the target
- with open(optfile) as fp:
- format = fp.readlines()
-
- # insert the lines of keywords
+ # insert the lines of keywords into the skeleton
try:
- start = format.index("#--start keywords--\n") + 1
- end = format.index("#--end keywords--\n")
+ start = format.index("#--start keywords--" + nl) + 1
+ end = format.index("#--end keywords--" + nl)
format[start:end] = lines
except ValueError:
sys.stderr.write("target does not contain format markers\n")
sys.exit(1)
# write the output file
- fp = open(optfile, 'w')
- fp.write(''.join(format))
- fp.close()
+ with open(optfile, 'w', newline='') as fp:
+ fp.writelines(format)
if __name__ == "__main__":
main()
diff --git a/Lib/lib2to3/Grammar.txt b/Lib/lib2to3/Grammar.txt
index 1e1f24c..e667bcd 100644
--- a/Lib/lib2to3/Grammar.txt
+++ b/Lib/lib2to3/Grammar.txt
@@ -56,7 +56,7 @@ small_stmt: (expr_stmt | print_stmt | del_stmt | pass_stmt | flow_stmt |
expr_stmt: testlist_star_expr (augassign (yield_expr|testlist) |
('=' (yield_expr|testlist_star_expr))*)
testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [',']
-augassign: ('+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' |
+augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' |
'<<=' | '>>=' | '**=' | '//=')
# For normal assignments, additional restrictions enforced by the interpreter
print_stmt: 'print' ( [ test (',' test)* [','] ] |
@@ -119,7 +119,7 @@ xor_expr: and_expr ('^' and_expr)*
and_expr: shift_expr ('&' shift_expr)*
shift_expr: arith_expr (('<<'|'>>') arith_expr)*
arith_expr: term (('+'|'-') term)*
-term: factor (('*'|'/'|'%'|'//') factor)*
+term: factor (('*'|'@'|'/'|'%'|'//') factor)*
factor: ('+'|'-'|'~') factor | power
power: atom trailer* ['**' factor]
atom: ('(' [yield_expr|testlist_gexp] ')' |
@@ -155,4 +155,5 @@ testlist1: test (',' test)*
# not used in grammar, but may appear in "node" passed from Parser to Compiler
encoding_decl: NAME
-yield_expr: 'yield' [testlist]
+yield_expr: 'yield' [yield_arg]
+yield_arg: 'from' test | testlist
diff --git a/Lib/lib2to3/btm_utils.py b/Lib/lib2to3/btm_utils.py
index 2276dc9..339750e 100644
--- a/Lib/lib2to3/btm_utils.py
+++ b/Lib/lib2to3/btm_utils.py
@@ -96,8 +96,7 @@ class MinNode(object):
def leaves(self):
"Generator that returns the leaves of the tree"
for child in self.children:
- for x in child.leaves():
- yield x
+ yield from child.leaves()
if not self.children:
yield self
@@ -277,7 +276,6 @@ def rec_test(sequence, test_func):
sub-iterables"""
for x in sequence:
if isinstance(x, (list, tuple)):
- for y in rec_test(x, test_func):
- yield y
+ yield from rec_test(x, test_func)
else:
yield test_func(x)
diff --git a/Lib/lib2to3/fixer_util.py b/Lib/lib2to3/fixer_util.py
index 60d219f..6e259c5 100644
--- a/Lib/lib2to3/fixer_util.py
+++ b/Lib/lib2to3/fixer_util.py
@@ -129,6 +129,29 @@ def FromImport(package_name, name_leafs):
imp = Node(syms.import_from, children)
return imp
+def ImportAndCall(node, results, names):
+ """Returns an import statement and calls a method
+ of the module:
+
+ import module
+ module.name()"""
+ obj = results["obj"].clone()
+ if obj.type == syms.arglist:
+ newarglist = obj.clone()
+ else:
+ newarglist = Node(syms.arglist, [obj.clone()])
+ after = results["after"]
+ if after:
+ after = [n.clone() for n in after]
+ new = Node(syms.power,
+ Attr(Name(names[0]), Name(names[1])) +
+ [Node(syms.trailer,
+ [results["lpar"].clone(),
+ newarglist,
+ results["rpar"].clone()])] + after)
+ new.prefix = node.prefix
+ return new
+
###########################################################
### Determine whether a node represents a given literal
diff --git a/Lib/lib2to3/fixes/fix_asserts.py b/Lib/lib2to3/fixes/fix_asserts.py
new file mode 100644
index 0000000..5bcec88
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_asserts.py
@@ -0,0 +1,34 @@
+"""Fixer that replaces deprecated unittest method names."""
+
+# Author: Ezio Melotti
+
+from ..fixer_base import BaseFix
+from ..fixer_util import Name
+
+NAMES = dict(
+ assert_="assertTrue",
+ assertEquals="assertEqual",
+ assertNotEquals="assertNotEqual",
+ assertAlmostEquals="assertAlmostEqual",
+ assertNotAlmostEquals="assertNotAlmostEqual",
+ assertRegexpMatches="assertRegex",
+ assertRaisesRegexp="assertRaisesRegex",
+ failUnlessEqual="assertEqual",
+ failIfEqual="assertNotEqual",
+ failUnlessAlmostEqual="assertAlmostEqual",
+ failIfAlmostEqual="assertNotAlmostEqual",
+ failUnless="assertTrue",
+ failUnlessRaises="assertRaises",
+ failIf="assertFalse",
+)
+
+
+class FixAsserts(BaseFix):
+
+ PATTERN = """
+ power< any+ trailer< '.' meth=(%s)> any* >
+ """ % '|'.join(map(repr, NAMES))
+
+ def transform(self, node, results):
+ name = results["meth"][0]
+ name.replace(Name(NAMES[str(name)], prefix=name.prefix))
diff --git a/Lib/lib2to3/fixes/fix_intern.py b/Lib/lib2to3/fixes/fix_intern.py
index 6be11cd..fb2973c 100644
--- a/Lib/lib2to3/fixes/fix_intern.py
+++ b/Lib/lib2to3/fixes/fix_intern.py
@@ -6,9 +6,8 @@
intern(s) -> sys.intern(s)"""
# Local imports
-from .. import pytree
from .. import fixer_base
-from ..fixer_util import Name, Attr, touch_import
+from ..fixer_util import ImportAndCall, touch_import
class FixIntern(fixer_base.BaseFix):
@@ -26,21 +25,7 @@ class FixIntern(fixer_base.BaseFix):
"""
def transform(self, node, results):
- syms = self.syms
- obj = results["obj"].clone()
- if obj.type == syms.arglist:
- newarglist = obj.clone()
- else:
- newarglist = pytree.Node(syms.arglist, [obj.clone()])
- after = results["after"]
- if after:
- after = [n.clone() for n in after]
- new = pytree.Node(syms.power,
- Attr(Name("sys"), Name("intern")) +
- [pytree.Node(syms.trailer,
- [results["lpar"].clone(),
- newarglist,
- results["rpar"].clone()])] + after)
- new.prefix = node.prefix
+ names = ('sys', 'intern')
+ new = ImportAndCall(node, results, names)
touch_import(None, 'sys', node)
return new
diff --git a/Lib/lib2to3/fixes/fix_reload.py b/Lib/lib2to3/fixes/fix_reload.py
new file mode 100644
index 0000000..1855357
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_reload.py
@@ -0,0 +1,28 @@
+"""Fixer for reload().
+
+reload(s) -> imp.reload(s)"""
+
+# Local imports
+from .. import fixer_base
+from ..fixer_util import ImportAndCall, touch_import
+
+
+class FixReload(fixer_base.BaseFix):
+ BM_compatible = True
+ order = "pre"
+
+ PATTERN = """
+ power< 'reload'
+ trailer< lpar='('
+ ( not(arglist | argument<any '=' any>) obj=any
+ | obj=arglist<(not argument<any '=' any>) any ','> )
+ rpar=')' >
+ after=any*
+ >
+ """
+
+ def transform(self, node, results):
+ names = ('imp', 'reload')
+ new = ImportAndCall(node, results, names)
+ touch_import(None, 'imp', node)
+ return new
diff --git a/Lib/lib2to3/main.py b/Lib/lib2to3/main.py
index f9cc18b..93bae90 100644
--- a/Lib/lib2to3/main.py
+++ b/Lib/lib2to3/main.py
@@ -90,11 +90,11 @@ class StdoutRefactoringTool(refactor.MultiprocessRefactoringTool):
if os.path.lexists(backup):
try:
os.remove(backup)
- except os.error as err:
+ except OSError as err:
self.log_message("Can't remove backup %s", backup)
try:
os.rename(filename, backup)
- except os.error as err:
+ except OSError as err:
self.log_message("Can't rename %s to %s", filename, backup)
# Actually write the new file
write = super(StdoutRefactoringTool, self).write_file
diff --git a/Lib/lib2to3/pgen2/conv.py b/Lib/lib2to3/pgen2/conv.py
index bf49762..ed0cac5 100644
--- a/Lib/lib2to3/pgen2/conv.py
+++ b/Lib/lib2to3/pgen2/conv.py
@@ -60,7 +60,7 @@ class Converter(grammar.Grammar):
"""
try:
f = open(filename)
- except IOError as err:
+ except OSError as err:
print("Can't open %s: %s" % (filename, err))
return False
self.symbol2number = {}
@@ -111,7 +111,7 @@ class Converter(grammar.Grammar):
"""
try:
f = open(filename)
- except IOError as err:
+ except OSError as err:
print("Can't open %s: %s" % (filename, err))
return False
# The code below essentially uses f's iterator-ness!
diff --git a/Lib/lib2to3/pgen2/driver.py b/Lib/lib2to3/pgen2/driver.py
index 4c611c6..3ccc69d 100644
--- a/Lib/lib2to3/pgen2/driver.py
+++ b/Lib/lib2to3/pgen2/driver.py
@@ -123,7 +123,7 @@ def load_grammar(gt="Grammar.txt", gp=None,
logger.info("Writing grammar tables to %s", gp)
try:
g.dump(gp)
- except IOError as e:
+ except OSError as e:
logger.info("Writing failed:"+str(e))
else:
g = grammar.Grammar()
diff --git a/Lib/lib2to3/pgen2/grammar.py b/Lib/lib2to3/pgen2/grammar.py
index 14c5f70..b4481d1 100644
--- a/Lib/lib2to3/pgen2/grammar.py
+++ b/Lib/lib2to3/pgen2/grammar.py
@@ -86,15 +86,13 @@ class Grammar(object):
def dump(self, filename):
"""Dump the grammar tables to a pickle file."""
- f = open(filename, "wb")
- pickle.dump(self.__dict__, f, 2)
- f.close()
+ with open(filename, "wb") as f:
+ pickle.dump(self.__dict__, f, 2)
def load(self, filename):
"""Load the grammar tables from a pickle file."""
- f = open(filename, "rb")
- d = pickle.load(f)
- f.close()
+ with open(filename, "rb") as f:
+ d = pickle.load(f)
self.__dict__.update(d)
def copy(self):
@@ -151,6 +149,7 @@ opmap_raw = """
{ LBRACE
} RBRACE
@ AT
+@= ATEQUAL
== EQEQUAL
!= NOTEQUAL
<> NOTEQUAL
diff --git a/Lib/lib2to3/pgen2/token.py b/Lib/lib2to3/pgen2/token.py
index 6a6d0b6..7599396 100755
--- a/Lib/lib2to3/pgen2/token.py
+++ b/Lib/lib2to3/pgen2/token.py
@@ -57,12 +57,13 @@ DOUBLESTAREQUAL = 47
DOUBLESLASH = 48
DOUBLESLASHEQUAL = 49
AT = 50
-OP = 51
-COMMENT = 52
-NL = 53
-RARROW = 54
-ERRORTOKEN = 55
-N_TOKENS = 56
+ATEQUAL = 51
+OP = 52
+COMMENT = 53
+NL = 54
+RARROW = 55
+ERRORTOKEN = 56
+N_TOKENS = 57
NT_OFFSET = 256
#--end constants--
diff --git a/Lib/lib2to3/pgen2/tokenize.py b/Lib/lib2to3/pgen2/tokenize.py
index 1bb931e..3dd1ee9 100644
--- a/Lib/lib2to3/pgen2/tokenize.py
+++ b/Lib/lib2to3/pgen2/tokenize.py
@@ -84,7 +84,7 @@ String = group(r"[uU]?[rR]?'[^\n'\\]*(?:\\.[^\n'\\]*)*'",
# recognized as two instances of =).
Operator = group(r"\*\*=?", r">>=?", r"<<=?", r"<>", r"!=",
r"//=?", r"->",
- r"[+\-*/%&|^=<>]=?",
+ r"[+\-*/%&@|^=<>]=?",
r"~")
Bracket = '[][(){}]'
diff --git a/Lib/lib2to3/pytree.py b/Lib/lib2to3/pytree.py
index 17cbf0a..c4a1be3 100644
--- a/Lib/lib2to3/pytree.py
+++ b/Lib/lib2to3/pytree.py
@@ -194,8 +194,7 @@ class Base(object):
def leaves(self):
for child in self.children:
- for x in child.leaves():
- yield x
+ yield from child.leaves()
def depth(self):
if self.parent is None:
@@ -274,16 +273,14 @@ class Node(Base):
def post_order(self):
"""Return a post-order iterator for the tree."""
for child in self.children:
- for node in child.post_order():
- yield node
+ yield from child.post_order()
yield self
def pre_order(self):
"""Return a pre-order iterator for the tree."""
yield self
for child in self.children:
- for node in child.pre_order():
- yield node
+ yield from child.pre_order()
def _prefix_getter(self):
"""
diff --git a/Lib/lib2to3/refactor.py b/Lib/lib2to3/refactor.py
index 201e193..8100317 100644
--- a/Lib/lib2to3/refactor.py
+++ b/Lib/lib2to3/refactor.py
@@ -326,7 +326,7 @@ class RefactoringTool(object):
"""
try:
f = open(filename, "rb")
- except IOError as err:
+ except OSError as err:
self.log_error("Can't open %s: %s", filename, err)
return None, None
try:
@@ -534,12 +534,12 @@ class RefactoringTool(object):
"""
try:
f = _open_with_encoding(filename, "w", encoding=encoding)
- except os.error as err:
+ except OSError as err:
self.log_error("Can't create %s: %s", filename, err)
return
try:
f.write(_to_system_newlines(new_text))
- except os.error as err:
+ except OSError as err:
self.log_error("Can't write %s: %s", filename, err)
finally:
f.close()
diff --git a/Lib/lib2to3/tests/pytree_idempotency.py b/Lib/lib2to3/tests/pytree_idempotency.py
index a02bbfe..731c403 100755
--- a/Lib/lib2to3/tests/pytree_idempotency.py
+++ b/Lib/lib2to3/tests/pytree_idempotency.py
@@ -53,7 +53,7 @@ def main():
for dir in sys.path:
try:
names = os.listdir(dir)
- except os.error:
+ except OSError:
continue
print("Scanning", dir, "...", file=sys.stderr)
for name in names:
diff --git a/Lib/lib2to3/tests/test_fixers.py b/Lib/lib2to3/tests/test_fixers.py
index 2f08f93..06b0033 100644
--- a/Lib/lib2to3/tests/test_fixers.py
+++ b/Lib/lib2to3/tests/test_fixers.py
@@ -282,6 +282,65 @@ class Test_apply(FixerTestCase):
b = """f(*args, **kwds)"""
self.check(a, b)
+class Test_reload(FixerTestCase):
+ fixer = "reload"
+
+ def test(self):
+ b = """reload(a)"""
+ a = """import imp\nimp.reload(a)"""
+ self.check(b, a)
+
+ def test_comment(self):
+ b = """reload( a ) # comment"""
+ a = """import imp\nimp.reload( a ) # comment"""
+ self.check(b, a)
+
+ # PEP 8 comments
+ b = """reload( a ) # comment"""
+ a = """import imp\nimp.reload( a ) # comment"""
+ self.check(b, a)
+
+ def test_space(self):
+ b = """reload( a )"""
+ a = """import imp\nimp.reload( a )"""
+ self.check(b, a)
+
+ b = """reload( a)"""
+ a = """import imp\nimp.reload( a)"""
+ self.check(b, a)
+
+ b = """reload(a )"""
+ a = """import imp\nimp.reload(a )"""
+ self.check(b, a)
+
+ def test_unchanged(self):
+ s = """reload(a=1)"""
+ self.unchanged(s)
+
+ s = """reload(f, g)"""
+ self.unchanged(s)
+
+ s = """reload(f, *h)"""
+ self.unchanged(s)
+
+ s = """reload(f, *h, **i)"""
+ self.unchanged(s)
+
+ s = """reload(f, **i)"""
+ self.unchanged(s)
+
+ s = """reload(*h, **i)"""
+ self.unchanged(s)
+
+ s = """reload(*h)"""
+ self.unchanged(s)
+
+ s = """reload(**i)"""
+ self.unchanged(s)
+
+ s = """reload()"""
+ self.unchanged(s)
+
class Test_intern(FixerTestCase):
fixer = "intern"
@@ -4576,3 +4635,53 @@ class Test_exitfunc(FixerTestCase):
def test_unchanged(self):
s = """f(sys.exitfunc)"""
self.unchanged(s)
+
+
+class Test_asserts(FixerTestCase):
+
+ fixer = "asserts"
+
+ def test_deprecated_names(self):
+ tests = [
+ ('self.assert_(True)', 'self.assertTrue(True)'),
+ ('self.assertEquals(2, 2)', 'self.assertEqual(2, 2)'),
+ ('self.assertNotEquals(2, 3)', 'self.assertNotEqual(2, 3)'),
+ ('self.assertAlmostEquals(2, 3)', 'self.assertAlmostEqual(2, 3)'),
+ ('self.assertNotAlmostEquals(2, 8)', 'self.assertNotAlmostEqual(2, 8)'),
+ ('self.failUnlessEqual(2, 2)', 'self.assertEqual(2, 2)'),
+ ('self.failIfEqual(2, 3)', 'self.assertNotEqual(2, 3)'),
+ ('self.failUnlessAlmostEqual(2, 3)', 'self.assertAlmostEqual(2, 3)'),
+ ('self.failIfAlmostEqual(2, 8)', 'self.assertNotAlmostEqual(2, 8)'),
+ ('self.failUnless(True)', 'self.assertTrue(True)'),
+ ('self.failUnlessRaises(foo)', 'self.assertRaises(foo)'),
+ ('self.failIf(False)', 'self.assertFalse(False)'),
+ ]
+ for b, a in tests:
+ self.check(b, a)
+
+ def test_variants(self):
+ b = 'eq = self.assertEquals'
+ a = 'eq = self.assertEqual'
+ self.check(b, a)
+ b = 'self.assertEquals(2, 3, msg="fail")'
+ a = 'self.assertEqual(2, 3, msg="fail")'
+ self.check(b, a)
+ b = 'self.assertEquals(2, 3, msg="fail") # foo'
+ a = 'self.assertEqual(2, 3, msg="fail") # foo'
+ self.check(b, a)
+ b = 'self.assertEquals (2, 3)'
+ a = 'self.assertEqual (2, 3)'
+ self.check(b, a)
+ b = ' self.assertEquals (2, 3)'
+ a = ' self.assertEqual (2, 3)'
+ self.check(b, a)
+ b = 'with self.failUnlessRaises(Explosion): explode()'
+ a = 'with self.assertRaises(Explosion): explode()'
+ self.check(b, a)
+ b = 'with self.failUnlessRaises(Explosion) as cm: explode()'
+ a = 'with self.assertRaises(Explosion) as cm: explode()'
+ self.check(b, a)
+
+ def test_unchanged(self):
+ self.unchanged('self.assertEqualsOnSaturday')
+ self.unchanged('self.assertEqualsOnSaturday(3, 5)')
diff --git a/Lib/lib2to3/tests/test_parser.py b/Lib/lib2to3/tests/test_parser.py
index a383a14..b64469c 100644
--- a/Lib/lib2to3/tests/test_parser.py
+++ b/Lib/lib2to3/tests/test_parser.py
@@ -48,6 +48,19 @@ class GrammarTest(support.TestCase):
raise AssertionError("Syntax shouldn't have been valid")
+class TestMatrixMultiplication(GrammarTest):
+ def test_matrix_multiplication_operator(self):
+ self.validate("a @ b")
+ self.validate("a @= b")
+
+
+class TestYieldFrom(GrammarTest):
+ def test_matrix_multiplication_operator(self):
+ self.validate("yield from x")
+ self.validate("(yield from x) + y")
+ self.invalid_syntax("yield from")
+
+
class TestRaiseChanges(GrammarTest):
def test_2x_style_1(self):
self.validate("raise")
diff --git a/Lib/linecache.py b/Lib/linecache.py
index c3f2c3f..02a9eb5 100644
--- a/Lib/linecache.py
+++ b/Lib/linecache.py
@@ -59,7 +59,7 @@ def checkcache(filename=None):
continue # no-op for files loaded via a __loader__
try:
stat = os.stat(fullname)
- except os.error:
+ except OSError:
del cache[filename]
continue
if size != stat.st_size or mtime != stat.st_mtime:
@@ -91,7 +91,7 @@ def updatecache(filename, module_globals=None):
if name and get_source:
try:
data = get_source(name)
- except (ImportError, IOError):
+ except (ImportError, OSError):
pass
else:
if data is None:
@@ -118,14 +118,14 @@ def updatecache(filename, module_globals=None):
try:
stat = os.stat(fullname)
break
- except os.error:
+ except OSError:
pass
else:
return []
try:
with tokenize.open(fullname) as fp:
lines = fp.readlines()
- except IOError:
+ except OSError:
return []
if lines and not lines[-1].endswith('\n'):
lines[-1] += '\n'
diff --git a/Lib/locale.py b/Lib/locale.py
index 7cfea61..fa24829 100644
--- a/Lib/locale.py
+++ b/Lib/locale.py
@@ -344,14 +344,32 @@ def _replace_encoding(code, encoding):
# Convert the encoding to a C lib compatible encoding string
norm_encoding = encodings.normalize_encoding(encoding)
#print('norm encoding: %r' % norm_encoding)
- norm_encoding = encodings.aliases.aliases.get(norm_encoding,
+ norm_encoding = encodings.aliases.aliases.get(norm_encoding.lower(),
norm_encoding)
#print('aliased encoding: %r' % norm_encoding)
- encoding = locale_encoding_alias.get(norm_encoding,
- norm_encoding)
+ encoding = norm_encoding
+ norm_encoding = norm_encoding.lower()
+ if norm_encoding in locale_encoding_alias:
+ encoding = locale_encoding_alias[norm_encoding]
+ else:
+ norm_encoding = norm_encoding.replace('_', '')
+ norm_encoding = norm_encoding.replace('-', '')
+ if norm_encoding in locale_encoding_alias:
+ encoding = locale_encoding_alias[norm_encoding]
#print('found encoding %r' % encoding)
return langname + '.' + encoding
+def _append_modifier(code, modifier):
+ if modifier == 'euro':
+ if '.' not in code:
+ return code + '.ISO8859-15'
+ _, _, encoding = code.partition('.')
+ if encoding in ('ISO8859-15', 'UTF-8'):
+ return code
+ if encoding == 'ISO8859-1':
+ return _replace_encoding(code, 'ISO8859-15')
+ return code + '@' + modifier
+
def normalize(localename):
""" Returns a normalized locale code for the given locale
@@ -403,7 +421,7 @@ def normalize(localename):
if code is not None:
#print('lookup without modifier succeeded')
if '@' not in code:
- return code + '@' + modifier
+ return _append_modifier(code, modifier)
if code.split('@', 1)[1].lower() == modifier:
return code
#print('second lookup failed')
@@ -427,7 +445,8 @@ def normalize(localename):
if code is not None:
#print('lookup without modifier and encoding succeeded')
if '@' not in code:
- return _replace_encoding(code, encoding) + '@' + modifier
+ code = _replace_encoding(code, encoding)
+ return _append_modifier(code, modifier)
code, defmod = code.split('@', 1)
if defmod.lower() == modifier:
return _replace_encoding(code, encoding) + '@' + defmod
@@ -586,8 +605,8 @@ if sys.platform.startswith("win"):
# On Win32, this will return the ANSI code page
def getpreferredencoding(do_setlocale = True):
"""Return the charset that the user is likely using."""
- import _locale
- return _locale._getdefaultlocale()[1]
+ import _bootlocale
+ return _bootlocale.getpreferredencoding(False)
else:
# On Unix, if CODESET is available, use that.
try:
@@ -606,27 +625,16 @@ else:
def getpreferredencoding(do_setlocale = True):
"""Return the charset that the user is likely using,
according to the system configuration."""
+ import _bootlocale
if do_setlocale:
oldloc = setlocale(LC_CTYPE)
try:
setlocale(LC_CTYPE, "")
except Error:
pass
- result = nl_langinfo(CODESET)
- if not result and sys.platform == 'darwin':
- # nl_langinfo can return an empty string
- # when the setting has an invalid value.
- # Default to UTF-8 in that case because
- # UTF-8 is the default charset on OSX and
- # returning nothing will crash the
- # interpreter.
- result = 'UTF-8'
+ result = _bootlocale.getpreferredencoding(False)
+ if do_setlocale:
setlocale(LC_CTYPE, oldloc)
- else:
- result = nl_langinfo(CODESET)
- if not result and sys.platform == 'darwin':
- # See above for explanation
- result = 'UTF-8'
return result
@@ -654,6 +662,14 @@ locale_encoding_alias = {
'jis': 'JIS7',
'jis7': 'JIS7',
'ajec': 'eucJP',
+ 'koi8c': 'KOI8-C',
+ 'microsoftcp1251': 'CP1251',
+ 'microsoftcp1255': 'CP1255',
+ 'microsoftcp1256': 'CP1256',
+ '88591': 'ISO8859-1',
+ '88592': 'ISO8859-2',
+ '88595': 'ISO8859-5',
+ '885915': 'ISO8859-15',
# Mappings from Python codec names to C lib encoding names
'ascii': 'ISO8859-1',
@@ -681,10 +697,18 @@ locale_encoding_alias = {
'utf_8': 'UTF-8',
'koi8_r': 'KOI8-R',
'koi8_u': 'KOI8-U',
+ 'cp1251': 'CP1251',
+ 'cp1255': 'CP1255',
+ 'cp1256': 'CP1256',
+
# XXX This list is still incomplete. If you know more
# mappings, please file a bug report. Thanks.
}
+for k, v in sorted(locale_encoding_alias.items()):
+ k = k.replace('_', '')
+ locale_encoding_alias.setdefault(k, v)
+
#
# The locale_alias table maps lowercase alias names to C locale names
# (case-sensitive). Encodings are always separated from the locale
@@ -797,55 +821,33 @@ locale_encoding_alias = {
locale_alias = {
'a3': 'az_AZ.KOI8-C',
'a3_az': 'az_AZ.KOI8-C',
- 'a3_az.koi8c': 'az_AZ.KOI8-C',
'a3_az.koic': 'az_AZ.KOI8-C',
'af': 'af_ZA.ISO8859-1',
'af_za': 'af_ZA.ISO8859-1',
- 'af_za.iso88591': 'af_ZA.ISO8859-1',
'am': 'am_ET.UTF-8',
'am_et': 'am_ET.UTF-8',
'american': 'en_US.ISO8859-1',
- 'american.iso88591': 'en_US.ISO8859-1',
'ar': 'ar_AA.ISO8859-6',
'ar_aa': 'ar_AA.ISO8859-6',
- 'ar_aa.iso88596': 'ar_AA.ISO8859-6',
'ar_ae': 'ar_AE.ISO8859-6',
- 'ar_ae.iso88596': 'ar_AE.ISO8859-6',
'ar_bh': 'ar_BH.ISO8859-6',
- 'ar_bh.iso88596': 'ar_BH.ISO8859-6',
'ar_dz': 'ar_DZ.ISO8859-6',
- 'ar_dz.iso88596': 'ar_DZ.ISO8859-6',
'ar_eg': 'ar_EG.ISO8859-6',
- 'ar_eg.iso88596': 'ar_EG.ISO8859-6',
'ar_in': 'ar_IN.UTF-8',
'ar_iq': 'ar_IQ.ISO8859-6',
- 'ar_iq.iso88596': 'ar_IQ.ISO8859-6',
'ar_jo': 'ar_JO.ISO8859-6',
- 'ar_jo.iso88596': 'ar_JO.ISO8859-6',
'ar_kw': 'ar_KW.ISO8859-6',
- 'ar_kw.iso88596': 'ar_KW.ISO8859-6',
'ar_lb': 'ar_LB.ISO8859-6',
- 'ar_lb.iso88596': 'ar_LB.ISO8859-6',
'ar_ly': 'ar_LY.ISO8859-6',
- 'ar_ly.iso88596': 'ar_LY.ISO8859-6',
'ar_ma': 'ar_MA.ISO8859-6',
- 'ar_ma.iso88596': 'ar_MA.ISO8859-6',
'ar_om': 'ar_OM.ISO8859-6',
- 'ar_om.iso88596': 'ar_OM.ISO8859-6',
'ar_qa': 'ar_QA.ISO8859-6',
- 'ar_qa.iso88596': 'ar_QA.ISO8859-6',
'ar_sa': 'ar_SA.ISO8859-6',
- 'ar_sa.iso88596': 'ar_SA.ISO8859-6',
'ar_sd': 'ar_SD.ISO8859-6',
- 'ar_sd.iso88596': 'ar_SD.ISO8859-6',
'ar_sy': 'ar_SY.ISO8859-6',
- 'ar_sy.iso88596': 'ar_SY.ISO8859-6',
'ar_tn': 'ar_TN.ISO8859-6',
- 'ar_tn.iso88596': 'ar_TN.ISO8859-6',
'ar_ye': 'ar_YE.ISO8859-6',
- 'ar_ye.iso88596': 'ar_YE.ISO8859-6',
'arabic': 'ar_AA.ISO8859-6',
- 'arabic.iso88596': 'ar_AA.ISO8859-6',
'as': 'as_IN.UTF-8',
'as_in': 'as_IN.UTF-8',
'az': 'az_AZ.ISO8859-9E',
@@ -854,35 +856,20 @@ locale_alias = {
'be': 'be_BY.CP1251',
'be@latin': 'be_BY.UTF-8@latin',
'be_by': 'be_BY.CP1251',
- 'be_by.cp1251': 'be_BY.CP1251',
- 'be_by.microsoftcp1251': 'be_BY.CP1251',
- 'be_by.utf8@latin': 'be_BY.UTF-8@latin',
'be_by@latin': 'be_BY.UTF-8@latin',
'bg': 'bg_BG.CP1251',
'bg_bg': 'bg_BG.CP1251',
- 'bg_bg.cp1251': 'bg_BG.CP1251',
- 'bg_bg.iso88595': 'bg_BG.ISO8859-5',
- 'bg_bg.koi8r': 'bg_BG.KOI8-R',
- 'bg_bg.microsoftcp1251': 'bg_BG.CP1251',
'bn_in': 'bn_IN.UTF-8',
'bo_in': 'bo_IN.UTF-8',
'bokmal': 'nb_NO.ISO8859-1',
'bokm\xe5l': 'nb_NO.ISO8859-1',
'br': 'br_FR.ISO8859-1',
'br_fr': 'br_FR.ISO8859-1',
- 'br_fr.iso88591': 'br_FR.ISO8859-1',
- 'br_fr.iso885914': 'br_FR.ISO8859-14',
- 'br_fr.iso885915': 'br_FR.ISO8859-15',
- 'br_fr.iso885915@euro': 'br_FR.ISO8859-15',
- 'br_fr.utf8@euro': 'br_FR.UTF-8',
- 'br_fr@euro': 'br_FR.ISO8859-15',
'bs': 'bs_BA.ISO8859-2',
'bs_ba': 'bs_BA.ISO8859-2',
- 'bs_ba.iso88592': 'bs_BA.ISO8859-2',
'bulgarian': 'bg_BG.CP1251',
'c': 'C',
'c-french': 'fr_CA.ISO8859-1',
- 'c-french.iso88591': 'fr_CA.ISO8859-1',
'c.ascii': 'C',
'c.en': 'C',
'c.iso88591': 'en_US.ISO8859-1',
@@ -890,343 +877,131 @@ locale_alias = {
'c_c.c': 'C',
'ca': 'ca_ES.ISO8859-1',
'ca_ad': 'ca_AD.ISO8859-1',
- 'ca_ad.iso88591': 'ca_AD.ISO8859-1',
- 'ca_ad.iso885915': 'ca_AD.ISO8859-15',
- 'ca_ad.iso885915@euro': 'ca_AD.ISO8859-15',
- 'ca_ad.utf8@euro': 'ca_AD.UTF-8',
- 'ca_ad@euro': 'ca_AD.ISO8859-15',
'ca_es': 'ca_ES.ISO8859-1',
- 'ca_es.iso88591': 'ca_ES.ISO8859-1',
- 'ca_es.iso885915': 'ca_ES.ISO8859-15',
- 'ca_es.iso885915@euro': 'ca_ES.ISO8859-15',
- 'ca_es.utf8@euro': 'ca_ES.UTF-8',
- 'ca_es@euro': 'ca_ES.ISO8859-15',
'ca_fr': 'ca_FR.ISO8859-1',
- 'ca_fr.iso88591': 'ca_FR.ISO8859-1',
- 'ca_fr.iso885915': 'ca_FR.ISO8859-15',
- 'ca_fr.iso885915@euro': 'ca_FR.ISO8859-15',
- 'ca_fr.utf8@euro': 'ca_FR.UTF-8',
- 'ca_fr@euro': 'ca_FR.ISO8859-15',
'ca_it': 'ca_IT.ISO8859-1',
- 'ca_it.iso88591': 'ca_IT.ISO8859-1',
- 'ca_it.iso885915': 'ca_IT.ISO8859-15',
- 'ca_it.iso885915@euro': 'ca_IT.ISO8859-15',
- 'ca_it.utf8@euro': 'ca_IT.UTF-8',
- 'ca_it@euro': 'ca_IT.ISO8859-15',
'catalan': 'ca_ES.ISO8859-1',
'cextend': 'en_US.ISO8859-1',
- 'cextend.en': 'en_US.ISO8859-1',
'chinese-s': 'zh_CN.eucCN',
'chinese-t': 'zh_TW.eucTW',
'croatian': 'hr_HR.ISO8859-2',
'cs': 'cs_CZ.ISO8859-2',
'cs_cs': 'cs_CZ.ISO8859-2',
- 'cs_cs.iso88592': 'cs_CZ.ISO8859-2',
'cs_cz': 'cs_CZ.ISO8859-2',
- 'cs_cz.iso88592': 'cs_CZ.ISO8859-2',
'cy': 'cy_GB.ISO8859-1',
'cy_gb': 'cy_GB.ISO8859-1',
- 'cy_gb.iso88591': 'cy_GB.ISO8859-1',
- 'cy_gb.iso885914': 'cy_GB.ISO8859-14',
- 'cy_gb.iso885915': 'cy_GB.ISO8859-15',
- 'cy_gb@euro': 'cy_GB.ISO8859-15',
'cz': 'cs_CZ.ISO8859-2',
'cz_cz': 'cs_CZ.ISO8859-2',
'czech': 'cs_CZ.ISO8859-2',
'da': 'da_DK.ISO8859-1',
- 'da.iso885915': 'da_DK.ISO8859-15',
'da_dk': 'da_DK.ISO8859-1',
- 'da_dk.88591': 'da_DK.ISO8859-1',
- 'da_dk.885915': 'da_DK.ISO8859-15',
- 'da_dk.iso88591': 'da_DK.ISO8859-1',
- 'da_dk.iso885915': 'da_DK.ISO8859-15',
- 'da_dk@euro': 'da_DK.ISO8859-15',
'danish': 'da_DK.ISO8859-1',
- 'danish.iso88591': 'da_DK.ISO8859-1',
'dansk': 'da_DK.ISO8859-1',
'de': 'de_DE.ISO8859-1',
- 'de.iso885915': 'de_DE.ISO8859-15',
'de_at': 'de_AT.ISO8859-1',
- 'de_at.iso88591': 'de_AT.ISO8859-1',
- 'de_at.iso885915': 'de_AT.ISO8859-15',
- 'de_at.iso885915@euro': 'de_AT.ISO8859-15',
- 'de_at.utf8@euro': 'de_AT.UTF-8',
- 'de_at@euro': 'de_AT.ISO8859-15',
'de_be': 'de_BE.ISO8859-1',
- 'de_be.iso88591': 'de_BE.ISO8859-1',
- 'de_be.iso885915': 'de_BE.ISO8859-15',
- 'de_be.iso885915@euro': 'de_BE.ISO8859-15',
- 'de_be.utf8@euro': 'de_BE.UTF-8',
- 'de_be@euro': 'de_BE.ISO8859-15',
'de_ch': 'de_CH.ISO8859-1',
- 'de_ch.iso88591': 'de_CH.ISO8859-1',
- 'de_ch.iso885915': 'de_CH.ISO8859-15',
- 'de_ch@euro': 'de_CH.ISO8859-15',
'de_de': 'de_DE.ISO8859-1',
- 'de_de.88591': 'de_DE.ISO8859-1',
- 'de_de.885915': 'de_DE.ISO8859-15',
- 'de_de.885915@euro': 'de_DE.ISO8859-15',
- 'de_de.iso88591': 'de_DE.ISO8859-1',
- 'de_de.iso885915': 'de_DE.ISO8859-15',
- 'de_de.iso885915@euro': 'de_DE.ISO8859-15',
- 'de_de.utf8@euro': 'de_DE.UTF-8',
- 'de_de@euro': 'de_DE.ISO8859-15',
'de_lu': 'de_LU.ISO8859-1',
- 'de_lu.iso88591': 'de_LU.ISO8859-1',
- 'de_lu.iso885915': 'de_LU.ISO8859-15',
- 'de_lu.iso885915@euro': 'de_LU.ISO8859-15',
- 'de_lu.utf8@euro': 'de_LU.UTF-8',
- 'de_lu@euro': 'de_LU.ISO8859-15',
'deutsch': 'de_DE.ISO8859-1',
'dutch': 'nl_NL.ISO8859-1',
'dutch.iso88591': 'nl_BE.ISO8859-1',
'ee': 'ee_EE.ISO8859-4',
'ee_ee': 'ee_EE.ISO8859-4',
- 'ee_ee.iso88594': 'ee_EE.ISO8859-4',
'eesti': 'et_EE.ISO8859-1',
'el': 'el_GR.ISO8859-7',
'el_gr': 'el_GR.ISO8859-7',
- 'el_gr.iso88597': 'el_GR.ISO8859-7',
'el_gr@euro': 'el_GR.ISO8859-15',
'en': 'en_US.ISO8859-1',
- 'en.iso88591': 'en_US.ISO8859-1',
'en_au': 'en_AU.ISO8859-1',
- 'en_au.iso88591': 'en_AU.ISO8859-1',
'en_be': 'en_BE.ISO8859-1',
- 'en_be@euro': 'en_BE.ISO8859-15',
'en_bw': 'en_BW.ISO8859-1',
- 'en_bw.iso88591': 'en_BW.ISO8859-1',
'en_ca': 'en_CA.ISO8859-1',
- 'en_ca.iso88591': 'en_CA.ISO8859-1',
'en_gb': 'en_GB.ISO8859-1',
- 'en_gb.88591': 'en_GB.ISO8859-1',
- 'en_gb.iso88591': 'en_GB.ISO8859-1',
- 'en_gb.iso885915': 'en_GB.ISO8859-15',
- 'en_gb@euro': 'en_GB.ISO8859-15',
'en_hk': 'en_HK.ISO8859-1',
- 'en_hk.iso88591': 'en_HK.ISO8859-1',
'en_ie': 'en_IE.ISO8859-1',
- 'en_ie.iso88591': 'en_IE.ISO8859-1',
- 'en_ie.iso885915': 'en_IE.ISO8859-15',
- 'en_ie.iso885915@euro': 'en_IE.ISO8859-15',
- 'en_ie.utf8@euro': 'en_IE.UTF-8',
- 'en_ie@euro': 'en_IE.ISO8859-15',
'en_in': 'en_IN.ISO8859-1',
'en_nz': 'en_NZ.ISO8859-1',
- 'en_nz.iso88591': 'en_NZ.ISO8859-1',
'en_ph': 'en_PH.ISO8859-1',
- 'en_ph.iso88591': 'en_PH.ISO8859-1',
'en_sg': 'en_SG.ISO8859-1',
- 'en_sg.iso88591': 'en_SG.ISO8859-1',
'en_uk': 'en_GB.ISO8859-1',
'en_us': 'en_US.ISO8859-1',
- 'en_us.88591': 'en_US.ISO8859-1',
- 'en_us.885915': 'en_US.ISO8859-15',
- 'en_us.iso88591': 'en_US.ISO8859-1',
- 'en_us.iso885915': 'en_US.ISO8859-15',
- 'en_us.iso885915@euro': 'en_US.ISO8859-15',
- 'en_us@euro': 'en_US.ISO8859-15',
'en_us@euro@euro': 'en_US.ISO8859-15',
'en_za': 'en_ZA.ISO8859-1',
- 'en_za.88591': 'en_ZA.ISO8859-1',
- 'en_za.iso88591': 'en_ZA.ISO8859-1',
- 'en_za.iso885915': 'en_ZA.ISO8859-15',
- 'en_za@euro': 'en_ZA.ISO8859-15',
'en_zw': 'en_ZW.ISO8859-1',
- 'en_zw.iso88591': 'en_ZW.ISO8859-1',
'eng_gb': 'en_GB.ISO8859-1',
- 'eng_gb.8859': 'en_GB.ISO8859-1',
'english': 'en_EN.ISO8859-1',
- 'english.iso88591': 'en_EN.ISO8859-1',
'english_uk': 'en_GB.ISO8859-1',
- 'english_uk.8859': 'en_GB.ISO8859-1',
'english_united-states': 'en_US.ISO8859-1',
'english_united-states.437': 'C',
'english_us': 'en_US.ISO8859-1',
- 'english_us.8859': 'en_US.ISO8859-1',
- 'english_us.ascii': 'en_US.ISO8859-1',
'eo': 'eo_XX.ISO8859-3',
'eo_eo': 'eo_EO.ISO8859-3',
- 'eo_eo.iso88593': 'eo_EO.ISO8859-3',
'eo_xx': 'eo_XX.ISO8859-3',
- 'eo_xx.iso88593': 'eo_XX.ISO8859-3',
'es': 'es_ES.ISO8859-1',
'es_ar': 'es_AR.ISO8859-1',
- 'es_ar.iso88591': 'es_AR.ISO8859-1',
'es_bo': 'es_BO.ISO8859-1',
- 'es_bo.iso88591': 'es_BO.ISO8859-1',
'es_cl': 'es_CL.ISO8859-1',
- 'es_cl.iso88591': 'es_CL.ISO8859-1',
'es_co': 'es_CO.ISO8859-1',
- 'es_co.iso88591': 'es_CO.ISO8859-1',
'es_cr': 'es_CR.ISO8859-1',
- 'es_cr.iso88591': 'es_CR.ISO8859-1',
'es_do': 'es_DO.ISO8859-1',
- 'es_do.iso88591': 'es_DO.ISO8859-1',
'es_ec': 'es_EC.ISO8859-1',
- 'es_ec.iso88591': 'es_EC.ISO8859-1',
'es_es': 'es_ES.ISO8859-1',
- 'es_es.88591': 'es_ES.ISO8859-1',
- 'es_es.iso88591': 'es_ES.ISO8859-1',
- 'es_es.iso885915': 'es_ES.ISO8859-15',
- 'es_es.iso885915@euro': 'es_ES.ISO8859-15',
- 'es_es.utf8@euro': 'es_ES.UTF-8',
- 'es_es@euro': 'es_ES.ISO8859-15',
'es_gt': 'es_GT.ISO8859-1',
- 'es_gt.iso88591': 'es_GT.ISO8859-1',
'es_hn': 'es_HN.ISO8859-1',
- 'es_hn.iso88591': 'es_HN.ISO8859-1',
'es_mx': 'es_MX.ISO8859-1',
- 'es_mx.iso88591': 'es_MX.ISO8859-1',
'es_ni': 'es_NI.ISO8859-1',
- 'es_ni.iso88591': 'es_NI.ISO8859-1',
'es_pa': 'es_PA.ISO8859-1',
- 'es_pa.iso88591': 'es_PA.ISO8859-1',
- 'es_pa.iso885915': 'es_PA.ISO8859-15',
- 'es_pa@euro': 'es_PA.ISO8859-15',
'es_pe': 'es_PE.ISO8859-1',
- 'es_pe.iso88591': 'es_PE.ISO8859-1',
- 'es_pe.iso885915': 'es_PE.ISO8859-15',
- 'es_pe@euro': 'es_PE.ISO8859-15',
'es_pr': 'es_PR.ISO8859-1',
- 'es_pr.iso88591': 'es_PR.ISO8859-1',
'es_py': 'es_PY.ISO8859-1',
- 'es_py.iso88591': 'es_PY.ISO8859-1',
- 'es_py.iso885915': 'es_PY.ISO8859-15',
- 'es_py@euro': 'es_PY.ISO8859-15',
'es_sv': 'es_SV.ISO8859-1',
- 'es_sv.iso88591': 'es_SV.ISO8859-1',
- 'es_sv.iso885915': 'es_SV.ISO8859-15',
- 'es_sv@euro': 'es_SV.ISO8859-15',
'es_us': 'es_US.ISO8859-1',
- 'es_us.iso88591': 'es_US.ISO8859-1',
'es_uy': 'es_UY.ISO8859-1',
- 'es_uy.iso88591': 'es_UY.ISO8859-1',
- 'es_uy.iso885915': 'es_UY.ISO8859-15',
- 'es_uy@euro': 'es_UY.ISO8859-15',
'es_ve': 'es_VE.ISO8859-1',
- 'es_ve.iso88591': 'es_VE.ISO8859-1',
- 'es_ve.iso885915': 'es_VE.ISO8859-15',
- 'es_ve@euro': 'es_VE.ISO8859-15',
'estonian': 'et_EE.ISO8859-1',
'et': 'et_EE.ISO8859-15',
'et_ee': 'et_EE.ISO8859-15',
- 'et_ee.iso88591': 'et_EE.ISO8859-1',
- 'et_ee.iso885913': 'et_EE.ISO8859-13',
- 'et_ee.iso885915': 'et_EE.ISO8859-15',
- 'et_ee.iso88594': 'et_EE.ISO8859-4',
- 'et_ee@euro': 'et_EE.ISO8859-15',
'eu': 'eu_ES.ISO8859-1',
'eu_es': 'eu_ES.ISO8859-1',
- 'eu_es.iso88591': 'eu_ES.ISO8859-1',
- 'eu_es.iso885915': 'eu_ES.ISO8859-15',
- 'eu_es.iso885915@euro': 'eu_ES.ISO8859-15',
- 'eu_es.utf8@euro': 'eu_ES.UTF-8',
- 'eu_es@euro': 'eu_ES.ISO8859-15',
'fa': 'fa_IR.UTF-8',
'fa_ir': 'fa_IR.UTF-8',
'fa_ir.isiri3342': 'fa_IR.ISIRI-3342',
'fi': 'fi_FI.ISO8859-15',
- 'fi.iso885915': 'fi_FI.ISO8859-15',
'fi_fi': 'fi_FI.ISO8859-15',
- 'fi_fi.88591': 'fi_FI.ISO8859-1',
- 'fi_fi.iso88591': 'fi_FI.ISO8859-1',
- 'fi_fi.iso885915': 'fi_FI.ISO8859-15',
- 'fi_fi.iso885915@euro': 'fi_FI.ISO8859-15',
- 'fi_fi.utf8@euro': 'fi_FI.UTF-8',
- 'fi_fi@euro': 'fi_FI.ISO8859-15',
'finnish': 'fi_FI.ISO8859-1',
- 'finnish.iso88591': 'fi_FI.ISO8859-1',
'fo': 'fo_FO.ISO8859-1',
'fo_fo': 'fo_FO.ISO8859-1',
- 'fo_fo.iso88591': 'fo_FO.ISO8859-1',
- 'fo_fo.iso885915': 'fo_FO.ISO8859-15',
- 'fo_fo@euro': 'fo_FO.ISO8859-15',
'fr': 'fr_FR.ISO8859-1',
- 'fr.iso885915': 'fr_FR.ISO8859-15',
'fr_be': 'fr_BE.ISO8859-1',
- 'fr_be.88591': 'fr_BE.ISO8859-1',
- 'fr_be.iso88591': 'fr_BE.ISO8859-1',
- 'fr_be.iso885915': 'fr_BE.ISO8859-15',
- 'fr_be.iso885915@euro': 'fr_BE.ISO8859-15',
- 'fr_be.utf8@euro': 'fr_BE.UTF-8',
- 'fr_be@euro': 'fr_BE.ISO8859-15',
'fr_ca': 'fr_CA.ISO8859-1',
- 'fr_ca.88591': 'fr_CA.ISO8859-1',
- 'fr_ca.iso88591': 'fr_CA.ISO8859-1',
- 'fr_ca.iso885915': 'fr_CA.ISO8859-15',
- 'fr_ca@euro': 'fr_CA.ISO8859-15',
'fr_ch': 'fr_CH.ISO8859-1',
- 'fr_ch.88591': 'fr_CH.ISO8859-1',
- 'fr_ch.iso88591': 'fr_CH.ISO8859-1',
- 'fr_ch.iso885915': 'fr_CH.ISO8859-15',
- 'fr_ch@euro': 'fr_CH.ISO8859-15',
'fr_fr': 'fr_FR.ISO8859-1',
- 'fr_fr.88591': 'fr_FR.ISO8859-1',
- 'fr_fr.iso88591': 'fr_FR.ISO8859-1',
- 'fr_fr.iso885915': 'fr_FR.ISO8859-15',
- 'fr_fr.iso885915@euro': 'fr_FR.ISO8859-15',
- 'fr_fr.utf8@euro': 'fr_FR.UTF-8',
- 'fr_fr@euro': 'fr_FR.ISO8859-15',
'fr_lu': 'fr_LU.ISO8859-1',
- 'fr_lu.88591': 'fr_LU.ISO8859-1',
- 'fr_lu.iso88591': 'fr_LU.ISO8859-1',
- 'fr_lu.iso885915': 'fr_LU.ISO8859-15',
- 'fr_lu.iso885915@euro': 'fr_LU.ISO8859-15',
- 'fr_lu.utf8@euro': 'fr_LU.UTF-8',
- 'fr_lu@euro': 'fr_LU.ISO8859-15',
'fran\xe7ais': 'fr_FR.ISO8859-1',
'fre_fr': 'fr_FR.ISO8859-1',
- 'fre_fr.8859': 'fr_FR.ISO8859-1',
'french': 'fr_FR.ISO8859-1',
'french.iso88591': 'fr_CH.ISO8859-1',
'french_france': 'fr_FR.ISO8859-1',
- 'french_france.8859': 'fr_FR.ISO8859-1',
'ga': 'ga_IE.ISO8859-1',
'ga_ie': 'ga_IE.ISO8859-1',
- 'ga_ie.iso88591': 'ga_IE.ISO8859-1',
- 'ga_ie.iso885914': 'ga_IE.ISO8859-14',
- 'ga_ie.iso885915': 'ga_IE.ISO8859-15',
- 'ga_ie.iso885915@euro': 'ga_IE.ISO8859-15',
- 'ga_ie.utf8@euro': 'ga_IE.UTF-8',
- 'ga_ie@euro': 'ga_IE.ISO8859-15',
'galego': 'gl_ES.ISO8859-1',
'galician': 'gl_ES.ISO8859-1',
'gd': 'gd_GB.ISO8859-1',
'gd_gb': 'gd_GB.ISO8859-1',
- 'gd_gb.iso88591': 'gd_GB.ISO8859-1',
- 'gd_gb.iso885914': 'gd_GB.ISO8859-14',
- 'gd_gb.iso885915': 'gd_GB.ISO8859-15',
- 'gd_gb@euro': 'gd_GB.ISO8859-15',
'ger_de': 'de_DE.ISO8859-1',
- 'ger_de.8859': 'de_DE.ISO8859-1',
'german': 'de_DE.ISO8859-1',
'german.iso88591': 'de_CH.ISO8859-1',
'german_germany': 'de_DE.ISO8859-1',
- 'german_germany.8859': 'de_DE.ISO8859-1',
'gl': 'gl_ES.ISO8859-1',
'gl_es': 'gl_ES.ISO8859-1',
- 'gl_es.iso88591': 'gl_ES.ISO8859-1',
- 'gl_es.iso885915': 'gl_ES.ISO8859-15',
- 'gl_es.iso885915@euro': 'gl_ES.ISO8859-15',
- 'gl_es.utf8@euro': 'gl_ES.UTF-8',
- 'gl_es@euro': 'gl_ES.ISO8859-15',
'greek': 'el_GR.ISO8859-7',
- 'greek.iso88597': 'el_GR.ISO8859-7',
'gu_in': 'gu_IN.UTF-8',
'gv': 'gv_GB.ISO8859-1',
'gv_gb': 'gv_GB.ISO8859-1',
- 'gv_gb.iso88591': 'gv_GB.ISO8859-1',
- 'gv_gb.iso885914': 'gv_GB.ISO8859-14',
- 'gv_gb.iso885915': 'gv_GB.ISO8859-15',
- 'gv_gb@euro': 'gv_GB.ISO8859-15',
'he': 'he_IL.ISO8859-8',
'he_il': 'he_IL.ISO8859-8',
- 'he_il.cp1255': 'he_IL.CP1255',
- 'he_il.iso88598': 'he_IL.ISO8859-8',
- 'he_il.microsoftcp1255': 'he_IL.CP1255',
'hebrew': 'he_IL.ISO8859-8',
- 'hebrew.iso88598': 'he_IL.ISO8859-8',
'hi': 'hi_IN.ISCII-DEV',
'hi_in': 'hi_IN.ISCII-DEV',
'hi_in.isciidev': 'hi_IN.ISCII-DEV',
@@ -1234,23 +1009,17 @@ locale_alias = {
'hne_in': 'hne_IN.UTF-8',
'hr': 'hr_HR.ISO8859-2',
'hr_hr': 'hr_HR.ISO8859-2',
- 'hr_hr.iso88592': 'hr_HR.ISO8859-2',
'hrvatski': 'hr_HR.ISO8859-2',
'hu': 'hu_HU.ISO8859-2',
'hu_hu': 'hu_HU.ISO8859-2',
- 'hu_hu.iso88592': 'hu_HU.ISO8859-2',
'hungarian': 'hu_HU.ISO8859-2',
'icelandic': 'is_IS.ISO8859-1',
- 'icelandic.iso88591': 'is_IS.ISO8859-1',
'id': 'id_ID.ISO8859-1',
'id_id': 'id_ID.ISO8859-1',
'in': 'id_ID.ISO8859-1',
'in_id': 'id_ID.ISO8859-1',
'is': 'is_IS.ISO8859-1',
'is_is': 'is_IS.ISO8859-1',
- 'is_is.iso88591': 'is_IS.ISO8859-1',
- 'is_is.iso885915': 'is_IS.ISO8859-15',
- 'is_is@euro': 'is_IS.ISO8859-15',
'iso-8859-1': 'en_US.ISO8859-1',
'iso-8859-15': 'en_US.ISO8859-15',
'iso8859-1': 'en_US.ISO8859-1',
@@ -1258,46 +1027,23 @@ locale_alias = {
'iso_8859_1': 'en_US.ISO8859-1',
'iso_8859_15': 'en_US.ISO8859-15',
'it': 'it_IT.ISO8859-1',
- 'it.iso885915': 'it_IT.ISO8859-15',
'it_ch': 'it_CH.ISO8859-1',
- 'it_ch.iso88591': 'it_CH.ISO8859-1',
- 'it_ch.iso885915': 'it_CH.ISO8859-15',
- 'it_ch@euro': 'it_CH.ISO8859-15',
'it_it': 'it_IT.ISO8859-1',
- 'it_it.88591': 'it_IT.ISO8859-1',
- 'it_it.iso88591': 'it_IT.ISO8859-1',
- 'it_it.iso885915': 'it_IT.ISO8859-15',
- 'it_it.iso885915@euro': 'it_IT.ISO8859-15',
- 'it_it.utf8@euro': 'it_IT.UTF-8',
- 'it_it@euro': 'it_IT.ISO8859-15',
'italian': 'it_IT.ISO8859-1',
- 'italian.iso88591': 'it_IT.ISO8859-1',
'iu': 'iu_CA.NUNACOM-8',
'iu_ca': 'iu_CA.NUNACOM-8',
'iu_ca.nunacom8': 'iu_CA.NUNACOM-8',
'iw': 'he_IL.ISO8859-8',
'iw_il': 'he_IL.ISO8859-8',
- 'iw_il.iso88598': 'he_IL.ISO8859-8',
'ja': 'ja_JP.eucJP',
- 'ja.jis': 'ja_JP.JIS7',
- 'ja.sjis': 'ja_JP.SJIS',
'ja_jp': 'ja_JP.eucJP',
- 'ja_jp.ajec': 'ja_JP.eucJP',
'ja_jp.euc': 'ja_JP.eucJP',
- 'ja_jp.eucjp': 'ja_JP.eucJP',
- 'ja_jp.iso-2022-jp': 'ja_JP.JIS7',
- 'ja_jp.iso2022jp': 'ja_JP.JIS7',
- 'ja_jp.jis': 'ja_JP.JIS7',
- 'ja_jp.jis7': 'ja_JP.JIS7',
'ja_jp.mscode': 'ja_JP.SJIS',
'ja_jp.pck': 'ja_JP.SJIS',
- 'ja_jp.sjis': 'ja_JP.SJIS',
- 'ja_jp.ujis': 'ja_JP.eucJP',
'japan': 'ja_JP.eucJP',
'japanese': 'ja_JP.eucJP',
'japanese-euc': 'ja_JP.eucJP',
'japanese.euc': 'ja_JP.eucJP',
- 'japanese.sjis': 'ja_JP.SJIS',
'jp_jp': 'ja_JP.eucJP',
'ka': 'ka_GE.GEORGIAN-ACADEMY',
'ka_ge': 'ka_GE.GEORGIAN-ACADEMY',
@@ -1306,27 +1052,18 @@ locale_alias = {
'ka_ge.georgianrs': 'ka_GE.GEORGIAN-ACADEMY',
'kl': 'kl_GL.ISO8859-1',
'kl_gl': 'kl_GL.ISO8859-1',
- 'kl_gl.iso88591': 'kl_GL.ISO8859-1',
- 'kl_gl.iso885915': 'kl_GL.ISO8859-15',
- 'kl_gl@euro': 'kl_GL.ISO8859-15',
'km_kh': 'km_KH.UTF-8',
'kn': 'kn_IN.UTF-8',
'kn_in': 'kn_IN.UTF-8',
'ko': 'ko_KR.eucKR',
'ko_kr': 'ko_KR.eucKR',
'ko_kr.euc': 'ko_KR.eucKR',
- 'ko_kr.euckr': 'ko_KR.eucKR',
'korean': 'ko_KR.eucKR',
'korean.euc': 'ko_KR.eucKR',
'ks': 'ks_IN.UTF-8',
'ks_in': 'ks_IN.UTF-8',
- 'ks_in@devanagari': 'ks_IN.UTF-8@devanagari',
'kw': 'kw_GB.ISO8859-1',
'kw_gb': 'kw_GB.ISO8859-1',
- 'kw_gb.iso88591': 'kw_GB.ISO8859-1',
- 'kw_gb.iso885914': 'kw_GB.ISO8859-14',
- 'kw_gb.iso885915': 'kw_GB.ISO8859-15',
- 'kw_gb@euro': 'kw_GB.ISO8859-15',
'ky': 'ky_KG.UTF-8',
'ky_kg': 'ky_KG.UTF-8',
'lithuanian': 'lt_LT.ISO8859-13',
@@ -1337,157 +1074,78 @@ locale_alias = {
'lo_la.mulelao1': 'lo_LA.MULELAO-1',
'lt': 'lt_LT.ISO8859-13',
'lt_lt': 'lt_LT.ISO8859-13',
- 'lt_lt.iso885913': 'lt_LT.ISO8859-13',
- 'lt_lt.iso88594': 'lt_LT.ISO8859-4',
'lv': 'lv_LV.ISO8859-13',
'lv_lv': 'lv_LV.ISO8859-13',
- 'lv_lv.iso885913': 'lv_LV.ISO8859-13',
- 'lv_lv.iso88594': 'lv_LV.ISO8859-4',
'mai': 'mai_IN.UTF-8',
'mai_in': 'mai_IN.UTF-8',
'mi': 'mi_NZ.ISO8859-1',
'mi_nz': 'mi_NZ.ISO8859-1',
- 'mi_nz.iso88591': 'mi_NZ.ISO8859-1',
'mk': 'mk_MK.ISO8859-5',
'mk_mk': 'mk_MK.ISO8859-5',
- 'mk_mk.cp1251': 'mk_MK.CP1251',
- 'mk_mk.iso88595': 'mk_MK.ISO8859-5',
- 'mk_mk.microsoftcp1251': 'mk_MK.CP1251',
'ml': 'ml_IN.UTF-8',
'ml_in': 'ml_IN.UTF-8',
'mr': 'mr_IN.UTF-8',
'mr_in': 'mr_IN.UTF-8',
'ms': 'ms_MY.ISO8859-1',
'ms_my': 'ms_MY.ISO8859-1',
- 'ms_my.iso88591': 'ms_MY.ISO8859-1',
'mt': 'mt_MT.ISO8859-3',
'mt_mt': 'mt_MT.ISO8859-3',
- 'mt_mt.iso88593': 'mt_MT.ISO8859-3',
'nb': 'nb_NO.ISO8859-1',
'nb_no': 'nb_NO.ISO8859-1',
- 'nb_no.88591': 'nb_NO.ISO8859-1',
- 'nb_no.iso88591': 'nb_NO.ISO8859-1',
- 'nb_no.iso885915': 'nb_NO.ISO8859-15',
- 'nb_no@euro': 'nb_NO.ISO8859-15',
'ne_np': 'ne_NP.UTF-8',
'nl': 'nl_NL.ISO8859-1',
- 'nl.iso885915': 'nl_NL.ISO8859-15',
'nl_be': 'nl_BE.ISO8859-1',
- 'nl_be.88591': 'nl_BE.ISO8859-1',
- 'nl_be.iso88591': 'nl_BE.ISO8859-1',
- 'nl_be.iso885915': 'nl_BE.ISO8859-15',
- 'nl_be.iso885915@euro': 'nl_BE.ISO8859-15',
- 'nl_be.utf8@euro': 'nl_BE.UTF-8',
- 'nl_be@euro': 'nl_BE.ISO8859-15',
'nl_nl': 'nl_NL.ISO8859-1',
- 'nl_nl.88591': 'nl_NL.ISO8859-1',
- 'nl_nl.iso88591': 'nl_NL.ISO8859-1',
- 'nl_nl.iso885915': 'nl_NL.ISO8859-15',
- 'nl_nl.iso885915@euro': 'nl_NL.ISO8859-15',
- 'nl_nl.utf8@euro': 'nl_NL.UTF-8',
- 'nl_nl@euro': 'nl_NL.ISO8859-15',
'nn': 'nn_NO.ISO8859-1',
'nn_no': 'nn_NO.ISO8859-1',
- 'nn_no.88591': 'nn_NO.ISO8859-1',
- 'nn_no.iso88591': 'nn_NO.ISO8859-1',
- 'nn_no.iso885915': 'nn_NO.ISO8859-15',
- 'nn_no@euro': 'nn_NO.ISO8859-15',
'no': 'no_NO.ISO8859-1',
'no@nynorsk': 'ny_NO.ISO8859-1',
'no_no': 'no_NO.ISO8859-1',
- 'no_no.88591': 'no_NO.ISO8859-1',
- 'no_no.iso88591': 'no_NO.ISO8859-1',
- 'no_no.iso885915': 'no_NO.ISO8859-15',
'no_no.iso88591@bokmal': 'no_NO.ISO8859-1',
'no_no.iso88591@nynorsk': 'no_NO.ISO8859-1',
- 'no_no@euro': 'no_NO.ISO8859-15',
'norwegian': 'no_NO.ISO8859-1',
- 'norwegian.iso88591': 'no_NO.ISO8859-1',
'nr': 'nr_ZA.ISO8859-1',
'nr_za': 'nr_ZA.ISO8859-1',
- 'nr_za.iso88591': 'nr_ZA.ISO8859-1',
'nso': 'nso_ZA.ISO8859-15',
'nso_za': 'nso_ZA.ISO8859-15',
- 'nso_za.iso885915': 'nso_ZA.ISO8859-15',
'ny': 'ny_NO.ISO8859-1',
'ny_no': 'ny_NO.ISO8859-1',
- 'ny_no.88591': 'ny_NO.ISO8859-1',
- 'ny_no.iso88591': 'ny_NO.ISO8859-1',
- 'ny_no.iso885915': 'ny_NO.ISO8859-15',
- 'ny_no@euro': 'ny_NO.ISO8859-15',
'nynorsk': 'nn_NO.ISO8859-1',
'oc': 'oc_FR.ISO8859-1',
'oc_fr': 'oc_FR.ISO8859-1',
- 'oc_fr.iso88591': 'oc_FR.ISO8859-1',
- 'oc_fr.iso885915': 'oc_FR.ISO8859-15',
- 'oc_fr@euro': 'oc_FR.ISO8859-15',
'or': 'or_IN.UTF-8',
'or_in': 'or_IN.UTF-8',
'pa': 'pa_IN.UTF-8',
'pa_in': 'pa_IN.UTF-8',
'pd': 'pd_US.ISO8859-1',
'pd_de': 'pd_DE.ISO8859-1',
- 'pd_de.iso88591': 'pd_DE.ISO8859-1',
- 'pd_de.iso885915': 'pd_DE.ISO8859-15',
- 'pd_de@euro': 'pd_DE.ISO8859-15',
'pd_us': 'pd_US.ISO8859-1',
- 'pd_us.iso88591': 'pd_US.ISO8859-1',
- 'pd_us.iso885915': 'pd_US.ISO8859-15',
- 'pd_us@euro': 'pd_US.ISO8859-15',
'ph': 'ph_PH.ISO8859-1',
'ph_ph': 'ph_PH.ISO8859-1',
- 'ph_ph.iso88591': 'ph_PH.ISO8859-1',
'pl': 'pl_PL.ISO8859-2',
'pl_pl': 'pl_PL.ISO8859-2',
- 'pl_pl.iso88592': 'pl_PL.ISO8859-2',
'polish': 'pl_PL.ISO8859-2',
'portuguese': 'pt_PT.ISO8859-1',
- 'portuguese.iso88591': 'pt_PT.ISO8859-1',
'portuguese_brazil': 'pt_BR.ISO8859-1',
- 'portuguese_brazil.8859': 'pt_BR.ISO8859-1',
'posix': 'C',
'posix-utf2': 'C',
'pp': 'pp_AN.ISO8859-1',
'pp_an': 'pp_AN.ISO8859-1',
- 'pp_an.iso88591': 'pp_AN.ISO8859-1',
'pt': 'pt_PT.ISO8859-1',
- 'pt.iso885915': 'pt_PT.ISO8859-15',
'pt_br': 'pt_BR.ISO8859-1',
- 'pt_br.88591': 'pt_BR.ISO8859-1',
- 'pt_br.iso88591': 'pt_BR.ISO8859-1',
- 'pt_br.iso885915': 'pt_BR.ISO8859-15',
- 'pt_br@euro': 'pt_BR.ISO8859-15',
'pt_pt': 'pt_PT.ISO8859-1',
- 'pt_pt.88591': 'pt_PT.ISO8859-1',
- 'pt_pt.iso88591': 'pt_PT.ISO8859-1',
- 'pt_pt.iso885915': 'pt_PT.ISO8859-15',
- 'pt_pt.iso885915@euro': 'pt_PT.ISO8859-15',
- 'pt_pt.utf8@euro': 'pt_PT.UTF-8',
- 'pt_pt@euro': 'pt_PT.ISO8859-15',
'ro': 'ro_RO.ISO8859-2',
'ro_ro': 'ro_RO.ISO8859-2',
- 'ro_ro.iso88592': 'ro_RO.ISO8859-2',
'romanian': 'ro_RO.ISO8859-2',
'ru': 'ru_RU.UTF-8',
- 'ru.koi8r': 'ru_RU.KOI8-R',
'ru_ru': 'ru_RU.UTF-8',
- 'ru_ru.cp1251': 'ru_RU.CP1251',
- 'ru_ru.iso88595': 'ru_RU.ISO8859-5',
- 'ru_ru.koi8r': 'ru_RU.KOI8-R',
- 'ru_ru.microsoftcp1251': 'ru_RU.CP1251',
'ru_ua': 'ru_UA.KOI8-U',
- 'ru_ua.cp1251': 'ru_UA.CP1251',
- 'ru_ua.koi8u': 'ru_UA.KOI8-U',
- 'ru_ua.microsoftcp1251': 'ru_UA.CP1251',
'rumanian': 'ro_RO.ISO8859-2',
'russian': 'ru_RU.ISO8859-5',
'rw': 'rw_RW.ISO8859-1',
'rw_rw': 'rw_RW.ISO8859-1',
- 'rw_rw.iso88591': 'rw_RW.ISO8859-1',
'sd': 'sd_IN.UTF-8',
- 'sd@devanagari': 'sd_IN.UTF-8@devanagari',
'sd_in': 'sd_IN.UTF-8',
- 'sd_in@devanagari': 'sd_IN.UTF-8@devanagari',
'se_no': 'se_NO.UTF-8',
'serbocroatian': 'sr_RS.UTF-8@latin',
'sh': 'sr_RS.UTF-8@latin',
@@ -1501,37 +1159,26 @@ locale_alias = {
'sinhala': 'si_LK.UTF-8',
'sk': 'sk_SK.ISO8859-2',
'sk_sk': 'sk_SK.ISO8859-2',
- 'sk_sk.iso88592': 'sk_SK.ISO8859-2',
'sl': 'sl_SI.ISO8859-2',
'sl_cs': 'sl_CS.ISO8859-2',
'sl_si': 'sl_SI.ISO8859-2',
- 'sl_si.iso88592': 'sl_SI.ISO8859-2',
'slovak': 'sk_SK.ISO8859-2',
'slovene': 'sl_SI.ISO8859-2',
'slovenian': 'sl_SI.ISO8859-2',
'sp': 'sr_CS.ISO8859-5',
'sp_yu': 'sr_CS.ISO8859-5',
'spanish': 'es_ES.ISO8859-1',
- 'spanish.iso88591': 'es_ES.ISO8859-1',
'spanish_spain': 'es_ES.ISO8859-1',
- 'spanish_spain.8859': 'es_ES.ISO8859-1',
'sq': 'sq_AL.ISO8859-2',
'sq_al': 'sq_AL.ISO8859-2',
- 'sq_al.iso88592': 'sq_AL.ISO8859-2',
'sr': 'sr_RS.UTF-8',
'sr@cyrillic': 'sr_RS.UTF-8',
- 'sr@latin': 'sr_RS.UTF-8@latin',
'sr@latn': 'sr_CS.UTF-8@latin',
'sr_cs': 'sr_CS.UTF-8',
- 'sr_cs.iso88592': 'sr_CS.ISO8859-2',
'sr_cs.iso88592@latn': 'sr_CS.ISO8859-2',
- 'sr_cs.iso88595': 'sr_CS.ISO8859-5',
- 'sr_cs.utf8@latn': 'sr_CS.UTF-8@latin',
'sr_cs@latn': 'sr_CS.UTF-8@latin',
'sr_me': 'sr_ME.UTF-8',
'sr_rs': 'sr_RS.UTF-8',
- 'sr_rs.utf8@latn': 'sr_RS.UTF-8@latin',
- 'sr_rs@latin': 'sr_RS.UTF-8@latin',
'sr_rs@latn': 'sr_RS.UTF-8@latin',
'sr_sp': 'sr_CS.ISO8859-2',
'sr_yu': 'sr_RS.UTF-8@latin',
@@ -1540,29 +1187,15 @@ locale_alias = {
'sr_yu.iso88595': 'sr_CS.ISO8859-5',
'sr_yu.iso88595@cyrillic': 'sr_CS.ISO8859-5',
'sr_yu.microsoftcp1251@cyrillic': 'sr_CS.CP1251',
- 'sr_yu.utf8@cyrillic': 'sr_RS.UTF-8',
'sr_yu@cyrillic': 'sr_RS.UTF-8',
'ss': 'ss_ZA.ISO8859-1',
'ss_za': 'ss_ZA.ISO8859-1',
- 'ss_za.iso88591': 'ss_ZA.ISO8859-1',
'st': 'st_ZA.ISO8859-1',
'st_za': 'st_ZA.ISO8859-1',
- 'st_za.iso88591': 'st_ZA.ISO8859-1',
'sv': 'sv_SE.ISO8859-1',
- 'sv.iso885915': 'sv_SE.ISO8859-15',
'sv_fi': 'sv_FI.ISO8859-1',
- 'sv_fi.iso88591': 'sv_FI.ISO8859-1',
- 'sv_fi.iso885915': 'sv_FI.ISO8859-15',
- 'sv_fi.iso885915@euro': 'sv_FI.ISO8859-15',
- 'sv_fi.utf8@euro': 'sv_FI.UTF-8',
- 'sv_fi@euro': 'sv_FI.ISO8859-15',
'sv_se': 'sv_SE.ISO8859-1',
- 'sv_se.88591': 'sv_SE.ISO8859-1',
- 'sv_se.iso88591': 'sv_SE.ISO8859-1',
- 'sv_se.iso885915': 'sv_SE.ISO8859-15',
- 'sv_se@euro': 'sv_SE.ISO8859-15',
'swedish': 'sv_SE.ISO8859-1',
- 'swedish.iso88591': 'sv_SE.ISO8859-1',
'ta': 'ta_IN.TSCII-0',
'ta_in': 'ta_IN.TSCII-0',
'ta_in.tscii': 'ta_IN.TSCII-0',
@@ -1570,49 +1203,33 @@ locale_alias = {
'te': 'te_IN.UTF-8',
'tg': 'tg_TJ.KOI8-C',
'tg_tj': 'tg_TJ.KOI8-C',
- 'tg_tj.koi8c': 'tg_TJ.KOI8-C',
'th': 'th_TH.ISO8859-11',
'th_th': 'th_TH.ISO8859-11',
- 'th_th.iso885911': 'th_TH.ISO8859-11',
'th_th.tactis': 'th_TH.TIS620',
'th_th.tis620': 'th_TH.TIS620',
'thai': 'th_TH.ISO8859-11',
'tl': 'tl_PH.ISO8859-1',
'tl_ph': 'tl_PH.ISO8859-1',
- 'tl_ph.iso88591': 'tl_PH.ISO8859-1',
'tn': 'tn_ZA.ISO8859-15',
'tn_za': 'tn_ZA.ISO8859-15',
- 'tn_za.iso885915': 'tn_ZA.ISO8859-15',
'tr': 'tr_TR.ISO8859-9',
'tr_tr': 'tr_TR.ISO8859-9',
- 'tr_tr.iso88599': 'tr_TR.ISO8859-9',
'ts': 'ts_ZA.ISO8859-1',
'ts_za': 'ts_ZA.ISO8859-1',
- 'ts_za.iso88591': 'ts_ZA.ISO8859-1',
'tt': 'tt_RU.TATAR-CYR',
'tt_ru': 'tt_RU.TATAR-CYR',
- 'tt_ru.koi8c': 'tt_RU.KOI8-C',
'tt_ru.tatarcyr': 'tt_RU.TATAR-CYR',
'turkish': 'tr_TR.ISO8859-9',
- 'turkish.iso88599': 'tr_TR.ISO8859-9',
'uk': 'uk_UA.KOI8-U',
'uk_ua': 'uk_UA.KOI8-U',
- 'uk_ua.cp1251': 'uk_UA.CP1251',
- 'uk_ua.iso88595': 'uk_UA.ISO8859-5',
- 'uk_ua.koi8u': 'uk_UA.KOI8-U',
- 'uk_ua.microsoftcp1251': 'uk_UA.CP1251',
'univ': 'en_US.utf',
'universal': 'en_US.utf',
'universal.utf8@ucs4': 'en_US.UTF-8',
'ur': 'ur_PK.CP1256',
'ur_in': 'ur_IN.UTF-8',
'ur_pk': 'ur_PK.CP1256',
- 'ur_pk.cp1256': 'ur_PK.CP1256',
- 'ur_pk.microsoftcp1256': 'ur_PK.CP1256',
'uz': 'uz_UZ.UTF-8',
'uz_uz': 'uz_UZ.UTF-8',
- 'uz_uz.iso88591': 'uz_UZ.ISO8859-1',
- 'uz_uz.utf8@cyrillic': 'uz_UZ.UTF-8',
'uz_uz@cyrillic': 'uz_UZ.UTF-8',
've': 've_ZA.UTF-8',
've_za': 've_ZA.UTF-8',
@@ -1624,35 +1241,21 @@ locale_alias = {
'vi_vn.viscii111': 'vi_VN.VISCII',
'wa': 'wa_BE.ISO8859-1',
'wa_be': 'wa_BE.ISO8859-1',
- 'wa_be.iso88591': 'wa_BE.ISO8859-1',
- 'wa_be.iso885915': 'wa_BE.ISO8859-15',
- 'wa_be.iso885915@euro': 'wa_BE.ISO8859-15',
- 'wa_be@euro': 'wa_BE.ISO8859-15',
'xh': 'xh_ZA.ISO8859-1',
'xh_za': 'xh_ZA.ISO8859-1',
- 'xh_za.iso88591': 'xh_ZA.ISO8859-1',
'yi': 'yi_US.CP1255',
'yi_us': 'yi_US.CP1255',
- 'yi_us.cp1255': 'yi_US.CP1255',
- 'yi_us.microsoftcp1255': 'yi_US.CP1255',
'zh': 'zh_CN.eucCN',
'zh_cn': 'zh_CN.gb2312',
'zh_cn.big5': 'zh_TW.big5',
'zh_cn.euc': 'zh_CN.eucCN',
- 'zh_cn.gb18030': 'zh_CN.gb18030',
- 'zh_cn.gb2312': 'zh_CN.gb2312',
- 'zh_cn.gbk': 'zh_CN.gbk',
'zh_hk': 'zh_HK.big5hkscs',
- 'zh_hk.big5': 'zh_HK.big5',
'zh_hk.big5hk': 'zh_HK.big5hkscs',
- 'zh_hk.big5hkscs': 'zh_HK.big5hkscs',
'zh_tw': 'zh_TW.big5',
- 'zh_tw.big5': 'zh_TW.big5',
'zh_tw.euc': 'zh_TW.eucTW',
'zh_tw.euctw': 'zh_TW.eucTW',
'zu': 'zu_ZA.ISO8859-1',
'zu_za': 'zu_ZA.ISO8859-1',
- 'zu_za.iso88591': 'zu_ZA.ISO8859-1',
}
#
diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py
index 9f436f3..dcfd9f6 100644
--- a/Lib/logging/__init__.py
+++ b/Lib/logging/__init__.py
@@ -1,4 +1,4 @@
-# Copyright 2001-2013 by Vinay Sajip. All Rights Reserved.
+# Copyright 2001-2014 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,
@@ -18,12 +18,13 @@
Logging package for Python. Based on PEP 282 and comments thereto in
comp.lang.python.
-Copyright (C) 2001-2013 Vinay Sajip. All Rights Reserved.
+Copyright (C) 2001-2014 Vinay Sajip. All Rights Reserved.
To use, simply 'import logging' and log away!
"""
-import sys, os, time, io, traceback, warnings, weakref
+import sys, os, time, io, traceback, warnings, weakref, collections
+
from string import Template
__all__ = ['BASIC_FORMAT', 'BufferingFormatter', 'CRITICAL', 'DEBUG', 'ERROR',
@@ -42,6 +43,7 @@ except ImportError: #pragma: no cover
__author__ = "Vinay Sajip <vinay_sajip@red-dove.com>"
__status__ = "production"
+# The following module attributes are no longer updated.
__version__ = "0.5.1.2"
__date__ = "07 February 2010"
@@ -67,7 +69,7 @@ else: #pragma: no cover
"""Return the frame object for the caller's stack frame."""
try:
raise Exception
- except:
+ except Exception:
return sys.exc_info()[2].tb_frame.f_back
# _srcfile is only used in conjunction with sys._getframe().
@@ -123,20 +125,22 @@ INFO = 20
DEBUG = 10
NOTSET = 0
-_levelNames = {
- CRITICAL : 'CRITICAL',
- ERROR : 'ERROR',
- WARNING : 'WARNING',
- INFO : 'INFO',
- DEBUG : 'DEBUG',
- NOTSET : 'NOTSET',
- 'CRITICAL' : CRITICAL,
- 'ERROR' : ERROR,
- 'WARN' : WARNING,
- 'WARNING' : WARNING,
- 'INFO' : INFO,
- 'DEBUG' : DEBUG,
- 'NOTSET' : NOTSET,
+_levelToName = {
+ CRITICAL: 'CRITICAL',
+ ERROR: 'ERROR',
+ WARNING: 'WARNING',
+ INFO: 'INFO',
+ DEBUG: 'DEBUG',
+ NOTSET: 'NOTSET',
+}
+_nameToLevel = {
+ 'CRITICAL': CRITICAL,
+ 'ERROR': ERROR,
+ 'WARN': WARNING,
+ 'WARNING': WARNING,
+ 'INFO': INFO,
+ 'DEBUG': DEBUG,
+ 'NOTSET': NOTSET,
}
def getLevelName(level):
@@ -153,7 +157,7 @@ def getLevelName(level):
Otherwise, the string "Level %s" % level is returned.
"""
- return _levelNames.get(level, ("Level %s" % level))
+ return _levelToName.get(level, ("Level %s" % level))
def addLevelName(level, levelName):
"""
@@ -163,8 +167,8 @@ def addLevelName(level, levelName):
"""
_acquireLock()
try: #unlikely to cause an exception, but you never know...
- _levelNames[level] = levelName
- _levelNames[levelName] = level
+ _levelToName[level] = levelName
+ _nameToLevel[levelName] = level
finally:
_releaseLock()
@@ -172,9 +176,9 @@ def _checkLevel(level):
if isinstance(level, int):
rv = level
elif str(level) == level:
- if level not in _levelNames:
+ if level not in _nameToLevel:
raise ValueError("Unknown level: %r" % level)
- rv = _levelNames[level]
+ rv = _nameToLevel[level]
else:
raise TypeError("Level not an integer or a valid string: %r" % level)
return rv
@@ -250,7 +254,13 @@ class LogRecord(object):
# 'Value is %d' instead of 'Value is 0'.
# For the use case of passing a dictionary, this should not be a
# problem.
- if args and len(args) == 1 and isinstance(args[0], dict) and args[0]:
+ # Issue #21172: a request was made to relax the isinstance check
+ # to hasattr(args[0], '__getitem__'). However, the docs on string
+ # formatting still seem to suggest a mapping object is required.
+ # Thus, while not removing the isinstance check, it does now look
+ # for collections.Mapping rather than, as before, dict.
+ if (args and len(args) == 1 and isinstance(args[0], collections.Mapping)
+ and args[0]):
args = args[0]
self.args = args
self.levelname = getLevelName(level)
@@ -708,16 +718,17 @@ def _removeHandlerRef(wr):
Remove a handler reference from the internal cleanup list.
"""
# This function can be called during module teardown, when globals are
- # set to None. If _acquireLock is None, assume this is the case and do
- # nothing.
- if (_acquireLock is not None and _handlerList is not None and
- _releaseLock is not None):
- _acquireLock()
+ # set to None. It can also be called from another thread. So we need to
+ # pre-emptively grab the necessary globals and check if they're None,
+ # to prevent race conditions and failures during interpreter shutdown.
+ acquire, release, handlers = _acquireLock, _releaseLock, _handlerList
+ if acquire and release and handlers:
+ acquire()
try:
- if wr in _handlerList:
- _handlerList.remove(wr)
+ if wr in handlers:
+ handlers.remove(wr)
finally:
- _releaseLock()
+ release()
def _addHandlerRef(handler):
"""
@@ -882,16 +893,37 @@ class Handler(Filterer):
The record which was being processed is passed in to this method.
"""
if raiseExceptions and sys.stderr: # see issue 13807
- ei = sys.exc_info()
+ t, v, tb = sys.exc_info()
try:
- traceback.print_exception(ei[0], ei[1], ei[2],
- None, sys.stderr)
- sys.stderr.write('Logged from file %s, line %s\n' % (
- record.filename, record.lineno))
- except IOError: #pragma: no cover
+ sys.stderr.write('--- Logging error ---\n')
+ traceback.print_exception(t, v, tb, None, sys.stderr)
+ sys.stderr.write('Call stack:\n')
+ # Walk the stack frame up until we're out of logging,
+ # so as to print the calling context.
+ frame = tb.tb_frame
+ while (frame and os.path.dirname(frame.f_code.co_filename) ==
+ __path__[0]):
+ frame = frame.f_back
+ if frame:
+ traceback.print_stack(frame, file=sys.stderr)
+ else:
+ # couldn't find the right stack frame, for some reason
+ sys.stderr.write('Logged from file %s, line %s\n' % (
+ record.filename, record.lineno))
+ # Issue 18671: output logging message and arguments
+ try:
+ sys.stderr.write('Message: %r\n'
+ 'Arguments: %s\n' % (record.msg,
+ record.args))
+ except Exception:
+ sys.stderr.write('Unable to print the message and arguments'
+ ' - possible formatting error.\nUse the'
+ ' traceback above to help find the error.\n'
+ )
+ except OSError: #pragma: no cover
pass # see issue 5971
finally:
- del ei
+ del t, v, tb
class StreamHandler(Handler):
"""
@@ -941,9 +973,7 @@ class StreamHandler(Handler):
stream.write(msg)
stream.write(self.terminator)
self.flush()
- except (KeyboardInterrupt, SystemExit): #pragma: no cover
- raise
- except:
+ except Exception:
self.handleError(record)
class FileHandler(StreamHandler):
@@ -1836,7 +1866,7 @@ def shutdown(handlerList=_handlerList):
h.acquire()
h.flush()
h.close()
- except (IOError, ValueError):
+ except (OSError, ValueError):
# Ignore errors which might be caused
# because handlers have been closed but
# references to them are still around at
@@ -1844,7 +1874,7 @@ def shutdown(handlerList=_handlerList):
pass
finally:
h.release()
- except:
+ except: # ignore everything, as we're shutting down
if raiseExceptions:
raise
#else, swallow
diff --git a/Lib/logging/config.py b/Lib/logging/config.py
index 1880614..895fb26 100644
--- a/Lib/logging/config.py
+++ b/Lib/logging/config.py
@@ -1,4 +1,4 @@
-# Copyright 2001-2013 by Vinay Sajip. All Rights Reserved.
+# Copyright 2001-2014 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,
@@ -19,13 +19,19 @@ Configuration functions for the logging package for Python. The core package
is based on PEP 282 and comments thereto in comp.lang.python, and influenced
by Apache's log4j system.
-Copyright (C) 2001-2013 Vinay Sajip. All Rights Reserved.
+Copyright (C) 2001-2014 Vinay Sajip. All Rights Reserved.
To use, simply 'import logging' and log away!
"""
-import sys, logging, logging.handlers, socket, struct, traceback, re
+import errno
import io
+import logging
+import logging.handlers
+import re
+import struct
+import sys
+import traceback
try:
import _thread as thread
@@ -38,10 +44,7 @@ from socketserver import ThreadingTCPServer, StreamRequestHandler
DEFAULT_LOGGING_CONFIG_PORT = 9030
-if sys.platform == "win32":
- RESET_ERROR = 10054 #WSAECONNRESET
-else:
- RESET_ERROR = 104 #ECONNRESET
+RESET_ERROR = errno.ECONNRESET
#
# The following code implements a socket listener for on-the-fly
@@ -61,11 +64,14 @@ def fileConfig(fname, defaults=None, disable_existing_loggers=True):
"""
import configparser
- cp = configparser.ConfigParser(defaults)
- if hasattr(fname, 'readline'):
- cp.read_file(fname)
+ if isinstance(fname, configparser.RawConfigParser):
+ cp = fname
else:
- cp.read(fname)
+ cp = configparser.ConfigParser(defaults)
+ if hasattr(fname, 'readline'):
+ cp.read_file(fname)
+ else:
+ cp.read(fname)
formatters = _create_formatters(cp)
@@ -141,7 +147,7 @@ def _install_handlers(cp, formatters):
h = klass(*args)
if "level" in section:
level = section["level"]
- h.setLevel(logging._levelNames[level])
+ h.setLevel(level)
if len(fmt):
h.setFormatter(formatters[fmt])
if issubclass(klass, logging.handlers.MemoryHandler):
@@ -188,7 +194,7 @@ def _install_loggers(cp, handlers, disable_existing):
log = root
if "level" in section:
level = section["level"]
- log.setLevel(logging._levelNames[level])
+ log.setLevel(level)
for h in root.handlers[:]:
root.removeHandler(h)
hlist = section["handlers"]
@@ -234,7 +240,7 @@ def _install_loggers(cp, handlers, disable_existing):
existing.remove(qn)
if "level" in section:
level = section["level"]
- logger.setLevel(logging._levelNames[level])
+ logger.setLevel(level)
for h in logger.handlers[:]:
logger.removeHandler(h)
logger.propagate = propagate
@@ -271,6 +277,30 @@ def valid_ident(s):
return True
+class ConvertingMixin(object):
+ """For ConvertingXXX's, this mixin class provides common functions"""
+
+ def convert_with_key(self, key, value, replace=True):
+ result = self.configurator.convert(value)
+ #If the converted value is different, save for next time
+ if value is not result:
+ if replace:
+ self[key] = result
+ if type(result) in (ConvertingDict, ConvertingList,
+ ConvertingTuple):
+ result.parent = self
+ result.key = key
+ return result
+
+ def convert(self, value):
+ result = self.configurator.convert(value)
+ if value is not result:
+ if type(result) in (ConvertingDict, ConvertingList,
+ ConvertingTuple):
+ result.parent = self
+ return result
+
+
# The ConvertingXXX classes are wrappers around standard Python containers,
# and they serve to convert any suitable values in the container. The
# conversion converts base dicts, lists and tuples to their wrapped
@@ -280,77 +310,37 @@ def valid_ident(s):
# Each wrapper should have a configurator attribute holding the actual
# configurator to use for conversion.
-class ConvertingDict(dict):
+class ConvertingDict(dict, ConvertingMixin):
"""A converting dictionary wrapper."""
def __getitem__(self, key):
value = dict.__getitem__(self, key)
- result = self.configurator.convert(value)
- #If the converted value is different, save for next time
- if value is not result:
- self[key] = result
- if type(result) in (ConvertingDict, ConvertingList,
- ConvertingTuple):
- result.parent = self
- result.key = key
- return result
+ return self.convert_with_key(key, value)
def get(self, key, default=None):
value = dict.get(self, key, default)
- result = self.configurator.convert(value)
- #If the converted value is different, save for next time
- if value is not result:
- self[key] = result
- if type(result) in (ConvertingDict, ConvertingList,
- ConvertingTuple):
- result.parent = self
- result.key = key
- return result
+ return self.convert_with_key(key, value)
def pop(self, key, default=None):
value = dict.pop(self, key, default)
- result = self.configurator.convert(value)
- if value is not result:
- if type(result) in (ConvertingDict, ConvertingList,
- ConvertingTuple):
- result.parent = self
- result.key = key
- return result
+ return self.convert_with_key(key, value, replace=False)
-class ConvertingList(list):
+class ConvertingList(list, ConvertingMixin):
"""A converting list wrapper."""
def __getitem__(self, key):
value = list.__getitem__(self, key)
- result = self.configurator.convert(value)
- #If the converted value is different, save for next time
- if value is not result:
- self[key] = result
- if type(result) in (ConvertingDict, ConvertingList,
- ConvertingTuple):
- result.parent = self
- result.key = key
- return result
+ return self.convert_with_key(key, value)
def pop(self, idx=-1):
value = list.pop(self, idx)
- result = self.configurator.convert(value)
- if value is not result:
- if type(result) in (ConvertingDict, ConvertingList,
- ConvertingTuple):
- result.parent = self
- return result
+ return self.convert(value)
-class ConvertingTuple(tuple):
+class ConvertingTuple(tuple, ConvertingMixin):
"""A converting tuple wrapper."""
def __getitem__(self, key):
value = tuple.__getitem__(self, key)
- result = self.configurator.convert(value)
- if value is not result:
- if type(result) in (ConvertingDict, ConvertingList,
- ConvertingTuple):
- result.parent = self
- result.key = key
- return result
+ # Can't replace a tuple entry.
+ return self.convert_with_key(key, value, replace=False)
class BaseConfigurator(object):
"""
@@ -729,6 +719,7 @@ class DictConfigurator(BaseConfigurator):
'address' in config:
config['address'] = self.as_tuple(config['address'])
factory = klass
+ props = config.pop('.', None)
kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
try:
result = factory(**kwargs)
@@ -747,6 +738,9 @@ class DictConfigurator(BaseConfigurator):
result.setLevel(logging._checkLevel(level))
if filters:
self.add_filters(result, filters)
+ if props:
+ for name, value in props.items():
+ setattr(result, name, value)
return result
def add_handlers(self, logger, handlers):
@@ -795,7 +789,7 @@ def dictConfig(config):
dictConfigClass(config).configure()
-def listen(port=DEFAULT_LOGGING_CONFIG_PORT):
+def listen(port=DEFAULT_LOGGING_CONFIG_PORT, verify=None):
"""
Start up a socket server on the specified port, and listen for new
configurations.
@@ -804,6 +798,15 @@ def listen(port=DEFAULT_LOGGING_CONFIG_PORT):
Returns a Thread object on which you can call start() to start the server,
and which you can join() when appropriate. To stop the server, call
stopListening().
+
+ Use the ``verify`` argument to verify any bytes received across the wire
+ from a client. If specified, it should be a callable which receives a
+ single argument - the bytes of configuration data received across the
+ network - and it should return either ``None``, to indicate that the
+ passed in bytes could not be verified and should be discarded, or a
+ byte string which is then passed to the configuration machinery as
+ normal. Note that you can return transformed bytes, e.g. by decrypting
+ the bytes passed in.
"""
if not thread: #pragma: no cover
raise NotImplementedError("listen() needs threading to work")
@@ -831,31 +834,28 @@ def listen(port=DEFAULT_LOGGING_CONFIG_PORT):
chunk = self.connection.recv(slen)
while len(chunk) < slen:
chunk = chunk + conn.recv(slen - len(chunk))
- chunk = chunk.decode("utf-8")
- try:
- import json
- d =json.loads(chunk)
- assert isinstance(d, dict)
- dictConfig(d)
- except:
- #Apply new configuration.
-
- file = io.StringIO(chunk)
+ if self.server.verify is not None:
+ chunk = self.server.verify(chunk)
+ if chunk is not None: # verified, can process
+ chunk = chunk.decode("utf-8")
try:
- fileConfig(file)
- except (KeyboardInterrupt, SystemExit): #pragma: no cover
- raise
- except:
- traceback.print_exc()
+ import json
+ d =json.loads(chunk)
+ assert isinstance(d, dict)
+ dictConfig(d)
+ except Exception:
+ #Apply new configuration.
+
+ file = io.StringIO(chunk)
+ try:
+ fileConfig(file)
+ except Exception:
+ traceback.print_exc()
if self.server.ready:
self.server.ready.set()
- except socket.error as e:
- if not isinstance(e.args, tuple):
+ except OSError as e:
+ if e.errno != RESET_ERROR:
raise
- else:
- errcode = e.args[0]
- if errcode != RESET_ERROR:
- raise
class ConfigSocketReceiver(ThreadingTCPServer):
"""
@@ -865,13 +865,14 @@ def listen(port=DEFAULT_LOGGING_CONFIG_PORT):
allow_reuse_address = 1
def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
- handler=None, ready=None):
+ handler=None, ready=None, verify=None):
ThreadingTCPServer.__init__(self, (host, port), handler)
logging._acquireLock()
self.abort = 0
logging._releaseLock()
self.timeout = 1
self.ready = ready
+ self.verify = verify
def serve_until_stopped(self):
import select
@@ -889,16 +890,18 @@ def listen(port=DEFAULT_LOGGING_CONFIG_PORT):
class Server(threading.Thread):
- def __init__(self, rcvr, hdlr, port):
+ def __init__(self, rcvr, hdlr, port, verify):
super(Server, self).__init__()
self.rcvr = rcvr
self.hdlr = hdlr
self.port = port
+ self.verify = verify
self.ready = threading.Event()
def run(self):
server = self.rcvr(port=self.port, handler=self.hdlr,
- ready=self.ready)
+ ready=self.ready,
+ verify=self.verify)
if self.port == 0:
self.port = server.server_address[1]
self.ready.set()
@@ -908,7 +911,7 @@ def listen(port=DEFAULT_LOGGING_CONFIG_PORT):
logging._releaseLock()
server.serve_until_stopped()
- return Server(ConfigSocketReceiver, ConfigStreamHandler, port)
+ return Server(ConfigSocketReceiver, ConfigStreamHandler, port, verify)
def stopListening():
"""
diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py
index ddec7dd..f547d17 100644
--- a/Lib/logging/handlers.py
+++ b/Lib/logging/handlers.py
@@ -23,8 +23,7 @@ Copyright (C) 2001-2013 Vinay Sajip. All Rights Reserved.
To use, simply 'import logging.handlers' and log away!
"""
-import errno, logging, socket, os, pickle, struct, time, re
-from codecs import BOM_UTF8
+import logging, socket, os, pickle, struct, time, re
from stat import ST_DEV, ST_INO, ST_MTIME
import queue
try:
@@ -72,9 +71,7 @@ class BaseRotatingHandler(logging.FileHandler):
if self.shouldRollover(record):
self.doRollover()
logging.FileHandler.emit(self, record)
- except (KeyboardInterrupt, SystemExit): #pragma: no cover
- raise
- except:
+ except Exception:
self.handleError(record)
def rotation_filename(self, default_name):
@@ -201,11 +198,12 @@ class TimedRotatingFileHandler(BaseRotatingHandler):
If backupCount is > 0, when rollover is done, no more than backupCount
files are kept - the oldest ones are deleted.
"""
- def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False):
+ def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False, atTime=None):
BaseRotatingHandler.__init__(self, filename, 'a', encoding, delay)
self.when = when.upper()
self.backupCount = backupCount
self.utc = utc
+ self.atTime = atTime
# Calculate the real rollover interval, which is just the number of
# seconds between rollovers. Also set the filename suffix used when
# a rollover occurs. Current 'when' events supported:
@@ -275,9 +273,22 @@ class TimedRotatingFileHandler(BaseRotatingHandler):
currentHour = t[3]
currentMinute = t[4]
currentSecond = t[5]
- # r is the number of seconds left between now and midnight
- r = _MIDNIGHT - ((currentHour * 60 + currentMinute) * 60 +
- currentSecond)
+ currentDay = t[6]
+ # r is the number of seconds left between now and the next rotation
+ if self.atTime is None:
+ rotate_ts = _MIDNIGHT
+ else:
+ rotate_ts = ((self.atTime.hour * 60 + self.atTime.minute)*60 +
+ self.atTime.second)
+
+ r = rotate_ts - ((currentHour * 60 + currentMinute) * 60 +
+ currentSecond)
+ if r < 0:
+ # Rotate time is before the current time (for example when
+ # self.rotateAt is 13:45 and it now 14:15), rotation is
+ # tomorrow.
+ r += _MIDNIGHT
+ currentDay = (currentDay + 1) % 7
result = currentTime + r
# If we are rolling over on a certain day, add in the number of days until
# the next rollover, but offset by 1 since we just calculated the time
@@ -295,7 +306,7 @@ class TimedRotatingFileHandler(BaseRotatingHandler):
# This is because the above time calculation takes us to midnight on this
# day, i.e. the start of the next day.
if self.when.startswith('W'):
- day = t[6] # 0 is Monday
+ day = currentDay # 0 is Monday
if day != self.dayOfWeek:
if day < self.dayOfWeek:
daysToWait = self.dayOfWeek - day
@@ -444,11 +455,8 @@ class WatchedFileHandler(logging.FileHandler):
try:
# stat the file by path, checking for existence
sres = os.stat(self.baseFilename)
- except OSError as err:
- if err.errno == errno.ENOENT:
- sres = None
- else:
- raise
+ except FileNotFoundError:
+ sres = None
# compare file system stat with that of our stream file handle
if not sres or sres[ST_DEV] != self.dev or sres[ST_INO] != self.ino:
if self.stream is not None:
@@ -485,6 +493,10 @@ class SocketHandler(logging.Handler):
logging.Handler.__init__(self)
self.host = host
self.port = port
+ if port is None:
+ self.address = host
+ else:
+ self.address = (host, port)
self.sock = None
self.closeOnError = False
self.retryTime = None
@@ -500,15 +512,17 @@ class SocketHandler(logging.Handler):
A factory method which allows subclasses to define the precise
type of socket they want.
"""
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- if hasattr(s, 'settimeout'):
- s.settimeout(timeout)
- try:
- s.connect((self.host, self.port))
- return s
- except socket.error:
- s.close()
- raise
+ if self.port is not None:
+ result = socket.create_connection(self.address, timeout=timeout)
+ else:
+ result = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ result.settimeout(timeout)
+ try:
+ result.connect(self.address)
+ except OSError:
+ result.close() # Issue 19182
+ raise
+ return result
def createSocket(self):
"""
@@ -528,7 +542,7 @@ class SocketHandler(logging.Handler):
try:
self.sock = self.makeSocket()
self.retryTime = None # next time, no delay before trying
- except socket.error:
+ except OSError:
#Creation failed, so set the retry time and return.
if self.retryTime is None:
self.retryPeriod = self.retryStart
@@ -552,16 +566,8 @@ class SocketHandler(logging.Handler):
#but are still unable to connect.
if self.sock:
try:
- if hasattr(self.sock, "sendall"):
- self.sock.sendall(s)
- 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: #pragma: no cover
+ self.sock.sendall(s)
+ except OSError: #pragma: no cover
self.sock.close()
self.sock = None # so we can call createSocket next time
@@ -611,9 +617,7 @@ class SocketHandler(logging.Handler):
try:
s = self.makePickle(record)
self.send(s)
- except (KeyboardInterrupt, SystemExit): #pragma: no cover
- raise
- except:
+ except Exception:
self.handleError(record)
def close(self):
@@ -652,7 +656,11 @@ class DatagramHandler(SocketHandler):
The factory method of SocketHandler is here overridden to create
a UDP socket (SOCK_DGRAM).
"""
- s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ if self.port is None:
+ family = socket.AF_UNIX
+ else:
+ family = socket.AF_INET
+ s = socket.socket(family, socket.SOCK_DGRAM)
return s
def send(self, s):
@@ -665,7 +673,7 @@ class DatagramHandler(SocketHandler):
"""
if self.sock is None:
self.createSocket()
- self.sock.sendto(s, (self.host, self.port))
+ self.sock.sendto(s, self.address)
class SysLogHandler(logging.Handler):
"""
@@ -777,7 +785,11 @@ class SysLogHandler(logging.Handler):
If address is specified as a string, a UNIX socket is used. To log to a
local syslogd, "SysLogHandler(address="/dev/log")" can be used.
- If facility is not specified, LOG_USER is used.
+ If facility is not specified, LOG_USER is used. If socktype is
+ specified as socket.SOCK_DGRAM or socket.SOCK_STREAM, that specific
+ socket type will be used. For Unix sockets, you can also specify a
+ socktype of None, in which case socket.SOCK_DGRAM will be used, falling
+ back to socket.SOCK_STREAM.
"""
logging.Handler.__init__(self)
@@ -807,7 +819,7 @@ class SysLogHandler(logging.Handler):
self.socket.connect(address)
# it worked, so set self.socktype to the used type
self.socktype = use_socktype
- except socket.error:
+ except OSError:
self.socket.close()
if self.socktype is not None:
# user didn't specify falling back, so fail
@@ -818,7 +830,7 @@ class SysLogHandler(logging.Handler):
self.socket.connect(address)
# it worked, so set self.socktype to the used type
self.socktype = use_socktype
- except socket.error:
+ except OSError:
self.socket.close()
raise
@@ -871,10 +883,9 @@ class SysLogHandler(logging.Handler):
msg = self.ident + msg
if self.append_nul:
msg += '\000'
- """
- We need to convert record level to lowercase, maybe this will
- change in the future.
- """
+
+ # We need to convert record level to lowercase, maybe this will
+ # change in the future.
prio = '<%d>' % self.encodePriority(self.facility,
self.mapPriority(record.levelname))
prio = prio.encode('utf-8')
@@ -885,7 +896,7 @@ class SysLogHandler(logging.Handler):
if self.unixsocket:
try:
self.socket.send(msg)
- except socket.error:
+ except OSError:
self.socket.close()
self._connect_unixsocket(self.address)
self.socket.send(msg)
@@ -893,9 +904,7 @@ class SysLogHandler(logging.Handler):
self.socket.sendto(msg, self.address)
else:
self.socket.sendall(msg)
- except (KeyboardInterrupt, SystemExit): #pragma: no cover
- raise
- except:
+ except Exception:
self.handleError(record)
class SMTPHandler(logging.Handler):
@@ -973,9 +982,7 @@ class SMTPHandler(logging.Handler):
smtp.login(self.username, self.password)
smtp.sendmail(self.fromaddr, self.toaddrs, msg)
smtp.quit()
- except (KeyboardInterrupt, SystemExit): #pragma: no cover
- raise
- except:
+ except Exception:
self.handleError(record)
class NTEventLogHandler(logging.Handler):
@@ -1060,9 +1067,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): #pragma: no cover
- raise
- except:
+ except Exception:
self.handleError(record)
def close(self):
@@ -1147,9 +1152,7 @@ class HTTPHandler(logging.Handler):
if self.method == "POST":
h.send(data.encode('utf-8'))
h.getresponse() #can't do anything with the result
- except (KeyboardInterrupt, SystemExit): #pragma: no cover
- raise
- except:
+ except Exception:
self.handleError(record)
class BufferingHandler(logging.Handler):
@@ -1329,9 +1332,7 @@ class QueueHandler(logging.Handler):
"""
try:
self.enqueue(self.prepare(record))
- except (KeyboardInterrupt, SystemExit): #pragma: no cover
- raise
- except:
+ except Exception:
self.handleError(record)
if threading:
diff --git a/Lib/lzma.py b/Lib/lzma.py
index 1a89887..f1d3958 100644
--- a/Lib/lzma.py
+++ b/Lib/lzma.py
@@ -54,9 +54,9 @@ class LZMAFile(io.BufferedIOBase):
bytes object), in which case the named file is opened, or it can
be an existing file object to read from or write to.
- mode can be "r" for reading (default), "w" for (over)writing, or
- "a" for appending. These can equivalently be given as "rb", "wb",
- and "ab" respectively.
+ mode can be "r" for reading (default), "w" for (over)writing,
+ "x" for creating exclusively, or "a" for appending. These can
+ equivalently be given as "rb", "wb", "xb" and "ab" respectively.
format specifies the container format to use for the file.
If mode is "r", this defaults to FORMAT_AUTO. Otherwise, the
@@ -110,8 +110,9 @@ class LZMAFile(io.BufferedIOBase):
# 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", "wb", "a", "ab"):
+ self._buffer = b""
+ self._buffer_offset = 0
+ elif mode in ("w", "wb", "a", "ab", "x", "xb"):
if format is None:
format = FORMAT_XZ
mode_code = _MODE_WRITE
@@ -143,7 +144,7 @@ class LZMAFile(io.BufferedIOBase):
try:
if self._mode in (_MODE_READ, _MODE_READ_EOF):
self._decompressor = None
- self._buffer = None
+ self._buffer = b""
elif self._mode == _MODE_WRITE:
self._fp.write(self._compressor.flush())
self._compressor = None
@@ -187,15 +188,18 @@ class LZMAFile(io.BufferedIOBase):
raise ValueError("I/O operation on closed file")
def _check_can_read(self):
- if not self.readable():
+ if self._mode not in (_MODE_READ, _MODE_READ_EOF):
+ self._check_not_closed()
raise io.UnsupportedOperation("File not open for reading")
def _check_can_write(self):
- if not self.writable():
+ if self._mode != _MODE_WRITE:
+ self._check_not_closed()
raise io.UnsupportedOperation("File not open for writing")
def _check_can_seek(self):
- if not self.readable():
+ if self._mode not in (_MODE_READ, _MODE_READ_EOF):
+ self._check_not_closed()
raise io.UnsupportedOperation("Seeking is only supported "
"on files open for reading")
if not self._fp.seekable():
@@ -204,16 +208,13 @@ class LZMAFile(io.BufferedIOBase):
# Fill the readahead buffer if it is empty. Returns False on EOF.
def _fill_buffer(self):
+ if self._mode == _MODE_READ_EOF:
+ return False
# Depending on the input data, our call to the decompressor may not
# return any data. In this case, try again after reading another block.
- while True:
- if self._buffer:
- return True
-
- if self._decompressor.unused_data:
- rawblock = self._decompressor.unused_data
- else:
- rawblock = self._fp.read(_BUFFER_SIZE)
+ while self._buffer_offset == len(self._buffer):
+ rawblock = (self._decompressor.unused_data or
+ self._fp.read(_BUFFER_SIZE))
if not rawblock:
if self._decompressor.eof:
@@ -236,30 +237,48 @@ class LZMAFile(io.BufferedIOBase):
return False
else:
self._buffer = self._decompressor.decompress(rawblock)
+ self._buffer_offset = 0
+ return True
# Read data until EOF.
# If return_data is false, consume the data without returning it.
def _read_all(self, return_data=True):
+ # The loop assumes that _buffer_offset is 0. Ensure that this is true.
+ self._buffer = self._buffer[self._buffer_offset:]
+ self._buffer_offset = 0
+
blocks = []
while self._fill_buffer():
if return_data:
blocks.append(self._buffer)
self._pos += len(self._buffer)
- self._buffer = None
+ self._buffer = b""
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):
+ # If we have enough data buffered, return immediately.
+ end = self._buffer_offset + n
+ if end <= len(self._buffer):
+ data = self._buffer[self._buffer_offset : end]
+ self._buffer_offset = end
+ self._pos += len(data)
+ return data if return_data else None
+
+ # The loop assumes that _buffer_offset is 0. Ensure that this is true.
+ self._buffer = self._buffer[self._buffer_offset:]
+ self._buffer_offset = 0
+
blocks = []
while n > 0 and self._fill_buffer():
if n < len(self._buffer):
data = self._buffer[:n]
- self._buffer = self._buffer[n:]
+ self._buffer_offset = n
else:
data = self._buffer
- self._buffer = None
+ self._buffer = b""
if return_data:
blocks.append(data)
self._pos += len(data)
@@ -274,9 +293,9 @@ class LZMAFile(io.BufferedIOBase):
The exact number of bytes returned is unspecified.
"""
self._check_can_read()
- if self._mode == _MODE_READ_EOF or not self._fill_buffer():
+ if not self._fill_buffer():
return b""
- return self._buffer
+ return self._buffer[self._buffer_offset:]
def read(self, size=-1):
"""Read up to size uncompressed bytes from the file.
@@ -285,7 +304,7 @@ class LZMAFile(io.BufferedIOBase):
Returns b"" if the file is already at EOF.
"""
self._check_can_read()
- if self._mode == _MODE_READ_EOF or size == 0:
+ if size == 0:
return b""
elif size < 0:
return self._read_all()
@@ -302,18 +321,40 @@ class LZMAFile(io.BufferedIOBase):
# this does not give enough data for the decompressor to make progress.
# In this case we make multiple reads, to avoid returning b"".
self._check_can_read()
- if (size == 0 or self._mode == _MODE_READ_EOF or
- not self._fill_buffer()):
+ if (size == 0 or
+ # Only call _fill_buffer() if the buffer is actually empty.
+ # This gives a significant speedup if *size* is small.
+ (self._buffer_offset == len(self._buffer) and not self._fill_buffer())):
return b""
- if 0 < size < len(self._buffer):
- data = self._buffer[:size]
- self._buffer = self._buffer[size:]
+ if size > 0:
+ data = self._buffer[self._buffer_offset :
+ self._buffer_offset + size]
+ self._buffer_offset += len(data)
else:
- data = self._buffer
- self._buffer = None
+ data = self._buffer[self._buffer_offset:]
+ self._buffer = b""
+ self._buffer_offset = 0
self._pos += len(data)
return data
+ 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.
+ """
+ self._check_can_read()
+ # Shortcut for the common case - the whole line is in the buffer.
+ if size < 0:
+ end = self._buffer.find(b"\n", self._buffer_offset) + 1
+ if end > 0:
+ line = self._buffer[self._buffer_offset : end]
+ self._buffer_offset = end
+ self._pos += len(line)
+ return line
+ return io.BufferedIOBase.readline(self, size)
+
def write(self, data):
"""Write a bytes object to the file.
@@ -333,7 +374,8 @@ class LZMAFile(io.BufferedIOBase):
self._mode = _MODE_READ
self._pos = 0
self._decompressor = LZMADecompressor(**self._init_args)
- self._buffer = None
+ self._buffer = b""
+ self._buffer_offset = 0
def seek(self, offset, whence=0):
"""Change the file position.
@@ -372,8 +414,7 @@ class LZMAFile(io.BufferedIOBase):
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)
+ self._read_block(offset, return_data=False)
return self._pos
@@ -388,23 +429,25 @@ def open(filename, mode="rb", *,
encoding=None, errors=None, newline=None):
"""Open an LZMA-compressed file in binary or text mode.
- filename can be either an actual file name (given as a str or bytes object),
- in which case the named file is opened, or it can be an existing file object
- to read from or write to.
+ filename can be either an actual file name (given as a str or bytes
+ object), in which case the named file is opened, or it can be an
+ existing file object to read from or write to.
- The mode argument can be "r", "rb" (default), "w", "wb", "a", or "ab" for
- binary mode, or "rt", "wt" or "at" for text mode.
+ The mode argument can be "r", "rb" (default), "w", "wb", "x", "xb",
+ "a", or "ab" for binary mode, or "rt", "wt", "xt", or "at" for text
+ mode.
- The format, check, preset and filters arguments specify the compression
- settings, as for LZMACompressor, LZMADecompressor and LZMAFile.
+ The format, check, preset and filters arguments specify the
+ compression settings, as for LZMACompressor, LZMADecompressor and
+ LZMAFile.
- For binary mode, this function is equivalent to the LZMAFile constructor:
- LZMAFile(filename, mode, ...). In this case, the encoding, errors and
- newline arguments must not be provided.
+ For binary mode, this function is equivalent to the LZMAFile
+ constructor: LZMAFile(filename, mode, ...). In this case, the
+ encoding, errors and newline arguments must not be provided.
For text mode, a LZMAFile object is created, and wrapped in an
- io.TextIOWrapper instance with the specified encoding, error handling
- behavior, and line ending(s).
+ io.TextIOWrapper instance with the specified encoding, error
+ handling behavior, and line ending(s).
"""
if "t" in mode:
@@ -434,7 +477,7 @@ def compress(data, format=FORMAT_XZ, check=-1, preset=None, filters=None):
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.
+ For incremental compression, use an LZMACompressor instead.
"""
comp = LZMACompressor(format, check, preset, filters)
return comp.compress(data) + comp.flush()
@@ -446,7 +489,7 @@ def decompress(data, format=FORMAT_AUTO, memlimit=None, filters=None):
Refer to LZMADecompressor's docstring for a description of the
optional arguments *format*, *check* and *filters*.
- For incremental decompression, use a LZMADecompressor object instead.
+ For incremental decompression, use an LZMADecompressor instead.
"""
results = []
while True:
diff --git a/Lib/macpath.py b/Lib/macpath.py
index 1615d91..d34f9e94 100644
--- a/Lib/macpath.py
+++ b/Lib/macpath.py
@@ -127,7 +127,7 @@ def lexists(path):
try:
st = os.lstat(path)
- except os.error:
+ except OSError:
return False
return True
diff --git a/Lib/mailbox.py b/Lib/mailbox.py
index 3b64c2e..8a25a19 100644
--- a/Lib/mailbox.py
+++ b/Lib/mailbox.py
@@ -6,7 +6,6 @@
# or returning from a flush() method. See functions _sync_flush() and
# _sync_close().
-import sys
import os
import time
import calendar
@@ -20,9 +19,6 @@ import email.generator
import io
import contextlib
try:
- if sys.platform == 'os2emx':
- # OS/2 EMX fcntl() not adequate
- raise ImportError
import fcntl
except ImportError:
fcntl = None
@@ -339,11 +335,8 @@ class Maildir(Mailbox):
# This overrides an inapplicable implementation in the superclass.
try:
self.remove(key)
- except KeyError:
+ except (KeyError, FileNotFoundError):
pass
- except OSError as e:
- if e.errno != errno.ENOENT:
- raise
def __setitem__(self, key, message):
"""Replace the keyed message; raise KeyError if it doesn't exist."""
@@ -375,14 +368,11 @@ class Maildir(Mailbox):
def get_message(self, key):
"""Return a Message representation or raise a KeyError."""
subpath = self._lookup(key)
- f = open(os.path.join(self._path, subpath), 'rb')
- try:
+ with open(os.path.join(self._path, subpath), 'rb') as f:
if self._factory:
msg = self._factory(f)
else:
msg = MaildirMessage(f)
- finally:
- f.close()
subdir, name = os.path.split(subpath)
msg.set_subdir(subdir)
if self.colon in name:
@@ -392,11 +382,8 @@ class Maildir(Mailbox):
def get_bytes(self, key):
"""Return a bytes representation or raise a KeyError."""
- f = open(os.path.join(self._path, self._lookup(key)), 'rb')
- try:
+ with open(os.path.join(self._path, self._lookup(key)), 'rb') as f:
return f.read().replace(linesep, b'\n')
- finally:
- f.close()
def get_file(self, key):
"""Return a file-like representation or raise a KeyError."""
@@ -508,16 +495,12 @@ class Maildir(Mailbox):
path = os.path.join(self._path, 'tmp', uniq)
try:
os.stat(path)
- except OSError as e:
- if e.errno == errno.ENOENT:
- Maildir._count += 1
- try:
- return _create_carefully(path)
- except OSError as e:
- if e.errno != errno.EEXIST:
- raise
- else:
- raise
+ except FileNotFoundError:
+ Maildir._count += 1
+ try:
+ return _create_carefully(path)
+ except FileExistsError:
+ pass
# Fall through to here if stat succeeded or open raised EEXIST.
raise ExternalClashError('Name clash prevented file creation: %s' %
@@ -594,7 +577,7 @@ class _singlefileMailbox(Mailbox):
Mailbox.__init__(self, path, factory, create)
try:
f = open(self._path, 'rb+')
- except IOError as e:
+ except OSError as e:
if e.errno == errno.ENOENT:
if create:
f = open(self._path, 'wb+')
@@ -637,8 +620,7 @@ class _singlefileMailbox(Mailbox):
def iterkeys(self):
"""Return an iterator over keys."""
self._lookup()
- for key in self._toc.keys():
- yield key
+ yield from self._toc.keys()
def __contains__(self, key):
"""Return True if the keyed message exists, False otherwise."""
@@ -716,13 +698,9 @@ class _singlefileMailbox(Mailbox):
os.chmod(new_file.name, mode)
try:
os.rename(new_file.name, self._path)
- except OSError as e:
- if e.errno == errno.EEXIST or \
- (os.name == 'os2' and e.errno == errno.EACCES):
- os.remove(self._path)
- os.rename(new_file.name, self._path)
- else:
- raise
+ except FileExistsError:
+ os.remove(self._path)
+ os.rename(new_file.name, self._path)
self._file = open(self._path, 'rb+')
self._toc = new_toc
self._pending = False
@@ -999,7 +977,7 @@ class MH(Mailbox):
path = os.path.join(self._path, str(key))
try:
f = open(path, 'rb+')
- except IOError as e:
+ except OSError as e:
if e.errno == errno.ENOENT:
raise KeyError('No message with key: %s' % key)
else:
@@ -1013,7 +991,7 @@ class MH(Mailbox):
path = os.path.join(self._path, str(key))
try:
f = open(path, 'rb+')
- except IOError as e:
+ except OSError as e:
if e.errno == errno.ENOENT:
raise KeyError('No message with key: %s' % key)
else:
@@ -1039,12 +1017,12 @@ class MH(Mailbox):
f = open(os.path.join(self._path, str(key)), 'rb+')
else:
f = open(os.path.join(self._path, str(key)), 'rb')
- except IOError as e:
+ except OSError as e:
if e.errno == errno.ENOENT:
raise KeyError('No message with key: %s' % key)
else:
raise
- try:
+ with f:
if self._locked:
_lock_file(f)
try:
@@ -1052,8 +1030,6 @@ class MH(Mailbox):
finally:
if self._locked:
_unlock_file(f)
- finally:
- f.close()
for name, key_list in self.get_sequences().items():
if key in key_list:
msg.add_sequence(name)
@@ -1066,12 +1042,12 @@ class MH(Mailbox):
f = open(os.path.join(self._path, str(key)), 'rb+')
else:
f = open(os.path.join(self._path, str(key)), 'rb')
- except IOError as e:
+ except OSError as e:
if e.errno == errno.ENOENT:
raise KeyError('No message with key: %s' % key)
else:
raise
- try:
+ with f:
if self._locked:
_lock_file(f)
try:
@@ -1079,14 +1055,12 @@ class MH(Mailbox):
finally:
if self._locked:
_unlock_file(f)
- finally:
- f.close()
def get_file(self, key):
"""Return a file-like representation or raise a KeyError."""
try:
f = open(os.path.join(self._path, str(key)), 'rb')
- except IOError as e:
+ except OSError as e:
if e.errno == errno.ENOENT:
raise KeyError('No message with key: %s' % key)
else:
@@ -2079,7 +2053,7 @@ def _lock_file(f, dotlock=True):
if fcntl:
try:
fcntl.lockf(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
- except IOError as e:
+ except OSError as e:
if e.errno in (errno.EAGAIN, errno.EACCES, errno.EROFS):
raise ExternalClashError('lockf: lock unavailable: %s' %
f.name)
@@ -2089,7 +2063,7 @@ def _lock_file(f, dotlock=True):
try:
pre_lock = _create_temporary(f.name + '.lock')
pre_lock.close()
- except IOError as e:
+ except OSError as e:
if e.errno in (errno.EACCES, errno.EROFS):
return # Without write access, just skip dotlocking.
else:
@@ -2102,14 +2076,10 @@ def _lock_file(f, dotlock=True):
else:
os.rename(pre_lock.name, f.name + '.lock')
dotlock_done = True
- except OSError as e:
- if e.errno == errno.EEXIST or \
- (os.name == 'os2' and e.errno == errno.EACCES):
- os.remove(pre_lock.name)
- raise ExternalClashError('dot lock unavailable: %s' %
- f.name)
- else:
- raise
+ except FileExistsError:
+ os.remove(pre_lock.name)
+ raise ExternalClashError('dot lock unavailable: %s' %
+ f.name)
except:
if fcntl:
fcntl.lockf(f, fcntl.LOCK_UN)
diff --git a/Lib/mailcap.py b/Lib/mailcap.py
index 0c0b19c..97e3035 100644
--- a/Lib/mailcap.py
+++ b/Lib/mailcap.py
@@ -20,7 +20,7 @@ def getcaps():
for mailcap in listmailcapfiles():
try:
fp = open(mailcap, 'r')
- except IOError:
+ except OSError:
continue
with fp:
morecaps = readmailcapfile(fp)
diff --git a/Lib/mimetypes.py b/Lib/mimetypes.py
index cdebf7a..b98c874 100644
--- a/Lib/mimetypes.py
+++ b/Lib/mimetypes.py
@@ -361,7 +361,7 @@ def init(files=None):
def read_mime_types(file):
try:
f = open(file)
- except IOError:
+ except OSError:
return None
with f:
db = MimeTypes()
diff --git a/Lib/modulefinder.py b/Lib/modulefinder.py
index 264b0f0..cc5b8cc 100644
--- a/Lib/modulefinder.py
+++ b/Lib/modulefinder.py
@@ -1,13 +1,17 @@
"""Find modules used by a script, using introspection."""
import dis
-import imp
+import importlib._bootstrap
import importlib.machinery
import marshal
import os
import sys
import types
import struct
+import warnings
+with warnings.catch_warnings():
+ warnings.simplefilter('ignore', PendingDeprecationWarning)
+ import imp
# XXX Clean up once str8's cstor matches bytes.
LOAD_CONST = bytes([dis.opname.index('LOAD_CONST')])
@@ -229,7 +233,7 @@ class ModuleFinder:
for dir in m.__path__:
try:
names = os.listdir(dir)
- except os.error:
+ except OSError:
self.msg(2, "can't list directory", dir)
continue
for name in names:
@@ -284,11 +288,12 @@ class ModuleFinder:
if type == imp.PY_SOURCE:
co = compile(fp.read()+'\n', pathname, 'exec')
elif type == imp.PY_COMPILED:
- if fp.read(4) != imp.get_magic():
- self.msgout(2, "raise ImportError: Bad magic number", pathname)
- raise ImportError("Bad magic number in %s" % pathname)
- fp.read(8) # Skip mtime and size.
- co = marshal.load(fp)
+ try:
+ marshal_data = importlib._bootstrap._validate_bytecode_header(fp.read())
+ except ImportError as exc:
+ self.msgout(2, "raise ImportError: " + str(exc), pathname)
+ raise
+ co = marshal.loads(marshal_data)
else:
co = None
m = self.add_module(fqname)
diff --git a/Lib/multiprocessing/__init__.py b/Lib/multiprocessing/__init__.py
index 1f3e67c..86df638 100644
--- a/Lib/multiprocessing/__init__.py
+++ b/Lib/multiprocessing/__init__.py
@@ -8,260 +8,31 @@
# subpackage 'multiprocessing.dummy' has the same API but is a simple
# wrapper for 'threading'.
#
-# Try calling `multiprocessing.doc.main()` to read the html
-# documentation in a webbrowser.
-#
-#
# Copyright (c) 2006-2008, R Oudkerk
# Licensed to PSF under a Contributor Agreement.
#
-__version__ = '0.70a1'
-
-__all__ = [
- 'Process', 'current_process', 'active_children', 'freeze_support',
- 'Manager', 'Pipe', 'cpu_count', 'log_to_stderr', 'get_logger',
- 'allow_connection_pickling', 'BufferTooShort', 'TimeoutError',
- 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Condition',
- 'Event', 'Barrier', 'Queue', 'SimpleQueue', 'JoinableQueue', 'Pool',
- 'Value', 'Array', 'RawValue', 'RawArray', 'SUBDEBUG', 'SUBWARNING',
- ]
-
-__author__ = 'R. Oudkerk (r.m.oudkerk@gmail.com)'
-
-#
-# Imports
-#
-
-import os
import sys
-
-from multiprocessing.process import Process, current_process, active_children
-from multiprocessing.util import SUBDEBUG, SUBWARNING
+from . import context
#
-# Exceptions
+# Copy stuff from default context
#
-class ProcessError(Exception):
- pass
-
-class BufferTooShort(ProcessError):
- pass
-
-class TimeoutError(ProcessError):
- pass
-
-class AuthenticationError(ProcessError):
- pass
-
-import _multiprocessing
-
-#
-# Definitions not depending on native semaphores
-#
-
-def Manager():
- '''
- Returns a manager associated with a running server process
-
- The managers methods such as `Lock()`, `Condition()` and `Queue()`
- can be used to create shared objects.
- '''
- from multiprocessing.managers import SyncManager
- m = SyncManager()
- m.start()
- return m
-
-def Pipe(duplex=True):
- '''
- Returns two connection object connected by a pipe
- '''
- from multiprocessing.connection import Pipe
- return Pipe(duplex)
-
-def cpu_count():
- '''
- Returns the number of CPUs in the system
- '''
- if sys.platform == 'win32':
- try:
- num = int(os.environ['NUMBER_OF_PROCESSORS'])
- except (ValueError, KeyError):
- num = 0
- elif 'bsd' in sys.platform or sys.platform == 'darwin':
- comm = '/sbin/sysctl -n hw.ncpu'
- if sys.platform == 'darwin':
- comm = '/usr' + comm
- try:
- with os.popen(comm) as p:
- num = int(p.read())
- except ValueError:
- num = 0
- else:
- try:
- num = os.sysconf('SC_NPROCESSORS_ONLN')
- except (ValueError, OSError, AttributeError):
- num = 0
-
- if num >= 1:
- return num
- else:
- raise NotImplementedError('cannot determine number of cpus')
-
-def freeze_support():
- '''
- Check whether this is a fake forked process in a frozen executable.
- If so then run code specified by commandline and exit.
- '''
- if sys.platform == 'win32' and getattr(sys, 'frozen', False):
- from multiprocessing.forking import freeze_support
- freeze_support()
-
-def get_logger():
- '''
- Return package logger -- if it does not already exist then it is created
- '''
- from multiprocessing.util import get_logger
- return get_logger()
-
-def log_to_stderr(level=None):
- '''
- Turn on logging and add a handler which prints to stderr
- '''
- from multiprocessing.util import log_to_stderr
- return log_to_stderr(level)
-
-def allow_connection_pickling():
- '''
- Install support for sending connections and sockets between processes
- '''
- # This is undocumented. In previous versions of multiprocessing
- # its only effect was to make socket objects inheritable on Windows.
- import multiprocessing.connection
+globals().update((name, getattr(context._default_context, name))
+ for name in context._default_context.__all__)
+__all__ = context._default_context.__all__
#
-# Definitions depending on native semaphores
+# XXX These should not really be documented or public.
#
-def Lock():
- '''
- Returns a non-recursive lock object
- '''
- from multiprocessing.synchronize import Lock
- return Lock()
-
-def RLock():
- '''
- Returns a recursive lock object
- '''
- from multiprocessing.synchronize import RLock
- return RLock()
-
-def Condition(lock=None):
- '''
- Returns a condition object
- '''
- from multiprocessing.synchronize import Condition
- return Condition(lock)
-
-def Semaphore(value=1):
- '''
- Returns a semaphore object
- '''
- from multiprocessing.synchronize import Semaphore
- return Semaphore(value)
-
-def BoundedSemaphore(value=1):
- '''
- Returns a bounded semaphore object
- '''
- from multiprocessing.synchronize import BoundedSemaphore
- return BoundedSemaphore(value)
-
-def Event():
- '''
- Returns an event object
- '''
- from multiprocessing.synchronize import Event
- return Event()
-
-def Barrier(parties, action=None, timeout=None):
- '''
- Returns a barrier object
- '''
- from multiprocessing.synchronize import Barrier
- return Barrier(parties, action, timeout)
-
-def Queue(maxsize=0):
- '''
- Returns a queue object
- '''
- from multiprocessing.queues import Queue
- return Queue(maxsize)
+SUBDEBUG = 5
+SUBWARNING = 25
-def JoinableQueue(maxsize=0):
- '''
- Returns a queue object
- '''
- from multiprocessing.queues import JoinableQueue
- return JoinableQueue(maxsize)
-
-def SimpleQueue():
- '''
- Returns a queue object
- '''
- from multiprocessing.queues import SimpleQueue
- return SimpleQueue()
-
-def Pool(processes=None, initializer=None, initargs=(), maxtasksperchild=None):
- '''
- Returns a process pool object
- '''
- from multiprocessing.pool import Pool
- return Pool(processes, initializer, initargs, maxtasksperchild)
-
-def RawValue(typecode_or_type, *args):
- '''
- Returns a shared object
- '''
- from multiprocessing.sharedctypes import RawValue
- return RawValue(typecode_or_type, *args)
-
-def RawArray(typecode_or_type, size_or_initializer):
- '''
- Returns a shared array
- '''
- from multiprocessing.sharedctypes import RawArray
- return RawArray(typecode_or_type, size_or_initializer)
-
-def Value(typecode_or_type, *args, lock=True):
- '''
- Returns a synchronized shared object
- '''
- from multiprocessing.sharedctypes import Value
- return Value(typecode_or_type, *args, lock=lock)
-
-def Array(typecode_or_type, size_or_initializer, *, lock=True):
- '''
- Returns a synchronized shared array
- '''
- from multiprocessing.sharedctypes import Array
- return Array(typecode_or_type, size_or_initializer, lock=lock)
-
-#
#
+# Alias for main module -- will be reset by bootstrapping child processes
#
-if sys.platform == 'win32':
-
- def set_executable(executable):
- '''
- Sets the path to a python.exe or pythonw.exe binary used to run
- child processes on Windows instead of sys.executable.
- Useful for people embedding Python.
- '''
- from multiprocessing.forking import set_executable
- set_executable(executable)
-
- __all__ += ['set_executable']
+if '__main__' in sys.modules:
+ sys.modules['__mp_main__'] = sys.modules['__main__']
diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py
index 22589d0..3bc716f 100644
--- a/Lib/multiprocessing/connection.py
+++ b/Lib/multiprocessing/connection.py
@@ -12,22 +12,23 @@ __all__ = [ 'Client', 'Listener', 'Pipe', 'wait' ]
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, BufferTooShort
-from multiprocessing.util import get_temp_dir, Finalize, sub_debug, debug
-from multiprocessing.forking import ForkingPickler
+
+from . import reduction
+from . import util
+
+from . import AuthenticationError, BufferTooShort
+from .reduction import ForkingPickler
+
try:
import _winapi
- from _winapi import WAIT_OBJECT_0, WAIT_TIMEOUT, INFINITE
+ from _winapi import WAIT_OBJECT_0, WAIT_ABANDONED_0, WAIT_TIMEOUT, INFINITE
except ImportError:
if sys.platform == 'win32':
raise
@@ -72,10 +73,10 @@ def arbitrary_address(family):
if family == 'AF_INET':
return ('localhost', 0)
elif family == 'AF_UNIX':
- return tempfile.mktemp(prefix='listener-', dir=get_temp_dir())
+ return tempfile.mktemp(prefix='listener-', dir=util.get_temp_dir())
elif family == 'AF_PIPE':
return tempfile.mktemp(prefix=r'\\.\pipe\pyc-%d-%d-' %
- (os.getpid(), next(_mmap_counter)))
+ (os.getpid(), next(_mmap_counter)), dir="")
else:
raise ValueError('unrecognized family')
@@ -132,22 +133,22 @@ class _ConnectionBase:
def _check_closed(self):
if self._handle is None:
- raise IOError("handle is closed")
+ raise OSError("handle is closed")
def _check_readable(self):
if not self._readable:
- raise IOError("connection is write-only")
+ raise OSError("connection is write-only")
def _check_writable(self):
if not self._writable:
- raise IOError("connection is read-only")
+ raise OSError("connection is read-only")
def _bad_message_length(self):
if self._writable:
self._readable = False
else:
self.close()
- raise IOError("bad message length")
+ raise OSError("bad message length")
@property
def closed(self):
@@ -202,9 +203,7 @@ class _ConnectionBase:
"""Send a (picklable) object"""
self._check_closed()
self._check_writable()
- buf = io.BytesIO()
- ForkingPickler(buf, pickle.HIGHEST_PROTOCOL).dump(obj)
- self._send_bytes(buf.getbuffer())
+ self._send_bytes(ForkingPickler.dumps(obj))
def recv_bytes(self, maxlength=None):
"""
@@ -249,7 +248,7 @@ class _ConnectionBase:
self._check_closed()
self._check_readable()
buf = self._recv_bytes()
- return pickle.loads(buf.getbuffer())
+ return ForkingPickler.loads(buf.getbuffer())
def poll(self, timeout=0.0):
"""Whether there is any input available to be read"""
@@ -317,7 +316,7 @@ if _winapi:
return f
elif err == _winapi.ERROR_MORE_DATA:
return self._get_more_data(ov, maxsize)
- except IOError as e:
+ except OSError as e:
if e.winerror == _winapi.ERROR_BROKEN_PIPE:
raise EOFError
else:
@@ -389,7 +388,7 @@ class Connection(_ConnectionBase):
if remaining == size:
raise EOFError
else:
- raise IOError("got end of file during message")
+ raise OSError("got end of file during message")
buf.write(chunk)
remaining -= n
return buf
@@ -459,7 +458,7 @@ class Listener(object):
Returns a `Connection` object.
'''
if self._listener is None:
- raise IOError('listener is closed')
+ raise OSError('listener is closed')
c = self._listener.accept()
if self._authkey:
deliver_challenge(c, self._authkey)
@@ -545,7 +544,9 @@ else:
_winapi.FILE_FLAG_FIRST_PIPE_INSTANCE,
_winapi.PIPE_TYPE_MESSAGE | _winapi.PIPE_READMODE_MESSAGE |
_winapi.PIPE_WAIT,
- 1, obsize, ibsize, _winapi.NMPWAIT_WAIT_FOREVER, _winapi.NULL
+ 1, obsize, ibsize, _winapi.NMPWAIT_WAIT_FOREVER,
+ # default security descriptor: the handle cannot be inherited
+ _winapi.NULL
)
h2 = _winapi.CreateFile(
address, access, 0, _winapi.NULL, _winapi.OPEN_EXISTING,
@@ -590,7 +591,7 @@ class SocketListener(object):
self._last_accepted = None
if family == 'AF_UNIX':
- self._unlink = Finalize(
+ self._unlink = util.Finalize(
self, os.unlink, args=(address,), exitpriority=0
)
else:
@@ -638,8 +639,8 @@ if sys.platform == 'win32':
self._handle_queue = [self._new_handle(first=True)]
self._last_accepted = None
- sub_debug('listener created with address=%r', self._address)
- self.close = Finalize(
+ util.sub_debug('listener created with address=%r', self._address)
+ self.close = util.Finalize(
self, PipeListener._finalize_pipe_listener,
args=(self._handle_queue, self._address), exitpriority=0
)
@@ -681,7 +682,7 @@ if sys.platform == 'win32':
@staticmethod
def _finalize_pipe_listener(queue, address):
- sub_debug('closing listener with address=%r', address)
+ util.sub_debug('closing listener with address=%r', address)
for handle in queue:
_winapi.CloseHandle(handle)
@@ -698,7 +699,7 @@ if sys.platform == 'win32':
0, _winapi.NULL, _winapi.OPEN_EXISTING,
_winapi.FILE_FLAG_OVERLAPPED, _winapi.NULL
)
- except WindowsError as e:
+ except OSError as e:
if e.winerror not in (_winapi.ERROR_SEM_TIMEOUT,
_winapi.ERROR_PIPE_BUSY) or _check_timeout(t):
raise
@@ -727,7 +728,7 @@ def deliver_challenge(connection, authkey):
assert isinstance(authkey, bytes)
message = os.urandom(MESSAGE_LENGTH)
connection.send_bytes(CHALLENGE + message)
- digest = hmac.new(authkey, message).digest()
+ digest = hmac.new(authkey, message, 'md5').digest()
response = connection.recv_bytes(256) # reject large message
if response == digest:
connection.send_bytes(WELCOME)
@@ -741,7 +742,7 @@ def answer_challenge(connection, authkey):
message = connection.recv_bytes(256) # reject large message
assert message[:len(CHALLENGE)] == CHALLENGE, 'message = %r' % message
message = message[len(CHALLENGE):]
- digest = hmac.new(authkey, message).digest()
+ digest = hmac.new(authkey, message, 'md5').digest()
connection.send_bytes(digest)
response = connection.recv_bytes(256) # reject large message
if response != WELCOME:
@@ -884,28 +885,15 @@ if sys.platform == 'win32':
else:
- if hasattr(select, 'poll'):
- def _poll(fds, timeout):
- if timeout is not None:
- timeout = int(timeout * 1000) # timeout is in milliseconds
- fd_map = {}
- pollster = select.poll()
- for fd in fds:
- pollster.register(fd, select.POLLIN)
- if hasattr(fd, 'fileno'):
- fd_map[fd.fileno()] = fd
- else:
- fd_map[fd] = fd
- ls = []
- for fd, event in pollster.poll(timeout):
- if event & select.POLLNVAL:
- raise ValueError('invalid file descriptor %i' % fd)
- ls.append(fd_map[fd])
- return ls
- else:
- def _poll(fds, timeout):
- return select.select(fds, [], [], timeout)[0]
+ import selectors
+ # poll/select have the advantage of not requiring any extra file
+ # descriptor, contrarily to epoll/kqueue (also, they require a single
+ # syscall).
+ if hasattr(selectors, 'PollSelector'):
+ _WaitSelector = selectors.PollSelector
+ else:
+ _WaitSelector = selectors.SelectSelector
def wait(object_list, timeout=None):
'''
@@ -913,34 +901,54 @@ else:
Returns list of those objects in object_list which are ready/readable.
'''
- if timeout is not None:
- if timeout <= 0:
- return _poll(object_list, 0)
- else:
- deadline = time.time() + timeout
- while True:
- try:
- return _poll(object_list, timeout)
- except OSError as e:
- if e.errno != errno.EINTR:
- raise
+ with _WaitSelector() as selector:
+ for obj in object_list:
+ selector.register(obj, selectors.EVENT_READ)
+
if timeout is not None:
- timeout = deadline - time.time()
+ deadline = time.time() + timeout
+
+ while True:
+ ready = selector.select(timeout)
+ if ready:
+ return [key.fileobj for (key, events) in ready]
+ else:
+ if timeout is not None:
+ timeout = deadline - time.time()
+ if timeout < 0:
+ return ready
#
# Make connection and socket objects sharable if possible
#
if sys.platform == 'win32':
- from . import reduction
- ForkingPickler.register(socket.socket, reduction.reduce_socket)
- ForkingPickler.register(Connection, reduction.reduce_connection)
- ForkingPickler.register(PipeConnection, reduction.reduce_pipe_connection)
+ def reduce_connection(conn):
+ handle = conn.fileno()
+ with socket.fromfd(handle, socket.AF_INET, socket.SOCK_STREAM) as s:
+ from . import resource_sharer
+ ds = resource_sharer.DupSocket(s)
+ return rebuild_connection, (ds, conn.readable, conn.writable)
+ def rebuild_connection(ds, readable, writable):
+ sock = ds.detach()
+ return Connection(sock.detach(), readable, writable)
+ reduction.register(Connection, reduce_connection)
+
+ def reduce_pipe_connection(conn):
+ access = ((_winapi.FILE_GENERIC_READ if conn.readable else 0) |
+ (_winapi.FILE_GENERIC_WRITE if conn.writable else 0))
+ dh = reduction.DupHandle(conn.fileno(), access)
+ return rebuild_pipe_connection, (dh, conn.readable, conn.writable)
+ def rebuild_pipe_connection(dh, readable, writable):
+ handle = dh.detach()
+ return PipeConnection(handle, readable, writable)
+ reduction.register(PipeConnection, reduce_pipe_connection)
+
else:
- try:
- from . import reduction
- except ImportError:
- pass
- else:
- ForkingPickler.register(socket.socket, reduction.reduce_socket)
- ForkingPickler.register(Connection, reduction.reduce_connection)
+ def reduce_connection(conn):
+ df = reduction.DupFd(conn.fileno())
+ return rebuild_connection, (df, conn.readable, conn.writable)
+ def rebuild_connection(df, readable, writable):
+ fd = df.detach()
+ return Connection(fd, readable, writable)
+ reduction.register(Connection, reduce_connection)
diff --git a/Lib/multiprocessing/context.py b/Lib/multiprocessing/context.py
new file mode 100644
index 0000000..63849f9
--- /dev/null
+++ b/Lib/multiprocessing/context.py
@@ -0,0 +1,348 @@
+import os
+import sys
+import threading
+
+from . import process
+
+__all__ = [] # things are copied from here to __init__.py
+
+#
+# Exceptions
+#
+
+class ProcessError(Exception):
+ pass
+
+class BufferTooShort(ProcessError):
+ pass
+
+class TimeoutError(ProcessError):
+ pass
+
+class AuthenticationError(ProcessError):
+ pass
+
+#
+# Base type for contexts
+#
+
+class BaseContext(object):
+
+ ProcessError = ProcessError
+ BufferTooShort = BufferTooShort
+ TimeoutError = TimeoutError
+ AuthenticationError = AuthenticationError
+
+ current_process = staticmethod(process.current_process)
+ active_children = staticmethod(process.active_children)
+
+ def cpu_count(self):
+ '''Returns the number of CPUs in the system'''
+ num = os.cpu_count()
+ if num is None:
+ raise NotImplementedError('cannot determine number of cpus')
+ else:
+ return num
+
+ def Manager(self):
+ '''Returns a manager associated with a running server process
+
+ The managers methods such as `Lock()`, `Condition()` and `Queue()`
+ can be used to create shared objects.
+ '''
+ from .managers import SyncManager
+ m = SyncManager(ctx=self.get_context())
+ m.start()
+ return m
+
+ def Pipe(self, duplex=True):
+ '''Returns two connection object connected by a pipe'''
+ from .connection import Pipe
+ return Pipe(duplex)
+
+ def Lock(self):
+ '''Returns a non-recursive lock object'''
+ from .synchronize import Lock
+ return Lock(ctx=self.get_context())
+
+ def RLock(self):
+ '''Returns a recursive lock object'''
+ from .synchronize import RLock
+ return RLock(ctx=self.get_context())
+
+ def Condition(self, lock=None):
+ '''Returns a condition object'''
+ from .synchronize import Condition
+ return Condition(lock, ctx=self.get_context())
+
+ def Semaphore(self, value=1):
+ '''Returns a semaphore object'''
+ from .synchronize import Semaphore
+ return Semaphore(value, ctx=self.get_context())
+
+ def BoundedSemaphore(self, value=1):
+ '''Returns a bounded semaphore object'''
+ from .synchronize import BoundedSemaphore
+ return BoundedSemaphore(value, ctx=self.get_context())
+
+ def Event(self):
+ '''Returns an event object'''
+ from .synchronize import Event
+ return Event(ctx=self.get_context())
+
+ def Barrier(self, parties, action=None, timeout=None):
+ '''Returns a barrier object'''
+ from .synchronize import Barrier
+ return Barrier(parties, action, timeout, ctx=self.get_context())
+
+ def Queue(self, maxsize=0):
+ '''Returns a queue object'''
+ from .queues import Queue
+ return Queue(maxsize, ctx=self.get_context())
+
+ def JoinableQueue(self, maxsize=0):
+ '''Returns a queue object'''
+ from .queues import JoinableQueue
+ return JoinableQueue(maxsize, ctx=self.get_context())
+
+ def SimpleQueue(self):
+ '''Returns a queue object'''
+ from .queues import SimpleQueue
+ return SimpleQueue(ctx=self.get_context())
+
+ def Pool(self, processes=None, initializer=None, initargs=(),
+ maxtasksperchild=None):
+ '''Returns a process pool object'''
+ from .pool import Pool
+ return Pool(processes, initializer, initargs, maxtasksperchild,
+ context=self.get_context())
+
+ def RawValue(self, typecode_or_type, *args):
+ '''Returns a shared object'''
+ from .sharedctypes import RawValue
+ return RawValue(typecode_or_type, *args)
+
+ def RawArray(self, typecode_or_type, size_or_initializer):
+ '''Returns a shared array'''
+ from .sharedctypes import RawArray
+ return RawArray(typecode_or_type, size_or_initializer)
+
+ def Value(self, typecode_or_type, *args, lock=True):
+ '''Returns a synchronized shared object'''
+ from .sharedctypes import Value
+ return Value(typecode_or_type, *args, lock=lock,
+ ctx=self.get_context())
+
+ def Array(self, typecode_or_type, size_or_initializer, *, lock=True):
+ '''Returns a synchronized shared array'''
+ from .sharedctypes import Array
+ return Array(typecode_or_type, size_or_initializer, lock=lock,
+ ctx=self.get_context())
+
+ def freeze_support(self):
+ '''Check whether this is a fake forked process in a frozen executable.
+ If so then run code specified by commandline and exit.
+ '''
+ if sys.platform == 'win32' and getattr(sys, 'frozen', False):
+ from .spawn import freeze_support
+ freeze_support()
+
+ def get_logger(self):
+ '''Return package logger -- if it does not already exist then
+ it is created.
+ '''
+ from .util import get_logger
+ return get_logger()
+
+ def log_to_stderr(self, level=None):
+ '''Turn on logging and add a handler which prints to stderr'''
+ from .util import log_to_stderr
+ return log_to_stderr(level)
+
+ def allow_connection_pickling(self):
+ '''Install support for sending connections and sockets
+ between processes
+ '''
+ # This is undocumented. In previous versions of multiprocessing
+ # its only effect was to make socket objects inheritable on Windows.
+ from . import connection
+
+ def set_executable(self, executable):
+ '''Sets the path to a python.exe or pythonw.exe binary used to run
+ child processes instead of sys.executable when using the 'spawn'
+ start method. Useful for people embedding Python.
+ '''
+ from .spawn import set_executable
+ set_executable(executable)
+
+ def set_forkserver_preload(self, module_names):
+ '''Set list of module names to try to load in forkserver process.
+ This is really just a hint.
+ '''
+ from .forkserver import set_forkserver_preload
+ set_forkserver_preload(module_names)
+
+ def get_context(self, method=None):
+ if method is None:
+ return self
+ try:
+ ctx = _concrete_contexts[method]
+ except KeyError:
+ raise ValueError('cannot find context for %r' % method)
+ ctx._check_available()
+ return ctx
+
+ def get_start_method(self, allow_none=False):
+ return self._name
+
+ def set_start_method(self, method=None):
+ raise ValueError('cannot set start method of concrete context')
+
+ def _check_available(self):
+ pass
+
+#
+# Type of default context -- underlying context can be set at most once
+#
+
+class Process(process.BaseProcess):
+ _start_method = None
+ @staticmethod
+ def _Popen(process_obj):
+ return _default_context.get_context().Process._Popen(process_obj)
+
+class DefaultContext(BaseContext):
+ Process = Process
+
+ def __init__(self, context):
+ self._default_context = context
+ self._actual_context = None
+
+ def get_context(self, method=None):
+ if method is None:
+ if self._actual_context is None:
+ self._actual_context = self._default_context
+ return self._actual_context
+ else:
+ return super().get_context(method)
+
+ def set_start_method(self, method, force=False):
+ if self._actual_context is not None and not force:
+ raise RuntimeError('context has already been set')
+ if method is None and force:
+ self._actual_context = None
+ return
+ self._actual_context = self.get_context(method)
+
+ def get_start_method(self, allow_none=False):
+ if self._actual_context is None:
+ if allow_none:
+ return None
+ self._actual_context = self._default_context
+ return self._actual_context._name
+
+ def get_all_start_methods(self):
+ if sys.platform == 'win32':
+ return ['spawn']
+ else:
+ from . import reduction
+ if reduction.HAVE_SEND_HANDLE:
+ return ['fork', 'spawn', 'forkserver']
+ else:
+ return ['fork', 'spawn']
+
+DefaultContext.__all__ = list(x for x in dir(DefaultContext) if x[0] != '_')
+
+#
+# Context types for fixed start method
+#
+
+if sys.platform != 'win32':
+
+ class ForkProcess(process.BaseProcess):
+ _start_method = 'fork'
+ @staticmethod
+ def _Popen(process_obj):
+ from .popen_fork import Popen
+ return Popen(process_obj)
+
+ class SpawnProcess(process.BaseProcess):
+ _start_method = 'spawn'
+ @staticmethod
+ def _Popen(process_obj):
+ from .popen_spawn_posix import Popen
+ return Popen(process_obj)
+
+ class ForkServerProcess(process.BaseProcess):
+ _start_method = 'forkserver'
+ @staticmethod
+ def _Popen(process_obj):
+ from .popen_forkserver import Popen
+ return Popen(process_obj)
+
+ class ForkContext(BaseContext):
+ _name = 'fork'
+ Process = ForkProcess
+
+ class SpawnContext(BaseContext):
+ _name = 'spawn'
+ Process = SpawnProcess
+
+ class ForkServerContext(BaseContext):
+ _name = 'forkserver'
+ Process = ForkServerProcess
+ def _check_available(self):
+ from . import reduction
+ if not reduction.HAVE_SEND_HANDLE:
+ raise ValueError('forkserver start method not available')
+
+ _concrete_contexts = {
+ 'fork': ForkContext(),
+ 'spawn': SpawnContext(),
+ 'forkserver': ForkServerContext(),
+ }
+ _default_context = DefaultContext(_concrete_contexts['fork'])
+
+else:
+
+ class SpawnProcess(process.BaseProcess):
+ _start_method = 'spawn'
+ @staticmethod
+ def _Popen(process_obj):
+ from .popen_spawn_win32 import Popen
+ return Popen(process_obj)
+
+ class SpawnContext(BaseContext):
+ _name = 'spawn'
+ Process = SpawnProcess
+
+ _concrete_contexts = {
+ 'spawn': SpawnContext(),
+ }
+ _default_context = DefaultContext(_concrete_contexts['spawn'])
+
+#
+# Force the start method
+#
+
+def _force_start_method(method):
+ _default_context._actual_context = _concrete_contexts[method]
+
+#
+# Check that the current thread is spawning a child process
+#
+
+_tls = threading.local()
+
+def get_spawning_popen():
+ return getattr(_tls, 'spawning_popen', None)
+
+def set_spawning_popen(popen):
+ _tls.spawning_popen = popen
+
+def assert_spawning(obj):
+ if get_spawning_popen() is None:
+ raise RuntimeError(
+ '%s objects should only be shared between processes'
+ ' through inheritance' % type(obj).__name__
+ )
diff --git a/Lib/multiprocessing/dummy/__init__.py b/Lib/multiprocessing/dummy/__init__.py
index e31fc61..97f7af7 100644
--- a/Lib/multiprocessing/dummy/__init__.py
+++ b/Lib/multiprocessing/dummy/__init__.py
@@ -4,32 +4,7 @@
# multiprocessing/dummy/__init__.py
#
# Copyright (c) 2006-2008, R Oudkerk
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-#
-# 1. Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-# 3. Neither the name of author nor the names of any contributors may be
-# used to endorse or promote products derived from this software
-# without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-# SUCH DAMAGE.
+# Licensed to PSF under a Contributor Agreement.
#
__all__ = [
@@ -47,7 +22,7 @@ import sys
import weakref
import array
-from multiprocessing.dummy.connection import Pipe
+from .connection import Pipe
from threading import Lock, RLock, Semaphore, BoundedSemaphore
from threading import Event, Condition, Barrier
from queue import Queue
@@ -138,7 +113,7 @@ def shutdown():
pass
def Pool(processes=None, initializer=None, initargs=()):
- from multiprocessing.pool import ThreadPool
+ from ..pool import ThreadPool
return ThreadPool(processes, initializer, initargs)
JoinableQueue = Queue
diff --git a/Lib/multiprocessing/dummy/connection.py b/Lib/multiprocessing/dummy/connection.py
index 874ec8e..694ef96 100644
--- a/Lib/multiprocessing/dummy/connection.py
+++ b/Lib/multiprocessing/dummy/connection.py
@@ -4,32 +4,7 @@
# multiprocessing/dummy/connection.py
#
# Copyright (c) 2006-2008, R Oudkerk
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-#
-# 1. Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-# 3. Neither the name of author nor the names of any contributors may be
-# used to endorse or promote products derived from this software
-# without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-# SUCH DAMAGE.
+# Licensed to PSF under a Contributor Agreement.
#
__all__ = [ 'Client', 'Listener', 'Pipe' ]
diff --git a/Lib/multiprocessing/forking.py b/Lib/multiprocessing/forking.py
deleted file mode 100644
index 449978a..0000000
--- a/Lib/multiprocessing/forking.py
+++ /dev/null
@@ -1,474 +0,0 @@
-#
-# Module for starting a process object using os.fork() or CreateProcess()
-#
-# multiprocessing/forking.py
-#
-# Copyright (c) 2006-2008, R Oudkerk
-# Licensed to PSF under a Contributor Agreement.
-#
-
-import os
-import sys
-import signal
-import errno
-
-from multiprocessing import util, process
-
-__all__ = ['Popen', 'assert_spawning', 'duplicate', 'close', 'ForkingPickler']
-
-#
-# Check that the current thread is spawning a child process
-#
-
-def assert_spawning(self):
- if not Popen.thread_is_spawning():
- raise RuntimeError(
- '%s objects should only be shared between processes'
- ' through inheritance' % type(self).__name__
- )
-
-#
-# Try making some callable types picklable
-#
-
-from pickle import Pickler
-from copyreg import dispatch_table
-
-class ForkingPickler(Pickler):
- _extra_reducers = {}
- def __init__(self, *args):
- Pickler.__init__(self, *args)
- self.dispatch_table = dispatch_table.copy()
- self.dispatch_table.update(self._extra_reducers)
- @classmethod
- def register(cls, type, reduce):
- cls._extra_reducers[type] = reduce
-
-def _reduce_method(m):
- if m.__self__ is None:
- return getattr, (m.__class__, m.__func__.__name__)
- else:
- return getattr, (m.__self__, m.__func__.__name__)
-class _C:
- def f(self):
- pass
-ForkingPickler.register(type(_C().f), _reduce_method)
-
-
-def _reduce_method_descriptor(m):
- return getattr, (m.__objclass__, m.__name__)
-ForkingPickler.register(type(list.append), _reduce_method_descriptor)
-ForkingPickler.register(type(int.__add__), _reduce_method_descriptor)
-
-try:
- from functools import partial
-except ImportError:
- pass
-else:
- def _reduce_partial(p):
- return _rebuild_partial, (p.func, p.args, p.keywords or {})
- def _rebuild_partial(func, args, keywords):
- return partial(func, *args, **keywords)
- ForkingPickler.register(partial, _reduce_partial)
-
-#
-# Unix
-#
-
-if sys.platform != 'win32':
- duplicate = os.dup
- close = os.close
-
- #
- # We define a Popen class similar to the one from subprocess, but
- # whose constructor takes a process object as its argument.
- #
-
- class Popen(object):
-
- def __init__(self, process_obj):
- sys.stdout.flush()
- 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()
- code = process_obj._bootstrap()
- 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:
- while True:
- try:
- pid, sts = os.waitpid(self.pid, flag)
- except os.error as e:
- if e.errno == errno.EINTR:
- continue
- # Child process not yet created. See #1731717
- # e.errno == errno.ECHILD == 10
- return None
- else:
- break
- if pid == self.pid:
- if os.WIFSIGNALED(sts):
- self.returncode = -os.WTERMSIG(sts)
- else:
- assert os.WIFEXITED(sts)
- self.returncode = os.WEXITSTATUS(sts)
- return self.returncode
-
- def wait(self, timeout=None):
- if self.returncode is None:
- if timeout is not None:
- from multiprocessing.connection import wait
- if not wait([self.sentinel], timeout):
- return None
- # This shouldn't block if wait() 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:
- if self.wait(timeout=0.1) is None:
- raise
-
- @staticmethod
- def thread_is_spawning():
- return False
-
-#
-# Windows
-#
-
-else:
- import _thread
- import msvcrt
- import _winapi
-
- from pickle import load, HIGHEST_PROTOCOL
-
- def dump(obj, file, protocol=None):
- ForkingPickler(file, protocol).dump(obj)
-
- #
- #
- #
-
- TERMINATE = 0x10000
- WINEXE = (sys.platform == 'win32' and getattr(sys, 'frozen', False))
- WINSERVICE = sys.executable.lower().endswith("pythonservice.exe")
-
- close = _winapi.CloseHandle
-
- #
- # _python_exe is the assumed path to the python executable.
- # People embedding Python want to modify it.
- #
-
- if WINSERVICE:
- _python_exe = os.path.join(sys.exec_prefix, 'python.exe')
- else:
- _python_exe = sys.executable
-
- def set_executable(exe):
- global _python_exe
- _python_exe = exe
-
- #
- #
- #
-
- def duplicate(handle, target_process=None, inheritable=False):
- if target_process is None:
- target_process = _winapi.GetCurrentProcess()
- return _winapi.DuplicateHandle(
- _winapi.GetCurrentProcess(), handle, target_process,
- 0, inheritable, _winapi.DUPLICATE_SAME_ACCESS
- )
-
- #
- # We define a Popen class similar to the one from subprocess, but
- # whose constructor takes a process object as its argument.
- #
-
- class Popen(object):
- '''
- Start a subprocess to run the code of a process object
- '''
- _tls = _thread._local()
-
- def __init__(self, process_obj):
- cmd = ' '.join('"%s"' % x for x in get_command_line())
- prep_data = get_preparation_data(process_obj._name)
-
- # create pipe for communication with child
- rfd, wfd = os.pipe()
-
- # get handle for read end of the pipe and make it inheritable
- rhandle = duplicate(msvcrt.get_osfhandle(rfd), inheritable=True)
- os.close(rfd)
-
- with open(wfd, 'wb', closefd=True) as to_child:
- # start process
- try:
- hp, ht, pid, tid = _winapi.CreateProcess(
- _python_exe, cmd + (' %s' % rhandle),
- None, None, 1, 0, None, None, None
- )
- _winapi.CloseHandle(ht)
- finally:
- close(rhandle)
-
- # set attributes of self
- self.pid = pid
- self.returncode = None
- self._handle = hp
- self.sentinel = int(hp)
- util.Finalize(self, _winapi.CloseHandle, (self.sentinel,))
-
- # send information to child
- Popen._tls.process_handle = int(hp)
- try:
- dump(prep_data, to_child, HIGHEST_PROTOCOL)
- dump(process_obj, to_child, HIGHEST_PROTOCOL)
- finally:
- del Popen._tls.process_handle
-
- @staticmethod
- def thread_is_spawning():
- return getattr(Popen._tls, 'process_handle', None) is not None
-
- @staticmethod
- def duplicate_for_child(handle):
- return duplicate(handle, Popen._tls.process_handle)
-
- def wait(self, timeout=None):
- if self.returncode is None:
- if timeout is None:
- msecs = _winapi.INFINITE
- else:
- msecs = max(0, int(timeout * 1000 + 0.5))
-
- res = _winapi.WaitForSingleObject(int(self._handle), msecs)
- if res == _winapi.WAIT_OBJECT_0:
- code = _winapi.GetExitCodeProcess(self._handle)
- if code == TERMINATE:
- code = -signal.SIGTERM
- self.returncode = code
-
- return self.returncode
-
- def poll(self):
- return self.wait(timeout=0)
-
- def terminate(self):
- if self.returncode is None:
- try:
- _winapi.TerminateProcess(int(self._handle), TERMINATE)
- except OSError:
- if self.wait(timeout=1.0) is None:
- raise
-
- #
- #
- #
-
- def is_forking(argv):
- '''
- Return whether commandline indicates we are forking
- '''
- if len(argv) >= 2 and argv[1] == '--multiprocessing-fork':
- assert len(argv) == 3
- return True
- else:
- return False
-
-
- def freeze_support():
- '''
- Run code for process object if this in not the main process
- '''
- if is_forking(sys.argv):
- main()
- sys.exit()
-
-
- def get_command_line():
- '''
- Returns prefix of command line used for spawning a child process
- '''
- if getattr(process.current_process(), '_inheriting', False):
- raise RuntimeError('''
- Attempt to start a new process before the current process
- has finished its bootstrapping phase.
-
- This probably means that you are on Windows and you have
- forgotten to use the proper idiom in the main module:
-
- if __name__ == '__main__':
- freeze_support()
- ...
-
- The "freeze_support()" line can be omitted if the program
- is not going to be frozen to produce a Windows executable.''')
-
- if getattr(sys, 'frozen', False):
- return [sys.executable, '--multiprocessing-fork']
- else:
- prog = 'from multiprocessing.forking import main; main()'
- opts = util._args_from_interpreter_flags()
- return [_python_exe] + opts + ['-c', prog, '--multiprocessing-fork']
-
-
- def main():
- '''
- Run code specified by data received over pipe
- '''
- assert is_forking(sys.argv)
-
- handle = int(sys.argv[-1])
- fd = msvcrt.open_osfhandle(handle, os.O_RDONLY)
- from_parent = os.fdopen(fd, 'rb')
-
- process.current_process()._inheriting = True
- preparation_data = load(from_parent)
- prepare(preparation_data)
- self = load(from_parent)
- process.current_process()._inheriting = False
-
- from_parent.close()
-
- exitcode = self._bootstrap()
- sys.exit(exitcode)
-
-
- def get_preparation_data(name):
- '''
- Return info about parent needed by child to unpickle process object
- '''
- from .util import _logger, _log_to_stderr
-
- d = dict(
- name=name,
- sys_path=sys.path,
- sys_argv=sys.argv,
- log_to_stderr=_log_to_stderr,
- orig_dir=process.ORIGINAL_DIR,
- authkey=process.current_process().authkey,
- )
-
- if _logger is not None:
- d['log_level'] = _logger.getEffectiveLevel()
-
- if not WINEXE and not WINSERVICE:
- main_path = getattr(sys.modules['__main__'], '__file__', None)
- if not main_path and sys.argv[0] not in ('', '-c'):
- main_path = sys.argv[0]
- if main_path is not None:
- if not os.path.isabs(main_path) and \
- process.ORIGINAL_DIR is not None:
- main_path = os.path.join(process.ORIGINAL_DIR, main_path)
- d['main_path'] = os.path.normpath(main_path)
-
- return d
-
-#
-# Prepare current process
-#
-
-old_main_modules = []
-
-def prepare(data):
- '''
- Try to get current process ready to unpickle process object
- '''
- old_main_modules.append(sys.modules['__main__'])
-
- if 'name' in data:
- process.current_process().name = data['name']
-
- if 'authkey' in data:
- process.current_process()._authkey = data['authkey']
-
- if 'log_to_stderr' in data and data['log_to_stderr']:
- util.log_to_stderr()
-
- if 'log_level' in data:
- util.get_logger().setLevel(data['log_level'])
-
- if 'sys_path' in data:
- sys.path = data['sys_path']
-
- if 'sys_argv' in data:
- sys.argv = data['sys_argv']
-
- if 'dir' in data:
- os.chdir(data['dir'])
-
- if 'orig_dir' in data:
- process.ORIGINAL_DIR = data['orig_dir']
-
- if 'main_path' in data:
- # XXX (ncoghlan): The following code makes several bogus
- # assumptions regarding the relationship between __file__
- # and a module's real name. See PEP 302 and issue #10845
- main_path = data['main_path']
- main_name = os.path.splitext(os.path.basename(main_path))[0]
- if main_name == '__init__':
- main_name = os.path.basename(os.path.dirname(main_path))
-
- if main_name == '__main__':
- main_module = sys.modules['__main__']
- main_module.__file__ = main_path
- elif main_name != 'ipython':
- # Main modules not actually called __main__.py may
- # contain additional code that should still be executed
- import imp
-
- if main_path is None:
- dirs = None
- elif os.path.basename(main_path).startswith('__init__.py'):
- dirs = [os.path.dirname(os.path.dirname(main_path))]
- else:
- dirs = [os.path.dirname(main_path)]
-
- assert main_name not in sys.modules, main_name
- file, path_name, etc = imp.find_module(main_name, dirs)
- try:
- # We would like to do "imp.load_module('__main__', ...)"
- # here. However, that would cause 'if __name__ ==
- # "__main__"' clauses to be executed.
- main_module = imp.load_module(
- '__parents_main__', file, path_name, etc
- )
- finally:
- if file:
- file.close()
-
- sys.modules['__main__'] = main_module
- main_module.__name__ = '__main__'
-
- # Try to make the potentially picklable objects in
- # sys.modules['__main__'] realize they are in the main
- # module -- somewhat ugly.
- for obj in list(main_module.__dict__.values()):
- try:
- if obj.__module__ == '__parents_main__':
- obj.__module__ = '__main__'
- except Exception:
- pass
diff --git a/Lib/multiprocessing/forkserver.py b/Lib/multiprocessing/forkserver.py
new file mode 100644
index 0000000..387517e
--- /dev/null
+++ b/Lib/multiprocessing/forkserver.py
@@ -0,0 +1,267 @@
+import errno
+import os
+import selectors
+import signal
+import socket
+import struct
+import sys
+import threading
+
+from . import connection
+from . import process
+from . import reduction
+from . import semaphore_tracker
+from . import spawn
+from . import util
+
+__all__ = ['ensure_running', 'get_inherited_fds', 'connect_to_new_process',
+ 'set_forkserver_preload']
+
+#
+#
+#
+
+MAXFDS_TO_SEND = 256
+UNSIGNED_STRUCT = struct.Struct('Q') # large enough for pid_t
+
+#
+# Forkserver class
+#
+
+class ForkServer(object):
+
+ def __init__(self):
+ self._forkserver_address = None
+ self._forkserver_alive_fd = None
+ self._inherited_fds = None
+ self._lock = threading.Lock()
+ self._preload_modules = ['__main__']
+
+ def set_forkserver_preload(self, modules_names):
+ '''Set list of module names to try to load in forkserver process.'''
+ if not all(type(mod) is str for mod in self._preload_modules):
+ raise TypeError('module_names must be a list of strings')
+ self._preload_modules = modules_names
+
+ def get_inherited_fds(self):
+ '''Return list of fds inherited from parent process.
+
+ This returns None if the current process was not started by fork
+ server.
+ '''
+ return self._inherited_fds
+
+ def connect_to_new_process(self, fds):
+ '''Request forkserver to create a child process.
+
+ Returns a pair of fds (status_r, data_w). The calling process can read
+ the child process's pid and (eventually) its returncode from status_r.
+ The calling process should write to data_w the pickled preparation and
+ process data.
+ '''
+ self.ensure_running()
+ if len(fds) + 4 >= MAXFDS_TO_SEND:
+ raise ValueError('too many fds')
+ with socket.socket(socket.AF_UNIX) as client:
+ client.connect(self._forkserver_address)
+ parent_r, child_w = os.pipe()
+ child_r, parent_w = os.pipe()
+ allfds = [child_r, child_w, self._forkserver_alive_fd,
+ semaphore_tracker.getfd()]
+ allfds += fds
+ try:
+ reduction.sendfds(client, allfds)
+ return parent_r, parent_w
+ except:
+ os.close(parent_r)
+ os.close(parent_w)
+ raise
+ finally:
+ os.close(child_r)
+ os.close(child_w)
+
+ def ensure_running(self):
+ '''Make sure that a fork server is running.
+
+ This can be called from any process. Note that usually a child
+ process will just reuse the forkserver started by its parent, so
+ ensure_running() will do nothing.
+ '''
+ with self._lock:
+ semaphore_tracker.ensure_running()
+ if self._forkserver_alive_fd is not None:
+ return
+
+ cmd = ('from multiprocessing.forkserver import main; ' +
+ 'main(%d, %d, %r, **%r)')
+
+ if self._preload_modules:
+ desired_keys = {'main_path', 'sys_path'}
+ data = spawn.get_preparation_data('ignore')
+ data = dict((x,y) for (x,y) in data.items()
+ if x in desired_keys)
+ else:
+ data = {}
+
+ with socket.socket(socket.AF_UNIX) as listener:
+ address = connection.arbitrary_address('AF_UNIX')
+ listener.bind(address)
+ os.chmod(address, 0o600)
+ listener.listen(100)
+
+ # all client processes own the write end of the "alive" pipe;
+ # when they all terminate the read end becomes ready.
+ alive_r, alive_w = os.pipe()
+ try:
+ fds_to_pass = [listener.fileno(), alive_r]
+ cmd %= (listener.fileno(), alive_r, self._preload_modules,
+ data)
+ exe = spawn.get_executable()
+ args = [exe] + util._args_from_interpreter_flags()
+ args += ['-c', cmd]
+ pid = util.spawnv_passfds(exe, args, fds_to_pass)
+ except:
+ os.close(alive_w)
+ raise
+ finally:
+ os.close(alive_r)
+ self._forkserver_address = address
+ self._forkserver_alive_fd = alive_w
+
+#
+#
+#
+
+def main(listener_fd, alive_r, preload, main_path=None, sys_path=None):
+ '''Run forkserver.'''
+ if preload:
+ if '__main__' in preload and main_path is not None:
+ process.current_process()._inheriting = True
+ try:
+ spawn.import_main_path(main_path)
+ finally:
+ del process.current_process()._inheriting
+ for modname in preload:
+ try:
+ __import__(modname)
+ except ImportError:
+ pass
+
+ # close sys.stdin
+ if sys.stdin is not None:
+ try:
+ sys.stdin.close()
+ sys.stdin = open(os.devnull)
+ except (OSError, ValueError):
+ pass
+
+ # ignoring SIGCHLD means no need to reap zombie processes
+ handler = signal.signal(signal.SIGCHLD, signal.SIG_IGN)
+ with socket.socket(socket.AF_UNIX, fileno=listener_fd) as listener, \
+ selectors.DefaultSelector() as selector:
+ _forkserver._forkserver_address = listener.getsockname()
+
+ selector.register(listener, selectors.EVENT_READ)
+ selector.register(alive_r, selectors.EVENT_READ)
+
+ while True:
+ try:
+ while True:
+ rfds = [key.fileobj for (key, events) in selector.select()]
+ if rfds:
+ break
+
+ if alive_r in rfds:
+ # EOF because no more client processes left
+ assert os.read(alive_r, 1) == b''
+ raise SystemExit
+
+ assert listener in rfds
+ with listener.accept()[0] as s:
+ code = 1
+ if os.fork() == 0:
+ try:
+ _serve_one(s, listener, alive_r, handler)
+ except Exception:
+ sys.excepthook(*sys.exc_info())
+ sys.stderr.flush()
+ finally:
+ os._exit(code)
+
+ except InterruptedError:
+ pass
+ except OSError as e:
+ if e.errno != errno.ECONNABORTED:
+ raise
+
+def _serve_one(s, listener, alive_r, handler):
+ # close unnecessary stuff and reset SIGCHLD handler
+ listener.close()
+ os.close(alive_r)
+ signal.signal(signal.SIGCHLD, handler)
+
+ # receive fds from parent process
+ fds = reduction.recvfds(s, MAXFDS_TO_SEND + 1)
+ s.close()
+ assert len(fds) <= MAXFDS_TO_SEND
+ (child_r, child_w, _forkserver._forkserver_alive_fd,
+ stfd, *_forkserver._inherited_fds) = fds
+ semaphore_tracker._semaphore_tracker._fd = stfd
+
+ # send pid to client processes
+ write_unsigned(child_w, os.getpid())
+
+ # reseed random number generator
+ if 'random' in sys.modules:
+ import random
+ random.seed()
+
+ # run process object received over pipe
+ code = spawn._main(child_r)
+
+ # write the exit code to the pipe
+ write_unsigned(child_w, code)
+
+#
+# Read and write unsigned numbers
+#
+
+def read_unsigned(fd):
+ data = b''
+ length = UNSIGNED_STRUCT.size
+ while len(data) < length:
+ while True:
+ try:
+ s = os.read(fd, length - len(data))
+ except InterruptedError:
+ pass
+ else:
+ break
+ if not s:
+ raise EOFError('unexpected EOF')
+ data += s
+ return UNSIGNED_STRUCT.unpack(data)[0]
+
+def write_unsigned(fd, n):
+ msg = UNSIGNED_STRUCT.pack(n)
+ while msg:
+ while True:
+ try:
+ nbytes = os.write(fd, msg)
+ except InterruptedError:
+ pass
+ else:
+ break
+ if nbytes == 0:
+ raise RuntimeError('should not get here')
+ msg = msg[nbytes:]
+
+#
+#
+#
+
+_forkserver = ForkServer()
+ensure_running = _forkserver.ensure_running
+get_inherited_fds = _forkserver.get_inherited_fds
+connect_to_new_process = _forkserver.connect_to_new_process
+set_forkserver_preload = _forkserver.set_forkserver_preload
diff --git a/Lib/multiprocessing/heap.py b/Lib/multiprocessing/heap.py
index e63fdb8..344a45f 100644
--- a/Lib/multiprocessing/heap.py
+++ b/Lib/multiprocessing/heap.py
@@ -11,12 +11,12 @@ import bisect
import mmap
import os
import sys
+import tempfile
import threading
-import itertools
-import _multiprocessing
-from multiprocessing.util import Finalize, info
-from multiprocessing.forking import assert_spawning
+from . import context
+from . import reduction
+from . import util
__all__ = ['BufferWrapper']
@@ -30,17 +30,25 @@ if sys.platform == 'win32':
class Arena(object):
- _counter = itertools.count()
+ _rand = tempfile._RandomNameSequence()
def __init__(self, size):
self.size = size
- self.name = 'pym-%d-%d' % (os.getpid(), next(Arena._counter))
- self.buffer = mmap.mmap(-1, self.size, tagname=self.name)
- assert _winapi.GetLastError() == 0, 'tagname already in use'
+ for i in range(100):
+ name = 'pym-%d-%s' % (os.getpid(), next(self._rand))
+ buf = mmap.mmap(-1, size, tagname=name)
+ if _winapi.GetLastError() == 0:
+ break
+ # We have reopened a preexisting mmap.
+ buf.close()
+ else:
+ raise FileExistsError('Cannot find name for new mmap')
+ self.name = name
+ self.buffer = buf
self._state = (self.size, self.name)
def __getstate__(self):
- assert_spawning(self)
+ context.assert_spawning(self)
return self._state
def __setstate__(self, state):
@@ -52,10 +60,28 @@ else:
class Arena(object):
- def __init__(self, size):
- self.buffer = mmap.mmap(-1, size)
+ def __init__(self, size, fd=-1):
self.size = size
- self.name = None
+ self.fd = fd
+ if fd == -1:
+ self.fd, name = tempfile.mkstemp(
+ prefix='pym-%d-'%os.getpid(), dir=util.get_temp_dir())
+ os.unlink(name)
+ util.Finalize(self, os.close, (self.fd,))
+ with open(self.fd, 'wb', closefd=False) as f:
+ f.write(b'\0'*size)
+ self.buffer = mmap.mmap(self.fd, self.size)
+
+ def reduce_arena(a):
+ if a.fd == -1:
+ raise ValueError('Arena is unpicklable because '
+ 'forking was enabled when it was created')
+ return rebuild_arena, (a.size, reduction.DupFd(a.fd))
+
+ def rebuild_arena(size, dupfd):
+ return Arena(size, dupfd.detach())
+
+ reduction.register(Arena, reduce_arena)
#
# Class allowing allocation of chunks of memory from arenas
@@ -90,7 +116,7 @@ class Heap(object):
if i == len(self._lengths):
length = self._roundup(max(self._size, size), mmap.PAGESIZE)
self._size *= 2
- info('allocating a new mmap of length %d', length)
+ util.info('allocating a new mmap of length %d', length)
arena = Arena(length)
self._arenas.append(arena)
return (arena, 0, length)
@@ -216,7 +242,7 @@ class BufferWrapper(object):
assert 0 <= size < sys.maxsize
block = BufferWrapper._heap.malloc(size)
self._state = (block, size)
- Finalize(self, BufferWrapper._heap.free, args=(block,))
+ util.Finalize(self, BufferWrapper._heap.free, args=(block,))
def create_memoryview(self):
(arena, start, stop), size = self._state
diff --git a/Lib/multiprocessing/managers.py b/Lib/multiprocessing/managers.py
index 96056b0..66d46fc 100644
--- a/Lib/multiprocessing/managers.py
+++ b/Lib/multiprocessing/managers.py
@@ -19,11 +19,16 @@ import threading
import array
import queue
-from traceback import format_exc
-from multiprocessing import Process, current_process, active_children, Pool, util, connection
-from multiprocessing.process import AuthenticationString
-from multiprocessing.forking import Popen, ForkingPickler
from time import time as _time
+from traceback import format_exc
+
+from . import connection
+from . import context
+from . import pool
+from . import process
+from . import reduction
+from . import util
+from . import get_context
#
# Register some things for pickling
@@ -31,16 +36,14 @@ from time import time as _time
def reduce_array(a):
return array.array, (a.typecode, a.tobytes())
-ForkingPickler.register(array.array, reduce_array)
+reduction.register(array.array, reduce_array)
view_types = [type(getattr({}, name)()) for name in ('items','keys','values')]
if view_types[0] is not list: # only needed in Py3.0
def rebuild_as_list(obj):
return list, (list(obj),)
for view_type in view_types:
- ForkingPickler.register(view_type, rebuild_as_list)
- import copyreg
- copyreg.pickle(view_type, rebuild_as_list)
+ reduction.register(view_type, rebuild_as_list)
#
# Type for identifying shared objects
@@ -130,7 +133,7 @@ class Server(object):
def __init__(self, registry, address, authkey, serializer):
assert isinstance(authkey, bytes)
self.registry = registry
- self.authkey = AuthenticationString(authkey)
+ self.authkey = process.AuthenticationString(authkey)
Listener, Client = listener_client[serializer]
# do authentication later
@@ -146,7 +149,7 @@ class Server(object):
Run the server forever
'''
self.stop_event = threading.Event()
- current_process()._manager_server = self
+ process.current_process()._manager_server = self
try:
accepter = threading.Thread(target=self.accepter)
accepter.daemon = True
@@ -167,7 +170,7 @@ class Server(object):
while True:
try:
c = self.listener.accept()
- except (OSError, IOError):
+ except OSError:
continue
t = threading.Thread(target=self.handle_request, args=(c,))
t.daemon = True
@@ -436,15 +439,17 @@ class BaseManager(object):
_registry = {}
_Server = Server
- def __init__(self, address=None, authkey=None, serializer='pickle'):
+ def __init__(self, address=None, authkey=None, serializer='pickle',
+ ctx=None):
if authkey is None:
- authkey = current_process().authkey
+ authkey = process.current_process().authkey
self._address = address # XXX not final address if eg ('', 0)
- self._authkey = AuthenticationString(authkey)
+ self._authkey = process.AuthenticationString(authkey)
self._state = State()
self._state.value = State.INITIAL
self._serializer = serializer
self._Listener, self._Client = listener_client[serializer]
+ self._ctx = ctx or get_context()
def get_server(self):
'''
@@ -476,7 +481,7 @@ class BaseManager(object):
reader, writer = connection.Pipe(duplex=False)
# spawn process which runs a server
- self._process = Process(
+ self._process = self._ctx.Process(
target=type(self)._run_server,
args=(self._registry, self._address, self._authkey,
self._serializer, writer, initializer, initargs),
@@ -691,11 +696,11 @@ class BaseProxy(object):
self._Client = listener_client[serializer][1]
if authkey is not None:
- self._authkey = AuthenticationString(authkey)
+ self._authkey = process.AuthenticationString(authkey)
elif self._manager is not None:
self._authkey = self._manager._authkey
else:
- self._authkey = current_process().authkey
+ self._authkey = process.current_process().authkey
if incref:
self._incref()
@@ -704,7 +709,7 @@ class BaseProxy(object):
def _connect(self):
util.debug('making connection to manager')
- name = current_process().name
+ name = process.current_process().name
if threading.current_thread().name != 'MainThread':
name += '|' + threading.current_thread().name
conn = self._Client(self._token.address, authkey=self._authkey)
@@ -798,7 +803,7 @@ class BaseProxy(object):
def __reduce__(self):
kwds = {}
- if Popen.thread_is_spawning():
+ if context.get_spawning_popen() is not None:
kwds['authkey'] = self._authkey
if getattr(self, '_isauto', False):
@@ -835,14 +840,14 @@ def RebuildProxy(func, token, serializer, kwds):
If possible the shared object is returned, or otherwise a proxy for it.
'''
- server = getattr(current_process(), '_manager_server', None)
+ server = getattr(process.current_process(), '_manager_server', None)
if server and server.address == token.address:
return server.id_to_obj[token.id][0]
else:
incref = (
kwds.pop('incref', True) and
- not getattr(current_process(), '_inheriting', False)
+ not getattr(process.current_process(), '_inheriting', False)
)
return func(token, serializer, incref=incref, **kwds)
@@ -889,7 +894,7 @@ def AutoProxy(token, serializer, manager=None, authkey=None,
if authkey is None and manager is not None:
authkey = manager._authkey
if authkey is None:
- authkey = current_process().authkey
+ authkey = process.current_process().authkey
ProxyType = MakeProxyType('AutoProxy[%s]' % token.typeid, exposed)
proxy = ProxyType(token, serializer, manager=manager, authkey=authkey,
@@ -1072,17 +1077,22 @@ ArrayProxy = MakeProxyType('ArrayProxy', (
))
-PoolProxy = MakeProxyType('PoolProxy', (
+BasePoolProxy = MakeProxyType('PoolProxy', (
'apply', 'apply_async', 'close', 'imap', 'imap_unordered', 'join',
- 'map', 'map_async', 'starmap', 'starmap_async', 'terminate'
+ 'map', 'map_async', 'starmap', 'starmap_async', 'terminate',
))
-PoolProxy._method_to_typeid_ = {
+BasePoolProxy._method_to_typeid_ = {
'apply_async': 'AsyncResult',
'map_async': 'AsyncResult',
'starmap_async': 'AsyncResult',
'imap': 'Iterator',
'imap_unordered': 'Iterator'
}
+class PoolProxy(BasePoolProxy):
+ def __enter__(self):
+ return self
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ self.terminate()
#
# Definition of SyncManager
@@ -1109,7 +1119,7 @@ SyncManager.register('BoundedSemaphore', threading.BoundedSemaphore,
AcquirerProxy)
SyncManager.register('Condition', threading.Condition, ConditionProxy)
SyncManager.register('Barrier', threading.Barrier, BarrierProxy)
-SyncManager.register('Pool', Pool, PoolProxy)
+SyncManager.register('Pool', pool.Pool, PoolProxy)
SyncManager.register('list', list, ListProxy)
SyncManager.register('dict', dict, DictProxy)
SyncManager.register('Value', Value, ValueProxy)
diff --git a/Lib/multiprocessing/pool.py b/Lib/multiprocessing/pool.py
index 0f2dab4..8832a5c 100644
--- a/Lib/multiprocessing/pool.py
+++ b/Lib/multiprocessing/pool.py
@@ -7,7 +7,7 @@
# Licensed to PSF under a Contributor Agreement.
#
-__all__ = ['Pool']
+__all__ = ['Pool', 'ThreadPool']
#
# Imports
@@ -17,10 +17,14 @@ import threading
import queue
import itertools
import collections
+import os
import time
+import traceback
-from multiprocessing import Process, cpu_count, TimeoutError
-from multiprocessing.util import Finalize, debug
+# If threading is available then ThreadPool should be provided. Therefore
+# we avoid top-level imports which are liable to fail on some systems.
+from . import util
+from . import get_context, TimeoutError
#
# Constants representing the state of a pool
@@ -43,6 +47,29 @@ def starmapstar(args):
return list(itertools.starmap(args[0], args[1]))
#
+# Hack to embed stringification of remote traceback in local traceback
+#
+
+class RemoteTraceback(Exception):
+ def __init__(self, tb):
+ self.tb = tb
+ def __str__(self):
+ return self.tb
+
+class ExceptionWithTraceback:
+ def __init__(self, exc, tb):
+ tb = traceback.format_exception(type(exc), exc, tb)
+ tb = ''.join(tb)
+ self.exc = exc
+ self.tb = '\n"""\n%s"""' % tb
+ def __reduce__(self):
+ return rebuild_exc, (self.exc, self.tb)
+
+def rebuild_exc(exc, tb):
+ exc.__cause__ = RemoteTraceback(tb)
+ return exc
+
+#
# Code run by worker processes
#
@@ -63,7 +90,8 @@ class MaybeEncodingError(Exception):
return "<MaybeEncodingError: %s>" % str(self)
-def worker(inqueue, outqueue, initializer=None, initargs=(), maxtasks=None):
+def worker(inqueue, outqueue, initializer=None, initargs=(), maxtasks=None,
+ wrap_exception=False):
assert maxtasks is None or (type(maxtasks) == int and maxtasks > 0)
put = outqueue.put
get = inqueue.get
@@ -78,28 +106,30 @@ def worker(inqueue, outqueue, initializer=None, initargs=(), maxtasks=None):
while maxtasks is None or (maxtasks and completed < maxtasks):
try:
task = get()
- except (EOFError, IOError):
- debug('worker got EOFError or IOError -- exiting')
+ except (EOFError, OSError):
+ util.debug('worker got EOFError or OSError -- exiting')
break
if task is None:
- debug('worker got sentinel -- exiting')
+ util.debug('worker got sentinel -- exiting')
break
job, i, func, args, kwds = task
try:
result = (True, func(*args, **kwds))
except Exception as e:
+ if wrap_exception:
+ e = ExceptionWithTraceback(e, e.__traceback__)
result = (False, e)
try:
put((job, i, result))
except Exception as e:
wrapped = MaybeEncodingError(e, result[1])
- debug("Possible encoding error while sending result: %s" % (
+ util.debug("Possible encoding error while sending result: %s" % (
wrapped))
put((job, i, (False, wrapped)))
completed += 1
- debug('worker exiting after %d tasks' % completed)
+ util.debug('worker exiting after %d tasks' % completed)
#
# Class representing a process pool
@@ -109,10 +139,14 @@ class Pool(object):
'''
Class which supports an async version of applying functions to arguments.
'''
- Process = Process
+ _wrap_exception = True
+
+ def Process(self, *args, **kwds):
+ return self._ctx.Process(*args, **kwds)
def __init__(self, processes=None, initializer=None, initargs=(),
- maxtasksperchild=None):
+ maxtasksperchild=None, context=None):
+ self._ctx = context or get_context()
self._setup_queues()
self._taskqueue = queue.Queue()
self._cache = {}
@@ -122,10 +156,7 @@ class Pool(object):
self._initargs = initargs
if processes is None:
- try:
- processes = cpu_count()
- except NotImplementedError:
- processes = 1
+ processes = os.cpu_count() or 1
if processes < 1:
raise ValueError("Number of processes must be at least 1")
@@ -162,7 +193,7 @@ class Pool(object):
self._result_handler._state = RUN
self._result_handler.start()
- self._terminate = Finalize(
+ self._terminate = util.Finalize(
self, self._terminate_pool,
args=(self._taskqueue, self._inqueue, self._outqueue, self._pool,
self._worker_handler, self._task_handler,
@@ -179,7 +210,7 @@ class Pool(object):
worker = self._pool[i]
if worker.exitcode is not None:
# worker exited
- debug('cleaning up worker %d' % i)
+ util.debug('cleaning up worker %d' % i)
worker.join()
cleaned = True
del self._pool[i]
@@ -193,13 +224,14 @@ class Pool(object):
w = self.Process(target=worker,
args=(self._inqueue, self._outqueue,
self._initializer,
- self._initargs, self._maxtasksperchild)
+ self._initargs, self._maxtasksperchild,
+ self._wrap_exception)
)
self._pool.append(w)
w.name = w.name.replace('Process', 'PoolWorker')
w.daemon = True
w.start()
- debug('added worker')
+ util.debug('added worker')
def _maintain_pool(self):
"""Clean up any exited workers and start replacements for them.
@@ -208,9 +240,8 @@ class Pool(object):
self._repopulate_pool()
def _setup_queues(self):
- from .queues import SimpleQueue
- self._inqueue = SimpleQueue()
- self._outqueue = SimpleQueue()
+ self._inqueue = self._ctx.SimpleQueue()
+ self._outqueue = self._ctx.SimpleQueue()
self._quick_put = self._inqueue._writer.send
self._quick_get = self._outqueue._reader.recv
@@ -336,7 +367,7 @@ class Pool(object):
time.sleep(0.1)
# send sentinel to stop workers
pool._taskqueue.put(None)
- debug('worker handler exiting')
+ util.debug('worker handler exiting')
@staticmethod
def _handle_tasks(taskqueue, put, outqueue, pool, cache):
@@ -346,7 +377,7 @@ class Pool(object):
i = -1
for i, task in enumerate(taskseq):
if thread._state:
- debug('task handler found thread._state != RUN')
+ util.debug('task handler found thread._state != RUN')
break
try:
put(task)
@@ -358,27 +389,27 @@ class Pool(object):
pass
else:
if set_length:
- debug('doing set_length()')
+ util.debug('doing set_length()')
set_length(i+1)
continue
break
else:
- debug('task handler got sentinel')
+ util.debug('task handler got sentinel')
try:
# tell result handler to finish when cache is empty
- debug('task handler sending sentinel to result handler')
+ util.debug('task handler sending sentinel to result handler')
outqueue.put(None)
# tell workers there is no more work
- debug('task handler sending sentinel to workers')
+ util.debug('task handler sending sentinel to workers')
for p in pool:
put(None)
- except IOError:
- debug('task handler got IOError when sending sentinels')
+ except OSError:
+ util.debug('task handler got OSError when sending sentinels')
- debug('task handler exiting')
+ util.debug('task handler exiting')
@staticmethod
def _handle_results(outqueue, get, cache):
@@ -387,17 +418,17 @@ class Pool(object):
while 1:
try:
task = get()
- except (IOError, EOFError):
- debug('result handler got EOFError/IOError -- exiting')
+ except (OSError, EOFError):
+ util.debug('result handler got EOFError/OSError -- exiting')
return
if thread._state:
assert thread._state == TERMINATE
- debug('result handler found thread._state=TERMINATE')
+ util.debug('result handler found thread._state=TERMINATE')
break
if task is None:
- debug('result handler got sentinel')
+ util.debug('result handler got sentinel')
break
job, i, obj = task
@@ -409,12 +440,12 @@ class Pool(object):
while cache and thread._state != TERMINATE:
try:
task = get()
- except (IOError, EOFError):
- debug('result handler got EOFError/IOError -- exiting')
+ except (OSError, EOFError):
+ util.debug('result handler got EOFError/OSError -- exiting')
return
if task is None:
- debug('result handler ignoring extra sentinel')
+ util.debug('result handler ignoring extra sentinel')
continue
job, i, obj = task
try:
@@ -423,7 +454,7 @@ class Pool(object):
pass
if hasattr(outqueue, '_reader'):
- debug('ensuring that outqueue is not full')
+ util.debug('ensuring that outqueue is not full')
# If we don't make room available in outqueue then
# attempts to add the sentinel (None) to outqueue may
# block. There is guaranteed to be no more than 2 sentinels.
@@ -432,10 +463,10 @@ class Pool(object):
if not outqueue._reader.poll():
break
get()
- except (IOError, EOFError):
+ except (OSError, EOFError):
pass
- debug('result handler exiting: len(cache)=%s, thread._state=%s',
+ util.debug('result handler exiting: len(cache)=%s, thread._state=%s',
len(cache), thread._state)
@staticmethod
@@ -453,19 +484,19 @@ class Pool(object):
)
def close(self):
- debug('closing pool')
+ util.debug('closing pool')
if self._state == RUN:
self._state = CLOSE
self._worker_handler._state = CLOSE
def terminate(self):
- debug('terminating pool')
+ util.debug('terminating pool')
self._state = TERMINATE
self._worker_handler._state = TERMINATE
self._terminate()
def join(self):
- debug('joining pool')
+ util.debug('joining pool')
assert self._state in (CLOSE, TERMINATE)
self._worker_handler.join()
self._task_handler.join()
@@ -476,7 +507,7 @@ class Pool(object):
@staticmethod
def _help_stuff_finish(inqueue, task_handler, size):
# task_handler may be blocked trying to put items on inqueue
- debug('removing tasks from inqueue until task handler finished')
+ util.debug('removing tasks from inqueue until task handler finished')
inqueue._rlock.acquire()
while task_handler.is_alive() and inqueue._reader.poll():
inqueue._reader.recv()
@@ -486,12 +517,12 @@ class Pool(object):
def _terminate_pool(cls, taskqueue, inqueue, outqueue, pool,
worker_handler, task_handler, result_handler, cache):
# this is guaranteed to only be called once
- debug('finalizing pool')
+ util.debug('finalizing pool')
worker_handler._state = TERMINATE
task_handler._state = TERMINATE
- debug('helping task handler/workers to finish')
+ util.debug('helping task handler/workers to finish')
cls._help_stuff_finish(inqueue, task_handler, len(pool))
assert result_handler.is_alive() or len(cache) == 0
@@ -501,31 +532,31 @@ class Pool(object):
# We must wait for the worker handler to exit before terminating
# workers because we don't want workers to be restarted behind our back.
- debug('joining worker handler')
+ util.debug('joining worker handler')
if threading.current_thread() is not worker_handler:
worker_handler.join()
# Terminate workers which haven't already finished.
if pool and hasattr(pool[0], 'terminate'):
- debug('terminating workers')
+ util.debug('terminating workers')
for p in pool:
if p.exitcode is None:
p.terminate()
- debug('joining task handler')
+ util.debug('joining task handler')
if threading.current_thread() is not task_handler:
task_handler.join()
- debug('joining result handler')
+ util.debug('joining result handler')
if threading.current_thread() is not result_handler:
result_handler.join()
if pool and hasattr(pool[0], 'terminate'):
- debug('joining pool workers')
+ util.debug('joining pool workers')
for p in pool:
if p.is_alive():
# worker has not yet exited
- debug('cleaning up worker %d' % p.pid)
+ util.debug('cleaning up worker %d' % p.pid)
p.join()
def __enter__(self):
@@ -710,8 +741,12 @@ class IMapUnorderedIterator(IMapIterator):
#
class ThreadPool(Pool):
+ _wrap_exception = False
- from .dummy import Process
+ @staticmethod
+ def Process(*args, **kwds):
+ from .dummy import Process
+ return Process(*args, **kwds)
def __init__(self, processes=None, initializer=None, initargs=()):
Pool.__init__(self, processes, initializer, initargs)
diff --git a/Lib/multiprocessing/popen_fork.py b/Lib/multiprocessing/popen_fork.py
new file mode 100644
index 0000000..367e72e
--- /dev/null
+++ b/Lib/multiprocessing/popen_fork.py
@@ -0,0 +1,83 @@
+import os
+import sys
+import signal
+import errno
+
+from . import util
+
+__all__ = ['Popen']
+
+#
+# Start child process using fork
+#
+
+class Popen(object):
+ method = 'fork'
+
+ def __init__(self, process_obj):
+ sys.stdout.flush()
+ sys.stderr.flush()
+ self.returncode = None
+ self._launch(process_obj)
+
+ def duplicate_for_child(self, fd):
+ return fd
+
+ def poll(self, flag=os.WNOHANG):
+ if self.returncode is None:
+ while True:
+ try:
+ pid, sts = os.waitpid(self.pid, flag)
+ except OSError as e:
+ if e.errno == errno.EINTR:
+ continue
+ # Child process not yet created. See #1731717
+ # e.errno == errno.ECHILD == 10
+ return None
+ else:
+ break
+ if pid == self.pid:
+ if os.WIFSIGNALED(sts):
+ self.returncode = -os.WTERMSIG(sts)
+ else:
+ assert os.WIFEXITED(sts)
+ self.returncode = os.WEXITSTATUS(sts)
+ return self.returncode
+
+ def wait(self, timeout=None):
+ if self.returncode is None:
+ if timeout is not None:
+ from multiprocessing.connection import wait
+ if not wait([self.sentinel], timeout):
+ return None
+ # This shouldn't block if wait() 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 ProcessLookupError:
+ pass
+ except OSError:
+ if self.wait(timeout=0.1) is None:
+ raise
+
+ def _launch(self, process_obj):
+ code = 1
+ parent_r, child_w = os.pipe()
+ self.pid = os.fork()
+ if self.pid == 0:
+ try:
+ os.close(parent_r)
+ if 'random' in sys.modules:
+ import random
+ random.seed()
+ code = process_obj._bootstrap()
+ finally:
+ os._exit(code)
+ else:
+ os.close(child_w)
+ util.Finalize(self, os.close, (parent_r,))
+ self.sentinel = parent_r
diff --git a/Lib/multiprocessing/popen_forkserver.py b/Lib/multiprocessing/popen_forkserver.py
new file mode 100644
index 0000000..e792194
--- /dev/null
+++ b/Lib/multiprocessing/popen_forkserver.py
@@ -0,0 +1,69 @@
+import io
+import os
+
+from . import reduction
+if not reduction.HAVE_SEND_HANDLE:
+ raise ImportError('No support for sending fds between processes')
+from . import context
+from . import forkserver
+from . import popen_fork
+from . import spawn
+from . import util
+
+
+__all__ = ['Popen']
+
+#
+# Wrapper for an fd used while launching a process
+#
+
+class _DupFd(object):
+ def __init__(self, ind):
+ self.ind = ind
+ def detach(self):
+ return forkserver.get_inherited_fds()[self.ind]
+
+#
+# Start child process using a server process
+#
+
+class Popen(popen_fork.Popen):
+ method = 'forkserver'
+ DupFd = _DupFd
+
+ def __init__(self, process_obj):
+ self._fds = []
+ super().__init__(process_obj)
+
+ def duplicate_for_child(self, fd):
+ self._fds.append(fd)
+ return len(self._fds) - 1
+
+ def _launch(self, process_obj):
+ prep_data = spawn.get_preparation_data(process_obj._name)
+ buf = io.BytesIO()
+ context.set_spawning_popen(self)
+ try:
+ reduction.dump(prep_data, buf)
+ reduction.dump(process_obj, buf)
+ finally:
+ context.set_spawning_popen(None)
+
+ self.sentinel, w = forkserver.connect_to_new_process(self._fds)
+ util.Finalize(self, os.close, (self.sentinel,))
+ with open(w, 'wb', closefd=True) as f:
+ f.write(buf.getbuffer())
+ self.pid = forkserver.read_unsigned(self.sentinel)
+
+ def poll(self, flag=os.WNOHANG):
+ if self.returncode is None:
+ from multiprocessing.connection import wait
+ timeout = 0 if flag == os.WNOHANG else None
+ if not wait([self.sentinel], timeout):
+ return None
+ try:
+ self.returncode = forkserver.read_unsigned(self.sentinel)
+ except (OSError, EOFError):
+ # The process ended abnormally perhaps because of a signal
+ self.returncode = 255
+ return self.returncode
diff --git a/Lib/multiprocessing/popen_spawn_posix.py b/Lib/multiprocessing/popen_spawn_posix.py
new file mode 100644
index 0000000..6b0a8d6
--- /dev/null
+++ b/Lib/multiprocessing/popen_spawn_posix.py
@@ -0,0 +1,69 @@
+import io
+import os
+
+from . import context
+from . import popen_fork
+from . import reduction
+from . import spawn
+from . import util
+
+__all__ = ['Popen']
+
+
+#
+# Wrapper for an fd used while launching a process
+#
+
+class _DupFd(object):
+ def __init__(self, fd):
+ self.fd = fd
+ def detach(self):
+ return self.fd
+
+#
+# Start child process using a fresh interpreter
+#
+
+class Popen(popen_fork.Popen):
+ method = 'spawn'
+ DupFd = _DupFd
+
+ def __init__(self, process_obj):
+ self._fds = []
+ super().__init__(process_obj)
+
+ def duplicate_for_child(self, fd):
+ self._fds.append(fd)
+ return fd
+
+ def _launch(self, process_obj):
+ from . import semaphore_tracker
+ tracker_fd = semaphore_tracker.getfd()
+ self._fds.append(tracker_fd)
+ prep_data = spawn.get_preparation_data(process_obj._name)
+ fp = io.BytesIO()
+ context.set_spawning_popen(self)
+ try:
+ reduction.dump(prep_data, fp)
+ reduction.dump(process_obj, fp)
+ finally:
+ context.set_spawning_popen(None)
+
+ parent_r = child_w = child_r = parent_w = None
+ try:
+ parent_r, child_w = os.pipe()
+ child_r, parent_w = os.pipe()
+ cmd = spawn.get_command_line(tracker_fd=tracker_fd,
+ pipe_handle=child_r)
+ self._fds.extend([child_r, child_w])
+ self.pid = util.spawnv_passfds(spawn.get_executable(),
+ cmd, self._fds)
+ self.sentinel = parent_r
+ with open(parent_w, 'wb', closefd=False) as f:
+ f.write(fp.getbuffer())
+ finally:
+ if parent_r is not None:
+ util.Finalize(self, os.close, (parent_r,))
+ for fd in (child_r, child_w, parent_w):
+ if fd is not None:
+ os.close(fd)
diff --git a/Lib/multiprocessing/popen_spawn_win32.py b/Lib/multiprocessing/popen_spawn_win32.py
new file mode 100644
index 0000000..3b53068
--- /dev/null
+++ b/Lib/multiprocessing/popen_spawn_win32.py
@@ -0,0 +1,99 @@
+import os
+import msvcrt
+import signal
+import sys
+import _winapi
+
+from . import context
+from . import spawn
+from . import reduction
+from . import util
+
+__all__ = ['Popen']
+
+#
+#
+#
+
+TERMINATE = 0x10000
+WINEXE = (sys.platform == 'win32' and getattr(sys, 'frozen', False))
+WINSERVICE = sys.executable.lower().endswith("pythonservice.exe")
+
+#
+# We define a Popen class similar to the one from subprocess, but
+# whose constructor takes a process object as its argument.
+#
+
+class Popen(object):
+ '''
+ Start a subprocess to run the code of a process object
+ '''
+ method = 'spawn'
+
+ def __init__(self, process_obj):
+ prep_data = spawn.get_preparation_data(process_obj._name)
+
+ # read end of pipe will be "stolen" by the child process
+ # -- see spawn_main() in spawn.py.
+ rhandle, whandle = _winapi.CreatePipe(None, 0)
+ wfd = msvcrt.open_osfhandle(whandle, 0)
+ cmd = spawn.get_command_line(parent_pid=os.getpid(),
+ pipe_handle=rhandle)
+ cmd = ' '.join('"%s"' % x for x in cmd)
+
+ with open(wfd, 'wb', closefd=True) as to_child:
+ # start process
+ try:
+ hp, ht, pid, tid = _winapi.CreateProcess(
+ spawn.get_executable(), cmd,
+ None, None, False, 0, None, None, None)
+ _winapi.CloseHandle(ht)
+ except:
+ _winapi.CloseHandle(rhandle)
+ raise
+
+ # set attributes of self
+ self.pid = pid
+ self.returncode = None
+ self._handle = hp
+ self.sentinel = int(hp)
+ util.Finalize(self, _winapi.CloseHandle, (self.sentinel,))
+
+ # send information to child
+ context.set_spawning_popen(self)
+ try:
+ reduction.dump(prep_data, to_child)
+ reduction.dump(process_obj, to_child)
+ finally:
+ context.set_spawning_popen(None)
+
+ def duplicate_for_child(self, handle):
+ assert self is context.get_spawning_popen()
+ return reduction.duplicate(handle, self.sentinel)
+
+ def wait(self, timeout=None):
+ if self.returncode is None:
+ if timeout is None:
+ msecs = _winapi.INFINITE
+ else:
+ msecs = max(0, int(timeout * 1000 + 0.5))
+
+ res = _winapi.WaitForSingleObject(int(self._handle), msecs)
+ if res == _winapi.WAIT_OBJECT_0:
+ code = _winapi.GetExitCodeProcess(self._handle)
+ if code == TERMINATE:
+ code = -signal.SIGTERM
+ self.returncode = code
+
+ return self.returncode
+
+ def poll(self):
+ return self.wait(timeout=0)
+
+ def terminate(self):
+ if self.returncode is None:
+ try:
+ _winapi.TerminateProcess(int(self._handle), TERMINATE)
+ except OSError:
+ if self.wait(timeout=1.0) is None:
+ raise
diff --git a/Lib/multiprocessing/process.py b/Lib/multiprocessing/process.py
index 3d32add..68959bf 100644
--- a/Lib/multiprocessing/process.py
+++ b/Lib/multiprocessing/process.py
@@ -7,7 +7,7 @@
# Licensed to PSF under a Contributor Agreement.
#
-__all__ = ['Process', 'current_process', 'active_children']
+__all__ = ['BaseProcess', 'current_process', 'active_children']
#
# Imports
@@ -43,7 +43,7 @@ def active_children():
Return list of process objects corresponding to live child processes
'''
_cleanup()
- return list(_current_process._children)
+ return list(_children)
#
#
@@ -51,33 +51,29 @@ def active_children():
def _cleanup():
# check for processes which have finished
- for p in list(_current_process._children):
+ for p in list(_children):
if p._popen.poll() is not None:
- _current_process._children.discard(p)
+ _children.discard(p)
#
# The `Process` class
#
-class Process(object):
+class BaseProcess(object):
'''
Process objects represent activity that is run in a separate process
- The class is analagous to `threading.Thread`
+ The class is analogous to `threading.Thread`
'''
- _Popen = None
+ def _Popen(self):
+ raise NotImplementedError
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)
+ count = next(_process_counter)
self._identity = _current_process._identity + (count,)
- self._authkey = _current_process._authkey
- if daemon is not None:
- self._daemonic = daemon
- else:
- self._daemonic = _current_process._daemonic
- self._tempdir = _current_process._tempdir
+ self._config = _current_process._config.copy()
self._parent_pid = os.getpid()
self._popen = None
self._target = target
@@ -85,6 +81,8 @@ class Process(object):
self._kwargs = dict(kwargs)
self._name = name or type(self).__name__ + '-' + \
':'.join(str(i) for i in self._identity)
+ if daemon is not None:
+ self.daemon = daemon
_dangling.add(self)
def run(self):
@@ -101,16 +99,12 @@ class Process(object):
assert self._popen is None, 'cannot start a process twice'
assert self._parent_pid == os.getpid(), \
'can only start a process object created by current process'
- assert not _current_process._daemonic, \
+ assert not _current_process._config.get('daemon'), \
'daemonic processes are not allowed to have children'
_cleanup()
- if self._Popen is not None:
- Popen = self._Popen
- else:
- from .forking import Popen
- self._popen = Popen(self)
+ self._popen = self._Popen(self)
self._sentinel = self._popen.sentinel
- _current_process._children.add(self)
+ _children.add(self)
def terminate(self):
'''
@@ -126,7 +120,7 @@ class Process(object):
assert self._popen is not None, 'can only join a started process'
res = self._popen.wait(timeout)
if res is not None:
- _current_process._children.discard(self)
+ _children.discard(self)
def is_alive(self):
'''
@@ -154,7 +148,7 @@ class Process(object):
'''
Return whether process is a daemon
'''
- return self._daemonic
+ return self._config.get('daemon', False)
@daemon.setter
def daemon(self, daemonic):
@@ -162,18 +156,18 @@ class Process(object):
Set whether process is a daemon
'''
assert self._popen is None, 'process has already started'
- self._daemonic = daemonic
+ self._config['daemon'] = daemonic
@property
def authkey(self):
- return self._authkey
+ return self._config['authkey']
@authkey.setter
def authkey(self, authkey):
'''
Set authorization key of process
'''
- self._authkey = AuthenticationString(authkey)
+ self._config['authkey'] = AuthenticationString(authkey)
@property
def exitcode(self):
@@ -227,17 +221,19 @@ class Process(object):
status = 'stopped[%s]' % _exitcode_to_name.get(status, status)
return '<%s(%s, %s%s)>' % (type(self).__name__, self._name,
- status, self._daemonic and ' daemon' or '')
+ status, self.daemon and ' daemon' or '')
##
def _bootstrap(self):
- from . import util
- global _current_process
+ from . import util, context
+ global _current_process, _process_counter, _children
try:
- self._children = set()
- self._counter = itertools.count(1)
+ if self._start_method is not None:
+ context._force_start_method(self._start_method)
+ _process_counter = itertools.count(1)
+ _children = set()
if sys.stdin is not None:
try:
sys.stdin.close()
@@ -285,8 +281,8 @@ class Process(object):
class AuthenticationString(bytes):
def __reduce__(self):
- from .forking import Popen
- if not Popen.thread_is_spawning():
+ from .context import get_spawning_popen
+ if get_spawning_popen() is None:
raise TypeError(
'Pickling an AuthenticationString object is '
'disallowed for security reasons'
@@ -297,20 +293,29 @@ class AuthenticationString(bytes):
# Create object representing the main process
#
-class _MainProcess(Process):
+class _MainProcess(BaseProcess):
def __init__(self):
self._identity = ()
- self._daemonic = False
self._name = 'MainProcess'
self._parent_pid = None
self._popen = None
- self._counter = itertools.count(1)
- self._children = set()
- self._authkey = AuthenticationString(os.urandom(32))
- self._tempdir = None
+ self._config = {'authkey': AuthenticationString(os.urandom(32)),
+ 'semprefix': '/mp'}
+ # Note that some versions of FreeBSD only allow named
+ # semaphores to have names of up to 14 characters. Therefore
+ # we choose a short prefix.
+ #
+ # On MacOSX in a sandbox it may be necessary to use a
+ # different prefix -- see #19478.
+ #
+ # Everything in self._config will be inherited by descendant
+ # processes.
+
_current_process = _MainProcess()
+_process_counter = itertools.count(1)
+_children = set()
del _MainProcess
#
diff --git a/Lib/multiprocessing/queues.py b/Lib/multiprocessing/queues.py
index 37271fb..f650771 100644
--- a/Lib/multiprocessing/queues.py
+++ b/Lib/multiprocessing/queues.py
@@ -18,11 +18,14 @@ import weakref
import errno
from queue import Empty, Full
+
import _multiprocessing
-from multiprocessing.connection import Pipe
-from multiprocessing.synchronize import Lock, BoundedSemaphore, Semaphore, Condition
-from multiprocessing.util import debug, info, Finalize, register_after_fork
-from multiprocessing.forking import assert_spawning
+
+from . import connection
+from . import context
+
+from .util import debug, info, Finalize, register_after_fork, is_exiting
+from .reduction import ForkingPickler
#
# Queue type using a pipe, buffer and thread
@@ -30,18 +33,18 @@ from multiprocessing.forking import assert_spawning
class Queue(object):
- def __init__(self, maxsize=0):
+ def __init__(self, maxsize=0, *, ctx):
if maxsize <= 0:
maxsize = _multiprocessing.SemLock.SEM_VALUE_MAX
self._maxsize = maxsize
- self._reader, self._writer = Pipe(duplex=False)
- self._rlock = Lock()
+ self._reader, self._writer = connection.Pipe(duplex=False)
+ self._rlock = ctx.Lock()
self._opid = os.getpid()
if sys.platform == 'win32':
self._wlock = None
else:
- self._wlock = Lock()
- self._sem = BoundedSemaphore(maxsize)
+ self._wlock = ctx.Lock()
+ self._sem = ctx.BoundedSemaphore(maxsize)
# For use by concurrent.futures
self._ignore_epipe = False
@@ -51,7 +54,7 @@ class Queue(object):
register_after_fork(self, Queue._after_fork)
def __getstate__(self):
- assert_spawning(self)
+ context.assert_spawning(self)
return (self._ignore_epipe, self._maxsize, self._reader, self._writer,
self._rlock, self._wlock, self._sem, self._opid)
@@ -69,8 +72,8 @@ class Queue(object):
self._joincancelled = False
self._closed = False
self._close = None
- self._send = self._writer.send
- self._recv = self._reader.recv
+ self._send_bytes = self._writer.send_bytes
+ self._recv_bytes = self._reader.recv_bytes
self._poll = self._reader.poll
def put(self, obj, block=True, timeout=None):
@@ -89,14 +92,9 @@ class Queue(object):
def get(self, block=True, timeout=None):
if block and timeout is None:
- self._rlock.acquire()
- try:
- res = self._recv()
- self._sem.release()
- return res
- finally:
- self._rlock.release()
-
+ with self._rlock:
+ res = self._recv_bytes()
+ self._sem.release()
else:
if block:
deadline = time.time() + timeout
@@ -109,11 +107,12 @@ class Queue(object):
raise Empty
elif not self._poll():
raise Empty
- res = self._recv()
+ res = self._recv_bytes()
self._sem.release()
- return res
finally:
self._rlock.release()
+ # unserialize the data after having released the lock
+ return ForkingPickler.loads(res)
def qsize(self):
# Raises NotImplementedError on Mac OSX because of broken sem_getvalue()
@@ -158,7 +157,7 @@ class Queue(object):
self._buffer.clear()
self._thread = threading.Thread(
target=Queue._feed,
- args=(self._buffer, self._notempty, self._send,
+ args=(self._buffer, self._notempty, self._send_bytes,
self._wlock, self._writer.close, self._ignore_epipe),
name='QueueFeederThread'
)
@@ -210,10 +209,8 @@ class Queue(object):
notempty.release()
@staticmethod
- def _feed(buffer, notempty, send, writelock, close, ignore_epipe):
+ def _feed(buffer, notempty, send_bytes, writelock, close, ignore_epipe):
debug('starting thread to feed data to pipe')
- from .util import is_exiting
-
nacquire = notempty.acquire
nrelease = notempty.release
nwait = notempty.wait
@@ -241,12 +238,14 @@ class Queue(object):
close()
return
+ # serialize the data before acquiring the lock
+ obj = ForkingPickler.dumps(obj)
if wacquire is None:
- send(obj)
+ send_bytes(obj)
else:
wacquire()
try:
- send(obj)
+ send_bytes(obj)
finally:
wrelease()
except IndexError:
@@ -279,10 +278,10 @@ _sentinel = object()
class JoinableQueue(Queue):
- def __init__(self, maxsize=0):
- Queue.__init__(self, maxsize)
- self._unfinished_tasks = Semaphore(0)
- self._cond = Condition()
+ def __init__(self, maxsize=0, *, ctx):
+ Queue.__init__(self, maxsize, ctx=ctx)
+ self._unfinished_tasks = ctx.Semaphore(0)
+ self._cond = ctx.Condition()
def __getstate__(self):
return Queue.__getstate__(self) + (self._cond, self._unfinished_tasks)
@@ -332,48 +331,37 @@ class JoinableQueue(Queue):
class SimpleQueue(object):
- def __init__(self):
- self._reader, self._writer = Pipe(duplex=False)
- self._rlock = Lock()
+ def __init__(self, *, ctx):
+ self._reader, self._writer = connection.Pipe(duplex=False)
+ self._rlock = ctx.Lock()
self._poll = self._reader.poll
if sys.platform == 'win32':
self._wlock = None
else:
- self._wlock = Lock()
- self._make_methods()
+ self._wlock = ctx.Lock()
def empty(self):
return not self._poll()
def __getstate__(self):
- assert_spawning(self)
+ context.assert_spawning(self)
return (self._reader, self._writer, self._rlock, self._wlock)
def __setstate__(self, state):
(self._reader, self._writer, self._rlock, self._wlock) = state
- self._make_methods()
- def _make_methods(self):
- recv = self._reader.recv
- racquire, rrelease = self._rlock.acquire, self._rlock.release
- def get():
- racquire()
- try:
- return recv()
- finally:
- rrelease()
- self.get = get
+ def get(self):
+ with self._rlock:
+ res = self._reader.recv_bytes()
+ # unserialize the data after having released the lock
+ return ForkingPickler.loads(res)
+ def put(self, obj):
+ # serialize the data before acquiring the lock
+ obj = ForkingPickler.dumps(obj)
if self._wlock is None:
# writes to a message oriented win32 pipe are atomic
- self.put = self._writer.send
+ self._writer.send_bytes(obj)
else:
- send = self._writer.send
- wacquire, wrelease = self._wlock.acquire, self._wlock.release
- def put(obj):
- wacquire()
- try:
- return send(obj)
- finally:
- wrelease()
- self.put = put
+ with self._wlock:
+ self._writer.send_bytes(obj)
diff --git a/Lib/multiprocessing/reduction.py b/Lib/multiprocessing/reduction.py
index 656fa8f..8f209b4 100644
--- a/Lib/multiprocessing/reduction.py
+++ b/Lib/multiprocessing/reduction.py
@@ -1,6 +1,5 @@
#
-# Module to allow connection and socket objects to be transferred
-# between processes
+# Module which deals with pickling of objects.
#
# multiprocessing/reduction.py
#
@@ -8,27 +7,56 @@
# Licensed to PSF under a Contributor Agreement.
#
-__all__ = ['reduce_socket', 'reduce_connection', 'send_handle', 'recv_handle']
-
+import copyreg
+import functools
+import io
import os
-import sys
+import pickle
import socket
-import threading
-import struct
-import signal
+import sys
-from multiprocessing import current_process
-from multiprocessing.util import register_after_fork, debug, sub_debug
-from multiprocessing.util import is_exiting, sub_warning
+from . import context
+__all__ = ['send_handle', 'recv_handle', 'ForkingPickler', 'register', 'dump']
+
+
+HAVE_SEND_HANDLE = (sys.platform == 'win32' or
+ (hasattr(socket, 'CMSG_LEN') and
+ hasattr(socket, 'SCM_RIGHTS') and
+ hasattr(socket.socket, 'sendmsg')))
#
+# Pickler subclass
#
-#
-if not(sys.platform == 'win32' or (hasattr(socket, 'CMSG_LEN') and
- hasattr(socket, 'SCM_RIGHTS'))):
- raise ImportError('pickling of connections not supported')
+class ForkingPickler(pickle.Pickler):
+ '''Pickler subclass used by multiprocessing.'''
+ _extra_reducers = {}
+ _copyreg_dispatch_table = copyreg.dispatch_table
+
+ def __init__(self, *args):
+ super().__init__(*args)
+ self.dispatch_table = self._copyreg_dispatch_table.copy()
+ self.dispatch_table.update(self._extra_reducers)
+
+ @classmethod
+ def register(cls, type, reduce):
+ '''Register a reduce function for a type.'''
+ cls._extra_reducers[type] = reduce
+
+ @classmethod
+ def dumps(cls, obj, protocol=None):
+ buf = io.BytesIO()
+ cls(buf, protocol).dump(obj)
+ return buf.getbuffer()
+
+ loads = pickle.loads
+
+register = ForkingPickler.register
+
+def dump(obj, file, protocol=None):
+ '''Replacement for pickle.dump() using ForkingPickler.'''
+ ForkingPickler(file, protocol).dump(obj)
#
# Platform specific definitions
@@ -36,20 +64,44 @@ if not(sys.platform == 'win32' or (hasattr(socket, 'CMSG_LEN') and
if sys.platform == 'win32':
# Windows
- __all__ += ['reduce_pipe_connection']
+ __all__ += ['DupHandle', 'duplicate', 'steal_handle']
import _winapi
+ def duplicate(handle, target_process=None, inheritable=False):
+ '''Duplicate a handle. (target_process is a handle not a pid!)'''
+ if target_process is None:
+ target_process = _winapi.GetCurrentProcess()
+ return _winapi.DuplicateHandle(
+ _winapi.GetCurrentProcess(), handle, target_process,
+ 0, inheritable, _winapi.DUPLICATE_SAME_ACCESS)
+
+ def steal_handle(source_pid, handle):
+ '''Steal a handle from process identified by source_pid.'''
+ source_process_handle = _winapi.OpenProcess(
+ _winapi.PROCESS_DUP_HANDLE, False, source_pid)
+ try:
+ return _winapi.DuplicateHandle(
+ source_process_handle, handle,
+ _winapi.GetCurrentProcess(), 0, False,
+ _winapi.DUPLICATE_SAME_ACCESS | _winapi.DUPLICATE_CLOSE_SOURCE)
+ finally:
+ _winapi.CloseHandle(source_process_handle)
+
def send_handle(conn, handle, destination_pid):
+ '''Send a handle over a local connection.'''
dh = DupHandle(handle, _winapi.DUPLICATE_SAME_ACCESS, destination_pid)
conn.send(dh)
def recv_handle(conn):
+ '''Receive a handle over a local connection.'''
return conn.recv().detach()
class DupHandle(object):
+ '''Picklable wrapper for a handle.'''
def __init__(self, handle, access, pid=None):
- # duplicate handle for process with given pid
if pid is None:
+ # We just duplicate the handle in the current process and
+ # let the receiving process steal the handle.
pid = os.getpid()
proc = _winapi.OpenProcess(_winapi.PROCESS_DUP_HANDLE, False, pid)
try:
@@ -62,9 +114,12 @@ if sys.platform == 'win32':
self._pid = pid
def detach(self):
+ '''Get the handle. This should only be called once.'''
# retrieve handle from process which currently owns it
if self._pid == os.getpid():
+ # The handle has already been duplicated for this process.
return self._handle
+ # We must steal the handle from the process whose pid is self._pid.
proc = _winapi.OpenProcess(_winapi.PROCESS_DUP_HANDLE, False,
self._pid)
try:
@@ -74,207 +129,112 @@ if sys.platform == 'win32':
finally:
_winapi.CloseHandle(proc)
- class DupSocket(object):
- def __init__(self, sock):
- new_sock = sock.dup()
- def send(conn, pid):
- share = new_sock.share(pid)
- conn.send_bytes(share)
- self._id = resource_sharer.register(send, new_sock.close)
-
- def detach(self):
- conn = resource_sharer.get_connection(self._id)
- try:
- share = conn.recv_bytes()
- return socket.fromshare(share)
- finally:
- conn.close()
-
- def reduce_socket(s):
- return rebuild_socket, (DupSocket(s),)
-
- def rebuild_socket(ds):
- return ds.detach()
-
- def reduce_connection(conn):
- handle = conn.fileno()
- with socket.fromfd(handle, socket.AF_INET, socket.SOCK_STREAM) as s:
- ds = DupSocket(s)
- return rebuild_connection, (ds, conn.readable, conn.writable)
-
- def rebuild_connection(ds, readable, writable):
- from .connection import Connection
- sock = ds.detach()
- return Connection(sock.detach(), readable, writable)
-
- def reduce_pipe_connection(conn):
- access = ((_winapi.FILE_GENERIC_READ if conn.readable else 0) |
- (_winapi.FILE_GENERIC_WRITE if conn.writable else 0))
- dh = DupHandle(conn.fileno(), access)
- return rebuild_pipe_connection, (dh, conn.readable, conn.writable)
-
- def rebuild_pipe_connection(dh, readable, writable):
- from .connection import PipeConnection
- handle = dh.detach()
- return PipeConnection(handle, readable, writable)
-
else:
# Unix
+ __all__ += ['DupFd', 'sendfds', 'recvfds']
+ import array
# On MacOSX we should acknowledge receipt of fds -- see Issue14669
ACKNOWLEDGE = sys.platform == 'darwin'
+ def sendfds(sock, fds):
+ '''Send an array of fds over an AF_UNIX socket.'''
+ fds = array.array('i', fds)
+ msg = bytes([len(fds) % 256])
+ sock.sendmsg([msg], [(socket.SOL_SOCKET, socket.SCM_RIGHTS, fds)])
+ if ACKNOWLEDGE and sock.recv(1) != b'A':
+ raise RuntimeError('did not receive acknowledgement of fd')
+
+ def recvfds(sock, size):
+ '''Receive an array of fds over an AF_UNIX socket.'''
+ a = array.array('i')
+ bytes_size = a.itemsize * size
+ msg, ancdata, flags, addr = sock.recvmsg(1, socket.CMSG_LEN(bytes_size))
+ if not msg and not ancdata:
+ raise EOFError
+ try:
+ if ACKNOWLEDGE:
+ sock.send(b'A')
+ if len(ancdata) != 1:
+ raise RuntimeError('received %d items of ancdata' %
+ len(ancdata))
+ cmsg_level, cmsg_type, cmsg_data = ancdata[0]
+ if (cmsg_level == socket.SOL_SOCKET and
+ cmsg_type == socket.SCM_RIGHTS):
+ if len(cmsg_data) % a.itemsize != 0:
+ raise ValueError
+ a.frombytes(cmsg_data)
+ assert len(a) % 256 == msg[0]
+ return list(a)
+ except (ValueError, IndexError):
+ pass
+ raise RuntimeError('Invalid data received')
+
def send_handle(conn, handle, destination_pid):
+ '''Send a handle over a local connection.'''
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))])
- if ACKNOWLEDGE and conn.recv_bytes() != b'ACK':
- raise RuntimeError('did not receive acknowledgement of fd')
+ sendfds(s, [handle])
def recv_handle(conn):
- size = struct.calcsize("@i")
+ '''Receive a handle over a local connection.'''
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:
- if ACKNOWLEDGE:
- conn.send_bytes(b'ACK')
- 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')
-
- class DupFd(object):
- def __init__(self, fd):
- new_fd = os.dup(fd)
- def send(conn, pid):
- send_handle(conn, new_fd, pid)
- def close():
- os.close(new_fd)
- self._id = resource_sharer.register(send, close)
+ return recvfds(s, 1)[0]
+
+ def DupFd(fd):
+ '''Return a wrapper for an fd.'''
+ popen_obj = context.get_spawning_popen()
+ if popen_obj is not None:
+ return popen_obj.DupFd(popen_obj.duplicate_for_child(fd))
+ elif HAVE_SEND_HANDLE:
+ from . import resource_sharer
+ return resource_sharer.DupFd(fd)
+ else:
+ raise ValueError('SCM_RIGHTS appears not to be available')
- def detach(self):
- conn = resource_sharer.get_connection(self._id)
- try:
- return recv_handle(conn)
- finally:
- conn.close()
+#
+# Try making some callable types picklable
+#
- def reduce_socket(s):
- df = DupFd(s.fileno())
- return rebuild_socket, (df, s.family, s.type, s.proto)
+def _reduce_method(m):
+ if m.__self__ is None:
+ return getattr, (m.__class__, m.__func__.__name__)
+ else:
+ return getattr, (m.__self__, m.__func__.__name__)
+class _C:
+ def f(self):
+ pass
+register(type(_C().f), _reduce_method)
- def rebuild_socket(df, family, type, proto):
- fd = df.detach()
- s = socket.fromfd(fd, family, type, proto)
- os.close(fd)
- return s
- def reduce_connection(conn):
- df = DupFd(conn.fileno())
- return rebuild_connection, (df, conn.readable, conn.writable)
+def _reduce_method_descriptor(m):
+ return getattr, (m.__objclass__, m.__name__)
+register(type(list.append), _reduce_method_descriptor)
+register(type(int.__add__), _reduce_method_descriptor)
- def rebuild_connection(df, readable, writable):
- from .connection import Connection
- fd = df.detach()
- return Connection(fd, readable, writable)
+
+def _reduce_partial(p):
+ return _rebuild_partial, (p.func, p.args, p.keywords or {})
+def _rebuild_partial(func, args, keywords):
+ return functools.partial(func, *args, **keywords)
+register(functools.partial, _reduce_partial)
#
-# Server which shares registered resources with clients
+# Make sockets picklable
#
-class ResourceSharer(object):
- def __init__(self):
- self._key = 0
- self._cache = {}
- self._old_locks = []
- self._lock = threading.Lock()
- self._listener = None
- self._address = None
- self._thread = None
- register_after_fork(self, ResourceSharer._afterfork)
-
- def register(self, send, close):
- with self._lock:
- if self._address is None:
- self._start()
- self._key += 1
- self._cache[self._key] = (send, close)
- return (self._address, self._key)
-
- @staticmethod
- def get_connection(ident):
- from .connection import Client
- address, key = ident
- c = Client(address, authkey=current_process().authkey)
- c.send((key, os.getpid()))
- return c
-
- def stop(self, timeout=None):
- from .connection import Client
- with self._lock:
- if self._address is not None:
- c = Client(self._address, authkey=current_process().authkey)
- c.send(None)
- c.close()
- self._thread.join(timeout)
- if self._thread.is_alive():
- sub_warn('ResourceSharer thread did not stop when asked')
- self._listener.close()
- self._thread = None
- self._address = None
- self._listener = None
- for key, (send, close) in self._cache.items():
- close()
- self._cache.clear()
-
- def _afterfork(self):
- for key, (send, close) in self._cache.items():
- close()
- self._cache.clear()
- # If self._lock was locked at the time of the fork, it may be broken
- # -- see issue 6721. Replace it without letting it be gc'ed.
- self._old_locks.append(self._lock)
- self._lock = threading.Lock()
- if self._listener is not None:
- self._listener.close()
- self._listener = None
- self._address = None
- self._thread = None
-
- def _start(self):
- from .connection import Listener
- assert self._listener is None
- debug('starting listener and thread for sending handles')
- self._listener = Listener(authkey=current_process().authkey)
- self._address = self._listener.address
- t = threading.Thread(target=self._serve)
- t.daemon = True
- t.start()
- self._thread = t
-
- def _serve(self):
- if hasattr(signal, 'pthread_sigmask'):
- signal.pthread_sigmask(signal.SIG_BLOCK, range(1, signal.NSIG))
- while 1:
- try:
- conn = self._listener.accept()
- msg = conn.recv()
- if msg is None:
- break
- key, destination_pid = msg
- send, close = self._cache.pop(key)
- send(conn, destination_pid)
- close()
- conn.close()
- except:
- if not is_exiting():
- import traceback
- sub_warning(
- 'thread for sharing handles raised exception :\n' +
- '-'*79 + '\n' + traceback.format_exc() + '-'*79
- )
-
-resource_sharer = ResourceSharer()
+if sys.platform == 'win32':
+ def _reduce_socket(s):
+ from .resource_sharer import DupSocket
+ return _rebuild_socket, (DupSocket(s),)
+ def _rebuild_socket(ds):
+ return ds.detach()
+ register(socket.socket, _reduce_socket)
+
+else:
+ def _reduce_socket(s):
+ df = DupFd(s.fileno())
+ return _rebuild_socket, (df, s.family, s.type, s.proto)
+ def _rebuild_socket(df, family, type, proto):
+ fd = df.detach()
+ return socket.socket(family, type, proto, fileno=fd)
+ register(socket.socket, _reduce_socket)
diff --git a/Lib/multiprocessing/resource_sharer.py b/Lib/multiprocessing/resource_sharer.py
new file mode 100644
index 0000000..5e46fc6
--- /dev/null
+++ b/Lib/multiprocessing/resource_sharer.py
@@ -0,0 +1,158 @@
+#
+# We use a background thread for sharing fds on Unix, and for sharing sockets on
+# Windows.
+#
+# A client which wants to pickle a resource registers it with the resource
+# sharer and gets an identifier in return. The unpickling process will connect
+# to the resource sharer, sends the identifier and its pid, and then receives
+# the resource.
+#
+
+import os
+import signal
+import socket
+import sys
+import threading
+
+from . import process
+from . import reduction
+from . import util
+
+__all__ = ['stop']
+
+
+if sys.platform == 'win32':
+ __all__ += ['DupSocket']
+
+ class DupSocket(object):
+ '''Picklable wrapper for a socket.'''
+ def __init__(self, sock):
+ new_sock = sock.dup()
+ def send(conn, pid):
+ share = new_sock.share(pid)
+ conn.send_bytes(share)
+ self._id = _resource_sharer.register(send, new_sock.close)
+
+ def detach(self):
+ '''Get the socket. This should only be called once.'''
+ with _resource_sharer.get_connection(self._id) as conn:
+ share = conn.recv_bytes()
+ return socket.fromshare(share)
+
+else:
+ __all__ += ['DupFd']
+
+ class DupFd(object):
+ '''Wrapper for fd which can be used at any time.'''
+ def __init__(self, fd):
+ new_fd = os.dup(fd)
+ def send(conn, pid):
+ reduction.send_handle(conn, new_fd, pid)
+ def close():
+ os.close(new_fd)
+ self._id = _resource_sharer.register(send, close)
+
+ def detach(self):
+ '''Get the fd. This should only be called once.'''
+ with _resource_sharer.get_connection(self._id) as conn:
+ return reduction.recv_handle(conn)
+
+
+class _ResourceSharer(object):
+ '''Manager for resouces using background thread.'''
+ def __init__(self):
+ self._key = 0
+ self._cache = {}
+ self._old_locks = []
+ self._lock = threading.Lock()
+ self._listener = None
+ self._address = None
+ self._thread = None
+ util.register_after_fork(self, _ResourceSharer._afterfork)
+
+ def register(self, send, close):
+ '''Register resource, returning an identifier.'''
+ with self._lock:
+ if self._address is None:
+ self._start()
+ self._key += 1
+ self._cache[self._key] = (send, close)
+ return (self._address, self._key)
+
+ @staticmethod
+ def get_connection(ident):
+ '''Return connection from which to receive identified resource.'''
+ from .connection import Client
+ address, key = ident
+ c = Client(address, authkey=process.current_process().authkey)
+ c.send((key, os.getpid()))
+ return c
+
+ def stop(self, timeout=None):
+ '''Stop the background thread and clear registered resources.'''
+ from .connection import Client
+ with self._lock:
+ if self._address is not None:
+ c = Client(self._address,
+ authkey=process.current_process().authkey)
+ c.send(None)
+ c.close()
+ self._thread.join(timeout)
+ if self._thread.is_alive():
+ util.sub_warning('_ResourceSharer thread did '
+ 'not stop when asked')
+ self._listener.close()
+ self._thread = None
+ self._address = None
+ self._listener = None
+ for key, (send, close) in self._cache.items():
+ close()
+ self._cache.clear()
+
+ def _afterfork(self):
+ for key, (send, close) in self._cache.items():
+ close()
+ self._cache.clear()
+ # If self._lock was locked at the time of the fork, it may be broken
+ # -- see issue 6721. Replace it without letting it be gc'ed.
+ self._old_locks.append(self._lock)
+ self._lock = threading.Lock()
+ if self._listener is not None:
+ self._listener.close()
+ self._listener = None
+ self._address = None
+ self._thread = None
+
+ def _start(self):
+ from .connection import Listener
+ assert self._listener is None
+ util.debug('starting listener and thread for sending handles')
+ self._listener = Listener(authkey=process.current_process().authkey)
+ self._address = self._listener.address
+ t = threading.Thread(target=self._serve)
+ t.daemon = True
+ t.start()
+ self._thread = t
+
+ def _serve(self):
+ if hasattr(signal, 'pthread_sigmask'):
+ signal.pthread_sigmask(signal.SIG_BLOCK, range(1, signal.NSIG))
+ while 1:
+ try:
+ with self._listener.accept() as conn:
+ msg = conn.recv()
+ if msg is None:
+ break
+ key, destination_pid = msg
+ send, close = self._cache.pop(key)
+ try:
+ send(conn, destination_pid)
+ finally:
+ close()
+ except:
+ if not util.is_exiting():
+ sys.excepthook(*sys.exc_info())
+
+
+_resource_sharer = _ResourceSharer()
+stop = _resource_sharer.stop
diff --git a/Lib/multiprocessing/semaphore_tracker.py b/Lib/multiprocessing/semaphore_tracker.py
new file mode 100644
index 0000000..de7738e
--- /dev/null
+++ b/Lib/multiprocessing/semaphore_tracker.py
@@ -0,0 +1,143 @@
+#
+# On Unix we run a server process which keeps track of unlinked
+# semaphores. The server ignores SIGINT and SIGTERM and reads from a
+# pipe. Every other process of the program has a copy of the writable
+# end of the pipe, so we get EOF when all other processes have exited.
+# Then the server process unlinks any remaining semaphore names.
+#
+# This is important because the system only supports a limited number
+# of named semaphores, and they will not be automatically removed till
+# the next reboot. Without this semaphore tracker process, "killall
+# python" would probably leave unlinked semaphores.
+#
+
+import os
+import signal
+import sys
+import threading
+import warnings
+import _multiprocessing
+
+from . import spawn
+from . import util
+
+__all__ = ['ensure_running', 'register', 'unregister']
+
+
+class SemaphoreTracker(object):
+
+ def __init__(self):
+ self._lock = threading.Lock()
+ self._fd = None
+
+ def getfd(self):
+ self.ensure_running()
+ return self._fd
+
+ def ensure_running(self):
+ '''Make sure that semaphore tracker process is running.
+
+ This can be run from any process. Usually a child process will use
+ the semaphore created by its parent.'''
+ with self._lock:
+ if self._fd is not None:
+ return
+ fds_to_pass = []
+ try:
+ fds_to_pass.append(sys.stderr.fileno())
+ except Exception:
+ pass
+ cmd = 'from multiprocessing.semaphore_tracker import main;main(%d)'
+ r, w = os.pipe()
+ try:
+ fds_to_pass.append(r)
+ # process will out live us, so no need to wait on pid
+ exe = spawn.get_executable()
+ args = [exe] + util._args_from_interpreter_flags()
+ args += ['-c', cmd % r]
+ util.spawnv_passfds(exe, args, fds_to_pass)
+ except:
+ os.close(w)
+ raise
+ else:
+ self._fd = w
+ finally:
+ os.close(r)
+
+ def register(self, name):
+ '''Register name of semaphore with semaphore tracker.'''
+ self._send('REGISTER', name)
+
+ def unregister(self, name):
+ '''Unregister name of semaphore with semaphore tracker.'''
+ self._send('UNREGISTER', name)
+
+ def _send(self, cmd, name):
+ self.ensure_running()
+ msg = '{0}:{1}\n'.format(cmd, name).encode('ascii')
+ if len(name) > 512:
+ # posix guarantees that writes to a pipe of less than PIPE_BUF
+ # bytes are atomic, and that PIPE_BUF >= 512
+ raise ValueError('name too long')
+ nbytes = os.write(self._fd, msg)
+ assert nbytes == len(msg)
+
+
+_semaphore_tracker = SemaphoreTracker()
+ensure_running = _semaphore_tracker.ensure_running
+register = _semaphore_tracker.register
+unregister = _semaphore_tracker.unregister
+getfd = _semaphore_tracker.getfd
+
+
+def main(fd):
+ '''Run semaphore tracker.'''
+ # protect the process from ^C and "killall python" etc
+ signal.signal(signal.SIGINT, signal.SIG_IGN)
+ signal.signal(signal.SIGTERM, signal.SIG_IGN)
+
+ for f in (sys.stdin, sys.stdout):
+ try:
+ f.close()
+ except Exception:
+ pass
+
+ cache = set()
+ try:
+ # keep track of registered/unregistered semaphores
+ with open(fd, 'rb') as f:
+ for line in f:
+ try:
+ cmd, name = line.strip().split(b':')
+ if cmd == b'REGISTER':
+ cache.add(name)
+ elif cmd == b'UNREGISTER':
+ cache.remove(name)
+ else:
+ raise RuntimeError('unrecognized command %r' % cmd)
+ except Exception:
+ try:
+ sys.excepthook(*sys.exc_info())
+ except:
+ pass
+ finally:
+ # all processes have terminated; cleanup any remaining semaphores
+ if cache:
+ try:
+ warnings.warn('semaphore_tracker: There appear to be %d '
+ 'leaked semaphores to clean up at shutdown' %
+ len(cache))
+ except Exception:
+ pass
+ for name in cache:
+ # For some reason the process which created and registered this
+ # semaphore has failed to unregister it. Presumably it has died.
+ # We therefore unlink it.
+ try:
+ name = name.decode('ascii')
+ try:
+ _multiprocessing.sem_unlink(name)
+ except Exception as e:
+ warnings.warn('semaphore_tracker: %r: %s' % (name, e))
+ finally:
+ pass
diff --git a/Lib/multiprocessing/sharedctypes.py b/Lib/multiprocessing/sharedctypes.py
index a358ed4..0c17825 100644
--- a/Lib/multiprocessing/sharedctypes.py
+++ b/Lib/multiprocessing/sharedctypes.py
@@ -10,8 +10,11 @@
import ctypes
import weakref
-from multiprocessing import heap, RLock
-from multiprocessing.forking import assert_spawning, ForkingPickler
+from . import heap
+from . import get_context
+
+from .context import assert_spawning
+from .reduction import ForkingPickler
__all__ = ['RawValue', 'RawArray', 'Value', 'Array', 'copy', 'synchronized']
@@ -63,7 +66,7 @@ def RawArray(typecode_or_type, size_or_initializer):
result.__init__(*size_or_initializer)
return result
-def Value(typecode_or_type, *args, lock=True):
+def Value(typecode_or_type, *args, lock=True, ctx=None):
'''
Return a synchronization wrapper for a Value
'''
@@ -71,12 +74,13 @@ def Value(typecode_or_type, *args, lock=True):
if lock is False:
return obj
if lock in (True, None):
- lock = RLock()
+ ctx = ctx or get_context()
+ lock = ctx.RLock()
if not hasattr(lock, 'acquire'):
raise AttributeError("'%r' has no method 'acquire'" % lock)
- return synchronized(obj, lock)
+ return synchronized(obj, lock, ctx=ctx)
-def Array(typecode_or_type, size_or_initializer, *, lock=True):
+def Array(typecode_or_type, size_or_initializer, *, lock=True, ctx=None):
'''
Return a synchronization wrapper for a RawArray
'''
@@ -84,25 +88,27 @@ def Array(typecode_or_type, size_or_initializer, *, lock=True):
if lock is False:
return obj
if lock in (True, None):
- lock = RLock()
+ ctx = ctx or get_context()
+ lock = ctx.RLock()
if not hasattr(lock, 'acquire'):
raise AttributeError("'%r' has no method 'acquire'" % lock)
- return synchronized(obj, lock)
+ return synchronized(obj, lock, ctx=ctx)
def copy(obj):
new_obj = _new_value(type(obj))
ctypes.pointer(new_obj)[0] = obj
return new_obj
-def synchronized(obj, lock=None):
+def synchronized(obj, lock=None, ctx=None):
assert not isinstance(obj, SynchronizedBase), 'object already synchronized'
+ ctx = ctx or get_context()
if isinstance(obj, ctypes._SimpleCData):
- return Synchronized(obj, lock)
+ return Synchronized(obj, lock, ctx)
elif isinstance(obj, ctypes.Array):
if obj._type_ is ctypes.c_char:
- return SynchronizedString(obj, lock)
- return SynchronizedArray(obj, lock)
+ return SynchronizedString(obj, lock, ctx)
+ return SynchronizedArray(obj, lock, ctx)
else:
cls = type(obj)
try:
@@ -112,7 +118,7 @@ def synchronized(obj, lock=None):
d = dict((name, make_property(name)) for name in names)
classname = 'Synchronized' + cls.__name__
scls = class_cache[cls] = type(classname, (SynchronizedBase,), d)
- return scls(obj, lock)
+ return scls(obj, lock, ctx)
#
# Functions for pickling/unpickling
@@ -172,9 +178,13 @@ class_cache = weakref.WeakKeyDictionary()
class SynchronizedBase(object):
- def __init__(self, obj, lock=None):
+ def __init__(self, obj, lock=None, ctx=None):
self._obj = obj
- self._lock = lock or RLock()
+ if lock:
+ self._lock = lock
+ else:
+ ctx = ctx or get_context(force=True)
+ self._lock = ctx.RLock()
self.acquire = self._lock.acquire
self.release = self._lock.release
diff --git a/Lib/multiprocessing/spawn.py b/Lib/multiprocessing/spawn.py
new file mode 100644
index 0000000..336e479
--- /dev/null
+++ b/Lib/multiprocessing/spawn.py
@@ -0,0 +1,287 @@
+#
+# Code used to start processes when using the spawn or forkserver
+# start methods.
+#
+# multiprocessing/spawn.py
+#
+# Copyright (c) 2006-2008, R Oudkerk
+# Licensed to PSF under a Contributor Agreement.
+#
+
+import os
+import pickle
+import sys
+import runpy
+import types
+
+from . import get_start_method, set_start_method
+from . import process
+from . import util
+
+__all__ = ['_main', 'freeze_support', 'set_executable', 'get_executable',
+ 'get_preparation_data', 'get_command_line', 'import_main_path']
+
+#
+# _python_exe is the assumed path to the python executable.
+# People embedding Python want to modify it.
+#
+
+if sys.platform != 'win32':
+ WINEXE = False
+ WINSERVICE = False
+else:
+ WINEXE = (sys.platform == 'win32' and getattr(sys, 'frozen', False))
+ WINSERVICE = sys.executable.lower().endswith("pythonservice.exe")
+
+if WINSERVICE:
+ _python_exe = os.path.join(sys.exec_prefix, 'python.exe')
+else:
+ _python_exe = sys.executable
+
+def set_executable(exe):
+ global _python_exe
+ _python_exe = exe
+
+def get_executable():
+ return _python_exe
+
+#
+#
+#
+
+def is_forking(argv):
+ '''
+ Return whether commandline indicates we are forking
+ '''
+ if len(argv) >= 2 and argv[1] == '--multiprocessing-fork':
+ return True
+ else:
+ return False
+
+
+def freeze_support():
+ '''
+ Run code for process object if this in not the main process
+ '''
+ if is_forking(sys.argv):
+ kwds = {}
+ for arg in sys.argv[2:]:
+ name, value = arg.split('=')
+ if value == 'None':
+ kwds[name] = None
+ else:
+ kwds[name] = int(value)
+ spawn_main(**kwds)
+ sys.exit()
+
+
+def get_command_line(**kwds):
+ '''
+ Returns prefix of command line used for spawning a child process
+ '''
+ if getattr(sys, 'frozen', False):
+ return ([sys.executable, '--multiprocessing-fork'] +
+ ['%s=%r' % item for item in kwds.items()])
+ else:
+ prog = 'from multiprocessing.spawn import spawn_main; spawn_main(%s)'
+ prog %= ', '.join('%s=%r' % item for item in kwds.items())
+ opts = util._args_from_interpreter_flags()
+ return [_python_exe] + opts + ['-c', prog, '--multiprocessing-fork']
+
+
+def spawn_main(pipe_handle, parent_pid=None, tracker_fd=None):
+ '''
+ Run code specifed by data received over pipe
+ '''
+ assert is_forking(sys.argv)
+ if sys.platform == 'win32':
+ import msvcrt
+ from .reduction import steal_handle
+ new_handle = steal_handle(parent_pid, pipe_handle)
+ fd = msvcrt.open_osfhandle(new_handle, os.O_RDONLY)
+ else:
+ from . import semaphore_tracker
+ semaphore_tracker._semaphore_tracker._fd = tracker_fd
+ fd = pipe_handle
+ exitcode = _main(fd)
+ sys.exit(exitcode)
+
+
+def _main(fd):
+ with os.fdopen(fd, 'rb', closefd=True) as from_parent:
+ process.current_process()._inheriting = True
+ try:
+ preparation_data = pickle.load(from_parent)
+ prepare(preparation_data)
+ self = pickle.load(from_parent)
+ finally:
+ del process.current_process()._inheriting
+ return self._bootstrap()
+
+
+def _check_not_importing_main():
+ if getattr(process.current_process(), '_inheriting', False):
+ raise RuntimeError('''
+ An attempt has been made to start a new process before the
+ current process has finished its bootstrapping phase.
+
+ This probably means that you are not using fork to start your
+ child processes and you have forgotten to use the proper idiom
+ in the main module:
+
+ if __name__ == '__main__':
+ freeze_support()
+ ...
+
+ The "freeze_support()" line can be omitted if the program
+ is not going to be frozen to produce an executable.''')
+
+
+def get_preparation_data(name):
+ '''
+ Return info about parent needed by child to unpickle process object
+ '''
+ _check_not_importing_main()
+ d = dict(
+ log_to_stderr=util._log_to_stderr,
+ authkey=process.current_process().authkey,
+ )
+
+ if util._logger is not None:
+ d['log_level'] = util._logger.getEffectiveLevel()
+
+ sys_path=sys.path.copy()
+ try:
+ i = sys_path.index('')
+ except ValueError:
+ pass
+ else:
+ sys_path[i] = process.ORIGINAL_DIR
+
+ d.update(
+ name=name,
+ sys_path=sys_path,
+ sys_argv=sys.argv,
+ orig_dir=process.ORIGINAL_DIR,
+ dir=os.getcwd(),
+ start_method=get_start_method(),
+ )
+
+ # Figure out whether to initialise main in the subprocess as a module
+ # or through direct execution (or to leave it alone entirely)
+ main_module = sys.modules['__main__']
+ main_mod_name = getattr(main_module.__spec__, "name", None)
+ if main_mod_name is not None:
+ d['init_main_from_name'] = main_mod_name
+ elif sys.platform != 'win32' or (not WINEXE and not WINSERVICE):
+ main_path = getattr(main_module, '__file__', None)
+ if main_path is not None:
+ if (not os.path.isabs(main_path) and
+ process.ORIGINAL_DIR is not None):
+ main_path = os.path.join(process.ORIGINAL_DIR, main_path)
+ d['init_main_from_path'] = os.path.normpath(main_path)
+
+ return d
+
+#
+# Prepare current process
+#
+
+old_main_modules = []
+
+def prepare(data):
+ '''
+ Try to get current process ready to unpickle process object
+ '''
+ if 'name' in data:
+ process.current_process().name = data['name']
+
+ if 'authkey' in data:
+ process.current_process().authkey = data['authkey']
+
+ if 'log_to_stderr' in data and data['log_to_stderr']:
+ util.log_to_stderr()
+
+ if 'log_level' in data:
+ util.get_logger().setLevel(data['log_level'])
+
+ if 'sys_path' in data:
+ sys.path = data['sys_path']
+
+ if 'sys_argv' in data:
+ sys.argv = data['sys_argv']
+
+ if 'dir' in data:
+ os.chdir(data['dir'])
+
+ if 'orig_dir' in data:
+ process.ORIGINAL_DIR = data['orig_dir']
+
+ if 'start_method' in data:
+ set_start_method(data['start_method'])
+
+ if 'init_main_from_name' in data:
+ _fixup_main_from_name(data['init_main_from_name'])
+ elif 'init_main_from_path' in data:
+ _fixup_main_from_path(data['init_main_from_path'])
+
+# Multiprocessing module helpers to fix up the main module in
+# spawned subprocesses
+def _fixup_main_from_name(mod_name):
+ # __main__.py files for packages, directories, zip archives, etc, run
+ # their "main only" code unconditionally, so we don't even try to
+ # populate anything in __main__, nor do we make any changes to
+ # __main__ attributes
+ current_main = sys.modules['__main__']
+ if mod_name == "__main__" or mod_name.endswith(".__main__"):
+ return
+
+ # If this process was forked, __main__ may already be populated
+ if getattr(current_main.__spec__, "name", None) == mod_name:
+ return
+
+ # Otherwise, __main__ may contain some non-main code where we need to
+ # support unpickling it properly. We rerun it as __mp_main__ and make
+ # the normal __main__ an alias to that
+ old_main_modules.append(current_main)
+ main_module = types.ModuleType("__mp_main__")
+ main_content = runpy.run_module(mod_name,
+ run_name="__mp_main__",
+ alter_sys=True)
+ main_module.__dict__.update(main_content)
+ sys.modules['__main__'] = sys.modules['__mp_main__'] = main_module
+
+
+def _fixup_main_from_path(main_path):
+ # If this process was forked, __main__ may already be populated
+ current_main = sys.modules['__main__']
+
+ # Unfortunately, the main ipython launch script historically had no
+ # "if __name__ == '__main__'" guard, so we work around that
+ # by treating it like a __main__.py file
+ # See https://github.com/ipython/ipython/issues/4698
+ main_name = os.path.splitext(os.path.basename(main_path))[0]
+ if main_name == 'ipython':
+ return
+
+ # Otherwise, if __file__ already has the setting we expect,
+ # there's nothing more to do
+ if getattr(current_main, '__file__', None) == main_path:
+ return
+
+ # If the parent process has sent a path through rather than a module
+ # name we assume it is an executable script that may contain
+ # non-main code that needs to be executed
+ old_main_modules.append(current_main)
+ main_module = types.ModuleType("__mp_main__")
+ main_content = runpy.run_path(main_path,
+ run_name="__mp_main__")
+ main_module.__dict__.update(main_content)
+ sys.modules['__main__'] = sys.modules['__mp_main__'] = main_module
+
+
+def import_main_path(main_path):
+ '''
+ Set sys.modules['__main__'] to module at main_path
+ '''
+ _fixup_main_from_path(main_path)
diff --git a/Lib/multiprocessing/synchronize.py b/Lib/multiprocessing/synchronize.py
index 0faca78..dea1cbd 100644
--- a/Lib/multiprocessing/synchronize.py
+++ b/Lib/multiprocessing/synchronize.py
@@ -13,18 +13,20 @@ __all__ = [
import threading
import sys
-
+import tempfile
import _multiprocessing
-from multiprocessing.process import current_process
-from multiprocessing.util import register_after_fork, debug
-from multiprocessing.forking import assert_spawning, Popen
+
from time import time as _time
+from . import context
+from . import process
+from . import util
+
# Try to import the mp.synchronize module cleanly, if it fails
# raise ImportError for platforms lacking a working sem_open implementation.
# See issue 3770
try:
- from _multiprocessing import SemLock
+ from _multiprocessing import SemLock, sem_unlink
except (ImportError):
raise ImportError("This platform lacks a functioning sem_open" +
" implementation, therefore, the required" +
@@ -44,15 +46,47 @@ SEM_VALUE_MAX = _multiprocessing.SemLock.SEM_VALUE_MAX
class SemLock(object):
- def __init__(self, kind, value, maxvalue):
- sl = self._semlock = _multiprocessing.SemLock(kind, value, maxvalue)
- debug('created semlock with handle %s' % sl.handle)
+ _rand = tempfile._RandomNameSequence()
+
+ def __init__(self, kind, value, maxvalue, *, ctx):
+ if ctx is None:
+ ctx = context._default_context.get_context()
+ name = ctx.get_start_method()
+ unlink_now = sys.platform == 'win32' or name == 'fork'
+ for i in range(100):
+ try:
+ sl = self._semlock = _multiprocessing.SemLock(
+ kind, value, maxvalue, self._make_name(),
+ unlink_now)
+ except FileExistsError:
+ pass
+ else:
+ break
+ else:
+ raise FileExistsError('cannot find name for semaphore')
+
+ util.debug('created semlock with handle %s' % sl.handle)
self._make_methods()
if sys.platform != 'win32':
def _after_fork(obj):
obj._semlock._after_fork()
- register_after_fork(self, _after_fork)
+ util.register_after_fork(self, _after_fork)
+
+ if self._semlock.name is not None:
+ # We only get here if we are on Unix with forking
+ # disabled. When the object is garbage collected or the
+ # process shuts down we unlink the semaphore name
+ from .semaphore_tracker import register
+ register(self._semlock.name)
+ util.Finalize(self, SemLock._cleanup, (self._semlock.name,),
+ exitpriority=0)
+
+ @staticmethod
+ def _cleanup(name):
+ from .semaphore_tracker import unregister
+ sem_unlink(name)
+ unregister(name)
def _make_methods(self):
self.acquire = self._semlock.acquire
@@ -65,23 +99,32 @@ class SemLock(object):
return self._semlock.__exit__(*args)
def __getstate__(self):
- assert_spawning(self)
+ context.assert_spawning(self)
sl = self._semlock
- return (Popen.duplicate_for_child(sl.handle), sl.kind, sl.maxvalue)
+ if sys.platform == 'win32':
+ h = context.get_spawning_popen().duplicate_for_child(sl.handle)
+ else:
+ h = sl.handle
+ return (h, sl.kind, sl.maxvalue, sl.name)
def __setstate__(self, state):
self._semlock = _multiprocessing.SemLock._rebuild(*state)
- debug('recreated blocker with handle %r' % state[0])
+ util.debug('recreated blocker with handle %r' % state[0])
self._make_methods()
+ @staticmethod
+ def _make_name():
+ return '%s-%s' % (process.current_process()._config['semprefix'],
+ next(SemLock._rand))
+
#
# Semaphore
#
class Semaphore(SemLock):
- def __init__(self, value=1):
- SemLock.__init__(self, SEMAPHORE, value, SEM_VALUE_MAX)
+ def __init__(self, value=1, *, ctx):
+ SemLock.__init__(self, SEMAPHORE, value, SEM_VALUE_MAX, ctx=ctx)
def get_value(self):
return self._semlock._get_value()
@@ -99,8 +142,8 @@ class Semaphore(SemLock):
class BoundedSemaphore(Semaphore):
- def __init__(self, value=1):
- SemLock.__init__(self, SEMAPHORE, value, value)
+ def __init__(self, value=1, *, ctx):
+ SemLock.__init__(self, SEMAPHORE, value, value, ctx=ctx)
def __repr__(self):
try:
@@ -116,13 +159,13 @@ class BoundedSemaphore(Semaphore):
class Lock(SemLock):
- def __init__(self):
- SemLock.__init__(self, SEMAPHORE, 1, 1)
+ def __init__(self, *, ctx):
+ SemLock.__init__(self, SEMAPHORE, 1, 1, ctx=ctx)
def __repr__(self):
try:
if self._semlock._is_mine():
- name = current_process().name
+ name = process.current_process().name
if threading.current_thread().name != 'MainThread':
name += '|' + threading.current_thread().name
elif self._semlock._get_value() == 1:
@@ -141,13 +184,13 @@ class Lock(SemLock):
class RLock(SemLock):
- def __init__(self):
- SemLock.__init__(self, RECURSIVE_MUTEX, 1, 1)
+ def __init__(self, *, ctx):
+ SemLock.__init__(self, RECURSIVE_MUTEX, 1, 1, ctx=ctx)
def __repr__(self):
try:
if self._semlock._is_mine():
- name = current_process().name
+ name = process.current_process().name
if threading.current_thread().name != 'MainThread':
name += '|' + threading.current_thread().name
count = self._semlock._count()
@@ -167,15 +210,15 @@ class RLock(SemLock):
class Condition(object):
- def __init__(self, lock=None):
- self._lock = lock or RLock()
- self._sleeping_count = Semaphore(0)
- self._woken_count = Semaphore(0)
- self._wait_semaphore = Semaphore(0)
+ def __init__(self, lock=None, *, ctx):
+ self._lock = lock or ctx.RLock()
+ self._sleeping_count = ctx.Semaphore(0)
+ self._woken_count = ctx.Semaphore(0)
+ self._wait_semaphore = ctx.Semaphore(0)
self._make_methods()
def __getstate__(self):
- assert_spawning(self)
+ context.assert_spawning(self)
return (self._lock, self._sleeping_count,
self._woken_count, self._wait_semaphore)
@@ -289,9 +332,9 @@ class Condition(object):
class Event(object):
- def __init__(self):
- self._cond = Condition(Lock())
- self._flag = Semaphore(0)
+ def __init__(self, *, ctx):
+ self._cond = ctx.Condition(ctx.Lock())
+ self._flag = ctx.Semaphore(0)
def is_set(self):
self._cond.acquire()
@@ -340,11 +383,11 @@ class Event(object):
class Barrier(threading.Barrier):
- def __init__(self, parties, action=None, timeout=None):
+ def __init__(self, parties, action=None, timeout=None, *, ctx):
import struct
- from multiprocessing.heap import BufferWrapper
+ from .heap import BufferWrapper
wrapper = BufferWrapper(struct.calcsize('i') * 2)
- cond = Condition()
+ cond = ctx.Condition()
self.__setstate__((parties, action, timeout, cond, wrapper))
self._state = 0
self._count = 0
diff --git a/Lib/multiprocessing/util.py b/Lib/multiprocessing/util.py
index f5862b4..0b695e4 100644
--- a/Lib/multiprocessing/util.py
+++ b/Lib/multiprocessing/util.py
@@ -7,8 +7,6 @@
# Licensed to PSF under a Contributor Agreement.
#
-import sys
-import functools
import os
import itertools
import weakref
@@ -17,13 +15,13 @@ import threading # we want threading to install it's
# cleanup function before multiprocessing does
from subprocess import _args_from_interpreter_flags
-from multiprocessing.process import current_process, active_children
+from . import process
__all__ = [
'sub_debug', 'debug', 'info', 'sub_warning', 'get_logger',
'log_to_stderr', 'get_temp_dir', 'register_after_fork',
'is_exiting', 'Finalize', 'ForkAwareThreadLock', 'ForkAwareLocal',
- 'SUBDEBUG', 'SUBWARNING',
+ 'close_all_fds_except', 'SUBDEBUG', 'SUBWARNING',
]
#
@@ -71,8 +69,6 @@ def get_logger():
_logger = logging.getLogger(LOGGER_NAME)
_logger.propagate = 0
- logging.addLevelName(SUBDEBUG, 'SUBDEBUG')
- logging.addLevelName(SUBWARNING, 'SUBWARNING')
# XXX multiprocessing should cleanup before logging
if hasattr(atexit, 'unregister'):
@@ -111,13 +107,14 @@ def log_to_stderr(level=None):
def get_temp_dir():
# get name of a temp directory which will be automatically cleaned up
- if current_process()._tempdir is None:
+ tempdir = process.current_process()._config.get('tempdir')
+ if tempdir is None:
import shutil, tempfile
tempdir = tempfile.mkdtemp(prefix='pymp-')
info('created temp directory %s', tempdir)
Finalize(None, shutil.rmtree, args=[tempdir], exitpriority=-100)
- current_process()._tempdir = tempdir
- return current_process()._tempdir
+ process.current_process()._config['tempdir'] = tempdir
+ return tempdir
#
# Support for reinitialization of objects when bootstrapping a child process
@@ -273,8 +270,8 @@ def is_exiting():
_exiting = False
def _exit_function(info=info, debug=debug, _run_finalizers=_run_finalizers,
- active_children=active_children,
- current_process=current_process):
+ active_children=process.active_children,
+ current_process=process.current_process):
# We hold on to references to functions in the arglist due to the
# situation described below, where this function is called after this
# module's globals are destroyed.
@@ -303,7 +300,7 @@ def _exit_function(info=info, debug=debug, _run_finalizers=_run_finalizers,
# #9207.
for p in active_children():
- if p._daemonic:
+ if p.daemon:
info('calling terminate() for daemon %s', p.name)
p._popen.terminate()
@@ -335,3 +332,36 @@ class ForkAwareLocal(threading.local):
register_after_fork(self, lambda obj : obj.__dict__.clear())
def __reduce__(self):
return type(self), ()
+
+#
+# Close fds except those specified
+#
+
+try:
+ MAXFD = os.sysconf("SC_OPEN_MAX")
+except Exception:
+ MAXFD = 256
+
+def close_all_fds_except(fds):
+ fds = list(fds) + [-1, MAXFD]
+ fds.sort()
+ assert fds[-1] == MAXFD, 'fd too large'
+ for i in range(len(fds) - 1):
+ os.closerange(fds[i]+1, fds[i+1])
+
+#
+# Start a program with only specified fds kept open
+#
+
+def spawnv_passfds(path, args, passfds):
+ import _posixsubprocess
+ passfds = sorted(passfds)
+ errpipe_read, errpipe_write = os.pipe()
+ try:
+ return _posixsubprocess.fork_exec(
+ args, [os.fsencode(path)], True, passfds, None, None,
+ -1, -1, -1, -1, -1, -1, errpipe_read, errpipe_write,
+ False, False, None)
+ finally:
+ os.close(errpipe_read)
+ os.close(errpipe_write)
diff --git a/Lib/netrc.py b/Lib/netrc.py
index 2aa48f3..bbb3d23 100644
--- a/Lib/netrc.py
+++ b/Lib/netrc.py
@@ -3,8 +3,6 @@
# Module and documentation by Eric S. Raymond, 21 Dec 1998
import os, shlex, stat
-if os.name == 'posix':
- import pwd
__all__ = ["netrc", "NetrcParseError"]
@@ -28,7 +26,7 @@ class netrc:
try:
file = os.path.join(os.environ['HOME'], ".netrc")
except KeyError:
- raise IOError("Could not find .netrc: $HOME is not set")
+ raise OSError("Could not find .netrc: $HOME is not set")
self.hosts = {}
self.macros = {}
with open(file) as fp:
@@ -92,6 +90,7 @@ class netrc:
if os.name == 'posix' and default_netrc:
prop = os.fstat(fp.fileno())
if prop.st_uid != os.getuid():
+ import pwd
try:
fowner = pwd.getpwuid(prop.st_uid)[0]
except KeyError:
diff --git a/Lib/nntplib.py b/Lib/nntplib.py
index 02cc37c..e2c6579 100644
--- a/Lib/nntplib.py
+++ b/Lib/nntplib.py
@@ -279,7 +279,7 @@ def _unparse_datetime(dt, legacy=False):
if _have_ssl:
- def _encrypt_on(sock, context):
+ def _encrypt_on(sock, context, hostname):
"""Wrap a socket in SSL/TLS. Arguments:
- sock: Socket to wrap
- context: SSL context to use for the encrypted connection
@@ -288,10 +288,9 @@ if _have_ssl:
"""
# Generate a default SSL context if none was passed.
if context is None:
- context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
- # SSLv2 considered harmful.
- context.options |= ssl.OP_NO_SSLv2
- return context.wrap_socket(sock)
+ context = ssl._create_stdlib_context()
+ server_hostname = hostname if ssl.HAS_SNI else None
+ return context.wrap_socket(sock, server_hostname=server_hostname)
# The classes themselves
@@ -366,7 +365,7 @@ class _NNTPBase:
if is_connected():
try:
self.quit()
- except (socket.error, EOFError):
+ except (OSError, EOFError):
pass
finally:
if is_connected():
@@ -956,7 +955,7 @@ class _NNTPBase:
if auth:
user = auth[0]
password = auth[2]
- except IOError:
+ except OSError:
pass
# Perform NNTP authentication if needed.
if not user:
@@ -1007,7 +1006,7 @@ class _NNTPBase:
resp = self._shortcmd('STARTTLS')
if resp.startswith('382'):
self.file.close()
- self.sock = _encrypt_on(self.sock, context)
+ self.sock = _encrypt_on(self.sock, context, self.host)
self.file = self.sock.makefile("rwb")
self.tls_on = True
# Capabilities may change after TLS starts up, so ask for them
@@ -1067,7 +1066,7 @@ if _have_ssl:
in default port and the `ssl_context` argument for SSL connections.
"""
self.sock = socket.create_connection((host, port), timeout)
- self.sock = _encrypt_on(self.sock, ssl_context)
+ self.sock = _encrypt_on(self.sock, ssl_context, host)
file = self.sock.makefile("rwb")
_NNTPBase.__init__(self, file, host,
readermode=readermode, timeout=timeout)
@@ -1086,7 +1085,6 @@ if _have_ssl:
# Test retrieval when run as a script.
if __name__ == '__main__':
import argparse
- from email.utils import parsedate
parser = argparse.ArgumentParser(description="""\
nntplib built-in demo - display the latest articles in a newsgroup""")
diff --git a/Lib/ntpath.py b/Lib/ntpath.py
index 303e586..af3fb87 100644
--- a/Lib/ntpath.py
+++ b/Lib/ntpath.py
@@ -17,7 +17,7 @@ __all__ = ["normcase","isabs","join","splitdrive","split","splitext",
"ismount", "expanduser","expandvars","normpath","abspath",
"splitunc","curdir","pardir","sep","pathsep","defpath","altsep",
"extsep","devnull","realpath","supports_unicode_filenames","relpath",
- "samefile", "sameopenfile",]
+ "samefile", "sameopenfile", "samestat",]
# strings representing various path-related bits and pieces
# These are primarily for export; internally, they are hardcoded.
@@ -30,9 +30,6 @@ altsep = '/'
defpath = '.;C:\\bin'
if 'ce' in sys.builtin_module_names:
defpath = '\\Windows'
-elif 'os2' in sys.builtin_module_names:
- # OS/2 w/ VACPP
- altsep = '/'
devnull = 'nul'
def _get_empty(path):
@@ -260,12 +257,11 @@ def dirname(p):
def islink(path):
"""Test whether a path is a symbolic link.
- This will always return false for Windows prior to 6.0
- and for OS/2.
+ This will always return false for Windows prior to 6.0.
"""
try:
st = os.lstat(path)
- except (os.error, AttributeError):
+ except (OSError, AttributeError):
return False
return stat.S_ISLNK(st.st_mode)
@@ -275,20 +271,39 @@ def lexists(path):
"""Test whether a path exists. Returns True for broken symbolic links"""
try:
st = os.lstat(path)
- except (os.error, WindowsError):
+ except OSError:
return False
return True
-# Is a path a mount point? Either a root (with or without drive letter)
-# or an UNC path with at most a / or \ after the mount point.
-
+# Is a path a mount point?
+# Any drive letter root (eg c:\)
+# Any share UNC (eg \\server\share)
+# Any volume mounted on a filesystem folder
+#
+# No one method detects all three situations. Historically we've lexically
+# detected drive letter roots and share UNCs. The canonical approach to
+# detecting mounted volumes (querying the reparse tag) fails for the most
+# common case: drive letter roots. The alternative which uses GetVolumePathName
+# fails if the drive letter is the result of a SUBST.
+try:
+ from nt import _getvolumepathname
+except ImportError:
+ _getvolumepathname = None
def ismount(path):
- """Test whether a path is a mount point (defined as root of drive)"""
+ """Test whether a path is a mount point (a drive root, the root of a
+ share, or a mounted volume)"""
seps = _get_bothseps(path)
+ path = abspath(path)
root, rest = splitdrive(path)
if root and root[0] in seps:
return (not rest) or (rest in seps)
- return rest in seps
+ if rest in seps:
+ return True
+
+ if _getvolumepathname:
+ return path.rstrip(seps) == _getvolumepathname(path).rstrip(seps)
+ else:
+ return False
# Expand paths beginning with '~' or '~user'.
@@ -530,7 +545,7 @@ else: # use native Windows method on Windows
if path: # Empty path must return current working directory.
try:
path = _getfullpathname(path)
- except WindowsError:
+ except OSError:
pass # Bad path - return unchanged.
elif isinstance(path, bytes):
path = os.getcwdb()
@@ -598,23 +613,6 @@ except (AttributeError, ImportError):
def _getfinalpathname(f):
return normcase(abspath(f))
-def samefile(f1, f2):
- "Test whether two pathnames reference the same actual file"
- return _getfinalpathname(f1) == _getfinalpathname(f2)
-
-
-try:
- from nt import _getfileinformation
-except ImportError:
- # On other operating systems, just return the fd and see that
- # it compares equal in sameopenfile.
- def _getfileinformation(fd):
- return fd
-
-def sameopenfile(f1, f2):
- """Test whether two file objects reference the same file"""
- return _getfileinformation(f1) == _getfileinformation(f2)
-
try:
# The genericpath.isdir implementation uses os.stat and checks the mode
diff --git a/Lib/nturl2path.py b/Lib/nturl2path.py
index 511dcec..5a6d44a 100644
--- a/Lib/nturl2path.py
+++ b/Lib/nturl2path.py
@@ -23,7 +23,7 @@ def url2pathname(url):
comp = url.split('|')
if len(comp) != 2 or comp[0][-1] not in string.ascii_letters:
error = 'Bad URL: ' + url
- raise IOError(error)
+ raise OSError(error)
drive = comp[0][-1].upper()
components = comp[1].split('/')
path = drive + ':'
@@ -55,7 +55,7 @@ def pathname2url(p):
comp = p.split(':')
if len(comp) != 2 or len(comp[0]) > 1:
error = 'Bad path: ' + p
- raise IOError(error)
+ raise OSError(error)
drive = urllib.parse.quote(comp[0].upper())
components = comp[1].split('\\')
diff --git a/Lib/opcode.py b/Lib/opcode.py
index a639fe3..0bd1ee6 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -8,6 +8,19 @@ __all__ = ["cmp_op", "hasconst", "hasname", "hasjrel", "hasjabs",
"haslocal", "hascompare", "hasfree", "opname", "opmap",
"HAVE_ARGUMENT", "EXTENDED_ARG", "hasnargs"]
+# It's a chicken-and-egg I'm afraid:
+# We're imported before _opcode's made.
+# With exception unheeded
+# (stack_effect is not needed)
+# Both our chickens and eggs are allayed.
+# --Larry Hastings, 2013/11/23
+
+try:
+ from _opcode import stack_effect
+ __all__.append('stack_effect')
+except ImportError:
+ pass
+
cmp_op = ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is',
'is not', 'exception match', 'BAD')
@@ -84,7 +97,6 @@ def_op('BINARY_XOR', 65)
def_op('BINARY_OR', 66)
def_op('INPLACE_POWER', 67)
def_op('GET_ITER', 68)
-def_op('STORE_LOCALS', 69)
def_op('PRINT_EXPR', 70)
def_op('LOAD_BUILD_CLASS', 71)
@@ -179,6 +191,9 @@ def_op('LIST_APPEND', 145)
def_op('SET_ADD', 146)
def_op('MAP_ADD', 147)
+def_op('LOAD_CLASSDEREF', 148)
+hasfree.append(148)
+
def_op('EXTENDED_ARG', 144)
EXTENDED_ARG = 144
diff --git a/Lib/operator.py b/Lib/operator.py
new file mode 100644
index 0000000..b60349f
--- /dev/null
+++ b/Lib/operator.py
@@ -0,0 +1,411 @@
+"""
+Operator Interface
+
+This module exports a set of functions corresponding to the intrinsic
+operators of Python. For example, operator.add(x, y) is equivalent
+to the expression x+y. The function names are those used for special
+methods; variants without leading and trailing '__' are also provided
+for convenience.
+
+This is the pure Python implementation of the module.
+"""
+
+__all__ = ['abs', 'add', 'and_', 'attrgetter', 'concat', 'contains', 'countOf',
+ 'delitem', 'eq', 'floordiv', 'ge', 'getitem', 'gt', 'iadd', 'iand',
+ 'iconcat', 'ifloordiv', 'ilshift', 'imod', 'imul', 'index',
+ 'indexOf', 'inv', 'invert', 'ior', 'ipow', 'irshift', 'is_',
+ 'is_not', 'isub', 'itemgetter', 'itruediv', 'ixor', 'le',
+ 'length_hint', 'lshift', 'lt', 'methodcaller', 'mod', 'mul', 'ne',
+ 'neg', 'not_', 'or_', 'pos', 'pow', 'rshift', 'setitem', 'sub',
+ 'truediv', 'truth', 'xor']
+
+from builtins import abs as _abs
+
+
+# Comparison Operations *******************************************************#
+
+def lt(a, b):
+ "Same as a < b."
+ return a < b
+
+def le(a, b):
+ "Same as a <= b."
+ return a <= b
+
+def eq(a, b):
+ "Same as a == b."
+ return a == b
+
+def ne(a, b):
+ "Same as a != b."
+ return a != b
+
+def ge(a, b):
+ "Same as a >= b."
+ return a >= b
+
+def gt(a, b):
+ "Same as a > b."
+ return a > b
+
+# Logical Operations **********************************************************#
+
+def not_(a):
+ "Same as not a."
+ return not a
+
+def truth(a):
+ "Return True if a is true, False otherwise."
+ return True if a else False
+
+def is_(a, b):
+ "Same as a is b."
+ return a is b
+
+def is_not(a, b):
+ "Same as a is not b."
+ return a is not b
+
+# Mathematical/Bitwise Operations *********************************************#
+
+def abs(a):
+ "Same as abs(a)."
+ return _abs(a)
+
+def add(a, b):
+ "Same as a + b."
+ return a + b
+
+def and_(a, b):
+ "Same as a & b."
+ return a & b
+
+def floordiv(a, b):
+ "Same as a // b."
+ return a // b
+
+def index(a):
+ "Same as a.__index__()."
+ return a.__index__()
+
+def inv(a):
+ "Same as ~a."
+ return ~a
+invert = inv
+
+def lshift(a, b):
+ "Same as a << b."
+ return a << b
+
+def mod(a, b):
+ "Same as a % b."
+ return a % b
+
+def mul(a, b):
+ "Same as a * b."
+ return a * b
+
+def neg(a):
+ "Same as -a."
+ return -a
+
+def or_(a, b):
+ "Same as a | b."
+ return a | b
+
+def pos(a):
+ "Same as +a."
+ return +a
+
+def pow(a, b):
+ "Same as a ** b."
+ return a ** b
+
+def rshift(a, b):
+ "Same as a >> b."
+ return a >> b
+
+def sub(a, b):
+ "Same as a - b."
+ return a - b
+
+def truediv(a, b):
+ "Same as a / b."
+ return a / b
+
+def xor(a, b):
+ "Same as a ^ b."
+ return a ^ b
+
+# Sequence Operations *********************************************************#
+
+def concat(a, b):
+ "Same as a + b, for a and b sequences."
+ if not hasattr(a, '__getitem__'):
+ msg = "'%s' object can't be concatenated" % type(a).__name__
+ raise TypeError(msg)
+ return a + b
+
+def contains(a, b):
+ "Same as b in a (note reversed operands)."
+ return b in a
+
+def countOf(a, b):
+ "Return the number of times b occurs in a."
+ count = 0
+ for i in a:
+ if i == b:
+ count += 1
+ return count
+
+def delitem(a, b):
+ "Same as del a[b]."
+ del a[b]
+
+def getitem(a, b):
+ "Same as a[b]."
+ return a[b]
+
+def indexOf(a, b):
+ "Return the first index of b in a."
+ for i, j in enumerate(a):
+ if j == b:
+ return i
+ else:
+ raise ValueError('sequence.index(x): x not in sequence')
+
+def setitem(a, b, c):
+ "Same as a[b] = c."
+ a[b] = c
+
+def length_hint(obj, default=0):
+ """
+ Return an estimate of the number of items in obj.
+ This is useful for presizing containers when building from an iterable.
+
+ If the object supports len(), the result will be exact. Otherwise, it may
+ over- or under-estimate by an arbitrary amount. The result will be an
+ integer >= 0.
+ """
+ if not isinstance(default, int):
+ msg = ("'%s' object cannot be interpreted as an integer" %
+ type(default).__name__)
+ raise TypeError(msg)
+
+ try:
+ return len(obj)
+ except TypeError:
+ pass
+
+ try:
+ hint = type(obj).__length_hint__
+ except AttributeError:
+ return default
+
+ try:
+ val = hint(obj)
+ except TypeError:
+ return default
+ if val is NotImplemented:
+ return default
+ if not isinstance(val, int):
+ msg = ('__length_hint__ must be integer, not %s' %
+ type(val).__name__)
+ raise TypeError(msg)
+ if val < 0:
+ msg = '__length_hint__() should return >= 0'
+ raise ValueError(msg)
+ return val
+
+# Generalized Lookup Objects **************************************************#
+
+class attrgetter:
+ """
+ Return a callable object that fetches the given attribute(s) from its operand.
+ After f = attrgetter('name'), the call f(r) returns r.name.
+ After g = attrgetter('name', 'date'), the call g(r) returns (r.name, r.date).
+ After h = attrgetter('name.first', 'name.last'), the call h(r) returns
+ (r.name.first, r.name.last).
+ """
+ def __init__(self, attr, *attrs):
+ if not attrs:
+ if not isinstance(attr, str):
+ raise TypeError('attribute name must be a string')
+ names = attr.split('.')
+ def func(obj):
+ for name in names:
+ obj = getattr(obj, name)
+ return obj
+ self._call = func
+ else:
+ getters = tuple(map(attrgetter, (attr,) + attrs))
+ def func(obj):
+ return tuple(getter(obj) for getter in getters)
+ self._call = func
+
+ def __call__(self, obj):
+ return self._call(obj)
+
+class itemgetter:
+ """
+ Return a callable object that fetches the given item(s) from its operand.
+ After f = itemgetter(2), the call f(r) returns r[2].
+ After g = itemgetter(2, 5, 3), the call g(r) returns (r[2], r[5], r[3])
+ """
+ def __init__(self, item, *items):
+ if not items:
+ def func(obj):
+ return obj[item]
+ self._call = func
+ else:
+ items = (item,) + items
+ def func(obj):
+ return tuple(obj[i] for i in items)
+ self._call = func
+
+ def __call__(self, obj):
+ return self._call(obj)
+
+class methodcaller:
+ """
+ Return a callable object that calls the given method on its operand.
+ After f = methodcaller('name'), the call f(r) returns r.name().
+ After g = methodcaller('name', 'date', foo=1), the call g(r) returns
+ r.name('date', foo=1).
+ """
+
+ def __init__(*args, **kwargs):
+ if len(args) < 2:
+ msg = "methodcaller needs at least one argument, the method name"
+ raise TypeError(msg)
+ self = args[0]
+ self._name = args[1]
+ self._args = args[2:]
+ self._kwargs = kwargs
+
+ def __call__(self, obj):
+ return getattr(obj, self._name)(*self._args, **self._kwargs)
+
+# In-place Operations *********************************************************#
+
+def iadd(a, b):
+ "Same as a += b."
+ a += b
+ return a
+
+def iand(a, b):
+ "Same as a &= b."
+ a &= b
+ return a
+
+def iconcat(a, b):
+ "Same as a += b, for a and b sequences."
+ if not hasattr(a, '__getitem__'):
+ msg = "'%s' object can't be concatenated" % type(a).__name__
+ raise TypeError(msg)
+ a += b
+ return a
+
+def ifloordiv(a, b):
+ "Same as a //= b."
+ a //= b
+ return a
+
+def ilshift(a, b):
+ "Same as a <<= b."
+ a <<= b
+ return a
+
+def imod(a, b):
+ "Same as a %= b."
+ a %= b
+ return a
+
+def imul(a, b):
+ "Same as a *= b."
+ a *= b
+ return a
+
+def ior(a, b):
+ "Same as a |= b."
+ a |= b
+ return a
+
+def ipow(a, b):
+ "Same as a **= b."
+ a **=b
+ return a
+
+def irshift(a, b):
+ "Same as a >>= b."
+ a >>= b
+ return a
+
+def isub(a, b):
+ "Same as a -= b."
+ a -= b
+ return a
+
+def itruediv(a, b):
+ "Same as a /= b."
+ a /= b
+ return a
+
+def ixor(a, b):
+ "Same as a ^= b."
+ a ^= b
+ return a
+
+
+try:
+ from _operator import *
+except ImportError:
+ pass
+else:
+ from _operator import __doc__
+
+# All of these "__func__ = func" assignments have to happen after importing
+# from _operator to make sure they're set to the right function
+__lt__ = lt
+__le__ = le
+__eq__ = eq
+__ne__ = ne
+__ge__ = ge
+__gt__ = gt
+__not__ = not_
+__abs__ = abs
+__add__ = add
+__and__ = and_
+__floordiv__ = floordiv
+__index__ = index
+__inv__ = inv
+__invert__ = invert
+__lshift__ = lshift
+__mod__ = mod
+__mul__ = mul
+__neg__ = neg
+__or__ = or_
+__pos__ = pos
+__pow__ = pow
+__rshift__ = rshift
+__sub__ = sub
+__truediv__ = truediv
+__xor__ = xor
+__concat__ = concat
+__contains__ = contains
+__delitem__ = delitem
+__getitem__ = getitem
+__setitem__ = setitem
+__iadd__ = iadd
+__iand__ = iand
+__iconcat__ = iconcat
+__ifloordiv__ = ifloordiv
+__ilshift__ = ilshift
+__imod__ = imod
+__imul__ = imul
+__ior__ = ior
+__ipow__ = ipow
+__irshift__ = irshift
+__isub__ = isub
+__itruediv__ = itruediv
+__ixor__ = ixor
diff --git a/Lib/os.py b/Lib/os.py
index b42ccba..8567f50 100644
--- a/Lib/os.py
+++ b/Lib/os.py
@@ -1,9 +1,9 @@
r"""OS routines for Mac, NT, or Posix depending on what system we're on.
This exports:
- - all functions from posix, nt, os2, or ce, e.g. unlink, stat, etc.
+ - all functions from posix, nt or ce, e.g. unlink, stat, etc.
- os.path is either posixpath or ntpath
- - os.name is either 'posix', 'nt', 'os2' or 'ce'.
+ - os.name is either 'posix', 'nt' or 'ce'.
- os.curdir is a string representing the current directory ('.' or ':')
- os.pardir is a string representing the parent directory ('..' or '::')
- os.sep is the (or a most common) pathname separator ('/' or ':' or '\\')
@@ -81,30 +81,6 @@ elif 'nt' in _names:
except ImportError:
pass
-elif 'os2' in _names:
- name = 'os2'
- linesep = '\r\n'
- from os2 import *
- try:
- from os2 import _exit
- __all__.append('_exit')
- except ImportError:
- pass
- if sys.version.find('EMX GCC') == -1:
- import ntpath as path
- else:
- import os2emxpath as path
- from _emx_link import link
-
- import os2
- __all__.extend(_get_exports_list(os2))
- del os2
-
- try:
- from os2 import _have_functions
- except ImportError:
- pass
-
elif 'ce' in _names:
name = 'ce'
linesep = '\r\n'
@@ -234,7 +210,7 @@ SEEK_END = 2
# (Inspired by Eric Raymond; the doc strings are mostly his)
def makedirs(name, mode=0o777, exist_ok=False):
- """makedirs(path [, mode=0o777][, exist_ok=False])
+ """makedirs(name [, mode=0o777][, exist_ok=False])
Super-mkdir; create a leaf directory and all intermediate ones. Works like
mkdir, except that any intermediate path segment (not just the rightmost)
@@ -249,10 +225,9 @@ def makedirs(name, mode=0o777, exist_ok=False):
if head and tail and not path.exists(head):
try:
makedirs(head, mode, exist_ok)
- except OSError as e:
+ except FileExistsError:
# be happy if someone already created the path
- if e.errno != errno.EEXIST:
- raise
+ pass
cdir = curdir
if isinstance(tail, bytes):
cdir = bytes(curdir, 'ASCII')
@@ -265,7 +240,7 @@ def makedirs(name, mode=0o777, exist_ok=False):
raise
def removedirs(name):
- """removedirs(path)
+ """removedirs(name)
Super-rmdir; remove a leaf directory and all empty intermediate
ones. Works like rmdir except that, if the leaf directory is
@@ -282,7 +257,7 @@ def removedirs(name):
while head and tail:
try:
rmdir(head)
- except error:
+ except OSError:
break
head, tail = path.split(head)
@@ -309,7 +284,7 @@ def renames(old, new):
if head and tail:
try:
removedirs(head)
- except error:
+ except OSError:
pass
__all__.extend(["makedirs", "removedirs", "renames"])
@@ -345,7 +320,7 @@ def walk(top, topdown=True, onerror=None, followlinks=False):
By default errors from the os.listdir() call are ignored. If
optional arg 'onerror' is specified, it should be a function; it
- will be called with one argument, an os.error instance. It can
+ will be called with one argument, an OSError instance. It can
report the error to continue with the walk, or raise the exception
to abort the walk. Note that the filename is available as the
filename attribute of the exception object.
@@ -379,10 +354,10 @@ def walk(top, topdown=True, onerror=None, followlinks=False):
# minor reason when (say) a thousand readable directories are still
# left to visit. That logic is copied here.
try:
- # Note that listdir and error are globals in this module due
+ # Note that listdir is global in this module due
# to earlier import-*.
names = listdir(top)
- except error as err:
+ except OSError as err:
if onerror is not None:
onerror(err)
return
@@ -484,7 +459,7 @@ if {open, stat} <= supports_dir_fd and {listdir, stat} <= supports_fd:
try:
orig_st = stat(name, dir_fd=topfd, follow_symlinks=follow_symlinks)
dirfd = open(name, O_RDONLY, dir_fd=topfd)
- except error as err:
+ except OSError as err:
if onerror is not None:
onerror(err)
return
@@ -579,7 +554,7 @@ def _execvpe(file, args, env=None):
fullname = path.join(dir, file)
try:
exec_func(fullname, *argrest)
- except error as e:
+ except OSError as e:
last_exc = e
tb = sys.exc_info()[2]
if (e.errno != errno.ENOENT and e.errno != errno.ENOTDIR
@@ -636,7 +611,7 @@ def get_exec_path(env=None):
# Change environ to automatically call putenv(), unsetenv if they exist.
-from collections.abc import MutableMapping
+from _collections_abc import MutableMapping
class _Environ(MutableMapping):
def __init__(self, data, encodekey, decodekey, encodevalue, decodevalue, putenv, unsetenv):
@@ -696,17 +671,19 @@ try:
except NameError:
_putenv = lambda key, value: None
else:
- __all__.append("putenv")
+ if "putenv" not in __all__:
+ __all__.append("putenv")
try:
_unsetenv = unsetenv
except NameError:
_unsetenv = lambda key: _putenv(key, "")
else:
- __all__.append("unsetenv")
+ if "unsetenv" not in __all__:
+ __all__.append("unsetenv")
def _createenviron():
- if name in ('os2', 'nt'):
+ if name == 'nt':
# Where Env Var Names Must Be UPPERCASE
def check_str(value):
if not isinstance(value, str):
@@ -746,7 +723,7 @@ def getenv(key, default=None):
key, default and the result are str."""
return environ.get(key, default)
-supports_bytes_environ = name not in ('os2', 'nt')
+supports_bytes_environ = (name != 'nt')
__all__.extend(("getenv", "supports_bytes_environ"))
if supports_bytes_environ:
@@ -845,7 +822,7 @@ if _exists("fork") and not _exists("spawnv") and _exists("execv"):
elif WIFEXITED(sts):
return WEXITSTATUS(sts)
else:
- raise error("Not stopped, signaled or exited???")
+ raise OSError("Not stopped, signaled or exited???")
def spawnv(mode, file, args):
"""spawnv(mode, file, args) -> integer
@@ -888,6 +865,10 @@ If mode == P_WAIT return the process's exit code if it exits normally;
otherwise return -SIG, where SIG is the signal that killed it. """
return _spawnvef(mode, file, args, env, execvpe)
+
+ __all__.extend(["spawnv", "spawnve", "spawnvp", "spawnvpe"])
+
+
if _exists("spawnv"):
# These aren't supplied by the basic Windows code
# but can be easily implemented in Python
@@ -913,7 +894,7 @@ otherwise return -SIG, where SIG is the signal that killed it. """
return spawnve(mode, file, args[:-1], env)
- __all__.extend(["spawnv", "spawnve", "spawnl", "spawnle",])
+ __all__.extend(["spawnl", "spawnle"])
if _exists("spawnvp"):
@@ -941,34 +922,8 @@ otherwise return -SIG, where SIG is the signal that killed it. """
return spawnvpe(mode, file, args[:-1], env)
- __all__.extend(["spawnvp", "spawnvpe", "spawnlp", "spawnlpe",])
-
-import copyreg as _copyreg
-
-def _make_stat_result(tup, dict):
- return stat_result(tup, dict)
+ __all__.extend(["spawnlp", "spawnlpe"])
-def _pickle_stat_result(sr):
- (type, args) = sr.__reduce__()
- return (_make_stat_result, args)
-
-try:
- _copyreg.pickle(stat_result, _pickle_stat_result, _make_stat_result)
-except NameError: # stat_result may not exist
- pass
-
-def _make_statvfs_result(tup, dict):
- return statvfs_result(tup, dict)
-
-def _pickle_statvfs_result(sr):
- (type, args) = sr.__reduce__()
- return (_make_statvfs_result, args)
-
-try:
- _copyreg.pickle(statvfs_result, _pickle_statvfs_result,
- _make_statvfs_result)
-except NameError: # statvfs_result may not exist
- pass
# Supply os.popen()
def popen(cmd, mode="r", buffering=-1):
diff --git a/Lib/os2emxpath.py b/Lib/os2emxpath.py
deleted file mode 100644
index 0ccbf8a..0000000
--- a/Lib/os2emxpath.py
+++ /dev/null
@@ -1,158 +0,0 @@
-# Module 'os2emxpath' -- common operations on OS/2 pathnames
-"""Common pathname manipulations, OS/2 EMX version.
-
-Instead of importing this module directly, import os and refer to this
-module as os.path.
-"""
-
-import os
-import stat
-from genericpath import *
-from ntpath import (expanduser, expandvars, isabs, islink, splitdrive,
- splitext, split)
-
-__all__ = ["normcase","isabs","join","splitdrive","split","splitext",
- "basename","dirname","commonprefix","getsize","getmtime",
- "getatime","getctime", "islink","exists","lexists","isdir","isfile",
- "ismount","expanduser","expandvars","normpath","abspath",
- "splitunc","curdir","pardir","sep","pathsep","defpath","altsep",
- "extsep","devnull","realpath","supports_unicode_filenames"]
-
-# strings representing various path-related bits and pieces
-curdir = '.'
-pardir = '..'
-extsep = '.'
-sep = '/'
-altsep = '\\'
-pathsep = ';'
-defpath = '.;C:\\bin'
-devnull = 'nul'
-
-# Normalize the case of a pathname and map slashes to backslashes.
-# Other normalizations (such as optimizing '../' away) are not done
-# (this is done by normpath).
-
-def normcase(s):
- """Normalize case of pathname.
-
- Makes all characters lowercase and all altseps into seps."""
- if not isinstance(s, (bytes, str)):
- raise TypeError("normcase() argument must be str or bytes, "
- "not '{}'".format(s.__class__.__name__))
- return s.replace('\\', '/').lower()
-
-
-# Join two (or more) paths.
-
-def join(a, *p):
- """Join two or more pathname components, inserting sep as needed"""
- path = a
- for b in p:
- if isabs(b):
- path = b
- elif path == '' or path[-1:] in '/\\:':
- path = path + b
- else:
- path = path + '/' + b
- return path
-
-
-# Parse UNC paths
-def splitunc(p):
- """Split a pathname into UNC mount point and relative path specifiers.
-
- Return a 2-tuple (unc, rest); either part may be empty.
- If unc is not empty, it has the form '//host/mount' (or similar
- using backslashes). unc+rest is always the input path.
- Paths containing drive letters never have an UNC part.
- """
- if p[1:2] == ':':
- return '', p # Drive letter present
- firstTwo = p[0:2]
- if firstTwo == '/' * 2 or firstTwo == '\\' * 2:
- # is a UNC path:
- # vvvvvvvvvvvvvvvvvvvv equivalent to drive letter
- # \\machine\mountpoint\directories...
- # directory ^^^^^^^^^^^^^^^
- normp = normcase(p)
- index = normp.find('/', 2)
- if index == -1:
- ##raise RuntimeError, 'illegal UNC path: "' + p + '"'
- return ("", p)
- index = normp.find('/', index + 1)
- if index == -1:
- index = len(p)
- return p[:index], p[index:]
- return '', p
-
-
-# Return the tail (basename) part of a path.
-
-def basename(p):
- """Returns the final component of a pathname"""
- return split(p)[1]
-
-
-# Return the head (dirname) part of a path.
-
-def dirname(p):
- """Returns the directory component of a pathname"""
- return split(p)[0]
-
-
-# alias exists to lexists
-lexists = exists
-
-
-# Is a path a directory?
-
-# Is a path a mount point? Either a root (with or without drive letter)
-# or an UNC path with at most a / or \ after the mount point.
-
-def ismount(path):
- """Test whether a path is a mount point (defined as root of drive)"""
- unc, rest = splitunc(path)
- if unc:
- return rest in ("", "/", "\\")
- p = splitdrive(path)[1]
- return len(p) == 1 and p[0] in '/\\'
-
-
-# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
-
-def normpath(path):
- """Normalize path, eliminating double slashes, etc."""
- path = path.replace('\\', '/')
- prefix, path = splitdrive(path)
- while path[:1] == '/':
- prefix = prefix + '/'
- path = path[1:]
- comps = path.split('/')
- i = 0
- while i < len(comps):
- if comps[i] == '.':
- del comps[i]
- elif comps[i] == '..' and i > 0 and comps[i-1] not in ('', '..'):
- del comps[i-1:i+1]
- i = i - 1
- elif comps[i] == '' and i > 0 and comps[i-1] != '':
- del comps[i]
- else:
- i = i + 1
- # If the path is now empty, substitute '.'
- if not prefix and not comps:
- comps.append('.')
- return prefix + '/'.join(comps)
-
-
-# Return an absolute path.
-def abspath(path):
- """Return the absolute version of a path"""
- if not isabs(path):
- path = join(os.getcwd(), path)
- return normpath(path)
-
-# realpath is a no-op on systems without islink support
-realpath = abspath
-
-supports_unicode_filenames = False
diff --git a/Lib/pathlib.py b/Lib/pathlib.py
new file mode 100644
index 0000000..d3d1af8
--- /dev/null
+++ b/Lib/pathlib.py
@@ -0,0 +1,1288 @@
+import fnmatch
+import functools
+import io
+import ntpath
+import os
+import posixpath
+import re
+import sys
+from collections import Sequence
+from contextlib import contextmanager
+from errno import EINVAL, ENOENT
+from operator import attrgetter
+from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO
+from urllib.parse import quote_from_bytes as urlquote_from_bytes
+
+
+supports_symlinks = True
+try:
+ import nt
+except ImportError:
+ nt = None
+else:
+ if sys.getwindowsversion()[:2] >= (6, 0):
+ from nt import _getfinalpathname
+ else:
+ supports_symlinks = False
+ _getfinalpathname = None
+
+
+__all__ = [
+ "PurePath", "PurePosixPath", "PureWindowsPath",
+ "Path", "PosixPath", "WindowsPath",
+ ]
+
+#
+# Internals
+#
+
+def _is_wildcard_pattern(pat):
+ # Whether this pattern needs actual matching using fnmatch, or can
+ # be looked up directly as a file.
+ return "*" in pat or "?" in pat or "[" in pat
+
+
+class _Flavour(object):
+ """A flavour implements a particular (platform-specific) set of path
+ semantics."""
+
+ def __init__(self):
+ self.join = self.sep.join
+
+ def parse_parts(self, parts):
+ parsed = []
+ sep = self.sep
+ altsep = self.altsep
+ drv = root = ''
+ it = reversed(parts)
+ for part in it:
+ if not part:
+ continue
+ if altsep:
+ part = part.replace(altsep, sep)
+ drv, root, rel = self.splitroot(part)
+ if sep in rel:
+ for x in reversed(rel.split(sep)):
+ if x and x != '.':
+ parsed.append(sys.intern(x))
+ else:
+ if rel and rel != '.':
+ parsed.append(sys.intern(rel))
+ if drv or root:
+ if not drv:
+ # If no drive is present, try to find one in the previous
+ # parts. This makes the result of parsing e.g.
+ # ("C:", "/", "a") reasonably intuitive.
+ for part in it:
+ drv = self.splitroot(part)[0]
+ if drv:
+ break
+ break
+ if drv or root:
+ parsed.append(drv + root)
+ parsed.reverse()
+ return drv, root, parsed
+
+ def join_parsed_parts(self, drv, root, parts, drv2, root2, parts2):
+ """
+ Join the two paths represented by the respective
+ (drive, root, parts) tuples. Return a new (drive, root, parts) tuple.
+ """
+ if root2:
+ if not drv2 and drv:
+ return drv, root2, [drv + root2] + parts2[1:]
+ elif drv2:
+ if drv2 == drv or self.casefold(drv2) == self.casefold(drv):
+ # Same drive => second path is relative to the first
+ return drv, root, parts + parts2[1:]
+ else:
+ # Second path is non-anchored (common case)
+ return drv, root, parts + parts2
+ return drv2, root2, parts2
+
+
+class _WindowsFlavour(_Flavour):
+ # Reference for Windows paths can be found at
+ # http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx
+
+ sep = '\\'
+ altsep = '/'
+ has_drv = True
+ pathmod = ntpath
+
+ is_supported = (nt is not None)
+
+ drive_letters = (
+ set(chr(x) for x in range(ord('a'), ord('z') + 1)) |
+ set(chr(x) for x in range(ord('A'), ord('Z') + 1))
+ )
+ ext_namespace_prefix = '\\\\?\\'
+
+ reserved_names = (
+ {'CON', 'PRN', 'AUX', 'NUL'} |
+ {'COM%d' % i for i in range(1, 10)} |
+ {'LPT%d' % i for i in range(1, 10)}
+ )
+
+ # Interesting findings about extended paths:
+ # - '\\?\c:\a', '//?/c:\a' and '//?/c:/a' are all supported
+ # but '\\?\c:/a' is not
+ # - extended paths are always absolute; "relative" extended paths will
+ # fail.
+
+ def splitroot(self, part, sep=sep):
+ first = part[0:1]
+ second = part[1:2]
+ if (second == sep and first == sep):
+ # XXX extended paths should also disable the collapsing of "."
+ # components (according to MSDN docs).
+ prefix, part = self._split_extended_path(part)
+ first = part[0:1]
+ second = part[1:2]
+ else:
+ prefix = ''
+ third = part[2:3]
+ if (second == sep and first == sep and third != sep):
+ # is a UNC path:
+ # vvvvvvvvvvvvvvvvvvvvv root
+ # \\machine\mountpoint\directory\etc\...
+ # directory ^^^^^^^^^^^^^^
+ index = part.find(sep, 2)
+ if index != -1:
+ index2 = part.find(sep, index + 1)
+ # a UNC path can't have two slashes in a row
+ # (after the initial two)
+ if index2 != index + 1:
+ if index2 == -1:
+ index2 = len(part)
+ if prefix:
+ return prefix + part[1:index2], sep, part[index2+1:]
+ else:
+ return part[:index2], sep, part[index2+1:]
+ drv = root = ''
+ if second == ':' and first in self.drive_letters:
+ drv = part[:2]
+ part = part[2:]
+ first = third
+ if first == sep:
+ root = first
+ part = part.lstrip(sep)
+ return prefix + drv, root, part
+
+ def casefold(self, s):
+ return s.lower()
+
+ def casefold_parts(self, parts):
+ return [p.lower() for p in parts]
+
+ def resolve(self, path):
+ s = str(path)
+ if not s:
+ return os.getcwd()
+ if _getfinalpathname is not None:
+ return self._ext_to_normal(_getfinalpathname(s))
+ # Means fallback on absolute
+ return None
+
+ def _split_extended_path(self, s, ext_prefix=ext_namespace_prefix):
+ prefix = ''
+ if s.startswith(ext_prefix):
+ prefix = s[:4]
+ s = s[4:]
+ if s.startswith('UNC\\'):
+ prefix += s[:3]
+ s = '\\' + s[3:]
+ return prefix, s
+
+ def _ext_to_normal(self, s):
+ # Turn back an extended path into a normal DOS-like path
+ return self._split_extended_path(s)[1]
+
+ def is_reserved(self, parts):
+ # NOTE: the rules for reserved names seem somewhat complicated
+ # (e.g. r"..\NUL" is reserved but not r"foo\NUL").
+ # We err on the side of caution and return True for paths which are
+ # not considered reserved by Windows.
+ if not parts:
+ return False
+ if parts[0].startswith('\\\\'):
+ # UNC paths are never reserved
+ return False
+ return parts[-1].partition('.')[0].upper() in self.reserved_names
+
+ def make_uri(self, path):
+ # Under Windows, file URIs use the UTF-8 encoding.
+ drive = path.drive
+ if len(drive) == 2 and drive[1] == ':':
+ # It's a path on a local drive => 'file:///c:/a/b'
+ rest = path.as_posix()[2:].lstrip('/')
+ return 'file:///%s/%s' % (
+ drive, urlquote_from_bytes(rest.encode('utf-8')))
+ else:
+ # It's a path on a network drive => 'file://host/share/a/b'
+ return 'file:' + urlquote_from_bytes(path.as_posix().encode('utf-8'))
+
+
+class _PosixFlavour(_Flavour):
+ sep = '/'
+ altsep = ''
+ has_drv = False
+ pathmod = posixpath
+
+ is_supported = (os.name != 'nt')
+
+ def splitroot(self, part, sep=sep):
+ if part and part[0] == sep:
+ stripped_part = part.lstrip(sep)
+ # According to POSIX path resolution:
+ # http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11
+ # "A pathname that begins with two successive slashes may be
+ # interpreted in an implementation-defined manner, although more
+ # than two leading slashes shall be treated as a single slash".
+ if len(part) - len(stripped_part) == 2:
+ return '', sep * 2, stripped_part
+ else:
+ return '', sep, stripped_part
+ else:
+ return '', '', part
+
+ def casefold(self, s):
+ return s
+
+ def casefold_parts(self, parts):
+ return parts
+
+ def resolve(self, path):
+ sep = self.sep
+ accessor = path._accessor
+ seen = {}
+ def _resolve(path, rest):
+ if rest.startswith(sep):
+ path = ''
+
+ for name in rest.split(sep):
+ if not name or name == '.':
+ # current dir
+ continue
+ if name == '..':
+ # parent dir
+ path, _, _ = path.rpartition(sep)
+ continue
+ newpath = path + sep + name
+ if newpath in seen:
+ # Already seen this path
+ path = seen[newpath]
+ if path is not None:
+ # use cached value
+ continue
+ # The symlink is not resolved, so we must have a symlink loop.
+ raise RuntimeError("Symlink loop from %r" % newpath)
+ # Resolve the symbolic link
+ try:
+ target = accessor.readlink(newpath)
+ except OSError as e:
+ if e.errno != EINVAL:
+ raise
+ # Not a symlink
+ path = newpath
+ else:
+ seen[newpath] = None # not resolved symlink
+ path = _resolve(path, target)
+ seen[newpath] = path # resolved symlink
+
+ return path
+ # NOTE: according to POSIX, getcwd() cannot contain path components
+ # which are symlinks.
+ base = '' if path.is_absolute() else os.getcwd()
+ return _resolve(base, str(path)) or sep
+
+ def is_reserved(self, parts):
+ return False
+
+ def make_uri(self, path):
+ # We represent the path using the local filesystem encoding,
+ # for portability to other applications.
+ bpath = bytes(path)
+ return 'file://' + urlquote_from_bytes(bpath)
+
+
+_windows_flavour = _WindowsFlavour()
+_posix_flavour = _PosixFlavour()
+
+
+class _Accessor:
+ """An accessor implements a particular (system-specific or not) way of
+ accessing paths on the filesystem."""
+
+
+class _NormalAccessor(_Accessor):
+
+ def _wrap_strfunc(strfunc):
+ @functools.wraps(strfunc)
+ def wrapped(pathobj, *args):
+ return strfunc(str(pathobj), *args)
+ return staticmethod(wrapped)
+
+ def _wrap_binary_strfunc(strfunc):
+ @functools.wraps(strfunc)
+ def wrapped(pathobjA, pathobjB, *args):
+ return strfunc(str(pathobjA), str(pathobjB), *args)
+ return staticmethod(wrapped)
+
+ stat = _wrap_strfunc(os.stat)
+
+ lstat = _wrap_strfunc(os.lstat)
+
+ open = _wrap_strfunc(os.open)
+
+ listdir = _wrap_strfunc(os.listdir)
+
+ chmod = _wrap_strfunc(os.chmod)
+
+ if hasattr(os, "lchmod"):
+ lchmod = _wrap_strfunc(os.lchmod)
+ else:
+ def lchmod(self, pathobj, mode):
+ raise NotImplementedError("lchmod() not available on this system")
+
+ mkdir = _wrap_strfunc(os.mkdir)
+
+ unlink = _wrap_strfunc(os.unlink)
+
+ rmdir = _wrap_strfunc(os.rmdir)
+
+ rename = _wrap_binary_strfunc(os.rename)
+
+ replace = _wrap_binary_strfunc(os.replace)
+
+ if nt:
+ if supports_symlinks:
+ symlink = _wrap_binary_strfunc(os.symlink)
+ else:
+ def symlink(a, b, target_is_directory):
+ raise NotImplementedError("symlink() not available on this system")
+ else:
+ # Under POSIX, os.symlink() takes two args
+ @staticmethod
+ def symlink(a, b, target_is_directory):
+ return os.symlink(str(a), str(b))
+
+ utime = _wrap_strfunc(os.utime)
+
+ # Helper for resolve()
+ def readlink(self, path):
+ return os.readlink(path)
+
+
+_normal_accessor = _NormalAccessor()
+
+
+#
+# Globbing helpers
+#
+
+@contextmanager
+def _cached(func):
+ try:
+ func.__cached__
+ yield func
+ except AttributeError:
+ cache = {}
+ def wrapper(*args):
+ try:
+ return cache[args]
+ except KeyError:
+ value = cache[args] = func(*args)
+ return value
+ wrapper.__cached__ = True
+ try:
+ yield wrapper
+ finally:
+ cache.clear()
+
+def _make_selector(pattern_parts):
+ pat = pattern_parts[0]
+ child_parts = pattern_parts[1:]
+ if pat == '**':
+ cls = _RecursiveWildcardSelector
+ elif '**' in pat:
+ raise ValueError("Invalid pattern: '**' can only be an entire path component")
+ elif _is_wildcard_pattern(pat):
+ cls = _WildcardSelector
+ else:
+ cls = _PreciseSelector
+ return cls(pat, child_parts)
+
+if hasattr(functools, "lru_cache"):
+ _make_selector = functools.lru_cache()(_make_selector)
+
+
+class _Selector:
+ """A selector matches a specific glob pattern part against the children
+ of a given path."""
+
+ def __init__(self, child_parts):
+ self.child_parts = child_parts
+ if child_parts:
+ self.successor = _make_selector(child_parts)
+ else:
+ self.successor = _TerminatingSelector()
+
+ def select_from(self, parent_path):
+ """Iterate over all child paths of `parent_path` matched by this
+ selector. This can contain parent_path itself."""
+ path_cls = type(parent_path)
+ is_dir = path_cls.is_dir
+ exists = path_cls.exists
+ listdir = parent_path._accessor.listdir
+ return self._select_from(parent_path, is_dir, exists, listdir)
+
+
+class _TerminatingSelector:
+
+ def _select_from(self, parent_path, is_dir, exists, listdir):
+ yield parent_path
+
+
+class _PreciseSelector(_Selector):
+
+ def __init__(self, name, child_parts):
+ self.name = name
+ _Selector.__init__(self, child_parts)
+
+ def _select_from(self, parent_path, is_dir, exists, listdir):
+ if not is_dir(parent_path):
+ return
+ path = parent_path._make_child_relpath(self.name)
+ if exists(path):
+ for p in self.successor._select_from(path, is_dir, exists, listdir):
+ yield p
+
+
+class _WildcardSelector(_Selector):
+
+ def __init__(self, pat, child_parts):
+ self.pat = re.compile(fnmatch.translate(pat))
+ _Selector.__init__(self, child_parts)
+
+ def _select_from(self, parent_path, is_dir, exists, listdir):
+ if not is_dir(parent_path):
+ return
+ cf = parent_path._flavour.casefold
+ for name in listdir(parent_path):
+ casefolded = cf(name)
+ if self.pat.match(casefolded):
+ path = parent_path._make_child_relpath(name)
+ for p in self.successor._select_from(path, is_dir, exists, listdir):
+ yield p
+
+
+class _RecursiveWildcardSelector(_Selector):
+
+ def __init__(self, pat, child_parts):
+ _Selector.__init__(self, child_parts)
+
+ def _iterate_directories(self, parent_path, is_dir, listdir):
+ yield parent_path
+ for name in listdir(parent_path):
+ path = parent_path._make_child_relpath(name)
+ if is_dir(path):
+ for p in self._iterate_directories(path, is_dir, listdir):
+ yield p
+
+ def _select_from(self, parent_path, is_dir, exists, listdir):
+ if not is_dir(parent_path):
+ return
+ with _cached(listdir) as listdir:
+ yielded = set()
+ try:
+ successor_select = self.successor._select_from
+ for starting_point in self._iterate_directories(parent_path, is_dir, listdir):
+ for p in successor_select(starting_point, is_dir, exists, listdir):
+ if p not in yielded:
+ yield p
+ yielded.add(p)
+ finally:
+ yielded.clear()
+
+
+#
+# Public API
+#
+
+class _PathParents(Sequence):
+ """This object provides sequence-like access to the logical ancestors
+ of a path. Don't try to construct it yourself."""
+ __slots__ = ('_pathcls', '_drv', '_root', '_parts')
+
+ def __init__(self, path):
+ # We don't store the instance to avoid reference cycles
+ self._pathcls = type(path)
+ self._drv = path._drv
+ self._root = path._root
+ self._parts = path._parts
+
+ def __len__(self):
+ if self._drv or self._root:
+ return len(self._parts) - 1
+ else:
+ return len(self._parts)
+
+ def __getitem__(self, idx):
+ if idx < 0 or idx >= len(self):
+ raise IndexError(idx)
+ return self._pathcls._from_parsed_parts(self._drv, self._root,
+ self._parts[:-idx - 1])
+
+ def __repr__(self):
+ return "<{}.parents>".format(self._pathcls.__name__)
+
+
+class PurePath(object):
+ """PurePath represents a filesystem path and offers operations which
+ don't imply any actual filesystem I/O. Depending on your system,
+ instantiating a PurePath will return either a PurePosixPath or a
+ PureWindowsPath object. You can also instantiate either of these classes
+ directly, regardless of your system.
+ """
+ __slots__ = (
+ '_drv', '_root', '_parts',
+ '_str', '_hash', '_pparts', '_cached_cparts',
+ )
+
+ def __new__(cls, *args):
+ """Construct a PurePath from one or several strings and or existing
+ PurePath objects. The strings and path objects are combined so as
+ to yield a canonicalized path, which is incorporated into the
+ new PurePath object.
+ """
+ if cls is PurePath:
+ cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
+ return cls._from_parts(args)
+
+ def __reduce__(self):
+ # Using the parts tuple helps share interned path parts
+ # when pickling related paths.
+ return (self.__class__, tuple(self._parts))
+
+ @classmethod
+ def _parse_args(cls, args):
+ # This is useful when you don't want to create an instance, just
+ # canonicalize some constructor arguments.
+ parts = []
+ for a in args:
+ if isinstance(a, PurePath):
+ parts += a._parts
+ elif isinstance(a, str):
+ # Force-cast str subclasses to str (issue #21127)
+ parts.append(str(a))
+ else:
+ raise TypeError(
+ "argument should be a path or str object, not %r"
+ % type(a))
+ return cls._flavour.parse_parts(parts)
+
+ @classmethod
+ def _from_parts(cls, args, init=True):
+ # We need to call _parse_args on the instance, so as to get the
+ # right flavour.
+ self = object.__new__(cls)
+ drv, root, parts = self._parse_args(args)
+ self._drv = drv
+ self._root = root
+ self._parts = parts
+ if init:
+ self._init()
+ return self
+
+ @classmethod
+ def _from_parsed_parts(cls, drv, root, parts, init=True):
+ self = object.__new__(cls)
+ self._drv = drv
+ self._root = root
+ self._parts = parts
+ if init:
+ self._init()
+ return self
+
+ @classmethod
+ def _format_parsed_parts(cls, drv, root, parts):
+ if drv or root:
+ return drv + root + cls._flavour.join(parts[1:])
+ else:
+ return cls._flavour.join(parts)
+
+ def _init(self):
+ # Overriden in concrete Path
+ pass
+
+ def _make_child(self, args):
+ drv, root, parts = self._parse_args(args)
+ drv, root, parts = self._flavour.join_parsed_parts(
+ self._drv, self._root, self._parts, drv, root, parts)
+ return self._from_parsed_parts(drv, root, parts)
+
+ def __str__(self):
+ """Return the string representation of the path, suitable for
+ passing to system calls."""
+ try:
+ return self._str
+ except AttributeError:
+ self._str = self._format_parsed_parts(self._drv, self._root,
+ self._parts) or '.'
+ return self._str
+
+ def as_posix(self):
+ """Return the string representation of the path with forward (/)
+ slashes."""
+ f = self._flavour
+ return str(self).replace(f.sep, '/')
+
+ def __bytes__(self):
+ """Return the bytes representation of the path. This is only
+ recommended to use under Unix."""
+ return os.fsencode(str(self))
+
+ def __repr__(self):
+ return "{}({!r})".format(self.__class__.__name__, self.as_posix())
+
+ def as_uri(self):
+ """Return the path as a 'file' URI."""
+ if not self.is_absolute():
+ raise ValueError("relative path can't be expressed as a file URI")
+ return self._flavour.make_uri(self)
+
+ @property
+ def _cparts(self):
+ # Cached casefolded parts, for hashing and comparison
+ try:
+ return self._cached_cparts
+ except AttributeError:
+ self._cached_cparts = self._flavour.casefold_parts(self._parts)
+ return self._cached_cparts
+
+ def __eq__(self, other):
+ if not isinstance(other, PurePath):
+ return NotImplemented
+ return self._cparts == other._cparts and self._flavour is other._flavour
+
+ def __ne__(self, other):
+ return not self == other
+
+ def __hash__(self):
+ try:
+ return self._hash
+ except AttributeError:
+ self._hash = hash(tuple(self._cparts))
+ return self._hash
+
+ def __lt__(self, other):
+ if not isinstance(other, PurePath) or self._flavour is not other._flavour:
+ return NotImplemented
+ return self._cparts < other._cparts
+
+ def __le__(self, other):
+ if not isinstance(other, PurePath) or self._flavour is not other._flavour:
+ return NotImplemented
+ return self._cparts <= other._cparts
+
+ def __gt__(self, other):
+ if not isinstance(other, PurePath) or self._flavour is not other._flavour:
+ return NotImplemented
+ return self._cparts > other._cparts
+
+ def __ge__(self, other):
+ if not isinstance(other, PurePath) or self._flavour is not other._flavour:
+ return NotImplemented
+ return self._cparts >= other._cparts
+
+ drive = property(attrgetter('_drv'),
+ doc="""The drive prefix (letter or UNC path), if any.""")
+
+ root = property(attrgetter('_root'),
+ doc="""The root of the path, if any.""")
+
+ @property
+ def anchor(self):
+ """The concatenation of the drive and root, or ''."""
+ anchor = self._drv + self._root
+ return anchor
+
+ @property
+ def name(self):
+ """The final path component, if any."""
+ parts = self._parts
+ if len(parts) == (1 if (self._drv or self._root) else 0):
+ return ''
+ return parts[-1]
+
+ @property
+ def suffix(self):
+ """The final component's last suffix, if any."""
+ name = self.name
+ i = name.rfind('.')
+ if 0 < i < len(name) - 1:
+ return name[i:]
+ else:
+ return ''
+
+ @property
+ def suffixes(self):
+ """A list of the final component's suffixes, if any."""
+ name = self.name
+ if name.endswith('.'):
+ return []
+ name = name.lstrip('.')
+ return ['.' + suffix for suffix in name.split('.')[1:]]
+
+ @property
+ def stem(self):
+ """The final path component, minus its last suffix."""
+ name = self.name
+ i = name.rfind('.')
+ if 0 < i < len(name) - 1:
+ return name[:i]
+ else:
+ return name
+
+ def with_name(self, name):
+ """Return a new path with the file name changed."""
+ if not self.name:
+ raise ValueError("%r has an empty name" % (self,))
+ return self._from_parsed_parts(self._drv, self._root,
+ self._parts[:-1] + [name])
+
+ def with_suffix(self, suffix):
+ """Return a new path with the file suffix changed (or added, if none)."""
+ # XXX if suffix is None, should the current suffix be removed?
+ drv, root, parts = self._flavour.parse_parts((suffix,))
+ if drv or root or len(parts) != 1:
+ raise ValueError("Invalid suffix %r" % (suffix))
+ suffix = parts[0]
+ if not suffix.startswith('.'):
+ raise ValueError("Invalid suffix %r" % (suffix))
+ name = self.name
+ if not name:
+ raise ValueError("%r has an empty name" % (self,))
+ old_suffix = self.suffix
+ if not old_suffix:
+ name = name + suffix
+ else:
+ name = name[:-len(old_suffix)] + suffix
+ return self._from_parsed_parts(self._drv, self._root,
+ self._parts[:-1] + [name])
+
+ def relative_to(self, *other):
+ """Return the relative path to another path identified by the passed
+ arguments. If the operation is not possible (because this is not
+ a subpath of the other path), raise ValueError.
+ """
+ # For the purpose of this method, drive and root are considered
+ # separate parts, i.e.:
+ # Path('c:/').relative_to('c:') gives Path('/')
+ # Path('c:/').relative_to('/') raise ValueError
+ if not other:
+ raise TypeError("need at least one argument")
+ parts = self._parts
+ drv = self._drv
+ root = self._root
+ if root:
+ abs_parts = [drv, root] + parts[1:]
+ else:
+ abs_parts = parts
+ to_drv, to_root, to_parts = self._parse_args(other)
+ if to_root:
+ to_abs_parts = [to_drv, to_root] + to_parts[1:]
+ else:
+ to_abs_parts = to_parts
+ n = len(to_abs_parts)
+ cf = self._flavour.casefold_parts
+ if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
+ formatted = self._format_parsed_parts(to_drv, to_root, to_parts)
+ raise ValueError("{!r} does not start with {!r}"
+ .format(str(self), str(formatted)))
+ return self._from_parsed_parts('', root if n == 1 else '',
+ abs_parts[n:])
+
+ @property
+ def parts(self):
+ """An object providing sequence-like access to the
+ components in the filesystem path."""
+ # We cache the tuple to avoid building a new one each time .parts
+ # is accessed. XXX is this necessary?
+ try:
+ return self._pparts
+ except AttributeError:
+ self._pparts = tuple(self._parts)
+ return self._pparts
+
+ def joinpath(self, *args):
+ """Combine this path with one or several arguments, and return a
+ new path representing either a subpath (if all arguments are relative
+ paths) or a totally different path (if one of the arguments is
+ anchored).
+ """
+ return self._make_child(args)
+
+ def __truediv__(self, key):
+ return self._make_child((key,))
+
+ def __rtruediv__(self, key):
+ return self._from_parts([key] + self._parts)
+
+ @property
+ def parent(self):
+ """The logical parent of the path."""
+ drv = self._drv
+ root = self._root
+ parts = self._parts
+ if len(parts) == 1 and (drv or root):
+ return self
+ return self._from_parsed_parts(drv, root, parts[:-1])
+
+ @property
+ def parents(self):
+ """A sequence of this path's logical parents."""
+ return _PathParents(self)
+
+ def is_absolute(self):
+ """True if the path is absolute (has both a root and, if applicable,
+ a drive)."""
+ if not self._root:
+ return False
+ return not self._flavour.has_drv or bool(self._drv)
+
+ def is_reserved(self):
+ """Return True if the path contains one of the special names reserved
+ by the system, if any."""
+ return self._flavour.is_reserved(self._parts)
+
+ def match(self, path_pattern):
+ """
+ Return True if this path matches the given pattern.
+ """
+ cf = self._flavour.casefold
+ path_pattern = cf(path_pattern)
+ drv, root, pat_parts = self._flavour.parse_parts((path_pattern,))
+ if not pat_parts:
+ raise ValueError("empty pattern")
+ if drv and drv != cf(self._drv):
+ return False
+ if root and root != cf(self._root):
+ return False
+ parts = self._cparts
+ if drv or root:
+ if len(pat_parts) != len(parts):
+ return False
+ pat_parts = pat_parts[1:]
+ elif len(pat_parts) > len(parts):
+ return False
+ for part, pat in zip(reversed(parts), reversed(pat_parts)):
+ if not fnmatch.fnmatchcase(part, pat):
+ return False
+ return True
+
+
+class PurePosixPath(PurePath):
+ _flavour = _posix_flavour
+ __slots__ = ()
+
+
+class PureWindowsPath(PurePath):
+ _flavour = _windows_flavour
+ __slots__ = ()
+
+
+# Filesystem-accessing classes
+
+
+class Path(PurePath):
+ __slots__ = (
+ '_accessor',
+ '_closed',
+ )
+
+ def __new__(cls, *args, **kwargs):
+ if cls is Path:
+ cls = WindowsPath if os.name == 'nt' else PosixPath
+ self = cls._from_parts(args, init=False)
+ if not self._flavour.is_supported:
+ raise NotImplementedError("cannot instantiate %r on your system"
+ % (cls.__name__,))
+ self._init()
+ return self
+
+ def _init(self,
+ # Private non-constructor arguments
+ template=None,
+ ):
+ self._closed = False
+ if template is not None:
+ self._accessor = template._accessor
+ else:
+ self._accessor = _normal_accessor
+
+ def _make_child_relpath(self, part):
+ # This is an optimization used for dir walking. `part` must be
+ # a single part relative to this path.
+ parts = self._parts + [part]
+ return self._from_parsed_parts(self._drv, self._root, parts)
+
+ def __enter__(self):
+ if self._closed:
+ self._raise_closed()
+ return self
+
+ def __exit__(self, t, v, tb):
+ self._closed = True
+
+ def _raise_closed(self):
+ raise ValueError("I/O operation on closed path")
+
+ def _opener(self, name, flags, mode=0o666):
+ # A stub for the opener argument to built-in open()
+ return self._accessor.open(self, flags, mode)
+
+ def _raw_open(self, flags, mode=0o777):
+ """
+ Open the file pointed by this path and return a file descriptor,
+ as os.open() does.
+ """
+ if self._closed:
+ self._raise_closed()
+ return self._accessor.open(self, flags, mode)
+
+ # Public API
+
+ @classmethod
+ def cwd(cls):
+ """Return a new path pointing to the current working directory
+ (as returned by os.getcwd()).
+ """
+ return cls(os.getcwd())
+
+ def iterdir(self):
+ """Iterate over the files in this directory. Does not yield any
+ result for the special paths '.' and '..'.
+ """
+ if self._closed:
+ self._raise_closed()
+ for name in self._accessor.listdir(self):
+ if name in {'.', '..'}:
+ # Yielding a path object for these makes little sense
+ continue
+ yield self._make_child_relpath(name)
+ if self._closed:
+ self._raise_closed()
+
+ def glob(self, pattern):
+ """Iterate over this subtree and yield all existing files (of any
+ kind, including directories) matching the given pattern.
+ """
+ pattern = self._flavour.casefold(pattern)
+ drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
+ if drv or root:
+ raise NotImplementedError("Non-relative patterns are unsupported")
+ selector = _make_selector(tuple(pattern_parts))
+ for p in selector.select_from(self):
+ yield p
+
+ def rglob(self, pattern):
+ """Recursively yield all existing files (of any kind, including
+ directories) matching the given pattern, anywhere in this subtree.
+ """
+ pattern = self._flavour.casefold(pattern)
+ drv, root, pattern_parts = self._flavour.parse_parts((pattern,))
+ if drv or root:
+ raise NotImplementedError("Non-relative patterns are unsupported")
+ selector = _make_selector(("**",) + tuple(pattern_parts))
+ for p in selector.select_from(self):
+ yield p
+
+ def absolute(self):
+ """Return an absolute version of this path. This function works
+ even if the path doesn't point to anything.
+
+ No normalization is done, i.e. all '.' and '..' will be kept along.
+ Use resolve() to get the canonical path to a file.
+ """
+ # XXX untested yet!
+ if self._closed:
+ self._raise_closed()
+ if self.is_absolute():
+ return self
+ # FIXME this must defer to the specific flavour (and, under Windows,
+ # use nt._getfullpathname())
+ obj = self._from_parts([os.getcwd()] + self._parts, init=False)
+ obj._init(template=self)
+ return obj
+
+ def resolve(self):
+ """
+ Make the path absolute, resolving all symlinks on the way and also
+ normalizing it (for example turning slashes into backslashes under
+ Windows).
+ """
+ if self._closed:
+ self._raise_closed()
+ s = self._flavour.resolve(self)
+ if s is None:
+ # No symlink resolution => for consistency, raise an error if
+ # the path doesn't exist or is forbidden
+ self.stat()
+ s = str(self.absolute())
+ # Now we have no symlinks in the path, it's safe to normalize it.
+ normed = self._flavour.pathmod.normpath(s)
+ obj = self._from_parts((normed,), init=False)
+ obj._init(template=self)
+ return obj
+
+ def stat(self):
+ """
+ Return the result of the stat() system call on this path, like
+ os.stat() does.
+ """
+ return self._accessor.stat(self)
+
+ def owner(self):
+ """
+ Return the login name of the file owner.
+ """
+ import pwd
+ return pwd.getpwuid(self.stat().st_uid).pw_name
+
+ def group(self):
+ """
+ Return the group name of the file gid.
+ """
+ import grp
+ return grp.getgrgid(self.stat().st_gid).gr_name
+
+ def open(self, mode='r', buffering=-1, encoding=None,
+ errors=None, newline=None):
+ """
+ Open the file pointed by this path and return a file object, as
+ the built-in open() function does.
+ """
+ if self._closed:
+ self._raise_closed()
+ return io.open(str(self), mode, buffering, encoding, errors, newline,
+ opener=self._opener)
+
+ def touch(self, mode=0o666, exist_ok=True):
+ """
+ Create this file with the given access mode, if it doesn't exist.
+ """
+ if self._closed:
+ self._raise_closed()
+ if exist_ok:
+ # First try to bump modification time
+ # Implementation note: GNU touch uses the UTIME_NOW option of
+ # the utimensat() / futimens() functions.
+ try:
+ self._accessor.utime(self, None)
+ except OSError:
+ # Avoid exception chaining
+ pass
+ else:
+ return
+ flags = os.O_CREAT | os.O_WRONLY
+ if not exist_ok:
+ flags |= os.O_EXCL
+ fd = self._raw_open(flags, mode)
+ os.close(fd)
+
+ def mkdir(self, mode=0o777, parents=False):
+ if self._closed:
+ self._raise_closed()
+ if not parents:
+ self._accessor.mkdir(self, mode)
+ else:
+ try:
+ self._accessor.mkdir(self, mode)
+ except OSError as e:
+ if e.errno != ENOENT:
+ raise
+ self.parent.mkdir(parents=True)
+ self._accessor.mkdir(self, mode)
+
+ def chmod(self, mode):
+ """
+ Change the permissions of the path, like os.chmod().
+ """
+ if self._closed:
+ self._raise_closed()
+ self._accessor.chmod(self, mode)
+
+ def lchmod(self, mode):
+ """
+ Like chmod(), except if the path points to a symlink, the symlink's
+ permissions are changed, rather than its target's.
+ """
+ if self._closed:
+ self._raise_closed()
+ self._accessor.lchmod(self, mode)
+
+ def unlink(self):
+ """
+ Remove this file or link.
+ If the path is a directory, use rmdir() instead.
+ """
+ if self._closed:
+ self._raise_closed()
+ self._accessor.unlink(self)
+
+ def rmdir(self):
+ """
+ Remove this directory. The directory must be empty.
+ """
+ if self._closed:
+ self._raise_closed()
+ self._accessor.rmdir(self)
+
+ def lstat(self):
+ """
+ Like stat(), except if the path points to a symlink, the symlink's
+ status information is returned, rather than its target's.
+ """
+ if self._closed:
+ self._raise_closed()
+ return self._accessor.lstat(self)
+
+ def rename(self, target):
+ """
+ Rename this path to the given path.
+ """
+ if self._closed:
+ self._raise_closed()
+ self._accessor.rename(self, target)
+
+ def replace(self, target):
+ """
+ Rename this path to the given path, clobbering the existing
+ destination if it exists.
+ """
+ if self._closed:
+ self._raise_closed()
+ self._accessor.replace(self, target)
+
+ def symlink_to(self, target, target_is_directory=False):
+ """
+ Make this path a symlink pointing to the given path.
+ Note the order of arguments (self, target) is the reverse of os.symlink's.
+ """
+ if self._closed:
+ self._raise_closed()
+ self._accessor.symlink(target, self, target_is_directory)
+
+ # Convenience functions for querying the stat results
+
+ def exists(self):
+ """
+ Whether this path exists.
+ """
+ try:
+ self.stat()
+ except OSError as e:
+ if e.errno != ENOENT:
+ raise
+ return False
+ return True
+
+ def is_dir(self):
+ """
+ Whether this path is a directory.
+ """
+ try:
+ return S_ISDIR(self.stat().st_mode)
+ except OSError as e:
+ if e.errno != ENOENT:
+ raise
+ # Path doesn't exist or is a broken symlink
+ # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
+ return False
+
+ def is_file(self):
+ """
+ Whether this path is a regular file (also True for symlinks pointing
+ to regular files).
+ """
+ try:
+ return S_ISREG(self.stat().st_mode)
+ except OSError as e:
+ if e.errno != ENOENT:
+ raise
+ # Path doesn't exist or is a broken symlink
+ # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
+ return False
+
+ def is_symlink(self):
+ """
+ Whether this path is a symbolic link.
+ """
+ try:
+ return S_ISLNK(self.lstat().st_mode)
+ except OSError as e:
+ if e.errno != ENOENT:
+ raise
+ # Path doesn't exist
+ return False
+
+ def is_block_device(self):
+ """
+ Whether this path is a block device.
+ """
+ try:
+ return S_ISBLK(self.stat().st_mode)
+ except OSError as e:
+ if e.errno != ENOENT:
+ raise
+ # Path doesn't exist or is a broken symlink
+ # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
+ return False
+
+ def is_char_device(self):
+ """
+ Whether this path is a character device.
+ """
+ try:
+ return S_ISCHR(self.stat().st_mode)
+ except OSError as e:
+ if e.errno != ENOENT:
+ raise
+ # Path doesn't exist or is a broken symlink
+ # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
+ return False
+
+ def is_fifo(self):
+ """
+ Whether this path is a FIFO.
+ """
+ try:
+ return S_ISFIFO(self.stat().st_mode)
+ except OSError as e:
+ if e.errno != ENOENT:
+ raise
+ # Path doesn't exist or is a broken symlink
+ # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
+ return False
+
+ def is_socket(self):
+ """
+ Whether this path is a socket.
+ """
+ try:
+ return S_ISSOCK(self.stat().st_mode)
+ except OSError as e:
+ if e.errno != ENOENT:
+ raise
+ # Path doesn't exist or is a broken symlink
+ # (see https://bitbucket.org/pitrou/pathlib/issue/12/)
+ return False
+
+
+class PosixPath(Path, PurePosixPath):
+ __slots__ = ()
+
+class WindowsPath(Path, PureWindowsPath):
+ __slots__ = ()
diff --git a/Lib/pdb.py b/Lib/pdb.py
index 80cba9d..dd7ceb8 100755
--- a/Lib/pdb.py
+++ b/Lib/pdb.py
@@ -92,21 +92,14 @@ def find_function(funcname, filename):
cre = re.compile(r'def\s+%s\s*[(]' % re.escape(funcname))
try:
fp = open(filename)
- except IOError:
+ except OSError:
return None
# consumer of this info expects the first line to be 1
- lineno = 1
- answer = None
- while True:
- line = fp.readline()
- if line == '':
- break
- if cre.match(line):
- answer = funcname, filename, lineno
- break
- lineno += 1
- fp.close()
- return answer
+ with fp:
+ for lineno, line in enumerate(fp, start=1):
+ if cre.match(line):
+ return funcname, filename, lineno
+ return None
def getsourcelines(obj):
lines, lineno = inspect.findsource(obj)
@@ -170,12 +163,12 @@ class Pdb(bdb.Bdb, cmd.Cmd):
try:
with open(os.path.join(envHome, ".pdbrc")) as rcFile:
self.rcLines.extend(rcFile)
- except IOError:
+ except OSError:
pass
try:
with open(".pdbrc") as rcFile:
self.rcLines.extend(rcFile)
- except IOError:
+ except OSError:
pass
self.commands = {} # associates a command list to breakpoint numbers
@@ -304,8 +297,16 @@ class Pdb(bdb.Bdb, cmd.Cmd):
return
exc_type, exc_value, exc_traceback = exc_info
frame.f_locals['__exception__'] = exc_type, exc_value
- self.message(traceback.format_exception_only(exc_type,
- exc_value)[-1].strip())
+
+ # An 'Internal StopIteration' exception is an exception debug event
+ # issued by the interpreter when handling a subgenerator run with
+ # 'yield from' or a generator controled by a for loop. No exception has
+ # actually occured in this case. The debugger uses this debug event to
+ # stop when the debuggee is returning from such generators.
+ prefix = 'Internal ' if (not exc_traceback
+ and exc_type is StopIteration) else ''
+ self.message('%s%s' % (prefix,
+ traceback.format_exception_only(exc_type, exc_value)[-1].strip()))
self.interaction(frame, exc_traceback)
# General interaction function
@@ -1163,15 +1164,13 @@ class Pdb(bdb.Bdb, cmd.Cmd):
return _rstr('** raised %s **' % err)
def do_p(self, arg):
- """p(rint) expression
+ """p expression
Print the value of the expression.
"""
try:
self.message(repr(self._getval(arg)))
except:
pass
- # make "print" an alias of "p" since print isn't a Python statement anymore
- do_print = do_p
def do_pp(self, arg):
"""pp expression
@@ -1245,7 +1244,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
breaklist = self.get_file_breaks(filename)
try:
lines, lineno = getsourcelines(self.curframe)
- except IOError as err:
+ except OSError as err:
self.error(err)
return
self._print_lines(lines, lineno, breaklist, self.curframe)
@@ -1261,7 +1260,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
return
try:
lines, lineno = getsourcelines(obj)
- except (IOError, TypeError) as err:
+ except (OSError, TypeError) as err:
self.error(err)
return
self._print_lines(lines, lineno)
@@ -1392,7 +1391,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
placed in the .pdbrc file):
# Print instance variables (usage "pi classInst")
- alias pi for k in %1.__dict__.keys(): print "%1.",k,"=",%1.__dict__[k]
+ alias pi for k in %1.__dict__.keys(): print("%1.",k,"=",%1.__dict__[k])
# Print instance variables in self
alias ps pi self
"""
@@ -1550,7 +1549,7 @@ if __doc__ is not None:
'help', 'where', 'down', 'up', 'break', 'tbreak', 'clear', 'disable',
'enable', 'ignore', 'condition', 'commands', 'step', 'next', 'until',
'jump', 'return', 'retval', 'run', 'continue', 'list', 'longlist',
- 'args', 'print', 'pp', 'whatis', 'source', 'display', 'undisplay',
+ 'args', 'p', 'pp', 'whatis', 'source', 'display', 'undisplay',
'interact', 'alias', 'unalias', 'debug', 'quit',
]
diff --git a/Lib/pickle.py b/Lib/pickle.py
index 386ffba..3fc2596 100644
--- a/Lib/pickle.py
+++ b/Lib/pickle.py
@@ -23,12 +23,13 @@ Misc variables:
"""
-from types import FunctionType, BuiltinFunctionType
+from types import FunctionType
from copyreg import dispatch_table
from copyreg import _extension_registry, _inverted_registry, _extension_cache
-import marshal
+from itertools import islice
import sys
-import struct
+from sys import maxsize
+from struct import pack, unpack
import re
import io
import codecs
@@ -41,28 +42,24 @@ __all__ = ["PickleError", "PicklingError", "UnpicklingError", "Pickler",
bytes_types = (bytes, bytearray)
# These are purely informational; no code uses these.
-format_version = "3.0" # File format version we write
+format_version = "4.0" # File format version we write
compatible_formats = ["1.0", # Original protocol 0
"1.1", # Protocol 0 with INST added
"1.2", # Original protocol 1
"1.3", # Protocol 1 with BINFLOAT added
"2.0", # Protocol 2
"3.0", # Protocol 3
+ "4.0", # Protocol 4
] # Old format versions we can read
# This is the highest protocol number we know how to read.
-HIGHEST_PROTOCOL = 3
+HIGHEST_PROTOCOL = 4
# The protocol we write by default. May be less than HIGHEST_PROTOCOL.
# We intentionally write a protocol that Python 2.x cannot read;
# there are too many issues with that.
DEFAULT_PROTOCOL = 3
-# Why use struct.pack() for pickling but marshal.loads() for
-# unpickling? struct.pack() is 40% faster than marshal.dumps(), but
-# marshal.loads() is twice as fast as struct.unpack()!
-mloads = marshal.loads
-
class PickleError(Exception):
"""A common base class for the other pickling exceptions."""
pass
@@ -168,7 +165,181 @@ _tuplesize2code = [EMPTY_TUPLE, TUPLE1, TUPLE2, TUPLE3]
BINBYTES = b'B' # push bytes; counted binary string argument
SHORT_BINBYTES = b'C' # " " ; " " " " < 256 bytes
-__all__.extend([x for x in dir() if re.match("[A-Z][A-Z0-9_]+$",x)])
+# Protocol 4
+SHORT_BINUNICODE = b'\x8c' # push short string; UTF-8 length < 256 bytes
+BINUNICODE8 = b'\x8d' # push very long string
+BINBYTES8 = b'\x8e' # push very long bytes string
+EMPTY_SET = b'\x8f' # push empty set on the stack
+ADDITEMS = b'\x90' # modify set by adding topmost stack items
+FROZENSET = b'\x91' # build frozenset from topmost stack items
+NEWOBJ_EX = b'\x92' # like NEWOBJ but work with keyword only arguments
+STACK_GLOBAL = b'\x93' # same as GLOBAL but using names on the stacks
+MEMOIZE = b'\x94' # store top of the stack in memo
+FRAME = b'\x95' # indicate the beginning of a new frame
+
+__all__.extend([x for x in dir() if re.match("[A-Z][A-Z0-9_]+$", x)])
+
+
+class _Framer:
+
+ _FRAME_SIZE_TARGET = 64 * 1024
+
+ def __init__(self, file_write):
+ self.file_write = file_write
+ self.current_frame = None
+
+ def start_framing(self):
+ self.current_frame = io.BytesIO()
+
+ def end_framing(self):
+ if self.current_frame and self.current_frame.tell() > 0:
+ self.commit_frame(force=True)
+ self.current_frame = None
+
+ def commit_frame(self, force=False):
+ if self.current_frame:
+ f = self.current_frame
+ if f.tell() >= self._FRAME_SIZE_TARGET or force:
+ with f.getbuffer() as data:
+ n = len(data)
+ write = self.file_write
+ write(FRAME)
+ write(pack("<Q", n))
+ write(data)
+ f.seek(0)
+ f.truncate()
+
+ def write(self, data):
+ if self.current_frame:
+ return self.current_frame.write(data)
+ else:
+ return self.file_write(data)
+
+
+class _Unframer:
+
+ def __init__(self, file_read, file_readline, file_tell=None):
+ self.file_read = file_read
+ self.file_readline = file_readline
+ self.current_frame = None
+
+ def read(self, n):
+ if self.current_frame:
+ data = self.current_frame.read(n)
+ if not data and n != 0:
+ self.current_frame = None
+ return self.file_read(n)
+ if len(data) < n:
+ raise UnpicklingError(
+ "pickle exhausted before end of frame")
+ return data
+ else:
+ return self.file_read(n)
+
+ def readline(self):
+ if self.current_frame:
+ data = self.current_frame.readline()
+ if not data:
+ self.current_frame = None
+ return self.file_readline()
+ if data[-1] != b'\n':
+ raise UnpicklingError(
+ "pickle exhausted before end of frame")
+ return data
+ else:
+ return self.file_readline()
+
+ def load_frame(self, frame_size):
+ if self.current_frame and self.current_frame.read() != b'':
+ raise UnpicklingError(
+ "beginning of a new frame before end of current frame")
+ self.current_frame = io.BytesIO(self.file_read(frame_size))
+
+
+# Tools used for pickling.
+
+def _getattribute(obj, name, allow_qualname=False):
+ dotted_path = name.split(".")
+ if not allow_qualname and len(dotted_path) > 1:
+ raise AttributeError("Can't get qualified attribute {!r} on {!r}; " +
+ "use protocols >= 4 to enable support"
+ .format(name, obj))
+ for subpath in dotted_path:
+ if subpath == '<locals>':
+ raise AttributeError("Can't get local attribute {!r} on {!r}"
+ .format(name, obj))
+ try:
+ obj = getattr(obj, subpath)
+ except AttributeError:
+ raise AttributeError("Can't get attribute {!r} on {!r}"
+ .format(name, obj))
+ return obj
+
+def whichmodule(obj, name, allow_qualname=False):
+ """Find the module an object belong to."""
+ module_name = getattr(obj, '__module__', None)
+ if module_name is not None:
+ return module_name
+ for module_name, module in sys.modules.items():
+ if module_name == '__main__' or module is None:
+ continue
+ try:
+ if _getattribute(module, name, allow_qualname) is obj:
+ return module_name
+ except AttributeError:
+ pass
+ return '__main__'
+
+def encode_long(x):
+ r"""Encode a long to a two's complement little-endian binary string.
+ Note that 0 is a special case, returning an empty string, to save a
+ byte in the LONG1 pickling context.
+
+ >>> encode_long(0)
+ b''
+ >>> encode_long(255)
+ b'\xff\x00'
+ >>> encode_long(32767)
+ b'\xff\x7f'
+ >>> encode_long(-256)
+ b'\x00\xff'
+ >>> encode_long(-32768)
+ b'\x00\x80'
+ >>> encode_long(-128)
+ b'\x80'
+ >>> encode_long(127)
+ b'\x7f'
+ >>>
+ """
+ if x == 0:
+ return b''
+ nbytes = (x.bit_length() >> 3) + 1
+ result = x.to_bytes(nbytes, byteorder='little', signed=True)
+ if x < 0 and nbytes > 1:
+ if result[-1] == 0xff and (result[-2] & 0x80) != 0:
+ result = result[:-1]
+ return result
+
+def decode_long(data):
+ r"""Decode a long from a two's complement little-endian binary string.
+
+ >>> decode_long(b'')
+ 0
+ >>> decode_long(b"\xff\x00")
+ 255
+ >>> decode_long(b"\xff\x7f")
+ 32767
+ >>> decode_long(b"\x00\xff")
+ -256
+ >>> decode_long(b"\x00\x80")
+ -32768
+ >>> decode_long(b"\x80")
+ -128
+ >>> decode_long(b"\x7f")
+ 127
+ """
+ return int.from_bytes(data, byteorder='little', signed=True)
+
# Pickling machinery
@@ -177,24 +348,25 @@ class _Pickler:
def __init__(self, file, protocol=None, *, fix_imports=True):
"""This takes a binary file for writing a pickle data stream.
- The optional protocol argument tells the pickler to use the
- given protocol; supported protocols are 0, 1, 2, 3. The default
- protocol is 3; a backward-incompatible protocol designed for
- Python 3.0.
+ The optional *protocol* argument tells the pickler to use the
+ given protocol; supported protocols are 0, 1, 2, 3 and 4. The
+ default protocol is 3; a backward-incompatible protocol designed
+ for Python 3.
Specifying a negative protocol version selects the highest
protocol version supported. The higher the protocol used, the
more recent the version of Python needed to read the pickle
produced.
- The file argument must have a write() method that accepts a single
- bytes argument. It can thus be a file object opened for binary
- writing, a io.BytesIO instance, or any other custom object that
- meets this interface.
+ The *file* argument must have a write() method that accepts a
+ single bytes argument. It can thus be a file object opened for
+ binary writing, a io.BytesIO instance, or any other custom
+ object that meets this interface.
- If fix_imports is True and protocol is less than 3, pickle will try to
- map the new Python 3.x names to the old module names used in Python
- 2.x, so that the pickle data stream is readable with Python 2.x.
+ If *fix_imports* is True and *protocol* is less than 3, pickle
+ will try to map the new Python 3 names to the old module names
+ used in Python 2, so that the pickle data stream is readable
+ with Python 2.
"""
if protocol is None:
protocol = DEFAULT_PROTOCOL
@@ -203,9 +375,11 @@ class _Pickler:
elif not 0 <= protocol <= HIGHEST_PROTOCOL:
raise ValueError("pickle protocol must be <= %d" % HIGHEST_PROTOCOL)
try:
- self.write = file.write
+ self._file_write = file.write
except AttributeError:
raise TypeError("file must have a 'write' attribute")
+ self.framer = _Framer(self._file_write)
+ self.write = self.framer.write
self.memo = {}
self.proto = int(protocol)
self.bin = protocol >= 1
@@ -216,10 +390,9 @@ class _Pickler:
"""Clears the pickler's "memo".
The memo is the data structure that remembers which objects the
- pickler has already seen, so that shared or recursive objects are
- pickled by reference and not by value. This method is useful when
- re-using picklers.
-
+ pickler has already seen, so that shared or recursive objects
+ are pickled by reference and not by value. This method is
+ useful when re-using picklers.
"""
self.memo.clear()
@@ -227,13 +400,16 @@ class _Pickler:
"""Write a pickled representation of obj to the open file."""
# Check whether Pickler was initialized correctly. This is
# only needed to mimic the behavior of _pickle.Pickler.dump().
- if not hasattr(self, "write"):
+ if not hasattr(self, "_file_write"):
raise PicklingError("Pickler.__init__() was not called by "
"%s.__init__()" % (self.__class__.__name__,))
if self.proto >= 2:
- self.write(PROTO + bytes([self.proto]))
+ self.write(PROTO + pack("<B", self.proto))
+ if self.proto >= 4:
+ self.framer.start_framing()
self.save(obj)
self.write(STOP)
+ self.framer.end_framing()
def memoize(self, obj):
"""Store an object in the memo."""
@@ -253,31 +429,35 @@ class _Pickler:
if self.fast:
return
assert id(obj) not in self.memo
- memo_len = len(self.memo)
- self.write(self.put(memo_len))
- self.memo[id(obj)] = memo_len, obj
+ idx = len(self.memo)
+ self.write(self.put(idx))
+ self.memo[id(obj)] = idx, obj
# Return a PUT (BINPUT, LONG_BINPUT) opcode string, with argument i.
- def put(self, i, pack=struct.pack):
- if self.bin:
- if i < 256:
- return BINPUT + bytes([i])
+ def put(self, idx):
+ if self.proto >= 4:
+ return MEMOIZE
+ elif self.bin:
+ if idx < 256:
+ return BINPUT + pack("<B", idx)
else:
- return LONG_BINPUT + pack("<I", i)
-
- return PUT + repr(i).encode("ascii") + b'\n'
+ return LONG_BINPUT + pack("<I", idx)
+ else:
+ return PUT + repr(idx).encode("ascii") + b'\n'
# Return a GET (BINGET, LONG_BINGET) opcode string, with argument i.
- def get(self, i, pack=struct.pack):
+ def get(self, i):
if self.bin:
if i < 256:
- return BINGET + bytes([i])
+ return BINGET + pack("<B", i)
else:
return LONG_BINGET + pack("<I", i)
return GET + repr(i).encode("ascii") + b'\n'
def save(self, obj, save_persistent_id=True):
+ self.framer.commit_frame()
+
# Check for persistent id (defined by a subclass)
pid = self.persistent_id(obj)
if pid is not None and save_persistent_id:
@@ -286,20 +466,20 @@ class _Pickler:
# Check the memo
x = self.memo.get(id(obj))
- if x:
+ if x is not None:
self.write(self.get(x[0]))
return
# Check the type dispatch table
t = type(obj)
f = self.dispatch.get(t)
- if f:
+ if f is not None:
f(self, obj) # Call unbound method with explicit self
return
# Check private dispatch table if any, or else copyreg.dispatch_table
reduce = getattr(self, 'dispatch_table', dispatch_table).get(t)
- if reduce:
+ if reduce is not None:
rv = reduce(obj)
else:
# Check for a class with a custom metaclass; treat as regular class
@@ -313,11 +493,11 @@ class _Pickler:
# Check for a __reduce_ex__ method, fall back to __reduce__
reduce = getattr(obj, "__reduce_ex__", None)
- if reduce:
+ if reduce is not None:
rv = reduce(self.proto)
else:
reduce = getattr(obj, "__reduce__", None)
- if reduce:
+ if reduce is not None:
rv = reduce()
else:
raise PicklingError("Can't pickle %r object: %r" %
@@ -353,24 +533,33 @@ class _Pickler:
else:
self.write(PERSID + str(pid).encode("ascii") + b'\n')
- def save_reduce(self, func, args, state=None,
- listitems=None, dictitems=None, obj=None):
+ def save_reduce(self, func, args, state=None, listitems=None,
+ dictitems=None, obj=None):
# This API is called by some subclasses
- # Assert that args is a tuple
if not isinstance(args, tuple):
- raise PicklingError("args from save_reduce() should be a tuple")
-
- # Assert that func is callable
+ raise PicklingError("args from save_reduce() must be a tuple")
if not callable(func):
- raise PicklingError("func from save_reduce() should be callable")
+ raise PicklingError("func from save_reduce() must be callable")
save = self.save
write = self.write
- # Protocol 2 special case: if func's name is __newobj__, use NEWOBJ
- if self.proto >= 2 and getattr(func, "__name__", "") == "__newobj__":
- # A __reduce__ implementation can direct protocol 2 to
+ func_name = getattr(func, "__name__", "")
+ if self.proto >= 4 and func_name == "__newobj_ex__":
+ cls, args, kwargs = args
+ if not hasattr(cls, "__new__"):
+ raise PicklingError("args[0] from {} args has no __new__"
+ .format(func_name))
+ if obj is not None and cls is not obj.__class__:
+ raise PicklingError("args[0] from {} args has the wrong class"
+ .format(func_name))
+ save(cls)
+ save(args)
+ save(kwargs)
+ write(NEWOBJ_EX)
+ elif self.proto >= 2 and func_name == "__newobj__":
+ # A __reduce__ implementation can direct protocol 2 or newer to
# use the more efficient NEWOBJ opcode, while still
# allowing protocol 0 and 1 to work normally. For this to
# work, the function returned by __reduce__ should be
@@ -413,7 +602,13 @@ class _Pickler:
write(REDUCE)
if obj is not None:
- self.memoize(obj)
+ # If the object is already in the memo, this means it is
+ # recursive. In this case, throw away everything we put on the
+ # stack, and fetch the object back from the memo.
+ if id(obj) in self.memo:
+ write(POP + self.get(self.memo[id(obj)][0]))
+ else:
+ self.memoize(obj)
# More new special cases (that work with older protocols as
# well): when __reduce__ returns a tuple with 4 or 5 items,
@@ -438,22 +633,14 @@ class _Pickler:
self.write(NONE)
dispatch[type(None)] = save_none
- def save_ellipsis(self, obj):
- self.save_global(Ellipsis, 'Ellipsis')
- dispatch[type(Ellipsis)] = save_ellipsis
-
- def save_notimplemented(self, obj):
- self.save_global(NotImplemented, 'NotImplemented')
- dispatch[type(NotImplemented)] = save_notimplemented
-
def save_bool(self, obj):
if self.proto >= 2:
- self.write(obj and NEWTRUE or NEWFALSE)
+ self.write(NEWTRUE if obj else NEWFALSE)
else:
- self.write(obj and TRUE or FALSE)
+ self.write(TRUE if obj else FALSE)
dispatch[bool] = save_bool
- def save_long(self, obj, pack=struct.pack):
+ def save_long(self, obj):
if self.bin:
# If the int is small enough to fit in a signed 4-byte 2's-comp
# format, we can store it more efficiently than the general
@@ -461,93 +648,95 @@ class _Pickler:
# First one- and two-byte unsigned ints:
if obj >= 0:
if obj <= 0xff:
- self.write(BININT1 + bytes([obj]))
+ self.write(BININT1 + pack("<B", obj))
return
if obj <= 0xffff:
- self.write(BININT2 + bytes([obj&0xff, obj>>8]))
+ self.write(BININT2 + pack("<H", obj))
return
# Next check for 4-byte signed ints:
- high_bits = obj >> 31 # note that Python shift sign-extends
- if high_bits == 0 or high_bits == -1:
- # All high bits are copies of bit 2**31, so the value
- # fits in a 4-byte signed int.
+ if -0x80000000 <= obj <= 0x7fffffff:
self.write(BININT + pack("<i", obj))
return
if self.proto >= 2:
encoded = encode_long(obj)
n = len(encoded)
if n < 256:
- self.write(LONG1 + bytes([n]) + encoded)
+ self.write(LONG1 + pack("<B", n) + encoded)
else:
self.write(LONG4 + pack("<i", n) + encoded)
return
self.write(LONG + repr(obj).encode("ascii") + b'L\n')
dispatch[int] = save_long
- def save_float(self, obj, pack=struct.pack):
+ def save_float(self, obj):
if self.bin:
self.write(BINFLOAT + pack('>d', obj))
else:
self.write(FLOAT + repr(obj).encode("ascii") + b'\n')
dispatch[float] = save_float
- def save_bytes(self, obj, pack=struct.pack):
+ def save_bytes(self, obj):
if self.proto < 3:
- if len(obj) == 0:
+ if not obj: # bytes object is empty
self.save_reduce(bytes, (), obj=obj)
else:
self.save_reduce(codecs.encode,
(str(obj, 'latin1'), 'latin1'), obj=obj)
return
n = len(obj)
- if n < 256:
- self.write(SHORT_BINBYTES + bytes([n]) + bytes(obj))
+ if n <= 0xff:
+ self.write(SHORT_BINBYTES + pack("<B", n) + obj)
+ elif n > 0xffffffff and self.proto >= 4:
+ self.write(BINBYTES8 + pack("<Q", n) + obj)
else:
- self.write(BINBYTES + pack("<I", n) + bytes(obj))
+ self.write(BINBYTES + pack("<I", n) + obj)
self.memoize(obj)
dispatch[bytes] = save_bytes
- def save_str(self, obj, pack=struct.pack):
+ def save_str(self, obj):
if self.bin:
encoded = obj.encode('utf-8', 'surrogatepass')
n = len(encoded)
- self.write(BINUNICODE + pack("<I", n) + encoded)
+ if n <= 0xff and self.proto >= 4:
+ self.write(SHORT_BINUNICODE + pack("<B", n) + encoded)
+ elif n > 0xffffffff and self.proto >= 4:
+ self.write(BINUNICODE8 + pack("<Q", n) + encoded)
+ else:
+ self.write(BINUNICODE + pack("<I", n) + encoded)
else:
obj = obj.replace("\\", "\\u005c")
obj = obj.replace("\n", "\\u000a")
- self.write(UNICODE + bytes(obj.encode('raw-unicode-escape')) +
+ self.write(UNICODE + obj.encode('raw-unicode-escape') +
b'\n')
self.memoize(obj)
dispatch[str] = save_str
def save_tuple(self, obj):
- write = self.write
- proto = self.proto
-
- n = len(obj)
- if n == 0:
- if proto:
- write(EMPTY_TUPLE)
+ if not obj: # tuple is empty
+ if self.bin:
+ self.write(EMPTY_TUPLE)
else:
- write(MARK + TUPLE)
+ self.write(MARK + TUPLE)
return
+ n = len(obj)
save = self.save
memo = self.memo
- if n <= 3 and proto >= 2:
+ if n <= 3 and self.proto >= 2:
for element in obj:
save(element)
# Subtle. Same as in the big comment below.
if id(obj) in memo:
get = self.get(memo[id(obj)][0])
- write(POP * n + get)
+ self.write(POP * n + get)
else:
- write(_tuplesize2code[n])
+ self.write(_tuplesize2code[n])
self.memoize(obj)
return
# proto 0 or proto 1 and tuple isn't empty, or proto > 1 and tuple
# has more than 3 elements.
+ write = self.write
write(MARK)
for element in obj:
save(element)
@@ -561,25 +750,23 @@ class _Pickler:
# could have been done in the "for element" loop instead, but
# recursive tuples are a rare thing.
get = self.get(memo[id(obj)][0])
- if proto:
+ if self.bin:
write(POP_MARK + get)
else: # proto 0 -- POP_MARK not available
write(POP * (n+1) + get)
return
# No recursion.
- self.write(TUPLE)
+ write(TUPLE)
self.memoize(obj)
dispatch[tuple] = save_tuple
def save_list(self, obj):
- write = self.write
-
if self.bin:
- write(EMPTY_LIST)
+ self.write(EMPTY_LIST)
else: # proto 0 -- can't use EMPTY_LIST
- write(MARK + LIST)
+ self.write(MARK + LIST)
self.memoize(obj)
self._batch_appends(obj)
@@ -599,17 +786,9 @@ class _Pickler:
write(APPEND)
return
- items = iter(items)
- r = range(self._BATCHSIZE)
- while items is not None:
- tmp = []
- for i in r:
- try:
- x = next(items)
- tmp.append(x)
- except StopIteration:
- items = None
- break
+ it = iter(items)
+ while True:
+ tmp = list(islice(it, self._BATCHSIZE))
n = len(tmp)
if n > 1:
write(MARK)
@@ -620,14 +799,14 @@ class _Pickler:
save(tmp[0])
write(APPEND)
# else tmp is empty, and we're done
+ if n < self._BATCHSIZE:
+ return
def save_dict(self, obj):
- write = self.write
-
if self.bin:
- write(EMPTY_DICT)
+ self.write(EMPTY_DICT)
else: # proto 0 -- can't use EMPTY_DICT
- write(MARK + DICT)
+ self.write(MARK + DICT)
self.memoize(obj)
self._batch_setitems(obj.items())
@@ -648,16 +827,9 @@ class _Pickler:
write(SETITEM)
return
- items = iter(items)
- r = range(self._BATCHSIZE)
- while items is not None:
- tmp = []
- for i in r:
- try:
- tmp.append(next(items))
- except StopIteration:
- items = None
- break
+ it = iter(items)
+ while True:
+ tmp = list(islice(it, self._BATCHSIZE))
n = len(tmp)
if n > 1:
write(MARK)
@@ -671,55 +843,109 @@ class _Pickler:
save(v)
write(SETITEM)
# else tmp is empty, and we're done
+ if n < self._BATCHSIZE:
+ return
+
+ def save_set(self, obj):
+ save = self.save
+ write = self.write
+
+ if self.proto < 4:
+ self.save_reduce(set, (list(obj),), obj=obj)
+ return
+
+ write(EMPTY_SET)
+ self.memoize(obj)
+
+ it = iter(obj)
+ while True:
+ batch = list(islice(it, self._BATCHSIZE))
+ n = len(batch)
+ if n > 0:
+ write(MARK)
+ for item in batch:
+ save(item)
+ write(ADDITEMS)
+ if n < self._BATCHSIZE:
+ return
+ dispatch[set] = save_set
+
+ def save_frozenset(self, obj):
+ save = self.save
+ write = self.write
+
+ if self.proto < 4:
+ self.save_reduce(frozenset, (list(obj),), obj=obj)
+ return
+
+ write(MARK)
+ for item in obj:
+ save(item)
+
+ if id(obj) in self.memo:
+ # If the object is already in the memo, this means it is
+ # recursive. In this case, throw away everything we put on the
+ # stack, and fetch the object back from the memo.
+ write(POP_MARK + self.get(self.memo[id(obj)][0]))
+ return
- def save_global(self, obj, name=None, pack=struct.pack):
+ write(FROZENSET)
+ self.memoize(obj)
+ dispatch[frozenset] = save_frozenset
+
+ def save_global(self, obj, name=None):
write = self.write
memo = self.memo
+ if name is None and self.proto >= 4:
+ name = getattr(obj, '__qualname__', None)
if name is None:
name = obj.__name__
- module = getattr(obj, "__module__", None)
- if module is None:
- module = whichmodule(obj, name)
-
+ module_name = whichmodule(obj, name, allow_qualname=self.proto >= 4)
try:
- __import__(module, level=0)
- mod = sys.modules[module]
- klass = getattr(mod, name)
+ __import__(module_name, level=0)
+ module = sys.modules[module_name]
+ obj2 = _getattribute(module, name, allow_qualname=self.proto >= 4)
except (ImportError, KeyError, AttributeError):
raise PicklingError(
"Can't pickle %r: it's not found as %s.%s" %
- (obj, module, name))
+ (obj, module_name, name))
else:
- if klass is not obj:
+ if obj2 is not obj:
raise PicklingError(
"Can't pickle %r: it's not the same object as %s.%s" %
- (obj, module, name))
+ (obj, module_name, name))
if self.proto >= 2:
- code = _extension_registry.get((module, name))
+ code = _extension_registry.get((module_name, name))
if code:
assert code > 0
if code <= 0xff:
- write(EXT1 + bytes([code]))
+ write(EXT1 + pack("<B", code))
elif code <= 0xffff:
- write(EXT2 + bytes([code&0xff, code>>8]))
+ write(EXT2 + pack("<H", code))
else:
write(EXT4 + pack("<i", code))
return
# Non-ASCII identifiers are supported only with protocols >= 3.
- if self.proto >= 3:
- write(GLOBAL + bytes(module, "utf-8") + b'\n' +
+ if self.proto >= 4:
+ self.save(module_name)
+ self.save(name)
+ write(STACK_GLOBAL)
+ elif self.proto >= 3:
+ write(GLOBAL + bytes(module_name, "utf-8") + b'\n' +
bytes(name, "utf-8") + b'\n')
else:
if self.fix_imports:
- if (module, name) in _compat_pickle.REVERSE_NAME_MAPPING:
- module, name = _compat_pickle.REVERSE_NAME_MAPPING[(module, name)]
- if module in _compat_pickle.REVERSE_IMPORT_MAPPING:
- module = _compat_pickle.REVERSE_IMPORT_MAPPING[module]
+ r_name_mapping = _compat_pickle.REVERSE_NAME_MAPPING
+ r_import_mapping = _compat_pickle.REVERSE_IMPORT_MAPPING
+ if (module_name, name) in r_name_mapping:
+ module_name, name = r_name_mapping[(module_name, name)]
+ if module_name in r_import_mapping:
+ module_name = r_import_mapping[module_name]
try:
- write(GLOBAL + bytes(module, "ascii") + b'\n' +
+ write(GLOBAL + bytes(module_name, "ascii") + b'\n' +
bytes(name, "ascii") + b'\n')
except UnicodeEncodeError:
raise PicklingError(
@@ -738,58 +964,8 @@ class _Pickler:
return self.save_global(obj)
dispatch[FunctionType] = save_global
- dispatch[BuiltinFunctionType] = save_global
dispatch[type] = save_type
-# Pickling helpers
-
-def _keep_alive(x, memo):
- """Keeps a reference to the object x in the memo.
-
- Because we remember objects by their id, we have
- to assure that possibly temporary objects are kept
- alive by referencing them.
- We store a reference at the id of the memo, which should
- normally not be used unless someone tries to deepcopy
- the memo itself...
- """
- try:
- memo[id(memo)].append(x)
- except KeyError:
- # aha, this is the first one :-)
- memo[id(memo)]=[x]
-
-
-# A cache for whichmodule(), mapping a function object to the name of
-# the module in which the function was found.
-
-classmap = {} # called classmap for backwards compatibility
-
-def whichmodule(func, funcname):
- """Figure out the module in which a function occurs.
-
- Search sys.modules for the module.
- Cache in classmap.
- Return a module name.
- If the function cannot be found, return "__main__".
- """
- # Python functions should always get an __module__ from their globals.
- mod = getattr(func, "__module__", None)
- if mod is not None:
- return mod
- if func in classmap:
- return classmap[func]
-
- for name, module in list(sys.modules.items()):
- if module is None:
- continue # skip dummy package entries
- if name != '__main__' and getattr(module, funcname, None) is func:
- break
- else:
- name = '__main__'
- classmap[func] = name
- return name
-
# Unpickling machinery
@@ -799,8 +975,14 @@ class _Unpickler:
encoding="ASCII", errors="strict"):
"""This takes a binary file for reading a pickle data stream.
- The protocol version of the pickle is detected automatically, so no
- proto argument is needed.
+ The protocol version of the pickle is detected automatically, so
+ no proto argument is needed.
+
+ The argument *file* must have two methods, a read() method that
+ takes an integer argument, and a readline() method that requires
+ no arguments. Both methods should return bytes. Thus *file*
+ can be a binary file object opened for reading, a io.BytesIO
+ object, or any other custom object that meets this interface.
The file-like object must have two methods, a read() method
that takes an integer argument, and a readline() method that
@@ -809,16 +991,17 @@ class _Unpickler:
reading, a BytesIO object, or any other custom object that
meets this interface.
- Optional keyword arguments are *fix_imports*, *encoding* and *errors*,
- which are used to control compatiblity support for pickle stream
- generated by Python 2.x. If *fix_imports* is True, pickle will try to
- map the old Python 2.x names to the new names used in Python 3.x. The
- *encoding* and *errors* tell pickle how to decode 8-bit string
- instances pickled by Python 2.x; these default to 'ASCII' and
- 'strict', respectively.
+ Optional keyword arguments are *fix_imports*, *encoding* and
+ *errors*, which are used to control compatiblity support for
+ pickle stream generated by Python 2. If *fix_imports* is True,
+ pickle will try to map the old Python 2 names to the new names
+ used in Python 3. The *encoding* and *errors* tell pickle how
+ to decode 8-bit string instances pickled by Python 2; these
+ default to 'ASCII' and 'strict', respectively. *encoding* can be
+ 'bytes' to read theses 8-bit string instances as bytes objects.
"""
- self.readline = file.readline
- self.read = file.read
+ self._file_readline = file.readline
+ self._file_read = file.read
self.memo = {}
self.encoding = encoding
self.errors = errors
@@ -832,16 +1015,20 @@ class _Unpickler:
"""
# Check whether Unpickler was initialized correctly. This is
# only needed to mimic the behavior of _pickle.Unpickler.dump().
- if not hasattr(self, "read"):
+ if not hasattr(self, "_file_read"):
raise UnpicklingError("Unpickler.__init__() was not called by "
"%s.__init__()" % (self.__class__.__name__,))
+ self._unframer = _Unframer(self._file_read, self._file_readline)
+ self.read = self._unframer.read
+ self.readline = self._unframer.readline
self.mark = object() # any new unique object
self.stack = []
self.append = self.stack.append
+ self.proto = 0
read = self.read
dispatch = self.dispatch
try:
- while 1:
+ while True:
key = read(1)
if not key:
raise EOFError
@@ -871,12 +1058,19 @@ class _Unpickler:
dispatch = {}
def load_proto(self):
- proto = ord(self.read(1))
+ proto = self.read(1)[0]
if not 0 <= proto <= HIGHEST_PROTOCOL:
raise ValueError("unsupported pickle protocol: %d" % proto)
self.proto = proto
dispatch[PROTO[0]] = load_proto
+ def load_frame(self):
+ frame_size, = unpack('<Q', self.read(8))
+ if frame_size > sys.maxsize:
+ raise ValueError("frame size > sys.maxsize: %d" % frame_size)
+ self._unframer.load_frame(frame_size)
+ dispatch[FRAME[0]] = load_frame
+
def load_persid(self):
pid = self.readline()[:-1].decode("ascii")
self.append(self.persistent_load(pid))
@@ -906,43 +1100,40 @@ class _Unpickler:
elif data == TRUE[1:]:
val = True
else:
- try:
- val = int(data, 0)
- except ValueError:
- val = int(data, 0)
+ val = int(data, 0)
self.append(val)
dispatch[INT[0]] = load_int
def load_binint(self):
- self.append(mloads(b'i' + self.read(4)))
+ self.append(unpack('<i', self.read(4))[0])
dispatch[BININT[0]] = load_binint
def load_binint1(self):
- self.append(ord(self.read(1)))
+ self.append(self.read(1)[0])
dispatch[BININT1[0]] = load_binint1
def load_binint2(self):
- self.append(mloads(b'i' + self.read(2) + b'\000\000'))
+ self.append(unpack('<H', self.read(2))[0])
dispatch[BININT2[0]] = load_binint2
def load_long(self):
- val = self.readline()[:-1].decode("ascii")
- if val and val[-1] == 'L':
+ val = self.readline()[:-1]
+ if val and val[-1] == b'L'[0]:
val = val[:-1]
self.append(int(val, 0))
dispatch[LONG[0]] = load_long
def load_long1(self):
- n = ord(self.read(1))
+ n = self.read(1)[0]
data = self.read(n)
self.append(decode_long(data))
dispatch[LONG1[0]] = load_long1
def load_long4(self):
- n = mloads(b'i' + self.read(4))
+ n, = unpack('<i', self.read(4))
if n < 0:
# Corrupt or hostile pickle -- we never write one like this
- raise UnpicklingError("LONG pickle has negative byte count");
+ raise UnpicklingError("LONG pickle has negative byte count")
data = self.read(n)
self.append(decode_long(data))
dispatch[LONG4[0]] = load_long4
@@ -951,39 +1142,43 @@ class _Unpickler:
self.append(float(self.readline()[:-1]))
dispatch[FLOAT[0]] = load_float
- def load_binfloat(self, unpack=struct.unpack):
+ def load_binfloat(self):
self.append(unpack('>d', self.read(8))[0])
dispatch[BINFLOAT[0]] = load_binfloat
+ def _decode_string(self, value):
+ # Used to allow strings from Python 2 to be decoded either as
+ # bytes or Unicode strings. This should be used only with the
+ # STRING, BINSTRING and SHORT_BINSTRING opcodes.
+ if self.encoding == "bytes":
+ return value
+ else:
+ return value.decode(self.encoding, self.errors)
+
def load_string(self):
- orig = self.readline()
- rep = orig[:-1]
- for q in (b'"', b"'"): # double or single quote
- if rep.startswith(q):
- if len(rep) < 2 or not rep.endswith(q):
- raise ValueError("insecure string pickle")
- rep = rep[len(q):-len(q)]
- break
+ data = self.readline()[:-1]
+ # Strip outermost quotes
+ if len(data) >= 2 and data[0] == data[-1] and data[0] in b'"\'':
+ data = data[1:-1]
else:
- raise ValueError("insecure string pickle: %r" % orig)
- self.append(codecs.escape_decode(rep)[0]
- .decode(self.encoding, self.errors))
+ raise UnpicklingError("the STRING opcode argument must be quoted")
+ self.append(self._decode_string(codecs.escape_decode(data)[0]))
dispatch[STRING[0]] = load_string
def load_binstring(self):
# Deprecated BINSTRING uses signed 32-bit length
- len = mloads(b'i' + self.read(4))
+ len, = unpack('<i', self.read(4))
if len < 0:
- raise UnpicklingError("BINSTRING pickle has negative byte count");
+ raise UnpicklingError("BINSTRING pickle has negative byte count")
data = self.read(len)
- value = str(data, self.encoding, self.errors)
- self.append(value)
+ self.append(self._decode_string(data))
dispatch[BINSTRING[0]] = load_binstring
- def load_binbytes(self, unpack=struct.unpack, maxsize=sys.maxsize):
+ def load_binbytes(self):
len, = unpack('<I', self.read(4))
if len > maxsize:
- raise UnpicklingError("BINBYTES exceeds system's maximum size of %d bytes" % maxsize);
+ raise UnpicklingError("BINBYTES exceeds system's maximum size "
+ "of %d bytes" % maxsize)
self.append(self.read(len))
dispatch[BINBYTES[0]] = load_binbytes
@@ -991,25 +1186,38 @@ class _Unpickler:
self.append(str(self.readline()[:-1], 'raw-unicode-escape'))
dispatch[UNICODE[0]] = load_unicode
- def load_binunicode(self, unpack=struct.unpack, maxsize=sys.maxsize):
+ def load_binunicode(self):
len, = unpack('<I', self.read(4))
if len > maxsize:
- raise UnpicklingError("BINUNICODE exceeds system's maximum size of %d bytes" % maxsize);
+ raise UnpicklingError("BINUNICODE exceeds system's maximum size "
+ "of %d bytes" % maxsize)
self.append(str(self.read(len), 'utf-8', 'surrogatepass'))
dispatch[BINUNICODE[0]] = load_binunicode
+ def load_binunicode8(self):
+ len, = unpack('<Q', self.read(8))
+ if len > maxsize:
+ raise UnpicklingError("BINUNICODE8 exceeds system's maximum size "
+ "of %d bytes" % maxsize)
+ self.append(str(self.read(len), 'utf-8', 'surrogatepass'))
+ dispatch[BINUNICODE8[0]] = load_binunicode8
+
def load_short_binstring(self):
- len = ord(self.read(1))
- data = bytes(self.read(len))
- value = str(data, self.encoding, self.errors)
- self.append(value)
+ len = self.read(1)[0]
+ data = self.read(len)
+ self.append(self._decode_string(data))
dispatch[SHORT_BINSTRING[0]] = load_short_binstring
def load_short_binbytes(self):
- len = ord(self.read(1))
- self.append(bytes(self.read(len)))
+ len = self.read(1)[0]
+ self.append(self.read(len))
dispatch[SHORT_BINBYTES[0]] = load_short_binbytes
+ def load_short_binunicode(self):
+ len = self.read(1)[0]
+ self.append(str(self.read(len), 'utf-8', 'surrogatepass'))
+ dispatch[SHORT_BINUNICODE[0]] = load_short_binunicode
+
def load_tuple(self):
k = self.marker()
self.stack[k:] = [tuple(self.stack[k+1:])]
@@ -1039,6 +1247,15 @@ class _Unpickler:
self.append({})
dispatch[EMPTY_DICT[0]] = load_empty_dictionary
+ def load_empty_set(self):
+ self.append(set())
+ dispatch[EMPTY_SET[0]] = load_empty_set
+
+ def load_frozenset(self):
+ k = self.marker()
+ self.stack[k:] = [frozenset(self.stack[k+1:])]
+ dispatch[FROZENSET[0]] = load_frozenset
+
def load_list(self):
k = self.marker()
self.stack[k:] = [self.stack[k+1:]]
@@ -1046,12 +1263,9 @@ class _Unpickler:
def load_dict(self):
k = self.marker()
- d = {}
items = self.stack[k+1:]
- for i in range(0, len(items), 2):
- key = items[i]
- value = items[i+1]
- d[key] = value
+ d = {items[i]: items[i+1]
+ for i in range(0, len(items), 2)}
self.stack[k:] = [d]
dispatch[DICT[0]] = load_dict
@@ -1090,11 +1304,19 @@ class _Unpickler:
def load_newobj(self):
args = self.stack.pop()
- cls = self.stack[-1]
+ cls = self.stack.pop()
obj = cls.__new__(cls, *args)
- self.stack[-1] = obj
+ self.append(obj)
dispatch[NEWOBJ[0]] = load_newobj
+ def load_newobj_ex(self):
+ kwargs = self.stack.pop()
+ args = self.stack.pop()
+ cls = self.stack.pop()
+ obj = cls.__new__(cls, *args, **kwargs)
+ self.append(obj)
+ dispatch[NEWOBJ_EX[0]] = load_newobj_ex
+
def load_global(self):
module = self.readline()[:-1].decode("utf-8")
name = self.readline()[:-1].decode("utf-8")
@@ -1102,18 +1324,26 @@ class _Unpickler:
self.append(klass)
dispatch[GLOBAL[0]] = load_global
+ def load_stack_global(self):
+ name = self.stack.pop()
+ module = self.stack.pop()
+ if type(name) is not str or type(module) is not str:
+ raise UnpicklingError("STACK_GLOBAL requires str")
+ self.append(self.find_class(module, name))
+ dispatch[STACK_GLOBAL[0]] = load_stack_global
+
def load_ext1(self):
- code = ord(self.read(1))
+ code = self.read(1)[0]
self.get_extension(code)
dispatch[EXT1[0]] = load_ext1
def load_ext2(self):
- code = mloads(b'i' + self.read(2) + b'\000\000')
+ code, = unpack('<H', self.read(2))
self.get_extension(code)
dispatch[EXT2[0]] = load_ext2
def load_ext4(self):
- code = mloads(b'i' + self.read(4))
+ code, = unpack('<i', self.read(4))
self.get_extension(code)
dispatch[EXT4[0]] = load_ext4
@@ -1127,7 +1357,7 @@ class _Unpickler:
if not key:
if code <= 0: # note that 0 is forbidden
# Corrupt or hostile pickle.
- raise UnpicklingError("EXT specifies code <= 0");
+ raise UnpicklingError("EXT specifies code <= 0")
raise ValueError("unregistered extension code %d" % code)
obj = self.find_class(*key)
_extension_cache[code] = obj
@@ -1141,9 +1371,8 @@ class _Unpickler:
if module in _compat_pickle.IMPORT_MAPPING:
module = _compat_pickle.IMPORT_MAPPING[module]
__import__(module, level=0)
- mod = sys.modules[module]
- klass = getattr(mod, name)
- return klass
+ return _getattribute(sys.modules[module], name,
+ allow_qualname=self.proto >= 4)
def load_reduce(self):
stack = self.stack
@@ -1181,7 +1410,7 @@ class _Unpickler:
self.append(self.memo[i])
dispatch[BINGET[0]] = load_binget
- def load_long_binget(self, unpack=struct.unpack):
+ def load_long_binget(self):
i, = unpack('<I', self.read(4))
self.append(self.memo[i])
dispatch[LONG_BINGET[0]] = load_long_binget
@@ -1200,13 +1429,18 @@ class _Unpickler:
self.memo[i] = self.stack[-1]
dispatch[BINPUT[0]] = load_binput
- def load_long_binput(self, unpack=struct.unpack, maxsize=sys.maxsize):
+ def load_long_binput(self):
i, = unpack('<I', self.read(4))
if i > maxsize:
raise ValueError("negative LONG_BINPUT argument")
self.memo[i] = self.stack[-1]
dispatch[LONG_BINPUT[0]] = load_long_binput
+ def load_memoize(self):
+ memo = self.memo
+ memo[len(memo)] = self.stack[-1]
+ dispatch[MEMOIZE[0]] = load_memoize
+
def load_append(self):
stack = self.stack
value = stack.pop()
@@ -1246,12 +1480,26 @@ class _Unpickler:
del stack[mark:]
dispatch[SETITEMS[0]] = load_setitems
+ def load_additems(self):
+ stack = self.stack
+ mark = self.marker()
+ set_obj = stack[mark - 1]
+ items = stack[mark + 1:]
+ if isinstance(set_obj, set):
+ set_obj.update(items)
+ else:
+ add = set_obj.add
+ for item in items:
+ add(item)
+ del stack[mark:]
+ dispatch[ADDITEMS[0]] = load_additems
+
def load_build(self):
stack = self.stack
state = stack.pop()
inst = stack[-1]
setstate = getattr(inst, "__setstate__", None)
- if setstate:
+ if setstate is not None:
setstate(state)
return
slotstate = None
@@ -1279,86 +1527,46 @@ class _Unpickler:
raise _Stop(value)
dispatch[STOP[0]] = load_stop
-# Encode/decode ints.
-
-def encode_long(x):
- r"""Encode a long to a two's complement little-endian binary string.
- Note that 0 is a special case, returning an empty string, to save a
- byte in the LONG1 pickling context.
-
- >>> encode_long(0)
- b''
- >>> encode_long(255)
- b'\xff\x00'
- >>> encode_long(32767)
- b'\xff\x7f'
- >>> encode_long(-256)
- b'\x00\xff'
- >>> encode_long(-32768)
- b'\x00\x80'
- >>> encode_long(-128)
- b'\x80'
- >>> encode_long(127)
- b'\x7f'
- >>>
- """
- if x == 0:
- return b''
- nbytes = (x.bit_length() >> 3) + 1
- result = x.to_bytes(nbytes, byteorder='little', signed=True)
- if x < 0 and nbytes > 1:
- if result[-1] == 0xff and (result[-2] & 0x80) != 0:
- result = result[:-1]
- return result
-
-def decode_long(data):
- r"""Decode an int from a two's complement little-endian binary string.
-
- >>> decode_long(b'')
- 0
- >>> decode_long(b"\xff\x00")
- 255
- >>> decode_long(b"\xff\x7f")
- 32767
- >>> decode_long(b"\x00\xff")
- -256
- >>> decode_long(b"\x00\x80")
- -32768
- >>> decode_long(b"\x80")
- -128
- >>> decode_long(b"\x7f")
- 127
- """
- return int.from_bytes(data, byteorder='little', signed=True)
# Shorthands
-def dump(obj, file, protocol=None, *, fix_imports=True):
- Pickler(file, protocol, fix_imports=fix_imports).dump(obj)
+def _dump(obj, file, protocol=None, *, fix_imports=True):
+ _Pickler(file, protocol, fix_imports=fix_imports).dump(obj)
-def dumps(obj, protocol=None, *, fix_imports=True):
+def _dumps(obj, protocol=None, *, fix_imports=True):
f = io.BytesIO()
- Pickler(f, protocol, fix_imports=fix_imports).dump(obj)
+ _Pickler(f, protocol, fix_imports=fix_imports).dump(obj)
res = f.getvalue()
assert isinstance(res, bytes_types)
return res
-def load(file, *, fix_imports=True, encoding="ASCII", errors="strict"):
- return Unpickler(file, fix_imports=fix_imports,
+def _load(file, *, fix_imports=True, encoding="ASCII", errors="strict"):
+ return _Unpickler(file, fix_imports=fix_imports,
encoding=encoding, errors=errors).load()
-def loads(s, *, fix_imports=True, encoding="ASCII", errors="strict"):
+def _loads(s, *, fix_imports=True, encoding="ASCII", errors="strict"):
if isinstance(s, str):
raise TypeError("Can't load pickle from unicode string")
file = io.BytesIO(s)
- return Unpickler(file, fix_imports=fix_imports,
- encoding=encoding, errors=errors).load()
+ return _Unpickler(file, fix_imports=fix_imports,
+ encoding=encoding, errors=errors).load()
# Use the faster _pickle if possible
try:
- from _pickle import *
+ from _pickle import (
+ PickleError,
+ PicklingError,
+ UnpicklingError,
+ Pickler,
+ Unpickler,
+ dump,
+ dumps,
+ load,
+ loads
+ )
except ImportError:
Pickler, Unpickler = _Pickler, _Unpickler
+ dump, dumps, load, loads = _dump, _dumps, _load, _loads
# Doctest
def _test():
diff --git a/Lib/pickletools.py b/Lib/pickletools.py
index 612fa8f..71c2aa1 100644
--- a/Lib/pickletools.py
+++ b/Lib/pickletools.py
@@ -11,6 +11,7 @@ dis(pickle, out=None, memo=None, indentlevel=4)
'''
import codecs
+import io
import pickle
import re
import sys
@@ -34,119 +35,118 @@ bytes_types = pickle.bytes_types
# by a later GET.
-"""
-"A pickle" is a program for a virtual pickle machine (PM, but more accurately
-called an unpickling machine). It's a sequence of opcodes, interpreted by the
-PM, building an arbitrarily complex Python object.
-
-For the most part, the PM is very simple: there are no looping, testing, or
-conditional instructions, no arithmetic and no function calls. Opcodes are
-executed once each, from first to last, until a STOP opcode is reached.
-
-The PM has two data areas, "the stack" and "the memo".
-
-Many opcodes push Python objects onto the stack; e.g., INT pushes a Python
-integer object on the stack, whose value is gotten from a decimal string
-literal immediately following the INT opcode in the pickle bytestream. Other
-opcodes take Python objects off the stack. The result of unpickling is
-whatever object is left on the stack when the final STOP opcode is executed.
-
-The memo is simply an array of objects, or it can be implemented as a dict
-mapping little integers to objects. The memo serves as the PM's "long term
-memory", and the little integers indexing the memo are akin to variable
-names. Some opcodes pop a stack object into the memo at a given index,
-and others push a memo object at a given index onto the stack again.
-
-At heart, that's all the PM has. Subtleties arise for these reasons:
-
-+ Object identity. Objects can be arbitrarily complex, and subobjects
- may be shared (for example, the list [a, a] refers to the same object a
- twice). It can be vital that unpickling recreate an isomorphic object
- graph, faithfully reproducing sharing.
-
-+ Recursive objects. For example, after "L = []; L.append(L)", L is a
- list, and L[0] is the same list. This is related to the object identity
- point, and some sequences of pickle opcodes are subtle in order to
- get the right result in all cases.
-
-+ Things pickle doesn't know everything about. Examples of things pickle
- does know everything about are Python's builtin scalar and container
- types, like ints and tuples. They generally have opcodes dedicated to
- them. For things like module references and instances of user-defined
- classes, pickle's knowledge is limited. Historically, many enhancements
- have been made to the pickle protocol in order to do a better (faster,
- and/or more compact) job on those.
-
-+ Backward compatibility and micro-optimization. As explained below,
- pickle opcodes never go away, not even when better ways to do a thing
- get invented. The repertoire of the PM just keeps growing over time.
- For example, protocol 0 had two opcodes for building Python integers (INT
- and LONG), protocol 1 added three more for more-efficient pickling of short
- integers, and protocol 2 added two more for more-efficient pickling of
- long integers (before protocol 2, the only ways to pickle a Python long
- took time quadratic in the number of digits, for both pickling and
- unpickling). "Opcode bloat" isn't so much a subtlety as a source of
- wearying complication.
-
-
-Pickle protocols:
-
-For compatibility, the meaning of a pickle opcode never changes. Instead new
-pickle opcodes get added, and each version's unpickler can handle all the
-pickle opcodes in all protocol versions to date. So old pickles continue to
-be readable forever. The pickler can generally be told to restrict itself to
-the subset of opcodes available under previous protocol versions too, so that
-users can create pickles under the current version readable by older
-versions. However, a pickle does not contain its version number embedded
-within it. If an older unpickler tries to read a pickle using a later
-protocol, the result is most likely an exception due to seeing an unknown (in
-the older unpickler) opcode.
-
-The original pickle used what's now called "protocol 0", and what was called
-"text mode" before Python 2.3. The entire pickle bytestream is made up of
-printable 7-bit ASCII characters, plus the newline character, in protocol 0.
-That's why it was called text mode. Protocol 0 is small and elegant, but
-sometimes painfully inefficient.
-
-The second major set of additions is now called "protocol 1", and was called
-"binary mode" before Python 2.3. This added many opcodes with arguments
-consisting of arbitrary bytes, including NUL bytes and unprintable "high bit"
-bytes. Binary mode pickles can be substantially smaller than equivalent
-text mode pickles, and sometimes faster too; e.g., BININT represents a 4-byte
-int as 4 bytes following the opcode, which is cheaper to unpickle than the
-(perhaps) 11-character decimal string attached to INT. Protocol 1 also added
-a number of opcodes that operate on many stack elements at once (like APPENDS
-and SETITEMS), and "shortcut" opcodes (like EMPTY_DICT and EMPTY_TUPLE).
-
-The third major set of additions came in Python 2.3, and is called "protocol
-2". This added:
-
-- A better way to pickle instances of new-style classes (NEWOBJ).
-
-- A way for a pickle to identify its protocol (PROTO).
-
-- Time- and space- efficient pickling of long ints (LONG{1,4}).
-
-- Shortcuts for small tuples (TUPLE{1,2,3}}.
-
-- Dedicated opcodes for bools (NEWTRUE, NEWFALSE).
-
-- The "extension registry", a vector of popular objects that can be pushed
- efficiently by index (EXT{1,2,4}). This is akin to the memo and GET, but
- the registry contents are predefined (there's nothing akin to the memo's
- PUT).
-
-Another independent change with Python 2.3 is the abandonment of any
-pretense that it might be safe to load pickles received from untrusted
-parties -- no sufficient security analysis has been done to guarantee
-this and there isn't a use case that warrants the expense of such an
-analysis.
-
-To this end, all tests for __safe_for_unpickling__ or for
-copyreg.safe_constructors are removed from the unpickling code.
-References to these variables in the descriptions below are to be seen
-as describing unpickling in Python 2.2 and before.
-"""
+# "A pickle" is a program for a virtual pickle machine (PM, but more accurately
+# called an unpickling machine). It's a sequence of opcodes, interpreted by the
+# PM, building an arbitrarily complex Python object.
+#
+# For the most part, the PM is very simple: there are no looping, testing, or
+# conditional instructions, no arithmetic and no function calls. Opcodes are
+# executed once each, from first to last, until a STOP opcode is reached.
+#
+# The PM has two data areas, "the stack" and "the memo".
+#
+# Many opcodes push Python objects onto the stack; e.g., INT pushes a Python
+# integer object on the stack, whose value is gotten from a decimal string
+# literal immediately following the INT opcode in the pickle bytestream. Other
+# opcodes take Python objects off the stack. The result of unpickling is
+# whatever object is left on the stack when the final STOP opcode is executed.
+#
+# The memo is simply an array of objects, or it can be implemented as a dict
+# mapping little integers to objects. The memo serves as the PM's "long term
+# memory", and the little integers indexing the memo are akin to variable
+# names. Some opcodes pop a stack object into the memo at a given index,
+# and others push a memo object at a given index onto the stack again.
+#
+# At heart, that's all the PM has. Subtleties arise for these reasons:
+#
+# + Object identity. Objects can be arbitrarily complex, and subobjects
+# may be shared (for example, the list [a, a] refers to the same object a
+# twice). It can be vital that unpickling recreate an isomorphic object
+# graph, faithfully reproducing sharing.
+#
+# + Recursive objects. For example, after "L = []; L.append(L)", L is a
+# list, and L[0] is the same list. This is related to the object identity
+# point, and some sequences of pickle opcodes are subtle in order to
+# get the right result in all cases.
+#
+# + Things pickle doesn't know everything about. Examples of things pickle
+# does know everything about are Python's builtin scalar and container
+# types, like ints and tuples. They generally have opcodes dedicated to
+# them. For things like module references and instances of user-defined
+# classes, pickle's knowledge is limited. Historically, many enhancements
+# have been made to the pickle protocol in order to do a better (faster,
+# and/or more compact) job on those.
+#
+# + Backward compatibility and micro-optimization. As explained below,
+# pickle opcodes never go away, not even when better ways to do a thing
+# get invented. The repertoire of the PM just keeps growing over time.
+# For example, protocol 0 had two opcodes for building Python integers (INT
+# and LONG), protocol 1 added three more for more-efficient pickling of short
+# integers, and protocol 2 added two more for more-efficient pickling of
+# long integers (before protocol 2, the only ways to pickle a Python long
+# took time quadratic in the number of digits, for both pickling and
+# unpickling). "Opcode bloat" isn't so much a subtlety as a source of
+# wearying complication.
+#
+#
+# Pickle protocols:
+#
+# For compatibility, the meaning of a pickle opcode never changes. Instead new
+# pickle opcodes get added, and each version's unpickler can handle all the
+# pickle opcodes in all protocol versions to date. So old pickles continue to
+# be readable forever. The pickler can generally be told to restrict itself to
+# the subset of opcodes available under previous protocol versions too, so that
+# users can create pickles under the current version readable by older
+# versions. However, a pickle does not contain its version number embedded
+# within it. If an older unpickler tries to read a pickle using a later
+# protocol, the result is most likely an exception due to seeing an unknown (in
+# the older unpickler) opcode.
+#
+# The original pickle used what's now called "protocol 0", and what was called
+# "text mode" before Python 2.3. The entire pickle bytestream is made up of
+# printable 7-bit ASCII characters, plus the newline character, in protocol 0.
+# That's why it was called text mode. Protocol 0 is small and elegant, but
+# sometimes painfully inefficient.
+#
+# The second major set of additions is now called "protocol 1", and was called
+# "binary mode" before Python 2.3. This added many opcodes with arguments
+# consisting of arbitrary bytes, including NUL bytes and unprintable "high bit"
+# bytes. Binary mode pickles can be substantially smaller than equivalent
+# text mode pickles, and sometimes faster too; e.g., BININT represents a 4-byte
+# int as 4 bytes following the opcode, which is cheaper to unpickle than the
+# (perhaps) 11-character decimal string attached to INT. Protocol 1 also added
+# a number of opcodes that operate on many stack elements at once (like APPENDS
+# and SETITEMS), and "shortcut" opcodes (like EMPTY_DICT and EMPTY_TUPLE).
+#
+# The third major set of additions came in Python 2.3, and is called "protocol
+# 2". This added:
+#
+# - A better way to pickle instances of new-style classes (NEWOBJ).
+#
+# - A way for a pickle to identify its protocol (PROTO).
+#
+# - Time- and space- efficient pickling of long ints (LONG{1,4}).
+#
+# - Shortcuts for small tuples (TUPLE{1,2,3}}.
+#
+# - Dedicated opcodes for bools (NEWTRUE, NEWFALSE).
+#
+# - The "extension registry", a vector of popular objects that can be pushed
+# efficiently by index (EXT{1,2,4}). This is akin to the memo and GET, but
+# the registry contents are predefined (there's nothing akin to the memo's
+# PUT).
+#
+# Another independent change with Python 2.3 is the abandonment of any
+# pretense that it might be safe to load pickles received from untrusted
+# parties -- no sufficient security analysis has been done to guarantee
+# this and there isn't a use case that warrants the expense of such an
+# analysis.
+#
+# To this end, all tests for __safe_for_unpickling__ or for
+# copyreg.safe_constructors are removed from the unpickling code.
+# References to these variables in the descriptions below are to be seen
+# as describing unpickling in Python 2.2 and before.
+
# Meta-rule: Descriptions are stored in instances of descriptor objects,
# with plain constructors. No meta-language is defined from which
@@ -169,6 +169,7 @@ UP_TO_NEWLINE = -1
TAKEN_FROM_ARGUMENT1 = -2 # num bytes is 1-byte unsigned int
TAKEN_FROM_ARGUMENT4 = -3 # num bytes is 4-byte signed little-endian int
TAKEN_FROM_ARGUMENT4U = -4 # num bytes is 4-byte unsigned little-endian int
+TAKEN_FROM_ARGUMENT8U = -5 # num bytes is 8-byte unsigned little-endian int
class ArgumentDescriptor(object):
__slots__ = (
@@ -176,7 +177,7 @@ class ArgumentDescriptor(object):
'name',
# length of argument, in bytes; an int; UP_TO_NEWLINE and
- # TAKEN_FROM_ARGUMENT{1,4} are negative values for variable-length
+ # TAKEN_FROM_ARGUMENT{1,4,8} are negative values for variable-length
# cases
'n',
@@ -197,7 +198,8 @@ class ArgumentDescriptor(object):
n in (UP_TO_NEWLINE,
TAKEN_FROM_ARGUMENT1,
TAKEN_FROM_ARGUMENT4,
- TAKEN_FROM_ARGUMENT4U))
+ TAKEN_FROM_ARGUMENT4U,
+ TAKEN_FROM_ARGUMENT8U))
self.n = n
self.reader = reader
@@ -289,6 +291,27 @@ uint4 = ArgumentDescriptor(
doc="Four-byte unsigned integer, little-endian.")
+def read_uint8(f):
+ r"""
+ >>> import io
+ >>> read_uint8(io.BytesIO(b'\xff\x00\x00\x00\x00\x00\x00\x00'))
+ 255
+ >>> read_uint8(io.BytesIO(b'\xff' * 8)) == 2**64-1
+ True
+ """
+
+ data = f.read(8)
+ if len(data) == 8:
+ return _unpack("<Q", data)[0]
+ raise ValueError("not enough data in stream to read uint8")
+
+uint8 = ArgumentDescriptor(
+ name='uint8',
+ n=8,
+ reader=read_uint8,
+ doc="Eight-byte unsigned integer, little-endian.")
+
+
def read_stringnl(f, decode=True, stripquotes=True):
r"""
>>> import io
@@ -382,6 +405,36 @@ stringnl_noescape_pair = ArgumentDescriptor(
a single blank separating the two strings.
""")
+
+def read_string1(f):
+ r"""
+ >>> import io
+ >>> read_string1(io.BytesIO(b"\x00"))
+ ''
+ >>> read_string1(io.BytesIO(b"\x03abcdef"))
+ 'abc'
+ """
+
+ n = read_uint1(f)
+ assert n >= 0
+ data = f.read(n)
+ if len(data) == n:
+ return data.decode("latin-1")
+ raise ValueError("expected %d bytes in a string1, but only %d remain" %
+ (n, len(data)))
+
+string1 = ArgumentDescriptor(
+ name="string1",
+ n=TAKEN_FROM_ARGUMENT1,
+ reader=read_string1,
+ doc="""A counted string.
+
+ The first argument is a 1-byte unsigned int giving the number
+ of bytes in the string, and the second argument is that many
+ bytes.
+ """)
+
+
def read_string4(f):
r"""
>>> import io
@@ -416,28 +469,28 @@ string4 = ArgumentDescriptor(
""")
-def read_string1(f):
+def read_bytes1(f):
r"""
>>> import io
- >>> read_string1(io.BytesIO(b"\x00"))
- ''
- >>> read_string1(io.BytesIO(b"\x03abcdef"))
- 'abc'
+ >>> read_bytes1(io.BytesIO(b"\x00"))
+ b''
+ >>> read_bytes1(io.BytesIO(b"\x03abcdef"))
+ b'abc'
"""
n = read_uint1(f)
assert n >= 0
data = f.read(n)
if len(data) == n:
- return data.decode("latin-1")
- raise ValueError("expected %d bytes in a string1, but only %d remain" %
+ return data
+ raise ValueError("expected %d bytes in a bytes1, but only %d remain" %
(n, len(data)))
-string1 = ArgumentDescriptor(
- name="string1",
+bytes1 = ArgumentDescriptor(
+ name="bytes1",
n=TAKEN_FROM_ARGUMENT1,
- reader=read_string1,
- doc="""A counted string.
+ reader=read_bytes1,
+ doc="""A counted bytes string.
The first argument is a 1-byte unsigned int giving the number
of bytes in the string, and the second argument is that many
@@ -487,6 +540,7 @@ def read_bytes4(f):
"""
n = read_uint4(f)
+ assert n >= 0
if n > sys.maxsize:
raise ValueError("bytes4 byte count > sys.maxsize: %d" % n)
data = f.read(n)
@@ -506,6 +560,40 @@ bytes4 = ArgumentDescriptor(
""")
+def read_bytes8(f):
+ r"""
+ >>> import io, struct, sys
+ >>> read_bytes8(io.BytesIO(b"\x00\x00\x00\x00\x00\x00\x00\x00abc"))
+ b''
+ >>> read_bytes8(io.BytesIO(b"\x03\x00\x00\x00\x00\x00\x00\x00abcdef"))
+ b'abc'
+ >>> bigsize8 = struct.pack("<Q", sys.maxsize//3)
+ >>> read_bytes8(io.BytesIO(bigsize8 + b"abcdef")) #doctest: +ELLIPSIS
+ Traceback (most recent call last):
+ ...
+ ValueError: expected ... bytes in a bytes8, but only 6 remain
+ """
+
+ n = read_uint8(f)
+ assert n >= 0
+ if n > sys.maxsize:
+ raise ValueError("bytes8 byte count > sys.maxsize: %d" % n)
+ data = f.read(n)
+ if len(data) == n:
+ return data
+ raise ValueError("expected %d bytes in a bytes8, but only %d remain" %
+ (n, len(data)))
+
+bytes8 = ArgumentDescriptor(
+ name="bytes8",
+ n=TAKEN_FROM_ARGUMENT8U,
+ reader=read_bytes8,
+ doc="""A counted bytes string.
+
+ The first argument is a 8-byte little-endian unsigned int giving
+ the number of bytes, and the second argument is that many bytes.
+ """)
+
def read_unicodestringnl(f):
r"""
>>> import io
@@ -531,6 +619,46 @@ unicodestringnl = ArgumentDescriptor(
escape sequences.
""")
+
+def read_unicodestring1(f):
+ r"""
+ >>> import io
+ >>> s = 'abcd\uabcd'
+ >>> enc = s.encode('utf-8')
+ >>> enc
+ b'abcd\xea\xaf\x8d'
+ >>> n = bytes([len(enc)]) # little-endian 1-byte length
+ >>> t = read_unicodestring1(io.BytesIO(n + enc + b'junk'))
+ >>> s == t
+ True
+
+ >>> read_unicodestring1(io.BytesIO(n + enc[:-1]))
+ Traceback (most recent call last):
+ ...
+ ValueError: expected 7 bytes in a unicodestring1, but only 6 remain
+ """
+
+ n = read_uint1(f)
+ assert n >= 0
+ data = f.read(n)
+ if len(data) == n:
+ return str(data, 'utf-8', 'surrogatepass')
+ raise ValueError("expected %d bytes in a unicodestring1, but only %d "
+ "remain" % (n, len(data)))
+
+unicodestring1 = ArgumentDescriptor(
+ name="unicodestring1",
+ n=TAKEN_FROM_ARGUMENT1,
+ reader=read_unicodestring1,
+ doc="""A counted Unicode string.
+
+ The first argument is a 1-byte little-endian signed int
+ giving the number of bytes in the string, and the second
+ argument-- the UTF-8 encoding of the Unicode string --
+ contains that many bytes.
+ """)
+
+
def read_unicodestring4(f):
r"""
>>> import io
@@ -550,6 +678,7 @@ def read_unicodestring4(f):
"""
n = read_uint4(f)
+ assert n >= 0
if n > sys.maxsize:
raise ValueError("unicodestring4 byte count > sys.maxsize: %d" % n)
data = f.read(n)
@@ -571,6 +700,47 @@ unicodestring4 = ArgumentDescriptor(
""")
+def read_unicodestring8(f):
+ r"""
+ >>> import io
+ >>> s = 'abcd\uabcd'
+ >>> enc = s.encode('utf-8')
+ >>> enc
+ b'abcd\xea\xaf\x8d'
+ >>> n = bytes([len(enc)]) + bytes(7) # little-endian 8-byte length
+ >>> t = read_unicodestring8(io.BytesIO(n + enc + b'junk'))
+ >>> s == t
+ True
+
+ >>> read_unicodestring8(io.BytesIO(n + enc[:-1]))
+ Traceback (most recent call last):
+ ...
+ ValueError: expected 7 bytes in a unicodestring8, but only 6 remain
+ """
+
+ n = read_uint8(f)
+ assert n >= 0
+ if n > sys.maxsize:
+ raise ValueError("unicodestring8 byte count > sys.maxsize: %d" % n)
+ data = f.read(n)
+ if len(data) == n:
+ return str(data, 'utf-8', 'surrogatepass')
+ raise ValueError("expected %d bytes in a unicodestring8, but only %d "
+ "remain" % (n, len(data)))
+
+unicodestring8 = ArgumentDescriptor(
+ name="unicodestring8",
+ n=TAKEN_FROM_ARGUMENT8U,
+ reader=read_unicodestring8,
+ doc="""A counted Unicode string.
+
+ The first argument is a 8-byte little-endian signed int
+ giving the number of bytes in the string, and the second
+ argument-- the UTF-8 encoding of the Unicode string --
+ contains that many bytes.
+ """)
+
+
def read_decimalnl_short(f):
r"""
>>> import io
@@ -799,103 +969,107 @@ class StackObject(object):
return self.name
-pyint = StackObject(
- name='int',
- obtype=int,
- doc="A short (as opposed to long) Python integer object.")
-
-pylong = StackObject(
- name='long',
- obtype=int,
- doc="A long (as opposed to short) Python integer object.")
+pyint = pylong = StackObject(
+ name='int',
+ obtype=int,
+ doc="A Python integer object.")
pyinteger_or_bool = StackObject(
- name='int_or_bool',
- obtype=(int, bool),
- doc="A Python integer object (short or long), or "
- "a Python bool.")
+ name='int_or_bool',
+ obtype=(int, bool),
+ doc="A Python integer or boolean object.")
pybool = StackObject(
- name='bool',
- obtype=(bool,),
- doc="A Python bool object.")
+ name='bool',
+ obtype=bool,
+ doc="A Python boolean object.")
pyfloat = StackObject(
- name='float',
- obtype=float,
- doc="A Python float object.")
+ name='float',
+ obtype=float,
+ doc="A Python float object.")
-pystring = StackObject(
- name='string',
- obtype=bytes,
- doc="A Python (8-bit) string object.")
+pybytes_or_str = pystring = StackObject(
+ name='bytes_or_str',
+ obtype=(bytes, str),
+ doc="A Python bytes or (Unicode) string object.")
pybytes = StackObject(
- name='bytes',
- obtype=bytes,
- doc="A Python bytes object.")
+ name='bytes',
+ obtype=bytes,
+ doc="A Python bytes object.")
pyunicode = StackObject(
- name='str',
- obtype=str,
- doc="A Python (Unicode) string object.")
+ name='str',
+ obtype=str,
+ doc="A Python (Unicode) string object.")
pynone = StackObject(
- name="None",
- obtype=type(None),
- doc="The Python None object.")
+ name="None",
+ obtype=type(None),
+ doc="The Python None object.")
pytuple = StackObject(
- name="tuple",
- obtype=tuple,
- doc="A Python tuple object.")
+ name="tuple",
+ obtype=tuple,
+ doc="A Python tuple object.")
pylist = StackObject(
- name="list",
- obtype=list,
- doc="A Python list object.")
+ name="list",
+ obtype=list,
+ doc="A Python list object.")
pydict = StackObject(
- name="dict",
- obtype=dict,
- doc="A Python dict object.")
+ name="dict",
+ obtype=dict,
+ doc="A Python dict object.")
+
+pyset = StackObject(
+ name="set",
+ obtype=set,
+ doc="A Python set object.")
+
+pyfrozenset = StackObject(
+ name="frozenset",
+ obtype=set,
+ doc="A Python frozenset object.")
anyobject = StackObject(
- name='any',
- obtype=object,
- doc="Any kind of object whatsoever.")
+ name='any',
+ obtype=object,
+ doc="Any kind of object whatsoever.")
markobject = StackObject(
- name="mark",
- obtype=StackObject,
- doc="""'The mark' is a unique object.
-
- Opcodes that operate on a variable number of objects
- generally don't embed the count of objects in the opcode,
- or pull it off the stack. Instead the MARK opcode is used
- to push a special marker object on the stack, and then
- some other opcodes grab all the objects from the top of
- the stack down to (but not including) the topmost marker
- object.
- """)
+ name="mark",
+ obtype=StackObject,
+ doc="""'The mark' is a unique object.
+
+Opcodes that operate on a variable number of objects
+generally don't embed the count of objects in the opcode,
+or pull it off the stack. Instead the MARK opcode is used
+to push a special marker object on the stack, and then
+some other opcodes grab all the objects from the top of
+the stack down to (but not including) the topmost marker
+object.
+""")
stackslice = StackObject(
- name="stackslice",
- obtype=StackObject,
- doc="""An object representing a contiguous slice of the stack.
+ name="stackslice",
+ obtype=StackObject,
+ doc="""An object representing a contiguous slice of the stack.
- This is used in conjunction with markobject, to represent all
- of the stack following the topmost markobject. For example,
- the POP_MARK opcode changes the stack from
+This is used in conjunction with markobject, to represent all
+of the stack following the topmost markobject. For example,
+the POP_MARK opcode changes the stack from
- [..., markobject, stackslice]
- to
- [...]
+ [..., markobject, stackslice]
+to
+ [...]
- No matter how many object are on the stack after the topmost
- markobject, POP_MARK gets rid of all of them (including the
- topmost markobject too).
- """)
+No matter how many object are on the stack after the topmost
+markobject, POP_MARK gets rid of all of them (including the
+topmost markobject too).
+""")
##############################################################################
# Descriptors for pickle opcodes.
@@ -1032,7 +1206,7 @@ opcodes = [
code='L',
arg=decimalnl_long,
stack_before=[],
- stack_after=[pylong],
+ stack_after=[pyint],
proto=0,
doc="""Push a long integer.
@@ -1050,7 +1224,7 @@ opcodes = [
code='\x8a',
arg=long1,
stack_before=[],
- stack_after=[pylong],
+ stack_after=[pyint],
proto=2,
doc="""Long integer using one-byte length.
@@ -1061,7 +1235,7 @@ opcodes = [
code='\x8b',
arg=long4,
stack_before=[],
- stack_after=[pylong],
+ stack_after=[pyint],
proto=2,
doc="""Long integer using found-byte length.
@@ -1074,45 +1248,50 @@ opcodes = [
code='S',
arg=stringnl,
stack_before=[],
- stack_after=[pystring],
+ stack_after=[pybytes_or_str],
proto=0,
doc="""Push a Python string object.
The argument is a repr-style string, with bracketing quote characters,
and perhaps embedded escapes. The argument extends until the next
- newline character. (Actually, they are decoded into a str instance
+ newline character. These are usually decoded into a str instance
using the encoding given to the Unpickler constructor. or the default,
- 'ASCII'.)
+ 'ASCII'. If the encoding given was 'bytes' however, they will be
+ decoded as bytes object instead.
"""),
I(name='BINSTRING',
code='T',
arg=string4,
stack_before=[],
- stack_after=[pystring],
+ stack_after=[pybytes_or_str],
proto=1,
doc="""Push a Python string object.
- There are two arguments: the first is a 4-byte little-endian signed int
- giving the number of bytes in the string, and the second is that many
- bytes, which are taken literally as the string content. (Actually,
- they are decoded into a str instance using the encoding given to the
- Unpickler constructor. or the default, 'ASCII'.)
+ There are two arguments: the first is a 4-byte little-endian
+ signed int giving the number of bytes in the string, and the
+ second is that many bytes, which are taken literally as the string
+ content. These are usually decoded into a str instance using the
+ encoding given to the Unpickler constructor. or the default,
+ 'ASCII'. If the encoding given was 'bytes' however, they will be
+ decoded as bytes object instead.
"""),
I(name='SHORT_BINSTRING',
code='U',
arg=string1,
stack_before=[],
- stack_after=[pystring],
+ stack_after=[pybytes_or_str],
proto=1,
doc="""Push a Python string object.
- There are two arguments: the first is a 1-byte unsigned int giving
- the number of bytes in the string, and the second is that many bytes,
- which are taken literally as the string content. (Actually, they
- are decoded into a str instance using the encoding given to the
- Unpickler constructor. or the default, 'ASCII'.)
+ There are two arguments: the first is a 1-byte unsigned int giving
+ the number of bytes in the string, and the second is that many
+ bytes, which are taken literally as the string content. These are
+ usually decoded into a str instance using the encoding given to
+ the Unpickler constructor. or the default, 'ASCII'. If the
+ encoding given was 'bytes' however, they will be decoded as bytes
+ object instead.
"""),
# Bytes (protocol 3 only; older protocols don't support bytes at all)
@@ -1143,6 +1322,19 @@ opcodes = [
literally as the string content.
"""),
+ I(name='BINBYTES8',
+ code='\x8e',
+ arg=bytes8,
+ stack_before=[],
+ stack_after=[pybytes],
+ proto=4,
+ doc="""Push a Python bytes object.
+
+ There are two arguments: the first is a 8-byte unsigned int giving
+ the number of bytes in the string, and the second is that many bytes,
+ which are taken literally as the string content.
+ """),
+
# Ways to spell None.
I(name='NONE',
@@ -1191,6 +1383,19 @@ opcodes = [
until the next newline character.
"""),
+ I(name='SHORT_BINUNICODE',
+ code='\x8c',
+ arg=unicodestring1,
+ stack_before=[],
+ stack_after=[pyunicode],
+ proto=4,
+ doc="""Push a Python Unicode string object.
+
+ There are two arguments: the first is a 1-byte little-endian signed int
+ giving the number of bytes in the string. The second is that many
+ bytes, and is the UTF-8 encoding of the Unicode string.
+ """),
+
I(name='BINUNICODE',
code='X',
arg=unicodestring4,
@@ -1204,6 +1409,19 @@ opcodes = [
bytes, and is the UTF-8 encoding of the Unicode string.
"""),
+ I(name='BINUNICODE8',
+ code='\x8d',
+ arg=unicodestring8,
+ stack_before=[],
+ stack_after=[pyunicode],
+ proto=4,
+ doc="""Push a Python Unicode string object.
+
+ There are two arguments: the first is a 8-byte little-endian signed int
+ giving the number of bytes in the string. The second is that many
+ bytes, and is the UTF-8 encoding of the Unicode string.
+ """),
+
# Ways to spell floats.
I(name='FLOAT',
@@ -1429,6 +1647,54 @@ opcodes = [
1, 2, ..., n, and in that order.
"""),
+ # Ways to build sets
+
+ I(name='EMPTY_SET',
+ code='\x8f',
+ arg=None,
+ stack_before=[],
+ stack_after=[pyset],
+ proto=4,
+ doc="Push an empty set."),
+
+ I(name='ADDITEMS',
+ code='\x90',
+ arg=None,
+ stack_before=[pyset, markobject, stackslice],
+ stack_after=[pyset],
+ proto=4,
+ doc="""Add an arbitrary number of items to an existing set.
+
+ The slice of the stack following the topmost markobject is taken as
+ a sequence of items, added to the set immediately under the topmost
+ markobject. Everything at and after the topmost markobject is popped,
+ leaving the mutated set at the top of the stack.
+
+ Stack before: ... pyset markobject item_1 ... item_n
+ Stack after: ... pyset
+
+ where pyset has been modified via pyset.add(item_i) = item_i for i in
+ 1, 2, ..., n, and in that order.
+ """),
+
+ # Way to build frozensets
+
+ I(name='FROZENSET',
+ code='\x91',
+ arg=None,
+ stack_before=[markobject, stackslice],
+ stack_after=[pyfrozenset],
+ proto=4,
+ doc="""Build a frozenset out of the topmost slice, after markobject.
+
+ All the stack entries following the topmost markobject are placed into
+ a single Python frozenset, which single frozenset object replaces all
+ of the stack from the topmost markobject onward. For example,
+
+ Stack before: ... markobject 1 2 3
+ Stack after: ... frozenset({1, 2, 3})
+ """),
+
# Stack manipulation.
I(name='POP',
@@ -1550,6 +1816,18 @@ opcodes = [
unsigned little-endian integer following.
"""),
+ I(name='MEMOIZE',
+ code='\x94',
+ arg=None,
+ stack_before=[anyobject],
+ stack_after=[anyobject],
+ proto=4,
+ doc="""Store the stack top into the memo. The stack is not popped.
+
+ The index of the memo location to write is the number of
+ elements currently present in the memo.
+ """),
+
# Access the extension registry (predefined objects). Akin to the GET
# family.
@@ -1615,6 +1893,15 @@ opcodes = [
stack, so unpickling subclasses can override this form of lookup.
"""),
+ I(name='STACK_GLOBAL',
+ code='\x93',
+ arg=None,
+ stack_before=[pyunicode, pyunicode],
+ stack_after=[anyobject],
+ proto=0,
+ doc="""Push a global object (module.attr) on the stack.
+ """),
+
# Ways to build objects of classes pickle doesn't know about directly
# (user-defined classes). I despair of documenting this accurately
# and comprehensibly -- you really have to read the pickle code to
@@ -1771,6 +2058,21 @@ opcodes = [
onto the stack.
"""),
+ I(name='NEWOBJ_EX',
+ code='\x92',
+ arg=None,
+ stack_before=[anyobject, anyobject, anyobject],
+ stack_after=[anyobject],
+ proto=4,
+ doc="""Build an object instance.
+
+ The stack before should be thought of as containing a class
+ object followed by an argument tuple and by a keyword argument dict
+ (the dict being the stack top). Call these cls and args. They are
+ popped off the stack, and the value returned by
+ cls.__new__(cls, *args, *kwargs) is pushed back onto the stack.
+ """),
+
# Machine control.
I(name='PROTO',
@@ -1798,6 +2100,20 @@ opcodes = [
empty then.
"""),
+ # Framing support.
+
+ I(name='FRAME',
+ code='\x95',
+ arg=uint8,
+ stack_before=[],
+ stack_after=[],
+ proto=4,
+ doc="""Indicate the beginning of a new frame.
+
+ The unpickler may use this opcode to safely prefetch data from its
+ underlying stream.
+ """),
+
# Ways to deal with persistent IDs.
I(name='PERSID',
@@ -1904,6 +2220,38 @@ del assure_pickle_consistency
##############################################################################
# A pickle opcode generator.
+def _genops(data, yield_end_pos=False):
+ if isinstance(data, bytes_types):
+ data = io.BytesIO(data)
+
+ if hasattr(data, "tell"):
+ getpos = data.tell
+ else:
+ getpos = lambda: None
+
+ while True:
+ pos = getpos()
+ code = data.read(1)
+ opcode = code2op.get(code.decode("latin-1"))
+ if opcode is None:
+ if code == b"":
+ raise ValueError("pickle exhausted before seeing STOP")
+ else:
+ raise ValueError("at position %s, opcode %r unknown" % (
+ "<unknown>" if pos is None else pos,
+ code))
+ if opcode.arg is None:
+ arg = None
+ else:
+ arg = opcode.arg.reader(data)
+ if yield_end_pos:
+ yield opcode, arg, pos, getpos()
+ else:
+ yield opcode, arg, pos
+ if code == b'.':
+ assert opcode.name == 'STOP'
+ break
+
def genops(pickle):
"""Generate all the opcodes in a pickle.
@@ -1927,62 +2275,48 @@ def genops(pickle):
used. Else (the pickle doesn't have a tell(), and it's not obvious how
to query its current position) pos is None.
"""
-
- if isinstance(pickle, bytes_types):
- import io
- pickle = io.BytesIO(pickle)
-
- if hasattr(pickle, "tell"):
- getpos = pickle.tell
- else:
- getpos = lambda: None
-
- while True:
- pos = getpos()
- code = pickle.read(1)
- opcode = code2op.get(code.decode("latin-1"))
- if opcode is None:
- if code == b"":
- raise ValueError("pickle exhausted before seeing STOP")
- else:
- raise ValueError("at position %s, opcode %r unknown" % (
- pos is None and "<unknown>" or pos,
- code))
- if opcode.arg is None:
- arg = None
- else:
- arg = opcode.arg.reader(pickle)
- yield opcode, arg, pos
- if code == b'.':
- assert opcode.name == 'STOP'
- break
+ return _genops(pickle)
##############################################################################
# A pickle optimizer.
def optimize(p):
'Optimize a pickle string by removing unused PUT opcodes'
- gets = set() # set of args used by a GET opcode
- puts = [] # (arg, startpos, stoppos) for the PUT opcodes
- prevpos = None # set to pos if previous opcode was a PUT
- for opcode, arg, pos in genops(p):
- if prevpos is not None:
- puts.append((prevarg, prevpos, pos))
- prevpos = None
+ not_a_put = object()
+ gets = { not_a_put } # set of args used by a GET opcode
+ opcodes = [] # (startpos, stoppos, putid)
+ proto = 0
+ for opcode, arg, pos, end_pos in _genops(p, yield_end_pos=True):
if 'PUT' in opcode.name:
- prevarg, prevpos = arg, pos
- elif 'GET' in opcode.name:
- gets.add(arg)
-
- # Copy the pickle string except for PUTS without a corresponding GET
- s = []
- i = 0
- for arg, start, stop in puts:
- j = stop if (arg in gets) else start
- s.append(p[i:j])
- i = stop
- s.append(p[i:])
- return b''.join(s)
+ opcodes.append((pos, end_pos, arg))
+ elif 'FRAME' in opcode.name:
+ pass
+ else:
+ if 'GET' in opcode.name:
+ gets.add(arg)
+ elif opcode.name == 'PROTO':
+ assert pos == 0, pos
+ proto = arg
+ opcodes.append((pos, end_pos, not_a_put))
+ prevpos, prevarg = pos, None
+
+ # Copy the opcodes except for PUTS without a corresponding GET
+ out = io.BytesIO()
+ opcodes = iter(opcodes)
+ if proto >= 2:
+ # Write the PROTO header before any framing
+ start, stop, _ = next(opcodes)
+ out.write(p[start:stop])
+ buf = pickle._Framer(out.write)
+ if proto >= 4:
+ buf.start_framing()
+ for start, stop, putid in opcodes:
+ if putid in gets:
+ buf.commit_frame()
+ buf.write(p[start:stop])
+ if proto >= 4:
+ buf.end_framing()
+ return out.getvalue()
##############################################################################
# A symbolic pickle disassembler.
@@ -2082,17 +2416,20 @@ def dis(pickle, out=None, memo=None, indentlevel=4, annotate=0):
errormsg = markmsg = "no MARK exists on stack"
# Check for correct memo usage.
- if opcode.name in ("PUT", "BINPUT", "LONG_BINPUT"):
- assert arg is not None
- if arg in memo:
+ if opcode.name in ("PUT", "BINPUT", "LONG_BINPUT", "MEMOIZE"):
+ if opcode.name == "MEMOIZE":
+ memo_idx = len(memo)
+ else:
+ assert arg is not None
+ memo_idx = arg
+ if memo_idx in memo:
errormsg = "memo key %r already defined" % arg
elif not stack:
errormsg = "stack is empty -- can't store into memo"
elif stack[-1] is markobject:
errormsg = "can't store markobject in the memo"
else:
- memo[arg] = stack[-1]
-
+ memo[memo_idx] = stack[-1]
elif opcode.name in ("GET", "BINGET", "LONG_BINGET"):
if arg in memo:
assert len(after) == 1
diff --git a/Lib/pkgutil.py b/Lib/pkgutil.py
index 20e6498..e42b6eb 100644
--- a/Lib/pkgutil.py
+++ b/Lib/pkgutil.py
@@ -1,12 +1,14 @@
"""Utilities to support packages."""
-import os
-import sys
+from functools import singledispatch as simplegeneric
import importlib
-import imp
+import importlib.util
+import importlib.machinery
+import os
import os.path
-from warnings import warn
+import sys
from types import ModuleType
+import warnings
__all__ = [
'get_importer', 'iter_importers', 'get_loader', 'find_loader',
@@ -14,59 +16,34 @@ __all__ = [
'ImpImporter', 'ImpLoader', 'read_code', 'extend_path',
]
+
+def _get_spec(finder, name):
+ """Return the finder-specific module spec."""
+ # Works with legacy finders.
+ try:
+ find_spec = finder.find_spec
+ except AttributeError:
+ loader = finder.find_module(name)
+ if loader is None:
+ return None
+ return importlib.util.spec_from_loader(name, loader)
+ else:
+ return find_spec(name)
+
+
def read_code(stream):
# This helper is needed in order for the PEP 302 emulation to
# correctly handle compiled files
import marshal
magic = stream.read(4)
- if magic != imp.get_magic():
+ if magic != importlib.util.MAGIC_NUMBER:
return None
stream.read(8) # Skip timestamp and size
return marshal.load(stream)
-def simplegeneric(func):
- """Make a trivial single-dispatch generic function"""
- registry = {}
- def wrapper(*args, **kw):
- ob = args[0]
- try:
- cls = ob.__class__
- except AttributeError:
- cls = type(ob)
- try:
- mro = cls.__mro__
- except AttributeError:
- try:
- class cls(cls, object):
- pass
- mro = cls.__mro__[1:]
- except TypeError:
- mro = object, # must be an ExtensionClass or some such :(
- for t in mro:
- if t in registry:
- return registry[t](*args, **kw)
- else:
- return func(*args, **kw)
- try:
- wrapper.__name__ = func.__name__
- except (TypeError, AttributeError):
- pass # Python 2.3 doesn't allow functions to be renamed
-
- def register(typ, func=None):
- if func is None:
- return lambda f: register(typ, f)
- registry[typ] = func
- return func
-
- wrapper.__dict__ = func.__dict__
- wrapper.__doc__ = func.__doc__
- wrapper.register = register
- return wrapper
-
-
def walk_packages(path=None, prefix='', onerror=None):
"""Yields (module_loader, name, ispkg) for all modules recursively
on path, or, if path is None, all accessible modules.
@@ -121,8 +98,7 @@ def walk_packages(path=None, prefix='', onerror=None):
# don't traverse path items we've seen before
path = [p for p in path if not seen(p)]
- for item in walk_packages(path, name+'.', onerror):
- yield item
+ yield from walk_packages(path, name+'.', onerror)
def iter_modules(path=None, prefix=''):
@@ -149,13 +125,12 @@ def iter_modules(path=None, prefix=''):
yield i, name, ispkg
-#@simplegeneric
+@simplegeneric
def iter_importer_modules(importer, prefix=''):
if not hasattr(importer, 'iter_modules'):
return []
return importer.iter_modules(prefix)
-iter_importer_modules = simplegeneric(iter_importer_modules)
# Implement a file walker for the normal importlib path hook
def _iter_file_finder_modules(importer, prefix=''):
@@ -201,6 +176,13 @@ def _iter_file_finder_modules(importer, prefix=''):
iter_importer_modules.register(
importlib.machinery.FileFinder, _iter_file_finder_modules)
+
+def _import_imp():
+ global imp
+ with warnings.catch_warnings():
+ warnings.simplefilter('ignore', PendingDeprecationWarning)
+ imp = importlib.import_module('imp')
+
class ImpImporter:
"""PEP 302 Importer that wraps Python's "classic" import algorithm
@@ -213,8 +195,10 @@ class ImpImporter:
"""
def __init__(self, path=None):
- warn("This emulation is deprecated, use 'importlib' instead",
+ global imp
+ warnings.warn("This emulation is deprecated, use 'importlib' instead",
DeprecationWarning)
+ _import_imp()
self.path = path
def find_module(self, fullname, path=None):
@@ -279,8 +263,9 @@ class ImpLoader:
code = source = None
def __init__(self, fullname, file, filename, etc):
- warn("This emulation is deprecated, use 'importlib' instead",
- DeprecationWarning)
+ warnings.warn("This emulation is deprecated, use 'importlib' instead",
+ DeprecationWarning)
+ _import_imp()
self.file = file
self.filename = filename
self.fullname = fullname
@@ -350,16 +335,16 @@ class ImpLoader:
self.file.close()
elif mod_type==imp.PY_COMPILED:
if os.path.exists(self.filename[:-1]):
- f = open(self.filename[:-1], 'r')
- self.source = f.read()
- f.close()
+ with open(self.filename[:-1], 'r') as f:
+ self.source = f.read()
elif mod_type==imp.PKG_DIRECTORY:
self.source = self._get_delegate().get_source()
return self.source
-
def _get_delegate(self):
- return ImpImporter(self.filename).find_module('__init__')
+ finder = ImpImporter(self.filename)
+ spec = _get_spec(finder, '__init__')
+ return spec.loader
def get_filename(self, fullname=None):
fullname = self._fix_name(fullname)
@@ -456,12 +441,12 @@ def iter_importers(fullname=""):
if path is None:
return
else:
- for importer in sys.meta_path:
- yield importer
+ yield from sys.meta_path
path = sys.path
for item in path:
yield get_importer(item)
+
def get_loader(module_or_name):
"""Get a PEP 302 "loader" object for module_or_name
@@ -476,6 +461,8 @@ def get_loader(module_or_name):
loader = getattr(module, '__loader__', None)
if loader is not None:
return loader
+ if getattr(module, '__spec__', None) is None:
+ return None
fullname = module.__name__
else:
fullname = module_or_name
@@ -485,29 +472,22 @@ def get_loader(module_or_name):
def find_loader(fullname):
"""Find a PEP 302 "loader" object for fullname
- This is s convenience wrapper around :func:`importlib.find_loader` that
- sets the *path* argument correctly when searching for submodules, and
- also ensures parent packages (if any) are imported before searching for
- submodules.
+ This is a backwards compatibility wrapper around
+ importlib.util.find_spec that converts most failures to ImportError
+ and only returns the loader rather than the full spec
"""
if fullname.startswith('.'):
msg = "Relative module name {!r} not supported".format(fullname)
raise ImportError(msg)
- path = None
- pkg_name = fullname.rpartition(".")[0]
- if pkg_name:
- pkg = importlib.import_module(pkg_name)
- path = getattr(pkg, "__path__", None)
- if path is None:
- return None
try:
- return importlib.find_loader(fullname, path)
+ spec = importlib.util.find_spec(fullname)
except (ImportError, AttributeError, TypeError, ValueError) as ex:
# This hack fixes an impedance mismatch between pkgutil and
# importlib, where the latter raises other errors for cases where
# pkgutil previously raised ImportError
msg = "Error while finding loader for {!r} ({}: {})"
raise ImportError(msg.format(fullname, type(ex), ex)) from ex
+ return spec.loader
def extend_path(path, name):
@@ -569,13 +549,14 @@ def extend_path(path, name):
finder = get_importer(dir)
if finder is not None:
+ portions = []
+ if hasattr(finder, 'find_spec'):
+ spec = finder.find_spec(final_name)
+ if spec is not None:
+ portions = spec.submodule_search_locations or []
# Is this finder PEP 420 compliant?
- if hasattr(finder, 'find_loader'):
- loader, portions = finder.find_loader(final_name)
- else:
- # No, no need to call it
- loader = None
- portions = []
+ elif hasattr(finder, 'find_loader'):
+ _, portions = finder.find_loader(final_name)
for portion in portions:
# XXX This may still add duplicate entries to path on
@@ -589,19 +570,20 @@ def extend_path(path, name):
if os.path.isfile(pkgfile):
try:
f = open(pkgfile)
- except IOError as msg:
+ except OSError as msg:
sys.stderr.write("Can't open %s: %s\n" %
(pkgfile, msg))
else:
- for line in f:
- line = line.rstrip('\n')
- if not line or line.startswith('#'):
- continue
- path.append(line) # Don't check for existence!
- f.close()
+ with f:
+ for line in f:
+ line = line.rstrip('\n')
+ if not line or line.startswith('#'):
+ continue
+ path.append(line) # Don't check for existence!
return path
+
def get_data(package, resource):
"""Get a resource from a package.
@@ -624,10 +606,15 @@ def get_data(package, resource):
which does not support get_data(), then None is returned.
"""
- loader = get_loader(package)
+ spec = importlib.util.find_spec(package)
+ if spec is None:
+ return None
+ loader = spec.loader
if loader is None or not hasattr(loader, 'get_data'):
return None
- mod = sys.modules.get(package) or loader.load_module(package)
+ # XXX needs test
+ mod = (sys.modules.get(package) or
+ importlib._bootstrap._SpecMethods(spec).load())
if mod is None or not hasattr(mod, '__file__'):
return None
diff --git a/Lib/plat-os2emx/IN.py b/Lib/plat-os2emx/IN.py
deleted file mode 100644
index 753ae24..0000000
--- a/Lib/plat-os2emx/IN.py
+++ /dev/null
@@ -1,82 +0,0 @@
-# Generated by h2py from f:/emx/include/netinet/in.h
-
-# Included from sys/param.h
-PAGE_SIZE = 0x1000
-HZ = 100
-MAXNAMLEN = 260
-MAXPATHLEN = 260
-def htonl(X): return _swapl(X)
-
-def ntohl(X): return _swapl(X)
-
-def htons(X): return _swaps(X)
-
-def ntohs(X): return _swaps(X)
-
-IPPROTO_IP = 0
-IPPROTO_ICMP = 1
-IPPROTO_IGMP = 2
-IPPROTO_GGP = 3
-IPPROTO_TCP = 6
-IPPROTO_EGP = 8
-IPPROTO_PUP = 12
-IPPROTO_UDP = 17
-IPPROTO_IDP = 22
-IPPROTO_TP = 29
-IPPROTO_EON = 80
-IPPROTO_RAW = 255
-IPPROTO_MAX = 256
-IPPORT_RESERVED = 1024
-IPPORT_USERRESERVED = 5000
-def IN_CLASSA(i): return (((int)(i) & 0x80000000) == 0)
-
-IN_CLASSA_NET = 0xff000000
-IN_CLASSA_NSHIFT = 24
-IN_CLASSA_HOST = 0x00ffffff
-IN_CLASSA_MAX = 128
-def IN_CLASSB(i): return (((int)(i) & 0xc0000000) == 0x80000000)
-
-IN_CLASSB_NET = 0xffff0000
-IN_CLASSB_NSHIFT = 16
-IN_CLASSB_HOST = 0x0000ffff
-IN_CLASSB_MAX = 65536
-def IN_CLASSC(i): return (((int)(i) & 0xe0000000) == 0xc0000000)
-
-IN_CLASSC_NET = 0xffffff00
-IN_CLASSC_NSHIFT = 8
-IN_CLASSC_HOST = 0x000000ff
-def IN_CLASSD(i): return (((int)(i) & 0xf0000000) == 0xe0000000)
-
-IN_CLASSD_NET = 0xf0000000
-IN_CLASSD_NSHIFT = 28
-IN_CLASSD_HOST = 0x0fffffff
-def IN_MULTICAST(i): return IN_CLASSD(i)
-
-def IN_EXPERIMENTAL(i): return (((int)(i) & 0xe0000000) == 0xe0000000)
-
-def IN_BADCLASS(i): return (((int)(i) & 0xf0000000) == 0xf0000000)
-
-INADDR_ANY = 0x00000000
-INADDR_LOOPBACK = 0x7f000001
-INADDR_BROADCAST = 0xffffffff
-INADDR_NONE = 0xffffffff
-INADDR_UNSPEC_GROUP = 0xe0000000
-INADDR_ALLHOSTS_GROUP = 0xe0000001
-INADDR_MAX_LOCAL_GROUP = 0xe00000ff
-IN_LOOPBACKNET = 127
-IP_OPTIONS = 1
-IP_MULTICAST_IF = 2
-IP_MULTICAST_TTL = 3
-IP_MULTICAST_LOOP = 4
-IP_ADD_MEMBERSHIP = 5
-IP_DROP_MEMBERSHIP = 6
-IP_HDRINCL = 2
-IP_TOS = 3
-IP_TTL = 4
-IP_RECVOPTS = 5
-IP_RECVRETOPTS = 6
-IP_RECVDSTADDR = 7
-IP_RETOPTS = 8
-IP_DEFAULT_MULTICAST_TTL = 1
-IP_DEFAULT_MULTICAST_LOOP = 1
-IP_MAX_MEMBERSHIPS = 20
diff --git a/Lib/plat-os2emx/SOCKET.py b/Lib/plat-os2emx/SOCKET.py
deleted file mode 100644
index dac594a..0000000
--- a/Lib/plat-os2emx/SOCKET.py
+++ /dev/null
@@ -1,106 +0,0 @@
-# Generated by h2py from f:/emx/include/sys/socket.h
-
-# Included from sys/types.h
-FD_SETSIZE = 256
-
-# Included from sys/uio.h
-FREAD = 1
-FWRITE = 2
-SOCK_STREAM = 1
-SOCK_DGRAM = 2
-SOCK_RAW = 3
-SOCK_RDM = 4
-SOCK_SEQPACKET = 5
-SO_DEBUG = 0x0001
-SO_ACCEPTCONN = 0x0002
-SO_REUSEADDR = 0x0004
-SO_KEEPALIVE = 0x0008
-SO_DONTROUTE = 0x0010
-SO_BROADCAST = 0x0020
-SO_USELOOPBACK = 0x0040
-SO_LINGER = 0x0080
-SO_OOBINLINE = 0x0100
-SO_L_BROADCAST = 0x0200
-SO_RCV_SHUTDOWN = 0x0400
-SO_SND_SHUTDOWN = 0x0800
-SO_SNDBUF = 0x1001
-SO_RCVBUF = 0x1002
-SO_SNDLOWAT = 0x1003
-SO_RCVLOWAT = 0x1004
-SO_SNDTIMEO = 0x1005
-SO_RCVTIMEO = 0x1006
-SO_ERROR = 0x1007
-SO_TYPE = 0x1008
-SO_OPTIONS = 0x1010
-SOL_SOCKET = 0xffff
-AF_UNSPEC = 0
-AF_UNIX = 1
-AF_INET = 2
-AF_IMPLINK = 3
-AF_PUP = 4
-AF_CHAOS = 5
-AF_NS = 6
-AF_NBS = 7
-AF_ISO = 7
-AF_OSI = AF_ISO
-AF_ECMA = 8
-AF_DATAKIT = 9
-AF_CCITT = 10
-AF_SNA = 11
-AF_DECnet = 12
-AF_DLI = 13
-AF_LAT = 14
-AF_HYLINK = 15
-AF_APPLETALK = 16
-AF_NB = 17
-AF_NETBIOS = AF_NB
-AF_OS2 = AF_UNIX
-AF_MAX = 18
-PF_UNSPEC = AF_UNSPEC
-PF_UNIX = AF_UNIX
-PF_INET = AF_INET
-PF_IMPLINK = AF_IMPLINK
-PF_PUP = AF_PUP
-PF_CHAOS = AF_CHAOS
-PF_NS = AF_NS
-PF_NBS = AF_NBS
-PF_ISO = AF_ISO
-PF_OSI = AF_ISO
-PF_ECMA = AF_ECMA
-PF_DATAKIT = AF_DATAKIT
-PF_CCITT = AF_CCITT
-PF_SNA = AF_SNA
-PF_DECnet = AF_DECnet
-PF_DLI = AF_DLI
-PF_LAT = AF_LAT
-PF_HYLINK = AF_HYLINK
-PF_APPLETALK = AF_APPLETALK
-PF_NB = AF_NB
-PF_NETBIOS = AF_NB
-PF_OS2 = AF_UNIX
-PF_MAX = AF_MAX
-SOMAXCONN = 5
-MSG_OOB = 0x1
-MSG_PEEK = 0x2
-MSG_DONTROUTE = 0x4
-MSG_EOR = 0x8
-MSG_TRUNC = 0x10
-MSG_CTRUNC = 0x20
-MSG_WAITALL = 0x40
-MSG_MAXIOVLEN = 16
-SCM_RIGHTS = 0x01
-MT_FREE = 0
-MT_DATA = 1
-MT_HEADER = 2
-MT_SOCKET = 3
-MT_PCB = 4
-MT_RTABLE = 5
-MT_HTABLE = 6
-MT_ATABLE = 7
-MT_SONAME = 8
-MT_ZOMBIE = 9
-MT_SOOPTS = 10
-MT_FTABLE = 11
-MT_RIGHTS = 12
-MT_IFADDR = 13
-MAXSOCKETS = 2048
diff --git a/Lib/plat-os2emx/_emx_link.py b/Lib/plat-os2emx/_emx_link.py
deleted file mode 100644
index 01e6b54..0000000
--- a/Lib/plat-os2emx/_emx_link.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# _emx_link.py
-
-# Written by Andrew I MacIntyre, December 2002.
-
-"""_emx_link.py is a simplistic emulation of the Unix link(2) library routine
-for creating so-called hard links. It is intended to be imported into
-the os module in place of the unimplemented (on OS/2) Posix link()
-function (os.link()).
-
-We do this on OS/2 by implementing a file copy, with link(2) semantics:-
- - the target cannot already exist;
- - we hope that the actual file open (if successful) is actually
- atomic...
-
-Limitations of this approach/implementation include:-
- - no support for correct link counts (EMX stat(target).st_nlink
- is always 1);
- - thread safety undefined;
- - default file permissions (r+w) used, can't be over-ridden;
- - implemented in Python so comparatively slow, especially for large
- source files;
- - need sufficient free disk space to store the copy.
-
-Behaviour:-
- - any exception should propagate to the caller;
- - want target to be an exact copy of the source, so use binary mode;
- - returns None, same as os.link() which is implemented in posixmodule.c;
- - target removed in the event of a failure where possible;
- - given the motivation to write this emulation came from trying to
- support a Unix resource lock implementation, where minimal overhead
- during creation of the target is desirable and the files are small,
- we read a source block before attempting to create the target so that
- we're ready to immediately write some data into it.
-"""
-
-import os
-import errno
-
-__all__ = ['link']
-
-def link(source, target):
- """link(source, target) -> None
-
- Attempt to hard link the source file to the target file name.
- On OS/2, this creates a complete copy of the source file.
- """
-
- s = os.open(source, os.O_RDONLY | os.O_BINARY)
- if os.isatty(s):
- raise OSError(errno.EXDEV, 'Cross-device link')
- data = os.read(s, 1024)
-
- try:
- t = os.open(target, os.O_WRONLY | os.O_BINARY | os.O_CREAT | os.O_EXCL)
- except OSError:
- os.close(s)
- raise
-
- try:
- while data:
- os.write(t, data)
- data = os.read(s, 1024)
- except OSError:
- os.close(s)
- os.close(t)
- os.unlink(target)
- raise
-
- os.close(s)
- os.close(t)
-
-if __name__ == '__main__':
- import sys
- try:
- link(sys.argv[1], sys.argv[2])
- except IndexError:
- print('Usage: emx_link <source> <target>')
- except OSError:
- print('emx_link: %s' % str(sys.exc_info()[1]))
diff --git a/Lib/plat-os2emx/grp.py b/Lib/plat-os2emx/grp.py
deleted file mode 100644
index ee63ef8..0000000
--- a/Lib/plat-os2emx/grp.py
+++ /dev/null
@@ -1,182 +0,0 @@
-# this module is an OS/2 oriented replacement for the grp standard
-# extension module.
-
-# written by Andrew MacIntyre, April 2001.
-# updated July 2003, adding field accessor support
-
-# note that this implementation checks whether ":" or ";" as used as
-# the field separator character.
-
-"""Replacement for grp standard extension module, intended for use on
-OS/2 and similar systems which don't normally have an /etc/group file.
-
-The standard Unix group database is an ASCII text file with 4 fields per
-record (line), separated by a colon:
- - group name (string)
- - group password (optional encrypted string)
- - group id (integer)
- - group members (comma delimited list of userids, with no spaces)
-
-Note that members are only included in the group file for groups that
-aren't their primary groups.
-(see the section 8.2 of the Python Library Reference)
-
-This implementation differs from the standard Unix implementation by
-allowing use of the platform's native path separator character - ';' on OS/2,
-DOS and MS-Windows - as the field separator in addition to the Unix
-standard ":".
-
-The module looks for the group database at the following locations
-(in order first to last):
- - ${ETC_GROUP} (or %ETC_GROUP%)
- - ${ETC}/group (or %ETC%/group)
- - ${PYTHONHOME}/Etc/group (or %PYTHONHOME%/Etc/group)
-
-Classes
--------
-
-None
-
-Functions
----------
-
-getgrgid(gid) - return the record for group-id gid as a 4-tuple
-
-getgrnam(name) - return the record for group 'name' as a 4-tuple
-
-getgrall() - return a list of 4-tuples, each tuple being one record
- (NOTE: the order is arbitrary)
-
-Attributes
-----------
-
-group_file - the path of the group database file
-
-"""
-
-import os
-
-# try and find the group file
-__group_path = []
-if 'ETC_GROUP' in os.environ:
- __group_path.append(os.environ['ETC_GROUP'])
-if 'ETC' in os.environ:
- __group_path.append('%s/group' % os.environ['ETC'])
-if 'PYTHONHOME' in os.environ:
- __group_path.append('%s/Etc/group' % os.environ['PYTHONHOME'])
-
-group_file = None
-for __i in __group_path:
- try:
- __f = open(__i, 'r')
- __f.close()
- group_file = __i
- break
- except:
- pass
-
-# decide what field separator we can try to use - Unix standard, with
-# the platform's path separator as an option. No special field conversion
-# handlers are required for the group file.
-__field_sep = [':']
-if os.pathsep:
- if os.pathsep != ':':
- __field_sep.append(os.pathsep)
-
-# helper routine to identify which separator character is in use
-def __get_field_sep(record):
- fs = None
- for c in __field_sep:
- # there should be 3 delimiter characters (for 4 fields)
- if record.count(c) == 3:
- fs = c
- break
- if fs:
- return fs
- else:
- raise KeyError('>> group database fields not delimited <<')
-
-# class to match the new record field name accessors.
-# the resulting object is intended to behave like a read-only tuple,
-# with each member also accessible by a field name.
-class Group:
- def __init__(self, name, passwd, gid, mem):
- self.__dict__['gr_name'] = name
- self.__dict__['gr_passwd'] = passwd
- self.__dict__['gr_gid'] = gid
- self.__dict__['gr_mem'] = mem
- self.__dict__['_record'] = (self.gr_name, self.gr_passwd,
- self.gr_gid, self.gr_mem)
-
- def __len__(self):
- return 4
-
- def __getitem__(self, key):
- return self._record[key]
-
- def __setattr__(self, name, value):
- raise AttributeError('attribute read-only: %s' % name)
-
- def __repr__(self):
- return str(self._record)
-
- def __cmp__(self, other):
- this = str(self._record)
- if this == other:
- return 0
- elif this < other:
- return -1
- else:
- return 1
-
-
-# read the whole file, parsing each entry into tuple form
-# with dictionaries to speed recall by GID or group name
-def __read_group_file():
- if group_file:
- group = open(group_file, 'r')
- else:
- raise KeyError('>> no group database <<')
- gidx = {}
- namx = {}
- sep = None
- while 1:
- entry = group.readline().strip()
- if len(entry) > 3:
- if sep is None:
- sep = __get_field_sep(entry)
- fields = entry.split(sep)
- fields[2] = int(fields[2])
- fields[3] = [f.strip() for f in fields[3].split(',')]
- record = Group(*fields)
- if fields[2] not in gidx:
- gidx[fields[2]] = record
- if fields[0] not in namx:
- namx[fields[0]] = record
- elif len(entry) > 0:
- pass # skip empty or malformed records
- else:
- break
- group.close()
- if len(gidx) == 0:
- raise KeyError
- return (gidx, namx)
-
-# return the group database entry by GID
-def getgrgid(gid):
- g, n = __read_group_file()
- return g[gid]
-
-# return the group database entry by group name
-def getgrnam(name):
- g, n = __read_group_file()
- return n[name]
-
-# return all the group database entries
-def getgrall():
- g, n = __read_group_file()
- return g.values()
-
-# test harness
-if __name__ == '__main__':
- getgrall()
diff --git a/Lib/plat-os2emx/pwd.py b/Lib/plat-os2emx/pwd.py
deleted file mode 100644
index 2cb077f..0000000
--- a/Lib/plat-os2emx/pwd.py
+++ /dev/null
@@ -1,208 +0,0 @@
-# this module is an OS/2 oriented replacement for the pwd standard
-# extension module.
-
-# written by Andrew MacIntyre, April 2001.
-# updated July 2003, adding field accessor support
-
-# note that this implementation checks whether ":" or ";" as used as
-# the field separator character. Path conversions are are applied when
-# the database uses ":" as the field separator character.
-
-"""Replacement for pwd standard extension module, intended for use on
-OS/2 and similar systems which don't normally have an /etc/passwd file.
-
-The standard Unix password database is an ASCII text file with 7 fields
-per record (line), separated by a colon:
- - user name (string)
- - password (encrypted string, or "*" or "")
- - user id (integer)
- - group id (integer)
- - description (usually user's name)
- - home directory (path to user's home directory)
- - shell (path to the user's login shell)
-
-(see the section 8.1 of the Python Library Reference)
-
-This implementation differs from the standard Unix implementation by
-allowing use of the platform's native path separator character - ';' on OS/2,
-DOS and MS-Windows - as the field separator in addition to the Unix
-standard ":". Additionally, when ":" is the separator path conversions
-are applied to deal with any munging of the drive letter reference.
-
-The module looks for the password database at the following locations
-(in order first to last):
- - ${ETC_PASSWD} (or %ETC_PASSWD%)
- - ${ETC}/passwd (or %ETC%/passwd)
- - ${PYTHONHOME}/Etc/passwd (or %PYTHONHOME%/Etc/passwd)
-
-Classes
--------
-
-None
-
-Functions
----------
-
-getpwuid(uid) - return the record for user-id uid as a 7-tuple
-
-getpwnam(name) - return the record for user 'name' as a 7-tuple
-
-getpwall() - return a list of 7-tuples, each tuple being one record
- (NOTE: the order is arbitrary)
-
-Attributes
-----------
-
-passwd_file - the path of the password database file
-
-"""
-
-import os
-
-# try and find the passwd file
-__passwd_path = []
-if 'ETC_PASSWD' in os.environ:
- __passwd_path.append(os.environ['ETC_PASSWD'])
-if 'ETC' in os.environ:
- __passwd_path.append('%s/passwd' % os.environ['ETC'])
-if 'PYTHONHOME' in os.environ:
- __passwd_path.append('%s/Etc/passwd' % os.environ['PYTHONHOME'])
-
-passwd_file = None
-for __i in __passwd_path:
- try:
- __f = open(__i, 'r')
- __f.close()
- passwd_file = __i
- break
- except:
- pass
-
-# path conversion handlers
-def __nullpathconv(path):
- return path.replace(os.altsep, os.sep)
-
-def __unixpathconv(path):
- # two known drive letter variations: "x;" and "$x"
- if path[0] == '$':
- conv = path[1] + ':' + path[2:]
- elif path[1] == ';':
- conv = path[0] + ':' + path[2:]
- else:
- conv = path
- return conv.replace(os.altsep, os.sep)
-
-# decide what field separator we can try to use - Unix standard, with
-# the platform's path separator as an option. No special field conversion
-# handler is required when using the platform's path separator as field
-# separator, but are required for the home directory and shell fields when
-# using the standard Unix (":") field separator.
-__field_sep = {':': __unixpathconv}
-if os.pathsep:
- if os.pathsep != ':':
- __field_sep[os.pathsep] = __nullpathconv
-
-# helper routine to identify which separator character is in use
-def __get_field_sep(record):
- fs = None
- for c in __field_sep.keys():
- # there should be 6 delimiter characters (for 7 fields)
- if record.count(c) == 6:
- fs = c
- break
- if fs:
- return fs
- else:
- raise KeyError('>> passwd database fields not delimited <<')
-
-# class to match the new record field name accessors.
-# the resulting object is intended to behave like a read-only tuple,
-# with each member also accessible by a field name.
-class Passwd:
- def __init__(self, name, passwd, uid, gid, gecos, dir, shell):
- self.__dict__['pw_name'] = name
- self.__dict__['pw_passwd'] = passwd
- self.__dict__['pw_uid'] = uid
- self.__dict__['pw_gid'] = gid
- self.__dict__['pw_gecos'] = gecos
- self.__dict__['pw_dir'] = dir
- self.__dict__['pw_shell'] = shell
- self.__dict__['_record'] = (self.pw_name, self.pw_passwd,
- self.pw_uid, self.pw_gid,
- self.pw_gecos, self.pw_dir,
- self.pw_shell)
-
- def __len__(self):
- return 7
-
- def __getitem__(self, key):
- return self._record[key]
-
- def __setattr__(self, name, value):
- raise AttributeError('attribute read-only: %s' % name)
-
- def __repr__(self):
- return str(self._record)
-
- def __cmp__(self, other):
- this = str(self._record)
- if this == other:
- return 0
- elif this < other:
- return -1
- else:
- return 1
-
-
-# read the whole file, parsing each entry into tuple form
-# with dictionaries to speed recall by UID or passwd name
-def __read_passwd_file():
- if passwd_file:
- passwd = open(passwd_file, 'r')
- else:
- raise KeyError('>> no password database <<')
- uidx = {}
- namx = {}
- sep = None
- while True:
- entry = passwd.readline().strip()
- if len(entry) > 6:
- if sep is None:
- sep = __get_field_sep(entry)
- fields = entry.split(sep)
- for i in (2, 3):
- fields[i] = int(fields[i])
- for i in (5, 6):
- fields[i] = __field_sep[sep](fields[i])
- record = Passwd(*fields)
- if fields[2] not in uidx:
- uidx[fields[2]] = record
- if fields[0] not in namx:
- namx[fields[0]] = record
- elif len(entry) > 0:
- pass # skip empty or malformed records
- else:
- break
- passwd.close()
- if len(uidx) == 0:
- raise KeyError
- return (uidx, namx)
-
-# return the passwd database entry by UID
-def getpwuid(uid):
- u, n = __read_passwd_file()
- return u[uid]
-
-# return the passwd database entry by passwd name
-def getpwnam(name):
- u, n = __read_passwd_file()
- return n[name]
-
-# return all the passwd database entries
-def getpwall():
- u, n = __read_passwd_file()
- return n.values()
-
-# test harness
-if __name__ == '__main__':
- getpwall()
diff --git a/Lib/plat-os2emx/regen b/Lib/plat-os2emx/regen
deleted file mode 100755
index 3ecd2a8..0000000
--- a/Lib/plat-os2emx/regen
+++ /dev/null
@@ -1,7 +0,0 @@
-#! /bin/sh
-export INCLUDE=$C_INCLUDE_PATH
-set -v
-python.exe ../../Tools/scripts/h2py.py $C_INCLUDE_PATH/fcntl.h
-python.exe ../../Tools/scripts/h2py.py $C_INCLUDE_PATH/sys/socket.h
-python.exe ../../Tools/scripts/h2py.py -i '(u_long)' $C_INCLUDE_PATH/netinet/in.h
-#python.exe ../../Tools/scripts/h2py.py $C_INCLUDE_PATH/termios.h
diff --git a/Lib/platform.py b/Lib/platform.py
index 030ef2a..c4ffe95 100755
--- a/Lib/platform.py
+++ b/Lib/platform.py
@@ -122,7 +122,7 @@ try:
except AttributeError:
# os.devnull was added in Python 2.4, so emulate it for earlier
# Python versions
- if sys.platform in ('dos','win32','win16','os2'):
+ if sys.platform in ('dos', 'win32', 'win16'):
# Use the old CP/M NUL as device name
DEV_NULL = 'NUL'
else:
@@ -141,7 +141,7 @@ _libc_search = re.compile(b'(__libc_init)'
b'|'
br'(libc(_\w+)?\.so(?:\.(\d[0-9.]*))?)', re.ASCII)
-def libc_ver(executable=sys.executable,lib='',version='',
+def libc_ver(executable=sys.executable, lib='', version='',
chunksize=16384):
@@ -163,12 +163,12 @@ def libc_ver(executable=sys.executable,lib='',version='',
# here to work around problems with Cygwin not being
# able to open symlinks for reading
executable = os.path.realpath(executable)
- f = open(executable,'rb')
+ f = open(executable, 'rb')
binary = f.read(chunksize)
pos = 0
while 1:
if b'libc' in binary or b'GLIBC' in binary:
- m = _libc_search.search(binary,pos)
+ m = _libc_search.search(binary, pos)
else:
m = None
if not m:
@@ -177,7 +177,7 @@ def libc_ver(executable=sys.executable,lib='',version='',
break
pos = 0
continue
- libcinit,glibc,glibcversion,so,threads,soversion = [
+ libcinit, glibc, glibcversion, so, threads, soversion = [
s.decode('latin1') if s is not None else s
for s in m.groups()]
if libcinit and not lib:
@@ -197,9 +197,9 @@ def libc_ver(executable=sys.executable,lib='',version='',
version = version + threads
pos = m.end()
f.close()
- return lib,version
+ return lib, version
-def _dist_try_harder(distname,version,id):
+def _dist_try_harder(distname, version, id):
""" Tries some special tricks to get the distribution
information in case the default method fails.
@@ -214,7 +214,7 @@ def _dist_try_harder(distname,version,id):
for line in open('/var/adm/inst-log/info'):
tv = line.split()
if len(tv) == 2:
- tag,value = tv
+ tag, value = tv
else:
continue
if tag == 'MIN_DIST_VERSION':
@@ -222,7 +222,7 @@ def _dist_try_harder(distname,version,id):
elif tag == 'DIST_IDENT':
values = value.split('-')
id = values[2]
- return distname,version,id
+ return distname, version, id
if os.path.exists('/etc/.installed'):
# Caldera OpenLinux has some infos in that file (thanks to Colin Kong)
@@ -231,7 +231,7 @@ def _dist_try_harder(distname,version,id):
if len(pkg) >= 2 and pkg[0] == 'OpenLinux':
# XXX does Caldera support non Intel platforms ? If yes,
# where can we find the needed id ?
- return 'OpenLinux',pkg[1],id
+ return 'OpenLinux', pkg[1], id
if os.path.isdir('/usr/lib/setup'):
# Check for slackware version tag file (thanks to Greg Andruk)
@@ -243,9 +243,9 @@ def _dist_try_harder(distname,version,id):
verfiles.sort()
distname = 'slackware'
version = verfiles[-1][14:]
- return distname,version,id
+ return distname, version, id
- return distname,version,id
+ return distname, version, id
_release_filename = re.compile(r'(\w+)[-_](release|version)', re.ASCII)
_lsb_release_version = re.compile(r'(.+)'
@@ -314,25 +314,25 @@ def linux_distribution(distname='', version='', id='',
distribution read from the OS is returned. Otherwise the short
name taken from supported_dists is used.
- Returns a tuple (distname,version,id) which default to the
+ Returns a tuple (distname, version, id) which default to the
args given as parameters.
"""
try:
etc = os.listdir(_UNIXCONFDIR)
- except os.error:
+ except OSError:
# Probably not a Unix system
- return distname,version,id
+ return distname, version, id
etc.sort()
for file in etc:
m = _release_filename.match(file)
if m is not None:
- _distname,dummy = m.groups()
+ _distname, dummy = m.groups()
if _distname in supported_dists:
distname = _distname
break
else:
- return _dist_try_harder(distname,version,id)
+ return _dist_try_harder(distname, version, id)
# Read the first line
with open(os.path.join(_UNIXCONFDIR, file), 'r',
@@ -350,7 +350,7 @@ def linux_distribution(distname='', version='', id='',
# To maintain backwards compatibility:
-def dist(distname='',version='',id='',
+def dist(distname='', version='', id='',
supported_dists=_supported_dists):
@@ -360,7 +360,7 @@ def dist(distname='',version='',id='',
/etc and then reverts to _dist_try_harder() in case no
suitable files are found.
- Returns a tuple (distname,version,id) which default to the
+ Returns a tuple (distname, version, id) which default to the
args given as parameters.
"""
@@ -385,11 +385,11 @@ def _norm_version(version, build=''):
if build:
l.append(build)
try:
- ints = map(int,l)
+ ints = map(int, l)
except ValueError:
strings = l
else:
- strings = list(map(str,ints))
+ strings = list(map(str, ints))
version = '.'.join(strings[:3])
return version
@@ -408,46 +408,43 @@ _ver_output = re.compile(r'(?:([\w ]+) ([\w.]+) '
def _syscmd_ver(system='', release='', version='',
- supported_platforms=('win32','win16','dos','os2')):
+ supported_platforms=('win32', 'win16', 'dos')):
""" Tries to figure out the OS version used and returns
- a tuple (system,release,version).
+ a tuple (system, release, version).
It uses the "ver" shell command for this which is known
- to exists on Windows, DOS and OS/2. XXX Others too ?
+ to exists on Windows, DOS. XXX Others too ?
In case this fails, the given parameters are used as
defaults.
"""
if sys.platform not in supported_platforms:
- return system,release,version
+ return system, release, version
# Try some common cmd strings
- for cmd in ('ver','command /c ver','cmd /c ver'):
+ for cmd in ('ver', 'command /c ver', 'cmd /c ver'):
try:
pipe = popen(cmd)
info = pipe.read()
if pipe.close():
- raise os.error('command failed')
+ raise OSError('command failed')
# XXX How can I suppress shell errors from being written
# to stderr ?
- except os.error as why:
- #print 'Command %s failed: %s' % (cmd,why)
- continue
- except IOError as why:
- #print 'Command %s failed: %s' % (cmd,why)
+ except OSError as why:
+ #print 'Command %s failed: %s' % (cmd, why)
continue
else:
break
else:
- return system,release,version
+ return system, release, version
# Parse the output
info = info.strip()
m = _ver_output.match(info)
if m is not None:
- system,release,version = m.groups()
+ system, release, version = m.groups()
# Strip trailing dots from version and release
if release[-1] == '.':
release = release[:-1]
@@ -456,9 +453,9 @@ def _syscmd_ver(system='', release='', version='',
# Normalize the version and build strings (eliminating additional
# zeros)
version = _norm_version(version)
- return system,release,version
+ return system, release, version
-def _win32_getvalue(key,name,default=''):
+def _win32_getvalue(key, name, default=''):
""" Read a value for name from the registry key.
@@ -473,14 +470,14 @@ def _win32_getvalue(key,name,default=''):
import winreg
RegQueryValueEx = winreg.QueryValueEx
try:
- return RegQueryValueEx(key,name)
+ return RegQueryValueEx(key, name)
except:
return default
-def win32_ver(release='',version='',csd='',ptype=''):
+def win32_ver(release='', version='', csd='', ptype=''):
""" Get additional version information from the Windows Registry
- and return a tuple (version,csd,ptype) referring to version
+ and return a tuple (version, csd, ptype) referring to version
number, CSD level (service pack), and OS type (multi/single
processor).
@@ -506,7 +503,6 @@ def win32_ver(release='',version='',csd='',ptype=''):
# Import the needed APIs
try:
- import win32api
from win32api import RegQueryValueEx, RegOpenKeyEx, \
RegCloseKey, GetVersionEx
from win32con import HKEY_LOCAL_MACHINE, VER_PLATFORM_WIN32_NT, \
@@ -517,7 +513,7 @@ def win32_ver(release='',version='',csd='',ptype=''):
sys.getwindowsversion
except AttributeError:
# No emulation possible, so return the defaults...
- return release,version,csd,ptype
+ return release, version, csd, ptype
else:
# Emulation using winreg (added in Python 2.0) and
# sys.getwindowsversion() (added in Python 2.3)
@@ -535,8 +531,8 @@ def win32_ver(release='',version='',csd='',ptype=''):
# Find out the registry key and some general version infos
winver = GetVersionEx()
- maj,min,buildno,plat,csd = winver
- version = '%i.%i.%i' % (maj,min,buildno & 0xFFFF)
+ maj, min, buildno, plat, csd = winver
+ version = '%i.%i.%i' % (maj, min, buildno & 0xFFFF)
if hasattr(winver, "service_pack"):
if winver.service_pack != "":
csd = 'SP%s' % winver.service_pack_major
@@ -586,7 +582,7 @@ def win32_ver(release='',version='',csd='',ptype=''):
# Discard any type that isn't REG_SZ
if type == REG_SZ and name.find("Server") != -1:
product_type = VER_NT_SERVER
- except WindowsError:
+ except OSError:
# Use default of VER_NT_WORKSTATION
pass
@@ -611,8 +607,8 @@ def win32_ver(release='',version='',csd='',ptype=''):
else:
if not release:
# E.g. Win3.1 with win32s
- release = '%i.%i' % (maj,min)
- return release,version,csd,ptype
+ release = '%i.%i' % (maj, min)
+ return release, version, csd, ptype
# Open the registry key
try:
@@ -620,7 +616,7 @@ def win32_ver(release='',version='',csd='',ptype=''):
# Get a value to make sure the key exists...
RegQueryValueEx(keyCurVer, 'SystemRoot')
except:
- return release,version,csd,ptype
+ return release, version, csd, ptype
# Parse values
#subversion = _win32_getvalue(keyCurVer,
@@ -630,73 +626,17 @@ def win32_ver(release='',version='',csd='',ptype=''):
# release = release + subversion # 95a, 95b, etc.
build = _win32_getvalue(keyCurVer,
'CurrentBuildNumber',
- ('',1))[0]
+ ('', 1))[0]
ptype = _win32_getvalue(keyCurVer,
'CurrentType',
- (ptype,1))[0]
+ (ptype, 1))[0]
# Normalize version
- version = _norm_version(version,build)
+ version = _norm_version(version, build)
# Close key
RegCloseKey(keyCurVer)
- return release,version,csd,ptype
-
-def _mac_ver_lookup(selectors,default=None):
-
- from _gestalt import gestalt
- l = []
- append = l.append
- for selector in selectors:
- try:
- append(gestalt(selector))
- except (RuntimeError, OSError):
- append(default)
- return l
-
-def _bcd2str(bcd):
-
- return hex(bcd)[2:]
-
-def _mac_ver_gestalt():
- """
- Thanks to Mark R. Levinson for mailing documentation links and
- code examples for this function. Documentation for the
- gestalt() API is available online at:
-
- http://www.rgaros.nl/gestalt/
- """
- # Check whether the version info module is available
- try:
- import _gestalt
- except ImportError:
- return None
- # Get the infos
- sysv, sysa = _mac_ver_lookup(('sysv','sysa'))
- # Decode the infos
- if sysv:
- major = (sysv & 0xFF00) >> 8
- minor = (sysv & 0x00F0) >> 4
- patch = (sysv & 0x000F)
-
- if (major, minor) >= (10, 4):
- # the 'sysv' gestald cannot return patchlevels
- # higher than 9. Apple introduced 3 new
- # gestalt codes in 10.4 to deal with this
- # issue (needed because patch levels can
- # run higher than 9, such as 10.4.11)
- major,minor,patch = _mac_ver_lookup(('sys1','sys2','sys3'))
- release = '%i.%i.%i' %(major, minor, patch)
- else:
- release = '%s.%i.%i' % (_bcd2str(major),minor,patch)
-
- if sysa:
- machine = {0x1: '68k',
- 0x2: 'PowerPC',
- 0xa: 'i386'}.get(sysa,'')
-
- versioninfo=('', '', '')
- return release,versioninfo,machine
+ return release, version, csd, ptype
def _mac_ver_xml():
fn = '/System/Library/CoreServices/SystemVersion.plist'
@@ -708,18 +648,19 @@ def _mac_ver_xml():
except ImportError:
return None
- pl = plistlib.readPlist(fn)
+ with open(fn, 'rb') as f:
+ pl = plistlib.load(f)
release = pl['ProductVersion']
- versioninfo=('', '', '')
+ versioninfo = ('', '', '')
machine = os.uname().machine
if machine in ('ppc', 'Power Macintosh'):
- # for compatibility with the gestalt based code
+ # Canonical name
machine = 'PowerPC'
- return release,versioninfo,machine
+ return release, versioninfo, machine
-def mac_ver(release='',versioninfo=('','',''),machine=''):
+def mac_ver(release='', versioninfo=('', '', ''), machine=''):
""" Get MacOS version information and return it as tuple (release,
versioninfo, machine) with versioninfo being a tuple (version,
@@ -735,16 +676,10 @@ def mac_ver(release='',versioninfo=('','',''),machine=''):
if info is not None:
return info
- # If that doesn't work for some reason fall back to reading the
- # information using gestalt calls.
- info = _mac_ver_gestalt()
- if info is not None:
- return info
-
# If that also doesn't work return the default values
- return release,versioninfo,machine
+ return release, versioninfo, machine
-def _java_getprop(name,default):
+def _java_getprop(name, default):
from java.lang import System
try:
@@ -755,13 +690,13 @@ def _java_getprop(name,default):
except AttributeError:
return default
-def java_ver(release='',vendor='',vminfo=('','',''),osinfo=('','','')):
+def java_ver(release='', vendor='', vminfo=('', '', ''), osinfo=('', '', '')):
""" Version interface for Jython.
- Returns a tuple (release,vendor,vminfo,osinfo) with vminfo being
- a tuple (vm_name,vm_release,vm_vendor) and osinfo being a
- tuple (os_name,os_version,os_arch).
+ Returns a tuple (release, vendor, vminfo, osinfo) with vminfo being
+ a tuple (vm_name, vm_release, vm_vendor) and osinfo being a
+ tuple (os_name, os_version, os_arch).
Values which cannot be determined are set to the defaults
given as parameters (which all default to '').
@@ -771,7 +706,7 @@ def java_ver(release='',vendor='',vminfo=('','',''),osinfo=('','','')):
try:
import java.lang
except ImportError:
- return release,vendor,vminfo,osinfo
+ return release, vendor, vminfo, osinfo
vendor = _java_getprop('java.vendor', vendor)
release = _java_getprop('java.version', release)
@@ -790,9 +725,9 @@ def java_ver(release='',vendor='',vminfo=('','',''),osinfo=('','','')):
### System name aliasing
-def system_alias(system,release,version):
+def system_alias(system, release, version):
- """ Returns (system,release,version) aliased to common
+ """ Returns (system, release, version) aliased to common
marketing names used for some systems.
It also does some reordering of the information in some cases
@@ -802,13 +737,13 @@ def system_alias(system,release,version):
if system == 'Rhapsody':
# Apple's BSD derivative
# XXX How can we determine the marketing release number ?
- return 'MacOS X Server',system+release,version
+ return 'MacOS X Server', system+release, version
elif system == 'SunOS':
# Sun's OS
if release < '5':
# These releases use the old name SunOS
- return system,release,version
+ return system, release, version
# Modify release (marketing release = SunOS release - 3)
l = release.split('.')
if l:
@@ -836,11 +771,11 @@ def system_alias(system,release,version):
else:
version = '64bit'
- elif system in ('win32','win16'):
+ elif system in ('win32', 'win16'):
# In case one of the other tricks
system = 'Windows'
- return system,release,version
+ return system, release, version
### Various internal helpers
@@ -853,21 +788,21 @@ def _platform(*args):
platform = '-'.join(x.strip() for x in filter(len, args))
# Cleanup some possible filename obstacles...
- platform = platform.replace(' ','_')
- platform = platform.replace('/','-')
- platform = platform.replace('\\','-')
- platform = platform.replace(':','-')
- platform = platform.replace(';','-')
- platform = platform.replace('"','-')
- platform = platform.replace('(','-')
- platform = platform.replace(')','-')
+ platform = platform.replace(' ', '_')
+ platform = platform.replace('/', '-')
+ platform = platform.replace('\\', '-')
+ platform = platform.replace(':', '-')
+ platform = platform.replace(';', '-')
+ platform = platform.replace('"', '-')
+ platform = platform.replace('(', '-')
+ platform = platform.replace(')', '-')
# No need to report 'unknown' information...
- platform = platform.replace('unknown','')
+ platform = platform.replace('unknown', '')
# Fold '--'s and remove trailing '-'
while 1:
- cleaned = platform.replace('--','-')
+ cleaned = platform.replace('--', '-')
if cleaned == platform:
break
platform = cleaned
@@ -887,7 +822,7 @@ def _node(default=''):
return default
try:
return socket.gethostname()
- except socket.error:
+ except OSError:
# Still not working...
return default
@@ -899,19 +834,19 @@ def _follow_symlinks(filepath):
filepath = os.path.abspath(filepath)
while os.path.islink(filepath):
filepath = os.path.normpath(
- os.path.join(os.path.dirname(filepath),os.readlink(filepath)))
+ os.path.join(os.path.dirname(filepath), os.readlink(filepath)))
return filepath
-def _syscmd_uname(option,default=''):
+def _syscmd_uname(option, default=''):
""" Interface to the system's uname command.
"""
- if sys.platform in ('dos','win32','win16','os2'):
+ if sys.platform in ('dos', 'win32', 'win16'):
# XXX Others too ?
return default
try:
f = os.popen('uname %s 2> %s' % (option, DEV_NULL))
- except (AttributeError,os.error):
+ except (AttributeError, OSError):
return default
output = f.read().strip()
rc = f.close()
@@ -920,7 +855,7 @@ def _syscmd_uname(option,default=''):
else:
return output
-def _syscmd_file(target,default=''):
+def _syscmd_file(target, default=''):
""" Interface to the system's file command.
@@ -929,7 +864,7 @@ def _syscmd_file(target,default=''):
default in case the command should fail.
"""
- if sys.platform in ('dos','win32','win16','os2'):
+ if sys.platform in ('dos', 'win32', 'win16'):
# XXX Others too ?
return default
target = _follow_symlinks(target)
@@ -937,7 +872,7 @@ def _syscmd_file(target,default=''):
proc = subprocess.Popen(['file', target],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- except (AttributeError,os.error):
+ except (AttributeError, OSError):
return default
output = proc.communicate()[0].decode('latin-1')
rc = proc.wait()
@@ -951,17 +886,17 @@ def _syscmd_file(target,default=''):
# Default values for architecture; non-empty strings override the
# defaults given as parameters
_default_architecture = {
- 'win32': ('','WindowsPE'),
- 'win16': ('','Windows'),
- 'dos': ('','MSDOS'),
+ 'win32': ('', 'WindowsPE'),
+ 'win16': ('', 'Windows'),
+ 'dos': ('', 'MSDOS'),
}
-def architecture(executable=sys.executable,bits='',linkage=''):
+def architecture(executable=sys.executable, bits='', linkage=''):
""" Queries the given executable (defaults to the Python interpreter
binary) for various architecture information.
- Returns a tuple (bits,linkage) which contains information about
+ Returns a tuple (bits, linkage) which contains information about
the bit architecture and the linkage format used for the
executable. Both values are returned as strings.
@@ -999,16 +934,16 @@ def architecture(executable=sys.executable,bits='',linkage=''):
# "file" command did not return anything; we'll try to provide
# some sensible defaults then...
if sys.platform in _default_architecture:
- b,l = _default_architecture[sys.platform]
+ b, l = _default_architecture[sys.platform]
if b:
bits = b
if l:
linkage = l
- return bits,linkage
+ return bits, linkage
if 'executable' not in fileout:
# Format not supported
- return bits,linkage
+ return bits, linkage
# Bits
if '32-bit' in fileout:
@@ -1036,7 +971,7 @@ def architecture(executable=sys.executable,bits='',linkage=''):
# XXX the A.OUT format also falls under this class...
pass
- return bits,linkage
+ return bits, linkage
### Portable uname() interface
@@ -1048,7 +983,7 @@ _uname_cache = None
def uname():
""" Fairly portable uname interface. Returns a tuple
- of strings (system,node,release,version,machine,processor)
+ of strings (system, node, release, version, machine, processor)
identifying the underlying platform.
Note that unlike the os.uname function this also returns
@@ -1067,7 +1002,7 @@ def uname():
# Get some infos from the builtin os.uname API...
try:
- system,node,release,version,machine = os.uname()
+ system, node, release, version, machine = os.uname()
except AttributeError:
no_os_uname = 1
@@ -1085,7 +1020,7 @@ def uname():
# Try win32_ver() on win32 platforms
if system == 'win32':
- release,version,csd,ptype = win32_ver()
+ release, version, csd, ptype = win32_ver()
if release and version:
use_syscmd_ver = 0
# Try to use the PROCESSOR_* environment variables
@@ -1104,7 +1039,7 @@ def uname():
# Try the 'ver' system command available on some
# platforms
if use_syscmd_ver:
- system,release,version = _syscmd_ver(system)
+ system, release, version = _syscmd_ver(system)
# Normalize system to what win32_ver() normally returns
# (_syscmd_ver() tends to return the vendor name as well)
if system == 'Microsoft Windows':
@@ -1122,7 +1057,7 @@ def uname():
# In case we still don't know anything useful, we'll try to
# help ourselves
- if system in ('win32','win16'):
+ if system in ('win32', 'win16'):
if not version:
if system == 'win32':
version = '32bit'
@@ -1131,7 +1066,7 @@ def uname():
system = 'Windows'
elif system[:4] == 'java':
- release,vendor,vminfo,osinfo = java_ver()
+ release, vendor, vminfo, osinfo = java_ver()
system = 'Java'
version = ', '.join(vminfo)
if not version:
@@ -1149,14 +1084,14 @@ def uname():
except ImportError:
pass
else:
- csid, cpu_number = vms_lib.getsyi('SYI$_CPU',0)
+ csid, cpu_number = vms_lib.getsyi('SYI$_CPU', 0)
if (cpu_number >= 128):
processor = 'Alpha'
else:
processor = 'VAX'
if not processor:
# Get processor information from the uname system command
- processor = _syscmd_uname('-p','')
+ processor = _syscmd_uname('-p', '')
#If any unknowns still exist, replace them with ''s, which are more portable
if system == 'unknown':
@@ -1177,7 +1112,8 @@ def uname():
system = 'Windows'
release = 'Vista'
- _uname_cache = uname_result(system,node,release,version,machine,processor)
+ _uname_cache = uname_result(system, node, release, version,
+ machine, processor)
return _uname_cache
### Direct interfaces to some of the uname() return values
@@ -1474,57 +1410,58 @@ def platform(aliased=0, terse=0):
# Get uname information and then apply platform specific cosmetics
# to it...
- system,node,release,version,machine,processor = uname()
+ system, node, release, version, machine, processor = uname()
if machine == processor:
processor = ''
if aliased:
- system,release,version = system_alias(system,release,version)
+ system, release, version = system_alias(system, release, version)
if system == 'Windows':
# MS platforms
- rel,vers,csd,ptype = win32_ver(version)
+ rel, vers, csd, ptype = win32_ver(version)
if terse:
- platform = _platform(system,release)
+ platform = _platform(system, release)
else:
- platform = _platform(system,release,version,csd)
+ platform = _platform(system, release, version, csd)
elif system in ('Linux',):
# Linux based systems
- distname,distversion,distid = dist('')
+ distname, distversion, distid = dist('')
if distname and not terse:
- platform = _platform(system,release,machine,processor,
+ platform = _platform(system, release, machine, processor,
'with',
- distname,distversion,distid)
+ distname, distversion, distid)
else:
# If the distribution name is unknown check for libc vs. glibc
- libcname,libcversion = libc_ver(sys.executable)
- platform = _platform(system,release,machine,processor,
+ libcname, libcversion = libc_ver(sys.executable)
+ platform = _platform(system, release, machine, processor,
'with',
libcname+libcversion)
elif system == 'Java':
# Java platforms
- r,v,vminfo,(os_name,os_version,os_arch) = java_ver()
+ r, v, vminfo, (os_name, os_version, os_arch) = java_ver()
if terse or not os_name:
- platform = _platform(system,release,version)
+ platform = _platform(system, release, version)
else:
- platform = _platform(system,release,version,
+ platform = _platform(system, release, version,
'on',
- os_name,os_version,os_arch)
+ os_name, os_version, os_arch)
elif system == 'MacOS':
# MacOS platforms
if terse:
- platform = _platform(system,release)
+ platform = _platform(system, release)
else:
- platform = _platform(system,release,machine)
+ platform = _platform(system, release, machine)
else:
# Generic handler
if terse:
- platform = _platform(system,release)
+ platform = _platform(system, release)
else:
- bits,linkage = architecture(sys.executable)
- platform = _platform(system,release,machine,processor,bits,linkage)
+ bits, linkage = architecture(sys.executable)
+ platform = _platform(system, release, machine,
+ processor, bits, linkage)
_platform_cache[(aliased, terse)] = platform
return platform
@@ -1535,5 +1472,5 @@ if __name__ == '__main__':
# Default is to print the aliased verbose platform string
terse = ('terse' in sys.argv or '--terse' in sys.argv)
aliased = (not 'nonaliased' in sys.argv and not '--nonaliased' in sys.argv)
- print(platform(aliased,terse))
+ print(platform(aliased, terse))
sys.exit(0)
diff --git a/Lib/plistlib.py b/Lib/plistlib.py
index 2b0b634..dcb0f9c 100644
--- a/Lib/plistlib.py
+++ b/Lib/plistlib.py
@@ -4,25 +4,20 @@ The property list (.plist) file format is a simple XML pickle supporting
basic object types, like dictionaries, lists, numbers and strings.
Usually the top level object is a dictionary.
-To write out a plist file, use the writePlist(rootObject, pathOrFile)
-function. 'rootObject' is the top level object, 'pathOrFile' is a
-filename or a (writable) file object.
+To write out a plist file, use the dump(value, file)
+function. 'value' is the top level object, 'file' is
+a (writable) file object.
-To parse a plist from a file, use the readPlist(pathOrFile) function,
-with a file name or a (readable) file object as the only argument. It
+To parse a plist from a file, use the load(file) function,
+with a (readable) file object as the only argument. It
returns the top level object (again, usually a dictionary).
-To work with plist data in bytes objects, you can use readPlistFromBytes()
-and writePlistToBytes().
+To work with plist data in bytes objects, you can use loads()
+and dumps().
Values can be strings, integers, floats, booleans, tuples, lists,
-dictionaries (but only with string keys), Data or datetime.datetime objects.
-String values (including dictionary keys) have to be unicode strings -- they
-will be written out as UTF-8.
-
-The <data> plist type is supported through the Data class. This is a
-thin wrapper around a Python bytes object. Use 'Data' if your strings
-contain control characters.
+dictionaries (but only with string keys), Data, bytes, bytearray, or
+datetime.datetime objects.
Generate Plist example:
@@ -37,226 +32,48 @@ Generate Plist example:
aTrueValue = True,
aFalseValue = False,
),
- someData = Data(b"<binary gunk>"),
- someMoreData = Data(b"<lots of binary gunk>" * 10),
+ someData = b"<binary gunk>",
+ someMoreData = b"<lots of binary gunk>" * 10,
aDate = datetime.datetime.fromtimestamp(time.mktime(time.gmtime())),
)
- writePlist(pl, fileName)
+ with open(fileName, 'wb') as fp:
+ dump(pl, fp)
Parse Plist example:
- pl = readPlist(pathOrFile)
- print pl["aKey"]
+ with open(fileName, 'rb') as fp:
+ pl = load(fp)
+ print(pl["aKey"])
"""
-
-
__all__ = [
"readPlist", "writePlist", "readPlistFromBytes", "writePlistToBytes",
- "Plist", "Data", "Dict"
+ "Plist", "Data", "Dict", "FMT_XML", "FMT_BINARY",
+ "load", "dump", "loads", "dumps"
]
-# Note: the Plist and Dict classes have been deprecated.
import binascii
+import codecs
+import contextlib
import datetime
+import enum
from io import BytesIO
+import itertools
+import os
import re
+import struct
+from warnings import warn
+from xml.parsers.expat import ParserCreate
-def readPlist(pathOrFile):
- """Read a .plist file. 'pathOrFile' may either be a file name or a
- (readable) file object. Return the unpacked root object (which
- usually is a dictionary).
- """
- didOpen = False
- try:
- if isinstance(pathOrFile, str):
- pathOrFile = open(pathOrFile, 'rb')
- didOpen = True
- p = PlistParser()
- rootObject = p.parse(pathOrFile)
- finally:
- if didOpen:
- pathOrFile.close()
- return rootObject
-
-
-def writePlist(rootObject, pathOrFile):
- """Write 'rootObject' to a .plist file. 'pathOrFile' may either be a
- file name or a (writable) file object.
- """
- didOpen = False
- try:
- if isinstance(pathOrFile, str):
- pathOrFile = open(pathOrFile, 'wb')
- didOpen = True
- writer = PlistWriter(pathOrFile)
- writer.writeln("<plist version=\"1.0\">")
- writer.writeValue(rootObject)
- writer.writeln("</plist>")
- finally:
- if didOpen:
- pathOrFile.close()
-
-
-def readPlistFromBytes(data):
- """Read a plist data from a bytes object. Return the root object.
- """
- return readPlist(BytesIO(data))
-
-
-def writePlistToBytes(rootObject):
- """Return 'rootObject' as a plist-formatted bytes object.
- """
- f = BytesIO()
- writePlist(rootObject, f)
- return f.getvalue()
-
-
-class DumbXMLWriter:
- def __init__(self, file, indentLevel=0, indent="\t"):
- self.file = file
- self.stack = []
- self.indentLevel = indentLevel
- self.indent = indent
-
- def beginElement(self, element):
- self.stack.append(element)
- self.writeln("<%s>" % element)
- self.indentLevel += 1
-
- def endElement(self, element):
- assert self.indentLevel > 0
- assert self.stack.pop() == element
- self.indentLevel -= 1
- self.writeln("</%s>" % element)
-
- def simpleElement(self, element, value=None):
- if value is not None:
- value = _escape(value)
- self.writeln("<%s>%s</%s>" % (element, value, element))
- else:
- self.writeln("<%s/>" % element)
-
- def writeln(self, line):
- if line:
- # plist has fixed encoding of utf-8
- if isinstance(line, str):
- line = line.encode('utf-8')
- self.file.write(self.indentLevel * self.indent)
- self.file.write(line)
- self.file.write(b'\n')
-
-
-# Contents should conform to a subset of ISO 8601
-# (in particular, YYYY '-' MM '-' DD 'T' HH ':' MM ':' SS 'Z'. Smaller units may be omitted with
-# a loss of precision)
-_dateParser = re.compile(r"(?P<year>\d\d\d\d)(?:-(?P<month>\d\d)(?:-(?P<day>\d\d)(?:T(?P<hour>\d\d)(?::(?P<minute>\d\d)(?::(?P<second>\d\d))?)?)?)?)?Z", re.ASCII)
-
-def _dateFromString(s):
- order = ('year', 'month', 'day', 'hour', 'minute', 'second')
- gd = _dateParser.match(s).groupdict()
- lst = []
- for key in order:
- val = gd[key]
- if val is None:
- break
- lst.append(int(val))
- return datetime.datetime(*lst)
-
-def _dateToString(d):
- return '%04d-%02d-%02dT%02d:%02d:%02dZ' % (
- d.year, d.month, d.day,
- d.hour, d.minute, d.second
- )
-
-
-# Regex to find any control chars, except for \t \n and \r
-_controlCharPat = re.compile(
- r"[\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0b\x0c\x0e\x0f"
- r"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f]")
-
-def _escape(text):
- m = _controlCharPat.search(text)
- if m is not None:
- raise ValueError("strings can't contains control characters; "
- "use plistlib.Data instead")
- text = text.replace("\r\n", "\n") # convert DOS line endings
- text = text.replace("\r", "\n") # convert Mac line endings
- text = text.replace("&", "&amp;") # escape '&'
- text = text.replace("<", "&lt;") # escape '<'
- text = text.replace(">", "&gt;") # escape '>'
- return text
-
-
-PLISTHEADER = b"""\
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-"""
-
-class PlistWriter(DumbXMLWriter):
-
- def __init__(self, file, indentLevel=0, indent=b"\t", writeHeader=1):
- if writeHeader:
- file.write(PLISTHEADER)
- DumbXMLWriter.__init__(self, file, indentLevel, indent)
-
- def writeValue(self, value):
- if isinstance(value, str):
- self.simpleElement("string", value)
- elif isinstance(value, bool):
- # must switch for bool before int, as bool is a
- # subclass of int...
- if value:
- self.simpleElement("true")
- else:
- self.simpleElement("false")
- elif isinstance(value, int):
- self.simpleElement("integer", "%d" % value)
- elif isinstance(value, float):
- self.simpleElement("real", repr(value))
- elif isinstance(value, dict):
- self.writeDict(value)
- elif isinstance(value, Data):
- self.writeData(value)
- elif isinstance(value, datetime.datetime):
- self.simpleElement("date", _dateToString(value))
- elif isinstance(value, (tuple, list)):
- self.writeArray(value)
- else:
- raise TypeError("unsupported type: %s" % type(value))
-
- def writeData(self, data):
- self.beginElement("data")
- self.indentLevel -= 1
- maxlinelength = max(16, 76 - len(self.indent.replace(b"\t", b" " * 8) *
- self.indentLevel))
- for line in data.asBase64(maxlinelength).split(b"\n"):
- if line:
- self.writeln(line)
- self.indentLevel += 1
- self.endElement("data")
+PlistFormat = enum.Enum('PlistFormat', 'FMT_XML FMT_BINARY', module=__name__)
+globals().update(PlistFormat.__members__)
- def writeDict(self, d):
- if d:
- self.beginElement("dict")
- items = sorted(d.items())
- for key, value in items:
- if not isinstance(key, str):
- raise TypeError("keys must be strings")
- self.simpleElement("key", key)
- self.writeValue(value)
- self.endElement("dict")
- else:
- self.simpleElement("dict")
- def writeArray(self, array):
- if array:
- self.beginElement("array")
- for value in array:
- self.writeValue(value)
- self.endElement("array")
- else:
- self.simpleElement("array")
+#
+#
+# Deprecated functionality
+#
+#
class _InternalDict(dict):
@@ -264,19 +81,18 @@ class _InternalDict(dict):
# This class is needed while Dict is scheduled for deprecation:
# we only need to warn when a *user* instantiates Dict or when
# the "attribute notation for dict keys" is used.
+ __slots__ = ()
def __getattr__(self, attr):
try:
value = self[attr]
except KeyError:
raise AttributeError(attr)
- from warnings import warn
warn("Attribute access from plist dicts is deprecated, use d[key] "
"notation instead", DeprecationWarning, 2)
return value
def __setattr__(self, attr, value):
- from warnings import warn
warn("Attribute access from plist dicts is deprecated, use d[key] "
"notation instead", DeprecationWarning, 2)
self[attr] = value
@@ -286,56 +102,111 @@ class _InternalDict(dict):
del self[attr]
except KeyError:
raise AttributeError(attr)
- from warnings import warn
warn("Attribute access from plist dicts is deprecated, use d[key] "
"notation instead", DeprecationWarning, 2)
+
class Dict(_InternalDict):
def __init__(self, **kwargs):
- from warnings import warn
warn("The plistlib.Dict class is deprecated, use builtin dict instead",
DeprecationWarning, 2)
super().__init__(**kwargs)
-class Plist(_InternalDict):
+@contextlib.contextmanager
+def _maybe_open(pathOrFile, mode):
+ if isinstance(pathOrFile, str):
+ with open(pathOrFile, mode) as fp:
+ yield fp
+
+ else:
+ yield pathOrFile
+
- """This class has been deprecated. Use readPlist() and writePlist()
+class Plist(_InternalDict):
+ """This class has been deprecated. Use dump() and load()
functions instead, together with regular dict objects.
"""
def __init__(self, **kwargs):
- from warnings import warn
- warn("The Plist class is deprecated, use the readPlist() and "
- "writePlist() functions instead", DeprecationWarning, 2)
+ warn("The Plist class is deprecated, use the load() and "
+ "dump() functions instead", DeprecationWarning, 2)
super().__init__(**kwargs)
+ @classmethod
def fromFile(cls, pathOrFile):
- """Deprecated. Use the readPlist() function instead."""
- rootObject = readPlist(pathOrFile)
+ """Deprecated. Use the load() function instead."""
+ with _maybe_open(pathOrFile, 'rb') as fp:
+ value = load(fp)
plist = cls()
- plist.update(rootObject)
+ plist.update(value)
return plist
- fromFile = classmethod(fromFile)
def write(self, pathOrFile):
- """Deprecated. Use the writePlist() function instead."""
- writePlist(self, pathOrFile)
+ """Deprecated. Use the dump() function instead."""
+ with _maybe_open(pathOrFile, 'wb') as fp:
+ dump(self, fp)
-def _encodeBase64(s, maxlinelength=76):
- # copied from base64.encodebytes(), with added maxlinelength argument
- maxbinsize = (maxlinelength//4)*3
- pieces = []
- for i in range(0, len(s), maxbinsize):
- chunk = s[i : i + maxbinsize]
- pieces.append(binascii.b2a_base64(chunk))
- return b''.join(pieces)
+def readPlist(pathOrFile):
+ """
+ Read a .plist from a path or file. pathOrFile should either
+ be a file name, or a readable binary file object.
+
+ This function is deprecated, use load instead.
+ """
+ warn("The readPlist function is deprecated, use load() instead",
+ DeprecationWarning, 2)
+
+ with _maybe_open(pathOrFile, 'rb') as fp:
+ return load(fp, fmt=None, use_builtin_types=False,
+ dict_type=_InternalDict)
+
+def writePlist(value, pathOrFile):
+ """
+ Write 'value' to a .plist file. 'pathOrFile' may either be a
+ file name or a (writable) file object.
+
+ This function is deprecated, use dump instead.
+ """
+ warn("The writePlist function is deprecated, use dump() instead",
+ DeprecationWarning, 2)
+ with _maybe_open(pathOrFile, 'wb') as fp:
+ dump(value, fp, fmt=FMT_XML, sort_keys=True, skipkeys=False)
+
+
+def readPlistFromBytes(data):
+ """
+ Read a plist data from a bytes object. Return the root object.
+
+ This function is deprecated, use loads instead.
+ """
+ warn("The readPlistFromBytes function is deprecated, use loads() instead",
+ DeprecationWarning, 2)
+ return load(BytesIO(data), fmt=None, use_builtin_types=False,
+ dict_type=_InternalDict)
+
+
+def writePlistToBytes(value):
+ """
+ Return 'value' as a plist-formatted bytes object.
+
+ This function is deprecated, use dumps instead.
+ """
+ warn("The writePlistToBytes function is deprecated, use dumps() instead",
+ DeprecationWarning, 2)
+ f = BytesIO()
+ dump(value, f, fmt=FMT_XML, sort_keys=True, skipkeys=False)
+ return f.getvalue()
+
class Data:
+ """
+ Wrapper for binary data.
- """Wrapper for binary data."""
+ This class is deprecated, use a bytes object instead.
+ """
def __init__(self, data):
if not isinstance(data, bytes):
@@ -346,10 +217,10 @@ class Data:
def fromBase64(cls, data):
# base64.decodebytes just calls binascii.a2b_base64;
# it seems overkill to use both base64 and binascii.
- return cls(binascii.a2b_base64(data))
+ return cls(_decode_base64(data))
def asBase64(self, maxlinelength=76):
- return _encodeBase64(self.data, maxlinelength)
+ return _encode_base64(self.data, maxlinelength)
def __eq__(self, other):
if isinstance(other, self.__class__):
@@ -362,43 +233,119 @@ class Data:
def __repr__(self):
return "%s(%s)" % (self.__class__.__name__, repr(self.data))
-class PlistParser:
+#
+#
+# End of deprecated functionality
+#
+#
+
+
+#
+# XML support
+#
+
+
+# XML 'header'
+PLISTHEADER = b"""\
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+"""
+
+
+# Regex to find any control chars, except for \t \n and \r
+_controlCharPat = re.compile(
+ r"[\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0b\x0c\x0e\x0f"
+ r"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f]")
+
+def _encode_base64(s, maxlinelength=76):
+ # copied from base64.encodebytes(), with added maxlinelength argument
+ maxbinsize = (maxlinelength//4)*3
+ pieces = []
+ for i in range(0, len(s), maxbinsize):
+ chunk = s[i : i + maxbinsize]
+ pieces.append(binascii.b2a_base64(chunk))
+ return b''.join(pieces)
+
+def _decode_base64(s):
+ if isinstance(s, str):
+ return binascii.a2b_base64(s.encode("utf-8"))
+
+ else:
+ return binascii.a2b_base64(s)
+
+# Contents should conform to a subset of ISO 8601
+# (in particular, YYYY '-' MM '-' DD 'T' HH ':' MM ':' SS 'Z'. Smaller units
+# may be omitted with # a loss of precision)
+_dateParser = re.compile(r"(?P<year>\d\d\d\d)(?:-(?P<month>\d\d)(?:-(?P<day>\d\d)(?:T(?P<hour>\d\d)(?::(?P<minute>\d\d)(?::(?P<second>\d\d))?)?)?)?)?Z", re.ASCII)
+
+
+def _date_from_string(s):
+ order = ('year', 'month', 'day', 'hour', 'minute', 'second')
+ gd = _dateParser.match(s).groupdict()
+ lst = []
+ for key in order:
+ val = gd[key]
+ if val is None:
+ break
+ lst.append(int(val))
+ return datetime.datetime(*lst)
+
+
+def _date_to_string(d):
+ return '%04d-%02d-%02dT%02d:%02d:%02dZ' % (
+ d.year, d.month, d.day,
+ d.hour, d.minute, d.second
+ )
- def __init__(self):
+def _escape(text):
+ m = _controlCharPat.search(text)
+ if m is not None:
+ raise ValueError("strings can't contains control characters; "
+ "use bytes instead")
+ text = text.replace("\r\n", "\n") # convert DOS line endings
+ text = text.replace("\r", "\n") # convert Mac line endings
+ text = text.replace("&", "&amp;") # escape '&'
+ text = text.replace("<", "&lt;") # escape '<'
+ text = text.replace(">", "&gt;") # escape '>'
+ return text
+
+class _PlistParser:
+ def __init__(self, use_builtin_types, dict_type):
self.stack = []
- self.currentKey = None
+ self.current_key = None
self.root = None
+ self._use_builtin_types = use_builtin_types
+ self._dict_type = dict_type
def parse(self, fileobj):
- from xml.parsers.expat import ParserCreate
self.parser = ParserCreate()
- self.parser.StartElementHandler = self.handleBeginElement
- self.parser.EndElementHandler = self.handleEndElement
- self.parser.CharacterDataHandler = self.handleData
+ self.parser.StartElementHandler = self.handle_begin_element
+ self.parser.EndElementHandler = self.handle_end_element
+ self.parser.CharacterDataHandler = self.handle_data
self.parser.ParseFile(fileobj)
return self.root
- def handleBeginElement(self, element, attrs):
+ def handle_begin_element(self, element, attrs):
self.data = []
handler = getattr(self, "begin_" + element, None)
if handler is not None:
handler(attrs)
- def handleEndElement(self, element):
+ def handle_end_element(self, element):
handler = getattr(self, "end_" + element, None)
if handler is not None:
handler()
- def handleData(self, data):
+ def handle_data(self, data):
self.data.append(data)
- def addObject(self, value):
- if self.currentKey is not None:
+ def add_object(self, value):
+ if self.current_key is not None:
if not isinstance(self.stack[-1], type({})):
raise ValueError("unexpected element at line %d" %
self.parser.CurrentLineNumber)
- self.stack[-1][self.currentKey] = value
- self.currentKey = None
+ self.stack[-1][self.current_key] = value
+ self.current_key = None
elif not self.stack:
# this is the root object
self.root = value
@@ -408,7 +355,7 @@ class PlistParser:
self.parser.CurrentLineNumber)
self.stack[-1].append(value)
- def getData(self):
+ def get_data(self):
data = ''.join(self.data)
self.data = []
return data
@@ -416,39 +363,661 @@ class PlistParser:
# element handlers
def begin_dict(self, attrs):
- d = _InternalDict()
- self.addObject(d)
+ d = self._dict_type()
+ self.add_object(d)
self.stack.append(d)
+
def end_dict(self):
- if self.currentKey:
+ if self.current_key:
raise ValueError("missing value for key '%s' at line %d" %
- (self.currentKey,self.parser.CurrentLineNumber))
+ (self.current_key,self.parser.CurrentLineNumber))
self.stack.pop()
def end_key(self):
- if self.currentKey or not isinstance(self.stack[-1], type({})):
+ if self.current_key or not isinstance(self.stack[-1], type({})):
raise ValueError("unexpected key at line %d" %
self.parser.CurrentLineNumber)
- self.currentKey = self.getData()
+ self.current_key = self.get_data()
def begin_array(self, attrs):
a = []
- self.addObject(a)
+ self.add_object(a)
self.stack.append(a)
+
def end_array(self):
self.stack.pop()
def end_true(self):
- self.addObject(True)
+ self.add_object(True)
+
def end_false(self):
- self.addObject(False)
+ self.add_object(False)
+
def end_integer(self):
- self.addObject(int(self.getData()))
+ self.add_object(int(self.get_data()))
+
def end_real(self):
- self.addObject(float(self.getData()))
+ self.add_object(float(self.get_data()))
+
def end_string(self):
- self.addObject(self.getData())
+ self.add_object(self.get_data())
+
def end_data(self):
- self.addObject(Data.fromBase64(self.getData().encode("utf-8")))
+ if self._use_builtin_types:
+ self.add_object(_decode_base64(self.get_data()))
+
+ else:
+ self.add_object(Data.fromBase64(self.get_data()))
+
def end_date(self):
- self.addObject(_dateFromString(self.getData()))
+ self.add_object(_date_from_string(self.get_data()))
+
+
+class _DumbXMLWriter:
+ def __init__(self, file, indent_level=0, indent="\t"):
+ self.file = file
+ self.stack = []
+ self._indent_level = indent_level
+ self.indent = indent
+
+ def begin_element(self, element):
+ self.stack.append(element)
+ self.writeln("<%s>" % element)
+ self._indent_level += 1
+
+ def end_element(self, element):
+ assert self._indent_level > 0
+ assert self.stack.pop() == element
+ self._indent_level -= 1
+ self.writeln("</%s>" % element)
+
+ def simple_element(self, element, value=None):
+ if value is not None:
+ value = _escape(value)
+ self.writeln("<%s>%s</%s>" % (element, value, element))
+
+ else:
+ self.writeln("<%s/>" % element)
+
+ def writeln(self, line):
+ if line:
+ # plist has fixed encoding of utf-8
+
+ # XXX: is this test needed?
+ if isinstance(line, str):
+ line = line.encode('utf-8')
+ self.file.write(self._indent_level * self.indent)
+ self.file.write(line)
+ self.file.write(b'\n')
+
+
+class _PlistWriter(_DumbXMLWriter):
+ def __init__(
+ self, file, indent_level=0, indent=b"\t", writeHeader=1,
+ sort_keys=True, skipkeys=False):
+
+ if writeHeader:
+ file.write(PLISTHEADER)
+ _DumbXMLWriter.__init__(self, file, indent_level, indent)
+ self._sort_keys = sort_keys
+ self._skipkeys = skipkeys
+
+ def write(self, value):
+ self.writeln("<plist version=\"1.0\">")
+ self.write_value(value)
+ self.writeln("</plist>")
+
+ def write_value(self, value):
+ if isinstance(value, str):
+ self.simple_element("string", value)
+
+ elif value is True:
+ self.simple_element("true")
+
+ elif value is False:
+ self.simple_element("false")
+
+ elif isinstance(value, int):
+ if -1 << 63 <= value < 1 << 64:
+ self.simple_element("integer", "%d" % value)
+ else:
+ raise OverflowError(value)
+
+ elif isinstance(value, float):
+ self.simple_element("real", repr(value))
+
+ elif isinstance(value, dict):
+ self.write_dict(value)
+
+ elif isinstance(value, Data):
+ self.write_data(value)
+
+ elif isinstance(value, (bytes, bytearray)):
+ self.write_bytes(value)
+
+ elif isinstance(value, datetime.datetime):
+ self.simple_element("date", _date_to_string(value))
+
+ elif isinstance(value, (tuple, list)):
+ self.write_array(value)
+
+ else:
+ raise TypeError("unsupported type: %s" % type(value))
+
+ def write_data(self, data):
+ self.write_bytes(data.data)
+
+ def write_bytes(self, data):
+ self.begin_element("data")
+ self._indent_level -= 1
+ maxlinelength = max(
+ 16,
+ 76 - len(self.indent.replace(b"\t", b" " * 8) * self._indent_level))
+
+ for line in _encode_base64(data, maxlinelength).split(b"\n"):
+ if line:
+ self.writeln(line)
+ self._indent_level += 1
+ self.end_element("data")
+
+ def write_dict(self, d):
+ if d:
+ self.begin_element("dict")
+ if self._sort_keys:
+ items = sorted(d.items())
+ else:
+ items = d.items()
+
+ for key, value in items:
+ if not isinstance(key, str):
+ if self._skipkeys:
+ continue
+ raise TypeError("keys must be strings")
+ self.simple_element("key", key)
+ self.write_value(value)
+ self.end_element("dict")
+
+ else:
+ self.simple_element("dict")
+
+ def write_array(self, array):
+ if array:
+ self.begin_element("array")
+ for value in array:
+ self.write_value(value)
+ self.end_element("array")
+
+ else:
+ self.simple_element("array")
+
+
+def _is_fmt_xml(header):
+ prefixes = (b'<?xml', b'<plist')
+
+ for pfx in prefixes:
+ if header.startswith(pfx):
+ return True
+
+ # Also check for alternative XML encodings, this is slightly
+ # overkill because the Apple tools (and plistlib) will not
+ # generate files with these encodings.
+ for bom, encoding in (
+ (codecs.BOM_UTF8, "utf-8"),
+ (codecs.BOM_UTF16_BE, "utf-16-be"),
+ (codecs.BOM_UTF16_LE, "utf-16-le"),
+ # expat does not support utf-32
+ #(codecs.BOM_UTF32_BE, "utf-32-be"),
+ #(codecs.BOM_UTF32_LE, "utf-32-le"),
+ ):
+ if not header.startswith(bom):
+ continue
+
+ for start in prefixes:
+ prefix = bom + start.decode('ascii').encode(encoding)
+ if header[:len(prefix)] == prefix:
+ return True
+
+ return False
+
+#
+# Binary Plist
+#
+
+
+class InvalidFileException (ValueError):
+ def __init__(self, message="Invalid file"):
+ ValueError.__init__(self, message)
+
+_BINARY_FORMAT = {1: 'B', 2: 'H', 4: 'L', 8: 'Q'}
+
+class _BinaryPlistParser:
+ """
+ Read or write a binary plist file, following the description of the binary
+ format. Raise InvalidFileException in case of error, otherwise return the
+ root object.
+
+ see also: http://opensource.apple.com/source/CF/CF-744.18/CFBinaryPList.c
+ """
+ def __init__(self, use_builtin_types, dict_type):
+ self._use_builtin_types = use_builtin_types
+ self._dict_type = dict_type
+
+ def parse(self, fp):
+ try:
+ # The basic file format:
+ # HEADER
+ # object...
+ # refid->offset...
+ # TRAILER
+ self._fp = fp
+ self._fp.seek(-32, os.SEEK_END)
+ trailer = self._fp.read(32)
+ if len(trailer) != 32:
+ raise InvalidFileException()
+ (
+ offset_size, self._ref_size, num_objects, top_object,
+ offset_table_offset
+ ) = struct.unpack('>6xBBQQQ', trailer)
+ self._fp.seek(offset_table_offset)
+ offset_format = '>' + _BINARY_FORMAT[offset_size] * num_objects
+ self._ref_format = _BINARY_FORMAT[self._ref_size]
+ self._object_offsets = struct.unpack(
+ offset_format, self._fp.read(offset_size * num_objects))
+ return self._read_object(self._object_offsets[top_object])
+
+ except (OSError, IndexError, struct.error):
+ raise InvalidFileException()
+
+ def _get_size(self, tokenL):
+ """ return the size of the next object."""
+ if tokenL == 0xF:
+ m = self._fp.read(1)[0] & 0x3
+ s = 1 << m
+ f = '>' + _BINARY_FORMAT[s]
+ return struct.unpack(f, self._fp.read(s))[0]
+
+ return tokenL
+
+ def _read_refs(self, n):
+ return struct.unpack(
+ '>' + self._ref_format * n, self._fp.read(n * self._ref_size))
+
+ def _read_object(self, offset):
+ """
+ read the object at offset.
+
+ May recursively read sub-objects (content of an array/dict/set)
+ """
+ self._fp.seek(offset)
+ token = self._fp.read(1)[0]
+ tokenH, tokenL = token & 0xF0, token & 0x0F
+
+ if token == 0x00:
+ return None
+
+ elif token == 0x08:
+ return False
+
+ elif token == 0x09:
+ return True
+
+ # The referenced source code also mentions URL (0x0c, 0x0d) and
+ # UUID (0x0e), but neither can be generated using the Cocoa libraries.
+
+ elif token == 0x0f:
+ return b''
+
+ elif tokenH == 0x10: # int
+ return int.from_bytes(self._fp.read(1 << tokenL),
+ 'big', signed=tokenL >= 3)
+
+ elif token == 0x22: # real
+ return struct.unpack('>f', self._fp.read(4))[0]
+
+ elif token == 0x23: # real
+ return struct.unpack('>d', self._fp.read(8))[0]
+
+ elif token == 0x33: # date
+ f = struct.unpack('>d', self._fp.read(8))[0]
+ # timestamp 0 of binary plists corresponds to 1/1/2001
+ # (year of Mac OS X 10.0), instead of 1/1/1970.
+ return datetime.datetime.utcfromtimestamp(f + (31 * 365 + 8) * 86400)
+
+ elif tokenH == 0x40: # data
+ s = self._get_size(tokenL)
+ if self._use_builtin_types:
+ return self._fp.read(s)
+ else:
+ return Data(self._fp.read(s))
+
+ elif tokenH == 0x50: # ascii string
+ s = self._get_size(tokenL)
+ result = self._fp.read(s).decode('ascii')
+ return result
+
+ elif tokenH == 0x60: # unicode string
+ s = self._get_size(tokenL)
+ return self._fp.read(s * 2).decode('utf-16be')
+
+ # tokenH == 0x80 is documented as 'UID' and appears to be used for
+ # keyed-archiving, not in plists.
+
+ elif tokenH == 0xA0: # array
+ s = self._get_size(tokenL)
+ obj_refs = self._read_refs(s)
+ return [self._read_object(self._object_offsets[x])
+ for x in obj_refs]
+
+ # tokenH == 0xB0 is documented as 'ordset', but is not actually
+ # implemented in the Apple reference code.
+
+ # tokenH == 0xC0 is documented as 'set', but sets cannot be used in
+ # plists.
+
+ elif tokenH == 0xD0: # dict
+ s = self._get_size(tokenL)
+ key_refs = self._read_refs(s)
+ obj_refs = self._read_refs(s)
+ result = self._dict_type()
+ for k, o in zip(key_refs, obj_refs):
+ result[self._read_object(self._object_offsets[k])
+ ] = self._read_object(self._object_offsets[o])
+ return result
+
+ raise InvalidFileException()
+
+def _count_to_size(count):
+ if count < 1 << 8:
+ return 1
+
+ elif count < 1 << 16:
+ return 2
+
+ elif count << 1 << 32:
+ return 4
+
+ else:
+ return 8
+
+class _BinaryPlistWriter (object):
+ def __init__(self, fp, sort_keys, skipkeys):
+ self._fp = fp
+ self._sort_keys = sort_keys
+ self._skipkeys = skipkeys
+
+ def write(self, value):
+
+ # Flattened object list:
+ self._objlist = []
+
+ # Mappings from object->objectid
+ # First dict has (type(object), object) as the key,
+ # second dict is used when object is not hashable and
+ # has id(object) as the key.
+ self._objtable = {}
+ self._objidtable = {}
+
+ # Create list of all objects in the plist
+ self._flatten(value)
+
+ # Size of object references in serialized containers
+ # depends on the number of objects in the plist.
+ num_objects = len(self._objlist)
+ self._object_offsets = [0]*num_objects
+ self._ref_size = _count_to_size(num_objects)
+
+ self._ref_format = _BINARY_FORMAT[self._ref_size]
+
+ # Write file header
+ self._fp.write(b'bplist00')
+
+ # Write object list
+ for obj in self._objlist:
+ self._write_object(obj)
+
+ # Write refnum->object offset table
+ top_object = self._getrefnum(value)
+ offset_table_offset = self._fp.tell()
+ offset_size = _count_to_size(offset_table_offset)
+ offset_format = '>' + _BINARY_FORMAT[offset_size] * num_objects
+ self._fp.write(struct.pack(offset_format, *self._object_offsets))
+
+ # Write trailer
+ sort_version = 0
+ trailer = (
+ sort_version, offset_size, self._ref_size, num_objects,
+ top_object, offset_table_offset
+ )
+ self._fp.write(struct.pack('>5xBBBQQQ', *trailer))
+
+ def _flatten(self, value):
+ # First check if the object is in the object table, not used for
+ # containers to ensure that two subcontainers with the same contents
+ # will be serialized as distinct values.
+ if isinstance(value, (
+ str, int, float, datetime.datetime, bytes, bytearray)):
+ if (type(value), value) in self._objtable:
+ return
+
+ elif isinstance(value, Data):
+ if (type(value.data), value.data) in self._objtable:
+ return
+
+ # Add to objectreference map
+ refnum = len(self._objlist)
+ self._objlist.append(value)
+ try:
+ if isinstance(value, Data):
+ self._objtable[(type(value.data), value.data)] = refnum
+ else:
+ self._objtable[(type(value), value)] = refnum
+ except TypeError:
+ self._objidtable[id(value)] = refnum
+
+ # And finally recurse into containers
+ if isinstance(value, dict):
+ keys = []
+ values = []
+ items = value.items()
+ if self._sort_keys:
+ items = sorted(items)
+
+ for k, v in items:
+ if not isinstance(k, str):
+ if self._skipkeys:
+ continue
+ raise TypeError("keys must be strings")
+ keys.append(k)
+ values.append(v)
+
+ for o in itertools.chain(keys, values):
+ self._flatten(o)
+
+ elif isinstance(value, (list, tuple)):
+ for o in value:
+ self._flatten(o)
+
+ def _getrefnum(self, value):
+ try:
+ if isinstance(value, Data):
+ return self._objtable[(type(value.data), value.data)]
+ else:
+ return self._objtable[(type(value), value)]
+ except TypeError:
+ return self._objidtable[id(value)]
+
+ def _write_size(self, token, size):
+ if size < 15:
+ self._fp.write(struct.pack('>B', token | size))
+
+ elif size < 1 << 8:
+ self._fp.write(struct.pack('>BBB', token | 0xF, 0x10, size))
+
+ elif size < 1 << 16:
+ self._fp.write(struct.pack('>BBH', token | 0xF, 0x11, size))
+
+ elif size < 1 << 32:
+ self._fp.write(struct.pack('>BBL', token | 0xF, 0x12, size))
+
+ else:
+ self._fp.write(struct.pack('>BBQ', token | 0xF, 0x13, size))
+
+ def _write_object(self, value):
+ ref = self._getrefnum(value)
+ self._object_offsets[ref] = self._fp.tell()
+ if value is None:
+ self._fp.write(b'\x00')
+
+ elif value is False:
+ self._fp.write(b'\x08')
+
+ elif value is True:
+ self._fp.write(b'\x09')
+
+ elif isinstance(value, int):
+ if value < 0:
+ try:
+ self._fp.write(struct.pack('>Bq', 0x13, value))
+ except struct.error:
+ raise OverflowError(value) from None
+ elif value < 1 << 8:
+ self._fp.write(struct.pack('>BB', 0x10, value))
+ elif value < 1 << 16:
+ self._fp.write(struct.pack('>BH', 0x11, value))
+ elif value < 1 << 32:
+ self._fp.write(struct.pack('>BL', 0x12, value))
+ elif value < 1 << 63:
+ self._fp.write(struct.pack('>BQ', 0x13, value))
+ elif value < 1 << 64:
+ self._fp.write(b'\x14' + value.to_bytes(16, 'big', signed=True))
+ else:
+ raise OverflowError(value)
+
+ elif isinstance(value, float):
+ self._fp.write(struct.pack('>Bd', 0x23, value))
+
+ elif isinstance(value, datetime.datetime):
+ f = (value - datetime.datetime(2001, 1, 1)).total_seconds()
+ self._fp.write(struct.pack('>Bd', 0x33, f))
+
+ elif isinstance(value, Data):
+ self._write_size(0x40, len(value.data))
+ self._fp.write(value.data)
+
+ elif isinstance(value, (bytes, bytearray)):
+ self._write_size(0x40, len(value))
+ self._fp.write(value)
+
+ elif isinstance(value, str):
+ try:
+ t = value.encode('ascii')
+ self._write_size(0x50, len(value))
+ except UnicodeEncodeError:
+ t = value.encode('utf-16be')
+ self._write_size(0x60, len(value))
+
+ self._fp.write(t)
+
+ elif isinstance(value, (list, tuple)):
+ refs = [self._getrefnum(o) for o in value]
+ s = len(refs)
+ self._write_size(0xA0, s)
+ self._fp.write(struct.pack('>' + self._ref_format * s, *refs))
+
+ elif isinstance(value, dict):
+ keyRefs, valRefs = [], []
+
+ if self._sort_keys:
+ rootItems = sorted(value.items())
+ else:
+ rootItems = value.items()
+
+ for k, v in rootItems:
+ if not isinstance(k, str):
+ if self._skipkeys:
+ continue
+ raise TypeError("keys must be strings")
+ keyRefs.append(self._getrefnum(k))
+ valRefs.append(self._getrefnum(v))
+
+ s = len(keyRefs)
+ self._write_size(0xD0, s)
+ self._fp.write(struct.pack('>' + self._ref_format * s, *keyRefs))
+ self._fp.write(struct.pack('>' + self._ref_format * s, *valRefs))
+
+ else:
+ raise TypeError(value)
+
+
+def _is_fmt_binary(header):
+ return header[:8] == b'bplist00'
+
+
+#
+# Generic bits
+#
+
+_FORMATS={
+ FMT_XML: dict(
+ detect=_is_fmt_xml,
+ parser=_PlistParser,
+ writer=_PlistWriter,
+ ),
+ FMT_BINARY: dict(
+ detect=_is_fmt_binary,
+ parser=_BinaryPlistParser,
+ writer=_BinaryPlistWriter,
+ )
+}
+
+
+def load(fp, *, fmt=None, use_builtin_types=True, dict_type=dict):
+ """Read a .plist file. 'fp' should be (readable) file object.
+ Return the unpacked root object (which usually is a dictionary).
+ """
+ if fmt is None:
+ header = fp.read(32)
+ fp.seek(0)
+ for info in _FORMATS.values():
+ if info['detect'](header):
+ p = info['parser'](
+ use_builtin_types=use_builtin_types,
+ dict_type=dict_type,
+ )
+ break
+
+ else:
+ raise InvalidFileException()
+
+ else:
+ p = _FORMATS[fmt]['parser'](use_builtin_types=use_builtin_types)
+
+ return p.parse(fp)
+
+
+def loads(value, *, fmt=None, use_builtin_types=True, dict_type=dict):
+ """Read a .plist file from a bytes object.
+ Return the unpacked root object (which usually is a dictionary).
+ """
+ fp = BytesIO(value)
+ return load(
+ fp, fmt=fmt, use_builtin_types=use_builtin_types, dict_type=dict_type)
+
+
+def dump(value, fp, *, fmt=FMT_XML, sort_keys=True, skipkeys=False):
+ """Write 'value' to a .plist file. 'fp' should be a (writable)
+ file object.
+ """
+ if fmt not in _FORMATS:
+ raise ValueError("Unsupported format: %r"%(fmt,))
+
+ writer = _FORMATS[fmt]["writer"](fp, sort_keys=sort_keys, skipkeys=skipkeys)
+ writer.write(value)
+
+
+def dumps(value, *, fmt=FMT_XML, skipkeys=False, sort_keys=True):
+ """Return a bytes object with the contents for a .plist file.
+ """
+ fp = BytesIO()
+ dump(value, fp, fmt=fmt, skipkeys=skipkeys, sort_keys=sort_keys)
+ return fp.getvalue()
diff --git a/Lib/poplib.py b/Lib/poplib.py
index 43f8305..23a3517 100644
--- a/Lib/poplib.py
+++ b/Lib/poplib.py
@@ -13,7 +13,15 @@ Based on the J. Myers POP3 draft, Jan. 96
# Imports
-import re, socket
+import errno
+import re
+import socket
+
+try:
+ import ssl
+ HAVE_SSL = True
+except ImportError:
+ HAVE_SSL = False
__all__ = ["POP3","error_proto"]
@@ -61,6 +69,8 @@ class POP3:
APOP name digest apop(name, digest)
TOP msg n top(msg, n)
UIDL [msg] uidl(msg = None)
+ CAPA capa()
+ STLS stls()
Raises one exception: 'error_proto'.
@@ -87,6 +97,7 @@ class POP3:
timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
self.host = host
self.port = port
+ self._tls_established = False
self.sock = self._create_socket(timeout)
self.file = self.sock.makefile('rb')
self._debugging = 0
@@ -268,7 +279,14 @@ class POP3:
if self.file is not None:
self.file.close()
if self.sock is not None:
- self.sock.close()
+ try:
+ self.sock.shutdown(socket.SHUT_RDWR)
+ except OSError as e:
+ # The server might already have closed the connection
+ if e.errno != errno.ENOTCONN:
+ raise
+ finally:
+ self.sock.close()
self.file = self.sock = None
#__del__ = quit
@@ -324,21 +342,72 @@ class POP3:
return self._shortcmd('UIDL %s' % which)
return self._longcmd('UIDL')
-try:
- import ssl
-except ImportError:
- pass
-else:
+
+ def capa(self):
+ """Return server capabilities (RFC 2449) as a dictionary
+ >>> c=poplib.POP3('localhost')
+ >>> c.capa()
+ {'IMPLEMENTATION': ['Cyrus', 'POP3', 'server', 'v2.2.12'],
+ 'TOP': [], 'LOGIN-DELAY': ['0'], 'AUTH-RESP-CODE': [],
+ 'EXPIRE': ['NEVER'], 'USER': [], 'STLS': [], 'PIPELINING': [],
+ 'UIDL': [], 'RESP-CODES': []}
+ >>>
+
+ Really, according to RFC 2449, the cyrus folks should avoid
+ having the implementation split into multiple arguments...
+ """
+ def _parsecap(line):
+ lst = line.decode('ascii').split()
+ return lst[0], lst[1:]
+
+ caps = {}
+ try:
+ resp = self._longcmd('CAPA')
+ rawcaps = resp[1]
+ for capline in rawcaps:
+ capnm, capargs = _parsecap(capline)
+ caps[capnm] = capargs
+ except error_proto as _err:
+ raise error_proto('-ERR CAPA not supported by server')
+ return caps
+
+
+ def stls(self, context=None):
+ """Start a TLS session on the active connection as specified in RFC 2595.
+
+ context - a ssl.SSLContext
+ """
+ if not HAVE_SSL:
+ raise error_proto('-ERR TLS support missing')
+ if self._tls_established:
+ raise error_proto('-ERR TLS session already established')
+ caps = self.capa()
+ if not 'STLS' in caps:
+ raise error_proto('-ERR STLS not supported by server')
+ if context is None:
+ context = ssl._create_stdlib_context()
+ resp = self._shortcmd('STLS')
+ server_hostname = self.host if ssl.HAS_SNI else None
+ self.sock = context.wrap_socket(self.sock,
+ server_hostname=server_hostname)
+ self.file = self.sock.makefile('rb')
+ self._tls_established = True
+ return resp
+
+
+if HAVE_SSL:
class POP3_SSL(POP3):
"""POP3 client class over SSL connection
- Instantiate with: POP3_SSL(hostname, port=995, keyfile=None, certfile=None)
+ Instantiate with: POP3_SSL(hostname, port=995, keyfile=None, certfile=None,
+ context=None)
hostname - the hostname of the pop3 over ssl server
port - port number
keyfile - PEM formatted file that contains your private key
certfile - PEM formatted certificate chain file
+ context - a ssl.SSLContext
See the methods of the parent class POP3 for more documentation.
"""
@@ -353,17 +422,26 @@ else:
"exclusive")
self.keyfile = keyfile
self.certfile = certfile
+ if context is None:
+ context = ssl._create_stdlib_context(certfile=certfile,
+ keyfile=keyfile)
self.context = context
POP3.__init__(self, host, port, timeout)
def _create_socket(self, timeout):
sock = POP3._create_socket(self, timeout)
- if self.context is not None:
- sock = self.context.wrap_socket(sock)
- else:
- sock = ssl.wrap_socket(sock, self.keyfile, self.certfile)
+ server_hostname = self.host if ssl.HAS_SNI else None
+ sock = self.context.wrap_socket(sock,
+ server_hostname=server_hostname)
return sock
+ def stls(self, keyfile=None, certfile=None, context=None):
+ """The method unconditionally raises an exception since the
+ STLS command doesn't make any sense on an already established
+ SSL/TLS session.
+ """
+ raise error_proto('-ERR TLS session already established')
+
__all__.append("POP3_SSL")
if __name__ == "__main__":
diff --git a/Lib/posixpath.py b/Lib/posixpath.py
index fd63f97..3e13239 100644
--- a/Lib/posixpath.py
+++ b/Lib/posixpath.py
@@ -162,7 +162,7 @@ def islink(path):
"""Test whether a path is a symbolic link"""
try:
st = os.lstat(path)
- except (os.error, AttributeError):
+ except (OSError, AttributeError):
return False
return stat.S_ISLNK(st.st_mode)
@@ -172,56 +172,35 @@ def lexists(path):
"""Test whether a path exists. Returns True for broken symbolic links"""
try:
os.lstat(path)
- except os.error:
+ except OSError:
return False
return True
-# Are two filenames really pointing to the same file?
-
-def samefile(f1, f2):
- """Test whether two pathnames reference the same actual file"""
- s1 = os.stat(f1)
- s2 = os.stat(f2)
- return samestat(s1, s2)
-
-
-# Are two open files really referencing the same file?
-# (Not necessarily the same file descriptor!)
-
-def sameopenfile(fp1, fp2):
- """Test whether two open file objects reference the same file"""
- s1 = os.fstat(fp1)
- s2 = os.fstat(fp2)
- return samestat(s1, s2)
-
-
-# Are two stat buffers (obtained from stat, fstat or lstat)
-# describing the same file?
-
-def samestat(s1, s2):
- """Test whether two stat buffers reference the same file"""
- return s1.st_ino == s2.st_ino and \
- s1.st_dev == s2.st_dev
-
-
# Is a path a mount point?
# (Does this work for all UNIXes? Is it even guaranteed to work by Posix?)
def ismount(path):
"""Test whether a path is a mount point"""
- if islink(path):
- # A symlink can never be a mount point
- return False
try:
s1 = os.lstat(path)
- if isinstance(path, bytes):
- parent = join(path, b'..')
- else:
- parent = join(path, '..')
+ except OSError:
+ # It doesn't exist -- so not a mount point. :-)
+ return False
+ else:
+ # A symlink can never be a mount point
+ if stat.S_ISLNK(s1.st_mode):
+ return False
+
+ if isinstance(path, bytes):
+ parent = join(path, b'..')
+ else:
+ parent = join(path, '..')
+ try:
s2 = os.lstat(parent)
- except os.error:
- return False # It doesn't exist -- so not a mount point :-)
+ except OSError:
+ return False
+
dev1 = s1.st_dev
dev2 = s2.st_dev
if dev1 != dev2:
diff --git a/Lib/pprint.py b/Lib/pprint.py
index 22be0b4b..3be9c36 100644
--- a/Lib/pprint.py
+++ b/Lib/pprint.py
@@ -34,6 +34,7 @@ saferepr()
"""
+import re
import sys as _sys
from collections import OrderedDict as _OrderedDict
from io import StringIO as _StringIO
@@ -41,22 +42,19 @@ from io import StringIO as _StringIO
__all__ = ["pprint","pformat","isreadable","isrecursive","saferepr",
"PrettyPrinter"]
-# cache these for faster access:
-_commajoin = ", ".join
-_id = id
-_len = len
-_type = type
-
-def pprint(object, stream=None, indent=1, width=80, depth=None):
+def pprint(object, stream=None, indent=1, width=80, depth=None, *,
+ compact=False):
"""Pretty-print a Python object to a stream [default is sys.stdout]."""
printer = PrettyPrinter(
- stream=stream, indent=indent, width=width, depth=depth)
+ stream=stream, indent=indent, width=width, depth=depth,
+ compact=compact)
printer.pprint(object)
-def pformat(object, indent=1, width=80, depth=None):
+def pformat(object, indent=1, width=80, depth=None, *, compact=False):
"""Format a Python object into a pretty-printed representation."""
- return PrettyPrinter(indent=indent, width=width, depth=depth).pformat(object)
+ return PrettyPrinter(indent=indent, width=width, depth=depth,
+ compact=compact).pformat(object)
def saferepr(object):
"""Version of repr() which can handle recursive data structures."""
@@ -101,7 +99,8 @@ def _safe_tuple(t):
return _safe_key(t[0]), _safe_key(t[1])
class PrettyPrinter:
- def __init__(self, indent=1, width=80, depth=None, stream=None):
+ def __init__(self, indent=1, width=80, depth=None, stream=None, *,
+ compact=False):
"""Handle pretty printing operations onto a stream using a set of
configured parameters.
@@ -118,6 +117,9 @@ class PrettyPrinter:
The desired output stream. If omitted (or false), the standard
output stream available at construction will be used.
+ compact
+ If true, several items will be combined in one line.
+
"""
indent = int(indent)
width = int(width)
@@ -131,6 +133,7 @@ class PrettyPrinter:
self._stream = stream
else:
self._stream = _sys.stdout
+ self._compact = bool(compact)
def pprint(self, object):
self._format(object, self._stream, 0, 0, {}, 0)
@@ -150,28 +153,25 @@ class PrettyPrinter:
def _format(self, object, stream, indent, allowance, context, level):
level = level + 1
- objid = _id(object)
+ objid = id(object)
if objid in context:
stream.write(_recursion(object))
self._recursive = True
self._readable = False
return
rep = self._repr(object, context, level - 1)
- typ = _type(object)
- sepLines = _len(rep) > (self._width - 1 - indent - allowance)
+ typ = type(object)
+ max_width = self._width - 1 - indent - allowance
+ sepLines = len(rep) > max_width
write = stream.write
- if self._depth and level > self._depth:
- write(rep)
- return
-
if sepLines:
r = getattr(typ, "__repr__", None)
if issubclass(typ, dict):
write('{')
if self._indent_per_level > 1:
write((self._indent_per_level - 1) * ' ')
- length = _len(object)
+ length = len(object)
if length:
context[objid] = 1
indent = indent + self._indent_per_level
@@ -183,13 +183,13 @@ class PrettyPrinter:
rep = self._repr(key, context, level)
write(rep)
write(': ')
- self._format(ent, stream, indent + _len(rep) + 2,
+ self._format(ent, stream, indent + len(rep) + 2,
allowance + 1, context, level)
if length > 1:
for key, ent in items[1:]:
rep = self._repr(key, context, level)
write(',\n%s%s: ' % (' '*indent, rep))
- self._format(ent, stream, indent + _len(rep) + 2,
+ self._format(ent, stream, indent + len(rep) + 2,
allowance + 1, context, level)
indent = indent - self._indent_per_level
del context[objid]
@@ -201,7 +201,7 @@ class PrettyPrinter:
(issubclass(typ, set) and r is set.__repr__) or
(issubclass(typ, frozenset) and r is frozenset.__repr__)
):
- length = _len(object)
+ length = len(object)
if issubclass(typ, list):
write('[')
endchar = ']'
@@ -225,23 +225,71 @@ class PrettyPrinter:
write((self._indent_per_level - 1) * ' ')
if length:
context[objid] = 1
- indent = indent + self._indent_per_level
- self._format(object[0], stream, indent, allowance + 1,
- context, level)
- if length > 1:
- for ent in object[1:]:
- write(',\n' + ' '*indent)
- self._format(ent, stream, indent,
- allowance + 1, context, level)
- indent = indent - self._indent_per_level
+ self._format_items(object, stream,
+ indent + self._indent_per_level,
+ allowance + 1, context, level)
del context[objid]
if issubclass(typ, tuple) and length == 1:
write(',')
write(endchar)
return
+ if issubclass(typ, str) and len(object) > 0 and r is str.__repr__:
+ def _str_parts(s):
+ """
+ Return a list of string literals comprising the repr()
+ of the given string using literal concatenation.
+ """
+ lines = s.splitlines(True)
+ for i, line in enumerate(lines):
+ rep = repr(line)
+ if len(rep) <= max_width:
+ yield rep
+ else:
+ # A list of alternating (non-space, space) strings
+ parts = re.split(r'(\s+)', line) + ['']
+ current = ''
+ for i in range(0, len(parts), 2):
+ part = parts[i] + parts[i+1]
+ candidate = current + part
+ if len(repr(candidate)) > max_width:
+ if current:
+ yield repr(current)
+ current = part
+ else:
+ current = candidate
+ if current:
+ yield repr(current)
+ for i, rep in enumerate(_str_parts(object)):
+ if i > 0:
+ write('\n' + ' '*indent)
+ write(rep)
+ return
write(rep)
+ def _format_items(self, items, stream, indent, allowance, context, level):
+ write = stream.write
+ delimnl = ',\n' + ' ' * indent
+ delim = ''
+ width = max_width = self._width - indent - allowance + 2
+ for ent in items:
+ if self._compact:
+ rep = self._repr(ent, context, level)
+ w = len(rep) + 2
+ if width < w:
+ width = max_width
+ if delim:
+ delim = delimnl
+ if width >= w:
+ width -= w
+ write(delim)
+ delim = ', '
+ write(rep)
+ continue
+ write(delim)
+ delim = delimnl
+ self._format(ent, stream, indent, allowance, context, level)
+
def _repr(self, object, context, level):
repr, readable, recursive = self.format(object, context.copy(),
self._depth, level)
@@ -262,7 +310,7 @@ class PrettyPrinter:
# Return triple (repr_string, isreadable, isrecursive).
def _safe_repr(object, context, maxlevels, level):
- typ = _type(object)
+ typ = type(object)
if typ is str:
if 'locale' not in _sys.modules:
return repr(object), True, False
@@ -286,7 +334,7 @@ def _safe_repr(object, context, maxlevels, level):
if issubclass(typ, dict) and r is dict.__repr__:
if not object:
return "{}", True, False
- objid = _id(object)
+ objid = id(object)
if maxlevels and level >= maxlevels:
return "{...}", False, objid in context
if objid in context:
@@ -307,7 +355,7 @@ def _safe_repr(object, context, maxlevels, level):
if krecur or vrecur:
recursive = True
del context[objid]
- return "{%s}" % _commajoin(components), readable, recursive
+ return "{%s}" % ", ".join(components), readable, recursive
if (issubclass(typ, list) and r is list.__repr__) or \
(issubclass(typ, tuple) and r is tuple.__repr__):
@@ -315,13 +363,13 @@ def _safe_repr(object, context, maxlevels, level):
if not object:
return "[]", True, False
format = "[%s]"
- elif _len(object) == 1:
+ elif len(object) == 1:
format = "(%s,)"
else:
if not object:
return "()", True, False
format = "(%s)"
- objid = _id(object)
+ objid = id(object)
if maxlevels and level >= maxlevels:
return format % "...", False, objid in context
if objid in context:
@@ -340,7 +388,7 @@ def _safe_repr(object, context, maxlevels, level):
if orecur:
recursive = True
del context[objid]
- return format % _commajoin(components), readable, recursive
+ return format % ", ".join(components), readable, recursive
rep = repr(object)
return rep, (rep and not rep.startswith('<')), False
@@ -348,7 +396,7 @@ def _safe_repr(object, context, maxlevels, level):
def _recursion(object):
return ("<Recursion on %s with id=%s>"
- % (_type(object).__name__, _id(object)))
+ % (type(object).__name__, id(object)))
def _perfcheck(object=None):
diff --git a/Lib/profile.py b/Lib/profile.py
index 743e77d..5d0e968 100755
--- a/Lib/profile.py
+++ b/Lib/profile.py
@@ -40,6 +40,40 @@ __all__ = ["run", "runctx", "Profile"]
# return i_count
#itimes = integer_timer # replace with C coded timer returning integers
+class _Utils:
+ """Support class for utility functions which are shared by
+ profile.py and cProfile.py modules.
+ Not supposed to be used directly.
+ """
+
+ def __init__(self, profiler):
+ self.profiler = profiler
+
+ def run(self, statement, filename, sort):
+ prof = self.profiler()
+ try:
+ prof.run(statement)
+ except SystemExit:
+ pass
+ finally:
+ self._show(prof, filename, sort)
+
+ def runctx(self, statement, globals, locals, filename, sort):
+ prof = self.profiler()
+ try:
+ prof.runctx(statement, globals, locals)
+ except SystemExit:
+ pass
+ finally:
+ self._show(prof, filename, sort)
+
+ def _show(self, prof, filename, sort):
+ if filename is not None:
+ prof.dump_stats(filename)
+ else:
+ prof.print_stats(sort)
+
+
#**************************************************************************
# The following are the static member functions for the profiler class
# Note that an instance of Profile() is *not* needed to call them.
@@ -56,15 +90,7 @@ def run(statement, filename=None, sort=-1):
standard name string (file/line/function-name) that is presented in
each line.
"""
- prof = Profile()
- try:
- prof = prof.run(statement)
- except SystemExit:
- pass
- if filename is not None:
- prof.dump_stats(filename)
- else:
- return prof.print_stats(sort)
+ return _Utils(Profile).run(statement, filename, sort)
def runctx(statement, globals, locals, filename=None, sort=-1):
"""Run statement under profiler, supplying your own globals and locals,
@@ -72,16 +98,8 @@ def runctx(statement, globals, locals, filename=None, sort=-1):
statement and filename have the same semantics as profile.run
"""
- prof = Profile()
- try:
- prof = prof.runctx(statement, globals, locals)
- except SystemExit:
- pass
-
- if filename is not None:
- prof.dump_stats(filename)
- else:
- return prof.print_stats(sort)
+ return _Utils(Profile).runctx(statement, globals, locals, filename, sort)
+
class Profile:
"""Profiler class.
@@ -373,10 +391,9 @@ class Profile:
print_stats()
def dump_stats(self, file):
- f = open(file, 'wb')
- self.create_stats()
- marshal.dump(self.stats, f)
- f.close()
+ with open(file, 'wb') as f:
+ self.create_stats()
+ marshal.dump(self.stats, f)
def create_stats(self):
self.simulate_cmd_complete()
diff --git a/Lib/pstats.py b/Lib/pstats.py
index 6a77605..e1ec355 100644
--- a/Lib/pstats.py
+++ b/Lib/pstats.py
@@ -93,9 +93,8 @@ class Stats:
self.stats = {}
return
elif isinstance(arg, str):
- f = open(arg, 'rb')
- self.stats = marshal.load(f)
- f.close()
+ with open(arg, 'rb') as f:
+ self.stats = marshal.load(f)
try:
file_stats = os.stat(arg)
arg = time.ctime(file_stats.st_mtime) + " " + arg
@@ -149,11 +148,8 @@ class Stats:
def dump_stats(self, filename):
"""Write the profile data to a file we know how to load back."""
- f = open(filename, 'wb')
- try:
+ with open(filename, 'wb') as f:
marshal.dump(self.stats, f)
- finally:
- f.close()
# list the tuple indices and directions for sorting,
# along with some printable description
@@ -612,7 +608,7 @@ if __name__ == '__main__':
if line:
try:
self.stats = Stats(line)
- except IOError as err:
+ except OSError as err:
print(err.args[1], file=self.stream)
return
except Exception as err:
diff --git a/Lib/pty.py b/Lib/pty.py
index 3ccf619..e841f12 100644
--- a/Lib/pty.py
+++ b/Lib/pty.py
@@ -47,27 +47,16 @@ def master_open():
return _open_terminal()
def _open_terminal():
- """Open pty master and return (master_fd, tty_name).
- SGI and generic BSD version, for when openpty() fails."""
- try:
- import sgi
- except ImportError:
- pass
- else:
- try:
- tty_name, master_fd = sgi._getpty(os.O_RDWR, 0o666, 0)
- except IOError as msg:
- raise os.error(msg)
- return master_fd, tty_name
+ """Open pty master and return (master_fd, tty_name)."""
for x in 'pqrstuvwxyzPQRST':
for y in '0123456789abcdef':
pty_name = '/dev/pty' + x + y
try:
fd = os.open(pty_name, os.O_RDWR)
- except os.error:
+ except OSError:
continue
return (fd, '/dev/tty' + x + y)
- raise os.error('out of pty devices')
+ raise OSError('out of pty devices')
def slave_open(tty_name):
"""slave_open(tty_name) -> slave_fd
@@ -83,7 +72,7 @@ def slave_open(tty_name):
try:
ioctl(result, I_PUSH, "ptem")
ioctl(result, I_PUSH, "ldterm")
- except IOError:
+ except OSError:
pass
return result
@@ -173,8 +162,9 @@ def spawn(argv, master_read=_read, stdin_read=_read):
restore = 0
try:
_copy(master_fd, master_read, stdin_read)
- except (IOError, OSError):
+ except OSError:
if restore:
tty.tcsetattr(STDIN_FILENO, tty.TCSAFLUSH, mode)
os.close(master_fd)
+ return os.waitpid(pid, 0)[1]
diff --git a/Lib/py_compile.py b/Lib/py_compile.py
index 62d69ad..1277b93 100644
--- a/Lib/py_compile.py
+++ b/Lib/py_compile.py
@@ -3,17 +3,14 @@
This module has intimate knowledge of the format of .pyc files.
"""
-import builtins
-import errno
-import imp
-import marshal
+import importlib._bootstrap
+import importlib.machinery
+import importlib.util
import os
+import os.path
import sys
-import tokenize
import traceback
-MAGIC = imp.get_magic()
-
__all__ = ["compile", "main", "PyCompileError"]
@@ -65,13 +62,6 @@ class PyCompileError(Exception):
return self.msg
-def wr_long(f, x):
- """Internal; write a 32-bit int to a file in little-endian order."""
- f.write(bytes([x & 0xff,
- (x >> 8) & 0xff,
- (x >> 16) & 0xff,
- (x >> 24) & 0xff]))
-
def compile(file, cfile=None, dfile=None, doraise=False, optimize=-1):
"""Byte-compile one Python source file to Python bytecode.
@@ -107,18 +97,31 @@ def compile(file, cfile=None, dfile=None, doraise=False, optimize=-1):
See compileall.py for a script/module that uses this module to
byte-compile all installed files (or all files in selected
directories).
+
+ Do note that FileExistsError is raised if cfile ends up pointing at a
+ non-regular file or symlink. Because the compilation uses a file renaming,
+ the resulting file would be regular and thus not the same type of file as
+ it was previously.
"""
- with tokenize.open(file) as f:
- try:
- st = os.fstat(f.fileno())
- except AttributeError:
- st = os.stat(file)
- timestamp = int(st.st_mtime)
- size = st.st_size & 0xFFFFFFFF
- codestring = f.read()
+ if cfile is None:
+ if optimize >= 0:
+ cfile = importlib.util.cache_from_source(file,
+ debug_override=not optimize)
+ else:
+ cfile = importlib.util.cache_from_source(file)
+ if os.path.islink(cfile):
+ msg = ('{} is a symlink and will be changed into a regular file if '
+ 'import writes a byte-compiled file to it')
+ raise FileExistsError(msg.format(cfile))
+ elif os.path.exists(cfile) and not os.path.isfile(cfile):
+ msg = ('{} is a non-regular file and will be changed into a regular '
+ 'one if import writes a byte-compiled file to it')
+ raise FileExistsError(msg.format(cfile))
+ loader = importlib.machinery.SourceFileLoader('<py_compile>', file)
+ source_bytes = loader.get_data(file)
try:
- codeobject = builtins.compile(codestring, dfile or file, 'exec',
- optimize=optimize)
+ code = loader.source_to_code(source_bytes, dfile or file,
+ _optimize=optimize)
except Exception as err:
py_exc = PyCompileError(err.__class__, err, dfile or file)
if doraise:
@@ -126,28 +129,20 @@ def compile(file, cfile=None, dfile=None, doraise=False, optimize=-1):
else:
sys.stderr.write(py_exc.msg + '\n')
return
- if cfile is None:
- if optimize >= 0:
- cfile = imp.cache_from_source(file, debug_override=not optimize)
- else:
- cfile = imp.cache_from_source(file)
try:
dirname = os.path.dirname(cfile)
if dirname:
os.makedirs(dirname)
- except OSError as error:
- if error.errno != errno.EEXIST:
- raise
- with open(cfile, 'wb') as fc:
- fc.write(b'\0\0\0\0')
- wr_long(fc, timestamp)
- wr_long(fc, size)
- marshal.dump(codeobject, fc)
- fc.flush()
- fc.seek(0, 0)
- fc.write(MAGIC)
+ except FileExistsError:
+ pass
+ source_stats = loader.path_stats(file)
+ bytecode = importlib._bootstrap._code_to_bytecode(
+ code, source_stats['mtime'], source_stats['size'])
+ mode = importlib._bootstrap._calc_mode(file)
+ importlib._bootstrap._write_atomic(cfile, bytecode, mode)
return cfile
+
def main(args=None):
"""Compile several source files.
@@ -173,7 +168,7 @@ def main(args=None):
except PyCompileError as error:
rv = 1
sys.stderr.write("%s\n" % error.msg)
- except IOError as error:
+ except OSError as error:
rv = 1
sys.stderr.write("%s\n" % error)
else:
diff --git a/Lib/pyclbr.py b/Lib/pyclbr.py
index 9ec05ee..dd58ada 100644
--- a/Lib/pyclbr.py
+++ b/Lib/pyclbr.py