From python-checkins at python.org Wed Dec 1 00:46:54 2010
From: python-checkins at python.org (brian.curtin)
Date: Wed, 1 Dec 2010 00:46:54 +0100 (CET)
Subject: [Python-checkins] r86906 - python/branches/py3k/Lib/test/test_os.py
Message-ID: <20101130234654.8B06DEE9F9@mail.python.org>
Author: brian.curtin
Date: Wed Dec 1 00:46:54 2010
New Revision: 86906
Log:
Fix #10591. Fix test_os for refleak runs.
Split a common setUp/tearDown into the appropriate parts.
Modified:
python/branches/py3k/Lib/test/test_os.py
Modified: python/branches/py3k/Lib/test/test_os.py
==============================================================================
--- python/branches/py3k/Lib/test/test_os.py (original)
+++ python/branches/py3k/Lib/test/test_os.py Wed Dec 1 00:46:54 2010
@@ -866,12 +866,11 @@
self.file1 = support.TESTFN
self.file2 = os.path.join(support.TESTFN + "2")
+ def tearDown(self):
for file in (self.file1, self.file2):
if os.path.exists(file):
os.unlink(file)
- tearDown = setUp
-
def _test_link(self, file1, file2):
with open(file1, "w") as f1:
f1.write("test")
From python-checkins at python.org Wed Dec 1 01:48:02 2010
From: python-checkins at python.org (raymond.hettinger)
Date: Wed, 1 Dec 2010 01:48:02 +0100 (CET)
Subject: [Python-checkins] r86907 - in python/branches/py3k:
Doc/library/functools.rst Lib/functools.py
Message-ID: <20101201004802.C9664EEA68@mail.python.org>
Author: raymond.hettinger
Date: Wed Dec 1 01:47:56 2010
New Revision: 86907
Log:
Doc and docstring nits.
Modified:
python/branches/py3k/Doc/library/functools.rst
python/branches/py3k/Lib/functools.py
Modified: python/branches/py3k/Doc/library/functools.rst
==============================================================================
--- python/branches/py3k/Doc/library/functools.rst (original)
+++ python/branches/py3k/Doc/library/functools.rst Wed Dec 1 01:47:56 2010
@@ -91,6 +91,10 @@
.. versionadded:: 3.2
+ .. seealso::
+
+ Recipe for a `plain cache without the LRU feature
+ `_.
.. decorator:: total_ordering
Modified: python/branches/py3k/Lib/functools.py
==============================================================================
--- python/branches/py3k/Lib/functools.py (original)
+++ python/branches/py3k/Lib/functools.py Wed Dec 1 01:47:56 2010
@@ -121,9 +121,9 @@
Arguments to the cached function must be hashable.
- View the cache statistics named tuple (maxsize, size, hits, misses) with
+ View the cache statistics named tuple (hits, misses, maxsize, currsize) with
f.cache_info(). Clear the cache and statistics with f.cache_clear().
- And access the underlying function with f.__wrapped__.
+ Access the underlying function with f.__wrapped__.
See: http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used
From python-checkins at python.org Wed Dec 1 01:56:10 2010
From: python-checkins at python.org (ezio.melotti)
Date: Wed, 1 Dec 2010 01:56:10 +0100 (CET)
Subject: [Python-checkins] r86908 - in python/branches/py3k:
Doc/library/unittest.rst Doc/library/warnings.rst
Lib/unittest/main.py Lib/unittest/runner.py
Lib/unittest/test/_test_warnings.py Lib/unittest/test/test_break.py
Lib/unittest/test/test_program.py Lib/unittest/test/test_runner.py
Message-ID: <20101201005610.E54D7EEA73@mail.python.org>
Author: ezio.melotti
Date: Wed Dec 1 01:56:10 2010
New Revision: 86908
Log:
#10535: Enable silenced warnings in unittest by default
Added:
python/branches/py3k/Lib/unittest/test/_test_warnings.py
Modified:
python/branches/py3k/Doc/library/unittest.rst
python/branches/py3k/Doc/library/warnings.rst
python/branches/py3k/Lib/unittest/main.py
python/branches/py3k/Lib/unittest/runner.py
python/branches/py3k/Lib/unittest/test/test_break.py
python/branches/py3k/Lib/unittest/test/test_program.py
python/branches/py3k/Lib/unittest/test/test_runner.py
Modified: python/branches/py3k/Doc/library/unittest.rst
==============================================================================
--- python/branches/py3k/Doc/library/unittest.rst (original)
+++ python/branches/py3k/Doc/library/unittest.rst Wed Dec 1 01:56:10 2010
@@ -1845,12 +1845,21 @@
instead of repeatedly creating new instances.
-.. class:: TextTestRunner(stream=sys.stderr, descriptions=True, verbosity=1, runnerclass=None)
+.. class:: TextTestRunner(stream=sys.stderr, descriptions=True, verbosity=1, runnerclass=None, warnings=None)
A basic test runner implementation which prints results on standard error. It
has a few configurable parameters, but is essentially very simple. Graphical
applications which run test suites should provide alternate implementations.
+ By default this runner shows :exc:`DeprecationWarning`,
+ :exc:`PendingDeprecationWarning`, and :exc:`ImportWarning` even if they are
+ :ref:`ignored by default `. Deprecation warnings caused by
+ :ref:`deprecated unittest methods ` are also
+ special-cased and, when the warning filters are ``'default'`` or ``'always'``,
+ they will appear only once per-module, in order to avoid too many warning
+ messages. This behavior can be overridden using the :option`-Wd` or
+ :option:`-Wa` options and leaving *warnings* to ``None``.
+
.. method:: _makeResult()
This method returns the instance of ``TestResult`` used by :meth:`run`.
@@ -1864,7 +1873,9 @@
stream, descriptions, verbosity
-.. function:: main(module='__main__', defaultTest=None, argv=None, testRunner=None, testLoader=unittest.loader.defaultTestLoader, exit=True, verbosity=1, failfast=None, catchbreak=None, buffer=None)
+ .. versionchanged:: 3.2 Added the ``warnings`` argument
+
+.. function:: main(module='__main__', defaultTest=None, argv=None, testRunner=None, testLoader=unittest.loader.defaultTestLoader, exit=True, verbosity=1, failfast=None, catchbreak=None, buffer=None, warnings=None)
A command-line program that runs a set of tests; this is primarily for making
test modules conveniently executable. The simplest use for this function is to
@@ -1893,12 +1904,17 @@
The ``failfast``, ``catchbreak`` and ``buffer`` parameters have the same
effect as the same-name `command-line options`_.
+ The *warning* argument specifies the :ref:`warning filter `
+ that should be used while running the tests. If it's not specified, it will
+ remain ``None`` if a :option:`-W` option is passed to :program:`python`,
+ otherwise it will be set to ``'default'``.
+
Calling ``main`` actually returns an instance of the ``TestProgram`` class.
This stores the result of the tests run as the ``result`` attribute.
.. versionchanged:: 3.2
- The ``exit``, ``verbosity``, ``failfast``, ``catchbreak`` and ``buffer``
- parameters were added.
+ The ``exit``, ``verbosity``, ``failfast``, ``catchbreak``, ``buffer``,
+ and ``warnings`` parameters were added.
load_tests Protocol
Modified: python/branches/py3k/Doc/library/warnings.rst
==============================================================================
--- python/branches/py3k/Doc/library/warnings.rst (original)
+++ python/branches/py3k/Doc/library/warnings.rst Wed Dec 1 01:56:10 2010
@@ -249,6 +249,8 @@
entries from the warnings list before each new operation).
+.. _warning-ignored:
+
Updating Code For New Versions of Python
----------------------------------------
@@ -279,6 +281,9 @@
developer want to be notified that your code is using a deprecated module, to a
user this information is essentially noise and provides no benefit to them.
+The :mod:`unittest` module has been also updated to use the ``'default'``
+filter while running tests.
+
.. _warning-functions:
Modified: python/branches/py3k/Lib/unittest/main.py
==============================================================================
--- python/branches/py3k/Lib/unittest/main.py (original)
+++ python/branches/py3k/Lib/unittest/main.py Wed Dec 1 01:56:10 2010
@@ -67,12 +67,12 @@
USAGE = USAGE_FROM_MODULE
# defaults for testing
- failfast = catchbreak = buffer = progName = None
+ failfast = catchbreak = buffer = progName = warnings = None
def __init__(self, module='__main__', defaultTest=None, argv=None,
testRunner=None, testLoader=loader.defaultTestLoader,
exit=True, verbosity=1, failfast=None, catchbreak=None,
- buffer=None):
+ buffer=None, warnings=None):
if isinstance(module, str):
self.module = __import__(module)
for part in module.split('.')[1:]:
@@ -87,6 +87,18 @@
self.catchbreak = catchbreak
self.verbosity = verbosity
self.buffer = buffer
+ if warnings is None and not sys.warnoptions:
+ # even if DreprecationWarnings are ignored by default
+ # print them anyway unless other warnings settings are
+ # specified by the warnings arg or the -W python flag
+ self.warnings = 'default'
+ else:
+ # here self.warnings is set either to the value passed
+ # to the warnings args or to None.
+ # If the user didn't pass a value self.warnings will
+ # be None. This means that the behavior is unchanged
+ # and depends on the values passed to -W.
+ self.warnings = warnings
self.defaultTest = defaultTest
self.testRunner = testRunner
self.testLoader = testLoader
@@ -220,7 +232,8 @@
try:
testRunner = self.testRunner(verbosity=self.verbosity,
failfast=self.failfast,
- buffer=self.buffer)
+ buffer=self.buffer,
+ warnings=self.warnings)
except TypeError:
# didn't accept the verbosity, buffer or failfast arguments
testRunner = self.testRunner()
Modified: python/branches/py3k/Lib/unittest/runner.py
==============================================================================
--- python/branches/py3k/Lib/unittest/runner.py (original)
+++ python/branches/py3k/Lib/unittest/runner.py Wed Dec 1 01:56:10 2010
@@ -2,6 +2,7 @@
import sys
import time
+import warnings
from . import result
from .signals import registerResult
@@ -125,12 +126,13 @@
resultclass = TextTestResult
def __init__(self, stream=sys.stderr, descriptions=True, verbosity=1,
- failfast=False, buffer=False, resultclass=None):
+ failfast=False, buffer=False, resultclass=None, warnings=None):
self.stream = _WritelnDecorator(stream)
self.descriptions = descriptions
self.verbosity = verbosity
self.failfast = failfast
self.buffer = buffer
+ self.warnings = warnings
if resultclass is not None:
self.resultclass = resultclass
@@ -143,17 +145,30 @@
registerResult(result)
result.failfast = self.failfast
result.buffer = self.buffer
- startTime = time.time()
- startTestRun = getattr(result, 'startTestRun', None)
- if startTestRun is not None:
- startTestRun()
- try:
- test(result)
- finally:
- stopTestRun = getattr(result, 'stopTestRun', None)
- if stopTestRun is not None:
- stopTestRun()
- stopTime = time.time()
+ with warnings.catch_warnings():
+ if self.warnings:
+ # if self.warnings is set, use it to filter all the warnings
+ warnings.simplefilter(self.warnings)
+ # if the filter is 'default' or 'always', special-case the
+ # warnings from the deprecated unittest methods to show them
+ # no more than once per module, because they can be fairly
+ # noisy. The -Wd and -Wa flags can be used to bypass this
+ # only when self.warnings is None.
+ if self.warnings in ['default', 'always']:
+ warnings.filterwarnings('module',
+ category=DeprecationWarning,
+ message='Please use assert\w+ instead.')
+ startTime = time.time()
+ startTestRun = getattr(result, 'startTestRun', None)
+ if startTestRun is not None:
+ startTestRun()
+ try:
+ test(result)
+ finally:
+ stopTestRun = getattr(result, 'stopTestRun', None)
+ if stopTestRun is not None:
+ stopTestRun()
+ stopTime = time.time()
timeTaken = stopTime - startTime
result.printErrors()
if hasattr(result, 'separator2'):
Added: python/branches/py3k/Lib/unittest/test/_test_warnings.py
==============================================================================
--- (empty file)
+++ python/branches/py3k/Lib/unittest/test/_test_warnings.py Wed Dec 1 01:56:10 2010
@@ -0,0 +1,74 @@
+# helper module for test_runner.Test_TextTestRunner.test_warnings
+
+"""
+This module has a number of tests that raise different kinds of warnings.
+When the tests are run, the warnings are caught and their messages are printed
+to stdout. This module also accepts an arg that is then passed to
+unittest.main to affect the behavior of warnings.
+Test_TextTestRunner.test_warnings executes this script with different
+combinations of warnings args and -W flags and check that the output is correct.
+See #10535.
+"""
+
+import io
+import sys
+import unittest
+import warnings
+
+def warnfun():
+ warnings.warn('rw', RuntimeWarning)
+
+class TestWarnings(unittest.TestCase):
+ # unittest warnings will be printed at most once per type (max one message
+ # for the fail* methods, and one for the assert* methods)
+ def test_assert(self):
+ self.assertEquals(2+2, 4)
+ self.assertEquals(2*2, 4)
+ self.assertEquals(2**2, 4)
+
+ def test_fail(self):
+ self.failUnless(1)
+ self.failUnless(True)
+
+ def test_other_unittest(self):
+ self.assertAlmostEqual(2+2, 4)
+ self.assertNotAlmostEqual(4+4, 2)
+
+ # these warnings are normally silenced, but they are printed in unittest
+ def test_deprecation(self):
+ warnings.warn('dw', DeprecationWarning)
+ warnings.warn('dw', DeprecationWarning)
+ warnings.warn('dw', DeprecationWarning)
+
+ def test_import(self):
+ warnings.warn('iw', ImportWarning)
+ warnings.warn('iw', ImportWarning)
+ warnings.warn('iw', ImportWarning)
+
+ # user warnings should always be printed
+ def test_warning(self):
+ warnings.warn('uw')
+ warnings.warn('uw')
+ warnings.warn('uw')
+
+ # these warnings come from the same place; they will be printed
+ # only once by default or three times if the 'always' filter is used
+ def test_function(self):
+
+ warnfun()
+ warnfun()
+ warnfun()
+
+
+
+if __name__ == '__main__':
+ with warnings.catch_warnings(record=True) as ws:
+ # if an arg is provided pass it to unittest.main as 'warnings'
+ if len(sys.argv) == 2:
+ unittest.main(exit=False, warnings=sys.argv.pop())
+ else:
+ unittest.main(exit=False)
+
+ # print all the warning messages collected
+ for w in ws:
+ print(w.message)
Modified: python/branches/py3k/Lib/unittest/test/test_break.py
==============================================================================
--- python/branches/py3k/Lib/unittest/test/test_break.py (original)
+++ python/branches/py3k/Lib/unittest/test/test_break.py Wed Dec 1 01:56:10 2010
@@ -209,7 +209,8 @@
self.assertEqual(FakeRunner.initArgs, [((), {'buffer': None,
'verbosity': verbosity,
- 'failfast': failfast})])
+ 'failfast': failfast,
+ 'warnings': None})])
self.assertEqual(FakeRunner.runArgs, [test])
self.assertEqual(p.result, result)
@@ -222,7 +223,8 @@
self.assertEqual(FakeRunner.initArgs, [((), {'buffer': None,
'verbosity': verbosity,
- 'failfast': failfast})])
+ 'failfast': failfast,
+ 'warnings': None})])
self.assertEqual(FakeRunner.runArgs, [test])
self.assertEqual(p.result, result)
Modified: python/branches/py3k/Lib/unittest/test/test_program.py
==============================================================================
--- python/branches/py3k/Lib/unittest/test/test_program.py (original)
+++ python/branches/py3k/Lib/unittest/test/test_program.py Wed Dec 1 01:56:10 2010
@@ -182,6 +182,27 @@
program.parseArgs([None, opt])
self.assertEqual(getattr(program, attr), not_none)
+ def testWarning(self):
+ """Test the warnings argument"""
+ # see #10535
+ class FakeTP(unittest.TestProgram):
+ def parseArgs(self, *args, **kw): pass
+ def runTests(self, *args, **kw): pass
+ warnoptions = sys.warnoptions
+ try:
+ sys.warnoptions[:] = []
+ # no warn options, no arg -> default
+ self.assertEqual(FakeTP().warnings, 'default')
+ # no warn options, w/ arg -> arg value
+ self.assertEqual(FakeTP(warnings='ignore').warnings, 'ignore')
+ sys.warnoptions[:] = ['somevalue']
+ # warn options, no arg -> None
+ # warn options, w/ arg -> arg value
+ self.assertEqual(FakeTP().warnings, None)
+ self.assertEqual(FakeTP(warnings='ignore').warnings, 'ignore')
+ finally:
+ sys.warnoptions[:] = warnoptions
+
def testRunTestsRunnerClass(self):
program = self.program
@@ -189,12 +210,14 @@
program.verbosity = 'verbosity'
program.failfast = 'failfast'
program.buffer = 'buffer'
+ program.warnings = 'warnings'
program.runTests()
self.assertEqual(FakeRunner.initArgs, {'verbosity': 'verbosity',
'failfast': 'failfast',
- 'buffer': 'buffer'})
+ 'buffer': 'buffer',
+ 'warnings': 'warnings'})
self.assertEqual(FakeRunner.test, 'test')
self.assertIs(program.result, RESULT)
Modified: python/branches/py3k/Lib/unittest/test/test_runner.py
==============================================================================
--- python/branches/py3k/Lib/unittest/test/test_runner.py (original)
+++ python/branches/py3k/Lib/unittest/test/test_runner.py Wed Dec 1 01:56:10 2010
@@ -1,5 +1,8 @@
import io
+import os
+import sys
import pickle
+import subprocess
import unittest
@@ -144,6 +147,7 @@
self.assertFalse(runner.failfast)
self.assertFalse(runner.buffer)
self.assertEqual(runner.verbosity, 1)
+ self.assertEqual(runner.warnings, None)
self.assertTrue(runner.descriptions)
self.assertEqual(runner.resultclass, unittest.TextTestResult)
@@ -244,3 +248,57 @@
expectedresult = (runner.stream, DESCRIPTIONS, VERBOSITY)
self.assertEqual(runner._makeResult(), expectedresult)
+
+ def test_warnings(self):
+ """
+ Check that warnings argument of TextTestRunner correctly affects the
+ behavior of the warnings.
+ """
+ # see #10535 and the _test_warnings file for more information
+
+ def get_parse_out_err(p):
+ return [b.splitlines() for b in p.communicate()]
+ opts = dict(stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ cwd=os.path.dirname(__file__))
+ ae_msg = b'Please use assertEqual instead.'
+ at_msg = b'Please use assertTrue instead.'
+
+ # no args -> all the warnings are printed, unittest warnings only once
+ p = subprocess.Popen([sys.executable, '_test_warnings.py'], **opts)
+ out, err = get_parse_out_err(p)
+ self.assertEqual(err[-1], b'OK')
+ # check that the total number of warnings in the output is correct
+ self.assertEqual(len(out), 12)
+ # check that the numbers of the different kind of warnings is correct
+ for msg in [b'dw', b'iw', b'uw']:
+ self.assertEqual(out.count(msg), 3)
+ for msg in [ae_msg, at_msg, b'rw']:
+ self.assertEqual(out.count(msg), 1)
+
+ args_list = (
+ # passing 'ignore' as warnings arg -> no warnings
+ [sys.executable, '_test_warnings.py', 'ignore'],
+ # -W doesn't affect the result if the arg is passed
+ [sys.executable, '-Wa', '_test_warnings.py', 'ignore'],
+ # -W affects the result if the arg is not passed
+ [sys.executable, '-Wi', '_test_warnings.py']
+ )
+ # in all these cases no warnings are printed
+ for args in args_list:
+ p = subprocess.Popen(args, **opts)
+ out, err = get_parse_out_err(p)
+ self.assertEqual(err[-1], b'OK')
+ self.assertEqual(len(out), 0)
+
+
+ # passing 'always' as warnings arg -> all the warnings printed,
+ # unittest warnings only once
+ p = subprocess.Popen([sys.executable, '_test_warnings.py', 'always'],
+ **opts)
+ out, err = get_parse_out_err(p)
+ self.assertEqual(err[-1], b'OK')
+ self.assertEqual(len(out), 14)
+ for msg in [b'dw', b'iw', b'uw', b'rw']:
+ self.assertEqual(out.count(msg), 3)
+ for msg in [ae_msg, at_msg]:
+ self.assertEqual(out.count(msg), 1)
From python-checkins at python.org Wed Dec 1 02:45:53 2010
From: python-checkins at python.org (ezio.melotti)
Date: Wed, 1 Dec 2010 02:45:53 +0100 (CET)
Subject: [Python-checkins] r86909 - in python/branches/py3k:
Lib/unittest/test/test_runner.py Misc/NEWS
Message-ID: <20101201014553.E34E2EEB37@mail.python.org>
Author: ezio.melotti
Date: Wed Dec 1 02:45:53 2010
New Revision: 86909
Log:
Fix test failure in debug builds and add NEWS entry for r86908
Modified:
python/branches/py3k/Lib/unittest/test/test_runner.py
python/branches/py3k/Misc/NEWS
Modified: python/branches/py3k/Lib/unittest/test/test_runner.py
==============================================================================
--- python/branches/py3k/Lib/unittest/test/test_runner.py (original)
+++ python/branches/py3k/Lib/unittest/test/test_runner.py Wed Dec 1 02:45:53 2010
@@ -266,7 +266,7 @@
# no args -> all the warnings are printed, unittest warnings only once
p = subprocess.Popen([sys.executable, '_test_warnings.py'], **opts)
out, err = get_parse_out_err(p)
- self.assertEqual(err[-1], b'OK')
+ self.assertIn(b'OK', err)
# check that the total number of warnings in the output is correct
self.assertEqual(len(out), 12)
# check that the numbers of the different kind of warnings is correct
@@ -287,7 +287,7 @@
for args in args_list:
p = subprocess.Popen(args, **opts)
out, err = get_parse_out_err(p)
- self.assertEqual(err[-1], b'OK')
+ self.assertIn(b'OK', err)
self.assertEqual(len(out), 0)
@@ -296,7 +296,7 @@
p = subprocess.Popen([sys.executable, '_test_warnings.py', 'always'],
**opts)
out, err = get_parse_out_err(p)
- self.assertEqual(err[-1], b'OK')
+ self.assertIn(b'OK', err)
self.assertEqual(len(out), 14)
for msg in [b'dw', b'iw', b'uw', b'rw']:
self.assertEqual(out.count(msg), 3)
Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS (original)
+++ python/branches/py3k/Misc/NEWS Wed Dec 1 02:45:53 2010
@@ -46,6 +46,8 @@
Library
-------
+- Issue #10535: Enable silenced warnings in unittest by default.
+
- Issue #9873: The URL parsing functions in urllib.parse now accept
ASCII byte sequences as input in addition to character strings.
From python-checkins at python.org Wed Dec 1 03:32:33 2010
From: python-checkins at python.org (ezio.melotti)
Date: Wed, 1 Dec 2010 03:32:33 +0100 (CET)
Subject: [Python-checkins] r86910 - in python/branches/py3k:
Doc/library/unittest.rst Lib/test/test_abc.py
Lib/test/test_asyncore.py Lib/test/test_concurrent_futures.py
Lib/test/test_contextlib.py Lib/test/test_dis.py
Lib/test/test_memoryview.py Lib/test/test_runpy.py
Lib/test/test_smtplib.py Lib/test/test_ssl.py
Lib/test/test_unicode.py Lib/test/test_urlparse.py
Lib/test/test_xmlrpc.py Lib/test/test_zlib.py
Lib/unittest/case.py Lib/unittest/test/test_assertions.py
Lib/unittest/test/test_case.py Lib/unittest/test/test_discovery.py
Lib/unittest/test/test_loader.py
Lib/unittest/test/test_setups.py Misc/NEWS
Message-ID: <20101201023233.2C46AEEA6D@mail.python.org>
Author: ezio.melotti
Date: Wed Dec 1 03:32:32 2010
New Revision: 86910
Log:
#10273: Rename assertRegexpMatches and assertRaisesRegexp to assertRegex and assertRaisesRegex.
Modified:
python/branches/py3k/Doc/library/unittest.rst
python/branches/py3k/Lib/test/test_abc.py
python/branches/py3k/Lib/test/test_asyncore.py
python/branches/py3k/Lib/test/test_concurrent_futures.py
python/branches/py3k/Lib/test/test_contextlib.py
python/branches/py3k/Lib/test/test_dis.py
python/branches/py3k/Lib/test/test_memoryview.py
python/branches/py3k/Lib/test/test_runpy.py
python/branches/py3k/Lib/test/test_smtplib.py
python/branches/py3k/Lib/test/test_ssl.py
python/branches/py3k/Lib/test/test_unicode.py
python/branches/py3k/Lib/test/test_urlparse.py
python/branches/py3k/Lib/test/test_xmlrpc.py
python/branches/py3k/Lib/test/test_zlib.py
python/branches/py3k/Lib/unittest/case.py
python/branches/py3k/Lib/unittest/test/test_assertions.py
python/branches/py3k/Lib/unittest/test/test_case.py
python/branches/py3k/Lib/unittest/test/test_discovery.py
python/branches/py3k/Lib/unittest/test/test_loader.py
python/branches/py3k/Lib/unittest/test/test_setups.py
python/branches/py3k/Misc/NEWS
Modified: python/branches/py3k/Doc/library/unittest.rst
==============================================================================
--- python/branches/py3k/Doc/library/unittest.rst (original)
+++ python/branches/py3k/Doc/library/unittest.rst Wed Dec 1 03:32:32 2010
@@ -835,7 +835,7 @@
+-----------------------------------------+-----------------------------+---------------+
All the assert methods (except :meth:`assertRaises`,
- :meth:`assertRaisesRegexp`, :meth:`assertWarns`, :meth:`assertWarnsRegexp`)
+ :meth:`assertRaisesRegex`, :meth:`assertWarns`, :meth:`assertWarnsRegex`)
accept a *msg* argument that, if specified, is used as the error message on
failure (see also :data:`longMessage`).
@@ -919,14 +919,14 @@
| :meth:`assertRaises(exc, fun, *args, **kwds) | ``fun(*args, **kwds)`` raises `exc` | |
| ` | | |
+---------------------------------------------------------+--------------------------------------+------------+
- | :meth:`assertRaisesRegexp(exc, re, fun, *args, **kwds) | ``fun(*args, **kwds)`` raises `exc` | 3.1 |
- | ` | and the message matches `re` | |
+ | :meth:`assertRaisesRegex(exc, re, fun, *args, **kwds) | ``fun(*args, **kwds)`` raises `exc` | 3.1 |
+ | ` | and the message matches `re` | |
+---------------------------------------------------------+--------------------------------------+------------+
| :meth:`assertWarns(warn, fun, *args, **kwds) | ``fun(*args, **kwds)`` raises `warn` | 3.2 |
| ` | | |
+---------------------------------------------------------+--------------------------------------+------------+
- | :meth:`assertWarnsRegexp(warn, re, fun, *args, **kwds) | ``fun(*args, **kwds)`` raises `warn` | 3.2 |
- | ` | and the message matches `re` | |
+ | :meth:`assertWarnsRegex(warn, re, fun, *args, **kwds) | ``fun(*args, **kwds)`` raises `warn` | 3.2 |
+ | ` | and the message matches `re` | |
+---------------------------------------------------------+--------------------------------------+------------+
.. method:: assertRaises(exception, callable, *args, **kwds)
@@ -962,23 +962,25 @@
Added the :attr:`exception` attribute.
- .. method:: assertRaisesRegexp(exception, regexp, callable, *args, **kwds)
- assertRaisesRegexp(exception, regexp)
+ .. method:: assertRaisesRegex(exception, regex, callable, *args, **kwds)
+ assertRaisesRegex(exception, regex)
- Like :meth:`assertRaises` but also tests that *regexp* matches
- on the string representation of the raised exception. *regexp* may be
+ Like :meth:`assertRaises` but also tests that *regex* matches
+ on the string representation of the raised exception. *regex* may be
a regular expression object or a string containing a regular expression
suitable for use by :func:`re.search`. Examples::
- self.assertRaisesRegexp(ValueError, 'invalid literal for.*XYZ$',
- int, 'XYZ')
+ self.assertRaisesRegex(ValueError, 'invalid literal for.*XYZ$',
+ int, 'XYZ')
or::
- with self.assertRaisesRegexp(ValueError, 'literal'):
+ with self.assertRaisesRegex(ValueError, 'literal'):
int('XYZ')
- .. versionadded:: 3.1
+ .. versionadded:: 3.1 ``assertRaisesRegexp``
+ .. versionchanged:: 3.2
+ The method has been renamed to :meth:`assertRaisesRegex`
.. method:: assertWarns(warning, callable, *args, **kwds)
@@ -1015,21 +1017,21 @@
.. versionadded:: 3.2
- .. method:: assertWarnsRegexp(warning, regexp, callable, *args, **kwds)
- assertWarnsRegexp(warning, regexp)
+ .. method:: assertWarnsRegex(warning, regex, callable, *args, **kwds)
+ assertWarnsRegex(warning, regex)
- Like :meth:`assertWarns` but also tests that *regexp* matches on the
- message of the triggered warning. *regexp* may be a regular expression
+ Like :meth:`assertWarns` but also tests that *regex* matches on the
+ message of the triggered warning. *regex* may be a regular expression
object or a string containing a regular expression suitable for use
by :func:`re.search`. Example::
- self.assertWarnsRegexp(DeprecationWarning,
- r'legacy_function\(\) is deprecated',
- legacy_function, 'XYZ')
+ self.assertWarnsRegex(DeprecationWarning,
+ r'legacy_function\(\) is deprecated',
+ legacy_function, 'XYZ')
or::
- with self.assertWarnsRegexp(RuntimeWarning, 'unsafe frobnicating'):
+ with self.assertWarnsRegex(RuntimeWarning, 'unsafe frobnicating'):
frobnicate('/etc/passwd')
.. versionadded:: 3.2
@@ -1059,11 +1061,11 @@
| :meth:`assertLessEqual(a, b) | ``a <= b`` | 3.1 |
| ` | | |
+---------------------------------------+--------------------------------+--------------+
- | :meth:`assertRegexpMatches(s, re) | ``regex.search(s)`` | 3.1 |
- | ` | | |
+ | :meth:`assertRegex(s, re) | ``regex.search(s)`` | 3.1 |
+ | ` | | |
+---------------------------------------+--------------------------------+--------------+
- | :meth:`assertNotRegexpMatches(s, re) | ``not regex.search(s)`` | 3.2 |
- | ` | | |
+ | :meth:`assertNotRegex(s, re) | ``not regex.search(s)`` | 3.2 |
+ | ` | | |
+---------------------------------------+--------------------------------+--------------+
| :meth:`assertDictContainsSubset(a, b) | all the key/value pairs | 3.1 |
| ` | in `a` exist in `b` | |
@@ -1108,17 +1110,19 @@
.. versionadded:: 3.1
- .. method:: assertRegexpMatches(text, regexp, msg=None)
- assertNotRegexpMatches(text, regexp, msg=None)
+ .. method:: assertRegex(text, regex, msg=None)
+ assertNotRegex(text, regex, msg=None)
- Test that a *regexp* search matches (or does not match) *text*. In case
+ Test that a *regex* search matches (or does not match) *text*. In case
of failure, the error message will include the pattern and the *text* (or
- the pattern and the part of *text* that unexpectedly matched). *regexp*
+ the pattern and the part of *text* that unexpectedly matched). *regex*
may be a regular expression object or a string containing a regular
expression suitable for use by :func:`re.search`.
- .. versionadded:: 3.1 :meth:`~TestCase.assertRegexpMatches`
- .. versionadded:: 3.2 :meth:`~TestCase.assertNotRegexpMatches`
+ .. versionadded:: 3.1 ``.assertRegexpMatches``
+ .. versionchanged:: 3.2
+ ``.assertRegexpMatches`` has been renamed to :meth:`.assertRegex`
+ .. versionadded:: 3.2 :meth:`.assertNotRegex`
.. method:: assertDictContainsSubset(expected, actual, msg=None)
@@ -1420,13 +1424,17 @@
:meth:`.assertRaises` failUnlessRaises
:meth:`.assertAlmostEqual` failUnlessAlmostEqual assertAlmostEquals
:meth:`.assertNotAlmostEqual` failIfAlmostEqual assertNotAlmostEquals
+ :meth:`.assertRegex` assertRegexpMatches
+ :meth:`.assertRaisesRegex` assertRaisesRegexp
============================== ====================== ======================
.. deprecated-removed:: 3.1 3.3
the fail* aliases listed in the second column.
.. deprecated:: 3.2
the assert* aliases listed in the third column.
-
+ .. deprecated:: 3.2
+ ``assertRegexpMatches`` and ``assertRaisesRegexp`` have been renamed to
+ :meth:`.assertRegex` and :meth:`.assertRaisesRegex`
.. _testsuite-objects:
Modified: python/branches/py3k/Lib/test/test_abc.py
==============================================================================
--- python/branches/py3k/Lib/test/test_abc.py (original)
+++ python/branches/py3k/Lib/test/test_abc.py Wed Dec 1 03:32:32 2010
@@ -192,8 +192,8 @@
def test_register_non_class(self):
class A(metaclass=abc.ABCMeta):
pass
- self.assertRaisesRegexp(TypeError, "Can only register classes",
- A.register, 4)
+ self.assertRaisesRegex(TypeError, "Can only register classes",
+ A.register, 4)
def test_registration_transitiveness(self):
class A(metaclass=abc.ABCMeta):
Modified: python/branches/py3k/Lib/test/test_asyncore.py
==============================================================================
--- python/branches/py3k/Lib/test/test_asyncore.py (original)
+++ python/branches/py3k/Lib/test/test_asyncore.py Wed Dec 1 03:32:32 2010
@@ -312,8 +312,8 @@
d = asyncore.dispatcher(socket.socket())
# make sure the error message no longer refers to the socket
# object but the dispatcher instance instead
- self.assertRaisesRegexp(AttributeError, 'dispatcher instance',
- getattr, d, 'foo')
+ self.assertRaisesRegex(AttributeError, 'dispatcher instance',
+ getattr, d, 'foo')
# cheap inheritance with the underlying socket is supposed
# to still work but a DeprecationWarning is expected
with warnings.catch_warnings(record=True) as w:
Modified: python/branches/py3k/Lib/test/test_concurrent_futures.py
==============================================================================
--- python/branches/py3k/Lib/test/test_concurrent_futures.py (original)
+++ python/branches/py3k/Lib/test/test_concurrent_futures.py Wed Dec 1 03:32:32 2010
@@ -682,18 +682,18 @@
self.assertTrue(was_cancelled)
def test_repr(self):
- self.assertRegexpMatches(repr(PENDING_FUTURE),
- '')
- self.assertRegexpMatches(repr(RUNNING_FUTURE),
- '')
- self.assertRegexpMatches(repr(CANCELLED_FUTURE),
- '')
- self.assertRegexpMatches(repr(CANCELLED_AND_NOTIFIED_FUTURE),
- '')
- self.assertRegexpMatches(
+ self.assertRegex(repr(PENDING_FUTURE),
+ '')
+ self.assertRegex(repr(RUNNING_FUTURE),
+ '')
+ self.assertRegex(repr(CANCELLED_FUTURE),
+ '')
+ self.assertRegex(repr(CANCELLED_AND_NOTIFIED_FUTURE),
+ '')
+ self.assertRegex(
repr(EXCEPTION_FUTURE),
'')
- self.assertRegexpMatches(
+ self.assertRegex(
repr(SUCCESSFUL_FUTURE),
'')
Modified: python/branches/py3k/Lib/test/test_contextlib.py
==============================================================================
--- python/branches/py3k/Lib/test/test_contextlib.py (original)
+++ python/branches/py3k/Lib/test/test_contextlib.py Wed Dec 1 03:32:32 2010
@@ -231,7 +231,7 @@
def test_contextdecorator_with_exception(self):
context = mycontext()
- with self.assertRaisesRegexp(NameError, 'foo'):
+ with self.assertRaisesRegex(NameError, 'foo'):
with context:
raise NameError('foo')
self.assertIsNotNone(context.exc)
@@ -265,7 +265,7 @@
self.assertTrue(context.started)
raise NameError('foo')
- with self.assertRaisesRegexp(NameError, 'foo'):
+ with self.assertRaisesRegex(NameError, 'foo'):
test()
self.assertIsNotNone(context.exc)
self.assertIs(context.exc[0], NameError)
Modified: python/branches/py3k/Lib/test/test_dis.py
==============================================================================
--- python/branches/py3k/Lib/test/test_dis.py (original)
+++ python/branches/py3k/Lib/test/test_dis.py Wed Dec 1 03:32:32 2010
@@ -354,14 +354,14 @@
def test_code_info(self):
self.maxDiff = 1000
for x, expected in self.test_pairs:
- self.assertRegexpMatches(dis.code_info(x), expected)
+ self.assertRegex(dis.code_info(x), expected)
def test_show_code(self):
self.maxDiff = 1000
for x, expected in self.test_pairs:
with captured_stdout() as output:
dis.show_code(x)
- self.assertRegexpMatches(output.getvalue(), expected+"\n")
+ self.assertRegex(output.getvalue(), expected+"\n")
def test_main():
run_unittest(DisTests, CodeInfoTests)
Modified: python/branches/py3k/Lib/test/test_memoryview.py
==============================================================================
--- python/branches/py3k/Lib/test/test_memoryview.py (original)
+++ python/branches/py3k/Lib/test/test_memoryview.py Wed Dec 1 03:32:32 2010
@@ -226,7 +226,7 @@
self.assertTrue(wr() is None, wr())
def _check_released(self, m, tp):
- check = self.assertRaisesRegexp(ValueError, "released")
+ check = self.assertRaisesRegex(ValueError, "released")
with check: bytes(m)
with check: m.tobytes()
with check: m.tolist()
Modified: python/branches/py3k/Lib/test/test_runpy.py
==============================================================================
--- python/branches/py3k/Lib/test/test_runpy.py (original)
+++ python/branches/py3k/Lib/test/test_runpy.py Wed Dec 1 03:32:32 2010
@@ -329,7 +329,7 @@
def _check_import_error(self, script_name, msg):
msg = re.escape(msg)
- self.assertRaisesRegexp(ImportError, msg, run_path, script_name)
+ self.assertRaisesRegex(ImportError, msg, run_path, script_name)
def test_basic_script(self):
with temp_dir() as script_dir:
@@ -403,7 +403,7 @@
script_name = self._make_test_script(script_dir, mod_name, source)
zip_name, fname = make_zip_script(script_dir, 'test_zip', script_name)
msg = "recursion depth exceeded"
- self.assertRaisesRegexp(RuntimeError, msg, run_path, zip_name)
+ self.assertRaisesRegex(RuntimeError, msg, run_path, zip_name)
Modified: python/branches/py3k/Lib/test/test_smtplib.py
==============================================================================
--- python/branches/py3k/Lib/test/test_smtplib.py (original)
+++ python/branches/py3k/Lib/test/test_smtplib.py Wed Dec 1 03:32:32 2010
@@ -319,12 +319,12 @@
self.assertEqual(self.output.getvalue(), mexpect)
debugout = smtpd.DEBUGSTREAM.getvalue()
sender = re.compile("^sender: foo at bar.com$", re.MULTILINE)
- self.assertRegexpMatches(debugout, sender)
+ self.assertRegex(debugout, sender)
for addr in ('John', 'Sally', 'Fred', 'root at localhost',
'warped at silly.walks.com'):
to_addr = re.compile(r"^recips: .*'{}'.*$".format(addr),
re.MULTILINE)
- self.assertRegexpMatches(debugout, to_addr)
+ self.assertRegex(debugout, to_addr)
def testSendMessageWithSomeAddresses(self):
# Make sure nothing breaks if not all of the three 'to' headers exist
@@ -347,11 +347,11 @@
self.assertEqual(self.output.getvalue(), mexpect)
debugout = smtpd.DEBUGSTREAM.getvalue()
sender = re.compile("^sender: foo at bar.com$", re.MULTILINE)
- self.assertRegexpMatches(debugout, sender)
+ self.assertRegex(debugout, sender)
for addr in ('John', 'Dinsdale'):
to_addr = re.compile(r"^recips: .*'{}'.*$".format(addr),
re.MULTILINE)
- self.assertRegexpMatches(debugout, to_addr)
+ self.assertRegex(debugout, to_addr)
class NonConnectingTests(unittest.TestCase):
Modified: python/branches/py3k/Lib/test/test_ssl.py
==============================================================================
--- python/branches/py3k/Lib/test/test_ssl.py (original)
+++ python/branches/py3k/Lib/test/test_ssl.py Wed Dec 1 03:32:32 2010
@@ -185,17 +185,17 @@
def test_errors(self):
sock = socket.socket()
- self.assertRaisesRegexp(ValueError,
+ self.assertRaisesRegex(ValueError,
"certfile must be specified",
ssl.wrap_socket, sock, keyfile=CERTFILE)
- self.assertRaisesRegexp(ValueError,
+ self.assertRaisesRegex(ValueError,
"certfile must be specified for server-side operations",
ssl.wrap_socket, sock, server_side=True)
- self.assertRaisesRegexp(ValueError,
+ self.assertRaisesRegex(ValueError,
"certfile must be specified for server-side operations",
ssl.wrap_socket, sock, server_side=True, certfile="")
s = ssl.wrap_socket(sock, server_side=True, certfile=CERTFILE)
- self.assertRaisesRegexp(ValueError, "can't connect in server-side mode",
+ self.assertRaisesRegex(ValueError, "can't connect in server-side mode",
s.connect, (HOST, 8080))
with self.assertRaises(IOError) as cm:
with socket.socket() as sock:
@@ -310,7 +310,7 @@
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
ctx.set_ciphers("ALL")
ctx.set_ciphers("DEFAULT")
- with self.assertRaisesRegexp(ssl.SSLError, "No cipher can be selected"):
+ with self.assertRaisesRegex(ssl.SSLError, "No cipher can be selected"):
ctx.set_ciphers("^$:,;?*'dorothyx")
@skip_if_broken_ubuntu_ssl
@@ -358,24 +358,24 @@
with self.assertRaises(IOError) as cm:
ctx.load_cert_chain(WRONGCERT)
self.assertEqual(cm.exception.errno, errno.ENOENT)
- with self.assertRaisesRegexp(ssl.SSLError, "PEM lib"):
+ with self.assertRaisesRegex(ssl.SSLError, "PEM lib"):
ctx.load_cert_chain(BADCERT)
- with self.assertRaisesRegexp(ssl.SSLError, "PEM lib"):
+ with self.assertRaisesRegex(ssl.SSLError, "PEM lib"):
ctx.load_cert_chain(EMPTYCERT)
# Separate key and cert
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
ctx.load_cert_chain(ONLYCERT, ONLYKEY)
ctx.load_cert_chain(certfile=ONLYCERT, keyfile=ONLYKEY)
ctx.load_cert_chain(certfile=BYTES_ONLYCERT, keyfile=BYTES_ONLYKEY)
- with self.assertRaisesRegexp(ssl.SSLError, "PEM lib"):
+ with self.assertRaisesRegex(ssl.SSLError, "PEM lib"):
ctx.load_cert_chain(ONLYCERT)
- with self.assertRaisesRegexp(ssl.SSLError, "PEM lib"):
+ with self.assertRaisesRegex(ssl.SSLError, "PEM lib"):
ctx.load_cert_chain(ONLYKEY)
- with self.assertRaisesRegexp(ssl.SSLError, "PEM lib"):
+ with self.assertRaisesRegex(ssl.SSLError, "PEM lib"):
ctx.load_cert_chain(certfile=ONLYKEY, keyfile=ONLYCERT)
# Mismatching key and cert
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
- with self.assertRaisesRegexp(ssl.SSLError, "key values mismatch"):
+ with self.assertRaisesRegex(ssl.SSLError, "key values mismatch"):
ctx.load_cert_chain(SVN_PYTHON_ORG_ROOT_CERT, ONLYKEY)
def test_load_verify_locations(self):
@@ -389,7 +389,7 @@
with self.assertRaises(IOError) as cm:
ctx.load_verify_locations(WRONGCERT)
self.assertEqual(cm.exception.errno, errno.ENOENT)
- with self.assertRaisesRegexp(ssl.SSLError, "PEM lib"):
+ with self.assertRaisesRegex(ssl.SSLError, "PEM lib"):
ctx.load_verify_locations(BADCERT)
ctx.load_verify_locations(CERTFILE, CAPATH)
ctx.load_verify_locations(CERTFILE, capath=BYTES_CAPATH)
@@ -434,8 +434,8 @@
# this should fail because we have no verification certs
s = ssl.wrap_socket(socket.socket(socket.AF_INET),
cert_reqs=ssl.CERT_REQUIRED)
- self.assertRaisesRegexp(ssl.SSLError, "certificate verify failed",
- s.connect, ("svn.python.org", 443))
+ self.assertRaisesRegex(ssl.SSLError, "certificate verify failed",
+ s.connect, ("svn.python.org", 443))
s.close()
# this should succeed because we specify the root cert
@@ -469,7 +469,7 @@
# This should fail because we have no verification certs
ctx.verify_mode = ssl.CERT_REQUIRED
s = ctx.wrap_socket(socket.socket(socket.AF_INET))
- self.assertRaisesRegexp(ssl.SSLError, "certificate verify failed",
+ self.assertRaisesRegex(ssl.SSLError, "certificate verify failed",
s.connect, ("svn.python.org", 443))
s.close()
# This should succeed because we specify the root cert
@@ -587,7 +587,7 @@
cert_reqs=ssl.CERT_NONE, ciphers="DEFAULT")
s.connect(remote)
# Error checking can happen at instantiation or when connecting
- with self.assertRaisesRegexp(ssl.SSLError, "No cipher can be selected"):
+ with self.assertRaisesRegex(ssl.SSLError, "No cipher can be selected"):
with socket.socket(socket.AF_INET) as sock:
s = ssl.wrap_socket(sock,
cert_reqs=ssl.CERT_NONE, ciphers="^$:,;?*'dorothyx")
@@ -1499,8 +1499,8 @@
c.settimeout(0.2)
c.connect((host, port))
# Will attempt handshake and time out
- self.assertRaisesRegexp(ssl.SSLError, "timed out",
- ssl.wrap_socket, c)
+ self.assertRaisesRegex(ssl.SSLError, "timed out",
+ ssl.wrap_socket, c)
finally:
c.close()
try:
@@ -1508,8 +1508,8 @@
c = ssl.wrap_socket(c)
c.settimeout(0.2)
# Will attempt handshake and time out
- self.assertRaisesRegexp(ssl.SSLError, "timed out",
- c.connect, (host, port))
+ self.assertRaisesRegex(ssl.SSLError, "timed out",
+ c.connect, (host, port))
finally:
c.close()
finally:
Modified: python/branches/py3k/Lib/test/test_unicode.py
==============================================================================
--- python/branches/py3k/Lib/test/test_unicode.py (original)
+++ python/branches/py3k/Lib/test/test_unicode.py Wed Dec 1 03:32:32 2010
@@ -1427,7 +1427,7 @@
# non-ascii format, ascii argument: ensure that PyUnicode_FromFormat()
# raises an error for a non-ascii format string.
- self.assertRaisesRegexp(ValueError,
+ self.assertRaisesRegex(ValueError,
'^PyUnicode_FromFormatV\(\) expects an ASCII-encoded format '
'string, got a non-ASCII byte: 0xe9$',
format_unicode, b'unicode\xe9=%s', 'ascii')
Modified: python/branches/py3k/Lib/test/test_urlparse.py
==============================================================================
--- python/branches/py3k/Lib/test/test_urlparse.py (original)
+++ python/branches/py3k/Lib/test/test_urlparse.py Wed Dec 1 03:32:32 2010
@@ -629,25 +629,25 @@
def test_mixed_types_rejected(self):
# Several functions that process either strings or ASCII encoded bytes
# accept multiple arguments. Check they reject mixed type input
- with self.assertRaisesRegexp(TypeError, "Cannot mix str"):
+ with self.assertRaisesRegex(TypeError, "Cannot mix str"):
urllib.parse.urlparse("www.python.org", b"http")
- with self.assertRaisesRegexp(TypeError, "Cannot mix str"):
+ with self.assertRaisesRegex(TypeError, "Cannot mix str"):
urllib.parse.urlparse(b"www.python.org", "http")
- with self.assertRaisesRegexp(TypeError, "Cannot mix str"):
+ with self.assertRaisesRegex(TypeError, "Cannot mix str"):
urllib.parse.urlsplit("www.python.org", b"http")
- with self.assertRaisesRegexp(TypeError, "Cannot mix str"):
+ with self.assertRaisesRegex(TypeError, "Cannot mix str"):
urllib.parse.urlsplit(b"www.python.org", "http")
- with self.assertRaisesRegexp(TypeError, "Cannot mix str"):
+ with self.assertRaisesRegex(TypeError, "Cannot mix str"):
urllib.parse.urlunparse(( b"http", "www.python.org","","","",""))
- with self.assertRaisesRegexp(TypeError, "Cannot mix str"):
+ with self.assertRaisesRegex(TypeError, "Cannot mix str"):
urllib.parse.urlunparse(("http", b"www.python.org","","","",""))
- with self.assertRaisesRegexp(TypeError, "Cannot mix str"):
+ with self.assertRaisesRegex(TypeError, "Cannot mix str"):
urllib.parse.urlunsplit((b"http", "www.python.org","","",""))
- with self.assertRaisesRegexp(TypeError, "Cannot mix str"):
+ with self.assertRaisesRegex(TypeError, "Cannot mix str"):
urllib.parse.urlunsplit(("http", b"www.python.org","","",""))
- with self.assertRaisesRegexp(TypeError, "Cannot mix str"):
+ with self.assertRaisesRegex(TypeError, "Cannot mix str"):
urllib.parse.urljoin("http://python.org", b"http://python.org")
- with self.assertRaisesRegexp(TypeError, "Cannot mix str"):
+ with self.assertRaisesRegex(TypeError, "Cannot mix str"):
urllib.parse.urljoin(b"http://python.org", "http://python.org")
def _check_result_type(self, str_type):
Modified: python/branches/py3k/Lib/test/test_xmlrpc.py
==============================================================================
--- python/branches/py3k/Lib/test/test_xmlrpc.py (original)
+++ python/branches/py3k/Lib/test/test_xmlrpc.py Wed Dec 1 03:32:32 2010
@@ -715,8 +715,8 @@
t.encode_threshold = None
t.fake_gzip = True
p = xmlrpclib.ServerProxy(URL, transport=t)
- cm = self.assertRaisesRegexp(xmlrpclib.ProtocolError,
- re.compile(r"\b400\b"))
+ cm = self.assertRaisesRegex(xmlrpclib.ProtocolError,
+ re.compile(r"\b400\b"))
with cm:
p.pow(6, 8)
Modified: python/branches/py3k/Lib/test/test_zlib.py
==============================================================================
--- python/branches/py3k/Lib/test/test_zlib.py (original)
+++ python/branches/py3k/Lib/test/test_zlib.py Wed Dec 1 03:32:32 2010
@@ -143,7 +143,7 @@
def test_incomplete_stream(self):
# An useful error message is given
x = zlib.compress(HAMLET_SCENE)
- self.assertRaisesRegexp(zlib.error,
+ self.assertRaisesRegex(zlib.error,
"Error -5 while decompressing data: incomplete or truncated stream",
zlib.decompress, x[:-1])
Modified: python/branches/py3k/Lib/unittest/case.py
==============================================================================
--- python/branches/py3k/Lib/unittest/case.py (original)
+++ python/branches/py3k/Lib/unittest/case.py Wed Dec 1 03:32:32 2010
@@ -94,7 +94,7 @@
class _AssertRaisesBaseContext(object):
def __init__(self, expected, test_case, callable_obj=None,
- expected_regexp=None):
+ expected_regex=None):
self.expected = expected
self.failureException = test_case.failureException
if callable_obj is not None:
@@ -104,9 +104,9 @@
self.obj_name = str(callable_obj)
else:
self.obj_name = None
- if isinstance(expected_regexp, (bytes, str)):
- expected_regexp = re.compile(expected_regexp)
- self.expected_regexp = expected_regexp
+ if isinstance(expected_regex, (bytes, str)):
+ expected_regex = re.compile(expected_regex)
+ self.expected_regex = expected_regex
class _AssertRaisesContext(_AssertRaisesBaseContext):
@@ -132,13 +132,13 @@
return False
# store exception, without traceback, for later retrieval
self.exception = exc_value.with_traceback(None)
- if self.expected_regexp is None:
+ if self.expected_regex is None:
return True
- expected_regexp = self.expected_regexp
- if not expected_regexp.search(str(exc_value)):
+ expected_regex = self.expected_regex
+ if not expected_regex.search(str(exc_value)):
raise self.failureException('"%s" does not match "%s"' %
- (expected_regexp.pattern, str(exc_value)))
+ (expected_regex.pattern, str(exc_value)))
return True
@@ -172,8 +172,8 @@
continue
if first_matching is None:
first_matching = w
- if (self.expected_regexp is not None and
- not self.expected_regexp.search(str(w))):
+ if (self.expected_regex is not None and
+ not self.expected_regex.search(str(w))):
continue
# store warning for later retrieval
self.warning = w
@@ -183,7 +183,7 @@
# Now we simply try to choose a helpful failure message
if first_matching is not None:
raise self.failureException('"%s" does not match "%s"' %
- (self.expected_regexp.pattern, str(first_matching)))
+ (self.expected_regex.pattern, str(first_matching)))
if self.obj_name:
raise self.failureException("{0} not triggered by {1}"
.format(exc_name, self.obj_name))
@@ -689,24 +689,6 @@
raise self.failureException(msg)
- def _deprecate(original_func):
- def deprecated_func(*args, **kwargs):
- warnings.warn(
- 'Please use {0} instead.'.format(original_func.__name__),
- DeprecationWarning, 2)
- return original_func(*args, **kwargs)
- return deprecated_func
-
- # The fail* methods can be removed in 3.3, the 5 assert* methods will
- # have to stay around for a few more versions. See #9424.
- failUnlessEqual = assertEquals = _deprecate(assertEqual)
- failIfEqual = assertNotEquals = _deprecate(assertNotEqual)
- failUnlessAlmostEqual = assertAlmostEquals = _deprecate(assertAlmostEqual)
- failIfAlmostEqual = assertNotAlmostEquals = _deprecate(assertNotAlmostEqual)
- failUnless = assert_ = _deprecate(assertTrue)
- failUnlessRaises = _deprecate(assertRaises)
- failIf = _deprecate(assertFalse)
-
def assertSequenceEqual(self, seq1, seq2, msg=None, seq_type=None):
"""An equality assertion for ordered sequences (like lists and tuples).
@@ -1095,27 +1077,27 @@
standardMsg = '%s is an instance of %r' % (safe_repr(obj), cls)
self.fail(self._formatMessage(msg, standardMsg))
- def assertRaisesRegexp(self, expected_exception, expected_regexp,
- callable_obj=None, *args, **kwargs):
- """Asserts that the message in a raised exception matches a regexp.
+ def assertRaisesRegex(self, expected_exception, expected_regex,
+ callable_obj=None, *args, **kwargs):
+ """Asserts that the message in a raised exception matches a regex.
Args:
expected_exception: Exception class expected to be raised.
- expected_regexp: Regexp (re pattern object or string) expected
+ expected_regex: Regex (re pattern object or string) expected
to be found in error message.
callable_obj: Function to be called.
args: Extra args.
kwargs: Extra kwargs.
"""
context = _AssertRaisesContext(expected_exception, self, callable_obj,
- expected_regexp)
+ expected_regex)
if callable_obj is None:
return context
with context:
callable_obj(*args, **kwargs)
- def assertWarnsRegexp(self, expected_warning, expected_regexp,
- callable_obj=None, *args, **kwargs):
+ def assertWarnsRegex(self, expected_warning, expected_regex,
+ callable_obj=None, *args, **kwargs):
"""Asserts that the message in a triggered warning matches a regexp.
Basic functioning is similar to assertWarns() with the addition
that only warnings whose messages also match the regular expression
@@ -1123,42 +1105,64 @@
Args:
expected_warning: Warning class expected to be triggered.
- expected_regexp: Regexp (re pattern object or string) expected
+ expected_regex: Regex (re pattern object or string) expected
to be found in error message.
callable_obj: Function to be called.
args: Extra args.
kwargs: Extra kwargs.
"""
context = _AssertWarnsContext(expected_warning, self, callable_obj,
- expected_regexp)
+ expected_regex)
if callable_obj is None:
return context
with context:
callable_obj(*args, **kwargs)
- def assertRegexpMatches(self, text, expected_regexp, msg=None):
+ def assertRegex(self, text, expected_regex, msg=None):
"""Fail the test unless the text matches the regular expression."""
- if isinstance(expected_regexp, (str, bytes)):
- expected_regexp = re.compile(expected_regexp)
- if not expected_regexp.search(text):
- msg = msg or "Regexp didn't match"
- msg = '%s: %r not found in %r' % (msg, expected_regexp.pattern, text)
+ if isinstance(expected_regex, (str, bytes)):
+ expected_regex = re.compile(expected_regex)
+ if not expected_regex.search(text):
+ msg = msg or "Regex didn't match"
+ msg = '%s: %r not found in %r' % (msg, expected_regex.pattern, text)
raise self.failureException(msg)
- def assertNotRegexpMatches(self, text, unexpected_regexp, msg=None):
+ def assertNotRegexMatches(self, text, unexpected_regex, msg=None):
"""Fail the test if the text matches the regular expression."""
- if isinstance(unexpected_regexp, (str, bytes)):
- unexpected_regexp = re.compile(unexpected_regexp)
- match = unexpected_regexp.search(text)
+ if isinstance(unexpected_regex, (str, bytes)):
+ unexpected_regex = re.compile(unexpected_regex)
+ match = unexpected_regex.search(text)
if match:
- msg = msg or "Regexp matched"
+ msg = msg or "Regex matched"
msg = '%s: %r matches %r in %r' % (msg,
text[match.start():match.end()],
- unexpected_regexp.pattern,
+ unexpected_regex.pattern,
text)
raise self.failureException(msg)
+ def _deprecate(original_func):
+ def deprecated_func(*args, **kwargs):
+ warnings.warn(
+ 'Please use {0} instead.'.format(original_func.__name__),
+ DeprecationWarning, 2)
+ return original_func(*args, **kwargs)
+ return deprecated_func
+
+ # The fail* methods can be removed in 3.3, the 5 assert* methods will
+ # have to stay around for a few more versions. See #9424.
+ failUnlessEqual = assertEquals = _deprecate(assertEqual)
+ failIfEqual = assertNotEquals = _deprecate(assertNotEqual)
+ failUnlessAlmostEqual = assertAlmostEquals = _deprecate(assertAlmostEqual)
+ failIfAlmostEqual = assertNotAlmostEquals = _deprecate(assertNotAlmostEqual)
+ failUnless = assert_ = _deprecate(assertTrue)
+ failUnlessRaises = _deprecate(assertRaises)
+ failIf = _deprecate(assertFalse)
+ assertRaisesRegexp = _deprecate(assertRaisesRegex)
+ assertRegexpMatches = _deprecate(assertRegex)
+
+
+
class FunctionTestCase(TestCase):
"""A test case that wraps a test function.
Modified: python/branches/py3k/Lib/unittest/test/test_assertions.py
==============================================================================
--- python/branches/py3k/Lib/unittest/test/test_assertions.py (original)
+++ python/branches/py3k/Lib/unittest/test/test_assertions.py Wed Dec 1 03:32:32 2010
@@ -92,15 +92,15 @@
else:
self.fail("assertRaises() didn't let exception pass through")
- def testAssertNotRegexpMatches(self):
- self.assertNotRegexpMatches('Ala ma kota', r'r+')
+ def testAssertNotRegexMatches(self):
+ self.assertNotRegexMatches('Ala ma kota', r'r+')
try:
- self.assertNotRegexpMatches('Ala ma kota', r'k.t', 'Message')
+ self.assertNotRegexMatches('Ala ma kota', r'k.t', 'Message')
except self.failureException as e:
self.assertIn("'kot'", e.args[0])
self.assertIn('Message', e.args[0])
else:
- self.fail('assertNotRegexpMatches should have failed.')
+ self.fail('assertNotRegexMatches should have failed.')
class TestLongMessage(unittest.TestCase):
@@ -153,15 +153,15 @@
test = self.testableTrue
return getattr(test, methodName)
- for i, expected_regexp in enumerate(errors):
+ for i, expected_regex in enumerate(errors):
testMethod = getMethod(i)
kwargs = {}
withMsg = i % 2
if withMsg:
kwargs = {"msg": "oops"}
- with self.assertRaisesRegexp(self.failureException,
- expected_regexp=expected_regexp):
+ with self.assertRaisesRegex(self.failureException,
+ expected_regex=expected_regex):
testMethod(*args, **kwargs)
def testAssertTrue(self):
Modified: python/branches/py3k/Lib/unittest/test/test_case.py
==============================================================================
--- python/branches/py3k/Lib/unittest/test/test_case.py (original)
+++ python/branches/py3k/Lib/unittest/test/test_case.py Wed Dec 1 03:32:32 2010
@@ -872,44 +872,44 @@
self.assertIsNotNone('DjZoPloGears on Rails')
self.assertRaises(self.failureException, self.assertIsNotNone, None)
- def testAssertRegexpMatches(self):
- self.assertRegexpMatches('asdfabasdf', r'ab+')
- self.assertRaises(self.failureException, self.assertRegexpMatches,
+ def testAssertRegex(self):
+ self.assertRegex('asdfabasdf', r'ab+')
+ self.assertRaises(self.failureException, self.assertRegex,
'saaas', r'aaaa')
- def testAssertRaisesRegexp(self):
+ def testAssertRaisesRegex(self):
class ExceptionMock(Exception):
pass
def Stub():
raise ExceptionMock('We expect')
- self.assertRaisesRegexp(ExceptionMock, re.compile('expect$'), Stub)
- self.assertRaisesRegexp(ExceptionMock, 'expect$', Stub)
+ self.assertRaisesRegex(ExceptionMock, re.compile('expect$'), Stub)
+ self.assertRaisesRegex(ExceptionMock, 'expect$', Stub)
- def testAssertNotRaisesRegexp(self):
- self.assertRaisesRegexp(
+ def testAssertNotRaisesRegex(self):
+ self.assertRaisesRegex(
self.failureException, '^Exception not raised by $',
- self.assertRaisesRegexp, Exception, re.compile('x'),
+ self.assertRaisesRegex, Exception, re.compile('x'),
lambda: None)
- self.assertRaisesRegexp(
+ self.assertRaisesRegex(
self.failureException, '^Exception not raised by $',
- self.assertRaisesRegexp, Exception, 'x',
+ self.assertRaisesRegex, Exception, 'x',
lambda: None)
- def testAssertRaisesRegexpMismatch(self):
+ def testAssertRaisesRegexMismatch(self):
def Stub():
raise Exception('Unexpected')
- self.assertRaisesRegexp(
+ self.assertRaisesRegex(
self.failureException,
r'"\^Expected\$" does not match "Unexpected"',
- self.assertRaisesRegexp, Exception, '^Expected$',
+ self.assertRaisesRegex, Exception, '^Expected$',
Stub)
- self.assertRaisesRegexp(
+ self.assertRaisesRegex(
self.failureException,
r'"\^Expected\$" does not match "Unexpected"',
- self.assertRaisesRegexp, Exception,
+ self.assertRaisesRegex, Exception,
re.compile('^Expected$'), Stub)
def testAssertRaisesExcValue(self):
@@ -993,26 +993,26 @@
with self.assertWarns(DeprecationWarning):
_runtime_warn()
- def testAssertWarnsRegexpCallable(self):
+ def testAssertWarnsRegexCallable(self):
def _runtime_warn(msg):
warnings.warn(msg, RuntimeWarning)
- self.assertWarnsRegexp(RuntimeWarning, "o+",
- _runtime_warn, "foox")
+ self.assertWarnsRegex(RuntimeWarning, "o+",
+ _runtime_warn, "foox")
# Failure when no warning is triggered
with self.assertRaises(self.failureException):
- self.assertWarnsRegexp(RuntimeWarning, "o+",
- lambda: 0)
+ self.assertWarnsRegex(RuntimeWarning, "o+",
+ lambda: 0)
# Failure when another warning is triggered
with warnings.catch_warnings():
# Force default filter (in case tests are run with -We)
warnings.simplefilter("default", RuntimeWarning)
with self.assertRaises(self.failureException):
- self.assertWarnsRegexp(DeprecationWarning, "o+",
- _runtime_warn, "foox")
+ self.assertWarnsRegex(DeprecationWarning, "o+",
+ _runtime_warn, "foox")
# Failure when message doesn't match
with self.assertRaises(self.failureException):
- self.assertWarnsRegexp(RuntimeWarning, "o+",
- _runtime_warn, "barz")
+ self.assertWarnsRegex(RuntimeWarning, "o+",
+ _runtime_warn, "barz")
# A little trickier: we ask RuntimeWarnings to be raised, and then
# check for some of them. It is implementation-defined whether
# non-matching RuntimeWarnings are simply re-raised, or produce a
@@ -1020,15 +1020,15 @@
with warnings.catch_warnings():
warnings.simplefilter("error", RuntimeWarning)
with self.assertRaises((RuntimeWarning, self.failureException)):
- self.assertWarnsRegexp(RuntimeWarning, "o+",
- _runtime_warn, "barz")
+ self.assertWarnsRegex(RuntimeWarning, "o+",
+ _runtime_warn, "barz")
- def testAssertWarnsRegexpContext(self):
- # Same as above, but with assertWarnsRegexp as a context manager
+ def testAssertWarnsRegexContext(self):
+ # Same as above, but with assertWarnsRegex as a context manager
def _runtime_warn(msg):
warnings.warn(msg, RuntimeWarning)
_runtime_warn_lineno = inspect.getsourcelines(_runtime_warn)[1]
- with self.assertWarnsRegexp(RuntimeWarning, "o+") as cm:
+ with self.assertWarnsRegex(RuntimeWarning, "o+") as cm:
_runtime_warn("foox")
self.assertIsInstance(cm.warning, RuntimeWarning)
self.assertEqual(cm.warning.args[0], "foox")
@@ -1036,18 +1036,18 @@
self.assertEqual(cm.lineno, _runtime_warn_lineno + 1)
# Failure when no warning is triggered
with self.assertRaises(self.failureException):
- with self.assertWarnsRegexp(RuntimeWarning, "o+"):
+ with self.assertWarnsRegex(RuntimeWarning, "o+"):
pass
# Failure when another warning is triggered
with warnings.catch_warnings():
# Force default filter (in case tests are run with -We)
warnings.simplefilter("default", RuntimeWarning)
with self.assertRaises(self.failureException):
- with self.assertWarnsRegexp(DeprecationWarning, "o+"):
+ with self.assertWarnsRegex(DeprecationWarning, "o+"):
_runtime_warn("foox")
# Failure when message doesn't match
with self.assertRaises(self.failureException):
- with self.assertWarnsRegexp(RuntimeWarning, "o+"):
+ with self.assertWarnsRegex(RuntimeWarning, "o+"):
_runtime_warn("barz")
# A little trickier: we ask RuntimeWarnings to be raised, and then
# check for some of them. It is implementation-defined whether
@@ -1056,7 +1056,7 @@
with warnings.catch_warnings():
warnings.simplefilter("error", RuntimeWarning)
with self.assertRaises((RuntimeWarning, self.failureException)):
- with self.assertWarnsRegexp(RuntimeWarning, "o+"):
+ with self.assertWarnsRegex(RuntimeWarning, "o+"):
_runtime_warn("barz")
def testDeprecatedMethodNames(self):
@@ -1078,7 +1078,9 @@
(self.assert_, (True,)),
(self.failUnlessRaises, (TypeError, lambda _: 3.14 + 'spam')),
(self.failIf, (False,)),
- (self.assertSameElements, ([1, 1, 2, 3], [1, 2, 3]))
+ (self.assertSameElements, ([1, 1, 2, 3], [1, 2, 3])),
+ (self.assertRaisesRegexp, (KeyError, 'foo', lambda: {}['foo'])),
+ (self.assertRegexpMatches, ('bar', 'bar')),
)
for meth, args in old:
with self.assertWarns(DeprecationWarning):
Modified: python/branches/py3k/Lib/unittest/test/test_discovery.py
==============================================================================
--- python/branches/py3k/Lib/unittest/test/test_discovery.py (original)
+++ python/branches/py3k/Lib/unittest/test/test_discovery.py Wed Dec 1 03:32:32 2010
@@ -354,7 +354,7 @@
expected_dir = os.path.abspath('foo')
msg = re.escape(r"'foo' module incorrectly imported from %r. Expected %r. "
"Is this module globally installed?" % (mod_dir, expected_dir))
- self.assertRaisesRegexp(
+ self.assertRaisesRegex(
ImportError, '^%s$' % msg, loader.discover,
start_dir='foo', pattern='foo.py'
)
Modified: python/branches/py3k/Lib/unittest/test/test_loader.py
==============================================================================
--- python/branches/py3k/Lib/unittest/test/test_loader.py (original)
+++ python/branches/py3k/Lib/unittest/test/test_loader.py Wed Dec 1 03:32:32 2010
@@ -186,7 +186,7 @@
self.assertEqual(suite.countTestCases(), 1)
test = list(suite)[0]
- self.assertRaisesRegexp(TypeError, "some failure", test.m)
+ self.assertRaisesRegex(TypeError, "some failure", test.m)
################################################################
### /Tests for TestLoader.loadTestsFromModule()
Modified: python/branches/py3k/Lib/unittest/test/test_setups.py
==============================================================================
--- python/branches/py3k/Lib/unittest/test/test_setups.py (original)
+++ python/branches/py3k/Lib/unittest/test/test_setups.py Wed Dec 1 03:32:32 2010
@@ -500,7 +500,7 @@
messages = ('setUpModule', 'tearDownModule', 'setUpClass', 'tearDownClass', 'test_something')
for phase, msg in enumerate(messages):
- with self.assertRaisesRegexp(Exception, msg):
+ with self.assertRaisesRegex(Exception, msg):
suite.debug()
if __name__ == '__main__':
Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS (original)
+++ python/branches/py3k/Misc/NEWS Wed Dec 1 03:32:32 2010
@@ -46,6 +46,9 @@
Library
-------
+- Issue #10273: Rename `assertRegexpMatches` and `assertRaisesRegexp` to
+ `assertRegex` and `assertRaisesRegex`.
+
- Issue #10535: Enable silenced warnings in unittest by default.
- Issue #9873: The URL parsing functions in urllib.parse now accept
From python-checkins at python.org Wed Dec 1 04:45:41 2010
From: python-checkins at python.org (raymond.hettinger)
Date: Wed, 1 Dec 2010 04:45:41 +0100 (CET)
Subject: [Python-checkins] r86911 - in python/branches/py3k:
Doc/library/functools.rst Lib/functools.py
Lib/test/test_functools.py
Message-ID: <20101201034541.46C66EEA2B@mail.python.org>
Author: raymond.hettinger
Date: Wed Dec 1 04:45:41 2010
New Revision: 86911
Log:
Issue 10593: Adopt Nick's suggestion for an lru_cache with maxsize=None.
Modified:
python/branches/py3k/Doc/library/functools.rst
python/branches/py3k/Lib/functools.py
python/branches/py3k/Lib/test/test_functools.py
Modified: python/branches/py3k/Doc/library/functools.rst
==============================================================================
--- python/branches/py3k/Doc/library/functools.rst (original)
+++ python/branches/py3k/Doc/library/functools.rst Wed Dec 1 04:45:41 2010
@@ -32,7 +32,7 @@
A compare function is any callable that accept two arguments, compares them,
and returns a negative number for less-than, zero for equality, or a positive
number for greater-than. A key function is a callable that accepts one
- argument and returns another value that indicates the position in the desired
+ argument and returns another value indicating the position in the desired
collation sequence.
Example::
@@ -51,10 +51,14 @@
Since a dictionary is used to cache results, the positional and keyword
arguments to the function must be hashable.
+ If *maxsize* is set to None, the LRU feature is disabled and the cache
+ can grow without bound.
+
To help measure the effectiveness of the cache and tune the *maxsize*
parameter, the wrapped function is instrumented with a :func:`cache_info`
function that returns a :term:`named tuple` showing *hits*, *misses*,
- *maxsize* and *currsize*.
+ *maxsize* and *currsize*. In a multi-threaded environment, the hits
+ and misses are approximate.
The decorator also provides a :func:`cache_clear` function for clearing or
invalidating the cache.
@@ -89,12 +93,25 @@
>>> print(get_pep.cache_info())
CacheInfo(hits=3, misses=8, maxsize=20, currsize=8)
- .. versionadded:: 3.2
+ Example of efficiently computing
+ `Fibonacci numbers `_
+ using a cache to implement a
+ `dynamic programming `_
+ technique::
+
+ @lru_cache(maxsize=None)
+ def fib(n):
+ if n < 2:
+ return n
+ return fib(n-1) + fib(n-2)
- .. seealso::
+ >>> print([fib(n) for n in range(16)])
+ [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]
- Recipe for a `plain cache without the LRU feature
- `_.
+ >>> print(fib.cache_info())
+ CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)
+
+ .. versionadded:: 3.2
.. decorator:: total_ordering
Modified: python/branches/py3k/Lib/functools.py
==============================================================================
--- python/branches/py3k/Lib/functools.py (original)
+++ python/branches/py3k/Lib/functools.py Wed Dec 1 04:45:41 2010
@@ -119,6 +119,9 @@
def lru_cache(maxsize=100):
"""Least-recently-used cache decorator.
+ If *maxsize* is set to None, the LRU features are disabled and the cache
+ can grow without bound.
+
Arguments to the cached function must be hashable.
View the cache statistics named tuple (hits, misses, maxsize, currsize) with
@@ -136,32 +139,51 @@
def decorating_function(user_function,
tuple=tuple, sorted=sorted, len=len, KeyError=KeyError):
- cache = OrderedDict() # ordered least recent to most recent
- cache_popitem = cache.popitem
- cache_renew = cache.move_to_end
hits = misses = 0
kwd_mark = object() # separates positional and keyword args
lock = Lock()
- @wraps(user_function)
- def wrapper(*args, **kwds):
- nonlocal hits, misses
- key = args
- if kwds:
- key += (kwd_mark,) + tuple(sorted(kwds.items()))
- try:
- with lock:
+ if maxsize is None:
+ cache = dict() # simple cache without ordering or size limit
+
+ @wraps(user_function)
+ def wrapper(*args, **kwds):
+ nonlocal hits, misses
+ key = args
+ if kwds:
+ key += (kwd_mark,) + tuple(sorted(kwds.items()))
+ try:
result = cache[key]
- cache_renew(key) # record recent use of this key
hits += 1
- except KeyError:
- result = user_function(*args, **kwds)
- with lock:
- cache[key] = result # record recent use of this key
+ except KeyError:
+ result = user_function(*args, **kwds)
+ cache[key] = result
misses += 1
- if len(cache) > maxsize:
- cache_popitem(0) # purge least recently used cache entry
- return result
+ return result
+ else:
+ cache = OrderedDict() # ordered least recent to most recent
+ cache_popitem = cache.popitem
+ cache_renew = cache.move_to_end
+
+ @wraps(user_function)
+ def wrapper(*args, **kwds):
+ nonlocal hits, misses
+ key = args
+ if kwds:
+ key += (kwd_mark,) + tuple(sorted(kwds.items()))
+ try:
+ with lock:
+ result = cache[key]
+ cache_renew(key) # record recent use of this key
+ hits += 1
+ except KeyError:
+ result = user_function(*args, **kwds)
+ with lock:
+ cache[key] = result # record recent use of this key
+ misses += 1
+ if len(cache) > maxsize:
+ cache_popitem(0) # purge least recently used cache entry
+ return result
def cache_info():
"""Report cache statistics"""
Modified: python/branches/py3k/Lib/test/test_functools.py
==============================================================================
--- python/branches/py3k/Lib/test/test_functools.py (original)
+++ python/branches/py3k/Lib/test/test_functools.py Wed Dec 1 04:45:41 2010
@@ -586,6 +586,20 @@
self.assertEqual(misses, 4)
self.assertEqual(currsize, 2)
+ def test_lru_with_maxsize_none(self):
+ @functools.lru_cache(maxsize=None)
+ def fib(n):
+ if n < 2:
+ return n
+ return fib(n-1) + fib(n-2)
+ self.assertEqual([fib(n) for n in range(16)],
+ [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610])
+ self.assertEqual(fib.cache_info(),
+ functools._CacheInfo(hits=28, misses=16, maxsize=None, currsize=16))
+ fib.cache_clear()
+ self.assertEqual(fib.cache_info(),
+ functools._CacheInfo(hits=0, misses=0, maxsize=None, currsize=0))
+
def test_main(verbose=None):
test_classes = (
TestPartial,
From solipsis at pitrou.net Wed Dec 1 04:50:58 2010
From: solipsis at pitrou.net (solipsis at pitrou.net)
Date: Wed, 01 Dec 2010 04:50:58 +0100
Subject: [Python-checkins] Daily py3k reference leaks (r86909): sum=0
Message-ID:
py3k results for svn r86909 (hg cset 502eca1dc051)
--------------------------------------------------
Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflogT8QCJk', '-x']
From python-checkins at python.org Wed Dec 1 11:49:19 2010
From: python-checkins at python.org (raymond.hettinger)
Date: Wed, 1 Dec 2010 11:49:19 +0100 (CET)
Subject: [Python-checkins] r86912 -
python/branches/py3k/Doc/library/itertools.rst
Message-ID: <20101201104919.99309EE99C@mail.python.org>
Author: raymond.hettinger
Date: Wed Dec 1 11:49:19 2010
New Revision: 86912
Log:
Add recipe to itertools doc.
Modified:
python/branches/py3k/Doc/library/itertools.rst
Modified: python/branches/py3k/Doc/library/itertools.rst
==============================================================================
--- python/branches/py3k/Doc/library/itertools.rst (original)
+++ python/branches/py3k/Doc/library/itertools.rst Wed Dec 1 11:49:19 2010
@@ -653,6 +653,14 @@
pending -= 1
nexts = cycle(islice(nexts, pending))
+ def accumulate(iterable):
+ 'Emit a running total'
+ # accumulate([1,2,3,4,5]) --> 1 3 6 10 15
+ total = 0
+ for element in iterable:
+ total += element
+ yield total
+
def partition(pred, iterable):
'Use a predicate to partition entries into false entries and true entries'
# partition(is_odd, range(10)) --> 0 2 4 6 8 and 1 3 5 7 9
From python-checkins at python.org Wed Dec 1 16:32:44 2010
From: python-checkins at python.org (georg.brandl)
Date: Wed, 1 Dec 2010 16:32:44 +0100 (CET)
Subject: [Python-checkins] r86913 -
python/branches/py3k/Doc/reference/expressions.rst
Message-ID: <20101201153244.298D7EE9A5@mail.python.org>
Author: georg.brandl
Date: Wed Dec 1 16:32:43 2010
New Revision: 86913
Log:
Add missing word, and add a better reference to the actual function.
Modified:
python/branches/py3k/Doc/reference/expressions.rst
Modified: python/branches/py3k/Doc/reference/expressions.rst
==============================================================================
--- python/branches/py3k/Doc/reference/expressions.rst (original)
+++ python/branches/py3k/Doc/reference/expressions.rst Wed Dec 1 16:32:43 2010
@@ -1322,8 +1322,8 @@
true numerically due to roundoff. For example, and assuming a platform on which
a Python float is an IEEE 754 double-precision number, in order that ``-1e-100 %
1e100`` have the same sign as ``1e100``, the computed result is ``-1e-100 +
- 1e100``, which is numerically exactly equal to ``1e100``. Function :func:`fmod`
- in the :mod:`math` module returns a result whose sign matches the sign of the
+ 1e100``, which is numerically exactly equal to ``1e100``. The function
+ :func:`math.fmod` returns a result whose sign matches the sign of the
first argument instead, and so returns ``-1e-100`` in this case. Which approach
is more appropriate depends on the application.
@@ -1344,7 +1344,8 @@
the :keyword:`is` operator, like those involving comparisons between instance
methods, or constants. Check their documentation for more info.
-.. [#] The ``%`` is also used for string formatting; the same precedence applies.
+.. [#] The ``%`` operator is also used for string formatting; the same
+ precedence applies.
.. [#] The power operator ``**`` binds less tightly than an arithmetic or
bitwise unary operator on its right, that is, ``2**-1`` is ``0.5``.
From python-checkins at python.org Wed Dec 1 16:36:33 2010
From: python-checkins at python.org (georg.brandl)
Date: Wed, 1 Dec 2010 16:36:33 +0100 (CET)
Subject: [Python-checkins] r86914 - python/branches/py3k/Doc/c-api/list.rst
Message-ID: <20101201153633.8B748F5C5@mail.python.org>
Author: georg.brandl
Date: Wed Dec 1 16:36:33 2010
New Revision: 86914
Log:
#10594: fix parameter names in PyList API docs.
Modified:
python/branches/py3k/Doc/c-api/list.rst
Modified: python/branches/py3k/Doc/c-api/list.rst
==============================================================================
--- python/branches/py3k/Doc/c-api/list.rst (original)
+++ python/branches/py3k/Doc/c-api/list.rst Wed Dec 1 16:36:33 2010
@@ -37,7 +37,7 @@
.. note::
- If *length* is greater than zero, the returned list object's items are
+ If *len* is greater than zero, the returned list object's items are
set to ``NULL``. Thus you cannot use abstract API functions such as
:c:func:`PySequence_SetItem` or expose the object to Python code before
setting all items to a real object with :c:func:`PyList_SetItem`.
@@ -58,9 +58,9 @@
.. c:function:: PyObject* PyList_GetItem(PyObject *list, Py_ssize_t index)
- Return the object at position *pos* in the list pointed to by *p*. The
+ Return the object at position *index* in the list pointed to by *list*. The
position must be positive, indexing from the end of the list is not
- supported. If *pos* is out of bounds, return *NULL* and set an
+ supported. If *index* is out of bounds, return *NULL* and set an
:exc:`IndexError` exception.
From python-checkins at python.org Wed Dec 1 16:44:25 2010
From: python-checkins at python.org (georg.brandl)
Date: Wed, 1 Dec 2010 16:44:25 +0100 (CET)
Subject: [Python-checkins] r86915 -
python/branches/py3k/Doc/library/unittest.rst
Message-ID: <20101201154425.CD388EE9A8@mail.python.org>
Author: georg.brandl
Date: Wed Dec 1 16:44:25 2010
New Revision: 86915
Log:
Fix some markup and style in the unittest docs.
Modified:
python/branches/py3k/Doc/library/unittest.rst
Modified: python/branches/py3k/Doc/library/unittest.rst
==============================================================================
--- python/branches/py3k/Doc/library/unittest.rst (original)
+++ python/branches/py3k/Doc/library/unittest.rst Wed Dec 1 16:44:25 2010
@@ -978,9 +978,10 @@
with self.assertRaisesRegex(ValueError, 'literal'):
int('XYZ')
- .. versionadded:: 3.1 ``assertRaisesRegexp``
+ .. versionadded:: 3.1
+ under the name ``assertRaisesRegexp``.
.. versionchanged:: 3.2
- The method has been renamed to :meth:`assertRaisesRegex`
+ Renamed to :meth:`assertRaisesRegex`.
.. method:: assertWarns(warning, callable, *args, **kwds)
@@ -1091,9 +1092,9 @@
Supplying both *delta* and *places* raises a ``TypeError``.
.. versionchanged:: 3.2
- assertAlmostEqual automatically considers almost equal objects that compare equal.
- assertNotAlmostEqual automatically fails if the objects compare equal.
- Added the ``delta`` keyword argument.
+ :meth:`assertAlmostEqual` automatically considers almost equal objects
+ that compare equal. :meth:`assertNotAlmostEqual` automatically fails
+ if the objects compare equal. Added the *delta* keyword argument.
.. method:: assertGreater(first, second, msg=None)
@@ -1119,10 +1120,13 @@
may be a regular expression object or a string containing a regular
expression suitable for use by :func:`re.search`.
- .. versionadded:: 3.1 ``.assertRegexpMatches``
+ .. versionadded:: 3.1
+ under the name ``assertRegexpMatches``.
.. versionchanged:: 3.2
- ``.assertRegexpMatches`` has been renamed to :meth:`.assertRegex`
- .. versionadded:: 3.2 :meth:`.assertNotRegex`
+ The method ``assertRegexpMatches()`` has been renamed to
+ :meth:`.assertRegex`.
+ .. versionadded:: 3.2
+ :meth:`.assertNotRegex`.
.. method:: assertDictContainsSubset(expected, actual, msg=None)
@@ -1359,11 +1363,11 @@
returns the first line of the test method's docstring, if available,
or ``None``.
- .. versionchanged:: 3.1,3.2
+ .. versionchanged:: 3.1
In 3.1 this was changed to add the test name to the short description
- even in the presence of a docstring. This caused compatibility issues
+ even in the presence of a docstring. This caused compatibility issues
with unittest extensions and adding the test name was moved to the
- :class:`TextTestResult`.
+ :class:`TextTestResult` in Python 3.2.
.. method:: addCleanup(function, *args, **kwargs)
@@ -1715,7 +1719,6 @@
The total number of tests run so far.
- .. attribute:: buffer
If set to true, ``sys.stdout`` and ``sys.stderr`` will be buffered in between
:meth:`startTest` and :meth:`stopTest` being called. Collected output will
@@ -1881,9 +1884,12 @@
stream, descriptions, verbosity
- .. versionchanged:: 3.2 Added the ``warnings`` argument
+ .. versionchanged:: 3.2
+ Added the ``warnings`` argument.
-.. function:: main(module='__main__', defaultTest=None, argv=None, testRunner=None, testLoader=unittest.loader.defaultTestLoader, exit=True, verbosity=1, failfast=None, catchbreak=None, buffer=None, warnings=None)
+.. function:: main(module='__main__', defaultTest=None, argv=None, testRunner=None, \
+ testLoader=unittest.loader.defaultTestLoader, exit=True, verbosity=1, \
+ failfast=None, catchbreak=None, buffer=None, warnings=None)
A command-line program that runs a set of tests; this is primarily for making
test modules conveniently executable. The simplest use for this function is to
@@ -1928,10 +1934,8 @@
load_tests Protocol
###################
-
.. versionadded:: 3.2
-
Modules or packages can customize how tests are loaded from them during normal
test runs or test discovery by implementing a function called ``load_tests``.
@@ -2078,6 +2082,8 @@
Signal Handling
---------------
+.. versionadded:: 3.2
+
The :option:`-c/--catch ` command-line option to unittest,
along with the ``catchbreak`` parameter to :func:`unittest.main()`, provide
more friendly handling of control-C during a test run. With catch break
@@ -2103,7 +2109,6 @@
(usually in response to the user pressing control-c) all registered results
have :meth:`~TestResult.stop` called.
- .. versionadded:: 3.2
.. function:: registerResult(result)
@@ -2115,7 +2120,6 @@
handling is not enabled, so test frameworks can unconditionally register
all results they create independently of whether or not handling is enabled.
- .. versionadded:: 3.2
.. function:: removeResult(result)
@@ -2123,7 +2127,6 @@
:meth:`~TestResult.stop` will no longer be called on that result object in
response to a control-c.
- .. versionadded:: 3.2
.. function:: removeHandler(function=None)
@@ -2134,6 +2137,3 @@
@unittest.removeHandler
def test_signal_handling(self):
...
-
- .. versionadded:: 3.2
-
From python-checkins at python.org Wed Dec 1 21:05:49 2010
From: python-checkins at python.org (alexander.belopolsky)
Date: Wed, 1 Dec 2010 21:05:49 +0100 (CET)
Subject: [Python-checkins] r86916 - in python/branches/py3k:
Lib/test/test_functools.py Lib/test/test_inspect.py Misc/NEWS
Modules/_functoolsmodule.c
Message-ID: <20101201200549.BD863EE981@mail.python.org>
Author: alexander.belopolsky
Date: Wed Dec 1 21:05:49 2010
New Revision: 86916
Log:
Issue #4113: Added custom __repr__ method to functools.partial.
Modified:
python/branches/py3k/Lib/test/test_functools.py
python/branches/py3k/Lib/test/test_inspect.py
python/branches/py3k/Misc/NEWS
python/branches/py3k/Modules/_functoolsmodule.c
Modified: python/branches/py3k/Lib/test/test_functools.py
==============================================================================
--- python/branches/py3k/Lib/test/test_functools.py (original)
+++ python/branches/py3k/Lib/test/test_functools.py Wed Dec 1 21:05:49 2010
@@ -146,6 +146,32 @@
join = self.thetype(''.join)
self.assertEqual(join(data), '0123456789')
+ def test_repr(self):
+ args = (object(), object())
+ args_repr = ', '.join(repr(a) for a in args)
+ kwargs = {'a': object(), 'b': object()}
+ kwargs_repr = ', '.join("%s=%r" % (k, v) for k, v in kwargs.items())
+ if self.thetype is functools.partial:
+ name = 'functools.partial'
+ else:
+ name = self.thetype.__name__
+
+ f = self.thetype(capture)
+ self.assertEqual('{}({!r})'.format(name, capture),
+ repr(f))
+
+ f = self.thetype(capture, *args)
+ self.assertEqual('{}({!r}, {})'.format(name, capture, args_repr),
+ repr(f))
+
+ f = self.thetype(capture, **kwargs)
+ self.assertEqual('{}({!r}, {})'.format(name, capture, kwargs_repr),
+ repr(f))
+
+ f = self.thetype(capture, *args, **kwargs)
+ self.assertEqual('{}({!r}, {}, {})'.format(name, capture, args_repr, kwargs_repr),
+ repr(f))
+
def test_pickle(self):
f = self.thetype(signature, 'asdf', bar=True)
f.add_something_to__dict__ = True
@@ -163,6 +189,9 @@
thetype = PythonPartial
+ # the python version hasn't a nice repr
+ def test_repr(self): pass
+
# the python version isn't picklable
def test_pickle(self): pass
Modified: python/branches/py3k/Lib/test/test_inspect.py
==============================================================================
--- python/branches/py3k/Lib/test/test_inspect.py (original)
+++ python/branches/py3k/Lib/test/test_inspect.py Wed Dec 1 21:05:49 2010
@@ -12,6 +12,7 @@
from test import inspect_fodder as mod
from test import inspect_fodder2 as mod2
+from test import inspect_fodder3 as mod3
# C module for test_findsource_binary
import unicodedata
@@ -388,6 +389,12 @@
self.assertEqual(inspect.findsource(co), (lines,0))
self.assertEqual(inspect.getsource(co), lines[0])
+class TestNoEOF(GetSourceBase):
+ fodderFile = mod3
+
+ def test_class(self):
+ self.assertSourceEqual(mod3.X, 1, 2)
+
# Helper for testing classify_class_attrs.
def attrs_wo_objs(cls):
return [t[:3] for t in inspect.classify_class_attrs(cls)]
Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS (original)
+++ python/branches/py3k/Misc/NEWS Wed Dec 1 21:05:49 2010
@@ -46,6 +46,9 @@
Library
-------
+- Issue #4113: Added custom ``__repr__`` method to ``functools.partial``.
+ Original patch by Daniel Urban.
+
- Issue #10273: Rename `assertRegexpMatches` and `assertRaisesRegexp` to
`assertRegex` and `assertRaisesRegex`.
Modified: python/branches/py3k/Modules/_functoolsmodule.c
==============================================================================
--- python/branches/py3k/Modules/_functoolsmodule.c (original)
+++ python/branches/py3k/Modules/_functoolsmodule.c Wed Dec 1 21:05:49 2010
@@ -196,6 +196,48 @@
{NULL} /* Sentinel */
};
+static PyObject *
+partial_repr(partialobject *pto)
+{
+ PyObject *result;
+ PyObject *arglist;
+ PyObject *tmp;
+ Py_ssize_t i, n;
+
+ arglist = PyUnicode_FromString("");
+ if (arglist == NULL) {
+ return NULL;
+ }
+ /* Pack positional arguments */
+ assert (PyTuple_Check(pto->args));
+ n = PyTuple_GET_SIZE(pto->args);
+ for (i = 0; i < n; i++) {
+ tmp = PyUnicode_FromFormat("%U, %R", arglist,
+ PyTuple_GET_ITEM(pto->args, i));
+ Py_DECREF(arglist);
+ if (tmp == NULL)
+ return NULL;
+ arglist = tmp;
+ }
+ /* Pack keyword arguments */
+ assert (pto->kw == Py_None || PyDict_Check(pto->kw));
+ if (pto->kw != Py_None) {
+ PyObject *key, *value;
+ for (i = 0; PyDict_Next(pto->kw, &i, &key, &value);) {
+ tmp = PyUnicode_FromFormat("%U, %U=%R", arglist,
+ key, value);
+ Py_DECREF(arglist);
+ if (tmp == NULL)
+ return NULL;
+ arglist = tmp;
+ }
+ }
+ result = PyUnicode_FromFormat("%s(%R%U)", Py_TYPE(pto)->tp_name,
+ pto->fn, arglist);
+ Py_DECREF(arglist);
+ return result;
+}
+
/* Pickle strategy:
__reduce__ by itself doesn't support getting kwargs in the unpickle
operation so we define a __setstate__ that replaces all the information
@@ -254,7 +296,7 @@
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
- 0, /* tp_repr */
+ (reprfunc)partial_repr, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
From python-checkins at python.org Wed Dec 1 22:55:40 2010
From: python-checkins at python.org (alexander.belopolsky)
Date: Wed, 1 Dec 2010 22:55:40 +0100 (CET)
Subject: [Python-checkins] r86917 -
python/branches/py3k/Lib/test/test_inspect.py
Message-ID: <20101201215540.52D68EE991@mail.python.org>
Author: alexander.belopolsky
Date: Wed Dec 1 22:55:40 2010
New Revision: 86917
Log:
Reverted unintended change from r86916
Modified:
python/branches/py3k/Lib/test/test_inspect.py
Modified: python/branches/py3k/Lib/test/test_inspect.py
==============================================================================
--- python/branches/py3k/Lib/test/test_inspect.py (original)
+++ python/branches/py3k/Lib/test/test_inspect.py Wed Dec 1 22:55:40 2010
@@ -12,7 +12,6 @@
from test import inspect_fodder as mod
from test import inspect_fodder2 as mod2
-from test import inspect_fodder3 as mod3
# C module for test_findsource_binary
import unicodedata
@@ -389,12 +388,6 @@
self.assertEqual(inspect.findsource(co), (lines,0))
self.assertEqual(inspect.getsource(co), lines[0])
-class TestNoEOF(GetSourceBase):
- fodderFile = mod3
-
- def test_class(self):
- self.assertSourceEqual(mod3.X, 1, 2)
-
# Helper for testing classify_class_attrs.
def attrs_wo_objs(cls):
return [t[:3] for t in inspect.classify_class_attrs(cls)]
From python-checkins at python.org Wed Dec 1 23:48:01 2010
From: python-checkins at python.org (raymond.hettinger)
Date: Wed, 1 Dec 2010 23:48:01 +0100 (CET)
Subject: [Python-checkins] r86918 - in python/branches/py3k:
Lib/test/test_itertools.py Misc/NEWS Modules/itertoolsmodule.c
Message-ID: <20101201224801.0500BEE992@mail.python.org>
Author: raymond.hettinger
Date: Wed Dec 1 23:48:00 2010
New Revision: 86918
Log:
Add itertools.accumulate().
Modified:
python/branches/py3k/Lib/test/test_itertools.py
python/branches/py3k/Misc/NEWS
python/branches/py3k/Modules/itertoolsmodule.c
Modified: python/branches/py3k/Lib/test/test_itertools.py
==============================================================================
--- python/branches/py3k/Lib/test/test_itertools.py (original)
+++ python/branches/py3k/Lib/test/test_itertools.py Wed Dec 1 23:48:00 2010
@@ -56,6 +56,23 @@
return prod(range(1, n+1))
class TestBasicOps(unittest.TestCase):
+
+ def test_accumulate(self):
+ self.assertEqual(list(accumulate(range(10))), # one positional arg
+ [0, 1, 3, 6, 10, 15, 21, 28, 36, 45])
+ self.assertEqual(list(accumulate(range(10), 100)), # two positional args
+ [100, 101, 103, 106, 110, 115, 121, 128, 136, 145])
+ self.assertEqual(list(accumulate(iterable=range(10), start=100)), # kw args
+ [100, 101, 103, 106, 110, 115, 121, 128, 136, 145])
+ for typ in int, complex, Decimal, Fraction: # multiple types
+ self.assertEqual(list(accumulate(range(10), typ(0))),
+ list(map(typ, [0, 1, 3, 6, 10, 15, 21, 28, 36, 45])))
+ self.assertEqual(list(accumulate([])), []) # empty iterable
+ self.assertRaises(TypeError, accumulate, range(10), 0, 5) # too many args
+ self.assertRaises(TypeError, accumulate) # too few args
+ self.assertRaises(TypeError, accumulate, range(10), x=7) # unexpected kwd args
+ self.assertRaises(TypeError, list, accumulate([1, []])) # args that don't add
+
def test_chain(self):
def chain2(*iterables):
@@ -932,6 +949,9 @@
class TestExamples(unittest.TestCase):
+ def test_accumlate(self):
+ self.assertEqual(list(accumulate([1,2,3,4,5])), [1, 3, 6, 10, 15])
+
def test_chain(self):
self.assertEqual(''.join(chain('ABC', 'DEF')), 'ABCDEF')
@@ -1019,6 +1039,10 @@
next(iterator)
del container, iterator
+ def test_accumulate(self):
+ a = []
+ self.makecycle(accumulate([1,2,a,3]), a)
+
def test_chain(self):
a = []
self.makecycle(chain(a), a)
@@ -1188,6 +1212,17 @@
class TestVariousIteratorArgs(unittest.TestCase):
+ def test_accumulate(self):
+ s = [1,2,3,4,5]
+ r = [1,3,6,10,15]
+ n = len(s)
+ for g in (G, I, Ig, L, R):
+ self.assertEqual(list(accumulate(g(s))), r)
+ self.assertEqual(list(accumulate(S(s))), [])
+ self.assertRaises(TypeError, accumulate, X(s))
+ self.assertRaises(TypeError, accumulate, N(s))
+ self.assertRaises(ZeroDivisionError, list, accumulate(E(s)))
+
def test_chain(self):
for s in ("123", "", range(1000), ('do', 1.2), range(2000,2200,5)):
for g in (G, I, Ig, S, L, R):
Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS (original)
+++ python/branches/py3k/Misc/NEWS Wed Dec 1 23:48:00 2010
@@ -46,6 +46,8 @@
Library
-------
+- Added itertools.accumulate().
+
- Issue #4113: Added custom ``__repr__`` method to ``functools.partial``.
Original patch by Daniel Urban.
Modified: python/branches/py3k/Modules/itertoolsmodule.c
==============================================================================
--- python/branches/py3k/Modules/itertoolsmodule.c (original)
+++ python/branches/py3k/Modules/itertoolsmodule.c Wed Dec 1 23:48:00 2010
@@ -2584,6 +2584,146 @@
PyObject_GC_Del, /* tp_free */
};
+/* accumulate object ************************************************************/
+
+typedef struct {
+ PyObject_HEAD
+ PyObject *total;
+ PyObject *it;
+} accumulateobject;
+
+static PyTypeObject accumulate_type;
+
+static PyObject *
+accumulate_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ static char *kwargs[] = {"iterable", "start", NULL};
+ PyObject *iterable;
+ PyObject *it;
+ PyObject *start = NULL;
+ accumulateobject *lz;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:accumulate",
+ kwargs, &iterable, &start))
+ return NULL;
+
+ /* Get iterator. */
+ it = PyObject_GetIter(iterable);
+ if (it == NULL)
+ return NULL;
+
+ /* Default start value */
+ if (start == NULL) {
+ start = PyLong_FromLong(0);
+ if (start == NULL) {
+ Py_DECREF(it);
+ return NULL;
+ }
+ } else {
+ Py_INCREF(start);
+ }
+
+ /* create accumulateobject structure */
+ lz = (accumulateobject *)type->tp_alloc(type, 0);
+ if (lz == NULL) {
+ Py_DECREF(it);
+ Py_DECREF(start);
+ return NULL;
+ }
+
+ lz->total = start;
+ lz->it = it;
+ return (PyObject *)lz;
+}
+
+static void
+accumulate_dealloc(accumulateobject *lz)
+{
+ PyObject_GC_UnTrack(lz);
+ Py_XDECREF(lz->total);
+ Py_XDECREF(lz->it);
+ Py_TYPE(lz)->tp_free(lz);
+}
+
+static int
+accumulate_traverse(accumulateobject *lz, visitproc visit, void *arg)
+{
+ Py_VISIT(lz->it);
+ Py_VISIT(lz->total);
+ return 0;
+}
+
+static PyObject *
+accumulate_next(accumulateobject *lz)
+{
+ PyObject *val, *oldtotal, *newtotal;
+
+ val = PyIter_Next(lz->it);
+ if (val == NULL)
+ return NULL;
+
+ newtotal = PyNumber_Add(lz->total, val);
+ Py_DECREF(val);
+ if (newtotal == NULL)
+ return NULL;
+
+ oldtotal = lz->total;
+ lz->total = newtotal;
+ Py_DECREF(oldtotal);
+
+ Py_INCREF(newtotal);
+ return newtotal;
+}
+
+PyDoc_STRVAR(accumulate_doc,
+"accumulate(iterable, start=0) --> accumulate object\n\
+\n\
+Return series of accumulated sums.");
+
+static PyTypeObject accumulate_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "itertools.accumulate", /* tp_name */
+ sizeof(accumulateobject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)accumulate_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_reserved */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ PyObject_GenericGetAttr, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
+ Py_TPFLAGS_BASETYPE, /* tp_flags */
+ accumulate_doc, /* tp_doc */
+ (traverseproc)accumulate_traverse, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ PyObject_SelfIter, /* tp_iter */
+ (iternextfunc)accumulate_next, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ accumulate_new, /* tp_new */
+ PyObject_GC_Del, /* tp_free */
+};
+
/* compress object ************************************************************/
@@ -3496,6 +3636,7 @@
repeat(elem [,n]) --> elem, elem, elem, ... endlessly or up to n times\n\
\n\
Iterators terminating on the shortest input sequence:\n\
+accumulate(p, start=0) --> p0, p0+p1, p0+p1+p2\n\
chain(p, q, ...) --> p0, p1, ... plast, q0, q1, ... \n\
compress(data, selectors) --> (d[0] if s[0]), (d[1] if s[1]), ...\n\
dropwhile(pred, seq) --> seq[n], seq[n+1], starting when pred fails\n\
@@ -3541,6 +3682,7 @@
PyObject *m;
char *name;
PyTypeObject *typelist[] = {
+ &accumulate_type,
&combinations_type,
&cwr_type,
&cycle_type,
From python-checkins at python.org Wed Dec 1 23:50:36 2010
From: python-checkins at python.org (raymond.hettinger)
Date: Wed, 1 Dec 2010 23:50:36 +0100 (CET)
Subject: [Python-checkins] r86919 -
python/branches/py3k/Doc/library/itertools.rst
Message-ID: <20101201225036.46F93EE99C@mail.python.org>
Author: raymond.hettinger
Date: Wed Dec 1 23:50:36 2010
New Revision: 86919
Log:
Add itertools.accumulate().
Modified:
python/branches/py3k/Doc/library/itertools.rst
Modified: python/branches/py3k/Doc/library/itertools.rst
==============================================================================
--- python/branches/py3k/Doc/library/itertools.rst (original)
+++ python/branches/py3k/Doc/library/itertools.rst Wed Dec 1 23:50:36 2010
@@ -2,14 +2,14 @@
=======================================================================
.. module:: itertools
- :synopsis: Functions creating iterators for efficient looping.
+ :synopsis: Functions creating iterators for efficient looping.
.. moduleauthor:: Raymond Hettinger
.. sectionauthor:: Raymond Hettinger
.. testsetup::
- from itertools import *
+ from itertools import *
This module implements a number of :term:`iterator` building blocks inspired
@@ -46,6 +46,7 @@
==================== ============================ ================================================= =============================================================
Iterator Arguments Results Example
==================== ============================ ================================================= =============================================================
+:func:`accumulate` p[, start=0] p0, p0+p1, p0+p1+p2 ... ` ``accumulate([1,2,3,4,5]) --> 1 3 6 10 15``
:func:`chain` p, q, ... p0, p1, ... plast, q0, q1, ... ``chain('ABC', 'DEF') --> A B C D E F``
:func:`compress` data, selectors (d[0] if s[0]), (d[1] if s[1]), ... ``compress('ABCDEF', [1,0,1,0,1,1]) --> A C E F``
:func:`dropwhile` pred, seq seq[n], seq[n+1], starting when pred fails ``dropwhile(lambda x: x<5, [1,4,6,4,1]) --> 6 4 1``
@@ -83,47 +84,62 @@
streams of infinite length, so they should only be accessed by functions or
loops that truncate the stream.
+.. function:: accumulate(iterable, start=0)
+
+ Make an iterator that returns accumulated sums plus the value of the *start*
+ parameter (which defaults to :const:`0`). Elements may be any addable type
+ including :class:`Decimal` or :class:`Fraction`. Equivalent to::
+
+ def accumulate(iterable, start=0):
+ 'Return running totals'
+ # accumulate([1,2,3,4,5]) --> 1 3 6 10 15
+ total = start
+ for element in iterable:
+ total += element
+ yield total
+
+ .. versionadded:: 3.2
.. function:: chain(*iterables)
- Make an iterator that returns elements from the first iterable until it is
- exhausted, then proceeds to the next iterable, until all of the iterables are
- exhausted. Used for treating consecutive sequences as a single sequence.
- Equivalent to::
-
- def chain(*iterables):
- # chain('ABC', 'DEF') --> A B C D E F
- for it in iterables:
- for element in it:
- yield element
+ Make an iterator that returns elements from the first iterable until it is
+ exhausted, then proceeds to the next iterable, until all of the iterables are
+ exhausted. Used for treating consecutive sequences as a single sequence.
+ Equivalent to::
+
+ def chain(*iterables):
+ # chain('ABC', 'DEF') --> A B C D E F
+ for it in iterables:
+ for element in it:
+ yield element
.. classmethod:: chain.from_iterable(iterable)
- Alternate constructor for :func:`chain`. Gets chained inputs from a
- single iterable argument that is evaluated lazily. Equivalent to::
+ Alternate constructor for :func:`chain`. Gets chained inputs from a
+ single iterable argument that is evaluated lazily. Equivalent to::
- @classmethod
- def from_iterable(iterables):
- # chain.from_iterable(['ABC', 'DEF']) --> A B C D E F
- for it in iterables:
- for element in it:
- yield element
+ @classmethod
+ def from_iterable(iterables):
+ # chain.from_iterable(['ABC', 'DEF']) --> A B C D E F
+ for it in iterables:
+ for element in it:
+ yield element
.. function:: combinations(iterable, r)
- Return *r* length subsequences of elements from the input *iterable*.
+ Return *r* length subsequences of elements from the input *iterable*.
- Combinations are emitted in lexicographic sort order. So, if the
- input *iterable* is sorted, the combination tuples will be produced
- in sorted order.
+ Combinations are emitted in lexicographic sort order. So, if the
+ input *iterable* is sorted, the combination tuples will be produced
+ in sorted order.
- Elements are treated as unique based on their position, not on their
- value. So if the input elements are unique, there will be no repeat
- values in each combination.
+ Elements are treated as unique based on their position, not on their
+ value. So if the input elements are unique, there will be no repeat
+ values in each combination.
- Equivalent to::
+ Equivalent to::
def combinations(iterable, r):
# combinations('ABCD', 2) --> AB AC AD BC BD CD
@@ -145,9 +161,9 @@
indices[j] = indices[j-1] + 1
yield tuple(pool[i] for i in indices)
- The code for :func:`combinations` can be also expressed as a subsequence
- of :func:`permutations` after filtering entries where the elements are not
- in sorted order (according to their position in the input pool)::
+ The code for :func:`combinations` can be also expressed as a subsequence
+ of :func:`permutations` after filtering entries where the elements are not
+ in sorted order (according to their position in the input pool)::
def combinations(iterable, r):
pool = tuple(iterable)
@@ -156,23 +172,23 @@
if sorted(indices) == list(indices):
yield tuple(pool[i] for i in indices)
- The number of items returned is ``n! / r! / (n-r)!`` when ``0 <= r <= n``
- or zero when ``r > n``.
+ The number of items returned is ``n! / r! / (n-r)!`` when ``0 <= r <= n``
+ or zero when ``r > n``.
.. function:: combinations_with_replacement(iterable, r)
- Return *r* length subsequences of elements from the input *iterable*
- allowing individual elements to be repeated more than once.
+ Return *r* length subsequences of elements from the input *iterable*
+ allowing individual elements to be repeated more than once.
- Combinations are emitted in lexicographic sort order. So, if the
- input *iterable* is sorted, the combination tuples will be produced
- in sorted order.
-
- Elements are treated as unique based on their position, not on their
- value. So if the input elements are unique, the generated combinations
- will also be unique.
+ Combinations are emitted in lexicographic sort order. So, if the
+ input *iterable* is sorted, the combination tuples will be produced
+ in sorted order.
+
+ Elements are treated as unique based on their position, not on their
+ value. So if the input elements are unique, the generated combinations
+ will also be unique.
- Equivalent to::
+ Equivalent to::
def combinations_with_replacement(iterable, r):
# combinations_with_replacement('ABC', 2) --> AA AB AC BB BC CC
@@ -191,9 +207,9 @@
indices[i:] = [indices[i] + 1] * (r - i)
yield tuple(pool[i] for i in indices)
- The code for :func:`combinations_with_replacement` can be also expressed as
- a subsequence of :func:`product` after filtering entries where the elements
- are not in sorted order (according to their position in the input pool)::
+ The code for :func:`combinations_with_replacement` can be also expressed as
+ a subsequence of :func:`product` after filtering entries where the elements
+ are not in sorted order (according to their position in the input pool)::
def combinations_with_replacement(iterable, r):
pool = tuple(iterable)
@@ -202,196 +218,196 @@
if sorted(indices) == list(indices):
yield tuple(pool[i] for i in indices)
- The number of items returned is ``(n+r-1)! / r! / (n-1)!`` when ``n > 0``.
+ The number of items returned is ``(n+r-1)! / r! / (n-1)!`` when ``n > 0``.
- .. versionadded:: 3.1
+ .. versionadded:: 3.1
.. function:: compress(data, selectors)
- Make an iterator that filters elements from *data* returning only those that
- have a corresponding element in *selectors* that evaluates to ``True``.
- Stops when either the *data* or *selectors* iterables has been exhausted.
- Equivalent to::
-
- def compress(data, selectors):
- # compress('ABCDEF', [1,0,1,0,1,1]) --> A C E F
- return (d for d, s in zip(data, selectors) if s)
+ Make an iterator that filters elements from *data* returning only those that
+ have a corresponding element in *selectors* that evaluates to ``True``.
+ Stops when either the *data* or *selectors* iterables has been exhausted.
+ Equivalent to::
+
+ def compress(data, selectors):
+ # compress('ABCDEF', [1,0,1,0,1,1]) --> A C E F
+ return (d for d, s in zip(data, selectors) if s)
- .. versionadded:: 3.1
+ .. versionadded:: 3.1
.. function:: count(start=0, step=1)
- Make an iterator that returns evenly spaced values starting with *n*. Often
- used as an argument to :func:`map` to generate consecutive data points.
- Also, used with :func:`zip` to add sequence numbers. Equivalent to::
-
- def count(start=0, step=1):
- # count(10) --> 10 11 12 13 14 ...
- # count(2.5, 0.5) -> 3.5 3.0 4.5 ...
- n = start
- while True:
- yield n
- n += step
-
- When counting with floating point numbers, better accuracy can sometimes be
- achieved by substituting multiplicative code such as: ``(start + step * i
- for i in count())``.
+ Make an iterator that returns evenly spaced values starting with *n*. Often
+ used as an argument to :func:`map` to generate consecutive data points.
+ Also, used with :func:`zip` to add sequence numbers. Equivalent to::
+
+ def count(start=0, step=1):
+ # count(10) --> 10 11 12 13 14 ...
+ # count(2.5, 0.5) -> 3.5 3.0 4.5 ...
+ n = start
+ while True:
+ yield n
+ n += step
+
+ When counting with floating point numbers, better accuracy can sometimes be
+ achieved by substituting multiplicative code such as: ``(start + step * i
+ for i in count())``.
- .. versionchanged:: 3.1
- Added *step* argument and allowed non-integer arguments.
+ .. versionchanged:: 3.1
+ Added *step* argument and allowed non-integer arguments.
.. function:: cycle(iterable)
- Make an iterator returning elements from the iterable and saving a copy of each.
- When the iterable is exhausted, return elements from the saved copy. Repeats
- indefinitely. Equivalent to::
-
- def cycle(iterable):
- # cycle('ABCD') --> A B C D A B C D A B C D ...
- saved = []
- for element in iterable:
- yield element
- saved.append(element)
- while saved:
- for element in saved:
+ Make an iterator returning elements from the iterable and saving a copy of each.
+ When the iterable is exhausted, return elements from the saved copy. Repeats
+ indefinitely. Equivalent to::
+
+ def cycle(iterable):
+ # cycle('ABCD') --> A B C D A B C D A B C D ...
+ saved = []
+ for element in iterable:
+ yield element
+ saved.append(element)
+ while saved:
+ for element in saved:
yield element
- Note, this member of the toolkit may require significant auxiliary storage
- (depending on the length of the iterable).
+ Note, this member of the toolkit may require significant auxiliary storage
+ (depending on the length of the iterable).
.. function:: dropwhile(predicate, iterable)
- Make an iterator that drops elements from the iterable as long as the predicate
- is true; afterwards, returns every element. Note, the iterator does not produce
- *any* output until the predicate first becomes false, so it may have a lengthy
- start-up time. Equivalent to::
-
- def dropwhile(predicate, iterable):
- # dropwhile(lambda x: x<5, [1,4,6,4,1]) --> 6 4 1
- iterable = iter(iterable)
- for x in iterable:
- if not predicate(x):
- yield x
- break
- for x in iterable:
- yield x
+ Make an iterator that drops elements from the iterable as long as the predicate
+ is true; afterwards, returns every element. Note, the iterator does not produce
+ *any* output until the predicate first becomes false, so it may have a lengthy
+ start-up time. Equivalent to::
+
+ def dropwhile(predicate, iterable):
+ # dropwhile(lambda x: x<5, [1,4,6,4,1]) --> 6 4 1
+ iterable = iter(iterable)
+ for x in iterable:
+ if not predicate(x):
+ yield x
+ break
+ for x in iterable:
+ yield x
.. function:: filterfalse(predicate, iterable)
- Make an iterator that filters elements from iterable returning only those for
- which the predicate is ``False``. If *predicate* is ``None``, return the items
- that are false. Equivalent to::
-
- def filterfalse(predicate, iterable):
- # filterfalse(lambda x: x%2, range(10)) --> 0 2 4 6 8
- if predicate is None:
- predicate = bool
- for x in iterable:
- if not predicate(x):
- yield x
+ Make an iterator that filters elements from iterable returning only those for
+ which the predicate is ``False``. If *predicate* is ``None``, return the items
+ that are false. Equivalent to::
+
+ def filterfalse(predicate, iterable):
+ # filterfalse(lambda x: x%2, range(10)) --> 0 2 4 6 8
+ if predicate is None:
+ predicate = bool
+ for x in iterable:
+ if not predicate(x):
+ yield x
.. function:: groupby(iterable, key=None)
- Make an iterator that returns consecutive keys and groups from the *iterable*.
- The *key* is a function computing a key value for each element. If not
- specified or is ``None``, *key* defaults to an identity function and returns
- the element unchanged. Generally, the iterable needs to already be sorted on
- the same key function.
-
- The operation of :func:`groupby` is similar to the ``uniq`` filter in Unix. It
- generates a break or new group every time the value of the key function changes
- (which is why it is usually necessary to have sorted the data using the same key
- function). That behavior differs from SQL's GROUP BY which aggregates common
- elements regardless of their input order.
-
- The returned group is itself an iterator that shares the underlying iterable
- with :func:`groupby`. Because the source is shared, when the :func:`groupby`
- object is advanced, the previous group is no longer visible. So, if that data
- is needed later, it should be stored as a list::
-
- groups = []
- uniquekeys = []
- data = sorted(data, key=keyfunc)
- for k, g in groupby(data, keyfunc):
- groups.append(list(g)) # Store group iterator as a list
- uniquekeys.append(k)
-
- :func:`groupby` is equivalent to::
-
- class groupby:
- # [k for k, g in groupby('AAAABBBCCDAABBB')] --> A B C D A B
- # [list(g) for k, g in groupby('AAAABBBCCD')] --> AAAA BBB CC D
- def __init__(self, iterable, key=None):
- if key is None:
- key = lambda x: x
- self.keyfunc = key
- self.it = iter(iterable)
- self.tgtkey = self.currkey = self.currvalue = object()
- def __iter__(self):
- return self
- def __next__(self):
- while self.currkey == self.tgtkey:
- self.currvalue = next(self.it) # Exit on StopIteration
- self.currkey = self.keyfunc(self.currvalue)
- self.tgtkey = self.currkey
- return (self.currkey, self._grouper(self.tgtkey))
- def _grouper(self, tgtkey):
- while self.currkey == tgtkey:
- yield self.currvalue
- self.currvalue = next(self.it) # Exit on StopIteration
- self.currkey = self.keyfunc(self.currvalue)
+ Make an iterator that returns consecutive keys and groups from the *iterable*.
+ The *key* is a function computing a key value for each element. If not
+ specified or is ``None``, *key* defaults to an identity function and returns
+ the element unchanged. Generally, the iterable needs to already be sorted on
+ the same key function.
+
+ The operation of :func:`groupby` is similar to the ``uniq`` filter in Unix. It
+ generates a break or new group every time the value of the key function changes
+ (which is why it is usually necessary to have sorted the data using the same key
+ function). That behavior differs from SQL's GROUP BY which aggregates common
+ elements regardless of their input order.
+
+ The returned group is itself an iterator that shares the underlying iterable
+ with :func:`groupby`. Because the source is shared, when the :func:`groupby`
+ object is advanced, the previous group is no longer visible. So, if that data
+ is needed later, it should be stored as a list::
+
+ groups = []
+ uniquekeys = []
+ data = sorted(data, key=keyfunc)
+ for k, g in groupby(data, keyfunc):
+ groups.append(list(g)) # Store group iterator as a list
+ uniquekeys.append(k)
+
+ :func:`groupby` is equivalent to::
+
+ class groupby:
+ # [k for k, g in groupby('AAAABBBCCDAABBB')] --> A B C D A B
+ # [list(g) for k, g in groupby('AAAABBBCCD')] --> AAAA BBB CC D
+ def __init__(self, iterable, key=None):
+ if key is None:
+ key = lambda x: x
+ self.keyfunc = key
+ self.it = iter(iterable)
+ self.tgtkey = self.currkey = self.currvalue = object()
+ def __iter__(self):
+ return self
+ def __next__(self):
+ while self.currkey == self.tgtkey:
+ self.currvalue = next(self.it) # Exit on StopIteration
+ self.currkey = self.keyfunc(self.currvalue)
+ self.tgtkey = self.currkey
+ return (self.currkey, self._grouper(self.tgtkey))
+ def _grouper(self, tgtkey):
+ while self.currkey == tgtkey:
+ yield self.currvalue
+ self.currvalue = next(self.it) # Exit on StopIteration
+ self.currkey = self.keyfunc(self.currvalue)
.. function:: islice(iterable, [start,] stop [, step])
- Make an iterator that returns selected elements from the iterable. If *start* is
- non-zero, then elements from the iterable are skipped until start is reached.
- Afterward, elements are returned consecutively unless *step* is set higher than
- one which results in items being skipped. If *stop* is ``None``, then iteration
- continues until the iterator is exhausted, if at all; otherwise, it stops at the
- specified position. Unlike regular slicing, :func:`islice` does not support
- negative values for *start*, *stop*, or *step*. Can be used to extract related
- fields from data where the internal structure has been flattened (for example, a
- multi-line report may list a name field on every third line). Equivalent to::
-
- def islice(iterable, *args):
- # islice('ABCDEFG', 2) --> A B
- # islice('ABCDEFG', 2, 4) --> C D
- # islice('ABCDEFG', 2, None) --> C D E F G
- # islice('ABCDEFG', 0, None, 2) --> A C E G
- s = slice(*args)
- it = iter(range(s.start or 0, s.stop or sys.maxsize, s.step or 1))
- nexti = next(it)
- for i, element in enumerate(iterable):
- if i == nexti:
- yield element
- nexti = next(it)
+ Make an iterator that returns selected elements from the iterable. If *start* is
+ non-zero, then elements from the iterable are skipped until start is reached.
+ Afterward, elements are returned consecutively unless *step* is set higher than
+ one which results in items being skipped. If *stop* is ``None``, then iteration
+ continues until the iterator is exhausted, if at all; otherwise, it stops at the
+ specified position. Unlike regular slicing, :func:`islice` does not support
+ negative values for *start*, *stop*, or *step*. Can be used to extract related
+ fields from data where the internal structure has been flattened (for example, a
+ multi-line report may list a name field on every third line). Equivalent to::
+
+ def islice(iterable, *args):
+ # islice('ABCDEFG', 2) --> A B
+ # islice('ABCDEFG', 2, 4) --> C D
+ # islice('ABCDEFG', 2, None) --> C D E F G
+ # islice('ABCDEFG', 0, None, 2) --> A C E G
+ s = slice(*args)
+ it = iter(range(s.start or 0, s.stop or sys.maxsize, s.step or 1))
+ nexti = next(it)
+ for i, element in enumerate(iterable):
+ if i == nexti:
+ yield element
+ nexti = next(it)
- If *start* is ``None``, then iteration starts at zero. If *step* is ``None``,
- then the step defaults to one.
+ If *start* is ``None``, then iteration starts at zero. If *step* is ``None``,
+ then the step defaults to one.
.. function:: permutations(iterable, r=None)
- Return successive *r* length permutations of elements in the *iterable*.
+ Return successive *r* length permutations of elements in the *iterable*.
- If *r* is not specified or is ``None``, then *r* defaults to the length
- of the *iterable* and all possible full-length permutations
- are generated.
+ If *r* is not specified or is ``None``, then *r* defaults to the length
+ of the *iterable* and all possible full-length permutations
+ are generated.
- Permutations are emitted in lexicographic sort order. So, if the
- input *iterable* is sorted, the permutation tuples will be produced
- in sorted order.
+ Permutations are emitted in lexicographic sort order. So, if the
+ input *iterable* is sorted, the permutation tuples will be produced
+ in sorted order.
- Elements are treated as unique based on their position, not on their
- value. So if the input elements are unique, there will be no repeat
- values in each permutation.
+ Elements are treated as unique based on their position, not on their
+ value. So if the input elements are unique, there will be no repeat
+ values in each permutation.
- Equivalent to::
+ Equivalent to::
def permutations(iterable, r=None):
# permutations('ABCD', 2) --> AB AC AD BA BC BD CA CB CD DA DB DC
@@ -418,9 +434,9 @@
else:
return
- The code for :func:`permutations` can be also expressed as a subsequence of
- :func:`product`, filtered to exclude entries with repeated elements (those
- from the same position in the input pool)::
+ The code for :func:`permutations` can be also expressed as a subsequence of
+ :func:`product`, filtered to exclude entries with repeated elements (those
+ from the same position in the input pool)::
def permutations(iterable, r=None):
pool = tuple(iterable)
@@ -430,87 +446,87 @@
if len(set(indices)) == r:
yield tuple(pool[i] for i in indices)
- The number of items returned is ``n! / (n-r)!`` when ``0 <= r <= n``
- or zero when ``r > n``.
+ The number of items returned is ``n! / (n-r)!`` when ``0 <= r <= n``
+ or zero when ``r > n``.
.. function:: product(*iterables, repeat=1)
- Cartesian product of input iterables.
+ Cartesian product of input iterables.
- Equivalent to nested for-loops in a generator expression. For example,
- ``product(A, B)`` returns the same as ``((x,y) for x in A for y in B)``.
+ Equivalent to nested for-loops in a generator expression. For example,
+ ``product(A, B)`` returns the same as ``((x,y) for x in A for y in B)``.
- The nested loops cycle like an odometer with the rightmost element advancing
- on every iteration. This pattern creates a lexicographic ordering so that if
- the input's iterables are sorted, the product tuples are emitted in sorted
- order.
-
- To compute the product of an iterable with itself, specify the number of
- repetitions with the optional *repeat* keyword argument. For example,
- ``product(A, repeat=4)`` means the same as ``product(A, A, A, A)``.
-
- This function is equivalent to the following code, except that the
- actual implementation does not build up intermediate results in memory::
-
- def product(*args, repeat=1):
- # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
- # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
- pools = [tuple(pool) for pool in args] * repeat
- result = [[]]
- for pool in pools:
- result = [x+[y] for x in result for y in pool]
- for prod in result:
- yield tuple(prod)
+ The nested loops cycle like an odometer with the rightmost element advancing
+ on every iteration. This pattern creates a lexicographic ordering so that if
+ the input's iterables are sorted, the product tuples are emitted in sorted
+ order.
+
+ To compute the product of an iterable with itself, specify the number of
+ repetitions with the optional *repeat* keyword argument. For example,
+ ``product(A, repeat=4)`` means the same as ``product(A, A, A, A)``.
+
+ This function is equivalent to the following code, except that the
+ actual implementation does not build up intermediate results in memory::
+
+ def product(*args, repeat=1):
+ # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
+ # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
+ pools = [tuple(pool) for pool in args] * repeat
+ result = [[]]
+ for pool in pools:
+ result = [x+[y] for x in result for y in pool]
+ for prod in result:
+ yield tuple(prod)
.. function:: repeat(object[, times])
- Make an iterator that returns *object* over and over again. Runs indefinitely
- unless the *times* argument is specified. Used as argument to :func:`map` for
- invariant parameters to the called function. Also used with :func:`zip` to
- create an invariant part of a tuple record. Equivalent to::
-
- def repeat(object, times=None):
- # repeat(10, 3) --> 10 10 10
- if times is None:
- while True:
- yield object
- else:
- for i in range(times):
- yield object
+ Make an iterator that returns *object* over and over again. Runs indefinitely
+ unless the *times* argument is specified. Used as argument to :func:`map` for
+ invariant parameters to the called function. Also used with :func:`zip` to
+ create an invariant part of a tuple record. Equivalent to::
+
+ def repeat(object, times=None):
+ # repeat(10, 3) --> 10 10 10
+ if times is None:
+ while True:
+ yield object
+ else:
+ for i in range(times):
+ yield object
.. function:: starmap(function, iterable)
- Make an iterator that computes the function using arguments obtained from
- the iterable. Used instead of :func:`map` when argument parameters are already
- grouped in tuples from a single iterable (the data has been "pre-zipped"). The
- difference between :func:`map` and :func:`starmap` parallels the distinction
- between ``function(a,b)`` and ``function(*c)``. Equivalent to::
-
- def starmap(function, iterable):
- # starmap(pow, [(2,5), (3,2), (10,3)]) --> 32 9 1000
- for args in iterable:
- yield function(*args)
+ Make an iterator that computes the function using arguments obtained from
+ the iterable. Used instead of :func:`map` when argument parameters are already
+ grouped in tuples from a single iterable (the data has been "pre-zipped"). The
+ difference between :func:`map` and :func:`starmap` parallels the distinction
+ between ``function(a,b)`` and ``function(*c)``. Equivalent to::
+
+ def starmap(function, iterable):
+ # starmap(pow, [(2,5), (3,2), (10,3)]) --> 32 9 1000
+ for args in iterable:
+ yield function(*args)
.. function:: takewhile(predicate, iterable)
- Make an iterator that returns elements from the iterable as long as the
- predicate is true. Equivalent to::
+ Make an iterator that returns elements from the iterable as long as the
+ predicate is true. Equivalent to::
- def takewhile(predicate, iterable):
- # takewhile(lambda x: x<5, [1,4,6,4,1]) --> 1 4
- for x in iterable:
- if predicate(x):
- yield x
- else:
- break
+ def takewhile(predicate, iterable):
+ # takewhile(lambda x: x<5, [1,4,6,4,1]) --> 1 4
+ for x in iterable:
+ if predicate(x):
+ yield x
+ else:
+ break
.. function:: tee(iterable, n=2)
- Return *n* independent iterators from a single iterable. Equivalent to::
+ Return *n* independent iterators from a single iterable. Equivalent to::
def tee(iterable, n=2):
it = iter(iterable)
@@ -524,38 +540,38 @@
yield mydeque.popleft()
return tuple(gen(d) for d in deques)
- Once :func:`tee` has made a split, the original *iterable* should not be
- used anywhere else; otherwise, the *iterable* could get advanced without
- the tee objects being informed.
-
- This itertool may require significant auxiliary storage (depending on how
- much temporary data needs to be stored). In general, if one iterator uses
- most or all of the data before another iterator starts, it is faster to use
- :func:`list` instead of :func:`tee`.
+ Once :func:`tee` has made a split, the original *iterable* should not be
+ used anywhere else; otherwise, the *iterable* could get advanced without
+ the tee objects being informed.
+
+ This itertool may require significant auxiliary storage (depending on how
+ much temporary data needs to be stored). In general, if one iterator uses
+ most or all of the data before another iterator starts, it is faster to use
+ :func:`list` instead of :func:`tee`.
.. function:: zip_longest(*iterables, fillvalue=None)
- Make an iterator that aggregates elements from each of the iterables. If the
- iterables are of uneven length, missing values are filled-in with *fillvalue*.
- Iteration continues until the longest iterable is exhausted. Equivalent to::
-
- def zip_longest(*args, fillvalue=None):
- # zip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D-
- def sentinel(counter = ([fillvalue]*(len(args)-1)).pop):
- yield counter() # yields the fillvalue, or raises IndexError
- fillers = repeat(fillvalue)
- iters = [chain(it, sentinel(), fillers) for it in args]
- try:
- for tup in zip(*iters):
- yield tup
- except IndexError:
- pass
-
- If one of the iterables is potentially infinite, then the :func:`zip_longest`
- function should be wrapped with something that limits the number of calls
- (for example :func:`islice` or :func:`takewhile`). If not specified,
- *fillvalue* defaults to ``None``.
+ Make an iterator that aggregates elements from each of the iterables. If the
+ iterables are of uneven length, missing values are filled-in with *fillvalue*.
+ Iteration continues until the longest iterable is exhausted. Equivalent to::
+
+ def zip_longest(*args, fillvalue=None):
+ # zip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D-
+ def sentinel(counter = ([fillvalue]*(len(args)-1)).pop):
+ yield counter() # yields the fillvalue, or raises IndexError
+ fillers = repeat(fillvalue)
+ iters = [chain(it, sentinel(), fillers) for it in args]
+ try:
+ for tup in zip(*iters):
+ yield tup
+ except IndexError:
+ pass
+
+ If one of the iterables is potentially infinite, then the :func:`zip_longest`
+ function should be wrapped with something that limits the number of calls
+ (for example :func:`islice` or :func:`takewhile`). If not specified,
+ *fillvalue* defaults to ``None``.
.. _itertools-recipes:
@@ -576,176 +592,168 @@
.. testcode::
- def take(n, iterable):
- "Return first n items of the iterable as a list"
- return list(islice(iterable, n))
-
- def tabulate(function, start=0):
- "Return function(0), function(1), ..."
- return map(function, count(start))
-
- def consume(iterator, n):
- "Advance the iterator n-steps ahead. If n is none, consume entirely."
- # Use functions that consume iterators at C speed.
- if n is None:
- # feed the entire iterator into a zero-length deque
- collections.deque(iterator, maxlen=0)
- else:
- # advance to the empty slice starting at position n
- next(islice(iterator, n, n), None)
-
- def nth(iterable, n, default=None):
- "Returns the nth item or a default value"
- return next(islice(iterable, n, None), default)
-
- def quantify(iterable, pred=bool):
- "Count how many times the predicate is true"
- return sum(map(pred, iterable))
-
- def padnone(iterable):
- """Returns the sequence elements and then returns None indefinitely.
-
- Useful for emulating the behavior of the built-in map() function.
- """
- return chain(iterable, repeat(None))
-
- def ncycles(iterable, n):
- "Returns the sequence elements n times"
- return chain.from_iterable(repeat(tuple(iterable), n))
-
- def dotproduct(vec1, vec2):
- return sum(map(operator.mul, vec1, vec2))
-
- def flatten(listOfLists):
- "Flatten one level of nesting"
- return chain.from_iterable(listOfLists)
-
- def repeatfunc(func, times=None, *args):
- """Repeat calls to func with specified arguments.
-
- Example: repeatfunc(random.random)
- """
- if times is None:
- return starmap(func, repeat(args))
- return starmap(func, repeat(args, times))
-
- def pairwise(iterable):
- "s -> (s0,s1), (s1,s2), (s2, s3), ..."
- a, b = tee(iterable)
- next(b, None)
- return zip(a, b)
-
- def grouper(n, iterable, fillvalue=None):
- "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
- args = [iter(iterable)] * n
- return zip_longest(*args, fillvalue=fillvalue)
-
- def roundrobin(*iterables):
- "roundrobin('ABC', 'D', 'EF') --> A D E B F C"
- # Recipe credited to George Sakkis
- pending = len(iterables)
- nexts = cycle(iter(it).__next__ for it in iterables)
- while pending:
- try:
- for next in nexts:
- yield next()
- except StopIteration:
- pending -= 1
- nexts = cycle(islice(nexts, pending))
-
- def accumulate(iterable):
- 'Emit a running total'
- # accumulate([1,2,3,4,5]) --> 1 3 6 10 15
- total = 0
- for element in iterable:
- total += element
- yield total
-
- def partition(pred, iterable):
- 'Use a predicate to partition entries into false entries and true entries'
- # partition(is_odd, range(10)) --> 0 2 4 6 8 and 1 3 5 7 9
- t1, t2 = tee(iterable)
- return filterfalse(pred, t1), filter(pred, t2)
-
- def powerset(iterable):
- "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
- s = list(iterable)
- return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
-
- def unique_everseen(iterable, key=None):
- "List unique elements, preserving order. Remember all elements ever seen."
- # unique_everseen('AAAABBBCCDAABBB') --> A B C D
- # unique_everseen('ABBCcAD', str.lower) --> A B C D
- seen = set()
- seen_add = seen.add
- if key is None:
- for element in filterfalse(seen.__contains__, iterable):
- seen_add(element)
- yield element
- else:
- for element in iterable:
- k = key(element)
- if k not in seen:
- seen_add(k)
- yield element
-
- def unique_justseen(iterable, key=None):
- "List unique elements, preserving order. Remember only the element just seen."
- # unique_justseen('AAAABBBCCDAABBB') --> A B C D A B
- # unique_justseen('ABBCcAD', str.lower) --> A B C A D
- return map(next, map(itemgetter(1), groupby(iterable, key)))
-
- def iter_except(func, exception, first=None):
- """ Call a function repeatedly until an exception is raised.
-
- Converts a call-until-exception interface to an iterator interface.
- Like __builtin__.iter(func, sentinel) but uses an exception instead
- of a sentinel to end the loop.
-
- Examples:
- iter_except(functools.partial(heappop, h), IndexError) # priority queue iterator
- iter_except(d.popitem, KeyError) # non-blocking dict iterator
- iter_except(d.popleft, IndexError) # non-blocking deque iterator
- iter_except(q.get_nowait, Queue.Empty) # loop over a producer Queue
- iter_except(s.pop, KeyError) # non-blocking set iterator
-
- """
- try:
- if first is not None:
- yield first() # For database APIs needing an initial cast to db.first()
- while 1:
- yield func()
- except exception:
- pass
-
- def random_product(*args, repeat=1):
- "Random selection from itertools.product(*args, **kwds)"
- pools = [tuple(pool) for pool in args] * repeat
- return tuple(random.choice(pool) for pool in pools)
-
- def random_permutation(iterable, r=None):
- "Random selection from itertools.permutations(iterable, r)"
- pool = tuple(iterable)
- r = len(pool) if r is None else r
- return tuple(random.sample(pool, r))
-
- def random_combination(iterable, r):
- "Random selection from itertools.combinations(iterable, r)"
- pool = tuple(iterable)
- n = len(pool)
- indices = sorted(random.sample(range(n), r))
- return tuple(pool[i] for i in indices)
-
- def random_combination_with_replacement(iterable, r):
- "Random selection from itertools.combinations_with_replacement(iterable, r)"
- pool = tuple(iterable)
- n = len(pool)
- indices = sorted(random.randrange(n) for i in range(r))
- return tuple(pool[i] for i in indices)
+ def take(n, iterable):
+ "Return first n items of the iterable as a list"
+ return list(islice(iterable, n))
+
+ def tabulate(function, start=0):
+ "Return function(0), function(1), ..."
+ return map(function, count(start))
+
+ def consume(iterator, n):
+ "Advance the iterator n-steps ahead. If n is none, consume entirely."
+ # Use functions that consume iterators at C speed.
+ if n is None:
+ # feed the entire iterator into a zero-length deque
+ collections.deque(iterator, maxlen=0)
+ else:
+ # advance to the empty slice starting at position n
+ next(islice(iterator, n, n), None)
+
+ def nth(iterable, n, default=None):
+ "Returns the nth item or a default value"
+ return next(islice(iterable, n, None), default)
+
+ def quantify(iterable, pred=bool):
+ "Count how many times the predicate is true"
+ return sum(map(pred, iterable))
+
+ def padnone(iterable):
+ """Returns the sequence elements and then returns None indefinitely.
+
+ Useful for emulating the behavior of the built-in map() function.
+ """
+ return chain(iterable, repeat(None))
+
+ def ncycles(iterable, n):
+ "Returns the sequence elements n times"
+ return chain.from_iterable(repeat(tuple(iterable), n))
+
+ def dotproduct(vec1, vec2):
+ return sum(map(operator.mul, vec1, vec2))
+
+ def flatten(listOfLists):
+ "Flatten one level of nesting"
+ return chain.from_iterable(listOfLists)
+
+ def repeatfunc(func, times=None, *args):
+ """Repeat calls to func with specified arguments.
+
+ Example: repeatfunc(random.random)
+ """
+ if times is None:
+ return starmap(func, repeat(args))
+ return starmap(func, repeat(args, times))
+
+ def pairwise(iterable):
+ "s -> (s0,s1), (s1,s2), (s2, s3), ..."
+ a, b = tee(iterable)
+ next(b, None)
+ return zip(a, b)
+
+ def grouper(n, iterable, fillvalue=None):
+ "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
+ args = [iter(iterable)] * n
+ return zip_longest(*args, fillvalue=fillvalue)
+
+ def roundrobin(*iterables):
+ "roundrobin('ABC', 'D', 'EF') --> A D E B F C"
+ # Recipe credited to George Sakkis
+ pending = len(iterables)
+ nexts = cycle(iter(it).__next__ for it in iterables)
+ while pending:
+ try:
+ for next in nexts:
+ yield next()
+ except StopIteration:
+ pending -= 1
+ nexts = cycle(islice(nexts, pending))
+
+ def partition(pred, iterable):
+ 'Use a predicate to partition entries into false entries and true entries'
+ # partition(is_odd, range(10)) --> 0 2 4 6 8 and 1 3 5 7 9
+ t1, t2 = tee(iterable)
+ return filterfalse(pred, t1), filter(pred, t2)
+
+ def powerset(iterable):
+ "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
+ s = list(iterable)
+ return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
+
+ def unique_everseen(iterable, key=None):
+ "List unique elements, preserving order. Remember all elements ever seen."
+ # unique_everseen('AAAABBBCCDAABBB') --> A B C D
+ # unique_everseen('ABBCcAD', str.lower) --> A B C D
+ seen = set()
+ seen_add = seen.add
+ if key is None:
+ for element in filterfalse(seen.__contains__, iterable):
+ seen_add(element)
+ yield element
+ else:
+ for element in iterable:
+ k = key(element)
+ if k not in seen:
+ seen_add(k)
+ yield element
+
+ def unique_justseen(iterable, key=None):
+ "List unique elements, preserving order. Remember only the element just seen."
+ # unique_justseen('AAAABBBCCDAABBB') --> A B C D A B
+ # unique_justseen('ABBCcAD', str.lower) --> A B C A D
+ return map(next, map(itemgetter(1), groupby(iterable, key)))
+
+ def iter_except(func, exception, first=None):
+ """ Call a function repeatedly until an exception is raised.
+
+ Converts a call-until-exception interface to an iterator interface.
+ Like __builtin__.iter(func, sentinel) but uses an exception instead
+ of a sentinel to end the loop.
+
+ Examples:
+ iter_except(functools.partial(heappop, h), IndexError) # priority queue iterator
+ iter_except(d.popitem, KeyError) # non-blocking dict iterator
+ iter_except(d.popleft, IndexError) # non-blocking deque iterator
+ iter_except(q.get_nowait, Queue.Empty) # loop over a producer Queue
+ iter_except(s.pop, KeyError) # non-blocking set iterator
+
+ """
+ try:
+ if first is not None:
+ yield first() # For database APIs needing an initial cast to db.first()
+ while 1:
+ yield func()
+ except exception:
+ pass
+
+ def random_product(*args, repeat=1):
+ "Random selection from itertools.product(*args, **kwds)"
+ pools = [tuple(pool) for pool in args] * repeat
+ return tuple(random.choice(pool) for pool in pools)
+
+ def random_permutation(iterable, r=None):
+ "Random selection from itertools.permutations(iterable, r)"
+ pool = tuple(iterable)
+ r = len(pool) if r is None else r
+ return tuple(random.sample(pool, r))
+
+ def random_combination(iterable, r):
+ "Random selection from itertools.combinations(iterable, r)"
+ pool = tuple(iterable)
+ n = len(pool)
+ indices = sorted(random.sample(range(n), r))
+ return tuple(pool[i] for i in indices)
+
+ def random_combination_with_replacement(iterable, r):
+ "Random selection from itertools.combinations_with_replacement(iterable, r)"
+ pool = tuple(iterable)
+ n = len(pool)
+ indices = sorted(random.randrange(n) for i in range(r))
+ return tuple(pool[i] for i in indices)
Note, many of the above recipes can be optimized by replacing global lookups
with local variables defined as default values. For example, the
*dotproduct* recipe can be written as::
- def dotproduct(vec1, vec2, sum=sum, map=map, mul=operator.mul):
- return sum(map(mul, vec1, vec2))
+ def dotproduct(vec1, vec2, sum=sum, map=map, mul=operator.mul):
+ return sum(map(mul, vec1, vec2))
From python-checkins at python.org Thu Dec 2 00:45:20 2010
From: python-checkins at python.org (raymond.hettinger)
Date: Thu, 2 Dec 2010 00:45:20 +0100 (CET)
Subject: [Python-checkins] r86920 -
python/branches/py3k/Doc/library/itertools.rst
Message-ID: <20101201234520.8E79AEE983@mail.python.org>
Author: raymond.hettinger
Date: Thu Dec 2 00:45:20 2010
New Revision: 86920
Log:
Clean-up last update (missing comma, unnecessary spacing change, spurious backtick).
Modified:
python/branches/py3k/Doc/library/itertools.rst
Modified: python/branches/py3k/Doc/library/itertools.rst
==============================================================================
--- python/branches/py3k/Doc/library/itertools.rst (original)
+++ python/branches/py3k/Doc/library/itertools.rst Thu Dec 2 00:45:20 2010
@@ -2,14 +2,14 @@
=======================================================================
.. module:: itertools
- :synopsis: Functions creating iterators for efficient looping.
+ :synopsis: Functions creating iterators for efficient looping.
.. moduleauthor:: Raymond Hettinger
.. sectionauthor:: Raymond Hettinger
.. testsetup::
- from itertools import *
+ from itertools import *
This module implements a number of :term:`iterator` building blocks inspired
@@ -46,7 +46,7 @@
==================== ============================ ================================================= =============================================================
Iterator Arguments Results Example
==================== ============================ ================================================= =============================================================
-:func:`accumulate` p[, start=0] p0, p0+p1, p0+p1+p2 ... ` ``accumulate([1,2,3,4,5]) --> 1 3 6 10 15``
+:func:`accumulate` p[, start=0] p0, p0+p1, p0+p1+p2, ... ``accumulate([1,2,3,4,5]) --> 1 3 6 10 15``
:func:`chain` p, q, ... p0, p1, ... plast, q0, q1, ... ``chain('ABC', 'DEF') --> A B C D E F``
:func:`compress` data, selectors (d[0] if s[0]), (d[1] if s[1]), ... ``compress('ABCDEF', [1,0,1,0,1,1]) --> A C E F``
:func:`dropwhile` pred, seq seq[n], seq[n+1], starting when pred fails ``dropwhile(lambda x: x<5, [1,4,6,4,1]) --> 6 4 1``
@@ -102,44 +102,44 @@
.. function:: chain(*iterables)
- Make an iterator that returns elements from the first iterable until it is
- exhausted, then proceeds to the next iterable, until all of the iterables are
- exhausted. Used for treating consecutive sequences as a single sequence.
- Equivalent to::
-
- def chain(*iterables):
- # chain('ABC', 'DEF') --> A B C D E F
- for it in iterables:
- for element in it:
- yield element
+ Make an iterator that returns elements from the first iterable until it is
+ exhausted, then proceeds to the next iterable, until all of the iterables are
+ exhausted. Used for treating consecutive sequences as a single sequence.
+ Equivalent to::
+
+ def chain(*iterables):
+ # chain('ABC', 'DEF') --> A B C D E F
+ for it in iterables:
+ for element in it:
+ yield element
.. classmethod:: chain.from_iterable(iterable)
- Alternate constructor for :func:`chain`. Gets chained inputs from a
- single iterable argument that is evaluated lazily. Equivalent to::
+ Alternate constructor for :func:`chain`. Gets chained inputs from a
+ single iterable argument that is evaluated lazily. Equivalent to::
- @classmethod
- def from_iterable(iterables):
- # chain.from_iterable(['ABC', 'DEF']) --> A B C D E F
- for it in iterables:
- for element in it:
- yield element
+ @classmethod
+ def from_iterable(iterables):
+ # chain.from_iterable(['ABC', 'DEF']) --> A B C D E F
+ for it in iterables:
+ for element in it:
+ yield element
.. function:: combinations(iterable, r)
- Return *r* length subsequences of elements from the input *iterable*.
+ Return *r* length subsequences of elements from the input *iterable*.
- Combinations are emitted in lexicographic sort order. So, if the
- input *iterable* is sorted, the combination tuples will be produced
- in sorted order.
+ Combinations are emitted in lexicographic sort order. So, if the
+ input *iterable* is sorted, the combination tuples will be produced
+ in sorted order.
- Elements are treated as unique based on their position, not on their
- value. So if the input elements are unique, there will be no repeat
- values in each combination.
+ Elements are treated as unique based on their position, not on their
+ value. So if the input elements are unique, there will be no repeat
+ values in each combination.
- Equivalent to::
+ Equivalent to::
def combinations(iterable, r):
# combinations('ABCD', 2) --> AB AC AD BC BD CD
@@ -161,9 +161,9 @@
indices[j] = indices[j-1] + 1
yield tuple(pool[i] for i in indices)
- The code for :func:`combinations` can be also expressed as a subsequence
- of :func:`permutations` after filtering entries where the elements are not
- in sorted order (according to their position in the input pool)::
+ The code for :func:`combinations` can be also expressed as a subsequence
+ of :func:`permutations` after filtering entries where the elements are not
+ in sorted order (according to their position in the input pool)::
def combinations(iterable, r):
pool = tuple(iterable)
@@ -172,23 +172,23 @@
if sorted(indices) == list(indices):
yield tuple(pool[i] for i in indices)
- The number of items returned is ``n! / r! / (n-r)!`` when ``0 <= r <= n``
- or zero when ``r > n``.
+ The number of items returned is ``n! / r! / (n-r)!`` when ``0 <= r <= n``
+ or zero when ``r > n``.
.. function:: combinations_with_replacement(iterable, r)
- Return *r* length subsequences of elements from the input *iterable*
- allowing individual elements to be repeated more than once.
+ Return *r* length subsequences of elements from the input *iterable*
+ allowing individual elements to be repeated more than once.
- Combinations are emitted in lexicographic sort order. So, if the
- input *iterable* is sorted, the combination tuples will be produced
- in sorted order.
-
- Elements are treated as unique based on their position, not on their
- value. So if the input elements are unique, the generated combinations
- will also be unique.
+ Combinations are emitted in lexicographic sort order. So, if the
+ input *iterable* is sorted, the combination tuples will be produced
+ in sorted order.
+
+ Elements are treated as unique based on their position, not on their
+ value. So if the input elements are unique, the generated combinations
+ will also be unique.
- Equivalent to::
+ Equivalent to::
def combinations_with_replacement(iterable, r):
# combinations_with_replacement('ABC', 2) --> AA AB AC BB BC CC
@@ -207,9 +207,9 @@
indices[i:] = [indices[i] + 1] * (r - i)
yield tuple(pool[i] for i in indices)
- The code for :func:`combinations_with_replacement` can be also expressed as
- a subsequence of :func:`product` after filtering entries where the elements
- are not in sorted order (according to their position in the input pool)::
+ The code for :func:`combinations_with_replacement` can be also expressed as
+ a subsequence of :func:`product` after filtering entries where the elements
+ are not in sorted order (according to their position in the input pool)::
def combinations_with_replacement(iterable, r):
pool = tuple(iterable)
@@ -218,196 +218,196 @@
if sorted(indices) == list(indices):
yield tuple(pool[i] for i in indices)
- The number of items returned is ``(n+r-1)! / r! / (n-1)!`` when ``n > 0``.
+ The number of items returned is ``(n+r-1)! / r! / (n-1)!`` when ``n > 0``.
- .. versionadded:: 3.1
+ .. versionadded:: 3.1
.. function:: compress(data, selectors)
- Make an iterator that filters elements from *data* returning only those that
- have a corresponding element in *selectors* that evaluates to ``True``.
- Stops when either the *data* or *selectors* iterables has been exhausted.
- Equivalent to::
-
- def compress(data, selectors):
- # compress('ABCDEF', [1,0,1,0,1,1]) --> A C E F
- return (d for d, s in zip(data, selectors) if s)
+ Make an iterator that filters elements from *data* returning only those that
+ have a corresponding element in *selectors* that evaluates to ``True``.
+ Stops when either the *data* or *selectors* iterables has been exhausted.
+ Equivalent to::
+
+ def compress(data, selectors):
+ # compress('ABCDEF', [1,0,1,0,1,1]) --> A C E F
+ return (d for d, s in zip(data, selectors) if s)
- .. versionadded:: 3.1
+ .. versionadded:: 3.1
.. function:: count(start=0, step=1)
- Make an iterator that returns evenly spaced values starting with *n*. Often
- used as an argument to :func:`map` to generate consecutive data points.
- Also, used with :func:`zip` to add sequence numbers. Equivalent to::
-
- def count(start=0, step=1):
- # count(10) --> 10 11 12 13 14 ...
- # count(2.5, 0.5) -> 3.5 3.0 4.5 ...
- n = start
- while True:
- yield n
- n += step
+ Make an iterator that returns evenly spaced values starting with *n*. Often
+ used as an argument to :func:`map` to generate consecutive data points.
+ Also, used with :func:`zip` to add sequence numbers. Equivalent to::
+
+ def count(start=0, step=1):
+ # count(10) --> 10 11 12 13 14 ...
+ # count(2.5, 0.5) -> 3.5 3.0 4.5 ...
+ n = start
+ while True:
+ yield n
+ n += step
+
+ When counting with floating point numbers, better accuracy can sometimes be
+ achieved by substituting multiplicative code such as: ``(start + step * i
+ for i in count())``.
- When counting with floating point numbers, better accuracy can sometimes be
- achieved by substituting multiplicative code such as: ``(start + step * i
- for i in count())``.
-
- .. versionchanged:: 3.1
- Added *step* argument and allowed non-integer arguments.
+ .. versionchanged:: 3.1
+ Added *step* argument and allowed non-integer arguments.
.. function:: cycle(iterable)
- Make an iterator returning elements from the iterable and saving a copy of each.
- When the iterable is exhausted, return elements from the saved copy. Repeats
- indefinitely. Equivalent to::
-
- def cycle(iterable):
- # cycle('ABCD') --> A B C D A B C D A B C D ...
- saved = []
- for element in iterable:
- yield element
- saved.append(element)
- while saved:
- for element in saved:
+ Make an iterator returning elements from the iterable and saving a copy of each.
+ When the iterable is exhausted, return elements from the saved copy. Repeats
+ indefinitely. Equivalent to::
+
+ def cycle(iterable):
+ # cycle('ABCD') --> A B C D A B C D A B C D ...
+ saved = []
+ for element in iterable:
+ yield element
+ saved.append(element)
+ while saved:
+ for element in saved:
yield element
- Note, this member of the toolkit may require significant auxiliary storage
- (depending on the length of the iterable).
+ Note, this member of the toolkit may require significant auxiliary storage
+ (depending on the length of the iterable).
.. function:: dropwhile(predicate, iterable)
- Make an iterator that drops elements from the iterable as long as the predicate
- is true; afterwards, returns every element. Note, the iterator does not produce
- *any* output until the predicate first becomes false, so it may have a lengthy
- start-up time. Equivalent to::
-
- def dropwhile(predicate, iterable):
- # dropwhile(lambda x: x<5, [1,4,6,4,1]) --> 6 4 1
- iterable = iter(iterable)
- for x in iterable:
- if not predicate(x):
- yield x
- break
- for x in iterable:
- yield x
+ Make an iterator that drops elements from the iterable as long as the predicate
+ is true; afterwards, returns every element. Note, the iterator does not produce
+ *any* output until the predicate first becomes false, so it may have a lengthy
+ start-up time. Equivalent to::
+
+ def dropwhile(predicate, iterable):
+ # dropwhile(lambda x: x<5, [1,4,6,4,1]) --> 6 4 1
+ iterable = iter(iterable)
+ for x in iterable:
+ if not predicate(x):
+ yield x
+ break
+ for x in iterable:
+ yield x
.. function:: filterfalse(predicate, iterable)
- Make an iterator that filters elements from iterable returning only those for
- which the predicate is ``False``. If *predicate* is ``None``, return the items
- that are false. Equivalent to::
-
- def filterfalse(predicate, iterable):
- # filterfalse(lambda x: x%2, range(10)) --> 0 2 4 6 8
- if predicate is None:
- predicate = bool
- for x in iterable:
- if not predicate(x):
- yield x
+ Make an iterator that filters elements from iterable returning only those for
+ which the predicate is ``False``. If *predicate* is ``None``, return the items
+ that are false. Equivalent to::
+
+ def filterfalse(predicate, iterable):
+ # filterfalse(lambda x: x%2, range(10)) --> 0 2 4 6 8
+ if predicate is None:
+ predicate = bool
+ for x in iterable:
+ if not predicate(x):
+ yield x
.. function:: groupby(iterable, key=None)
- Make an iterator that returns consecutive keys and groups from the *iterable*.
- The *key* is a function computing a key value for each element. If not
- specified or is ``None``, *key* defaults to an identity function and returns
- the element unchanged. Generally, the iterable needs to already be sorted on
- the same key function.
-
- The operation of :func:`groupby` is similar to the ``uniq`` filter in Unix. It
- generates a break or new group every time the value of the key function changes
- (which is why it is usually necessary to have sorted the data using the same key
- function). That behavior differs from SQL's GROUP BY which aggregates common
- elements regardless of their input order.
-
- The returned group is itself an iterator that shares the underlying iterable
- with :func:`groupby`. Because the source is shared, when the :func:`groupby`
- object is advanced, the previous group is no longer visible. So, if that data
- is needed later, it should be stored as a list::
-
- groups = []
- uniquekeys = []
- data = sorted(data, key=keyfunc)
- for k, g in groupby(data, keyfunc):
- groups.append(list(g)) # Store group iterator as a list
- uniquekeys.append(k)
-
- :func:`groupby` is equivalent to::
-
- class groupby:
- # [k for k, g in groupby('AAAABBBCCDAABBB')] --> A B C D A B
- # [list(g) for k, g in groupby('AAAABBBCCD')] --> AAAA BBB CC D
- def __init__(self, iterable, key=None):
- if key is None:
- key = lambda x: x
- self.keyfunc = key
- self.it = iter(iterable)
- self.tgtkey = self.currkey = self.currvalue = object()
- def __iter__(self):
- return self
- def __next__(self):
- while self.currkey == self.tgtkey:
- self.currvalue = next(self.it) # Exit on StopIteration
- self.currkey = self.keyfunc(self.currvalue)
- self.tgtkey = self.currkey
- return (self.currkey, self._grouper(self.tgtkey))
- def _grouper(self, tgtkey):
- while self.currkey == tgtkey:
- yield self.currvalue
- self.currvalue = next(self.it) # Exit on StopIteration
- self.currkey = self.keyfunc(self.currvalue)
+ Make an iterator that returns consecutive keys and groups from the *iterable*.
+ The *key* is a function computing a key value for each element. If not
+ specified or is ``None``, *key* defaults to an identity function and returns
+ the element unchanged. Generally, the iterable needs to already be sorted on
+ the same key function.
+
+ The operation of :func:`groupby` is similar to the ``uniq`` filter in Unix. It
+ generates a break or new group every time the value of the key function changes
+ (which is why it is usually necessary to have sorted the data using the same key
+ function). That behavior differs from SQL's GROUP BY which aggregates common
+ elements regardless of their input order.
+
+ The returned group is itself an iterator that shares the underlying iterable
+ with :func:`groupby`. Because the source is shared, when the :func:`groupby`
+ object is advanced, the previous group is no longer visible. So, if that data
+ is needed later, it should be stored as a list::
+
+ groups = []
+ uniquekeys = []
+ data = sorted(data, key=keyfunc)
+ for k, g in groupby(data, keyfunc):
+ groups.append(list(g)) # Store group iterator as a list
+ uniquekeys.append(k)
+
+ :func:`groupby` is equivalent to::
+
+ class groupby:
+ # [k for k, g in groupby('AAAABBBCCDAABBB')] --> A B C D A B
+ # [list(g) for k, g in groupby('AAAABBBCCD')] --> AAAA BBB CC D
+ def __init__(self, iterable, key=None):
+ if key is None:
+ key = lambda x: x
+ self.keyfunc = key
+ self.it = iter(iterable)
+ self.tgtkey = self.currkey = self.currvalue = object()
+ def __iter__(self):
+ return self
+ def __next__(self):
+ while self.currkey == self.tgtkey:
+ self.currvalue = next(self.it) # Exit on StopIteration
+ self.currkey = self.keyfunc(self.currvalue)
+ self.tgtkey = self.currkey
+ return (self.currkey, self._grouper(self.tgtkey))
+ def _grouper(self, tgtkey):
+ while self.currkey == tgtkey:
+ yield self.currvalue
+ self.currvalue = next(self.it) # Exit on StopIteration
+ self.currkey = self.keyfunc(self.currvalue)
.. function:: islice(iterable, [start,] stop [, step])
- Make an iterator that returns selected elements from the iterable. If *start* is
- non-zero, then elements from the iterable are skipped until start is reached.
- Afterward, elements are returned consecutively unless *step* is set higher than
- one which results in items being skipped. If *stop* is ``None``, then iteration
- continues until the iterator is exhausted, if at all; otherwise, it stops at the
- specified position. Unlike regular slicing, :func:`islice` does not support
- negative values for *start*, *stop*, or *step*. Can be used to extract related
- fields from data where the internal structure has been flattened (for example, a
- multi-line report may list a name field on every third line). Equivalent to::
-
- def islice(iterable, *args):
- # islice('ABCDEFG', 2) --> A B
- # islice('ABCDEFG', 2, 4) --> C D
- # islice('ABCDEFG', 2, None) --> C D E F G
- # islice('ABCDEFG', 0, None, 2) --> A C E G
- s = slice(*args)
- it = iter(range(s.start or 0, s.stop or sys.maxsize, s.step or 1))
- nexti = next(it)
- for i, element in enumerate(iterable):
- if i == nexti:
- yield element
- nexti = next(it)
+ Make an iterator that returns selected elements from the iterable. If *start* is
+ non-zero, then elements from the iterable are skipped until start is reached.
+ Afterward, elements are returned consecutively unless *step* is set higher than
+ one which results in items being skipped. If *stop* is ``None``, then iteration
+ continues until the iterator is exhausted, if at all; otherwise, it stops at the
+ specified position. Unlike regular slicing, :func:`islice` does not support
+ negative values for *start*, *stop*, or *step*. Can be used to extract related
+ fields from data where the internal structure has been flattened (for example, a
+ multi-line report may list a name field on every third line). Equivalent to::
+
+ def islice(iterable, *args):
+ # islice('ABCDEFG', 2) --> A B
+ # islice('ABCDEFG', 2, 4) --> C D
+ # islice('ABCDEFG', 2, None) --> C D E F G
+ # islice('ABCDEFG', 0, None, 2) --> A C E G
+ s = slice(*args)
+ it = iter(range(s.start or 0, s.stop or sys.maxsize, s.step or 1))
+ nexti = next(it)
+ for i, element in enumerate(iterable):
+ if i == nexti:
+ yield element
+ nexti = next(it)
- If *start* is ``None``, then iteration starts at zero. If *step* is ``None``,
- then the step defaults to one.
+ If *start* is ``None``, then iteration starts at zero. If *step* is ``None``,
+ then the step defaults to one.
.. function:: permutations(iterable, r=None)
- Return successive *r* length permutations of elements in the *iterable*.
+ Return successive *r* length permutations of elements in the *iterable*.
- If *r* is not specified or is ``None``, then *r* defaults to the length
- of the *iterable* and all possible full-length permutations
- are generated.
+ If *r* is not specified or is ``None``, then *r* defaults to the length
+ of the *iterable* and all possible full-length permutations
+ are generated.
- Permutations are emitted in lexicographic sort order. So, if the
- input *iterable* is sorted, the permutation tuples will be produced
- in sorted order.
+ Permutations are emitted in lexicographic sort order. So, if the
+ input *iterable* is sorted, the permutation tuples will be produced
+ in sorted order.
- Elements are treated as unique based on their position, not on their
- value. So if the input elements are unique, there will be no repeat
- values in each permutation.
+ Elements are treated as unique based on their position, not on their
+ value. So if the input elements are unique, there will be no repeat
+ values in each permutation.
- Equivalent to::
+ Equivalent to::
def permutations(iterable, r=None):
# permutations('ABCD', 2) --> AB AC AD BA BC BD CA CB CD DA DB DC
@@ -434,9 +434,9 @@
else:
return
- The code for :func:`permutations` can be also expressed as a subsequence of
- :func:`product`, filtered to exclude entries with repeated elements (those
- from the same position in the input pool)::
+ The code for :func:`permutations` can be also expressed as a subsequence of
+ :func:`product`, filtered to exclude entries with repeated elements (those
+ from the same position in the input pool)::
def permutations(iterable, r=None):
pool = tuple(iterable)
@@ -446,87 +446,87 @@
if len(set(indices)) == r:
yield tuple(pool[i] for i in indices)
- The number of items returned is ``n! / (n-r)!`` when ``0 <= r <= n``
- or zero when ``r > n``.
+ The number of items returned is ``n! / (n-r)!`` when ``0 <= r <= n``
+ or zero when ``r > n``.
.. function:: product(*iterables, repeat=1)
- Cartesian product of input iterables.
+ Cartesian product of input iterables.
- Equivalent to nested for-loops in a generator expression. For example,
- ``product(A, B)`` returns the same as ``((x,y) for x in A for y in B)``.
+ Equivalent to nested for-loops in a generator expression. For example,
+ ``product(A, B)`` returns the same as ``((x,y) for x in A for y in B)``.
- The nested loops cycle like an odometer with the rightmost element advancing
- on every iteration. This pattern creates a lexicographic ordering so that if
- the input's iterables are sorted, the product tuples are emitted in sorted
- order.
-
- To compute the product of an iterable with itself, specify the number of
- repetitions with the optional *repeat* keyword argument. For example,
- ``product(A, repeat=4)`` means the same as ``product(A, A, A, A)``.
-
- This function is equivalent to the following code, except that the
- actual implementation does not build up intermediate results in memory::
-
- def product(*args, repeat=1):
- # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
- # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
- pools = [tuple(pool) for pool in args] * repeat
- result = [[]]
- for pool in pools:
- result = [x+[y] for x in result for y in pool]
- for prod in result:
- yield tuple(prod)
+ The nested loops cycle like an odometer with the rightmost element advancing
+ on every iteration. This pattern creates a lexicographic ordering so that if
+ the input's iterables are sorted, the product tuples are emitted in sorted
+ order.
+
+ To compute the product of an iterable with itself, specify the number of
+ repetitions with the optional *repeat* keyword argument. For example,
+ ``product(A, repeat=4)`` means the same as ``product(A, A, A, A)``.
+
+ This function is equivalent to the following code, except that the
+ actual implementation does not build up intermediate results in memory::
+
+ def product(*args, repeat=1):
+ # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
+ # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
+ pools = [tuple(pool) for pool in args] * repeat
+ result = [[]]
+ for pool in pools:
+ result = [x+[y] for x in result for y in pool]
+ for prod in result:
+ yield tuple(prod)
.. function:: repeat(object[, times])
- Make an iterator that returns *object* over and over again. Runs indefinitely
- unless the *times* argument is specified. Used as argument to :func:`map` for
- invariant parameters to the called function. Also used with :func:`zip` to
- create an invariant part of a tuple record. Equivalent to::
-
- def repeat(object, times=None):
- # repeat(10, 3) --> 10 10 10
- if times is None:
- while True:
- yield object
- else:
- for i in range(times):
- yield object
+ Make an iterator that returns *object* over and over again. Runs indefinitely
+ unless the *times* argument is specified. Used as argument to :func:`map` for
+ invariant parameters to the called function. Also used with :func:`zip` to
+ create an invariant part of a tuple record. Equivalent to::
+
+ def repeat(object, times=None):
+ # repeat(10, 3) --> 10 10 10
+ if times is None:
+ while True:
+ yield object
+ else:
+ for i in range(times):
+ yield object
.. function:: starmap(function, iterable)
- Make an iterator that computes the function using arguments obtained from
- the iterable. Used instead of :func:`map` when argument parameters are already
- grouped in tuples from a single iterable (the data has been "pre-zipped"). The
- difference between :func:`map` and :func:`starmap` parallels the distinction
- between ``function(a,b)`` and ``function(*c)``. Equivalent to::
-
- def starmap(function, iterable):
- # starmap(pow, [(2,5), (3,2), (10,3)]) --> 32 9 1000
- for args in iterable:
- yield function(*args)
+ Make an iterator that computes the function using arguments obtained from
+ the iterable. Used instead of :func:`map` when argument parameters are already
+ grouped in tuples from a single iterable (the data has been "pre-zipped"). The
+ difference between :func:`map` and :func:`starmap` parallels the distinction
+ between ``function(a,b)`` and ``function(*c)``. Equivalent to::
+
+ def starmap(function, iterable):
+ # starmap(pow, [(2,5), (3,2), (10,3)]) --> 32 9 1000
+ for args in iterable:
+ yield function(*args)
.. function:: takewhile(predicate, iterable)
- Make an iterator that returns elements from the iterable as long as the
- predicate is true. Equivalent to::
+ Make an iterator that returns elements from the iterable as long as the
+ predicate is true. Equivalent to::
- def takewhile(predicate, iterable):
- # takewhile(lambda x: x<5, [1,4,6,4,1]) --> 1 4
- for x in iterable:
- if predicate(x):
- yield x
- else:
- break
+ def takewhile(predicate, iterable):
+ # takewhile(lambda x: x<5, [1,4,6,4,1]) --> 1 4
+ for x in iterable:
+ if predicate(x):
+ yield x
+ else:
+ break
.. function:: tee(iterable, n=2)
- Return *n* independent iterators from a single iterable. Equivalent to::
+ Return *n* independent iterators from a single iterable. Equivalent to::
def tee(iterable, n=2):
it = iter(iterable)
@@ -540,38 +540,38 @@
yield mydeque.popleft()
return tuple(gen(d) for d in deques)
- Once :func:`tee` has made a split, the original *iterable* should not be
- used anywhere else; otherwise, the *iterable* could get advanced without
- the tee objects being informed.
-
- This itertool may require significant auxiliary storage (depending on how
- much temporary data needs to be stored). In general, if one iterator uses
- most or all of the data before another iterator starts, it is faster to use
- :func:`list` instead of :func:`tee`.
+ Once :func:`tee` has made a split, the original *iterable* should not be
+ used anywhere else; otherwise, the *iterable* could get advanced without
+ the tee objects being informed.
+
+ This itertool may require significant auxiliary storage (depending on how
+ much temporary data needs to be stored). In general, if one iterator uses
+ most or all of the data before another iterator starts, it is faster to use
+ :func:`list` instead of :func:`tee`.
.. function:: zip_longest(*iterables, fillvalue=None)
- Make an iterator that aggregates elements from each of the iterables. If the
- iterables are of uneven length, missing values are filled-in with *fillvalue*.
- Iteration continues until the longest iterable is exhausted. Equivalent to::
-
- def zip_longest(*args, fillvalue=None):
- # zip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D-
- def sentinel(counter = ([fillvalue]*(len(args)-1)).pop):
- yield counter() # yields the fillvalue, or raises IndexError
- fillers = repeat(fillvalue)
- iters = [chain(it, sentinel(), fillers) for it in args]
- try:
- for tup in zip(*iters):
- yield tup
- except IndexError:
- pass
-
- If one of the iterables is potentially infinite, then the :func:`zip_longest`
- function should be wrapped with something that limits the number of calls
- (for example :func:`islice` or :func:`takewhile`). If not specified,
- *fillvalue* defaults to ``None``.
+ Make an iterator that aggregates elements from each of the iterables. If the
+ iterables are of uneven length, missing values are filled-in with *fillvalue*.
+ Iteration continues until the longest iterable is exhausted. Equivalent to::
+
+ def zip_longest(*args, fillvalue=None):
+ # zip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D-
+ def sentinel(counter = ([fillvalue]*(len(args)-1)).pop):
+ yield counter() # yields the fillvalue, or raises IndexError
+ fillers = repeat(fillvalue)
+ iters = [chain(it, sentinel(), fillers) for it in args]
+ try:
+ for tup in zip(*iters):
+ yield tup
+ except IndexError:
+ pass
+
+ If one of the iterables is potentially infinite, then the :func:`zip_longest`
+ function should be wrapped with something that limits the number of calls
+ (for example :func:`islice` or :func:`takewhile`). If not specified,
+ *fillvalue* defaults to ``None``.
.. _itertools-recipes:
@@ -592,168 +592,168 @@
.. testcode::
- def take(n, iterable):
- "Return first n items of the iterable as a list"
- return list(islice(iterable, n))
-
- def tabulate(function, start=0):
- "Return function(0), function(1), ..."
- return map(function, count(start))
-
- def consume(iterator, n):
- "Advance the iterator n-steps ahead. If n is none, consume entirely."
- # Use functions that consume iterators at C speed.
- if n is None:
- # feed the entire iterator into a zero-length deque
- collections.deque(iterator, maxlen=0)
- else:
- # advance to the empty slice starting at position n
- next(islice(iterator, n, n), None)
-
- def nth(iterable, n, default=None):
- "Returns the nth item or a default value"
- return next(islice(iterable, n, None), default)
-
- def quantify(iterable, pred=bool):
- "Count how many times the predicate is true"
- return sum(map(pred, iterable))
-
- def padnone(iterable):
- """Returns the sequence elements and then returns None indefinitely.
-
- Useful for emulating the behavior of the built-in map() function.
- """
- return chain(iterable, repeat(None))
-
- def ncycles(iterable, n):
- "Returns the sequence elements n times"
- return chain.from_iterable(repeat(tuple(iterable), n))
-
- def dotproduct(vec1, vec2):
- return sum(map(operator.mul, vec1, vec2))
-
- def flatten(listOfLists):
- "Flatten one level of nesting"
- return chain.from_iterable(listOfLists)
-
- def repeatfunc(func, times=None, *args):
- """Repeat calls to func with specified arguments.
-
- Example: repeatfunc(random.random)
- """
- if times is None:
- return starmap(func, repeat(args))
- return starmap(func, repeat(args, times))
-
- def pairwise(iterable):
- "s -> (s0,s1), (s1,s2), (s2, s3), ..."
- a, b = tee(iterable)
- next(b, None)
- return zip(a, b)
-
- def grouper(n, iterable, fillvalue=None):
- "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
- args = [iter(iterable)] * n
- return zip_longest(*args, fillvalue=fillvalue)
-
- def roundrobin(*iterables):
- "roundrobin('ABC', 'D', 'EF') --> A D E B F C"
- # Recipe credited to George Sakkis
- pending = len(iterables)
- nexts = cycle(iter(it).__next__ for it in iterables)
- while pending:
- try:
- for next in nexts:
- yield next()
- except StopIteration:
- pending -= 1
- nexts = cycle(islice(nexts, pending))
-
- def partition(pred, iterable):
- 'Use a predicate to partition entries into false entries and true entries'
- # partition(is_odd, range(10)) --> 0 2 4 6 8 and 1 3 5 7 9
- t1, t2 = tee(iterable)
- return filterfalse(pred, t1), filter(pred, t2)
-
- def powerset(iterable):
- "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
- s = list(iterable)
- return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
-
- def unique_everseen(iterable, key=None):
- "List unique elements, preserving order. Remember all elements ever seen."
- # unique_everseen('AAAABBBCCDAABBB') --> A B C D
- # unique_everseen('ABBCcAD', str.lower) --> A B C D
- seen = set()
- seen_add = seen.add
- if key is None:
- for element in filterfalse(seen.__contains__, iterable):
- seen_add(element)
- yield element
- else:
- for element in iterable:
- k = key(element)
- if k not in seen:
- seen_add(k)
- yield element
-
- def unique_justseen(iterable, key=None):
- "List unique elements, preserving order. Remember only the element just seen."
- # unique_justseen('AAAABBBCCDAABBB') --> A B C D A B
- # unique_justseen('ABBCcAD', str.lower) --> A B C A D
- return map(next, map(itemgetter(1), groupby(iterable, key)))
-
- def iter_except(func, exception, first=None):
- """ Call a function repeatedly until an exception is raised.
-
- Converts a call-until-exception interface to an iterator interface.
- Like __builtin__.iter(func, sentinel) but uses an exception instead
- of a sentinel to end the loop.
-
- Examples:
- iter_except(functools.partial(heappop, h), IndexError) # priority queue iterator
- iter_except(d.popitem, KeyError) # non-blocking dict iterator
- iter_except(d.popleft, IndexError) # non-blocking deque iterator
- iter_except(q.get_nowait, Queue.Empty) # loop over a producer Queue
- iter_except(s.pop, KeyError) # non-blocking set iterator
-
- """
- try:
- if first is not None:
- yield first() # For database APIs needing an initial cast to db.first()
- while 1:
- yield func()
- except exception:
- pass
-
- def random_product(*args, repeat=1):
- "Random selection from itertools.product(*args, **kwds)"
- pools = [tuple(pool) for pool in args] * repeat
- return tuple(random.choice(pool) for pool in pools)
-
- def random_permutation(iterable, r=None):
- "Random selection from itertools.permutations(iterable, r)"
- pool = tuple(iterable)
- r = len(pool) if r is None else r
- return tuple(random.sample(pool, r))
-
- def random_combination(iterable, r):
- "Random selection from itertools.combinations(iterable, r)"
- pool = tuple(iterable)
- n = len(pool)
- indices = sorted(random.sample(range(n), r))
- return tuple(pool[i] for i in indices)
-
- def random_combination_with_replacement(iterable, r):
- "Random selection from itertools.combinations_with_replacement(iterable, r)"
- pool = tuple(iterable)
- n = len(pool)
- indices = sorted(random.randrange(n) for i in range(r))
- return tuple(pool[i] for i in indices)
+ def take(n, iterable):
+ "Return first n items of the iterable as a list"
+ return list(islice(iterable, n))
+
+ def tabulate(function, start=0):
+ "Return function(0), function(1), ..."
+ return map(function, count(start))
+
+ def consume(iterator, n):
+ "Advance the iterator n-steps ahead. If n is none, consume entirely."
+ # Use functions that consume iterators at C speed.
+ if n is None:
+ # feed the entire iterator into a zero-length deque
+ collections.deque(iterator, maxlen=0)
+ else:
+ # advance to the empty slice starting at position n
+ next(islice(iterator, n, n), None)
+
+ def nth(iterable, n, default=None):
+ "Returns the nth item or a default value"
+ return next(islice(iterable, n, None), default)
+
+ def quantify(iterable, pred=bool):
+ "Count how many times the predicate is true"
+ return sum(map(pred, iterable))
+
+ def padnone(iterable):
+ """Returns the sequence elements and then returns None indefinitely.
+
+ Useful for emulating the behavior of the built-in map() function.
+ """
+ return chain(iterable, repeat(None))
+
+ def ncycles(iterable, n):
+ "Returns the sequence elements n times"
+ return chain.from_iterable(repeat(tuple(iterable), n))
+
+ def dotproduct(vec1, vec2):
+ return sum(map(operator.mul, vec1, vec2))
+
+ def flatten(listOfLists):
+ "Flatten one level of nesting"
+ return chain.from_iterable(listOfLists)
+
+ def repeatfunc(func, times=None, *args):
+ """Repeat calls to func with specified arguments.
+
+ Example: repeatfunc(random.random)
+ """
+ if times is None:
+ return starmap(func, repeat(args))
+ return starmap(func, repeat(args, times))
+
+ def pairwise(iterable):
+ "s -> (s0,s1), (s1,s2), (s2, s3), ..."
+ a, b = tee(iterable)
+ next(b, None)
+ return zip(a, b)
+
+ def grouper(n, iterable, fillvalue=None):
+ "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
+ args = [iter(iterable)] * n
+ return zip_longest(*args, fillvalue=fillvalue)
+
+ def roundrobin(*iterables):
+ "roundrobin('ABC', 'D', 'EF') --> A D E B F C"
+ # Recipe credited to George Sakkis
+ pending = len(iterables)
+ nexts = cycle(iter(it).__next__ for it in iterables)
+ while pending:
+ try:
+ for next in nexts:
+ yield next()
+ except StopIteration:
+ pending -= 1
+ nexts = cycle(islice(nexts, pending))
+
+ def partition(pred, iterable):
+ 'Use a predicate to partition entries into false entries and true entries'
+ # partition(is_odd, range(10)) --> 0 2 4 6 8 and 1 3 5 7 9
+ t1, t2 = tee(iterable)
+ return filterfalse(pred, t1), filter(pred, t2)
+
+ def powerset(iterable):
+ "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
+ s = list(iterable)
+ return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
+
+ def unique_everseen(iterable, key=None):
+ "List unique elements, preserving order. Remember all elements ever seen."
+ # unique_everseen('AAAABBBCCDAABBB') --> A B C D
+ # unique_everseen('ABBCcAD', str.lower) --> A B C D
+ seen = set()
+ seen_add = seen.add
+ if key is None:
+ for element in filterfalse(seen.__contains__, iterable):
+ seen_add(element)
+ yield element
+ else:
+ for element in iterable:
+ k = key(element)
+ if k not in seen:
+ seen_add(k)
+ yield element
+
+ def unique_justseen(iterable, key=None):
+ "List unique elements, preserving order. Remember only the element just seen."
+ # unique_justseen('AAAABBBCCDAABBB') --> A B C D A B
+ # unique_justseen('ABBCcAD', str.lower) --> A B C A D
+ return map(next, map(itemgetter(1), groupby(iterable, key)))
+
+ def iter_except(func, exception, first=None):
+ """ Call a function repeatedly until an exception is raised.
+
+ Converts a call-until-exception interface to an iterator interface.
+ Like __builtin__.iter(func, sentinel) but uses an exception instead
+ of a sentinel to end the loop.
+
+ Examples:
+ iter_except(functools.partial(heappop, h), IndexError) # priority queue iterator
+ iter_except(d.popitem, KeyError) # non-blocking dict iterator
+ iter_except(d.popleft, IndexError) # non-blocking deque iterator
+ iter_except(q.get_nowait, Queue.Empty) # loop over a producer Queue
+ iter_except(s.pop, KeyError) # non-blocking set iterator
+
+ """
+ try:
+ if first is not None:
+ yield first() # For database APIs needing an initial cast to db.first()
+ while 1:
+ yield func()
+ except exception:
+ pass
+
+ def random_product(*args, repeat=1):
+ "Random selection from itertools.product(*args, **kwds)"
+ pools = [tuple(pool) for pool in args] * repeat
+ return tuple(random.choice(pool) for pool in pools)
+
+ def random_permutation(iterable, r=None):
+ "Random selection from itertools.permutations(iterable, r)"
+ pool = tuple(iterable)
+ r = len(pool) if r is None else r
+ return tuple(random.sample(pool, r))
+
+ def random_combination(iterable, r):
+ "Random selection from itertools.combinations(iterable, r)"
+ pool = tuple(iterable)
+ n = len(pool)
+ indices = sorted(random.sample(range(n), r))
+ return tuple(pool[i] for i in indices)
+
+ def random_combination_with_replacement(iterable, r):
+ "Random selection from itertools.combinations_with_replacement(iterable, r)"
+ pool = tuple(iterable)
+ n = len(pool)
+ indices = sorted(random.randrange(n) for i in range(r))
+ return tuple(pool[i] for i in indices)
Note, many of the above recipes can be optimized by replacing global lookups
with local variables defined as default values. For example, the
*dotproduct* recipe can be written as::
- def dotproduct(vec1, vec2, sum=sum, map=map, mul=operator.mul):
- return sum(map(mul, vec1, vec2))
+ def dotproduct(vec1, vec2, sum=sum, map=map, mul=operator.mul):
+ return sum(map(mul, vec1, vec2))
From python-checkins at python.org Thu Dec 2 01:05:58 2010
From: python-checkins at python.org (alexander.belopolsky)
Date: Thu, 2 Dec 2010 01:05:58 +0100 (CET)
Subject: [Python-checkins] r86921 - python/branches/py3k/Doc/whatsnew/3.2.rst
Message-ID: <20101202000558.1E046EE985@mail.python.org>
Author: alexander.belopolsky
Date: Thu Dec 2 01:05:57 2010
New Revision: 86921
Log:
With Raymond's approval added a paragraph describing Unicode 6.0.0 changes. Not reST formatted.
Modified:
python/branches/py3k/Doc/whatsnew/3.2.rst
Modified: python/branches/py3k/Doc/whatsnew/3.2.rst
==============================================================================
--- python/branches/py3k/Doc/whatsnew/3.2.rst (original)
+++ python/branches/py3k/Doc/whatsnew/3.2.rst Thu Dec 2 01:05:57 2010
@@ -661,6 +661,23 @@
Unicode
=======
+Python has been updated to Unicode 6.0.0. The new features of the
+Unicode Standard that will affect Python users include:
+
+* adds 2,088 characters, including over 1,000 additional symbols?chief
+among them the additional emoji symbols, which are especially
+important for mobile phones;
+
+* corrects character properties for existing characters including
+ - a general category change to two Kannada characters (U+0CF1,
+U+0CF2), which has the effect of making them newly eligible for
+inclusion in identifiers;
+
+ - a general category change to one New Tai Lue numeric character
+(U+19DA), which would have the effect of disqualifying it from
+inclusion in identifiers unless grandfathering measures are in place
+for the defining identifier syntax.
+
The :mod:`os` module has two new functions: :func:`~os.fsencode` and
:func:`~os.fsdecode`. Add :data:`os.environb`: bytes version of
:data:`os.environ`, :func:`os.getenvb` function and
From python-checkins at python.org Thu Dec 2 01:10:12 2010
From: python-checkins at python.org (alexander.belopolsky)
Date: Thu, 2 Dec 2010 01:10:12 +0100 (CET)
Subject: [Python-checkins] r86922 -
python/branches/py3k/Lib/test/test_inspect.py
Message-ID: <20101202001012.0236DEE997@mail.python.org>
Author: alexander.belopolsky
Date: Thu Dec 2 01:10:11 2010
New Revision: 86922
Log:
Issue4335: Added a test for inspect.getsourcelines with a module without EOL at EOF.
Modified:
python/branches/py3k/Lib/test/test_inspect.py
Modified: python/branches/py3k/Lib/test/test_inspect.py
==============================================================================
--- python/branches/py3k/Lib/test/test_inspect.py (original)
+++ python/branches/py3k/Lib/test/test_inspect.py Thu Dec 2 01:10:11 2010
@@ -6,9 +6,11 @@
import linecache
import datetime
import collections
+import os
+import shutil
from os.path import normcase
-from test.support import run_unittest
+from test.support import run_unittest, TESTFN, DirsOnSysPath
from test import inspect_fodder as mod
from test import inspect_fodder2 as mod2
@@ -194,12 +196,12 @@
class GetSourceBase(unittest.TestCase):
# Subclasses must override.
- fodderFile = None
+ fodderModule = None
def __init__(self, *args, **kwargs):
unittest.TestCase.__init__(self, *args, **kwargs)
- with open(inspect.getsourcefile(self.fodderFile)) as fp:
+ with open(inspect.getsourcefile(self.fodderModule)) as fp:
self.source = fp.read()
def sourcerange(self, top, bottom):
@@ -211,7 +213,7 @@
self.sourcerange(top, bottom))
class TestRetrievingSourceCode(GetSourceBase):
- fodderFile = mod
+ fodderModule = mod
def test_getclasses(self):
classes = inspect.getmembers(mod, inspect.isclass)
@@ -297,7 +299,7 @@
inspect.getmodule(compile('a=10','','single'))
class TestDecorators(GetSourceBase):
- fodderFile = mod2
+ fodderModule = mod2
def test_wrapped_decorator(self):
self.assertSourceEqual(mod2.wrapped, 14, 17)
@@ -306,7 +308,7 @@
self.assertSourceEqual(mod2.gone, 9, 10)
class TestOneliners(GetSourceBase):
- fodderFile = mod2
+ fodderModule = mod2
def test_oneline_lambda(self):
# Test inspect.getsource with a one-line lambda function.
self.assertSourceEqual(mod2.oll, 25, 25)
@@ -348,7 +350,7 @@
self.assertSourceEqual(mod2.anonymous, 55, 55)
class TestBuggyCases(GetSourceBase):
- fodderFile = mod2
+ fodderModule = mod2
def test_with_comment(self):
self.assertSourceEqual(mod2.with_comment, 58, 59)
@@ -388,6 +390,24 @@
self.assertEqual(inspect.findsource(co), (lines,0))
self.assertEqual(inspect.getsource(co), lines[0])
+class TestNoEOL(GetSourceBase):
+ def __init__(self, *args, **kwargs):
+ self.tempdir = TESTFN + '_dir'
+ os.mkdir(self.tempdir)
+ with open(os.path.join(self.tempdir,
+ 'inspect_fodder3%spy' % os.extsep), 'w') as f:
+ f.write("class X:\n pass # No EOL")
+ with DirsOnSysPath(self.tempdir):
+ import inspect_fodder3 as mod3
+ self.fodderModule = mod3
+ GetSourceBase.__init__(self, *args, **kwargs)
+
+ def tearDown(self):
+ shutil.rmtree(self.tempdir)
+
+ def test_class(self):
+ self.assertSourceEqual(self.fodderModule.X, 1, 2)
+
# Helper for testing classify_class_attrs.
def attrs_wo_objs(cls):
return [t[:3] for t in inspect.classify_class_attrs(cls)]
@@ -945,7 +965,8 @@
TestDecorators, TestRetrievingSourceCode, TestOneliners, TestBuggyCases,
TestInterpreterStack, TestClassesAndFunctions, TestPredicates,
TestGetcallargsFunctions, TestGetcallargsMethods,
- TestGetcallargsUnboundMethods, TestGetattrStatic, TestGetGeneratorState
+ TestGetcallargsUnboundMethods, TestGetattrStatic, TestGetGeneratorState,
+ TestNoEOL
)
if __name__ == "__main__":
From python-checkins at python.org Thu Dec 2 02:38:31 2010
From: python-checkins at python.org (raymond.hettinger)
Date: Thu, 2 Dec 2010 02:38:31 +0100 (CET)
Subject: [Python-checkins] r86923 - python/branches/py3k/Doc/whatsnew/3.2.rst
Message-ID: <20101202013831.19C2AC839@mail.python.org>
Author: raymond.hettinger
Date: Thu Dec 2 02:38:25 2010
New Revision: 86923
Log:
Fix markup
Modified:
python/branches/py3k/Doc/whatsnew/3.2.rst
Modified: python/branches/py3k/Doc/whatsnew/3.2.rst
==============================================================================
--- python/branches/py3k/Doc/whatsnew/3.2.rst (original)
+++ python/branches/py3k/Doc/whatsnew/3.2.rst Thu Dec 2 02:38:25 2010
@@ -665,18 +665,19 @@
Unicode Standard that will affect Python users include:
* adds 2,088 characters, including over 1,000 additional symbols?chief
-among them the additional emoji symbols, which are especially
-important for mobile phones;
+ among them the additional emoji symbols, which are especially
+ important for mobile phones;
* corrects character properties for existing characters including
- - a general category change to two Kannada characters (U+0CF1,
-U+0CF2), which has the effect of making them newly eligible for
-inclusion in identifiers;
-
- - a general category change to one New Tai Lue numeric character
-(U+19DA), which would have the effect of disqualifying it from
-inclusion in identifiers unless grandfathering measures are in place
-for the defining identifier syntax.
+
+ - a general category change to two Kannada characters (U+0CF1,
+ U+0CF2), which has the effect of making them newly eligible for
+ inclusion in identifiers;
+
+ - a general category change to one New Tai Lue numeric character
+ (U+19DA), which would have the effect of disqualifying it from
+ inclusion in identifiers unless grandfathering measures are in place
+ for the defining identifier syntax.
The :mod:`os` module has two new functions: :func:`~os.fsencode` and
:func:`~os.fsdecode`. Add :data:`os.environb`: bytes version of
From python-checkins at python.org Thu Dec 2 03:41:33 2010
From: python-checkins at python.org (raymond.hettinger)
Date: Thu, 2 Dec 2010 03:41:33 +0100 (CET)
Subject: [Python-checkins] r86924 -
python/branches/py3k/Doc/library/random.rst
Message-ID: <20101202024133.CCAF1EE985@mail.python.org>
Author: raymond.hettinger
Date: Thu Dec 2 03:41:33 2010
New Revision: 86924
Log:
Add an example to the random docs.
Modified:
python/branches/py3k/Doc/library/random.rst
Modified: python/branches/py3k/Doc/library/random.rst
==============================================================================
--- python/branches/py3k/Doc/library/random.rst (original)
+++ python/branches/py3k/Doc/library/random.rst Thu Dec 2 03:41:33 2010
@@ -295,3 +295,29 @@
* The generator's :meth:`random` method will continue to produce the same
sequence when the compatible seeder is given the same seed.
+
+
+.. _random-examples:
+
+Examples and Recipes
+====================
+
+A common task is to make a :func:`random.choice` with weighted probababilites.
+
+If the weights are small integer ratios, a simple technique is to build a sample
+population with repeats::
+
+ >>> weighted_choices = [('Red', 3), ('Blue', 2), ('Yellow', 1), ('Green', 4)]
+ >>> population = [val for val, cnt in weighted_choices for i in range(cnt)]
+ >>> random.choice(population)
+ 'Green'
+
+A more general approach is to arrange the weights in a cumulative probability
+distribution with :func:`itertools.accumulate`, and then locate the random value
+with :func:`bisect.bisect`::
+
+ >>> choices, weights = zip(*weighted_choices)
+ >>> cumdist = list(itertools.accumulate(weights))
+ >>> x = random.random() * cumdist[-1]
+ >>> choices[bisect.bisect(cumdist, x)]
+ 'Blue'
From python-checkins at python.org Thu Dec 2 03:58:07 2010
From: python-checkins at python.org (r.david.murray)
Date: Thu, 2 Dec 2010 03:58:07 +0100 (CET)
Subject: [Python-checkins] r86925 - in python/branches/py3k: Lib/netrc.py
Lib/test/test_netrc.py Misc/ACKS Misc/NEWS
Message-ID: <20101202025807.6133EF9A0@mail.python.org>
Author: r.david.murray
Date: Thu Dec 2 03:58:07 2010
New Revision: 86925
Log:
#10464: fix netrc handling of lines with embedded '#" characters.
Patch by Xuanji Li.
Modified:
python/branches/py3k/Lib/netrc.py
python/branches/py3k/Lib/test/test_netrc.py
python/branches/py3k/Misc/ACKS
python/branches/py3k/Misc/NEWS
Modified: python/branches/py3k/Lib/netrc.py
==============================================================================
--- python/branches/py3k/Lib/netrc.py (original)
+++ python/branches/py3k/Lib/netrc.py Thu Dec 2 03:58:07 2010
@@ -34,11 +34,15 @@
def _parse(self, file, fp):
lexer = shlex.shlex(fp)
lexer.wordchars += r"""!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~"""
+ lexer.commenters = lexer.commenters.replace('#', '')
while 1:
# Look for a machine, default, or macdef top-level keyword
toplevel = tt = lexer.get_token()
if not tt:
break
+ elif tt[0] == '#':
+ fp.readline();
+ continue;
elif tt == 'machine':
entryname = lexer.get_token()
elif tt == 'default':
Modified: python/branches/py3k/Lib/test/test_netrc.py
==============================================================================
--- python/branches/py3k/Lib/test/test_netrc.py (original)
+++ python/branches/py3k/Lib/test/test_netrc.py Thu Dec 2 03:58:07 2010
@@ -3,7 +3,13 @@
from test import support
TEST_NETRC = """
+
+ #this is a comment
+#this is a comment
+# this is a comment
+
machine foo login log1 password pass1 account acct1
+machine bar login log1 password pass# account acct1
macdef macro1
line1
@@ -28,17 +34,21 @@
fp = open(temp_filename, mode)
fp.write(TEST_NETRC)
fp.close()
+ self.nrc = netrc.netrc(temp_filename)
def tearDown(self):
os.unlink(temp_filename)
def test_case_1(self):
- nrc = netrc.netrc(temp_filename)
- self.assertTrue(nrc.macros == {'macro1':['line1\n', 'line2\n'],
- 'macro2':['line3\n', 'line4\n']}
- )
- self.assertTrue(nrc.hosts['foo'] == ('log1', 'acct1', 'pass1'))
- self.assertTrue(nrc.hosts['default'] == ('log2', None, 'pass2'))
+ self.assertEqual(self.nrc.hosts['foo'], ('log1', 'acct1', 'pass1'))
+ self.assertEqual(self.nrc.hosts['default'], ('log2', None, 'pass2'))
+
+ def test_macros(self):
+ self.assertEqual(self.nrc.macros, {'macro1':['line1\n', 'line2\n'],
+ 'macro2':['line3\n', 'line4\n']})
+
+ def test_parses_passwords_with_hash_character(self):
+ self.assertEqual(self.nrc.hosts['bar'], ('log1', 'acct1', 'pass#'))
def test_main():
support.run_unittest(NetrcTestCase)
Modified: python/branches/py3k/Misc/ACKS
==============================================================================
--- python/branches/py3k/Misc/ACKS (original)
+++ python/branches/py3k/Misc/ACKS Thu Dec 2 03:58:07 2010
@@ -501,6 +501,7 @@
Christopher Tur Lesniewski-Laas
Mark Levinson
William Lewis
+Xuanji Li
Robert van Liere
Ross Light
Shawn Ligocki
Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS (original)
+++ python/branches/py3k/Misc/NEWS Thu Dec 2 03:58:07 2010
@@ -46,6 +46,8 @@
Library
-------
+- Issue #10464: netrc now correctly handles lines with embedded '#' characters.
+
- Added itertools.accumulate().
- Issue #4113: Added custom ``__repr__`` method to ``functools.partial``.
From python-checkins at python.org Thu Dec 2 04:10:44 2010
From: python-checkins at python.org (r.david.murray)
Date: Thu, 2 Dec 2010 04:10:44 +0100 (CET)
Subject: [Python-checkins] r86926 - in python/branches/release31-maint:
Lib/netrc.py Lib/test/test_netrc.py Misc/ACKS Misc/NEWS
Message-ID: <20101202031044.26BFFEE9E7@mail.python.org>
Author: r.david.murray
Date: Thu Dec 2 04:10:43 2010
New Revision: 86926
Log:
Merged revisions 86925 via svnmerge from
svn+ssh://pythondev at svn.python.org/python/branches/py3k
........
r86925 | r.david.murray | 2010-12-01 21:58:07 -0500 (Wed, 01 Dec 2010) | 4 lines
#10464: fix netrc handling of lines with embedded '#" characters.
Patch by Xuanji Li.
........
Modified:
python/branches/release31-maint/ (props changed)
python/branches/release31-maint/Lib/netrc.py
python/branches/release31-maint/Lib/test/test_netrc.py
python/branches/release31-maint/Misc/ACKS
python/branches/release31-maint/Misc/NEWS
Modified: python/branches/release31-maint/Lib/netrc.py
==============================================================================
--- python/branches/release31-maint/Lib/netrc.py (original)
+++ python/branches/release31-maint/Lib/netrc.py Thu Dec 2 04:10:43 2010
@@ -34,11 +34,15 @@
def _parse(self, file, fp):
lexer = shlex.shlex(fp)
lexer.wordchars += r"""!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~"""
+ lexer.commenters = lexer.commenters.replace('#', '')
while 1:
# Look for a machine, default, or macdef top-level keyword
toplevel = tt = lexer.get_token()
if not tt:
break
+ elif tt[0] == '#':
+ fp.readline();
+ continue;
elif tt == 'machine':
entryname = lexer.get_token()
elif tt == 'default':
Modified: python/branches/release31-maint/Lib/test/test_netrc.py
==============================================================================
--- python/branches/release31-maint/Lib/test/test_netrc.py (original)
+++ python/branches/release31-maint/Lib/test/test_netrc.py Thu Dec 2 04:10:43 2010
@@ -3,7 +3,13 @@
from test import support
TEST_NETRC = """
+
+ #this is a comment
+#this is a comment
+# this is a comment
+
machine foo login log1 password pass1 account acct1
+machine bar login log1 password pass# account acct1
macdef macro1
line1
@@ -28,17 +34,21 @@
fp = open(temp_filename, mode)
fp.write(TEST_NETRC)
fp.close()
+ self.nrc = netrc.netrc(temp_filename)
def tearDown(self):
os.unlink(temp_filename)
def test_case_1(self):
- nrc = netrc.netrc(temp_filename)
- self.assertTrue(nrc.macros == {'macro1':['line1\n', 'line2\n'],
- 'macro2':['line3\n', 'line4\n']}
- )
- self.assertTrue(nrc.hosts['foo'] == ('log1', 'acct1', 'pass1'))
- self.assertTrue(nrc.hosts['default'] == ('log2', None, 'pass2'))
+ self.assertEqual(self.nrc.hosts['foo'], ('log1', 'acct1', 'pass1'))
+ self.assertEqual(self.nrc.hosts['default'], ('log2', None, 'pass2'))
+
+ def test_macros(self):
+ self.assertEqual(self.nrc.macros, {'macro1':['line1\n', 'line2\n'],
+ 'macro2':['line3\n', 'line4\n']})
+
+ def test_parses_passwords_with_hash_character(self):
+ self.assertEqual(self.nrc.hosts['bar'], ('log1', 'acct1', 'pass#'))
def test_main():
support.run_unittest(NetrcTestCase)
Modified: python/branches/release31-maint/Misc/ACKS
==============================================================================
--- python/branches/release31-maint/Misc/ACKS (original)
+++ python/branches/release31-maint/Misc/ACKS Thu Dec 2 04:10:43 2010
@@ -468,6 +468,7 @@
Christopher Tur Lesniewski-Laas
Mark Levinson
William Lewis
+Xuanji Li
Robert van Liere
Ross Light
Shawn Ligocki
Modified: python/branches/release31-maint/Misc/NEWS
==============================================================================
--- python/branches/release31-maint/Misc/NEWS (original)
+++ python/branches/release31-maint/Misc/NEWS Thu Dec 2 04:10:43 2010
@@ -13,6 +13,8 @@
Library
-------
+- Issue #10464: netrc now correctly handles lines with embedded '#' characters.
+
What's New in Python 3.1.3?
===========================
From ncoghlan at gmail.com Thu Dec 2 04:14:12 2010
From: ncoghlan at gmail.com (Nick Coghlan)
Date: Thu, 2 Dec 2010 13:14:12 +1000
Subject: [Python-checkins] r86924 -
python/branches/py3k/Doc/library/random.rst
In-Reply-To: <20101202024133.CCAF1EE985@mail.python.org>
References: <20101202024133.CCAF1EE985@mail.python.org>
Message-ID:
On Thu, Dec 2, 2010 at 12:41 PM, raymond.hettinger
wrote:
> +A more general approach is to arrange the weights in a cumulative probability
> +distribution with :func:`itertools.accumulate`, and then locate the random value
> +with :func:`bisect.bisect`::
> +
> + ? ?>>> choices, weights = zip(*weighted_choices)
> + ? ?>>> cumdist = list(itertools.accumulate(weights))
> + ? ?>>> x = random.random() * cumdist[-1]
> + ? ?>>> choices[bisect.bisect(cumdist, x)]
> + ? ?'Blue'
Neat example, although it would be easier to follow if you broke that
last line into two pieces:
.>>> random_index = bisect.bisect(cumdist, x)
.>>> choices[random_index]
'Blue'
It took me a moment to remember how bisect.bisect worked, but it would
have been instant if the return value was assigned to an appropriately
named variable.
Cheers,
Nick.
--
Nick Coghlan?? |?? ncoghlan at gmail.com?? |?? Brisbane, Australia
From python-checkins at python.org Thu Dec 2 04:16:23 2010
From: python-checkins at python.org (r.david.murray)
Date: Thu, 2 Dec 2010 04:16:23 +0100 (CET)
Subject: [Python-checkins] r86927 - in python/branches/release27-maint:
Lib/netrc.py Lib/test/test_netrc.py Misc/ACKS Misc/NEWS
Message-ID: <20101202031623.44F41EE9BF@mail.python.org>
Author: r.david.murray
Date: Thu Dec 2 04:16:23 2010
New Revision: 86927
Log:
Merged revisions 86925 via svnmerge from
svn+ssh://pythondev at svn.python.org/python/branches/py3k
........
r86925 | r.david.murray | 2010-12-01 21:58:07 -0500 (Wed, 01 Dec 2010) | 4 lines
#10464: fix netrc handling of lines with embedded '#" characters.
Patch by Xuanji Li.
........
Modified:
python/branches/release27-maint/ (props changed)
python/branches/release27-maint/Lib/netrc.py
python/branches/release27-maint/Lib/test/test_netrc.py
python/branches/release27-maint/Misc/ACKS
python/branches/release27-maint/Misc/NEWS
Modified: python/branches/release27-maint/Lib/netrc.py
==============================================================================
--- python/branches/release27-maint/Lib/netrc.py (original)
+++ python/branches/release27-maint/Lib/netrc.py Thu Dec 2 04:16:23 2010
@@ -34,11 +34,15 @@
def _parse(self, file, fp):
lexer = shlex.shlex(fp)
lexer.wordchars += r"""!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~"""
+ lexer.commenters = lexer.commenters.replace('#', '')
while 1:
# Look for a machine, default, or macdef top-level keyword
toplevel = tt = lexer.get_token()
if not tt:
break
+ elif tt[0] == '#':
+ fp.readline();
+ continue;
elif tt == 'machine':
entryname = lexer.get_token()
elif tt == 'default':
Modified: python/branches/release27-maint/Lib/test/test_netrc.py
==============================================================================
--- python/branches/release27-maint/Lib/test/test_netrc.py (original)
+++ python/branches/release27-maint/Lib/test/test_netrc.py Thu Dec 2 04:16:23 2010
@@ -3,7 +3,13 @@
from test import test_support
TEST_NETRC = """
+
+ #this is a comment
+#this is a comment
+# this is a comment
+
machine foo login log1 password pass1 account acct1
+machine bar login log1 password pass# account acct1
macdef macro1
line1
@@ -28,18 +34,21 @@
fp = open(temp_filename, mode)
fp.write(TEST_NETRC)
fp.close()
- self.netrc = netrc.netrc(temp_filename)
+ self.nrc = netrc.netrc(temp_filename)
def tearDown (self):
- del self.netrc
os.unlink(temp_filename)
def test_case_1(self):
- self.assertTrue(self.netrc.macros == {'macro1':['line1\n', 'line2\n'],
- 'macro2':['line3\n', 'line4\n']}
- )
- self.assertTrue(self.netrc.hosts['foo'] == ('log1', 'acct1', 'pass1'))
- self.assertTrue(self.netrc.hosts['default'] == ('log2', None, 'pass2'))
+ self.assertEqual(self.nrc.hosts['foo'], ('log1', 'acct1', 'pass1'))
+ self.assertEqual(self.nrc.hosts['default'], ('log2', None, 'pass2'))
+
+ def test_macros(self):
+ self.assertEqual(self.nrc.macros, {'macro1':['line1\n', 'line2\n'],
+ 'macro2':['line3\n', 'line4\n']})
+
+ def test_parses_passwords_with_hash_character(self):
+ self.assertEqual(self.nrc.hosts['bar'], ('log1', 'acct1', 'pass#'))
def test_main():
test_support.run_unittest(NetrcTestCase)
Modified: python/branches/release27-maint/Misc/ACKS
==============================================================================
--- python/branches/release27-maint/Misc/ACKS (original)
+++ python/branches/release27-maint/Misc/ACKS Thu Dec 2 04:16:23 2010
@@ -472,6 +472,7 @@
Christopher Tur Lesniewski-Laas
Mark Levinson
William Lewis
+Xuanji Li
Robert van Liere
Ross Light
Shawn Ligocki
Modified: python/branches/release27-maint/Misc/NEWS
==============================================================================
--- python/branches/release27-maint/Misc/NEWS (original)
+++ python/branches/release27-maint/Misc/NEWS Thu Dec 2 04:16:23 2010
@@ -13,6 +13,8 @@
Library
-------
+- Issue #10464: netrc now correctly handles lines with embedded '#' characters.
+
What's New in Python 2.7.1?
===========================
From solipsis at pitrou.net Thu Dec 2 04:49:08 2010
From: solipsis at pitrou.net (solipsis at pitrou.net)
Date: Thu, 02 Dec 2010 04:49:08 +0100
Subject: [Python-checkins] Daily py3k reference leaks (r86923): sum=0
Message-ID:
py3k results for svn r86923 (hg cset 735f74e110e8)
--------------------------------------------------
Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflogpF2Qot', '-x']
From python-checkins at python.org Thu Dec 2 05:11:46 2010
From: python-checkins at python.org (nick.coghlan)
Date: Thu, 2 Dec 2010 05:11:46 +0100 (CET)
Subject: [Python-checkins] r86928 - in python/branches/py3k:
Lib/test/test_fork1.py Misc/NEWS Python/import.c
Message-ID: <20101202041146.622C7EE994@mail.python.org>
Author: nick.coghlan
Date: Thu Dec 2 05:11:46 2010
New Revision: 86928
Log:
Issue #9573: os.fork now works when triggered as a side effect of import (the wisdom of actually relying on this remains questionable!)
Modified:
python/branches/py3k/Lib/test/test_fork1.py
python/branches/py3k/Misc/NEWS
python/branches/py3k/Python/import.c
Modified: python/branches/py3k/Lib/test/test_fork1.py
==============================================================================
--- python/branches/py3k/Lib/test/test_fork1.py (original)
+++ python/branches/py3k/Lib/test/test_fork1.py Thu Dec 2 05:11:46 2010
@@ -8,13 +8,14 @@
import time
from test.fork_wait import ForkWait
-from test.support import run_unittest, reap_children, get_attribute, import_module
+from test.support import (run_unittest, reap_children, get_attribute,
+ import_module, verbose)
+
threading = import_module('threading')
# Skip test if fork does not exist.
get_attribute(os, 'fork')
-
class ForkTest(ForkWait):
def wait_impl(self, cpid):
for i in range(10):
@@ -28,7 +29,8 @@
self.assertEqual(spid, cpid)
self.assertEqual(status, 0, "cause = %d, exit = %d" % (status&0xff, status>>8))
- def test_import_lock_fork(self):
+ def test_threaded_import_lock_fork(self):
+ """Check fork() in main thread works while a subthread is doing an import"""
import_started = threading.Event()
fake_module_name = "fake test module"
partial_module = "partial"
@@ -45,11 +47,16 @@
import_started.wait()
pid = os.fork()
try:
+ # PyOS_BeforeFork should have waited for the import to complete
+ # before forking, so the child can recreate the import lock
+ # correctly, but also won't see a partially initialised module
if not pid:
m = __import__(fake_module_name)
if m == complete_module:
os._exit(0)
else:
+ if verbose > 1:
+ print("Child encountered partial module")
os._exit(1)
else:
t.join()
@@ -63,6 +70,39 @@
except OSError:
pass
+
+ def test_nested_import_lock_fork(self):
+ """Check fork() in main thread works while the main thread is doing an import"""
+ # Issue 9573: this used to trigger RuntimeError in the child process
+ def fork_with_import_lock(level):
+ release = 0
+ in_child = False
+ try:
+ try:
+ for i in range(level):
+ imp.acquire_lock()
+ release += 1
+ pid = os.fork()
+ in_child = not pid
+ finally:
+ for i in range(release):
+ imp.release_lock()
+ except RuntimeError:
+ if in_child:
+ if verbose > 1:
+ print("RuntimeError in child")
+ os._exit(1)
+ raise
+ if in_child:
+ os._exit(0)
+ self.wait_impl(pid)
+
+ # Check this works with various levels of nested
+ # import in the main thread
+ for level in range(5):
+ fork_with_import_lock(level)
+
+
def test_main():
run_unittest(ForkTest)
reap_children()
Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS (original)
+++ python/branches/py3k/Misc/NEWS Thu Dec 2 05:11:46 2010
@@ -46,6 +46,9 @@
Library
-------
+- Issue #9573: os.fork() now works correctly when triggered as a side effect
+ of a module import
+
- Issue #10464: netrc now correctly handles lines with embedded '#' characters.
- Added itertools.accumulate().
Modified: python/branches/py3k/Python/import.c
==============================================================================
--- python/branches/py3k/Python/import.c (original)
+++ python/branches/py3k/Python/import.c Thu Dec 2 05:11:46 2010
@@ -325,8 +325,17 @@
{
if (import_lock != NULL)
import_lock = PyThread_allocate_lock();
- import_lock_thread = -1;
- import_lock_level = 0;
+ if (import_lock_level > 1) {
+ /* Forked as a side effect of import */
+ long me = PyThread_get_thread_ident();
+ PyThread_acquire_lock(import_lock, 0);
+ /* XXX: can the previous line fail? */
+ import_lock_thread = me;
+ import_lock_level--;
+ } else {
+ import_lock_thread = -1;
+ import_lock_level = 0;
+ }
}
#endif
From python-checkins at python.org Thu Dec 2 06:35:36 2010
From: python-checkins at python.org (raymond.hettinger)
Date: Thu, 2 Dec 2010 06:35:36 +0100 (CET)
Subject: [Python-checkins] r86929 -
python/branches/py3k/Doc/library/random.rst
Message-ID: <20101202053536.46D81EE989@mail.python.org>
Author: raymond.hettinger
Date: Thu Dec 2 06:35:35 2010
New Revision: 86929
Log:
Neaten-up random module docs.
Modified:
python/branches/py3k/Doc/library/random.rst
Modified: python/branches/py3k/Doc/library/random.rst
==============================================================================
--- python/branches/py3k/Doc/library/random.rst (original)
+++ python/branches/py3k/Doc/library/random.rst Thu Dec 2 06:35:35 2010
@@ -233,41 +233,18 @@
parameter.
-Alternative Generators:
+Alternative Generator:
.. class:: SystemRandom([seed])
Class that uses the :func:`os.urandom` function for generating random numbers
from sources provided by the operating system. Not available on all systems.
- Does not rely on software state and sequences are not reproducible. Accordingly,
+ Does not rely on software state, and sequences are not reproducible. Accordingly,
the :meth:`seed` method has no effect and is ignored.
The :meth:`getstate` and :meth:`setstate` methods raise
:exc:`NotImplementedError` if called.
-Examples of basic usage::
-
- >>> random.random() # Random float x, 0.0 <= x < 1.0
- 0.37444887175646646
- >>> random.uniform(1, 10) # Random float x, 1.0 <= x < 10.0
- 1.1800146073117523
- >>> random.randint(1, 10) # Integer from 1 to 10, endpoints included
- 7
- >>> random.randrange(0, 101, 2) # Even integer from 0 to 100
- 26
- >>> random.choice('abcdefghij') # Choose a random element
- 'c'
-
- >>> items = [1, 2, 3, 4, 5, 6, 7]
- >>> random.shuffle(items)
- >>> items
- [7, 3, 2, 5, 6, 4, 1]
-
- >>> random.sample([1, 2, 3, 4, 5], 3) # Choose 3 elements
- [4, 1, 5]
-
-
-
.. seealso::
M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-dimensionally
@@ -280,6 +257,7 @@
random number generator with a long period and comparatively simple update
operations.
+
Notes on Reproducibility
========================
@@ -297,11 +275,34 @@
sequence when the compatible seeder is given the same seed.
-.. _random-examples:
-
Examples and Recipes
====================
+Basic usage::
+
+ >>> random.random() # Random float x, 0.0 <= x < 1.0
+ 0.37444887175646646
+
+ >>> random.uniform(1, 10) # Random float x, 1.0 <= x < 10.0
+ 1.1800146073117523
+
+ >>> random.randrange(10) # Integer from 0 to 9
+ 7
+
+ >>> random.randrange(0, 101, 2) # Even integer from 0 to 100
+ 26
+
+ >>> random.choice('abcdefghij') # Single random element
+ 'c'
+
+ >>> items = [1, 2, 3, 4, 5, 6, 7]
+ >>> random.shuffle(items)
+ >>> items
+ [7, 3, 2, 5, 6, 4, 1]
+
+ >>> random.sample([1, 2, 3, 4, 5], 3) # Three samples without replacement
+ [4, 1, 5]
+
A common task is to make a :func:`random.choice` with weighted probababilites.
If the weights are small integer ratios, a simple technique is to build a sample
@@ -312,9 +313,9 @@
>>> random.choice(population)
'Green'
-A more general approach is to arrange the weights in a cumulative probability
-distribution with :func:`itertools.accumulate`, and then locate the random value
-with :func:`bisect.bisect`::
+A more general approach is to arrange the weights in a cumulative distribution
+with :func:`itertools.accumulate`, and then locate the random value with
+:func:`bisect.bisect`::
>>> choices, weights = zip(*weighted_choices)
>>> cumdist = list(itertools.accumulate(weights))
From python-checkins at python.org Thu Dec 2 08:05:57 2010
From: python-checkins at python.org (terry.reedy)
Date: Thu, 2 Dec 2010 08:05:57 +0100 (CET)
Subject: [Python-checkins] r86930 - in python/branches/py3k:
Doc/library/os.rst Lib/os.py Lib/test/test_os.py Misc/ACKS Misc/NEWS
Message-ID: <20101202070557.1D971EE98C@mail.python.org>
Author: terry.reedy
Date: Thu Dec 2 08:05:56 2010
New Revision: 86930
Log:
Issue 9299 Add exist_ok parameter to os.makedirs to suppress 'File exists' exception. Patch by Ray Allen.
Modified:
python/branches/py3k/Doc/library/os.rst
python/branches/py3k/Lib/os.py
python/branches/py3k/Lib/test/test_os.py
python/branches/py3k/Misc/ACKS
python/branches/py3k/Misc/NEWS
Modified: python/branches/py3k/Doc/library/os.rst
==============================================================================
--- python/branches/py3k/Doc/library/os.rst (original)
+++ python/branches/py3k/Doc/library/os.rst Thu Dec 2 08:05:56 2010
@@ -1143,17 +1143,20 @@
Availability: Unix, Windows.
-.. function:: makedirs(path[, mode])
+.. function:: makedirs(path[, mode][, exist_ok=False])
.. index::
single: directory; creating
single: UNC paths; and os.makedirs()
Recursive directory creation function. Like :func:`mkdir`, but makes all
- intermediate-level directories needed to contain the leaf directory. Raises
- an :exc:`error` exception if the leaf directory already exists or cannot be
- created. The default *mode* is ``0o777`` (octal). On some systems, *mode*
- is ignored. Where it is used, the current umask value is first masked out.
+ intermediate-level directories needed to contain the leaf directory. If
+ the target directory with the same mode as we specified already exists,
+ raises an :exc:`OSError` exception if *exist_ok* is False, otherwise no
+ exception is raised. If the directory cannot be created in other cases,
+ raises an :exc:`OSError` exception. The default *mode* is ``0o777`` (octal).
+ On some systems, *mode* is ignored. Where it is used, the current umask
+ value is first masked out.
.. note::
@@ -1162,6 +1165,9 @@
This function handles UNC paths correctly.
+ .. versionadded:: 3.2
+ The *exist_ok* parameter.
+
.. function:: pathconf(path, name)
Modified: python/branches/py3k/Lib/os.py
==============================================================================
--- python/branches/py3k/Lib/os.py (original)
+++ python/branches/py3k/Lib/os.py Thu Dec 2 08:05:56 2010
@@ -114,18 +114,26 @@
SEEK_CUR = 1
SEEK_END = 2
+
+def _get_masked_mode(mode):
+ mask = umask(0)
+ umask(mask)
+ return mode & ~mask
+
#'
# Super directory utilities.
# (Inspired by Eric Raymond; the doc strings are mostly his)
-def makedirs(name, mode=0o777):
- """makedirs(path [, mode=0o777])
+def makedirs(name, mode=0o777, exist_ok=False):
+ """makedirs(path [, 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) will be created if it does not exist. This is
- recursive.
+ just the rightmost) will be created if it does not exist. If the
+ target directory with the same mode as we specified already exists,
+ raises an OSError if exist_ok is False, otherwise no exception is
+ raised. This is recursive.
"""
head, tail = path.split(name)
@@ -133,14 +141,20 @@
head, tail = path.split(head)
if head and tail and not path.exists(head):
try:
- makedirs(head, mode)
+ makedirs(head, mode, exist_ok)
except OSError as e:
# be happy if someone already created the path
if e.errno != errno.EEXIST:
raise
if tail == curdir: # xxx/newdir/. exists if xxx/newdir exists
return
- mkdir(name, mode)
+ try:
+ mkdir(name, mode)
+ except OSError as e:
+ import stat as st
+ if not (e.errno == errno.EEXIST and exist_ok and path.isdir(name) and
+ st.S_IMODE(lstat(name).st_mode) == _get_masked_mode(mode)):
+ raise
def removedirs(name):
"""removedirs(path)
Modified: python/branches/py3k/Lib/test/test_os.py
==============================================================================
--- python/branches/py3k/Lib/test/test_os.py (original)
+++ python/branches/py3k/Lib/test/test_os.py Thu Dec 2 08:05:56 2010
@@ -630,6 +630,28 @@
'dir5', 'dir6')
os.makedirs(path)
+ def test_exist_ok_existing_directory(self):
+ path = os.path.join(support.TESTFN, 'dir1')
+ mode = 0o777
+ old_mask = os.umask(0o022)
+ os.makedirs(path, mode)
+ self.assertRaises(OSError, os.makedirs, path, mode)
+ self.assertRaises(OSError, os.makedirs, path, mode, exist_ok=False)
+ self.assertRaises(OSError, os.makedirs, path, 0o776, exist_ok=True)
+ os.makedirs(path, mode=mode, exist_ok=True)
+ os.umask(old_mask)
+
+ def test_exist_ok_existing_regular_file(self):
+ base = support.TESTFN
+ path = os.path.join(support.TESTFN, 'dir1')
+ f = open(path, 'w')
+ f.write('abc')
+ f.close()
+ self.assertRaises(OSError, os.makedirs, path)
+ self.assertRaises(OSError, os.makedirs, path, exist_ok=False)
+ self.assertRaises(OSError, os.makedirs, path, exist_ok=True)
+ os.remove(path)
+
def tearDown(self):
path = os.path.join(support.TESTFN, 'dir1', 'dir2', 'dir3',
'dir4', 'dir5', 'dir6')
Modified: python/branches/py3k/Misc/ACKS
==============================================================================
--- python/branches/py3k/Misc/ACKS (original)
+++ python/branches/py3k/Misc/ACKS Thu Dec 2 08:05:56 2010
@@ -18,6 +18,7 @@
Nir Aides
Yaniv Aknin
Jyrki Alakuijala
+Ray Allen
Billy G. Allie
Kevin Altis
Joe Amenta
Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS (original)
+++ python/branches/py3k/Misc/NEWS Thu Dec 2 08:05:56 2010
@@ -46,6 +46,10 @@
Library
-------
+- Issue #9299: Add exist_ok parameter to os.makedirs to suppress the
+ 'File exists' exception when a target directory already exists with the
+ specified mode. Patch by Ray Allen.
+
- Issue #9573: os.fork() now works correctly when triggered as a side effect
of a module import
From python-checkins at python.org Thu Dec 2 10:06:13 2010
From: python-checkins at python.org (georg.brandl)
Date: Thu, 2 Dec 2010 10:06:13 +0100 (CET)
Subject: [Python-checkins] r86931 - python/branches/py3k/Doc/library/os.rst
Message-ID: <20101202090613.093E5EE98C@mail.python.org>
Author: georg.brandl
Date: Thu Dec 2 10:06:12 2010
New Revision: 86931
Log:
Fix-up documentation of makedirs().
Modified:
python/branches/py3k/Doc/library/os.rst
Modified: python/branches/py3k/Doc/library/os.rst
==============================================================================
--- python/branches/py3k/Doc/library/os.rst (original)
+++ python/branches/py3k/Doc/library/os.rst Thu Dec 2 10:06:12 2010
@@ -1143,7 +1143,7 @@
Availability: Unix, Windows.
-.. function:: makedirs(path[, mode][, exist_ok=False])
+.. function:: makedirs(path, mode=0o777, exist_ok=False)
.. index::
single: directory; creating
@@ -1151,17 +1151,17 @@
Recursive directory creation function. Like :func:`mkdir`, but makes all
intermediate-level directories needed to contain the leaf directory. If
- the target directory with the same mode as we specified already exists,
+ the target directory with the same mode as specified already exists,
raises an :exc:`OSError` exception if *exist_ok* is False, otherwise no
exception is raised. If the directory cannot be created in other cases,
raises an :exc:`OSError` exception. The default *mode* is ``0o777`` (octal).
- On some systems, *mode* is ignored. Where it is used, the current umask
+ On some systems, *mode* is ignored. Where it is used, the current umask
value is first masked out.
.. note::
- :func:`makedirs` will become confused if the path elements to create include
- :data:`os.pardir`.
+ :func:`makedirs` will become confused if the path elements to create
+ include :data:`pardir`.
This function handles UNC paths correctly.
From ncoghlan at gmail.com Thu Dec 2 10:32:19 2010
From: ncoghlan at gmail.com (Nick Coghlan)
Date: Thu, 2 Dec 2010 19:32:19 +1000
Subject: [Python-checkins] r86930 - in python/branches/py3k:
Doc/library/os.rst Lib/os.py Lib/test/test_os.py Misc/ACKS Misc/NEWS
In-Reply-To: <20101202070557.1D971EE98C@mail.python.org>
References: <20101202070557.1D971EE98C@mail.python.org>
Message-ID:
On Thu, Dec 2, 2010 at 5:05 PM, terry.reedy wrote:
> + If
> + ? the target directory with the same mode as we specified already exists,
> + ? raises an :exc:`OSError` exception if *exist_ok* is False, otherwise no
> + ? exception is raised. ?If the directory cannot be created in other cases,
> + ? raises an :exc:`OSError` exception.
I would suggest being explicit here that "directory exists, but has a
mode other than the one requested" always triggers an exception.
Perhaps something like the following:
"Raises an :exc:`OSError` exception if the target directory already
exists, unless *exist_ok* is True and the existing directory has the
same mode as is specified in the current call. Also raises an
:exc:`OSError` exception if the directory cannot be created for any
other reason."
Cheers,
Nick.
--
Nick Coghlan?? |?? ncoghlan at gmail.com?? |?? Brisbane, Australia
From ocean-city at m2.ccsnet.ne.jp Thu Dec 2 16:50:51 2010
From: ocean-city at m2.ccsnet.ne.jp (Hirokazu Yamamoto)
Date: Fri, 03 Dec 2010 00:50:51 +0900
Subject: [Python-checkins] r86817 -
python/branches/py3k-stat-on-windows/Lib/test/test_shutil.py
In-Reply-To:
References: <20101126184428.E04A0EE984@mail.python.org> <4CF00E4E.6030507@m2.ccsnet.ne.jp> <4CF01632.8070504@m2.ccsnet.ne.jp>
Message-ID: <4CF7C05B.7030909@m2.ccsnet.ne.jp>
On 2010/11/27 5:31, Brian Curtin wrote:
> On Fri, Nov 26, 2010 at 14:18, Hirokazu Yamamoto> wrote:
>
>> On 2010/11/27 5:02, Brian Curtin wrote:
>>
>>> We briefly chatted about this on the os.link
>>> feature issue, but I never found a way around it.
>>>
>>
>> How about implementing os.path.samefile in
>> Modules/posixmodule.c like this?
>>
>> http://bugs.python.org/file19262/py3k_fix_kill_python_for_short_path.patch
>>
>> # I hope this works.
>>
>
> That's almost identical to what the current os.path.sameopenfile is.
>
> Lib/ntpath.py opens both files, then compares them via _getfileinformation.
> That function is implemented to take in a file descriptor, call
> GetFileInformationByHandle with it, then returns a tuple
> of dwVolumeSerialNumber, nFileIndexHigh, and nFileIndexLow.
>
Yes. Difference is, file object cannot represent directory,
and probably FILE_FLAG_BACKUP_SEMANTICS makes it faster to open file.
From python-checkins at python.org Thu Dec 2 17:41:00 2010
From: python-checkins at python.org (david.malcolm)
Date: Thu, 2 Dec 2010 17:41:00 +0100 (CET)
Subject: [Python-checkins] r86932 - python/branches/py3k/Lib/urllib/parse.py
Message-ID: <20101202164100.567B4EE9EA@mail.python.org>
Author: david.malcolm
Date: Thu Dec 2 17:41:00 2010
New Revision: 86932
Log:
Fix spelling of Jamie Zawinski's surname in urllib.parse docstring (issue 10606)
Modified:
python/branches/py3k/Lib/urllib/parse.py
Modified: python/branches/py3k/Lib/urllib/parse.py
==============================================================================
--- python/branches/py3k/Lib/urllib/parse.py (original)
+++ python/branches/py3k/Lib/urllib/parse.py Thu Dec 2 17:41:00 2010
@@ -11,7 +11,7 @@
RFC 2396: "Uniform Resource Identifiers (URI)": Generic Syntax by T.
Berners-Lee, R. Fielding, and L. Masinter, August 1998.
-RFC 2368: "The mailto URL scheme", by P.Hoffman , L Masinter, J. Zwinski, July 1998.
+RFC 2368: "The mailto URL scheme", by P.Hoffman , L Masinter, J. Zawinski, July 1998.
RFC 1808: "Relative Uniform Resource Locators", by R. Fielding, UC Irvine, June
1995.
From python-checkins at python.org Thu Dec 2 19:02:02 2010
From: python-checkins at python.org (georg.brandl)
Date: Thu, 2 Dec 2010 19:02:02 +0100 (CET)
Subject: [Python-checkins] r86933 - python/branches/py3k/Doc/c-api/init.rst
Message-ID: <20101202180202.4C1ADEE9AA@mail.python.org>
Author: georg.brandl
Date: Thu Dec 2 19:02:01 2010
New Revision: 86933
Log:
#10597: fix Py_SetPythonHome docs by pointing to where the meaning of PYTHONHOME is already documented.
Modified:
python/branches/py3k/Doc/c-api/init.rst
Modified: python/branches/py3k/Doc/c-api/init.rst
==============================================================================
--- python/branches/py3k/Doc/c-api/init.rst (original)
+++ python/branches/py3k/Doc/c-api/init.rst Thu Dec 2 19:02:01 2010
@@ -412,8 +412,9 @@
.. c:function:: void Py_SetPythonHome(wchar_t *home)
Set the default "home" directory, that is, the location of the standard
- Python libraries. The libraries are searched in
- :file:`{home}/lib/python{version}` and :file:`{home}/lib/python{version}`.
+ Python libraries. See :envvar:`PYTHONHOME` for the meaning of the
+ argument string.
+
The argument should point to a zero-terminated character string in static
storage whose contents will not change for the duration of the program's
execution. No code in the Python interpreter will change the contents of
From python-checkins at python.org Thu Dec 2 19:06:51 2010
From: python-checkins at python.org (georg.brandl)
Date: Thu, 2 Dec 2010 19:06:51 +0100 (CET)
Subject: [Python-checkins] r86934 - in python/branches/py3k:
Doc/library/codecs.rst Doc/library/stdtypes.rst Lib/codecs.py
Lib/encodings/aliases.py Lib/encodings/base64_codec.py
Lib/encodings/bz2_codec.py Lib/encodings/hex_codec.py
Lib/encodings/quopri_codec.py Lib/encodings/rot_13.py
Lib/encodings/uu_codec.py Lib/encodings/zlib_codec.py
Lib/test/test_bytes.py Lib/test/test_codecs.py Misc/NEWS
Objects/bytearrayobject.c Objects/bytesobject.c
Objects/unicodeobject.c
Message-ID: <20101202180651.F2AB0EE9CD@mail.python.org>
Author: georg.brandl
Date: Thu Dec 2 19:06:51 2010
New Revision: 86934
Log:
#7475: add (un)transform method to bytes/bytearray and str, add back codecs that can be used with them from Python 2.
Added:
python/branches/py3k/Lib/encodings/base64_codec.py (contents, props changed)
python/branches/py3k/Lib/encodings/bz2_codec.py (contents, props changed)
python/branches/py3k/Lib/encodings/hex_codec.py (contents, props changed)
python/branches/py3k/Lib/encodings/quopri_codec.py (contents, props changed)
python/branches/py3k/Lib/encodings/rot_13.py (contents, props changed)
python/branches/py3k/Lib/encodings/uu_codec.py (contents, props changed)
python/branches/py3k/Lib/encodings/zlib_codec.py (contents, props changed)
Modified:
python/branches/py3k/Doc/library/codecs.rst
python/branches/py3k/Doc/library/stdtypes.rst
python/branches/py3k/Lib/codecs.py
python/branches/py3k/Lib/encodings/aliases.py
python/branches/py3k/Lib/test/test_bytes.py
python/branches/py3k/Lib/test/test_codecs.py
python/branches/py3k/Misc/NEWS
python/branches/py3k/Objects/bytearrayobject.c
python/branches/py3k/Objects/bytesobject.c
python/branches/py3k/Objects/unicodeobject.c
Modified: python/branches/py3k/Doc/library/codecs.rst
==============================================================================
--- python/branches/py3k/Doc/library/codecs.rst (original)
+++ python/branches/py3k/Doc/library/codecs.rst Thu Dec 2 19:06:51 2010
@@ -1165,6 +1165,46 @@
| | | operand |
+--------------------+---------+---------------------------+
+The following codecs provide bytes-to-bytes mappings. They can be used with
+:meth:`bytes.transform` and :meth:`bytes.untransform`.
+
++--------------------+---------------------------+---------------------------+
+| Codec | Aliases | Purpose |
++====================+===========================+===========================+
+| base64_codec | base64, base-64 | Convert operand to MIME |
+| | | base64 |
++--------------------+---------------------------+---------------------------+
+| bz2_codec | bz2 | Compress the operand |
+| | | using bz2 |
++--------------------+---------------------------+---------------------------+
+| hex_codec | hex | Convert operand to |
+| | | hexadecimal |
+| | | representation, with two |
+| | | digits per byte |
++--------------------+---------------------------+---------------------------+
+| quopri_codec | quopri, quoted-printable, | Convert operand to MIME |
+| | quotedprintable | quoted printable |
++--------------------+---------------------------+---------------------------+
+| uu_codec | uu | Convert the operand using |
+| | | uuencode |
++--------------------+---------------------------+---------------------------+
+| zlib_codec | zip, zlib | Compress the operand |
+| | | using gzip |
++--------------------+---------------------------+---------------------------+
+
+The following codecs provide string-to-string mappings. They can be used with
+:meth:`str.transform` and :meth:`str.untransform`.
+
++--------------------+---------------------------+---------------------------+
+| Codec | Aliases | Purpose |
++====================+===========================+===========================+
+| rot_13 | rot13 | Returns the Caesar-cypher |
+| | | encryption of the operand |
++--------------------+---------------------------+---------------------------+
+
+.. versionadded:: 3.2
+ bytes-to-bytes and string-to-string codecs.
+
:mod:`encodings.idna` --- Internationalized Domain Names in Applications
------------------------------------------------------------------------
Modified: python/branches/py3k/Doc/library/stdtypes.rst
==============================================================================
--- python/branches/py3k/Doc/library/stdtypes.rst (original)
+++ python/branches/py3k/Doc/library/stdtypes.rst Thu Dec 2 19:06:51 2010
@@ -1352,6 +1352,19 @@
"They're Bill's Friends."
+.. method:: str.transform(encoding, errors='strict')
+
+ Return an encoded version of the string. In contrast to :meth:`encode`, this
+ method works with codecs that provide string-to-string mappings, and not
+ string-to-bytes mappings. :meth:`transform` therefore returns a string
+ object.
+
+ The codecs that can be used with this method are listed in
+ :ref:`standard-encodings`.
+
+ .. versionadded:: 3.2
+
+
.. method:: str.translate(map)
Return a copy of the *s* where all characters have been mapped through the
@@ -1369,6 +1382,14 @@
example).
+.. method:: str.untransform(encoding, errors='strict')
+
+ Return a decoded version of the string. This provides the reverse operation
+ of :meth:`transform`.
+
+ .. versionadded:: 3.2
+
+
.. method:: str.upper()
Return a copy of the string converted to uppercase.
@@ -1800,6 +1821,20 @@
The maketrans and translate methods differ in semantics from the versions
available on strings:
+.. method:: bytes.transform(encoding, errors='strict')
+ bytearray.transform(encoding, errors='strict')
+
+ Return an encoded version of the bytes object. In contrast to
+ :meth:`encode`, this method works with codecs that provide bytes-to-bytes
+ mappings, and not string-to-bytes mappings. :meth:`transform` therefore
+ returns a bytes or bytearray object.
+
+ The codecs that can be used with this method are listed in
+ :ref:`standard-encodings`.
+
+ .. versionadded:: 3.2
+
+
.. method:: bytes.translate(table[, delete])
bytearray.translate(table[, delete])
@@ -1817,6 +1852,15 @@
b'rd ths shrt txt'
+.. method:: bytes.untransform(encoding, errors='strict')
+ bytearray.untransform(encoding, errors='strict')
+
+ Return an decoded version of the bytes object. This provides the reverse
+ operation of :meth:`transform`.
+
+ .. versionadded:: 3.2
+
+
.. staticmethod:: bytes.maketrans(from, to)
bytearray.maketrans(from, to)
Modified: python/branches/py3k/Lib/codecs.py
==============================================================================
--- python/branches/py3k/Lib/codecs.py (original)
+++ python/branches/py3k/Lib/codecs.py Thu Dec 2 19:06:51 2010
@@ -396,6 +396,8 @@
class StreamReader(Codec):
+ charbuffertype = str
+
def __init__(self, stream, errors='strict'):
""" Creates a StreamReader instance.
@@ -417,9 +419,8 @@
self.stream = stream
self.errors = errors
self.bytebuffer = b""
- # For str->str decoding this will stay a str
- # For str->unicode decoding the first read will promote it to unicode
- self.charbuffer = ""
+ self._empty_charbuffer = self.charbuffertype()
+ self.charbuffer = self._empty_charbuffer
self.linebuffer = None
def decode(self, input, errors='strict'):
@@ -455,7 +456,7 @@
"""
# If we have lines cached, first merge them back into characters
if self.linebuffer:
- self.charbuffer = "".join(self.linebuffer)
+ self.charbuffer = self._empty_charbuffer.join(self.linebuffer)
self.linebuffer = None
# read until we get the required number of characters (if available)
@@ -498,7 +499,7 @@
if chars < 0:
# Return everything we've got
result = self.charbuffer
- self.charbuffer = ""
+ self.charbuffer = self._empty_charbuffer
else:
# Return the first chars characters
result = self.charbuffer[:chars]
@@ -529,7 +530,7 @@
return line
readsize = size or 72
- line = ""
+ line = self._empty_charbuffer
# If size is given, we call read() only once
while True:
data = self.read(readsize, firstline=True)
@@ -537,7 +538,8 @@
# If we're at a "\r" read one extra character (which might
# be a "\n") to get a proper line ending. If the stream is
# temporarily exhausted we return the wrong line ending.
- if data.endswith("\r"):
+ if (isinstance(data, str) and data.endswith("\r")) or \
+ (isinstance(data, bytes) and data.endswith(b"\r")):
data += self.read(size=1, chars=1)
line += data
@@ -563,7 +565,8 @@
line0withoutend = lines[0].splitlines(False)[0]
if line0withend != line0withoutend: # We really have a line end
# Put the rest back together and keep it until the next call
- self.charbuffer = "".join(lines[1:]) + self.charbuffer
+ self.charbuffer = self._empty_charbuffer.join(lines[1:]) + \
+ self.charbuffer
if keepends:
line = line0withend
else:
@@ -574,7 +577,7 @@
if line and not keepends:
line = line.splitlines(False)[0]
break
- if readsize<8000:
+ if readsize < 8000:
readsize *= 2
return line
@@ -603,7 +606,7 @@
"""
self.bytebuffer = b""
- self.charbuffer = ""
+ self.charbuffer = self._empty_charbuffer
self.linebuffer = None
def seek(self, offset, whence=0):
Modified: python/branches/py3k/Lib/encodings/aliases.py
==============================================================================
--- python/branches/py3k/Lib/encodings/aliases.py (original)
+++ python/branches/py3k/Lib/encodings/aliases.py Thu Dec 2 19:06:51 2010
@@ -33,9 +33,9 @@
'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 @@
'big5_hkscs' : 'big5hkscs',
'hkscs' : 'big5hkscs',
- ## bz2_codec codec
- #'bz2' : 'bz2_codec',
+ # bz2_codec codec
+ 'bz2' : 'bz2_codec',
# cp037 codec
'037' : 'cp037',
@@ -248,8 +248,8 @@
'cp936' : 'gbk',
'ms936' : 'gbk',
- ## hex_codec codec
- #'hex' : 'hex_codec',
+ # hex_codec codec
+ 'hex' : 'hex_codec',
# hp_roman8 codec
'roman8' : 'hp_roman8',
@@ -450,13 +450,13 @@
'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 +518,12 @@
'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',
Added: python/branches/py3k/Lib/encodings/base64_codec.py
==============================================================================
--- (empty file)
+++ python/branches/py3k/Lib/encodings/base64_codec.py Thu Dec 2 19:06:51 2010
@@ -0,0 +1,55 @@
+"""Python 'base64_codec' Codec - base64 content transfer encoding.
+
+This codec de/encodes from bytes to bytes and is therefore usable with
+bytes.transform() and bytes.untransform().
+
+Written by Marc-Andre Lemburg (mal at lemburg.com).
+"""
+
+import codecs
+import base64
+
+### Codec APIs
+
+def base64_encode(input, errors='strict'):
+ assert errors == 'strict'
+ return (base64.encodestring(input), len(input))
+
+def base64_decode(input, errors='strict'):
+ assert errors == 'strict'
+ return (base64.decodestring(input), len(input))
+
+class Codec(codecs.Codec):
+ def encode(self, input, errors='strict'):
+ return base64_encode(input, errors)
+ def decode(self, input, errors='strict'):
+ return base64_decode(input, errors)
+
+class IncrementalEncoder(codecs.IncrementalEncoder):
+ def encode(self, input, final=False):
+ assert self.errors == 'strict'
+ return base64.encodestring(input)
+
+class IncrementalDecoder(codecs.IncrementalDecoder):
+ def decode(self, input, final=False):
+ assert self.errors == 'strict'
+ return base64.decodestring(input)
+
+class StreamWriter(Codec, codecs.StreamWriter):
+ charbuffertype = bytes
+
+class StreamReader(Codec, codecs.StreamReader):
+ charbuffertype = bytes
+
+### encodings module API
+
+def getregentry():
+ return codecs.CodecInfo(
+ name='base64',
+ encode=base64_encode,
+ decode=base64_decode,
+ incrementalencoder=IncrementalEncoder,
+ incrementaldecoder=IncrementalDecoder,
+ streamwriter=StreamWriter,
+ streamreader=StreamReader,
+ )
Added: python/branches/py3k/Lib/encodings/bz2_codec.py
==============================================================================
--- (empty file)
+++ python/branches/py3k/Lib/encodings/bz2_codec.py Thu Dec 2 19:06:51 2010
@@ -0,0 +1,77 @@
+"""Python 'bz2_codec' Codec - bz2 compression encoding.
+
+This codec de/encodes from bytes to bytes and is therefore usable with
+bytes.transform() and bytes.untransform().
+
+Adapted by Raymond Hettinger from zlib_codec.py which was written
+by Marc-Andre Lemburg (mal at lemburg.com).
+"""
+
+import codecs
+import bz2 # this codec needs the optional bz2 module !
+
+### Codec APIs
+
+def bz2_encode(input, errors='strict'):
+ assert errors == 'strict'
+ return (bz2.compress(input), len(input))
+
+def bz2_decode(input, errors='strict'):
+ assert errors == 'strict'
+ return (bz2.decompress(input), len(input))
+
+class Codec(codecs.Codec):
+ def encode(self, input, errors='strict'):
+ return bz2_encode(input, errors)
+ def decode(self, input, errors='strict'):
+ return bz2_decode(input, errors)
+
+class IncrementalEncoder(codecs.IncrementalEncoder):
+ def __init__(self, errors='strict'):
+ assert errors == 'strict'
+ self.errors = errors
+ self.compressobj = bz2.BZ2Compressor()
+
+ def encode(self, input, final=False):
+ if final:
+ c = self.compressobj.compress(input)
+ return c + self.compressobj.flush()
+ else:
+ return self.compressobj.compress(input)
+
+ def reset(self):
+ self.compressobj = bz2.BZ2Compressor()
+
+class IncrementalDecoder(codecs.IncrementalDecoder):
+ def __init__(self, errors='strict'):
+ assert errors == 'strict'
+ self.errors = errors
+ self.decompressobj = bz2.BZ2Decompressor()
+
+ def decode(self, input, final=False):
+ try:
+ return self.decompressobj.decompress(input)
+ except EOFError:
+ return ''
+
+ def reset(self):
+ self.decompressobj = bz2.BZ2Decompressor()
+
+class StreamWriter(Codec, codecs.StreamWriter):
+ charbuffertype = bytes
+
+class StreamReader(Codec, codecs.StreamReader):
+ charbuffertype = bytes
+
+### encodings module API
+
+def getregentry():
+ return codecs.CodecInfo(
+ name="bz2",
+ encode=bz2_encode,
+ decode=bz2_decode,
+ incrementalencoder=IncrementalEncoder,
+ incrementaldecoder=IncrementalDecoder,
+ streamwriter=StreamWriter,
+ streamreader=StreamReader,
+ )
Added: python/branches/py3k/Lib/encodings/hex_codec.py
==============================================================================
--- (empty file)
+++ python/branches/py3k/Lib/encodings/hex_codec.py Thu Dec 2 19:06:51 2010
@@ -0,0 +1,55 @@
+"""Python 'hex_codec' Codec - 2-digit hex content transfer encoding.
+
+This codec de/encodes from bytes to bytes and is therefore usable with
+bytes.transform() and bytes.untransform().
+
+Written by Marc-Andre Lemburg (mal at lemburg.com).
+"""
+
+import codecs
+import binascii
+
+### Codec APIs
+
+def hex_encode(input, errors='strict'):
+ assert errors == 'strict'
+ return (binascii.b2a_hex(input), len(input))
+
+def hex_decode(input, errors='strict'):
+ assert errors == 'strict'
+ return (binascii.a2b_hex(input), len(input))
+
+class Codec(codecs.Codec):
+ def encode(self, input, errors='strict'):
+ return hex_encode(input, errors)
+ def decode(self, input, errors='strict'):
+ return hex_decode(input, errors)
+
+class IncrementalEncoder(codecs.IncrementalEncoder):
+ def encode(self, input, final=False):
+ assert self.errors == 'strict'
+ return binascii.b2a_hex(input)
+
+class IncrementalDecoder(codecs.IncrementalDecoder):
+ def decode(self, input, final=False):
+ assert self.errors == 'strict'
+ return binascii.a2b_hex(input)
+
+class StreamWriter(Codec, codecs.StreamWriter):
+ charbuffertype = bytes
+
+class StreamReader(Codec, codecs.StreamReader):
+ charbuffertype = bytes
+
+### encodings module API
+
+def getregentry():
+ return codecs.CodecInfo(
+ name='hex',
+ encode=hex_encode,
+ decode=hex_decode,
+ incrementalencoder=IncrementalEncoder,
+ incrementaldecoder=IncrementalDecoder,
+ streamwriter=StreamWriter,
+ streamreader=StreamReader,
+ )
Added: python/branches/py3k/Lib/encodings/quopri_codec.py
==============================================================================
--- (empty file)
+++ python/branches/py3k/Lib/encodings/quopri_codec.py Thu Dec 2 19:06:51 2010
@@ -0,0 +1,56 @@
+"""Codec for quoted-printable encoding.
+
+This codec de/encodes from bytes to bytes and is therefore usable with
+bytes.transform() and bytes.untransform().
+"""
+
+import codecs
+import quopri
+from io import BytesIO
+
+def quopri_encode(input, errors='strict'):
+ assert errors == 'strict'
+ f = BytesIO(input)
+ g = BytesIO()
+ quopri.encode(f, g, 1)
+ return (g.getvalue(), len(input))
+
+def quopri_decode(input, errors='strict'):
+ assert errors == 'strict'
+ f = BytesIO(input)
+ g = BytesIO()
+ quopri.decode(f, g)
+ return (g.getvalue(), len(input))
+
+class Codec(codecs.Codec):
+ def encode(self, input, errors='strict'):
+ return quopri_encode(input, errors)
+ def decode(self, input, errors='strict'):
+ return quopri_decode(input, errors)
+
+class IncrementalEncoder(codecs.IncrementalEncoder):
+ def encode(self, input, final=False):
+ return quopri_encode(input, self.errors)[0]
+
+class IncrementalDecoder(codecs.IncrementalDecoder):
+ def decode(self, input, final=False):
+ return quopri_decode(input, self.errors)[0]
+
+class StreamWriter(Codec, codecs.StreamWriter):
+ charbuffertype = bytes
+
+class StreamReader(Codec, codecs.StreamReader):
+ charbuffertype = bytes
+
+# encodings module API
+
+def getregentry():
+ return codecs.CodecInfo(
+ name='quopri',
+ encode=quopri_encode,
+ decode=quopri_decode,
+ incrementalencoder=IncrementalEncoder,
+ incrementaldecoder=IncrementalDecoder,
+ streamwriter=StreamWriter,
+ streamreader=StreamReader,
+ )
Added: python/branches/py3k/Lib/encodings/rot_13.py
==============================================================================
--- (empty file)
+++ python/branches/py3k/Lib/encodings/rot_13.py Thu Dec 2 19:06:51 2010
@@ -0,0 +1,113 @@
+#!/usr/bin/env python
+""" Python Character Mapping Codec for ROT13.
+
+This codec de/encodes from str to str and is therefore usable with
+str.transform() and str.untransform().
+
+Written by Marc-Andre Lemburg (mal at lemburg.com).
+"""
+
+import codecs
+
+### Codec APIs
+
+class Codec(codecs.Codec):
+ def encode(self, input, errors='strict'):
+ return (input.translate(rot13_map), len(input))
+
+ def decode(self, input, errors='strict'):
+ return (input.translate(rot13_map), len(input))
+
+class IncrementalEncoder(codecs.IncrementalEncoder):
+ def encode(self, input, final=False):
+ return input.translate(rot13_map)
+
+class IncrementalDecoder(codecs.IncrementalDecoder):
+ def decode(self, input, final=False):
+ return input.translate(rot13_map)
+
+class StreamWriter(Codec,codecs.StreamWriter):
+ pass
+
+class StreamReader(Codec,codecs.StreamReader):
+ pass
+
+### encodings module API
+
+def getregentry():
+ return codecs.CodecInfo(
+ name='rot-13',
+ encode=Codec().encode,
+ decode=Codec().decode,
+ incrementalencoder=IncrementalEncoder,
+ incrementaldecoder=IncrementalDecoder,
+ streamwriter=StreamWriter,
+ streamreader=StreamReader,
+ )
+
+### Map
+
+rot13_map = codecs.make_identity_dict(range(256))
+rot13_map.update({
+ 0x0041: 0x004e,
+ 0x0042: 0x004f,
+ 0x0043: 0x0050,
+ 0x0044: 0x0051,
+ 0x0045: 0x0052,
+ 0x0046: 0x0053,
+ 0x0047: 0x0054,
+ 0x0048: 0x0055,
+ 0x0049: 0x0056,
+ 0x004a: 0x0057,
+ 0x004b: 0x0058,
+ 0x004c: 0x0059,
+ 0x004d: 0x005a,
+ 0x004e: 0x0041,
+ 0x004f: 0x0042,
+ 0x0050: 0x0043,
+ 0x0051: 0x0044,
+ 0x0052: 0x0045,
+ 0x0053: 0x0046,
+ 0x0054: 0x0047,
+ 0x0055: 0x0048,
+ 0x0056: 0x0049,
+ 0x0057: 0x004a,
+ 0x0058: 0x004b,
+ 0x0059: 0x004c,
+ 0x005a: 0x004d,
+ 0x0061: 0x006e,
+ 0x0062: 0x006f,
+ 0x0063: 0x0070,
+ 0x0064: 0x0071,
+ 0x0065: 0x0072,
+ 0x0066: 0x0073,
+ 0x0067: 0x0074,
+ 0x0068: 0x0075,
+ 0x0069: 0x0076,
+ 0x006a: 0x0077,
+ 0x006b: 0x0078,
+ 0x006c: 0x0079,
+ 0x006d: 0x007a,
+ 0x006e: 0x0061,
+ 0x006f: 0x0062,
+ 0x0070: 0x0063,
+ 0x0071: 0x0064,
+ 0x0072: 0x0065,
+ 0x0073: 0x0066,
+ 0x0074: 0x0067,
+ 0x0075: 0x0068,
+ 0x0076: 0x0069,
+ 0x0077: 0x006a,
+ 0x0078: 0x006b,
+ 0x0079: 0x006c,
+ 0x007a: 0x006d,
+})
+
+### Filter API
+
+def rot13(infile, outfile):
+ outfile.write(infile.read().encode('rot-13'))
+
+if __name__ == '__main__':
+ import sys
+ rot13(sys.stdin, sys.stdout)
Added: python/branches/py3k/Lib/encodings/uu_codec.py
==============================================================================
--- (empty file)
+++ python/branches/py3k/Lib/encodings/uu_codec.py Thu Dec 2 19:06:51 2010
@@ -0,0 +1,99 @@
+"""Python 'uu_codec' Codec - UU content transfer encoding.
+
+This codec de/encodes from bytes to bytes and is therefore usable with
+bytes.transform() and bytes.untransform().
+
+Written by Marc-Andre Lemburg (mal at lemburg.com). Some details were
+adapted from uu.py which was written by Lance Ellinghouse and
+modified by Jack Jansen and Fredrik Lundh.
+"""
+
+import codecs
+import binascii
+from io import BytesIO
+
+### Codec APIs
+
+def uu_encode(input, errors='strict', filename='', mode=0o666):
+ assert errors == 'strict'
+ infile = BytesIO(input)
+ outfile = BytesIO()
+ read = infile.read
+ write = outfile.write
+
+ # Encode
+ write(('begin %o %s\n' % (mode & 0o777, filename)).encode('ascii'))
+ chunk = read(45)
+ while chunk:
+ write(binascii.b2a_uu(chunk))
+ chunk = read(45)
+ write(b' \nend\n')
+
+ return (outfile.getvalue(), len(input))
+
+def uu_decode(input, errors='strict'):
+ assert errors == 'strict'
+ infile = BytesIO(input)
+ outfile = BytesIO()
+ readline = infile.readline
+ write = outfile.write
+
+ # Find start of encoded data
+ while 1:
+ s = readline()
+ if not s:
+ raise ValueError('Missing "begin" line in input data')
+ if s[:5] == b'begin':
+ break
+
+ # Decode
+ while True:
+ s = readline()
+ if not s or s == b'end\n':
+ break
+ try:
+ data = binascii.a2b_uu(s)
+ except binascii.Error as v:
+ # Workaround for broken uuencoders by /Fredrik Lundh
+ nbytes = (((ord(s[0])-32) & 63) * 4 + 5) / 3
+ data = binascii.a2b_uu(s[:nbytes])
+ #sys.stderr.write("Warning: %s\n" % str(v))
+ write(data)
+ if not s:
+ raise ValueError('Truncated input data')
+
+ return (outfile.getvalue(), len(input))
+
+class Codec(codecs.Codec):
+ def encode(self, input, errors='strict'):
+ return uu_encode(input, errors)
+
+ def decode(self, input, errors='strict'):
+ return uu_decode(input, errors)
+
+class IncrementalEncoder(codecs.IncrementalEncoder):
+ def encode(self, input, final=False):
+ return uu_encode(input, self.errors)[0]
+
+class IncrementalDecoder(codecs.IncrementalDecoder):
+ def decode(self, input, final=False):
+ return uu_decode(input, self.errors)[0]
+
+class StreamWriter(Codec, codecs.StreamWriter):
+ charbuffertype = bytes
+
+class StreamReader(Codec, codecs.StreamReader):
+ charbuffertype = bytes
+
+### encodings module API
+
+def getregentry():
+ return codecs.CodecInfo(
+ name='uu',
+ encode=uu_encode,
+ decode=uu_decode,
+ incrementalencoder=IncrementalEncoder,
+ incrementaldecoder=IncrementalDecoder,
+ streamreader=StreamReader,
+ streamwriter=StreamWriter,
+ )
Added: python/branches/py3k/Lib/encodings/zlib_codec.py
==============================================================================
--- (empty file)
+++ python/branches/py3k/Lib/encodings/zlib_codec.py Thu Dec 2 19:06:51 2010
@@ -0,0 +1,77 @@
+"""Python 'zlib_codec' Codec - zlib compression encoding.
+
+This codec de/encodes from bytes to bytes and is therefore usable with
+bytes.transform() and bytes.untransform().
+
+Written by Marc-Andre Lemburg (mal at lemburg.com).
+"""
+
+import codecs
+import zlib # this codec needs the optional zlib module !
+
+### Codec APIs
+
+def zlib_encode(input, errors='strict'):
+ assert errors == 'strict'
+ return (zlib.compress(input), len(input))
+
+def zlib_decode(input, errors='strict'):
+ assert errors == 'strict'
+ return (zlib.decompress(input), len(input))
+
+class Codec(codecs.Codec):
+ def encode(self, input, errors='strict'):
+ return zlib_encode(input, errors)
+ def decode(self, input, errors='strict'):
+ return zlib_decode(input, errors)
+
+class IncrementalEncoder(codecs.IncrementalEncoder):
+ def __init__(self, errors='strict'):
+ assert errors == 'strict'
+ self.errors = errors
+ self.compressobj = zlib.compressobj()
+
+ def encode(self, input, final=False):
+ if final:
+ c = self.compressobj.compress(input)
+ return c + self.compressobj.flush()
+ else:
+ return self.compressobj.compress(input)
+
+ def reset(self):
+ self.compressobj = zlib.compressobj()
+
+class IncrementalDecoder(codecs.IncrementalDecoder):
+ def __init__(self, errors='strict'):
+ assert errors == 'strict'
+ self.errors = errors
+ self.decompressobj = zlib.decompressobj()
+
+ def decode(self, input, final=False):
+ if final:
+ c = self.decompressobj.decompress(input)
+ return c + self.decompressobj.flush()
+ else:
+ return self.decompressobj.decompress(input)
+
+ def reset(self):
+ self.decompressobj = zlib.decompressobj()
+
+class StreamWriter(Codec, codecs.StreamWriter):
+ charbuffertype = bytes
+
+class StreamReader(Codec, codecs.StreamReader):
+ charbuffertype = bytes
+
+### encodings module API
+
+def getregentry():
+ return codecs.CodecInfo(
+ name='zlib',
+ encode=zlib_encode,
+ decode=zlib_decode,
+ incrementalencoder=IncrementalEncoder,
+ incrementaldecoder=IncrementalDecoder,
+ streamreader=StreamReader,
+ streamwriter=StreamWriter,
+ )
Modified: python/branches/py3k/Lib/test/test_bytes.py
==============================================================================
--- python/branches/py3k/Lib/test/test_bytes.py (original)
+++ python/branches/py3k/Lib/test/test_bytes.py Thu Dec 2 19:06:51 2010
@@ -207,6 +207,11 @@
self.assertEqual(b.decode(errors="ignore", encoding="utf8"),
"Hello world\n")
+ def test_transform(self):
+ b1 = self.type2test(range(256))
+ b2 = b1.transform("base64").untransform("base64")
+ self.assertEqual(b2, b1)
+
def test_from_int(self):
b = self.type2test(0)
self.assertEqual(b, self.type2test())
Modified: python/branches/py3k/Lib/test/test_codecs.py
==============================================================================
--- python/branches/py3k/Lib/test/test_codecs.py (original)
+++ python/branches/py3k/Lib/test/test_codecs.py Thu Dec 2 19:06:51 2010
@@ -1659,6 +1659,67 @@
self.assertEqual(f.read(), data * 2)
+bytes_transform_encodings = [
+ "base64_codec",
+ "uu_codec",
+ "quopri_codec",
+ "hex_codec",
+]
+try:
+ import zlib
+except ImportError:
+ pass
+else:
+ bytes_transform_encodings.append("zlib_codec")
+try:
+ import bz2
+except ImportError:
+ pass
+else:
+ bytes_transform_encodings.append("bz2_codec")
+
+class TransformCodecTest(unittest.TestCase):
+ def test_basics(self):
+ binput = bytes(range(256))
+ ainput = bytearray(binput)
+ for encoding in bytes_transform_encodings:
+ # generic codecs interface
+ (o, size) = codecs.getencoder(encoding)(binput)
+ self.assertEqual(size, len(binput))
+ (i, size) = codecs.getdecoder(encoding)(o)
+ self.assertEqual(size, len(o))
+ self.assertEqual(i, binput)
+
+ # transform interface
+ boutput = binput.transform(encoding)
+ aoutput = ainput.transform(encoding)
+ self.assertEqual(boutput, aoutput)
+ self.assertIsInstance(boutput, bytes)
+ self.assertIsInstance(aoutput, bytearray)
+ bback = boutput.untransform(encoding)
+ aback = aoutput.untransform(encoding)
+ self.assertEqual(bback, aback)
+ self.assertEqual(bback, binput)
+ self.assertIsInstance(bback, bytes)
+ self.assertIsInstance(aback, bytearray)
+
+ def test_read(self):
+ for encoding in bytes_transform_encodings:
+ sin = b"\x80".transform(encoding)
+ reader = codecs.getreader(encoding)(io.BytesIO(sin))
+ sout = reader.read()
+ self.assertEqual(sout, b"\x80")
+
+ def test_readline(self):
+ for encoding in bytes_transform_encodings:
+ if encoding in ['uu_codec', 'zlib_codec']:
+ continue
+ sin = b"\x80".transform(encoding)
+ reader = codecs.getreader(encoding)(io.BytesIO(sin))
+ sout = reader.readline()
+ self.assertEqual(sout, b"\x80")
+
+
def test_main():
support.run_unittest(
UTF32Test,
@@ -1686,6 +1747,7 @@
TypesTest,
SurrogateEscapeTest,
BomTest,
+ TransformCodecTest,
)
Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS (original)
+++ python/branches/py3k/Misc/NEWS Thu Dec 2 19:06:51 2010
@@ -10,6 +10,10 @@
Core and Builtins
-----------------
+- Issue #7475: Added transform() and untransform() methods to both bytes
+ and string types. They can be used to access those codecs providing
+ bytes-to-bytes and string-to-string mappings.
+
- Issue #8685: Speed up set difference ``a - b`` when source set ``a`` is
much larger than operand ``b``. Patch by Andrew Bennetts.
Modified: python/branches/py3k/Objects/bytearrayobject.c
==============================================================================
--- python/branches/py3k/Objects/bytearrayobject.c (original)
+++ python/branches/py3k/Objects/bytearrayobject.c Thu Dec 2 19:06:51 2010
@@ -2488,6 +2488,75 @@
return PyUnicode_FromEncodedObject(self, encoding, errors);
}
+PyDoc_STRVAR(transform__doc__,
+"B.transform(encoding, errors='strict') -> bytearray\n\
+\n\
+Transform B using the codec registered for encoding. errors may be given\n\
+to set a different error handling scheme.");
+
+static PyObject *
+bytearray_transform(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ const char *encoding = NULL;
+ const char *errors = NULL;
+ static char *kwlist[] = {"encoding", "errors", 0};
+ PyObject *v, *w;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|s:transform",
+ kwlist, &encoding, &errors))
+ return NULL;
+
+ v = PyCodec_Encode(self, encoding, errors);
+ if (v == NULL)
+ return NULL;
+ if (!PyBytes_Check(v)) {
+ PyErr_Format(PyExc_TypeError,
+ "encoder did not return a bytes object (type=%.400s)",
+ Py_TYPE(v)->tp_name);
+ Py_DECREF(v);
+ return NULL;
+ }
+ w = PyByteArray_FromStringAndSize(PyBytes_AS_STRING(v),
+ PyBytes_GET_SIZE(v));
+ Py_DECREF(v);
+ return w;
+}
+
+
+PyDoc_STRVAR(untransform__doc__,
+"B.untransform(encoding, errors='strict') -> bytearray\n\
+\n\
+Reverse-transform B using the codec registered for encoding. errors may\n\
+be given to set a different error handling scheme.");
+
+static PyObject *
+bytearray_untransform(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ const char *encoding = NULL;
+ const char *errors = NULL;
+ static char *kwlist[] = {"encoding", "errors", 0};
+ PyObject *v, *w;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|s:untransform",
+ kwlist, &encoding, &errors))
+ return NULL;
+
+ v = PyCodec_Decode(self, encoding, errors);
+ if (v == NULL)
+ return NULL;
+ if (!PyBytes_Check(v)) {
+ PyErr_Format(PyExc_TypeError,
+ "decoder did not return a bytes object (type=%.400s)",
+ Py_TYPE(v)->tp_name);
+ Py_DECREF(v);
+ return NULL;
+ }
+ w = PyByteArray_FromStringAndSize(PyBytes_AS_STRING(v),
+ PyBytes_GET_SIZE(v));
+ Py_DECREF(v);
+ return w;
+}
+
PyDoc_STRVAR(alloc_doc,
"B.__alloc__() -> int\n\
\n\
@@ -2782,8 +2851,12 @@
{"swapcase", (PyCFunction)stringlib_swapcase, METH_NOARGS,
_Py_swapcase__doc__},
{"title", (PyCFunction)stringlib_title, METH_NOARGS, _Py_title__doc__},
+ {"transform", (PyCFunction)bytearray_transform, METH_VARARGS | METH_KEYWORDS,
+ transform__doc__},
{"translate", (PyCFunction)bytearray_translate, METH_VARARGS,
translate__doc__},
+ {"untransform", (PyCFunction)bytearray_untransform, METH_VARARGS | METH_KEYWORDS,
+ untransform__doc__},
{"upper", (PyCFunction)stringlib_upper, METH_NOARGS, _Py_upper__doc__},
{"zfill", (PyCFunction)stringlib_zfill, METH_VARARGS, zfill__doc__},
{NULL}
Modified: python/branches/py3k/Objects/bytesobject.c
==============================================================================
--- python/branches/py3k/Objects/bytesobject.c (original)
+++ python/branches/py3k/Objects/bytesobject.c Thu Dec 2 19:06:51 2010
@@ -2312,6 +2312,68 @@
return PyUnicode_FromEncodedObject(self, encoding, errors);
}
+PyDoc_STRVAR(transform__doc__,
+"B.transform(encoding, errors='strict') -> bytes\n\
+\n\
+Transform B using the codec registered for encoding. errors may be given\n\
+to set a different error handling scheme.");
+
+static PyObject *
+bytes_transform(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ const char *encoding = NULL;
+ const char *errors = NULL;
+ static char *kwlist[] = {"encoding", "errors", 0};
+ PyObject *v;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|s:transform",
+ kwlist, &encoding, &errors))
+ return NULL;
+
+ v = PyCodec_Encode(self, encoding, errors);
+ if (v == NULL)
+ return NULL;
+ if (!PyBytes_Check(v)) {
+ PyErr_Format(PyExc_TypeError,
+ "encoder did not return a bytes object (type=%.400s)",
+ Py_TYPE(v)->tp_name);
+ Py_DECREF(v);
+ return NULL;
+ }
+ return v;
+}
+
+
+PyDoc_STRVAR(untransform__doc__,
+"B.untransform(encoding, errors='strict') -> bytes\n\
+\n\
+Reverse-transform B using the codec registered for encoding. errors may\n\
+be given to set a different error handling scheme.");
+
+static PyObject *
+bytes_untransform(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ const char *encoding = NULL;
+ const char *errors = NULL;
+ static char *kwlist[] = {"encoding", "errors", 0};
+ PyObject *v;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|s:untransform",
+ kwlist, &encoding, &errors))
+ return NULL;
+
+ v = PyCodec_Decode(self, encoding, errors);
+ if (v == NULL)
+ return NULL;
+ if (!PyBytes_Check(v)) {
+ PyErr_Format(PyExc_TypeError,
+ "decoder did not return a bytes object (type=%.400s)",
+ Py_TYPE(v)->tp_name);
+ Py_DECREF(v);
+ return NULL;
+ }
+ return v;
+}
PyDoc_STRVAR(splitlines__doc__,
"B.splitlines([keepends]) -> list of lines\n\
@@ -2475,8 +2537,10 @@
{"swapcase", (PyCFunction)stringlib_swapcase, METH_NOARGS,
_Py_swapcase__doc__},
{"title", (PyCFunction)stringlib_title, METH_NOARGS, _Py_title__doc__},
+ {"transform", (PyCFunction)bytes_transform, METH_VARARGS | METH_KEYWORDS, transform__doc__},
{"translate", (PyCFunction)bytes_translate, METH_VARARGS,
translate__doc__},
+ {"untransform", (PyCFunction)bytes_untransform, METH_VARARGS | METH_KEYWORDS, untransform__doc__},
{"upper", (PyCFunction)stringlib_upper, METH_NOARGS, _Py_upper__doc__},
{"zfill", (PyCFunction)stringlib_zfill, METH_VARARGS, zfill__doc__},
{"__sizeof__", (PyCFunction)bytes_sizeof, METH_NOARGS,
Modified: python/branches/py3k/Objects/unicodeobject.c
==============================================================================
--- python/branches/py3k/Objects/unicodeobject.c (original)
+++ python/branches/py3k/Objects/unicodeobject.c Thu Dec 2 19:06:51 2010
@@ -7432,6 +7432,7 @@
v = PyUnicode_AsEncodedString((PyObject *)self, encoding, errors);
if (v == NULL)
goto onError;
+ /* XXX this check is redundant */
if (!PyBytes_Check(v)) {
PyErr_Format(PyExc_TypeError,
"encoder did not return a bytes object "
@@ -7446,6 +7447,44 @@
return NULL;
}
+PyDoc_STRVAR(transform__doc__,
+ "S.transform(encoding, errors='strict') -> str\n\
+\n\
+Transform S using the codec registered for encoding. errors may be given\n\
+to set a different error handling scheme.");
+
+static PyObject *
+unicode_transform(PyUnicodeObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = {"encoding", "errors", 0};
+ char *encoding = NULL;
+ char *errors = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|s:transform",
+ kwlist, &encoding, &errors))
+ return NULL;
+ return PyUnicode_AsEncodedUnicode((PyObject *)self, encoding, errors);
+}
+
+PyDoc_STRVAR(untransform__doc__,
+ "S.untransform(encoding, errors='strict') -> str\n\
+\n\
+Reverse-transform S using the codec registered for encoding. errors may be\n\
+given to set a different error handling scheme.");
+
+static PyObject *
+unicode_untransform(PyUnicodeObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = {"encoding", "errors", 0};
+ char *encoding = NULL;
+ char *errors = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|s:untransform",
+ kwlist, &encoding, &errors))
+ return NULL;
+ return PyUnicode_AsDecodedUnicode((PyObject *)self, encoding, errors);
+}
+
PyDoc_STRVAR(expandtabs__doc__,
"S.expandtabs([tabsize]) -> str\n\
\n\
@@ -9091,7 +9130,8 @@
/* Order is according to common usage: often used methods should
appear first, since lookup is done sequentially. */
- {"encode", (PyCFunction) unicode_encode, METH_VARARGS | METH_KEYWORDS, encode__doc__},
+ {"encode", (PyCFunction) unicode_encode, METH_VARARGS | METH_KEYWORDS,
+ encode__doc__},
{"replace", (PyCFunction) unicode_replace, METH_VARARGS, replace__doc__},
{"split", (PyCFunction) unicode_split, METH_VARARGS, split__doc__},
{"rsplit", (PyCFunction) unicode_rsplit, METH_VARARGS, rsplit__doc__},
@@ -9136,6 +9176,10 @@
{"__format__", (PyCFunction) unicode__format__, METH_VARARGS, p_format__doc__},
{"maketrans", (PyCFunction) unicode_maketrans,
METH_VARARGS | METH_STATIC, maketrans__doc__},
+ {"transform", (PyCFunction) unicode_transform, METH_VARARGS | METH_KEYWORDS,
+ transform__doc__},
+ {"untransform", (PyCFunction) unicode_untransform, METH_VARARGS | METH_KEYWORDS,
+ untransform__doc__},
{"__sizeof__", (PyCFunction) unicode__sizeof__, METH_NOARGS, sizeof__doc__},
#if 0
{"capwords", (PyCFunction) unicode_capwords, METH_NOARGS, capwords__doc__},
From python-checkins at python.org Thu Dec 2 19:29:19 2010
From: python-checkins at python.org (brian.curtin)
Date: Thu, 2 Dec 2010 19:29:19 +0100 (CET)
Subject: [Python-checkins] r86935 - in python/branches/py3k:
Doc/library/os.rst Lib/test/support.py Lib/test/test_glob.py
Lib/test/test_httpservers.py Lib/test/test_os.py
Lib/test/test_platform.py Lib/test/test_posixpath.py
Lib/test/test_shutil.py Lib/test/test_sysconfig.py
Lib/test/test_tarfile.py Misc/NEWS Modules/posixmodule.c
Message-ID: <20101202182919.275BEEE9C4@mail.python.org>
Author: brian.curtin
Date: Thu Dec 2 19:29:18 2010
New Revision: 86935
Log:
Fix #9333. Expose os.symlink on Windows only when usable.
In order to create symlinks on Windows, SeCreateSymbolicLinkPrivilege
is an account privilege that is required to be held by the user. Not only
must the privilege be enabled for the account, the activated privileges for
the currently running application must be adjusted to enable the requested
privilege.
Rather than exposing an additional function to be called prior to the user's
first os.symlink call, we handle the AdjustTokenPrivileges Windows API call
internally and only expose os.symlink when the privilege escalation was
successful.
Due to the change of only exposing os.symlink when it's available, we can
go back to the original test skipping methods of checking via `hasattr`.
Modified:
python/branches/py3k/Doc/library/os.rst
python/branches/py3k/Lib/test/support.py
python/branches/py3k/Lib/test/test_glob.py
python/branches/py3k/Lib/test/test_httpservers.py
python/branches/py3k/Lib/test/test_os.py
python/branches/py3k/Lib/test/test_platform.py
python/branches/py3k/Lib/test/test_posixpath.py
python/branches/py3k/Lib/test/test_shutil.py
python/branches/py3k/Lib/test/test_sysconfig.py
python/branches/py3k/Lib/test/test_tarfile.py
python/branches/py3k/Misc/NEWS
python/branches/py3k/Modules/posixmodule.c
Modified: python/branches/py3k/Doc/library/os.rst
==============================================================================
--- python/branches/py3k/Doc/library/os.rst (original)
+++ python/branches/py3k/Doc/library/os.rst Thu Dec 2 19:29:18 2010
@@ -1392,7 +1392,15 @@
Symbolic link support was introduced in Windows 6.0 (Vista). :func:`symlink`
will raise a :exc:`NotImplementedError` on Windows versions earlier than 6.0.
- The *SeCreateSymbolicLinkPrivilege* is required in order to create symlinks.
+
+ .. note::
+
+ The *SeCreateSymbolicLinkPrivilege* is required in order to create
+ symlinks, so the function is only available when the privilege is held.
+ This privilege is not typically granted to regular users but is available
+ to accounts which can escalate privileges to the administrator level.
+ Either obtaining the privilege or running your application as an
+ administrator are ways to successfully create symlinks.
Availability: Unix, Windows.
Modified: python/branches/py3k/Lib/test/support.py
==============================================================================
--- python/branches/py3k/Lib/test/support.py (original)
+++ python/branches/py3k/Lib/test/support.py Thu Dec 2 19:29:18 2010
@@ -42,7 +42,7 @@
"set_memlimit", "bigmemtest", "bigaddrspacetest", "BasicTestRunner",
"run_unittest", "run_doctest", "threading_setup", "threading_cleanup",
"reap_children", "cpython_only", "check_impl_detail", "get_attribute",
- "swap_item", "swap_attr", "can_symlink", "skip_unless_symlink"]
+ "swap_item", "swap_attr"]
class Error(Exception):
@@ -1256,27 +1256,6 @@
except:
break
-try:
- from .symlink_support import enable_symlink_privilege
-except:
- enable_symlink_privilege = lambda: True
-
-def can_symlink():
- """It's no longer sufficient to test for the presence of symlink in the
- os module - on Windows XP and earlier, os.symlink exists but a
- NotImplementedError is thrown.
- """
- has_symlink = hasattr(os, 'symlink')
- is_old_windows = sys.platform == "win32" and sys.getwindowsversion().major < 6
- has_privilege = False if is_old_windows else enable_symlink_privilege()
- return has_symlink and (not is_old_windows) and has_privilege
-
-def skip_unless_symlink(test):
- """Skip decorator for tests that require functional symlink"""
- selector = can_symlink()
- msg = "Requires functional symlink implementation"
- return [unittest.skip(msg)(test), test][selector]
-
@contextlib.contextmanager
def swap_attr(obj, attr, new_val):
"""Temporary swap out an attribute with a new object.
Modified: python/branches/py3k/Lib/test/test_glob.py
==============================================================================
--- python/branches/py3k/Lib/test/test_glob.py (original)
+++ python/branches/py3k/Lib/test/test_glob.py Thu Dec 2 19:29:18 2010
@@ -1,5 +1,5 @@
import unittest
-from test.support import run_unittest, TESTFN, skip_unless_symlink, can_symlink
+from test.support import run_unittest, TESTFN
import glob
import os
import shutil
@@ -25,7 +25,7 @@
self.mktemp('ZZZ')
self.mktemp('a', 'bcd', 'EF')
self.mktemp('a', 'bcd', 'efg', 'ha')
- if can_symlink():
+ if hasattr(os, "symlink"):
os.symlink(self.norm('broken'), self.norm('sym1'))
os.symlink(self.norm('broken'), self.norm('sym2'))
@@ -98,7 +98,8 @@
# either of these results are reasonable
self.assertIn(res[0], [self.tempdir, self.tempdir + os.sep])
- @skip_unless_symlink
+ @unittest.skipUnless(hasattr(os, "symlink"),
+ "Missing symlink implementation")
def test_glob_broken_symlinks(self):
eq = self.assertSequencesEqual_noorder
eq(self.glob('sym*'), [self.norm('sym1'), self.norm('sym2')])
Modified: python/branches/py3k/Lib/test/test_httpservers.py
==============================================================================
--- python/branches/py3k/Lib/test/test_httpservers.py (original)
+++ python/branches/py3k/Lib/test/test_httpservers.py Thu Dec 2 19:29:18 2010
@@ -304,7 +304,7 @@
# The shebang line should be pure ASCII: use symlink if possible.
# See issue #7668.
- if support.can_symlink():
+ if hasattr(os, "symlink"):
self.pythonexe = os.path.join(self.parent_dir, 'python')
os.symlink(sys.executable, self.pythonexe)
else:
Modified: python/branches/py3k/Lib/test/test_os.py
==============================================================================
--- python/branches/py3k/Lib/test/test_os.py (original)
+++ python/branches/py3k/Lib/test/test_os.py Thu Dec 2 19:29:18 2010
@@ -541,7 +541,7 @@
f = open(path, "w")
f.write("I'm " + path + " and proud of it. Blame test_os.\n")
f.close()
- if support.can_symlink():
+ if hasattr(os, "symlink"):
os.symlink(os.path.abspath(t2_path), link_path)
sub2_tree = (sub2_path, ["link"], ["tmp3"])
else:
@@ -585,7 +585,7 @@
self.assertEqual(all[flipped + 1], (sub1_path, ["SUB11"], ["tmp2"]))
self.assertEqual(all[2 - 2 * flipped], sub2_tree)
- if support.can_symlink():
+ if hasattr(os, "symlink"):
# Walk, following symlinks.
for root, dirs, files in os.walk(walk_path, followlinks=True):
if root == link_path:
@@ -1146,14 +1146,8 @@
self._kill_with_event(signal.CTRL_BREAK_EVENT, "CTRL_BREAK_EVENT")
-def skipUnlessWindows6(test):
- if (hasattr(sys, 'getwindowsversion')
- and sys.getwindowsversion().major >= 6):
- return test
- return unittest.skip("Requires Windows Vista or later")(test)
-
@unittest.skipUnless(sys.platform == "win32", "Win32 specific tests")
- at support.skip_unless_symlink
+ at unittest.skipUnless(hasattr(os, "symlink"), "Requires symlink implementation")
class Win32SymlinkTests(unittest.TestCase):
filelink = 'filelinktest'
filelink_target = os.path.abspath(__file__)
Modified: python/branches/py3k/Lib/test/test_platform.py
==============================================================================
--- python/branches/py3k/Lib/test/test_platform.py (original)
+++ python/branches/py3k/Lib/test/test_platform.py Thu Dec 2 19:29:18 2010
@@ -10,7 +10,8 @@
def test_architecture(self):
res = platform.architecture()
- @support.skip_unless_symlink
+ @unittest.skipUnless(hasattr(os, "symlink"),
+ "Missing symlink implementation")
def test_architecture_via_symlink(self): # issue3762
# On Windows, the EXE needs to know where pythonXY.dll is at so we have
# to add the directory to the path.
Modified: python/branches/py3k/Lib/test/test_posixpath.py
==============================================================================
--- python/branches/py3k/Lib/test/test_posixpath.py (original)
+++ python/branches/py3k/Lib/test/test_posixpath.py Thu Dec 2 19:29:18 2010
@@ -155,7 +155,7 @@
f.write(b"foo")
f.close()
self.assertIs(posixpath.islink(support.TESTFN + "1"), False)
- if support.can_symlink():
+ if hasattr(os, "symlink"):
os.symlink(support.TESTFN + "1", support.TESTFN + "2")
self.assertIs(posixpath.islink(support.TESTFN + "2"), True)
os.remove(support.TESTFN + "1")
@@ -180,7 +180,8 @@
@unittest.skipIf(
sys.platform.startswith('win'),
"posixpath.samefile does not work on links in Windows")
- @support.skip_unless_symlink
+ @unittest.skipUnless(hasattr(os, "symlink"),
+ "Missing symlink implementation")
def test_samefile_on_links(self):
test_fn1 = support.TESTFN + "1"
test_fn2 = support.TESTFN + "2"
@@ -204,7 +205,8 @@
@unittest.skipIf(
sys.platform.startswith('win'),
"posixpath.samestat does not work on links in Windows")
- @support.skip_unless_symlink
+ @unittest.skipUnless(hasattr(os, "symlink"),
+ "Missing symlink implementation")
def test_samestat_on_links(self):
test_fn1 = support.TESTFN + "1"
test_fn2 = support.TESTFN + "2"
@@ -273,7 +275,8 @@
self.assertEqual(posixpath.normpath(b"///..//./foo/.//bar"),
b"/foo/bar")
- @support.skip_unless_symlink
+ @unittest.skipUnless(hasattr(os, "symlink"),
+ "Missing symlink implementation")
@skip_if_ABSTFN_contains_backslash
def test_realpath_basic(self):
# Basic operation.
@@ -283,7 +286,8 @@
finally:
support.unlink(ABSTFN)
- @support.skip_unless_symlink
+ @unittest.skipUnless(hasattr(os, "symlink"),
+ "Missing symlink implementation")
@skip_if_ABSTFN_contains_backslash
def test_realpath_symlink_loops(self):
# Bug #930024, return the path unchanged if we get into an infinite
@@ -307,7 +311,8 @@
support.unlink(ABSTFN+"1")
support.unlink(ABSTFN+"2")
- @support.skip_unless_symlink
+ @unittest.skipUnless(hasattr(os, "symlink"),
+ "Missing symlink implementation")
@skip_if_ABSTFN_contains_backslash
def test_realpath_resolve_parents(self):
# We also need to resolve any symlinks in the parents of a relative
@@ -328,7 +333,8 @@
safe_rmdir(ABSTFN + "/y")
safe_rmdir(ABSTFN)
- @support.skip_unless_symlink
+ @unittest.skipUnless(hasattr(os, "symlink"),
+ "Missing symlink implementation")
@skip_if_ABSTFN_contains_backslash
def test_realpath_resolve_before_normalizing(self):
# Bug #990669: Symbolic links should be resolved before we
@@ -358,7 +364,8 @@
safe_rmdir(ABSTFN + "/k")
safe_rmdir(ABSTFN)
- @support.skip_unless_symlink
+ @unittest.skipUnless(hasattr(os, "symlink"),
+ "Missing symlink implementation")
@skip_if_ABSTFN_contains_backslash
def test_realpath_resolve_first(self):
# Bug #1213894: The first component of the path, if not absolute,
Modified: python/branches/py3k/Lib/test/test_shutil.py
==============================================================================
--- python/branches/py3k/Lib/test/test_shutil.py (original)
+++ python/branches/py3k/Lib/test/test_shutil.py Thu Dec 2 19:29:18 2010
@@ -271,7 +271,8 @@
shutil.rmtree(src_dir)
shutil.rmtree(os.path.dirname(dst_dir))
- @support.skip_unless_symlink
+ @unittest.skipUnless(hasattr(os, "symlink"),
+ "Missing symlink implementation")
def test_dont_copy_file_onto_link_to_itself(self):
# bug 851123.
os.mkdir(TESTFN)
@@ -303,7 +304,8 @@
except OSError:
pass
- @support.skip_unless_symlink
+ @unittest.skipUnless(hasattr(os, "symlink"),
+ "Missing symlink implementation")
def test_rmtree_on_symlink(self):
# bug 1669.
os.mkdir(TESTFN)
@@ -328,26 +330,27 @@
finally:
os.remove(TESTFN)
- @unittest.skipUnless(hasattr(os, 'mkfifo'), 'requires os.mkfifo')
- def test_copytree_named_pipe(self):
- os.mkdir(TESTFN)
- try:
- subdir = os.path.join(TESTFN, "subdir")
- os.mkdir(subdir)
- pipe = os.path.join(subdir, "mypipe")
- os.mkfifo(pipe)
+ @unittest.skipUnless(hasattr(os, "symlink"),
+ "Missing symlink implementation")
+ def test_copytree_named_pipe(self):
+ os.mkdir(TESTFN)
try:
- shutil.copytree(TESTFN, TESTFN2)
- except shutil.Error as e:
- errors = e.args[0]
- self.assertEqual(len(errors), 1)
- src, dst, error_msg = errors[0]
- self.assertEqual("`%s` is a named pipe" % pipe, error_msg)
- else:
- self.fail("shutil.Error should have been raised")
- finally:
- shutil.rmtree(TESTFN, ignore_errors=True)
- shutil.rmtree(TESTFN2, ignore_errors=True)
+ subdir = os.path.join(TESTFN, "subdir")
+ os.mkdir(subdir)
+ pipe = os.path.join(subdir, "mypipe")
+ os.mkfifo(pipe)
+ try:
+ shutil.copytree(TESTFN, TESTFN2)
+ except shutil.Error as e:
+ errors = e.args[0]
+ self.assertEqual(len(errors), 1)
+ src, dst, error_msg = errors[0]
+ self.assertEqual("`%s` is a named pipe" % pipe, error_msg)
+ else:
+ self.fail("shutil.Error should have been raised")
+ finally:
+ shutil.rmtree(TESTFN, ignore_errors=True)
+ shutil.rmtree(TESTFN2, ignore_errors=True)
def test_copytree_special_func(self):
@@ -364,7 +367,8 @@
shutil.copytree(src_dir, dst_dir, copy_function=_copy)
self.assertEqual(len(copied), 2)
- @support.skip_unless_symlink
+ @unittest.skipUnless(hasattr(os, "symlink"),
+ "Missing symlink implementation")
def test_copytree_dangling_symlinks(self):
# a dangling symlink raises an error at the end
Modified: python/branches/py3k/Lib/test/test_sysconfig.py
==============================================================================
--- python/branches/py3k/Lib/test/test_sysconfig.py (original)
+++ python/branches/py3k/Lib/test/test_sysconfig.py Thu Dec 2 19:29:18 2010
@@ -12,7 +12,7 @@
from copy import copy, deepcopy
from test.support import (run_unittest, TESTFN, unlink, get_attribute,
- captured_stdout, skip_unless_symlink)
+ captured_stdout)
import sysconfig
from sysconfig import (get_paths, get_platform, get_config_vars,
@@ -245,7 +245,8 @@
'posix_home', 'posix_prefix', 'posix_user')
self.assertEqual(get_scheme_names(), wanted)
- @skip_unless_symlink
+ @unittest.skipUnless(hasattr(os, "symlink"),
+ "Missing symlink implementation")
def test_symlink(self):
# On Windows, the EXE needs to know where pythonXY.dll is at so we have
# to add the directory to the path.
Modified: python/branches/py3k/Lib/test/test_tarfile.py
==============================================================================
--- python/branches/py3k/Lib/test/test_tarfile.py (original)
+++ python/branches/py3k/Lib/test/test_tarfile.py Thu Dec 2 19:29:18 2010
@@ -322,7 +322,8 @@
@unittest.skipUnless(hasattr(os, "link"),
"Missing hardlink implementation")
- @support.skip_unless_symlink
+ @unittest.skipUnless(hasattr(os, "symlink"),
+ "Missing symlink implementation")
def test_extract_hardlink(self):
# Test hardlink extraction (e.g. bug #857297).
tar = tarfile.open(tarname, errorlevel=1, encoding="iso8859-1")
@@ -840,7 +841,8 @@
os.remove(target)
os.remove(link)
- @support.skip_unless_symlink
+ @unittest.skipUnless(hasattr(os, "symlink"),
+ "Missing symlink implementation")
def test_symlink_size(self):
path = os.path.join(TEMPDIR, "symlink")
os.symlink("link_target", path)
Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS (original)
+++ python/branches/py3k/Misc/NEWS Thu Dec 2 19:29:18 2010
@@ -10,6 +10,9 @@
Core and Builtins
-----------------
+- Issue #9333: Expose os.symlink only when the SeCreateSymbolicLinkPrivilege
+ is held by the user's account, i.e., when the function can actually be used.
+
- Issue #7475: Added transform() and untransform() methods to both bytes
and string types. They can be used to access those codecs providing
bytes-to-bytes and string-to-string mappings.
Modified: python/branches/py3k/Modules/posixmodule.c
==============================================================================
--- python/branches/py3k/Modules/posixmodule.c (original)
+++ python/branches/py3k/Modules/posixmodule.c Thu Dec 2 19:29:18 2010
@@ -277,6 +277,9 @@
#include
#include /* for ShellExecute() */
#include /* for UNLEN */
+#ifdef SE_CREATE_SYMBOLIC_LINK_NAME /* Available starting with Vista */
+#define HAVE_SYMLINK
+#endif
#endif /* _MSC_VER */
#if defined(PYCC_VACPP) && defined(PYOS_OS2)
@@ -5091,7 +5094,7 @@
#endif /* HAVE_READLINK */
-#ifdef HAVE_SYMLINK
+#if defined(HAVE_SYMLINK) && !defined(MS_WINDOWS)
PyDoc_STRVAR(posix_symlink__doc__,
"symlink(src, dst)\n\n\
Create a symbolic link pointing to src named dst.");
@@ -5179,7 +5182,7 @@
#endif /* !defined(HAVE_READLINK) && defined(MS_WINDOWS) */
-#if !defined(HAVE_SYMLINK) && defined(MS_WINDOWS)
+#if defined(HAVE_SYMLINK) && defined(MS_WINDOWS)
/* Grab CreateSymbolicLinkW dynamically from kernel32 */
static int has_CreateSymbolicLinkW = 0;
@@ -5257,7 +5260,7 @@
Py_INCREF(Py_None);
return Py_None;
}
-#endif /* !defined(HAVE_SYMLINK) && defined(MS_WINDOWS) */
+#endif /* defined(HAVE_SYMLINK) && defined(MS_WINDOWS) */
#ifdef HAVE_TIMES
#if defined(PYCC_VACPP) && defined(PYOS_OS2)
@@ -7779,13 +7782,13 @@
{"rmdir", posix_rmdir, METH_VARARGS, posix_rmdir__doc__},
{"stat", posix_stat, METH_VARARGS, posix_stat__doc__},
{"stat_float_times", stat_float_times, METH_VARARGS, stat_float_times__doc__},
-#ifdef HAVE_SYMLINK
+#if defined(HAVE_SYMLINK) && !defined(MS_WINDOWS)
{"symlink", posix_symlink, METH_VARARGS, posix_symlink__doc__},
#endif /* HAVE_SYMLINK */
-#if !defined(HAVE_SYMLINK) && defined(MS_WINDOWS)
- {"symlink", (PyCFunction)win_symlink, METH_VARARGS | METH_KEYWORDS,
- win_symlink__doc__},
-#endif /* !defined(HAVE_SYMLINK) && defined(MS_WINDOWS) */
+#if defined(HAVE_SYMLINK) && defined(MS_WINDOWS)
+ {"_symlink", (PyCFunction)win_symlink, METH_VARARGS | METH_KEYWORDS,
+ win_symlink__doc__},
+#endif /* defined(HAVE_SYMLINK) && defined(MS_WINDOWS) */
#ifdef HAVE_SYSTEM
{"system", posix_system, METH_VARARGS, posix_system__doc__},
#endif
@@ -8099,6 +8102,46 @@
}
#endif
+#if defined(HAVE_SYMLINK) && defined(MS_WINDOWS)
+void
+enable_symlink()
+{
+ HANDLE tok;
+ TOKEN_PRIVILEGES tok_priv;
+ LUID luid;
+ int meth_idx = 0;
+
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &tok))
+ return;
+
+ if (!LookupPrivilegeValue(NULL, SE_CREATE_SYMBOLIC_LINK_NAME, &luid))
+ return;
+
+ tok_priv.PrivilegeCount = 1;
+ tok_priv.Privileges[0].Luid = luid;
+ tok_priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+ if (!AdjustTokenPrivileges(tok, FALSE, &tok_priv,
+ sizeof(TOKEN_PRIVILEGES),
+ (PTOKEN_PRIVILEGES) NULL, (PDWORD) NULL))
+ return;
+
+ if(GetLastError() == ERROR_NOT_ALL_ASSIGNED) {
+ /* We couldn't acquire the necessary privilege, so leave the
+ method hidden for this user. */
+ return;
+ } else {
+ /* We've successfully acquired the symlink privilege so rename
+ the method to it's proper "os.symlink" name. */
+ while(posix_methods[meth_idx].ml_meth != (PyCFunction)win_symlink)
+ meth_idx++;
+ posix_methods[meth_idx].ml_name = "symlink";
+
+ return;
+ }
+}
+#endif /* defined(HAVE_SYMLINK) && defined(MS_WINDOWS) */
+
static int
all_ins(PyObject *d)
{
@@ -8360,6 +8403,10 @@
{
PyObject *m, *v;
+#if defined(HAVE_SYMLINK) && defined(MS_WINDOWS)
+ enable_symlink();
+#endif
+
m = PyModule_Create(&posixmodule);
if (m == NULL)
return NULL;
From python-checkins at python.org Thu Dec 2 22:47:19 2010
From: python-checkins at python.org (r.david.murray)
Date: Thu, 2 Dec 2010 22:47:19 +0100 (CET)
Subject: [Python-checkins] r86936 - in python/branches/py3k:
Doc/library/email.util.rst Lib/email/test/test_email.py
Lib/email/utils.py Misc/ACKS Misc/NEWS
Message-ID: <20101202214719.BE98DEE98A@mail.python.org>
Author: r.david.murray
Date: Thu Dec 2 22:47:19 2010
New Revision: 86936
Log:
#8989: add 'domain' keyword to make_msgid.
Patch by Adrian von Bidder.
Modified:
python/branches/py3k/Doc/library/email.util.rst
python/branches/py3k/Lib/email/test/test_email.py
python/branches/py3k/Lib/email/utils.py
python/branches/py3k/Misc/ACKS
python/branches/py3k/Misc/NEWS
Modified: python/branches/py3k/Doc/library/email.util.rst
==============================================================================
--- python/branches/py3k/Doc/library/email.util.rst (original)
+++ python/branches/py3k/Doc/library/email.util.rst Thu Dec 2 22:47:19 2010
@@ -105,11 +105,17 @@
``False``. The default is ``False``.
-.. function:: make_msgid(idstring=None)
+.. function:: make_msgid(idstring=None, domain=None)
Returns a string suitable for an :rfc:`2822`\ -compliant
:mailheader:`Message-ID` header. Optional *idstring* if given, is a string
- used to strengthen the uniqueness of the message id.
+ used to strengthen the uniqueness of the message id. Optional *domain* if
+ given provides the portion of the msgid after the '@'. The default is the
+ local hostname. It is not normally necessary to override this default, but
+ may be useful certain cases, such as a constructing distributed system that
+ uses a consistent domain name across multiple hosts.
+
+ .. versionchanged:: 3.2 domain keyword added
.. function:: decode_rfc2231(s)
Modified: python/branches/py3k/Lib/email/test/test_email.py
==============================================================================
--- python/branches/py3k/Lib/email/test/test_email.py (original)
+++ python/branches/py3k/Lib/email/test/test_email.py Thu Dec 2 22:47:19 2010
@@ -2457,6 +2457,10 @@
text/rfc822-headers
""")
+ def test_make_msgid_domain(self):
+ self.assertEqual(
+ email.utils.make_msgid(domain='testdomain-string')[-19:],
+ '@testdomain-string>')
# Test the iterator/generators
Modified: python/branches/py3k/Lib/email/utils.py
==============================================================================
--- python/branches/py3k/Lib/email/utils.py (original)
+++ python/branches/py3k/Lib/email/utils.py Thu Dec 2 22:47:19 2010
@@ -148,13 +148,15 @@
-def make_msgid(idstring=None):
+def make_msgid(idstring=None, domain=None):
"""Returns a string suitable for RFC 2822 compliant Message-ID, e.g:
<20020201195627.33539.96671 at nightshade.la.mastaler.com>
Optional idstring if given is a string used to strengthen the
- uniqueness of the message id.
+ uniqueness of the message id. Optional domain if given provides the
+ portion of the message id after the '@'. It defaults to the locally
+ defined hostname.
"""
timeval = time.time()
utcdate = time.strftime('%Y%m%d%H%M%S', time.gmtime(timeval))
@@ -164,8 +166,9 @@
idstring = ''
else:
idstring = '.' + idstring
- idhost = socket.getfqdn()
- msgid = '<%s.%s.%s%s@%s>' % (utcdate, pid, randint, idstring, idhost)
+ if domain is None:
+ domain = socket.getfqdn()
+ msgid = '<%s.%s.%s%s@%s>' % (utcdate, pid, randint, idstring, domain)
return msgid
Modified: python/branches/py3k/Misc/ACKS
==============================================================================
--- python/branches/py3k/Misc/ACKS (original)
+++ python/branches/py3k/Misc/ACKS Thu Dec 2 22:47:19 2010
@@ -77,6 +77,7 @@
Steven Bethard
Stephen Bevan
Ron Bickers
+Adrian von Bidder
David Binger
Dominic Binks
Philippe Biondi
Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS (original)
+++ python/branches/py3k/Misc/NEWS Thu Dec 2 22:47:19 2010
@@ -53,6 +53,9 @@
Library
-------
+- Issue #8989: email.utils.make_msgid now has a domain parameter that can
+ override the domain name used in the generated msgid.
+
- Issue #9299: Add exist_ok parameter to os.makedirs to suppress the
'File exists' exception when a target directory already exists with the
specified mode. Patch by Ray Allen.
From python-checkins at python.org Thu Dec 2 22:55:33 2010
From: python-checkins at python.org (daniel.stutzbach)
Date: Thu, 2 Dec 2010 22:55:33 +0100 (CET)
Subject: [Python-checkins] r86937 - in python/branches/py3k: Misc/NEWS
Objects/listobject.c
Message-ID: <20101202215533.761DAEE9CD@mail.python.org>
Author: daniel.stutzbach
Date: Thu Dec 2 22:55:33 2010
New Revision: 86937
Log:
Issue9915: speeding up sorting with a key
Modified:
python/branches/py3k/Misc/NEWS
python/branches/py3k/Objects/listobject.c
Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS (original)
+++ python/branches/py3k/Misc/NEWS Thu Dec 2 22:55:33 2010
@@ -10,6 +10,8 @@
Core and Builtins
-----------------
+- Issue #9915: Speed up sorting with a key.
+
- Issue #9333: Expose os.symlink only when the SeCreateSymbolicLinkPrivilege
is held by the user's account, i.e., when the function can actually be used.
Modified: python/branches/py3k/Objects/listobject.c
==============================================================================
--- python/branches/py3k/Objects/listobject.c (original)
+++ python/branches/py3k/Objects/listobject.c Thu Dec 2 22:55:33 2010
@@ -940,6 +940,66 @@
* pieces to this algorithm; read listsort.txt for overviews and details.
*/
+/* A sortslice contains a pointer to an array of keys and a pointer to
+ * an array of corresponding values. In other words, keys[i]
+ * corresponds with values[i]. If values == NULL, then the keys are
+ * also the values.
+ *
+ * Several convenience routines are provided here, so that keys and
+ * values are always moved in sync.
+ */
+
+typedef struct {
+ PyObject **keys;
+ PyObject **values;
+} sortslice;
+
+Py_LOCAL_INLINE(void)
+sortslice_copy(sortslice *s1, Py_ssize_t i, sortslice *s2, Py_ssize_t j)
+{
+ s1->keys[i] = s2->keys[j];
+ if (s1->values != NULL)
+ s1->values[i] = s2->values[j];
+}
+
+Py_LOCAL_INLINE(void)
+sortslice_copy_incr(sortslice *dst, sortslice *src) {
+ *dst->keys++ = *src->keys++;
+ if (dst->values != NULL)
+ *dst->values++ = *src->values++;
+}
+
+Py_LOCAL_INLINE(void)
+sortslice_copy_decr(sortslice *dst, sortslice *src) {
+ *dst->keys-- = *src->keys--;
+ if (dst->values != NULL)
+ *dst->values-- = *src->values--;
+}
+
+
+Py_LOCAL_INLINE(void)
+sortslice_memcpy(sortslice *s1, Py_ssize_t i, sortslice *s2, Py_ssize_t j,
+ Py_ssize_t n) {
+ memcpy(&s1->keys[i], &s2->keys[j], sizeof(PyObject *) * n);
+ if (s1->values != NULL)
+ memcpy(&s1->values[i], &s2->values[j], sizeof(PyObject *) * n);
+}
+
+Py_LOCAL_INLINE(void)
+sortslice_memmove(sortslice *s1, Py_ssize_t i, sortslice *s2, Py_ssize_t j,
+ Py_ssize_t n) {
+ memmove(&s1->keys[i], &s2->keys[j], sizeof(PyObject *) * n);
+ if (s1->values != NULL)
+ memmove(&s1->values[i], &s2->values[j], sizeof(PyObject *) * n);
+}
+
+Py_LOCAL_INLINE(void)
+sortslice_advance(sortslice *slice, Py_ssize_t n) {
+ slice->keys += n;
+ if (slice->values != NULL)
+ slice->values += n;
+}
+
/* Comparison function: PyObject_RichCompareBool with Py_LT.
* Returns -1 on error, 1 if x < y, 0 if x >= y.
*/
@@ -965,19 +1025,19 @@
the input (nothing is lost or duplicated).
*/
static int
-binarysort(PyObject **lo, PyObject **hi, PyObject **start)
+binarysort(sortslice lo, PyObject **hi, PyObject **start)
{
register Py_ssize_t k;
register PyObject **l, **p, **r;
register PyObject *pivot;
- assert(lo <= start && start <= hi);
+ assert(lo.keys <= start && start <= hi);
/* assert [lo, start) is sorted */
- if (lo == start)
+ if (lo.keys == start)
++start;
for (; start < hi; ++start) {
/* set l to where *start belongs */
- l = lo;
+ l = lo.keys;
r = start;
pivot = *r;
/* Invariants:
@@ -1004,6 +1064,15 @@
for (p = start; p > l; --p)
*p = *(p-1);
*l = pivot;
+ if (lo.values != NULL) {
+ Py_ssize_t offset = lo.values - lo.keys;
+ p = start + offset;
+ pivot = *p;
+ l += offset;
+ for (p = start + offset; p > l; --p)
+ *p = *(p-1);
+ *l = pivot;
+ }
}
return 0;
@@ -1272,7 +1341,7 @@
* a convenient way to pass state around among the helper functions.
*/
struct s_slice {
- PyObject **base;
+ sortslice base;
Py_ssize_t len;
};
@@ -1286,7 +1355,7 @@
/* 'a' is temp storage to help with merges. It contains room for
* alloced entries.
*/
- PyObject **a; /* may point to temparray below */
+ sortslice a; /* may point to temparray below */
Py_ssize_t alloced;
/* A stack of n pending runs yet to be merged. Run #i starts at
@@ -1307,11 +1376,29 @@
/* Conceptually a MergeState's constructor. */
static void
-merge_init(MergeState *ms)
+merge_init(MergeState *ms, int list_size, int has_keyfunc)
{
assert(ms != NULL);
- ms->a = ms->temparray;
- ms->alloced = MERGESTATE_TEMP_SIZE;
+ if (has_keyfunc) {
+ /* The temporary space for merging will need at most half the list
+ * size rounded up. Use the minimum possible space so we can use the
+ * rest of temparray for other things. In particular, if there is
+ * enough extra space, listsort() will use it to store the keys.
+ */
+ ms->alloced = (list_size + 1) / 2;
+
+ /* ms->alloced describes how many keys will be stored at
+ ms->temparray, but we also need to store the values. Hence,
+ ms->alloced is capped at half of MERGESTATE_TEMP_SIZE. */
+ if (MERGESTATE_TEMP_SIZE / 2 < ms->alloced)
+ ms->alloced = MERGESTATE_TEMP_SIZE / 2;
+ ms->a.values = &ms->temparray[ms->alloced];
+ }
+ else {
+ ms->alloced = MERGESTATE_TEMP_SIZE;
+ ms->a.values = NULL;
+ }
+ ms->a.keys = ms->temparray;
ms->n = 0;
ms->min_gallop = MIN_GALLOP;
}
@@ -1324,10 +1411,8 @@
merge_freemem(MergeState *ms)
{
assert(ms != NULL);
- if (ms->a != ms->temparray)
- PyMem_Free(ms->a);
- ms->a = ms->temparray;
- ms->alloced = MERGESTATE_TEMP_SIZE;
+ if (ms->a.keys != ms->temparray)
+ PyMem_Free(ms->a.keys);
}
/* Ensure enough temp memory for 'need' array slots is available.
@@ -1336,52 +1421,60 @@
static int
merge_getmem(MergeState *ms, Py_ssize_t need)
{
+ int multiplier;
+
assert(ms != NULL);
if (need <= ms->alloced)
return 0;
+
+ multiplier = ms->a.values != NULL ? 2 : 1;
+
/* Don't realloc! That can cost cycles to copy the old data, but
* we don't care what's in the block.
*/
merge_freemem(ms);
- if ((size_t)need > PY_SSIZE_T_MAX / sizeof(PyObject*)) {
+ if ((size_t)need > PY_SSIZE_T_MAX / sizeof(PyObject*) / multiplier) {
PyErr_NoMemory();
return -1;
}
- ms->a = (PyObject **)PyMem_Malloc(need * sizeof(PyObject*));
- if (ms->a) {
+ ms->a.keys = (PyObject**)PyMem_Malloc(multiplier * need
+ * sizeof(PyObject *));
+ if (ms->a.keys != NULL) {
ms->alloced = need;
+ if (ms->a.values != NULL)
+ ms->a.values = &ms->a.keys[need];
return 0;
}
PyErr_NoMemory();
- merge_freemem(ms); /* reset to sane state */
return -1;
}
#define MERGE_GETMEM(MS, NEED) ((NEED) <= (MS)->alloced ? 0 : \
merge_getmem(MS, NEED))
-/* Merge the na elements starting at pa with the nb elements starting at pb
- * in a stable way, in-place. na and nb must be > 0, and pa + na == pb.
- * Must also have that *pb < *pa, that pa[na-1] belongs at the end of the
- * merge, and should have na <= nb. See listsort.txt for more info.
- * Return 0 if successful, -1 if error.
+/* Merge the na elements starting at ssa with the nb elements starting at
+ * ssb.keys = ssa.keys + na in a stable way, in-place. na and nb must be > 0.
+ * Must also have that ssa.keys[na-1] belongs at the end of the merge, and
+ * should have na <= nb. See listsort.txt for more info. Return 0 if
+ * successful, -1 if error.
*/
static Py_ssize_t
-merge_lo(MergeState *ms, PyObject **pa, Py_ssize_t na,
- PyObject **pb, Py_ssize_t nb)
+merge_lo(MergeState *ms, sortslice ssa, Py_ssize_t na,
+ sortslice ssb, Py_ssize_t nb)
{
Py_ssize_t k;
- PyObject **dest;
+ sortslice dest;
int result = -1; /* guilty until proved innocent */
Py_ssize_t min_gallop;
- assert(ms && pa && pb && na > 0 && nb > 0 && pa + na == pb);
+ assert(ms && ssa.keys && ssb.keys && na > 0 && nb > 0);
+ assert(ssa.keys + na == ssb.keys);
if (MERGE_GETMEM(ms, na) < 0)
return -1;
- memcpy(ms->a, pa, na * sizeof(PyObject*));
- dest = pa;
- pa = ms->a;
+ sortslice_memcpy(&ms->a, 0, &ssa, 0, na);
+ dest = ssa;
+ ssa = ms->a;
- *dest++ = *pb++;
+ sortslice_copy_incr(&dest, &ssb);
--nb;
if (nb == 0)
goto Succeed;
@@ -1398,11 +1491,11 @@
*/
for (;;) {
assert(na > 1 && nb > 0);
- k = ISLT(*pb, *pa);
+ k = ISLT(ssb.keys[0], ssa.keys[0]);
if (k) {
if (k < 0)
goto Fail;
- *dest++ = *pb++;
+ sortslice_copy_incr(&dest, &ssb);
++bcount;
acount = 0;
--nb;
@@ -1412,7 +1505,7 @@
break;
}
else {
- *dest++ = *pa++;
+ sortslice_copy_incr(&dest, &ssa);
++acount;
bcount = 0;
--na;
@@ -1433,14 +1526,14 @@
assert(na > 1 && nb > 0);
min_gallop -= min_gallop > 1;
ms->min_gallop = min_gallop;
- k = gallop_right(*pb, pa, na, 0);
+ k = gallop_right(ssb.keys[0], ssa.keys, na, 0);
acount = k;
if (k) {
if (k < 0)
goto Fail;
- memcpy(dest, pa, k * sizeof(PyObject *));
- dest += k;
- pa += k;
+ sortslice_memcpy(&dest, 0, &ssa, 0, k);
+ sortslice_advance(&dest, k);
+ sortslice_advance(&ssa, k);
na -= k;
if (na == 1)
goto CopyB;
@@ -1451,24 +1544,24 @@
if (na == 0)
goto Succeed;
}
- *dest++ = *pb++;
+ sortslice_copy_incr(&dest, &ssb);
--nb;
if (nb == 0)
goto Succeed;
- k = gallop_left(*pa, pb, nb, 0);
+ k = gallop_left(ssa.keys[0], ssb.keys, nb, 0);
bcount = k;
if (k) {
if (k < 0)
goto Fail;
- memmove(dest, pb, k * sizeof(PyObject *));
- dest += k;
- pb += k;
+ sortslice_memmove(&dest, 0, &ssb, 0, k);
+ sortslice_advance(&dest, k);
+ sortslice_advance(&ssb, k);
nb -= k;
if (nb == 0)
goto Succeed;
}
- *dest++ = *pa++;
+ sortslice_copy_incr(&dest, &ssa);
--na;
if (na == 1)
goto CopyB;
@@ -1480,43 +1573,46 @@
result = 0;
Fail:
if (na)
- memcpy(dest, pa, na * sizeof(PyObject*));
+ sortslice_memcpy(&dest, 0, &ssa, 0, na);
return result;
CopyB:
assert(na == 1 && nb > 0);
- /* The last element of pa belongs at the end of the merge. */
- memmove(dest, pb, nb * sizeof(PyObject *));
- dest[nb] = *pa;
+ /* The last element of ssa belongs at the end of the merge. */
+ sortslice_memmove(&dest, 0, &ssb, 0, nb);
+ sortslice_copy(&dest, nb, &ssa, 0);
return 0;
}
-/* Merge the na elements starting at pa with the nb elements starting at pb
- * in a stable way, in-place. na and nb must be > 0, and pa + na == pb.
- * Must also have that *pb < *pa, that pa[na-1] belongs at the end of the
- * merge, and should have na >= nb. See listsort.txt for more info.
- * Return 0 if successful, -1 if error.
+/* Merge the na elements starting at pa with the nb elements starting at
+ * ssb.keys = ssa.keys + na in a stable way, in-place. na and nb must be > 0.
+ * Must also have that ssa.keys[na-1] belongs at the end of the merge, and
+ * should have na >= nb. See listsort.txt for more info. Return 0 if
+ * successful, -1 if error.
*/
static Py_ssize_t
-merge_hi(MergeState *ms, PyObject **pa, Py_ssize_t na, PyObject **pb, Py_ssize_t nb)
+merge_hi(MergeState *ms, sortslice ssa, Py_ssize_t na,
+ sortslice ssb, Py_ssize_t nb)
{
Py_ssize_t k;
- PyObject **dest;
+ sortslice dest, basea, baseb;
int result = -1; /* guilty until proved innocent */
- PyObject **basea;
- PyObject **baseb;
Py_ssize_t min_gallop;
- assert(ms && pa && pb && na > 0 && nb > 0 && pa + na == pb);
+ assert(ms && ssa.keys && ssb.keys && na > 0 && nb > 0);
+ assert(ssa.keys + na == ssb.keys);
if (MERGE_GETMEM(ms, nb) < 0)
return -1;
- dest = pb + nb - 1;
- memcpy(ms->a, pb, nb * sizeof(PyObject*));
- basea = pa;
+ dest = ssb;
+ sortslice_advance(&dest, nb-1);
+ sortslice_memcpy(&ms->a, 0, &ssb, 0, nb);
+ basea = ssa;
baseb = ms->a;
- pb = ms->a + nb - 1;
- pa += na - 1;
+ ssb.keys = ms->a.keys + nb - 1;
+ if (ssb.values != NULL)
+ ssb.values = ms->a.values + nb - 1;
+ sortslice_advance(&ssa, na - 1);
- *dest-- = *pa--;
+ sortslice_copy_decr(&dest, &ssa);
--na;
if (na == 0)
goto Succeed;
@@ -1533,11 +1629,11 @@
*/
for (;;) {
assert(na > 0 && nb > 1);
- k = ISLT(*pb, *pa);
+ k = ISLT(ssb.keys[0], ssa.keys[0]);
if (k) {
if (k < 0)
goto Fail;
- *dest-- = *pa--;
+ sortslice_copy_decr(&dest, &ssa);
++acount;
bcount = 0;
--na;
@@ -1547,7 +1643,7 @@
break;
}
else {
- *dest-- = *pb--;
+ sortslice_copy_decr(&dest, &ssb);
++bcount;
acount = 0;
--nb;
@@ -1568,33 +1664,33 @@
assert(na > 0 && nb > 1);
min_gallop -= min_gallop > 1;
ms->min_gallop = min_gallop;
- k = gallop_right(*pb, basea, na, na-1);
+ k = gallop_right(ssb.keys[0], basea.keys, na, na-1);
if (k < 0)
goto Fail;
k = na - k;
acount = k;
if (k) {
- dest -= k;
- pa -= k;
- memmove(dest+1, pa+1, k * sizeof(PyObject *));
+ sortslice_advance(&dest, -k);
+ sortslice_advance(&ssa, -k);
+ sortslice_memmove(&dest, 1, &ssa, 1, k);
na -= k;
if (na == 0)
goto Succeed;
}
- *dest-- = *pb--;
+ sortslice_copy_decr(&dest, &ssb);
--nb;
if (nb == 1)
goto CopyA;
- k = gallop_left(*pa, baseb, nb, nb-1);
+ k = gallop_left(ssa.keys[0], baseb.keys, nb, nb-1);
if (k < 0)
goto Fail;
k = nb - k;
bcount = k;
if (k) {
- dest -= k;
- pb -= k;
- memcpy(dest+1, pb+1, k * sizeof(PyObject *));
+ sortslice_advance(&dest, -k);
+ sortslice_advance(&ssb, -k);
+ sortslice_memcpy(&dest, 1, &ssb, 1, k);
nb -= k;
if (nb == 1)
goto CopyA;
@@ -1605,7 +1701,7 @@
if (nb == 0)
goto Succeed;
}
- *dest-- = *pa--;
+ sortslice_copy_decr(&dest, &ssa);
--na;
if (na == 0)
goto Succeed;
@@ -1617,15 +1713,15 @@
result = 0;
Fail:
if (nb)
- memcpy(dest-(nb-1), baseb, nb * sizeof(PyObject*));
+ sortslice_memcpy(&dest, -(nb-1), &baseb, 0, nb);
return result;
CopyA:
assert(nb == 1 && na > 0);
- /* The first element of pb belongs at the front of the merge. */
- dest -= na;
- pa -= na;
- memmove(dest+1, pa+1, na * sizeof(PyObject *));
- *dest = *pb;
+ /* The first element of ssb belongs at the front of the merge. */
+ sortslice_memmove(&dest, 1-na, &ssa, 1-na, na);
+ sortslice_advance(&dest, -na);
+ sortslice_advance(&ssa, -na);
+ sortslice_copy(&dest, 0, &ssb, 0);
return 0;
}
@@ -1635,7 +1731,7 @@
static Py_ssize_t
merge_at(MergeState *ms, Py_ssize_t i)
{
- PyObject **pa, **pb;
+ sortslice ssa, ssb;
Py_ssize_t na, nb;
Py_ssize_t k;
@@ -1644,12 +1740,12 @@
assert(i >= 0);
assert(i == ms->n - 2 || i == ms->n - 3);
- pa = ms->pending[i].base;
+ ssa = ms->pending[i].base;
na = ms->pending[i].len;
- pb = ms->pending[i+1].base;
+ ssb = ms->pending[i+1].base;
nb = ms->pending[i+1].len;
assert(na > 0 && nb > 0);
- assert(pa + na == pb);
+ assert(ssa.keys + na == ssb.keys);
/* Record the length of the combined runs; if i is the 3rd-last
* run now, also slide over the last run (which isn't involved
@@ -1663,10 +1759,10 @@
/* Where does b start in a? Elements in a before that can be
* ignored (already in place).
*/
- k = gallop_right(*pb, pa, na, 0);
+ k = gallop_right(*ssb.keys, ssa.keys, na, 0);
if (k < 0)
return -1;
- pa += k;
+ sortslice_advance(&ssa, k);
na -= k;
if (na == 0)
return 0;
@@ -1674,7 +1770,7 @@
/* Where does a end in b? Elements in b after that can be
* ignored (already in place).
*/
- nb = gallop_left(pa[na-1], pb, nb, nb-1);
+ nb = gallop_left(ssa.keys[na-1], ssb.keys, nb, nb-1);
if (nb <= 0)
return nb;
@@ -1682,9 +1778,9 @@
* min(na, nb) elements.
*/
if (na <= nb)
- return merge_lo(ms, pa, na, pb, nb);
+ return merge_lo(ms, ssa, na, ssb, nb);
else
- return merge_hi(ms, pa, na, pb, nb);
+ return merge_hi(ms, ssa, na, ssb, nb);
}
/* Examine the stack of runs waiting to be merged, merging adjacent runs
@@ -1765,103 +1861,12 @@
return n + r;
}
-/* Special wrapper to support stable sorting using the decorate-sort-undecorate
- pattern. Holds a key which is used for comparisons and the original record
- which is returned during the undecorate phase. By exposing only the key
- during comparisons, the underlying sort stability characteristics are left
- unchanged. Also, the comparison function will only see the key instead of
- a full record. */
-
-typedef struct {
- PyObject_HEAD
- PyObject *key;
- PyObject *value;
-} sortwrapperobject;
-
-PyDoc_STRVAR(sortwrapper_doc, "Object wrapper with a custom sort key.");
-static PyObject *
-sortwrapper_richcompare(sortwrapperobject *, sortwrapperobject *, int);
static void
-sortwrapper_dealloc(sortwrapperobject *);
-
-PyTypeObject PySortWrapper_Type = {
- PyVarObject_HEAD_INIT(&PyType_Type, 0)
- "sortwrapper", /* tp_name */
- sizeof(sortwrapperobject), /* tp_basicsize */
- 0, /* tp_itemsize */
- /* methods */
- (destructor)sortwrapper_dealloc, /* tp_dealloc */
- 0, /* tp_print */
- 0, /* tp_getattr */
- 0, /* tp_setattr */
- 0, /* tp_reserved */
- 0, /* tp_repr */
- 0, /* tp_as_number */
- 0, /* tp_as_sequence */
- 0, /* tp_as_mapping */
- 0, /* tp_hash */
- 0, /* tp_call */
- 0, /* tp_str */
- PyObject_GenericGetAttr, /* tp_getattro */
- 0, /* tp_setattro */
- 0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT, /* tp_flags */
- sortwrapper_doc, /* tp_doc */
- 0, /* tp_traverse */
- 0, /* tp_clear */
- (richcmpfunc)sortwrapper_richcompare, /* tp_richcompare */
-};
-
-
-static PyObject *
-sortwrapper_richcompare(sortwrapperobject *a, sortwrapperobject *b, int op)
+reverse_sortslice(sortslice *s, Py_ssize_t n)
{
- if (!PyObject_TypeCheck(b, &PySortWrapper_Type)) {
- PyErr_SetString(PyExc_TypeError,
- "expected a sortwrapperobject");
- return NULL;
- }
- return PyObject_RichCompare(a->key, b->key, op);
-}
-
-static void
-sortwrapper_dealloc(sortwrapperobject *so)
-{
- Py_XDECREF(so->key);
- Py_XDECREF(so->value);
- PyObject_Del(so);
-}
-
-/* Returns a new reference to a sortwrapper.
- Consumes the references to the two underlying objects. */
-
-static PyObject *
-build_sortwrapper(PyObject *key, PyObject *value)
-{
- sortwrapperobject *so;
-
- so = PyObject_New(sortwrapperobject, &PySortWrapper_Type);
- if (so == NULL)
- return NULL;
- so->key = key;
- so->value = value;
- return (PyObject *)so;
-}
-
-/* Returns a new reference to the value underlying the wrapper. */
-static PyObject *
-sortwrapper_getvalue(PyObject *so)
-{
- PyObject *value;
-
- if (!PyObject_TypeCheck(so, &PySortWrapper_Type)) {
- PyErr_SetString(PyExc_TypeError,
- "expected a sortwrapperobject");
- return NULL;
- }
- value = ((sortwrapperobject *)so)->value;
- Py_INCREF(value);
- return value;
+ reverse_slice(s->keys, &s->keys[n]);
+ if (s->values != NULL)
+ reverse_slice(s->values, &s->values[n]);
}
/* An adaptive, stable, natural mergesort. See listsort.txt.
@@ -1873,9 +1878,9 @@
listsort(PyListObject *self, PyObject *args, PyObject *kwds)
{
MergeState ms;
- PyObject **lo, **hi;
Py_ssize_t nremaining;
Py_ssize_t minrun;
+ sortslice lo;
Py_ssize_t saved_ob_size, saved_allocated;
PyObject **saved_ob_item;
PyObject **final_ob_item;
@@ -1883,8 +1888,8 @@
int reverse = 0;
PyObject *keyfunc = NULL;
Py_ssize_t i;
- PyObject *key, *value, *kvpair;
static char *kwlist[] = {"key", "reverse", 0};
+ PyObject **keys;
assert(self != NULL);
assert (PyList_Check(self));
@@ -1913,28 +1918,36 @@
self->ob_item = NULL;
self->allocated = -1; /* any operation will reset it to >= 0 */
- if (keyfunc != NULL) {
- for (i=0 ; i < saved_ob_size ; i++) {
- value = saved_ob_item[i];
- key = PyObject_CallFunctionObjArgs(keyfunc, value,
- NULL);
- if (key == NULL) {
- for (i=i-1 ; i>=0 ; i--) {
- kvpair = saved_ob_item[i];
- value = sortwrapper_getvalue(kvpair);
- saved_ob_item[i] = value;
- Py_DECREF(kvpair);
- }
- goto dsu_fail;
+ if (keyfunc == NULL) {
+ keys = NULL;
+ lo.keys = saved_ob_item;
+ lo.values = NULL;
+ }
+ else {
+ if (saved_ob_size < MERGESTATE_TEMP_SIZE/2)
+ /* Leverage stack space we allocated but won't otherwise use */
+ keys = &ms.temparray[saved_ob_size+1];
+ else {
+ keys = PyMem_MALLOC(sizeof(PyObject *) * saved_ob_size);
+ if (keys == NULL)
+ return NULL;
+ }
+
+ for (i = 0; i < saved_ob_size ; i++) {
+ keys[i] = PyObject_CallFunctionObjArgs(keyfunc, saved_ob_item[i],
+ NULL);
+ if (keys[i] == NULL) {
+ for (i=i-1 ; i>=0 ; i--)
+ Py_DECREF(keys[i]);
+ goto keyfunc_fail;
}
- kvpair = build_sortwrapper(key, value);
- if (kvpair == NULL)
- goto dsu_fail;
- saved_ob_item[i] = kvpair;
}
+
+ lo.keys = keys;
+ lo.values = saved_ob_item;
}
- merge_init(&ms);
+ merge_init(&ms, saved_ob_size, keys != NULL);
nremaining = saved_ob_size;
if (nremaining < 2)
@@ -1942,30 +1955,31 @@
/* Reverse sort stability achieved by initially reversing the list,
applying a stable forward sort, then reversing the final result. */
- if (reverse)
- reverse_slice(saved_ob_item, saved_ob_item + saved_ob_size);
+ if (reverse) {
+ if (keys != NULL)
+ reverse_slice(&keys[0], &keys[saved_ob_size]);
+ reverse_slice(&saved_ob_item[0], &saved_ob_item[saved_ob_size]);
+ }
/* March over the array once, left to right, finding natural runs,
* and extending short natural runs to minrun elements.
*/
- lo = saved_ob_item;
- hi = lo + nremaining;
minrun = merge_compute_minrun(nremaining);
do {
int descending;
Py_ssize_t n;
/* Identify next run. */
- n = count_run(lo, hi, &descending);
+ n = count_run(lo.keys, lo.keys + nremaining, &descending);
if (n < 0)
goto fail;
if (descending)
- reverse_slice(lo, lo + n);
+ reverse_sortslice(&lo, n);
/* If short, extend to min(minrun, nremaining). */
if (n < minrun) {
const Py_ssize_t force = nremaining <= minrun ?
nremaining : minrun;
- if (binarysort(lo, lo + force, lo + n) < 0)
+ if (binarysort(lo, lo.keys + force, lo.keys + n) < 0)
goto fail;
n = force;
}
@@ -1977,27 +1991,27 @@
if (merge_collapse(&ms) < 0)
goto fail;
/* Advance to find next run. */
- lo += n;
+ sortslice_advance(&lo, n);
nremaining -= n;
} while (nremaining);
- assert(lo == hi);
if (merge_force_collapse(&ms) < 0)
goto fail;
assert(ms.n == 1);
- assert(ms.pending[0].base == saved_ob_item);
+ assert(keys == NULL
+ ? ms.pending[0].base.keys == saved_ob_item
+ : ms.pending[0].base.keys == &keys[0]);
assert(ms.pending[0].len == saved_ob_size);
+ lo = ms.pending[0].base;
succeed:
result = Py_None;
fail:
- if (keyfunc != NULL) {
- for (i=0 ; i < saved_ob_size ; i++) {
- kvpair = saved_ob_item[i];
- value = sortwrapper_getvalue(kvpair);
- saved_ob_item[i] = value;
- Py_DECREF(kvpair);
- }
+ if (keys != NULL) {
+ for (i = 0; i < saved_ob_size; i++)
+ Py_DECREF(keys[i]);
+ if (keys != &ms.temparray[saved_ob_size+1])
+ PyMem_FREE(keys);
}
if (self->allocated != -1 && result != NULL) {
@@ -2013,7 +2027,7 @@
merge_freemem(&ms);
-dsu_fail:
+keyfunc_fail:
final_ob_item = self->ob_item;
i = Py_SIZE(self);
Py_SIZE(self) = saved_ob_size;
@@ -2862,4 +2876,3 @@
len = 0;
return PyLong_FromSsize_t(len);
}
-
From python-checkins at python.org Thu Dec 2 22:55:58 2010
From: python-checkins at python.org (r.david.murray)
Date: Thu, 2 Dec 2010 22:55:58 +0100 (CET)
Subject: [Python-checkins] r86938 - python/branches/release31-maint
Message-ID: <20101202215558.1DB9CEE9CD@mail.python.org>
Author: r.david.murray
Date: Thu Dec 2 22:55:57 2010
New Revision: 86938
Log:
Blocked revisions 86936 via svnmerge
........
r86936 | r.david.murray | 2010-12-02 16:47:19 -0500 (Thu, 02 Dec 2010) | 4 lines
#8989: add 'domain' keyword to make_msgid.
Patch by Adrian von Bidder.
........
Modified:
python/branches/release31-maint/ (props changed)
From python-checkins at python.org Thu Dec 2 22:56:45 2010
From: python-checkins at python.org (r.david.murray)
Date: Thu, 2 Dec 2010 22:56:45 +0100 (CET)
Subject: [Python-checkins] r86939 - python/branches/release27-maint
Message-ID: <20101202215645.89454EE9B7@mail.python.org>
Author: r.david.murray
Date: Thu Dec 2 22:56:45 2010
New Revision: 86939
Log:
Blocked revisions 86936 via svnmerge
........
r86936 | r.david.murray | 2010-12-02 16:47:19 -0500 (Thu, 02 Dec 2010) | 4 lines
#8989: add 'domain' keyword to make_msgid.
Patch by Adrian von Bidder.
........
Modified:
python/branches/release27-maint/ (props changed)
From python-checkins at python.org Thu Dec 2 23:16:20 2010
From: python-checkins at python.org (eric.araujo)
Date: Thu, 2 Dec 2010 23:16:20 +0100 (CET)
Subject: [Python-checkins] r86940 - python/branches/py3k/Lib/test/test_csv.py
Message-ID: <20101202221620.01FD2EE9D9@mail.python.org>
Author: eric.araujo
Date: Thu Dec 2 23:16:19 2010
New Revision: 86940
Log:
Fix wrong test code in test_csv (#10602)
Modified:
python/branches/py3k/Lib/test/test_csv.py
Modified: python/branches/py3k/Lib/test/test_csv.py
==============================================================================
--- python/branches/py3k/Lib/test/test_csv.py (original)
+++ python/branches/py3k/Lib/test/test_csv.py Thu Dec 2 23:16:19 2010
@@ -313,22 +313,17 @@
expected_dialects = csv.list_dialects() + [name]
expected_dialects.sort()
csv.register_dialect(name, myexceltsv)
- try:
- self.assertTrue(csv.get_dialect(name).delimiter, '\t')
- got_dialects = csv.list_dialects()
- got_dialects.sort()
- self.assertEqual(expected_dialects, got_dialects)
- finally:
- csv.unregister_dialect(name)
+ self.addCleanup(csv.unregister_dialect, name)
+ self.assertEqual(csv.get_dialect(name).delimiter, '\t')
+ got_dialects = sorted(csv.list_dialects())
+ self.assertEqual(expected_dialects, got_dialects)
def test_register_kwargs(self):
name = 'fedcba'
csv.register_dialect(name, delimiter=';')
- try:
- self.assertTrue(csv.get_dialect(name).delimiter, '\t')
- self.assertTrue(list(csv.reader('X;Y;Z', name)), ['X', 'Y', 'Z'])
- finally:
- csv.unregister_dialect(name)
+ self.addCleanup(csv.unregister_dialect, name)
+ self.assertEqual(csv.get_dialect(name).delimiter, ';')
+ self.assertEqual([['X', 'Y', 'Z']], list(csv.reader(['X;Y;Z'], name)))
def test_incomplete_dialect(self):
class myexceltsv(csv.Dialect):
From python-checkins at python.org Thu Dec 2 23:29:59 2010
From: python-checkins at python.org (eric.araujo)
Date: Thu, 2 Dec 2010 23:29:59 +0100 (CET)
Subject: [Python-checkins] r86941 - in python/branches/release31-maint:
Lib/test/test_csv.py
Message-ID: <20101202222959.59616EE9E0@mail.python.org>
Author: eric.araujo
Date: Thu Dec 2 23:29:59 2010
New Revision: 86941
Log:
Merged revisions 86940 via svnmerge from
svn+ssh://pythondev at svn.python.org/python/branches/py3k
........
r86940 | eric.araujo | 2010-12-02 23:16:19 +0100 (jeu., 02 d?c. 2010) | 2 lines
Fix wrong test code in test_csv (#10602)
........
Modified:
python/branches/release31-maint/ (props changed)
python/branches/release31-maint/Lib/test/test_csv.py
Modified: python/branches/release31-maint/Lib/test/test_csv.py
==============================================================================
--- python/branches/release31-maint/Lib/test/test_csv.py (original)
+++ python/branches/release31-maint/Lib/test/test_csv.py Thu Dec 2 23:29:59 2010
@@ -314,22 +314,17 @@
expected_dialects = csv.list_dialects() + [name]
expected_dialects.sort()
csv.register_dialect(name, myexceltsv)
- try:
- self.assertTrue(csv.get_dialect(name).delimiter, '\t')
- got_dialects = csv.list_dialects()
- got_dialects.sort()
- self.assertEqual(expected_dialects, got_dialects)
- finally:
- csv.unregister_dialect(name)
+ self.addCleanup(csv.unregister_dialect, name)
+ self.assertEqual(csv.get_dialect(name).delimiter, '\t')
+ got_dialects = sorted(csv.list_dialects())
+ self.assertEqual(expected_dialects, got_dialects)
def test_register_kwargs(self):
name = 'fedcba'
csv.register_dialect(name, delimiter=';')
- try:
- self.assertTrue(csv.get_dialect(name).delimiter, '\t')
- self.assertTrue(list(csv.reader('X;Y;Z', name)), ['X', 'Y', 'Z'])
- finally:
- csv.unregister_dialect(name)
+ self.addCleanup(csv.unregister_dialect, name)
+ self.assertEqual(csv.get_dialect(name).delimiter, ';')
+ self.assertEqual([['X', 'Y', 'Z']], list(csv.reader(['X;Y;Z'], name)))
def test_incomplete_dialect(self):
class myexceltsv(csv.Dialect):
From python-checkins at python.org Thu Dec 2 23:35:10 2010
From: python-checkins at python.org (eric.araujo)
Date: Thu, 2 Dec 2010 23:35:10 +0100 (CET)
Subject: [Python-checkins] r86942 - in python/branches/release27-maint:
Lib/test/test_csv.py
Message-ID: <20101202223510.619F7EE9B1@mail.python.org>
Author: eric.araujo
Date: Thu Dec 2 23:35:10 2010
New Revision: 86942
Log:
Merged revisions 86940 via svnmerge from
svn+ssh://pythondev at svn.python.org/python/branches/py3k
........
r86940 | eric.araujo | 2010-12-02 23:16:19 +0100 (jeu., 02 d?c. 2010) | 2 lines
Fix wrong test code in test_csv (#10602)
........
Modified:
python/branches/release27-maint/ (props changed)
python/branches/release27-maint/Lib/test/test_csv.py
Modified: python/branches/release27-maint/Lib/test/test_csv.py
==============================================================================
--- python/branches/release27-maint/Lib/test/test_csv.py (original)
+++ python/branches/release27-maint/Lib/test/test_csv.py Thu Dec 2 23:35:10 2010
@@ -326,22 +326,17 @@
expected_dialects = csv.list_dialects() + [name]
expected_dialects.sort()
csv.register_dialect(name, myexceltsv)
- try:
- self.assertTrue(csv.get_dialect(name).delimiter, '\t')
- got_dialects = csv.list_dialects()
- got_dialects.sort()
- self.assertEqual(expected_dialects, got_dialects)
- finally:
- csv.unregister_dialect(name)
+ self.addCleanup(csv.unregister_dialect, name)
+ self.assertEqual(csv.get_dialect(name).delimiter, '\t')
+ got_dialects = sorted(csv.list_dialects())
+ self.assertEqual(expected_dialects, got_dialects)
def test_register_kwargs(self):
name = 'fedcba'
csv.register_dialect(name, delimiter=';')
- try:
- self.assertTrue(csv.get_dialect(name).delimiter, '\t')
- self.assertTrue(list(csv.reader('X;Y;Z', name)), ['X', 'Y', 'Z'])
- finally:
- csv.unregister_dialect(name)
+ self.addCleanup(csv.unregister_dialect, name)
+ self.assertEqual(csv.get_dialect(name).delimiter, ';')
+ self.assertEqual([['X', 'Y', 'Z']], list(csv.reader(['X;Y;Z'], name)))
def test_incomplete_dialect(self):
class myexceltsv(csv.Dialect):
From python-checkins at python.org Thu Dec 2 23:35:25 2010
From: python-checkins at python.org (georg.brandl)
Date: Thu, 2 Dec 2010 23:35:25 +0100 (CET)
Subject: [Python-checkins] r86943 -
python/branches/py3k/Doc/library/unittest.rst
Message-ID: <20101202223525.DACD3EE99E@mail.python.org>
Author: georg.brandl
Date: Thu Dec 2 23:35:25 2010
New Revision: 86943
Log:
Re-add accidentally removed line.
Modified:
python/branches/py3k/Doc/library/unittest.rst
Modified: python/branches/py3k/Doc/library/unittest.rst
==============================================================================
--- python/branches/py3k/Doc/library/unittest.rst (original)
+++ python/branches/py3k/Doc/library/unittest.rst Thu Dec 2 23:35:25 2010
@@ -1719,6 +1719,7 @@
The total number of tests run so far.
+ .. attribute:: buffer
If set to true, ``sys.stdout`` and ``sys.stderr`` will be buffered in between
:meth:`startTest` and :meth:`stopTest` being called. Collected output will
From python-checkins at python.org Fri Dec 3 01:53:09 2010
From: python-checkins at python.org (michael.foord)
Date: Fri, 3 Dec 2010 01:53:09 +0100 (CET)
Subject: [Python-checkins] r86944 - in python/branches/py3k:
Doc/library/unittest.rst Lib/unittest/case.py
Lib/unittest/test/test_assertions.py Misc/NEWS
Message-ID: <20101203005309.F1423F414@mail.python.org>
Author: michael.foord
Date: Fri Dec 3 01:53:09 2010
New Revision: 86944
Log:
Issue 7911: unittest.TestCase.longMessage defaults to True for improved failure messages by default
Modified:
python/branches/py3k/Doc/library/unittest.rst
python/branches/py3k/Lib/unittest/case.py
python/branches/py3k/Lib/unittest/test/test_assertions.py
python/branches/py3k/Misc/NEWS
Modified: python/branches/py3k/Doc/library/unittest.rst
==============================================================================
--- python/branches/py3k/Doc/library/unittest.rst (original)
+++ python/branches/py3k/Doc/library/unittest.rst Fri Dec 3 01:53:09 2010
@@ -1305,8 +1305,8 @@
to ``True`` allows you to have a custom error message in addition to the
normal one.
- This attribute defaults to ``False``, meaning that a custom message passed
- to an assert method will silence the normal message.
+ This attribute defaults to ``True``. If set to False then a custom message
+ passed to an assert method will silence the normal message.
The class setting can be overridden in individual tests by assigning an
instance attribute to ``True`` or ``False`` before calling the assert methods.
Modified: python/branches/py3k/Lib/unittest/case.py
==============================================================================
--- python/branches/py3k/Lib/unittest/case.py (original)
+++ python/branches/py3k/Lib/unittest/case.py Fri Dec 3 01:53:09 2010
@@ -245,7 +245,7 @@
# objects used in assert methods) will be printed on failure in *addition*
# to any explicit message passed.
- longMessage = False
+ longMessage = True
# This attribute sets the maximum length of a diff in failure messages
# by assert methods using difflib. It is looked up as an instance attribute
Modified: python/branches/py3k/Lib/unittest/test/test_assertions.py
==============================================================================
--- python/branches/py3k/Lib/unittest/test/test_assertions.py (original)
+++ python/branches/py3k/Lib/unittest/test/test_assertions.py Fri Dec 3 01:53:09 2010
@@ -127,7 +127,7 @@
self.testableFalse = TestableTestFalse('testTest')
def testDefault(self):
- self.assertFalse(unittest.TestCase.longMessage)
+ self.assertTrue(unittest.TestCase.longMessage)
def test_formatMsg(self):
self.assertEqual(self.testableFalse._formatMessage(None, "foo"), "foo")
Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS (original)
+++ python/branches/py3k/Misc/NEWS Fri Dec 3 01:53:09 2010
@@ -10,6 +10,9 @@
Core and Builtins
-----------------
+- Issue 7911: `unittest.TestCase.longMessage` defaults to True for improved
+ failure messages by default. Patch by Mark Roddy.
+
- Issue #9915: Speed up sorting with a key.
- Issue #9333: Expose os.symlink only when the SeCreateSymbolicLinkPrivilege
From python-checkins at python.org Fri Dec 3 02:34:01 2010
From: python-checkins at python.org (michael.foord)
Date: Fri, 3 Dec 2010 02:34:01 +0100 (CET)
Subject: [Python-checkins] r86945 - python/branches/py3k/Lib/test/__main__.py
Message-ID: <20101203013401.A454BEE981@mail.python.org>
Author: michael.foord
Date: Fri Dec 3 02:34:01 2010
New Revision: 86945
Log:
Initial implementation of Lib/test/__main__.py so we can run tests with 'python -m test'
Added:
python/branches/py3k/Lib/test/__main__.py (contents, props changed)
Added: python/branches/py3k/Lib/test/__main__.py
==============================================================================
--- (empty file)
+++ python/branches/py3k/Lib/test/__main__.py Fri Dec 3 02:34:01 2010
@@ -0,0 +1,38 @@
+import os
+import sys
+import sysconfig
+
+from test import support
+from test.regrtest import main
+
+# findtestdir() gets the dirname out of __file__, so we have to make it
+# absolute before changing the working directory.
+# For example __file__ may be relative when running trace or profile.
+# See issue #9323.
+__file__ = os.path.abspath(__file__)
+
+# sanity check
+assert __file__ == os.path.abspath(sys.argv[0])
+
+# When tests are run from the Python build directory, it is best practice
+# to keep the test files in a subfolder. It eases the cleanup of leftover
+# files using command "make distclean".
+if sysconfig.is_python_build():
+ TEMPDIR = os.path.join(sysconfig.get_config_var('srcdir'), 'build')
+ TEMPDIR = os.path.abspath(TEMPDIR)
+ if not os.path.exists(TEMPDIR):
+ os.mkdir(TEMPDIR)
+
+# Define a writable temp dir that will be used as cwd while running
+# the tests. The name of the dir includes the pid to allow parallel
+# testing (see the -j option).
+TESTCWD = 'test_python_{}'.format(os.getpid())
+
+TESTCWD = os.path.join(TEMPDIR, TESTCWD)
+
+# Run the tests in a context manager that temporary changes the CWD to a
+# temporary and writable directory. If it's not possible to create or
+# change the CWD, the original CWD will be used. The original CWD is
+# available from support.SAVEDCWD.
+with support.temp_cwd(TESTCWD, quiet=True):
+ main()
From python-checkins at python.org Fri Dec 3 02:44:16 2010
From: python-checkins at python.org (benjamin.peterson)
Date: Fri, 3 Dec 2010 02:44:16 +0100 (CET)
Subject: [Python-checkins] r86946 - python/branches/py3k/Objects/listobject.c
Message-ID: <20101203014416.67837EE9AC@mail.python.org>
Author: benjamin.peterson
Date: Fri Dec 3 02:44:10 2010
New Revision: 86946
Log:
code style
Modified:
python/branches/py3k/Objects/listobject.c
Modified: python/branches/py3k/Objects/listobject.c
==============================================================================
--- python/branches/py3k/Objects/listobject.c (original)
+++ python/branches/py3k/Objects/listobject.c Fri Dec 3 02:44:10 2010
@@ -963,14 +963,16 @@
}
Py_LOCAL_INLINE(void)
-sortslice_copy_incr(sortslice *dst, sortslice *src) {
+sortslice_copy_incr(sortslice *dst, sortslice *src)
+{
*dst->keys++ = *src->keys++;
if (dst->values != NULL)
*dst->values++ = *src->values++;
}
Py_LOCAL_INLINE(void)
-sortslice_copy_decr(sortslice *dst, sortslice *src) {
+sortslice_copy_decr(sortslice *dst, sortslice *src)
+{
*dst->keys-- = *src->keys--;
if (dst->values != NULL)
*dst->values-- = *src->values--;
@@ -979,7 +981,8 @@
Py_LOCAL_INLINE(void)
sortslice_memcpy(sortslice *s1, Py_ssize_t i, sortslice *s2, Py_ssize_t j,
- Py_ssize_t n) {
+ Py_ssize_t n)
+{
memcpy(&s1->keys[i], &s2->keys[j], sizeof(PyObject *) * n);
if (s1->values != NULL)
memcpy(&s1->values[i], &s2->values[j], sizeof(PyObject *) * n);
@@ -987,14 +990,16 @@
Py_LOCAL_INLINE(void)
sortslice_memmove(sortslice *s1, Py_ssize_t i, sortslice *s2, Py_ssize_t j,
- Py_ssize_t n) {
+ Py_ssize_t n)
+{
memmove(&s1->keys[i], &s2->keys[j], sizeof(PyObject *) * n);
if (s1->values != NULL)
memmove(&s1->values[i], &s2->values[j], sizeof(PyObject *) * n);
}
Py_LOCAL_INLINE(void)
-sortslice_advance(sortslice *slice, Py_ssize_t n) {
+sortslice_advance(sortslice *slice, Py_ssize_t n)
+{
slice->keys += n;
if (slice->values != NULL)
slice->values += n;
From python-checkins at python.org Fri Dec 3 03:03:30 2010
From: python-checkins at python.org (michael.foord)
Date: Fri, 3 Dec 2010 03:03:30 +0100 (CET)
Subject: [Python-checkins] r86947 - python/branches/py3k/Lib/test/__main__.py
Message-ID: <20101203020330.E7A85EE983@mail.python.org>
Author: michael.foord
Date: Fri Dec 3 03:03:30 2010
New Revision: 86947
Log:
Set test.regrtest.TEMPDIR correctly when run with 'python -m test'
Modified:
python/branches/py3k/Lib/test/__main__.py
Modified: python/branches/py3k/Lib/test/__main__.py
==============================================================================
--- python/branches/py3k/Lib/test/__main__.py (original)
+++ python/branches/py3k/Lib/test/__main__.py Fri Dec 3 03:03:30 2010
@@ -3,7 +3,7 @@
import sysconfig
from test import support
-from test.regrtest import main
+from test import regrtest
# findtestdir() gets the dirname out of __file__, so we have to make it
# absolute before changing the working directory.
@@ -22,6 +22,7 @@
TEMPDIR = os.path.abspath(TEMPDIR)
if not os.path.exists(TEMPDIR):
os.mkdir(TEMPDIR)
+ regrtest.TEMPDIR = TEMPDIR
# Define a writable temp dir that will be used as cwd while running
# the tests. The name of the dir includes the pid to allow parallel
@@ -29,10 +30,11 @@
TESTCWD = 'test_python_{}'.format(os.getpid())
TESTCWD = os.path.join(TEMPDIR, TESTCWD)
+regrtest.TESTCWD = TESTCWD
# Run the tests in a context manager that temporary changes the CWD to a
# temporary and writable directory. If it's not possible to create or
# change the CWD, the original CWD will be used. The original CWD is
# available from support.SAVEDCWD.
with support.temp_cwd(TESTCWD, quiet=True):
- main()
+ regrtest.main()
From python-checkins at python.org Fri Dec 3 03:09:34 2010
From: python-checkins at python.org (raymond.hettinger)
Date: Fri, 3 Dec 2010 03:09:34 +0100 (CET)
Subject: [Python-checkins] r86948 - in python/branches/py3k:
Doc/library/itertools.rst Lib/test/test_itertools.py
Modules/itertoolsmodule.c
Message-ID: <20101203020934.70CCAEE98C@mail.python.org>
Author: raymond.hettinger
Date: Fri Dec 3 03:09:34 2010
New Revision: 86948
Log:
Simplify the signature for itertools.accumulate() to match numpy. Handle one item iterable the same way as min()/max().
Modified:
python/branches/py3k/Doc/library/itertools.rst
python/branches/py3k/Lib/test/test_itertools.py
python/branches/py3k/Modules/itertoolsmodule.c
Modified: python/branches/py3k/Doc/library/itertools.rst
==============================================================================
--- python/branches/py3k/Doc/library/itertools.rst (original)
+++ python/branches/py3k/Doc/library/itertools.rst Fri Dec 3 03:09:34 2010
@@ -90,13 +90,15 @@
parameter (which defaults to :const:`0`). Elements may be any addable type
including :class:`Decimal` or :class:`Fraction`. Equivalent to::
- def accumulate(iterable, start=0):
+ def accumulate(iterable):
'Return running totals'
- # accumulate([1,2,3,4,5]) --> 1 3 6 10 15
- total = start
- for element in iterable:
- total += element
- yield total
+ # accumulate([1,2,3,4,5]) --> 1 3 6 10 15
+ it = iter(iterable)
+ total = next(it)
+ yield total
+ for element in it:
+ total += element
+ yield total
.. versionadded:: 3.2
Modified: python/branches/py3k/Lib/test/test_itertools.py
==============================================================================
--- python/branches/py3k/Lib/test/test_itertools.py (original)
+++ python/branches/py3k/Lib/test/test_itertools.py Fri Dec 3 03:09:34 2010
@@ -59,18 +59,18 @@
def test_accumulate(self):
self.assertEqual(list(accumulate(range(10))), # one positional arg
- [0, 1, 3, 6, 10, 15, 21, 28, 36, 45])
- self.assertEqual(list(accumulate(range(10), 100)), # two positional args
- [100, 101, 103, 106, 110, 115, 121, 128, 136, 145])
- self.assertEqual(list(accumulate(iterable=range(10), start=100)), # kw args
- [100, 101, 103, 106, 110, 115, 121, 128, 136, 145])
+ [0, 1, 3, 6, 10, 15, 21, 28, 36, 45])
+ self.assertEqual(list(accumulate(iterable=range(10))), # kw arg
+ [0, 1, 3, 6, 10, 15, 21, 28, 36, 45])
for typ in int, complex, Decimal, Fraction: # multiple types
- self.assertEqual(list(accumulate(range(10), typ(0))),
+ self.assertEqual(
+ list(accumulate(map(typ, range(10)))),
list(map(typ, [0, 1, 3, 6, 10, 15, 21, 28, 36, 45])))
self.assertEqual(list(accumulate([])), []) # empty iterable
- self.assertRaises(TypeError, accumulate, range(10), 0, 5) # too many args
+ self.assertEqual(list(accumulate([7])), [7]) # iterable of length one
+ self.assertRaises(TypeError, accumulate, range(10), 5) # too many args
self.assertRaises(TypeError, accumulate) # too few args
- self.assertRaises(TypeError, accumulate, range(10), x=7) # unexpected kwd args
+ self.assertRaises(TypeError, accumulate, x=range(10)) # unexpected kwd arg
self.assertRaises(TypeError, list, accumulate([1, []])) # args that don't add
def test_chain(self):
Modified: python/branches/py3k/Modules/itertoolsmodule.c
==============================================================================
--- python/branches/py3k/Modules/itertoolsmodule.c (original)
+++ python/branches/py3k/Modules/itertoolsmodule.c Fri Dec 3 03:09:34 2010
@@ -2597,41 +2597,27 @@
static PyObject *
accumulate_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
- static char *kwargs[] = {"iterable", "start", NULL};
+ static char *kwargs[] = {"iterable", NULL};
PyObject *iterable;
PyObject *it;
- PyObject *start = NULL;
accumulateobject *lz;
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:accumulate",
- kwargs, &iterable, &start))
- return NULL;
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:accumulate", kwargs, &iterable))
+ return NULL;
/* Get iterator. */
it = PyObject_GetIter(iterable);
if (it == NULL)
return NULL;
- /* Default start value */
- if (start == NULL) {
- start = PyLong_FromLong(0);
- if (start == NULL) {
- Py_DECREF(it);
- return NULL;
- }
- } else {
- Py_INCREF(start);
- }
-
/* create accumulateobject structure */
lz = (accumulateobject *)type->tp_alloc(type, 0);
if (lz == NULL) {
Py_DECREF(it);
- Py_DECREF(start);
- return NULL;
+ return NULL;
}
- lz->total = start;
+ lz->total = NULL;
lz->it = it;
return (PyObject *)lz;
}
@@ -2661,11 +2647,17 @@
val = PyIter_Next(lz->it);
if (val == NULL)
return NULL;
-
+
+ if (lz->total == NULL) {
+ Py_INCREF(val);
+ lz->total = val;
+ return lz->total;
+ }
+
newtotal = PyNumber_Add(lz->total, val);
- Py_DECREF(val);
+ Py_DECREF(val);
if (newtotal == NULL)
- return NULL;
+ return NULL;
oldtotal = lz->total;
lz->total = newtotal;
@@ -2676,7 +2668,7 @@
}
PyDoc_STRVAR(accumulate_doc,
-"accumulate(iterable, start=0) --> accumulate object\n\
+"accumulate(iterable) --> accumulate object\n\
\n\
Return series of accumulated sums.");
From python-checkins at python.org Fri Dec 3 03:27:45 2010
From: python-checkins at python.org (michael.foord)
Date: Fri, 3 Dec 2010 03:27:45 +0100 (CET)
Subject: [Python-checkins] r86949 - python/branches/py3k/Lib/test/__main__.py
Message-ID: <20101203022745.08CE1EE989@mail.python.org>
Author: michael.foord
Date: Fri Dec 3 03:27:44 2010
New Revision: 86949
Log:
Remove test/__main__.py until runpy tests can be fixed
Removed:
python/branches/py3k/Lib/test/__main__.py
Deleted: python/branches/py3k/Lib/test/__main__.py
==============================================================================
--- python/branches/py3k/Lib/test/__main__.py Fri Dec 3 03:27:44 2010
+++ (empty file)
@@ -1,40 +0,0 @@
-import os
-import sys
-import sysconfig
-
-from test import support
-from test import regrtest
-
-# findtestdir() gets the dirname out of __file__, so we have to make it
-# absolute before changing the working directory.
-# For example __file__ may be relative when running trace or profile.
-# See issue #9323.
-__file__ = os.path.abspath(__file__)
-
-# sanity check
-assert __file__ == os.path.abspath(sys.argv[0])
-
-# When tests are run from the Python build directory, it is best practice
-# to keep the test files in a subfolder. It eases the cleanup of leftover
-# files using command "make distclean".
-if sysconfig.is_python_build():
- TEMPDIR = os.path.join(sysconfig.get_config_var('srcdir'), 'build')
- TEMPDIR = os.path.abspath(TEMPDIR)
- if not os.path.exists(TEMPDIR):
- os.mkdir(TEMPDIR)
- regrtest.TEMPDIR = TEMPDIR
-
-# Define a writable temp dir that will be used as cwd while running
-# the tests. The name of the dir includes the pid to allow parallel
-# testing (see the -j option).
-TESTCWD = 'test_python_{}'.format(os.getpid())
-
-TESTCWD = os.path.join(TEMPDIR, TESTCWD)
-regrtest.TESTCWD = TESTCWD
-
-# Run the tests in a context manager that temporary changes the CWD to a
-# temporary and writable directory. If it's not possible to create or
-# change the CWD, the original CWD will be used. The original CWD is
-# available from support.SAVEDCWD.
-with support.temp_cwd(TESTCWD, quiet=True):
- regrtest.main()
From python-checkins at python.org Fri Dec 3 03:33:54 2010
From: python-checkins at python.org (raymond.hettinger)
Date: Fri, 3 Dec 2010 03:33:54 +0100 (CET)
Subject: [Python-checkins] r86950 - in python/branches/py3k:
Doc/library/itertools.rst Lib/test/test_itertools.py
Message-ID: <20101203023354.1557BEE981@mail.python.org>
Author: raymond.hettinger
Date: Fri Dec 3 03:33:53 2010
New Revision: 86950
Log:
Update the itertools.accumulate() docs.
Modified:
python/branches/py3k/Doc/library/itertools.rst
python/branches/py3k/Lib/test/test_itertools.py
Modified: python/branches/py3k/Doc/library/itertools.rst
==============================================================================
--- python/branches/py3k/Doc/library/itertools.rst (original)
+++ python/branches/py3k/Doc/library/itertools.rst Fri Dec 3 03:33:53 2010
@@ -46,7 +46,7 @@
==================== ============================ ================================================= =============================================================
Iterator Arguments Results Example
==================== ============================ ================================================= =============================================================
-:func:`accumulate` p[, start=0] p0, p0+p1, p0+p1+p2, ... ``accumulate([1,2,3,4,5]) --> 1 3 6 10 15``
+:func:`accumulate` p p0, p0+p1, p0+p1+p2, ... ``accumulate([1,2,3,4,5]) --> 1 3 6 10 15``
:func:`chain` p, q, ... p0, p1, ... plast, q0, q1, ... ``chain('ABC', 'DEF') --> A B C D E F``
:func:`compress` data, selectors (d[0] if s[0]), (d[1] if s[1]), ... ``compress('ABCDEF', [1,0,1,0,1,1]) --> A C E F``
:func:`dropwhile` pred, seq seq[n], seq[n+1], starting when pred fails ``dropwhile(lambda x: x<5, [1,4,6,4,1]) --> 6 4 1``
@@ -84,11 +84,10 @@
streams of infinite length, so they should only be accessed by functions or
loops that truncate the stream.
-.. function:: accumulate(iterable, start=0)
+.. function:: accumulate(iterable)
- Make an iterator that returns accumulated sums plus the value of the *start*
- parameter (which defaults to :const:`0`). Elements may be any addable type
- including :class:`Decimal` or :class:`Fraction`. Equivalent to::
+ Make an iterator that returns accumulated sums. Elements may be any addable
+ type including :class:`Decimal` or :class:`Fraction`. Equivalent to::
def accumulate(iterable):
'Return running totals'
Modified: python/branches/py3k/Lib/test/test_itertools.py
==============================================================================
--- python/branches/py3k/Lib/test/test_itertools.py (original)
+++ python/branches/py3k/Lib/test/test_itertools.py Fri Dec 3 03:33:53 2010
@@ -66,6 +66,7 @@
self.assertEqual(
list(accumulate(map(typ, range(10)))),
list(map(typ, [0, 1, 3, 6, 10, 15, 21, 28, 36, 45])))
+ self.assertEqual(list(accumulate('abc')), ['a', 'ab', 'abc']) # works with non-numeric
self.assertEqual(list(accumulate([])), []) # empty iterable
self.assertEqual(list(accumulate([7])), [7]) # iterable of length one
self.assertRaises(TypeError, accumulate, range(10), 5) # too many args
From python-checkins at python.org Fri Dec 3 03:46:02 2010
From: python-checkins at python.org (brian.curtin)
Date: Fri, 3 Dec 2010 03:46:02 +0100 (CET)
Subject: [Python-checkins] r86951 - in python/branches/py3k:
Doc/library/subprocess.rst Lib/subprocess.py
Lib/test/test_subprocess.py Misc/NEWS
Message-ID: <20101203024602.3E450EE9BB@mail.python.org>
Author: brian.curtin
Date: Fri Dec 3 03:46:02 2010
New Revision: 86951
Log:
Fix #10554. Added context manager support to Popen objects.
Added a few common Popen uses to the tests like we've done for a few other
instances of adding context managers. Eventually the entire test suite
could be converted to use the context manager format.
Modified:
python/branches/py3k/Doc/library/subprocess.rst
python/branches/py3k/Lib/subprocess.py
python/branches/py3k/Lib/test/test_subprocess.py
python/branches/py3k/Misc/NEWS
Modified: python/branches/py3k/Doc/library/subprocess.rst
==============================================================================
--- python/branches/py3k/Doc/library/subprocess.rst (original)
+++ python/branches/py3k/Doc/library/subprocess.rst Fri Dec 3 03:46:02 2010
@@ -208,6 +208,16 @@
underlying CreateProcess() function. They can specify things such as appearance
of the main window and priority for the new process. (Windows only)
+ Popen objects are supported as context managers via the :keyword:`with` statement,
+ closing any open file descriptors on exit.
+ ::
+
+ with Popen(["ifconfig"], stdout=PIPE) as proc:
+ log.write(proc.stdout.read())
+
+ .. versionchanged:: 3.2
+ Added context manager support.
+
.. data:: PIPE
Modified: python/branches/py3k/Lib/subprocess.py
==============================================================================
--- python/branches/py3k/Lib/subprocess.py (original)
+++ python/branches/py3k/Lib/subprocess.py Fri Dec 3 03:46:02 2010
@@ -697,6 +697,16 @@
data = data.replace(b"\r\n", b"\n").replace(b"\r", b"\n")
return data.decode(encoding)
+ def __enter__(self):
+ return self
+
+ def __exit__(self, type, value, traceback):
+ if self.stdout:
+ self.stdout.close()
+ if self.stderr:
+ self.stderr.close()
+ if self.stdin:
+ self.stdin.close()
def __del__(self, _maxsize=sys.maxsize, _active=_active):
if not self._child_created:
Modified: python/branches/py3k/Lib/test/test_subprocess.py
==============================================================================
--- python/branches/py3k/Lib/test/test_subprocess.py (original)
+++ python/branches/py3k/Lib/test/test_subprocess.py Fri Dec 3 03:46:02 2010
@@ -1183,6 +1183,47 @@
# call() function with sequence argument with spaces on Windows
self.with_spaces([sys.executable, self.fname, "ab cd"])
+
+class ContextManagerTests(ProcessTestCase):
+
+ def test_pipe(self):
+ with subprocess.Popen([sys.executable, "-c",
+ "import sys;"
+ "sys.stdout.write('stdout');"
+ "sys.stderr.write('stderr');"],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE) as proc:
+ self.assertEqual(proc.stdout.read(), b"stdout")
+ self.assertStderrEqual(proc.stderr.read(), b"stderr")
+
+ self.assertTrue(proc.stdout.closed)
+ self.assertTrue(proc.stderr.closed)
+
+ def test_returncode(self):
+ with subprocess.Popen([sys.executable, "-c",
+ "import sys; sys.exit(100)"]) as proc:
+ proc.wait()
+ self.assertEqual(proc.returncode, 100)
+
+ def test_communicate_stdin(self):
+ with subprocess.Popen([sys.executable, "-c",
+ "import sys;"
+ "sys.exit(sys.stdin.read() == 'context')"],
+ stdin=subprocess.PIPE) as proc:
+ proc.communicate(b"context")
+ self.assertEqual(proc.returncode, 1)
+
+ def test_invalid_args(self):
+ with self.assertRaises(EnvironmentError) as c:
+ with subprocess.Popen(['nonexisting_i_hope'],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE) as proc:
+ pass
+
+ if c.exception.errno != errno.ENOENT: # ignore "no such file"
+ raise c.exception
+
+
def test_main():
unit_tests = (ProcessTestCase,
POSIXProcessTestCase,
@@ -1191,7 +1232,8 @@
CommandTests,
ProcessTestCaseNoPoll,
HelperFunctionTests,
- CommandsWithSpaces)
+ CommandsWithSpaces,
+ ContextManagerTests)
support.run_unittest(*unit_tests)
support.reap_children()
Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS (original)
+++ python/branches/py3k/Misc/NEWS Fri Dec 3 03:46:02 2010
@@ -58,6 +58,8 @@
Library
-------
+- Issue #10554: Add context manager support to subprocess.Popen objects.
+
- Issue #8989: email.utils.make_msgid now has a domain parameter that can
override the domain name used in the generated msgid.
From solipsis at pitrou.net Fri Dec 3 04:49:34 2010
From: solipsis at pitrou.net (solipsis at pitrou.net)
Date: Fri, 03 Dec 2010 04:49:34 +0100
Subject: [Python-checkins] Daily py3k reference leaks (r86948): sum=0
Message-ID:
py3k results for svn r86948 (hg cset 798212ac6f3c)
--------------------------------------------------
Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflogxzkNCb', '-x']
From python-checkins at python.org Fri Dec 3 05:06:39 2010
From: python-checkins at python.org (r.david.murray)
Date: Fri, 3 Dec 2010 05:06:39 +0100 (CET)
Subject: [Python-checkins] r86952 - in python/branches/py3k:
Doc/library/html.parser.rst Lib/html/parser.py
Lib/test/test_htmlparser.py Misc/NEWS
Message-ID: <20101203040639.AA9BEEE981@mail.python.org>
Author: r.david.murray
Date: Fri Dec 3 05:06:39 2010
New Revision: 86952
Log:
#1486713: Add a tolerant mode to HTMLParser.
The motivation for adding this option is that the the functionality it
provides used to be provided by sgmllib in Python2, and was used by,
for example, BeautifulSoup. Without this option, the Python3 version
of BeautifulSoup and the many programs that use it are crippled.
The original patch was by 'kxroberto'. I modified it heavily but kept his
heuristics and test. I also added additional heuristics to fix #975556,
#1046092, and part of #6191. This patch should be completely backward
compatible: the behavior with the default strict=True is unchanged.
Modified:
python/branches/py3k/Doc/library/html.parser.rst
python/branches/py3k/Lib/html/parser.py
python/branches/py3k/Lib/test/test_htmlparser.py
python/branches/py3k/Misc/NEWS
Modified: python/branches/py3k/Doc/library/html.parser.rst
==============================================================================
--- python/branches/py3k/Doc/library/html.parser.rst (original)
+++ python/branches/py3k/Doc/library/html.parser.rst Fri Dec 3 05:06:39 2010
@@ -12,9 +12,13 @@
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()
+.. class:: HTMLParser(strict=True)
- The :class:`HTMLParser` class is instantiated without arguments.
+ Create a parser instance. If *strict* is ``True`` (the default), invalid
+ html results in :exc:`~html.parser.HTMLParseError` exceptions [#]_. If
+ *strict* is ``False``, the parser uses heuristics to make a best guess at
+ the intention of any invalid html it encounters, similar to the way most
+ browsers do.
An :class:`HTMLParser` instance is fed HTML data and calls handler functions when tags
begin and end. The :class:`HTMLParser` class is meant to be overridden by the
@@ -191,3 +195,8 @@
Encountered a html end tag
+.. rubric:: Footnotes
+
+.. [#] For backward compatibility reasons *strict* mode does not throw
+ errors for all non-compliant HTML. That is, some invalid HTML
+ is tolerated even in *strict* mode.
Modified: python/branches/py3k/Lib/html/parser.py
==============================================================================
--- python/branches/py3k/Lib/html/parser.py (original)
+++ python/branches/py3k/Lib/html/parser.py Fri Dec 3 05:06:39 2010
@@ -24,10 +24,14 @@
piclose = re.compile('>')
commentclose = re.compile(r'--\s*>')
tagfind = re.compile('[a-zA-Z][-.a-zA-Z0-9:_]*')
+# Note, the strict one of this pair isn't really strict, but we can't
+# make it correctly strict without breaking backward compatibility.
attrfind = re.compile(
r'\s*([a-zA-Z_][-.:a-zA-Z_0-9]*)(\s*=\s*'
r'(\'[^\']*\'|"[^"]*"|[-a-zA-Z0-9./,:;+*%?!&$\(\)_#=~@]*))?')
-
+attrfind_tolerant = re.compile(
+ r'\s*([a-zA-Z_][-.:a-zA-Z_0-9]*)(\s*=\s*'
+ r'(\'[^\']*\'|"[^"]*"|[^>\s]*))?')
locatestarttagend = re.compile(r"""
<[a-zA-Z][-.a-zA-Z0-9:_]* # tag name
(?:\s+ # whitespace before attribute name
@@ -42,6 +46,21 @@
)*
\s* # trailing whitespace
""", re.VERBOSE)
+locatestarttagend_tolerant = re.compile(r"""
+ <[a-zA-Z][-.a-zA-Z0-9:_]* # tag name
+ (?:\s* # optional whitespace before attribute name
+ (?:[a-zA-Z_][-.:a-zA-Z0-9_]* # attribute name
+ (?:\s*=\s* # value indicator
+ (?:'[^']*' # LITA-enclosed value
+ |\"[^\"]*\" # LIT-enclosed value
+ |[^'\">\s]+ # bare value
+ )
+ (?:\s*,)* # possibly followed by a comma
+ )?
+ )
+ )*
+ \s* # trailing whitespace
+""", re.VERBOSE)
endendtag = re.compile('>')
endtagfind = re.compile('\s*([a-zA-Z][-.a-zA-Z0-9:_]*)\s*>')
@@ -86,9 +105,15 @@
CDATA_CONTENT_ELEMENTS = ("script", "style")
+ def __init__(self, strict=True):
+ """Initialize and reset this instance.
- def __init__(self):
- """Initialize and reset this instance."""
+ If strict is set to True (the default), errors are raised when invalid
+ HTML is encountered. If set to False, an attempt is instead made to
+ continue parsing, making "best guesses" about the intended meaning, in
+ a fashion similar to what browsers typically do.
+ """
+ self.strict = strict
self.reset()
def reset(self):
@@ -160,9 +185,18 @@
else:
break
if k < 0:
- if end:
+ if not end:
+ break
+ if self.strict:
self.error("EOF in middle of construct")
- break
+ k = rawdata.find('>', i + 1)
+ if k < 0:
+ k = rawdata.find('<', i + 1)
+ if k < 0:
+ k = i + 1
+ else:
+ k += 1
+ self.handle_data(rawdata[i:k])
i = self.updatepos(i, k)
elif startswith("", i):
match = charref.match(rawdata, i)
@@ -193,7 +227,12 @@
if match:
# match.group() will contain at least 2 chars
if end and match.group() == rawdata[i:]:
- self.error("EOF in middle of entity or char ref")
+ if self.strict:
+ self.error("EOF in middle of entity or char ref")
+ else:
+ if k <= i:
+ k = n
+ i = self.updatepos(i, i + 1)
# incomplete
break
elif (i + 1) < n:
@@ -240,7 +279,10 @@
self.lasttag = tag = rawdata[i+1:k].lower()
while k < endpos:
- m = attrfind.match(rawdata, k)
+ if self.strict:
+ m = attrfind.match(rawdata, k)
+ else:
+ m = attrfind_tolerant.search(rawdata, k)
if not m:
break
attrname, rest, attrvalue = m.group(1, 2, 3)
@@ -262,8 +304,11 @@
- self.__starttag_text.rfind("\n")
else:
offset = offset + len(self.__starttag_text)
- self.error("junk characters in start tag: %r"
- % (rawdata[k:endpos][:20],))
+ if self.strict:
+ self.error("junk characters in start tag: %r"
+ % (rawdata[k:endpos][:20],))
+ self.handle_data(rawdata[i:endpos])
+ return endpos
if end.endswith('/>'):
# XHTML-style empty tag:
self.handle_startendtag(tag, attrs)
@@ -277,7 +322,10 @@
# or -1 if incomplete.
def check_for_whole_start_tag(self, i):
rawdata = self.rawdata
- m = locatestarttagend.match(rawdata, i)
+ if self.strict:
+ m = locatestarttagend.match(rawdata, i)
+ else:
+ m = locatestarttagend_tolerant.match(rawdata, i)
if m:
j = m.end()
next = rawdata[j:j+1]
@@ -290,8 +338,13 @@
# buffer boundary
return -1
# else bogus input
- self.updatepos(i, j + 1)
- self.error("malformed empty start tag")
+ if self.strict:
+ self.updatepos(i, j + 1)
+ self.error("malformed empty start tag")
+ if j > i:
+ return j
+ else:
+ return i + 1
if next == "":
# end of input
return -1
@@ -300,8 +353,13 @@
# end of input in or before attribute value, or we have the
# '/' from a '/>' ending
return -1
- self.updatepos(i, j)
- self.error("malformed start tag")
+ if self.strict:
+ self.updatepos(i, j)
+ self.error("malformed start tag")
+ if j > i:
+ return j
+ else:
+ return i + 1
raise AssertionError("we should not get here!")
# Internal -- parse endtag, return end or -1 if incomplete
@@ -314,7 +372,15 @@
j = match.end()
match = endtagfind.match(rawdata, i) # + tag + >
if not match:
- self.error("bad end tag: %r" % (rawdata[i:j],))
+ if self.strict:
+ self.error("bad end tag: %r" % (rawdata[i:j],))
+ k = rawdata.find('<', i + 1, j)
+ if k > i:
+ j = k
+ if j <= i:
+ j = i + 1
+ self.handle_data(rawdata[i:j])
+ return j
tag = match.group(1)
self.handle_endtag(tag.lower())
self.clear_cdata_mode()
@@ -358,7 +424,8 @@
pass
def unknown_decl(self, data):
- self.error("unknown declaration: %r" % (data,))
+ if self.strict:
+ self.error("unknown declaration: %r" % (data,))
# Internal -- helper to remove special character quoting
entitydefs = None
Modified: python/branches/py3k/Lib/test/test_htmlparser.py
==============================================================================
--- python/branches/py3k/Lib/test/test_htmlparser.py (original)
+++ python/branches/py3k/Lib/test/test_htmlparser.py Fri Dec 3 05:06:39 2010
@@ -8,10 +8,10 @@
class EventCollector(html.parser.HTMLParser):
- def __init__(self):
+ def __init__(self, *args, **kw):
self.events = []
self.append = self.events.append
- html.parser.HTMLParser.__init__(self)
+ html.parser.HTMLParser.__init__(self, *args, **kw)
def get_events(self):
# Normalize the list of events so that buffer artefacts don't
@@ -72,8 +72,10 @@
class TestCaseBase(unittest.TestCase):
- def _run_check(self, source, expected_events, collector=EventCollector):
- parser = collector()
+ def _run_check(self, source, expected_events, collector=None):
+ if collector is None:
+ collector = EventCollector()
+ parser = collector
for s in source:
parser.feed(s)
parser.close()
@@ -84,7 +86,7 @@
"\nReceived:\n" + pprint.pformat(events))
def _run_check_extra(self, source, events):
- self._run_check(source, events, EventCollectorExtra)
+ self._run_check(source, events, EventCollectorExtra())
def _parse_error(self, source):
def parse(source=source):
@@ -321,8 +323,42 @@
])
+class HTMLParserTolerantTestCase(TestCaseBase):
+
+ def setUp(self):
+ self.collector = EventCollector(strict=False)
+
+ def test_tolerant_parsing(self):
+ self._run_check('te>>xt&a<\n'
+ '/img>