From bc91176aa05a4c7e67cffaf31286a86728bc7c99 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 3 Jun 2016 21:42:55 +0300 Subject: Issue #26983: float() now always return an instance of exact float. The deprecation warning is emitted if __float__ returns an instance of a strict subclass of float. In a future versions of Python this can be an error. --- Lib/test/test_float.py | 21 +++++++++++++-------- Lib/test/test_getargs2.py | 6 ++++-- 2 files changed, 17 insertions(+), 10 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py index 427f044af4..68b212e195 100644 --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -161,11 +161,12 @@ class GeneralFloatCases(unittest.TestCase): def __float__(self): return float(str(self)) + 1 - self.assertAlmostEqual(float(Foo1()), 42.) - self.assertAlmostEqual(float(Foo2()), 42.) - self.assertAlmostEqual(float(Foo3(21)), 42.) + self.assertEqual(float(Foo1()), 42.) + self.assertEqual(float(Foo2()), 42.) + with self.assertWarns(DeprecationWarning): + self.assertEqual(float(Foo3(21)), 42.) self.assertRaises(TypeError, float, Foo4(42)) - self.assertAlmostEqual(float(FooStr('8')), 9.) + self.assertEqual(float(FooStr('8')), 9.) class Foo5: def __float__(self): @@ -176,10 +177,14 @@ class GeneralFloatCases(unittest.TestCase): class F: def __float__(self): return OtherFloatSubclass(42.) - self.assertAlmostEqual(float(F()), 42.) - self.assertIs(type(float(F())), OtherFloatSubclass) - self.assertAlmostEqual(FloatSubclass(F()), 42.) - self.assertIs(type(FloatSubclass(F())), FloatSubclass) + with self.assertWarns(DeprecationWarning): + self.assertEqual(float(F()), 42.) + with self.assertWarns(DeprecationWarning): + self.assertIs(type(float(F())), float) + with self.assertWarns(DeprecationWarning): + self.assertEqual(FloatSubclass(F()), 42.) + with self.assertWarns(DeprecationWarning): + self.assertIs(type(FloatSubclass(F())), FloatSubclass) def test_is_integer(self): self.assertFalse((1.1).is_integer()) diff --git a/Lib/test/test_getargs2.py b/Lib/test/test_getargs2.py index 8a51e0bf8c..ecc19088ff 100644 --- a/Lib/test/test_getargs2.py +++ b/Lib/test/test_getargs2.py @@ -365,7 +365,8 @@ class Float_TestCase(unittest.TestCase): self.assertEqual(getargs_f(FloatSubclass(7.5)), 7.5) self.assertEqual(getargs_f(FloatSubclass2(7.5)), 7.5) self.assertRaises(TypeError, getargs_f, BadFloat()) - self.assertEqual(getargs_f(BadFloat2()), 4.25) + with self.assertWarns(DeprecationWarning): + self.assertEqual(getargs_f(BadFloat2()), 4.25) self.assertEqual(getargs_f(BadFloat3(7.5)), 7.5) for x in (FLT_MIN, -FLT_MIN, FLT_MAX, -FLT_MAX, INF, -INF): @@ -390,7 +391,8 @@ class Float_TestCase(unittest.TestCase): self.assertEqual(getargs_d(FloatSubclass(7.5)), 7.5) self.assertEqual(getargs_d(FloatSubclass2(7.5)), 7.5) self.assertRaises(TypeError, getargs_d, BadFloat()) - self.assertEqual(getargs_d(BadFloat2()), 4.25) + with self.assertWarns(DeprecationWarning): + self.assertEqual(getargs_d(BadFloat2()), 4.25) self.assertEqual(getargs_d(BadFloat3(7.5)), 7.5) for x in (DBL_MIN, -DBL_MIN, DBL_MAX, -DBL_MAX, INF, -INF): -- cgit v1.2.1 From 2b9af62fd84f8822d2811a9489779922e0173ffd Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Fri, 3 Jun 2016 19:14:52 +0000 Subject: signal, socket, and ssl module IntEnum constant name lookups now return a consistent name for values having multiple names. Ex: signal.Signals(6) now refers to itself as signal.SIGALRM rather than flipping between that and signal.SIGIOT based on the interpreter's hash randomization seed. This helps finish issue27167. --- Lib/enum.py | 10 ++++++++-- Lib/test/test_enum.py | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/enum.py b/Lib/enum.py index c3a0a8b4a9..99db9e6b7f 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -550,8 +550,14 @@ class Enum(metaclass=EnumMeta): source = vars(source) else: source = module_globals - members = {name: value for name, value in source.items() - if filter(name)} + # We use an OrderedDict of sorted source keys so that the + # _value2member_map is populated in the same order every time + # for a consistent reverse mapping of number to name when there + # are multiple names for the same number rather than varying + # between runs due to hash randomization of the module dictionary. + members = OrderedDict((name, source[name]) + for name in sorted(source.keys()) + if filter(name)) cls = cls(name, members, module=module) cls.__reduce_ex__ = _reduce_ex_by_name module_globals.update(cls.__members__) diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index b6cb00fcf4..b1a5855790 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -1768,5 +1768,41 @@ class MiscTestCase(unittest.TestCase): support.check__all__(self, enum) +# These are unordered here on purpose to ensure that declaration order +# makes no difference. +CONVERT_TEST_NAME_D = 5 +CONVERT_TEST_NAME_C = 5 +CONVERT_TEST_NAME_B = 5 +CONVERT_TEST_NAME_A = 5 # This one should sort first. +CONVERT_TEST_NAME_E = 5 +CONVERT_TEST_NAME_F = 5 + +class TestIntEnumConvert(unittest.TestCase): + def test_convert_value_lookup_priority(self): + test_type = enum.IntEnum._convert( + 'UnittestConvert', 'test.test_enum', + filter=lambda x: x.startswith('CONVERT_TEST_')) + # We don't want the reverse lookup value to vary when there are + # multiple possible names for a given value. It should always + # report the first lexigraphical name in that case. + self.assertEqual(test_type(5).name, 'CONVERT_TEST_NAME_A') + + def test_convert(self): + test_type = enum.IntEnum._convert( + 'UnittestConvert', 'test.test_enum', + filter=lambda x: x.startswith('CONVERT_TEST_')) + # Ensure that test_type has all of the desired names and values. + self.assertEqual(test_type.CONVERT_TEST_NAME_F, + test_type.CONVERT_TEST_NAME_A) + self.assertEqual(test_type.CONVERT_TEST_NAME_B, 5) + self.assertEqual(test_type.CONVERT_TEST_NAME_C, 5) + self.assertEqual(test_type.CONVERT_TEST_NAME_D, 5) + self.assertEqual(test_type.CONVERT_TEST_NAME_E, 5) + # Ensure that test_type only picked up names matching the filter. + self.assertEqual([name for name in dir(test_type) + if name[0:2] not in ('CO', '__')], + [], msg='Names other than CONVERT_TEST_* found.') + + if __name__ == '__main__': unittest.main() -- cgit v1.2.1 From 5822fc112a39f2620ffbb28c1341802bb6cb1536 Mon Sep 17 00:00:00 2001 From: R David Murray Date: Fri, 3 Jun 2016 20:16:06 -0400 Subject: Clean up urlopen doc string. Clarifies what is returned when and that the methods are common between the two. Patch by Alexander Liu as part of #22797. --- Lib/urllib/request.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'Lib') diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py index 4a3daec5d0..333c3f245c 100644 --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -173,12 +173,7 @@ def urlopen(url, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, The *cadefault* parameter is ignored. For http and https urls, this function returns a http.client.HTTPResponse - object which has the following HTTPResponse Objects methods. - - For ftp, file, and data urls and requests explicitly handled by legacy - URLopener and FancyURLopener classes, this function returns a - urllib.response.addinfourl object which can work as context manager and has - methods such as: + object which has the following HTTPResponse Objects methods: * geturl() - return the URL of the resource retrieved, commonly used to determine if a redirect was followed @@ -190,6 +185,11 @@ def urlopen(url, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, * getcode() - return the HTTP status code of the response. Raises URLError on errors. + For ftp, file, and data urls and requests explicitly handled by legacy + URLopener and FancyURLopener classes, this function returns a + urllib.response.addinfourl object which can work as context manager and + also support the geturl(), info(), getcode() methods listed above. + Note that *None& may be returned if no handler handles the request (though the default installed global OpenerDirector uses UnknownHandler to ensure this never happens). -- cgit v1.2.1 From adada454292d261ba95bab68c6c9e9247eeedaa3 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Fri, 3 Jun 2016 17:50:44 -0700 Subject: Issue #24225: Fix additional renamed module references. --- Lib/idlelib/macosx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/idlelib/macosx.py b/Lib/idlelib/macosx.py index 1e16f2c586..618cc2631b 100644 --- a/Lib/idlelib/macosx.py +++ b/Lib/idlelib/macosx.py @@ -155,7 +155,7 @@ def overrideRootMenu(root, flist): if end > 0: menu.delete(0, end) windows.add_windows_to_menu(menu) - Windows.register_callback(postwindowsmenu) + windows.register_callback(postwindowsmenu) def about_dialog(event=None): "Handle Help 'About IDLE' event." -- cgit v1.2.1 From 2d9f46b35d978f5a5540cae40471e4d460d7d982 Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Sat, 4 Jun 2016 05:06:25 +0000 Subject: More typo fixes for 3.6 --- Lib/trace.py | 2 +- Lib/urllib/request.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/trace.py b/Lib/trace.py index 7afbe76713..ae154615fa 100755 --- a/Lib/trace.py +++ b/Lib/trace.py @@ -224,7 +224,7 @@ class CoverageResults: :param show_missing: Show lines that had no hits. :param summary: Include coverage summary per module. - :param coverdir: If None, the results of each module are placed in it's + :param coverdir: If None, the results of each module are placed in its directory, otherwise it is included in the directory specified. """ diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py index 333c3f245c..b16b642458 100644 --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -187,7 +187,7 @@ def urlopen(url, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, For ftp, file, and data urls and requests explicitly handled by legacy URLopener and FancyURLopener classes, this function returns a - urllib.response.addinfourl object which can work as context manager and + urllib.response.addinfourl object which can work as a context manager and also support the geturl(), info(), getcode() methods listed above. Note that *None& may be returned if no handler handles the request (though -- cgit v1.2.1 From ed84ab3214da74d437d7fa0211173bbd046560b0 Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Sat, 4 Jun 2016 05:06:34 +0000 Subject: Issue #22797: Synchronize urlopen() doc string with RST documentation --- Lib/urllib/request.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'Lib') diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py index b16b642458..67e73f9ef8 100644 --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -172,8 +172,8 @@ def urlopen(url, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, The *cadefault* parameter is ignored. - For http and https urls, this function returns a http.client.HTTPResponse - object which has the following HTTPResponse Objects methods: + This function always returns an object which can work as a context + manager and has methods such as * geturl() - return the URL of the resource retrieved, commonly used to determine if a redirect was followed @@ -185,12 +185,17 @@ def urlopen(url, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, * getcode() - return the HTTP status code of the response. Raises URLError on errors. - For ftp, file, and data urls and requests explicitly handled by legacy + For HTTP and HTTPS URLs, this function returns a http.client.HTTPResponse + object slightly modified. In addition to the three new methods above, the + msg attribute contains the same information as the reason attribute --- + the reason phrase returned by the server --- instead of the response + headers as it is specified in the documentation for HTTPResponse. + + For FTP, file, and data URLs and requests explicitly handled by legacy URLopener and FancyURLopener classes, this function returns a - urllib.response.addinfourl object which can work as a context manager and - also support the geturl(), info(), getcode() methods listed above. + urllib.response.addinfourl object. - Note that *None& may be returned if no handler handles the request (though + Note that None may be returned if no handler handles the request (though the default installed global OpenerDirector uses UnknownHandler to ensure this never happens). -- cgit v1.2.1 From 4a77d54cf048d84026d18cea2978bd2c8177e2c4 Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Sat, 4 Jun 2016 10:19:27 -0700 Subject: issue27182: update fsencode and fsdecode for os.path(); patch by Dusty Phillips --- Lib/os.py | 22 ++++++++++++++-------- Lib/test/test_os.py | 15 +++++++++++++++ 2 files changed, 29 insertions(+), 8 deletions(-) (limited to 'Lib') diff --git a/Lib/os.py b/Lib/os.py index edd61ab8f8..1318de6b5a 100644 --- a/Lib/os.py +++ b/Lib/os.py @@ -877,29 +877,35 @@ def _fscodec(): def fsencode(filename): """ - Encode filename to the filesystem encoding with 'surrogateescape' error - handler, return bytes unchanged. On Windows, use 'strict' error handler if - the file system encoding is 'mbcs' (which is the default encoding). + Encode filename (an os.PathLike, bytes, or str) to the filesystem + encoding with 'surrogateescape' error handler, return bytes unchanged. + On Windows, use 'strict' error handler if the file system encoding is + 'mbcs' (which is the default encoding). """ + filename = fspath(filename) if isinstance(filename, bytes): return filename elif isinstance(filename, str): return filename.encode(encoding, errors) else: - raise TypeError("expect bytes or str, not %s" % type(filename).__name__) + raise TypeError("expected str, bytes or os.PathLike object, not " + + path_type.__name__) def fsdecode(filename): """ - Decode filename from the filesystem encoding with 'surrogateescape' error - handler, return str unchanged. On Windows, use 'strict' error handler if - the file system encoding is 'mbcs' (which is the default encoding). + Decode filename (an os.PathLike, bytes, or str) from the filesystem + encoding with 'surrogateescape' error handler, return str unchanged. On + Windows, use 'strict' error handler if the file system encoding is + 'mbcs' (which is the default encoding). """ + filename = fspath(filename) if isinstance(filename, str): return filename elif isinstance(filename, bytes): return filename.decode(encoding, errors) else: - raise TypeError("expect bytes or str, not %s" % type(filename).__name__) + raise TypeError("expected str, bytes or os.PathLike object, not " + + path_type.__name__) return fsencode, fsdecode diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index f52de28768..84ef150f82 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -3106,6 +3106,21 @@ class TestPEP519(unittest.TestCase): for s in 'hello', 'goodbye', 'some/path/and/file': self.assertEqual(s, os.fspath(s)) + def test_fsencode_fsdecode_return_pathlike(self): + class Pathlike: + def __init__(self, path): + self.path = path + + def __fspath__(self): + return self.path + + for p in "path/like/object", b"path/like/object": + pathlike = Pathlike(p) + + self.assertEqual(p, os.fspath(pathlike)) + self.assertEqual(b"path/like/object", os.fsencode(pathlike)) + self.assertEqual("path/like/object", os.fsdecode(pathlike)) + def test_garbage_in_exception_out(self): vapor = type('blah', (), {}) for o in int, type, os, vapor(): -- cgit v1.2.1 From 740ae7d4e192f389a9695844313e73d987800fbf Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Sat, 4 Jun 2016 12:06:26 -0700 Subject: issue27186: add C version of os.fspath(); patch by Jelle Zijlstra --- Lib/os.py | 35 ++++++++++++++++++----------------- Lib/test/test_os.py | 7 +++++++ 2 files changed, 25 insertions(+), 17 deletions(-) (limited to 'Lib') diff --git a/Lib/os.py b/Lib/os.py index 1318de6b5a..0131ed8195 100644 --- a/Lib/os.py +++ b/Lib/os.py @@ -1104,23 +1104,24 @@ def fdopen(fd, *args, **kwargs): import io return io.open(fd, *args, **kwargs) -# Supply os.fspath() -def fspath(path): - """Return the string representation of the path. +# Supply os.fspath() if not defined in C +if not _exists('fspath'): + def fspath(path): + """Return the string representation of the path. - If str or bytes is passed in, it is returned unchanged. - """ - if isinstance(path, (str, bytes)): - return path + If str or bytes is passed in, it is returned unchanged. + """ + if isinstance(path, (str, bytes)): + return path - # Work from the object's type to match method resolution of other magic - # methods. - path_type = type(path) - try: - return path_type.__fspath__(path) - except AttributeError: - if hasattr(path_type, '__fspath__'): - raise + # Work from the object's type to match method resolution of other magic + # methods. + path_type = type(path) + try: + return path_type.__fspath__(path) + except AttributeError: + if hasattr(path_type, '__fspath__'): + raise - raise TypeError("expected str, bytes or os.PathLike object, not " - + path_type.__name__) + raise TypeError("expected str, bytes or os.PathLike object, not " + + path_type.__name__) diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 84ef150f82..bf06438db2 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -3121,6 +3121,13 @@ class TestPEP519(unittest.TestCase): self.assertEqual(b"path/like/object", os.fsencode(pathlike)) self.assertEqual("path/like/object", os.fsdecode(pathlike)) + def test_fspathlike(self): + class PathLike(object): + def __fspath__(self): + return '#feelthegil' + + self.assertEqual('#feelthegil', os.fspath(PathLike())) + def test_garbage_in_exception_out(self): vapor = type('blah', (), {}) for o in int, type, os, vapor(): -- cgit v1.2.1 From 51bb1a0925b69604832251cb2e57493f495671e0 Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Sat, 4 Jun 2016 12:49:35 -0700 Subject: issue27186: add PathLike ABC --- Lib/os.py | 17 ++++++++++++++++- Lib/test/test_os.py | 2 ++ 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/os.py b/Lib/os.py index 0131ed8195..e7d089ed35 100644 --- a/Lib/os.py +++ b/Lib/os.py @@ -22,7 +22,7 @@ and opendir), and leave all pathname manipulation to os.path """ #' - +import abc import sys, errno import stat as st @@ -1125,3 +1125,18 @@ if not _exists('fspath'): raise TypeError("expected str, bytes or os.PathLike object, not " + path_type.__name__) + +class PathLike(abc.ABC): + """ + Abstract base class for implementing the file system path protocol. + """ + @abc.abstractmethod + def __fspath__(self): + """ + Return the file system path representation of the object. + """ + raise NotImplementedError + + @classmethod + def __subclasshook__(cls, subclass): + return hasattr(subclass, '__fspath__') diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index bf06438db2..e740edf644 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -3127,6 +3127,8 @@ class TestPEP519(unittest.TestCase): return '#feelthegil' self.assertEqual('#feelthegil', os.fspath(PathLike())) + self.assertTrue(issubclass(PathLike, os.PathLike)) + self.assertTrue(isinstance(PathLike(), os.PathLike)) def test_garbage_in_exception_out(self): vapor = type('blah', (), {}) -- cgit v1.2.1 From 3f20dfd9ec88fa83c6d9f93eca4d5b06eb1ffe69 Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Sat, 4 Jun 2016 13:47:39 -0700 Subject: issue27186: fix fsencode/fsdecode and update tests; patch by Jelle Zijlstra --- Lib/os.py | 4 ++-- Lib/test/test_os.py | 19 +++++++++++++------ 2 files changed, 15 insertions(+), 8 deletions(-) (limited to 'Lib') diff --git a/Lib/os.py b/Lib/os.py index e7d089ed35..56a2f7187b 100644 --- a/Lib/os.py +++ b/Lib/os.py @@ -889,7 +889,7 @@ def _fscodec(): return filename.encode(encoding, errors) else: raise TypeError("expected str, bytes or os.PathLike object, not " - + path_type.__name__) + + type(filename).__name__) def fsdecode(filename): """ @@ -905,7 +905,7 @@ def _fscodec(): return filename.decode(encoding, errors) else: raise TypeError("expected str, bytes or os.PathLike object, not " - + path_type.__name__) + + type(filename).__name__) return fsencode, fsdecode diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index e740edf644..6853ebbd7f 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -3107,29 +3107,36 @@ class TestPEP519(unittest.TestCase): self.assertEqual(s, os.fspath(s)) def test_fsencode_fsdecode_return_pathlike(self): - class Pathlike: + class PathLike: def __init__(self, path): self.path = path - def __fspath__(self): return self.path for p in "path/like/object", b"path/like/object": - pathlike = Pathlike(p) + pathlike = PathLike(p) self.assertEqual(p, os.fspath(pathlike)) self.assertEqual(b"path/like/object", os.fsencode(pathlike)) self.assertEqual("path/like/object", os.fsdecode(pathlike)) def test_fspathlike(self): - class PathLike(object): + class PathLike: + def __init__(self, path=''): + self.path = path def __fspath__(self): - return '#feelthegil' + return self.path - self.assertEqual('#feelthegil', os.fspath(PathLike())) + self.assertEqual('#feelthegil', os.fspath(PathLike('#feelthegil'))) self.assertTrue(issubclass(PathLike, os.PathLike)) self.assertTrue(isinstance(PathLike(), os.PathLike)) + message = 'expected str, bytes or os.PathLike object, not' + for fn in (os.fsencode, os.fsdecode): + for obj in PathLike(None), None: + with self.assertRaisesRegex(TypeError, message): + fn(obj) + def test_garbage_in_exception_out(self): vapor = type('blah', (), {}) for o in int, type, os, vapor(): -- cgit v1.2.1 From 3f04a353d7bac512bee38f15e8a12269a868d356 Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Sat, 4 Jun 2016 14:38:43 -0700 Subject: issue27186: add open/io.open; patch by Jelle Zijlstra --- Lib/_pyio.py | 2 ++ Lib/test/test_io.py | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) (limited to 'Lib') diff --git a/Lib/_pyio.py b/Lib/_pyio.py index 7b89347a4c..40df79d345 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -161,6 +161,8 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None, opened in a text mode, and for bytes a BytesIO can be used like a file opened in a binary mode. """ + if not isinstance(file, int): + file = os.fspath(file) if not isinstance(file, (str, bytes, int)): raise TypeError("invalid file: %r" % file) if not isinstance(mode, str): diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 53e776d910..5584d6b13e 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -844,6 +844,32 @@ class IOTest(unittest.TestCase): self.assertEqual(getattr(stream, method)(buffer), 5) self.assertEqual(bytes(buffer), b"12345") + def test_fspath_support(self): + class PathLike: + def __init__(self, path): + self.path = path + + def __fspath__(self): + return self.path + + def check_path_succeeds(path): + with self.open(path, "w") as f: + f.write("egg\n") + + with self.open(path, "r") as f: + self.assertEqual(f.read(), "egg\n") + + check_path_succeeds(PathLike(support.TESTFN)) + check_path_succeeds(PathLike(support.TESTFN.encode('utf-8'))) + + bad_path = PathLike(TypeError) + with self.assertRaisesRegex(TypeError, 'invalid file'): + self.open(bad_path, 'w') + + # ensure that refcounting is correct with some error conditions + with self.assertRaisesRegex(ValueError, 'read/write/append mode'): + self.open(PathLike(support.TESTFN), 'rwxa') + class CIOTest(IOTest): -- cgit v1.2.1 From f3a8dc69e152d5c92eb7469123be8514d0489878 Mon Sep 17 00:00:00 2001 From: Nick Coghlan Date: Sat, 4 Jun 2016 14:40:03 -0700 Subject: Issue #19611: handle implicit parameters in inspect.signature inspect.signature now reports the implicit ``.0`` parameters generated by the compiler for comprehension and generator expression scopes as if they were positional-only parameters called ``implicit0``. Patch by Jelle Zijlstra. --- Lib/inspect.py | 14 ++++++++++++++ Lib/test/test_inspect.py | 26 ++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) (limited to 'Lib') diff --git a/Lib/inspect.py b/Lib/inspect.py index 582bb0e7fa..45d2110106 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -2396,6 +2396,20 @@ class Parameter: if not isinstance(name, str): raise TypeError("name must be a str, not a {!r}".format(name)) + if name[0] == '.' and name[1:].isdigit(): + # These are implicit arguments generated by comprehensions. In + # order to provide a friendlier interface to users, we recast + # their name as "implicitN" and treat them as positional-only. + # See issue 19611. + if kind != _POSITIONAL_OR_KEYWORD: + raise ValueError( + 'implicit arguments must be passed in as {}'.format( + _POSITIONAL_OR_KEYWORD + ) + ) + self._kind = _POSITIONAL_ONLY + name = 'implicit{}'.format(name[1:]) + if not name.isidentifier(): raise ValueError('{!r} is not a valid parameter name'.format(name)) diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index 76cebcc221..47244aeaa4 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -2903,6 +2903,10 @@ class TestParameterObject(unittest.TestCase): 'is not a valid parameter name'): inspect.Parameter('$', kind=inspect.Parameter.VAR_KEYWORD) + with self.assertRaisesRegex(ValueError, + 'is not a valid parameter name'): + inspect.Parameter('.a', kind=inspect.Parameter.VAR_KEYWORD) + with self.assertRaisesRegex(ValueError, 'cannot have default values'): inspect.Parameter('a', default=42, kind=inspect.Parameter.VAR_KEYWORD) @@ -2986,6 +2990,17 @@ class TestParameterObject(unittest.TestCase): with self.assertRaisesRegex(TypeError, 'name must be a str'): inspect.Parameter(None, kind=inspect.Parameter.POSITIONAL_ONLY) + @cpython_only + def test_signature_parameter_implicit(self): + with self.assertRaisesRegex(ValueError, + 'implicit arguments must be passed in as'): + inspect.Parameter('.0', kind=inspect.Parameter.POSITIONAL_ONLY) + + param = inspect.Parameter( + '.0', kind=inspect.Parameter.POSITIONAL_OR_KEYWORD) + self.assertEqual(param.kind, inspect.Parameter.POSITIONAL_ONLY) + self.assertEqual(param.name, 'implicit0') + def test_signature_parameter_immutability(self): p = inspect.Parameter('spam', kind=inspect.Parameter.KEYWORD_ONLY) @@ -3234,6 +3249,17 @@ class TestSignatureBind(unittest.TestCase): ba = sig.bind(args=1) self.assertEqual(ba.arguments, {'kwargs': {'args': 1}}) + @cpython_only + def test_signature_bind_implicit_arg(self): + # Issue #19611: getcallargs should work with set comprehensions + def make_set(): + return {z * z for z in range(5)} + setcomp_code = make_set.__code__.co_consts[1] + setcomp_func = types.FunctionType(setcomp_code, {}) + + iterator = iter(range(5)) + self.assertEqual(self.call(setcomp_func, iterator), {0, 1, 4, 9, 16}) + class TestBoundArguments(unittest.TestCase): def test_signature_bound_arguments_unhashable(self): -- cgit v1.2.1 From ec09e0e9556e3775e8096ae1a9ec7dc03c8a7bf5 Mon Sep 17 00:00:00 2001 From: doko Date: Sun, 5 Jun 2016 01:17:57 +0200 Subject: - Issue #21272: Use _sysconfigdata.py to initialize distutils.sysconfig. --- Lib/distutils/sysconfig.py | 35 ++++------------------------------- 1 file changed, 4 insertions(+), 31 deletions(-) (limited to 'Lib') diff --git a/Lib/distutils/sysconfig.py b/Lib/distutils/sysconfig.py index d203f8e42b..f205dcadeb 100644 --- a/Lib/distutils/sysconfig.py +++ b/Lib/distutils/sysconfig.py @@ -415,38 +415,11 @@ _config_vars = None def _init_posix(): """Initialize the module as appropriate for POSIX systems.""" - g = {} - # load the installed Makefile: - try: - filename = get_makefile_filename() - parse_makefile(filename, g) - except OSError as msg: - my_msg = "invalid Python installation: unable to open %s" % filename - if hasattr(msg, "strerror"): - my_msg = my_msg + " (%s)" % msg.strerror - - raise DistutilsPlatformError(my_msg) - - # load the installed pyconfig.h: - try: - filename = get_config_h_filename() - with open(filename) as file: - parse_config_h(file, g) - except OSError as msg: - my_msg = "invalid Python installation: unable to open %s" % filename - if hasattr(msg, "strerror"): - my_msg = my_msg + " (%s)" % msg.strerror - - raise DistutilsPlatformError(my_msg) - - # On AIX, there are wrong paths to the linker scripts in the Makefile - # -- these paths are relative to the Python source, but when installed - # the scripts are in another directory. - if python_build: - g['LDSHARED'] = g['BLDSHARED'] - + # _sysconfigdata is generated at build time, see the sysconfig module + from _sysconfigdata import build_time_vars global _config_vars - _config_vars = g + _config_vars = {} + _config_vars.update(build_time_vars) def _init_nt(): -- cgit v1.2.1 From b2bfc00b0792a26f499225fc5dd60b9b7e9287f3 Mon Sep 17 00:00:00 2001 From: Kushal Das Date: Sat, 4 Jun 2016 16:21:13 -0700 Subject: Issue #25548: Showing memory address of class objects in repl --- Lib/ctypes/test/test_structures.py | 10 +++++----- Lib/statistics.py | 8 ++++---- Lib/test/test_class.py | 10 ++++++++++ Lib/test/test_cmd_line_script.py | 9 ++++++++- Lib/test/test_defaultdict.py | 2 +- Lib/test/test_descr.py | 4 ++-- Lib/test/test_descrtut.py | 31 ++++++++++++++++--------------- Lib/test/test_doctest.py | 2 +- Lib/test/test_doctest3.txt | 2 +- Lib/test/test_functools.py | 33 ++++++++++++--------------------- Lib/test/test_generators.py | 37 +++++++++++++++++++------------------ Lib/test/test_genexps.py | 5 +++-- Lib/test/test_metaclass.py | 9 +++++---- Lib/test/test_pprint.py | 9 ++++----- Lib/test/test_reprlib.py | 6 +++--- Lib/test/test_statistics.py | 2 +- Lib/test/test_wsgiref.py | 7 ++++--- Lib/test/test_xmlrpc.py | 4 ++-- 18 files changed, 101 insertions(+), 89 deletions(-) (limited to 'Lib') diff --git a/Lib/ctypes/test/test_structures.py b/Lib/ctypes/test/test_structures.py index e00bb046b4..736679f630 100644 --- a/Lib/ctypes/test/test_structures.py +++ b/Lib/ctypes/test/test_structures.py @@ -320,14 +320,14 @@ class StructureTestCase(unittest.TestCase): cls, msg = self.get_except(Person, b"Someone", (1, 2)) self.assertEqual(cls, RuntimeError) - self.assertEqual(msg, - "(Phone) : " - "expected bytes, int found") + self.assertRegex(msg, + r"\(Phone\) : " + r"expected bytes, int found") cls, msg = self.get_except(Person, b"Someone", (b"a", b"b", b"c")) self.assertEqual(cls, RuntimeError) - self.assertEqual(msg, - "(Phone) : too many initializers") + self.assertRegex(msg, + r"\(Phone\) : too many initializers") def test_huge_field_name(self): # issue12881: segfault with large structure field names diff --git a/Lib/statistics.py b/Lib/statistics.py index b081b5a006..63adc1a44f 100644 --- a/Lib/statistics.py +++ b/Lib/statistics.py @@ -131,23 +131,23 @@ def _sum(data, start=0): -------- >>> _sum([3, 2.25, 4.5, -0.5, 1.0], 0.75) - (, Fraction(11, 1), 5) + (, Fraction(11, 1), 5) Some sources of round-off error will be avoided: >>> _sum([1e50, 1, -1e50] * 1000) # Built-in sum returns zero. - (, Fraction(1000, 1), 3000) + (, Fraction(1000, 1), 3000) Fractions and Decimals are also supported: >>> from fractions import Fraction as F >>> _sum([F(2, 3), F(7, 5), F(1, 4), F(5, 6)]) - (, Fraction(63, 20), 4) + (, Fraction(63, 20), 4) >>> from decimal import Decimal as D >>> data = [D("0.1375"), D("0.2108"), D("0.3061"), D("0.0419")] >>> _sum(data) - (, Fraction(6963, 10000), 4) + (, Fraction(6963, 10000), 4) Mixed types are currently treated as an error, except that int is allowed. diff --git a/Lib/test/test_class.py b/Lib/test/test_class.py index 4d554a397b..e02a3cbb97 100644 --- a/Lib/test/test_class.py +++ b/Lib/test/test_class.py @@ -568,5 +568,15 @@ class ClassTests(unittest.TestCase): a = A(hash(A.f)^(-1)) hash(a.f) + def test_class_repr(self): + # We should get the address of the object + class A: + pass + + result = repr(A) + self.assertRegex(result, + ".A'" + " at 0x.+>") + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_cmd_line_script.py b/Lib/test/test_cmd_line_script.py index 01fb7dcf40..e7c6351317 100644 --- a/Lib/test/test_cmd_line_script.py +++ b/Lib/test/test_cmd_line_script.py @@ -127,7 +127,10 @@ class CmdLineTest(unittest.TestCase): print(printed_package) print(printed_argv0) print(printed_cwd) - self.assertIn(printed_loader.encode('utf-8'), data) + expected = printed_loader.encode('utf-8') + idx = expected.find(b"at 0x") + expected = expected[:idx] + self.assertIn(expected, data) self.assertIn(printed_file.encode('utf-8'), data) self.assertIn(printed_package.encode('utf-8'), data) self.assertIn(printed_argv0.encode('utf-8'), data) @@ -158,6 +161,8 @@ class CmdLineTest(unittest.TestCase): def test_dash_c_loader(self): rc, out, err = assert_python_ok("-c", "print(__loader__)") expected = repr(importlib.machinery.BuiltinImporter).encode("utf-8") + idx = expected.find(b"at 0x") + expected = expected[:idx] self.assertIn(expected, out) def test_stdin_loader(self): @@ -171,6 +176,8 @@ class CmdLineTest(unittest.TestCase): finally: out = kill_python(p) expected = repr(importlib.machinery.BuiltinImporter).encode("utf-8") + idx = expected.find(b"at 0x") + expected = expected[:idx] self.assertIn(expected, out) @contextlib.contextmanager diff --git a/Lib/test/test_defaultdict.py b/Lib/test/test_defaultdict.py index a90bc2b488..5bb4e12278 100644 --- a/Lib/test/test_defaultdict.py +++ b/Lib/test/test_defaultdict.py @@ -65,7 +65,7 @@ class TestDefaultDict(unittest.TestCase): d2 = defaultdict(int) self.assertEqual(d2.default_factory, int) d2[12] = 42 - self.assertEqual(repr(d2), "defaultdict(, {12: 42})") + self.assertRegex(repr(d2), r"defaultdict\(, {12: 42}\)") def foo(): return 43 d3 = defaultdict(foo) self.assertTrue(d3.default_factory is foo) diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 0a5ecd5a0d..954ef2d3b3 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -4544,9 +4544,9 @@ order (MRO) for bases """ pass foo = Foo() self.assertRegex(repr(foo.method), # access via instance - r">") + r">") self.assertRegex(repr(Foo.method), # access via the class - r">") + r">") class MyCallable: diff --git a/Lib/test/test_descrtut.py b/Lib/test/test_descrtut.py index 506d1abeb6..80cfa41ecb 100644 --- a/Lib/test/test_descrtut.py +++ b/Lib/test/test_descrtut.py @@ -37,16 +37,16 @@ test_1 = """ Here's the new type at work: >>> print(defaultdict) # show our type - + >>> print(type(defaultdict)) # its metatype - + >>> a = defaultdict(default=0.0) # create an instance >>> print(a) # show the instance {} >>> print(type(a)) # show its type - + >>> print(a.__class__) # show its class - + >>> print(type(a) is a.__class__) # its type is its class True >>> a[1] = 3.25 # modify the instance @@ -149,11 +149,11 @@ Introspecting instances of built-in types For instance of built-in types, x.__class__ is now the same as type(x): >>> type([]) - + >>> [].__class__ - + >>> list - + >>> isinstance([], list) True >>> isinstance([], dict) @@ -258,19 +258,19 @@ implicit first argument that is the *class* for which they are invoked. ... print("classmethod", cls, y) >>> C.foo(1) - classmethod 1 + classmethod 1 >>> c = C() >>> c.foo(1) - classmethod 1 + classmethod 1 >>> class D(C): ... pass >>> D.foo(1) - classmethod 1 + classmethod 1 >>> d = D() >>> d.foo(1) - classmethod 1 + classmethod 1 This prints "classmethod __main__.D 1" both times; in other words, the class passed as the first argument of foo() is the class involved in the @@ -286,11 +286,11 @@ But notice this: >>> E.foo(1) E.foo() called - classmethod 1 + classmethod 1 >>> e = E() >>> e.foo(1) E.foo() called - classmethod 1 + classmethod 1 In this example, the call to C.foo() from E.foo() will see class C as its first argument, not class E. This is to be expected, since the call @@ -350,7 +350,7 @@ Hmm -- property is builtin now, so let's try it that way too. >>> del property # unmask the builtin >>> property - + >>> class C(object): ... def __init__(self): @@ -478,7 +478,8 @@ def test_main(verbose=None): # business is used the name can change depending on how the test is # invoked. from test import support, test_descrtut - support.run_doctest(test_descrtut, verbose) + import doctest + support.run_doctest(test_descrtut, verbose, optionflags=doctest.ELLIPSIS) # This part isn't needed for regrtest, but for running the test directly. if __name__ == "__main__": diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py index 2068fb70f8..ec12fe7372 100644 --- a/Lib/test/test_doctest.py +++ b/Lib/test/test_doctest.py @@ -2338,7 +2338,7 @@ def test_DocFileSuite(): `__file__` global, which is set to the name of the file containing the tests: - >>> suite = doctest.DocFileSuite('test_doctest3.txt') + >>> suite = doctest.DocFileSuite('test_doctest3.txt', optionflags=doctest.ELLIPSIS) >>> suite.run(unittest.TestResult()) diff --git a/Lib/test/test_doctest3.txt b/Lib/test/test_doctest3.txt index dd8557e57a..380ea76e4c 100644 --- a/Lib/test/test_doctest3.txt +++ b/Lib/test/test_doctest3.txt @@ -2,4 +2,4 @@ Here we check that `__file__` is provided: >>> type(__file__) - + diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index 9cf115d42b..852173cc0a 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -1697,13 +1697,10 @@ class TestSingleDispatch(unittest.TestCase): c.Container.register(P) with self.assertRaises(RuntimeError) as re_one: g(p) - self.assertIn( - str(re_one.exception), - (("Ambiguous dispatch: " - "or "), - ("Ambiguous dispatch: " - "or ")), - ) + self.assertIn("Ambiguous dispatch:", str(re_one.exception)) + self.assertIn(" " - "or "), - ("Ambiguous dispatch: " - "or ")), - ) + self.assertIn("Ambiguous dispatch:", str(re_two.exception)) + self.assertIn(" " - "or "), - ("Ambiguous dispatch: " - "or ")), - ) + self.assertIn("Ambiguous dispatch:", str(re_three.exception)) + self.assertIn(">> type(g) - + >>> i = g() >>> type(i) - + >>> [s for s in dir(i) if not s.startswith('_')] ['close', 'gi_code', 'gi_frame', 'gi_running', 'gi_yieldfrom', 'send', 'throw'] >>> from test.support import HAVE_DOCSTRINGS @@ -691,7 +691,7 @@ And more, added later. >>> i.gi_running 0 >>> type(i.gi_frame) - + >>> i.gi_running = 42 Traceback (most recent call last): ... @@ -1066,27 +1066,27 @@ These are fine: >>> def f(): ... yield >>> type(f()) - + >>> def f(): ... if 0: ... yield >>> type(f()) - + >>> def f(): ... if 0: ... yield 1 >>> type(f()) - + >>> def f(): ... if "": ... yield None >>> type(f()) - + >>> def f(): ... return @@ -1110,7 +1110,7 @@ These are fine: ... x = 1 ... return >>> type(f()) - + >>> def f(): ... if 0: @@ -1118,7 +1118,7 @@ These are fine: ... yield 1 ... >>> type(f()) - + >>> def f(): ... if 0: @@ -1128,7 +1128,7 @@ These are fine: ... def f(self): ... yield 2 >>> type(f()) - + >>> def f(): ... if 0: @@ -1136,7 +1136,7 @@ These are fine: ... if 0: ... yield 2 >>> type(f()) - + This one caused a crash (see SF bug 567538): @@ -1791,7 +1791,7 @@ And a more sane, but still weird usage: >>> def f(): list(i for i in [(yield 26)]) >>> type(f()) - + A yield expression with augmented assignment. @@ -2047,25 +2047,25 @@ enclosing function a generator: >>> def f(): x += yield >>> type(f()) - + >>> def f(): x = yield >>> type(f()) - + >>> def f(): lambda x=(yield): 1 >>> type(f()) - + >>> def f(): x=(i for i in (yield) if (yield)) >>> type(f()) - + >>> def f(d): d[(yield "a")] = d[(yield "b")] = 27 >>> data = [1,2] >>> g = f(data) >>> type(g) - + >>> g.send(None) 'a' >>> data @@ -2174,8 +2174,9 @@ __test__ = {"tut": tutorial_tests, # so this works as expected in both ways of running regrtest. def test_main(verbose=None): from test import support, test_generators + import doctest support.run_unittest(__name__) - support.run_doctest(test_generators, verbose) + support.run_doctest(test_generators, verbose, optionflags=doctest.ELLIPSIS) # This part isn't needed for regrtest, but for running the test directly. if __name__ == "__main__": diff --git a/Lib/test/test_genexps.py b/Lib/test/test_genexps.py index fb531d6d47..c5e10dda8c 100644 --- a/Lib/test/test_genexps.py +++ b/Lib/test/test_genexps.py @@ -27,7 +27,7 @@ Test first class >>> g = (i*i for i in range(4)) >>> type(g) - + >>> list(g) [0, 1, 4, 9] @@ -269,7 +269,8 @@ else: def test_main(verbose=None): from test import support from test import test_genexps - support.run_doctest(test_genexps, verbose) + import doctest + support.run_doctest(test_genexps, verbose, optionflags=doctest.ELLIPSIS) # verify reference counting if verbose and hasattr(sys, "gettotalrefcount"): diff --git a/Lib/test/test_metaclass.py b/Lib/test/test_metaclass.py index e6fe20a107..be683dd1a1 100644 --- a/Lib/test/test_metaclass.py +++ b/Lib/test/test_metaclass.py @@ -78,7 +78,7 @@ Also pass another keyword. >>> class C(object, metaclass=M, other="haha"): ... pass ... - Prepare called: ('C', (,)) {'other': 'haha'} + Prepare called: ('C', (,)) {'other': 'haha'} New called: {'other': 'haha'} >>> C.__class__ is M True @@ -104,7 +104,7 @@ Use various combinations of explicit keywords and **kwds. >>> kwds = {'metaclass': M, 'other': 'haha'} >>> class C(*bases, **kwds): pass ... - Prepare called: ('C', (,)) {'other': 'haha'} + Prepare called: ('C', (,)) {'other': 'haha'} New called: {'other': 'haha'} >>> C.__class__ is M True @@ -114,7 +114,7 @@ Use various combinations of explicit keywords and **kwds. >>> kwds = {'other': 'haha'} >>> class C(B, metaclass=M, *bases, **kwds): pass ... - Prepare called: ('C', (, )) {'other': 'haha'} + Prepare called: ('C', (, )) {'other': 'haha'} New called: {'other': 'haha'} >>> C.__class__ is M True @@ -259,7 +259,8 @@ else: def test_main(verbose=False): from test import support from test import test_metaclass - support.run_doctest(test_metaclass, verbose) + import doctest + support.run_doctest(test_metaclass, verbose, optionflags=doctest.ELLIPSIS) if __name__ == "__main__": test_main(verbose=True) diff --git a/Lib/test/test_pprint.py b/Lib/test/test_pprint.py index 7ebc298337..2283923cee 100644 --- a/Lib/test/test_pprint.py +++ b/Lib/test/test_pprint.py @@ -848,12 +848,11 @@ bytearray(b'\\x00\\x01\\x02\\x03' def test_default_dict(self): d = collections.defaultdict(int) - self.assertEqual(pprint.pformat(d, width=1), "defaultdict(, {})") + self.assertRegex(pprint.pformat(d, width=1), r"defaultdict\(, {}\)") words = 'the quick brown fox jumped over a lazy dog'.split() d = collections.defaultdict(int, zip(words, itertools.count())) - self.assertEqual(pprint.pformat(d), -"""\ -defaultdict(, + self.assertRegex(pprint.pformat(d), +r"""defaultdict\(, {'a': 6, 'brown': 2, 'dog': 8, @@ -862,7 +861,7 @@ defaultdict(, 'lazy': 7, 'over': 5, 'quick': 1, - 'the': 0})""") + 'the': 0}\)""") def test_counter(self): d = collections.Counter() diff --git a/Lib/test/test_reprlib.py b/Lib/test/test_reprlib.py index 4bf91945ea..2ecea0221e 100644 --- a/Lib/test/test_reprlib.py +++ b/Lib/test/test_reprlib.py @@ -292,8 +292,8 @@ class foo(object): ''') importlib.invalidate_caches() from areallylongpackageandmodulenametotestreprtruncation.areallylongpackageandmodulenametotestreprtruncation import foo - eq(repr(foo.foo), - "" % foo.__name__) + self.assertRegex(repr(foo.foo), + r"" % foo.__name__) @unittest.skip('need a suitable object') def test_object(self): @@ -310,7 +310,7 @@ class bar: importlib.invalidate_caches() from areallylongpackageandmodulenametotestreprtruncation.areallylongpackageandmodulenametotestreprtruncation import bar # Module name may be prefixed with "test.", depending on how run. - self.assertEqual(repr(bar.bar), "" % bar.__name__) + self.assertRegex(repr(bar.bar), r"" % bar.__name__) def test_instance(self): self._check_path_limitations('baz') diff --git a/Lib/test/test_statistics.py b/Lib/test/test_statistics.py index 5275cb681d..5dd50483f2 100644 --- a/Lib/test/test_statistics.py +++ b/Lib/test/test_statistics.py @@ -659,7 +659,7 @@ class DocTests(unittest.TestCase): @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -OO and above") def test_doc_tests(self): - failed, tried = doctest.testmod(statistics) + failed, tried = doctest.testmod(statistics, optionflags=doctest.ELLIPSIS) self.assertGreater(tried, 0) self.assertEqual(failed, 0) diff --git a/Lib/test/test_wsgiref.py b/Lib/test/test_wsgiref.py index b7d02e868c..3977e230b0 100644 --- a/Lib/test/test_wsgiref.py +++ b/Lib/test/test_wsgiref.py @@ -161,10 +161,10 @@ class IntegrationTests(TestCase): self.assertTrue(out.endswith( b"A server error occurred. Please contact the administrator." )) - self.assertEqual( + self.assertRegex( err.splitlines()[-2], - "AssertionError: Headers (('Content-Type', 'text/plain')) must" - " be of type list: " + r"AssertionError: Headers \(\('Content-Type', 'text/plain'\)\) must" + r" be of type list: " ) def test_status_validation_errors(self): @@ -704,3 +704,4 @@ class HandlerTests(TestCase): if __name__ == "__main__": unittest.main() + diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py index 0773a86984..f2fdc44cbe 100644 --- a/Lib/test/test_xmlrpc.py +++ b/Lib/test/test_xmlrpc.py @@ -775,8 +775,8 @@ class SimpleServerTestCase(BaseServerTestCase): # 'method "this_is_not_exists" is not supported'>}] self.assertEqual(result.results[0]['faultCode'], 1) - self.assertEqual(result.results[0]['faultString'], - ':method "this_is_not_exists" ' + self.assertRegex(result.results[0]['faultString'], + ':method "this_is_not_exists" ' 'is not supported') except (xmlrpclib.ProtocolError, OSError) as e: # ignore failures due to non-blocking socket 'unavailable' errors -- cgit v1.2.1 From 72e1a1c89b50ad4a6b3a89315f76c33666b88825 Mon Sep 17 00:00:00 2001 From: Kushal Das Date: Sat, 4 Jun 2016 16:24:05 -0700 Subject: Fixes whitespace issue --- Lib/test/test_wsgiref.py | 1 - 1 file changed, 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_wsgiref.py b/Lib/test/test_wsgiref.py index 3977e230b0..7e7a836845 100644 --- a/Lib/test/test_wsgiref.py +++ b/Lib/test/test_wsgiref.py @@ -704,4 +704,3 @@ class HandlerTests(TestCase): if __name__ == "__main__": unittest.main() - -- cgit v1.2.1 From bde090754012febf123ce9706cbe56a9ec245283 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Sun, 5 Jun 2016 21:32:45 -0400 Subject: Issue #27156: Remove more unused idlelib code. --- Lib/idlelib/macosx.py | 10 ---------- Lib/idlelib/stackviewer.py | 3 --- 2 files changed, 13 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/macosx.py b/Lib/idlelib/macosx.py index 618cc2631b..8c50a598b2 100644 --- a/Lib/idlelib/macosx.py +++ b/Lib/idlelib/macosx.py @@ -5,16 +5,6 @@ import sys import tkinter import warnings -def runningAsOSXApp(): - warnings.warn("runningAsOSXApp() is deprecated, use isAquaTk()", - DeprecationWarning, stacklevel=2) - return isAquaTk() - -def isCarbonAquaTk(root): - warnings.warn("isCarbonAquaTk(root) is deprecated, use isCarbonTk()", - DeprecationWarning, stacklevel=2) - return isCarbonTk() - _tk_type = None def _initializeTkVariantTests(root): diff --git a/Lib/idlelib/stackviewer.py b/Lib/idlelib/stackviewer.py index 5c188f07e7..87c964e7af 100644 --- a/Lib/idlelib/stackviewer.py +++ b/Lib/idlelib/stackviewer.py @@ -120,9 +120,6 @@ class VariablesTreeItem(ObjectTreeItem): sublist.append(item) return sublist - def keys(self): # unused, left for possible 3rd party use - return list(self.object.keys()) - def _stack_viewer(parent): root = tk.Tk() root.title("Test StackViewer") -- cgit v1.2.1 From 11551dad75b93b405b8a6a4a9847f91f686f6a53 Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Mon, 6 Jun 2016 01:53:28 +0000 Subject: Issue #27105: Add cgi.test() to __all__, based on Jacek Ko?odziej?s patch --- Lib/cgi.py | 2 +- Lib/test/test_cgi.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/cgi.py b/Lib/cgi.py index 189c6d5b4a..233a496e81 100755 --- a/Lib/cgi.py +++ b/Lib/cgi.py @@ -45,7 +45,7 @@ import tempfile __all__ = ["MiniFieldStorage", "FieldStorage", "parse", "parse_qs", "parse_qsl", "parse_multipart", - "parse_header", "print_exception", "print_environ", + "parse_header", "test", "print_exception", "print_environ", "print_form", "print_directory", "print_arguments", "print_environ_usage", "escape"] diff --git a/Lib/test/test_cgi.py b/Lib/test/test_cgi.py index ab9f6ab6a5..164784923a 100644 --- a/Lib/test/test_cgi.py +++ b/Lib/test/test_cgi.py @@ -7,6 +7,7 @@ import unittest import warnings from collections import namedtuple from io import StringIO, BytesIO +from test import support class HackedSysModule: # The regression test will have real values in sys.argv, which @@ -473,6 +474,11 @@ this is the content of the fake file cgi.parse_header('form-data; name="files"; filename="fo\\"o;bar"'), ("form-data", {"name": "files", "filename": 'fo"o;bar'})) + def test_all(self): + blacklist = {"logfile", "logfp", "initlog", "dolog", "nolog", + "closelog", "log", "maxlen", "valid_boundary"} + support.check__all__(self, cgi, blacklist=blacklist) + BOUNDARY = "---------------------------721837373350705526688164684" -- cgit v1.2.1 From d7171759da0aadc50e4eda8a5690b123e33bd33b Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Mon, 6 Jun 2016 01:56:09 +0000 Subject: Issue #27107: Add exception classes to mailbox.__all__, by Jacek Ko?odziej --- Lib/mailbox.py | 7 ++++--- Lib/test/test_mailbox.py | 8 +++++++- 2 files changed, 11 insertions(+), 4 deletions(-) (limited to 'Lib') diff --git a/Lib/mailbox.py b/Lib/mailbox.py index 0270e25b00..0e23987ce7 100644 --- a/Lib/mailbox.py +++ b/Lib/mailbox.py @@ -23,9 +23,10 @@ try: except ImportError: fcntl = None -__all__ = [ 'Mailbox', 'Maildir', 'mbox', 'MH', 'Babyl', 'MMDF', - 'Message', 'MaildirMessage', 'mboxMessage', 'MHMessage', - 'BabylMessage', 'MMDFMessage'] +__all__ = ['Mailbox', 'Maildir', 'mbox', 'MH', 'Babyl', 'MMDF', + 'Message', 'MaildirMessage', 'mboxMessage', 'MHMessage', + 'BabylMessage', 'MMDFMessage', 'Error', 'NoSuchMailboxError', + 'NotEmptyError', 'ExternalClashError', 'FormatError'] linesep = os.linesep.encode('ascii') diff --git a/Lib/test/test_mailbox.py b/Lib/test/test_mailbox.py index 1f30fa6e8f..21bec9d6bb 100644 --- a/Lib/test/test_mailbox.py +++ b/Lib/test/test_mailbox.py @@ -2268,12 +2268,18 @@ Gregory K. Johnson """) +class MiscTestCase(unittest.TestCase): + def test__all__(self): + blacklist = {"linesep"} + support.check__all__(self, mailbox, blacklist=blacklist) + + def test_main(): tests = (TestMailboxSuperclass, TestMaildir, TestMbox, TestMMDF, TestMH, TestBabyl, TestMessage, TestMaildirMessage, TestMboxMessage, TestMHMessage, TestBabylMessage, TestMMDFMessage, TestMessageConversion, TestProxyFile, TestPartialFile, - MaildirTestCase, TestFakeMailBox) + MaildirTestCase, TestFakeMailBox, MiscTestCase) support.run_unittest(*tests) support.reap_children() -- cgit v1.2.1 From 75ad320ffdeeb2fd8c26340ec82f9d738dd4dfcf Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Mon, 6 Jun 2016 01:59:19 +0000 Subject: Issue #27108: Add missing names to mimetypes.__all__, by Jacek Ko?odziej --- Lib/mimetypes.py | 6 ++++-- Lib/test/test_mimetypes.py | 6 ++++++ 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/mimetypes.py b/Lib/mimetypes.py index 0be76ad4f7..9a886803dc 100644 --- a/Lib/mimetypes.py +++ b/Lib/mimetypes.py @@ -33,8 +33,10 @@ except ImportError: _winreg = None __all__ = [ - "guess_type","guess_extension","guess_all_extensions", - "add_type","read_mime_types","init" + "knownfiles", "inited", "MimeTypes", + "guess_type", "guess_all_extensions", "guess_extension", + "add_type", "init", "read_mime_types", + "suffix_map", "encodings_map", "types_map", "common_types" ] knownfiles = [ diff --git a/Lib/test/test_mimetypes.py b/Lib/test/test_mimetypes.py index 68565930a4..4e2c9089b5 100644 --- a/Lib/test/test_mimetypes.py +++ b/Lib/test/test_mimetypes.py @@ -101,5 +101,11 @@ class Win32MimeTypesTestCase(unittest.TestCase): eq(self.db.guess_type("image.jpg"), ("image/jpeg", None)) eq(self.db.guess_type("image.png"), ("image/png", None)) + +class MiscTestCase(unittest.TestCase): + def test__all__(self): + support.check__all__(self, mimetypes) + + if __name__ == "__main__": unittest.main() -- cgit v1.2.1 From f6fa750a89fcd193234eb3a4fe35b983a74bf3e7 Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Mon, 6 Jun 2016 02:00:50 +0000 Subject: Issue #27109: Add InvalidFileException to __all__, by Jacek Ko?odziej --- Lib/plistlib.py | 2 +- Lib/test/test_plistlib.py | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/plistlib.py b/Lib/plistlib.py index b66639ca9e..4f360f3220 100644 --- a/Lib/plistlib.py +++ b/Lib/plistlib.py @@ -47,7 +47,7 @@ Parse Plist example: """ __all__ = [ "readPlist", "writePlist", "readPlistFromBytes", "writePlistToBytes", - "Plist", "Data", "Dict", "FMT_XML", "FMT_BINARY", + "Plist", "Data", "Dict", "InvalidFileException", "FMT_XML", "FMT_BINARY", "load", "dump", "loads", "dumps" ] diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py index 662ed904b0..60ff9183c2 100644 --- a/Lib/test/test_plistlib.py +++ b/Lib/test/test_plistlib.py @@ -526,8 +526,14 @@ class TestPlistlibDeprecated(unittest.TestCase): self.assertEqual(cur, in_data) +class MiscTestCase(unittest.TestCase): + def test__all__(self): + blacklist = {"PlistFormat", "PLISTHEADER"} + support.check__all__(self, plistlib, blacklist=blacklist) + + def test_main(): - support.run_unittest(TestPlistlib, TestPlistlibDeprecated) + support.run_unittest(TestPlistlib, TestPlistlibDeprecated, MiscTestCase) if __name__ == '__main__': -- cgit v1.2.1 From 208bfa055a9777d6ba270bd2d60ad44282488cfa Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Mon, 6 Jun 2016 02:03:11 +0000 Subject: Issue #27110: Add smtpd.SMTPChannel to __all__, by Jacek Ko?odziej --- Lib/smtpd.py | 5 ++++- Lib/test/test_smtpd.py | 11 +++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/smtpd.py b/Lib/smtpd.py index 5f3b05eff8..8103ca9af0 100755 --- a/Lib/smtpd.py +++ b/Lib/smtpd.py @@ -89,7 +89,10 @@ import collections from warnings import warn from email._header_value_parser import get_addr_spec, get_angle_addr -__all__ = ["SMTPServer","DebuggingServer","PureProxy","MailmanProxy"] +__all__ = [ + "SMTPChannel", "SMTPServer", "DebuggingServer", "PureProxy", + "MailmanProxy", +] program = sys.argv[0] __version__ = 'Python SMTP proxy version 0.3' diff --git a/Lib/test/test_smtpd.py b/Lib/test/test_smtpd.py index d92c9b3b02..3eebe948ad 100644 --- a/Lib/test/test_smtpd.py +++ b/Lib/test/test_smtpd.py @@ -998,5 +998,16 @@ class SMTPDChannelTestWithEnableSMTPUTF8True(unittest.TestCase): self.write_line(b'test\r\n.') self.assertEqual(self.channel.socket.last[0:3], b'250') + +class MiscTestCase(unittest.TestCase): + def test__all__(self): + blacklist = { + "program", "Devnull", "DEBUGSTREAM", "NEWLINE", "COMMASPACE", + "DATA_SIZE_DEFAULT", "usage", "Options", "parseargs", + + } + support.check__all__(self, smtpd, blacklist=blacklist) + + if __name__ == "__main__": unittest.main() -- cgit v1.2.1 From cf193109f4c0f590e3a7b0113d0ffb1138848e15 Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Mon, 6 Jun 2016 02:49:54 +0000 Subject: Issue #27107: mailbox.fcntl = None on Windows --- Lib/test/test_mailbox.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_mailbox.py b/Lib/test/test_mailbox.py index 21bec9d6bb..aeabdbb2b5 100644 --- a/Lib/test/test_mailbox.py +++ b/Lib/test/test_mailbox.py @@ -2270,7 +2270,7 @@ Gregory K. Johnson class MiscTestCase(unittest.TestCase): def test__all__(self): - blacklist = {"linesep"} + blacklist = {"linesep", "fcntl"} support.check__all__(self, mailbox, blacklist=blacklist) -- cgit v1.2.1 From 02d2c4e6a0a1f22af54a06913f5615c384ffaa52 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 6 Jun 2016 13:00:03 +0300 Subject: Issue #26983: Fixed test_format failure. Patch by SilentGhost. --- Lib/test/test_format.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_format.py b/Lib/test/test_format.py index 9924fde13e..8afd5b8f1f 100644 --- a/Lib/test/test_format.py +++ b/Lib/test/test_format.py @@ -274,7 +274,7 @@ class FormatTest(unittest.TestCase): test_exc('%d', '1', TypeError, "%d format: a number is required, not str") test_exc('%x', '1', TypeError, "%x format: an integer is required, not str") test_exc('%x', 3.14, TypeError, "%x format: an integer is required, not float") - test_exc('%g', '1', TypeError, "a float is required") + test_exc('%g', '1', TypeError, "must be real number, not str") test_exc('no format', '1', TypeError, "not all arguments converted during string formatting") test_exc('%c', -1, OverflowError, "%c arg not in range(0x110000)") -- cgit v1.2.1 From 9b459ec20e055bc75da059b05c8b091ff9e4c208 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Wed, 8 Jun 2016 14:37:05 -0400 Subject: Issue #27262: move Aqua unbinding code, which enable context menus, to maxosx. --- Lib/idlelib/macosx.py | 11 +++++++++++ Lib/idlelib/pyshell.py | 8 -------- 2 files changed, 11 insertions(+), 8 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/macosx.py b/Lib/idlelib/macosx.py index 8c50a598b2..9d75631388 100644 --- a/Lib/idlelib/macosx.py +++ b/Lib/idlelib/macosx.py @@ -206,6 +206,16 @@ def overrideRootMenu(root, flist): # remove redundant "IDLE Help" from menu del mainmenu.menudefs[-1][1][0] +def fixb2context(root): + '''Removed bad AquaTk Button-2 (right) and Paste bindings. + + They prevent context menu access and seem to be gone in AquaTk8.6. + See issue #24801. + ''' + root.unbind_class('Text', '') + root.unbind_class('Text', '') + root.unbind_class('Text', '<>') + def setupApp(root, flist): """ Perform initial OS X customizations if needed. @@ -227,3 +237,4 @@ def setupApp(root, flist): hideTkConsole(root) overrideRootMenu(root, flist) addOpenEventSupport(root, flist) + fixb2context() diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py index 8341cd952b..9fc46caa63 100755 --- a/Lib/idlelib/pyshell.py +++ b/Lib/idlelib/pyshell.py @@ -1548,14 +1548,6 @@ def main(): flist = PyShellFileList(root) macosx.setupApp(root, flist) - if macosx.isAquaTk(): - # There are some screwed up <2> class bindings for text - # widgets defined in Tk which we need to do away with. - # See issue #24801. - root.unbind_class('Text', '') - root.unbind_class('Text', '') - root.unbind_class('Text', '<>') - if enable_edit: if not (cmd or script): for filename in args[:]: -- cgit v1.2.1 From 71253d45eb3dabdda906ac508fbc0d746e8c31aa Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Wed, 8 Jun 2016 18:09:22 -0400 Subject: Issue #27239: idlelib.macosx.isXyzTk functions initialize as needed. --- Lib/idlelib/idle_test/htest.py | 4 +- Lib/idlelib/idle_test/test_configdialog.py | 2 - Lib/idlelib/idle_test/test_macosx.py | 69 ++++++++++++++++++++++++++++++ Lib/idlelib/macosx.py | 19 +++++--- 4 files changed, 84 insertions(+), 10 deletions(-) create mode 100644 Lib/idlelib/idle_test/test_macosx.py (limited to 'Lib') diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py index 4675645962..686ccc5fd5 100644 --- a/Lib/idlelib/idle_test/htest.py +++ b/Lib/idlelib/idle_test/htest.py @@ -66,7 +66,7 @@ outwin.OutputWindow (indirectly being tested with grep test) ''' from importlib import import_module -from idlelib.macosx import _initializeTkVariantTests +from idlelib.macosx import _init_tk_type import tkinter as tk AboutDialog_spec = { @@ -337,7 +337,7 @@ def run(*tests): root = tk.Tk() root.title('IDLE htest') root.resizable(0, 0) - _initializeTkVariantTests(root) + _init_tk_type(root) # a scrollable Label like constant width text widget. frameLabel = tk.Frame(root, padx=10) diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py index 4f5d216af8..1801a7d6c2 100644 --- a/Lib/idlelib/idle_test/test_configdialog.py +++ b/Lib/idlelib/idle_test/test_configdialog.py @@ -8,14 +8,12 @@ from test.support import requires requires('gui') from tkinter import Tk import unittest -from idlelib import macosx class ConfigDialogTest(unittest.TestCase): @classmethod def setUpClass(cls): cls.root = Tk() - macosx._initializeTkVariantTests(cls.root) @classmethod def tearDownClass(cls): diff --git a/Lib/idlelib/idle_test/test_macosx.py b/Lib/idlelib/idle_test/test_macosx.py new file mode 100644 index 0000000000..0f90fb65f1 --- /dev/null +++ b/Lib/idlelib/idle_test/test_macosx.py @@ -0,0 +1,69 @@ +'''Test idlelib.macosx.py +''' +from idlelib import macosx +from test.support import requires +import sys +import tkinter as tk +import unittest +import unittest.mock as mock + +MAC = sys.platform == 'darwin' +mactypes = {'carbon', 'cocoa', 'xquartz'} +nontypes = {'other'} +alltypes = mactypes | nontypes + + +class InitTktypeTest(unittest.TestCase): + "Test _init_tk_type." + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = tk.Tk() + + @classmethod + def tearDownClass(cls): + cls.root.update_idletasks() + cls.root.destroy() + del cls.root + + def test_init_sets_tktype(self): + "Test that _init_tk_type sets _tk_type according to platform." + for root in (None, self.root): + with self.subTest(root=root): + macosx._tk_type == None + macosx._init_tk_type(root) + self.assertIn(macosx._tk_type, + mactypes if MAC else nontypes) + + +class IsTypeTkTest(unittest.TestCase): + "Test each of the four isTypeTk predecates." + isfuncs = ((macosx.isAquaTk, ('carbon', 'cocoa')), + (macosx.isCarbonTk, ('carbon')), + (macosx.isCocoaTk, ('cocoa')), + (macosx.isXQuartz, ('xquartz')), + ) + + @mock.patch('idlelib.macosx._init_tk_type') + def test_is_calls_init(self, mockinit): + "Test that each isTypeTk calls _init_tk_type when _tk_type is None." + macosx._tk_type = None + for func, whentrue in self.isfuncs: + with self.subTest(func=func): + func() + self.assertTrue(mockinit.called) + mockinit.reset_mock() + + def test_isfuncs(self): + "Test that each isTypeTk return correct bool." + for func, whentrue in self.isfuncs: + for tktype in alltypes: + with self.subTest(func=func, whentrue=whentrue, tktype=tktype): + macosx._tk_type = tktype + (self.assertTrue if tktype in whentrue else self.assertFalse)\ + (func()) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/Lib/idlelib/macosx.py b/Lib/idlelib/macosx.py index 9d75631388..4e4dcd6ae7 100644 --- a/Lib/idlelib/macosx.py +++ b/Lib/idlelib/macosx.py @@ -7,13 +7,14 @@ import warnings _tk_type = None -def _initializeTkVariantTests(root): +def _init_tk_type(idleroot=None): """ Initializes OS X Tk variant values for isAquaTk(), isCarbonTk(), isCocoaTk(), and isXQuartz(). """ global _tk_type if sys.platform == 'darwin': + root = idleroot or tkinter.Tk() ws = root.tk.call('tk', 'windowingsystem') if 'x11' in ws: _tk_type = "xquartz" @@ -23,6 +24,8 @@ def _initializeTkVariantTests(root): _tk_type = "cocoa" else: _tk_type = "carbon" + if not idleroot: + root.destroy else: _tk_type = "other" @@ -30,7 +33,8 @@ def isAquaTk(): """ Returns True if IDLE is using a native OS X Tk (Cocoa or Carbon). """ - assert _tk_type is not None + if not _tk_type: + _init_tk_type() return _tk_type == "cocoa" or _tk_type == "carbon" def isCarbonTk(): @@ -38,21 +42,24 @@ def isCarbonTk(): Returns True if IDLE is using a Carbon Aqua Tk (instead of the newer Cocoa Aqua Tk). """ - assert _tk_type is not None + if not _tk_type: + _init_tk_type() return _tk_type == "carbon" def isCocoaTk(): """ Returns True if IDLE is using a Cocoa Aqua Tk. """ - assert _tk_type is not None + if not _tk_type: + _init_tk_type() return _tk_type == "cocoa" def isXQuartz(): """ Returns True if IDLE is using an OS X X11 Tk. """ - assert _tk_type is not None + if not _tk_type: + _init_tk_type() return _tk_type == "xquartz" def tkVersionWarning(root): @@ -232,7 +239,7 @@ def setupApp(root, flist): isAquaTk(), isCarbonTk(), isCocoaTk(), isXQuartz() functions which are initialized here as well. """ - _initializeTkVariantTests(root) + _init_tk_type(root) if isAquaTk(): hideTkConsole(root) overrideRootMenu(root, flist) -- cgit v1.2.1 From 40ffd98696e42ac9b37040ff24713a08ea558620 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Thu, 9 Jun 2016 14:29:25 -0700 Subject: Fix some PEP 8 violations. --- Lib/os.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) (limited to 'Lib') diff --git a/Lib/os.py b/Lib/os.py index 56a2f7187b..67e1992836 100644 --- a/Lib/os.py +++ b/Lib/os.py @@ -876,8 +876,7 @@ def _fscodec(): errors = 'surrogateescape' def fsencode(filename): - """ - Encode filename (an os.PathLike, bytes, or str) to the filesystem + """Encode filename (an os.PathLike, bytes, or str) to the filesystem encoding with 'surrogateescape' error handler, return bytes unchanged. On Windows, use 'strict' error handler if the file system encoding is 'mbcs' (which is the default encoding). @@ -892,8 +891,7 @@ def _fscodec(): + type(filename).__name__) def fsdecode(filename): - """ - Decode filename (an os.PathLike, bytes, or str) from the filesystem + """Decode filename (an os.PathLike, bytes, or str) from the filesystem encoding with 'surrogateescape' error handler, return str unchanged. On Windows, use 'strict' error handler if the file system encoding is 'mbcs' (which is the default encoding). @@ -1127,14 +1125,12 @@ if not _exists('fspath'): + path_type.__name__) class PathLike(abc.ABC): - """ - Abstract base class for implementing the file system path protocol. - """ + + """Abstract base class for implementing the file system path protocol.""" + @abc.abstractmethod def __fspath__(self): - """ - Return the file system path representation of the object. - """ + """Return the file system path representation of the object.""" raise NotImplementedError @classmethod -- cgit v1.2.1 From 29aba49ff2809720eae7a299b1c89843b5572de2 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Thu, 9 Jun 2016 21:04:09 -0400 Subject: Issue #24759: Add test for IDLE syntax colorizoer. --- Lib/idlelib/idle_test/test_colorizer.py | 56 +++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 Lib/idlelib/idle_test/test_colorizer.py (limited to 'Lib') diff --git a/Lib/idlelib/idle_test/test_colorizer.py b/Lib/idlelib/idle_test/test_colorizer.py new file mode 100644 index 0000000000..238bc3e114 --- /dev/null +++ b/Lib/idlelib/idle_test/test_colorizer.py @@ -0,0 +1,56 @@ +'''Test idlelib/colorizer.py + +Perform minimal sanity checks that module imports and some things run. + +Coverage 22%. +''' +from idlelib import colorizer # always test import +from test.support import requires +from tkinter import Tk, Text +import unittest + + +class FunctionTest(unittest.TestCase): + + def test_any(self): + self.assertTrue(colorizer.any('test', ('a', 'b'))) + + def test_make_pat(self): + self.assertTrue(colorizer.make_pat()) + + +class ColorConfigTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = Tk() + cls.text = Text(cls.root) + + @classmethod + def tearDownClass(cls): + del cls.text + cls.root.destroy() + del cls.root + + def test_colorizer(self): + colorizer.color_config(self.text) + +class ColorDelegatorTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = Tk() + + @classmethod + def tearDownClass(cls): + cls.root.destroy() + del cls.root + + def test_colorizer(self): + colorizer.ColorDelegator() + + +if __name__ == '__main__': + unittest.main(verbosity=2) -- cgit v1.2.1 From 913c101eab980f097f4b18211f2deed1e53535d6 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Thu, 9 Jun 2016 21:09:15 -0400 Subject: Issue #24759: IDLE requires tk 8.5 and availability ttk widgets. Delete now unneeded tk version tests and code for older versions. --- Lib/idlelib/__init__.py | 1 + Lib/idlelib/colorizer.py | 11 +++++------ Lib/idlelib/config.py | 20 ++++++++------------ Lib/idlelib/editor.py | 11 ++++------- Lib/idlelib/idle_test/__init__.py | 2 ++ Lib/idlelib/macosx.py | 6 ------ Lib/idlelib/pyshell.py | 25 ++++++++++++++++--------- Lib/test/test_idle.py | 2 ++ 8 files changed, 38 insertions(+), 40 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/__init__.py b/Lib/idlelib/__init__.py index 711f61bb69..fef21bee14 100644 --- a/Lib/idlelib/__init__.py +++ b/Lib/idlelib/__init__.py @@ -1,6 +1,7 @@ """The idlelib package implements the Idle application. Idle includes an interactive shell and editor. +Starting with Python 3.6, IDLE requires tcl/tk 8.5 or later. Use the files named idle.* to start Idle. The other files are private implementations. Their details are subject to diff --git a/Lib/idlelib/colorizer.py b/Lib/idlelib/colorizer.py index ec84b81a72..5b6dc67594 100644 --- a/Lib/idlelib/colorizer.py +++ b/Lib/idlelib/colorizer.py @@ -2,7 +2,6 @@ import time import re import keyword import builtins -from tkinter import TkVersion from idlelib.delegator import Delegator from idlelib.config import idleConf @@ -49,11 +48,8 @@ def color_config(text): # Called from htest, Editor, and Turtle Demo. insertbackground=cursor_color, selectforeground=select_colors['foreground'], selectbackground=select_colors['background'], - ) - if TkVersion >= 8.5: - text.config( - inactiveselectbackground=select_colors['background']) - + inactiveselectbackground=select_colors['background'], # new in 8.5 + ) class ColorDelegator(Delegator): @@ -277,5 +273,8 @@ def _color_delegator(parent): # htest # p.insertfilter(d) if __name__ == "__main__": + import unittest + unittest.main('idlelib.idle_test.test_colorizer', + verbosity=2, exit=False) from idlelib.idle_test.htest import run run(_color_delegator) diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index b9e1c6df00..4d87de0605 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -22,7 +22,6 @@ import os import sys from configparser import ConfigParser -from tkinter import TkVersion from tkinter.font import Font, nametofont class InvalidConfigType(Exception): pass @@ -713,16 +712,13 @@ class IdleConf: bold = self.GetOption(configType, section, 'font-bold', default=0, type='bool') if (family == 'TkFixedFont'): - if TkVersion < 8.5: - family = 'Courier' - else: - f = Font(name='TkFixedFont', exists=True, root=root) - actualFont = Font.actual(f) - family = actualFont['family'] - size = actualFont['size'] - if size <= 0: - size = 10 # if font in pixels, ignore actual size - bold = actualFont['weight']=='bold' + f = Font(name='TkFixedFont', exists=True, root=root) + actualFont = Font.actual(f) + family = actualFont['family'] + size = actualFont['size'] + if size <= 0: + size = 10 # if font in pixels, ignore actual size + bold = actualFont['weight'] == 'bold' return (family, size, 'bold' if bold else 'normal') def LoadCfgFiles(self): @@ -740,7 +736,7 @@ class IdleConf: idleConf = IdleConf() # TODO Revise test output, write expanded unittest -### module test +# if __name__ == '__main__': def dumpCfg(cfg): print('\n', cfg, '\n') diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index b214c6ab9b..d04fc08e47 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -110,13 +110,10 @@ class EditorWindow(object): 'wrap': 'none', 'highlightthickness': 0, 'width': self.width, - 'height': idleConf.GetOption('main', 'EditorWindow', - 'height', type='int')} - if TkVersion >= 8.5: - # Starting with tk 8.5 we have to set the new tabstyle option - # to 'wordprocessor' to achieve the same display of tabs as in - # older tk versions. - text_options['tabstyle'] = 'wordprocessor' + 'tabstyle': 'wordprocessor', # new in 8.5 + 'height': idleConf.GetOption( + 'main', 'EditorWindow', 'height', type='int'), + } self.text = text = MultiCallCreator(Text)(text_frame, **text_options) self.top.focused_widget = self.text diff --git a/Lib/idlelib/idle_test/__init__.py b/Lib/idlelib/idle_test/__init__.py index 845c92d372..ad067b405c 100644 --- a/Lib/idlelib/idle_test/__init__.py +++ b/Lib/idlelib/idle_test/__init__.py @@ -1,6 +1,8 @@ '''idlelib.idle_test is a private implementation of test.test_idle, which tests the IDLE application as part of the stdlib test suite. Run IDLE tests alone with "python -m test.test_idle". +Starting with Python 3.6, IDLE requires tcl/tk 8.5 or later. + This package and its contained modules are subject to change and any direct use is at your own risk. ''' diff --git a/Lib/idlelib/macosx.py b/Lib/idlelib/macosx.py index 4e4dcd6ae7..b16e0523c0 100644 --- a/Lib/idlelib/macosx.py +++ b/Lib/idlelib/macosx.py @@ -199,12 +199,6 @@ def overrideRootMenu(root, flist): ('About IDLE', '<>'), None, ])) - tkversion = root.tk.eval('info patchlevel') - if tuple(map(int, tkversion.split('.'))) < (8, 4, 14): - # for earlier AquaTk versions, supply a Preferences menu item - mainmenu.menudefs[0][1].append( - ('_Preferences....', '<>'), - ) if isCocoaTk(): # replace default About dialog with About IDLE one root.createcommand('tkAboutDialog', about_dialog) diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py index 9fc46caa63..38c12cd8bf 100755 --- a/Lib/idlelib/pyshell.py +++ b/Lib/idlelib/pyshell.py @@ -1,5 +1,20 @@ #! /usr/bin/env python3 +try: + from tkinter import * +except ImportError: + print("** IDLE can't import Tkinter.\n" + "Your Python may not be configured for Tk. **", file=sys.__stderr__) + sys.exit(1) +import tkinter.messagebox as tkMessageBox +if TkVersion < 8.5: + root = Tk() # otherwise create root in main + root.withdraw() + tkMessageBox.showerror("Idle Cannot Start", + "Idle requires tcl/tk 8.5+, not $s." % TkVersion, + parent=root) + sys.exit(1) + import getopt import os import os.path @@ -16,14 +31,6 @@ import linecache from code import InteractiveInterpreter from platform import python_version, system -try: - from tkinter import * -except ImportError: - print("** IDLE can't import Tkinter.\n" - "Your Python may not be configured for Tk. **", file=sys.__stderr__) - sys.exit(1) -import tkinter.messagebox as tkMessageBox - from idlelib.editor import EditorWindow, fixwordbreaks from idlelib.filelist import FileList from idlelib.colorizer import ColorDelegator @@ -1536,7 +1543,7 @@ def main(): if system() == 'Windows': iconfile = os.path.join(icondir, 'idle.ico') root.wm_iconbitmap(default=iconfile) - elif TkVersion >= 8.5: + else: ext = '.png' if TkVersion >= 8.6 else '.gif' iconfiles = [os.path.join(icondir, 'idle_%d%s' % (size, ext)) for size in (16, 32, 48)] diff --git a/Lib/test/test_idle.py b/Lib/test/test_idle.py index 0b34e97ccc..46f1a5c3ec 100644 --- a/Lib/test/test_idle.py +++ b/Lib/test/test_idle.py @@ -4,6 +4,8 @@ from test.support import import_module # Skip test if _thread or _tkinter wasn't built or idlelib was deleted. import_module('threading') # imported by PyShell, imports _thread tk = import_module('tkinter') # imports _tkinter +if tk.TkVersion < 8.5: + raise unittest.SkipTest("IDLE requires tk 8.5 or later.") idletest = import_module('idlelib.idle_test') # Without test_main present, regrtest.runtest_inner (line1219) calls -- cgit v1.2.1 From 6562977b6a026761071df890854ac71d446bb3b1 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 9 Jun 2016 16:30:29 +0300 Subject: Issue #26282: PyArg_ParseTupleAndKeywords() and Argument Clinic now support positional-only and keyword parameters in the same function. --- Lib/test/test_capi.py | 25 +++++++++++++++++++++++++ Lib/test/test_getargs2.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index a0746f097a..6852381d01 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -527,6 +527,31 @@ class SkipitemTest(unittest.TestCase): self.assertRaises(ValueError, _testcapi.parse_tuple_and_keywords, (), {}, b'', [42]) + def test_positional_only(self): + parse = _testcapi.parse_tuple_and_keywords + + parse((1, 2, 3), {}, b'OOO', ['', '', 'a']) + parse((1, 2), {'a': 3}, b'OOO', ['', '', 'a']) + with self.assertRaisesRegex(TypeError, + 'Function takes at least 2 positional arguments \(1 given\)'): + parse((1,), {'a': 3}, b'OOO', ['', '', 'a']) + parse((1,), {}, b'O|OO', ['', '', 'a']) + with self.assertRaisesRegex(TypeError, + 'Function takes at least 1 positional arguments \(0 given\)'): + parse((), {}, b'O|OO', ['', '', 'a']) + parse((1, 2), {'a': 3}, b'OO$O', ['', '', 'a']) + with self.assertRaisesRegex(TypeError, + 'Function takes exactly 2 positional arguments \(1 given\)'): + parse((1,), {'a': 3}, b'OO$O', ['', '', 'a']) + parse((1,), {}, b'O|O$O', ['', '', 'a']) + with self.assertRaisesRegex(TypeError, + 'Function takes at least 1 positional arguments \(0 given\)'): + parse((), {}, b'O|O$O', ['', '', 'a']) + with self.assertRaisesRegex(SystemError, 'Empty parameter name after \$'): + parse((1,), {}, b'O|$OO', ['', '', 'a']) + with self.assertRaisesRegex(SystemError, 'Empty keyword'): + parse((1,), {}, b'O|OO', ['', 'a', '']) + @unittest.skipUnless(threading, 'Threading required for this test.') class TestThreadState(unittest.TestCase): diff --git a/Lib/test/test_getargs2.py b/Lib/test/test_getargs2.py index ecc19088ff..16e163a964 100644 --- a/Lib/test/test_getargs2.py +++ b/Lib/test/test_getargs2.py @@ -658,6 +658,39 @@ class KeywordOnly_TestCase(unittest.TestCase): getargs_keyword_only(1, 2, **{'\uDC80': 10}) +class PositionalOnlyAndKeywords_TestCase(unittest.TestCase): + from _testcapi import getargs_positional_only_and_keywords as getargs + + def test_positional_args(self): + # using all possible positional args + self.assertEqual(self.getargs(1, 2, 3), (1, 2, 3)) + + def test_mixed_args(self): + # positional and keyword args + self.assertEqual(self.getargs(1, 2, keyword=3), (1, 2, 3)) + + def test_optional_args(self): + # missing optional args + self.assertEqual(self.getargs(1, 2), (1, 2, -1)) + self.assertEqual(self.getargs(1, keyword=3), (1, -1, 3)) + + def test_required_args(self): + self.assertEqual(self.getargs(1), (1, -1, -1)) + # required positional arg missing + with self.assertRaisesRegex(TypeError, + "Function takes at least 1 positional arguments \(0 given\)"): + self.getargs() + + with self.assertRaisesRegex(TypeError, + "Function takes at least 1 positional arguments \(0 given\)"): + self.getargs(keyword=3) + + def test_empty_keyword(self): + with self.assertRaisesRegex(TypeError, + "'' is an invalid keyword argument for this function"): + self.getargs(1, 2, **{'': 666}) + + class Bytes_TestCase(unittest.TestCase): def test_c(self): from _testcapi import getargs_c -- cgit v1.2.1 From 8b181a95597c521835b5701d1d9c91d712375b68 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Fri, 10 Jun 2016 12:20:49 -0700 Subject: Issue #27186: Add os.PathLike support to pathlib. This adds support both to pathlib.PurePath's constructor as well as implementing __fspath__(). This removes the provisional status for pathlib. Initial patch by Dusty Phillips. --- Lib/pathlib.py | 22 ++++++++++++++++------ Lib/test/test_pathlib.py | 11 +++++++++++ 2 files changed, 27 insertions(+), 6 deletions(-) (limited to 'Lib') diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 1480e2fc71..a06676fbe4 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -634,13 +634,16 @@ class PurePath(object): for a in args: if isinstance(a, PurePath): parts += a._parts - elif isinstance(a, str): - # Force-cast str subclasses to str (issue #21127) - parts.append(str(a)) else: - raise TypeError( - "argument should be a path or str object, not %r" - % type(a)) + a = os.fspath(a) + if isinstance(a, str): + # Force-cast str subclasses to str (issue #21127) + parts.append(str(a)) + else: + raise TypeError( + "argument should be a str object or an os.PathLike " + "object returning str, not %r" + % type(a)) return cls._flavour.parse_parts(parts) @classmethod @@ -693,6 +696,9 @@ class PurePath(object): self._parts) or '.' return self._str + def __fspath__(self): + return str(self) + def as_posix(self): """Return the string representation of the path with forward (/) slashes.""" @@ -943,6 +949,10 @@ class PurePath(object): return False return True +# Can't subclass os.PathLike from PurePath and keep the constructor +# optimizations in PurePath._parse_args(). +os.PathLike.register(PurePath) + class PurePosixPath(PurePath): _flavour = _posix_flavour diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index fbbd448f65..2f2ba3cbfc 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -190,13 +190,18 @@ class _BasePurePathTest(object): P = self.cls p = P('a') self.assertIsInstance(p, P) + class PathLike: + def __fspath__(self): + return "a/b/c" P('a', 'b', 'c') P('/a', 'b', 'c') P('a/b/c') P('/a/b/c') + P(PathLike()) self.assertEqual(P(P('a')), P('a')) self.assertEqual(P(P('a'), 'b'), P('a/b')) self.assertEqual(P(P('a'), P('b')), P('a/b')) + self.assertEqual(P(P('a'), P('b'), P('c')), P(PathLike())) def _check_str_subclass(self, *args): # Issue #21127: it should be possible to construct a PurePath object @@ -384,6 +389,12 @@ class _BasePurePathTest(object): parts = p.parts self.assertEqual(parts, (sep, 'a', 'b')) + def test_fspath_common(self): + P = self.cls + p = P('a/b') + self._check_str(p.__fspath__(), ('a/b',)) + self._check_str(os.fspath(p), ('a/b',)) + def test_equivalences(self): for k, tuples in self.equivalences.items(): canon = k.replace('/', self.sep) -- cgit v1.2.1 From 2d3cd9e42e536185d9b1c6acbb4b900a848f5204 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Fri, 10 Jun 2016 14:37:21 -0700 Subject: Issue #27186: Add os.PathLike support to DirEntry Initial patch thanks to Jelle Zijlstra. --- Lib/test/test_os.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 6853ebbd7f..3f955713c4 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2824,11 +2824,13 @@ class TestScandir(unittest.TestCase): def setUp(self): self.path = os.path.realpath(support.TESTFN) + self.bytes_path = os.fsencode(self.path) self.addCleanup(support.rmtree, self.path) os.mkdir(self.path) def create_file(self, name="file.txt"): - filename = os.path.join(self.path, name) + path = self.bytes_path if isinstance(name, bytes) else self.path + filename = os.path.join(path, name) create_file(filename, b'python') return filename @@ -2917,15 +2919,16 @@ class TestScandir(unittest.TestCase): self.check_entry(entry, 'symlink_file.txt', False, True, True) def get_entry(self, name): - entries = list(os.scandir(self.path)) + path = self.bytes_path if isinstance(name, bytes) else self.path + entries = list(os.scandir(path)) self.assertEqual(len(entries), 1) entry = entries[0] self.assertEqual(entry.name, name) return entry - def create_file_entry(self): - filename = self.create_file() + def create_file_entry(self, name='file.txt'): + filename = self.create_file(name=name) return self.get_entry(os.path.basename(filename)) def test_current_directory(self): @@ -2946,6 +2949,18 @@ class TestScandir(unittest.TestCase): entry = self.create_file_entry() self.assertEqual(repr(entry), "") + def test_fspath_protocol(self): + entry = self.create_file_entry() + self.assertEqual(os.fspath(entry), os.path.join(self.path, 'file.txt')) + + def test_fspath_protocol_bytes(self): + bytes_filename = os.fsencode('bytesfile.txt') + bytes_entry = self.create_file_entry(name=bytes_filename) + fspath = os.fspath(bytes_entry) + self.assertIsInstance(fspath, bytes) + self.assertEqual(fspath, + os.path.join(os.fsencode(self.path),bytes_filename)) + def test_removed_dir(self): path = os.path.join(self.path, 'dir') -- cgit v1.2.1 From 0fe43be4e1abcd24bb503bf4ee150b08e02e4322 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 10 Jun 2016 18:19:21 -0400 Subject: Issue *24750: Switch all scrollbars in IDLE to ttk versions. Where needed, add minimal tests to cover changes. --- Lib/idlelib/autocomplete_w.py | 1 + Lib/idlelib/config_key.py | 21 ++++++++++------- Lib/idlelib/configdialog.py | 1 + Lib/idlelib/debugger.py | 1 + Lib/idlelib/editor.py | 1 + Lib/idlelib/help.py | 22 +++++++++--------- Lib/idlelib/idle_test/htest.py | 3 ++- Lib/idlelib/idle_test/test_autocomplete.py | 8 +++++-- Lib/idlelib/idle_test/test_config_key.py | 31 +++++++++++++++++++++++++ Lib/idlelib/idle_test/test_debugger.py | 29 ++++++++++++++++++++++++ Lib/idlelib/idle_test/test_help.py | 34 ++++++++++++++++++++++++++++ Lib/idlelib/idle_test/test_scrolledlist.py | 29 ++++++++++++++++++++++++ Lib/idlelib/idle_test/test_textview.py | 2 +- Lib/idlelib/idle_test/test_tree.py | 36 ++++++++++++++++++++++++++++++ Lib/idlelib/scrolledlist.py | 13 +++++------ Lib/idlelib/textview.py | 3 ++- Lib/idlelib/tree.py | 15 ++++++------- 17 files changed, 211 insertions(+), 39 deletions(-) create mode 100644 Lib/idlelib/idle_test/test_config_key.py create mode 100644 Lib/idlelib/idle_test/test_debugger.py create mode 100644 Lib/idlelib/idle_test/test_help.py create mode 100644 Lib/idlelib/idle_test/test_scrolledlist.py create mode 100644 Lib/idlelib/idle_test/test_tree.py (limited to 'Lib') diff --git a/Lib/idlelib/autocomplete_w.py b/Lib/idlelib/autocomplete_w.py index c66b3dfff0..38f8601156 100644 --- a/Lib/idlelib/autocomplete_w.py +++ b/Lib/idlelib/autocomplete_w.py @@ -2,6 +2,7 @@ An auto-completion window for IDLE, used by the autocomplete extension """ from tkinter import * +from tkinter.ttk import Scrollbar from idlelib.multicall import MC_SHIFT from idlelib.autocomplete import COMPLETE_FILES, COMPLETE_ATTRIBUTES diff --git a/Lib/idlelib/config_key.py b/Lib/idlelib/config_key.py index e6438bfc39..26022934c3 100644 --- a/Lib/idlelib/config_key.py +++ b/Lib/idlelib/config_key.py @@ -2,31 +2,35 @@ Dialog for building Tkinter accelerator key bindings """ from tkinter import * +from tkinter.ttk import Scrollbar import tkinter.messagebox as tkMessageBox import string import sys class GetKeysDialog(Toplevel): - def __init__(self,parent,title,action,currentKeySequences,_htest=False): + def __init__(self, parent, title, action, currentKeySequences, + _htest=False, _utest=False): """ action - string, the name of the virtual event these keys will be mapped to currentKeys - list, a list of all key sequence lists currently mapped to virtual events, for overlap checking + _utest - bool, do not wait when running unittest _htest - bool, change box location when running htest """ Toplevel.__init__(self, parent) + self.withdraw() #hide while setting geometry self.configure(borderwidth=5) - self.resizable(height=FALSE,width=FALSE) + self.resizable(height=FALSE, width=FALSE) self.title(title) self.transient(parent) self.grab_set() self.protocol("WM_DELETE_WINDOW", self.Cancel) self.parent = parent self.action=action - self.currentKeySequences=currentKeySequences - self.result='' - self.keyString=StringVar(self) + self.currentKeySequences = currentKeySequences + self.result = '' + self.keyString = StringVar(self) self.keyString.set('') self.SetModifiersForPlatform() # set self.modifiers, self.modifier_label self.modifier_vars = [] @@ -37,7 +41,6 @@ class GetKeysDialog(Toplevel): self.advanced = False self.CreateWidgets() self.LoadFinalKeyList() - self.withdraw() #hide while setting geometry self.update_idletasks() self.geometry( "+%d+%d" % ( @@ -47,8 +50,9 @@ class GetKeysDialog(Toplevel): ((parent.winfo_height()/2 - self.winfo_reqheight()/2) if not _htest else 150) ) ) #centre dialog over parent (or below htest box) - self.deiconify() #geometry set, unhide - self.wait_window() + if not _utest: + self.deiconify() #geometry set, unhide + self.wait_window() def CreateWidgets(self): frameMain = Frame(self,borderwidth=2,relief=SUNKEN) @@ -261,6 +265,7 @@ class GetKeysDialog(Toplevel): keysOK = True return keysOK + if __name__ == '__main__': from idlelib.idle_test.htest import run run(GetKeysDialog) diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index b58806e38c..1d7b4179b0 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -10,6 +10,7 @@ Refer to comments in EditorWindow autoindent code for details. """ from tkinter import * +from tkinter.ttk import Scrollbar import tkinter.messagebox as tkMessageBox import tkinter.colorchooser as tkColorChooser import tkinter.font as tkFont diff --git a/Lib/idlelib/debugger.py b/Lib/idlelib/debugger.py index 9af626cca9..ea393b12c0 100644 --- a/Lib/idlelib/debugger.py +++ b/Lib/idlelib/debugger.py @@ -1,6 +1,7 @@ import os import bdb from tkinter import * +from tkinter.ttk import Scrollbar from idlelib.windows import ListedToplevel from idlelib.scrolledlist import ScrolledList from idlelib import macosx diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index d04fc08e47..7f910e76f9 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -7,6 +7,7 @@ import re import string import sys from tkinter import * +from tkinter.ttk import Scrollbar import tkinter.simpledialog as tkSimpleDialog import tkinter.messagebox as tkMessageBox import traceback diff --git a/Lib/idlelib/help.py b/Lib/idlelib/help.py index 00930151dc..d18d1ca3f2 100644 --- a/Lib/idlelib/help.py +++ b/Lib/idlelib/help.py @@ -26,14 +26,11 @@ show_idlehelp - Create HelpWindow. Called in EditorWindow.help_dialog. """ from html.parser import HTMLParser from os.path import abspath, dirname, isfile, join -from tkinter import Toplevel, Frame, Text, Scrollbar, Menu, Menubutton +from tkinter import Toplevel, Frame, Text, Menu +from tkinter.ttk import Menubutton, Scrollbar from tkinter import font as tkfont from idlelib.config import idleConf -use_ttk = False # until available to import -if use_ttk: - from tkinter.ttk import Menubutton - ## About IDLE ## @@ -196,15 +193,18 @@ class HelpFrame(Frame): "Display html text, scrollbar, and toc." def __init__(self, parent, filename): Frame.__init__(self, parent) - text = HelpText(self, filename) + # keep references to widgets for test access. + self.text = text = HelpText(self, filename) self['background'] = text['background'] - scroll = Scrollbar(self, command=text.yview) + self.toc = toc = self.toc_menu(text) + self.scroll = scroll = Scrollbar(self, command=text.yview) text['yscrollcommand'] = scroll.set + self.rowconfigure(0, weight=1) self.columnconfigure(1, weight=1) # text - self.toc_menu(text).grid(column=0, row=0, sticky='nw') - text.grid(column=1, row=0, sticky='nsew') - scroll.grid(column=2, row=0, sticky='ns') + toc.grid(row=0, column=0, sticky='nw') + text.grid(row=0, column=1, sticky='nsew') + scroll.grid(row=0, column=2, sticky='ns') def toc_menu(self, text): "Create table of contents as drop-down menu." @@ -265,7 +265,7 @@ def show_idlehelp(parent): if not isfile(filename): # try copy_strip, present message return - HelpWindow(parent, filename, 'IDLE Help') + return HelpWindow(parent, filename, 'IDLE Help') if __name__ == '__main__': from idlelib.idle_test.htest import run diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py index 686ccc5fd5..5fd33a6c69 100644 --- a/Lib/idlelib/idle_test/htest.py +++ b/Lib/idlelib/idle_test/htest.py @@ -68,6 +68,7 @@ outwin.OutputWindow (indirectly being tested with grep test) from importlib import import_module from idlelib.macosx import _init_tk_type import tkinter as tk +from tkinter.ttk import Scrollbar AboutDialog_spec = { 'file': 'help_about', @@ -344,7 +345,7 @@ def run(*tests): frameLabel.pack() text = tk.Text(frameLabel, wrap='word') text.configure(bg=root.cget('bg'), relief='flat', height=4, width=70) - scrollbar = tk.Scrollbar(frameLabel, command=text.yview) + scrollbar = Scrollbar(frameLabel, command=text.yview) text.config(yscrollcommand=scrollbar.set) scrollbar.pack(side='right', fill='y', expand=False) text.pack(side='left', fill='both', expand=True) diff --git a/Lib/idlelib/idle_test/test_autocomplete.py b/Lib/idlelib/idle_test/test_autocomplete.py index 496a41dbe7..a14c6db349 100644 --- a/Lib/idlelib/idle_test/test_autocomplete.py +++ b/Lib/idlelib/idle_test/test_autocomplete.py @@ -1,10 +1,14 @@ +''' Test autocomplete and autocomple_w + +Coverage of autocomple: 56% +''' import unittest from test.support import requires from tkinter import Tk, Text import idlelib.autocomplete as ac import idlelib.autocomplete_w as acw -import idlelib.macosx as mac +from idlelib import macosx from idlelib.idle_test.mock_idle import Func from idlelib.idle_test.mock_tk import Event @@ -27,7 +31,7 @@ class AutoCompleteTest(unittest.TestCase): def setUpClass(cls): requires('gui') cls.root = Tk() - mac.setupApp(cls.root, None) + macosx.setupApp(cls.root, None) cls.text = Text(cls.root) cls.editor = DummyEditwin(cls.root, cls.text) diff --git a/Lib/idlelib/idle_test/test_config_key.py b/Lib/idlelib/idle_test/test_config_key.py new file mode 100644 index 0000000000..8109829f10 --- /dev/null +++ b/Lib/idlelib/idle_test/test_config_key.py @@ -0,0 +1,31 @@ +''' Test idlelib.config_key. + +Coverage: 56% +''' +from idlelib import config_key +from test.support import requires +requires('gui') +import unittest +from tkinter import Tk, Text + + +class GetKeysTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.root = Tk() + + @classmethod + def tearDownClass(cls): + cls.root.destroy() + del cls.root + + + def test_init(self): + dia = config_key.GetKeysDialog( + self.root, 'test', '<>', [''], _utest=True) + dia.Cancel() + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/Lib/idlelib/idle_test/test_debugger.py b/Lib/idlelib/idle_test/test_debugger.py new file mode 100644 index 0000000000..bcba9a45c1 --- /dev/null +++ b/Lib/idlelib/idle_test/test_debugger.py @@ -0,0 +1,29 @@ +''' Test idlelib.debugger. + +Coverage: 19% +''' +from idlelib import debugger +from test.support import requires +requires('gui') +import unittest +from tkinter import Tk + + +class NameSpaceTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.root = Tk() + cls.root.withdraw() + + @classmethod + def tearDownClass(cls): + cls.root.destroy() + del cls.root + + def test_init(self): + debugger.NamespaceViewer(self.root, 'Test') + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/Lib/idlelib/idle_test/test_help.py b/Lib/idlelib/idle_test/test_help.py new file mode 100644 index 0000000000..cdded2ac79 --- /dev/null +++ b/Lib/idlelib/idle_test/test_help.py @@ -0,0 +1,34 @@ +'''Test idlelib.help. + +Coverage: 87% +''' +from idlelib import help +from test.support import requires +requires('gui') +from os.path import abspath, dirname, join +from tkinter import Tk +import unittest + +class HelpFrameTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + "By itself, this tests that file parsed without exception." + cls.root = root = Tk() + root.withdraw() + helpfile = join(abspath(dirname(dirname(__file__))), 'help.html') + cls.frame = help.HelpFrame(root, helpfile) + + @classmethod + def tearDownClass(cls): + del cls.frame + cls.root.update_idletasks() + cls.root.destroy() + del cls.root + + def test_line1(self): + text = self.frame.text + self.assertEqual(text.get('1.0', '1.end'), ' IDLE ') + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/Lib/idlelib/idle_test/test_scrolledlist.py b/Lib/idlelib/idle_test/test_scrolledlist.py new file mode 100644 index 0000000000..56aabfecf4 --- /dev/null +++ b/Lib/idlelib/idle_test/test_scrolledlist.py @@ -0,0 +1,29 @@ +''' Test idlelib.scrolledlist. + +Coverage: 39% +''' +from idlelib import scrolledlist +from test.support import requires +requires('gui') +import unittest +from tkinter import Tk + + +class ScrolledListTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.root = Tk() + + @classmethod + def tearDownClass(cls): + cls.root.destroy() + del cls.root + + + def test_init(self): + scrolledlist.ScrolledList(self.root) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/Lib/idlelib/idle_test/test_textview.py b/Lib/idlelib/idle_test/test_textview.py index 4c70ebb905..764a1a5e1b 100644 --- a/Lib/idlelib/idle_test/test_textview.py +++ b/Lib/idlelib/idle_test/test_textview.py @@ -7,13 +7,13 @@ information about calls. The coverage is essentially 100%. ''' +from idlelib import textview as tv from test.support import requires requires('gui') import unittest import os from tkinter import Tk -from idlelib import textview as tv from idlelib.idle_test.mock_idle import Func from idlelib.idle_test.mock_tk import Mbox diff --git a/Lib/idlelib/idle_test/test_tree.py b/Lib/idlelib/idle_test/test_tree.py new file mode 100644 index 0000000000..09ba9641af --- /dev/null +++ b/Lib/idlelib/idle_test/test_tree.py @@ -0,0 +1,36 @@ +''' Test idlelib.tree. + +Coverage: 56% +''' +from idlelib import tree +from test.support import requires +requires('gui') +import os +import unittest +from tkinter import Tk + + +class TreeTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.root = Tk() + cls.root.withdraw() + + @classmethod + def tearDownClass(cls): + cls.root.destroy() + del cls.root + + def test_init(self): + # Start with code slightly adapted from htest. + sc = tree.ScrolledCanvas( + self.root, bg="white", highlightthickness=0, takefocus=1) + sc.frame.pack(expand=1, fill="both", side='left') + item = tree.FileTreeItem(tree.ICONDIR) + node = tree.TreeNode(sc.canvas, None, item) + node.expand() + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/Lib/idlelib/scrolledlist.py b/Lib/idlelib/scrolledlist.py index 80df0f8132..d0b66107ac 100644 --- a/Lib/idlelib/scrolledlist.py +++ b/Lib/idlelib/scrolledlist.py @@ -1,5 +1,6 @@ from tkinter import * from idlelib import macosx +from tkinter.ttk import Scrollbar class ScrolledList: @@ -124,22 +125,20 @@ class ScrolledList: pass -def _scrolled_list(parent): - root = Tk() - root.title("Test ScrolledList") +def _scrolled_list(parent): # htest # + top = Toplevel(parent) width, height, x, y = list(map(int, re.split('[x+]', parent.geometry()))) - root.geometry("+%d+%d"%(x, y + 150)) + top.geometry("+%d+%d"%(x+200, y + 175)) class MyScrolledList(ScrolledList): def fill_menu(self): self.menu.add_command(label="right click") def on_select(self, index): print("select", self.get(index)) def on_double(self, index): print("double", self.get(index)) - scrolled_list = MyScrolledList(root) + scrolled_list = MyScrolledList(top) for i in range(30): scrolled_list.append("Item %02d" % i) - root.mainloop() - if __name__ == '__main__': + # At the moment, test_scrolledlist merely creates instance, like htest. from idlelib.idle_test.htest import run run(_scrolled_list) diff --git a/Lib/idlelib/textview.py b/Lib/idlelib/textview.py index 01b2d8f4ab..9dc83574ee 100644 --- a/Lib/idlelib/textview.py +++ b/Lib/idlelib/textview.py @@ -3,6 +3,7 @@ """ from tkinter import * +from tkinter.ttk import Scrollbar import tkinter.messagebox as tkMessageBox class TextViewer(Toplevel): @@ -50,7 +51,7 @@ class TextViewer(Toplevel): self.buttonOk = Button(frameButtons, text='Close', command=self.Ok, takefocus=FALSE) self.scrollbarView = Scrollbar(frameText, orient=VERTICAL, - takefocus=FALSE, highlightthickness=0) + takefocus=FALSE) self.textView = Text(frameText, wrap=WORD, highlightthickness=0, fg=self.fg, bg=self.bg) self.scrollbarView.config(command=self.textView.yview) diff --git a/Lib/idlelib/tree.py b/Lib/idlelib/tree.py index 08cc6370dd..cb7f9ae4b4 100644 --- a/Lib/idlelib/tree.py +++ b/Lib/idlelib/tree.py @@ -16,7 +16,7 @@ import os from tkinter import * - +from tkinter.ttk import Scrollbar from idlelib import zoomheight from idlelib.config import idleConf @@ -449,18 +449,17 @@ class ScrolledCanvas: return "break" -def _tree_widget(parent): - root = Tk() - root.title("Test TreeWidget") +def _tree_widget(parent): # htest # + top = Toplevel(parent) width, height, x, y = list(map(int, re.split('[x+]', parent.geometry()))) - root.geometry("+%d+%d"%(x, y + 150)) - sc = ScrolledCanvas(root, bg="white", highlightthickness=0, takefocus=1) + top.geometry("+%d+%d"%(x+50, y+175)) + sc = ScrolledCanvas(top, bg="white", highlightthickness=0, takefocus=1) sc.frame.pack(expand=1, fill="both", side=LEFT) - item = FileTreeItem(os.getcwd()) + item = FileTreeItem(ICONDIR) node = TreeNode(sc.canvas, None, item) node.expand() - root.mainloop() if __name__ == '__main__': + # test_tree is currently a copy of this from idlelib.idle_test.htest import run run(_tree_widget) -- cgit v1.2.1 From b78ad9a4f25a82f5a5618b47a00afbe99efe1f17 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Sat, 11 Jun 2016 02:05:52 -0400 Subject: Issue #5124: Temporary pyshell rename to avoid case-folding collision in merge. --- Lib/idlelib/PyShell.py | 1618 ++++++++++++++++++++++++++++++++++++++++++++++++ Lib/idlelib/pyshell.py | 1618 ------------------------------------------------ 2 files changed, 1618 insertions(+), 1618 deletions(-) create mode 100755 Lib/idlelib/PyShell.py delete mode 100755 Lib/idlelib/pyshell.py (limited to 'Lib') diff --git a/Lib/idlelib/PyShell.py b/Lib/idlelib/PyShell.py new file mode 100755 index 0000000000..38c12cd8bf --- /dev/null +++ b/Lib/idlelib/PyShell.py @@ -0,0 +1,1618 @@ +#! /usr/bin/env python3 + +try: + from tkinter import * +except ImportError: + print("** IDLE can't import Tkinter.\n" + "Your Python may not be configured for Tk. **", file=sys.__stderr__) + sys.exit(1) +import tkinter.messagebox as tkMessageBox +if TkVersion < 8.5: + root = Tk() # otherwise create root in main + root.withdraw() + tkMessageBox.showerror("Idle Cannot Start", + "Idle requires tcl/tk 8.5+, not $s." % TkVersion, + parent=root) + sys.exit(1) + +import getopt +import os +import os.path +import re +import socket +import subprocess +import sys +import threading +import time +import tokenize +import io + +import linecache +from code import InteractiveInterpreter +from platform import python_version, system + +from idlelib.editor import EditorWindow, fixwordbreaks +from idlelib.filelist import FileList +from idlelib.colorizer import ColorDelegator +from idlelib.undo import UndoDelegator +from idlelib.outwin import OutputWindow +from idlelib.config import idleConf +from idlelib import rpc +from idlelib import debugger +from idlelib import debugger_r +from idlelib import macosx + +HOST = '127.0.0.1' # python execution server on localhost loopback +PORT = 0 # someday pass in host, port for remote debug capability + +# Override warnings module to write to warning_stream. Initialize to send IDLE +# internal warnings to the console. ScriptBinding.check_syntax() will +# temporarily redirect the stream to the shell window to display warnings when +# checking user's code. +warning_stream = sys.__stderr__ # None, at least on Windows, if no console. +import warnings + +def idle_formatwarning(message, category, filename, lineno, line=None): + """Format warnings the IDLE way.""" + + s = "\nWarning (from warnings module):\n" + s += ' File \"%s\", line %s\n' % (filename, lineno) + if line is None: + line = linecache.getline(filename, lineno) + line = line.strip() + if line: + s += " %s\n" % line + s += "%s: %s\n" % (category.__name__, message) + return s + +def idle_showwarning( + message, category, filename, lineno, file=None, line=None): + """Show Idle-format warning (after replacing warnings.showwarning). + + The differences are the formatter called, the file=None replacement, + which can be None, the capture of the consequence AttributeError, + and the output of a hard-coded prompt. + """ + if file is None: + file = warning_stream + try: + file.write(idle_formatwarning( + message, category, filename, lineno, line=line)) + file.write(">>> ") + except (AttributeError, OSError): + pass # if file (probably __stderr__) is invalid, skip warning. + +_warnings_showwarning = None + +def capture_warnings(capture): + "Replace warning.showwarning with idle_showwarning, or reverse." + + global _warnings_showwarning + if capture: + if _warnings_showwarning is None: + _warnings_showwarning = warnings.showwarning + warnings.showwarning = idle_showwarning + else: + if _warnings_showwarning is not None: + warnings.showwarning = _warnings_showwarning + _warnings_showwarning = None + +capture_warnings(True) + +def extended_linecache_checkcache(filename=None, + orig_checkcache=linecache.checkcache): + """Extend linecache.checkcache to preserve the entries + + Rather than repeating the linecache code, patch it to save the + entries, call the original linecache.checkcache() + (skipping them), and then restore the saved entries. + + orig_checkcache is bound at definition time to the original + method, allowing it to be patched. + """ + cache = linecache.cache + save = {} + for key in list(cache): + if key[:1] + key[-1:] == '<>': + save[key] = cache.pop(key) + orig_checkcache(filename) + cache.update(save) + +# Patch linecache.checkcache(): +linecache.checkcache = extended_linecache_checkcache + + +class PyShellEditorWindow(EditorWindow): + "Regular text edit window in IDLE, supports breakpoints" + + def __init__(self, *args): + self.breakpoints = [] + EditorWindow.__init__(self, *args) + self.text.bind("<>", self.set_breakpoint_here) + self.text.bind("<>", self.clear_breakpoint_here) + self.text.bind("<>", self.flist.open_shell) + + self.breakpointPath = os.path.join(idleConf.GetUserCfgDir(), + 'breakpoints.lst') + # whenever a file is changed, restore breakpoints + def filename_changed_hook(old_hook=self.io.filename_change_hook, + self=self): + self.restore_file_breaks() + old_hook() + self.io.set_filename_change_hook(filename_changed_hook) + if self.io.filename: + self.restore_file_breaks() + self.color_breakpoint_text() + + rmenu_specs = [ + ("Cut", "<>", "rmenu_check_cut"), + ("Copy", "<>", "rmenu_check_copy"), + ("Paste", "<>", "rmenu_check_paste"), + (None, None, None), + ("Set Breakpoint", "<>", None), + ("Clear Breakpoint", "<>", None) + ] + + def color_breakpoint_text(self, color=True): + "Turn colorizing of breakpoint text on or off" + if self.io is None: + # possible due to update in restore_file_breaks + return + if color: + theme = idleConf.CurrentTheme() + cfg = idleConf.GetHighlight(theme, "break") + else: + cfg = {'foreground': '', 'background': ''} + self.text.tag_config('BREAK', cfg) + + def set_breakpoint(self, lineno): + text = self.text + filename = self.io.filename + text.tag_add("BREAK", "%d.0" % lineno, "%d.0" % (lineno+1)) + try: + self.breakpoints.index(lineno) + except ValueError: # only add if missing, i.e. do once + self.breakpoints.append(lineno) + try: # update the subprocess debugger + debug = self.flist.pyshell.interp.debugger + debug.set_breakpoint_here(filename, lineno) + except: # but debugger may not be active right now.... + pass + + def set_breakpoint_here(self, event=None): + text = self.text + filename = self.io.filename + if not filename: + text.bell() + return + lineno = int(float(text.index("insert"))) + self.set_breakpoint(lineno) + + def clear_breakpoint_here(self, event=None): + text = self.text + filename = self.io.filename + if not filename: + text.bell() + return + lineno = int(float(text.index("insert"))) + try: + self.breakpoints.remove(lineno) + except: + pass + text.tag_remove("BREAK", "insert linestart",\ + "insert lineend +1char") + try: + debug = self.flist.pyshell.interp.debugger + debug.clear_breakpoint_here(filename, lineno) + except: + pass + + def clear_file_breaks(self): + if self.breakpoints: + text = self.text + filename = self.io.filename + if not filename: + text.bell() + return + self.breakpoints = [] + text.tag_remove("BREAK", "1.0", END) + try: + debug = self.flist.pyshell.interp.debugger + debug.clear_file_breaks(filename) + except: + pass + + def store_file_breaks(self): + "Save breakpoints when file is saved" + # XXX 13 Dec 2002 KBK Currently the file must be saved before it can + # be run. The breaks are saved at that time. If we introduce + # a temporary file save feature the save breaks functionality + # needs to be re-verified, since the breaks at the time the + # temp file is created may differ from the breaks at the last + # permanent save of the file. Currently, a break introduced + # after a save will be effective, but not persistent. + # This is necessary to keep the saved breaks synched with the + # saved file. + # + # Breakpoints are set as tagged ranges in the text. + # Since a modified file has to be saved before it is + # run, and since self.breakpoints (from which the subprocess + # debugger is loaded) is updated during the save, the visible + # breaks stay synched with the subprocess even if one of these + # unexpected breakpoint deletions occurs. + breaks = self.breakpoints + filename = self.io.filename + try: + with open(self.breakpointPath, "r") as fp: + lines = fp.readlines() + except OSError: + lines = [] + try: + with open(self.breakpointPath, "w") as new_file: + for line in lines: + if not line.startswith(filename + '='): + new_file.write(line) + self.update_breakpoints() + breaks = self.breakpoints + if breaks: + new_file.write(filename + '=' + str(breaks) + '\n') + except OSError as err: + if not getattr(self.root, "breakpoint_error_displayed", False): + self.root.breakpoint_error_displayed = True + tkMessageBox.showerror(title='IDLE Error', + message='Unable to update breakpoint list:\n%s' + % str(err), + parent=self.text) + + def restore_file_breaks(self): + self.text.update() # this enables setting "BREAK" tags to be visible + if self.io is None: + # can happen if IDLE closes due to the .update() call + return + filename = self.io.filename + if filename is None: + return + if os.path.isfile(self.breakpointPath): + with open(self.breakpointPath, "r") as fp: + lines = fp.readlines() + for line in lines: + if line.startswith(filename + '='): + breakpoint_linenumbers = eval(line[len(filename)+1:]) + for breakpoint_linenumber in breakpoint_linenumbers: + self.set_breakpoint(breakpoint_linenumber) + + def update_breakpoints(self): + "Retrieves all the breakpoints in the current window" + text = self.text + ranges = text.tag_ranges("BREAK") + linenumber_list = self.ranges_to_linenumbers(ranges) + self.breakpoints = linenumber_list + + def ranges_to_linenumbers(self, ranges): + lines = [] + for index in range(0, len(ranges), 2): + lineno = int(float(ranges[index].string)) + end = int(float(ranges[index+1].string)) + while lineno < end: + lines.append(lineno) + lineno += 1 + return lines + +# XXX 13 Dec 2002 KBK Not used currently +# def saved_change_hook(self): +# "Extend base method - clear breaks if module is modified" +# if not self.get_saved(): +# self.clear_file_breaks() +# EditorWindow.saved_change_hook(self) + + def _close(self): + "Extend base method - clear breaks when module is closed" + self.clear_file_breaks() + EditorWindow._close(self) + + +class PyShellFileList(FileList): + "Extend base class: IDLE supports a shell and breakpoints" + + # override FileList's class variable, instances return PyShellEditorWindow + # instead of EditorWindow when new edit windows are created. + EditorWindow = PyShellEditorWindow + + pyshell = None + + def open_shell(self, event=None): + if self.pyshell: + self.pyshell.top.wakeup() + else: + self.pyshell = PyShell(self) + if self.pyshell: + if not self.pyshell.begin(): + return None + return self.pyshell + + +class ModifiedColorDelegator(ColorDelegator): + "Extend base class: colorizer for the shell window itself" + + def __init__(self): + ColorDelegator.__init__(self) + self.LoadTagDefs() + + def recolorize_main(self): + self.tag_remove("TODO", "1.0", "iomark") + self.tag_add("SYNC", "1.0", "iomark") + ColorDelegator.recolorize_main(self) + + def LoadTagDefs(self): + ColorDelegator.LoadTagDefs(self) + theme = idleConf.CurrentTheme() + self.tagdefs.update({ + "stdin": {'background':None,'foreground':None}, + "stdout": idleConf.GetHighlight(theme, "stdout"), + "stderr": idleConf.GetHighlight(theme, "stderr"), + "console": idleConf.GetHighlight(theme, "console"), + }) + + def removecolors(self): + # Don't remove shell color tags before "iomark" + for tag in self.tagdefs: + self.tag_remove(tag, "iomark", "end") + +class ModifiedUndoDelegator(UndoDelegator): + "Extend base class: forbid insert/delete before the I/O mark" + + def insert(self, index, chars, tags=None): + try: + if self.delegate.compare(index, "<", "iomark"): + self.delegate.bell() + return + except TclError: + pass + UndoDelegator.insert(self, index, chars, tags) + + def delete(self, index1, index2=None): + try: + if self.delegate.compare(index1, "<", "iomark"): + self.delegate.bell() + return + except TclError: + pass + UndoDelegator.delete(self, index1, index2) + + +class MyRPCClient(rpc.RPCClient): + + def handle_EOF(self): + "Override the base class - just re-raise EOFError" + raise EOFError + + +class ModifiedInterpreter(InteractiveInterpreter): + + def __init__(self, tkconsole): + self.tkconsole = tkconsole + locals = sys.modules['__main__'].__dict__ + InteractiveInterpreter.__init__(self, locals=locals) + self.save_warnings_filters = None + self.restarting = False + self.subprocess_arglist = None + self.port = PORT + self.original_compiler_flags = self.compile.compiler.flags + + _afterid = None + rpcclt = None + rpcsubproc = None + + def spawn_subprocess(self): + if self.subprocess_arglist is None: + self.subprocess_arglist = self.build_subprocess_arglist() + self.rpcsubproc = subprocess.Popen(self.subprocess_arglist) + + def build_subprocess_arglist(self): + assert (self.port!=0), ( + "Socket should have been assigned a port number.") + w = ['-W' + s for s in sys.warnoptions] + # Maybe IDLE is installed and is being accessed via sys.path, + # or maybe it's not installed and the idle.py script is being + # run from the IDLE source directory. + del_exitf = idleConf.GetOption('main', 'General', 'delete-exitfunc', + default=False, type='bool') + if __name__ == 'idlelib.pyshell': + command = "__import__('idlelib.run').run.main(%r)" % (del_exitf,) + else: + command = "__import__('run').main(%r)" % (del_exitf,) + return [sys.executable] + w + ["-c", command, str(self.port)] + + def start_subprocess(self): + addr = (HOST, self.port) + # GUI makes several attempts to acquire socket, listens for connection + for i in range(3): + time.sleep(i) + try: + self.rpcclt = MyRPCClient(addr) + break + except OSError: + pass + else: + self.display_port_binding_error() + return None + # if PORT was 0, system will assign an 'ephemeral' port. Find it out: + self.port = self.rpcclt.listening_sock.getsockname()[1] + # if PORT was not 0, probably working with a remote execution server + if PORT != 0: + # To allow reconnection within the 2MSL wait (cf. Stevens TCP + # V1, 18.6), set SO_REUSEADDR. Note that this can be problematic + # on Windows since the implementation allows two active sockets on + # the same address! + self.rpcclt.listening_sock.setsockopt(socket.SOL_SOCKET, + socket.SO_REUSEADDR, 1) + self.spawn_subprocess() + #time.sleep(20) # test to simulate GUI not accepting connection + # Accept the connection from the Python execution server + self.rpcclt.listening_sock.settimeout(10) + try: + self.rpcclt.accept() + except socket.timeout: + self.display_no_subprocess_error() + return None + self.rpcclt.register("console", self.tkconsole) + self.rpcclt.register("stdin", self.tkconsole.stdin) + self.rpcclt.register("stdout", self.tkconsole.stdout) + self.rpcclt.register("stderr", self.tkconsole.stderr) + self.rpcclt.register("flist", self.tkconsole.flist) + self.rpcclt.register("linecache", linecache) + self.rpcclt.register("interp", self) + self.transfer_path(with_cwd=True) + self.poll_subprocess() + return self.rpcclt + + def restart_subprocess(self, with_cwd=False, filename=''): + if self.restarting: + return self.rpcclt + self.restarting = True + # close only the subprocess debugger + debug = self.getdebugger() + if debug: + try: + # Only close subprocess debugger, don't unregister gui_adap! + debugger_r.close_subprocess_debugger(self.rpcclt) + except: + pass + # Kill subprocess, spawn a new one, accept connection. + self.rpcclt.close() + self.terminate_subprocess() + console = self.tkconsole + was_executing = console.executing + console.executing = False + self.spawn_subprocess() + try: + self.rpcclt.accept() + except socket.timeout: + self.display_no_subprocess_error() + return None + self.transfer_path(with_cwd=with_cwd) + console.stop_readline() + # annotate restart in shell window and mark it + console.text.delete("iomark", "end-1c") + tag = 'RESTART: ' + (filename if filename else 'Shell') + halfbar = ((int(console.width) -len(tag) - 4) // 2) * '=' + console.write("\n{0} {1} {0}".format(halfbar, tag)) + console.text.mark_set("restart", "end-1c") + console.text.mark_gravity("restart", "left") + if not filename: + console.showprompt() + # restart subprocess debugger + if debug: + # Restarted debugger connects to current instance of debug GUI + debugger_r.restart_subprocess_debugger(self.rpcclt) + # reload remote debugger breakpoints for all PyShellEditWindows + debug.load_breakpoints() + self.compile.compiler.flags = self.original_compiler_flags + self.restarting = False + return self.rpcclt + + def __request_interrupt(self): + self.rpcclt.remotecall("exec", "interrupt_the_server", (), {}) + + def interrupt_subprocess(self): + threading.Thread(target=self.__request_interrupt).start() + + def kill_subprocess(self): + if self._afterid is not None: + self.tkconsole.text.after_cancel(self._afterid) + try: + self.rpcclt.listening_sock.close() + except AttributeError: # no socket + pass + try: + self.rpcclt.close() + except AttributeError: # no socket + pass + self.terminate_subprocess() + self.tkconsole.executing = False + self.rpcclt = None + + def terminate_subprocess(self): + "Make sure subprocess is terminated" + try: + self.rpcsubproc.kill() + except OSError: + # process already terminated + return + else: + try: + self.rpcsubproc.wait() + except OSError: + return + + def transfer_path(self, with_cwd=False): + if with_cwd: # Issue 13506 + path = [''] # include Current Working Directory + path.extend(sys.path) + else: + path = sys.path + + self.runcommand("""if 1: + import sys as _sys + _sys.path = %r + del _sys + \n""" % (path,)) + + active_seq = None + + def poll_subprocess(self): + clt = self.rpcclt + if clt is None: + return + try: + response = clt.pollresponse(self.active_seq, wait=0.05) + except (EOFError, OSError, KeyboardInterrupt): + # lost connection or subprocess terminated itself, restart + # [the KBI is from rpc.SocketIO.handle_EOF()] + if self.tkconsole.closing: + return + response = None + self.restart_subprocess() + if response: + self.tkconsole.resetoutput() + self.active_seq = None + how, what = response + console = self.tkconsole.console + if how == "OK": + if what is not None: + print(repr(what), file=console) + elif how == "EXCEPTION": + if self.tkconsole.getvar("<>"): + self.remote_stack_viewer() + elif how == "ERROR": + errmsg = "pyshell.ModifiedInterpreter: Subprocess ERROR:\n" + print(errmsg, what, file=sys.__stderr__) + print(errmsg, what, file=console) + # we received a response to the currently active seq number: + try: + self.tkconsole.endexecuting() + except AttributeError: # shell may have closed + pass + # Reschedule myself + if not self.tkconsole.closing: + self._afterid = self.tkconsole.text.after( + self.tkconsole.pollinterval, self.poll_subprocess) + + debugger = None + + def setdebugger(self, debugger): + self.debugger = debugger + + def getdebugger(self): + return self.debugger + + def open_remote_stack_viewer(self): + """Initiate the remote stack viewer from a separate thread. + + This method is called from the subprocess, and by returning from this + method we allow the subprocess to unblock. After a bit the shell + requests the subprocess to open the remote stack viewer which returns a + static object looking at the last exception. It is queried through + the RPC mechanism. + + """ + self.tkconsole.text.after(300, self.remote_stack_viewer) + return + + def remote_stack_viewer(self): + from idlelib import debugobj_r + oid = self.rpcclt.remotequeue("exec", "stackviewer", ("flist",), {}) + if oid is None: + self.tkconsole.root.bell() + return + item = debugobj_r.StubObjectTreeItem(self.rpcclt, oid) + from idlelib.tree import ScrolledCanvas, TreeNode + top = Toplevel(self.tkconsole.root) + theme = idleConf.CurrentTheme() + background = idleConf.GetHighlight(theme, 'normal')['background'] + sc = ScrolledCanvas(top, bg=background, highlightthickness=0) + sc.frame.pack(expand=1, fill="both") + node = TreeNode(sc.canvas, None, item) + node.expand() + # XXX Should GC the remote tree when closing the window + + gid = 0 + + def execsource(self, source): + "Like runsource() but assumes complete exec source" + filename = self.stuffsource(source) + self.execfile(filename, source) + + def execfile(self, filename, source=None): + "Execute an existing file" + if source is None: + with tokenize.open(filename) as fp: + source = fp.read() + try: + code = compile(source, filename, "exec") + except (OverflowError, SyntaxError): + self.tkconsole.resetoutput() + print('*** Error in script or command!\n' + 'Traceback (most recent call last):', + file=self.tkconsole.stderr) + InteractiveInterpreter.showsyntaxerror(self, filename) + self.tkconsole.showprompt() + else: + self.runcode(code) + + def runsource(self, source): + "Extend base class method: Stuff the source in the line cache first" + filename = self.stuffsource(source) + self.more = 0 + self.save_warnings_filters = warnings.filters[:] + warnings.filterwarnings(action="error", category=SyntaxWarning) + # at the moment, InteractiveInterpreter expects str + assert isinstance(source, str) + #if isinstance(source, str): + # from idlelib import iomenu + # try: + # source = source.encode(iomenu.encoding) + # except UnicodeError: + # self.tkconsole.resetoutput() + # self.write("Unsupported characters in input\n") + # return + try: + # InteractiveInterpreter.runsource() calls its runcode() method, + # which is overridden (see below) + return InteractiveInterpreter.runsource(self, source, filename) + finally: + if self.save_warnings_filters is not None: + warnings.filters[:] = self.save_warnings_filters + self.save_warnings_filters = None + + def stuffsource(self, source): + "Stuff source in the filename cache" + filename = "" % self.gid + self.gid = self.gid + 1 + lines = source.split("\n") + linecache.cache[filename] = len(source)+1, 0, lines, filename + return filename + + def prepend_syspath(self, filename): + "Prepend sys.path with file's directory if not already included" + self.runcommand("""if 1: + _filename = %r + import sys as _sys + from os.path import dirname as _dirname + _dir = _dirname(_filename) + if not _dir in _sys.path: + _sys.path.insert(0, _dir) + del _filename, _sys, _dirname, _dir + \n""" % (filename,)) + + def showsyntaxerror(self, filename=None): + """Override Interactive Interpreter method: Use Colorizing + + Color the offending position instead of printing it and pointing at it + with a caret. + + """ + tkconsole = self.tkconsole + text = tkconsole.text + text.tag_remove("ERROR", "1.0", "end") + type, value, tb = sys.exc_info() + msg = getattr(value, 'msg', '') or value or "" + lineno = getattr(value, 'lineno', '') or 1 + offset = getattr(value, 'offset', '') or 0 + if offset == 0: + lineno += 1 #mark end of offending line + if lineno == 1: + pos = "iomark + %d chars" % (offset-1) + else: + pos = "iomark linestart + %d lines + %d chars" % \ + (lineno-1, offset-1) + tkconsole.colorize_syntax_error(text, pos) + tkconsole.resetoutput() + self.write("SyntaxError: %s\n" % msg) + tkconsole.showprompt() + + def showtraceback(self): + "Extend base class method to reset output properly" + self.tkconsole.resetoutput() + self.checklinecache() + InteractiveInterpreter.showtraceback(self) + if self.tkconsole.getvar("<>"): + self.tkconsole.open_stack_viewer() + + def checklinecache(self): + c = linecache.cache + for key in list(c.keys()): + if key[:1] + key[-1:] != "<>": + del c[key] + + def runcommand(self, code): + "Run the code without invoking the debugger" + # The code better not raise an exception! + if self.tkconsole.executing: + self.display_executing_dialog() + return 0 + if self.rpcclt: + self.rpcclt.remotequeue("exec", "runcode", (code,), {}) + else: + exec(code, self.locals) + return 1 + + def runcode(self, code): + "Override base class method" + if self.tkconsole.executing: + self.interp.restart_subprocess() + self.checklinecache() + if self.save_warnings_filters is not None: + warnings.filters[:] = self.save_warnings_filters + self.save_warnings_filters = None + debugger = self.debugger + try: + self.tkconsole.beginexecuting() + if not debugger and self.rpcclt is not None: + self.active_seq = self.rpcclt.asyncqueue("exec", "runcode", + (code,), {}) + elif debugger: + debugger.run(code, self.locals) + else: + exec(code, self.locals) + except SystemExit: + if not self.tkconsole.closing: + if tkMessageBox.askyesno( + "Exit?", + "Do you want to exit altogether?", + default="yes", + parent=self.tkconsole.text): + raise + else: + self.showtraceback() + else: + raise + except: + if use_subprocess: + print("IDLE internal error in runcode()", + file=self.tkconsole.stderr) + self.showtraceback() + self.tkconsole.endexecuting() + else: + if self.tkconsole.canceled: + self.tkconsole.canceled = False + print("KeyboardInterrupt", file=self.tkconsole.stderr) + else: + self.showtraceback() + finally: + if not use_subprocess: + try: + self.tkconsole.endexecuting() + except AttributeError: # shell may have closed + pass + + def write(self, s): + "Override base class method" + return self.tkconsole.stderr.write(s) + + def display_port_binding_error(self): + tkMessageBox.showerror( + "Port Binding Error", + "IDLE can't bind to a TCP/IP port, which is necessary to " + "communicate with its Python execution server. This might be " + "because no networking is installed on this computer. " + "Run IDLE with the -n command line switch to start without a " + "subprocess and refer to Help/IDLE Help 'Running without a " + "subprocess' for further details.", + parent=self.tkconsole.text) + + def display_no_subprocess_error(self): + tkMessageBox.showerror( + "Subprocess Startup Error", + "IDLE's subprocess didn't make connection. Either IDLE can't " + "start a subprocess or personal firewall software is blocking " + "the connection.", + parent=self.tkconsole.text) + + def display_executing_dialog(self): + tkMessageBox.showerror( + "Already executing", + "The Python Shell window is already executing a command; " + "please wait until it is finished.", + parent=self.tkconsole.text) + + +class PyShell(OutputWindow): + + shell_title = "Python " + python_version() + " Shell" + + # Override classes + ColorDelegator = ModifiedColorDelegator + UndoDelegator = ModifiedUndoDelegator + + # Override menus + menu_specs = [ + ("file", "_File"), + ("edit", "_Edit"), + ("debug", "_Debug"), + ("options", "_Options"), + ("windows", "_Window"), + ("help", "_Help"), + ] + + + # New classes + from idlelib.history import History + + def __init__(self, flist=None): + if use_subprocess: + ms = self.menu_specs + if ms[2][0] != "shell": + ms.insert(2, ("shell", "She_ll")) + self.interp = ModifiedInterpreter(self) + if flist is None: + root = Tk() + fixwordbreaks(root) + root.withdraw() + flist = PyShellFileList(root) + # + OutputWindow.__init__(self, flist, None, None) + # +## self.config(usetabs=1, indentwidth=8, context_use_ps1=1) + self.usetabs = True + # indentwidth must be 8 when using tabs. See note in EditorWindow: + self.indentwidth = 8 + self.context_use_ps1 = True + # + text = self.text + text.configure(wrap="char") + text.bind("<>", self.enter_callback) + text.bind("<>", self.linefeed_callback) + text.bind("<>", self.cancel_callback) + text.bind("<>", self.eof_callback) + text.bind("<>", self.open_stack_viewer) + text.bind("<>", self.toggle_debugger) + text.bind("<>", self.toggle_jit_stack_viewer) + if use_subprocess: + text.bind("<>", self.view_restart_mark) + text.bind("<>", self.restart_shell) + # + self.save_stdout = sys.stdout + self.save_stderr = sys.stderr + self.save_stdin = sys.stdin + from idlelib import iomenu + self.stdin = PseudoInputFile(self, "stdin", iomenu.encoding) + self.stdout = PseudoOutputFile(self, "stdout", iomenu.encoding) + self.stderr = PseudoOutputFile(self, "stderr", iomenu.encoding) + self.console = PseudoOutputFile(self, "console", iomenu.encoding) + if not use_subprocess: + sys.stdout = self.stdout + sys.stderr = self.stderr + sys.stdin = self.stdin + try: + # page help() text to shell. + import pydoc # import must be done here to capture i/o rebinding. + # XXX KBK 27Dec07 use TextViewer someday, but must work w/o subproc + pydoc.pager = pydoc.plainpager + except: + sys.stderr = sys.__stderr__ + raise + # + self.history = self.History(self.text) + # + self.pollinterval = 50 # millisec + + def get_standard_extension_names(self): + return idleConf.GetExtensions(shell_only=True) + + reading = False + executing = False + canceled = False + endoffile = False + closing = False + _stop_readline_flag = False + + def set_warning_stream(self, stream): + global warning_stream + warning_stream = stream + + def get_warning_stream(self): + return warning_stream + + def toggle_debugger(self, event=None): + if self.executing: + tkMessageBox.showerror("Don't debug now", + "You can only toggle the debugger when idle", + parent=self.text) + self.set_debugger_indicator() + return "break" + else: + db = self.interp.getdebugger() + if db: + self.close_debugger() + else: + self.open_debugger() + + def set_debugger_indicator(self): + db = self.interp.getdebugger() + self.setvar("<>", not not db) + + def toggle_jit_stack_viewer(self, event=None): + pass # All we need is the variable + + def close_debugger(self): + db = self.interp.getdebugger() + if db: + self.interp.setdebugger(None) + db.close() + if self.interp.rpcclt: + debugger_r.close_remote_debugger(self.interp.rpcclt) + self.resetoutput() + self.console.write("[DEBUG OFF]\n") + sys.ps1 = ">>> " + self.showprompt() + self.set_debugger_indicator() + + def open_debugger(self): + if self.interp.rpcclt: + dbg_gui = debugger_r.start_remote_debugger(self.interp.rpcclt, + self) + else: + dbg_gui = debugger.Debugger(self) + self.interp.setdebugger(dbg_gui) + dbg_gui.load_breakpoints() + sys.ps1 = "[DEBUG ON]\n>>> " + self.showprompt() + self.set_debugger_indicator() + + def beginexecuting(self): + "Helper for ModifiedInterpreter" + self.resetoutput() + self.executing = 1 + + def endexecuting(self): + "Helper for ModifiedInterpreter" + self.executing = 0 + self.canceled = 0 + self.showprompt() + + def close(self): + "Extend EditorWindow.close()" + if self.executing: + response = tkMessageBox.askokcancel( + "Kill?", + "Your program is still running!\n Do you want to kill it?", + default="ok", + parent=self.text) + if response is False: + return "cancel" + self.stop_readline() + self.canceled = True + self.closing = True + return EditorWindow.close(self) + + def _close(self): + "Extend EditorWindow._close(), shut down debugger and execution server" + self.close_debugger() + if use_subprocess: + self.interp.kill_subprocess() + # Restore std streams + sys.stdout = self.save_stdout + sys.stderr = self.save_stderr + sys.stdin = self.save_stdin + # Break cycles + self.interp = None + self.console = None + self.flist.pyshell = None + self.history = None + EditorWindow._close(self) + + def ispythonsource(self, filename): + "Override EditorWindow method: never remove the colorizer" + return True + + def short_title(self): + return self.shell_title + + COPYRIGHT = \ + 'Type "copyright", "credits" or "license()" for more information.' + + def begin(self): + self.text.mark_set("iomark", "insert") + self.resetoutput() + if use_subprocess: + nosub = '' + client = self.interp.start_subprocess() + if not client: + self.close() + return False + else: + nosub = ("==== No Subprocess ====\n\n" + + "WARNING: Running IDLE without a Subprocess is deprecated\n" + + "and will be removed in a later version. See Help/IDLE Help\n" + + "for details.\n\n") + sys.displayhook = rpc.displayhook + + self.write("Python %s on %s\n%s\n%s" % + (sys.version, sys.platform, self.COPYRIGHT, nosub)) + self.text.focus_force() + self.showprompt() + import tkinter + tkinter._default_root = None # 03Jan04 KBK What's this? + return True + + def stop_readline(self): + if not self.reading: # no nested mainloop to exit. + return + self._stop_readline_flag = True + self.top.quit() + + def readline(self): + save = self.reading + try: + self.reading = 1 + self.top.mainloop() # nested mainloop() + finally: + self.reading = save + if self._stop_readline_flag: + self._stop_readline_flag = False + return "" + line = self.text.get("iomark", "end-1c") + if len(line) == 0: # may be EOF if we quit our mainloop with Ctrl-C + line = "\n" + self.resetoutput() + if self.canceled: + self.canceled = 0 + if not use_subprocess: + raise KeyboardInterrupt + if self.endoffile: + self.endoffile = 0 + line = "" + return line + + def isatty(self): + return True + + def cancel_callback(self, event=None): + try: + if self.text.compare("sel.first", "!=", "sel.last"): + return # Active selection -- always use default binding + except: + pass + if not (self.executing or self.reading): + self.resetoutput() + self.interp.write("KeyboardInterrupt\n") + self.showprompt() + return "break" + self.endoffile = 0 + self.canceled = 1 + if (self.executing and self.interp.rpcclt): + if self.interp.getdebugger(): + self.interp.restart_subprocess() + else: + self.interp.interrupt_subprocess() + if self.reading: + self.top.quit() # exit the nested mainloop() in readline() + return "break" + + def eof_callback(self, event): + if self.executing and not self.reading: + return # Let the default binding (delete next char) take over + if not (self.text.compare("iomark", "==", "insert") and + self.text.compare("insert", "==", "end-1c")): + return # Let the default binding (delete next char) take over + if not self.executing: + self.resetoutput() + self.close() + else: + self.canceled = 0 + self.endoffile = 1 + self.top.quit() + return "break" + + def linefeed_callback(self, event): + # Insert a linefeed without entering anything (still autoindented) + if self.reading: + self.text.insert("insert", "\n") + self.text.see("insert") + else: + self.newline_and_indent_event(event) + return "break" + + def enter_callback(self, event): + if self.executing and not self.reading: + return # Let the default binding (insert '\n') take over + # If some text is selected, recall the selection + # (but only if this before the I/O mark) + try: + sel = self.text.get("sel.first", "sel.last") + if sel: + if self.text.compare("sel.last", "<=", "iomark"): + self.recall(sel, event) + return "break" + except: + pass + # If we're strictly before the line containing iomark, recall + # the current line, less a leading prompt, less leading or + # trailing whitespace + if self.text.compare("insert", "<", "iomark linestart"): + # Check if there's a relevant stdin range -- if so, use it + prev = self.text.tag_prevrange("stdin", "insert") + if prev and self.text.compare("insert", "<", prev[1]): + self.recall(self.text.get(prev[0], prev[1]), event) + return "break" + next = self.text.tag_nextrange("stdin", "insert") + if next and self.text.compare("insert lineend", ">=", next[0]): + self.recall(self.text.get(next[0], next[1]), event) + return "break" + # No stdin mark -- just get the current line, less any prompt + indices = self.text.tag_nextrange("console", "insert linestart") + if indices and \ + self.text.compare(indices[0], "<=", "insert linestart"): + self.recall(self.text.get(indices[1], "insert lineend"), event) + else: + self.recall(self.text.get("insert linestart", "insert lineend"), event) + return "break" + # If we're between the beginning of the line and the iomark, i.e. + # in the prompt area, move to the end of the prompt + if self.text.compare("insert", "<", "iomark"): + self.text.mark_set("insert", "iomark") + # If we're in the current input and there's only whitespace + # beyond the cursor, erase that whitespace first + s = self.text.get("insert", "end-1c") + if s and not s.strip(): + self.text.delete("insert", "end-1c") + # If we're in the current input before its last line, + # insert a newline right at the insert point + if self.text.compare("insert", "<", "end-1c linestart"): + self.newline_and_indent_event(event) + return "break" + # We're in the last line; append a newline and submit it + self.text.mark_set("insert", "end-1c") + if self.reading: + self.text.insert("insert", "\n") + self.text.see("insert") + else: + self.newline_and_indent_event(event) + self.text.tag_add("stdin", "iomark", "end-1c") + self.text.update_idletasks() + if self.reading: + self.top.quit() # Break out of recursive mainloop() + else: + self.runit() + return "break" + + def recall(self, s, event): + # remove leading and trailing empty or whitespace lines + s = re.sub(r'^\s*\n', '' , s) + s = re.sub(r'\n\s*$', '', s) + lines = s.split('\n') + self.text.undo_block_start() + try: + self.text.tag_remove("sel", "1.0", "end") + self.text.mark_set("insert", "end-1c") + prefix = self.text.get("insert linestart", "insert") + if prefix.rstrip().endswith(':'): + self.newline_and_indent_event(event) + prefix = self.text.get("insert linestart", "insert") + self.text.insert("insert", lines[0].strip()) + if len(lines) > 1: + orig_base_indent = re.search(r'^([ \t]*)', lines[0]).group(0) + new_base_indent = re.search(r'^([ \t]*)', prefix).group(0) + for line in lines[1:]: + if line.startswith(orig_base_indent): + # replace orig base indentation with new indentation + line = new_base_indent + line[len(orig_base_indent):] + self.text.insert('insert', '\n'+line.rstrip()) + finally: + self.text.see("insert") + self.text.undo_block_stop() + + def runit(self): + line = self.text.get("iomark", "end-1c") + # Strip off last newline and surrounding whitespace. + # (To allow you to hit return twice to end a statement.) + i = len(line) + while i > 0 and line[i-1] in " \t": + i = i-1 + if i > 0 and line[i-1] == "\n": + i = i-1 + while i > 0 and line[i-1] in " \t": + i = i-1 + line = line[:i] + self.interp.runsource(line) + + def open_stack_viewer(self, event=None): + if self.interp.rpcclt: + return self.interp.remote_stack_viewer() + try: + sys.last_traceback + except: + tkMessageBox.showerror("No stack trace", + "There is no stack trace yet.\n" + "(sys.last_traceback is not defined)", + parent=self.text) + return + from idlelib.stackviewer import StackBrowser + StackBrowser(self.root, self.flist) + + def view_restart_mark(self, event=None): + self.text.see("iomark") + self.text.see("restart") + + def restart_shell(self, event=None): + "Callback for Run/Restart Shell Cntl-F6" + self.interp.restart_subprocess(with_cwd=True) + + def showprompt(self): + self.resetoutput() + try: + s = str(sys.ps1) + except: + s = "" + self.console.write(s) + self.text.mark_set("insert", "end-1c") + self.set_line_and_column() + self.io.reset_undo() + + def resetoutput(self): + source = self.text.get("iomark", "end-1c") + if self.history: + self.history.store(source) + if self.text.get("end-2c") != "\n": + self.text.insert("end-1c", "\n") + self.text.mark_set("iomark", "end-1c") + self.set_line_and_column() + + def write(self, s, tags=()): + if isinstance(s, str) and len(s) and max(s) > '\uffff': + # Tk doesn't support outputting non-BMP characters + # Let's assume what printed string is not very long, + # find first non-BMP character and construct informative + # UnicodeEncodeError exception. + for start, char in enumerate(s): + if char > '\uffff': + break + raise UnicodeEncodeError("UCS-2", char, start, start+1, + 'Non-BMP character not supported in Tk') + try: + self.text.mark_gravity("iomark", "right") + count = OutputWindow.write(self, s, tags, "iomark") + self.text.mark_gravity("iomark", "left") + except: + raise ###pass # ### 11Aug07 KBK if we are expecting exceptions + # let's find out what they are and be specific. + if self.canceled: + self.canceled = 0 + if not use_subprocess: + raise KeyboardInterrupt + return count + + def rmenu_check_cut(self): + try: + if self.text.compare('sel.first', '<', 'iomark'): + return 'disabled' + except TclError: # no selection, so the index 'sel.first' doesn't exist + return 'disabled' + return super().rmenu_check_cut() + + def rmenu_check_paste(self): + if self.text.compare('insert','<','iomark'): + return 'disabled' + return super().rmenu_check_paste() + +class PseudoFile(io.TextIOBase): + + def __init__(self, shell, tags, encoding=None): + self.shell = shell + self.tags = tags + self._encoding = encoding + + @property + def encoding(self): + return self._encoding + + @property + def name(self): + return '<%s>' % self.tags + + def isatty(self): + return True + + +class PseudoOutputFile(PseudoFile): + + def writable(self): + return True + + def write(self, s): + if self.closed: + raise ValueError("write to closed file") + if type(s) is not str: + if not isinstance(s, str): + raise TypeError('must be str, not ' + type(s).__name__) + # See issue #19481 + s = str.__str__(s) + return self.shell.write(s, self.tags) + + +class PseudoInputFile(PseudoFile): + + def __init__(self, shell, tags, encoding=None): + PseudoFile.__init__(self, shell, tags, encoding) + self._line_buffer = '' + + def readable(self): + return True + + def read(self, size=-1): + if self.closed: + raise ValueError("read from closed file") + if size is None: + size = -1 + elif not isinstance(size, int): + raise TypeError('must be int, not ' + type(size).__name__) + result = self._line_buffer + self._line_buffer = '' + if size < 0: + while True: + line = self.shell.readline() + if not line: break + result += line + else: + while len(result) < size: + line = self.shell.readline() + if not line: break + result += line + self._line_buffer = result[size:] + result = result[:size] + return result + + def readline(self, size=-1): + if self.closed: + raise ValueError("read from closed file") + if size is None: + size = -1 + elif not isinstance(size, int): + raise TypeError('must be int, not ' + type(size).__name__) + line = self._line_buffer or self.shell.readline() + if size < 0: + size = len(line) + eol = line.find('\n', 0, size) + if eol >= 0: + size = eol + 1 + self._line_buffer = line[size:] + return line[:size] + + def close(self): + self.shell.close() + + +usage_msg = """\ + +USAGE: idle [-deins] [-t title] [file]* + idle [-dns] [-t title] (-c cmd | -r file) [arg]* + idle [-dns] [-t title] - [arg]* + + -h print this help message and exit + -n run IDLE without a subprocess (DEPRECATED, + see Help/IDLE Help for details) + +The following options will override the IDLE 'settings' configuration: + + -e open an edit window + -i open a shell window + +The following options imply -i and will open a shell: + + -c cmd run the command in a shell, or + -r file run script from file + + -d enable the debugger + -s run $IDLESTARTUP or $PYTHONSTARTUP before anything else + -t title set title of shell window + +A default edit window will be bypassed when -c, -r, or - are used. + +[arg]* are passed to the command (-c) or script (-r) in sys.argv[1:]. + +Examples: + +idle + Open an edit window or shell depending on IDLE's configuration. + +idle foo.py foobar.py + Edit the files, also open a shell if configured to start with shell. + +idle -est "Baz" foo.py + Run $IDLESTARTUP or $PYTHONSTARTUP, edit foo.py, and open a shell + window with the title "Baz". + +idle -c "import sys; print(sys.argv)" "foo" + Open a shell window and run the command, passing "-c" in sys.argv[0] + and "foo" in sys.argv[1]. + +idle -d -s -r foo.py "Hello World" + Open a shell window, run a startup script, enable the debugger, and + run foo.py, passing "foo.py" in sys.argv[0] and "Hello World" in + sys.argv[1]. + +echo "import sys; print(sys.argv)" | idle - "foobar" + Open a shell window, run the script piped in, passing '' in sys.argv[0] + and "foobar" in sys.argv[1]. +""" + +def main(): + global flist, root, use_subprocess + + capture_warnings(True) + use_subprocess = True + enable_shell = False + enable_edit = False + debug = False + cmd = None + script = None + startup = False + try: + opts, args = getopt.getopt(sys.argv[1:], "c:deihnr:st:") + except getopt.error as msg: + print("Error: %s\n%s" % (msg, usage_msg), file=sys.stderr) + sys.exit(2) + for o, a in opts: + if o == '-c': + cmd = a + enable_shell = True + if o == '-d': + debug = True + enable_shell = True + if o == '-e': + enable_edit = True + if o == '-h': + sys.stdout.write(usage_msg) + sys.exit() + if o == '-i': + enable_shell = True + if o == '-n': + print(" Warning: running IDLE without a subprocess is deprecated.", + file=sys.stderr) + use_subprocess = False + if o == '-r': + script = a + if os.path.isfile(script): + pass + else: + print("No script file: ", script) + sys.exit() + enable_shell = True + if o == '-s': + startup = True + enable_shell = True + if o == '-t': + PyShell.shell_title = a + enable_shell = True + if args and args[0] == '-': + cmd = sys.stdin.read() + enable_shell = True + # process sys.argv and sys.path: + for i in range(len(sys.path)): + sys.path[i] = os.path.abspath(sys.path[i]) + if args and args[0] == '-': + sys.argv = [''] + args[1:] + elif cmd: + sys.argv = ['-c'] + args + elif script: + sys.argv = [script] + args + elif args: + enable_edit = True + pathx = [] + for filename in args: + pathx.append(os.path.dirname(filename)) + for dir in pathx: + dir = os.path.abspath(dir) + if not dir in sys.path: + sys.path.insert(0, dir) + else: + dir = os.getcwd() + if dir not in sys.path: + sys.path.insert(0, dir) + # check the IDLE settings configuration (but command line overrides) + edit_start = idleConf.GetOption('main', 'General', + 'editor-on-startup', type='bool') + enable_edit = enable_edit or edit_start + enable_shell = enable_shell or not enable_edit + # start editor and/or shell windows: + root = Tk(className="Idle") + + # set application icon + icondir = os.path.join(os.path.dirname(__file__), 'Icons') + if system() == 'Windows': + iconfile = os.path.join(icondir, 'idle.ico') + root.wm_iconbitmap(default=iconfile) + else: + ext = '.png' if TkVersion >= 8.6 else '.gif' + iconfiles = [os.path.join(icondir, 'idle_%d%s' % (size, ext)) + for size in (16, 32, 48)] + icons = [PhotoImage(file=iconfile) for iconfile in iconfiles] + root.wm_iconphoto(True, *icons) + + fixwordbreaks(root) + root.withdraw() + flist = PyShellFileList(root) + macosx.setupApp(root, flist) + + if enable_edit: + if not (cmd or script): + for filename in args[:]: + if flist.open(filename) is None: + # filename is a directory actually, disconsider it + args.remove(filename) + if not args: + flist.new() + + if enable_shell: + shell = flist.open_shell() + if not shell: + return # couldn't open shell + if macosx.isAquaTk() and flist.dict: + # On OSX: when the user has double-clicked on a file that causes + # IDLE to be launched the shell window will open just in front of + # the file she wants to see. Lower the interpreter window when + # there are open files. + shell.top.lower() + else: + shell = flist.pyshell + + # Handle remaining options. If any of these are set, enable_shell + # was set also, so shell must be true to reach here. + if debug: + shell.open_debugger() + if startup: + filename = os.environ.get("IDLESTARTUP") or \ + os.environ.get("PYTHONSTARTUP") + if filename and os.path.isfile(filename): + shell.interp.execfile(filename) + if cmd or script: + shell.interp.runcommand("""if 1: + import sys as _sys + _sys.argv = %r + del _sys + \n""" % (sys.argv,)) + if cmd: + shell.interp.execsource(cmd) + elif script: + shell.interp.prepend_syspath(script) + shell.interp.execfile(script) + elif shell: + # If there is a shell window and no cmd or script in progress, + # check for problematic OS X Tk versions and print a warning + # message in the IDLE shell window; this is less intrusive + # than always opening a separate window. + tkversionwarning = macosx.tkVersionWarning(root) + if tkversionwarning: + shell.interp.runcommand("print('%s')" % tkversionwarning) + + while flist.inversedict: # keep IDLE running while files are open. + root.mainloop() + root.destroy() + capture_warnings(False) + +if __name__ == "__main__": + sys.modules['pyshell'] = sys.modules['__main__'] + main() + +capture_warnings(False) # Make sure turned off; see issue 18081 diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py deleted file mode 100755 index 38c12cd8bf..0000000000 --- a/Lib/idlelib/pyshell.py +++ /dev/null @@ -1,1618 +0,0 @@ -#! /usr/bin/env python3 - -try: - from tkinter import * -except ImportError: - print("** IDLE can't import Tkinter.\n" - "Your Python may not be configured for Tk. **", file=sys.__stderr__) - sys.exit(1) -import tkinter.messagebox as tkMessageBox -if TkVersion < 8.5: - root = Tk() # otherwise create root in main - root.withdraw() - tkMessageBox.showerror("Idle Cannot Start", - "Idle requires tcl/tk 8.5+, not $s." % TkVersion, - parent=root) - sys.exit(1) - -import getopt -import os -import os.path -import re -import socket -import subprocess -import sys -import threading -import time -import tokenize -import io - -import linecache -from code import InteractiveInterpreter -from platform import python_version, system - -from idlelib.editor import EditorWindow, fixwordbreaks -from idlelib.filelist import FileList -from idlelib.colorizer import ColorDelegator -from idlelib.undo import UndoDelegator -from idlelib.outwin import OutputWindow -from idlelib.config import idleConf -from idlelib import rpc -from idlelib import debugger -from idlelib import debugger_r -from idlelib import macosx - -HOST = '127.0.0.1' # python execution server on localhost loopback -PORT = 0 # someday pass in host, port for remote debug capability - -# Override warnings module to write to warning_stream. Initialize to send IDLE -# internal warnings to the console. ScriptBinding.check_syntax() will -# temporarily redirect the stream to the shell window to display warnings when -# checking user's code. -warning_stream = sys.__stderr__ # None, at least on Windows, if no console. -import warnings - -def idle_formatwarning(message, category, filename, lineno, line=None): - """Format warnings the IDLE way.""" - - s = "\nWarning (from warnings module):\n" - s += ' File \"%s\", line %s\n' % (filename, lineno) - if line is None: - line = linecache.getline(filename, lineno) - line = line.strip() - if line: - s += " %s\n" % line - s += "%s: %s\n" % (category.__name__, message) - return s - -def idle_showwarning( - message, category, filename, lineno, file=None, line=None): - """Show Idle-format warning (after replacing warnings.showwarning). - - The differences are the formatter called, the file=None replacement, - which can be None, the capture of the consequence AttributeError, - and the output of a hard-coded prompt. - """ - if file is None: - file = warning_stream - try: - file.write(idle_formatwarning( - message, category, filename, lineno, line=line)) - file.write(">>> ") - except (AttributeError, OSError): - pass # if file (probably __stderr__) is invalid, skip warning. - -_warnings_showwarning = None - -def capture_warnings(capture): - "Replace warning.showwarning with idle_showwarning, or reverse." - - global _warnings_showwarning - if capture: - if _warnings_showwarning is None: - _warnings_showwarning = warnings.showwarning - warnings.showwarning = idle_showwarning - else: - if _warnings_showwarning is not None: - warnings.showwarning = _warnings_showwarning - _warnings_showwarning = None - -capture_warnings(True) - -def extended_linecache_checkcache(filename=None, - orig_checkcache=linecache.checkcache): - """Extend linecache.checkcache to preserve the entries - - Rather than repeating the linecache code, patch it to save the - entries, call the original linecache.checkcache() - (skipping them), and then restore the saved entries. - - orig_checkcache is bound at definition time to the original - method, allowing it to be patched. - """ - cache = linecache.cache - save = {} - for key in list(cache): - if key[:1] + key[-1:] == '<>': - save[key] = cache.pop(key) - orig_checkcache(filename) - cache.update(save) - -# Patch linecache.checkcache(): -linecache.checkcache = extended_linecache_checkcache - - -class PyShellEditorWindow(EditorWindow): - "Regular text edit window in IDLE, supports breakpoints" - - def __init__(self, *args): - self.breakpoints = [] - EditorWindow.__init__(self, *args) - self.text.bind("<>", self.set_breakpoint_here) - self.text.bind("<>", self.clear_breakpoint_here) - self.text.bind("<>", self.flist.open_shell) - - self.breakpointPath = os.path.join(idleConf.GetUserCfgDir(), - 'breakpoints.lst') - # whenever a file is changed, restore breakpoints - def filename_changed_hook(old_hook=self.io.filename_change_hook, - self=self): - self.restore_file_breaks() - old_hook() - self.io.set_filename_change_hook(filename_changed_hook) - if self.io.filename: - self.restore_file_breaks() - self.color_breakpoint_text() - - rmenu_specs = [ - ("Cut", "<>", "rmenu_check_cut"), - ("Copy", "<>", "rmenu_check_copy"), - ("Paste", "<>", "rmenu_check_paste"), - (None, None, None), - ("Set Breakpoint", "<>", None), - ("Clear Breakpoint", "<>", None) - ] - - def color_breakpoint_text(self, color=True): - "Turn colorizing of breakpoint text on or off" - if self.io is None: - # possible due to update in restore_file_breaks - return - if color: - theme = idleConf.CurrentTheme() - cfg = idleConf.GetHighlight(theme, "break") - else: - cfg = {'foreground': '', 'background': ''} - self.text.tag_config('BREAK', cfg) - - def set_breakpoint(self, lineno): - text = self.text - filename = self.io.filename - text.tag_add("BREAK", "%d.0" % lineno, "%d.0" % (lineno+1)) - try: - self.breakpoints.index(lineno) - except ValueError: # only add if missing, i.e. do once - self.breakpoints.append(lineno) - try: # update the subprocess debugger - debug = self.flist.pyshell.interp.debugger - debug.set_breakpoint_here(filename, lineno) - except: # but debugger may not be active right now.... - pass - - def set_breakpoint_here(self, event=None): - text = self.text - filename = self.io.filename - if not filename: - text.bell() - return - lineno = int(float(text.index("insert"))) - self.set_breakpoint(lineno) - - def clear_breakpoint_here(self, event=None): - text = self.text - filename = self.io.filename - if not filename: - text.bell() - return - lineno = int(float(text.index("insert"))) - try: - self.breakpoints.remove(lineno) - except: - pass - text.tag_remove("BREAK", "insert linestart",\ - "insert lineend +1char") - try: - debug = self.flist.pyshell.interp.debugger - debug.clear_breakpoint_here(filename, lineno) - except: - pass - - def clear_file_breaks(self): - if self.breakpoints: - text = self.text - filename = self.io.filename - if not filename: - text.bell() - return - self.breakpoints = [] - text.tag_remove("BREAK", "1.0", END) - try: - debug = self.flist.pyshell.interp.debugger - debug.clear_file_breaks(filename) - except: - pass - - def store_file_breaks(self): - "Save breakpoints when file is saved" - # XXX 13 Dec 2002 KBK Currently the file must be saved before it can - # be run. The breaks are saved at that time. If we introduce - # a temporary file save feature the save breaks functionality - # needs to be re-verified, since the breaks at the time the - # temp file is created may differ from the breaks at the last - # permanent save of the file. Currently, a break introduced - # after a save will be effective, but not persistent. - # This is necessary to keep the saved breaks synched with the - # saved file. - # - # Breakpoints are set as tagged ranges in the text. - # Since a modified file has to be saved before it is - # run, and since self.breakpoints (from which the subprocess - # debugger is loaded) is updated during the save, the visible - # breaks stay synched with the subprocess even if one of these - # unexpected breakpoint deletions occurs. - breaks = self.breakpoints - filename = self.io.filename - try: - with open(self.breakpointPath, "r") as fp: - lines = fp.readlines() - except OSError: - lines = [] - try: - with open(self.breakpointPath, "w") as new_file: - for line in lines: - if not line.startswith(filename + '='): - new_file.write(line) - self.update_breakpoints() - breaks = self.breakpoints - if breaks: - new_file.write(filename + '=' + str(breaks) + '\n') - except OSError as err: - if not getattr(self.root, "breakpoint_error_displayed", False): - self.root.breakpoint_error_displayed = True - tkMessageBox.showerror(title='IDLE Error', - message='Unable to update breakpoint list:\n%s' - % str(err), - parent=self.text) - - def restore_file_breaks(self): - self.text.update() # this enables setting "BREAK" tags to be visible - if self.io is None: - # can happen if IDLE closes due to the .update() call - return - filename = self.io.filename - if filename is None: - return - if os.path.isfile(self.breakpointPath): - with open(self.breakpointPath, "r") as fp: - lines = fp.readlines() - for line in lines: - if line.startswith(filename + '='): - breakpoint_linenumbers = eval(line[len(filename)+1:]) - for breakpoint_linenumber in breakpoint_linenumbers: - self.set_breakpoint(breakpoint_linenumber) - - def update_breakpoints(self): - "Retrieves all the breakpoints in the current window" - text = self.text - ranges = text.tag_ranges("BREAK") - linenumber_list = self.ranges_to_linenumbers(ranges) - self.breakpoints = linenumber_list - - def ranges_to_linenumbers(self, ranges): - lines = [] - for index in range(0, len(ranges), 2): - lineno = int(float(ranges[index].string)) - end = int(float(ranges[index+1].string)) - while lineno < end: - lines.append(lineno) - lineno += 1 - return lines - -# XXX 13 Dec 2002 KBK Not used currently -# def saved_change_hook(self): -# "Extend base method - clear breaks if module is modified" -# if not self.get_saved(): -# self.clear_file_breaks() -# EditorWindow.saved_change_hook(self) - - def _close(self): - "Extend base method - clear breaks when module is closed" - self.clear_file_breaks() - EditorWindow._close(self) - - -class PyShellFileList(FileList): - "Extend base class: IDLE supports a shell and breakpoints" - - # override FileList's class variable, instances return PyShellEditorWindow - # instead of EditorWindow when new edit windows are created. - EditorWindow = PyShellEditorWindow - - pyshell = None - - def open_shell(self, event=None): - if self.pyshell: - self.pyshell.top.wakeup() - else: - self.pyshell = PyShell(self) - if self.pyshell: - if not self.pyshell.begin(): - return None - return self.pyshell - - -class ModifiedColorDelegator(ColorDelegator): - "Extend base class: colorizer for the shell window itself" - - def __init__(self): - ColorDelegator.__init__(self) - self.LoadTagDefs() - - def recolorize_main(self): - self.tag_remove("TODO", "1.0", "iomark") - self.tag_add("SYNC", "1.0", "iomark") - ColorDelegator.recolorize_main(self) - - def LoadTagDefs(self): - ColorDelegator.LoadTagDefs(self) - theme = idleConf.CurrentTheme() - self.tagdefs.update({ - "stdin": {'background':None,'foreground':None}, - "stdout": idleConf.GetHighlight(theme, "stdout"), - "stderr": idleConf.GetHighlight(theme, "stderr"), - "console": idleConf.GetHighlight(theme, "console"), - }) - - def removecolors(self): - # Don't remove shell color tags before "iomark" - for tag in self.tagdefs: - self.tag_remove(tag, "iomark", "end") - -class ModifiedUndoDelegator(UndoDelegator): - "Extend base class: forbid insert/delete before the I/O mark" - - def insert(self, index, chars, tags=None): - try: - if self.delegate.compare(index, "<", "iomark"): - self.delegate.bell() - return - except TclError: - pass - UndoDelegator.insert(self, index, chars, tags) - - def delete(self, index1, index2=None): - try: - if self.delegate.compare(index1, "<", "iomark"): - self.delegate.bell() - return - except TclError: - pass - UndoDelegator.delete(self, index1, index2) - - -class MyRPCClient(rpc.RPCClient): - - def handle_EOF(self): - "Override the base class - just re-raise EOFError" - raise EOFError - - -class ModifiedInterpreter(InteractiveInterpreter): - - def __init__(self, tkconsole): - self.tkconsole = tkconsole - locals = sys.modules['__main__'].__dict__ - InteractiveInterpreter.__init__(self, locals=locals) - self.save_warnings_filters = None - self.restarting = False - self.subprocess_arglist = None - self.port = PORT - self.original_compiler_flags = self.compile.compiler.flags - - _afterid = None - rpcclt = None - rpcsubproc = None - - def spawn_subprocess(self): - if self.subprocess_arglist is None: - self.subprocess_arglist = self.build_subprocess_arglist() - self.rpcsubproc = subprocess.Popen(self.subprocess_arglist) - - def build_subprocess_arglist(self): - assert (self.port!=0), ( - "Socket should have been assigned a port number.") - w = ['-W' + s for s in sys.warnoptions] - # Maybe IDLE is installed and is being accessed via sys.path, - # or maybe it's not installed and the idle.py script is being - # run from the IDLE source directory. - del_exitf = idleConf.GetOption('main', 'General', 'delete-exitfunc', - default=False, type='bool') - if __name__ == 'idlelib.pyshell': - command = "__import__('idlelib.run').run.main(%r)" % (del_exitf,) - else: - command = "__import__('run').main(%r)" % (del_exitf,) - return [sys.executable] + w + ["-c", command, str(self.port)] - - def start_subprocess(self): - addr = (HOST, self.port) - # GUI makes several attempts to acquire socket, listens for connection - for i in range(3): - time.sleep(i) - try: - self.rpcclt = MyRPCClient(addr) - break - except OSError: - pass - else: - self.display_port_binding_error() - return None - # if PORT was 0, system will assign an 'ephemeral' port. Find it out: - self.port = self.rpcclt.listening_sock.getsockname()[1] - # if PORT was not 0, probably working with a remote execution server - if PORT != 0: - # To allow reconnection within the 2MSL wait (cf. Stevens TCP - # V1, 18.6), set SO_REUSEADDR. Note that this can be problematic - # on Windows since the implementation allows two active sockets on - # the same address! - self.rpcclt.listening_sock.setsockopt(socket.SOL_SOCKET, - socket.SO_REUSEADDR, 1) - self.spawn_subprocess() - #time.sleep(20) # test to simulate GUI not accepting connection - # Accept the connection from the Python execution server - self.rpcclt.listening_sock.settimeout(10) - try: - self.rpcclt.accept() - except socket.timeout: - self.display_no_subprocess_error() - return None - self.rpcclt.register("console", self.tkconsole) - self.rpcclt.register("stdin", self.tkconsole.stdin) - self.rpcclt.register("stdout", self.tkconsole.stdout) - self.rpcclt.register("stderr", self.tkconsole.stderr) - self.rpcclt.register("flist", self.tkconsole.flist) - self.rpcclt.register("linecache", linecache) - self.rpcclt.register("interp", self) - self.transfer_path(with_cwd=True) - self.poll_subprocess() - return self.rpcclt - - def restart_subprocess(self, with_cwd=False, filename=''): - if self.restarting: - return self.rpcclt - self.restarting = True - # close only the subprocess debugger - debug = self.getdebugger() - if debug: - try: - # Only close subprocess debugger, don't unregister gui_adap! - debugger_r.close_subprocess_debugger(self.rpcclt) - except: - pass - # Kill subprocess, spawn a new one, accept connection. - self.rpcclt.close() - self.terminate_subprocess() - console = self.tkconsole - was_executing = console.executing - console.executing = False - self.spawn_subprocess() - try: - self.rpcclt.accept() - except socket.timeout: - self.display_no_subprocess_error() - return None - self.transfer_path(with_cwd=with_cwd) - console.stop_readline() - # annotate restart in shell window and mark it - console.text.delete("iomark", "end-1c") - tag = 'RESTART: ' + (filename if filename else 'Shell') - halfbar = ((int(console.width) -len(tag) - 4) // 2) * '=' - console.write("\n{0} {1} {0}".format(halfbar, tag)) - console.text.mark_set("restart", "end-1c") - console.text.mark_gravity("restart", "left") - if not filename: - console.showprompt() - # restart subprocess debugger - if debug: - # Restarted debugger connects to current instance of debug GUI - debugger_r.restart_subprocess_debugger(self.rpcclt) - # reload remote debugger breakpoints for all PyShellEditWindows - debug.load_breakpoints() - self.compile.compiler.flags = self.original_compiler_flags - self.restarting = False - return self.rpcclt - - def __request_interrupt(self): - self.rpcclt.remotecall("exec", "interrupt_the_server", (), {}) - - def interrupt_subprocess(self): - threading.Thread(target=self.__request_interrupt).start() - - def kill_subprocess(self): - if self._afterid is not None: - self.tkconsole.text.after_cancel(self._afterid) - try: - self.rpcclt.listening_sock.close() - except AttributeError: # no socket - pass - try: - self.rpcclt.close() - except AttributeError: # no socket - pass - self.terminate_subprocess() - self.tkconsole.executing = False - self.rpcclt = None - - def terminate_subprocess(self): - "Make sure subprocess is terminated" - try: - self.rpcsubproc.kill() - except OSError: - # process already terminated - return - else: - try: - self.rpcsubproc.wait() - except OSError: - return - - def transfer_path(self, with_cwd=False): - if with_cwd: # Issue 13506 - path = [''] # include Current Working Directory - path.extend(sys.path) - else: - path = sys.path - - self.runcommand("""if 1: - import sys as _sys - _sys.path = %r - del _sys - \n""" % (path,)) - - active_seq = None - - def poll_subprocess(self): - clt = self.rpcclt - if clt is None: - return - try: - response = clt.pollresponse(self.active_seq, wait=0.05) - except (EOFError, OSError, KeyboardInterrupt): - # lost connection or subprocess terminated itself, restart - # [the KBI is from rpc.SocketIO.handle_EOF()] - if self.tkconsole.closing: - return - response = None - self.restart_subprocess() - if response: - self.tkconsole.resetoutput() - self.active_seq = None - how, what = response - console = self.tkconsole.console - if how == "OK": - if what is not None: - print(repr(what), file=console) - elif how == "EXCEPTION": - if self.tkconsole.getvar("<>"): - self.remote_stack_viewer() - elif how == "ERROR": - errmsg = "pyshell.ModifiedInterpreter: Subprocess ERROR:\n" - print(errmsg, what, file=sys.__stderr__) - print(errmsg, what, file=console) - # we received a response to the currently active seq number: - try: - self.tkconsole.endexecuting() - except AttributeError: # shell may have closed - pass - # Reschedule myself - if not self.tkconsole.closing: - self._afterid = self.tkconsole.text.after( - self.tkconsole.pollinterval, self.poll_subprocess) - - debugger = None - - def setdebugger(self, debugger): - self.debugger = debugger - - def getdebugger(self): - return self.debugger - - def open_remote_stack_viewer(self): - """Initiate the remote stack viewer from a separate thread. - - This method is called from the subprocess, and by returning from this - method we allow the subprocess to unblock. After a bit the shell - requests the subprocess to open the remote stack viewer which returns a - static object looking at the last exception. It is queried through - the RPC mechanism. - - """ - self.tkconsole.text.after(300, self.remote_stack_viewer) - return - - def remote_stack_viewer(self): - from idlelib import debugobj_r - oid = self.rpcclt.remotequeue("exec", "stackviewer", ("flist",), {}) - if oid is None: - self.tkconsole.root.bell() - return - item = debugobj_r.StubObjectTreeItem(self.rpcclt, oid) - from idlelib.tree import ScrolledCanvas, TreeNode - top = Toplevel(self.tkconsole.root) - theme = idleConf.CurrentTheme() - background = idleConf.GetHighlight(theme, 'normal')['background'] - sc = ScrolledCanvas(top, bg=background, highlightthickness=0) - sc.frame.pack(expand=1, fill="both") - node = TreeNode(sc.canvas, None, item) - node.expand() - # XXX Should GC the remote tree when closing the window - - gid = 0 - - def execsource(self, source): - "Like runsource() but assumes complete exec source" - filename = self.stuffsource(source) - self.execfile(filename, source) - - def execfile(self, filename, source=None): - "Execute an existing file" - if source is None: - with tokenize.open(filename) as fp: - source = fp.read() - try: - code = compile(source, filename, "exec") - except (OverflowError, SyntaxError): - self.tkconsole.resetoutput() - print('*** Error in script or command!\n' - 'Traceback (most recent call last):', - file=self.tkconsole.stderr) - InteractiveInterpreter.showsyntaxerror(self, filename) - self.tkconsole.showprompt() - else: - self.runcode(code) - - def runsource(self, source): - "Extend base class method: Stuff the source in the line cache first" - filename = self.stuffsource(source) - self.more = 0 - self.save_warnings_filters = warnings.filters[:] - warnings.filterwarnings(action="error", category=SyntaxWarning) - # at the moment, InteractiveInterpreter expects str - assert isinstance(source, str) - #if isinstance(source, str): - # from idlelib import iomenu - # try: - # source = source.encode(iomenu.encoding) - # except UnicodeError: - # self.tkconsole.resetoutput() - # self.write("Unsupported characters in input\n") - # return - try: - # InteractiveInterpreter.runsource() calls its runcode() method, - # which is overridden (see below) - return InteractiveInterpreter.runsource(self, source, filename) - finally: - if self.save_warnings_filters is not None: - warnings.filters[:] = self.save_warnings_filters - self.save_warnings_filters = None - - def stuffsource(self, source): - "Stuff source in the filename cache" - filename = "" % self.gid - self.gid = self.gid + 1 - lines = source.split("\n") - linecache.cache[filename] = len(source)+1, 0, lines, filename - return filename - - def prepend_syspath(self, filename): - "Prepend sys.path with file's directory if not already included" - self.runcommand("""if 1: - _filename = %r - import sys as _sys - from os.path import dirname as _dirname - _dir = _dirname(_filename) - if not _dir in _sys.path: - _sys.path.insert(0, _dir) - del _filename, _sys, _dirname, _dir - \n""" % (filename,)) - - def showsyntaxerror(self, filename=None): - """Override Interactive Interpreter method: Use Colorizing - - Color the offending position instead of printing it and pointing at it - with a caret. - - """ - tkconsole = self.tkconsole - text = tkconsole.text - text.tag_remove("ERROR", "1.0", "end") - type, value, tb = sys.exc_info() - msg = getattr(value, 'msg', '') or value or "" - lineno = getattr(value, 'lineno', '') or 1 - offset = getattr(value, 'offset', '') or 0 - if offset == 0: - lineno += 1 #mark end of offending line - if lineno == 1: - pos = "iomark + %d chars" % (offset-1) - else: - pos = "iomark linestart + %d lines + %d chars" % \ - (lineno-1, offset-1) - tkconsole.colorize_syntax_error(text, pos) - tkconsole.resetoutput() - self.write("SyntaxError: %s\n" % msg) - tkconsole.showprompt() - - def showtraceback(self): - "Extend base class method to reset output properly" - self.tkconsole.resetoutput() - self.checklinecache() - InteractiveInterpreter.showtraceback(self) - if self.tkconsole.getvar("<>"): - self.tkconsole.open_stack_viewer() - - def checklinecache(self): - c = linecache.cache - for key in list(c.keys()): - if key[:1] + key[-1:] != "<>": - del c[key] - - def runcommand(self, code): - "Run the code without invoking the debugger" - # The code better not raise an exception! - if self.tkconsole.executing: - self.display_executing_dialog() - return 0 - if self.rpcclt: - self.rpcclt.remotequeue("exec", "runcode", (code,), {}) - else: - exec(code, self.locals) - return 1 - - def runcode(self, code): - "Override base class method" - if self.tkconsole.executing: - self.interp.restart_subprocess() - self.checklinecache() - if self.save_warnings_filters is not None: - warnings.filters[:] = self.save_warnings_filters - self.save_warnings_filters = None - debugger = self.debugger - try: - self.tkconsole.beginexecuting() - if not debugger and self.rpcclt is not None: - self.active_seq = self.rpcclt.asyncqueue("exec", "runcode", - (code,), {}) - elif debugger: - debugger.run(code, self.locals) - else: - exec(code, self.locals) - except SystemExit: - if not self.tkconsole.closing: - if tkMessageBox.askyesno( - "Exit?", - "Do you want to exit altogether?", - default="yes", - parent=self.tkconsole.text): - raise - else: - self.showtraceback() - else: - raise - except: - if use_subprocess: - print("IDLE internal error in runcode()", - file=self.tkconsole.stderr) - self.showtraceback() - self.tkconsole.endexecuting() - else: - if self.tkconsole.canceled: - self.tkconsole.canceled = False - print("KeyboardInterrupt", file=self.tkconsole.stderr) - else: - self.showtraceback() - finally: - if not use_subprocess: - try: - self.tkconsole.endexecuting() - except AttributeError: # shell may have closed - pass - - def write(self, s): - "Override base class method" - return self.tkconsole.stderr.write(s) - - def display_port_binding_error(self): - tkMessageBox.showerror( - "Port Binding Error", - "IDLE can't bind to a TCP/IP port, which is necessary to " - "communicate with its Python execution server. This might be " - "because no networking is installed on this computer. " - "Run IDLE with the -n command line switch to start without a " - "subprocess and refer to Help/IDLE Help 'Running without a " - "subprocess' for further details.", - parent=self.tkconsole.text) - - def display_no_subprocess_error(self): - tkMessageBox.showerror( - "Subprocess Startup Error", - "IDLE's subprocess didn't make connection. Either IDLE can't " - "start a subprocess or personal firewall software is blocking " - "the connection.", - parent=self.tkconsole.text) - - def display_executing_dialog(self): - tkMessageBox.showerror( - "Already executing", - "The Python Shell window is already executing a command; " - "please wait until it is finished.", - parent=self.tkconsole.text) - - -class PyShell(OutputWindow): - - shell_title = "Python " + python_version() + " Shell" - - # Override classes - ColorDelegator = ModifiedColorDelegator - UndoDelegator = ModifiedUndoDelegator - - # Override menus - menu_specs = [ - ("file", "_File"), - ("edit", "_Edit"), - ("debug", "_Debug"), - ("options", "_Options"), - ("windows", "_Window"), - ("help", "_Help"), - ] - - - # New classes - from idlelib.history import History - - def __init__(self, flist=None): - if use_subprocess: - ms = self.menu_specs - if ms[2][0] != "shell": - ms.insert(2, ("shell", "She_ll")) - self.interp = ModifiedInterpreter(self) - if flist is None: - root = Tk() - fixwordbreaks(root) - root.withdraw() - flist = PyShellFileList(root) - # - OutputWindow.__init__(self, flist, None, None) - # -## self.config(usetabs=1, indentwidth=8, context_use_ps1=1) - self.usetabs = True - # indentwidth must be 8 when using tabs. See note in EditorWindow: - self.indentwidth = 8 - self.context_use_ps1 = True - # - text = self.text - text.configure(wrap="char") - text.bind("<>", self.enter_callback) - text.bind("<>", self.linefeed_callback) - text.bind("<>", self.cancel_callback) - text.bind("<>", self.eof_callback) - text.bind("<>", self.open_stack_viewer) - text.bind("<>", self.toggle_debugger) - text.bind("<>", self.toggle_jit_stack_viewer) - if use_subprocess: - text.bind("<>", self.view_restart_mark) - text.bind("<>", self.restart_shell) - # - self.save_stdout = sys.stdout - self.save_stderr = sys.stderr - self.save_stdin = sys.stdin - from idlelib import iomenu - self.stdin = PseudoInputFile(self, "stdin", iomenu.encoding) - self.stdout = PseudoOutputFile(self, "stdout", iomenu.encoding) - self.stderr = PseudoOutputFile(self, "stderr", iomenu.encoding) - self.console = PseudoOutputFile(self, "console", iomenu.encoding) - if not use_subprocess: - sys.stdout = self.stdout - sys.stderr = self.stderr - sys.stdin = self.stdin - try: - # page help() text to shell. - import pydoc # import must be done here to capture i/o rebinding. - # XXX KBK 27Dec07 use TextViewer someday, but must work w/o subproc - pydoc.pager = pydoc.plainpager - except: - sys.stderr = sys.__stderr__ - raise - # - self.history = self.History(self.text) - # - self.pollinterval = 50 # millisec - - def get_standard_extension_names(self): - return idleConf.GetExtensions(shell_only=True) - - reading = False - executing = False - canceled = False - endoffile = False - closing = False - _stop_readline_flag = False - - def set_warning_stream(self, stream): - global warning_stream - warning_stream = stream - - def get_warning_stream(self): - return warning_stream - - def toggle_debugger(self, event=None): - if self.executing: - tkMessageBox.showerror("Don't debug now", - "You can only toggle the debugger when idle", - parent=self.text) - self.set_debugger_indicator() - return "break" - else: - db = self.interp.getdebugger() - if db: - self.close_debugger() - else: - self.open_debugger() - - def set_debugger_indicator(self): - db = self.interp.getdebugger() - self.setvar("<>", not not db) - - def toggle_jit_stack_viewer(self, event=None): - pass # All we need is the variable - - def close_debugger(self): - db = self.interp.getdebugger() - if db: - self.interp.setdebugger(None) - db.close() - if self.interp.rpcclt: - debugger_r.close_remote_debugger(self.interp.rpcclt) - self.resetoutput() - self.console.write("[DEBUG OFF]\n") - sys.ps1 = ">>> " - self.showprompt() - self.set_debugger_indicator() - - def open_debugger(self): - if self.interp.rpcclt: - dbg_gui = debugger_r.start_remote_debugger(self.interp.rpcclt, - self) - else: - dbg_gui = debugger.Debugger(self) - self.interp.setdebugger(dbg_gui) - dbg_gui.load_breakpoints() - sys.ps1 = "[DEBUG ON]\n>>> " - self.showprompt() - self.set_debugger_indicator() - - def beginexecuting(self): - "Helper for ModifiedInterpreter" - self.resetoutput() - self.executing = 1 - - def endexecuting(self): - "Helper for ModifiedInterpreter" - self.executing = 0 - self.canceled = 0 - self.showprompt() - - def close(self): - "Extend EditorWindow.close()" - if self.executing: - response = tkMessageBox.askokcancel( - "Kill?", - "Your program is still running!\n Do you want to kill it?", - default="ok", - parent=self.text) - if response is False: - return "cancel" - self.stop_readline() - self.canceled = True - self.closing = True - return EditorWindow.close(self) - - def _close(self): - "Extend EditorWindow._close(), shut down debugger and execution server" - self.close_debugger() - if use_subprocess: - self.interp.kill_subprocess() - # Restore std streams - sys.stdout = self.save_stdout - sys.stderr = self.save_stderr - sys.stdin = self.save_stdin - # Break cycles - self.interp = None - self.console = None - self.flist.pyshell = None - self.history = None - EditorWindow._close(self) - - def ispythonsource(self, filename): - "Override EditorWindow method: never remove the colorizer" - return True - - def short_title(self): - return self.shell_title - - COPYRIGHT = \ - 'Type "copyright", "credits" or "license()" for more information.' - - def begin(self): - self.text.mark_set("iomark", "insert") - self.resetoutput() - if use_subprocess: - nosub = '' - client = self.interp.start_subprocess() - if not client: - self.close() - return False - else: - nosub = ("==== No Subprocess ====\n\n" + - "WARNING: Running IDLE without a Subprocess is deprecated\n" + - "and will be removed in a later version. See Help/IDLE Help\n" + - "for details.\n\n") - sys.displayhook = rpc.displayhook - - self.write("Python %s on %s\n%s\n%s" % - (sys.version, sys.platform, self.COPYRIGHT, nosub)) - self.text.focus_force() - self.showprompt() - import tkinter - tkinter._default_root = None # 03Jan04 KBK What's this? - return True - - def stop_readline(self): - if not self.reading: # no nested mainloop to exit. - return - self._stop_readline_flag = True - self.top.quit() - - def readline(self): - save = self.reading - try: - self.reading = 1 - self.top.mainloop() # nested mainloop() - finally: - self.reading = save - if self._stop_readline_flag: - self._stop_readline_flag = False - return "" - line = self.text.get("iomark", "end-1c") - if len(line) == 0: # may be EOF if we quit our mainloop with Ctrl-C - line = "\n" - self.resetoutput() - if self.canceled: - self.canceled = 0 - if not use_subprocess: - raise KeyboardInterrupt - if self.endoffile: - self.endoffile = 0 - line = "" - return line - - def isatty(self): - return True - - def cancel_callback(self, event=None): - try: - if self.text.compare("sel.first", "!=", "sel.last"): - return # Active selection -- always use default binding - except: - pass - if not (self.executing or self.reading): - self.resetoutput() - self.interp.write("KeyboardInterrupt\n") - self.showprompt() - return "break" - self.endoffile = 0 - self.canceled = 1 - if (self.executing and self.interp.rpcclt): - if self.interp.getdebugger(): - self.interp.restart_subprocess() - else: - self.interp.interrupt_subprocess() - if self.reading: - self.top.quit() # exit the nested mainloop() in readline() - return "break" - - def eof_callback(self, event): - if self.executing and not self.reading: - return # Let the default binding (delete next char) take over - if not (self.text.compare("iomark", "==", "insert") and - self.text.compare("insert", "==", "end-1c")): - return # Let the default binding (delete next char) take over - if not self.executing: - self.resetoutput() - self.close() - else: - self.canceled = 0 - self.endoffile = 1 - self.top.quit() - return "break" - - def linefeed_callback(self, event): - # Insert a linefeed without entering anything (still autoindented) - if self.reading: - self.text.insert("insert", "\n") - self.text.see("insert") - else: - self.newline_and_indent_event(event) - return "break" - - def enter_callback(self, event): - if self.executing and not self.reading: - return # Let the default binding (insert '\n') take over - # If some text is selected, recall the selection - # (but only if this before the I/O mark) - try: - sel = self.text.get("sel.first", "sel.last") - if sel: - if self.text.compare("sel.last", "<=", "iomark"): - self.recall(sel, event) - return "break" - except: - pass - # If we're strictly before the line containing iomark, recall - # the current line, less a leading prompt, less leading or - # trailing whitespace - if self.text.compare("insert", "<", "iomark linestart"): - # Check if there's a relevant stdin range -- if so, use it - prev = self.text.tag_prevrange("stdin", "insert") - if prev and self.text.compare("insert", "<", prev[1]): - self.recall(self.text.get(prev[0], prev[1]), event) - return "break" - next = self.text.tag_nextrange("stdin", "insert") - if next and self.text.compare("insert lineend", ">=", next[0]): - self.recall(self.text.get(next[0], next[1]), event) - return "break" - # No stdin mark -- just get the current line, less any prompt - indices = self.text.tag_nextrange("console", "insert linestart") - if indices and \ - self.text.compare(indices[0], "<=", "insert linestart"): - self.recall(self.text.get(indices[1], "insert lineend"), event) - else: - self.recall(self.text.get("insert linestart", "insert lineend"), event) - return "break" - # If we're between the beginning of the line and the iomark, i.e. - # in the prompt area, move to the end of the prompt - if self.text.compare("insert", "<", "iomark"): - self.text.mark_set("insert", "iomark") - # If we're in the current input and there's only whitespace - # beyond the cursor, erase that whitespace first - s = self.text.get("insert", "end-1c") - if s and not s.strip(): - self.text.delete("insert", "end-1c") - # If we're in the current input before its last line, - # insert a newline right at the insert point - if self.text.compare("insert", "<", "end-1c linestart"): - self.newline_and_indent_event(event) - return "break" - # We're in the last line; append a newline and submit it - self.text.mark_set("insert", "end-1c") - if self.reading: - self.text.insert("insert", "\n") - self.text.see("insert") - else: - self.newline_and_indent_event(event) - self.text.tag_add("stdin", "iomark", "end-1c") - self.text.update_idletasks() - if self.reading: - self.top.quit() # Break out of recursive mainloop() - else: - self.runit() - return "break" - - def recall(self, s, event): - # remove leading and trailing empty or whitespace lines - s = re.sub(r'^\s*\n', '' , s) - s = re.sub(r'\n\s*$', '', s) - lines = s.split('\n') - self.text.undo_block_start() - try: - self.text.tag_remove("sel", "1.0", "end") - self.text.mark_set("insert", "end-1c") - prefix = self.text.get("insert linestart", "insert") - if prefix.rstrip().endswith(':'): - self.newline_and_indent_event(event) - prefix = self.text.get("insert linestart", "insert") - self.text.insert("insert", lines[0].strip()) - if len(lines) > 1: - orig_base_indent = re.search(r'^([ \t]*)', lines[0]).group(0) - new_base_indent = re.search(r'^([ \t]*)', prefix).group(0) - for line in lines[1:]: - if line.startswith(orig_base_indent): - # replace orig base indentation with new indentation - line = new_base_indent + line[len(orig_base_indent):] - self.text.insert('insert', '\n'+line.rstrip()) - finally: - self.text.see("insert") - self.text.undo_block_stop() - - def runit(self): - line = self.text.get("iomark", "end-1c") - # Strip off last newline and surrounding whitespace. - # (To allow you to hit return twice to end a statement.) - i = len(line) - while i > 0 and line[i-1] in " \t": - i = i-1 - if i > 0 and line[i-1] == "\n": - i = i-1 - while i > 0 and line[i-1] in " \t": - i = i-1 - line = line[:i] - self.interp.runsource(line) - - def open_stack_viewer(self, event=None): - if self.interp.rpcclt: - return self.interp.remote_stack_viewer() - try: - sys.last_traceback - except: - tkMessageBox.showerror("No stack trace", - "There is no stack trace yet.\n" - "(sys.last_traceback is not defined)", - parent=self.text) - return - from idlelib.stackviewer import StackBrowser - StackBrowser(self.root, self.flist) - - def view_restart_mark(self, event=None): - self.text.see("iomark") - self.text.see("restart") - - def restart_shell(self, event=None): - "Callback for Run/Restart Shell Cntl-F6" - self.interp.restart_subprocess(with_cwd=True) - - def showprompt(self): - self.resetoutput() - try: - s = str(sys.ps1) - except: - s = "" - self.console.write(s) - self.text.mark_set("insert", "end-1c") - self.set_line_and_column() - self.io.reset_undo() - - def resetoutput(self): - source = self.text.get("iomark", "end-1c") - if self.history: - self.history.store(source) - if self.text.get("end-2c") != "\n": - self.text.insert("end-1c", "\n") - self.text.mark_set("iomark", "end-1c") - self.set_line_and_column() - - def write(self, s, tags=()): - if isinstance(s, str) and len(s) and max(s) > '\uffff': - # Tk doesn't support outputting non-BMP characters - # Let's assume what printed string is not very long, - # find first non-BMP character and construct informative - # UnicodeEncodeError exception. - for start, char in enumerate(s): - if char > '\uffff': - break - raise UnicodeEncodeError("UCS-2", char, start, start+1, - 'Non-BMP character not supported in Tk') - try: - self.text.mark_gravity("iomark", "right") - count = OutputWindow.write(self, s, tags, "iomark") - self.text.mark_gravity("iomark", "left") - except: - raise ###pass # ### 11Aug07 KBK if we are expecting exceptions - # let's find out what they are and be specific. - if self.canceled: - self.canceled = 0 - if not use_subprocess: - raise KeyboardInterrupt - return count - - def rmenu_check_cut(self): - try: - if self.text.compare('sel.first', '<', 'iomark'): - return 'disabled' - except TclError: # no selection, so the index 'sel.first' doesn't exist - return 'disabled' - return super().rmenu_check_cut() - - def rmenu_check_paste(self): - if self.text.compare('insert','<','iomark'): - return 'disabled' - return super().rmenu_check_paste() - -class PseudoFile(io.TextIOBase): - - def __init__(self, shell, tags, encoding=None): - self.shell = shell - self.tags = tags - self._encoding = encoding - - @property - def encoding(self): - return self._encoding - - @property - def name(self): - return '<%s>' % self.tags - - def isatty(self): - return True - - -class PseudoOutputFile(PseudoFile): - - def writable(self): - return True - - def write(self, s): - if self.closed: - raise ValueError("write to closed file") - if type(s) is not str: - if not isinstance(s, str): - raise TypeError('must be str, not ' + type(s).__name__) - # See issue #19481 - s = str.__str__(s) - return self.shell.write(s, self.tags) - - -class PseudoInputFile(PseudoFile): - - def __init__(self, shell, tags, encoding=None): - PseudoFile.__init__(self, shell, tags, encoding) - self._line_buffer = '' - - def readable(self): - return True - - def read(self, size=-1): - if self.closed: - raise ValueError("read from closed file") - if size is None: - size = -1 - elif not isinstance(size, int): - raise TypeError('must be int, not ' + type(size).__name__) - result = self._line_buffer - self._line_buffer = '' - if size < 0: - while True: - line = self.shell.readline() - if not line: break - result += line - else: - while len(result) < size: - line = self.shell.readline() - if not line: break - result += line - self._line_buffer = result[size:] - result = result[:size] - return result - - def readline(self, size=-1): - if self.closed: - raise ValueError("read from closed file") - if size is None: - size = -1 - elif not isinstance(size, int): - raise TypeError('must be int, not ' + type(size).__name__) - line = self._line_buffer or self.shell.readline() - if size < 0: - size = len(line) - eol = line.find('\n', 0, size) - if eol >= 0: - size = eol + 1 - self._line_buffer = line[size:] - return line[:size] - - def close(self): - self.shell.close() - - -usage_msg = """\ - -USAGE: idle [-deins] [-t title] [file]* - idle [-dns] [-t title] (-c cmd | -r file) [arg]* - idle [-dns] [-t title] - [arg]* - - -h print this help message and exit - -n run IDLE without a subprocess (DEPRECATED, - see Help/IDLE Help for details) - -The following options will override the IDLE 'settings' configuration: - - -e open an edit window - -i open a shell window - -The following options imply -i and will open a shell: - - -c cmd run the command in a shell, or - -r file run script from file - - -d enable the debugger - -s run $IDLESTARTUP or $PYTHONSTARTUP before anything else - -t title set title of shell window - -A default edit window will be bypassed when -c, -r, or - are used. - -[arg]* are passed to the command (-c) or script (-r) in sys.argv[1:]. - -Examples: - -idle - Open an edit window or shell depending on IDLE's configuration. - -idle foo.py foobar.py - Edit the files, also open a shell if configured to start with shell. - -idle -est "Baz" foo.py - Run $IDLESTARTUP or $PYTHONSTARTUP, edit foo.py, and open a shell - window with the title "Baz". - -idle -c "import sys; print(sys.argv)" "foo" - Open a shell window and run the command, passing "-c" in sys.argv[0] - and "foo" in sys.argv[1]. - -idle -d -s -r foo.py "Hello World" - Open a shell window, run a startup script, enable the debugger, and - run foo.py, passing "foo.py" in sys.argv[0] and "Hello World" in - sys.argv[1]. - -echo "import sys; print(sys.argv)" | idle - "foobar" - Open a shell window, run the script piped in, passing '' in sys.argv[0] - and "foobar" in sys.argv[1]. -""" - -def main(): - global flist, root, use_subprocess - - capture_warnings(True) - use_subprocess = True - enable_shell = False - enable_edit = False - debug = False - cmd = None - script = None - startup = False - try: - opts, args = getopt.getopt(sys.argv[1:], "c:deihnr:st:") - except getopt.error as msg: - print("Error: %s\n%s" % (msg, usage_msg), file=sys.stderr) - sys.exit(2) - for o, a in opts: - if o == '-c': - cmd = a - enable_shell = True - if o == '-d': - debug = True - enable_shell = True - if o == '-e': - enable_edit = True - if o == '-h': - sys.stdout.write(usage_msg) - sys.exit() - if o == '-i': - enable_shell = True - if o == '-n': - print(" Warning: running IDLE without a subprocess is deprecated.", - file=sys.stderr) - use_subprocess = False - if o == '-r': - script = a - if os.path.isfile(script): - pass - else: - print("No script file: ", script) - sys.exit() - enable_shell = True - if o == '-s': - startup = True - enable_shell = True - if o == '-t': - PyShell.shell_title = a - enable_shell = True - if args and args[0] == '-': - cmd = sys.stdin.read() - enable_shell = True - # process sys.argv and sys.path: - for i in range(len(sys.path)): - sys.path[i] = os.path.abspath(sys.path[i]) - if args and args[0] == '-': - sys.argv = [''] + args[1:] - elif cmd: - sys.argv = ['-c'] + args - elif script: - sys.argv = [script] + args - elif args: - enable_edit = True - pathx = [] - for filename in args: - pathx.append(os.path.dirname(filename)) - for dir in pathx: - dir = os.path.abspath(dir) - if not dir in sys.path: - sys.path.insert(0, dir) - else: - dir = os.getcwd() - if dir not in sys.path: - sys.path.insert(0, dir) - # check the IDLE settings configuration (but command line overrides) - edit_start = idleConf.GetOption('main', 'General', - 'editor-on-startup', type='bool') - enable_edit = enable_edit or edit_start - enable_shell = enable_shell or not enable_edit - # start editor and/or shell windows: - root = Tk(className="Idle") - - # set application icon - icondir = os.path.join(os.path.dirname(__file__), 'Icons') - if system() == 'Windows': - iconfile = os.path.join(icondir, 'idle.ico') - root.wm_iconbitmap(default=iconfile) - else: - ext = '.png' if TkVersion >= 8.6 else '.gif' - iconfiles = [os.path.join(icondir, 'idle_%d%s' % (size, ext)) - for size in (16, 32, 48)] - icons = [PhotoImage(file=iconfile) for iconfile in iconfiles] - root.wm_iconphoto(True, *icons) - - fixwordbreaks(root) - root.withdraw() - flist = PyShellFileList(root) - macosx.setupApp(root, flist) - - if enable_edit: - if not (cmd or script): - for filename in args[:]: - if flist.open(filename) is None: - # filename is a directory actually, disconsider it - args.remove(filename) - if not args: - flist.new() - - if enable_shell: - shell = flist.open_shell() - if not shell: - return # couldn't open shell - if macosx.isAquaTk() and flist.dict: - # On OSX: when the user has double-clicked on a file that causes - # IDLE to be launched the shell window will open just in front of - # the file she wants to see. Lower the interpreter window when - # there are open files. - shell.top.lower() - else: - shell = flist.pyshell - - # Handle remaining options. If any of these are set, enable_shell - # was set also, so shell must be true to reach here. - if debug: - shell.open_debugger() - if startup: - filename = os.environ.get("IDLESTARTUP") or \ - os.environ.get("PYTHONSTARTUP") - if filename and os.path.isfile(filename): - shell.interp.execfile(filename) - if cmd or script: - shell.interp.runcommand("""if 1: - import sys as _sys - _sys.argv = %r - del _sys - \n""" % (sys.argv,)) - if cmd: - shell.interp.execsource(cmd) - elif script: - shell.interp.prepend_syspath(script) - shell.interp.execfile(script) - elif shell: - # If there is a shell window and no cmd or script in progress, - # check for problematic OS X Tk versions and print a warning - # message in the IDLE shell window; this is less intrusive - # than always opening a separate window. - tkversionwarning = macosx.tkVersionWarning(root) - if tkversionwarning: - shell.interp.runcommand("print('%s')" % tkversionwarning) - - while flist.inversedict: # keep IDLE running while files are open. - root.mainloop() - root.destroy() - capture_warnings(False) - -if __name__ == "__main__": - sys.modules['pyshell'] = sys.modules['__main__'] - main() - -capture_warnings(False) # Make sure turned off; see issue 18081 -- cgit v1.2.1 From f884b9be44487ff479a2593633ac9f3de408cd23 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Sat, 11 Jun 2016 02:10:59 -0400 Subject: Issue #5124: rename PyShell back to pyshell and patch test for 3.6 --- Lib/idlelib/PyShell.py | 1631 -------------------------------- Lib/idlelib/idle_test/test_editmenu.py | 28 +- Lib/idlelib/pyshell.py | 1631 ++++++++++++++++++++++++++++++++ 3 files changed, 1646 insertions(+), 1644 deletions(-) delete mode 100755 Lib/idlelib/PyShell.py create mode 100755 Lib/idlelib/pyshell.py (limited to 'Lib') diff --git a/Lib/idlelib/PyShell.py b/Lib/idlelib/PyShell.py deleted file mode 100755 index 0f7a01d77b..0000000000 --- a/Lib/idlelib/PyShell.py +++ /dev/null @@ -1,1631 +0,0 @@ -#! /usr/bin/env python3 - -try: - from tkinter import * -except ImportError: - print("** IDLE can't import Tkinter.\n" - "Your Python may not be configured for Tk. **", file=sys.__stderr__) - sys.exit(1) -import tkinter.messagebox as tkMessageBox -if TkVersion < 8.5: - root = Tk() # otherwise create root in main - root.withdraw() - tkMessageBox.showerror("Idle Cannot Start", - "Idle requires tcl/tk 8.5+, not $s." % TkVersion, - parent=root) - sys.exit(1) - -import getopt -import os -import os.path -import re -import socket -import subprocess -import sys -import threading -import time -import tokenize -import io - -import linecache -from code import InteractiveInterpreter -from platform import python_version, system - -from idlelib.editor import EditorWindow, fixwordbreaks -from idlelib.filelist import FileList -from idlelib.colorizer import ColorDelegator -from idlelib.undo import UndoDelegator -from idlelib.outwin import OutputWindow -from idlelib.config import idleConf -from idlelib import rpc -from idlelib import debugger -from idlelib import debugger_r -from idlelib import macosx - -HOST = '127.0.0.1' # python execution server on localhost loopback -PORT = 0 # someday pass in host, port for remote debug capability - -# Override warnings module to write to warning_stream. Initialize to send IDLE -# internal warnings to the console. ScriptBinding.check_syntax() will -# temporarily redirect the stream to the shell window to display warnings when -# checking user's code. -warning_stream = sys.__stderr__ # None, at least on Windows, if no console. -import warnings - -def idle_formatwarning(message, category, filename, lineno, line=None): - """Format warnings the IDLE way.""" - - s = "\nWarning (from warnings module):\n" - s += ' File \"%s\", line %s\n' % (filename, lineno) - if line is None: - line = linecache.getline(filename, lineno) - line = line.strip() - if line: - s += " %s\n" % line - s += "%s: %s\n" % (category.__name__, message) - return s - -def idle_showwarning( - message, category, filename, lineno, file=None, line=None): - """Show Idle-format warning (after replacing warnings.showwarning). - - The differences are the formatter called, the file=None replacement, - which can be None, the capture of the consequence AttributeError, - and the output of a hard-coded prompt. - """ - if file is None: - file = warning_stream - try: - file.write(idle_formatwarning( - message, category, filename, lineno, line=line)) - file.write(">>> ") - except (AttributeError, OSError): - pass # if file (probably __stderr__) is invalid, skip warning. - -_warnings_showwarning = None - -def capture_warnings(capture): - "Replace warning.showwarning with idle_showwarning, or reverse." - - global _warnings_showwarning - if capture: - if _warnings_showwarning is None: - _warnings_showwarning = warnings.showwarning - warnings.showwarning = idle_showwarning - else: - if _warnings_showwarning is not None: - warnings.showwarning = _warnings_showwarning - _warnings_showwarning = None - -capture_warnings(True) - -def extended_linecache_checkcache(filename=None, - orig_checkcache=linecache.checkcache): - """Extend linecache.checkcache to preserve the entries - - Rather than repeating the linecache code, patch it to save the - entries, call the original linecache.checkcache() - (skipping them), and then restore the saved entries. - - orig_checkcache is bound at definition time to the original - method, allowing it to be patched. - """ - cache = linecache.cache - save = {} - for key in list(cache): - if key[:1] + key[-1:] == '<>': - save[key] = cache.pop(key) - orig_checkcache(filename) - cache.update(save) - -# Patch linecache.checkcache(): -linecache.checkcache = extended_linecache_checkcache - - -class PyShellEditorWindow(EditorWindow): - "Regular text edit window in IDLE, supports breakpoints" - - def __init__(self, *args): - self.breakpoints = [] - EditorWindow.__init__(self, *args) - self.text.bind("<>", self.set_breakpoint_here) - self.text.bind("<>", self.clear_breakpoint_here) - self.text.bind("<>", self.flist.open_shell) - - self.breakpointPath = os.path.join(idleConf.GetUserCfgDir(), - 'breakpoints.lst') - # whenever a file is changed, restore breakpoints - def filename_changed_hook(old_hook=self.io.filename_change_hook, - self=self): - self.restore_file_breaks() - old_hook() - self.io.set_filename_change_hook(filename_changed_hook) - if self.io.filename: - self.restore_file_breaks() - self.color_breakpoint_text() - - rmenu_specs = [ - ("Cut", "<>", "rmenu_check_cut"), - ("Copy", "<>", "rmenu_check_copy"), - ("Paste", "<>", "rmenu_check_paste"), - (None, None, None), - ("Set Breakpoint", "<>", None), - ("Clear Breakpoint", "<>", None) - ] - - def color_breakpoint_text(self, color=True): - "Turn colorizing of breakpoint text on or off" - if self.io is None: - # possible due to update in restore_file_breaks - return - if color: - theme = idleConf.CurrentTheme() - cfg = idleConf.GetHighlight(theme, "break") - else: - cfg = {'foreground': '', 'background': ''} - self.text.tag_config('BREAK', cfg) - - def set_breakpoint(self, lineno): - text = self.text - filename = self.io.filename - text.tag_add("BREAK", "%d.0" % lineno, "%d.0" % (lineno+1)) - try: - self.breakpoints.index(lineno) - except ValueError: # only add if missing, i.e. do once - self.breakpoints.append(lineno) - try: # update the subprocess debugger - debug = self.flist.pyshell.interp.debugger - debug.set_breakpoint_here(filename, lineno) - except: # but debugger may not be active right now.... - pass - - def set_breakpoint_here(self, event=None): - text = self.text - filename = self.io.filename - if not filename: - text.bell() - return - lineno = int(float(text.index("insert"))) - self.set_breakpoint(lineno) - - def clear_breakpoint_here(self, event=None): - text = self.text - filename = self.io.filename - if not filename: - text.bell() - return - lineno = int(float(text.index("insert"))) - try: - self.breakpoints.remove(lineno) - except: - pass - text.tag_remove("BREAK", "insert linestart",\ - "insert lineend +1char") - try: - debug = self.flist.pyshell.interp.debugger - debug.clear_breakpoint_here(filename, lineno) - except: - pass - - def clear_file_breaks(self): - if self.breakpoints: - text = self.text - filename = self.io.filename - if not filename: - text.bell() - return - self.breakpoints = [] - text.tag_remove("BREAK", "1.0", END) - try: - debug = self.flist.pyshell.interp.debugger - debug.clear_file_breaks(filename) - except: - pass - - def store_file_breaks(self): - "Save breakpoints when file is saved" - # XXX 13 Dec 2002 KBK Currently the file must be saved before it can - # be run. The breaks are saved at that time. If we introduce - # a temporary file save feature the save breaks functionality - # needs to be re-verified, since the breaks at the time the - # temp file is created may differ from the breaks at the last - # permanent save of the file. Currently, a break introduced - # after a save will be effective, but not persistent. - # This is necessary to keep the saved breaks synched with the - # saved file. - # - # Breakpoints are set as tagged ranges in the text. - # Since a modified file has to be saved before it is - # run, and since self.breakpoints (from which the subprocess - # debugger is loaded) is updated during the save, the visible - # breaks stay synched with the subprocess even if one of these - # unexpected breakpoint deletions occurs. - breaks = self.breakpoints - filename = self.io.filename - try: - with open(self.breakpointPath, "r") as fp: - lines = fp.readlines() - except OSError: - lines = [] - try: - with open(self.breakpointPath, "w") as new_file: - for line in lines: - if not line.startswith(filename + '='): - new_file.write(line) - self.update_breakpoints() - breaks = self.breakpoints - if breaks: - new_file.write(filename + '=' + str(breaks) + '\n') - except OSError as err: - if not getattr(self.root, "breakpoint_error_displayed", False): - self.root.breakpoint_error_displayed = True - tkMessageBox.showerror(title='IDLE Error', - message='Unable to update breakpoint list:\n%s' - % str(err), - parent=self.text) - - def restore_file_breaks(self): - self.text.update() # this enables setting "BREAK" tags to be visible - if self.io is None: - # can happen if IDLE closes due to the .update() call - return - filename = self.io.filename - if filename is None: - return - if os.path.isfile(self.breakpointPath): - with open(self.breakpointPath, "r") as fp: - lines = fp.readlines() - for line in lines: - if line.startswith(filename + '='): - breakpoint_linenumbers = eval(line[len(filename)+1:]) - for breakpoint_linenumber in breakpoint_linenumbers: - self.set_breakpoint(breakpoint_linenumber) - - def update_breakpoints(self): - "Retrieves all the breakpoints in the current window" - text = self.text - ranges = text.tag_ranges("BREAK") - linenumber_list = self.ranges_to_linenumbers(ranges) - self.breakpoints = linenumber_list - - def ranges_to_linenumbers(self, ranges): - lines = [] - for index in range(0, len(ranges), 2): - lineno = int(float(ranges[index].string)) - end = int(float(ranges[index+1].string)) - while lineno < end: - lines.append(lineno) - lineno += 1 - return lines - -# XXX 13 Dec 2002 KBK Not used currently -# def saved_change_hook(self): -# "Extend base method - clear breaks if module is modified" -# if not self.get_saved(): -# self.clear_file_breaks() -# EditorWindow.saved_change_hook(self) - - def _close(self): - "Extend base method - clear breaks when module is closed" - self.clear_file_breaks() - EditorWindow._close(self) - - -class PyShellFileList(FileList): - "Extend base class: IDLE supports a shell and breakpoints" - - # override FileList's class variable, instances return PyShellEditorWindow - # instead of EditorWindow when new edit windows are created. - EditorWindow = PyShellEditorWindow - - pyshell = None - - def open_shell(self, event=None): - if self.pyshell: - self.pyshell.top.wakeup() - else: - self.pyshell = PyShell(self) - if self.pyshell: - if not self.pyshell.begin(): - return None - return self.pyshell - - -class ModifiedColorDelegator(ColorDelegator): - "Extend base class: colorizer for the shell window itself" - - def __init__(self): - ColorDelegator.__init__(self) - self.LoadTagDefs() - - def recolorize_main(self): - self.tag_remove("TODO", "1.0", "iomark") - self.tag_add("SYNC", "1.0", "iomark") - ColorDelegator.recolorize_main(self) - - def LoadTagDefs(self): - ColorDelegator.LoadTagDefs(self) - theme = idleConf.CurrentTheme() - self.tagdefs.update({ - "stdin": {'background':None,'foreground':None}, - "stdout": idleConf.GetHighlight(theme, "stdout"), - "stderr": idleConf.GetHighlight(theme, "stderr"), - "console": idleConf.GetHighlight(theme, "console"), - }) - - def removecolors(self): - # Don't remove shell color tags before "iomark" - for tag in self.tagdefs: - self.tag_remove(tag, "iomark", "end") - -class ModifiedUndoDelegator(UndoDelegator): - "Extend base class: forbid insert/delete before the I/O mark" - - def insert(self, index, chars, tags=None): - try: - if self.delegate.compare(index, "<", "iomark"): - self.delegate.bell() - return - except TclError: - pass - UndoDelegator.insert(self, index, chars, tags) - - def delete(self, index1, index2=None): - try: - if self.delegate.compare(index1, "<", "iomark"): - self.delegate.bell() - return - except TclError: - pass - UndoDelegator.delete(self, index1, index2) - - -class MyRPCClient(rpc.RPCClient): - - def handle_EOF(self): - "Override the base class - just re-raise EOFError" - raise EOFError - - -class ModifiedInterpreter(InteractiveInterpreter): - - def __init__(self, tkconsole): - self.tkconsole = tkconsole - locals = sys.modules['__main__'].__dict__ - InteractiveInterpreter.__init__(self, locals=locals) - self.save_warnings_filters = None - self.restarting = False - self.subprocess_arglist = None - self.port = PORT - self.original_compiler_flags = self.compile.compiler.flags - - _afterid = None - rpcclt = None - rpcsubproc = None - - def spawn_subprocess(self): - if self.subprocess_arglist is None: - self.subprocess_arglist = self.build_subprocess_arglist() - self.rpcsubproc = subprocess.Popen(self.subprocess_arglist) - - def build_subprocess_arglist(self): - assert (self.port!=0), ( - "Socket should have been assigned a port number.") - w = ['-W' + s for s in sys.warnoptions] - # Maybe IDLE is installed and is being accessed via sys.path, - # or maybe it's not installed and the idle.py script is being - # run from the IDLE source directory. - del_exitf = idleConf.GetOption('main', 'General', 'delete-exitfunc', - default=False, type='bool') - if __name__ == 'idlelib.pyshell': - command = "__import__('idlelib.run').run.main(%r)" % (del_exitf,) - else: - command = "__import__('run').main(%r)" % (del_exitf,) - return [sys.executable] + w + ["-c", command, str(self.port)] - - def start_subprocess(self): - addr = (HOST, self.port) - # GUI makes several attempts to acquire socket, listens for connection - for i in range(3): - time.sleep(i) - try: - self.rpcclt = MyRPCClient(addr) - break - except OSError: - pass - else: - self.display_port_binding_error() - return None - # if PORT was 0, system will assign an 'ephemeral' port. Find it out: - self.port = self.rpcclt.listening_sock.getsockname()[1] - # if PORT was not 0, probably working with a remote execution server - if PORT != 0: - # To allow reconnection within the 2MSL wait (cf. Stevens TCP - # V1, 18.6), set SO_REUSEADDR. Note that this can be problematic - # on Windows since the implementation allows two active sockets on - # the same address! - self.rpcclt.listening_sock.setsockopt(socket.SOL_SOCKET, - socket.SO_REUSEADDR, 1) - self.spawn_subprocess() - #time.sleep(20) # test to simulate GUI not accepting connection - # Accept the connection from the Python execution server - self.rpcclt.listening_sock.settimeout(10) - try: - self.rpcclt.accept() - except socket.timeout: - self.display_no_subprocess_error() - return None - self.rpcclt.register("console", self.tkconsole) - self.rpcclt.register("stdin", self.tkconsole.stdin) - self.rpcclt.register("stdout", self.tkconsole.stdout) - self.rpcclt.register("stderr", self.tkconsole.stderr) - self.rpcclt.register("flist", self.tkconsole.flist) - self.rpcclt.register("linecache", linecache) - self.rpcclt.register("interp", self) - self.transfer_path(with_cwd=True) - self.poll_subprocess() - return self.rpcclt - - def restart_subprocess(self, with_cwd=False, filename=''): - if self.restarting: - return self.rpcclt - self.restarting = True - # close only the subprocess debugger - debug = self.getdebugger() - if debug: - try: - # Only close subprocess debugger, don't unregister gui_adap! - debugger_r.close_subprocess_debugger(self.rpcclt) - except: - pass - # Kill subprocess, spawn a new one, accept connection. - self.rpcclt.close() - self.terminate_subprocess() - console = self.tkconsole - was_executing = console.executing - console.executing = False - self.spawn_subprocess() - try: - self.rpcclt.accept() - except socket.timeout: - self.display_no_subprocess_error() - return None - self.transfer_path(with_cwd=with_cwd) - console.stop_readline() - # annotate restart in shell window and mark it - console.text.delete("iomark", "end-1c") - tag = 'RESTART: ' + (filename if filename else 'Shell') - halfbar = ((int(console.width) -len(tag) - 4) // 2) * '=' - console.write("\n{0} {1} {0}".format(halfbar, tag)) - console.text.mark_set("restart", "end-1c") - console.text.mark_gravity("restart", "left") - if not filename: - console.showprompt() - # restart subprocess debugger - if debug: - # Restarted debugger connects to current instance of debug GUI - debugger_r.restart_subprocess_debugger(self.rpcclt) - # reload remote debugger breakpoints for all PyShellEditWindows - debug.load_breakpoints() - self.compile.compiler.flags = self.original_compiler_flags - self.restarting = False - return self.rpcclt - - def __request_interrupt(self): - self.rpcclt.remotecall("exec", "interrupt_the_server", (), {}) - - def interrupt_subprocess(self): - threading.Thread(target=self.__request_interrupt).start() - - def kill_subprocess(self): - if self._afterid is not None: - self.tkconsole.text.after_cancel(self._afterid) - try: - self.rpcclt.listening_sock.close() - except AttributeError: # no socket - pass - try: - self.rpcclt.close() - except AttributeError: # no socket - pass - self.terminate_subprocess() - self.tkconsole.executing = False - self.rpcclt = None - - def terminate_subprocess(self): - "Make sure subprocess is terminated" - try: - self.rpcsubproc.kill() - except OSError: - # process already terminated - return - else: - try: - self.rpcsubproc.wait() - except OSError: - return - - def transfer_path(self, with_cwd=False): - if with_cwd: # Issue 13506 - path = [''] # include Current Working Directory - path.extend(sys.path) - else: - path = sys.path - - self.runcommand("""if 1: - import sys as _sys - _sys.path = %r - del _sys - \n""" % (path,)) - - active_seq = None - - def poll_subprocess(self): - clt = self.rpcclt - if clt is None: - return - try: - response = clt.pollresponse(self.active_seq, wait=0.05) - except (EOFError, OSError, KeyboardInterrupt): - # lost connection or subprocess terminated itself, restart - # [the KBI is from rpc.SocketIO.handle_EOF()] - if self.tkconsole.closing: - return - response = None - self.restart_subprocess() - if response: - self.tkconsole.resetoutput() - self.active_seq = None - how, what = response - console = self.tkconsole.console - if how == "OK": - if what is not None: - print(repr(what), file=console) - elif how == "EXCEPTION": - if self.tkconsole.getvar("<>"): - self.remote_stack_viewer() - elif how == "ERROR": - errmsg = "pyshell.ModifiedInterpreter: Subprocess ERROR:\n" - print(errmsg, what, file=sys.__stderr__) - print(errmsg, what, file=console) - # we received a response to the currently active seq number: - try: - self.tkconsole.endexecuting() - except AttributeError: # shell may have closed - pass - # Reschedule myself - if not self.tkconsole.closing: - self._afterid = self.tkconsole.text.after( - self.tkconsole.pollinterval, self.poll_subprocess) - - debugger = None - - def setdebugger(self, debugger): - self.debugger = debugger - - def getdebugger(self): - return self.debugger - - def open_remote_stack_viewer(self): - """Initiate the remote stack viewer from a separate thread. - - This method is called from the subprocess, and by returning from this - method we allow the subprocess to unblock. After a bit the shell - requests the subprocess to open the remote stack viewer which returns a - static object looking at the last exception. It is queried through - the RPC mechanism. - - """ - self.tkconsole.text.after(300, self.remote_stack_viewer) - return - - def remote_stack_viewer(self): - from idlelib import debugobj_r - oid = self.rpcclt.remotequeue("exec", "stackviewer", ("flist",), {}) - if oid is None: - self.tkconsole.root.bell() - return - item = debugobj_r.StubObjectTreeItem(self.rpcclt, oid) - from idlelib.tree import ScrolledCanvas, TreeNode - top = Toplevel(self.tkconsole.root) - theme = idleConf.CurrentTheme() - background = idleConf.GetHighlight(theme, 'normal')['background'] - sc = ScrolledCanvas(top, bg=background, highlightthickness=0) - sc.frame.pack(expand=1, fill="both") - node = TreeNode(sc.canvas, None, item) - node.expand() - # XXX Should GC the remote tree when closing the window - - gid = 0 - - def execsource(self, source): - "Like runsource() but assumes complete exec source" - filename = self.stuffsource(source) - self.execfile(filename, source) - - def execfile(self, filename, source=None): - "Execute an existing file" - if source is None: - with tokenize.open(filename) as fp: - source = fp.read() - try: - code = compile(source, filename, "exec") - except (OverflowError, SyntaxError): - self.tkconsole.resetoutput() - print('*** Error in script or command!\n' - 'Traceback (most recent call last):', - file=self.tkconsole.stderr) - InteractiveInterpreter.showsyntaxerror(self, filename) - self.tkconsole.showprompt() - else: - self.runcode(code) - - def runsource(self, source): - "Extend base class method: Stuff the source in the line cache first" - filename = self.stuffsource(source) - self.more = 0 - self.save_warnings_filters = warnings.filters[:] - warnings.filterwarnings(action="error", category=SyntaxWarning) - # at the moment, InteractiveInterpreter expects str - assert isinstance(source, str) - #if isinstance(source, str): - # from idlelib import iomenu - # try: - # source = source.encode(iomenu.encoding) - # except UnicodeError: - # self.tkconsole.resetoutput() - # self.write("Unsupported characters in input\n") - # return - try: - # InteractiveInterpreter.runsource() calls its runcode() method, - # which is overridden (see below) - return InteractiveInterpreter.runsource(self, source, filename) - finally: - if self.save_warnings_filters is not None: - warnings.filters[:] = self.save_warnings_filters - self.save_warnings_filters = None - - def stuffsource(self, source): - "Stuff source in the filename cache" - filename = "" % self.gid - self.gid = self.gid + 1 - lines = source.split("\n") - linecache.cache[filename] = len(source)+1, 0, lines, filename - return filename - - def prepend_syspath(self, filename): - "Prepend sys.path with file's directory if not already included" - self.runcommand("""if 1: - _filename = %r - import sys as _sys - from os.path import dirname as _dirname - _dir = _dirname(_filename) - if not _dir in _sys.path: - _sys.path.insert(0, _dir) - del _filename, _sys, _dirname, _dir - \n""" % (filename,)) - - def showsyntaxerror(self, filename=None): - """Override Interactive Interpreter method: Use Colorizing - - Color the offending position instead of printing it and pointing at it - with a caret. - - """ - tkconsole = self.tkconsole - text = tkconsole.text - text.tag_remove("ERROR", "1.0", "end") - type, value, tb = sys.exc_info() - msg = getattr(value, 'msg', '') or value or "" - lineno = getattr(value, 'lineno', '') or 1 - offset = getattr(value, 'offset', '') or 0 - if offset == 0: - lineno += 1 #mark end of offending line - if lineno == 1: - pos = "iomark + %d chars" % (offset-1) - else: - pos = "iomark linestart + %d lines + %d chars" % \ - (lineno-1, offset-1) - tkconsole.colorize_syntax_error(text, pos) - tkconsole.resetoutput() - self.write("SyntaxError: %s\n" % msg) - tkconsole.showprompt() - - def showtraceback(self): - "Extend base class method to reset output properly" - self.tkconsole.resetoutput() - self.checklinecache() - InteractiveInterpreter.showtraceback(self) - if self.tkconsole.getvar("<>"): - self.tkconsole.open_stack_viewer() - - def checklinecache(self): - c = linecache.cache - for key in list(c.keys()): - if key[:1] + key[-1:] != "<>": - del c[key] - - def runcommand(self, code): - "Run the code without invoking the debugger" - # The code better not raise an exception! - if self.tkconsole.executing: - self.display_executing_dialog() - return 0 - if self.rpcclt: - self.rpcclt.remotequeue("exec", "runcode", (code,), {}) - else: - exec(code, self.locals) - return 1 - - def runcode(self, code): - "Override base class method" - if self.tkconsole.executing: - self.interp.restart_subprocess() - self.checklinecache() - if self.save_warnings_filters is not None: - warnings.filters[:] = self.save_warnings_filters - self.save_warnings_filters = None - debugger = self.debugger - try: - self.tkconsole.beginexecuting() - if not debugger and self.rpcclt is not None: - self.active_seq = self.rpcclt.asyncqueue("exec", "runcode", - (code,), {}) - elif debugger: - debugger.run(code, self.locals) - else: - exec(code, self.locals) - except SystemExit: - if not self.tkconsole.closing: - if tkMessageBox.askyesno( - "Exit?", - "Do you want to exit altogether?", - default="yes", - parent=self.tkconsole.text): - raise - else: - self.showtraceback() - else: - raise - except: - if use_subprocess: - print("IDLE internal error in runcode()", - file=self.tkconsole.stderr) - self.showtraceback() - self.tkconsole.endexecuting() - else: - if self.tkconsole.canceled: - self.tkconsole.canceled = False - print("KeyboardInterrupt", file=self.tkconsole.stderr) - else: - self.showtraceback() - finally: - if not use_subprocess: - try: - self.tkconsole.endexecuting() - except AttributeError: # shell may have closed - pass - - def write(self, s): - "Override base class method" - return self.tkconsole.stderr.write(s) - - def display_port_binding_error(self): - tkMessageBox.showerror( - "Port Binding Error", - "IDLE can't bind to a TCP/IP port, which is necessary to " - "communicate with its Python execution server. This might be " - "because no networking is installed on this computer. " - "Run IDLE with the -n command line switch to start without a " - "subprocess and refer to Help/IDLE Help 'Running without a " - "subprocess' for further details.", - parent=self.tkconsole.text) - - def display_no_subprocess_error(self): - tkMessageBox.showerror( - "Subprocess Startup Error", - "IDLE's subprocess didn't make connection. Either IDLE can't " - "start a subprocess or personal firewall software is blocking " - "the connection.", - parent=self.tkconsole.text) - - def display_executing_dialog(self): - tkMessageBox.showerror( - "Already executing", - "The Python Shell window is already executing a command; " - "please wait until it is finished.", - parent=self.tkconsole.text) - - -class PyShell(OutputWindow): - - shell_title = "Python " + python_version() + " Shell" - - # Override classes - ColorDelegator = ModifiedColorDelegator - UndoDelegator = ModifiedUndoDelegator - - # Override menus - menu_specs = [ - ("file", "_File"), - ("edit", "_Edit"), - ("debug", "_Debug"), - ("options", "_Options"), - ("windows", "_Window"), - ("help", "_Help"), - ] - - - # New classes - from idlelib.history import History - - def __init__(self, flist=None): - if use_subprocess: - ms = self.menu_specs - if ms[2][0] != "shell": - ms.insert(2, ("shell", "She_ll")) - self.interp = ModifiedInterpreter(self) - if flist is None: - root = Tk() - fixwordbreaks(root) - root.withdraw() - flist = PyShellFileList(root) - # - OutputWindow.__init__(self, flist, None, None) - # -## self.config(usetabs=1, indentwidth=8, context_use_ps1=1) - self.usetabs = True - # indentwidth must be 8 when using tabs. See note in EditorWindow: - self.indentwidth = 8 - self.context_use_ps1 = True - # - text = self.text - text.configure(wrap="char") - text.bind("<>", self.enter_callback) - text.bind("<>", self.linefeed_callback) - text.bind("<>", self.cancel_callback) - text.bind("<>", self.eof_callback) - text.bind("<>", self.open_stack_viewer) - text.bind("<>", self.toggle_debugger) - text.bind("<>", self.toggle_jit_stack_viewer) - if use_subprocess: - text.bind("<>", self.view_restart_mark) - text.bind("<>", self.restart_shell) - # - self.save_stdout = sys.stdout - self.save_stderr = sys.stderr - self.save_stdin = sys.stdin - from idlelib import iomenu - self.stdin = PseudoInputFile(self, "stdin", iomenu.encoding) - self.stdout = PseudoOutputFile(self, "stdout", iomenu.encoding) - self.stderr = PseudoOutputFile(self, "stderr", iomenu.encoding) - self.console = PseudoOutputFile(self, "console", iomenu.encoding) - if not use_subprocess: - sys.stdout = self.stdout - sys.stderr = self.stderr - sys.stdin = self.stdin - try: - # page help() text to shell. - import pydoc # import must be done here to capture i/o rebinding. - # XXX KBK 27Dec07 use TextViewer someday, but must work w/o subproc - pydoc.pager = pydoc.plainpager - except: - sys.stderr = sys.__stderr__ - raise - # - self.history = self.History(self.text) - # - self.pollinterval = 50 # millisec - - def get_standard_extension_names(self): - return idleConf.GetExtensions(shell_only=True) - - reading = False - executing = False - canceled = False - endoffile = False - closing = False - _stop_readline_flag = False - - def set_warning_stream(self, stream): - global warning_stream - warning_stream = stream - - def get_warning_stream(self): - return warning_stream - - def toggle_debugger(self, event=None): - if self.executing: - tkMessageBox.showerror("Don't debug now", - "You can only toggle the debugger when idle", - parent=self.text) - self.set_debugger_indicator() - return "break" - else: - db = self.interp.getdebugger() - if db: - self.close_debugger() - else: - self.open_debugger() - - def set_debugger_indicator(self): - db = self.interp.getdebugger() - self.setvar("<>", not not db) - - def toggle_jit_stack_viewer(self, event=None): - pass # All we need is the variable - - def close_debugger(self): - db = self.interp.getdebugger() - if db: - self.interp.setdebugger(None) - db.close() - if self.interp.rpcclt: - debugger_r.close_remote_debugger(self.interp.rpcclt) - self.resetoutput() - self.console.write("[DEBUG OFF]\n") - sys.ps1 = ">>> " - self.showprompt() - self.set_debugger_indicator() - - def open_debugger(self): - if self.interp.rpcclt: - dbg_gui = debugger_r.start_remote_debugger(self.interp.rpcclt, - self) - else: - dbg_gui = debugger.Debugger(self) - self.interp.setdebugger(dbg_gui) - dbg_gui.load_breakpoints() - sys.ps1 = "[DEBUG ON]\n>>> " - self.showprompt() - self.set_debugger_indicator() - - def beginexecuting(self): - "Helper for ModifiedInterpreter" - self.resetoutput() - self.executing = 1 - - def endexecuting(self): - "Helper for ModifiedInterpreter" - self.executing = 0 - self.canceled = 0 - self.showprompt() - - def close(self): - "Extend EditorWindow.close()" - if self.executing: - response = tkMessageBox.askokcancel( - "Kill?", - "Your program is still running!\n Do you want to kill it?", - default="ok", - parent=self.text) - if response is False: - return "cancel" - self.stop_readline() - self.canceled = True - self.closing = True - return EditorWindow.close(self) - - def _close(self): - "Extend EditorWindow._close(), shut down debugger and execution server" - self.close_debugger() - if use_subprocess: - self.interp.kill_subprocess() - # Restore std streams - sys.stdout = self.save_stdout - sys.stderr = self.save_stderr - sys.stdin = self.save_stdin - # Break cycles - self.interp = None - self.console = None - self.flist.pyshell = None - self.history = None - EditorWindow._close(self) - - def ispythonsource(self, filename): - "Override EditorWindow method: never remove the colorizer" - return True - - def short_title(self): - return self.shell_title - - COPYRIGHT = \ - 'Type "copyright", "credits" or "license()" for more information.' - - def begin(self): - self.text.mark_set("iomark", "insert") - self.resetoutput() - if use_subprocess: - nosub = '' - client = self.interp.start_subprocess() - if not client: - self.close() - return False - else: - nosub = ("==== No Subprocess ====\n\n" + - "WARNING: Running IDLE without a Subprocess is deprecated\n" + - "and will be removed in a later version. See Help/IDLE Help\n" + - "for details.\n\n") - sys.displayhook = rpc.displayhook - - self.write("Python %s on %s\n%s\n%s" % - (sys.version, sys.platform, self.COPYRIGHT, nosub)) - self.text.focus_force() - self.showprompt() - import tkinter - tkinter._default_root = None # 03Jan04 KBK What's this? - return True - - def stop_readline(self): - if not self.reading: # no nested mainloop to exit. - return - self._stop_readline_flag = True - self.top.quit() - - def readline(self): - save = self.reading - try: - self.reading = 1 - self.top.mainloop() # nested mainloop() - finally: - self.reading = save - if self._stop_readline_flag: - self._stop_readline_flag = False - return "" - line = self.text.get("iomark", "end-1c") - if len(line) == 0: # may be EOF if we quit our mainloop with Ctrl-C - line = "\n" - self.resetoutput() - if self.canceled: - self.canceled = 0 - if not use_subprocess: - raise KeyboardInterrupt - if self.endoffile: - self.endoffile = 0 - line = "" - return line - - def isatty(self): - return True - - def cancel_callback(self, event=None): - try: - if self.text.compare("sel.first", "!=", "sel.last"): - return # Active selection -- always use default binding - except: - pass - if not (self.executing or self.reading): - self.resetoutput() - self.interp.write("KeyboardInterrupt\n") - self.showprompt() - return "break" - self.endoffile = 0 - self.canceled = 1 - if (self.executing and self.interp.rpcclt): - if self.interp.getdebugger(): - self.interp.restart_subprocess() - else: - self.interp.interrupt_subprocess() - if self.reading: - self.top.quit() # exit the nested mainloop() in readline() - return "break" - - def eof_callback(self, event): - if self.executing and not self.reading: - return # Let the default binding (delete next char) take over - if not (self.text.compare("iomark", "==", "insert") and - self.text.compare("insert", "==", "end-1c")): - return # Let the default binding (delete next char) take over - if not self.executing: - self.resetoutput() - self.close() - else: - self.canceled = 0 - self.endoffile = 1 - self.top.quit() - return "break" - - def linefeed_callback(self, event): - # Insert a linefeed without entering anything (still autoindented) - if self.reading: - self.text.insert("insert", "\n") - self.text.see("insert") - else: - self.newline_and_indent_event(event) - return "break" - - def enter_callback(self, event): - if self.executing and not self.reading: - return # Let the default binding (insert '\n') take over - # If some text is selected, recall the selection - # (but only if this before the I/O mark) - try: - sel = self.text.get("sel.first", "sel.last") - if sel: - if self.text.compare("sel.last", "<=", "iomark"): - self.recall(sel, event) - return "break" - except: - pass - # If we're strictly before the line containing iomark, recall - # the current line, less a leading prompt, less leading or - # trailing whitespace - if self.text.compare("insert", "<", "iomark linestart"): - # Check if there's a relevant stdin range -- if so, use it - prev = self.text.tag_prevrange("stdin", "insert") - if prev and self.text.compare("insert", "<", prev[1]): - self.recall(self.text.get(prev[0], prev[1]), event) - return "break" - next = self.text.tag_nextrange("stdin", "insert") - if next and self.text.compare("insert lineend", ">=", next[0]): - self.recall(self.text.get(next[0], next[1]), event) - return "break" - # No stdin mark -- just get the current line, less any prompt - indices = self.text.tag_nextrange("console", "insert linestart") - if indices and \ - self.text.compare(indices[0], "<=", "insert linestart"): - self.recall(self.text.get(indices[1], "insert lineend"), event) - else: - self.recall(self.text.get("insert linestart", "insert lineend"), event) - return "break" - # If we're between the beginning of the line and the iomark, i.e. - # in the prompt area, move to the end of the prompt - if self.text.compare("insert", "<", "iomark"): - self.text.mark_set("insert", "iomark") - # If we're in the current input and there's only whitespace - # beyond the cursor, erase that whitespace first - s = self.text.get("insert", "end-1c") - if s and not s.strip(): - self.text.delete("insert", "end-1c") - # If we're in the current input before its last line, - # insert a newline right at the insert point - if self.text.compare("insert", "<", "end-1c linestart"): - self.newline_and_indent_event(event) - return "break" - # We're in the last line; append a newline and submit it - self.text.mark_set("insert", "end-1c") - if self.reading: - self.text.insert("insert", "\n") - self.text.see("insert") - else: - self.newline_and_indent_event(event) - self.text.tag_add("stdin", "iomark", "end-1c") - self.text.update_idletasks() - if self.reading: - self.top.quit() # Break out of recursive mainloop() - else: - self.runit() - return "break" - - def recall(self, s, event): - # remove leading and trailing empty or whitespace lines - s = re.sub(r'^\s*\n', '' , s) - s = re.sub(r'\n\s*$', '', s) - lines = s.split('\n') - self.text.undo_block_start() - try: - self.text.tag_remove("sel", "1.0", "end") - self.text.mark_set("insert", "end-1c") - prefix = self.text.get("insert linestart", "insert") - if prefix.rstrip().endswith(':'): - self.newline_and_indent_event(event) - prefix = self.text.get("insert linestart", "insert") - self.text.insert("insert", lines[0].strip()) - if len(lines) > 1: - orig_base_indent = re.search(r'^([ \t]*)', lines[0]).group(0) - new_base_indent = re.search(r'^([ \t]*)', prefix).group(0) - for line in lines[1:]: - if line.startswith(orig_base_indent): - # replace orig base indentation with new indentation - line = new_base_indent + line[len(orig_base_indent):] - self.text.insert('insert', '\n'+line.rstrip()) - finally: - self.text.see("insert") - self.text.undo_block_stop() - - def runit(self): - line = self.text.get("iomark", "end-1c") - # Strip off last newline and surrounding whitespace. - # (To allow you to hit return twice to end a statement.) - i = len(line) - while i > 0 and line[i-1] in " \t": - i = i-1 - if i > 0 and line[i-1] == "\n": - i = i-1 - while i > 0 and line[i-1] in " \t": - i = i-1 - line = line[:i] - self.interp.runsource(line) - - def open_stack_viewer(self, event=None): - if self.interp.rpcclt: - return self.interp.remote_stack_viewer() - try: - sys.last_traceback - except: - tkMessageBox.showerror("No stack trace", - "There is no stack trace yet.\n" - "(sys.last_traceback is not defined)", - parent=self.text) - return - from idlelib.stackviewer import StackBrowser - StackBrowser(self.root, self.flist) - - def view_restart_mark(self, event=None): - self.text.see("iomark") - self.text.see("restart") - - def restart_shell(self, event=None): - "Callback for Run/Restart Shell Cntl-F6" - self.interp.restart_subprocess(with_cwd=True) - - def showprompt(self): - self.resetoutput() - try: - s = str(sys.ps1) - except: - s = "" - self.console.write(s) - self.text.mark_set("insert", "end-1c") - self.set_line_and_column() - self.io.reset_undo() - - def resetoutput(self): - source = self.text.get("iomark", "end-1c") - if self.history: - self.history.store(source) - if self.text.get("end-2c") != "\n": - self.text.insert("end-1c", "\n") - self.text.mark_set("iomark", "end-1c") - self.set_line_and_column() - - def write(self, s, tags=()): - if isinstance(s, str) and len(s) and max(s) > '\uffff': - # Tk doesn't support outputting non-BMP characters - # Let's assume what printed string is not very long, - # find first non-BMP character and construct informative - # UnicodeEncodeError exception. - for start, char in enumerate(s): - if char > '\uffff': - break - raise UnicodeEncodeError("UCS-2", char, start, start+1, - 'Non-BMP character not supported in Tk') - try: - self.text.mark_gravity("iomark", "right") - count = OutputWindow.write(self, s, tags, "iomark") - self.text.mark_gravity("iomark", "left") - except: - raise ###pass # ### 11Aug07 KBK if we are expecting exceptions - # let's find out what they are and be specific. - if self.canceled: - self.canceled = 0 - if not use_subprocess: - raise KeyboardInterrupt - return count - - def rmenu_check_cut(self): - try: - if self.text.compare('sel.first', '<', 'iomark'): - return 'disabled' - except TclError: # no selection, so the index 'sel.first' doesn't exist - return 'disabled' - return super().rmenu_check_cut() - - def rmenu_check_paste(self): - if self.text.compare('insert','<','iomark'): - return 'disabled' - return super().rmenu_check_paste() - -class PseudoFile(io.TextIOBase): - - def __init__(self, shell, tags, encoding=None): - self.shell = shell - self.tags = tags - self._encoding = encoding - - @property - def encoding(self): - return self._encoding - - @property - def name(self): - return '<%s>' % self.tags - - def isatty(self): - return True - - -class PseudoOutputFile(PseudoFile): - - def writable(self): - return True - - def write(self, s): - if self.closed: - raise ValueError("write to closed file") - if type(s) is not str: - if not isinstance(s, str): - raise TypeError('must be str, not ' + type(s).__name__) - # See issue #19481 - s = str.__str__(s) - return self.shell.write(s, self.tags) - - -class PseudoInputFile(PseudoFile): - - def __init__(self, shell, tags, encoding=None): - PseudoFile.__init__(self, shell, tags, encoding) - self._line_buffer = '' - - def readable(self): - return True - - def read(self, size=-1): - if self.closed: - raise ValueError("read from closed file") - if size is None: - size = -1 - elif not isinstance(size, int): - raise TypeError('must be int, not ' + type(size).__name__) - result = self._line_buffer - self._line_buffer = '' - if size < 0: - while True: - line = self.shell.readline() - if not line: break - result += line - else: - while len(result) < size: - line = self.shell.readline() - if not line: break - result += line - self._line_buffer = result[size:] - result = result[:size] - return result - - def readline(self, size=-1): - if self.closed: - raise ValueError("read from closed file") - if size is None: - size = -1 - elif not isinstance(size, int): - raise TypeError('must be int, not ' + type(size).__name__) - line = self._line_buffer or self.shell.readline() - if size < 0: - size = len(line) - eol = line.find('\n', 0, size) - if eol >= 0: - size = eol + 1 - self._line_buffer = line[size:] - return line[:size] - - def close(self): - self.shell.close() - - -def fix_x11_paste(root): - "Make paste replace selection on x11. See issue #5124." - if root._windowingsystem == 'x11': - for cls in 'Text', 'Entry', 'Spinbox': - root.bind_class( - cls, - '<>', - 'catch {%W delete sel.first sel.last}\n' + - root.bind_class(cls, '<>')) - - -usage_msg = """\ - -USAGE: idle [-deins] [-t title] [file]* - idle [-dns] [-t title] (-c cmd | -r file) [arg]* - idle [-dns] [-t title] - [arg]* - - -h print this help message and exit - -n run IDLE without a subprocess (DEPRECATED, - see Help/IDLE Help for details) - -The following options will override the IDLE 'settings' configuration: - - -e open an edit window - -i open a shell window - -The following options imply -i and will open a shell: - - -c cmd run the command in a shell, or - -r file run script from file - - -d enable the debugger - -s run $IDLESTARTUP or $PYTHONSTARTUP before anything else - -t title set title of shell window - -A default edit window will be bypassed when -c, -r, or - are used. - -[arg]* are passed to the command (-c) or script (-r) in sys.argv[1:]. - -Examples: - -idle - Open an edit window or shell depending on IDLE's configuration. - -idle foo.py foobar.py - Edit the files, also open a shell if configured to start with shell. - -idle -est "Baz" foo.py - Run $IDLESTARTUP or $PYTHONSTARTUP, edit foo.py, and open a shell - window with the title "Baz". - -idle -c "import sys; print(sys.argv)" "foo" - Open a shell window and run the command, passing "-c" in sys.argv[0] - and "foo" in sys.argv[1]. - -idle -d -s -r foo.py "Hello World" - Open a shell window, run a startup script, enable the debugger, and - run foo.py, passing "foo.py" in sys.argv[0] and "Hello World" in - sys.argv[1]. - -echo "import sys; print(sys.argv)" | idle - "foobar" - Open a shell window, run the script piped in, passing '' in sys.argv[0] - and "foobar" in sys.argv[1]. -""" - -def main(): - global flist, root, use_subprocess - - capture_warnings(True) - use_subprocess = True - enable_shell = False - enable_edit = False - debug = False - cmd = None - script = None - startup = False - try: - opts, args = getopt.getopt(sys.argv[1:], "c:deihnr:st:") - except getopt.error as msg: - print("Error: %s\n%s" % (msg, usage_msg), file=sys.stderr) - sys.exit(2) - for o, a in opts: - if o == '-c': - cmd = a - enable_shell = True - if o == '-d': - debug = True - enable_shell = True - if o == '-e': - enable_edit = True - if o == '-h': - sys.stdout.write(usage_msg) - sys.exit() - if o == '-i': - enable_shell = True - if o == '-n': - print(" Warning: running IDLE without a subprocess is deprecated.", - file=sys.stderr) - use_subprocess = False - if o == '-r': - script = a - if os.path.isfile(script): - pass - else: - print("No script file: ", script) - sys.exit() - enable_shell = True - if o == '-s': - startup = True - enable_shell = True - if o == '-t': - PyShell.shell_title = a - enable_shell = True - if args and args[0] == '-': - cmd = sys.stdin.read() - enable_shell = True - # process sys.argv and sys.path: - for i in range(len(sys.path)): - sys.path[i] = os.path.abspath(sys.path[i]) - if args and args[0] == '-': - sys.argv = [''] + args[1:] - elif cmd: - sys.argv = ['-c'] + args - elif script: - sys.argv = [script] + args - elif args: - enable_edit = True - pathx = [] - for filename in args: - pathx.append(os.path.dirname(filename)) - for dir in pathx: - dir = os.path.abspath(dir) - if not dir in sys.path: - sys.path.insert(0, dir) - else: - dir = os.getcwd() - if dir not in sys.path: - sys.path.insert(0, dir) - # check the IDLE settings configuration (but command line overrides) - edit_start = idleConf.GetOption('main', 'General', - 'editor-on-startup', type='bool') - enable_edit = enable_edit or edit_start - enable_shell = enable_shell or not enable_edit - - # start editor and/or shell windows: - root = Tk(className="Idle") - root.withdraw() - - # set application icon - icondir = os.path.join(os.path.dirname(__file__), 'Icons') - if system() == 'Windows': - iconfile = os.path.join(icondir, 'idle.ico') - root.wm_iconbitmap(default=iconfile) - else: - ext = '.png' if TkVersion >= 8.6 else '.gif' - iconfiles = [os.path.join(icondir, 'idle_%d%s' % (size, ext)) - for size in (16, 32, 48)] - icons = [PhotoImage(file=iconfile) for iconfile in iconfiles] - root.wm_iconphoto(True, *icons) - - fixwordbreaks(root) - fix_x11_paste(root) - flist = PyShellFileList(root) - macosx.setupApp(root, flist) - - if enable_edit: - if not (cmd or script): - for filename in args[:]: - if flist.open(filename) is None: - # filename is a directory actually, disconsider it - args.remove(filename) - if not args: - flist.new() - - if enable_shell: - shell = flist.open_shell() - if not shell: - return # couldn't open shell - if macosx.isAquaTk() and flist.dict: - # On OSX: when the user has double-clicked on a file that causes - # IDLE to be launched the shell window will open just in front of - # the file she wants to see. Lower the interpreter window when - # there are open files. - shell.top.lower() - else: - shell = flist.pyshell - - # Handle remaining options. If any of these are set, enable_shell - # was set also, so shell must be true to reach here. - if debug: - shell.open_debugger() - if startup: - filename = os.environ.get("IDLESTARTUP") or \ - os.environ.get("PYTHONSTARTUP") - if filename and os.path.isfile(filename): - shell.interp.execfile(filename) - if cmd or script: - shell.interp.runcommand("""if 1: - import sys as _sys - _sys.argv = %r - del _sys - \n""" % (sys.argv,)) - if cmd: - shell.interp.execsource(cmd) - elif script: - shell.interp.prepend_syspath(script) - shell.interp.execfile(script) - elif shell: - # If there is a shell window and no cmd or script in progress, - # check for problematic OS X Tk versions and print a warning - # message in the IDLE shell window; this is less intrusive - # than always opening a separate window. - tkversionwarning = macosx.tkVersionWarning(root) - if tkversionwarning: - shell.interp.runcommand("print('%s')" % tkversionwarning) - - while flist.inversedict: # keep IDLE running while files are open. - root.mainloop() - root.destroy() - capture_warnings(False) - -if __name__ == "__main__": - sys.modules['pyshell'] = sys.modules['__main__'] - main() - -capture_warnings(False) # Make sure turned off; see issue 18081 diff --git a/Lib/idlelib/idle_test/test_editmenu.py b/Lib/idlelib/idle_test/test_editmenu.py index 50317a97e5..654f060225 100644 --- a/Lib/idlelib/idle_test/test_editmenu.py +++ b/Lib/idlelib/idle_test/test_editmenu.py @@ -5,8 +5,9 @@ Edit modules have their own test files files from test.support import requires requires('gui') import tkinter as tk +from tkinter import ttk import unittest -from idlelib import PyShell +from idlelib import pyshell class PasteTest(unittest.TestCase): '''Test pasting into widgets that allow pasting. @@ -16,16 +17,17 @@ class PasteTest(unittest.TestCase): @classmethod def setUpClass(cls): cls.root = root = tk.Tk() - PyShell.fix_x11_paste(root) + pyshell.fix_x11_paste(root) cls.text = tk.Text(root) cls.entry = tk.Entry(root) + cls.tentry = ttk.Entry(root) cls.spin = tk.Spinbox(root) root.clipboard_clear() root.clipboard_append('two') @classmethod def tearDownClass(cls): - del cls.text, cls.entry, cls.spin + del cls.text, cls.entry, cls.tentry cls.root.clipboard_clear() cls.root.update_idletasks() cls.root.destroy() @@ -43,16 +45,16 @@ class PasteTest(unittest.TestCase): def test_paste_entry(self): "Test pasting into an entry with and without a selection." - # On 3.6, generated <> fails without empty select range - # for 'no selection'. Live widget works fine. - entry = self.entry - for end, ans in (0, 'onetwo'), ('end', 'two'): - with self.subTest(entry=entry, end=end, ans=ans): - entry.delete(0, 'end') - entry.insert(0, 'one') - entry.select_range(0, end) # see note - entry.event_generate('<>') - self.assertEqual(entry.get(), ans) + # Generated <> fails for tk entry without empty select + # range for 'no selection'. Live widget works fine. + for entry in self.entry, self.tentry: + for end, ans in (0, 'onetwo'), ('end', 'two'): + with self.subTest(entry=entry, end=end, ans=ans): + entry.delete(0, 'end') + entry.insert(0, 'one') + entry.select_range(0, end) + entry.event_generate('<>') + self.assertEqual(entry.get(), ans) def test_paste_spin(self): "Test pasting into a spinbox with and without a selection." diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py new file mode 100755 index 0000000000..0f7a01d77b --- /dev/null +++ b/Lib/idlelib/pyshell.py @@ -0,0 +1,1631 @@ +#! /usr/bin/env python3 + +try: + from tkinter import * +except ImportError: + print("** IDLE can't import Tkinter.\n" + "Your Python may not be configured for Tk. **", file=sys.__stderr__) + sys.exit(1) +import tkinter.messagebox as tkMessageBox +if TkVersion < 8.5: + root = Tk() # otherwise create root in main + root.withdraw() + tkMessageBox.showerror("Idle Cannot Start", + "Idle requires tcl/tk 8.5+, not $s." % TkVersion, + parent=root) + sys.exit(1) + +import getopt +import os +import os.path +import re +import socket +import subprocess +import sys +import threading +import time +import tokenize +import io + +import linecache +from code import InteractiveInterpreter +from platform import python_version, system + +from idlelib.editor import EditorWindow, fixwordbreaks +from idlelib.filelist import FileList +from idlelib.colorizer import ColorDelegator +from idlelib.undo import UndoDelegator +from idlelib.outwin import OutputWindow +from idlelib.config import idleConf +from idlelib import rpc +from idlelib import debugger +from idlelib import debugger_r +from idlelib import macosx + +HOST = '127.0.0.1' # python execution server on localhost loopback +PORT = 0 # someday pass in host, port for remote debug capability + +# Override warnings module to write to warning_stream. Initialize to send IDLE +# internal warnings to the console. ScriptBinding.check_syntax() will +# temporarily redirect the stream to the shell window to display warnings when +# checking user's code. +warning_stream = sys.__stderr__ # None, at least on Windows, if no console. +import warnings + +def idle_formatwarning(message, category, filename, lineno, line=None): + """Format warnings the IDLE way.""" + + s = "\nWarning (from warnings module):\n" + s += ' File \"%s\", line %s\n' % (filename, lineno) + if line is None: + line = linecache.getline(filename, lineno) + line = line.strip() + if line: + s += " %s\n" % line + s += "%s: %s\n" % (category.__name__, message) + return s + +def idle_showwarning( + message, category, filename, lineno, file=None, line=None): + """Show Idle-format warning (after replacing warnings.showwarning). + + The differences are the formatter called, the file=None replacement, + which can be None, the capture of the consequence AttributeError, + and the output of a hard-coded prompt. + """ + if file is None: + file = warning_stream + try: + file.write(idle_formatwarning( + message, category, filename, lineno, line=line)) + file.write(">>> ") + except (AttributeError, OSError): + pass # if file (probably __stderr__) is invalid, skip warning. + +_warnings_showwarning = None + +def capture_warnings(capture): + "Replace warning.showwarning with idle_showwarning, or reverse." + + global _warnings_showwarning + if capture: + if _warnings_showwarning is None: + _warnings_showwarning = warnings.showwarning + warnings.showwarning = idle_showwarning + else: + if _warnings_showwarning is not None: + warnings.showwarning = _warnings_showwarning + _warnings_showwarning = None + +capture_warnings(True) + +def extended_linecache_checkcache(filename=None, + orig_checkcache=linecache.checkcache): + """Extend linecache.checkcache to preserve the entries + + Rather than repeating the linecache code, patch it to save the + entries, call the original linecache.checkcache() + (skipping them), and then restore the saved entries. + + orig_checkcache is bound at definition time to the original + method, allowing it to be patched. + """ + cache = linecache.cache + save = {} + for key in list(cache): + if key[:1] + key[-1:] == '<>': + save[key] = cache.pop(key) + orig_checkcache(filename) + cache.update(save) + +# Patch linecache.checkcache(): +linecache.checkcache = extended_linecache_checkcache + + +class PyShellEditorWindow(EditorWindow): + "Regular text edit window in IDLE, supports breakpoints" + + def __init__(self, *args): + self.breakpoints = [] + EditorWindow.__init__(self, *args) + self.text.bind("<>", self.set_breakpoint_here) + self.text.bind("<>", self.clear_breakpoint_here) + self.text.bind("<>", self.flist.open_shell) + + self.breakpointPath = os.path.join(idleConf.GetUserCfgDir(), + 'breakpoints.lst') + # whenever a file is changed, restore breakpoints + def filename_changed_hook(old_hook=self.io.filename_change_hook, + self=self): + self.restore_file_breaks() + old_hook() + self.io.set_filename_change_hook(filename_changed_hook) + if self.io.filename: + self.restore_file_breaks() + self.color_breakpoint_text() + + rmenu_specs = [ + ("Cut", "<>", "rmenu_check_cut"), + ("Copy", "<>", "rmenu_check_copy"), + ("Paste", "<>", "rmenu_check_paste"), + (None, None, None), + ("Set Breakpoint", "<>", None), + ("Clear Breakpoint", "<>", None) + ] + + def color_breakpoint_text(self, color=True): + "Turn colorizing of breakpoint text on or off" + if self.io is None: + # possible due to update in restore_file_breaks + return + if color: + theme = idleConf.CurrentTheme() + cfg = idleConf.GetHighlight(theme, "break") + else: + cfg = {'foreground': '', 'background': ''} + self.text.tag_config('BREAK', cfg) + + def set_breakpoint(self, lineno): + text = self.text + filename = self.io.filename + text.tag_add("BREAK", "%d.0" % lineno, "%d.0" % (lineno+1)) + try: + self.breakpoints.index(lineno) + except ValueError: # only add if missing, i.e. do once + self.breakpoints.append(lineno) + try: # update the subprocess debugger + debug = self.flist.pyshell.interp.debugger + debug.set_breakpoint_here(filename, lineno) + except: # but debugger may not be active right now.... + pass + + def set_breakpoint_here(self, event=None): + text = self.text + filename = self.io.filename + if not filename: + text.bell() + return + lineno = int(float(text.index("insert"))) + self.set_breakpoint(lineno) + + def clear_breakpoint_here(self, event=None): + text = self.text + filename = self.io.filename + if not filename: + text.bell() + return + lineno = int(float(text.index("insert"))) + try: + self.breakpoints.remove(lineno) + except: + pass + text.tag_remove("BREAK", "insert linestart",\ + "insert lineend +1char") + try: + debug = self.flist.pyshell.interp.debugger + debug.clear_breakpoint_here(filename, lineno) + except: + pass + + def clear_file_breaks(self): + if self.breakpoints: + text = self.text + filename = self.io.filename + if not filename: + text.bell() + return + self.breakpoints = [] + text.tag_remove("BREAK", "1.0", END) + try: + debug = self.flist.pyshell.interp.debugger + debug.clear_file_breaks(filename) + except: + pass + + def store_file_breaks(self): + "Save breakpoints when file is saved" + # XXX 13 Dec 2002 KBK Currently the file must be saved before it can + # be run. The breaks are saved at that time. If we introduce + # a temporary file save feature the save breaks functionality + # needs to be re-verified, since the breaks at the time the + # temp file is created may differ from the breaks at the last + # permanent save of the file. Currently, a break introduced + # after a save will be effective, but not persistent. + # This is necessary to keep the saved breaks synched with the + # saved file. + # + # Breakpoints are set as tagged ranges in the text. + # Since a modified file has to be saved before it is + # run, and since self.breakpoints (from which the subprocess + # debugger is loaded) is updated during the save, the visible + # breaks stay synched with the subprocess even if one of these + # unexpected breakpoint deletions occurs. + breaks = self.breakpoints + filename = self.io.filename + try: + with open(self.breakpointPath, "r") as fp: + lines = fp.readlines() + except OSError: + lines = [] + try: + with open(self.breakpointPath, "w") as new_file: + for line in lines: + if not line.startswith(filename + '='): + new_file.write(line) + self.update_breakpoints() + breaks = self.breakpoints + if breaks: + new_file.write(filename + '=' + str(breaks) + '\n') + except OSError as err: + if not getattr(self.root, "breakpoint_error_displayed", False): + self.root.breakpoint_error_displayed = True + tkMessageBox.showerror(title='IDLE Error', + message='Unable to update breakpoint list:\n%s' + % str(err), + parent=self.text) + + def restore_file_breaks(self): + self.text.update() # this enables setting "BREAK" tags to be visible + if self.io is None: + # can happen if IDLE closes due to the .update() call + return + filename = self.io.filename + if filename is None: + return + if os.path.isfile(self.breakpointPath): + with open(self.breakpointPath, "r") as fp: + lines = fp.readlines() + for line in lines: + if line.startswith(filename + '='): + breakpoint_linenumbers = eval(line[len(filename)+1:]) + for breakpoint_linenumber in breakpoint_linenumbers: + self.set_breakpoint(breakpoint_linenumber) + + def update_breakpoints(self): + "Retrieves all the breakpoints in the current window" + text = self.text + ranges = text.tag_ranges("BREAK") + linenumber_list = self.ranges_to_linenumbers(ranges) + self.breakpoints = linenumber_list + + def ranges_to_linenumbers(self, ranges): + lines = [] + for index in range(0, len(ranges), 2): + lineno = int(float(ranges[index].string)) + end = int(float(ranges[index+1].string)) + while lineno < end: + lines.append(lineno) + lineno += 1 + return lines + +# XXX 13 Dec 2002 KBK Not used currently +# def saved_change_hook(self): +# "Extend base method - clear breaks if module is modified" +# if not self.get_saved(): +# self.clear_file_breaks() +# EditorWindow.saved_change_hook(self) + + def _close(self): + "Extend base method - clear breaks when module is closed" + self.clear_file_breaks() + EditorWindow._close(self) + + +class PyShellFileList(FileList): + "Extend base class: IDLE supports a shell and breakpoints" + + # override FileList's class variable, instances return PyShellEditorWindow + # instead of EditorWindow when new edit windows are created. + EditorWindow = PyShellEditorWindow + + pyshell = None + + def open_shell(self, event=None): + if self.pyshell: + self.pyshell.top.wakeup() + else: + self.pyshell = PyShell(self) + if self.pyshell: + if not self.pyshell.begin(): + return None + return self.pyshell + + +class ModifiedColorDelegator(ColorDelegator): + "Extend base class: colorizer for the shell window itself" + + def __init__(self): + ColorDelegator.__init__(self) + self.LoadTagDefs() + + def recolorize_main(self): + self.tag_remove("TODO", "1.0", "iomark") + self.tag_add("SYNC", "1.0", "iomark") + ColorDelegator.recolorize_main(self) + + def LoadTagDefs(self): + ColorDelegator.LoadTagDefs(self) + theme = idleConf.CurrentTheme() + self.tagdefs.update({ + "stdin": {'background':None,'foreground':None}, + "stdout": idleConf.GetHighlight(theme, "stdout"), + "stderr": idleConf.GetHighlight(theme, "stderr"), + "console": idleConf.GetHighlight(theme, "console"), + }) + + def removecolors(self): + # Don't remove shell color tags before "iomark" + for tag in self.tagdefs: + self.tag_remove(tag, "iomark", "end") + +class ModifiedUndoDelegator(UndoDelegator): + "Extend base class: forbid insert/delete before the I/O mark" + + def insert(self, index, chars, tags=None): + try: + if self.delegate.compare(index, "<", "iomark"): + self.delegate.bell() + return + except TclError: + pass + UndoDelegator.insert(self, index, chars, tags) + + def delete(self, index1, index2=None): + try: + if self.delegate.compare(index1, "<", "iomark"): + self.delegate.bell() + return + except TclError: + pass + UndoDelegator.delete(self, index1, index2) + + +class MyRPCClient(rpc.RPCClient): + + def handle_EOF(self): + "Override the base class - just re-raise EOFError" + raise EOFError + + +class ModifiedInterpreter(InteractiveInterpreter): + + def __init__(self, tkconsole): + self.tkconsole = tkconsole + locals = sys.modules['__main__'].__dict__ + InteractiveInterpreter.__init__(self, locals=locals) + self.save_warnings_filters = None + self.restarting = False + self.subprocess_arglist = None + self.port = PORT + self.original_compiler_flags = self.compile.compiler.flags + + _afterid = None + rpcclt = None + rpcsubproc = None + + def spawn_subprocess(self): + if self.subprocess_arglist is None: + self.subprocess_arglist = self.build_subprocess_arglist() + self.rpcsubproc = subprocess.Popen(self.subprocess_arglist) + + def build_subprocess_arglist(self): + assert (self.port!=0), ( + "Socket should have been assigned a port number.") + w = ['-W' + s for s in sys.warnoptions] + # Maybe IDLE is installed and is being accessed via sys.path, + # or maybe it's not installed and the idle.py script is being + # run from the IDLE source directory. + del_exitf = idleConf.GetOption('main', 'General', 'delete-exitfunc', + default=False, type='bool') + if __name__ == 'idlelib.pyshell': + command = "__import__('idlelib.run').run.main(%r)" % (del_exitf,) + else: + command = "__import__('run').main(%r)" % (del_exitf,) + return [sys.executable] + w + ["-c", command, str(self.port)] + + def start_subprocess(self): + addr = (HOST, self.port) + # GUI makes several attempts to acquire socket, listens for connection + for i in range(3): + time.sleep(i) + try: + self.rpcclt = MyRPCClient(addr) + break + except OSError: + pass + else: + self.display_port_binding_error() + return None + # if PORT was 0, system will assign an 'ephemeral' port. Find it out: + self.port = self.rpcclt.listening_sock.getsockname()[1] + # if PORT was not 0, probably working with a remote execution server + if PORT != 0: + # To allow reconnection within the 2MSL wait (cf. Stevens TCP + # V1, 18.6), set SO_REUSEADDR. Note that this can be problematic + # on Windows since the implementation allows two active sockets on + # the same address! + self.rpcclt.listening_sock.setsockopt(socket.SOL_SOCKET, + socket.SO_REUSEADDR, 1) + self.spawn_subprocess() + #time.sleep(20) # test to simulate GUI not accepting connection + # Accept the connection from the Python execution server + self.rpcclt.listening_sock.settimeout(10) + try: + self.rpcclt.accept() + except socket.timeout: + self.display_no_subprocess_error() + return None + self.rpcclt.register("console", self.tkconsole) + self.rpcclt.register("stdin", self.tkconsole.stdin) + self.rpcclt.register("stdout", self.tkconsole.stdout) + self.rpcclt.register("stderr", self.tkconsole.stderr) + self.rpcclt.register("flist", self.tkconsole.flist) + self.rpcclt.register("linecache", linecache) + self.rpcclt.register("interp", self) + self.transfer_path(with_cwd=True) + self.poll_subprocess() + return self.rpcclt + + def restart_subprocess(self, with_cwd=False, filename=''): + if self.restarting: + return self.rpcclt + self.restarting = True + # close only the subprocess debugger + debug = self.getdebugger() + if debug: + try: + # Only close subprocess debugger, don't unregister gui_adap! + debugger_r.close_subprocess_debugger(self.rpcclt) + except: + pass + # Kill subprocess, spawn a new one, accept connection. + self.rpcclt.close() + self.terminate_subprocess() + console = self.tkconsole + was_executing = console.executing + console.executing = False + self.spawn_subprocess() + try: + self.rpcclt.accept() + except socket.timeout: + self.display_no_subprocess_error() + return None + self.transfer_path(with_cwd=with_cwd) + console.stop_readline() + # annotate restart in shell window and mark it + console.text.delete("iomark", "end-1c") + tag = 'RESTART: ' + (filename if filename else 'Shell') + halfbar = ((int(console.width) -len(tag) - 4) // 2) * '=' + console.write("\n{0} {1} {0}".format(halfbar, tag)) + console.text.mark_set("restart", "end-1c") + console.text.mark_gravity("restart", "left") + if not filename: + console.showprompt() + # restart subprocess debugger + if debug: + # Restarted debugger connects to current instance of debug GUI + debugger_r.restart_subprocess_debugger(self.rpcclt) + # reload remote debugger breakpoints for all PyShellEditWindows + debug.load_breakpoints() + self.compile.compiler.flags = self.original_compiler_flags + self.restarting = False + return self.rpcclt + + def __request_interrupt(self): + self.rpcclt.remotecall("exec", "interrupt_the_server", (), {}) + + def interrupt_subprocess(self): + threading.Thread(target=self.__request_interrupt).start() + + def kill_subprocess(self): + if self._afterid is not None: + self.tkconsole.text.after_cancel(self._afterid) + try: + self.rpcclt.listening_sock.close() + except AttributeError: # no socket + pass + try: + self.rpcclt.close() + except AttributeError: # no socket + pass + self.terminate_subprocess() + self.tkconsole.executing = False + self.rpcclt = None + + def terminate_subprocess(self): + "Make sure subprocess is terminated" + try: + self.rpcsubproc.kill() + except OSError: + # process already terminated + return + else: + try: + self.rpcsubproc.wait() + except OSError: + return + + def transfer_path(self, with_cwd=False): + if with_cwd: # Issue 13506 + path = [''] # include Current Working Directory + path.extend(sys.path) + else: + path = sys.path + + self.runcommand("""if 1: + import sys as _sys + _sys.path = %r + del _sys + \n""" % (path,)) + + active_seq = None + + def poll_subprocess(self): + clt = self.rpcclt + if clt is None: + return + try: + response = clt.pollresponse(self.active_seq, wait=0.05) + except (EOFError, OSError, KeyboardInterrupt): + # lost connection or subprocess terminated itself, restart + # [the KBI is from rpc.SocketIO.handle_EOF()] + if self.tkconsole.closing: + return + response = None + self.restart_subprocess() + if response: + self.tkconsole.resetoutput() + self.active_seq = None + how, what = response + console = self.tkconsole.console + if how == "OK": + if what is not None: + print(repr(what), file=console) + elif how == "EXCEPTION": + if self.tkconsole.getvar("<>"): + self.remote_stack_viewer() + elif how == "ERROR": + errmsg = "pyshell.ModifiedInterpreter: Subprocess ERROR:\n" + print(errmsg, what, file=sys.__stderr__) + print(errmsg, what, file=console) + # we received a response to the currently active seq number: + try: + self.tkconsole.endexecuting() + except AttributeError: # shell may have closed + pass + # Reschedule myself + if not self.tkconsole.closing: + self._afterid = self.tkconsole.text.after( + self.tkconsole.pollinterval, self.poll_subprocess) + + debugger = None + + def setdebugger(self, debugger): + self.debugger = debugger + + def getdebugger(self): + return self.debugger + + def open_remote_stack_viewer(self): + """Initiate the remote stack viewer from a separate thread. + + This method is called from the subprocess, and by returning from this + method we allow the subprocess to unblock. After a bit the shell + requests the subprocess to open the remote stack viewer which returns a + static object looking at the last exception. It is queried through + the RPC mechanism. + + """ + self.tkconsole.text.after(300, self.remote_stack_viewer) + return + + def remote_stack_viewer(self): + from idlelib import debugobj_r + oid = self.rpcclt.remotequeue("exec", "stackviewer", ("flist",), {}) + if oid is None: + self.tkconsole.root.bell() + return + item = debugobj_r.StubObjectTreeItem(self.rpcclt, oid) + from idlelib.tree import ScrolledCanvas, TreeNode + top = Toplevel(self.tkconsole.root) + theme = idleConf.CurrentTheme() + background = idleConf.GetHighlight(theme, 'normal')['background'] + sc = ScrolledCanvas(top, bg=background, highlightthickness=0) + sc.frame.pack(expand=1, fill="both") + node = TreeNode(sc.canvas, None, item) + node.expand() + # XXX Should GC the remote tree when closing the window + + gid = 0 + + def execsource(self, source): + "Like runsource() but assumes complete exec source" + filename = self.stuffsource(source) + self.execfile(filename, source) + + def execfile(self, filename, source=None): + "Execute an existing file" + if source is None: + with tokenize.open(filename) as fp: + source = fp.read() + try: + code = compile(source, filename, "exec") + except (OverflowError, SyntaxError): + self.tkconsole.resetoutput() + print('*** Error in script or command!\n' + 'Traceback (most recent call last):', + file=self.tkconsole.stderr) + InteractiveInterpreter.showsyntaxerror(self, filename) + self.tkconsole.showprompt() + else: + self.runcode(code) + + def runsource(self, source): + "Extend base class method: Stuff the source in the line cache first" + filename = self.stuffsource(source) + self.more = 0 + self.save_warnings_filters = warnings.filters[:] + warnings.filterwarnings(action="error", category=SyntaxWarning) + # at the moment, InteractiveInterpreter expects str + assert isinstance(source, str) + #if isinstance(source, str): + # from idlelib import iomenu + # try: + # source = source.encode(iomenu.encoding) + # except UnicodeError: + # self.tkconsole.resetoutput() + # self.write("Unsupported characters in input\n") + # return + try: + # InteractiveInterpreter.runsource() calls its runcode() method, + # which is overridden (see below) + return InteractiveInterpreter.runsource(self, source, filename) + finally: + if self.save_warnings_filters is not None: + warnings.filters[:] = self.save_warnings_filters + self.save_warnings_filters = None + + def stuffsource(self, source): + "Stuff source in the filename cache" + filename = "" % self.gid + self.gid = self.gid + 1 + lines = source.split("\n") + linecache.cache[filename] = len(source)+1, 0, lines, filename + return filename + + def prepend_syspath(self, filename): + "Prepend sys.path with file's directory if not already included" + self.runcommand("""if 1: + _filename = %r + import sys as _sys + from os.path import dirname as _dirname + _dir = _dirname(_filename) + if not _dir in _sys.path: + _sys.path.insert(0, _dir) + del _filename, _sys, _dirname, _dir + \n""" % (filename,)) + + def showsyntaxerror(self, filename=None): + """Override Interactive Interpreter method: Use Colorizing + + Color the offending position instead of printing it and pointing at it + with a caret. + + """ + tkconsole = self.tkconsole + text = tkconsole.text + text.tag_remove("ERROR", "1.0", "end") + type, value, tb = sys.exc_info() + msg = getattr(value, 'msg', '') or value or "" + lineno = getattr(value, 'lineno', '') or 1 + offset = getattr(value, 'offset', '') or 0 + if offset == 0: + lineno += 1 #mark end of offending line + if lineno == 1: + pos = "iomark + %d chars" % (offset-1) + else: + pos = "iomark linestart + %d lines + %d chars" % \ + (lineno-1, offset-1) + tkconsole.colorize_syntax_error(text, pos) + tkconsole.resetoutput() + self.write("SyntaxError: %s\n" % msg) + tkconsole.showprompt() + + def showtraceback(self): + "Extend base class method to reset output properly" + self.tkconsole.resetoutput() + self.checklinecache() + InteractiveInterpreter.showtraceback(self) + if self.tkconsole.getvar("<>"): + self.tkconsole.open_stack_viewer() + + def checklinecache(self): + c = linecache.cache + for key in list(c.keys()): + if key[:1] + key[-1:] != "<>": + del c[key] + + def runcommand(self, code): + "Run the code without invoking the debugger" + # The code better not raise an exception! + if self.tkconsole.executing: + self.display_executing_dialog() + return 0 + if self.rpcclt: + self.rpcclt.remotequeue("exec", "runcode", (code,), {}) + else: + exec(code, self.locals) + return 1 + + def runcode(self, code): + "Override base class method" + if self.tkconsole.executing: + self.interp.restart_subprocess() + self.checklinecache() + if self.save_warnings_filters is not None: + warnings.filters[:] = self.save_warnings_filters + self.save_warnings_filters = None + debugger = self.debugger + try: + self.tkconsole.beginexecuting() + if not debugger and self.rpcclt is not None: + self.active_seq = self.rpcclt.asyncqueue("exec", "runcode", + (code,), {}) + elif debugger: + debugger.run(code, self.locals) + else: + exec(code, self.locals) + except SystemExit: + if not self.tkconsole.closing: + if tkMessageBox.askyesno( + "Exit?", + "Do you want to exit altogether?", + default="yes", + parent=self.tkconsole.text): + raise + else: + self.showtraceback() + else: + raise + except: + if use_subprocess: + print("IDLE internal error in runcode()", + file=self.tkconsole.stderr) + self.showtraceback() + self.tkconsole.endexecuting() + else: + if self.tkconsole.canceled: + self.tkconsole.canceled = False + print("KeyboardInterrupt", file=self.tkconsole.stderr) + else: + self.showtraceback() + finally: + if not use_subprocess: + try: + self.tkconsole.endexecuting() + except AttributeError: # shell may have closed + pass + + def write(self, s): + "Override base class method" + return self.tkconsole.stderr.write(s) + + def display_port_binding_error(self): + tkMessageBox.showerror( + "Port Binding Error", + "IDLE can't bind to a TCP/IP port, which is necessary to " + "communicate with its Python execution server. This might be " + "because no networking is installed on this computer. " + "Run IDLE with the -n command line switch to start without a " + "subprocess and refer to Help/IDLE Help 'Running without a " + "subprocess' for further details.", + parent=self.tkconsole.text) + + def display_no_subprocess_error(self): + tkMessageBox.showerror( + "Subprocess Startup Error", + "IDLE's subprocess didn't make connection. Either IDLE can't " + "start a subprocess or personal firewall software is blocking " + "the connection.", + parent=self.tkconsole.text) + + def display_executing_dialog(self): + tkMessageBox.showerror( + "Already executing", + "The Python Shell window is already executing a command; " + "please wait until it is finished.", + parent=self.tkconsole.text) + + +class PyShell(OutputWindow): + + shell_title = "Python " + python_version() + " Shell" + + # Override classes + ColorDelegator = ModifiedColorDelegator + UndoDelegator = ModifiedUndoDelegator + + # Override menus + menu_specs = [ + ("file", "_File"), + ("edit", "_Edit"), + ("debug", "_Debug"), + ("options", "_Options"), + ("windows", "_Window"), + ("help", "_Help"), + ] + + + # New classes + from idlelib.history import History + + def __init__(self, flist=None): + if use_subprocess: + ms = self.menu_specs + if ms[2][0] != "shell": + ms.insert(2, ("shell", "She_ll")) + self.interp = ModifiedInterpreter(self) + if flist is None: + root = Tk() + fixwordbreaks(root) + root.withdraw() + flist = PyShellFileList(root) + # + OutputWindow.__init__(self, flist, None, None) + # +## self.config(usetabs=1, indentwidth=8, context_use_ps1=1) + self.usetabs = True + # indentwidth must be 8 when using tabs. See note in EditorWindow: + self.indentwidth = 8 + self.context_use_ps1 = True + # + text = self.text + text.configure(wrap="char") + text.bind("<>", self.enter_callback) + text.bind("<>", self.linefeed_callback) + text.bind("<>", self.cancel_callback) + text.bind("<>", self.eof_callback) + text.bind("<>", self.open_stack_viewer) + text.bind("<>", self.toggle_debugger) + text.bind("<>", self.toggle_jit_stack_viewer) + if use_subprocess: + text.bind("<>", self.view_restart_mark) + text.bind("<>", self.restart_shell) + # + self.save_stdout = sys.stdout + self.save_stderr = sys.stderr + self.save_stdin = sys.stdin + from idlelib import iomenu + self.stdin = PseudoInputFile(self, "stdin", iomenu.encoding) + self.stdout = PseudoOutputFile(self, "stdout", iomenu.encoding) + self.stderr = PseudoOutputFile(self, "stderr", iomenu.encoding) + self.console = PseudoOutputFile(self, "console", iomenu.encoding) + if not use_subprocess: + sys.stdout = self.stdout + sys.stderr = self.stderr + sys.stdin = self.stdin + try: + # page help() text to shell. + import pydoc # import must be done here to capture i/o rebinding. + # XXX KBK 27Dec07 use TextViewer someday, but must work w/o subproc + pydoc.pager = pydoc.plainpager + except: + sys.stderr = sys.__stderr__ + raise + # + self.history = self.History(self.text) + # + self.pollinterval = 50 # millisec + + def get_standard_extension_names(self): + return idleConf.GetExtensions(shell_only=True) + + reading = False + executing = False + canceled = False + endoffile = False + closing = False + _stop_readline_flag = False + + def set_warning_stream(self, stream): + global warning_stream + warning_stream = stream + + def get_warning_stream(self): + return warning_stream + + def toggle_debugger(self, event=None): + if self.executing: + tkMessageBox.showerror("Don't debug now", + "You can only toggle the debugger when idle", + parent=self.text) + self.set_debugger_indicator() + return "break" + else: + db = self.interp.getdebugger() + if db: + self.close_debugger() + else: + self.open_debugger() + + def set_debugger_indicator(self): + db = self.interp.getdebugger() + self.setvar("<>", not not db) + + def toggle_jit_stack_viewer(self, event=None): + pass # All we need is the variable + + def close_debugger(self): + db = self.interp.getdebugger() + if db: + self.interp.setdebugger(None) + db.close() + if self.interp.rpcclt: + debugger_r.close_remote_debugger(self.interp.rpcclt) + self.resetoutput() + self.console.write("[DEBUG OFF]\n") + sys.ps1 = ">>> " + self.showprompt() + self.set_debugger_indicator() + + def open_debugger(self): + if self.interp.rpcclt: + dbg_gui = debugger_r.start_remote_debugger(self.interp.rpcclt, + self) + else: + dbg_gui = debugger.Debugger(self) + self.interp.setdebugger(dbg_gui) + dbg_gui.load_breakpoints() + sys.ps1 = "[DEBUG ON]\n>>> " + self.showprompt() + self.set_debugger_indicator() + + def beginexecuting(self): + "Helper for ModifiedInterpreter" + self.resetoutput() + self.executing = 1 + + def endexecuting(self): + "Helper for ModifiedInterpreter" + self.executing = 0 + self.canceled = 0 + self.showprompt() + + def close(self): + "Extend EditorWindow.close()" + if self.executing: + response = tkMessageBox.askokcancel( + "Kill?", + "Your program is still running!\n Do you want to kill it?", + default="ok", + parent=self.text) + if response is False: + return "cancel" + self.stop_readline() + self.canceled = True + self.closing = True + return EditorWindow.close(self) + + def _close(self): + "Extend EditorWindow._close(), shut down debugger and execution server" + self.close_debugger() + if use_subprocess: + self.interp.kill_subprocess() + # Restore std streams + sys.stdout = self.save_stdout + sys.stderr = self.save_stderr + sys.stdin = self.save_stdin + # Break cycles + self.interp = None + self.console = None + self.flist.pyshell = None + self.history = None + EditorWindow._close(self) + + def ispythonsource(self, filename): + "Override EditorWindow method: never remove the colorizer" + return True + + def short_title(self): + return self.shell_title + + COPYRIGHT = \ + 'Type "copyright", "credits" or "license()" for more information.' + + def begin(self): + self.text.mark_set("iomark", "insert") + self.resetoutput() + if use_subprocess: + nosub = '' + client = self.interp.start_subprocess() + if not client: + self.close() + return False + else: + nosub = ("==== No Subprocess ====\n\n" + + "WARNING: Running IDLE without a Subprocess is deprecated\n" + + "and will be removed in a later version. See Help/IDLE Help\n" + + "for details.\n\n") + sys.displayhook = rpc.displayhook + + self.write("Python %s on %s\n%s\n%s" % + (sys.version, sys.platform, self.COPYRIGHT, nosub)) + self.text.focus_force() + self.showprompt() + import tkinter + tkinter._default_root = None # 03Jan04 KBK What's this? + return True + + def stop_readline(self): + if not self.reading: # no nested mainloop to exit. + return + self._stop_readline_flag = True + self.top.quit() + + def readline(self): + save = self.reading + try: + self.reading = 1 + self.top.mainloop() # nested mainloop() + finally: + self.reading = save + if self._stop_readline_flag: + self._stop_readline_flag = False + return "" + line = self.text.get("iomark", "end-1c") + if len(line) == 0: # may be EOF if we quit our mainloop with Ctrl-C + line = "\n" + self.resetoutput() + if self.canceled: + self.canceled = 0 + if not use_subprocess: + raise KeyboardInterrupt + if self.endoffile: + self.endoffile = 0 + line = "" + return line + + def isatty(self): + return True + + def cancel_callback(self, event=None): + try: + if self.text.compare("sel.first", "!=", "sel.last"): + return # Active selection -- always use default binding + except: + pass + if not (self.executing or self.reading): + self.resetoutput() + self.interp.write("KeyboardInterrupt\n") + self.showprompt() + return "break" + self.endoffile = 0 + self.canceled = 1 + if (self.executing and self.interp.rpcclt): + if self.interp.getdebugger(): + self.interp.restart_subprocess() + else: + self.interp.interrupt_subprocess() + if self.reading: + self.top.quit() # exit the nested mainloop() in readline() + return "break" + + def eof_callback(self, event): + if self.executing and not self.reading: + return # Let the default binding (delete next char) take over + if not (self.text.compare("iomark", "==", "insert") and + self.text.compare("insert", "==", "end-1c")): + return # Let the default binding (delete next char) take over + if not self.executing: + self.resetoutput() + self.close() + else: + self.canceled = 0 + self.endoffile = 1 + self.top.quit() + return "break" + + def linefeed_callback(self, event): + # Insert a linefeed without entering anything (still autoindented) + if self.reading: + self.text.insert("insert", "\n") + self.text.see("insert") + else: + self.newline_and_indent_event(event) + return "break" + + def enter_callback(self, event): + if self.executing and not self.reading: + return # Let the default binding (insert '\n') take over + # If some text is selected, recall the selection + # (but only if this before the I/O mark) + try: + sel = self.text.get("sel.first", "sel.last") + if sel: + if self.text.compare("sel.last", "<=", "iomark"): + self.recall(sel, event) + return "break" + except: + pass + # If we're strictly before the line containing iomark, recall + # the current line, less a leading prompt, less leading or + # trailing whitespace + if self.text.compare("insert", "<", "iomark linestart"): + # Check if there's a relevant stdin range -- if so, use it + prev = self.text.tag_prevrange("stdin", "insert") + if prev and self.text.compare("insert", "<", prev[1]): + self.recall(self.text.get(prev[0], prev[1]), event) + return "break" + next = self.text.tag_nextrange("stdin", "insert") + if next and self.text.compare("insert lineend", ">=", next[0]): + self.recall(self.text.get(next[0], next[1]), event) + return "break" + # No stdin mark -- just get the current line, less any prompt + indices = self.text.tag_nextrange("console", "insert linestart") + if indices and \ + self.text.compare(indices[0], "<=", "insert linestart"): + self.recall(self.text.get(indices[1], "insert lineend"), event) + else: + self.recall(self.text.get("insert linestart", "insert lineend"), event) + return "break" + # If we're between the beginning of the line and the iomark, i.e. + # in the prompt area, move to the end of the prompt + if self.text.compare("insert", "<", "iomark"): + self.text.mark_set("insert", "iomark") + # If we're in the current input and there's only whitespace + # beyond the cursor, erase that whitespace first + s = self.text.get("insert", "end-1c") + if s and not s.strip(): + self.text.delete("insert", "end-1c") + # If we're in the current input before its last line, + # insert a newline right at the insert point + if self.text.compare("insert", "<", "end-1c linestart"): + self.newline_and_indent_event(event) + return "break" + # We're in the last line; append a newline and submit it + self.text.mark_set("insert", "end-1c") + if self.reading: + self.text.insert("insert", "\n") + self.text.see("insert") + else: + self.newline_and_indent_event(event) + self.text.tag_add("stdin", "iomark", "end-1c") + self.text.update_idletasks() + if self.reading: + self.top.quit() # Break out of recursive mainloop() + else: + self.runit() + return "break" + + def recall(self, s, event): + # remove leading and trailing empty or whitespace lines + s = re.sub(r'^\s*\n', '' , s) + s = re.sub(r'\n\s*$', '', s) + lines = s.split('\n') + self.text.undo_block_start() + try: + self.text.tag_remove("sel", "1.0", "end") + self.text.mark_set("insert", "end-1c") + prefix = self.text.get("insert linestart", "insert") + if prefix.rstrip().endswith(':'): + self.newline_and_indent_event(event) + prefix = self.text.get("insert linestart", "insert") + self.text.insert("insert", lines[0].strip()) + if len(lines) > 1: + orig_base_indent = re.search(r'^([ \t]*)', lines[0]).group(0) + new_base_indent = re.search(r'^([ \t]*)', prefix).group(0) + for line in lines[1:]: + if line.startswith(orig_base_indent): + # replace orig base indentation with new indentation + line = new_base_indent + line[len(orig_base_indent):] + self.text.insert('insert', '\n'+line.rstrip()) + finally: + self.text.see("insert") + self.text.undo_block_stop() + + def runit(self): + line = self.text.get("iomark", "end-1c") + # Strip off last newline and surrounding whitespace. + # (To allow you to hit return twice to end a statement.) + i = len(line) + while i > 0 and line[i-1] in " \t": + i = i-1 + if i > 0 and line[i-1] == "\n": + i = i-1 + while i > 0 and line[i-1] in " \t": + i = i-1 + line = line[:i] + self.interp.runsource(line) + + def open_stack_viewer(self, event=None): + if self.interp.rpcclt: + return self.interp.remote_stack_viewer() + try: + sys.last_traceback + except: + tkMessageBox.showerror("No stack trace", + "There is no stack trace yet.\n" + "(sys.last_traceback is not defined)", + parent=self.text) + return + from idlelib.stackviewer import StackBrowser + StackBrowser(self.root, self.flist) + + def view_restart_mark(self, event=None): + self.text.see("iomark") + self.text.see("restart") + + def restart_shell(self, event=None): + "Callback for Run/Restart Shell Cntl-F6" + self.interp.restart_subprocess(with_cwd=True) + + def showprompt(self): + self.resetoutput() + try: + s = str(sys.ps1) + except: + s = "" + self.console.write(s) + self.text.mark_set("insert", "end-1c") + self.set_line_and_column() + self.io.reset_undo() + + def resetoutput(self): + source = self.text.get("iomark", "end-1c") + if self.history: + self.history.store(source) + if self.text.get("end-2c") != "\n": + self.text.insert("end-1c", "\n") + self.text.mark_set("iomark", "end-1c") + self.set_line_and_column() + + def write(self, s, tags=()): + if isinstance(s, str) and len(s) and max(s) > '\uffff': + # Tk doesn't support outputting non-BMP characters + # Let's assume what printed string is not very long, + # find first non-BMP character and construct informative + # UnicodeEncodeError exception. + for start, char in enumerate(s): + if char > '\uffff': + break + raise UnicodeEncodeError("UCS-2", char, start, start+1, + 'Non-BMP character not supported in Tk') + try: + self.text.mark_gravity("iomark", "right") + count = OutputWindow.write(self, s, tags, "iomark") + self.text.mark_gravity("iomark", "left") + except: + raise ###pass # ### 11Aug07 KBK if we are expecting exceptions + # let's find out what they are and be specific. + if self.canceled: + self.canceled = 0 + if not use_subprocess: + raise KeyboardInterrupt + return count + + def rmenu_check_cut(self): + try: + if self.text.compare('sel.first', '<', 'iomark'): + return 'disabled' + except TclError: # no selection, so the index 'sel.first' doesn't exist + return 'disabled' + return super().rmenu_check_cut() + + def rmenu_check_paste(self): + if self.text.compare('insert','<','iomark'): + return 'disabled' + return super().rmenu_check_paste() + +class PseudoFile(io.TextIOBase): + + def __init__(self, shell, tags, encoding=None): + self.shell = shell + self.tags = tags + self._encoding = encoding + + @property + def encoding(self): + return self._encoding + + @property + def name(self): + return '<%s>' % self.tags + + def isatty(self): + return True + + +class PseudoOutputFile(PseudoFile): + + def writable(self): + return True + + def write(self, s): + if self.closed: + raise ValueError("write to closed file") + if type(s) is not str: + if not isinstance(s, str): + raise TypeError('must be str, not ' + type(s).__name__) + # See issue #19481 + s = str.__str__(s) + return self.shell.write(s, self.tags) + + +class PseudoInputFile(PseudoFile): + + def __init__(self, shell, tags, encoding=None): + PseudoFile.__init__(self, shell, tags, encoding) + self._line_buffer = '' + + def readable(self): + return True + + def read(self, size=-1): + if self.closed: + raise ValueError("read from closed file") + if size is None: + size = -1 + elif not isinstance(size, int): + raise TypeError('must be int, not ' + type(size).__name__) + result = self._line_buffer + self._line_buffer = '' + if size < 0: + while True: + line = self.shell.readline() + if not line: break + result += line + else: + while len(result) < size: + line = self.shell.readline() + if not line: break + result += line + self._line_buffer = result[size:] + result = result[:size] + return result + + def readline(self, size=-1): + if self.closed: + raise ValueError("read from closed file") + if size is None: + size = -1 + elif not isinstance(size, int): + raise TypeError('must be int, not ' + type(size).__name__) + line = self._line_buffer or self.shell.readline() + if size < 0: + size = len(line) + eol = line.find('\n', 0, size) + if eol >= 0: + size = eol + 1 + self._line_buffer = line[size:] + return line[:size] + + def close(self): + self.shell.close() + + +def fix_x11_paste(root): + "Make paste replace selection on x11. See issue #5124." + if root._windowingsystem == 'x11': + for cls in 'Text', 'Entry', 'Spinbox': + root.bind_class( + cls, + '<>', + 'catch {%W delete sel.first sel.last}\n' + + root.bind_class(cls, '<>')) + + +usage_msg = """\ + +USAGE: idle [-deins] [-t title] [file]* + idle [-dns] [-t title] (-c cmd | -r file) [arg]* + idle [-dns] [-t title] - [arg]* + + -h print this help message and exit + -n run IDLE without a subprocess (DEPRECATED, + see Help/IDLE Help for details) + +The following options will override the IDLE 'settings' configuration: + + -e open an edit window + -i open a shell window + +The following options imply -i and will open a shell: + + -c cmd run the command in a shell, or + -r file run script from file + + -d enable the debugger + -s run $IDLESTARTUP or $PYTHONSTARTUP before anything else + -t title set title of shell window + +A default edit window will be bypassed when -c, -r, or - are used. + +[arg]* are passed to the command (-c) or script (-r) in sys.argv[1:]. + +Examples: + +idle + Open an edit window or shell depending on IDLE's configuration. + +idle foo.py foobar.py + Edit the files, also open a shell if configured to start with shell. + +idle -est "Baz" foo.py + Run $IDLESTARTUP or $PYTHONSTARTUP, edit foo.py, and open a shell + window with the title "Baz". + +idle -c "import sys; print(sys.argv)" "foo" + Open a shell window and run the command, passing "-c" in sys.argv[0] + and "foo" in sys.argv[1]. + +idle -d -s -r foo.py "Hello World" + Open a shell window, run a startup script, enable the debugger, and + run foo.py, passing "foo.py" in sys.argv[0] and "Hello World" in + sys.argv[1]. + +echo "import sys; print(sys.argv)" | idle - "foobar" + Open a shell window, run the script piped in, passing '' in sys.argv[0] + and "foobar" in sys.argv[1]. +""" + +def main(): + global flist, root, use_subprocess + + capture_warnings(True) + use_subprocess = True + enable_shell = False + enable_edit = False + debug = False + cmd = None + script = None + startup = False + try: + opts, args = getopt.getopt(sys.argv[1:], "c:deihnr:st:") + except getopt.error as msg: + print("Error: %s\n%s" % (msg, usage_msg), file=sys.stderr) + sys.exit(2) + for o, a in opts: + if o == '-c': + cmd = a + enable_shell = True + if o == '-d': + debug = True + enable_shell = True + if o == '-e': + enable_edit = True + if o == '-h': + sys.stdout.write(usage_msg) + sys.exit() + if o == '-i': + enable_shell = True + if o == '-n': + print(" Warning: running IDLE without a subprocess is deprecated.", + file=sys.stderr) + use_subprocess = False + if o == '-r': + script = a + if os.path.isfile(script): + pass + else: + print("No script file: ", script) + sys.exit() + enable_shell = True + if o == '-s': + startup = True + enable_shell = True + if o == '-t': + PyShell.shell_title = a + enable_shell = True + if args and args[0] == '-': + cmd = sys.stdin.read() + enable_shell = True + # process sys.argv and sys.path: + for i in range(len(sys.path)): + sys.path[i] = os.path.abspath(sys.path[i]) + if args and args[0] == '-': + sys.argv = [''] + args[1:] + elif cmd: + sys.argv = ['-c'] + args + elif script: + sys.argv = [script] + args + elif args: + enable_edit = True + pathx = [] + for filename in args: + pathx.append(os.path.dirname(filename)) + for dir in pathx: + dir = os.path.abspath(dir) + if not dir in sys.path: + sys.path.insert(0, dir) + else: + dir = os.getcwd() + if dir not in sys.path: + sys.path.insert(0, dir) + # check the IDLE settings configuration (but command line overrides) + edit_start = idleConf.GetOption('main', 'General', + 'editor-on-startup', type='bool') + enable_edit = enable_edit or edit_start + enable_shell = enable_shell or not enable_edit + + # start editor and/or shell windows: + root = Tk(className="Idle") + root.withdraw() + + # set application icon + icondir = os.path.join(os.path.dirname(__file__), 'Icons') + if system() == 'Windows': + iconfile = os.path.join(icondir, 'idle.ico') + root.wm_iconbitmap(default=iconfile) + else: + ext = '.png' if TkVersion >= 8.6 else '.gif' + iconfiles = [os.path.join(icondir, 'idle_%d%s' % (size, ext)) + for size in (16, 32, 48)] + icons = [PhotoImage(file=iconfile) for iconfile in iconfiles] + root.wm_iconphoto(True, *icons) + + fixwordbreaks(root) + fix_x11_paste(root) + flist = PyShellFileList(root) + macosx.setupApp(root, flist) + + if enable_edit: + if not (cmd or script): + for filename in args[:]: + if flist.open(filename) is None: + # filename is a directory actually, disconsider it + args.remove(filename) + if not args: + flist.new() + + if enable_shell: + shell = flist.open_shell() + if not shell: + return # couldn't open shell + if macosx.isAquaTk() and flist.dict: + # On OSX: when the user has double-clicked on a file that causes + # IDLE to be launched the shell window will open just in front of + # the file she wants to see. Lower the interpreter window when + # there are open files. + shell.top.lower() + else: + shell = flist.pyshell + + # Handle remaining options. If any of these are set, enable_shell + # was set also, so shell must be true to reach here. + if debug: + shell.open_debugger() + if startup: + filename = os.environ.get("IDLESTARTUP") or \ + os.environ.get("PYTHONSTARTUP") + if filename and os.path.isfile(filename): + shell.interp.execfile(filename) + if cmd or script: + shell.interp.runcommand("""if 1: + import sys as _sys + _sys.argv = %r + del _sys + \n""" % (sys.argv,)) + if cmd: + shell.interp.execsource(cmd) + elif script: + shell.interp.prepend_syspath(script) + shell.interp.execfile(script) + elif shell: + # If there is a shell window and no cmd or script in progress, + # check for problematic OS X Tk versions and print a warning + # message in the IDLE shell window; this is less intrusive + # than always opening a separate window. + tkversionwarning = macosx.tkVersionWarning(root) + if tkversionwarning: + shell.interp.runcommand("print('%s')" % tkversionwarning) + + while flist.inversedict: # keep IDLE running while files are open. + root.mainloop() + root.destroy() + capture_warnings(False) + +if __name__ == "__main__": + sys.modules['pyshell'] = sys.modules['__main__'] + main() + +capture_warnings(False) # Make sure turned off; see issue 18081 -- cgit v1.2.1 From 6a1ca1bba00db090793f4d915684430cd197a980 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Sat, 11 Jun 2016 02:57:56 -0400 Subject: Issue #27262: fix missing parameter typo --- Lib/idlelib/macosx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/idlelib/macosx.py b/Lib/idlelib/macosx.py index b16e0523c0..98d7887b5c 100644 --- a/Lib/idlelib/macosx.py +++ b/Lib/idlelib/macosx.py @@ -238,4 +238,4 @@ def setupApp(root, flist): hideTkConsole(root) overrideRootMenu(root, flist) addOpenEventSupport(root, flist) - fixb2context() + fixb2context(root) -- cgit v1.2.1 From 747dd5b7e0e6a3ac411c8b18e828c7189390a0d5 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Sat, 11 Jun 2016 04:36:34 -0400 Subject: IDLE NEWS entries --- Lib/idlelib/NEWS.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'Lib') diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index 3fc21e00da..68a555c993 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -6,6 +6,17 @@ What's New in IDLE 3.6.0? This matches how paste works on Windows, Mac, most modern Linux apps, and ttk widgets. Original patch by Serhiy Storchaka. +- Issue #24750: Switch all scrollbars in IDLE to ttk versions. + Where needed, minimal tests are added to cover changes. + +- Issue #24759: IDLE requires tk 8.5 and availability ttk widgets. + Delete now unneeded tk version tests and code for older versions. + Add test for IDLE syntax colorizer. + +- Issue #27239: idlelib.macosx.isXyzTk functions initialize as needed. + +- Issue #27262: move Aqua unbinding code, which enable context menus, to maxosx. + - Issue #24759: Make clear in idlelib.idle_test.__init__ that the directory is a private implementation of test.test_idle and tool for maintainers. -- cgit v1.2.1 From b8960afa32d12f1475abff578ffd128663c15f7c Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 11 Jun 2016 19:15:00 +0300 Subject: Issue #27030: Unknown escapes consisting of ``'\'`` and ASCII letter in regular expressions now are errors. --- Lib/sre_parse.py | 49 +++++-------------------------------------------- Lib/test/test_re.py | 41 +++++++++++++---------------------------- 2 files changed, 18 insertions(+), 72 deletions(-) (limited to 'Lib') diff --git a/Lib/sre_parse.py b/Lib/sre_parse.py index 4ff50d1006..521e379e72 100644 --- a/Lib/sre_parse.py +++ b/Lib/sre_parse.py @@ -282,33 +282,6 @@ class Tokenizer: def error(self, msg, offset=0): return error(msg, self.string, self.tell() - offset) -# The following three functions are not used in this module anymore, but we keep -# them here (with DeprecationWarnings) for backwards compatibility. - -def isident(char): - import warnings - warnings.warn('sre_parse.isident() will be removed in 3.5', - DeprecationWarning, stacklevel=2) - return "a" <= char <= "z" or "A" <= char <= "Z" or char == "_" - -def isdigit(char): - import warnings - warnings.warn('sre_parse.isdigit() will be removed in 3.5', - DeprecationWarning, stacklevel=2) - return "0" <= char <= "9" - -def isname(name): - import warnings - warnings.warn('sre_parse.isname() will be removed in 3.5', - DeprecationWarning, stacklevel=2) - # check that group name is a valid string - if not isident(name[0]): - return False - for char in name[1:]: - if not isident(char) and not isdigit(char): - return False - return True - def _class_escape(source, escape): # handle escape code inside character class code = ESCAPES.get(escape) @@ -351,9 +324,7 @@ def _class_escape(source, escape): raise ValueError if len(escape) == 2: if c in ASCIILETTERS: - import warnings - warnings.warn('bad escape %s' % escape, - DeprecationWarning, stacklevel=8) + raise source.error('bad escape %s' % escape, len(escape)) return LITERAL, ord(escape[1]) except ValueError: pass @@ -418,9 +389,7 @@ def _escape(source, escape, state): raise source.error("invalid group reference", len(escape)) if len(escape) == 2: if c in ASCIILETTERS: - import warnings - warnings.warn('bad escape %s' % escape, - DeprecationWarning, stacklevel=8) + raise source.error("bad escape %s" % escape, len(escape)) return LITERAL, ord(escape[1]) except ValueError: pass @@ -798,10 +767,7 @@ def fix_flags(src, flags): # Check and fix flags according to the type of pattern (str or bytes) if isinstance(src, str): if flags & SRE_FLAG_LOCALE: - import warnings - warnings.warn("LOCALE flag with a str pattern is deprecated. " - "Will be an error in 3.6", - DeprecationWarning, stacklevel=6) + raise ValueError("cannot use LOCALE flag with a str pattern") if not flags & SRE_FLAG_ASCII: flags |= SRE_FLAG_UNICODE elif flags & SRE_FLAG_UNICODE: @@ -810,10 +776,7 @@ def fix_flags(src, flags): if flags & SRE_FLAG_UNICODE: raise ValueError("cannot use UNICODE flag with a bytes pattern") if flags & SRE_FLAG_LOCALE and flags & SRE_FLAG_ASCII: - import warnings - warnings.warn("ASCII and LOCALE flags are incompatible. " - "Will be an error in 3.6", - DeprecationWarning, stacklevel=6) + raise ValueError("ASCII and LOCALE flags are incompatible") return flags def parse(str, flags=0, pattern=None): @@ -914,9 +877,7 @@ def parse_template(source, pattern): this = chr(ESCAPES[this][1]) except KeyError: if c in ASCIILETTERS: - import warnings - warnings.warn('bad escape %s' % this, - DeprecationWarning, stacklevel=4) + raise s.error('bad escape %s' % this, len(this)) lappend(this) else: lappend(this) diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index 7a741416b4..e27591c4fc 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -124,7 +124,7 @@ class ReTests(unittest.TestCase): (chr(9)+chr(10)+chr(11)+chr(13)+chr(12)+chr(7)+chr(8))) for c in 'cdehijklmopqsuwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ': with self.subTest(c): - with self.assertWarns(DeprecationWarning): + with self.assertRaises(re.error): self.assertEqual(re.sub('a', '\\' + c, 'a'), '\\' + c) self.assertEqual(re.sub('^\s*', 'X', 'test'), 'Xtest') @@ -633,14 +633,10 @@ class ReTests(unittest.TestCase): re.purge() # for warnings for c in 'ceghijklmopqyzCEFGHIJKLMNOPQRTVXY': with self.subTest(c): - with self.assertWarns(DeprecationWarning): - self.assertEqual(re.fullmatch('\\%c' % c, c).group(), c) - self.assertIsNone(re.match('\\%c' % c, 'a')) + self.assertRaises(re.error, re.compile, '\\%c' % c) for c in 'ceghijklmopqyzABCEFGHIJKLMNOPQRTVXYZ': with self.subTest(c): - with self.assertWarns(DeprecationWarning): - self.assertEqual(re.fullmatch('[\\%c]' % c, c).group(), c) - self.assertIsNone(re.match('[\\%c]' % c, 'a')) + self.assertRaises(re.error, re.compile, '[\\%c]' % c) def test_string_boundaries(self): # See http://bugs.python.org/issue10713 @@ -993,10 +989,8 @@ class ReTests(unittest.TestCase): self.assertTrue(re.match((r"\x%02x" % i).encode(), bytes([i]))) self.assertTrue(re.match((r"\x%02x0" % i).encode(), bytes([i])+b"0")) self.assertTrue(re.match((r"\x%02xz" % i).encode(), bytes([i])+b"z")) - with self.assertWarns(DeprecationWarning): - self.assertTrue(re.match(br"\u1234", b'u1234')) - with self.assertWarns(DeprecationWarning): - self.assertTrue(re.match(br"\U00012345", b'U00012345')) + self.assertRaises(re.error, re.compile, br"\u1234") + self.assertRaises(re.error, re.compile, br"\U00012345") self.assertTrue(re.match(br"\0", b"\000")) self.assertTrue(re.match(br"\08", b"\0008")) self.assertTrue(re.match(br"\01", b"\001")) @@ -1018,10 +1012,8 @@ class ReTests(unittest.TestCase): self.assertTrue(re.match((r"[\x%02x]" % i).encode(), bytes([i]))) self.assertTrue(re.match((r"[\x%02x0]" % i).encode(), bytes([i]))) self.assertTrue(re.match((r"[\x%02xz]" % i).encode(), bytes([i]))) - with self.assertWarns(DeprecationWarning): - self.assertTrue(re.match(br"[\u1234]", b'u')) - with self.assertWarns(DeprecationWarning): - self.assertTrue(re.match(br"[\U00012345]", b'U')) + self.assertRaises(re.error, re.compile, br"[\u1234]") + self.assertRaises(re.error, re.compile, br"[\U00012345]") self.checkPatternError(br"[\567]", r'octal escape value \567 outside of ' r'range 0-0o377', 1) @@ -1363,12 +1355,12 @@ class ReTests(unittest.TestCase): if bletter: self.assertIsNone(pat.match(bletter)) # Incompatibilities - self.assertWarns(DeprecationWarning, re.compile, '', re.LOCALE) - self.assertWarns(DeprecationWarning, re.compile, '(?L)') - self.assertWarns(DeprecationWarning, re.compile, b'', re.LOCALE | re.ASCII) - self.assertWarns(DeprecationWarning, re.compile, b'(?L)', re.ASCII) - self.assertWarns(DeprecationWarning, re.compile, b'(?a)', re.LOCALE) - self.assertWarns(DeprecationWarning, re.compile, b'(?aL)') + self.assertRaises(ValueError, re.compile, '', re.LOCALE) + self.assertRaises(ValueError, re.compile, '(?L)') + self.assertRaises(ValueError, re.compile, b'', re.LOCALE | re.ASCII) + self.assertRaises(ValueError, re.compile, b'(?L)', re.ASCII) + self.assertRaises(ValueError, re.compile, b'(?a)', re.LOCALE) + self.assertRaises(ValueError, re.compile, b'(?aL)') def test_bug_6509(self): # Replacement strings of both types must parse properly. @@ -1419,13 +1411,6 @@ class ReTests(unittest.TestCase): # Test behaviour when not given a string or pattern as parameter self.assertRaises(TypeError, re.compile, 0) - def test_bug_13899(self): - # Issue #13899: re pattern r"[\A]" should work like "A" but matches - # nothing. Ditto B and Z. - with self.assertWarns(DeprecationWarning): - self.assertEqual(re.findall(r'[\A\B\b\C\Z]', 'AB\bCZ'), - ['A', 'B', '\b', 'C', 'Z']) - @bigmemtest(size=_2G, memuse=1) def test_large_search(self, size): # Issue #10182: indices were 32-bit-truncated. -- cgit v1.2.1 From fe9b61ceff66fe071a6fc0c7337eab4db2a6f416 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 11 Jun 2016 19:32:44 +0300 Subject: Issue #27029: Removed deprecated support of universal newlines mode from ZipFile.open(). --- Lib/test/test_zipfile.py | 167 ++--------------------------------------------- Lib/zipfile.py | 50 ++------------ 2 files changed, 8 insertions(+), 209 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index 6a77d6c91f..ef3c3d8d67 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -38,10 +38,6 @@ def get_files(test): yield f test.assertFalse(f.closed) -def openU(zipfp, fn): - with check_warnings(('', DeprecationWarning)): - return zipfp.open(fn, 'rU') - class AbstractTestsWithSourceFile: @classmethod def setUpClass(cls): @@ -1035,32 +1031,6 @@ class OtherTests(unittest.TestCase): data += zipfp.read(info) self.assertIn(data, {b"foobar", b"barfoo"}) - def test_universal_deprecation(self): - f = io.BytesIO() - with zipfile.ZipFile(f, "w") as zipfp: - zipfp.writestr('spam.txt', b'ababagalamaga') - - with zipfile.ZipFile(f, "r") as zipfp: - for mode in 'U', 'rU': - with self.assertWarns(DeprecationWarning): - zipopen = zipfp.open('spam.txt', mode) - zipopen.close() - - def test_universal_readaheads(self): - f = io.BytesIO() - - data = b'a\r\n' * 16 * 1024 - with zipfile.ZipFile(f, 'w', zipfile.ZIP_STORED) as zipfp: - zipfp.writestr(TESTFN, data) - - data2 = b'' - with zipfile.ZipFile(f, 'r') as zipfp, \ - openU(zipfp, TESTFN) as zipopen: - for line in zipopen: - data2 += line - - self.assertEqual(data, data2.replace(b'\n', b'\r\n')) - def test_writestr_extended_local_header_issue1202(self): with zipfile.ZipFile(TESTFN2, 'w') as orig_zip: for data in 'abcdefghijklmnop': @@ -1268,9 +1238,12 @@ class OtherTests(unittest.TestCase): zipf.writestr("foo.txt", "O, for a Muse of Fire!") with zipfile.ZipFile(TESTFN, mode="r") as zipf: - # read the data to make sure the file is there + # read the data to make sure the file is there zipf.read("foo.txt") self.assertRaises(RuntimeError, zipf.open, "foo.txt", "q") + # universal newlines support is removed + self.assertRaises(RuntimeError, zipf.open, "foo.txt", "U") + self.assertRaises(RuntimeError, zipf.open, "foo.txt", "rU") def test_read0(self): """Check that calling read(0) on a ZipExtFile object returns an empty @@ -2011,138 +1984,6 @@ class TestWithDirectory(unittest.TestCase): unlink(TESTFN) -class AbstractUniversalNewlineTests: - @classmethod - def setUpClass(cls): - cls.line_gen = [bytes("Test of zipfile line %d." % i, "ascii") - for i in range(FIXEDTEST_SIZE)] - cls.seps = (b'\r', b'\r\n', b'\n') - cls.arcdata = {} - for n, s in enumerate(cls.seps): - cls.arcdata[s] = s.join(cls.line_gen) + s - - def setUp(self): - self.arcfiles = {} - for n, s in enumerate(self.seps): - self.arcfiles[s] = '%s-%d' % (TESTFN, n) - with open(self.arcfiles[s], "wb") as f: - f.write(self.arcdata[s]) - - def make_test_archive(self, f, compression): - # Create the ZIP archive - with zipfile.ZipFile(f, "w", compression) as zipfp: - for fn in self.arcfiles.values(): - zipfp.write(fn, fn) - - def read_test(self, f, compression): - self.make_test_archive(f, compression) - - # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp: - for sep, fn in self.arcfiles.items(): - with openU(zipfp, fn) as fp: - zipdata = fp.read() - self.assertEqual(self.arcdata[sep], zipdata) - - def test_read(self): - for f in get_files(self): - self.read_test(f, self.compression) - - def readline_read_test(self, f, compression): - self.make_test_archive(f, compression) - - # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp: - for sep, fn in self.arcfiles.items(): - with openU(zipfp, fn) as zipopen: - data = b'' - while True: - read = zipopen.readline() - if not read: - break - data += read - - read = zipopen.read(5) - if not read: - break - data += read - - self.assertEqual(data, self.arcdata[b'\n']) - - def test_readline_read(self): - for f in get_files(self): - self.readline_read_test(f, self.compression) - - def readline_test(self, f, compression): - self.make_test_archive(f, compression) - - # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp: - for sep, fn in self.arcfiles.items(): - with openU(zipfp, fn) as zipopen: - for line in self.line_gen: - linedata = zipopen.readline() - self.assertEqual(linedata, line + b'\n') - - def test_readline(self): - for f in get_files(self): - self.readline_test(f, self.compression) - - def readlines_test(self, f, compression): - self.make_test_archive(f, compression) - - # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp: - for sep, fn in self.arcfiles.items(): - with openU(zipfp, fn) as fp: - ziplines = fp.readlines() - for line, zipline in zip(self.line_gen, ziplines): - self.assertEqual(zipline, line + b'\n') - - def test_readlines(self): - for f in get_files(self): - self.readlines_test(f, self.compression) - - def iterlines_test(self, f, compression): - self.make_test_archive(f, compression) - - # Read the ZIP archive - with zipfile.ZipFile(f, "r") as zipfp: - for sep, fn in self.arcfiles.items(): - with openU(zipfp, fn) as fp: - for line, zipline in zip(self.line_gen, fp): - self.assertEqual(zipline, line + b'\n') - - def test_iterlines(self): - for f in get_files(self): - self.iterlines_test(f, self.compression) - - def tearDown(self): - for sep, fn in self.arcfiles.items(): - unlink(fn) - unlink(TESTFN) - unlink(TESTFN2) - - -class StoredUniversalNewlineTests(AbstractUniversalNewlineTests, - unittest.TestCase): - compression = zipfile.ZIP_STORED - -@requires_zlib -class DeflateUniversalNewlineTests(AbstractUniversalNewlineTests, - unittest.TestCase): - compression = zipfile.ZIP_DEFLATED - -@requires_bz2 -class Bzip2UniversalNewlineTests(AbstractUniversalNewlineTests, - unittest.TestCase): - compression = zipfile.ZIP_BZIP2 - -@requires_lzma -class LzmaUniversalNewlineTests(AbstractUniversalNewlineTests, - unittest.TestCase): - compression = zipfile.ZIP_LZMA - class ZipInfoTests(unittest.TestCase): def test_from_file(self): zi = zipfile.ZipInfo.from_file(__file__) diff --git a/Lib/zipfile.py b/Lib/zipfile.py index 27a4c713e7..8dd064a2c6 100644 --- a/Lib/zipfile.py +++ b/Lib/zipfile.py @@ -743,9 +743,6 @@ class ZipExtFile(io.BufferedIOBase): # Read from compressed files in 4k blocks. MIN_READ_SIZE = 4096 - # Search for universal newlines or line chunks. - PATTERN = re.compile(br'^(?P[^\r\n]+)|(?P\n|\r\n?)') - def __init__(self, fileobj, mode, zipinfo, decrypter=None, close_fileobj=False): self._fileobj = fileobj @@ -762,7 +759,6 @@ class ZipExtFile(io.BufferedIOBase): self._readbuffer = b'' self._offset = 0 - self._universal = 'U' in mode self.newlines = None # Adjust read size for encrypted files since the first 12 bytes @@ -799,7 +795,7 @@ class ZipExtFile(io.BufferedIOBase): If limit is specified, at most limit bytes will be read. """ - if not self._universal and limit < 0: + if limit < 0: # Shortcut common case - newline found in buffer. i = self._readbuffer.find(b'\n', self._offset) + 1 if i > 0: @@ -807,41 +803,7 @@ class ZipExtFile(io.BufferedIOBase): self._offset = i return line - if not self._universal: - return io.BufferedIOBase.readline(self, limit) - - line = b'' - while limit < 0 or len(line) < limit: - readahead = self.peek(2) - if readahead == b'': - return line - - # - # Search for universal newlines or line chunks. - # - # The pattern returns either a line chunk or a newline, but not - # both. Combined with peek(2), we are assured that the sequence - # '\r\n' is always retrieved completely and never split into - # separate newlines - '\r', '\n' due to coincidental readaheads. - # - match = self.PATTERN.search(readahead) - newline = match.group('newline') - if newline is not None: - if self.newlines is None: - self.newlines = [] - if newline not in self.newlines: - self.newlines.append(newline) - self._offset += len(newline) - return line + b'\n' - - chunk = match.group('chunk') - if limit >= 0: - chunk = chunk[: limit - len(line)] - - self._offset += len(chunk) - line += chunk - - return line + return io.BufferedIOBase.readline(self, limit) def peek(self, n=1): """Returns buffered bytes without advancing the position.""" @@ -1360,12 +1322,8 @@ class ZipFile: files. If the size is known in advance, it is best to pass a ZipInfo instance for name, with zinfo.file_size set. """ - if mode not in {"r", "w", "U", "rU"}: - raise RuntimeError('open() requires mode "r", "w", "U", or "rU"') - if 'U' in mode: - import warnings - warnings.warn("'U' mode is deprecated", - DeprecationWarning, 2) + if mode not in {"r", "w"}: + raise RuntimeError('open() requires mode "r" or "w"') if pwd and not isinstance(pwd, bytes): raise TypeError("pwd: expected bytes, got %s" % type(pwd)) if pwd and (mode == "w"): -- cgit v1.2.1 From 075b2e415866f63240800f23433414265484c410 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Sat, 11 Jun 2016 22:30:05 +0300 Subject: Issue #20508: Improve exception message of IPv{4,6}Network.__getitem__ Patch by Gareth Rees. --- Lib/ipaddress.py | 4 ++-- Lib/test/test_ipaddress.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py index 1f90058382..20f33cbdeb 100644 --- a/Lib/ipaddress.py +++ b/Lib/ipaddress.py @@ -636,12 +636,12 @@ class _BaseNetwork(_IPAddressBase): broadcast = int(self.broadcast_address) if n >= 0: if network + n > broadcast: - raise IndexError + raise IndexError('address out of range') return self._address_class(network + n) else: n += 1 if broadcast + n < network: - raise IndexError + raise IndexError('address out of range') return self._address_class(broadcast + n) def __lt__(self, other): diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py index be62fadf0d..6c08f805da 100644 --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -1176,6 +1176,7 @@ class IpaddrUnitTest(unittest.TestCase): self.assertEqual(str(self.ipv6_network[5]), '2001:658:22a:cafe::5') + self.assertRaises(IndexError, self.ipv6_network.__getitem__, 1 << 64) def testGetitem(self): # http://code.google.com/p/ipaddr-py/issues/detail?id=15 -- cgit v1.2.1 From ec39bddd7533d909f2e4679be596d950edcf05a7 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 12 Jun 2016 00:19:44 +0300 Subject: Issue #27294: Improved repr for Tkinter event objects. --- Lib/tkinter/__init__.py | 75 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index 174e04e89e..cfb5268622 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -30,6 +30,7 @@ button.pack(side=BOTTOM) tk.mainloop() """ +import enum import sys import _tkinter # If this fails your Python may not be configured for Tk @@ -132,6 +133,50 @@ def _splitdict(tk, v, cut_minus=True, conv=None): dict[key] = value return dict + +class EventType(str, enum.Enum): + KeyPress = '2' + Key = KeyPress, + KeyRelease = '3' + ButtonPress = '4' + Button = ButtonPress, + ButtonRelease = '5' + Motion = '6' + Enter = '7' + Leave = '8' + FocusIn = '9' + FocusOut = '10' + Keymap = '11' # undocumented + Expose = '12' + GraphicsExpose = '13' # undocumented + NoExpose = '14' # undocumented + Visibility = '15' + Create = '16' + Destroy = '17' + Unmap = '18' + Map = '19' + MapRequest = '20' + Reparent = '21' + Configure = '22' + ConfigureRequest = '23' + Gravity = '24' + ResizeRequest = '25' + Circulate = '26' + CirculateRequest = '27' + Property = '28' + SelectionClear = '29' # undocumented + SelectionRequest = '30' # undocumented + Selection = '31' # undocumented + Colormap = '32' + ClientMessage = '33' # undocumented + Mapping = '34' # undocumented + VirtualEvent = '35', # undocumented + Activate = '36', + Deactivate = '37', + MouseWheel = '38', + def __str__(self): + return self.name + class Event: """Container for the properties of an event. @@ -174,7 +219,30 @@ class Event: widget - widget in which the event occurred delta - delta of wheel movement (MouseWheel) """ - pass + def __repr__(self): + state = {k: v for k, v in self.__dict__.items() if v != '??'} + if not self.char: + del state['char'] + elif self.char != '??': + state['char'] = repr(self.char) + if not getattr(self, 'send_event', True): + del state['send_event'] + if self.state == 0: + del state['state'] + if self.delta == 0: + del state['delta'] + # widget usually is known + # serial and time are not very interesing + # keysym_num duplicates keysym + # x_root and y_root mostly duplicate x and y + keys = ('send_event', + 'state', 'keycode', 'char', 'keysym', + 'num', 'delta', 'focus', + 'x', 'y', 'width', 'height') + return '<%s event%s>' % ( + self.type, + ''.join(' %s=%s' % (k, state[k]) for k in keys if k in state) + ) _support_default_root = 1 _default_root = None @@ -1271,7 +1339,10 @@ class Misc: except TclError: pass e.keysym = K e.keysym_num = getint_event(N) - e.type = T + try: + e.type = EventType(T) + except ValueError: + e.type = T try: e.widget = self._nametowidget(W) except KeyError: -- cgit v1.2.1 From 5486e94c1ad7342d7a892631cc8e7ff33a022ae8 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 12 Jun 2016 00:39:41 +0300 Subject: Issue #27140: Added BUILD_CONST_KEY_MAP opcode. --- Lib/importlib/_bootstrap_external.py | 3 ++- Lib/opcode.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 034b783990..b05281f471 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -226,6 +226,7 @@ _code_type = type(_write_atomic.__code__) # Python 3.6a0 3360 (add FORMAT_VALUE opcode #25483 # Python 3.6a0 3361 (lineno delta of code.co_lnotab becomes signed) # Python 3.6a0 3370 (16 bit wordcode) +# Python 3.6a0 3371 (add BUILD_CONST_KEY_MAP opcode #27140) # # MAGIC must change whenever the bytecode emitted by the compiler may no # longer be understood by older implementations of the eval loop (usually @@ -234,7 +235,7 @@ _code_type = type(_write_atomic.__code__) # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # in PC/launcher.c must also be updated. -MAGIC_NUMBER = (3370).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3371).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c _PYCACHE = '__pycache__' diff --git a/Lib/opcode.py b/Lib/opcode.py index ecc24bb165..2303407923 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -213,5 +213,6 @@ def_op('BUILD_TUPLE_UNPACK', 152) def_op('BUILD_SET_UNPACK', 153) def_op('FORMAT_VALUE', 155) +def_op('BUILD_CONST_KEY_MAP', 156) del def_op, name_op, jrel_op, jabs_op -- cgit v1.2.1 From 3a9cf1568b8063cd4c1c9168ee4aa7e15ffc131d Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Sun, 12 Jun 2016 01:56:50 +0000 Subject: Drop unused import --- Lib/_pyio.py | 1 - 1 file changed, 1 deletion(-) (limited to 'Lib') diff --git a/Lib/_pyio.py b/Lib/_pyio.py index 40df79d345..d0947f06d5 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -6,7 +6,6 @@ import os import abc import codecs import errno -import array import stat import sys # Import _thread instead of threading to reduce startup cost -- cgit v1.2.1 From e5585ad97cb7527c73b3b758df87c8b982ceb8de Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 12 Jun 2016 17:02:10 +0300 Subject: Comment fixes extracted from patch by Demur Rumed. --- Lib/importlib/_bootstrap_external.py | 4 ---- 1 file changed, 4 deletions(-) (limited to 'Lib') diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 18aec067fb..6fe3643601 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -130,10 +130,6 @@ _code_type = type(_write_atomic.__code__) # a .pyc file in text mode the magic number will be wrong; also, the # Apple MPW compiler swaps their values, botching string constants. # -# The magic numbers must be spaced apart at least 2 values, as the -# -U interpeter flag will cause MAGIC+1 being used. They have been -# odd numbers for some time now. -# # There were a variety of old schemes for setting the magic number. # The current working scheme is to increment the previous value by # 10. -- cgit v1.2.1 From fa60de800763bc0f7b70d67ca749fce15d2d53de Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 12 Jun 2016 17:36:24 +0300 Subject: Issue #27095: Simplified MAKE_FUNCTION and removed MAKE_CLOSURE opcodes. Patch by Demur Rumed. --- Lib/importlib/_bootstrap_external.py | 8 +-- Lib/lib2to3/tests/data/py3_test_grammar.py | 2 +- Lib/opcode.py | 3 +- Lib/test/test_dis.py | 80 +++++++++++++++--------------- Lib/test/test_grammar.py | 2 +- 5 files changed, 47 insertions(+), 48 deletions(-) (limited to 'Lib') diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 6fe3643601..30e833044d 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -222,8 +222,10 @@ _code_type = type(_write_atomic.__code__) # Python 3.5.2 3351 (fix BUILD_MAP_UNPACK_WITH_CALL opcode #27286) # Python 3.6a0 3360 (add FORMAT_VALUE opcode #25483 # Python 3.6a0 3361 (lineno delta of code.co_lnotab becomes signed) -# Python 3.6a0 3370 (16 bit wordcode) -# Python 3.6a0 3371 (add BUILD_CONST_KEY_MAP opcode #27140) +# Python 3.6a1 3370 (16 bit wordcode) +# Python 3.6a1 3371 (add BUILD_CONST_KEY_MAP opcode #27140) +# Python 3.6a1 3372 (MAKE_FUNCTION simplification, remove MAKE_CLOSURE + #27095) # # MAGIC must change whenever the bytecode emitted by the compiler may no # longer be understood by older implementations of the eval loop (usually @@ -232,7 +234,7 @@ _code_type = type(_write_atomic.__code__) # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # in PC/launcher.c must also be updated. -MAGIC_NUMBER = (3371).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3372).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c _PYCACHE = '__pycache__' diff --git a/Lib/lib2to3/tests/data/py3_test_grammar.py b/Lib/lib2to3/tests/data/py3_test_grammar.py index c0bf7f27aa..cf31a5411a 100644 --- a/Lib/lib2to3/tests/data/py3_test_grammar.py +++ b/Lib/lib2to3/tests/data/py3_test_grammar.py @@ -319,7 +319,7 @@ class GrammarTests(unittest.TestCase): def f(x) -> list: pass self.assertEquals(f.__annotations__, {'return': list}) - # test MAKE_CLOSURE with a variety of oparg's + # test closures with a variety of oparg's closure = 1 def f(): return closure def f(x=1): return closure diff --git a/Lib/opcode.py b/Lib/opcode.py index 2303407923..064081981d 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -173,9 +173,8 @@ haslocal.append(126) def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3) def_op('CALL_FUNCTION', 131) # #args + (#kwargs << 8) hasnargs.append(131) -def_op('MAKE_FUNCTION', 132) # Number of args with default values +def_op('MAKE_FUNCTION', 132) # Flags def_op('BUILD_SLICE', 133) # Number of items -def_op('MAKE_CLOSURE', 134) def_op('LOAD_CLOSURE', 135) hasfree.append(135) def_op('LOAD_DEREF', 136) diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 7e125ef59b..09e68ce70a 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -649,50 +649,48 @@ expected_jumpy_line = 1 Instruction = dis.Instruction expected_opinfo_outer = [ - Instruction(opname='LOAD_CONST', opcode=100, arg=1, argval=3, argrepr='3', offset=0, starts_line=2, is_jump_target=False), - Instruction(opname='LOAD_CONST', opcode=100, arg=2, argval=4, argrepr='4', offset=2, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CLOSURE', opcode=135, arg=0, argval='a', argrepr='a', offset=4, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CLOSURE', opcode=135, arg=1, argval='b', argrepr='b', offset=6, starts_line=None, is_jump_target=False), - Instruction(opname='BUILD_TUPLE', opcode=102, arg=2, argval=2, argrepr='', offset=8, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=code_object_f, argrepr=repr(code_object_f), offset=10, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CONST', opcode=100, arg=4, argval='outer..f', argrepr="'outer..f'", offset=12, starts_line=None, is_jump_target=False), - Instruction(opname='MAKE_CLOSURE', opcode=134, arg=2, argval=2, argrepr='', offset=14, starts_line=None, is_jump_target=False), - Instruction(opname='STORE_FAST', opcode=125, arg=2, argval='f', argrepr='f', offset=16, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='print', argrepr='print', offset=18, starts_line=7, is_jump_target=False), - Instruction(opname='LOAD_DEREF', opcode=136, arg=0, argval='a', argrepr='a', offset=20, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_DEREF', opcode=136, arg=1, argval='b', argrepr='b', offset=22, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval='', argrepr="''", offset=24, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CONST', opcode=100, arg=6, argval=1, argrepr='1', offset=26, starts_line=None, is_jump_target=False), - Instruction(opname='BUILD_LIST', opcode=103, arg=0, argval=0, argrepr='', offset=28, starts_line=None, is_jump_target=False), - Instruction(opname='BUILD_MAP', opcode=105, arg=0, argval=0, argrepr='', offset=30, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CONST', opcode=100, arg=7, argval='Hello world!', argrepr="'Hello world!'", offset=32, starts_line=None, is_jump_target=False), - Instruction(opname='CALL_FUNCTION', opcode=131, arg=7, argval=7, argrepr='7 positional, 0 keyword pair', offset=34, starts_line=None, is_jump_target=False), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=36, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_FAST', opcode=124, arg=2, argval='f', argrepr='f', offset=38, starts_line=8, is_jump_target=False), - Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=40, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=8, argval=(3, 4), argrepr='(3, 4)', offset=0, starts_line=2, is_jump_target=False), + Instruction(opname='LOAD_CLOSURE', opcode=135, arg=0, argval='a', argrepr='a', offset=2, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CLOSURE', opcode=135, arg=1, argval='b', argrepr='b', offset=4, starts_line=None, is_jump_target=False), + Instruction(opname='BUILD_TUPLE', opcode=102, arg=2, argval=2, argrepr='', offset=6, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=code_object_f, argrepr=repr(code_object_f), offset=8, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=4, argval='outer..f', argrepr="'outer..f'", offset=10, starts_line=None, is_jump_target=False), + Instruction(opname='MAKE_FUNCTION', opcode=132, arg=9, argval=9, argrepr='', offset=12, starts_line=None, is_jump_target=False), + Instruction(opname='STORE_FAST', opcode=125, arg=2, argval='f', argrepr='f', offset=14, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='print', argrepr='print', offset=16, starts_line=7, is_jump_target=False), + Instruction(opname='LOAD_DEREF', opcode=136, arg=0, argval='a', argrepr='a', offset=18, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_DEREF', opcode=136, arg=1, argval='b', argrepr='b', offset=20, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval='', argrepr="''", offset=22, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=6, argval=1, argrepr='1', offset=24, starts_line=None, is_jump_target=False), + Instruction(opname='BUILD_LIST', opcode=103, arg=0, argval=0, argrepr='', offset=26, starts_line=None, is_jump_target=False), + Instruction(opname='BUILD_MAP', opcode=105, arg=0, argval=0, argrepr='', offset=28, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=7, argval='Hello world!', argrepr="'Hello world!'", offset=30, starts_line=None, is_jump_target=False), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=7, argval=7, argrepr='7 positional, 0 keyword pair', offset=32, starts_line=None, is_jump_target=False), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=34, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_FAST', opcode=124, arg=2, argval='f', argrepr='f', offset=36, starts_line=8, is_jump_target=False), + Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=38, starts_line=None, is_jump_target=False), ] expected_opinfo_f = [ - Instruction(opname='LOAD_CONST', opcode=100, arg=1, argval=5, argrepr='5', offset=0, starts_line=3, is_jump_target=False), - Instruction(opname='LOAD_CONST', opcode=100, arg=2, argval=6, argrepr='6', offset=2, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CLOSURE', opcode=135, arg=2, argval='a', argrepr='a', offset=4, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CLOSURE', opcode=135, arg=3, argval='b', argrepr='b', offset=6, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CLOSURE', opcode=135, arg=0, argval='c', argrepr='c', offset=8, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CLOSURE', opcode=135, arg=1, argval='d', argrepr='d', offset=10, starts_line=None, is_jump_target=False), - Instruction(opname='BUILD_TUPLE', opcode=102, arg=4, argval=4, argrepr='', offset=12, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=code_object_inner, argrepr=repr(code_object_inner), offset=14, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CONST', opcode=100, arg=4, argval='outer..f..inner', argrepr="'outer..f..inner'", offset=16, starts_line=None, is_jump_target=False), - Instruction(opname='MAKE_CLOSURE', opcode=134, arg=2, argval=2, argrepr='', offset=18, starts_line=None, is_jump_target=False), - Instruction(opname='STORE_FAST', opcode=125, arg=2, argval='inner', argrepr='inner', offset=20, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='print', argrepr='print', offset=22, starts_line=5, is_jump_target=False), - Instruction(opname='LOAD_DEREF', opcode=136, arg=2, argval='a', argrepr='a', offset=24, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_DEREF', opcode=136, arg=3, argval='b', argrepr='b', offset=26, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_DEREF', opcode=136, arg=0, argval='c', argrepr='c', offset=28, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_DEREF', opcode=136, arg=1, argval='d', argrepr='d', offset=30, starts_line=None, is_jump_target=False), - Instruction(opname='CALL_FUNCTION', opcode=131, arg=4, argval=4, argrepr='4 positional, 0 keyword pair', offset=32, starts_line=None, is_jump_target=False), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=34, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_FAST', opcode=124, arg=2, argval='inner', argrepr='inner', offset=36, starts_line=6, is_jump_target=False), - Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=38, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval=(5, 6), argrepr='(5, 6)', offset=0, starts_line=3, is_jump_target=False), + Instruction(opname='LOAD_CLOSURE', opcode=135, arg=2, argval='a', argrepr='a', offset=2, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CLOSURE', opcode=135, arg=3, argval='b', argrepr='b', offset=4, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CLOSURE', opcode=135, arg=0, argval='c', argrepr='c', offset=6, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CLOSURE', opcode=135, arg=1, argval='d', argrepr='d', offset=8, starts_line=None, is_jump_target=False), + Instruction(opname='BUILD_TUPLE', opcode=102, arg=4, argval=4, argrepr='', offset=10, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=code_object_inner, argrepr=repr(code_object_inner), offset=12, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=4, argval='outer..f..inner', argrepr="'outer..f..inner'", offset=14, starts_line=None, is_jump_target=False), + Instruction(opname='MAKE_FUNCTION', opcode=132, arg=9, argval=9, argrepr='', offset=16, starts_line=None, is_jump_target=False), + Instruction(opname='STORE_FAST', opcode=125, arg=2, argval='inner', argrepr='inner', offset=18, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='print', argrepr='print', offset=20, starts_line=5, is_jump_target=False), + Instruction(opname='LOAD_DEREF', opcode=136, arg=2, argval='a', argrepr='a', offset=22, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_DEREF', opcode=136, arg=3, argval='b', argrepr='b', offset=24, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_DEREF', opcode=136, arg=0, argval='c', argrepr='c', offset=26, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_DEREF', opcode=136, arg=1, argval='d', argrepr='d', offset=28, starts_line=None, is_jump_target=False), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=4, argval=4, argrepr='4 positional, 0 keyword pair', offset=30, starts_line=None, is_jump_target=False), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=32, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_FAST', opcode=124, arg=2, argval='inner', argrepr='inner', offset=34, starts_line=6, is_jump_target=False), + Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=36, starts_line=None, is_jump_target=False), ] expected_opinfo_inner = [ diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py index c3ecd0a07c..bfe5225f77 100644 --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -345,7 +345,7 @@ class GrammarTests(unittest.TestCase): def f(x) -> list: pass self.assertEqual(f.__annotations__, {'return': list}) - # test MAKE_CLOSURE with a variety of oparg's + # test closures with a variety of opargs closure = 1 def f(): return closure def f(x=1): return closure -- cgit v1.2.1 From 25d6027ebfff42c4e1d0891e7ce93679b2f5ccbf Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Sun, 12 Jun 2016 11:11:20 -0700 Subject: Issue #27186: skip bytes path test for os.scandir() on Windows --- Lib/test/test_os.py | 1 + 1 file changed, 1 insertion(+) (limited to 'Lib') diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 3f955713c4..352ffd2edd 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2953,6 +2953,7 @@ class TestScandir(unittest.TestCase): entry = self.create_file_entry() self.assertEqual(os.fspath(entry), os.path.join(self.path, 'file.txt')) + @unittest.skipIf(sys.platform == "nt", "test requires bytes path support") def test_fspath_protocol_bytes(self): bytes_filename = os.fsencode('bytesfile.txt') bytes_entry = self.create_file_entry(name=bytes_filename) -- cgit v1.2.1 From e50944692ee6fb82b6dfb8b55e51e42e416f726c Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Sun, 12 Jun 2016 15:49:20 -0400 Subject: Issue #27239: Continue refactoring idlelib.macosx and adding macosx tests. --- Lib/idlelib/idle_test/htest.py | 2 -- Lib/idlelib/idle_test/test_macosx.py | 42 ++++++++++++++++++++++++++++++------ Lib/idlelib/macosx.py | 25 +++++++++++++++------ 3 files changed, 53 insertions(+), 16 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py index 5fd33a6c69..d0177bb5ad 100644 --- a/Lib/idlelib/idle_test/htest.py +++ b/Lib/idlelib/idle_test/htest.py @@ -66,7 +66,6 @@ outwin.OutputWindow (indirectly being tested with grep test) ''' from importlib import import_module -from idlelib.macosx import _init_tk_type import tkinter as tk from tkinter.ttk import Scrollbar @@ -338,7 +337,6 @@ def run(*tests): root = tk.Tk() root.title('IDLE htest') root.resizable(0, 0) - _init_tk_type(root) # a scrollable Label like constant width text widget. frameLabel = tk.Frame(root, padx=10) diff --git a/Lib/idlelib/idle_test/test_macosx.py b/Lib/idlelib/idle_test/test_macosx.py index 0f90fb65f1..d7f8f5db27 100644 --- a/Lib/idlelib/idle_test/test_macosx.py +++ b/Lib/idlelib/idle_test/test_macosx.py @@ -1,4 +1,6 @@ -'''Test idlelib.macosx.py +'''Test idlelib.macosx.py. + +Coverage: 71% on Windows. ''' from idlelib import macosx from test.support import requires @@ -6,8 +8,8 @@ import sys import tkinter as tk import unittest import unittest.mock as mock +from idlelib.filelist import FileList -MAC = sys.platform == 'darwin' mactypes = {'carbon', 'cocoa', 'xquartz'} nontypes = {'other'} alltypes = mactypes | nontypes @@ -20,21 +22,23 @@ class InitTktypeTest(unittest.TestCase): def setUpClass(cls): requires('gui') cls.root = tk.Tk() + cls.orig_platform = macosx.platform @classmethod def tearDownClass(cls): cls.root.update_idletasks() cls.root.destroy() del cls.root + macosx.platform = cls.orig_platform def test_init_sets_tktype(self): "Test that _init_tk_type sets _tk_type according to platform." - for root in (None, self.root): - with self.subTest(root=root): + for platform, types in ('darwin', alltypes), ('other', nontypes): + with self.subTest(platform=platform): + macosx.platform = platform macosx._tk_type == None - macosx._init_tk_type(root) - self.assertIn(macosx._tk_type, - mactypes if MAC else nontypes) + macosx._init_tk_type() + self.assertIn(macosx._tk_type, types) class IsTypeTkTest(unittest.TestCase): @@ -65,5 +69,29 @@ class IsTypeTkTest(unittest.TestCase): (func()) +class SetupTest(unittest.TestCase): + "Test setupApp." + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = tk.Tk() + + @classmethod + def tearDownClass(cls): + cls.root.update_idletasks() + cls.root.destroy() + del cls.root + + def test_setupapp(self): + "Call setupApp with each possible graphics type." + root = self.root + flist = FileList(root) + for tktype in alltypes: + with self.subTest(tktype=tktype): + macosx._tk_type = tktype + macosx.setupApp(root, flist) + + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/Lib/idlelib/macosx.py b/Lib/idlelib/macosx.py index 98d7887b5c..f9f558de18 100644 --- a/Lib/idlelib/macosx.py +++ b/Lib/idlelib/macosx.py @@ -1,20 +1,24 @@ """ A number of functions that enhance IDLE on Mac OSX. """ -import sys +from sys import platform # Used in _init_tk_type, changed by test. import tkinter import warnings + +## Define functions that query the Mac graphics type. +## _tk_type and its initializer are private to this section. + _tk_type = None -def _init_tk_type(idleroot=None): +def _init_tk_type(): """ Initializes OS X Tk variant values for isAquaTk(), isCarbonTk(), isCocoaTk(), and isXQuartz(). """ global _tk_type - if sys.platform == 'darwin': - root = idleroot or tkinter.Tk() + if platform == 'darwin': + root = tkinter.Tk() ws = root.tk.call('tk', 'windowingsystem') if 'x11' in ws: _tk_type = "xquartz" @@ -24,8 +28,7 @@ def _init_tk_type(idleroot=None): _tk_type = "cocoa" else: _tk_type = "carbon" - if not idleroot: - root.destroy + root.destroy() else: _tk_type = "other" @@ -62,6 +65,7 @@ def isXQuartz(): _init_tk_type() return _tk_type == "xquartz" + def tkVersionWarning(root): """ Returns a string warning message if the Tk version in use appears to @@ -82,6 +86,9 @@ def tkVersionWarning(root): else: return False + +## Fix the menu and related functions. + def addOpenEventSupport(root, flist): """ This ensures that the application will respond to open AppleEvents, which @@ -233,9 +240,13 @@ def setupApp(root, flist): isAquaTk(), isCarbonTk(), isCocoaTk(), isXQuartz() functions which are initialized here as well. """ - _init_tk_type(root) if isAquaTk(): hideTkConsole(root) overrideRootMenu(root, flist) addOpenEventSupport(root, flist) fixb2context(root) + + +if __name__ == '__main__': + from unittest import main + main('idlelib.idle_test.test_macosx', verbosity=2) -- cgit v1.2.1 From c27a5732e9b2e5b40958261fb8ea06a26e781359 Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Mon, 13 Jun 2016 03:28:35 +0000 Subject: Issue #27186: Skip scandir(bytes) test with os.name == "nt" --- Lib/test/test_os.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 352ffd2edd..d34f6c6432 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2953,7 +2953,7 @@ class TestScandir(unittest.TestCase): entry = self.create_file_entry() self.assertEqual(os.fspath(entry), os.path.join(self.path, 'file.txt')) - @unittest.skipIf(sys.platform == "nt", "test requires bytes path support") + @unittest.skipIf(os.name == "nt", "test requires bytes path support") def test_fspath_protocol_bytes(self): bytes_filename = os.fsencode('bytesfile.txt') bytes_entry = self.create_file_entry(name=bytes_filename) -- cgit v1.2.1 From 7728833ae1eee3f58d0d76bc6dea34ecacf4e56f Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 13 Jun 2016 09:24:11 +0300 Subject: Issue #27025: Generated names for Tkinter widgets are now more meanful and recognizirable. --- Lib/tkinter/__init__.py | 16 ++++++++++++---- Lib/tkinter/test/test_tkinter/test_misc.py | 8 ++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) (limited to 'Lib') diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index cfb5268622..c687da580c 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -489,6 +489,9 @@ class Misc: Base class which defines methods common for interior widgets.""" + # used for generating child widget names + _last_child_ids = None + # XXX font command? _tclCommands = None def destroy(self): @@ -2174,7 +2177,15 @@ class BaseWidget(Misc): name = cnf['name'] del cnf['name'] if not name: - name = repr(id(self)) + name = self.__class__.__name__.lower() + if master._last_child_ids is None: + master._last_child_ids = {} + count = master._last_child_ids.get(name, 0) + 1 + master._last_child_ids[name] = count + if count == 1: + name = '`%s' % (name,) + else: + name = '`%s%d' % (name, count) self._name = name if master._w=='.': self._w = '.' + name @@ -3392,9 +3403,6 @@ class Image: if not name: Image._last_id += 1 name = "pyimage%r" % (Image._last_id,) # tk itself would use image - # The following is needed for systems where id(x) - # can return a negative number, such as Linux/m68k: - if name[0] == '-': name = '_' + name[1:] if kw and cnf: cnf = _cnfmerge((cnf, kw)) elif kw: cnf = kw options = () diff --git a/Lib/tkinter/test/test_tkinter/test_misc.py b/Lib/tkinter/test/test_tkinter/test_misc.py index 85ee2c70b1..9dc1e37547 100644 --- a/Lib/tkinter/test/test_tkinter/test_misc.py +++ b/Lib/tkinter/test/test_tkinter/test_misc.py @@ -12,6 +12,14 @@ class MiscTest(AbstractTkTest, unittest.TestCase): f = tkinter.Frame(t, name='child') self.assertEqual(repr(f), '') + def test_generated_names(self): + t = tkinter.Toplevel(self.root) + f = tkinter.Frame(t) + f2 = tkinter.Frame(t) + b = tkinter.Button(f2) + for name in str(b).split('.'): + self.assertFalse(name.isidentifier(), msg=repr(name)) + def test_tk_setPalette(self): root = self.root root.tk_setPalette('black') -- cgit v1.2.1 From 42eabdfcca2576a8998dbcbe2d2f95d546aa309e Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Mon, 13 Jun 2016 16:51:55 -0400 Subject: Update pydoc topics for 3.6.0a2 --- Lib/pydoc_data/topics.py | 202 ++++++++++++++++++++++++++++------------------- 1 file changed, 122 insertions(+), 80 deletions(-) (limited to 'Lib') diff --git a/Lib/pydoc_data/topics.py b/Lib/pydoc_data/topics.py index a0b071b0c5..be61bdd1e8 100644 --- a/Lib/pydoc_data/topics.py +++ b/Lib/pydoc_data/topics.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Autogenerated by Sphinx on Mon May 16 13:41:38 2016 +# Autogenerated by Sphinx on Mon Jun 13 16:49:58 2016 topics = {'assert': '\n' 'The "assert" statement\n' '**********************\n' @@ -47,12 +47,12 @@ topics = {'assert': '\n' 'to\n' 'modify attributes or items of mutable objects:\n' '\n' - ' assignment_stmt ::= (target_list "=")+ (expression_list | ' - 'yield_expression)\n' + ' assignment_stmt ::= (target_list "=")+ (starred_expression ' + '| yield_expression)\n' ' target_list ::= target ("," target)* [","]\n' ' target ::= identifier\n' - ' | "(" target_list ")"\n' - ' | "[" target_list "]"\n' + ' | "(" [target_list] ")"\n' + ' | "[" [target_list] "]"\n' ' | attributeref\n' ' | subscription\n' ' | slicing\n' @@ -89,35 +89,42 @@ topics = {'assert': '\n' 'parentheses or square brackets, is recursively defined as ' 'follows.\n' '\n' - '* If the target list is a single target: The object is ' - 'assigned to\n' - ' that target.\n' + '* If the target list is empty: The object must also be an ' + 'empty\n' + ' iterable.\n' '\n' - '* If the target list is a comma-separated list of targets: ' - 'The\n' - ' object must be an iterable with the same number of items as ' - 'there\n' - ' are targets in the target list, and the items are assigned, ' - 'from\n' - ' left to right, to the corresponding targets.\n' + '* If the target list is a single target in parentheses: The ' + 'object\n' + ' is assigned to that target.\n' '\n' - ' * If the target list contains one target prefixed with an\n' - ' asterisk, called a "starred" target: The object must be a ' - 'sequence\n' - ' with at least as many items as there are targets in the ' + '* If the target list is a comma-separated list of targets, or ' + 'a\n' + ' single target in square brackets: The object must be an ' + 'iterable\n' + ' with the same number of items as there are targets in the ' 'target\n' - ' list, minus one. The first items of the sequence are ' - 'assigned,\n' - ' from left to right, to the targets before the starred ' - 'target. The\n' - ' final items of the sequence are assigned to the targets ' - 'after the\n' - ' starred target. A list of the remaining items in the ' - 'sequence is\n' - ' then assigned to the starred target (the list can be ' - 'empty).\n' + ' list, and the items are assigned, from left to right, to ' + 'the\n' + ' corresponding targets.\n' '\n' - ' * Else: The object must be a sequence with the same number ' + ' * If the target list contains one target prefixed with an\n' + ' asterisk, called a "starred" target: The object must be ' + 'an\n' + ' iterable with at least as many items as there are targets ' + 'in the\n' + ' target list, minus one. The first items of the iterable ' + 'are\n' + ' assigned, from left to right, to the targets before the ' + 'starred\n' + ' target. The final items of the iterable are assigned to ' + 'the\n' + ' targets after the starred target. A list of the remaining ' + 'items\n' + ' in the iterable is then assigned to the starred target ' + '(the list\n' + ' can be empty).\n' + '\n' + ' * Else: The object must be an iterable with the same number ' 'of\n' ' items as there are targets in the target list, and the ' 'items are\n' @@ -149,14 +156,6 @@ topics = {'assert': '\n' 'destructor (if it\n' ' has one) to be called.\n' '\n' - '* If the target is a target list enclosed in parentheses or ' - 'in\n' - ' square brackets: The object must be an iterable with the ' - 'same number\n' - ' of items as there are targets in the target list, and its ' - 'items are\n' - ' assigned, from left to right, to the corresponding targets.\n' - '\n' '* If the target is an attribute reference: The primary ' 'expression in\n' ' the reference is evaluated. It should yield an object with\n' @@ -1148,18 +1147,18 @@ topics = {'assert': '\n' ' call ::= primary "(" [argument_list [","] | ' 'comprehension] ")"\n' ' argument_list ::= positional_arguments ["," ' - 'keyword_arguments]\n' - ' ["," "*" expression] ["," ' - 'keyword_arguments]\n' - ' ["," "**" expression]\n' - ' | keyword_arguments ["," "*" expression]\n' - ' ["," keyword_arguments] ["," "**" ' - 'expression]\n' - ' | "*" expression ["," keyword_arguments] ["," ' - '"**" expression]\n' - ' | "**" expression\n' - ' positional_arguments ::= expression ("," expression)*\n' - ' keyword_arguments ::= keyword_item ("," keyword_item)*\n' + 'starred_and_keywords]\n' + ' ["," keywords_arguments]\n' + ' | starred_and_keywords ["," ' + 'keywords_arguments]\n' + ' | keywords_arguments\n' + ' positional_arguments ::= ["*"] expression ("," ["*"] ' + 'expression)*\n' + ' starred_and_keywords ::= ("*" expression | keyword_item)\n' + ' ("," "*" expression | "," ' + 'keyword_item)*\n' + ' keywords_arguments ::= (keyword_item | "**" expression)\n' + ' ("," keyword_item | "**" expression)*\n' ' keyword_item ::= identifier "=" expression\n' '\n' 'An optional trailing comma may be present after the positional and\n' @@ -1235,20 +1234,21 @@ topics = {'assert': '\n' '\n' 'If the syntax "*expression" appears in the function call, ' '"expression"\n' - 'must evaluate to an iterable. Elements from this iterable are ' - 'treated\n' - 'as if they were additional positional arguments; if there are\n' - 'positional arguments *x1*, ..., *xN*, and "expression" evaluates to ' - 'a\n' - 'sequence *y1*, ..., *yM*, this is equivalent to a call with M+N\n' - 'positional arguments *x1*, ..., *xN*, *y1*, ..., *yM*.\n' + 'must evaluate to an *iterable*. Elements from these iterables are\n' + 'treated as if they were additional positional arguments. For the ' + 'call\n' + '"f(x1, x2, *y, x3, x4)", if *y* evaluates to a sequence *y1*, ...,\n' + '*yM*, this is equivalent to a call with M+4 positional arguments ' + '*x1*,\n' + '*x2*, *y1*, ..., *yM*, *x3*, *x4*.\n' '\n' 'A consequence of this is that although the "*expression" syntax ' 'may\n' - 'appear *after* some keyword arguments, it is processed *before* ' - 'the\n' - 'keyword arguments (and the "**expression" argument, if any -- see\n' - 'below). So:\n' + 'appear *after* explicit keyword arguments, it is processed ' + '*before*\n' + 'the keyword arguments (and any "**expression" arguments -- see ' + 'below).\n' + 'So:\n' '\n' ' >>> def f(a, b):\n' ' ... print(a, b)\n' @@ -1269,16 +1269,25 @@ topics = {'assert': '\n' 'arise.\n' '\n' 'If the syntax "**expression" appears in the function call,\n' - '"expression" must evaluate to a mapping, the contents of which are\n' - 'treated as additional keyword arguments. In the case of a keyword\n' - 'appearing in both "expression" and as an explicit keyword argument, ' - 'a\n' - '"TypeError" exception is raised.\n' + '"expression" must evaluate to a *mapping*, the contents of which ' + 'are\n' + 'treated as additional keyword arguments. If a keyword is already\n' + 'present (as an explicit keyword argument, or from another ' + 'unpacking),\n' + 'a "TypeError" exception is raised.\n' '\n' 'Formal parameters using the syntax "*identifier" or "**identifier"\n' 'cannot be used as positional argument slots or as keyword argument\n' 'names.\n' '\n' + 'Changed in version 3.5: Function calls accept any number of "*" ' + 'and\n' + '"**" unpackings, positional arguments may follow iterable ' + 'unpackings\n' + '("*"), and keyword arguments may follow dictionary unpackings ' + '("**").\n' + 'Originally proposed by **PEP 448**.\n' + '\n' 'A call always returns some value, possibly "None", unless it raises ' 'an\n' 'exception. How this value is computed depends on the type of the\n' @@ -1324,7 +1333,7 @@ topics = {'assert': '\n' '\n' ' classdef ::= [decorators] "class" classname [inheritance] ":" ' 'suite\n' - ' inheritance ::= "(" [parameter_list] ")"\n' + ' inheritance ::= "(" [argument_list] ")"\n' ' classname ::= identifier\n' '\n' 'A class definition is an executable statement. The inheritance ' @@ -2261,7 +2270,7 @@ topics = {'assert': '\n' '[parameter_list] ")" ["->" expression] ":" suite\n' ' decorators ::= decorator+\n' ' decorator ::= "@" dotted_name ["(" ' - '[parameter_list [","]] ")"] NEWLINE\n' + '[argument_list [","]] ")"] NEWLINE\n' ' dotted_name ::= identifier ("." identifier)*\n' ' parameter_list ::= defparameter ("," defparameter)* ' '["," [parameter_list_starargs]]\n' @@ -2426,7 +2435,7 @@ topics = {'assert': '\n' '\n' ' classdef ::= [decorators] "class" classname [inheritance] ' '":" suite\n' - ' inheritance ::= "(" [parameter_list] ")"\n' + ' inheritance ::= "(" [argument_list] ")"\n' ' classname ::= identifier\n' '\n' 'A class definition is an executable statement. The inheritance ' @@ -2563,7 +2572,7 @@ topics = {'assert': '\n' 'Is semantically equivalent to:\n' '\n' ' iter = (ITER)\n' - ' iter = await type(iter).__aiter__(iter)\n' + ' iter = type(iter).__aiter__(iter)\n' ' running = True\n' ' while running:\n' ' try:\n' @@ -3889,7 +3898,7 @@ topics = {'assert': '\n' ' dict_display ::= "{" [key_datum_list | dict_comprehension] ' '"}"\n' ' key_datum_list ::= key_datum ("," key_datum)* [","]\n' - ' key_datum ::= expression ":" expression\n' + ' key_datum ::= expression ":" expression | "**" or_expr\n' ' dict_comprehension ::= expression ":" expression comp_for\n' '\n' 'A dictionary display yields a new dictionary object.\n' @@ -3903,6 +3912,14 @@ topics = {'assert': '\n' 'value\n' 'for that key will be the last one given.\n' '\n' + 'A double asterisk "**" denotes *dictionary unpacking*. Its operand\n' + 'must be a *mapping*. Each mapping item is added to the new\n' + 'dictionary. Later values replace values already set by earlier\n' + 'key/datum pairs and earlier dictionary unpackings.\n' + '\n' + 'New in version 3.5: Unpacking into dictionary displays, originally\n' + 'proposed by **PEP 448**.\n' + '\n' 'A dict comprehension, in contrast to list and set comprehensions,\n' 'needs two expressions separated with a colon followed by the usual\n' '"for" and "if" clauses. When the comprehension is run, the ' @@ -4384,13 +4401,30 @@ topics = {'assert': '\n' 'Expression lists\n' '****************\n' '\n' - ' expression_list ::= expression ( "," expression )* [","]\n' + ' expression_list ::= expression ( "," expression )* [","]\n' + ' starred_list ::= starred_item ( "," starred_item )* ' + '[","]\n' + ' starred_expression ::= expression | ( starred_item "," )* ' + '[starred_item]\n' + ' starred_item ::= expression | "*" or_expr\n' '\n' - 'An expression list containing at least one comma yields a ' - 'tuple. The\n' - 'length of the tuple is the number of expressions in the list. ' - 'The\n' - 'expressions are evaluated from left to right.\n' + 'Except when part of a list or set display, an expression list\n' + 'containing at least one comma yields a tuple. The length of ' + 'the tuple\n' + 'is the number of expressions in the list. The expressions are\n' + 'evaluated from left to right.\n' + '\n' + 'An asterisk "*" denotes *iterable unpacking*. Its operand must ' + 'be an\n' + '*iterable*. The iterable is expanded into a sequence of items, ' + 'which\n' + 'are included in the new tuple, list, or set, at the site of ' + 'the\n' + 'unpacking.\n' + '\n' + 'New in version 3.5: Iterable unpacking in expression lists, ' + 'originally\n' + 'proposed by **PEP 448**.\n' '\n' 'The trailing comma is required only to create a single tuple ' '(a.k.a. a\n' @@ -5220,7 +5254,7 @@ topics = {'assert': '\n' '[parameter_list] ")" ["->" expression] ":" suite\n' ' decorators ::= decorator+\n' ' decorator ::= "@" dotted_name ["(" ' - '[parameter_list [","]] ")"] NEWLINE\n' + '[argument_list [","]] ")"] NEWLINE\n' ' dotted_name ::= identifier ("." identifier)*\n' ' parameter_list ::= defparameter ("," defparameter)* ' '["," [parameter_list_starargs]]\n' @@ -5682,7 +5716,7 @@ topics = {'assert': '\n' 'the\n' 'two steps are carried out separately for each clause, just as ' 'though\n' - 'the clauses had been separated out into individiual import ' + 'the clauses had been separated out into individual import ' 'statements.\n' '\n' 'The details of the first step, finding and loading modules are\n' @@ -6016,7 +6050,7 @@ topics = {'assert': '\n' 'in\n' 'square brackets:\n' '\n' - ' list_display ::= "[" [expression_list | comprehension] "]"\n' + ' list_display ::= "[" [starred_list | comprehension] "]"\n' '\n' 'A list display yields a new list object, the contents being ' 'specified\n' @@ -8305,6 +8339,14 @@ topics = {'assert': '\n' 'object is bound in the local namespace as the defined ' 'class.\n' '\n' + 'When a new class is created by "type.__new__", the object ' + 'provided as\n' + 'the namespace parameter is copied to a standard Python ' + 'dictionary and\n' + 'the original object is discarded. The new copy becomes the ' + '"__dict__"\n' + 'attribute of the class object.\n' + '\n' 'See also:\n' '\n' ' **PEP 3135** - New super\n' -- cgit v1.2.1 From cfab8ab54ed5d20f88e22bab6e2b6bca3f0ffb6f Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Tue, 14 Jun 2016 00:53:30 -0400 Subject: Issue #27245: temporary rename for merge. --- Lib/idlelib/configDialog.py | 1435 +++++++++++++++++++++++++++++++++++++++++++ Lib/idlelib/configdialog.py | 1435 ------------------------------------------- 2 files changed, 1435 insertions(+), 1435 deletions(-) create mode 100644 Lib/idlelib/configDialog.py delete mode 100644 Lib/idlelib/configdialog.py (limited to 'Lib') diff --git a/Lib/idlelib/configDialog.py b/Lib/idlelib/configDialog.py new file mode 100644 index 0000000000..1d7b4179b0 --- /dev/null +++ b/Lib/idlelib/configDialog.py @@ -0,0 +1,1435 @@ +"""IDLE Configuration Dialog: support user customization of IDLE by GUI + +Customize font faces, sizes, and colorization attributes. Set indentation +defaults. Customize keybindings. Colorization and keybindings can be +saved as user defined sets. Select startup options including shell/editor +and default window size. Define additional help sources. + +Note that tab width in IDLE is currently fixed at eight due to Tk issues. +Refer to comments in EditorWindow autoindent code for details. + +""" +from tkinter import * +from tkinter.ttk import Scrollbar +import tkinter.messagebox as tkMessageBox +import tkinter.colorchooser as tkColorChooser +import tkinter.font as tkFont + +from idlelib.config import idleConf +from idlelib.dynoption import DynOptionMenu +from idlelib.config_key import GetKeysDialog +from idlelib.config_sec import GetCfgSectionNameDialog +from idlelib.config_help import GetHelpSourceDialog +from idlelib.tabbedpages import TabbedPageSet +from idlelib.textview import view_text +from idlelib import macosx + +class ConfigDialog(Toplevel): + + def __init__(self, parent, title='', _htest=False, _utest=False): + """ + _htest - bool, change box location when running htest + _utest - bool, don't wait_window when running unittest + """ + Toplevel.__init__(self, parent) + self.parent = parent + if _htest: + parent.instance_dict = {} + self.wm_withdraw() + + self.configure(borderwidth=5) + self.title(title or 'IDLE Preferences') + self.geometry( + "+%d+%d" % (parent.winfo_rootx() + 20, + parent.winfo_rooty() + (30 if not _htest else 150))) + #Theme Elements. Each theme element key is its display name. + #The first value of the tuple is the sample area tag name. + #The second value is the display name list sort index. + self.themeElements={ + 'Normal Text': ('normal', '00'), + 'Python Keywords': ('keyword', '01'), + 'Python Definitions': ('definition', '02'), + 'Python Builtins': ('builtin', '03'), + 'Python Comments': ('comment', '04'), + 'Python Strings': ('string', '05'), + 'Selected Text': ('hilite', '06'), + 'Found Text': ('hit', '07'), + 'Cursor': ('cursor', '08'), + 'Editor Breakpoint': ('break', '09'), + 'Shell Normal Text': ('console', '10'), + 'Shell Error Text': ('error', '11'), + 'Shell Stdout Text': ('stdout', '12'), + 'Shell Stderr Text': ('stderr', '13'), + } + self.ResetChangedItems() #load initial values in changed items dict + self.CreateWidgets() + self.resizable(height=FALSE, width=FALSE) + self.transient(parent) + self.grab_set() + self.protocol("WM_DELETE_WINDOW", self.Cancel) + self.tabPages.focus_set() + #key bindings for this dialog + #self.bind('', self.Cancel) #dismiss dialog, no save + #self.bind('', self.Apply) #apply changes, save + #self.bind('', self.Help) #context help + self.LoadConfigs() + self.AttachVarCallbacks() #avoid callbacks during LoadConfigs + + if not _utest: + self.wm_deiconify() + self.wait_window() + + def CreateWidgets(self): + self.tabPages = TabbedPageSet(self, + page_names=['Fonts/Tabs', 'Highlighting', 'Keys', 'General', + 'Extensions']) + self.tabPages.pack(side=TOP, expand=TRUE, fill=BOTH) + self.CreatePageFontTab() + self.CreatePageHighlight() + self.CreatePageKeys() + self.CreatePageGeneral() + self.CreatePageExtensions() + self.create_action_buttons().pack(side=BOTTOM) + + def create_action_buttons(self): + if macosx.isAquaTk(): + # Changing the default padding on OSX results in unreadable + # text in the buttons + paddingArgs = {} + else: + paddingArgs = {'padx':6, 'pady':3} + outer = Frame(self, pady=2) + buttons = Frame(outer, pady=2) + for txt, cmd in ( + ('Ok', self.Ok), + ('Apply', self.Apply), + ('Cancel', self.Cancel), + ('Help', self.Help)): + Button(buttons, text=txt, command=cmd, takefocus=FALSE, + **paddingArgs).pack(side=LEFT, padx=5) + # add space above buttons + Frame(outer, height=2, borderwidth=0).pack(side=TOP) + buttons.pack(side=BOTTOM) + return outer + + def CreatePageFontTab(self): + parent = self.parent + self.fontSize = StringVar(parent) + self.fontBold = BooleanVar(parent) + self.fontName = StringVar(parent) + self.spaceNum = IntVar(parent) + self.editFont = tkFont.Font(parent, ('courier', 10, 'normal')) + + ##widget creation + #body frame + frame = self.tabPages.pages['Fonts/Tabs'].frame + #body section frames + frameFont = LabelFrame( + frame, borderwidth=2, relief=GROOVE, text=' Base Editor Font ') + frameIndent = LabelFrame( + frame, borderwidth=2, relief=GROOVE, text=' Indentation Width ') + #frameFont + frameFontName = Frame(frameFont) + frameFontParam = Frame(frameFont) + labelFontNameTitle = Label( + frameFontName, justify=LEFT, text='Font Face :') + self.listFontName = Listbox( + frameFontName, height=5, takefocus=FALSE, exportselection=FALSE) + self.listFontName.bind( + '', self.OnListFontButtonRelease) + scrollFont = Scrollbar(frameFontName) + scrollFont.config(command=self.listFontName.yview) + self.listFontName.config(yscrollcommand=scrollFont.set) + labelFontSizeTitle = Label(frameFontParam, text='Size :') + self.optMenuFontSize = DynOptionMenu( + frameFontParam, self.fontSize, None, command=self.SetFontSample) + checkFontBold = Checkbutton( + frameFontParam, variable=self.fontBold, onvalue=1, + offvalue=0, text='Bold', command=self.SetFontSample) + frameFontSample = Frame(frameFont, relief=SOLID, borderwidth=1) + self.labelFontSample = Label( + frameFontSample, justify=LEFT, font=self.editFont, + text='AaBbCcDdEe\nFfGgHhIiJjK\n1234567890\n#:+=(){}[]') + #frameIndent + frameIndentSize = Frame(frameIndent) + labelSpaceNumTitle = Label( + frameIndentSize, justify=LEFT, + text='Python Standard: 4 Spaces!') + self.scaleSpaceNum = Scale( + frameIndentSize, variable=self.spaceNum, + orient='horizontal', tickinterval=2, from_=2, to=16) + + #widget packing + #body + frameFont.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) + frameIndent.pack(side=LEFT, padx=5, pady=5, fill=Y) + #frameFont + frameFontName.pack(side=TOP, padx=5, pady=5, fill=X) + frameFontParam.pack(side=TOP, padx=5, pady=5, fill=X) + labelFontNameTitle.pack(side=TOP, anchor=W) + self.listFontName.pack(side=LEFT, expand=TRUE, fill=X) + scrollFont.pack(side=LEFT, fill=Y) + labelFontSizeTitle.pack(side=LEFT, anchor=W) + self.optMenuFontSize.pack(side=LEFT, anchor=W) + checkFontBold.pack(side=LEFT, anchor=W, padx=20) + frameFontSample.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) + self.labelFontSample.pack(expand=TRUE, fill=BOTH) + #frameIndent + frameIndentSize.pack(side=TOP, fill=X) + labelSpaceNumTitle.pack(side=TOP, anchor=W, padx=5) + self.scaleSpaceNum.pack(side=TOP, padx=5, fill=X) + return frame + + def CreatePageHighlight(self): + parent = self.parent + self.builtinTheme = StringVar(parent) + self.customTheme = StringVar(parent) + self.fgHilite = BooleanVar(parent) + self.colour = StringVar(parent) + self.fontName = StringVar(parent) + self.themeIsBuiltin = BooleanVar(parent) + self.highlightTarget = StringVar(parent) + + ##widget creation + #body frame + frame = self.tabPages.pages['Highlighting'].frame + #body section frames + frameCustom = LabelFrame(frame, borderwidth=2, relief=GROOVE, + text=' Custom Highlighting ') + frameTheme = LabelFrame(frame, borderwidth=2, relief=GROOVE, + text=' Highlighting Theme ') + #frameCustom + self.textHighlightSample=Text( + frameCustom, relief=SOLID, borderwidth=1, + font=('courier', 12, ''), cursor='hand2', width=21, height=11, + takefocus=FALSE, highlightthickness=0, wrap=NONE) + text=self.textHighlightSample + text.bind('', lambda e: 'break') + text.bind('', lambda e: 'break') + textAndTags=( + ('#you can click here', 'comment'), ('\n', 'normal'), + ('#to choose items', 'comment'), ('\n', 'normal'), + ('def', 'keyword'), (' ', 'normal'), + ('func', 'definition'), ('(param):\n ', 'normal'), + ('"""string"""', 'string'), ('\n var0 = ', 'normal'), + ("'string'", 'string'), ('\n var1 = ', 'normal'), + ("'selected'", 'hilite'), ('\n var2 = ', 'normal'), + ("'found'", 'hit'), ('\n var3 = ', 'normal'), + ('list', 'builtin'), ('(', 'normal'), + ('None', 'keyword'), (')\n', 'normal'), + (' breakpoint("line")', 'break'), ('\n\n', 'normal'), + (' error ', 'error'), (' ', 'normal'), + ('cursor |', 'cursor'), ('\n ', 'normal'), + ('shell', 'console'), (' ', 'normal'), + ('stdout', 'stdout'), (' ', 'normal'), + ('stderr', 'stderr'), ('\n', 'normal')) + for txTa in textAndTags: + text.insert(END, txTa[0], txTa[1]) + for element in self.themeElements: + def tem(event, elem=element): + event.widget.winfo_toplevel().highlightTarget.set(elem) + text.tag_bind( + self.themeElements[element][0], '', tem) + text.config(state=DISABLED) + self.frameColourSet = Frame(frameCustom, relief=SOLID, borderwidth=1) + frameFgBg = Frame(frameCustom) + buttonSetColour = Button( + self.frameColourSet, text='Choose Colour for :', + command=self.GetColour, highlightthickness=0) + self.optMenuHighlightTarget = DynOptionMenu( + self.frameColourSet, self.highlightTarget, None, + highlightthickness=0) #, command=self.SetHighlightTargetBinding + self.radioFg = Radiobutton( + frameFgBg, variable=self.fgHilite, value=1, + text='Foreground', command=self.SetColourSampleBinding) + self.radioBg=Radiobutton( + frameFgBg, variable=self.fgHilite, value=0, + text='Background', command=self.SetColourSampleBinding) + self.fgHilite.set(1) + buttonSaveCustomTheme = Button( + frameCustom, text='Save as New Custom Theme', + command=self.SaveAsNewTheme) + #frameTheme + labelTypeTitle = Label(frameTheme, text='Select : ') + self.radioThemeBuiltin = Radiobutton( + frameTheme, variable=self.themeIsBuiltin, value=1, + command=self.SetThemeType, text='a Built-in Theme') + self.radioThemeCustom = Radiobutton( + frameTheme, variable=self.themeIsBuiltin, value=0, + command=self.SetThemeType, text='a Custom Theme') + self.optMenuThemeBuiltin = DynOptionMenu( + frameTheme, self.builtinTheme, None, command=None) + self.optMenuThemeCustom=DynOptionMenu( + frameTheme, self.customTheme, None, command=None) + self.buttonDeleteCustomTheme=Button( + frameTheme, text='Delete Custom Theme', + command=self.DeleteCustomTheme) + self.new_custom_theme = Label(frameTheme, bd=2) + + ##widget packing + #body + frameCustom.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) + frameTheme.pack(side=LEFT, padx=5, pady=5, fill=Y) + #frameCustom + self.frameColourSet.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=X) + frameFgBg.pack(side=TOP, padx=5, pady=0) + self.textHighlightSample.pack( + side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) + buttonSetColour.pack(side=TOP, expand=TRUE, fill=X, padx=8, pady=4) + self.optMenuHighlightTarget.pack( + side=TOP, expand=TRUE, fill=X, padx=8, pady=3) + self.radioFg.pack(side=LEFT, anchor=E) + self.radioBg.pack(side=RIGHT, anchor=W) + buttonSaveCustomTheme.pack(side=BOTTOM, fill=X, padx=5, pady=5) + #frameTheme + labelTypeTitle.pack(side=TOP, anchor=W, padx=5, pady=5) + self.radioThemeBuiltin.pack(side=TOP, anchor=W, padx=5) + self.radioThemeCustom.pack(side=TOP, anchor=W, padx=5, pady=2) + self.optMenuThemeBuiltin.pack(side=TOP, fill=X, padx=5, pady=5) + self.optMenuThemeCustom.pack(side=TOP, fill=X, anchor=W, padx=5, pady=5) + self.buttonDeleteCustomTheme.pack(side=TOP, fill=X, padx=5, pady=5) + self.new_custom_theme.pack(side=TOP, fill=X, pady=5) + return frame + + def CreatePageKeys(self): + parent = self.parent + self.bindingTarget = StringVar(parent) + self.builtinKeys = StringVar(parent) + self.customKeys = StringVar(parent) + self.keysAreBuiltin = BooleanVar(parent) + self.keyBinding = StringVar(parent) + + ##widget creation + #body frame + frame = self.tabPages.pages['Keys'].frame + #body section frames + frameCustom = LabelFrame( + frame, borderwidth=2, relief=GROOVE, + text=' Custom Key Bindings ') + frameKeySets = LabelFrame( + frame, borderwidth=2, relief=GROOVE, text=' Key Set ') + #frameCustom + frameTarget = Frame(frameCustom) + labelTargetTitle = Label(frameTarget, text='Action - Key(s)') + scrollTargetY = Scrollbar(frameTarget) + scrollTargetX = Scrollbar(frameTarget, orient=HORIZONTAL) + self.listBindings = Listbox( + frameTarget, takefocus=FALSE, exportselection=FALSE) + self.listBindings.bind('', self.KeyBindingSelected) + scrollTargetY.config(command=self.listBindings.yview) + scrollTargetX.config(command=self.listBindings.xview) + self.listBindings.config(yscrollcommand=scrollTargetY.set) + self.listBindings.config(xscrollcommand=scrollTargetX.set) + self.buttonNewKeys = Button( + frameCustom, text='Get New Keys for Selection', + command=self.GetNewKeys, state=DISABLED) + #frameKeySets + frames = [Frame(frameKeySets, padx=2, pady=2, borderwidth=0) + for i in range(2)] + self.radioKeysBuiltin = Radiobutton( + frames[0], variable=self.keysAreBuiltin, value=1, + command=self.SetKeysType, text='Use a Built-in Key Set') + self.radioKeysCustom = Radiobutton( + frames[0], variable=self.keysAreBuiltin, value=0, + command=self.SetKeysType, text='Use a Custom Key Set') + self.optMenuKeysBuiltin = DynOptionMenu( + frames[0], self.builtinKeys, None, command=None) + self.optMenuKeysCustom = DynOptionMenu( + frames[0], self.customKeys, None, command=None) + self.buttonDeleteCustomKeys = Button( + frames[1], text='Delete Custom Key Set', + command=self.DeleteCustomKeys) + buttonSaveCustomKeys = Button( + frames[1], text='Save as New Custom Key Set', + command=self.SaveAsNewKeySet) + + ##widget packing + #body + frameCustom.pack(side=BOTTOM, padx=5, pady=5, expand=TRUE, fill=BOTH) + frameKeySets.pack(side=BOTTOM, padx=5, pady=5, fill=BOTH) + #frameCustom + self.buttonNewKeys.pack(side=BOTTOM, fill=X, padx=5, pady=5) + frameTarget.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) + #frame target + frameTarget.columnconfigure(0, weight=1) + frameTarget.rowconfigure(1, weight=1) + labelTargetTitle.grid(row=0, column=0, columnspan=2, sticky=W) + self.listBindings.grid(row=1, column=0, sticky=NSEW) + scrollTargetY.grid(row=1, column=1, sticky=NS) + scrollTargetX.grid(row=2, column=0, sticky=EW) + #frameKeySets + self.radioKeysBuiltin.grid(row=0, column=0, sticky=W+NS) + self.radioKeysCustom.grid(row=1, column=0, sticky=W+NS) + self.optMenuKeysBuiltin.grid(row=0, column=1, sticky=NSEW) + self.optMenuKeysCustom.grid(row=1, column=1, sticky=NSEW) + self.buttonDeleteCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2) + buttonSaveCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2) + frames[0].pack(side=TOP, fill=BOTH, expand=True) + frames[1].pack(side=TOP, fill=X, expand=True, pady=2) + return frame + + def CreatePageGeneral(self): + parent = self.parent + self.winWidth = StringVar(parent) + self.winHeight = StringVar(parent) + self.startupEdit = IntVar(parent) + self.autoSave = IntVar(parent) + self.encoding = StringVar(parent) + self.userHelpBrowser = BooleanVar(parent) + self.helpBrowser = StringVar(parent) + + #widget creation + #body + frame = self.tabPages.pages['General'].frame + #body section frames + frameRun = LabelFrame(frame, borderwidth=2, relief=GROOVE, + text=' Startup Preferences ') + frameSave = LabelFrame(frame, borderwidth=2, relief=GROOVE, + text=' Autosave Preferences ') + frameWinSize = Frame(frame, borderwidth=2, relief=GROOVE) + frameHelp = LabelFrame(frame, borderwidth=2, relief=GROOVE, + text=' Additional Help Sources ') + #frameRun + labelRunChoiceTitle = Label(frameRun, text='At Startup') + radioStartupEdit = Radiobutton( + frameRun, variable=self.startupEdit, value=1, + command=self.SetKeysType, text="Open Edit Window") + radioStartupShell = Radiobutton( + frameRun, variable=self.startupEdit, value=0, + command=self.SetKeysType, text='Open Shell Window') + #frameSave + labelRunSaveTitle = Label(frameSave, text='At Start of Run (F5) ') + radioSaveAsk = Radiobutton( + frameSave, variable=self.autoSave, value=0, + command=self.SetKeysType, text="Prompt to Save") + radioSaveAuto = Radiobutton( + frameSave, variable=self.autoSave, value=1, + command=self.SetKeysType, text='No Prompt') + #frameWinSize + labelWinSizeTitle = Label( + frameWinSize, text='Initial Window Size (in characters)') + labelWinWidthTitle = Label(frameWinSize, text='Width') + entryWinWidth = Entry( + frameWinSize, textvariable=self.winWidth, width=3) + labelWinHeightTitle = Label(frameWinSize, text='Height') + entryWinHeight = Entry( + frameWinSize, textvariable=self.winHeight, width=3) + #frameHelp + frameHelpList = Frame(frameHelp) + frameHelpListButtons = Frame(frameHelpList) + scrollHelpList = Scrollbar(frameHelpList) + self.listHelp = Listbox( + frameHelpList, height=5, takefocus=FALSE, + exportselection=FALSE) + scrollHelpList.config(command=self.listHelp.yview) + self.listHelp.config(yscrollcommand=scrollHelpList.set) + self.listHelp.bind('', self.HelpSourceSelected) + self.buttonHelpListEdit = Button( + frameHelpListButtons, text='Edit', state=DISABLED, + width=8, command=self.HelpListItemEdit) + self.buttonHelpListAdd = Button( + frameHelpListButtons, text='Add', + width=8, command=self.HelpListItemAdd) + self.buttonHelpListRemove = Button( + frameHelpListButtons, text='Remove', state=DISABLED, + width=8, command=self.HelpListItemRemove) + + #widget packing + #body + frameRun.pack(side=TOP, padx=5, pady=5, fill=X) + frameSave.pack(side=TOP, padx=5, pady=5, fill=X) + frameWinSize.pack(side=TOP, padx=5, pady=5, fill=X) + frameHelp.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) + #frameRun + labelRunChoiceTitle.pack(side=LEFT, anchor=W, padx=5, pady=5) + radioStartupShell.pack(side=RIGHT, anchor=W, padx=5, pady=5) + radioStartupEdit.pack(side=RIGHT, anchor=W, padx=5, pady=5) + #frameSave + labelRunSaveTitle.pack(side=LEFT, anchor=W, padx=5, pady=5) + radioSaveAuto.pack(side=RIGHT, anchor=W, padx=5, pady=5) + radioSaveAsk.pack(side=RIGHT, anchor=W, padx=5, pady=5) + #frameWinSize + labelWinSizeTitle.pack(side=LEFT, anchor=W, padx=5, pady=5) + entryWinHeight.pack(side=RIGHT, anchor=E, padx=10, pady=5) + labelWinHeightTitle.pack(side=RIGHT, anchor=E, pady=5) + entryWinWidth.pack(side=RIGHT, anchor=E, padx=10, pady=5) + labelWinWidthTitle.pack(side=RIGHT, anchor=E, pady=5) + #frameHelp + frameHelpListButtons.pack(side=RIGHT, padx=5, pady=5, fill=Y) + frameHelpList.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) + scrollHelpList.pack(side=RIGHT, anchor=W, fill=Y) + self.listHelp.pack(side=LEFT, anchor=E, expand=TRUE, fill=BOTH) + self.buttonHelpListEdit.pack(side=TOP, anchor=W, pady=5) + self.buttonHelpListAdd.pack(side=TOP, anchor=W) + self.buttonHelpListRemove.pack(side=TOP, anchor=W, pady=5) + return frame + + def AttachVarCallbacks(self): + self.fontSize.trace_variable('w', self.VarChanged_font) + self.fontName.trace_variable('w', self.VarChanged_font) + self.fontBold.trace_variable('w', self.VarChanged_font) + self.spaceNum.trace_variable('w', self.VarChanged_spaceNum) + self.colour.trace_variable('w', self.VarChanged_colour) + self.builtinTheme.trace_variable('w', self.VarChanged_builtinTheme) + self.customTheme.trace_variable('w', self.VarChanged_customTheme) + self.themeIsBuiltin.trace_variable('w', self.VarChanged_themeIsBuiltin) + self.highlightTarget.trace_variable('w', self.VarChanged_highlightTarget) + self.keyBinding.trace_variable('w', self.VarChanged_keyBinding) + self.builtinKeys.trace_variable('w', self.VarChanged_builtinKeys) + self.customKeys.trace_variable('w', self.VarChanged_customKeys) + self.keysAreBuiltin.trace_variable('w', self.VarChanged_keysAreBuiltin) + self.winWidth.trace_variable('w', self.VarChanged_winWidth) + self.winHeight.trace_variable('w', self.VarChanged_winHeight) + self.startupEdit.trace_variable('w', self.VarChanged_startupEdit) + self.autoSave.trace_variable('w', self.VarChanged_autoSave) + self.encoding.trace_variable('w', self.VarChanged_encoding) + + def remove_var_callbacks(self): + "Remove callbacks to prevent memory leaks." + for var in ( + self.fontSize, self.fontName, self.fontBold, + self.spaceNum, self.colour, self.builtinTheme, + self.customTheme, self.themeIsBuiltin, self.highlightTarget, + self.keyBinding, self.builtinKeys, self.customKeys, + self.keysAreBuiltin, self.winWidth, self.winHeight, + self.startupEdit, self.autoSave, self.encoding,): + var.trace_vdelete('w', var.trace_vinfo()[0][1]) + + def VarChanged_font(self, *params): + '''When one font attribute changes, save them all, as they are + not independent from each other. In particular, when we are + overriding the default font, we need to write out everything. + ''' + value = self.fontName.get() + self.AddChangedItem('main', 'EditorWindow', 'font', value) + value = self.fontSize.get() + self.AddChangedItem('main', 'EditorWindow', 'font-size', value) + value = self.fontBold.get() + self.AddChangedItem('main', 'EditorWindow', 'font-bold', value) + + def VarChanged_spaceNum(self, *params): + value = self.spaceNum.get() + self.AddChangedItem('main', 'Indent', 'num-spaces', value) + + def VarChanged_colour(self, *params): + self.OnNewColourSet() + + def VarChanged_builtinTheme(self, *params): + value = self.builtinTheme.get() + if value == 'IDLE Dark': + if idleConf.GetOption('main', 'Theme', 'name') != 'IDLE New': + self.AddChangedItem('main', 'Theme', 'name', 'IDLE Classic') + self.AddChangedItem('main', 'Theme', 'name2', value) + self.new_custom_theme.config(text='New theme, see Help', + fg='#500000') + else: + self.AddChangedItem('main', 'Theme', 'name', value) + self.AddChangedItem('main', 'Theme', 'name2', '') + self.new_custom_theme.config(text='', fg='black') + self.PaintThemeSample() + + def VarChanged_customTheme(self, *params): + value = self.customTheme.get() + if value != '- no custom themes -': + self.AddChangedItem('main', 'Theme', 'name', value) + self.PaintThemeSample() + + def VarChanged_themeIsBuiltin(self, *params): + value = self.themeIsBuiltin.get() + self.AddChangedItem('main', 'Theme', 'default', value) + if value: + self.VarChanged_builtinTheme() + else: + self.VarChanged_customTheme() + + def VarChanged_highlightTarget(self, *params): + self.SetHighlightTarget() + + def VarChanged_keyBinding(self, *params): + value = self.keyBinding.get() + keySet = self.customKeys.get() + event = self.listBindings.get(ANCHOR).split()[0] + if idleConf.IsCoreBinding(event): + #this is a core keybinding + self.AddChangedItem('keys', keySet, event, value) + else: #this is an extension key binding + extName = idleConf.GetExtnNameForEvent(event) + extKeybindSection = extName + '_cfgBindings' + self.AddChangedItem('extensions', extKeybindSection, event, value) + + def VarChanged_builtinKeys(self, *params): + value = self.builtinKeys.get() + self.AddChangedItem('main', 'Keys', 'name', value) + self.LoadKeysList(value) + + def VarChanged_customKeys(self, *params): + value = self.customKeys.get() + if value != '- no custom keys -': + self.AddChangedItem('main', 'Keys', 'name', value) + self.LoadKeysList(value) + + def VarChanged_keysAreBuiltin(self, *params): + value = self.keysAreBuiltin.get() + self.AddChangedItem('main', 'Keys', 'default', value) + if value: + self.VarChanged_builtinKeys() + else: + self.VarChanged_customKeys() + + def VarChanged_winWidth(self, *params): + value = self.winWidth.get() + self.AddChangedItem('main', 'EditorWindow', 'width', value) + + def VarChanged_winHeight(self, *params): + value = self.winHeight.get() + self.AddChangedItem('main', 'EditorWindow', 'height', value) + + def VarChanged_startupEdit(self, *params): + value = self.startupEdit.get() + self.AddChangedItem('main', 'General', 'editor-on-startup', value) + + def VarChanged_autoSave(self, *params): + value = self.autoSave.get() + self.AddChangedItem('main', 'General', 'autosave', value) + + def VarChanged_encoding(self, *params): + value = self.encoding.get() + self.AddChangedItem('main', 'EditorWindow', 'encoding', value) + + def ResetChangedItems(self): + #When any config item is changed in this dialog, an entry + #should be made in the relevant section (config type) of this + #dictionary. The key should be the config file section name and the + #value a dictionary, whose key:value pairs are item=value pairs for + #that config file section. + self.changedItems = {'main':{}, 'highlight':{}, 'keys':{}, + 'extensions':{}} + + def AddChangedItem(self, typ, section, item, value): + value = str(value) #make sure we use a string + if section not in self.changedItems[typ]: + self.changedItems[typ][section] = {} + self.changedItems[typ][section][item] = value + + def GetDefaultItems(self): + dItems={'main':{}, 'highlight':{}, 'keys':{}, 'extensions':{}} + for configType in dItems: + sections = idleConf.GetSectionList('default', configType) + for section in sections: + dItems[configType][section] = {} + options = idleConf.defaultCfg[configType].GetOptionList(section) + for option in options: + dItems[configType][section][option] = ( + idleConf.defaultCfg[configType].Get(section, option)) + return dItems + + def SetThemeType(self): + if self.themeIsBuiltin.get(): + self.optMenuThemeBuiltin.config(state=NORMAL) + self.optMenuThemeCustom.config(state=DISABLED) + self.buttonDeleteCustomTheme.config(state=DISABLED) + else: + self.optMenuThemeBuiltin.config(state=DISABLED) + self.radioThemeCustom.config(state=NORMAL) + self.optMenuThemeCustom.config(state=NORMAL) + self.buttonDeleteCustomTheme.config(state=NORMAL) + + def SetKeysType(self): + if self.keysAreBuiltin.get(): + self.optMenuKeysBuiltin.config(state=NORMAL) + self.optMenuKeysCustom.config(state=DISABLED) + self.buttonDeleteCustomKeys.config(state=DISABLED) + else: + self.optMenuKeysBuiltin.config(state=DISABLED) + self.radioKeysCustom.config(state=NORMAL) + self.optMenuKeysCustom.config(state=NORMAL) + self.buttonDeleteCustomKeys.config(state=NORMAL) + + def GetNewKeys(self): + listIndex = self.listBindings.index(ANCHOR) + binding = self.listBindings.get(listIndex) + bindName = binding.split()[0] #first part, up to first space + if self.keysAreBuiltin.get(): + currentKeySetName = self.builtinKeys.get() + else: + currentKeySetName = self.customKeys.get() + currentBindings = idleConf.GetCurrentKeySet() + if currentKeySetName in self.changedItems['keys']: #unsaved changes + keySetChanges = self.changedItems['keys'][currentKeySetName] + for event in keySetChanges: + currentBindings[event] = keySetChanges[event].split() + currentKeySequences = list(currentBindings.values()) + newKeys = GetKeysDialog(self, 'Get New Keys', bindName, + currentKeySequences).result + if newKeys: #new keys were specified + if self.keysAreBuiltin.get(): #current key set is a built-in + message = ('Your changes will be saved as a new Custom Key Set.' + ' Enter a name for your new Custom Key Set below.') + newKeySet = self.GetNewKeysName(message) + if not newKeySet: #user cancelled custom key set creation + self.listBindings.select_set(listIndex) + self.listBindings.select_anchor(listIndex) + return + else: #create new custom key set based on previously active key set + self.CreateNewKeySet(newKeySet) + self.listBindings.delete(listIndex) + self.listBindings.insert(listIndex, bindName+' - '+newKeys) + self.listBindings.select_set(listIndex) + self.listBindings.select_anchor(listIndex) + self.keyBinding.set(newKeys) + else: + self.listBindings.select_set(listIndex) + self.listBindings.select_anchor(listIndex) + + def GetNewKeysName(self, message): + usedNames = (idleConf.GetSectionList('user', 'keys') + + idleConf.GetSectionList('default', 'keys')) + newKeySet = GetCfgSectionNameDialog( + self, 'New Custom Key Set', message, usedNames).result + return newKeySet + + def SaveAsNewKeySet(self): + newKeysName = self.GetNewKeysName('New Key Set Name:') + if newKeysName: + self.CreateNewKeySet(newKeysName) + + def KeyBindingSelected(self, event): + self.buttonNewKeys.config(state=NORMAL) + + def CreateNewKeySet(self, newKeySetName): + #creates new custom key set based on the previously active key set, + #and makes the new key set active + if self.keysAreBuiltin.get(): + prevKeySetName = self.builtinKeys.get() + else: + prevKeySetName = self.customKeys.get() + prevKeys = idleConf.GetCoreKeys(prevKeySetName) + newKeys = {} + for event in prevKeys: #add key set to changed items + eventName = event[2:-2] #trim off the angle brackets + binding = ' '.join(prevKeys[event]) + newKeys[eventName] = binding + #handle any unsaved changes to prev key set + if prevKeySetName in self.changedItems['keys']: + keySetChanges = self.changedItems['keys'][prevKeySetName] + for event in keySetChanges: + newKeys[event] = keySetChanges[event] + #save the new theme + self.SaveNewKeySet(newKeySetName, newKeys) + #change gui over to the new key set + customKeyList = idleConf.GetSectionList('user', 'keys') + customKeyList.sort() + self.optMenuKeysCustom.SetMenu(customKeyList, newKeySetName) + self.keysAreBuiltin.set(0) + self.SetKeysType() + + def LoadKeysList(self, keySetName): + reselect = 0 + newKeySet = 0 + if self.listBindings.curselection(): + reselect = 1 + listIndex = self.listBindings.index(ANCHOR) + keySet = idleConf.GetKeySet(keySetName) + bindNames = list(keySet.keys()) + bindNames.sort() + self.listBindings.delete(0, END) + for bindName in bindNames: + key = ' '.join(keySet[bindName]) #make key(s) into a string + bindName = bindName[2:-2] #trim off the angle brackets + if keySetName in self.changedItems['keys']: + #handle any unsaved changes to this key set + if bindName in self.changedItems['keys'][keySetName]: + key = self.changedItems['keys'][keySetName][bindName] + self.listBindings.insert(END, bindName+' - '+key) + if reselect: + self.listBindings.see(listIndex) + self.listBindings.select_set(listIndex) + self.listBindings.select_anchor(listIndex) + + def DeleteCustomKeys(self): + keySetName=self.customKeys.get() + delmsg = 'Are you sure you wish to delete the key set %r ?' + if not tkMessageBox.askyesno( + 'Delete Key Set', delmsg % keySetName, parent=self): + return + #remove key set from config + idleConf.userCfg['keys'].remove_section(keySetName) + if keySetName in self.changedItems['keys']: + del(self.changedItems['keys'][keySetName]) + #write changes + idleConf.userCfg['keys'].Save() + #reload user key set list + itemList = idleConf.GetSectionList('user', 'keys') + itemList.sort() + if not itemList: + self.radioKeysCustom.config(state=DISABLED) + self.optMenuKeysCustom.SetMenu(itemList, '- no custom keys -') + else: + self.optMenuKeysCustom.SetMenu(itemList, itemList[0]) + #revert to default key set + self.keysAreBuiltin.set(idleConf.defaultCfg['main'].Get('Keys', 'default')) + self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys', 'name')) + #user can't back out of these changes, they must be applied now + self.Apply() + self.SetKeysType() + + def DeleteCustomTheme(self): + themeName = self.customTheme.get() + delmsg = 'Are you sure you wish to delete the theme %r ?' + if not tkMessageBox.askyesno( + 'Delete Theme', delmsg % themeName, parent=self): + return + #remove theme from config + idleConf.userCfg['highlight'].remove_section(themeName) + if themeName in self.changedItems['highlight']: + del(self.changedItems['highlight'][themeName]) + #write changes + idleConf.userCfg['highlight'].Save() + #reload user theme list + itemList = idleConf.GetSectionList('user', 'highlight') + itemList.sort() + if not itemList: + self.radioThemeCustom.config(state=DISABLED) + self.optMenuThemeCustom.SetMenu(itemList, '- no custom themes -') + else: + self.optMenuThemeCustom.SetMenu(itemList, itemList[0]) + #revert to default theme + self.themeIsBuiltin.set(idleConf.defaultCfg['main'].Get('Theme', 'default')) + self.builtinTheme.set(idleConf.defaultCfg['main'].Get('Theme', 'name')) + #user can't back out of these changes, they must be applied now + self.Apply() + self.SetThemeType() + + def GetColour(self): + target = self.highlightTarget.get() + prevColour = self.frameColourSet.cget('bg') + rgbTuplet, colourString = tkColorChooser.askcolor( + parent=self, title='Pick new colour for : '+target, + initialcolor=prevColour) + if colourString and (colourString != prevColour): + #user didn't cancel, and they chose a new colour + if self.themeIsBuiltin.get(): #current theme is a built-in + message = ('Your changes will be saved as a new Custom Theme. ' + 'Enter a name for your new Custom Theme below.') + newTheme = self.GetNewThemeName(message) + if not newTheme: #user cancelled custom theme creation + return + else: #create new custom theme based on previously active theme + self.CreateNewTheme(newTheme) + self.colour.set(colourString) + else: #current theme is user defined + self.colour.set(colourString) + + def OnNewColourSet(self): + newColour=self.colour.get() + self.frameColourSet.config(bg=newColour) #set sample + plane ='foreground' if self.fgHilite.get() else 'background' + sampleElement = self.themeElements[self.highlightTarget.get()][0] + self.textHighlightSample.tag_config(sampleElement, **{plane:newColour}) + theme = self.customTheme.get() + themeElement = sampleElement + '-' + plane + self.AddChangedItem('highlight', theme, themeElement, newColour) + + def GetNewThemeName(self, message): + usedNames = (idleConf.GetSectionList('user', 'highlight') + + idleConf.GetSectionList('default', 'highlight')) + newTheme = GetCfgSectionNameDialog( + self, 'New Custom Theme', message, usedNames).result + return newTheme + + def SaveAsNewTheme(self): + newThemeName = self.GetNewThemeName('New Theme Name:') + if newThemeName: + self.CreateNewTheme(newThemeName) + + def CreateNewTheme(self, newThemeName): + #creates new custom theme based on the previously active theme, + #and makes the new theme active + if self.themeIsBuiltin.get(): + themeType = 'default' + themeName = self.builtinTheme.get() + else: + themeType = 'user' + themeName = self.customTheme.get() + newTheme = idleConf.GetThemeDict(themeType, themeName) + #apply any of the old theme's unsaved changes to the new theme + if themeName in self.changedItems['highlight']: + themeChanges = self.changedItems['highlight'][themeName] + for element in themeChanges: + newTheme[element] = themeChanges[element] + #save the new theme + self.SaveNewTheme(newThemeName, newTheme) + #change gui over to the new theme + customThemeList = idleConf.GetSectionList('user', 'highlight') + customThemeList.sort() + self.optMenuThemeCustom.SetMenu(customThemeList, newThemeName) + self.themeIsBuiltin.set(0) + self.SetThemeType() + + def OnListFontButtonRelease(self, event): + font = self.listFontName.get(ANCHOR) + self.fontName.set(font.lower()) + self.SetFontSample() + + def SetFontSample(self, event=None): + fontName = self.fontName.get() + fontWeight = tkFont.BOLD if self.fontBold.get() else tkFont.NORMAL + newFont = (fontName, self.fontSize.get(), fontWeight) + self.labelFontSample.config(font=newFont) + self.textHighlightSample.configure(font=newFont) + + def SetHighlightTarget(self): + if self.highlightTarget.get() == 'Cursor': #bg not possible + self.radioFg.config(state=DISABLED) + self.radioBg.config(state=DISABLED) + self.fgHilite.set(1) + else: #both fg and bg can be set + self.radioFg.config(state=NORMAL) + self.radioBg.config(state=NORMAL) + self.fgHilite.set(1) + self.SetColourSample() + + def SetColourSampleBinding(self, *args): + self.SetColourSample() + + def SetColourSample(self): + #set the colour smaple area + tag = self.themeElements[self.highlightTarget.get()][0] + plane = 'foreground' if self.fgHilite.get() else 'background' + colour = self.textHighlightSample.tag_cget(tag, plane) + self.frameColourSet.config(bg=colour) + + def PaintThemeSample(self): + if self.themeIsBuiltin.get(): #a default theme + theme = self.builtinTheme.get() + else: #a user theme + theme = self.customTheme.get() + for elementTitle in self.themeElements: + element = self.themeElements[elementTitle][0] + colours = idleConf.GetHighlight(theme, element) + if element == 'cursor': #cursor sample needs special painting + colours['background'] = idleConf.GetHighlight( + theme, 'normal', fgBg='bg') + #handle any unsaved changes to this theme + if theme in self.changedItems['highlight']: + themeDict = self.changedItems['highlight'][theme] + if element + '-foreground' in themeDict: + colours['foreground'] = themeDict[element + '-foreground'] + if element + '-background' in themeDict: + colours['background'] = themeDict[element + '-background'] + self.textHighlightSample.tag_config(element, **colours) + self.SetColourSample() + + def HelpSourceSelected(self, event): + self.SetHelpListButtonStates() + + def SetHelpListButtonStates(self): + if self.listHelp.size() < 1: #no entries in list + self.buttonHelpListEdit.config(state=DISABLED) + self.buttonHelpListRemove.config(state=DISABLED) + else: #there are some entries + if self.listHelp.curselection(): #there currently is a selection + self.buttonHelpListEdit.config(state=NORMAL) + self.buttonHelpListRemove.config(state=NORMAL) + else: #there currently is not a selection + self.buttonHelpListEdit.config(state=DISABLED) + self.buttonHelpListRemove.config(state=DISABLED) + + def HelpListItemAdd(self): + helpSource = GetHelpSourceDialog(self, 'New Help Source').result + if helpSource: + self.userHelpList.append((helpSource[0], helpSource[1])) + self.listHelp.insert(END, helpSource[0]) + self.UpdateUserHelpChangedItems() + self.SetHelpListButtonStates() + + def HelpListItemEdit(self): + itemIndex = self.listHelp.index(ANCHOR) + helpSource = self.userHelpList[itemIndex] + newHelpSource = GetHelpSourceDialog( + self, 'Edit Help Source', menuItem=helpSource[0], + filePath=helpSource[1]).result + if (not newHelpSource) or (newHelpSource == helpSource): + return #no changes + self.userHelpList[itemIndex] = newHelpSource + self.listHelp.delete(itemIndex) + self.listHelp.insert(itemIndex, newHelpSource[0]) + self.UpdateUserHelpChangedItems() + self.SetHelpListButtonStates() + + def HelpListItemRemove(self): + itemIndex = self.listHelp.index(ANCHOR) + del(self.userHelpList[itemIndex]) + self.listHelp.delete(itemIndex) + self.UpdateUserHelpChangedItems() + self.SetHelpListButtonStates() + + def UpdateUserHelpChangedItems(self): + "Clear and rebuild the HelpFiles section in self.changedItems" + self.changedItems['main']['HelpFiles'] = {} + for num in range(1, len(self.userHelpList) + 1): + self.AddChangedItem( + 'main', 'HelpFiles', str(num), + ';'.join(self.userHelpList[num-1][:2])) + + def LoadFontCfg(self): + ##base editor font selection list + fonts = list(tkFont.families(self)) + fonts.sort() + for font in fonts: + self.listFontName.insert(END, font) + configuredFont = idleConf.GetFont(self, 'main', 'EditorWindow') + fontName = configuredFont[0].lower() + fontSize = configuredFont[1] + fontBold = configuredFont[2]=='bold' + self.fontName.set(fontName) + lc_fonts = [s.lower() for s in fonts] + try: + currentFontIndex = lc_fonts.index(fontName) + self.listFontName.see(currentFontIndex) + self.listFontName.select_set(currentFontIndex) + self.listFontName.select_anchor(currentFontIndex) + except ValueError: + pass + ##font size dropdown + self.optMenuFontSize.SetMenu(('7', '8', '9', '10', '11', '12', '13', + '14', '16', '18', '20', '22'), fontSize ) + ##fontWeight + self.fontBold.set(fontBold) + ##font sample + self.SetFontSample() + + def LoadTabCfg(self): + ##indent sizes + spaceNum = idleConf.GetOption( + 'main', 'Indent', 'num-spaces', default=4, type='int') + self.spaceNum.set(spaceNum) + + def LoadThemeCfg(self): + ##current theme type radiobutton + self.themeIsBuiltin.set(idleConf.GetOption( + 'main', 'Theme', 'default', type='bool', default=1)) + ##currently set theme + currentOption = idleConf.CurrentTheme() + ##load available theme option menus + if self.themeIsBuiltin.get(): #default theme selected + itemList = idleConf.GetSectionList('default', 'highlight') + itemList.sort() + self.optMenuThemeBuiltin.SetMenu(itemList, currentOption) + itemList = idleConf.GetSectionList('user', 'highlight') + itemList.sort() + if not itemList: + self.radioThemeCustom.config(state=DISABLED) + self.customTheme.set('- no custom themes -') + else: + self.optMenuThemeCustom.SetMenu(itemList, itemList[0]) + else: #user theme selected + itemList = idleConf.GetSectionList('user', 'highlight') + itemList.sort() + self.optMenuThemeCustom.SetMenu(itemList, currentOption) + itemList = idleConf.GetSectionList('default', 'highlight') + itemList.sort() + self.optMenuThemeBuiltin.SetMenu(itemList, itemList[0]) + self.SetThemeType() + ##load theme element option menu + themeNames = list(self.themeElements.keys()) + themeNames.sort(key=lambda x: self.themeElements[x][1]) + self.optMenuHighlightTarget.SetMenu(themeNames, themeNames[0]) + self.PaintThemeSample() + self.SetHighlightTarget() + + def LoadKeyCfg(self): + ##current keys type radiobutton + self.keysAreBuiltin.set(idleConf.GetOption( + 'main', 'Keys', 'default', type='bool', default=1)) + ##currently set keys + currentOption = idleConf.CurrentKeys() + ##load available keyset option menus + if self.keysAreBuiltin.get(): #default theme selected + itemList = idleConf.GetSectionList('default', 'keys') + itemList.sort() + self.optMenuKeysBuiltin.SetMenu(itemList, currentOption) + itemList = idleConf.GetSectionList('user', 'keys') + itemList.sort() + if not itemList: + self.radioKeysCustom.config(state=DISABLED) + self.customKeys.set('- no custom keys -') + else: + self.optMenuKeysCustom.SetMenu(itemList, itemList[0]) + else: #user key set selected + itemList = idleConf.GetSectionList('user', 'keys') + itemList.sort() + self.optMenuKeysCustom.SetMenu(itemList, currentOption) + itemList = idleConf.GetSectionList('default', 'keys') + itemList.sort() + self.optMenuKeysBuiltin.SetMenu(itemList, itemList[0]) + self.SetKeysType() + ##load keyset element list + keySetName = idleConf.CurrentKeys() + self.LoadKeysList(keySetName) + + def LoadGeneralCfg(self): + #startup state + self.startupEdit.set(idleConf.GetOption( + 'main', 'General', 'editor-on-startup', default=1, type='bool')) + #autosave state + self.autoSave.set(idleConf.GetOption( + 'main', 'General', 'autosave', default=0, type='bool')) + #initial window size + self.winWidth.set(idleConf.GetOption( + 'main', 'EditorWindow', 'width', type='int')) + self.winHeight.set(idleConf.GetOption( + 'main', 'EditorWindow', 'height', type='int')) + # default source encoding + self.encoding.set(idleConf.GetOption( + 'main', 'EditorWindow', 'encoding', default='none')) + # additional help sources + self.userHelpList = idleConf.GetAllExtraHelpSourcesList() + for helpItem in self.userHelpList: + self.listHelp.insert(END, helpItem[0]) + self.SetHelpListButtonStates() + + def LoadConfigs(self): + """ + load configuration from default and user config files and populate + the widgets on the config dialog pages. + """ + ### fonts / tabs page + self.LoadFontCfg() + self.LoadTabCfg() + ### highlighting page + self.LoadThemeCfg() + ### keys page + self.LoadKeyCfg() + ### general page + self.LoadGeneralCfg() + # note: extension page handled separately + + def SaveNewKeySet(self, keySetName, keySet): + """ + save a newly created core key set. + keySetName - string, the name of the new key set + keySet - dictionary containing the new key set + """ + if not idleConf.userCfg['keys'].has_section(keySetName): + idleConf.userCfg['keys'].add_section(keySetName) + for event in keySet: + value = keySet[event] + idleConf.userCfg['keys'].SetOption(keySetName, event, value) + + def SaveNewTheme(self, themeName, theme): + """ + save a newly created theme. + themeName - string, the name of the new theme + theme - dictionary containing the new theme + """ + if not idleConf.userCfg['highlight'].has_section(themeName): + idleConf.userCfg['highlight'].add_section(themeName) + for element in theme: + value = theme[element] + idleConf.userCfg['highlight'].SetOption(themeName, element, value) + + def SetUserValue(self, configType, section, item, value): + if idleConf.defaultCfg[configType].has_option(section, item): + if idleConf.defaultCfg[configType].Get(section, item) == value: + #the setting equals a default setting, remove it from user cfg + return idleConf.userCfg[configType].RemoveOption(section, item) + #if we got here set the option + return idleConf.userCfg[configType].SetOption(section, item, value) + + def SaveAllChangedConfigs(self): + "Save configuration changes to the user config file." + idleConf.userCfg['main'].Save() + for configType in self.changedItems: + cfgTypeHasChanges = False + for section in self.changedItems[configType]: + if section == 'HelpFiles': + #this section gets completely replaced + idleConf.userCfg['main'].remove_section('HelpFiles') + cfgTypeHasChanges = True + for item in self.changedItems[configType][section]: + value = self.changedItems[configType][section][item] + if self.SetUserValue(configType, section, item, value): + cfgTypeHasChanges = True + if cfgTypeHasChanges: + idleConf.userCfg[configType].Save() + for configType in ['keys', 'highlight']: + # save these even if unchanged! + idleConf.userCfg[configType].Save() + self.ResetChangedItems() #clear the changed items dict + self.save_all_changed_extensions() # uses a different mechanism + + def DeactivateCurrentConfig(self): + #Before a config is saved, some cleanup of current + #config must be done - remove the previous keybindings + winInstances = self.parent.instance_dict.keys() + for instance in winInstances: + instance.RemoveKeybindings() + + def ActivateConfigChanges(self): + "Dynamically apply configuration changes" + winInstances = self.parent.instance_dict.keys() + for instance in winInstances: + instance.ResetColorizer() + instance.ResetFont() + instance.set_notabs_indentwidth() + instance.ApplyKeybindings() + instance.reset_help_menu_entries() + + def Cancel(self): + self.destroy() + + def Ok(self): + self.Apply() + self.destroy() + + def Apply(self): + self.DeactivateCurrentConfig() + self.SaveAllChangedConfigs() + self.ActivateConfigChanges() + + def Help(self): + page = self.tabPages._current_page + view_text(self, title='Help for IDLE preferences', + text=help_common+help_pages.get(page, '')) + + def CreatePageExtensions(self): + """Part of the config dialog used for configuring IDLE extensions. + + This code is generic - it works for any and all IDLE extensions. + + IDLE extensions save their configuration options using idleConf. + This code reads the current configuration using idleConf, supplies a + GUI interface to change the configuration values, and saves the + changes using idleConf. + + Not all changes take effect immediately - some may require restarting IDLE. + This depends on each extension's implementation. + + All values are treated as text, and it is up to the user to supply + reasonable values. The only exception to this are the 'enable*' options, + which are boolean, and can be toggled with a True/False button. + """ + parent = self.parent + frame = self.tabPages.pages['Extensions'].frame + self.ext_defaultCfg = idleConf.defaultCfg['extensions'] + self.ext_userCfg = idleConf.userCfg['extensions'] + self.is_int = self.register(is_int) + self.load_extensions() + # create widgets - a listbox shows all available extensions, with the + # controls for the extension selected in the listbox to the right + self.extension_names = StringVar(self) + frame.rowconfigure(0, weight=1) + frame.columnconfigure(2, weight=1) + self.extension_list = Listbox(frame, listvariable=self.extension_names, + selectmode='browse') + self.extension_list.bind('<>', self.extension_selected) + scroll = Scrollbar(frame, command=self.extension_list.yview) + self.extension_list.yscrollcommand=scroll.set + self.details_frame = LabelFrame(frame, width=250, height=250) + self.extension_list.grid(column=0, row=0, sticky='nws') + scroll.grid(column=1, row=0, sticky='ns') + self.details_frame.grid(column=2, row=0, sticky='nsew', padx=[10, 0]) + frame.configure(padx=10, pady=10) + self.config_frame = {} + self.current_extension = None + + self.outerframe = self # TEMPORARY + self.tabbed_page_set = self.extension_list # TEMPORARY + + # create the frame holding controls for each extension + ext_names = '' + for ext_name in sorted(self.extensions): + self.create_extension_frame(ext_name) + ext_names = ext_names + '{' + ext_name + '} ' + self.extension_names.set(ext_names) + self.extension_list.selection_set(0) + self.extension_selected(None) + + def load_extensions(self): + "Fill self.extensions with data from the default and user configs." + self.extensions = {} + for ext_name in idleConf.GetExtensions(active_only=False): + self.extensions[ext_name] = [] + + for ext_name in self.extensions: + opt_list = sorted(self.ext_defaultCfg.GetOptionList(ext_name)) + + # bring 'enable' options to the beginning of the list + enables = [opt_name for opt_name in opt_list + if opt_name.startswith('enable')] + for opt_name in enables: + opt_list.remove(opt_name) + opt_list = enables + opt_list + + for opt_name in opt_list: + def_str = self.ext_defaultCfg.Get( + ext_name, opt_name, raw=True) + try: + def_obj = {'True':True, 'False':False}[def_str] + opt_type = 'bool' + except KeyError: + try: + def_obj = int(def_str) + opt_type = 'int' + except ValueError: + def_obj = def_str + opt_type = None + try: + value = self.ext_userCfg.Get( + ext_name, opt_name, type=opt_type, raw=True, + default=def_obj) + except ValueError: # Need this until .Get fixed + value = def_obj # bad values overwritten by entry + var = StringVar(self) + var.set(str(value)) + + self.extensions[ext_name].append({'name': opt_name, + 'type': opt_type, + 'default': def_str, + 'value': value, + 'var': var, + }) + + def extension_selected(self, event): + newsel = self.extension_list.curselection() + if newsel: + newsel = self.extension_list.get(newsel) + if newsel is None or newsel != self.current_extension: + if self.current_extension: + self.details_frame.config(text='') + self.config_frame[self.current_extension].grid_forget() + self.current_extension = None + if newsel: + self.details_frame.config(text=newsel) + self.config_frame[newsel].grid(column=0, row=0, sticky='nsew') + self.current_extension = newsel + + def create_extension_frame(self, ext_name): + """Create a frame holding the widgets to configure one extension""" + f = VerticalScrolledFrame(self.details_frame, height=250, width=250) + self.config_frame[ext_name] = f + entry_area = f.interior + # create an entry for each configuration option + for row, opt in enumerate(self.extensions[ext_name]): + # create a row with a label and entry/checkbutton + label = Label(entry_area, text=opt['name']) + label.grid(row=row, column=0, sticky=NW) + var = opt['var'] + if opt['type'] == 'bool': + Checkbutton(entry_area, textvariable=var, variable=var, + onvalue='True', offvalue='False', + indicatoron=FALSE, selectcolor='', width=8 + ).grid(row=row, column=1, sticky=W, padx=7) + elif opt['type'] == 'int': + Entry(entry_area, textvariable=var, validate='key', + validatecommand=(self.is_int, '%P') + ).grid(row=row, column=1, sticky=NSEW, padx=7) + + else: + Entry(entry_area, textvariable=var + ).grid(row=row, column=1, sticky=NSEW, padx=7) + return + + def set_extension_value(self, section, opt): + name = opt['name'] + default = opt['default'] + value = opt['var'].get().strip() or default + opt['var'].set(value) + # if self.defaultCfg.has_section(section): + # Currently, always true; if not, indent to return + if (value == default): + return self.ext_userCfg.RemoveOption(section, name) + # set the option + return self.ext_userCfg.SetOption(section, name, value) + + def save_all_changed_extensions(self): + """Save configuration changes to the user config file.""" + has_changes = False + for ext_name in self.extensions: + options = self.extensions[ext_name] + for opt in options: + if self.set_extension_value(ext_name, opt): + has_changes = True + if has_changes: + self.ext_userCfg.Save() + + +help_common = '''\ +When you click either the Apply or Ok buttons, settings in this +dialog that are different from IDLE's default are saved in +a .idlerc directory in your home directory. Except as noted, +these changes apply to all versions of IDLE installed on this +machine. Some do not take affect until IDLE is restarted. +[Cancel] only cancels changes made since the last save. +''' +help_pages = { + 'Highlighting':''' +Highlighting: +The IDLE Dark color theme is new in October 2015. It can only +be used with older IDLE releases if it is saved as a custom +theme, with a different name. +''' +} + + +def is_int(s): + "Return 's is blank or represents an int'" + if not s: + return True + try: + int(s) + return True + except ValueError: + return False + + +class VerticalScrolledFrame(Frame): + """A pure Tkinter vertically scrollable frame. + + * Use the 'interior' attribute to place widgets inside the scrollable frame + * Construct and pack/place/grid normally + * This frame only allows vertical scrolling + """ + def __init__(self, parent, *args, **kw): + Frame.__init__(self, parent, *args, **kw) + + # create a canvas object and a vertical scrollbar for scrolling it + vscrollbar = Scrollbar(self, orient=VERTICAL) + vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE) + canvas = Canvas(self, bd=0, highlightthickness=0, + yscrollcommand=vscrollbar.set, width=240) + canvas.pack(side=LEFT, fill=BOTH, expand=TRUE) + vscrollbar.config(command=canvas.yview) + + # reset the view + canvas.xview_moveto(0) + canvas.yview_moveto(0) + + # create a frame inside the canvas which will be scrolled with it + self.interior = interior = Frame(canvas) + interior_id = canvas.create_window(0, 0, window=interior, anchor=NW) + + # track changes to the canvas and frame width and sync them, + # also updating the scrollbar + def _configure_interior(event): + # update the scrollbars to match the size of the inner frame + size = (interior.winfo_reqwidth(), interior.winfo_reqheight()) + canvas.config(scrollregion="0 0 %s %s" % size) + interior.bind('', _configure_interior) + + def _configure_canvas(event): + if interior.winfo_reqwidth() != canvas.winfo_width(): + # update the inner frame's width to fill the canvas + canvas.itemconfigure(interior_id, width=canvas.winfo_width()) + canvas.bind('', _configure_canvas) + + return + + +if __name__ == '__main__': + import unittest + unittest.main('idlelib.idle_test.test_configdialog', + verbosity=2, exit=False) + from idlelib.idle_test.htest import run + run(ConfigDialog) diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py deleted file mode 100644 index 1d7b4179b0..0000000000 --- a/Lib/idlelib/configdialog.py +++ /dev/null @@ -1,1435 +0,0 @@ -"""IDLE Configuration Dialog: support user customization of IDLE by GUI - -Customize font faces, sizes, and colorization attributes. Set indentation -defaults. Customize keybindings. Colorization and keybindings can be -saved as user defined sets. Select startup options including shell/editor -and default window size. Define additional help sources. - -Note that tab width in IDLE is currently fixed at eight due to Tk issues. -Refer to comments in EditorWindow autoindent code for details. - -""" -from tkinter import * -from tkinter.ttk import Scrollbar -import tkinter.messagebox as tkMessageBox -import tkinter.colorchooser as tkColorChooser -import tkinter.font as tkFont - -from idlelib.config import idleConf -from idlelib.dynoption import DynOptionMenu -from idlelib.config_key import GetKeysDialog -from idlelib.config_sec import GetCfgSectionNameDialog -from idlelib.config_help import GetHelpSourceDialog -from idlelib.tabbedpages import TabbedPageSet -from idlelib.textview import view_text -from idlelib import macosx - -class ConfigDialog(Toplevel): - - def __init__(self, parent, title='', _htest=False, _utest=False): - """ - _htest - bool, change box location when running htest - _utest - bool, don't wait_window when running unittest - """ - Toplevel.__init__(self, parent) - self.parent = parent - if _htest: - parent.instance_dict = {} - self.wm_withdraw() - - self.configure(borderwidth=5) - self.title(title or 'IDLE Preferences') - self.geometry( - "+%d+%d" % (parent.winfo_rootx() + 20, - parent.winfo_rooty() + (30 if not _htest else 150))) - #Theme Elements. Each theme element key is its display name. - #The first value of the tuple is the sample area tag name. - #The second value is the display name list sort index. - self.themeElements={ - 'Normal Text': ('normal', '00'), - 'Python Keywords': ('keyword', '01'), - 'Python Definitions': ('definition', '02'), - 'Python Builtins': ('builtin', '03'), - 'Python Comments': ('comment', '04'), - 'Python Strings': ('string', '05'), - 'Selected Text': ('hilite', '06'), - 'Found Text': ('hit', '07'), - 'Cursor': ('cursor', '08'), - 'Editor Breakpoint': ('break', '09'), - 'Shell Normal Text': ('console', '10'), - 'Shell Error Text': ('error', '11'), - 'Shell Stdout Text': ('stdout', '12'), - 'Shell Stderr Text': ('stderr', '13'), - } - self.ResetChangedItems() #load initial values in changed items dict - self.CreateWidgets() - self.resizable(height=FALSE, width=FALSE) - self.transient(parent) - self.grab_set() - self.protocol("WM_DELETE_WINDOW", self.Cancel) - self.tabPages.focus_set() - #key bindings for this dialog - #self.bind('', self.Cancel) #dismiss dialog, no save - #self.bind('', self.Apply) #apply changes, save - #self.bind('', self.Help) #context help - self.LoadConfigs() - self.AttachVarCallbacks() #avoid callbacks during LoadConfigs - - if not _utest: - self.wm_deiconify() - self.wait_window() - - def CreateWidgets(self): - self.tabPages = TabbedPageSet(self, - page_names=['Fonts/Tabs', 'Highlighting', 'Keys', 'General', - 'Extensions']) - self.tabPages.pack(side=TOP, expand=TRUE, fill=BOTH) - self.CreatePageFontTab() - self.CreatePageHighlight() - self.CreatePageKeys() - self.CreatePageGeneral() - self.CreatePageExtensions() - self.create_action_buttons().pack(side=BOTTOM) - - def create_action_buttons(self): - if macosx.isAquaTk(): - # Changing the default padding on OSX results in unreadable - # text in the buttons - paddingArgs = {} - else: - paddingArgs = {'padx':6, 'pady':3} - outer = Frame(self, pady=2) - buttons = Frame(outer, pady=2) - for txt, cmd in ( - ('Ok', self.Ok), - ('Apply', self.Apply), - ('Cancel', self.Cancel), - ('Help', self.Help)): - Button(buttons, text=txt, command=cmd, takefocus=FALSE, - **paddingArgs).pack(side=LEFT, padx=5) - # add space above buttons - Frame(outer, height=2, borderwidth=0).pack(side=TOP) - buttons.pack(side=BOTTOM) - return outer - - def CreatePageFontTab(self): - parent = self.parent - self.fontSize = StringVar(parent) - self.fontBold = BooleanVar(parent) - self.fontName = StringVar(parent) - self.spaceNum = IntVar(parent) - self.editFont = tkFont.Font(parent, ('courier', 10, 'normal')) - - ##widget creation - #body frame - frame = self.tabPages.pages['Fonts/Tabs'].frame - #body section frames - frameFont = LabelFrame( - frame, borderwidth=2, relief=GROOVE, text=' Base Editor Font ') - frameIndent = LabelFrame( - frame, borderwidth=2, relief=GROOVE, text=' Indentation Width ') - #frameFont - frameFontName = Frame(frameFont) - frameFontParam = Frame(frameFont) - labelFontNameTitle = Label( - frameFontName, justify=LEFT, text='Font Face :') - self.listFontName = Listbox( - frameFontName, height=5, takefocus=FALSE, exportselection=FALSE) - self.listFontName.bind( - '', self.OnListFontButtonRelease) - scrollFont = Scrollbar(frameFontName) - scrollFont.config(command=self.listFontName.yview) - self.listFontName.config(yscrollcommand=scrollFont.set) - labelFontSizeTitle = Label(frameFontParam, text='Size :') - self.optMenuFontSize = DynOptionMenu( - frameFontParam, self.fontSize, None, command=self.SetFontSample) - checkFontBold = Checkbutton( - frameFontParam, variable=self.fontBold, onvalue=1, - offvalue=0, text='Bold', command=self.SetFontSample) - frameFontSample = Frame(frameFont, relief=SOLID, borderwidth=1) - self.labelFontSample = Label( - frameFontSample, justify=LEFT, font=self.editFont, - text='AaBbCcDdEe\nFfGgHhIiJjK\n1234567890\n#:+=(){}[]') - #frameIndent - frameIndentSize = Frame(frameIndent) - labelSpaceNumTitle = Label( - frameIndentSize, justify=LEFT, - text='Python Standard: 4 Spaces!') - self.scaleSpaceNum = Scale( - frameIndentSize, variable=self.spaceNum, - orient='horizontal', tickinterval=2, from_=2, to=16) - - #widget packing - #body - frameFont.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) - frameIndent.pack(side=LEFT, padx=5, pady=5, fill=Y) - #frameFont - frameFontName.pack(side=TOP, padx=5, pady=5, fill=X) - frameFontParam.pack(side=TOP, padx=5, pady=5, fill=X) - labelFontNameTitle.pack(side=TOP, anchor=W) - self.listFontName.pack(side=LEFT, expand=TRUE, fill=X) - scrollFont.pack(side=LEFT, fill=Y) - labelFontSizeTitle.pack(side=LEFT, anchor=W) - self.optMenuFontSize.pack(side=LEFT, anchor=W) - checkFontBold.pack(side=LEFT, anchor=W, padx=20) - frameFontSample.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) - self.labelFontSample.pack(expand=TRUE, fill=BOTH) - #frameIndent - frameIndentSize.pack(side=TOP, fill=X) - labelSpaceNumTitle.pack(side=TOP, anchor=W, padx=5) - self.scaleSpaceNum.pack(side=TOP, padx=5, fill=X) - return frame - - def CreatePageHighlight(self): - parent = self.parent - self.builtinTheme = StringVar(parent) - self.customTheme = StringVar(parent) - self.fgHilite = BooleanVar(parent) - self.colour = StringVar(parent) - self.fontName = StringVar(parent) - self.themeIsBuiltin = BooleanVar(parent) - self.highlightTarget = StringVar(parent) - - ##widget creation - #body frame - frame = self.tabPages.pages['Highlighting'].frame - #body section frames - frameCustom = LabelFrame(frame, borderwidth=2, relief=GROOVE, - text=' Custom Highlighting ') - frameTheme = LabelFrame(frame, borderwidth=2, relief=GROOVE, - text=' Highlighting Theme ') - #frameCustom - self.textHighlightSample=Text( - frameCustom, relief=SOLID, borderwidth=1, - font=('courier', 12, ''), cursor='hand2', width=21, height=11, - takefocus=FALSE, highlightthickness=0, wrap=NONE) - text=self.textHighlightSample - text.bind('', lambda e: 'break') - text.bind('', lambda e: 'break') - textAndTags=( - ('#you can click here', 'comment'), ('\n', 'normal'), - ('#to choose items', 'comment'), ('\n', 'normal'), - ('def', 'keyword'), (' ', 'normal'), - ('func', 'definition'), ('(param):\n ', 'normal'), - ('"""string"""', 'string'), ('\n var0 = ', 'normal'), - ("'string'", 'string'), ('\n var1 = ', 'normal'), - ("'selected'", 'hilite'), ('\n var2 = ', 'normal'), - ("'found'", 'hit'), ('\n var3 = ', 'normal'), - ('list', 'builtin'), ('(', 'normal'), - ('None', 'keyword'), (')\n', 'normal'), - (' breakpoint("line")', 'break'), ('\n\n', 'normal'), - (' error ', 'error'), (' ', 'normal'), - ('cursor |', 'cursor'), ('\n ', 'normal'), - ('shell', 'console'), (' ', 'normal'), - ('stdout', 'stdout'), (' ', 'normal'), - ('stderr', 'stderr'), ('\n', 'normal')) - for txTa in textAndTags: - text.insert(END, txTa[0], txTa[1]) - for element in self.themeElements: - def tem(event, elem=element): - event.widget.winfo_toplevel().highlightTarget.set(elem) - text.tag_bind( - self.themeElements[element][0], '', tem) - text.config(state=DISABLED) - self.frameColourSet = Frame(frameCustom, relief=SOLID, borderwidth=1) - frameFgBg = Frame(frameCustom) - buttonSetColour = Button( - self.frameColourSet, text='Choose Colour for :', - command=self.GetColour, highlightthickness=0) - self.optMenuHighlightTarget = DynOptionMenu( - self.frameColourSet, self.highlightTarget, None, - highlightthickness=0) #, command=self.SetHighlightTargetBinding - self.radioFg = Radiobutton( - frameFgBg, variable=self.fgHilite, value=1, - text='Foreground', command=self.SetColourSampleBinding) - self.radioBg=Radiobutton( - frameFgBg, variable=self.fgHilite, value=0, - text='Background', command=self.SetColourSampleBinding) - self.fgHilite.set(1) - buttonSaveCustomTheme = Button( - frameCustom, text='Save as New Custom Theme', - command=self.SaveAsNewTheme) - #frameTheme - labelTypeTitle = Label(frameTheme, text='Select : ') - self.radioThemeBuiltin = Radiobutton( - frameTheme, variable=self.themeIsBuiltin, value=1, - command=self.SetThemeType, text='a Built-in Theme') - self.radioThemeCustom = Radiobutton( - frameTheme, variable=self.themeIsBuiltin, value=0, - command=self.SetThemeType, text='a Custom Theme') - self.optMenuThemeBuiltin = DynOptionMenu( - frameTheme, self.builtinTheme, None, command=None) - self.optMenuThemeCustom=DynOptionMenu( - frameTheme, self.customTheme, None, command=None) - self.buttonDeleteCustomTheme=Button( - frameTheme, text='Delete Custom Theme', - command=self.DeleteCustomTheme) - self.new_custom_theme = Label(frameTheme, bd=2) - - ##widget packing - #body - frameCustom.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) - frameTheme.pack(side=LEFT, padx=5, pady=5, fill=Y) - #frameCustom - self.frameColourSet.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=X) - frameFgBg.pack(side=TOP, padx=5, pady=0) - self.textHighlightSample.pack( - side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) - buttonSetColour.pack(side=TOP, expand=TRUE, fill=X, padx=8, pady=4) - self.optMenuHighlightTarget.pack( - side=TOP, expand=TRUE, fill=X, padx=8, pady=3) - self.radioFg.pack(side=LEFT, anchor=E) - self.radioBg.pack(side=RIGHT, anchor=W) - buttonSaveCustomTheme.pack(side=BOTTOM, fill=X, padx=5, pady=5) - #frameTheme - labelTypeTitle.pack(side=TOP, anchor=W, padx=5, pady=5) - self.radioThemeBuiltin.pack(side=TOP, anchor=W, padx=5) - self.radioThemeCustom.pack(side=TOP, anchor=W, padx=5, pady=2) - self.optMenuThemeBuiltin.pack(side=TOP, fill=X, padx=5, pady=5) - self.optMenuThemeCustom.pack(side=TOP, fill=X, anchor=W, padx=5, pady=5) - self.buttonDeleteCustomTheme.pack(side=TOP, fill=X, padx=5, pady=5) - self.new_custom_theme.pack(side=TOP, fill=X, pady=5) - return frame - - def CreatePageKeys(self): - parent = self.parent - self.bindingTarget = StringVar(parent) - self.builtinKeys = StringVar(parent) - self.customKeys = StringVar(parent) - self.keysAreBuiltin = BooleanVar(parent) - self.keyBinding = StringVar(parent) - - ##widget creation - #body frame - frame = self.tabPages.pages['Keys'].frame - #body section frames - frameCustom = LabelFrame( - frame, borderwidth=2, relief=GROOVE, - text=' Custom Key Bindings ') - frameKeySets = LabelFrame( - frame, borderwidth=2, relief=GROOVE, text=' Key Set ') - #frameCustom - frameTarget = Frame(frameCustom) - labelTargetTitle = Label(frameTarget, text='Action - Key(s)') - scrollTargetY = Scrollbar(frameTarget) - scrollTargetX = Scrollbar(frameTarget, orient=HORIZONTAL) - self.listBindings = Listbox( - frameTarget, takefocus=FALSE, exportselection=FALSE) - self.listBindings.bind('', self.KeyBindingSelected) - scrollTargetY.config(command=self.listBindings.yview) - scrollTargetX.config(command=self.listBindings.xview) - self.listBindings.config(yscrollcommand=scrollTargetY.set) - self.listBindings.config(xscrollcommand=scrollTargetX.set) - self.buttonNewKeys = Button( - frameCustom, text='Get New Keys for Selection', - command=self.GetNewKeys, state=DISABLED) - #frameKeySets - frames = [Frame(frameKeySets, padx=2, pady=2, borderwidth=0) - for i in range(2)] - self.radioKeysBuiltin = Radiobutton( - frames[0], variable=self.keysAreBuiltin, value=1, - command=self.SetKeysType, text='Use a Built-in Key Set') - self.radioKeysCustom = Radiobutton( - frames[0], variable=self.keysAreBuiltin, value=0, - command=self.SetKeysType, text='Use a Custom Key Set') - self.optMenuKeysBuiltin = DynOptionMenu( - frames[0], self.builtinKeys, None, command=None) - self.optMenuKeysCustom = DynOptionMenu( - frames[0], self.customKeys, None, command=None) - self.buttonDeleteCustomKeys = Button( - frames[1], text='Delete Custom Key Set', - command=self.DeleteCustomKeys) - buttonSaveCustomKeys = Button( - frames[1], text='Save as New Custom Key Set', - command=self.SaveAsNewKeySet) - - ##widget packing - #body - frameCustom.pack(side=BOTTOM, padx=5, pady=5, expand=TRUE, fill=BOTH) - frameKeySets.pack(side=BOTTOM, padx=5, pady=5, fill=BOTH) - #frameCustom - self.buttonNewKeys.pack(side=BOTTOM, fill=X, padx=5, pady=5) - frameTarget.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) - #frame target - frameTarget.columnconfigure(0, weight=1) - frameTarget.rowconfigure(1, weight=1) - labelTargetTitle.grid(row=0, column=0, columnspan=2, sticky=W) - self.listBindings.grid(row=1, column=0, sticky=NSEW) - scrollTargetY.grid(row=1, column=1, sticky=NS) - scrollTargetX.grid(row=2, column=0, sticky=EW) - #frameKeySets - self.radioKeysBuiltin.grid(row=0, column=0, sticky=W+NS) - self.radioKeysCustom.grid(row=1, column=0, sticky=W+NS) - self.optMenuKeysBuiltin.grid(row=0, column=1, sticky=NSEW) - self.optMenuKeysCustom.grid(row=1, column=1, sticky=NSEW) - self.buttonDeleteCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2) - buttonSaveCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2) - frames[0].pack(side=TOP, fill=BOTH, expand=True) - frames[1].pack(side=TOP, fill=X, expand=True, pady=2) - return frame - - def CreatePageGeneral(self): - parent = self.parent - self.winWidth = StringVar(parent) - self.winHeight = StringVar(parent) - self.startupEdit = IntVar(parent) - self.autoSave = IntVar(parent) - self.encoding = StringVar(parent) - self.userHelpBrowser = BooleanVar(parent) - self.helpBrowser = StringVar(parent) - - #widget creation - #body - frame = self.tabPages.pages['General'].frame - #body section frames - frameRun = LabelFrame(frame, borderwidth=2, relief=GROOVE, - text=' Startup Preferences ') - frameSave = LabelFrame(frame, borderwidth=2, relief=GROOVE, - text=' Autosave Preferences ') - frameWinSize = Frame(frame, borderwidth=2, relief=GROOVE) - frameHelp = LabelFrame(frame, borderwidth=2, relief=GROOVE, - text=' Additional Help Sources ') - #frameRun - labelRunChoiceTitle = Label(frameRun, text='At Startup') - radioStartupEdit = Radiobutton( - frameRun, variable=self.startupEdit, value=1, - command=self.SetKeysType, text="Open Edit Window") - radioStartupShell = Radiobutton( - frameRun, variable=self.startupEdit, value=0, - command=self.SetKeysType, text='Open Shell Window') - #frameSave - labelRunSaveTitle = Label(frameSave, text='At Start of Run (F5) ') - radioSaveAsk = Radiobutton( - frameSave, variable=self.autoSave, value=0, - command=self.SetKeysType, text="Prompt to Save") - radioSaveAuto = Radiobutton( - frameSave, variable=self.autoSave, value=1, - command=self.SetKeysType, text='No Prompt') - #frameWinSize - labelWinSizeTitle = Label( - frameWinSize, text='Initial Window Size (in characters)') - labelWinWidthTitle = Label(frameWinSize, text='Width') - entryWinWidth = Entry( - frameWinSize, textvariable=self.winWidth, width=3) - labelWinHeightTitle = Label(frameWinSize, text='Height') - entryWinHeight = Entry( - frameWinSize, textvariable=self.winHeight, width=3) - #frameHelp - frameHelpList = Frame(frameHelp) - frameHelpListButtons = Frame(frameHelpList) - scrollHelpList = Scrollbar(frameHelpList) - self.listHelp = Listbox( - frameHelpList, height=5, takefocus=FALSE, - exportselection=FALSE) - scrollHelpList.config(command=self.listHelp.yview) - self.listHelp.config(yscrollcommand=scrollHelpList.set) - self.listHelp.bind('', self.HelpSourceSelected) - self.buttonHelpListEdit = Button( - frameHelpListButtons, text='Edit', state=DISABLED, - width=8, command=self.HelpListItemEdit) - self.buttonHelpListAdd = Button( - frameHelpListButtons, text='Add', - width=8, command=self.HelpListItemAdd) - self.buttonHelpListRemove = Button( - frameHelpListButtons, text='Remove', state=DISABLED, - width=8, command=self.HelpListItemRemove) - - #widget packing - #body - frameRun.pack(side=TOP, padx=5, pady=5, fill=X) - frameSave.pack(side=TOP, padx=5, pady=5, fill=X) - frameWinSize.pack(side=TOP, padx=5, pady=5, fill=X) - frameHelp.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) - #frameRun - labelRunChoiceTitle.pack(side=LEFT, anchor=W, padx=5, pady=5) - radioStartupShell.pack(side=RIGHT, anchor=W, padx=5, pady=5) - radioStartupEdit.pack(side=RIGHT, anchor=W, padx=5, pady=5) - #frameSave - labelRunSaveTitle.pack(side=LEFT, anchor=W, padx=5, pady=5) - radioSaveAuto.pack(side=RIGHT, anchor=W, padx=5, pady=5) - radioSaveAsk.pack(side=RIGHT, anchor=W, padx=5, pady=5) - #frameWinSize - labelWinSizeTitle.pack(side=LEFT, anchor=W, padx=5, pady=5) - entryWinHeight.pack(side=RIGHT, anchor=E, padx=10, pady=5) - labelWinHeightTitle.pack(side=RIGHT, anchor=E, pady=5) - entryWinWidth.pack(side=RIGHT, anchor=E, padx=10, pady=5) - labelWinWidthTitle.pack(side=RIGHT, anchor=E, pady=5) - #frameHelp - frameHelpListButtons.pack(side=RIGHT, padx=5, pady=5, fill=Y) - frameHelpList.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) - scrollHelpList.pack(side=RIGHT, anchor=W, fill=Y) - self.listHelp.pack(side=LEFT, anchor=E, expand=TRUE, fill=BOTH) - self.buttonHelpListEdit.pack(side=TOP, anchor=W, pady=5) - self.buttonHelpListAdd.pack(side=TOP, anchor=W) - self.buttonHelpListRemove.pack(side=TOP, anchor=W, pady=5) - return frame - - def AttachVarCallbacks(self): - self.fontSize.trace_variable('w', self.VarChanged_font) - self.fontName.trace_variable('w', self.VarChanged_font) - self.fontBold.trace_variable('w', self.VarChanged_font) - self.spaceNum.trace_variable('w', self.VarChanged_spaceNum) - self.colour.trace_variable('w', self.VarChanged_colour) - self.builtinTheme.trace_variable('w', self.VarChanged_builtinTheme) - self.customTheme.trace_variable('w', self.VarChanged_customTheme) - self.themeIsBuiltin.trace_variable('w', self.VarChanged_themeIsBuiltin) - self.highlightTarget.trace_variable('w', self.VarChanged_highlightTarget) - self.keyBinding.trace_variable('w', self.VarChanged_keyBinding) - self.builtinKeys.trace_variable('w', self.VarChanged_builtinKeys) - self.customKeys.trace_variable('w', self.VarChanged_customKeys) - self.keysAreBuiltin.trace_variable('w', self.VarChanged_keysAreBuiltin) - self.winWidth.trace_variable('w', self.VarChanged_winWidth) - self.winHeight.trace_variable('w', self.VarChanged_winHeight) - self.startupEdit.trace_variable('w', self.VarChanged_startupEdit) - self.autoSave.trace_variable('w', self.VarChanged_autoSave) - self.encoding.trace_variable('w', self.VarChanged_encoding) - - def remove_var_callbacks(self): - "Remove callbacks to prevent memory leaks." - for var in ( - self.fontSize, self.fontName, self.fontBold, - self.spaceNum, self.colour, self.builtinTheme, - self.customTheme, self.themeIsBuiltin, self.highlightTarget, - self.keyBinding, self.builtinKeys, self.customKeys, - self.keysAreBuiltin, self.winWidth, self.winHeight, - self.startupEdit, self.autoSave, self.encoding,): - var.trace_vdelete('w', var.trace_vinfo()[0][1]) - - def VarChanged_font(self, *params): - '''When one font attribute changes, save them all, as they are - not independent from each other. In particular, when we are - overriding the default font, we need to write out everything. - ''' - value = self.fontName.get() - self.AddChangedItem('main', 'EditorWindow', 'font', value) - value = self.fontSize.get() - self.AddChangedItem('main', 'EditorWindow', 'font-size', value) - value = self.fontBold.get() - self.AddChangedItem('main', 'EditorWindow', 'font-bold', value) - - def VarChanged_spaceNum(self, *params): - value = self.spaceNum.get() - self.AddChangedItem('main', 'Indent', 'num-spaces', value) - - def VarChanged_colour(self, *params): - self.OnNewColourSet() - - def VarChanged_builtinTheme(self, *params): - value = self.builtinTheme.get() - if value == 'IDLE Dark': - if idleConf.GetOption('main', 'Theme', 'name') != 'IDLE New': - self.AddChangedItem('main', 'Theme', 'name', 'IDLE Classic') - self.AddChangedItem('main', 'Theme', 'name2', value) - self.new_custom_theme.config(text='New theme, see Help', - fg='#500000') - else: - self.AddChangedItem('main', 'Theme', 'name', value) - self.AddChangedItem('main', 'Theme', 'name2', '') - self.new_custom_theme.config(text='', fg='black') - self.PaintThemeSample() - - def VarChanged_customTheme(self, *params): - value = self.customTheme.get() - if value != '- no custom themes -': - self.AddChangedItem('main', 'Theme', 'name', value) - self.PaintThemeSample() - - def VarChanged_themeIsBuiltin(self, *params): - value = self.themeIsBuiltin.get() - self.AddChangedItem('main', 'Theme', 'default', value) - if value: - self.VarChanged_builtinTheme() - else: - self.VarChanged_customTheme() - - def VarChanged_highlightTarget(self, *params): - self.SetHighlightTarget() - - def VarChanged_keyBinding(self, *params): - value = self.keyBinding.get() - keySet = self.customKeys.get() - event = self.listBindings.get(ANCHOR).split()[0] - if idleConf.IsCoreBinding(event): - #this is a core keybinding - self.AddChangedItem('keys', keySet, event, value) - else: #this is an extension key binding - extName = idleConf.GetExtnNameForEvent(event) - extKeybindSection = extName + '_cfgBindings' - self.AddChangedItem('extensions', extKeybindSection, event, value) - - def VarChanged_builtinKeys(self, *params): - value = self.builtinKeys.get() - self.AddChangedItem('main', 'Keys', 'name', value) - self.LoadKeysList(value) - - def VarChanged_customKeys(self, *params): - value = self.customKeys.get() - if value != '- no custom keys -': - self.AddChangedItem('main', 'Keys', 'name', value) - self.LoadKeysList(value) - - def VarChanged_keysAreBuiltin(self, *params): - value = self.keysAreBuiltin.get() - self.AddChangedItem('main', 'Keys', 'default', value) - if value: - self.VarChanged_builtinKeys() - else: - self.VarChanged_customKeys() - - def VarChanged_winWidth(self, *params): - value = self.winWidth.get() - self.AddChangedItem('main', 'EditorWindow', 'width', value) - - def VarChanged_winHeight(self, *params): - value = self.winHeight.get() - self.AddChangedItem('main', 'EditorWindow', 'height', value) - - def VarChanged_startupEdit(self, *params): - value = self.startupEdit.get() - self.AddChangedItem('main', 'General', 'editor-on-startup', value) - - def VarChanged_autoSave(self, *params): - value = self.autoSave.get() - self.AddChangedItem('main', 'General', 'autosave', value) - - def VarChanged_encoding(self, *params): - value = self.encoding.get() - self.AddChangedItem('main', 'EditorWindow', 'encoding', value) - - def ResetChangedItems(self): - #When any config item is changed in this dialog, an entry - #should be made in the relevant section (config type) of this - #dictionary. The key should be the config file section name and the - #value a dictionary, whose key:value pairs are item=value pairs for - #that config file section. - self.changedItems = {'main':{}, 'highlight':{}, 'keys':{}, - 'extensions':{}} - - def AddChangedItem(self, typ, section, item, value): - value = str(value) #make sure we use a string - if section not in self.changedItems[typ]: - self.changedItems[typ][section] = {} - self.changedItems[typ][section][item] = value - - def GetDefaultItems(self): - dItems={'main':{}, 'highlight':{}, 'keys':{}, 'extensions':{}} - for configType in dItems: - sections = idleConf.GetSectionList('default', configType) - for section in sections: - dItems[configType][section] = {} - options = idleConf.defaultCfg[configType].GetOptionList(section) - for option in options: - dItems[configType][section][option] = ( - idleConf.defaultCfg[configType].Get(section, option)) - return dItems - - def SetThemeType(self): - if self.themeIsBuiltin.get(): - self.optMenuThemeBuiltin.config(state=NORMAL) - self.optMenuThemeCustom.config(state=DISABLED) - self.buttonDeleteCustomTheme.config(state=DISABLED) - else: - self.optMenuThemeBuiltin.config(state=DISABLED) - self.radioThemeCustom.config(state=NORMAL) - self.optMenuThemeCustom.config(state=NORMAL) - self.buttonDeleteCustomTheme.config(state=NORMAL) - - def SetKeysType(self): - if self.keysAreBuiltin.get(): - self.optMenuKeysBuiltin.config(state=NORMAL) - self.optMenuKeysCustom.config(state=DISABLED) - self.buttonDeleteCustomKeys.config(state=DISABLED) - else: - self.optMenuKeysBuiltin.config(state=DISABLED) - self.radioKeysCustom.config(state=NORMAL) - self.optMenuKeysCustom.config(state=NORMAL) - self.buttonDeleteCustomKeys.config(state=NORMAL) - - def GetNewKeys(self): - listIndex = self.listBindings.index(ANCHOR) - binding = self.listBindings.get(listIndex) - bindName = binding.split()[0] #first part, up to first space - if self.keysAreBuiltin.get(): - currentKeySetName = self.builtinKeys.get() - else: - currentKeySetName = self.customKeys.get() - currentBindings = idleConf.GetCurrentKeySet() - if currentKeySetName in self.changedItems['keys']: #unsaved changes - keySetChanges = self.changedItems['keys'][currentKeySetName] - for event in keySetChanges: - currentBindings[event] = keySetChanges[event].split() - currentKeySequences = list(currentBindings.values()) - newKeys = GetKeysDialog(self, 'Get New Keys', bindName, - currentKeySequences).result - if newKeys: #new keys were specified - if self.keysAreBuiltin.get(): #current key set is a built-in - message = ('Your changes will be saved as a new Custom Key Set.' - ' Enter a name for your new Custom Key Set below.') - newKeySet = self.GetNewKeysName(message) - if not newKeySet: #user cancelled custom key set creation - self.listBindings.select_set(listIndex) - self.listBindings.select_anchor(listIndex) - return - else: #create new custom key set based on previously active key set - self.CreateNewKeySet(newKeySet) - self.listBindings.delete(listIndex) - self.listBindings.insert(listIndex, bindName+' - '+newKeys) - self.listBindings.select_set(listIndex) - self.listBindings.select_anchor(listIndex) - self.keyBinding.set(newKeys) - else: - self.listBindings.select_set(listIndex) - self.listBindings.select_anchor(listIndex) - - def GetNewKeysName(self, message): - usedNames = (idleConf.GetSectionList('user', 'keys') + - idleConf.GetSectionList('default', 'keys')) - newKeySet = GetCfgSectionNameDialog( - self, 'New Custom Key Set', message, usedNames).result - return newKeySet - - def SaveAsNewKeySet(self): - newKeysName = self.GetNewKeysName('New Key Set Name:') - if newKeysName: - self.CreateNewKeySet(newKeysName) - - def KeyBindingSelected(self, event): - self.buttonNewKeys.config(state=NORMAL) - - def CreateNewKeySet(self, newKeySetName): - #creates new custom key set based on the previously active key set, - #and makes the new key set active - if self.keysAreBuiltin.get(): - prevKeySetName = self.builtinKeys.get() - else: - prevKeySetName = self.customKeys.get() - prevKeys = idleConf.GetCoreKeys(prevKeySetName) - newKeys = {} - for event in prevKeys: #add key set to changed items - eventName = event[2:-2] #trim off the angle brackets - binding = ' '.join(prevKeys[event]) - newKeys[eventName] = binding - #handle any unsaved changes to prev key set - if prevKeySetName in self.changedItems['keys']: - keySetChanges = self.changedItems['keys'][prevKeySetName] - for event in keySetChanges: - newKeys[event] = keySetChanges[event] - #save the new theme - self.SaveNewKeySet(newKeySetName, newKeys) - #change gui over to the new key set - customKeyList = idleConf.GetSectionList('user', 'keys') - customKeyList.sort() - self.optMenuKeysCustom.SetMenu(customKeyList, newKeySetName) - self.keysAreBuiltin.set(0) - self.SetKeysType() - - def LoadKeysList(self, keySetName): - reselect = 0 - newKeySet = 0 - if self.listBindings.curselection(): - reselect = 1 - listIndex = self.listBindings.index(ANCHOR) - keySet = idleConf.GetKeySet(keySetName) - bindNames = list(keySet.keys()) - bindNames.sort() - self.listBindings.delete(0, END) - for bindName in bindNames: - key = ' '.join(keySet[bindName]) #make key(s) into a string - bindName = bindName[2:-2] #trim off the angle brackets - if keySetName in self.changedItems['keys']: - #handle any unsaved changes to this key set - if bindName in self.changedItems['keys'][keySetName]: - key = self.changedItems['keys'][keySetName][bindName] - self.listBindings.insert(END, bindName+' - '+key) - if reselect: - self.listBindings.see(listIndex) - self.listBindings.select_set(listIndex) - self.listBindings.select_anchor(listIndex) - - def DeleteCustomKeys(self): - keySetName=self.customKeys.get() - delmsg = 'Are you sure you wish to delete the key set %r ?' - if not tkMessageBox.askyesno( - 'Delete Key Set', delmsg % keySetName, parent=self): - return - #remove key set from config - idleConf.userCfg['keys'].remove_section(keySetName) - if keySetName in self.changedItems['keys']: - del(self.changedItems['keys'][keySetName]) - #write changes - idleConf.userCfg['keys'].Save() - #reload user key set list - itemList = idleConf.GetSectionList('user', 'keys') - itemList.sort() - if not itemList: - self.radioKeysCustom.config(state=DISABLED) - self.optMenuKeysCustom.SetMenu(itemList, '- no custom keys -') - else: - self.optMenuKeysCustom.SetMenu(itemList, itemList[0]) - #revert to default key set - self.keysAreBuiltin.set(idleConf.defaultCfg['main'].Get('Keys', 'default')) - self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys', 'name')) - #user can't back out of these changes, they must be applied now - self.Apply() - self.SetKeysType() - - def DeleteCustomTheme(self): - themeName = self.customTheme.get() - delmsg = 'Are you sure you wish to delete the theme %r ?' - if not tkMessageBox.askyesno( - 'Delete Theme', delmsg % themeName, parent=self): - return - #remove theme from config - idleConf.userCfg['highlight'].remove_section(themeName) - if themeName in self.changedItems['highlight']: - del(self.changedItems['highlight'][themeName]) - #write changes - idleConf.userCfg['highlight'].Save() - #reload user theme list - itemList = idleConf.GetSectionList('user', 'highlight') - itemList.sort() - if not itemList: - self.radioThemeCustom.config(state=DISABLED) - self.optMenuThemeCustom.SetMenu(itemList, '- no custom themes -') - else: - self.optMenuThemeCustom.SetMenu(itemList, itemList[0]) - #revert to default theme - self.themeIsBuiltin.set(idleConf.defaultCfg['main'].Get('Theme', 'default')) - self.builtinTheme.set(idleConf.defaultCfg['main'].Get('Theme', 'name')) - #user can't back out of these changes, they must be applied now - self.Apply() - self.SetThemeType() - - def GetColour(self): - target = self.highlightTarget.get() - prevColour = self.frameColourSet.cget('bg') - rgbTuplet, colourString = tkColorChooser.askcolor( - parent=self, title='Pick new colour for : '+target, - initialcolor=prevColour) - if colourString and (colourString != prevColour): - #user didn't cancel, and they chose a new colour - if self.themeIsBuiltin.get(): #current theme is a built-in - message = ('Your changes will be saved as a new Custom Theme. ' - 'Enter a name for your new Custom Theme below.') - newTheme = self.GetNewThemeName(message) - if not newTheme: #user cancelled custom theme creation - return - else: #create new custom theme based on previously active theme - self.CreateNewTheme(newTheme) - self.colour.set(colourString) - else: #current theme is user defined - self.colour.set(colourString) - - def OnNewColourSet(self): - newColour=self.colour.get() - self.frameColourSet.config(bg=newColour) #set sample - plane ='foreground' if self.fgHilite.get() else 'background' - sampleElement = self.themeElements[self.highlightTarget.get()][0] - self.textHighlightSample.tag_config(sampleElement, **{plane:newColour}) - theme = self.customTheme.get() - themeElement = sampleElement + '-' + plane - self.AddChangedItem('highlight', theme, themeElement, newColour) - - def GetNewThemeName(self, message): - usedNames = (idleConf.GetSectionList('user', 'highlight') + - idleConf.GetSectionList('default', 'highlight')) - newTheme = GetCfgSectionNameDialog( - self, 'New Custom Theme', message, usedNames).result - return newTheme - - def SaveAsNewTheme(self): - newThemeName = self.GetNewThemeName('New Theme Name:') - if newThemeName: - self.CreateNewTheme(newThemeName) - - def CreateNewTheme(self, newThemeName): - #creates new custom theme based on the previously active theme, - #and makes the new theme active - if self.themeIsBuiltin.get(): - themeType = 'default' - themeName = self.builtinTheme.get() - else: - themeType = 'user' - themeName = self.customTheme.get() - newTheme = idleConf.GetThemeDict(themeType, themeName) - #apply any of the old theme's unsaved changes to the new theme - if themeName in self.changedItems['highlight']: - themeChanges = self.changedItems['highlight'][themeName] - for element in themeChanges: - newTheme[element] = themeChanges[element] - #save the new theme - self.SaveNewTheme(newThemeName, newTheme) - #change gui over to the new theme - customThemeList = idleConf.GetSectionList('user', 'highlight') - customThemeList.sort() - self.optMenuThemeCustom.SetMenu(customThemeList, newThemeName) - self.themeIsBuiltin.set(0) - self.SetThemeType() - - def OnListFontButtonRelease(self, event): - font = self.listFontName.get(ANCHOR) - self.fontName.set(font.lower()) - self.SetFontSample() - - def SetFontSample(self, event=None): - fontName = self.fontName.get() - fontWeight = tkFont.BOLD if self.fontBold.get() else tkFont.NORMAL - newFont = (fontName, self.fontSize.get(), fontWeight) - self.labelFontSample.config(font=newFont) - self.textHighlightSample.configure(font=newFont) - - def SetHighlightTarget(self): - if self.highlightTarget.get() == 'Cursor': #bg not possible - self.radioFg.config(state=DISABLED) - self.radioBg.config(state=DISABLED) - self.fgHilite.set(1) - else: #both fg and bg can be set - self.radioFg.config(state=NORMAL) - self.radioBg.config(state=NORMAL) - self.fgHilite.set(1) - self.SetColourSample() - - def SetColourSampleBinding(self, *args): - self.SetColourSample() - - def SetColourSample(self): - #set the colour smaple area - tag = self.themeElements[self.highlightTarget.get()][0] - plane = 'foreground' if self.fgHilite.get() else 'background' - colour = self.textHighlightSample.tag_cget(tag, plane) - self.frameColourSet.config(bg=colour) - - def PaintThemeSample(self): - if self.themeIsBuiltin.get(): #a default theme - theme = self.builtinTheme.get() - else: #a user theme - theme = self.customTheme.get() - for elementTitle in self.themeElements: - element = self.themeElements[elementTitle][0] - colours = idleConf.GetHighlight(theme, element) - if element == 'cursor': #cursor sample needs special painting - colours['background'] = idleConf.GetHighlight( - theme, 'normal', fgBg='bg') - #handle any unsaved changes to this theme - if theme in self.changedItems['highlight']: - themeDict = self.changedItems['highlight'][theme] - if element + '-foreground' in themeDict: - colours['foreground'] = themeDict[element + '-foreground'] - if element + '-background' in themeDict: - colours['background'] = themeDict[element + '-background'] - self.textHighlightSample.tag_config(element, **colours) - self.SetColourSample() - - def HelpSourceSelected(self, event): - self.SetHelpListButtonStates() - - def SetHelpListButtonStates(self): - if self.listHelp.size() < 1: #no entries in list - self.buttonHelpListEdit.config(state=DISABLED) - self.buttonHelpListRemove.config(state=DISABLED) - else: #there are some entries - if self.listHelp.curselection(): #there currently is a selection - self.buttonHelpListEdit.config(state=NORMAL) - self.buttonHelpListRemove.config(state=NORMAL) - else: #there currently is not a selection - self.buttonHelpListEdit.config(state=DISABLED) - self.buttonHelpListRemove.config(state=DISABLED) - - def HelpListItemAdd(self): - helpSource = GetHelpSourceDialog(self, 'New Help Source').result - if helpSource: - self.userHelpList.append((helpSource[0], helpSource[1])) - self.listHelp.insert(END, helpSource[0]) - self.UpdateUserHelpChangedItems() - self.SetHelpListButtonStates() - - def HelpListItemEdit(self): - itemIndex = self.listHelp.index(ANCHOR) - helpSource = self.userHelpList[itemIndex] - newHelpSource = GetHelpSourceDialog( - self, 'Edit Help Source', menuItem=helpSource[0], - filePath=helpSource[1]).result - if (not newHelpSource) or (newHelpSource == helpSource): - return #no changes - self.userHelpList[itemIndex] = newHelpSource - self.listHelp.delete(itemIndex) - self.listHelp.insert(itemIndex, newHelpSource[0]) - self.UpdateUserHelpChangedItems() - self.SetHelpListButtonStates() - - def HelpListItemRemove(self): - itemIndex = self.listHelp.index(ANCHOR) - del(self.userHelpList[itemIndex]) - self.listHelp.delete(itemIndex) - self.UpdateUserHelpChangedItems() - self.SetHelpListButtonStates() - - def UpdateUserHelpChangedItems(self): - "Clear and rebuild the HelpFiles section in self.changedItems" - self.changedItems['main']['HelpFiles'] = {} - for num in range(1, len(self.userHelpList) + 1): - self.AddChangedItem( - 'main', 'HelpFiles', str(num), - ';'.join(self.userHelpList[num-1][:2])) - - def LoadFontCfg(self): - ##base editor font selection list - fonts = list(tkFont.families(self)) - fonts.sort() - for font in fonts: - self.listFontName.insert(END, font) - configuredFont = idleConf.GetFont(self, 'main', 'EditorWindow') - fontName = configuredFont[0].lower() - fontSize = configuredFont[1] - fontBold = configuredFont[2]=='bold' - self.fontName.set(fontName) - lc_fonts = [s.lower() for s in fonts] - try: - currentFontIndex = lc_fonts.index(fontName) - self.listFontName.see(currentFontIndex) - self.listFontName.select_set(currentFontIndex) - self.listFontName.select_anchor(currentFontIndex) - except ValueError: - pass - ##font size dropdown - self.optMenuFontSize.SetMenu(('7', '8', '9', '10', '11', '12', '13', - '14', '16', '18', '20', '22'), fontSize ) - ##fontWeight - self.fontBold.set(fontBold) - ##font sample - self.SetFontSample() - - def LoadTabCfg(self): - ##indent sizes - spaceNum = idleConf.GetOption( - 'main', 'Indent', 'num-spaces', default=4, type='int') - self.spaceNum.set(spaceNum) - - def LoadThemeCfg(self): - ##current theme type radiobutton - self.themeIsBuiltin.set(idleConf.GetOption( - 'main', 'Theme', 'default', type='bool', default=1)) - ##currently set theme - currentOption = idleConf.CurrentTheme() - ##load available theme option menus - if self.themeIsBuiltin.get(): #default theme selected - itemList = idleConf.GetSectionList('default', 'highlight') - itemList.sort() - self.optMenuThemeBuiltin.SetMenu(itemList, currentOption) - itemList = idleConf.GetSectionList('user', 'highlight') - itemList.sort() - if not itemList: - self.radioThemeCustom.config(state=DISABLED) - self.customTheme.set('- no custom themes -') - else: - self.optMenuThemeCustom.SetMenu(itemList, itemList[0]) - else: #user theme selected - itemList = idleConf.GetSectionList('user', 'highlight') - itemList.sort() - self.optMenuThemeCustom.SetMenu(itemList, currentOption) - itemList = idleConf.GetSectionList('default', 'highlight') - itemList.sort() - self.optMenuThemeBuiltin.SetMenu(itemList, itemList[0]) - self.SetThemeType() - ##load theme element option menu - themeNames = list(self.themeElements.keys()) - themeNames.sort(key=lambda x: self.themeElements[x][1]) - self.optMenuHighlightTarget.SetMenu(themeNames, themeNames[0]) - self.PaintThemeSample() - self.SetHighlightTarget() - - def LoadKeyCfg(self): - ##current keys type radiobutton - self.keysAreBuiltin.set(idleConf.GetOption( - 'main', 'Keys', 'default', type='bool', default=1)) - ##currently set keys - currentOption = idleConf.CurrentKeys() - ##load available keyset option menus - if self.keysAreBuiltin.get(): #default theme selected - itemList = idleConf.GetSectionList('default', 'keys') - itemList.sort() - self.optMenuKeysBuiltin.SetMenu(itemList, currentOption) - itemList = idleConf.GetSectionList('user', 'keys') - itemList.sort() - if not itemList: - self.radioKeysCustom.config(state=DISABLED) - self.customKeys.set('- no custom keys -') - else: - self.optMenuKeysCustom.SetMenu(itemList, itemList[0]) - else: #user key set selected - itemList = idleConf.GetSectionList('user', 'keys') - itemList.sort() - self.optMenuKeysCustom.SetMenu(itemList, currentOption) - itemList = idleConf.GetSectionList('default', 'keys') - itemList.sort() - self.optMenuKeysBuiltin.SetMenu(itemList, itemList[0]) - self.SetKeysType() - ##load keyset element list - keySetName = idleConf.CurrentKeys() - self.LoadKeysList(keySetName) - - def LoadGeneralCfg(self): - #startup state - self.startupEdit.set(idleConf.GetOption( - 'main', 'General', 'editor-on-startup', default=1, type='bool')) - #autosave state - self.autoSave.set(idleConf.GetOption( - 'main', 'General', 'autosave', default=0, type='bool')) - #initial window size - self.winWidth.set(idleConf.GetOption( - 'main', 'EditorWindow', 'width', type='int')) - self.winHeight.set(idleConf.GetOption( - 'main', 'EditorWindow', 'height', type='int')) - # default source encoding - self.encoding.set(idleConf.GetOption( - 'main', 'EditorWindow', 'encoding', default='none')) - # additional help sources - self.userHelpList = idleConf.GetAllExtraHelpSourcesList() - for helpItem in self.userHelpList: - self.listHelp.insert(END, helpItem[0]) - self.SetHelpListButtonStates() - - def LoadConfigs(self): - """ - load configuration from default and user config files and populate - the widgets on the config dialog pages. - """ - ### fonts / tabs page - self.LoadFontCfg() - self.LoadTabCfg() - ### highlighting page - self.LoadThemeCfg() - ### keys page - self.LoadKeyCfg() - ### general page - self.LoadGeneralCfg() - # note: extension page handled separately - - def SaveNewKeySet(self, keySetName, keySet): - """ - save a newly created core key set. - keySetName - string, the name of the new key set - keySet - dictionary containing the new key set - """ - if not idleConf.userCfg['keys'].has_section(keySetName): - idleConf.userCfg['keys'].add_section(keySetName) - for event in keySet: - value = keySet[event] - idleConf.userCfg['keys'].SetOption(keySetName, event, value) - - def SaveNewTheme(self, themeName, theme): - """ - save a newly created theme. - themeName - string, the name of the new theme - theme - dictionary containing the new theme - """ - if not idleConf.userCfg['highlight'].has_section(themeName): - idleConf.userCfg['highlight'].add_section(themeName) - for element in theme: - value = theme[element] - idleConf.userCfg['highlight'].SetOption(themeName, element, value) - - def SetUserValue(self, configType, section, item, value): - if idleConf.defaultCfg[configType].has_option(section, item): - if idleConf.defaultCfg[configType].Get(section, item) == value: - #the setting equals a default setting, remove it from user cfg - return idleConf.userCfg[configType].RemoveOption(section, item) - #if we got here set the option - return idleConf.userCfg[configType].SetOption(section, item, value) - - def SaveAllChangedConfigs(self): - "Save configuration changes to the user config file." - idleConf.userCfg['main'].Save() - for configType in self.changedItems: - cfgTypeHasChanges = False - for section in self.changedItems[configType]: - if section == 'HelpFiles': - #this section gets completely replaced - idleConf.userCfg['main'].remove_section('HelpFiles') - cfgTypeHasChanges = True - for item in self.changedItems[configType][section]: - value = self.changedItems[configType][section][item] - if self.SetUserValue(configType, section, item, value): - cfgTypeHasChanges = True - if cfgTypeHasChanges: - idleConf.userCfg[configType].Save() - for configType in ['keys', 'highlight']: - # save these even if unchanged! - idleConf.userCfg[configType].Save() - self.ResetChangedItems() #clear the changed items dict - self.save_all_changed_extensions() # uses a different mechanism - - def DeactivateCurrentConfig(self): - #Before a config is saved, some cleanup of current - #config must be done - remove the previous keybindings - winInstances = self.parent.instance_dict.keys() - for instance in winInstances: - instance.RemoveKeybindings() - - def ActivateConfigChanges(self): - "Dynamically apply configuration changes" - winInstances = self.parent.instance_dict.keys() - for instance in winInstances: - instance.ResetColorizer() - instance.ResetFont() - instance.set_notabs_indentwidth() - instance.ApplyKeybindings() - instance.reset_help_menu_entries() - - def Cancel(self): - self.destroy() - - def Ok(self): - self.Apply() - self.destroy() - - def Apply(self): - self.DeactivateCurrentConfig() - self.SaveAllChangedConfigs() - self.ActivateConfigChanges() - - def Help(self): - page = self.tabPages._current_page - view_text(self, title='Help for IDLE preferences', - text=help_common+help_pages.get(page, '')) - - def CreatePageExtensions(self): - """Part of the config dialog used for configuring IDLE extensions. - - This code is generic - it works for any and all IDLE extensions. - - IDLE extensions save their configuration options using idleConf. - This code reads the current configuration using idleConf, supplies a - GUI interface to change the configuration values, and saves the - changes using idleConf. - - Not all changes take effect immediately - some may require restarting IDLE. - This depends on each extension's implementation. - - All values are treated as text, and it is up to the user to supply - reasonable values. The only exception to this are the 'enable*' options, - which are boolean, and can be toggled with a True/False button. - """ - parent = self.parent - frame = self.tabPages.pages['Extensions'].frame - self.ext_defaultCfg = idleConf.defaultCfg['extensions'] - self.ext_userCfg = idleConf.userCfg['extensions'] - self.is_int = self.register(is_int) - self.load_extensions() - # create widgets - a listbox shows all available extensions, with the - # controls for the extension selected in the listbox to the right - self.extension_names = StringVar(self) - frame.rowconfigure(0, weight=1) - frame.columnconfigure(2, weight=1) - self.extension_list = Listbox(frame, listvariable=self.extension_names, - selectmode='browse') - self.extension_list.bind('<>', self.extension_selected) - scroll = Scrollbar(frame, command=self.extension_list.yview) - self.extension_list.yscrollcommand=scroll.set - self.details_frame = LabelFrame(frame, width=250, height=250) - self.extension_list.grid(column=0, row=0, sticky='nws') - scroll.grid(column=1, row=0, sticky='ns') - self.details_frame.grid(column=2, row=0, sticky='nsew', padx=[10, 0]) - frame.configure(padx=10, pady=10) - self.config_frame = {} - self.current_extension = None - - self.outerframe = self # TEMPORARY - self.tabbed_page_set = self.extension_list # TEMPORARY - - # create the frame holding controls for each extension - ext_names = '' - for ext_name in sorted(self.extensions): - self.create_extension_frame(ext_name) - ext_names = ext_names + '{' + ext_name + '} ' - self.extension_names.set(ext_names) - self.extension_list.selection_set(0) - self.extension_selected(None) - - def load_extensions(self): - "Fill self.extensions with data from the default and user configs." - self.extensions = {} - for ext_name in idleConf.GetExtensions(active_only=False): - self.extensions[ext_name] = [] - - for ext_name in self.extensions: - opt_list = sorted(self.ext_defaultCfg.GetOptionList(ext_name)) - - # bring 'enable' options to the beginning of the list - enables = [opt_name for opt_name in opt_list - if opt_name.startswith('enable')] - for opt_name in enables: - opt_list.remove(opt_name) - opt_list = enables + opt_list - - for opt_name in opt_list: - def_str = self.ext_defaultCfg.Get( - ext_name, opt_name, raw=True) - try: - def_obj = {'True':True, 'False':False}[def_str] - opt_type = 'bool' - except KeyError: - try: - def_obj = int(def_str) - opt_type = 'int' - except ValueError: - def_obj = def_str - opt_type = None - try: - value = self.ext_userCfg.Get( - ext_name, opt_name, type=opt_type, raw=True, - default=def_obj) - except ValueError: # Need this until .Get fixed - value = def_obj # bad values overwritten by entry - var = StringVar(self) - var.set(str(value)) - - self.extensions[ext_name].append({'name': opt_name, - 'type': opt_type, - 'default': def_str, - 'value': value, - 'var': var, - }) - - def extension_selected(self, event): - newsel = self.extension_list.curselection() - if newsel: - newsel = self.extension_list.get(newsel) - if newsel is None or newsel != self.current_extension: - if self.current_extension: - self.details_frame.config(text='') - self.config_frame[self.current_extension].grid_forget() - self.current_extension = None - if newsel: - self.details_frame.config(text=newsel) - self.config_frame[newsel].grid(column=0, row=0, sticky='nsew') - self.current_extension = newsel - - def create_extension_frame(self, ext_name): - """Create a frame holding the widgets to configure one extension""" - f = VerticalScrolledFrame(self.details_frame, height=250, width=250) - self.config_frame[ext_name] = f - entry_area = f.interior - # create an entry for each configuration option - for row, opt in enumerate(self.extensions[ext_name]): - # create a row with a label and entry/checkbutton - label = Label(entry_area, text=opt['name']) - label.grid(row=row, column=0, sticky=NW) - var = opt['var'] - if opt['type'] == 'bool': - Checkbutton(entry_area, textvariable=var, variable=var, - onvalue='True', offvalue='False', - indicatoron=FALSE, selectcolor='', width=8 - ).grid(row=row, column=1, sticky=W, padx=7) - elif opt['type'] == 'int': - Entry(entry_area, textvariable=var, validate='key', - validatecommand=(self.is_int, '%P') - ).grid(row=row, column=1, sticky=NSEW, padx=7) - - else: - Entry(entry_area, textvariable=var - ).grid(row=row, column=1, sticky=NSEW, padx=7) - return - - def set_extension_value(self, section, opt): - name = opt['name'] - default = opt['default'] - value = opt['var'].get().strip() or default - opt['var'].set(value) - # if self.defaultCfg.has_section(section): - # Currently, always true; if not, indent to return - if (value == default): - return self.ext_userCfg.RemoveOption(section, name) - # set the option - return self.ext_userCfg.SetOption(section, name, value) - - def save_all_changed_extensions(self): - """Save configuration changes to the user config file.""" - has_changes = False - for ext_name in self.extensions: - options = self.extensions[ext_name] - for opt in options: - if self.set_extension_value(ext_name, opt): - has_changes = True - if has_changes: - self.ext_userCfg.Save() - - -help_common = '''\ -When you click either the Apply or Ok buttons, settings in this -dialog that are different from IDLE's default are saved in -a .idlerc directory in your home directory. Except as noted, -these changes apply to all versions of IDLE installed on this -machine. Some do not take affect until IDLE is restarted. -[Cancel] only cancels changes made since the last save. -''' -help_pages = { - 'Highlighting':''' -Highlighting: -The IDLE Dark color theme is new in October 2015. It can only -be used with older IDLE releases if it is saved as a custom -theme, with a different name. -''' -} - - -def is_int(s): - "Return 's is blank or represents an int'" - if not s: - return True - try: - int(s) - return True - except ValueError: - return False - - -class VerticalScrolledFrame(Frame): - """A pure Tkinter vertically scrollable frame. - - * Use the 'interior' attribute to place widgets inside the scrollable frame - * Construct and pack/place/grid normally - * This frame only allows vertical scrolling - """ - def __init__(self, parent, *args, **kw): - Frame.__init__(self, parent, *args, **kw) - - # create a canvas object and a vertical scrollbar for scrolling it - vscrollbar = Scrollbar(self, orient=VERTICAL) - vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE) - canvas = Canvas(self, bd=0, highlightthickness=0, - yscrollcommand=vscrollbar.set, width=240) - canvas.pack(side=LEFT, fill=BOTH, expand=TRUE) - vscrollbar.config(command=canvas.yview) - - # reset the view - canvas.xview_moveto(0) - canvas.yview_moveto(0) - - # create a frame inside the canvas which will be scrolled with it - self.interior = interior = Frame(canvas) - interior_id = canvas.create_window(0, 0, window=interior, anchor=NW) - - # track changes to the canvas and frame width and sync them, - # also updating the scrollbar - def _configure_interior(event): - # update the scrollbars to match the size of the inner frame - size = (interior.winfo_reqwidth(), interior.winfo_reqheight()) - canvas.config(scrollregion="0 0 %s %s" % size) - interior.bind('', _configure_interior) - - def _configure_canvas(event): - if interior.winfo_reqwidth() != canvas.winfo_width(): - # update the inner frame's width to fill the canvas - canvas.itemconfigure(interior_id, width=canvas.winfo_width()) - canvas.bind('', _configure_canvas) - - return - - -if __name__ == '__main__': - import unittest - unittest.main('idlelib.idle_test.test_configdialog', - verbosity=2, exit=False) - from idlelib.idle_test.htest import run - run(ConfigDialog) -- cgit v1.2.1 From 045c6ff433dd1684b0d233488e5496bdafa1642b Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Tue, 14 Jun 2016 00:55:09 -0400 Subject: Issue #27245: revert temporary rename --- Lib/idlelib/configDialog.py | 1439 ------------------------------------------- Lib/idlelib/configdialog.py | 1439 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1439 insertions(+), 1439 deletions(-) delete mode 100644 Lib/idlelib/configDialog.py create mode 100644 Lib/idlelib/configdialog.py (limited to 'Lib') diff --git a/Lib/idlelib/configDialog.py b/Lib/idlelib/configDialog.py deleted file mode 100644 index d19749a07e..0000000000 --- a/Lib/idlelib/configDialog.py +++ /dev/null @@ -1,1439 +0,0 @@ -"""IDLE Configuration Dialog: support user customization of IDLE by GUI - -Customize font faces, sizes, and colorization attributes. Set indentation -defaults. Customize keybindings. Colorization and keybindings can be -saved as user defined sets. Select startup options including shell/editor -and default window size. Define additional help sources. - -Note that tab width in IDLE is currently fixed at eight due to Tk issues. -Refer to comments in EditorWindow autoindent code for details. - -""" -from tkinter import * -from tkinter.ttk import Scrollbar -import tkinter.messagebox as tkMessageBox -import tkinter.colorchooser as tkColorChooser -import tkinter.font as tkFont - -from idlelib.config import idleConf -from idlelib.dynoption import DynOptionMenu -from idlelib.config_key import GetKeysDialog -from idlelib.config_sec import GetCfgSectionNameDialog -from idlelib.config_help import GetHelpSourceDialog -from idlelib.tabbedpages import TabbedPageSet -from idlelib.textview import view_text -from idlelib import macosx - -class ConfigDialog(Toplevel): - - def __init__(self, parent, title='', _htest=False, _utest=False): - """ - _htest - bool, change box location when running htest - _utest - bool, don't wait_window when running unittest - """ - Toplevel.__init__(self, parent) - self.parent = parent - if _htest: - parent.instance_dict = {} - self.wm_withdraw() - - self.configure(borderwidth=5) - self.title(title or 'IDLE Preferences') - self.geometry( - "+%d+%d" % (parent.winfo_rootx() + 20, - parent.winfo_rooty() + (30 if not _htest else 150))) - #Theme Elements. Each theme element key is its display name. - #The first value of the tuple is the sample area tag name. - #The second value is the display name list sort index. - self.themeElements={ - 'Normal Text': ('normal', '00'), - 'Python Keywords': ('keyword', '01'), - 'Python Definitions': ('definition', '02'), - 'Python Builtins': ('builtin', '03'), - 'Python Comments': ('comment', '04'), - 'Python Strings': ('string', '05'), - 'Selected Text': ('hilite', '06'), - 'Found Text': ('hit', '07'), - 'Cursor': ('cursor', '08'), - 'Editor Breakpoint': ('break', '09'), - 'Shell Normal Text': ('console', '10'), - 'Shell Error Text': ('error', '11'), - 'Shell Stdout Text': ('stdout', '12'), - 'Shell Stderr Text': ('stderr', '13'), - } - self.ResetChangedItems() #load initial values in changed items dict - self.CreateWidgets() - self.resizable(height=FALSE, width=FALSE) - self.transient(parent) - self.grab_set() - self.protocol("WM_DELETE_WINDOW", self.Cancel) - self.tabPages.focus_set() - #key bindings for this dialog - #self.bind('', self.Cancel) #dismiss dialog, no save - #self.bind('', self.Apply) #apply changes, save - #self.bind('', self.Help) #context help - self.LoadConfigs() - self.AttachVarCallbacks() #avoid callbacks during LoadConfigs - - if not _utest: - self.wm_deiconify() - self.wait_window() - - def CreateWidgets(self): - self.tabPages = TabbedPageSet(self, - page_names=['Fonts/Tabs', 'Highlighting', 'Keys', 'General', - 'Extensions']) - self.tabPages.pack(side=TOP, expand=TRUE, fill=BOTH) - self.CreatePageFontTab() - self.CreatePageHighlight() - self.CreatePageKeys() - self.CreatePageGeneral() - self.CreatePageExtensions() - self.create_action_buttons().pack(side=BOTTOM) - - def create_action_buttons(self): - if macosx.isAquaTk(): - # Changing the default padding on OSX results in unreadable - # text in the buttons - paddingArgs = {} - else: - paddingArgs = {'padx':6, 'pady':3} - outer = Frame(self, pady=2) - buttons = Frame(outer, pady=2) - for txt, cmd in ( - ('Ok', self.Ok), - ('Apply', self.Apply), - ('Cancel', self.Cancel), - ('Help', self.Help)): - Button(buttons, text=txt, command=cmd, takefocus=FALSE, - **paddingArgs).pack(side=LEFT, padx=5) - # add space above buttons - Frame(outer, height=2, borderwidth=0).pack(side=TOP) - buttons.pack(side=BOTTOM) - return outer - - def CreatePageFontTab(self): - parent = self.parent - self.fontSize = StringVar(parent) - self.fontBold = BooleanVar(parent) - self.fontName = StringVar(parent) - self.spaceNum = IntVar(parent) - self.editFont = tkFont.Font(parent, ('courier', 10, 'normal')) - - ##widget creation - #body frame - frame = self.tabPages.pages['Fonts/Tabs'].frame - #body section frames - frameFont = LabelFrame( - frame, borderwidth=2, relief=GROOVE, text=' Base Editor Font ') - frameIndent = LabelFrame( - frame, borderwidth=2, relief=GROOVE, text=' Indentation Width ') - #frameFont - frameFontName = Frame(frameFont) - frameFontParam = Frame(frameFont) - labelFontNameTitle = Label( - frameFontName, justify=LEFT, text='Font Face :') - self.listFontName = Listbox( - frameFontName, height=5, takefocus=FALSE, exportselection=FALSE) - self.listFontName.bind( - '', self.OnListFontButtonRelease) - scrollFont = Scrollbar(frameFontName) - scrollFont.config(command=self.listFontName.yview) - self.listFontName.config(yscrollcommand=scrollFont.set) - labelFontSizeTitle = Label(frameFontParam, text='Size :') - self.optMenuFontSize = DynOptionMenu( - frameFontParam, self.fontSize, None, command=self.SetFontSample) - checkFontBold = Checkbutton( - frameFontParam, variable=self.fontBold, onvalue=1, - offvalue=0, text='Bold', command=self.SetFontSample) - frameFontSample = Frame(frameFont, relief=SOLID, borderwidth=1) - self.labelFontSample = Label( - frameFontSample, justify=LEFT, font=self.editFont, - text='AaBbCcDdEe\nFfGgHhIiJjK\n1234567890\n#:+=(){}[]') - #frameIndent - frameIndentSize = Frame(frameIndent) - labelSpaceNumTitle = Label( - frameIndentSize, justify=LEFT, - text='Python Standard: 4 Spaces!') - self.scaleSpaceNum = Scale( - frameIndentSize, variable=self.spaceNum, - orient='horizontal', tickinterval=2, from_=2, to=16) - - #widget packing - #body - frameFont.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) - frameIndent.pack(side=LEFT, padx=5, pady=5, fill=Y) - #frameFont - frameFontName.pack(side=TOP, padx=5, pady=5, fill=X) - frameFontParam.pack(side=TOP, padx=5, pady=5, fill=X) - labelFontNameTitle.pack(side=TOP, anchor=W) - self.listFontName.pack(side=LEFT, expand=TRUE, fill=X) - scrollFont.pack(side=LEFT, fill=Y) - labelFontSizeTitle.pack(side=LEFT, anchor=W) - self.optMenuFontSize.pack(side=LEFT, anchor=W) - checkFontBold.pack(side=LEFT, anchor=W, padx=20) - frameFontSample.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) - self.labelFontSample.pack(expand=TRUE, fill=BOTH) - #frameIndent - frameIndentSize.pack(side=TOP, fill=X) - labelSpaceNumTitle.pack(side=TOP, anchor=W, padx=5) - self.scaleSpaceNum.pack(side=TOP, padx=5, fill=X) - return frame - - def CreatePageHighlight(self): - parent = self.parent - self.builtinTheme = StringVar(parent) - self.customTheme = StringVar(parent) - self.fgHilite = BooleanVar(parent) - self.colour = StringVar(parent) - self.fontName = StringVar(parent) - self.themeIsBuiltin = BooleanVar(parent) - self.highlightTarget = StringVar(parent) - - ##widget creation - #body frame - frame = self.tabPages.pages['Highlighting'].frame - #body section frames - frameCustom = LabelFrame(frame, borderwidth=2, relief=GROOVE, - text=' Custom Highlighting ') - frameTheme = LabelFrame(frame, borderwidth=2, relief=GROOVE, - text=' Highlighting Theme ') - #frameCustom - self.textHighlightSample=Text( - frameCustom, relief=SOLID, borderwidth=1, - font=('courier', 12, ''), cursor='hand2', width=21, height=11, - takefocus=FALSE, highlightthickness=0, wrap=NONE) - text=self.textHighlightSample - text.bind('', lambda e: 'break') - text.bind('', lambda e: 'break') - textAndTags=( - ('#you can click here', 'comment'), ('\n', 'normal'), - ('#to choose items', 'comment'), ('\n', 'normal'), - ('def', 'keyword'), (' ', 'normal'), - ('func', 'definition'), ('(param):\n ', 'normal'), - ('"""string"""', 'string'), ('\n var0 = ', 'normal'), - ("'string'", 'string'), ('\n var1 = ', 'normal'), - ("'selected'", 'hilite'), ('\n var2 = ', 'normal'), - ("'found'", 'hit'), ('\n var3 = ', 'normal'), - ('list', 'builtin'), ('(', 'normal'), - ('None', 'keyword'), (')\n', 'normal'), - (' breakpoint("line")', 'break'), ('\n\n', 'normal'), - (' error ', 'error'), (' ', 'normal'), - ('cursor |', 'cursor'), ('\n ', 'normal'), - ('shell', 'console'), (' ', 'normal'), - ('stdout', 'stdout'), (' ', 'normal'), - ('stderr', 'stderr'), ('\n', 'normal')) - for txTa in textAndTags: - text.insert(END, txTa[0], txTa[1]) - for element in self.themeElements: - def tem(event, elem=element): - event.widget.winfo_toplevel().highlightTarget.set(elem) - text.tag_bind( - self.themeElements[element][0], '', tem) - text.config(state=DISABLED) - self.frameColourSet = Frame(frameCustom, relief=SOLID, borderwidth=1) - frameFgBg = Frame(frameCustom) - buttonSetColour = Button( - self.frameColourSet, text='Choose Colour for :', - command=self.GetColour, highlightthickness=0) - self.optMenuHighlightTarget = DynOptionMenu( - self.frameColourSet, self.highlightTarget, None, - highlightthickness=0) #, command=self.SetHighlightTargetBinding - self.radioFg = Radiobutton( - frameFgBg, variable=self.fgHilite, value=1, - text='Foreground', command=self.SetColourSampleBinding) - self.radioBg=Radiobutton( - frameFgBg, variable=self.fgHilite, value=0, - text='Background', command=self.SetColourSampleBinding) - self.fgHilite.set(1) - buttonSaveCustomTheme = Button( - frameCustom, text='Save as New Custom Theme', - command=self.SaveAsNewTheme) - #frameTheme - labelTypeTitle = Label(frameTheme, text='Select : ') - self.radioThemeBuiltin = Radiobutton( - frameTheme, variable=self.themeIsBuiltin, value=1, - command=self.SetThemeType, text='a Built-in Theme') - self.radioThemeCustom = Radiobutton( - frameTheme, variable=self.themeIsBuiltin, value=0, - command=self.SetThemeType, text='a Custom Theme') - self.optMenuThemeBuiltin = DynOptionMenu( - frameTheme, self.builtinTheme, None, command=None) - self.optMenuThemeCustom=DynOptionMenu( - frameTheme, self.customTheme, None, command=None) - self.buttonDeleteCustomTheme=Button( - frameTheme, text='Delete Custom Theme', - command=self.DeleteCustomTheme) - self.new_custom_theme = Label(frameTheme, bd=2) - - ##widget packing - #body - frameCustom.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) - frameTheme.pack(side=LEFT, padx=5, pady=5, fill=Y) - #frameCustom - self.frameColourSet.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=X) - frameFgBg.pack(side=TOP, padx=5, pady=0) - self.textHighlightSample.pack( - side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) - buttonSetColour.pack(side=TOP, expand=TRUE, fill=X, padx=8, pady=4) - self.optMenuHighlightTarget.pack( - side=TOP, expand=TRUE, fill=X, padx=8, pady=3) - self.radioFg.pack(side=LEFT, anchor=E) - self.radioBg.pack(side=RIGHT, anchor=W) - buttonSaveCustomTheme.pack(side=BOTTOM, fill=X, padx=5, pady=5) - #frameTheme - labelTypeTitle.pack(side=TOP, anchor=W, padx=5, pady=5) - self.radioThemeBuiltin.pack(side=TOP, anchor=W, padx=5) - self.radioThemeCustom.pack(side=TOP, anchor=W, padx=5, pady=2) - self.optMenuThemeBuiltin.pack(side=TOP, fill=X, padx=5, pady=5) - self.optMenuThemeCustom.pack(side=TOP, fill=X, anchor=W, padx=5, pady=5) - self.buttonDeleteCustomTheme.pack(side=TOP, fill=X, padx=5, pady=5) - self.new_custom_theme.pack(side=TOP, fill=X, pady=5) - return frame - - def CreatePageKeys(self): - parent = self.parent - self.bindingTarget = StringVar(parent) - self.builtinKeys = StringVar(parent) - self.customKeys = StringVar(parent) - self.keysAreBuiltin = BooleanVar(parent) - self.keyBinding = StringVar(parent) - - ##widget creation - #body frame - frame = self.tabPages.pages['Keys'].frame - #body section frames - frameCustom = LabelFrame( - frame, borderwidth=2, relief=GROOVE, - text=' Custom Key Bindings ') - frameKeySets = LabelFrame( - frame, borderwidth=2, relief=GROOVE, text=' Key Set ') - #frameCustom - frameTarget = Frame(frameCustom) - labelTargetTitle = Label(frameTarget, text='Action - Key(s)') - scrollTargetY = Scrollbar(frameTarget) - scrollTargetX = Scrollbar(frameTarget, orient=HORIZONTAL) - self.listBindings = Listbox( - frameTarget, takefocus=FALSE, exportselection=FALSE) - self.listBindings.bind('', self.KeyBindingSelected) - scrollTargetY.config(command=self.listBindings.yview) - scrollTargetX.config(command=self.listBindings.xview) - self.listBindings.config(yscrollcommand=scrollTargetY.set) - self.listBindings.config(xscrollcommand=scrollTargetX.set) - self.buttonNewKeys = Button( - frameCustom, text='Get New Keys for Selection', - command=self.GetNewKeys, state=DISABLED) - #frameKeySets - frames = [Frame(frameKeySets, padx=2, pady=2, borderwidth=0) - for i in range(2)] - self.radioKeysBuiltin = Radiobutton( - frames[0], variable=self.keysAreBuiltin, value=1, - command=self.SetKeysType, text='Use a Built-in Key Set') - self.radioKeysCustom = Radiobutton( - frames[0], variable=self.keysAreBuiltin, value=0, - command=self.SetKeysType, text='Use a Custom Key Set') - self.optMenuKeysBuiltin = DynOptionMenu( - frames[0], self.builtinKeys, None, command=None) - self.optMenuKeysCustom = DynOptionMenu( - frames[0], self.customKeys, None, command=None) - self.buttonDeleteCustomKeys = Button( - frames[1], text='Delete Custom Key Set', - command=self.DeleteCustomKeys) - buttonSaveCustomKeys = Button( - frames[1], text='Save as New Custom Key Set', - command=self.SaveAsNewKeySet) - - ##widget packing - #body - frameCustom.pack(side=BOTTOM, padx=5, pady=5, expand=TRUE, fill=BOTH) - frameKeySets.pack(side=BOTTOM, padx=5, pady=5, fill=BOTH) - #frameCustom - self.buttonNewKeys.pack(side=BOTTOM, fill=X, padx=5, pady=5) - frameTarget.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) - #frame target - frameTarget.columnconfigure(0, weight=1) - frameTarget.rowconfigure(1, weight=1) - labelTargetTitle.grid(row=0, column=0, columnspan=2, sticky=W) - self.listBindings.grid(row=1, column=0, sticky=NSEW) - scrollTargetY.grid(row=1, column=1, sticky=NS) - scrollTargetX.grid(row=2, column=0, sticky=EW) - #frameKeySets - self.radioKeysBuiltin.grid(row=0, column=0, sticky=W+NS) - self.radioKeysCustom.grid(row=1, column=0, sticky=W+NS) - self.optMenuKeysBuiltin.grid(row=0, column=1, sticky=NSEW) - self.optMenuKeysCustom.grid(row=1, column=1, sticky=NSEW) - self.buttonDeleteCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2) - buttonSaveCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2) - frames[0].pack(side=TOP, fill=BOTH, expand=True) - frames[1].pack(side=TOP, fill=X, expand=True, pady=2) - return frame - - def CreatePageGeneral(self): - parent = self.parent - self.winWidth = StringVar(parent) - self.winHeight = StringVar(parent) - self.startupEdit = IntVar(parent) - self.autoSave = IntVar(parent) - self.encoding = StringVar(parent) - self.userHelpBrowser = BooleanVar(parent) - self.helpBrowser = StringVar(parent) - - #widget creation - #body - frame = self.tabPages.pages['General'].frame - #body section frames - frameRun = LabelFrame(frame, borderwidth=2, relief=GROOVE, - text=' Startup Preferences ') - frameSave = LabelFrame(frame, borderwidth=2, relief=GROOVE, - text=' Autosave Preferences ') - frameWinSize = Frame(frame, borderwidth=2, relief=GROOVE) - frameHelp = LabelFrame(frame, borderwidth=2, relief=GROOVE, - text=' Additional Help Sources ') - #frameRun - labelRunChoiceTitle = Label(frameRun, text='At Startup') - radioStartupEdit = Radiobutton( - frameRun, variable=self.startupEdit, value=1, - command=self.SetKeysType, text="Open Edit Window") - radioStartupShell = Radiobutton( - frameRun, variable=self.startupEdit, value=0, - command=self.SetKeysType, text='Open Shell Window') - #frameSave - labelRunSaveTitle = Label(frameSave, text='At Start of Run (F5) ') - radioSaveAsk = Radiobutton( - frameSave, variable=self.autoSave, value=0, - command=self.SetKeysType, text="Prompt to Save") - radioSaveAuto = Radiobutton( - frameSave, variable=self.autoSave, value=1, - command=self.SetKeysType, text='No Prompt') - #frameWinSize - labelWinSizeTitle = Label( - frameWinSize, text='Initial Window Size (in characters)') - labelWinWidthTitle = Label(frameWinSize, text='Width') - entryWinWidth = Entry( - frameWinSize, textvariable=self.winWidth, width=3) - labelWinHeightTitle = Label(frameWinSize, text='Height') - entryWinHeight = Entry( - frameWinSize, textvariable=self.winHeight, width=3) - #frameHelp - frameHelpList = Frame(frameHelp) - frameHelpListButtons = Frame(frameHelpList) - scrollHelpList = Scrollbar(frameHelpList) - self.listHelp = Listbox( - frameHelpList, height=5, takefocus=FALSE, - exportselection=FALSE) - scrollHelpList.config(command=self.listHelp.yview) - self.listHelp.config(yscrollcommand=scrollHelpList.set) - self.listHelp.bind('', self.HelpSourceSelected) - self.buttonHelpListEdit = Button( - frameHelpListButtons, text='Edit', state=DISABLED, - width=8, command=self.HelpListItemEdit) - self.buttonHelpListAdd = Button( - frameHelpListButtons, text='Add', - width=8, command=self.HelpListItemAdd) - self.buttonHelpListRemove = Button( - frameHelpListButtons, text='Remove', state=DISABLED, - width=8, command=self.HelpListItemRemove) - - #widget packing - #body - frameRun.pack(side=TOP, padx=5, pady=5, fill=X) - frameSave.pack(side=TOP, padx=5, pady=5, fill=X) - frameWinSize.pack(side=TOP, padx=5, pady=5, fill=X) - frameHelp.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) - #frameRun - labelRunChoiceTitle.pack(side=LEFT, anchor=W, padx=5, pady=5) - radioStartupShell.pack(side=RIGHT, anchor=W, padx=5, pady=5) - radioStartupEdit.pack(side=RIGHT, anchor=W, padx=5, pady=5) - #frameSave - labelRunSaveTitle.pack(side=LEFT, anchor=W, padx=5, pady=5) - radioSaveAuto.pack(side=RIGHT, anchor=W, padx=5, pady=5) - radioSaveAsk.pack(side=RIGHT, anchor=W, padx=5, pady=5) - #frameWinSize - labelWinSizeTitle.pack(side=LEFT, anchor=W, padx=5, pady=5) - entryWinHeight.pack(side=RIGHT, anchor=E, padx=10, pady=5) - labelWinHeightTitle.pack(side=RIGHT, anchor=E, pady=5) - entryWinWidth.pack(side=RIGHT, anchor=E, padx=10, pady=5) - labelWinWidthTitle.pack(side=RIGHT, anchor=E, pady=5) - #frameHelp - frameHelpListButtons.pack(side=RIGHT, padx=5, pady=5, fill=Y) - frameHelpList.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) - scrollHelpList.pack(side=RIGHT, anchor=W, fill=Y) - self.listHelp.pack(side=LEFT, anchor=E, expand=TRUE, fill=BOTH) - self.buttonHelpListEdit.pack(side=TOP, anchor=W, pady=5) - self.buttonHelpListAdd.pack(side=TOP, anchor=W) - self.buttonHelpListRemove.pack(side=TOP, anchor=W, pady=5) - return frame - - def AttachVarCallbacks(self): - self.fontSize.trace_variable('w', self.VarChanged_font) - self.fontName.trace_variable('w', self.VarChanged_font) - self.fontBold.trace_variable('w', self.VarChanged_font) - self.spaceNum.trace_variable('w', self.VarChanged_spaceNum) - self.colour.trace_variable('w', self.VarChanged_colour) - self.builtinTheme.trace_variable('w', self.VarChanged_builtinTheme) - self.customTheme.trace_variable('w', self.VarChanged_customTheme) - self.themeIsBuiltin.trace_variable('w', self.VarChanged_themeIsBuiltin) - self.highlightTarget.trace_variable('w', self.VarChanged_highlightTarget) - self.keyBinding.trace_variable('w', self.VarChanged_keyBinding) - self.builtinKeys.trace_variable('w', self.VarChanged_builtinKeys) - self.customKeys.trace_variable('w', self.VarChanged_customKeys) - self.keysAreBuiltin.trace_variable('w', self.VarChanged_keysAreBuiltin) - self.winWidth.trace_variable('w', self.VarChanged_winWidth) - self.winHeight.trace_variable('w', self.VarChanged_winHeight) - self.startupEdit.trace_variable('w', self.VarChanged_startupEdit) - self.autoSave.trace_variable('w', self.VarChanged_autoSave) - self.encoding.trace_variable('w', self.VarChanged_encoding) - - def remove_var_callbacks(self): - "Remove callbacks to prevent memory leaks." - for var in ( - self.fontSize, self.fontName, self.fontBold, - self.spaceNum, self.colour, self.builtinTheme, - self.customTheme, self.themeIsBuiltin, self.highlightTarget, - self.keyBinding, self.builtinKeys, self.customKeys, - self.keysAreBuiltin, self.winWidth, self.winHeight, - self.startupEdit, self.autoSave, self.encoding,): - var.trace_vdelete('w', var.trace_vinfo()[0][1]) - - def VarChanged_font(self, *params): - '''When one font attribute changes, save them all, as they are - not independent from each other. In particular, when we are - overriding the default font, we need to write out everything. - ''' - value = self.fontName.get() - self.AddChangedItem('main', 'EditorWindow', 'font', value) - value = self.fontSize.get() - self.AddChangedItem('main', 'EditorWindow', 'font-size', value) - value = self.fontBold.get() - self.AddChangedItem('main', 'EditorWindow', 'font-bold', value) - - def VarChanged_spaceNum(self, *params): - value = self.spaceNum.get() - self.AddChangedItem('main', 'Indent', 'num-spaces', value) - - def VarChanged_colour(self, *params): - self.OnNewColourSet() - - def VarChanged_builtinTheme(self, *params): - value = self.builtinTheme.get() - if value == 'IDLE Dark': - if idleConf.GetOption('main', 'Theme', 'name') != 'IDLE New': - self.AddChangedItem('main', 'Theme', 'name', 'IDLE Classic') - self.AddChangedItem('main', 'Theme', 'name2', value) - self.new_custom_theme.config(text='New theme, see Help', - fg='#500000') - else: - self.AddChangedItem('main', 'Theme', 'name', value) - self.AddChangedItem('main', 'Theme', 'name2', '') - self.new_custom_theme.config(text='', fg='black') - self.PaintThemeSample() - - def VarChanged_customTheme(self, *params): - value = self.customTheme.get() - if value != '- no custom themes -': - self.AddChangedItem('main', 'Theme', 'name', value) - self.PaintThemeSample() - - def VarChanged_themeIsBuiltin(self, *params): - value = self.themeIsBuiltin.get() - self.AddChangedItem('main', 'Theme', 'default', value) - if value: - self.VarChanged_builtinTheme() - else: - self.VarChanged_customTheme() - - def VarChanged_highlightTarget(self, *params): - self.SetHighlightTarget() - - def VarChanged_keyBinding(self, *params): - value = self.keyBinding.get() - keySet = self.customKeys.get() - event = self.listBindings.get(ANCHOR).split()[0] - if idleConf.IsCoreBinding(event): - #this is a core keybinding - self.AddChangedItem('keys', keySet, event, value) - else: #this is an extension key binding - extName = idleConf.GetExtnNameForEvent(event) - extKeybindSection = extName + '_cfgBindings' - self.AddChangedItem('extensions', extKeybindSection, event, value) - - def VarChanged_builtinKeys(self, *params): - value = self.builtinKeys.get() - self.AddChangedItem('main', 'Keys', 'name', value) - self.LoadKeysList(value) - - def VarChanged_customKeys(self, *params): - value = self.customKeys.get() - if value != '- no custom keys -': - self.AddChangedItem('main', 'Keys', 'name', value) - self.LoadKeysList(value) - - def VarChanged_keysAreBuiltin(self, *params): - value = self.keysAreBuiltin.get() - self.AddChangedItem('main', 'Keys', 'default', value) - if value: - self.VarChanged_builtinKeys() - else: - self.VarChanged_customKeys() - - def VarChanged_winWidth(self, *params): - value = self.winWidth.get() - self.AddChangedItem('main', 'EditorWindow', 'width', value) - - def VarChanged_winHeight(self, *params): - value = self.winHeight.get() - self.AddChangedItem('main', 'EditorWindow', 'height', value) - - def VarChanged_startupEdit(self, *params): - value = self.startupEdit.get() - self.AddChangedItem('main', 'General', 'editor-on-startup', value) - - def VarChanged_autoSave(self, *params): - value = self.autoSave.get() - self.AddChangedItem('main', 'General', 'autosave', value) - - def VarChanged_encoding(self, *params): - value = self.encoding.get() - self.AddChangedItem('main', 'EditorWindow', 'encoding', value) - - def ResetChangedItems(self): - #When any config item is changed in this dialog, an entry - #should be made in the relevant section (config type) of this - #dictionary. The key should be the config file section name and the - #value a dictionary, whose key:value pairs are item=value pairs for - #that config file section. - self.changedItems = {'main':{}, 'highlight':{}, 'keys':{}, - 'extensions':{}} - - def AddChangedItem(self, typ, section, item, value): - value = str(value) #make sure we use a string - if section not in self.changedItems[typ]: - self.changedItems[typ][section] = {} - self.changedItems[typ][section][item] = value - - def GetDefaultItems(self): - dItems={'main':{}, 'highlight':{}, 'keys':{}, 'extensions':{}} - for configType in dItems: - sections = idleConf.GetSectionList('default', configType) - for section in sections: - dItems[configType][section] = {} - options = idleConf.defaultCfg[configType].GetOptionList(section) - for option in options: - dItems[configType][section][option] = ( - idleConf.defaultCfg[configType].Get(section, option)) - return dItems - - def SetThemeType(self): - if self.themeIsBuiltin.get(): - self.optMenuThemeBuiltin.config(state=NORMAL) - self.optMenuThemeCustom.config(state=DISABLED) - self.buttonDeleteCustomTheme.config(state=DISABLED) - else: - self.optMenuThemeBuiltin.config(state=DISABLED) - self.radioThemeCustom.config(state=NORMAL) - self.optMenuThemeCustom.config(state=NORMAL) - self.buttonDeleteCustomTheme.config(state=NORMAL) - - def SetKeysType(self): - if self.keysAreBuiltin.get(): - self.optMenuKeysBuiltin.config(state=NORMAL) - self.optMenuKeysCustom.config(state=DISABLED) - self.buttonDeleteCustomKeys.config(state=DISABLED) - else: - self.optMenuKeysBuiltin.config(state=DISABLED) - self.radioKeysCustom.config(state=NORMAL) - self.optMenuKeysCustom.config(state=NORMAL) - self.buttonDeleteCustomKeys.config(state=NORMAL) - - def GetNewKeys(self): - listIndex = self.listBindings.index(ANCHOR) - binding = self.listBindings.get(listIndex) - bindName = binding.split()[0] #first part, up to first space - if self.keysAreBuiltin.get(): - currentKeySetName = self.builtinKeys.get() - else: - currentKeySetName = self.customKeys.get() - currentBindings = idleConf.GetCurrentKeySet() - if currentKeySetName in self.changedItems['keys']: #unsaved changes - keySetChanges = self.changedItems['keys'][currentKeySetName] - for event in keySetChanges: - currentBindings[event] = keySetChanges[event].split() - currentKeySequences = list(currentBindings.values()) - newKeys = GetKeysDialog(self, 'Get New Keys', bindName, - currentKeySequences).result - if newKeys: #new keys were specified - if self.keysAreBuiltin.get(): #current key set is a built-in - message = ('Your changes will be saved as a new Custom Key Set.' - ' Enter a name for your new Custom Key Set below.') - newKeySet = self.GetNewKeysName(message) - if not newKeySet: #user cancelled custom key set creation - self.listBindings.select_set(listIndex) - self.listBindings.select_anchor(listIndex) - return - else: #create new custom key set based on previously active key set - self.CreateNewKeySet(newKeySet) - self.listBindings.delete(listIndex) - self.listBindings.insert(listIndex, bindName+' - '+newKeys) - self.listBindings.select_set(listIndex) - self.listBindings.select_anchor(listIndex) - self.keyBinding.set(newKeys) - else: - self.listBindings.select_set(listIndex) - self.listBindings.select_anchor(listIndex) - - def GetNewKeysName(self, message): - usedNames = (idleConf.GetSectionList('user', 'keys') + - idleConf.GetSectionList('default', 'keys')) - newKeySet = GetCfgSectionNameDialog( - self, 'New Custom Key Set', message, usedNames).result - return newKeySet - - def SaveAsNewKeySet(self): - newKeysName = self.GetNewKeysName('New Key Set Name:') - if newKeysName: - self.CreateNewKeySet(newKeysName) - - def KeyBindingSelected(self, event): - self.buttonNewKeys.config(state=NORMAL) - - def CreateNewKeySet(self, newKeySetName): - #creates new custom key set based on the previously active key set, - #and makes the new key set active - if self.keysAreBuiltin.get(): - prevKeySetName = self.builtinKeys.get() - else: - prevKeySetName = self.customKeys.get() - prevKeys = idleConf.GetCoreKeys(prevKeySetName) - newKeys = {} - for event in prevKeys: #add key set to changed items - eventName = event[2:-2] #trim off the angle brackets - binding = ' '.join(prevKeys[event]) - newKeys[eventName] = binding - #handle any unsaved changes to prev key set - if prevKeySetName in self.changedItems['keys']: - keySetChanges = self.changedItems['keys'][prevKeySetName] - for event in keySetChanges: - newKeys[event] = keySetChanges[event] - #save the new theme - self.SaveNewKeySet(newKeySetName, newKeys) - #change gui over to the new key set - customKeyList = idleConf.GetSectionList('user', 'keys') - customKeyList.sort() - self.optMenuKeysCustom.SetMenu(customKeyList, newKeySetName) - self.keysAreBuiltin.set(0) - self.SetKeysType() - - def LoadKeysList(self, keySetName): - reselect = 0 - newKeySet = 0 - if self.listBindings.curselection(): - reselect = 1 - listIndex = self.listBindings.index(ANCHOR) - keySet = idleConf.GetKeySet(keySetName) - bindNames = list(keySet.keys()) - bindNames.sort() - self.listBindings.delete(0, END) - for bindName in bindNames: - key = ' '.join(keySet[bindName]) #make key(s) into a string - bindName = bindName[2:-2] #trim off the angle brackets - if keySetName in self.changedItems['keys']: - #handle any unsaved changes to this key set - if bindName in self.changedItems['keys'][keySetName]: - key = self.changedItems['keys'][keySetName][bindName] - self.listBindings.insert(END, bindName+' - '+key) - if reselect: - self.listBindings.see(listIndex) - self.listBindings.select_set(listIndex) - self.listBindings.select_anchor(listIndex) - - def DeleteCustomKeys(self): - keySetName=self.customKeys.get() - delmsg = 'Are you sure you wish to delete the key set %r ?' - if not tkMessageBox.askyesno( - 'Delete Key Set', delmsg % keySetName, parent=self): - return - self.DeactivateCurrentConfig() - #remove key set from config - idleConf.userCfg['keys'].remove_section(keySetName) - if keySetName in self.changedItems['keys']: - del(self.changedItems['keys'][keySetName]) - #write changes - idleConf.userCfg['keys'].Save() - #reload user key set list - itemList = idleConf.GetSectionList('user', 'keys') - itemList.sort() - if not itemList: - self.radioKeysCustom.config(state=DISABLED) - self.optMenuKeysCustom.SetMenu(itemList, '- no custom keys -') - else: - self.optMenuKeysCustom.SetMenu(itemList, itemList[0]) - #revert to default key set - self.keysAreBuiltin.set(idleConf.defaultCfg['main'].Get('Keys', 'default')) - self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys', 'name')) - #user can't back out of these changes, they must be applied now - self.SaveAllChangedConfigs() - self.ActivateConfigChanges() - self.SetKeysType() - - def DeleteCustomTheme(self): - themeName = self.customTheme.get() - delmsg = 'Are you sure you wish to delete the theme %r ?' - if not tkMessageBox.askyesno( - 'Delete Theme', delmsg % themeName, parent=self): - return - self.DeactivateCurrentConfig() - #remove theme from config - idleConf.userCfg['highlight'].remove_section(themeName) - if themeName in self.changedItems['highlight']: - del(self.changedItems['highlight'][themeName]) - #write changes - idleConf.userCfg['highlight'].Save() - #reload user theme list - itemList = idleConf.GetSectionList('user', 'highlight') - itemList.sort() - if not itemList: - self.radioThemeCustom.config(state=DISABLED) - self.optMenuThemeCustom.SetMenu(itemList, '- no custom themes -') - else: - self.optMenuThemeCustom.SetMenu(itemList, itemList[0]) - #revert to default theme - self.themeIsBuiltin.set(idleConf.defaultCfg['main'].Get('Theme', 'default')) - self.builtinTheme.set(idleConf.defaultCfg['main'].Get('Theme', 'name')) - #user can't back out of these changes, they must be applied now - self.SaveAllChangedConfigs() - self.ActivateConfigChanges() - self.SetThemeType() - - def GetColour(self): - target = self.highlightTarget.get() - prevColour = self.frameColourSet.cget('bg') - rgbTuplet, colourString = tkColorChooser.askcolor( - parent=self, title='Pick new colour for : '+target, - initialcolor=prevColour) - if colourString and (colourString != prevColour): - #user didn't cancel, and they chose a new colour - if self.themeIsBuiltin.get(): #current theme is a built-in - message = ('Your changes will be saved as a new Custom Theme. ' - 'Enter a name for your new Custom Theme below.') - newTheme = self.GetNewThemeName(message) - if not newTheme: #user cancelled custom theme creation - return - else: #create new custom theme based on previously active theme - self.CreateNewTheme(newTheme) - self.colour.set(colourString) - else: #current theme is user defined - self.colour.set(colourString) - - def OnNewColourSet(self): - newColour=self.colour.get() - self.frameColourSet.config(bg=newColour) #set sample - plane ='foreground' if self.fgHilite.get() else 'background' - sampleElement = self.themeElements[self.highlightTarget.get()][0] - self.textHighlightSample.tag_config(sampleElement, **{plane:newColour}) - theme = self.customTheme.get() - themeElement = sampleElement + '-' + plane - self.AddChangedItem('highlight', theme, themeElement, newColour) - - def GetNewThemeName(self, message): - usedNames = (idleConf.GetSectionList('user', 'highlight') + - idleConf.GetSectionList('default', 'highlight')) - newTheme = GetCfgSectionNameDialog( - self, 'New Custom Theme', message, usedNames).result - return newTheme - - def SaveAsNewTheme(self): - newThemeName = self.GetNewThemeName('New Theme Name:') - if newThemeName: - self.CreateNewTheme(newThemeName) - - def CreateNewTheme(self, newThemeName): - #creates new custom theme based on the previously active theme, - #and makes the new theme active - if self.themeIsBuiltin.get(): - themeType = 'default' - themeName = self.builtinTheme.get() - else: - themeType = 'user' - themeName = self.customTheme.get() - newTheme = idleConf.GetThemeDict(themeType, themeName) - #apply any of the old theme's unsaved changes to the new theme - if themeName in self.changedItems['highlight']: - themeChanges = self.changedItems['highlight'][themeName] - for element in themeChanges: - newTheme[element] = themeChanges[element] - #save the new theme - self.SaveNewTheme(newThemeName, newTheme) - #change gui over to the new theme - customThemeList = idleConf.GetSectionList('user', 'highlight') - customThemeList.sort() - self.optMenuThemeCustom.SetMenu(customThemeList, newThemeName) - self.themeIsBuiltin.set(0) - self.SetThemeType() - - def OnListFontButtonRelease(self, event): - font = self.listFontName.get(ANCHOR) - self.fontName.set(font.lower()) - self.SetFontSample() - - def SetFontSample(self, event=None): - fontName = self.fontName.get() - fontWeight = tkFont.BOLD if self.fontBold.get() else tkFont.NORMAL - newFont = (fontName, self.fontSize.get(), fontWeight) - self.labelFontSample.config(font=newFont) - self.textHighlightSample.configure(font=newFont) - - def SetHighlightTarget(self): - if self.highlightTarget.get() == 'Cursor': #bg not possible - self.radioFg.config(state=DISABLED) - self.radioBg.config(state=DISABLED) - self.fgHilite.set(1) - else: #both fg and bg can be set - self.radioFg.config(state=NORMAL) - self.radioBg.config(state=NORMAL) - self.fgHilite.set(1) - self.SetColourSample() - - def SetColourSampleBinding(self, *args): - self.SetColourSample() - - def SetColourSample(self): - #set the colour smaple area - tag = self.themeElements[self.highlightTarget.get()][0] - plane = 'foreground' if self.fgHilite.get() else 'background' - colour = self.textHighlightSample.tag_cget(tag, plane) - self.frameColourSet.config(bg=colour) - - def PaintThemeSample(self): - if self.themeIsBuiltin.get(): #a default theme - theme = self.builtinTheme.get() - else: #a user theme - theme = self.customTheme.get() - for elementTitle in self.themeElements: - element = self.themeElements[elementTitle][0] - colours = idleConf.GetHighlight(theme, element) - if element == 'cursor': #cursor sample needs special painting - colours['background'] = idleConf.GetHighlight( - theme, 'normal', fgBg='bg') - #handle any unsaved changes to this theme - if theme in self.changedItems['highlight']: - themeDict = self.changedItems['highlight'][theme] - if element + '-foreground' in themeDict: - colours['foreground'] = themeDict[element + '-foreground'] - if element + '-background' in themeDict: - colours['background'] = themeDict[element + '-background'] - self.textHighlightSample.tag_config(element, **colours) - self.SetColourSample() - - def HelpSourceSelected(self, event): - self.SetHelpListButtonStates() - - def SetHelpListButtonStates(self): - if self.listHelp.size() < 1: #no entries in list - self.buttonHelpListEdit.config(state=DISABLED) - self.buttonHelpListRemove.config(state=DISABLED) - else: #there are some entries - if self.listHelp.curselection(): #there currently is a selection - self.buttonHelpListEdit.config(state=NORMAL) - self.buttonHelpListRemove.config(state=NORMAL) - else: #there currently is not a selection - self.buttonHelpListEdit.config(state=DISABLED) - self.buttonHelpListRemove.config(state=DISABLED) - - def HelpListItemAdd(self): - helpSource = GetHelpSourceDialog(self, 'New Help Source').result - if helpSource: - self.userHelpList.append((helpSource[0], helpSource[1])) - self.listHelp.insert(END, helpSource[0]) - self.UpdateUserHelpChangedItems() - self.SetHelpListButtonStates() - - def HelpListItemEdit(self): - itemIndex = self.listHelp.index(ANCHOR) - helpSource = self.userHelpList[itemIndex] - newHelpSource = GetHelpSourceDialog( - self, 'Edit Help Source', menuItem=helpSource[0], - filePath=helpSource[1]).result - if (not newHelpSource) or (newHelpSource == helpSource): - return #no changes - self.userHelpList[itemIndex] = newHelpSource - self.listHelp.delete(itemIndex) - self.listHelp.insert(itemIndex, newHelpSource[0]) - self.UpdateUserHelpChangedItems() - self.SetHelpListButtonStates() - - def HelpListItemRemove(self): - itemIndex = self.listHelp.index(ANCHOR) - del(self.userHelpList[itemIndex]) - self.listHelp.delete(itemIndex) - self.UpdateUserHelpChangedItems() - self.SetHelpListButtonStates() - - def UpdateUserHelpChangedItems(self): - "Clear and rebuild the HelpFiles section in self.changedItems" - self.changedItems['main']['HelpFiles'] = {} - for num in range(1, len(self.userHelpList) + 1): - self.AddChangedItem( - 'main', 'HelpFiles', str(num), - ';'.join(self.userHelpList[num-1][:2])) - - def LoadFontCfg(self): - ##base editor font selection list - fonts = list(tkFont.families(self)) - fonts.sort() - for font in fonts: - self.listFontName.insert(END, font) - configuredFont = idleConf.GetFont(self, 'main', 'EditorWindow') - fontName = configuredFont[0].lower() - fontSize = configuredFont[1] - fontBold = configuredFont[2]=='bold' - self.fontName.set(fontName) - lc_fonts = [s.lower() for s in fonts] - try: - currentFontIndex = lc_fonts.index(fontName) - self.listFontName.see(currentFontIndex) - self.listFontName.select_set(currentFontIndex) - self.listFontName.select_anchor(currentFontIndex) - except ValueError: - pass - ##font size dropdown - self.optMenuFontSize.SetMenu(('7', '8', '9', '10', '11', '12', '13', - '14', '16', '18', '20', '22'), fontSize ) - ##fontWeight - self.fontBold.set(fontBold) - ##font sample - self.SetFontSample() - - def LoadTabCfg(self): - ##indent sizes - spaceNum = idleConf.GetOption( - 'main', 'Indent', 'num-spaces', default=4, type='int') - self.spaceNum.set(spaceNum) - - def LoadThemeCfg(self): - ##current theme type radiobutton - self.themeIsBuiltin.set(idleConf.GetOption( - 'main', 'Theme', 'default', type='bool', default=1)) - ##currently set theme - currentOption = idleConf.CurrentTheme() - ##load available theme option menus - if self.themeIsBuiltin.get(): #default theme selected - itemList = idleConf.GetSectionList('default', 'highlight') - itemList.sort() - self.optMenuThemeBuiltin.SetMenu(itemList, currentOption) - itemList = idleConf.GetSectionList('user', 'highlight') - itemList.sort() - if not itemList: - self.radioThemeCustom.config(state=DISABLED) - self.customTheme.set('- no custom themes -') - else: - self.optMenuThemeCustom.SetMenu(itemList, itemList[0]) - else: #user theme selected - itemList = idleConf.GetSectionList('user', 'highlight') - itemList.sort() - self.optMenuThemeCustom.SetMenu(itemList, currentOption) - itemList = idleConf.GetSectionList('default', 'highlight') - itemList.sort() - self.optMenuThemeBuiltin.SetMenu(itemList, itemList[0]) - self.SetThemeType() - ##load theme element option menu - themeNames = list(self.themeElements.keys()) - themeNames.sort(key=lambda x: self.themeElements[x][1]) - self.optMenuHighlightTarget.SetMenu(themeNames, themeNames[0]) - self.PaintThemeSample() - self.SetHighlightTarget() - - def LoadKeyCfg(self): - ##current keys type radiobutton - self.keysAreBuiltin.set(idleConf.GetOption( - 'main', 'Keys', 'default', type='bool', default=1)) - ##currently set keys - currentOption = idleConf.CurrentKeys() - ##load available keyset option menus - if self.keysAreBuiltin.get(): #default theme selected - itemList = idleConf.GetSectionList('default', 'keys') - itemList.sort() - self.optMenuKeysBuiltin.SetMenu(itemList, currentOption) - itemList = idleConf.GetSectionList('user', 'keys') - itemList.sort() - if not itemList: - self.radioKeysCustom.config(state=DISABLED) - self.customKeys.set('- no custom keys -') - else: - self.optMenuKeysCustom.SetMenu(itemList, itemList[0]) - else: #user key set selected - itemList = idleConf.GetSectionList('user', 'keys') - itemList.sort() - self.optMenuKeysCustom.SetMenu(itemList, currentOption) - itemList = idleConf.GetSectionList('default', 'keys') - itemList.sort() - self.optMenuKeysBuiltin.SetMenu(itemList, itemList[0]) - self.SetKeysType() - ##load keyset element list - keySetName = idleConf.CurrentKeys() - self.LoadKeysList(keySetName) - - def LoadGeneralCfg(self): - #startup state - self.startupEdit.set(idleConf.GetOption( - 'main', 'General', 'editor-on-startup', default=1, type='bool')) - #autosave state - self.autoSave.set(idleConf.GetOption( - 'main', 'General', 'autosave', default=0, type='bool')) - #initial window size - self.winWidth.set(idleConf.GetOption( - 'main', 'EditorWindow', 'width', type='int')) - self.winHeight.set(idleConf.GetOption( - 'main', 'EditorWindow', 'height', type='int')) - # default source encoding - self.encoding.set(idleConf.GetOption( - 'main', 'EditorWindow', 'encoding', default='none')) - # additional help sources - self.userHelpList = idleConf.GetAllExtraHelpSourcesList() - for helpItem in self.userHelpList: - self.listHelp.insert(END, helpItem[0]) - self.SetHelpListButtonStates() - - def LoadConfigs(self): - """ - load configuration from default and user config files and populate - the widgets on the config dialog pages. - """ - ### fonts / tabs page - self.LoadFontCfg() - self.LoadTabCfg() - ### highlighting page - self.LoadThemeCfg() - ### keys page - self.LoadKeyCfg() - ### general page - self.LoadGeneralCfg() - # note: extension page handled separately - - def SaveNewKeySet(self, keySetName, keySet): - """ - save a newly created core key set. - keySetName - string, the name of the new key set - keySet - dictionary containing the new key set - """ - if not idleConf.userCfg['keys'].has_section(keySetName): - idleConf.userCfg['keys'].add_section(keySetName) - for event in keySet: - value = keySet[event] - idleConf.userCfg['keys'].SetOption(keySetName, event, value) - - def SaveNewTheme(self, themeName, theme): - """ - save a newly created theme. - themeName - string, the name of the new theme - theme - dictionary containing the new theme - """ - if not idleConf.userCfg['highlight'].has_section(themeName): - idleConf.userCfg['highlight'].add_section(themeName) - for element in theme: - value = theme[element] - idleConf.userCfg['highlight'].SetOption(themeName, element, value) - - def SetUserValue(self, configType, section, item, value): - if idleConf.defaultCfg[configType].has_option(section, item): - if idleConf.defaultCfg[configType].Get(section, item) == value: - #the setting equals a default setting, remove it from user cfg - return idleConf.userCfg[configType].RemoveOption(section, item) - #if we got here set the option - return idleConf.userCfg[configType].SetOption(section, item, value) - - def SaveAllChangedConfigs(self): - "Save configuration changes to the user config file." - idleConf.userCfg['main'].Save() - for configType in self.changedItems: - cfgTypeHasChanges = False - for section in self.changedItems[configType]: - if section == 'HelpFiles': - #this section gets completely replaced - idleConf.userCfg['main'].remove_section('HelpFiles') - cfgTypeHasChanges = True - for item in self.changedItems[configType][section]: - value = self.changedItems[configType][section][item] - if self.SetUserValue(configType, section, item, value): - cfgTypeHasChanges = True - if cfgTypeHasChanges: - idleConf.userCfg[configType].Save() - for configType in ['keys', 'highlight']: - # save these even if unchanged! - idleConf.userCfg[configType].Save() - self.ResetChangedItems() #clear the changed items dict - self.save_all_changed_extensions() # uses a different mechanism - - def DeactivateCurrentConfig(self): - #Before a config is saved, some cleanup of current - #config must be done - remove the previous keybindings - winInstances = self.parent.instance_dict.keys() - for instance in winInstances: - instance.RemoveKeybindings() - - def ActivateConfigChanges(self): - "Dynamically apply configuration changes" - winInstances = self.parent.instance_dict.keys() - for instance in winInstances: - instance.ResetColorizer() - instance.ResetFont() - instance.set_notabs_indentwidth() - instance.ApplyKeybindings() - instance.reset_help_menu_entries() - - def Cancel(self): - self.destroy() - - def Ok(self): - self.Apply() - self.destroy() - - def Apply(self): - self.DeactivateCurrentConfig() - self.SaveAllChangedConfigs() - self.ActivateConfigChanges() - - def Help(self): - page = self.tabPages._current_page - view_text(self, title='Help for IDLE preferences', - text=help_common+help_pages.get(page, '')) - - def CreatePageExtensions(self): - """Part of the config dialog used for configuring IDLE extensions. - - This code is generic - it works for any and all IDLE extensions. - - IDLE extensions save their configuration options using idleConf. - This code reads the current configuration using idleConf, supplies a - GUI interface to change the configuration values, and saves the - changes using idleConf. - - Not all changes take effect immediately - some may require restarting IDLE. - This depends on each extension's implementation. - - All values are treated as text, and it is up to the user to supply - reasonable values. The only exception to this are the 'enable*' options, - which are boolean, and can be toggled with a True/False button. - """ - parent = self.parent - frame = self.tabPages.pages['Extensions'].frame - self.ext_defaultCfg = idleConf.defaultCfg['extensions'] - self.ext_userCfg = idleConf.userCfg['extensions'] - self.is_int = self.register(is_int) - self.load_extensions() - # create widgets - a listbox shows all available extensions, with the - # controls for the extension selected in the listbox to the right - self.extension_names = StringVar(self) - frame.rowconfigure(0, weight=1) - frame.columnconfigure(2, weight=1) - self.extension_list = Listbox(frame, listvariable=self.extension_names, - selectmode='browse') - self.extension_list.bind('<>', self.extension_selected) - scroll = Scrollbar(frame, command=self.extension_list.yview) - self.extension_list.yscrollcommand=scroll.set - self.details_frame = LabelFrame(frame, width=250, height=250) - self.extension_list.grid(column=0, row=0, sticky='nws') - scroll.grid(column=1, row=0, sticky='ns') - self.details_frame.grid(column=2, row=0, sticky='nsew', padx=[10, 0]) - frame.configure(padx=10, pady=10) - self.config_frame = {} - self.current_extension = None - - self.outerframe = self # TEMPORARY - self.tabbed_page_set = self.extension_list # TEMPORARY - - # create the frame holding controls for each extension - ext_names = '' - for ext_name in sorted(self.extensions): - self.create_extension_frame(ext_name) - ext_names = ext_names + '{' + ext_name + '} ' - self.extension_names.set(ext_names) - self.extension_list.selection_set(0) - self.extension_selected(None) - - def load_extensions(self): - "Fill self.extensions with data from the default and user configs." - self.extensions = {} - for ext_name in idleConf.GetExtensions(active_only=False): - self.extensions[ext_name] = [] - - for ext_name in self.extensions: - opt_list = sorted(self.ext_defaultCfg.GetOptionList(ext_name)) - - # bring 'enable' options to the beginning of the list - enables = [opt_name for opt_name in opt_list - if opt_name.startswith('enable')] - for opt_name in enables: - opt_list.remove(opt_name) - opt_list = enables + opt_list - - for opt_name in opt_list: - def_str = self.ext_defaultCfg.Get( - ext_name, opt_name, raw=True) - try: - def_obj = {'True':True, 'False':False}[def_str] - opt_type = 'bool' - except KeyError: - try: - def_obj = int(def_str) - opt_type = 'int' - except ValueError: - def_obj = def_str - opt_type = None - try: - value = self.ext_userCfg.Get( - ext_name, opt_name, type=opt_type, raw=True, - default=def_obj) - except ValueError: # Need this until .Get fixed - value = def_obj # bad values overwritten by entry - var = StringVar(self) - var.set(str(value)) - - self.extensions[ext_name].append({'name': opt_name, - 'type': opt_type, - 'default': def_str, - 'value': value, - 'var': var, - }) - - def extension_selected(self, event): - newsel = self.extension_list.curselection() - if newsel: - newsel = self.extension_list.get(newsel) - if newsel is None or newsel != self.current_extension: - if self.current_extension: - self.details_frame.config(text='') - self.config_frame[self.current_extension].grid_forget() - self.current_extension = None - if newsel: - self.details_frame.config(text=newsel) - self.config_frame[newsel].grid(column=0, row=0, sticky='nsew') - self.current_extension = newsel - - def create_extension_frame(self, ext_name): - """Create a frame holding the widgets to configure one extension""" - f = VerticalScrolledFrame(self.details_frame, height=250, width=250) - self.config_frame[ext_name] = f - entry_area = f.interior - # create an entry for each configuration option - for row, opt in enumerate(self.extensions[ext_name]): - # create a row with a label and entry/checkbutton - label = Label(entry_area, text=opt['name']) - label.grid(row=row, column=0, sticky=NW) - var = opt['var'] - if opt['type'] == 'bool': - Checkbutton(entry_area, textvariable=var, variable=var, - onvalue='True', offvalue='False', - indicatoron=FALSE, selectcolor='', width=8 - ).grid(row=row, column=1, sticky=W, padx=7) - elif opt['type'] == 'int': - Entry(entry_area, textvariable=var, validate='key', - validatecommand=(self.is_int, '%P') - ).grid(row=row, column=1, sticky=NSEW, padx=7) - - else: - Entry(entry_area, textvariable=var - ).grid(row=row, column=1, sticky=NSEW, padx=7) - return - - def set_extension_value(self, section, opt): - name = opt['name'] - default = opt['default'] - value = opt['var'].get().strip() or default - opt['var'].set(value) - # if self.defaultCfg.has_section(section): - # Currently, always true; if not, indent to return - if (value == default): - return self.ext_userCfg.RemoveOption(section, name) - # set the option - return self.ext_userCfg.SetOption(section, name, value) - - def save_all_changed_extensions(self): - """Save configuration changes to the user config file.""" - has_changes = False - for ext_name in self.extensions: - options = self.extensions[ext_name] - for opt in options: - if self.set_extension_value(ext_name, opt): - has_changes = True - if has_changes: - self.ext_userCfg.Save() - - -help_common = '''\ -When you click either the Apply or Ok buttons, settings in this -dialog that are different from IDLE's default are saved in -a .idlerc directory in your home directory. Except as noted, -these changes apply to all versions of IDLE installed on this -machine. Some do not take affect until IDLE is restarted. -[Cancel] only cancels changes made since the last save. -''' -help_pages = { - 'Highlighting':''' -Highlighting: -The IDLE Dark color theme is new in October 2015. It can only -be used with older IDLE releases if it is saved as a custom -theme, with a different name. -''' -} - - -def is_int(s): - "Return 's is blank or represents an int'" - if not s: - return True - try: - int(s) - return True - except ValueError: - return False - - -class VerticalScrolledFrame(Frame): - """A pure Tkinter vertically scrollable frame. - - * Use the 'interior' attribute to place widgets inside the scrollable frame - * Construct and pack/place/grid normally - * This frame only allows vertical scrolling - """ - def __init__(self, parent, *args, **kw): - Frame.__init__(self, parent, *args, **kw) - - # create a canvas object and a vertical scrollbar for scrolling it - vscrollbar = Scrollbar(self, orient=VERTICAL) - vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE) - canvas = Canvas(self, bd=0, highlightthickness=0, - yscrollcommand=vscrollbar.set, width=240) - canvas.pack(side=LEFT, fill=BOTH, expand=TRUE) - vscrollbar.config(command=canvas.yview) - - # reset the view - canvas.xview_moveto(0) - canvas.yview_moveto(0) - - # create a frame inside the canvas which will be scrolled with it - self.interior = interior = Frame(canvas) - interior_id = canvas.create_window(0, 0, window=interior, anchor=NW) - - # track changes to the canvas and frame width and sync them, - # also updating the scrollbar - def _configure_interior(event): - # update the scrollbars to match the size of the inner frame - size = (interior.winfo_reqwidth(), interior.winfo_reqheight()) - canvas.config(scrollregion="0 0 %s %s" % size) - interior.bind('', _configure_interior) - - def _configure_canvas(event): - if interior.winfo_reqwidth() != canvas.winfo_width(): - # update the inner frame's width to fill the canvas - canvas.itemconfigure(interior_id, width=canvas.winfo_width()) - canvas.bind('', _configure_canvas) - - return - - -if __name__ == '__main__': - import unittest - unittest.main('idlelib.idle_test.test_configdialog', - verbosity=2, exit=False) - from idlelib.idle_test.htest import run - run(ConfigDialog) diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py new file mode 100644 index 0000000000..d19749a07e --- /dev/null +++ b/Lib/idlelib/configdialog.py @@ -0,0 +1,1439 @@ +"""IDLE Configuration Dialog: support user customization of IDLE by GUI + +Customize font faces, sizes, and colorization attributes. Set indentation +defaults. Customize keybindings. Colorization and keybindings can be +saved as user defined sets. Select startup options including shell/editor +and default window size. Define additional help sources. + +Note that tab width in IDLE is currently fixed at eight due to Tk issues. +Refer to comments in EditorWindow autoindent code for details. + +""" +from tkinter import * +from tkinter.ttk import Scrollbar +import tkinter.messagebox as tkMessageBox +import tkinter.colorchooser as tkColorChooser +import tkinter.font as tkFont + +from idlelib.config import idleConf +from idlelib.dynoption import DynOptionMenu +from idlelib.config_key import GetKeysDialog +from idlelib.config_sec import GetCfgSectionNameDialog +from idlelib.config_help import GetHelpSourceDialog +from idlelib.tabbedpages import TabbedPageSet +from idlelib.textview import view_text +from idlelib import macosx + +class ConfigDialog(Toplevel): + + def __init__(self, parent, title='', _htest=False, _utest=False): + """ + _htest - bool, change box location when running htest + _utest - bool, don't wait_window when running unittest + """ + Toplevel.__init__(self, parent) + self.parent = parent + if _htest: + parent.instance_dict = {} + self.wm_withdraw() + + self.configure(borderwidth=5) + self.title(title or 'IDLE Preferences') + self.geometry( + "+%d+%d" % (parent.winfo_rootx() + 20, + parent.winfo_rooty() + (30 if not _htest else 150))) + #Theme Elements. Each theme element key is its display name. + #The first value of the tuple is the sample area tag name. + #The second value is the display name list sort index. + self.themeElements={ + 'Normal Text': ('normal', '00'), + 'Python Keywords': ('keyword', '01'), + 'Python Definitions': ('definition', '02'), + 'Python Builtins': ('builtin', '03'), + 'Python Comments': ('comment', '04'), + 'Python Strings': ('string', '05'), + 'Selected Text': ('hilite', '06'), + 'Found Text': ('hit', '07'), + 'Cursor': ('cursor', '08'), + 'Editor Breakpoint': ('break', '09'), + 'Shell Normal Text': ('console', '10'), + 'Shell Error Text': ('error', '11'), + 'Shell Stdout Text': ('stdout', '12'), + 'Shell Stderr Text': ('stderr', '13'), + } + self.ResetChangedItems() #load initial values in changed items dict + self.CreateWidgets() + self.resizable(height=FALSE, width=FALSE) + self.transient(parent) + self.grab_set() + self.protocol("WM_DELETE_WINDOW", self.Cancel) + self.tabPages.focus_set() + #key bindings for this dialog + #self.bind('', self.Cancel) #dismiss dialog, no save + #self.bind('', self.Apply) #apply changes, save + #self.bind('', self.Help) #context help + self.LoadConfigs() + self.AttachVarCallbacks() #avoid callbacks during LoadConfigs + + if not _utest: + self.wm_deiconify() + self.wait_window() + + def CreateWidgets(self): + self.tabPages = TabbedPageSet(self, + page_names=['Fonts/Tabs', 'Highlighting', 'Keys', 'General', + 'Extensions']) + self.tabPages.pack(side=TOP, expand=TRUE, fill=BOTH) + self.CreatePageFontTab() + self.CreatePageHighlight() + self.CreatePageKeys() + self.CreatePageGeneral() + self.CreatePageExtensions() + self.create_action_buttons().pack(side=BOTTOM) + + def create_action_buttons(self): + if macosx.isAquaTk(): + # Changing the default padding on OSX results in unreadable + # text in the buttons + paddingArgs = {} + else: + paddingArgs = {'padx':6, 'pady':3} + outer = Frame(self, pady=2) + buttons = Frame(outer, pady=2) + for txt, cmd in ( + ('Ok', self.Ok), + ('Apply', self.Apply), + ('Cancel', self.Cancel), + ('Help', self.Help)): + Button(buttons, text=txt, command=cmd, takefocus=FALSE, + **paddingArgs).pack(side=LEFT, padx=5) + # add space above buttons + Frame(outer, height=2, borderwidth=0).pack(side=TOP) + buttons.pack(side=BOTTOM) + return outer + + def CreatePageFontTab(self): + parent = self.parent + self.fontSize = StringVar(parent) + self.fontBold = BooleanVar(parent) + self.fontName = StringVar(parent) + self.spaceNum = IntVar(parent) + self.editFont = tkFont.Font(parent, ('courier', 10, 'normal')) + + ##widget creation + #body frame + frame = self.tabPages.pages['Fonts/Tabs'].frame + #body section frames + frameFont = LabelFrame( + frame, borderwidth=2, relief=GROOVE, text=' Base Editor Font ') + frameIndent = LabelFrame( + frame, borderwidth=2, relief=GROOVE, text=' Indentation Width ') + #frameFont + frameFontName = Frame(frameFont) + frameFontParam = Frame(frameFont) + labelFontNameTitle = Label( + frameFontName, justify=LEFT, text='Font Face :') + self.listFontName = Listbox( + frameFontName, height=5, takefocus=FALSE, exportselection=FALSE) + self.listFontName.bind( + '', self.OnListFontButtonRelease) + scrollFont = Scrollbar(frameFontName) + scrollFont.config(command=self.listFontName.yview) + self.listFontName.config(yscrollcommand=scrollFont.set) + labelFontSizeTitle = Label(frameFontParam, text='Size :') + self.optMenuFontSize = DynOptionMenu( + frameFontParam, self.fontSize, None, command=self.SetFontSample) + checkFontBold = Checkbutton( + frameFontParam, variable=self.fontBold, onvalue=1, + offvalue=0, text='Bold', command=self.SetFontSample) + frameFontSample = Frame(frameFont, relief=SOLID, borderwidth=1) + self.labelFontSample = Label( + frameFontSample, justify=LEFT, font=self.editFont, + text='AaBbCcDdEe\nFfGgHhIiJjK\n1234567890\n#:+=(){}[]') + #frameIndent + frameIndentSize = Frame(frameIndent) + labelSpaceNumTitle = Label( + frameIndentSize, justify=LEFT, + text='Python Standard: 4 Spaces!') + self.scaleSpaceNum = Scale( + frameIndentSize, variable=self.spaceNum, + orient='horizontal', tickinterval=2, from_=2, to=16) + + #widget packing + #body + frameFont.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) + frameIndent.pack(side=LEFT, padx=5, pady=5, fill=Y) + #frameFont + frameFontName.pack(side=TOP, padx=5, pady=5, fill=X) + frameFontParam.pack(side=TOP, padx=5, pady=5, fill=X) + labelFontNameTitle.pack(side=TOP, anchor=W) + self.listFontName.pack(side=LEFT, expand=TRUE, fill=X) + scrollFont.pack(side=LEFT, fill=Y) + labelFontSizeTitle.pack(side=LEFT, anchor=W) + self.optMenuFontSize.pack(side=LEFT, anchor=W) + checkFontBold.pack(side=LEFT, anchor=W, padx=20) + frameFontSample.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) + self.labelFontSample.pack(expand=TRUE, fill=BOTH) + #frameIndent + frameIndentSize.pack(side=TOP, fill=X) + labelSpaceNumTitle.pack(side=TOP, anchor=W, padx=5) + self.scaleSpaceNum.pack(side=TOP, padx=5, fill=X) + return frame + + def CreatePageHighlight(self): + parent = self.parent + self.builtinTheme = StringVar(parent) + self.customTheme = StringVar(parent) + self.fgHilite = BooleanVar(parent) + self.colour = StringVar(parent) + self.fontName = StringVar(parent) + self.themeIsBuiltin = BooleanVar(parent) + self.highlightTarget = StringVar(parent) + + ##widget creation + #body frame + frame = self.tabPages.pages['Highlighting'].frame + #body section frames + frameCustom = LabelFrame(frame, borderwidth=2, relief=GROOVE, + text=' Custom Highlighting ') + frameTheme = LabelFrame(frame, borderwidth=2, relief=GROOVE, + text=' Highlighting Theme ') + #frameCustom + self.textHighlightSample=Text( + frameCustom, relief=SOLID, borderwidth=1, + font=('courier', 12, ''), cursor='hand2', width=21, height=11, + takefocus=FALSE, highlightthickness=0, wrap=NONE) + text=self.textHighlightSample + text.bind('', lambda e: 'break') + text.bind('', lambda e: 'break') + textAndTags=( + ('#you can click here', 'comment'), ('\n', 'normal'), + ('#to choose items', 'comment'), ('\n', 'normal'), + ('def', 'keyword'), (' ', 'normal'), + ('func', 'definition'), ('(param):\n ', 'normal'), + ('"""string"""', 'string'), ('\n var0 = ', 'normal'), + ("'string'", 'string'), ('\n var1 = ', 'normal'), + ("'selected'", 'hilite'), ('\n var2 = ', 'normal'), + ("'found'", 'hit'), ('\n var3 = ', 'normal'), + ('list', 'builtin'), ('(', 'normal'), + ('None', 'keyword'), (')\n', 'normal'), + (' breakpoint("line")', 'break'), ('\n\n', 'normal'), + (' error ', 'error'), (' ', 'normal'), + ('cursor |', 'cursor'), ('\n ', 'normal'), + ('shell', 'console'), (' ', 'normal'), + ('stdout', 'stdout'), (' ', 'normal'), + ('stderr', 'stderr'), ('\n', 'normal')) + for txTa in textAndTags: + text.insert(END, txTa[0], txTa[1]) + for element in self.themeElements: + def tem(event, elem=element): + event.widget.winfo_toplevel().highlightTarget.set(elem) + text.tag_bind( + self.themeElements[element][0], '', tem) + text.config(state=DISABLED) + self.frameColourSet = Frame(frameCustom, relief=SOLID, borderwidth=1) + frameFgBg = Frame(frameCustom) + buttonSetColour = Button( + self.frameColourSet, text='Choose Colour for :', + command=self.GetColour, highlightthickness=0) + self.optMenuHighlightTarget = DynOptionMenu( + self.frameColourSet, self.highlightTarget, None, + highlightthickness=0) #, command=self.SetHighlightTargetBinding + self.radioFg = Radiobutton( + frameFgBg, variable=self.fgHilite, value=1, + text='Foreground', command=self.SetColourSampleBinding) + self.radioBg=Radiobutton( + frameFgBg, variable=self.fgHilite, value=0, + text='Background', command=self.SetColourSampleBinding) + self.fgHilite.set(1) + buttonSaveCustomTheme = Button( + frameCustom, text='Save as New Custom Theme', + command=self.SaveAsNewTheme) + #frameTheme + labelTypeTitle = Label(frameTheme, text='Select : ') + self.radioThemeBuiltin = Radiobutton( + frameTheme, variable=self.themeIsBuiltin, value=1, + command=self.SetThemeType, text='a Built-in Theme') + self.radioThemeCustom = Radiobutton( + frameTheme, variable=self.themeIsBuiltin, value=0, + command=self.SetThemeType, text='a Custom Theme') + self.optMenuThemeBuiltin = DynOptionMenu( + frameTheme, self.builtinTheme, None, command=None) + self.optMenuThemeCustom=DynOptionMenu( + frameTheme, self.customTheme, None, command=None) + self.buttonDeleteCustomTheme=Button( + frameTheme, text='Delete Custom Theme', + command=self.DeleteCustomTheme) + self.new_custom_theme = Label(frameTheme, bd=2) + + ##widget packing + #body + frameCustom.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) + frameTheme.pack(side=LEFT, padx=5, pady=5, fill=Y) + #frameCustom + self.frameColourSet.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=X) + frameFgBg.pack(side=TOP, padx=5, pady=0) + self.textHighlightSample.pack( + side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) + buttonSetColour.pack(side=TOP, expand=TRUE, fill=X, padx=8, pady=4) + self.optMenuHighlightTarget.pack( + side=TOP, expand=TRUE, fill=X, padx=8, pady=3) + self.radioFg.pack(side=LEFT, anchor=E) + self.radioBg.pack(side=RIGHT, anchor=W) + buttonSaveCustomTheme.pack(side=BOTTOM, fill=X, padx=5, pady=5) + #frameTheme + labelTypeTitle.pack(side=TOP, anchor=W, padx=5, pady=5) + self.radioThemeBuiltin.pack(side=TOP, anchor=W, padx=5) + self.radioThemeCustom.pack(side=TOP, anchor=W, padx=5, pady=2) + self.optMenuThemeBuiltin.pack(side=TOP, fill=X, padx=5, pady=5) + self.optMenuThemeCustom.pack(side=TOP, fill=X, anchor=W, padx=5, pady=5) + self.buttonDeleteCustomTheme.pack(side=TOP, fill=X, padx=5, pady=5) + self.new_custom_theme.pack(side=TOP, fill=X, pady=5) + return frame + + def CreatePageKeys(self): + parent = self.parent + self.bindingTarget = StringVar(parent) + self.builtinKeys = StringVar(parent) + self.customKeys = StringVar(parent) + self.keysAreBuiltin = BooleanVar(parent) + self.keyBinding = StringVar(parent) + + ##widget creation + #body frame + frame = self.tabPages.pages['Keys'].frame + #body section frames + frameCustom = LabelFrame( + frame, borderwidth=2, relief=GROOVE, + text=' Custom Key Bindings ') + frameKeySets = LabelFrame( + frame, borderwidth=2, relief=GROOVE, text=' Key Set ') + #frameCustom + frameTarget = Frame(frameCustom) + labelTargetTitle = Label(frameTarget, text='Action - Key(s)') + scrollTargetY = Scrollbar(frameTarget) + scrollTargetX = Scrollbar(frameTarget, orient=HORIZONTAL) + self.listBindings = Listbox( + frameTarget, takefocus=FALSE, exportselection=FALSE) + self.listBindings.bind('', self.KeyBindingSelected) + scrollTargetY.config(command=self.listBindings.yview) + scrollTargetX.config(command=self.listBindings.xview) + self.listBindings.config(yscrollcommand=scrollTargetY.set) + self.listBindings.config(xscrollcommand=scrollTargetX.set) + self.buttonNewKeys = Button( + frameCustom, text='Get New Keys for Selection', + command=self.GetNewKeys, state=DISABLED) + #frameKeySets + frames = [Frame(frameKeySets, padx=2, pady=2, borderwidth=0) + for i in range(2)] + self.radioKeysBuiltin = Radiobutton( + frames[0], variable=self.keysAreBuiltin, value=1, + command=self.SetKeysType, text='Use a Built-in Key Set') + self.radioKeysCustom = Radiobutton( + frames[0], variable=self.keysAreBuiltin, value=0, + command=self.SetKeysType, text='Use a Custom Key Set') + self.optMenuKeysBuiltin = DynOptionMenu( + frames[0], self.builtinKeys, None, command=None) + self.optMenuKeysCustom = DynOptionMenu( + frames[0], self.customKeys, None, command=None) + self.buttonDeleteCustomKeys = Button( + frames[1], text='Delete Custom Key Set', + command=self.DeleteCustomKeys) + buttonSaveCustomKeys = Button( + frames[1], text='Save as New Custom Key Set', + command=self.SaveAsNewKeySet) + + ##widget packing + #body + frameCustom.pack(side=BOTTOM, padx=5, pady=5, expand=TRUE, fill=BOTH) + frameKeySets.pack(side=BOTTOM, padx=5, pady=5, fill=BOTH) + #frameCustom + self.buttonNewKeys.pack(side=BOTTOM, fill=X, padx=5, pady=5) + frameTarget.pack(side=LEFT, padx=5, pady=5, expand=TRUE, fill=BOTH) + #frame target + frameTarget.columnconfigure(0, weight=1) + frameTarget.rowconfigure(1, weight=1) + labelTargetTitle.grid(row=0, column=0, columnspan=2, sticky=W) + self.listBindings.grid(row=1, column=0, sticky=NSEW) + scrollTargetY.grid(row=1, column=1, sticky=NS) + scrollTargetX.grid(row=2, column=0, sticky=EW) + #frameKeySets + self.radioKeysBuiltin.grid(row=0, column=0, sticky=W+NS) + self.radioKeysCustom.grid(row=1, column=0, sticky=W+NS) + self.optMenuKeysBuiltin.grid(row=0, column=1, sticky=NSEW) + self.optMenuKeysCustom.grid(row=1, column=1, sticky=NSEW) + self.buttonDeleteCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2) + buttonSaveCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2) + frames[0].pack(side=TOP, fill=BOTH, expand=True) + frames[1].pack(side=TOP, fill=X, expand=True, pady=2) + return frame + + def CreatePageGeneral(self): + parent = self.parent + self.winWidth = StringVar(parent) + self.winHeight = StringVar(parent) + self.startupEdit = IntVar(parent) + self.autoSave = IntVar(parent) + self.encoding = StringVar(parent) + self.userHelpBrowser = BooleanVar(parent) + self.helpBrowser = StringVar(parent) + + #widget creation + #body + frame = self.tabPages.pages['General'].frame + #body section frames + frameRun = LabelFrame(frame, borderwidth=2, relief=GROOVE, + text=' Startup Preferences ') + frameSave = LabelFrame(frame, borderwidth=2, relief=GROOVE, + text=' Autosave Preferences ') + frameWinSize = Frame(frame, borderwidth=2, relief=GROOVE) + frameHelp = LabelFrame(frame, borderwidth=2, relief=GROOVE, + text=' Additional Help Sources ') + #frameRun + labelRunChoiceTitle = Label(frameRun, text='At Startup') + radioStartupEdit = Radiobutton( + frameRun, variable=self.startupEdit, value=1, + command=self.SetKeysType, text="Open Edit Window") + radioStartupShell = Radiobutton( + frameRun, variable=self.startupEdit, value=0, + command=self.SetKeysType, text='Open Shell Window') + #frameSave + labelRunSaveTitle = Label(frameSave, text='At Start of Run (F5) ') + radioSaveAsk = Radiobutton( + frameSave, variable=self.autoSave, value=0, + command=self.SetKeysType, text="Prompt to Save") + radioSaveAuto = Radiobutton( + frameSave, variable=self.autoSave, value=1, + command=self.SetKeysType, text='No Prompt') + #frameWinSize + labelWinSizeTitle = Label( + frameWinSize, text='Initial Window Size (in characters)') + labelWinWidthTitle = Label(frameWinSize, text='Width') + entryWinWidth = Entry( + frameWinSize, textvariable=self.winWidth, width=3) + labelWinHeightTitle = Label(frameWinSize, text='Height') + entryWinHeight = Entry( + frameWinSize, textvariable=self.winHeight, width=3) + #frameHelp + frameHelpList = Frame(frameHelp) + frameHelpListButtons = Frame(frameHelpList) + scrollHelpList = Scrollbar(frameHelpList) + self.listHelp = Listbox( + frameHelpList, height=5, takefocus=FALSE, + exportselection=FALSE) + scrollHelpList.config(command=self.listHelp.yview) + self.listHelp.config(yscrollcommand=scrollHelpList.set) + self.listHelp.bind('', self.HelpSourceSelected) + self.buttonHelpListEdit = Button( + frameHelpListButtons, text='Edit', state=DISABLED, + width=8, command=self.HelpListItemEdit) + self.buttonHelpListAdd = Button( + frameHelpListButtons, text='Add', + width=8, command=self.HelpListItemAdd) + self.buttonHelpListRemove = Button( + frameHelpListButtons, text='Remove', state=DISABLED, + width=8, command=self.HelpListItemRemove) + + #widget packing + #body + frameRun.pack(side=TOP, padx=5, pady=5, fill=X) + frameSave.pack(side=TOP, padx=5, pady=5, fill=X) + frameWinSize.pack(side=TOP, padx=5, pady=5, fill=X) + frameHelp.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) + #frameRun + labelRunChoiceTitle.pack(side=LEFT, anchor=W, padx=5, pady=5) + radioStartupShell.pack(side=RIGHT, anchor=W, padx=5, pady=5) + radioStartupEdit.pack(side=RIGHT, anchor=W, padx=5, pady=5) + #frameSave + labelRunSaveTitle.pack(side=LEFT, anchor=W, padx=5, pady=5) + radioSaveAuto.pack(side=RIGHT, anchor=W, padx=5, pady=5) + radioSaveAsk.pack(side=RIGHT, anchor=W, padx=5, pady=5) + #frameWinSize + labelWinSizeTitle.pack(side=LEFT, anchor=W, padx=5, pady=5) + entryWinHeight.pack(side=RIGHT, anchor=E, padx=10, pady=5) + labelWinHeightTitle.pack(side=RIGHT, anchor=E, pady=5) + entryWinWidth.pack(side=RIGHT, anchor=E, padx=10, pady=5) + labelWinWidthTitle.pack(side=RIGHT, anchor=E, pady=5) + #frameHelp + frameHelpListButtons.pack(side=RIGHT, padx=5, pady=5, fill=Y) + frameHelpList.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) + scrollHelpList.pack(side=RIGHT, anchor=W, fill=Y) + self.listHelp.pack(side=LEFT, anchor=E, expand=TRUE, fill=BOTH) + self.buttonHelpListEdit.pack(side=TOP, anchor=W, pady=5) + self.buttonHelpListAdd.pack(side=TOP, anchor=W) + self.buttonHelpListRemove.pack(side=TOP, anchor=W, pady=5) + return frame + + def AttachVarCallbacks(self): + self.fontSize.trace_variable('w', self.VarChanged_font) + self.fontName.trace_variable('w', self.VarChanged_font) + self.fontBold.trace_variable('w', self.VarChanged_font) + self.spaceNum.trace_variable('w', self.VarChanged_spaceNum) + self.colour.trace_variable('w', self.VarChanged_colour) + self.builtinTheme.trace_variable('w', self.VarChanged_builtinTheme) + self.customTheme.trace_variable('w', self.VarChanged_customTheme) + self.themeIsBuiltin.trace_variable('w', self.VarChanged_themeIsBuiltin) + self.highlightTarget.trace_variable('w', self.VarChanged_highlightTarget) + self.keyBinding.trace_variable('w', self.VarChanged_keyBinding) + self.builtinKeys.trace_variable('w', self.VarChanged_builtinKeys) + self.customKeys.trace_variable('w', self.VarChanged_customKeys) + self.keysAreBuiltin.trace_variable('w', self.VarChanged_keysAreBuiltin) + self.winWidth.trace_variable('w', self.VarChanged_winWidth) + self.winHeight.trace_variable('w', self.VarChanged_winHeight) + self.startupEdit.trace_variable('w', self.VarChanged_startupEdit) + self.autoSave.trace_variable('w', self.VarChanged_autoSave) + self.encoding.trace_variable('w', self.VarChanged_encoding) + + def remove_var_callbacks(self): + "Remove callbacks to prevent memory leaks." + for var in ( + self.fontSize, self.fontName, self.fontBold, + self.spaceNum, self.colour, self.builtinTheme, + self.customTheme, self.themeIsBuiltin, self.highlightTarget, + self.keyBinding, self.builtinKeys, self.customKeys, + self.keysAreBuiltin, self.winWidth, self.winHeight, + self.startupEdit, self.autoSave, self.encoding,): + var.trace_vdelete('w', var.trace_vinfo()[0][1]) + + def VarChanged_font(self, *params): + '''When one font attribute changes, save them all, as they are + not independent from each other. In particular, when we are + overriding the default font, we need to write out everything. + ''' + value = self.fontName.get() + self.AddChangedItem('main', 'EditorWindow', 'font', value) + value = self.fontSize.get() + self.AddChangedItem('main', 'EditorWindow', 'font-size', value) + value = self.fontBold.get() + self.AddChangedItem('main', 'EditorWindow', 'font-bold', value) + + def VarChanged_spaceNum(self, *params): + value = self.spaceNum.get() + self.AddChangedItem('main', 'Indent', 'num-spaces', value) + + def VarChanged_colour(self, *params): + self.OnNewColourSet() + + def VarChanged_builtinTheme(self, *params): + value = self.builtinTheme.get() + if value == 'IDLE Dark': + if idleConf.GetOption('main', 'Theme', 'name') != 'IDLE New': + self.AddChangedItem('main', 'Theme', 'name', 'IDLE Classic') + self.AddChangedItem('main', 'Theme', 'name2', value) + self.new_custom_theme.config(text='New theme, see Help', + fg='#500000') + else: + self.AddChangedItem('main', 'Theme', 'name', value) + self.AddChangedItem('main', 'Theme', 'name2', '') + self.new_custom_theme.config(text='', fg='black') + self.PaintThemeSample() + + def VarChanged_customTheme(self, *params): + value = self.customTheme.get() + if value != '- no custom themes -': + self.AddChangedItem('main', 'Theme', 'name', value) + self.PaintThemeSample() + + def VarChanged_themeIsBuiltin(self, *params): + value = self.themeIsBuiltin.get() + self.AddChangedItem('main', 'Theme', 'default', value) + if value: + self.VarChanged_builtinTheme() + else: + self.VarChanged_customTheme() + + def VarChanged_highlightTarget(self, *params): + self.SetHighlightTarget() + + def VarChanged_keyBinding(self, *params): + value = self.keyBinding.get() + keySet = self.customKeys.get() + event = self.listBindings.get(ANCHOR).split()[0] + if idleConf.IsCoreBinding(event): + #this is a core keybinding + self.AddChangedItem('keys', keySet, event, value) + else: #this is an extension key binding + extName = idleConf.GetExtnNameForEvent(event) + extKeybindSection = extName + '_cfgBindings' + self.AddChangedItem('extensions', extKeybindSection, event, value) + + def VarChanged_builtinKeys(self, *params): + value = self.builtinKeys.get() + self.AddChangedItem('main', 'Keys', 'name', value) + self.LoadKeysList(value) + + def VarChanged_customKeys(self, *params): + value = self.customKeys.get() + if value != '- no custom keys -': + self.AddChangedItem('main', 'Keys', 'name', value) + self.LoadKeysList(value) + + def VarChanged_keysAreBuiltin(self, *params): + value = self.keysAreBuiltin.get() + self.AddChangedItem('main', 'Keys', 'default', value) + if value: + self.VarChanged_builtinKeys() + else: + self.VarChanged_customKeys() + + def VarChanged_winWidth(self, *params): + value = self.winWidth.get() + self.AddChangedItem('main', 'EditorWindow', 'width', value) + + def VarChanged_winHeight(self, *params): + value = self.winHeight.get() + self.AddChangedItem('main', 'EditorWindow', 'height', value) + + def VarChanged_startupEdit(self, *params): + value = self.startupEdit.get() + self.AddChangedItem('main', 'General', 'editor-on-startup', value) + + def VarChanged_autoSave(self, *params): + value = self.autoSave.get() + self.AddChangedItem('main', 'General', 'autosave', value) + + def VarChanged_encoding(self, *params): + value = self.encoding.get() + self.AddChangedItem('main', 'EditorWindow', 'encoding', value) + + def ResetChangedItems(self): + #When any config item is changed in this dialog, an entry + #should be made in the relevant section (config type) of this + #dictionary. The key should be the config file section name and the + #value a dictionary, whose key:value pairs are item=value pairs for + #that config file section. + self.changedItems = {'main':{}, 'highlight':{}, 'keys':{}, + 'extensions':{}} + + def AddChangedItem(self, typ, section, item, value): + value = str(value) #make sure we use a string + if section not in self.changedItems[typ]: + self.changedItems[typ][section] = {} + self.changedItems[typ][section][item] = value + + def GetDefaultItems(self): + dItems={'main':{}, 'highlight':{}, 'keys':{}, 'extensions':{}} + for configType in dItems: + sections = idleConf.GetSectionList('default', configType) + for section in sections: + dItems[configType][section] = {} + options = idleConf.defaultCfg[configType].GetOptionList(section) + for option in options: + dItems[configType][section][option] = ( + idleConf.defaultCfg[configType].Get(section, option)) + return dItems + + def SetThemeType(self): + if self.themeIsBuiltin.get(): + self.optMenuThemeBuiltin.config(state=NORMAL) + self.optMenuThemeCustom.config(state=DISABLED) + self.buttonDeleteCustomTheme.config(state=DISABLED) + else: + self.optMenuThemeBuiltin.config(state=DISABLED) + self.radioThemeCustom.config(state=NORMAL) + self.optMenuThemeCustom.config(state=NORMAL) + self.buttonDeleteCustomTheme.config(state=NORMAL) + + def SetKeysType(self): + if self.keysAreBuiltin.get(): + self.optMenuKeysBuiltin.config(state=NORMAL) + self.optMenuKeysCustom.config(state=DISABLED) + self.buttonDeleteCustomKeys.config(state=DISABLED) + else: + self.optMenuKeysBuiltin.config(state=DISABLED) + self.radioKeysCustom.config(state=NORMAL) + self.optMenuKeysCustom.config(state=NORMAL) + self.buttonDeleteCustomKeys.config(state=NORMAL) + + def GetNewKeys(self): + listIndex = self.listBindings.index(ANCHOR) + binding = self.listBindings.get(listIndex) + bindName = binding.split()[0] #first part, up to first space + if self.keysAreBuiltin.get(): + currentKeySetName = self.builtinKeys.get() + else: + currentKeySetName = self.customKeys.get() + currentBindings = idleConf.GetCurrentKeySet() + if currentKeySetName in self.changedItems['keys']: #unsaved changes + keySetChanges = self.changedItems['keys'][currentKeySetName] + for event in keySetChanges: + currentBindings[event] = keySetChanges[event].split() + currentKeySequences = list(currentBindings.values()) + newKeys = GetKeysDialog(self, 'Get New Keys', bindName, + currentKeySequences).result + if newKeys: #new keys were specified + if self.keysAreBuiltin.get(): #current key set is a built-in + message = ('Your changes will be saved as a new Custom Key Set.' + ' Enter a name for your new Custom Key Set below.') + newKeySet = self.GetNewKeysName(message) + if not newKeySet: #user cancelled custom key set creation + self.listBindings.select_set(listIndex) + self.listBindings.select_anchor(listIndex) + return + else: #create new custom key set based on previously active key set + self.CreateNewKeySet(newKeySet) + self.listBindings.delete(listIndex) + self.listBindings.insert(listIndex, bindName+' - '+newKeys) + self.listBindings.select_set(listIndex) + self.listBindings.select_anchor(listIndex) + self.keyBinding.set(newKeys) + else: + self.listBindings.select_set(listIndex) + self.listBindings.select_anchor(listIndex) + + def GetNewKeysName(self, message): + usedNames = (idleConf.GetSectionList('user', 'keys') + + idleConf.GetSectionList('default', 'keys')) + newKeySet = GetCfgSectionNameDialog( + self, 'New Custom Key Set', message, usedNames).result + return newKeySet + + def SaveAsNewKeySet(self): + newKeysName = self.GetNewKeysName('New Key Set Name:') + if newKeysName: + self.CreateNewKeySet(newKeysName) + + def KeyBindingSelected(self, event): + self.buttonNewKeys.config(state=NORMAL) + + def CreateNewKeySet(self, newKeySetName): + #creates new custom key set based on the previously active key set, + #and makes the new key set active + if self.keysAreBuiltin.get(): + prevKeySetName = self.builtinKeys.get() + else: + prevKeySetName = self.customKeys.get() + prevKeys = idleConf.GetCoreKeys(prevKeySetName) + newKeys = {} + for event in prevKeys: #add key set to changed items + eventName = event[2:-2] #trim off the angle brackets + binding = ' '.join(prevKeys[event]) + newKeys[eventName] = binding + #handle any unsaved changes to prev key set + if prevKeySetName in self.changedItems['keys']: + keySetChanges = self.changedItems['keys'][prevKeySetName] + for event in keySetChanges: + newKeys[event] = keySetChanges[event] + #save the new theme + self.SaveNewKeySet(newKeySetName, newKeys) + #change gui over to the new key set + customKeyList = idleConf.GetSectionList('user', 'keys') + customKeyList.sort() + self.optMenuKeysCustom.SetMenu(customKeyList, newKeySetName) + self.keysAreBuiltin.set(0) + self.SetKeysType() + + def LoadKeysList(self, keySetName): + reselect = 0 + newKeySet = 0 + if self.listBindings.curselection(): + reselect = 1 + listIndex = self.listBindings.index(ANCHOR) + keySet = idleConf.GetKeySet(keySetName) + bindNames = list(keySet.keys()) + bindNames.sort() + self.listBindings.delete(0, END) + for bindName in bindNames: + key = ' '.join(keySet[bindName]) #make key(s) into a string + bindName = bindName[2:-2] #trim off the angle brackets + if keySetName in self.changedItems['keys']: + #handle any unsaved changes to this key set + if bindName in self.changedItems['keys'][keySetName]: + key = self.changedItems['keys'][keySetName][bindName] + self.listBindings.insert(END, bindName+' - '+key) + if reselect: + self.listBindings.see(listIndex) + self.listBindings.select_set(listIndex) + self.listBindings.select_anchor(listIndex) + + def DeleteCustomKeys(self): + keySetName=self.customKeys.get() + delmsg = 'Are you sure you wish to delete the key set %r ?' + if not tkMessageBox.askyesno( + 'Delete Key Set', delmsg % keySetName, parent=self): + return + self.DeactivateCurrentConfig() + #remove key set from config + idleConf.userCfg['keys'].remove_section(keySetName) + if keySetName in self.changedItems['keys']: + del(self.changedItems['keys'][keySetName]) + #write changes + idleConf.userCfg['keys'].Save() + #reload user key set list + itemList = idleConf.GetSectionList('user', 'keys') + itemList.sort() + if not itemList: + self.radioKeysCustom.config(state=DISABLED) + self.optMenuKeysCustom.SetMenu(itemList, '- no custom keys -') + else: + self.optMenuKeysCustom.SetMenu(itemList, itemList[0]) + #revert to default key set + self.keysAreBuiltin.set(idleConf.defaultCfg['main'].Get('Keys', 'default')) + self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys', 'name')) + #user can't back out of these changes, they must be applied now + self.SaveAllChangedConfigs() + self.ActivateConfigChanges() + self.SetKeysType() + + def DeleteCustomTheme(self): + themeName = self.customTheme.get() + delmsg = 'Are you sure you wish to delete the theme %r ?' + if not tkMessageBox.askyesno( + 'Delete Theme', delmsg % themeName, parent=self): + return + self.DeactivateCurrentConfig() + #remove theme from config + idleConf.userCfg['highlight'].remove_section(themeName) + if themeName in self.changedItems['highlight']: + del(self.changedItems['highlight'][themeName]) + #write changes + idleConf.userCfg['highlight'].Save() + #reload user theme list + itemList = idleConf.GetSectionList('user', 'highlight') + itemList.sort() + if not itemList: + self.radioThemeCustom.config(state=DISABLED) + self.optMenuThemeCustom.SetMenu(itemList, '- no custom themes -') + else: + self.optMenuThemeCustom.SetMenu(itemList, itemList[0]) + #revert to default theme + self.themeIsBuiltin.set(idleConf.defaultCfg['main'].Get('Theme', 'default')) + self.builtinTheme.set(idleConf.defaultCfg['main'].Get('Theme', 'name')) + #user can't back out of these changes, they must be applied now + self.SaveAllChangedConfigs() + self.ActivateConfigChanges() + self.SetThemeType() + + def GetColour(self): + target = self.highlightTarget.get() + prevColour = self.frameColourSet.cget('bg') + rgbTuplet, colourString = tkColorChooser.askcolor( + parent=self, title='Pick new colour for : '+target, + initialcolor=prevColour) + if colourString and (colourString != prevColour): + #user didn't cancel, and they chose a new colour + if self.themeIsBuiltin.get(): #current theme is a built-in + message = ('Your changes will be saved as a new Custom Theme. ' + 'Enter a name for your new Custom Theme below.') + newTheme = self.GetNewThemeName(message) + if not newTheme: #user cancelled custom theme creation + return + else: #create new custom theme based on previously active theme + self.CreateNewTheme(newTheme) + self.colour.set(colourString) + else: #current theme is user defined + self.colour.set(colourString) + + def OnNewColourSet(self): + newColour=self.colour.get() + self.frameColourSet.config(bg=newColour) #set sample + plane ='foreground' if self.fgHilite.get() else 'background' + sampleElement = self.themeElements[self.highlightTarget.get()][0] + self.textHighlightSample.tag_config(sampleElement, **{plane:newColour}) + theme = self.customTheme.get() + themeElement = sampleElement + '-' + plane + self.AddChangedItem('highlight', theme, themeElement, newColour) + + def GetNewThemeName(self, message): + usedNames = (idleConf.GetSectionList('user', 'highlight') + + idleConf.GetSectionList('default', 'highlight')) + newTheme = GetCfgSectionNameDialog( + self, 'New Custom Theme', message, usedNames).result + return newTheme + + def SaveAsNewTheme(self): + newThemeName = self.GetNewThemeName('New Theme Name:') + if newThemeName: + self.CreateNewTheme(newThemeName) + + def CreateNewTheme(self, newThemeName): + #creates new custom theme based on the previously active theme, + #and makes the new theme active + if self.themeIsBuiltin.get(): + themeType = 'default' + themeName = self.builtinTheme.get() + else: + themeType = 'user' + themeName = self.customTheme.get() + newTheme = idleConf.GetThemeDict(themeType, themeName) + #apply any of the old theme's unsaved changes to the new theme + if themeName in self.changedItems['highlight']: + themeChanges = self.changedItems['highlight'][themeName] + for element in themeChanges: + newTheme[element] = themeChanges[element] + #save the new theme + self.SaveNewTheme(newThemeName, newTheme) + #change gui over to the new theme + customThemeList = idleConf.GetSectionList('user', 'highlight') + customThemeList.sort() + self.optMenuThemeCustom.SetMenu(customThemeList, newThemeName) + self.themeIsBuiltin.set(0) + self.SetThemeType() + + def OnListFontButtonRelease(self, event): + font = self.listFontName.get(ANCHOR) + self.fontName.set(font.lower()) + self.SetFontSample() + + def SetFontSample(self, event=None): + fontName = self.fontName.get() + fontWeight = tkFont.BOLD if self.fontBold.get() else tkFont.NORMAL + newFont = (fontName, self.fontSize.get(), fontWeight) + self.labelFontSample.config(font=newFont) + self.textHighlightSample.configure(font=newFont) + + def SetHighlightTarget(self): + if self.highlightTarget.get() == 'Cursor': #bg not possible + self.radioFg.config(state=DISABLED) + self.radioBg.config(state=DISABLED) + self.fgHilite.set(1) + else: #both fg and bg can be set + self.radioFg.config(state=NORMAL) + self.radioBg.config(state=NORMAL) + self.fgHilite.set(1) + self.SetColourSample() + + def SetColourSampleBinding(self, *args): + self.SetColourSample() + + def SetColourSample(self): + #set the colour smaple area + tag = self.themeElements[self.highlightTarget.get()][0] + plane = 'foreground' if self.fgHilite.get() else 'background' + colour = self.textHighlightSample.tag_cget(tag, plane) + self.frameColourSet.config(bg=colour) + + def PaintThemeSample(self): + if self.themeIsBuiltin.get(): #a default theme + theme = self.builtinTheme.get() + else: #a user theme + theme = self.customTheme.get() + for elementTitle in self.themeElements: + element = self.themeElements[elementTitle][0] + colours = idleConf.GetHighlight(theme, element) + if element == 'cursor': #cursor sample needs special painting + colours['background'] = idleConf.GetHighlight( + theme, 'normal', fgBg='bg') + #handle any unsaved changes to this theme + if theme in self.changedItems['highlight']: + themeDict = self.changedItems['highlight'][theme] + if element + '-foreground' in themeDict: + colours['foreground'] = themeDict[element + '-foreground'] + if element + '-background' in themeDict: + colours['background'] = themeDict[element + '-background'] + self.textHighlightSample.tag_config(element, **colours) + self.SetColourSample() + + def HelpSourceSelected(self, event): + self.SetHelpListButtonStates() + + def SetHelpListButtonStates(self): + if self.listHelp.size() < 1: #no entries in list + self.buttonHelpListEdit.config(state=DISABLED) + self.buttonHelpListRemove.config(state=DISABLED) + else: #there are some entries + if self.listHelp.curselection(): #there currently is a selection + self.buttonHelpListEdit.config(state=NORMAL) + self.buttonHelpListRemove.config(state=NORMAL) + else: #there currently is not a selection + self.buttonHelpListEdit.config(state=DISABLED) + self.buttonHelpListRemove.config(state=DISABLED) + + def HelpListItemAdd(self): + helpSource = GetHelpSourceDialog(self, 'New Help Source').result + if helpSource: + self.userHelpList.append((helpSource[0], helpSource[1])) + self.listHelp.insert(END, helpSource[0]) + self.UpdateUserHelpChangedItems() + self.SetHelpListButtonStates() + + def HelpListItemEdit(self): + itemIndex = self.listHelp.index(ANCHOR) + helpSource = self.userHelpList[itemIndex] + newHelpSource = GetHelpSourceDialog( + self, 'Edit Help Source', menuItem=helpSource[0], + filePath=helpSource[1]).result + if (not newHelpSource) or (newHelpSource == helpSource): + return #no changes + self.userHelpList[itemIndex] = newHelpSource + self.listHelp.delete(itemIndex) + self.listHelp.insert(itemIndex, newHelpSource[0]) + self.UpdateUserHelpChangedItems() + self.SetHelpListButtonStates() + + def HelpListItemRemove(self): + itemIndex = self.listHelp.index(ANCHOR) + del(self.userHelpList[itemIndex]) + self.listHelp.delete(itemIndex) + self.UpdateUserHelpChangedItems() + self.SetHelpListButtonStates() + + def UpdateUserHelpChangedItems(self): + "Clear and rebuild the HelpFiles section in self.changedItems" + self.changedItems['main']['HelpFiles'] = {} + for num in range(1, len(self.userHelpList) + 1): + self.AddChangedItem( + 'main', 'HelpFiles', str(num), + ';'.join(self.userHelpList[num-1][:2])) + + def LoadFontCfg(self): + ##base editor font selection list + fonts = list(tkFont.families(self)) + fonts.sort() + for font in fonts: + self.listFontName.insert(END, font) + configuredFont = idleConf.GetFont(self, 'main', 'EditorWindow') + fontName = configuredFont[0].lower() + fontSize = configuredFont[1] + fontBold = configuredFont[2]=='bold' + self.fontName.set(fontName) + lc_fonts = [s.lower() for s in fonts] + try: + currentFontIndex = lc_fonts.index(fontName) + self.listFontName.see(currentFontIndex) + self.listFontName.select_set(currentFontIndex) + self.listFontName.select_anchor(currentFontIndex) + except ValueError: + pass + ##font size dropdown + self.optMenuFontSize.SetMenu(('7', '8', '9', '10', '11', '12', '13', + '14', '16', '18', '20', '22'), fontSize ) + ##fontWeight + self.fontBold.set(fontBold) + ##font sample + self.SetFontSample() + + def LoadTabCfg(self): + ##indent sizes + spaceNum = idleConf.GetOption( + 'main', 'Indent', 'num-spaces', default=4, type='int') + self.spaceNum.set(spaceNum) + + def LoadThemeCfg(self): + ##current theme type radiobutton + self.themeIsBuiltin.set(idleConf.GetOption( + 'main', 'Theme', 'default', type='bool', default=1)) + ##currently set theme + currentOption = idleConf.CurrentTheme() + ##load available theme option menus + if self.themeIsBuiltin.get(): #default theme selected + itemList = idleConf.GetSectionList('default', 'highlight') + itemList.sort() + self.optMenuThemeBuiltin.SetMenu(itemList, currentOption) + itemList = idleConf.GetSectionList('user', 'highlight') + itemList.sort() + if not itemList: + self.radioThemeCustom.config(state=DISABLED) + self.customTheme.set('- no custom themes -') + else: + self.optMenuThemeCustom.SetMenu(itemList, itemList[0]) + else: #user theme selected + itemList = idleConf.GetSectionList('user', 'highlight') + itemList.sort() + self.optMenuThemeCustom.SetMenu(itemList, currentOption) + itemList = idleConf.GetSectionList('default', 'highlight') + itemList.sort() + self.optMenuThemeBuiltin.SetMenu(itemList, itemList[0]) + self.SetThemeType() + ##load theme element option menu + themeNames = list(self.themeElements.keys()) + themeNames.sort(key=lambda x: self.themeElements[x][1]) + self.optMenuHighlightTarget.SetMenu(themeNames, themeNames[0]) + self.PaintThemeSample() + self.SetHighlightTarget() + + def LoadKeyCfg(self): + ##current keys type radiobutton + self.keysAreBuiltin.set(idleConf.GetOption( + 'main', 'Keys', 'default', type='bool', default=1)) + ##currently set keys + currentOption = idleConf.CurrentKeys() + ##load available keyset option menus + if self.keysAreBuiltin.get(): #default theme selected + itemList = idleConf.GetSectionList('default', 'keys') + itemList.sort() + self.optMenuKeysBuiltin.SetMenu(itemList, currentOption) + itemList = idleConf.GetSectionList('user', 'keys') + itemList.sort() + if not itemList: + self.radioKeysCustom.config(state=DISABLED) + self.customKeys.set('- no custom keys -') + else: + self.optMenuKeysCustom.SetMenu(itemList, itemList[0]) + else: #user key set selected + itemList = idleConf.GetSectionList('user', 'keys') + itemList.sort() + self.optMenuKeysCustom.SetMenu(itemList, currentOption) + itemList = idleConf.GetSectionList('default', 'keys') + itemList.sort() + self.optMenuKeysBuiltin.SetMenu(itemList, itemList[0]) + self.SetKeysType() + ##load keyset element list + keySetName = idleConf.CurrentKeys() + self.LoadKeysList(keySetName) + + def LoadGeneralCfg(self): + #startup state + self.startupEdit.set(idleConf.GetOption( + 'main', 'General', 'editor-on-startup', default=1, type='bool')) + #autosave state + self.autoSave.set(idleConf.GetOption( + 'main', 'General', 'autosave', default=0, type='bool')) + #initial window size + self.winWidth.set(idleConf.GetOption( + 'main', 'EditorWindow', 'width', type='int')) + self.winHeight.set(idleConf.GetOption( + 'main', 'EditorWindow', 'height', type='int')) + # default source encoding + self.encoding.set(idleConf.GetOption( + 'main', 'EditorWindow', 'encoding', default='none')) + # additional help sources + self.userHelpList = idleConf.GetAllExtraHelpSourcesList() + for helpItem in self.userHelpList: + self.listHelp.insert(END, helpItem[0]) + self.SetHelpListButtonStates() + + def LoadConfigs(self): + """ + load configuration from default and user config files and populate + the widgets on the config dialog pages. + """ + ### fonts / tabs page + self.LoadFontCfg() + self.LoadTabCfg() + ### highlighting page + self.LoadThemeCfg() + ### keys page + self.LoadKeyCfg() + ### general page + self.LoadGeneralCfg() + # note: extension page handled separately + + def SaveNewKeySet(self, keySetName, keySet): + """ + save a newly created core key set. + keySetName - string, the name of the new key set + keySet - dictionary containing the new key set + """ + if not idleConf.userCfg['keys'].has_section(keySetName): + idleConf.userCfg['keys'].add_section(keySetName) + for event in keySet: + value = keySet[event] + idleConf.userCfg['keys'].SetOption(keySetName, event, value) + + def SaveNewTheme(self, themeName, theme): + """ + save a newly created theme. + themeName - string, the name of the new theme + theme - dictionary containing the new theme + """ + if not idleConf.userCfg['highlight'].has_section(themeName): + idleConf.userCfg['highlight'].add_section(themeName) + for element in theme: + value = theme[element] + idleConf.userCfg['highlight'].SetOption(themeName, element, value) + + def SetUserValue(self, configType, section, item, value): + if idleConf.defaultCfg[configType].has_option(section, item): + if idleConf.defaultCfg[configType].Get(section, item) == value: + #the setting equals a default setting, remove it from user cfg + return idleConf.userCfg[configType].RemoveOption(section, item) + #if we got here set the option + return idleConf.userCfg[configType].SetOption(section, item, value) + + def SaveAllChangedConfigs(self): + "Save configuration changes to the user config file." + idleConf.userCfg['main'].Save() + for configType in self.changedItems: + cfgTypeHasChanges = False + for section in self.changedItems[configType]: + if section == 'HelpFiles': + #this section gets completely replaced + idleConf.userCfg['main'].remove_section('HelpFiles') + cfgTypeHasChanges = True + for item in self.changedItems[configType][section]: + value = self.changedItems[configType][section][item] + if self.SetUserValue(configType, section, item, value): + cfgTypeHasChanges = True + if cfgTypeHasChanges: + idleConf.userCfg[configType].Save() + for configType in ['keys', 'highlight']: + # save these even if unchanged! + idleConf.userCfg[configType].Save() + self.ResetChangedItems() #clear the changed items dict + self.save_all_changed_extensions() # uses a different mechanism + + def DeactivateCurrentConfig(self): + #Before a config is saved, some cleanup of current + #config must be done - remove the previous keybindings + winInstances = self.parent.instance_dict.keys() + for instance in winInstances: + instance.RemoveKeybindings() + + def ActivateConfigChanges(self): + "Dynamically apply configuration changes" + winInstances = self.parent.instance_dict.keys() + for instance in winInstances: + instance.ResetColorizer() + instance.ResetFont() + instance.set_notabs_indentwidth() + instance.ApplyKeybindings() + instance.reset_help_menu_entries() + + def Cancel(self): + self.destroy() + + def Ok(self): + self.Apply() + self.destroy() + + def Apply(self): + self.DeactivateCurrentConfig() + self.SaveAllChangedConfigs() + self.ActivateConfigChanges() + + def Help(self): + page = self.tabPages._current_page + view_text(self, title='Help for IDLE preferences', + text=help_common+help_pages.get(page, '')) + + def CreatePageExtensions(self): + """Part of the config dialog used for configuring IDLE extensions. + + This code is generic - it works for any and all IDLE extensions. + + IDLE extensions save their configuration options using idleConf. + This code reads the current configuration using idleConf, supplies a + GUI interface to change the configuration values, and saves the + changes using idleConf. + + Not all changes take effect immediately - some may require restarting IDLE. + This depends on each extension's implementation. + + All values are treated as text, and it is up to the user to supply + reasonable values. The only exception to this are the 'enable*' options, + which are boolean, and can be toggled with a True/False button. + """ + parent = self.parent + frame = self.tabPages.pages['Extensions'].frame + self.ext_defaultCfg = idleConf.defaultCfg['extensions'] + self.ext_userCfg = idleConf.userCfg['extensions'] + self.is_int = self.register(is_int) + self.load_extensions() + # create widgets - a listbox shows all available extensions, with the + # controls for the extension selected in the listbox to the right + self.extension_names = StringVar(self) + frame.rowconfigure(0, weight=1) + frame.columnconfigure(2, weight=1) + self.extension_list = Listbox(frame, listvariable=self.extension_names, + selectmode='browse') + self.extension_list.bind('<>', self.extension_selected) + scroll = Scrollbar(frame, command=self.extension_list.yview) + self.extension_list.yscrollcommand=scroll.set + self.details_frame = LabelFrame(frame, width=250, height=250) + self.extension_list.grid(column=0, row=0, sticky='nws') + scroll.grid(column=1, row=0, sticky='ns') + self.details_frame.grid(column=2, row=0, sticky='nsew', padx=[10, 0]) + frame.configure(padx=10, pady=10) + self.config_frame = {} + self.current_extension = None + + self.outerframe = self # TEMPORARY + self.tabbed_page_set = self.extension_list # TEMPORARY + + # create the frame holding controls for each extension + ext_names = '' + for ext_name in sorted(self.extensions): + self.create_extension_frame(ext_name) + ext_names = ext_names + '{' + ext_name + '} ' + self.extension_names.set(ext_names) + self.extension_list.selection_set(0) + self.extension_selected(None) + + def load_extensions(self): + "Fill self.extensions with data from the default and user configs." + self.extensions = {} + for ext_name in idleConf.GetExtensions(active_only=False): + self.extensions[ext_name] = [] + + for ext_name in self.extensions: + opt_list = sorted(self.ext_defaultCfg.GetOptionList(ext_name)) + + # bring 'enable' options to the beginning of the list + enables = [opt_name for opt_name in opt_list + if opt_name.startswith('enable')] + for opt_name in enables: + opt_list.remove(opt_name) + opt_list = enables + opt_list + + for opt_name in opt_list: + def_str = self.ext_defaultCfg.Get( + ext_name, opt_name, raw=True) + try: + def_obj = {'True':True, 'False':False}[def_str] + opt_type = 'bool' + except KeyError: + try: + def_obj = int(def_str) + opt_type = 'int' + except ValueError: + def_obj = def_str + opt_type = None + try: + value = self.ext_userCfg.Get( + ext_name, opt_name, type=opt_type, raw=True, + default=def_obj) + except ValueError: # Need this until .Get fixed + value = def_obj # bad values overwritten by entry + var = StringVar(self) + var.set(str(value)) + + self.extensions[ext_name].append({'name': opt_name, + 'type': opt_type, + 'default': def_str, + 'value': value, + 'var': var, + }) + + def extension_selected(self, event): + newsel = self.extension_list.curselection() + if newsel: + newsel = self.extension_list.get(newsel) + if newsel is None or newsel != self.current_extension: + if self.current_extension: + self.details_frame.config(text='') + self.config_frame[self.current_extension].grid_forget() + self.current_extension = None + if newsel: + self.details_frame.config(text=newsel) + self.config_frame[newsel].grid(column=0, row=0, sticky='nsew') + self.current_extension = newsel + + def create_extension_frame(self, ext_name): + """Create a frame holding the widgets to configure one extension""" + f = VerticalScrolledFrame(self.details_frame, height=250, width=250) + self.config_frame[ext_name] = f + entry_area = f.interior + # create an entry for each configuration option + for row, opt in enumerate(self.extensions[ext_name]): + # create a row with a label and entry/checkbutton + label = Label(entry_area, text=opt['name']) + label.grid(row=row, column=0, sticky=NW) + var = opt['var'] + if opt['type'] == 'bool': + Checkbutton(entry_area, textvariable=var, variable=var, + onvalue='True', offvalue='False', + indicatoron=FALSE, selectcolor='', width=8 + ).grid(row=row, column=1, sticky=W, padx=7) + elif opt['type'] == 'int': + Entry(entry_area, textvariable=var, validate='key', + validatecommand=(self.is_int, '%P') + ).grid(row=row, column=1, sticky=NSEW, padx=7) + + else: + Entry(entry_area, textvariable=var + ).grid(row=row, column=1, sticky=NSEW, padx=7) + return + + def set_extension_value(self, section, opt): + name = opt['name'] + default = opt['default'] + value = opt['var'].get().strip() or default + opt['var'].set(value) + # if self.defaultCfg.has_section(section): + # Currently, always true; if not, indent to return + if (value == default): + return self.ext_userCfg.RemoveOption(section, name) + # set the option + return self.ext_userCfg.SetOption(section, name, value) + + def save_all_changed_extensions(self): + """Save configuration changes to the user config file.""" + has_changes = False + for ext_name in self.extensions: + options = self.extensions[ext_name] + for opt in options: + if self.set_extension_value(ext_name, opt): + has_changes = True + if has_changes: + self.ext_userCfg.Save() + + +help_common = '''\ +When you click either the Apply or Ok buttons, settings in this +dialog that are different from IDLE's default are saved in +a .idlerc directory in your home directory. Except as noted, +these changes apply to all versions of IDLE installed on this +machine. Some do not take affect until IDLE is restarted. +[Cancel] only cancels changes made since the last save. +''' +help_pages = { + 'Highlighting':''' +Highlighting: +The IDLE Dark color theme is new in October 2015. It can only +be used with older IDLE releases if it is saved as a custom +theme, with a different name. +''' +} + + +def is_int(s): + "Return 's is blank or represents an int'" + if not s: + return True + try: + int(s) + return True + except ValueError: + return False + + +class VerticalScrolledFrame(Frame): + """A pure Tkinter vertically scrollable frame. + + * Use the 'interior' attribute to place widgets inside the scrollable frame + * Construct and pack/place/grid normally + * This frame only allows vertical scrolling + """ + def __init__(self, parent, *args, **kw): + Frame.__init__(self, parent, *args, **kw) + + # create a canvas object and a vertical scrollbar for scrolling it + vscrollbar = Scrollbar(self, orient=VERTICAL) + vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE) + canvas = Canvas(self, bd=0, highlightthickness=0, + yscrollcommand=vscrollbar.set, width=240) + canvas.pack(side=LEFT, fill=BOTH, expand=TRUE) + vscrollbar.config(command=canvas.yview) + + # reset the view + canvas.xview_moveto(0) + canvas.yview_moveto(0) + + # create a frame inside the canvas which will be scrolled with it + self.interior = interior = Frame(canvas) + interior_id = canvas.create_window(0, 0, window=interior, anchor=NW) + + # track changes to the canvas and frame width and sync them, + # also updating the scrollbar + def _configure_interior(event): + # update the scrollbars to match the size of the inner frame + size = (interior.winfo_reqwidth(), interior.winfo_reqheight()) + canvas.config(scrollregion="0 0 %s %s" % size) + interior.bind('', _configure_interior) + + def _configure_canvas(event): + if interior.winfo_reqwidth() != canvas.winfo_width(): + # update the inner frame's width to fill the canvas + canvas.itemconfigure(interior_id, width=canvas.winfo_width()) + canvas.bind('', _configure_canvas) + + return + + +if __name__ == '__main__': + import unittest + unittest.main('idlelib.idle_test.test_configdialog', + verbosity=2, exit=False) + from idlelib.idle_test.htest import run + run(ConfigDialog) -- cgit v1.2.1 From cb3e3ba86a4aaf964aa574377dd38be0505a9d3f Mon Sep 17 00:00:00 2001 From: doko Date: Tue, 14 Jun 2016 08:39:31 +0200 Subject: - Issue #8637: Honor a pager set by the env var MANPAGER (in preference to one set by the env var PAGER). --- Lib/pydoc.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/pydoc.py b/Lib/pydoc.py index 063aa9cf07..de0084c3e5 100644 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -1429,7 +1429,8 @@ def getpager(): return plainpager if not sys.stdin.isatty() or not sys.stdout.isatty(): return plainpager - if 'PAGER' in os.environ: + use_pager = os.environ.get('MANPAGER') or os.environ.get('PAGER') + if use_pager: if sys.platform == 'win32': # pipes completely broken in Windows return lambda text: tempfilepager(plain(text), os.environ['PAGER']) elif os.environ.get('TERM') in ('dumb', 'emacs'): -- cgit v1.2.1 From 008c426d4a560e63e9a5c56491de91c72417c68b Mon Sep 17 00:00:00 2001 From: doko Date: Tue, 14 Jun 2016 08:55:19 +0200 Subject: - Issue #23968: Rename the platform directory from plat-$(MACHDEP) to plat-$(PLATFORM_TRIPLET). Rename the config directory (LIBPL) from config-$(LDVERSION) to config-$(LDVERSION)-$(PLATFORM_TRIPLET). Install the platform specifc _sysconfigdata module into the platform directory and rename it to include the ABIFLAGS. --- Lib/distutils/sysconfig.py | 2 ++ Lib/plat-linux/regen | 31 ++++++++++++++++++++++++++++--- Lib/sysconfig.py | 8 ++++++-- 3 files changed, 36 insertions(+), 5 deletions(-) (limited to 'Lib') diff --git a/Lib/distutils/sysconfig.py b/Lib/distutils/sysconfig.py index f205dcadeb..e9cc4a95d4 100644 --- a/Lib/distutils/sysconfig.py +++ b/Lib/distutils/sysconfig.py @@ -242,6 +242,8 @@ def get_makefile_filename(): return os.path.join(_sys_home or project_base, "Makefile") lib_dir = get_python_lib(plat_specific=0, standard_lib=1) config_file = 'config-{}{}'.format(get_python_version(), build_flags) + if hasattr(sys.implementation, '_multiarch'): + config_file += '-%s' % sys.implementation._multiarch return os.path.join(lib_dir, config_file, 'Makefile') diff --git a/Lib/plat-linux/regen b/Lib/plat-linux/regen index c76950e232..10633cbc9a 100755 --- a/Lib/plat-linux/regen +++ b/Lib/plat-linux/regen @@ -1,8 +1,33 @@ #! /bin/sh case `uname` in -Linux*) ;; +Linux*|GNU*) ;; *) echo Probably not on a Linux system 1>&2 exit 1;; esac -set -v -h2py -i '(u_long)' /usr/include/sys/types.h /usr/include/netinet/in.h /usr/include/dlfcn.h +if [ -z "$CC" ]; then + echo >&2 "$(basename $0): CC is not set" + exit 1 +fi +headers="sys/types.h netinet/in.h dlfcn.h" +incdirs="$(echo $($CC -v -E - < /dev/null 2>&1|awk '/^#include/, /^End of search/' | grep '^ '))" +if [ -z "$incdirs" ]; then + incdirs="/usr/include" +fi +for h in $headers; do + absh= + for d in $incdirs; do + if [ -f "$d/$h" ]; then + absh="$d/$h" + break + fi + done + if [ -n "$absh" ]; then + absheaders="$absheaders $absh" + else + echo >&2 "$(basename $0): header $h not found" + exit 1 + fi +done + +set -x +${H2PY:-h2py} -i '(u_long)' $absheaders diff --git a/Lib/sysconfig.py b/Lib/sysconfig.py index f18b1bc958..ef530617a3 100644 --- a/Lib/sysconfig.py +++ b/Lib/sysconfig.py @@ -337,6 +337,8 @@ def get_makefile_filename(): config_dir_name = 'config-%s%s' % (_PY_VERSION_SHORT, sys.abiflags) else: config_dir_name = 'config' + if hasattr(sys.implementation, '_multiarch'): + config_dir_name += '-%s' % sys.implementation._multiarch return os.path.join(get_path('stdlib'), config_dir_name, 'Makefile') def _generate_posix_vars(): @@ -379,7 +381,7 @@ def _generate_posix_vars(): # _sysconfigdata module manually and populate it with the build vars. # This is more than sufficient for ensuring the subsequent call to # get_platform() succeeds. - name = '_sysconfigdata' + name = '_sysconfigdata_' + sys.abiflags if 'darwin' in sys.platform: import types module = types.ModuleType(name) @@ -405,7 +407,9 @@ def _generate_posix_vars(): def _init_posix(vars): """Initialize the module as appropriate for POSIX systems.""" # _sysconfigdata is generated at build time, see _generate_posix_vars() - from _sysconfigdata import build_time_vars + name = '_sysconfigdata_' + sys.abiflags + _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0) + build_time_vars = _temp.build_time_vars vars.update(build_time_vars) def _init_non_posix(vars): -- cgit v1.2.1 From cca66dc999934b30748111bc26cee19af1ea02cc Mon Sep 17 00:00:00 2001 From: doko Date: Tue, 14 Jun 2016 09:03:52 +0200 Subject: - Issue #8637: Honor a pager set by the env var MANPAGER (in preference to one set by the env var PAGER). --- Lib/pydoc.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Lib') diff --git a/Lib/pydoc.py b/Lib/pydoc.py index de0084c3e5..d7a177f1a2 100644 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -1432,11 +1432,11 @@ def getpager(): use_pager = os.environ.get('MANPAGER') or os.environ.get('PAGER') if use_pager: if sys.platform == 'win32': # pipes completely broken in Windows - return lambda text: tempfilepager(plain(text), os.environ['PAGER']) + return lambda text: tempfilepager(plain(text), use_pager) elif os.environ.get('TERM') in ('dumb', 'emacs'): - return lambda text: pipepager(plain(text), os.environ['PAGER']) + return lambda text: pipepager(plain(text), use_pager) else: - return lambda text: pipepager(text, os.environ['PAGER']) + return lambda text: pipepager(text, use_pager) if os.environ.get('TERM') in ('dumb', 'emacs'): return plainpager if sys.platform == 'win32': -- cgit v1.2.1 From 9591cc1bd328a80dc1fe6a2f2691d2b6f24451e3 Mon Sep 17 00:00:00 2001 From: doko Date: Tue, 14 Jun 2016 09:22:16 +0200 Subject: - Issue #23968: Update distutils/sysconfig.py to look for the renamed _sysconfigdata module too. --- Lib/distutils/sysconfig.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/distutils/sysconfig.py b/Lib/distutils/sysconfig.py index e9cc4a95d4..f72b7f5a19 100644 --- a/Lib/distutils/sysconfig.py +++ b/Lib/distutils/sysconfig.py @@ -418,7 +418,9 @@ _config_vars = None def _init_posix(): """Initialize the module as appropriate for POSIX systems.""" # _sysconfigdata is generated at build time, see the sysconfig module - from _sysconfigdata import build_time_vars + name = '_sysconfigdata_' + sys.abiflags + _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0) + build_time_vars = _temp.build_time_vars global _config_vars _config_vars = {} _config_vars.update(build_time_vars) -- cgit v1.2.1 From 23644a94ef72f52069c54ffa65a39267880b1e5e Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Tue, 14 Jun 2016 15:25:36 +0300 Subject: Issue #16864: Cursor.lastrowid now supports REPLACE statement Initial patch by Alex LordThorsen. --- Lib/sqlite3/test/dbapi.py | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/sqlite3/test/dbapi.py b/Lib/sqlite3/test/dbapi.py index ec42eb7920..51c4d539bd 100644 --- a/Lib/sqlite3/test/dbapi.py +++ b/Lib/sqlite3/test/dbapi.py @@ -188,7 +188,10 @@ class CursorTests(unittest.TestCase): def setUp(self): self.cx = sqlite.connect(":memory:") self.cu = self.cx.cursor() - self.cu.execute("create table test(id integer primary key, name text, income number)") + self.cu.execute( + "create table test(id integer primary key, name text, " + "income number, unique_test text unique)" + ) self.cu.execute("insert into test(name) values (?)", ("foo",)) def tearDown(self): @@ -462,6 +465,44 @@ class CursorTests(unittest.TestCase): with self.assertRaises(TypeError): cur = sqlite.Cursor(foo) + def CheckLastRowIDOnReplace(self): + """ + INSERT OR REPLACE and REPLACE INTO should produce the same behavior. + """ + sql = '{} INTO test(id, unique_test) VALUES (?, ?)' + for statement in ('INSERT OR REPLACE', 'REPLACE'): + with self.subTest(statement=statement): + self.cu.execute(sql.format(statement), (1, 'foo')) + self.assertEqual(self.cu.lastrowid, 1) + + def CheckLastRowIDOnIgnore(self): + self.cu.execute( + "insert or ignore into test(unique_test) values (?)", + ('test',)) + self.assertEqual(self.cu.lastrowid, 2) + self.cu.execute( + "insert or ignore into test(unique_test) values (?)", + ('test',)) + self.assertEqual(self.cu.lastrowid, 2) + + def CheckLastRowIDInsertOR(self): + results = [] + for statement in ('FAIL', 'ABORT', 'ROLLBACK'): + sql = 'INSERT OR {} INTO test(unique_test) VALUES (?)' + with self.subTest(statement='INSERT OR {}'.format(statement)): + self.cu.execute(sql.format(statement), (statement,)) + results.append((statement, self.cu.lastrowid)) + with self.assertRaises(sqlite.IntegrityError): + self.cu.execute(sql.format(statement), (statement,)) + results.append((statement, self.cu.lastrowid)) + expected = [ + ('FAIL', 2), ('FAIL', 2), + ('ABORT', 3), ('ABORT', 3), + ('ROLLBACK', 4), ('ROLLBACK', 4), + ] + self.assertEqual(results, expected) + + @unittest.skipUnless(threading, 'This test requires threading.') class ThreadTests(unittest.TestCase): def setUp(self): -- cgit v1.2.1 From 4d7898f73a43a65f4c13bd306cb4c1076a5f90ca Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 14 Jun 2016 16:42:59 +0200 Subject: subprocess: enhance ResourceWarning message * Add the process identifier to the warning message * Add also a comment to explain the issue --- Lib/subprocess.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'Lib') diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 98f339ee5c..3dea089e6a 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -993,7 +993,6 @@ class Popen(object): raise - def _translate_newlines(self, data, encoding): data = data.decode(encoding) return data.replace("\r\n", "\n").replace("\r", "\n") @@ -1018,8 +1017,10 @@ class Popen(object): # We didn't get to successfully create a child process. return if self.returncode is None: - warnings.warn("running subprocess %r" % self, ResourceWarning, - source=self) + # Not reading subprocess exit status creates a zombi process which + # is only destroyed at the parent python process exit + warnings.warn("subprocess %s is still running" % self.pid, + ResourceWarning, source=self) # In case the child hasn't been waited on, check if it's done. self._internal_poll(_deadstate=_maxsize) if self.returncode is None and _active is not None: -- cgit v1.2.1 From 7cf8527b1cd148771bd4ac2581a429db0bb95b81 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Fri, 17 Jun 2016 12:52:18 -0700 Subject: Issue #26536: socket.ioctl now supports SIO_LOOPBACK_FAST_PATH. Patch by Daniel Stokes. --- Lib/test/test_socket.py | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 1ddd6044b9..fa318b3aae 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1217,6 +1217,16 @@ class GeneralModuleTests(unittest.TestCase): self.assertRaises(ValueError, s.ioctl, -1, None) s.ioctl(socket.SIO_KEEPALIVE_VALS, (1, 100, 100)) + @unittest.skipUnless(os.name == "nt", "Windows specific") + @unittest.skipUnless(hasattr(socket, 'SIO_LOOPBACK_FAST_PATH'), + 'Loopback fast path support required for this test') + def test_sio_loopback_fast_path(self): + s = socket.socket() + self.addCleanup(s.close) + s.ioctl(socket.SIO_LOOPBACK_FAST_PATH, True) + self.assertRaises(TypeError, s.ioctl, socket.SIO_LOOPBACK_FAST_PATH, None) + + def testGetaddrinfo(self): try: socket.getaddrinfo('localhost', 80) -- cgit v1.2.1 From 70ca4617289f1ebba7a74e60d2ca89a07a17e742 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 17 Jun 2016 19:55:46 -0400 Subject: Issue #27312: mock out function that fails when called from setupApp during IDLE test_macosx and see if addOpenEventSupport() fails. --- Lib/idlelib/idle_test/test_macosx.py | 1 + 1 file changed, 1 insertion(+) (limited to 'Lib') diff --git a/Lib/idlelib/idle_test/test_macosx.py b/Lib/idlelib/idle_test/test_macosx.py index d7f8f5db27..189dc486ca 100644 --- a/Lib/idlelib/idle_test/test_macosx.py +++ b/Lib/idlelib/idle_test/test_macosx.py @@ -83,6 +83,7 @@ class SetupTest(unittest.TestCase): cls.root.destroy() del cls.root + @mock.patch('idlelib.macosx.overrideRootMenu') #27312 def test_setupapp(self): "Call setupApp with each possible graphics type." root = self.root -- cgit v1.2.1 From 77ad5db4f7a36cb042ee4a19efcf73d3c81ad3ab Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Sat, 18 Jun 2016 04:18:24 +0300 Subject: Issue #27312: Fix TypeError in test_setupapp --- Lib/idlelib/idle_test/test_macosx.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/idlelib/idle_test/test_macosx.py b/Lib/idlelib/idle_test/test_macosx.py index 189dc486ca..3c6161c518 100644 --- a/Lib/idlelib/idle_test/test_macosx.py +++ b/Lib/idlelib/idle_test/test_macosx.py @@ -84,7 +84,7 @@ class SetupTest(unittest.TestCase): del cls.root @mock.patch('idlelib.macosx.overrideRootMenu') #27312 - def test_setupapp(self): + def test_setupapp(self, overrideRootMenu): "Call setupApp with each possible graphics type." root = self.root flist = FileList(root) @@ -92,6 +92,9 @@ class SetupTest(unittest.TestCase): with self.subTest(tktype=tktype): macosx._tk_type = tktype macosx.setupApp(root, flist) + if tktype in ('carbon', 'cocoa'): + self.assertTrue(overrideRootMenu.called) + overrideRootMenu.reset_mock() if __name__ == '__main__': -- cgit v1.2.1 From c2faca749811c7a4ff11ccb38ca3c3c89878a584 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Sat, 18 Jun 2016 16:10:07 +0300 Subject: Issue #26536: Skip test_sio_loopback_fast_path under Windows 7 --- Lib/test/test_socket.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index fa318b3aae..faacd61fc1 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1223,10 +1223,16 @@ class GeneralModuleTests(unittest.TestCase): def test_sio_loopback_fast_path(self): s = socket.socket() self.addCleanup(s.close) - s.ioctl(socket.SIO_LOOPBACK_FAST_PATH, True) + try: + s.ioctl(socket.SIO_LOOPBACK_FAST_PATH, True) + except OSError as exc: + WSAEOPNOTSUPP = 10045 + if exc.winerror == WSAEOPNOTSUPP: + self.skipTest("SIO_LOOPBACK_FAST_PATH is defined but " + "doesn't implemented in this Windows version") + raise self.assertRaises(TypeError, s.ioctl, socket.SIO_LOOPBACK_FAST_PATH, None) - def testGetaddrinfo(self): try: socket.getaddrinfo('localhost', 80) -- cgit v1.2.1 From cf81c5ce3d5b91f342c65bdce7b8df7d47b7ab3e Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 18 Jun 2016 16:48:07 +0300 Subject: Issue #27177: Match objects in the re module now support index-like objects as group indices. Based on patches by Jeroen Demeyer and Xiang Zhang. --- Lib/test/test_re.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index e27591c4fc..24a0604948 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -414,19 +414,33 @@ class ReTests(unittest.TestCase): self.assertEqual(pat.match('bc').groups(), ('b', None, 'b', 'c')) self.assertEqual(pat.match('bc').groups(""), ('b', "", 'b', 'c')) - # A single group - m = re.match('(a)', 'a') - self.assertEqual(m.group(0), 'a') - self.assertEqual(m.group(0), 'a') - self.assertEqual(m.group(1), 'a') - self.assertEqual(m.group(1, 1), ('a', 'a')) - pat = re.compile('(?:(?Pa)|(?Pb))(?Pc)?') self.assertEqual(pat.match('a').group(1, 2, 3), ('a', None, None)) self.assertEqual(pat.match('b').group('a1', 'b2', 'c3'), (None, 'b', None)) self.assertEqual(pat.match('ac').group(1, 'b2', 3), ('a', None, 'c')) + def test_group(self): + class Index: + def __init__(self, value): + self.value = value + def __index__(self): + return self.value + # A single group + m = re.match('(a)(b)', 'ab') + self.assertEqual(m.group(), 'ab') + self.assertEqual(m.group(0), 'ab') + self.assertEqual(m.group(1), 'a') + self.assertEqual(m.group(Index(1)), 'a') + self.assertRaises(IndexError, m.group, -1) + self.assertRaises(IndexError, m.group, 3) + self.assertRaises(IndexError, m.group, 1<<1000) + self.assertRaises(IndexError, m.group, Index(1<<1000)) + self.assertRaises(IndexError, m.group, 'x') + # Multiple groups + self.assertEqual(m.group(2, 1), ('b', 'a')) + self.assertEqual(m.group(Index(2), Index(1)), ('b', 'a')) + def test_re_fullmatch(self): # Issue 16203: Proposal: add re.fullmatch() method. self.assertEqual(re.fullmatch(r"a", "a").span(), (0, 1)) -- cgit v1.2.1 From 9eb6368f657be36394df6ca6a81dd17730d4e6b1 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 18 Jun 2016 21:55:26 +0300 Subject: Issue #27294: Numerical state in the repr for Tkinter event objects is now represented as a compination of known flags. --- Lib/tkinter/__init__.py | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) (limited to 'Lib') diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index c687da580c..aaa7d14a87 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -220,28 +220,41 @@ class Event: delta - delta of wheel movement (MouseWheel) """ def __repr__(self): - state = {k: v for k, v in self.__dict__.items() if v != '??'} + attrs = {k: v for k, v in self.__dict__.items() if v != '??'} if not self.char: - del state['char'] + del attrs['char'] elif self.char != '??': - state['char'] = repr(self.char) + attrs['char'] = repr(self.char) if not getattr(self, 'send_event', True): - del state['send_event'] + del attrs['send_event'] if self.state == 0: - del state['state'] + del attrs['state'] + elif isinstance(self.state, int): + state = self.state + mods = ('Shift', 'Lock', 'Control', + 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5', + 'Button1', 'Button2', 'Button3', 'Button4', 'Button5') + s = [] + for i, n in enumerate(mods): + if state & (1 << i): + s.append(n) + state = state & ~((1<< len(mods)) - 1) + if state or not s: + s.append(hex(state)) + attrs['state'] = '|'.join(s) if self.delta == 0: - del state['delta'] + del attrs['delta'] # widget usually is known # serial and time are not very interesing # keysym_num duplicates keysym # x_root and y_root mostly duplicate x and y keys = ('send_event', - 'state', 'keycode', 'char', 'keysym', + 'state', 'keysym', 'keycode', 'char', 'num', 'delta', 'focus', 'x', 'y', 'width', 'height') return '<%s event%s>' % ( self.type, - ''.join(' %s=%s' % (k, state[k]) for k in keys if k in state) + ''.join(' %s=%s' % (k, attrs[k]) for k in keys if k in attrs) ) _support_default_root = 1 -- cgit v1.2.1 From 7d742d8ed628b0d5fd74e1b902b59b72da14364f Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 20 Jun 2016 00:05:40 +0300 Subject: Issue #27319: Methods selection_set(), selection_add(), selection_remove() and selection_toggle() of ttk.TreeView now allow to pass multiple items as multiple arguments instead of passing them as a tuple. Deprecated undocumented ability of calling the selection() method with arguments. --- Lib/tkinter/test/test_ttk/test_widgets.py | 49 ++++++++++++++++++++++++--- Lib/tkinter/ttk.py | 56 ++++++++++++++++++++++--------- 2 files changed, 85 insertions(+), 20 deletions(-) (limited to 'Lib') diff --git a/Lib/tkinter/test/test_ttk/test_widgets.py b/Lib/tkinter/test/test_ttk/test_widgets.py index 8bd22d03e5..26766a8a1f 100644 --- a/Lib/tkinter/test/test_ttk/test_widgets.py +++ b/Lib/tkinter/test/test_ttk/test_widgets.py @@ -1487,6 +1487,7 @@ class TreeviewTest(AbstractWidgetTest, unittest.TestCase): def test_selection(self): + self.assertRaises(TypeError, self.tv.selection, 'spam') # item 'none' doesn't exist self.assertRaises(tkinter.TclError, self.tv.selection_set, 'none') self.assertRaises(tkinter.TclError, self.tv.selection_add, 'none') @@ -1500,25 +1501,31 @@ class TreeviewTest(AbstractWidgetTest, unittest.TestCase): c3 = self.tv.insert(item1, 'end') self.assertEqual(self.tv.selection(), ()) - self.tv.selection_set((c1, item2)) + self.tv.selection_set(c1, item2) self.assertEqual(self.tv.selection(), (c1, item2)) self.tv.selection_set(c2) self.assertEqual(self.tv.selection(), (c2,)) - self.tv.selection_add((c1, item2)) + self.tv.selection_add(c1, item2) self.assertEqual(self.tv.selection(), (c1, c2, item2)) self.tv.selection_add(item1) self.assertEqual(self.tv.selection(), (item1, c1, c2, item2)) + self.tv.selection_add() + self.assertEqual(self.tv.selection(), (item1, c1, c2, item2)) - self.tv.selection_remove((item1, c3)) + self.tv.selection_remove(item1, c3) self.assertEqual(self.tv.selection(), (c1, c2, item2)) self.tv.selection_remove(c2) self.assertEqual(self.tv.selection(), (c1, item2)) + self.tv.selection_remove() + self.assertEqual(self.tv.selection(), (c1, item2)) - self.tv.selection_toggle((c1, c3)) + self.tv.selection_toggle(c1, c3) self.assertEqual(self.tv.selection(), (c3, item2)) self.tv.selection_toggle(item2) self.assertEqual(self.tv.selection(), (c3,)) + self.tv.selection_toggle() + self.assertEqual(self.tv.selection(), (c3,)) self.tv.insert('', 'end', id='with spaces') self.tv.selection_set('with spaces') @@ -1536,6 +1543,40 @@ class TreeviewTest(AbstractWidgetTest, unittest.TestCase): self.tv.selection_set(b'bytes\xe2\x82\xac') self.assertEqual(self.tv.selection(), ('bytes\xe2\x82\xac',)) + self.tv.selection_set() + self.assertEqual(self.tv.selection(), ()) + + # Old interface + self.tv.selection_set((c1, item2)) + self.assertEqual(self.tv.selection(), (c1, item2)) + self.tv.selection_add((c1, item1)) + self.assertEqual(self.tv.selection(), (item1, c1, item2)) + self.tv.selection_remove((item1, c3)) + self.assertEqual(self.tv.selection(), (c1, item2)) + self.tv.selection_toggle((c1, c3)) + self.assertEqual(self.tv.selection(), (c3, item2)) + + if sys.version_info >= (3, 7): + import warnings + warnings.warn( + 'Deprecated API of Treeview.selection() should be removed') + self.tv.selection_set() + self.assertEqual(self.tv.selection(), ()) + with self.assertWarns(DeprecationWarning): + self.tv.selection('set', (c1, item2)) + self.assertEqual(self.tv.selection(), (c1, item2)) + with self.assertWarns(DeprecationWarning): + self.tv.selection('add', (c1, item1)) + self.assertEqual(self.tv.selection(), (item1, c1, item2)) + with self.assertWarns(DeprecationWarning): + self.tv.selection('remove', (item1, c3)) + self.assertEqual(self.tv.selection(), (c1, item2)) + with self.assertWarns(DeprecationWarning): + self.tv.selection('toggle', (c1, c3)) + self.assertEqual(self.tv.selection(), (c3, item2)) + with self.assertWarns(DeprecationWarning): + selection = self.tv.selection(None) + self.assertEqual(selection, (c3, item2)) def test_set(self): self.tv['columns'] = ['A', 'B'] diff --git a/Lib/tkinter/ttk.py b/Lib/tkinter/ttk.py index 7b71e77ad8..71ac2a7d10 100644 --- a/Lib/tkinter/ttk.py +++ b/Lib/tkinter/ttk.py @@ -28,6 +28,8 @@ __all__ = ["Button", "Checkbutton", "Combobox", "Entry", "Frame", "Label", import tkinter from tkinter import _flatten, _join, _stringify, _splitdict +_sentinel = object() + # Verify if Tk is new enough to not need the Tile package _REQUIRE_TILE = True if tkinter.TkVersion < 8.5 else False @@ -1394,31 +1396,53 @@ class Treeview(Widget, tkinter.XView, tkinter.YView): self.tk.call(self._w, "see", item) - def selection(self, selop=None, items=None): - """If selop is not specified, returns selected items.""" - if isinstance(items, (str, bytes)): - items = (items,) + def selection(self, selop=_sentinel, items=None): + """Returns the tuple of selected items.""" + if selop is _sentinel: + selop = None + elif selop is None: + import warnings + warnings.warn( + "The selop=None argument of selection() is deprecated " + "and will be removed in Python 3.7", + DeprecationWarning, 3) + elif selop in ('set', 'add', 'remove', 'toggle'): + import warnings + warnings.warn( + "The selop argument of selection() is deprecated " + "and will be removed in Python 3.7, " + "use selection_%s() instead" % (selop,), + DeprecationWarning, 3) + else: + raise TypeError('Unsupported operation') return self.tk.splitlist(self.tk.call(self._w, "selection", selop, items)) - def selection_set(self, items): - """items becomes the new selection.""" - self.selection("set", items) + def _selection(self, selop, items): + if len(items) == 1 and isinstance(items[0], (tuple, list)): + items = items[0] + + self.tk.call(self._w, "selection", selop, items) + + + def selection_set(self, *items): + """The specified items becomes the new selection.""" + self._selection("set", items) - def selection_add(self, items): - """Add items to the selection.""" - self.selection("add", items) + def selection_add(self, *items): + """Add all of the specified items to the selection.""" + self._selection("add", items) - def selection_remove(self, items): - """Remove items from the selection.""" - self.selection("remove", items) + def selection_remove(self, *items): + """Remove all of the specified items from the selection.""" + self._selection("remove", items) - def selection_toggle(self, items): - """Toggle the selection state of each item in items.""" - self.selection("toggle", items) + def selection_toggle(self, *items): + """Toggle the selection state of each specified item.""" + self._selection("toggle", items) def set(self, item, column=None, value=None): -- cgit v1.2.1 From 09199610864aaaea6bdc86ed13106111b48b27f4 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 22 Jun 2016 00:03:20 +0300 Subject: Issue #18726: All optional parameters of the dump(), dumps(), load() and loads() functions and JSONEncoder and JSONDecoder class constructors in the json module are now keyword-only. --- Lib/json/__init__.py | 8 ++++---- Lib/json/decoder.py | 2 +- Lib/json/encoder.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'Lib') diff --git a/Lib/json/__init__.py b/Lib/json/__init__.py index 2612657faf..28057dd187 100644 --- a/Lib/json/__init__.py +++ b/Lib/json/__init__.py @@ -116,7 +116,7 @@ _default_encoder = JSONEncoder( default=None, ) -def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, +def dump(obj, fp, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw): """Serialize ``obj`` as a JSON formatted stream to ``fp`` (a @@ -179,7 +179,7 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, fp.write(chunk) -def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, +def dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw): """Serialize ``obj`` to a JSON formatted ``str``. @@ -240,7 +240,7 @@ def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, _default_decoder = JSONDecoder(object_hook=None, object_pairs_hook=None) -def load(fp, cls=None, object_hook=None, parse_float=None, +def load(fp, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw): """Deserialize ``fp`` (a ``.read()``-supporting file-like object containing a JSON document) to a Python object. @@ -268,7 +268,7 @@ def load(fp, cls=None, object_hook=None, parse_float=None, parse_constant=parse_constant, object_pairs_hook=object_pairs_hook, **kw) -def loads(s, encoding=None, cls=None, object_hook=None, parse_float=None, +def loads(s, *, encoding=None, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw): """Deserialize ``s`` (a ``str`` instance containing a JSON document) to a Python object. diff --git a/Lib/json/decoder.py b/Lib/json/decoder.py index 0f03f20042..2422c6ac10 100644 --- a/Lib/json/decoder.py +++ b/Lib/json/decoder.py @@ -280,7 +280,7 @@ class JSONDecoder(object): """ - def __init__(self, object_hook=None, parse_float=None, + def __init__(self, *, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, strict=True, object_pairs_hook=None): """``object_hook``, if specified, will be called with the result diff --git a/Lib/json/encoder.py b/Lib/json/encoder.py index 0772bbc06b..41a497c5da 100644 --- a/Lib/json/encoder.py +++ b/Lib/json/encoder.py @@ -101,7 +101,7 @@ class JSONEncoder(object): """ item_separator = ', ' key_separator = ': ' - def __init__(self, skipkeys=False, ensure_ascii=True, + def __init__(self, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False, indent=None, separators=None, default=None): """Constructor for JSONEncoder, with sensible defaults. -- cgit v1.2.1 From f5ad79173d10bf7e0a86bc139cd03b5c2d7d6ce9 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Tue, 21 Jun 2016 18:41:38 -0400 Subject: Issue #24137: Run IDLE, test_idle, and htest with tkinter default root disabled. Fix code and tests that fail with this restriction. Fix htests to not create a second and redundant root and mainloop. --- Lib/idlelib/debugobj.py | 17 ++++++++--------- Lib/idlelib/dynoption.py | 4 ++-- Lib/idlelib/grep.py | 15 +++++++-------- Lib/idlelib/idle_test/htest.py | 3 ++- Lib/idlelib/idle_test/test_searchbase.py | 10 ++++++---- Lib/idlelib/idle_test/test_text.py | 21 +++++++++++++++------ Lib/idlelib/multicall.py | 11 +++++------ Lib/idlelib/percolator.py | 4 ++-- Lib/idlelib/pyshell.py | 5 ++++- Lib/idlelib/redirector.py | 13 ++++++------- Lib/idlelib/stackviewer.py | 10 +++++----- Lib/idlelib/statusbar.py | 28 +++++++++++++--------------- Lib/idlelib/tabbedpages.py | 22 ++++++++++------------ Lib/idlelib/undo.py | 12 ++++++------ Lib/test/test_idle.py | 1 + 15 files changed, 92 insertions(+), 84 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/debugobj.py b/Lib/idlelib/debugobj.py index 4016c032d4..0d8b2b2c7d 100644 --- a/Lib/idlelib/debugobj.py +++ b/Lib/idlelib/debugobj.py @@ -122,21 +122,20 @@ def make_objecttreeitem(labeltext, object, setfunction=None): return c(labeltext, object, setfunction) -def _object_browser(parent): +def _object_browser(parent): # htest # import sys - from tkinter import Tk - root = Tk() - root.title("Test debug object browser") + from tkinter import Toplevel + top = Toplevel(parent) + top.title("Test debug object browser") width, height, x, y = list(map(int, re.split('[x+]', parent.geometry()))) - root.geometry("+%d+%d"%(x, y + 150)) - root.configure(bd=0, bg="yellow") - root.focus_set() - sc = ScrolledCanvas(root, bg="white", highlightthickness=0, takefocus=1) + top.geometry("+%d+%d"%(x + 100, y + 175)) + top.configure(bd=0, bg="yellow") + top.focus_set() + sc = ScrolledCanvas(top, bg="white", highlightthickness=0, takefocus=1) sc.frame.pack(expand=1, fill="both") item = make_objecttreeitem("sys", sys) node = TreeNode(sc.canvas, None, item) node.update() - root.mainloop() if __name__ == '__main__': from idlelib.idle_test.htest import run diff --git a/Lib/idlelib/dynoption.py b/Lib/idlelib/dynoption.py index 515b4bafc2..922ad5e4af 100644 --- a/Lib/idlelib/dynoption.py +++ b/Lib/idlelib/dynoption.py @@ -34,9 +34,9 @@ class DynOptionMenu(OptionMenu): self.variable.set(value) def _dyn_option_menu(parent): # htest # - from tkinter import Toplevel + from tkinter import Toplevel # + StringVar, Button - top = Toplevel() + top = Toplevel(parent) top.title("Tets dynamic option menu") top.geometry("200x100+%d+%d" % (parent.winfo_rootx() + 200, parent.winfo_rooty() + 150)) diff --git a/Lib/idlelib/grep.py b/Lib/idlelib/grep.py index 28132a8917..6324b4f112 100644 --- a/Lib/idlelib/grep.py +++ b/Lib/idlelib/grep.py @@ -3,7 +3,6 @@ import fnmatch import re # for htest import sys from tkinter import StringVar, BooleanVar, Checkbutton # for GrepDialog -from tkinter import Tk, Text, Button, SEL, END # for htest from idlelib import searchengine from idlelib.searchbase import SearchDialogBase # Importing OutputWindow fails due to import loop @@ -132,13 +131,14 @@ class GrepDialog(SearchDialogBase): def _grep_dialog(parent): # htest # from idlelib.pyshell import PyShellFileList - root = Tk() - root.title("Test GrepDialog") + from tkinter import Toplevel, Text, Button, SEL, END + top = Toplevel(parent) + top.title("Test GrepDialog") width, height, x, y = list(map(int, re.split('[x+]', parent.geometry()))) - root.geometry("+%d+%d"%(x, y + 150)) + top.geometry("+%d+%d"%(x, y + 150)) - flist = PyShellFileList(root) - text = Text(root, height=5) + flist = PyShellFileList(top) + text = Text(top, height=5) text.pack() def show_grep_dialog(): @@ -146,9 +146,8 @@ def _grep_dialog(parent): # htest # grep(text, flist=flist) text.tag_remove(SEL, "1.0", END) - button = Button(root, text="Show GrepDialog", command=show_grep_dialog) + button = Button(top, text="Show GrepDialog", command=show_grep_dialog) button.pack() - root.mainloop() if __name__ == "__main__": import unittest diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py index d0177bb5ad..701f4d9fe6 100644 --- a/Lib/idlelib/idle_test/htest.py +++ b/Lib/idlelib/idle_test/htest.py @@ -68,6 +68,7 @@ outwin.OutputWindow (indirectly being tested with grep test) from importlib import import_module import tkinter as tk from tkinter.ttk import Scrollbar +tk.NoDefaultRoot() AboutDialog_spec = { 'file': 'help_about', @@ -364,7 +365,7 @@ def run(*tests): test = getattr(mod, test_name) test_list.append((test_spec, test)) - test_name = tk.StringVar('') + test_name = tk.StringVar(root) callable_object = None test_kwds = None diff --git a/Lib/idlelib/idle_test/test_searchbase.py b/Lib/idlelib/idle_test/test_searchbase.py index 5ff9476c87..a0b1231ecd 100644 --- a/Lib/idlelib/idle_test/test_searchbase.py +++ b/Lib/idlelib/idle_test/test_searchbase.py @@ -74,7 +74,7 @@ class SearchDialogBaseTest(unittest.TestCase): def test_make_entry(self): equal = self.assertEqual self.dialog.row = 0 - self.dialog.top = Toplevel(self.root) + self.dialog.top = self.root entry, label = self.dialog.make_entry("Test:", 'hello') equal(label['text'], 'Test:') @@ -87,6 +87,7 @@ class SearchDialogBaseTest(unittest.TestCase): equal(self.dialog.row, 1) def test_create_entries(self): + self.dialog.top = self.root self.dialog.row = 0 self.engine.setpat('hello') self.dialog.create_entries() @@ -94,7 +95,7 @@ class SearchDialogBaseTest(unittest.TestCase): def test_make_frame(self): self.dialog.row = 0 - self.dialog.top = Toplevel(self.root) + self.dialog.top = self.root frame, label = self.dialog.make_frame() self.assertEqual(label, '') self.assertIsInstance(frame, Frame) @@ -104,7 +105,7 @@ class SearchDialogBaseTest(unittest.TestCase): self.assertIsInstance(frame, Frame) def btn_test_setup(self, meth): - self.dialog.top = Toplevel(self.root) + self.dialog.top = self.root self.dialog.row = 0 return meth() @@ -145,12 +146,13 @@ class SearchDialogBaseTest(unittest.TestCase): self.assertEqual(var.get(), state) def test_make_button(self): - self.dialog.top = Toplevel(self.root) + self.dialog.top = self.root self.dialog.buttonframe = Frame(self.dialog.top) btn = self.dialog.make_button('Test', self.dialog.close) self.assertEqual(btn['text'], 'Test') def test_create_command_buttons(self): + self.dialog.top = self.root self.dialog.create_command_buttons() # Look for close button command in buttonframe closebuttoncommand = '' diff --git a/Lib/idlelib/idle_test/test_text.py b/Lib/idlelib/idle_test/test_text.py index 7e823df3db..a5ba7bb213 100644 --- a/Lib/idlelib/idle_test/test_text.py +++ b/Lib/idlelib/idle_test/test_text.py @@ -1,17 +1,19 @@ -# Test mock_tk.Text class against tkinter.Text class by running same tests with both. +''' Test mock_tk.Text class against tkinter.Text class + +Run same tests with both by creating a mixin class. +''' import unittest from test.support import requires - from _tkinter import TclError class TextTest(object): + "Define items common to both sets of tests." - hw = 'hello\nworld' # usual initial insert after initialization + hw = 'hello\nworld' # Several tests insert this after after initialization. hwn = hw+'\n' # \n present at initialization, before insert - Text = None - def setUp(self): - self.text = self.Text() + # setUpClass defines cls.Text and maybe cls.root. + # setUp defines self.text from Text and maybe root. def test_init(self): self.assertEqual(self.text.get('1.0'), '\n') @@ -196,6 +198,10 @@ class MockTextTest(TextTest, unittest.TestCase): from idlelib.idle_test.mock_tk import Text cls.Text = Text + def setUp(self): + self.text = self.Text() + + def test_decode(self): # test endflags (-1, 0) not tested by test_index (which uses +1) decode = self.text._decode @@ -222,6 +228,9 @@ class TkTextTest(TextTest, unittest.TestCase): cls.root.destroy() del cls.root + def setUp(self): + self.text = self.Text(self.root) + if __name__ == '__main__': unittest.main(verbosity=2, exit=False) diff --git a/Lib/idlelib/multicall.py b/Lib/idlelib/multicall.py index 8462854921..bf02f597f3 100644 --- a/Lib/idlelib/multicall.py +++ b/Lib/idlelib/multicall.py @@ -414,12 +414,12 @@ def MultiCallCreator(widget): return MultiCall -def _multi_call(parent): - root = tkinter.Tk() - root.title("Test MultiCall") +def _multi_call(parent): # htest # + top = tkinter.Toplevel(parent) + top.title("Test MultiCall") width, height, x, y = list(map(int, re.split('[x+]', parent.geometry()))) - root.geometry("+%d+%d"%(x, y + 150)) - text = MultiCallCreator(tkinter.Text)(root) + top.geometry("+%d+%d"%(x, y + 150)) + text = MultiCallCreator(tkinter.Text)(top) text.pack() def bindseq(seq, n=[0]): def handler(event): @@ -439,7 +439,6 @@ def _multi_call(parent): bindseq("") bindseq("") bindseq("") - root.mainloop() if __name__ == "__main__": from idlelib.idle_test.htest import run diff --git a/Lib/idlelib/percolator.py b/Lib/idlelib/percolator.py index 227144581b..2111e036e7 100644 --- a/Lib/idlelib/percolator.py +++ b/Lib/idlelib/percolator.py @@ -89,10 +89,10 @@ def _percolator(parent): # htest # (pin if var2.get() else pout)(t2) text.pack() - var1 = tk.IntVar() + var1 = tk.IntVar(parent) cb1 = tk.Checkbutton(box, text="Tracer1", command=toggle1, variable=var1) cb1.pack() - var2 = tk.IntVar() + var2 = tk.IntVar(parent) cb2 = tk.Checkbutton(box, text="Tracer2", command=toggle2, variable=var2) cb2.pack() diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py index 0f7a01d77b..3e8351fdf3 100755 --- a/Lib/idlelib/pyshell.py +++ b/Lib/idlelib/pyshell.py @@ -1547,7 +1547,9 @@ def main(): enable_edit = enable_edit or edit_start enable_shell = enable_shell or not enable_edit - # start editor and/or shell windows: + # Setup root. + if use_subprocess: # Don't break user code run in IDLE process + NoDefaultRoot() root = Tk(className="Idle") root.withdraw() @@ -1563,6 +1565,7 @@ def main(): icons = [PhotoImage(file=iconfile) for iconfile in iconfiles] root.wm_iconphoto(True, *icons) + # start editor and/or shell windows: fixwordbreaks(root) fix_x11_paste(root) flist = PyShellFileList(root) diff --git a/Lib/idlelib/redirector.py b/Lib/idlelib/redirector.py index 5ca7a2d5ff..3a110557d9 100644 --- a/Lib/idlelib/redirector.py +++ b/Lib/idlelib/redirector.py @@ -151,14 +151,14 @@ class OriginalCommand: def _widget_redirector(parent): # htest # - from tkinter import Tk, Text + from tkinter import Toplevel, Text import re - root = Tk() - root.title("Test WidgetRedirector") + top = Toplevel(parent) + top.title("Test WidgetRedirector") width, height, x, y = list(map(int, re.split('[x+]', parent.geometry()))) - root.geometry("+%d+%d"%(x, y + 150)) - text = Text(root) + top.geometry("+%d+%d"%(x, y + 150)) + text = Text(top) text.pack() text.focus_set() redir = WidgetRedirector(text) @@ -166,11 +166,10 @@ def _widget_redirector(parent): # htest # print("insert", args) original_insert(*args) original_insert = redir.register("insert", my_insert) - root.mainloop() if __name__ == "__main__": import unittest - unittest.main('idlelib.idle_test.test_widgetredir', + unittest.main('idlelib.idle_test.test_redirector', verbosity=2, exit=False) from idlelib.idle_test.htest import run run(_widget_redirector) diff --git a/Lib/idlelib/stackviewer.py b/Lib/idlelib/stackviewer.py index 87c964e7af..b3b99bcefd 100644 --- a/Lib/idlelib/stackviewer.py +++ b/Lib/idlelib/stackviewer.py @@ -121,11 +121,11 @@ class VariablesTreeItem(ObjectTreeItem): return sublist def _stack_viewer(parent): - root = tk.Tk() - root.title("Test StackViewer") + top = tk.Toplevel(parent) + top.title("Test StackViewer") width, height, x, y = list(map(int, re.split('[x+]', parent.geometry()))) - root.geometry("+%d+%d"%(x, y + 150)) - flist = PyShellFileList(root) + top.geometry("+%d+%d"%(x, y + 150)) + flist = PyShellFileList(top) try: # to obtain a traceback object intentional_name_error except NameError: @@ -136,7 +136,7 @@ def _stack_viewer(parent): sys.last_value = exc_value sys.last_traceback = exc_tb - StackBrowser(root, flist=flist, top=root, tb=exc_tb) + StackBrowser(top, flist=flist, top=top, tb=exc_tb) # restore sys to original state del sys.last_type diff --git a/Lib/idlelib/statusbar.py b/Lib/idlelib/statusbar.py index e82ba9ab2f..c093920be4 100644 --- a/Lib/idlelib/statusbar.py +++ b/Lib/idlelib/statusbar.py @@ -1,16 +1,14 @@ -from tkinter import * +from tkinter import Frame, Label class MultiStatusBar(Frame): - def __init__(self, master=None, **kw): - if master is None: - master = Tk() + def __init__(self, master, **kw): Frame.__init__(self, master, **kw) self.labels = {} - def set_label(self, name, text='', side=LEFT, width=0): + def set_label(self, name, text='', side='left', width=0): if name not in self.labels: - label = Label(self, borderwidth=0, anchor=W) + label = Label(self, borderwidth=0, anchor='w') label.pack(side=side, pady=0, padx=4) self.labels[name] = label else: @@ -20,27 +18,27 @@ class MultiStatusBar(Frame): label.config(text=text) def _multistatus_bar(parent): - root = Tk() + import re + from tkinter import Toplevel, Frame, Text, Button + top = Toplevel(parent) width, height, x, y = list(map(int, re.split('[x+]', parent.geometry()))) - root.geometry("+%d+%d" %(x, y + 150)) - root.title("Test multistatus bar") - frame = Frame(root) + top.geometry("+%d+%d" %(x, y + 150)) + top.title("Test multistatus bar") + frame = Frame(top) text = Text(frame) text.pack() msb = MultiStatusBar(frame) msb.set_label("one", "hello") msb.set_label("two", "world") - msb.pack(side=BOTTOM, fill=X) + msb.pack(side='bottom', fill='x') def change(): msb.set_label("one", "foo") msb.set_label("two", "bar") - button = Button(root, text="Update status", command=change) - button.pack(side=BOTTOM) + button = Button(top, text="Update status", command=change) + button.pack(side='bottom') frame.pack() - frame.mainloop() - root.mainloop() if __name__ == '__main__': from idlelib.idle_test.htest import run diff --git a/Lib/idlelib/tabbedpages.py b/Lib/idlelib/tabbedpages.py index 965f9f8593..5f67097b5a 100644 --- a/Lib/idlelib/tabbedpages.py +++ b/Lib/idlelib/tabbedpages.py @@ -467,31 +467,29 @@ class TabbedPageSet(Frame): self._tab_set.set_selected_tab(page_name) -def _tabbed_pages(parent): - # test dialog - root=Tk() +def _tabbed_pages(parent): # htest # + import re + top=Toplevel(parent) width, height, x, y = list(map(int, re.split('[x+]', parent.geometry()))) - root.geometry("+%d+%d"%(x, y + 175)) - root.title("Test tabbed pages") - tabPage=TabbedPageSet(root, page_names=['Foobar','Baz'], n_rows=0, + top.geometry("+%d+%d"%(x, y + 175)) + top.title("Test tabbed pages") + tabPage=TabbedPageSet(top, page_names=['Foobar','Baz'], n_rows=0, expand_tabs=False, ) tabPage.pack(side=TOP, expand=TRUE, fill=BOTH) Label(tabPage.pages['Foobar'].frame, text='Foo', pady=20).pack() Label(tabPage.pages['Foobar'].frame, text='Bar', pady=20).pack() Label(tabPage.pages['Baz'].frame, text='Baz').pack() - entryPgName=Entry(root) - buttonAdd=Button(root, text='Add Page', + entryPgName=Entry(top) + buttonAdd=Button(top, text='Add Page', command=lambda:tabPage.add_page(entryPgName.get())) - buttonRemove=Button(root, text='Remove Page', + buttonRemove=Button(top, text='Remove Page', command=lambda:tabPage.remove_page(entryPgName.get())) - labelPgName=Label(root, text='name of page to add/remove:') + labelPgName=Label(top, text='name of page to add/remove:') buttonAdd.pack(padx=5, pady=5) buttonRemove.pack(padx=5, pady=5) labelPgName.pack(padx=5) entryPgName.pack(padx=5) - root.mainloop() - if __name__ == '__main__': from idlelib.idle_test.htest import run diff --git a/Lib/idlelib/undo.py b/Lib/idlelib/undo.py index 3e94b69bbe..ccc962a122 100644 --- a/Lib/idlelib/undo.py +++ b/Lib/idlelib/undo.py @@ -1,7 +1,7 @@ import string -from tkinter import * - from idlelib.delegator import Delegator +# tkintter import not needed because module does not create widgets, +# although many methods operate on text widget arguments. #$ event <> #$ win @@ -339,12 +339,12 @@ class CommandSequence(Command): def _undo_delegator(parent): # htest # import re - import tkinter as tk + from tkinter import Toplevel, Text, Button from idlelib.percolator import Percolator - undowin = tk.Toplevel() + undowin = Toplevel(parent) undowin.title("Test UndoDelegator") width, height, x, y = list(map(int, re.split('[x+]', parent.geometry()))) - undowin.geometry("+%d+%d"%(x, y + 150)) + undowin.geometry("+%d+%d"%(x, y + 175)) text = Text(undowin, height=10) text.pack() @@ -362,7 +362,7 @@ def _undo_delegator(parent): # htest # if __name__ == "__main__": import unittest - unittest.main('idlelib.idle_test.test_undodelegator', verbosity=2, + unittest.main('idlelib.idle_test.test_undo', verbosity=2, exit=False) from idlelib.idle_test.htest import run run(_undo_delegator) diff --git a/Lib/test/test_idle.py b/Lib/test/test_idle.py index 46f1a5c3ec..9d5239409f 100644 --- a/Lib/test/test_idle.py +++ b/Lib/test/test_idle.py @@ -6,6 +6,7 @@ import_module('threading') # imported by PyShell, imports _thread tk = import_module('tkinter') # imports _tkinter if tk.TkVersion < 8.5: raise unittest.SkipTest("IDLE requires tk 8.5 or later.") +tk.NoDefaultRoot() idletest = import_module('idlelib.idle_test') # Without test_main present, regrtest.runtest_inner (line1219) calls -- cgit v1.2.1 From 6c8154a7b71336598fab6ea71a22ad2b817eecb4 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Wed, 22 Jun 2016 04:21:22 -0400 Subject: Issue #27365: temporary rename --- Lib/idlelib/textView.py | 87 +++++++++++++++++++++++++++++++++++++++++++++++++ Lib/idlelib/textview.py | 87 ------------------------------------------------- 2 files changed, 87 insertions(+), 87 deletions(-) create mode 100644 Lib/idlelib/textView.py delete mode 100644 Lib/idlelib/textview.py (limited to 'Lib') diff --git a/Lib/idlelib/textView.py b/Lib/idlelib/textView.py new file mode 100644 index 0000000000..9dc83574ee --- /dev/null +++ b/Lib/idlelib/textView.py @@ -0,0 +1,87 @@ +"""Simple text browser for IDLE + +""" + +from tkinter import * +from tkinter.ttk import Scrollbar +import tkinter.messagebox as tkMessageBox + +class TextViewer(Toplevel): + """A simple text viewer dialog for IDLE + + """ + def __init__(self, parent, title, text, modal=True, _htest=False): + """Show the given text in a scrollable window with a 'close' button + + If modal option set to False, user can interact with other windows, + otherwise they will be unable to interact with other windows until + the textview window is closed. + + _htest - bool; change box location when running htest. + """ + Toplevel.__init__(self, parent) + self.configure(borderwidth=5) + # place dialog below parent if running htest + self.geometry("=%dx%d+%d+%d" % (750, 500, + parent.winfo_rootx() + 10, + parent.winfo_rooty() + (10 if not _htest else 100))) + #elguavas - config placeholders til config stuff completed + self.bg = '#ffffff' + self.fg = '#000000' + + self.CreateWidgets() + self.title(title) + self.protocol("WM_DELETE_WINDOW", self.Ok) + self.parent = parent + self.textView.focus_set() + #key bindings for this dialog + self.bind('',self.Ok) #dismiss dialog + self.bind('',self.Ok) #dismiss dialog + self.textView.insert(0.0, text) + self.textView.config(state=DISABLED) + + if modal: + self.transient(parent) + self.grab_set() + self.wait_window() + + def CreateWidgets(self): + frameText = Frame(self, relief=SUNKEN, height=700) + frameButtons = Frame(self) + self.buttonOk = Button(frameButtons, text='Close', + command=self.Ok, takefocus=FALSE) + self.scrollbarView = Scrollbar(frameText, orient=VERTICAL, + takefocus=FALSE) + self.textView = Text(frameText, wrap=WORD, highlightthickness=0, + fg=self.fg, bg=self.bg) + self.scrollbarView.config(command=self.textView.yview) + self.textView.config(yscrollcommand=self.scrollbarView.set) + self.buttonOk.pack() + self.scrollbarView.pack(side=RIGHT,fill=Y) + self.textView.pack(side=LEFT,expand=TRUE,fill=BOTH) + frameButtons.pack(side=BOTTOM,fill=X) + frameText.pack(side=TOP,expand=TRUE,fill=BOTH) + + def Ok(self, event=None): + self.destroy() + + +def view_text(parent, title, text, modal=True): + return TextViewer(parent, title, text, modal) + +def view_file(parent, title, filename, encoding=None, modal=True): + try: + with open(filename, 'r', encoding=encoding) as file: + contents = file.read() + except IOError: + tkMessageBox.showerror(title='File Load Error', + message='Unable to load file %r .' % filename, + parent=parent) + else: + return view_text(parent, title, contents, modal) + +if __name__ == '__main__': + import unittest + unittest.main('idlelib.idle_test.test_textview', verbosity=2, exit=False) + from idlelib.idle_test.htest import run + run(TextViewer) diff --git a/Lib/idlelib/textview.py b/Lib/idlelib/textview.py deleted file mode 100644 index 9dc83574ee..0000000000 --- a/Lib/idlelib/textview.py +++ /dev/null @@ -1,87 +0,0 @@ -"""Simple text browser for IDLE - -""" - -from tkinter import * -from tkinter.ttk import Scrollbar -import tkinter.messagebox as tkMessageBox - -class TextViewer(Toplevel): - """A simple text viewer dialog for IDLE - - """ - def __init__(self, parent, title, text, modal=True, _htest=False): - """Show the given text in a scrollable window with a 'close' button - - If modal option set to False, user can interact with other windows, - otherwise they will be unable to interact with other windows until - the textview window is closed. - - _htest - bool; change box location when running htest. - """ - Toplevel.__init__(self, parent) - self.configure(borderwidth=5) - # place dialog below parent if running htest - self.geometry("=%dx%d+%d+%d" % (750, 500, - parent.winfo_rootx() + 10, - parent.winfo_rooty() + (10 if not _htest else 100))) - #elguavas - config placeholders til config stuff completed - self.bg = '#ffffff' - self.fg = '#000000' - - self.CreateWidgets() - self.title(title) - self.protocol("WM_DELETE_WINDOW", self.Ok) - self.parent = parent - self.textView.focus_set() - #key bindings for this dialog - self.bind('',self.Ok) #dismiss dialog - self.bind('',self.Ok) #dismiss dialog - self.textView.insert(0.0, text) - self.textView.config(state=DISABLED) - - if modal: - self.transient(parent) - self.grab_set() - self.wait_window() - - def CreateWidgets(self): - frameText = Frame(self, relief=SUNKEN, height=700) - frameButtons = Frame(self) - self.buttonOk = Button(frameButtons, text='Close', - command=self.Ok, takefocus=FALSE) - self.scrollbarView = Scrollbar(frameText, orient=VERTICAL, - takefocus=FALSE) - self.textView = Text(frameText, wrap=WORD, highlightthickness=0, - fg=self.fg, bg=self.bg) - self.scrollbarView.config(command=self.textView.yview) - self.textView.config(yscrollcommand=self.scrollbarView.set) - self.buttonOk.pack() - self.scrollbarView.pack(side=RIGHT,fill=Y) - self.textView.pack(side=LEFT,expand=TRUE,fill=BOTH) - frameButtons.pack(side=BOTTOM,fill=X) - frameText.pack(side=TOP,expand=TRUE,fill=BOTH) - - def Ok(self, event=None): - self.destroy() - - -def view_text(parent, title, text, modal=True): - return TextViewer(parent, title, text, modal) - -def view_file(parent, title, filename, encoding=None, modal=True): - try: - with open(filename, 'r', encoding=encoding) as file: - contents = file.read() - except IOError: - tkMessageBox.showerror(title='File Load Error', - message='Unable to load file %r .' % filename, - parent=parent) - else: - return view_text(parent, title, contents, modal) - -if __name__ == '__main__': - import unittest - unittest.main('idlelib.idle_test.test_textview', verbosity=2, exit=False) - from idlelib.idle_test.htest import run - run(TextViewer) -- cgit v1.2.1 From 7864550dc9a1e11f7a0615ee7a358db2f77c1b02 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Wed, 22 Jun 2016 04:32:06 -0400 Subject: Issue #27365: revert temporary rename --- Lib/idlelib/textView.py | 4 ---- 1 file changed, 4 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/textView.py b/Lib/idlelib/textView.py index b9ca9e3269..9dc83574ee 100644 --- a/Lib/idlelib/textView.py +++ b/Lib/idlelib/textView.py @@ -77,10 +77,6 @@ def view_file(parent, title, filename, encoding=None, modal=True): tkMessageBox.showerror(title='File Load Error', message='Unable to load file %r .' % filename, parent=parent) - except UnicodeDecodeError as err: - tkMessageBox.showerror(title='Unicode Decode Error', - message=str(err), - parent=parent) else: return view_text(parent, title, contents, modal) -- cgit v1.2.1 From 88fe78cbea2d5aff16157e2b0316f519af4575a0 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Wed, 22 Jun 2016 04:50:16 -0400 Subject: Issue #27365: add chunk --- Lib/idlelib/help_about.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Lib') diff --git a/Lib/idlelib/help_about.py b/Lib/idlelib/help_about.py index 362dcbb1df..65d94fce71 100644 --- a/Lib/idlelib/help_about.py +++ b/Lib/idlelib/help_about.py @@ -145,5 +145,7 @@ class AboutDialog(Toplevel): self.destroy() if __name__ == '__main__': + import unittest + unittest.main('idlelib.idle_test.test_helpabout', verbosity=2, exit=False) from idlelib.idle_test.htest import run run(AboutDialog) -- cgit v1.2.1 From ea8a07971bcd43ff3aa402f86d70526a68ec9bd9 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Wed, 22 Jun 2016 04:54:18 -0400 Subject: Issue #27365: add chunk --- Lib/idlelib/textView.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/textView.py b/Lib/idlelib/textView.py index 9dc83574ee..7664524cd3 100644 --- a/Lib/idlelib/textView.py +++ b/Lib/idlelib/textView.py @@ -4,7 +4,7 @@ from tkinter import * from tkinter.ttk import Scrollbar -import tkinter.messagebox as tkMessageBox +from tkinter.messagebox import showerror class TextViewer(Toplevel): """A simple text viewer dialog for IDLE @@ -73,10 +73,14 @@ def view_file(parent, title, filename, encoding=None, modal=True): try: with open(filename, 'r', encoding=encoding) as file: contents = file.read() - except IOError: - tkMessageBox.showerror(title='File Load Error', - message='Unable to load file %r .' % filename, - parent=parent) + except OSError: + showerror(title='File Load Error', + message='Unable to load file %r .' % filename, + parent=parent) + except UnicodeDecodeError as err: + showerror(title='Unicode Decode Error', + message=str(err), + parent=parent) else: return view_text(parent, title, contents, modal) -- cgit v1.2.1 From e92e9a9d667a4ef28f9fc534c55511cde32dfa75 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Wed, 22 Jun 2016 04:57:23 -0400 Subject: Issue #27365: revert temporary rename --- Lib/idlelib/textView.py | 91 ------------------------------------------------- Lib/idlelib/textview.py | 91 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 91 deletions(-) delete mode 100644 Lib/idlelib/textView.py create mode 100644 Lib/idlelib/textview.py (limited to 'Lib') diff --git a/Lib/idlelib/textView.py b/Lib/idlelib/textView.py deleted file mode 100644 index 7664524cd3..0000000000 --- a/Lib/idlelib/textView.py +++ /dev/null @@ -1,91 +0,0 @@ -"""Simple text browser for IDLE - -""" - -from tkinter import * -from tkinter.ttk import Scrollbar -from tkinter.messagebox import showerror - -class TextViewer(Toplevel): - """A simple text viewer dialog for IDLE - - """ - def __init__(self, parent, title, text, modal=True, _htest=False): - """Show the given text in a scrollable window with a 'close' button - - If modal option set to False, user can interact with other windows, - otherwise they will be unable to interact with other windows until - the textview window is closed. - - _htest - bool; change box location when running htest. - """ - Toplevel.__init__(self, parent) - self.configure(borderwidth=5) - # place dialog below parent if running htest - self.geometry("=%dx%d+%d+%d" % (750, 500, - parent.winfo_rootx() + 10, - parent.winfo_rooty() + (10 if not _htest else 100))) - #elguavas - config placeholders til config stuff completed - self.bg = '#ffffff' - self.fg = '#000000' - - self.CreateWidgets() - self.title(title) - self.protocol("WM_DELETE_WINDOW", self.Ok) - self.parent = parent - self.textView.focus_set() - #key bindings for this dialog - self.bind('',self.Ok) #dismiss dialog - self.bind('',self.Ok) #dismiss dialog - self.textView.insert(0.0, text) - self.textView.config(state=DISABLED) - - if modal: - self.transient(parent) - self.grab_set() - self.wait_window() - - def CreateWidgets(self): - frameText = Frame(self, relief=SUNKEN, height=700) - frameButtons = Frame(self) - self.buttonOk = Button(frameButtons, text='Close', - command=self.Ok, takefocus=FALSE) - self.scrollbarView = Scrollbar(frameText, orient=VERTICAL, - takefocus=FALSE) - self.textView = Text(frameText, wrap=WORD, highlightthickness=0, - fg=self.fg, bg=self.bg) - self.scrollbarView.config(command=self.textView.yview) - self.textView.config(yscrollcommand=self.scrollbarView.set) - self.buttonOk.pack() - self.scrollbarView.pack(side=RIGHT,fill=Y) - self.textView.pack(side=LEFT,expand=TRUE,fill=BOTH) - frameButtons.pack(side=BOTTOM,fill=X) - frameText.pack(side=TOP,expand=TRUE,fill=BOTH) - - def Ok(self, event=None): - self.destroy() - - -def view_text(parent, title, text, modal=True): - return TextViewer(parent, title, text, modal) - -def view_file(parent, title, filename, encoding=None, modal=True): - try: - with open(filename, 'r', encoding=encoding) as file: - contents = file.read() - except OSError: - showerror(title='File Load Error', - message='Unable to load file %r .' % filename, - parent=parent) - except UnicodeDecodeError as err: - showerror(title='Unicode Decode Error', - message=str(err), - parent=parent) - else: - return view_text(parent, title, contents, modal) - -if __name__ == '__main__': - import unittest - unittest.main('idlelib.idle_test.test_textview', verbosity=2, exit=False) - from idlelib.idle_test.htest import run - run(TextViewer) diff --git a/Lib/idlelib/textview.py b/Lib/idlelib/textview.py new file mode 100644 index 0000000000..7664524cd3 --- /dev/null +++ b/Lib/idlelib/textview.py @@ -0,0 +1,91 @@ +"""Simple text browser for IDLE + +""" + +from tkinter import * +from tkinter.ttk import Scrollbar +from tkinter.messagebox import showerror + +class TextViewer(Toplevel): + """A simple text viewer dialog for IDLE + + """ + def __init__(self, parent, title, text, modal=True, _htest=False): + """Show the given text in a scrollable window with a 'close' button + + If modal option set to False, user can interact with other windows, + otherwise they will be unable to interact with other windows until + the textview window is closed. + + _htest - bool; change box location when running htest. + """ + Toplevel.__init__(self, parent) + self.configure(borderwidth=5) + # place dialog below parent if running htest + self.geometry("=%dx%d+%d+%d" % (750, 500, + parent.winfo_rootx() + 10, + parent.winfo_rooty() + (10 if not _htest else 100))) + #elguavas - config placeholders til config stuff completed + self.bg = '#ffffff' + self.fg = '#000000' + + self.CreateWidgets() + self.title(title) + self.protocol("WM_DELETE_WINDOW", self.Ok) + self.parent = parent + self.textView.focus_set() + #key bindings for this dialog + self.bind('',self.Ok) #dismiss dialog + self.bind('',self.Ok) #dismiss dialog + self.textView.insert(0.0, text) + self.textView.config(state=DISABLED) + + if modal: + self.transient(parent) + self.grab_set() + self.wait_window() + + def CreateWidgets(self): + frameText = Frame(self, relief=SUNKEN, height=700) + frameButtons = Frame(self) + self.buttonOk = Button(frameButtons, text='Close', + command=self.Ok, takefocus=FALSE) + self.scrollbarView = Scrollbar(frameText, orient=VERTICAL, + takefocus=FALSE) + self.textView = Text(frameText, wrap=WORD, highlightthickness=0, + fg=self.fg, bg=self.bg) + self.scrollbarView.config(command=self.textView.yview) + self.textView.config(yscrollcommand=self.scrollbarView.set) + self.buttonOk.pack() + self.scrollbarView.pack(side=RIGHT,fill=Y) + self.textView.pack(side=LEFT,expand=TRUE,fill=BOTH) + frameButtons.pack(side=BOTTOM,fill=X) + frameText.pack(side=TOP,expand=TRUE,fill=BOTH) + + def Ok(self, event=None): + self.destroy() + + +def view_text(parent, title, text, modal=True): + return TextViewer(parent, title, text, modal) + +def view_file(parent, title, filename, encoding=None, modal=True): + try: + with open(filename, 'r', encoding=encoding) as file: + contents = file.read() + except OSError: + showerror(title='File Load Error', + message='Unable to load file %r .' % filename, + parent=parent) + except UnicodeDecodeError as err: + showerror(title='Unicode Decode Error', + message=str(err), + parent=parent) + else: + return view_text(parent, title, contents, modal) + +if __name__ == '__main__': + import unittest + unittest.main('idlelib.idle_test.test_textview', verbosity=2, exit=False) + from idlelib.idle_test.htest import run + run(TextViewer) -- cgit v1.2.1 From 09d313e9b76458db2a4f5d7a50af43711aeb7874 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Wed, 22 Jun 2016 05:49:15 -0400 Subject: Issue #27365: Finish merge so tests pass. --- Lib/idlelib/help_about.py | 2 +- Lib/idlelib/idle_test/test_help_about.py | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/help_about.py b/Lib/idlelib/help_about.py index 65d94fce71..54f3599ca4 100644 --- a/Lib/idlelib/help_about.py +++ b/Lib/idlelib/help_about.py @@ -146,6 +146,6 @@ class AboutDialog(Toplevel): if __name__ == '__main__': import unittest - unittest.main('idlelib.idle_test.test_helpabout', verbosity=2, exit=False) + unittest.main('idlelib.idle_test.test_help_about', verbosity=2, exit=False) from idlelib.idle_test.htest import run run(AboutDialog) diff --git a/Lib/idlelib/idle_test/test_help_about.py b/Lib/idlelib/idle_test/test_help_about.py index d0a012767a..843efb9ad2 100644 --- a/Lib/idlelib/idle_test/test_help_about.py +++ b/Lib/idlelib/idle_test/test_help_about.py @@ -2,10 +2,10 @@ Coverage: ''' -from idlelib import aboutDialog as help_about -from idlelib import textView as textview +from idlelib import help_about +from idlelib import textview from idlelib.idle_test.mock_idle import Func -from idlelib.idle_test.mock_tk import Mbox +from idlelib.idle_test.mock_tk import Mbox_func import unittest About = help_about.AboutDialog @@ -19,33 +19,33 @@ class Dummy_about_dialog(): class DisplayFileTest(unittest.TestCase): - "Test that .txt files are found and properly decoded." dialog = Dummy_about_dialog() @classmethod def setUpClass(cls): - cls.orig_mbox = textview.tkMessageBox + cls.orig_error = textview.showerror cls.orig_view = textview.view_text - cls.mbox = Mbox() + cls.error = Mbox_func() cls.view = Func() - textview.tkMessageBox = cls.mbox + textview.showerror = cls.error textview.view_text = cls.view cls.About = Dummy_about_dialog() @classmethod def tearDownClass(cls): - textview.tkMessageBox = cls.orig_mbox + textview.showerror = cls.orig_error textview.view_text = cls.orig_view def test_file_isplay(self): for handler in (self.dialog.idle_credits, self.dialog.idle_readme, self.dialog.idle_news): - self.mbox.showerror.message = '' + self.error.message = '' self.view.called = False - handler() - self.assertEqual(self.mbox.showerror.message, '') - self.assertEqual(self.view.called, True) + with self.subTest(handler=handler): + handler() + self.assertEqual(self.error.message, '') + self.assertEqual(self.view.called, True) if __name__ == '__main__': -- cgit v1.2.1 From a52c718a5d46902bbe429710e02f45f738565889 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Fri, 24 Jun 2016 12:03:43 -0700 Subject: Issue #27186: Update os.fspath()/PyOS_FSPath() to check the return type of __fspath__(). As part of this change, also make sure that the pure Python implementation of os.fspath() is tested. --- Lib/os.py | 71 +++++++++++++++++++++++++++++++---------------------- Lib/test/test_io.py | 2 +- Lib/test/test_os.py | 62 ++++++++++++++++++++++++---------------------- 3 files changed, 75 insertions(+), 60 deletions(-) (limited to 'Lib') diff --git a/Lib/os.py b/Lib/os.py index 67e1992836..c31ecb2f05 100644 --- a/Lib/os.py +++ b/Lib/os.py @@ -881,14 +881,11 @@ def _fscodec(): On Windows, use 'strict' error handler if the file system encoding is 'mbcs' (which is the default encoding). """ - filename = fspath(filename) - if isinstance(filename, bytes): - return filename - elif isinstance(filename, str): + filename = fspath(filename) # Does type-checking of `filename`. + if isinstance(filename, str): return filename.encode(encoding, errors) else: - raise TypeError("expected str, bytes or os.PathLike object, not " - + type(filename).__name__) + return filename def fsdecode(filename): """Decode filename (an os.PathLike, bytes, or str) from the filesystem @@ -896,14 +893,11 @@ def _fscodec(): Windows, use 'strict' error handler if the file system encoding is 'mbcs' (which is the default encoding). """ - filename = fspath(filename) - if isinstance(filename, str): - return filename - elif isinstance(filename, bytes): + filename = fspath(filename) # Does type-checking of `filename`. + if isinstance(filename, bytes): return filename.decode(encoding, errors) else: - raise TypeError("expected str, bytes or os.PathLike object, not " - + type(filename).__name__) + return filename return fsencode, fsdecode @@ -1102,27 +1096,44 @@ def fdopen(fd, *args, **kwargs): import io return io.open(fd, *args, **kwargs) -# Supply os.fspath() if not defined in C -if not _exists('fspath'): - def fspath(path): - """Return the string representation of the path. - If str or bytes is passed in, it is returned unchanged. - """ - if isinstance(path, (str, bytes)): - return path +# For testing purposes, make sure the function is available when the C +# implementation exists. +def _fspath(path): + """Return the path representation of a path-like object. - # Work from the object's type to match method resolution of other magic - # methods. - path_type = type(path) - try: - return path_type.__fspath__(path) - except AttributeError: - if hasattr(path_type, '__fspath__'): - raise + If str or bytes is passed in, it is returned unchanged. Otherwise the + os.PathLike interface is used to get the path representation. If the + path representation is not str or bytes, TypeError is raised. If the + provided path is not str, bytes, or os.PathLike, TypeError is raised. + """ + if isinstance(path, (str, bytes)): + return path + + # Work from the object's type to match method resolution of other magic + # methods. + path_type = type(path) + try: + path_repr = path_type.__fspath__(path) + except AttributeError: + if hasattr(path_type, '__fspath__'): + raise + else: + raise TypeError("expected str, bytes or os.PathLike object, " + "not " + path_type.__name__) + if isinstance(path_repr, (str, bytes)): + return path_repr + else: + raise TypeError("expected {}.__fspath__() to return str or bytes, " + "not {}".format(path_type.__name__, + type(path_repr).__name__)) + +# If there is no C implementation, make the pure Python version the +# implementation as transparently as possible. +if not _exists('fspath'): + fspath = _fspath + fspath.__name__ = "fspath" - raise TypeError("expected str, bytes or os.PathLike object, not " - + path_type.__name__) class PathLike(abc.ABC): diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 8581865145..0bfaba9f66 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -879,7 +879,7 @@ class IOTest(unittest.TestCase): check_path_succeeds(PathLike(support.TESTFN.encode('utf-8'))) bad_path = PathLike(TypeError) - with self.assertRaisesRegex(TypeError, 'invalid file'): + with self.assertRaises(TypeError): self.open(bad_path, 'w') # ensure that refcounting is correct with some error conditions diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index d34f6c6432..869985edf2 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -3112,55 +3112,59 @@ class TestScandir(unittest.TestCase): class TestPEP519(unittest.TestCase): - "os.fspath()" + + # Abstracted so it can be overridden to test pure Python implementation + # if a C version is provided. + fspath = staticmethod(os.fspath) + + class PathLike: + def __init__(self, path=''): + self.path = path + def __fspath__(self): + return self.path def test_return_bytes(self): for b in b'hello', b'goodbye', b'some/path/and/file': - self.assertEqual(b, os.fspath(b)) + self.assertEqual(b, self.fspath(b)) def test_return_string(self): for s in 'hello', 'goodbye', 'some/path/and/file': - self.assertEqual(s, os.fspath(s)) - - def test_fsencode_fsdecode_return_pathlike(self): - class PathLike: - def __init__(self, path): - self.path = path - def __fspath__(self): - return self.path + self.assertEqual(s, self.fspath(s)) + def test_fsencode_fsdecode(self): for p in "path/like/object", b"path/like/object": - pathlike = PathLike(p) + pathlike = self.PathLike(p) - self.assertEqual(p, os.fspath(pathlike)) + self.assertEqual(p, self.fspath(pathlike)) self.assertEqual(b"path/like/object", os.fsencode(pathlike)) self.assertEqual("path/like/object", os.fsdecode(pathlike)) - def test_fspathlike(self): - class PathLike: - def __init__(self, path=''): - self.path = path - def __fspath__(self): - return self.path + def test_pathlike(self): + self.assertEqual('#feelthegil', self.fspath(self.PathLike('#feelthegil'))) + self.assertTrue(issubclass(self.PathLike, os.PathLike)) + self.assertTrue(isinstance(self.PathLike(), os.PathLike)) - self.assertEqual('#feelthegil', os.fspath(PathLike('#feelthegil'))) - self.assertTrue(issubclass(PathLike, os.PathLike)) - self.assertTrue(isinstance(PathLike(), os.PathLike)) - - message = 'expected str, bytes or os.PathLike object, not' - for fn in (os.fsencode, os.fsdecode): - for obj in PathLike(None), None: - with self.assertRaisesRegex(TypeError, message): - fn(obj) + with self.assertRaises(TypeError): + self.fspath(self.PathLike(42)) def test_garbage_in_exception_out(self): vapor = type('blah', (), {}) for o in int, type, os, vapor(): - self.assertRaises(TypeError, os.fspath, o) + self.assertRaises(TypeError, self.fspath, o) def test_argument_required(self): with self.assertRaises(TypeError): - os.fspath() + self.fspath() + + +# Only test if the C version is provided, otherwise TestPEP519 already tested +# the pure Python implementation. +if hasattr(os, "_fspath"): + class TestPEP519PurePython(TestPEP519): + + """Explicitly test the pure Python implementation of os.fspath().""" + + fspath = staticmethod(os._fspath) if __name__ == "__main__": -- cgit v1.2.1 From 8ecaa879ea3137279269eb78ead7ad57c1d269e0 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Fri, 24 Jun 2016 14:14:44 -0700 Subject: Issue #27038: Expose DirEntry as os.DirEntry. Thanks to Jelle Zijlstra for the code portion of the patch. --- Lib/test/test_os.py | 1 + 1 file changed, 1 insertion(+) (limited to 'Lib') diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 869985edf2..4ade4aa9db 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2854,6 +2854,7 @@ class TestScandir(unittest.TestCase): self.assertEqual(stat1, stat2) def check_entry(self, entry, name, is_dir, is_file, is_symlink): + self.assertIsInstance(entry, os.DirEntry) self.assertEqual(entry.name, name) self.assertEqual(entry.path, os.path.join(self.path, name)) self.assertEqual(entry.inode(), -- cgit v1.2.1 From 4a67374f04b0dff9dcc56ecb5569cd7deacd8d71 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sat, 25 Jun 2016 05:36:42 +0300 Subject: Minor beautification --- Lib/random.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/random.py b/Lib/random.py index 5950735e3e..1cffb0aaad 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -606,11 +606,11 @@ class Random(_random.Random): # This version due to Janne Sinkkonen, and matches all the std # texts (e.g., Knuth Vol 2 Ed 3 pg 134 "the beta distribution"). - y = self.gammavariate(alpha, 1.) + y = self.gammavariate(alpha, 1.0) if y == 0: return 0.0 else: - return y / (y + self.gammavariate(beta, 1.)) + return y / (y + self.gammavariate(beta, 1.0)) ## -------------------- Pareto -------------------- -- cgit v1.2.1 From 6fa1c5ac450c8408c19d213e70d8b80d4e326815 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Sat, 25 Jun 2016 10:58:17 -0700 Subject: Issue #26186: Remove the restriction that built-in and extension modules can't be lazily loaded. Thanks to Python 3.6 allowing for types.ModuleType to have its __class__ mutated, the restriction can be lifted by calling create_module() on the wrapped loader. --- Lib/importlib/util.py | 24 +++++++++++------------- Lib/test/test_importlib/test_lazy.py | 2 ++ 2 files changed, 13 insertions(+), 13 deletions(-) (limited to 'Lib') diff --git a/Lib/importlib/util.py b/Lib/importlib/util.py index a9d0f1e514..6bdf0d445d 100644 --- a/Lib/importlib/util.py +++ b/Lib/importlib/util.py @@ -204,11 +204,6 @@ def module_for_loader(fxn): return module_for_loader_wrapper -class _Module(types.ModuleType): - - """A subclass of the module type to allow __class__ manipulation.""" - - class _LazyModule(types.ModuleType): """A subclass of the module type which triggers loading upon attribute access.""" @@ -218,13 +213,14 @@ class _LazyModule(types.ModuleType): # All module metadata must be garnered from __spec__ in order to avoid # using mutated values. # Stop triggering this method. - self.__class__ = _Module + self.__class__ = types.ModuleType # Get the original name to make sure no object substitution occurred # in sys.modules. original_name = self.__spec__.name # Figure out exactly what attributes were mutated between the creation # of the module and now. - attrs_then = self.__spec__.loader_state + attrs_then = self.__spec__.loader_state['__dict__'] + original_type = self.__spec__.loader_state['__class__'] attrs_now = self.__dict__ attrs_updated = {} for key, value in attrs_now.items(): @@ -239,9 +235,9 @@ class _LazyModule(types.ModuleType): # object was put into sys.modules. if original_name in sys.modules: if id(self) != id(sys.modules[original_name]): - msg = ('module object for {!r} substituted in sys.modules ' - 'during a lazy load') - raise ValueError(msg.format(original_name)) + raise ValueError(f"module object for {original_name!r} " + "substituted in sys.modules during a lazy " + "load") # Update after loading since that's what would happen in an eager # loading situation. self.__dict__.update(attrs_updated) @@ -275,8 +271,7 @@ class LazyLoader(abc.Loader): self.loader = loader def create_module(self, spec): - """Create a module which can have its __class__ manipulated.""" - return _Module(spec.name) + return self.loader.create_module(spec) def exec_module(self, module): """Make the module load lazily.""" @@ -286,5 +281,8 @@ class LazyLoader(abc.Loader): # on an object would have triggered the load, # e.g. ``module.__spec__.loader = None`` would trigger a load from # trying to access module.__spec__. - module.__spec__.loader_state = module.__dict__.copy() + loader_state = {} + loader_state['__dict__'] = module.__dict__.copy() + loader_state['__class__'] = module.__class__ + module.__spec__.loader_state = loader_state module.__class__ = _LazyModule diff --git a/Lib/test/test_importlib/test_lazy.py b/Lib/test/test_importlib/test_lazy.py index cc383c286d..ffd8dc6cb0 100644 --- a/Lib/test/test_importlib/test_lazy.py +++ b/Lib/test/test_importlib/test_lazy.py @@ -66,6 +66,8 @@ class LazyLoaderTests(unittest.TestCase): spec = util.spec_from_loader(TestingImporter.module_name, util.LazyLoader(loader)) module = spec.loader.create_module(spec) + if module is None: + module = types.ModuleType(TestingImporter.module_name) module.__spec__ = spec module.__loader__ = spec.loader spec.loader.exec_module(module) -- cgit v1.2.1 From 4a078bdcc0f92d69fe95398c9036f1a28c44c230 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 25 Jun 2016 22:43:05 +0300 Subject: Issue #26243: Only the level argument to zlib.compress() is keyword argument now. The first argument is positional-only. --- Lib/test/test_zlib.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_zlib.py b/Lib/test/test_zlib.py index 4d3611c4a6..bb9292bac4 100644 --- a/Lib/test/test_zlib.py +++ b/Lib/test/test_zlib.py @@ -163,8 +163,10 @@ class CompressTestCase(BaseCompressTestCase, unittest.TestCase): self.assertEqual(zlib.decompress(x), HAMLET_SCENE) def test_keywords(self): - x = zlib.compress(data=HAMLET_SCENE, level=3) + x = zlib.compress(HAMLET_SCENE, level=3) self.assertEqual(zlib.decompress(x), HAMLET_SCENE) + with self.assertRaises(TypeError): + zlib.compress(data=HAMLET_SCENE, level=3) def test_speech128(self): # compress more data -- cgit v1.2.1 From e22f897835624a8b3eb3cc8a88b225fa0220d7f2 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 25 Jun 2016 23:52:51 +0300 Subject: Issue #24137: Fixed IDLE on Linux with tkinter default root disabled. --- Lib/idlelib/pyshell.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py index 3e8351fdf3..82e77f97a1 100755 --- a/Lib/idlelib/pyshell.py +++ b/Lib/idlelib/pyshell.py @@ -1562,7 +1562,8 @@ def main(): ext = '.png' if TkVersion >= 8.6 else '.gif' iconfiles = [os.path.join(icondir, 'idle_%d%s' % (size, ext)) for size in (16, 32, 48)] - icons = [PhotoImage(file=iconfile) for iconfile in iconfiles] + icons = [PhotoImage(master=root, file=iconfile) + for iconfile in iconfiles] root.wm_iconphoto(True, *icons) # start editor and/or shell windows: -- cgit v1.2.1 From 95547a8f9b8acf17860515670d4c44231dd7b44d Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 26 Jun 2016 00:09:19 +0300 Subject: Issue #20350. tkapp.splitlist() is now always used instead of unreliable tkapp.split() in the tkinter package. --- Lib/tkinter/__init__.py | 16 +++++++--------- Lib/tkinter/tix.py | 11 +++++------ 2 files changed, 12 insertions(+), 15 deletions(-) (limited to 'Lib') diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index aaa7d14a87..c1d5addcd4 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -383,7 +383,7 @@ class Variable: pass def trace_vinfo(self): """Return all trace callback information.""" - return [self._tk.split(x) for x in self._tk.splitlist( + return [self._tk.splitlist(x) for x in self._tk.splitlist( self._tk.call("trace", "vinfo", self._name))] def __eq__(self, other): """Comparison for equality (==). @@ -1043,18 +1043,16 @@ class Misc: def winfo_visualid(self): """Return the X identifier for the visual for this widget.""" return self.tk.call('winfo', 'visualid', self._w) - def winfo_visualsavailable(self, includeids=0): + def winfo_visualsavailable(self, includeids=False): """Return a list of all visuals available for the screen of this widget. Each item in the list consists of a visual name (see winfo_visual), a - depth and if INCLUDEIDS=1 is given also the X identifier.""" - data = self.tk.split( - self.tk.call('winfo', 'visualsavailable', self._w, - includeids and 'includeids' or None)) - if isinstance(data, str): - data = [self.tk.split(data)] - return [self.__winfo_parseitem(x) for x in data] + depth and if includeids is true is given also the X identifier.""" + data = self.tk.call('winfo', 'visualsavailable', self._w, + 'includeids' if includeids else None) + data = [self.tk.splitlist(x) for x in self.tk.splitlist(data)] + return [self.__winfo_parseitem(x) for x in data] def __winfo_parseitem(self, t): """Internal function.""" return t[:1] + tuple(map(self.__winfo_getint, t[1:])) diff --git a/Lib/tkinter/tix.py b/Lib/tkinter/tix.py index a283cf120a..3d38e5db07 100644 --- a/Lib/tkinter/tix.py +++ b/Lib/tkinter/tix.py @@ -1106,7 +1106,7 @@ class ListNoteBook(TixWidget): def pages(self): # Can't call subwidgets_all directly because we don't want .nbframe - names = self.tk.split(self.tk.call(self._w, 'pages')) + names = self.tk.splitlist(self.tk.call(self._w, 'pages')) ret = [] for x in names: ret.append(self.subwidget(x)) @@ -1152,7 +1152,7 @@ class NoteBook(TixWidget): def pages(self): # Can't call subwidgets_all directly because we don't want .nbframe - names = self.tk.split(self.tk.call(self._w, 'pages')) + names = self.tk.splitlist(self.tk.call(self._w, 'pages')) ret = [] for x in names: ret.append(self.subwidget(x)) @@ -1575,8 +1575,7 @@ class CheckList(TixWidget): '''Returns a list of items whose status matches status. If status is not specified, the list of items in the "on" status will be returned. Mode can be on, off, default''' - c = self.tk.split(self.tk.call(self._w, 'getselection', mode)) - return self.tk.splitlist(c) + return self.tk.splitlist(self.tk.call(self._w, 'getselection', mode)) def getstatus(self, entrypath): '''Returns the current status of entryPath.''' @@ -1897,7 +1896,7 @@ class Grid(TixWidget, XView, YView): or a real number following by the word chars (e.g. 3.4chars) that sets the width of the column to the given number of characters.""" - return self.tk.split(self.tk.call(self._w, 'size', 'column', index, + return self.tk.splitlist(self.tk.call(self._w, 'size', 'column', index, *self._options({}, kw))) def size_row(self, index, **kw): @@ -1922,7 +1921,7 @@ class Grid(TixWidget, XView, YView): or a real number following by the word chars (e.g. 3.4chars) that sets the height of the row to the given number of characters.""" - return self.tk.split(self.tk.call( + return self.tk.splitlist(self.tk.call( self, 'size', 'row', index, *self._options({}, kw))) def unset(self, x, y): -- cgit v1.2.1 From 079c5ddae80a617bec3bf391c3fbce996fd48fa4 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 26 Jun 2016 09:46:57 +0300 Subject: Issue #22115: Added methods trace_add, trace_remove and trace_info in the tkinter.Variable class. They replace old methods trace_variable, trace, trace_vdelete and trace_vinfo that use obsolete Tcl commands and might not work in future versions of Tcl. --- Lib/idlelib/configdialog.py | 38 ++++----- Lib/tkinter/__init__.py | 97 +++++++++++++++++++---- Lib/tkinter/test/test_tkinter/test_variables.py | 101 +++++++++++++++++++++++- 3 files changed, 201 insertions(+), 35 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index d19749a07e..f57c9a1adf 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -465,24 +465,24 @@ class ConfigDialog(Toplevel): return frame def AttachVarCallbacks(self): - self.fontSize.trace_variable('w', self.VarChanged_font) - self.fontName.trace_variable('w', self.VarChanged_font) - self.fontBold.trace_variable('w', self.VarChanged_font) - self.spaceNum.trace_variable('w', self.VarChanged_spaceNum) - self.colour.trace_variable('w', self.VarChanged_colour) - self.builtinTheme.trace_variable('w', self.VarChanged_builtinTheme) - self.customTheme.trace_variable('w', self.VarChanged_customTheme) - self.themeIsBuiltin.trace_variable('w', self.VarChanged_themeIsBuiltin) - self.highlightTarget.trace_variable('w', self.VarChanged_highlightTarget) - self.keyBinding.trace_variable('w', self.VarChanged_keyBinding) - self.builtinKeys.trace_variable('w', self.VarChanged_builtinKeys) - self.customKeys.trace_variable('w', self.VarChanged_customKeys) - self.keysAreBuiltin.trace_variable('w', self.VarChanged_keysAreBuiltin) - self.winWidth.trace_variable('w', self.VarChanged_winWidth) - self.winHeight.trace_variable('w', self.VarChanged_winHeight) - self.startupEdit.trace_variable('w', self.VarChanged_startupEdit) - self.autoSave.trace_variable('w', self.VarChanged_autoSave) - self.encoding.trace_variable('w', self.VarChanged_encoding) + self.fontSize.trace_add('write', self.VarChanged_font) + self.fontName.trace_add('write', self.VarChanged_font) + self.fontBold.trace_add('write', self.VarChanged_font) + self.spaceNum.trace_add('write', self.VarChanged_spaceNum) + self.colour.trace_add('write', self.VarChanged_colour) + self.builtinTheme.trace_add('write', self.VarChanged_builtinTheme) + self.customTheme.trace_add('write', self.VarChanged_customTheme) + self.themeIsBuiltin.trace_add('write', self.VarChanged_themeIsBuiltin) + self.highlightTarget.trace_add('write', self.VarChanged_highlightTarget) + self.keyBinding.trace_add('write', self.VarChanged_keyBinding) + self.builtinKeys.trace_add('write', self.VarChanged_builtinKeys) + self.customKeys.trace_add('write', self.VarChanged_customKeys) + self.keysAreBuiltin.trace_add('write', self.VarChanged_keysAreBuiltin) + self.winWidth.trace_add('write', self.VarChanged_winWidth) + self.winHeight.trace_add('write', self.VarChanged_winHeight) + self.startupEdit.trace_add('write', self.VarChanged_startupEdit) + self.autoSave.trace_add('write', self.VarChanged_autoSave) + self.encoding.trace_add('write', self.VarChanged_encoding) def remove_var_callbacks(self): "Remove callbacks to prevent memory leaks." @@ -493,7 +493,7 @@ class ConfigDialog(Toplevel): self.keyBinding, self.builtinKeys, self.customKeys, self.keysAreBuiltin, self.winWidth, self.winHeight, self.startupEdit, self.autoSave, self.encoding,): - var.trace_vdelete('w', var.trace_vinfo()[0][1]) + var.trace_remove('write', var.trace_info()[0][1]) def VarChanged_font(self, *params): '''When one font attribute changes, save them all, as they are diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index c1d5addcd4..35643e646b 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -343,16 +343,9 @@ class Variable: def get(self): """Return value of variable.""" return self._tk.globalgetvar(self._name) - def trace_variable(self, mode, callback): - """Define a trace callback for the variable. - MODE is one of "r", "w", "u" for read, write, undefine. - CALLBACK must be a function which is called when - the variable is read, written or undefined. - - Return the name of the callback. - """ - f = CallWrapper(callback, None, self).__call__ + def _register(self, callback): + f = CallWrapper(callback, None, self._root).__call__ cbname = repr(id(f)) try: callback = callback.__func__ @@ -366,25 +359,99 @@ class Variable: if self._tclCommands is None: self._tclCommands = [] self._tclCommands.append(cbname) + return cbname + + def trace_add(self, mode, callback): + """Define a trace callback for the variable. + + Mode is one of "read", "write", "unset", or a list or tuple of + such strings. + Callback must be a function which is called when the variable is + read, written or unset. + + Return the name of the callback. + """ + cbname = self._register(callback) + self._tk.call('trace', 'add', 'variable', + self._name, mode, (cbname,)) + return cbname + + def trace_remove(self, mode, cbname): + """Delete the trace callback for a variable. + + Mode is one of "read", "write", "unset" or a list or tuple of + such strings. Must be same as were specified in trace_add(). + cbname is the name of the callback returned from trace_add(). + """ + self._tk.call('trace', 'remove', 'variable', + self._name, mode, cbname) + for m, ca in self.trace_info(): + if self._tk.splitlist(ca)[0] == cbname: + break + else: + self._tk.deletecommand(cbname) + try: + self._tclCommands.remove(cbname) + except ValueError: + pass + + def trace_info(self): + """Return all trace callback information.""" + splitlist = self._tk.splitlist + return [(splitlist(k), v) for k, v in map(splitlist, + splitlist(self._tk.call('trace', 'info', 'variable', self._name)))] + + def trace_variable(self, mode, callback): + """Define a trace callback for the variable. + + MODE is one of "r", "w", "u" for read, write, undefine. + CALLBACK must be a function which is called when + the variable is read, written or undefined. + + Return the name of the callback. + + This deprecated method wraps a deprecated Tcl method that will + likely be removed in the future. Use trace_add() instead. + """ + # TODO: Add deprecation warning + cbname = self._register(callback) self._tk.call("trace", "variable", self._name, mode, cbname) return cbname + trace = trace_variable + def trace_vdelete(self, mode, cbname): """Delete the trace callback for a variable. MODE is one of "r", "w", "u" for read, write, undefine. CBNAME is the name of the callback returned from trace_variable or trace. + + This deprecated method wraps a deprecated Tcl method that will + likely be removed in the future. Use trace_remove() instead. """ + # TODO: Add deprecation warning self._tk.call("trace", "vdelete", self._name, mode, cbname) - self._tk.deletecommand(cbname) - try: - self._tclCommands.remove(cbname) - except ValueError: - pass + cbname = self._tk.splitlist(cbname)[0] + for m, ca in self.trace_info(): + if self._tk.splitlist(ca)[0] == cbname: + break + else: + self._tk.deletecommand(cbname) + try: + self._tclCommands.remove(cbname) + except ValueError: + pass + def trace_vinfo(self): - """Return all trace callback information.""" + """Return all trace callback information. + + This deprecated method wraps a deprecated Tcl method that will + likely be removed in the future. Use trace_info() instead. + """ + # TODO: Add deprecation warning return [self._tk.splitlist(x) for x in self._tk.splitlist( self._tk.call("trace", "vinfo", self._name))] + def __eq__(self, other): """Comparison for equality (==). diff --git a/Lib/tkinter/test/test_tkinter/test_variables.py b/Lib/tkinter/test/test_tkinter/test_variables.py index abdce96998..c529259190 100644 --- a/Lib/tkinter/test/test_tkinter/test_variables.py +++ b/Lib/tkinter/test/test_tkinter/test_variables.py @@ -1,5 +1,5 @@ import unittest - +import gc from tkinter import (Variable, StringVar, IntVar, DoubleVar, BooleanVar, Tcl, TclError) @@ -87,6 +87,105 @@ class TestVariable(TestBase): v.set("value") self.assertTrue(v.side_effect) + def test_trace_old(self): + # Old interface + v = Var(self.root) + vname = str(v) + trace = [] + def read_tracer(*args): + trace.append(('read',) + args) + def write_tracer(*args): + trace.append(('write',) + args) + cb1 = v.trace_variable('r', read_tracer) + cb2 = v.trace_variable('wu', write_tracer) + self.assertEqual(sorted(v.trace_vinfo()), [('r', cb1), ('wu', cb2)]) + self.assertEqual(trace, []) + + v.set('spam') + self.assertEqual(trace, [('write', vname, '', 'w')]) + + trace = [] + v.get() + self.assertEqual(trace, [('read', vname, '', 'r')]) + + trace = [] + info = sorted(v.trace_vinfo()) + v.trace_vdelete('w', cb1) # Wrong mode + self.assertEqual(sorted(v.trace_vinfo()), info) + with self.assertRaises(TclError): + v.trace_vdelete('r', 'spam') # Wrong command name + self.assertEqual(sorted(v.trace_vinfo()), info) + v.trace_vdelete('r', (cb1, 43)) # Wrong arguments + self.assertEqual(sorted(v.trace_vinfo()), info) + v.get() + self.assertEqual(trace, [('read', vname, '', 'r')]) + + trace = [] + v.trace_vdelete('r', cb1) + self.assertEqual(v.trace_vinfo(), [('wu', cb2)]) + v.get() + self.assertEqual(trace, []) + + trace = [] + del write_tracer + gc.collect() + v.set('eggs') + self.assertEqual(trace, [('write', vname, '', 'w')]) + + trace = [] + del v + gc.collect() + self.assertEqual(trace, [('write', vname, '', 'u')]) + + def test_trace(self): + v = Var(self.root) + vname = str(v) + trace = [] + def read_tracer(*args): + trace.append(('read',) + args) + def write_tracer(*args): + trace.append(('write',) + args) + tr1 = v.trace_add('read', read_tracer) + tr2 = v.trace_add(['write', 'unset'], write_tracer) + self.assertEqual(sorted(v.trace_info()), [ + (('read',), tr1), + (('write', 'unset'), tr2)]) + self.assertEqual(trace, []) + + v.set('spam') + self.assertEqual(trace, [('write', vname, '', 'write')]) + + trace = [] + v.get() + self.assertEqual(trace, [('read', vname, '', 'read')]) + + trace = [] + info = sorted(v.trace_info()) + v.trace_remove('write', tr1) # Wrong mode + self.assertEqual(sorted(v.trace_info()), info) + with self.assertRaises(TclError): + v.trace_remove('read', 'spam') # Wrong command name + self.assertEqual(sorted(v.trace_info()), info) + v.get() + self.assertEqual(trace, [('read', vname, '', 'read')]) + + trace = [] + v.trace_remove('read', tr1) + self.assertEqual(v.trace_info(), [(('write', 'unset'), tr2)]) + v.get() + self.assertEqual(trace, []) + + trace = [] + del write_tracer + gc.collect() + v.set('eggs') + self.assertEqual(trace, [('write', vname, '', 'write')]) + + trace = [] + del v + gc.collect() + self.assertEqual(trace, [('write', vname, '', 'unset')]) + class TestStringVar(TestBase): -- cgit v1.2.1 From 1a87623e51d6f4813c849e4d89762a6b4de9b19b Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Sun, 26 Jun 2016 17:48:02 -0400 Subject: Issue 27372: Stop test_idle from changing locale, so test passes. In 3.6, the warning is now called an error, making it harder to ignore. --- Lib/idlelib/__init__.py | 1 + Lib/idlelib/editor.py | 8 +++-- Lib/idlelib/iomenu.py | 86 ++++++++++++++++++++++++++----------------------- Lib/test/test_idle.py | 15 +++++---- 4 files changed, 62 insertions(+), 48 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/__init__.py b/Lib/idlelib/__init__.py index fef21bee14..791ddeab79 100644 --- a/Lib/idlelib/__init__.py +++ b/Lib/idlelib/__init__.py @@ -7,3 +7,4 @@ Use the files named idle.* to start Idle. The other files are private implementations. Their details are subject to change. See PEP 434 for more. Import them at your own risk. """ +testing = False # Set True by test.test_idle. diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index 7f910e76f9..07a1181c7f 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -26,9 +26,9 @@ from idlelib import help # The default tab setting for a Text widget, in average-width characters. TK_TABWIDTH_DEFAULT = 8 - _py_version = ' (%s)' % platform.python_version() + def _sphinx_version(): "Format sys.version_info to produce the Sphinx version string used to install the chm docs" major, minor, micro, level, serial = sys.version_info @@ -45,11 +45,12 @@ class EditorWindow(object): from idlelib.percolator import Percolator from idlelib.colorizer import ColorDelegator, color_config from idlelib.undo import UndoDelegator - from idlelib.iomenu import IOBinding, filesystemencoding, encoding + from idlelib.iomenu import IOBinding, encoding from idlelib import mainmenu from tkinter import Toplevel from idlelib.statusbar import MultiStatusBar + filesystemencoding = sys.getfilesystemencoding() # for file names help_url = None def __init__(self, flist=None, filename=None, key=None, root=None): @@ -1649,5 +1650,8 @@ def _editor_window(parent): # htest # # edit.text.bind("<>", edit.close_event) if __name__ == '__main__': + import unittest + unittest.main('idlelib.idle_test.test_editor', verbosity=2, exit=False) + from idlelib.idle_test.htest import run run(_editor_window) diff --git a/Lib/idlelib/iomenu.py b/Lib/idlelib/iomenu.py index f6a7f1489f..18c68bd35e 100644 --- a/Lib/idlelib/iomenu.py +++ b/Lib/idlelib/iomenu.py @@ -10,57 +10,60 @@ import tkinter.filedialog as tkFileDialog import tkinter.messagebox as tkMessageBox from tkinter.simpledialog import askstring +import idlelib from idlelib.config import idleConf - -# Try setting the locale, so that we can find out -# what encoding to use -try: - import locale - locale.setlocale(locale.LC_CTYPE, "") -except (ImportError, locale.Error): - pass - -# Encoding for file names -filesystemencoding = sys.getfilesystemencoding() ### currently unused - -locale_encoding = 'ascii' -if sys.platform == 'win32': - # On Windows, we could use "mbcs". However, to give the user - # a portable encoding name, we need to find the code page - try: - locale_encoding = locale.getdefaultlocale()[1] - codecs.lookup(locale_encoding) - except LookupError: - pass +if idlelib.testing: # Set True by test.test_idle to avoid setlocale. + encoding = 'utf-8' else: + # Try setting the locale, so that we can find out + # what encoding to use try: - # Different things can fail here: the locale module may not be - # loaded, it may not offer nl_langinfo, or CODESET, or the - # resulting codeset may be unknown to Python. We ignore all - # these problems, falling back to ASCII - locale_encoding = locale.nl_langinfo(locale.CODESET) - if locale_encoding is None or locale_encoding is '': - # situation occurs on Mac OS X - locale_encoding = 'ascii' - codecs.lookup(locale_encoding) - except (NameError, AttributeError, LookupError): - # Try getdefaultlocale: it parses environment variables, - # which may give a clue. Unfortunately, getdefaultlocale has - # bugs that can cause ValueError. + import locale + locale.setlocale(locale.LC_CTYPE, "") + except (ImportError, locale.Error): + pass + + locale_decode = 'ascii' + if sys.platform == 'win32': + # On Windows, we could use "mbcs". However, to give the user + # a portable encoding name, we need to find the code page try: locale_encoding = locale.getdefaultlocale()[1] + codecs.lookup(locale_encoding) + except LookupError: + pass + else: + try: + # Different things can fail here: the locale module may not be + # loaded, it may not offer nl_langinfo, or CODESET, or the + # resulting codeset may be unknown to Python. We ignore all + # these problems, falling back to ASCII + locale_encoding = locale.nl_langinfo(locale.CODESET) if locale_encoding is None or locale_encoding is '': # situation occurs on Mac OS X locale_encoding = 'ascii' codecs.lookup(locale_encoding) - except (ValueError, LookupError): - pass + except (NameError, AttributeError, LookupError): + # Try getdefaultlocale: it parses environment variables, + # which may give a clue. Unfortunately, getdefaultlocale has + # bugs that can cause ValueError. + try: + locale_encoding = locale.getdefaultlocale()[1] + if locale_encoding is None or locale_encoding is '': + # situation occurs on Mac OS X + locale_encoding = 'ascii' + codecs.lookup(locale_encoding) + except (ValueError, LookupError): + pass -locale_encoding = locale_encoding.lower() + locale_encoding = locale_encoding.lower() -encoding = locale_encoding ### KBK 07Sep07 This is used all over IDLE, check! - ### 'encoding' is used below in encode(), check! + encoding = locale_encoding + # Encoding is used in multiple files; locale_encoding nowhere. + # The only use of 'encoding' below is in _decode as initial value + # of deprecated block asking user for encoding. + # Perhaps use elsewhere should be reviewed. coding_re = re.compile(r'^[ \t\f]*#.*?coding[:=][ \t]*([-\w.]+)', re.ASCII) blank_re = re.compile(r'^[ \t\f]*(?:[#\r\n]|$)', re.ASCII) @@ -304,7 +307,7 @@ class IOBinding: "The file's encoding is invalid for Python 3.x.\n" "IDLE will convert it to UTF-8.\n" "What is the current encoding of the file?", - initialvalue = locale_encoding, + initialvalue = encoding, parent = self.editwin.text) if enc: @@ -564,5 +567,8 @@ def _io_binding(parent): # htest # IOBinding(editwin) if __name__ == "__main__": + import unittest + unittest.main('idlelib.idle_test.test_iomenu', verbosity=2, exit=False) + from idlelib.idle_test.htest import run run(_io_binding) diff --git a/Lib/test/test_idle.py b/Lib/test/test_idle.py index 9d5239409f..ad88e2451c 100644 --- a/Lib/test/test_idle.py +++ b/Lib/test/test_idle.py @@ -1,18 +1,21 @@ import unittest from test.support import import_module -# Skip test if _thread or _tkinter wasn't built or idlelib was deleted. +# Skip test if _thread or _tkinter wasn't built, or idlelib is missing, +# or if tcl/tk version before 8.5, which is needed for ttk widgets. + import_module('threading') # imported by PyShell, imports _thread tk = import_module('tkinter') # imports _tkinter if tk.TkVersion < 8.5: raise unittest.SkipTest("IDLE requires tk 8.5 or later.") tk.NoDefaultRoot() -idletest = import_module('idlelib.idle_test') +idlelib = import_module('idlelib') +idlelib.testing = True # Avoid locale-changed test error -# Without test_main present, regrtest.runtest_inner (line1219) calls -# unittest.TestLoader().loadTestsFromModule(this_module) which calls -# load_tests() if it finds it. (Unittest.main does the same.) -load_tests = idletest.load_tests +# Without test_main present, test.libregrtest.runtest.runtest_inner +# calls (line 173) unittest.TestLoader().loadTestsFromModule(module) +# which calls load_tests() if it finds it. (Unittest.main does the same.) +from idlelib.idle_test import load_tests if __name__ == '__main__': unittest.main(verbosity=2, exit=False) -- cgit v1.2.1 From 74ef5828da00935196f4dfc5d4cafb4a9eaba467 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Sun, 26 Jun 2016 22:05:10 -0400 Subject: Issue #27380: IDLE: add base Query dialog, with ttk widgets and subclass SectionName. These split class GetCfgSectionNameDialog from configSectionNameDialog.py, temporarily renamed config_sec.py in 3.7.9a2. More Query subclasses are planned. --- Lib/idlelib/config_sec.py | 98 ------------------ Lib/idlelib/configdialog.py | 6 +- Lib/idlelib/idle_test/htest.py | 23 +++-- Lib/idlelib/idle_test/test_config_sec.py | 75 -------------- Lib/idlelib/idle_test/test_query.py | 164 +++++++++++++++++++++++++++++++ Lib/idlelib/query.py | 148 ++++++++++++++++++++++++++++ 6 files changed, 326 insertions(+), 188 deletions(-) delete mode 100644 Lib/idlelib/config_sec.py delete mode 100644 Lib/idlelib/idle_test/test_config_sec.py create mode 100644 Lib/idlelib/idle_test/test_query.py create mode 100644 Lib/idlelib/query.py (limited to 'Lib') diff --git a/Lib/idlelib/config_sec.py b/Lib/idlelib/config_sec.py deleted file mode 100644 index 7b59124507..0000000000 --- a/Lib/idlelib/config_sec.py +++ /dev/null @@ -1,98 +0,0 @@ -""" -Dialog that allows user to specify a new config file section name. -Used to get new highlight theme and keybinding set names. -The 'return value' for the dialog, used two placed in configdialog.py, -is the .result attribute set in the Ok and Cancel methods. -""" -from tkinter import * -import tkinter.messagebox as tkMessageBox - -class GetCfgSectionNameDialog(Toplevel): - def __init__(self, parent, title, message, used_names, _htest=False): - """ - message - string, informational message to display - used_names - string collection, names already in use for validity check - _htest - bool, change box location when running htest - """ - Toplevel.__init__(self, parent) - self.configure(borderwidth=5) - self.resizable(height=FALSE, width=FALSE) - self.title(title) - self.transient(parent) - self.grab_set() - self.protocol("WM_DELETE_WINDOW", self.Cancel) - self.parent = parent - self.message = message - self.used_names = used_names - self.create_widgets() - self.withdraw() #hide while setting geometry - self.update_idletasks() - #needs to be done here so that the winfo_reqwidth is valid - self.messageInfo.config(width=self.frameMain.winfo_reqwidth()) - self.geometry( - "+%d+%d" % ( - parent.winfo_rootx() + - (parent.winfo_width()/2 - self.winfo_reqwidth()/2), - parent.winfo_rooty() + - ((parent.winfo_height()/2 - self.winfo_reqheight()/2) - if not _htest else 100) - ) ) #centre dialog over parent (or below htest box) - self.deiconify() #geometry set, unhide - self.wait_window() - - def create_widgets(self): - self.name = StringVar(self.parent) - self.fontSize = StringVar(self.parent) - self.frameMain = Frame(self, borderwidth=2, relief=SUNKEN) - self.frameMain.pack(side=TOP, expand=TRUE, fill=BOTH) - self.messageInfo = Message(self.frameMain, anchor=W, justify=LEFT, - padx=5, pady=5, text=self.message) #,aspect=200) - entryName = Entry(self.frameMain, textvariable=self.name, width=30) - entryName.focus_set() - self.messageInfo.pack(padx=5, pady=5) #, expand=TRUE, fill=BOTH) - entryName.pack(padx=5, pady=5) - - frameButtons = Frame(self, pady=2) - frameButtons.pack(side=BOTTOM) - self.buttonOk = Button(frameButtons, text='Ok', - width=8, command=self.Ok) - self.buttonOk.pack(side=LEFT, padx=5) - self.buttonCancel = Button(frameButtons, text='Cancel', - width=8, command=self.Cancel) - self.buttonCancel.pack(side=RIGHT, padx=5) - - def name_ok(self): - ''' After stripping entered name, check that it is a sensible - ConfigParser file section name. Return it if it is, '' if not. - ''' - name = self.name.get().strip() - if not name: #no name specified - tkMessageBox.showerror(title='Name Error', - message='No name specified.', parent=self) - elif len(name)>30: #name too long - tkMessageBox.showerror(title='Name Error', - message='Name too long. It should be no more than '+ - '30 characters.', parent=self) - name = '' - elif name in self.used_names: - tkMessageBox.showerror(title='Name Error', - message='This name is already in use.', parent=self) - name = '' - return name - - def Ok(self, event=None): - name = self.name_ok() - if name: - self.result = name - self.destroy() - - def Cancel(self, event=None): - self.result = '' - self.destroy() - -if __name__ == '__main__': - import unittest - unittest.main('idlelib.idle_test.test_config_name', verbosity=2, exit=False) - - from idlelib.idle_test.htest import run - run(GetCfgSectionNameDialog) diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index f57c9a1adf..6629d70ec6 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -18,7 +18,7 @@ import tkinter.font as tkFont from idlelib.config import idleConf from idlelib.dynoption import DynOptionMenu from idlelib.config_key import GetKeysDialog -from idlelib.config_sec import GetCfgSectionNameDialog +from idlelib.query import SectionName from idlelib.config_help import GetHelpSourceDialog from idlelib.tabbedpages import TabbedPageSet from idlelib.textview import view_text @@ -684,7 +684,7 @@ class ConfigDialog(Toplevel): def GetNewKeysName(self, message): usedNames = (idleConf.GetSectionList('user', 'keys') + idleConf.GetSectionList('default', 'keys')) - newKeySet = GetCfgSectionNameDialog( + newKeySet = SectionName( self, 'New Custom Key Set', message, usedNames).result return newKeySet @@ -837,7 +837,7 @@ class ConfigDialog(Toplevel): def GetNewThemeName(self, message): usedNames = (idleConf.GetSectionList('user', 'highlight') + idleConf.GetSectionList('default', 'highlight')) - newTheme = GetCfgSectionNameDialog( + newTheme = SectionName( self, 'New Custom Theme', message, usedNames).result return newTheme diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py index 701f4d9fe6..d809d30dd0 100644 --- a/Lib/idlelib/idle_test/htest.py +++ b/Lib/idlelib/idle_test/htest.py @@ -137,18 +137,6 @@ _editor_window_spec = { "Best to close editor first." } -GetCfgSectionNameDialog_spec = { - 'file': 'config_sec', - 'kwds': {'title':'Get Name', - 'message':'Enter something', - 'used_names': {'abc'}, - '_htest': True}, - 'msg': "After the text entered with [Ok] is stripped, , " - "'abc', or more that 30 chars are errors.\n" - "Close 'Get Name' with a valid entry (printed to Shell), " - "[Cancel], or [X]", - } - GetHelpSourceDialog_spec = { 'file': 'config_help', 'kwds': {'title': 'Get helpsource', @@ -245,6 +233,17 @@ _percolator_spec = { "Test for actions like text entry, and removal." } +Query_spec = { + 'file': 'query', + 'kwds': {'title':'Query', + 'message':'Enter something', + '_htest': True}, + 'msg': "Enter with or [Ok]. Print valid entry to Shell\n" + "Blank line, after stripping, is ignored\n" + "Close dialog with valid entry, [Cancel] or [X]", + } + + _replace_dialog_spec = { 'file': 'replace', 'kwds': {}, diff --git a/Lib/idlelib/idle_test/test_config_sec.py b/Lib/idlelib/idle_test/test_config_sec.py deleted file mode 100644 index a98b484bf7..0000000000 --- a/Lib/idlelib/idle_test/test_config_sec.py +++ /dev/null @@ -1,75 +0,0 @@ -"""Unit tests for idlelib.config_sec""" -import unittest -from idlelib.idle_test.mock_tk import Var, Mbox -from idlelib import config_sec as name_dialog_module - -name_dialog = name_dialog_module.GetCfgSectionNameDialog - -class Dummy_name_dialog: - # Mock for testing the following methods of name_dialog - name_ok = name_dialog.name_ok - Ok = name_dialog.Ok - Cancel = name_dialog.Cancel - # Attributes, constant or variable, needed for tests - used_names = ['used'] - name = Var() - result = None - destroyed = False - def destroy(self): - self.destroyed = True - -# name_ok calls Mbox.showerror if name is not ok -orig_mbox = name_dialog_module.tkMessageBox -showerror = Mbox.showerror - -class ConfigNameTest(unittest.TestCase): - dialog = Dummy_name_dialog() - - @classmethod - def setUpClass(cls): - name_dialog_module.tkMessageBox = Mbox - - @classmethod - def tearDownClass(cls): - name_dialog_module.tkMessageBox = orig_mbox - - def test_blank_name(self): - self.dialog.name.set(' ') - self.assertEqual(self.dialog.name_ok(), '') - self.assertEqual(showerror.title, 'Name Error') - self.assertIn('No', showerror.message) - - def test_used_name(self): - self.dialog.name.set('used') - self.assertEqual(self.dialog.name_ok(), '') - self.assertEqual(showerror.title, 'Name Error') - self.assertIn('use', showerror.message) - - def test_long_name(self): - self.dialog.name.set('good'*8) - self.assertEqual(self.dialog.name_ok(), '') - self.assertEqual(showerror.title, 'Name Error') - self.assertIn('too long', showerror.message) - - def test_good_name(self): - self.dialog.name.set(' good ') - showerror.title = 'No Error' # should not be called - self.assertEqual(self.dialog.name_ok(), 'good') - self.assertEqual(showerror.title, 'No Error') - - def test_ok(self): - self.dialog.destroyed = False - self.dialog.name.set('good') - self.dialog.Ok() - self.assertEqual(self.dialog.result, 'good') - self.assertTrue(self.dialog.destroyed) - - def test_cancel(self): - self.dialog.destroyed = False - self.dialog.Cancel() - self.assertEqual(self.dialog.result, '') - self.assertTrue(self.dialog.destroyed) - - -if __name__ == '__main__': - unittest.main(verbosity=2, exit=False) diff --git a/Lib/idlelib/idle_test/test_query.py b/Lib/idlelib/idle_test/test_query.py new file mode 100644 index 0000000000..e9c4bd4029 --- /dev/null +++ b/Lib/idlelib/idle_test/test_query.py @@ -0,0 +1,164 @@ +"""Test idlelib.query. + +Coverage: 100%. +""" +from test.support import requires +from tkinter import Tk +import unittest +from unittest import mock +from idlelib.idle_test.mock_tk import Var, Mbox_func +from idlelib import query +Query, SectionName = query.Query, query.SectionName + +class Dummy_Query: + # Mock for testing the following methods Query + entry_ok = Query.entry_ok + ok = Query.ok + cancel = Query.cancel + # Attributes, constant or variable, needed for tests + entry = Var() + result = None + destroyed = False + def destroy(self): + self.destroyed = True + +# entry_ok calls modal messagebox.showerror if entry is not ok. +# Mock showerrer returns, so don't need to click to continue. +orig_showerror = query.showerror +showerror = Mbox_func() # Instance has __call__ method. + +def setUpModule(): + query.showerror = showerror + +def tearDownModule(): + query.showerror = orig_showerror + + +class QueryTest(unittest.TestCase): + dialog = Dummy_Query() + + def setUp(self): + showerror.title = None + self.dialog.result = None + self.dialog.destroyed = False + + def test_blank_entry(self): + dialog = self.dialog + Equal = self.assertEqual + dialog.entry.set(' ') + Equal(dialog.entry_ok(), '') + Equal((dialog.result, dialog.destroyed), (None, False)) + Equal(showerror.title, 'Entry Error') + self.assertIn('Blank', showerror.message) + + def test_good_entry(self): + dialog = self.dialog + Equal = self.assertEqual + dialog.entry.set(' good ') + Equal(dialog.entry_ok(), 'good') + Equal((dialog.result, dialog.destroyed), (None, False)) + Equal(showerror.title, None) + + def test_ok(self): + dialog = self.dialog + Equal = self.assertEqual + dialog.entry.set('good') + Equal(dialog.ok(), None) + Equal((dialog.result, dialog.destroyed), ('good', True)) + + def test_cancel(self): + dialog = self.dialog + Equal = self.assertEqual + Equal(self.dialog.cancel(), None) + Equal((dialog.result, dialog.destroyed), (None, True)) + + +class Dummy_SectionName: + # Mock for testing the following method of Section_Name + entry_ok = SectionName.entry_ok + # Attributes, constant or variable, needed for tests + used_names = ['used'] + entry = Var() + +class SectionNameTest(unittest.TestCase): + dialog = Dummy_SectionName() + + + def setUp(self): + showerror.title = None + + def test_blank_name(self): + dialog = self.dialog + Equal = self.assertEqual + dialog.entry.set(' ') + Equal(dialog.entry_ok(), '') + Equal(showerror.title, 'Name Error') + self.assertIn('No', showerror.message) + + def test_used_name(self): + dialog = self.dialog + Equal = self.assertEqual + dialog.entry.set('used') + Equal(self.dialog.entry_ok(), '') + Equal(showerror.title, 'Name Error') + self.assertIn('use', showerror.message) + + def test_long_name(self): + dialog = self.dialog + Equal = self.assertEqual + dialog.entry.set('good'*8) + Equal(self.dialog.entry_ok(), '') + Equal(showerror.title, 'Name Error') + self.assertIn('too long', showerror.message) + + def test_good_entry(self): + dialog = self.dialog + Equal = self.assertEqual + dialog.entry.set(' good ') + Equal(dialog.entry_ok(), 'good') + Equal(showerror.title, None) + + +class QueryGuiTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + requires('gui') + cls.root = Tk() + cls.dialog = Query(cls.root, 'TEST', 'test', _utest=True) + cls.dialog.destroy = mock.Mock() + + @classmethod + def tearDownClass(cls): + del cls.dialog + cls.root.destroy() + del cls.root + + def setUp(self): + self.dialog.entry.delete(0, 'end') + self.dialog.result = None + self.dialog.destroy.reset_mock() + + def test_click_ok(self): + dialog = self.dialog + dialog.entry.insert(0, 'abc') + dialog.button_ok.invoke() + self.assertEqual(dialog.result, 'abc') + self.assertTrue(dialog.destroy.called) + + def test_click_blank(self): + dialog = self.dialog + dialog.button_ok.invoke() + self.assertEqual(dialog.result, None) + self.assertFalse(dialog.destroy.called) + + def test_click_cancel(self): + dialog = self.dialog + dialog.entry.insert(0, 'abc') + dialog.button_cancel.invoke() + self.assertEqual(dialog.result, None) + self.assertTrue(dialog.destroy.called) + + +if __name__ == '__main__': + unittest.main(verbosity=2, exit=False) diff --git a/Lib/idlelib/query.py b/Lib/idlelib/query.py new file mode 100644 index 0000000000..e3937a1340 --- /dev/null +++ b/Lib/idlelib/query.py @@ -0,0 +1,148 @@ +""" +Dialogs that query users and verify the answer before accepting. +Use ttk widgets, limiting use to tcl/tk 8.5+, as in IDLE 3.6+. + +Query is the generic base class for a popup dialog. +The user must either enter a valid answer or close the dialog. +Entries are validated when is entered or [Ok] is clicked. +Entries are ignored when [Cancel] or [X] are clicked. +The 'return value' is .result set to either a valid answer or None. + +Subclass SectionName gets a name for a new config file section. +Configdialog uses it for new highlight theme and keybinding set names. +""" +# Query and Section name result from splitting GetCfgSectionNameDialog +# of configSectionNameDialog.py (temporarily config_sec.py) into +# generic and specific parts. + +from tkinter import FALSE, TRUE, Toplevel +from tkinter.messagebox import showerror +from tkinter.ttk import Frame, Button, Entry, Label + +class Query(Toplevel): + """Base class for getting verified answer from a user. + + For this base class, accept any non-blank string. + """ + def __init__(self, parent, title, message, + *, _htest=False, _utest=False): # Call from override. + """Create popup, do not return until tk widget destroyed. + + Additional subclass init must be done before calling this. + + title - string, title of popup dialog + message - string, informational message to display + _htest - bool, change box location when running htest + _utest - bool, leave window hidden and not modal + """ + Toplevel.__init__(self, parent) + self.configure(borderwidth=5) + self.resizable(height=FALSE, width=FALSE) + self.title(title) + self.transient(parent) + self.grab_set() + self.bind('', self.ok) + self.protocol("WM_DELETE_WINDOW", self.cancel) + self.parent = parent + self.message = message + self.create_widgets() + self.update_idletasks() + #needs to be done here so that the winfo_reqwidth is valid + self.withdraw() # Hide while configuring, especially geometry. + self.geometry( + "+%d+%d" % ( + parent.winfo_rootx() + + (parent.winfo_width()/2 - self.winfo_reqwidth()/2), + parent.winfo_rooty() + + ((parent.winfo_height()/2 - self.winfo_reqheight()/2) + if not _htest else 150) + ) ) #centre dialog over parent (or below htest box) + if not _utest: + self.deiconify() #geometry set, unhide + self.wait_window() + + def create_widgets(self): # Call from override, if any. + frame = Frame(self, borderwidth=2, relief='sunken', ) + label = Label(frame, anchor='w', justify='left', + text=self.message) + self.entry = Entry(frame, width=30) # Bind name for entry_ok. + self.entry.focus_set() + + buttons = Frame(self) # Bind buttons for invoke in unittest. + self.button_ok = Button(buttons, text='Ok', + width=8, command=self.ok) + self.button_cancel = Button(buttons, text='Cancel', + width=8, command=self.cancel) + + frame.pack(side='top', expand=TRUE, fill='both') + label.pack(padx=5, pady=5) + self.entry.pack(padx=5, pady=5) + buttons.pack(side='bottom') + self.button_ok.pack(side='left', padx=5) + self.button_cancel.pack(side='right', padx=5) + + def entry_ok(self): # Usually replace. + "Check that entry not blank." + entry = self.entry.get().strip() + if not entry: + showerror(title='Entry Error', + message='Blank line.', parent=self) + return entry + + def ok(self, event=None): # Do not replace. + '''If entry is valid, bind it to 'result' and destroy tk widget. + + Otherwise leave dialog open for user to correct entry or cancel. + ''' + entry = self.entry_ok() + if entry: + self.result = entry + self.destroy() + else: + # [Ok] (but not ) moves focus. Move it back. + self.entry.focus_set() + + def cancel(self, event=None): # Do not replace. + "Set dialog result to None and destroy tk widget." + self.result = None + self.destroy() + + +class SectionName(Query): + "Get a name for a config file section name." + + def __init__(self, parent, title, message, used_names, + *, _htest=False, _utest=False): + "used_names - collection of strings already in use" + + self.used_names = used_names + Query.__init__(self, parent, title, message, + _htest=_htest, _utest=_utest) + # This call does ot return until tk widget is destroyed. + + def entry_ok(self): + '''Stripping entered name, check that it is a sensible + ConfigParser file section name. Return it if it is, '' if not. + ''' + name = self.entry.get().strip() + if not name: + showerror(title='Name Error', + message='No name specified.', parent=self) + elif len(name)>30: + showerror(title='Name Error', + message='Name too long. It should be no more than '+ + '30 characters.', parent=self) + name = '' + elif name in self.used_names: + showerror(title='Name Error', + message='This name is already in use.', parent=self) + name = '' + return name + + +if __name__ == '__main__': + import unittest + unittest.main('idlelib.idle_test.test_query', verbosity=2, exit=False) + + from idlelib.idle_test.htest import run + run(Query) -- cgit v1.2.1 From 7529665d1adf1ad2ce671326d87c5dd00538ac9a Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 27 Jun 2016 23:40:43 +0300 Subject: Issue #27352: Fixed an error message in a test. --- Lib/test/test_ast.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index 7b43be6dfc..e032f6d27a 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -754,7 +754,7 @@ class ASTValidatorTests(unittest.TestCase): def test_importfrom(self): imp = ast.ImportFrom(None, [ast.alias("x", None)], -42) - self.stmt(imp, "level less than -1") + self.stmt(imp, "Negative ImportFrom level") self.stmt(ast.ImportFrom(None, [], 0), "empty names on ImportFrom") def test_global(self): -- cgit v1.2.1 From ef9608a0640c17f0f0dd05ee79d78ecc833d0806 Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Wed, 29 Jun 2016 10:12:22 +0000 Subject: Issue #26721: Change StreamRequestHandler.wfile to BufferedIOBase --- Lib/socketserver.py | 24 ++++++++++++- Lib/test/test_socketserver.py | 79 +++++++++++++++++++++++++++++++++++++++++++ Lib/wsgiref/simple_server.py | 17 +++------- 3 files changed, 107 insertions(+), 13 deletions(-) (limited to 'Lib') diff --git a/Lib/socketserver.py b/Lib/socketserver.py index c6d38c775a..41a3766772 100644 --- a/Lib/socketserver.py +++ b/Lib/socketserver.py @@ -132,6 +132,7 @@ try: import threading except ImportError: import dummy_threading as threading +from io import BufferedIOBase from time import monotonic as time __all__ = ["BaseServer", "TCPServer", "UDPServer", @@ -743,7 +744,10 @@ class StreamRequestHandler(BaseRequestHandler): self.connection.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, True) self.rfile = self.connection.makefile('rb', self.rbufsize) - self.wfile = self.connection.makefile('wb', self.wbufsize) + if self.wbufsize == 0: + self.wfile = _SocketWriter(self.connection) + else: + self.wfile = self.connection.makefile('wb', self.wbufsize) def finish(self): if not self.wfile.closed: @@ -756,6 +760,24 @@ class StreamRequestHandler(BaseRequestHandler): self.wfile.close() self.rfile.close() +class _SocketWriter(BufferedIOBase): + """Simple writable BufferedIOBase implementation for a socket + + Does not hold data in a buffer, avoiding any need to call flush().""" + + def __init__(self, sock): + self._sock = sock + + def writable(self): + return True + + def write(self, b): + self._sock.sendall(b) + with memoryview(b) as view: + return view.nbytes + + def fileno(self): + return self._sock.fileno() class DatagramRequestHandler(BaseRequestHandler): diff --git a/Lib/test/test_socketserver.py b/Lib/test/test_socketserver.py index 9a907292aa..3f4dfa1aa7 100644 --- a/Lib/test/test_socketserver.py +++ b/Lib/test/test_socketserver.py @@ -3,6 +3,7 @@ Test suite for socketserver. """ import contextlib +import io import os import select import signal @@ -376,6 +377,84 @@ if HAVE_FORKING: self.active_children.clear() +class SocketWriterTest(unittest.TestCase): + def test_basics(self): + class Handler(socketserver.StreamRequestHandler): + def handle(self): + self.server.wfile = self.wfile + self.server.wfile_fileno = self.wfile.fileno() + self.server.request_fileno = self.request.fileno() + + server = socketserver.TCPServer((HOST, 0), Handler) + self.addCleanup(server.server_close) + s = socket.socket( + server.address_family, socket.SOCK_STREAM, socket.IPPROTO_TCP) + with s: + s.connect(server.server_address) + server.handle_request() + self.assertIsInstance(server.wfile, io.BufferedIOBase) + self.assertEqual(server.wfile_fileno, server.request_fileno) + + @unittest.skipUnless(threading, 'Threading required for this test.') + def test_write(self): + # Test that wfile.write() sends data immediately, and that it does + # not truncate sends when interrupted by a Unix signal + pthread_kill = test.support.get_attribute(signal, 'pthread_kill') + + class Handler(socketserver.StreamRequestHandler): + def handle(self): + self.server.sent1 = self.wfile.write(b'write data\n') + # Should be sent immediately, without requiring flush() + self.server.received = self.rfile.readline() + big_chunk = bytes(test.support.SOCK_MAX_SIZE) + self.server.sent2 = self.wfile.write(big_chunk) + + server = socketserver.TCPServer((HOST, 0), Handler) + self.addCleanup(server.server_close) + interrupted = threading.Event() + + def signal_handler(signum, frame): + interrupted.set() + + original = signal.signal(signal.SIGUSR1, signal_handler) + self.addCleanup(signal.signal, signal.SIGUSR1, original) + response1 = None + received2 = None + main_thread = threading.get_ident() + + def run_client(): + s = socket.socket(server.address_family, socket.SOCK_STREAM, + socket.IPPROTO_TCP) + with s, s.makefile('rb') as reader: + s.connect(server.server_address) + nonlocal response1 + response1 = reader.readline() + s.sendall(b'client response\n') + + reader.read(100) + # The main thread should now be blocking in a send() syscall. + # But in theory, it could get interrupted by other signals, + # and then retried. So keep sending the signal in a loop, in + # case an earlier signal happens to be delivered at an + # inconvenient moment. + while True: + pthread_kill(main_thread, signal.SIGUSR1) + if interrupted.wait(timeout=float(1)): + break + nonlocal received2 + received2 = len(reader.read()) + + background = threading.Thread(target=run_client) + background.start() + server.handle_request() + background.join() + self.assertEqual(server.sent1, len(response1)) + self.assertEqual(response1, b'write data\n') + self.assertEqual(server.received, b'client response\n') + self.assertEqual(server.sent2, test.support.SOCK_MAX_SIZE) + self.assertEqual(received2, test.support.SOCK_MAX_SIZE - 100) + + class MiscTestCase(unittest.TestCase): def test_all(self): diff --git a/Lib/wsgiref/simple_server.py b/Lib/wsgiref/simple_server.py index da74d7b47a..f71563a5ae 100644 --- a/Lib/wsgiref/simple_server.py +++ b/Lib/wsgiref/simple_server.py @@ -11,7 +11,6 @@ module. See also the BaseHTTPServer module docs for other API information. """ from http.server import BaseHTTPRequestHandler, HTTPServer -from io import BufferedWriter import sys import urllib.parse from wsgiref.handlers import SimpleHandler @@ -127,17 +126,11 @@ class WSGIRequestHandler(BaseHTTPRequestHandler): if not self.parse_request(): # An error code has been sent, just exit return - # Avoid passing the raw file object wfile, which can do partial - # writes (Issue 24291) - stdout = BufferedWriter(self.wfile) - try: - handler = ServerHandler( - self.rfile, stdout, self.get_stderr(), self.get_environ() - ) - handler.request_handler = self # backpointer for logging - handler.run(self.server.get_app()) - finally: - stdout.detach() + handler = ServerHandler( + self.rfile, self.wfile, self.get_stderr(), self.get_environ() + ) + handler.request_handler = self # backpointer for logging + handler.run(self.server.get_app()) -- cgit v1.2.1 From b328cd8c2bd46f38c4b7e947520fa77c3f591aeb Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 1 Jul 2016 17:22:31 +0300 Subject: Issue #27007: The fromhex() class methods of bytes and bytearray subclasses now return an instance of corresponding subclass. --- Lib/test/test_bytes.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index 1cedd3a855..9d878caa44 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -1589,7 +1589,32 @@ class SubclassTest: self.assertEqual(type(a), type(b)) self.assertEqual(type(a.y), type(b.y)) - test_fromhex = BaseBytesTest.test_fromhex + def test_fromhex(self): + b = self.type2test.fromhex('1a2B30') + self.assertEqual(b, b'\x1a\x2b\x30') + self.assertIs(type(b), self.type2test) + + class B1(self.basetype): + def __new__(cls, value): + me = self.basetype.__new__(cls, value) + me.foo = 'bar' + return me + + b = B1.fromhex('1a2B30') + self.assertEqual(b, b'\x1a\x2b\x30') + self.assertIs(type(b), B1) + self.assertEqual(b.foo, 'bar') + + class B2(self.basetype): + def __init__(me, *args, **kwargs): + if self.basetype is not bytes: + self.basetype.__init__(me, *args, **kwargs) + me.foo = 'bar' + + b = B2.fromhex('1a2B30') + self.assertEqual(b, b'\x1a\x2b\x30') + self.assertIs(type(b), B2) + self.assertEqual(b.foo, 'bar') class ByteArraySubclass(bytearray): -- cgit v1.2.1 From 1c0be37eb86a60c7107ae2081f1ec2bbf167815a Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Sun, 3 Jul 2016 19:11:13 -0400 Subject: Issue 27437: Add query.ModuleName and use it for file => Load Module. Users can now edit bad entries instead of starting over. --- Lib/idlelib/README.txt | 6 +- Lib/idlelib/editor.py | 58 +++++++------------ Lib/idlelib/idle_test/htest.py | 5 +- Lib/idlelib/idle_test/test_query.py | 110 ++++++++++++++++++++++++++++++------ Lib/idlelib/query.py | 83 +++++++++++++++++++++------ 5 files changed, 185 insertions(+), 77 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/README.txt b/Lib/idlelib/README.txt index e1d682e276..d333b47633 100644 --- a/Lib/idlelib/README.txt +++ b/Lib/idlelib/README.txt @@ -41,7 +41,6 @@ config.py # Load, fetch, and save configuration (nim). configdialog.py # Display user configuration dialogs. config_help.py # Specify help source in configdialog. config_key.py # Change keybindings. -config_sec.py # Spefify user config section name dynoption.py # Define mutable OptionMenu widget (nim). debugobj.py # Define class used in stackviewer. debugobj_r.py # Communicate objects between processes with rpc (nim). @@ -66,6 +65,7 @@ pathbrowser.py # Create path browser window. percolator.py # Manage delegator stack (nim). pyparse.py # Give information on code indentation pyshell.py # Start IDLE, manage shell, complete editor window +query.py # Query user for informtion redirector.py # Intercept widget subcommands (for percolator) (nim). replace.py # Search and replace pattern in text. rpc.py # Commuicate between idle and user processes (nim). @@ -192,8 +192,8 @@ Options Configure IDLE # eEW.config_dialog, configdialog (tabs in the dialog) Font tab # config-main.def - Highlight tab # config_sec, config-highlight.def - Keys tab # config_key, configconfig_secg-keus.def + Highlight tab # query, config-highlight.def + Keys tab # query, config_key, config_keys.def General tab # config_help, config-main.def Extensions tab # config-extensions.def, corresponding .py --- diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index 07a1181c7f..7372ecf2d7 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -14,6 +14,7 @@ import traceback import webbrowser from idlelib.multicall import MultiCallCreator +from idlelib import query from idlelib import windows from idlelib import search from idlelib import grep @@ -573,46 +574,27 @@ class EditorWindow(object): text.see("insert") def open_module(self, event=None): - # XXX Shouldn't this be in IOBinding? + """Get module name from user and open it. + + Return module path or None for calls by open_class_browser + when latter is not invoked in named editor window. + """ + # XXX This, open_class_browser, and open_path_browser + # would fit better in iomenu.IOBinding. try: - name = self.text.get("sel.first", "sel.last") + name = self.text.get("sel.first", "sel.last").strip() except TclError: - name = "" - else: - name = name.strip() - name = tkSimpleDialog.askstring("Module", - "Enter the name of a Python module\n" - "to search on sys.path and open:", - parent=self.text, initialvalue=name) - if name: - name = name.strip() - if not name: - return - # XXX Ought to insert current file's directory in front of path - try: - spec = importlib.util.find_spec(name) - except (ValueError, ImportError) as msg: - tkMessageBox.showerror("Import error", str(msg), parent=self.text) - return - if spec is None: - tkMessageBox.showerror("Import error", "module not found", - parent=self.text) - return - if not isinstance(spec.loader, importlib.abc.SourceLoader): - tkMessageBox.showerror("Import error", "not a source-based module", - parent=self.text) - return - try: - file_path = spec.loader.get_filename(name) - except AttributeError: - tkMessageBox.showerror("Import error", - "loader does not support get_filename", - parent=self.text) - return - if self.flist: - self.flist.open(file_path) - else: - self.io.loadfile(file_path) + name = '' + file_path = query.ModuleName( + self.text, "Open Module", + "Enter the name of a Python module\n" + "to search on sys.path and open:", + name).result + if file_path is not None: + if self.flist: + self.flist.open(file_path) + else: + self.io.loadfile(file_path) return file_path def open_class_browser(self, event=None): diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py index d809d30dd0..71302d03fa 100644 --- a/Lib/idlelib/idle_test/htest.py +++ b/Lib/idlelib/idle_test/htest.py @@ -235,8 +235,9 @@ _percolator_spec = { Query_spec = { 'file': 'query', - 'kwds': {'title':'Query', - 'message':'Enter something', + 'kwds': {'title': 'Query', + 'message': 'Enter something', + 'text0': 'Go', '_htest': True}, 'msg': "Enter with or [Ok]. Print valid entry to Shell\n" "Blank line, after stripping, is ignored\n" diff --git a/Lib/idlelib/idle_test/test_query.py b/Lib/idlelib/idle_test/test_query.py index e9c4bd4029..58873c4998 100644 --- a/Lib/idlelib/idle_test/test_query.py +++ b/Lib/idlelib/idle_test/test_query.py @@ -8,8 +8,8 @@ import unittest from unittest import mock from idlelib.idle_test.mock_tk import Var, Mbox_func from idlelib import query -Query, SectionName = query.Query, query.SectionName +Query = query.Query class Dummy_Query: # Mock for testing the following methods Query entry_ok = Query.entry_ok @@ -23,7 +23,7 @@ class Dummy_Query: self.destroyed = True # entry_ok calls modal messagebox.showerror if entry is not ok. -# Mock showerrer returns, so don't need to click to continue. +# Mock showerrer so don't need to click to continue. orig_showerror = query.showerror showerror = Mbox_func() # Instance has __call__ method. @@ -46,7 +46,7 @@ class QueryTest(unittest.TestCase): dialog = self.dialog Equal = self.assertEqual dialog.entry.set(' ') - Equal(dialog.entry_ok(), '') + Equal(dialog.entry_ok(), None) Equal((dialog.result, dialog.destroyed), (None, False)) Equal(showerror.title, 'Entry Error') self.assertIn('Blank', showerror.message) @@ -74,44 +74,41 @@ class QueryTest(unittest.TestCase): class Dummy_SectionName: - # Mock for testing the following method of Section_Name - entry_ok = SectionName.entry_ok - # Attributes, constant or variable, needed for tests + entry_ok = query.SectionName.entry_ok # Test override. used_names = ['used'] entry = Var() class SectionNameTest(unittest.TestCase): dialog = Dummy_SectionName() - def setUp(self): showerror.title = None - def test_blank_name(self): + def test_blank_section_name(self): dialog = self.dialog Equal = self.assertEqual dialog.entry.set(' ') - Equal(dialog.entry_ok(), '') + Equal(dialog.entry_ok(), None) Equal(showerror.title, 'Name Error') self.assertIn('No', showerror.message) - def test_used_name(self): + def test_used_section_name(self): dialog = self.dialog Equal = self.assertEqual dialog.entry.set('used') - Equal(self.dialog.entry_ok(), '') + Equal(self.dialog.entry_ok(), None) Equal(showerror.title, 'Name Error') self.assertIn('use', showerror.message) - def test_long_name(self): + def test_long_section_name(self): dialog = self.dialog Equal = self.assertEqual dialog.entry.set('good'*8) - Equal(self.dialog.entry_ok(), '') + Equal(self.dialog.entry_ok(), None) Equal(showerror.title, 'Name Error') self.assertIn('too long', showerror.message) - def test_good_entry(self): + def test_good_section_name(self): dialog = self.dialog Equal = self.assertEqual dialog.entry.set(' good ') @@ -119,13 +116,56 @@ class SectionNameTest(unittest.TestCase): Equal(showerror.title, None) +class Dummy_ModuleName: + entry_ok = query.ModuleName.entry_ok # Test override + text0 = '' + entry = Var() + +class ModuleNameTest(unittest.TestCase): + dialog = Dummy_ModuleName() + + def setUp(self): + showerror.title = None + + def test_blank_module_name(self): + dialog = self.dialog + Equal = self.assertEqual + dialog.entry.set(' ') + Equal(dialog.entry_ok(), None) + Equal(showerror.title, 'Name Error') + self.assertIn('No', showerror.message) + + def test_bogus_module_name(self): + dialog = self.dialog + Equal = self.assertEqual + dialog.entry.set('__name_xyz123_should_not_exist__') + Equal(self.dialog.entry_ok(), None) + Equal(showerror.title, 'Import Error') + self.assertIn('not found', showerror.message) + + def test_c_source_name(self): + dialog = self.dialog + Equal = self.assertEqual + dialog.entry.set('itertools') + Equal(self.dialog.entry_ok(), None) + Equal(showerror.title, 'Import Error') + self.assertIn('source-based', showerror.message) + + def test_good_module_name(self): + dialog = self.dialog + Equal = self.assertEqual + dialog.entry.set('idlelib') + self.assertTrue(dialog.entry_ok().endswith('__init__.py')) + Equal(showerror.title, None) + + class QueryGuiTest(unittest.TestCase): @classmethod def setUpClass(cls): requires('gui') - cls.root = Tk() - cls.dialog = Query(cls.root, 'TEST', 'test', _utest=True) + cls.root = root = Tk() + cls.dialog = Query(root, 'TEST', 'test', _utest=True) cls.dialog.destroy = mock.Mock() @classmethod @@ -160,5 +200,43 @@ class QueryGuiTest(unittest.TestCase): self.assertTrue(dialog.destroy.called) +class SectionnameGuiTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + requires('gui') + + def test_click_section_name(self): + root = Tk() + dialog = query.SectionName(root, 'T', 't', {'abc'}, _utest=True) + Equal = self.assertEqual + Equal(dialog.used_names, {'abc'}) + dialog.entry.insert(0, 'okay') + dialog.button_ok.invoke() + Equal(dialog.result, 'okay') + del dialog + root.destroy() + del root + + +class ModulenameGuiTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + requires('gui') + + def test_click_module_name(self): + root = Tk() + dialog = query.ModuleName(root, 'T', 't', 'idlelib', _utest=True) + Equal = self.assertEqual + Equal(dialog.text0, 'idlelib') + Equal(dialog.entry.get(), 'idlelib') + dialog.button_ok.invoke() + self.assertTrue(dialog.result.endswith('__init__.py')) + del dialog + root.destroy() + del root + + if __name__ == '__main__': unittest.main(verbosity=2, exit=False) diff --git a/Lib/idlelib/query.py b/Lib/idlelib/query.py index e3937a1340..fd9716f5d4 100644 --- a/Lib/idlelib/query.py +++ b/Lib/idlelib/query.py @@ -15,7 +15,8 @@ Configdialog uses it for new highlight theme and keybinding set names. # of configSectionNameDialog.py (temporarily config_sec.py) into # generic and specific parts. -from tkinter import FALSE, TRUE, Toplevel +import importlib +from tkinter import Toplevel, StringVar from tkinter.messagebox import showerror from tkinter.ttk import Frame, Button, Entry, Label @@ -24,20 +25,22 @@ class Query(Toplevel): For this base class, accept any non-blank string. """ - def __init__(self, parent, title, message, - *, _htest=False, _utest=False): # Call from override. + def __init__(self, parent, title, message, text0='', + *, _htest=False, _utest=False): """Create popup, do not return until tk widget destroyed. - Additional subclass init must be done before calling this. + Additional subclass init must be done before calling this + unless _utest=True is passed to suppress wait_window(). title - string, title of popup dialog message - string, informational message to display + text0 - initial value for entry _htest - bool, change box location when running htest _utest - bool, leave window hidden and not modal """ Toplevel.__init__(self, parent) self.configure(borderwidth=5) - self.resizable(height=FALSE, width=FALSE) + self.resizable(height=False, width=False) self.title(title) self.transient(parent) self.grab_set() @@ -45,6 +48,7 @@ class Query(Toplevel): self.protocol("WM_DELETE_WINDOW", self.cancel) self.parent = parent self.message = message + self.text0 = text0 self.create_widgets() self.update_idletasks() #needs to be done here so that the winfo_reqwidth is valid @@ -62,31 +66,34 @@ class Query(Toplevel): self.wait_window() def create_widgets(self): # Call from override, if any. + # Bind widgets needed for entry_ok or unittest to self. frame = Frame(self, borderwidth=2, relief='sunken', ) label = Label(frame, anchor='w', justify='left', text=self.message) - self.entry = Entry(frame, width=30) # Bind name for entry_ok. + self.entryvar = StringVar(self, self.text0) + self.entry = Entry(frame, width=30, textvariable=self.entryvar) self.entry.focus_set() - buttons = Frame(self) # Bind buttons for invoke in unittest. + buttons = Frame(self) self.button_ok = Button(buttons, text='Ok', width=8, command=self.ok) self.button_cancel = Button(buttons, text='Cancel', width=8, command=self.cancel) - frame.pack(side='top', expand=TRUE, fill='both') + frame.pack(side='top', expand=True, fill='both') label.pack(padx=5, pady=5) self.entry.pack(padx=5, pady=5) buttons.pack(side='bottom') self.button_ok.pack(side='left', padx=5) self.button_cancel.pack(side='right', padx=5) - def entry_ok(self): # Usually replace. - "Check that entry not blank." + def entry_ok(self): # Example: usually replace. + "Return non-blank entry or None." entry = self.entry.get().strip() if not entry: showerror(title='Entry Error', message='Blank line.', parent=self) + return return entry def ok(self, event=None): # Do not replace. @@ -95,7 +102,7 @@ class Query(Toplevel): Otherwise leave dialog open for user to correct entry or cancel. ''' entry = self.entry_ok() - if entry: + if entry is not None: self.result = entry self.destroy() else: @@ -114,32 +121,72 @@ class SectionName(Query): def __init__(self, parent, title, message, used_names, *, _htest=False, _utest=False): "used_names - collection of strings already in use" - self.used_names = used_names Query.__init__(self, parent, title, message, _htest=_htest, _utest=_utest) - # This call does ot return until tk widget is destroyed. def entry_ok(self): - '''Stripping entered name, check that it is a sensible - ConfigParser file section name. Return it if it is, '' if not. - ''' + "Return sensible ConfigParser section name or None." name = self.entry.get().strip() if not name: showerror(title='Name Error', message='No name specified.', parent=self) + return elif len(name)>30: showerror(title='Name Error', message='Name too long. It should be no more than '+ '30 characters.', parent=self) - name = '' + return elif name in self.used_names: showerror(title='Name Error', message='This name is already in use.', parent=self) - name = '' + return return name +class ModuleName(Query): + "Get a module name for Open Module menu entry." + # Used in open_module (editor.EditorWindow until move to iobinding). + + def __init__(self, parent, title, message, text0='', + *, _htest=False, _utest=False): + """text0 - name selected in text before Open Module invoked" + """ + Query.__init__(self, parent, title, message, text0=text0, + _htest=_htest, _utest=_utest) + + def entry_ok(self): + "Return entered module name as file path or None." + # Moved here from Editor_Window.load_module 2016 July. + name = self.entry.get().strip() + if not name: + showerror(title='Name Error', + message='No name specified.', parent=self) + return + # XXX Ought to insert current file's directory in front of path + try: + spec = importlib.util.find_spec(name) + except (ValueError, ImportError) as msg: + showerror("Import Error", str(msg), parent=self) + return + if spec is None: + showerror("Import Error", "module not found", + parent=self) + return + if not isinstance(spec.loader, importlib.abc.SourceLoader): + showerror("Import Error", "not a source-based module", + parent=self) + return + try: + file_path = spec.loader.get_filename(name) + except AttributeError: + showerror("Import Error", + "loader does not support get_filename", + parent=self) + return + return file_path + + if __name__ == '__main__': import unittest unittest.main('idlelib.idle_test.test_query', verbosity=2, exit=False) -- cgit v1.2.1 From 302dcd33efa4772d89f462710795a4e714040b9f Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Tue, 5 Jul 2016 21:51:56 -0400 Subject: Issue #27452: make command line idle-test> python test_help.py work. __file__ is relative in this case. --- Lib/idlelib/idle_test/test_help.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/idlelib/idle_test/test_help.py b/Lib/idlelib/idle_test/test_help.py index cdded2ac79..2c68e23b32 100644 --- a/Lib/idlelib/idle_test/test_help.py +++ b/Lib/idlelib/idle_test/test_help.py @@ -16,7 +16,7 @@ class HelpFrameTest(unittest.TestCase): "By itself, this tests that file parsed without exception." cls.root = root = Tk() root.withdraw() - helpfile = join(abspath(dirname(dirname(__file__))), 'help.html') + helpfile = join(dirname(dirname(abspath(__file__))), 'help.html') cls.frame = help.HelpFrame(root, helpfile) @classmethod -- cgit v1.2.1 From 2e4aad28c53af5c646f39e9aa581c9ff7e83a0c4 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 6 Jul 2016 12:21:58 +0300 Subject: Issue #21708: Deprecated dbm.dumb behavior that differs from common dbm behavior: creating a database in 'r' and 'w' modes and modifying a database in 'r' mode. --- Lib/dbm/dumb.py | 18 ++++++++++++++++++ Lib/test/test_dbm_dumb.py | 27 ++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/dbm/dumb.py b/Lib/dbm/dumb.py index 7777a7cae6..e7c6440ee6 100644 --- a/Lib/dbm/dumb.py +++ b/Lib/dbm/dumb.py @@ -47,6 +47,7 @@ class _Database(collections.MutableMapping): def __init__(self, filebasename, mode, flag='c'): self._mode = mode + self._readonly = (flag == 'r') # The directory file is a text file. Each line looks like # "%r, (%d, %d)\n" % (key, pos, siz) @@ -80,6 +81,11 @@ class _Database(collections.MutableMapping): try: f = _io.open(self._datfile, 'r', encoding="Latin-1") except OSError: + if flag not in ('c', 'n'): + import warnings + warnings.warn("The database file is missing, the " + "semantics of the 'c' flag will be used.", + DeprecationWarning, stacklevel=4) with _io.open(self._datfile, 'w', encoding="Latin-1") as f: self._chmod(self._datfile) else: @@ -178,6 +184,10 @@ class _Database(collections.MutableMapping): f.write("%r, %r\n" % (key.decode("Latin-1"), pos_and_siz_pair)) def __setitem__(self, key, val): + if self._readonly: + import warnings + warnings.warn('The database is opened for reading only', + DeprecationWarning, stacklevel=2) if isinstance(key, str): key = key.encode('utf-8') elif not isinstance(key, (bytes, bytearray)): @@ -212,6 +222,10 @@ class _Database(collections.MutableMapping): # (so that _commit() never gets called). def __delitem__(self, key): + if self._readonly: + import warnings + warnings.warn('The database is opened for reading only', + DeprecationWarning, stacklevel=2) if isinstance(key, str): key = key.encode('utf-8') self._verify_open() @@ -300,4 +314,8 @@ def open(file, flag='c', mode=0o666): else: # Turn off any bits that are set in the umask mode = mode & (~um) + if flag not in ('r', 'w', 'c', 'n'): + import warnings + warnings.warn("Flag must be one of 'r', 'w', 'c', or 'n'", + DeprecationWarning, stacklevel=2) return _Database(file, mode, flag=flag) diff --git a/Lib/test/test_dbm_dumb.py b/Lib/test/test_dbm_dumb.py index ff63c88c0b..2d77f078b7 100644 --- a/Lib/test/test_dbm_dumb.py +++ b/Lib/test/test_dbm_dumb.py @@ -6,6 +6,7 @@ import io import operator import os import unittest +import warnings import dbm.dumb as dumbdbm from test import support from functools import partial @@ -78,6 +79,12 @@ class DumbDBMTestCase(unittest.TestCase): self.init_db() f = dumbdbm.open(_fname, 'r') self.read_helper(f) + with self.assertWarnsRegex(DeprecationWarning, + 'The database is opened for reading only'): + f[b'g'] = b'x' + with self.assertWarnsRegex(DeprecationWarning, + 'The database is opened for reading only'): + del f[b'a'] f.close() def test_dumbdbm_keys(self): @@ -148,7 +155,7 @@ class DumbDBMTestCase(unittest.TestCase): self.assertEqual(self._dict[key], f[key]) def init_db(self): - f = dumbdbm.open(_fname, 'w') + f = dumbdbm.open(_fname, 'n') for k in self._dict: f[k] = self._dict[k] f.close() @@ -234,6 +241,24 @@ class DumbDBMTestCase(unittest.TestCase): pass self.assertEqual(stdout.getvalue(), '') + def test_warn_on_ignored_flags(self): + for value in ('r', 'w'): + _delete_files() + with self.assertWarnsRegex(DeprecationWarning, + "The database file is missing, the " + "semantics of the 'c' flag will " + "be used."): + f = dumbdbm.open(_fname, value) + f.close() + + def test_invalid_flag(self): + for flag in ('x', 'rf', None): + with self.assertWarnsRegex(DeprecationWarning, + "Flag must be one of " + "'r', 'w', 'c', or 'n'"): + f = dumbdbm.open(_fname, flag) + f.close() + def tearDown(self): _delete_files() -- cgit v1.2.1 From d481822e79d2d920e101a00ab184829b3dac7f57 Mon Sep 17 00:00:00 2001 From: Steven D'Aprano Date: Fri, 8 Jul 2016 02:38:45 +1000 Subject: Issue27139 patch by Julio C Cardoza. --- Lib/test/test_statistics.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_statistics.py b/Lib/test/test_statistics.py index 5dd50483f2..4e03d983d3 100644 --- a/Lib/test/test_statistics.py +++ b/Lib/test/test_statistics.py @@ -1600,6 +1600,22 @@ class TestMedianGrouped(TestMedian): data = [220, 220, 240, 260, 260, 260, 260, 280, 280, 300, 320, 340] self.assertEqual(self.func(data, 20), 265.0) + def test_data_type_error(self): + # Test median_grouped with str, bytes data types for data and interval + data = ["", "", ""] + self.assertRaises(TypeError, self.func, data) + #--- + data = [b"", b"", b""] + self.assertRaises(TypeError, self.func, data) + #--- + data = [1, 2, 3] + interval = "" + self.assertRaises(TypeError, self.func, data, interval) + #--- + data = [1, 2, 3] + interval = b"" + self.assertRaises(TypeError, self.func, data, interval) + class TestMode(NumericTestCase, AverageMixin, UnivariateTypeMixin): # Test cases for the discrete version of mode. -- cgit v1.2.1 From 61e4ac554cf110c33ad4a3c19734724c1199db06 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 8 Jul 2016 00:22:50 -0400 Subject: Issue #27380: IDLE: add query.HelpSource class and tests. Remove modules that are combined in new module. --- Lib/idlelib/config_help.py | 170 ------------------------ Lib/idlelib/configdialog.py | 27 ++-- Lib/idlelib/idle_test/htest.py | 30 +++-- Lib/idlelib/idle_test/test_config_help.py | 106 --------------- Lib/idlelib/idle_test/test_query.py | 214 +++++++++++++++++++++++++----- Lib/idlelib/query.py | 163 ++++++++++++++++++----- 6 files changed, 342 insertions(+), 368 deletions(-) delete mode 100644 Lib/idlelib/config_help.py delete mode 100644 Lib/idlelib/idle_test/test_config_help.py (limited to 'Lib') diff --git a/Lib/idlelib/config_help.py b/Lib/idlelib/config_help.py deleted file mode 100644 index cde8118fe6..0000000000 --- a/Lib/idlelib/config_help.py +++ /dev/null @@ -1,170 +0,0 @@ -"Dialog to specify or edit the parameters for a user configured help source." - -import os -import sys - -from tkinter import * -import tkinter.messagebox as tkMessageBox -import tkinter.filedialog as tkFileDialog - -class GetHelpSourceDialog(Toplevel): - def __init__(self, parent, title, menuItem='', filePath='', _htest=False): - """Get menu entry and url/ local file location for Additional Help - - User selects a name for the Help resource and provides a web url - or a local file as its source. The user can enter a url or browse - for the file. - - _htest - bool, change box location when running htest - """ - Toplevel.__init__(self, parent) - self.configure(borderwidth=5) - self.resizable(height=FALSE, width=FALSE) - self.title(title) - self.transient(parent) - self.grab_set() - self.protocol("WM_DELETE_WINDOW", self.cancel) - self.parent = parent - self.result = None - self.create_widgets() - self.menu.set(menuItem) - self.path.set(filePath) - self.withdraw() #hide while setting geometry - #needs to be done here so that the winfo_reqwidth is valid - self.update_idletasks() - #centre dialog over parent. below parent if running htest. - self.geometry( - "+%d+%d" % ( - parent.winfo_rootx() + - (parent.winfo_width()/2 - self.winfo_reqwidth()/2), - parent.winfo_rooty() + - ((parent.winfo_height()/2 - self.winfo_reqheight()/2) - if not _htest else 150))) - self.deiconify() #geometry set, unhide - self.bind('', self.ok) - self.wait_window() - - def create_widgets(self): - self.menu = StringVar(self) - self.path = StringVar(self) - self.fontSize = StringVar(self) - self.frameMain = Frame(self, borderwidth=2, relief=GROOVE) - self.frameMain.pack(side=TOP, expand=TRUE, fill=BOTH) - labelMenu = Label(self.frameMain, anchor=W, justify=LEFT, - text='Menu Item:') - self.entryMenu = Entry(self.frameMain, textvariable=self.menu, - width=30) - self.entryMenu.focus_set() - labelPath = Label(self.frameMain, anchor=W, justify=LEFT, - text='Help File Path: Enter URL or browse for file') - self.entryPath = Entry(self.frameMain, textvariable=self.path, - width=40) - self.entryMenu.focus_set() - labelMenu.pack(anchor=W, padx=5, pady=3) - self.entryMenu.pack(anchor=W, padx=5, pady=3) - labelPath.pack(anchor=W, padx=5, pady=3) - self.entryPath.pack(anchor=W, padx=5, pady=3) - browseButton = Button(self.frameMain, text='Browse', width=8, - command=self.browse_file) - browseButton.pack(pady=3) - frameButtons = Frame(self) - frameButtons.pack(side=BOTTOM, fill=X) - self.buttonOk = Button(frameButtons, text='OK', - width=8, default=ACTIVE, command=self.ok) - self.buttonOk.grid(row=0, column=0, padx=5,pady=5) - self.buttonCancel = Button(frameButtons, text='Cancel', - width=8, command=self.cancel) - self.buttonCancel.grid(row=0, column=1, padx=5, pady=5) - - def browse_file(self): - filetypes = [ - ("HTML Files", "*.htm *.html", "TEXT"), - ("PDF Files", "*.pdf", "TEXT"), - ("Windows Help Files", "*.chm"), - ("Text Files", "*.txt", "TEXT"), - ("All Files", "*")] - path = self.path.get() - if path: - dir, base = os.path.split(path) - else: - base = None - if sys.platform[:3] == 'win': - dir = os.path.join(os.path.dirname(sys.executable), 'Doc') - if not os.path.isdir(dir): - dir = os.getcwd() - else: - dir = os.getcwd() - opendialog = tkFileDialog.Open(parent=self, filetypes=filetypes) - file = opendialog.show(initialdir=dir, initialfile=base) - if file: - self.path.set(file) - - def menu_ok(self): - "Simple validity check for a sensible menu item name" - menu_ok = True - menu = self.menu.get() - menu.strip() - if not menu: - tkMessageBox.showerror(title='Menu Item Error', - message='No menu item specified', - parent=self) - self.entryMenu.focus_set() - menu_ok = False - elif len(menu) > 30: - tkMessageBox.showerror(title='Menu Item Error', - message='Menu item too long:' - '\nLimit 30 characters.', - parent=self) - self.entryMenu.focus_set() - menu_ok = False - return menu_ok - - def path_ok(self): - "Simple validity check for menu file path" - path_ok = True - path = self.path.get() - path.strip() - if not path: #no path specified - tkMessageBox.showerror(title='File Path Error', - message='No help file path specified.', - parent=self) - self.entryPath.focus_set() - path_ok = False - elif path.startswith(('www.', 'http')): - pass - else: - if path[:5] == 'file:': - path = path[5:] - if not os.path.exists(path): - tkMessageBox.showerror(title='File Path Error', - message='Help file path does not exist.', - parent=self) - self.entryPath.focus_set() - path_ok = False - return path_ok - - def ok(self, event=None): - if self.menu_ok() and self.path_ok(): - self.result = (self.menu.get().strip(), - self.path.get().strip()) - if sys.platform == 'darwin': - path = self.result[1] - if path.startswith(('www', 'file:', 'http:', 'https:')): - pass - else: - # Mac Safari insists on using the URI form for local files - self.result = list(self.result) - self.result[1] = "file://" + path - self.destroy() - - def cancel(self, event=None): - self.result = None - self.destroy() - -if __name__ == '__main__': - import unittest - unittest.main('idlelib.idle_test.test_config_help', - verbosity=2, exit=False) - - from idlelib.idle_test.htest import run - run(GetHelpSourceDialog) diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 6629d70ec6..388b48f088 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -18,8 +18,7 @@ import tkinter.font as tkFont from idlelib.config import idleConf from idlelib.dynoption import DynOptionMenu from idlelib.config_key import GetKeysDialog -from idlelib.query import SectionName -from idlelib.config_help import GetHelpSourceDialog +from idlelib.query import SectionName, HelpSource from idlelib.tabbedpages import TabbedPageSet from idlelib.textview import view_text from idlelib import macosx @@ -940,7 +939,8 @@ class ConfigDialog(Toplevel): self.buttonHelpListRemove.config(state=DISABLED) def HelpListItemAdd(self): - helpSource = GetHelpSourceDialog(self, 'New Help Source').result + helpSource = HelpSource(self, 'New Help Source', + ).result if helpSource: self.userHelpList.append((helpSource[0], helpSource[1])) self.listHelp.insert(END, helpSource[0]) @@ -950,16 +950,17 @@ class ConfigDialog(Toplevel): def HelpListItemEdit(self): itemIndex = self.listHelp.index(ANCHOR) helpSource = self.userHelpList[itemIndex] - newHelpSource = GetHelpSourceDialog( - self, 'Edit Help Source', menuItem=helpSource[0], - filePath=helpSource[1]).result - if (not newHelpSource) or (newHelpSource == helpSource): - return #no changes - self.userHelpList[itemIndex] = newHelpSource - self.listHelp.delete(itemIndex) - self.listHelp.insert(itemIndex, newHelpSource[0]) - self.UpdateUserHelpChangedItems() - self.SetHelpListButtonStates() + newHelpSource = HelpSource( + self, 'Edit Help Source', + menuitem=helpSource[0], + filepath=helpSource[1], + ).result + if newHelpSource and newHelpSource != helpSource: + self.userHelpList[itemIndex] = newHelpSource + self.listHelp.delete(itemIndex) + self.listHelp.insert(itemIndex, newHelpSource[0]) + self.UpdateUserHelpChangedItems() + self.SetHelpListButtonStates() def HelpListItemRemove(self): itemIndex = self.listHelp.index(ANCHOR) diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py index 71302d03fa..f5311e966c 100644 --- a/Lib/idlelib/idle_test/htest.py +++ b/Lib/idlelib/idle_test/htest.py @@ -137,18 +137,6 @@ _editor_window_spec = { "Best to close editor first." } -GetHelpSourceDialog_spec = { - 'file': 'config_help', - 'kwds': {'title': 'Get helpsource', - '_htest': True}, - 'msg': "Enter menu item name and help file path\n " - " and more than 30 chars are invalid menu item names.\n" - ", file does not exist are invalid path items.\n" - "Test for incomplete web address for help file path.\n" - "A valid entry will be printed to shell with [0k].\n" - "[Cancel] will print None to shell", - } - # Update once issue21519 is resolved. GetKeysDialog_spec = { 'file': 'config_key', @@ -175,6 +163,22 @@ _grep_dialog_spec = { "should open that file \nin a new EditorWindow." } +HelpSource_spec = { + 'file': 'query', + 'kwds': {'title': 'Help name and source', + 'menuitem': 'test', + 'filepath': __file__, + 'used_names': {'abc'}, + '_htest': True}, + 'msg': "Enter menu item name and help file path\n" + "'', > than 30 chars, and 'abc' are invalid menu item names.\n" + "'' and file does not exist are invalid path items.\n" + "Any url ('www...', 'http...') is accepted.\n" + "Test Browse with and without path, as cannot unittest.\n" + "A valid entry will be printed to shell with [0k]\n" + "or . [Cancel] will print None to shell" + } + _io_binding_spec = { 'file': 'iomenu', 'kwds': {}, @@ -241,7 +245,7 @@ Query_spec = { '_htest': True}, 'msg': "Enter with or [Ok]. Print valid entry to Shell\n" "Blank line, after stripping, is ignored\n" - "Close dialog with valid entry, [Cancel] or [X]", + "Close dialog with valid entry, [Cancel] or [X]" } diff --git a/Lib/idlelib/idle_test/test_config_help.py b/Lib/idlelib/idle_test/test_config_help.py deleted file mode 100644 index b89b4e3ca1..0000000000 --- a/Lib/idlelib/idle_test/test_config_help.py +++ /dev/null @@ -1,106 +0,0 @@ -"""Unittests for idlelib.config_help.py""" -import unittest -from idlelib.idle_test.mock_tk import Var, Mbox, Entry -from idlelib import config_help as help_dialog_module - -help_dialog = help_dialog_module.GetHelpSourceDialog - - -class Dummy_help_dialog: - # Mock for testing the following methods of help_dialog - menu_ok = help_dialog.menu_ok - path_ok = help_dialog.path_ok - ok = help_dialog.ok - cancel = help_dialog.cancel - # Attributes, constant or variable, needed for tests - menu = Var() - entryMenu = Entry() - path = Var() - entryPath = Entry() - result = None - destroyed = False - - def destroy(self): - self.destroyed = True - - -# menu_ok and path_ok call Mbox.showerror if menu and path are not ok. -orig_mbox = help_dialog_module.tkMessageBox -showerror = Mbox.showerror - - -class ConfigHelpTest(unittest.TestCase): - dialog = Dummy_help_dialog() - - @classmethod - def setUpClass(cls): - help_dialog_module.tkMessageBox = Mbox - - @classmethod - def tearDownClass(cls): - help_dialog_module.tkMessageBox = orig_mbox - - def test_blank_menu(self): - self.dialog.menu.set('') - self.assertFalse(self.dialog.menu_ok()) - self.assertEqual(showerror.title, 'Menu Item Error') - self.assertIn('No', showerror.message) - - def test_long_menu(self): - self.dialog.menu.set('hello' * 10) - self.assertFalse(self.dialog.menu_ok()) - self.assertEqual(showerror.title, 'Menu Item Error') - self.assertIn('long', showerror.message) - - def test_good_menu(self): - self.dialog.menu.set('help') - showerror.title = 'No Error' # should not be called - self.assertTrue(self.dialog.menu_ok()) - self.assertEqual(showerror.title, 'No Error') - - def test_blank_path(self): - self.dialog.path.set('') - self.assertFalse(self.dialog.path_ok()) - self.assertEqual(showerror.title, 'File Path Error') - self.assertIn('No', showerror.message) - - def test_invalid_file_path(self): - self.dialog.path.set('foobar' * 100) - self.assertFalse(self.dialog.path_ok()) - self.assertEqual(showerror.title, 'File Path Error') - self.assertIn('not exist', showerror.message) - - def test_invalid_url_path(self): - self.dialog.path.set('ww.foobar.com') - self.assertFalse(self.dialog.path_ok()) - self.assertEqual(showerror.title, 'File Path Error') - self.assertIn('not exist', showerror.message) - - self.dialog.path.set('htt.foobar.com') - self.assertFalse(self.dialog.path_ok()) - self.assertEqual(showerror.title, 'File Path Error') - self.assertIn('not exist', showerror.message) - - def test_good_path(self): - self.dialog.path.set('https://docs.python.org') - showerror.title = 'No Error' # should not be called - self.assertTrue(self.dialog.path_ok()) - self.assertEqual(showerror.title, 'No Error') - - def test_ok(self): - self.dialog.destroyed = False - self.dialog.menu.set('help') - self.dialog.path.set('https://docs.python.org') - self.dialog.ok() - self.assertEqual(self.dialog.result, ('help', - 'https://docs.python.org')) - self.assertTrue(self.dialog.destroyed) - - def test_cancel(self): - self.dialog.destroyed = False - self.dialog.cancel() - self.assertEqual(self.dialog.result, None) - self.assertTrue(self.dialog.destroyed) - -if __name__ == '__main__': - unittest.main(verbosity=2, exit=False) diff --git a/Lib/idlelib/idle_test/test_query.py b/Lib/idlelib/idle_test/test_query.py index 58873c4998..45c99fac24 100644 --- a/Lib/idlelib/idle_test/test_query.py +++ b/Lib/idlelib/idle_test/test_query.py @@ -1,6 +1,16 @@ """Test idlelib.query. -Coverage: 100%. +Non-gui tests for Query, SectionName, ModuleName, and HelpSource use +dummy versions that extract the non-gui methods and add other needed +attributes. GUI tests create an instance of each class and simulate +entries and button clicks. Subclass tests only target the new code in +the subclass definition. + +The appearance of the widgets is checked by the Query and +HelpSource htests. These are run by running query.py. + +Coverage: 94% (100% for Query and SectionName). +6 of 8 missing are ModuleName exceptions I don't know how to trigger. """ from test.support import requires from tkinter import Tk @@ -9,21 +19,9 @@ from unittest import mock from idlelib.idle_test.mock_tk import Var, Mbox_func from idlelib import query -Query = query.Query -class Dummy_Query: - # Mock for testing the following methods Query - entry_ok = Query.entry_ok - ok = Query.ok - cancel = Query.cancel - # Attributes, constant or variable, needed for tests - entry = Var() - result = None - destroyed = False - def destroy(self): - self.destroyed = True - -# entry_ok calls modal messagebox.showerror if entry is not ok. -# Mock showerrer so don't need to click to continue. +# Mock entry.showerror messagebox so don't need click to continue +# when entry_ok and path_ok methods call it to display errors. + orig_showerror = query.showerror showerror = Mbox_func() # Instance has __call__ method. @@ -34,7 +32,23 @@ def tearDownModule(): query.showerror = orig_showerror +# NON-GUI TESTS + class QueryTest(unittest.TestCase): + "Test Query base class." + + class Dummy_Query: + # Test the following Query methods. + entry_ok = query.Query.entry_ok + ok = query.Query.ok + cancel = query.Query.cancel + # Add attributes needed for the tests. + entry = Var() + result = None + destroyed = False + def destroy(self): + self.destroyed = True + dialog = Dummy_Query() def setUp(self): @@ -42,7 +56,7 @@ class QueryTest(unittest.TestCase): self.dialog.result = None self.dialog.destroyed = False - def test_blank_entry(self): + def test_entry_ok_blank(self): dialog = self.dialog Equal = self.assertEqual dialog.entry.set(' ') @@ -51,7 +65,7 @@ class QueryTest(unittest.TestCase): Equal(showerror.title, 'Entry Error') self.assertIn('Blank', showerror.message) - def test_good_entry(self): + def test_entry_ok_good(self): dialog = self.dialog Equal = self.assertEqual dialog.entry.set(' good ') @@ -59,7 +73,17 @@ class QueryTest(unittest.TestCase): Equal((dialog.result, dialog.destroyed), (None, False)) Equal(showerror.title, None) - def test_ok(self): + def test_ok_blank(self): + dialog = self.dialog + Equal = self.assertEqual + dialog.entry.set('') + dialog.entry.focus_set = mock.Mock() + Equal(dialog.ok(), None) + self.assertTrue(dialog.entry.focus_set.called) + del dialog.entry.focus_set + Equal((dialog.result, dialog.destroyed), (None, False)) + + def test_ok_good(self): dialog = self.dialog Equal = self.assertEqual dialog.entry.set('good') @@ -73,12 +97,14 @@ class QueryTest(unittest.TestCase): Equal((dialog.result, dialog.destroyed), (None, True)) -class Dummy_SectionName: - entry_ok = query.SectionName.entry_ok # Test override. - used_names = ['used'] - entry = Var() - class SectionNameTest(unittest.TestCase): + "Test SectionName subclass of Query." + + class Dummy_SectionName: + entry_ok = query.SectionName.entry_ok # Function being tested. + used_names = ['used'] + entry = Var() + dialog = Dummy_SectionName() def setUp(self): @@ -116,12 +142,14 @@ class SectionNameTest(unittest.TestCase): Equal(showerror.title, None) -class Dummy_ModuleName: - entry_ok = query.ModuleName.entry_ok # Test override - text0 = '' - entry = Var() - class ModuleNameTest(unittest.TestCase): + "Test ModuleName subclass of Query." + + class Dummy_ModuleName: + entry_ok = query.ModuleName.entry_ok # Funtion being tested. + text0 = '' + entry = Var() + dialog = Dummy_ModuleName() def setUp(self): @@ -159,13 +187,119 @@ class ModuleNameTest(unittest.TestCase): Equal(showerror.title, None) +# 3 HelpSource test classes each test one function. + +orig_platform = query.platform + +class HelpsourceBrowsefileTest(unittest.TestCase): + "Test browse_file method of ModuleName subclass of Query." + + class Dummy_HelpSource: + browse_file = query.HelpSource.browse_file + pathvar = Var() + + dialog = Dummy_HelpSource() + + def test_file_replaces_path(self): + # Path is widget entry, file is file dialog return. + dialog = self.dialog + for path, func, result in ( + # We need all combination to test all (most) code paths. + ('', lambda a,b,c:'', ''), + ('', lambda a,b,c: __file__, __file__), + ('htest', lambda a,b,c:'', 'htest'), + ('htest', lambda a,b,c: __file__, __file__)): + with self.subTest(): + dialog.pathvar.set(path) + dialog.askfilename = func + dialog.browse_file() + self.assertEqual(dialog.pathvar.get(), result) + + +class HelpsourcePathokTest(unittest.TestCase): + "Test path_ok method of ModuleName subclass of Query." + + class Dummy_HelpSource: + path_ok = query.HelpSource.path_ok + path = Var() + + dialog = Dummy_HelpSource() + + @classmethod + def tearDownClass(cls): + query.platform = orig_platform + + def setUp(self): + showerror.title = None + + def test_path_ok_blank(self): + dialog = self.dialog + Equal = self.assertEqual + dialog.path.set(' ') + Equal(dialog.path_ok(), None) + Equal(showerror.title, 'File Path Error') + self.assertIn('No help', showerror.message) + + def test_path_ok_bad(self): + dialog = self.dialog + Equal = self.assertEqual + dialog.path.set(__file__ + 'bad-bad-bad') + Equal(dialog.path_ok(), None) + Equal(showerror.title, 'File Path Error') + self.assertIn('not exist', showerror.message) + + def test_path_ok_web(self): + dialog = self.dialog + Equal = self.assertEqual + for url in 'www.py.org', 'http://py.org': + with self.subTest(): + dialog.path.set(url) + Equal(dialog.path_ok(), url) + Equal(showerror.title, None) + + def test_path_ok_file(self): + dialog = self.dialog + Equal = self.assertEqual + for platform, prefix in ('darwin', 'file://'), ('other', ''): + with self.subTest(): + query.platform = platform + dialog.path.set(__file__) + Equal(dialog.path_ok(), prefix + __file__) + Equal(showerror.title, None) + + +class HelpsourceEntryokTest(unittest.TestCase): + "Test entry_ok method of ModuleName subclass of Query." + + class Dummy_HelpSource: + entry_ok = query.HelpSource.entry_ok + def item_ok(self): + return self.name + def path_ok(self): + return self.path + + dialog = Dummy_HelpSource() + + def test_entry_ok_helpsource(self): + dialog = self.dialog + for name, path, result in ((None, None, None), + (None, 'doc.txt', None), + ('doc', None, None), + ('doc', 'doc.txt', ('doc', 'doc.txt'))): + with self.subTest(): + dialog.name, dialog.path = name, path + self.assertEqual(self.dialog.entry_ok(), result) + + +# GUI TESTS + class QueryGuiTest(unittest.TestCase): @classmethod def setUpClass(cls): requires('gui') cls.root = root = Tk() - cls.dialog = Query(root, 'TEST', 'test', _utest=True) + cls.dialog = query.Query(root, 'TEST', 'test', _utest=True) cls.dialog.destroy = mock.Mock() @classmethod @@ -238,5 +372,25 @@ class ModulenameGuiTest(unittest.TestCase): del root +class HelpsourceGuiTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + requires('gui') + + def test_click_help_source(self): + root = Tk() + dialog = query.HelpSource(root, 'T', menuitem='__test__', + filepath=__file__, _utest=True) + Equal = self.assertEqual + Equal(dialog.entry.get(), '__test__') + Equal(dialog.path.get(), __file__) + dialog.button_ok.invoke() + Equal(dialog.result, ('__test__', __file__)) + del dialog + root.destroy() + del root + + if __name__ == '__main__': unittest.main(verbosity=2, exit=False) diff --git a/Lib/idlelib/query.py b/Lib/idlelib/query.py index fd9716f5d4..d2d1472a0e 100644 --- a/Lib/idlelib/query.py +++ b/Lib/idlelib/query.py @@ -13,10 +13,16 @@ Configdialog uses it for new highlight theme and keybinding set names. """ # Query and Section name result from splitting GetCfgSectionNameDialog # of configSectionNameDialog.py (temporarily config_sec.py) into -# generic and specific parts. +# generic and specific parts. 3.6 only, July 2016. +# ModuleName.entry_ok came from editor.EditorWindow.load_module. +# HelpSource was extracted from configHelpSourceEdit.py (temporarily +# config_help.py), with darwin code moved from ok to path_ok. import importlib +import os +from sys import executable, platform # Platform is set for one test. from tkinter import Toplevel, StringVar +from tkinter import filedialog from tkinter.messagebox import showerror from tkinter.ttk import Frame, Button, Entry, Label @@ -25,8 +31,8 @@ class Query(Toplevel): For this base class, accept any non-blank string. """ - def __init__(self, parent, title, message, text0='', - *, _htest=False, _utest=False): + def __init__(self, parent, title, message, *, text0='', used_names={}, + _htest=False, _utest=False): """Create popup, do not return until tk widget destroyed. Additional subclass init must be done before calling this @@ -35,10 +41,12 @@ class Query(Toplevel): title - string, title of popup dialog message - string, informational message to display text0 - initial value for entry + used_names - names already in use _htest - bool, change box location when running htest _utest - bool, leave window hidden and not modal """ Toplevel.__init__(self, parent) + self.withdraw() # Hide while configuring, especially geometry. self.configure(borderwidth=5) self.resizable(height=False, width=False) self.title(title) @@ -49,27 +57,26 @@ class Query(Toplevel): self.parent = parent self.message = message self.text0 = text0 + self.used_names = used_names self.create_widgets() - self.update_idletasks() - #needs to be done here so that the winfo_reqwidth is valid - self.withdraw() # Hide while configuring, especially geometry. - self.geometry( + self.update_idletasks() # Needed here for winfo_reqwidth below. + self.geometry( # Center dialog over parent (or below htest box). "+%d+%d" % ( parent.winfo_rootx() + (parent.winfo_width()/2 - self.winfo_reqwidth()/2), parent.winfo_rooty() + ((parent.winfo_height()/2 - self.winfo_reqheight()/2) if not _htest else 150) - ) ) #centre dialog over parent (or below htest box) + ) ) if not _utest: - self.deiconify() #geometry set, unhide + self.deiconify() # Unhide now that geometry set. self.wait_window() def create_widgets(self): # Call from override, if any. - # Bind widgets needed for entry_ok or unittest to self. - frame = Frame(self, borderwidth=2, relief='sunken', ) - label = Label(frame, anchor='w', justify='left', - text=self.message) + # Bind to self widgets needed for entry_ok or unittest. + self.frame = frame = Frame(self, borderwidth=2, relief='sunken', ) + entrylabel = Label(frame, anchor='w', justify='left', + text=self.message) self.entryvar = StringVar(self, self.text0) self.entry = Entry(frame, width=30, textvariable=self.entryvar) self.entry.focus_set() @@ -81,7 +88,7 @@ class Query(Toplevel): width=8, command=self.cancel) frame.pack(side='top', expand=True, fill='both') - label.pack(padx=5, pady=5) + entrylabel.pack(padx=5, pady=5) self.entry.pack(padx=5, pady=5) buttons.pack(side='bottom') self.button_ok.pack(side='left', padx=5) @@ -93,7 +100,7 @@ class Query(Toplevel): if not entry: showerror(title='Entry Error', message='Blank line.', parent=self) - return + return None return entry def ok(self, event=None): # Do not replace. @@ -106,7 +113,7 @@ class Query(Toplevel): self.result = entry self.destroy() else: - # [Ok] (but not ) moves focus. Move it back. + # [Ok] moves focus. ( does not.) Move it back. self.entry.focus_set() def cancel(self, event=None): # Do not replace. @@ -117,13 +124,12 @@ class Query(Toplevel): class SectionName(Query): "Get a name for a config file section name." + # Used in ConfigDialog.GetNewKeysName, .GetNewThemeName (837) def __init__(self, parent, title, message, used_names, *, _htest=False, _utest=False): - "used_names - collection of strings already in use" - self.used_names = used_names - Query.__init__(self, parent, title, message, - _htest=_htest, _utest=_utest) + super().__init__(parent, title, message, used_names=used_names, + _htest=_htest, _utest=_utest) def entry_ok(self): "Return sensible ConfigParser section name or None." @@ -131,16 +137,16 @@ class SectionName(Query): if not name: showerror(title='Name Error', message='No name specified.', parent=self) - return + return None elif len(name)>30: showerror(title='Name Error', message='Name too long. It should be no more than '+ '30 characters.', parent=self) - return + return None elif name in self.used_names: showerror(title='Name Error', message='This name is already in use.', parent=self) - return + return None return name @@ -148,48 +154,133 @@ class ModuleName(Query): "Get a module name for Open Module menu entry." # Used in open_module (editor.EditorWindow until move to iobinding). - def __init__(self, parent, title, message, text0='', + def __init__(self, parent, title, message, text0, *, _htest=False, _utest=False): - """text0 - name selected in text before Open Module invoked" - """ - Query.__init__(self, parent, title, message, text0=text0, - _htest=_htest, _utest=_utest) + super().__init__(parent, title, message, text0=text0, + _htest=_htest, _utest=_utest) def entry_ok(self): "Return entered module name as file path or None." - # Moved here from Editor_Window.load_module 2016 July. name = self.entry.get().strip() if not name: showerror(title='Name Error', message='No name specified.', parent=self) - return - # XXX Ought to insert current file's directory in front of path + return None + # XXX Ought to insert current file's directory in front of path. try: spec = importlib.util.find_spec(name) except (ValueError, ImportError) as msg: showerror("Import Error", str(msg), parent=self) - return + return None if spec is None: showerror("Import Error", "module not found", parent=self) - return + return None if not isinstance(spec.loader, importlib.abc.SourceLoader): showerror("Import Error", "not a source-based module", parent=self) - return + return None try: file_path = spec.loader.get_filename(name) except AttributeError: showerror("Import Error", "loader does not support get_filename", parent=self) - return + return None return file_path +class HelpSource(Query): + "Get menu name and help source for Help menu." + # Used in ConfigDialog.HelpListItemAdd/Edit, (941/9) + + def __init__(self, parent, title, *, menuitem='', filepath='', + used_names={}, _htest=False, _utest=False): + """Get menu entry and url/local file for Additional Help. + + User enters a name for the Help resource and a web url or file + name. The user can browse for the file. + """ + self.filepath = filepath + message = 'Name for item on Help menu:' + super().__init__(parent, title, message, text0=menuitem, + used_names=used_names, _htest=_htest, _utest=_utest) + + def create_widgets(self): + super().create_widgets() + frame = self.frame + pathlabel = Label(frame, anchor='w', justify='left', + text='Help File Path: Enter URL or browse for file') + self.pathvar = StringVar(self, self.filepath) + self.path = Entry(frame, textvariable=self.pathvar, width=40) + browse = Button(frame, text='Browse', width=8, + command=self.browse_file) + + pathlabel.pack(anchor='w', padx=5, pady=3) + self.path.pack(anchor='w', padx=5, pady=3) + browse.pack(pady=3) + + def askfilename(self, filetypes, initdir, initfile): # htest # + # Extracted from browse_file so can mock for unittests. + # Cannot unittest as cannot simulate button clicks. + # Test by running htest, such as by running this file. + return filedialog.Open(parent=self, filetypes=filetypes)\ + .show(initialdir=initdir, initialfile=initfile) + + def browse_file(self): + filetypes = [ + ("HTML Files", "*.htm *.html", "TEXT"), + ("PDF Files", "*.pdf", "TEXT"), + ("Windows Help Files", "*.chm"), + ("Text Files", "*.txt", "TEXT"), + ("All Files", "*")] + path = self.pathvar.get() + if path: + dir, base = os.path.split(path) + else: + base = None + if platform[:3] == 'win': + dir = os.path.join(os.path.dirname(executable), 'Doc') + if not os.path.isdir(dir): + dir = os.getcwd() + else: + dir = os.getcwd() + file = self.askfilename(filetypes, dir, base) + if file: + self.pathvar.set(file) + + item_ok = SectionName.entry_ok # localize for test override + + def path_ok(self): + "Simple validity check for menu file path" + path = self.path.get().strip() + if not path: #no path specified + showerror(title='File Path Error', + message='No help file path specified.', + parent=self) + return None + elif not path.startswith(('www.', 'http')): + if path[:5] == 'file:': + path = path[5:] + if not os.path.exists(path): + showerror(title='File Path Error', + message='Help file path does not exist.', + parent=self) + return None + if platform == 'darwin': # for Mac Safari + path = "file://" + path + return path + + def entry_ok(self): + "Return apparently valid (name, path) or None" + name = self.item_ok() + path = self.path_ok() + return None if name is None or path is None else (name, path) + + if __name__ == '__main__': import unittest unittest.main('idlelib.idle_test.test_query', verbosity=2, exit=False) from idlelib.idle_test.htest import run - run(Query) + run(Query, HelpSource) -- cgit v1.2.1 From 394d3532b5c842dbe736c07f151e591a607f6960 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 8 Jul 2016 00:26:20 -0400 Subject: Whitespace --- Lib/idlelib/idle_test/test_query.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/idlelib/idle_test/test_query.py b/Lib/idlelib/idle_test/test_query.py index 45c99fac24..d7372a305e 100644 --- a/Lib/idlelib/idle_test/test_query.py +++ b/Lib/idlelib/idle_test/test_query.py @@ -196,7 +196,7 @@ class HelpsourceBrowsefileTest(unittest.TestCase): class Dummy_HelpSource: browse_file = query.HelpSource.browse_file - pathvar = Var() + pathvar = Var() dialog = Dummy_HelpSource() -- cgit v1.2.1 From 8a33284d191025bc387644666807311eb7d60384 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Fri, 8 Jul 2016 11:00:00 -0700 Subject: Issue #26896: Disambiguate uses of "importer" with "finder". Thanks to Oren Milman for the patch. --- Lib/pkgutil.py | 22 +++++++++++----------- Lib/runpy.py | 4 ++-- Lib/test/test_importlib/import_/test_meta_path.py | 1 - Lib/test/test_importlib/util.py | 1 - Lib/test/test_pkgutil.py | 2 +- 5 files changed, 14 insertions(+), 16 deletions(-) (limited to 'Lib') diff --git a/Lib/pkgutil.py b/Lib/pkgutil.py index a04b7d15f4..15b3ae1161 100644 --- a/Lib/pkgutil.py +++ b/Lib/pkgutil.py @@ -45,7 +45,7 @@ def read_code(stream): def walk_packages(path=None, prefix='', onerror=None): - """Yields (module_loader, name, ispkg) for all modules recursively + """Yields (module_finder, name, ispkg) for all modules recursively on path, or, if path is None, all accessible modules. 'path' should be either None or a list of paths to look for @@ -102,7 +102,7 @@ def walk_packages(path=None, prefix='', onerror=None): def iter_modules(path=None, prefix=''): - """Yields (module_loader, name, ispkg) for all submodules on path, + """Yields (module_finder, name, ispkg) for all submodules on path, or, if path is None, all top-level modules on sys.path. 'path' should be either None or a list of paths to look for @@ -184,10 +184,10 @@ def _import_imp(): imp = importlib.import_module('imp') class ImpImporter: - """PEP 302 Importer that wraps Python's "classic" import algorithm + """PEP 302 Finder that wraps Python's "classic" import algorithm - ImpImporter(dirname) produces a PEP 302 importer that searches that - directory. ImpImporter(None) produces a PEP 302 importer that searches + ImpImporter(dirname) produces a PEP 302 finder that searches that + directory. ImpImporter(None) produces a PEP 302 finder that searches the current sys.path, plus any modules that are frozen or built-in. Note that ImpImporter does not currently support being used by placement @@ -395,9 +395,9 @@ except ImportError: def get_importer(path_item): - """Retrieve a PEP 302 importer for the given path item + """Retrieve a PEP 302 finder for the given path item - The returned importer is cached in sys.path_importer_cache + The returned finder is cached in sys.path_importer_cache if it was newly created by a path hook. The cache (or part of it) can be cleared manually if a @@ -419,16 +419,16 @@ def get_importer(path_item): def iter_importers(fullname=""): - """Yield PEP 302 importers for the given module name + """Yield PEP 302 finders for the given module name - If fullname contains a '.', the importers will be for the package + If fullname contains a '.', the finders will be for the package containing fullname, otherwise they will be all registered top level - importers (i.e. those on both sys.meta_path and sys.path_hooks). + finders (i.e. those on both sys.meta_path and sys.path_hooks). If the named module is in a package, that package is imported as a side effect of invoking this function. - If no module name is specified, all top level importers are produced. + If no module name is specified, all top level finders are produced. """ if fullname.startswith('.'): msg = "Relative module name {!r} not supported".format(fullname) diff --git a/Lib/runpy.py b/Lib/runpy.py index af6205db49..6b6fc24c36 100644 --- a/Lib/runpy.py +++ b/Lib/runpy.py @@ -98,7 +98,7 @@ def _run_module_code(code, init_globals=None, # may be cleared when the temporary module goes away return mod_globals.copy() -# Helper to get the loader, code and filename for a module +# Helper to get the full name, spec and code for a module def _get_module_details(mod_name, error=ImportError): if mod_name.startswith("."): raise error("Relative module names not supported") @@ -253,7 +253,7 @@ def run_path(path_name, init_globals=None, run_name=None): return _run_module_code(code, init_globals, run_name, pkg_name=pkg_name, script_name=fname) else: - # Importer is defined for path, so add it to + # Finder is defined for path, so add it to # the start of sys.path sys.path.insert(0, path_name) try: diff --git a/Lib/test/test_importlib/import_/test_meta_path.py b/Lib/test/test_importlib/import_/test_meta_path.py index c452cdd063..5a41e8968a 100644 --- a/Lib/test/test_importlib/import_/test_meta_path.py +++ b/Lib/test/test_importlib/import_/test_meta_path.py @@ -76,7 +76,6 @@ class CallSignature: self.__import__(mod_name) assert len(log) == 1 args = log[0][0] - kwargs = log[0][1] # Assuming all arguments are positional. self.assertEqual(args[0], mod_name) self.assertIsNone(args[1]) diff --git a/Lib/test/test_importlib/util.py b/Lib/test/test_importlib/util.py index ce20377f8c..f72dc45678 100644 --- a/Lib/test/test_importlib/util.py +++ b/Lib/test/test_importlib/util.py @@ -266,7 +266,6 @@ class mock_spec(_ImporterMock): module = self.modules[fullname] except KeyError: return None - is_package = hasattr(module, '__path__') spec = util.spec_from_file_location( fullname, module.__file__, loader=self, submodule_search_locations=getattr(module, '__path__', None)) diff --git a/Lib/test/test_pkgutil.py b/Lib/test/test_pkgutil.py index 9d2035464c..a82058760d 100644 --- a/Lib/test/test_pkgutil.py +++ b/Lib/test/test_pkgutil.py @@ -205,7 +205,7 @@ class PkgutilPEP302Tests(unittest.TestCase): del sys.meta_path[0] def test_getdata_pep302(self): - # Use a dummy importer/loader + # Use a dummy finder/loader self.assertEqual(pkgutil.get_data('foo', 'dummy'), "Hello, world!") del sys.modules['foo'] -- cgit v1.2.1 From 7f4dac3e09b7171e333d3d33fc993c3dc7f243be Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Fri, 8 Jul 2016 11:09:35 -0700 Subject: Issue #26972: Fix some mistakes in importlib-related docstrings. Thanks to Oren Milman for the patch. --- Lib/importlib/_bootstrap.py | 6 +++--- Lib/importlib/_bootstrap_external.py | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'Lib') diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py index afc31ee5e8..11df7060c1 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -270,7 +270,7 @@ def _load_module_shim(self, fullname): # Module specifications ####################################################### def _module_repr(module): - # The implementation of ModuleType__repr__(). + # The implementation of ModuleType.__repr__(). loader = getattr(module, '__loader__', None) if hasattr(loader, 'module_repr'): # As soon as BuiltinImporter, FrozenImporter, and NamespaceLoader @@ -603,7 +603,7 @@ def _module_repr_from_spec(spec): # Used by importlib.reload() and _load_module_shim(). def _exec(spec, module): - """Execute the spec in an existing module's namespace.""" + """Execute the spec's specified module in an existing module's namespace.""" name = spec.name _imp.acquire_lock() with _ModuleLockManager(name): @@ -877,7 +877,7 @@ def _find_spec_legacy(finder, name, path): def _find_spec(name, path, target=None): - """Find a module's loader.""" + """Find a module's spec.""" meta_path = sys.meta_path if meta_path is None: # PyImport_Cleanup() is running or has been called. diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 30e833044d..a4805f3a9d 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -1048,11 +1048,7 @@ class PathFinder: @classmethod def _path_hooks(cls, path): - """Search sequence of hooks for a finder for 'path'. - - If 'hooks' is false then use sys.path_hooks. - - """ + """Search sys.path_hooks for a finder for 'path'.""" if sys.path_hooks is not None and not sys.path_hooks: _warnings.warn('sys.path_hooks is empty', ImportWarning) for hook in sys.path_hooks: @@ -1134,8 +1130,10 @@ class PathFinder: @classmethod def find_spec(cls, fullname, path=None, target=None): - """find the module on sys.path or 'path' based on sys.path_hooks and - sys.path_importer_cache.""" + """Try to find a spec for 'fullname' on sys.path or 'path'. + + The search is based on sys.path_hooks and sys.path_importer_cache. + """ if path is None: path = sys.path spec = cls._get_spec(fullname, path, target) @@ -1215,8 +1213,10 @@ class FileFinder: submodule_search_locations=smsl) def find_spec(self, fullname, target=None): - """Try to find a spec for the specified module. Returns the - matching spec, or None if not found.""" + """Try to find a spec for the specified module. + + Returns the matching spec, or None if not found. + """ is_namespace = False tail_module = fullname.rpartition('.')[2] try: -- cgit v1.2.1 From 597b7ea1772f5b97e7037e05562bb9903f843f7a Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 10 Jul 2016 12:37:30 +0300 Subject: Issue #27474: Unified error messages in the __contains__ method of bytes and bytearray for integers in and out of the Py_ssize_t range. Patch by Xiang Zhang. --- Lib/test/test_bytes.py | 1 + 1 file changed, 1 insertion(+) (limited to 'Lib') diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index 05dc26afaf..129b4abf33 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -269,6 +269,7 @@ class BaseBytesTest: self.assertNotIn(200, b) self.assertRaises(ValueError, lambda: 300 in b) self.assertRaises(ValueError, lambda: -1 in b) + self.assertRaises(ValueError, lambda: sys.maxsize+1 in b) self.assertRaises(TypeError, lambda: None in b) self.assertRaises(TypeError, lambda: float(ord('a')) in b) self.assertRaises(TypeError, lambda: "a" in b) -- cgit v1.2.1 From efda837b76bfe4c64accab28be0c026b195b2a9c Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Sun, 10 Jul 2016 18:20:15 +0200 Subject: Issue #27027: Added test.support.is_android that is True when this is an Android build. --- Lib/test/support/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index ef6b4f505d..2b2966876f 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -92,7 +92,7 @@ __all__ = [ "anticipate_failure", "load_package_tests", "detect_api_mismatch", "check__all__", # sys - "is_jython", "check_impl_detail", + "is_jython", "is_android", "check_impl_detail", # network "HOST", "IPV6_ENABLED", "find_unused_port", "bind_port", "open_urlresource", # processes @@ -734,6 +734,8 @@ requires_lzma = unittest.skipUnless(lzma, 'requires lzma') is_jython = sys.platform.startswith('java') +is_android = bool(sysconfig.get_config_var('ANDROID_API_LEVEL')) + # Filename used for testing if os.name == 'java': # Jython disallows @ in module names -- cgit v1.2.1 From fc6e39842812db71ae9b63797be6e38370624bf7 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Sun, 10 Jul 2016 13:46:34 -0400 Subject: Issue #27173: Add 'IDLE Modern Unix' to the built-in key sets. Make the default key set depend on the platform. Add tests for changes to the config module. --- Lib/idlelib/config-keys.def | 51 ++++++++++++++ Lib/idlelib/config-main.def | 4 +- Lib/idlelib/config.py | 130 ++++++++++++++++++++++------------- Lib/idlelib/configdialog.py | 44 +++++++++--- Lib/idlelib/idle_test/test_config.py | 98 ++++++++++++++++++++++++++ 5 files changed, 270 insertions(+), 57 deletions(-) create mode 100644 Lib/idlelib/idle_test/test_config.py (limited to 'Lib') diff --git a/Lib/idlelib/config-keys.def b/Lib/idlelib/config-keys.def index 3bfcb69015..64788f9adf 100644 --- a/Lib/idlelib/config-keys.def +++ b/Lib/idlelib/config-keys.def @@ -109,6 +109,57 @@ change-indentwidth= del-word-left= del-word-right= +[IDLE Modern Unix] +copy = +cut = +paste = +beginning-of-line = +center-insert = +close-all-windows = +close-window = +do-nothing = +end-of-file = +history-next = +history-previous = +interrupt-execution = +view-restart = +restart-shell = +open-class-browser = +open-module = +open-new-window = +open-window-from-file = +plain-newline-and-indent = +print-window = +python-context-help = +python-docs = +redo = +remove-selection = +save-copy-of-window-as-file = +save-window-as-file = +save-window = +select-all = +toggle-auto-coloring = +undo = +find = +find-again = +find-in-files = +find-selection = +replace = +goto-line = +smart-backspace = +newline-and-indent = +smart-indent = +indent-region = +dedent-region = +comment-region = +uncomment-region = +tabify-region = +untabify-region = +toggle-tabs = +change-indentwidth = +del-word-left = +del-word-right = + [IDLE Classic Mac] copy= cut= diff --git a/Lib/idlelib/config-main.def b/Lib/idlelib/config-main.def index 8ebbc1b4c2..a61bba7ef3 100644 --- a/Lib/idlelib/config-main.def +++ b/Lib/idlelib/config-main.def @@ -70,7 +70,9 @@ name2= [Keys] default= 1 -name= IDLE Classic Windows +name= +name2= +# name2 set in user config-main.cfg for keys added after 2016 July 1 [History] cyclic=1 diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index 51ef21b107..f2437a8631 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -234,10 +234,7 @@ class IdleConf: ' from section %r: %r' % (type, option, section, self.userCfg[configType].Get(section, option, raw=raw))) - try: - print(warning, file=sys.stderr) - except OSError: - pass + _warn(warning, configType, section, option) try: if self.defaultCfg[configType].has_option(section,option): return self.defaultCfg[configType].Get( @@ -251,10 +248,7 @@ class IdleConf: ' from section %r.\n' ' returning default value: %r' % (option, section, default)) - try: - print(warning, file=sys.stderr) - except OSError: - pass + _warn(warning, configType, section, option) return default def SetOption(self, configType, section, option, value): @@ -362,47 +356,68 @@ class IdleConf: '\n from theme %r.\n' ' returning default color: %r' % (element, themeName, theme[element])) - try: - print(warning, file=sys.stderr) - except OSError: - pass + _warn(warning, 'highlight', themeName, element) theme[element] = cfgParser.Get( themeName, element, default=theme[element]) return theme def CurrentTheme(self): - """Return the name of the currently active text color theme. + "Return the name of the currently active text color theme." + return self.current_colors_and_keys('Theme') + + def CurrentKeys(self): + """Return the name of the currently active key set.""" + return self.current_colors_and_keys('Keys') + + def current_colors_and_keys(self, section): + """Return the currently active name for Theme or Keys section. + + idlelib.config-main.def ('default') includes these sections - idlelib.config-main.def includes this section [Theme] default= 1 name= IDLE Classic name2= - # name2 set in user config-main.cfg for themes added after 2015 Oct 1 - Item name2 is needed because setting name to a new builtin - causes older IDLEs to display multiple error messages or quit. + [Keys] + default= 1 + name= + name2= + + Item 'name2', is used for built-in ('default') themes and keys + added after 2015 Oct 1 and 2016 July 1. This kludge is needed + because setting 'name' to a builtin not defined in older IDLEs + to display multiple error messages or quit. See https://bugs.python.org/issue25313. - When default = True, name2 takes precedence over name, - while older IDLEs will just use name. + When default = True, 'name2' takes precedence over 'name', + while older IDLEs will just use name. When default = False, + 'name2' may still be set, but it is ignored. """ + cfgname = 'highlight' if section == 'Theme' else 'keys' default = self.GetOption('main', 'Theme', 'default', type='bool', default=True) + name = '' if default: - theme = self.GetOption('main', 'Theme', 'name2', default='') - if default and not theme or not default: - theme = self.GetOption('main', 'Theme', 'name', default='') - source = self.defaultCfg if default else self.userCfg - if source['highlight'].has_section(theme): - return theme + name = self.GetOption('main', section, 'name2', default='') + if not name: + name = self.GetOption('main', section, 'name', default='') + if name: + source = self.defaultCfg if default else self.userCfg + if source[cfgname].has_section(name): + return name + return "IDLE Classic" if section == 'Theme' else self.default_keys() + + @staticmethod + def default_keys(): + if sys.platform[:3] == 'win': + return 'IDLE Classic Windows' + elif sys.platform == 'darwin': + return 'IDLE Classic OSX' else: - return "IDLE Classic" + return 'IDLE Modern Unix' - def CurrentKeys(self): - "Return the name of the currently active key set." - return self.GetOption('main', 'Keys', 'name', default='') - - def GetExtensions(self, active_only=True, editor_only=False, shell_only=False): + def GetExtensions(self, active_only=True, + editor_only=False, shell_only=False): """Return extensions in default and user config-extensions files. If active_only True, only return active (enabled) extensions @@ -422,7 +437,7 @@ class IdleConf: if self.GetOption('extensions', extn, 'enable', default=True, type='bool'): #the extension is enabled - if editor_only or shell_only: # TODO if both, contradictory + if editor_only or shell_only: # TODO both True contradict if editor_only: option = "enable_editor" else: @@ -527,7 +542,8 @@ class IdleConf: eventStr - virtual event, including brackets, as in '<>'. """ eventName = eventStr[2:-2] #trim off the angle brackets - binding = self.GetOption('keys', keySetName, eventName, default='').split() + binding = self.GetOption('keys', keySetName, eventName, default='', + warn_on_default=False).split() return binding def GetCurrentKeySet(self): @@ -638,20 +654,28 @@ class IdleConf: '<>': [''] } if keySetName: - for event in keyBindings: - binding = self.GetKeyBinding(keySetName, event) - if binding: - keyBindings[event] = binding - else: #we are going to return a default, print warning - warning=('\n Warning: config.py - IdleConf.GetCoreKeys' - ' -\n problem retrieving key binding for event %r' - '\n from key set %r.\n' - ' returning default value: %r' % - (event, keySetName, keyBindings[event])) - try: - print(warning, file=sys.stderr) - except OSError: - pass + if not (self.userCfg['keys'].has_section(keySetName) or + self.defaultCfg['keys'].has_section(keySetName)): + warning = ( + '\n Warning: config.py - IdleConf.GetCoreKeys -\n' + ' key set %r is not defined, using default bindings.' % + (keySetName,) + ) + _warn(warning, 'keys', keySetName) + else: + for event in keyBindings: + binding = self.GetKeyBinding(keySetName, event) + if binding: + keyBindings[event] = binding + else: #we are going to return a default, print warning + warning = ( + '\n Warning: config.py - IdleConf.GetCoreKeys -\n' + ' problem retrieving key binding for event %r\n' + ' from key set %r.\n' + ' returning default value: %r' % + (event, keySetName, keyBindings[event]) + ) + _warn(warning, 'keys', keySetName, event) return keyBindings def GetExtraHelpSourceList(self, configSet): @@ -735,6 +759,18 @@ class IdleConf: idleConf = IdleConf() + +_warned = set() +def _warn(msg, *key): + key = (msg,) + key + if key not in _warned: + try: + print(msg, file=sys.stderr) + except OSError: + pass + _warned.add(key) + + # TODO Revise test output, write expanded unittest # if __name__ == '__main__': diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 388b48f088..fda655f5d7 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -341,6 +341,7 @@ class ConfigDialog(Toplevel): buttonSaveCustomKeys = Button( frames[1], text='Save as New Custom Key Set', command=self.SaveAsNewKeySet) + self.new_custom_keys = Label(frames[0], bd=2) ##widget packing #body @@ -361,6 +362,7 @@ class ConfigDialog(Toplevel): self.radioKeysCustom.grid(row=1, column=0, sticky=W+NS) self.optMenuKeysBuiltin.grid(row=0, column=1, sticky=NSEW) self.optMenuKeysCustom.grid(row=1, column=1, sticky=NSEW) + self.new_custom_keys.grid(row=0, column=2, sticky=NSEW, padx=5, pady=5) self.buttonDeleteCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2) buttonSaveCustomKeys.pack(side=LEFT, fill=X, expand=True, padx=2) frames[0].pack(side=TOP, fill=BOTH, expand=True) @@ -514,10 +516,11 @@ class ConfigDialog(Toplevel): self.OnNewColourSet() def VarChanged_builtinTheme(self, *params): + oldthemes = ('IDLE Classic', 'IDLE New') value = self.builtinTheme.get() - if value == 'IDLE Dark': - if idleConf.GetOption('main', 'Theme', 'name') != 'IDLE New': - self.AddChangedItem('main', 'Theme', 'name', 'IDLE Classic') + if value not in oldthemes: + if idleConf.GetOption('main', 'Theme', 'name') not in oldthemes: + self.AddChangedItem('main', 'Theme', 'name', oldthemes[0]) self.AddChangedItem('main', 'Theme', 'name2', value) self.new_custom_theme.config(text='New theme, see Help', fg='#500000') @@ -557,8 +560,23 @@ class ConfigDialog(Toplevel): self.AddChangedItem('extensions', extKeybindSection, event, value) def VarChanged_builtinKeys(self, *params): + oldkeys = ( + 'IDLE Classic Windows', + 'IDLE Classic Unix', + 'IDLE Classic Mac', + 'IDLE Classic OSX', + ) value = self.builtinKeys.get() - self.AddChangedItem('main', 'Keys', 'name', value) + if value not in oldkeys: + if idleConf.GetOption('main', 'Keys', 'name') not in oldkeys: + self.AddChangedItem('main', 'Keys', 'name', oldkeys[0]) + self.AddChangedItem('main', 'Keys', 'name2', value) + self.new_custom_keys.config(text='New key set, see Help', + fg='#500000') + else: + self.AddChangedItem('main', 'Keys', 'name', value) + self.AddChangedItem('main', 'Keys', 'name2', '') + self.new_custom_keys.config(text='', fg='black') self.LoadKeysList(value) def VarChanged_customKeys(self, *params): @@ -767,8 +785,10 @@ class ConfigDialog(Toplevel): else: self.optMenuKeysCustom.SetMenu(itemList, itemList[0]) #revert to default key set - self.keysAreBuiltin.set(idleConf.defaultCfg['main'].Get('Keys', 'default')) - self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys', 'name')) + self.keysAreBuiltin.set(idleConf.defaultCfg['main'] + .Get('Keys', 'default')) + self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys', 'name') + or idleConf.default_keys()) #user can't back out of these changes, they must be applied now self.SaveAllChangedConfigs() self.ActivateConfigChanges() @@ -1067,7 +1087,7 @@ class ConfigDialog(Toplevel): self.optMenuKeysCustom.SetMenu(itemList, currentOption) itemList = idleConf.GetSectionList('default', 'keys') itemList.sort() - self.optMenuKeysBuiltin.SetMenu(itemList, itemList[0]) + self.optMenuKeysBuiltin.SetMenu(itemList, idleConf.default_keys()) self.SetKeysType() ##load keyset element list keySetName = idleConf.CurrentKeys() @@ -1369,12 +1389,18 @@ machine. Some do not take affect until IDLE is restarted. [Cancel] only cancels changes made since the last save. ''' help_pages = { - 'Highlighting':''' + 'Highlighting': ''' Highlighting: The IDLE Dark color theme is new in October 2015. It can only be used with older IDLE releases if it is saved as a custom theme, with a different name. -''' +''', + 'Keys': ''' +Keys: +The IDLE Modern Unix key set is new in June 2016. It can only +be used with older IDLE releases if it is saved as a custom +key set, with a different name. +''', } diff --git a/Lib/idlelib/idle_test/test_config.py b/Lib/idlelib/idle_test/test_config.py new file mode 100644 index 0000000000..bb7732cf7c --- /dev/null +++ b/Lib/idlelib/idle_test/test_config.py @@ -0,0 +1,98 @@ +'''Test idlelib.config. + +Much is tested by opening config dialog live or in test_configdialog. +Coverage: 27% +''' +from sys import modules +from test.support import captured_stderr +from tkinter import Tk +import unittest +from idlelib import config + +# Tests should not depend on fortuitous user configurations. +# They must not affect actual user .cfg files. +# Replace user parsers with empty parsers that cannot be saved. + +idleConf = config.idleConf +usercfg = idleConf.userCfg +testcfg = {} +usermain = testcfg['main'] = config.IdleUserConfParser('') # filename +userhigh = testcfg['highlight'] = config.IdleUserConfParser('') +userkeys = testcfg['keys'] = config.IdleUserConfParser('') + +def setUpModule(): + idleConf.userCfg = testcfg + +def tearDownModule(): + idleConf.userCfg = testcfg + + +class CurrentColorKeysTest(unittest.TestCase): + """Test correct scenarios for colorkeys and wrap functions. + + The 5 correct patterns are possible results of config dialog. + """ + colorkeys = idleConf.current_colors_and_keys + + def test_old_default(self): + # name2 must be blank + usermain.read_string(''' + [Theme] + default= 1 + ''') + self.assertEqual(self.colorkeys('Theme'), 'IDLE Classic') + usermain['Theme']['name'] = 'IDLE New' + self.assertEqual(self.colorkeys('Theme'), 'IDLE New') + usermain['Theme']['name'] = 'non-default' # error + self.assertEqual(self.colorkeys('Theme'), 'IDLE Classic') + usermain.remove_section('Theme') + + def test_new_default(self): + # name2 overrides name + usermain.read_string(''' + [Theme] + default= 1 + name= IDLE New + name2= IDLE Dark + ''') + self.assertEqual(self.colorkeys('Theme'), 'IDLE Dark') + usermain['Theme']['name2'] = 'non-default' # error + self.assertEqual(self.colorkeys('Theme'), 'IDLE Classic') + usermain.remove_section('Theme') + + def test_user_override(self): + # name2 does not matter + usermain.read_string(''' + [Theme] + default= 0 + name= Custom Dark + ''') # error until set userhigh + self.assertEqual(self.colorkeys('Theme'), 'IDLE Classic') + userhigh.read_string('[Custom Dark]\na=b') + self.assertEqual(self.colorkeys('Theme'), 'Custom Dark') + usermain['Theme']['name2'] = 'IDLE Dark' + self.assertEqual(self.colorkeys('Theme'), 'Custom Dark') + usermain.remove_section('Theme') + userhigh.remove_section('Custom Dark') + + +class WarningTest(unittest.TestCase): + + def test_warn(self): + Equal = self.assertEqual + config._warned = set() + with captured_stderr() as stderr: + config._warn('warning', 'key') + Equal(config._warned, {('warning','key')}) + Equal(stderr.getvalue(), 'warning'+'\n') + with captured_stderr() as stderr: + config._warn('warning', 'key') + Equal(stderr.getvalue(), '') + with captured_stderr() as stderr: + config._warn('warn2', 'yek') + Equal(config._warned, {('warning','key'), ('warn2','yek')}) + Equal(stderr.getvalue(), 'warn2'+'\n') + + +if __name__ == '__main__': + unittest.main(verbosity=2) -- cgit v1.2.1 From 67c0294641100ab504d02f257183eb4e4f39922e Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Sun, 10 Jul 2016 17:26:24 -0400 Subject: Issue #27173: Fix error in test_config that caused test_idle to fail. --- Lib/idlelib/idle_test/test_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/idlelib/idle_test/test_config.py b/Lib/idlelib/idle_test/test_config.py index bb7732cf7c..53665cd761 100644 --- a/Lib/idlelib/idle_test/test_config.py +++ b/Lib/idlelib/idle_test/test_config.py @@ -24,7 +24,7 @@ def setUpModule(): idleConf.userCfg = testcfg def tearDownModule(): - idleConf.userCfg = testcfg + idleConf.userCfg = usercfg class CurrentColorKeysTest(unittest.TestCase): -- cgit v1.2.1 From 5ebfdc9de3428a14a008c8508eebe74c12afca63 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Sun, 10 Jul 2016 17:28:10 -0400 Subject: Refine geometry of idlelib htests (and a few other fix-ups). --- Lib/idlelib/calltip_w.py | 4 ++-- Lib/idlelib/colorizer.py | 5 +++-- Lib/idlelib/debugobj.py | 6 ++---- Lib/idlelib/dynoption.py | 4 ++-- Lib/idlelib/grep.py | 5 ++--- Lib/idlelib/idle_test/test_configdialog.py | 2 +- Lib/idlelib/iomenu.py | 4 ++-- Lib/idlelib/multicall.py | 4 ++-- Lib/idlelib/percolator.py | 5 ++--- Lib/idlelib/redirector.py | 6 +++--- Lib/idlelib/replace.py | 6 +++--- Lib/idlelib/scrolledlist.py | 4 ++-- Lib/idlelib/search.py | 7 ++++--- Lib/idlelib/stackviewer.py | 6 +++--- Lib/idlelib/statusbar.py | 9 ++++----- Lib/idlelib/tabbedpages.py | 5 ++--- Lib/idlelib/tooltip.py | 15 +++++++-------- Lib/idlelib/tree.py | 4 ++-- Lib/idlelib/undo.py | 9 ++++----- 19 files changed, 52 insertions(+), 58 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/calltip_w.py b/Lib/idlelib/calltip_w.py index 9f6cdc1771..b3c3e5e284 100644 --- a/Lib/idlelib/calltip_w.py +++ b/Lib/idlelib/calltip_w.py @@ -138,8 +138,8 @@ def _calltip_window(parent): # htest # top = Toplevel(parent) top.title("Test calltips") - top.geometry("200x100+%d+%d" % (parent.winfo_rootx() + 200, - parent.winfo_rooty() + 150)) + x, y = map(int, parent.geometry().split('+')[1:]) + top.geometry("200x100+%d+%d" % (x + 250, y + 175)) text = Text(top) text.pack(side=LEFT, fill=BOTH, expand=1) text.insert("insert", "string.split") diff --git a/Lib/idlelib/colorizer.py b/Lib/idlelib/colorizer.py index 5b6dc67594..f5dd03d092 100644 --- a/Lib/idlelib/colorizer.py +++ b/Lib/idlelib/colorizer.py @@ -259,8 +259,8 @@ def _color_delegator(parent): # htest # top = Toplevel(parent) top.title("Test ColorDelegator") - top.geometry("200x100+%d+%d" % (parent.winfo_rootx() + 200, - parent.winfo_rooty() + 150)) + x, y = map(int, parent.geometry().split('+')[1:]) + top.geometry("200x100+%d+%d" % (x + 250, y + 175)) source = "if somename: x = 'abc' # comment\nprint\n" text = Text(top, background="white") text.pack(expand=1, fill="both") @@ -276,5 +276,6 @@ if __name__ == "__main__": import unittest unittest.main('idlelib.idle_test.test_colorizer', verbosity=2, exit=False) + from idlelib.idle_test.htest import run run(_color_delegator) diff --git a/Lib/idlelib/debugobj.py b/Lib/idlelib/debugobj.py index 0d8b2b2c7d..c116fcda23 100644 --- a/Lib/idlelib/debugobj.py +++ b/Lib/idlelib/debugobj.py @@ -9,8 +9,6 @@ # XXX TO DO: # - for classes/modules, add "open source" to object browser -import re - from idlelib.tree import TreeItem, TreeNode, ScrolledCanvas from reprlib import Repr @@ -127,8 +125,8 @@ def _object_browser(parent): # htest # from tkinter import Toplevel top = Toplevel(parent) top.title("Test debug object browser") - width, height, x, y = list(map(int, re.split('[x+]', parent.geometry()))) - top.geometry("+%d+%d"%(x + 100, y + 175)) + x, y = map(int, parent.geometry().split('+')[1:]) + top.geometry("+%d+%d" % (x + 100, y + 175)) top.configure(bd=0, bg="yellow") top.focus_set() sc = ScrolledCanvas(top, bg="white", highlightthickness=0, takefocus=1) diff --git a/Lib/idlelib/dynoption.py b/Lib/idlelib/dynoption.py index 922ad5e4af..962f2c30d9 100644 --- a/Lib/idlelib/dynoption.py +++ b/Lib/idlelib/dynoption.py @@ -38,8 +38,8 @@ def _dyn_option_menu(parent): # htest # top = Toplevel(parent) top.title("Tets dynamic option menu") - top.geometry("200x100+%d+%d" % (parent.winfo_rootx() + 200, - parent.winfo_rooty() + 150)) + x, y = map(int, parent.geometry().split('+')[1:]) + top.geometry("200x100+%d+%d" % (x + 250, y + 175)) top.focus_set() var = StringVar(top) diff --git a/Lib/idlelib/grep.py b/Lib/idlelib/grep.py index 6324b4f112..f1382c9d65 100644 --- a/Lib/idlelib/grep.py +++ b/Lib/idlelib/grep.py @@ -1,6 +1,5 @@ import os import fnmatch -import re # for htest import sys from tkinter import StringVar, BooleanVar, Checkbutton # for GrepDialog from idlelib import searchengine @@ -134,8 +133,8 @@ def _grep_dialog(parent): # htest # from tkinter import Toplevel, Text, Button, SEL, END top = Toplevel(parent) top.title("Test GrepDialog") - width, height, x, y = list(map(int, re.split('[x+]', parent.geometry()))) - top.geometry("+%d+%d"%(x, y + 150)) + x, y = map(int, parent.geometry().split('+')[1:]) + top.geometry("+%d+%d" % (x, y + 175)) flist = PyShellFileList(top) text = Text(top, height=5) diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py index 1801a7d6c2..736b098d2a 100644 --- a/Lib/idlelib/idle_test/test_configdialog.py +++ b/Lib/idlelib/idle_test/test_configdialog.py @@ -21,7 +21,7 @@ class ConfigDialogTest(unittest.TestCase): cls.root.destroy() del cls.root - def test_dialog(self): + def test_configdialog(self): d = ConfigDialog(self.root, 'Test', _utest=True) d.remove_var_callbacks() diff --git a/Lib/idlelib/iomenu.py b/Lib/idlelib/iomenu.py index 18c68bd35e..3414c7b3af 100644 --- a/Lib/idlelib/iomenu.py +++ b/Lib/idlelib/iomenu.py @@ -535,8 +535,8 @@ def _io_binding(parent): # htest # root = Toplevel(parent) root.title("Test IOBinding") - width, height, x, y = list(map(int, re.split('[x+]', parent.geometry()))) - root.geometry("+%d+%d"%(x, y + 150)) + x, y = map(int, parent.geometry().split('+')[1:]) + root.geometry("+%d+%d" % (x, y + 175)) class MyEditWin: def __init__(self, text): self.text = text diff --git a/Lib/idlelib/multicall.py b/Lib/idlelib/multicall.py index bf02f597f3..8a66cd9f72 100644 --- a/Lib/idlelib/multicall.py +++ b/Lib/idlelib/multicall.py @@ -417,8 +417,8 @@ def MultiCallCreator(widget): def _multi_call(parent): # htest # top = tkinter.Toplevel(parent) top.title("Test MultiCall") - width, height, x, y = list(map(int, re.split('[x+]', parent.geometry()))) - top.geometry("+%d+%d"%(x, y + 150)) + x, y = map(int, parent.geometry().split('+')[1:]) + top.geometry("+%d+%d" % (x, y + 175)) text = MultiCallCreator(tkinter.Text)(top) text.pack() def bindseq(seq, n=[0]): diff --git a/Lib/idlelib/percolator.py b/Lib/idlelib/percolator.py index 2111e036e7..4474f9abea 100644 --- a/Lib/idlelib/percolator.py +++ b/Lib/idlelib/percolator.py @@ -57,7 +57,6 @@ class Percolator: def _percolator(parent): # htest # import tkinter as tk - import re class Tracer(Delegator): def __init__(self, name): @@ -74,8 +73,8 @@ def _percolator(parent): # htest # box = tk.Toplevel(parent) box.title("Test Percolator") - width, height, x, y = list(map(int, re.split('[x+]', parent.geometry()))) - box.geometry("+%d+%d" % (x, y + 150)) + x, y = map(int, parent.geometry().split('+')[1:]) + box.geometry("+%d+%d" % (x, y + 175)) text = tk.Text(box) p = Percolator(text) pin = p.insertfilter diff --git a/Lib/idlelib/redirector.py b/Lib/idlelib/redirector.py index 3a110557d9..ec681de38d 100644 --- a/Lib/idlelib/redirector.py +++ b/Lib/idlelib/redirector.py @@ -152,12 +152,11 @@ class OriginalCommand: def _widget_redirector(parent): # htest # from tkinter import Toplevel, Text - import re top = Toplevel(parent) top.title("Test WidgetRedirector") - width, height, x, y = list(map(int, re.split('[x+]', parent.geometry()))) - top.geometry("+%d+%d"%(x, y + 150)) + x, y = map(int, parent.geometry().split('+')[1:]) + top.geometry("+%d+%d" % (x, y + 175)) text = Text(top) text.pack() text.focus_set() @@ -171,5 +170,6 @@ if __name__ == "__main__": import unittest unittest.main('idlelib.idle_test.test_redirector', verbosity=2, exit=False) + from idlelib.idle_test.htest import run run(_widget_redirector) diff --git a/Lib/idlelib/replace.py b/Lib/idlelib/replace.py index 589b814192..a0acd41ed2 100644 --- a/Lib/idlelib/replace.py +++ b/Lib/idlelib/replace.py @@ -207,8 +207,8 @@ def _replace_dialog(parent): # htest # """htest wrapper function""" box = Toplevel(parent) box.title("Test ReplaceDialog") - width, height, x, y = list(map(int, re.split('[x+]', parent.geometry()))) - box.geometry("+%d+%d"%(x, y + 150)) + x, y = map(int, parent.geometry().split('+')[1:]) + box.geometry("+%d+%d" % (x, y + 175)) # mock undo delegator methods def undo_block_start(): @@ -234,7 +234,7 @@ def _replace_dialog(parent): # htest # if __name__ == '__main__': import unittest - unittest.main('idlelib.idle_test.test_replacedialog', + unittest.main('idlelib.idle_test.test_replace', verbosity=2, exit=False) from idlelib.idle_test.htest import run diff --git a/Lib/idlelib/scrolledlist.py b/Lib/idlelib/scrolledlist.py index d0b66107ac..4799995800 100644 --- a/Lib/idlelib/scrolledlist.py +++ b/Lib/idlelib/scrolledlist.py @@ -127,8 +127,8 @@ class ScrolledList: def _scrolled_list(parent): # htest # top = Toplevel(parent) - width, height, x, y = list(map(int, re.split('[x+]', parent.geometry()))) - top.geometry("+%d+%d"%(x+200, y + 175)) + x, y = map(int, parent.geometry().split('+')[1:]) + top.geometry("+%d+%d" % (x+200, y + 175)) class MyScrolledList(ScrolledList): def fill_menu(self): self.menu.add_command(label="right click") def on_select(self, index): print("select", self.get(index)) diff --git a/Lib/idlelib/search.py b/Lib/idlelib/search.py index a609fd9583..17a9ef3651 100644 --- a/Lib/idlelib/search.py +++ b/Lib/idlelib/search.py @@ -75,8 +75,8 @@ def _search_dialog(parent): # htest # '''Display search test box.''' box = Toplevel(parent) box.title("Test SearchDialog") - width, height, x, y = list(map(int, re.split('[x+]', parent.geometry()))) - box.geometry("+%d+%d"%(x, y + 150)) + x, y = map(int, parent.geometry().split('+')[1:]) + box.geometry("+%d+%d" % (x, y + 175)) text = Text(box, inactiveselectbackground='gray') text.pack() text.insert("insert","This is a sample string.\n"*5) @@ -91,7 +91,8 @@ def _search_dialog(parent): # htest # if __name__ == '__main__': import unittest - unittest.main('idlelib.idle_test.test_searchdialog', + unittest.main('idlelib.idle_test.test_search', verbosity=2, exit=False) + from idlelib.idle_test.htest import run run(_search_dialog) diff --git a/Lib/idlelib/stackviewer.py b/Lib/idlelib/stackviewer.py index b3b99bcefd..657f0a9b01 100644 --- a/Lib/idlelib/stackviewer.py +++ b/Lib/idlelib/stackviewer.py @@ -120,11 +120,11 @@ class VariablesTreeItem(ObjectTreeItem): sublist.append(item) return sublist -def _stack_viewer(parent): +def _stack_viewer(parent): # htest # top = tk.Toplevel(parent) top.title("Test StackViewer") - width, height, x, y = list(map(int, re.split('[x+]', parent.geometry()))) - top.geometry("+%d+%d"%(x, y + 150)) + x, y = map(int, parent.geometry().split('+')[1:]) + top.geometry("+%d+%d" % (x + 50, y + 175)) flist = PyShellFileList(top) try: # to obtain a traceback object intentional_name_error diff --git a/Lib/idlelib/statusbar.py b/Lib/idlelib/statusbar.py index c093920be4..a65bfb3393 100644 --- a/Lib/idlelib/statusbar.py +++ b/Lib/idlelib/statusbar.py @@ -17,15 +17,14 @@ class MultiStatusBar(Frame): label.config(width=width) label.config(text=text) -def _multistatus_bar(parent): - import re +def _multistatus_bar(parent): # htest # from tkinter import Toplevel, Frame, Text, Button top = Toplevel(parent) - width, height, x, y = list(map(int, re.split('[x+]', parent.geometry()))) - top.geometry("+%d+%d" %(x, y + 150)) + x, y = map(int, parent.geometry().split('+')[1:]) + top.geometry("+%d+%d" %(x, y + 175)) top.title("Test multistatus bar") frame = Frame(top) - text = Text(frame) + text = Text(frame, height=5, width=40) text.pack() msb = MultiStatusBar(frame) msb.set_label("one", "hello") diff --git a/Lib/idlelib/tabbedpages.py b/Lib/idlelib/tabbedpages.py index 5f67097b5a..ed07588fc4 100644 --- a/Lib/idlelib/tabbedpages.py +++ b/Lib/idlelib/tabbedpages.py @@ -468,10 +468,9 @@ class TabbedPageSet(Frame): self._tab_set.set_selected_tab(page_name) def _tabbed_pages(parent): # htest # - import re top=Toplevel(parent) - width, height, x, y = list(map(int, re.split('[x+]', parent.geometry()))) - top.geometry("+%d+%d"%(x, y + 175)) + x, y = map(int, parent.geometry().split('+')[1:]) + top.geometry("+%d+%d" % (x, y + 175)) top.title("Test tabbed pages") tabPage=TabbedPageSet(top, page_names=['Foobar','Baz'], n_rows=0, expand_tabs=False, diff --git a/Lib/idlelib/tooltip.py b/Lib/idlelib/tooltip.py index c3eafed9d5..843fb4a7d0 100644 --- a/Lib/idlelib/tooltip.py +++ b/Lib/idlelib/tooltip.py @@ -77,20 +77,19 @@ class ListboxToolTip(ToolTipBase): listbox.insert(END, item) def _tooltip(parent): # htest # - root = Tk() - root.title("Test tooltip") - width, height, x, y = list(map(int, re.split('[x+]', parent.geometry()))) - root.geometry("+%d+%d"%(x, y + 150)) - label = Label(root, text="Place your mouse over buttons") + top = Toplevel(parent) + top.title("Test tooltip") + x, y = map(int, parent.geometry().split('+')[1:]) + top.geometry("+%d+%d" % (x, y + 150)) + label = Label(top, text="Place your mouse over buttons") label.pack() - button1 = Button(root, text="Button 1") - button2 = Button(root, text="Button 2") + button1 = Button(top, text="Button 1") + button2 = Button(top, text="Button 2") button1.pack() button2.pack() ToolTip(button1, "This is tooltip text for button1.") ListboxToolTip(button2, ["This is","multiple line", "tooltip text","for button2"]) - root.mainloop() if __name__ == '__main__': from idlelib.idle_test.htest import run diff --git a/Lib/idlelib/tree.py b/Lib/idlelib/tree.py index cb7f9ae4b4..04e0734ec3 100644 --- a/Lib/idlelib/tree.py +++ b/Lib/idlelib/tree.py @@ -451,8 +451,8 @@ class ScrolledCanvas: def _tree_widget(parent): # htest # top = Toplevel(parent) - width, height, x, y = list(map(int, re.split('[x+]', parent.geometry()))) - top.geometry("+%d+%d"%(x+50, y+175)) + x, y = map(int, parent.geometry().split('+')[1:]) + top.geometry("+%d+%d" % (x+50, y+175)) sc = ScrolledCanvas(top, bg="white", highlightthickness=0, takefocus=1) sc.frame.pack(expand=1, fill="both", side=LEFT) item = FileTreeItem(ICONDIR) diff --git a/Lib/idlelib/undo.py b/Lib/idlelib/undo.py index ccc962a122..9f291e599b 100644 --- a/Lib/idlelib/undo.py +++ b/Lib/idlelib/undo.py @@ -338,13 +338,12 @@ class CommandSequence(Command): def _undo_delegator(parent): # htest # - import re from tkinter import Toplevel, Text, Button from idlelib.percolator import Percolator undowin = Toplevel(parent) undowin.title("Test UndoDelegator") - width, height, x, y = list(map(int, re.split('[x+]', parent.geometry()))) - undowin.geometry("+%d+%d"%(x, y + 175)) + x, y = map(int, parent.geometry().split('+')[1:]) + undowin.geometry("+%d+%d" % (x, y + 175)) text = Text(undowin, height=10) text.pack() @@ -362,7 +361,7 @@ def _undo_delegator(parent): # htest # if __name__ == "__main__": import unittest - unittest.main('idlelib.idle_test.test_undo', verbosity=2, - exit=False) + unittest.main('idlelib.idle_test.test_undo', verbosity=2, exit=False) + from idlelib.idle_test.htest import run run(_undo_delegator) -- cgit v1.2.1 From b50fa3e242d1da3a2e500909b7958eb6fe99fff8 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Sun, 10 Jul 2016 20:21:31 -0400 Subject: Issue #27477: Convert IDLE search dialogs to using ttk widgets. --- Lib/idlelib/grep.py | 13 ++++++------ Lib/idlelib/idle_test/htest.py | 7 +++++++ Lib/idlelib/idle_test/test_searchbase.py | 13 +----------- Lib/idlelib/replace.py | 6 ++++-- Lib/idlelib/search.py | 11 ++++++---- Lib/idlelib/searchbase.py | 36 ++++++++++++++++++++++---------- 6 files changed, 50 insertions(+), 36 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/grep.py b/Lib/idlelib/grep.py index f1382c9d65..cfb0ea0ad8 100644 --- a/Lib/idlelib/grep.py +++ b/Lib/idlelib/grep.py @@ -1,7 +1,8 @@ import os import fnmatch import sys -from tkinter import StringVar, BooleanVar, Checkbutton # for GrepDialog +from tkinter import StringVar, BooleanVar +from tkinter.ttk import Checkbutton from idlelib import searchengine from idlelib.searchbase import SearchDialogBase # Importing OutputWindow fails due to import loop @@ -45,13 +46,10 @@ class GrepDialog(SearchDialogBase): self.globent = self.make_entry("In files:", self.globvar)[0] def create_other_buttons(self): - f = self.make_frame()[0] - - btn = Checkbutton(f, anchor="w", - variable=self.recvar, + btn = Checkbutton( + self.make_frame()[0], variable=self.recvar, text="Recurse down subdirectories") btn.pack(side="top", fill="both") - btn.select() def create_command_buttons(self): SearchDialogBase.create_command_buttons(self) @@ -130,7 +128,8 @@ class GrepDialog(SearchDialogBase): def _grep_dialog(parent): # htest # from idlelib.pyshell import PyShellFileList - from tkinter import Toplevel, Text, Button, SEL, END + from tkinter import Toplevel, Text, SEL, END + from tkinter.ttk import Button top = Toplevel(parent) top.title("Test GrepDialog") x, y = map(int, parent.geometry().split('+')[1:]) diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py index f5311e966c..4d98924d9b 100644 --- a/Lib/idlelib/idle_test/htest.py +++ b/Lib/idlelib/idle_test/htest.py @@ -265,6 +265,13 @@ _search_dialog_spec = { "Click [Close] or [X] to close the 'Search Dialog'." } +_searchbase_spec = { + 'file': 'searchbase', + 'kwds': {}, + 'msg': "Check the appearance of the base search dialog\n" + "Its only action is to close." + } + _scrolled_list_spec = { 'file': 'scrolledlist', 'kwds': {}, diff --git a/Lib/idlelib/idle_test/test_searchbase.py b/Lib/idlelib/idle_test/test_searchbase.py index a0b1231ecd..d769fa2fc2 100644 --- a/Lib/idlelib/idle_test/test_searchbase.py +++ b/Lib/idlelib/idle_test/test_searchbase.py @@ -1,8 +1,7 @@ -'''Unittests for idlelib/searchbase.py +'''tests idlelib.searchbase. Coverage: 99%. The only thing not covered is inconsequential -- testing skipping of suite when self.needwrapbutton is false. - ''' import unittest from test.support import requires @@ -120,11 +119,6 @@ class SearchDialogBaseTest(unittest.TestCase): var, label = spec self.assertEqual(button['text'], label) self.assertEqual(var.get(), state) - if state == 1: - button.deselect() - else: - button.select() - self.assertEqual(var.get(), 1 - state) def test_create_other_buttons(self): for state in (False, True): @@ -140,10 +134,6 @@ class SearchDialogBaseTest(unittest.TestCase): # hit other button, then this one # indexes depend on button order self.assertEqual(var.get(), state) - buttons[val].select() - self.assertEqual(var.get(), 1 - state) - buttons[1-val].select() - self.assertEqual(var.get(), state) def test_make_button(self): self.dialog.top = self.root @@ -162,6 +152,5 @@ class SearchDialogBaseTest(unittest.TestCase): self.assertIn('close', closebuttoncommand) - if __name__ == '__main__': unittest.main(verbosity=2, exit=2) diff --git a/Lib/idlelib/replace.py b/Lib/idlelib/replace.py index a0acd41ed2..7c9573330f 100644 --- a/Lib/idlelib/replace.py +++ b/Lib/idlelib/replace.py @@ -3,7 +3,7 @@ Uses idlelib.SearchEngine for search capability. Defines various replace related functions like replace, replace all, replace+find. """ -from tkinter import * +from tkinter import StringVar, TclError from idlelib import searchengine from idlelib.searchbase import SearchDialogBase @@ -204,7 +204,9 @@ class ReplaceDialog(SearchDialogBase): def _replace_dialog(parent): # htest # - """htest wrapper function""" + from tkinter import Toplevel, Text + from tkiter.ttk import Button + box = Toplevel(parent) box.title("Test ReplaceDialog") x, y = map(int, parent.geometry().split('+')[1:]) diff --git a/Lib/idlelib/search.py b/Lib/idlelib/search.py index 17a9ef3651..4c2acef7b0 100644 --- a/Lib/idlelib/search.py +++ b/Lib/idlelib/search.py @@ -1,4 +1,4 @@ -from tkinter import * +from tkinter import TclError from idlelib import searchengine from idlelib.searchbase import SearchDialogBase @@ -72,7 +72,10 @@ class SearchDialog(SearchDialogBase): def _search_dialog(parent): # htest # - '''Display search test box.''' + "Display search test box." + from tkinter import Toplevel, Text + from tkinter.ttk import Button + box = Toplevel(parent) box.title("Test SearchDialog") x, y = map(int, parent.geometry().split('+')[1:]) @@ -82,9 +85,9 @@ def _search_dialog(parent): # htest # text.insert("insert","This is a sample string.\n"*5) def show_find(): - text.tag_add(SEL, "1.0", END) + text.tag_add('sel', '1.0', 'end') _setup(text).open(text) - text.tag_remove(SEL, "1.0", END) + text.tag_remove('sel', '1.0', 'end') button = Button(box, text="Search (selection ignored)", command=show_find) button.pack() diff --git a/Lib/idlelib/searchbase.py b/Lib/idlelib/searchbase.py index 9206bf5051..cfb40520e7 100644 --- a/Lib/idlelib/searchbase.py +++ b/Lib/idlelib/searchbase.py @@ -1,7 +1,7 @@ '''Define SearchDialogBase used by Search, Replace, and Grep dialogs.''' -from tkinter import (Toplevel, Frame, Entry, Label, Button, - Checkbutton, Radiobutton) +from tkinter import Toplevel, Frame +from tkinter.ttk import Entry, Label, Button, Checkbutton, Radiobutton class SearchDialogBase: '''Create most of a 3 or 4 row, 3 column search dialog. @@ -137,10 +137,8 @@ class SearchDialogBase: if self.needwrapbutton: options.append((engine.wrapvar, "Wrap around")) for var, label in options: - btn = Checkbutton(frame, anchor="w", variable=var, text=label) + btn = Checkbutton(frame, variable=var, text=label) btn.pack(side="left", fill="both") - if var.get(): - btn.select() return frame, options def create_other_buttons(self): @@ -153,11 +151,8 @@ class SearchDialogBase: var = self.engine.backvar others = [(1, 'Up'), (0, 'Down')] for val, label in others: - btn = Radiobutton(frame, anchor="w", - variable=var, value=val, text=label) + btn = Radiobutton(frame, variable=var, value=val, text=label) btn.pack(side="left", fill="both") - if var.get() == val: - btn.select() return frame, others def make_button(self, label, command, isdef=0): @@ -178,7 +173,26 @@ class SearchDialogBase: b = self.make_button("close", self.close) b.lower() + +class _searchbase(SearchDialogBase): # htest # + "Create auto-opening dialog with no text connection." + + def __init__(self, parent): + import re + from idlelib import searchengine + + self.root = parent + self.engine = searchengine.get(parent) + self.create_widgets() + print(parent.geometry()) + width,height, x,y = list(map(int, re.split('[x+]', parent.geometry()))) + self.top.geometry("+%d+%d" % (x + 40, y + 175)) + + def default_command(self): pass + if __name__ == '__main__': import unittest - unittest.main( - 'idlelib.idle_test.test_searchdialogbase', verbosity=2) + unittest.main('idlelib.idle_test.test_searchbase', verbosity=2, exit=False) + + from idlelib.idle_test.htest import run + run(_searchbase) -- cgit v1.2.1 From ee4c0cd7d94d6402892fb1917cabc7f4881af2fe Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Sun, 10 Jul 2016 20:30:43 -0400 Subject: IDLE NEWS items. --- Lib/idlelib/NEWS.txt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'Lib') diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index 4a69249060..4c2882a967 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -2,6 +2,24 @@ What's New in IDLE 3.6.0? =========================== *Release date: 2016-09-??* +- Issue #27477: IDLE search dialogs now use ttk widgets. + +- Issue #27173: Add 'IDLE Modern Unix' to the built-in key sets. + Make the default key set depend on the platform. + Add tests for the changes to the config module. + +- Issue #27452: make command line "idle-test> python test_help.py" work. + __file__ is relative when python is started in the file's directory. + +- Issue #27452: add line counter and crc to IDLE configHandler test dump. + +- Issue #27380: IDLE: add query.py with base Query dialog and ttk widgets. + Module had subclasses SectionName, ModuleName, and HelpSource, which are + used to get information from users by configdialog and file =>Load Module. + Each subclass has itw own validity checks. Using ModuleName allows users + to edit bad module names instead of starting over. + Add tests and delete the two files combined into the new one. + - Issue #27372: Test_idle no longer changes the locale. - Issue #27365: Allow non-ascii chars in IDLE NEWS.txt, for contributor names. -- cgit v1.2.1 From db444aae5a15c10cec73acc46ebb9b160a978556 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Mon, 11 Jul 2016 15:32:48 -0400 Subject: Update pydoc topics for 3.6.0a3 --- Lib/pydoc_data/topics.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'Lib') diff --git a/Lib/pydoc_data/topics.py b/Lib/pydoc_data/topics.py index be61bdd1e8..7378dc9eb8 100644 --- a/Lib/pydoc_data/topics.py +++ b/Lib/pydoc_data/topics.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Autogenerated by Sphinx on Mon Jun 13 16:49:58 2016 +# Autogenerated by Sphinx on Mon Jul 11 15:30:24 2016 topics = {'assert': '\n' 'The "assert" statement\n' '**********************\n' @@ -7288,13 +7288,17 @@ topics = {'assert': '\n' '\n' ' The tuple of base classes of a class object.\n' '\n' - 'class.__name__\n' + 'definition.__name__\n' '\n' - ' The name of the class or type.\n' + ' The name of the class, function, method, descriptor, or ' + 'generator\n' + ' instance.\n' '\n' - 'class.__qualname__\n' + 'definition.__qualname__\n' '\n' - ' The *qualified name* of the class or type.\n' + ' The *qualified name* of the class, function, method, ' + 'descriptor, or\n' + ' generator instance.\n' '\n' ' New in version 3.3.\n' '\n' -- cgit v1.2.1 From daf3dbb16a92e9d37441b1e5ea737a41be5bc07f Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Wed, 13 Jul 2016 21:13:29 -0700 Subject: Backed out changeset af29d89083b3 (closes #25548) (closes #27498) --- Lib/ctypes/test/test_structures.py | 10 +++++----- Lib/statistics.py | 8 ++++---- Lib/test/test_class.py | 10 ---------- Lib/test/test_cmd_line_script.py | 9 +-------- Lib/test/test_defaultdict.py | 2 +- Lib/test/test_descr.py | 4 ++-- Lib/test/test_descrtut.py | 31 +++++++++++++++---------------- Lib/test/test_doctest.py | 2 +- Lib/test/test_doctest3.txt | 2 +- Lib/test/test_functools.py | 33 +++++++++++++++++++++------------ Lib/test/test_generators.py | 37 ++++++++++++++++++------------------- Lib/test/test_genexps.py | 5 ++--- Lib/test/test_metaclass.py | 9 ++++----- Lib/test/test_pprint.py | 9 +++++---- Lib/test/test_reprlib.py | 6 +++--- Lib/test/test_statistics.py | 2 +- Lib/test/test_wsgiref.py | 6 +++--- Lib/test/test_xmlrpc.py | 4 ++-- 18 files changed, 89 insertions(+), 100 deletions(-) (limited to 'Lib') diff --git a/Lib/ctypes/test/test_structures.py b/Lib/ctypes/test/test_structures.py index 94a86ea6dd..60bae8322b 100644 --- a/Lib/ctypes/test/test_structures.py +++ b/Lib/ctypes/test/test_structures.py @@ -320,14 +320,14 @@ class StructureTestCase(unittest.TestCase): cls, msg = self.get_except(Person, b"Someone", (1, 2)) self.assertEqual(cls, RuntimeError) - self.assertRegex(msg, - r"\(Phone\) : " - r"expected bytes, int found") + self.assertEqual(msg, + "(Phone) : " + "expected bytes, int found") cls, msg = self.get_except(Person, b"Someone", (b"a", b"b", b"c")) self.assertEqual(cls, RuntimeError) - self.assertRegex(msg, - r"\(Phone\) : too many initializers") + self.assertEqual(msg, + "(Phone) : too many initializers") def test_huge_field_name(self): # issue12881: segfault with large structure field names diff --git a/Lib/statistics.py b/Lib/statistics.py index 63adc1a44f..b081b5a006 100644 --- a/Lib/statistics.py +++ b/Lib/statistics.py @@ -131,23 +131,23 @@ def _sum(data, start=0): -------- >>> _sum([3, 2.25, 4.5, -0.5, 1.0], 0.75) - (, Fraction(11, 1), 5) + (, Fraction(11, 1), 5) Some sources of round-off error will be avoided: >>> _sum([1e50, 1, -1e50] * 1000) # Built-in sum returns zero. - (, Fraction(1000, 1), 3000) + (, Fraction(1000, 1), 3000) Fractions and Decimals are also supported: >>> from fractions import Fraction as F >>> _sum([F(2, 3), F(7, 5), F(1, 4), F(5, 6)]) - (, Fraction(63, 20), 4) + (, Fraction(63, 20), 4) >>> from decimal import Decimal as D >>> data = [D("0.1375"), D("0.2108"), D("0.3061"), D("0.0419")] >>> _sum(data) - (, Fraction(6963, 10000), 4) + (, Fraction(6963, 10000), 4) Mixed types are currently treated as an error, except that int is allowed. diff --git a/Lib/test/test_class.py b/Lib/test/test_class.py index e02a3cbb97..4d554a397b 100644 --- a/Lib/test/test_class.py +++ b/Lib/test/test_class.py @@ -568,15 +568,5 @@ class ClassTests(unittest.TestCase): a = A(hash(A.f)^(-1)) hash(a.f) - def test_class_repr(self): - # We should get the address of the object - class A: - pass - - result = repr(A) - self.assertRegex(result, - ".A'" - " at 0x.+>") - if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_cmd_line_script.py b/Lib/test/test_cmd_line_script.py index e7c6351317..01fb7dcf40 100644 --- a/Lib/test/test_cmd_line_script.py +++ b/Lib/test/test_cmd_line_script.py @@ -127,10 +127,7 @@ class CmdLineTest(unittest.TestCase): print(printed_package) print(printed_argv0) print(printed_cwd) - expected = printed_loader.encode('utf-8') - idx = expected.find(b"at 0x") - expected = expected[:idx] - self.assertIn(expected, data) + self.assertIn(printed_loader.encode('utf-8'), data) self.assertIn(printed_file.encode('utf-8'), data) self.assertIn(printed_package.encode('utf-8'), data) self.assertIn(printed_argv0.encode('utf-8'), data) @@ -161,8 +158,6 @@ class CmdLineTest(unittest.TestCase): def test_dash_c_loader(self): rc, out, err = assert_python_ok("-c", "print(__loader__)") expected = repr(importlib.machinery.BuiltinImporter).encode("utf-8") - idx = expected.find(b"at 0x") - expected = expected[:idx] self.assertIn(expected, out) def test_stdin_loader(self): @@ -176,8 +171,6 @@ class CmdLineTest(unittest.TestCase): finally: out = kill_python(p) expected = repr(importlib.machinery.BuiltinImporter).encode("utf-8") - idx = expected.find(b"at 0x") - expected = expected[:idx] self.assertIn(expected, out) @contextlib.contextmanager diff --git a/Lib/test/test_defaultdict.py b/Lib/test/test_defaultdict.py index 5bb4e12278..a90bc2b488 100644 --- a/Lib/test/test_defaultdict.py +++ b/Lib/test/test_defaultdict.py @@ -65,7 +65,7 @@ class TestDefaultDict(unittest.TestCase): d2 = defaultdict(int) self.assertEqual(d2.default_factory, int) d2[12] = 42 - self.assertRegex(repr(d2), r"defaultdict\(, {12: 42}\)") + self.assertEqual(repr(d2), "defaultdict(, {12: 42})") def foo(): return 43 d3 = defaultdict(foo) self.assertTrue(d3.default_factory is foo) diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 954ef2d3b3..0a5ecd5a0d 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -4544,9 +4544,9 @@ order (MRO) for bases """ pass foo = Foo() self.assertRegex(repr(foo.method), # access via instance - r">") + r">") self.assertRegex(repr(Foo.method), # access via the class - r">") + r">") class MyCallable: diff --git a/Lib/test/test_descrtut.py b/Lib/test/test_descrtut.py index 80cfa41ecb..506d1abeb6 100644 --- a/Lib/test/test_descrtut.py +++ b/Lib/test/test_descrtut.py @@ -37,16 +37,16 @@ test_1 = """ Here's the new type at work: >>> print(defaultdict) # show our type - + >>> print(type(defaultdict)) # its metatype - + >>> a = defaultdict(default=0.0) # create an instance >>> print(a) # show the instance {} >>> print(type(a)) # show its type - + >>> print(a.__class__) # show its class - + >>> print(type(a) is a.__class__) # its type is its class True >>> a[1] = 3.25 # modify the instance @@ -149,11 +149,11 @@ Introspecting instances of built-in types For instance of built-in types, x.__class__ is now the same as type(x): >>> type([]) - + >>> [].__class__ - + >>> list - + >>> isinstance([], list) True >>> isinstance([], dict) @@ -258,19 +258,19 @@ implicit first argument that is the *class* for which they are invoked. ... print("classmethod", cls, y) >>> C.foo(1) - classmethod 1 + classmethod 1 >>> c = C() >>> c.foo(1) - classmethod 1 + classmethod 1 >>> class D(C): ... pass >>> D.foo(1) - classmethod 1 + classmethod 1 >>> d = D() >>> d.foo(1) - classmethod 1 + classmethod 1 This prints "classmethod __main__.D 1" both times; in other words, the class passed as the first argument of foo() is the class involved in the @@ -286,11 +286,11 @@ But notice this: >>> E.foo(1) E.foo() called - classmethod 1 + classmethod 1 >>> e = E() >>> e.foo(1) E.foo() called - classmethod 1 + classmethod 1 In this example, the call to C.foo() from E.foo() will see class C as its first argument, not class E. This is to be expected, since the call @@ -350,7 +350,7 @@ Hmm -- property is builtin now, so let's try it that way too. >>> del property # unmask the builtin >>> property - + >>> class C(object): ... def __init__(self): @@ -478,8 +478,7 @@ def test_main(verbose=None): # business is used the name can change depending on how the test is # invoked. from test import support, test_descrtut - import doctest - support.run_doctest(test_descrtut, verbose, optionflags=doctest.ELLIPSIS) + support.run_doctest(test_descrtut, verbose) # This part isn't needed for regrtest, but for running the test directly. if __name__ == "__main__": diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py index b416cbb0aa..a9520a5572 100644 --- a/Lib/test/test_doctest.py +++ b/Lib/test/test_doctest.py @@ -2338,7 +2338,7 @@ def test_DocFileSuite(): `__file__` global, which is set to the name of the file containing the tests: - >>> suite = doctest.DocFileSuite('test_doctest3.txt', optionflags=doctest.ELLIPSIS) + >>> suite = doctest.DocFileSuite('test_doctest3.txt') >>> suite.run(unittest.TestResult()) diff --git a/Lib/test/test_doctest3.txt b/Lib/test/test_doctest3.txt index 380ea76e4c..dd8557e57a 100644 --- a/Lib/test/test_doctest3.txt +++ b/Lib/test/test_doctest3.txt @@ -2,4 +2,4 @@ Here we check that `__file__` is provided: >>> type(__file__) - + diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index e2ab6549e0..06eacfb97d 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -1758,10 +1758,13 @@ class TestSingleDispatch(unittest.TestCase): c.Container.register(P) with self.assertRaises(RuntimeError) as re_one: g(p) - self.assertIn("Ambiguous dispatch:", str(re_one.exception)) - self.assertIn(" " + "or "), + ("Ambiguous dispatch: " + "or ")), + ) class Q(c.Sized): def __len__(self): return 0 @@ -1787,10 +1790,13 @@ class TestSingleDispatch(unittest.TestCase): # perspective. with self.assertRaises(RuntimeError) as re_two: h(c.defaultdict(lambda: 0)) - self.assertIn("Ambiguous dispatch:", str(re_two.exception)) - self.assertIn(" " + "or "), + ("Ambiguous dispatch: " + "or ")), + ) class R(c.defaultdict): pass c.MutableSequence.register(R) @@ -1824,10 +1830,13 @@ class TestSingleDispatch(unittest.TestCase): # There is no preference for registered versus inferred ABCs. with self.assertRaises(RuntimeError) as re_three: h(u) - self.assertIn("Ambiguous dispatch:", str(re_three.exception)) - self.assertIn(" " + "or "), + ("Ambiguous dispatch: " + "or ")), + ) class V(c.Sized, S): def __len__(self): return 0 diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py index c193301b27..f4b33afe14 100644 --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -671,10 +671,10 @@ From the Iterators list, about the types of these things. ... yield 1 ... >>> type(g) - + >>> i = g() >>> type(i) - + >>> [s for s in dir(i) if not s.startswith('_')] ['close', 'gi_code', 'gi_frame', 'gi_running', 'gi_yieldfrom', 'send', 'throw'] >>> from test.support import HAVE_DOCSTRINGS @@ -691,7 +691,7 @@ And more, added later. >>> i.gi_running 0 >>> type(i.gi_frame) - + >>> i.gi_running = 42 Traceback (most recent call last): ... @@ -1066,27 +1066,27 @@ These are fine: >>> def f(): ... yield >>> type(f()) - + >>> def f(): ... if 0: ... yield >>> type(f()) - + >>> def f(): ... if 0: ... yield 1 >>> type(f()) - + >>> def f(): ... if "": ... yield None >>> type(f()) - + >>> def f(): ... return @@ -1110,7 +1110,7 @@ These are fine: ... x = 1 ... return >>> type(f()) - + >>> def f(): ... if 0: @@ -1118,7 +1118,7 @@ These are fine: ... yield 1 ... >>> type(f()) - + >>> def f(): ... if 0: @@ -1128,7 +1128,7 @@ These are fine: ... def f(self): ... yield 2 >>> type(f()) - + >>> def f(): ... if 0: @@ -1136,7 +1136,7 @@ These are fine: ... if 0: ... yield 2 >>> type(f()) - + This one caused a crash (see SF bug 567538): @@ -1791,7 +1791,7 @@ And a more sane, but still weird usage: >>> def f(): list(i for i in [(yield 26)]) >>> type(f()) - + A yield expression with augmented assignment. @@ -2047,25 +2047,25 @@ enclosing function a generator: >>> def f(): x += yield >>> type(f()) - + >>> def f(): x = yield >>> type(f()) - + >>> def f(): lambda x=(yield): 1 >>> type(f()) - + >>> def f(): x=(i for i in (yield) if (yield)) >>> type(f()) - + >>> def f(d): d[(yield "a")] = d[(yield "b")] = 27 >>> data = [1,2] >>> g = f(data) >>> type(g) - + >>> g.send(None) 'a' >>> data @@ -2174,9 +2174,8 @@ __test__ = {"tut": tutorial_tests, # so this works as expected in both ways of running regrtest. def test_main(verbose=None): from test import support, test_generators - import doctest support.run_unittest(__name__) - support.run_doctest(test_generators, verbose, optionflags=doctest.ELLIPSIS) + support.run_doctest(test_generators, verbose) # This part isn't needed for regrtest, but for running the test directly. if __name__ == "__main__": diff --git a/Lib/test/test_genexps.py b/Lib/test/test_genexps.py index c5e10dda8c..fb531d6d47 100644 --- a/Lib/test/test_genexps.py +++ b/Lib/test/test_genexps.py @@ -27,7 +27,7 @@ Test first class >>> g = (i*i for i in range(4)) >>> type(g) - + >>> list(g) [0, 1, 4, 9] @@ -269,8 +269,7 @@ else: def test_main(verbose=None): from test import support from test import test_genexps - import doctest - support.run_doctest(test_genexps, verbose, optionflags=doctest.ELLIPSIS) + support.run_doctest(test_genexps, verbose) # verify reference counting if verbose and hasattr(sys, "gettotalrefcount"): diff --git a/Lib/test/test_metaclass.py b/Lib/test/test_metaclass.py index be683dd1a1..e6fe20a107 100644 --- a/Lib/test/test_metaclass.py +++ b/Lib/test/test_metaclass.py @@ -78,7 +78,7 @@ Also pass another keyword. >>> class C(object, metaclass=M, other="haha"): ... pass ... - Prepare called: ('C', (,)) {'other': 'haha'} + Prepare called: ('C', (,)) {'other': 'haha'} New called: {'other': 'haha'} >>> C.__class__ is M True @@ -104,7 +104,7 @@ Use various combinations of explicit keywords and **kwds. >>> kwds = {'metaclass': M, 'other': 'haha'} >>> class C(*bases, **kwds): pass ... - Prepare called: ('C', (,)) {'other': 'haha'} + Prepare called: ('C', (,)) {'other': 'haha'} New called: {'other': 'haha'} >>> C.__class__ is M True @@ -114,7 +114,7 @@ Use various combinations of explicit keywords and **kwds. >>> kwds = {'other': 'haha'} >>> class C(B, metaclass=M, *bases, **kwds): pass ... - Prepare called: ('C', (, )) {'other': 'haha'} + Prepare called: ('C', (, )) {'other': 'haha'} New called: {'other': 'haha'} >>> C.__class__ is M True @@ -259,8 +259,7 @@ else: def test_main(verbose=False): from test import support from test import test_metaclass - import doctest - support.run_doctest(test_metaclass, verbose, optionflags=doctest.ELLIPSIS) + support.run_doctest(test_metaclass, verbose) if __name__ == "__main__": test_main(verbose=True) diff --git a/Lib/test/test_pprint.py b/Lib/test/test_pprint.py index 2283923cee..7ebc298337 100644 --- a/Lib/test/test_pprint.py +++ b/Lib/test/test_pprint.py @@ -848,11 +848,12 @@ bytearray(b'\\x00\\x01\\x02\\x03' def test_default_dict(self): d = collections.defaultdict(int) - self.assertRegex(pprint.pformat(d, width=1), r"defaultdict\(, {}\)") + self.assertEqual(pprint.pformat(d, width=1), "defaultdict(, {})") words = 'the quick brown fox jumped over a lazy dog'.split() d = collections.defaultdict(int, zip(words, itertools.count())) - self.assertRegex(pprint.pformat(d), -r"""defaultdict\(, + self.assertEqual(pprint.pformat(d), +"""\ +defaultdict(, {'a': 6, 'brown': 2, 'dog': 8, @@ -861,7 +862,7 @@ r"""defaultdict\(, 'lazy': 7, 'over': 5, 'quick': 1, - 'the': 0}\)""") + 'the': 0})""") def test_counter(self): d = collections.Counter() diff --git a/Lib/test/test_reprlib.py b/Lib/test/test_reprlib.py index 2ecea0221e..4bf91945ea 100644 --- a/Lib/test/test_reprlib.py +++ b/Lib/test/test_reprlib.py @@ -292,8 +292,8 @@ class foo(object): ''') importlib.invalidate_caches() from areallylongpackageandmodulenametotestreprtruncation.areallylongpackageandmodulenametotestreprtruncation import foo - self.assertRegex(repr(foo.foo), - r"" % foo.__name__) + eq(repr(foo.foo), + "" % foo.__name__) @unittest.skip('need a suitable object') def test_object(self): @@ -310,7 +310,7 @@ class bar: importlib.invalidate_caches() from areallylongpackageandmodulenametotestreprtruncation.areallylongpackageandmodulenametotestreprtruncation import bar # Module name may be prefixed with "test.", depending on how run. - self.assertRegex(repr(bar.bar), r"" % bar.__name__) + self.assertEqual(repr(bar.bar), "" % bar.__name__) def test_instance(self): self._check_path_limitations('baz') diff --git a/Lib/test/test_statistics.py b/Lib/test/test_statistics.py index 4e03d983d3..cccc1b9343 100644 --- a/Lib/test/test_statistics.py +++ b/Lib/test/test_statistics.py @@ -659,7 +659,7 @@ class DocTests(unittest.TestCase): @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -OO and above") def test_doc_tests(self): - failed, tried = doctest.testmod(statistics, optionflags=doctest.ELLIPSIS) + failed, tried = doctest.testmod(statistics) self.assertGreater(tried, 0) self.assertEqual(failed, 0) diff --git a/Lib/test/test_wsgiref.py b/Lib/test/test_wsgiref.py index 3c5a27a12e..61a750c622 100644 --- a/Lib/test/test_wsgiref.py +++ b/Lib/test/test_wsgiref.py @@ -165,10 +165,10 @@ class IntegrationTests(TestCase): self.assertTrue(out.endswith( b"A server error occurred. Please contact the administrator." )) - self.assertRegex( + self.assertEqual( err.splitlines()[-2], - r"AssertionError: Headers \(\('Content-Type', 'text/plain'\)\) must" - r" be of type list: " + "AssertionError: Headers (('Content-Type', 'text/plain')) must" + " be of type list: " ) def test_status_validation_errors(self): diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py index f2fdc44cbe..0773a86984 100644 --- a/Lib/test/test_xmlrpc.py +++ b/Lib/test/test_xmlrpc.py @@ -775,8 +775,8 @@ class SimpleServerTestCase(BaseServerTestCase): # 'method "this_is_not_exists" is not supported'>}] self.assertEqual(result.results[0]['faultCode'], 1) - self.assertRegex(result.results[0]['faultString'], - ':method "this_is_not_exists" ' + self.assertEqual(result.results[0]['faultString'], + ':method "this_is_not_exists" ' 'is not supported') except (xmlrpclib.ProtocolError, OSError) as e: # ignore failures due to non-blocking socket 'unavailable' errors -- cgit v1.2.1 From e30c0dfb84728e8c3f9e2f695b64071e53e1811b Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Fri, 15 Jul 2016 02:43:03 -0400 Subject: Issue #25507: Move 4 objects from pyshell to run and switch inports. This removes one problem inport and reduces len(sys.modules) by 37. --- Lib/idlelib/pyshell.py | 101 +------------------------------------ Lib/idlelib/run.py | 121 +++++++++++++++++++++++++++++++++++++++++---- Lib/idlelib/stackviewer.py | 2 +- 3 files changed, 114 insertions(+), 110 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py index 82e77f97a1..28584acfbb 100755 --- a/Lib/idlelib/pyshell.py +++ b/Lib/idlelib/pyshell.py @@ -25,7 +25,6 @@ import sys import threading import time import tokenize -import io import linecache from code import InteractiveInterpreter @@ -37,6 +36,7 @@ from idlelib.colorizer import ColorDelegator from idlelib.undo import UndoDelegator from idlelib.outwin import OutputWindow from idlelib.config import idleConf +from idlelib.run import idle_formatwarning, PseudoInputFile, PseudoOutputFile from idlelib import rpc from idlelib import debugger from idlelib import debugger_r @@ -52,19 +52,6 @@ PORT = 0 # someday pass in host, port for remote debug capability warning_stream = sys.__stderr__ # None, at least on Windows, if no console. import warnings -def idle_formatwarning(message, category, filename, lineno, line=None): - """Format warnings the IDLE way.""" - - s = "\nWarning (from warnings module):\n" - s += ' File \"%s\", line %s\n' % (filename, lineno) - if line is None: - line = linecache.getline(filename, lineno) - line = line.strip() - if line: - s += " %s\n" % line - s += "%s: %s\n" % (category.__name__, message) - return s - def idle_showwarning( message, category, filename, lineno, file=None, line=None): """Show Idle-format warning (after replacing warnings.showwarning). @@ -1316,92 +1303,6 @@ class PyShell(OutputWindow): return 'disabled' return super().rmenu_check_paste() -class PseudoFile(io.TextIOBase): - - def __init__(self, shell, tags, encoding=None): - self.shell = shell - self.tags = tags - self._encoding = encoding - - @property - def encoding(self): - return self._encoding - - @property - def name(self): - return '<%s>' % self.tags - - def isatty(self): - return True - - -class PseudoOutputFile(PseudoFile): - - def writable(self): - return True - - def write(self, s): - if self.closed: - raise ValueError("write to closed file") - if type(s) is not str: - if not isinstance(s, str): - raise TypeError('must be str, not ' + type(s).__name__) - # See issue #19481 - s = str.__str__(s) - return self.shell.write(s, self.tags) - - -class PseudoInputFile(PseudoFile): - - def __init__(self, shell, tags, encoding=None): - PseudoFile.__init__(self, shell, tags, encoding) - self._line_buffer = '' - - def readable(self): - return True - - def read(self, size=-1): - if self.closed: - raise ValueError("read from closed file") - if size is None: - size = -1 - elif not isinstance(size, int): - raise TypeError('must be int, not ' + type(size).__name__) - result = self._line_buffer - self._line_buffer = '' - if size < 0: - while True: - line = self.shell.readline() - if not line: break - result += line - else: - while len(result) < size: - line = self.shell.readline() - if not line: break - result += line - self._line_buffer = result[size:] - result = result[:size] - return result - - def readline(self, size=-1): - if self.closed: - raise ValueError("read from closed file") - if size is None: - size = -1 - elif not isinstance(size, int): - raise TypeError('must be int, not ' + type(size).__name__) - line = self._line_buffer or self.shell.readline() - if size < 0: - size = len(line) - eol = line.find('\n', 0, size) - if eol >= 0: - size = eol + 1 - self._line_buffer = line[size:] - return line[:size] - - def close(self): - self.shell.close() - def fix_x11_paste(root): "Make paste replace selection on x11. See issue #5124." diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py index eb34944eb3..10ede99e33 100644 --- a/Lib/idlelib/run.py +++ b/Lib/idlelib/run.py @@ -1,10 +1,11 @@ -import sys +import io import linecache -import time -import traceback +import queue +import sys import _thread as thread import threading -import queue +import time +import traceback import tkinter from idlelib import calltips @@ -14,7 +15,6 @@ from idlelib import debugger_r from idlelib import debugobj_r from idlelib import stackviewer from idlelib import rpc -from idlelib import pyshell from idlelib import iomenu import __main__ @@ -23,6 +23,19 @@ LOCALHOST = '127.0.0.1' import warnings +def idle_formatwarning(message, category, filename, lineno, line=None): + """Format warnings the IDLE way.""" + + s = "\nWarning (from warnings module):\n" + s += ' File \"%s\", line %s\n' % (filename, lineno) + if line is None: + line = linecache.getline(filename, lineno) + line = line.strip() + if line: + s += " %s\n" % line + s += "%s: %s\n" % (category.__name__, message) + return s + def idle_showwarning_subproc( message, category, filename, lineno, file=None, line=None): """Show Idle-format warning after replacing warnings.showwarning. @@ -32,7 +45,7 @@ def idle_showwarning_subproc( if file is None: file = sys.stderr try: - file.write(pyshell.idle_formatwarning( + file.write(idle_formatwarning( message, category, filename, lineno, line)) except IOError: pass # the file (probably stderr) is invalid - this warning gets lost. @@ -291,6 +304,96 @@ class MyRPCServer(rpc.RPCServer): quitting = True thread.interrupt_main() + +# Pseudofiles for shell-remote communication (also used in pyshell) + +class PseudoFile(io.TextIOBase): + + def __init__(self, shell, tags, encoding=None): + self.shell = shell + self.tags = tags + self._encoding = encoding + + @property + def encoding(self): + return self._encoding + + @property + def name(self): + return '<%s>' % self.tags + + def isatty(self): + return True + + +class PseudoOutputFile(PseudoFile): + + def writable(self): + return True + + def write(self, s): + if self.closed: + raise ValueError("write to closed file") + if type(s) is not str: + if not isinstance(s, str): + raise TypeError('must be str, not ' + type(s).__name__) + # See issue #19481 + s = str.__str__(s) + return self.shell.write(s, self.tags) + + +class PseudoInputFile(PseudoFile): + + def __init__(self, shell, tags, encoding=None): + PseudoFile.__init__(self, shell, tags, encoding) + self._line_buffer = '' + + def readable(self): + return True + + def read(self, size=-1): + if self.closed: + raise ValueError("read from closed file") + if size is None: + size = -1 + elif not isinstance(size, int): + raise TypeError('must be int, not ' + type(size).__name__) + result = self._line_buffer + self._line_buffer = '' + if size < 0: + while True: + line = self.shell.readline() + if not line: break + result += line + else: + while len(result) < size: + line = self.shell.readline() + if not line: break + result += line + self._line_buffer = result[size:] + result = result[:size] + return result + + def readline(self, size=-1): + if self.closed: + raise ValueError("read from closed file") + if size is None: + size = -1 + elif not isinstance(size, int): + raise TypeError('must be int, not ' + type(size).__name__) + line = self._line_buffer or self.shell.readline() + if size < 0: + size = len(line) + eol = line.find('\n', 0, size) + if eol >= 0: + size = eol + 1 + self._line_buffer = line[size:] + return line[:size] + + def close(self): + self.shell.close() + + class MyHandler(rpc.RPCHandler): def handle(self): @@ -298,11 +401,11 @@ class MyHandler(rpc.RPCHandler): executive = Executive(self) self.register("exec", executive) self.console = self.get_remote_proxy("console") - sys.stdin = pyshell.PseudoInputFile(self.console, "stdin", + sys.stdin = PseudoInputFile(self.console, "stdin", iomenu.encoding) - sys.stdout = pyshell.PseudoOutputFile(self.console, "stdout", + sys.stdout = PseudoOutputFile(self.console, "stdout", iomenu.encoding) - sys.stderr = pyshell.PseudoOutputFile(self.console, "stderr", + sys.stderr = PseudoOutputFile(self.console, "stderr", iomenu.encoding) sys.displayhook = rpc.displayhook diff --git a/Lib/idlelib/stackviewer.py b/Lib/idlelib/stackviewer.py index 657f0a9b01..c8c802ce2a 100644 --- a/Lib/idlelib/stackviewer.py +++ b/Lib/idlelib/stackviewer.py @@ -6,7 +6,6 @@ import tkinter as tk from idlelib.tree import TreeNode, TreeItem, ScrolledCanvas from idlelib.debugobj import ObjectTreeItem, make_objecttreeitem -from idlelib.pyshell import PyShellFileList def StackBrowser(root, flist=None, tb=None, top=None): if top is None: @@ -121,6 +120,7 @@ class VariablesTreeItem(ObjectTreeItem): return sublist def _stack_viewer(parent): # htest # + from idlelib.pyshell import PyShellFileList top = tk.Toplevel(parent) top.title("Test StackViewer") x, y = map(int, parent.geometry().split('+')[1:]) -- cgit v1.2.1 From 03e4d0c5c6c7af36230ec72d1ad7842cf1e887ef Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Fri, 15 Jul 2016 10:41:49 -0700 Subject: Issue #27512: Don't segfault when os.fspath() calls an object whose __fspath__() raises an exception. Thanks to Xiang Zhang for the patch. --- Lib/test/test_os.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 4ade4aa9db..ecd2efb0e9 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -3122,7 +3122,10 @@ class TestPEP519(unittest.TestCase): def __init__(self, path=''): self.path = path def __fspath__(self): - return self.path + if isinstance(self.path, BaseException): + raise self.path + else: + return self.path def test_return_bytes(self): for b in b'hello', b'goodbye', b'some/path/and/file': @@ -3145,18 +3148,25 @@ class TestPEP519(unittest.TestCase): self.assertTrue(issubclass(self.PathLike, os.PathLike)) self.assertTrue(isinstance(self.PathLike(), os.PathLike)) - with self.assertRaises(TypeError): - self.fspath(self.PathLike(42)) - def test_garbage_in_exception_out(self): vapor = type('blah', (), {}) for o in int, type, os, vapor(): self.assertRaises(TypeError, self.fspath, o) def test_argument_required(self): - with self.assertRaises(TypeError): - self.fspath() - + self.assertRaises(TypeError, self.fspath) + + def test_bad_pathlike(self): + # __fspath__ returns a value other than str or bytes. + self.assertRaises(TypeError, self.fspath, self.PathLike(42)) + # __fspath__ attribute that is not callable. + c = type('foo', (), {}) + c.__fspath__ = 1 + self.assertRaises(TypeError, self.fspath, c()) + # __fspath__ raises an exception. + c.__fspath__ = lambda self: self.__not_exist + self.assertRaises(ZeroDivisionError, self.fspath, + self.PathLike(ZeroDivisionError)) # Only test if the C version is provided, otherwise TestPEP519 already tested # the pure Python implementation. -- cgit v1.2.1 From 238a55125d241591cac906f008f7ce7da503eb78 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Fri, 15 Jul 2016 11:26:53 -0700 Subject: Fix a failing test introduced as part of issue #27512 --- Lib/test/test_os.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index ecd2efb0e9..6493d76c9a 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -3164,9 +3164,8 @@ class TestPEP519(unittest.TestCase): c.__fspath__ = 1 self.assertRaises(TypeError, self.fspath, c()) # __fspath__ raises an exception. - c.__fspath__ = lambda self: self.__not_exist self.assertRaises(ZeroDivisionError, self.fspath, - self.PathLike(ZeroDivisionError)) + self.PathLike(ZeroDivisionError())) # Only test if the C version is provided, otherwise TestPEP519 already tested # the pure Python implementation. -- cgit v1.2.1 From 1968105b8f73b8e9efeee7a8735df76f6ad06b29 Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Fri, 22 Jul 2016 12:15:29 +0200 Subject: Issue #27472: Add test.support.unix_shell as the path to the default shell. --- Lib/distutils/tests/test_spawn.py | 11 ++++++----- Lib/test/support/__init__.py | 7 ++++++- Lib/test/test_os.py | 13 ++++++++----- Lib/test/test_subprocess.py | 4 ++-- 4 files changed, 22 insertions(+), 13 deletions(-) (limited to 'Lib') diff --git a/Lib/distutils/tests/test_spawn.py b/Lib/distutils/tests/test_spawn.py index f507ef7750..5edc24a3a1 100644 --- a/Lib/distutils/tests/test_spawn.py +++ b/Lib/distutils/tests/test_spawn.py @@ -1,7 +1,8 @@ """Tests for distutils.spawn.""" import unittest +import sys import os -from test.support import run_unittest +from test.support import run_unittest, unix_shell from distutils.spawn import _nt_quote_args from distutils.spawn import spawn @@ -29,9 +30,9 @@ class SpawnTestCase(support.TempdirManager, # creating something executable # through the shell that returns 1 - if os.name == 'posix': + if sys.platform != 'win32': exe = os.path.join(tmpdir, 'foo.sh') - self.write_file(exe, '#!/bin/sh\nexit 1') + self.write_file(exe, '#!%s\nexit 1' % unix_shell) else: exe = os.path.join(tmpdir, 'foo.bat') self.write_file(exe, 'exit 1') @@ -40,9 +41,9 @@ class SpawnTestCase(support.TempdirManager, self.assertRaises(DistutilsExecError, spawn, [exe]) # now something that works - if os.name == 'posix': + if sys.platform != 'win32': exe = os.path.join(tmpdir, 'foo.sh') - self.write_file(exe, '#!/bin/sh\nexit 0') + self.write_file(exe, '#!%s\nexit 0' % unix_shell) else: exe = os.path.join(tmpdir, 'foo.bat') self.write_file(exe, 'exit 0') diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 2b2966876f..aa6725feec 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -92,7 +92,7 @@ __all__ = [ "anticipate_failure", "load_package_tests", "detect_api_mismatch", "check__all__", # sys - "is_jython", "is_android", "check_impl_detail", + "is_jython", "is_android", "check_impl_detail", "unix_shell", # network "HOST", "IPV6_ENABLED", "find_unused_port", "bind_port", "open_urlresource", # processes @@ -736,6 +736,11 @@ is_jython = sys.platform.startswith('java') is_android = bool(sysconfig.get_config_var('ANDROID_API_LEVEL')) +if sys.platform != 'win32': + unix_shell = '/system/bin/sh' if is_android else '/bin/sh' +else: + unix_shell = None + # Filename used for testing if os.name == 'java': # Jython disallows @ in module names diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 6493d76c9a..aa9b538748 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -64,6 +64,7 @@ except ImportError: INT_MAX = PY_SSIZE_T_MAX = sys.maxsize from test.support.script_helper import assert_python_ok +from test.support import unix_shell root_in_posix = False @@ -670,18 +671,20 @@ class EnvironTests(mapping_tests.BasicTestMappingProtocol): return os.environ # Bug 1110478 - @unittest.skipUnless(os.path.exists('/bin/sh'), 'requires /bin/sh') + @unittest.skipUnless(unix_shell and os.path.exists(unix_shell), + 'requires a shell') def test_update2(self): os.environ.clear() os.environ.update(HELLO="World") - with os.popen("/bin/sh -c 'echo $HELLO'") as popen: + with os.popen("%s -c 'echo $HELLO'" % unix_shell) as popen: value = popen.read().strip() self.assertEqual(value, "World") - @unittest.skipUnless(os.path.exists('/bin/sh'), 'requires /bin/sh') + @unittest.skipUnless(unix_shell and os.path.exists(unix_shell), + 'requires a shell') def test_os_popen_iter(self): - with os.popen( - "/bin/sh -c 'echo \"line1\nline2\nline3\"'") as popen: + with os.popen("%s -c 'echo \"line1\nline2\nline3\"'" + % unix_shell) as popen: it = iter(popen) self.assertEqual(next(it), "line1\n") self.assertEqual(next(it), "line2\n") diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index ba91bbc081..092e2ce753 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1579,7 +1579,7 @@ class POSIXProcessTestCase(BaseTestCase): fd, fname = tempfile.mkstemp() # reopen in text mode with open(fd, "w", errors="surrogateescape") as fobj: - fobj.write("#!/bin/sh\n") + fobj.write("#!%s\n" % support.unix_shell) fobj.write("exec '%s' -c 'import sys; sys.exit(47)'\n" % sys.executable) os.chmod(fname, 0o700) @@ -1624,7 +1624,7 @@ class POSIXProcessTestCase(BaseTestCase): fd, fname = tempfile.mkstemp() # reopen in text mode with open(fd, "w", errors="surrogateescape") as fobj: - fobj.write("#!/bin/sh\n") + fobj.write("#!%s\n" % support.unix_shell) fobj.write("exec '%s' -c 'import sys; sys.exit(47)'\n" % sys.executable) os.chmod(fname, 0o700) -- cgit v1.2.1 From 9295250b0a4a41917c6ef3a6b32ff239391e24a7 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Fri, 22 Jul 2016 16:27:31 +0100 Subject: Closes #26559: Allow configuring flush-on-close behaviour of MemoryHandler. --- Lib/logging/handlers.py | 20 +++++++++++++++----- Lib/test/test_logging.py | 32 +++++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 6 deletions(-) (limited to 'Lib') diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py index c0748a8853..296d6cfa30 100644 --- a/Lib/logging/handlers.py +++ b/Lib/logging/handlers.py @@ -1,4 +1,4 @@ -# Copyright 2001-2015 by Vinay Sajip. All Rights Reserved. +# Copyright 2001-2016 by Vinay Sajip. All Rights Reserved. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose and without fee is hereby granted, @@ -18,7 +18,7 @@ Additional handlers for the logging package for Python. The core package is based on PEP 282 and comments thereto in comp.lang.python. -Copyright (C) 2001-2015 Vinay Sajip. All Rights Reserved. +Copyright (C) 2001-2016 Vinay Sajip. All Rights Reserved. To use, simply 'import logging.handlers' and log away! """ @@ -1238,17 +1238,25 @@ class MemoryHandler(BufferingHandler): flushing them to a target handler. Flushing occurs whenever the buffer is full, or when an event of a certain severity or greater is seen. """ - def __init__(self, capacity, flushLevel=logging.ERROR, target=None): + def __init__(self, capacity, flushLevel=logging.ERROR, target=None, + flushOnClose=True): """ Initialize the handler with the buffer size, the level at which flushing should occur and an optional target. Note that without a target being set either here or via setTarget(), a MemoryHandler is no use to anyone! + + The ``flushOnClose`` argument is ``True`` for backward compatibility + reasons - the old behaviour is that when the handler is closed, the + buffer is flushed, even if the flush level hasn't been exceeded nor the + capacity exceeded. To prevent this, set ``flushOnClose`` to ``False``. """ BufferingHandler.__init__(self, capacity) self.flushLevel = flushLevel self.target = target + # See Issue #26559 for why this has been added + self.flushOnClose = flushOnClose def shouldFlush(self, record): """ @@ -1282,10 +1290,12 @@ class MemoryHandler(BufferingHandler): def close(self): """ - Flush, set the target to None and lose the buffer. + Flush, if appropriately configured, set the target to None and lose the + buffer. """ try: - self.flush() + if self.flushOnClose: + self.flush() finally: self.acquire() try: diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 062df0f746..9e9a439eab 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -958,7 +958,7 @@ class MemoryHandlerTest(BaseTest): def setUp(self): BaseTest.setUp(self) self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING, - self.root_hdlr) + self.root_hdlr) self.mem_logger = logging.getLogger('mem') self.mem_logger.propagate = 0 self.mem_logger.addHandler(self.mem_hdlr) @@ -995,6 +995,36 @@ class MemoryHandlerTest(BaseTest): self.mem_logger.debug(self.next_message()) self.assert_log_lines(lines) + def test_flush_on_close(self): + """ + Test that the flush-on-close configuration works as expected. + """ + self.mem_logger.debug(self.next_message()) + self.assert_log_lines([]) + self.mem_logger.info(self.next_message()) + self.assert_log_lines([]) + self.mem_logger.removeHandler(self.mem_hdlr) + # Default behaviour is to flush on close. Check that it happens. + self.mem_hdlr.close() + lines = [ + ('DEBUG', '1'), + ('INFO', '2'), + ] + self.assert_log_lines(lines) + # Now configure for flushing not to be done on close. + self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING, + self.root_hdlr, + False) + self.mem_logger.addHandler(self.mem_hdlr) + self.mem_logger.debug(self.next_message()) + self.assert_log_lines(lines) # no change + self.mem_logger.info(self.next_message()) + self.assert_log_lines(lines) # no change + self.mem_logger.removeHandler(self.mem_hdlr) + self.mem_hdlr.close() + # assert that no new lines have been added + self.assert_log_lines(lines) # no change + class ExceptionFormatter(logging.Formatter): """A special exception formatter.""" -- cgit v1.2.1 From bc72d8635aadf9302dd3bd72acdb6d0039ec5cc4 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Fri, 22 Jul 2016 18:23:04 +0100 Subject: Closes #27493: accepted Path objects in file handlers for logging. --- Lib/logging/__init__.py | 6 ++++-- Lib/logging/handlers.py | 3 +++ Lib/test/test_logging.py | 24 ++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index f941f4884b..fd422ea1e5 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2001-2015 by Vinay Sajip. All Rights Reserved. +# Copyright 2001-2016 by Vinay Sajip. All Rights Reserved. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose and without fee is hereby granted, @@ -18,7 +18,7 @@ Logging package for Python. Based on PEP 282 and comments thereto in comp.lang.python. -Copyright (C) 2001-2015 Vinay Sajip. All Rights Reserved. +Copyright (C) 2001-2016 Vinay Sajip. All Rights Reserved. To use, simply 'import logging' and log away! """ @@ -994,6 +994,8 @@ class FileHandler(StreamHandler): """ Open the specified file and use it as the stream for logging. """ + # Issue #27493: add support for Path objects to be passed in + filename = os.fspath(filename) #keep the absolute path, otherwise derived classes which use this #may come a cropper when the current directory changes self.baseFilename = os.path.abspath(filename) diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py index 296d6cfa30..ba00a69139 100644 --- a/Lib/logging/handlers.py +++ b/Lib/logging/handlers.py @@ -246,6 +246,9 @@ class TimedRotatingFileHandler(BaseRotatingHandler): self.extMatch = re.compile(self.extMatch, re.ASCII) self.interval = self.interval * interval # multiply by units requested + # The following line added because the filename passed in could be a + # path object (see Issue #27493), but self.baseFilename will be a string + filename = self.baseFilename if os.path.exists(filename): t = os.stat(filename)[ST_MTIME] else: diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 9e9a439eab..e998f6038e 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -26,6 +26,7 @@ import logging.config import codecs import configparser import datetime +import pathlib import pickle import io import gc @@ -575,6 +576,29 @@ class HandlerTest(BaseTest): self.assertFalse(h.shouldFlush(r)) h.close() + def test_path_objects(self): + """ + Test that Path objects are accepted as filename arguments to handlers. + + See Issue #27493. + """ + fd, fn = tempfile.mkstemp() + os.close(fd) + os.unlink(fn) + pfn = pathlib.Path(fn) + cases = ( + (logging.FileHandler, (pfn, 'w')), + (logging.handlers.RotatingFileHandler, (pfn, 'a')), + (logging.handlers.TimedRotatingFileHandler, (pfn, 'h')), + ) + if sys.platform in ('linux', 'darwin'): + cases += ((logging.handlers.WatchedFileHandler, (pfn, 'w')),) + for cls, args in cases: + h = cls(*args) + self.assertTrue(os.path.exists(fn)) + os.unlink(fn) + h.close() + @unittest.skipIf(os.name == 'nt', 'WatchedFileHandler not appropriate for Windows.') @unittest.skipUnless(threading, 'Threading required for this test.') def test_race(self): -- cgit v1.2.1 From f81de5e1946bcc07b86bac16f51ac0b4d0208da6 Mon Sep 17 00:00:00 2001 From: Alexander Belopolsky Date: Fri, 22 Jul 2016 18:47:04 -0400 Subject: Closes issue #24773: Implement PEP 495 (Local Time Disambiguation). --- Lib/datetime.py | 242 ++++++++---- Lib/test/datetimetester.py | 807 +++++++++++++++++++++++++++++++++++++++- Lib/test/libregrtest/cmdline.py | 4 +- 3 files changed, 975 insertions(+), 78 deletions(-) (limited to 'Lib') diff --git a/Lib/datetime.py b/Lib/datetime.py index b1321a34e3..19d2f676e5 100644 --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -250,9 +250,9 @@ def _check_utc_offset(name, offset): if not isinstance(offset, timedelta): raise TypeError("tzinfo.%s() must return None " "or timedelta, not '%s'" % (name, type(offset))) - if offset % timedelta(minutes=1) or offset.microseconds: + if offset.microseconds: raise ValueError("tzinfo.%s() must return a whole number " - "of minutes, got %s" % (name, offset)) + "of seconds, got %s" % (name, offset)) if not -timedelta(1) < offset < timedelta(1): raise ValueError("%s()=%s, must be strictly between " "-timedelta(hours=24) and timedelta(hours=24)" % @@ -930,7 +930,7 @@ class date: # Pickle support. - def _getstate(self): + def _getstate(self, protocol=3): yhi, ylo = divmod(self._year, 256) return bytes([yhi, ylo, self._month, self._day]), @@ -938,8 +938,8 @@ class date: yhi, ylo, self._month, self._day = string self._year = yhi * 256 + ylo - def __reduce__(self): - return (self.__class__, self._getstate()) + def __reduce_ex__(self, protocol): + return (self.__class__, self._getstate(protocol)) _date_class = date # so functions w/ args named "date" can get at the class @@ -947,6 +947,7 @@ date.min = date(1, 1, 1) date.max = date(9999, 12, 31) date.resolution = timedelta(days=1) + class tzinfo: """Abstract base class for time zone info classes. @@ -1038,11 +1039,11 @@ class time: dst() Properties (readonly): - hour, minute, second, microsecond, tzinfo + hour, minute, second, microsecond, tzinfo, fold """ - __slots__ = '_hour', '_minute', '_second', '_microsecond', '_tzinfo', '_hashcode' + __slots__ = '_hour', '_minute', '_second', '_microsecond', '_tzinfo', '_hashcode', '_fold' - def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None): + def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0): """Constructor. Arguments: @@ -1050,8 +1051,9 @@ class time: hour, minute (required) second, microsecond (default to zero) tzinfo (default to None) + fold (keyword only, default to True) """ - if isinstance(hour, bytes) and len(hour) == 6 and hour[0] < 24: + if isinstance(hour, bytes) and len(hour) == 6 and hour[0]&0x7F < 24: # Pickle support self = object.__new__(cls) self.__setstate(hour, minute or None) @@ -1067,6 +1069,7 @@ class time: self._microsecond = microsecond self._tzinfo = tzinfo self._hashcode = -1 + self._fold = fold return self # Read-only field accessors @@ -1095,6 +1098,10 @@ class time: """timezone info object""" return self._tzinfo + @property + def fold(self): + return self._fold + # Standard conversions, __hash__ (and helpers) # Comparisons of time objects with other. @@ -1160,9 +1167,13 @@ class time: def __hash__(self): """Hash.""" if self._hashcode == -1: - tzoff = self.utcoffset() + if self.fold: + t = self.replace(fold=0) + else: + t = self + tzoff = t.utcoffset() if not tzoff: # zero or None - self._hashcode = hash(self._getstate()[0]) + self._hashcode = hash(t._getstate()[0]) else: h, m = divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff, timedelta(hours=1)) @@ -1186,10 +1197,11 @@ class time: else: sign = "+" hh, mm = divmod(off, timedelta(hours=1)) - assert not mm % timedelta(minutes=1), "whole minute" - mm //= timedelta(minutes=1) + mm, ss = divmod(mm, timedelta(minutes=1)) assert 0 <= hh < 24 off = "%s%02d%s%02d" % (sign, hh, sep, mm) + if ss: + off += ':%02d' % ss.seconds return off def __repr__(self): @@ -1206,6 +1218,9 @@ class time: if self._tzinfo is not None: assert s[-1:] == ")" s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")" + if self._fold: + assert s[-1:] == ")" + s = s[:-1] + ", fold=1)" return s def isoformat(self, timespec='auto'): @@ -1284,7 +1299,7 @@ class time: return offset def replace(self, hour=None, minute=None, second=None, microsecond=None, - tzinfo=True): + tzinfo=True, *, fold=None): """Return a new time with new values for the specified fields.""" if hour is None: hour = self.hour @@ -1296,14 +1311,19 @@ class time: microsecond = self.microsecond if tzinfo is True: tzinfo = self.tzinfo - return time(hour, minute, second, microsecond, tzinfo) + if fold is None: + fold = self._fold + return time(hour, minute, second, microsecond, tzinfo, fold=fold) # Pickle support. - def _getstate(self): + def _getstate(self, protocol=3): us2, us3 = divmod(self._microsecond, 256) us1, us2 = divmod(us2, 256) - basestate = bytes([self._hour, self._minute, self._second, + h = self._hour + if self._fold and protocol > 3: + h += 128 + basestate = bytes([h, self._minute, self._second, us1, us2, us3]) if self._tzinfo is None: return (basestate,) @@ -1313,12 +1333,18 @@ class time: def __setstate(self, string, tzinfo): if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class): raise TypeError("bad tzinfo state arg") - self._hour, self._minute, self._second, us1, us2, us3 = string + h, self._minute, self._second, us1, us2, us3 = string + if h > 127: + self._fold = 1 + self._hour = h - 128 + else: + self._fold = 0 + self._hour = h self._microsecond = (((us1 << 8) | us2) << 8) | us3 self._tzinfo = tzinfo - def __reduce__(self): - return (time, self._getstate()) + def __reduce_ex__(self, protocol): + return (time, self._getstate(protocol)) _time_class = time # so functions w/ args named "time" can get at the class @@ -1335,8 +1361,8 @@ class datetime(date): __slots__ = date.__slots__ + time.__slots__ def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0, - microsecond=0, tzinfo=None): - if isinstance(year, bytes) and len(year) == 10 and 1 <= year[2] <= 12: + microsecond=0, tzinfo=None, *, fold=0): + if isinstance(year, bytes) and len(year) == 10 and 1 <= year[2]&0x7F <= 12: # Pickle support self = object.__new__(cls) self.__setstate(year, month) @@ -1356,6 +1382,7 @@ class datetime(date): self._microsecond = microsecond self._tzinfo = tzinfo self._hashcode = -1 + self._fold = fold return self # Read-only field accessors @@ -1384,6 +1411,10 @@ class datetime(date): """timezone info object""" return self._tzinfo + @property + def fold(self): + return self._fold + @classmethod def _fromtimestamp(cls, t, utc, tz): """Construct a datetime from a POSIX timestamp (like time.time()). @@ -1402,7 +1433,23 @@ class datetime(date): converter = _time.gmtime if utc else _time.localtime y, m, d, hh, mm, ss, weekday, jday, dst = converter(t) ss = min(ss, 59) # clamp out leap seconds if the platform has them - return cls(y, m, d, hh, mm, ss, us, tz) + result = cls(y, m, d, hh, mm, ss, us, tz) + if tz is None: + # As of version 2015f max fold in IANA database is + # 23 hours at 1969-09-30 13:00:00 in Kwajalein. + # Let's probe 24 hours in the past to detect a transition: + max_fold_seconds = 24 * 3600 + y, m, d, hh, mm, ss = converter(t - max_fold_seconds)[:6] + probe1 = cls(y, m, d, hh, mm, ss, us, tz) + trans = result - probe1 - timedelta(0, max_fold_seconds) + if trans.days < 0: + y, m, d, hh, mm, ss = converter(t + trans // timedelta(0, 1))[:6] + probe2 = cls(y, m, d, hh, mm, ss, us, tz) + if probe2 == result: + result._fold = 1 + else: + result = tz.fromutc(result) + return result @classmethod def fromtimestamp(cls, t, tz=None): @@ -1412,10 +1459,7 @@ class datetime(date): """ _check_tzinfo_arg(tz) - result = cls._fromtimestamp(t, tz is not None, tz) - if tz is not None: - result = tz.fromutc(result) - return result + return cls._fromtimestamp(t, tz is not None, tz) @classmethod def utcfromtimestamp(cls, t): @@ -1443,7 +1487,7 @@ class datetime(date): raise TypeError("time argument must be a time instance") return cls(date.year, date.month, date.day, time.hour, time.minute, time.second, time.microsecond, - time.tzinfo) + time.tzinfo, fold=time.fold) def timetuple(self): "Return local time tuple compatible with time.localtime()." @@ -1458,12 +1502,46 @@ class datetime(date): self.hour, self.minute, self.second, dst) + def _mktime(self): + """Return integer POSIX timestamp.""" + epoch = datetime(1970, 1, 1) + max_fold_seconds = 24 * 3600 + t = (self - epoch) // timedelta(0, 1) + def local(u): + y, m, d, hh, mm, ss = _time.localtime(u)[:6] + return (datetime(y, m, d, hh, mm, ss) - epoch) // timedelta(0, 1) + + # Our goal is to solve t = local(u) for u. + a = local(t) - t + u1 = t - a + t1 = local(u1) + if t1 == t: + # We found one solution, but it may not be the one we need. + # Look for an earlier solution (if `fold` is 0), or a + # later one (if `fold` is 1). + u2 = u1 + (-max_fold_seconds, max_fold_seconds)[self.fold] + b = local(u2) - u2 + if a == b: + return u1 + else: + b = t1 - u1 + assert a != b + u2 = t - b + t2 = local(u2) + if t2 == t: + return u2 + if t1 == t: + return u1 + # We have found both offsets a and b, but neither t - a nor t - b is + # a solution. This means t is in the gap. + return (max, min)[self.fold](u1, u2) + + def timestamp(self): "Return POSIX timestamp as float" if self._tzinfo is None: - return _time.mktime((self.year, self.month, self.day, - self.hour, self.minute, self.second, - -1, -1, -1)) + self.microsecond / 1e6 + s = self._mktime() + return s + self.microsecond / 1e6 else: return (self - _EPOCH).total_seconds() @@ -1482,15 +1560,16 @@ class datetime(date): def time(self): "Return the time part, with tzinfo None." - return time(self.hour, self.minute, self.second, self.microsecond) + return time(self.hour, self.minute, self.second, self.microsecond, fold=self.fold) def timetz(self): "Return the time part, with same tzinfo." return time(self.hour, self.minute, self.second, self.microsecond, - self._tzinfo) + self._tzinfo, fold=self.fold) def replace(self, year=None, month=None, day=None, hour=None, - minute=None, second=None, microsecond=None, tzinfo=True): + minute=None, second=None, microsecond=None, tzinfo=True, + *, fold=None): """Return a new datetime with new values for the specified fields.""" if year is None: year = self.year @@ -1508,46 +1587,45 @@ class datetime(date): microsecond = self.microsecond if tzinfo is True: tzinfo = self.tzinfo - return datetime(year, month, day, hour, minute, second, microsecond, - tzinfo) + if fold is None: + fold = self.fold + return datetime(year, month, day, hour, minute, second, + microsecond, tzinfo, fold=fold) + + def _local_timezone(self): + if self.tzinfo is None: + ts = self._mktime() + else: + ts = (self - _EPOCH) // timedelta(seconds=1) + localtm = _time.localtime(ts) + local = datetime(*localtm[:6]) + try: + # Extract TZ data if available + gmtoff = localtm.tm_gmtoff + zone = localtm.tm_zone + except AttributeError: + delta = local - datetime(*_time.gmtime(ts)[:6]) + zone = _time.strftime('%Z', localtm) + tz = timezone(delta, zone) + else: + tz = timezone(timedelta(seconds=gmtoff), zone) + return tz def astimezone(self, tz=None): if tz is None: - if self.tzinfo is None: - raise ValueError("astimezone() requires an aware datetime") - ts = (self - _EPOCH) // timedelta(seconds=1) - localtm = _time.localtime(ts) - local = datetime(*localtm[:6]) - try: - # Extract TZ data if available - gmtoff = localtm.tm_gmtoff - zone = localtm.tm_zone - except AttributeError: - # Compute UTC offset and compare with the value implied - # by tm_isdst. If the values match, use the zone name - # implied by tm_isdst. - delta = local - datetime(*_time.gmtime(ts)[:6]) - dst = _time.daylight and localtm.tm_isdst > 0 - gmtoff = -(_time.altzone if dst else _time.timezone) - if delta == timedelta(seconds=gmtoff): - tz = timezone(delta, _time.tzname[dst]) - else: - tz = timezone(delta) - else: - tz = timezone(timedelta(seconds=gmtoff), zone) - + tz = self._local_timezone() elif not isinstance(tz, tzinfo): raise TypeError("tz argument must be an instance of tzinfo") mytz = self.tzinfo if mytz is None: - raise ValueError("astimezone() requires an aware datetime") + mytz = self._local_timezone() if tz is mytz: return self # Convert self to UTC, and attach the new time zone object. - myoffset = self.utcoffset() + myoffset = mytz.utcoffset(self) if myoffset is None: raise ValueError("astimezone() requires an aware datetime") utc = (self - myoffset).replace(tzinfo=tz) @@ -1594,9 +1672,11 @@ class datetime(date): else: sign = "+" hh, mm = divmod(off, timedelta(hours=1)) - assert not mm % timedelta(minutes=1), "whole minute" - mm //= timedelta(minutes=1) + mm, ss = divmod(mm, timedelta(minutes=1)) s += "%s%02d:%02d" % (sign, hh, mm) + if ss: + assert not ss.microseconds + s += ":%02d" % ss.seconds return s def __repr__(self): @@ -1613,6 +1693,9 @@ class datetime(date): if self._tzinfo is not None: assert s[-1:] == ")" s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")" + if self._fold: + assert s[-1:] == ")" + s = s[:-1] + ", fold=1)" return s def __str__(self): @@ -1715,6 +1798,12 @@ class datetime(date): else: myoff = self.utcoffset() otoff = other.utcoffset() + # Assume that allow_mixed means that we are called from __eq__ + if allow_mixed: + if myoff != self.replace(fold=not self.fold).utcoffset(): + return 2 + if otoff != other.replace(fold=not other.fold).utcoffset(): + return 2 base_compare = myoff == otoff if base_compare: @@ -1782,9 +1871,13 @@ class datetime(date): def __hash__(self): if self._hashcode == -1: - tzoff = self.utcoffset() + if self.fold: + t = self.replace(fold=0) + else: + t = self + tzoff = t.utcoffset() if tzoff is None: - self._hashcode = hash(self._getstate()[0]) + self._hashcode = hash(t._getstate()[0]) else: days = _ymd2ord(self.year, self.month, self.day) seconds = self.hour * 3600 + self.minute * 60 + self.second @@ -1793,11 +1886,14 @@ class datetime(date): # Pickle support. - def _getstate(self): + def _getstate(self, protocol=3): yhi, ylo = divmod(self._year, 256) us2, us3 = divmod(self._microsecond, 256) us1, us2 = divmod(us2, 256) - basestate = bytes([yhi, ylo, self._month, self._day, + m = self._month + if self._fold and protocol > 3: + m += 128 + basestate = bytes([yhi, ylo, m, self._day, self._hour, self._minute, self._second, us1, us2, us3]) if self._tzinfo is None: @@ -1808,14 +1904,20 @@ class datetime(date): def __setstate(self, string, tzinfo): if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class): raise TypeError("bad tzinfo state arg") - (yhi, ylo, self._month, self._day, self._hour, + (yhi, ylo, m, self._day, self._hour, self._minute, self._second, us1, us2, us3) = string + if m > 127: + self._fold = 1 + self._month = m - 128 + else: + self._fold = 0 + self._month = m self._year = yhi * 256 + ylo self._microsecond = (((us1 << 8) | us2) << 8) | us3 self._tzinfo = tzinfo - def __reduce__(self): - return (self.__class__, self._getstate()) + def __reduce_ex__(self, protocol): + return (self.__class__, self._getstate(protocol)) datetime.min = datetime(1, 1, 1) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 8fc01390b3..e0d23da707 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -2,14 +2,22 @@ See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases """ +from test.support import requires + +import itertools +import bisect import copy import decimal import sys +import os import pickle import random +import struct import unittest +from array import array + from operator import lt, le, gt, ge, eq, ne, truediv, floordiv, mod from test import support @@ -1592,6 +1600,10 @@ class TestDateTime(TestDate): self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00") # str is ISO format with the separator forced to a blank. self.assertEqual(str(t), "0002-03-02 00:00:00") + # ISO format with timezone + tz = FixedOffset(timedelta(seconds=16), 'XXX') + t = self.theclass(2, 3, 2, tzinfo=tz) + self.assertEqual(t.isoformat(), "0002-03-02T00:00:00+00:00:16") def test_format(self): dt = self.theclass(2007, 9, 10, 4, 5, 1, 123) @@ -1711,6 +1723,9 @@ class TestDateTime(TestDate): self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 59, 1000000) + # Positional fold: + self.assertRaises(TypeError, self.theclass, + 2000, 1, 31, 23, 59, 59, 0, None, 1) def test_hash_equality(self): d = self.theclass(2000, 12, 31, 23, 30, 17) @@ -1894,16 +1909,20 @@ class TestDateTime(TestDate): t = self.theclass(1970, 1, 1, 1, 2, 3, 4) self.assertEqual(t.timestamp(), 18000.0 + 3600 + 2*60 + 3 + 4*1e-6) - # Missing hour may produce platform-dependent result - t = self.theclass(2012, 3, 11, 2, 30) - self.assertIn(self.theclass.fromtimestamp(t.timestamp()), - [t - timedelta(hours=1), t + timedelta(hours=1)]) + # Missing hour + t0 = self.theclass(2012, 3, 11, 2, 30) + t1 = t0.replace(fold=1) + self.assertEqual(self.theclass.fromtimestamp(t1.timestamp()), + t0 - timedelta(hours=1)) + self.assertEqual(self.theclass.fromtimestamp(t0.timestamp()), + t1 + timedelta(hours=1)) # Ambiguous hour defaults to DST t = self.theclass(2012, 11, 4, 1, 30) self.assertEqual(self.theclass.fromtimestamp(t.timestamp()), t) # Timestamp may raise an overflow error on some platforms - for t in [self.theclass(1,1,1), self.theclass(9999,12,12)]: + # XXX: Do we care to support the first and last year? + for t in [self.theclass(2,1,1), self.theclass(9998,12,12)]: try: s = t.timestamp() except OverflowError: @@ -1922,6 +1941,7 @@ class TestDateTime(TestDate): self.assertEqual(t.timestamp(), 18000 + 3600 + 2*60 + 3 + 4*1e-6) + @support.run_with_tz('MSK-03') # Something east of Greenwich def test_microsecond_rounding(self): for fts in [self.theclass.fromtimestamp, self.theclass.utcfromtimestamp]: @@ -2127,6 +2147,7 @@ class TestDateTime(TestDate): self.assertRaises(ValueError, base.replace, year=2001) def test_astimezone(self): + return # The rest is no longer applicable # Pretty boring! The TZ test is more interesting here. astimezone() # simply can't be applied to a naive object. dt = self.theclass.now() @@ -2619,9 +2640,9 @@ class TZInfoBase: self.assertRaises(ValueError, t.utcoffset) self.assertRaises(ValueError, t.dst) - # Not a whole number of minutes. + # Not a whole number of seconds. class C7(tzinfo): - def utcoffset(self, dt): return timedelta(seconds=61) + def utcoffset(self, dt): return timedelta(microseconds=61) def dst(self, dt): return timedelta(microseconds=-81) t = cls(1, 1, 1, tzinfo=C7()) self.assertRaises(ValueError, t.utcoffset) @@ -3994,5 +4015,777 @@ class Oddballs(unittest.TestCase): with self.assertRaises(TypeError): datetime(10, 10, 10, 10, 10, 10, 10.) +############################################################################# +# Local Time Disambiguation + +# An experimental reimplementation of fromutc that respects the "fold" flag. + +class tzinfo2(tzinfo): + + def fromutc(self, dt): + "datetime in UTC -> datetime in local time." + + if not isinstance(dt, datetime): + raise TypeError("fromutc() requires a datetime argument") + if dt.tzinfo is not self: + raise ValueError("dt.tzinfo is not self") + # Returned value satisfies + # dt + ldt.utcoffset() = ldt + off0 = dt.replace(fold=0).utcoffset() + off1 = dt.replace(fold=1).utcoffset() + if off0 is None or off1 is None or dt.dst() is None: + raise ValueError + if off0 == off1: + ldt = dt + off0 + off1 = ldt.utcoffset() + if off0 == off1: + return ldt + # Now, we discovered both possible offsets, so + # we can just try four possible solutions: + for off in [off0, off1]: + ldt = dt + off + if ldt.utcoffset() == off: + return ldt + ldt = ldt.replace(fold=1) + if ldt.utcoffset() == off: + return ldt + + raise ValueError("No suitable local time found") + +# Reimplementing simplified US timezones to respect the "fold" flag: + +class USTimeZone2(tzinfo2): + + def __init__(self, hours, reprname, stdname, dstname): + self.stdoffset = timedelta(hours=hours) + self.reprname = reprname + self.stdname = stdname + self.dstname = dstname + + def __repr__(self): + return self.reprname + + def tzname(self, dt): + if self.dst(dt): + return self.dstname + else: + return self.stdname + + def utcoffset(self, dt): + return self.stdoffset + self.dst(dt) + + def dst(self, dt): + if dt is None or dt.tzinfo is None: + # An exception instead may be sensible here, in one or more of + # the cases. + return ZERO + assert dt.tzinfo is self + + # Find first Sunday in April. + start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year)) + assert start.weekday() == 6 and start.month == 4 and start.day <= 7 + + # Find last Sunday in October. + end = first_sunday_on_or_after(DSTEND.replace(year=dt.year)) + assert end.weekday() == 6 and end.month == 10 and end.day >= 25 + + # Can't compare naive to aware objects, so strip the timezone from + # dt first. + dt = dt.replace(tzinfo=None) + if start + HOUR <= dt < end: + # DST is in effect. + return HOUR + elif end <= dt < end + HOUR: + # Fold (an ambiguous hour): use dt.fold to disambiguate. + return ZERO if dt.fold else HOUR + elif start <= dt < start + HOUR: + # Gap (a non-existent hour): reverse the fold rule. + return HOUR if dt.fold else ZERO + else: + # DST is off. + return ZERO + +Eastern2 = USTimeZone2(-5, "Eastern2", "EST", "EDT") +Central2 = USTimeZone2(-6, "Central2", "CST", "CDT") +Mountain2 = USTimeZone2(-7, "Mountain2", "MST", "MDT") +Pacific2 = USTimeZone2(-8, "Pacific2", "PST", "PDT") + +# Europe_Vilnius_1941 tzinfo implementation reproduces the following +# 1941 transition from Olson's tzdist: +# +# Zone NAME GMTOFF RULES FORMAT [UNTIL] +# ZoneEurope/Vilnius 1:00 - CET 1940 Aug 3 +# 3:00 - MSK 1941 Jun 24 +# 1:00 C-Eur CE%sT 1944 Aug +# +# $ zdump -v Europe/Vilnius | grep 1941 +# Europe/Vilnius Mon Jun 23 20:59:59 1941 UTC = Mon Jun 23 23:59:59 1941 MSK isdst=0 gmtoff=10800 +# Europe/Vilnius Mon Jun 23 21:00:00 1941 UTC = Mon Jun 23 23:00:00 1941 CEST isdst=1 gmtoff=7200 + +class Europe_Vilnius_1941(tzinfo): + def _utc_fold(self): + return [datetime(1941, 6, 23, 21, tzinfo=self), # Mon Jun 23 21:00:00 1941 UTC + datetime(1941, 6, 23, 22, tzinfo=self)] # Mon Jun 23 22:00:00 1941 UTC + + def _loc_fold(self): + return [datetime(1941, 6, 23, 23, tzinfo=self), # Mon Jun 23 23:00:00 1941 MSK / CEST + datetime(1941, 6, 24, 0, tzinfo=self)] # Mon Jun 24 00:00:00 1941 CEST + + def utcoffset(self, dt): + fold_start, fold_stop = self._loc_fold() + if dt < fold_start: + return 3 * HOUR + if dt < fold_stop: + return (2 if dt.fold else 3) * HOUR + # if dt >= fold_stop + return 2 * HOUR + + def dst(self, dt): + fold_start, fold_stop = self._loc_fold() + if dt < fold_start: + return 0 * HOUR + if dt < fold_stop: + return (1 if dt.fold else 0) * HOUR + # if dt >= fold_stop + return 1 * HOUR + + def tzname(self, dt): + fold_start, fold_stop = self._loc_fold() + if dt < fold_start: + return 'MSK' + if dt < fold_stop: + return ('MSK', 'CEST')[dt.fold] + # if dt >= fold_stop + return 'CEST' + + def fromutc(self, dt): + assert dt.fold == 0 + assert dt.tzinfo is self + if dt.year != 1941: + raise NotImplementedError + fold_start, fold_stop = self._utc_fold() + if dt < fold_start: + return dt + 3 * HOUR + if dt < fold_stop: + return (dt + 2 * HOUR).replace(fold=1) + # if dt >= fold_stop + return dt + 2 * HOUR + + +class TestLocalTimeDisambiguation(unittest.TestCase): + + def test_vilnius_1941_fromutc(self): + Vilnius = Europe_Vilnius_1941() + + gdt = datetime(1941, 6, 23, 20, 59, 59, tzinfo=timezone.utc) + ldt = gdt.astimezone(Vilnius) + self.assertEqual(ldt.strftime("%c %Z%z"), + 'Mon Jun 23 23:59:59 1941 MSK+0300') + self.assertEqual(ldt.fold, 0) + self.assertFalse(ldt.dst()) + + gdt = datetime(1941, 6, 23, 21, tzinfo=timezone.utc) + ldt = gdt.astimezone(Vilnius) + self.assertEqual(ldt.strftime("%c %Z%z"), + 'Mon Jun 23 23:00:00 1941 CEST+0200') + self.assertEqual(ldt.fold, 1) + self.assertTrue(ldt.dst()) + + gdt = datetime(1941, 6, 23, 22, tzinfo=timezone.utc) + ldt = gdt.astimezone(Vilnius) + self.assertEqual(ldt.strftime("%c %Z%z"), + 'Tue Jun 24 00:00:00 1941 CEST+0200') + self.assertEqual(ldt.fold, 0) + self.assertTrue(ldt.dst()) + + def test_vilnius_1941_toutc(self): + Vilnius = Europe_Vilnius_1941() + + ldt = datetime(1941, 6, 23, 22, 59, 59, tzinfo=Vilnius) + gdt = ldt.astimezone(timezone.utc) + self.assertEqual(gdt.strftime("%c %Z"), + 'Mon Jun 23 19:59:59 1941 UTC') + + ldt = datetime(1941, 6, 23, 23, 59, 59, tzinfo=Vilnius) + gdt = ldt.astimezone(timezone.utc) + self.assertEqual(gdt.strftime("%c %Z"), + 'Mon Jun 23 20:59:59 1941 UTC') + + ldt = datetime(1941, 6, 23, 23, 59, 59, tzinfo=Vilnius, fold=1) + gdt = ldt.astimezone(timezone.utc) + self.assertEqual(gdt.strftime("%c %Z"), + 'Mon Jun 23 21:59:59 1941 UTC') + + ldt = datetime(1941, 6, 24, 0, tzinfo=Vilnius) + gdt = ldt.astimezone(timezone.utc) + self.assertEqual(gdt.strftime("%c %Z"), + 'Mon Jun 23 22:00:00 1941 UTC') + + + def test_constructors(self): + t = time(0, fold=1) + dt = datetime(1, 1, 1, fold=1) + self.assertEqual(t.fold, 1) + self.assertEqual(dt.fold, 1) + with self.assertRaises(TypeError): + time(0, 0, 0, 0, None, 0) + + def test_member(self): + dt = datetime(1, 1, 1, fold=1) + t = dt.time() + self.assertEqual(t.fold, 1) + t = dt.timetz() + self.assertEqual(t.fold, 1) + + def test_replace(self): + t = time(0) + dt = datetime(1, 1, 1) + self.assertEqual(t.replace(fold=1).fold, 1) + self.assertEqual(dt.replace(fold=1).fold, 1) + self.assertEqual(t.replace(fold=0).fold, 0) + self.assertEqual(dt.replace(fold=0).fold, 0) + # Check that replacement of other fields does not change "fold". + t = t.replace(fold=1, tzinfo=Eastern) + dt = dt.replace(fold=1, tzinfo=Eastern) + self.assertEqual(t.replace(tzinfo=None).fold, 1) + self.assertEqual(dt.replace(tzinfo=None).fold, 1) + # Check that fold is a keyword-only argument + with self.assertRaises(TypeError): + t.replace(1, 1, 1, None, 1) + with self.assertRaises(TypeError): + dt.replace(1, 1, 1, 1, 1, 1, 1, None, 1) + + def test_comparison(self): + t = time(0) + dt = datetime(1, 1, 1) + self.assertEqual(t, t.replace(fold=1)) + self.assertEqual(dt, dt.replace(fold=1)) + + def test_hash(self): + t = time(0) + dt = datetime(1, 1, 1) + self.assertEqual(hash(t), hash(t.replace(fold=1))) + self.assertEqual(hash(dt), hash(dt.replace(fold=1))) + + @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0') + def test_fromtimestamp(self): + s = 1414906200 + dt0 = datetime.fromtimestamp(s) + dt1 = datetime.fromtimestamp(s + 3600) + self.assertEqual(dt0.fold, 0) + self.assertEqual(dt1.fold, 1) + + @support.run_with_tz('Australia/Lord_Howe') + def test_fromtimestamp_lord_howe(self): + tm = _time.localtime(1.4e9) + if _time.strftime('%Z%z', tm) != 'LHST+1030': + self.skipTest('Australia/Lord_Howe timezone is not supported on this platform') + # $ TZ=Australia/Lord_Howe date -r 1428158700 + # Sun Apr 5 01:45:00 LHDT 2015 + # $ TZ=Australia/Lord_Howe date -r 1428160500 + # Sun Apr 5 01:45:00 LHST 2015 + s = 1428158700 + t0 = datetime.fromtimestamp(s) + t1 = datetime.fromtimestamp(s + 1800) + self.assertEqual(t0, t1) + self.assertEqual(t0.fold, 0) + self.assertEqual(t1.fold, 1) + + + @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0') + def test_timestamp(self): + dt0 = datetime(2014, 11, 2, 1, 30) + dt1 = dt0.replace(fold=1) + self.assertEqual(dt0.timestamp() + 3600, + dt1.timestamp()) + + @support.run_with_tz('Australia/Lord_Howe') + def test_timestamp_lord_howe(self): + tm = _time.localtime(1.4e9) + if _time.strftime('%Z%z', tm) != 'LHST+1030': + self.skipTest('Australia/Lord_Howe timezone is not supported on this platform') + t = datetime(2015, 4, 5, 1, 45) + s0 = t.replace(fold=0).timestamp() + s1 = t.replace(fold=1).timestamp() + self.assertEqual(s0 + 1800, s1) + + + @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0') + def test_astimezone(self): + dt0 = datetime(2014, 11, 2, 1, 30) + dt1 = dt0.replace(fold=1) + # Convert both naive instances to aware. + adt0 = dt0.astimezone() + adt1 = dt1.astimezone() + # Check that the first instance in DST zone and the second in STD + self.assertEqual(adt0.tzname(), 'EDT') + self.assertEqual(adt1.tzname(), 'EST') + self.assertEqual(adt0 + HOUR, adt1) + # Aware instances with fixed offset tzinfo's always have fold=0 + self.assertEqual(adt0.fold, 0) + self.assertEqual(adt1.fold, 0) + + + def test_pickle_fold(self): + t = time(fold=1) + dt = datetime(1, 1, 1, fold=1) + for pickler, unpickler, proto in pickle_choices: + for x in [t, dt]: + s = pickler.dumps(x, proto) + y = unpickler.loads(s) + self.assertEqual(x, y) + self.assertEqual((0 if proto < 4 else x.fold), y.fold) + + def test_repr(self): + t = time(fold=1) + dt = datetime(1, 1, 1, fold=1) + self.assertEqual(repr(t), 'datetime.time(0, 0, fold=1)') + self.assertEqual(repr(dt), + 'datetime.datetime(1, 1, 1, 0, 0, fold=1)') + + def test_dst(self): + # Let's first establish that things work in regular times. + dt_summer = datetime(2002, 10, 27, 1, tzinfo=Eastern2) - timedelta.resolution + dt_winter = datetime(2002, 10, 27, 2, tzinfo=Eastern2) + self.assertEqual(dt_summer.dst(), HOUR) + self.assertEqual(dt_winter.dst(), ZERO) + # The disambiguation flag is ignored + self.assertEqual(dt_summer.replace(fold=1).dst(), HOUR) + self.assertEqual(dt_winter.replace(fold=1).dst(), ZERO) + + # Pick local time in the fold. + for minute in [0, 30, 59]: + dt = datetime(2002, 10, 27, 1, minute, tzinfo=Eastern2) + # With fold=0 (the default) it is in DST. + self.assertEqual(dt.dst(), HOUR) + # With fold=1 it is in STD. + self.assertEqual(dt.replace(fold=1).dst(), ZERO) + + # Pick local time in the gap. + for minute in [0, 30, 59]: + dt = datetime(2002, 4, 7, 2, minute, tzinfo=Eastern2) + # With fold=0 (the default) it is in STD. + self.assertEqual(dt.dst(), ZERO) + # With fold=1 it is in DST. + self.assertEqual(dt.replace(fold=1).dst(), HOUR) + + + def test_utcoffset(self): + # Let's first establish that things work in regular times. + dt_summer = datetime(2002, 10, 27, 1, tzinfo=Eastern2) - timedelta.resolution + dt_winter = datetime(2002, 10, 27, 2, tzinfo=Eastern2) + self.assertEqual(dt_summer.utcoffset(), -4 * HOUR) + self.assertEqual(dt_winter.utcoffset(), -5 * HOUR) + # The disambiguation flag is ignored + self.assertEqual(dt_summer.replace(fold=1).utcoffset(), -4 * HOUR) + self.assertEqual(dt_winter.replace(fold=1).utcoffset(), -5 * HOUR) + + def test_fromutc(self): + # Let's first establish that things work in regular times. + u_summer = datetime(2002, 10, 27, 6, tzinfo=Eastern2) - timedelta.resolution + u_winter = datetime(2002, 10, 27, 7, tzinfo=Eastern2) + t_summer = Eastern2.fromutc(u_summer) + t_winter = Eastern2.fromutc(u_winter) + self.assertEqual(t_summer, u_summer - 4 * HOUR) + self.assertEqual(t_winter, u_winter - 5 * HOUR) + self.assertEqual(t_summer.fold, 0) + self.assertEqual(t_winter.fold, 0) + + # What happens in the fall-back fold? + u = datetime(2002, 10, 27, 5, 30, tzinfo=Eastern2) + t0 = Eastern2.fromutc(u) + u += HOUR + t1 = Eastern2.fromutc(u) + self.assertEqual(t0, t1) + self.assertEqual(t0.fold, 0) + self.assertEqual(t1.fold, 1) + # The tricky part is when u is in the local fold: + u = datetime(2002, 10, 27, 1, 30, tzinfo=Eastern2) + t = Eastern2.fromutc(u) + self.assertEqual((t.day, t.hour), (26, 21)) + # .. or gets into the local fold after a standard time adjustment + u = datetime(2002, 10, 27, 6, 30, tzinfo=Eastern2) + t = Eastern2.fromutc(u) + self.assertEqual((t.day, t.hour), (27, 1)) + + # What happens in the spring-forward gap? + u = datetime(2002, 4, 7, 2, 0, tzinfo=Eastern2) + t = Eastern2.fromutc(u) + self.assertEqual((t.day, t.hour), (6, 21)) + + def test_mixed_compare_regular(self): + t = datetime(2000, 1, 1, tzinfo=Eastern2) + self.assertEqual(t, t.astimezone(timezone.utc)) + t = datetime(2000, 6, 1, tzinfo=Eastern2) + self.assertEqual(t, t.astimezone(timezone.utc)) + + def test_mixed_compare_fold(self): + t_fold = datetime(2002, 10, 27, 1, 45, tzinfo=Eastern2) + t_fold_utc = t_fold.astimezone(timezone.utc) + self.assertNotEqual(t_fold, t_fold_utc) + + def test_mixed_compare_gap(self): + t_gap = datetime(2002, 4, 7, 2, 45, tzinfo=Eastern2) + t_gap_utc = t_gap.astimezone(timezone.utc) + self.assertNotEqual(t_gap, t_gap_utc) + + def test_hash_aware(self): + t = datetime(2000, 1, 1, tzinfo=Eastern2) + self.assertEqual(hash(t), hash(t.replace(fold=1))) + t_fold = datetime(2002, 10, 27, 1, 45, tzinfo=Eastern2) + t_gap = datetime(2002, 4, 7, 2, 45, tzinfo=Eastern2) + self.assertEqual(hash(t_fold), hash(t_fold.replace(fold=1))) + self.assertEqual(hash(t_gap), hash(t_gap.replace(fold=1))) + +SEC = timedelta(0, 1) + +def pairs(iterable): + a, b = itertools.tee(iterable) + next(b, None) + return zip(a, b) + +class ZoneInfo(tzinfo): + zoneroot = '/usr/share/zoneinfo' + def __init__(self, ut, ti): + """ + + :param ut: array + Array of transition point timestamps + :param ti: list + A list of (offset, isdst, abbr) tuples + :return: None + """ + self.ut = ut + self.ti = ti + self.lt = self.invert(ut, ti) + + @staticmethod + def invert(ut, ti): + lt = (ut.__copy__(), ut.__copy__()) + if ut: + offset = ti[0][0] // SEC + lt[0][0] = max(-2**31, lt[0][0] + offset) + lt[1][0] = max(-2**31, lt[1][0] + offset) + for i in range(1, len(ut)): + lt[0][i] += ti[i-1][0] // SEC + lt[1][i] += ti[i][0] // SEC + return lt + + @classmethod + def fromfile(cls, fileobj): + if fileobj.read(4).decode() != "TZif": + raise ValueError("not a zoneinfo file") + fileobj.seek(32) + counts = array('i') + counts.fromfile(fileobj, 3) + if sys.byteorder != 'big': + counts.byteswap() + + ut = array('i') + ut.fromfile(fileobj, counts[0]) + if sys.byteorder != 'big': + ut.byteswap() + + type_indices = array('B') + type_indices.fromfile(fileobj, counts[0]) + + ttis = [] + for i in range(counts[1]): + ttis.append(struct.unpack(">lbb", fileobj.read(6))) + + abbrs = fileobj.read(counts[2]) + + # Convert ttis + for i, (gmtoff, isdst, abbrind) in enumerate(ttis): + abbr = abbrs[abbrind:abbrs.find(0, abbrind)].decode() + ttis[i] = (timedelta(0, gmtoff), isdst, abbr) + + ti = [None] * len(ut) + for i, idx in enumerate(type_indices): + ti[i] = ttis[idx] + + self = cls(ut, ti) + + return self + + @classmethod + def fromname(cls, name): + path = os.path.join(cls.zoneroot, name) + with open(path, 'rb') as f: + return cls.fromfile(f) + + EPOCHORDINAL = date(1970, 1, 1).toordinal() + + def fromutc(self, dt): + """datetime in UTC -> datetime in local time.""" + + if not isinstance(dt, datetime): + raise TypeError("fromutc() requires a datetime argument") + if dt.tzinfo is not self: + raise ValueError("dt.tzinfo is not self") + + timestamp = ((dt.toordinal() - self.EPOCHORDINAL) * 86400 + + dt.hour * 3600 + + dt.minute * 60 + + dt.second) + + if timestamp < self.ut[1]: + tti = self.ti[0] + fold = 0 + else: + idx = bisect.bisect_right(self.ut, timestamp) + assert self.ut[idx-1] <= timestamp + assert idx == len(self.ut) or timestamp < self.ut[idx] + tti_prev, tti = self.ti[idx-2:idx] + # Detect fold + shift = tti_prev[0] - tti[0] + fold = (shift > timedelta(0, timestamp - self.ut[idx-1])) + dt += tti[0] + if fold: + return dt.replace(fold=1) + else: + return dt + + def _find_ti(self, dt, i): + timestamp = ((dt.toordinal() - self.EPOCHORDINAL) * 86400 + + dt.hour * 3600 + + dt.minute * 60 + + dt.second) + lt = self.lt[dt.fold] + idx = bisect.bisect_right(lt, timestamp) + + return self.ti[max(0, idx - 1)][i] + + def utcoffset(self, dt): + return self._find_ti(dt, 0) + + def dst(self, dt): + isdst = self._find_ti(dt, 1) + # XXX: We cannot accurately determine the "save" value, + # so let's return 1h whenever DST is in effect. Since + # we don't use dst() in fromutc(), it is unlikely that + # it will be needed for anything more than bool(dst()). + return ZERO if isdst else HOUR + + def tzname(self, dt): + return self._find_ti(dt, 2) + + @classmethod + def zonenames(cls, zonedir=None): + if zonedir is None: + zonedir = cls.zoneroot + for root, _, files in os.walk(zonedir): + for f in files: + p = os.path.join(root, f) + with open(p, 'rb') as o: + magic = o.read(4) + if magic == b'TZif': + yield p[len(zonedir) + 1:] + + @classmethod + def stats(cls, start_year=1): + count = gap_count = fold_count = zeros_count = 0 + min_gap = min_fold = timedelta.max + max_gap = max_fold = ZERO + min_gap_datetime = max_gap_datetime = datetime.min + min_gap_zone = max_gap_zone = None + min_fold_datetime = max_fold_datetime = datetime.min + min_fold_zone = max_fold_zone = None + stats_since = datetime(start_year, 1, 1) # Starting from 1970 eliminates a lot of noise + for zonename in cls.zonenames(): + count += 1 + tz = cls.fromname(zonename) + for dt, shift in tz.transitions(): + if dt < stats_since: + continue + if shift > ZERO: + gap_count += 1 + if (shift, dt) > (max_gap, max_gap_datetime): + max_gap = shift + max_gap_zone = zonename + max_gap_datetime = dt + if (shift, datetime.max - dt) < (min_gap, datetime.max - min_gap_datetime): + min_gap = shift + min_gap_zone = zonename + min_gap_datetime = dt + elif shift < ZERO: + fold_count += 1 + shift = -shift + if (shift, dt) > (max_fold, max_fold_datetime): + max_fold = shift + max_fold_zone = zonename + max_fold_datetime = dt + if (shift, datetime.max - dt) < (min_fold, datetime.max - min_fold_datetime): + min_fold = shift + min_fold_zone = zonename + min_fold_datetime = dt + else: + zeros_count += 1 + trans_counts = (gap_count, fold_count, zeros_count) + print("Number of zones: %5d" % count) + print("Number of transitions: %5d = %d (gaps) + %d (folds) + %d (zeros)" % + ((sum(trans_counts),) + trans_counts)) + print("Min gap: %16s at %s in %s" % (min_gap, min_gap_datetime, min_gap_zone)) + print("Max gap: %16s at %s in %s" % (max_gap, max_gap_datetime, max_gap_zone)) + print("Min fold: %16s at %s in %s" % (min_fold, min_fold_datetime, min_fold_zone)) + print("Max fold: %16s at %s in %s" % (max_fold, max_fold_datetime, max_fold_zone)) + + + def transitions(self): + for (_, prev_ti), (t, ti) in pairs(zip(self.ut, self.ti)): + shift = ti[0] - prev_ti[0] + yield datetime.utcfromtimestamp(t), shift + + def nondst_folds(self): + """Find all folds with the same value of isdst on both sides of the transition.""" + for (_, prev_ti), (t, ti) in pairs(zip(self.ut, self.ti)): + shift = ti[0] - prev_ti[0] + if shift < ZERO and ti[1] == prev_ti[1]: + yield datetime.utcfromtimestamp(t), -shift, prev_ti[2], ti[2] + + @classmethod + def print_all_nondst_folds(cls, same_abbr=False, start_year=1): + count = 0 + for zonename in cls.zonenames(): + tz = cls.fromname(zonename) + for dt, shift, prev_abbr, abbr in tz.nondst_folds(): + if dt.year < start_year or same_abbr and prev_abbr != abbr: + continue + count += 1 + print("%3d) %-30s %s %10s %5s -> %s" % + (count, zonename, dt, shift, prev_abbr, abbr)) + + def folds(self): + for t, shift in self.transitions(): + if shift < ZERO: + yield t, -shift + + def gaps(self): + for t, shift in self.transitions(): + if shift > ZERO: + yield t, shift + + def zeros(self): + for t, shift in self.transitions(): + if not shift: + yield t + + +class ZoneInfoTest(unittest.TestCase): + zonename = 'America/New_York' + + def setUp(self): + if sys.platform == "win32": + self.skipTest("Skipping zoneinfo tests on Windows") + self.tz = ZoneInfo.fromname(self.zonename) + + def assertEquivDatetimes(self, a, b): + self.assertEqual((a.replace(tzinfo=None), a.fold, id(a.tzinfo)), + (b.replace(tzinfo=None), b.fold, id(b.tzinfo))) + + def test_folds(self): + tz = self.tz + for dt, shift in tz.folds(): + for x in [0 * shift, 0.5 * shift, shift - timedelta.resolution]: + udt = dt + x + ldt = tz.fromutc(udt.replace(tzinfo=tz)) + self.assertEqual(ldt.fold, 1) + adt = udt.replace(tzinfo=timezone.utc).astimezone(tz) + self.assertEquivDatetimes(adt, ldt) + utcoffset = ldt.utcoffset() + self.assertEqual(ldt.replace(tzinfo=None), udt + utcoffset) + # Round trip + self.assertEquivDatetimes(ldt.astimezone(timezone.utc), + udt.replace(tzinfo=timezone.utc)) + + + for x in [-timedelta.resolution, shift]: + udt = dt + x + udt = udt.replace(tzinfo=tz) + ldt = tz.fromutc(udt) + self.assertEqual(ldt.fold, 0) + + def test_gaps(self): + tz = self.tz + for dt, shift in tz.gaps(): + for x in [0 * shift, 0.5 * shift, shift - timedelta.resolution]: + udt = dt + x + udt = udt.replace(tzinfo=tz) + ldt = tz.fromutc(udt) + self.assertEqual(ldt.fold, 0) + adt = udt.replace(tzinfo=timezone.utc).astimezone(tz) + self.assertEquivDatetimes(adt, ldt) + utcoffset = ldt.utcoffset() + self.assertEqual(ldt.replace(tzinfo=None), udt.replace(tzinfo=None) + utcoffset) + # Create a local time inside the gap + ldt = tz.fromutc(dt.replace(tzinfo=tz)) - shift + x + self.assertLess(ldt.replace(fold=1).utcoffset(), + ldt.replace(fold=0).utcoffset(), + "At %s." % ldt) + + for x in [-timedelta.resolution, shift]: + udt = dt + x + ldt = tz.fromutc(udt.replace(tzinfo=tz)) + self.assertEqual(ldt.fold, 0) + + def test_system_transitions(self): + if ('Riyadh8' in self.zonename or + # From tzdata NEWS file: + # The files solar87, solar88, and solar89 are no longer distributed. + # They were a negative experiment - that is, a demonstration that + # tz data can represent solar time only with some difficulty and error. + # Their presence in the distribution caused confusion, as Riyadh + # civil time was generally not solar time in those years. + self.zonename.startswith('right/')): + self.skipTest("Skipping %s" % self.zonename) + tz = ZoneInfo.fromname(self.zonename) + TZ = os.environ.get('TZ') + os.environ['TZ'] = self.zonename + try: + _time.tzset() + for udt, shift in tz.transitions(): + if self.zonename == 'Europe/Tallinn' and udt.date() == date(1999, 10, 31): + print("Skip %s %s transition" % (self.zonename, udt)) + continue + s0 = (udt - datetime(1970, 1, 1)) // SEC + ss = shift // SEC # shift seconds + for x in [-40 * 3600, -20*3600, -1, 0, + ss - 1, ss + 20 * 3600, ss + 40 * 3600]: + s = s0 + x + sdt = datetime.fromtimestamp(s) + tzdt = datetime.fromtimestamp(s, tz).replace(tzinfo=None) + self.assertEquivDatetimes(sdt, tzdt) + s1 = sdt.timestamp() + self.assertEqual(s, s1) + if ss > 0: # gap + # Create local time inside the gap + dt = datetime.fromtimestamp(s0) - shift / 2 + ts0 = dt.timestamp() + ts1 = dt.replace(fold=1).timestamp() + self.assertEqual(ts0, s0 + ss / 2) + self.assertEqual(ts1, s0 - ss / 2) + finally: + if TZ is None: + del os.environ['TZ'] + else: + os.environ['TZ'] = TZ + _time.tzset() + + +class ZoneInfoCompleteTest(unittest.TestCase): + def test_all(self): + requires('tzdata', 'test requires tzdata and a long time to run') + for name in ZoneInfo.zonenames(): + class Test(ZoneInfoTest): + zonename = name + for suffix in ['folds', 'gaps', 'system_transitions']: + test = Test('test_' + suffix) + result = test.run() + self.assertTrue(result.wasSuccessful(), name + ' ' + suffix) + +# Iran had a sub-minute UTC offset before 1946. +class IranTest(ZoneInfoTest): + zonename = 'Iran' + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/libregrtest/cmdline.py b/Lib/test/libregrtest/cmdline.py index de09a0122a..f2ec0bd4d2 100644 --- a/Lib/test/libregrtest/cmdline.py +++ b/Lib/test/libregrtest/cmdline.py @@ -112,6 +112,8 @@ resources to test. Currently only the following are defined: gui - Run tests that require a running GUI. + tzdata - Run tests that require timezone data. + To enable all resources except one, use '-uall,-'. For example, to run all the tests except for the gui tests, give the option '-uall,-gui'. @@ -119,7 +121,7 @@ option '-uall,-gui'. RESOURCE_NAMES = ('audio', 'curses', 'largefile', 'network', - 'decimal', 'cpu', 'subprocess', 'urlfetch', 'gui') + 'decimal', 'cpu', 'subprocess', 'urlfetch', 'gui', 'tzdata') class _ArgParser(argparse.ArgumentParser): -- cgit v1.2.1 From e9d135e3a5738073968e26284d69d387b7a040cf Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Sat, 23 Jul 2016 07:15:12 +0300 Subject: Issue #27493: Fix test_path_objects under Windows --- Lib/test/test_logging.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index e998f6038e..7899c77fb9 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -596,8 +596,8 @@ class HandlerTest(BaseTest): for cls, args in cases: h = cls(*args) self.assertTrue(os.path.exists(fn)) - os.unlink(fn) h.close() + os.unlink(fn) @unittest.skipIf(os.name == 'nt', 'WatchedFileHandler not appropriate for Windows.') @unittest.skipUnless(threading, 'Threading required for this test.') -- cgit v1.2.1 From 11cad0b0082fd972ffe3d88b72279f8ebeaeeceb Mon Sep 17 00:00:00 2001 From: Alexander Belopolsky Date: Sat, 23 Jul 2016 11:16:56 -0400 Subject: Issue 24773: Make zoneinfo tests more robust. --- Lib/test/datetimetester.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index e0d23da707..3ffafa7294 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -4677,7 +4677,10 @@ class ZoneInfoTest(unittest.TestCase): def setUp(self): if sys.platform == "win32": self.skipTest("Skipping zoneinfo tests on Windows") - self.tz = ZoneInfo.fromname(self.zonename) + try: + self.tz = ZoneInfo.fromname(self.zonename) + except FileNotFoundError as err: + self.skipTest("Skipping %s: %s" % (self.zonename, err)) def assertEquivDatetimes(self, a, b): self.assertEqual((a.replace(tzinfo=None), a.fold, id(a.tzinfo)), @@ -4738,7 +4741,7 @@ class ZoneInfoTest(unittest.TestCase): # civil time was generally not solar time in those years. self.zonename.startswith('right/')): self.skipTest("Skipping %s" % self.zonename) - tz = ZoneInfo.fromname(self.zonename) + tz = self.tz TZ = os.environ.get('TZ') os.environ['TZ'] = self.zonename try: -- cgit v1.2.1 From a0db23f821a39e5ca0c205a2dab70cb6c98b38ec Mon Sep 17 00:00:00 2001 From: Alexander Belopolsky Date: Sun, 24 Jul 2016 14:39:28 -0400 Subject: Issue #24773: Made ZoneInfoCompleteTest a TestSuit. This should improve the diagnostic and progress reports. --- Lib/test/datetimetester.py | 39 +++++++++++++++++++++------------------ Lib/test/test_datetime.py | 10 ++++++++-- 2 files changed, 29 insertions(+), 20 deletions(-) (limited to 'Lib') diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 3ffafa7294..bb0dae5189 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -2,7 +2,7 @@ See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases """ -from test.support import requires +from test.support import is_resource_enabled import itertools import bisect @@ -1726,7 +1726,7 @@ class TestDateTime(TestDate): # Positional fold: self.assertRaises(TypeError, self.theclass, 2000, 1, 31, 23, 59, 59, 0, None, 1) - + def test_hash_equality(self): d = self.theclass(2000, 12, 31, 23, 30, 17) e = self.theclass(2000, 12, 31, 23, 30, 17) @@ -4254,7 +4254,7 @@ class TestLocalTimeDisambiguation(unittest.TestCase): t.replace(1, 1, 1, None, 1) with self.assertRaises(TypeError): dt.replace(1, 1, 1, 1, 1, 1, 1, None, 1) - + def test_comparison(self): t = time(0) dt = datetime(1, 1, 1) @@ -4677,10 +4677,7 @@ class ZoneInfoTest(unittest.TestCase): def setUp(self): if sys.platform == "win32": self.skipTest("Skipping zoneinfo tests on Windows") - try: - self.tz = ZoneInfo.fromname(self.zonename) - except FileNotFoundError as err: - self.skipTest("Skipping %s: %s" % (self.zonename, err)) + self.tz = ZoneInfo.fromname(self.zonename) def assertEquivDatetimes(self, a, b): self.assertEqual((a.replace(tzinfo=None), a.fold, id(a.tzinfo)), @@ -4741,7 +4738,7 @@ class ZoneInfoTest(unittest.TestCase): # civil time was generally not solar time in those years. self.zonename.startswith('right/')): self.skipTest("Skipping %s" % self.zonename) - tz = self.tz + tz = ZoneInfo.fromname(self.zonename) TZ = os.environ.get('TZ') os.environ['TZ'] = self.zonename try: @@ -4775,20 +4772,26 @@ class ZoneInfoTest(unittest.TestCase): _time.tzset() -class ZoneInfoCompleteTest(unittest.TestCase): - def test_all(self): - requires('tzdata', 'test requires tzdata and a long time to run') - for name in ZoneInfo.zonenames(): - class Test(ZoneInfoTest): - zonename = name - for suffix in ['folds', 'gaps', 'system_transitions']: - test = Test('test_' + suffix) - result = test.run() - self.assertTrue(result.wasSuccessful(), name + ' ' + suffix) +class ZoneInfoCompleteTest(unittest.TestSuite): + def __init__(self): + tests = [] + if is_resource_enabled('tzdata'): + for name in ZoneInfo.zonenames(): + Test = type('ZoneInfoTest[%s]' % name, (ZoneInfoTest,), {}) + Test.zonename = name + for method in dir(Test): + if method.startswith('test_'): + tests.append(Test(method)) + super().__init__(tests) # Iran had a sub-minute UTC offset before 1946. class IranTest(ZoneInfoTest): zonename = 'Iran' +def load_tests(loader, standard_tests, pattern): + standard_tests.addTest(ZoneInfoCompleteTest()) + return standard_tests + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_datetime.py b/Lib/test/test_datetime.py index 2d4eb52c62..242e1bba03 100644 --- a/Lib/test/test_datetime.py +++ b/Lib/test/test_datetime.py @@ -23,9 +23,16 @@ test_suffixes = ["_Pure", "_Fast"] test_classes = [] for module, suffix in zip(test_modules, test_suffixes): + test_classes = [] for name, cls in module.__dict__.items(): - if not (isinstance(cls, type) and issubclass(cls, unittest.TestCase)): + if not isinstance(cls, type): continue + if issubclass(cls, unittest.TestCase): + test_classes.append(cls) + elif issubclass(cls, unittest.TestSuite): + suit = cls() + test_classes.extend(type(test) for test in suit) + for cls in test_classes: cls.__name__ = name + suffix @classmethod def setUpClass(cls_, module=module): @@ -39,7 +46,6 @@ for module, suffix in zip(test_modules, test_suffixes): sys.modules.update(cls_._save_sys_modules) cls.setUpClass = setUpClass cls.tearDownClass = tearDownClass - test_classes.append(cls) def test_main(): run_unittest(*test_classes) -- cgit v1.2.1 From 38c2342e7a98bbee33d808226934af107661f292 Mon Sep 17 00:00:00 2001 From: Alexander Belopolsky Date: Sun, 24 Jul 2016 14:41:08 -0400 Subject: Reindented Lib/test/datetimetester.py. --- Lib/test/datetimetester.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index bb0dae5189..0abba447e4 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -1726,7 +1726,7 @@ class TestDateTime(TestDate): # Positional fold: self.assertRaises(TypeError, self.theclass, 2000, 1, 31, 23, 59, 59, 0, None, 1) - + def test_hash_equality(self): d = self.theclass(2000, 12, 31, 23, 30, 17) e = self.theclass(2000, 12, 31, 23, 30, 17) @@ -4254,7 +4254,7 @@ class TestLocalTimeDisambiguation(unittest.TestCase): t.replace(1, 1, 1, None, 1) with self.assertRaises(TypeError): dt.replace(1, 1, 1, 1, 1, 1, 1, None, 1) - + def test_comparison(self): t = time(0) dt = datetime(1, 1, 1) -- cgit v1.2.1 From 25125a0a8d10de5ee1c5749e38f161170ee2e879 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Sun, 24 Jul 2016 20:35:43 -0400 Subject: Issue #27609: Explicitly return None when there are other returns. In a few cases, reverse a condition and eliminate a return. --- Lib/idlelib/autocomplete.py | 33 +++++++++++++++------------------ Lib/idlelib/autocomplete_w.py | 24 ++++++++++++------------ 2 files changed, 27 insertions(+), 30 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/autocomplete.py b/Lib/idlelib/autocomplete.py index 5ba8dc5dd5..1c0b12d908 100644 --- a/Lib/idlelib/autocomplete.py +++ b/Lib/idlelib/autocomplete.py @@ -37,16 +37,14 @@ class AutoComplete: def __init__(self, editwin=None): self.editwin = editwin - if editwin is None: # subprocess and test - return - self.text = editwin.text - self.autocompletewindow = None - - # id of delayed call, and the index of the text insert when the delayed - # call was issued. If _delayed_completion_id is None, there is no - # delayed call. - self._delayed_completion_id = None - self._delayed_completion_index = None + if editwin is not None: # not in subprocess or test + self.text = editwin.text + self.autocompletewindow = None + # id of delayed call, and the index of the text insert when + # the delayed call was issued. If _delayed_completion_id is + # None, there is no delayed call. + self._delayed_completion_id = None + self._delayed_completion_index = None def _make_autocomplete_window(self): return autocomplete_w.AutoCompleteWindow(self.text) @@ -82,7 +80,7 @@ class AutoComplete: """ if hasattr(event, "mc_state") and event.mc_state: # A modifier was pressed along with the tab, continue as usual. - return + return None if self.autocompletewindow and self.autocompletewindow.is_active(): self.autocompletewindow.complete() return "break" @@ -101,9 +99,8 @@ class AutoComplete: def _delayed_open_completions(self, *args): self._delayed_completion_id = None - if self.text.index("insert") != self._delayed_completion_index: - return - self.open_completions(*args) + if self.text.index("insert") == self._delayed_completion_index: + self.open_completions(*args) def open_completions(self, evalfuncs, complete, userWantsWin, mode=None): """Find the completions and create the AutoCompleteWindow. @@ -148,17 +145,17 @@ class AutoComplete: comp_what = hp.get_expression() if not comp_what or \ (not evalfuncs and comp_what.find('(') != -1): - return + return None else: comp_what = "" else: - return + return None if complete and not comp_what and not comp_start: - return + return None comp_lists = self.fetch_completions(comp_what, mode) if not comp_lists[0]: - return + return None self.autocompletewindow = self._make_autocomplete_window() return not self.autocompletewindow.show_window( comp_lists, "insert-%dc" % len(comp_start), diff --git a/Lib/idlelib/autocomplete_w.py b/Lib/idlelib/autocomplete_w.py index 38f8601156..37d89289a1 100644 --- a/Lib/idlelib/autocomplete_w.py +++ b/Lib/idlelib/autocomplete_w.py @@ -216,6 +216,7 @@ class AutoCompleteWindow: self.winconfigid = acw.bind(WINCONFIG_SEQUENCE, self.winconfig_event) self.doubleclickid = listbox.bind(DOUBLECLICK_SEQUENCE, self.doubleclick_event) + return None def winconfig_event(self, event): if not self.is_active(): @@ -244,11 +245,10 @@ class AutoCompleteWindow: self.hide_window() def listselect_event(self, event): - if not self.is_active(): - return - self.userwantswindow = True - cursel = int(self.listbox.curselection()[0]) - self._change_start(self.completions[cursel]) + if self.is_active(): + self.userwantswindow = True + cursel = int(self.listbox.curselection()[0]) + self._change_start(self.completions[cursel]) def doubleclick_event(self, event): # Put the selected completion in the text, and close the list @@ -258,7 +258,7 @@ class AutoCompleteWindow: def keypress_event(self, event): if not self.is_active(): - return + return None keysym = event.keysym if hasattr(event, "mc_state"): state = event.mc_state @@ -283,7 +283,7 @@ class AutoCompleteWindow: # keysym == "BackSpace" if len(self.start) == 0: self.hide_window() - return + return None self._change_start(self.start[:-1]) self.lasttypedstart = self.start self.listbox.select_clear(0, int(self.listbox.curselection()[0])) @@ -293,7 +293,7 @@ class AutoCompleteWindow: elif keysym == "Return": self.hide_window() - return + return None elif (self.mode == COMPLETE_ATTRIBUTES and keysym in ("period", "space", "parenleft", "parenright", "bracketleft", @@ -309,7 +309,7 @@ class AutoCompleteWindow: and (self.mode == COMPLETE_ATTRIBUTES or self.start): self._change_start(self.completions[cursel]) self.hide_window() - return + return None elif keysym in ("Home", "End", "Prior", "Next", "Up", "Down") and \ not state: @@ -350,12 +350,12 @@ class AutoCompleteWindow: # first tab; let AutoComplete handle the completion self.userwantswindow = True self.lastkey_was_tab = True - return + return None elif any(s in keysym for s in ("Shift", "Control", "Alt", "Meta", "Command", "Option")): # A modifier key, so ignore - return + return None elif event.char and event.char >= ' ': # Regular character with a non-length-1 keycode @@ -369,7 +369,7 @@ class AutoCompleteWindow: else: # Unknown event, close the window and let it through. self.hide_window() - return + return None def keyrelease_event(self, event): if not self.is_active(): -- cgit v1.2.1 From 7defa9f9b06b1880395bcb91eb58569e1f02db6f Mon Sep 17 00:00:00 2001 From: Alexander Belopolsky Date: Sun, 24 Jul 2016 20:36:55 -0400 Subject: Issue 24773: Make zoneinfo tests more robust. (reapply) --- Lib/test/datetimetester.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 0abba447e4..78f0a8726f 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -4677,7 +4677,10 @@ class ZoneInfoTest(unittest.TestCase): def setUp(self): if sys.platform == "win32": self.skipTest("Skipping zoneinfo tests on Windows") - self.tz = ZoneInfo.fromname(self.zonename) + try: + self.tz = ZoneInfo.fromname(self.zonename) + except FileNotFoundError as err: + self.skipTest("Skipping %s: %s" % (self.zonename, err)) def assertEquivDatetimes(self, a, b): self.assertEqual((a.replace(tzinfo=None), a.fold, id(a.tzinfo)), @@ -4738,7 +4741,7 @@ class ZoneInfoTest(unittest.TestCase): # civil time was generally not solar time in those years. self.zonename.startswith('right/')): self.skipTest("Skipping %s" % self.zonename) - tz = ZoneInfo.fromname(self.zonename) + tz = self.tz TZ = os.environ.get('TZ') os.environ['TZ'] = self.zonename try: -- cgit v1.2.1 From f59bee4046bf7bd0b7167312e002afa5fb589f80 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Sun, 24 Jul 2016 23:01:28 -0400 Subject: Issue #19198: IDLE: tab after initial whitespace should tab, not autocomplete. Fixes problem with writing docstrings at lease twice indented. --- Lib/idlelib/autocomplete.py | 9 +++++---- Lib/idlelib/autocomplete_w.py | 5 ++--- Lib/idlelib/idle_test/test_autocomplete.py | 5 +++++ 3 files changed, 12 insertions(+), 7 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/autocomplete.py b/Lib/idlelib/autocomplete.py index 1c0b12d908..1200008ba9 100644 --- a/Lib/idlelib/autocomplete.py +++ b/Lib/idlelib/autocomplete.py @@ -78,16 +78,17 @@ class AutoComplete: open a completion list after that (if there is more than one completion) """ - if hasattr(event, "mc_state") and event.mc_state: - # A modifier was pressed along with the tab, continue as usual. + if hasattr(event, "mc_state") and event.mc_state or\ + not self.text.get("insert linestart", "insert").strip(): + # A modifier was pressed along with the tab or + # there is only previous whitespace on this line, so tab. return None if self.autocompletewindow and self.autocompletewindow.is_active(): self.autocompletewindow.complete() return "break" else: opened = self.open_completions(False, True, True) - if opened: - return "break" + return "break" if opened else None def _open_completions_later(self, *args): self._delayed_completion_index = self.text.index("insert") diff --git a/Lib/idlelib/autocomplete_w.py b/Lib/idlelib/autocomplete_w.py index 37d89289a1..31837e0740 100644 --- a/Lib/idlelib/autocomplete_w.py +++ b/Lib/idlelib/autocomplete_w.py @@ -240,9 +240,8 @@ class AutoCompleteWindow: acw.wm_geometry("+%d+%d" % (new_x, new_y)) def hide_event(self, event): - if not self.is_active(): - return - self.hide_window() + if self.is_active(): + self.hide_window() def listselect_event(self, event): if self.is_active(): diff --git a/Lib/idlelib/idle_test/test_autocomplete.py b/Lib/idlelib/idle_test/test_autocomplete.py index a14c6db349..97bfab5a56 100644 --- a/Lib/idlelib/idle_test/test_autocomplete.py +++ b/Lib/idlelib/idle_test/test_autocomplete.py @@ -97,6 +97,11 @@ class AutoCompleteTest(unittest.TestCase): self.assertIsNone(autocomplete.autocomplete_event(ev)) del ev.mc_state + # Test that tab after whitespace is ignored. + self.text.insert('1.0', ' """Docstring.\n ') + self.assertIsNone(autocomplete.autocomplete_event(ev)) + self.text.delete('1.0', 'end') + # If autocomplete window is open, complete() method is called self.text.insert('1.0', 're.') # This must call autocomplete._make_autocomplete_window() -- cgit v1.2.1 From d668db1ce63db878bfbe6e05ccff9addd3003d64 Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Mon, 25 Jul 2016 02:39:20 +0000 Subject: Issue #1621: Avoid signed overflow in list and tuple operations Patch by Xiang Zhang. --- Lib/test/list_tests.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/list_tests.py b/Lib/test/list_tests.py index f20fdc0a5f..26e93687f4 100644 --- a/Lib/test/list_tests.py +++ b/Lib/test/list_tests.py @@ -266,9 +266,21 @@ class CommonTest(seq_tests.CommonTest): self.assertEqual(a, list("spameggs")) self.assertRaises(TypeError, a.extend, None) - self.assertRaises(TypeError, a.extend) + # overflow test. issue1621 + class CustomIter: + def __iter__(self): + return self + def __next__(self): + raise StopIteration + def __length_hint__(self): + return sys.maxsize + a = self.type2test([1,2,3,4]) + a.extend(CustomIter()) + self.assertEqual(a, [1,2,3,4]) + + def test_insert(self): a = self.type2test([0, 1, 2]) a.insert(0, -2) -- cgit v1.2.1 From 17f0e27fccb38529bc077b187da9c3829d0a3f3d Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Mon, 25 Jul 2016 00:31:54 -0400 Subject: Issue #24137, issue #27611: Restore tkinter after test_idle. --- Lib/test/test_idle.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_idle.py b/Lib/test/test_idle.py index ad88e2451c..b266fcfbdc 100644 --- a/Lib/test/test_idle.py +++ b/Lib/test/test_idle.py @@ -19,3 +19,5 @@ from idlelib.idle_test import load_tests if __name__ == '__main__': unittest.main(verbosity=2, exit=False) + tk._support_default_root = 1 + tk._default_root = None -- cgit v1.2.1 From 89cf813056b4a70a503ee0309f29623ddb0511d1 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Mon, 25 Jul 2016 20:58:43 -0400 Subject: Issue #27620: Escape key closes Query box as cancelled. --- Lib/idlelib/idle_test/htest.py | 6 +++--- Lib/idlelib/query.py | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py index 4d98924d9b..c59ed8c88e 100644 --- a/Lib/idlelib/idle_test/htest.py +++ b/Lib/idlelib/idle_test/htest.py @@ -175,8 +175,8 @@ HelpSource_spec = { "'' and file does not exist are invalid path items.\n" "Any url ('www...', 'http...') is accepted.\n" "Test Browse with and without path, as cannot unittest.\n" - "A valid entry will be printed to shell with [0k]\n" - "or . [Cancel] will print None to shell" + "[Ok] or prints valid entry to shell\n" + "[Cancel] or prints None to shell" } _io_binding_spec = { @@ -245,7 +245,7 @@ Query_spec = { '_htest': True}, 'msg': "Enter with or [Ok]. Print valid entry to Shell\n" "Blank line, after stripping, is ignored\n" - "Close dialog with valid entry, [Cancel] or [X]" + "Close dialog with valid entry, , [Cancel], [X]" } diff --git a/Lib/idlelib/query.py b/Lib/idlelib/query.py index d2d1472a0e..c4e2891f25 100644 --- a/Lib/idlelib/query.py +++ b/Lib/idlelib/query.py @@ -53,6 +53,7 @@ class Query(Toplevel): self.transient(parent) self.grab_set() self.bind('', self.ok) + self.bind('', self.cancel) self.protocol("WM_DELETE_WINDOW", self.cancel) self.parent = parent self.message = message -- cgit v1.2.1 From 2be803c4dcc7a54e9dc474ef86e71e3380378df6 Mon Sep 17 00:00:00 2001 From: Alexander Belopolsky Date: Tue, 26 Jul 2016 12:23:16 -0400 Subject: Issue #24773: Fixed tests failures on systems with 32-bit time_t. Several 32-bit systems have issues with transitions in the year 2037. This is a bug in the system C library since time_t does not overflow until 2038, but let's skip tests starting from 2037 to work around those bugs. --- Lib/test/datetimetester.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'Lib') diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 78f0a8726f..d17c996b23 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -15,6 +15,7 @@ import pickle import random import struct import unittest +import sysconfig from array import array @@ -4675,6 +4676,7 @@ class ZoneInfoTest(unittest.TestCase): zonename = 'America/New_York' def setUp(self): + self.sizeof_time_t = sysconfig.get_config_var('SIZEOF_TIME_T') if sys.platform == "win32": self.skipTest("Skipping zoneinfo tests on Windows") try: @@ -4750,6 +4752,9 @@ class ZoneInfoTest(unittest.TestCase): if self.zonename == 'Europe/Tallinn' and udt.date() == date(1999, 10, 31): print("Skip %s %s transition" % (self.zonename, udt)) continue + if self.sizeof_time_t == 4 and udt.year >= 2037: + print("Skip %s %s transition for 32-bit time_t" % (self.zonename, udt)) + continue s0 = (udt - datetime(1970, 1, 1)) // SEC ss = shift // SEC # shift seconds for x in [-40 * 3600, -20*3600, -1, 0, -- cgit v1.2.1 From 529bef9874d8de2a6109015d232775bedaa7bce3 Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Thu, 28 Jul 2016 01:25:31 +0000 Subject: Issue #27626: Further spelling fixes for 3.6 --- Lib/idlelib/idle_test/test_query.py | 2 +- Lib/test/test_fstring.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/idle_test/test_query.py b/Lib/idlelib/idle_test/test_query.py index d7372a305e..ec86868bd4 100644 --- a/Lib/idlelib/idle_test/test_query.py +++ b/Lib/idlelib/idle_test/test_query.py @@ -146,7 +146,7 @@ class ModuleNameTest(unittest.TestCase): "Test ModuleName subclass of Query." class Dummy_ModuleName: - entry_ok = query.ModuleName.entry_ok # Funtion being tested. + entry_ok = query.ModuleName.entry_ok # Function being tested. text0 = '' entry = Var() diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index a82dedffe4..905ae63126 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -250,7 +250,7 @@ f'{a * x()}'""" ]) self.assertAllRaise(SyntaxError, "invalid syntax", - [# Invalid sytax inside a nested spec. + [# Invalid syntax inside a nested spec. "f'{4:{/5}}'", ]) -- cgit v1.2.1 From d4e20f2d0ce5d362231a4c032b201da87876aff5 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Wed, 27 Jul 2016 21:42:54 -0400 Subject: Issue #27620: Mark the default action button as the default. --- Lib/idlelib/query.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/idlelib/query.py b/Lib/idlelib/query.py index c4e2891f25..c806c6b196 100644 --- a/Lib/idlelib/query.py +++ b/Lib/idlelib/query.py @@ -83,7 +83,7 @@ class Query(Toplevel): self.entry.focus_set() buttons = Frame(self) - self.button_ok = Button(buttons, text='Ok', + self.button_ok = Button(buttons, text='Ok', default='active', width=8, command=self.ok) self.button_cancel = Button(buttons, text='Cancel', width=8, command=self.cancel) -- cgit v1.2.1 From 4556b36da537a59104c0bcd92c59975dbc4bdddd Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Wed, 27 Jul 2016 22:17:05 -0400 Subject: Issue #27620: Make htest box respond to and . --- Lib/idlelib/idle_test/htest.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py index c59ed8c88e..6f676ae865 100644 --- a/Lib/idlelib/idle_test/htest.py +++ b/Lib/idlelib/idle_test/htest.py @@ -380,7 +380,7 @@ def run(*tests): callable_object = None test_kwds = None - def next(): + def next_test(): nonlocal test_name, callable_object, test_kwds if len(test_list) == 1: @@ -395,20 +395,26 @@ def run(*tests): text.insert("1.0",test_spec['msg']) text.configure(state='disabled') # preserve read-only property - def run_test(): + def run_test(_=None): widget = callable_object(**test_kwds) try: print(widget.result) except AttributeError: pass - button = tk.Button(root, textvariable=test_name, command=run_test) + def close(_=None): + root.destroy() + + button = tk.Button(root, textvariable=test_name, + default='active', command=run_test) + next_button = tk.Button(root, text="Next", command=next_test) button.pack() - next_button = tk.Button(root, text="Next", command=next) next_button.pack() + next_button.focus_set() + root.bind('', run_test) + root.bind('', close) - next() - + next_test() root.mainloop() if __name__ == '__main__': -- cgit v1.2.1 From 6ffbf5b3559df21d9d42066b3b54351f3f55156c Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Fri, 29 Jul 2016 22:35:03 +0100 Subject: Closes #1521950: Made shlex parsing more shell-like. --- Lib/shlex.py | 74 ++++++++++++++++++++++---------- Lib/test/test_shlex.py | 112 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+), 21 deletions(-) (limited to 'Lib') diff --git a/Lib/shlex.py b/Lib/shlex.py index f08391800b..e87266f8dd 100644 --- a/Lib/shlex.py +++ b/Lib/shlex.py @@ -5,6 +5,7 @@ # push_source() and pop_source() made explicit by ESR, January 2001. # Posix compliance, split(), string arguments, and # iterator interface by Gustavo Niemeyer, April 2003. +# changes to tokenize more like Posix shells by Vinay Sajip, July 2016. import os import re @@ -17,7 +18,8 @@ __all__ = ["shlex", "split", "quote"] class shlex: "A lexical analyzer class for simple shell-like syntaxes." - def __init__(self, instream=None, infile=None, posix=False): + def __init__(self, instream=None, infile=None, posix=False, + punctuation_chars=False): if isinstance(instream, str): instream = StringIO(instream) if instream is not None: @@ -49,6 +51,19 @@ class shlex: self.token = '' self.filestack = deque() self.source = None + if not punctuation_chars: + punctuation_chars = '' + elif punctuation_chars is True: + punctuation_chars = '();<>|&' + self.punctuation_chars = punctuation_chars + if punctuation_chars: + # _pushback_chars is a push back queue used by lookahead logic + self._pushback_chars = deque() + # these chars added because allowed in file names, args, wildcards + self.wordchars += '~-./*?=' + #remove any punctuation chars from wordchars + t = self.wordchars.maketrans(dict.fromkeys(punctuation_chars)) + self.wordchars = self.wordchars.translate(t) def push_token(self, tok): "Push a token onto the stack popped by the get_token method" @@ -115,12 +130,15 @@ class shlex: quoted = False escapedstate = ' ' while True: - nextchar = self.instream.read(1) + if self.punctuation_chars and self._pushback_chars: + nextchar = self._pushback_chars.pop() + else: + nextchar = self.instream.read(1) if nextchar == '\n': - self.lineno = self.lineno + 1 + self.lineno += 1 if self.debug >= 3: - print("shlex: in state", repr(self.state), \ - "I see character:", repr(nextchar)) + print("shlex: in state %r I see character: %r" % (self.state, + nextchar)) if self.state is None: self.token = '' # past end of file break @@ -137,13 +155,16 @@ class shlex: continue elif nextchar in self.commenters: self.instream.readline() - self.lineno = self.lineno + 1 + self.lineno += 1 elif self.posix and nextchar in self.escape: escapedstate = 'a' self.state = nextchar elif nextchar in self.wordchars: self.token = nextchar self.state = 'a' + elif nextchar in self.punctuation_chars: + self.token = nextchar + self.state = 'c' elif nextchar in self.quotes: if not self.posix: self.token = nextchar @@ -166,17 +187,17 @@ class shlex: raise ValueError("No closing quotation") if nextchar == self.state: if not self.posix: - self.token = self.token + nextchar + self.token += nextchar self.state = ' ' break else: self.state = 'a' - elif self.posix and nextchar in self.escape and \ - self.state in self.escapedquotes: + elif (self.posix and nextchar in self.escape and self.state + in self.escapedquotes): escapedstate = self.state self.state = nextchar else: - self.token = self.token + nextchar + self.token += nextchar elif self.state in self.escape: if not nextchar: # end of file if self.debug >= 2: @@ -185,12 +206,12 @@ class shlex: raise ValueError("No escaped character") # In posix shells, only the quote itself or the escape # character may be escaped within quotes. - if escapedstate in self.quotes and \ - nextchar != self.state and nextchar != escapedstate: - self.token = self.token + self.state - self.token = self.token + nextchar + if (escapedstate in self.quotes and + nextchar != self.state and nextchar != escapedstate): + self.token += self.state + self.token += nextchar self.state = escapedstate - elif self.state == 'a': + elif self.state in ('a', 'c'): if not nextchar: self.state = None # end of file break @@ -204,7 +225,7 @@ class shlex: continue elif nextchar in self.commenters: self.instream.readline() - self.lineno = self.lineno + 1 + self.lineno += 1 if self.posix: self.state = ' ' if self.token or (self.posix and quoted): @@ -216,15 +237,26 @@ class shlex: elif self.posix and nextchar in self.escape: escapedstate = 'a' self.state = nextchar - elif nextchar in self.wordchars or nextchar in self.quotes \ - or self.whitespace_split: - self.token = self.token + nextchar + elif self.state == 'c': + if nextchar in self.punctuation_chars: + self.token += nextchar + else: + if nextchar not in self.whitespace: + self._pushback_chars.append(nextchar) + self.state = ' ' + break + elif (nextchar in self.wordchars or nextchar in self.quotes + or self.whitespace_split): + self.token += nextchar else: - self.pushback.appendleft(nextchar) + if self.punctuation_chars: + self._pushback_chars.append(nextchar) + else: + self.pushback.appendleft(nextchar) if self.debug >= 2: print("shlex: I see punctuation in word state") self.state = ' ' - if self.token: + if self.token or (self.posix and quoted): break # emit current token else: continue diff --git a/Lib/test/test_shlex.py b/Lib/test/test_shlex.py index 4fafdd4f2e..3936c97c8b 100644 --- a/Lib/test/test_shlex.py +++ b/Lib/test/test_shlex.py @@ -173,6 +173,118 @@ class ShlexTest(unittest.TestCase): "%s: %s != %s" % (self.data[i][0], l, self.data[i][1:])) + def testSyntaxSplitAmpersandAndPipe(self): + """Test handling of syntax splitting of &, |""" + # Could take these forms: &&, &, |&, ;&, ;;& + # of course, the same applies to | and || + # these should all parse to the same output + for delimiter in ('&&', '&', '|&', ';&', ';;&', + '||', '|', '&|', ';|', ';;|'): + src = ['echo hi %s echo bye' % delimiter, + 'echo hi%secho bye' % delimiter] + ref = ['echo', 'hi', delimiter, 'echo', 'bye'] + for ss in src: + s = shlex.shlex(ss, punctuation_chars=True) + result = list(s) + self.assertEqual(ref, result, "While splitting '%s'" % ss) + + def testSyntaxSplitSemicolon(self): + """Test handling of syntax splitting of ;""" + # Could take these forms: ;, ;;, ;&, ;;& + # these should all parse to the same output + for delimiter in (';', ';;', ';&', ';;&'): + src = ['echo hi %s echo bye' % delimiter, + 'echo hi%s echo bye' % delimiter, + 'echo hi%secho bye' % delimiter] + ref = ['echo', 'hi', delimiter, 'echo', 'bye'] + for ss in src: + s = shlex.shlex(ss, punctuation_chars=True) + result = list(s) + self.assertEqual(ref, result, "While splitting '%s'" % ss) + + def testSyntaxSplitRedirect(self): + """Test handling of syntax splitting of >""" + # of course, the same applies to <, | + # these should all parse to the same output + for delimiter in ('<', '|'): + src = ['echo hi %s out' % delimiter, + 'echo hi%s out' % delimiter, + 'echo hi%sout' % delimiter] + ref = ['echo', 'hi', delimiter, 'out'] + for ss in src: + s = shlex.shlex(ss, punctuation_chars=True) + result = list(s) + self.assertEqual(ref, result, "While splitting '%s'" % ss) + + def testSyntaxSplitParen(self): + """Test handling of syntax splitting of ()""" + # these should all parse to the same output + src = ['( echo hi )', + '(echo hi)'] + ref = ['(', 'echo', 'hi', ')'] + for ss in src: + s = shlex.shlex(ss, punctuation_chars=True) + result = list(s) + self.assertEqual(ref, result, "While splitting '%s'" % ss) + + def testSyntaxSplitCustom(self): + """Test handling of syntax splitting with custom chars""" + ref = ['~/a', '&', '&', 'b-c', '--color=auto', '||', 'd', '*.py?'] + ss = "~/a && b-c --color=auto || d *.py?" + s = shlex.shlex(ss, punctuation_chars="|") + result = list(s) + self.assertEqual(ref, result, "While splitting '%s'" % ss) + + def testTokenTypes(self): + """Test that tokens are split with types as expected.""" + for source, expected in ( + ('a && b || c', + [('a', 'a'), ('&&', 'c'), ('b', 'a'), + ('||', 'c'), ('c', 'a')]), + ): + s = shlex.shlex(source, punctuation_chars=True) + observed = [] + while True: + t = s.get_token() + if t == s.eof: + break + if t[0] in s.punctuation_chars: + tt = 'c' + else: + tt = 'a' + observed.append((t, tt)) + self.assertEqual(observed, expected) + + def testPunctuationInWordChars(self): + """Test that any punctuation chars are removed from wordchars""" + s = shlex.shlex('a_b__c', punctuation_chars='_') + self.assertNotIn('_', s.wordchars) + self.assertEqual(list(s), ['a', '_', 'b', '__', 'c']) + + def testPunctuationWithWhitespaceSplit(self): + """Test that with whitespace_split, behaviour is as expected""" + s = shlex.shlex('a && b || c', punctuation_chars='&') + # whitespace_split is False, so splitting will be based on + # punctuation_chars + self.assertEqual(list(s), ['a', '&&', 'b', '|', '|', 'c']) + s = shlex.shlex('a && b || c', punctuation_chars='&') + s.whitespace_split = True + # whitespace_split is True, so splitting will be based on + # white space + self.assertEqual(list(s), ['a', '&&', 'b', '||', 'c']) + + def testEmptyStringHandling(self): + """Test that parsing of empty strings is correctly handled.""" + # see Issue #21999 + expected = ['', ')', 'abc'] + for punct in (False, True): + s = shlex.shlex("'')abc", posix=True, punctuation_chars=punct) + slist = list(s) + self.assertEqual(slist, expected) + expected = ["''", ')', 'abc'] + s = shlex.shlex("'')abc", punctuation_chars=True) + self.assertEqual(list(s), expected) + def testQuote(self): safeunquoted = string.ascii_letters + string.digits + '@%_-+=:,./' unicode_sample = '\xe9\xe0\xdf' # e + acute accent, a + grave, sharp s -- cgit v1.2.1 From cb4af3cc4c01baeea5ff7e41ca7de1f877222535 Mon Sep 17 00:00:00 2001 From: Nick Coghlan Date: Sat, 30 Jul 2016 16:26:03 +1000 Subject: Issue #27366: Implement PEP 487 - __init_subclass__ called when new subclasses defined - __set_name__ called when descriptors are part of a class definition --- Lib/test/test_builtin.py | 20 +--- Lib/test/test_descrtut.py | 1 + Lib/test/test_pydoc.py | 3 +- Lib/test/test_subclassinit.py | 244 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 252 insertions(+), 16 deletions(-) create mode 100644 Lib/test/test_subclassinit.py (limited to 'Lib') diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 8cc1b0074b..acc4f9ce81 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1699,21 +1699,11 @@ class TestType(unittest.TestCase): self.assertEqual(x.spam(), 'spam42') self.assertEqual(x.to_bytes(2, 'little'), b'\x2a\x00') - def test_type_new_keywords(self): - class B: - def ham(self): - return 'ham%d' % self - C = type.__new__(type, - name='C', - bases=(B, int), - dict={'spam': lambda self: 'spam%s' % self}) - self.assertEqual(C.__name__, 'C') - self.assertEqual(C.__qualname__, 'C') - self.assertEqual(C.__module__, __name__) - self.assertEqual(C.__bases__, (B, int)) - self.assertIs(C.__base__, int) - self.assertIn('spam', C.__dict__) - self.assertNotIn('ham', C.__dict__) + def test_type_nokwargs(self): + with self.assertRaises(TypeError): + type('a', (), {}, x=5) + with self.assertRaises(TypeError): + type('a', (), dict={}) def test_type_name(self): for name in 'A', '\xc4', '\U0001f40d', 'B.A', '42', '': diff --git a/Lib/test/test_descrtut.py b/Lib/test/test_descrtut.py index 506d1abeb6..b84d644785 100644 --- a/Lib/test/test_descrtut.py +++ b/Lib/test/test_descrtut.py @@ -182,6 +182,7 @@ You can get the information from the list type: '__iadd__', '__imul__', '__init__', + '__init_subclass__', '__iter__', '__le__', '__len__', diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py index 889ce592db..4998597e21 100644 --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -638,8 +638,9 @@ class PydocDocTest(unittest.TestCase): del expected['__doc__'] del expected['__class__'] # inspect resolves descriptors on type into methods, but vars doesn't, - # so we need to update __subclasshook__. + # so we need to update __subclasshook__ and __init_subclass__. expected['__subclasshook__'] = TestClass.__subclasshook__ + expected['__init_subclass__'] = TestClass.__init_subclass__ methods = pydoc.allmethods(TestClass) self.assertDictEqual(methods, expected) diff --git a/Lib/test/test_subclassinit.py b/Lib/test/test_subclassinit.py new file mode 100644 index 0000000000..eb5ed706ff --- /dev/null +++ b/Lib/test/test_subclassinit.py @@ -0,0 +1,244 @@ +from unittest import TestCase, main +import sys +import types + + +class Test(TestCase): + def test_init_subclass(self): + class A(object): + initialized = False + + def __init_subclass__(cls): + super().__init_subclass__() + cls.initialized = True + + class B(A): + pass + + self.assertFalse(A.initialized) + self.assertTrue(B.initialized) + + def test_init_subclass_dict(self): + class A(dict, object): + initialized = False + + def __init_subclass__(cls): + super().__init_subclass__() + cls.initialized = True + + class B(A): + pass + + self.assertFalse(A.initialized) + self.assertTrue(B.initialized) + + def test_init_subclass_kwargs(self): + class A(object): + def __init_subclass__(cls, **kwargs): + cls.kwargs = kwargs + + class B(A, x=3): + pass + + self.assertEqual(B.kwargs, dict(x=3)) + + def test_init_subclass_error(self): + class A(object): + def __init_subclass__(cls): + raise RuntimeError + + with self.assertRaises(RuntimeError): + class B(A): + pass + + def test_init_subclass_wrong(self): + class A(object): + def __init_subclass__(cls, whatever): + pass + + with self.assertRaises(TypeError): + class B(A): + pass + + def test_init_subclass_skipped(self): + class BaseWithInit(object): + def __init_subclass__(cls, **kwargs): + super().__init_subclass__(**kwargs) + cls.initialized = cls + + class BaseWithoutInit(BaseWithInit): + pass + + class A(BaseWithoutInit): + pass + + self.assertIs(A.initialized, A) + self.assertIs(BaseWithoutInit.initialized, BaseWithoutInit) + + def test_init_subclass_diamond(self): + class Base(object): + def __init_subclass__(cls, **kwargs): + super().__init_subclass__(**kwargs) + cls.calls = [] + + class Left(Base): + pass + + class Middle(object): + def __init_subclass__(cls, middle, **kwargs): + super().__init_subclass__(**kwargs) + cls.calls += [middle] + + class Right(Base): + def __init_subclass__(cls, right="right", **kwargs): + super().__init_subclass__(**kwargs) + cls.calls += [right] + + class A(Left, Middle, Right, middle="middle"): + pass + + self.assertEqual(A.calls, ["right", "middle"]) + self.assertEqual(Left.calls, []) + self.assertEqual(Right.calls, []) + + def test_set_name(self): + class Descriptor: + def __set_name__(self, owner, name): + self.owner = owner + self.name = name + + class A(object): + d = Descriptor() + + self.assertEqual(A.d.name, "d") + self.assertIs(A.d.owner, A) + + def test_set_name_metaclass(self): + class Meta(type): + def __new__(cls, name, bases, ns): + ret = super().__new__(cls, name, bases, ns) + self.assertEqual(ret.d.name, "d") + self.assertIs(ret.d.owner, ret) + return 0 + + class Descriptor(object): + def __set_name__(self, owner, name): + self.owner = owner + self.name = name + + class A(object, metaclass=Meta): + d = Descriptor() + self.assertEqual(A, 0) + + def test_set_name_error(self): + class Descriptor: + def __set_name__(self, owner, name): + raise RuntimeError + + with self.assertRaises(RuntimeError): + class A(object): + d = Descriptor() + + def test_set_name_wrong(self): + class Descriptor: + def __set_name__(self): + pass + + with self.assertRaises(TypeError): + class A(object): + d = Descriptor() + + def test_set_name_init_subclass(self): + class Descriptor: + def __set_name__(self, owner, name): + self.owner = owner + self.name = name + + class Meta(type): + def __new__(cls, name, bases, ns): + self = super().__new__(cls, name, bases, ns) + self.meta_owner = self.owner + self.meta_name = self.name + return self + + class A(object): + def __init_subclass__(cls): + cls.owner = cls.d.owner + cls.name = cls.d.name + + class B(A, metaclass=Meta): + d = Descriptor() + + self.assertIs(B.owner, B) + self.assertEqual(B.name, 'd') + self.assertIs(B.meta_owner, B) + self.assertEqual(B.name, 'd') + + def test_errors(self): + class MyMeta(type): + pass + + with self.assertRaises(TypeError): + class MyClass(object, metaclass=MyMeta, otherarg=1): + pass + + with self.assertRaises(TypeError): + types.new_class("MyClass", (object,), + dict(metaclass=MyMeta, otherarg=1)) + types.prepare_class("MyClass", (object,), + dict(metaclass=MyMeta, otherarg=1)) + + class MyMeta(type): + def __init__(self, name, bases, namespace, otherarg): + super().__init__(name, bases, namespace) + + with self.assertRaises(TypeError): + class MyClass(object, metaclass=MyMeta, otherarg=1): + pass + + class MyMeta(type): + def __new__(cls, name, bases, namespace, otherarg): + return super().__new__(cls, name, bases, namespace) + + def __init__(self, name, bases, namespace, otherarg): + super().__init__(name, bases, namespace) + self.otherarg = otherarg + + class MyClass(object, metaclass=MyMeta, otherarg=1): + pass + + self.assertEqual(MyClass.otherarg, 1) + + def test_errors_changed_pep487(self): + # These tests failed before Python 3.6, PEP 487 + class MyMeta(type): + def __new__(cls, name, bases, namespace): + return super().__new__(cls, name=name, bases=bases, + dict=namespace) + + with self.assertRaises(TypeError): + class MyClass(object, metaclass=MyMeta): + pass + + class MyMeta(type): + def __new__(cls, name, bases, namespace, otherarg): + self = super().__new__(cls, name, bases, namespace) + self.otherarg = otherarg + return self + + class MyClass(object, metaclass=MyMeta, otherarg=1): + pass + + self.assertEqual(MyClass.otherarg, 1) + + def test_type(self): + t = type('NewClass', (object,), {}) + self.assertIsInstance(t, type) + self.assertEqual(t.__name__, 'NewClass') + + with self.assertRaises(TypeError): + type(name='NewClass', bases=(object,), dict={}) + + +if __name__ == "__main__": + main() -- cgit v1.2.1 From 320abf0a0c2055e8f172c1aeab9fa51b37a9ac68 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Sat, 30 Jul 2016 14:06:15 +0300 Subject: Issue #27366: Tweak PEP 487 documentation * Added versionadded directives * Deleted duplicate sentence from __init_subclass__ docstring * Modernized tests --- Lib/test/test_subclassinit.py | 44 +++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_subclassinit.py b/Lib/test/test_subclassinit.py index eb5ed706ff..ea6de757c6 100644 --- a/Lib/test/test_subclassinit.py +++ b/Lib/test/test_subclassinit.py @@ -1,11 +1,11 @@ -from unittest import TestCase, main import sys import types +import unittest -class Test(TestCase): +class Test(unittest.TestCase): def test_init_subclass(self): - class A(object): + class A: initialized = False def __init_subclass__(cls): @@ -19,7 +19,7 @@ class Test(TestCase): self.assertTrue(B.initialized) def test_init_subclass_dict(self): - class A(dict, object): + class A(dict): initialized = False def __init_subclass__(cls): @@ -33,7 +33,7 @@ class Test(TestCase): self.assertTrue(B.initialized) def test_init_subclass_kwargs(self): - class A(object): + class A: def __init_subclass__(cls, **kwargs): cls.kwargs = kwargs @@ -43,7 +43,7 @@ class Test(TestCase): self.assertEqual(B.kwargs, dict(x=3)) def test_init_subclass_error(self): - class A(object): + class A: def __init_subclass__(cls): raise RuntimeError @@ -52,7 +52,7 @@ class Test(TestCase): pass def test_init_subclass_wrong(self): - class A(object): + class A: def __init_subclass__(cls, whatever): pass @@ -61,7 +61,7 @@ class Test(TestCase): pass def test_init_subclass_skipped(self): - class BaseWithInit(object): + class BaseWithInit: def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) cls.initialized = cls @@ -76,7 +76,7 @@ class Test(TestCase): self.assertIs(BaseWithoutInit.initialized, BaseWithoutInit) def test_init_subclass_diamond(self): - class Base(object): + class Base: def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) cls.calls = [] @@ -84,7 +84,7 @@ class Test(TestCase): class Left(Base): pass - class Middle(object): + class Middle: def __init_subclass__(cls, middle, **kwargs): super().__init_subclass__(**kwargs) cls.calls += [middle] @@ -107,7 +107,7 @@ class Test(TestCase): self.owner = owner self.name = name - class A(object): + class A: d = Descriptor() self.assertEqual(A.d.name, "d") @@ -121,12 +121,12 @@ class Test(TestCase): self.assertIs(ret.d.owner, ret) return 0 - class Descriptor(object): + class Descriptor: def __set_name__(self, owner, name): self.owner = owner self.name = name - class A(object, metaclass=Meta): + class A(metaclass=Meta): d = Descriptor() self.assertEqual(A, 0) @@ -136,7 +136,7 @@ class Test(TestCase): raise RuntimeError with self.assertRaises(RuntimeError): - class A(object): + class A: d = Descriptor() def test_set_name_wrong(self): @@ -145,7 +145,7 @@ class Test(TestCase): pass with self.assertRaises(TypeError): - class A(object): + class A: d = Descriptor() def test_set_name_init_subclass(self): @@ -161,7 +161,7 @@ class Test(TestCase): self.meta_name = self.name return self - class A(object): + class A: def __init_subclass__(cls): cls.owner = cls.d.owner cls.name = cls.d.name @@ -179,7 +179,7 @@ class Test(TestCase): pass with self.assertRaises(TypeError): - class MyClass(object, metaclass=MyMeta, otherarg=1): + class MyClass(metaclass=MyMeta, otherarg=1): pass with self.assertRaises(TypeError): @@ -193,7 +193,7 @@ class Test(TestCase): super().__init__(name, bases, namespace) with self.assertRaises(TypeError): - class MyClass(object, metaclass=MyMeta, otherarg=1): + class MyClass(metaclass=MyMeta, otherarg=1): pass class MyMeta(type): @@ -204,7 +204,7 @@ class Test(TestCase): super().__init__(name, bases, namespace) self.otherarg = otherarg - class MyClass(object, metaclass=MyMeta, otherarg=1): + class MyClass(metaclass=MyMeta, otherarg=1): pass self.assertEqual(MyClass.otherarg, 1) @@ -217,7 +217,7 @@ class Test(TestCase): dict=namespace) with self.assertRaises(TypeError): - class MyClass(object, metaclass=MyMeta): + class MyClass(metaclass=MyMeta): pass class MyMeta(type): @@ -226,7 +226,7 @@ class Test(TestCase): self.otherarg = otherarg return self - class MyClass(object, metaclass=MyMeta, otherarg=1): + class MyClass(metaclass=MyMeta, otherarg=1): pass self.assertEqual(MyClass.otherarg, 1) @@ -241,4 +241,4 @@ class Test(TestCase): if __name__ == "__main__": - main() + unittest.main() -- cgit v1.2.1 From 29c5e1261382b8956afb2b935777ee9372ec5833 Mon Sep 17 00:00:00 2001 From: Alexander Belopolsky Date: Sat, 30 Jul 2016 11:41:02 -0400 Subject: Issue 24773: Use the standard Asia/Tehran name in the Iran test. --- Lib/test/datetimetester.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index d17c996b23..02deb7c616 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -4794,7 +4794,7 @@ class ZoneInfoCompleteTest(unittest.TestSuite): # Iran had a sub-minute UTC offset before 1946. class IranTest(ZoneInfoTest): - zonename = 'Iran' + zonename = 'Asia/Tehran' def load_tests(loader, standard_tests, pattern): standard_tests.addTest(ZoneInfoCompleteTest()) -- cgit v1.2.1 From 397036ed027b18e04743c0808399debc0338f336 Mon Sep 17 00:00:00 2001 From: Alexander Belopolsky Date: Tue, 2 Aug 2016 17:49:30 -0400 Subject: Closes #27661: Added tzinfo keyword argument to datetime.combine. --- Lib/datetime.py | 6 ++++-- Lib/test/datetimetester.py | 13 ++++++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) (limited to 'Lib') diff --git a/Lib/datetime.py b/Lib/datetime.py index df8eb666a0..9f942a207e 100644 --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -1479,15 +1479,17 @@ class datetime(date): return cls.utcfromtimestamp(t) @classmethod - def combine(cls, date, time): + def combine(cls, date, time, tzinfo=True): "Construct a datetime from a given date and a given time." if not isinstance(date, _date_class): raise TypeError("date argument must be a date instance") if not isinstance(time, _time_class): raise TypeError("time argument must be a time instance") + if tzinfo is True: + tzinfo = time.tzinfo return cls(date.year, date.month, date.day, time.hour, time.minute, time.second, time.microsecond, - time.tzinfo, fold=time.fold) + tzinfo, fold=time.fold) def timetuple(self): "Return local time tuple compatible with time.localtime()." diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 02deb7c616..e71f3aa9b4 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -2117,11 +2117,22 @@ class TestDateTime(TestDate): self.assertRaises(TypeError, combine) # need an arg self.assertRaises(TypeError, combine, d) # need two args self.assertRaises(TypeError, combine, t, d) # args reversed - self.assertRaises(TypeError, combine, d, t, 1) # too many args + self.assertRaises(TypeError, combine, d, t, 1) # wrong tzinfo type + self.assertRaises(TypeError, combine, d, t, 1, 2) # too many args self.assertRaises(TypeError, combine, "date", "time") # wrong types self.assertRaises(TypeError, combine, d, "time") # wrong type self.assertRaises(TypeError, combine, "date", t) # wrong type + # tzinfo= argument + dt = combine(d, t, timezone.utc) + self.assertIs(dt.tzinfo, timezone.utc) + dt = combine(d, t, tzinfo=timezone.utc) + self.assertIs(dt.tzinfo, timezone.utc) + t = time() + dt = combine(dt, t) + self.assertEqual(dt.date(), d) + self.assertEqual(dt.time(), t) + def test_replace(self): cls = self.theclass args = [1, 2, 3, 4, 5, 6, 7] -- cgit v1.2.1 From f64d4105a1c2be31b9fa01bed0bc3e1622ed65bd Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Fri, 5 Aug 2016 16:03:16 -0700 Subject: Add AutoEnum: automatically provides next value if missing. Issue 26988. --- Lib/enum.py | 135 +++++++++++++++++++-- Lib/test/test_enum.py | 324 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 445 insertions(+), 14 deletions(-) (limited to 'Lib') diff --git a/Lib/enum.py b/Lib/enum.py index 99db9e6b7f..eaf50403d2 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -8,7 +8,9 @@ except ImportError: from collections import OrderedDict -__all__ = ['EnumMeta', 'Enum', 'IntEnum', 'unique'] +__all__ = [ + 'EnumMeta', 'Enum', 'IntEnum', 'AutoEnum', 'unique', + ] def _is_descriptor(obj): @@ -52,7 +54,30 @@ class _EnumDict(dict): """ def __init__(self): super().__init__() + # list of enum members self._member_names = [] + # starting value + self._start = None + # last assigned value + self._last_value = None + # when the magic turns off + self._locked = True + # list of temporary names + self._ignore = [] + + def __getitem__(self, key): + if ( + self._generate_next_value_ is None + or self._locked + or key in self + or key in self._ignore + or _is_sunder(key) + or _is_dunder(key) + ): + return super(_EnumDict, self).__getitem__(key) + next_value = self._generate_next_value_(key, self._start, len(self._member_names), self._last_value) + self[key] = next_value + return next_value def __setitem__(self, key, value): """Changes anything not dundered or not a descriptor. @@ -64,19 +89,55 @@ class _EnumDict(dict): """ if _is_sunder(key): - raise ValueError('_names_ are reserved for future Enum use') + if key not in ('_settings_', '_order_', '_ignore_', '_start_', '_generate_next_value_'): + raise ValueError('_names_ are reserved for future Enum use') + elif key == '_generate_next_value_': + if isinstance(value, staticmethod): + value = value.__get__(None, self) + self._generate_next_value_ = value + self._locked = False + elif key == '_ignore_': + if isinstance(value, str): + value = value.split() + else: + value = list(value) + self._ignore = value + already = set(value) & set(self._member_names) + if already: + raise ValueError( + '_ignore_ cannot specify already set names: %r' + % (already, )) + elif key == '_start_': + self._start = value + self._locked = False elif _is_dunder(key): - pass + if key == '__order__': + key = '_order_' + if _is_descriptor(value): + self._locked = True elif key in self._member_names: # descriptor overwriting an enum? raise TypeError('Attempted to reuse key: %r' % key) + elif key in self._ignore: + pass elif not _is_descriptor(value): if key in self: # enum overwriting a descriptor? - raise TypeError('Key already defined as: %r' % self[key]) + raise TypeError('%r already defined as: %r' % (key, self[key])) self._member_names.append(key) + if self._generate_next_value_ is not None: + self._last_value = value + else: + # not a new member, turn off the autoassign magic + self._locked = True super().__setitem__(key, value) + # for magic "auto values" an Enum class should specify a `_generate_next_value_` + # method; that method will be used to generate missing values, and is + # implicitly a staticmethod; + # the signature should be `def _generate_next_value_(name, last_value)` + # last_value will be the last value created and/or assigned, or None + _generate_next_value_ = None # Dummy value for Enum as EnumMeta explicitly checks for it, but of course @@ -84,14 +145,31 @@ class _EnumDict(dict): # This is also why there are checks in EnumMeta like `if Enum is not None` Enum = None - +_ignore_sentinel = object() class EnumMeta(type): """Metaclass for Enum""" @classmethod - def __prepare__(metacls, cls, bases): - return _EnumDict() - - def __new__(metacls, cls, bases, classdict): + def __prepare__(metacls, cls, bases, start=None, ignore=_ignore_sentinel): + # create the namespace dict + enum_dict = _EnumDict() + # inherit previous flags and _generate_next_value_ function + member_type, first_enum = metacls._get_mixins_(bases) + if first_enum is not None: + enum_dict['_generate_next_value_'] = getattr(first_enum, '_generate_next_value_', None) + if start is None: + start = getattr(first_enum, '_start_', None) + if ignore is _ignore_sentinel: + enum_dict['_ignore_'] = 'property classmethod staticmethod'.split() + elif ignore: + enum_dict['_ignore_'] = ignore + if start is not None: + enum_dict['_start_'] = start + return enum_dict + + def __init__(cls, *args , **kwds): + super(EnumMeta, cls).__init__(*args) + + def __new__(metacls, cls, bases, classdict, **kwds): # an Enum class is final once enumeration items have been defined; it # cannot be mixed with other types (int, float, etc.) if it has an # inherited __new__ unless a new __new__ is defined (or the resulting @@ -102,12 +180,24 @@ class EnumMeta(type): # save enum items into separate mapping so they don't get baked into # the new class - members = {k: classdict[k] for k in classdict._member_names} + enum_members = {k: classdict[k] for k in classdict._member_names} for name in classdict._member_names: del classdict[name] + # adjust the sunders + _order_ = classdict.pop('_order_', None) + classdict.pop('_ignore_', None) + + # py3 support for definition order (helps keep py2/py3 code in sync) + if _order_ is not None: + if isinstance(_order_, str): + _order_ = _order_.replace(',', ' ').split() + unique_members = [n for n in clsdict._member_names if n in _order_] + if _order_ != unique_members: + raise TypeError('member order does not match _order_') + # check for illegal enum names (any others?) - invalid_names = set(members) & {'mro', } + invalid_names = set(enum_members) & {'mro', } if invalid_names: raise ValueError('Invalid enum member name: {0}'.format( ','.join(invalid_names))) @@ -151,7 +241,7 @@ class EnumMeta(type): # a custom __new__ is doing something funky with the values -- such as # auto-numbering ;) for member_name in classdict._member_names: - value = members[member_name] + value = enum_members[member_name] if not isinstance(value, tuple): args = (value, ) else: @@ -165,7 +255,10 @@ class EnumMeta(type): else: enum_member = __new__(enum_class, *args) if not hasattr(enum_member, '_value_'): - enum_member._value_ = member_type(*args) + if member_type is object: + enum_member._value_ = value + else: + enum_member._value_ = member_type(*args) value = enum_member._value_ enum_member._name_ = member_name enum_member.__objclass__ = enum_class @@ -572,6 +665,22 @@ class IntEnum(int, Enum): def _reduce_ex_by_name(self, proto): return self.name +class AutoEnum(Enum): + """Enum where values are automatically assigned.""" + def _generate_next_value_(name, start, count, last_value): + """ + Generate the next value when not given. + + name: the name of the member + start: the initital start value or None + count: the number of existing members + last_value: the last value assigned or None + """ + # add one to the last assigned value + if not count: + return start if start is not None else 1 + return last_value + 1 + def unique(enumeration): """Class decorator for enumerations ensuring unique member values.""" duplicates = [] diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index 564c0e9f7d..4a732f908e 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -3,7 +3,7 @@ import inspect import pydoc import unittest from collections import OrderedDict -from enum import Enum, IntEnum, EnumMeta, unique +from enum import EnumMeta, Enum, IntEnum, AutoEnum, unique from io import StringIO from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL from test import support @@ -1570,6 +1570,328 @@ class TestEnum(unittest.TestCase): self.assertEqual(LabelledList.unprocessed, 1) self.assertEqual(LabelledList(1), LabelledList.unprocessed) + def test_ignore_as_str(self): + from datetime import timedelta + class Period(Enum, ignore='Period i'): + """ + different lengths of time + """ + def __new__(cls, value, period): + obj = object.__new__(cls) + obj._value_ = value + obj.period = period + return obj + Period = vars() + for i in range(367): + Period['Day%d' % i] = timedelta(days=i), 'day' + for i in range(53): + Period['Week%d' % i] = timedelta(days=i*7), 'week' + for i in range(13): + Period['Month%d' % i] = i, 'month' + OneDay = Day1 + OneWeek = Week1 + self.assertEqual(Period.Day7.value, timedelta(days=7)) + self.assertEqual(Period.Day7.period, 'day') + + def test_ignore_as_list(self): + from datetime import timedelta + class Period(Enum, ignore=['Period', 'i']): + """ + different lengths of time + """ + def __new__(cls, value, period): + obj = object.__new__(cls) + obj._value_ = value + obj.period = period + return obj + Period = vars() + for i in range(367): + Period['Day%d' % i] = timedelta(days=i), 'day' + for i in range(53): + Period['Week%d' % i] = timedelta(days=i*7), 'week' + for i in range(13): + Period['Month%d' % i] = i, 'month' + OneDay = Day1 + OneWeek = Week1 + self.assertEqual(Period.Day7.value, timedelta(days=7)) + self.assertEqual(Period.Day7.period, 'day') + + def test_new_with_no_value_and_int_base_class(self): + class NoValue(int, Enum): + def __new__(cls, value): + obj = int.__new__(cls, value) + obj.index = len(cls.__members__) + return obj + this = 1 + that = 2 + self.assertEqual(list(NoValue), [NoValue.this, NoValue.that]) + self.assertEqual(NoValue.this, 1) + self.assertEqual(NoValue.this.value, 1) + self.assertEqual(NoValue.this.index, 0) + self.assertEqual(NoValue.that, 2) + self.assertEqual(NoValue.that.value, 2) + self.assertEqual(NoValue.that.index, 1) + + def test_new_with_no_value(self): + class NoValue(Enum): + def __new__(cls, value): + obj = object.__new__(cls) + obj.index = len(cls.__members__) + return obj + this = 1 + that = 2 + self.assertEqual(list(NoValue), [NoValue.this, NoValue.that]) + self.assertEqual(NoValue.this.value, 1) + self.assertEqual(NoValue.this.index, 0) + self.assertEqual(NoValue.that.value, 2) + self.assertEqual(NoValue.that.index, 1) + + +class TestAutoNumber(unittest.TestCase): + + def test_autonumbering(self): + class Color(AutoEnum): + red + green + blue + self.assertEqual(list(Color), [Color.red, Color.green, Color.blue]) + self.assertEqual(Color.red.value, 1) + self.assertEqual(Color.green.value, 2) + self.assertEqual(Color.blue.value, 3) + + def test_autointnumbering(self): + class Color(int, AutoEnum): + red + green + blue + self.assertTrue(isinstance(Color.red, int)) + self.assertEqual(Color.green, 2) + self.assertTrue(Color.blue > Color.red) + + def test_autonumbering_with_start(self): + class Color(AutoEnum, start=7): + red + green + blue + self.assertEqual(list(Color), [Color.red, Color.green, Color.blue]) + self.assertEqual(Color.red.value, 7) + self.assertEqual(Color.green.value, 8) + self.assertEqual(Color.blue.value, 9) + + def test_autonumbering_with_start_and_skip(self): + class Color(AutoEnum, start=7): + red + green + blue = 11 + brown + self.assertEqual(list(Color), [Color.red, Color.green, Color.blue, Color.brown]) + self.assertEqual(Color.red.value, 7) + self.assertEqual(Color.green.value, 8) + self.assertEqual(Color.blue.value, 11) + self.assertEqual(Color.brown.value, 12) + + + def test_badly_overridden_ignore(self): + with self.assertRaisesRegex(TypeError, "'int' object is not callable"): + class Color(AutoEnum): + _ignore_ = () + red + green + blue + @property + def whatever(self): + pass + with self.assertRaisesRegex(TypeError, "'int' object is not callable"): + class Color(AutoEnum, ignore=None): + red + green + blue + @property + def whatever(self): + pass + with self.assertRaisesRegex(TypeError, "'int' object is not callable"): + class Color(AutoEnum, ignore='classmethod staticmethod'): + red + green + blue + @property + def whatever(self): + pass + + def test_property(self): + class Color(AutoEnum): + red + green + blue + @property + def cap_name(self): + return self.name.title() + self.assertEqual(Color.blue.cap_name, 'Blue') + + def test_magic_turns_off(self): + with self.assertRaisesRegex(NameError, "brown"): + class Color(AutoEnum): + red + green + blue + @property + def cap_name(self): + return self.name.title() + brown + + with self.assertRaisesRegex(NameError, "rose"): + class Color(AutoEnum): + red + green + blue + def hello(self): + print('Hello! My serial is %s.' % self.value) + rose + + with self.assertRaisesRegex(NameError, "cyan"): + class Color(AutoEnum): + red + green + blue + def __init__(self, *args): + pass + cyan + + +class TestGenerateMethod(unittest.TestCase): + + def test_autonaming(self): + class Color(Enum): + def _generate_next_value_(name, start, count, last_value): + return name + Red + Green + Blue + self.assertEqual(list(Color), [Color.Red, Color.Green, Color.Blue]) + self.assertEqual(Color.Red.value, 'Red') + self.assertEqual(Color.Green.value, 'Green') + self.assertEqual(Color.Blue.value, 'Blue') + + def test_autonamestr(self): + class Color(str, Enum): + def _generate_next_value_(name, start, count, last_value): + return name + Red + Green + Blue + self.assertTrue(isinstance(Color.Red, str)) + self.assertEqual(Color.Green, 'Green') + self.assertTrue(Color.Blue < Color.Red) + + def test_generate_as_staticmethod(self): + class Color(str, Enum): + @staticmethod + def _generate_next_value_(name, start, count, last_value): + return name.lower() + Red + Green + Blue + self.assertTrue(isinstance(Color.Red, str)) + self.assertEqual(Color.Green, 'green') + self.assertTrue(Color.Blue < Color.Red) + + + def test_overridden_ignore(self): + with self.assertRaisesRegex(TypeError, "'str' object is not callable"): + class Color(Enum): + def _generate_next_value_(name, start, count, last_value): + return name + _ignore_ = () + red + green + blue + @property + def whatever(self): + pass + with self.assertRaisesRegex(TypeError, "'str' object is not callable"): + class Color(Enum, ignore=None): + def _generate_next_value_(name, start, count, last_value): + return name + red + green + blue + @property + def whatever(self): + pass + + def test_property(self): + class Color(Enum): + def _generate_next_value_(name, start, count, last_value): + return name + red + green + blue + @property + def upper_name(self): + return self.name.upper() + self.assertEqual(Color.blue.upper_name, 'BLUE') + + def test_magic_turns_off(self): + with self.assertRaisesRegex(NameError, "brown"): + class Color(Enum): + def _generate_next_value_(name, start, count, last_value): + return name + red + green + blue + @property + def cap_name(self): + return self.name.title() + brown + + with self.assertRaisesRegex(NameError, "rose"): + class Color(Enum): + def _generate_next_value_(name, start, count, last_value): + return name + red + green + blue + def hello(self): + print('Hello! My value %s.' % self.value) + rose + + with self.assertRaisesRegex(NameError, "cyan"): + class Color(Enum): + def _generate_next_value_(name, start, count, last_value): + return name + red + green + blue + def __init__(self, *args): + pass + cyan + + def test_powers_of_two(self): + class Bits(Enum): + def _generate_next_value_(name, start, count, last_value): + return 2 ** count + one + two + four + eight + self.assertEqual(Bits.one.value, 1) + self.assertEqual(Bits.two.value, 2) + self.assertEqual(Bits.four.value, 4) + self.assertEqual(Bits.eight.value, 8) + + def test_powers_of_two_as_int(self): + class Bits(int, Enum): + def _generate_next_value_(name, start, count, last_value): + return 2 ** count + one + two + four + eight + self.assertEqual(Bits.one, 1) + self.assertEqual(Bits.two, 2) + self.assertEqual(Bits.four, 4) + self.assertEqual(Bits.eight, 8) + class TestUnique(unittest.TestCase): -- cgit v1.2.1 From 29efc9ef2b969daa1e95a6b0008409fe045ac40f Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Sat, 6 Aug 2016 10:28:31 +0100 Subject: Closes #27650: Implemented repr methods for logging objects. --- Lib/logging/__init__.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'Lib') diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index fd422ea1e5..4d872bd044 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -935,6 +935,10 @@ class Handler(Filterer): finally: del t, v, tb + def __repr__(self): + level = getLevelName(self.level) + return '<%s (%s)>' % (self.__class__.__name__, level) + class StreamHandler(Handler): """ A handler class which writes logging records, appropriately formatted, @@ -986,6 +990,14 @@ class StreamHandler(Handler): except Exception: self.handleError(record) + def __repr__(self): + level = getLevelName(self.level) + name = getattr(self.stream, 'name', '') + if name: + name += ' ' + return '<%s %s(%s)>' % (self.__class__.__name__, name, level) + + class FileHandler(StreamHandler): """ A handler class which writes formatted logging records to disk files. @@ -1050,6 +1062,11 @@ class FileHandler(StreamHandler): self.stream = self._open() StreamHandler.emit(self, record) + def __repr__(self): + level = getLevelName(self.level) + return '<%s %s (%s)>' % (self.__class__.__name__, self.baseFilename, level) + + class _StderrHandler(StreamHandler): """ This class is like a StreamHandler using sys.stderr, but always uses @@ -1542,6 +1559,11 @@ class Logger(Filterer): suffix = '.'.join((self.name, suffix)) return self.manager.getLogger(suffix) + def __repr__(self): + level = getLevelName(self.getEffectiveLevel()) + return '<%s %s (%s)>' % (self.__class__.__name__, self.name, level) + + class RootLogger(Logger): """ A root logger is not that different to any other logger, except that @@ -1668,6 +1690,11 @@ class LoggerAdapter(object): """ return self.logger.hasHandlers() + def __repr__(self): + logger = self.logger + level = getLevelName(logger.getEffectiveLevel()) + return '<%s %s (%s)>' % (self.__class__.__name__, logger.name, level) + root = RootLogger(WARNING) Logger.root = root Logger.manager = Manager(Logger.root) -- cgit v1.2.1 From 529e1e66fd3c9a9d08686db3b50b8c1f54c12f4a Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Sat, 6 Aug 2016 10:43:44 +0100 Subject: Closes #22829: Added --prompt option to venv. --- Lib/test/test_venv.py | 11 +++++++++++ Lib/venv/__init__.py | 18 +++++++++++++----- 2 files changed, 24 insertions(+), 5 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index a2842b0217..f4ad7c7c5c 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -114,6 +114,17 @@ class BasicTest(BaseTest): print(' %r' % os.listdir(bd)) self.assertTrue(os.path.exists(fn), 'File %r should exist.' % fn) + def test_prompt(self): + env_name = os.path.split(self.env_dir)[1] + + builder = venv.EnvBuilder() + context = builder.ensure_directories(self.env_dir) + self.assertEqual(context.prompt, '(%s) ' % env_name) + + builder = venv.EnvBuilder(prompt='My prompt') + context = builder.ensure_directories(self.env_dir) + self.assertEqual(context.prompt, '(My prompt) ') + @skipInVenv def test_prefixes(self): """ diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py index fa3d2a3056..0a094e3498 100644 --- a/Lib/venv/__init__.py +++ b/Lib/venv/__init__.py @@ -36,15 +36,17 @@ class EnvBuilder: :param upgrade: If True, upgrade an existing virtual environment. :param with_pip: If True, ensure pip is installed in the virtual environment + :param prompt: Alternative terminal prefix for the environment. """ def __init__(self, system_site_packages=False, clear=False, - symlinks=False, upgrade=False, with_pip=False): + symlinks=False, upgrade=False, with_pip=False, prompt=None): self.system_site_packages = system_site_packages self.clear = clear self.symlinks = symlinks self.upgrade = upgrade self.with_pip = with_pip + self.prompt = prompt def create(self, env_dir): """ @@ -90,7 +92,8 @@ class EnvBuilder: context = types.SimpleNamespace() context.env_dir = env_dir context.env_name = os.path.split(env_dir)[1] - context.prompt = '(%s) ' % context.env_name + prompt = self.prompt if self.prompt is not None else context.env_name + context.prompt = '(%s) ' % prompt create_if_needed(env_dir) env = os.environ if sys.platform == 'darwin' and '__PYVENV_LAUNCHER__' in env: @@ -326,10 +329,11 @@ class EnvBuilder: def create(env_dir, system_site_packages=False, clear=False, - symlinks=False, with_pip=False): + symlinks=False, with_pip=False, prompt=None): """Create a virtual environment in a directory.""" builder = EnvBuilder(system_site_packages=system_site_packages, - clear=clear, symlinks=symlinks, with_pip=with_pip) + clear=clear, symlinks=symlinks, with_pip=with_pip, + prompt=prompt) builder.create(env_dir) def main(args=None): @@ -389,6 +393,9 @@ def main(args=None): help='Skips installing or upgrading pip in the ' 'virtual environment (pip is bootstrapped ' 'by default)') + parser.add_argument('--prompt', + help='Provides an alternative prompt prefix for ' + 'this environment.') options = parser.parse_args(args) if options.upgrade and options.clear: raise ValueError('you cannot supply --upgrade and --clear together.') @@ -396,7 +403,8 @@ def main(args=None): clear=options.clear, symlinks=options.symlinks, upgrade=options.upgrade, - with_pip=options.with_pip) + with_pip=options.with_pip, + prompt=options.prompt) for d in options.dirs: builder.create(d) -- cgit v1.2.1 From bb2540a70ca8864f8c62210872762fc7d0cc3a01 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 6 Aug 2016 23:22:08 +0300 Subject: Issue #26800: Undocumented support of general bytes-like objects as paths in os functions is now deprecated. --- Lib/test/test_os.py | 9 ++++++++- Lib/test/test_posix.py | 4 +++- 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index aa9b538748..d8920d99c5 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2626,6 +2626,7 @@ class OSErrorTests(unittest.TestCase): else: encoded = os.fsencode(support.TESTFN) self.bytes_filenames.append(encoded) + self.bytes_filenames.append(bytearray(encoded)) self.bytes_filenames.append(memoryview(encoded)) self.filenames = self.bytes_filenames + self.unicode_filenames @@ -2699,8 +2700,14 @@ class OSErrorTests(unittest.TestCase): for filenames, func, *func_args in funcs: for name in filenames: try: - with bytes_filename_warn(False): + if isinstance(name, str): func(name, *func_args) + elif isinstance(name, bytes): + with bytes_filename_warn(False): + func(name, *func_args) + else: + with self.assertWarnsRegex(DeprecationWarning, 'should be'): + func(name, *func_args) except OSError as err: self.assertIs(err.filename, name) else: diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index 6a1c82917a..de22513e34 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -407,8 +407,10 @@ class PosixTester(unittest.TestCase): def test_stat(self): self.assertTrue(posix.stat(support.TESTFN)) self.assertTrue(posix.stat(os.fsencode(support.TESTFN))) - self.assertTrue(posix.stat(bytearray(os.fsencode(support.TESTFN)))) + self.assertWarnsRegex(DeprecationWarning, + 'should be string, bytes or integer, not', + posix.stat, bytearray(os.fsencode(support.TESTFN))) self.assertRaisesRegex(TypeError, 'should be string, bytes or integer, not', posix.stat, None) -- cgit v1.2.1 From ad8f75ba5400a06f539f014ea840cbae7d12eff9 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 6 Aug 2016 23:29:29 +0300 Subject: Issue #26754: Undocumented support of general bytes-like objects as path in compile() and similar functions is now deprecated. --- Lib/test/test_compile.py | 7 +++++-- Lib/test/test_parser.py | 10 ++++++++-- Lib/test/test_symtable.py | 6 ++++-- Lib/test/test_zipimport.py | 6 ++++-- 4 files changed, 21 insertions(+), 8 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 824e843914..9638e6975a 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -473,10 +473,13 @@ if 1: self.assertEqual(d, {1: 2, 3: 4}) def test_compile_filename(self): - for filename in ('file.py', b'file.py', - bytearray(b'file.py'), memoryview(b'file.py')): + for filename in 'file.py', b'file.py': code = compile('pass', filename, 'exec') self.assertEqual(code.co_filename, 'file.py') + for filename in bytearray(b'file.py'), memoryview(b'file.py'): + with self.assertWarns(DeprecationWarning): + code = compile('pass', filename, 'exec') + self.assertEqual(code.co_filename, 'file.py') self.assertRaises(TypeError, compile, 'pass', list(b'file.py'), 'exec') @support.cpython_only diff --git a/Lib/test/test_parser.py b/Lib/test/test_parser.py index 1e7d331bc0..e2a42f9715 100644 --- a/Lib/test/test_parser.py +++ b/Lib/test/test_parser.py @@ -632,12 +632,18 @@ class CompileTestCase(unittest.TestCase): self.assertEqual(code.co_filename, '') code = st.compile() self.assertEqual(code.co_filename, '') - for filename in ('file.py', b'file.py', - bytearray(b'file.py'), memoryview(b'file.py')): + for filename in 'file.py', b'file.py': code = parser.compilest(st, filename) self.assertEqual(code.co_filename, 'file.py') code = st.compile(filename) self.assertEqual(code.co_filename, 'file.py') + for filename in bytearray(b'file.py'), memoryview(b'file.py'): + with self.assertWarns(DeprecationWarning): + code = parser.compilest(st, filename) + self.assertEqual(code.co_filename, 'file.py') + with self.assertWarns(DeprecationWarning): + code = st.compile(filename) + self.assertEqual(code.co_filename, 'file.py') self.assertRaises(TypeError, parser.compilest, st, list(b'file.py')) self.assertRaises(TypeError, st.compile, list(b'file.py')) diff --git a/Lib/test/test_symtable.py b/Lib/test/test_symtable.py index c5d7facfdb..bf99505623 100644 --- a/Lib/test/test_symtable.py +++ b/Lib/test/test_symtable.py @@ -158,9 +158,11 @@ class SymtableTest(unittest.TestCase): checkfilename("def f(x): foo)(") # parse-time checkfilename("def f(x): global x") # symtable-build-time symtable.symtable("pass", b"spam", "exec") - with self.assertRaises(TypeError): + with self.assertWarns(DeprecationWarning), \ + self.assertRaises(TypeError): symtable.symtable("pass", bytearray(b"spam"), "exec") - symtable.symtable("pass", memoryview(b"spam"), "exec") + with self.assertWarns(DeprecationWarning): + symtable.symtable("pass", memoryview(b"spam"), "exec") with self.assertRaises(TypeError): symtable.symtable("pass", list(b"spam"), "exec") diff --git a/Lib/test/test_zipimport.py b/Lib/test/test_zipimport.py index 20491cde7a..a2482d45a7 100644 --- a/Lib/test/test_zipimport.py +++ b/Lib/test/test_zipimport.py @@ -629,8 +629,10 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase): zipimport.zipimporter(filename) zipimport.zipimporter(os.fsencode(filename)) - zipimport.zipimporter(bytearray(os.fsencode(filename))) - zipimport.zipimporter(memoryview(os.fsencode(filename))) + with self.assertWarns(DeprecationWarning): + zipimport.zipimporter(bytearray(os.fsencode(filename))) + with self.assertWarns(DeprecationWarning): + zipimport.zipimporter(memoryview(os.fsencode(filename))) @support.requires_zlib -- cgit v1.2.1 From e28c42a25e25b49306b342a55ec0d28ef2ee692b Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sun, 7 Aug 2016 10:19:20 -0700 Subject: Issue #27664: Add to concurrent.futures.thread.ThreadPoolExecutor() the ability to specify a thread name prefix. --- Lib/concurrent/futures/thread.py | 11 ++++++++--- Lib/test/test_concurrent_futures.py | 24 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) (limited to 'Lib') diff --git a/Lib/concurrent/futures/thread.py b/Lib/concurrent/futures/thread.py index 3ae442d987..6266f38eb7 100644 --- a/Lib/concurrent/futures/thread.py +++ b/Lib/concurrent/futures/thread.py @@ -81,12 +81,13 @@ def _worker(executor_reference, work_queue): _base.LOGGER.critical('Exception in worker', exc_info=True) class ThreadPoolExecutor(_base.Executor): - def __init__(self, max_workers=None): + def __init__(self, max_workers=None, thread_name_prefix=''): """Initializes a new ThreadPoolExecutor instance. Args: max_workers: The maximum number of threads that can be used to execute the given calls. + thread_name_prefix: An optional name prefix to give our threads. """ if max_workers is None: # Use this number because ThreadPoolExecutor is often @@ -100,6 +101,7 @@ class ThreadPoolExecutor(_base.Executor): self._threads = set() self._shutdown = False self._shutdown_lock = threading.Lock() + self._thread_name_prefix = thread_name_prefix def submit(self, fn, *args, **kwargs): with self._shutdown_lock: @@ -121,8 +123,11 @@ class ThreadPoolExecutor(_base.Executor): q.put(None) # TODO(bquinlan): Should avoid creating new threads if there are more # idle threads than items in the work queue. - if len(self._threads) < self._max_workers: - t = threading.Thread(target=_worker, + num_threads = len(self._threads) + if num_threads < self._max_workers: + thread_name = '%s_%d' % (self._thread_name_prefix or self, + num_threads) + t = threading.Thread(name=thread_name, target=_worker, args=(weakref.ref(self, weakref_cb), self._work_queue)) t.daemon = True diff --git a/Lib/test/test_concurrent_futures.py b/Lib/test/test_concurrent_futures.py index cdb93088a2..46b069c59d 100644 --- a/Lib/test/test_concurrent_futures.py +++ b/Lib/test/test_concurrent_futures.py @@ -154,6 +154,30 @@ class ThreadPoolShutdownTest(ThreadPoolMixin, ExecutorShutdownTest, unittest.Tes for t in threads: t.join() + def test_thread_names_assigned(self): + executor = futures.ThreadPoolExecutor( + max_workers=5, thread_name_prefix='SpecialPool') + executor.map(abs, range(-5, 5)) + threads = executor._threads + del executor + + for t in threads: + self.assertRegex(t.name, r'^SpecialPool_[0-4]$') + t.join() + + def test_thread_names_default(self): + executor = futures.ThreadPoolExecutor(max_workers=5) + executor.map(abs, range(-5, 5)) + threads = executor._threads + del executor + + for t in threads: + # We don't particularly care what the default name is, just that + # it has a default name implying that it is a ThreadPoolExecutor + # followed by what looks like a thread number. + self.assertRegex(t.name, r'^.*ThreadPoolExecutor.*_[0-4]$') + t.join() + class ProcessPoolShutdownTest(ProcessPoolMixin, ExecutorShutdownTest, unittest.TestCase): def _prime_executor(self): -- cgit v1.2.1 From 9162f0b2df3927f38d4ed66abc3e27cd6b4db3b5 Mon Sep 17 00:00:00 2001 From: Alexander Belopolsky Date: Mon, 8 Aug 2016 17:05:40 -0400 Subject: Closes #27710: Disallow fold not in [0, 1] in time and datetime constructors. --- Lib/datetime.py | 14 ++++++++------ Lib/test/datetimetester.py | 5 +++++ 2 files changed, 13 insertions(+), 6 deletions(-) (limited to 'Lib') diff --git a/Lib/datetime.py b/Lib/datetime.py index 9f942a207e..36374aa94c 100644 --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -288,7 +288,7 @@ def _check_date_fields(year, month, day): raise ValueError('day must be in 1..%d' % dim, day) return year, month, day -def _check_time_fields(hour, minute, second, microsecond): +def _check_time_fields(hour, minute, second, microsecond, fold): hour = _check_int_field(hour) minute = _check_int_field(minute) second = _check_int_field(second) @@ -301,7 +301,9 @@ def _check_time_fields(hour, minute, second, microsecond): raise ValueError('second must be in 0..59', second) if not 0 <= microsecond <= 999999: raise ValueError('microsecond must be in 0..999999', microsecond) - return hour, minute, second, microsecond + if fold not in (0, 1): + raise ValueError('fold must be either 0 or 1', fold) + return hour, minute, second, microsecond, fold def _check_tzinfo_arg(tz): if tz is not None and not isinstance(tz, tzinfo): @@ -1059,8 +1061,8 @@ class time: self.__setstate(hour, minute or None) self._hashcode = -1 return self - hour, minute, second, microsecond = _check_time_fields( - hour, minute, second, microsecond) + hour, minute, second, microsecond, fold = _check_time_fields( + hour, minute, second, microsecond, fold) _check_tzinfo_arg(tzinfo) self = object.__new__(cls) self._hour = hour @@ -1369,8 +1371,8 @@ class datetime(date): self._hashcode = -1 return self year, month, day = _check_date_fields(year, month, day) - hour, minute, second, microsecond = _check_time_fields( - hour, minute, second, microsecond) + hour, minute, second, microsecond, fold = _check_time_fields( + hour, minute, second, microsecond, fold) _check_tzinfo_arg(tzinfo) self = object.__new__(cls) self._year = year diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index e71f3aa9b4..726b7fde10 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -1724,6 +1724,11 @@ class TestDateTime(TestDate): self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 59, 1000000) + # bad fold + self.assertRaises(ValueError, self.theclass, + 2000, 1, 31, fold=-1) + self.assertRaises(ValueError, self.theclass, + 2000, 1, 31, fold=2) # Positional fold: self.assertRaises(TypeError, self.theclass, 2000, 1, 31, 23, 59, 59, 0, None, 1) -- cgit v1.2.1 From 1c4a11d45505b9c308f841c0ec36bef1f7b991b7 Mon Sep 17 00:00:00 2001 From: Steven D'Aprano Date: Tue, 9 Aug 2016 12:49:01 +1000 Subject: Add harmonic mean and tests. --- Lib/statistics.py | 66 ++++++++++++++++-- Lib/test/test_statistics.py | 159 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 211 insertions(+), 14 deletions(-) (limited to 'Lib') diff --git a/Lib/statistics.py b/Lib/statistics.py index b081b5a006..8c41dd3463 100644 --- a/Lib/statistics.py +++ b/Lib/statistics.py @@ -28,6 +28,7 @@ Calculating averages Function Description ================== ============================================= mean Arithmetic mean (average) of data. +harmonic_mean Harmonic mean of data. median Median (middle value) of data. median_low Low median of data. median_high High median of data. @@ -95,16 +96,17 @@ A single exception is defined: StatisticsError is a subclass of ValueError. __all__ = [ 'StatisticsError', 'pstdev', 'pvariance', 'stdev', 'variance', 'median', 'median_low', 'median_high', 'median_grouped', - 'mean', 'mode', + 'mean', 'mode', 'harmonic_mean', ] - import collections +import decimal import math +import numbers from fractions import Fraction from decimal import Decimal -from itertools import groupby +from itertools import groupby, chain from bisect import bisect_left, bisect_right @@ -135,7 +137,8 @@ def _sum(data, start=0): Some sources of round-off error will be avoided: - >>> _sum([1e50, 1, -1e50] * 1000) # Built-in sum returns zero. + # Built-in sum returns zero. + >>> _sum([1e50, 1, -1e50] * 1000) (, Fraction(1000, 1), 3000) Fractions and Decimals are also supported: @@ -291,6 +294,15 @@ def _find_rteq(a, l, x): return i-1 raise ValueError + +def _fail_neg(values, errmsg='negative value'): + """Iterate over values, failing if any are less than zero.""" + for x in values: + if x < 0: + raise StatisticsError(errmsg) + yield x + + # === Measures of central tendency (averages) === def mean(data): @@ -319,6 +331,52 @@ def mean(data): return _convert(total/n, T) +def harmonic_mean(data): + """Return the harmonic mean of data. + + The harmonic mean, sometimes called the subcontrary mean, is the + reciprocal of the arithmetic mean of the reciprocals of the data, + and is often appropriate when averaging quantities which are rates + or ratios, for example speeds. Example: + + Suppose an investor purchases an equal value of shares in each of + three companies, with P/E (price/earning) ratios of 2.5, 3 and 10. + What is the average P/E ratio for the investor's portfolio? + + >>> harmonic_mean([2.5, 3, 10]) # For an equal investment portfolio. + 3.6 + + Using the arithmetic mean would give an average of about 5.167, which + is too high. + + If ``data`` is empty, or any element is less than zero, + ``harmonic_mean`` will raise ``StatisticsError``. + """ + # For a justification for using harmonic mean for P/E ratios, see + # http://fixthepitch.pellucid.com/comps-analysis-the-missing-harmony-of-summary-statistics/ + # http://papers.ssrn.com/sol3/papers.cfm?abstract_id=2621087 + if iter(data) is data: + data = list(data) + errmsg = 'harmonic mean does not support negative values' + n = len(data) + if n < 1: + raise StatisticsError('harmonic_mean requires at least one data point') + elif n == 1: + x = data[0] + if isinstance(x, (numbers.Real, Decimal)): + if x < 0: + raise StatisticsError(errmsg) + return x + else: + raise TypeError('unsupported type') + try: + T, total, count = _sum(1/x for x in _fail_neg(data, errmsg)) + except ZeroDivisionError: + return 0 + assert count == n + return _convert(n/total, T) + + # FIXME: investigate ways to calculate medians without sorting? Quickselect? def median(data): """Return the median (middle value) of numeric data. diff --git a/Lib/test/test_statistics.py b/Lib/test/test_statistics.py index cccc1b9343..1542d6460a 100644 --- a/Lib/test/test_statistics.py +++ b/Lib/test/test_statistics.py @@ -21,6 +21,10 @@ import statistics # === Helper functions and class === +def sign(x): + """Return -1.0 for negatives, including -0.0, otherwise +1.0.""" + return math.copysign(1, x) + def _nan_equal(a, b): """Return True if a and b are both the same kind of NAN. @@ -264,6 +268,13 @@ class NumericTestCase(unittest.TestCase): # === Test the helpers === # ======================== +class TestSign(unittest.TestCase): + """Test that the helper function sign() works correctly.""" + def testZeroes(self): + # Test that signed zeroes report their sign correctly. + self.assertEqual(sign(0.0), +1) + self.assertEqual(sign(-0.0), -1) + # --- Tests for approx_equal --- @@ -659,7 +670,7 @@ class DocTests(unittest.TestCase): @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -OO and above") def test_doc_tests(self): - failed, tried = doctest.testmod(statistics) + failed, tried = doctest.testmod(statistics, optionflags=doctest.ELLIPSIS) self.assertGreater(tried, 0) self.assertEqual(failed, 0) @@ -971,6 +982,34 @@ class ConvertTest(unittest.TestCase): self.assertTrue(_nan_equal(x, nan)) +class FailNegTest(unittest.TestCase): + """Test _fail_neg private function.""" + + def test_pass_through(self): + # Test that values are passed through unchanged. + values = [1, 2.0, Fraction(3), Decimal(4)] + new = list(statistics._fail_neg(values)) + self.assertEqual(values, new) + + def test_negatives_raise(self): + # Test that negatives raise an exception. + for x in [1, 2.0, Fraction(3), Decimal(4)]: + seq = [-x] + it = statistics._fail_neg(seq) + self.assertRaises(statistics.StatisticsError, next, it) + + def test_error_msg(self): + # Test that a given error message is used. + msg = "badness #%d" % random.randint(10000, 99999) + try: + next(statistics._fail_neg([-1], msg)) + except statistics.StatisticsError as e: + errmsg = e.args[0] + else: + self.fail("expected exception, but it didn't happen") + self.assertEqual(errmsg, msg) + + # === Tests for public functions === class UnivariateCommonMixin: @@ -1082,13 +1121,13 @@ class UnivariateTypeMixin: Not all tests to do with types need go in this class. Only those that rely on the function returning the same type as its input data. """ - def test_types_conserved(self): - # Test that functions keeps the same type as their data points. - # (Excludes mixed data types.) This only tests the type of the return - # result, not the value. + def prepare_types_for_conservation_test(self): + """Return the types which are expected to be conserved.""" class MyFloat(float): def __truediv__(self, other): return type(self)(super().__truediv__(other)) + def __rtruediv__(self, other): + return type(self)(super().__rtruediv__(other)) def __sub__(self, other): return type(self)(super().__sub__(other)) def __rsub__(self, other): @@ -1098,9 +1137,14 @@ class UnivariateTypeMixin: def __add__(self, other): return type(self)(super().__add__(other)) __radd__ = __add__ + return (float, Decimal, Fraction, MyFloat) + def test_types_conserved(self): + # Test that functions keeps the same type as their data points. + # (Excludes mixed data types.) This only tests the type of the return + # result, not the value. data = self.prepare_data() - for kind in (float, Decimal, Fraction, MyFloat): + for kind in self.prepare_types_for_conservation_test(): d = [kind(x) for x in data] result = self.func(d) self.assertIs(type(result), kind) @@ -1275,12 +1319,16 @@ class AverageMixin(UnivariateCommonMixin): for x in (23, 42.5, 1.3e15, Fraction(15, 19), Decimal('0.28')): self.assertEqual(self.func([x]), x) + def prepare_values_for_repeated_single_test(self): + return (3.5, 17, 2.5e15, Fraction(61, 67), Decimal('4.9712')) + def test_repeated_single_value(self): # The average of a single repeated value is the value itself. - for x in (3.5, 17, 2.5e15, Fraction(61, 67), Decimal('4.9712')): + for x in self.prepare_values_for_repeated_single_test(): for count in (2, 5, 10, 20): - data = [x]*count - self.assertEqual(self.func(data), x) + with self.subTest(x=x, count=count): + data = [x]*count + self.assertEqual(self.func(data), x) class TestMean(NumericTestCase, AverageMixin, UnivariateTypeMixin): @@ -1304,7 +1352,7 @@ class TestMean(NumericTestCase, AverageMixin, UnivariateTypeMixin): self.assertEqual(self.func(data), 22.015625) def test_decimals(self): - # Test mean with ints. + # Test mean with Decimals. D = Decimal data = [D("1.634"), D("2.517"), D("3.912"), D("4.072"), D("5.813")] random.shuffle(data) @@ -1379,6 +1427,97 @@ class TestMean(NumericTestCase, AverageMixin, UnivariateTypeMixin): self.assertEqual(statistics.mean([tiny]*n), tiny) +class TestHarmonicMean(NumericTestCase, AverageMixin, UnivariateTypeMixin): + def setUp(self): + self.func = statistics.harmonic_mean + + def prepare_data(self): + # Override mixin method. + values = super().prepare_data() + values.remove(0) + return values + + def prepare_values_for_repeated_single_test(self): + # Override mixin method. + return (3.5, 17, 2.5e15, Fraction(61, 67), Decimal('4.125')) + + def test_zero(self): + # Test that harmonic mean returns zero when given zero. + values = [1, 0, 2] + self.assertEqual(self.func(values), 0) + + def test_negative_error(self): + # Test that harmonic mean raises when given a negative value. + exc = statistics.StatisticsError + for values in ([-1], [1, -2, 3]): + with self.subTest(values=values): + self.assertRaises(exc, self.func, values) + + def test_ints(self): + # Test harmonic mean with ints. + data = [2, 4, 4, 8, 16, 16] + random.shuffle(data) + self.assertEqual(self.func(data), 6*4/5) + + def test_floats_exact(self): + # Test harmonic mean with some carefully chosen floats. + data = [1/8, 1/4, 1/4, 1/2, 1/2] + random.shuffle(data) + self.assertEqual(self.func(data), 1/4) + self.assertEqual(self.func([0.25, 0.5, 1.0, 1.0]), 0.5) + + def test_singleton_lists(self): + # Test that harmonic mean([x]) returns (approximately) x. + for x in range(1, 101): + if x in (49, 93, 98, 99): + self.assertApproxEqual(self.func([x]), x, tol=2e-14) + else: + self.assertEqual(self.func([x]), x) + + def test_decimals_exact(self): + # Test harmonic mean with some carefully chosen Decimals. + D = Decimal + self.assertEqual(self.func([D(15), D(30), D(60), D(60)]), D(30)) + data = [D("0.05"), D("0.10"), D("0.20"), D("0.20")] + random.shuffle(data) + self.assertEqual(self.func(data), D("0.10")) + data = [D("1.68"), D("0.32"), D("5.94"), D("2.75")] + random.shuffle(data) + self.assertEqual(self.func(data), D(66528)/70723) + + def test_fractions(self): + # Test harmonic mean with Fractions. + F = Fraction + data = [F(1, 2), F(2, 3), F(3, 4), F(4, 5), F(5, 6), F(6, 7), F(7, 8)] + random.shuffle(data) + self.assertEqual(self.func(data), F(7*420, 4029)) + + def test_inf(self): + # Test harmonic mean with infinity. + values = [2.0, float('inf'), 1.0] + self.assertEqual(self.func(values), 2.0) + + def test_nan(self): + # Test harmonic mean with NANs. + values = [2.0, float('nan'), 1.0] + self.assertTrue(math.isnan(self.func(values))) + + def test_multiply_data_points(self): + # Test multiplying every data point by a constant. + c = 111 + data = [3.4, 4.5, 4.9, 6.7, 6.8, 7.2, 8.0, 8.1, 9.4] + expected = self.func(data)*c + result = self.func([x*c for x in data]) + self.assertEqual(result, expected) + + def test_doubled_data(self): + # Harmonic mean of [a,b...z] should be same as for [a,a,b,b...z,z]. + data = [random.uniform(1, 5) for _ in range(1000)] + expected = self.func(data) + actual = self.func(data*2) + self.assertApproxEqual(actual, expected) + + class TestMedian(NumericTestCase, AverageMixin): # Common tests for median and all median.* functions. def setUp(self): -- cgit v1.2.1 From f7ec817f63c33ca18e2f726c5b01544cbc402ffe Mon Sep 17 00:00:00 2001 From: Steven D'Aprano Date: Tue, 9 Aug 2016 13:19:48 +1000 Subject: Tighten up test of harmonic mean on a single value. --- Lib/test/test_statistics.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_statistics.py b/Lib/test/test_statistics.py index 1542d6460a..4b3fd364a7 100644 --- a/Lib/test/test_statistics.py +++ b/Lib/test/test_statistics.py @@ -1469,10 +1469,7 @@ class TestHarmonicMean(NumericTestCase, AverageMixin, UnivariateTypeMixin): def test_singleton_lists(self): # Test that harmonic mean([x]) returns (approximately) x. for x in range(1, 101): - if x in (49, 93, 98, 99): - self.assertApproxEqual(self.func([x]), x, tol=2e-14) - else: - self.assertEqual(self.func([x]), x) + self.assertEqual(self.func([x]), x) def test_decimals_exact(self): # Test harmonic mean with some carefully chosen Decimals. -- cgit v1.2.1 From f211d220c7ce55344639faf6c4301023fe78b17d Mon Sep 17 00:00:00 2001 From: Steven D'Aprano Date: Tue, 9 Aug 2016 13:58:10 +1000 Subject: Issue27181 add geometric mean. --- Lib/statistics.py | 267 +++++++++++++++++++++++++++++++++++++++++ Lib/test/test_statistics.py | 285 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 552 insertions(+) (limited to 'Lib') diff --git a/Lib/statistics.py b/Lib/statistics.py index 8c41dd3463..f4b49b5d0c 100644 --- a/Lib/statistics.py +++ b/Lib/statistics.py @@ -303,6 +303,230 @@ def _fail_neg(values, errmsg='negative value'): yield x +class _nroot_NS: + """Hands off! Don't touch! + + Everything inside this namespace (class) is an even-more-private + implementation detail of the private _nth_root function. + """ + # This class exists only to be used as a namespace, for convenience + # of being able to keep the related functions together, and to + # collapse the group in an editor. If this were C# or C++, I would + # use a Namespace, but the closest Python has is a class. + # + # FIXME possibly move this out into a separate module? + # That feels like overkill, and may encourage people to treat it as + # a public feature. + def __init__(self): + raise TypeError('namespace only, do not instantiate') + + def nth_root(x, n): + """Return the positive nth root of numeric x. + + This may be more accurate than ** or pow(): + + >>> math.pow(1000, 1.0/3) #doctest:+SKIP + 9.999999999999998 + + >>> _nth_root(1000, 3) + 10.0 + >>> _nth_root(11**5, 5) + 11.0 + >>> _nth_root(2, 12) + 1.0594630943592953 + + """ + if not isinstance(n, int): + raise TypeError('degree n must be an int') + if n < 2: + raise ValueError('degree n must be 2 or more') + if isinstance(x, decimal.Decimal): + return _nroot_NS.decimal_nroot(x, n) + elif isinstance(x, numbers.Real): + return _nroot_NS.float_nroot(x, n) + else: + raise TypeError('expected a number, got %s') % type(x).__name__ + + def float_nroot(x, n): + """Handle nth root of Reals, treated as a float.""" + assert isinstance(n, int) and n > 1 + if x < 0: + if n%2 == 0: + raise ValueError('domain error: even root of negative number') + else: + return -_nroot_NS.nroot(-x, n) + elif x == 0: + return math.copysign(0.0, x) + elif x > 0: + try: + isinfinity = math.isinf(x) + except OverflowError: + return _nroot_NS.bignum_nroot(x, n) + else: + if isinfinity: + return float('inf') + else: + return _nroot_NS.nroot(x, n) + else: + assert math.isnan(x) + return float('nan') + + def nroot(x, n): + """Calculate x**(1/n), then improve the answer.""" + # This uses math.pow() to calculate an initial guess for the root, + # then uses the iterated nroot algorithm to improve it. + # + # By my testing, about 8% of the time the iterated algorithm ends + # up converging to a result which is less accurate than the initial + # guess. [FIXME: is this still true?] In that case, we use the + # guess instead of the "improved" value. This way, we're never + # less accurate than math.pow(). + r1 = math.pow(x, 1.0/n) + eps1 = abs(r1**n - x) + if eps1 == 0.0: + # r1 is the exact root, so we're done. By my testing, this + # occurs about 80% of the time for x < 1 and 30% of the + # time for x > 1. + return r1 + else: + try: + r2 = _nroot_NS.iterated_nroot(x, n, r1) + except RuntimeError: + return r1 + else: + eps2 = abs(r2**n - x) + if eps1 < eps2: + return r1 + return r2 + + def iterated_nroot(a, n, g): + """Return the nth root of a, starting with guess g. + + This is a special case of Newton's Method. + https://en.wikipedia.org/wiki/Nth_root_algorithm + """ + np = n - 1 + def iterate(r): + try: + return (np*r + a/math.pow(r, np))/n + except OverflowError: + # If r is large enough, r**np may overflow. If that + # happens, r**-np will be small, but not necessarily zero. + return (np*r + a*math.pow(r, -np))/n + # With a good guess, such as g = a**(1/n), this will converge in + # only a few iterations. However a poor guess can take thousands + # of iterations to converge, if at all. We guard against poor + # guesses by setting an upper limit to the number of iterations. + r1 = g + r2 = iterate(g) + for i in range(1000): + if r1 == r2: + break + # Use Floyd's cycle-finding algorithm to avoid being trapped + # in a cycle. + # https://en.wikipedia.org/wiki/Cycle_detection#Tortoise_and_hare + r1 = iterate(r1) + r2 = iterate(iterate(r2)) + else: + # If the guess is particularly bad, the above may fail to + # converge in any reasonable time. + raise RuntimeError('nth-root failed to converge') + return r2 + + def decimal_nroot(x, n): + """Handle nth root of Decimals.""" + assert isinstance(x, decimal.Decimal) + assert isinstance(n, int) + if x.is_snan(): + # Signalling NANs always raise. + raise decimal.InvalidOperation('nth-root of snan') + if x.is_qnan(): + # Quiet NANs only raise if the context is set to raise, + # otherwise return a NAN. + ctx = decimal.getcontext() + if ctx.traps[decimal.InvalidOperation]: + raise decimal.InvalidOperation('nth-root of nan') + else: + # Preserve the input NAN. + return x + if x.is_infinite(): + return x + # FIXME this hasn't had the extensive testing of the float + # version _iterated_nroot so there's possibly some buggy + # corner cases buried in here. Can it overflow? Fail to + # converge or get trapped in a cycle? Converge to a less + # accurate root? + np = n - 1 + def iterate(r): + return (np*r + x/r**np)/n + r0 = x**(decimal.Decimal(1)/n) + assert isinstance(r0, decimal.Decimal) + r1 = iterate(r0) + while True: + if r1 == r0: + return r1 + r0, r1 = r1, iterate(r1) + + def bignum_nroot(x, n): + """Return the nth root of a positive huge number.""" + assert x > 0 + # I state without proof that ⁿ√x ≈ ⁿ√2·ⁿ√(x//2) + # and that for sufficiently big x the error is acceptible. + # We now halve x until it is small enough to get the root. + m = 0 + while True: + x //= 2 + m += 1 + try: + y = float(x) + except OverflowError: + continue + break + a = _nroot_NS.nroot(y, n) + # At this point, we want the nth-root of 2**m, or 2**(m/n). + # We can write that as 2**(q + r/n) = 2**q * ⁿ√2**r where q = m//n. + q, r = divmod(m, n) + b = 2**q * _nroot_NS.nroot(2**r, n) + return a * b + + +# This is the (private) function for calculating nth roots: +_nth_root = _nroot_NS.nth_root +assert type(_nth_root) is type(lambda: None) + + +def _product(values): + """Return product of values as (exponent, mantissa).""" + errmsg = 'mixed Decimal and float is not supported' + prod = 1 + for x in values: + if isinstance(x, float): + break + prod *= x + else: + return (0, prod) + if isinstance(prod, Decimal): + raise TypeError(errmsg) + # Since floats can overflow easily, we calculate the product as a + # sort of poor-man's BigFloat. Given that: + # + # x = 2**p * m # p == power or exponent (scale), m = mantissa + # + # we can calculate the product of two (or more) x values as: + # + # x1*x2 = 2**p1*m1 * 2**p2*m2 = 2**(p1+p2)*(m1*m2) + # + mant, scale = 1, 0 #math.frexp(prod) # FIXME + for y in chain([x], values): + if isinstance(y, Decimal): + raise TypeError(errmsg) + m1, e1 = math.frexp(y) + m2, e2 = math.frexp(mant) + scale += (e1 + e2) + mant = m1*m2 + return (scale, mant) + + # === Measures of central tendency (averages) === def mean(data): @@ -331,6 +555,49 @@ def mean(data): return _convert(total/n, T) +def geometric_mean(data): + """Return the geometric mean of data. + + The geometric mean is appropriate when averaging quantities which + are multiplied together rather than added, for example growth rates. + Suppose an investment grows by 10% in the first year, falls by 5% in + the second, then grows by 12% in the third, what is the average rate + of growth over the three years? + + >>> geometric_mean([1.10, 0.95, 1.12]) + 1.0538483123382172 + + giving an average growth of 5.385%. Using the arithmetic mean will + give approximately 5.667%, which is too high. + + ``StatisticsError`` will be raised if ``data`` is empty, or any + element is less than zero. + """ + if iter(data) is data: + data = list(data) + errmsg = 'geometric mean does not support negative values' + n = len(data) + if n < 1: + raise StatisticsError('geometric_mean requires at least one data point') + elif n == 1: + x = data[0] + if isinstance(g, (numbers.Real, Decimal)): + if x < 0: + raise StatisticsError(errmsg) + return x + else: + raise TypeError('unsupported type') + else: + scale, prod = _product(_fail_neg(data, errmsg)) + r = _nth_root(prod, n) + if scale: + p, q = divmod(scale, n) + s = 2**p * _nth_root(2**q, n) + else: + s = 1 + return s*r + + def harmonic_mean(data): """Return the harmonic mean of data. diff --git a/Lib/test/test_statistics.py b/Lib/test/test_statistics.py index 4b3fd364a7..8b0c01fd85 100644 --- a/Lib/test/test_statistics.py +++ b/Lib/test/test_statistics.py @@ -1010,6 +1010,291 @@ class FailNegTest(unittest.TestCase): self.assertEqual(errmsg, msg) +class Test_Product(NumericTestCase): + """Test the private _product function.""" + + def test_ints(self): + data = [1, 2, 5, 7, 9] + self.assertEqual(statistics._product(data), (0, 630)) + self.assertEqual(statistics._product(data*100), (0, 630**100)) + + def test_floats(self): + data = [1.0, 2.0, 4.0, 8.0] + self.assertEqual(statistics._product(data), (8, 0.25)) + + def test_overflow(self): + # Test with floats that overflow. + data = [1e300]*5 + self.assertEqual(statistics._product(data), (5980, 0.6928287951283193)) + + def test_fractions(self): + F = Fraction + data = [F(14, 23), F(69, 1), F(665, 529), F(299, 105), F(1683, 39)] + exp, mant = statistics._product(data) + self.assertEqual(exp, 0) + self.assertEqual(mant, F(2*3*7*11*17*19, 23)) + self.assertTrue(isinstance(mant, F)) + # Mixed Fraction and int. + data = [3, 25, F(2, 15)] + exp, mant = statistics._product(data) + self.assertEqual(exp, 0) + self.assertEqual(mant, F(10)) + self.assertTrue(isinstance(mant, F)) + + @unittest.expectedFailure + def test_decimal(self): + D = Decimal + data = [D('24.5'), D('17.6'), D('0.025'), D('1.3')] + assert False + + def test_mixed_decimal_float(self): + # Test that mixed Decimal and float raises. + self.assertRaises(TypeError, statistics._product, [1.0, Decimal(1)]) + self.assertRaises(TypeError, statistics._product, [Decimal(1), 1.0]) + + +class Test_Nth_Root(NumericTestCase): + """Test the functionality of the private _nth_root function.""" + + def setUp(self): + self.nroot = statistics._nth_root + + # --- Special values (infinities, NANs, zeroes) --- + + def test_float_NAN(self): + # Test that the root of a float NAN is a float NAN. + NAN = float('nan') + for n in range(2, 9): + with self.subTest(n=n): + result = self.nroot(NAN, n) + self.assertTrue(math.isnan(result)) + + def test_decimal_QNAN(self): + # Test the behaviour when taking the root of a Decimal quiet NAN. + NAN = decimal.Decimal('nan') + with decimal.localcontext() as ctx: + ctx.traps[decimal.InvalidOperation] = 1 + self.assertRaises(decimal.InvalidOperation, self.nroot, NAN, 5) + ctx.traps[decimal.InvalidOperation] = 0 + self.assertTrue(self.nroot(NAN, 5).is_qnan()) + + def test_decimal_SNAN(self): + # Test that taking the root of a Decimal sNAN always raises. + sNAN = decimal.Decimal('snan') + with decimal.localcontext() as ctx: + ctx.traps[decimal.InvalidOperation] = 1 + self.assertRaises(decimal.InvalidOperation, self.nroot, sNAN, 5) + ctx.traps[decimal.InvalidOperation] = 0 + self.assertRaises(decimal.InvalidOperation, self.nroot, sNAN, 5) + + def test_inf(self): + # Test that the root of infinity is infinity. + for INF in (float('inf'), decimal.Decimal('inf')): + for n in range(2, 9): + with self.subTest(n=n, inf=INF): + self.assertEqual(self.nroot(INF, n), INF) + + def testNInf(self): + # Test that the root of -inf is -inf for odd n. + for NINF in (float('-inf'), decimal.Decimal('-inf')): + for n in range(3, 11, 2): + with self.subTest(n=n, inf=NINF): + self.assertEqual(self.nroot(NINF, n), NINF) + + # FIXME: need to check Decimal zeroes too. + def test_zero(self): + # Test that the root of +0.0 is +0.0. + for n in range(2, 11): + with self.subTest(n=n): + result = self.nroot(+0.0, n) + self.assertEqual(result, 0.0) + self.assertEqual(sign(result), +1) + + # FIXME: need to check Decimal zeroes too. + def test_neg_zero(self): + # Test that the root of -0.0 is -0.0. + for n in range(2, 11): + with self.subTest(n=n): + result = self.nroot(-0.0, n) + self.assertEqual(result, 0.0) + self.assertEqual(sign(result), -1) + + # --- Test return types --- + + def check_result_type(self, x, n, outtype): + self.assertIsInstance(self.nroot(x, n), outtype) + class MySubclass(type(x)): + pass + self.assertIsInstance(self.nroot(MySubclass(x), n), outtype) + + def testDecimal(self): + # Test that Decimal arguments return Decimal results. + self.check_result_type(decimal.Decimal('33.3'), 3, decimal.Decimal) + + def testFloat(self): + # Test that other arguments return float results. + for x in (0.2, Fraction(11, 7), 91): + self.check_result_type(x, 6, float) + + # --- Test bad input --- + + def testBadOrderTypes(self): + # Test that nroot raises correctly when n has the wrong type. + for n in (5.0, 2j, None, 'x', b'x', [], {}, set(), sign): + with self.subTest(n=n): + self.assertRaises(TypeError, self.nroot, 2.5, n) + + def testBadOrderValues(self): + # Test that nroot raises correctly when n has a wrong value. + for n in (1, 0, -1, -2, -87): + with self.subTest(n=n): + self.assertRaises(ValueError, self.nroot, 2.5, n) + + def testBadTypes(self): + # Test that nroot raises correctly when x has the wrong type. + for x in (None, 'x', b'x', [], {}, set(), sign): + with self.subTest(x=x): + self.assertRaises(TypeError, self.nroot, x, 3) + + def testNegativeEvenPower(self): + # Test negative x with even n raises correctly. + x = random.uniform(-20.0, -0.1) + assert x < 0 + for n in range(2, 9, 2): + with self.subTest(x=x, n=n): + self.assertRaises(ValueError, self.nroot, x, n) + + # --- Test that nroot is never worse than calling math.pow() --- + + def check_error_is_no_worse(self, x, n): + y = math.pow(x, n) + with self.subTest(x=x, n=n, y=y): + err1 = abs(self.nroot(y, n) - x) + err2 = abs(math.pow(y, 1.0/n) - x) + self.assertLessEqual(err1, err2) + + def testCompareWithPowSmall(self): + # Compare nroot with pow for small values of x. + for i in range(200): + x = random.uniform(1e-9, 1.0-1e-9) + n = random.choice(range(2, 16)) + self.check_error_is_no_worse(x, n) + + def testCompareWithPowMedium(self): + # Compare nroot with pow for medium-sized values of x. + for i in range(200): + x = random.uniform(1.0, 100.0) + n = random.choice(range(2, 16)) + self.check_error_is_no_worse(x, n) + + def testCompareWithPowLarge(self): + # Compare nroot with pow for largish values of x. + for i in range(200): + x = random.uniform(100.0, 10000.0) + n = random.choice(range(2, 16)) + self.check_error_is_no_worse(x, n) + + def testCompareWithPowHuge(self): + # Compare nroot with pow for huge values of x. + for i in range(200): + x = random.uniform(1e20, 1e50) + # We restrict the order here to avoid an Overflow error. + n = random.choice(range(2, 7)) + self.check_error_is_no_worse(x, n) + + # --- Test for numerically correct answers --- + + def testExactPowers(self): + # Test that small integer powers are calculated exactly. + for i in range(1, 51): + for n in range(2, 16): + if (i, n) == (35, 13): + # See testExpectedFailure35p13 + continue + with self.subTest(i=i, n=n): + x = i**n + self.assertEqual(self.nroot(x, n), i) + + def testExactPowersNegatives(self): + # Test that small negative integer powers are calculated exactly. + for i in range(-1, -51, -1): + for n in range(3, 16, 2): + if (i, n) == (-35, 13): + # See testExpectedFailure35p13 + continue + with self.subTest(i=i, n=n): + x = i**n + assert sign(x) == -1 + self.assertEqual(self.nroot(x, n), i) + + def testExpectedFailure35p13(self): + # Test the expected failure 35**13 is almost exact. + x = 35**13 + err = abs(self.nroot(x, 13) - 35) + self.assertLessEqual(err, 0.000000001) + err = abs(self.nroot(-x, 13) + 35) + self.assertLessEqual(err, 0.000000001) + + def testOne(self): + # Test that the root of 1.0 is 1.0. + for n in range(2, 11): + with self.subTest(n=n): + self.assertEqual(self.nroot(1.0, n), 1.0) + + def testFraction(self): + # Test Fraction results. + x = Fraction(89, 75) + self.assertEqual(self.nroot(x**12, 12), float(x)) + + def testInt(self): + # Test int results. + x = 276 + self.assertEqual(self.nroot(x**24, 24), x) + + def testBigInt(self): + # Test that ints too big to convert to floats work. + bignum = 10**20 # That's not that big... + self.assertEqual(self.nroot(bignum**280, 280), bignum) + # Can we make it bigger? + hugenum = bignum**50 + # Make sure that it is too big to convert to a float. + try: + y = float(hugenum) + except OverflowError: + pass + else: + raise AssertionError('hugenum is not big enough') + self.assertEqual(self.nroot(hugenum, 50), float(bignum)) + + def testDecimal(self): + # Test Decimal results. + for s in '3.759 64.027 5234.338'.split(): + x = decimal.Decimal(s) + with self.subTest(x=x): + a = self.nroot(x**5, 5) + self.assertEqual(a, x) + a = self.nroot(x**17, 17) + self.assertEqual(a, x) + + def testFloat(self): + # Test float results. + for x in (3.04e-16, 18.25, 461.3, 1.9e17): + with self.subTest(x=x): + self.assertEqual(self.nroot(x**3, 3), x) + self.assertEqual(self.nroot(x**8, 8), x) + self.assertEqual(self.nroot(x**11, 11), x) + + +class Test_NthRoot_NS(unittest.TestCase): + """Test internals of the nth_root function, hidden in _nroot_NS.""" + + def test_class_cannot_be_instantiated(self): + # Test that _nroot_NS cannot be instantiated. + # It should be a namespace, like in C++ or C#, but Python + # lacks that feature and so we have to make do with a class. + self.assertRaises(TypeError, statistics._nroot_NS) + + # === Tests for public functions === class UnivariateCommonMixin: -- cgit v1.2.1 From 49b21b10f03eba5ba1b21c324f102ec4054bf4aa Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Wed, 10 Aug 2016 12:50:16 -0400 Subject: Issue #27621: Put query response validation error messages in query box instead of in separate massagebox. Redo tests to match. Add Mac OSX refinements. Original patch by Mark Roseman. --- Lib/idlelib/idle_test/test_query.py | 235 ++++++++++++++---------------------- Lib/idlelib/query.py | 124 +++++++++++-------- 2 files changed, 165 insertions(+), 194 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/idle_test/test_query.py b/Lib/idlelib/idle_test/test_query.py index ec86868bd4..584cd88992 100644 --- a/Lib/idlelib/idle_test/test_query.py +++ b/Lib/idlelib/idle_test/test_query.py @@ -16,21 +16,9 @@ from test.support import requires from tkinter import Tk import unittest from unittest import mock -from idlelib.idle_test.mock_tk import Var, Mbox_func +from idlelib.idle_test.mock_tk import Var from idlelib import query -# Mock entry.showerror messagebox so don't need click to continue -# when entry_ok and path_ok methods call it to display errors. - -orig_showerror = query.showerror -showerror = Mbox_func() # Instance has __call__ method. - -def setUpModule(): - query.showerror = showerror - -def tearDownModule(): - query.showerror = orig_showerror - # NON-GUI TESTS @@ -42,59 +30,49 @@ class QueryTest(unittest.TestCase): entry_ok = query.Query.entry_ok ok = query.Query.ok cancel = query.Query.cancel - # Add attributes needed for the tests. + # Add attributes and initialization needed for tests. entry = Var() - result = None - destroyed = False + entry_error = {} + def __init__(self, dummy_entry): + self.entry.set(dummy_entry) + self.entry_error['text'] = '' + self.result = None + self.destroyed = False + def showerror(self, message): + self.entry_error['text'] = message def destroy(self): self.destroyed = True - dialog = Dummy_Query() - - def setUp(self): - showerror.title = None - self.dialog.result = None - self.dialog.destroyed = False - def test_entry_ok_blank(self): - dialog = self.dialog - Equal = self.assertEqual - dialog.entry.set(' ') - Equal(dialog.entry_ok(), None) - Equal((dialog.result, dialog.destroyed), (None, False)) - Equal(showerror.title, 'Entry Error') - self.assertIn('Blank', showerror.message) + dialog = self.Dummy_Query(' ') + self.assertEqual(dialog.entry_ok(), None) + self.assertEqual((dialog.result, dialog.destroyed), (None, False)) + self.assertIn('blank line', dialog.entry_error['text']) def test_entry_ok_good(self): - dialog = self.dialog + dialog = self.Dummy_Query(' good ') Equal = self.assertEqual - dialog.entry.set(' good ') Equal(dialog.entry_ok(), 'good') Equal((dialog.result, dialog.destroyed), (None, False)) - Equal(showerror.title, None) + Equal(dialog.entry_error['text'], '') def test_ok_blank(self): - dialog = self.dialog - Equal = self.assertEqual - dialog.entry.set('') + dialog = self.Dummy_Query('') dialog.entry.focus_set = mock.Mock() - Equal(dialog.ok(), None) + self.assertEqual(dialog.ok(), None) self.assertTrue(dialog.entry.focus_set.called) del dialog.entry.focus_set - Equal((dialog.result, dialog.destroyed), (None, False)) + self.assertEqual((dialog.result, dialog.destroyed), (None, False)) def test_ok_good(self): - dialog = self.dialog - Equal = self.assertEqual - dialog.entry.set('good') - Equal(dialog.ok(), None) - Equal((dialog.result, dialog.destroyed), ('good', True)) + dialog = self.Dummy_Query('good') + self.assertEqual(dialog.ok(), None) + self.assertEqual((dialog.result, dialog.destroyed), ('good', True)) def test_cancel(self): - dialog = self.dialog - Equal = self.assertEqual - Equal(self.dialog.cancel(), None) - Equal((dialog.result, dialog.destroyed), (None, True)) + dialog = self.Dummy_Query('does not matter') + self.assertEqual(dialog.cancel(), None) + self.assertEqual((dialog.result, dialog.destroyed), (None, True)) class SectionNameTest(unittest.TestCase): @@ -104,42 +82,32 @@ class SectionNameTest(unittest.TestCase): entry_ok = query.SectionName.entry_ok # Function being tested. used_names = ['used'] entry = Var() - - dialog = Dummy_SectionName() - - def setUp(self): - showerror.title = None + entry_error = {} + def __init__(self, dummy_entry): + self.entry.set(dummy_entry) + self.entry_error['text'] = '' + def showerror(self, message): + self.entry_error['text'] = message def test_blank_section_name(self): - dialog = self.dialog - Equal = self.assertEqual - dialog.entry.set(' ') - Equal(dialog.entry_ok(), None) - Equal(showerror.title, 'Name Error') - self.assertIn('No', showerror.message) + dialog = self.Dummy_SectionName(' ') + self.assertEqual(dialog.entry_ok(), None) + self.assertIn('no name', dialog.entry_error['text']) def test_used_section_name(self): - dialog = self.dialog - Equal = self.assertEqual - dialog.entry.set('used') - Equal(self.dialog.entry_ok(), None) - Equal(showerror.title, 'Name Error') - self.assertIn('use', showerror.message) + dialog = self.Dummy_SectionName('used') + self.assertEqual(dialog.entry_ok(), None) + self.assertIn('use', dialog.entry_error['text']) def test_long_section_name(self): - dialog = self.dialog - Equal = self.assertEqual - dialog.entry.set('good'*8) - Equal(self.dialog.entry_ok(), None) - Equal(showerror.title, 'Name Error') - self.assertIn('too long', showerror.message) + dialog = self.Dummy_SectionName('good'*8) + self.assertEqual(dialog.entry_ok(), None) + self.assertIn('longer than 30', dialog.entry_error['text']) def test_good_section_name(self): - dialog = self.dialog - Equal = self.assertEqual - dialog.entry.set(' good ') - Equal(dialog.entry_ok(), 'good') - Equal(showerror.title, None) + dialog = self.Dummy_SectionName(' good ') + self.assertEqual(dialog.entry_ok(), 'good') + self.assertEqual(dialog.entry_error['text'], '') class ModuleNameTest(unittest.TestCase): @@ -149,42 +117,32 @@ class ModuleNameTest(unittest.TestCase): entry_ok = query.ModuleName.entry_ok # Function being tested. text0 = '' entry = Var() - - dialog = Dummy_ModuleName() - - def setUp(self): - showerror.title = None + entry_error = {} + def __init__(self, dummy_entry): + self.entry.set(dummy_entry) + self.entry_error['text'] = '' + def showerror(self, message): + self.entry_error['text'] = message def test_blank_module_name(self): - dialog = self.dialog - Equal = self.assertEqual - dialog.entry.set(' ') - Equal(dialog.entry_ok(), None) - Equal(showerror.title, 'Name Error') - self.assertIn('No', showerror.message) + dialog = self.Dummy_ModuleName(' ') + self.assertEqual(dialog.entry_ok(), None) + self.assertIn('no name', dialog.entry_error['text']) def test_bogus_module_name(self): - dialog = self.dialog - Equal = self.assertEqual - dialog.entry.set('__name_xyz123_should_not_exist__') - Equal(self.dialog.entry_ok(), None) - Equal(showerror.title, 'Import Error') - self.assertIn('not found', showerror.message) + dialog = self.Dummy_ModuleName('__name_xyz123_should_not_exist__') + self.assertEqual(dialog.entry_ok(), None) + self.assertIn('not found', dialog.entry_error['text']) def test_c_source_name(self): - dialog = self.dialog - Equal = self.assertEqual - dialog.entry.set('itertools') - Equal(self.dialog.entry_ok(), None) - Equal(showerror.title, 'Import Error') - self.assertIn('source-based', showerror.message) + dialog = self.Dummy_ModuleName('itertools') + self.assertEqual(dialog.entry_ok(), None) + self.assertIn('source-based', dialog.entry_error['text']) def test_good_module_name(self): - dialog = self.dialog - Equal = self.assertEqual - dialog.entry.set('idlelib') + dialog = self.Dummy_ModuleName('idlelib') self.assertTrue(dialog.entry_ok().endswith('__init__.py')) - Equal(showerror.title, None) + self.assertEqual(dialog.entry_error['text'], '') # 3 HelpSource test classes each test one function. @@ -198,13 +156,13 @@ class HelpsourceBrowsefileTest(unittest.TestCase): browse_file = query.HelpSource.browse_file pathvar = Var() - dialog = Dummy_HelpSource() - def test_file_replaces_path(self): - # Path is widget entry, file is file dialog return. - dialog = self.dialog + dialog = self.Dummy_HelpSource() + # Path is widget entry, either '' or something. + # Func return is file dialog return, either '' or something. + # Func return should override widget entry. + # We need all 4 combination to test all (most) code paths. for path, func, result in ( - # We need all combination to test all (most) code paths. ('', lambda a,b,c:'', ''), ('', lambda a,b,c: __file__, __file__), ('htest', lambda a,b,c:'', 'htest'), @@ -217,78 +175,72 @@ class HelpsourceBrowsefileTest(unittest.TestCase): class HelpsourcePathokTest(unittest.TestCase): - "Test path_ok method of ModuleName subclass of Query." + "Test path_ok method of HelpSource subclass of Query." class Dummy_HelpSource: path_ok = query.HelpSource.path_ok path = Var() - - dialog = Dummy_HelpSource() + path_error = {} + def __init__(self, dummy_path): + self.path.set(dummy_path) + self.path_error['text'] = '' + def showerror(self, message, widget=None): + self.path_error['text'] = message @classmethod def tearDownClass(cls): query.platform = orig_platform - def setUp(self): - showerror.title = None - def test_path_ok_blank(self): - dialog = self.dialog - Equal = self.assertEqual - dialog.path.set(' ') - Equal(dialog.path_ok(), None) - Equal(showerror.title, 'File Path Error') - self.assertIn('No help', showerror.message) + dialog = self.Dummy_HelpSource(' ') + self.assertEqual(dialog.path_ok(), None) + self.assertIn('no help file', dialog.path_error['text']) def test_path_ok_bad(self): - dialog = self.dialog - Equal = self.assertEqual - dialog.path.set(__file__ + 'bad-bad-bad') - Equal(dialog.path_ok(), None) - Equal(showerror.title, 'File Path Error') - self.assertIn('not exist', showerror.message) + dialog = self.Dummy_HelpSource(__file__ + 'bad-bad-bad') + self.assertEqual(dialog.path_ok(), None) + self.assertIn('not exist', dialog.path_error['text']) def test_path_ok_web(self): - dialog = self.dialog + dialog = self.Dummy_HelpSource('') Equal = self.assertEqual for url in 'www.py.org', 'http://py.org': with self.subTest(): dialog.path.set(url) - Equal(dialog.path_ok(), url) - Equal(showerror.title, None) + self.assertEqual(dialog.path_ok(), url) + self.assertEqual(dialog.path_error['text'], '') def test_path_ok_file(self): - dialog = self.dialog - Equal = self.assertEqual + dialog = self.Dummy_HelpSource('') for platform, prefix in ('darwin', 'file://'), ('other', ''): with self.subTest(): query.platform = platform dialog.path.set(__file__) - Equal(dialog.path_ok(), prefix + __file__) - Equal(showerror.title, None) + self.assertEqual(dialog.path_ok(), prefix + __file__) + self.assertEqual(dialog.path_error['text'], '') class HelpsourceEntryokTest(unittest.TestCase): - "Test entry_ok method of ModuleName subclass of Query." + "Test entry_ok method of HelpSource subclass of Query." class Dummy_HelpSource: entry_ok = query.HelpSource.entry_ok + entry_error = {} + path_error = {} def item_ok(self): return self.name def path_ok(self): return self.path - dialog = Dummy_HelpSource() - def test_entry_ok_helpsource(self): - dialog = self.dialog + dialog = self.Dummy_HelpSource() for name, path, result in ((None, None, None), (None, 'doc.txt', None), ('doc', None, None), ('doc', 'doc.txt', ('doc', 'doc.txt'))): with self.subTest(): dialog.name, dialog.path = name, path - self.assertEqual(self.dialog.entry_ok(), result) + self.assertEqual(dialog.entry_ok(), result) # GUI TESTS @@ -344,10 +296,10 @@ class SectionnameGuiTest(unittest.TestCase): root = Tk() dialog = query.SectionName(root, 'T', 't', {'abc'}, _utest=True) Equal = self.assertEqual - Equal(dialog.used_names, {'abc'}) + self.assertEqual(dialog.used_names, {'abc'}) dialog.entry.insert(0, 'okay') dialog.button_ok.invoke() - Equal(dialog.result, 'okay') + self.assertEqual(dialog.result, 'okay') del dialog root.destroy() del root @@ -362,9 +314,8 @@ class ModulenameGuiTest(unittest.TestCase): def test_click_module_name(self): root = Tk() dialog = query.ModuleName(root, 'T', 't', 'idlelib', _utest=True) - Equal = self.assertEqual - Equal(dialog.text0, 'idlelib') - Equal(dialog.entry.get(), 'idlelib') + self.assertEqual(dialog.text0, 'idlelib') + self.assertEqual(dialog.entry.get(), 'idlelib') dialog.button_ok.invoke() self.assertTrue(dialog.result.endswith('__init__.py')) del dialog diff --git a/Lib/idlelib/query.py b/Lib/idlelib/query.py index c806c6b196..a4584df98d 100644 --- a/Lib/idlelib/query.py +++ b/Lib/idlelib/query.py @@ -10,6 +10,8 @@ The 'return value' is .result set to either a valid answer or None. Subclass SectionName gets a name for a new config file section. Configdialog uses it for new highlight theme and keybinding set names. +Subclass ModuleName gets a name for File => Open Module. +Subclass HelpSource gets menu item and path for additions to Help menu. """ # Query and Section name result from splitting GetCfgSectionNameDialog # of configSectionNameDialog.py (temporarily config_sec.py) into @@ -21,10 +23,10 @@ Configdialog uses it for new highlight theme and keybinding set names. import importlib import os from sys import executable, platform # Platform is set for one test. -from tkinter import Toplevel, StringVar +from tkinter import Toplevel, StringVar, W, E, N, S from tkinter import filedialog -from tkinter.messagebox import showerror from tkinter.ttk import Frame, Button, Entry, Label +from tkinter.font import Font class Query(Toplevel): """Base class for getting verified answer from a user. @@ -47,18 +49,26 @@ class Query(Toplevel): """ Toplevel.__init__(self, parent) self.withdraw() # Hide while configuring, especially geometry. - self.configure(borderwidth=5) - self.resizable(height=False, width=False) + self.parent = parent self.title(title) + self.message = message + self.text0 = text0 + self.used_names = used_names self.transient(parent) self.grab_set() - self.bind('', self.ok) + windowingsystem = self.tk.call('tk', 'windowingsystem') + if windowingsystem == 'aqua': + try: + self.tk.call('::tk::unsupported::MacWindowStyle', 'style', + self._w, 'moveableModal', '') + except: + pass + self.bind("", self.cancel) self.bind('', self.cancel) self.protocol("WM_DELETE_WINDOW", self.cancel) - self.parent = parent - self.message = message - self.text0 = text0 - self.used_names = used_names + self.bind('', self.ok) + self.bind("", self.ok) + self.resizable(height=False, width=False) self.create_widgets() self.update_idletasks() # Needed here for winfo_reqwidth below. self.geometry( # Center dialog over parent (or below htest box). @@ -75,32 +85,42 @@ class Query(Toplevel): def create_widgets(self): # Call from override, if any. # Bind to self widgets needed for entry_ok or unittest. - self.frame = frame = Frame(self, borderwidth=2, relief='sunken', ) + self.frame = frame = Frame(self, padding=10) + frame.grid(column=0, row=0, sticky='news') + frame.grid_columnconfigure(0, weight=1) + entrylabel = Label(frame, anchor='w', justify='left', text=self.message) self.entryvar = StringVar(self, self.text0) self.entry = Entry(frame, width=30, textvariable=self.entryvar) self.entry.focus_set() - - buttons = Frame(self) - self.button_ok = Button(buttons, text='Ok', default='active', - width=8, command=self.ok) - self.button_cancel = Button(buttons, text='Cancel', - width=8, command=self.cancel) - - frame.pack(side='top', expand=True, fill='both') - entrylabel.pack(padx=5, pady=5) - self.entry.pack(padx=5, pady=5) - buttons.pack(side='bottom') - self.button_ok.pack(side='left', padx=5) - self.button_cancel.pack(side='right', padx=5) + self.error_font = Font(name='TkCaptionFont', + exists=True, root=self.parent) + self.entry_error = Label(frame, text=' ', foreground='red', + font=self.error_font) + self.button_ok = Button( + frame, text='OK', default='active', command=self.ok) + self.button_cancel = Button( + frame, text='Cancel', command=self.cancel) + + entrylabel.grid(column=0, row=0, columnspan=3, padx=5, sticky=W) + self.entry.grid(column=0, row=1, columnspan=3, padx=5, sticky=W+E, + pady=[10,0]) + self.entry_error.grid(column=0, row=2, columnspan=3, padx=5, + sticky=W+E) + self.button_ok.grid(column=1, row=99, padx=5) + self.button_cancel.grid(column=2, row=99, padx=5) + + def showerror(self, message, widget=None): + #self.bell(displayof=self) + (widget or self.entry_error)['text'] = 'ERROR: ' + message def entry_ok(self): # Example: usually replace. "Return non-blank entry or None." + self.entry_error['text'] = '' entry = self.entry.get().strip() if not entry: - showerror(title='Entry Error', - message='Blank line.', parent=self) + self.showerror('blank line.') return None return entry @@ -134,19 +154,16 @@ class SectionName(Query): def entry_ok(self): "Return sensible ConfigParser section name or None." + self.entry_error['text'] = '' name = self.entry.get().strip() if not name: - showerror(title='Name Error', - message='No name specified.', parent=self) + self.showerror('no name specified.') return None elif len(name)>30: - showerror(title='Name Error', - message='Name too long. It should be no more than '+ - '30 characters.', parent=self) + self.showerror('name is longer than 30 characters.') return None elif name in self.used_names: - showerror(title='Name Error', - message='This name is already in use.', parent=self) + self.showerror('name is already in use.') return None return name @@ -162,30 +179,27 @@ class ModuleName(Query): def entry_ok(self): "Return entered module name as file path or None." + self.entry_error['text'] = '' name = self.entry.get().strip() if not name: - showerror(title='Name Error', - message='No name specified.', parent=self) + self.showerror('no name specified.') return None # XXX Ought to insert current file's directory in front of path. try: spec = importlib.util.find_spec(name) except (ValueError, ImportError) as msg: - showerror("Import Error", str(msg), parent=self) + self.showerror(str(msg)) return None if spec is None: - showerror("Import Error", "module not found", - parent=self) + self.showerror("module not found") return None if not isinstance(spec.loader, importlib.abc.SourceLoader): - showerror("Import Error", "not a source-based module", - parent=self) + self.showerror("not a source-based module") return None try: file_path = spec.loader.get_filename(name) except AttributeError: - showerror("Import Error", - "loader does not support get_filename", + self.showerror("loader does not support get_filename", parent=self) return None return file_path @@ -204,8 +218,9 @@ class HelpSource(Query): """ self.filepath = filepath message = 'Name for item on Help menu:' - super().__init__(parent, title, message, text0=menuitem, - used_names=used_names, _htest=_htest, _utest=_utest) + super().__init__( + parent, title, message, text0=menuitem, + used_names=used_names, _htest=_htest, _utest=_utest) def create_widgets(self): super().create_widgets() @@ -216,10 +231,16 @@ class HelpSource(Query): self.path = Entry(frame, textvariable=self.pathvar, width=40) browse = Button(frame, text='Browse', width=8, command=self.browse_file) + self.path_error = Label(frame, text=' ', foreground='red', + font=self.error_font) - pathlabel.pack(anchor='w', padx=5, pady=3) - self.path.pack(anchor='w', padx=5, pady=3) - browse.pack(pady=3) + pathlabel.grid(column=0, row=10, columnspan=3, padx=5, pady=[10,0], + sticky=W) + self.path.grid(column=0, row=11, columnspan=2, padx=5, sticky=W+E, + pady=[10,0]) + browse.grid(column=2, row=11, padx=5, sticky=W+S) + self.path_error.grid(column=0, row=12, columnspan=3, padx=5, + sticky=W+E) def askfilename(self, filetypes, initdir, initfile): # htest # # Extracted from browse_file so can mock for unittests. @@ -256,17 +277,14 @@ class HelpSource(Query): "Simple validity check for menu file path" path = self.path.get().strip() if not path: #no path specified - showerror(title='File Path Error', - message='No help file path specified.', - parent=self) + self.showerror('no help file path specified.', self.path_error) return None elif not path.startswith(('www.', 'http')): if path[:5] == 'file:': path = path[5:] if not os.path.exists(path): - showerror(title='File Path Error', - message='Help file path does not exist.', - parent=self) + self.showerror('help file path does not exist.', + self.path_error) return None if platform == 'darwin': # for Mac Safari path = "file://" + path @@ -274,6 +292,8 @@ class HelpSource(Query): def entry_ok(self): "Return apparently valid (name, path) or None" + self.entry_error['text'] = '' + self.path_error['text'] = '' name = self.item_ok() path = self.path_ok() return None if name is None or path is None else (name, path) -- cgit v1.2.1 From a13bf1ff3eed70c03f723a55c399716e264d226e Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Wed, 10 Aug 2016 13:16:26 -0400 Subject: Issue #27380: For test_query on Mac, adjust one expected result. --- Lib/idlelib/idle_test/test_query.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/idlelib/idle_test/test_query.py b/Lib/idlelib/idle_test/test_query.py index 584cd88992..e9ed694dc0 100644 --- a/Lib/idlelib/idle_test/test_query.py +++ b/Lib/idlelib/idle_test/test_query.py @@ -13,6 +13,7 @@ Coverage: 94% (100% for Query and SectionName). 6 of 8 missing are ModuleName exceptions I don't know how to trigger. """ from test.support import requires +import sys from tkinter import Tk import unittest from unittest import mock @@ -337,7 +338,8 @@ class HelpsourceGuiTest(unittest.TestCase): Equal(dialog.entry.get(), '__test__') Equal(dialog.path.get(), __file__) dialog.button_ok.invoke() - Equal(dialog.result, ('__test__', __file__)) + prefix = "file://" if sys.platform == 'darwin' else '' + Equal(dialog.result, ('__test__', prefix + __file__)) del dialog root.destroy() del root -- cgit v1.2.1 From 65a19c41b6a398312aa69e55843f60c219f15e99 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Wed, 10 Aug 2016 15:15:25 -0400 Subject: Issue #27714: text_textview now passes when re-run in the same process because test_idle failed while running with test -w (and no -jn). Prevent a non-fatal warning from test_config_key. --- Lib/idlelib/idle_test/test_config_key.py | 3 ++- Lib/idlelib/idle_test/test_textview.py | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/idle_test/test_config_key.py b/Lib/idlelib/idle_test/test_config_key.py index 8109829f10..59d8e817e3 100644 --- a/Lib/idlelib/idle_test/test_config_key.py +++ b/Lib/idlelib/idle_test/test_config_key.py @@ -1,6 +1,6 @@ ''' Test idlelib.config_key. -Coverage: 56% +Coverage: 56% from creating and closing dialog. ''' from idlelib import config_key from test.support import requires @@ -17,6 +17,7 @@ class GetKeysTest(unittest.TestCase): @classmethod def tearDownClass(cls): + cls.root.update() # Stop "can't run event command" warning. cls.root.destroy() del cls.root diff --git a/Lib/idlelib/idle_test/test_textview.py b/Lib/idlelib/idle_test/test_textview.py index 0c625eefe9..c1edcb040c 100644 --- a/Lib/idlelib/idle_test/test_textview.py +++ b/Lib/idlelib/idle_test/test_textview.py @@ -22,8 +22,7 @@ def setUpModule(): root = Tk() def tearDownModule(): - global root, TV - del TV + global root root.update_idletasks() root.destroy() # pyflakes falsely sees root as undefined del root -- cgit v1.2.1 From 6c6ddb6eca31a4538857c99db9fa5038b5c8f4ba Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Wed, 10 Aug 2016 23:44:54 -0400 Subject: Issue #27732: Silence test_idle with dummy bell functions. --- Lib/idlelib/autoexpand.py | 6 ++++-- Lib/idlelib/idle_test/test_autoexpand.py | 2 ++ Lib/idlelib/idle_test/test_parenmatch.py | 13 +++++++++---- Lib/idlelib/idle_test/test_replace.py | 6 +++--- Lib/idlelib/idle_test/test_search.py | 2 ++ Lib/idlelib/idle_test/test_undo.py | 2 +- Lib/idlelib/parenmatch.py | 9 +++------ Lib/idlelib/replace.py | 8 ++++---- Lib/idlelib/search.py | 4 ++-- Lib/idlelib/searchbase.py | 3 ++- 10 files changed, 32 insertions(+), 23 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/autoexpand.py b/Lib/idlelib/autoexpand.py index 7059054281..719060765b 100644 --- a/Lib/idlelib/autoexpand.py +++ b/Lib/idlelib/autoexpand.py @@ -31,6 +31,7 @@ class AutoExpand: def __init__(self, editwin): self.text = editwin.text + self.bell = self.text.bell self.state = None def expand_word_event(self, event): @@ -46,14 +47,14 @@ class AutoExpand: words = self.getwords() index = 0 if not words: - self.text.bell() + self.bell() return "break" word = self.getprevword() self.text.delete("insert - %d chars" % len(word), "insert") newword = words[index] index = (index + 1) % len(words) if index == 0: - self.text.bell() # Warn we cycled around + self.bell() # Warn we cycled around self.text.insert("insert", newword) curinsert = self.text.index("insert") curline = self.text.get("insert linestart", "insert lineend") @@ -99,6 +100,7 @@ class AutoExpand: i = i-1 return line[i:] + if __name__ == '__main__': import unittest unittest.main('idlelib.idle_test.test_autoexpand', verbosity=2) diff --git a/Lib/idlelib/idle_test/test_autoexpand.py b/Lib/idlelib/idle_test/test_autoexpand.py index 5d234dd862..ae8186cdc4 100644 --- a/Lib/idlelib/idle_test/test_autoexpand.py +++ b/Lib/idlelib/idle_test/test_autoexpand.py @@ -22,6 +22,7 @@ class AutoExpandTest(unittest.TestCase): else: cls.text = Text() cls.auto_expand = AutoExpand(Dummy_Editwin(cls.text)) + cls.auto_expand.bell = lambda: None @classmethod def tearDownClass(cls): @@ -137,5 +138,6 @@ class AutoExpandTest(unittest.TestCase): new_state = self.auto_expand.state self.assertNotEqual(initial_state, new_state) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/Lib/idlelib/idle_test/test_parenmatch.py b/Lib/idlelib/idle_test/test_parenmatch.py index d467a9a97a..051f7eac2d 100644 --- a/Lib/idlelib/idle_test/test_parenmatch.py +++ b/Lib/idlelib/idle_test/test_parenmatch.py @@ -38,12 +38,17 @@ class ParenMatchTest(unittest.TestCase): def tearDown(self): self.text.delete('1.0', 'end') + def get_parenmatch(self): + pm = ParenMatch(self.editwin) + pm.bell = lambda: None + return pm + def test_paren_expression(self): """ Test ParenMatch with 'expression' style. """ text = self.text - pm = ParenMatch(self.editwin) + pm = self.get_parenmatch() pm.set_style('expression') text.insert('insert', 'def foobar(a, b') @@ -66,7 +71,7 @@ class ParenMatchTest(unittest.TestCase): Test ParenMatch with 'default' style. """ text = self.text - pm = ParenMatch(self.editwin) + pm = self.get_parenmatch() pm.set_style('default') text.insert('insert', 'def foobar(a, b') @@ -86,7 +91,7 @@ class ParenMatchTest(unittest.TestCase): These cases force conditional expression and alternate paths. """ text = self.text - pm = ParenMatch(self.editwin) + pm = self.get_parenmatch() text.insert('insert', '# this is a commen)') self.assertIsNone(pm.paren_closed_event('event')) @@ -99,7 +104,7 @@ class ParenMatchTest(unittest.TestCase): self.assertIsNone(pm.paren_closed_event('event')) def test_handle_restore_timer(self): - pm = ParenMatch(self.editwin) + pm = self.get_parenmatch() pm.restore_event = Mock() pm.handle_restore_timer(0) self.assertTrue(pm.restore_event.called) diff --git a/Lib/idlelib/idle_test/test_replace.py b/Lib/idlelib/idle_test/test_replace.py index a9f3e15f25..7afd4d9e6c 100644 --- a/Lib/idlelib/idle_test/test_replace.py +++ b/Lib/idlelib/idle_test/test_replace.py @@ -7,7 +7,7 @@ from unittest.mock import Mock from tkinter import Tk, Text from idlelib.idle_test.mock_tk import Mbox import idlelib.searchengine as se -import idlelib.replace as rd +from idlelib.replace import ReplaceDialog orig_mbox = se.tkMessageBox showerror = Mbox.showerror @@ -21,7 +21,8 @@ class ReplaceDialogTest(unittest.TestCase): cls.root.withdraw() se.tkMessageBox = Mbox cls.engine = se.SearchEngine(cls.root) - cls.dialog = rd.ReplaceDialog(cls.root, cls.engine) + cls.dialog = ReplaceDialog(cls.root, cls.engine) + cls.dialog.bell = lambda: None cls.dialog.ok = Mock() cls.text = Text(cls.root) cls.text.undo_block_start = Mock() @@ -70,7 +71,6 @@ class ReplaceDialogTest(unittest.TestCase): # text found and replaced pv.set('a') rv.set('asdf') - self.dialog.open(self.text) replace() equal(text.get('1.8', '1.12'), 'asdf') diff --git a/Lib/idlelib/idle_test/test_search.py b/Lib/idlelib/idle_test/test_search.py index 0735d84356..80fa93adf5 100644 --- a/Lib/idlelib/idle_test/test_search.py +++ b/Lib/idlelib/idle_test/test_search.py @@ -29,6 +29,7 @@ class SearchDialogTest(unittest.TestCase): def setUp(self): self.engine = se.SearchEngine(self.root) self.dialog = sd.SearchDialog(self.root, self.engine) + self.dialog.bell = lambda: None self.text = tk.Text(self.root) self.text.insert('1.0', 'Hello World!') @@ -38,6 +39,7 @@ class SearchDialogTest(unittest.TestCase): self.engine.setpat('') self.assertFalse(self.dialog.find_again(text)) + self.dialog.bell = lambda: None self.engine.setpat('Hello') self.assertTrue(self.dialog.find_again(text)) diff --git a/Lib/idlelib/idle_test/test_undo.py b/Lib/idlelib/idle_test/test_undo.py index 80f1b8071e..e872927a6c 100644 --- a/Lib/idlelib/idle_test/test_undo.py +++ b/Lib/idlelib/idle_test/test_undo.py @@ -29,8 +29,8 @@ class UndoDelegatorTest(unittest.TestCase): def setUp(self): self.delegator = UndoDelegator() + self.delegator.bell = Mock() self.percolator.insertfilter(self.delegator) - self.delegator.bell = Mock(wraps=self.delegator.bell) def tearDown(self): self.percolator.removefilter(self.delegator) diff --git a/Lib/idlelib/parenmatch.py b/Lib/idlelib/parenmatch.py index 14281141f8..9586a3b91d 100644 --- a/Lib/idlelib/parenmatch.py +++ b/Lib/idlelib/parenmatch.py @@ -64,6 +64,7 @@ class ParenMatch: # and deactivate_restore (which calls event_delete). editwin.text.bind(self.RESTORE_VIRTUAL_EVENT_NAME, self.restore_event) + self.bell = self.text.bell if self.BELL else lambda: None self.counter = 0 self.is_restore_active = 0 self.set_style(self.STYLE) @@ -93,7 +94,7 @@ class ParenMatch: indices = (HyperParser(self.editwin, "insert") .get_surrounding_brackets()) if indices is None: - self.warn_mismatched() + self.bell() return self.activate_restore() self.create_tag(indices) @@ -109,7 +110,7 @@ class ParenMatch: return indices = hp.get_surrounding_brackets(_openers[closer], True) if indices is None: - self.warn_mismatched() + self.bell() return self.activate_restore() self.create_tag(indices) @@ -124,10 +125,6 @@ class ParenMatch: if timer_count == self.counter: self.restore_event() - def warn_mismatched(self): - if self.BELL: - self.text.bell() - # any one of the create_tag_XXX methods can be used depending on # the style diff --git a/Lib/idlelib/replace.py b/Lib/idlelib/replace.py index 7c9573330f..367bfc9063 100644 --- a/Lib/idlelib/replace.py +++ b/Lib/idlelib/replace.py @@ -95,7 +95,7 @@ class ReplaceDialog(SearchDialogBase): text = self.text res = self.engine.search_text(text, prog) if not res: - text.bell() + self.bell() return text.tag_remove("sel", "1.0", "end") text.tag_remove("hit", "1.0", "end") @@ -142,7 +142,7 @@ class ReplaceDialog(SearchDialogBase): text = self.text res = self.engine.search_text(text, None, ok) if not res: - text.bell() + self.bell() return False line, m = res i, j = m.span() @@ -204,8 +204,8 @@ class ReplaceDialog(SearchDialogBase): def _replace_dialog(parent): # htest # - from tkinter import Toplevel, Text - from tkiter.ttk import Button + from tkinter import Toplevel, Text, END, SEL + from tkinter.ttk import Button box = Toplevel(parent) box.title("Test ReplaceDialog") diff --git a/Lib/idlelib/search.py b/Lib/idlelib/search.py index 4c2acef7b0..508a35c3f1 100644 --- a/Lib/idlelib/search.py +++ b/Lib/idlelib/search.py @@ -51,7 +51,7 @@ class SearchDialog(SearchDialogBase): selfirst = text.index("sel.first") sellast = text.index("sel.last") if selfirst == first and sellast == last: - text.bell() + self.bell() return False except TclError: pass @@ -61,7 +61,7 @@ class SearchDialog(SearchDialogBase): text.see("insert") return True else: - text.bell() + self.bell() return False def find_selection(self, text): diff --git a/Lib/idlelib/searchbase.py b/Lib/idlelib/searchbase.py index cfb40520e7..b326a1c6aa 100644 --- a/Lib/idlelib/searchbase.py +++ b/Lib/idlelib/searchbase.py @@ -79,6 +79,7 @@ class SearchDialogBase: top.wm_title(self.title) top.wm_iconname(self.icon) self.top = top + self.bell = top.bell self.row = 0 self.top.grid_columnconfigure(0, pad=2, weight=0) @@ -188,7 +189,7 @@ class _searchbase(SearchDialogBase): # htest # width,height, x,y = list(map(int, re.split('[x+]', parent.geometry()))) self.top.geometry("+%d+%d" % (x + 40, y + 175)) - def default_command(self): pass + def default_command(self, dummy): pass if __name__ == '__main__': import unittest -- cgit v1.2.1 From defe1e0d0e623b9abf0a9b995c16c6861ea45725 Mon Sep 17 00:00:00 2001 From: Alexander Belopolsky Date: Thu, 11 Aug 2016 11:01:45 -0400 Subject: Issue #24773: Fix and speed-up ZoneInfoCompleteTest. * Read the zone.tab file for the list of zones to exclude the aliases. * Skip Casablanca and El_Aaiun October 2037 transitions. --- Lib/test/datetimetester.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'Lib') diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 726b7fde10..65eae72235 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -15,7 +15,6 @@ import pickle import random import struct import unittest -import sysconfig from array import array @@ -4591,13 +4590,16 @@ class ZoneInfo(tzinfo): def zonenames(cls, zonedir=None): if zonedir is None: zonedir = cls.zoneroot - for root, _, files in os.walk(zonedir): - for f in files: - p = os.path.join(root, f) - with open(p, 'rb') as o: - magic = o.read(4) - if magic == b'TZif': - yield p[len(zonedir) + 1:] + zone_tab = os.path.join(zonedir, 'zone.tab') + try: + f = open(zone_tab) + except OSError: + return + with f: + for line in f: + line = line.strip() + if line and not line.startswith('#'): + yield line.split()[2] @classmethod def stats(cls, start_year=1): @@ -4692,7 +4694,6 @@ class ZoneInfoTest(unittest.TestCase): zonename = 'America/New_York' def setUp(self): - self.sizeof_time_t = sysconfig.get_config_var('SIZEOF_TIME_T') if sys.platform == "win32": self.skipTest("Skipping zoneinfo tests on Windows") try: @@ -4765,12 +4766,11 @@ class ZoneInfoTest(unittest.TestCase): try: _time.tzset() for udt, shift in tz.transitions(): - if self.zonename == 'Europe/Tallinn' and udt.date() == date(1999, 10, 31): + if (self.zonename == 'Europe/Tallinn' and udt.date() == date(1999, 10, 31) or + self.zonename.endswith(('Casablanca', 'El_Aaiun')) and + udt.date() == date(2037, 10, 4)): print("Skip %s %s transition" % (self.zonename, udt)) continue - if self.sizeof_time_t == 4 and udt.year >= 2037: - print("Skip %s %s transition for 32-bit time_t" % (self.zonename, udt)) - continue s0 = (udt - datetime(1970, 1, 1)) // SEC ss = shift // SEC # shift seconds for x in [-40 * 3600, -20*3600, -1, 0, -- cgit v1.2.1 From 3517d089841796cb8f9982fff81db0b03c65915f Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Fri, 12 Aug 2016 10:53:53 -0700 Subject: Issue #25805: Skip a test for test_pkgutil when __name__ == __main__. Thanks to SilentGhost for the patch. --- Lib/test/test_pkgutil.py | 1 + 1 file changed, 1 insertion(+) (limited to 'Lib') diff --git a/Lib/test/test_pkgutil.py b/Lib/test/test_pkgutil.py index a82058760d..ae2aa1bcea 100644 --- a/Lib/test/test_pkgutil.py +++ b/Lib/test/test_pkgutil.py @@ -413,6 +413,7 @@ class ImportlibMigrationTests(unittest.TestCase): self.assertIsNotNone(pkgutil.get_loader("test.support")) self.assertEqual(len(w.warnings), 0) + @unittest.skipIf(__name__ == '__main__', 'not compatible with __main__') def test_get_loader_handles_missing_loader_attribute(self): global __loader__ this_loader = __loader__ -- cgit v1.2.1 From 5ff9f793b1c1a084d31fa225acbd054165c6b590 Mon Sep 17 00:00:00 2001 From: Alexander Belopolsky Date: Fri, 12 Aug 2016 19:08:15 -0400 Subject: Issue #24773: Skip system tests for transitions in year 2037 and later. --- Lib/test/datetimetester.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'Lib') diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 65eae72235..5413bcaa92 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -4766,9 +4766,11 @@ class ZoneInfoTest(unittest.TestCase): try: _time.tzset() for udt, shift in tz.transitions(): - if (self.zonename == 'Europe/Tallinn' and udt.date() == date(1999, 10, 31) or - self.zonename.endswith(('Casablanca', 'El_Aaiun')) and - udt.date() == date(2037, 10, 4)): + if udt.year >= 2037: + # System support for times around the end of 32-bit time_t + # and later is flaky on many systems. + break + if self.zonename == 'Europe/Tallinn' and udt.date() == date(1999, 10, 31): print("Skip %s %s transition" % (self.zonename, udt)) continue s0 = (udt - datetime(1970, 1, 1)) // SEC -- cgit v1.2.1 From 9e4c1ee698f4d5cd404902fa92bfd887f5b15e9a Mon Sep 17 00:00:00 2001 From: Steven D'Aprano Date: Mon, 15 Aug 2016 01:27:03 +1000 Subject: Issue6422 add autorange method to timeit.Timer --- Lib/test/test_timeit.py | 22 ++++++++++++++++++++++ Lib/timeit.py | 41 ++++++++++++++++++++++++++++++----------- 2 files changed, 52 insertions(+), 11 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_timeit.py b/Lib/test/test_timeit.py index 2db3c1bed2..1a95e2979c 100644 --- a/Lib/test/test_timeit.py +++ b/Lib/test/test_timeit.py @@ -354,6 +354,28 @@ class TestTimeit(unittest.TestCase): s = self.run_main(switches=['-n1', '1/0']) self.assert_exc_string(error_stringio.getvalue(), 'ZeroDivisionError') + def autorange(self, callback=None): + timer = FakeTimer(seconds_per_increment=0.001) + t = timeit.Timer(stmt=self.fake_stmt, setup=self.fake_setup, timer=timer) + return t.autorange(callback) + + def test_autorange(self): + num_loops, time_taken = self.autorange() + self.assertEqual(num_loops, 1000) + self.assertEqual(time_taken, 1.0) + + def test_autorange_with_callback(self): + def callback(a, b): + print("{} {:.3f}".format(a, b)) + with captured_stdout() as s: + num_loops, time_taken = self.autorange(callback) + self.assertEqual(num_loops, 1000) + self.assertEqual(time_taken, 1.0) + expected = ('10 0.010\n' + '100 0.100\n' + '1000 1.000\n') + self.assertEqual(s.getvalue(), expected) + if __name__ == '__main__': unittest.main() diff --git a/Lib/timeit.py b/Lib/timeit.py index 98cb3eb89a..2770efa35a 100644 --- a/Lib/timeit.py +++ b/Lib/timeit.py @@ -207,6 +207,26 @@ class Timer: r.append(t) return r + def autorange(self, callback=None): + """Return the number of loops so that total time >= 0.2. + + Calls the timeit method with *number* set to successive powers of + ten (10, 100, 1000, ...) up to a maximum of one billion, until + the time taken is at least 0.2 second, or the maximum is reached. + Returns ``(number, time_taken)``. + + If *callback* is given and is not None, it will be called after + each trial with two arguments: ``callback(number, time_taken)``. + """ + for i in range(1, 10): + number = 10**i + time_taken = self.timeit(number) + if callback: + callback(number, time_taken) + if time_taken >= 0.2: + break + return (number, time_taken) + def timeit(stmt="pass", setup="pass", timer=default_timer, number=default_number, globals=None): """Convenience function to create Timer object and call timeit method.""" @@ -295,17 +315,16 @@ def main(args=None, *, _wrap_timer=None): t = Timer(stmt, setup, timer) if number == 0: # determine number so that 0.2 <= total time < 2.0 - for i in range(1, 10): - number = 10**i - try: - x = t.timeit(number) - except: - t.print_exc() - return 1 - if verbose: - print("%d loops -> %.*g secs" % (number, precision, x)) - if x >= 0.2: - break + callback = None + if verbose: + def callback(number, time_taken): + msg = "{num} loops -> {secs:.{prec}g} secs" + print(msg.format(num=number, secs=time_taken, prec=precision)) + try: + number, _ = t.autorange(callback) + except: + t.print_exc() + return 1 try: r = t.repeat(repeat, number) except: -- cgit v1.2.1 From 875dbe9f04416103f3afafc4ff85e363be5cfcfd Mon Sep 17 00:00:00 2001 From: Steven D'Aprano Date: Mon, 15 Aug 2016 04:14:33 +1000 Subject: Issue27573 code.interact prints a message when exiting. --- Lib/code.py | 1 + Lib/test/test_code_module.py | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/code.py b/Lib/code.py index 53244e32ad..c8b72042e0 100644 --- a/Lib/code.py +++ b/Lib/code.py @@ -230,6 +230,7 @@ class InteractiveConsole(InteractiveInterpreter): self.write("\nKeyboardInterrupt\n") self.resetbuffer() more = 0 + self.write('now exiting %s...\n' % self.__class__.__name__) def push(self, line): """Push a line to the interpreter. diff --git a/Lib/test/test_code_module.py b/Lib/test/test_code_module.py index 3394b39e01..08ba3f3704 100644 --- a/Lib/test/test_code_module.py +++ b/Lib/test/test_code_module.py @@ -69,7 +69,7 @@ class TestInteractiveConsole(unittest.TestCase): # with banner self.infunc.side_effect = EOFError('Finished') self.console.interact(banner='Foo') - self.assertEqual(len(self.stderr.method_calls), 2) + self.assertEqual(len(self.stderr.method_calls), 3) banner_call = self.stderr.method_calls[0] self.assertEqual(banner_call, ['write', ('Foo\n',), {}]) @@ -77,7 +77,15 @@ class TestInteractiveConsole(unittest.TestCase): self.stderr.reset_mock() self.infunc.side_effect = EOFError('Finished') self.console.interact(banner='') - self.assertEqual(len(self.stderr.method_calls), 1) + self.assertEqual(len(self.stderr.method_calls), 2) + + def test_exit_msg(self): + self.infunc.side_effect = EOFError('Finished') + self.console.interact(banner='') + self.assertEqual(len(self.stderr.method_calls), 2) + err_msg = self.stderr.method_calls[1] + expected = 'now exiting InteractiveConsole...\n' + self.assertEqual(err_msg, ['write', (expected,), {}]) def test_cause_tb(self): self.infunc.side_effect = ["raise ValueError('') from AttributeError", -- cgit v1.2.1 From 471ec83b9274103af494dac719ea8a7eb64ea351 Mon Sep 17 00:00:00 2001 From: Nick Coghlan Date: Mon, 15 Aug 2016 13:11:34 +1000 Subject: Issue #26823: Abbreviate recursive tracebacks Large sections of repeated lines in tracebacks are now abbreviated as "[Previous line repeated {count} more times]" by both the traceback module and the builtin traceback rendering. Patch by Emanuel Barry. --- Lib/test/test_traceback.py | 131 +++++++++++++++++++++++++++++++++++++++++++++ Lib/traceback.py | 23 ++++++++ 2 files changed, 154 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 787409c5fe..665abb462b 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -303,6 +303,137 @@ class TracebackFormatTests(unittest.TestCase): ' traceback.print_stack()', ]) + # issue 26823 - Shrink recursive tracebacks + def _check_recursive_traceback_display(self, render_exc): + # Always show full diffs when this test fails + # Note that rearranging things may require adjusting + # the relative line numbers in the expected tracebacks + self.maxDiff = None + + # Check hitting the recursion limit + def f(): + f() + + with captured_output("stderr") as stderr_f: + try: + f() + except RecursionError as exc: + render_exc() + else: + self.fail("no recursion occurred") + + lineno_f = f.__code__.co_firstlineno + result_f = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {lineno_f+5}, in _check_recursive_traceback_display\n' + ' f()\n' + f' File "{__file__}", line {lineno_f+1}, in f\n' + ' f()\n' + f' File "{__file__}", line {lineno_f+1}, in f\n' + ' f()\n' + f' File "{__file__}", line {lineno_f+1}, in f\n' + ' f()\n' + # XXX: The following line changes depending on whether the tests + # are run through the interactive interpreter or with -m + # It also varies depending on the platform (stack size) + # Fortunately, we don't care about exactness here, so we use regex + r' \[Previous line repeated (\d+) more times\]' '\n' + 'RecursionError: maximum recursion depth exceeded\n' + ) + + expected = result_f.splitlines() + actual = stderr_f.getvalue().splitlines() + + # Check the output text matches expectations + # 2nd last line contains the repetition count + self.assertEqual(actual[:-2], expected[:-2]) + self.assertRegex(actual[-2], expected[-2]) + self.assertEqual(actual[-1], expected[-1]) + + # Check the recursion count is roughly as expected + rec_limit = sys.getrecursionlimit() + self.assertIn(int(re.search(r"\d+", actual[-2]).group()), range(rec_limit-50, rec_limit)) + + # Check a known (limited) number of recursive invocations + def g(count=10): + if count: + return g(count-1) + raise ValueError + + with captured_output("stderr") as stderr_g: + try: + g() + except ValueError as exc: + render_exc() + else: + self.fail("no value error was raised") + + lineno_g = g.__code__.co_firstlineno + result_g = ( + f' File "{__file__}", line {lineno_g+2}, in g\n' + ' return g(count-1)\n' + f' File "{__file__}", line {lineno_g+2}, in g\n' + ' return g(count-1)\n' + f' File "{__file__}", line {lineno_g+2}, in g\n' + ' return g(count-1)\n' + ' [Previous line repeated 6 more times]\n' + f' File "{__file__}", line {lineno_g+3}, in g\n' + ' raise ValueError\n' + 'ValueError\n' + ) + tb_line = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {lineno_g+7}, in _check_recursive_traceback_display\n' + ' g()\n' + ) + expected = (tb_line + result_g).splitlines() + actual = stderr_g.getvalue().splitlines() + self.assertEqual(actual, expected) + + # Check 2 different repetitive sections + def h(count=10): + if count: + return h(count-1) + g() + + with captured_output("stderr") as stderr_h: + try: + h() + except ValueError as exc: + render_exc() + else: + self.fail("no value error was raised") + + lineno_h = h.__code__.co_firstlineno + result_h = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {lineno_h+7}, in _check_recursive_traceback_display\n' + ' h()\n' + f' File "{__file__}", line {lineno_h+2}, in h\n' + ' return h(count-1)\n' + f' File "{__file__}", line {lineno_h+2}, in h\n' + ' return h(count-1)\n' + f' File "{__file__}", line {lineno_h+2}, in h\n' + ' return h(count-1)\n' + ' [Previous line repeated 6 more times]\n' + f' File "{__file__}", line {lineno_h+3}, in h\n' + ' g()\n' + ) + expected = (result_h + result_g).splitlines() + actual = stderr_h.getvalue().splitlines() + self.assertEqual(actual, expected) + + def test_recursive_traceback_python(self): + self._check_recursive_traceback_display(traceback.print_exc) + + @cpython_only + def test_recursive_traceback_cpython_internal(self): + from _testcapi import exception_print + def render_exc(): + exc_type, exc_value, exc_tb = sys.exc_info() + exception_print(exc_value) + self._check_recursive_traceback_display(render_exc) + def test_format_stack(self): def fmt(): return traceback.format_stack() diff --git a/Lib/traceback.py b/Lib/traceback.py index 3b46c0b050..a1cb5fb1ef 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -385,9 +385,30 @@ class StackSummary(list): resulting list corresponds to a single frame from the stack. Each string ends in a newline; the strings may contain internal newlines as well, for those items with source text lines. + + For long sequences of the same frame and line, the first few + repetitions are shown, followed by a summary line stating the exact + number of further repetitions. """ result = [] + last_file = None + last_line = None + last_name = None + count = 0 for frame in self: + if (last_file is not None and last_file == frame.filename and + last_line is not None and last_line == frame.lineno and + last_name is not None and last_name == frame.name): + count += 1 + else: + if count > 3: + result.append(f' [Previous line repeated {count-3} more times]\n') + last_file = frame.filename + last_line = frame.lineno + last_name = frame.name + count = 0 + if count >= 3: + continue row = [] row.append(' File "{}", line {}, in {}\n'.format( frame.filename, frame.lineno, frame.name)) @@ -397,6 +418,8 @@ class StackSummary(list): for name, value in sorted(frame.locals.items()): row.append(' {name} = {value}\n'.format(name=name, value=value)) result.append(''.join(row)) + if count > 3: + result.append(f' [Previous line repeated {count-3} more times]\n') return result -- cgit v1.2.1 From ce08dc2936ca4cb2f7f65f8122055390211bd797 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 15 Aug 2016 10:06:16 +0300 Subject: Issue #16764: Support keyword arguments to zlib.decompress(). Patch by Xiang Zhang. --- Lib/test/test_zlib.py | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_zlib.py b/Lib/test/test_zlib.py index f9740f16a6..4d72d6fdd0 100644 --- a/Lib/test/test_zlib.py +++ b/Lib/test/test_zlib.py @@ -169,6 +169,14 @@ class CompressTestCase(BaseCompressTestCase, unittest.TestCase): self.assertEqual(zlib.decompress(x), HAMLET_SCENE) with self.assertRaises(TypeError): zlib.compress(data=HAMLET_SCENE, level=3) + self.assertEqual(zlib.decompress(x, + wbits=zlib.MAX_WBITS, + bufsize=zlib.DEF_BUF_SIZE), + HAMLET_SCENE) + with self.assertRaises(TypeError): + zlib.decompress(data=x, + wbits=zlib.MAX_WBITS, + bufsize=zlib.DEF_BUF_SIZE) def test_speech128(self): # compress more data @@ -240,6 +248,27 @@ class CompressObjectTestCase(BaseCompressTestCase, unittest.TestCase): self.assertIsInstance(dco.unconsumed_tail, bytes) self.assertIsInstance(dco.unused_data, bytes) + def test_keywords(self): + level = 2 + method = zlib.DEFLATED + wbits = -12 + memLevel = 9 + strategy = zlib.Z_FILTERED + co = zlib.compressobj(level=level, + method=method, + wbits=wbits, + memLevel=memLevel, + strategy=strategy, + zdict=b"") + do = zlib.decompressobj(wbits=wbits, zdict=b"") + with self.assertRaises(TypeError): + co.compress(data=HAMLET_SCENE) + with self.assertRaises(TypeError): + do.decompress(data=zlib.compress(HAMLET_SCENE)) + x = co.compress(HAMLET_SCENE) + co.flush() + y = do.decompress(x, max_length=len(HAMLET_SCENE)) + do.flush() + self.assertEqual(HAMLET_SCENE, y) + def test_compressoptions(self): # specify lots of options to compressobj() level = 2 @@ -255,10 +284,6 @@ class CompressObjectTestCase(BaseCompressTestCase, unittest.TestCase): y2 = dco.flush() self.assertEqual(HAMLET_SCENE, y1 + y2) - # keyword arguments should also be supported - zlib.compressobj(level=level, method=method, wbits=wbits, - memLevel=memLevel, strategy=strategy, zdict=b"") - def test_compressincremental(self): # compress object in steps, decompress object as one-shot data = HAMLET_SCENE * 128 -- cgit v1.2.1 From 2cc8235deb4eb24b5fbbc9446a5eb291fb89ee73 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 15 Aug 2016 09:12:52 -0700 Subject: Issue #12345: Add mathemathcal constant tau to math and cmath. Patch by Lisa Roach. See also PEP 628. --- Lib/test/test_math.py | 1 + 1 file changed, 1 insertion(+) (limited to 'Lib') diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 605adb527a..48e8007bc7 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -196,6 +196,7 @@ class MathTests(unittest.TestCase): def testConstants(self): self.ftest('pi', math.pi, 3.1415926) self.ftest('e', math.e, 2.7182818) + self.assertEqual(math.tau, 2*math.pi) def testAcos(self): self.assertRaises(TypeError, math.acos) -- cgit v1.2.1 From 2103a25f969bb5efd6e69ca2e214f3d5945c43ef Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Mon, 15 Aug 2016 16:12:59 -0400 Subject: Update pydoc topics for 3.6.0a4 --- Lib/pydoc_data/topics.py | 132 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 105 insertions(+), 27 deletions(-) (limited to 'Lib') diff --git a/Lib/pydoc_data/topics.py b/Lib/pydoc_data/topics.py index 7378dc9eb8..590f6135cf 100644 --- a/Lib/pydoc_data/topics.py +++ b/Lib/pydoc_data/topics.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Autogenerated by Sphinx on Mon Jul 11 15:30:24 2016 +# Autogenerated by Sphinx on Mon Aug 15 16:11:20 2016 topics = {'assert': '\n' 'The "assert" statement\n' '**********************\n' @@ -569,6 +569,14 @@ topics = {'assert': '\n' '*instance* of the\n' ' owner class.\n' '\n' + 'object.__set_name__(self, owner, name)\n' + '\n' + ' Called at the time the owning class *owner* is ' + 'created. The\n' + ' descriptor has been assigned to *name*.\n' + '\n' + ' New in version 3.6.\n' + '\n' 'The attribute "__objclass__" is interpreted by the ' '"inspect" module as\n' 'specifying the class where this object was defined ' @@ -1338,13 +1346,12 @@ topics = {'assert': '\n' '\n' 'A class definition is an executable statement. The inheritance ' 'list\n' - 'usually gives a list of base classes (see Customizing class ' - 'creation\n' - 'for more advanced uses), so each item in the list should evaluate ' - 'to a\n' - 'class object which allows subclassing. Classes without an ' - 'inheritance\n' - 'list inherit, by default, from the base class "object"; hence,\n' + 'usually gives a list of base classes (see Metaclasses for more\n' + 'advanced uses), so each item in the list should evaluate to a ' + 'class\n' + 'object which allows subclassing. Classes without an inheritance ' + 'list\n' + 'inherit, by default, from the base class "object"; hence,\n' '\n' ' class Foo:\n' ' pass\n' @@ -1377,16 +1384,14 @@ topics = {'assert': '\n' ' @f2\n' ' class Foo: pass\n' '\n' - 'is equivalent to\n' + 'is roughly equivalent to\n' '\n' ' class Foo: pass\n' ' Foo = f1(arg)(f2(Foo))\n' '\n' 'The evaluation rules for the decorator expressions are the same as ' 'for\n' - 'function decorators. The result must be a class object, which is ' - 'then\n' - 'bound to the class name.\n' + 'function decorators. The result is then bound to the class name.\n' '\n' "**Programmer's note:** Variables defined in the class definition " 'are\n' @@ -2312,11 +2317,15 @@ topics = {'assert': '\n' ' @f2\n' ' def func(): pass\n' '\n' - 'is equivalent to\n' + 'is roughly equivalent to\n' '\n' ' def func(): pass\n' ' func = f1(arg)(f2(func))\n' '\n' + 'except that the original function is not temporarily bound to ' + 'the name\n' + '"func".\n' + '\n' 'When one or more *parameters* have the form *parameter* "="\n' '*expression*, the function is said to have "default parameter ' 'values."\n' @@ -2440,13 +2449,12 @@ topics = {'assert': '\n' '\n' 'A class definition is an executable statement. The inheritance ' 'list\n' - 'usually gives a list of base classes (see Customizing class ' - 'creation\n' - 'for more advanced uses), so each item in the list should ' - 'evaluate to a\n' - 'class object which allows subclassing. Classes without an ' - 'inheritance\n' - 'list inherit, by default, from the base class "object"; hence,\n' + 'usually gives a list of base classes (see Metaclasses for more\n' + 'advanced uses), so each item in the list should evaluate to a ' + 'class\n' + 'object which allows subclassing. Classes without an inheritance ' + 'list\n' + 'inherit, by default, from the base class "object"; hence,\n' '\n' ' class Foo:\n' ' pass\n' @@ -2482,16 +2490,15 @@ topics = {'assert': '\n' ' @f2\n' ' class Foo: pass\n' '\n' - 'is equivalent to\n' + 'is roughly equivalent to\n' '\n' ' class Foo: pass\n' ' Foo = f1(arg)(f2(Foo))\n' '\n' 'The evaluation rules for the decorator expressions are the same ' 'as for\n' - 'function decorators. The result must be a class object, which ' - 'is then\n' - 'bound to the class name.\n' + 'function decorators. The result is then bound to the class ' + 'name.\n' '\n' "**Programmer's note:** Variables defined in the class definition " 'are\n' @@ -3776,7 +3783,7 @@ topics = {'assert': '\n' '\n' 'interact\n' '\n' - ' Start an interative interpreter (using the "code" module) ' + ' Start an interactive interpreter (using the "code" module) ' 'whose\n' ' global namespace contains all the (global and local) names ' 'found in\n' @@ -5296,11 +5303,15 @@ topics = {'assert': '\n' ' @f2\n' ' def func(): pass\n' '\n' - 'is equivalent to\n' + 'is roughly equivalent to\n' '\n' ' def func(): pass\n' ' func = f1(arg)(f2(func))\n' '\n' + 'except that the original function is not temporarily bound to ' + 'the name\n' + '"func".\n' + '\n' 'When one or more *parameters* have the form *parameter* "="\n' '*expression*, the function is said to have "default parameter ' 'values."\n' @@ -6032,7 +6043,7 @@ topics = {'assert': '\n' 'expression"\n' 'yields a function object. The unnamed object behaves like a ' 'function\n' - 'object defined with\n' + 'object defined with:\n' '\n' ' def (arguments):\n' ' return expression\n' @@ -7964,6 +7975,14 @@ topics = {'assert': '\n' 'of the\n' ' owner class.\n' '\n' + 'object.__set_name__(self, owner, name)\n' + '\n' + ' Called at the time the owning class *owner* is created. ' + 'The\n' + ' descriptor has been assigned to *name*.\n' + '\n' + ' New in version 3.6.\n' + '\n' 'The attribute "__objclass__" is interpreted by the "inspect" ' 'module as\n' 'specifying the class where this object was defined (setting ' @@ -8188,6 +8207,65 @@ topics = {'assert': '\n' 'Customizing class creation\n' '==========================\n' '\n' + 'Whenever a class inherits from another class, ' + '*__init_subclass__* is\n' + 'called on that class. This way, it is possible to write ' + 'classes which\n' + 'change the behavior of subclasses. This is closely related ' + 'to class\n' + 'decorators, but where class decorators only affect the ' + 'specific class\n' + 'they\'re applied to, "__init_subclass__" solely applies to ' + 'future\n' + 'subclasses of the class defining the method.\n' + '\n' + 'classmethod object.__init_subclass__(cls)\n' + '\n' + ' This method is called whenever the containing class is ' + 'subclassed.\n' + ' *cls* is then the new subclass. If defined as a normal ' + 'instance\n' + ' method, this method is implicitly converted to a class ' + 'method.\n' + '\n' + ' Keyword arguments which are given to a new class are ' + 'passed to the\n' + ' parent\'s class "__init_subclass__". For compatibility ' + 'with other\n' + ' classes using "__init_subclass__", one should take out ' + 'the needed\n' + ' keyword arguments and pass the others over to the base ' + 'class, as\n' + ' in:\n' + '\n' + ' class Philosopher:\n' + ' def __init_subclass__(cls, default_name, ' + '**kwargs):\n' + ' super().__init_subclass__(**kwargs)\n' + ' cls.default_name = default_name\n' + '\n' + ' class AustralianPhilosopher(Philosopher, ' + 'default_name="Bruce"):\n' + ' pass\n' + '\n' + ' The default implementation "object.__init_subclass__" ' + 'does nothing,\n' + ' but raises an error if it is called with any arguments.\n' + '\n' + ' Note: The metaclass hint "metaclass" is consumed by the ' + 'rest of\n' + ' the type machinery, and is never passed to ' + '"__init_subclass__"\n' + ' implementations. The actual metaclass (rather than the ' + 'explicit\n' + ' hint) can be accessed as "type(cls)".\n' + '\n' + ' New in version 3.6.\n' + '\n' + '\n' + 'Metaclasses\n' + '-----------\n' + '\n' 'By default, classes are constructed using "type()". The ' 'class body is\n' 'executed in a new namespace and the class name is bound ' -- cgit v1.2.1 From bd69a5ff781d8c4d7e8db66590a249bec02ba95c Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Tue, 16 Aug 2016 00:10:14 -0400 Subject: Issue #27611, #24137: Only change tkinter when easily restored. --- Lib/idlelib/pyshell.py | 6 ++++-- Lib/test/test_idle.py | 18 +++++++++--------- 2 files changed, 13 insertions(+), 11 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py index 28584acfbb..740c72e2a3 100755 --- a/Lib/idlelib/pyshell.py +++ b/Lib/idlelib/pyshell.py @@ -30,6 +30,7 @@ import linecache from code import InteractiveInterpreter from platform import python_version, system +from idlelib import testing from idlelib.editor import EditorWindow, fixwordbreaks from idlelib.filelist import FileList from idlelib.colorizer import ColorDelegator @@ -1448,8 +1449,9 @@ def main(): enable_edit = enable_edit or edit_start enable_shell = enable_shell or not enable_edit - # Setup root. - if use_subprocess: # Don't break user code run in IDLE process + # Setup root. Don't break user code run in IDLE process. + # Don't change environment when testing. + if use_subprocess and not testing: NoDefaultRoot() root = Tk(className="Idle") root.withdraw() diff --git a/Lib/test/test_idle.py b/Lib/test/test_idle.py index b266fcfbdc..da05da50f3 100644 --- a/Lib/test/test_idle.py +++ b/Lib/test/test_idle.py @@ -1,23 +1,23 @@ import unittest from test.support import import_module -# Skip test if _thread or _tkinter wasn't built, or idlelib is missing, -# or if tcl/tk version before 8.5, which is needed for ttk widgets. - +# Skip test if _thread or _tkinter wasn't built, if idlelib is missing, +# or if tcl/tk is not the 8.5+ needed for ttk widgets. import_module('threading') # imported by PyShell, imports _thread tk = import_module('tkinter') # imports _tkinter if tk.TkVersion < 8.5: raise unittest.SkipTest("IDLE requires tk 8.5 or later.") -tk.NoDefaultRoot() idlelib = import_module('idlelib') -idlelib.testing = True # Avoid locale-changed test error -# Without test_main present, test.libregrtest.runtest.runtest_inner -# calls (line 173) unittest.TestLoader().loadTestsFromModule(module) -# which calls load_tests() if it finds it. (Unittest.main does the same.) +# Before test imports, tell IDLE to avoid changing the environment. +idlelib.testing = True + +# unittest.main and test.libregrtest.runtest.runtest_inner +# call load_tests, when present, to discover tests to run. from idlelib.idle_test import load_tests if __name__ == '__main__': - unittest.main(verbosity=2, exit=False) + tk.NoDefaultRoot() + unittest.main(exit=False) tk._support_default_root = 1 tk._default_root = None -- cgit v1.2.1 From 1ccf0773ccba98b6c84fd902ab89150fe9929f0d Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 16 Aug 2016 10:55:43 -0700 Subject: Issue #25628: Make namedtuple "rename" and "verbose" parameters keyword-only. --- Lib/collections/__init__.py | 2 +- Lib/test/test_collections.py | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py index b9419506e9..f465e74770 100644 --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -353,7 +353,7 @@ _field_template = '''\ {name} = _property(_itemgetter({index:d}), doc='Alias for field number {index:d}') ''' -def namedtuple(typename, field_names, verbose=False, rename=False): +def namedtuple(typename, field_names, *, verbose=False, rename=False): """Returns a new subclass of tuple with named fields. >>> Point = namedtuple('Point', ['x', 'y']) diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index a80c49c278..c4c0a169fe 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -412,6 +412,18 @@ class TestNamedTuple(unittest.TestCase): self.assertEqual(NTColor._fields, ('red', 'green', 'blue')) globals().pop('NTColor', None) # clean-up after this test + def test_keyword_only_arguments(self): + # See issue 25628 + with support.captured_stdout() as template: + NT = namedtuple('NT', ['x', 'y'], verbose=True) + self.assertIn('class NT', NT._source) + with self.assertRaises(TypeError): + NT = namedtuple('NT', ['x', 'y'], True) + + NT = namedtuple('NT', ['abc', 'def'], rename=True) + self.assertEqual(NT._fields, ('abc', '_1')) + with self.assertRaises(TypeError): + NT = namedtuple('NT', ['abc', 'def'], False, True) def test_namedtuple_subclass_issue_24931(self): class Point(namedtuple('_Point', ['x', 'y'])): -- cgit v1.2.1 From 122f66b9b018f368adb68f876d1d1c4fe65c91e5 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 16 Aug 2016 22:22:21 +0200 Subject: Issue #27181: Skip tests known to fail until a fix is found --- Lib/test/test_statistics.py | 1 + 1 file changed, 1 insertion(+) (limited to 'Lib') diff --git a/Lib/test/test_statistics.py b/Lib/test/test_statistics.py index 8b0c01fd85..dff0cd4476 100644 --- a/Lib/test/test_statistics.py +++ b/Lib/test/test_statistics.py @@ -1053,6 +1053,7 @@ class Test_Product(NumericTestCase): self.assertRaises(TypeError, statistics._product, [Decimal(1), 1.0]) +@unittest.skipIf(True, "FIXME: tests known to fail, see issue #27181") class Test_Nth_Root(NumericTestCase): """Test the functionality of the private _nth_root function.""" -- cgit v1.2.1 From 4452d21513ac037f8398286a69b9b544202193b9 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Wed, 17 Aug 2016 00:46:48 -0700 Subject: Minor readability tweak --- Lib/collections/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py index f465e74770..03ecea27cc 100644 --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -393,7 +393,7 @@ def namedtuple(typename, field_names, *, verbose=False, rename=False): field_names[index] = '_%d' % index seen.add(name) for name in [typename] + field_names: - if type(name) != str: + if type(name) is not str: raise TypeError('Type names and field names must be strings') if not name.isidentifier(): raise ValueError('Type names and field names must be valid ' -- cgit v1.2.1 From fa1579282fa5a0268cd535cb6d79cb59f0866606 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 17 Aug 2016 11:07:21 +0200 Subject: Fix typo in test_time.py --- Lib/test/test_time.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py index f883c45d04..f2242126d1 100644 --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -729,7 +729,7 @@ class CPyTimeTestCase: for seconds in (_testcapi.INT_MIN, _testcapi.INT_MAX): ns_timestamps.append(seconds * SEC_TO_NS) if use_float: - # numbers with an extract representation in IEEE 754 (base 2) + # numbers with an exact representation in IEEE 754 (base 2) for pow2 in (3, 7, 10, 15): ns = 2.0 ** (-pow2) ns_timestamps.extend((-ns, ns)) -- cgit v1.2.1 From e2507f5ffdeb4fc680e7f70e9e1028d3c4c79b2a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 17 Aug 2016 11:25:43 +0200 Subject: regrtest: rename --slow option to --slowest Thanks to optparse, --slow syntax still works ;-) --- Lib/test/libregrtest/cmdline.py | 2 +- Lib/test/test_regrtest.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'Lib') diff --git a/Lib/test/libregrtest/cmdline.py b/Lib/test/libregrtest/cmdline.py index f2ec0bd4d2..1f7aaedae3 100644 --- a/Lib/test/libregrtest/cmdline.py +++ b/Lib/test/libregrtest/cmdline.py @@ -168,7 +168,7 @@ def _create_parser(): help='display test output on failure') group.add_argument('-q', '--quiet', action='store_true', help='no output unless one or more tests fail') - group.add_argument('-o', '--slow', action='store_true', dest='print_slow', + group.add_argument('-o', '--slowest', action='store_true', dest='print_slow', help='print the slowest 10 tests') group.add_argument('--header', action='store_true', help='print header with interpreter info') diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py index 32edff856f..4a96c6f069 100644 --- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -109,7 +109,7 @@ class ParseArgsTestCase(unittest.TestCase): self.assertEqual(ns.verbose, 0) def test_slow(self): - for opt in '-o', '--slow': + for opt in '-o', '--slowest': with self.subTest(opt=opt): ns = libregrtest._parse_args([opt]) self.assertTrue(ns.print_slow) @@ -661,9 +661,9 @@ class ArgsTestCase(BaseTestCase): self.check_executed_tests(output, test, omitted=test) def test_slow(self): - # test --slow + # test --slowest tests = [self.create_test() for index in range(3)] - output = self.run_tests("--slow", *tests) + output = self.run_tests("--slowest", *tests) self.check_executed_tests(output, tests) regex = ('10 slowest tests:\n' '(?:%s: [0-9]+\.[0-9]+s\n){%s}' @@ -671,15 +671,15 @@ class ArgsTestCase(BaseTestCase): self.check_line(output, regex) def test_slow_interrupted(self): - # Issue #25373: test --slow with an interrupted test + # Issue #25373: test --slowest with an interrupted test code = TEST_INTERRUPTED test = self.create_test("sigint", code=code) for multiprocessing in (False, True): if multiprocessing: - args = ("--slow", "-j2", test) + args = ("--slowest", "-j2", test) else: - args = ("--slow", test) + args = ("--slowest", test) output = self.run_tests(*args, exitcode=1) self.check_executed_tests(output, test, omitted=test) regex = ('10 slowest tests:\n') -- cgit v1.2.1 From 95bb93b1a604a35f68a6225f22cfd2891c7fa595 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 17 Aug 2016 12:22:52 +0200 Subject: regrtest: nicer output for durations Use milliseconds and minutes units, not only seconds. --- Lib/test/libregrtest/main.py | 31 +++++++++++++++++++------------ Lib/test/test_regrtest.py | 4 ++-- 2 files changed, 21 insertions(+), 14 deletions(-) (limited to 'Lib') diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index e503c131ac..edf38b4d17 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -34,6 +34,16 @@ else: TEMPDIR = os.path.abspath(TEMPDIR) +def format_duration(seconds): + if seconds < 1.0: + return '%.0f ms' % (seconds * 1e3) + if seconds < 60.0: + return '%.0f sec' % seconds + + minutes, seconds = divmod(seconds, 60.0) + return '%.0f min %.0f sec' % (minutes, seconds) + + class Regrtest: """Execute a test suite. @@ -107,14 +117,6 @@ class Regrtest: self.skipped.append(test) self.resource_denieds.append(test) - def time_delta(self, ceil=False): - seconds = time.monotonic() - self.start_time - if ceil: - seconds = math.ceil(seconds) - else: - seconds = int(seconds) - return datetime.timedelta(seconds=seconds) - def display_progress(self, test_index, test): if self.ns.quiet: return @@ -122,12 +124,14 @@ class Regrtest: fmt = "{time} [{test_index:{count_width}}{test_count}/{nbad}] {test_name}" else: fmt = "{time} [{test_index:{count_width}}{test_count}] {test_name}" + test_time = time.monotonic() - self.start_time + test_time = datetime.timedelta(seconds=int(test_time)) line = fmt.format(count_width=self.test_count_width, test_index=test_index, test_count=self.test_count, nbad=len(self.bad), test_name=test, - time=self.time_delta()) + time=test_time) print(line, flush=True) def parse_args(self, kwargs): @@ -286,9 +290,10 @@ class Regrtest: if self.ns.print_slow: self.test_times.sort(reverse=True) + print() print("10 slowest tests:") for time, test in self.test_times[:10]: - print("%s: %.1fs" % (test, time)) + print("- %s: %s" % (test, format_duration(time))) if self.bad: print(count(len(self.bad), "test"), "failed:") @@ -342,7 +347,7 @@ class Regrtest: previous_test = format_test_result(test, result[0]) test_time = time.monotonic() - start_time if test_time >= PROGRESS_MIN_TIME: - previous_test = "%s in %.0f sec" % (previous_test, test_time) + previous_test = "%s in %s" % (previous_test, format_duration(test_time)) elif result[0] == PASSED: # be quiet: say nothing if the test passed shortly previous_test = None @@ -418,7 +423,9 @@ class Regrtest: r.write_results(show_missing=True, summary=True, coverdir=self.ns.coverdir) - print("Total duration: %s" % self.time_delta(ceil=True)) + print() + duration = time.monotonic() - self.start_time + print("Total duration: %s" % format_duration(duration)) if self.ns.runleaks: os.system("leaks %d" % os.getpid()) diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py index 4a96c6f069..0b6f2ea537 100644 --- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -660,13 +660,13 @@ class ArgsTestCase(BaseTestCase): output = self.run_tests(test, exitcode=1) self.check_executed_tests(output, test, omitted=test) - def test_slow(self): + def test_slowest(self): # test --slowest tests = [self.create_test() for index in range(3)] output = self.run_tests("--slowest", *tests) self.check_executed_tests(output, tests) regex = ('10 slowest tests:\n' - '(?:%s: [0-9]+\.[0-9]+s\n){%s}' + '(?:- %s: .*\n){%s}' % (self.TESTNAME_REGEX, len(tests))) self.check_line(output, regex) -- cgit v1.2.1 From d9154139869289351286a063f67cf831087cc5e4 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 17 Aug 2016 15:42:21 +0200 Subject: regrtest: add newlines in output for readability --- Lib/test/libregrtest/main.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'Lib') diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index edf38b4d17..92ecc5b36f 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -296,15 +296,18 @@ class Regrtest: print("- %s: %s" % (test, format_duration(time))) if self.bad: + print() print(count(len(self.bad), "test"), "failed:") printlist(self.bad) if self.environment_changed: + print() print("{} altered the execution environment:".format( count(len(self.environment_changed), "test"))) printlist(self.environment_changed) if self.skipped and not self.ns.quiet: + print() print(count(len(self.skipped), "test"), "skipped:") printlist(self.skipped) -- cgit v1.2.1 From 6601667e40acf305cffb2db8bc00bcac568ea384 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 17 Aug 2016 16:00:12 +0200 Subject: regrtest: set interrupted to True if re-run is interrupted --- Lib/test/libregrtest/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index 92ecc5b36f..0723c435db 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -254,6 +254,7 @@ class Regrtest: self.ns.verbose = True ok = runtest(self.ns, test) except KeyboardInterrupt: + self.interrupted = True # print a newline separate from the ^C print() break @@ -341,8 +342,8 @@ class Regrtest: try: result = runtest(self.ns, test) except KeyboardInterrupt: - self.accumulate_result(test, (INTERRUPTED, None)) self.interrupted = True + self.accumulate_result(test, (INTERRUPTED, None)) break else: self.accumulate_result(test, result) -- cgit v1.2.1 From efb4dbe976b8c43b6d886dc2f99c85178fcec0a9 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 17 Aug 2016 16:12:16 +0200 Subject: regrtest: add a summary of the summary, "Result: xxx" It's sometimes hard to check quickly if tests succeeded, failed or something bad happened. I added a final "Result: xxx" line which summarizes all outputs into a single line, written at the end (it should always be the last line of the output). --- Lib/test/libregrtest/main.py | 8 ++++++++ Lib/test/test_regrtest.py | 21 +++++++++++++++++---- 2 files changed, 25 insertions(+), 4 deletions(-) (limited to 'Lib') diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index 0723c435db..78c52bd48a 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -431,6 +431,14 @@ class Regrtest: duration = time.monotonic() - self.start_time print("Total duration: %s" % format_duration(duration)) + if self.bad: + result = "FAILURE" + elif self.interrupted: + result = "INTERRUPTED" + else: + result = "SUCCESS" + print("Result: %s" % result) + if self.ns.runleaks: os.system("leaks %d" % os.getpid()) diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py index 0b6f2ea537..40862e6589 100644 --- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -349,7 +349,7 @@ class BaseTestCase(unittest.TestCase): return list(match.group(1) for match in parser) def check_executed_tests(self, output, tests, skipped=(), failed=(), - omitted=(), randomize=False): + omitted=(), randomize=False, interrupted=False): if isinstance(tests, str): tests = [tests] if isinstance(skipped, str): @@ -398,6 +398,17 @@ class BaseTestCase(unittest.TestCase): regex = 'All %s' % regex self.check_line(output, regex) + if interrupted: + self.check_line(output, 'Test suite interrupted by signal SIGINT.') + + if nfailed: + result = 'FAILURE' + elif interrupted: + result = 'INTERRUPTED' + else: + result = 'SUCCESS' + self.check_line(output, 'Result: %s' % result) + def parse_random_seed(self, output): match = self.regex_search(r'Using random seed ([0-9]+)', output) randseed = int(match.group(1)) @@ -658,7 +669,8 @@ class ArgsTestCase(BaseTestCase): code = TEST_INTERRUPTED test = self.create_test('sigint', code=code) output = self.run_tests(test, exitcode=1) - self.check_executed_tests(output, test, omitted=test) + self.check_executed_tests(output, test, omitted=test, + interrupted=True) def test_slowest(self): # test --slowest @@ -681,10 +693,11 @@ class ArgsTestCase(BaseTestCase): else: args = ("--slowest", test) output = self.run_tests(*args, exitcode=1) - self.check_executed_tests(output, test, omitted=test) + self.check_executed_tests(output, test, + omitted=test, interrupted=True) + regex = ('10 slowest tests:\n') self.check_line(output, regex) - self.check_line(output, 'Test suite interrupted by signal SIGINT.') def test_coverage(self): # test --coverage -- cgit v1.2.1 From 569ff74dd4ca3942bf31724602788a5d388e73bb Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Wed, 17 Aug 2016 16:20:07 +0100 Subject: Closes #9998: Allowed find_library to search additional locations for libraries. --- Lib/ctypes/test/test_find.py | 43 +++++++++++++++++++++++++++++++++++++++++++ Lib/ctypes/util.py | 26 +++++++++++++++++++++++++- 2 files changed, 68 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/ctypes/test/test_find.py b/Lib/ctypes/test/test_find.py index 20c5337a8b..c7205f5ddf 100644 --- a/Lib/ctypes/test/test_find.py +++ b/Lib/ctypes/test/test_find.py @@ -69,6 +69,49 @@ class Test_OpenGL_libs(unittest.TestCase): self.assertFalse(os.path.lexists(test.support.TESTFN)) self.assertIsNone(result) + +@unittest.skipUnless(sys.platform.startswith('linux'), + 'Test only valid for Linux') +class LibPathFindTest(unittest.TestCase): + def test_find_on_libpath(self): + import subprocess + import tempfile + + try: + p = subprocess.Popen(['gcc', '--version'], stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL) + out, _ = p.communicate() + except OSError: + raise unittest.SkipTest('gcc, needed for test, not available') + with tempfile.TemporaryDirectory() as d: + # create an empty temporary file + srcname = os.path.join(d, 'dummy.c') + libname = 'py_ctypes_test_dummy' + dstname = os.path.join(d, 'lib%s.so' % libname) + with open(srcname, 'w') as f: + pass + self.assertTrue(os.path.exists(srcname)) + # compile the file to a shared library + cmd = ['gcc', '-o', dstname, '--shared', + '-Wl,-soname,lib%s.so' % libname, srcname] + out = subprocess.check_output(cmd) + self.assertTrue(os.path.exists(dstname)) + # now check that the .so can't be found (since not in + # LD_LIBRARY_PATH) + self.assertIsNone(find_library(libname)) + # now add the location to LD_LIBRARY_PATH + with test.support.EnvironmentVarGuard() as env: + KEY = 'LD_LIBRARY_PATH' + if KEY not in env: + v = d + else: + v = '%s:%s' % (env[KEY], d) + env.set(KEY, v) + # now check that the .so can be found (since in + # LD_LIBRARY_PATH) + self.assertEqual(find_library(libname), 'lib%s.so' % libname) + + # On platforms where the default shared library suffix is '.so', # at least some libraries can be loaded as attributes of the cdll # object, since ctypes now tries loading the lib again diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py index e25a886a05..f5c6b266b6 100644 --- a/Lib/ctypes/util.py +++ b/Lib/ctypes/util.py @@ -285,8 +285,32 @@ elif os.name == "posix": except OSError: pass + def _findLib_ld(name): + # See issue #9998 for why this is needed + expr = r'[^\(\)\s]*lib%s\.[^\(\)\s]*' % re.escape(name) + cmd = ['ld', '-t'] + libpath = os.environ.get('LD_LIBRARY_PATH') + if libpath: + for d in libpath.split(':'): + cmd.extend(['-L', d]) + cmd.extend(['-o', os.devnull, '-l%s' % name]) + result = None + try: + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True) + out, _ = p.communicate() + res = re.search(expr, os.fsdecode(out)) + if res: + result = res.group(0) + except Exception as e: + pass # result will be None + return result + def find_library(name): - return _findSoname_ldconfig(name) or _get_soname(_findLib_gcc(name)) + # See issue #9998 + return _findSoname_ldconfig(name) or \ + _get_soname(_findLib_gcc(name) or _findLib_ld(name)) ################################################################ # test code -- cgit v1.2.1 From e00154721d025a6f1bef5652817255007f23e99e Mon Sep 17 00:00:00 2001 From: Alexander Belopolsky Date: Wed, 17 Aug 2016 19:56:17 -0400 Subject: Issue #24773: Include Tallinn 1999-10-31 transition in tests. Does not appear to be a problem anymore and I cannot figure out why it was skipped in the first place. --- Lib/test/datetimetester.py | 3 --- 1 file changed, 3 deletions(-) (limited to 'Lib') diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 5413bcaa92..a0583072d9 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -4770,9 +4770,6 @@ class ZoneInfoTest(unittest.TestCase): # System support for times around the end of 32-bit time_t # and later is flaky on many systems. break - if self.zonename == 'Europe/Tallinn' and udt.date() == date(1999, 10, 31): - print("Skip %s %s transition" % (self.zonename, udt)) - continue s0 = (udt - datetime(1970, 1, 1)) // SEC ss = shift // SEC # shift seconds for x in [-40 * 3600, -20*3600, -1, 0, -- cgit v1.2.1 From 7f08fb418e539233279bba870844bce3674fb7cf Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 18 Aug 2016 09:14:47 +0300 Subject: Issue #16764: Move NEWS entry to correct section and remove too strict test. --- Lib/test/test_zlib.py | 4 ---- 1 file changed, 4 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_zlib.py b/Lib/test/test_zlib.py index 4d72d6fdd0..20174d8343 100644 --- a/Lib/test/test_zlib.py +++ b/Lib/test/test_zlib.py @@ -173,10 +173,6 @@ class CompressTestCase(BaseCompressTestCase, unittest.TestCase): wbits=zlib.MAX_WBITS, bufsize=zlib.DEF_BUF_SIZE), HAMLET_SCENE) - with self.assertRaises(TypeError): - zlib.decompress(data=x, - wbits=zlib.MAX_WBITS, - bufsize=zlib.DEF_BUF_SIZE) def test_speech128(self): # compress more data -- cgit v1.2.1 From 00e6e6f7a778b035d8d138c6955dbbede87c155f Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 18 Aug 2016 09:22:23 -0700 Subject: Anti-registration of various ABC methods. - Issue #25958: Support "anti-registration" of special methods from various ABCs, like __hash__, __iter__ or __len__. All these (and several more) can be set to None in an implementation class and the behavior will be as if the method is not defined at all. (Previously, this mechanism existed only for __hash__, to make mutable classes unhashable.) Code contributed by Andrew Barnert and Ivan Levkivskyi. --- Lib/_collections_abc.py | 78 ++++++++++++++++---------------------------- Lib/test/test_augassign.py | 8 +++++ Lib/test/test_binop.py | 50 +++++++++++++++++++++++++++- Lib/test/test_bool.py | 11 +++++++ Lib/test/test_bytes.py | 30 +++++++++++++++++ Lib/test/test_collections.py | 60 ++++++++++++++++++++++++++++++---- Lib/test/test_contains.py | 25 ++++++++++++++ Lib/test/test_enumerate.py | 9 ++++- Lib/test/test_iter.py | 12 +++++++ Lib/test/test_unicode.py | 23 +++++++++++++ 10 files changed, 248 insertions(+), 58 deletions(-) (limited to 'Lib') diff --git a/Lib/_collections_abc.py b/Lib/_collections_abc.py index ffee38594f..077bde4d21 100644 --- a/Lib/_collections_abc.py +++ b/Lib/_collections_abc.py @@ -62,6 +62,18 @@ del _coro ### ONE-TRICK PONIES ### +def _check_methods(C, *methods): + mro = C.__mro__ + for method in methods: + for B in mro: + if method in B.__dict__: + if B.__dict__[method] is None: + return NotImplemented + break + else: + return NotImplemented + return True + class Hashable(metaclass=ABCMeta): __slots__ = () @@ -73,11 +85,7 @@ class Hashable(metaclass=ABCMeta): @classmethod def __subclasshook__(cls, C): if cls is Hashable: - for B in C.__mro__: - if "__hash__" in B.__dict__: - if B.__dict__["__hash__"]: - return True - break + return _check_methods(C, "__hash__") return NotImplemented @@ -92,11 +100,7 @@ class Awaitable(metaclass=ABCMeta): @classmethod def __subclasshook__(cls, C): if cls is Awaitable: - for B in C.__mro__: - if "__await__" in B.__dict__: - if B.__dict__["__await__"]: - return True - break + return _check_methods(C, "__await__") return NotImplemented @@ -137,14 +141,7 @@ class Coroutine(Awaitable): @classmethod def __subclasshook__(cls, C): if cls is Coroutine: - mro = C.__mro__ - for method in ('__await__', 'send', 'throw', 'close'): - for base in mro: - if method in base.__dict__: - break - else: - return NotImplemented - return True + return _check_methods(C, '__await__', 'send', 'throw', 'close') return NotImplemented @@ -162,8 +159,7 @@ class AsyncIterable(metaclass=ABCMeta): @classmethod def __subclasshook__(cls, C): if cls is AsyncIterable: - if any("__aiter__" in B.__dict__ for B in C.__mro__): - return True + return _check_methods(C, "__aiter__") return NotImplemented @@ -182,9 +178,7 @@ class AsyncIterator(AsyncIterable): @classmethod def __subclasshook__(cls, C): if cls is AsyncIterator: - if (any("__anext__" in B.__dict__ for B in C.__mro__) and - any("__aiter__" in B.__dict__ for B in C.__mro__)): - return True + return _check_methods(C, "__anext__", "__aiter__") return NotImplemented @@ -200,8 +194,7 @@ class Iterable(metaclass=ABCMeta): @classmethod def __subclasshook__(cls, C): if cls is Iterable: - if any("__iter__" in B.__dict__ for B in C.__mro__): - return True + return _check_methods(C, "__iter__") return NotImplemented @@ -220,9 +213,7 @@ class Iterator(Iterable): @classmethod def __subclasshook__(cls, C): if cls is Iterator: - if (any("__next__" in B.__dict__ for B in C.__mro__) and - any("__iter__" in B.__dict__ for B in C.__mro__)): - return True + return _check_methods(C, '__iter__', '__next__') return NotImplemented Iterator.register(bytes_iterator) @@ -246,16 +237,13 @@ class Reversible(Iterable): @abstractmethod def __reversed__(self): - return NotImplemented + while False: + yield None @classmethod def __subclasshook__(cls, C): if cls is Reversible: - for B in C.__mro__: - if "__reversed__" in B.__dict__: - if B.__dict__["__reversed__"] is not None: - return True - break + return _check_methods(C, "__reversed__", "__iter__") return NotImplemented @@ -302,17 +290,10 @@ class Generator(Iterator): @classmethod def __subclasshook__(cls, C): if cls is Generator: - mro = C.__mro__ - for method in ('__iter__', '__next__', 'send', 'throw', 'close'): - for base in mro: - if method in base.__dict__: - break - else: - return NotImplemented - return True + return _check_methods(C, '__iter__', '__next__', + 'send', 'throw', 'close') return NotImplemented - Generator.register(generator) @@ -327,8 +308,7 @@ class Sized(metaclass=ABCMeta): @classmethod def __subclasshook__(cls, C): if cls is Sized: - if any("__len__" in B.__dict__ for B in C.__mro__): - return True + return _check_methods(C, "__len__") return NotImplemented @@ -343,8 +323,7 @@ class Container(metaclass=ABCMeta): @classmethod def __subclasshook__(cls, C): if cls is Container: - if any("__contains__" in B.__dict__ for B in C.__mro__): - return True + return _check_methods(C, "__contains__") return NotImplemented @@ -359,8 +338,7 @@ class Callable(metaclass=ABCMeta): @classmethod def __subclasshook__(cls, C): if cls is Callable: - if any("__call__" in B.__dict__ for B in C.__mro__): - return True + return _check_methods(C, "__call__") return NotImplemented @@ -640,6 +618,8 @@ class Mapping(Sized, Iterable, Container): return NotImplemented return dict(self.items()) == dict(other.items()) + __reversed__ = None + Mapping.register(mappingproxy) diff --git a/Lib/test/test_augassign.py b/Lib/test/test_augassign.py index 5093e9d0f3..5930d9e7a2 100644 --- a/Lib/test/test_augassign.py +++ b/Lib/test/test_augassign.py @@ -83,6 +83,10 @@ class AugAssignTest(unittest.TestCase): def __iadd__(self, val): return aug_test3(self.val + val) + class aug_test4(aug_test3): + """Blocks inheritance, and fallback to __add__""" + __iadd__ = None + x = aug_test(1) y = x x += 10 @@ -106,6 +110,10 @@ class AugAssignTest(unittest.TestCase): self.assertTrue(y is not x) self.assertEqual(x.val, 13) + x = aug_test4(4) + with self.assertRaises(TypeError): + x += 10 + def testCustomMethods2(test_self): output = [] diff --git a/Lib/test/test_binop.py b/Lib/test/test_binop.py index fc8d30fac1..3ed018e089 100644 --- a/Lib/test/test_binop.py +++ b/Lib/test/test_binop.py @@ -2,7 +2,7 @@ import unittest from test import support -from operator import eq, le +from operator import eq, le, ne from abc import ABCMeta def gcd(a, b): @@ -388,6 +388,54 @@ class OperationOrderTests(unittest.TestCase): self.assertEqual(op_sequence(eq, B, V), ['B.__eq__', 'V.__eq__']) self.assertEqual(op_sequence(le, B, V), ['B.__le__', 'V.__ge__']) +class SupEq(object): + """Class that can test equality""" + def __eq__(self, other): + return True + +class S(SupEq): + """Subclass of SupEq that should fail""" + __eq__ = None + +class F(object): + """Independent class that should fall back""" + +class X(object): + """Independent class that should fail""" + __eq__ = None + +class SN(SupEq): + """Subclass of SupEq that can test equality, but not non-equality""" + __ne__ = None + +class XN: + """Independent class that can test equality, but not non-equality""" + def __eq__(self, other): + return True + __ne__ = None + +class FallbackBlockingTests(unittest.TestCase): + """Unit tests for None method blocking""" + + def test_fallback_rmethod_blocking(self): + e, f, s, x = SupEq(), F(), S(), X() + self.assertEqual(e, e) + self.assertEqual(e, f) + self.assertEqual(f, e) + # left operand is checked first + self.assertEqual(e, x) + self.assertRaises(TypeError, eq, x, e) + # S is a subclass, so it's always checked first + self.assertRaises(TypeError, eq, e, s) + self.assertRaises(TypeError, eq, s, e) + + def test_fallback_ne_blocking(self): + e, sn, xn = SupEq(), SN(), XN() + self.assertFalse(e != e) + self.assertRaises(TypeError, ne, e, sn) + self.assertRaises(TypeError, ne, sn, e) + self.assertFalse(e != xn) + self.assertRaises(TypeError, ne, xn, e) if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_bool.py b/Lib/test/test_bool.py index d30a3b9c0f..5f7e842da2 100644 --- a/Lib/test/test_bool.py +++ b/Lib/test/test_bool.py @@ -333,6 +333,17 @@ class BoolTest(unittest.TestCase): except (Exception) as e_len: self.assertEqual(str(e_bool), str(e_len)) + def test_blocked(self): + class A: + __bool__ = None + self.assertRaises(TypeError, bool, A()) + + class B: + def __len__(self): + return 10 + __bool__ = None + self.assertRaises(TypeError, bool, B()) + def test_real_and_imag(self): self.assertEqual(True.real, 1) self.assertEqual(True.imag, 0) diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index 129b4abf33..64644e7ffb 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -843,6 +843,36 @@ class BytesTest(BaseBytesTest, unittest.TestCase): self.assertRaises(OverflowError, PyBytes_FromFormat, b'%c', c_int(256)) + def test_bytes_blocking(self): + class IterationBlocked(list): + __bytes__ = None + i = [0, 1, 2, 3] + self.assertEqual(bytes(i), b'\x00\x01\x02\x03') + self.assertRaises(TypeError, bytes, IterationBlocked(i)) + + # At least in CPython, because bytes.__new__ and the C API + # PyBytes_FromObject have different fallback rules, integer + # fallback is handled specially, so test separately. + class IntBlocked(int): + __bytes__ = None + self.assertEqual(bytes(3), b'\0\0\0') + self.assertRaises(TypeError, bytes, IntBlocked(3)) + + # While there is no separately-defined rule for handling bytes + # subclasses differently from other buffer-interface classes, + # an implementation may well special-case them (as CPython 2.x + # str did), so test them separately. + class BytesSubclassBlocked(bytes): + __bytes__ = None + self.assertEqual(bytes(b'ab'), b'ab') + self.assertRaises(TypeError, bytes, BytesSubclassBlocked(b'ab')) + + class BufferBlocked(bytearray): + __bytes__ = None + ba, bb = bytearray(b'ab'), BufferBlocked(b'ab') + self.assertEqual(bytes(ba), b'ab') + self.assertRaises(TypeError, bytes, bb) + class ByteArrayTest(BaseBytesTest, unittest.TestCase): type2test = bytearray diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index c4c0a169fe..6e858c0d09 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -499,6 +499,9 @@ class ABCTestCase(unittest.TestCase): self.assertTrue(other.right_side,'Right side not called for %s.%s' % (type(instance), name)) +def _test_gen(): + yield + class TestOneTrickPonyABCs(ABCTestCase): def test_Awaitable(self): @@ -686,7 +689,7 @@ class TestOneTrickPonyABCs(ABCTestCase): samples = [bytes(), str(), tuple(), list(), set(), frozenset(), dict(), dict().keys(), dict().items(), dict().values(), - (lambda: (yield))(), + _test_gen(), (x for x in []), ] for x in samples: @@ -700,6 +703,15 @@ class TestOneTrickPonyABCs(ABCTestCase): self.assertFalse(issubclass(str, I)) self.validate_abstract_methods(Iterable, '__iter__') self.validate_isinstance(Iterable, '__iter__') + # Check None blocking + class It: + def __iter__(self): return iter([]) + class ItBlocked(It): + __iter__ = None + self.assertTrue(issubclass(It, Iterable)) + self.assertTrue(isinstance(It(), Iterable)) + self.assertFalse(issubclass(ItBlocked, Iterable)) + self.assertFalse(isinstance(ItBlocked(), Iterable)) def test_Reversible(self): # Check some non-reversibles @@ -707,8 +719,18 @@ class TestOneTrickPonyABCs(ABCTestCase): for x in non_samples: self.assertNotIsInstance(x, Reversible) self.assertFalse(issubclass(type(x), Reversible), repr(type(x))) - # Check some reversibles - samples = [tuple(), list()] + # Check some non-reversible iterables + non_reversibles = [dict().keys(), dict().items(), dict().values(), + Counter(), Counter().keys(), Counter().items(), + Counter().values(), _test_gen(), + (x for x in []), iter([]), reversed([])] + for x in non_reversibles: + self.assertNotIsInstance(x, Reversible) + self.assertFalse(issubclass(type(x), Reversible), repr(type(x))) + # Check some reversible iterables + samples = [bytes(), str(), tuple(), list(), OrderedDict(), + OrderedDict().keys(), OrderedDict().items(), + OrderedDict().values()] for x in samples: self.assertIsInstance(x, Reversible) self.assertTrue(issubclass(type(x), Reversible), repr(type(x))) @@ -725,6 +747,29 @@ class TestOneTrickPonyABCs(ABCTestCase): self.assertEqual(list(reversed(R())), []) self.assertFalse(issubclass(float, R)) self.validate_abstract_methods(Reversible, '__reversed__', '__iter__') + # Check reversible non-iterable (which is not Reversible) + class RevNoIter: + def __reversed__(self): return reversed([]) + class RevPlusIter(RevNoIter): + def __iter__(self): return iter([]) + self.assertFalse(issubclass(RevNoIter, Reversible)) + self.assertFalse(isinstance(RevNoIter(), Reversible)) + self.assertTrue(issubclass(RevPlusIter, Reversible)) + self.assertTrue(isinstance(RevPlusIter(), Reversible)) + # Check None blocking + class Rev: + def __iter__(self): return iter([]) + def __reversed__(self): return reversed([]) + class RevItBlocked(Rev): + __iter__ = None + class RevRevBlocked(Rev): + __reversed__ = None + self.assertTrue(issubclass(Rev, Reversible)) + self.assertTrue(isinstance(Rev(), Reversible)) + self.assertFalse(issubclass(RevItBlocked, Reversible)) + self.assertFalse(isinstance(RevItBlocked(), Reversible)) + self.assertFalse(issubclass(RevRevBlocked, Reversible)) + self.assertFalse(isinstance(RevRevBlocked(), Reversible)) def test_Iterator(self): non_samples = [None, 42, 3.14, 1j, b"", "", (), [], {}, set()] @@ -736,7 +781,7 @@ class TestOneTrickPonyABCs(ABCTestCase): iter(set()), iter(frozenset()), iter(dict().keys()), iter(dict().items()), iter(dict().values()), - (lambda: (yield))(), + _test_gen(), (x for x in []), ] for x in samples: @@ -824,7 +869,7 @@ class TestOneTrickPonyABCs(ABCTestCase): def test_Sized(self): non_samples = [None, 42, 3.14, 1j, - (lambda: (yield))(), + _test_gen(), (x for x in []), ] for x in non_samples: @@ -842,7 +887,7 @@ class TestOneTrickPonyABCs(ABCTestCase): def test_Container(self): non_samples = [None, 42, 3.14, 1j, - (lambda: (yield))(), + _test_gen(), (x for x in []), ] for x in non_samples: @@ -861,7 +906,7 @@ class TestOneTrickPonyABCs(ABCTestCase): def test_Callable(self): non_samples = [None, 42, 3.14, 1j, "", b"", (), [], {}, set(), - (lambda: (yield))(), + _test_gen(), (x for x in []), ] for x in non_samples: @@ -1276,6 +1321,7 @@ class TestCollectionABCs(ABCTestCase): def __iter__(self): return iter(()) self.validate_comparison(MyMapping()) + self.assertRaises(TypeError, reversed, MyMapping()) def test_MutableMapping(self): for sample in [dict]: diff --git a/Lib/test/test_contains.py b/Lib/test/test_contains.py index 3c6bdeffda..036a1d012d 100644 --- a/Lib/test/test_contains.py +++ b/Lib/test/test_contains.py @@ -84,6 +84,31 @@ class TestContains(unittest.TestCase): self.assertTrue(container == constructor(values)) self.assertTrue(container == container) + def test_block_fallback(self): + # blocking fallback with __contains__ = None + class ByContains(object): + def __contains__(self, other): + return False + c = ByContains() + class BlockContains(ByContains): + """Is not a container + + This class is a perfectly good iterable (as tested by + list(bc)), as well as inheriting from a perfectly good + container, but __contains__ = None prevents the usual + fallback to iteration in the container protocol. That + is, normally, 0 in bc would fall back to the equivalent + of any(x==0 for x in bc), but here it's blocked from + doing so. + """ + def __iter__(self): + while False: + yield None + __contains__ = None + bc = BlockContains() + self.assertFalse(0 in c) + self.assertFalse(0 in list(bc)) + self.assertRaises(TypeError, lambda: 0 in bc) if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_enumerate.py b/Lib/test/test_enumerate.py index 2630cf2d50..e455adee50 100644 --- a/Lib/test/test_enumerate.py +++ b/Lib/test/test_enumerate.py @@ -223,7 +223,7 @@ class TestReversed(unittest.TestCase, PickleTest): def test_objmethods(self): # Objects must have __len__() and __getitem__() implemented. class NoLen(object): - def __getitem__(self): return 1 + def __getitem__(self, i): return 1 nl = NoLen() self.assertRaises(TypeError, reversed, nl) @@ -232,6 +232,13 @@ class TestReversed(unittest.TestCase, PickleTest): ngi = NoGetItem() self.assertRaises(TypeError, reversed, ngi) + class Blocked(object): + def __getitem__(self, i): return 1 + def __len__(self): return 2 + __reversed__ = None + b = Blocked() + self.assertRaises(TypeError, reversed, b) + def test_pickle(self): for data in 'abc', range(5), tuple(enumerate('abc')), range(1,17,5): self.check_pickle(reversed(data), list(data)[::-1]) diff --git a/Lib/test/test_iter.py b/Lib/test/test_iter.py index a91670b4a1..542b28419e 100644 --- a/Lib/test/test_iter.py +++ b/Lib/test/test_iter.py @@ -54,6 +54,14 @@ class UnlimitedSequenceClass: def __getitem__(self, i): return i +class DefaultIterClass: + pass + +class NoIterClass: + def __getitem__(self, i): + return i + __iter__ = None + # Main test suite class TestCase(unittest.TestCase): @@ -995,6 +1003,10 @@ class TestCase(unittest.TestCase): def test_free_after_iterating(self): check_free_after_iterating(self, iter, SequenceClass, (0,)) + def test_error_iter(self): + for typ in (DefaultIterClass, NoIterClass): + self.assertRaises(TypeError, iter, typ()) + def test_main(): run_unittest(TestCase) diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py index a38e7b1610..78f9668e19 100644 --- a/Lib/test/test_unicode.py +++ b/Lib/test/test_unicode.py @@ -986,6 +986,19 @@ class UnicodeTest(string_tests.CommonTest, def __format__(self, format_spec): return int.__format__(self * 2, format_spec) + class M: + def __init__(self, x): + self.x = x + def __repr__(self): + return 'M(' + self.x + ')' + __str__ = None + + class N: + def __init__(self, x): + self.x = x + def __repr__(self): + return 'N(' + self.x + ')' + __format__ = None self.assertEqual(''.format(), '') self.assertEqual('abc'.format(), 'abc') @@ -1200,6 +1213,16 @@ class UnicodeTest(string_tests.CommonTest, self.assertEqual("0x{:0{:d}X}".format(0x0,16), "0x0000000000000000") + # Blocking fallback + m = M('data') + self.assertEqual("{!r}".format(m), 'M(data)') + self.assertRaises(TypeError, "{!s}".format, m) + self.assertRaises(TypeError, "{}".format, m) + n = N('data') + self.assertEqual("{!r}".format(n), 'N(data)') + self.assertEqual("{!s}".format(n), 'N(data)') + self.assertRaises(TypeError, "{}".format, n) + def test_format_map(self): self.assertEqual(''.format_map({}), '') self.assertEqual('a'.format_map({}), 'a') -- cgit v1.2.1 From cd5483c03b713df469c2228af9122cc2f2396451 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Thu, 18 Aug 2016 21:23:48 +0100 Subject: Closes #12713: Allowed abbreviation of subcommands in argparse. --- Lib/argparse.py | 23 +++++++++--- Lib/test/test_argparse.py | 92 +++++++++++++++++++++++++++++++++-------------- 2 files changed, 83 insertions(+), 32 deletions(-) (limited to 'Lib') diff --git a/Lib/argparse.py b/Lib/argparse.py index 209b4e99c1..e0edad8e42 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -1110,6 +1110,12 @@ class _SubParsersAction(Action): parser_name = values[0] arg_strings = values[1:] + # get full parser_name from (optional) abbreviated one + for p in self._name_parser_map: + if p.startswith(parser_name): + parser_name = p + break + # set the parser name if requested if self.dest is not SUPPRESS: setattr(namespace, self.dest, parser_name) @@ -2307,11 +2313,18 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): def _check_value(self, action, value): # converted value must be one of the choices (if specified) - if action.choices is not None and value not in action.choices: - args = {'value': value, - 'choices': ', '.join(map(repr, action.choices))} - msg = _('invalid choice: %(value)r (choose from %(choices)s)') - raise ArgumentError(action, msg % args) + if action.choices is not None: + ac = [ax for ax in action.choices if str(ax).startswith(str(value))] + if len(ac) == 0: + args = {'value': value, + 'choices': ', '.join(map(repr, action.choices))} + msg = _('invalid choice: %(value)r (choose from %(choices)s)') + raise ArgumentError(action, msg % args) + elif len(ac) > 1: + args = {'value': value, + 'choices': ', '.join(ac)} + msg = _('ambiguous choice: %(value)r could match %(choices)s') + raise ArgumentError(action, msg % args) # ======================= # Help-formatting methods diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index 52c624771c..32d1b0cbb9 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -1842,6 +1842,22 @@ class TestAddSubparsers(TestCase): parser3.add_argument('t', type=int, help='t help') parser3.add_argument('u', nargs='...', help='u help') + # add fourth sub-parser (to test abbreviations) + parser4_kwargs = dict(description='lost description') + if subparser_help: + parser4_kwargs['help'] = 'lost help' + parser4 = subparsers.add_parser('lost', **parser4_kwargs) + parser4.add_argument('-w', type=int, help='w help') + parser4.add_argument('x', choices='abc', help='x help') + + # add fifth sub-parser, with longer name (to test abbreviations) + parser5_kwargs = dict(description='long description') + if subparser_help: + parser5_kwargs['help'] = 'long help' + parser5 = subparsers.add_parser('long', **parser5_kwargs) + parser5.add_argument('-w', type=int, help='w help') + parser5.add_argument('x', choices='abc', help='x help') + # return the main parser return parser @@ -1857,6 +1873,24 @@ class TestAddSubparsers(TestCase): args = args_str.split() self.assertArgumentParserError(self.parser.parse_args, args) + def test_parse_args_abbreviation(self): + # check some non-failure cases: + self.assertEqual( + self.parser.parse_args('0.5 long b -w 7'.split()), + NS(foo=False, bar=0.5, w=7, x='b'), + ) + self.assertEqual( + self.parser.parse_args('0.5 lon b -w 7'.split()), + NS(foo=False, bar=0.5, w=7, x='b'), + ) + self.assertEqual( + self.parser.parse_args('0.5 los b -w 7'.split()), + NS(foo=False, bar=0.5, w=7, x='b'), + ) + # check a failure case: 'lo' is ambiguous + self.assertArgumentParserError(self.parser.parse_args, + '0.5 lo b -w 7'.split()) + def test_parse_args(self): # check some non-failure cases: self.assertEqual( @@ -1909,78 +1943,80 @@ class TestAddSubparsers(TestCase): def test_help(self): self.assertEqual(self.parser.format_usage(), - 'usage: PROG [-h] [--foo] bar {1,2,3} ...\n') + 'usage: PROG [-h] [--foo] bar {1,2,3,lost,long} ...\n') self.assertEqual(self.parser.format_help(), textwrap.dedent('''\ - usage: PROG [-h] [--foo] bar {1,2,3} ... + usage: PROG [-h] [--foo] bar {1,2,3,lost,long} ... main description positional arguments: - bar bar help - {1,2,3} command help + bar bar help + {1,2,3,lost,long} command help optional arguments: - -h, --help show this help message and exit - --foo foo help + -h, --help show this help message and exit + --foo foo help ''')) def test_help_extra_prefix_chars(self): # Make sure - is still used for help if it is a non-first prefix char parser = self._get_parser(prefix_chars='+:-') self.assertEqual(parser.format_usage(), - 'usage: PROG [-h] [++foo] bar {1,2,3} ...\n') + 'usage: PROG [-h] [++foo] bar {1,2,3,lost,long} ...\n') self.assertEqual(parser.format_help(), textwrap.dedent('''\ - usage: PROG [-h] [++foo] bar {1,2,3} ... + usage: PROG [-h] [++foo] bar {1,2,3,lost,long} ... main description positional arguments: - bar bar help - {1,2,3} command help + bar bar help + {1,2,3,lost,long} command help optional arguments: - -h, --help show this help message and exit - ++foo foo help + -h, --help show this help message and exit + ++foo foo help ''')) def test_help_alternate_prefix_chars(self): parser = self._get_parser(prefix_chars='+:/') self.assertEqual(parser.format_usage(), - 'usage: PROG [+h] [++foo] bar {1,2,3} ...\n') + 'usage: PROG [+h] [++foo] bar {1,2,3,lost,long} ...\n') self.assertEqual(parser.format_help(), textwrap.dedent('''\ - usage: PROG [+h] [++foo] bar {1,2,3} ... + usage: PROG [+h] [++foo] bar {1,2,3,lost,long} ... main description positional arguments: - bar bar help - {1,2,3} command help + bar bar help + {1,2,3,lost,long} command help optional arguments: - +h, ++help show this help message and exit - ++foo foo help + +h, ++help show this help message and exit + ++foo foo help ''')) def test_parser_command_help(self): self.assertEqual(self.command_help_parser.format_usage(), - 'usage: PROG [-h] [--foo] bar {1,2,3} ...\n') + 'usage: PROG [-h] [--foo] bar {1,2,3,lost,long} ...\n') self.assertEqual(self.command_help_parser.format_help(), textwrap.dedent('''\ - usage: PROG [-h] [--foo] bar {1,2,3} ... + usage: PROG [-h] [--foo] bar {1,2,3,lost,long} ... main description positional arguments: - bar bar help - {1,2,3} command help - 1 1 help - 2 2 help - 3 3 help + bar bar help + {1,2,3,lost,long} command help + 1 1 help + 2 2 help + 3 3 help + lost lost help + long long help optional arguments: - -h, --help show this help message and exit - --foo foo help + -h, --help show this help message and exit + --foo foo help ''')) def test_subparser_title_help(self): @@ -2083,6 +2119,8 @@ class TestAddSubparsers(TestCase): 1 help 2 2 help 3 3 help + lost lost help + long long help """)) # ============ -- cgit v1.2.1 From 4966c1cb947fa33dbb34b49840a1fc8c191330c6 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Fri, 19 Aug 2016 11:04:07 +0300 Subject: Issue #27157: Make only type() itself accept the one-argument form Patch by Eryk Sun and Emanuel Barry. --- Lib/test/test_types.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index 5e741153f4..a202196bd2 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -1000,6 +1000,24 @@ class ClassCreationTests(unittest.TestCase): with self.assertRaises(TypeError): X = types.new_class("X", (int(), C)) + def test_one_argument_type(self): + expected_message = 'type.__new__() takes exactly 3 arguments (1 given)' + + # Only type itself can use the one-argument form (#27157) + self.assertIs(type(5), int) + + class M(type): + pass + with self.assertRaises(TypeError) as cm: + M(5) + self.assertEqual(str(cm.exception), expected_message) + + class N(type, metaclass=M): + pass + with self.assertRaises(TypeError) as cm: + N(5) + self.assertEqual(str(cm.exception), expected_message) + class SimpleNamespaceTests(unittest.TestCase): -- cgit v1.2.1 From ee6d5c064dc74b80559255e71a20f81d5a7fd4df Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 19 Aug 2016 17:54:25 +0200 Subject: regrtest: replace "Result:" with "Tests result:" --- Lib/test/libregrtest/main.py | 2 +- Lib/test/test_regrtest.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index 78c52bd48a..b9d7ab4de0 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -437,7 +437,7 @@ class Regrtest: result = "INTERRUPTED" else: result = "SUCCESS" - print("Result: %s" % result) + print("Tests result: %s" % result) if self.ns.runleaks: os.system("leaks %d" % os.getpid()) diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py index 40862e6589..dc154616b6 100644 --- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -407,7 +407,7 @@ class BaseTestCase(unittest.TestCase): result = 'INTERRUPTED' else: result = 'SUCCESS' - self.check_line(output, 'Result: %s' % result) + self.check_line(output, 'Tests result: %s' % result) def parse_random_seed(self, output): match = self.regex_search(r'Using random seed ([0-9]+)', output) -- cgit v1.2.1 From 434ab7375d1941b9ccf8c2877cb2b17a7e200070 Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Sat, 20 Aug 2016 00:00:52 -0700 Subject: Issue26988: remove AutoEnum --- Lib/enum.py | 135 ++------------------- Lib/test/test_enum.py | 324 +------------------------------------------------- 2 files changed, 14 insertions(+), 445 deletions(-) (limited to 'Lib') diff --git a/Lib/enum.py b/Lib/enum.py index eaf50403d2..99db9e6b7f 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -8,9 +8,7 @@ except ImportError: from collections import OrderedDict -__all__ = [ - 'EnumMeta', 'Enum', 'IntEnum', 'AutoEnum', 'unique', - ] +__all__ = ['EnumMeta', 'Enum', 'IntEnum', 'unique'] def _is_descriptor(obj): @@ -54,30 +52,7 @@ class _EnumDict(dict): """ def __init__(self): super().__init__() - # list of enum members self._member_names = [] - # starting value - self._start = None - # last assigned value - self._last_value = None - # when the magic turns off - self._locked = True - # list of temporary names - self._ignore = [] - - def __getitem__(self, key): - if ( - self._generate_next_value_ is None - or self._locked - or key in self - or key in self._ignore - or _is_sunder(key) - or _is_dunder(key) - ): - return super(_EnumDict, self).__getitem__(key) - next_value = self._generate_next_value_(key, self._start, len(self._member_names), self._last_value) - self[key] = next_value - return next_value def __setitem__(self, key, value): """Changes anything not dundered or not a descriptor. @@ -89,55 +64,19 @@ class _EnumDict(dict): """ if _is_sunder(key): - if key not in ('_settings_', '_order_', '_ignore_', '_start_', '_generate_next_value_'): - raise ValueError('_names_ are reserved for future Enum use') - elif key == '_generate_next_value_': - if isinstance(value, staticmethod): - value = value.__get__(None, self) - self._generate_next_value_ = value - self._locked = False - elif key == '_ignore_': - if isinstance(value, str): - value = value.split() - else: - value = list(value) - self._ignore = value - already = set(value) & set(self._member_names) - if already: - raise ValueError( - '_ignore_ cannot specify already set names: %r' - % (already, )) - elif key == '_start_': - self._start = value - self._locked = False + raise ValueError('_names_ are reserved for future Enum use') elif _is_dunder(key): - if key == '__order__': - key = '_order_' - if _is_descriptor(value): - self._locked = True + pass elif key in self._member_names: # descriptor overwriting an enum? raise TypeError('Attempted to reuse key: %r' % key) - elif key in self._ignore: - pass elif not _is_descriptor(value): if key in self: # enum overwriting a descriptor? - raise TypeError('%r already defined as: %r' % (key, self[key])) + raise TypeError('Key already defined as: %r' % self[key]) self._member_names.append(key) - if self._generate_next_value_ is not None: - self._last_value = value - else: - # not a new member, turn off the autoassign magic - self._locked = True super().__setitem__(key, value) - # for magic "auto values" an Enum class should specify a `_generate_next_value_` - # method; that method will be used to generate missing values, and is - # implicitly a staticmethod; - # the signature should be `def _generate_next_value_(name, last_value)` - # last_value will be the last value created and/or assigned, or None - _generate_next_value_ = None # Dummy value for Enum as EnumMeta explicitly checks for it, but of course @@ -145,31 +84,14 @@ class _EnumDict(dict): # This is also why there are checks in EnumMeta like `if Enum is not None` Enum = None -_ignore_sentinel = object() + class EnumMeta(type): """Metaclass for Enum""" @classmethod - def __prepare__(metacls, cls, bases, start=None, ignore=_ignore_sentinel): - # create the namespace dict - enum_dict = _EnumDict() - # inherit previous flags and _generate_next_value_ function - member_type, first_enum = metacls._get_mixins_(bases) - if first_enum is not None: - enum_dict['_generate_next_value_'] = getattr(first_enum, '_generate_next_value_', None) - if start is None: - start = getattr(first_enum, '_start_', None) - if ignore is _ignore_sentinel: - enum_dict['_ignore_'] = 'property classmethod staticmethod'.split() - elif ignore: - enum_dict['_ignore_'] = ignore - if start is not None: - enum_dict['_start_'] = start - return enum_dict - - def __init__(cls, *args , **kwds): - super(EnumMeta, cls).__init__(*args) - - def __new__(metacls, cls, bases, classdict, **kwds): + def __prepare__(metacls, cls, bases): + return _EnumDict() + + def __new__(metacls, cls, bases, classdict): # an Enum class is final once enumeration items have been defined; it # cannot be mixed with other types (int, float, etc.) if it has an # inherited __new__ unless a new __new__ is defined (or the resulting @@ -180,24 +102,12 @@ class EnumMeta(type): # save enum items into separate mapping so they don't get baked into # the new class - enum_members = {k: classdict[k] for k in classdict._member_names} + members = {k: classdict[k] for k in classdict._member_names} for name in classdict._member_names: del classdict[name] - # adjust the sunders - _order_ = classdict.pop('_order_', None) - classdict.pop('_ignore_', None) - - # py3 support for definition order (helps keep py2/py3 code in sync) - if _order_ is not None: - if isinstance(_order_, str): - _order_ = _order_.replace(',', ' ').split() - unique_members = [n for n in clsdict._member_names if n in _order_] - if _order_ != unique_members: - raise TypeError('member order does not match _order_') - # check for illegal enum names (any others?) - invalid_names = set(enum_members) & {'mro', } + invalid_names = set(members) & {'mro', } if invalid_names: raise ValueError('Invalid enum member name: {0}'.format( ','.join(invalid_names))) @@ -241,7 +151,7 @@ class EnumMeta(type): # a custom __new__ is doing something funky with the values -- such as # auto-numbering ;) for member_name in classdict._member_names: - value = enum_members[member_name] + value = members[member_name] if not isinstance(value, tuple): args = (value, ) else: @@ -255,10 +165,7 @@ class EnumMeta(type): else: enum_member = __new__(enum_class, *args) if not hasattr(enum_member, '_value_'): - if member_type is object: - enum_member._value_ = value - else: - enum_member._value_ = member_type(*args) + enum_member._value_ = member_type(*args) value = enum_member._value_ enum_member._name_ = member_name enum_member.__objclass__ = enum_class @@ -665,22 +572,6 @@ class IntEnum(int, Enum): def _reduce_ex_by_name(self, proto): return self.name -class AutoEnum(Enum): - """Enum where values are automatically assigned.""" - def _generate_next_value_(name, start, count, last_value): - """ - Generate the next value when not given. - - name: the name of the member - start: the initital start value or None - count: the number of existing members - last_value: the last value assigned or None - """ - # add one to the last assigned value - if not count: - return start if start is not None else 1 - return last_value + 1 - def unique(enumeration): """Class decorator for enumerations ensuring unique member values.""" duplicates = [] diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index 4a732f908e..564c0e9f7d 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -3,7 +3,7 @@ import inspect import pydoc import unittest from collections import OrderedDict -from enum import EnumMeta, Enum, IntEnum, AutoEnum, unique +from enum import Enum, IntEnum, EnumMeta, unique from io import StringIO from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL from test import support @@ -1570,328 +1570,6 @@ class TestEnum(unittest.TestCase): self.assertEqual(LabelledList.unprocessed, 1) self.assertEqual(LabelledList(1), LabelledList.unprocessed) - def test_ignore_as_str(self): - from datetime import timedelta - class Period(Enum, ignore='Period i'): - """ - different lengths of time - """ - def __new__(cls, value, period): - obj = object.__new__(cls) - obj._value_ = value - obj.period = period - return obj - Period = vars() - for i in range(367): - Period['Day%d' % i] = timedelta(days=i), 'day' - for i in range(53): - Period['Week%d' % i] = timedelta(days=i*7), 'week' - for i in range(13): - Period['Month%d' % i] = i, 'month' - OneDay = Day1 - OneWeek = Week1 - self.assertEqual(Period.Day7.value, timedelta(days=7)) - self.assertEqual(Period.Day7.period, 'day') - - def test_ignore_as_list(self): - from datetime import timedelta - class Period(Enum, ignore=['Period', 'i']): - """ - different lengths of time - """ - def __new__(cls, value, period): - obj = object.__new__(cls) - obj._value_ = value - obj.period = period - return obj - Period = vars() - for i in range(367): - Period['Day%d' % i] = timedelta(days=i), 'day' - for i in range(53): - Period['Week%d' % i] = timedelta(days=i*7), 'week' - for i in range(13): - Period['Month%d' % i] = i, 'month' - OneDay = Day1 - OneWeek = Week1 - self.assertEqual(Period.Day7.value, timedelta(days=7)) - self.assertEqual(Period.Day7.period, 'day') - - def test_new_with_no_value_and_int_base_class(self): - class NoValue(int, Enum): - def __new__(cls, value): - obj = int.__new__(cls, value) - obj.index = len(cls.__members__) - return obj - this = 1 - that = 2 - self.assertEqual(list(NoValue), [NoValue.this, NoValue.that]) - self.assertEqual(NoValue.this, 1) - self.assertEqual(NoValue.this.value, 1) - self.assertEqual(NoValue.this.index, 0) - self.assertEqual(NoValue.that, 2) - self.assertEqual(NoValue.that.value, 2) - self.assertEqual(NoValue.that.index, 1) - - def test_new_with_no_value(self): - class NoValue(Enum): - def __new__(cls, value): - obj = object.__new__(cls) - obj.index = len(cls.__members__) - return obj - this = 1 - that = 2 - self.assertEqual(list(NoValue), [NoValue.this, NoValue.that]) - self.assertEqual(NoValue.this.value, 1) - self.assertEqual(NoValue.this.index, 0) - self.assertEqual(NoValue.that.value, 2) - self.assertEqual(NoValue.that.index, 1) - - -class TestAutoNumber(unittest.TestCase): - - def test_autonumbering(self): - class Color(AutoEnum): - red - green - blue - self.assertEqual(list(Color), [Color.red, Color.green, Color.blue]) - self.assertEqual(Color.red.value, 1) - self.assertEqual(Color.green.value, 2) - self.assertEqual(Color.blue.value, 3) - - def test_autointnumbering(self): - class Color(int, AutoEnum): - red - green - blue - self.assertTrue(isinstance(Color.red, int)) - self.assertEqual(Color.green, 2) - self.assertTrue(Color.blue > Color.red) - - def test_autonumbering_with_start(self): - class Color(AutoEnum, start=7): - red - green - blue - self.assertEqual(list(Color), [Color.red, Color.green, Color.blue]) - self.assertEqual(Color.red.value, 7) - self.assertEqual(Color.green.value, 8) - self.assertEqual(Color.blue.value, 9) - - def test_autonumbering_with_start_and_skip(self): - class Color(AutoEnum, start=7): - red - green - blue = 11 - brown - self.assertEqual(list(Color), [Color.red, Color.green, Color.blue, Color.brown]) - self.assertEqual(Color.red.value, 7) - self.assertEqual(Color.green.value, 8) - self.assertEqual(Color.blue.value, 11) - self.assertEqual(Color.brown.value, 12) - - - def test_badly_overridden_ignore(self): - with self.assertRaisesRegex(TypeError, "'int' object is not callable"): - class Color(AutoEnum): - _ignore_ = () - red - green - blue - @property - def whatever(self): - pass - with self.assertRaisesRegex(TypeError, "'int' object is not callable"): - class Color(AutoEnum, ignore=None): - red - green - blue - @property - def whatever(self): - pass - with self.assertRaisesRegex(TypeError, "'int' object is not callable"): - class Color(AutoEnum, ignore='classmethod staticmethod'): - red - green - blue - @property - def whatever(self): - pass - - def test_property(self): - class Color(AutoEnum): - red - green - blue - @property - def cap_name(self): - return self.name.title() - self.assertEqual(Color.blue.cap_name, 'Blue') - - def test_magic_turns_off(self): - with self.assertRaisesRegex(NameError, "brown"): - class Color(AutoEnum): - red - green - blue - @property - def cap_name(self): - return self.name.title() - brown - - with self.assertRaisesRegex(NameError, "rose"): - class Color(AutoEnum): - red - green - blue - def hello(self): - print('Hello! My serial is %s.' % self.value) - rose - - with self.assertRaisesRegex(NameError, "cyan"): - class Color(AutoEnum): - red - green - blue - def __init__(self, *args): - pass - cyan - - -class TestGenerateMethod(unittest.TestCase): - - def test_autonaming(self): - class Color(Enum): - def _generate_next_value_(name, start, count, last_value): - return name - Red - Green - Blue - self.assertEqual(list(Color), [Color.Red, Color.Green, Color.Blue]) - self.assertEqual(Color.Red.value, 'Red') - self.assertEqual(Color.Green.value, 'Green') - self.assertEqual(Color.Blue.value, 'Blue') - - def test_autonamestr(self): - class Color(str, Enum): - def _generate_next_value_(name, start, count, last_value): - return name - Red - Green - Blue - self.assertTrue(isinstance(Color.Red, str)) - self.assertEqual(Color.Green, 'Green') - self.assertTrue(Color.Blue < Color.Red) - - def test_generate_as_staticmethod(self): - class Color(str, Enum): - @staticmethod - def _generate_next_value_(name, start, count, last_value): - return name.lower() - Red - Green - Blue - self.assertTrue(isinstance(Color.Red, str)) - self.assertEqual(Color.Green, 'green') - self.assertTrue(Color.Blue < Color.Red) - - - def test_overridden_ignore(self): - with self.assertRaisesRegex(TypeError, "'str' object is not callable"): - class Color(Enum): - def _generate_next_value_(name, start, count, last_value): - return name - _ignore_ = () - red - green - blue - @property - def whatever(self): - pass - with self.assertRaisesRegex(TypeError, "'str' object is not callable"): - class Color(Enum, ignore=None): - def _generate_next_value_(name, start, count, last_value): - return name - red - green - blue - @property - def whatever(self): - pass - - def test_property(self): - class Color(Enum): - def _generate_next_value_(name, start, count, last_value): - return name - red - green - blue - @property - def upper_name(self): - return self.name.upper() - self.assertEqual(Color.blue.upper_name, 'BLUE') - - def test_magic_turns_off(self): - with self.assertRaisesRegex(NameError, "brown"): - class Color(Enum): - def _generate_next_value_(name, start, count, last_value): - return name - red - green - blue - @property - def cap_name(self): - return self.name.title() - brown - - with self.assertRaisesRegex(NameError, "rose"): - class Color(Enum): - def _generate_next_value_(name, start, count, last_value): - return name - red - green - blue - def hello(self): - print('Hello! My value %s.' % self.value) - rose - - with self.assertRaisesRegex(NameError, "cyan"): - class Color(Enum): - def _generate_next_value_(name, start, count, last_value): - return name - red - green - blue - def __init__(self, *args): - pass - cyan - - def test_powers_of_two(self): - class Bits(Enum): - def _generate_next_value_(name, start, count, last_value): - return 2 ** count - one - two - four - eight - self.assertEqual(Bits.one.value, 1) - self.assertEqual(Bits.two.value, 2) - self.assertEqual(Bits.four.value, 4) - self.assertEqual(Bits.eight.value, 8) - - def test_powers_of_two_as_int(self): - class Bits(int, Enum): - def _generate_next_value_(name, start, count, last_value): - return 2 ** count - one - two - four - eight - self.assertEqual(Bits.one, 1) - self.assertEqual(Bits.two, 2) - self.assertEqual(Bits.four, 4) - self.assertEqual(Bits.eight, 8) - class TestUnique(unittest.TestCase): -- cgit v1.2.1 From 969800d5fa708608d2906ca2692dc7c5dfafc978 Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Sat, 20 Aug 2016 07:19:31 -0700 Subject: issue26981: add _order_ compatibility shim to enum.Enum --- Lib/enum.py | 17 ++++++++++++-- Lib/test/test_enum.py | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/enum.py b/Lib/enum.py index 99db9e6b7f..e7889a8dc7 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -64,9 +64,11 @@ class _EnumDict(dict): """ if _is_sunder(key): - raise ValueError('_names_ are reserved for future Enum use') + if key not in ('_order_', ): + raise ValueError('_names_ are reserved for future Enum use') elif _is_dunder(key): - pass + if key == '__order__': + key = '_order_' elif key in self._member_names: # descriptor overwriting an enum? raise TypeError('Attempted to reuse key: %r' % key) @@ -106,6 +108,9 @@ class EnumMeta(type): for name in classdict._member_names: del classdict[name] + # adjust the sunders + _order_ = classdict.pop('_order_', None) + # check for illegal enum names (any others?) invalid_names = set(members) & {'mro', } if invalid_names: @@ -210,6 +215,14 @@ class EnumMeta(type): if save_new: enum_class.__new_member__ = __new__ enum_class.__new__ = Enum.__new__ + + # py3 support for definition order (helps keep py2/py3 code in sync) + if _order_ is not None: + if isinstance(_order_, str): + _order_ = _order_.replace(',', ' ').split() + if _order_ != enum_class._member_names_: + raise TypeError('member order does not match _order_') + return enum_class def __bool__(self): diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index 564c0e9f7d..2d4519e9ed 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -1571,6 +1571,68 @@ class TestEnum(unittest.TestCase): self.assertEqual(LabelledList(1), LabelledList.unprocessed) +class TestOrder(unittest.TestCase): + + def test_same_members(self): + class Color(Enum): + _order_ = 'red green blue' + red = 1 + green = 2 + blue = 3 + + def test_same_members_with_aliases(self): + class Color(Enum): + _order_ = 'red green blue' + red = 1 + green = 2 + blue = 3 + verde = green + + def test_same_members_wrong_order(self): + with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): + class Color(Enum): + _order_ = 'red green blue' + red = 1 + blue = 3 + green = 2 + + def test_order_has_extra_members(self): + with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): + class Color(Enum): + _order_ = 'red green blue purple' + red = 1 + green = 2 + blue = 3 + + def test_order_has_extra_members_with_aliases(self): + with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): + class Color(Enum): + _order_ = 'red green blue purple' + red = 1 + green = 2 + blue = 3 + verde = green + + def test_enum_has_extra_members(self): + with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): + class Color(Enum): + _order_ = 'red green blue' + red = 1 + green = 2 + blue = 3 + purple = 4 + + def test_enum_has_extra_members_with_aliases(self): + with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): + class Color(Enum): + _order_ = 'red green blue' + red = 1 + green = 2 + blue = 3 + purple = 4 + verde = green + + class TestUnique(unittest.TestCase): def test_unique_clean(self): -- cgit v1.2.1 From 99d9701d3084cc587261b78cc1d7b518d10f2ab1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 20 Aug 2016 17:31:07 -0400 Subject: Issue #27819: Simply default to gztar for sdist formats by default on all platforms. --- Lib/distutils/command/sdist.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'Lib') diff --git a/Lib/distutils/command/sdist.py b/Lib/distutils/command/sdist.py index 35a06eb09b..f1b8d91977 100644 --- a/Lib/distutils/command/sdist.py +++ b/Lib/distutils/command/sdist.py @@ -91,9 +91,6 @@ class sdist(Command): negative_opt = {'no-defaults': 'use-defaults', 'no-prune': 'prune' } - default_format = {'posix': 'gztar', - 'nt': 'zip' } - sub_commands = [('check', checking_metadata)] def initialize_options(self): @@ -110,7 +107,7 @@ class sdist(Command): self.manifest_only = 0 self.force_manifest = 0 - self.formats = None + self.formats = ['gztar'] self.keep_temp = 0 self.dist_dir = None @@ -126,13 +123,6 @@ class sdist(Command): self.template = "MANIFEST.in" self.ensure_string_list('formats') - if self.formats is None: - try: - self.formats = [self.default_format[os.name]] - except KeyError: - raise DistutilsPlatformError( - "don't know how to create source distributions " - "on platform %s" % os.name) bad_format = archive_util.check_archive_formats(self.formats) if bad_format: -- cgit v1.2.1 From 40bc2958ebd0639b025c56a3269b08d402548019 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 21 Aug 2016 20:03:08 +0300 Subject: Issue #26984: int() now always returns an instance of exact int. --- Lib/test/test_int.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_int.py b/Lib/test/test_int.py index b66c5d6709..8847f4ce97 100644 --- a/Lib/test/test_int.py +++ b/Lib/test/test_int.py @@ -430,21 +430,24 @@ class IntTestCases(unittest.TestCase): with self.assertWarns(DeprecationWarning): n = int(bad_int) self.assertEqual(n, 1) + self.assertIs(type(n), int) bad_int = BadInt2() with self.assertWarns(DeprecationWarning): n = int(bad_int) self.assertEqual(n, 1) + self.assertIs(type(n), int) bad_int = TruncReturnsBadInt() with self.assertWarns(DeprecationWarning): n = int(bad_int) self.assertEqual(n, 1) + self.assertIs(type(n), int) good_int = TruncReturnsIntSubclass() n = int(good_int) self.assertEqual(n, 1) - self.assertIs(type(n), bool) + self.assertIs(type(n), int) n = IntSubclass(good_int) self.assertEqual(n, 1) self.assertIs(type(n), IntSubclass) -- cgit v1.2.1 From 1eaab50e31a5e7523b5f22a360212a8cff2af256 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sun, 21 Aug 2016 20:52:26 -0700 Subject: Remove main section that was only used during testing and development --- Lib/collections/__main__.py | 38 -------------------------------------- 1 file changed, 38 deletions(-) delete mode 100644 Lib/collections/__main__.py (limited to 'Lib') diff --git a/Lib/collections/__main__.py b/Lib/collections/__main__.py deleted file mode 100644 index 763e38e0c4..0000000000 --- a/Lib/collections/__main__.py +++ /dev/null @@ -1,38 +0,0 @@ -################################################################################ -### Simple tests -################################################################################ - -# verify that instances can be pickled -from collections import namedtuple -from pickle import loads, dumps -Point = namedtuple('Point', 'x, y', True) -p = Point(x=10, y=20) -assert p == loads(dumps(p)) - -# test and demonstrate ability to override methods -class Point(namedtuple('Point', 'x y')): - __slots__ = () - @property - def hypot(self): - return (self.x ** 2 + self.y ** 2) ** 0.5 - def __str__(self): - return 'Point: x=%6.3f y=%6.3f hypot=%6.3f' % (self.x, self.y, self.hypot) - -for p in Point(3, 4), Point(14, 5/7.): - print (p) - -class Point(namedtuple('Point', 'x y')): - 'Point class with optimized _make() and _replace() without error-checking' - __slots__ = () - _make = classmethod(tuple.__new__) - def _replace(self, _map=map, **kwds): - return self._make(_map(kwds.get, ('x', 'y'), self)) - -print(Point(11, 22)._replace(x=100)) - -Point3D = namedtuple('Point3D', Point._fields + ('z',)) -print(Point3D.__doc__) - -import doctest, collections -TestResults = namedtuple('TestResults', 'failed attempted') -print(TestResults(*doctest.testmod(collections))) -- cgit v1.2.1 From 0536748e5e42c34e4c6cb9cb9a672d79b7f03729 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Mon, 22 Aug 2016 12:24:46 +0100 Subject: Issue #27792: force int return type for modulo operations involving bools. --- Lib/test/test_bool.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_bool.py b/Lib/test/test_bool.py index 5f7e842da2..9f8f0e122c 100644 --- a/Lib/test/test_bool.py +++ b/Lib/test/test_bool.py @@ -96,6 +96,13 @@ class BoolTest(unittest.TestCase): self.assertEqual(False/1, 0) self.assertIsNot(False/1, False) + self.assertEqual(True%1, 0) + self.assertIsNot(True%1, False) + self.assertEqual(True%2, 1) + self.assertIsNot(True%2, True) + self.assertEqual(False%1, 0) + self.assertIsNot(False%1, False) + for b in False, True: for i in 0, 1, 2: self.assertEqual(b**i, int(b)**i) -- cgit v1.2.1 From 2541d1fd5b2914c638d13b6d0b9a9ba2a979cc5a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 22 Aug 2016 14:28:52 +0200 Subject: Cleanup libregrtest * main.py: remove unused import * runtest: simplify runtest_inner() parameters, reuse ns parameter --- Lib/test/libregrtest/main.py | 1 - Lib/test/libregrtest/runtest.py | 56 +++++++++++++++-------------------------- 2 files changed, 20 insertions(+), 37 deletions(-) (limited to 'Lib') diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index b9d7ab4de0..ba9e48b448 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -1,6 +1,5 @@ import datetime import faulthandler -import math import os import platform import random diff --git a/Lib/test/libregrtest/runtest.py b/Lib/test/libregrtest/runtest.py index ef1feb738a..7d5290e0c7 100644 --- a/Lib/test/libregrtest/runtest.py +++ b/Lib/test/libregrtest/runtest.py @@ -74,18 +74,12 @@ def findtests(testdir=None, stdtests=STDTESTS, nottests=NOTTESTS): def runtest(ns, test): """Run a single test. + ns -- regrtest namespace of options test -- the name of the test - verbose -- if true, print more messages - quiet -- if true, don't print 'skipped' messages (probably redundant) - huntrleaks -- run multiple times to test for leaks; requires a debug - build; a triple corresponding to -R's three arguments - output_on_failure -- if true, display test output on failure - timeout -- dump the traceback and exit if a test takes more than - timeout seconds - failfast, match_tests -- See regrtest command-line flags for these. - pgo -- if true, suppress any info irrelevant to a generating a PGO build - Returns the tuple result, test_time, where result is one of the constants: + Returns the tuple (result, test_time), where result is one of the + constants: + INTERRUPTED KeyboardInterrupt when run under -j RESOURCE_DENIED test skipped because resource denied SKIPPED test skipped for some other reason @@ -94,21 +88,14 @@ def runtest(ns, test): PASSED test passed """ - verbose = ns.verbose - quiet = ns.quiet - huntrleaks = ns.huntrleaks output_on_failure = ns.verbose3 - failfast = ns.failfast - match_tests = ns.match_tests - timeout = ns.timeout - pgo = ns.pgo - use_timeout = (timeout is not None) + use_timeout = (ns.timeout is not None) if use_timeout: - faulthandler.dump_traceback_later(timeout, exit=True) + faulthandler.dump_traceback_later(ns.timeout, exit=True) try: - support.match_tests = match_tests - if failfast: + support.match_tests = ns.match_tests + if ns.failfast: support.failfast = True if output_on_failure: support.verbose = True @@ -129,8 +116,7 @@ def runtest(ns, test): try: sys.stdout = stream sys.stderr = stream - result = runtest_inner(ns, test, verbose, quiet, huntrleaks, - display_failure=False, pgo=pgo) + result = runtest_inner(ns, test, display_failure=False) if result[0] == FAILED: output = stream.getvalue() orig_stderr.write(output) @@ -139,19 +125,17 @@ def runtest(ns, test): sys.stdout = orig_stdout sys.stderr = orig_stderr else: - support.verbose = verbose # Tell tests to be moderately quiet - result = runtest_inner(ns, test, verbose, quiet, huntrleaks, - display_failure=not verbose, pgo=pgo) + support.verbose = ns.verbose # Tell tests to be moderately quiet + result = runtest_inner(ns, test, display_failure=not ns.verbose) return result finally: if use_timeout: faulthandler.cancel_dump_traceback_later() - cleanup_test_droppings(test, verbose) + cleanup_test_droppings(test, ns.verbose) runtest.stringio = None -def runtest_inner(ns, test, verbose, quiet, - huntrleaks=False, display_failure=True, *, pgo=False): +def runtest_inner(ns, test, display_failure=True): support.unload(test) test_time = 0.0 @@ -162,7 +146,7 @@ def runtest_inner(ns, test, verbose, quiet, else: # Always import it from the test package abstest = 'test.' + test - with saved_test_environment(test, verbose, quiet, pgo=pgo) as environment: + with saved_test_environment(test, ns.verbose, ns.quiet, pgo=ns.pgo) as environment: start_time = time.time() the_module = importlib.import_module(abstest) # If the test has a test_main, that will run the appropriate @@ -178,21 +162,21 @@ def runtest_inner(ns, test, verbose, quiet, raise Exception("errors while loading tests") support.run_unittest(tests) test_runner() - if huntrleaks: - refleak = dash_R(the_module, test, test_runner, huntrleaks) + if ns.huntrleaks: + refleak = dash_R(the_module, test, test_runner, ns.huntrleaks) test_time = time.time() - start_time except support.ResourceDenied as msg: - if not quiet and not pgo: + if not ns.quiet and not ns.pgo: print(test, "skipped --", msg, flush=True) return RESOURCE_DENIED, test_time except unittest.SkipTest as msg: - if not quiet and not pgo: + if not ns.quiet and not ns.pgo: print(test, "skipped --", msg, flush=True) return SKIPPED, test_time except KeyboardInterrupt: raise except support.TestFailed as msg: - if not pgo: + if not ns.pgo: if display_failure: print("test", test, "failed --", msg, file=sys.stderr, flush=True) @@ -201,7 +185,7 @@ def runtest_inner(ns, test, verbose, quiet, return FAILED, test_time except: msg = traceback.format_exc() - if not pgo: + if not ns.pgo: print("test", test, "crashed --", msg, file=sys.stderr, flush=True) return FAILED, test_time -- cgit v1.2.1 From c9db6f51d304d60303b35fc483a801d24e4546f5 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 22 Aug 2016 14:29:54 +0200 Subject: Issue #27829: libregrtest.save_env: flush stderr Use flush=True to try to get a warning which is missing in buildbots. Use also f-string to make the code shorter. --- Lib/test/libregrtest/save_env.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'Lib') diff --git a/Lib/test/libregrtest/save_env.py b/Lib/test/libregrtest/save_env.py index 261c8f4753..96ad3af8df 100644 --- a/Lib/test/libregrtest/save_env.py +++ b/Lib/test/libregrtest/save_env.py @@ -277,11 +277,9 @@ class saved_test_environment: self.changed = True restore(original) if not self.quiet and not self.pgo: - print("Warning -- {} was modified by {}".format( - name, self.testname), - file=sys.stderr) + print(f"Warning -- {name} was modified by {self.testname}", + file=sys.stderr, flush=True) if self.verbose > 1: - print(" Before: {}\n After: {} ".format( - original, current), - file=sys.stderr) + print(f" Before: {original}\n After: {current} ", + file=sys.stderr, flush=True) return False -- cgit v1.2.1 From 53b09e11d5c12b544f3ac12c9166b3954c86e414 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Tue, 23 Aug 2016 08:43:16 +0100 Subject: Issue #12713: reverted fix pending further discussion. --- Lib/argparse.py | 23 +++--------- Lib/test/test_argparse.py | 92 ++++++++++++++--------------------------------- 2 files changed, 32 insertions(+), 83 deletions(-) (limited to 'Lib') diff --git a/Lib/argparse.py b/Lib/argparse.py index e0edad8e42..209b4e99c1 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -1110,12 +1110,6 @@ class _SubParsersAction(Action): parser_name = values[0] arg_strings = values[1:] - # get full parser_name from (optional) abbreviated one - for p in self._name_parser_map: - if p.startswith(parser_name): - parser_name = p - break - # set the parser name if requested if self.dest is not SUPPRESS: setattr(namespace, self.dest, parser_name) @@ -2313,18 +2307,11 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer): def _check_value(self, action, value): # converted value must be one of the choices (if specified) - if action.choices is not None: - ac = [ax for ax in action.choices if str(ax).startswith(str(value))] - if len(ac) == 0: - args = {'value': value, - 'choices': ', '.join(map(repr, action.choices))} - msg = _('invalid choice: %(value)r (choose from %(choices)s)') - raise ArgumentError(action, msg % args) - elif len(ac) > 1: - args = {'value': value, - 'choices': ', '.join(ac)} - msg = _('ambiguous choice: %(value)r could match %(choices)s') - raise ArgumentError(action, msg % args) + if action.choices is not None and value not in action.choices: + args = {'value': value, + 'choices': ', '.join(map(repr, action.choices))} + msg = _('invalid choice: %(value)r (choose from %(choices)s)') + raise ArgumentError(action, msg % args) # ======================= # Help-formatting methods diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index 32d1b0cbb9..52c624771c 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -1842,22 +1842,6 @@ class TestAddSubparsers(TestCase): parser3.add_argument('t', type=int, help='t help') parser3.add_argument('u', nargs='...', help='u help') - # add fourth sub-parser (to test abbreviations) - parser4_kwargs = dict(description='lost description') - if subparser_help: - parser4_kwargs['help'] = 'lost help' - parser4 = subparsers.add_parser('lost', **parser4_kwargs) - parser4.add_argument('-w', type=int, help='w help') - parser4.add_argument('x', choices='abc', help='x help') - - # add fifth sub-parser, with longer name (to test abbreviations) - parser5_kwargs = dict(description='long description') - if subparser_help: - parser5_kwargs['help'] = 'long help' - parser5 = subparsers.add_parser('long', **parser5_kwargs) - parser5.add_argument('-w', type=int, help='w help') - parser5.add_argument('x', choices='abc', help='x help') - # return the main parser return parser @@ -1873,24 +1857,6 @@ class TestAddSubparsers(TestCase): args = args_str.split() self.assertArgumentParserError(self.parser.parse_args, args) - def test_parse_args_abbreviation(self): - # check some non-failure cases: - self.assertEqual( - self.parser.parse_args('0.5 long b -w 7'.split()), - NS(foo=False, bar=0.5, w=7, x='b'), - ) - self.assertEqual( - self.parser.parse_args('0.5 lon b -w 7'.split()), - NS(foo=False, bar=0.5, w=7, x='b'), - ) - self.assertEqual( - self.parser.parse_args('0.5 los b -w 7'.split()), - NS(foo=False, bar=0.5, w=7, x='b'), - ) - # check a failure case: 'lo' is ambiguous - self.assertArgumentParserError(self.parser.parse_args, - '0.5 lo b -w 7'.split()) - def test_parse_args(self): # check some non-failure cases: self.assertEqual( @@ -1943,80 +1909,78 @@ class TestAddSubparsers(TestCase): def test_help(self): self.assertEqual(self.parser.format_usage(), - 'usage: PROG [-h] [--foo] bar {1,2,3,lost,long} ...\n') + 'usage: PROG [-h] [--foo] bar {1,2,3} ...\n') self.assertEqual(self.parser.format_help(), textwrap.dedent('''\ - usage: PROG [-h] [--foo] bar {1,2,3,lost,long} ... + usage: PROG [-h] [--foo] bar {1,2,3} ... main description positional arguments: - bar bar help - {1,2,3,lost,long} command help + bar bar help + {1,2,3} command help optional arguments: - -h, --help show this help message and exit - --foo foo help + -h, --help show this help message and exit + --foo foo help ''')) def test_help_extra_prefix_chars(self): # Make sure - is still used for help if it is a non-first prefix char parser = self._get_parser(prefix_chars='+:-') self.assertEqual(parser.format_usage(), - 'usage: PROG [-h] [++foo] bar {1,2,3,lost,long} ...\n') + 'usage: PROG [-h] [++foo] bar {1,2,3} ...\n') self.assertEqual(parser.format_help(), textwrap.dedent('''\ - usage: PROG [-h] [++foo] bar {1,2,3,lost,long} ... + usage: PROG [-h] [++foo] bar {1,2,3} ... main description positional arguments: - bar bar help - {1,2,3,lost,long} command help + bar bar help + {1,2,3} command help optional arguments: - -h, --help show this help message and exit - ++foo foo help + -h, --help show this help message and exit + ++foo foo help ''')) def test_help_alternate_prefix_chars(self): parser = self._get_parser(prefix_chars='+:/') self.assertEqual(parser.format_usage(), - 'usage: PROG [+h] [++foo] bar {1,2,3,lost,long} ...\n') + 'usage: PROG [+h] [++foo] bar {1,2,3} ...\n') self.assertEqual(parser.format_help(), textwrap.dedent('''\ - usage: PROG [+h] [++foo] bar {1,2,3,lost,long} ... + usage: PROG [+h] [++foo] bar {1,2,3} ... main description positional arguments: - bar bar help - {1,2,3,lost,long} command help + bar bar help + {1,2,3} command help optional arguments: - +h, ++help show this help message and exit - ++foo foo help + +h, ++help show this help message and exit + ++foo foo help ''')) def test_parser_command_help(self): self.assertEqual(self.command_help_parser.format_usage(), - 'usage: PROG [-h] [--foo] bar {1,2,3,lost,long} ...\n') + 'usage: PROG [-h] [--foo] bar {1,2,3} ...\n') self.assertEqual(self.command_help_parser.format_help(), textwrap.dedent('''\ - usage: PROG [-h] [--foo] bar {1,2,3,lost,long} ... + usage: PROG [-h] [--foo] bar {1,2,3} ... main description positional arguments: - bar bar help - {1,2,3,lost,long} command help - 1 1 help - 2 2 help - 3 3 help - lost lost help - long long help + bar bar help + {1,2,3} command help + 1 1 help + 2 2 help + 3 3 help optional arguments: - -h, --help show this help message and exit - --foo foo help + -h, --help show this help message and exit + --foo foo help ''')) def test_subparser_title_help(self): @@ -2119,8 +2083,6 @@ class TestAddSubparsers(TestCase): 1 help 2 2 help 3 3 help - lost lost help - long long help """)) # ============ -- cgit v1.2.1 From 381e33dc240586f1f7820e3bef0c152970b5cd63 Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Tue, 23 Aug 2016 09:01:43 +0000 Subject: Issue #27787: Remove test_main() and hard-coded list of test classes The @reap_threads decorator made the test wait (for up to 1 s) until background threads have finished. Calling join() with a timeout should be equivalent. --- Lib/test/test_httplib.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index f45e352d6a..5c500cbc46 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -940,6 +940,7 @@ class BasicTest(TestCase): thread = threading.Thread(target=run_server) thread.start() + self.addCleanup(thread.join, float(1)) conn = client.HTTPConnection(*serv.getsockname()) conn.request("CONNECT", "dummy:1234") response = conn.getresponse() @@ -953,7 +954,7 @@ class BasicTest(TestCase): finally: response.close() conn.close() - thread.join() + thread.join() self.assertEqual(result, b"proxied data\n") class ExtendedReadTest(TestCase): @@ -1711,13 +1712,5 @@ class TunnelTests(TestCase): self.assertIn('header: {}'.format(expected_header), lines) -@support.reap_threads -def test_main(verbose=None): - support.run_unittest(HeaderTests, OfflineTest, BasicTest, TimeoutTest, - PersistenceTest, - HTTPSTest, RequestBodyTest, SourceAddressTest, - HTTPResponseTest, ExtendedReadTest, - ExtendedReadTestChunked, TunnelTests) - if __name__ == '__main__': test_main() -- cgit v1.2.1 From 863af1ef1a68b6ada5d968888a0b77526826746c Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Tue, 23 Aug 2016 16:16:52 +0100 Subject: Issue #27832: Make _normalize parameter to Fraction.__init__ keyword-only. --- Lib/fractions.py | 2 +- Lib/test/test_fractions.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/fractions.py b/Lib/fractions.py index a7120522cf..8330202d70 100644 --- a/Lib/fractions.py +++ b/Lib/fractions.py @@ -81,7 +81,7 @@ class Fraction(numbers.Rational): __slots__ = ('_numerator', '_denominator') # We're immutable, so use __new__ not __init__ - def __new__(cls, numerator=0, denominator=None, _normalize=True): + def __new__(cls, numerator=0, denominator=None, *, _normalize=True): """Constructs a Rational. Takes a string like '3/2' or '1.5', another Rational instance, a diff --git a/Lib/test/test_fractions.py b/Lib/test/test_fractions.py index 664c735469..7905c367ba 100644 --- a/Lib/test/test_fractions.py +++ b/Lib/test/test_fractions.py @@ -150,6 +150,7 @@ class FractionTest(unittest.TestCase): self.assertRaises(TypeError, F, "3/2", 3) self.assertRaises(TypeError, F, 3, 0j) self.assertRaises(TypeError, F, 3, 1j) + self.assertRaises(TypeError, F, 1, 2, 3) @requires_IEEE_754 def testInitFromFloat(self): -- cgit v1.2.1 From eade75be94670ca0ac65cde253f8e4ae36adaf56 Mon Sep 17 00:00:00 2001 From: Steven D'Aprano Date: Wed, 24 Aug 2016 01:42:15 +1000 Subject: Issue #27573 make the exit message configurable. --- Lib/code.py | 17 +++++++++++++---- Lib/test/test_code_module.py | 20 ++++++++++++++++++++ 2 files changed, 33 insertions(+), 4 deletions(-) (limited to 'Lib') diff --git a/Lib/code.py b/Lib/code.py index c8b72042e0..23295f4cf5 100644 --- a/Lib/code.py +++ b/Lib/code.py @@ -186,7 +186,7 @@ class InteractiveConsole(InteractiveInterpreter): """Reset the input buffer.""" self.buffer = [] - def interact(self, banner=None): + def interact(self, banner=None, exitmsg=None): """Closely emulate the interactive Python console. The optional banner argument specifies the banner to print @@ -196,6 +196,11 @@ class InteractiveConsole(InteractiveInterpreter): to confuse this with the real interpreter -- since it's so close!). + The optional exitmsg argument specifies the exit message + printed when exiting. Pass the empty string to suppress + printing an exit message. If exitmsg is not given or None, + a default message is printed. + """ try: sys.ps1 @@ -230,7 +235,10 @@ class InteractiveConsole(InteractiveInterpreter): self.write("\nKeyboardInterrupt\n") self.resetbuffer() more = 0 - self.write('now exiting %s...\n' % self.__class__.__name__) + if exitmsg is None: + self.write('now exiting %s...\n' % self.__class__.__name__) + elif exitmsg != '': + self.write('%s\n' % exitmsg) def push(self, line): """Push a line to the interpreter. @@ -268,7 +276,7 @@ class InteractiveConsole(InteractiveInterpreter): -def interact(banner=None, readfunc=None, local=None): +def interact(banner=None, readfunc=None, local=None, exitmsg=None): """Closely emulate the interactive Python interpreter. This is a backwards compatible interface to the InteractiveConsole @@ -280,6 +288,7 @@ def interact(banner=None, readfunc=None, local=None): banner -- passed to InteractiveConsole.interact() readfunc -- if not None, replaces InteractiveConsole.raw_input() local -- passed to InteractiveInterpreter.__init__() + exitmsg -- passed to InteractiveConsole.interact() """ console = InteractiveConsole(local) @@ -290,7 +299,7 @@ def interact(banner=None, readfunc=None, local=None): import readline except ImportError: pass - console.interact(banner) + console.interact(banner, exitmsg) if __name__ == "__main__": diff --git a/Lib/test/test_code_module.py b/Lib/test/test_code_module.py index 08ba3f3704..1a8f6990df 100644 --- a/Lib/test/test_code_module.py +++ b/Lib/test/test_code_module.py @@ -80,6 +80,7 @@ class TestInteractiveConsole(unittest.TestCase): self.assertEqual(len(self.stderr.method_calls), 2) def test_exit_msg(self): + # default exit message self.infunc.side_effect = EOFError('Finished') self.console.interact(banner='') self.assertEqual(len(self.stderr.method_calls), 2) @@ -87,6 +88,25 @@ class TestInteractiveConsole(unittest.TestCase): expected = 'now exiting InteractiveConsole...\n' self.assertEqual(err_msg, ['write', (expected,), {}]) + # no exit message + self.stderr.reset_mock() + self.infunc.side_effect = EOFError('Finished') + self.console.interact(banner='', exitmsg='') + self.assertEqual(len(self.stderr.method_calls), 1) + + # custom exit message + self.stderr.reset_mock() + message = ( + 'bye! \N{GREEK SMALL LETTER ZETA}\N{CYRILLIC SMALL LETTER ZHE}' + ) + self.infunc.side_effect = EOFError('Finished') + self.console.interact(banner='', exitmsg=message) + self.assertEqual(len(self.stderr.method_calls), 2) + err_msg = self.stderr.method_calls[1] + expected = message + '\n' + self.assertEqual(err_msg, ['write', (expected,), {}]) + + def test_cause_tb(self): self.infunc.side_effect = ["raise ValueError('') from AttributeError", EOFError('Finished')] -- cgit v1.2.1 From d8ed3d8e85511ecc218cfd5e1067dc9e66788915 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Tue, 23 Aug 2016 17:33:54 +0100 Subject: Issue #26040 (part 1): add new testcases to cmath_testcases.txt. Thanks Jeff Allen. --- Lib/test/cmath_testcases.txt | 141 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) (limited to 'Lib') diff --git a/Lib/test/cmath_testcases.txt b/Lib/test/cmath_testcases.txt index 9b0865302e..dd7e458ddc 100644 --- a/Lib/test/cmath_testcases.txt +++ b/Lib/test/cmath_testcases.txt @@ -53,6 +53,12 @@ -- MPFR homepage at http://www.mpfr.org for more information about the -- MPFR project. +-- A minority of the test cases were generated with the help of +-- mpmath 0.19 at 100 bit accuracy (http://mpmath.org) to improve +-- coverage of real functions with real-valued arguments. These are +-- used in test.test_math.MathTests.test_testfile, as well as in +-- test_cmath. + -------------------------- -- acos: Inverse cosine -- @@ -848,6 +854,18 @@ atan0302 atan 9.9999999999999999e-161 -1.0 -> 0.78539816339744828 -184.553381029 atan0303 atan -1e-165 1.0 -> -0.78539816339744828 190.30984376228875 atan0304 atan -9.9998886718268301e-321 -1.0 -> -0.78539816339744828 -368.76019403576692 +-- Additional real values (mpmath) +atan0400 atan 1.7976931348623157e+308 0.0 -> 1.5707963267948966192 0.0 +atan0401 atan -1.7976931348623157e+308 0.0 -> -1.5707963267948966192 0.0 +atan0402 atan 1e-17 0.0 -> 1.0000000000000000715e-17 0.0 +atan0403 atan -1e-17 0.0 -> -1.0000000000000000715e-17 0.0 +atan0404 atan 0.0001 0.0 -> 0.000099999999666666673459 0.0 +atan0405 atan -0.0001 0.0 -> -0.000099999999666666673459 0.0 +atan0406 atan 0.999999999999999 0.0 -> 0.78539816339744781002 0.0 +atan0407 atan 1.000000000000001 0.0 -> 0.78539816339744886473 0.0 +atan0408 atan 14.101419947171719 0.0 -> 1.4999999999999999969 0.0 +atan0409 atan 1255.7655915007897 0.0 -> 1.5700000000000000622 0.0 + -- special values atan1000 atan -0.0 0.0 -> -0.0 0.0 atan1001 atan nan 0.0 -> nan 0.0 @@ -1514,6 +1532,11 @@ sqrt0131 sqrt -1.5477066694467245e-310 -0.0 -> 0.0 -1.2440685951533077e-155 sqrt0140 sqrt 1.6999999999999999e+308 -1.6999999999999999e+308 -> 1.4325088230154573e+154 -5.9336458271212207e+153 sqrt0141 sqrt -1.797e+308 -9.9999999999999999e+306 -> 3.7284476432057307e+152 -1.3410406899802901e+154 +-- Additional real values (mpmath) +sqrt0150 sqrt 1.7976931348623157e+308 0.0 -> 1.3407807929942596355e+154 0.0 +sqrt0151 sqrt 2.2250738585072014e-308 0.0 -> 1.4916681462400413487e-154 0.0 +sqrt0152 sqrt 5e-324 0.0 -> 2.2227587494850774834e-162 0.0 + -- special values sqrt1000 sqrt 0.0 0.0 -> 0.0 0.0 sqrt1001 sqrt -0.0 0.0 -> 0.0 0.0 @@ -1616,6 +1639,20 @@ exp0052 exp 710.0 1.5 -> 1.5802653829857376e+307 inf overflow exp0053 exp 710.0 1.6 -> -6.5231579995501372e+306 inf overflow exp0054 exp 710.0 2.8 -> -inf 7.4836177417448528e+307 overflow +-- Additional real values (mpmath) +exp0070 exp 1e-08 0.0 -> 1.00000001000000005 0.0 +exp0071 exp 0.0003 0.0 -> 1.0003000450045003375 0.0 +exp0072 exp 0.2 0.0 -> 1.2214027581601698475 0.0 +exp0073 exp 1.0 0.0 -> 2.7182818284590452354 0.0 +exp0074 exp -1e-08 0.0 -> 0.99999999000000005 0.0 +exp0075 exp -0.0003 0.0 -> 0.99970004499550033751 0.0 +exp0076 exp -1.0 0.0 -> 0.3678794411714423216 0.0 +exp0077 exp 2.220446049250313e-16 0.0 -> 1.000000000000000222 0.0 +exp0078 exp -1.1102230246251565e-16 0.0 -> 0.99999999999999988898 0.0 +exp0079 exp 2.302585092994046 0.0 -> 10.000000000000002171 0.0 +exp0080 exp -2.302585092994046 0.0 -> 0.099999999999999978292 0.0 +exp0081 exp 709.7827 0.0 -> 1.7976699566638014654e+308 0.0 + -- special values exp1000 exp 0.0 0.0 -> 1.0 0.0 exp1001 exp -0.0 0.0 -> 1.0 0.0 @@ -1708,6 +1745,23 @@ cosh0023 cosh 2.218885944363501 2.0015727395883687 -> -1.94294321081968 4.129026 cosh0030 cosh 710.5 2.3519999999999999 -> -1.2967465239355998e+308 1.3076707908857333e+308 cosh0031 cosh -710.5 0.69999999999999996 -> 1.4085466381392499e+308 -1.1864024666450239e+308 +-- Additional real values (mpmath) +cosh0050 cosh 1e-150 0.0 -> 1.0 0.0 +cosh0051 cosh 1e-18 0.0 -> 1.0 0.0 +cosh0052 cosh 1e-09 0.0 -> 1.0000000000000000005 0.0 +cosh0053 cosh 0.0003 0.0 -> 1.0000000450000003375 0.0 +cosh0054 cosh 0.2 0.0 -> 1.0200667556190758485 0.0 +cosh0055 cosh 1.0 0.0 -> 1.5430806348152437785 0.0 +cosh0056 cosh -1e-18 0.0 -> 1.0 -0.0 +cosh0057 cosh -0.0003 0.0 -> 1.0000000450000003375 -0.0 +cosh0058 cosh -1.0 0.0 -> 1.5430806348152437785 -0.0 +cosh0059 cosh 1.3169578969248168 0.0 -> 2.0000000000000001504 0.0 +cosh0060 cosh -1.3169578969248168 0.0 -> 2.0000000000000001504 -0.0 +cosh0061 cosh 17.328679513998633 0.0 -> 16777216.000000021938 0.0 +cosh0062 cosh 18.714973875118524 0.0 -> 67108864.000000043662 0.0 +cosh0063 cosh 709.7827 0.0 -> 8.9883497833190073272e+307 0.0 +cosh0064 cosh -709.7827 0.0 -> 8.9883497833190073272e+307 -0.0 + -- special values cosh1000 cosh 0.0 0.0 -> 1.0 0.0 cosh1001 cosh 0.0 inf -> nan 0.0 invalid ignore-imag-sign @@ -1800,6 +1854,24 @@ sinh0023 sinh 0.043713693678420068 0.22512549887532657 -> 0.042624198673416713 0 sinh0030 sinh 710.5 -2.3999999999999999 -> -1.3579970564885919e+308 -1.24394470907798e+308 sinh0031 sinh -710.5 0.80000000000000004 -> -1.2830671601735164e+308 1.3210954193997678e+308 +-- Additional real values (mpmath) +sinh0050 sinh 1e-100 0.0 -> 1.00000000000000002e-100 0.0 +sinh0051 sinh 5e-17 0.0 -> 4.9999999999999998955e-17 0.0 +sinh0052 sinh 1e-16 0.0 -> 9.999999999999999791e-17 0.0 +sinh0053 sinh 3.7e-08 0.0 -> 3.7000000000000008885e-8 0.0 +sinh0054 sinh 0.001 0.0 -> 0.0010000001666666750208 0.0 +sinh0055 sinh 0.2 0.0 -> 0.20133600254109399895 0.0 +sinh0056 sinh 1.0 0.0 -> 1.1752011936438014569 0.0 +sinh0057 sinh -3.7e-08 0.0 -> -3.7000000000000008885e-8 0.0 +sinh0058 sinh -0.001 0.0 -> -0.0010000001666666750208 0.0 +sinh0059 sinh -1.0 0.0 -> -1.1752011936438014569 0.0 +sinh0060 sinh 1.4436354751788103 0.0 -> 1.9999999999999999078 0.0 +sinh0061 sinh -1.4436354751788103 0.0 -> -1.9999999999999999078 0.0 +sinh0062 sinh 17.328679513998633 0.0 -> 16777215.999999992136 0.0 +sinh0063 sinh 18.714973875118524 0.0 -> 67108864.000000036211 0.0 +sinh0064 sinh 709.7827 0.0 -> 8.9883497833190073272e+307 0.0 +sinh0065 sinh -709.7827 0.0 -> -8.9883497833190073272e+307 0.0 + -- special values sinh1000 sinh 0.0 0.0 -> 0.0 0.0 sinh1001 sinh 0.0 inf -> 0.0 nan invalid ignore-real-sign @@ -1897,6 +1969,24 @@ tanh0031 tanh -711 7.4000000000000004 -> -1.0 0.0 tanh0032 tanh 1000 -2.3199999999999998 -> 1.0 0.0 tanh0033 tanh -1.0000000000000001e+300 -9.6699999999999999 -> -1.0 -0.0 +-- Additional real values (mpmath) +tanh0050 tanh 1e-100 0.0 -> 1.00000000000000002e-100 0.0 +tanh0051 tanh 5e-17 0.0 -> 4.9999999999999998955e-17 0.0 +tanh0052 tanh 1e-16 0.0 -> 9.999999999999999791e-17 0.0 +tanh0053 tanh 3.7e-08 0.0 -> 3.6999999999999983559e-8 0.0 +tanh0054 tanh 0.001 0.0 -> 0.00099999966666680002076 0.0 +tanh0055 tanh 0.2 0.0 -> 0.19737532022490401141 0.0 +tanh0056 tanh 1.0 0.0 -> 0.76159415595576488812 0.0 +tanh0057 tanh -3.7e-08 0.0 -> -3.6999999999999983559e-8 0.0 +tanh0058 tanh -0.001 0.0 -> -0.00099999966666680002076 0.0 +tanh0059 tanh -1.0 0.0 -> -0.76159415595576488812 0.0 +tanh0060 tanh 0.5493061443340549 0.0 -> 0.50000000000000003402 0.0 +tanh0061 tanh -0.5493061443340549 0.0 -> -0.50000000000000003402 0.0 +tanh0062 tanh 17.328679513998633 0.0 -> 0.99999999999999822364 0.0 +tanh0063 tanh 18.714973875118524 0.0 -> 0.99999999999999988898 0.0 +tanh0064 tanh 711 0.0 -> 1.0 0.0 +tanh0065 tanh 1.797e+308 0.0 -> 1.0 0.0 + --special values tanh1000 tanh 0.0 0.0 -> 0.0 0.0 tanh1001 tanh 0.0 inf -> nan nan invalid @@ -1985,6 +2075,22 @@ cos0021 cos 4.8048375263775256 0.0062248852898515658 -> 0.092318702015846243 0.0 cos0022 cos 7.9914515433858515 0.71659966615501436 -> -0.17375439906936566 -0.77217043527294582 cos0023 cos 0.45124351152540226 1.6992693993812158 -> 2.543477948972237 -1.1528193694875477 +-- Additional real values (mpmath) +cos0050 cos 1e-150 0.0 -> 1.0 -0.0 +cos0051 cos 1e-18 0.0 -> 1.0 -0.0 +cos0052 cos 1e-09 0.0 -> 0.9999999999999999995 -0.0 +cos0053 cos 0.0003 0.0 -> 0.9999999550000003375 -0.0 +cos0054 cos 0.2 0.0 -> 0.98006657784124162892 -0.0 +cos0055 cos 1.0 0.0 -> 0.5403023058681397174 -0.0 +cos0056 cos -1e-18 0.0 -> 1.0 0.0 +cos0057 cos -0.0003 0.0 -> 0.9999999550000003375 0.0 +cos0058 cos -1.0 0.0 -> 0.5403023058681397174 0.0 +cos0059 cos 1.0471975511965976 0.0 -> 0.50000000000000009945 -0.0 +cos0060 cos 2.5707963267948966 0.0 -> -0.84147098480789647357 -0.0 +cos0061 cos -2.5707963267948966 0.0 -> -0.84147098480789647357 0.0 +cos0062 cos 7.225663103256523 0.0 -> 0.58778525229247407559 -0.0 +cos0063 cos -8.79645943005142 0.0 -> -0.80901699437494722255 0.0 + -- special values cos1000 cos -0.0 0.0 -> 1.0 0.0 cos1001 cos -inf 0.0 -> nan 0.0 invalid ignore-imag-sign @@ -2073,6 +2179,22 @@ sin0021 sin 1.4342727387492671 0.81361889790284347 -> 1.3370960060947923 0.12336 sin0022 sin 1.1518087354403725 4.8597235966150558 -> 58.919141989603041 26.237003403758852 sin0023 sin 0.00087773078406649192 34.792379211312095 -> 565548145569.38245 644329685822700.62 +-- Additional real values (mpmath) +sin0050 sin 1e-100 0.0 -> 1.00000000000000002e-100 0.0 +sin0051 sin 3.7e-08 0.0 -> 3.6999999999999992001e-8 0.0 +sin0052 sin 0.001 0.0 -> 0.00099999983333334168748 0.0 +sin0053 sin 0.2 0.0 -> 0.19866933079506122634 0.0 +sin0054 sin 1.0 0.0 -> 0.84147098480789650665 0.0 +sin0055 sin -3.7e-08 0.0 -> -3.6999999999999992001e-8 0.0 +sin0056 sin -0.001 0.0 -> -0.00099999983333334168748 0.0 +sin0057 sin -1.0 0.0 -> -0.84147098480789650665 0.0 +sin0058 sin 0.5235987755982989 0.0 -> 0.50000000000000004642 0.0 +sin0059 sin -0.5235987755982989 0.0 -> -0.50000000000000004642 0.0 +sin0060 sin 2.6179938779914944 0.0 -> 0.49999999999999996018 -0.0 +sin0061 sin -2.6179938779914944 0.0 -> -0.49999999999999996018 -0.0 +sin0062 sin 7.225663103256523 0.0 -> 0.80901699437494673648 0.0 +sin0063 sin -8.79645943005142 0.0 -> -0.58778525229247340658 -0.0 + -- special values sin1000 sin -0.0 0.0 -> -0.0 0.0 sin1001 sin -inf 0.0 -> nan 0.0 invalid ignore-imag-sign @@ -2161,6 +2283,25 @@ tan0021 tan 1.7809617968443272 1.5052381702853379 -> -0.044066222118946903 1.093 tan0022 tan 1.1615313900880577 1.7956298728647107 -> 0.041793186826390362 1.0375339546034792 tan0023 tan 0.067014779477908945 5.8517361577457097 -> 2.2088639754800034e-06 0.9999836182420061 +-- Additional real values (mpmath) +tan0050 tan 1e-100 0.0 -> 1.00000000000000002e-100 0.0 +tan0051 tan 3.7e-08 0.0 -> 3.7000000000000017328e-8 0.0 +tan0052 tan 0.001 0.0 -> 0.0010000003333334666875 0.0 +tan0053 tan 0.2 0.0 -> 0.20271003550867249488 0.0 +tan0054 tan 1.0 0.0 -> 1.5574077246549022305 0.0 +tan0055 tan -3.7e-08 0.0 -> -3.7000000000000017328e-8 0.0 +tan0056 tan -0.001 0.0 -> -0.0010000003333334666875 0.0 +tan0057 tan -1.0 0.0 -> -1.5574077246549022305 0.0 +tan0058 tan 0.4636476090008061 0.0 -> 0.49999999999999997163 0.0 +tan0059 tan -0.4636476090008061 0.0 -> -0.49999999999999997163 0.0 +tan0060 tan 1.1071487177940904 0.0 -> 1.9999999999999995298 0.0 +tan0061 tan -1.1071487177940904 0.0 -> -1.9999999999999995298 0.0 +tan0062 tan 1.5 0.0 -> 14.101419947171719388 0.0 +tan0063 tan 1.57 0.0 -> 1255.7655915007896475 0.0 +tan0064 tan 1.5707963267948961 0.0 -> 1978937966095219.0538 0.0 +tan0065 tan 7.225663103256523 0.0 -> 1.3763819204711701522 0.0 +tan0066 tan -8.79645943005142 0.0 -> 0.7265425280053614098 0.0 + -- special values tan1000 tan -0.0 0.0 -> -0.0 0.0 tan1001 tan -inf 0.0 -> nan nan invalid -- cgit v1.2.1 From 9c460834aacc112c086b41ead22e110dedd2a9b8 Mon Sep 17 00:00:00 2001 From: Steven D'Aprano Date: Wed, 24 Aug 2016 02:40:03 +1000 Subject: Re-licence statistics.py under the standard Python licence. --- Lib/statistics.py | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'Lib') diff --git a/Lib/statistics.py b/Lib/statistics.py index f4b49b5d0c..17e471bb56 100644 --- a/Lib/statistics.py +++ b/Lib/statistics.py @@ -1,20 +1,3 @@ -## Module statistics.py -## -## Copyright (c) 2013 Steven D'Aprano . -## -## Licensed under the Apache License, Version 2.0 (the "License"); -## you may not use this file except in compliance with the License. -## You may obtain a copy of the License at -## -## http://www.apache.org/licenses/LICENSE-2.0 -## -## Unless required by applicable law or agreed to in writing, software -## distributed under the License is distributed on an "AS IS" BASIS, -## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -## See the License for the specific language governing permissions and -## limitations under the License. - - """ Basic statistics module. -- cgit v1.2.1 From d1bb5bc3660b73f9912653e45855f25c3b9fca97 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 23 Aug 2016 10:47:07 -0700 Subject: Issue 27598: Add Collections to collections.abc. Patch by Ivan Levkivskyi, docs by Neil Girdhar. --- Lib/_collections_abc.py | 17 +++++++-- Lib/test/test_collections.py | 90 +++++++++++++++++++++++++++++++++++++++++++- Lib/test/test_functools.py | 17 +++++---- 3 files changed, 112 insertions(+), 12 deletions(-) (limited to 'Lib') diff --git a/Lib/_collections_abc.py b/Lib/_collections_abc.py index 077bde4d21..f035970a8e 100644 --- a/Lib/_collections_abc.py +++ b/Lib/_collections_abc.py @@ -11,7 +11,7 @@ import sys __all__ = ["Awaitable", "Coroutine", "AsyncIterable", "AsyncIterator", "Hashable", "Iterable", "Iterator", "Generator", "Reversible", - "Sized", "Container", "Callable", + "Sized", "Container", "Callable", "Collection", "Set", "MutableSet", "Mapping", "MutableMapping", "MappingView", "KeysView", "ItemsView", "ValuesView", @@ -326,6 +326,15 @@ class Container(metaclass=ABCMeta): return _check_methods(C, "__contains__") return NotImplemented +class Collection(Sized, Iterable, Container): + + __slots__ = () + + @classmethod + def __subclasshook__(cls, C): + if cls is Collection: + return _check_methods(C, "__len__", "__iter__", "__contains__") + return NotImplemented class Callable(metaclass=ABCMeta): @@ -345,7 +354,7 @@ class Callable(metaclass=ABCMeta): ### SETS ### -class Set(Sized, Iterable, Container): +class Set(Collection): """A set is a finite, iterable container. @@ -570,7 +579,7 @@ MutableSet.register(set) ### MAPPINGS ### -class Mapping(Sized, Iterable, Container): +class Mapping(Collection): __slots__ = () @@ -794,7 +803,7 @@ MutableMapping.register(dict) ### SEQUENCES ### -class Sequence(Sized, Reversible, Container): +class Sequence(Reversible, Collection): """All the operations on a read-only sequence. diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index 6e858c0d09..f1fb011266 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -21,7 +21,7 @@ from collections import ChainMap from collections import deque from collections.abc import Awaitable, Coroutine, AsyncIterator, AsyncIterable from collections.abc import Hashable, Iterable, Iterator, Generator, Reversible -from collections.abc import Sized, Container, Callable +from collections.abc import Sized, Container, Callable, Collection from collections.abc import Set, MutableSet from collections.abc import Mapping, MutableMapping, KeysView, ItemsView, ValuesView from collections.abc import Sequence, MutableSequence @@ -771,6 +771,94 @@ class TestOneTrickPonyABCs(ABCTestCase): self.assertFalse(issubclass(RevRevBlocked, Reversible)) self.assertFalse(isinstance(RevRevBlocked(), Reversible)) + def test_Collection(self): + # Check some non-collections + non_collections = [None, 42, 3.14, 1j, lambda x: 2*x] + for x in non_collections: + self.assertNotIsInstance(x, Collection) + self.assertFalse(issubclass(type(x), Collection), repr(type(x))) + # Check some non-collection iterables + non_col_iterables = [_test_gen(), iter(b''), iter(bytearray()), + (x for x in []), dict().values()] + for x in non_col_iterables: + self.assertNotIsInstance(x, Collection) + self.assertFalse(issubclass(type(x), Collection), repr(type(x))) + # Check some collections + samples = [set(), frozenset(), dict(), bytes(), str(), tuple(), + list(), dict().keys(), dict().items()] + for x in samples: + self.assertIsInstance(x, Collection) + self.assertTrue(issubclass(type(x), Collection), repr(type(x))) + # Check also Mapping, MutableMapping, etc. + self.assertTrue(issubclass(Sequence, Collection), repr(Sequence)) + self.assertTrue(issubclass(Mapping, Collection), repr(Mapping)) + self.assertTrue(issubclass(MutableMapping, Collection), + repr(MutableMapping)) + self.assertTrue(issubclass(Set, Collection), repr(Set)) + self.assertTrue(issubclass(MutableSet, Collection), repr(MutableSet)) + self.assertTrue(issubclass(Sequence, Collection), repr(MutableSet)) + # Check direct subclassing + class Col(Collection): + def __iter__(self): + return iter(list()) + def __len__(self): + return 0 + def __contains__(self, item): + return False + class DerCol(Col): pass + self.assertEqual(list(iter(Col())), []) + self.assertFalse(issubclass(list, Col)) + self.assertFalse(issubclass(set, Col)) + self.assertFalse(issubclass(float, Col)) + self.assertEqual(list(iter(DerCol())), []) + self.assertFalse(issubclass(list, DerCol)) + self.assertFalse(issubclass(set, DerCol)) + self.assertFalse(issubclass(float, DerCol)) + self.validate_abstract_methods(Collection, '__len__', '__iter__', + '__contains__') + # Check sized container non-iterable (which is not Collection) etc. + class ColNoIter: + def __len__(self): return 0 + def __contains__(self, item): return False + class ColNoSize: + def __iter__(self): return iter([]) + def __contains__(self, item): return False + class ColNoCont: + def __iter__(self): return iter([]) + def __len__(self): return 0 + self.assertFalse(issubclass(ColNoIter, Collection)) + self.assertFalse(isinstance(ColNoIter(), Collection)) + self.assertFalse(issubclass(ColNoSize, Collection)) + self.assertFalse(isinstance(ColNoSize(), Collection)) + self.assertFalse(issubclass(ColNoCont, Collection)) + self.assertFalse(isinstance(ColNoCont(), Collection)) + # Check None blocking + class SizeBlock: + def __iter__(self): return iter([]) + def __contains__(self): return False + __len__ = None + class IterBlock: + def __len__(self): return 0 + def __contains__(self): return True + __iter__ = None + self.assertFalse(issubclass(SizeBlock, Collection)) + self.assertFalse(isinstance(SizeBlock(), Collection)) + self.assertFalse(issubclass(IterBlock, Collection)) + self.assertFalse(isinstance(IterBlock(), Collection)) + # Check None blocking in subclass + class ColImpl: + def __iter__(self): + return iter(list()) + def __len__(self): + return 0 + def __contains__(self, item): + return False + class NonCol(ColImpl): + __contains__ = None + self.assertFalse(issubclass(NonCol, Collection)) + self.assertFalse(isinstance(NonCol(), Collection)) + + def test_Iterator(self): non_samples = [None, 42, 3.14, 1j, b"", "", (), [], {}, set()] for x in non_samples: diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index 06eacfb97d..40f2234a7f 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -1548,13 +1548,15 @@ class TestSingleDispatch(unittest.TestCase): bases = [c.Sequence, c.MutableMapping, c.Mapping, c.Set] for haystack in permutations(bases): m = mro(dict, haystack) - self.assertEqual(m, [dict, c.MutableMapping, c.Mapping, c.Sized, - c.Iterable, c.Container, object]) + self.assertEqual(m, [dict, c.MutableMapping, c.Mapping, + c.Collection, c.Sized, c.Iterable, + c.Container, object]) bases = [c.Container, c.Mapping, c.MutableMapping, c.OrderedDict] for haystack in permutations(bases): m = mro(c.ChainMap, haystack) self.assertEqual(m, [c.ChainMap, c.MutableMapping, c.Mapping, - c.Sized, c.Iterable, c.Container, object]) + c.Collection, c.Sized, c.Iterable, + c.Container, object]) # If there's a generic function with implementations registered for # both Sized and Container, passing a defaultdict to it results in an @@ -1575,9 +1577,9 @@ class TestSingleDispatch(unittest.TestCase): bases = [c.MutableSequence, c.MutableMapping] for haystack in permutations(bases): m = mro(D, bases) - self.assertEqual(m, [D, c.MutableSequence, c.Sequence, - c.defaultdict, dict, c.MutableMapping, - c.Mapping, c.Sized, c.Reversible, c.Iterable, c.Container, + self.assertEqual(m, [D, c.MutableSequence, c.Sequence, c.Reversible, + c.defaultdict, dict, c.MutableMapping, c.Mapping, + c.Collection, c.Sized, c.Iterable, c.Container, object]) # Container and Callable are registered on different base classes and @@ -1590,7 +1592,8 @@ class TestSingleDispatch(unittest.TestCase): for haystack in permutations(bases): m = mro(C, haystack) self.assertEqual(m, [C, c.Callable, c.defaultdict, dict, c.Mapping, - c.Sized, c.Iterable, c.Container, object]) + c.Collection, c.Sized, c.Iterable, + c.Container, object]) def test_register_abc(self): c = collections -- cgit v1.2.1 From 3e7ac560d792f8b7952a5c3f9f150d12baa8a4ca Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Tue, 23 Aug 2016 14:20:37 -0400 Subject: Issue #27787: No longer call deleted test_main(). --- Lib/test/test_httplib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index 5c500cbc46..1768a34308 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -1713,4 +1713,4 @@ class TunnelTests(TestCase): if __name__ == '__main__': - test_main() + unittest.main(verbosity=2) -- cgit v1.2.1 From 9313084dbc03dacc0d93b8b04300b2f252190aca Mon Sep 17 00:00:00 2001 From: Alexander Belopolsky Date: Tue, 23 Aug 2016 14:44:51 -0400 Subject: Issue #27834: Avoid overflow error in ZoneInfo.invert(). --- Lib/test/datetimetester.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Lib') diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index a0583072d9..e21d487a12 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -4477,11 +4477,11 @@ class ZoneInfo(tzinfo): @staticmethod def invert(ut, ti): - lt = (ut.__copy__(), ut.__copy__()) + lt = (array('q', ut), array('q', ut)) if ut: offset = ti[0][0] // SEC - lt[0][0] = max(-2**31, lt[0][0] + offset) - lt[1][0] = max(-2**31, lt[1][0] + offset) + lt[0][0] += offset + lt[1][0] += offset for i in range(1, len(ut)): lt[0][i] += ti[i-1][0] // SEC lt[1][i] += ti[i][0] // SEC -- cgit v1.2.1 From 16eb9944497ad751f8cb55c8e6f56607fe1b07d7 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Tue, 23 Aug 2016 20:00:49 +0100 Subject: Backed out changeset 1017215f5492 --- Lib/test/cmath_testcases.txt | 141 ------------------------------------------- 1 file changed, 141 deletions(-) (limited to 'Lib') diff --git a/Lib/test/cmath_testcases.txt b/Lib/test/cmath_testcases.txt index dd7e458ddc..9b0865302e 100644 --- a/Lib/test/cmath_testcases.txt +++ b/Lib/test/cmath_testcases.txt @@ -53,12 +53,6 @@ -- MPFR homepage at http://www.mpfr.org for more information about the -- MPFR project. --- A minority of the test cases were generated with the help of --- mpmath 0.19 at 100 bit accuracy (http://mpmath.org) to improve --- coverage of real functions with real-valued arguments. These are --- used in test.test_math.MathTests.test_testfile, as well as in --- test_cmath. - -------------------------- -- acos: Inverse cosine -- @@ -854,18 +848,6 @@ atan0302 atan 9.9999999999999999e-161 -1.0 -> 0.78539816339744828 -184.553381029 atan0303 atan -1e-165 1.0 -> -0.78539816339744828 190.30984376228875 atan0304 atan -9.9998886718268301e-321 -1.0 -> -0.78539816339744828 -368.76019403576692 --- Additional real values (mpmath) -atan0400 atan 1.7976931348623157e+308 0.0 -> 1.5707963267948966192 0.0 -atan0401 atan -1.7976931348623157e+308 0.0 -> -1.5707963267948966192 0.0 -atan0402 atan 1e-17 0.0 -> 1.0000000000000000715e-17 0.0 -atan0403 atan -1e-17 0.0 -> -1.0000000000000000715e-17 0.0 -atan0404 atan 0.0001 0.0 -> 0.000099999999666666673459 0.0 -atan0405 atan -0.0001 0.0 -> -0.000099999999666666673459 0.0 -atan0406 atan 0.999999999999999 0.0 -> 0.78539816339744781002 0.0 -atan0407 atan 1.000000000000001 0.0 -> 0.78539816339744886473 0.0 -atan0408 atan 14.101419947171719 0.0 -> 1.4999999999999999969 0.0 -atan0409 atan 1255.7655915007897 0.0 -> 1.5700000000000000622 0.0 - -- special values atan1000 atan -0.0 0.0 -> -0.0 0.0 atan1001 atan nan 0.0 -> nan 0.0 @@ -1532,11 +1514,6 @@ sqrt0131 sqrt -1.5477066694467245e-310 -0.0 -> 0.0 -1.2440685951533077e-155 sqrt0140 sqrt 1.6999999999999999e+308 -1.6999999999999999e+308 -> 1.4325088230154573e+154 -5.9336458271212207e+153 sqrt0141 sqrt -1.797e+308 -9.9999999999999999e+306 -> 3.7284476432057307e+152 -1.3410406899802901e+154 --- Additional real values (mpmath) -sqrt0150 sqrt 1.7976931348623157e+308 0.0 -> 1.3407807929942596355e+154 0.0 -sqrt0151 sqrt 2.2250738585072014e-308 0.0 -> 1.4916681462400413487e-154 0.0 -sqrt0152 sqrt 5e-324 0.0 -> 2.2227587494850774834e-162 0.0 - -- special values sqrt1000 sqrt 0.0 0.0 -> 0.0 0.0 sqrt1001 sqrt -0.0 0.0 -> 0.0 0.0 @@ -1639,20 +1616,6 @@ exp0052 exp 710.0 1.5 -> 1.5802653829857376e+307 inf overflow exp0053 exp 710.0 1.6 -> -6.5231579995501372e+306 inf overflow exp0054 exp 710.0 2.8 -> -inf 7.4836177417448528e+307 overflow --- Additional real values (mpmath) -exp0070 exp 1e-08 0.0 -> 1.00000001000000005 0.0 -exp0071 exp 0.0003 0.0 -> 1.0003000450045003375 0.0 -exp0072 exp 0.2 0.0 -> 1.2214027581601698475 0.0 -exp0073 exp 1.0 0.0 -> 2.7182818284590452354 0.0 -exp0074 exp -1e-08 0.0 -> 0.99999999000000005 0.0 -exp0075 exp -0.0003 0.0 -> 0.99970004499550033751 0.0 -exp0076 exp -1.0 0.0 -> 0.3678794411714423216 0.0 -exp0077 exp 2.220446049250313e-16 0.0 -> 1.000000000000000222 0.0 -exp0078 exp -1.1102230246251565e-16 0.0 -> 0.99999999999999988898 0.0 -exp0079 exp 2.302585092994046 0.0 -> 10.000000000000002171 0.0 -exp0080 exp -2.302585092994046 0.0 -> 0.099999999999999978292 0.0 -exp0081 exp 709.7827 0.0 -> 1.7976699566638014654e+308 0.0 - -- special values exp1000 exp 0.0 0.0 -> 1.0 0.0 exp1001 exp -0.0 0.0 -> 1.0 0.0 @@ -1745,23 +1708,6 @@ cosh0023 cosh 2.218885944363501 2.0015727395883687 -> -1.94294321081968 4.129026 cosh0030 cosh 710.5 2.3519999999999999 -> -1.2967465239355998e+308 1.3076707908857333e+308 cosh0031 cosh -710.5 0.69999999999999996 -> 1.4085466381392499e+308 -1.1864024666450239e+308 --- Additional real values (mpmath) -cosh0050 cosh 1e-150 0.0 -> 1.0 0.0 -cosh0051 cosh 1e-18 0.0 -> 1.0 0.0 -cosh0052 cosh 1e-09 0.0 -> 1.0000000000000000005 0.0 -cosh0053 cosh 0.0003 0.0 -> 1.0000000450000003375 0.0 -cosh0054 cosh 0.2 0.0 -> 1.0200667556190758485 0.0 -cosh0055 cosh 1.0 0.0 -> 1.5430806348152437785 0.0 -cosh0056 cosh -1e-18 0.0 -> 1.0 -0.0 -cosh0057 cosh -0.0003 0.0 -> 1.0000000450000003375 -0.0 -cosh0058 cosh -1.0 0.0 -> 1.5430806348152437785 -0.0 -cosh0059 cosh 1.3169578969248168 0.0 -> 2.0000000000000001504 0.0 -cosh0060 cosh -1.3169578969248168 0.0 -> 2.0000000000000001504 -0.0 -cosh0061 cosh 17.328679513998633 0.0 -> 16777216.000000021938 0.0 -cosh0062 cosh 18.714973875118524 0.0 -> 67108864.000000043662 0.0 -cosh0063 cosh 709.7827 0.0 -> 8.9883497833190073272e+307 0.0 -cosh0064 cosh -709.7827 0.0 -> 8.9883497833190073272e+307 -0.0 - -- special values cosh1000 cosh 0.0 0.0 -> 1.0 0.0 cosh1001 cosh 0.0 inf -> nan 0.0 invalid ignore-imag-sign @@ -1854,24 +1800,6 @@ sinh0023 sinh 0.043713693678420068 0.22512549887532657 -> 0.042624198673416713 0 sinh0030 sinh 710.5 -2.3999999999999999 -> -1.3579970564885919e+308 -1.24394470907798e+308 sinh0031 sinh -710.5 0.80000000000000004 -> -1.2830671601735164e+308 1.3210954193997678e+308 --- Additional real values (mpmath) -sinh0050 sinh 1e-100 0.0 -> 1.00000000000000002e-100 0.0 -sinh0051 sinh 5e-17 0.0 -> 4.9999999999999998955e-17 0.0 -sinh0052 sinh 1e-16 0.0 -> 9.999999999999999791e-17 0.0 -sinh0053 sinh 3.7e-08 0.0 -> 3.7000000000000008885e-8 0.0 -sinh0054 sinh 0.001 0.0 -> 0.0010000001666666750208 0.0 -sinh0055 sinh 0.2 0.0 -> 0.20133600254109399895 0.0 -sinh0056 sinh 1.0 0.0 -> 1.1752011936438014569 0.0 -sinh0057 sinh -3.7e-08 0.0 -> -3.7000000000000008885e-8 0.0 -sinh0058 sinh -0.001 0.0 -> -0.0010000001666666750208 0.0 -sinh0059 sinh -1.0 0.0 -> -1.1752011936438014569 0.0 -sinh0060 sinh 1.4436354751788103 0.0 -> 1.9999999999999999078 0.0 -sinh0061 sinh -1.4436354751788103 0.0 -> -1.9999999999999999078 0.0 -sinh0062 sinh 17.328679513998633 0.0 -> 16777215.999999992136 0.0 -sinh0063 sinh 18.714973875118524 0.0 -> 67108864.000000036211 0.0 -sinh0064 sinh 709.7827 0.0 -> 8.9883497833190073272e+307 0.0 -sinh0065 sinh -709.7827 0.0 -> -8.9883497833190073272e+307 0.0 - -- special values sinh1000 sinh 0.0 0.0 -> 0.0 0.0 sinh1001 sinh 0.0 inf -> 0.0 nan invalid ignore-real-sign @@ -1969,24 +1897,6 @@ tanh0031 tanh -711 7.4000000000000004 -> -1.0 0.0 tanh0032 tanh 1000 -2.3199999999999998 -> 1.0 0.0 tanh0033 tanh -1.0000000000000001e+300 -9.6699999999999999 -> -1.0 -0.0 --- Additional real values (mpmath) -tanh0050 tanh 1e-100 0.0 -> 1.00000000000000002e-100 0.0 -tanh0051 tanh 5e-17 0.0 -> 4.9999999999999998955e-17 0.0 -tanh0052 tanh 1e-16 0.0 -> 9.999999999999999791e-17 0.0 -tanh0053 tanh 3.7e-08 0.0 -> 3.6999999999999983559e-8 0.0 -tanh0054 tanh 0.001 0.0 -> 0.00099999966666680002076 0.0 -tanh0055 tanh 0.2 0.0 -> 0.19737532022490401141 0.0 -tanh0056 tanh 1.0 0.0 -> 0.76159415595576488812 0.0 -tanh0057 tanh -3.7e-08 0.0 -> -3.6999999999999983559e-8 0.0 -tanh0058 tanh -0.001 0.0 -> -0.00099999966666680002076 0.0 -tanh0059 tanh -1.0 0.0 -> -0.76159415595576488812 0.0 -tanh0060 tanh 0.5493061443340549 0.0 -> 0.50000000000000003402 0.0 -tanh0061 tanh -0.5493061443340549 0.0 -> -0.50000000000000003402 0.0 -tanh0062 tanh 17.328679513998633 0.0 -> 0.99999999999999822364 0.0 -tanh0063 tanh 18.714973875118524 0.0 -> 0.99999999999999988898 0.0 -tanh0064 tanh 711 0.0 -> 1.0 0.0 -tanh0065 tanh 1.797e+308 0.0 -> 1.0 0.0 - --special values tanh1000 tanh 0.0 0.0 -> 0.0 0.0 tanh1001 tanh 0.0 inf -> nan nan invalid @@ -2075,22 +1985,6 @@ cos0021 cos 4.8048375263775256 0.0062248852898515658 -> 0.092318702015846243 0.0 cos0022 cos 7.9914515433858515 0.71659966615501436 -> -0.17375439906936566 -0.77217043527294582 cos0023 cos 0.45124351152540226 1.6992693993812158 -> 2.543477948972237 -1.1528193694875477 --- Additional real values (mpmath) -cos0050 cos 1e-150 0.0 -> 1.0 -0.0 -cos0051 cos 1e-18 0.0 -> 1.0 -0.0 -cos0052 cos 1e-09 0.0 -> 0.9999999999999999995 -0.0 -cos0053 cos 0.0003 0.0 -> 0.9999999550000003375 -0.0 -cos0054 cos 0.2 0.0 -> 0.98006657784124162892 -0.0 -cos0055 cos 1.0 0.0 -> 0.5403023058681397174 -0.0 -cos0056 cos -1e-18 0.0 -> 1.0 0.0 -cos0057 cos -0.0003 0.0 -> 0.9999999550000003375 0.0 -cos0058 cos -1.0 0.0 -> 0.5403023058681397174 0.0 -cos0059 cos 1.0471975511965976 0.0 -> 0.50000000000000009945 -0.0 -cos0060 cos 2.5707963267948966 0.0 -> -0.84147098480789647357 -0.0 -cos0061 cos -2.5707963267948966 0.0 -> -0.84147098480789647357 0.0 -cos0062 cos 7.225663103256523 0.0 -> 0.58778525229247407559 -0.0 -cos0063 cos -8.79645943005142 0.0 -> -0.80901699437494722255 0.0 - -- special values cos1000 cos -0.0 0.0 -> 1.0 0.0 cos1001 cos -inf 0.0 -> nan 0.0 invalid ignore-imag-sign @@ -2179,22 +2073,6 @@ sin0021 sin 1.4342727387492671 0.81361889790284347 -> 1.3370960060947923 0.12336 sin0022 sin 1.1518087354403725 4.8597235966150558 -> 58.919141989603041 26.237003403758852 sin0023 sin 0.00087773078406649192 34.792379211312095 -> 565548145569.38245 644329685822700.62 --- Additional real values (mpmath) -sin0050 sin 1e-100 0.0 -> 1.00000000000000002e-100 0.0 -sin0051 sin 3.7e-08 0.0 -> 3.6999999999999992001e-8 0.0 -sin0052 sin 0.001 0.0 -> 0.00099999983333334168748 0.0 -sin0053 sin 0.2 0.0 -> 0.19866933079506122634 0.0 -sin0054 sin 1.0 0.0 -> 0.84147098480789650665 0.0 -sin0055 sin -3.7e-08 0.0 -> -3.6999999999999992001e-8 0.0 -sin0056 sin -0.001 0.0 -> -0.00099999983333334168748 0.0 -sin0057 sin -1.0 0.0 -> -0.84147098480789650665 0.0 -sin0058 sin 0.5235987755982989 0.0 -> 0.50000000000000004642 0.0 -sin0059 sin -0.5235987755982989 0.0 -> -0.50000000000000004642 0.0 -sin0060 sin 2.6179938779914944 0.0 -> 0.49999999999999996018 -0.0 -sin0061 sin -2.6179938779914944 0.0 -> -0.49999999999999996018 -0.0 -sin0062 sin 7.225663103256523 0.0 -> 0.80901699437494673648 0.0 -sin0063 sin -8.79645943005142 0.0 -> -0.58778525229247340658 -0.0 - -- special values sin1000 sin -0.0 0.0 -> -0.0 0.0 sin1001 sin -inf 0.0 -> nan 0.0 invalid ignore-imag-sign @@ -2283,25 +2161,6 @@ tan0021 tan 1.7809617968443272 1.5052381702853379 -> -0.044066222118946903 1.093 tan0022 tan 1.1615313900880577 1.7956298728647107 -> 0.041793186826390362 1.0375339546034792 tan0023 tan 0.067014779477908945 5.8517361577457097 -> 2.2088639754800034e-06 0.9999836182420061 --- Additional real values (mpmath) -tan0050 tan 1e-100 0.0 -> 1.00000000000000002e-100 0.0 -tan0051 tan 3.7e-08 0.0 -> 3.7000000000000017328e-8 0.0 -tan0052 tan 0.001 0.0 -> 0.0010000003333334666875 0.0 -tan0053 tan 0.2 0.0 -> 0.20271003550867249488 0.0 -tan0054 tan 1.0 0.0 -> 1.5574077246549022305 0.0 -tan0055 tan -3.7e-08 0.0 -> -3.7000000000000017328e-8 0.0 -tan0056 tan -0.001 0.0 -> -0.0010000003333334666875 0.0 -tan0057 tan -1.0 0.0 -> -1.5574077246549022305 0.0 -tan0058 tan 0.4636476090008061 0.0 -> 0.49999999999999997163 0.0 -tan0059 tan -0.4636476090008061 0.0 -> -0.49999999999999997163 0.0 -tan0060 tan 1.1071487177940904 0.0 -> 1.9999999999999995298 0.0 -tan0061 tan -1.1071487177940904 0.0 -> -1.9999999999999995298 0.0 -tan0062 tan 1.5 0.0 -> 14.101419947171719388 0.0 -tan0063 tan 1.57 0.0 -> 1255.7655915007896475 0.0 -tan0064 tan 1.5707963267948961 0.0 -> 1978937966095219.0538 0.0 -tan0065 tan 7.225663103256523 0.0 -> 1.3763819204711701522 0.0 -tan0066 tan -8.79645943005142 0.0 -> 0.7265425280053614098 0.0 - -- special values tan1000 tan -0.0 0.0 -> -0.0 0.0 tan1001 tan -inf 0.0 -> nan nan invalid -- cgit v1.2.1 From 7d657dde9695015f7fac5e13aaf09ed1845afd03 Mon Sep 17 00:00:00 2001 From: Steven D'Aprano Date: Wed, 24 Aug 2016 12:17:00 +1000 Subject: Add geometric_mean to __all__ --- Lib/statistics.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/statistics.py b/Lib/statistics.py index 17e471bb56..632127af4d 100644 --- a/Lib/statistics.py +++ b/Lib/statistics.py @@ -11,6 +11,7 @@ Calculating averages Function Description ================== ============================================= mean Arithmetic mean (average) of data. +geometric_mean Geometric mean of data. harmonic_mean Harmonic mean of data. median Median (middle value) of data. median_low Low median of data. @@ -79,7 +80,7 @@ A single exception is defined: StatisticsError is a subclass of ValueError. __all__ = [ 'StatisticsError', 'pstdev', 'pvariance', 'stdev', 'variance', 'median', 'median_low', 'median_high', 'median_grouped', - 'mean', 'mode', 'harmonic_mean', + 'mean', 'mode', 'geometric_mean', 'harmonic_mean', ] import collections -- cgit v1.2.1 From 4baf44dcbfef76dffdc0f49a660e04de1eeb16bf Mon Sep 17 00:00:00 2001 From: Steven D'Aprano Date: Wed, 24 Aug 2016 12:48:12 +1000 Subject: Remove support for nth root of negative numbers with odd powers. Although nth roots of negative numbers are real for odd n, the statistics module doesn't make use of this. Remove support for negative roots from the private _nth_root function, which simplifies the test suite. --- Lib/statistics.py | 7 +++---- Lib/test/test_statistics.py | 29 +++++------------------------ 2 files changed, 8 insertions(+), 28 deletions(-) (limited to 'Lib') diff --git a/Lib/statistics.py b/Lib/statistics.py index 632127af4d..40c72db0c0 100644 --- a/Lib/statistics.py +++ b/Lib/statistics.py @@ -335,10 +335,7 @@ class _nroot_NS: """Handle nth root of Reals, treated as a float.""" assert isinstance(n, int) and n > 1 if x < 0: - if n%2 == 0: - raise ValueError('domain error: even root of negative number') - else: - return -_nroot_NS.nroot(-x, n) + raise ValueError('domain error: root of negative number') elif x == 0: return math.copysign(0.0, x) elif x > 0: @@ -433,6 +430,8 @@ class _nroot_NS: else: # Preserve the input NAN. return x + if x < 0: + raise ValueError('domain error: root of negative number') if x.is_infinite(): return x # FIXME this hasn't had the extensive testing of the float diff --git a/Lib/test/test_statistics.py b/Lib/test/test_statistics.py index dff0cd4476..9443ff0c61 100644 --- a/Lib/test/test_statistics.py +++ b/Lib/test/test_statistics.py @@ -1095,13 +1095,6 @@ class Test_Nth_Root(NumericTestCase): with self.subTest(n=n, inf=INF): self.assertEqual(self.nroot(INF, n), INF) - def testNInf(self): - # Test that the root of -inf is -inf for odd n. - for NINF in (float('-inf'), decimal.Decimal('-inf')): - for n in range(3, 11, 2): - with self.subTest(n=n, inf=NINF): - self.assertEqual(self.nroot(NINF, n), NINF) - # FIXME: need to check Decimal zeroes too. def test_zero(self): # Test that the root of +0.0 is +0.0. @@ -1157,13 +1150,15 @@ class Test_Nth_Root(NumericTestCase): with self.subTest(x=x): self.assertRaises(TypeError, self.nroot, x, 3) - def testNegativeEvenPower(self): - # Test negative x with even n raises correctly. + def testNegativeError(self): + # Test negative x raises correctly. x = random.uniform(-20.0, -0.1) assert x < 0 - for n in range(2, 9, 2): + for n in range(3, 7): with self.subTest(x=x, n=n): self.assertRaises(ValueError, self.nroot, x, n) + # And Decimal. + self.assertRaises(ValueError, self.nroot, Decimal(-27), 3) # --- Test that nroot is never worse than calling math.pow() --- @@ -1216,25 +1211,11 @@ class Test_Nth_Root(NumericTestCase): x = i**n self.assertEqual(self.nroot(x, n), i) - def testExactPowersNegatives(self): - # Test that small negative integer powers are calculated exactly. - for i in range(-1, -51, -1): - for n in range(3, 16, 2): - if (i, n) == (-35, 13): - # See testExpectedFailure35p13 - continue - with self.subTest(i=i, n=n): - x = i**n - assert sign(x) == -1 - self.assertEqual(self.nroot(x, n), i) - def testExpectedFailure35p13(self): # Test the expected failure 35**13 is almost exact. x = 35**13 err = abs(self.nroot(x, 13) - 35) self.assertLessEqual(err, 0.000000001) - err = abs(self.nroot(-x, 13) + 35) - self.assertLessEqual(err, 0.000000001) def testOne(self): # Test that the root of 1.0 is 1.0. -- cgit v1.2.1 From 0d689ad264ea85f6b557cade826f4405c9eeba75 Mon Sep 17 00:00:00 2001 From: Steven D'Aprano Date: Wed, 24 Aug 2016 13:54:31 +1000 Subject: Remove expected failure from test of _product internal function. --- Lib/test/test_statistics.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_statistics.py b/Lib/test/test_statistics.py index 9443ff0c61..6cac7095c2 100644 --- a/Lib/test/test_statistics.py +++ b/Lib/test/test_statistics.py @@ -1041,11 +1041,11 @@ class Test_Product(NumericTestCase): self.assertEqual(mant, F(10)) self.assertTrue(isinstance(mant, F)) - @unittest.expectedFailure def test_decimal(self): D = Decimal data = [D('24.5'), D('17.6'), D('0.025'), D('1.3')] - assert False + expected = D('14.014000') + self.assertEqual(statistics._product(data), (0, expected)) def test_mixed_decimal_float(self): # Test that mixed Decimal and float raises. -- cgit v1.2.1 From 1d17079876f2e0b64a048eee2c1d11f1b4f7815e Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Wed, 24 Aug 2016 06:33:33 +0000 Subject: Issue #12319: Support for chunked encoding of HTTP request bodies When the body object is a file, its size is no longer determined with fstat(), since that can report the wrong result (e.g. reading from a pipe). Instead, determine the size using seek(), or fall back to chunked encoding for unseekable files. Also, change the logic for detecting text files to check for TextIOBase inheritance, rather than inspecting the ?mode? attribute, which may not exist (e.g. BytesIO and StringIO). The Content-Length for text files is no longer determined ahead of time, because the original logic could have been wrong depending on the codec and newline translation settings. Patch by Demian Brecht and Rolf Krahl, with a few tweaks by me. --- Lib/http/client.py | 199 +++++++++++++++++++++++++++++++++++------------ Lib/test/test_httplib.py | 151 ++++++++++++++++++++++++++++++++++- Lib/test/test_urllib2.py | 103 ++++++++++++++++++------ Lib/urllib/request.py | 42 +++++----- 4 files changed, 396 insertions(+), 99 deletions(-) (limited to 'Lib') diff --git a/Lib/http/client.py b/Lib/http/client.py index 763e1ef4f6..b242ba6559 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -795,6 +795,58 @@ class HTTPConnection: auto_open = 1 debuglevel = 0 + @staticmethod + def _is_textIO(stream): + """Test whether a file-like object is a text or a binary stream. + """ + return isinstance(stream, io.TextIOBase) + + @staticmethod + def _get_content_length(body, method): + """Get the content-length based on the body. + + If the body is "empty", we set Content-Length: 0 for methods + that expect a body (RFC 7230, Section 3.3.2). If the body is + set for other methods, we set the header provided we can + figure out what the length is. + """ + if not body: + # do an explicit check for not None here to distinguish + # between unset and set but empty + if method.upper() in _METHODS_EXPECTING_BODY or body is not None: + return 0 + else: + return None + + if hasattr(body, 'read'): + # file-like object. + if HTTPConnection._is_textIO(body): + # text streams are unpredictable because it depends on + # character encoding and line ending translation. + return None + else: + # Is it seekable? + try: + curpos = body.tell() + sz = body.seek(0, io.SEEK_END) + except (TypeError, AttributeError, OSError): + return None + else: + body.seek(curpos) + return sz - curpos + + try: + # does it implement the buffer protocol (bytes, bytearray, array)? + mv = memoryview(body) + return mv.nbytes + except TypeError: + pass + + if isinstance(body, str): + return len(body) + + return None + def __init__(self, host, port=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, source_address=None): self.timeout = timeout @@ -933,18 +985,9 @@ class HTTPConnection: if hasattr(data, "read") : if self.debuglevel > 0: print("sendIng a read()able") - encode = False - try: - mode = data.mode - except AttributeError: - # io.BytesIO and other file-like objects don't have a `mode` - # attribute. - pass - else: - if "b" not in mode: - encode = True - if self.debuglevel > 0: - print("encoding file using iso-8859-1") + encode = self._is_textIO(data) + if encode and self.debuglevel > 0: + print("encoding file using iso-8859-1") while 1: datablock = data.read(blocksize) if not datablock: @@ -970,7 +1013,22 @@ class HTTPConnection: """ self._buffer.append(s) - def _send_output(self, message_body=None): + def _read_readable(self, readable): + blocksize = 8192 + if self.debuglevel > 0: + print("sendIng a read()able") + encode = self._is_textIO(readable) + if encode and self.debuglevel > 0: + print("encoding file using iso-8859-1") + while True: + datablock = readable.read(blocksize) + if not datablock: + break + if encode: + datablock = datablock.encode("iso-8859-1") + yield datablock + + def _send_output(self, message_body=None, encode_chunked=False): """Send the currently buffered request and clear the buffer. Appends an extra \\r\\n to the buffer. @@ -979,10 +1037,50 @@ class HTTPConnection: self._buffer.extend((b"", b"")) msg = b"\r\n".join(self._buffer) del self._buffer[:] - self.send(msg) + if message_body is not None: - self.send(message_body) + + # create a consistent interface to message_body + if hasattr(message_body, 'read'): + # Let file-like take precedence over byte-like. This + # is needed to allow the current position of mmap'ed + # files to be taken into account. + chunks = self._read_readable(message_body) + else: + try: + # this is solely to check to see if message_body + # implements the buffer API. it /would/ be easier + # to capture if PyObject_CheckBuffer was exposed + # to Python. + memoryview(message_body) + except TypeError: + try: + chunks = iter(message_body) + except TypeError: + raise TypeError("message_body should be a bytes-like " + "object or an iterable, got %r" + % type(message_body)) + else: + # the object implements the buffer interface and + # can be passed directly into socket methods + chunks = (message_body,) + + for chunk in chunks: + if not chunk: + if self.debuglevel > 0: + print('Zero length chunk ignored') + continue + + if encode_chunked and self._http_vsn == 11: + # chunked encoding + chunk = f'{len(chunk):X}\r\n'.encode('ascii') + chunk \ + + b'\r\n' + self.send(chunk) + + if encode_chunked and self._http_vsn == 11: + # end chunked transfer + self.send(b'0\r\n\r\n') def putrequest(self, method, url, skip_host=0, skip_accept_encoding=0): """Send a request to the server. @@ -1135,52 +1233,27 @@ class HTTPConnection: header = header + b': ' + value self._output(header) - def endheaders(self, message_body=None): + def endheaders(self, message_body=None, *, encode_chunked=False): """Indicate that the last header line has been sent to the server. This method sends the request to the server. The optional message_body argument can be used to pass a message body associated with the - request. The message body will be sent in the same packet as the - message headers if it is a string, otherwise it is sent as a separate - packet. + request. """ if self.__state == _CS_REQ_STARTED: self.__state = _CS_REQ_SENT else: raise CannotSendHeader() - self._send_output(message_body) + self._send_output(message_body, encode_chunked=encode_chunked) - def request(self, method, url, body=None, headers={}): + def request(self, method, url, body=None, headers={}, *, + encode_chunked=False): """Send a complete request to the server.""" - self._send_request(method, url, body, headers) - - def _set_content_length(self, body, method): - # Set the content-length based on the body. If the body is "empty", we - # set Content-Length: 0 for methods that expect a body (RFC 7230, - # Section 3.3.2). If the body is set for other methods, we set the - # header provided we can figure out what the length is. - thelen = None - method_expects_body = method.upper() in _METHODS_EXPECTING_BODY - if body is None and method_expects_body: - thelen = '0' - elif body is not None: - try: - thelen = str(len(body)) - except TypeError: - # If this is a file-like object, try to - # fstat its file descriptor - try: - thelen = str(os.fstat(body.fileno()).st_size) - except (AttributeError, OSError): - # Don't send a length if this failed - if self.debuglevel > 0: print("Cannot stat!!") + self._send_request(method, url, body, headers, encode_chunked) - if thelen is not None: - self.putheader('Content-Length', thelen) - - def _send_request(self, method, url, body, headers): + def _send_request(self, method, url, body, headers, encode_chunked): # Honor explicitly requested Host: and Accept-Encoding: headers. - header_names = dict.fromkeys([k.lower() for k in headers]) + header_names = frozenset(k.lower() for k in headers) skips = {} if 'host' in header_names: skips['skip_host'] = 1 @@ -1189,15 +1262,41 @@ class HTTPConnection: self.putrequest(method, url, **skips) + # chunked encoding will happen if HTTP/1.1 is used and either + # the caller passes encode_chunked=True or the following + # conditions hold: + # 1. content-length has not been explicitly set + # 2. the length of the body cannot be determined + # (e.g. it is a generator or unseekable file) + # 3. Transfer-Encoding has NOT been explicitly set by the caller + if 'content-length' not in header_names: - self._set_content_length(body, method) + # only chunk body if not explicitly set for backwards + # compatibility, assuming the client code is already handling the + # chunking + if 'transfer-encoding' not in header_names: + # if content-length cannot be automatically determined, fall + # back to chunked encoding + encode_chunked = False + content_length = self._get_content_length(body, method) + if content_length is None: + if body: + if self.debuglevel > 0: + print('Unable to determine size of %r' % body) + encode_chunked = True + self.putheader('Transfer-Encoding', 'chunked') + else: + self.putheader('Content-Length', str(content_length)) + else: + encode_chunked = False + for hdr, value in headers.items(): self.putheader(hdr, value) if isinstance(body, str): # RFC 2616 Section 3.7.1 says that text default has a # default charset of iso-8859-1. body = _encode(body, 'body') - self.endheaders(body) + self.endheaders(body, encode_chunked=encode_chunked) def getresponse(self): """Get the response from the server. diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index 1768a34308..a1796123e4 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -314,6 +314,124 @@ class HeaderTests(TestCase): conn.putheader(name, value) +class TransferEncodingTest(TestCase): + expected_body = b"It's just a flesh wound" + + def test_endheaders_chunked(self): + conn = client.HTTPConnection('example.com') + conn.sock = FakeSocket(b'') + conn.putrequest('POST', '/') + conn.endheaders(self._make_body(), encode_chunked=True) + + _, _, body = self._parse_request(conn.sock.data) + body = self._parse_chunked(body) + self.assertEqual(body, self.expected_body) + + def test_explicit_headers(self): + # explicit chunked + conn = client.HTTPConnection('example.com') + conn.sock = FakeSocket(b'') + # this shouldn't actually be automatically chunk-encoded because the + # calling code has explicitly stated that it's taking care of it + conn.request( + 'POST', '/', self._make_body(), {'Transfer-Encoding': 'chunked'}) + + _, headers, body = self._parse_request(conn.sock.data) + self.assertNotIn('content-length', [k.lower() for k in headers.keys()]) + self.assertEqual(headers['Transfer-Encoding'], 'chunked') + self.assertEqual(body, self.expected_body) + + # explicit chunked, string body + conn = client.HTTPConnection('example.com') + conn.sock = FakeSocket(b'') + conn.request( + 'POST', '/', self.expected_body.decode('latin-1'), + {'Transfer-Encoding': 'chunked'}) + + _, headers, body = self._parse_request(conn.sock.data) + self.assertNotIn('content-length', [k.lower() for k in headers.keys()]) + self.assertEqual(headers['Transfer-Encoding'], 'chunked') + self.assertEqual(body, self.expected_body) + + # User-specified TE, but request() does the chunk encoding + conn = client.HTTPConnection('example.com') + conn.sock = FakeSocket(b'') + conn.request('POST', '/', + headers={'Transfer-Encoding': 'gzip, chunked'}, + encode_chunked=True, + body=self._make_body()) + _, headers, body = self._parse_request(conn.sock.data) + self.assertNotIn('content-length', [k.lower() for k in headers]) + self.assertEqual(headers['Transfer-Encoding'], 'gzip, chunked') + self.assertEqual(self._parse_chunked(body), self.expected_body) + + def test_request(self): + for empty_lines in (False, True,): + conn = client.HTTPConnection('example.com') + conn.sock = FakeSocket(b'') + conn.request( + 'POST', '/', self._make_body(empty_lines=empty_lines)) + + _, headers, body = self._parse_request(conn.sock.data) + body = self._parse_chunked(body) + self.assertEqual(body, self.expected_body) + self.assertEqual(headers['Transfer-Encoding'], 'chunked') + + # Content-Length and Transfer-Encoding SHOULD not be sent in the + # same request + self.assertNotIn('content-length', [k.lower() for k in headers]) + + def _make_body(self, empty_lines=False): + lines = self.expected_body.split(b' ') + for idx, line in enumerate(lines): + # for testing handling empty lines + if empty_lines and idx % 2: + yield b'' + if idx < len(lines) - 1: + yield line + b' ' + else: + yield line + + def _parse_request(self, data): + lines = data.split(b'\r\n') + request = lines[0] + headers = {} + n = 1 + while n < len(lines) and len(lines[n]) > 0: + key, val = lines[n].split(b':') + key = key.decode('latin-1').strip() + headers[key] = val.decode('latin-1').strip() + n += 1 + + return request, headers, b'\r\n'.join(lines[n + 1:]) + + def _parse_chunked(self, data): + body = [] + trailers = {} + n = 0 + lines = data.split(b'\r\n') + # parse body + while True: + size, chunk = lines[n:n+2] + size = int(size, 16) + + if size == 0: + n += 1 + break + + self.assertEqual(size, len(chunk)) + body.append(chunk) + + n += 2 + # we /should/ hit the end chunk, but check against the size of + # lines so we're not stuck in an infinite loop should we get + # malformed data + if n > len(lines): + break + + return b''.join(body) + + class BasicTest(TestCase): def test_status_lines(self): # Test HTTP status lines @@ -564,11 +682,11 @@ class BasicTest(TestCase): yield None yield 'data_two' - class UpdatingFile(): + class UpdatingFile(io.TextIOBase): mode = 'r' d = data() def read(self, blocksize=-1): - return self.d.__next__() + return next(self.d) expected = b'data' @@ -1546,6 +1664,26 @@ class RequestBodyTest(TestCase): message = client.parse_headers(f) return message, f + def test_list_body(self): + # Note that no content-length is automatically calculated for + # an iterable. The request will fall back to send chunked + # transfer encoding. + cases = ( + ([b'foo', b'bar'], b'3\r\nfoo\r\n3\r\nbar\r\n0\r\n\r\n'), + ((b'foo', b'bar'), b'3\r\nfoo\r\n3\r\nbar\r\n0\r\n\r\n'), + ) + for body, expected in cases: + with self.subTest(body): + self.conn = client.HTTPConnection('example.com') + self.conn.sock = self.sock = FakeSocket('') + + self.conn.request('PUT', '/url', body) + msg, f = self.get_headers_and_fp() + self.assertNotIn('Content-Type', msg) + self.assertNotIn('Content-Length', msg) + self.assertEqual(msg.get('Transfer-Encoding'), 'chunked') + self.assertEqual(expected, f.read()) + def test_manual_content_length(self): # Set an incorrect content-length so that we can verify that # it will not be over-ridden by the library. @@ -1588,8 +1726,13 @@ class RequestBodyTest(TestCase): message, f = self.get_headers_and_fp() self.assertEqual("text/plain", message.get_content_type()) self.assertIsNone(message.get_charset()) - self.assertEqual("4", message.get("content-length")) - self.assertEqual(b'body', f.read()) + # Note that the length of text files is unpredictable + # because it depends on character encoding and line ending + # translation. No content-length will be set, the body + # will be sent using chunked transfer encoding. + self.assertIsNone(message.get("content-length")) + self.assertEqual("chunked", message.get("transfer-encoding")) + self.assertEqual(b'4\r\nbody\r\n0\r\n\r\n', f.read()) def test_binary_file_body(self): self.addCleanup(support.unlink, support.TESTFN) diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py index eda7cccc60..0eea0c7f98 100644 --- a/Lib/test/test_urllib2.py +++ b/Lib/test/test_urllib2.py @@ -7,6 +7,8 @@ import io import socket import array import sys +import tempfile +import subprocess import urllib.request # The proxy bypass method imported below has logic specific to the OSX @@ -335,7 +337,8 @@ class MockHTTPClass: else: self._tunnel_headers.clear() - def request(self, method, url, body=None, headers=None): + def request(self, method, url, body=None, headers=None, *, + encode_chunked=False): self.method = method self.selector = url if headers is not None: @@ -343,6 +346,7 @@ class MockHTTPClass: self.req_headers.sort() if body: self.data = body + self.encode_chunked = encode_chunked if self.raise_on_endheaders: raise OSError() @@ -908,41 +912,96 @@ class HandlerTests(unittest.TestCase): self.assertEqual(req.unredirected_hdrs["Host"], "baz") self.assertEqual(req.unredirected_hdrs["Spam"], "foo") - # Check iterable body support - def iterable_body(): - yield b"one" - yield b"two" - yield b"three" + def test_http_body_file(self): + # A regular file - Content Length is calculated unless already set. - for headers in {}, {"Content-Length": 11}: - req = Request("http://example.com/", iterable_body(), headers) - if not headers: - # Having an iterable body without a Content-Length should - # raise an exception - self.assertRaises(ValueError, h.do_request_, req) - else: + h = urllib.request.AbstractHTTPHandler() + o = h.parent = MockOpener() + + file_obj = tempfile.NamedTemporaryFile(mode='w+b', delete=False) + file_path = file_obj.name + file_obj.write(b"Something\nSomething\nSomething\n") + file_obj.close() + + for headers in {}, {"Content-Length": 30}: + with open(file_path, "rb") as f: + req = Request("http://example.com/", f, headers) newreq = h.do_request_(req) + self.assertEqual(int(newreq.get_header('Content-length')), 30) - # A file object. - # Test only Content-Length attribute of request. + os.unlink(file_path) + + def test_http_body_fileobj(self): + # A file object - Content Length is calculated unless already set. + # (Note that there are some subtle differences to a regular + # file, that is why we are testing both cases.) + + h = urllib.request.AbstractHTTPHandler() + o = h.parent = MockOpener() file_obj = io.BytesIO() file_obj.write(b"Something\nSomething\nSomething\n") for headers in {}, {"Content-Length": 30}: + file_obj.seek(0) req = Request("http://example.com/", file_obj, headers) - if not headers: - # Having an iterable body without a Content-Length should - # raise an exception - self.assertRaises(ValueError, h.do_request_, req) - else: - newreq = h.do_request_(req) - self.assertEqual(int(newreq.get_header('Content-length')), 30) + newreq = h.do_request_(req) + self.assertEqual(int(newreq.get_header('Content-length')), 30) file_obj.close() + def test_http_body_pipe(self): + # A file reading from a pipe. + # A pipe cannot be seek'ed. There is no way to determine the + # content length up front. Thus, do_request_() should fall + # back to Transfer-encoding chunked. + + h = urllib.request.AbstractHTTPHandler() + o = h.parent = MockOpener() + + cmd = [sys.executable, "-c", + r"import sys; " + r"sys.stdout.buffer.write(b'Something\nSomething\nSomething\n')"] + for headers in {}, {"Content-Length": 30}: + with subprocess.Popen(cmd, stdout=subprocess.PIPE) as proc: + req = Request("http://example.com/", proc.stdout, headers) + newreq = h.do_request_(req) + if not headers: + self.assertEqual(newreq.get_header('Content-length'), None) + self.assertEqual(newreq.get_header('Transfer-encoding'), + 'chunked') + else: + self.assertEqual(int(newreq.get_header('Content-length')), + 30) + + def test_http_body_iterable(self): + # Generic iterable. There is no way to determine the content + # length up front. Fall back to Transfer-encoding chunked. + + h = urllib.request.AbstractHTTPHandler() + o = h.parent = MockOpener() + + def iterable_body(): + yield b"one" + yield b"two" + yield b"three" + + for headers in {}, {"Content-Length": 11}: + req = Request("http://example.com/", iterable_body(), headers) + newreq = h.do_request_(req) + if not headers: + self.assertEqual(newreq.get_header('Content-length'), None) + self.assertEqual(newreq.get_header('Transfer-encoding'), + 'chunked') + else: + self.assertEqual(int(newreq.get_header('Content-length')), 11) + + def test_http_body_array(self): # array.array Iterable - Content Length is calculated + h = urllib.request.AbstractHTTPHandler() + o = h.parent = MockOpener() + iterable_array = array.array("I",[1,2,3,4]) for headers in {}, {"Content-Length": 16}: diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py index dc436bc73f..30bf6e051e 100644 --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -141,17 +141,9 @@ def urlopen(url, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, *, cafile=None, capath=None, cadefault=False, context=None): '''Open the URL url, which can be either a string or a Request object. - *data* must be a bytes object specifying additional data to be sent to the - server, or None if no such data is needed. data may also be an iterable - object and in that case Content-Length value must be specified in the - headers. Currently HTTP requests are the only ones that use data; the HTTP - request will be a POST instead of a GET when the data parameter is - provided. - - *data* should be a buffer in the standard application/x-www-form-urlencoded - format. The urllib.parse.urlencode() function takes a mapping or sequence - of 2-tuples and returns an ASCII text string in this format. It should be - encoded to bytes before being used as the data parameter. + *data* must be an object specifying additional data to be sent to + the server, or None if no such data is needed. See Request for + details. urllib.request module uses HTTP/1.1 and includes a "Connection:close" header in its HTTP requests. @@ -1235,6 +1227,11 @@ class AbstractHTTPHandler(BaseHandler): def set_http_debuglevel(self, level): self._debuglevel = level + def _get_content_length(self, request): + return http.client.HTTPConnection._get_content_length( + request.data, + request.get_method()) + def do_request_(self, request): host = request.host if not host: @@ -1243,24 +1240,22 @@ class AbstractHTTPHandler(BaseHandler): if request.data is not None: # POST data = request.data if isinstance(data, str): - msg = "POST data should be bytes or an iterable of bytes. " \ - "It cannot be of type str." + msg = "POST data should be bytes, an iterable of bytes, " \ + "or a file object. It cannot be of type str." raise TypeError(msg) if not request.has_header('Content-type'): request.add_unredirected_header( 'Content-type', 'application/x-www-form-urlencoded') - if not request.has_header('Content-length'): - try: - mv = memoryview(data) - except TypeError: - if isinstance(data, collections.Iterable): - raise ValueError("Content-Length should be specified " - "for iterable data of type %r %r" % (type(data), - data)) + if (not request.has_header('Content-length') + and not request.has_header('Transfer-encoding')): + content_length = self._get_content_length(request) + if content_length is not None: + request.add_unredirected_header( + 'Content-length', str(content_length)) else: request.add_unredirected_header( - 'Content-length', '%d' % (len(mv) * mv.itemsize)) + 'Transfer-encoding', 'chunked') sel_host = host if request.has_proxy(): @@ -1316,7 +1311,8 @@ class AbstractHTTPHandler(BaseHandler): try: try: - h.request(req.get_method(), req.selector, req.data, headers) + h.request(req.get_method(), req.selector, req.data, headers, + encode_chunked=req.has_header('Transfer-encoding')) except OSError as err: # timeout error raise URLError(err) r = h.getresponse() -- cgit v1.2.1 From 27433522e06ac6d1f444abdc8657aad05c776dc5 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Thu, 25 Aug 2016 01:13:34 +0300 Subject: Fix typo in test name Noticed by Xiang Zhang. --- Lib/test/test_httpservers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 7f4b0f90d0..75044cbafa 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -190,7 +190,7 @@ class BaseHTTPServerTestCase(BaseTestCase): res = self.con.getresponse() self.assertEqual(res.status, HTTPStatus.NOT_IMPLEMENTED) - def test_head_keep_alive(self): + def test_header_keep_alive(self): self.con._http_vsn_str = 'HTTP/1.1' self.con.putrequest('GET', '/') self.con.putheader('Connection', 'keep-alive') -- cgit v1.2.1 From 3771bcf123f6491f23eae5bc4f6100326cac9cd5 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Wed, 24 Aug 2016 22:08:01 -0400 Subject: Issue #27821: Fix bug in idlelib.comfig function and add new tests. --- Lib/idlelib/config.py | 2 +- Lib/idlelib/idle_test/test_config.py | 106 +++++++++++++++++++++++++++-------- 2 files changed, 85 insertions(+), 23 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index f2437a8631..d2f0b139b5 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -394,7 +394,7 @@ class IdleConf: 'name2' may still be set, but it is ignored. """ cfgname = 'highlight' if section == 'Theme' else 'keys' - default = self.GetOption('main', 'Theme', 'default', + default = self.GetOption('main', section, 'default', type='bool', default=True) name = '' if default: diff --git a/Lib/idlelib/idle_test/test_config.py b/Lib/idlelib/idle_test/test_config.py index 53665cd761..a3fa1a341a 100644 --- a/Lib/idlelib/idle_test/test_config.py +++ b/Lib/idlelib/idle_test/test_config.py @@ -28,53 +28,115 @@ def tearDownModule(): class CurrentColorKeysTest(unittest.TestCase): - """Test correct scenarios for colorkeys and wrap functions. + """ Test colorkeys function with user config [Theme] and [Keys] patterns. - The 5 correct patterns are possible results of config dialog. + colorkeys = config.IdleConf.current_colors_and_keys + Test all patterns written by IDLE and some errors + Item 'default' should really be 'builtin' (versus 'custom). """ colorkeys = idleConf.current_colors_and_keys + default_theme = 'IDLE Classic' + default_keys = idleConf.default_keys() - def test_old_default(self): - # name2 must be blank + def test_old_builtin_theme(self): + # On initial installation, user main is blank. + self.assertEqual(self.colorkeys('Theme'), self.default_theme) + # For old default, name2 must be blank. usermain.read_string(''' [Theme] - default= 1 + default = True ''') - self.assertEqual(self.colorkeys('Theme'), 'IDLE Classic') + # IDLE omits 'name' for default old builtin theme. + self.assertEqual(self.colorkeys('Theme'), self.default_theme) + # IDLE adds 'name' for non-default old builtin theme. usermain['Theme']['name'] = 'IDLE New' self.assertEqual(self.colorkeys('Theme'), 'IDLE New') - usermain['Theme']['name'] = 'non-default' # error - self.assertEqual(self.colorkeys('Theme'), 'IDLE Classic') + # Erroneous non-default old builtin reverts to default. + usermain['Theme']['name'] = 'non-existent' + self.assertEqual(self.colorkeys('Theme'), self.default_theme) usermain.remove_section('Theme') - def test_new_default(self): - # name2 overrides name + def test_new_builtin_theme(self): + # IDLE writes name2 for new builtins. usermain.read_string(''' [Theme] - default= 1 - name= IDLE New - name2= IDLE Dark + default = True + name2 = IDLE Dark ''') self.assertEqual(self.colorkeys('Theme'), 'IDLE Dark') - usermain['Theme']['name2'] = 'non-default' # error - self.assertEqual(self.colorkeys('Theme'), 'IDLE Classic') + # Leftover 'name', not removed, is ignored. + usermain['Theme']['name'] = 'IDLE New' + self.assertEqual(self.colorkeys('Theme'), 'IDLE Dark') + # Erroneous non-default new builtin reverts to default. + usermain['Theme']['name2'] = 'non-existent' + self.assertEqual(self.colorkeys('Theme'), self.default_theme) usermain.remove_section('Theme') - def test_user_override(self): - # name2 does not matter + def test_user_override_theme(self): + # Erroneous custom name (no definition) reverts to default. usermain.read_string(''' [Theme] - default= 0 - name= Custom Dark - ''') # error until set userhigh - self.assertEqual(self.colorkeys('Theme'), 'IDLE Classic') + default = False + name = Custom Dark + ''') + self.assertEqual(self.colorkeys('Theme'), self.default_theme) + # Custom name is valid with matching Section name. userhigh.read_string('[Custom Dark]\na=b') self.assertEqual(self.colorkeys('Theme'), 'Custom Dark') - usermain['Theme']['name2'] = 'IDLE Dark' + # Name2 is ignored. + usermain['Theme']['name2'] = 'non-existent' self.assertEqual(self.colorkeys('Theme'), 'Custom Dark') usermain.remove_section('Theme') userhigh.remove_section('Custom Dark') + def test_old_builtin_keys(self): + # On initial installation, user main is blank. + self.assertEqual(self.colorkeys('Keys'), self.default_keys) + # For old default, name2 must be blank, name is always used. + usermain.read_string(''' + [Keys] + default = True + name = IDLE Classic Unix + ''') + self.assertEqual(self.colorkeys('Keys'), 'IDLE Classic Unix') + # Erroneous non-default old builtin reverts to default. + usermain['Keys']['name'] = 'non-existent' + self.assertEqual(self.colorkeys('Keys'), self.default_keys) + usermain.remove_section('Keys') + + def test_new_builtin_keys(self): + # IDLE writes name2 for new builtins. + usermain.read_string(''' + [Keys] + default = True + name2 = IDLE Modern Unix + ''') + self.assertEqual(self.colorkeys('Keys'), 'IDLE Modern Unix') + # Leftover 'name', not removed, is ignored. + usermain['Keys']['name'] = 'IDLE Classic Unix' + self.assertEqual(self.colorkeys('Keys'), 'IDLE Modern Unix') + # Erroneous non-default new builtin reverts to default. + usermain['Keys']['name2'] = 'non-existent' + self.assertEqual(self.colorkeys('Keys'), self.default_keys) + usermain.remove_section('Keys') + + def test_user_override_keys(self): + # Erroneous custom name (no definition) reverts to default. + usermain.read_string(''' + [Keys] + default = False + name = Custom Keys + ''') + self.assertEqual(self.colorkeys('Keys'), self.default_keys) + # Custom name is valid with matching Section name. + userkeys.read_string('[Custom Keys]\na=b') + self.assertEqual(self.colorkeys('Keys'), 'Custom Keys') + # Name2 is ignored. + usermain['Keys']['name2'] = 'non-existent' + self.assertEqual(self.colorkeys('Keys'), 'Custom Keys') + usermain.remove_section('Keys') + userkeys.remove_section('Custom Keys') + class WarningTest(unittest.TestCase): -- cgit v1.2.1 From af8fbafe368b5cc4432e47bb0e38fd1e5c119840 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Fri, 26 Aug 2016 14:44:48 -0700 Subject: Issue #26027, #27524: Add PEP 519/__fspath__() support to os and os.path. Thanks to Jelle Zijlstra for the initial patch against posixmodule.c. --- Lib/genericpath.py | 6 ++++ Lib/ntpath.py | 18 ++++++++-- Lib/os.py | 4 ++- Lib/posixpath.py | 19 +++++++++- Lib/test/test_genericpath.py | 64 ++++++++++++++++++++++++++++------ Lib/test/test_ntpath.py | 83 ++++++++++++++++++++++++++++++++++++++++++++ Lib/test/test_os.py | 83 ++++++++++++++++++++++++++++++++++++++++++-- Lib/test/test_posix.py | 10 +++--- Lib/test/test_posixpath.py | 80 ++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 345 insertions(+), 22 deletions(-) (limited to 'Lib') diff --git a/Lib/genericpath.py b/Lib/genericpath.py index 671406197a..303b3b349a 100644 --- a/Lib/genericpath.py +++ b/Lib/genericpath.py @@ -69,6 +69,12 @@ def getctime(filename): def commonprefix(m): "Given a list of pathnames, returns the longest common leading component" if not m: return '' + # Some people pass in a list of pathname parts to operate in an OS-agnostic + # fashion; don't try to translate in that case as that's an abuse of the + # API and they are already doing what they need to be OS-agnostic and so + # they most likely won't be using an os.PathLike object in the sublists. + if not isinstance(m[0], (list, tuple)): + m = tuple(map(os.fspath, m)) s1 = min(m) s2 = max(m) for i, c in enumerate(s1): diff --git a/Lib/ntpath.py b/Lib/ntpath.py index af6a7091f9..1fa4448426 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -46,6 +46,7 @@ def normcase(s): """Normalize case of pathname. Makes all characters lowercase and all slashes into backslashes.""" + s = os.fspath(s) try: if isinstance(s, bytes): return s.replace(b'/', b'\\').lower() @@ -66,12 +67,14 @@ def normcase(s): def isabs(s): """Test whether a path is absolute""" + s = os.fspath(s) s = splitdrive(s)[1] return len(s) > 0 and s[0] in _get_bothseps(s) # Join two (or more) paths. def join(path, *paths): + path = os.fspath(path) if isinstance(path, bytes): sep = b'\\' seps = b'\\/' @@ -84,7 +87,7 @@ def join(path, *paths): if not paths: path[:0] + sep #23780: Ensure compatible data type even if p is null. result_drive, result_path = splitdrive(path) - for p in paths: + for p in map(os.fspath, paths): p_drive, p_path = splitdrive(p) if p_path and p_path[0] in seps: # Second path is absolute @@ -136,6 +139,7 @@ def splitdrive(p): Paths cannot contain both a drive letter and a UNC path. """ + p = os.fspath(p) if len(p) >= 2: if isinstance(p, bytes): sep = b'\\' @@ -199,7 +203,7 @@ def split(p): Return tuple (head, tail) where tail is everything after the final slash. Either part may be empty.""" - + p = os.fspath(p) seps = _get_bothseps(p) d, p = splitdrive(p) # set i to index beyond p's last slash @@ -218,6 +222,7 @@ def split(p): # It is always true that root + ext == p. def splitext(p): + p = os.fspath(p) if isinstance(p, bytes): return genericpath._splitext(p, b'\\', b'/', b'.') else: @@ -278,6 +283,7 @@ except ImportError: def ismount(path): """Test whether a path is a mount point (a drive root, the root of a share, or a mounted volume)""" + path = os.fspath(path) seps = _get_bothseps(path) path = abspath(path) root, rest = splitdrive(path) @@ -305,6 +311,7 @@ def expanduser(path): """Expand ~ and ~user constructs. If user or $HOME is unknown, do nothing.""" + path = os.fspath(path) if isinstance(path, bytes): tilde = b'~' else: @@ -354,6 +361,7 @@ def expandvars(path): """Expand shell variables of the forms $var, ${var} and %var%. Unknown variables are left unchanged.""" + path = os.fspath(path) if isinstance(path, bytes): if b'$' not in path and b'%' not in path: return path @@ -464,6 +472,7 @@ def expandvars(path): def normpath(path): """Normalize path, eliminating double slashes, etc.""" + path = os.fspath(path) if isinstance(path, bytes): sep = b'\\' altsep = b'/' @@ -518,6 +527,7 @@ try: except ImportError: # not running on Windows - mock up something sensible def abspath(path): """Return the absolute version of a path.""" + path = os.fspath(path) if not isabs(path): if isinstance(path, bytes): cwd = os.getcwdb() @@ -531,6 +541,7 @@ else: # use native Windows method on Windows """Return the absolute version of a path.""" if path: # Empty path must return current working directory. + path = os.fspath(path) try: path = _getfullpathname(path) except OSError: @@ -549,6 +560,7 @@ supports_unicode_filenames = (hasattr(sys, "getwindowsversion") and def relpath(path, start=None): """Return a relative version of a path""" + path = os.fspath(path) if isinstance(path, bytes): sep = b'\\' curdir = b'.' @@ -564,6 +576,7 @@ def relpath(path, start=None): if not path: raise ValueError("no path specified") + start = os.fspath(start) try: start_abs = abspath(normpath(start)) path_abs = abspath(normpath(path)) @@ -607,6 +620,7 @@ def commonpath(paths): if not paths: raise ValueError('commonpath() arg is an empty sequence') + paths = tuple(map(os.fspath, paths)) if isinstance(paths[0], bytes): sep = b'\\' altsep = b'/' diff --git a/Lib/os.py b/Lib/os.py index c31ecb2f05..307a1ded42 100644 --- a/Lib/os.py +++ b/Lib/os.py @@ -353,7 +353,7 @@ def walk(top, topdown=True, onerror=None, followlinks=False): dirs.remove('CVS') # don't visit CVS directories """ - + top = fspath(top) dirs = [] nondirs = [] walk_dirs = [] @@ -536,6 +536,8 @@ if {open, stat} <= supports_dir_fd and {listdir, stat} <= supports_fd: if 'CVS' in dirs: dirs.remove('CVS') # don't visit CVS directories """ + if not isinstance(top, int) or not hasattr(top, '__index__'): + top = fspath(top) # Note: To guard against symlink races, we use the standard # lstat()/open()/fstat() trick. orig_st = stat(top, follow_symlinks=False, dir_fd=dir_fd) diff --git a/Lib/posixpath.py b/Lib/posixpath.py index d9f3f993da..6dbdab2749 100644 --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -49,6 +49,7 @@ def _get_sep(path): def normcase(s): """Normalize case of pathname. Has no effect under Posix""" + s = os.fspath(s) if not isinstance(s, (bytes, str)): raise TypeError("normcase() argument must be str or bytes, " "not '{}'".format(s.__class__.__name__)) @@ -60,6 +61,7 @@ def normcase(s): def isabs(s): """Test whether a path is absolute""" + s = os.fspath(s) sep = _get_sep(s) return s.startswith(sep) @@ -73,12 +75,13 @@ def join(a, *p): If any component is an absolute path, all previous path components will be discarded. An empty last part will result in a path that ends with a separator.""" + a = os.fspath(a) sep = _get_sep(a) path = a try: if not p: path[:0] + sep #23780: Ensure compatible data type even if p is null. - for b in p: + for b in map(os.fspath, p): if b.startswith(sep): path = b elif not path or path.endswith(sep): @@ -99,6 +102,7 @@ def join(a, *p): def split(p): """Split a pathname. Returns tuple "(head, tail)" where "tail" is everything after the final slash. Either part may be empty.""" + p = os.fspath(p) sep = _get_sep(p) i = p.rfind(sep) + 1 head, tail = p[:i], p[i:] @@ -113,6 +117,7 @@ def split(p): # It is always true that root + ext == p. def splitext(p): + p = os.fspath(p) if isinstance(p, bytes): sep = b'/' extsep = b'.' @@ -128,6 +133,7 @@ splitext.__doc__ = genericpath._splitext.__doc__ def splitdrive(p): """Split a pathname into drive and path. On Posix, drive is always empty.""" + p = os.fspath(p) return p[:0], p @@ -135,6 +141,7 @@ def splitdrive(p): def basename(p): """Returns the final component of a pathname""" + p = os.fspath(p) sep = _get_sep(p) i = p.rfind(sep) + 1 return p[i:] @@ -144,6 +151,7 @@ def basename(p): def dirname(p): """Returns the directory component of a pathname""" + p = os.fspath(p) sep = _get_sep(p) i = p.rfind(sep) + 1 head = p[:i] @@ -222,6 +230,7 @@ def ismount(path): def expanduser(path): """Expand ~ and ~user constructions. If user or $HOME is unknown, do nothing.""" + path = os.fspath(path) if isinstance(path, bytes): tilde = b'~' else: @@ -267,6 +276,7 @@ _varprogb = None def expandvars(path): """Expand shell variables of form $var and ${var}. Unknown variables are left unchanged.""" + path = os.fspath(path) global _varprog, _varprogb if isinstance(path, bytes): if b'$' not in path: @@ -318,6 +328,7 @@ def expandvars(path): def normpath(path): """Normalize path, eliminating double slashes, etc.""" + path = os.fspath(path) if isinstance(path, bytes): sep = b'/' empty = b'' @@ -355,6 +366,7 @@ def normpath(path): def abspath(path): """Return an absolute path.""" + path = os.fspath(path) if not isabs(path): if isinstance(path, bytes): cwd = os.getcwdb() @@ -370,6 +382,7 @@ def abspath(path): def realpath(filename): """Return the canonical path of the specified filename, eliminating any symbolic links encountered in the path.""" + filename = os.fspath(filename) path, ok = _joinrealpath(filename[:0], filename, {}) return abspath(path) @@ -434,6 +447,7 @@ def relpath(path, start=None): if not path: raise ValueError("no path specified") + path = os.fspath(path) if isinstance(path, bytes): curdir = b'.' sep = b'/' @@ -445,6 +459,8 @@ def relpath(path, start=None): if start is None: start = curdir + else: + start = os.fspath(start) try: start_list = [x for x in abspath(start).split(sep) if x] @@ -472,6 +488,7 @@ def commonpath(paths): if not paths: raise ValueError('commonpath() arg is an empty sequence') + paths = tuple(map(os.fspath, paths)) if isinstance(paths[0], bytes): sep = b'/' curdir = b'.' diff --git a/Lib/test/test_genericpath.py b/Lib/test/test_genericpath.py index 5331b241ef..c8f158d0c5 100644 --- a/Lib/test/test_genericpath.py +++ b/Lib/test/test_genericpath.py @@ -450,16 +450,15 @@ class CommonTest(GenericTest): with self.assertRaisesRegex(TypeError, errmsg): self.pathmodule.join('str', b'bytes') # regression, see #15377 - errmsg = r'join\(\) argument must be str or bytes, not %r' - with self.assertRaisesRegex(TypeError, errmsg % 'int'): + with self.assertRaisesRegex(TypeError, 'int'): self.pathmodule.join(42, 'str') - with self.assertRaisesRegex(TypeError, errmsg % 'int'): + with self.assertRaisesRegex(TypeError, 'int'): self.pathmodule.join('str', 42) - with self.assertRaisesRegex(TypeError, errmsg % 'int'): + with self.assertRaisesRegex(TypeError, 'int'): self.pathmodule.join(42) - with self.assertRaisesRegex(TypeError, errmsg % 'list'): + with self.assertRaisesRegex(TypeError, 'list'): self.pathmodule.join([]) - with self.assertRaisesRegex(TypeError, errmsg % 'bytearray'): + with self.assertRaisesRegex(TypeError, 'bytearray'): self.pathmodule.join(bytearray(b'foo'), bytearray(b'bar')) def test_relpath_errors(self): @@ -471,14 +470,59 @@ class CommonTest(GenericTest): self.pathmodule.relpath(b'bytes', 'str') with self.assertRaisesRegex(TypeError, errmsg): self.pathmodule.relpath('str', b'bytes') - errmsg = r'relpath\(\) argument must be str or bytes, not %r' - with self.assertRaisesRegex(TypeError, errmsg % 'int'): + with self.assertRaisesRegex(TypeError, 'int'): self.pathmodule.relpath(42, 'str') - with self.assertRaisesRegex(TypeError, errmsg % 'int'): + with self.assertRaisesRegex(TypeError, 'int'): self.pathmodule.relpath('str', 42) - with self.assertRaisesRegex(TypeError, errmsg % 'bytearray'): + with self.assertRaisesRegex(TypeError, 'bytearray'): self.pathmodule.relpath(bytearray(b'foo'), bytearray(b'bar')) +class PathLikeTests(unittest.TestCase): + + class PathLike: + def __init__(self, path=''): + self.path = path + def __fspath__(self): + if isinstance(self.path, BaseException): + raise self.path + else: + return self.path + + def setUp(self): + self.file_name = support.TESTFN.lower() + self.file_path = self.PathLike(support.TESTFN) + self.addCleanup(support.unlink, self.file_name) + create_file(self.file_name, b"test_genericpath.PathLikeTests") + + def assertPathEqual(self, func): + self.assertEqual(func(self.file_path), func(self.file_name)) + + def test_path_exists(self): + self.assertPathEqual(os.path.exists) + + def test_path_isfile(self): + self.assertPathEqual(os.path.isfile) + + def test_path_isdir(self): + self.assertPathEqual(os.path.isdir) + + def test_path_commonprefix(self): + self.assertEqual(os.path.commonprefix([self.file_path, self.file_name]), + self.file_name) + + def test_path_getsize(self): + self.assertPathEqual(os.path.getsize) + + def test_path_getmtime(self): + self.assertPathEqual(os.path.getatime) + + def test_path_getctime(self): + self.assertPathEqual(os.path.getctime) + + def test_path_samefile(self): + self.assertTrue(os.path.samefile(self.file_path, self.file_name)) + + if __name__=="__main__": unittest.main() diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py index 580f2030a3..90edb6d080 100644 --- a/Lib/test/test_ntpath.py +++ b/Lib/test/test_ntpath.py @@ -452,5 +452,88 @@ class NtCommonTest(test_genericpath.CommonTest, unittest.TestCase): attributes = ['relpath', 'splitunc'] +class PathLikeTests(unittest.TestCase): + + path = ntpath + + class PathLike: + def __init__(self, path=''): + self.path = path + def __fspath__(self): + if isinstance(self.path, BaseException): + raise self.path + else: + return self.path + + def setUp(self): + self.file_name = support.TESTFN.lower() + self.file_path = self.PathLike(support.TESTFN) + self.addCleanup(support.unlink, self.file_name) + with open(self.file_name, 'xb', 0) as file: + file.write(b"test_ntpath.PathLikeTests") + + def assertPathEqual(self, func): + self.assertEqual(func(self.file_path), func(self.file_name)) + + def test_path_normcase(self): + self.assertPathEqual(self.path.normcase) + + def test_path_isabs(self): + self.assertPathEqual(self.path.isabs) + + def test_path_join(self): + self.assertEqual(self.path.join('a', self.PathLike('b'), 'c'), + self.path.join('a', 'b', 'c')) + + def test_path_split(self): + self.assertPathEqual(self.path.split) + + def test_path_splitext(self): + self.assertPathEqual(self.path.splitext) + + def test_path_splitdrive(self): + self.assertPathEqual(self.path.splitdrive) + + def test_path_basename(self): + self.assertPathEqual(self.path.basename) + + def test_path_dirname(self): + self.assertPathEqual(self.path.dirname) + + def test_path_islink(self): + self.assertPathEqual(self.path.islink) + + def test_path_lexists(self): + self.assertPathEqual(self.path.lexists) + + def test_path_ismount(self): + self.assertPathEqual(self.path.ismount) + + def test_path_expanduser(self): + self.assertPathEqual(self.path.expanduser) + + def test_path_expandvars(self): + self.assertPathEqual(self.path.expandvars) + + def test_path_normpath(self): + self.assertPathEqual(self.path.normpath) + + def test_path_abspath(self): + self.assertPathEqual(self.path.abspath) + + def test_path_realpath(self): + self.assertPathEqual(self.path.realpath) + + def test_path_relpath(self): + self.assertPathEqual(self.path.relpath) + + def test_path_commonpath(self): + common_path = self.path.commonpath([self.file_path, self.file_name]) + self.assertEqual(common_path, self.file_name) + + def test_path_isdir(self): + self.assertPathEqual(self.path.isdir) + + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index d8920d99c5..5ac4d64de2 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -874,10 +874,12 @@ class WalkTests(unittest.TestCase): self.assertEqual(all[2 + flipped], (self.sub11_path, [], [])) self.assertEqual(all[3 - 2 * flipped], self.sub2_tree) - def test_walk_prune(self): + def test_walk_prune(self, walk_path=None): + if walk_path is None: + walk_path = self.walk_path # Prune the search. all = [] - for root, dirs, files in self.walk(self.walk_path): + for root, dirs, files in self.walk(walk_path): all.append((root, dirs, files)) # Don't descend into SUB1. if 'SUB1' in dirs: @@ -886,11 +888,22 @@ class WalkTests(unittest.TestCase): self.assertEqual(len(all), 2) self.assertEqual(all[0], - (self.walk_path, ["SUB2"], ["tmp1"])) + (str(walk_path), ["SUB2"], ["tmp1"])) all[1][-1].sort() self.assertEqual(all[1], self.sub2_tree) + def test_file_like_path(self): + class FileLike: + def __init__(self, path): + self._path = path + def __str__(self): + return str(self._path) + def __fspath__(self): + return self._path + + self.test_walk_prune(FileLike(self.walk_path)) + def test_walk_bottom_up(self): # Walk bottom-up. all = list(self.walk(self.walk_path, topdown=False)) @@ -2807,6 +2820,70 @@ class FDInheritanceTests(unittest.TestCase): self.assertEqual(os.get_inheritable(slave_fd), False) +class PathTConverterTests(unittest.TestCase): + # tuples of (function name, allows fd arguments, additional arguments to + # function, cleanup function) + functions = [ + ('stat', True, (), None), + ('lstat', False, (), None), + ('access', True, (os.F_OK,), None), + ('chflags', False, (0,), None), + ('lchflags', False, (0,), None), + ('open', False, (0,), getattr(os, 'close', None)), + ] + + def test_path_t_converter(self): + class PathLike: + def __init__(self, path): + self.path = path + + def __fspath__(self): + return self.path + + str_filename = support.TESTFN + bytes_filename = support.TESTFN.encode('ascii') + bytearray_filename = bytearray(bytes_filename) + fd = os.open(PathLike(str_filename), os.O_WRONLY|os.O_CREAT) + self.addCleanup(os.close, fd) + self.addCleanup(support.unlink, support.TESTFN) + + int_fspath = PathLike(fd) + str_fspath = PathLike(str_filename) + bytes_fspath = PathLike(bytes_filename) + bytearray_fspath = PathLike(bytearray_filename) + + for name, allow_fd, extra_args, cleanup_fn in self.functions: + with self.subTest(name=name): + try: + fn = getattr(os, name) + except AttributeError: + continue + + for path in (str_filename, bytes_filename, bytearray_filename, + str_fspath, bytes_fspath): + with self.subTest(name=name, path=path): + result = fn(path, *extra_args) + if cleanup_fn is not None: + cleanup_fn(result) + + with self.assertRaisesRegex( + TypeError, 'should be string, bytes'): + fn(int_fspath, *extra_args) + with self.assertRaisesRegex( + TypeError, 'should be string, bytes'): + fn(bytearray_fspath, *extra_args) + + if allow_fd: + result = fn(fd, *extra_args) # should not fail + if cleanup_fn is not None: + cleanup_fn(result) + else: + with self.assertRaisesRegex( + TypeError, + 'os.PathLike'): + fn(fd, *extra_args) + + @unittest.skipUnless(hasattr(os, 'get_blocking'), 'needs os.get_blocking() and os.set_blocking()') class BlockingTests(unittest.TestCase): diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index de22513e34..d2f58baae6 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -397,7 +397,7 @@ class PosixTester(unittest.TestCase): self.assertTrue(posix.stat(fp.fileno())) self.assertRaisesRegex(TypeError, - 'should be string, bytes or integer, not', + 'should be string, bytes, os.PathLike or integer, not', posix.stat, float(fp.fileno())) finally: fp.close() @@ -409,16 +409,16 @@ class PosixTester(unittest.TestCase): self.assertTrue(posix.stat(os.fsencode(support.TESTFN))) self.assertWarnsRegex(DeprecationWarning, - 'should be string, bytes or integer, not', + 'should be string, bytes, os.PathLike or integer, not', posix.stat, bytearray(os.fsencode(support.TESTFN))) self.assertRaisesRegex(TypeError, - 'should be string, bytes or integer, not', + 'should be string, bytes, os.PathLike or integer, not', posix.stat, None) self.assertRaisesRegex(TypeError, - 'should be string, bytes or integer, not', + 'should be string, bytes, os.PathLike or integer, not', posix.stat, list(support.TESTFN)) self.assertRaisesRegex(TypeError, - 'should be string, bytes or integer, not', + 'should be string, bytes, os.PathLike or integer, not', posix.stat, list(os.fsencode(support.TESTFN))) @unittest.skipUnless(hasattr(posix, 'mkfifo'), "don't have mkfifo()") diff --git a/Lib/test/test_posixpath.py b/Lib/test/test_posixpath.py index 0783c36b9f..8a1e33b0c8 100644 --- a/Lib/test/test_posixpath.py +++ b/Lib/test/test_posixpath.py @@ -596,5 +596,85 @@ class PosixCommonTest(test_genericpath.CommonTest, unittest.TestCase): attributes = ['relpath', 'samefile', 'sameopenfile', 'samestat'] +class PathLikeTests(unittest.TestCase): + + path = posixpath + + class PathLike: + def __init__(self, path=''): + self.path = path + def __fspath__(self): + if isinstance(self.path, BaseException): + raise self.path + else: + return self.path + + def setUp(self): + self.file_name = support.TESTFN.lower() + self.file_path = self.PathLike(support.TESTFN) + self.addCleanup(support.unlink, self.file_name) + with open(self.file_name, 'xb', 0) as file: + file.write(b"test_posixpath.PathLikeTests") + + def assertPathEqual(self, func): + self.assertEqual(func(self.file_path), func(self.file_name)) + + def test_path_normcase(self): + self.assertPathEqual(self.path.normcase) + + def test_path_isabs(self): + self.assertPathEqual(self.path.isabs) + + def test_path_join(self): + self.assertEqual(self.path.join('a', self.PathLike('b'), 'c'), + self.path.join('a', 'b', 'c')) + + def test_path_split(self): + self.assertPathEqual(self.path.split) + + def test_path_splitext(self): + self.assertPathEqual(self.path.splitext) + + def test_path_splitdrive(self): + self.assertPathEqual(self.path.splitdrive) + + def test_path_basename(self): + self.assertPathEqual(self.path.basename) + + def test_path_dirname(self): + self.assertPathEqual(self.path.dirname) + + def test_path_islink(self): + self.assertPathEqual(self.path.islink) + + def test_path_lexists(self): + self.assertPathEqual(self.path.lexists) + + def test_path_ismount(self): + self.assertPathEqual(self.path.ismount) + + def test_path_expanduser(self): + self.assertPathEqual(self.path.expanduser) + + def test_path_expandvars(self): + self.assertPathEqual(self.path.expandvars) + + def test_path_normpath(self): + self.assertPathEqual(self.path.normpath) + + def test_path_abspath(self): + self.assertPathEqual(self.path.abspath) + + def test_path_realpath(self): + self.assertPathEqual(self.path.realpath) + + def test_path_relpath(self): + self.assertPathEqual(self.path.relpath) + + def test_path_commonpath(self): + common_path = self.path.commonpath([self.file_path, self.file_name]) + self.assertEqual(common_path, self.file_name) + + if __name__=="__main__": unittest.main() -- cgit v1.2.1 From f48172e2666e16b380de1ce4e552b79d2ff62317 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Fri, 26 Aug 2016 19:30:11 -0700 Subject: Issue #26027: Don't test for bytearray in path_t as that's now deprecated. --- Lib/test/test_os.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 5ac4d64de2..8c6a8c0815 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2842,7 +2842,6 @@ class PathTConverterTests(unittest.TestCase): str_filename = support.TESTFN bytes_filename = support.TESTFN.encode('ascii') - bytearray_filename = bytearray(bytes_filename) fd = os.open(PathLike(str_filename), os.O_WRONLY|os.O_CREAT) self.addCleanup(os.close, fd) self.addCleanup(support.unlink, support.TESTFN) @@ -2850,7 +2849,6 @@ class PathTConverterTests(unittest.TestCase): int_fspath = PathLike(fd) str_fspath = PathLike(str_filename) bytes_fspath = PathLike(bytes_filename) - bytearray_fspath = PathLike(bytearray_filename) for name, allow_fd, extra_args, cleanup_fn in self.functions: with self.subTest(name=name): @@ -2859,8 +2857,8 @@ class PathTConverterTests(unittest.TestCase): except AttributeError: continue - for path in (str_filename, bytes_filename, bytearray_filename, - str_fspath, bytes_fspath): + for path in (str_filename, bytes_filename, str_fspath, + bytes_fspath): with self.subTest(name=name, path=path): result = fn(path, *extra_args) if cleanup_fn is not None: @@ -2869,9 +2867,6 @@ class PathTConverterTests(unittest.TestCase): with self.assertRaisesRegex( TypeError, 'should be string, bytes'): fn(int_fspath, *extra_args) - with self.assertRaisesRegex( - TypeError, 'should be string, bytes'): - fn(bytearray_fspath, *extra_args) if allow_fd: result = fn(fd, *extra_args) # should not fail -- cgit v1.2.1 From 058e6a49c6e7b2847286eea5284a89b2757148d9 Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Sat, 27 Aug 2016 01:39:26 +0000 Subject: Issue #12319: Always send file request bodies using chunked encoding The previous attempt to determine the file?s Content-Length gave a false positive for pipes on Windows. Also, drop the special case for sending zero-length iterable bodies. --- Lib/http/client.py | 31 +++++++-------------------- Lib/test/test_httplib.py | 27 ++++++++++++++++------- Lib/test/test_urllib2.py | 56 ++++++++++++++++++++++++++++++------------------ 3 files changed, 62 insertions(+), 52 deletions(-) (limited to 'Lib') diff --git a/Lib/http/client.py b/Lib/http/client.py index b242ba6559..9d5cf4518f 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -805,35 +805,21 @@ class HTTPConnection: def _get_content_length(body, method): """Get the content-length based on the body. - If the body is "empty", we set Content-Length: 0 for methods - that expect a body (RFC 7230, Section 3.3.2). If the body is - set for other methods, we set the header provided we can - figure out what the length is. + If the body is None, we set Content-Length: 0 for methods that expect + a body (RFC 7230, Section 3.3.2). We also set the Content-Length for + any method if the body is a str or bytes-like object and not a file. """ - if not body: + if body is None: # do an explicit check for not None here to distinguish # between unset and set but empty - if method.upper() in _METHODS_EXPECTING_BODY or body is not None: + if method.upper() in _METHODS_EXPECTING_BODY: return 0 else: return None if hasattr(body, 'read'): # file-like object. - if HTTPConnection._is_textIO(body): - # text streams are unpredictable because it depends on - # character encoding and line ending translation. - return None - else: - # Is it seekable? - try: - curpos = body.tell() - sz = body.seek(0, io.SEEK_END) - except (TypeError, AttributeError, OSError): - return None - else: - body.seek(curpos) - return sz - curpos + return None try: # does it implement the buffer protocol (bytes, bytearray, array)? @@ -1266,8 +1252,7 @@ class HTTPConnection: # the caller passes encode_chunked=True or the following # conditions hold: # 1. content-length has not been explicitly set - # 2. the length of the body cannot be determined - # (e.g. it is a generator or unseekable file) + # 2. the body is a file or iterable, but not a str or bytes-like # 3. Transfer-Encoding has NOT been explicitly set by the caller if 'content-length' not in header_names: @@ -1280,7 +1265,7 @@ class HTTPConnection: encode_chunked = False content_length = self._get_content_length(body, method) if content_length is None: - if body: + if body is not None: if self.debuglevel > 0: print('Unable to determine size of %r' % body) encode_chunked = True diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index a1796123e4..359e0bb94a 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -381,6 +381,16 @@ class TransferEncodingTest(TestCase): # same request self.assertNotIn('content-length', [k.lower() for k in headers]) + def test_empty_body(self): + # Zero-length iterable should be treated like any other iterable + conn = client.HTTPConnection('example.com') + conn.sock = FakeSocket(b'') + conn.request('POST', '/', ()) + _, headers, body = self._parse_request(conn.sock.data) + self.assertEqual(headers['Transfer-Encoding'], 'chunked') + self.assertNotIn('content-length', [k.lower() for k in headers]) + self.assertEqual(body, b"0\r\n\r\n") + def _make_body(self, empty_lines=False): lines = self.expected_body.split(b' ') for idx, line in enumerate(lines): @@ -652,7 +662,9 @@ class BasicTest(TestCase): def test_send_file(self): expected = (b'GET /foo HTTP/1.1\r\nHost: example.com\r\n' - b'Accept-Encoding: identity\r\nContent-Length:') + b'Accept-Encoding: identity\r\n' + b'Transfer-Encoding: chunked\r\n' + b'\r\n') with open(__file__, 'rb') as body: conn = client.HTTPConnection('example.com') @@ -1717,7 +1729,7 @@ class RequestBodyTest(TestCase): self.assertEqual("5", message.get("content-length")) self.assertEqual(b'body\xc1', f.read()) - def test_file_body(self): + def test_text_file_body(self): self.addCleanup(support.unlink, support.TESTFN) with open(support.TESTFN, "w") as f: f.write("body") @@ -1726,10 +1738,8 @@ class RequestBodyTest(TestCase): message, f = self.get_headers_and_fp() self.assertEqual("text/plain", message.get_content_type()) self.assertIsNone(message.get_charset()) - # Note that the length of text files is unpredictable - # because it depends on character encoding and line ending - # translation. No content-length will be set, the body - # will be sent using chunked transfer encoding. + # No content-length will be determined for files; the body + # will be sent using chunked transfer encoding instead. self.assertIsNone(message.get("content-length")) self.assertEqual("chunked", message.get("transfer-encoding")) self.assertEqual(b'4\r\nbody\r\n0\r\n\r\n', f.read()) @@ -1743,8 +1753,9 @@ class RequestBodyTest(TestCase): message, f = self.get_headers_and_fp() self.assertEqual("text/plain", message.get_content_type()) self.assertIsNone(message.get_charset()) - self.assertEqual("5", message.get("content-length")) - self.assertEqual(b'body\xc1', f.read()) + self.assertEqual("chunked", message.get("Transfer-Encoding")) + self.assertNotIn("Content-Length", message) + self.assertEqual(b'5\r\nbody\xc1\r\n0\r\n\r\n', f.read()) class HTTPResponseTest(TestCase): diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py index 0eea0c7f98..34329f8716 100644 --- a/Lib/test/test_urllib2.py +++ b/Lib/test/test_urllib2.py @@ -913,40 +913,50 @@ class HandlerTests(unittest.TestCase): self.assertEqual(req.unredirected_hdrs["Spam"], "foo") def test_http_body_file(self): - # A regular file - Content Length is calculated unless already set. + # A regular file - chunked encoding is used unless Content Length is + # already set. h = urllib.request.AbstractHTTPHandler() o = h.parent = MockOpener() file_obj = tempfile.NamedTemporaryFile(mode='w+b', delete=False) file_path = file_obj.name - file_obj.write(b"Something\nSomething\nSomething\n") file_obj.close() + self.addCleanup(os.unlink, file_path) - for headers in {}, {"Content-Length": 30}: - with open(file_path, "rb") as f: - req = Request("http://example.com/", f, headers) - newreq = h.do_request_(req) - self.assertEqual(int(newreq.get_header('Content-length')), 30) + with open(file_path, "rb") as f: + req = Request("http://example.com/", f, {}) + newreq = h.do_request_(req) + te = newreq.get_header('Transfer-encoding') + self.assertEqual(te, "chunked") + self.assertFalse(newreq.has_header('Content-length')) - os.unlink(file_path) + with open(file_path, "rb") as f: + req = Request("http://example.com/", f, {"Content-Length": 30}) + newreq = h.do_request_(req) + self.assertEqual(int(newreq.get_header('Content-length')), 30) + self.assertFalse(newreq.has_header("Transfer-encoding")) def test_http_body_fileobj(self): - # A file object - Content Length is calculated unless already set. + # A file object - chunked encoding is used + # unless Content Length is already set. # (Note that there are some subtle differences to a regular # file, that is why we are testing both cases.) h = urllib.request.AbstractHTTPHandler() o = h.parent = MockOpener() - file_obj = io.BytesIO() - file_obj.write(b"Something\nSomething\nSomething\n") - for headers in {}, {"Content-Length": 30}: - file_obj.seek(0) - req = Request("http://example.com/", file_obj, headers) - newreq = h.do_request_(req) - self.assertEqual(int(newreq.get_header('Content-length')), 30) + req = Request("http://example.com/", file_obj, {}) + newreq = h.do_request_(req) + self.assertEqual(newreq.get_header('Transfer-encoding'), 'chunked') + self.assertFalse(newreq.has_header('Content-length')) + + headers = {"Content-Length": 30} + req = Request("http://example.com/", file_obj, headers) + newreq = h.do_request_(req) + self.assertEqual(int(newreq.get_header('Content-length')), 30) + self.assertFalse(newreq.has_header("Transfer-encoding")) file_obj.close() @@ -959,9 +969,7 @@ class HandlerTests(unittest.TestCase): h = urllib.request.AbstractHTTPHandler() o = h.parent = MockOpener() - cmd = [sys.executable, "-c", - r"import sys; " - r"sys.stdout.buffer.write(b'Something\nSomething\nSomething\n')"] + cmd = [sys.executable, "-c", r"pass"] for headers in {}, {"Content-Length": 30}: with subprocess.Popen(cmd, stdout=subprocess.PIPE) as proc: req = Request("http://example.com/", proc.stdout, headers) @@ -983,8 +991,6 @@ class HandlerTests(unittest.TestCase): def iterable_body(): yield b"one" - yield b"two" - yield b"three" for headers in {}, {"Content-Length": 11}: req = Request("http://example.com/", iterable_body(), headers) @@ -996,6 +1002,14 @@ class HandlerTests(unittest.TestCase): else: self.assertEqual(int(newreq.get_header('Content-length')), 11) + def test_http_body_empty_seq(self): + # Zero-length iterable body should be treated like any other iterable + h = urllib.request.AbstractHTTPHandler() + h.parent = MockOpener() + req = h.do_request_(Request("http://example.com/", ())) + self.assertEqual(req.get_header("Transfer-encoding"), "chunked") + self.assertFalse(req.has_header("Content-length")) + def test_http_body_array(self): # array.array Iterable - Content Length is calculated -- cgit v1.2.1 From cfca21d2c0676e30605feffdd4012b631163dedc Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Sat, 27 Aug 2016 08:35:02 +0000 Subject: Issue #27506: Support bytes/bytearray.translate() delete as keyword argument Patch by Xiang Zhang. --- Lib/test/test_bytes.py | 49 +++++++++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 18 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index 64644e7ffb..8bbd669fc2 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -689,6 +689,37 @@ class BaseBytesTest: test.support.check_free_after_iterating(self, iter, self.type2test) test.support.check_free_after_iterating(self, reversed, self.type2test) + def test_translate(self): + b = self.type2test(b'hello') + rosetta = bytearray(range(256)) + rosetta[ord('o')] = ord('e') + + self.assertRaises(TypeError, b.translate) + self.assertRaises(TypeError, b.translate, None, None) + self.assertRaises(ValueError, b.translate, bytes(range(255))) + + c = b.translate(rosetta, b'hello') + self.assertEqual(b, b'hello') + self.assertIsInstance(c, self.type2test) + + c = b.translate(rosetta) + d = b.translate(rosetta, b'') + self.assertEqual(c, d) + self.assertEqual(c, b'helle') + + c = b.translate(rosetta, b'l') + self.assertEqual(c, b'hee') + c = b.translate(None, b'e') + self.assertEqual(c, b'hllo') + + # test delete as a keyword argument + c = b.translate(rosetta, delete=b'') + self.assertEqual(c, b'helle') + c = b.translate(rosetta, delete=b'l') + self.assertEqual(c, b'hee') + c = b.translate(None, delete=b'e') + self.assertEqual(c, b'hllo') + class BytesTest(BaseBytesTest, unittest.TestCase): type2test = bytes @@ -1449,24 +1480,6 @@ class AssortedBytesTest(unittest.TestCase): self.assertRaises(SyntaxError, eval, 'b"%s"' % chr(c)) - def test_translate(self): - b = b'hello' - ba = bytearray(b) - rosetta = bytearray(range(0, 256)) - rosetta[ord('o')] = ord('e') - c = b.translate(rosetta, b'l') - self.assertEqual(b, b'hello') - self.assertEqual(c, b'hee') - c = ba.translate(rosetta, b'l') - self.assertEqual(ba, b'hello') - self.assertEqual(c, b'hee') - c = b.translate(None, b'e') - self.assertEqual(c, b'hllo') - c = ba.translate(None, b'e') - self.assertEqual(c, b'hllo') - self.assertRaises(TypeError, b.translate, None, None) - self.assertRaises(TypeError, ba.translate, None, None) - def test_split_bytearray(self): self.assertEqual(b'a b'.split(memoryview(b' ')), [b'a', b'b']) -- cgit v1.2.1 From 7e445b494a0b2834d33648c2b2ca0f449e56319a Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Sat, 27 Aug 2016 09:42:40 -0700 Subject: Don't test for path-like bytes paths on Windows --- Lib/test/test_os.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 8c6a8c0815..dfffed2720 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2841,14 +2841,17 @@ class PathTConverterTests(unittest.TestCase): return self.path str_filename = support.TESTFN - bytes_filename = support.TESTFN.encode('ascii') + if os.name == 'nt': + bytes_fspath = bytes_filename = None + else: + bytes_filename = support.TESTFN.encode('ascii') + bytes_fspath = PathLike(bytes_filename) fd = os.open(PathLike(str_filename), os.O_WRONLY|os.O_CREAT) self.addCleanup(os.close, fd) self.addCleanup(support.unlink, support.TESTFN) int_fspath = PathLike(fd) str_fspath = PathLike(str_filename) - bytes_fspath = PathLike(bytes_filename) for name, allow_fd, extra_args, cleanup_fn in self.functions: with self.subTest(name=name): @@ -2859,6 +2862,8 @@ class PathTConverterTests(unittest.TestCase): for path in (str_filename, bytes_filename, str_fspath, bytes_fspath): + if path is None: + continue with self.subTest(name=name, path=path): result = fn(path, *extra_args) if cleanup_fn is not None: -- cgit v1.2.1 From 4cd290cccc12938ee530a41ce9c11bd79674da4e Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Sat, 27 Aug 2016 21:26:35 +0300 Subject: Issue #26027: Fix test_path_t_converter on Windows --- Lib/test/test_os.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index dfffed2720..c1e1adc8be 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2847,8 +2847,8 @@ class PathTConverterTests(unittest.TestCase): bytes_filename = support.TESTFN.encode('ascii') bytes_fspath = PathLike(bytes_filename) fd = os.open(PathLike(str_filename), os.O_WRONLY|os.O_CREAT) - self.addCleanup(os.close, fd) self.addCleanup(support.unlink, support.TESTFN) + self.addCleanup(os.close, fd) int_fspath = PathLike(fd) str_fspath = PathLike(str_filename) -- cgit v1.2.1 From 4ba2ef99caecb18be5348f1bbf8c4000c59cd884 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Mon, 29 Aug 2016 13:56:58 +0100 Subject: Issue 23229: add cmath.inf, cmath.nan, cmath.infj and cmath.nanj. --- Lib/test/test_cmath.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_cmath.py b/Lib/test/test_cmath.py index 1f884e52a2..11b0c61202 100644 --- a/Lib/test/test_cmath.py +++ b/Lib/test/test_cmath.py @@ -154,6 +154,23 @@ class CMathTests(unittest.TestCase): self.assertAlmostEqual(cmath.e, e_expected, places=9, msg="cmath.e is {}; should be {}".format(cmath.e, e_expected)) + def test_infinity_and_nan_constants(self): + self.assertEqual(cmath.inf.real, math.inf) + self.assertEqual(cmath.inf.imag, 0.0) + self.assertEqual(cmath.infj.real, 0.0) + self.assertEqual(cmath.infj.imag, math.inf) + + self.assertTrue(math.isnan(cmath.nan.real)) + self.assertEqual(cmath.nan.imag, 0.0) + self.assertEqual(cmath.nanj.real, 0.0) + self.assertTrue(math.isnan(cmath.nanj.imag)) + + # Check consistency with reprs. + self.assertEqual(repr(cmath.inf), "inf") + self.assertEqual(repr(cmath.infj), "infj") + self.assertEqual(repr(cmath.nan), "nan") + self.assertEqual(repr(cmath.nanj), "nanj") + def test_user_object(self): # Test automatic calling of __complex__ and __float__ by cmath # functions -- cgit v1.2.1 From 6d1e2c53f0cfc973e10f53c478e617e2e32fd041 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Mon, 29 Aug 2016 19:27:06 +0100 Subject: Issue #27870: A left shift of zero by a large integer no longer attempts to allocate large amounts of memory. --- Lib/test/test_long.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py index f0dd0749f7..4d293f24e0 100644 --- a/Lib/test/test_long.py +++ b/Lib/test/test_long.py @@ -878,6 +878,21 @@ class LongTest(unittest.TestCase): self.check_truediv(-x, y) self.check_truediv(-x, -y) + def test_lshift_of_zero(self): + self.assertEqual(0 << 0, 0) + self.assertEqual(0 << 10, 0) + with self.assertRaises(ValueError): + 0 << -1 + + @support.cpython_only + def test_huge_lshift_of_zero(self): + # Shouldn't try to allocate memory for a huge shift. See issue #27870. + # Other implementations may have a different boundary for overflow, + # or not raise at all. + self.assertEqual(0 << sys.maxsize, 0) + with self.assertRaises(OverflowError): + 0 << (sys.maxsize + 1) + def test_small_ints(self): for i in range(-5, 257): self.assertIs(i, i + 0) -- cgit v1.2.1 From 4846ba81c69ebc815b35a89ed54fef7b5ffb1417 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 30 Aug 2016 10:47:49 -0700 Subject: Issue #27895: Spelling fixes (Contributed by Ville Skytt?). --- Lib/asyncio/streams.py | 2 +- Lib/concurrent/futures/process.py | 2 +- Lib/concurrent/futures/thread.py | 2 +- Lib/distutils/tests/test_msvc9compiler.py | 2 +- Lib/email/contentmanager.py | 6 +++--- Lib/email/generator.py | 2 +- Lib/email/header.py | 4 ++-- Lib/email/message.py | 2 +- Lib/http/client.py | 2 +- Lib/idlelib/README.txt | 2 +- Lib/idlelib/help.html | 2 +- Lib/idlelib/idle_test/test_paragraph.py | 2 +- Lib/shutil.py | 2 +- Lib/statistics.py | 2 +- Lib/test/_test_multiprocessing.py | 2 +- Lib/test/datetimetester.py | 2 +- Lib/test/test_asyncio/test_locks.py | 4 ++-- Lib/test/test_concurrent_futures.py | 2 +- Lib/test/test_descr.py | 2 +- Lib/test/test_difflib.py | 12 ++++++------ Lib/test/test_difflib_expect.html | 12 ++++++------ Lib/test/test_email/test_email.py | 4 ++-- Lib/test/test_email/test_generator.py | 2 +- Lib/test/test_importlib/test_util.py | 2 +- Lib/test/test_ipaddress.py | 6 +++--- Lib/test/test_pep247.py | 2 +- Lib/test/test_shutil.py | 4 ++-- Lib/test/test_subprocess.py | 2 +- Lib/test/test_urllib.py | 2 +- Lib/test/test_winreg.py | 2 +- Lib/tkinter/__init__.py | 2 +- Lib/unittest/test/test_discovery.py | 2 +- Lib/unittest/test/testmock/testcallable.py | 2 +- Lib/venv/scripts/posix/activate | 2 +- Lib/venv/scripts/posix/activate.csh | 2 +- Lib/venv/scripts/posix/activate.fish | 2 +- 36 files changed, 54 insertions(+), 54 deletions(-) (limited to 'Lib') diff --git a/Lib/asyncio/streams.py b/Lib/asyncio/streams.py index c88a87cd09..b4adc7d9c6 100644 --- a/Lib/asyncio/streams.py +++ b/Lib/asyncio/streams.py @@ -590,7 +590,7 @@ class StreamReader: bytes. If the EOF was received and the internal buffer is empty, return an empty bytes object. - If n is zero, return empty bytes object immediatelly. + If n is zero, return empty bytes object immediately. If n is positive, this function try to read `n` bytes, and may return less or equal bytes than requested, but at least one byte. If EOF was diff --git a/Lib/concurrent/futures/process.py b/Lib/concurrent/futures/process.py index 590edba24e..8f1d714193 100644 --- a/Lib/concurrent/futures/process.py +++ b/Lib/concurrent/futures/process.py @@ -63,7 +63,7 @@ import traceback # interpreter to exit when there are still idle processes in a # ProcessPoolExecutor's process pool (i.e. shutdown() was not called). However, # allowing workers to die with the interpreter has two undesirable properties: -# - The workers would still be running during interpretor shutdown, +# - The workers would still be running during interpreter shutdown, # meaning that they would fail in unpredictable ways. # - The workers could be killed while evaluating a work item, which could # be bad if the callable being evaluated has external side-effects e.g. diff --git a/Lib/concurrent/futures/thread.py b/Lib/concurrent/futures/thread.py index 6266f38eb7..03d276b63f 100644 --- a/Lib/concurrent/futures/thread.py +++ b/Lib/concurrent/futures/thread.py @@ -16,7 +16,7 @@ import os # to exit when there are still idle threads in a ThreadPoolExecutor's thread # pool (i.e. shutdown() was not called). However, allowing workers to die with # the interpreter has two undesirable properties: -# - The workers would still be running during interpretor shutdown, +# - The workers would still be running during interpreter shutdown, # meaning that they would fail in unpredictable ways. # - The workers could be killed while evaluating a work item, which could # be bad if the callable being evaluated has external side-effects e.g. diff --git a/Lib/distutils/tests/test_msvc9compiler.py b/Lib/distutils/tests/test_msvc9compiler.py index 5e18c61360..77a07ef39d 100644 --- a/Lib/distutils/tests/test_msvc9compiler.py +++ b/Lib/distutils/tests/test_msvc9compiler.py @@ -125,7 +125,7 @@ class msvc9compilerTestCase(support.TempdirManager, self.assertRaises(KeyError, Reg.get_value, 'xxx', 'xxx') # looking for values that should exist on all - # windows registeries versions. + # windows registry versions. path = r'Control Panel\Desktop' v = Reg.get_value(path, 'dragfullwindows') self.assertIn(v, ('0', '1', '2')) diff --git a/Lib/email/contentmanager.py b/Lib/email/contentmanager.py index d3636529b6..099c314628 100644 --- a/Lib/email/contentmanager.py +++ b/Lib/email/contentmanager.py @@ -141,7 +141,7 @@ def _encode_base64(data, max_line_length): def _encode_text(string, charset, cte, policy): lines = string.encode(charset).splitlines() linesep = policy.linesep.encode('ascii') - def embeded_body(lines): return linesep.join(lines) + linesep + def embedded_body(lines): return linesep.join(lines) + linesep def normal_body(lines): return b'\n'.join(lines) + b'\n' if cte==None: # Use heuristics to decide on the "best" encoding. @@ -152,7 +152,7 @@ def _encode_text(string, charset, cte, policy): if (policy.cte_type == '8bit' and max(len(x) for x in lines) <= policy.max_line_length): return '8bit', normal_body(lines).decode('ascii', 'surrogateescape') - sniff = embeded_body(lines[:10]) + sniff = embedded_body(lines[:10]) sniff_qp = quoprimime.body_encode(sniff.decode('latin-1'), policy.max_line_length) sniff_base64 = binascii.b2a_base64(sniff) @@ -171,7 +171,7 @@ def _encode_text(string, charset, cte, policy): data = quoprimime.body_encode(normal_body(lines).decode('latin-1'), policy.max_line_length) elif cte == 'base64': - data = _encode_base64(embeded_body(lines), policy.max_line_length) + data = _encode_base64(embedded_body(lines), policy.max_line_length) else: raise ValueError("Unknown content transfer encoding {}".format(cte)) return cte, data diff --git a/Lib/email/generator.py b/Lib/email/generator.py index 11ff16df9a..7c3cdc96d5 100644 --- a/Lib/email/generator.py +++ b/Lib/email/generator.py @@ -97,7 +97,7 @@ class Generator: self._NL = policy.linesep self._encoded_NL = self._encode(self._NL) self._EMPTY = '' - self._encoded_EMTPY = self._encode('') + self._encoded_EMPTY = self._encode('') # Because we use clone (below) when we recursively process message # subparts, and because clone uses the computed policy (not None), # submessages will automatically get set to the computed policy when diff --git a/Lib/email/header.py b/Lib/email/header.py index 6820ea16ba..c7b2dd9f31 100644 --- a/Lib/email/header.py +++ b/Lib/email/header.py @@ -49,7 +49,7 @@ fcre = re.compile(r'[\041-\176]+:$') # Find a header embedded in a putative header value. Used to check for # header injection attack. -_embeded_header = re.compile(r'\n[^ \t]+:') +_embedded_header = re.compile(r'\n[^ \t]+:') @@ -385,7 +385,7 @@ class Header: if self._chunks: formatter.add_transition() value = formatter._str(linesep) - if _embeded_header.search(value): + if _embedded_header.search(value): raise HeaderParseError("header value appears to contain " "an embedded header: {!r}".format(value)) return value diff --git a/Lib/email/message.py b/Lib/email/message.py index aefaf57d00..65bb237752 100644 --- a/Lib/email/message.py +++ b/Lib/email/message.py @@ -1043,7 +1043,7 @@ class MIMEPart(Message): yield from parts return # Otherwise we more or less invert the remaining logic in get_body. - # This only really works in edge cases (ex: non-text relateds or + # This only really works in edge cases (ex: non-text related or # alternatives) if the sending agent sets content-disposition. seen = [] # Only skip the first example of each candidate type. for part in parts: diff --git a/Lib/http/client.py b/Lib/http/client.py index 9d5cf4518f..9107412922 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -136,7 +136,7 @@ _MAXHEADERS = 100 # # VCHAR defined in http://tools.ietf.org/html/rfc5234#appendix-B.1 -# the patterns for both name and value are more leniant than RFC +# the patterns for both name and value are more lenient than RFC # definitions to allow for backwards compatibility _is_legal_header_name = re.compile(rb'[^:\s][^:\r\n]*').fullmatch _is_illegal_header_value = re.compile(rb'\n(?![ \t])|\r(?![ \t\n])').search diff --git a/Lib/idlelib/README.txt b/Lib/idlelib/README.txt index d333b47633..f7aad68ae3 100644 --- a/Lib/idlelib/README.txt +++ b/Lib/idlelib/README.txt @@ -65,7 +65,7 @@ pathbrowser.py # Create path browser window. percolator.py # Manage delegator stack (nim). pyparse.py # Give information on code indentation pyshell.py # Start IDLE, manage shell, complete editor window -query.py # Query user for informtion +query.py # Query user for information redirector.py # Intercept widget subcommands (for percolator) (nim). replace.py # Search and replace pattern in text. rpc.py # Commuicate between idle and user processes (nim). diff --git a/Lib/idlelib/help.html b/Lib/idlelib/help.html index 1357289fff..b2d8fdcbb9 100644 --- a/Lib/idlelib/help.html +++ b/Lib/idlelib/help.html @@ -497,7 +497,7 @@ functions to be used from IDLE’s Python shell.

-c command run command in the shell window -d enable debugger and open shell window -e open editor window --h print help message with legal combinatios and exit +-h print help message with legal combinations and exit -i open shell window -r file run file in shell window -s run $IDLESTARTUP or $PYTHONSTARTUP first, in shell window diff --git a/Lib/idlelib/idle_test/test_paragraph.py b/Lib/idlelib/idle_test/test_paragraph.py index 4741eb87be..ba350c9765 100644 --- a/Lib/idlelib/idle_test/test_paragraph.py +++ b/Lib/idlelib/idle_test/test_paragraph.py @@ -159,7 +159,7 @@ class FindTest(unittest.TestCase): class ReformatFunctionTest(unittest.TestCase): """Test the reformat_paragraph function without the editor window.""" - def test_reformat_paragrah(self): + def test_reformat_paragraph(self): Equal = self.assertEqual reform = fp.reformat_paragraph hw = "O hello world" diff --git a/Lib/shutil.py b/Lib/shutil.py index ac04cc593a..9d193b567c 100644 --- a/Lib/shutil.py +++ b/Lib/shutil.py @@ -64,7 +64,7 @@ class ReadError(OSError): class RegistryError(Exception): """Raised when a registry operation with the archiving - and unpacking registeries fails""" + and unpacking registries fails""" def copyfileobj(fsrc, fdst, length=16*1024): diff --git a/Lib/statistics.py b/Lib/statistics.py index 40c72db0c0..7d53e0c0e2 100644 --- a/Lib/statistics.py +++ b/Lib/statistics.py @@ -454,7 +454,7 @@ class _nroot_NS: """Return the nth root of a positive huge number.""" assert x > 0 # I state without proof that ⁿ√x ≈ ⁿ√2·ⁿ√(x//2) - # and that for sufficiently big x the error is acceptible. + # and that for sufficiently big x the error is acceptable. # We now halve x until it is small enough to get the root. m = 0 while True: diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 16407db7b3..cfd801e55c 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -26,7 +26,7 @@ import test.support.script_helper _multiprocessing = test.support.import_module('_multiprocessing') # Skip tests if sem_open implementation is broken. test.support.import_module('multiprocessing.synchronize') -# import threading after _multiprocessing to raise a more revelant error +# import threading after _multiprocessing to raise a more relevant error # message: "No module named _multiprocessing". _multiprocessing is not compiled # without thread support. import threading diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index e21d487a12..86c937388e 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -3958,7 +3958,7 @@ class Oddballs(unittest.TestCase): self.assertRaises(TypeError, lambda: as_date >= as_datetime) self.assertRaises(TypeError, lambda: as_datetime >= as_date) - # Neverthelss, comparison should work with the base-class (date) + # Nevertheless, comparison should work with the base-class (date) # projection if use of a date method is forced. self.assertEqual(as_date.__eq__(as_datetime), True) different_day = (as_date.day + 1) % 20 + 1 diff --git a/Lib/test/test_asyncio/test_locks.py b/Lib/test/test_asyncio/test_locks.py index d3bdc51385..e557212f96 100644 --- a/Lib/test/test_asyncio/test_locks.py +++ b/Lib/test/test_asyncio/test_locks.py @@ -130,8 +130,8 @@ class LockTests(test_utils.TestCase): def test_cancel_race(self): # Several tasks: # - A acquires the lock - # - B is blocked in aqcuire() - # - C is blocked in aqcuire() + # - B is blocked in acquire() + # - C is blocked in acquire() # # Now, concurrently: # - B is cancelled diff --git a/Lib/test/test_concurrent_futures.py b/Lib/test/test_concurrent_futures.py index 46b069c59d..23e95b2124 100644 --- a/Lib/test/test_concurrent_futures.py +++ b/Lib/test/test_concurrent_futures.py @@ -4,7 +4,7 @@ import test.support test.support.import_module('_multiprocessing') # Skip tests if sem_open implementation is broken. test.support.import_module('multiprocessing.synchronize') -# import threading after _multiprocessing to raise a more revelant error +# import threading after _multiprocessing to raise a more relevant error # message: "No module named _multiprocessing". _multiprocessing is not compiled # without thread support. test.support.import_module('threading') diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 0a5ecd5a0d..0950b8e47e 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -876,7 +876,7 @@ class ClassPropertiesAndMethods(unittest.TestCase): self.assertEqual(Frag().__int__(), 42) self.assertEqual(int(Frag()), 42) - def test_diamond_inheritence(self): + def test_diamond_inheritance(self): # Testing multiple inheritance special cases... class A(object): def spam(self): return "A" diff --git a/Lib/test/test_difflib.py b/Lib/test/test_difflib.py index ab9debf8e2..156b523c38 100644 --- a/Lib/test/test_difflib.py +++ b/Lib/test/test_difflib.py @@ -122,17 +122,17 @@ patch914575_nonascii_to1 = """ """ patch914575_from2 = """ -\t\tLine 1: preceeded by from:[tt] to:[ssss] - \t\tLine 2: preceeded by from:[sstt] to:[sssst] - \t \tLine 3: preceeded by from:[sstst] to:[ssssss] +\t\tLine 1: preceded by from:[tt] to:[ssss] + \t\tLine 2: preceded by from:[sstt] to:[sssst] + \t \tLine 3: preceded by from:[sstst] to:[ssssss] Line 4: \thas from:[sst] to:[sss] after : Line 5: has from:[t] to:[ss] at end\t """ patch914575_to2 = """ - Line 1: preceeded by from:[tt] to:[ssss] - \tLine 2: preceeded by from:[sstt] to:[sssst] - Line 3: preceeded by from:[sstst] to:[ssssss] + Line 1: preceded by from:[tt] to:[ssss] + \tLine 2: preceded by from:[sstt] to:[sssst] + Line 3: preceded by from:[sstst] to:[ssssss] Line 4: has from:[sst] to:[sss] after : Line 5: has from:[t] to:[ss] at end """ diff --git a/Lib/test/test_difflib_expect.html b/Lib/test/test_difflib_expect.html index ea7a24ef4b..3e6a7b7a99 100644 --- a/Lib/test/test_difflib_expect.html +++ b/Lib/test/test_difflib_expect.html @@ -387,9 +387,9 @@ f1f1 - t2    Line 1: preceeded by from:[tt] to:[ssss]t2    Line 1: preceeded by from:[tt] to:[ssss] - 3      Line 2: preceeded by from:[sstt] to:[sssst]3      Line 2: preceeded by from:[sstt] to:[sssst] - 4      Line 3: preceeded by from:[sstst] to:[ssssss]4      Line 3: preceeded by from:[sstst] to:[ssssss] + t2    Line 1: preceded by from:[tt] to:[ssss]t2    Line 1: preceded by from:[tt] to:[ssss] + 3      Line 2: preceded by from:[sstt] to:[sssst]3      Line 2: preceded by from:[sstt] to:[sssst] + 4      Line 3: preceded by from:[sstst] to:[ssssss]4      Line 3: preceded by from:[sstst] to:[ssssss] 5Line 4:   has from:[sst] to:[sss] after :5Line 4:   has from:[sst] to:[sss] after : 6Line 5: has from:[t] to:[ss] at end 6Line 5: has from:[t] to:[ss] at end @@ -403,9 +403,9 @@ f1f1 - t2                Line 1: preceeded by from:[tt] to:[ssss]t2    Line 1: preceeded by from:[tt] to:[ssss] - 3                Line 2: preceeded by from:[sstt] to:[sssst]3        Line 2: preceeded by from:[sstt] to:[sssst] - 4                Line 3: preceeded by from:[sstst] to:[ssssss]4      Line 3: preceeded by from:[sstst] to:[ssssss] + t2                Line 1: preceded by from:[tt] to:[ssss]t2    Line 1: preceded by from:[tt] to:[ssss] + 3                Line 2: preceded by from:[sstt] to:[sssst]3        Line 2: preceded by from:[sstt] to:[sssst] + 4                Line 3: preceded by from:[sstst] to:[ssssss]4      Line 3: preceded by from:[sstst] to:[ssssss] 5Line 4:         has from:[sst] to:[sss] after :5Line 4:   has from:[sst] to:[sss] after : 6Line 5: has from:[t] to:[ss] at end     6Line 5: has from:[t] to:[ss] at end diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py index 8a7e06e4ad..8aaca01dba 100644 --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -723,12 +723,12 @@ class TestMessageAPI(TestEmailBase): # Issue 5871: reject an attempt to embed a header inside a header value # (header injection attack). - def test_embeded_header_via_Header_rejected(self): + def test_embedded_header_via_Header_rejected(self): msg = Message() msg['Dummy'] = Header('dummy\nX-Injected-Header: test') self.assertRaises(errors.HeaderParseError, msg.as_string) - def test_embeded_header_via_string_rejected(self): + def test_embedded_header_via_string_rejected(self): msg = Message() msg['Dummy'] = 'dummy\nX-Injected-Header: test' self.assertRaises(errors.HeaderParseError, msg.as_string) diff --git a/Lib/test/test_email/test_generator.py b/Lib/test/test_email/test_generator.py index b1cbce26d5..7c8877fdcb 100644 --- a/Lib/test/test_email/test_generator.py +++ b/Lib/test/test_email/test_generator.py @@ -143,7 +143,7 @@ class TestGeneratorBase: def test_set_mangle_from_via_policy(self): source = textwrap.dedent("""\ Subject: test that - from is mangeld in the body! + from is mangled in the body! From time to time I write a rhyme. """) diff --git a/Lib/test/test_importlib/test_util.py b/Lib/test/test_importlib/test_util.py index 69466b2e77..41ca3332d5 100644 --- a/Lib/test/test_importlib/test_util.py +++ b/Lib/test/test_importlib/test_util.py @@ -372,7 +372,7 @@ class ResolveNameTests: # bacon self.assertEqual('bacon', self.util.resolve_name('bacon', None)) - def test_aboslute_within_package(self): + def test_absolute_within_package(self): # bacon in spam self.assertEqual('bacon', self.util.resolve_name('bacon', 'spam')) diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py index 2e31f4289a..5f08f0c295 100644 --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -1263,7 +1263,7 @@ class IpaddrUnitTest(unittest.TestCase): ip4 = ipaddress.IPv4Address('1.1.1.3') ip5 = ipaddress.IPv4Address('1.1.1.4') ip6 = ipaddress.IPv4Address('1.1.1.0') - # check that addreses are subsumed properly. + # check that addresses are subsumed properly. collapsed = ipaddress.collapse_addresses( [ip1, ip2, ip3, ip4, ip5, ip6]) self.assertEqual(list(collapsed), @@ -1277,7 +1277,7 @@ class IpaddrUnitTest(unittest.TestCase): ip4 = ipaddress.IPv4Address('1.1.1.3') #ip5 = ipaddress.IPv4Interface('1.1.1.4/30') #ip6 = ipaddress.IPv4Interface('1.1.1.4/30') - # check that addreses are subsumed properly. + # check that addresses are subsumed properly. collapsed = ipaddress.collapse_addresses([ip1, ip2, ip3, ip4]) self.assertEqual(list(collapsed), [ipaddress.IPv4Network('1.1.1.0/30')]) @@ -1291,7 +1291,7 @@ class IpaddrUnitTest(unittest.TestCase): # stored in no particular order b/c we want CollapseAddr to call # [].sort ip6 = ipaddress.IPv4Network('1.1.0.0/22') - # check that addreses are subsumed properly. + # check that addresses are subsumed properly. collapsed = ipaddress.collapse_addresses([ip1, ip2, ip3, ip4, ip5, ip6]) self.assertEqual(list(collapsed), diff --git a/Lib/test/test_pep247.py b/Lib/test/test_pep247.py index ab5f41894b..c17ceed810 100644 --- a/Lib/test/test_pep247.py +++ b/Lib/test/test_pep247.py @@ -1,5 +1,5 @@ """ -Test suite to check compilance with PEP 247, the standard API +Test suite to check compliance with PEP 247, the standard API for hashing algorithms """ diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index 1d5e01a92d..90a31d7b18 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -1306,10 +1306,10 @@ class TestShutil(unittest.TestCase): shutil.chown(filename) with self.assertRaises(LookupError): - shutil.chown(filename, user='non-exising username') + shutil.chown(filename, user='non-existing username') with self.assertRaises(LookupError): - shutil.chown(filename, group='non-exising groupname') + shutil.chown(filename, group='non-existing groupname') with self.assertRaises(TypeError): shutil.chown(filename, b'spam') diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 154e3300ed..2bfb69cbfe 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -682,7 +682,7 @@ class ProcessTestCase(BaseTestCase): self.assertEqual(stdout, "banana") self.assertStderrEqual(stderr.encode(), b"pineapple\npear\n") - def test_communicate_timeout_large_ouput(self): + def test_communicate_timeout_large_output(self): # Test an expiring timeout while the child is outputting lots of data. p = subprocess.Popen([sys.executable, "-c", 'import sys,os,time;' diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py index c26c52a6c5..247598ac57 100644 --- a/Lib/test/test_urllib.py +++ b/Lib/test/test_urllib.py @@ -1,4 +1,4 @@ -"""Regresssion tests for what was in Python 2's "urllib" module""" +"""Regression tests for what was in Python 2's "urllib" module""" import urllib.parse import urllib.request diff --git a/Lib/test/test_winreg.py b/Lib/test/test_winreg.py index ef40e8bc37..d642b13f68 100644 --- a/Lib/test/test_winreg.py +++ b/Lib/test/test_winreg.py @@ -169,7 +169,7 @@ class BaseWinregTests(unittest.TestCase): DeleteKey(key, subkeystr) try: - # Shouldnt be able to delete it twice! + # Shouldn't be able to delete it twice! DeleteKey(key, subkeystr) self.fail("Deleting the key twice succeeded") except OSError: diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index 35643e646b..99ad2a7c01 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -245,7 +245,7 @@ class Event: if self.delta == 0: del attrs['delta'] # widget usually is known - # serial and time are not very interesing + # serial and time are not very interesting # keysym_num duplicates keysym # x_root and y_root mostly duplicate x and y keys = ('send_event', diff --git a/Lib/unittest/test/test_discovery.py b/Lib/unittest/test/test_discovery.py index 8f4017f667..1996a8ee5d 100644 --- a/Lib/unittest/test/test_discovery.py +++ b/Lib/unittest/test/test_discovery.py @@ -349,7 +349,7 @@ class TestDiscovery(unittest.TestCase): suite = list(loader._find_tests(abspath('/foo'), 'test*.py')) # We should have loaded tests from both my_package and - # my_pacakge.test_module, and also run the load_tests hook in both. + # my_package.test_module, and also run the load_tests hook in both. # (normally this would be nested TestSuites.) self.assertEqual(suite, [['my_package load_tests', [], diff --git a/Lib/unittest/test/testmock/testcallable.py b/Lib/unittest/test/testmock/testcallable.py index 5390a4e10f..af1ce7ebba 100644 --- a/Lib/unittest/test/testmock/testcallable.py +++ b/Lib/unittest/test/testmock/testcallable.py @@ -27,7 +27,7 @@ class TestCallable(unittest.TestCase): self.assertIn(mock.__class__.__name__, repr(mock)) - def test_heirarchy(self): + def test_hierarchy(self): self.assertTrue(issubclass(MagicMock, Mock)) self.assertTrue(issubclass(NonCallableMagicMock, NonCallableMock)) diff --git a/Lib/venv/scripts/posix/activate b/Lib/venv/scripts/posix/activate index 7bbffd9ba6..c78a4efa15 100644 --- a/Lib/venv/scripts/posix/activate +++ b/Lib/venv/scripts/posix/activate @@ -34,7 +34,7 @@ deactivate () { fi } -# unset irrelavent variables +# unset irrelevant variables deactivate nondestructive VIRTUAL_ENV="__VENV_DIR__" diff --git a/Lib/venv/scripts/posix/activate.csh b/Lib/venv/scripts/posix/activate.csh index 99d79e0138..b0c7028a92 100644 --- a/Lib/venv/scripts/posix/activate.csh +++ b/Lib/venv/scripts/posix/activate.csh @@ -5,7 +5,7 @@ alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; test "\!:*" != "nondestructive" && unalias deactivate' -# Unset irrelavent variables. +# Unset irrelevant variables. deactivate nondestructive setenv VIRTUAL_ENV "__VENV_DIR__" diff --git a/Lib/venv/scripts/posix/activate.fish b/Lib/venv/scripts/posix/activate.fish index 45391aa01c..ca98466148 100644 --- a/Lib/venv/scripts/posix/activate.fish +++ b/Lib/venv/scripts/posix/activate.fish @@ -29,7 +29,7 @@ function deactivate -d "Exit virtualenv and return to normal shell environment" end end -# unset irrelavent variables +# unset irrelevant variables deactivate nondestructive set -gx VIRTUAL_ENV "__VENV_DIR__" -- cgit v1.2.1 From 1b667b4bfc00b79b84bb3801f7a22ce579de166d Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 30 Aug 2016 12:35:50 -0700 Subject: Issue #27842: The csv.DictReader now returns rows of type OrderedDict. --- Lib/csv.py | 3 ++- Lib/test/test_csv.py | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/csv.py b/Lib/csv.py index 90461dbe1e..2e2303a28e 100644 --- a/Lib/csv.py +++ b/Lib/csv.py @@ -11,6 +11,7 @@ from _csv import Error, __version__, writer, reader, register_dialect, \ __doc__ from _csv import Dialect as _Dialect +from collections import OrderedDict from io import StringIO __all__ = ["QUOTE_MINIMAL", "QUOTE_ALL", "QUOTE_NONNUMERIC", "QUOTE_NONE", @@ -116,7 +117,7 @@ class DictReader: # values while row == []: row = next(self.reader) - d = dict(zip(self.fieldnames, row)) + d = OrderedDict(zip(self.fieldnames, row)) lf = len(self.fieldnames) lr = len(row) if lf < lr: diff --git a/Lib/test/test_csv.py b/Lib/test/test_csv.py index e97c9f366e..9df408048b 100644 --- a/Lib/test/test_csv.py +++ b/Lib/test/test_csv.py @@ -10,6 +10,7 @@ import csv import gc import pickle from test import support +from itertools import permutations class Test_Csv(unittest.TestCase): """ @@ -1092,6 +1093,21 @@ class TestUnicode(unittest.TestCase): fileobj.seek(0) self.assertEqual(fileobj.read(), expected) +class KeyOrderingTest(unittest.TestCase): + + def test_ordering_for_the_dict_reader_and_writer(self): + resultset = set() + for keys in permutations("abcde"): + with TemporaryFile('w+', newline='', encoding="utf-8") as fileobject: + dw = csv.DictWriter(fileobject, keys) + dw.writeheader() + fileobject.seek(0) + dr = csv.DictReader(fileobject) + kt = tuple(dr.fieldnames) + self.assertEqual(keys, kt) + resultset.add(kt) + # Final sanity check: were all permutations unique? + self.assertEqual(len(resultset), 120, "Key ordering: some key permutations not collected (expected 120)") class MiscTestCase(unittest.TestCase): def test__all__(self): -- cgit v1.2.1 From cdb66f1ee299e53bc29668671db411eb6c8f9c8d Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 30 Aug 2016 12:57:26 -0700 Subject: Issue #27895: Strengthen the dict reader tests. --- Lib/test/test_csv.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_csv.py b/Lib/test/test_csv.py index 9df408048b..7dcea9ccb3 100644 --- a/Lib/test/test_csv.py +++ b/Lib/test/test_csv.py @@ -11,6 +11,8 @@ import gc import pickle from test import support from itertools import permutations +from textwrap import dedent +from collections import OrderedDict class Test_Csv(unittest.TestCase): """ @@ -1109,6 +1111,42 @@ class KeyOrderingTest(unittest.TestCase): # Final sanity check: were all permutations unique? self.assertEqual(len(resultset), 120, "Key ordering: some key permutations not collected (expected 120)") + def test_ordered_dict_reader(self): + data = dedent('''\ + FirstName,LastName + Eric,Idle + Graham,Chapman,Over1,Over2 + + Under1 + John,Cleese + ''').splitlines() + + self.assertEqual(list(csv.DictReader(data)), + [OrderedDict([('FirstName', 'Eric'), ('LastName', 'Idle')]), + OrderedDict([('FirstName', 'Graham'), ('LastName', 'Chapman'), + (None, ['Over1', 'Over2'])]), + OrderedDict([('FirstName', 'Under1'), ('LastName', None)]), + OrderedDict([('FirstName', 'John'), ('LastName', 'Cleese')]), + ]) + + self.assertEqual(list(csv.DictReader(data, restkey='OtherInfo')), + [OrderedDict([('FirstName', 'Eric'), ('LastName', 'Idle')]), + OrderedDict([('FirstName', 'Graham'), ('LastName', 'Chapman'), + ('OtherInfo', ['Over1', 'Over2'])]), + OrderedDict([('FirstName', 'Under1'), ('LastName', None)]), + OrderedDict([('FirstName', 'John'), ('LastName', 'Cleese')]), + ]) + + del data[0] # Remove the header row + self.assertEqual(list(csv.DictReader(data, fieldnames=['fname', 'lname'])), + [OrderedDict([('fname', 'Eric'), ('lname', 'Idle')]), + OrderedDict([('fname', 'Graham'), ('lname', 'Chapman'), + (None, ['Over1', 'Over2'])]), + OrderedDict([('fname', 'Under1'), ('lname', None)]), + OrderedDict([('fname', 'John'), ('lname', 'Cleese')]), + ]) + + class MiscTestCase(unittest.TestCase): def test__all__(self): extra = {'__doc__', '__version__'} -- cgit v1.2.1 From 89c8100b3da772b1394e841e5b524f7b4b3147a0 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Tue, 30 Aug 2016 20:19:13 -0400 Subject: Issue #17642: add larger font sizes for classroom projection. --- Lib/idlelib/configdialog.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index fda655f5d7..2f361bdbba 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -1018,7 +1018,8 @@ class ConfigDialog(Toplevel): pass ##font size dropdown self.optMenuFontSize.SetMenu(('7', '8', '9', '10', '11', '12', '13', - '14', '16', '18', '20', '22'), fontSize ) + '14', '16', '18', '20', '22', + '25', '29', '34', '40'), fontSize ) ##fontWeight self.fontBold.set(fontBold) ##font sample -- cgit v1.2.1 From 03c72086eea7efd488f3d7eb42f383cc81bd9a6b Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Wed, 31 Aug 2016 00:50:55 -0400 Subject: Issue #27891: Consistently group and sort imports within idlelib modules. --- Lib/idlelib/README.txt | 21 +++++++++++++++++- Lib/idlelib/autocomplete.py | 17 ++++++++------- Lib/idlelib/autocomplete_w.py | 3 ++- Lib/idlelib/autoexpand.py | 3 +-- Lib/idlelib/browser.py | 6 +++--- Lib/idlelib/calltips.py | 2 +- Lib/idlelib/codecontext.py | 6 ++++-- Lib/idlelib/colorizer.py | 9 ++++---- Lib/idlelib/config.py | 2 +- Lib/idlelib/configdialog.py | 6 +++--- Lib/idlelib/debugger.py | 8 ++++--- Lib/idlelib/debugobj.py | 3 +-- Lib/idlelib/dynoption.py | 1 + Lib/idlelib/editor.py | 41 ++++++++++++++++++------------------ Lib/idlelib/filelist.py | 1 + Lib/idlelib/grep.py | 11 ++++++---- Lib/idlelib/help.py | 2 ++ Lib/idlelib/help_about.py | 5 ++++- Lib/idlelib/history.py | 4 +++- Lib/idlelib/hyperparser.py | 5 ++--- Lib/idlelib/idle_test/test_iomenu.py | 5 +++-- Lib/idlelib/macosx.py | 3 ++- Lib/idlelib/multicall.py | 4 ++-- Lib/idlelib/outwin.py | 7 ++++-- Lib/idlelib/paragraph.py | 4 +++- Lib/idlelib/parenmatch.py | 1 - Lib/idlelib/pathbrowser.py | 7 ++++-- Lib/idlelib/percolator.py | 2 +- Lib/idlelib/pyparse.py | 2 +- Lib/idlelib/pyshell.py | 24 ++++++++++----------- Lib/idlelib/query.py | 3 ++- Lib/idlelib/replace.py | 6 +++--- Lib/idlelib/rpc.py | 26 ++++++++++++++--------- Lib/idlelib/run.py | 24 ++++++++++----------- Lib/idlelib/runscript.py | 3 ++- Lib/idlelib/scrolledlist.py | 4 +++- Lib/idlelib/search.py | 1 + Lib/idlelib/searchbase.py | 1 + Lib/idlelib/searchengine.py | 3 +++ Lib/idlelib/stackviewer.py | 11 +++++++--- Lib/idlelib/statusbar.py | 2 ++ Lib/idlelib/tabbedpages.py | 3 +++ Lib/idlelib/textview.py | 2 +- Lib/idlelib/tree.py | 4 +++- Lib/idlelib/undo.py | 7 +++--- Lib/idlelib/windows.py | 2 ++ Lib/idlelib/zoomheight.py | 2 ++ 47 files changed, 197 insertions(+), 122 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/README.txt b/Lib/idlelib/README.txt index f7aad68ae3..e52b5cd7b2 100644 --- a/Lib/idlelib/README.txt +++ b/Lib/idlelib/README.txt @@ -228,4 +228,23 @@ Help Center Insert # eEW.center_insert_event - + + +CODE STYLE -- Generally PEP 8. + +import +------ +Put import at the top, unless there is a good reason otherwise. +PEP 8 says to group stdlib, 3rd-party dependencies, and package imports. +For idlelib, the groups are general stdlib, tkinter, and idlelib. +Sort modules within each group, except that tkinter.ttk follows tkinter. +Sort 'from idlelib import mod1' and 'from idlelib.mod2 import object' +together by module, ignoring within module objects. +Put 'import __main__' after other idlelib imports. + +Imports only needed for testing are not at the top but are put in the +htest function def or the "if __name__ == '__main__'" clause. + +Within module imports like "from idlelib.mod import class" may cause +circular imports to deadlock. Even without this, circular imports may +require at least one of the imports to be delayed until a function call. diff --git a/Lib/idlelib/autocomplete.py b/Lib/idlelib/autocomplete.py index 1200008ba9..1e44fa5bc6 100644 --- a/Lib/idlelib/autocomplete.py +++ b/Lib/idlelib/autocomplete.py @@ -4,26 +4,27 @@ This extension can complete either attribute names or file names. It can pop a window with all available names, for the user to select from. """ import os -import sys import string +import sys -from idlelib.config import idleConf - -# This string includes all chars that may be in an identifier -ID_CHARS = string.ascii_letters + string.digits + "_" - -# These constants represent the two different types of completions +# These constants represent the two different types of completions. +# They must be defined here so autocomple_w can import them. COMPLETE_ATTRIBUTES, COMPLETE_FILES = range(1, 2+1) from idlelib import autocomplete_w +from idlelib.config import idleConf from idlelib.hyperparser import HyperParser - import __main__ +# This string includes all chars that may be in an identifier. +# TODO Update this here and elsewhere. +ID_CHARS = string.ascii_letters + string.digits + "_" + SEPS = os.sep if os.altsep: # e.g. '/' on Windows... SEPS += os.altsep + class AutoComplete: menudefs = [ diff --git a/Lib/idlelib/autocomplete_w.py b/Lib/idlelib/autocomplete_w.py index 31837e0740..3374c6e945 100644 --- a/Lib/idlelib/autocomplete_w.py +++ b/Lib/idlelib/autocomplete_w.py @@ -3,8 +3,9 @@ An auto-completion window for IDLE, used by the autocomplete extension """ from tkinter import * from tkinter.ttk import Scrollbar -from idlelib.multicall import MC_SHIFT + from idlelib.autocomplete import COMPLETE_FILES, COMPLETE_ATTRIBUTES +from idlelib.multicall import MC_SHIFT HIDE_VIRTUAL_EVENT_NAME = "<>" HIDE_SEQUENCES = ("", "") diff --git a/Lib/idlelib/autoexpand.py b/Lib/idlelib/autoexpand.py index 719060765b..6b46bee69c 100644 --- a/Lib/idlelib/autoexpand.py +++ b/Lib/idlelib/autoexpand.py @@ -12,8 +12,8 @@ its state. This is an extension file and there is only one instance of AutoExpand. ''' -import string import re +import string ###$ event <> ###$ win @@ -100,7 +100,6 @@ class AutoExpand: i = i-1 return line[i:] - if __name__ == '__main__': import unittest unittest.main('idlelib.idle_test.test_autoexpand', verbosity=2) diff --git a/Lib/idlelib/browser.py b/Lib/idlelib/browser.py index 9968333c24..ea05638df1 100644 --- a/Lib/idlelib/browser.py +++ b/Lib/idlelib/browser.py @@ -11,13 +11,13 @@ XXX TO DO: """ import os -import sys import pyclbr +import sys +from idlelib.config import idleConf from idlelib import pyshell -from idlelib.windows import ListedToplevel from idlelib.tree import TreeNode, TreeItem, ScrolledCanvas -from idlelib.config import idleConf +from idlelib.windows import ListedToplevel file_open = None # Method...Item and Class...Item use this. # Normally pyshell.flist.open, but there is no pyshell.flist for htest. diff --git a/Lib/idlelib/calltips.py b/Lib/idlelib/calltips.py index 3a9b1c6776..abcc1427c4 100644 --- a/Lib/idlelib/calltips.py +++ b/Lib/idlelib/calltips.py @@ -5,7 +5,6 @@ parameter and docstring information when you type an opening parenthesis, and which disappear when you type a closing parenthesis. """ -import __main__ import inspect import re import sys @@ -14,6 +13,7 @@ import types from idlelib import calltip_w from idlelib.hyperparser import HyperParser +import __main__ class CallTips: diff --git a/Lib/idlelib/codecontext.py b/Lib/idlelib/codecontext.py index 2a21a1f84a..f25e1b33a0 100644 --- a/Lib/idlelib/codecontext.py +++ b/Lib/idlelib/codecontext.py @@ -9,10 +9,12 @@ variable in the codecontext section of config-extensions.def. Lines which do not open blocks are not shown in the context hints pane. """ -import tkinter -from tkinter.constants import TOP, LEFT, X, W, SUNKEN import re from sys import maxsize as INFINITY + +import tkinter +from tkinter.constants import TOP, LEFT, X, W, SUNKEN + from idlelib.config import idleConf BLOCKOPENERS = {"class", "def", "elif", "else", "except", "finally", "for", diff --git a/Lib/idlelib/colorizer.py b/Lib/idlelib/colorizer.py index f5dd03d092..7310bb2cc8 100644 --- a/Lib/idlelib/colorizer.py +++ b/Lib/idlelib/colorizer.py @@ -1,9 +1,10 @@ -import time -import re -import keyword import builtins -from idlelib.delegator import Delegator +import keyword +import re +import time + from idlelib.config import idleConf +from idlelib.delegator import Delegator DEBUG = False diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index d2f0b139b5..10fe3baceb 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -18,10 +18,10 @@ configuration problem notification and resolution. """ # TODOs added Oct 2014, tjr +from configparser import ConfigParser import os import sys -from configparser import ConfigParser from tkinter.font import Font, nametofont class InvalidConfigType(Exception): pass diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index 2f361bdbba..fa47aaad14 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -11,17 +11,17 @@ Refer to comments in EditorWindow autoindent code for details. """ from tkinter import * from tkinter.ttk import Scrollbar -import tkinter.messagebox as tkMessageBox import tkinter.colorchooser as tkColorChooser import tkinter.font as tkFont +import tkinter.messagebox as tkMessageBox from idlelib.config import idleConf -from idlelib.dynoption import DynOptionMenu from idlelib.config_key import GetKeysDialog +from idlelib.dynoption import DynOptionMenu +from idlelib import macosx from idlelib.query import SectionName, HelpSource from idlelib.tabbedpages import TabbedPageSet from idlelib.textview import view_text -from idlelib import macosx class ConfigDialog(Toplevel): diff --git a/Lib/idlelib/debugger.py b/Lib/idlelib/debugger.py index ea393b12c0..114d0d128e 100644 --- a/Lib/idlelib/debugger.py +++ b/Lib/idlelib/debugger.py @@ -1,10 +1,12 @@ -import os import bdb +import os + from tkinter import * from tkinter.ttk import Scrollbar -from idlelib.windows import ListedToplevel -from idlelib.scrolledlist import ScrolledList + from idlelib import macosx +from idlelib.scrolledlist import ScrolledList +from idlelib.windows import ListedToplevel class Idb(bdb.Bdb): diff --git a/Lib/idlelib/debugobj.py b/Lib/idlelib/debugobj.py index c116fcda23..b70b13cec4 100644 --- a/Lib/idlelib/debugobj.py +++ b/Lib/idlelib/debugobj.py @@ -8,11 +8,10 @@ # XXX TO DO: # - for classes/modules, add "open source" to object browser +from reprlib import Repr from idlelib.tree import TreeItem, TreeNode, ScrolledCanvas -from reprlib import Repr - myrepr = Repr() myrepr.maxstring = 100 myrepr.maxother = 100 diff --git a/Lib/idlelib/dynoption.py b/Lib/idlelib/dynoption.py index 962f2c30d9..9c6ffa435a 100644 --- a/Lib/idlelib/dynoption.py +++ b/Lib/idlelib/dynoption.py @@ -3,6 +3,7 @@ OptionMenu widget modified to allow dynamic menu reconfiguration and setting of highlightthickness """ import copy + from tkinter import OptionMenu, _setit, StringVar, Button class DynOptionMenu(OptionMenu): diff --git a/Lib/idlelib/editor.py b/Lib/idlelib/editor.py index 7372ecf2d7..ae475cb9f9 100644 --- a/Lib/idlelib/editor.py +++ b/Lib/idlelib/editor.py @@ -6,24 +6,28 @@ import platform import re import string import sys +import tokenize +import traceback +import webbrowser + from tkinter import * from tkinter.ttk import Scrollbar import tkinter.simpledialog as tkSimpleDialog import tkinter.messagebox as tkMessageBox -import traceback -import webbrowser +from idlelib.config import idleConf +from idlelib import configdialog +from idlelib import grep +from idlelib import help +from idlelib import help_about +from idlelib import macosx from idlelib.multicall import MultiCallCreator +from idlelib import pyparse from idlelib import query -from idlelib import windows -from idlelib import search -from idlelib import grep from idlelib import replace -from idlelib import pyparse -from idlelib.config import idleConf -from idlelib import help_about, textview, configdialog -from idlelib import macosx -from idlelib import help +from idlelib import search +from idlelib import textview +from idlelib import windows # The default tab setting for a Text widget, in average-width characters. TK_TABWIDTH_DEFAULT = 8 @@ -1515,9 +1519,6 @@ def classifyws(s, tabwidth): break return raw, effective -import tokenize -_tokenize = tokenize -del tokenize class IndentSearcher(object): @@ -1542,8 +1543,8 @@ class IndentSearcher(object): return self.text.get(mark, mark + " lineend+1c") def tokeneater(self, type, token, start, end, line, - INDENT=_tokenize.INDENT, - NAME=_tokenize.NAME, + INDENT=tokenize.INDENT, + NAME=tokenize.NAME, OPENERS=('class', 'def', 'for', 'if', 'try', 'while')): if self.finished: pass @@ -1554,19 +1555,19 @@ class IndentSearcher(object): self.finished = 1 def run(self): - save_tabsize = _tokenize.tabsize - _tokenize.tabsize = self.tabwidth + save_tabsize = tokenize.tabsize + tokenize.tabsize = self.tabwidth try: try: - tokens = _tokenize.generate_tokens(self.readline) + tokens = tokenize.generate_tokens(self.readline) for token in tokens: self.tokeneater(*token) - except (_tokenize.TokenError, SyntaxError): + except (tokenize.TokenError, SyntaxError): # since we cut off the tokenizer early, we can trigger # spurious errors pass finally: - _tokenize.tabsize = save_tabsize + tokenize.tabsize = save_tabsize return self.blkopenline, self.indentedline ### end autoindent code ### diff --git a/Lib/idlelib/filelist.py b/Lib/idlelib/filelist.py index b5af90ccaf..f46ad7cd7e 100644 --- a/Lib/idlelib/filelist.py +++ b/Lib/idlelib/filelist.py @@ -1,4 +1,5 @@ import os + from tkinter import * import tkinter.messagebox as tkMessageBox diff --git a/Lib/idlelib/grep.py b/Lib/idlelib/grep.py index cfb0ea0ad8..64ba28d94a 100644 --- a/Lib/idlelib/grep.py +++ b/Lib/idlelib/grep.py @@ -1,11 +1,14 @@ -import os import fnmatch +import os import sys + from tkinter import StringVar, BooleanVar from tkinter.ttk import Checkbutton -from idlelib import searchengine + from idlelib.searchbase import SearchDialogBase -# Importing OutputWindow fails due to import loop +from idlelib import searchengine + +# Importing OutputWindow here fails due to import loop # EditorWindow -> GrepDialop -> OutputWindow -> EditorWindow def grep(text, io=None, flist=None): @@ -127,9 +130,9 @@ class GrepDialog(SearchDialogBase): def _grep_dialog(parent): # htest # - from idlelib.pyshell import PyShellFileList from tkinter import Toplevel, Text, SEL, END from tkinter.ttk import Button + from idlelib.pyshell import PyShellFileList top = Toplevel(parent) top.title("Test GrepDialog") x, y = map(int, parent.geometry().split('+')[1:]) diff --git a/Lib/idlelib/help.py b/Lib/idlelib/help.py index 03d6ea24e0..77e01a31c0 100644 --- a/Lib/idlelib/help.py +++ b/Lib/idlelib/help.py @@ -27,9 +27,11 @@ show_idlehelp - Create HelpWindow. Called in EditorWindow.help_dialog. from html.parser import HTMLParser from os.path import abspath, dirname, isfile, join from platform import python_version + from tkinter import Toplevel, Frame, Text, Menu from tkinter.ttk import Menubutton, Scrollbar from tkinter import font as tkfont + from idlelib.config import idleConf ## About IDLE ## diff --git a/Lib/idlelib/help_about.py b/Lib/idlelib/help_about.py index 54f3599ca4..071bd3ec0f 100644 --- a/Lib/idlelib/help_about.py +++ b/Lib/idlelib/help_about.py @@ -1,12 +1,14 @@ """About Dialog for IDLE """ - import os from sys import version + from tkinter import * + from idlelib import textview + class AboutDialog(Toplevel): """Modal about dialog for idle @@ -144,6 +146,7 @@ class AboutDialog(Toplevel): def Ok(self, event=None): self.destroy() + if __name__ == '__main__': import unittest unittest.main('idlelib.idle_test.test_help_about', verbosity=2, exit=False) diff --git a/Lib/idlelib/history.py b/Lib/idlelib/history.py index 6068d4f9fa..56f53a0f2f 100644 --- a/Lib/idlelib/history.py +++ b/Lib/idlelib/history.py @@ -2,6 +2,7 @@ from idlelib.config import idleConf + class History: ''' Implement Idle Shell history mechanism. @@ -99,6 +100,7 @@ class History: self.pointer = None self.prefix = None + if __name__ == "__main__": from unittest import main - main('idlelib.idle_test.test_idlehistory', verbosity=2, exit=False) + main('idlelib.idle_test.test_history', verbosity=2, exit=False) diff --git a/Lib/idlelib/hyperparser.py b/Lib/idlelib/hyperparser.py index f904a39e24..450a709c09 100644 --- a/Lib/idlelib/hyperparser.py +++ b/Lib/idlelib/hyperparser.py @@ -4,11 +4,10 @@ HyperParser uses PyParser. PyParser mostly gives information on the proper indentation of code. HyperParser gives additional information on the structure of code. """ - -import string from keyword import iskeyword -from idlelib import pyparse +import string +from idlelib import pyparse # all ASCII chars that may be in an identifier _ASCII_ID_CHARS = frozenset(string.ascii_letters + string.digits + "_") diff --git a/Lib/idlelib/idle_test/test_iomenu.py b/Lib/idlelib/idle_test/test_iomenu.py index f8ff1127c3..65bf593055 100644 --- a/Lib/idlelib/idle_test/test_iomenu.py +++ b/Lib/idlelib/idle_test/test_iomenu.py @@ -1,6 +1,7 @@ import unittest import io -from idlelib.pyshell import PseudoInputFile, PseudoOutputFile + +from idlelib.run import PseudoInputFile, PseudoOutputFile class S(str): @@ -230,4 +231,4 @@ class PseudeInputFilesTest(unittest.TestCase): if __name__ == '__main__': - unittest.main() + unittest.main(verbosity=2) diff --git a/Lib/idlelib/macosx.py b/Lib/idlelib/macosx.py index f9f558de18..c225dd9e1a 100644 --- a/Lib/idlelib/macosx.py +++ b/Lib/idlelib/macosx.py @@ -2,9 +2,10 @@ A number of functions that enhance IDLE on Mac OSX. """ from sys import platform # Used in _init_tk_type, changed by test. -import tkinter import warnings +import tkinter + ## Define functions that query the Mac graphics type. ## _tk_type and its initializer are private to this section. diff --git a/Lib/idlelib/multicall.py b/Lib/idlelib/multicall.py index 8a66cd9f72..b74fed4c0c 100644 --- a/Lib/idlelib/multicall.py +++ b/Lib/idlelib/multicall.py @@ -28,9 +28,9 @@ The order by which events are called is defined by these rules: unless this conflicts with the first rule. Each function will be called at most once for each event. """ - -import sys import re +import sys + import tkinter # the event type constants, which define the meaning of mc_type diff --git a/Lib/idlelib/outwin.py b/Lib/idlelib/outwin.py index b3bc786151..f6d2915c62 100644 --- a/Lib/idlelib/outwin.py +++ b/Lib/idlelib/outwin.py @@ -1,9 +1,12 @@ -from tkinter import * -from idlelib.editor import EditorWindow import re + +from tkinter import * import tkinter.messagebox as tkMessageBox + +from idlelib.editor import EditorWindow from idlelib import iomenu + class OutputWindow(EditorWindow): """An editor window that can serve as an output file. diff --git a/Lib/idlelib/paragraph.py b/Lib/idlelib/paragraph.py index 0323b53d86..5d358eed05 100644 --- a/Lib/idlelib/paragraph.py +++ b/Lib/idlelib/paragraph.py @@ -14,10 +14,11 @@ Known problems with comment reformatting: spaces, they will not be considered part of the same block. * Fancy comments, like this bulleted list, aren't handled :-) """ - import re + from idlelib.config import idleConf + class FormatParagraph: menudefs = [ @@ -189,6 +190,7 @@ def get_comment_header(line): if m is None: return "" return m.group(1) + if __name__ == "__main__": import unittest unittest.main('idlelib.idle_test.test_paragraph', diff --git a/Lib/idlelib/parenmatch.py b/Lib/idlelib/parenmatch.py index 9586a3b91d..ccec708f31 100644 --- a/Lib/idlelib/parenmatch.py +++ b/Lib/idlelib/parenmatch.py @@ -4,7 +4,6 @@ When you hit a right paren, the cursor should move briefly to the left paren. Paren here is used generically; the matching applies to parentheses, square brackets, and curly braces. """ - from idlelib.hyperparser import HyperParser from idlelib.config import idleConf diff --git a/Lib/idlelib/pathbrowser.py b/Lib/idlelib/pathbrowser.py index 966af4b259..6c19508d31 100644 --- a/Lib/idlelib/pathbrowser.py +++ b/Lib/idlelib/pathbrowser.py @@ -1,10 +1,10 @@ +import importlib.machinery import os import sys -import importlib.machinery -from idlelib.tree import TreeItem from idlelib.browser import ClassBrowser, ModuleBrowserTreeItem from idlelib.pyshell import PyShellFileList +from idlelib.tree import TreeItem class PathBrowser(ClassBrowser): @@ -24,6 +24,7 @@ class PathBrowser(ClassBrowser): def rootnode(self): return PathBrowserTreeItem() + class PathBrowserTreeItem(TreeItem): def GetText(self): @@ -36,6 +37,7 @@ class PathBrowserTreeItem(TreeItem): sublist.append(item) return sublist + class DirBrowserTreeItem(TreeItem): def __init__(self, dir, packages=[]): @@ -95,6 +97,7 @@ class DirBrowserTreeItem(TreeItem): sorted.sort() return sorted + def _path_browser(parent): # htest # flist = PyShellFileList(parent) PathBrowser(flist, _htest=True) diff --git a/Lib/idlelib/percolator.py b/Lib/idlelib/percolator.py index 4474f9abea..d18daf0586 100644 --- a/Lib/idlelib/percolator.py +++ b/Lib/idlelib/percolator.py @@ -1,5 +1,5 @@ -from idlelib.redirector import WidgetRedirector from idlelib.delegator import Delegator +from idlelib.redirector import WidgetRedirector class Percolator: diff --git a/Lib/idlelib/pyparse.py b/Lib/idlelib/pyparse.py index 9ccbb25076..6739dfd1a0 100644 --- a/Lib/idlelib/pyparse.py +++ b/Lib/idlelib/pyparse.py @@ -1,6 +1,6 @@ +from collections import Mapping import re import sys -from collections import Mapping # Reason last stmt is continued (or C_NONE if it's not). (C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE, diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py index 740c72e2a3..e1eade1eea 100755 --- a/Lib/idlelib/pyshell.py +++ b/Lib/idlelib/pyshell.py @@ -15,9 +15,13 @@ if TkVersion < 8.5: parent=root) sys.exit(1) +from code import InteractiveInterpreter import getopt +import io +import linecache import os import os.path +from platform import python_version, system import re import socket import subprocess @@ -25,23 +29,20 @@ import sys import threading import time import tokenize +import warnings -import linecache -from code import InteractiveInterpreter -from platform import python_version, system - -from idlelib import testing -from idlelib.editor import EditorWindow, fixwordbreaks -from idlelib.filelist import FileList +from idlelib import testing # bool value from idlelib.colorizer import ColorDelegator -from idlelib.undo import UndoDelegator -from idlelib.outwin import OutputWindow from idlelib.config import idleConf -from idlelib.run import idle_formatwarning, PseudoInputFile, PseudoOutputFile -from idlelib import rpc from idlelib import debugger from idlelib import debugger_r +from idlelib.editor import EditorWindow, fixwordbreaks +from idlelib.filelist import FileList from idlelib import macosx +from idlelib.outwin import OutputWindow +from idlelib import rpc +from idlelib.run import idle_formatwarning, PseudoInputFile, PseudoOutputFile +from idlelib.undo import UndoDelegator HOST = '127.0.0.1' # python execution server on localhost loopback PORT = 0 # someday pass in host, port for remote debug capability @@ -51,7 +52,6 @@ PORT = 0 # someday pass in host, port for remote debug capability # temporarily redirect the stream to the shell window to display warnings when # checking user's code. warning_stream = sys.__stderr__ # None, at least on Windows, if no console. -import warnings def idle_showwarning( message, category, filename, lineno, file=None, line=None): diff --git a/Lib/idlelib/query.py b/Lib/idlelib/query.py index a4584df98d..3b1f1e25be 100644 --- a/Lib/idlelib/query.py +++ b/Lib/idlelib/query.py @@ -23,9 +23,10 @@ Subclass HelpSource gets menu item and path for additions to Help menu. import importlib import os from sys import executable, platform # Platform is set for one test. + from tkinter import Toplevel, StringVar, W, E, N, S -from tkinter import filedialog from tkinter.ttk import Frame, Button, Entry, Label +from tkinter import filedialog from tkinter.font import Font class Query(Toplevel): diff --git a/Lib/idlelib/replace.py b/Lib/idlelib/replace.py index 367bfc9063..abd9e59f4e 100644 --- a/Lib/idlelib/replace.py +++ b/Lib/idlelib/replace.py @@ -3,12 +3,12 @@ Uses idlelib.SearchEngine for search capability. Defines various replace related functions like replace, replace all, replace+find. """ +import re + from tkinter import StringVar, TclError -from idlelib import searchengine from idlelib.searchbase import SearchDialogBase -import re - +from idlelib import searchengine def replace(text): """Returns a singleton ReplaceDialog instance.The single dialog diff --git a/Lib/idlelib/rpc.py b/Lib/idlelib/rpc.py index 48105f2aa1..8f57edb836 100644 --- a/Lib/idlelib/rpc.py +++ b/Lib/idlelib/rpc.py @@ -26,23 +26,21 @@ See the Idle run.main() docstring for further information on how this was accomplished in Idle. """ - -import sys -import os +import builtins +import copyreg import io -import socket +import marshal +import os +import pickle +import queue import select +import socket import socketserver import struct -import pickle +import sys import threading -import queue import traceback -import copyreg import types -import marshal -import builtins - def unpickle_code(ms): co = marshal.loads(ms) @@ -60,10 +58,12 @@ def dumps(obj, protocol=None): p.dump(obj) return f.getvalue() + class CodePickler(pickle.Pickler): dispatch_table = {types.CodeType: pickle_code} dispatch_table.update(copyreg.dispatch_table) + BUFSIZE = 8*1024 LOCALHOST = '127.0.0.1' @@ -487,16 +487,19 @@ class RemoteObject(object): # Token mix-in class pass + def remoteref(obj): oid = id(obj) objecttable[oid] = obj return RemoteProxy(oid) + class RemoteProxy(object): def __init__(self, oid): self.oid = oid + class RPCHandler(socketserver.BaseRequestHandler, SocketIO): debugging = False @@ -514,6 +517,7 @@ class RPCHandler(socketserver.BaseRequestHandler, SocketIO): def get_remote_proxy(self, oid): return RPCProxy(self, oid) + class RPCClient(SocketIO): debugging = False @@ -539,6 +543,7 @@ class RPCClient(SocketIO): def get_remote_proxy(self, oid): return RPCProxy(self, oid) + class RPCProxy(object): __methods = None @@ -587,6 +592,7 @@ def _getattributes(obj, attributes): if not callable(attr): attributes[name] = 1 + class MethodProxy(object): def __init__(self, sockio, oid, name): diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py index c7ee0b3e45..afa9744a34 100644 --- a/Lib/idlelib/run.py +++ b/Lib/idlelib/run.py @@ -2,21 +2,21 @@ import io import linecache import queue import sys -import _thread as thread -import threading import time import traceback -import tkinter - -from idlelib import calltips -from idlelib import autocomplete +import _thread as thread +import threading +import warnings -from idlelib import debugger_r -from idlelib import debugobj_r -from idlelib import stackviewer -from idlelib import rpc -from idlelib import iomenu +import tkinter # Tcl, deletions, messagebox if startup fails +from idlelib import autocomplete # AutoComplete, fetch_encodings +from idlelib import calltips # CallTips +from idlelib import debugger_r # start_debugger +from idlelib import debugobj_r # remote_object_tree_item +from idlelib import iomenu # encoding +from idlelib import rpc # multiple objects +from idlelib import stackviewer # StackTreeItem import __main__ for mod in ('simpledialog', 'messagebox', 'font', @@ -27,7 +27,6 @@ for mod in ('simpledialog', 'messagebox', 'font', LOCALHOST = '127.0.0.1' -import warnings def idle_formatwarning(message, category, filename, lineno, line=None): """Format warnings the IDLE way.""" @@ -280,6 +279,7 @@ def exit(): capture_warnings(False) sys.exit(0) + class MyRPCServer(rpc.RPCServer): def handle_error(self, request, client_address): diff --git a/Lib/idlelib/runscript.py b/Lib/idlelib/runscript.py index 7e7524a3c4..79d86adabc 100644 --- a/Lib/idlelib/runscript.py +++ b/Lib/idlelib/runscript.py @@ -20,11 +20,12 @@ XXX GvR Redesign this interface (yet again) as follows: import os import tabnanny import tokenize + import tkinter.messagebox as tkMessageBox -from idlelib import pyshell from idlelib.config import idleConf from idlelib import macosx +from idlelib import pyshell indent_message = """Error: Inconsistent indentation detected! diff --git a/Lib/idlelib/scrolledlist.py b/Lib/idlelib/scrolledlist.py index 4799995800..cc08c26e5e 100644 --- a/Lib/idlelib/scrolledlist.py +++ b/Lib/idlelib/scrolledlist.py @@ -1,7 +1,9 @@ from tkinter import * -from idlelib import macosx from tkinter.ttk import Scrollbar +from idlelib import macosx + + class ScrolledList: default = "(None)" diff --git a/Lib/idlelib/search.py b/Lib/idlelib/search.py index 508a35c3f1..4b90659308 100644 --- a/Lib/idlelib/search.py +++ b/Lib/idlelib/search.py @@ -24,6 +24,7 @@ def find_selection(text): "Handle the editor edit menu item and corresponding event." return _setup(text).find_selection(text) + class SearchDialog(SearchDialogBase): def create_widgets(self): diff --git a/Lib/idlelib/searchbase.py b/Lib/idlelib/searchbase.py index b326a1c6aa..5f81785b71 100644 --- a/Lib/idlelib/searchbase.py +++ b/Lib/idlelib/searchbase.py @@ -3,6 +3,7 @@ from tkinter import Toplevel, Frame from tkinter.ttk import Entry, Label, Button, Checkbutton, Radiobutton + class SearchDialogBase: '''Create most of a 3 or 4 row, 3 column search dialog. diff --git a/Lib/idlelib/searchengine.py b/Lib/idlelib/searchengine.py index 2e3700ece3..253f1b0831 100644 --- a/Lib/idlelib/searchengine.py +++ b/Lib/idlelib/searchengine.py @@ -1,5 +1,6 @@ '''Define SearchEngine for search dialogs.''' import re + from tkinter import StringVar, BooleanVar, TclError import tkinter.messagebox as tkMessageBox @@ -14,6 +15,7 @@ def get(root): # This creates a cycle that persists until root is deleted. return root._searchengine + class SearchEngine: """Handles searching a text widget for Find, Replace, and Grep.""" @@ -186,6 +188,7 @@ class SearchEngine: col = len(chars) - 1 return None + def search_reverse(prog, chars, col): '''Search backwards and return an re match object or None. diff --git a/Lib/idlelib/stackviewer.py b/Lib/idlelib/stackviewer.py index c8c802ce2a..0698def5d4 100644 --- a/Lib/idlelib/stackviewer.py +++ b/Lib/idlelib/stackviewer.py @@ -1,11 +1,12 @@ -import os -import sys import linecache +import os import re +import sys + import tkinter as tk -from idlelib.tree import TreeNode, TreeItem, ScrolledCanvas from idlelib.debugobj import ObjectTreeItem, make_objecttreeitem +from idlelib.tree import TreeNode, TreeItem, ScrolledCanvas def StackBrowser(root, flist=None, tb=None, top=None): if top is None: @@ -16,6 +17,7 @@ def StackBrowser(root, flist=None, tb=None, top=None): node = TreeNode(sc.canvas, None, item) node.expand() + class StackTreeItem(TreeItem): def __init__(self, flist=None, tb=None): @@ -54,6 +56,7 @@ class StackTreeItem(TreeItem): sublist.append(item) return sublist + class FrameTreeItem(TreeItem): def __init__(self, info, flist): @@ -95,6 +98,7 @@ class FrameTreeItem(TreeItem): if os.path.isfile(filename): self.flist.gotofileline(filename, lineno) + class VariablesTreeItem(ObjectTreeItem): def GetText(self): @@ -119,6 +123,7 @@ class VariablesTreeItem(ObjectTreeItem): sublist.append(item) return sublist + def _stack_viewer(parent): # htest # from idlelib.pyshell import PyShellFileList top = tk.Toplevel(parent) diff --git a/Lib/idlelib/statusbar.py b/Lib/idlelib/statusbar.py index a65bfb3393..8618528d82 100644 --- a/Lib/idlelib/statusbar.py +++ b/Lib/idlelib/statusbar.py @@ -1,5 +1,6 @@ from tkinter import Frame, Label + class MultiStatusBar(Frame): def __init__(self, master, **kw): @@ -17,6 +18,7 @@ class MultiStatusBar(Frame): label.config(width=width) label.config(text=text) + def _multistatus_bar(parent): # htest # from tkinter import Toplevel, Frame, Text, Button top = Toplevel(parent) diff --git a/Lib/idlelib/tabbedpages.py b/Lib/idlelib/tabbedpages.py index ed07588fc4..4186fa2013 100644 --- a/Lib/idlelib/tabbedpages.py +++ b/Lib/idlelib/tabbedpages.py @@ -285,6 +285,7 @@ class TabSet(Frame): # placed hide it self.tab_set.lower() + class TabbedPageSet(Frame): """A Tkinter tabbed-pane widget. @@ -302,6 +303,7 @@ class TabbedPageSet(Frame): remove_page() methods. """ + class Page(object): """Abstract base class for TabbedPageSet's pages. @@ -467,6 +469,7 @@ class TabbedPageSet(Frame): self._tab_set.set_selected_tab(page_name) + def _tabbed_pages(parent): # htest # top=Toplevel(parent) x, y = map(int, parent.geometry().split('+')[1:]) diff --git a/Lib/idlelib/textview.py b/Lib/idlelib/textview.py index 7664524cd3..b5c9f9b0ee 100644 --- a/Lib/idlelib/textview.py +++ b/Lib/idlelib/textview.py @@ -1,11 +1,11 @@ """Simple text browser for IDLE """ - from tkinter import * from tkinter.ttk import Scrollbar from tkinter.messagebox import showerror + class TextViewer(Toplevel): """A simple text viewer dialog for IDLE diff --git a/Lib/idlelib/tree.py b/Lib/idlelib/tree.py index 04e0734ec3..292ce36184 100644 --- a/Lib/idlelib/tree.py +++ b/Lib/idlelib/tree.py @@ -15,10 +15,12 @@ # - optimize tree redraw after expand of subnode import os + from tkinter import * from tkinter.ttk import Scrollbar -from idlelib import zoomheight + from idlelib.config import idleConf +from idlelib import zoomheight ICONDIR = "Icons" diff --git a/Lib/idlelib/undo.py b/Lib/idlelib/undo.py index 9f291e599b..4332f10993 100644 --- a/Lib/idlelib/undo.py +++ b/Lib/idlelib/undo.py @@ -1,5 +1,7 @@ import string + from idlelib.delegator import Delegator + # tkintter import not needed because module does not create widgets, # although many methods operate on text widget arguments. @@ -158,7 +160,6 @@ class UndoDelegator(Delegator): class Command: - # Base class for Undoable commands tags = None @@ -204,7 +205,6 @@ class Command: class InsertCommand(Command): - # Undoable insert command def __init__(self, index1, chars, tags=None): @@ -262,7 +262,6 @@ class InsertCommand(Command): class DeleteCommand(Command): - # Undoable delete command def __init__(self, index1, index2=None): @@ -297,8 +296,8 @@ class DeleteCommand(Command): text.see('insert') ##sys.__stderr__.write("undo: %s\n" % self) -class CommandSequence(Command): +class CommandSequence(Command): # Wrapper for a sequence of undoable cmds to be undone/redone # as a unit diff --git a/Lib/idlelib/windows.py b/Lib/idlelib/windows.py index bc74348f56..a3f858aa73 100644 --- a/Lib/idlelib/windows.py +++ b/Lib/idlelib/windows.py @@ -1,5 +1,6 @@ from tkinter import * + class WindowList: def __init__(self): @@ -48,6 +49,7 @@ class WindowList: t, v, tb = sys.exc_info() print("warning: callback failed in WindowList", t, ":", v) + registry = WindowList() add_windows_to_menu = registry.add_windows_to_menu diff --git a/Lib/idlelib/zoomheight.py b/Lib/idlelib/zoomheight.py index 0016e9de5f..aa4a427eab 100644 --- a/Lib/idlelib/zoomheight.py +++ b/Lib/idlelib/zoomheight.py @@ -5,6 +5,7 @@ import sys from idlelib import macosx + class ZoomHeight: menudefs = [ @@ -20,6 +21,7 @@ class ZoomHeight: top = self.editwin.top zoom_height(top) + def zoom_height(top): geom = top.wm_geometry() m = re.match(r"(\d+)x(\d+)\+(-?\d+)\+(-?\d+)", geom) -- cgit v1.2.1 From 68bb0ff567486a6312757fc5cc4cbe624a854673 Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Wed, 31 Aug 2016 00:12:15 -0700 Subject: issue23591: add Flags, IntFlags, and tests --- Lib/enum.py | 227 +++++++++++++++++++++++-- Lib/test/test_enum.py | 452 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 668 insertions(+), 11 deletions(-) (limited to 'Lib') diff --git a/Lib/enum.py b/Lib/enum.py index e7889a8dc7..e89c17d583 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -1,5 +1,7 @@ import sys from types import MappingProxyType, DynamicClassAttribute +from functools import reduce +from operator import or_ as _or_ # try _collections first to reduce startup cost try: @@ -8,7 +10,7 @@ except ImportError: from collections import OrderedDict -__all__ = ['EnumMeta', 'Enum', 'IntEnum', 'unique'] +__all__ = ['EnumMeta', 'Enum', 'IntEnum', 'Flags', 'IntFlags', 'unique'] def _is_descriptor(obj): @@ -64,7 +66,10 @@ class _EnumDict(dict): """ if _is_sunder(key): - if key not in ('_order_', ): + if key not in ( + '_order_', '_create_pseudo_member_', '_decompose_', + '_generate_next_value_', '_missing_', + ): raise ValueError('_names_ are reserved for future Enum use') elif _is_dunder(key): if key == '__order__': @@ -75,7 +80,7 @@ class _EnumDict(dict): elif not _is_descriptor(value): if key in self: # enum overwriting a descriptor? - raise TypeError('Key already defined as: %r' % self[key]) + raise TypeError('%r already defined as: %r' % (key, self[key])) self._member_names.append(key) super().__setitem__(key, value) @@ -91,9 +96,15 @@ class EnumMeta(type): """Metaclass for Enum""" @classmethod def __prepare__(metacls, cls, bases): - return _EnumDict() + # create the namespace dict + enum_dict = _EnumDict() + # inherit previous flags and _generate_next_value_ function + member_type, first_enum = metacls._get_mixins_(bases) + if first_enum is not None: + enum_dict['_generate_next_value_'] = getattr(first_enum, '_generate_next_value_', None) + return enum_dict - def __new__(metacls, cls, bases, classdict): + def __new__(metacls, cls, bases, classdict, **kwds): # an Enum class is final once enumeration items have been defined; it # cannot be mixed with other types (int, float, etc.) if it has an # inherited __new__ unless a new __new__ is defined (or the resulting @@ -104,7 +115,7 @@ class EnumMeta(type): # save enum items into separate mapping so they don't get baked into # the new class - members = {k: classdict[k] for k in classdict._member_names} + enum_members = {k: classdict[k] for k in classdict._member_names} for name in classdict._member_names: del classdict[name] @@ -112,7 +123,7 @@ class EnumMeta(type): _order_ = classdict.pop('_order_', None) # check for illegal enum names (any others?) - invalid_names = set(members) & {'mro', } + invalid_names = set(enum_members) & {'mro', } if invalid_names: raise ValueError('Invalid enum member name: {0}'.format( ','.join(invalid_names))) @@ -156,7 +167,7 @@ class EnumMeta(type): # a custom __new__ is doing something funky with the values -- such as # auto-numbering ;) for member_name in classdict._member_names: - value = members[member_name] + value = enum_members[member_name] if not isinstance(value, tuple): args = (value, ) else: @@ -170,7 +181,10 @@ class EnumMeta(type): else: enum_member = __new__(enum_class, *args) if not hasattr(enum_member, '_value_'): - enum_member._value_ = member_type(*args) + if member_type is object: + enum_member._value_ = value + else: + enum_member._value_ = member_type(*args) value = enum_member._value_ enum_member._name_ = member_name enum_member.__objclass__ = enum_class @@ -344,13 +358,18 @@ class EnumMeta(type): """ metacls = cls.__class__ bases = (cls, ) if type is None else (type, cls) + _, first_enum = cls._get_mixins_(bases) classdict = metacls.__prepare__(class_name, bases) # special processing needed for names? if isinstance(names, str): names = names.replace(',', ' ').split() if isinstance(names, (tuple, list)) and isinstance(names[0], str): - names = [(e, i) for (i, e) in enumerate(names, start)] + original_names, names = names, [] + last_value = None + for count, name in enumerate(original_names): + last_value = first_enum._generate_next_value_(name, start, count, last_value) + names.append((name, last_value)) # Here, names is either an iterable of (name, value) or a mapping. for item in names: @@ -492,6 +511,16 @@ class Enum(metaclass=EnumMeta): for member in cls._member_map_.values(): if member._value_ == value: return member + # still not found -- try _missing_ hook + return cls._missing_(value) + + @staticmethod + def _generate_next_value_(name, start, count, last_value): + if not count: + return start + return last_value + 1 + @classmethod + def _missing_(cls, value): raise ValueError("%r is not a valid %s" % (value, cls.__name__)) def __repr__(self): @@ -585,6 +614,184 @@ class IntEnum(int, Enum): def _reduce_ex_by_name(self, proto): return self.name +class Flags(Enum): + """Support for flags""" + @staticmethod + def _generate_next_value_(name, start, count, last_value): + """ + Generate the next value when not given. + + name: the name of the member + start: the initital start value or None + count: the number of existing members + last_value: the last value assigned or None + """ + if not count: + return start if start is not None else 1 + high_bit = _high_bit(last_value) + return 2 ** (high_bit+1) + + @classmethod + def _missing_(cls, value): + original_value = value + if value < 0: + value = ~value + possible_member = cls._create_pseudo_member_(value) + for member in possible_member._decompose_(): + if member._name_ is None and member._value_ != 0: + raise ValueError('%r is not a valid %s' % (original_value, cls.__name__)) + if original_value < 0: + possible_member = ~possible_member + return possible_member + + @classmethod + def _create_pseudo_member_(cls, value): + pseudo_member = cls._value2member_map_.get(value, None) + if pseudo_member is None: + # construct a non-singleton enum pseudo-member + pseudo_member = object.__new__(cls) + pseudo_member._name_ = None + pseudo_member._value_ = value + cls._value2member_map_[value] = pseudo_member + return pseudo_member + + def _decompose_(self): + """Extract all members from the value.""" + value = self._value_ + members = [] + cls = self.__class__ + for member in sorted(cls, key=lambda m: m._value_, reverse=True): + while _high_bit(value) > _high_bit(member._value_): + unknown = self._create_pseudo_member_(2 ** _high_bit(value)) + members.append(unknown) + value &= ~unknown._value_ + if ( + (value & member._value_ == member._value_) + and (member._value_ or not members) + ): + value &= ~member._value_ + members.append(member) + if not members or value: + members.append(self._create_pseudo_member_(value)) + members = list(members) + return members + + def __contains__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return other._value_ & self._value_ == other._value_ + + def __iter__(self): + if self.value == 0: + return iter([]) + else: + return iter(self._decompose_()) + + def __repr__(self): + cls = self.__class__ + if self._name_ is not None: + return '<%s.%s: %r>' % (cls.__name__, self._name_, self._value_) + members = self._decompose_() + if len(members) == 1 and members[0]._name_ is None: + return '<%s: %r>' % (cls.__name__, members[0]._value_) + else: + return '<%s.%s: %r>' % ( + cls.__name__, + '|'.join([str(m._name_ or m._value_) for m in members]), + self._value_, + ) + + def __str__(self): + cls = self.__class__ + if self._name_ is not None: + return '%s.%s' % (cls.__name__, self._name_) + members = self._decompose_() + if len(members) == 1 and members[0]._name_ is None: + return '%s.%r' % (cls.__name__, members[0]._value_) + else: + return '%s.%s' % ( + cls.__name__, + '|'.join([str(m._name_ or m._value_) for m in members]), + ) + + def __or__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return self.__class__(self._value_ | other._value_) + + def __and__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return self.__class__(self._value_ & other._value_) + + def __xor__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return self.__class__(self._value_ ^ other._value_) + + def __invert__(self): + members = self._decompose_() + inverted_members = [m for m in self.__class__ if m not in members and not m._value_ & self._value_] + inverted = reduce(_or_, inverted_members, self.__class__(0)) + return self.__class__(inverted) + + +class IntFlags(int, Flags): + """Support for integer-based Flags""" + + @classmethod + def _create_pseudo_member_(cls, value): + pseudo_member = cls._value2member_map_.get(value, None) + if pseudo_member is None: + # construct a non-singleton enum pseudo-member + pseudo_member = int.__new__(cls, value) + pseudo_member._name_ = None + pseudo_member._value_ = value + cls._value2member_map_[value] = pseudo_member + return pseudo_member + + @classmethod + def _missing_(cls, value): + possible_member = cls._create_pseudo_member_(value) + return possible_member + + def __or__(self, other): + if not isinstance(other, (self.__class__, int)): + return NotImplemented + return self.__class__(self._value_ | self.__class__(other)._value_) + + def __and__(self, other): + if not isinstance(other, (self.__class__, int)): + return NotImplemented + return self.__class__(self._value_ & self.__class__(other)._value_) + + def __xor__(self, other): + if not isinstance(other, (self.__class__, int)): + return NotImplemented + return self.__class__(self._value_ ^ self.__class__(other)._value_) + + __ror__ = __or__ + __rand__ = __and__ + __rxor__ = __xor__ + + def __invert__(self): + # members = self._decompose_() + # inverted_members = [m for m in self.__class__ if m not in members and not m._value_ & self._value_] + # inverted = reduce(_or_, inverted_members, self.__class__(0)) + return self.__class__(~self._value_) + + + + +def _high_bit(value): + """return the highest bit set in value""" + bit = 0 + while 'looking for the highest bit': + limit = 2 ** bit + if limit > value: + return bit - 1 + bit += 1 + def unique(enumeration): """Class decorator for enumerations ensuring unique member values.""" duplicates = [] diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index 2d4519e9ed..1831895d3b 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -3,7 +3,7 @@ import inspect import pydoc import unittest from collections import OrderedDict -from enum import Enum, IntEnum, EnumMeta, unique +from enum import Enum, IntEnum, EnumMeta, Flags, IntFlags, unique from io import StringIO from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL from test import support @@ -1633,6 +1633,456 @@ class TestOrder(unittest.TestCase): verde = green +class TestFlags(unittest.TestCase): + """Tests of the Flags.""" + + class Perm(Flags): + R, W, X = 4, 2, 1 + + class Open(Flags): + RO = 0 + WO = 1 + RW = 2 + AC = 3 + CE = 1<<19 + + def test_str(self): + Perm = self.Perm + self.assertEqual(str(Perm.R), 'Perm.R') + self.assertEqual(str(Perm.W), 'Perm.W') + self.assertEqual(str(Perm.X), 'Perm.X') + self.assertEqual(str(Perm.R | Perm.W), 'Perm.R|W') + self.assertEqual(str(Perm.R | Perm.W | Perm.X), 'Perm.R|W|X') + self.assertEqual(str(Perm(0)), 'Perm.0') + self.assertEqual(str(~Perm.R), 'Perm.W|X') + self.assertEqual(str(~Perm.W), 'Perm.R|X') + self.assertEqual(str(~Perm.X), 'Perm.R|W') + self.assertEqual(str(~(Perm.R | Perm.W)), 'Perm.X') + self.assertEqual(str(~(Perm.R | Perm.W | Perm.X)), 'Perm.0') + self.assertEqual(str(Perm(~0)), 'Perm.R|W|X') + + Open = self.Open + self.assertEqual(str(Open.RO), 'Open.RO') + self.assertEqual(str(Open.WO), 'Open.WO') + self.assertEqual(str(Open.AC), 'Open.AC') + self.assertEqual(str(Open.RO | Open.CE), 'Open.CE') + self.assertEqual(str(Open.WO | Open.CE), 'Open.CE|WO') + self.assertEqual(str(~Open.RO), 'Open.CE|AC') + self.assertEqual(str(~Open.WO), 'Open.CE|RW') + self.assertEqual(str(~Open.AC), 'Open.CE') + self.assertEqual(str(~(Open.RO | Open.CE)), 'Open.AC') + self.assertEqual(str(~(Open.WO | Open.CE)), 'Open.RW') + + def test_repr(self): + Perm = self.Perm + self.assertEqual(repr(Perm.R), '') + self.assertEqual(repr(Perm.W), '') + self.assertEqual(repr(Perm.X), '') + self.assertEqual(repr(Perm.R | Perm.W), '') + self.assertEqual(repr(Perm.R | Perm.W | Perm.X), '') + self.assertEqual(repr(Perm(0)), '') + self.assertEqual(repr(~Perm.R), '') + self.assertEqual(repr(~Perm.W), '') + self.assertEqual(repr(~Perm.X), '') + self.assertEqual(repr(~(Perm.R | Perm.W)), '') + self.assertEqual(repr(~(Perm.R | Perm.W | Perm.X)), '') + self.assertEqual(repr(Perm(~0)), '') + + Open = self.Open + self.assertEqual(repr(Open.RO), '') + self.assertEqual(repr(Open.WO), '') + self.assertEqual(repr(Open.AC), '') + self.assertEqual(repr(Open.RO | Open.CE), '') + self.assertEqual(repr(Open.WO | Open.CE), '') + self.assertEqual(repr(~Open.RO), '') + self.assertEqual(repr(~Open.WO), '') + self.assertEqual(repr(~Open.AC), '') + self.assertEqual(repr(~(Open.RO | Open.CE)), '') + self.assertEqual(repr(~(Open.WO | Open.CE)), '') + + def test_or(self): + Perm = self.Perm + for i in Perm: + for j in Perm: + self.assertEqual((i | j), Perm(i.value | j.value)) + self.assertEqual((i | j).value, i.value | j.value) + self.assertIs(type(i | j), Perm) + for i in Perm: + self.assertIs(i | i, i) + Open = self.Open + self.assertIs(Open.RO | Open.CE, Open.CE) + + def test_and(self): + Perm = self.Perm + RW = Perm.R | Perm.W + RX = Perm.R | Perm.X + WX = Perm.W | Perm.X + RWX = Perm.R | Perm.W | Perm.X + values = list(Perm) + [RW, RX, WX, RWX, Perm(0)] + for i in values: + for j in values: + self.assertEqual((i & j).value, i.value & j.value) + self.assertIs(type(i & j), Perm) + for i in Perm: + self.assertIs(i & i, i) + self.assertIs(i & RWX, i) + self.assertIs(RWX & i, i) + Open = self.Open + self.assertIs(Open.RO & Open.CE, Open.RO) + + def test_xor(self): + Perm = self.Perm + for i in Perm: + for j in Perm: + self.assertEqual((i ^ j).value, i.value ^ j.value) + self.assertIs(type(i ^ j), Perm) + for i in Perm: + self.assertIs(i ^ Perm(0), i) + self.assertIs(Perm(0) ^ i, i) + Open = self.Open + self.assertIs(Open.RO ^ Open.CE, Open.CE) + self.assertIs(Open.CE ^ Open.CE, Open.RO) + + def test_invert(self): + Perm = self.Perm + RW = Perm.R | Perm.W + RX = Perm.R | Perm.X + WX = Perm.W | Perm.X + RWX = Perm.R | Perm.W | Perm.X + values = list(Perm) + [RW, RX, WX, RWX, Perm(0)] + for i in values: + self.assertIs(type(~i), Perm) + self.assertEqual(~~i, i) + for i in Perm: + self.assertIs(~~i, i) + Open = self.Open + self.assertIs(Open.WO & ~Open.WO, Open.RO) + self.assertIs((Open.WO|Open.CE) & ~Open.WO, Open.CE) + + def test_programatic_function_string(self): + Perm = Flags('Perm', 'R W X') + lst = list(Perm) + self.assertEqual(len(lst), len(Perm)) + self.assertEqual(len(Perm), 3, Perm) + self.assertEqual(lst, [Perm.R, Perm.W, Perm.X]) + for i, n in enumerate('R W X'.split()): + v = 1<') + self.assertEqual(repr(Perm.W), '') + self.assertEqual(repr(Perm.X), '') + self.assertEqual(repr(Perm.R | Perm.W), '') + self.assertEqual(repr(Perm.R | Perm.W | Perm.X), '') + self.assertEqual(repr(Perm.R | 8), '') + self.assertEqual(repr(Perm(0)), '') + self.assertEqual(repr(Perm(8)), '') + self.assertEqual(repr(~Perm.R), '') + self.assertEqual(repr(~Perm.W), '') + self.assertEqual(repr(~Perm.X), '') + self.assertEqual(repr(~(Perm.R | Perm.W)), '') + self.assertEqual(repr(~(Perm.R | Perm.W | Perm.X)), '') + self.assertEqual(repr(~(Perm.R | 8)), '') + self.assertEqual(repr(Perm(~0)), '') + self.assertEqual(repr(Perm(~8)), '') + + Open = self.Open + self.assertEqual(repr(Open.RO), '') + self.assertEqual(repr(Open.WO), '') + self.assertEqual(repr(Open.AC), '') + self.assertEqual(repr(Open.RO | Open.CE), '') + self.assertEqual(repr(Open.WO | Open.CE), '') + self.assertEqual(repr(Open(4)), '') + self.assertEqual(repr(~Open.RO), '') + self.assertEqual(repr(~Open.WO), '') + self.assertEqual(repr(~Open.AC), '') + self.assertEqual(repr(~(Open.RO | Open.CE)), '') + self.assertEqual(repr(~(Open.WO | Open.CE)), '') + self.assertEqual(repr(Open(~4)), '') + + def test_or(self): + Perm = self.Perm + for i in Perm: + for j in Perm: + self.assertEqual(i | j, i.value | j.value) + self.assertEqual((i | j).value, i.value | j.value) + self.assertIs(type(i | j), Perm) + for j in range(8): + self.assertEqual(i | j, i.value | j) + self.assertEqual((i | j).value, i.value | j) + self.assertIs(type(i | j), Perm) + self.assertEqual(j | i, j | i.value) + self.assertEqual((j | i).value, j | i.value) + self.assertIs(type(j | i), Perm) + for i in Perm: + self.assertIs(i | i, i) + self.assertIs(i | 0, i) + self.assertIs(0 | i, i) + Open = self.Open + self.assertIs(Open.RO | Open.CE, Open.CE) + + def test_and(self): + Perm = self.Perm + RW = Perm.R | Perm.W + RX = Perm.R | Perm.X + WX = Perm.W | Perm.X + RWX = Perm.R | Perm.W | Perm.X + values = list(Perm) + [RW, RX, WX, RWX, Perm(0)] + for i in values: + for j in values: + self.assertEqual(i & j, i.value & j.value, 'i is %r, j is %r' % (i, j)) + self.assertEqual((i & j).value, i.value & j.value, 'i is %r, j is %r' % (i, j)) + self.assertIs(type(i & j), Perm, 'i is %r, j is %r' % (i, j)) + for j in range(8): + self.assertEqual(i & j, i.value & j) + self.assertEqual((i & j).value, i.value & j) + self.assertIs(type(i & j), Perm) + self.assertEqual(j & i, j & i.value) + self.assertEqual((j & i).value, j & i.value) + self.assertIs(type(j & i), Perm) + for i in Perm: + self.assertIs(i & i, i) + self.assertIs(i & 7, i) + self.assertIs(7 & i, i) + Open = self.Open + self.assertIs(Open.RO & Open.CE, Open.RO) + + def test_xor(self): + Perm = self.Perm + for i in Perm: + for j in Perm: + self.assertEqual(i ^ j, i.value ^ j.value) + self.assertEqual((i ^ j).value, i.value ^ j.value) + self.assertIs(type(i ^ j), Perm) + for j in range(8): + self.assertEqual(i ^ j, i.value ^ j) + self.assertEqual((i ^ j).value, i.value ^ j) + self.assertIs(type(i ^ j), Perm) + self.assertEqual(j ^ i, j ^ i.value) + self.assertEqual((j ^ i).value, j ^ i.value) + self.assertIs(type(j ^ i), Perm) + for i in Perm: + self.assertIs(i ^ 0, i) + self.assertIs(0 ^ i, i) + Open = self.Open + self.assertIs(Open.RO ^ Open.CE, Open.CE) + self.assertIs(Open.CE ^ Open.CE, Open.RO) + + def test_invert(self): + Perm = self.Perm + RW = Perm.R | Perm.W + RX = Perm.R | Perm.X + WX = Perm.W | Perm.X + RWX = Perm.R | Perm.W | Perm.X + values = list(Perm) + [RW, RX, WX, RWX, Perm(0)] + for i in values: + self.assertEqual(~i, ~i.value) + self.assertEqual((~i).value, ~i.value) + self.assertIs(type(~i), Perm) + self.assertEqual(~~i, i) + for i in Perm: + self.assertIs(~~i, i) + Open = self.Open + self.assertIs(Open.WO & ~Open.WO, Open.RO) + self.assertIs((Open.WO|Open.CE) & ~Open.WO, Open.CE) + + def test_programatic_function_string(self): + Perm = IntFlags('Perm', 'R W X') + lst = list(Perm) + self.assertEqual(len(lst), len(Perm)) + self.assertEqual(len(Perm), 3, Perm) + self.assertEqual(lst, [Perm.R, Perm.W, Perm.X]) + for i, n in enumerate('R W X'.split()): + v = 1< Date: Wed, 31 Aug 2016 08:22:29 +0100 Subject: Closes #27904: Improved logging statements to defer formatting until needed. --- Lib/asyncio/base_events.py | 4 ++-- Lib/distutils/archive_util.py | 2 +- Lib/distutils/cmd.py | 3 +-- Lib/distutils/command/bdist_dumb.py | 2 +- Lib/distutils/command/build_ext.py | 6 +++--- Lib/distutils/command/config.py | 2 +- Lib/distutils/command/install.py | 2 +- Lib/distutils/command/register.py | 6 +++--- Lib/distutils/command/sdist.py | 2 +- 9 files changed, 14 insertions(+), 15 deletions(-) (limited to 'Lib') diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index 0916da8b0a..918b869526 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -1069,7 +1069,7 @@ class BaseEventLoop(events.AbstractEventLoop): transport = yield from self._make_subprocess_transport( protocol, cmd, True, stdin, stdout, stderr, bufsize, **kwargs) if self._debug: - logger.info('%s: %r' % (debug_log, transport)) + logger.info('%s: %r', debug_log, transport) return transport, protocol @coroutine @@ -1099,7 +1099,7 @@ class BaseEventLoop(events.AbstractEventLoop): protocol, popen_args, False, stdin, stdout, stderr, bufsize, **kwargs) if self._debug: - logger.info('%s: %r' % (debug_log, transport)) + logger.info('%s: %r', debug_log, transport) return transport, protocol def get_exception_handler(self): diff --git a/Lib/distutils/archive_util.py b/Lib/distutils/archive_util.py index bed1384900..78ae5757c3 100644 --- a/Lib/distutils/archive_util.py +++ b/Lib/distutils/archive_util.py @@ -171,7 +171,7 @@ def make_zipfile(base_name, base_dir, verbose=0, dry_run=0): path = os.path.normpath(os.path.join(dirpath, name)) if os.path.isfile(path): zip.write(path, path) - log.info("adding '%s'" % path) + log.info("adding '%s'", path) zip.close() return zip_filename diff --git a/Lib/distutils/cmd.py b/Lib/distutils/cmd.py index c89d5efc45..b5d9dc387d 100644 --- a/Lib/distutils/cmd.py +++ b/Lib/distutils/cmd.py @@ -329,8 +329,7 @@ class Command: # -- External world manipulation ----------------------------------- def warn(self, msg): - log.warn("warning: %s: %s\n" % - (self.get_command_name(), msg)) + log.warn("warning: %s: %s\n", self.get_command_name(), msg) def execute(self, func, args, msg=None, level=1): util.execute(func, args, msg, dry_run=self.dry_run) diff --git a/Lib/distutils/command/bdist_dumb.py b/Lib/distutils/command/bdist_dumb.py index f1bfb24923..e9274d925a 100644 --- a/Lib/distutils/command/bdist_dumb.py +++ b/Lib/distutils/command/bdist_dumb.py @@ -85,7 +85,7 @@ class bdist_dumb(Command): install.skip_build = self.skip_build install.warn_dir = 0 - log.info("installing to %s" % self.bdist_dir) + log.info("installing to %s", self.bdist_dir) self.run_command('install') # And make an archive relative to the root of the diff --git a/Lib/distutils/command/build_ext.py b/Lib/distutils/command/build_ext.py index f03a4e31d8..5e51ae4ba1 100644 --- a/Lib/distutils/command/build_ext.py +++ b/Lib/distutils/command/build_ext.py @@ -363,9 +363,9 @@ class build_ext(Command): ext_name, build_info = ext - log.warn(("old-style (ext_name, build_info) tuple found in " - "ext_modules for extension '%s'" - "-- please convert to Extension instance" % ext_name)) + log.warn("old-style (ext_name, build_info) tuple found in " + "ext_modules for extension '%s'" + "-- please convert to Extension instance", ext_name) if not (isinstance(ext_name, str) and extension_name_re.match(ext_name)): diff --git a/Lib/distutils/command/config.py b/Lib/distutils/command/config.py index b1fd09e016..4ae153d194 100644 --- a/Lib/distutils/command/config.py +++ b/Lib/distutils/command/config.py @@ -337,7 +337,7 @@ def dump_file(filename, head=None): If head is not None, will be dumped before the file content. """ if head is None: - log.info('%s' % filename) + log.info('%s', filename) else: log.info(head) file = open(filename) diff --git a/Lib/distutils/command/install.py b/Lib/distutils/command/install.py index 9474e9c599..fca05d69c6 100644 --- a/Lib/distutils/command/install.py +++ b/Lib/distutils/command/install.py @@ -385,7 +385,7 @@ class install(Command): else: opt_name = opt_name.translate(longopt_xlate) val = getattr(self, opt_name) - log.debug(" %s: %s" % (opt_name, val)) + log.debug(" %s: %s", opt_name, val) def finalize_unix(self): """Finalizes options for posix platforms.""" diff --git a/Lib/distutils/command/register.py b/Lib/distutils/command/register.py index 456d50d519..0fac94e9e5 100644 --- a/Lib/distutils/command/register.py +++ b/Lib/distutils/command/register.py @@ -94,7 +94,7 @@ class register(PyPIRCCommand): ''' # send the info to the server and report the result (code, result) = self.post_to_server(self.build_post_data('verify')) - log.info('Server response (%s): %s' % (code, result)) + log.info('Server response (%s): %s', code, result) def send_metadata(self): ''' Send the metadata to the package index server. @@ -205,7 +205,7 @@ Your selection [default 1]: ''', log.INFO) data['email'] = input(' EMail: ') code, result = self.post_to_server(data) if code != 200: - log.info('Server response (%s): %s' % (code, result)) + log.info('Server response (%s): %s', code, result) else: log.info('You will receive an email shortly.') log.info(('Follow the instructions in it to ' @@ -216,7 +216,7 @@ Your selection [default 1]: ''', log.INFO) while not data['email']: data['email'] = input('Your email address: ') code, result = self.post_to_server(data) - log.info('Server response (%s): %s' % (code, result)) + log.info('Server response (%s): %s', code, result) def build_post_data(self, action): # figure the data to send - the metadata plus some additional diff --git a/Lib/distutils/command/sdist.py b/Lib/distutils/command/sdist.py index f1b8d91977..4fd1d4715d 100644 --- a/Lib/distutils/command/sdist.py +++ b/Lib/distutils/command/sdist.py @@ -412,7 +412,7 @@ class sdist(Command): log.info(msg) for file in files: if not os.path.isfile(file): - log.warn("'%s' not a regular file -- skipping" % file) + log.warn("'%s' not a regular file -- skipping", file) else: dest = os.path.join(base_dir, file) self.copy_file(file, dest, link=link) -- cgit v1.2.1 From 4a1c7fc65d46f2d4b28a01496e0bf6c57b36bc35 Mon Sep 17 00:00:00 2001 From: R David Murray Date: Wed, 31 Aug 2016 11:39:35 -0400 Subject: #27904: fix distutils tests. Patch by Ville Skytt?. --- Lib/distutils/tests/test_build_py.py | 3 ++- Lib/distutils/tests/test_install_lib.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/distutils/tests/test_build_py.py b/Lib/distutils/tests/test_build_py.py index 18283dc722..0712e92c6a 100644 --- a/Lib/distutils/tests/test_build_py.py +++ b/Lib/distutils/tests/test_build_py.py @@ -168,7 +168,8 @@ class BuildPyTestCase(support.TempdirManager, finally: sys.dont_write_bytecode = old_dont_write_bytecode - self.assertIn('byte-compiling is disabled', self.logs[0][1]) + self.assertIn('byte-compiling is disabled', + self.logs[0][1] % self.logs[0][2]) def test_suite(): diff --git a/Lib/distutils/tests/test_install_lib.py b/Lib/distutils/tests/test_install_lib.py index 5378aa8249..fda6315bbc 100644 --- a/Lib/distutils/tests/test_install_lib.py +++ b/Lib/distutils/tests/test_install_lib.py @@ -104,7 +104,8 @@ class InstallLibTestCase(support.TempdirManager, finally: sys.dont_write_bytecode = old_dont_write_bytecode - self.assertIn('byte-compiling is disabled', self.logs[0][1]) + self.assertIn('byte-compiling is disabled', + self.logs[0][1] % self.logs[0][2]) def test_suite(): -- cgit v1.2.1 From 0d1cc16ecf6bb153f1f79ad3b9f534bf98fe8145 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Wed, 31 Aug 2016 19:37:28 -0400 Subject: Issue #27891: Tweak new idlelib README entry. --- Lib/idlelib/README.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/README.txt b/Lib/idlelib/README.txt index e52b5cd7b2..a879c1766e 100644 --- a/Lib/idlelib/README.txt +++ b/Lib/idlelib/README.txt @@ -242,8 +242,8 @@ Sort 'from idlelib import mod1' and 'from idlelib.mod2 import object' together by module, ignoring within module objects. Put 'import __main__' after other idlelib imports. -Imports only needed for testing are not at the top but are put in the -htest function def or the "if __name__ == '__main__'" clause. +Imports only needed for testing are put not at the top but in an +htest function def or "if __name__ == '__main__'" clause. Within module imports like "from idlelib.mod import class" may cause circular imports to deadlock. Even without this, circular imports may -- cgit v1.2.1 From c4ec850eca5230e37a53c848d3c4a683bedf3958 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Wed, 31 Aug 2016 19:45:39 -0400 Subject: Improve idlelib.textview comments. --- Lib/idlelib/textview.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/textview.py b/Lib/idlelib/textview.py index b5c9f9b0ee..adee326e1d 100644 --- a/Lib/idlelib/textview.py +++ b/Lib/idlelib/textview.py @@ -7,9 +7,8 @@ from tkinter.messagebox import showerror class TextViewer(Toplevel): - """A simple text viewer dialog for IDLE + "A simple text viewer dialog for IDLE." - """ def __init__(self, parent, title, text, modal=True, _htest=False): """Show the given text in a scrollable window with a 'close' button @@ -21,11 +20,11 @@ class TextViewer(Toplevel): """ Toplevel.__init__(self, parent) self.configure(borderwidth=5) - # place dialog below parent if running htest + # Place dialog below parent if running htest. self.geometry("=%dx%d+%d+%d" % (750, 500, parent.winfo_rootx() + 10, parent.winfo_rooty() + (10 if not _htest else 100))) - #elguavas - config placeholders til config stuff completed + # TODO: get fg/bg from theme. self.bg = '#ffffff' self.fg = '#000000' @@ -34,9 +33,9 @@ class TextViewer(Toplevel): self.protocol("WM_DELETE_WINDOW", self.Ok) self.parent = parent self.textView.focus_set() - #key bindings for this dialog - self.bind('',self.Ok) #dismiss dialog - self.bind('',self.Ok) #dismiss dialog + # Bind keys for closing this dialog. + self.bind('',self.Ok) + self.bind('',self.Ok) self.textView.insert(0.0, text) self.textView.config(state=DISABLED) -- cgit v1.2.1 From 87e182a3b8af2c419ac17927c131e111ddb0d0c9 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Wed, 31 Aug 2016 23:00:32 -0700 Subject: Minor beautification (turn nested-if into a conjunction). --- Lib/random.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'Lib') diff --git a/Lib/random.py b/Lib/random.py index 0178693ba9..13e115a8f4 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -119,12 +119,11 @@ class Random(_random.Random): x ^= len(a) a = -2 if x == -1 else x - if version == 2: - if isinstance(a, (str, bytes, bytearray)): - if isinstance(a, str): - a = a.encode() - a += _sha512(a).digest() - a = int.from_bytes(a, 'big') + if version == 2 and isinstance(a, (str, bytes, bytearray)): + if isinstance(a, str): + a = a.encode() + a += _sha512(a).digest() + a = int.from_bytes(a, 'big') super().seed(a) self.gauss_next = None -- cgit v1.2.1 From e7256f048f6506575dd31ea2a39f298692b09ee9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 1 Sep 2016 13:55:33 -0400 Subject: Issue #27919: Deprecate extra_path option in distutils. --- Lib/distutils/command/install.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'Lib') diff --git a/Lib/distutils/command/install.py b/Lib/distutils/command/install.py index fca05d69c6..0258d3deae 100644 --- a/Lib/distutils/command/install.py +++ b/Lib/distutils/command/install.py @@ -175,6 +175,7 @@ class install(Command): self.compile = None self.optimize = None + # Deprecated # These two are for putting non-packagized distributions into their # own directory and creating a .pth file if it makes sense. # 'extra_path' comes from the setup file; 'install_path_file' can @@ -344,6 +345,7 @@ class install(Command): 'scripts', 'data', 'headers', 'userbase', 'usersite') + # Deprecated # Well, we're not actually fully completely finalized yet: we still # have to deal with 'extra_path', which is the hack for allowing # non-packagized module distributions (hello, Numerical Python!) to @@ -490,6 +492,10 @@ class install(Command): self.extra_path = self.distribution.extra_path if self.extra_path is not None: + log.warn( + "Distribution option extra_path is deprecated. " + "See issue27919 for details." + ) if isinstance(self.extra_path, str): self.extra_path = self.extra_path.split(',') -- cgit v1.2.1 From eb0643ca80ce589f984e17d0dcd07e0592fca384 Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Thu, 1 Sep 2016 23:55:19 -0700 Subject: issue23591: add docs; code cleanup; more tests --- Lib/enum.py | 8 ++--- Lib/test/test_enum.py | 88 +++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 75 insertions(+), 21 deletions(-) (limited to 'Lib') diff --git a/Lib/enum.py b/Lib/enum.py index e89c17d583..83696313a4 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -10,7 +10,7 @@ except ImportError: from collections import OrderedDict -__all__ = ['EnumMeta', 'Enum', 'IntEnum', 'Flags', 'IntFlags', 'unique'] +__all__ = ['EnumMeta', 'Enum', 'IntEnum', 'Flag', 'IntFlag', 'unique'] def _is_descriptor(obj): @@ -104,7 +104,7 @@ class EnumMeta(type): enum_dict['_generate_next_value_'] = getattr(first_enum, '_generate_next_value_', None) return enum_dict - def __new__(metacls, cls, bases, classdict, **kwds): + def __new__(metacls, cls, bases, classdict): # an Enum class is final once enumeration items have been defined; it # cannot be mixed with other types (int, float, etc.) if it has an # inherited __new__ unless a new __new__ is defined (or the resulting @@ -614,7 +614,7 @@ class IntEnum(int, Enum): def _reduce_ex_by_name(self, proto): return self.name -class Flags(Enum): +class Flag(Enum): """Support for flags""" @staticmethod def _generate_next_value_(name, start, count, last_value): @@ -736,7 +736,7 @@ class Flags(Enum): return self.__class__(inverted) -class IntFlags(int, Flags): +class IntFlag(int, Flag): """Support for integer-based Flags""" @classmethod diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index 1831895d3b..cfe1b18e9a 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -3,7 +3,7 @@ import inspect import pydoc import unittest from collections import OrderedDict -from enum import Enum, IntEnum, EnumMeta, Flags, IntFlags, unique +from enum import Enum, IntEnum, EnumMeta, Flag, IntFlag, unique from io import StringIO from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL from test import support @@ -33,6 +33,14 @@ try: except Exception as exc: FloatStooges = exc +try: + class FlagStooges(Flag): + LARRY = 1 + CURLY = 2 + MOE = 3 +except Exception as exc: + FlagStooges = exc + # for pickle test and subclass tests try: class StrEnum(str, Enum): @@ -1633,13 +1641,13 @@ class TestOrder(unittest.TestCase): verde = green -class TestFlags(unittest.TestCase): +class TestFlag(unittest.TestCase): """Tests of the Flags.""" - class Perm(Flags): + class Perm(Flag): R, W, X = 4, 2, 1 - class Open(Flags): + class Open(Flag): RO = 0 WO = 1 RW = 2 @@ -1760,7 +1768,7 @@ class TestFlags(unittest.TestCase): self.assertIs((Open.WO|Open.CE) & ~Open.WO, Open.CE) def test_programatic_function_string(self): - Perm = Flags('Perm', 'R W X') + Perm = Flag('Perm', 'R W X') lst = list(Perm) self.assertEqual(len(lst), len(Perm)) self.assertEqual(len(Perm), 3, Perm) @@ -1775,7 +1783,7 @@ class TestFlags(unittest.TestCase): self.assertIs(type(e), Perm) def test_programatic_function_string_with_start(self): - Perm = Flags('Perm', 'R W X', start=8) + Perm = Flag('Perm', 'R W X', start=8) lst = list(Perm) self.assertEqual(len(lst), len(Perm)) self.assertEqual(len(Perm), 3, Perm) @@ -1790,7 +1798,7 @@ class TestFlags(unittest.TestCase): self.assertIs(type(e), Perm) def test_programatic_function_string_list(self): - Perm = Flags('Perm', ['R', 'W', 'X']) + Perm = Flag('Perm', ['R', 'W', 'X']) lst = list(Perm) self.assertEqual(len(lst), len(Perm)) self.assertEqual(len(Perm), 3, Perm) @@ -1805,7 +1813,7 @@ class TestFlags(unittest.TestCase): self.assertIs(type(e), Perm) def test_programatic_function_iterable(self): - Perm = Flags('Perm', (('R', 2), ('W', 8), ('X', 32))) + Perm = Flag('Perm', (('R', 2), ('W', 8), ('X', 32))) lst = list(Perm) self.assertEqual(len(lst), len(Perm)) self.assertEqual(len(Perm), 3, Perm) @@ -1820,7 +1828,7 @@ class TestFlags(unittest.TestCase): self.assertIs(type(e), Perm) def test_programatic_function_from_dict(self): - Perm = Flags('Perm', OrderedDict((('R', 2), ('W', 8), ('X', 32)))) + Perm = Flag('Perm', OrderedDict((('R', 2), ('W', 8), ('X', 32)))) lst = list(Perm) self.assertEqual(len(lst), len(Perm)) self.assertEqual(len(Perm), 3, Perm) @@ -1834,16 +1842,42 @@ class TestFlags(unittest.TestCase): self.assertIn(e, Perm) self.assertIs(type(e), Perm) + def test_pickle(self): + if isinstance(FlagStooges, Exception): + raise FlagStooges + test_pickle_dump_load(self.assertIs, FlagStooges.CURLY|FlagStooges.MOE) + test_pickle_dump_load(self.assertIs, FlagStooges) + -class TestIntFlags(unittest.TestCase): + def test_containment(self): + Perm = self.Perm + R, W, X = Perm + RW = R | W + RX = R | X + WX = W | X + RWX = R | W | X + self.assertTrue(R in RW) + self.assertTrue(R in RX) + self.assertTrue(R in RWX) + self.assertTrue(W in RW) + self.assertTrue(W in WX) + self.assertTrue(W in RWX) + self.assertTrue(X in RX) + self.assertTrue(X in WX) + self.assertTrue(X in RWX) + self.assertFalse(R in WX) + self.assertFalse(W in RX) + self.assertFalse(X in RW) + +class TestIntFlag(unittest.TestCase): """Tests of the IntFlags.""" - class Perm(IntFlags): + class Perm(IntFlag): X = 1 << 0 W = 1 << 1 R = 1 << 2 - class Open(IntFlags): + class Open(IntFlag): RO = 0 WO = 1 RW = 2 @@ -2003,7 +2037,7 @@ class TestIntFlags(unittest.TestCase): self.assertIs((Open.WO|Open.CE) & ~Open.WO, Open.CE) def test_programatic_function_string(self): - Perm = IntFlags('Perm', 'R W X') + Perm = IntFlag('Perm', 'R W X') lst = list(Perm) self.assertEqual(len(lst), len(Perm)) self.assertEqual(len(Perm), 3, Perm) @@ -2019,7 +2053,7 @@ class TestIntFlags(unittest.TestCase): self.assertIs(type(e), Perm) def test_programatic_function_string_with_start(self): - Perm = IntFlags('Perm', 'R W X', start=8) + Perm = IntFlag('Perm', 'R W X', start=8) lst = list(Perm) self.assertEqual(len(lst), len(Perm)) self.assertEqual(len(Perm), 3, Perm) @@ -2035,7 +2069,7 @@ class TestIntFlags(unittest.TestCase): self.assertIs(type(e), Perm) def test_programatic_function_string_list(self): - Perm = IntFlags('Perm', ['R', 'W', 'X']) + Perm = IntFlag('Perm', ['R', 'W', 'X']) lst = list(Perm) self.assertEqual(len(lst), len(Perm)) self.assertEqual(len(Perm), 3, Perm) @@ -2051,7 +2085,7 @@ class TestIntFlags(unittest.TestCase): self.assertIs(type(e), Perm) def test_programatic_function_iterable(self): - Perm = IntFlags('Perm', (('R', 2), ('W', 8), ('X', 32))) + Perm = IntFlag('Perm', (('R', 2), ('W', 8), ('X', 32))) lst = list(Perm) self.assertEqual(len(lst), len(Perm)) self.assertEqual(len(Perm), 3, Perm) @@ -2067,7 +2101,7 @@ class TestIntFlags(unittest.TestCase): self.assertIs(type(e), Perm) def test_programatic_function_from_dict(self): - Perm = IntFlags('Perm', OrderedDict((('R', 2), ('W', 8), ('X', 32)))) + Perm = IntFlag('Perm', OrderedDict((('R', 2), ('W', 8), ('X', 32)))) lst = list(Perm) self.assertEqual(len(lst), len(Perm)) self.assertEqual(len(Perm), 3, Perm) @@ -2083,6 +2117,26 @@ class TestIntFlags(unittest.TestCase): self.assertIs(type(e), Perm) + def test_containment(self): + Perm = self.Perm + R, W, X = Perm + RW = R | W + RX = R | X + WX = W | X + RWX = R | W | X + self.assertTrue(R in RW) + self.assertTrue(R in RX) + self.assertTrue(R in RWX) + self.assertTrue(W in RW) + self.assertTrue(W in WX) + self.assertTrue(W in RWX) + self.assertTrue(X in RX) + self.assertTrue(X in WX) + self.assertTrue(X in RWX) + self.assertFalse(R in WX) + self.assertFalse(W in RX) + self.assertFalse(X in RW) + class TestUnique(unittest.TestCase): def test_unique_clean(self): -- cgit v1.2.1 From 631c3df7f708d99b0384c07d5650af03e2b51de2 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 2 Sep 2016 12:12:23 +0200 Subject: PEP 7 style for if/else in C Add also a newline for readability in normalize_encoding(). --- Lib/encodings/__init__.py | 1 + 1 file changed, 1 insertion(+) (limited to 'Lib') diff --git a/Lib/encodings/__init__.py b/Lib/encodings/__init__.py index 8dd713056e..320011bd0e 100644 --- a/Lib/encodings/__init__.py +++ b/Lib/encodings/__init__.py @@ -54,6 +54,7 @@ def normalize_encoding(encoding): """ if isinstance(encoding, bytes): encoding = str(encoding, "ascii") + chars = [] punct = False for c in encoding: -- cgit v1.2.1 From 66c9c618217fe911751f4e77925bd77c91885c81 Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Fri, 2 Sep 2016 15:50:21 -0700 Subject: issue23591: optimize _high_bit() --- Lib/enum.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'Lib') diff --git a/Lib/enum.py b/Lib/enum.py index 83696313a4..8d23933d3e 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -784,13 +784,8 @@ class IntFlag(int, Flag): def _high_bit(value): - """return the highest bit set in value""" - bit = 0 - while 'looking for the highest bit': - limit = 2 ** bit - if limit > value: - return bit - 1 - bit += 1 + """returns index of highest bit, or -1 if value is zero or negative""" + return value.bit_length() - 1 if value > 0 else -1 def unique(enumeration): """Class decorator for enumerations ensuring unique member values.""" -- cgit v1.2.1 From aaffa44e37af7aa449a6bd0cab5d7ba035b47aa1 Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Fri, 2 Sep 2016 16:32:32 -0700 Subject: issue23591: bool(empty_flags) == False; more docs & tests --- Lib/enum.py | 3 +++ Lib/test/test_enum.py | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) (limited to 'Lib') diff --git a/Lib/enum.py b/Lib/enum.py index 8d23933d3e..1e028a364f 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -714,6 +714,9 @@ class Flag(Enum): '|'.join([str(m._name_ or m._value_) for m in members]), ) + def __bool__(self): + return bool(self._value_) + def __or__(self, other): if not isinstance(other, self.__class__): return NotImplemented diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index cfe1b18e9a..cf704edb1b 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -1767,6 +1767,14 @@ class TestFlag(unittest.TestCase): self.assertIs(Open.WO & ~Open.WO, Open.RO) self.assertIs((Open.WO|Open.CE) & ~Open.WO, Open.CE) + def test_bool(self): + Perm = self.Perm + for f in Perm: + self.assertTrue(f) + Open = self.Open + for f in Open: + self.assertEqual(bool(f.value), bool(f)) + def test_programatic_function_string(self): Perm = Flag('Perm', 'R W X') lst = list(Perm) @@ -2137,6 +2145,14 @@ class TestIntFlag(unittest.TestCase): self.assertFalse(W in RX) self.assertFalse(X in RW) + def test_bool(self): + Perm = self.Perm + for f in Perm: + self.assertTrue(f) + Open = self.Open + for f in Open: + self.assertEqual(bool(f.value), bool(f)) + class TestUnique(unittest.TestCase): def test_unique_clean(self): -- cgit v1.2.1 From 85910de019618a6cf261de10b697d444bfc3a127 Mon Sep 17 00:00:00 2001 From: "Eric V. Smith" Date: Sat, 3 Sep 2016 09:18:34 -0400 Subject: Closes issue 27921: Disallow backslashes anywhere in f-strings. This is a temporary restriction. In 3.6 beta 2, the plan is to again allow backslashes in the string parts of f-strings, but disallow them in the expression parts. --- Lib/test/libregrtest/save_env.py | 2 +- Lib/test/test_fstring.py | 142 +++++++++++++----------------------- Lib/test/test_tools/test_unparse.py | 4 - Lib/traceback.py | 4 +- 4 files changed, 54 insertions(+), 98 deletions(-) (limited to 'Lib') diff --git a/Lib/test/libregrtest/save_env.py b/Lib/test/libregrtest/save_env.py index 96ad3af8df..eefbc14ad2 100644 --- a/Lib/test/libregrtest/save_env.py +++ b/Lib/test/libregrtest/save_env.py @@ -280,6 +280,6 @@ class saved_test_environment: print(f"Warning -- {name} was modified by {self.testname}", file=sys.stderr, flush=True) if self.verbose > 1: - print(f" Before: {original}\n After: {current} ", + print(f" Before: {original}""\n"f" After: {current} ", file=sys.stderr, flush=True) return False diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index 905ae63126..2ba1b2169f 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -96,30 +96,6 @@ f'{a * x()}'""" self.assertEqual(f'', '') self.assertEqual(f'a', 'a') self.assertEqual(f' ', ' ') - self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}', - '\N{GREEK CAPITAL LETTER DELTA}') - self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}', - '\u0394') - self.assertEqual(f'\N{True}', '\u22a8') - self.assertEqual(rf'\N{True}', r'\NTrue') - - def test_escape_order(self): - # note that hex(ord('{')) == 0x7b, so this - # string becomes f'a{4*10}b' - self.assertEqual(f'a\u007b4*10}b', 'a40b') - self.assertEqual(f'a\x7b4*10}b', 'a40b') - self.assertEqual(f'a\x7b4*10\N{RIGHT CURLY BRACKET}b', 'a40b') - self.assertEqual(f'{"a"!\N{LATIN SMALL LETTER R}}', "'a'") - self.assertEqual(f'{10\x3a02X}', '0A') - self.assertEqual(f'{10:02\N{LATIN CAPITAL LETTER X}}', '0A') - - self.assertAllRaise(SyntaxError, "f-string: single '}' is not allowed", - [r"""f'a{\u007b4*10}b'""", # mis-matched brackets - ]) - self.assertAllRaise(SyntaxError, 'unexpected character after line continuation character', - [r"""f'{"a"\!r}'""", - r"""f'{a\!r}'""", - ]) def test_unterminated_string(self): self.assertAllRaise(SyntaxError, 'f-string: unterminated string', @@ -285,8 +261,6 @@ f'{a * x()}'""" "f'{ !r}'", "f'{10:{ }}'", "f' { } '", - r"f'{\n}'", - r"f'{\n \n}'", # Catch the empty expression before the # invalid conversion. @@ -328,24 +302,61 @@ f'{a * x()}'""" ["f'{\n}'", ]) + def test_no_backslashes(self): + # See issue 27921 + + # These should work, but currently don't + self.assertAllRaise(SyntaxError, 'backslashes not allowed', + [r"f'\t'", + r"f'{2}\t'", + r"f'{2}\t{3}'", + r"f'\t{3}'", + + r"f'\N{GREEK CAPITAL LETTER DELTA}'", + r"f'{2}\N{GREEK CAPITAL LETTER DELTA}'", + r"f'{2}\N{GREEK CAPITAL LETTER DELTA}{3}'", + r"f'\N{GREEK CAPITAL LETTER DELTA}{3}'", + + r"f'\u0394'", + r"f'{2}\u0394'", + r"f'{2}\u0394{3}'", + r"f'\u0394{3}'", + + r"f'\U00000394'", + r"f'{2}\U00000394'", + r"f'{2}\U00000394{3}'", + r"f'\U00000394{3}'", + + r"f'\x20'", + r"f'{2}\x20'", + r"f'{2}\x20{3}'", + r"f'\x20{3}'", + + r"f'2\x20'", + r"f'2\x203'", + r"f'2\x203'", + ]) + + # And these don't work now, and shouldn't work in the future. + self.assertAllRaise(SyntaxError, 'backslashes not allowed', + [r"f'{\'a\'}'", + r"f'{\t3}'", + ]) + + # add this when backslashes are allowed again. see issue 27921 + # these test will be needed because unicode names will be parsed + # differently once backslashes are allowed inside expressions + ## def test_misformed_unicode_character_name(self): + ## self.assertAllRaise(SyntaxError, 'xx', + ## [r"f'\N'", + ## [r"f'\N{'", + ## [r"f'\N{GREEK CAPITAL LETTER DELTA'", + ## ]) + def test_newlines_in_expressions(self): self.assertEqual(f'{0}', '0') - self.assertEqual(f'{0\n}', '0') - self.assertEqual(f'{0\r}', '0') - self.assertEqual(f'{\n0\n}', '0') - self.assertEqual(f'{\r0\r}', '0') - self.assertEqual(f'{\n0\r}', '0') - self.assertEqual(f'{\n0}', '0') - self.assertEqual(f'{3+\n4}', '7') - self.assertEqual(f'{3+\\\n4}', '7') self.assertEqual(rf'''{3+ 4}''', '7') - self.assertEqual(f'''{3+\ -4}''', '7') - - self.assertAllRaise(SyntaxError, 'f-string: empty expression not allowed', - [r"f'{\n}'", - ]) def test_lambda(self): x = 5 @@ -380,9 +391,6 @@ f'{a * x()}'""" def test_expressions_with_triple_quoted_strings(self): self.assertEqual(f"{'''x'''}", 'x') self.assertEqual(f"{'''eric's'''}", "eric's") - self.assertEqual(f'{"""eric\'s"""}', "eric's") - self.assertEqual(f"{'''eric\"s'''}", 'eric"s') - self.assertEqual(f'{"""eric"s"""}', 'eric"s') # Test concatenation within an expression self.assertEqual(f'{"x" """eric"s""" "y"}', 'xeric"sy') @@ -484,10 +492,6 @@ f'{a * x()}'""" y = 5 self.assertEqual(f'{f"{0}"*3}', '000') self.assertEqual(f'{f"{y}"*3}', '555') - self.assertEqual(f'{f"{\'x\'}"*3}', 'xxx') - - self.assertEqual(f"{r'x' f'{\"s\"}'}", 'xs') - self.assertEqual(f"{r'x'rf'{\"s\"}'}", 'xs') def test_invalid_string_prefixes(self): self.assertAllRaise(SyntaxError, 'unexpected EOF while parsing', @@ -510,24 +514,14 @@ f'{a * x()}'""" def test_leading_trailing_spaces(self): self.assertEqual(f'{ 3}', '3') self.assertEqual(f'{ 3}', '3') - self.assertEqual(f'{\t3}', '3') - self.assertEqual(f'{\t\t3}', '3') self.assertEqual(f'{3 }', '3') self.assertEqual(f'{3 }', '3') - self.assertEqual(f'{3\t}', '3') - self.assertEqual(f'{3\t\t}', '3') self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]}}', 'expr={1: 2}') self.assertEqual(f'expr={ {x: y for x, y in [(1, 2), ]} }', 'expr={1: 2}') - def test_character_name(self): - self.assertEqual(f'{4}\N{GREEK CAPITAL LETTER DELTA}{3}', - '4\N{GREEK CAPITAL LETTER DELTA}3') - self.assertEqual(f'{{}}\N{GREEK CAPITAL LETTER DELTA}{3}', - '{}\N{GREEK CAPITAL LETTER DELTA}3') - def test_not_equal(self): # There's a special test for this because there's a special # case in the f-string parser to look for != as not ending an @@ -554,10 +548,6 @@ f'{a * x()}'""" # Not a conversion, but show that ! is allowed in a format spec. self.assertEqual(f'{3.14:!<10.10}', '3.14!!!!!!') - self.assertEqual(f'{"\N{GREEK CAPITAL LETTER DELTA}"}', '\u0394') - self.assertEqual(f'{"\N{GREEK CAPITAL LETTER DELTA}"!r}', "'\u0394'") - self.assertEqual(f'{"\N{GREEK CAPITAL LETTER DELTA}"!a}', "'\\u0394'") - self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character', ["f'{3!g}'", "f'{3!A}'", @@ -565,9 +555,7 @@ f'{a * x()}'""" "f'{3!A}'", "f'{3!!}'", "f'{3!:}'", - "f'{3!\N{GREEK CAPITAL LETTER DELTA}}'", "f'{3! s}'", # no space before conversion char - "f'{x!\\x00:.<10}'", ]) self.assertAllRaise(SyntaxError, "f-string: expecting '}'", @@ -600,7 +588,6 @@ f'{a * x()}'""" # Can't have { or } in a format spec. "f'{3:}>10}'", - r"f'{3:\\}>10}'", "f'{3:}}>10}'", ]) @@ -620,10 +607,6 @@ f'{a * x()}'""" "f'{'", ]) - self.assertAllRaise(SyntaxError, 'invalid syntax', - [r"f'{3:\\{>10}'", - ]) - # But these are just normal strings. self.assertEqual(f'{"{"}', '{') self.assertEqual(f'{"}"}', '}') @@ -712,34 +695,11 @@ f'{a * x()}'""" "'": 'squote', 'foo': 'bar', } - self.assertEqual(f'{d["\'"]}', 'squote') - self.assertEqual(f"{d['\"']}", 'dquote') - self.assertEqual(f'''{d["'"]}''', 'squote') self.assertEqual(f"""{d['"']}""", 'dquote') self.assertEqual(f'{d["foo"]}', 'bar') self.assertEqual(f"{d['foo']}", 'bar') - self.assertEqual(f'{d[\'foo\']}', 'bar') - self.assertEqual(f"{d[\"foo\"]}", 'bar') - - def test_escaped_quotes(self): - d = {'"': 'a', - "'": 'b'} - - self.assertEqual(fr"{d['\"']}", 'a') - self.assertEqual(fr'{d["\'"]}', 'b') - self.assertEqual(fr"{'\"'}", '"') - self.assertEqual(fr'{"\'"}', "'") - self.assertEqual(f'{"\\"3"}', '"3') - - self.assertAllRaise(SyntaxError, 'f-string: unterminated string', - [r'''f'{"""\\}' ''', # Backslash at end of expression - ]) - self.assertAllRaise(SyntaxError, 'unexpected character after line continuation', - [r"rf'{3\}'", - ]) - if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_tools/test_unparse.py b/Lib/test/test_tools/test_unparse.py index d91ade9228..4a903b6c68 100644 --- a/Lib/test/test_tools/test_unparse.py +++ b/Lib/test/test_tools/test_unparse.py @@ -138,10 +138,6 @@ class UnparseTestCase(ASTTestCase): # See issue 25180 self.check_roundtrip(r"""f'{f"{0}"*3}'""") self.check_roundtrip(r"""f'{f"{y}"*3}'""") - self.check_roundtrip(r"""f'{f"{\'x\'}"*3}'""") - - self.check_roundtrip(r'''f"{r'x' f'{\"s\"}'}"''') - self.check_roundtrip(r'''f"{r'x'rf'{\"s\"}'}"''') def test_del_statement(self): self.check_roundtrip("del x, y, z") diff --git a/Lib/traceback.py b/Lib/traceback.py index a1cb5fb1ef..6fc643628e 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -402,7 +402,7 @@ class StackSummary(list): count += 1 else: if count > 3: - result.append(f' [Previous line repeated {count-3} more times]\n') + result.append(f' [Previous line repeated {count-3} more times]''\n') last_file = frame.filename last_line = frame.lineno last_name = frame.name @@ -419,7 +419,7 @@ class StackSummary(list): row.append(' {name} = {value}\n'.format(name=name, value=value)) result.append(''.join(row)) if count > 3: - result.append(f' [Previous line repeated {count-3} more times]\n') + result.append(f' [Previous line repeated {count-3} more times]''\n') return result -- cgit v1.2.1 From 1d2b9e79f95d43e224e4a3e898b5e3c9bab30582 Mon Sep 17 00:00:00 2001 From: "Eric V. Smith" Date: Sat, 3 Sep 2016 10:43:20 -0400 Subject: Issue 27921: Remove backslash from another f-string. --- Lib/http/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/http/client.py b/Lib/http/client.py index 9107412922..230bccec98 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -1060,7 +1060,7 @@ class HTTPConnection: if encode_chunked and self._http_vsn == 11: # chunked encoding - chunk = f'{len(chunk):X}\r\n'.encode('ascii') + chunk \ + chunk = f'{len(chunk):X}''\r\n'.encode('ascii') + chunk \ + b'\r\n' self.send(chunk) -- cgit v1.2.1 From b0e37ae0088ddea4584a58696ba66416e5dc6e95 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Sat, 3 Sep 2016 15:56:07 +0100 Subject: Fixes #27937: optimise code used in all logging calls. --- Lib/logging/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index 4d872bd044..0c5c2ec28f 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -130,8 +130,9 @@ def getLevelName(level): Otherwise, the string "Level %s" % level is returned. """ - # See Issue #22386 for the reason for this convoluted expression - return _levelToName.get(level, _nameToLevel.get(level, ("Level %s" % level))) + # See Issues #22386 and #27937 for why it's this way + return (_levelToName.get(level) or _nameToLevel.get(level) or + "Level %s" % level) def addLevelName(level, levelName): """ -- cgit v1.2.1 From 62684db1cd476b3c64270058d4686a965b4802ff Mon Sep 17 00:00:00 2001 From: "Eric V. Smith" Date: Sat, 3 Sep 2016 11:01:53 -0400 Subject: Issue 27921: Remove backslash from another f-string. I'll revert this change before beta 2. --- Lib/test/test_traceback.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 665abb462b..fc7e6cce96 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -325,13 +325,13 @@ class TracebackFormatTests(unittest.TestCase): lineno_f = f.__code__.co_firstlineno result_f = ( 'Traceback (most recent call last):\n' - f' File "{__file__}", line {lineno_f+5}, in _check_recursive_traceback_display\n' + f' File "{__file__}", line {lineno_f+5}, in _check_recursive_traceback_display''\n' ' f()\n' - f' File "{__file__}", line {lineno_f+1}, in f\n' + f' File "{__file__}", line {lineno_f+1}, in f''\n' ' f()\n' - f' File "{__file__}", line {lineno_f+1}, in f\n' + f' File "{__file__}", line {lineno_f+1}, in f''\n' ' f()\n' - f' File "{__file__}", line {lineno_f+1}, in f\n' + f' File "{__file__}", line {lineno_f+1}, in f''\n' ' f()\n' # XXX: The following line changes depending on whether the tests # are run through the interactive interpreter or with -m @@ -370,20 +370,20 @@ class TracebackFormatTests(unittest.TestCase): lineno_g = g.__code__.co_firstlineno result_g = ( - f' File "{__file__}", line {lineno_g+2}, in g\n' + f' File "{__file__}", line {lineno_g+2}, in g''\n' ' return g(count-1)\n' - f' File "{__file__}", line {lineno_g+2}, in g\n' + f' File "{__file__}", line {lineno_g+2}, in g''\n' ' return g(count-1)\n' - f' File "{__file__}", line {lineno_g+2}, in g\n' + f' File "{__file__}", line {lineno_g+2}, in g''\n' ' return g(count-1)\n' ' [Previous line repeated 6 more times]\n' - f' File "{__file__}", line {lineno_g+3}, in g\n' + f' File "{__file__}", line {lineno_g+3}, in g''\n' ' raise ValueError\n' 'ValueError\n' ) tb_line = ( 'Traceback (most recent call last):\n' - f' File "{__file__}", line {lineno_g+7}, in _check_recursive_traceback_display\n' + f' File "{__file__}", line {lineno_g+7}, in _check_recursive_traceback_display''\n' ' g()\n' ) expected = (tb_line + result_g).splitlines() @@ -407,16 +407,16 @@ class TracebackFormatTests(unittest.TestCase): lineno_h = h.__code__.co_firstlineno result_h = ( 'Traceback (most recent call last):\n' - f' File "{__file__}", line {lineno_h+7}, in _check_recursive_traceback_display\n' + f' File "{__file__}", line {lineno_h+7}, in _check_recursive_traceback_display''\n' ' h()\n' - f' File "{__file__}", line {lineno_h+2}, in h\n' + f' File "{__file__}", line {lineno_h+2}, in h''\n' ' return h(count-1)\n' - f' File "{__file__}", line {lineno_h+2}, in h\n' + f' File "{__file__}", line {lineno_h+2}, in h''\n' ' return h(count-1)\n' - f' File "{__file__}", line {lineno_h+2}, in h\n' + f' File "{__file__}", line {lineno_h+2}, in h''\n' ' return h(count-1)\n' ' [Previous line repeated 6 more times]\n' - f' File "{__file__}", line {lineno_h+3}, in h\n' + f' File "{__file__}", line {lineno_h+3}, in h''\n' ' g()\n' ) expected = (result_h + result_g).splitlines() -- cgit v1.2.1 From 3594ee1f37b406e07df3ebcbe97e19339c447fac Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Sat, 3 Sep 2016 17:04:36 +0100 Subject: Closes #27935: returned numeric value for 'FATAL' logging level. --- Lib/logging/__init__.py | 1 + Lib/test/test_logging.py | 4 ++++ 2 files changed, 5 insertions(+) (limited to 'Lib') diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index 0c5c2ec28f..2590d6528f 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -108,6 +108,7 @@ _levelToName = { } _nameToLevel = { 'CRITICAL': CRITICAL, + 'FATAL': FATAL, 'ERROR': ERROR, 'WARN': WARNING, 'WARNING': WARNING, diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 7899c77fb9..ff0012beb2 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -309,6 +309,10 @@ class BuiltinLevelsTest(BaseTest): self.assertEqual(logging.getLevelName('INFO'), logging.INFO) self.assertEqual(logging.getLevelName(logging.INFO), 'INFO') + def test_issue27935(self): + fatal = logging.getLevelName('FATAL') + self.assertEqual(fatal, logging.FATAL) + class BasicFilterTest(BaseTest): """Test the bundled Filter class.""" -- cgit v1.2.1 From 0a6df009f15614433b04a5640f5167a1d99245ff Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sat, 3 Sep 2016 17:21:29 +0100 Subject: Issue #11734: Add support for IEEE 754 half-precision floats to the struct module. Original patch by Eli Stevens. --- Lib/test/test_struct.py | 107 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 105 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py index efbdbfcc58..2ce855d458 100644 --- a/Lib/test/test_struct.py +++ b/Lib/test/test_struct.py @@ -1,5 +1,6 @@ from collections import abc import array +import math import operator import unittest import struct @@ -366,8 +367,6 @@ class StructTest(unittest.TestCase): # SF bug 705836. "f" had a severe rounding bug, where a carry # from the low-order discarded bits could propagate into the exponent # field, causing the result to be wrong by a factor of 2. - import math - for base in range(1, 33): # smaller <- largest representable float less than base. delta = 0.5 @@ -659,6 +658,110 @@ class UnpackIteratorTest(unittest.TestCase): self.assertRaises(StopIteration, next, it) self.assertRaises(StopIteration, next, it) + def test_half_float(self): + # Little-endian examples from: + # http://en.wikipedia.org/wiki/Half_precision_floating-point_format + format_bits_float__cleanRoundtrip_list = [ + (b'\x00\x3c', 1.0), + (b'\x00\xc0', -2.0), + (b'\xff\x7b', 65504.0), # (max half precision) + (b'\x00\x04', 2**-14), # ~= 6.10352 * 10**-5 (min pos normal) + (b'\x01\x00', 2**-24), # ~= 5.96046 * 10**-8 (min pos subnormal) + (b'\x00\x00', 0.0), + (b'\x00\x80', -0.0), + (b'\x00\x7c', float('+inf')), + (b'\x00\xfc', float('-inf')), + (b'\x55\x35', 0.333251953125), # ~= 1/3 + ] + + for le_bits, f in format_bits_float__cleanRoundtrip_list: + be_bits = le_bits[::-1] + self.assertEqual(f, struct.unpack('e', be_bits)[0]) + self.assertEqual(be_bits, struct.pack('>e', f)) + if sys.byteorder == 'little': + self.assertEqual(f, struct.unpack('e', le_bits)[0]) + self.assertEqual(le_bits, struct.pack('e', f)) + else: + self.assertEqual(f, struct.unpack('e', be_bits)[0]) + self.assertEqual(be_bits, struct.pack('e', f)) + + # Check for NaN handling: + format_bits__nan_list = [ + ('e', bits[::-1])[0])) + + # Check that packing produces a bit pattern representing a quiet NaN: + # all exponent bits and the msb of the fraction should all be 1. + packed = struct.pack('e', b'\x00\x01', 2.0**-25 + 2.0**-35), # Rounds to minimum subnormal + ('>e', b'\x00\x00', 2.0**-25), # Underflows to zero (nearest even mode) + ('>e', b'\x00\x00', 2.0**-26), # Underflows to zero + ('>e', b'\x03\xff', 2.0**-14 - 2.0**-24), # Largest subnormal. + ('>e', b'\x03\xff', 2.0**-14 - 2.0**-25 - 2.0**-65), + ('>e', b'\x04\x00', 2.0**-14 - 2.0**-25), + ('>e', b'\x04\x00', 2.0**-14), # Smallest normal. + ('>e', b'\x3c\x01', 1.0+2.0**-11 + 2.0**-16), # rounds to 1.0+2**(-10) + ('>e', b'\x3c\x00', 1.0+2.0**-11), # rounds to 1.0 (nearest even mode) + ('>e', b'\x3c\x00', 1.0+2.0**-12), # rounds to 1.0 + ('>e', b'\x7b\xff', 65504), # largest normal + ('>e', b'\x7b\xff', 65519), # rounds to 65504 + ('>e', b'\x80\x01', -2.0**-25 - 2.0**-35), # Rounds to minimum subnormal + ('>e', b'\x80\x00', -2.0**-25), # Underflows to zero (nearest even mode) + ('>e', b'\x80\x00', -2.0**-26), # Underflows to zero + ('>e', b'\xbc\x01', -1.0-2.0**-11 - 2.0**-16), # rounds to 1.0+2**(-10) + ('>e', b'\xbc\x00', -1.0-2.0**-11), # rounds to 1.0 (nearest even mode) + ('>e', b'\xbc\x00', -1.0-2.0**-12), # rounds to 1.0 + ('>e', b'\xfb\xff', -65519), # rounds to 65504 + ] + + for formatcode, bits, f in format_bits_float__rounding_list: + self.assertEqual(bits, struct.pack(formatcode, f)) + + # This overflows, and so raises an error + format_bits_float__roundingError_list = [ + # Values that round to infinity. + ('>e', 65520.0), + ('>e', 65536.0), + ('>e', 1e300), + ('>e', -65520.0), + ('>e', -65536.0), + ('>e', -1e300), + ('e', b'\x67\xff', 0x1ffdffffff * 2**-26), # should be 2047, if double-rounded 64>32>16, becomes 2048 + ] + + for formatcode, bits, f in format_bits_float__doubleRoundingError_list: + self.assertEqual(bits, struct.pack(formatcode, f)) + if __name__ == '__main__': unittest.main() -- cgit v1.2.1 From f497c6e5515fe994e22b60a1ba1f9cb104919e10 Mon Sep 17 00:00:00 2001 From: "Eric V. Smith" Date: Sat, 3 Sep 2016 12:33:38 -0400 Subject: Issue 27921: Remove backslash from another f-string. I'll revert this change before beta 2. I also need to look in to why test_tools/test_unparse fails with the files that are now being skipped. --- Lib/test/test_faulthandler.py | 4 ++-- Lib/test/test_tools/test_unparse.py | 10 ++++++++++ Lib/traceback.py | 4 ++-- 3 files changed, 14 insertions(+), 4 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py index fc2d6d7bae..1ff17bbcf4 100644 --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py @@ -735,11 +735,11 @@ class FaultHandlerTests(unittest.TestCase): ('EXCEPTION_INT_DIVIDE_BY_ZERO', 'int divide by zero'), ('EXCEPTION_STACK_OVERFLOW', 'stack overflow'), ): - self.check_windows_exception(f""" + self.check_windows_exception(""" import faulthandler faulthandler.enable() faulthandler._raise_exception(faulthandler._{exc}) - """, + """.format(exc=exc), 3, name) diff --git a/Lib/test/test_tools/test_unparse.py b/Lib/test/test_tools/test_unparse.py index 4a903b6c68..1865fc8e44 100644 --- a/Lib/test/test_tools/test_unparse.py +++ b/Lib/test/test_tools/test_unparse.py @@ -284,6 +284,16 @@ class DirectoryTestCase(ASTTestCase): for filename in names: if test.support.verbose: print('Testing %s' % filename) + + # it's very much a hack that I'm skipping these files, but + # I can't figure out why they fail. I'll fix it when I + # address issue #27948. + if (filename.endswith('/test_fstring.py') or + filename.endswith('/test_traceback.py')): + if test.support.verbose: + print(f'Skipping {filename}: see issue 27921') + continue + source = read_pyfile(filename) self.check_roundtrip(source) diff --git a/Lib/traceback.py b/Lib/traceback.py index 6fc643628e..a15b818565 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -402,7 +402,7 @@ class StackSummary(list): count += 1 else: if count > 3: - result.append(f' [Previous line repeated {count-3} more times]''\n') + result.append(f' [Previous line repeated {count-3} more times]'+'\n') last_file = frame.filename last_line = frame.lineno last_name = frame.name @@ -419,7 +419,7 @@ class StackSummary(list): row.append(' {name} = {value}\n'.format(name=name, value=value)) result.append(''.join(row)) if count > 3: - result.append(f' [Previous line repeated {count-3} more times]''\n') + result.append(f' [Previous line repeated {count-3} more times]'+'\n') return result -- cgit v1.2.1 From 87cfa860698b2a649decb8cb93e1d621cd7dadc2 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sat, 3 Sep 2016 19:30:22 +0100 Subject: Issue #26040: Improve test_math and test_cmath coverage and rigour. Thanks Jeff Allen. --- Lib/test/cmath_testcases.txt | 141 +++++++++++++++++++++++ Lib/test/test_math.py | 265 +++++++++++++++++++++++++++++-------------- 2 files changed, 319 insertions(+), 87 deletions(-) (limited to 'Lib') diff --git a/Lib/test/cmath_testcases.txt b/Lib/test/cmath_testcases.txt index 9b0865302e..dd7e458ddc 100644 --- a/Lib/test/cmath_testcases.txt +++ b/Lib/test/cmath_testcases.txt @@ -53,6 +53,12 @@ -- MPFR homepage at http://www.mpfr.org for more information about the -- MPFR project. +-- A minority of the test cases were generated with the help of +-- mpmath 0.19 at 100 bit accuracy (http://mpmath.org) to improve +-- coverage of real functions with real-valued arguments. These are +-- used in test.test_math.MathTests.test_testfile, as well as in +-- test_cmath. + -------------------------- -- acos: Inverse cosine -- @@ -848,6 +854,18 @@ atan0302 atan 9.9999999999999999e-161 -1.0 -> 0.78539816339744828 -184.553381029 atan0303 atan -1e-165 1.0 -> -0.78539816339744828 190.30984376228875 atan0304 atan -9.9998886718268301e-321 -1.0 -> -0.78539816339744828 -368.76019403576692 +-- Additional real values (mpmath) +atan0400 atan 1.7976931348623157e+308 0.0 -> 1.5707963267948966192 0.0 +atan0401 atan -1.7976931348623157e+308 0.0 -> -1.5707963267948966192 0.0 +atan0402 atan 1e-17 0.0 -> 1.0000000000000000715e-17 0.0 +atan0403 atan -1e-17 0.0 -> -1.0000000000000000715e-17 0.0 +atan0404 atan 0.0001 0.0 -> 0.000099999999666666673459 0.0 +atan0405 atan -0.0001 0.0 -> -0.000099999999666666673459 0.0 +atan0406 atan 0.999999999999999 0.0 -> 0.78539816339744781002 0.0 +atan0407 atan 1.000000000000001 0.0 -> 0.78539816339744886473 0.0 +atan0408 atan 14.101419947171719 0.0 -> 1.4999999999999999969 0.0 +atan0409 atan 1255.7655915007897 0.0 -> 1.5700000000000000622 0.0 + -- special values atan1000 atan -0.0 0.0 -> -0.0 0.0 atan1001 atan nan 0.0 -> nan 0.0 @@ -1514,6 +1532,11 @@ sqrt0131 sqrt -1.5477066694467245e-310 -0.0 -> 0.0 -1.2440685951533077e-155 sqrt0140 sqrt 1.6999999999999999e+308 -1.6999999999999999e+308 -> 1.4325088230154573e+154 -5.9336458271212207e+153 sqrt0141 sqrt -1.797e+308 -9.9999999999999999e+306 -> 3.7284476432057307e+152 -1.3410406899802901e+154 +-- Additional real values (mpmath) +sqrt0150 sqrt 1.7976931348623157e+308 0.0 -> 1.3407807929942596355e+154 0.0 +sqrt0151 sqrt 2.2250738585072014e-308 0.0 -> 1.4916681462400413487e-154 0.0 +sqrt0152 sqrt 5e-324 0.0 -> 2.2227587494850774834e-162 0.0 + -- special values sqrt1000 sqrt 0.0 0.0 -> 0.0 0.0 sqrt1001 sqrt -0.0 0.0 -> 0.0 0.0 @@ -1616,6 +1639,20 @@ exp0052 exp 710.0 1.5 -> 1.5802653829857376e+307 inf overflow exp0053 exp 710.0 1.6 -> -6.5231579995501372e+306 inf overflow exp0054 exp 710.0 2.8 -> -inf 7.4836177417448528e+307 overflow +-- Additional real values (mpmath) +exp0070 exp 1e-08 0.0 -> 1.00000001000000005 0.0 +exp0071 exp 0.0003 0.0 -> 1.0003000450045003375 0.0 +exp0072 exp 0.2 0.0 -> 1.2214027581601698475 0.0 +exp0073 exp 1.0 0.0 -> 2.7182818284590452354 0.0 +exp0074 exp -1e-08 0.0 -> 0.99999999000000005 0.0 +exp0075 exp -0.0003 0.0 -> 0.99970004499550033751 0.0 +exp0076 exp -1.0 0.0 -> 0.3678794411714423216 0.0 +exp0077 exp 2.220446049250313e-16 0.0 -> 1.000000000000000222 0.0 +exp0078 exp -1.1102230246251565e-16 0.0 -> 0.99999999999999988898 0.0 +exp0079 exp 2.302585092994046 0.0 -> 10.000000000000002171 0.0 +exp0080 exp -2.302585092994046 0.0 -> 0.099999999999999978292 0.0 +exp0081 exp 709.7827 0.0 -> 1.7976699566638014654e+308 0.0 + -- special values exp1000 exp 0.0 0.0 -> 1.0 0.0 exp1001 exp -0.0 0.0 -> 1.0 0.0 @@ -1708,6 +1745,23 @@ cosh0023 cosh 2.218885944363501 2.0015727395883687 -> -1.94294321081968 4.129026 cosh0030 cosh 710.5 2.3519999999999999 -> -1.2967465239355998e+308 1.3076707908857333e+308 cosh0031 cosh -710.5 0.69999999999999996 -> 1.4085466381392499e+308 -1.1864024666450239e+308 +-- Additional real values (mpmath) +cosh0050 cosh 1e-150 0.0 -> 1.0 0.0 +cosh0051 cosh 1e-18 0.0 -> 1.0 0.0 +cosh0052 cosh 1e-09 0.0 -> 1.0000000000000000005 0.0 +cosh0053 cosh 0.0003 0.0 -> 1.0000000450000003375 0.0 +cosh0054 cosh 0.2 0.0 -> 1.0200667556190758485 0.0 +cosh0055 cosh 1.0 0.0 -> 1.5430806348152437785 0.0 +cosh0056 cosh -1e-18 0.0 -> 1.0 -0.0 +cosh0057 cosh -0.0003 0.0 -> 1.0000000450000003375 -0.0 +cosh0058 cosh -1.0 0.0 -> 1.5430806348152437785 -0.0 +cosh0059 cosh 1.3169578969248168 0.0 -> 2.0000000000000001504 0.0 +cosh0060 cosh -1.3169578969248168 0.0 -> 2.0000000000000001504 -0.0 +cosh0061 cosh 17.328679513998633 0.0 -> 16777216.000000021938 0.0 +cosh0062 cosh 18.714973875118524 0.0 -> 67108864.000000043662 0.0 +cosh0063 cosh 709.7827 0.0 -> 8.9883497833190073272e+307 0.0 +cosh0064 cosh -709.7827 0.0 -> 8.9883497833190073272e+307 -0.0 + -- special values cosh1000 cosh 0.0 0.0 -> 1.0 0.0 cosh1001 cosh 0.0 inf -> nan 0.0 invalid ignore-imag-sign @@ -1800,6 +1854,24 @@ sinh0023 sinh 0.043713693678420068 0.22512549887532657 -> 0.042624198673416713 0 sinh0030 sinh 710.5 -2.3999999999999999 -> -1.3579970564885919e+308 -1.24394470907798e+308 sinh0031 sinh -710.5 0.80000000000000004 -> -1.2830671601735164e+308 1.3210954193997678e+308 +-- Additional real values (mpmath) +sinh0050 sinh 1e-100 0.0 -> 1.00000000000000002e-100 0.0 +sinh0051 sinh 5e-17 0.0 -> 4.9999999999999998955e-17 0.0 +sinh0052 sinh 1e-16 0.0 -> 9.999999999999999791e-17 0.0 +sinh0053 sinh 3.7e-08 0.0 -> 3.7000000000000008885e-8 0.0 +sinh0054 sinh 0.001 0.0 -> 0.0010000001666666750208 0.0 +sinh0055 sinh 0.2 0.0 -> 0.20133600254109399895 0.0 +sinh0056 sinh 1.0 0.0 -> 1.1752011936438014569 0.0 +sinh0057 sinh -3.7e-08 0.0 -> -3.7000000000000008885e-8 0.0 +sinh0058 sinh -0.001 0.0 -> -0.0010000001666666750208 0.0 +sinh0059 sinh -1.0 0.0 -> -1.1752011936438014569 0.0 +sinh0060 sinh 1.4436354751788103 0.0 -> 1.9999999999999999078 0.0 +sinh0061 sinh -1.4436354751788103 0.0 -> -1.9999999999999999078 0.0 +sinh0062 sinh 17.328679513998633 0.0 -> 16777215.999999992136 0.0 +sinh0063 sinh 18.714973875118524 0.0 -> 67108864.000000036211 0.0 +sinh0064 sinh 709.7827 0.0 -> 8.9883497833190073272e+307 0.0 +sinh0065 sinh -709.7827 0.0 -> -8.9883497833190073272e+307 0.0 + -- special values sinh1000 sinh 0.0 0.0 -> 0.0 0.0 sinh1001 sinh 0.0 inf -> 0.0 nan invalid ignore-real-sign @@ -1897,6 +1969,24 @@ tanh0031 tanh -711 7.4000000000000004 -> -1.0 0.0 tanh0032 tanh 1000 -2.3199999999999998 -> 1.0 0.0 tanh0033 tanh -1.0000000000000001e+300 -9.6699999999999999 -> -1.0 -0.0 +-- Additional real values (mpmath) +tanh0050 tanh 1e-100 0.0 -> 1.00000000000000002e-100 0.0 +tanh0051 tanh 5e-17 0.0 -> 4.9999999999999998955e-17 0.0 +tanh0052 tanh 1e-16 0.0 -> 9.999999999999999791e-17 0.0 +tanh0053 tanh 3.7e-08 0.0 -> 3.6999999999999983559e-8 0.0 +tanh0054 tanh 0.001 0.0 -> 0.00099999966666680002076 0.0 +tanh0055 tanh 0.2 0.0 -> 0.19737532022490401141 0.0 +tanh0056 tanh 1.0 0.0 -> 0.76159415595576488812 0.0 +tanh0057 tanh -3.7e-08 0.0 -> -3.6999999999999983559e-8 0.0 +tanh0058 tanh -0.001 0.0 -> -0.00099999966666680002076 0.0 +tanh0059 tanh -1.0 0.0 -> -0.76159415595576488812 0.0 +tanh0060 tanh 0.5493061443340549 0.0 -> 0.50000000000000003402 0.0 +tanh0061 tanh -0.5493061443340549 0.0 -> -0.50000000000000003402 0.0 +tanh0062 tanh 17.328679513998633 0.0 -> 0.99999999999999822364 0.0 +tanh0063 tanh 18.714973875118524 0.0 -> 0.99999999999999988898 0.0 +tanh0064 tanh 711 0.0 -> 1.0 0.0 +tanh0065 tanh 1.797e+308 0.0 -> 1.0 0.0 + --special values tanh1000 tanh 0.0 0.0 -> 0.0 0.0 tanh1001 tanh 0.0 inf -> nan nan invalid @@ -1985,6 +2075,22 @@ cos0021 cos 4.8048375263775256 0.0062248852898515658 -> 0.092318702015846243 0.0 cos0022 cos 7.9914515433858515 0.71659966615501436 -> -0.17375439906936566 -0.77217043527294582 cos0023 cos 0.45124351152540226 1.6992693993812158 -> 2.543477948972237 -1.1528193694875477 +-- Additional real values (mpmath) +cos0050 cos 1e-150 0.0 -> 1.0 -0.0 +cos0051 cos 1e-18 0.0 -> 1.0 -0.0 +cos0052 cos 1e-09 0.0 -> 0.9999999999999999995 -0.0 +cos0053 cos 0.0003 0.0 -> 0.9999999550000003375 -0.0 +cos0054 cos 0.2 0.0 -> 0.98006657784124162892 -0.0 +cos0055 cos 1.0 0.0 -> 0.5403023058681397174 -0.0 +cos0056 cos -1e-18 0.0 -> 1.0 0.0 +cos0057 cos -0.0003 0.0 -> 0.9999999550000003375 0.0 +cos0058 cos -1.0 0.0 -> 0.5403023058681397174 0.0 +cos0059 cos 1.0471975511965976 0.0 -> 0.50000000000000009945 -0.0 +cos0060 cos 2.5707963267948966 0.0 -> -0.84147098480789647357 -0.0 +cos0061 cos -2.5707963267948966 0.0 -> -0.84147098480789647357 0.0 +cos0062 cos 7.225663103256523 0.0 -> 0.58778525229247407559 -0.0 +cos0063 cos -8.79645943005142 0.0 -> -0.80901699437494722255 0.0 + -- special values cos1000 cos -0.0 0.0 -> 1.0 0.0 cos1001 cos -inf 0.0 -> nan 0.0 invalid ignore-imag-sign @@ -2073,6 +2179,22 @@ sin0021 sin 1.4342727387492671 0.81361889790284347 -> 1.3370960060947923 0.12336 sin0022 sin 1.1518087354403725 4.8597235966150558 -> 58.919141989603041 26.237003403758852 sin0023 sin 0.00087773078406649192 34.792379211312095 -> 565548145569.38245 644329685822700.62 +-- Additional real values (mpmath) +sin0050 sin 1e-100 0.0 -> 1.00000000000000002e-100 0.0 +sin0051 sin 3.7e-08 0.0 -> 3.6999999999999992001e-8 0.0 +sin0052 sin 0.001 0.0 -> 0.00099999983333334168748 0.0 +sin0053 sin 0.2 0.0 -> 0.19866933079506122634 0.0 +sin0054 sin 1.0 0.0 -> 0.84147098480789650665 0.0 +sin0055 sin -3.7e-08 0.0 -> -3.6999999999999992001e-8 0.0 +sin0056 sin -0.001 0.0 -> -0.00099999983333334168748 0.0 +sin0057 sin -1.0 0.0 -> -0.84147098480789650665 0.0 +sin0058 sin 0.5235987755982989 0.0 -> 0.50000000000000004642 0.0 +sin0059 sin -0.5235987755982989 0.0 -> -0.50000000000000004642 0.0 +sin0060 sin 2.6179938779914944 0.0 -> 0.49999999999999996018 -0.0 +sin0061 sin -2.6179938779914944 0.0 -> -0.49999999999999996018 -0.0 +sin0062 sin 7.225663103256523 0.0 -> 0.80901699437494673648 0.0 +sin0063 sin -8.79645943005142 0.0 -> -0.58778525229247340658 -0.0 + -- special values sin1000 sin -0.0 0.0 -> -0.0 0.0 sin1001 sin -inf 0.0 -> nan 0.0 invalid ignore-imag-sign @@ -2161,6 +2283,25 @@ tan0021 tan 1.7809617968443272 1.5052381702853379 -> -0.044066222118946903 1.093 tan0022 tan 1.1615313900880577 1.7956298728647107 -> 0.041793186826390362 1.0375339546034792 tan0023 tan 0.067014779477908945 5.8517361577457097 -> 2.2088639754800034e-06 0.9999836182420061 +-- Additional real values (mpmath) +tan0050 tan 1e-100 0.0 -> 1.00000000000000002e-100 0.0 +tan0051 tan 3.7e-08 0.0 -> 3.7000000000000017328e-8 0.0 +tan0052 tan 0.001 0.0 -> 0.0010000003333334666875 0.0 +tan0053 tan 0.2 0.0 -> 0.20271003550867249488 0.0 +tan0054 tan 1.0 0.0 -> 1.5574077246549022305 0.0 +tan0055 tan -3.7e-08 0.0 -> -3.7000000000000017328e-8 0.0 +tan0056 tan -0.001 0.0 -> -0.0010000003333334666875 0.0 +tan0057 tan -1.0 0.0 -> -1.5574077246549022305 0.0 +tan0058 tan 0.4636476090008061 0.0 -> 0.49999999999999997163 0.0 +tan0059 tan -0.4636476090008061 0.0 -> -0.49999999999999997163 0.0 +tan0060 tan 1.1071487177940904 0.0 -> 1.9999999999999995298 0.0 +tan0061 tan -1.1071487177940904 0.0 -> -1.9999999999999995298 0.0 +tan0062 tan 1.5 0.0 -> 14.101419947171719388 0.0 +tan0063 tan 1.57 0.0 -> 1255.7655915007896475 0.0 +tan0064 tan 1.5707963267948961 0.0 -> 1978937966095219.0538 0.0 +tan0065 tan 7.225663103256523 0.0 -> 1.3763819204711701522 0.0 +tan0066 tan -8.79645943005142 0.0 -> 0.7265425280053614098 0.0 + -- special values tan1000 tan -0.0 0.0 -> -0.0 0.0 tan1001 tan -inf 0.0 -> nan nan invalid diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 48e8007bc7..02d8b47c13 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -29,6 +29,7 @@ test_dir = os.path.dirname(file) or os.curdir math_testcases = os.path.join(test_dir, 'math_testcases.txt') test_file = os.path.join(test_dir, 'cmath_testcases.txt') + def to_ulps(x): """Convert a non-NaN float x to an integer, in such a way that adjacent floats are converted to adjacent integers. Then @@ -36,25 +37,39 @@ def to_ulps(x): floats. The results from this function will only make sense on platforms - where C doubles are represented in IEEE 754 binary64 format. + where native doubles are represented in IEEE 754 binary64 format. + Note: 0.0 and -0.0 are converted to 0 and -1, respectively. """ n = struct.unpack(' eps: - # Use %r instead of %f so the error message - # displays full precision. Otherwise discrepancies - # in the last few bits will lead to very confusing - # error messages - self.fail('%s returned %r, expected %r' % - (name, value, expected)) + def ftest(self, name, got, expected, ulp_tol=5, abs_tol=0.0): + """Compare arguments expected and got, as floats, if either + is a float, using a tolerance expressed in multiples of + ulp(expected) or absolutely, whichever is greater. + + As a convenience, when neither argument is a float, and for + non-finite floats, exact equality is demanded. Also, nan==nan + in this function. + """ + failure = result_check(expected, got, ulp_tol, abs_tol) + if failure is not None: + self.fail("{}: {}".format(name, failure)) def testConstants(self): - self.ftest('pi', math.pi, 3.1415926) - self.ftest('e', math.e, 2.7182818) + # Ref: Abramowitz & Stegun (Dover, 1965) + self.ftest('pi', math.pi, 3.141592653589793238462643) + self.ftest('e', math.e, 2.718281828459045235360287) self.assertEqual(math.tau, 2*math.pi) def testAcos(self): @@ -378,9 +443,9 @@ class MathTests(unittest.TestCase): def testCos(self): self.assertRaises(TypeError, math.cos) - self.ftest('cos(-pi/2)', math.cos(-math.pi/2), 0) + self.ftest('cos(-pi/2)', math.cos(-math.pi/2), 0, abs_tol=ulp(1)) self.ftest('cos(0)', math.cos(0), 1) - self.ftest('cos(pi/2)', math.cos(math.pi/2), 0) + self.ftest('cos(pi/2)', math.cos(math.pi/2), 0, abs_tol=ulp(1)) self.ftest('cos(pi)', math.cos(math.pi), -1) try: self.assertTrue(math.isnan(math.cos(INF))) @@ -970,7 +1035,8 @@ class MathTests(unittest.TestCase): def testTanh(self): self.assertRaises(TypeError, math.tanh) self.ftest('tanh(0)', math.tanh(0), 0) - self.ftest('tanh(1)+tanh(-1)', math.tanh(1)+math.tanh(-1), 0) + self.ftest('tanh(1)+tanh(-1)', math.tanh(1)+math.tanh(-1), 0, + abs_tol=ulp(1)) self.ftest('tanh(inf)', math.tanh(INF), 1) self.ftest('tanh(-inf)', math.tanh(NINF), -1) self.assertTrue(math.isnan(math.tanh(NAN))) @@ -1084,30 +1150,48 @@ class MathTests(unittest.TestCase): @requires_IEEE_754 def test_testfile(self): + fail_fmt = "{}: {}({!r}): {}" + + failures = [] for id, fn, ar, ai, er, ei, flags in parse_testfile(test_file): - # Skip if either the input or result is complex, or if - # flags is nonempty - if ai != 0. or ei != 0. or flags: + # Skip if either the input or result is complex + if ai != 0.0 or ei != 0.0: continue if fn in ['rect', 'polar']: # no real versions of rect, polar continue + func = getattr(math, fn) + + if 'invalid' in flags or 'divide-by-zero' in flags: + er = 'ValueError' + elif 'overflow' in flags: + er = 'OverflowError' + try: result = func(ar) - except ValueError as exc: - message = (("Unexpected ValueError: %s\n " + - "in test %s:%s(%r)\n") % (exc.args[0], id, fn, ar)) - self.fail(message) + except ValueError: + result = 'ValueError' except OverflowError: - message = ("Unexpected OverflowError in " + - "test %s:%s(%r)\n" % (id, fn, ar)) - self.fail(message) - self.ftest("%s:%s(%r)" % (id, fn, ar), result, er) + result = 'OverflowError' + + # Default tolerances + ulp_tol, abs_tol = 5, 0.0 + + failure = result_check(er, result, ulp_tol, abs_tol) + if failure is None: + continue + + msg = fail_fmt.format(id, fn, ar, failure) + failures.append(msg) + + if failures: + self.fail('Failures in test_testfile:\n ' + + '\n '.join(failures)) @requires_IEEE_754 def test_mtestfile(self): - fail_fmt = "{}:{}({!r}): expected {!r}, got {!r}" + fail_fmt = "{}: {}({!r}): {}" failures = [] for id, fn, arg, expected, flags in parse_mtestfile(math_testcases): @@ -1125,41 +1209,48 @@ class MathTests(unittest.TestCase): except OverflowError: got = 'OverflowError' - accuracy_failure = None - if isinstance(got, float) and isinstance(expected, float): - if math.isnan(expected) and math.isnan(got): - continue - if not math.isnan(expected) and not math.isnan(got): - if fn == 'lgamma': - # we use a weaker accuracy test for lgamma; - # lgamma only achieves an absolute error of - # a few multiples of the machine accuracy, in - # general. - accuracy_failure = acc_check(expected, got, - rel_err = 5e-15, - abs_err = 5e-15) - elif fn == 'erfc': - # erfc has less-than-ideal accuracy for large - # arguments (x ~ 25 or so), mainly due to the - # error involved in computing exp(-x*x). - # - # XXX Would be better to weaken this test only - # for large x, instead of for all x. - accuracy_failure = ulps_check(expected, got, 2000) - - else: - accuracy_failure = ulps_check(expected, got, 20) - if accuracy_failure is None: - continue - - if isinstance(got, str) and isinstance(expected, str): - if got == expected: - continue - - fail_msg = fail_fmt.format(id, fn, arg, expected, got) - if accuracy_failure is not None: - fail_msg += ' ({})'.format(accuracy_failure) - failures.append(fail_msg) + # Default tolerances + ulp_tol, abs_tol = 5, 0.0 + + # Exceptions to the defaults + if fn == 'gamma': + # Experimental results on one platform gave + # an accuracy of <= 10 ulps across the entire float + # domain. We weaken that to require 20 ulp accuracy. + ulp_tol = 20 + + elif fn == 'lgamma': + # we use a weaker accuracy test for lgamma; + # lgamma only achieves an absolute error of + # a few multiples of the machine accuracy, in + # general. + abs_tol = 1e-15 + + elif fn == 'erfc' and arg >= 0.0: + # erfc has less-than-ideal accuracy for large + # arguments (x ~ 25 or so), mainly due to the + # error involved in computing exp(-x*x). + # + # Observed between CPython and mpmath at 25 dp: + # x < 0 : err <= 2 ulp + # 0 <= x < 1 : err <= 10 ulp + # 1 <= x < 10 : err <= 100 ulp + # 10 <= x < 20 : err <= 300 ulp + # 20 <= x : < 600 ulp + # + if arg < 1.0: + ulp_tol = 10 + elif arg < 10.0: + ulp_tol = 100 + else: + ulp_tol = 1000 + + failure = result_check(expected, got, ulp_tol, abs_tol) + if failure is None: + continue + + msg = fail_fmt.format(id, fn, arg, failure) + failures.append(msg) if failures: self.fail('Failures in test_mtestfile:\n ' + -- cgit v1.2.1 From c7fe9e5f9209e6dc8c01abecad0046db26e4bbe5 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sun, 4 Sep 2016 09:58:51 +0100 Subject: Issue #27953: skip failing math and cmath tests for tan on OS X 10.4. --- Lib/test/test_cmath.py | 20 ++++++++++++++++++++ Lib/test/test_math.py | 19 ++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_cmath.py b/Lib/test/test_cmath.py index 11b0c61202..0451fb0aa2 100644 --- a/Lib/test/test_cmath.py +++ b/Lib/test/test_cmath.py @@ -4,6 +4,8 @@ import test.test_math as test_math import unittest import cmath, math from cmath import phase, polar, rect, pi +import platform +import sys import sysconfig INF = float('inf') @@ -332,6 +334,18 @@ class CMathTests(unittest.TestCase): @requires_IEEE_754 def test_specific_values(self): + # Some tests need to be skipped on ancient OS X versions. + # See issue #27953. + SKIP_ON_TIGER = {'tan0064'} + + osx_version = None + if sys.platform == 'darwin': + version_txt = platform.mac_ver()[0] + try: + osx_version = tuple(map(int, version_txt.split('.'))) + except ValueError: + pass + def rect_complex(z): """Wrapped version of rect that accepts a complex number instead of two float arguments.""" @@ -345,6 +359,12 @@ class CMathTests(unittest.TestCase): for id, fn, ar, ai, er, ei, flags in parse_testfile(test_file): arg = complex(ar, ai) expected = complex(er, ei) + + # Skip certain tests on OS X 10.4. + if osx_version is not None and osx_version < (10, 5): + if id in SKIP_ON_TIGER: + continue + if fn == 'rect': function = rect_complex elif fn == 'polar': diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 02d8b47c13..93b77b771e 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -6,8 +6,9 @@ from test import support import unittest import math import os -import sys +import platform import struct +import sys import sysconfig eps = 1E-05 @@ -1150,6 +1151,18 @@ class MathTests(unittest.TestCase): @requires_IEEE_754 def test_testfile(self): + # Some tests need to be skipped on ancient OS X versions. + # See issue #27953. + SKIP_ON_TIGER = {'tan0064'} + + osx_version = None + if sys.platform == 'darwin': + version_txt = platform.mac_ver()[0] + try: + osx_version = tuple(map(int, version_txt.split('.'))) + except ValueError: + pass + fail_fmt = "{}: {}({!r}): {}" failures = [] @@ -1160,6 +1173,10 @@ class MathTests(unittest.TestCase): if fn in ['rect', 'polar']: # no real versions of rect, polar continue + # Skip certain tests on OS X 10.4. + if osx_version is not None and osx_version < (10, 5): + if id in SKIP_ON_TIGER: + continue func = getattr(math, fn) -- cgit v1.2.1 From 450839785558339bc38c4228a33e0778db6d00ba Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sun, 4 Sep 2016 12:29:14 +0100 Subject: Issue #27427: Additional tests for the math module. Thanks Francisco Couzo. --- Lib/test/test_math.py | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 93b77b771e..eaa41bca3f 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -15,6 +15,7 @@ eps = 1E-05 NAN = float('nan') INF = float('inf') NINF = float('-inf') +FLOAT_MAX = sys.float_info.max # detect evidence of double-rounding: fsum is not always correctly # rounded on machines that suffer from double rounding. @@ -271,6 +272,8 @@ class MathTests(unittest.TestCase): self.ftest('acos(1)', math.acos(1), 0) self.assertRaises(ValueError, math.acos, INF) self.assertRaises(ValueError, math.acos, NINF) + self.assertRaises(ValueError, math.acos, 1 + eps) + self.assertRaises(ValueError, math.acos, -1 - eps) self.assertTrue(math.isnan(math.acos(NAN))) def testAcosh(self): @@ -290,6 +293,8 @@ class MathTests(unittest.TestCase): self.ftest('asin(1)', math.asin(1), math.pi/2) self.assertRaises(ValueError, math.asin, INF) self.assertRaises(ValueError, math.asin, NINF) + self.assertRaises(ValueError, math.asin, 1 + eps) + self.assertRaises(ValueError, math.asin, -1 - eps) self.assertTrue(math.isnan(math.asin(NAN))) def testAsinh(self): @@ -469,6 +474,7 @@ class MathTests(unittest.TestCase): self.ftest('degrees(pi)', math.degrees(math.pi), 180.0) self.ftest('degrees(pi/2)', math.degrees(math.pi/2), 90.0) self.ftest('degrees(-pi/4)', math.degrees(-math.pi/4), -45.0) + self.ftest('degrees(0)', math.degrees(0), 0) def testExp(self): self.assertRaises(TypeError, math.exp) @@ -478,6 +484,7 @@ class MathTests(unittest.TestCase): self.assertEqual(math.exp(INF), INF) self.assertEqual(math.exp(NINF), 0.) self.assertTrue(math.isnan(math.exp(NAN))) + self.assertRaises(OverflowError, math.exp, 1000000) def testFabs(self): self.assertRaises(TypeError, math.fabs) @@ -720,6 +727,7 @@ class MathTests(unittest.TestCase): self.assertEqual(math.hypot(INF, NAN), INF) self.assertEqual(math.hypot(NAN, NINF), INF) self.assertEqual(math.hypot(NINF, NAN), INF) + self.assertRaises(OverflowError, math.hypot, FLOAT_MAX, FLOAT_MAX) self.assertTrue(math.isnan(math.hypot(1.0, NAN))) self.assertTrue(math.isnan(math.hypot(NAN, -2.0))) @@ -773,8 +781,10 @@ class MathTests(unittest.TestCase): def testLog1p(self): self.assertRaises(TypeError, math.log1p) - n= 2**90 - self.assertAlmostEqual(math.log1p(n), math.log1p(float(n))) + for n in [2, 2**90, 2**300]: + self.assertAlmostEqual(math.log1p(n), math.log1p(float(n))) + self.assertRaises(ValueError, math.log1p, -1) + self.assertEqual(math.log1p(INF), INF) @requires_IEEE_754 def testLog2(self): @@ -988,6 +998,7 @@ class MathTests(unittest.TestCase): self.ftest('radians(180)', math.radians(180), math.pi) self.ftest('radians(90)', math.radians(90), math.pi/2) self.ftest('radians(-45)', math.radians(-45), -math.pi/4) + self.ftest('radians(0)', math.radians(0), 0) def testSin(self): self.assertRaises(TypeError, math.sin) @@ -1017,6 +1028,7 @@ class MathTests(unittest.TestCase): self.ftest('sqrt(1)', math.sqrt(1), 1) self.ftest('sqrt(4)', math.sqrt(4), 2) self.assertEqual(math.sqrt(INF), INF) + self.assertRaises(ValueError, math.sqrt, -1) self.assertRaises(ValueError, math.sqrt, NINF) self.assertTrue(math.isnan(math.sqrt(NAN))) @@ -1087,7 +1099,8 @@ class MathTests(unittest.TestCase): def testIsnan(self): self.assertTrue(math.isnan(float("nan"))) - self.assertTrue(math.isnan(float("inf")* 0.)) + self.assertTrue(math.isnan(float("-nan"))) + self.assertTrue(math.isnan(float("inf") * 0.)) self.assertFalse(math.isnan(float("inf"))) self.assertFalse(math.isnan(0.)) self.assertFalse(math.isnan(1.)) @@ -1380,7 +1393,8 @@ class IsCloseTests(unittest.TestCase): decimal_examples = [(Decimal('1.00000001'), Decimal('1.0')), (Decimal('1.00000001e-20'), Decimal('1.0e-20')), - (Decimal('1.00000001e-100'), Decimal('1.0e-100'))] + (Decimal('1.00000001e-100'), Decimal('1.0e-100')), + (Decimal('1.00000001e20'), Decimal('1.0e20'))] self.assertAllClose(decimal_examples, rel_tol=1e-8) self.assertAllNotClose(decimal_examples, rel_tol=1e-9) @@ -1388,8 +1402,10 @@ class IsCloseTests(unittest.TestCase): # test with Fraction values from fractions import Fraction - # could use some more examples here! - fraction_examples = [(Fraction(1, 100000000) + 1, Fraction(1))] + fraction_examples = [ + (Fraction(1, 100000000) + 1, Fraction(1)), + (Fraction(100000001), Fraction(100000000)), + (Fraction(10**8 + 1, 10**28), Fraction(1, 10**20))] self.assertAllClose(fraction_examples, rel_tol=1e-8) self.assertAllNotClose(fraction_examples, rel_tol=1e-9) -- cgit v1.2.1 From 245f129595cb3a48da8fb0254fcd6811c346e588 Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Sun, 4 Sep 2016 11:39:01 -0700 Subject: issue23591: more docs; slight change to repr --- Lib/enum.py | 13 +++++-------- Lib/test/test_enum.py | 12 ++++++------ 2 files changed, 11 insertions(+), 14 deletions(-) (limited to 'Lib') diff --git a/Lib/enum.py b/Lib/enum.py index 1e028a364f..6a1899941f 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -692,14 +692,11 @@ class Flag(Enum): if self._name_ is not None: return '<%s.%s: %r>' % (cls.__name__, self._name_, self._value_) members = self._decompose_() - if len(members) == 1 and members[0]._name_ is None: - return '<%s: %r>' % (cls.__name__, members[0]._value_) - else: - return '<%s.%s: %r>' % ( - cls.__name__, - '|'.join([str(m._name_ or m._value_) for m in members]), - self._value_, - ) + return '<%s.%s: %r>' % ( + cls.__name__, + '|'.join([str(m._name_ or m._value_) for m in members]), + self._value_, + ) def __str__(self): cls = self.__class__ diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index cf704edb1b..698fd307a0 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -1688,12 +1688,12 @@ class TestFlag(unittest.TestCase): self.assertEqual(repr(Perm.X), '') self.assertEqual(repr(Perm.R | Perm.W), '') self.assertEqual(repr(Perm.R | Perm.W | Perm.X), '') - self.assertEqual(repr(Perm(0)), '') + self.assertEqual(repr(Perm(0)), '') self.assertEqual(repr(~Perm.R), '') self.assertEqual(repr(~Perm.W), '') self.assertEqual(repr(~Perm.X), '') self.assertEqual(repr(~(Perm.R | Perm.W)), '') - self.assertEqual(repr(~(Perm.R | Perm.W | Perm.X)), '') + self.assertEqual(repr(~(Perm.R | Perm.W | Perm.X)), '') self.assertEqual(repr(Perm(~0)), '') Open = self.Open @@ -1933,13 +1933,13 @@ class TestIntFlag(unittest.TestCase): self.assertEqual(repr(Perm.R | Perm.W), '') self.assertEqual(repr(Perm.R | Perm.W | Perm.X), '') self.assertEqual(repr(Perm.R | 8), '') - self.assertEqual(repr(Perm(0)), '') - self.assertEqual(repr(Perm(8)), '') + self.assertEqual(repr(Perm(0)), '') + self.assertEqual(repr(Perm(8)), '') self.assertEqual(repr(~Perm.R), '') self.assertEqual(repr(~Perm.W), '') self.assertEqual(repr(~Perm.X), '') self.assertEqual(repr(~(Perm.R | Perm.W)), '') - self.assertEqual(repr(~(Perm.R | Perm.W | Perm.X)), '') + self.assertEqual(repr(~(Perm.R | Perm.W | Perm.X)), '') self.assertEqual(repr(~(Perm.R | 8)), '') self.assertEqual(repr(Perm(~0)), '') self.assertEqual(repr(Perm(~8)), '') @@ -1950,7 +1950,7 @@ class TestIntFlag(unittest.TestCase): self.assertEqual(repr(Open.AC), '') self.assertEqual(repr(Open.RO | Open.CE), '') self.assertEqual(repr(Open.WO | Open.CE), '') - self.assertEqual(repr(Open(4)), '') + self.assertEqual(repr(Open(4)), '') self.assertEqual(repr(~Open.RO), '') self.assertEqual(repr(~Open.WO), '') self.assertEqual(repr(~Open.AC), '') -- cgit v1.2.1 From 7b9e1c65cbe1ed320c5f207db8c14f796d82b026 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Mon, 5 Sep 2016 11:13:07 -0700 Subject: fix skipping #27921 for windows --- Lib/test/test_tools/test_unparse.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_tools/test_unparse.py b/Lib/test/test_tools/test_unparse.py index 1865fc8e44..517f2613aa 100644 --- a/Lib/test/test_tools/test_unparse.py +++ b/Lib/test/test_tools/test_unparse.py @@ -288,8 +288,7 @@ class DirectoryTestCase(ASTTestCase): # it's very much a hack that I'm skipping these files, but # I can't figure out why they fail. I'll fix it when I # address issue #27948. - if (filename.endswith('/test_fstring.py') or - filename.endswith('/test_traceback.py')): + if os.path.basename(filename) in ('test_fstring.py', 'test_traceback.py'): if test.support.verbose: print(f'Skipping {filename}: see issue 27921') continue -- cgit v1.2.1 From d4c6b44f0689ed5f7f98eed08bcaeb3a8cb164bc Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Mon, 5 Sep 2016 16:31:21 -0500 Subject: Closes #11620: Fix support for SND_MEMORY in winsound.PlaySound. Based on a patch by Tim Lesher. --- Lib/test/test_winsound.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_winsound.py b/Lib/test/test_winsound.py index 4a8ab7de58..1cfef779d6 100644 --- a/Lib/test/test_winsound.py +++ b/Lib/test/test_winsound.py @@ -87,6 +87,22 @@ class PlaySoundTest(unittest.TestCase): winsound.PlaySound, "none", winsound.SND_ASYNC | winsound.SND_MEMORY ) + self.assertRaises(TypeError, winsound.PlaySound, b"bad", 0) + self.assertRaises(TypeError, winsound.PlaySound, "bad", + winsound.SND_MEMORY) + self.assertRaises(TypeError, winsound.PlaySound, 1, 0) + + def test_snd_memory(self): + with open(support.findfile('pluck-pcm8.wav', + subdir='audiodata'), 'rb') as f: + audio_data = f.read() + safe_PlaySound(audio_data, winsound.SND_MEMORY) + audio_data = bytearray(audio_data) + safe_PlaySound(audio_data, winsound.SND_MEMORY) + + def test_snd_filename(self): + fn = support.findfile('pluck-pcm8.wav', subdir='audiodata') + safe_PlaySound(fn, winsound.SND_FILENAME | winsound.SND_NODEFAULT) def test_aliases(self): aliases = [ -- cgit v1.2.1 From 823616bdc22856b5df918a443051ec6ec2e9073c Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 5 Sep 2016 14:50:11 -0700 Subject: Issue #24254: Preserve class attribute definition order. --- Lib/test/test_builtin.py | 192 ++++++++++++++++++++++++++++++++++++++++++++- Lib/test/test_metaclass.py | 11 ++- Lib/test/test_pydoc.py | 8 +- Lib/test/test_sys.py | 2 +- Lib/test/test_types.py | 22 ++++++ Lib/types.py | 5 +- Lib/typing.py | 1 + 7 files changed, 234 insertions(+), 7 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 7741a7911d..486f445069 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -16,8 +16,10 @@ import traceback import types import unittest import warnings +from collections import OrderedDict from operator import neg -from test.support import TESTFN, unlink, run_unittest, check_warnings +from test.support import (TESTFN, unlink, run_unittest, check_warnings, + cpython_only) from test.support.script_helper import assert_python_ok try: import pty, signal @@ -1778,6 +1780,194 @@ class TestType(unittest.TestCase): A.__doc__ = doc self.assertEqual(A.__doc__, doc) + def test_type_definition_order_nonempty(self): + class Spam: + b = 1 + c = 3 + a = 2 + d = 4 + eggs = 2 + e = 5 + b = 42 + + self.assertEqual(Spam.__definition_order__, + ('__module__', '__qualname__', + 'b', 'c', 'a', 'd', 'eggs', 'e')) + + def test_type_definition_order_empty(self): + class Empty: + pass + + self.assertEqual(Empty.__definition_order__, + ('__module__', '__qualname__')) + + def test_type_definition_order_on_instance(self): + class Spam: + a = 2 + b = 1 + c = 3 + with self.assertRaises(AttributeError): + Spam().__definition_order__ + + def test_type_definition_order_set_to_None(self): + class Spam: + a = 2 + b = 1 + c = 3 + Spam.__definition_order__ = None + self.assertEqual(Spam.__definition_order__, None) + + def test_type_definition_order_set_to_tuple(self): + class Spam: + a = 2 + b = 1 + c = 3 + Spam.__definition_order__ = ('x', 'y', 'z') + self.assertEqual(Spam.__definition_order__, ('x', 'y', 'z')) + + def test_type_definition_order_deleted(self): + class Spam: + a = 2 + b = 1 + c = 3 + del Spam.__definition_order__ + self.assertEqual(Spam.__definition_order__, None) + + def test_type_definition_order_set_to_bad_type(self): + class Spam: + a = 2 + b = 1 + c = 3 + Spam.__definition_order__ = 42 + self.assertEqual(Spam.__definition_order__, 42) + + def test_type_definition_order_builtins(self): + self.assertEqual(object.__definition_order__, None) + self.assertEqual(type.__definition_order__, None) + self.assertEqual(dict.__definition_order__, None) + self.assertEqual(type(None).__definition_order__, None) + + def test_type_definition_order_dunder_names_included(self): + class Dunder: + VAR = 3 + def __init__(self): + pass + + self.assertEqual(Dunder.__definition_order__, + ('__module__', '__qualname__', + 'VAR', '__init__')) + + def test_type_definition_order_only_dunder_names(self): + class DunderOnly: + __xyz__ = None + def __init__(self): + pass + + self.assertEqual(DunderOnly.__definition_order__, + ('__module__', '__qualname__', + '__xyz__', '__init__')) + + def test_type_definition_order_underscore_names(self): + class HalfDunder: + __whether_to_be = True + or_not_to_be__ = False + + self.assertEqual(HalfDunder.__definition_order__, + ('__module__', '__qualname__', + '_HalfDunder__whether_to_be', 'or_not_to_be__')) + + def test_type_definition_order_with_slots(self): + class Slots: + __slots__ = ('x', 'y') + a = 1 + b = 2 + + self.assertEqual(Slots.__definition_order__, + ('__module__', '__qualname__', + '__slots__', 'a', 'b')) + + def test_type_definition_order_overwritten_None(self): + class OverwrittenNone: + __definition_order__ = None + a = 1 + b = 2 + c = 3 + + self.assertEqual(OverwrittenNone.__definition_order__, None) + + def test_type_definition_order_overwritten_tuple(self): + class OverwrittenTuple: + __definition_order__ = ('x', 'y', 'z') + a = 1 + b = 2 + c = 3 + + self.assertEqual(OverwrittenTuple.__definition_order__, + ('x', 'y', 'z')) + + def test_type_definition_order_overwritten_bad_item(self): + with self.assertRaises(TypeError): + class PoorlyOverwritten: + __definition_order__ = ('a', 2, 'c') + a = 1 + b = 2 + c = 3 + + def test_type_definition_order_overwritten_bad_type(self): + with self.assertRaises(TypeError): + class PoorlyOverwritten: + __definition_order__ = ['a', 2, 'c'] + a = 1 + b = 2 + c = 3 + + def test_type_definition_order_metaclass(self): + class Meta(type): + SPAM = 42 + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.assertEqual(Meta.__definition_order__, + ('__module__', '__qualname__', + 'SPAM', '__init__')) + + def test_type_definition_order_OrderedDict(self): + class Meta(type): + def __prepare__(self, *args, **kwargs): + return OrderedDict() + + class WithODict(metaclass=Meta): + x='y' + + self.assertEqual(WithODict.__definition_order__, + ('__module__', '__qualname__', 'x')) + + class Meta(type): + def __prepare__(self, *args, **kwargs): + class ODictSub(OrderedDict): + pass + return ODictSub() + + class WithODictSub(metaclass=Meta): + x='y' + + self.assertEqual(WithODictSub.__definition_order__, + ('__module__', '__qualname__', 'x')) + + @cpython_only + def test_type_definition_order_cpython(self): + # some implementations will have an ordered-by-default dict. + + class Meta(type): + def __prepare__(self, *args, **kwargs): + return {} + + class NotOrdered(metaclass=Meta): + x='y' + + self.assertEqual(NotOrdered.__definition_order__, None) + def test_bad_args(self): with self.assertRaises(TypeError): type() diff --git a/Lib/test/test_metaclass.py b/Lib/test/test_metaclass.py index e6fe20a107..4db792ecef 100644 --- a/Lib/test/test_metaclass.py +++ b/Lib/test/test_metaclass.py @@ -180,7 +180,7 @@ Use a metaclass that doesn't derive from type. meta: C () ns: [('__module__', 'test.test_metaclass'), ('__qualname__', 'C'), ('a', 42), ('b', 24)] kw: [] - >>> type(C) is dict + >>> type(C) is types._DefaultClassNamespaceType True >>> print(sorted(C.items())) [('__module__', 'test.test_metaclass'), ('__qualname__', 'C'), ('a', 42), ('b', 24)] @@ -211,8 +211,11 @@ And again, with a __prepare__ attribute. The default metaclass must define a __prepare__() method. - >>> type.__prepare__() - {} + >>> ns = type.__prepare__() + >>> type(ns) is types._DefaultClassNamespaceType + True + >>> list(ns) == [] + True >>> Make sure it works with subclassing. @@ -248,7 +251,9 @@ Test failures in looking up the __prepare__ method work. """ +from collections import OrderedDict import sys +import types # Trace function introduces __locals__ which causes various tests to fail. if hasattr(sys, 'gettrace') and sys.gettrace(): diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py index 4998597e21..527234bc6e 100644 --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -427,6 +427,7 @@ class PydocDocTest(unittest.TestCase): expected_html = expected_html_pattern % ( (mod_url, mod_file, doc_loc) + expected_html_data_docstrings) + self.maxDiff = None self.assertEqual(result, expected_html) @unittest.skipIf(sys.flags.optimize >= 2, @@ -473,13 +474,18 @@ class PydocDocTest(unittest.TestCase): def test_non_str_name(self): # issue14638 # Treat illegal (non-str) name like no name + # Definition order is set to None so it looks the same in both + # cases. class A: + __definition_order__ = None __name__ = 42 class B: pass adoc = pydoc.render_doc(A()) bdoc = pydoc.render_doc(B()) - self.assertEqual(adoc.replace("A", "B"), bdoc) + self.maxDiff = None + expected = adoc.replace("A", "B") + self.assertEqual(bdoc, expected) def test_not_here(self): missing_module = "test.i_am_not_here" diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 4435d6995b..3131f367cc 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1084,7 +1084,7 @@ class SizeofTest(unittest.TestCase): check((1,2,3), vsize('') + 3*self.P) # type # static type: PyTypeObject - fmt = 'P2n15Pl4Pn9Pn11PIP' + fmt = 'P2n15Pl4Pn9Pn11PIPP' if hasattr(sys, 'getcounts'): fmt += '3n2P' s = vsize(fmt) diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index a202196bd2..e5e110f9c2 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -825,6 +825,28 @@ class ClassCreationTests(unittest.TestCase): self.assertEqual(C.y, 1) self.assertEqual(C.z, 2) + def test_new_class_deforder(self): + C = types.new_class("C") + self.assertEqual(C.__definition_order__, tuple()) + + Meta = self.Meta + def func(ns): + ns["x"] = 0 + D = types.new_class("D", (), {"metaclass": Meta, "z": 2}, func) + self.assertEqual(D.__definition_order__, ('y', 'z', 'x')) + + def func(ns): + ns["__definition_order__"] = None + ns["x"] = 0 + D = types.new_class("D", (), {"metaclass": Meta, "z": 2}, func) + self.assertEqual(D.__definition_order__, None) + + def func(ns): + ns["__definition_order__"] = ('a', 'b', 'c') + ns["x"] = 0 + D = types.new_class("D", (), {"metaclass": Meta, "z": 2}, func) + self.assertEqual(D.__definition_order__, ('a', 'b', 'c')) + # Many of the following tests are derived from test_descr.py def test_prepare_class(self): # Basic test of metaclass derivation diff --git a/Lib/types.py b/Lib/types.py index 48891cd3f6..cc093cb403 100644 --- a/Lib/types.py +++ b/Lib/types.py @@ -25,8 +25,11 @@ CoroutineType = type(_c) _c.close() # Prevent ResourceWarning class _C: + _nsType = type(locals()) def _m(self): pass MethodType = type(_C()._m) +# In CPython, this should end up as OrderedDict. +_DefaultClassNamespaceType = _C._nsType BuiltinFunctionType = type(len) BuiltinMethodType = type([].append) # Same as BuiltinFunctionType @@ -85,7 +88,7 @@ def prepare_class(name, bases=(), kwds=None): if hasattr(meta, '__prepare__'): ns = meta.__prepare__(name, bases, **kwds) else: - ns = {} + ns = _DefaultClassNamespaceType() return meta, ns, kwds def _calculate_meta(meta, bases): diff --git a/Lib/typing.py b/Lib/typing.py index 5573a1fbf9..5f451b0ca1 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1301,6 +1301,7 @@ class _ProtocolMeta(GenericMeta): if (not attr.startswith('_abc_') and attr != '__abstractmethods__' and attr != '_is_protocol' and + attr != '__definition_order__' and attr != '__dict__' and attr != '__args__' and attr != '__slots__' and -- cgit v1.2.1 From c4a3f724702fba76349034c07e43915d221fcf4d Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Mon, 5 Sep 2016 23:54:41 +0200 Subject: Issue #27744: Add AF_ALG (Linux Kernel crypto) to socket module. --- Lib/test/test_socket.py | 165 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 7fce13e175..6fc6e277c0 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -5325,6 +5325,170 @@ class SendfileUsingSendfileTest(SendfileUsingSendTest): def meth_from_sock(self, sock): return getattr(sock, "_sendfile_use_sendfile") +@unittest.skipUnless(hasattr(socket, "AF_ALG"), 'AF_ALG required') +class LinuxKernelCryptoAPI(unittest.TestCase): + # tests for AF_ALG + def create_alg(self, typ, name): + sock = socket.socket(socket.AF_ALG, socket.SOCK_SEQPACKET, 0) + sock.bind((typ, name)) + return sock + + def test_sha256(self): + expected = bytes.fromhex("ba7816bf8f01cfea414140de5dae2223b00361a396" + "177a9cb410ff61f20015ad") + with self.create_alg('hash', 'sha256') as algo: + op, _ = algo.accept() + with op: + op.sendall(b"abc") + self.assertEqual(op.recv(512), expected) + + op, _ = algo.accept() + with op: + op.send(b'a', socket.MSG_MORE) + op.send(b'b', socket.MSG_MORE) + op.send(b'c', socket.MSG_MORE) + op.send(b'') + self.assertEqual(op.recv(512), expected) + + def test_hmac_sha1(self): + expected = bytes.fromhex("effcdf6ae5eb2fa2d27416d5f184df9c259a7c79") + with self.create_alg('hash', 'hmac(sha1)') as algo: + algo.setsockopt(socket.SOL_ALG, socket.ALG_SET_KEY, b"Jefe") + op, _ = algo.accept() + with op: + op.sendall(b"what do ya want for nothing?") + self.assertEqual(op.recv(512), expected) + + def test_aes_cbc(self): + key = bytes.fromhex('06a9214036b8a15b512e03d534120006') + iv = bytes.fromhex('3dafba429d9eb430b422da802c9fac41') + msg = b"Single block msg" + ciphertext = bytes.fromhex('e353779c1079aeb82708942dbe77181a') + msglen = len(msg) + with self.create_alg('skcipher', 'cbc(aes)') as algo: + algo.setsockopt(socket.SOL_ALG, socket.ALG_SET_KEY, key) + op, _ = algo.accept() + with op: + op.sendmsg_afalg(op=socket.ALG_OP_ENCRYPT, iv=iv, + flags=socket.MSG_MORE) + op.sendall(msg) + self.assertEqual(op.recv(msglen), ciphertext) + + op, _ = algo.accept() + with op: + op.sendmsg_afalg([ciphertext], + op=socket.ALG_OP_DECRYPT, iv=iv) + self.assertEqual(op.recv(msglen), msg) + + # long message + multiplier = 1024 + longmsg = [msg] * multiplier + op, _ = algo.accept() + with op: + op.sendmsg_afalg(longmsg, + op=socket.ALG_OP_ENCRYPT, iv=iv) + enc = op.recv(msglen * multiplier) + self.assertEqual(len(enc), msglen * multiplier) + self.assertTrue(enc[:msglen], ciphertext) + + op, _ = algo.accept() + with op: + op.sendmsg_afalg([enc], + op=socket.ALG_OP_DECRYPT, iv=iv) + dec = op.recv(msglen * multiplier) + self.assertEqual(len(dec), msglen * multiplier) + self.assertEqual(dec, msg * multiplier) + + @support.requires_linux_version(3, 19) + def test_aead_aes_gcm(self): + key = bytes.fromhex('c939cc13397c1d37de6ae0e1cb7c423c') + iv = bytes.fromhex('b3d8cc017cbb89b39e0f67e2') + plain = bytes.fromhex('c3b3c41f113a31b73d9a5cd432103069') + assoc = bytes.fromhex('24825602bd12a984e0092d3e448eda5f') + expected_ct = bytes.fromhex('93fe7d9e9bfd10348a5606e5cafa7354') + expected_tag = bytes.fromhex('0032a1dc85f1c9786925a2e71d8272dd') + + taglen = len(expected_tag) + assoclen = len(assoc) + + with self.create_alg('aead', 'gcm(aes)') as algo: + algo.setsockopt(socket.SOL_ALG, socket.ALG_SET_KEY, key) + algo.setsockopt(socket.SOL_ALG, socket.ALG_SET_AEAD_AUTHSIZE, + None, taglen) + + # send assoc, plain and tag buffer in separate steps + op, _ = algo.accept() + with op: + op.sendmsg_afalg(op=socket.ALG_OP_ENCRYPT, iv=iv, + assoclen=assoclen, flags=socket.MSG_MORE) + op.sendall(assoc, socket.MSG_MORE) + op.sendall(plain, socket.MSG_MORE) + op.sendall(b'\x00' * taglen) + res = op.recv(assoclen + len(plain) + taglen) + self.assertEqual(expected_ct, res[assoclen:-taglen]) + self.assertEqual(expected_tag, res[-taglen:]) + + # now with msg + op, _ = algo.accept() + with op: + msg = assoc + plain + b'\x00' * taglen + op.sendmsg_afalg([msg], op=socket.ALG_OP_ENCRYPT, iv=iv, + assoclen=assoclen) + res = op.recv(assoclen + len(plain) + taglen) + self.assertEqual(expected_ct, res[assoclen:-taglen]) + self.assertEqual(expected_tag, res[-taglen:]) + + # create anc data manually + pack_uint32 = struct.Struct('I').pack + op, _ = algo.accept() + with op: + msg = assoc + plain + b'\x00' * taglen + op.sendmsg( + [msg], + ([socket.SOL_ALG, socket.ALG_SET_OP, pack_uint32(socket.ALG_OP_ENCRYPT)], + [socket.SOL_ALG, socket.ALG_SET_IV, pack_uint32(len(iv)) + iv], + [socket.SOL_ALG, socket.ALG_SET_AEAD_ASSOCLEN, pack_uint32(assoclen)], + ) + ) + res = op.recv(len(msg)) + self.assertEqual(expected_ct, res[assoclen:-taglen]) + self.assertEqual(expected_tag, res[-taglen:]) + + # decrypt and verify + op, _ = algo.accept() + with op: + msg = assoc + expected_ct + expected_tag + op.sendmsg_afalg([msg], op=socket.ALG_OP_DECRYPT, iv=iv, + assoclen=assoclen) + res = op.recv(len(msg)) + self.assertEqual(plain, res[assoclen:-taglen]) + + def test_drbg_pr_sha256(self): + # deterministic random bit generator, prediction resistance, sha256 + with self.create_alg('rng', 'drbg_pr_sha256') as algo: + extra_seed = os.urandom(32) + algo.setsockopt(socket.SOL_ALG, socket.ALG_SET_KEY, extra_seed) + op, _ = algo.accept() + with op: + rn = op.recv(32) + self.assertEqual(len(rn), 32) + + def test_sendmsg_afalg_args(self): + sock = socket.socket(socket.AF_ALG, socket.SOCK_SEQPACKET, 0) + with self.assertRaises(TypeError): + sock.sendmsg_afalg() + + with self.assertRaises(TypeError): + sock.sendmsg_afalg(op=None) + + with self.assertRaises(TypeError): + sock.sendmsg_afalg(1) + + with self.assertRaises(TypeError): + sock.sendmsg_afalg(op=socket.ALG_OP_ENCRYPT, assoclen=None) + + with self.assertRaises(TypeError): + sock.sendmsg_afalg(op=socket.ALG_OP_ENCRYPT, assoclen=-1) def test_main(): tests = [GeneralModuleTests, BasicTCPTest, TCPCloserTest, TCPTimeoutTest, @@ -5352,6 +5516,7 @@ def test_main(): tests.extend([TIPCTest, TIPCThreadableTest]) tests.extend([BasicCANTest, CANTest]) tests.extend([BasicRDSTest, RDSTest]) + tests.append(LinuxKernelCryptoAPI) tests.extend([ CmsgMacroTests, SendmsgUDPTest, -- cgit v1.2.1 From 8b5d969b2f06427f0460be47b20e0e2bc9400535 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Tue, 6 Sep 2016 00:04:45 +0200 Subject: Issue #27866: Add SSLContext.get_ciphers() method to get a list of all enabled ciphers. --- Lib/test/test_ssl.py | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index f6afa267c5..f19cf43336 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -834,6 +834,15 @@ class ContextTests(unittest.TestCase): with self.assertRaisesRegex(ssl.SSLError, "No cipher can be selected"): ctx.set_ciphers("^$:,;?*'dorothyx") + @unittest.skipIf(ssl.OPENSSL_VERSION_INFO < (1, 0, 2, 0, 0), 'OpenSSL too old') + def test_get_ciphers(self): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + ctx.set_ciphers('ECDHE+AESGCM:!ECDSA') + names = set(d['name'] for d in ctx.get_ciphers()) + self.assertEqual(names, + {'ECDHE-RSA-AES256-GCM-SHA384', + 'ECDHE-RSA-AES128-GCM-SHA256'}) + @skip_if_broken_ubuntu_ssl def test_options(self): ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) -- cgit v1.2.1 From bb0ec1fc07628252f17d66a58a4a4b3654e571c3 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Mon, 5 Sep 2016 15:11:23 -0700 Subject: Issue #27355: Removed support for Windows CE. It was never finished, and Windows CE is no longer a relevant platform for Python. --- Lib/asyncore.py | 2 +- Lib/ctypes/__init__.py | 17 +++++++---------- Lib/ctypes/test/test_bitfields.py | 4 ++-- Lib/ctypes/test/test_funcptr.py | 2 +- Lib/ctypes/test/test_loading.py | 17 +++++------------ Lib/ctypes/util.py | 10 ---------- Lib/ntpath.py | 2 -- Lib/os.py | 25 ++----------------------- Lib/platform.py | 1 - Lib/tarfile.py | 2 +- Lib/test/support/__init__.py | 2 +- 11 files changed, 20 insertions(+), 64 deletions(-) (limited to 'Lib') diff --git a/Lib/asyncore.py b/Lib/asyncore.py index 4b046d67e3..705e406813 100644 --- a/Lib/asyncore.py +++ b/Lib/asyncore.py @@ -333,7 +333,7 @@ class dispatcher: self.connecting = True err = self.socket.connect_ex(address) if err in (EINPROGRESS, EALREADY, EWOULDBLOCK) \ - or err == EINVAL and os.name in ('nt', 'ce'): + or err == EINVAL and os.name == 'nt': self.addr = address return if err in (0, EISCONN): diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index 0d860784bc..1c9d3a525b 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -16,7 +16,7 @@ from struct import calcsize as _calcsize if __version__ != _ctypes_version: raise Exception("Version number mismatch", __version__, _ctypes_version) -if _os.name in ("nt", "ce"): +if _os.name == "nt": from _ctypes import FormatError DEFAULT_MODE = RTLD_LOCAL @@ -103,12 +103,9 @@ def CFUNCTYPE(restype, *argtypes, **kw): _c_functype_cache[(restype, argtypes, flags)] = CFunctionType return CFunctionType -if _os.name in ("nt", "ce"): +if _os.name == "nt": from _ctypes import LoadLibrary as _dlopen from _ctypes import FUNCFLAG_STDCALL as _FUNCFLAG_STDCALL - if _os.name == "ce": - # 'ce' doesn't have the stdcall calling convention - _FUNCFLAG_STDCALL = _FUNCFLAG_CDECL _win_functype_cache = {} def WINFUNCTYPE(restype, *argtypes, **kw): @@ -262,7 +259,7 @@ class c_wchar(_SimpleCData): def _reset_cache(): _pointer_type_cache.clear() _c_functype_cache.clear() - if _os.name in ("nt", "ce"): + if _os.name == "nt": _win_functype_cache.clear() # _SimpleCData.c_wchar_p_from_param POINTER(c_wchar).from_param = c_wchar_p.from_param @@ -374,7 +371,7 @@ class PyDLL(CDLL): """ _func_flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI -if _os.name in ("nt", "ce"): +if _os.name == "nt": class WinDLL(CDLL): """This class represents a dll exporting functions using the @@ -427,7 +424,7 @@ class LibraryLoader(object): cdll = LibraryLoader(CDLL) pydll = LibraryLoader(PyDLL) -if _os.name in ("nt", "ce"): +if _os.name == "nt": pythonapi = PyDLL("python dll", None, _sys.dllhandle) elif _sys.platform == "cygwin": pythonapi = PyDLL("libpython%d.%d.dll" % _sys.version_info[:2]) @@ -435,7 +432,7 @@ else: pythonapi = PyDLL(None) -if _os.name in ("nt", "ce"): +if _os.name == "nt": windll = LibraryLoader(WinDLL) oledll = LibraryLoader(OleDLL) @@ -503,7 +500,7 @@ else: return _wstring_at(ptr, size) -if _os.name in ("nt", "ce"): # COM stuff +if _os.name == "nt": # COM stuff def DllGetClassObject(rclsid, riid, ppv): try: ccom = __import__("comtypes.server.inprocserver", globals(), locals(), ['*']) diff --git a/Lib/ctypes/test/test_bitfields.py b/Lib/ctypes/test/test_bitfields.py index 0eb09fb4bf..c71d71de69 100644 --- a/Lib/ctypes/test/test_bitfields.py +++ b/Lib/ctypes/test/test_bitfields.py @@ -196,7 +196,7 @@ class BitFieldTest(unittest.TestCase): class X(Structure): _fields_ = [("a", c_byte, 4), ("b", c_int, 4)] - if os.name in ("nt", "ce"): + if os.name == "nt": self.assertEqual(sizeof(X), sizeof(c_int)*2) else: self.assertEqual(sizeof(X), sizeof(c_int)) @@ -224,7 +224,7 @@ class BitFieldTest(unittest.TestCase): # MSVC does NOT combine c_short and c_int into one field, GCC # does (unless GCC is run with '-mms-bitfields' which # produces code compatible with MSVC). - if os.name in ("nt", "ce"): + if os.name == "nt": self.assertEqual(sizeof(X), sizeof(c_int) * 4) else: self.assertEqual(sizeof(X), sizeof(c_int) * 2) diff --git a/Lib/ctypes/test/test_funcptr.py b/Lib/ctypes/test/test_funcptr.py index ff25c8febd..636c045c9e 100644 --- a/Lib/ctypes/test/test_funcptr.py +++ b/Lib/ctypes/test/test_funcptr.py @@ -39,7 +39,7 @@ class CFuncPtrTestCase(unittest.TestCase): # possible, as in C, to call cdecl functions with more parameters. #self.assertRaises(TypeError, c, 1, 2, 3) self.assertEqual(c(1, 2, 3, 4, 5, 6), 3) - if not WINFUNCTYPE is CFUNCTYPE and os.name != "ce": + if not WINFUNCTYPE is CFUNCTYPE: self.assertRaises(TypeError, s, 1, 2, 3) def test_structures(self): diff --git a/Lib/ctypes/test/test_loading.py b/Lib/ctypes/test/test_loading.py index 28468c1cd3..45571f3d3d 100644 --- a/Lib/ctypes/test/test_loading.py +++ b/Lib/ctypes/test/test_loading.py @@ -11,8 +11,6 @@ def setUpModule(): global libc_name if os.name == "nt": libc_name = find_library("c") - elif os.name == "ce": - libc_name = "coredll" elif sys.platform == "cygwin": libc_name = "cygwin1.dll" else: @@ -49,8 +47,8 @@ class LoaderTest(unittest.TestCase): cdll.LoadLibrary(lib) CDLL(lib) - @unittest.skipUnless(os.name in ("nt", "ce"), - 'test specific to Windows (NT/CE)') + @unittest.skipUnless(os.name == "nt", + 'test specific to Windows') def test_load_library(self): # CRT is no longer directly loadable. See issue23606 for the # discussion about alternative approaches. @@ -64,14 +62,9 @@ class LoaderTest(unittest.TestCase): windll["kernel32"].GetModuleHandleW windll.LoadLibrary("kernel32").GetModuleHandleW WinDLL("kernel32").GetModuleHandleW - elif os.name == "ce": - windll.coredll.GetModuleHandleW - windll["coredll"].GetModuleHandleW - windll.LoadLibrary("coredll").GetModuleHandleW - WinDLL("coredll").GetModuleHandleW - - @unittest.skipUnless(os.name in ("nt", "ce"), - 'test specific to Windows (NT/CE)') + + @unittest.skipUnless(os.name == "nt", + 'test specific to Windows') def test_load_ordinal_functions(self): import _ctypes_test dll = WinDLL(_ctypes_test.__file__) diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py index f5c6b266b6..339ae8aa8a 100644 --- a/Lib/ctypes/util.py +++ b/Lib/ctypes/util.py @@ -67,16 +67,6 @@ if os.name == "nt": return fname return None -if os.name == "ce": - # search path according to MSDN: - # - absolute path specified by filename - # - The .exe launch directory - # - the Windows directory - # - ROM dll files (where are they?) - # - OEM specified search path: HKLM\Loader\SystemPath - def find_library(name): - return name - if os.name == "posix" and sys.platform == "darwin": from ctypes.macholib.dyld import dyld_find as _dyld_find def find_library(name): diff --git a/Lib/ntpath.py b/Lib/ntpath.py index 1fa4448426..a8f4b37f64 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -28,8 +28,6 @@ sep = '\\' pathsep = ';' altsep = '/' defpath = '.;C:\\bin' -if 'ce' in sys.builtin_module_names: - defpath = '\\Windows' devnull = 'nul' def _get_bothseps(path): diff --git a/Lib/os.py b/Lib/os.py index 307a1ded42..10d70ada4d 100644 --- a/Lib/os.py +++ b/Lib/os.py @@ -1,9 +1,9 @@ r"""OS routines for NT or Posix depending on what system we're on. This exports: - - all functions from posix, nt or ce, e.g. unlink, stat, etc. + - all functions from posix or nt, e.g. unlink, stat, etc. - os.path is either posixpath or ntpath - - os.name is either 'posix', 'nt' or 'ce'. + - os.name is either 'posix' or 'nt' - os.curdir is a string representing the current directory ('.' or ':') - os.pardir is a string representing the parent directory ('..' or '::') - os.sep is the (or a most common) pathname separator ('/' or ':' or '\\') @@ -85,27 +85,6 @@ elif 'nt' in _names: except ImportError: pass -elif 'ce' in _names: - name = 'ce' - linesep = '\r\n' - from ce import * - try: - from ce import _exit - __all__.append('_exit') - except ImportError: - pass - # We can use the standard Windows path. - import ntpath as path - - import ce - __all__.extend(_get_exports_list(ce)) - del ce - - try: - from ce import _have_functions - except ImportError: - pass - else: raise ImportError('no os specific module found') diff --git a/Lib/platform.py b/Lib/platform.py index b8f3ebc7b1..a5dd763907 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -13,7 +13,6 @@ # Python bug tracker (http://bugs.python.org) and assign them to "lemburg". # # Still needed: -# * more support for WinCE # * support for MS-DOS (PythonDX ?) # * support for Amiga and other still unsupported platforms running Python # * support for additional Linux distributions diff --git a/Lib/tarfile.py b/Lib/tarfile.py index df4745d6ba..960c673067 100755 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -144,7 +144,7 @@ PAX_NUMBER_FIELDS = { #--------------------------------------------------------- # initialization #--------------------------------------------------------- -if os.name in ("nt", "ce"): +if os.name == "nt": ENCODING = "utf-8" else: ENCODING = sys.getfilesystemencoding() diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index d907b108c9..4a7cd40a0f 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -811,7 +811,7 @@ TESTFN_ENCODING = sys.getfilesystemencoding() # encoded by the filesystem encoding (in strict mode). It can be None if we # cannot generate such filename. TESTFN_UNENCODABLE = None -if os.name in ('nt', 'ce'): +if os.name == 'nt': # skip win32s (0) or Windows 9x/ME (1) if sys.getwindowsversion().platform >= 2: # Different kinds of characters from various languages to minimize the -- cgit v1.2.1 From 6ac9162212cbc4cdb3d62cefd170028301ec39c5 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Tue, 6 Sep 2016 00:37:46 +0200 Subject: Issue 27744: Check for AF_ALG support in Kernel --- Lib/test/test_socket.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 6fc6e277c0..1ee6237fa1 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -65,10 +65,22 @@ def _have_socket_rds(): s.close() return True +def _have_socket_alg(): + """Check whether AF_ALG sockets are supported on this host.""" + try: + s = socket.socket(socket.AF_ALG, socket.SOCK_SEQPACKET, 0) + except (AttributeError, OSError): + return False + else: + s.close() + return True + HAVE_SOCKET_CAN = _have_socket_can() HAVE_SOCKET_RDS = _have_socket_rds() +HAVE_SOCKET_ALG = _have_socket_alg() + # Size in bytes of the int type SIZEOF_INT = array.array("i").itemsize @@ -5325,7 +5337,8 @@ class SendfileUsingSendfileTest(SendfileUsingSendTest): def meth_from_sock(self, sock): return getattr(sock, "_sendfile_use_sendfile") -@unittest.skipUnless(hasattr(socket, "AF_ALG"), 'AF_ALG required') + +@unittest.skipUnless(HAVE_SOCKET_ALG, 'AF_ALG required') class LinuxKernelCryptoAPI(unittest.TestCase): # tests for AF_ALG def create_alg(self, typ, name): -- cgit v1.2.1 From eff51a6b66fc3b745040c4390997524e8925655e Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Mon, 5 Sep 2016 15:40:59 -0700 Subject: os.access does not allow a fd --- Lib/test/test_os.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index c1e1adc8be..c5bea85183 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2826,7 +2826,7 @@ class PathTConverterTests(unittest.TestCase): functions = [ ('stat', True, (), None), ('lstat', False, (), None), - ('access', True, (os.F_OK,), None), + ('access', False, (os.F_OK,), None), ('chflags', False, (0,), None), ('lchflags', False, (0,), None), ('open', False, (0,), getattr(os, 'close', None)), -- cgit v1.2.1 From eceac73ef6b9b50527c251dff5c83ac20df4c717 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Tue, 6 Sep 2016 00:58:47 +0200 Subject: Issue 27744: AES-CBC and DRBG need Kernel 3.19+ --- Lib/test/test_socket.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 1ee6237fa1..71837143c5 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -5372,6 +5372,7 @@ class LinuxKernelCryptoAPI(unittest.TestCase): op.sendall(b"what do ya want for nothing?") self.assertEqual(op.recv(512), expected) + @support.requires_linux_version(3, 19) def test_aes_cbc(self): key = bytes.fromhex('06a9214036b8a15b512e03d534120006') iv = bytes.fromhex('3dafba429d9eb430b422da802c9fac41') @@ -5476,6 +5477,7 @@ class LinuxKernelCryptoAPI(unittest.TestCase): res = op.recv(len(msg)) self.assertEqual(plain, res[assoclen:-taglen]) + @support.requires_linux_version(3, 19) def test_drbg_pr_sha256(self): # deterministic random bit generator, prediction resistance, sha256 with self.create_alg('rng', 'drbg_pr_sha256') as algo: -- cgit v1.2.1 From 907ccded8b6fa68f6f572ae5de6760595df0d5a1 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Mon, 5 Sep 2016 17:44:18 -0700 Subject: require a long long data type (closes #27961) --- Lib/test/test_struct.py | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py index 2ce855d458..4d9d601ef4 100644 --- a/Lib/test/test_struct.py +++ b/Lib/test/test_struct.py @@ -16,22 +16,10 @@ byteorders = '', '@', '=', '<', '>', '!' def iter_integer_formats(byteorders=byteorders): for code in integer_codes: for byteorder in byteorders: - if (byteorder in ('', '@') and code in ('q', 'Q') and - not HAVE_LONG_LONG): - continue if (byteorder not in ('', '@') and code in ('n', 'N')): continue yield code, byteorder -# Native 'q' packing isn't available on systems that don't have the C -# long long type. -try: - struct.pack('q', 5) -except struct.error: - HAVE_LONG_LONG = False -else: - HAVE_LONG_LONG = True - def string_reverse(s): return s[::-1] @@ -159,9 +147,7 @@ class StructTest(unittest.TestCase): self.assertEqual(size, expected_size[code]) # native integer sizes - native_pairs = 'bB', 'hH', 'iI', 'lL', 'nN' - if HAVE_LONG_LONG: - native_pairs += 'qQ', + native_pairs = 'bB', 'hH', 'iI', 'lL', 'nN', 'qQ' for format_pair in native_pairs: for byteorder in '', '@': signed_size = struct.calcsize(byteorder + format_pair[0]) @@ -174,9 +160,8 @@ class StructTest(unittest.TestCase): self.assertLessEqual(4, struct.calcsize('l')) self.assertLessEqual(struct.calcsize('h'), struct.calcsize('i')) self.assertLessEqual(struct.calcsize('i'), struct.calcsize('l')) - if HAVE_LONG_LONG: - self.assertLessEqual(8, struct.calcsize('q')) - self.assertLessEqual(struct.calcsize('l'), struct.calcsize('q')) + self.assertLessEqual(8, struct.calcsize('q')) + self.assertLessEqual(struct.calcsize('l'), struct.calcsize('q')) self.assertGreaterEqual(struct.calcsize('n'), struct.calcsize('i')) self.assertGreaterEqual(struct.calcsize('n'), struct.calcsize('P')) -- cgit v1.2.1 From bf7ed725f5a3ba7f168dfbe5500a8949a97f917b Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Tue, 6 Sep 2016 02:18:16 +0000 Subject: Issue #27355: Import no longer needed --- Lib/ctypes/test/test_funcptr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/ctypes/test/test_funcptr.py b/Lib/ctypes/test/test_funcptr.py index 636c045c9e..f34734b164 100644 --- a/Lib/ctypes/test/test_funcptr.py +++ b/Lib/ctypes/test/test_funcptr.py @@ -1,4 +1,4 @@ -import os, unittest +import unittest from ctypes import * try: -- cgit v1.2.1 From 2b780402fed75e28ab5fd75bb9ff9ecefa270ca5 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Tue, 6 Sep 2016 10:45:44 +0200 Subject: Issue 27866: relax test case for set_cipher() and allow more cipher suites --- Lib/test/test_ssl.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index f19cf43336..07fb1026a5 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -837,11 +837,10 @@ class ContextTests(unittest.TestCase): @unittest.skipIf(ssl.OPENSSL_VERSION_INFO < (1, 0, 2, 0, 0), 'OpenSSL too old') def test_get_ciphers(self): ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) - ctx.set_ciphers('ECDHE+AESGCM:!ECDSA') + ctx.set_ciphers('AESGCM') names = set(d['name'] for d in ctx.get_ciphers()) - self.assertEqual(names, - {'ECDHE-RSA-AES256-GCM-SHA384', - 'ECDHE-RSA-AES128-GCM-SHA256'}) + self.assertIn('ECDHE-RSA-AES256-GCM-SHA384', names) + self.assertIn('ECDHE-RSA-AES128-GCM-SHA256', names) @skip_if_broken_ubuntu_ssl def test_options(self): -- cgit v1.2.1 From f37f417c148a1dee68eff66f4568214bcbb46a0a Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Tue, 6 Sep 2016 11:14:09 +0200 Subject: Issue 27744: skip test if AF_ALG socket bind fails --- Lib/test/test_socket.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 71837143c5..b3632e9af9 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -5343,7 +5343,11 @@ class LinuxKernelCryptoAPI(unittest.TestCase): # tests for AF_ALG def create_alg(self, typ, name): sock = socket.socket(socket.AF_ALG, socket.SOCK_SEQPACKET, 0) - sock.bind((typ, name)) + try: + sock.bind((typ, name)) + except FileNotFoundError as e: + # type / algorithm is not available + raise unittest.SkipTest(str(e), typ, name) return sock def test_sha256(self): -- cgit v1.2.1 From e295df31d14122eb1c56300d2f2932e47195d8a9 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Tue, 6 Sep 2016 11:27:25 +0200 Subject: Issue 27866: relax get_cipher() test even more. Gentoo buildbot has no ECDHE --- Lib/test/test_ssl.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 07fb1026a5..4e0e4a2185 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -839,8 +839,8 @@ class ContextTests(unittest.TestCase): ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) ctx.set_ciphers('AESGCM') names = set(d['name'] for d in ctx.get_ciphers()) - self.assertIn('ECDHE-RSA-AES256-GCM-SHA384', names) - self.assertIn('ECDHE-RSA-AES128-GCM-SHA256', names) + self.assertIn('AES256-GCM-SHA384', names) + self.assertIn('AES128-GCM-SHA256', names) @skip_if_broken_ubuntu_ssl def test_options(self): -- cgit v1.2.1 From 205ba651be1bebdc2c1fffd368feebe82783541b Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Tue, 6 Sep 2016 20:22:28 +0200 Subject: Issue #27928: Add scrypt (password-based key derivation function) to hashlib module (requires OpenSSL 1.1.0). --- Lib/hashlib.py | 6 ++++++ Lib/test/test_hashlib.py | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) (limited to 'Lib') diff --git a/Lib/hashlib.py b/Lib/hashlib.py index 316cecedc2..348ea14a05 100644 --- a/Lib/hashlib.py +++ b/Lib/hashlib.py @@ -202,6 +202,12 @@ except ImportError: return dkey[:dklen] +try: + # OpenSSL's scrypt requires OpenSSL 1.1+ + from _hashlib import scrypt +except ImportError: + pass + for __func_name in __always_supported: # try them all, some may not work due to the OpenSSL diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py index c9b113e6ef..b010a74a72 100644 --- a/Lib/test/test_hashlib.py +++ b/Lib/test/test_hashlib.py @@ -7,6 +7,7 @@ # import array +from binascii import unhexlify import hashlib import itertools import os @@ -447,6 +448,12 @@ class KDFTests(unittest.TestCase): (b'pass\0word', b'sa\0lt', 4096, 16), ] + scrypt_test_vectors = [ + (b'', b'', 16, 1, 1, unhexlify('77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906')), + (b'password', b'NaCl', 1024, 8, 16, unhexlify('fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b3731622eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640')), + (b'pleaseletmein', b'SodiumChloride', 16384, 8, 1, unhexlify('7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2d5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887')), + ] + pbkdf2_results = { "sha1": [ # official test vectors from RFC 6070 @@ -526,5 +533,45 @@ class KDFTests(unittest.TestCase): self._test_pbkdf2_hmac(c_hashlib.pbkdf2_hmac) + @unittest.skipUnless(hasattr(c_hashlib, 'scrypt'), + ' test requires OpenSSL > 1.1') + def test_scrypt(self): + for password, salt, n, r, p, expected in self.scrypt_test_vectors: + result = hashlib.scrypt(password, salt=salt, n=n, r=r, p=p) + self.assertEqual(result, expected) + + # this values should work + hashlib.scrypt(b'password', salt=b'salt', n=2, r=8, p=1) + # password and salt must be bytes-like + with self.assertRaises(TypeError): + hashlib.scrypt('password', salt=b'salt', n=2, r=8, p=1) + with self.assertRaises(TypeError): + hashlib.scrypt(b'password', salt='salt', n=2, r=8, p=1) + # require keyword args + with self.assertRaises(TypeError): + hashlib.scrypt(b'password') + with self.assertRaises(TypeError): + hashlib.scrypt(b'password', b'salt') + with self.assertRaises(TypeError): + hashlib.scrypt(b'password', 2, 8, 1, salt=b'salt') + for n in [-1, 0, 1, None]: + with self.assertRaises((ValueError, OverflowError, TypeError)): + hashlib.scrypt(b'password', salt=b'salt', n=n, r=8, p=1) + for r in [-1, 0, None]: + with self.assertRaises((ValueError, OverflowError, TypeError)): + hashlib.scrypt(b'password', salt=b'salt', n=2, r=r, p=1) + for p in [-1, 0, None]: + with self.assertRaises((ValueError, OverflowError, TypeError)): + hashlib.scrypt(b'password', salt=b'salt', n=2, r=8, p=p) + for maxmem in [-1, None]: + with self.assertRaises((ValueError, OverflowError, TypeError)): + hashlib.scrypt(b'password', salt=b'salt', n=2, r=8, p=1, + maxmem=maxmem) + for dklen in [-1, None]: + with self.assertRaises((ValueError, OverflowError, TypeError)): + hashlib.scrypt(b'password', salt=b'salt', n=2, r=8, p=1, + dklen=dklen) + + if __name__ == "__main__": unittest.main() -- cgit v1.2.1 From 0c3c0375111bde5f7586e6c951f08a0035039a39 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 6 Sep 2016 22:07:53 +0300 Subject: Issue #27078: Added BUILD_STRING opcode. Optimized f-strings evaluation. --- Lib/importlib/_bootstrap_external.py | 5 +++-- Lib/opcode.py | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 46f1bab19b..d36e4ac521 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -232,7 +232,8 @@ _code_type = type(_write_atomic.__code__) # Python 3.6a1 3370 (16 bit wordcode) # Python 3.6a1 3371 (add BUILD_CONST_KEY_MAP opcode #27140) # Python 3.6a1 3372 (MAKE_FUNCTION simplification, remove MAKE_CLOSURE - #27095) +# #27095) +# Python 3.6b1 3373 (add BUILD_STRING opcode #27078) # # MAGIC must change whenever the bytecode emitted by the compiler may no # longer be understood by older implementations of the eval loop (usually @@ -241,7 +242,7 @@ _code_type = type(_write_atomic.__code__) # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # in PC/launcher.c must also be updated. -MAGIC_NUMBER = (3372).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3373).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c _PYCACHE = '__pycache__' diff --git a/Lib/opcode.py b/Lib/opcode.py index 064081981d..d9202e8353 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -213,5 +213,6 @@ def_op('BUILD_SET_UNPACK', 153) def_op('FORMAT_VALUE', 155) def_op('BUILD_CONST_KEY_MAP', 156) +def_op('BUILD_STRING', 157) del def_op, name_op, jrel_op, jabs_op -- cgit v1.2.1 From 062d26e876867a0f8c7dd08dfbe8ac208f156786 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 6 Sep 2016 22:33:41 +0300 Subject: Issue #25596: Optimized glob() and iglob() functions in the glob module; they are now about 3--6 times faster. --- Lib/glob.py | 70 ++++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 41 insertions(+), 29 deletions(-) (limited to 'Lib') diff --git a/Lib/glob.py b/Lib/glob.py index 16330d816a..002cd92019 100644 --- a/Lib/glob.py +++ b/Lib/glob.py @@ -30,15 +30,16 @@ def iglob(pathname, *, recursive=False): If recursive is true, the pattern '**' will match any files and zero or more directories and subdirectories. """ - it = _iglob(pathname, recursive) + it = _iglob(pathname, recursive, False) if recursive and _isrecursive(pathname): s = next(it) # skip empty string assert not s return it -def _iglob(pathname, recursive): +def _iglob(pathname, recursive, dironly): dirname, basename = os.path.split(pathname) if not has_magic(pathname): + assert not dironly if basename: if os.path.lexists(pathname): yield pathname @@ -49,47 +50,39 @@ def _iglob(pathname, recursive): return if not dirname: if recursive and _isrecursive(basename): - yield from glob2(dirname, basename) + yield from _glob2(dirname, basename, dironly) else: - yield from glob1(dirname, basename) + yield from _glob1(dirname, basename, dironly) return # `os.path.split()` returns the argument itself as a dirname if it is a # drive or UNC path. Prevent an infinite recursion if a drive or UNC path # contains magic characters (i.e. r'\\?\C:'). if dirname != pathname and has_magic(dirname): - dirs = _iglob(dirname, recursive) + dirs = _iglob(dirname, recursive, True) else: dirs = [dirname] if has_magic(basename): if recursive and _isrecursive(basename): - glob_in_dir = glob2 + glob_in_dir = _glob2 else: - glob_in_dir = glob1 + glob_in_dir = _glob1 else: - glob_in_dir = glob0 + glob_in_dir = _glob0 for dirname in dirs: - for name in glob_in_dir(dirname, basename): + for name in glob_in_dir(dirname, basename, dironly): yield os.path.join(dirname, name) # These 2 helper functions non-recursively glob inside a literal directory. -# They return a list of basenames. `glob1` accepts a pattern while `glob0` +# They return a list of basenames. _glob1 accepts a pattern while _glob0 # takes a literal basename (so it only has to check for its existence). -def glob1(dirname, pattern): - if not dirname: - if isinstance(pattern, bytes): - dirname = bytes(os.curdir, 'ASCII') - else: - dirname = os.curdir - try: - names = os.listdir(dirname) - except OSError: - return [] +def _glob1(dirname, pattern, dironly): + names = list(_iterdir(dirname, dironly)) if not _ishidden(pattern): - names = [x for x in names if not _ishidden(x)] + names = (x for x in names if not _ishidden(x)) return fnmatch.filter(names, pattern) -def glob0(dirname, basename): +def _glob0(dirname, basename, dironly): if not basename: # `os.path.split()` returns an empty basename for paths ending with a # directory separator. 'q*x/' should match only directories. @@ -100,30 +93,49 @@ def glob0(dirname, basename): return [basename] return [] +# Following functions are not public but can be used by third-party code. + +def glob0(dirname, pattern): + return _glob0(dirname, pattern, False) + +def glob1(dirname, pattern): + return _glob1(dirname, pattern, False) + # This helper function recursively yields relative pathnames inside a literal # directory. -def glob2(dirname, pattern): +def _glob2(dirname, pattern, dironly): assert _isrecursive(pattern) yield pattern[:0] - yield from _rlistdir(dirname) + yield from _rlistdir(dirname, dironly) -# Recursively yields relative pathnames inside a literal directory. -def _rlistdir(dirname): +# If dironly is false, yields all file names inside a directory. +# If dironly is true, yields only directory names. +def _iterdir(dirname, dironly): if not dirname: if isinstance(dirname, bytes): dirname = bytes(os.curdir, 'ASCII') else: dirname = os.curdir try: - names = os.listdir(dirname) - except os.error: + with os.scandir(dirname) as it: + for entry in it: + try: + if not dironly or entry.is_dir(): + yield entry.name + except OSError: + pass + except OSError: return + +# Recursively yields relative pathnames inside a literal directory. +def _rlistdir(dirname, dironly): + names = list(_iterdir(dirname, dironly)) for x in names: if not _ishidden(x): yield x path = os.path.join(dirname, x) if dirname else x - for y in _rlistdir(path): + for y in _rlistdir(path, dironly): yield os.path.join(x, y) -- cgit v1.2.1 From cb9bcb8b55671d4dac0f2cc4998da3d961bd88d4 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Tue, 6 Sep 2016 22:03:25 +0200 Subject: Issue #26798: Add BLAKE2 (blake2b and blake2s) to hashlib. --- Lib/hashlib.py | 32 +++++--- Lib/test/test_hashlib.py | 201 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 217 insertions(+), 16 deletions(-) (limited to 'Lib') diff --git a/Lib/hashlib.py b/Lib/hashlib.py index 348ea14a05..40ccdec351 100644 --- a/Lib/hashlib.py +++ b/Lib/hashlib.py @@ -4,14 +4,14 @@ __doc__ = """hashlib module - A common interface to many hash functions. -new(name, data=b'') - returns a new hash object implementing the - given hash function; initializing the hash - using the given binary data. +new(name, data=b'', **kwargs) - returns a new hash object implementing the + given hash function; initializing the hash + using the given binary data. Named constructor functions are also available, these are faster than using new(name): -md5(), sha1(), sha224(), sha256(), sha384(), and sha512() +md5(), sha1(), sha224(), sha256(), sha384(), sha512(), blake2b(), and blake2s() More algorithms may be available on your platform but the above are guaranteed to exist. See the algorithms_guaranteed and algorithms_available attributes @@ -54,7 +54,8 @@ More condensed: # This tuple and __get_builtin_constructor() must be modified if a new # always available algorithm is added. -__always_supported = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512') +__always_supported = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', + 'blake2b', 'blake2s') algorithms_guaranteed = set(__always_supported) algorithms_available = set(__always_supported) @@ -85,6 +86,10 @@ def __get_builtin_constructor(name): import _sha512 cache['SHA384'] = cache['sha384'] = _sha512.sha384 cache['SHA512'] = cache['sha512'] = _sha512.sha512 + elif name in ('blake2b', 'blake2s'): + import _blake2 + cache['blake2b'] = _blake2.blake2b + cache['blake2s'] = _blake2.blake2s except ImportError: pass # no extension module, this hash is unsupported. @@ -107,17 +112,23 @@ def __get_openssl_constructor(name): return __get_builtin_constructor(name) -def __py_new(name, data=b''): - """new(name, data=b'') - Return a new hashing object using the named algorithm; - optionally initialized with data (which must be bytes). +def __py_new(name, data=b'', **kwargs): + """new(name, data=b'', **kwargs) - Return a new hashing object using the + named algorithm; optionally initialized with data (which must be bytes). """ - return __get_builtin_constructor(name)(data) + return __get_builtin_constructor(name)(data, **kwargs) -def __hash_new(name, data=b''): +def __hash_new(name, data=b'', **kwargs): """new(name, data=b'') - Return a new hashing object using the named algorithm; optionally initialized with data (which must be bytes). """ + if name in {'blake2b', 'blake2s'}: + # Prefer our blake2 implementation. + # OpenSSL 1.1.0 comes with a limited implementation of blake2b/s. + # It does neither support keyed blake2 nor advanced features like + # salt, personal, tree hashing or SSE. + return __get_builtin_constructor(name)(data, **kwargs) try: return _hashlib.new(name, data) except ValueError: @@ -218,6 +229,7 @@ for __func_name in __always_supported: import logging logging.exception('code for hash %s was not found.', __func_name) + # Cleanup locals() del __always_supported, __func_name, __get_hash del __py_new, __hash_new, __get_openssl_constructor diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py index b010a74a72..e6df09cbf6 100644 --- a/Lib/test/test_hashlib.py +++ b/Lib/test/test_hashlib.py @@ -27,6 +27,14 @@ COMPILED_WITH_PYDEBUG = hasattr(sys, 'gettotalrefcount') c_hashlib = import_fresh_module('hashlib', fresh=['_hashlib']) py_hashlib = import_fresh_module('hashlib', blocked=['_hashlib']) +try: + import _blake2 +except ImportError: + _blake2 = None + +requires_blake2 = unittest.skipUnless(_blake2, 'requires _blake2') + + def hexstr(s): assert isinstance(s, bytes), repr(s) h = "0123456789abcdef" @@ -36,10 +44,24 @@ def hexstr(s): return r +URL = "https://raw.githubusercontent.com/tiran/python_vectors/master/{}.txt" + +def read_vectors(hash_name): + with support.open_urlresource(URL.format(hash_name)) as f: + for line in f: + line = line.strip() + if line.startswith('#') or not line: + continue + parts = line.split(',') + parts[0] = bytes.fromhex(parts[0]) + yield parts + + class HashLibTestCase(unittest.TestCase): supported_hash_names = ( 'md5', 'MD5', 'sha1', 'SHA1', 'sha224', 'SHA224', 'sha256', 'SHA256', - 'sha384', 'SHA384', 'sha512', 'SHA512') + 'sha384', 'SHA384', 'sha512', 'SHA512', + 'blake2b', 'blake2s') # Issue #14693: fallback modules are always compiled under POSIX _warn_on_extension_import = os.name == 'posix' or COMPILED_WITH_PYDEBUG @@ -57,6 +79,11 @@ class HashLibTestCase(unittest.TestCase): algorithms = set() for algorithm in self.supported_hash_names: algorithms.add(algorithm.lower()) + + _blake2 = self._conditional_import_module('_blake2') + if _blake2: + algorithms.update({'blake2b', 'blake2s'}) + self.constructors_to_test = {} for algorithm in algorithms: self.constructors_to_test[algorithm] = set() @@ -65,10 +92,10 @@ class HashLibTestCase(unittest.TestCase): # of hashlib.new given the algorithm name. for algorithm, constructors in self.constructors_to_test.items(): constructors.add(getattr(hashlib, algorithm)) - def _test_algorithm_via_hashlib_new(data=None, _alg=algorithm): + def _test_algorithm_via_hashlib_new(data=None, _alg=algorithm, **kwargs): if data is None: - return hashlib.new(_alg) - return hashlib.new(_alg, data) + return hashlib.new(_alg, **kwargs) + return hashlib.new(_alg, data, **kwargs) constructors.add(_test_algorithm_via_hashlib_new) _hashlib = self._conditional_import_module('_hashlib') @@ -100,6 +127,9 @@ class HashLibTestCase(unittest.TestCase): if _sha512: add_builtin_constructor('sha384') add_builtin_constructor('sha512') + if _blake2: + add_builtin_constructor('blake2s') + add_builtin_constructor('blake2b') super(HashLibTestCase, self).__init__(*args, **kwargs) @@ -194,13 +224,13 @@ class HashLibTestCase(unittest.TestCase): self.assertEqual(m1.digest(), m4_copy.digest()) self.assertEqual(m4.digest(), m4_digest) - def check(self, name, data, hexdigest): + def check(self, name, data, hexdigest, **kwargs): hexdigest = hexdigest.lower() constructors = self.constructors_to_test[name] # 2 is for hashlib.name(...) and hashlib.new(name, ...) self.assertGreaterEqual(len(constructors), 2) for hash_object_constructor in constructors: - m = hash_object_constructor(data) + m = hash_object_constructor(data, **kwargs) computed = m.hexdigest() self.assertEqual( computed, hexdigest, @@ -227,6 +257,11 @@ class HashLibTestCase(unittest.TestCase): self.check_no_unicode('sha384') self.check_no_unicode('sha512') + @requires_blake2 + def test_no_unicode_blake2(self): + self.check_no_unicode('blake2b') + self.check_no_unicode('blake2s') + def check_blocksize_name(self, name, block_size=0, digest_size=0): constructors = self.constructors_to_test[name] for hash_object_constructor in constructors: @@ -246,6 +281,11 @@ class HashLibTestCase(unittest.TestCase): self.check_blocksize_name('sha384', 128, 48) self.check_blocksize_name('sha512', 128, 64) + @requires_blake2 + def test_blocksize_name_blake2(self): + self.check_blocksize_name('blake2b', 128, 64) + self.check_blocksize_name('blake2s', 64, 32) + def test_case_md5_0(self): self.check('md5', b'', 'd41d8cd98f00b204e9800998ecf8427e') @@ -374,6 +414,155 @@ class HashLibTestCase(unittest.TestCase): "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973eb"+ "de0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b") + def check_blake2(self, constructor, salt_size, person_size, key_size, + digest_size, max_offset): + self.assertEqual(constructor.SALT_SIZE, salt_size) + for i in range(salt_size + 1): + constructor(salt=b'a' * i) + salt = b'a' * (salt_size + 1) + self.assertRaises(ValueError, constructor, salt=salt) + + self.assertEqual(constructor.PERSON_SIZE, person_size) + for i in range(person_size+1): + constructor(person=b'a' * i) + person = b'a' * (person_size + 1) + self.assertRaises(ValueError, constructor, person=person) + + self.assertEqual(constructor.MAX_DIGEST_SIZE, digest_size) + for i in range(1, digest_size + 1): + constructor(digest_size=i) + self.assertRaises(ValueError, constructor, digest_size=-1) + self.assertRaises(ValueError, constructor, digest_size=0) + self.assertRaises(ValueError, constructor, digest_size=digest_size+1) + + self.assertEqual(constructor.MAX_KEY_SIZE, key_size) + for i in range(key_size+1): + constructor(key=b'a' * i) + key = b'a' * (key_size + 1) + self.assertRaises(ValueError, constructor, key=key) + self.assertEqual(constructor().hexdigest(), + constructor(key=b'').hexdigest()) + + for i in range(0, 256): + constructor(fanout=i) + self.assertRaises(ValueError, constructor, fanout=-1) + self.assertRaises(ValueError, constructor, fanout=256) + + for i in range(1, 256): + constructor(depth=i) + self.assertRaises(ValueError, constructor, depth=-1) + self.assertRaises(ValueError, constructor, depth=0) + self.assertRaises(ValueError, constructor, depth=256) + + for i in range(0, 256): + constructor(node_depth=i) + self.assertRaises(ValueError, constructor, node_depth=-1) + self.assertRaises(ValueError, constructor, node_depth=256) + + for i in range(0, digest_size + 1): + constructor(inner_size=i) + self.assertRaises(ValueError, constructor, inner_size=-1) + self.assertRaises(ValueError, constructor, inner_size=digest_size+1) + + constructor(leaf_size=0) + constructor(leaf_size=(1<<32)-1) + self.assertRaises(OverflowError, constructor, leaf_size=-1) + self.assertRaises(OverflowError, constructor, leaf_size=1<<32) + + constructor(node_offset=0) + constructor(node_offset=max_offset) + self.assertRaises(OverflowError, constructor, node_offset=-1) + self.assertRaises(OverflowError, constructor, node_offset=max_offset+1) + + constructor( + string=b'', + key=b'', + salt=b'', + person=b'', + digest_size=17, + fanout=1, + depth=1, + leaf_size=256, + node_offset=512, + node_depth=1, + inner_size=7, + last_node=True + ) + + def blake2_rfc7693(self, constructor, md_len, in_len): + def selftest_seq(length, seed): + mask = (1<<32)-1 + a = (0xDEAD4BAD * seed) & mask + b = 1 + out = bytearray(length) + for i in range(length): + t = (a + b) & mask + a, b = b, t + out[i] = (t >> 24) & 0xFF + return out + outer = constructor(digest_size=32) + for outlen in md_len: + for inlen in in_len: + indata = selftest_seq(inlen, inlen) + key = selftest_seq(outlen, outlen) + unkeyed = constructor(indata, digest_size=outlen) + outer.update(unkeyed.digest()) + keyed = constructor(indata, key=key, digest_size=outlen) + outer.update(keyed.digest()) + return outer.hexdigest() + + @requires_blake2 + def test_blake2b(self): + self.check_blake2(hashlib.blake2b, 16, 16, 64, 64, (1<<64)-1) + b2b_md_len = [20, 32, 48, 64] + b2b_in_len = [0, 3, 128, 129, 255, 1024] + self.assertEqual( + self.blake2_rfc7693(hashlib.blake2b, b2b_md_len, b2b_in_len), + "c23a7800d98123bd10f506c61e29da5603d763b8bbad2e737f5e765a7bccd475") + + @requires_blake2 + def test_case_blake2b_0(self): + self.check('blake2b', b"", + "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419"+ + "d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce") + + @requires_blake2 + def test_case_blake2b_1(self): + self.check('blake2b', b"abc", + "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1"+ + "7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923") + + @requires_blake2 + def test_blake2b_vectors(self): + for msg, key, md in read_vectors('blake2b'): + key = bytes.fromhex(key) + self.check('blake2b', msg, md, key=key) + + @requires_blake2 + def test_blake2s(self): + self.check_blake2(hashlib.blake2s, 8, 8, 32, 32, (1<<48)-1) + b2s_md_len = [16, 20, 28, 32] + b2s_in_len = [0, 3, 64, 65, 255, 1024] + self.assertEqual( + self.blake2_rfc7693(hashlib.blake2s, b2s_md_len, b2s_in_len), + "6a411f08ce25adcdfb02aba641451cec53c598b24f4fc787fbdc88797f4c1dfe") + + @requires_blake2 + def test_case_blake2s_0(self): + self.check('blake2s', b"", + "69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9") + + @requires_blake2 + def test_case_blake2s_1(self): + self.check('blake2s', b"abc", + "508c5e8c327c14e2e1a72ba34eeb452f37458b209ed63a294d999b4c86675982") + + @requires_blake2 + def test_blake2s_vectors(self): + for msg, key, md in read_vectors('blake2s'): + key = bytes.fromhex(key) + self.check('blake2s', msg, md, key=key) + def test_gil(self): # Check things work fine with an input larger than the size required # for multithreaded operation (which is hardwired to 2048). -- cgit v1.2.1 From b04d7bfe501cbc6990d2d24ff41308c74e7299fa Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 6 Sep 2016 23:55:11 +0300 Subject: Issue #25761: Improved error reporting about truncated pickle data in C implementation of unpickler. UnpicklingError is now raised instead of AttributeError and ValueError in some cases. --- Lib/test/test_pickle.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_pickle.py b/Lib/test/test_pickle.py index 36970fefe0..05203e52cd 100644 --- a/Lib/test/test_pickle.py +++ b/Lib/test/test_pickle.py @@ -139,8 +139,7 @@ if has_c_implementation: class CUnpicklerTests(PyUnpicklerTests): unpickler = _pickle.Unpickler bad_stack_errors = (pickle.UnpicklingError,) - truncated_errors = (pickle.UnpicklingError, EOFError, - AttributeError, ValueError) + truncated_errors = (pickle.UnpicklingError,) class CPicklerTests(PyPicklerTests): pickler = _pickle.Pickler -- cgit v1.2.1 From 1b330a040ac363eea07d1e0e0c4dc8b1f4d8b02c Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Tue, 6 Sep 2016 16:32:43 -0500 Subject: Closes #27982: Allow keyword arguments to winsound functions --- Lib/test/test_winsound.py | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_winsound.py b/Lib/test/test_winsound.py index 1cfef779d6..179e069a1c 100644 --- a/Lib/test/test_winsound.py +++ b/Lib/test/test_winsound.py @@ -51,6 +51,10 @@ class BeepTest(unittest.TestCase): for i in range(100, 2000, 100): safe_Beep(i, 75) + def test_keyword_args(self): + safe_Beep(duration=75, frequency=2000) + + class MessageBeepTest(unittest.TestCase): def tearDown(self): @@ -76,6 +80,9 @@ class MessageBeepTest(unittest.TestCase): def test_question(self): safe_MessageBeep(winsound.MB_ICONQUESTION) + def test_keyword_args(self): + safe_MessageBeep(type=winsound.MB_OK) + class PlaySoundTest(unittest.TestCase): @@ -92,6 +99,9 @@ class PlaySoundTest(unittest.TestCase): winsound.SND_MEMORY) self.assertRaises(TypeError, winsound.PlaySound, 1, 0) + def test_keyword_args(self): + safe_PlaySound(flags=winsound.SND_ALIAS, sound="SystemExit") + def test_snd_memory(self): with open(support.findfile('pluck-pcm8.wav', subdir='audiodata'), 'rb') as f: -- cgit v1.2.1 From 6943920987d1a53b4df9ad93a01e4eb590ede3a7 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Tue, 6 Sep 2016 15:50:29 -0700 Subject: Issue #26027: Support path-like objects in PyUnicode-FSConverter(). This is to add support for os.exec*() and os.spawn*() functions. Part of PEP 519. --- Lib/test/test_os.py | 64 +++++++++++++++++++++++------------------------------ 1 file changed, 28 insertions(+), 36 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index c5bea85183..d79a32fc71 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -100,6 +100,21 @@ def bytes_filename_warn(expected): yield +class _PathLike(os.PathLike): + + def __init__(self, path=""): + self.path = path + + def __str__(self): + return str(self.path) + + def __fspath__(self): + if isinstance(self.path, BaseException): + raise self.path + else: + return self.path + + def create_file(filename, content=b'content'): with open(filename, "xb", 0) as fp: fp.write(content) @@ -894,15 +909,7 @@ class WalkTests(unittest.TestCase): self.assertEqual(all[1], self.sub2_tree) def test_file_like_path(self): - class FileLike: - def __init__(self, path): - self._path = path - def __str__(self): - return str(self._path) - def __fspath__(self): - return self._path - - self.test_walk_prune(FileLike(self.walk_path)) + self.test_walk_prune(_PathLike(self.walk_path)) def test_walk_bottom_up(self): # Walk bottom-up. @@ -2124,7 +2131,8 @@ class PidTests(unittest.TestCase): def test_waitpid(self): args = [sys.executable, '-c', 'pass'] - pid = os.spawnv(os.P_NOWAIT, args[0], args) + # Add an implicit test for PyUnicode_FSConverter(). + pid = os.spawnv(os.P_NOWAIT, _PathLike(args[0]), args) status = os.waitpid(pid, 0) self.assertEqual(status, (pid, 0)) @@ -2833,25 +2841,18 @@ class PathTConverterTests(unittest.TestCase): ] def test_path_t_converter(self): - class PathLike: - def __init__(self, path): - self.path = path - - def __fspath__(self): - return self.path - str_filename = support.TESTFN if os.name == 'nt': bytes_fspath = bytes_filename = None else: bytes_filename = support.TESTFN.encode('ascii') - bytes_fspath = PathLike(bytes_filename) - fd = os.open(PathLike(str_filename), os.O_WRONLY|os.O_CREAT) + bytes_fspath = _PathLike(bytes_filename) + fd = os.open(_PathLike(str_filename), os.O_WRONLY|os.O_CREAT) self.addCleanup(support.unlink, support.TESTFN) self.addCleanup(os.close, fd) - int_fspath = PathLike(fd) - str_fspath = PathLike(str_filename) + int_fspath = _PathLike(fd) + str_fspath = _PathLike(str_filename) for name, allow_fd, extra_args, cleanup_fn in self.functions: with self.subTest(name=name): @@ -3205,15 +3206,6 @@ class TestPEP519(unittest.TestCase): # if a C version is provided. fspath = staticmethod(os.fspath) - class PathLike: - def __init__(self, path=''): - self.path = path - def __fspath__(self): - if isinstance(self.path, BaseException): - raise self.path - else: - return self.path - def test_return_bytes(self): for b in b'hello', b'goodbye', b'some/path/and/file': self.assertEqual(b, self.fspath(b)) @@ -3224,16 +3216,16 @@ class TestPEP519(unittest.TestCase): def test_fsencode_fsdecode(self): for p in "path/like/object", b"path/like/object": - pathlike = self.PathLike(p) + pathlike = _PathLike(p) self.assertEqual(p, self.fspath(pathlike)) self.assertEqual(b"path/like/object", os.fsencode(pathlike)) self.assertEqual("path/like/object", os.fsdecode(pathlike)) def test_pathlike(self): - self.assertEqual('#feelthegil', self.fspath(self.PathLike('#feelthegil'))) - self.assertTrue(issubclass(self.PathLike, os.PathLike)) - self.assertTrue(isinstance(self.PathLike(), os.PathLike)) + self.assertEqual('#feelthegil', self.fspath(_PathLike('#feelthegil'))) + self.assertTrue(issubclass(_PathLike, os.PathLike)) + self.assertTrue(isinstance(_PathLike(), os.PathLike)) def test_garbage_in_exception_out(self): vapor = type('blah', (), {}) @@ -3245,14 +3237,14 @@ class TestPEP519(unittest.TestCase): def test_bad_pathlike(self): # __fspath__ returns a value other than str or bytes. - self.assertRaises(TypeError, self.fspath, self.PathLike(42)) + self.assertRaises(TypeError, self.fspath, _PathLike(42)) # __fspath__ attribute that is not callable. c = type('foo', (), {}) c.__fspath__ = 1 self.assertRaises(TypeError, self.fspath, c()) # __fspath__ raises an exception. self.assertRaises(ZeroDivisionError, self.fspath, - self.PathLike(ZeroDivisionError())) + _PathLike(ZeroDivisionError())) # Only test if the C version is provided, otherwise TestPEP519 already tested # the pure Python implementation. -- cgit v1.2.1 From b0d817f3e501393694cb1b6ffd47c60195578ce1 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 6 Sep 2016 16:18:52 -0700 Subject: Add os.getrandom() Issue #27778: Expose the Linux getrandom() syscall as a new os.getrandom() function. This change is part of the PEP 524. --- Lib/test/test_os.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index d79a32fc71..52056725ce 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -1248,6 +1248,7 @@ class URandomTests(unittest.TestCase): def test_urandom_value(self): data1 = os.urandom(16) + self.assertIsInstance(data1, bytes) data2 = os.urandom(16) self.assertNotEqual(data1, data2) @@ -1268,6 +1269,37 @@ class URandomTests(unittest.TestCase): self.assertNotEqual(data1, data2) +@unittest.skipUnless(hasattr(os, 'getrandom'), 'need os.getrandom()') +class GetRandomTests(unittest.TestCase): + def test_getrandom_type(self): + data = os.getrandom(16) + self.assertIsInstance(data, bytes) + self.assertEqual(len(data), 16) + + def test_getrandom0(self): + empty = os.getrandom(0) + self.assertEqual(empty, b'') + + def test_getrandom_random(self): + self.assertTrue(hasattr(os, 'GRND_RANDOM')) + + # Don't test os.getrandom(1, os.GRND_RANDOM) to not consume the rare + # resource /dev/random + + def test_getrandom_nonblock(self): + # The call must not fail. Check also that the flag exists + try: + os.getrandom(1, os.GRND_NONBLOCK) + except BlockingIOError: + # System urandom is not initialized yet + pass + + def test_getrandom_value(self): + data1 = os.getrandom(16) + data2 = os.getrandom(16) + self.assertNotEqual(data1, data2) + + # os.urandom() doesn't use a file descriptor when it is implemented with the # getentropy() function, the getrandom() function or the getrandom() syscall OS_URANDOM_DONT_USE_FD = ( -- cgit v1.2.1 From 7907df3f3ca86f01f3d1d7f619b042e20770249c Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Tue, 6 Sep 2016 16:20:46 -0700 Subject: Issue #27974: Remove importlib._bootstrap._ManageReload. Class was dead code. Thanks to Xiang Zhang for the patch. --- Lib/importlib/_bootstrap.py | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'Lib') diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py index 11df7060c1..2eeafe1dfb 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -36,23 +36,6 @@ def _new_module(name): return type(sys)(name) -class _ManageReload: - - """Manages the possible clean-up of sys.modules for load_module().""" - - def __init__(self, name): - self._name = name - - def __enter__(self): - self._is_reload = self._name in sys.modules - - def __exit__(self, *args): - if any(arg is not None for arg in args) and not self._is_reload: - try: - del sys.modules[self._name] - except KeyError: - pass - # Module-level locking ######################################################## # A dict mapping module names to weakrefs of _ModuleLock instances -- cgit v1.2.1 From f746e54b9ce055f56ae28b16af6b0eef27f34d79 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Wed, 7 Sep 2016 01:21:14 +0200 Subject: Download hashlib test vectors from pythontest.net --- Lib/test/test_hashlib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py index e6df09cbf6..e94fc3f1e8 100644 --- a/Lib/test/test_hashlib.py +++ b/Lib/test/test_hashlib.py @@ -44,7 +44,7 @@ def hexstr(s): return r -URL = "https://raw.githubusercontent.com/tiran/python_vectors/master/{}.txt" +URL = "http://www.pythontest.net/hashlib/{}.txt" def read_vectors(hash_name): with support.open_urlresource(URL.format(hash_name)) as f: -- cgit v1.2.1 From 87da9f01c3bc74aebedc52a91aa908ddae83a579 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 6 Sep 2016 16:33:52 -0700 Subject: os.urandom() now blocks on Linux Issue #27776: The os.urandom() function does now block on Linux 3.17 and newer until the system urandom entropy pool is initialized to increase the security. This change is part of the PEP 524. --- Lib/random.py | 9 --------- 1 file changed, 9 deletions(-) (limited to 'Lib') diff --git a/Lib/random.py b/Lib/random.py index 5abcdbcf22..82f6013b1f 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -105,15 +105,6 @@ class Random(_random.Random): """ - if a is None: - try: - # Seed with enough bytes to span the 19937 bit - # state space for the Mersenne Twister - a = int.from_bytes(_urandom(2500), 'big') - except NotImplementedError: - import time - a = int(time.time() * 256) # use fractional seconds - if version == 1 and isinstance(a, (str, bytes)): x = ord(a[0]) << 7 if a else 0 for c in a: -- cgit v1.2.1 From b5a0dafe44e3794f6c9ce54a7068d56a57a81bd5 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 6 Sep 2016 17:15:29 -0700 Subject: Issue #18844: Add random.weighted_choices() --- Lib/random.py | 28 +++++++++++++++++++- Lib/test/test_random.py | 68 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/random.py b/Lib/random.py index 82f6013b1f..136395e938 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -8,6 +8,7 @@ --------- pick random element pick random sample + pick weighted random sample generate random permutation distributions on the real line: @@ -43,12 +44,14 @@ from math import sqrt as _sqrt, acos as _acos, cos as _cos, sin as _sin from os import urandom as _urandom from _collections_abc import Set as _Set, Sequence as _Sequence from hashlib import sha512 as _sha512 +import itertools as _itertools +import bisect as _bisect __all__ = ["Random","seed","random","uniform","randint","choice","sample", "randrange","shuffle","normalvariate","lognormvariate", "expovariate","vonmisesvariate","gammavariate","triangular", "gauss","betavariate","paretovariate","weibullvariate", - "getstate","setstate", "getrandbits", + "getstate","setstate", "getrandbits", "weighted_choices", "SystemRandom"] NV_MAGICCONST = 4 * _exp(-0.5)/_sqrt(2.0) @@ -334,6 +337,28 @@ class Random(_random.Random): result[i] = population[j] return result + def weighted_choices(self, k, population, weights=None, *, cum_weights=None): + """Return a k sized list of population elements chosen with replacement. + + If the relative weights or cumulative weights are not specified, + the selections are made with equal probability. + + """ + if cum_weights is None: + if weights is None: + choice = self.choice + return [choice(population) for i in range(k)] + else: + cum_weights = list(_itertools.accumulate(weights)) + elif weights is not None: + raise TypeError('Cannot specify both weights and cumulative_weights') + if len(cum_weights) != len(population): + raise ValueError('The number of weights does not match the population') + bisect = _bisect.bisect + random = self.random + total = cum_weights[-1] + return [population[bisect(cum_weights, random() * total)] for i in range(k)] + ## -------------------- real-valued distributions ------------------- ## -------------------- uniform distribution ------------------- @@ -724,6 +749,7 @@ choice = _inst.choice randrange = _inst.randrange sample = _inst.sample shuffle = _inst.shuffle +weighted_choices = _inst.weighted_choices normalvariate = _inst.normalvariate lognormvariate = _inst.lognormvariate expovariate = _inst.expovariate diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py index e80ed17a8c..b3741a8845 100644 --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -7,6 +7,7 @@ import warnings from functools import partial from math import log, exp, pi, fsum, sin from test import support +from fractions import Fraction class TestBasicOps: # Superclass with tests common to all generators. @@ -141,6 +142,73 @@ class TestBasicOps: def test_sample_on_dicts(self): self.assertRaises(TypeError, self.gen.sample, dict.fromkeys('abcdef'), 2) + def test_weighted_choices(self): + weighted_choices = self.gen.weighted_choices + data = ['red', 'green', 'blue', 'yellow'] + str_data = 'abcd' + range_data = range(4) + set_data = set(range(4)) + + # basic functionality + for sample in [ + weighted_choices(5, data), + weighted_choices(5, data, range(4)), + weighted_choices(k=5, population=data, weights=range(4)), + weighted_choices(k=5, population=data, cum_weights=range(4)), + ]: + self.assertEqual(len(sample), 5) + self.assertEqual(type(sample), list) + self.assertTrue(set(sample) <= set(data)) + + # test argument handling + with self.assertRaises(TypeError): # missing arguments + weighted_choices(2) + + self.assertEqual(weighted_choices(0, data), []) # k == 0 + self.assertEqual(weighted_choices(-1, data), []) # negative k behaves like ``[0] * -1`` + with self.assertRaises(TypeError): + weighted_choices(2.5, data) # k is a float + + self.assertTrue(set(weighted_choices(5, str_data)) <= set(str_data)) # population is a string sequence + self.assertTrue(set(weighted_choices(5, range_data)) <= set(range_data)) # population is a range + with self.assertRaises(TypeError): + weighted_choices(2.5, set_data) # population is not a sequence + + self.assertTrue(set(weighted_choices(5, data, None)) <= set(data)) # weights is None + self.assertTrue(set(weighted_choices(5, data, weights=None)) <= set(data)) + with self.assertRaises(ValueError): + weighted_choices(5, data, [1,2]) # len(weights) != len(population) + with self.assertRaises(IndexError): + weighted_choices(5, data, [0]*4) # weights sum to zero + with self.assertRaises(TypeError): + weighted_choices(5, data, 10) # non-iterable weights + with self.assertRaises(TypeError): + weighted_choices(5, data, [None]*4) # non-numeric weights + for weights in [ + [15, 10, 25, 30], # integer weights + [15.1, 10.2, 25.2, 30.3], # float weights + [Fraction(1, 3), Fraction(2, 6), Fraction(3, 6), Fraction(4, 6)], # fractional weights + [True, False, True, False] # booleans (include / exclude) + ]: + self.assertTrue(set(weighted_choices(5, data, weights)) <= set(data)) + + with self.assertRaises(ValueError): + weighted_choices(5, data, cum_weights=[1,2]) # len(weights) != len(population) + with self.assertRaises(IndexError): + weighted_choices(5, data, cum_weights=[0]*4) # cum_weights sum to zero + with self.assertRaises(TypeError): + weighted_choices(5, data, cum_weights=10) # non-iterable cum_weights + with self.assertRaises(TypeError): + weighted_choices(5, data, cum_weights=[None]*4) # non-numeric cum_weights + with self.assertRaises(TypeError): + weighted_choices(5, data, range(4), cum_weights=range(4)) # both weights and cum_weights + for weights in [ + [15, 10, 25, 30], # integer cum_weights + [15.1, 10.2, 25.2, 30.3], # float cum_weights + [Fraction(1, 3), Fraction(2, 6), Fraction(3, 6), Fraction(4, 6)], # fractional cum_weights + ]: + self.assertTrue(set(weighted_choices(5, data, cum_weights=weights)) <= set(data)) + def test_gauss(self): # Ensure that the seed() method initializes all the hidden state. In # particular, through 2.2.1 it failed to reset a piece of state used -- cgit v1.2.1 From 65b3c4590c3d98982f9c34b660c6b8b7f6b60467 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Wed, 7 Sep 2016 00:22:22 +0000 Subject: Correct a comment in the test referencing the wrong issue number (issue3100 is correct, not 3110). --- Lib/test/test_weakref.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index 6d50a66d7b..b1476d0a1a 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -911,7 +911,7 @@ class SubclassableWeakrefTestCase(TestBase): self.assertFalse(hasattr(r, "__dict__")) def test_subclass_refs_with_cycle(self): - # Bug #3110 + """Confirm https://bugs.python.org/issue3100 is fixed.""" # An instance of a weakref subclass can have attributes. # If such a weakref holds the only strong reference to the object, # deleting the weakref will delete the object. In this case, -- cgit v1.2.1 From 44d7015fa936394a0831df01c7050177d219537b Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Wed, 7 Sep 2016 02:35:13 +0200 Subject: Bypass __get_openssl_constructor() and always use our own blake2 implementation --- Lib/hashlib.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'Lib') diff --git a/Lib/hashlib.py b/Lib/hashlib.py index 40ccdec351..2d5e92ea33 100644 --- a/Lib/hashlib.py +++ b/Lib/hashlib.py @@ -101,6 +101,9 @@ def __get_builtin_constructor(name): def __get_openssl_constructor(name): + if name in {'blake2b', 'blake2s'}: + # Prefer our blake2 implementation. + return __get_builtin_constructor(name) try: f = getattr(_hashlib, 'openssl_' + name) # Allow the C module to raise ValueError. The function will be -- cgit v1.2.1 From b643ff43c058dcd50cef902f0b6e16cb53051c7c Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Tue, 6 Sep 2016 19:36:01 -0700 Subject: Issue #27182: Add support for path-like objects to PyUnicode_FSDecoder(). --- Lib/test/test_compile.py | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index 9638e6975a..409ec86c75 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -664,6 +664,16 @@ if 1: self.assertTrue(f1(0)) self.assertTrue(f2(0.0)) + def test_path_like_objects(self): + # An implicit test for PyUnicode_FSDecoder(). + class PathLike: + def __init__(self, path): + self._path = path + def __fspath__(self): + return self._path + + compile("42", PathLike("test_compile_pathlike"), "single") + class TestStackSize(unittest.TestCase): # These tests check that the computed stack size for a code object -- cgit v1.2.1 From 0c5d31799ea0cc8ecf4968187f7c7e6fd3dd3d7f Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Tue, 6 Sep 2016 19:38:15 -0700 Subject: Adds test.support.PGO and skips tests that are not useful for PGO. --- Lib/test/libregrtest/main.py | 2 ++ Lib/test/support/__init__.py | 6 +++++- Lib/test/test_asyncore.py | 3 +++ Lib/test/test_gdb.py | 1 + Lib/test/test_multiprocessing_fork.py | 6 ++++++ Lib/test/test_multiprocessing_forkserver.py | 5 +++++ Lib/test/test_multiprocessing_main_handling.py | 3 +++ Lib/test/test_multiprocessing_spawn.py | 5 +++++ Lib/test/test_subprocess.py | 3 +++ 9 files changed, 33 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index ba9e48b448..dbbf72f1b5 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -473,6 +473,8 @@ class Regrtest: if self.ns.wait: input("Press any key to continue...") + support.PGO = self.ns.pgo + setup_tests(self.ns) self.find_tests(tests) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 4a7cd40a0f..3e2ab43ca4 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -105,7 +105,7 @@ __all__ = [ "check_warnings", "check_no_resource_warning", "EnvironmentVarGuard", "run_with_locale", "swap_item", "swap_attr", "Matcher", "set_memlimit", "SuppressCrashReport", "sortdict", - "run_with_tz", + "run_with_tz", "PGO", ] class Error(Exception): @@ -878,6 +878,10 @@ else: # Save the initial cwd SAVEDCWD = os.getcwd() +# Set by libregrtest/main.py so we can skip tests that are not +# useful for PGO +PGO = False + @contextlib.contextmanager def temp_dir(path=None, quiet=False): """Return a context manager that creates a temporary directory. diff --git a/Lib/test/test_asyncore.py b/Lib/test/test_asyncore.py index 38579168cf..dbee593c54 100644 --- a/Lib/test/test_asyncore.py +++ b/Lib/test/test_asyncore.py @@ -11,6 +11,9 @@ import struct from test import support from io import BytesIO +if support.PGO: + raise unittest.SkipTest("test is not helpful for PGO") + try: import threading except ImportError: diff --git a/Lib/test/test_gdb.py b/Lib/test/test_gdb.py index 09bafbdd93..30b5f16780 100644 --- a/Lib/test/test_gdb.py +++ b/Lib/test/test_gdb.py @@ -110,6 +110,7 @@ HAS_PYUP_PYDOWN = gdb_has_frame_select() BREAKPOINT_FN='builtin_id' +@support.skipIf(support.PGO, "not useful for PGO") class DebuggerTests(unittest.TestCase): """Test that the debugger can debug Python.""" diff --git a/Lib/test/test_multiprocessing_fork.py b/Lib/test/test_multiprocessing_fork.py index 2bf4e75644..9f22500e8f 100644 --- a/Lib/test/test_multiprocessing_fork.py +++ b/Lib/test/test_multiprocessing_fork.py @@ -1,6 +1,12 @@ import unittest import test._test_multiprocessing +from test import support + +if support.PGO: + raise unittest.SkipTest("test is not helpful for PGO") + + test._test_multiprocessing.install_tests_in_module_dict(globals(), 'fork') if __name__ == '__main__': diff --git a/Lib/test/test_multiprocessing_forkserver.py b/Lib/test/test_multiprocessing_forkserver.py index 193a04a5fc..407bb3f9b6 100644 --- a/Lib/test/test_multiprocessing_forkserver.py +++ b/Lib/test/test_multiprocessing_forkserver.py @@ -1,6 +1,11 @@ import unittest import test._test_multiprocessing +from test import support + +if support.PGO: + raise unittest.SkipTest("test is not helpful for PGO") + test._test_multiprocessing.install_tests_in_module_dict(globals(), 'forkserver') if __name__ == '__main__': diff --git a/Lib/test/test_multiprocessing_main_handling.py b/Lib/test/test_multiprocessing_main_handling.py index 2e15cd8a6b..32593dab86 100644 --- a/Lib/test/test_multiprocessing_main_handling.py +++ b/Lib/test/test_multiprocessing_main_handling.py @@ -16,6 +16,9 @@ from test.support.script_helper import ( make_pkg, make_script, make_zip_pkg, make_zip_script, assert_python_ok) +if support.PGO: + raise unittest.SkipTest("test is not helpful for PGO") + # Look up which start methods are available to test import multiprocessing AVAILABLE_START_METHODS = set(multiprocessing.get_all_start_methods()) diff --git a/Lib/test/test_multiprocessing_spawn.py b/Lib/test/test_multiprocessing_spawn.py index 334ae9e8f7..6558952308 100644 --- a/Lib/test/test_multiprocessing_spawn.py +++ b/Lib/test/test_multiprocessing_spawn.py @@ -1,6 +1,11 @@ import unittest import test._test_multiprocessing +from test import support + +if support.PGO: + raise unittest.SkipTest("test is not helpful for PGO") + test._test_multiprocessing.install_tests_in_module_dict(globals(), 'spawn') if __name__ == '__main__': diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 2bfb69cbfe..09066063cb 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -21,6 +21,9 @@ try: except ImportError: threading = None +if support.PGO: + raise unittest.SkipTest("test is not helpful for PGO") + mswindows = (sys.platform == "win32") # -- cgit v1.2.1 From 7bbc40238d3182b2d495024df0e473cd2ab03bce Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Tue, 6 Sep 2016 19:42:27 -0700 Subject: Issue #27959: Adds oem encoding, alias ansi to mbcs, move aliasmbcs to codec lookup --- Lib/encodings/__init__.py | 10 ++++++++ Lib/encodings/aliases.py | 1 + Lib/encodings/oem.py | 41 +++++++++++++++++++++++++++++++ Lib/site.py | 16 ------------ Lib/test/test_codecs.py | 62 ++++++++++++++++++++++------------------------- 5 files changed, 81 insertions(+), 49 deletions(-) create mode 100644 Lib/encodings/oem.py (limited to 'Lib') diff --git a/Lib/encodings/__init__.py b/Lib/encodings/__init__.py index 320011bd0e..9a9b90b004 100644 --- a/Lib/encodings/__init__.py +++ b/Lib/encodings/__init__.py @@ -29,6 +29,7 @@ Written by Marc-Andre Lemburg (mal@lemburg.com). """#" import codecs +import sys from . import aliases _cache = {} @@ -151,3 +152,12 @@ def search_function(encoding): # Register the search_function in the Python codec registry codecs.register(search_function) + +if sys.platform == 'win32': + def _alias_mbcs(encoding): + import _bootlocale + if encoding == _bootlocale.getpreferredencoding(False): + import encodings.mbcs + return encodings.mbcs.getregentry() + + codecs.register(_alias_mbcs) diff --git a/Lib/encodings/aliases.py b/Lib/encodings/aliases.py index 67c828d639..2e63c2f949 100644 --- a/Lib/encodings/aliases.py +++ b/Lib/encodings/aliases.py @@ -458,6 +458,7 @@ aliases = { 'macturkish' : 'mac_turkish', # mbcs codec + 'ansi' : 'mbcs', 'dbcs' : 'mbcs', # ptcp154 codec diff --git a/Lib/encodings/oem.py b/Lib/encodings/oem.py new file mode 100644 index 0000000000..2c3426ba48 --- /dev/null +++ b/Lib/encodings/oem.py @@ -0,0 +1,41 @@ +""" Python 'oem' Codec for Windows + +""" +# Import them explicitly to cause an ImportError +# on non-Windows systems +from codecs import oem_encode, oem_decode +# for IncrementalDecoder, IncrementalEncoder, ... +import codecs + +### Codec APIs + +encode = oem_encode + +def decode(input, errors='strict'): + return oem_decode(input, errors, True) + +class IncrementalEncoder(codecs.IncrementalEncoder): + def encode(self, input, final=False): + return oem_encode(input, self.errors)[0] + +class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + _buffer_decode = oem_decode + +class StreamWriter(codecs.StreamWriter): + encode = oem_encode + +class StreamReader(codecs.StreamReader): + decode = oem_decode + +### encodings module API + +def getregentry(): + return codecs.CodecInfo( + name='oem', + encode=encode, + decode=decode, + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamreader=StreamReader, + streamwriter=StreamWriter, + ) diff --git a/Lib/site.py b/Lib/site.py index a84e3bb7c5..a536ef17ca 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -423,21 +423,6 @@ def enablerlcompleter(): sys.__interactivehook__ = register_readline -def aliasmbcs(): - """On Windows, some default encodings are not provided by Python, - while they are always available as "mbcs" in each locale. Make - them usable by aliasing to "mbcs" in such a case.""" - if sys.platform == 'win32': - import _bootlocale, codecs - enc = _bootlocale.getpreferredencoding(False) - if enc.startswith('cp'): # "cp***" ? - try: - codecs.lookup(enc) - except LookupError: - import encodings - encodings._cache[enc] = encodings._unknown - encodings.aliases.aliases[enc] = 'mbcs' - CONFIG_LINE = r'^(?P(\w|[-_])+)\s*=\s*(?P.*)\s*$' def venv(known_paths): @@ -560,7 +545,6 @@ def main(): setcopyright() sethelper() enablerlcompleter() - aliasmbcs() execsitecustomize() if ENABLE_USER_SITE: execusercustomize() diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index d8753402ef..825a7dd95f 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -8,11 +8,6 @@ import encodings from test import support -if sys.platform == 'win32': - VISTA_OR_LATER = (sys.getwindowsversion().major >= 6) -else: - VISTA_OR_LATER = False - try: import ctypes except ImportError: @@ -841,18 +836,13 @@ class CP65001Test(ReadTest, unittest.TestCase): ('abc', 'strict', b'abc'), ('\xe9\u20ac', 'strict', b'\xc3\xa9\xe2\x82\xac'), ('\U0010ffff', 'strict', b'\xf4\x8f\xbf\xbf'), + ('\udc80', 'strict', None), + ('\udc80', 'ignore', b''), + ('\udc80', 'replace', b'?'), + ('\udc80', 'backslashreplace', b'\\udc80'), + ('\udc80', 'namereplace', b'\\udc80'), + ('\udc80', 'surrogatepass', b'\xed\xb2\x80'), ] - if VISTA_OR_LATER: - tests.extend(( - ('\udc80', 'strict', None), - ('\udc80', 'ignore', b''), - ('\udc80', 'replace', b'?'), - ('\udc80', 'backslashreplace', b'\\udc80'), - ('\udc80', 'namereplace', b'\\udc80'), - ('\udc80', 'surrogatepass', b'\xed\xb2\x80'), - )) - else: - tests.append(('\udc80', 'strict', b'\xed\xb2\x80')) for text, errors, expected in tests: if expected is not None: try: @@ -879,17 +869,10 @@ class CP65001Test(ReadTest, unittest.TestCase): (b'[\xff]', 'ignore', '[]'), (b'[\xff]', 'replace', '[\ufffd]'), (b'[\xff]', 'surrogateescape', '[\udcff]'), + (b'[\xed\xb2\x80]', 'strict', None), + (b'[\xed\xb2\x80]', 'ignore', '[]'), + (b'[\xed\xb2\x80]', 'replace', '[\ufffd\ufffd\ufffd]'), ] - if VISTA_OR_LATER: - tests.extend(( - (b'[\xed\xb2\x80]', 'strict', None), - (b'[\xed\xb2\x80]', 'ignore', '[]'), - (b'[\xed\xb2\x80]', 'replace', '[\ufffd\ufffd\ufffd]'), - )) - else: - tests.extend(( - (b'[\xed\xb2\x80]', 'strict', '[\udc80]'), - )) for raw, errors, expected in tests: if expected is not None: try: @@ -904,7 +887,6 @@ class CP65001Test(ReadTest, unittest.TestCase): self.assertRaises(UnicodeDecodeError, raw.decode, 'cp65001', errors) - @unittest.skipUnless(VISTA_OR_LATER, 'require Windows Vista or later') def test_lone_surrogates(self): self.assertRaises(UnicodeEncodeError, "\ud800".encode, "cp65001") self.assertRaises(UnicodeDecodeError, b"\xed\xa0\x80".decode, "cp65001") @@ -921,7 +903,6 @@ class CP65001Test(ReadTest, unittest.TestCase): self.assertEqual("[\uDC80]".encode("cp65001", "replace"), b'[?]') - @unittest.skipUnless(VISTA_OR_LATER, 'require Windows Vista or later') def test_surrogatepass_handler(self): self.assertEqual("abc\ud800def".encode("cp65001", "surrogatepass"), b"abc\xed\xa0\x80def") @@ -1951,6 +1932,8 @@ all_unicode_encodings = [ if hasattr(codecs, "mbcs_encode"): all_unicode_encodings.append("mbcs") +if hasattr(codecs, "oem_encode"): + all_unicode_encodings.append("oem") # The following encoding is not tested, because it's not supposed # to work: @@ -3119,11 +3102,10 @@ class CodePageTest(unittest.TestCase): (b'\xff\xf4\x8f\xbf\xbf', 'ignore', '\U0010ffff'), (b'\xff\xf4\x8f\xbf\xbf', 'replace', '\ufffd\U0010ffff'), )) - if VISTA_OR_LATER: - self.check_encode(self.CP_UTF8, ( - ('[\U0010ffff\uDC80]', 'ignore', b'[\xf4\x8f\xbf\xbf]'), - ('[\U0010ffff\uDC80]', 'replace', b'[\xf4\x8f\xbf\xbf?]'), - )) + self.check_encode(self.CP_UTF8, ( + ('[\U0010ffff\uDC80]', 'ignore', b'[\xf4\x8f\xbf\xbf]'), + ('[\U0010ffff\uDC80]', 'replace', b'[\xf4\x8f\xbf\xbf?]'), + )) def test_incremental(self): decoded = codecs.code_page_decode(932, b'\x82', 'strict', False) @@ -3144,6 +3126,20 @@ class CodePageTest(unittest.TestCase): False) self.assertEqual(decoded, ('abc', 3)) + def test_mbcs_alias(self): + # Check that looking up our 'default' codepage will return + # mbcs when we don't have a more specific one available + import _bootlocale + def _get_fake_codepage(*a): + return 'cp123' + old_getpreferredencoding = _bootlocale.getpreferredencoding + _bootlocale.getpreferredencoding = _get_fake_codepage + try: + codec = codecs.lookup('cp123') + self.assertEqual(codec.name, 'mbcs') + finally: + _bootlocale.getpreferredencoding = old_getpreferredencoding + class ASCIITest(unittest.TestCase): def test_encode(self): -- cgit v1.2.1 From d7d2d1dcb6cd6f41e5023525be3e264dd08e1422 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 6 Sep 2016 19:57:40 -0700 Subject: Fix test_os.GetRandomTests() Issue #27778: Skip getrandom() tests if getrandom() fails with ENOSYS. --- Lib/test/test_os.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 52056725ce..2de94c643d 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -1271,6 +1271,18 @@ class URandomTests(unittest.TestCase): @unittest.skipUnless(hasattr(os, 'getrandom'), 'need os.getrandom()') class GetRandomTests(unittest.TestCase): + @classmethod + def setUpClass(cls): + try: + os.getrandom(1) + except OSError as exc: + if exc.errno == errno.ENOSYS: + # Python compiled on a more recent Linux version + # than the current Linux kernel + raise unittest.SkipTest("getrandom() syscall fails with ENOSYS") + else: + raise + def test_getrandom_type(self): data = os.getrandom(16) self.assertIsInstance(data, bytes) -- cgit v1.2.1 From b54005a701444260deeb4b2880baa17d9a514eb1 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Tue, 6 Sep 2016 20:16:17 -0700 Subject: Issue #6135: Adds encoding and errors parameters to subprocess --- Lib/subprocess.py | 87 +++++++++++++++++++++++---------------------- Lib/test/test_subprocess.py | 59 ++++++++++++++++++++---------- 2 files changed, 86 insertions(+), 60 deletions(-) (limited to 'Lib') diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 3dea089e6a..9df9318245 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -30,7 +30,8 @@ class Popen(args, bufsize=-1, executable=None, preexec_fn=None, close_fds=True, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0, - restore_signals=True, start_new_session=False, pass_fds=()): + restore_signals=True, start_new_session=False, pass_fds=(), + *, encoding=None, errors=None): Arguments are: @@ -104,20 +105,13 @@ in the child process prior to executing the command. If env is not None, it defines the environment variables for the new process. -If universal_newlines is False, the file objects stdin, stdout and stderr -are opened as binary files, and no line ending conversion is done. +If encoding or errors are specified or universal_newlines is True, the file +objects stdout and stderr are opened in text mode. See io.TextIOWrapper for +the interpretation of these parameters are used. -If universal_newlines is True, the file objects stdout and stderr are -opened as a text file, but lines may be terminated by any of '\n', -the Unix end-of-line convention, '\r', the old Macintosh convention or -'\r\n', the Windows convention. All of these external representations -are seen as '\n' by the Python program. Also, the newlines attribute -of the file objects stdout, stdin and stderr are not updated by the -communicate() method. - -In either case, the process being communicated with should start up -expecting to receive bytes on its standard input and decode them with -the same encoding they are sent in. +If no encoding is specified and universal_newlines is False, the file +objects stdin, stdout and stderr are opened as binary files, and no +line ending conversion is done. The startupinfo and creationflags, if given, will be passed to the underlying CreateProcess() function. They can specify things such as @@ -234,11 +228,8 @@ communicate(input=None) and stderr, until end-of-file is reached. Wait for process to terminate. The optional input argument should be data to be sent to the child process, or None, if no data should be sent to - the child. If the Popen instance was constructed with universal_newlines - set to True, the input argument should be a string and will be encoded - using the preferred system encoding (see locale.getpreferredencoding); - if universal_newlines is False, the input argument should be a - byte string. + the child. If the Popen instance was constructed in text mode, the + input argument should be a string. Otherwise, it should be bytes. communicate() returns a tuple (stdout, stderr). @@ -808,8 +799,8 @@ def getstatusoutput(cmd): """ Return (status, output) of executing cmd in a shell. Execute the string 'cmd' in a shell with 'check_output' and - return a 2-tuple (status, output). Universal newlines mode is used, - meaning that the result with be decoded to a string. + return a 2-tuple (status, output). The locale encoding is used + to decode the output and process newlines. A trailing newline is stripped from the output. The exit status for the command can be interpreted @@ -859,7 +850,7 @@ class Popen(object): shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0, restore_signals=True, start_new_session=False, - pass_fds=()): + pass_fds=(), *, encoding=None, errors=None): """Create new Popen instance.""" _cleanup() # Held while anything is calling waitpid before returncode has been @@ -912,6 +903,8 @@ class Popen(object): self.pid = None self.returncode = None self.universal_newlines = universal_newlines + self.encoding = encoding + self.errors = errors # Input and output objects. The general principle is like # this: @@ -944,22 +937,28 @@ class Popen(object): if errread != -1: errread = msvcrt.open_osfhandle(errread.Detach(), 0) - if p2cwrite != -1: - self.stdin = io.open(p2cwrite, 'wb', bufsize) - if universal_newlines: - self.stdin = io.TextIOWrapper(self.stdin, write_through=True, - line_buffering=(bufsize == 1)) - if c2pread != -1: - self.stdout = io.open(c2pread, 'rb', bufsize) - if universal_newlines: - self.stdout = io.TextIOWrapper(self.stdout) - if errread != -1: - self.stderr = io.open(errread, 'rb', bufsize) - if universal_newlines: - self.stderr = io.TextIOWrapper(self.stderr) + text_mode = encoding or errors or universal_newlines self._closed_child_pipe_fds = False + try: + if p2cwrite != -1: + self.stdin = io.open(p2cwrite, 'wb', bufsize) + if text_mode: + self.stdin = io.TextIOWrapper(self.stdin, write_through=True, + line_buffering=(bufsize == 1), + encoding=encoding, errors=errors) + if c2pread != -1: + self.stdout = io.open(c2pread, 'rb', bufsize) + if text_mode: + self.stdout = io.TextIOWrapper(self.stdout, + encoding=encoding, errors=errors) + if errread != -1: + self.stderr = io.open(errread, 'rb', bufsize) + if text_mode: + self.stderr = io.TextIOWrapper(self.stderr, + encoding=encoding, errors=errors) + self._execute_child(args, executable, preexec_fn, close_fds, pass_fds, cwd, env, startupinfo, creationflags, shell, @@ -993,8 +992,8 @@ class Popen(object): raise - def _translate_newlines(self, data, encoding): - data = data.decode(encoding) + def _translate_newlines(self, data, encoding, errors): + data = data.decode(encoding, errors) return data.replace("\r\n", "\n").replace("\r", "\n") def __enter__(self): @@ -1779,13 +1778,15 @@ class Popen(object): # Translate newlines, if requested. # This also turns bytes into strings. - if self.universal_newlines: + if self.encoding or self.errors or self.universal_newlines: if stdout is not None: stdout = self._translate_newlines(stdout, - self.stdout.encoding) + self.stdout.encoding, + self.stdout.errors) if stderr is not None: stderr = self._translate_newlines(stderr, - self.stderr.encoding) + self.stderr.encoding, + self.stderr.errors) return (stdout, stderr) @@ -1797,8 +1798,10 @@ class Popen(object): if self.stdin and self._input is None: self._input_offset = 0 self._input = input - if self.universal_newlines and input is not None: - self._input = self._input.encode(self.stdin.encoding) + if input is not None and ( + self.encoding or self.errors or self.universal_newlines): + self._input = self._input.encode(self.stdin.encoding, + self.stdin.errors) def send_signal(self, sig): diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 09066063cb..2d9239c759 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -894,31 +894,42 @@ class ProcessTestCase(BaseTestCase): # # UTF-16 and UTF-32-BE are sufficient to check both with BOM and # without, and UTF-16 and UTF-32. - import _bootlocale for encoding in ['utf-16', 'utf-32-be']: - old_getpreferredencoding = _bootlocale.getpreferredencoding - # Indirectly via io.TextIOWrapper, Popen() defaults to - # locale.getpreferredencoding(False) and earlier in Python 3.2 to - # locale.getpreferredencoding(). - def getpreferredencoding(do_setlocale=True): - return encoding code = ("import sys; " r"sys.stdout.buffer.write('1\r\n2\r3\n4'.encode('%s'))" % encoding) args = [sys.executable, '-c', code] - try: - _bootlocale.getpreferredencoding = getpreferredencoding - # We set stdin to be non-None because, as of this writing, - # a different code path is used when the number of pipes is - # zero or one. - popen = subprocess.Popen(args, universal_newlines=True, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE) - stdout, stderr = popen.communicate(input='') - finally: - _bootlocale.getpreferredencoding = old_getpreferredencoding + # We set stdin to be non-None because, as of this writing, + # a different code path is used when the number of pipes is + # zero or one. + popen = subprocess.Popen(args, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + encoding=encoding) + stdout, stderr = popen.communicate(input='') self.assertEqual(stdout, '1\n2\n3\n4') + def test_communicate_errors(self): + for errors, expected in [ + ('ignore', ''), + ('replace', '\ufffd\ufffd'), + ('surrogateescape', '\udc80\udc80'), + ('backslashreplace', '\\x80\\x80'), + ]: + code = ("import sys; " + r"sys.stdout.buffer.write(b'[\x80\x80]')") + args = [sys.executable, '-c', code] + # We set stdin to be non-None because, as of this writing, + # a different code path is used when the number of pipes is + # zero or one. + popen = subprocess.Popen(args, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + encoding='utf-8', + errors=errors) + stdout, stderr = popen.communicate(input='') + self.assertEqual(stdout, '[{}]'.format(expected)) + def test_no_leaking(self): # Make sure we leak no resources if not mswindows: @@ -2539,6 +2550,18 @@ class Win32ProcessTestCase(BaseTestCase): with p: self.assertIn(b"physalis", p.stdout.read()) + def test_shell_encodings(self): + # Run command through the shell (string) + for enc in ['ansi', 'oem']: + newenv = os.environ.copy() + newenv["FRUIT"] = "physalis" + p = subprocess.Popen("set", shell=1, + stdout=subprocess.PIPE, + env=newenv, + encoding=enc) + with p: + self.assertIn("physalis", p.stdout.read(), enc) + def test_call_string(self): # call() function with string argument on Windows rc = subprocess.call(sys.executable + -- cgit v1.2.1 From 25789bae5ee6d0a67658a40f0561906148b8707b Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Tue, 6 Sep 2016 20:22:41 -0700 Subject: get skipIf from the right place --- Lib/test/test_gdb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_gdb.py b/Lib/test/test_gdb.py index 30b5f16780..382c5d009c 100644 --- a/Lib/test/test_gdb.py +++ b/Lib/test/test_gdb.py @@ -110,7 +110,7 @@ HAS_PYUP_PYDOWN = gdb_has_frame_select() BREAKPOINT_FN='builtin_id' -@support.skipIf(support.PGO, "not useful for PGO") +@unittest.skipIf(support.PGO, "not useful for PGO") class DebuggerTests(unittest.TestCase): """Test that the debugger can debug Python.""" -- cgit v1.2.1 From 2e097fe5ef1f64508814e5aa89534dc856768e8f Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 7 Sep 2016 09:49:42 +0300 Subject: Issue #25596: Falls back to listdir in glob for bytes paths on Windows. --- Lib/glob.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) (limited to 'Lib') diff --git a/Lib/glob.py b/Lib/glob.py index 002cd92019..7c3cccb740 100644 --- a/Lib/glob.py +++ b/Lib/glob.py @@ -118,13 +118,22 @@ def _iterdir(dirname, dironly): else: dirname = os.curdir try: - with os.scandir(dirname) as it: - for entry in it: - try: - if not dironly or entry.is_dir(): - yield entry.name - except OSError: - pass + if os.name == 'nt' and isinstance(dirname, bytes): + names = os.listdir(dirname) + if dironly: + for name in names: + if os.path.isdir(os.path.join(dirname, name)): + yield name + else: + yield from names + else: + with os.scandir(dirname) as it: + for entry in it: + try: + if not dironly or entry.is_dir(): + yield entry.name + except OSError: + pass except OSError: return -- cgit v1.2.1 From d0d61a98c4926f12d0dbe0b6fb5f17f9bff0fd64 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Wed, 7 Sep 2016 00:08:44 -0700 Subject: Rename weighted_choices() to just choices() --- Lib/random.py | 6 +++--- Lib/test/test_random.py | 54 ++++++++++++++++++++++++------------------------- 2 files changed, 30 insertions(+), 30 deletions(-) (limited to 'Lib') diff --git a/Lib/random.py b/Lib/random.py index 136395e938..cd8583fe4a 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -51,7 +51,7 @@ __all__ = ["Random","seed","random","uniform","randint","choice","sample", "randrange","shuffle","normalvariate","lognormvariate", "expovariate","vonmisesvariate","gammavariate","triangular", "gauss","betavariate","paretovariate","weibullvariate", - "getstate","setstate", "getrandbits", "weighted_choices", + "getstate","setstate", "getrandbits", "choices", "SystemRandom"] NV_MAGICCONST = 4 * _exp(-0.5)/_sqrt(2.0) @@ -337,7 +337,7 @@ class Random(_random.Random): result[i] = population[j] return result - def weighted_choices(self, k, population, weights=None, *, cum_weights=None): + def choices(self, k, population, weights=None, *, cum_weights=None): """Return a k sized list of population elements chosen with replacement. If the relative weights or cumulative weights are not specified, @@ -749,7 +749,7 @@ choice = _inst.choice randrange = _inst.randrange sample = _inst.sample shuffle = _inst.shuffle -weighted_choices = _inst.weighted_choices +choices = _inst.choices normalvariate = _inst.normalvariate lognormvariate = _inst.lognormvariate expovariate = _inst.expovariate diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py index b3741a8845..9c1383d7db 100644 --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -142,8 +142,8 @@ class TestBasicOps: def test_sample_on_dicts(self): self.assertRaises(TypeError, self.gen.sample, dict.fromkeys('abcdef'), 2) - def test_weighted_choices(self): - weighted_choices = self.gen.weighted_choices + def test_choices(self): + choices = self.gen.choices data = ['red', 'green', 'blue', 'yellow'] str_data = 'abcd' range_data = range(4) @@ -151,63 +151,63 @@ class TestBasicOps: # basic functionality for sample in [ - weighted_choices(5, data), - weighted_choices(5, data, range(4)), - weighted_choices(k=5, population=data, weights=range(4)), - weighted_choices(k=5, population=data, cum_weights=range(4)), + choices(5, data), + choices(5, data, range(4)), + choices(k=5, population=data, weights=range(4)), + choices(k=5, population=data, cum_weights=range(4)), ]: self.assertEqual(len(sample), 5) self.assertEqual(type(sample), list) self.assertTrue(set(sample) <= set(data)) # test argument handling - with self.assertRaises(TypeError): # missing arguments - weighted_choices(2) + with self.assertRaises(TypeError): # missing arguments + choices(2) - self.assertEqual(weighted_choices(0, data), []) # k == 0 - self.assertEqual(weighted_choices(-1, data), []) # negative k behaves like ``[0] * -1`` + self.assertEqual(choices(0, data), []) # k == 0 + self.assertEqual(choices(-1, data), []) # negative k behaves like ``[0] * -1`` with self.assertRaises(TypeError): - weighted_choices(2.5, data) # k is a float + choices(2.5, data) # k is a float - self.assertTrue(set(weighted_choices(5, str_data)) <= set(str_data)) # population is a string sequence - self.assertTrue(set(weighted_choices(5, range_data)) <= set(range_data)) # population is a range + self.assertTrue(set(choices(5, str_data)) <= set(str_data)) # population is a string sequence + self.assertTrue(set(choices(5, range_data)) <= set(range_data)) # population is a range with self.assertRaises(TypeError): - weighted_choices(2.5, set_data) # population is not a sequence + choices(2.5, set_data) # population is not a sequence - self.assertTrue(set(weighted_choices(5, data, None)) <= set(data)) # weights is None - self.assertTrue(set(weighted_choices(5, data, weights=None)) <= set(data)) + self.assertTrue(set(choices(5, data, None)) <= set(data)) # weights is None + self.assertTrue(set(choices(5, data, weights=None)) <= set(data)) with self.assertRaises(ValueError): - weighted_choices(5, data, [1,2]) # len(weights) != len(population) + choices(5, data, [1,2]) # len(weights) != len(population) with self.assertRaises(IndexError): - weighted_choices(5, data, [0]*4) # weights sum to zero + choices(5, data, [0]*4) # weights sum to zero with self.assertRaises(TypeError): - weighted_choices(5, data, 10) # non-iterable weights + choices(5, data, 10) # non-iterable weights with self.assertRaises(TypeError): - weighted_choices(5, data, [None]*4) # non-numeric weights + choices(5, data, [None]*4) # non-numeric weights for weights in [ [15, 10, 25, 30], # integer weights [15.1, 10.2, 25.2, 30.3], # float weights [Fraction(1, 3), Fraction(2, 6), Fraction(3, 6), Fraction(4, 6)], # fractional weights [True, False, True, False] # booleans (include / exclude) ]: - self.assertTrue(set(weighted_choices(5, data, weights)) <= set(data)) + self.assertTrue(set(choices(5, data, weights)) <= set(data)) with self.assertRaises(ValueError): - weighted_choices(5, data, cum_weights=[1,2]) # len(weights) != len(population) + choices(5, data, cum_weights=[1,2]) # len(weights) != len(population) with self.assertRaises(IndexError): - weighted_choices(5, data, cum_weights=[0]*4) # cum_weights sum to zero + choices(5, data, cum_weights=[0]*4) # cum_weights sum to zero with self.assertRaises(TypeError): - weighted_choices(5, data, cum_weights=10) # non-iterable cum_weights + choices(5, data, cum_weights=10) # non-iterable cum_weights with self.assertRaises(TypeError): - weighted_choices(5, data, cum_weights=[None]*4) # non-numeric cum_weights + choices(5, data, cum_weights=[None]*4) # non-numeric cum_weights with self.assertRaises(TypeError): - weighted_choices(5, data, range(4), cum_weights=range(4)) # both weights and cum_weights + choices(5, data, range(4), cum_weights=range(4)) # both weights and cum_weights for weights in [ [15, 10, 25, 30], # integer cum_weights [15.1, 10.2, 25.2, 30.3], # float cum_weights [Fraction(1, 3), Fraction(2, 6), Fraction(3, 6), Fraction(4, 6)], # fractional cum_weights ]: - self.assertTrue(set(weighted_choices(5, data, cum_weights=weights)) <= set(data)) + self.assertTrue(set(choices(5, data, cum_weights=weights)) <= set(data)) def test_gauss(self): # Ensure that the seed() method initializes all the hidden state. In -- cgit v1.2.1 From 0e4eb4663be86749f50811ca6be28efbecfbeed7 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 7 Sep 2016 10:58:05 +0300 Subject: Issue #26032: Optimized globbing in pathlib by using os.scandir(); it is now about 1.5--4 times faster. --- Lib/pathlib.py | 94 ++++++++++++++++++++++++---------------------------------- 1 file changed, 39 insertions(+), 55 deletions(-) (limited to 'Lib') diff --git a/Lib/pathlib.py b/Lib/pathlib.py index a06676fbe4..1b5ab387a6 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -385,6 +385,8 @@ class _NormalAccessor(_Accessor): listdir = _wrap_strfunc(os.listdir) + scandir = _wrap_strfunc(os.scandir) + chmod = _wrap_strfunc(os.chmod) if hasattr(os, "lchmod"): @@ -429,25 +431,6 @@ _normal_accessor = _NormalAccessor() # Globbing helpers # -@contextmanager -def _cached(func): - try: - func.__cached__ - yield func - except AttributeError: - cache = {} - def wrapper(*args): - try: - return cache[args] - except KeyError: - value = cache[args] = func(*args) - return value - wrapper.__cached__ = True - try: - yield wrapper - finally: - cache.clear() - def _make_selector(pattern_parts): pat = pattern_parts[0] child_parts = pattern_parts[1:] @@ -473,8 +456,10 @@ class _Selector: self.child_parts = child_parts if child_parts: self.successor = _make_selector(child_parts) + self.dironly = True else: self.successor = _TerminatingSelector() + self.dironly = False def select_from(self, parent_path): """Iterate over all child paths of `parent_path` matched by this @@ -482,13 +467,15 @@ class _Selector: path_cls = type(parent_path) is_dir = path_cls.is_dir exists = path_cls.exists - listdir = parent_path._accessor.listdir - return self._select_from(parent_path, is_dir, exists, listdir) + scandir = parent_path._accessor.scandir + if not is_dir(parent_path): + return iter([]) + return self._select_from(parent_path, is_dir, exists, scandir) class _TerminatingSelector: - def _select_from(self, parent_path, is_dir, exists, listdir): + def _select_from(self, parent_path, is_dir, exists, scandir): yield parent_path @@ -498,13 +485,11 @@ class _PreciseSelector(_Selector): self.name = name _Selector.__init__(self, child_parts) - def _select_from(self, parent_path, is_dir, exists, listdir): + def _select_from(self, parent_path, is_dir, exists, scandir): try: - if not is_dir(parent_path): - return path = parent_path._make_child_relpath(self.name) - if exists(path): - for p in self.successor._select_from(path, is_dir, exists, listdir): + if (is_dir if self.dironly else exists)(path): + for p in self.successor._select_from(path, is_dir, exists, scandir): yield p except PermissionError: return @@ -516,17 +501,18 @@ class _WildcardSelector(_Selector): self.pat = re.compile(fnmatch.translate(pat)) _Selector.__init__(self, child_parts) - def _select_from(self, parent_path, is_dir, exists, listdir): + def _select_from(self, parent_path, is_dir, exists, scandir): try: - if not is_dir(parent_path): - return cf = parent_path._flavour.casefold - for name in listdir(parent_path): - casefolded = cf(name) - if self.pat.match(casefolded): - path = parent_path._make_child_relpath(name) - for p in self.successor._select_from(path, is_dir, exists, listdir): - yield p + entries = list(scandir(parent_path)) + for entry in entries: + if not self.dironly or entry.is_dir(): + name = entry.name + casefolded = cf(name) + if self.pat.match(casefolded): + path = parent_path._make_child_relpath(name) + for p in self.successor._select_from(path, is_dir, exists, scandir): + yield p except PermissionError: return @@ -537,32 +523,30 @@ class _RecursiveWildcardSelector(_Selector): def __init__(self, pat, child_parts): _Selector.__init__(self, child_parts) - def _iterate_directories(self, parent_path, is_dir, listdir): + def _iterate_directories(self, parent_path, is_dir, scandir): yield parent_path try: - for name in listdir(parent_path): - path = parent_path._make_child_relpath(name) - if is_dir(path) and not path.is_symlink(): - for p in self._iterate_directories(path, is_dir, listdir): + entries = list(scandir(parent_path)) + for entry in entries: + if entry.is_dir() and not entry.is_symlink(): + path = parent_path._make_child_relpath(entry.name) + for p in self._iterate_directories(path, is_dir, scandir): yield p except PermissionError: return - def _select_from(self, parent_path, is_dir, exists, listdir): + def _select_from(self, parent_path, is_dir, exists, scandir): try: - if not is_dir(parent_path): - return - with _cached(listdir) as listdir: - yielded = set() - try: - successor_select = self.successor._select_from - for starting_point in self._iterate_directories(parent_path, is_dir, listdir): - for p in successor_select(starting_point, is_dir, exists, listdir): - if p not in yielded: - yield p - yielded.add(p) - finally: - yielded.clear() + yielded = set() + try: + successor_select = self.successor._select_from + for starting_point in self._iterate_directories(parent_path, is_dir, scandir): + for p in successor_select(starting_point, is_dir, exists, scandir): + if p not in yielded: + yield p + yielded.add(p) + finally: + yielded.clear() except PermissionError: return -- cgit v1.2.1 From 1f47f54b74c6838a93b43b485a290c7463f2531b Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Wed, 7 Sep 2016 11:58:24 +0200 Subject: Issue #16113: Add SHA-3 and SHAKE support to hashlib module. --- Lib/hashlib.py | 17 ++++- Lib/test/test_hashlib.py | 157 ++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 156 insertions(+), 18 deletions(-) (limited to 'Lib') diff --git a/Lib/hashlib.py b/Lib/hashlib.py index 2d5e92ea33..053a7add45 100644 --- a/Lib/hashlib.py +++ b/Lib/hashlib.py @@ -11,7 +11,8 @@ new(name, data=b'', **kwargs) - returns a new hash object implementing the Named constructor functions are also available, these are faster than using new(name): -md5(), sha1(), sha224(), sha256(), sha384(), sha512(), blake2b(), and blake2s() +md5(), sha1(), sha224(), sha256(), sha384(), sha512(), blake2b(), blake2s(), +sha3_224, sha3_256, sha3_384, sha3_512, shake_128, and shake_256. More algorithms may be available on your platform but the above are guaranteed to exist. See the algorithms_guaranteed and algorithms_available attributes @@ -55,7 +56,10 @@ More condensed: # This tuple and __get_builtin_constructor() must be modified if a new # always available algorithm is added. __always_supported = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', - 'blake2b', 'blake2s') + 'blake2b', 'blake2s', + 'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512', + 'shake_128', 'shake_256') + algorithms_guaranteed = set(__always_supported) algorithms_available = set(__always_supported) @@ -90,6 +94,15 @@ def __get_builtin_constructor(name): import _blake2 cache['blake2b'] = _blake2.blake2b cache['blake2s'] = _blake2.blake2s + elif name in {'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512', + 'shake_128', 'shake_256'}: + import _sha3 + cache['sha3_224'] = _sha3.sha3_224 + cache['sha3_256'] = _sha3.sha3_256 + cache['sha3_384'] = _sha3.sha3_384 + cache['sha3_512'] = _sha3.sha3_512 + cache['shake_128'] = _sha3.shake_128 + cache['shake_256'] = _sha3.shake_256 except ImportError: pass # no extension module, this hash is unsupported. diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py index e94fc3f1e8..21260f6ea6 100644 --- a/Lib/test/test_hashlib.py +++ b/Lib/test/test_hashlib.py @@ -34,6 +34,13 @@ except ImportError: requires_blake2 = unittest.skipUnless(_blake2, 'requires _blake2') +try: + import _sha3 +except ImportError: + _sha3 = None + +requires_sha3 = unittest.skipUnless(_sha3, 'requires _sha3') + def hexstr(s): assert isinstance(s, bytes), repr(s) @@ -61,7 +68,11 @@ class HashLibTestCase(unittest.TestCase): supported_hash_names = ( 'md5', 'MD5', 'sha1', 'SHA1', 'sha224', 'SHA224', 'sha256', 'SHA256', 'sha384', 'SHA384', 'sha512', 'SHA512', - 'blake2b', 'blake2s') + 'blake2b', 'blake2s', + 'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512', + 'shake_128', 'shake_256') + + shakes = {'shake_128', 'shake_256'} # Issue #14693: fallback modules are always compiled under POSIX _warn_on_extension_import = os.name == 'posix' or COMPILED_WITH_PYDEBUG @@ -131,6 +142,15 @@ class HashLibTestCase(unittest.TestCase): add_builtin_constructor('blake2s') add_builtin_constructor('blake2b') + _sha3 = self._conditional_import_module('_sha3') + if _sha3: + add_builtin_constructor('sha3_224') + add_builtin_constructor('sha3_256') + add_builtin_constructor('sha3_384') + add_builtin_constructor('sha3_512') + add_builtin_constructor('shake_128') + add_builtin_constructor('shake_256') + super(HashLibTestCase, self).__init__(*args, **kwargs) @property @@ -142,7 +162,10 @@ class HashLibTestCase(unittest.TestCase): a = array.array("b", range(10)) for cons in self.hash_constructors: c = cons(a) - c.hexdigest() + if c.name in self.shakes: + c.hexdigest(16) + else: + c.hexdigest() def test_algorithms_guaranteed(self): self.assertEqual(hashlib.algorithms_guaranteed, @@ -186,14 +209,21 @@ class HashLibTestCase(unittest.TestCase): def test_hexdigest(self): for cons in self.hash_constructors: h = cons() - self.assertIsInstance(h.digest(), bytes) - self.assertEqual(hexstr(h.digest()), h.hexdigest()) + if h.name in self.shakes: + self.assertIsInstance(h.digest(16), bytes) + self.assertEqual(hexstr(h.digest(16)), h.hexdigest(16)) + else: + self.assertIsInstance(h.digest(), bytes) + self.assertEqual(hexstr(h.digest()), h.hexdigest()) def test_name_attribute(self): for cons in self.hash_constructors: h = cons() self.assertIsInstance(h.name, str) - self.assertIn(h.name, self.supported_hash_names) + if h.name in self.supported_hash_names: + self.assertIn(h.name, self.supported_hash_names) + else: + self.assertNotIn(h.name, self.supported_hash_names) self.assertEqual(h.name, hashlib.new(h.name).name) def test_large_update(self): @@ -208,40 +238,46 @@ class HashLibTestCase(unittest.TestCase): m1.update(bees) m1.update(cees) m1.update(dees) + if m1.name in self.shakes: + args = (16,) + else: + args = () m2 = cons() m2.update(aas + bees + cees + dees) - self.assertEqual(m1.digest(), m2.digest()) + self.assertEqual(m1.digest(*args), m2.digest(*args)) m3 = cons(aas + bees + cees + dees) - self.assertEqual(m1.digest(), m3.digest()) + self.assertEqual(m1.digest(*args), m3.digest(*args)) # verify copy() doesn't touch original m4 = cons(aas + bees + cees) - m4_digest = m4.digest() + m4_digest = m4.digest(*args) m4_copy = m4.copy() m4_copy.update(dees) - self.assertEqual(m1.digest(), m4_copy.digest()) - self.assertEqual(m4.digest(), m4_digest) + self.assertEqual(m1.digest(*args), m4_copy.digest(*args)) + self.assertEqual(m4.digest(*args), m4_digest) - def check(self, name, data, hexdigest, **kwargs): + def check(self, name, data, hexdigest, shake=False, **kwargs): + length = len(hexdigest)//2 hexdigest = hexdigest.lower() constructors = self.constructors_to_test[name] # 2 is for hashlib.name(...) and hashlib.new(name, ...) self.assertGreaterEqual(len(constructors), 2) for hash_object_constructor in constructors: m = hash_object_constructor(data, **kwargs) - computed = m.hexdigest() + computed = m.hexdigest() if not shake else m.hexdigest(length) self.assertEqual( computed, hexdigest, "Hash algorithm %s constructed using %s returned hexdigest" " %r for %d byte input data that should have hashed to %r." % (name, hash_object_constructor, computed, len(data), hexdigest)) - computed = m.digest() + computed = m.digest() if not shake else m.digest(length) digest = bytes.fromhex(hexdigest) self.assertEqual(computed, digest) - self.assertEqual(len(digest), m.digest_size) + if not shake: + self.assertEqual(len(digest), m.digest_size) def check_no_unicode(self, algorithm_name): # Unicode objects are not allowed as input. @@ -262,13 +298,30 @@ class HashLibTestCase(unittest.TestCase): self.check_no_unicode('blake2b') self.check_no_unicode('blake2s') - def check_blocksize_name(self, name, block_size=0, digest_size=0): + @requires_sha3 + def test_no_unicode_sha3(self): + self.check_no_unicode('sha3_224') + self.check_no_unicode('sha3_256') + self.check_no_unicode('sha3_384') + self.check_no_unicode('sha3_512') + self.check_no_unicode('shake_128') + self.check_no_unicode('shake_256') + + def check_blocksize_name(self, name, block_size=0, digest_size=0, + digest_length=None): constructors = self.constructors_to_test[name] for hash_object_constructor in constructors: m = hash_object_constructor() self.assertEqual(m.block_size, block_size) self.assertEqual(m.digest_size, digest_size) - self.assertEqual(len(m.digest()), digest_size) + if digest_length: + self.assertEqual(len(m.digest(digest_length)), + digest_length) + self.assertEqual(len(m.hexdigest(digest_length)), + 2*digest_length) + else: + self.assertEqual(len(m.digest()), digest_size) + self.assertEqual(len(m.hexdigest()), 2*digest_size) self.assertEqual(m.name, name) # split for sha3_512 / _sha3.sha3 object self.assertIn(name.split("_")[0], repr(m)) @@ -280,6 +333,12 @@ class HashLibTestCase(unittest.TestCase): self.check_blocksize_name('sha256', 64, 32) self.check_blocksize_name('sha384', 128, 48) self.check_blocksize_name('sha512', 128, 64) + self.check_blocksize_name('sha3_224', 144, 28) + self.check_blocksize_name('sha3_256', 136, 32) + self.check_blocksize_name('sha3_384', 104, 48) + self.check_blocksize_name('sha3_512', 72, 64) + self.check_blocksize_name('shake_128', 168, 0, 32) + self.check_blocksize_name('shake_256', 136, 0, 64) @requires_blake2 def test_blocksize_name_blake2(self): @@ -563,6 +622,72 @@ class HashLibTestCase(unittest.TestCase): key = bytes.fromhex(key) self.check('blake2s', msg, md, key=key) + @requires_sha3 + def test_case_sha3_224_0(self): + self.check('sha3_224', b"", + "6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7") + + @requires_sha3 + def test_case_sha3_224_vector(self): + for msg, md in read_vectors('sha3_224'): + self.check('sha3_224', msg, md) + + @requires_sha3 + def test_case_sha3_256_0(self): + self.check('sha3_256', b"", + "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a") + + @requires_sha3 + def test_case_sha3_256_vector(self): + for msg, md in read_vectors('sha3_256'): + self.check('sha3_256', msg, md) + + @requires_sha3 + def test_case_sha3_384_0(self): + self.check('sha3_384', b"", + "0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2a"+ + "c3713831264adb47fb6bd1e058d5f004") + + @requires_sha3 + def test_case_sha3_384_vector(self): + for msg, md in read_vectors('sha3_384'): + self.check('sha3_384', msg, md) + + @requires_sha3 + def test_case_sha3_512_0(self): + self.check('sha3_512', b"", + "a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a6"+ + "15b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26") + + @requires_sha3 + def test_case_sha3_512_vector(self): + for msg, md in read_vectors('sha3_512'): + self.check('sha3_512', msg, md) + + @requires_sha3 + def test_case_shake_128_0(self): + self.check('shake_128', b"", + "7f9c2ba4e88f827d616045507605853ed73b8093f6efbc88eb1a6eacfa66ef26", + True) + self.check('shake_128', b"", "7f9c", True) + + @requires_sha3 + def test_case_shake128_vector(self): + for msg, md in read_vectors('shake_128'): + self.check('shake_128', msg, md, True) + + @requires_sha3 + def test_case_shake_256_0(self): + self.check('shake_256', b"", + "46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762f", + True) + self.check('shake_256', b"", "46b9", True) + + @requires_sha3 + def test_case_shake256_vector(self): + for msg, md in read_vectors('shake_256'): + self.check('shake_256', msg, md, True) + def test_gil(self): # Check things work fine with an input larger than the size required # for multithreaded operation (which is hardwired to 2048). -- cgit v1.2.1 From e3f6e21e773567f63c683b707120fa646462ce46 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Wed, 7 Sep 2016 08:54:35 -0700 Subject: new and exciting shutdown error on windows --- Lib/test/test_io.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index c48ec3a239..f9f5d7a7ee 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -3230,7 +3230,8 @@ def _to_memoryview(buf): class CTextIOWrapperTest(TextIOWrapperTest): io = io - shutdown_error = "RuntimeError: could not find io module state" + shutdown_error = ("ImportError: sys.meta_path is None" + if os.name == "nt" else "RuntimeError: could not find io module state") def test_initialization(self): r = self.BytesIO(b"\xc3\xa9\n\n") -- cgit v1.2.1 From 8ae876a1a48148ef9beb090f87351c27580c2454 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Wed, 7 Sep 2016 09:31:52 -0700 Subject: Issue #27959: Prevent ImportError from escaping codec search function --- Lib/encodings/__init__.py | 12 ++++++++---- Lib/test/test_io.py | 3 +-- 2 files changed, 9 insertions(+), 6 deletions(-) (limited to 'Lib') diff --git a/Lib/encodings/__init__.py b/Lib/encodings/__init__.py index 9a9b90b004..ca07881f34 100644 --- a/Lib/encodings/__init__.py +++ b/Lib/encodings/__init__.py @@ -155,9 +155,13 @@ codecs.register(search_function) if sys.platform == 'win32': def _alias_mbcs(encoding): - import _bootlocale - if encoding == _bootlocale.getpreferredencoding(False): - import encodings.mbcs - return encodings.mbcs.getregentry() + try: + import _bootlocale + if encoding == _bootlocale.getpreferredencoding(False): + import encodings.mbcs + return encodings.mbcs.getregentry() + except ImportError: + # Imports may fail while we are shutting down + pass codecs.register(_alias_mbcs) diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index f9f5d7a7ee..c48ec3a239 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -3230,8 +3230,7 @@ def _to_memoryview(buf): class CTextIOWrapperTest(TextIOWrapperTest): io = io - shutdown_error = ("ImportError: sys.meta_path is None" - if os.name == "nt" else "RuntimeError: could not find io module state") + shutdown_error = "RuntimeError: could not find io module state" def test_initialization(self): r = self.BytesIO(b"\xc3\xa9\n\n") -- cgit v1.2.1 From e8350b2f0f77fe6a0571a248b590d24483b0d39e Mon Sep 17 00:00:00 2001 From: R David Murray Date: Wed, 7 Sep 2016 16:48:35 -0400 Subject: #27331: add policy keyword argument to all MIME subclasses. Patch by Berker Peksag. --- Lib/email/mime/application.py | 5 +++-- Lib/email/mime/audio.py | 5 +++-- Lib/email/mime/base.py | 8 ++++++-- Lib/email/mime/image.py | 5 +++-- Lib/email/mime/message.py | 4 ++-- Lib/email/mime/multipart.py | 3 ++- Lib/email/mime/text.py | 4 ++-- Lib/test/test_email/test_email.py | 41 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 62 insertions(+), 13 deletions(-) (limited to 'Lib') diff --git a/Lib/email/mime/application.py b/Lib/email/mime/application.py index f5c5905564..6877e554e1 100644 --- a/Lib/email/mime/application.py +++ b/Lib/email/mime/application.py @@ -14,7 +14,7 @@ class MIMEApplication(MIMENonMultipart): """Class for generating application/* MIME documents.""" def __init__(self, _data, _subtype='octet-stream', - _encoder=encoders.encode_base64, **_params): + _encoder=encoders.encode_base64, *, policy=None, **_params): """Create an application/* type MIME document. _data is a string containing the raw application data. @@ -31,6 +31,7 @@ class MIMEApplication(MIMENonMultipart): """ if _subtype is None: raise TypeError('Invalid application MIME subtype') - MIMENonMultipart.__init__(self, 'application', _subtype, **_params) + MIMENonMultipart.__init__(self, 'application', _subtype, policy=policy, + **_params) self.set_payload(_data) _encoder(self) diff --git a/Lib/email/mime/audio.py b/Lib/email/mime/audio.py index fbc118951a..4bcd7b224a 100644 --- a/Lib/email/mime/audio.py +++ b/Lib/email/mime/audio.py @@ -43,7 +43,7 @@ class MIMEAudio(MIMENonMultipart): """Class for generating audio/* MIME documents.""" def __init__(self, _audiodata, _subtype=None, - _encoder=encoders.encode_base64, **_params): + _encoder=encoders.encode_base64, *, policy=None, **_params): """Create an audio/* type MIME document. _audiodata is a string containing the raw audio data. If this data @@ -68,6 +68,7 @@ class MIMEAudio(MIMENonMultipart): _subtype = _whatsnd(_audiodata) if _subtype is None: raise TypeError('Could not find audio MIME subtype') - MIMENonMultipart.__init__(self, 'audio', _subtype, **_params) + MIMENonMultipart.__init__(self, 'audio', _subtype, policy=policy, + **_params) self.set_payload(_audiodata) _encoder(self) diff --git a/Lib/email/mime/base.py b/Lib/email/mime/base.py index ac919258b1..1a3f9b51f6 100644 --- a/Lib/email/mime/base.py +++ b/Lib/email/mime/base.py @@ -6,6 +6,8 @@ __all__ = ['MIMEBase'] +import email.policy + from email import message @@ -13,14 +15,16 @@ from email import message class MIMEBase(message.Message): """Base class for MIME specializations.""" - def __init__(self, _maintype, _subtype, **_params): + def __init__(self, _maintype, _subtype, *, policy=None, **_params): """This constructor adds a Content-Type: and a MIME-Version: header. The Content-Type: header is taken from the _maintype and _subtype arguments. Additional parameters for this header are taken from the keyword arguments. """ - message.Message.__init__(self) + if policy is None: + policy = email.policy.compat32 + message.Message.__init__(self, policy=policy) ctype = '%s/%s' % (_maintype, _subtype) self.add_header('Content-Type', ctype, **_params) self['MIME-Version'] = '1.0' diff --git a/Lib/email/mime/image.py b/Lib/email/mime/image.py index 5563823239..92724643cd 100644 --- a/Lib/email/mime/image.py +++ b/Lib/email/mime/image.py @@ -17,7 +17,7 @@ class MIMEImage(MIMENonMultipart): """Class for generating image/* type MIME documents.""" def __init__(self, _imagedata, _subtype=None, - _encoder=encoders.encode_base64, **_params): + _encoder=encoders.encode_base64, *, policy=None, **_params): """Create an image/* type MIME document. _imagedata is a string containing the raw image data. If this data @@ -41,6 +41,7 @@ class MIMEImage(MIMENonMultipart): _subtype = imghdr.what(None, _imagedata) if _subtype is None: raise TypeError('Could not guess image MIME subtype') - MIMENonMultipart.__init__(self, 'image', _subtype, **_params) + MIMENonMultipart.__init__(self, 'image', _subtype, policy=policy, + **_params) self.set_payload(_imagedata) _encoder(self) diff --git a/Lib/email/mime/message.py b/Lib/email/mime/message.py index 275dbfd088..07e4f2d119 100644 --- a/Lib/email/mime/message.py +++ b/Lib/email/mime/message.py @@ -14,7 +14,7 @@ from email.mime.nonmultipart import MIMENonMultipart class MIMEMessage(MIMENonMultipart): """Class representing message/* MIME documents.""" - def __init__(self, _msg, _subtype='rfc822'): + def __init__(self, _msg, _subtype='rfc822', *, policy=None): """Create a message/* type MIME document. _msg is a message object and must be an instance of Message, or a @@ -24,7 +24,7 @@ class MIMEMessage(MIMENonMultipart): default is "rfc822" (this is defined by the MIME standard, even though the term "rfc822" is technically outdated by RFC 2822). """ - MIMENonMultipart.__init__(self, 'message', _subtype) + MIMENonMultipart.__init__(self, 'message', _subtype, policy=policy) if not isinstance(_msg, message.Message): raise TypeError('Argument is not an instance of Message') # It's convenient to use this base class method. We need to do it diff --git a/Lib/email/mime/multipart.py b/Lib/email/mime/multipart.py index 96618650c5..2d3f288810 100644 --- a/Lib/email/mime/multipart.py +++ b/Lib/email/mime/multipart.py @@ -14,6 +14,7 @@ class MIMEMultipart(MIMEBase): """Base class for MIME multipart/* type messages.""" def __init__(self, _subtype='mixed', boundary=None, _subparts=None, + *, policy=None, **_params): """Creates a multipart/* type message. @@ -33,7 +34,7 @@ class MIMEMultipart(MIMEBase): Additional parameters for the Content-Type header are taken from the keyword arguments (or passed into the _params argument). """ - MIMEBase.__init__(self, 'multipart', _subtype, **_params) + MIMEBase.__init__(self, 'multipart', _subtype, policy=policy, **_params) # Initialise _payload to an empty list as the Message superclass's # implementation of is_multipart assumes that _payload is a list for diff --git a/Lib/email/mime/text.py b/Lib/email/mime/text.py index 479928ec94..87de8d235f 100644 --- a/Lib/email/mime/text.py +++ b/Lib/email/mime/text.py @@ -14,7 +14,7 @@ from email.mime.nonmultipart import MIMENonMultipart class MIMEText(MIMENonMultipart): """Class for generating text/* type MIME documents.""" - def __init__(self, _text, _subtype='plain', _charset=None): + def __init__(self, _text, _subtype='plain', _charset=None, *, policy=None): """Create a text/* type MIME document. _text is the string for this message object. @@ -38,7 +38,7 @@ class MIMEText(MIMENonMultipart): if isinstance(_charset, Charset): _charset = str(_charset) - MIMENonMultipart.__init__(self, 'text', _subtype, + MIMENonMultipart.__init__(self, 'text', _subtype, policy=policy, **{'charset': _charset}) self.set_payload(_text, _charset) diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py index 8aaca01dba..85fccf9548 100644 --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -31,6 +31,7 @@ from email.mime.image import MIMEImage from email.mime.base import MIMEBase from email.mime.message import MIMEMessage from email.mime.multipart import MIMEMultipart +from email.mime.nonmultipart import MIMENonMultipart from email import utils from email import errors from email import encoders @@ -2062,7 +2063,13 @@ YXNkZg== --===============0012394164==--""") self.assertEqual(m.get_payload(0).get_payload(), 'YXNkZg==') + def test_mimebase_default_policy(self): + m = MIMEBase('multipart', 'mixed') + self.assertIs(m.policy, email.policy.compat32) + def test_mimebase_custom_policy(self): + m = MIMEBase('multipart', 'mixed', policy=email.policy.default) + self.assertIs(m.policy, email.policy.default) # Test some badly formatted messages class TestNonConformant(TestEmailBase): @@ -2664,6 +2671,19 @@ message 2 msg = MIMEMultipart() self.assertTrue(msg.is_multipart()) + def test_multipart_default_policy(self): + msg = MIMEMultipart() + msg['To'] = 'a@b.com' + msg['To'] = 'c@d.com' + self.assertEqual(msg.get_all('to'), ['a@b.com', 'c@d.com']) + + def test_multipart_custom_policy(self): + msg = MIMEMultipart(policy=email.policy.default) + msg['To'] = 'a@b.com' + with self.assertRaises(ValueError) as cm: + msg['To'] = 'c@d.com' + self.assertEqual(str(cm.exception), + 'There may be at most 1 To headers in a message') # A general test of parser->model->generator idempotency. IOW, read a message # in, parse it into a message object tree, then without touching the tree, @@ -3313,6 +3333,27 @@ multipart/report g.flatten(msg, linesep='\r\n') self.assertEqual(s.getvalue(), msgtxt) + def test_mime_classes_policy_argument(self): + with openfile('audiotest.au', 'rb') as fp: + audiodata = fp.read() + with openfile('PyBanner048.gif', 'rb') as fp: + bindata = fp.read() + classes = [ + (MIMEApplication, ('',)), + (MIMEAudio, (audiodata,)), + (MIMEImage, (bindata,)), + (MIMEMessage, (Message(),)), + (MIMENonMultipart, ('multipart', 'mixed')), + (MIMEText, ('',)), + ] + for cls, constructor in classes: + with self.subTest(cls=cls.__name__, policy='compat32'): + m = cls(*constructor) + self.assertIs(m.policy, email.policy.compat32) + with self.subTest(cls=cls.__name__, policy='default'): + m = cls(*constructor, policy=email.policy.default) + self.assertIs(m.policy, email.policy.default) + # Test the iterator/generators class TestIterators(TestEmailBase): -- cgit v1.2.1 From 84086d511b5989bafb61687486375079b673cf7d Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Wed, 7 Sep 2016 14:56:15 -0700 Subject: fix expected layout of code objects --- Lib/test/test_sys.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 3131f367cc..332acf8088 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -912,13 +912,13 @@ class SizeofTest(unittest.TestCase): return inner check(get_cell().__closure__[0], size('P')) # code - check(get_cell().__code__, size('5i9Pi3P')) - check(get_cell.__code__, size('5i9Pi3P')) + check(get_cell().__code__, size('6i13P')) + check(get_cell.__code__, size('6i13P')) def get_cell2(x): def inner(): return x return inner - check(get_cell2.__code__, size('5i9Pi3P') + 1) + check(get_cell2.__code__, size('6i13P') + 1) # complex check(complex(0,1), size('2d')) # method_descriptor (descriptor object) -- cgit v1.2.1 From 060f6e9d2882fc7ae95df2e89abf029965fc7dda Mon Sep 17 00:00:00 2001 From: Davin Potts Date: Wed, 7 Sep 2016 18:48:01 -0500 Subject: Fixes issue #6766: Updated multiprocessing Proxy Objects to support nesting --- Lib/multiprocessing/managers.py | 95 ++++++++++++++++++++++++++++----------- Lib/test/_test_multiprocessing.py | 70 ++++++++++++++++++++++++++++- 2 files changed, 138 insertions(+), 27 deletions(-) (limited to 'Lib') diff --git a/Lib/multiprocessing/managers.py b/Lib/multiprocessing/managers.py index c559b55a3f..6e63a60f85 100644 --- a/Lib/multiprocessing/managers.py +++ b/Lib/multiprocessing/managers.py @@ -142,7 +142,8 @@ class Server(object): self.id_to_obj = {'0': (None, ())} self.id_to_refcount = {} - self.mutex = threading.RLock() + self.id_to_local_proxy_obj = {} + self.mutex = threading.Lock() def serve_forever(self): ''' @@ -227,7 +228,14 @@ class Server(object): methodname = obj = None request = recv() ident, methodname, args, kwds = request - obj, exposed, gettypeid = id_to_obj[ident] + try: + obj, exposed, gettypeid = id_to_obj[ident] + except KeyError as ke: + try: + obj, exposed, gettypeid = \ + self.id_to_local_proxy_obj[ident] + except KeyError as second_ke: + raise ke if methodname not in exposed: raise AttributeError( @@ -308,7 +316,7 @@ class Server(object): ''' with self.mutex: result = [] - keys = list(self.id_to_obj.keys()) + keys = list(self.id_to_refcount.keys()) keys.sort() for ident in keys: if ident != '0': @@ -321,7 +329,8 @@ class Server(object): ''' Number of shared objects ''' - return len(self.id_to_obj) - 1 # don't count ident='0' + # Doesn't use (len(self.id_to_obj) - 1) as we shouldn't count ident='0' + return len(self.id_to_refcount) def shutdown(self, c): ''' @@ -363,13 +372,9 @@ class Server(object): self.id_to_obj[ident] = (obj, set(exposed), method_to_typeid) if ident not in self.id_to_refcount: self.id_to_refcount[ident] = 0 - # increment the reference count immediately, to avoid - # this object being garbage collected before a Proxy - # object for it can be created. The caller of create() - # is responsible for doing a decref once the Proxy object - # has been created. - self.incref(c, ident) - return ident, tuple(exposed) + + self.incref(c, ident) + return ident, tuple(exposed) def get_methods(self, c, token): ''' @@ -387,15 +392,45 @@ class Server(object): def incref(self, c, ident): with self.mutex: - self.id_to_refcount[ident] += 1 + try: + self.id_to_refcount[ident] += 1 + except KeyError as ke: + # If no external references exist but an internal (to the + # manager) still does and a new external reference is created + # from it, restore the manager's tracking of it from the + # previously stashed internal ref. + if ident in self.id_to_local_proxy_obj: + self.id_to_refcount[ident] = 1 + self.id_to_obj[ident] = \ + self.id_to_local_proxy_obj[ident] + obj, exposed, gettypeid = self.id_to_obj[ident] + util.debug('Server re-enabled tracking & INCREF %r', ident) + else: + raise ke def decref(self, c, ident): + if ident not in self.id_to_refcount and \ + ident in self.id_to_local_proxy_obj: + util.debug('Server DECREF skipping %r', ident) + return + with self.mutex: assert self.id_to_refcount[ident] >= 1 self.id_to_refcount[ident] -= 1 if self.id_to_refcount[ident] == 0: - del self.id_to_obj[ident], self.id_to_refcount[ident] - util.debug('disposing of obj with id %r', ident) + del self.id_to_refcount[ident] + + if ident not in self.id_to_refcount: + # Two-step process in case the object turns out to contain other + # proxy objects (e.g. a managed list of managed lists). + # Otherwise, deleting self.id_to_obj[ident] would trigger the + # deleting of the stored value (another managed object) which would + # in turn attempt to acquire the mutex that is already held here. + self.id_to_obj[ident] = (None, (), None) # thread-safe + util.debug('disposing of obj with id %r', ident) + with self.mutex: + del self.id_to_obj[ident] + # # Class to represent state of a manager @@ -658,7 +693,7 @@ class BaseProxy(object): _mutex = util.ForkAwareThreadLock() def __init__(self, token, serializer, manager=None, - authkey=None, exposed=None, incref=True): + authkey=None, exposed=None, incref=True, manager_owned=False): with BaseProxy._mutex: tls_idset = BaseProxy._address_to_local.get(token.address, None) if tls_idset is None: @@ -680,6 +715,12 @@ class BaseProxy(object): self._serializer = serializer self._Client = listener_client[serializer][1] + # Should be set to True only when a proxy object is being created + # on the manager server; primary use case: nested proxy objects. + # RebuildProxy detects when a proxy is being created on the manager + # and sets this value appropriately. + self._owned_by_manager = manager_owned + if authkey is not None: self._authkey = process.AuthenticationString(authkey) elif self._manager is not None: @@ -738,6 +779,10 @@ class BaseProxy(object): return self._callmethod('#GETVALUE') def _incref(self): + if self._owned_by_manager: + util.debug('owned_by_manager skipped INCREF of %r', self._token.id) + return + conn = self._Client(self._token.address, authkey=self._authkey) dispatch(conn, None, 'incref', (self._id,)) util.debug('INCREF %r', self._token.id) @@ -822,19 +867,19 @@ class BaseProxy(object): def RebuildProxy(func, token, serializer, kwds): ''' Function used for unpickling proxy objects. - - If possible the shared object is returned, or otherwise a proxy for it. ''' server = getattr(process.current_process(), '_manager_server', None) - if server and server.address == token.address: - return server.id_to_obj[token.id][0] - else: - incref = ( - kwds.pop('incref', True) and - not getattr(process.current_process(), '_inheriting', False) - ) - return func(token, serializer, incref=incref, **kwds) + util.debug('Rebuild a proxy owned by manager, token=%r', token) + kwds['manager_owned'] = True + if token.id not in server.id_to_local_proxy_obj: + server.id_to_local_proxy_obj[token.id] = \ + server.id_to_obj[token.id] + incref = ( + kwds.pop('incref', True) and + not getattr(process.current_process(), '_inheriting', False) + ) + return func(token, serializer, incref=incref, **kwds) # # Functions to create proxies and proxy types diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index cfd801e55c..d88cd07618 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -1628,13 +1628,33 @@ class _TestContainers(BaseTestCase): d = [a, b] e = self.list(d) self.assertEqual( - e[:], + [element[:] for element in e], [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 0, 1, 2, 3, 4]] ) f = self.list([a]) a.append('hello') - self.assertEqual(f[:], [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'hello']]) + self.assertEqual(f[0][:], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'hello']) + + def test_list_proxy_in_list(self): + a = self.list([self.list(range(3)) for _i in range(3)]) + self.assertEqual([inner[:] for inner in a], [[0, 1, 2]] * 3) + + a[0][-1] = 55 + self.assertEqual(a[0][:], [0, 1, 55]) + for i in range(1, 3): + self.assertEqual(a[i][:], [0, 1, 2]) + + self.assertEqual(a[1].pop(), 2) + self.assertEqual(len(a[1]), 2) + for i in range(0, 3, 2): + self.assertEqual(len(a[i]), 3) + + del a + + b = self.list() + b.append(b) + del b def test_dict(self): d = self.dict() @@ -1646,6 +1666,52 @@ class _TestContainers(BaseTestCase): self.assertEqual(sorted(d.values()), [chr(i) for i in indices]) self.assertEqual(sorted(d.items()), [(i, chr(i)) for i in indices]) + def test_dict_proxy_nested(self): + pets = self.dict(ferrets=2, hamsters=4) + supplies = self.dict(water=10, feed=3) + d = self.dict(pets=pets, supplies=supplies) + + self.assertEqual(supplies['water'], 10) + self.assertEqual(d['supplies']['water'], 10) + + d['supplies']['blankets'] = 5 + self.assertEqual(supplies['blankets'], 5) + self.assertEqual(d['supplies']['blankets'], 5) + + d['supplies']['water'] = 7 + self.assertEqual(supplies['water'], 7) + self.assertEqual(d['supplies']['water'], 7) + + del pets + del supplies + self.assertEqual(d['pets']['ferrets'], 2) + d['supplies']['blankets'] = 11 + self.assertEqual(d['supplies']['blankets'], 11) + + pets = d['pets'] + supplies = d['supplies'] + supplies['water'] = 7 + self.assertEqual(supplies['water'], 7) + self.assertEqual(d['supplies']['water'], 7) + + d.clear() + self.assertEqual(len(d), 0) + self.assertEqual(supplies['water'], 7) + self.assertEqual(pets['hamsters'], 4) + + l = self.list([pets, supplies]) + l[0]['marmots'] = 1 + self.assertEqual(pets['marmots'], 1) + self.assertEqual(l[0]['marmots'], 1) + + del pets + del supplies + self.assertEqual(l[0]['marmots'], 1) + + outer = self.list([[88, 99], l]) + self.assertIsInstance(outer[0], list) # Not a ListProxy + self.assertEqual(outer[-1][-1]['feed'], 3) + def test_namespace(self): n = self.Namespace() n.name = 'Bob' -- cgit v1.2.1 From ee1f94f1161d806ce7158ad039522798ff4f35ac Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 7 Sep 2016 15:42:32 -0700 Subject: Issue #15767: Add ModuleNotFoundError. --- Lib/_compat_pickle.py | 7 +++++++ Lib/test/exception_hierarchy.txt | 1 + Lib/test/test_pickle.py | 8 ++++++++ 3 files changed, 16 insertions(+) (limited to 'Lib') diff --git a/Lib/_compat_pickle.py b/Lib/_compat_pickle.py index c0e0443cf2..f68496ae63 100644 --- a/Lib/_compat_pickle.py +++ b/Lib/_compat_pickle.py @@ -242,3 +242,10 @@ PYTHON3_OSERROR_EXCEPTIONS = ( for excname in PYTHON3_OSERROR_EXCEPTIONS: REVERSE_NAME_MAPPING[('builtins', excname)] = ('exceptions', 'OSError') + +PYTHON3_IMPORTERROR_EXCEPTIONS = ( + 'ModuleNotFoundError', +) + +for excname in PYTHON3_IMPORTERROR_EXCEPTIONS: + REVERSE_NAME_MAPPING[('builtins', excname)] = ('exceptions', 'ImportError') diff --git a/Lib/test/exception_hierarchy.txt b/Lib/test/exception_hierarchy.txt index 05137654de..7333b2aa62 100644 --- a/Lib/test/exception_hierarchy.txt +++ b/Lib/test/exception_hierarchy.txt @@ -14,6 +14,7 @@ BaseException +-- BufferError +-- EOFError +-- ImportError + +-- ModuleNotFoundError +-- LookupError | +-- IndexError | +-- KeyError diff --git a/Lib/test/test_pickle.py b/Lib/test/test_pickle.py index 05203e52cd..e6c5d08522 100644 --- a/Lib/test/test_pickle.py +++ b/Lib/test/test_pickle.py @@ -335,6 +335,9 @@ class CompatPickleTests(unittest.TestCase): if (module2, name2) == ('exceptions', 'OSError'): attr = getattribute(module3, name3) self.assertTrue(issubclass(attr, OSError)) + elif (module2, name2) == ('exceptions', 'ImportError'): + attr = getattribute(module3, name3) + self.assertTrue(issubclass(attr, ImportError)) else: module, name = mapping(module2, name2) if module3[:1] != '_': @@ -401,6 +404,11 @@ class CompatPickleTests(unittest.TestCase): if exc is not OSError and issubclass(exc, OSError): self.assertEqual(reverse_mapping('builtins', name), ('exceptions', 'OSError')) + elif exc is not ImportError and issubclass(exc, ImportError): + self.assertEqual(reverse_mapping('builtins', name), + ('exceptions', 'ImportError')) + self.assertEqual(mapping('exceptions', name), + ('exceptions', name)) else: self.assertEqual(reverse_mapping('builtins', name), ('exceptions', name)) -- cgit v1.2.1 From 54dc09e399e40109e5b61f0ed2e94558cc07aaa8 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 7 Sep 2016 16:56:15 -0700 Subject: Issue #15767: Use ModuleNotFoundError. --- Lib/importlib/_bootstrap.py | 14 +++++++------- Lib/pydoc.py | 2 +- Lib/test/test_cmd_line_script.py | 2 +- Lib/test/test_import/__init__.py | 12 ++++++++++++ Lib/test/test_importlib/import_/test_api.py | 4 ++++ Lib/test/test_importlib/import_/test_fromlist.py | 10 +++++----- Lib/test/test_pydoc.py | 2 +- Lib/test/test_site.py | 2 +- 8 files changed, 32 insertions(+), 16 deletions(-) (limited to 'Lib') diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py index 2eeafe1dfb..8cd0262bbf 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -943,10 +943,10 @@ def _find_and_load_unlocked(name, import_): path = parent_module.__path__ except AttributeError: msg = (_ERR_MSG + '; {!r} is not a package').format(name, parent) - raise ImportError(msg, name=name) from None + raise ModuleNotFoundError(msg, name=name) from None spec = _find_spec(name, path) if spec is None: - raise ImportError(_ERR_MSG.format(name), name=name) + raise ModuleNotFoundError(_ERR_MSG.format(name), name=name) else: module = _load_unlocked(spec) if parent: @@ -982,10 +982,11 @@ def _gcd_import(name, package=None, level=0): _imp.release_lock() message = ('import of {} halted; ' 'None in sys.modules'.format(name)) - raise ImportError(message, name=name) + raise ModuleNotFoundError(message, name=name) _lock_unlock_module(name) return module + def _handle_fromlist(module, fromlist, import_): """Figure out what __import__ should return. @@ -1007,13 +1008,12 @@ def _handle_fromlist(module, fromlist, import_): from_name = '{}.{}'.format(module.__name__, x) try: _call_with_frames_removed(import_, from_name) - except ImportError as exc: + except ModuleNotFoundError as exc: # Backwards-compatibility dictates we ignore failed # imports triggered by fromlist for modules that don't # exist. - if str(exc).startswith(_ERR_MSG_PREFIX): - if exc.name == from_name: - continue + if exc.name == from_name: + continue raise return module diff --git a/Lib/pydoc.py b/Lib/pydoc.py index d7a177f1a2..39db3915dc 100644 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -350,7 +350,7 @@ def safeimport(path, forceload=0, cache={}): elif exc is SyntaxError: # A SyntaxError occurred before we could execute the module. raise ErrorDuringImport(value.filename, info) - elif exc is ImportError and value.name == path: + elif issubclass(exc, ImportError) and value.name == path: # No such module in the path. return None else: diff --git a/Lib/test/test_cmd_line_script.py b/Lib/test/test_cmd_line_script.py index 6e0b6699fb..38cb2e206f 100644 --- a/Lib/test/test_cmd_line_script.py +++ b/Lib/test/test_cmd_line_script.py @@ -428,7 +428,7 @@ class CmdLineTest(unittest.TestCase): ('builtins.x', br'Error while finding module specification.*' br'AttributeError'), ('builtins.x.y', br'Error while finding module specification.*' - br'ImportError.*No module named.*not a package'), + br'ModuleNotFoundError.*No module named.*not a package'), ('os.path', br'loader.*cannot handle'), ('importlib', br'No module named.*' br'is a package and cannot be directly executed'), diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index 1e33274b87..760908efe6 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -69,6 +69,18 @@ class ImportTests(unittest.TestCase): def tearDown(self): unload(TESTFN) + def test_import_raises_ModuleNotFoundError(self): + with self.assertRaises(ModuleNotFoundError): + import something_that_should_not_exist_anywhere + + def test_from_import_missing_module_raises_ModuleNotFoundError(self): + with self.assertRaises(ModuleNotFoundError): + from something_that_should_not_exist_anywhere import blah + + def test_from_import_missing_attr_raises_ImportError(self): + with self.assertRaises(ImportError): + from importlib import something_that_should_not_exist_anywhere + def test_case_sensitivity(self): # Brief digression to test that import is case-sensitive: if we got # this far, we know for sure that "random" exists. diff --git a/Lib/test/test_importlib/import_/test_api.py b/Lib/test/test_importlib/import_/test_api.py index 7069d9e58d..a7bf2749cf 100644 --- a/Lib/test/test_importlib/import_/test_api.py +++ b/Lib/test/test_importlib/import_/test_api.py @@ -43,6 +43,10 @@ class APITest: """Test API-specific details for __import__ (e.g. raising the right exception when passing in an int for the module name).""" + def test_raises_ModuleNotFoundError(self): + with self.assertRaises(ModuleNotFoundError): + util.import_importlib('some module that does not exist') + def test_name_requires_rparition(self): # Raise TypeError if a non-string is passed in for the module name. with self.assertRaises(TypeError): diff --git a/Lib/test/test_importlib/import_/test_fromlist.py b/Lib/test/test_importlib/import_/test_fromlist.py index 80454655ad..14640032b4 100644 --- a/Lib/test/test_importlib/import_/test_fromlist.py +++ b/Lib/test/test_importlib/import_/test_fromlist.py @@ -73,16 +73,16 @@ class HandlingFromlist: self.assertTrue(hasattr(module, 'module')) self.assertEqual(module.module.__name__, 'pkg.module') - def test_module_from_package_triggers_ImportError(self): - # If a submodule causes an ImportError because it tries to import - # a module which doesn't exist, that should let the ImportError - # propagate. + def test_module_from_package_triggers_ModuleNotFoundError(self): + # If a submodule causes an ModuleNotFoundError because it tries + # to import a module which doesn't exist, that should let the + # ModuleNotFoundError propagate. def module_code(): import i_do_not_exist with util.mock_modules('pkg.__init__', 'pkg.mod', module_code={'pkg.mod': module_code}) as importer: with util.import_state(meta_path=[importer]): - with self.assertRaises(ImportError) as exc: + with self.assertRaises(ModuleNotFoundError) as exc: self.__import__('pkg', fromlist=['mod']) self.assertEqual('i_do_not_exist', exc.exception.name) diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py index 527234bc6e..229fff47c9 100644 --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -263,7 +263,7 @@ Use help() to get the interactive help utility. Use help(str) for help on the str class.'''.replace('\n', os.linesep) # output pattern for module with bad imports -badimport_pattern = "problem in %s - ImportError: No module named %r" +badimport_pattern = "problem in %s - ModuleNotFoundError: No module named %r" expected_dynamicattribute_pattern = """ Help on class DA in module %s: diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index f698927f37..0720230f24 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -138,7 +138,7 @@ class HelperFunctionsTests(unittest.TestCase): re.escape(os.path.join(pth_dir, pth_fn))) # XXX: ditto previous XXX comment. self.assertRegex(err_out.getvalue(), 'Traceback') - self.assertRegex(err_out.getvalue(), 'ImportError') + self.assertRegex(err_out.getvalue(), 'ModuleNotFoundError') @unittest.skipIf(sys.platform == "win32", "Windows does not raise an " "error for file paths containing null characters") -- cgit v1.2.1 From 1d90c904cf3275b6952fcfc987c1487d9d80e75f Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Wed, 7 Sep 2016 17:27:33 -0700 Subject: Issue #28005: Allow ImportErrors in encoding implementation to propagate. --- Lib/encodings/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/encodings/__init__.py b/Lib/encodings/__init__.py index ca07881f34..cf90568605 100644 --- a/Lib/encodings/__init__.py +++ b/Lib/encodings/__init__.py @@ -98,8 +98,9 @@ def search_function(encoding): # module with side-effects that is not in the 'encodings' package. mod = __import__('encodings.' + modname, fromlist=_import_tail, level=0) - except ImportError: - pass + except ModuleNotFoundError as ex: + if ex.name != 'encodings.' + modname: + raise else: break else: -- cgit v1.2.1 From 9e4c295c3e7a66735e45c0cbd1a13757eee4d896 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Thu, 8 Sep 2016 01:37:03 +0100 Subject: Added back test code lost during merge. --- Lib/test/test_logging.py | 72 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 85344de573..e45a982dbf 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -26,6 +26,7 @@ import logging.config import codecs import configparser import datetime +import pathlib import pickle import io import gc @@ -308,6 +309,10 @@ class BuiltinLevelsTest(BaseTest): self.assertEqual(logging.getLevelName('INFO'), logging.INFO) self.assertEqual(logging.getLevelName(logging.INFO), 'INFO') + def test_issue27935(self): + fatal = logging.getLevelName('FATAL') + self.assertEqual(fatal, logging.FATAL) + class BasicFilterTest(BaseTest): """Test the bundled Filter class.""" @@ -575,6 +580,29 @@ class HandlerTest(BaseTest): self.assertFalse(h.shouldFlush(r)) h.close() + def test_path_objects(self): + """ + Test that Path objects are accepted as filename arguments to handlers. + + See Issue #27493. + """ + fd, fn = tempfile.mkstemp() + os.close(fd) + os.unlink(fn) + pfn = pathlib.Path(fn) + cases = ( + (logging.FileHandler, (pfn, 'w')), + (logging.handlers.RotatingFileHandler, (pfn, 'a')), + (logging.handlers.TimedRotatingFileHandler, (pfn, 'h')), + ) + if sys.platform in ('linux', 'darwin'): + cases += ((logging.handlers.WatchedFileHandler, (pfn, 'w')),) + for cls, args in cases: + h = cls(*args) + self.assertTrue(os.path.exists(fn)) + h.close() + os.unlink(fn) + @unittest.skipIf(os.name == 'nt', 'WatchedFileHandler not appropriate for Windows.') @unittest.skipUnless(threading, 'Threading required for this test.') def test_race(self): @@ -958,7 +986,7 @@ class MemoryHandlerTest(BaseTest): def setUp(self): BaseTest.setUp(self) self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING, - self.root_hdlr) + self.root_hdlr) self.mem_logger = logging.getLogger('mem') self.mem_logger.propagate = 0 self.mem_logger.addHandler(self.mem_hdlr) @@ -995,6 +1023,36 @@ class MemoryHandlerTest(BaseTest): self.mem_logger.debug(self.next_message()) self.assert_log_lines(lines) + def test_flush_on_close(self): + """ + Test that the flush-on-close configuration works as expected. + """ + self.mem_logger.debug(self.next_message()) + self.assert_log_lines([]) + self.mem_logger.info(self.next_message()) + self.assert_log_lines([]) + self.mem_logger.removeHandler(self.mem_hdlr) + # Default behaviour is to flush on close. Check that it happens. + self.mem_hdlr.close() + lines = [ + ('DEBUG', '1'), + ('INFO', '2'), + ] + self.assert_log_lines(lines) + # Now configure for flushing not to be done on close. + self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING, + self.root_hdlr, + False) + self.mem_logger.addHandler(self.mem_hdlr) + self.mem_logger.debug(self.next_message()) + self.assert_log_lines(lines) # no change + self.mem_logger.info(self.next_message()) + self.assert_log_lines(lines) # no change + self.mem_logger.removeHandler(self.mem_hdlr) + self.mem_hdlr.close() + # assert that no new lines have been added + self.assert_log_lines(lines) # no change + class ExceptionFormatter(logging.Formatter): """A special exception formatter.""" @@ -4239,6 +4297,17 @@ class NTEventLogHandlerTest(BaseTest): msg = 'Record not found in event log, went back %d records' % GO_BACK self.assertTrue(found, msg=msg) + +class MiscTestCase(unittest.TestCase): + def test__all__(self): + blacklist = {'logThreads', 'logMultiprocessing', + 'logProcesses', 'currentframe', + 'PercentStyle', 'StrFormatStyle', 'StringTemplateStyle', + 'Filterer', 'PlaceHolder', 'Manager', 'RootLogger', + 'root'} + support.check__all__(self, logging, blacklist=blacklist) + + # Set the locale to the platform-dependent default. I have no idea # why the test does this, but in any case we save the current locale # first and restore it at the end. @@ -4256,6 +4325,7 @@ def test_main(): ExceptionTest, SysLogHandlerTest, HTTPHandlerTest, NTEventLogHandlerTest, TimedRotatingFileHandlerTest, UnixSocketHandlerTest, UnixDatagramHandlerTest, UnixSysLogHandlerTest, + MiscTestCase ] if hasattr(logging.handlers, 'QueueListener'): tests.append(QueueListenerTest) -- cgit v1.2.1 From 38d075e5194a4a4fa38cb15dbd925600d48fe771 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Wed, 7 Sep 2016 17:51:30 -0700 Subject: Fix expected error message in PyTextIOWrapperTest --- Lib/test/test_io.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index c48ec3a239..1115d9f1c2 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -3276,7 +3276,7 @@ class CTextIOWrapperTest(TextIOWrapperTest): class PyTextIOWrapperTest(TextIOWrapperTest): io = pyio - shutdown_error = "LookupError: unknown encoding: ascii" + shutdown_error = "ImportError: sys.meta_path is None, Python is likely shutting down" class IncrementalNewlineDecoderTest(unittest.TestCase): -- cgit v1.2.1 From cf374389c705dd7512c178e1cb51a504ebccb60a Mon Sep 17 00:00:00 2001 From: R David Murray Date: Wed, 7 Sep 2016 21:15:59 -0400 Subject: #24277: The new email API is no longer provisional. This is a wholesale reorganization and editing of the email documentation to make the new API the standard one, and the old API the 'legacy' one. The default is still the compat32 policy, for backward compatibility. We will change that eventually. --- Lib/email/message.py | 20 ++++++++++++++++++++ Lib/test/test_email/test_message.py | 20 ++++++++++++++++++++ 2 files changed, 40 insertions(+) (limited to 'Lib') diff --git a/Lib/email/message.py b/Lib/email/message.py index 4b042836ad..c07da436ac 100644 --- a/Lib/email/message.py +++ b/Lib/email/message.py @@ -951,6 +951,26 @@ class MIMEPart(Message): policy = default Message.__init__(self, policy) + + def as_string(self, unixfrom=False, maxheaderlen=None, policy=None): + """Return the entire formatted message as a string. + + Optional 'unixfrom', when true, means include the Unix From_ envelope + header. maxheaderlen is retained for backward compatibility with the + base Message class, but defaults to None, meaning that the policy value + for max_line_length controls the header maximum length. 'policy' is + passed to the Generator instance used to serialize the mesasge; if it + is not specified the policy associated with the message instance is + used. + """ + policy = self.policy if policy is None else policy + if maxheaderlen is None: + maxheaderlen = policy.max_line_length + return super().as_string(maxheaderlen=maxheaderlen, policy=policy) + + def __str__(self): + return self.as_string(policy=self.policy.clone(utf8=True)) + def is_attachment(self): c_d = self.get('content-disposition') return False if c_d is None else c_d.content_disposition == 'attachment' diff --git a/Lib/test/test_email/test_message.py b/Lib/test/test_email/test_message.py index 434516226c..f3a57df9e9 100644 --- a/Lib/test/test_email/test_message.py +++ b/Lib/test/test_email/test_message.py @@ -764,6 +764,26 @@ class TestEmailMessage(TestEmailMessageBase, TestEmailBase): m.set_content(content_manager=cm) self.assertEqual(m['MIME-Version'], '1.0') + def test_as_string_uses_max_header_length_by_default(self): + m = self._str_msg('Subject: long line' + ' ab'*50 + '\n\n') + self.assertEqual(len(m.as_string().strip().splitlines()), 3) + + def test_as_string_allows_maxheaderlen(self): + m = self._str_msg('Subject: long line' + ' ab'*50 + '\n\n') + self.assertEqual(len(m.as_string(maxheaderlen=0).strip().splitlines()), + 1) + self.assertEqual(len(m.as_string(maxheaderlen=34).strip().splitlines()), + 6) + + def test_str_defaults_to_policy_max_line_length(self): + m = self._str_msg('Subject: long line' + ' ab'*50 + '\n\n') + self.assertEqual(len(str(m).strip().splitlines()), 3) + + def test_str_defaults_to_utf8(self): + m = EmailMessage() + m['Subject'] = 'unicöde' + self.assertEqual(str(m), 'Subject: unicöde\n\n') + class TestMIMEPart(TestEmailMessageBase, TestEmailBase): # Doing the full test run here may seem a bit redundant, since the two -- cgit v1.2.1 From c34cb33ce06360d4c26202402755fb05d7a134ac Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 7 Sep 2016 18:37:17 -0700 Subject: Issue #17211: Yield a namedtuple in pkgutil. Patch by Ramchandra Apte. --- Lib/pkgutil.py | 29 +++++++++++++++++------------ Lib/test/test_pkgutil.py | 5 +++-- Lib/test/test_runpy.py | 11 ++++++----- 3 files changed, 26 insertions(+), 19 deletions(-) (limited to 'Lib') diff --git a/Lib/pkgutil.py b/Lib/pkgutil.py index 7fc356c9ae..e37ad45196 100644 --- a/Lib/pkgutil.py +++ b/Lib/pkgutil.py @@ -1,5 +1,6 @@ """Utilities to support packages.""" +from collections import namedtuple from functools import singledispatch as simplegeneric import importlib import importlib.util @@ -14,9 +15,14 @@ __all__ = [ 'get_importer', 'iter_importers', 'get_loader', 'find_loader', 'walk_packages', 'iter_modules', 'get_data', 'ImpImporter', 'ImpLoader', 'read_code', 'extend_path', + 'ModuleInfo', ] +ModuleInfo = namedtuple('ModuleInfo', 'module_finder name ispkg') +ModuleInfo.__doc__ = 'A namedtuple with minimal info about a module.' + + def _get_spec(finder, name): """Return the finder-specific module spec.""" # Works with legacy finders. @@ -45,7 +51,7 @@ def read_code(stream): def walk_packages(path=None, prefix='', onerror=None): - """Yields (module_finder, name, ispkg) for all modules recursively + """Yields ModuleInfo for all modules recursively on path, or, if path is None, all accessible modules. 'path' should be either None or a list of paths to look for @@ -78,31 +84,31 @@ def walk_packages(path=None, prefix='', onerror=None): return True m[p] = True - for importer, name, ispkg in iter_modules(path, prefix): - yield importer, name, ispkg + for info in iter_modules(path, prefix): + yield info - if ispkg: + if info.ispkg: try: - __import__(name) + __import__(info.name) except ImportError: if onerror is not None: - onerror(name) + onerror(info.name) except Exception: if onerror is not None: - onerror(name) + onerror(info.name) else: raise else: - path = getattr(sys.modules[name], '__path__', None) or [] + path = getattr(sys.modules[info.name], '__path__', None) or [] # don't traverse path items we've seen before path = [p for p in path if not seen(p)] - yield from walk_packages(path, name+'.', onerror) + yield from walk_packages(path, info.name+'.', onerror) def iter_modules(path=None, prefix=''): - """Yields (module_finder, name, ispkg) for all submodules on path, + """Yields ModuleInfo for all submodules on path, or, if path is None, all top-level modules on sys.path. 'path' should be either None or a list of paths to look for @@ -111,7 +117,6 @@ def iter_modules(path=None, prefix=''): 'prefix' is a string to output on the front of every module name on output. """ - if path is None: importers = iter_importers() else: @@ -122,7 +127,7 @@ def iter_modules(path=None, prefix=''): for name, ispkg in iter_importer_modules(i, prefix): if name not in yielded: yielded[name] = 1 - yield i, name, ispkg + yield ModuleInfo(i, name, ispkg) @simplegeneric diff --git a/Lib/test/test_pkgutil.py b/Lib/test/test_pkgutil.py index ae2aa1bcea..fc04dcfd7f 100644 --- a/Lib/test/test_pkgutil.py +++ b/Lib/test/test_pkgutil.py @@ -81,8 +81,9 @@ class PkgutilTests(unittest.TestCase): self.assertEqual(res2, RESOURCE_DATA) names = [] - for loader, name, ispkg in pkgutil.iter_modules([zip_file]): - names.append(name) + for moduleinfo in pkgutil.iter_modules([zip_file]): + self.assertIsInstance(moduleinfo, pkgutil.ModuleInfo) + names.append(moduleinfo.name) self.assertEqual(names, ['test_getdata_zipfile']) del sys.path[0] diff --git a/Lib/test/test_runpy.py b/Lib/test/test_runpy.py index db55db702b..02b4d62567 100644 --- a/Lib/test/test_runpy.py +++ b/Lib/test/test_runpy.py @@ -577,13 +577,14 @@ from ..uncle.cousin import nephew self.addCleanup(self._del_pkg, pkg_dir) for depth in range(2, max_depth+1): self._add_relative_modules(pkg_dir, "", depth) - for finder, mod_name, ispkg in pkgutil.walk_packages([pkg_dir]): - self.assertIsInstance(finder, + for moduleinfo in pkgutil.walk_packages([pkg_dir]): + self.assertIsInstance(moduleinfo, pkgutil.ModuleInfo) + self.assertIsInstance(moduleinfo.module_finder, importlib.machinery.FileFinder) - if ispkg: - expected_packages.remove(mod_name) + if moduleinfo.ispkg: + expected_packages.remove(moduleinfo.name) else: - expected_modules.remove(mod_name) + expected_modules.remove(moduleinfo.name) self.assertEqual(len(expected_packages), 0, expected_packages) self.assertEqual(len(expected_modules), 0, expected_modules) -- cgit v1.2.1 From 2c3db86a33ade21fbe9dadb182f4513f04aee19f Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Wed, 7 Sep 2016 18:39:18 -0700 Subject: Issue #26667: Add path-like object support to importlib.util. --- Lib/importlib/_bootstrap_external.py | 4 ++++ Lib/test/test_importlib/test_spec.py | 6 ++++++ Lib/test/test_importlib/test_util.py | 19 +++++++++++++++++++ 3 files changed, 29 insertions(+) (limited to 'Lib') diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index d36e4ac521..828246cf9c 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -279,6 +279,7 @@ def cache_from_source(path, debug_override=None, *, optimization=None): message = 'debug_override or optimization must be set to None' raise TypeError(message) optimization = '' if debug_override else 1 + path = _os.fspath(path) head, tail = _path_split(path) base, sep, rest = tail.rpartition('.') tag = sys.implementation.cache_tag @@ -309,6 +310,7 @@ def source_from_cache(path): """ if sys.implementation.cache_tag is None: raise NotImplementedError('sys.implementation.cache_tag is None') + path = _os.fspath(path) head, pycache_filename = _path_split(path) head, pycache = _path_split(head) if pycache != _PYCACHE: @@ -536,6 +538,8 @@ def spec_from_file_location(name, location=None, *, loader=None, location = loader.get_filename(name) except ImportError: pass + else: + location = _os.fspath(location) # If the location is on the filesystem, but doesn't actually exist, # we could return None here, indicating that the location is not diff --git a/Lib/test/test_importlib/test_spec.py b/Lib/test/test_importlib/test_spec.py index 8b333e8c2f..5a16a03de6 100644 --- a/Lib/test/test_importlib/test_spec.py +++ b/Lib/test/test_importlib/test_spec.py @@ -5,6 +5,7 @@ machinery = test_util.import_importlib('importlib.machinery') util = test_util.import_importlib('importlib.util') import os.path +import pathlib from test.support import CleanImport import unittest import sys @@ -659,6 +660,11 @@ class FactoryTests: self.assertEqual(spec.cached, self.cached) self.assertTrue(spec.has_location) + def test_spec_from_file_location_path_like_arg(self): + spec = self.util.spec_from_file_location(self.name, + pathlib.PurePath(self.path)) + self.assertEqual(spec.origin, self.path) + def test_spec_from_file_location_default_without_location(self): spec = self.util.spec_from_file_location(self.name) diff --git a/Lib/test/test_importlib/test_util.py b/Lib/test/test_importlib/test_util.py index 41ca3332d5..2aa1131cf0 100644 --- a/Lib/test/test_importlib/test_util.py +++ b/Lib/test/test_importlib/test_util.py @@ -5,6 +5,7 @@ machinery = util.import_importlib('importlib.machinery') importlib_util = util.import_importlib('importlib.util') import os +import pathlib import string import sys from test import support @@ -676,6 +677,15 @@ class PEP3147Tests: self.util.cache_from_source('\\foo\\bar\\baz/qux.py', optimization=''), '\\foo\\bar\\baz\\__pycache__\\qux.{}.pyc'.format(self.tag)) + @unittest.skipUnless(sys.implementation.cache_tag is not None, + 'requires sys.implementation.cache_tag not be None') + def test_source_from_cache_path_like_arg(self): + path = pathlib.PurePath('foo', 'bar', 'baz', 'qux.py') + expect = os.path.join('foo', 'bar', 'baz', '__pycache__', + 'qux.{}.pyc'.format(self.tag)) + self.assertEqual(self.util.cache_from_source(path, optimization=''), + expect) + @unittest.skipUnless(sys.implementation.cache_tag is not None, 'requires sys.implementation.cache_tag to not be ' 'None') @@ -738,6 +748,15 @@ class PEP3147Tests: with self.assertRaises(ValueError): self.util.source_from_cache(path) + @unittest.skipUnless(sys.implementation.cache_tag is not None, + 'requires sys.implementation.cache_tag to not be ' + 'None') + def test_source_from_cache_path_like_arg(self): + path = pathlib.PurePath('foo', 'bar', 'baz', '__pycache__', + 'qux.{}.pyc'.format(self.tag)) + expect = os.path.join('foo', 'bar', 'baz', 'qux.py') + self.assertEqual(self.util.source_from_cache(path), expect) + (Frozen_PEP3147Tests, Source_PEP3147Tests -- cgit v1.2.1 From 67d84271d08d5eafeb22ae859c97bb2b1fe13780 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Thu, 8 Sep 2016 10:53:40 +0200 Subject: Issue 26798: fetch OSError and HTTPException like other tests that use open_urlresource. --- Lib/test/test_hashlib.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py index 21260f6ea6..5ae8c07037 100644 --- a/Lib/test/test_hashlib.py +++ b/Lib/test/test_hashlib.py @@ -20,6 +20,7 @@ import unittest import warnings from test import support from test.support import _4G, bigmemtest, import_fresh_module +from http.client import HTTPException # Were we compiled --with-pydebug or with #define Py_DEBUG? COMPILED_WITH_PYDEBUG = hasattr(sys, 'gettotalrefcount') @@ -54,8 +55,13 @@ def hexstr(s): URL = "http://www.pythontest.net/hashlib/{}.txt" def read_vectors(hash_name): - with support.open_urlresource(URL.format(hash_name)) as f: - for line in f: + url = URL.format(hash_name) + try: + testdata = support.open_urlresource(url) + except (OSError, HTTPException): + raise unittest.SkipTest("Could not retrieve {}".format(url)) + with testdata: + for line in testdata: line = line.strip() if line.startswith('#') or not line: continue -- cgit v1.2.1 From ede1656431004f9ea3765e6c93f548f6a9b0b2f7 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 8 Sep 2016 15:47:27 +0300 Subject: Remove old typo. Initially (e0b7e34b5971) it should be \udef0, but after 52a77ef069cd (issue #3672) lone surrogates are not accepted and should be removed. --- Lib/test/test_bytes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index 8bbd669fc2..2224424efb 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -216,7 +216,7 @@ class BaseBytesTest: self.assertEqual(b, self.type2test(sample[:-3], "utf-8")) def test_decode(self): - sample = "Hello world\n\u1234\u5678\u9abc\def0\def0" + sample = "Hello world\n\u1234\u5678\u9abc" for enc in ("utf-8", "utf-16"): b = self.type2test(sample, enc) self.assertEqual(b.decode(enc), sample) -- cgit v1.2.1 From 16bd0974887b5c50b726c4455107daad3b0d41cf Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Thu, 8 Sep 2016 09:15:54 -0700 Subject: more PY_LONG_LONG to long long --- Lib/test/test_getargs2.py | 5 ----- 1 file changed, 5 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_getargs2.py b/Lib/test/test_getargs2.py index 16e163a964..0fbe12dd14 100644 --- a/Lib/test/test_getargs2.py +++ b/Lib/test/test_getargs2.py @@ -5,10 +5,6 @@ from test import support # Skip this test if the _testcapi module isn't available. support.import_module('_testcapi') from _testcapi import getargs_keywords, getargs_keyword_only -try: - from _testcapi import getargs_L, getargs_K -except ImportError: - getargs_L = None # PY_LONG_LONG not available # > How about the following counterproposal. This also changes some of # > the other format codes to be a little more regular. @@ -309,7 +305,6 @@ class Signed_TestCase(unittest.TestCase): self.assertRaises(OverflowError, getargs_n, VERY_LARGE) -@unittest.skipIf(getargs_L is None, 'PY_LONG_LONG is not available') class LongLong_TestCase(unittest.TestCase): def test_L(self): from _testcapi import getargs_L -- cgit v1.2.1 From 7a1aba85e771486e07e4d1d72bbd8d17ce53e0f1 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 7 Sep 2016 17:40:12 -0700 Subject: Implement compact dict Issue #27350: `dict` implementation is changed like PyPy. It is more compact and preserves insertion order. _PyDict_Dummy() function has been removed. Disable test_gdb: python-gdb.py is not updated yet to the new structure of compact dictionaries (issue #28023). Patch written by INADA Naoki. --- Lib/test/test_descr.py | 6 ++++-- Lib/test/test_gdb.py | 3 +++ Lib/test/test_ordered_dict.py | 33 +++++++++++++++++++++++++++------ Lib/test/test_sys.py | 10 +++++----- Lib/test/test_weakref.py | 7 +++++-- 5 files changed, 44 insertions(+), 15 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 0950b8e47e..1e08ed92ce 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -5116,12 +5116,14 @@ class SharedKeyTests(unittest.TestCase): a, b = A(), B() self.assertEqual(sys.getsizeof(vars(a)), sys.getsizeof(vars(b))) self.assertLess(sys.getsizeof(vars(a)), sys.getsizeof({})) - a.x, a.y, a.z, a.w = range(4) + # Initial hash table can contain at most 5 elements. + # Set 6 attributes to cause internal resizing. + a.x, a.y, a.z, a.w, a.v, a.u = range(6) self.assertNotEqual(sys.getsizeof(vars(a)), sys.getsizeof(vars(b))) a2 = A() self.assertEqual(sys.getsizeof(vars(a)), sys.getsizeof(vars(a2))) self.assertLess(sys.getsizeof(vars(a)), sys.getsizeof({})) - b.u, b.v, b.w, b.t = range(4) + b.u, b.v, b.w, b.t, b.s, b.r = range(6) self.assertLess(sys.getsizeof(vars(b)), sys.getsizeof({})) diff --git a/Lib/test/test_gdb.py b/Lib/test/test_gdb.py index 382c5d009c..ff651903e9 100644 --- a/Lib/test/test_gdb.py +++ b/Lib/test/test_gdb.py @@ -11,6 +11,9 @@ import sysconfig import unittest import locale +# FIXME: issue #28023 +raise unittest.SkipTest("FIXME: issue #28023, compact dict (issue #27350) broke python-gdb.py") + # Is this Python configured to support threads? try: import _thread diff --git a/Lib/test/test_ordered_dict.py b/Lib/test/test_ordered_dict.py index 6fbc1b4132..43600e4fc8 100644 --- a/Lib/test/test_ordered_dict.py +++ b/Lib/test/test_ordered_dict.py @@ -1,3 +1,4 @@ +import builtins import contextlib import copy import gc @@ -621,6 +622,25 @@ class PurePythonOrderedDictTests(OrderedDictTests, unittest.TestCase): OrderedDict = py_coll.OrderedDict +class CPythonBuiltinDictTests(unittest.TestCase): + """Builtin dict preserves insertion order. + + Reuse some of tests in OrderedDict selectively. + """ + + module = builtins + OrderedDict = dict + +for method in ( + "test_init test_update test_abc test_clear test_delitem " + + "test_setitem test_detect_deletion_during_iteration " + + "test_popitem test_reinsert test_override_update " + + "test_highly_nested test_highly_nested_subclass " + + "test_delitem_hash_collision ").split(): + setattr(CPythonBuiltinDictTests, method, getattr(OrderedDictTests, method)) +del method + + @unittest.skipUnless(c_coll, 'requires the C version of the collections module') class CPythonOrderedDictTests(OrderedDictTests, unittest.TestCase): @@ -635,18 +655,19 @@ class CPythonOrderedDictTests(OrderedDictTests, unittest.TestCase): size = support.calcobjsize check = self.check_sizeof - basicsize = size('n2P' + '3PnPn2P') + calcsize('2nPn') - entrysize = calcsize('n2P') + calcsize('P') + basicsize = size('n2P' + '3PnPn2P') + calcsize('2nP2n') + entrysize = calcsize('n2P') + p = calcsize('P') nodesize = calcsize('Pn2P') od = OrderedDict() - check(od, basicsize + 8*entrysize) + check(od, basicsize + 8*p + 8 + 5*entrysize) # 8byte indicies + 8*2//3 * entry table od.x = 1 - check(od, basicsize + 8*entrysize) + check(od, basicsize + 8*p + 8 + 5*entrysize) od.update([(i, i) for i in range(3)]) - check(od, basicsize + 8*entrysize + 3*nodesize) + check(od, basicsize + 8*p + 8 + 5*entrysize + 3*nodesize) od.update([(i, i) for i in range(3, 10)]) - check(od, basicsize + 16*entrysize + 10*nodesize) + check(od, basicsize + 16*p + 16 + 10*entrysize + 10*nodesize) check(od.keys(), size('P')) check(od.items(), size('P')) diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 332acf8088..ea152c1ed5 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -936,9 +936,9 @@ class SizeofTest(unittest.TestCase): # method-wrapper (descriptor object) check({}.__iter__, size('2P')) # dict - check({}, size('n2P') + calcsize('2nPn') + 8*calcsize('n2P')) + check({}, size('n2P') + calcsize('2nP2n') + 8 + (8*2//3)*calcsize('n2P')) longdict = {1:1, 2:2, 3:3, 4:4, 5:5, 6:6, 7:7, 8:8} - check(longdict, size('n2P') + calcsize('2nPn') + 16*calcsize('n2P')) + check(longdict, size('n2P') + calcsize('2nP2n') + 16 + (16*2//3)*calcsize('n2P')) # dictionary-keyview check({}.keys(), size('P')) # dictionary-valueview @@ -1096,13 +1096,13 @@ class SizeofTest(unittest.TestCase): '10P' # PySequenceMethods '2P' # PyBufferProcs '4P') - # Separate block for PyDictKeysObject with 4 entries - s += calcsize("2nPn") + 4*calcsize("n2P") + # Separate block for PyDictKeysObject with 8 keys and 5 entries + s += calcsize("2nP2n") + 8 + 5*calcsize("n2P") # class class newstyleclass(object): pass check(newstyleclass, s) # dict with shared keys - check(newstyleclass().__dict__, size('n2P' + '2nPn')) + check(newstyleclass().__dict__, size('n2P' + '2nP2n')) # unicode # each tuple contains a string and its expected character size # don't put any static strings here, as they may contain diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index b1476d0a1a..6e6990cd58 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -1325,13 +1325,16 @@ class MappingTestCase(TestBase): o = Object(123456) with testcontext(): n = len(dict) - dict.popitem() + # Since underlaying dict is ordered, first item is popped + dict.pop(next(dict.keys())) self.assertEqual(len(dict), n - 1) dict[o] = o self.assertEqual(len(dict), n) + # last item in objects is removed from dict in context shutdown with testcontext(): self.assertEqual(len(dict), n - 1) - dict.pop(next(dict.keys())) + # Then, (o, o) is popped + dict.popitem() self.assertEqual(len(dict), n - 2) with testcontext(): self.assertEqual(len(dict), n - 3) -- cgit v1.2.1 From 9c9a45c1f0a264d3006971b8211d948d6883a05b Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Thu, 8 Sep 2016 10:35:16 -0700 Subject: Issue #27781: Change file system encoding on Windows to UTF-8 (PEP 529) --- Lib/os.py | 5 +-- Lib/test/test_os.py | 113 ++++++++++------------------------------------------ 2 files changed, 23 insertions(+), 95 deletions(-) (limited to 'Lib') diff --git a/Lib/os.py b/Lib/os.py index 10d70ada4d..7379dad41a 100644 --- a/Lib/os.py +++ b/Lib/os.py @@ -851,10 +851,7 @@ if supports_bytes_environ: def _fscodec(): encoding = sys.getfilesystemencoding() - if encoding == 'mbcs': - errors = 'strict' - else: - errors = 'surrogateescape' + errors = sys.getfilesystemencodeerrors() def fsencode(filename): """Encode filename (an os.PathLike, bytes, or str) to the filesystem diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 2de94c643d..aee31ed1ef 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -90,16 +90,6 @@ def ignore_deprecation_warnings(msg_regex, quiet=False): yield -@contextlib.contextmanager -def bytes_filename_warn(expected): - msg = 'The Windows bytes API has been deprecated' - if os.name == 'nt': - with ignore_deprecation_warnings(msg, quiet=not expected): - yield - else: - yield - - class _PathLike(os.PathLike): def __init__(self, path=""): @@ -342,8 +332,7 @@ class StatAttributeTests(unittest.TestCase): fname = self.fname.encode(sys.getfilesystemencoding()) except UnicodeEncodeError: self.skipTest("cannot encode %a for the filesystem" % self.fname) - with bytes_filename_warn(True): - self.check_stat_attributes(fname) + self.check_stat_attributes(fname) def test_stat_result_pickle(self): result = os.stat(self.fname) @@ -1032,8 +1021,6 @@ class BytesWalkTests(WalkTests): def setUp(self): super().setUp() self.stack = contextlib.ExitStack() - if os.name == 'nt': - self.stack.enter_context(bytes_filename_warn(False)) def tearDown(self): self.stack.close() @@ -1640,8 +1627,7 @@ class LinkTests(unittest.TestCase): def _test_link(self, file1, file2): create_file(file1) - with bytes_filename_warn(False): - os.link(file1, file2) + os.link(file1, file2) with open(file1, "r") as f1, open(file2, "r") as f2: self.assertTrue(os.path.sameopenfile(f1.fileno(), f2.fileno())) @@ -1934,10 +1920,9 @@ class Win32ListdirTests(unittest.TestCase): self.created_paths) # bytes - with bytes_filename_warn(False): - self.assertEqual( - sorted(os.listdir(os.fsencode(support.TESTFN))), - [os.fsencode(path) for path in self.created_paths]) + self.assertEqual( + sorted(os.listdir(os.fsencode(support.TESTFN))), + [os.fsencode(path) for path in self.created_paths]) def test_listdir_extended_path(self): """Test when the path starts with '\\\\?\\'.""" @@ -1949,11 +1934,10 @@ class Win32ListdirTests(unittest.TestCase): self.created_paths) # bytes - with bytes_filename_warn(False): - path = b'\\\\?\\' + os.fsencode(os.path.abspath(support.TESTFN)) - self.assertEqual( - sorted(os.listdir(path)), - [os.fsencode(path) for path in self.created_paths]) + path = b'\\\\?\\' + os.fsencode(os.path.abspath(support.TESTFN)) + self.assertEqual( + sorted(os.listdir(path)), + [os.fsencode(path) for path in self.created_paths]) @unittest.skipUnless(sys.platform == "win32", "Win32 specific tests") @@ -2028,10 +2012,8 @@ class Win32SymlinkTests(unittest.TestCase): self.assertNotEqual(os.lstat(link), os.stat(link)) bytes_link = os.fsencode(link) - with bytes_filename_warn(True): - self.assertEqual(os.stat(bytes_link), os.stat(target)) - with bytes_filename_warn(True): - self.assertNotEqual(os.lstat(bytes_link), os.stat(bytes_link)) + self.assertEqual(os.stat(bytes_link), os.stat(target)) + self.assertNotEqual(os.lstat(bytes_link), os.stat(bytes_link)) def test_12084(self): level1 = os.path.abspath(support.TESTFN) @@ -2589,46 +2571,6 @@ class ExtendedAttributeTests(unittest.TestCase): self._check_xattrs(getxattr, setxattr, removexattr, listxattr) -@unittest.skipUnless(sys.platform == "win32", "Win32 specific tests") -class Win32DeprecatedBytesAPI(unittest.TestCase): - def test_deprecated(self): - import nt - filename = os.fsencode(support.TESTFN) - for func, *args in ( - (nt._getfullpathname, filename), - (nt._isdir, filename), - (os.access, filename, os.R_OK), - (os.chdir, filename), - (os.chmod, filename, 0o777), - (os.getcwdb,), - (os.link, filename, filename), - (os.listdir, filename), - (os.lstat, filename), - (os.mkdir, filename), - (os.open, filename, os.O_RDONLY), - (os.rename, filename, filename), - (os.rmdir, filename), - (os.startfile, filename), - (os.stat, filename), - (os.unlink, filename), - (os.utime, filename), - ): - with bytes_filename_warn(True): - try: - func(*args) - except OSError: - # ignore OSError, we only care about DeprecationWarning - pass - - @support.skip_unless_symlink - def test_symlink(self): - self.addCleanup(support.unlink, support.TESTFN) - - filename = os.fsencode(support.TESTFN) - with bytes_filename_warn(True): - os.symlink(filename, filename) - - @unittest.skipUnless(hasattr(os, 'get_terminal_size'), "requires os.get_terminal_size") class TermsizeTests(unittest.TestCase): def test_does_not_crash(self): @@ -2712,16 +2654,7 @@ class OSErrorTests(unittest.TestCase): (self.bytes_filenames, os.replace, b"dst"), (self.unicode_filenames, os.rename, "dst"), (self.unicode_filenames, os.replace, "dst"), - # Issue #16414: Don't test undecodable names with listdir() - # because of a Windows bug. - # - # With the ANSI code page 932, os.listdir(b'\xe7') return an - # empty list (instead of failing), whereas os.listdir(b'\xff') - # raises a FileNotFoundError. It looks like a Windows bug: - # b'\xe7' directory does not exist, FindFirstFileA(b'\xe7') - # fails with ERROR_FILE_NOT_FOUND (2), instead of - # ERROR_PATH_NOT_FOUND (3). - (self.unicode_filenames, os.listdir,), + (self.unicode_filenames, os.listdir, ), )) else: funcs.extend(( @@ -2762,19 +2695,24 @@ class OSErrorTests(unittest.TestCase): else: funcs.append((self.filenames, os.readlink,)) + for filenames, func, *func_args in funcs: for name in filenames: try: - if isinstance(name, str): + if isinstance(name, (str, bytes)): func(name, *func_args) - elif isinstance(name, bytes): - with bytes_filename_warn(False): - func(name, *func_args) else: with self.assertWarnsRegex(DeprecationWarning, 'should be'): func(name, *func_args) except OSError as err: - self.assertIs(err.filename, name) + self.assertIs(err.filename, name, str(func)) + except RuntimeError as err: + if sys.platform != 'win32': + raise + + # issue27781: undecodable bytes currently raise RuntimeError + # by 3.6.0b4 this will become UnicodeDecodeError or nothing + self.assertIsInstance(err.__context__, UnicodeDecodeError) else: self.fail("No exception thrown by {}".format(func)) @@ -3086,7 +3024,6 @@ class TestScandir(unittest.TestCase): entry = self.create_file_entry() self.assertEqual(os.fspath(entry), os.path.join(self.path, 'file.txt')) - @unittest.skipIf(os.name == "nt", "test requires bytes path support") def test_fspath_protocol_bytes(self): bytes_filename = os.fsencode('bytesfile.txt') bytes_entry = self.create_file_entry(name=bytes_filename) @@ -3158,12 +3095,6 @@ class TestScandir(unittest.TestCase): entry.stat(follow_symlinks=False) def test_bytes(self): - if os.name == "nt": - # On Windows, os.scandir(bytes) must raise an exception - with bytes_filename_warn(True): - self.assertRaises(TypeError, os.scandir, b'.') - return - self.create_file("file.txt") path_bytes = os.fsencode(self.path) -- cgit v1.2.1 From 03c8a473999b08710ae3661724cf1bf1d76bc555 Mon Sep 17 00:00:00 2001 From: R David Murray Date: Thu, 8 Sep 2016 13:59:53 -0400 Subject: #27364: fix "incorrect" uses of escape character in the stdlib. And most of the tools. Patch by Emanual Barry, reviewed by me, Serhiy Storchaka, and Martin Panter. --- Lib/_osx_support.py | 10 +- Lib/csv.py | 8 +- Lib/difflib.py | 2 +- Lib/distutils/cmd.py | 2 +- Lib/distutils/command/bdist_msi.py | 4 +- Lib/distutils/command/build_scripts.py | 2 +- Lib/distutils/cygwinccompiler.py | 2 +- Lib/distutils/msvc9compiler.py | 2 +- Lib/distutils/sysconfig.py | 2 +- Lib/distutils/versionpredicate.py | 2 +- Lib/doctest.py | 8 +- Lib/email/_header_value_parser.py | 16 +- Lib/email/feedparser.py | 8 +- Lib/fnmatch.py | 2 +- Lib/ftplib.py | 2 +- Lib/html/parser.py | 4 +- Lib/http/client.py | 2 +- Lib/http/cookiejar.py | 6 +- Lib/http/cookies.py | 2 +- Lib/idlelib/calltips.py | 2 +- Lib/idlelib/idle_test/test_replace.py | 6 +- Lib/idlelib/idle_test/test_searchengine.py | 12 +- Lib/idlelib/paragraph.py | 2 +- Lib/imaplib.py | 4 +- Lib/msilib/__init__.py | 2 +- Lib/platform.py | 34 ++-- Lib/string.py | 2 +- Lib/sysconfig.py | 2 +- Lib/test/_test_multiprocessing.py | 2 +- Lib/test/datetimetester.py | 4 +- Lib/test/re_tests.py | 4 +- Lib/test/sortperf.py | 2 +- Lib/test/support/__init__.py | 2 +- Lib/test/test_asyncio/test_streams.py | 2 +- Lib/test/test_builtin.py | 4 +- Lib/test/test_capi.py | 10 +- Lib/test/test_cgi.py | 2 +- Lib/test/test_codeccallbacks.py | 4 +- Lib/test/test_codecs.py | 14 +- Lib/test/test_coroutines.py | 8 +- Lib/test/test_doctest.py | 2 +- Lib/test/test_email/test__header_value_parser.py | 4 +- Lib/test/test_email/test_email.py | 2 +- Lib/test/test_faulthandler.py | 8 +- Lib/test/test_fnmatch.py | 16 +- Lib/test/test_future.py | 2 +- Lib/test/test_gdb.py | 12 +- Lib/test/test_getargs2.py | 12 +- Lib/test/test_htmlparser.py | 2 +- Lib/test/test_http_cookiejar.py | 6 +- Lib/test/test_mailcap.py | 2 +- Lib/test/test_os.py | 2 +- Lib/test/test_platform.py | 4 +- Lib/test/test_re.py | 200 +++++++++++------------ Lib/test/test_regrtest.py | 4 +- Lib/test/test_ssl.py | 2 +- Lib/test/test_strftime.py | 6 +- Lib/test/test_strlit.py | 4 +- Lib/test/test_strptime.py | 10 +- Lib/test/test_unicode.py | 4 +- Lib/test/test_urllib.py | 4 +- Lib/test/test_xmlrpc.py | 2 +- Lib/unittest/runner.py | 2 +- Lib/unittest/test/test_assertions.py | 18 +- Lib/unittest/test/test_loader.py | 8 +- Lib/xml/etree/ElementPath.py | 22 +-- Lib/xml/etree/ElementTree.py | 2 +- 67 files changed, 286 insertions(+), 286 deletions(-) (limited to 'Lib') diff --git a/Lib/_osx_support.py b/Lib/_osx_support.py index 13fcd8b8d2..eadf06f20e 100644 --- a/Lib/_osx_support.py +++ b/Lib/_osx_support.py @@ -210,7 +210,7 @@ def _remove_universal_flags(_config_vars): # Do not alter a config var explicitly overridden by env var if cv in _config_vars and cv not in os.environ: flags = _config_vars[cv] - flags = re.sub('-arch\s+\w+\s', ' ', flags, re.ASCII) + flags = re.sub(r'-arch\s+\w+\s', ' ', flags, re.ASCII) flags = re.sub('-isysroot [^ \t]*', ' ', flags) _save_modified_value(_config_vars, cv, flags) @@ -232,7 +232,7 @@ def _remove_unsupported_archs(_config_vars): if 'CC' in os.environ: return _config_vars - if re.search('-arch\s+ppc', _config_vars['CFLAGS']) is not None: + if re.search(r'-arch\s+ppc', _config_vars['CFLAGS']) is not None: # NOTE: Cannot use subprocess here because of bootstrap # issues when building Python itself status = os.system( @@ -251,7 +251,7 @@ def _remove_unsupported_archs(_config_vars): for cv in _UNIVERSAL_CONFIG_VARS: if cv in _config_vars and cv not in os.environ: flags = _config_vars[cv] - flags = re.sub('-arch\s+ppc\w*\s', ' ', flags) + flags = re.sub(r'-arch\s+ppc\w*\s', ' ', flags) _save_modified_value(_config_vars, cv, flags) return _config_vars @@ -267,7 +267,7 @@ def _override_all_archs(_config_vars): for cv in _UNIVERSAL_CONFIG_VARS: if cv in _config_vars and '-arch' in _config_vars[cv]: flags = _config_vars[cv] - flags = re.sub('-arch\s+\w+\s', ' ', flags) + flags = re.sub(r'-arch\s+\w+\s', ' ', flags) flags = flags + ' ' + arch _save_modified_value(_config_vars, cv, flags) @@ -465,7 +465,7 @@ def get_platform_osx(_config_vars, osname, release, machine): machine = 'fat' - archs = re.findall('-arch\s+(\S+)', cflags) + archs = re.findall(r'-arch\s+(\S+)', cflags) archs = tuple(sorted(set(archs))) if len(archs) == 1: diff --git a/Lib/csv.py b/Lib/csv.py index 2e2303a28e..0481ea5586 100644 --- a/Lib/csv.py +++ b/Lib/csv.py @@ -215,10 +215,10 @@ class Sniffer: """ matches = [] - for restr in ('(?P[^\w\n"\'])(?P ?)(?P["\']).*?(?P=quote)(?P=delim)', # ,".*?", - '(?:^|\n)(?P["\']).*?(?P=quote)(?P[^\w\n"\'])(?P ?)', # ".*?", - '(?P>[^\w\n"\'])(?P ?)(?P["\']).*?(?P=quote)(?:$|\n)', # ,".*?" - '(?:^|\n)(?P["\']).*?(?P=quote)(?:$|\n)'): # ".*?" (no delim, no space) + for restr in (r'(?P[^\w\n"\'])(?P ?)(?P["\']).*?(?P=quote)(?P=delim)', # ,".*?", + r'(?:^|\n)(?P["\']).*?(?P=quote)(?P[^\w\n"\'])(?P ?)', # ".*?", + r'(?P>[^\w\n"\'])(?P ?)(?P["\']).*?(?P=quote)(?:$|\n)', # ,".*?" + r'(?:^|\n)(?P["\']).*?(?P=quote)(?:$|\n)'): # ".*?" (no delim, no space) regexp = re.compile(restr, re.DOTALL | re.MULTILINE) matches = regexp.findall(data) if matches: diff --git a/Lib/difflib.py b/Lib/difflib.py index 076bbac01d..2095a5e517 100644 --- a/Lib/difflib.py +++ b/Lib/difflib.py @@ -1415,7 +1415,7 @@ def _mdiff(fromlines, tolines, context=None, linejunk=None, import re # regular expression for finding intraline change indices - change_re = re.compile('(\++|\-+|\^+)') + change_re = re.compile(r'(\++|\-+|\^+)') # create the difference iterator to generate the differences diff_lines_iterator = ndiff(fromlines,tolines,linejunk,charjunk) diff --git a/Lib/distutils/cmd.py b/Lib/distutils/cmd.py index b5d9dc387d..939f795945 100644 --- a/Lib/distutils/cmd.py +++ b/Lib/distutils/cmd.py @@ -221,7 +221,7 @@ class Command: self._ensure_stringlike(option, "string", default) def ensure_string_list(self, option): - """Ensure that 'option' is a list of strings. If 'option' is + r"""Ensure that 'option' is a list of strings. If 'option' is currently a string, we split it either on /,\s*/ or /\s+/, so "foo bar baz", "foo,bar,baz", and "foo, bar baz" all become ["foo", "bar", "baz"]. diff --git a/Lib/distutils/command/bdist_msi.py b/Lib/distutils/command/bdist_msi.py index f6c21aee44..a4bd5a589d 100644 --- a/Lib/distutils/command/bdist_msi.py +++ b/Lib/distutils/command/bdist_msi.py @@ -623,7 +623,7 @@ class bdist_msi(Command): cost = PyDialog(db, "DiskCostDlg", x, y, w, h, modal, title, "OK", "OK", "OK", bitmap=False) cost.text("Title", 15, 6, 200, 15, 0x30003, - "{\DlgFontBold8}Disk Space Requirements") + r"{\DlgFontBold8}Disk Space Requirements") cost.text("Description", 20, 20, 280, 20, 0x30003, "The disk space required for the installation of the selected features.") cost.text("Text", 20, 53, 330, 60, 3, @@ -670,7 +670,7 @@ class bdist_msi(Command): progress = PyDialog(db, "ProgressDlg", x, y, w, h, modeless, title, "Cancel", "Cancel", "Cancel", bitmap=False) progress.text("Title", 20, 15, 200, 15, 0x30003, - "{\DlgFontBold8}[Progress1] [ProductName]") + r"{\DlgFontBold8}[Progress1] [ProductName]") progress.text("Text", 35, 65, 300, 30, 3, "Please wait while the Installer [Progress2] [ProductName]. " "This may take several minutes.") diff --git a/Lib/distutils/command/build_scripts.py b/Lib/distutils/command/build_scripts.py index 90a8380a04..ccc70e6465 100644 --- a/Lib/distutils/command/build_scripts.py +++ b/Lib/distutils/command/build_scripts.py @@ -51,7 +51,7 @@ class build_scripts(Command): def copy_scripts(self): - """Copy each script listed in 'self.scripts'; if it's marked as a + r"""Copy each script listed in 'self.scripts'; if it's marked as a Python script in the Unix way (first line matches 'first_line_re', ie. starts with "\#!" and contains "python"), then adjust the first line to refer to the current Python interpreter as we copy. diff --git a/Lib/distutils/cygwinccompiler.py b/Lib/distutils/cygwinccompiler.py index c879646c0f..1c36990347 100644 --- a/Lib/distutils/cygwinccompiler.py +++ b/Lib/distutils/cygwinccompiler.py @@ -368,7 +368,7 @@ def check_config_h(): return (CONFIG_H_UNCERTAIN, "couldn't read '%s': %s" % (fn, exc.strerror)) -RE_VERSION = re.compile(b'(\d+\.\d+(\.\d+)*)') +RE_VERSION = re.compile(br'(\d+\.\d+(\.\d+)*)') def _find_exe_version(cmd): """Find the version of an executable by running `cmd` in the shell. diff --git a/Lib/distutils/msvc9compiler.py b/Lib/distutils/msvc9compiler.py index 0b1fd19ff6..2119127622 100644 --- a/Lib/distutils/msvc9compiler.py +++ b/Lib/distutils/msvc9compiler.py @@ -716,7 +716,7 @@ class MSVCCompiler(CCompiler) : r"""VC\d{2}\.CRT("|').*?(/>|)""", re.DOTALL) manifest_buf = re.sub(pattern, "", manifest_buf) - pattern = "\s*" + pattern = r"\s*" manifest_buf = re.sub(pattern, "", manifest_buf) # Now see if any other assemblies are referenced - if not, we # don't want a manifest embedded. diff --git a/Lib/distutils/sysconfig.py b/Lib/distutils/sysconfig.py index f72b7f5a19..681359870c 100644 --- a/Lib/distutils/sysconfig.py +++ b/Lib/distutils/sysconfig.py @@ -278,7 +278,7 @@ def parse_config_h(fp, g=None): # Regexes needed for parsing Makefile (and similar syntaxes, # like old-style Setup files). -_variable_rx = re.compile("([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)") +_variable_rx = re.compile(r"([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)") _findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)") _findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}") diff --git a/Lib/distutils/versionpredicate.py b/Lib/distutils/versionpredicate.py index b0dd9f45bf..062c98f248 100644 --- a/Lib/distutils/versionpredicate.py +++ b/Lib/distutils/versionpredicate.py @@ -154,7 +154,7 @@ def split_provision(value): global _provision_rx if _provision_rx is None: _provision_rx = re.compile( - "([a-zA-Z_]\w*(?:\.[a-zA-Z_]\w*)*)(?:\s*\(\s*([^)\s]+)\s*\))?$", + r"([a-zA-Z_]\w*(?:\.[a-zA-Z_]\w*)*)(?:\s*\(\s*([^)\s]+)\s*\))?$", re.ASCII) value = value.strip() m = _provision_rx.match(value) diff --git a/Lib/doctest.py b/Lib/doctest.py index 5630220c5f..0b78544d8d 100644 --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -765,7 +765,7 @@ class DocTestParser: # This regular expression finds the indentation of every non-blank # line in a string. - _INDENT_RE = re.compile('^([ ]*)(?=\S)', re.MULTILINE) + _INDENT_RE = re.compile(r'^([ ]*)(?=\S)', re.MULTILINE) def _min_indent(self, s): "Return the minimum indentation of any non-blank line in `s`" @@ -1106,7 +1106,7 @@ class DocTestFinder: if lineno is not None: if source_lines is None: return lineno+1 - pat = re.compile('(^|.*:)\s*\w*("|\')') + pat = re.compile(r'(^|.*:)\s*\w*("|\')') for lineno in range(lineno, len(source_lines)): if pat.match(source_lines[lineno]): return lineno @@ -1608,11 +1608,11 @@ class OutputChecker: # blank line, unless the DONT_ACCEPT_BLANKLINE flag is used. if not (optionflags & DONT_ACCEPT_BLANKLINE): # Replace in want with a blank line. - want = re.sub('(?m)^%s\s*?$' % re.escape(BLANKLINE_MARKER), + want = re.sub(r'(?m)^%s\s*?$' % re.escape(BLANKLINE_MARKER), '', want) # If a line in got contains only spaces, then remove the # spaces. - got = re.sub('(?m)^\s*?$', '', got) + got = re.sub(r'(?m)^\s*?$', '', got) if got == want: return True diff --git a/Lib/email/_header_value_parser.py b/Lib/email/_header_value_parser.py index 5df9511e8d..57d01fbcb0 100644 --- a/Lib/email/_header_value_parser.py +++ b/Lib/email/_header_value_parser.py @@ -652,8 +652,8 @@ class Comment(WhiteSpaceTokenList): if value.token_type == 'comment': return str(value) return str(value).replace('\\', '\\\\').replace( - '(', '\(').replace( - ')', '\)') + '(', r'\(').replace( + ')', r'\)') @property def content(self): @@ -1356,15 +1356,15 @@ RouteComponentMarker = ValueTerminal('@', 'route-component-marker') _wsp_splitter = re.compile(r'([{}]+)'.format(''.join(WSP))).split _non_atom_end_matcher = re.compile(r"[^{}]+".format( - ''.join(ATOM_ENDS).replace('\\','\\\\').replace(']','\]'))).match + ''.join(ATOM_ENDS).replace('\\','\\\\').replace(']',r'\]'))).match _non_printable_finder = re.compile(r"[\x00-\x20\x7F]").findall _non_token_end_matcher = re.compile(r"[^{}]+".format( - ''.join(TOKEN_ENDS).replace('\\','\\\\').replace(']','\]'))).match + ''.join(TOKEN_ENDS).replace('\\','\\\\').replace(']',r'\]'))).match _non_attribute_end_matcher = re.compile(r"[^{}]+".format( - ''.join(ATTRIBUTE_ENDS).replace('\\','\\\\').replace(']','\]'))).match + ''.join(ATTRIBUTE_ENDS).replace('\\','\\\\').replace(']',r'\]'))).match _non_extended_attribute_end_matcher = re.compile(r"[^{}]+".format( ''.join(EXTENDED_ATTRIBUTE_ENDS).replace( - '\\','\\\\').replace(']','\]'))).match + '\\','\\\\').replace(']',r'\]'))).match def _validate_xtext(xtext): """If input token contains ASCII non-printables, register a defect.""" @@ -1517,7 +1517,7 @@ def get_unstructured(value): return unstructured def get_qp_ctext(value): - """ctext = + r"""ctext = This is not the RFC ctext, since we are handling nested comments in comment and unquoting quoted-pairs here. We allow anything except the '()' @@ -1878,7 +1878,7 @@ def get_obs_local_part(value): return obs_local_part, value def get_dtext(value): - """ dtext = / obs-dtext + r""" dtext = / obs-dtext obs-dtext = obs-NO-WS-CTL / quoted-pair We allow anything except the excluded characters, but if we find any diff --git a/Lib/email/feedparser.py b/Lib/email/feedparser.py index 0b312e567b..2fa77d7afc 100644 --- a/Lib/email/feedparser.py +++ b/Lib/email/feedparser.py @@ -29,10 +29,10 @@ from email._policybase import compat32 from collections import deque from io import StringIO -NLCRE = re.compile('\r\n|\r|\n') -NLCRE_bol = re.compile('(\r\n|\r|\n)') -NLCRE_eol = re.compile('(\r\n|\r|\n)\Z') -NLCRE_crack = re.compile('(\r\n|\r|\n)') +NLCRE = re.compile(r'\r\n|\r|\n') +NLCRE_bol = re.compile(r'(\r\n|\r|\n)') +NLCRE_eol = re.compile(r'(\r\n|\r|\n)\Z') +NLCRE_crack = re.compile(r'(\r\n|\r|\n)') # RFC 2822 $3.6.8 Optional fields. ftext is %d33-57 / %d59-126, Any character # except controls, SP, and ":". headerRE = re.compile(r'^(From |[\041-\071\073-\176]*:|[\t ])') diff --git a/Lib/fnmatch.py b/Lib/fnmatch.py index 6330b0cfda..07b12295df 100644 --- a/Lib/fnmatch.py +++ b/Lib/fnmatch.py @@ -106,4 +106,4 @@ def translate(pat): res = '%s[%s]' % (res, stuff) else: res = res + re.escape(c) - return res + '\Z(?ms)' + return res + r'\Z(?ms)' diff --git a/Lib/ftplib.py b/Lib/ftplib.py index 2ab1d568d7..ee2a137a5c 100644 --- a/Lib/ftplib.py +++ b/Lib/ftplib.py @@ -821,7 +821,7 @@ def parse150(resp): if _150_re is None: import re _150_re = re.compile( - "150 .* \((\d+) bytes\)", re.IGNORECASE | re.ASCII) + r"150 .* \((\d+) bytes\)", re.IGNORECASE | re.ASCII) m = _150_re.match(resp) if not m: return None diff --git a/Lib/html/parser.py b/Lib/html/parser.py index b781c63f33..ef869bc72d 100644 --- a/Lib/html/parser.py +++ b/Lib/html/parser.py @@ -34,7 +34,7 @@ commentclose = re.compile(r'--\s*>') # explode, so don't do it. # see http://www.w3.org/TR/html5/tokenization.html#tag-open-state # and http://www.w3.org/TR/html5/tokenization.html#tag-name-state -tagfind_tolerant = re.compile('([a-zA-Z][^\t\n\r\f />\x00]*)(?:\s|/(?!>))*') +tagfind_tolerant = re.compile(r'([a-zA-Z][^\t\n\r\f />\x00]*)(?:\s|/(?!>))*') attrfind_tolerant = re.compile( r'((?<=[\'"\s/])[^\s/>][^\s/=>]*)(\s*=+\s*' r'(\'[^\']*\'|"[^"]*"|(?![\'"])[^>\s]*))?(?:\s|/(?!>))*') @@ -56,7 +56,7 @@ locatestarttagend_tolerant = re.compile(r""" endendtag = re.compile('>') # the HTML 5 spec, section 8.1.2.2, doesn't allow spaces between # ') +endtagfind = re.compile(r'') diff --git a/Lib/http/client.py b/Lib/http/client.py index 230bccec98..a1c4ab9482 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -1,4 +1,4 @@ -"""HTTP/1.1 client library +r"""HTTP/1.1 client library diff --git a/Lib/http/cookiejar.py b/Lib/http/cookiejar.py index 6d4572af03..adf956d66a 100644 --- a/Lib/http/cookiejar.py +++ b/Lib/http/cookiejar.py @@ -200,7 +200,7 @@ def _str2time(day, mon, yr, hr, min, sec, tz): STRICT_DATE_RE = re.compile( r"^[SMTWF][a-z][a-z], (\d\d) ([JFMASOND][a-z][a-z]) " - "(\d\d\d\d) (\d\d):(\d\d):(\d\d) GMT$", re.ASCII) + r"(\d\d\d\d) (\d\d):(\d\d):(\d\d) GMT$", re.ASCII) WEEKDAY_RE = re.compile( r"^(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)[a-z]*,?\s*", re.I | re.ASCII) LOOSE_HTTP_DATE_RE = re.compile( @@ -277,7 +277,7 @@ def http2time(text): return _str2time(day, mon, yr, hr, min, sec, tz) ISO_DATE_RE = re.compile( - """^ + r"""^ (\d{4}) # year [-\/]? (\d\d?) # numerical month @@ -411,7 +411,7 @@ def split_header_words(header_values): pairs = [] else: # skip junk - non_junk, nr_junk_chars = re.subn("^[=\s;]*", "", text) + non_junk, nr_junk_chars = re.subn(r"^[=\s;]*", "", text) assert nr_junk_chars > 0, ( "split_header_words bug: '%s', '%s', %s" % (orig_text, text, pairs)) diff --git a/Lib/http/cookies.py b/Lib/http/cookies.py index a73fe387f8..f078da5469 100644 --- a/Lib/http/cookies.py +++ b/Lib/http/cookies.py @@ -456,7 +456,7 @@ class Morsel(dict): # _LegalKeyChars = r"\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=" -_LegalValueChars = _LegalKeyChars + '\[\]' +_LegalValueChars = _LegalKeyChars + r'\[\]' _CookiePattern = re.compile(r""" (?x) # This is a verbose pattern \s* # Optional whitespace at start of cookie diff --git a/Lib/idlelib/calltips.py b/Lib/idlelib/calltips.py index abcc1427c4..4c5aea2acb 100644 --- a/Lib/idlelib/calltips.py +++ b/Lib/idlelib/calltips.py @@ -120,7 +120,7 @@ def get_entity(expression): _MAX_COLS = 85 _MAX_LINES = 5 # enough for bytes _INDENT = ' '*4 # for wrapped signatures -_first_param = re.compile('(?<=\()\w*\,?\s*') +_first_param = re.compile(r'(?<=\()\w*\,?\s*') _default_callable_argspec = "See source or doc" diff --git a/Lib/idlelib/idle_test/test_replace.py b/Lib/idlelib/idle_test/test_replace.py index 7afd4d9e6c..9913ed2b7c 100644 --- a/Lib/idlelib/idle_test/test_replace.py +++ b/Lib/idlelib/idle_test/test_replace.py @@ -91,7 +91,7 @@ class ReplaceDialogTest(unittest.TestCase): text.mark_set('insert', 'end') text.insert('insert', '\nline42:') before_text = text.get('1.0', 'end') - pv.set('[a-z][\d]+') + pv.set(r'[a-z][\d]+') replace() after_text = text.get('1.0', 'end') equal(before_text, after_text) @@ -192,7 +192,7 @@ class ReplaceDialogTest(unittest.TestCase): self.engine.revar.set(True) before_text = text.get('1.0', 'end') - pv.set('[a-z][\d]+') + pv.set(r'[a-z][\d]+') rv.set('hello') replace() after_text = text.get('1.0', 'end') @@ -207,7 +207,7 @@ class ReplaceDialogTest(unittest.TestCase): self.assertIn('error', showerror.title) self.assertIn('Empty', showerror.message) - pv.set('[\d') + pv.set(r'[\d') replace() self.assertIn('error', showerror.title) self.assertIn('Pattern', showerror.message) diff --git a/Lib/idlelib/idle_test/test_searchengine.py b/Lib/idlelib/idle_test/test_searchengine.py index 7e6f8b71a8..b3aa8eb812 100644 --- a/Lib/idlelib/idle_test/test_searchengine.py +++ b/Lib/idlelib/idle_test/test_searchengine.py @@ -139,10 +139,10 @@ class SearchEngineTest(unittest.TestCase): def test_setcookedpat(self): engine = self.engine - engine.setcookedpat('\s') - self.assertEqual(engine.getpat(), '\s') + engine.setcookedpat(r'\s') + self.assertEqual(engine.getpat(), r'\s') engine.revar.set(1) - engine.setcookedpat('\s') + engine.setcookedpat(r'\s') self.assertEqual(engine.getpat(), r'\\s') def test_getcookedpat(self): @@ -156,10 +156,10 @@ class SearchEngineTest(unittest.TestCase): Equal(engine.getcookedpat(), r'\bhello\b') engine.wordvar.set(False) - engine.setpat('\s') + engine.setpat(r'\s') Equal(engine.getcookedpat(), r'\\s') engine.revar.set(True) - Equal(engine.getcookedpat(), '\s') + Equal(engine.getcookedpat(), r'\s') def test_getprog(self): engine = self.engine @@ -282,7 +282,7 @@ class ForwardBackwardTest(unittest.TestCase): cls.pat = re.compile('target') cls.res = (2, (10, 16)) # line, slice indexes of 'target' cls.failpat = re.compile('xyz') # not in text - cls.emptypat = re.compile('\w*') # empty match possible + cls.emptypat = re.compile(r'\w*') # empty match possible def make_search(self, func): def search(pat, line, col, wrap, ok=0): diff --git a/Lib/idlelib/paragraph.py b/Lib/idlelib/paragraph.py index 5d358eed05..f11bdaeb77 100644 --- a/Lib/idlelib/paragraph.py +++ b/Lib/idlelib/paragraph.py @@ -130,7 +130,7 @@ def reformat_paragraph(data, limit): partial = indent1 while i < n and not is_all_white(lines[i]): # XXX Should take double space after period (etc.) into account - words = re.split("(\s+)", lines[i]) + words = re.split(r"(\s+)", lines[i]) for j in range(0, len(words), 2): word = words[j] if not word: diff --git a/Lib/imaplib.py b/Lib/imaplib.py index a63ba8dbe0..965ed83f2b 100644 --- a/Lib/imaplib.py +++ b/Lib/imaplib.py @@ -132,7 +132,7 @@ _Untagged_status = br'\* (?P\d+) (?P[A-Z-]+)( (?P.*))?' class IMAP4: - """IMAP4 client class. + r"""IMAP4 client class. Instantiate with: IMAP4([host[, port]]) @@ -1535,7 +1535,7 @@ if __name__ == '__main__': ('select', ('/tmp/yyz 2',)), ('search', (None, 'SUBJECT', 'test')), ('fetch', ('1', '(FLAGS INTERNALDATE RFC822)')), - ('store', ('1', 'FLAGS', '(\Deleted)')), + ('store', ('1', 'FLAGS', r'(\Deleted)')), ('namespace', ()), ('expunge', ()), ('recent', ()), diff --git a/Lib/msilib/__init__.py b/Lib/msilib/__init__.py index 823644a006..f0370c2a0f 100644 --- a/Lib/msilib/__init__.py +++ b/Lib/msilib/__init__.py @@ -289,7 +289,7 @@ class Directory: def make_short(self, file): oldfile = file file = file.replace('+', '_') - file = ''.join(c for c in file if not c in ' "/\[]:;=,') + file = ''.join(c for c in file if not c in r' "/\[]:;=,') parts = file.split(".") if len(parts) > 1: prefix = "".join(parts[:-1]).upper() diff --git a/Lib/platform.py b/Lib/platform.py index a5dd763907..9f7b59f9e6 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -251,13 +251,13 @@ def _dist_try_harder(distname, version, id): _release_filename = re.compile(r'(\w+)[-_](release|version)', re.ASCII) _lsb_release_version = re.compile(r'(.+)' - ' release ' - '([\d.]+)' - '[^(]*(?:\((.+)\))?', re.ASCII) + r' release ' + r'([\d.]+)' + r'[^(]*(?:\((.+)\))?', re.ASCII) _release_version = re.compile(r'([^0-9]+)' - '(?: release )?' - '([\d.]+)' - '[^(]*(?:\((.+)\))?', re.ASCII) + r'(?: release )?' + r'([\d.]+)' + r'[^(]*(?:\((.+)\))?', re.ASCII) # See also http://www.novell.com/coolsolutions/feature/11251.html # and http://linuxmafia.com/faq/Admin/release-files.html @@ -407,8 +407,8 @@ def _norm_version(version, build=''): return version _ver_output = re.compile(r'(?:([\w ]+) ([\w.]+) ' - '.*' - '\[.* ([\d.]+)\])') + r'.*' + r'\[.* ([\d.]+)\])') # Examples of VER command output: # @@ -1153,22 +1153,22 @@ _sys_version_parser = re.compile( _ironpython_sys_version_parser = re.compile( r'IronPython\s*' - '([\d\.]+)' - '(?: \(([\d\.]+)\))?' - ' on (.NET [\d\.]+)', re.ASCII) + r'([\d\.]+)' + r'(?: \(([\d\.]+)\))?' + r' on (.NET [\d\.]+)', re.ASCII) # IronPython covering 2.6 and 2.7 _ironpython26_sys_version_parser = re.compile( r'([\d.]+)\s*' - '\(IronPython\s*' - '[\d.]+\s*' - '\(([\d.]+)\) on ([\w.]+ [\d.]+(?: \(\d+-bit\))?)\)' + r'\(IronPython\s*' + r'[\d.]+\s*' + r'\(([\d.]+)\) on ([\w.]+ [\d.]+(?: \(\d+-bit\))?)\)' ) _pypy_sys_version_parser = re.compile( r'([\w.+]+)\s*' - '\(#?([^,]+),\s*([\w ]+),\s*([\w :]+)\)\s*' - '\[PyPy [^\]]+\]?') + r'\(#?([^,]+),\s*([\w ]+),\s*([\w :]+)\)\s*' + r'\[PyPy [^\]]+\]?') _sys_version_cache = {} @@ -1403,7 +1403,7 @@ def platform(aliased=0, terse=0): # see issue #1322 for more information warnings.filterwarnings( 'ignore', - 'dist\(\) and linux_distribution\(\) ' + r'dist\(\) and linux_distribution\(\) ' 'functions are deprecated .*', PendingDeprecationWarning, ) diff --git a/Lib/string.py b/Lib/string.py index 7298c89087..c902007643 100644 --- a/Lib/string.py +++ b/Lib/string.py @@ -28,7 +28,7 @@ ascii_letters = ascii_lowercase + ascii_uppercase digits = '0123456789' hexdigits = digits + 'abcdef' + 'ABCDEF' octdigits = '01234567' -punctuation = """!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~""" +punctuation = r"""!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~""" printable = digits + ascii_letters + punctuation + whitespace # Functions which aren't available as string methods. diff --git a/Lib/sysconfig.py b/Lib/sysconfig.py index ef530617a3..7b78440ed3 100644 --- a/Lib/sysconfig.py +++ b/Lib/sysconfig.py @@ -215,7 +215,7 @@ def _parse_makefile(filename, vars=None): # Regexes needed for parsing Makefile (and similar syntaxes, # like old-style Setup files). import re - _variable_rx = re.compile("([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)") + _variable_rx = re.compile(r"([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)") _findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)") _findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}") diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index d88cd07618..c00846c7b0 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -3857,7 +3857,7 @@ class TestSemaphoreTracker(unittest.TestCase): p.stderr.close() expected = 'semaphore_tracker: There appear to be 2 leaked semaphores' self.assertRegex(err, expected) - self.assertRegex(err, 'semaphore_tracker: %r: \[Errno' % name1) + self.assertRegex(err, r'semaphore_tracker: %r: \[Errno' % name1) # # Mixins diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 86c937388e..988c6f722e 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -4001,12 +4001,12 @@ class Oddballs(unittest.TestCase): datetime(xx, xx, xx, xx, xx, xx, xx)) with self.assertRaisesRegex(TypeError, '^an integer is required ' - '\(got type str\)$'): + r'\(got type str\)$'): datetime(10, 10, '10') f10 = Number(10.9) with self.assertRaisesRegex(TypeError, '^__int__ returned non-int ' - '\(type float\)$'): + r'\(type float\)$'): datetime(10, 10, f10) class Float(float): diff --git a/Lib/test/re_tests.py b/Lib/test/re_tests.py index 8c158f883b..d3692f859a 100755 --- a/Lib/test/re_tests.py +++ b/Lib/test/re_tests.py @@ -158,7 +158,7 @@ tests = [ ('(abc', '-', SYNTAX_ERROR), ('a]', 'a]', SUCCEED, 'found', 'a]'), ('a[]]b', 'a]b', SUCCEED, 'found', 'a]b'), - ('a[\]]b', 'a]b', SUCCEED, 'found', 'a]b'), + ('a[\\]]b', 'a]b', SUCCEED, 'found', 'a]b'), ('a[^bc]d', 'aed', SUCCEED, 'found', 'aed'), ('a[^bc]d', 'abd', FAIL), ('a[^-b]c', 'adc', SUCCEED, 'found', 'adc'), @@ -551,7 +551,7 @@ tests = [ # lookbehind: split by : but not if it is escaped by -. ('(?= 39 except OSError: can = False diff --git a/Lib/test/test_asyncio/test_streams.py b/Lib/test/test_asyncio/test_streams.py index 1783d5f630..35557c3ce4 100644 --- a/Lib/test/test_asyncio/test_streams.py +++ b/Lib/test/test_asyncio/test_streams.py @@ -831,7 +831,7 @@ os.close(fd) stream._waiter = asyncio.Future(loop=self.loop) self.assertRegex( repr(stream), - ">") + r">") stream._waiter.set_result(None) self.loop.run_until_complete(stream._waiter) stream._waiter = None diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 486f445069..c0343eff68 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -83,7 +83,7 @@ test_conv_no_sign = [ ('', ValueError), (' ', ValueError), (' \t\t ', ValueError), - (str(b'\u0663\u0661\u0664 ','raw-unicode-escape'), 314), + (str(br'\u0663\u0661\u0664 ','raw-unicode-escape'), 314), (chr(0x200), ValueError), ] @@ -105,7 +105,7 @@ test_conv_sign = [ ('', ValueError), (' ', ValueError), (' \t\t ', ValueError), - (str(b'\u0663\u0661\u0664 ','raw-unicode-escape'), 314), + (str(br'\u0663\u0661\u0664 ','raw-unicode-escape'), 314), (chr(0x200), ValueError), ] diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index 6852381d01..5521e76112 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -533,21 +533,21 @@ class SkipitemTest(unittest.TestCase): parse((1, 2, 3), {}, b'OOO', ['', '', 'a']) parse((1, 2), {'a': 3}, b'OOO', ['', '', 'a']) with self.assertRaisesRegex(TypeError, - 'Function takes at least 2 positional arguments \(1 given\)'): + r'Function takes at least 2 positional arguments \(1 given\)'): parse((1,), {'a': 3}, b'OOO', ['', '', 'a']) parse((1,), {}, b'O|OO', ['', '', 'a']) with self.assertRaisesRegex(TypeError, - 'Function takes at least 1 positional arguments \(0 given\)'): + r'Function takes at least 1 positional arguments \(0 given\)'): parse((), {}, b'O|OO', ['', '', 'a']) parse((1, 2), {'a': 3}, b'OO$O', ['', '', 'a']) with self.assertRaisesRegex(TypeError, - 'Function takes exactly 2 positional arguments \(1 given\)'): + r'Function takes exactly 2 positional arguments \(1 given\)'): parse((1,), {'a': 3}, b'OO$O', ['', '', 'a']) parse((1,), {}, b'O|O$O', ['', '', 'a']) with self.assertRaisesRegex(TypeError, - 'Function takes at least 1 positional arguments \(0 given\)'): + r'Function takes at least 1 positional arguments \(0 given\)'): parse((), {}, b'O|O$O', ['', '', 'a']) - with self.assertRaisesRegex(SystemError, 'Empty parameter name after \$'): + with self.assertRaisesRegex(SystemError, r'Empty parameter name after \$'): parse((1,), {}, b'O|$OO', ['', '', 'a']) with self.assertRaisesRegex(SystemError, 'Empty keyword'): parse((1,), {}, b'O|OO', ['', 'a', '']) diff --git a/Lib/test/test_cgi.py b/Lib/test/test_cgi.py index 164784923a..637322137d 100644 --- a/Lib/test/test_cgi.py +++ b/Lib/test/test_cgi.py @@ -148,7 +148,7 @@ class CgiTests(unittest.TestCase): def test_escape(self): # cgi.escape() is deprecated. with warnings.catch_warnings(): - warnings.filterwarnings('ignore', 'cgi\.escape', + warnings.filterwarnings('ignore', r'cgi\.escape', DeprecationWarning) self.assertEqual("test & string", cgi.escape("test & string")) self.assertEqual("<test string>", cgi.escape("")) diff --git a/Lib/test/test_codeccallbacks.py b/Lib/test/test_codeccallbacks.py index c8cdacf718..6a3e993265 100644 --- a/Lib/test/test_codeccallbacks.py +++ b/Lib/test/test_codeccallbacks.py @@ -280,12 +280,12 @@ class CodecCallbackTest(unittest.TestCase): ) self.assertEqual( - b"\\u3042\u3xxx".decode("unicode-escape", "test.handler1"), + b"\\u3042\\u3xxx".decode("unicode-escape", "test.handler1"), "\u3042[<92><117><51>]xxx" ) self.assertEqual( - b"\\u3042\u3xx".decode("unicode-escape", "test.handler1"), + b"\\u3042\\u3xx".decode("unicode-escape", "test.handler1"), "\u3042[<92><117><51>]xx" ) diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index 825a7dd95f..1af552405c 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -2703,8 +2703,8 @@ class TransformCodecTest(unittest.TestCase): bad_input = "bad input type" for encoding in bytes_transform_encodings: with self.subTest(encoding=encoding): - fmt = ( "{!r} is not a text encoding; " - "use codecs.encode\(\) to handle arbitrary codecs") + fmt = (r"{!r} is not a text encoding; " + r"use codecs.encode\(\) to handle arbitrary codecs") msg = fmt.format(encoding) with self.assertRaisesRegex(LookupError, msg) as failure: bad_input.encode(encoding) @@ -2713,7 +2713,7 @@ class TransformCodecTest(unittest.TestCase): def test_text_to_binary_blacklists_text_transforms(self): # Check str.encode gives a good error message for str -> str codecs msg = (r"^'rot_13' is not a text encoding; " - "use codecs.encode\(\) to handle arbitrary codecs") + r"use codecs.encode\(\) to handle arbitrary codecs") with self.assertRaisesRegex(LookupError, msg): "just an example message".encode("rot_13") @@ -2725,7 +2725,7 @@ class TransformCodecTest(unittest.TestCase): with self.subTest(encoding=encoding): encoded_data = codecs.encode(data, encoding) fmt = (r"{!r} is not a text encoding; " - "use codecs.decode\(\) to handle arbitrary codecs") + r"use codecs.decode\(\) to handle arbitrary codecs") msg = fmt.format(encoding) with self.assertRaisesRegex(LookupError, msg): encoded_data.decode(encoding) @@ -2737,7 +2737,7 @@ class TransformCodecTest(unittest.TestCase): for bad_input in (b"immutable", bytearray(b"mutable")): with self.subTest(bad_input=bad_input): msg = (r"^'rot_13' is not a text encoding; " - "use codecs.decode\(\) to handle arbitrary codecs") + r"use codecs.decode\(\) to handle arbitrary codecs") with self.assertRaisesRegex(LookupError, msg) as failure: bad_input.decode("rot_13") self.assertIsNone(failure.exception.__cause__) @@ -2956,12 +2956,12 @@ class ExceptionChainingTest(unittest.TestCase): self.assertEqual(decoded, b"not str!") # Text model methods should complain fmt = (r"^{!r} encoder returned 'str' instead of 'bytes'; " - "use codecs.encode\(\) to encode to arbitrary types$") + r"use codecs.encode\(\) to encode to arbitrary types$") msg = fmt.format(self.codec_name) with self.assertRaisesRegex(TypeError, msg): "str_input".encode(self.codec_name) fmt = (r"^{!r} decoder returned 'bytes' instead of 'str'; " - "use codecs.decode\(\) to decode to arbitrary types$") + r"use codecs.decode\(\) to decode to arbitrary types$") msg = fmt.format(self.codec_name) with self.assertRaisesRegex(TypeError, msg): b"bytes input".decode(self.codec_name) diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py index e52654c14d..ee2482d2a3 100644 --- a/Lib/test/test_coroutines.py +++ b/Lib/test/test_coroutines.py @@ -891,7 +891,7 @@ class CoroutineTest(unittest.TestCase): return await Awaitable() with self.assertRaisesRegex( - TypeError, "__await__\(\) returned a coroutine"): + TypeError, r"__await__\(\) returned a coroutine"): run_async(foo()) @@ -1333,7 +1333,7 @@ class CoroutineTest(unittest.TestCase): with self.assertRaisesRegex( TypeError, - "async for' received an invalid object.*__aiter.*\: I"): + r"async for' received an invalid object.*__aiter.*\: I"): run_async(foo()) @@ -1667,8 +1667,8 @@ class SysSetCoroWrapperTest(unittest.TestCase): try: with silence_coro_gc(), self.assertRaisesRegex( RuntimeError, - "coroutine wrapper.*\.wrapper at 0x.*attempted to " - "recursively wrap .* wrap .*"): + r"coroutine wrapper.*\.wrapper at 0x.*attempted to " + r"recursively wrap .* wrap .*"): foo() finally: diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py index a9520a5572..2258c6b1ba 100644 --- a/Lib/test/test_doctest.py +++ b/Lib/test/test_doctest.py @@ -291,7 +291,7 @@ constructor: ... ... Non-example text. ... - ... >>> print('another\example') + ... >>> print('another\\example') ... another ... example ... ''' diff --git a/Lib/test/test_email/test__header_value_parser.py b/Lib/test/test_email/test__header_value_parser.py index f7ac0e3ded..26ece6911f 100644 --- a/Lib/test/test_email/test__header_value_parser.py +++ b/Lib/test/test_email/test__header_value_parser.py @@ -581,7 +581,7 @@ class TestParser(TestParserMixin, TestEmailBase): def test_get_comment_quoted_parens(self): self._test_get_x(parser.get_comment, - '(foo\) \(\)bar)', '(foo\) \(\)bar)', ' ', [], '', ['foo) ()bar']) + r'(foo\) \(\)bar)', r'(foo\) \(\)bar)', ' ', [], '', ['foo) ()bar']) def test_get_comment_non_printable(self): self._test_get_x(parser.get_comment, @@ -625,7 +625,7 @@ class TestParser(TestParserMixin, TestEmailBase): def test_get_comment_qs_in_nested_comment(self): comment = self._test_get_x(parser.get_comment, - '(foo (b\)))', '(foo (b\)))', ' ', [], '', ['foo (b\))']) + r'(foo (b\)))', r'(foo (b\)))', ' ', [], '', [r'foo (b\))']) self.assertEqual(comment[2].content, 'b)') # get_cfws diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py index 529d3ef0e3..e95f08df7d 100644 --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -3040,7 +3040,7 @@ class TestMiscellaneous(TestEmailBase): def test_escape_backslashes(self): self.assertEqual( - utils.formataddr(('Arthur \Backslash\ Foobar', 'person@dom.ain')), + utils.formataddr((r'Arthur \Backslash\ Foobar', 'person@dom.ain')), r'"Arthur \\Backslash\\ Foobar" ') a = r'Arthur \Backslash\ Foobar' b = 'person@dom.ain' diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py index 1ff17bbcf4..d2bd2d21e8 100644 --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py @@ -93,7 +93,7 @@ class FaultHandlerTests(unittest.TestCase): header = 'Thread 0x[0-9a-f]+' else: header = 'Stack' - regex = """ + regex = r""" ^{fatal_error} {header} \(most recent call first\): @@ -490,7 +490,7 @@ class FaultHandlerTests(unittest.TestCase): lineno = 8 else: lineno = 10 - regex = """ + regex = r""" ^Thread 0x[0-9a-f]+ \(most recent call first\): (?: File ".*threading.py", line [0-9]+ in [_a-z]+ ){{1,3}} File "", line 23 in run @@ -669,9 +669,9 @@ class FaultHandlerTests(unittest.TestCase): trace = '\n'.join(trace) if not unregister: if all_threads: - regex = 'Current thread 0x[0-9a-f]+ \(most recent call first\):\n' + regex = r'Current thread 0x[0-9a-f]+ \(most recent call first\):\n' else: - regex = 'Stack \(most recent call first\):\n' + regex = r'Stack \(most recent call first\):\n' regex = expected_traceback(14, 32, regex) self.assertRegex(trace, regex) else: diff --git a/Lib/test/test_fnmatch.py b/Lib/test/test_fnmatch.py index fa37f90c27..a5f5832544 100644 --- a/Lib/test/test_fnmatch.py +++ b/Lib/test/test_fnmatch.py @@ -62,14 +62,14 @@ class FnmatchTestCase(unittest.TestCase): class TranslateTestCase(unittest.TestCase): def test_translate(self): - self.assertEqual(translate('*'), '.*\Z(?ms)') - self.assertEqual(translate('?'), '.\Z(?ms)') - self.assertEqual(translate('a?b*'), 'a.b.*\Z(?ms)') - self.assertEqual(translate('[abc]'), '[abc]\Z(?ms)') - self.assertEqual(translate('[]]'), '[]]\Z(?ms)') - self.assertEqual(translate('[!x]'), '[^x]\Z(?ms)') - self.assertEqual(translate('[^x]'), '[\\^x]\Z(?ms)') - self.assertEqual(translate('[x'), '\\[x\Z(?ms)') + self.assertEqual(translate('*'), r'.*\Z(?ms)') + self.assertEqual(translate('?'), r'.\Z(?ms)') + self.assertEqual(translate('a?b*'), r'a.b.*\Z(?ms)') + self.assertEqual(translate('[abc]'), r'[abc]\Z(?ms)') + self.assertEqual(translate('[]]'), r'[]]\Z(?ms)') + self.assertEqual(translate('[!x]'), r'[^x]\Z(?ms)') + self.assertEqual(translate('[^x]'), r'[\^x]\Z(?ms)') + self.assertEqual(translate('[x'), r'\[x\Z(?ms)') class FilterTestCase(unittest.TestCase): diff --git a/Lib/test/test_future.py b/Lib/test/test_future.py index beac993e4d..213b2ba075 100644 --- a/Lib/test/test_future.py +++ b/Lib/test/test_future.py @@ -4,7 +4,7 @@ import unittest from test import support import re -rx = re.compile('\((\S+).py, line (\d+)') +rx = re.compile(r'\((\S+).py, line (\d+)') def get_error_location(msg): mo = rx.search(str(msg)) diff --git a/Lib/test/test_gdb.py b/Lib/test/test_gdb.py index ff651903e9..5fbf154662 100644 --- a/Lib/test/test_gdb.py +++ b/Lib/test/test_gdb.py @@ -244,7 +244,7 @@ class DebuggerTests(unittest.TestCase): # gdb can insert additional '\n' and space characters in various places # in its output, depending on the width of the terminal it's connected # to (using its "wrap_here" function) - m = re.match('.*#0\s+builtin_id\s+\(self\=.*,\s+v=\s*(.*?)\)\s+at\s+\S*Python/bltinmodule.c.*', + m = re.match(r'.*#0\s+builtin_id\s+\(self\=.*,\s+v=\s*(.*?)\)\s+at\s+\S*Python/bltinmodule.c.*', gdb_output, re.DOTALL) if not m: self.fail('Unexpected gdb output: %r\n%s' % (gdb_output, gdb_output)) @@ -552,7 +552,7 @@ class Foo: foo = Foo() foo.an_attr = foo id(foo)''') - self.assertTrue(re.match('\) at remote 0x-?[0-9a-f]+>', + self.assertTrue(re.match(r'\) at remote 0x-?[0-9a-f]+>', gdb_repr), 'Unexpected gdb representation: %r\n%s' % \ (gdb_repr, gdb_output)) @@ -565,7 +565,7 @@ class Foo(object): foo = Foo() foo.an_attr = foo id(foo)''') - self.assertTrue(re.match('\) at remote 0x-?[0-9a-f]+>', + self.assertTrue(re.match(r'\) at remote 0x-?[0-9a-f]+>', gdb_repr), 'Unexpected gdb representation: %r\n%s' % \ (gdb_repr, gdb_output)) @@ -579,7 +579,7 @@ b = Foo() a.an_attr = b b.an_attr = a id(a)''') - self.assertTrue(re.match('\) at remote 0x-?[0-9a-f]+>\) at remote 0x-?[0-9a-f]+>', + self.assertTrue(re.match(r'\) at remote 0x-?[0-9a-f]+>\) at remote 0x-?[0-9a-f]+>', gdb_repr), 'Unexpected gdb representation: %r\n%s' % \ (gdb_repr, gdb_output)) @@ -614,7 +614,7 @@ id(a)''') def test_builtin_method(self): gdb_repr, gdb_output = self.get_gdb_repr('import sys; id(sys.stdout.readlines)') - self.assertTrue(re.match('', + self.assertTrue(re.match(r'', gdb_repr), 'Unexpected gdb representation: %r\n%s' % \ (gdb_repr, gdb_output)) @@ -629,7 +629,7 @@ id(foo.__code__)''', breakpoint='builtin_id', cmds_after_breakpoint=['print (PyFrameObject*)(((PyCodeObject*)v)->co_zombieframe)'] ) - self.assertTrue(re.match('.*\s+\$1 =\s+Frame 0x-?[0-9a-f]+, for file , line 3, in foo \(\)\s+.*', + self.assertTrue(re.match(r'.*\s+\$1 =\s+Frame 0x-?[0-9a-f]+, for file , line 3, in foo \(\)\s+.*', gdb_output, re.DOTALL), 'Unexpected gdb representation: %r\n%s' % (gdb_output, gdb_output)) diff --git a/Lib/test/test_getargs2.py b/Lib/test/test_getargs2.py index 0fbe12dd14..8a194aa03d 100644 --- a/Lib/test/test_getargs2.py +++ b/Lib/test/test_getargs2.py @@ -625,20 +625,20 @@ class KeywordOnly_TestCase(unittest.TestCase): ) # required arg missing with self.assertRaisesRegex(TypeError, - "Required argument 'required' \(pos 1\) not found"): + r"Required argument 'required' \(pos 1\) not found"): getargs_keyword_only(optional=2) with self.assertRaisesRegex(TypeError, - "Required argument 'required' \(pos 1\) not found"): + r"Required argument 'required' \(pos 1\) not found"): getargs_keyword_only(keyword_only=3) def test_too_many_args(self): with self.assertRaisesRegex(TypeError, - "Function takes at most 2 positional arguments \(3 given\)"): + r"Function takes at most 2 positional arguments \(3 given\)"): getargs_keyword_only(1, 2, 3) with self.assertRaisesRegex(TypeError, - "function takes at most 3 arguments \(4 given\)"): + r"function takes at most 3 arguments \(4 given\)"): getargs_keyword_only(1, 2, 3, keyword_only=5) def test_invalid_keyword(self): @@ -673,11 +673,11 @@ class PositionalOnlyAndKeywords_TestCase(unittest.TestCase): self.assertEqual(self.getargs(1), (1, -1, -1)) # required positional arg missing with self.assertRaisesRegex(TypeError, - "Function takes at least 1 positional arguments \(0 given\)"): + r"Function takes at least 1 positional arguments \(0 given\)"): self.getargs() with self.assertRaisesRegex(TypeError, - "Function takes at least 1 positional arguments \(0 given\)"): + r"Function takes at least 1 positional arguments \(0 given\)"): self.getargs(keyword=3) def test_empty_keyword(self): diff --git a/Lib/test/test_htmlparser.py b/Lib/test/test_htmlparser.py index a7f53d382e..326e34290f 100644 --- a/Lib/test/test_htmlparser.py +++ b/Lib/test/test_htmlparser.py @@ -701,7 +701,7 @@ class AttributesTestCase(TestCaseBase): def test_attr_funky_names2(self): self._run_check( - "", + r"", [("starttag", "a", [("$", None)]), ("starttag", "b", [("$", "%")]), ("starttag", "c", [("\\", "/")])]) diff --git a/Lib/test/test_http_cookiejar.py b/Lib/test/test_http_cookiejar.py index 49c01ae489..6fee4df10a 100644 --- a/Lib/test/test_http_cookiejar.py +++ b/Lib/test/test_http_cookiejar.py @@ -1051,7 +1051,7 @@ class CookieTests(unittest.TestCase): url = "http://foo.bar.com/" interact_2965(c, url, "spam=eggs; Version=1; Port") h = interact_2965(c, url) - self.assertRegex(h, "\$Port([^=]|$)", + self.assertRegex(h, r"\$Port([^=]|$)", "port with no value not returned with no value") c = CookieJar(pol) @@ -1396,9 +1396,9 @@ class LWPCookieTests(unittest.TestCase): self.assertRegex(cookie, r'^\$Version="?1"?;') self.assertRegex(cookie, r'Part_Number="?Rocket_Launcher_0001"?;' - '\s*\$Path="\/acme"') + r'\s*\$Path="\/acme"') self.assertRegex(cookie, r'Customer="?WILE_E_COYOTE"?;' - '\s*\$Path="\/acme"') + r'\s*\$Path="\/acme"') # # 7. User Agent -> Server diff --git a/Lib/test/test_mailcap.py b/Lib/test/test_mailcap.py index 623fadb8ed..5c7d6cb431 100644 --- a/Lib/test/test_mailcap.py +++ b/Lib/test/test_mailcap.py @@ -101,7 +101,7 @@ class HelperFunctionTest(unittest.TestCase): (["echo foo", "audio/*", "foo.txt"], "echo foo"), (["echo %s", "audio/*", "foo.txt"], "echo foo.txt"), (["echo %t", "audio/*", "foo.txt"], "echo audio/*"), - (["echo \%t", "audio/*", "foo.txt"], "echo %t"), + (["echo \\%t", "audio/*", "foo.txt"], "echo %t"), (["echo foo", "audio/*", "foo.txt", plist], "echo foo"), (["echo %{total}", "audio/*", "foo.txt", plist], "echo 3") ] diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index aee31ed1ef..b504cf7976 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2086,7 +2086,7 @@ class Win32JunctionTests(unittest.TestCase): class NonLocalSymlinkTests(unittest.TestCase): def setUp(self): - """ + r""" Create this structure: base diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py index ed18773326..8eed7c00eb 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py @@ -255,7 +255,7 @@ class PlatformTest(unittest.TestCase): with warnings.catch_warnings(): warnings.filterwarnings( 'ignore', - 'dist\(\) and linux_distribution\(\) ' + r'dist\(\) and linux_distribution\(\) ' 'functions are deprecated .*', PendingDeprecationWarning, ) @@ -331,7 +331,7 @@ class PlatformTest(unittest.TestCase): with warnings.catch_warnings(): warnings.filterwarnings( 'ignore', - 'dist\(\) and linux_distribution\(\) ' + r'dist\(\) and linux_distribution\(\) ' 'functions are deprecated .*', PendingDeprecationWarning, ) diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index 24a0604948..02fed21992 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -113,10 +113,10 @@ class ReTests(unittest.TestCase): self.assertEqual(re.sub('(.)', re.escape(s), 'x'), s) self.assertEqual(re.sub('(.)', lambda m: s, 'x'), s) - self.assertEqual(re.sub('(?Px)', '\g\g', 'xx'), 'xxxx') - self.assertEqual(re.sub('(?Px)', '\g\g<1>', 'xx'), 'xxxx') - self.assertEqual(re.sub('(?Px)', '\g\g', 'xx'), 'xxxx') - self.assertEqual(re.sub('(?Px)', '\g<1>\g<1>', 'xx'), 'xxxx') + self.assertEqual(re.sub('(?Px)', r'\g\g', 'xx'), 'xxxx') + self.assertEqual(re.sub('(?Px)', r'\g\g<1>', 'xx'), 'xxxx') + self.assertEqual(re.sub('(?Px)', r'\g\g', 'xx'), 'xxxx') + self.assertEqual(re.sub('(?Px)', r'\g<1>\g<1>', 'xx'), 'xxxx') self.assertEqual(re.sub('a', r'\t\n\v\r\f\a\b', 'a'), '\t\n\v\r\f\a\b') self.assertEqual(re.sub('a', '\t\n\v\r\f\a\b', 'a'), '\t\n\v\r\f\a\b') @@ -127,11 +127,11 @@ class ReTests(unittest.TestCase): with self.assertRaises(re.error): self.assertEqual(re.sub('a', '\\' + c, 'a'), '\\' + c) - self.assertEqual(re.sub('^\s*', 'X', 'test'), 'Xtest') + self.assertEqual(re.sub(r'^\s*', 'X', 'test'), 'Xtest') def test_bug_449964(self): # fails for group followed by other escape - self.assertEqual(re.sub(r'(?Px)', '\g<1>\g<1>\\b', 'xx'), + self.assertEqual(re.sub(r'(?Px)', r'\g<1>\g<1>\b', 'xx'), 'xx\bxx\b') def test_bug_449000(self): @@ -218,26 +218,26 @@ class ReTests(unittest.TestCase): self.assertEqual(re.sub('x+', '-', 'abxd'), 'ab-d') def test_symbolic_groups(self): - re.compile('(?Px)(?P=a)(?(a)y)') - re.compile('(?Px)(?P=a1)(?(a1)y)') - re.compile('(?Px)\1(?(1)y)') - self.checkPatternError('(?P)(?P)', + re.compile(r'(?Px)(?P=a)(?(a)y)') + re.compile(r'(?Px)(?P=a1)(?(a1)y)') + re.compile(r'(?Px)\1(?(1)y)') + self.checkPatternError(r'(?P)(?P)', "redefinition of group name 'a' as group 2; " "was group 1") - self.checkPatternError('(?P(?P=a))', + self.checkPatternError(r'(?P(?P=a))', "cannot refer to an open group", 10) - self.checkPatternError('(?Pxy)', 'unknown extension ?Px') - self.checkPatternError('(?P)(?P=a', 'missing ), unterminated name', 11) - self.checkPatternError('(?P=', 'missing group name', 4) - self.checkPatternError('(?P=)', 'missing group name', 4) - self.checkPatternError('(?P=1)', "bad character in group name '1'", 4) - self.checkPatternError('(?P=a)', "unknown group name 'a'") - self.checkPatternError('(?P=a1)', "unknown group name 'a1'") - self.checkPatternError('(?P=a.)', "bad character in group name 'a.'", 4) - self.checkPatternError('(?P<)', 'missing >, unterminated name', 4) - self.checkPatternError('(?P, unterminated name', 4) - self.checkPatternError('(?P<', 'missing group name', 4) - self.checkPatternError('(?P<>)', 'missing group name', 4) + self.checkPatternError(r'(?Pxy)', 'unknown extension ?Px') + self.checkPatternError(r'(?P)(?P=a', 'missing ), unterminated name', 11) + self.checkPatternError(r'(?P=', 'missing group name', 4) + self.checkPatternError(r'(?P=)', 'missing group name', 4) + self.checkPatternError(r'(?P=1)', "bad character in group name '1'", 4) + self.checkPatternError(r'(?P=a)', "unknown group name 'a'") + self.checkPatternError(r'(?P=a1)', "unknown group name 'a1'") + self.checkPatternError(r'(?P=a.)', "bad character in group name 'a.'", 4) + self.checkPatternError(r'(?P<)', 'missing >, unterminated name', 4) + self.checkPatternError(r'(?P, unterminated name', 4) + self.checkPatternError(r'(?P<', 'missing group name', 4) + self.checkPatternError(r'(?P<>)', 'missing group name', 4) self.checkPatternError(r'(?P<1>)', "bad character in group name '1'", 4) self.checkPatternError(r'(?P)', "bad character in group name 'a.'", 4) self.checkPatternError(r'(?(', 'missing group name', 3) @@ -256,35 +256,35 @@ class ReTests(unittest.TestCase): self.assertEqual(re.match(pat, 'xc8yz').span(), (0, 5)) def test_symbolic_refs(self): - self.checkTemplateError('(?Px)', '\gx)', r'\g, unterminated name', 3) - self.checkTemplateError('(?Px)', '\g<', 'xx', + self.checkTemplateError('(?Px)', r'\g<', 'xx', 'missing group name', 3) - self.checkTemplateError('(?Px)', '\g', 'xx', 'missing <', 2) - self.checkTemplateError('(?Px)', '\g', 'xx', + self.checkTemplateError('(?Px)', r'\g', 'xx', 'missing <', 2) + self.checkTemplateError('(?Px)', r'\g', 'xx', "bad character in group name 'a a'", 3) - self.checkTemplateError('(?Px)', '\g<>', 'xx', + self.checkTemplateError('(?Px)', r'\g<>', 'xx', 'missing group name', 3) - self.checkTemplateError('(?Px)', '\g<1a1>', 'xx', + self.checkTemplateError('(?Px)', r'\g<1a1>', 'xx', "bad character in group name '1a1'", 3) self.checkTemplateError('(?Px)', r'\g<2>', 'xx', 'invalid group reference') self.checkTemplateError('(?Px)', r'\2', 'xx', 'invalid group reference') with self.assertRaisesRegex(IndexError, "unknown group name 'ab'"): - re.sub('(?Px)', '\g', 'xx') + re.sub('(?Px)', r'\g', 'xx') self.assertEqual(re.sub('(?Px)|(?Py)', r'\g', 'xx'), '') self.assertEqual(re.sub('(?Px)|(?Py)', r'\2', 'xx'), '') - self.checkTemplateError('(?Px)', '\g<-1>', 'xx', + self.checkTemplateError('(?Px)', r'\g<-1>', 'xx', "bad character in group name '-1'", 3) # New valid/invalid identifiers in Python 3 self.assertEqual(re.sub('(?P<µ>x)', r'\g<µ>', 'xx'), 'xx') self.assertEqual(re.sub('(?P<𝔘𝔫𝔦𝔠𝔬𝔡𝔢>x)', r'\g<𝔘𝔫𝔦𝔠𝔬𝔡𝔢>', 'xx'), 'xx') - self.checkTemplateError('(?Px)', '\g<©>', 'xx', + self.checkTemplateError('(?Px)', r'\g<©>', 'xx', "bad character in group name '©'", 3) # Support > 100 groups. pat = '|'.join('x(?P%x)y' % (i, i) for i in range(1, 200 + 1)) - self.assertEqual(re.sub(pat, '\g<200>', 'xc8yzxc8y'), 'c8zc8') + self.assertEqual(re.sub(pat, r'\g<200>', 'xc8yzxc8y'), 'c8zc8') def test_re_subn(self): self.assertEqual(re.subn("(?i)b+", "x", "bbbb BBBB"), ('x x', 2)) @@ -472,19 +472,19 @@ class ReTests(unittest.TestCase): re.compile(r".*?").fullmatch("abcd", pos=1, endpos=3).span(), (1, 3)) def test_re_groupref_exists(self): - self.assertEqual(re.match('^(\()?([^()]+)(?(1)\))$', '(a)').groups(), + self.assertEqual(re.match(r'^(\()?([^()]+)(?(1)\))$', '(a)').groups(), ('(', 'a')) - self.assertEqual(re.match('^(\()?([^()]+)(?(1)\))$', 'a').groups(), + self.assertEqual(re.match(r'^(\()?([^()]+)(?(1)\))$', 'a').groups(), (None, 'a')) - self.assertIsNone(re.match('^(\()?([^()]+)(?(1)\))$', 'a)')) - self.assertIsNone(re.match('^(\()?([^()]+)(?(1)\))$', '(a')) + self.assertIsNone(re.match(r'^(\()?([^()]+)(?(1)\))$', 'a)')) + self.assertIsNone(re.match(r'^(\()?([^()]+)(?(1)\))$', '(a')) self.assertEqual(re.match('^(?:(a)|c)((?(1)b|d))$', 'ab').groups(), ('a', 'b')) - self.assertEqual(re.match('^(?:(a)|c)((?(1)b|d))$', 'cd').groups(), + self.assertEqual(re.match(r'^(?:(a)|c)((?(1)b|d))$', 'cd').groups(), (None, 'd')) - self.assertEqual(re.match('^(?:(a)|c)((?(1)|d))$', 'cd').groups(), + self.assertEqual(re.match(r'^(?:(a)|c)((?(1)|d))$', 'cd').groups(), (None, 'd')) - self.assertEqual(re.match('^(?:(a)|c)((?(1)|d))$', 'a').groups(), + self.assertEqual(re.match(r'^(?:(a)|c)((?(1)|d))$', 'a').groups(), ('a', '')) # Tests for bug #1177831: exercise groups other than the first group @@ -509,7 +509,7 @@ class ReTests(unittest.TestCase): 'two branches', 10) def test_re_groupref_overflow(self): - self.checkTemplateError('()', '\g<%s>' % sre_constants.MAXGROUPS, 'xx', + self.checkTemplateError('()', r'\g<%s>' % sre_constants.MAXGROUPS, 'xx', 'invalid group reference', 3) self.checkPatternError(r'(?P)(?(%d))' % sre_constants.MAXGROUPS, 'invalid group reference', 10) @@ -544,37 +544,37 @@ class ReTests(unittest.TestCase): " ") def test_repeat_minmax(self): - self.assertIsNone(re.match("^(\w){1}$", "abc")) - self.assertIsNone(re.match("^(\w){1}?$", "abc")) - self.assertIsNone(re.match("^(\w){1,2}$", "abc")) - self.assertIsNone(re.match("^(\w){1,2}?$", "abc")) - - self.assertEqual(re.match("^(\w){3}$", "abc").group(1), "c") - self.assertEqual(re.match("^(\w){1,3}$", "abc").group(1), "c") - self.assertEqual(re.match("^(\w){1,4}$", "abc").group(1), "c") - self.assertEqual(re.match("^(\w){3,4}?$", "abc").group(1), "c") - self.assertEqual(re.match("^(\w){3}?$", "abc").group(1), "c") - self.assertEqual(re.match("^(\w){1,3}?$", "abc").group(1), "c") - self.assertEqual(re.match("^(\w){1,4}?$", "abc").group(1), "c") - self.assertEqual(re.match("^(\w){3,4}?$", "abc").group(1), "c") - - self.assertIsNone(re.match("^x{1}$", "xxx")) - self.assertIsNone(re.match("^x{1}?$", "xxx")) - self.assertIsNone(re.match("^x{1,2}$", "xxx")) - self.assertIsNone(re.match("^x{1,2}?$", "xxx")) - - self.assertTrue(re.match("^x{3}$", "xxx")) - self.assertTrue(re.match("^x{1,3}$", "xxx")) - self.assertTrue(re.match("^x{3,3}$", "xxx")) - self.assertTrue(re.match("^x{1,4}$", "xxx")) - self.assertTrue(re.match("^x{3,4}?$", "xxx")) - self.assertTrue(re.match("^x{3}?$", "xxx")) - self.assertTrue(re.match("^x{1,3}?$", "xxx")) - self.assertTrue(re.match("^x{1,4}?$", "xxx")) - self.assertTrue(re.match("^x{3,4}?$", "xxx")) - - self.assertIsNone(re.match("^x{}$", "xxx")) - self.assertTrue(re.match("^x{}$", "x{}")) + self.assertIsNone(re.match(r"^(\w){1}$", "abc")) + self.assertIsNone(re.match(r"^(\w){1}?$", "abc")) + self.assertIsNone(re.match(r"^(\w){1,2}$", "abc")) + self.assertIsNone(re.match(r"^(\w){1,2}?$", "abc")) + + self.assertEqual(re.match(r"^(\w){3}$", "abc").group(1), "c") + self.assertEqual(re.match(r"^(\w){1,3}$", "abc").group(1), "c") + self.assertEqual(re.match(r"^(\w){1,4}$", "abc").group(1), "c") + self.assertEqual(re.match(r"^(\w){3,4}?$", "abc").group(1), "c") + self.assertEqual(re.match(r"^(\w){3}?$", "abc").group(1), "c") + self.assertEqual(re.match(r"^(\w){1,3}?$", "abc").group(1), "c") + self.assertEqual(re.match(r"^(\w){1,4}?$", "abc").group(1), "c") + self.assertEqual(re.match(r"^(\w){3,4}?$", "abc").group(1), "c") + + self.assertIsNone(re.match(r"^x{1}$", "xxx")) + self.assertIsNone(re.match(r"^x{1}?$", "xxx")) + self.assertIsNone(re.match(r"^x{1,2}$", "xxx")) + self.assertIsNone(re.match(r"^x{1,2}?$", "xxx")) + + self.assertTrue(re.match(r"^x{3}$", "xxx")) + self.assertTrue(re.match(r"^x{1,3}$", "xxx")) + self.assertTrue(re.match(r"^x{3,3}$", "xxx")) + self.assertTrue(re.match(r"^x{1,4}$", "xxx")) + self.assertTrue(re.match(r"^x{3,4}?$", "xxx")) + self.assertTrue(re.match(r"^x{3}?$", "xxx")) + self.assertTrue(re.match(r"^x{1,3}?$", "xxx")) + self.assertTrue(re.match(r"^x{1,4}?$", "xxx")) + self.assertTrue(re.match(r"^x{3,4}?$", "xxx")) + + self.assertIsNone(re.match(r"^x{}$", "xxx")) + self.assertTrue(re.match(r"^x{}$", "x{}")) self.checkPatternError(r'x{2,1}', 'min repeat greater than max repeat', 2) @@ -697,10 +697,10 @@ class ReTests(unittest.TestCase): "a\n\nb") def test_lookahead(self): - self.assertEqual(re.match("(a(?=\s[^a]))", "a b").group(1), "a") - self.assertEqual(re.match("(a(?=\s[^a]*))", "a b").group(1), "a") - self.assertEqual(re.match("(a(?=\s[abc]))", "a b").group(1), "a") - self.assertEqual(re.match("(a(?=\s[abc]*))", "a bc").group(1), "a") + self.assertEqual(re.match(r"(a(?=\s[^a]))", "a b").group(1), "a") + self.assertEqual(re.match(r"(a(?=\s[^a]*))", "a b").group(1), "a") + self.assertEqual(re.match(r"(a(?=\s[abc]))", "a b").group(1), "a") + self.assertEqual(re.match(r"(a(?=\s[abc]*))", "a bc").group(1), "a") self.assertEqual(re.match(r"(a)(?=\s\1)", "a a").group(1), "a") self.assertEqual(re.match(r"(a)(?=\s\1*)", "a aa").group(1), "a") self.assertEqual(re.match(r"(a)(?=\s(abc|a))", "a a").group(1), "a") @@ -848,12 +848,12 @@ class ReTests(unittest.TestCase): self.assertEqual(re.match(b"abc", b"ABC", re.I|re.L).group(0), b"ABC") def test_not_literal(self): - self.assertEqual(re.search("\s([^a])", " b").group(1), "b") - self.assertEqual(re.search("\s([^a]*)", " bb").group(1), "bb") + self.assertEqual(re.search(r"\s([^a])", " b").group(1), "b") + self.assertEqual(re.search(r"\s([^a]*)", " bb").group(1), "bb") def test_search_coverage(self): - self.assertEqual(re.search("\s(b)", " b").group(1), "b") - self.assertEqual(re.search("a\s", "a ").group(0), "a ") + self.assertEqual(re.search(r"\s(b)", " b").group(1), "b") + self.assertEqual(re.search(r"a\s", "a ").group(0), "a ") def assertMatch(self, pattern, text, match=None, span=None, matcher=re.match): @@ -1055,8 +1055,8 @@ class ReTests(unittest.TestCase): self.assertIsNone(re.match(r'(a)?a','a').lastindex) self.assertEqual(re.match(r'(a)(b)?b','ab').lastindex, 1) self.assertEqual(re.match(r'(?Pa)(?Pb)?b','ab').lastgroup, 'a') - self.assertEqual(re.match("(?Pa(b))", "ab").lastgroup, 'a') - self.assertEqual(re.match("((a))", "a").lastindex, 1) + self.assertEqual(re.match(r"(?Pa(b))", "ab").lastgroup, 'a') + self.assertEqual(re.match(r"((a))", "a").lastindex, 1) def test_bug_418626(self): # bugs 418626 at al. -- Testing Greg Chapman's addition of op code @@ -1228,7 +1228,7 @@ class ReTests(unittest.TestCase): '\uff10', # '\N{FULLWIDTH DIGIT ZERO}', category 'Nd' ] for x in decimal_digits: - self.assertEqual(re.match('^\d$', x).group(0), x) + self.assertEqual(re.match(r'^\d$', x).group(0), x) not_decimal_digits = [ '\u2165', # '\N{ROMAN NUMERAL SIX}', category 'Nl' @@ -1237,7 +1237,7 @@ class ReTests(unittest.TestCase): '\u32b4', # '\N{CIRCLED NUMBER THIRTY NINE}', category 'No' ] for x in not_decimal_digits: - self.assertIsNone(re.match('^\d$', x)) + self.assertIsNone(re.match(r'^\d$', x)) def test_empty_array(self): # SF buf 1647541 @@ -1306,29 +1306,29 @@ class ReTests(unittest.TestCase): for flags in (0, re.UNICODE): pat = re.compile('\xc0', flags | re.IGNORECASE) self.assertTrue(pat.match('\xe0')) - pat = re.compile('\w', flags) + pat = re.compile(r'\w', flags) self.assertTrue(pat.match('\xe0')) pat = re.compile('\xc0', re.ASCII | re.IGNORECASE) self.assertIsNone(pat.match('\xe0')) pat = re.compile('(?a)\xc0', re.IGNORECASE) self.assertIsNone(pat.match('\xe0')) - pat = re.compile('\w', re.ASCII) + pat = re.compile(r'\w', re.ASCII) self.assertIsNone(pat.match('\xe0')) - pat = re.compile('(?a)\w') + pat = re.compile(r'(?a)\w') self.assertIsNone(pat.match('\xe0')) # Bytes patterns for flags in (0, re.ASCII): pat = re.compile(b'\xc0', flags | re.IGNORECASE) self.assertIsNone(pat.match(b'\xe0')) - pat = re.compile(b'\w', flags) + pat = re.compile(br'\w', flags) self.assertIsNone(pat.match(b'\xe0')) # Incompatibilities - self.assertRaises(ValueError, re.compile, b'\w', re.UNICODE) - self.assertRaises(ValueError, re.compile, b'(?u)\w') - self.assertRaises(ValueError, re.compile, '\w', re.UNICODE | re.ASCII) - self.assertRaises(ValueError, re.compile, '(?u)\w', re.ASCII) - self.assertRaises(ValueError, re.compile, '(?a)\w', re.UNICODE) - self.assertRaises(ValueError, re.compile, '(?au)\w') + self.assertRaises(ValueError, re.compile, br'\w', re.UNICODE) + self.assertRaises(ValueError, re.compile, br'(?u)\w') + self.assertRaises(ValueError, re.compile, r'\w', re.UNICODE | re.ASCII) + self.assertRaises(ValueError, re.compile, r'(?u)\w', re.ASCII) + self.assertRaises(ValueError, re.compile, r'(?a)\w', re.UNICODE) + self.assertRaises(ValueError, re.compile, r'(?au)\w') def test_locale_flag(self): import locale @@ -1359,13 +1359,13 @@ class ReTests(unittest.TestCase): pat = re.compile(bpat, re.IGNORECASE) if bletter: self.assertIsNone(pat.match(bletter)) - pat = re.compile(b'\w', re.LOCALE) + pat = re.compile(br'\w', re.LOCALE) if bletter: self.assertTrue(pat.match(bletter)) - pat = re.compile(b'(?L)\w') + pat = re.compile(br'(?L)\w') if bletter: self.assertTrue(pat.match(bletter)) - pat = re.compile(b'\w') + pat = re.compile(br'\w') if bletter: self.assertIsNone(pat.match(bletter)) # Incompatibilities @@ -1379,7 +1379,7 @@ class ReTests(unittest.TestCase): def test_bug_6509(self): # Replacement strings of both types must parse properly. # all strings - pat = re.compile('a(\w)') + pat = re.compile(r'a(\w)') self.assertEqual(pat.sub('b\\1', 'ac'), 'bc') pat = re.compile('a(.)') self.assertEqual(pat.sub('b\\1', 'a\u1234'), 'b\u1234') @@ -1387,7 +1387,7 @@ class ReTests(unittest.TestCase): self.assertEqual(pat.sub(lambda m: 'str', 'a5'), 'str') # all bytes - pat = re.compile(b'a(\w)') + pat = re.compile(br'a(\w)') self.assertEqual(pat.sub(b'b\\1', b'ac'), b'bc') pat = re.compile(b'a(.)') self.assertEqual(pat.sub(b'b\\1', b'a\xCD'), b'b\xCD') @@ -1509,7 +1509,7 @@ class ReTests(unittest.TestCase): for string in (b'[abracadabra]', B(b'[abracadabra]'), bytearray(b'[abracadabra]'), memoryview(b'[abracadabra]')): - m = re.search(rb'(.+)(.*?)\1', string) + m = re.search(br'(.+)(.*?)\1', string) self.assertEqual(repr(m), "<%s.%s object; " "span=(1, 12), match=b'abracadabra'>" % (type(m).__module__, type(m).__qualname__)) diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py index dc154616b6..7c95b64243 100644 --- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -704,8 +704,8 @@ class ArgsTestCase(BaseTestCase): test = self.create_test('coverage') output = self.run_tests("--coverage", test) self.check_executed_tests(output, [test]) - regex = ('lines +cov% +module +\(path\)\n' - '(?: *[0-9]+ *[0-9]{1,2}% *[^ ]+ +\([^)]+\)+)+') + regex = (r'lines +cov% +module +\(path\)\n' + r'(?: *[0-9]+ *[0-9]{1,2}% *[^ ]+ +\([^)]+\)+)+') self.check_line(output, regex) def test_wait(self): diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index d8d53af62b..0bdb86d5f2 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -3405,7 +3405,7 @@ def test_main(verbose=False): with warnings.catch_warnings(): warnings.filterwarnings( 'ignore', - 'dist\(\) and linux_distribution\(\) ' + r'dist\(\) and linux_distribution\(\) ' 'functions are deprecated .*', PendingDeprecationWarning, ) diff --git a/Lib/test/test_strftime.py b/Lib/test/test_strftime.py index 772cd0688c..72b1910c38 100644 --- a/Lib/test/test_strftime.py +++ b/Lib/test/test_strftime.py @@ -23,9 +23,9 @@ def escapestr(text, ampm): """ new_text = re.escape(text) new_text = new_text.replace(re.escape(ampm), ampm) - new_text = new_text.replace('\%', '%') - new_text = new_text.replace('\:', ':') - new_text = new_text.replace('\?', '?') + new_text = new_text.replace(r'\%', '%') + new_text = new_text.replace(r'\:', ':') + new_text = new_text.replace(r'\?', '?') return new_text diff --git a/Lib/test/test_strlit.py b/Lib/test/test_strlit.py index 87cffe843a..37ace230f5 100644 --- a/Lib/test/test_strlit.py +++ b/Lib/test/test_strlit.py @@ -121,9 +121,9 @@ class TestLiterals(unittest.TestCase): self.assertEqual(eval(""" b'\x01' """), byte(1)) self.assertEqual(eval(r""" b'\x81' """), byte(0x81)) self.assertRaises(SyntaxError, eval, """ b'\x81' """) - self.assertEqual(eval(r""" b'\u1881' """), b'\\' + b'u1881') + self.assertEqual(eval(r""" br'\u1881' """), b'\\' + b'u1881') self.assertRaises(SyntaxError, eval, """ b'\u1881' """) - self.assertEqual(eval(r""" b'\U0001d120' """), b'\\' + b'U0001d120') + self.assertEqual(eval(r""" br'\U0001d120' """), b'\\' + b'U0001d120') self.assertRaises(SyntaxError, eval, """ b'\U0001d120' """) def test_eval_bytes_incomplete(self): diff --git a/Lib/test/test_strptime.py b/Lib/test/test_strptime.py index 8c8f97ba50..22eac32917 100644 --- a/Lib/test/test_strptime.py +++ b/Lib/test/test_strptime.py @@ -129,7 +129,7 @@ class TimeRETests(unittest.TestCase): def test_pattern_escaping(self): # Make sure any characters in the format string that might be taken as # regex syntax is escaped. - pattern_string = self.time_re.pattern("\d+") + pattern_string = self.time_re.pattern(r"\d+") self.assertIn(r"\\d\+", pattern_string, "%s does not have re characters escaped properly" % pattern_string) @@ -170,9 +170,9 @@ class TimeRETests(unittest.TestCase): def test_matching_with_escapes(self): # Make sure a format that requires escaping of characters works - compiled_re = self.time_re.compile("\w+ %m") - found = compiled_re.match("\w+ 10") - self.assertTrue(found, "Escaping failed of format '\w+ 10'") + compiled_re = self.time_re.compile(r"\w+ %m") + found = compiled_re.match(r"\w+ 10") + self.assertTrue(found, r"Escaping failed of format '\w+ 10'") def test_locale_data_w_regex_metacharacters(self): # Check that if locale data contains regex metacharacters they are @@ -403,7 +403,7 @@ class StrptimeTests(unittest.TestCase): # unbalanced parentheses when the regex is compiled if they are not # escaped. # Test instigated by bug #796149 . - need_escaping = ".^$*+?{}\[]|)(" + need_escaping = r".^$*+?{}\[]|)(" self.assertTrue(_strptime._strptime_time(need_escaping, need_escaping)) def test_feb29_on_leap_year_without_year(self): diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py index 78f9668e19..9ab624e6fc 100644 --- a/Lib/test/test_unicode.py +++ b/Lib/test/test_unicode.py @@ -1564,7 +1564,7 @@ class UnicodeTest(string_tests.CommonTest, ('+', b'+-'), ('+-', b'+--'), ('+?', b'+-?'), - ('\?', b'+AFw?'), + (r'\?', b'+AFw?'), ('+?', b'+-?'), (r'\\?', b'+AFwAXA?'), (r'\\\?', b'+AFwAXABc?'), @@ -2326,7 +2326,7 @@ class UnicodeTest(string_tests.CommonTest, # non-ascii format, ascii argument: ensure that PyUnicode_FromFormatV() # raises an error self.assertRaisesRegex(ValueError, - '^PyUnicode_FromFormatV\(\) expects an ASCII-encoded format ' + r'^PyUnicode_FromFormatV\(\) expects an ASCII-encoded format ' 'string, got a non-ASCII byte: 0xe9$', PyUnicode_FromFormat, b'unicode\xe9=%s', 'ascii') diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py index 247598ac57..8f06b08afa 100644 --- a/Lib/test/test_urllib.py +++ b/Lib/test/test_urllib.py @@ -729,7 +729,7 @@ FF class QuotingTests(unittest.TestCase): - """Tests for urllib.quote() and urllib.quote_plus() + r"""Tests for urllib.quote() and urllib.quote_plus() According to RFC 2396 (Uniform Resource Identifiers), to escape a character you write it as '%' + <2 character US-ASCII hex value>. @@ -804,7 +804,7 @@ class QuotingTests(unittest.TestCase): # Make sure all characters that should be quoted are by default sans # space (separate test for that). should_quote = [chr(num) for num in range(32)] # For 0x00 - 0x1F - should_quote.append('<>#%"{}|\^[]`') + should_quote.append(r'<>#%"{}|\^[]`') should_quote.append(chr(127)) # For 0x7F should_quote = ''.join(should_quote) for char in should_quote: diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py index 0773a86984..29a9878dd0 100644 --- a/Lib/test/test_xmlrpc.py +++ b/Lib/test/test_xmlrpc.py @@ -1218,7 +1218,7 @@ class CGIHandlerTestCase(unittest.TestCase): content = handle[handle.find(" Date: Thu, 8 Sep 2016 11:11:13 -0700 Subject: Fixes tests broken by issue #27781. --- Lib/test/test_genericpath.py | 5 ++++- Lib/test/test_httpservers.py | 2 ++ Lib/test/test_shutil.py | 4 +--- Lib/test/test_sys.py | 5 +++-- 4 files changed, 10 insertions(+), 6 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_genericpath.py b/Lib/test/test_genericpath.py index c8f158d0c5..ae5dd6a5f7 100644 --- a/Lib/test/test_genericpath.py +++ b/Lib/test/test_genericpath.py @@ -388,10 +388,13 @@ class CommonTest(GenericTest): warnings.simplefilter("ignore", DeprecationWarning) self.assertIn(b"foo", self.pathmodule.abspath(b"foo")) + # avoid UnicodeDecodeError on Windows + undecodable_path = b'' if sys.platform == 'win32' else b'f\xf2\xf2' + # Abspath returns bytes when the arg is bytes with warnings.catch_warnings(): warnings.simplefilter("ignore", DeprecationWarning) - for path in (b'', b'foo', b'f\xf2\xf2', b'/foo', b'C:\\'): + for path in (b'', b'foo', undecodable_path, b'/foo', b'C:\\'): self.assertIsInstance(self.pathmodule.abspath(path), bytes) def test_realpath(self): diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 75044cbafa..4e931446b9 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -370,6 +370,8 @@ class SimpleHTTPServerTestCase(BaseTestCase): return body @support.requires_mac_ver(10, 5) + @unittest.skipIf(sys.platform == 'win32', + 'undecodable name cannot be decoded on win32') @unittest.skipUnless(support.TESTFN_UNDECODABLE, 'need support.TESTFN_UNDECODABLE') def test_undecodable_filename(self): diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index 90a31d7b18..990fae55db 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -132,9 +132,7 @@ class TestShutil(unittest.TestCase): write_file(os.path.join(victim, 'somefile'), 'foo') victim = os.fsencode(victim) self.assertIsInstance(victim, bytes) - win = (os.name == 'nt') - with self.assertWarns(DeprecationWarning) if win else ExitStack(): - shutil.rmtree(victim) + shutil.rmtree(victim) @support.skip_unless_symlink def test_rmtree_fails_on_symlink(self): diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index ea152c1ed5..6084d2d70d 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -10,6 +10,7 @@ import codecs import gc import sysconfig import platform +import locale # count the number of test runs, used to create unique # strings to intern in test_intern() @@ -627,6 +628,8 @@ class SysModuleTest(unittest.TestCase): @unittest.skipUnless(test.support.FS_NONASCII, 'requires OS support of non-ASCII encodings') + @unittest.skipUnless(sys.getfilesystemencoding() == locale.getpreferredencoding(False), + 'requires FS encoding to match locale') def test_ioencoding_nonascii(self): env = dict(os.environ) @@ -669,8 +672,6 @@ class SysModuleTest(unittest.TestCase): fs_encoding = sys.getfilesystemencoding() if sys.platform == 'darwin': expected = 'utf-8' - elif sys.platform == 'win32': - expected = 'mbcs' else: expected = None self.check_fsencoding(fs_encoding, expected) -- cgit v1.2.1 From 9157fe330ec0d127c07455dad7455857ad22c1c4 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 8 Sep 2016 11:12:31 -0700 Subject: Issue #28026: Raise ImportError when exec_module() exists but create_module() is missing. --- Lib/importlib/_bootstrap.py | 5 ++--- Lib/test/test_importlib/test_util.py | 8 +------- 2 files changed, 3 insertions(+), 10 deletions(-) (limited to 'Lib') diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py index 8cd0262bbf..a531a0351d 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -559,9 +559,8 @@ def module_from_spec(spec): # module creation should be used. module = spec.loader.create_module(spec) elif hasattr(spec.loader, 'exec_module'): - _warnings.warn('starting in Python 3.6, loaders defining exec_module() ' - 'must also define create_module()', - DeprecationWarning, stacklevel=2) + raise ImportError('loaders that define exec_module() ' + 'must also define create_module()') if module is None: module = _new_module(spec.name) _init_module_attrs(spec, module) diff --git a/Lib/test/test_importlib/test_util.py b/Lib/test/test_importlib/test_util.py index 2aa1131cf0..d615375b34 100644 --- a/Lib/test/test_importlib/test_util.py +++ b/Lib/test/test_importlib/test_util.py @@ -47,14 +47,8 @@ class ModuleFromSpecTests: def exec_module(self, module): pass spec = self.machinery.ModuleSpec('test', Loader()) - with warnings.catch_warnings(record=True) as w: - warnings.simplefilter('always') + with self.assertRaises(ImportError): module = self.util.module_from_spec(spec) - self.assertEqual(1, len(w)) - self.assertTrue(issubclass(w[0].category, DeprecationWarning)) - self.assertIn('create_module', str(w[0].message)) - self.assertIsInstance(module, types.ModuleType) - self.assertEqual(module.__name__, spec.name) def test_create_module_returns_None(self): class Loader(self.abc.Loader): -- cgit v1.2.1 From cffe59d63b65be3135ccdd6476d194c1c1a3b56f Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Thu, 8 Sep 2016 11:38:46 -0700 Subject: Issue #28027: Remove Lib/plat-* files --- Lib/plat-aix4/IN.py | 165 ---- Lib/plat-aix4/regen | 8 - Lib/plat-darwin/IN.py | 662 --------------- Lib/plat-darwin/regen | 3 - Lib/plat-freebsd4/IN.py | 355 -------- Lib/plat-freebsd4/regen | 3 - Lib/plat-freebsd5/IN.py | 355 -------- Lib/plat-freebsd5/regen | 3 - Lib/plat-freebsd6/IN.py | 551 ------------- Lib/plat-freebsd6/regen | 3 - Lib/plat-freebsd7/IN.py | 571 ------------- Lib/plat-freebsd7/regen | 3 - Lib/plat-freebsd8/IN.py | 571 ------------- Lib/plat-freebsd8/regen | 3 - Lib/plat-generic/regen | 3 - Lib/plat-linux/CDROM.py | 207 ----- Lib/plat-linux/DLFCN.py | 83 -- Lib/plat-linux/IN.py | 615 -------------- Lib/plat-linux/TYPES.py | 170 ---- Lib/plat-linux/regen | 33 - Lib/plat-netbsd1/IN.py | 56 -- Lib/plat-netbsd1/regen | 3 - Lib/plat-next3/regen | 6 - Lib/plat-sunos5/CDIO.py | 73 -- Lib/plat-sunos5/DLFCN.py | 27 - Lib/plat-sunos5/IN.py | 1421 -------------------------------- Lib/plat-sunos5/STROPTS.py | 1813 ----------------------------------------- Lib/plat-sunos5/TYPES.py | 313 ------- Lib/plat-sunos5/regen | 9 - Lib/plat-unixware7/IN.py | 836 ------------------- Lib/plat-unixware7/STROPTS.py | 328 -------- Lib/plat-unixware7/regen | 9 - 32 files changed, 9261 deletions(-) delete mode 100644 Lib/plat-aix4/IN.py delete mode 100755 Lib/plat-aix4/regen delete mode 100644 Lib/plat-darwin/IN.py delete mode 100755 Lib/plat-darwin/regen delete mode 100644 Lib/plat-freebsd4/IN.py delete mode 100644 Lib/plat-freebsd4/regen delete mode 100644 Lib/plat-freebsd5/IN.py delete mode 100644 Lib/plat-freebsd5/regen delete mode 100644 Lib/plat-freebsd6/IN.py delete mode 100644 Lib/plat-freebsd6/regen delete mode 100644 Lib/plat-freebsd7/IN.py delete mode 100644 Lib/plat-freebsd7/regen delete mode 100644 Lib/plat-freebsd8/IN.py delete mode 100644 Lib/plat-freebsd8/regen delete mode 100755 Lib/plat-generic/regen delete mode 100644 Lib/plat-linux/CDROM.py delete mode 100644 Lib/plat-linux/DLFCN.py delete mode 100644 Lib/plat-linux/IN.py delete mode 100644 Lib/plat-linux/TYPES.py delete mode 100755 Lib/plat-linux/regen delete mode 100644 Lib/plat-netbsd1/IN.py delete mode 100755 Lib/plat-netbsd1/regen delete mode 100755 Lib/plat-next3/regen delete mode 100644 Lib/plat-sunos5/CDIO.py delete mode 100644 Lib/plat-sunos5/DLFCN.py delete mode 100755 Lib/plat-sunos5/IN.py delete mode 100644 Lib/plat-sunos5/STROPTS.py delete mode 100644 Lib/plat-sunos5/TYPES.py delete mode 100755 Lib/plat-sunos5/regen delete mode 100644 Lib/plat-unixware7/IN.py delete mode 100644 Lib/plat-unixware7/STROPTS.py delete mode 100755 Lib/plat-unixware7/regen (limited to 'Lib') diff --git a/Lib/plat-aix4/IN.py b/Lib/plat-aix4/IN.py deleted file mode 100644 index 43f8f231ae..0000000000 --- a/Lib/plat-aix4/IN.py +++ /dev/null @@ -1,165 +0,0 @@ -# Generated by h2py from /usr/include/netinet/in.h - -# Included from net/nh.h - -# Included from sys/machine.h -LITTLE_ENDIAN = 1234 -BIG_ENDIAN = 4321 -PDP_ENDIAN = 3412 -BYTE_ORDER = BIG_ENDIAN -DEFAULT_GPR = 0xDEADBEEF -MSR_EE = 0x8000 -MSR_PR = 0x4000 -MSR_FP = 0x2000 -MSR_ME = 0x1000 -MSR_FE = 0x0800 -MSR_FE0 = 0x0800 -MSR_SE = 0x0400 -MSR_BE = 0x0200 -MSR_IE = 0x0100 -MSR_FE1 = 0x0100 -MSR_AL = 0x0080 -MSR_IP = 0x0040 -MSR_IR = 0x0020 -MSR_DR = 0x0010 -MSR_PM = 0x0004 -DEFAULT_MSR = (MSR_EE | MSR_ME | MSR_AL | MSR_IR | MSR_DR) -DEFAULT_USER_MSR = (DEFAULT_MSR | MSR_PR) -CR_LT = 0x80000000 -CR_GT = 0x40000000 -CR_EQ = 0x20000000 -CR_SO = 0x10000000 -CR_FX = 0x08000000 -CR_FEX = 0x04000000 -CR_VX = 0x02000000 -CR_OX = 0x01000000 -XER_SO = 0x80000000 -XER_OV = 0x40000000 -XER_CA = 0x20000000 -def XER_COMP_BYTE(xer): return ((xer >> 8) & 0x000000FF) - -def XER_LENGTH(xer): return (xer & 0x0000007F) - -DSISR_IO = 0x80000000 -DSISR_PFT = 0x40000000 -DSISR_LOCK = 0x20000000 -DSISR_FPIO = 0x10000000 -DSISR_PROT = 0x08000000 -DSISR_LOOP = 0x04000000 -DSISR_DRST = 0x04000000 -DSISR_ST = 0x02000000 -DSISR_SEGB = 0x01000000 -DSISR_DABR = 0x00400000 -DSISR_EAR = 0x00100000 -SRR_IS_PFT = 0x40000000 -SRR_IS_ISPEC = 0x20000000 -SRR_IS_IIO = 0x10000000 -SRR_IS_GUARD = 0x10000000 -SRR_IS_PROT = 0x08000000 -SRR_IS_LOOP = 0x04000000 -SRR_PR_FPEN = 0x00100000 -SRR_PR_INVAL = 0x00080000 -SRR_PR_PRIV = 0x00040000 -SRR_PR_TRAP = 0x00020000 -SRR_PR_IMPRE = 0x00010000 -def BUID_7F_SRVAL(raddr): return (0x87F00000 | (((uint)(raddr)) >> 28)) - -BT_256M = 0x1FFC -BT_128M = 0x0FFC -BT_64M = 0x07FC -BT_32M = 0x03FC -BT_16M = 0x01FC -BT_8M = 0x00FC -BT_4M = 0x007C -BT_2M = 0x003C -BT_1M = 0x001C -BT_512K = 0x000C -BT_256K = 0x0004 -BT_128K = 0x0000 -BT_NOACCESS = 0x0 -BT_RDONLY = 0x1 -BT_WRITE = 0x2 -BT_VS = 0x2 -BT_VP = 0x1 -def BAT_ESEG(dbatu): return (((uint)(dbatu) >> 28)) - -MIN_BAT_SIZE = 0x00020000 -MAX_BAT_SIZE = 0x10000000 -def ntohl(x): return (x) - -def ntohs(x): return (x) - -def htonl(x): return (x) - -def htons(x): return (x) - -IPPROTO_IP = 0 -IPPROTO_ICMP = 1 -IPPROTO_IGMP = 2 -IPPROTO_GGP = 3 -IPPROTO_TCP = 6 -IPPROTO_EGP = 8 -IPPROTO_PUP = 12 -IPPROTO_UDP = 17 -IPPROTO_IDP = 22 -IPPROTO_TP = 29 -IPPROTO_LOCAL = 63 -IPPROTO_EON = 80 -IPPROTO_BIP = 0x53 -IPPROTO_RAW = 255 -IPPROTO_MAX = 256 -IPPORT_RESERVED = 1024 -IPPORT_USERRESERVED = 5000 -IPPORT_TIMESERVER = 37 -def IN_CLASSA(i): return (((int)(i) & 0x80000000) == 0) - -IN_CLASSA_NET = 0xff000000 -IN_CLASSA_NSHIFT = 24 -IN_CLASSA_HOST = 0x00ffffff -IN_CLASSA_MAX = 128 -def IN_CLASSB(i): return (((int)(i) & 0xc0000000) == 0x80000000) - -IN_CLASSB_NET = 0xffff0000 -IN_CLASSB_NSHIFT = 16 -IN_CLASSB_HOST = 0x0000ffff -IN_CLASSB_MAX = 65536 -def IN_CLASSC(i): return (((int)(i) & 0xe0000000) == 0xc0000000) - -IN_CLASSC_NET = 0xffffff00 -IN_CLASSC_NSHIFT = 8 -IN_CLASSC_HOST = 0x000000ff -def IN_CLASSD(i): return (((int)(i) & 0xf0000000) == 0xe0000000) - -def IN_MULTICAST(i): return IN_CLASSD(i) - -IN_CLASSD_NET = 0xf0000000 -IN_CLASSD_NSHIFT = 28 -IN_CLASSD_HOST = 0x0fffffff -INADDR_UNSPEC_GROUP = 0xe0000000 -INADDR_ALLHOSTS_GROUP = 0xe0000001 -INADDR_MAX_LOCAL_GROUP = 0xe00000ff -def IN_EXPERIMENTAL(i): return (((int)(i) & 0xe0000000) == 0xe0000000) - -def IN_BADCLASS(i): return (((int)(i) & 0xf0000000) == 0xf0000000) - -INADDR_ANY = 0x00000000 -INADDR_BROADCAST = 0xffffffff -INADDR_LOOPBACK = 0x7f000001 -INADDR_NONE = 0xffffffff -IN_LOOPBACKNET = 127 -IP_OPTIONS = 1 -IP_HDRINCL = 2 -IP_TOS = 3 -IP_TTL = 4 -IP_RECVOPTS = 5 -IP_RECVRETOPTS = 6 -IP_RECVDSTADDR = 7 -IP_RETOPTS = 8 -IP_MULTICAST_IF = 9 -IP_MULTICAST_TTL = 10 -IP_MULTICAST_LOOP = 11 -IP_ADD_MEMBERSHIP = 12 -IP_DROP_MEMBERSHIP = 13 -IP_DEFAULT_MULTICAST_TTL = 1 -IP_DEFAULT_MULTICAST_LOOP = 1 -IP_MAX_MEMBERSHIPS = 20 diff --git a/Lib/plat-aix4/regen b/Lib/plat-aix4/regen deleted file mode 100755 index 57a71c4ed4..0000000000 --- a/Lib/plat-aix4/regen +++ /dev/null @@ -1,8 +0,0 @@ -#! /bin/sh -case `uname -sv` in -'AIX 4'*) ;; -*) echo Probably not on an AIX 4 system 1>&2 - exit 1;; -esac -set -v -h2py.py -i '(u_long)' /usr/include/netinet/in.h diff --git a/Lib/plat-darwin/IN.py b/Lib/plat-darwin/IN.py deleted file mode 100644 index 6b6be33a5a..0000000000 --- a/Lib/plat-darwin/IN.py +++ /dev/null @@ -1,662 +0,0 @@ -# Generated by h2py from /usr/include/netinet/in.h - -# Included from sys/appleapiopts.h - -# Included from sys/_types.h - -# Included from sys/cdefs.h -def __P(protos): return protos - -def __STRING(x): return #x - -def __P(protos): return () - -def __STRING(x): return "x" - -def __attribute__(x): return - -def __COPYRIGHT(s): return __IDSTRING(copyright,s) - -def __RCSID(s): return __IDSTRING(rcsid,s) - -def __SCCSID(s): return __IDSTRING(sccsid,s) - -def __PROJECT_VERSION(s): return __IDSTRING(project_version,s) - -__DARWIN_UNIX03 = 1 -__DARWIN_UNIX03 = 0 -__DARWIN_UNIX03 = 0 -__DARWIN_UNIX03 = 1 -__DARWIN_64_BIT_INO_T = 1 -__DARWIN_64_BIT_INO_T = 0 -__DARWIN_64_BIT_INO_T = 0 -__DARWIN_NON_CANCELABLE = 0 -__DARWIN_VERS_1050 = 1 -__DARWIN_VERS_1050 = 0 -__DARWIN_SUF_UNIX03 = "$UNIX2003" -__DARWIN_SUF_UNIX03_SET = 1 -__DARWIN_SUF_UNIX03_SET = 0 -__DARWIN_SUF_64_BIT_INO_T = "$INODE64" -__DARWIN_SUF_NON_CANCELABLE = "$NOCANCEL" -__DARWIN_SUF_1050 = "$1050" -__DARWIN_SUF_UNIX03_SET = 0 -__DARWIN_SUF_EXTSN = "$DARWIN_EXTSN" -__DARWIN_LONG_DOUBLE_IS_DOUBLE = 0 -def __DARWIN_LDBL_COMPAT(x): return - -def __DARWIN_LDBL_COMPAT2(x): return - -__DARWIN_LONG_DOUBLE_IS_DOUBLE = 1 -def __DARWIN_LDBL_COMPAT(x): return - -def __DARWIN_LDBL_COMPAT2(x): return - -__DARWIN_LONG_DOUBLE_IS_DOUBLE = 0 -_DARWIN_FEATURE_LONG_DOUBLE_IS_DOUBLE = 1 -_DARWIN_FEATURE_UNIX_CONFORMANCE = 3 -_DARWIN_FEATURE_64_BIT_INODE = 1 - -# Included from machine/_types.h -__PTHREAD_SIZE__ = 1168 -__PTHREAD_ATTR_SIZE__ = 56 -__PTHREAD_MUTEXATTR_SIZE__ = 8 -__PTHREAD_MUTEX_SIZE__ = 56 -__PTHREAD_CONDATTR_SIZE__ = 8 -__PTHREAD_COND_SIZE__ = 40 -__PTHREAD_ONCE_SIZE__ = 8 -__PTHREAD_RWLOCK_SIZE__ = 192 -__PTHREAD_RWLOCKATTR_SIZE__ = 16 -__PTHREAD_SIZE__ = 596 -__PTHREAD_ATTR_SIZE__ = 36 -__PTHREAD_MUTEXATTR_SIZE__ = 8 -__PTHREAD_MUTEX_SIZE__ = 40 -__PTHREAD_CONDATTR_SIZE__ = 4 -__PTHREAD_COND_SIZE__ = 24 -__PTHREAD_ONCE_SIZE__ = 4 -__PTHREAD_RWLOCK_SIZE__ = 124 -__PTHREAD_RWLOCKATTR_SIZE__ = 12 -__DARWIN_NULL = 0 - -# Included from stdint.h -__WORDSIZE = 64 -__WORDSIZE = 32 -INT8_MAX = 127 -INT16_MAX = 32767 -INT32_MAX = 2147483647 -INT8_MIN = -128 -INT16_MIN = -32768 -INT32_MIN = (-INT32_MAX-1) -UINT8_MAX = 255 -UINT16_MAX = 65535 -INT_LEAST8_MIN = INT8_MIN -INT_LEAST16_MIN = INT16_MIN -INT_LEAST32_MIN = INT32_MIN -INT_LEAST8_MAX = INT8_MAX -INT_LEAST16_MAX = INT16_MAX -INT_LEAST32_MAX = INT32_MAX -UINT_LEAST8_MAX = UINT8_MAX -UINT_LEAST16_MAX = UINT16_MAX -INT_FAST8_MIN = INT8_MIN -INT_FAST16_MIN = INT16_MIN -INT_FAST32_MIN = INT32_MIN -INT_FAST8_MAX = INT8_MAX -INT_FAST16_MAX = INT16_MAX -INT_FAST32_MAX = INT32_MAX -UINT_FAST8_MAX = UINT8_MAX -UINT_FAST16_MAX = UINT16_MAX -INTPTR_MIN = INT32_MIN -INTPTR_MAX = INT32_MAX -PTRDIFF_MIN = INT32_MIN -PTRDIFF_MAX = INT32_MAX -WCHAR_MAX = 0x7fffffff -WCHAR_MIN = 0 -WCHAR_MIN = (-WCHAR_MAX-1) -WINT_MIN = INT32_MIN -WINT_MAX = INT32_MAX -SIG_ATOMIC_MIN = INT32_MIN -SIG_ATOMIC_MAX = INT32_MAX -def INT8_C(v): return (v) - -def INT16_C(v): return (v) - -def INT32_C(v): return (v) - - -# Included from sys/socket.h - -# Included from machine/_param.h -SOCK_STREAM = 1 -SOCK_DGRAM = 2 -SOCK_RAW = 3 -SOCK_RDM = 4 -SOCK_SEQPACKET = 5 -SO_DEBUG = 0x0001 -SO_ACCEPTCONN = 0x0002 -SO_REUSEADDR = 0x0004 -SO_KEEPALIVE = 0x0008 -SO_DONTROUTE = 0x0010 -SO_BROADCAST = 0x0020 -SO_USELOOPBACK = 0x0040 -SO_LINGER = 0x0080 -SO_LINGER = 0x1080 -SO_OOBINLINE = 0x0100 -SO_REUSEPORT = 0x0200 -SO_TIMESTAMP = 0x0400 -SO_ACCEPTFILTER = 0x1000 -SO_DONTTRUNC = 0x2000 -SO_WANTMORE = 0x4000 -SO_WANTOOBFLAG = 0x8000 -SO_SNDBUF = 0x1001 -SO_RCVBUF = 0x1002 -SO_SNDLOWAT = 0x1003 -SO_RCVLOWAT = 0x1004 -SO_SNDTIMEO = 0x1005 -SO_RCVTIMEO = 0x1006 -SO_ERROR = 0x1007 -SO_TYPE = 0x1008 -SO_NREAD = 0x1020 -SO_NKE = 0x1021 -SO_NOSIGPIPE = 0x1022 -SO_NOADDRERR = 0x1023 -SO_NWRITE = 0x1024 -SO_REUSESHAREUID = 0x1025 -SO_NOTIFYCONFLICT = 0x1026 -SO_LINGER_SEC = 0x1080 -SO_RESTRICTIONS = 0x1081 -SO_RESTRICT_DENYIN = 0x00000001 -SO_RESTRICT_DENYOUT = 0x00000002 -SO_RESTRICT_DENYSET = (-2147483648) -SO_LABEL = 0x1010 -SO_PEERLABEL = 0x1011 -SOL_SOCKET = 0xffff -AF_UNSPEC = 0 -AF_UNIX = 1 -AF_LOCAL = AF_UNIX -AF_INET = 2 -AF_IMPLINK = 3 -AF_PUP = 4 -AF_CHAOS = 5 -AF_NS = 6 -AF_ISO = 7 -AF_OSI = AF_ISO -AF_ECMA = 8 -AF_DATAKIT = 9 -AF_CCITT = 10 -AF_SNA = 11 -AF_DECnet = 12 -AF_DLI = 13 -AF_LAT = 14 -AF_HYLINK = 15 -AF_APPLETALK = 16 -AF_ROUTE = 17 -AF_LINK = 18 -pseudo_AF_XTP = 19 -AF_COIP = 20 -AF_CNT = 21 -pseudo_AF_RTIP = 22 -AF_IPX = 23 -AF_SIP = 24 -pseudo_AF_PIP = 25 -AF_NDRV = 27 -AF_ISDN = 28 -AF_E164 = AF_ISDN -pseudo_AF_KEY = 29 -AF_INET6 = 30 -AF_NATM = 31 -AF_SYSTEM = 32 -AF_NETBIOS = 33 -AF_PPP = 34 -AF_ATM = 30 -pseudo_AF_HDRCMPLT = 35 -AF_RESERVED_36 = 36 -AF_NETGRAPH = 32 -AF_MAX = 37 -SOCK_MAXADDRLEN = 255 -_SS_MAXSIZE = 128 -PF_UNSPEC = AF_UNSPEC -PF_LOCAL = AF_LOCAL -PF_UNIX = PF_LOCAL -PF_INET = AF_INET -PF_IMPLINK = AF_IMPLINK -PF_PUP = AF_PUP -PF_CHAOS = AF_CHAOS -PF_NS = AF_NS -PF_ISO = AF_ISO -PF_OSI = AF_ISO -PF_ECMA = AF_ECMA -PF_DATAKIT = AF_DATAKIT -PF_CCITT = AF_CCITT -PF_SNA = AF_SNA -PF_DECnet = AF_DECnet -PF_DLI = AF_DLI -PF_LAT = AF_LAT -PF_HYLINK = AF_HYLINK -PF_APPLETALK = AF_APPLETALK -PF_ROUTE = AF_ROUTE -PF_LINK = AF_LINK -PF_XTP = pseudo_AF_XTP -PF_COIP = AF_COIP -PF_CNT = AF_CNT -PF_SIP = AF_SIP -PF_IPX = AF_IPX -PF_RTIP = pseudo_AF_RTIP -PF_PIP = pseudo_AF_PIP -PF_NDRV = AF_NDRV -PF_ISDN = AF_ISDN -PF_KEY = pseudo_AF_KEY -PF_INET6 = AF_INET6 -PF_NATM = AF_NATM -PF_SYSTEM = AF_SYSTEM -PF_NETBIOS = AF_NETBIOS -PF_PPP = AF_PPP -PF_RESERVED_36 = AF_RESERVED_36 -PF_ATM = AF_ATM -PF_NETGRAPH = AF_NETGRAPH -PF_MAX = AF_MAX -NET_MAXID = AF_MAX -NET_RT_DUMP = 1 -NET_RT_FLAGS = 2 -NET_RT_IFLIST = 3 -NET_RT_STAT = 4 -NET_RT_TRASH = 5 -NET_RT_IFLIST2 = 6 -NET_RT_DUMP2 = 7 -NET_RT_MAXID = 8 -SOMAXCONN = 128 -MSG_OOB = 0x1 -MSG_PEEK = 0x2 -MSG_DONTROUTE = 0x4 -MSG_EOR = 0x8 -MSG_TRUNC = 0x10 -MSG_CTRUNC = 0x20 -MSG_WAITALL = 0x40 -MSG_DONTWAIT = 0x80 -MSG_EOF = 0x100 -MSG_WAITSTREAM = 0x200 -MSG_FLUSH = 0x400 -MSG_HOLD = 0x800 -MSG_SEND = 0x1000 -MSG_HAVEMORE = 0x2000 -MSG_RCVMORE = 0x4000 -MSG_NEEDSA = 0x10000 -CMGROUP_MAX = 16 -SCM_RIGHTS = 0x01 -SCM_TIMESTAMP = 0x02 -SCM_CREDS = 0x03 -SHUT_RD = 0 -SHUT_WR = 1 -SHUT_RDWR = 2 - -# Included from machine/endian.h - -# Included from sys/_endian.h -def ntohl(x): return (x) - -def ntohs(x): return (x) - -def htonl(x): return (x) - -def htons(x): return (x) - -def NTOHL(x): return (x) - -def NTOHS(x): return (x) - -def HTONL(x): return (x) - -def HTONS(x): return (x) - - -# Included from libkern/_OSByteOrder.h -def __DARWIN_OSSwapConstInt16(x): return \ - -def __DARWIN_OSSwapConstInt32(x): return \ - -def __DARWIN_OSSwapConstInt64(x): return \ - - -# Included from libkern/i386/_OSByteOrder.h -def __DARWIN_OSSwapInt16(x): return \ - -def __DARWIN_OSSwapInt32(x): return \ - -def __DARWIN_OSSwapInt64(x): return \ - -def __DARWIN_OSSwapInt16(x): return _OSSwapInt16(x) - -def __DARWIN_OSSwapInt32(x): return _OSSwapInt32(x) - -def __DARWIN_OSSwapInt64(x): return _OSSwapInt64(x) - -def ntohs(x): return __DARWIN_OSSwapInt16(x) - -def htons(x): return __DARWIN_OSSwapInt16(x) - -def ntohl(x): return __DARWIN_OSSwapInt32(x) - -def htonl(x): return __DARWIN_OSSwapInt32(x) - -IPPROTO_IP = 0 -IPPROTO_HOPOPTS = 0 -IPPROTO_ICMP = 1 -IPPROTO_IGMP = 2 -IPPROTO_GGP = 3 -IPPROTO_IPV4 = 4 -IPPROTO_IPIP = IPPROTO_IPV4 -IPPROTO_TCP = 6 -IPPROTO_ST = 7 -IPPROTO_EGP = 8 -IPPROTO_PIGP = 9 -IPPROTO_RCCMON = 10 -IPPROTO_NVPII = 11 -IPPROTO_PUP = 12 -IPPROTO_ARGUS = 13 -IPPROTO_EMCON = 14 -IPPROTO_XNET = 15 -IPPROTO_CHAOS = 16 -IPPROTO_UDP = 17 -IPPROTO_MUX = 18 -IPPROTO_MEAS = 19 -IPPROTO_HMP = 20 -IPPROTO_PRM = 21 -IPPROTO_IDP = 22 -IPPROTO_TRUNK1 = 23 -IPPROTO_TRUNK2 = 24 -IPPROTO_LEAF1 = 25 -IPPROTO_LEAF2 = 26 -IPPROTO_RDP = 27 -IPPROTO_IRTP = 28 -IPPROTO_TP = 29 -IPPROTO_BLT = 30 -IPPROTO_NSP = 31 -IPPROTO_INP = 32 -IPPROTO_SEP = 33 -IPPROTO_3PC = 34 -IPPROTO_IDPR = 35 -IPPROTO_XTP = 36 -IPPROTO_DDP = 37 -IPPROTO_CMTP = 38 -IPPROTO_TPXX = 39 -IPPROTO_IL = 40 -IPPROTO_IPV6 = 41 -IPPROTO_SDRP = 42 -IPPROTO_ROUTING = 43 -IPPROTO_FRAGMENT = 44 -IPPROTO_IDRP = 45 -IPPROTO_RSVP = 46 -IPPROTO_GRE = 47 -IPPROTO_MHRP = 48 -IPPROTO_BHA = 49 -IPPROTO_ESP = 50 -IPPROTO_AH = 51 -IPPROTO_INLSP = 52 -IPPROTO_SWIPE = 53 -IPPROTO_NHRP = 54 -IPPROTO_ICMPV6 = 58 -IPPROTO_NONE = 59 -IPPROTO_DSTOPTS = 60 -IPPROTO_AHIP = 61 -IPPROTO_CFTP = 62 -IPPROTO_HELLO = 63 -IPPROTO_SATEXPAK = 64 -IPPROTO_KRYPTOLAN = 65 -IPPROTO_RVD = 66 -IPPROTO_IPPC = 67 -IPPROTO_ADFS = 68 -IPPROTO_SATMON = 69 -IPPROTO_VISA = 70 -IPPROTO_IPCV = 71 -IPPROTO_CPNX = 72 -IPPROTO_CPHB = 73 -IPPROTO_WSN = 74 -IPPROTO_PVP = 75 -IPPROTO_BRSATMON = 76 -IPPROTO_ND = 77 -IPPROTO_WBMON = 78 -IPPROTO_WBEXPAK = 79 -IPPROTO_EON = 80 -IPPROTO_VMTP = 81 -IPPROTO_SVMTP = 82 -IPPROTO_VINES = 83 -IPPROTO_TTP = 84 -IPPROTO_IGP = 85 -IPPROTO_DGP = 86 -IPPROTO_TCF = 87 -IPPROTO_IGRP = 88 -IPPROTO_OSPFIGP = 89 -IPPROTO_SRPC = 90 -IPPROTO_LARP = 91 -IPPROTO_MTP = 92 -IPPROTO_AX25 = 93 -IPPROTO_IPEIP = 94 -IPPROTO_MICP = 95 -IPPROTO_SCCSP = 96 -IPPROTO_ETHERIP = 97 -IPPROTO_ENCAP = 98 -IPPROTO_APES = 99 -IPPROTO_GMTP = 100 -IPPROTO_IPCOMP = 108 -IPPROTO_PIM = 103 -IPPROTO_PGM = 113 -IPPROTO_DIVERT = 254 -IPPROTO_RAW = 255 -IPPROTO_MAX = 256 -IPPROTO_DONE = 257 -__DARWIN_IPPORT_RESERVED = 1024 -IPPORT_RESERVED = __DARWIN_IPPORT_RESERVED -IPPORT_USERRESERVED = 5000 -IPPORT_HIFIRSTAUTO = 49152 -IPPORT_HILASTAUTO = 65535 -IPPORT_RESERVEDSTART = 600 -def IN_CLASSA(i): return (((u_int32_t)(i) & (-2147483648)) == 0) - -IN_CLASSA_NET = (-16777216) -IN_CLASSA_NSHIFT = 24 -IN_CLASSA_HOST = 0x00ffffff -IN_CLASSA_MAX = 128 -def IN_CLASSB(i): return (((u_int32_t)(i) & (-1073741824)) == (-2147483648)) - -IN_CLASSB_NET = (-65536) -IN_CLASSB_NSHIFT = 16 -IN_CLASSB_HOST = 0x0000ffff -IN_CLASSB_MAX = 65536 -def IN_CLASSC(i): return (((u_int32_t)(i) & (-536870912)) == (-1073741824)) - -IN_CLASSC_NET = (-256) -IN_CLASSC_NSHIFT = 8 -IN_CLASSC_HOST = 0x000000ff -def IN_CLASSD(i): return (((u_int32_t)(i) & (-268435456)) == (-536870912)) - -IN_CLASSD_NET = (-268435456) -IN_CLASSD_NSHIFT = 28 -IN_CLASSD_HOST = 0x0fffffff -def IN_MULTICAST(i): return IN_CLASSD(i) - -def IN_EXPERIMENTAL(i): return (((u_int32_t)(i) & (-268435456)) == (-268435456)) - -def IN_BADCLASS(i): return (((u_int32_t)(i) & (-268435456)) == (-268435456)) - -INADDR_NONE = (-1) -def IN_LINKLOCAL(i): return (((u_int32_t)(i) & IN_CLASSB_NET) == IN_LINKLOCALNETNUM) - -IN_LOOPBACKNET = 127 -INET_ADDRSTRLEN = 16 -IP_OPTIONS = 1 -IP_HDRINCL = 2 -IP_TOS = 3 -IP_TTL = 4 -IP_RECVOPTS = 5 -IP_RECVRETOPTS = 6 -IP_RECVDSTADDR = 7 -IP_RETOPTS = 8 -IP_MULTICAST_IF = 9 -IP_MULTICAST_TTL = 10 -IP_MULTICAST_LOOP = 11 -IP_ADD_MEMBERSHIP = 12 -IP_DROP_MEMBERSHIP = 13 -IP_MULTICAST_VIF = 14 -IP_RSVP_ON = 15 -IP_RSVP_OFF = 16 -IP_RSVP_VIF_ON = 17 -IP_RSVP_VIF_OFF = 18 -IP_PORTRANGE = 19 -IP_RECVIF = 20 -IP_IPSEC_POLICY = 21 -IP_FAITH = 22 -IP_STRIPHDR = 23 -IP_RECVTTL = 24 -IP_FW_ADD = 40 -IP_FW_DEL = 41 -IP_FW_FLUSH = 42 -IP_FW_ZERO = 43 -IP_FW_GET = 44 -IP_FW_RESETLOG = 45 -IP_OLD_FW_ADD = 50 -IP_OLD_FW_DEL = 51 -IP_OLD_FW_FLUSH = 52 -IP_OLD_FW_ZERO = 53 -IP_OLD_FW_GET = 54 -IP_NAT__XXX = 55 -IP_OLD_FW_RESETLOG = 56 -IP_DUMMYNET_CONFIGURE = 60 -IP_DUMMYNET_DEL = 61 -IP_DUMMYNET_FLUSH = 62 -IP_DUMMYNET_GET = 64 -IP_TRAFFIC_MGT_BACKGROUND = 65 -IP_FORCE_OUT_IFP = 69 -TRAFFIC_MGT_SO_BACKGROUND = 0x0001 -TRAFFIC_MGT_SO_BG_SUPPRESSED = 0x0002 -IP_DEFAULT_MULTICAST_TTL = 1 -IP_DEFAULT_MULTICAST_LOOP = 1 -IP_MAX_MEMBERSHIPS = 20 -IP_PORTRANGE_DEFAULT = 0 -IP_PORTRANGE_HIGH = 1 -IP_PORTRANGE_LOW = 2 -IPPROTO_MAXID = (IPPROTO_AH + 1) -IPCTL_FORWARDING = 1 -IPCTL_SENDREDIRECTS = 2 -IPCTL_DEFTTL = 3 -IPCTL_DEFMTU = 4 -IPCTL_RTEXPIRE = 5 -IPCTL_RTMINEXPIRE = 6 -IPCTL_RTMAXCACHE = 7 -IPCTL_SOURCEROUTE = 8 -IPCTL_DIRECTEDBROADCAST = 9 -IPCTL_INTRQMAXLEN = 10 -IPCTL_INTRQDROPS = 11 -IPCTL_STATS = 12 -IPCTL_ACCEPTSOURCEROUTE = 13 -IPCTL_FASTFORWARDING = 14 -IPCTL_KEEPFAITH = 15 -IPCTL_GIF_TTL = 16 -IPCTL_MAXID = 17 - -# Included from netinet6/in6.h -__KAME_VERSION = "20010528/apple-darwin" -IPV6PORT_RESERVED = 1024 -IPV6PORT_ANONMIN = 49152 -IPV6PORT_ANONMAX = 65535 -IPV6PORT_RESERVEDMIN = 600 -IPV6PORT_RESERVEDMAX = (IPV6PORT_RESERVED-1) -INET6_ADDRSTRLEN = 46 -def IN6_IS_ADDR_UNSPECIFIED(a): return \ - -def IN6_IS_ADDR_LOOPBACK(a): return \ - -def IN6_IS_ADDR_V4COMPAT(a): return \ - -def IN6_IS_ADDR_V4MAPPED(a): return \ - -__IPV6_ADDR_SCOPE_NODELOCAL = 0x01 -__IPV6_ADDR_SCOPE_LINKLOCAL = 0x02 -__IPV6_ADDR_SCOPE_SITELOCAL = 0x05 -__IPV6_ADDR_SCOPE_ORGLOCAL = 0x08 -__IPV6_ADDR_SCOPE_GLOBAL = 0x0e -def IN6_IS_ADDR_LINKLOCAL(a): return \ - -def IN6_IS_ADDR_SITELOCAL(a): return \ - -def IN6_IS_ADDR_MC_NODELOCAL(a): return \ - -def IN6_IS_ADDR_MC_LINKLOCAL(a): return \ - -def IN6_IS_ADDR_MC_SITELOCAL(a): return \ - -def IN6_IS_ADDR_MC_ORGLOCAL(a): return \ - -def IN6_IS_ADDR_MC_GLOBAL(a): return \ - -IPV6_OPTIONS = 1 -IPV6_RECVOPTS = 5 -IPV6_RECVRETOPTS = 6 -IPV6_RECVDSTADDR = 7 -IPV6_RETOPTS = 8 -IPV6_SOCKOPT_RESERVED1 = 3 -IPV6_UNICAST_HOPS = 4 -IPV6_MULTICAST_IF = 9 -IPV6_MULTICAST_HOPS = 10 -IPV6_MULTICAST_LOOP = 11 -IPV6_JOIN_GROUP = 12 -IPV6_LEAVE_GROUP = 13 -IPV6_PORTRANGE = 14 -ICMP6_FILTER = 18 -IPV6_PKTINFO = 19 -IPV6_HOPLIMIT = 20 -IPV6_NEXTHOP = 21 -IPV6_HOPOPTS = 22 -IPV6_DSTOPTS = 23 -IPV6_RTHDR = 24 -IPV6_PKTOPTIONS = 25 -IPV6_CHECKSUM = 26 -IPV6_V6ONLY = 27 -IPV6_BINDV6ONLY = IPV6_V6ONLY -IPV6_IPSEC_POLICY = 28 -IPV6_FAITH = 29 -IPV6_FW_ADD = 30 -IPV6_FW_DEL = 31 -IPV6_FW_FLUSH = 32 -IPV6_FW_ZERO = 33 -IPV6_FW_GET = 34 -IPV6_RTHDR_LOOSE = 0 -IPV6_RTHDR_STRICT = 1 -IPV6_RTHDR_TYPE_0 = 0 -IPV6_DEFAULT_MULTICAST_HOPS = 1 -IPV6_DEFAULT_MULTICAST_LOOP = 1 -IPV6_PORTRANGE_DEFAULT = 0 -IPV6_PORTRANGE_HIGH = 1 -IPV6_PORTRANGE_LOW = 2 -IPV6PROTO_MAXID = (IPPROTO_PIM + 1) -IPV6CTL_FORWARDING = 1 -IPV6CTL_SENDREDIRECTS = 2 -IPV6CTL_DEFHLIM = 3 -IPV6CTL_DEFMTU = 4 -IPV6CTL_FORWSRCRT = 5 -IPV6CTL_STATS = 6 -IPV6CTL_MRTSTATS = 7 -IPV6CTL_MRTPROTO = 8 -IPV6CTL_MAXFRAGPACKETS = 9 -IPV6CTL_SOURCECHECK = 10 -IPV6CTL_SOURCECHECK_LOGINT = 11 -IPV6CTL_ACCEPT_RTADV = 12 -IPV6CTL_KEEPFAITH = 13 -IPV6CTL_LOG_INTERVAL = 14 -IPV6CTL_HDRNESTLIMIT = 15 -IPV6CTL_DAD_COUNT = 16 -IPV6CTL_AUTO_FLOWLABEL = 17 -IPV6CTL_DEFMCASTHLIM = 18 -IPV6CTL_GIF_HLIM = 19 -IPV6CTL_KAME_VERSION = 20 -IPV6CTL_USE_DEPRECATED = 21 -IPV6CTL_RR_PRUNE = 22 -IPV6CTL_MAPPED_ADDR = 23 -IPV6CTL_V6ONLY = 24 -IPV6CTL_RTEXPIRE = 25 -IPV6CTL_RTMINEXPIRE = 26 -IPV6CTL_RTMAXCACHE = 27 -IPV6CTL_USETEMPADDR = 32 -IPV6CTL_TEMPPLTIME = 33 -IPV6CTL_TEMPVLTIME = 34 -IPV6CTL_AUTO_LINKLOCAL = 35 -IPV6CTL_RIP6STATS = 36 -IPV6CTL_MAXFRAGS = 41 -IPV6CTL_MAXID = 42 diff --git a/Lib/plat-darwin/regen b/Lib/plat-darwin/regen deleted file mode 100755 index a20cdc1518..0000000000 --- a/Lib/plat-darwin/regen +++ /dev/null @@ -1,3 +0,0 @@ -#! /bin/sh -set -v -python$EXE ../../Tools/scripts/h2py.py -i '(u_long)' /usr/include/netinet/in.h diff --git a/Lib/plat-freebsd4/IN.py b/Lib/plat-freebsd4/IN.py deleted file mode 100644 index bca241884f..0000000000 --- a/Lib/plat-freebsd4/IN.py +++ /dev/null @@ -1,355 +0,0 @@ -# Generated by h2py from /usr/include/netinet/in.h -IPPROTO_IP = 0 -IPPROTO_HOPOPTS = 0 -IPPROTO_ICMP = 1 -IPPROTO_IGMP = 2 -IPPROTO_GGP = 3 -IPPROTO_IPV4 = 4 -IPPROTO_IPIP = IPPROTO_IPV4 -IPPROTO_TCP = 6 -IPPROTO_ST = 7 -IPPROTO_EGP = 8 -IPPROTO_PIGP = 9 -IPPROTO_RCCMON = 10 -IPPROTO_NVPII = 11 -IPPROTO_PUP = 12 -IPPROTO_ARGUS = 13 -IPPROTO_EMCON = 14 -IPPROTO_XNET = 15 -IPPROTO_CHAOS = 16 -IPPROTO_UDP = 17 -IPPROTO_MUX = 18 -IPPROTO_MEAS = 19 -IPPROTO_HMP = 20 -IPPROTO_PRM = 21 -IPPROTO_IDP = 22 -IPPROTO_TRUNK1 = 23 -IPPROTO_TRUNK2 = 24 -IPPROTO_LEAF1 = 25 -IPPROTO_LEAF2 = 26 -IPPROTO_RDP = 27 -IPPROTO_IRTP = 28 -IPPROTO_TP = 29 -IPPROTO_BLT = 30 -IPPROTO_NSP = 31 -IPPROTO_INP = 32 -IPPROTO_SEP = 33 -IPPROTO_3PC = 34 -IPPROTO_IDPR = 35 -IPPROTO_XTP = 36 -IPPROTO_DDP = 37 -IPPROTO_CMTP = 38 -IPPROTO_TPXX = 39 -IPPROTO_IL = 40 -IPPROTO_IPV6 = 41 -IPPROTO_SDRP = 42 -IPPROTO_ROUTING = 43 -IPPROTO_FRAGMENT = 44 -IPPROTO_IDRP = 45 -IPPROTO_RSVP = 46 -IPPROTO_GRE = 47 -IPPROTO_MHRP = 48 -IPPROTO_BHA = 49 -IPPROTO_ESP = 50 -IPPROTO_AH = 51 -IPPROTO_INLSP = 52 -IPPROTO_SWIPE = 53 -IPPROTO_NHRP = 54 -IPPROTO_ICMPV6 = 58 -IPPROTO_NONE = 59 -IPPROTO_DSTOPTS = 60 -IPPROTO_AHIP = 61 -IPPROTO_CFTP = 62 -IPPROTO_HELLO = 63 -IPPROTO_SATEXPAK = 64 -IPPROTO_KRYPTOLAN = 65 -IPPROTO_RVD = 66 -IPPROTO_IPPC = 67 -IPPROTO_ADFS = 68 -IPPROTO_SATMON = 69 -IPPROTO_VISA = 70 -IPPROTO_IPCV = 71 -IPPROTO_CPNX = 72 -IPPROTO_CPHB = 73 -IPPROTO_WSN = 74 -IPPROTO_PVP = 75 -IPPROTO_BRSATMON = 76 -IPPROTO_ND = 77 -IPPROTO_WBMON = 78 -IPPROTO_WBEXPAK = 79 -IPPROTO_EON = 80 -IPPROTO_VMTP = 81 -IPPROTO_SVMTP = 82 -IPPROTO_VINES = 83 -IPPROTO_TTP = 84 -IPPROTO_IGP = 85 -IPPROTO_DGP = 86 -IPPROTO_TCF = 87 -IPPROTO_IGRP = 88 -IPPROTO_OSPFIGP = 89 -IPPROTO_SRPC = 90 -IPPROTO_LARP = 91 -IPPROTO_MTP = 92 -IPPROTO_AX25 = 93 -IPPROTO_IPEIP = 94 -IPPROTO_MICP = 95 -IPPROTO_SCCSP = 96 -IPPROTO_ETHERIP = 97 -IPPROTO_ENCAP = 98 -IPPROTO_APES = 99 -IPPROTO_GMTP = 100 -IPPROTO_IPCOMP = 108 -IPPROTO_PIM = 103 -IPPROTO_PGM = 113 -IPPROTO_DIVERT = 254 -IPPROTO_RAW = 255 -IPPROTO_MAX = 256 -IPPROTO_DONE = 257 -IPPORT_RESERVED = 1024 -IPPORT_USERRESERVED = 5000 -IPPORT_HIFIRSTAUTO = 49152 -IPPORT_HILASTAUTO = 65535 -IPPORT_RESERVEDSTART = 600 -def IN_CLASSA(i): return (((u_int32_t)(i) & 0x80000000) == 0) - -IN_CLASSA_NET = 0xff000000 -IN_CLASSA_NSHIFT = 24 -IN_CLASSA_HOST = 0x00ffffff -IN_CLASSA_MAX = 128 -def IN_CLASSB(i): return (((u_int32_t)(i) & 0xc0000000) == 0x80000000) - -IN_CLASSB_NET = 0xffff0000 -IN_CLASSB_NSHIFT = 16 -IN_CLASSB_HOST = 0x0000ffff -IN_CLASSB_MAX = 65536 -def IN_CLASSC(i): return (((u_int32_t)(i) & 0xe0000000) == 0xc0000000) - -IN_CLASSC_NET = 0xffffff00 -IN_CLASSC_NSHIFT = 8 -IN_CLASSC_HOST = 0x000000ff -def IN_CLASSD(i): return (((u_int32_t)(i) & 0xf0000000) == 0xe0000000) - -IN_CLASSD_NET = 0xf0000000 -IN_CLASSD_NSHIFT = 28 -IN_CLASSD_HOST = 0x0fffffff -def IN_MULTICAST(i): return IN_CLASSD(i) - -def IN_EXPERIMENTAL(i): return (((u_int32_t)(i) & 0xf0000000) == 0xf0000000) - -def IN_BADCLASS(i): return (((u_int32_t)(i) & 0xf0000000) == 0xf0000000) - -INADDR_NONE = 0xffffffff -IN_LOOPBACKNET = 127 -INET_ADDRSTRLEN = 16 -IP_OPTIONS = 1 -IP_HDRINCL = 2 -IP_TOS = 3 -IP_TTL = 4 -IP_RECVOPTS = 5 -IP_RECVRETOPTS = 6 -IP_RECVDSTADDR = 7 -IP_RETOPTS = 8 -IP_MULTICAST_IF = 9 -IP_MULTICAST_TTL = 10 -IP_MULTICAST_LOOP = 11 -IP_ADD_MEMBERSHIP = 12 -IP_DROP_MEMBERSHIP = 13 -IP_MULTICAST_VIF = 14 -IP_RSVP_ON = 15 -IP_RSVP_OFF = 16 -IP_RSVP_VIF_ON = 17 -IP_RSVP_VIF_OFF = 18 -IP_PORTRANGE = 19 -IP_RECVIF = 20 -IP_IPSEC_POLICY = 21 -IP_FAITH = 22 -IP_FW_ADD = 50 -IP_FW_DEL = 51 -IP_FW_FLUSH = 52 -IP_FW_ZERO = 53 -IP_FW_GET = 54 -IP_FW_RESETLOG = 55 -IP_DUMMYNET_CONFIGURE = 60 -IP_DUMMYNET_DEL = 61 -IP_DUMMYNET_FLUSH = 62 -IP_DUMMYNET_GET = 64 -IP_DEFAULT_MULTICAST_TTL = 1 -IP_DEFAULT_MULTICAST_LOOP = 1 -IP_MAX_MEMBERSHIPS = 20 -IP_PORTRANGE_DEFAULT = 0 -IP_PORTRANGE_HIGH = 1 -IP_PORTRANGE_LOW = 2 -IPPROTO_MAXID = (IPPROTO_AH + 1) -IPCTL_FORWARDING = 1 -IPCTL_SENDREDIRECTS = 2 -IPCTL_DEFTTL = 3 -IPCTL_DEFMTU = 4 -IPCTL_RTEXPIRE = 5 -IPCTL_RTMINEXPIRE = 6 -IPCTL_RTMAXCACHE = 7 -IPCTL_SOURCEROUTE = 8 -IPCTL_DIRECTEDBROADCAST = 9 -IPCTL_INTRQMAXLEN = 10 -IPCTL_INTRQDROPS = 11 -IPCTL_STATS = 12 -IPCTL_ACCEPTSOURCEROUTE = 13 -IPCTL_FASTFORWARDING = 14 -IPCTL_KEEPFAITH = 15 -IPCTL_GIF_TTL = 16 -IPCTL_MAXID = 17 - -# Included from netinet6/in6.h - -# Included from sys/queue.h -def SLIST_HEAD_INITIALIZER(head): return \ - -def SLIST_ENTRY(type): return \ - -def STAILQ_HEAD_INITIALIZER(head): return \ - -def STAILQ_ENTRY(type): return \ - -def LIST_HEAD_INITIALIZER(head): return \ - -def LIST_ENTRY(type): return \ - -def TAILQ_HEAD_INITIALIZER(head): return \ - -def TAILQ_ENTRY(type): return \ - -def CIRCLEQ_ENTRY(type): return \ - -__KAME_VERSION = "20000701/FreeBSD-current" -IPV6PORT_RESERVED = 1024 -IPV6PORT_ANONMIN = 49152 -IPV6PORT_ANONMAX = 65535 -IPV6PORT_RESERVEDMIN = 600 -IPV6PORT_RESERVEDMAX = (IPV6PORT_RESERVED-1) -INET6_ADDRSTRLEN = 46 -IPV6_ADDR_INT32_ONE = 1 -IPV6_ADDR_INT32_TWO = 2 -IPV6_ADDR_INT32_MNL = 0xff010000 -IPV6_ADDR_INT32_MLL = 0xff020000 -IPV6_ADDR_INT32_SMP = 0x0000ffff -IPV6_ADDR_INT16_ULL = 0xfe80 -IPV6_ADDR_INT16_USL = 0xfec0 -IPV6_ADDR_INT16_MLL = 0xff02 -IPV6_ADDR_INT32_ONE = 0x01000000 -IPV6_ADDR_INT32_TWO = 0x02000000 -IPV6_ADDR_INT32_MNL = 0x000001ff -IPV6_ADDR_INT32_MLL = 0x000002ff -IPV6_ADDR_INT32_SMP = 0xffff0000 -IPV6_ADDR_INT16_ULL = 0x80fe -IPV6_ADDR_INT16_USL = 0xc0fe -IPV6_ADDR_INT16_MLL = 0x02ff -def IN6_IS_ADDR_UNSPECIFIED(a): return \ - -def IN6_IS_ADDR_LOOPBACK(a): return \ - -def IN6_IS_ADDR_V4COMPAT(a): return \ - -def IN6_IS_ADDR_V4MAPPED(a): return \ - -IPV6_ADDR_SCOPE_NODELOCAL = 0x01 -IPV6_ADDR_SCOPE_LINKLOCAL = 0x02 -IPV6_ADDR_SCOPE_SITELOCAL = 0x05 -IPV6_ADDR_SCOPE_ORGLOCAL = 0x08 -IPV6_ADDR_SCOPE_GLOBAL = 0x0e -__IPV6_ADDR_SCOPE_NODELOCAL = 0x01 -__IPV6_ADDR_SCOPE_LINKLOCAL = 0x02 -__IPV6_ADDR_SCOPE_SITELOCAL = 0x05 -__IPV6_ADDR_SCOPE_ORGLOCAL = 0x08 -__IPV6_ADDR_SCOPE_GLOBAL = 0x0e -def IN6_IS_ADDR_LINKLOCAL(a): return \ - -def IN6_IS_ADDR_SITELOCAL(a): return \ - -def IN6_IS_ADDR_MC_NODELOCAL(a): return \ - -def IN6_IS_ADDR_MC_LINKLOCAL(a): return \ - -def IN6_IS_ADDR_MC_SITELOCAL(a): return \ - -def IN6_IS_ADDR_MC_ORGLOCAL(a): return \ - -def IN6_IS_ADDR_MC_GLOBAL(a): return \ - -def IN6_IS_ADDR_MC_NODELOCAL(a): return \ - -def IN6_IS_ADDR_MC_LINKLOCAL(a): return \ - -def IN6_IS_ADDR_MC_SITELOCAL(a): return \ - -def IN6_IS_ADDR_MC_ORGLOCAL(a): return \ - -def IN6_IS_ADDR_MC_GLOBAL(a): return \ - -def IN6_IS_SCOPE_LINKLOCAL(a): return \ - -IPV6_OPTIONS = 1 -IPV6_RECVOPTS = 5 -IPV6_RECVRETOPTS = 6 -IPV6_RECVDSTADDR = 7 -IPV6_RETOPTS = 8 -IPV6_SOCKOPT_RESERVED1 = 3 -IPV6_UNICAST_HOPS = 4 -IPV6_MULTICAST_IF = 9 -IPV6_MULTICAST_HOPS = 10 -IPV6_MULTICAST_LOOP = 11 -IPV6_JOIN_GROUP = 12 -IPV6_LEAVE_GROUP = 13 -IPV6_PORTRANGE = 14 -ICMP6_FILTER = 18 -IPV6_PKTINFO = 19 -IPV6_HOPLIMIT = 20 -IPV6_NEXTHOP = 21 -IPV6_HOPOPTS = 22 -IPV6_DSTOPTS = 23 -IPV6_RTHDR = 24 -IPV6_PKTOPTIONS = 25 -IPV6_CHECKSUM = 26 -IPV6_BINDV6ONLY = 27 -IPV6_IPSEC_POLICY = 28 -IPV6_FAITH = 29 -IPV6_FW_ADD = 30 -IPV6_FW_DEL = 31 -IPV6_FW_FLUSH = 32 -IPV6_FW_ZERO = 33 -IPV6_FW_GET = 34 -IPV6_RTHDR_LOOSE = 0 -IPV6_RTHDR_STRICT = 1 -IPV6_RTHDR_TYPE_0 = 0 -IPV6_DEFAULT_MULTICAST_HOPS = 1 -IPV6_DEFAULT_MULTICAST_LOOP = 1 -IPV6_PORTRANGE_DEFAULT = 0 -IPV6_PORTRANGE_HIGH = 1 -IPV6_PORTRANGE_LOW = 2 -IPV6PROTO_MAXID = (IPPROTO_PIM + 1) -IPV6CTL_FORWARDING = 1 -IPV6CTL_SENDREDIRECTS = 2 -IPV6CTL_DEFHLIM = 3 -IPV6CTL_DEFMTU = 4 -IPV6CTL_FORWSRCRT = 5 -IPV6CTL_STATS = 6 -IPV6CTL_MRTSTATS = 7 -IPV6CTL_MRTPROTO = 8 -IPV6CTL_MAXFRAGPACKETS = 9 -IPV6CTL_SOURCECHECK = 10 -IPV6CTL_SOURCECHECK_LOGINT = 11 -IPV6CTL_ACCEPT_RTADV = 12 -IPV6CTL_KEEPFAITH = 13 -IPV6CTL_LOG_INTERVAL = 14 -IPV6CTL_HDRNESTLIMIT = 15 -IPV6CTL_DAD_COUNT = 16 -IPV6CTL_AUTO_FLOWLABEL = 17 -IPV6CTL_DEFMCASTHLIM = 18 -IPV6CTL_GIF_HLIM = 19 -IPV6CTL_KAME_VERSION = 20 -IPV6CTL_USE_DEPRECATED = 21 -IPV6CTL_RR_PRUNE = 22 -IPV6CTL_MAPPED_ADDR = 23 -IPV6CTL_BINDV6ONLY = 24 -IPV6CTL_RTEXPIRE = 25 -IPV6CTL_RTMINEXPIRE = 26 -IPV6CTL_RTMAXCACHE = 27 -IPV6CTL_MAXID = 28 diff --git a/Lib/plat-freebsd4/regen b/Lib/plat-freebsd4/regen deleted file mode 100644 index 8aa6898c6a..0000000000 --- a/Lib/plat-freebsd4/regen +++ /dev/null @@ -1,3 +0,0 @@ -#! /bin/sh -set -v -python ../../Tools/scripts/h2py.py -i '(u_long)' /usr/include/netinet/in.h diff --git a/Lib/plat-freebsd5/IN.py b/Lib/plat-freebsd5/IN.py deleted file mode 100644 index bca241884f..0000000000 --- a/Lib/plat-freebsd5/IN.py +++ /dev/null @@ -1,355 +0,0 @@ -# Generated by h2py from /usr/include/netinet/in.h -IPPROTO_IP = 0 -IPPROTO_HOPOPTS = 0 -IPPROTO_ICMP = 1 -IPPROTO_IGMP = 2 -IPPROTO_GGP = 3 -IPPROTO_IPV4 = 4 -IPPROTO_IPIP = IPPROTO_IPV4 -IPPROTO_TCP = 6 -IPPROTO_ST = 7 -IPPROTO_EGP = 8 -IPPROTO_PIGP = 9 -IPPROTO_RCCMON = 10 -IPPROTO_NVPII = 11 -IPPROTO_PUP = 12 -IPPROTO_ARGUS = 13 -IPPROTO_EMCON = 14 -IPPROTO_XNET = 15 -IPPROTO_CHAOS = 16 -IPPROTO_UDP = 17 -IPPROTO_MUX = 18 -IPPROTO_MEAS = 19 -IPPROTO_HMP = 20 -IPPROTO_PRM = 21 -IPPROTO_IDP = 22 -IPPROTO_TRUNK1 = 23 -IPPROTO_TRUNK2 = 24 -IPPROTO_LEAF1 = 25 -IPPROTO_LEAF2 = 26 -IPPROTO_RDP = 27 -IPPROTO_IRTP = 28 -IPPROTO_TP = 29 -IPPROTO_BLT = 30 -IPPROTO_NSP = 31 -IPPROTO_INP = 32 -IPPROTO_SEP = 33 -IPPROTO_3PC = 34 -IPPROTO_IDPR = 35 -IPPROTO_XTP = 36 -IPPROTO_DDP = 37 -IPPROTO_CMTP = 38 -IPPROTO_TPXX = 39 -IPPROTO_IL = 40 -IPPROTO_IPV6 = 41 -IPPROTO_SDRP = 42 -IPPROTO_ROUTING = 43 -IPPROTO_FRAGMENT = 44 -IPPROTO_IDRP = 45 -IPPROTO_RSVP = 46 -IPPROTO_GRE = 47 -IPPROTO_MHRP = 48 -IPPROTO_BHA = 49 -IPPROTO_ESP = 50 -IPPROTO_AH = 51 -IPPROTO_INLSP = 52 -IPPROTO_SWIPE = 53 -IPPROTO_NHRP = 54 -IPPROTO_ICMPV6 = 58 -IPPROTO_NONE = 59 -IPPROTO_DSTOPTS = 60 -IPPROTO_AHIP = 61 -IPPROTO_CFTP = 62 -IPPROTO_HELLO = 63 -IPPROTO_SATEXPAK = 64 -IPPROTO_KRYPTOLAN = 65 -IPPROTO_RVD = 66 -IPPROTO_IPPC = 67 -IPPROTO_ADFS = 68 -IPPROTO_SATMON = 69 -IPPROTO_VISA = 70 -IPPROTO_IPCV = 71 -IPPROTO_CPNX = 72 -IPPROTO_CPHB = 73 -IPPROTO_WSN = 74 -IPPROTO_PVP = 75 -IPPROTO_BRSATMON = 76 -IPPROTO_ND = 77 -IPPROTO_WBMON = 78 -IPPROTO_WBEXPAK = 79 -IPPROTO_EON = 80 -IPPROTO_VMTP = 81 -IPPROTO_SVMTP = 82 -IPPROTO_VINES = 83 -IPPROTO_TTP = 84 -IPPROTO_IGP = 85 -IPPROTO_DGP = 86 -IPPROTO_TCF = 87 -IPPROTO_IGRP = 88 -IPPROTO_OSPFIGP = 89 -IPPROTO_SRPC = 90 -IPPROTO_LARP = 91 -IPPROTO_MTP = 92 -IPPROTO_AX25 = 93 -IPPROTO_IPEIP = 94 -IPPROTO_MICP = 95 -IPPROTO_SCCSP = 96 -IPPROTO_ETHERIP = 97 -IPPROTO_ENCAP = 98 -IPPROTO_APES = 99 -IPPROTO_GMTP = 100 -IPPROTO_IPCOMP = 108 -IPPROTO_PIM = 103 -IPPROTO_PGM = 113 -IPPROTO_DIVERT = 254 -IPPROTO_RAW = 255 -IPPROTO_MAX = 256 -IPPROTO_DONE = 257 -IPPORT_RESERVED = 1024 -IPPORT_USERRESERVED = 5000 -IPPORT_HIFIRSTAUTO = 49152 -IPPORT_HILASTAUTO = 65535 -IPPORT_RESERVEDSTART = 600 -def IN_CLASSA(i): return (((u_int32_t)(i) & 0x80000000) == 0) - -IN_CLASSA_NET = 0xff000000 -IN_CLASSA_NSHIFT = 24 -IN_CLASSA_HOST = 0x00ffffff -IN_CLASSA_MAX = 128 -def IN_CLASSB(i): return (((u_int32_t)(i) & 0xc0000000) == 0x80000000) - -IN_CLASSB_NET = 0xffff0000 -IN_CLASSB_NSHIFT = 16 -IN_CLASSB_HOST = 0x0000ffff -IN_CLASSB_MAX = 65536 -def IN_CLASSC(i): return (((u_int32_t)(i) & 0xe0000000) == 0xc0000000) - -IN_CLASSC_NET = 0xffffff00 -IN_CLASSC_NSHIFT = 8 -IN_CLASSC_HOST = 0x000000ff -def IN_CLASSD(i): return (((u_int32_t)(i) & 0xf0000000) == 0xe0000000) - -IN_CLASSD_NET = 0xf0000000 -IN_CLASSD_NSHIFT = 28 -IN_CLASSD_HOST = 0x0fffffff -def IN_MULTICAST(i): return IN_CLASSD(i) - -def IN_EXPERIMENTAL(i): return (((u_int32_t)(i) & 0xf0000000) == 0xf0000000) - -def IN_BADCLASS(i): return (((u_int32_t)(i) & 0xf0000000) == 0xf0000000) - -INADDR_NONE = 0xffffffff -IN_LOOPBACKNET = 127 -INET_ADDRSTRLEN = 16 -IP_OPTIONS = 1 -IP_HDRINCL = 2 -IP_TOS = 3 -IP_TTL = 4 -IP_RECVOPTS = 5 -IP_RECVRETOPTS = 6 -IP_RECVDSTADDR = 7 -IP_RETOPTS = 8 -IP_MULTICAST_IF = 9 -IP_MULTICAST_TTL = 10 -IP_MULTICAST_LOOP = 11 -IP_ADD_MEMBERSHIP = 12 -IP_DROP_MEMBERSHIP = 13 -IP_MULTICAST_VIF = 14 -IP_RSVP_ON = 15 -IP_RSVP_OFF = 16 -IP_RSVP_VIF_ON = 17 -IP_RSVP_VIF_OFF = 18 -IP_PORTRANGE = 19 -IP_RECVIF = 20 -IP_IPSEC_POLICY = 21 -IP_FAITH = 22 -IP_FW_ADD = 50 -IP_FW_DEL = 51 -IP_FW_FLUSH = 52 -IP_FW_ZERO = 53 -IP_FW_GET = 54 -IP_FW_RESETLOG = 55 -IP_DUMMYNET_CONFIGURE = 60 -IP_DUMMYNET_DEL = 61 -IP_DUMMYNET_FLUSH = 62 -IP_DUMMYNET_GET = 64 -IP_DEFAULT_MULTICAST_TTL = 1 -IP_DEFAULT_MULTICAST_LOOP = 1 -IP_MAX_MEMBERSHIPS = 20 -IP_PORTRANGE_DEFAULT = 0 -IP_PORTRANGE_HIGH = 1 -IP_PORTRANGE_LOW = 2 -IPPROTO_MAXID = (IPPROTO_AH + 1) -IPCTL_FORWARDING = 1 -IPCTL_SENDREDIRECTS = 2 -IPCTL_DEFTTL = 3 -IPCTL_DEFMTU = 4 -IPCTL_RTEXPIRE = 5 -IPCTL_RTMINEXPIRE = 6 -IPCTL_RTMAXCACHE = 7 -IPCTL_SOURCEROUTE = 8 -IPCTL_DIRECTEDBROADCAST = 9 -IPCTL_INTRQMAXLEN = 10 -IPCTL_INTRQDROPS = 11 -IPCTL_STATS = 12 -IPCTL_ACCEPTSOURCEROUTE = 13 -IPCTL_FASTFORWARDING = 14 -IPCTL_KEEPFAITH = 15 -IPCTL_GIF_TTL = 16 -IPCTL_MAXID = 17 - -# Included from netinet6/in6.h - -# Included from sys/queue.h -def SLIST_HEAD_INITIALIZER(head): return \ - -def SLIST_ENTRY(type): return \ - -def STAILQ_HEAD_INITIALIZER(head): return \ - -def STAILQ_ENTRY(type): return \ - -def LIST_HEAD_INITIALIZER(head): return \ - -def LIST_ENTRY(type): return \ - -def TAILQ_HEAD_INITIALIZER(head): return \ - -def TAILQ_ENTRY(type): return \ - -def CIRCLEQ_ENTRY(type): return \ - -__KAME_VERSION = "20000701/FreeBSD-current" -IPV6PORT_RESERVED = 1024 -IPV6PORT_ANONMIN = 49152 -IPV6PORT_ANONMAX = 65535 -IPV6PORT_RESERVEDMIN = 600 -IPV6PORT_RESERVEDMAX = (IPV6PORT_RESERVED-1) -INET6_ADDRSTRLEN = 46 -IPV6_ADDR_INT32_ONE = 1 -IPV6_ADDR_INT32_TWO = 2 -IPV6_ADDR_INT32_MNL = 0xff010000 -IPV6_ADDR_INT32_MLL = 0xff020000 -IPV6_ADDR_INT32_SMP = 0x0000ffff -IPV6_ADDR_INT16_ULL = 0xfe80 -IPV6_ADDR_INT16_USL = 0xfec0 -IPV6_ADDR_INT16_MLL = 0xff02 -IPV6_ADDR_INT32_ONE = 0x01000000 -IPV6_ADDR_INT32_TWO = 0x02000000 -IPV6_ADDR_INT32_MNL = 0x000001ff -IPV6_ADDR_INT32_MLL = 0x000002ff -IPV6_ADDR_INT32_SMP = 0xffff0000 -IPV6_ADDR_INT16_ULL = 0x80fe -IPV6_ADDR_INT16_USL = 0xc0fe -IPV6_ADDR_INT16_MLL = 0x02ff -def IN6_IS_ADDR_UNSPECIFIED(a): return \ - -def IN6_IS_ADDR_LOOPBACK(a): return \ - -def IN6_IS_ADDR_V4COMPAT(a): return \ - -def IN6_IS_ADDR_V4MAPPED(a): return \ - -IPV6_ADDR_SCOPE_NODELOCAL = 0x01 -IPV6_ADDR_SCOPE_LINKLOCAL = 0x02 -IPV6_ADDR_SCOPE_SITELOCAL = 0x05 -IPV6_ADDR_SCOPE_ORGLOCAL = 0x08 -IPV6_ADDR_SCOPE_GLOBAL = 0x0e -__IPV6_ADDR_SCOPE_NODELOCAL = 0x01 -__IPV6_ADDR_SCOPE_LINKLOCAL = 0x02 -__IPV6_ADDR_SCOPE_SITELOCAL = 0x05 -__IPV6_ADDR_SCOPE_ORGLOCAL = 0x08 -__IPV6_ADDR_SCOPE_GLOBAL = 0x0e -def IN6_IS_ADDR_LINKLOCAL(a): return \ - -def IN6_IS_ADDR_SITELOCAL(a): return \ - -def IN6_IS_ADDR_MC_NODELOCAL(a): return \ - -def IN6_IS_ADDR_MC_LINKLOCAL(a): return \ - -def IN6_IS_ADDR_MC_SITELOCAL(a): return \ - -def IN6_IS_ADDR_MC_ORGLOCAL(a): return \ - -def IN6_IS_ADDR_MC_GLOBAL(a): return \ - -def IN6_IS_ADDR_MC_NODELOCAL(a): return \ - -def IN6_IS_ADDR_MC_LINKLOCAL(a): return \ - -def IN6_IS_ADDR_MC_SITELOCAL(a): return \ - -def IN6_IS_ADDR_MC_ORGLOCAL(a): return \ - -def IN6_IS_ADDR_MC_GLOBAL(a): return \ - -def IN6_IS_SCOPE_LINKLOCAL(a): return \ - -IPV6_OPTIONS = 1 -IPV6_RECVOPTS = 5 -IPV6_RECVRETOPTS = 6 -IPV6_RECVDSTADDR = 7 -IPV6_RETOPTS = 8 -IPV6_SOCKOPT_RESERVED1 = 3 -IPV6_UNICAST_HOPS = 4 -IPV6_MULTICAST_IF = 9 -IPV6_MULTICAST_HOPS = 10 -IPV6_MULTICAST_LOOP = 11 -IPV6_JOIN_GROUP = 12 -IPV6_LEAVE_GROUP = 13 -IPV6_PORTRANGE = 14 -ICMP6_FILTER = 18 -IPV6_PKTINFO = 19 -IPV6_HOPLIMIT = 20 -IPV6_NEXTHOP = 21 -IPV6_HOPOPTS = 22 -IPV6_DSTOPTS = 23 -IPV6_RTHDR = 24 -IPV6_PKTOPTIONS = 25 -IPV6_CHECKSUM = 26 -IPV6_BINDV6ONLY = 27 -IPV6_IPSEC_POLICY = 28 -IPV6_FAITH = 29 -IPV6_FW_ADD = 30 -IPV6_FW_DEL = 31 -IPV6_FW_FLUSH = 32 -IPV6_FW_ZERO = 33 -IPV6_FW_GET = 34 -IPV6_RTHDR_LOOSE = 0 -IPV6_RTHDR_STRICT = 1 -IPV6_RTHDR_TYPE_0 = 0 -IPV6_DEFAULT_MULTICAST_HOPS = 1 -IPV6_DEFAULT_MULTICAST_LOOP = 1 -IPV6_PORTRANGE_DEFAULT = 0 -IPV6_PORTRANGE_HIGH = 1 -IPV6_PORTRANGE_LOW = 2 -IPV6PROTO_MAXID = (IPPROTO_PIM + 1) -IPV6CTL_FORWARDING = 1 -IPV6CTL_SENDREDIRECTS = 2 -IPV6CTL_DEFHLIM = 3 -IPV6CTL_DEFMTU = 4 -IPV6CTL_FORWSRCRT = 5 -IPV6CTL_STATS = 6 -IPV6CTL_MRTSTATS = 7 -IPV6CTL_MRTPROTO = 8 -IPV6CTL_MAXFRAGPACKETS = 9 -IPV6CTL_SOURCECHECK = 10 -IPV6CTL_SOURCECHECK_LOGINT = 11 -IPV6CTL_ACCEPT_RTADV = 12 -IPV6CTL_KEEPFAITH = 13 -IPV6CTL_LOG_INTERVAL = 14 -IPV6CTL_HDRNESTLIMIT = 15 -IPV6CTL_DAD_COUNT = 16 -IPV6CTL_AUTO_FLOWLABEL = 17 -IPV6CTL_DEFMCASTHLIM = 18 -IPV6CTL_GIF_HLIM = 19 -IPV6CTL_KAME_VERSION = 20 -IPV6CTL_USE_DEPRECATED = 21 -IPV6CTL_RR_PRUNE = 22 -IPV6CTL_MAPPED_ADDR = 23 -IPV6CTL_BINDV6ONLY = 24 -IPV6CTL_RTEXPIRE = 25 -IPV6CTL_RTMINEXPIRE = 26 -IPV6CTL_RTMAXCACHE = 27 -IPV6CTL_MAXID = 28 diff --git a/Lib/plat-freebsd5/regen b/Lib/plat-freebsd5/regen deleted file mode 100644 index 8aa6898c6a..0000000000 --- a/Lib/plat-freebsd5/regen +++ /dev/null @@ -1,3 +0,0 @@ -#! /bin/sh -set -v -python ../../Tools/scripts/h2py.py -i '(u_long)' /usr/include/netinet/in.h diff --git a/Lib/plat-freebsd6/IN.py b/Lib/plat-freebsd6/IN.py deleted file mode 100644 index 560bf84877..0000000000 --- a/Lib/plat-freebsd6/IN.py +++ /dev/null @@ -1,551 +0,0 @@ -# Generated by h2py from /usr/include/netinet/in.h - -# Included from sys/cdefs.h -__GNUCLIKE_ASM = 3 -__GNUCLIKE_ASM = 2 -__GNUCLIKE___TYPEOF = 1 -__GNUCLIKE___OFFSETOF = 1 -__GNUCLIKE___SECTION = 1 -__GNUCLIKE_ATTRIBUTE_MODE_DI = 1 -__GNUCLIKE_CTOR_SECTION_HANDLING = 1 -__GNUCLIKE_BUILTIN_CONSTANT_P = 1 -__GNUCLIKE_BUILTIN_VARARGS = 1 -__GNUCLIKE_BUILTIN_STDARG = 1 -__GNUCLIKE_BUILTIN_VAALIST = 1 -__GNUC_VA_LIST_COMPATIBILITY = 1 -__GNUCLIKE_BUILTIN_NEXT_ARG = 1 -__GNUCLIKE_BUILTIN_MEMCPY = 1 -__CC_SUPPORTS_INLINE = 1 -__CC_SUPPORTS___INLINE = 1 -__CC_SUPPORTS___INLINE__ = 1 -__CC_SUPPORTS___FUNC__ = 1 -__CC_SUPPORTS_WARNING = 1 -__CC_SUPPORTS_VARADIC_XXX = 1 -__CC_SUPPORTS_DYNAMIC_ARRAY_INIT = 1 -__CC_INT_IS_32BIT = 1 -def __P(protos): return protos - -def __STRING(x): return #x - -def __XSTRING(x): return __STRING(x) - -def __P(protos): return () - -def __STRING(x): return "x" - -def __aligned(x): return __attribute__((__aligned__(x))) - -def __section(x): return __attribute__((__section__(x))) - -def __aligned(x): return __attribute__((__aligned__(x))) - -def __section(x): return __attribute__((__section__(x))) - -def __nonnull(x): return __attribute__((__nonnull__(x))) - -def __predict_true(exp): return __builtin_expect((exp), 1) - -def __predict_false(exp): return __builtin_expect((exp), 0) - -def __predict_true(exp): return (exp) - -def __predict_false(exp): return (exp) - -def __format_arg(fmtarg): return __attribute__((__format_arg__ (fmtarg))) - -def __FBSDID(s): return __IDSTRING(__CONCAT(__rcsid_,__LINE__),s) - -def __RCSID(s): return __IDSTRING(__CONCAT(__rcsid_,__LINE__),s) - -def __RCSID_SOURCE(s): return __IDSTRING(__CONCAT(__rcsid_source_,__LINE__),s) - -def __SCCSID(s): return __IDSTRING(__CONCAT(__sccsid_,__LINE__),s) - -def __COPYRIGHT(s): return __IDSTRING(__CONCAT(__copyright_,__LINE__),s) - -_POSIX_C_SOURCE = 199009 -_POSIX_C_SOURCE = 199209 -__XSI_VISIBLE = 600 -_POSIX_C_SOURCE = 200112 -__XSI_VISIBLE = 500 -_POSIX_C_SOURCE = 199506 -_POSIX_C_SOURCE = 198808 -__POSIX_VISIBLE = 200112 -__ISO_C_VISIBLE = 1999 -__POSIX_VISIBLE = 199506 -__ISO_C_VISIBLE = 1990 -__POSIX_VISIBLE = 199309 -__ISO_C_VISIBLE = 1990 -__POSIX_VISIBLE = 199209 -__ISO_C_VISIBLE = 1990 -__POSIX_VISIBLE = 199009 -__ISO_C_VISIBLE = 1990 -__POSIX_VISIBLE = 198808 -__ISO_C_VISIBLE = 0 -__POSIX_VISIBLE = 0 -__XSI_VISIBLE = 0 -__BSD_VISIBLE = 0 -__ISO_C_VISIBLE = 1990 -__POSIX_VISIBLE = 0 -__XSI_VISIBLE = 0 -__BSD_VISIBLE = 0 -__ISO_C_VISIBLE = 1999 -__POSIX_VISIBLE = 200112 -__XSI_VISIBLE = 600 -__BSD_VISIBLE = 1 -__ISO_C_VISIBLE = 1999 - -# Included from sys/_types.h - -# Included from machine/_types.h - -# Included from machine/endian.h -_QUAD_HIGHWORD = 1 -_QUAD_LOWWORD = 0 -_LITTLE_ENDIAN = 1234 -_BIG_ENDIAN = 4321 -_PDP_ENDIAN = 3412 -_BYTE_ORDER = _LITTLE_ENDIAN -LITTLE_ENDIAN = _LITTLE_ENDIAN -BIG_ENDIAN = _BIG_ENDIAN -PDP_ENDIAN = _PDP_ENDIAN -BYTE_ORDER = _BYTE_ORDER -def __word_swap_int_var(x): return \ - -def __word_swap_int_const(x): return \ - -def __word_swap_int(x): return __word_swap_int_var(x) - -def __byte_swap_int_var(x): return \ - -def __byte_swap_int_const(x): return \ - -def __byte_swap_int(x): return __byte_swap_int_var(x) - -def __byte_swap_long_var(x): return \ - -def __byte_swap_long_const(x): return \ - -def __byte_swap_long(x): return __byte_swap_long_var(x) - -def __byte_swap_word_var(x): return \ - -def __byte_swap_word_const(x): return \ - -def __byte_swap_word(x): return __byte_swap_word_var(x) - -def __htonl(x): return __bswap32(x) - -def __htons(x): return __bswap16(x) - -def __ntohl(x): return __bswap32(x) - -def __ntohs(x): return __bswap16(x) - -IPPROTO_IP = 0 -IPPROTO_ICMP = 1 -IPPROTO_TCP = 6 -IPPROTO_UDP = 17 -def htonl(x): return __htonl(x) - -def htons(x): return __htons(x) - -def ntohl(x): return __ntohl(x) - -def ntohs(x): return __ntohs(x) - -IPPROTO_RAW = 255 -INET_ADDRSTRLEN = 16 -IPPROTO_HOPOPTS = 0 -IPPROTO_IGMP = 2 -IPPROTO_GGP = 3 -IPPROTO_IPV4 = 4 -IPPROTO_IPIP = IPPROTO_IPV4 -IPPROTO_ST = 7 -IPPROTO_EGP = 8 -IPPROTO_PIGP = 9 -IPPROTO_RCCMON = 10 -IPPROTO_NVPII = 11 -IPPROTO_PUP = 12 -IPPROTO_ARGUS = 13 -IPPROTO_EMCON = 14 -IPPROTO_XNET = 15 -IPPROTO_CHAOS = 16 -IPPROTO_MUX = 18 -IPPROTO_MEAS = 19 -IPPROTO_HMP = 20 -IPPROTO_PRM = 21 -IPPROTO_IDP = 22 -IPPROTO_TRUNK1 = 23 -IPPROTO_TRUNK2 = 24 -IPPROTO_LEAF1 = 25 -IPPROTO_LEAF2 = 26 -IPPROTO_RDP = 27 -IPPROTO_IRTP = 28 -IPPROTO_TP = 29 -IPPROTO_BLT = 30 -IPPROTO_NSP = 31 -IPPROTO_INP = 32 -IPPROTO_SEP = 33 -IPPROTO_3PC = 34 -IPPROTO_IDPR = 35 -IPPROTO_XTP = 36 -IPPROTO_DDP = 37 -IPPROTO_CMTP = 38 -IPPROTO_TPXX = 39 -IPPROTO_IL = 40 -IPPROTO_IPV6 = 41 -IPPROTO_SDRP = 42 -IPPROTO_ROUTING = 43 -IPPROTO_FRAGMENT = 44 -IPPROTO_IDRP = 45 -IPPROTO_RSVP = 46 -IPPROTO_GRE = 47 -IPPROTO_MHRP = 48 -IPPROTO_BHA = 49 -IPPROTO_ESP = 50 -IPPROTO_AH = 51 -IPPROTO_INLSP = 52 -IPPROTO_SWIPE = 53 -IPPROTO_NHRP = 54 -IPPROTO_MOBILE = 55 -IPPROTO_TLSP = 56 -IPPROTO_SKIP = 57 -IPPROTO_ICMPV6 = 58 -IPPROTO_NONE = 59 -IPPROTO_DSTOPTS = 60 -IPPROTO_AHIP = 61 -IPPROTO_CFTP = 62 -IPPROTO_HELLO = 63 -IPPROTO_SATEXPAK = 64 -IPPROTO_KRYPTOLAN = 65 -IPPROTO_RVD = 66 -IPPROTO_IPPC = 67 -IPPROTO_ADFS = 68 -IPPROTO_SATMON = 69 -IPPROTO_VISA = 70 -IPPROTO_IPCV = 71 -IPPROTO_CPNX = 72 -IPPROTO_CPHB = 73 -IPPROTO_WSN = 74 -IPPROTO_PVP = 75 -IPPROTO_BRSATMON = 76 -IPPROTO_ND = 77 -IPPROTO_WBMON = 78 -IPPROTO_WBEXPAK = 79 -IPPROTO_EON = 80 -IPPROTO_VMTP = 81 -IPPROTO_SVMTP = 82 -IPPROTO_VINES = 83 -IPPROTO_TTP = 84 -IPPROTO_IGP = 85 -IPPROTO_DGP = 86 -IPPROTO_TCF = 87 -IPPROTO_IGRP = 88 -IPPROTO_OSPFIGP = 89 -IPPROTO_SRPC = 90 -IPPROTO_LARP = 91 -IPPROTO_MTP = 92 -IPPROTO_AX25 = 93 -IPPROTO_IPEIP = 94 -IPPROTO_MICP = 95 -IPPROTO_SCCSP = 96 -IPPROTO_ETHERIP = 97 -IPPROTO_ENCAP = 98 -IPPROTO_APES = 99 -IPPROTO_GMTP = 100 -IPPROTO_IPCOMP = 108 -IPPROTO_SCTP = 132 -IPPROTO_PIM = 103 -IPPROTO_CARP = 112 -IPPROTO_PGM = 113 -IPPROTO_PFSYNC = 240 -IPPROTO_OLD_DIVERT = 254 -IPPROTO_MAX = 256 -IPPROTO_DONE = 257 -IPPROTO_DIVERT = 258 -IPPROTO_SPACER = 32767 -IPPORT_RESERVED = 1024 -IPPORT_HIFIRSTAUTO = 49152 -IPPORT_HILASTAUTO = 65535 -IPPORT_RESERVEDSTART = 600 -IPPORT_MAX = 65535 -def IN_CLASSA(i): return (((u_int32_t)(i) & 0x80000000) == 0) - -IN_CLASSA_NET = 0xff000000 -IN_CLASSA_NSHIFT = 24 -IN_CLASSA_HOST = 0x00ffffff -IN_CLASSA_MAX = 128 -def IN_CLASSB(i): return (((u_int32_t)(i) & 0xc0000000) == 0x80000000) - -IN_CLASSB_NET = 0xffff0000 -IN_CLASSB_NSHIFT = 16 -IN_CLASSB_HOST = 0x0000ffff -IN_CLASSB_MAX = 65536 -def IN_CLASSC(i): return (((u_int32_t)(i) & 0xe0000000) == 0xc0000000) - -IN_CLASSC_NET = 0xffffff00 -IN_CLASSC_NSHIFT = 8 -IN_CLASSC_HOST = 0x000000ff -def IN_CLASSD(i): return (((u_int32_t)(i) & 0xf0000000) == 0xe0000000) - -IN_CLASSD_NET = 0xf0000000 -IN_CLASSD_NSHIFT = 28 -IN_CLASSD_HOST = 0x0fffffff -def IN_MULTICAST(i): return IN_CLASSD(i) - -def IN_EXPERIMENTAL(i): return (((u_int32_t)(i) & 0xf0000000) == 0xf0000000) - -def IN_BADCLASS(i): return (((u_int32_t)(i) & 0xf0000000) == 0xf0000000) - -INADDR_NONE = 0xffffffff -IN_LOOPBACKNET = 127 -IP_OPTIONS = 1 -IP_HDRINCL = 2 -IP_TOS = 3 -IP_TTL = 4 -IP_RECVOPTS = 5 -IP_RECVRETOPTS = 6 -IP_RECVDSTADDR = 7 -IP_SENDSRCADDR = IP_RECVDSTADDR -IP_RETOPTS = 8 -IP_MULTICAST_IF = 9 -IP_MULTICAST_TTL = 10 -IP_MULTICAST_LOOP = 11 -IP_ADD_MEMBERSHIP = 12 -IP_DROP_MEMBERSHIP = 13 -IP_MULTICAST_VIF = 14 -IP_RSVP_ON = 15 -IP_RSVP_OFF = 16 -IP_RSVP_VIF_ON = 17 -IP_RSVP_VIF_OFF = 18 -IP_PORTRANGE = 19 -IP_RECVIF = 20 -IP_IPSEC_POLICY = 21 -IP_FAITH = 22 -IP_ONESBCAST = 23 -IP_FW_TABLE_ADD = 40 -IP_FW_TABLE_DEL = 41 -IP_FW_TABLE_FLUSH = 42 -IP_FW_TABLE_GETSIZE = 43 -IP_FW_TABLE_LIST = 44 -IP_FW_ADD = 50 -IP_FW_DEL = 51 -IP_FW_FLUSH = 52 -IP_FW_ZERO = 53 -IP_FW_GET = 54 -IP_FW_RESETLOG = 55 -IP_DUMMYNET_CONFIGURE = 60 -IP_DUMMYNET_DEL = 61 -IP_DUMMYNET_FLUSH = 62 -IP_DUMMYNET_GET = 64 -IP_RECVTTL = 65 -IP_MINTTL = 66 -IP_DONTFRAG = 67 -IP_DEFAULT_MULTICAST_TTL = 1 -IP_DEFAULT_MULTICAST_LOOP = 1 -IP_MAX_MEMBERSHIPS = 20 -IP_PORTRANGE_DEFAULT = 0 -IP_PORTRANGE_HIGH = 1 -IP_PORTRANGE_LOW = 2 -IPPROTO_MAXID = (IPPROTO_AH + 1) -IPCTL_FORWARDING = 1 -IPCTL_SENDREDIRECTS = 2 -IPCTL_DEFTTL = 3 -IPCTL_DEFMTU = 4 -IPCTL_RTEXPIRE = 5 -IPCTL_RTMINEXPIRE = 6 -IPCTL_RTMAXCACHE = 7 -IPCTL_SOURCEROUTE = 8 -IPCTL_DIRECTEDBROADCAST = 9 -IPCTL_INTRQMAXLEN = 10 -IPCTL_INTRQDROPS = 11 -IPCTL_STATS = 12 -IPCTL_ACCEPTSOURCEROUTE = 13 -IPCTL_FASTFORWARDING = 14 -IPCTL_KEEPFAITH = 15 -IPCTL_GIF_TTL = 16 -IPCTL_MAXID = 17 -def in_nullhost(x): return ((x).s_addr == INADDR_ANY) - - -# Included from netinet6/in6.h -__KAME_VERSION = "FreeBSD" -IPV6PORT_RESERVED = 1024 -IPV6PORT_ANONMIN = 49152 -IPV6PORT_ANONMAX = 65535 -IPV6PORT_RESERVEDMIN = 600 -IPV6PORT_RESERVEDMAX = (IPV6PORT_RESERVED-1) -INET6_ADDRSTRLEN = 46 -IPV6_ADDR_INT32_ONE = 1 -IPV6_ADDR_INT32_TWO = 2 -IPV6_ADDR_INT32_MNL = 0xff010000 -IPV6_ADDR_INT32_MLL = 0xff020000 -IPV6_ADDR_INT32_SMP = 0x0000ffff -IPV6_ADDR_INT16_ULL = 0xfe80 -IPV6_ADDR_INT16_USL = 0xfec0 -IPV6_ADDR_INT16_MLL = 0xff02 -IPV6_ADDR_INT32_ONE = 0x01000000 -IPV6_ADDR_INT32_TWO = 0x02000000 -IPV6_ADDR_INT32_MNL = 0x000001ff -IPV6_ADDR_INT32_MLL = 0x000002ff -IPV6_ADDR_INT32_SMP = 0xffff0000 -IPV6_ADDR_INT16_ULL = 0x80fe -IPV6_ADDR_INT16_USL = 0xc0fe -IPV6_ADDR_INT16_MLL = 0x02ff -def IN6_IS_ADDR_UNSPECIFIED(a): return \ - -def IN6_IS_ADDR_LOOPBACK(a): return \ - -def IN6_IS_ADDR_V4COMPAT(a): return \ - -def IN6_IS_ADDR_V4MAPPED(a): return \ - -IPV6_ADDR_SCOPE_NODELOCAL = 0x01 -IPV6_ADDR_SCOPE_INTFACELOCAL = 0x01 -IPV6_ADDR_SCOPE_LINKLOCAL = 0x02 -IPV6_ADDR_SCOPE_SITELOCAL = 0x05 -IPV6_ADDR_SCOPE_ORGLOCAL = 0x08 -IPV6_ADDR_SCOPE_GLOBAL = 0x0e -__IPV6_ADDR_SCOPE_NODELOCAL = 0x01 -__IPV6_ADDR_SCOPE_INTFACELOCAL = 0x01 -__IPV6_ADDR_SCOPE_LINKLOCAL = 0x02 -__IPV6_ADDR_SCOPE_SITELOCAL = 0x05 -__IPV6_ADDR_SCOPE_ORGLOCAL = 0x08 -__IPV6_ADDR_SCOPE_GLOBAL = 0x0e -def IN6_IS_ADDR_LINKLOCAL(a): return \ - -def IN6_IS_ADDR_SITELOCAL(a): return \ - -def IN6_IS_ADDR_MC_NODELOCAL(a): return \ - -def IN6_IS_ADDR_MC_INTFACELOCAL(a): return \ - -def IN6_IS_ADDR_MC_LINKLOCAL(a): return \ - -def IN6_IS_ADDR_MC_SITELOCAL(a): return \ - -def IN6_IS_ADDR_MC_ORGLOCAL(a): return \ - -def IN6_IS_ADDR_MC_GLOBAL(a): return \ - -def IN6_IS_ADDR_MC_NODELOCAL(a): return \ - -def IN6_IS_ADDR_MC_LINKLOCAL(a): return \ - -def IN6_IS_ADDR_MC_SITELOCAL(a): return \ - -def IN6_IS_ADDR_MC_ORGLOCAL(a): return \ - -def IN6_IS_ADDR_MC_GLOBAL(a): return \ - -def IN6_IS_SCOPE_LINKLOCAL(a): return \ - -def IFA6_IS_DEPRECATED(a): return \ - -def IFA6_IS_INVALID(a): return \ - -IPV6_OPTIONS = 1 -IPV6_RECVOPTS = 5 -IPV6_RECVRETOPTS = 6 -IPV6_RECVDSTADDR = 7 -IPV6_RETOPTS = 8 -IPV6_SOCKOPT_RESERVED1 = 3 -IPV6_UNICAST_HOPS = 4 -IPV6_MULTICAST_IF = 9 -IPV6_MULTICAST_HOPS = 10 -IPV6_MULTICAST_LOOP = 11 -IPV6_JOIN_GROUP = 12 -IPV6_LEAVE_GROUP = 13 -IPV6_PORTRANGE = 14 -ICMP6_FILTER = 18 -IPV6_2292PKTINFO = 19 -IPV6_2292HOPLIMIT = 20 -IPV6_2292NEXTHOP = 21 -IPV6_2292HOPOPTS = 22 -IPV6_2292DSTOPTS = 23 -IPV6_2292RTHDR = 24 -IPV6_2292PKTOPTIONS = 25 -IPV6_CHECKSUM = 26 -IPV6_V6ONLY = 27 -IPV6_BINDV6ONLY = IPV6_V6ONLY -IPV6_IPSEC_POLICY = 28 -IPV6_FAITH = 29 -IPV6_FW_ADD = 30 -IPV6_FW_DEL = 31 -IPV6_FW_FLUSH = 32 -IPV6_FW_ZERO = 33 -IPV6_FW_GET = 34 -IPV6_RTHDRDSTOPTS = 35 -IPV6_RECVPKTINFO = 36 -IPV6_RECVHOPLIMIT = 37 -IPV6_RECVRTHDR = 38 -IPV6_RECVHOPOPTS = 39 -IPV6_RECVDSTOPTS = 40 -IPV6_RECVRTHDRDSTOPTS = 41 -IPV6_USE_MIN_MTU = 42 -IPV6_RECVPATHMTU = 43 -IPV6_PATHMTU = 44 -IPV6_REACHCONF = 45 -IPV6_PKTINFO = 46 -IPV6_HOPLIMIT = 47 -IPV6_NEXTHOP = 48 -IPV6_HOPOPTS = 49 -IPV6_DSTOPTS = 50 -IPV6_RTHDR = 51 -IPV6_PKTOPTIONS = 52 -IPV6_RECVTCLASS = 57 -IPV6_AUTOFLOWLABEL = 59 -IPV6_TCLASS = 61 -IPV6_DONTFRAG = 62 -IPV6_PREFER_TEMPADDR = 63 -IPV6_RTHDR_LOOSE = 0 -IPV6_RTHDR_STRICT = 1 -IPV6_RTHDR_TYPE_0 = 0 -IPV6_DEFAULT_MULTICAST_HOPS = 1 -IPV6_DEFAULT_MULTICAST_LOOP = 1 -IPV6_PORTRANGE_DEFAULT = 0 -IPV6_PORTRANGE_HIGH = 1 -IPV6_PORTRANGE_LOW = 2 -IPV6PROTO_MAXID = (IPPROTO_PIM + 1) -IPV6CTL_FORWARDING = 1 -IPV6CTL_SENDREDIRECTS = 2 -IPV6CTL_DEFHLIM = 3 -IPV6CTL_DEFMTU = 4 -IPV6CTL_FORWSRCRT = 5 -IPV6CTL_STATS = 6 -IPV6CTL_MRTSTATS = 7 -IPV6CTL_MRTPROTO = 8 -IPV6CTL_MAXFRAGPACKETS = 9 -IPV6CTL_SOURCECHECK = 10 -IPV6CTL_SOURCECHECK_LOGINT = 11 -IPV6CTL_ACCEPT_RTADV = 12 -IPV6CTL_KEEPFAITH = 13 -IPV6CTL_LOG_INTERVAL = 14 -IPV6CTL_HDRNESTLIMIT = 15 -IPV6CTL_DAD_COUNT = 16 -IPV6CTL_AUTO_FLOWLABEL = 17 -IPV6CTL_DEFMCASTHLIM = 18 -IPV6CTL_GIF_HLIM = 19 -IPV6CTL_KAME_VERSION = 20 -IPV6CTL_USE_DEPRECATED = 21 -IPV6CTL_RR_PRUNE = 22 -IPV6CTL_MAPPED_ADDR = 23 -IPV6CTL_V6ONLY = 24 -IPV6CTL_RTEXPIRE = 25 -IPV6CTL_RTMINEXPIRE = 26 -IPV6CTL_RTMAXCACHE = 27 -IPV6CTL_USETEMPADDR = 32 -IPV6CTL_TEMPPLTIME = 33 -IPV6CTL_TEMPVLTIME = 34 -IPV6CTL_AUTO_LINKLOCAL = 35 -IPV6CTL_RIP6STATS = 36 -IPV6CTL_PREFER_TEMPADDR = 37 -IPV6CTL_ADDRCTLPOLICY = 38 -IPV6CTL_USE_DEFAULTZONE = 39 -IPV6CTL_MAXFRAGS = 41 -IPV6CTL_IFQ = 42 -IPV6CTL_ISATAPRTR = 43 -IPV6CTL_MCAST_PMTU = 44 -IPV6CTL_STEALTH = 45 -IPV6CTL_MAXID = 46 diff --git a/Lib/plat-freebsd6/regen b/Lib/plat-freebsd6/regen deleted file mode 100644 index 8aa6898c6a..0000000000 --- a/Lib/plat-freebsd6/regen +++ /dev/null @@ -1,3 +0,0 @@ -#! /bin/sh -set -v -python ../../Tools/scripts/h2py.py -i '(u_long)' /usr/include/netinet/in.h diff --git a/Lib/plat-freebsd7/IN.py b/Lib/plat-freebsd7/IN.py deleted file mode 100644 index 4e3b3a23dd..0000000000 --- a/Lib/plat-freebsd7/IN.py +++ /dev/null @@ -1,571 +0,0 @@ -# Generated by h2py from /usr/include/netinet/in.h - -# Included from sys/cdefs.h -__GNUCLIKE_ASM = 3 -__GNUCLIKE_ASM = 2 -__GNUCLIKE___TYPEOF = 1 -__GNUCLIKE___OFFSETOF = 1 -__GNUCLIKE___SECTION = 1 -__GNUCLIKE_ATTRIBUTE_MODE_DI = 1 -__GNUCLIKE_CTOR_SECTION_HANDLING = 1 -__GNUCLIKE_BUILTIN_CONSTANT_P = 1 -__GNUCLIKE_BUILTIN_VARARGS = 1 -__GNUCLIKE_BUILTIN_STDARG = 1 -__GNUCLIKE_BUILTIN_VAALIST = 1 -__GNUC_VA_LIST_COMPATIBILITY = 1 -__GNUCLIKE_BUILTIN_NEXT_ARG = 1 -__GNUCLIKE_BUILTIN_MEMCPY = 1 -__CC_SUPPORTS_INLINE = 1 -__CC_SUPPORTS___INLINE = 1 -__CC_SUPPORTS___INLINE__ = 1 -__CC_SUPPORTS___FUNC__ = 1 -__CC_SUPPORTS_WARNING = 1 -__CC_SUPPORTS_VARADIC_XXX = 1 -__CC_SUPPORTS_DYNAMIC_ARRAY_INIT = 1 -__CC_INT_IS_32BIT = 1 -def __P(protos): return protos - -def __STRING(x): return #x - -def __XSTRING(x): return __STRING(x) - -def __P(protos): return () - -def __STRING(x): return "x" - -def __aligned(x): return __attribute__((__aligned__(x))) - -def __section(x): return __attribute__((__section__(x))) - -def __aligned(x): return __attribute__((__aligned__(x))) - -def __section(x): return __attribute__((__section__(x))) - -def __nonnull(x): return __attribute__((__nonnull__(x))) - -def __predict_true(exp): return __builtin_expect((exp), 1) - -def __predict_false(exp): return __builtin_expect((exp), 0) - -def __predict_true(exp): return (exp) - -def __predict_false(exp): return (exp) - -def __format_arg(fmtarg): return __attribute__((__format_arg__ (fmtarg))) - -def __FBSDID(s): return __IDSTRING(__CONCAT(__rcsid_,__LINE__),s) - -def __RCSID(s): return __IDSTRING(__CONCAT(__rcsid_,__LINE__),s) - -def __RCSID_SOURCE(s): return __IDSTRING(__CONCAT(__rcsid_source_,__LINE__),s) - -def __SCCSID(s): return __IDSTRING(__CONCAT(__sccsid_,__LINE__),s) - -def __COPYRIGHT(s): return __IDSTRING(__CONCAT(__copyright_,__LINE__),s) - -_POSIX_C_SOURCE = 199009 -_POSIX_C_SOURCE = 199209 -__XSI_VISIBLE = 600 -_POSIX_C_SOURCE = 200112 -__XSI_VISIBLE = 500 -_POSIX_C_SOURCE = 199506 -_POSIX_C_SOURCE = 198808 -__POSIX_VISIBLE = 200112 -__ISO_C_VISIBLE = 1999 -__POSIX_VISIBLE = 199506 -__ISO_C_VISIBLE = 1990 -__POSIX_VISIBLE = 199309 -__ISO_C_VISIBLE = 1990 -__POSIX_VISIBLE = 199209 -__ISO_C_VISIBLE = 1990 -__POSIX_VISIBLE = 199009 -__ISO_C_VISIBLE = 1990 -__POSIX_VISIBLE = 198808 -__ISO_C_VISIBLE = 0 -__POSIX_VISIBLE = 0 -__XSI_VISIBLE = 0 -__BSD_VISIBLE = 0 -__ISO_C_VISIBLE = 1990 -__POSIX_VISIBLE = 0 -__XSI_VISIBLE = 0 -__BSD_VISIBLE = 0 -__ISO_C_VISIBLE = 1999 -__POSIX_VISIBLE = 200112 -__XSI_VISIBLE = 600 -__BSD_VISIBLE = 1 -__ISO_C_VISIBLE = 1999 - -# Included from sys/_types.h - -# Included from machine/_types.h - -# Included from machine/endian.h -_QUAD_HIGHWORD = 1 -_QUAD_LOWWORD = 0 -_LITTLE_ENDIAN = 1234 -_BIG_ENDIAN = 4321 -_PDP_ENDIAN = 3412 -_BYTE_ORDER = _LITTLE_ENDIAN -LITTLE_ENDIAN = _LITTLE_ENDIAN -BIG_ENDIAN = _BIG_ENDIAN -PDP_ENDIAN = _PDP_ENDIAN -BYTE_ORDER = _BYTE_ORDER -def __word_swap_int_var(x): return \ - -def __word_swap_int_const(x): return \ - -def __word_swap_int(x): return __word_swap_int_var(x) - -def __byte_swap_int_var(x): return \ - -def __byte_swap_int_const(x): return \ - -def __byte_swap_int(x): return __byte_swap_int_var(x) - -def __byte_swap_word_var(x): return \ - -def __byte_swap_word_const(x): return \ - -def __byte_swap_word(x): return __byte_swap_word_var(x) - -def __htonl(x): return __bswap32(x) - -def __htons(x): return __bswap16(x) - -def __ntohl(x): return __bswap32(x) - -def __ntohs(x): return __bswap16(x) - -IPPROTO_IP = 0 -IPPROTO_ICMP = 1 -IPPROTO_TCP = 6 -IPPROTO_UDP = 17 -def htonl(x): return __htonl(x) - -def htons(x): return __htons(x) - -def ntohl(x): return __ntohl(x) - -def ntohs(x): return __ntohs(x) - -IPPROTO_RAW = 255 -INET_ADDRSTRLEN = 16 -IPPROTO_HOPOPTS = 0 -IPPROTO_IGMP = 2 -IPPROTO_GGP = 3 -IPPROTO_IPV4 = 4 -IPPROTO_IPIP = IPPROTO_IPV4 -IPPROTO_ST = 7 -IPPROTO_EGP = 8 -IPPROTO_PIGP = 9 -IPPROTO_RCCMON = 10 -IPPROTO_NVPII = 11 -IPPROTO_PUP = 12 -IPPROTO_ARGUS = 13 -IPPROTO_EMCON = 14 -IPPROTO_XNET = 15 -IPPROTO_CHAOS = 16 -IPPROTO_MUX = 18 -IPPROTO_MEAS = 19 -IPPROTO_HMP = 20 -IPPROTO_PRM = 21 -IPPROTO_IDP = 22 -IPPROTO_TRUNK1 = 23 -IPPROTO_TRUNK2 = 24 -IPPROTO_LEAF1 = 25 -IPPROTO_LEAF2 = 26 -IPPROTO_RDP = 27 -IPPROTO_IRTP = 28 -IPPROTO_TP = 29 -IPPROTO_BLT = 30 -IPPROTO_NSP = 31 -IPPROTO_INP = 32 -IPPROTO_SEP = 33 -IPPROTO_3PC = 34 -IPPROTO_IDPR = 35 -IPPROTO_XTP = 36 -IPPROTO_DDP = 37 -IPPROTO_CMTP = 38 -IPPROTO_TPXX = 39 -IPPROTO_IL = 40 -IPPROTO_IPV6 = 41 -IPPROTO_SDRP = 42 -IPPROTO_ROUTING = 43 -IPPROTO_FRAGMENT = 44 -IPPROTO_IDRP = 45 -IPPROTO_RSVP = 46 -IPPROTO_GRE = 47 -IPPROTO_MHRP = 48 -IPPROTO_BHA = 49 -IPPROTO_ESP = 50 -IPPROTO_AH = 51 -IPPROTO_INLSP = 52 -IPPROTO_SWIPE = 53 -IPPROTO_NHRP = 54 -IPPROTO_MOBILE = 55 -IPPROTO_TLSP = 56 -IPPROTO_SKIP = 57 -IPPROTO_ICMPV6 = 58 -IPPROTO_NONE = 59 -IPPROTO_DSTOPTS = 60 -IPPROTO_AHIP = 61 -IPPROTO_CFTP = 62 -IPPROTO_HELLO = 63 -IPPROTO_SATEXPAK = 64 -IPPROTO_KRYPTOLAN = 65 -IPPROTO_RVD = 66 -IPPROTO_IPPC = 67 -IPPROTO_ADFS = 68 -IPPROTO_SATMON = 69 -IPPROTO_VISA = 70 -IPPROTO_IPCV = 71 -IPPROTO_CPNX = 72 -IPPROTO_CPHB = 73 -IPPROTO_WSN = 74 -IPPROTO_PVP = 75 -IPPROTO_BRSATMON = 76 -IPPROTO_ND = 77 -IPPROTO_WBMON = 78 -IPPROTO_WBEXPAK = 79 -IPPROTO_EON = 80 -IPPROTO_VMTP = 81 -IPPROTO_SVMTP = 82 -IPPROTO_VINES = 83 -IPPROTO_TTP = 84 -IPPROTO_IGP = 85 -IPPROTO_DGP = 86 -IPPROTO_TCF = 87 -IPPROTO_IGRP = 88 -IPPROTO_OSPFIGP = 89 -IPPROTO_SRPC = 90 -IPPROTO_LARP = 91 -IPPROTO_MTP = 92 -IPPROTO_AX25 = 93 -IPPROTO_IPEIP = 94 -IPPROTO_MICP = 95 -IPPROTO_SCCSP = 96 -IPPROTO_ETHERIP = 97 -IPPROTO_ENCAP = 98 -IPPROTO_APES = 99 -IPPROTO_GMTP = 100 -IPPROTO_IPCOMP = 108 -IPPROTO_SCTP = 132 -IPPROTO_PIM = 103 -IPPROTO_CARP = 112 -IPPROTO_PGM = 113 -IPPROTO_PFSYNC = 240 -IPPROTO_OLD_DIVERT = 254 -IPPROTO_MAX = 256 -IPPROTO_DONE = 257 -IPPROTO_DIVERT = 258 -IPPROTO_SPACER = 32767 -IPPORT_RESERVED = 1024 -IPPORT_HIFIRSTAUTO = 49152 -IPPORT_HILASTAUTO = 65535 -IPPORT_RESERVEDSTART = 600 -IPPORT_MAX = 65535 -def IN_CLASSA(i): return (((u_int32_t)(i) & (-2147483648)) == 0) - -IN_CLASSA_NET = (-16777216) -IN_CLASSA_NSHIFT = 24 -IN_CLASSA_HOST = 0x00ffffff -IN_CLASSA_MAX = 128 -def IN_CLASSB(i): return (((u_int32_t)(i) & (-1073741824)) == (-2147483648)) - -IN_CLASSB_NET = (-65536) -IN_CLASSB_NSHIFT = 16 -IN_CLASSB_HOST = 0x0000ffff -IN_CLASSB_MAX = 65536 -def IN_CLASSC(i): return (((u_int32_t)(i) & (-536870912)) == (-1073741824)) - -IN_CLASSC_NET = (-256) -IN_CLASSC_NSHIFT = 8 -IN_CLASSC_HOST = 0x000000ff -def IN_CLASSD(i): return (((u_int32_t)(i) & (-268435456)) == (-536870912)) - -IN_CLASSD_NET = (-268435456) -IN_CLASSD_NSHIFT = 28 -IN_CLASSD_HOST = 0x0fffffff -def IN_MULTICAST(i): return IN_CLASSD(i) - -def IN_EXPERIMENTAL(i): return (((u_int32_t)(i) & (-268435456)) == (-268435456)) - -def IN_BADCLASS(i): return (((u_int32_t)(i) & (-268435456)) == (-268435456)) - -def IN_LINKLOCAL(i): return (((u_int32_t)(i) & (-65536)) == (-1442971648)) - -def IN_LOCAL_GROUP(i): return (((u_int32_t)(i) & (-256)) == (-536870912)) - -INADDR_NONE = (-1) -IN_LOOPBACKNET = 127 -IP_OPTIONS = 1 -IP_HDRINCL = 2 -IP_TOS = 3 -IP_TTL = 4 -IP_RECVOPTS = 5 -IP_RECVRETOPTS = 6 -IP_RECVDSTADDR = 7 -IP_SENDSRCADDR = IP_RECVDSTADDR -IP_RETOPTS = 8 -IP_MULTICAST_IF = 9 -IP_MULTICAST_TTL = 10 -IP_MULTICAST_LOOP = 11 -IP_ADD_MEMBERSHIP = 12 -IP_DROP_MEMBERSHIP = 13 -IP_MULTICAST_VIF = 14 -IP_RSVP_ON = 15 -IP_RSVP_OFF = 16 -IP_RSVP_VIF_ON = 17 -IP_RSVP_VIF_OFF = 18 -IP_PORTRANGE = 19 -IP_RECVIF = 20 -IP_IPSEC_POLICY = 21 -IP_FAITH = 22 -IP_ONESBCAST = 23 -IP_FW_TABLE_ADD = 40 -IP_FW_TABLE_DEL = 41 -IP_FW_TABLE_FLUSH = 42 -IP_FW_TABLE_GETSIZE = 43 -IP_FW_TABLE_LIST = 44 -IP_FW_ADD = 50 -IP_FW_DEL = 51 -IP_FW_FLUSH = 52 -IP_FW_ZERO = 53 -IP_FW_GET = 54 -IP_FW_RESETLOG = 55 -IP_FW_NAT_CFG = 56 -IP_FW_NAT_DEL = 57 -IP_FW_NAT_GET_CONFIG = 58 -IP_FW_NAT_GET_LOG = 59 -IP_DUMMYNET_CONFIGURE = 60 -IP_DUMMYNET_DEL = 61 -IP_DUMMYNET_FLUSH = 62 -IP_DUMMYNET_GET = 64 -IP_RECVTTL = 65 -IP_MINTTL = 66 -IP_DONTFRAG = 67 -IP_ADD_SOURCE_MEMBERSHIP = 70 -IP_DROP_SOURCE_MEMBERSHIP = 71 -IP_BLOCK_SOURCE = 72 -IP_UNBLOCK_SOURCE = 73 -IP_MSFILTER = 74 -MCAST_JOIN_GROUP = 80 -MCAST_LEAVE_GROUP = 81 -MCAST_JOIN_SOURCE_GROUP = 82 -MCAST_LEAVE_SOURCE_GROUP = 83 -MCAST_BLOCK_SOURCE = 84 -MCAST_UNBLOCK_SOURCE = 85 -IP_DEFAULT_MULTICAST_TTL = 1 -IP_DEFAULT_MULTICAST_LOOP = 1 -IP_MIN_MEMBERSHIPS = 31 -IP_MAX_MEMBERSHIPS = 4095 -IP_MAX_SOURCE_FILTER = 1024 -MCAST_INCLUDE = 1 -MCAST_EXCLUDE = 2 -IP_PORTRANGE_DEFAULT = 0 -IP_PORTRANGE_HIGH = 1 -IP_PORTRANGE_LOW = 2 -IPPROTO_MAXID = (IPPROTO_AH + 1) -IPCTL_FORWARDING = 1 -IPCTL_SENDREDIRECTS = 2 -IPCTL_DEFTTL = 3 -IPCTL_DEFMTU = 4 -IPCTL_RTEXPIRE = 5 -IPCTL_RTMINEXPIRE = 6 -IPCTL_RTMAXCACHE = 7 -IPCTL_SOURCEROUTE = 8 -IPCTL_DIRECTEDBROADCAST = 9 -IPCTL_INTRQMAXLEN = 10 -IPCTL_INTRQDROPS = 11 -IPCTL_STATS = 12 -IPCTL_ACCEPTSOURCEROUTE = 13 -IPCTL_FASTFORWARDING = 14 -IPCTL_KEEPFAITH = 15 -IPCTL_GIF_TTL = 16 -IPCTL_MAXID = 17 -def in_nullhost(x): return ((x).s_addr == INADDR_ANY) - - -# Included from netinet6/in6.h -__KAME_VERSION = "FreeBSD" -IPV6PORT_RESERVED = 1024 -IPV6PORT_ANONMIN = 49152 -IPV6PORT_ANONMAX = 65535 -IPV6PORT_RESERVEDMIN = 600 -IPV6PORT_RESERVEDMAX = (IPV6PORT_RESERVED-1) -INET6_ADDRSTRLEN = 46 -IPV6_ADDR_INT32_ONE = 1 -IPV6_ADDR_INT32_TWO = 2 -IPV6_ADDR_INT32_MNL = (-16711680) -IPV6_ADDR_INT32_MLL = (-16646144) -IPV6_ADDR_INT32_SMP = 0x0000ffff -IPV6_ADDR_INT16_ULL = 0xfe80 -IPV6_ADDR_INT16_USL = 0xfec0 -IPV6_ADDR_INT16_MLL = 0xff02 -IPV6_ADDR_INT32_ONE = 0x01000000 -IPV6_ADDR_INT32_TWO = 0x02000000 -IPV6_ADDR_INT32_MNL = 0x000001ff -IPV6_ADDR_INT32_MLL = 0x000002ff -IPV6_ADDR_INT32_SMP = (-65536) -IPV6_ADDR_INT16_ULL = 0x80fe -IPV6_ADDR_INT16_USL = 0xc0fe -IPV6_ADDR_INT16_MLL = 0x02ff -def IN6_IS_ADDR_UNSPECIFIED(a): return \ - -def IN6_IS_ADDR_LOOPBACK(a): return \ - -def IN6_IS_ADDR_V4COMPAT(a): return \ - -def IN6_IS_ADDR_V4MAPPED(a): return \ - -IPV6_ADDR_SCOPE_NODELOCAL = 0x01 -IPV6_ADDR_SCOPE_INTFACELOCAL = 0x01 -IPV6_ADDR_SCOPE_LINKLOCAL = 0x02 -IPV6_ADDR_SCOPE_SITELOCAL = 0x05 -IPV6_ADDR_SCOPE_ORGLOCAL = 0x08 -IPV6_ADDR_SCOPE_GLOBAL = 0x0e -__IPV6_ADDR_SCOPE_NODELOCAL = 0x01 -__IPV6_ADDR_SCOPE_INTFACELOCAL = 0x01 -__IPV6_ADDR_SCOPE_LINKLOCAL = 0x02 -__IPV6_ADDR_SCOPE_SITELOCAL = 0x05 -__IPV6_ADDR_SCOPE_ORGLOCAL = 0x08 -__IPV6_ADDR_SCOPE_GLOBAL = 0x0e -def IN6_IS_ADDR_LINKLOCAL(a): return \ - -def IN6_IS_ADDR_SITELOCAL(a): return \ - -def IN6_IS_ADDR_MC_NODELOCAL(a): return \ - -def IN6_IS_ADDR_MC_INTFACELOCAL(a): return \ - -def IN6_IS_ADDR_MC_LINKLOCAL(a): return \ - -def IN6_IS_ADDR_MC_SITELOCAL(a): return \ - -def IN6_IS_ADDR_MC_ORGLOCAL(a): return \ - -def IN6_IS_ADDR_MC_GLOBAL(a): return \ - -def IN6_IS_ADDR_MC_NODELOCAL(a): return \ - -def IN6_IS_ADDR_MC_LINKLOCAL(a): return \ - -def IN6_IS_ADDR_MC_SITELOCAL(a): return \ - -def IN6_IS_ADDR_MC_ORGLOCAL(a): return \ - -def IN6_IS_ADDR_MC_GLOBAL(a): return \ - -def IN6_IS_SCOPE_LINKLOCAL(a): return \ - -def IN6_IS_SCOPE_EMBED(a): return \ - -def IFA6_IS_DEPRECATED(a): return \ - -def IFA6_IS_INVALID(a): return \ - -IPV6_OPTIONS = 1 -IPV6_RECVOPTS = 5 -IPV6_RECVRETOPTS = 6 -IPV6_RECVDSTADDR = 7 -IPV6_RETOPTS = 8 -IPV6_SOCKOPT_RESERVED1 = 3 -IPV6_UNICAST_HOPS = 4 -IPV6_MULTICAST_IF = 9 -IPV6_MULTICAST_HOPS = 10 -IPV6_MULTICAST_LOOP = 11 -IPV6_JOIN_GROUP = 12 -IPV6_LEAVE_GROUP = 13 -IPV6_PORTRANGE = 14 -ICMP6_FILTER = 18 -IPV6_2292PKTINFO = 19 -IPV6_2292HOPLIMIT = 20 -IPV6_2292NEXTHOP = 21 -IPV6_2292HOPOPTS = 22 -IPV6_2292DSTOPTS = 23 -IPV6_2292RTHDR = 24 -IPV6_2292PKTOPTIONS = 25 -IPV6_CHECKSUM = 26 -IPV6_V6ONLY = 27 -IPV6_BINDV6ONLY = IPV6_V6ONLY -IPV6_IPSEC_POLICY = 28 -IPV6_FAITH = 29 -IPV6_FW_ADD = 30 -IPV6_FW_DEL = 31 -IPV6_FW_FLUSH = 32 -IPV6_FW_ZERO = 33 -IPV6_FW_GET = 34 -IPV6_RTHDRDSTOPTS = 35 -IPV6_RECVPKTINFO = 36 -IPV6_RECVHOPLIMIT = 37 -IPV6_RECVRTHDR = 38 -IPV6_RECVHOPOPTS = 39 -IPV6_RECVDSTOPTS = 40 -IPV6_RECVRTHDRDSTOPTS = 41 -IPV6_USE_MIN_MTU = 42 -IPV6_RECVPATHMTU = 43 -IPV6_PATHMTU = 44 -IPV6_REACHCONF = 45 -IPV6_PKTINFO = 46 -IPV6_HOPLIMIT = 47 -IPV6_NEXTHOP = 48 -IPV6_HOPOPTS = 49 -IPV6_DSTOPTS = 50 -IPV6_RTHDR = 51 -IPV6_PKTOPTIONS = 52 -IPV6_RECVTCLASS = 57 -IPV6_AUTOFLOWLABEL = 59 -IPV6_TCLASS = 61 -IPV6_DONTFRAG = 62 -IPV6_PREFER_TEMPADDR = 63 -IPV6_MSFILTER = 74 -IPV6_RTHDR_LOOSE = 0 -IPV6_RTHDR_STRICT = 1 -IPV6_RTHDR_TYPE_0 = 0 -IPV6_DEFAULT_MULTICAST_HOPS = 1 -IPV6_DEFAULT_MULTICAST_LOOP = 1 -IPV6_PORTRANGE_DEFAULT = 0 -IPV6_PORTRANGE_HIGH = 1 -IPV6_PORTRANGE_LOW = 2 -IPV6PROTO_MAXID = (IPPROTO_PIM + 1) -IPV6CTL_FORWARDING = 1 -IPV6CTL_SENDREDIRECTS = 2 -IPV6CTL_DEFHLIM = 3 -IPV6CTL_DEFMTU = 4 -IPV6CTL_FORWSRCRT = 5 -IPV6CTL_STATS = 6 -IPV6CTL_MRTSTATS = 7 -IPV6CTL_MRTPROTO = 8 -IPV6CTL_MAXFRAGPACKETS = 9 -IPV6CTL_SOURCECHECK = 10 -IPV6CTL_SOURCECHECK_LOGINT = 11 -IPV6CTL_ACCEPT_RTADV = 12 -IPV6CTL_KEEPFAITH = 13 -IPV6CTL_LOG_INTERVAL = 14 -IPV6CTL_HDRNESTLIMIT = 15 -IPV6CTL_DAD_COUNT = 16 -IPV6CTL_AUTO_FLOWLABEL = 17 -IPV6CTL_DEFMCASTHLIM = 18 -IPV6CTL_GIF_HLIM = 19 -IPV6CTL_KAME_VERSION = 20 -IPV6CTL_USE_DEPRECATED = 21 -IPV6CTL_RR_PRUNE = 22 -IPV6CTL_MAPPED_ADDR = 23 -IPV6CTL_V6ONLY = 24 -IPV6CTL_RTEXPIRE = 25 -IPV6CTL_RTMINEXPIRE = 26 -IPV6CTL_RTMAXCACHE = 27 -IPV6CTL_USETEMPADDR = 32 -IPV6CTL_TEMPPLTIME = 33 -IPV6CTL_TEMPVLTIME = 34 -IPV6CTL_AUTO_LINKLOCAL = 35 -IPV6CTL_RIP6STATS = 36 -IPV6CTL_PREFER_TEMPADDR = 37 -IPV6CTL_ADDRCTLPOLICY = 38 -IPV6CTL_USE_DEFAULTZONE = 39 -IPV6CTL_MAXFRAGS = 41 -IPV6CTL_IFQ = 42 -IPV6CTL_ISATAPRTR = 43 -IPV6CTL_MCAST_PMTU = 44 -IPV6CTL_STEALTH = 45 -IPV6CTL_MAXID = 46 diff --git a/Lib/plat-freebsd7/regen b/Lib/plat-freebsd7/regen deleted file mode 100644 index 8aa6898c6a..0000000000 --- a/Lib/plat-freebsd7/regen +++ /dev/null @@ -1,3 +0,0 @@ -#! /bin/sh -set -v -python ../../Tools/scripts/h2py.py -i '(u_long)' /usr/include/netinet/in.h diff --git a/Lib/plat-freebsd8/IN.py b/Lib/plat-freebsd8/IN.py deleted file mode 100644 index 4e3b3a23dd..0000000000 --- a/Lib/plat-freebsd8/IN.py +++ /dev/null @@ -1,571 +0,0 @@ -# Generated by h2py from /usr/include/netinet/in.h - -# Included from sys/cdefs.h -__GNUCLIKE_ASM = 3 -__GNUCLIKE_ASM = 2 -__GNUCLIKE___TYPEOF = 1 -__GNUCLIKE___OFFSETOF = 1 -__GNUCLIKE___SECTION = 1 -__GNUCLIKE_ATTRIBUTE_MODE_DI = 1 -__GNUCLIKE_CTOR_SECTION_HANDLING = 1 -__GNUCLIKE_BUILTIN_CONSTANT_P = 1 -__GNUCLIKE_BUILTIN_VARARGS = 1 -__GNUCLIKE_BUILTIN_STDARG = 1 -__GNUCLIKE_BUILTIN_VAALIST = 1 -__GNUC_VA_LIST_COMPATIBILITY = 1 -__GNUCLIKE_BUILTIN_NEXT_ARG = 1 -__GNUCLIKE_BUILTIN_MEMCPY = 1 -__CC_SUPPORTS_INLINE = 1 -__CC_SUPPORTS___INLINE = 1 -__CC_SUPPORTS___INLINE__ = 1 -__CC_SUPPORTS___FUNC__ = 1 -__CC_SUPPORTS_WARNING = 1 -__CC_SUPPORTS_VARADIC_XXX = 1 -__CC_SUPPORTS_DYNAMIC_ARRAY_INIT = 1 -__CC_INT_IS_32BIT = 1 -def __P(protos): return protos - -def __STRING(x): return #x - -def __XSTRING(x): return __STRING(x) - -def __P(protos): return () - -def __STRING(x): return "x" - -def __aligned(x): return __attribute__((__aligned__(x))) - -def __section(x): return __attribute__((__section__(x))) - -def __aligned(x): return __attribute__((__aligned__(x))) - -def __section(x): return __attribute__((__section__(x))) - -def __nonnull(x): return __attribute__((__nonnull__(x))) - -def __predict_true(exp): return __builtin_expect((exp), 1) - -def __predict_false(exp): return __builtin_expect((exp), 0) - -def __predict_true(exp): return (exp) - -def __predict_false(exp): return (exp) - -def __format_arg(fmtarg): return __attribute__((__format_arg__ (fmtarg))) - -def __FBSDID(s): return __IDSTRING(__CONCAT(__rcsid_,__LINE__),s) - -def __RCSID(s): return __IDSTRING(__CONCAT(__rcsid_,__LINE__),s) - -def __RCSID_SOURCE(s): return __IDSTRING(__CONCAT(__rcsid_source_,__LINE__),s) - -def __SCCSID(s): return __IDSTRING(__CONCAT(__sccsid_,__LINE__),s) - -def __COPYRIGHT(s): return __IDSTRING(__CONCAT(__copyright_,__LINE__),s) - -_POSIX_C_SOURCE = 199009 -_POSIX_C_SOURCE = 199209 -__XSI_VISIBLE = 600 -_POSIX_C_SOURCE = 200112 -__XSI_VISIBLE = 500 -_POSIX_C_SOURCE = 199506 -_POSIX_C_SOURCE = 198808 -__POSIX_VISIBLE = 200112 -__ISO_C_VISIBLE = 1999 -__POSIX_VISIBLE = 199506 -__ISO_C_VISIBLE = 1990 -__POSIX_VISIBLE = 199309 -__ISO_C_VISIBLE = 1990 -__POSIX_VISIBLE = 199209 -__ISO_C_VISIBLE = 1990 -__POSIX_VISIBLE = 199009 -__ISO_C_VISIBLE = 1990 -__POSIX_VISIBLE = 198808 -__ISO_C_VISIBLE = 0 -__POSIX_VISIBLE = 0 -__XSI_VISIBLE = 0 -__BSD_VISIBLE = 0 -__ISO_C_VISIBLE = 1990 -__POSIX_VISIBLE = 0 -__XSI_VISIBLE = 0 -__BSD_VISIBLE = 0 -__ISO_C_VISIBLE = 1999 -__POSIX_VISIBLE = 200112 -__XSI_VISIBLE = 600 -__BSD_VISIBLE = 1 -__ISO_C_VISIBLE = 1999 - -# Included from sys/_types.h - -# Included from machine/_types.h - -# Included from machine/endian.h -_QUAD_HIGHWORD = 1 -_QUAD_LOWWORD = 0 -_LITTLE_ENDIAN = 1234 -_BIG_ENDIAN = 4321 -_PDP_ENDIAN = 3412 -_BYTE_ORDER = _LITTLE_ENDIAN -LITTLE_ENDIAN = _LITTLE_ENDIAN -BIG_ENDIAN = _BIG_ENDIAN -PDP_ENDIAN = _PDP_ENDIAN -BYTE_ORDER = _BYTE_ORDER -def __word_swap_int_var(x): return \ - -def __word_swap_int_const(x): return \ - -def __word_swap_int(x): return __word_swap_int_var(x) - -def __byte_swap_int_var(x): return \ - -def __byte_swap_int_const(x): return \ - -def __byte_swap_int(x): return __byte_swap_int_var(x) - -def __byte_swap_word_var(x): return \ - -def __byte_swap_word_const(x): return \ - -def __byte_swap_word(x): return __byte_swap_word_var(x) - -def __htonl(x): return __bswap32(x) - -def __htons(x): return __bswap16(x) - -def __ntohl(x): return __bswap32(x) - -def __ntohs(x): return __bswap16(x) - -IPPROTO_IP = 0 -IPPROTO_ICMP = 1 -IPPROTO_TCP = 6 -IPPROTO_UDP = 17 -def htonl(x): return __htonl(x) - -def htons(x): return __htons(x) - -def ntohl(x): return __ntohl(x) - -def ntohs(x): return __ntohs(x) - -IPPROTO_RAW = 255 -INET_ADDRSTRLEN = 16 -IPPROTO_HOPOPTS = 0 -IPPROTO_IGMP = 2 -IPPROTO_GGP = 3 -IPPROTO_IPV4 = 4 -IPPROTO_IPIP = IPPROTO_IPV4 -IPPROTO_ST = 7 -IPPROTO_EGP = 8 -IPPROTO_PIGP = 9 -IPPROTO_RCCMON = 10 -IPPROTO_NVPII = 11 -IPPROTO_PUP = 12 -IPPROTO_ARGUS = 13 -IPPROTO_EMCON = 14 -IPPROTO_XNET = 15 -IPPROTO_CHAOS = 16 -IPPROTO_MUX = 18 -IPPROTO_MEAS = 19 -IPPROTO_HMP = 20 -IPPROTO_PRM = 21 -IPPROTO_IDP = 22 -IPPROTO_TRUNK1 = 23 -IPPROTO_TRUNK2 = 24 -IPPROTO_LEAF1 = 25 -IPPROTO_LEAF2 = 26 -IPPROTO_RDP = 27 -IPPROTO_IRTP = 28 -IPPROTO_TP = 29 -IPPROTO_BLT = 30 -IPPROTO_NSP = 31 -IPPROTO_INP = 32 -IPPROTO_SEP = 33 -IPPROTO_3PC = 34 -IPPROTO_IDPR = 35 -IPPROTO_XTP = 36 -IPPROTO_DDP = 37 -IPPROTO_CMTP = 38 -IPPROTO_TPXX = 39 -IPPROTO_IL = 40 -IPPROTO_IPV6 = 41 -IPPROTO_SDRP = 42 -IPPROTO_ROUTING = 43 -IPPROTO_FRAGMENT = 44 -IPPROTO_IDRP = 45 -IPPROTO_RSVP = 46 -IPPROTO_GRE = 47 -IPPROTO_MHRP = 48 -IPPROTO_BHA = 49 -IPPROTO_ESP = 50 -IPPROTO_AH = 51 -IPPROTO_INLSP = 52 -IPPROTO_SWIPE = 53 -IPPROTO_NHRP = 54 -IPPROTO_MOBILE = 55 -IPPROTO_TLSP = 56 -IPPROTO_SKIP = 57 -IPPROTO_ICMPV6 = 58 -IPPROTO_NONE = 59 -IPPROTO_DSTOPTS = 60 -IPPROTO_AHIP = 61 -IPPROTO_CFTP = 62 -IPPROTO_HELLO = 63 -IPPROTO_SATEXPAK = 64 -IPPROTO_KRYPTOLAN = 65 -IPPROTO_RVD = 66 -IPPROTO_IPPC = 67 -IPPROTO_ADFS = 68 -IPPROTO_SATMON = 69 -IPPROTO_VISA = 70 -IPPROTO_IPCV = 71 -IPPROTO_CPNX = 72 -IPPROTO_CPHB = 73 -IPPROTO_WSN = 74 -IPPROTO_PVP = 75 -IPPROTO_BRSATMON = 76 -IPPROTO_ND = 77 -IPPROTO_WBMON = 78 -IPPROTO_WBEXPAK = 79 -IPPROTO_EON = 80 -IPPROTO_VMTP = 81 -IPPROTO_SVMTP = 82 -IPPROTO_VINES = 83 -IPPROTO_TTP = 84 -IPPROTO_IGP = 85 -IPPROTO_DGP = 86 -IPPROTO_TCF = 87 -IPPROTO_IGRP = 88 -IPPROTO_OSPFIGP = 89 -IPPROTO_SRPC = 90 -IPPROTO_LARP = 91 -IPPROTO_MTP = 92 -IPPROTO_AX25 = 93 -IPPROTO_IPEIP = 94 -IPPROTO_MICP = 95 -IPPROTO_SCCSP = 96 -IPPROTO_ETHERIP = 97 -IPPROTO_ENCAP = 98 -IPPROTO_APES = 99 -IPPROTO_GMTP = 100 -IPPROTO_IPCOMP = 108 -IPPROTO_SCTP = 132 -IPPROTO_PIM = 103 -IPPROTO_CARP = 112 -IPPROTO_PGM = 113 -IPPROTO_PFSYNC = 240 -IPPROTO_OLD_DIVERT = 254 -IPPROTO_MAX = 256 -IPPROTO_DONE = 257 -IPPROTO_DIVERT = 258 -IPPROTO_SPACER = 32767 -IPPORT_RESERVED = 1024 -IPPORT_HIFIRSTAUTO = 49152 -IPPORT_HILASTAUTO = 65535 -IPPORT_RESERVEDSTART = 600 -IPPORT_MAX = 65535 -def IN_CLASSA(i): return (((u_int32_t)(i) & (-2147483648)) == 0) - -IN_CLASSA_NET = (-16777216) -IN_CLASSA_NSHIFT = 24 -IN_CLASSA_HOST = 0x00ffffff -IN_CLASSA_MAX = 128 -def IN_CLASSB(i): return (((u_int32_t)(i) & (-1073741824)) == (-2147483648)) - -IN_CLASSB_NET = (-65536) -IN_CLASSB_NSHIFT = 16 -IN_CLASSB_HOST = 0x0000ffff -IN_CLASSB_MAX = 65536 -def IN_CLASSC(i): return (((u_int32_t)(i) & (-536870912)) == (-1073741824)) - -IN_CLASSC_NET = (-256) -IN_CLASSC_NSHIFT = 8 -IN_CLASSC_HOST = 0x000000ff -def IN_CLASSD(i): return (((u_int32_t)(i) & (-268435456)) == (-536870912)) - -IN_CLASSD_NET = (-268435456) -IN_CLASSD_NSHIFT = 28 -IN_CLASSD_HOST = 0x0fffffff -def IN_MULTICAST(i): return IN_CLASSD(i) - -def IN_EXPERIMENTAL(i): return (((u_int32_t)(i) & (-268435456)) == (-268435456)) - -def IN_BADCLASS(i): return (((u_int32_t)(i) & (-268435456)) == (-268435456)) - -def IN_LINKLOCAL(i): return (((u_int32_t)(i) & (-65536)) == (-1442971648)) - -def IN_LOCAL_GROUP(i): return (((u_int32_t)(i) & (-256)) == (-536870912)) - -INADDR_NONE = (-1) -IN_LOOPBACKNET = 127 -IP_OPTIONS = 1 -IP_HDRINCL = 2 -IP_TOS = 3 -IP_TTL = 4 -IP_RECVOPTS = 5 -IP_RECVRETOPTS = 6 -IP_RECVDSTADDR = 7 -IP_SENDSRCADDR = IP_RECVDSTADDR -IP_RETOPTS = 8 -IP_MULTICAST_IF = 9 -IP_MULTICAST_TTL = 10 -IP_MULTICAST_LOOP = 11 -IP_ADD_MEMBERSHIP = 12 -IP_DROP_MEMBERSHIP = 13 -IP_MULTICAST_VIF = 14 -IP_RSVP_ON = 15 -IP_RSVP_OFF = 16 -IP_RSVP_VIF_ON = 17 -IP_RSVP_VIF_OFF = 18 -IP_PORTRANGE = 19 -IP_RECVIF = 20 -IP_IPSEC_POLICY = 21 -IP_FAITH = 22 -IP_ONESBCAST = 23 -IP_FW_TABLE_ADD = 40 -IP_FW_TABLE_DEL = 41 -IP_FW_TABLE_FLUSH = 42 -IP_FW_TABLE_GETSIZE = 43 -IP_FW_TABLE_LIST = 44 -IP_FW_ADD = 50 -IP_FW_DEL = 51 -IP_FW_FLUSH = 52 -IP_FW_ZERO = 53 -IP_FW_GET = 54 -IP_FW_RESETLOG = 55 -IP_FW_NAT_CFG = 56 -IP_FW_NAT_DEL = 57 -IP_FW_NAT_GET_CONFIG = 58 -IP_FW_NAT_GET_LOG = 59 -IP_DUMMYNET_CONFIGURE = 60 -IP_DUMMYNET_DEL = 61 -IP_DUMMYNET_FLUSH = 62 -IP_DUMMYNET_GET = 64 -IP_RECVTTL = 65 -IP_MINTTL = 66 -IP_DONTFRAG = 67 -IP_ADD_SOURCE_MEMBERSHIP = 70 -IP_DROP_SOURCE_MEMBERSHIP = 71 -IP_BLOCK_SOURCE = 72 -IP_UNBLOCK_SOURCE = 73 -IP_MSFILTER = 74 -MCAST_JOIN_GROUP = 80 -MCAST_LEAVE_GROUP = 81 -MCAST_JOIN_SOURCE_GROUP = 82 -MCAST_LEAVE_SOURCE_GROUP = 83 -MCAST_BLOCK_SOURCE = 84 -MCAST_UNBLOCK_SOURCE = 85 -IP_DEFAULT_MULTICAST_TTL = 1 -IP_DEFAULT_MULTICAST_LOOP = 1 -IP_MIN_MEMBERSHIPS = 31 -IP_MAX_MEMBERSHIPS = 4095 -IP_MAX_SOURCE_FILTER = 1024 -MCAST_INCLUDE = 1 -MCAST_EXCLUDE = 2 -IP_PORTRANGE_DEFAULT = 0 -IP_PORTRANGE_HIGH = 1 -IP_PORTRANGE_LOW = 2 -IPPROTO_MAXID = (IPPROTO_AH + 1) -IPCTL_FORWARDING = 1 -IPCTL_SENDREDIRECTS = 2 -IPCTL_DEFTTL = 3 -IPCTL_DEFMTU = 4 -IPCTL_RTEXPIRE = 5 -IPCTL_RTMINEXPIRE = 6 -IPCTL_RTMAXCACHE = 7 -IPCTL_SOURCEROUTE = 8 -IPCTL_DIRECTEDBROADCAST = 9 -IPCTL_INTRQMAXLEN = 10 -IPCTL_INTRQDROPS = 11 -IPCTL_STATS = 12 -IPCTL_ACCEPTSOURCEROUTE = 13 -IPCTL_FASTFORWARDING = 14 -IPCTL_KEEPFAITH = 15 -IPCTL_GIF_TTL = 16 -IPCTL_MAXID = 17 -def in_nullhost(x): return ((x).s_addr == INADDR_ANY) - - -# Included from netinet6/in6.h -__KAME_VERSION = "FreeBSD" -IPV6PORT_RESERVED = 1024 -IPV6PORT_ANONMIN = 49152 -IPV6PORT_ANONMAX = 65535 -IPV6PORT_RESERVEDMIN = 600 -IPV6PORT_RESERVEDMAX = (IPV6PORT_RESERVED-1) -INET6_ADDRSTRLEN = 46 -IPV6_ADDR_INT32_ONE = 1 -IPV6_ADDR_INT32_TWO = 2 -IPV6_ADDR_INT32_MNL = (-16711680) -IPV6_ADDR_INT32_MLL = (-16646144) -IPV6_ADDR_INT32_SMP = 0x0000ffff -IPV6_ADDR_INT16_ULL = 0xfe80 -IPV6_ADDR_INT16_USL = 0xfec0 -IPV6_ADDR_INT16_MLL = 0xff02 -IPV6_ADDR_INT32_ONE = 0x01000000 -IPV6_ADDR_INT32_TWO = 0x02000000 -IPV6_ADDR_INT32_MNL = 0x000001ff -IPV6_ADDR_INT32_MLL = 0x000002ff -IPV6_ADDR_INT32_SMP = (-65536) -IPV6_ADDR_INT16_ULL = 0x80fe -IPV6_ADDR_INT16_USL = 0xc0fe -IPV6_ADDR_INT16_MLL = 0x02ff -def IN6_IS_ADDR_UNSPECIFIED(a): return \ - -def IN6_IS_ADDR_LOOPBACK(a): return \ - -def IN6_IS_ADDR_V4COMPAT(a): return \ - -def IN6_IS_ADDR_V4MAPPED(a): return \ - -IPV6_ADDR_SCOPE_NODELOCAL = 0x01 -IPV6_ADDR_SCOPE_INTFACELOCAL = 0x01 -IPV6_ADDR_SCOPE_LINKLOCAL = 0x02 -IPV6_ADDR_SCOPE_SITELOCAL = 0x05 -IPV6_ADDR_SCOPE_ORGLOCAL = 0x08 -IPV6_ADDR_SCOPE_GLOBAL = 0x0e -__IPV6_ADDR_SCOPE_NODELOCAL = 0x01 -__IPV6_ADDR_SCOPE_INTFACELOCAL = 0x01 -__IPV6_ADDR_SCOPE_LINKLOCAL = 0x02 -__IPV6_ADDR_SCOPE_SITELOCAL = 0x05 -__IPV6_ADDR_SCOPE_ORGLOCAL = 0x08 -__IPV6_ADDR_SCOPE_GLOBAL = 0x0e -def IN6_IS_ADDR_LINKLOCAL(a): return \ - -def IN6_IS_ADDR_SITELOCAL(a): return \ - -def IN6_IS_ADDR_MC_NODELOCAL(a): return \ - -def IN6_IS_ADDR_MC_INTFACELOCAL(a): return \ - -def IN6_IS_ADDR_MC_LINKLOCAL(a): return \ - -def IN6_IS_ADDR_MC_SITELOCAL(a): return \ - -def IN6_IS_ADDR_MC_ORGLOCAL(a): return \ - -def IN6_IS_ADDR_MC_GLOBAL(a): return \ - -def IN6_IS_ADDR_MC_NODELOCAL(a): return \ - -def IN6_IS_ADDR_MC_LINKLOCAL(a): return \ - -def IN6_IS_ADDR_MC_SITELOCAL(a): return \ - -def IN6_IS_ADDR_MC_ORGLOCAL(a): return \ - -def IN6_IS_ADDR_MC_GLOBAL(a): return \ - -def IN6_IS_SCOPE_LINKLOCAL(a): return \ - -def IN6_IS_SCOPE_EMBED(a): return \ - -def IFA6_IS_DEPRECATED(a): return \ - -def IFA6_IS_INVALID(a): return \ - -IPV6_OPTIONS = 1 -IPV6_RECVOPTS = 5 -IPV6_RECVRETOPTS = 6 -IPV6_RECVDSTADDR = 7 -IPV6_RETOPTS = 8 -IPV6_SOCKOPT_RESERVED1 = 3 -IPV6_UNICAST_HOPS = 4 -IPV6_MULTICAST_IF = 9 -IPV6_MULTICAST_HOPS = 10 -IPV6_MULTICAST_LOOP = 11 -IPV6_JOIN_GROUP = 12 -IPV6_LEAVE_GROUP = 13 -IPV6_PORTRANGE = 14 -ICMP6_FILTER = 18 -IPV6_2292PKTINFO = 19 -IPV6_2292HOPLIMIT = 20 -IPV6_2292NEXTHOP = 21 -IPV6_2292HOPOPTS = 22 -IPV6_2292DSTOPTS = 23 -IPV6_2292RTHDR = 24 -IPV6_2292PKTOPTIONS = 25 -IPV6_CHECKSUM = 26 -IPV6_V6ONLY = 27 -IPV6_BINDV6ONLY = IPV6_V6ONLY -IPV6_IPSEC_POLICY = 28 -IPV6_FAITH = 29 -IPV6_FW_ADD = 30 -IPV6_FW_DEL = 31 -IPV6_FW_FLUSH = 32 -IPV6_FW_ZERO = 33 -IPV6_FW_GET = 34 -IPV6_RTHDRDSTOPTS = 35 -IPV6_RECVPKTINFO = 36 -IPV6_RECVHOPLIMIT = 37 -IPV6_RECVRTHDR = 38 -IPV6_RECVHOPOPTS = 39 -IPV6_RECVDSTOPTS = 40 -IPV6_RECVRTHDRDSTOPTS = 41 -IPV6_USE_MIN_MTU = 42 -IPV6_RECVPATHMTU = 43 -IPV6_PATHMTU = 44 -IPV6_REACHCONF = 45 -IPV6_PKTINFO = 46 -IPV6_HOPLIMIT = 47 -IPV6_NEXTHOP = 48 -IPV6_HOPOPTS = 49 -IPV6_DSTOPTS = 50 -IPV6_RTHDR = 51 -IPV6_PKTOPTIONS = 52 -IPV6_RECVTCLASS = 57 -IPV6_AUTOFLOWLABEL = 59 -IPV6_TCLASS = 61 -IPV6_DONTFRAG = 62 -IPV6_PREFER_TEMPADDR = 63 -IPV6_MSFILTER = 74 -IPV6_RTHDR_LOOSE = 0 -IPV6_RTHDR_STRICT = 1 -IPV6_RTHDR_TYPE_0 = 0 -IPV6_DEFAULT_MULTICAST_HOPS = 1 -IPV6_DEFAULT_MULTICAST_LOOP = 1 -IPV6_PORTRANGE_DEFAULT = 0 -IPV6_PORTRANGE_HIGH = 1 -IPV6_PORTRANGE_LOW = 2 -IPV6PROTO_MAXID = (IPPROTO_PIM + 1) -IPV6CTL_FORWARDING = 1 -IPV6CTL_SENDREDIRECTS = 2 -IPV6CTL_DEFHLIM = 3 -IPV6CTL_DEFMTU = 4 -IPV6CTL_FORWSRCRT = 5 -IPV6CTL_STATS = 6 -IPV6CTL_MRTSTATS = 7 -IPV6CTL_MRTPROTO = 8 -IPV6CTL_MAXFRAGPACKETS = 9 -IPV6CTL_SOURCECHECK = 10 -IPV6CTL_SOURCECHECK_LOGINT = 11 -IPV6CTL_ACCEPT_RTADV = 12 -IPV6CTL_KEEPFAITH = 13 -IPV6CTL_LOG_INTERVAL = 14 -IPV6CTL_HDRNESTLIMIT = 15 -IPV6CTL_DAD_COUNT = 16 -IPV6CTL_AUTO_FLOWLABEL = 17 -IPV6CTL_DEFMCASTHLIM = 18 -IPV6CTL_GIF_HLIM = 19 -IPV6CTL_KAME_VERSION = 20 -IPV6CTL_USE_DEPRECATED = 21 -IPV6CTL_RR_PRUNE = 22 -IPV6CTL_MAPPED_ADDR = 23 -IPV6CTL_V6ONLY = 24 -IPV6CTL_RTEXPIRE = 25 -IPV6CTL_RTMINEXPIRE = 26 -IPV6CTL_RTMAXCACHE = 27 -IPV6CTL_USETEMPADDR = 32 -IPV6CTL_TEMPPLTIME = 33 -IPV6CTL_TEMPVLTIME = 34 -IPV6CTL_AUTO_LINKLOCAL = 35 -IPV6CTL_RIP6STATS = 36 -IPV6CTL_PREFER_TEMPADDR = 37 -IPV6CTL_ADDRCTLPOLICY = 38 -IPV6CTL_USE_DEFAULTZONE = 39 -IPV6CTL_MAXFRAGS = 41 -IPV6CTL_IFQ = 42 -IPV6CTL_ISATAPRTR = 43 -IPV6CTL_MCAST_PMTU = 44 -IPV6CTL_STEALTH = 45 -IPV6CTL_MAXID = 46 diff --git a/Lib/plat-freebsd8/regen b/Lib/plat-freebsd8/regen deleted file mode 100644 index 8aa6898c6a..0000000000 --- a/Lib/plat-freebsd8/regen +++ /dev/null @@ -1,3 +0,0 @@ -#! /bin/sh -set -v -python ../../Tools/scripts/h2py.py -i '(u_long)' /usr/include/netinet/in.h diff --git a/Lib/plat-generic/regen b/Lib/plat-generic/regen deleted file mode 100755 index c96167dcb0..0000000000 --- a/Lib/plat-generic/regen +++ /dev/null @@ -1,3 +0,0 @@ -#! /bin/sh -set -v -eval $PYTHON_FOR_BUILD ../../Tools/scripts/h2py.py -i "'(u_long)'" /usr/include/netinet/in.h diff --git a/Lib/plat-linux/CDROM.py b/Lib/plat-linux/CDROM.py deleted file mode 100644 index 434093684c..0000000000 --- a/Lib/plat-linux/CDROM.py +++ /dev/null @@ -1,207 +0,0 @@ -# Generated by h2py from /usr/include/linux/cdrom.h - -CDROMPAUSE = 0x5301 -CDROMRESUME = 0x5302 -CDROMPLAYMSF = 0x5303 -CDROMPLAYTRKIND = 0x5304 -CDROMREADTOCHDR = 0x5305 -CDROMREADTOCENTRY = 0x5306 -CDROMSTOP = 0x5307 -CDROMSTART = 0x5308 -CDROMEJECT = 0x5309 -CDROMVOLCTRL = 0x530a -CDROMSUBCHNL = 0x530b -CDROMREADMODE2 = 0x530c -CDROMREADMODE1 = 0x530d -CDROMREADAUDIO = 0x530e -CDROMEJECT_SW = 0x530f -CDROMMULTISESSION = 0x5310 -CDROM_GET_MCN = 0x5311 -CDROM_GET_UPC = CDROM_GET_MCN -CDROMRESET = 0x5312 -CDROMVOLREAD = 0x5313 -CDROMREADRAW = 0x5314 -CDROMREADCOOKED = 0x5315 -CDROMSEEK = 0x5316 -CDROMPLAYBLK = 0x5317 -CDROMREADALL = 0x5318 -CDROMGETSPINDOWN = 0x531d -CDROMSETSPINDOWN = 0x531e -CDROMCLOSETRAY = 0x5319 -CDROM_SET_OPTIONS = 0x5320 -CDROM_CLEAR_OPTIONS = 0x5321 -CDROM_SELECT_SPEED = 0x5322 -CDROM_SELECT_DISC = 0x5323 -CDROM_MEDIA_CHANGED = 0x5325 -CDROM_DRIVE_STATUS = 0x5326 -CDROM_DISC_STATUS = 0x5327 -CDROM_CHANGER_NSLOTS = 0x5328 -CDROM_LOCKDOOR = 0x5329 -CDROM_DEBUG = 0x5330 -CDROM_GET_CAPABILITY = 0x5331 -CDROMAUDIOBUFSIZ = 0x5382 -DVD_READ_STRUCT = 0x5390 -DVD_WRITE_STRUCT = 0x5391 -DVD_AUTH = 0x5392 -CDROM_SEND_PACKET = 0x5393 -CDROM_NEXT_WRITABLE = 0x5394 -CDROM_LAST_WRITTEN = 0x5395 -CDROM_PACKET_SIZE = 12 -CGC_DATA_UNKNOWN = 0 -CGC_DATA_WRITE = 1 -CGC_DATA_READ = 2 -CGC_DATA_NONE = 3 -CD_MINS = 74 -CD_SECS = 60 -CD_FRAMES = 75 -CD_SYNC_SIZE = 12 -CD_MSF_OFFSET = 150 -CD_CHUNK_SIZE = 24 -CD_NUM_OF_CHUNKS = 98 -CD_FRAMESIZE_SUB = 96 -CD_HEAD_SIZE = 4 -CD_SUBHEAD_SIZE = 8 -CD_EDC_SIZE = 4 -CD_ZERO_SIZE = 8 -CD_ECC_SIZE = 276 -CD_FRAMESIZE = 2048 -CD_FRAMESIZE_RAW = 2352 -CD_FRAMESIZE_RAWER = 2646 -CD_FRAMESIZE_RAW1 = (CD_FRAMESIZE_RAW-CD_SYNC_SIZE) -CD_FRAMESIZE_RAW0 = (CD_FRAMESIZE_RAW-CD_SYNC_SIZE-CD_HEAD_SIZE) -CD_XA_HEAD = (CD_HEAD_SIZE+CD_SUBHEAD_SIZE) -CD_XA_TAIL = (CD_EDC_SIZE+CD_ECC_SIZE) -CD_XA_SYNC_HEAD = (CD_SYNC_SIZE+CD_XA_HEAD) -CDROM_LBA = 0x01 -CDROM_MSF = 0x02 -CDROM_DATA_TRACK = 0x04 -CDROM_LEADOUT = 0xAA -CDROM_AUDIO_INVALID = 0x00 -CDROM_AUDIO_PLAY = 0x11 -CDROM_AUDIO_PAUSED = 0x12 -CDROM_AUDIO_COMPLETED = 0x13 -CDROM_AUDIO_ERROR = 0x14 -CDROM_AUDIO_NO_STATUS = 0x15 -CDC_CLOSE_TRAY = 0x1 -CDC_OPEN_TRAY = 0x2 -CDC_LOCK = 0x4 -CDC_SELECT_SPEED = 0x8 -CDC_SELECT_DISC = 0x10 -CDC_MULTI_SESSION = 0x20 -CDC_MCN = 0x40 -CDC_MEDIA_CHANGED = 0x80 -CDC_PLAY_AUDIO = 0x100 -CDC_RESET = 0x200 -CDC_IOCTLS = 0x400 -CDC_DRIVE_STATUS = 0x800 -CDC_GENERIC_PACKET = 0x1000 -CDC_CD_R = 0x2000 -CDC_CD_RW = 0x4000 -CDC_DVD = 0x8000 -CDC_DVD_R = 0x10000 -CDC_DVD_RAM = 0x20000 -CDS_NO_INFO = 0 -CDS_NO_DISC = 1 -CDS_TRAY_OPEN = 2 -CDS_DRIVE_NOT_READY = 3 -CDS_DISC_OK = 4 -CDS_AUDIO = 100 -CDS_DATA_1 = 101 -CDS_DATA_2 = 102 -CDS_XA_2_1 = 103 -CDS_XA_2_2 = 104 -CDS_MIXED = 105 -CDO_AUTO_CLOSE = 0x1 -CDO_AUTO_EJECT = 0x2 -CDO_USE_FFLAGS = 0x4 -CDO_LOCK = 0x8 -CDO_CHECK_TYPE = 0x10 -CD_PART_MAX = 64 -CD_PART_MASK = (CD_PART_MAX - 1) -GPCMD_BLANK = 0xa1 -GPCMD_CLOSE_TRACK = 0x5b -GPCMD_FLUSH_CACHE = 0x35 -GPCMD_FORMAT_UNIT = 0x04 -GPCMD_GET_CONFIGURATION = 0x46 -GPCMD_GET_EVENT_STATUS_NOTIFICATION = 0x4a -GPCMD_GET_PERFORMANCE = 0xac -GPCMD_INQUIRY = 0x12 -GPCMD_LOAD_UNLOAD = 0xa6 -GPCMD_MECHANISM_STATUS = 0xbd -GPCMD_MODE_SELECT_10 = 0x55 -GPCMD_MODE_SENSE_10 = 0x5a -GPCMD_PAUSE_RESUME = 0x4b -GPCMD_PLAY_AUDIO_10 = 0x45 -GPCMD_PLAY_AUDIO_MSF = 0x47 -GPCMD_PLAY_AUDIO_TI = 0x48 -GPCMD_PLAY_CD = 0xbc -GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL = 0x1e -GPCMD_READ_10 = 0x28 -GPCMD_READ_12 = 0xa8 -GPCMD_READ_CDVD_CAPACITY = 0x25 -GPCMD_READ_CD = 0xbe -GPCMD_READ_CD_MSF = 0xb9 -GPCMD_READ_DISC_INFO = 0x51 -GPCMD_READ_DVD_STRUCTURE = 0xad -GPCMD_READ_FORMAT_CAPACITIES = 0x23 -GPCMD_READ_HEADER = 0x44 -GPCMD_READ_TRACK_RZONE_INFO = 0x52 -GPCMD_READ_SUBCHANNEL = 0x42 -GPCMD_READ_TOC_PMA_ATIP = 0x43 -GPCMD_REPAIR_RZONE_TRACK = 0x58 -GPCMD_REPORT_KEY = 0xa4 -GPCMD_REQUEST_SENSE = 0x03 -GPCMD_RESERVE_RZONE_TRACK = 0x53 -GPCMD_SCAN = 0xba -GPCMD_SEEK = 0x2b -GPCMD_SEND_DVD_STRUCTURE = 0xad -GPCMD_SEND_EVENT = 0xa2 -GPCMD_SEND_KEY = 0xa3 -GPCMD_SEND_OPC = 0x54 -GPCMD_SET_READ_AHEAD = 0xa7 -GPCMD_SET_STREAMING = 0xb6 -GPCMD_START_STOP_UNIT = 0x1b -GPCMD_STOP_PLAY_SCAN = 0x4e -GPCMD_TEST_UNIT_READY = 0x00 -GPCMD_VERIFY_10 = 0x2f -GPCMD_WRITE_10 = 0x2a -GPCMD_WRITE_AND_VERIFY_10 = 0x2e -GPCMD_SET_SPEED = 0xbb -GPCMD_PLAYAUDIO_TI = 0x48 -GPCMD_GET_MEDIA_STATUS = 0xda -GPMODE_R_W_ERROR_PAGE = 0x01 -GPMODE_WRITE_PARMS_PAGE = 0x05 -GPMODE_AUDIO_CTL_PAGE = 0x0e -GPMODE_POWER_PAGE = 0x1a -GPMODE_FAULT_FAIL_PAGE = 0x1c -GPMODE_TO_PROTECT_PAGE = 0x1d -GPMODE_CAPABILITIES_PAGE = 0x2a -GPMODE_ALL_PAGES = 0x3f -GPMODE_CDROM_PAGE = 0x0d -DVD_STRUCT_PHYSICAL = 0x00 -DVD_STRUCT_COPYRIGHT = 0x01 -DVD_STRUCT_DISCKEY = 0x02 -DVD_STRUCT_BCA = 0x03 -DVD_STRUCT_MANUFACT = 0x04 -DVD_LAYERS = 4 -DVD_LU_SEND_AGID = 0 -DVD_HOST_SEND_CHALLENGE = 1 -DVD_LU_SEND_KEY1 = 2 -DVD_LU_SEND_CHALLENGE = 3 -DVD_HOST_SEND_KEY2 = 4 -DVD_AUTH_ESTABLISHED = 5 -DVD_AUTH_FAILURE = 6 -DVD_LU_SEND_TITLE_KEY = 7 -DVD_LU_SEND_ASF = 8 -DVD_INVALIDATE_AGID = 9 -DVD_LU_SEND_RPC_STATE = 10 -DVD_HOST_SEND_RPC_STATE = 11 -DVD_CPM_NO_COPYRIGHT = 0 -DVD_CPM_COPYRIGHTED = 1 -DVD_CP_SEC_NONE = 0 -DVD_CP_SEC_EXIST = 1 -DVD_CGMS_UNRESTRICTED = 0 -DVD_CGMS_SINGLE = 2 -DVD_CGMS_RESTRICTED = 3 - -CDROM_MAX_SLOTS = 256 diff --git a/Lib/plat-linux/DLFCN.py b/Lib/plat-linux/DLFCN.py deleted file mode 100644 index dd10ac4ead..0000000000 --- a/Lib/plat-linux/DLFCN.py +++ /dev/null @@ -1,83 +0,0 @@ -# Generated by h2py from /usr/include/dlfcn.h -_DLFCN_H = 1 - -# Included from features.h -_FEATURES_H = 1 -__USE_ANSI = 1 -__FAVOR_BSD = 1 -_ISOC99_SOURCE = 1 -_POSIX_SOURCE = 1 -_POSIX_C_SOURCE = 199506 -_XOPEN_SOURCE = 600 -_XOPEN_SOURCE_EXTENDED = 1 -_LARGEFILE64_SOURCE = 1 -_BSD_SOURCE = 1 -_SVID_SOURCE = 1 -_BSD_SOURCE = 1 -_SVID_SOURCE = 1 -__USE_ISOC99 = 1 -_POSIX_SOURCE = 1 -_POSIX_C_SOURCE = 2 -_POSIX_C_SOURCE = 199506 -__USE_POSIX = 1 -__USE_POSIX2 = 1 -__USE_POSIX199309 = 1 -__USE_POSIX199506 = 1 -__USE_XOPEN = 1 -__USE_XOPEN_EXTENDED = 1 -__USE_UNIX98 = 1 -_LARGEFILE_SOURCE = 1 -__USE_XOPEN2K = 1 -__USE_ISOC99 = 1 -__USE_XOPEN_EXTENDED = 1 -__USE_LARGEFILE = 1 -__USE_LARGEFILE64 = 1 -__USE_FILE_OFFSET64 = 1 -__USE_MISC = 1 -__USE_BSD = 1 -__USE_SVID = 1 -__USE_GNU = 1 -__USE_REENTRANT = 1 -__STDC_IEC_559__ = 1 -__STDC_IEC_559_COMPLEX__ = 1 -__STDC_ISO_10646__ = 200009 -__GNU_LIBRARY__ = 6 -__GLIBC__ = 2 -__GLIBC_MINOR__ = 2 - -# Included from sys/cdefs.h -_SYS_CDEFS_H = 1 -def __PMT(args): return args - -def __P(args): return args - -def __PMT(args): return args - -def __STRING(x): return #x - -__flexarr = [] -__flexarr = [0] -__flexarr = [] -__flexarr = [1] -def __ASMNAME(cname): return __ASMNAME2 (__USER_LABEL_PREFIX__, cname) - -def __attribute__(xyz): return - -def __attribute_format_arg__(x): return __attribute__ ((__format_arg__ (x))) - -def __attribute_format_arg__(x): return - -__USE_LARGEFILE = 1 -__USE_LARGEFILE64 = 1 -__USE_EXTERN_INLINES = 1 - -# Included from gnu/stubs.h - -# Included from bits/dlfcn.h -RTLD_LAZY = 0x00001 -RTLD_NOW = 0x00002 -RTLD_BINDING_MASK = 0x3 -RTLD_NOLOAD = 0x00004 -RTLD_GLOBAL = 0x00100 -RTLD_LOCAL = 0 -RTLD_NODELETE = 0x01000 diff --git a/Lib/plat-linux/IN.py b/Lib/plat-linux/IN.py deleted file mode 100644 index d7d30024c2..0000000000 --- a/Lib/plat-linux/IN.py +++ /dev/null @@ -1,615 +0,0 @@ -# Generated by h2py from /usr/include/netinet/in.h -_NETINET_IN_H = 1 - -# Included from features.h -_FEATURES_H = 1 -__USE_ANSI = 1 -__FAVOR_BSD = 1 -_ISOC99_SOURCE = 1 -_POSIX_SOURCE = 1 -_POSIX_C_SOURCE = 199506 -_XOPEN_SOURCE = 600 -_XOPEN_SOURCE_EXTENDED = 1 -_LARGEFILE64_SOURCE = 1 -_BSD_SOURCE = 1 -_SVID_SOURCE = 1 -_BSD_SOURCE = 1 -_SVID_SOURCE = 1 -__USE_ISOC99 = 1 -_POSIX_SOURCE = 1 -_POSIX_C_SOURCE = 2 -_POSIX_C_SOURCE = 199506 -__USE_POSIX = 1 -__USE_POSIX2 = 1 -__USE_POSIX199309 = 1 -__USE_POSIX199506 = 1 -__USE_XOPEN = 1 -__USE_XOPEN_EXTENDED = 1 -__USE_UNIX98 = 1 -_LARGEFILE_SOURCE = 1 -__USE_XOPEN2K = 1 -__USE_ISOC99 = 1 -__USE_XOPEN_EXTENDED = 1 -__USE_LARGEFILE = 1 -__USE_LARGEFILE64 = 1 -__USE_FILE_OFFSET64 = 1 -__USE_MISC = 1 -__USE_BSD = 1 -__USE_SVID = 1 -__USE_GNU = 1 -__USE_REENTRANT = 1 -__STDC_IEC_559__ = 1 -__STDC_IEC_559_COMPLEX__ = 1 -__STDC_ISO_10646__ = 200009 -__GNU_LIBRARY__ = 6 -__GLIBC__ = 2 -__GLIBC_MINOR__ = 2 - -# Included from sys/cdefs.h -_SYS_CDEFS_H = 1 -def __PMT(args): return args - -def __P(args): return args - -def __PMT(args): return args - -def __STRING(x): return #x - -__flexarr = [] -__flexarr = [0] -__flexarr = [] -__flexarr = [1] -def __ASMNAME(cname): return __ASMNAME2 (__USER_LABEL_PREFIX__, cname) - -def __attribute__(xyz): return - -def __attribute_format_arg__(x): return __attribute__ ((__format_arg__ (x))) - -def __attribute_format_arg__(x): return - -__USE_LARGEFILE = 1 -__USE_LARGEFILE64 = 1 -__USE_EXTERN_INLINES = 1 - -# Included from gnu/stubs.h - -# Included from stdint.h -_STDINT_H = 1 - -# Included from bits/wchar.h -_BITS_WCHAR_H = 1 -__WCHAR_MIN = (-2147483647 - 1) -__WCHAR_MAX = (2147483647) - -# Included from bits/wordsize.h -__WORDSIZE = 32 -def __INT64_C(c): return c ## L - -def __UINT64_C(c): return c ## UL - -def __INT64_C(c): return c ## LL - -def __UINT64_C(c): return c ## ULL - -INT8_MIN = (-128) -INT16_MIN = (-32767-1) -INT32_MIN = (-2147483647-1) -INT64_MIN = (-__INT64_C(9223372036854775807)-1) -INT8_MAX = (127) -INT16_MAX = (32767) -INT32_MAX = (2147483647) -INT64_MAX = (__INT64_C(9223372036854775807)) -UINT8_MAX = (255) -UINT16_MAX = (65535) -UINT64_MAX = (__UINT64_C(18446744073709551615)) -INT_LEAST8_MIN = (-128) -INT_LEAST16_MIN = (-32767-1) -INT_LEAST32_MIN = (-2147483647-1) -INT_LEAST64_MIN = (-__INT64_C(9223372036854775807)-1) -INT_LEAST8_MAX = (127) -INT_LEAST16_MAX = (32767) -INT_LEAST32_MAX = (2147483647) -INT_LEAST64_MAX = (__INT64_C(9223372036854775807)) -UINT_LEAST8_MAX = (255) -UINT_LEAST16_MAX = (65535) -UINT_LEAST64_MAX = (__UINT64_C(18446744073709551615)) -INT_FAST8_MIN = (-128) -INT_FAST16_MIN = (-9223372036854775807-1) -INT_FAST32_MIN = (-9223372036854775807-1) -INT_FAST16_MIN = (-2147483647-1) -INT_FAST32_MIN = (-2147483647-1) -INT_FAST64_MIN = (-__INT64_C(9223372036854775807)-1) -INT_FAST8_MAX = (127) -INT_FAST16_MAX = (9223372036854775807) -INT_FAST32_MAX = (9223372036854775807) -INT_FAST16_MAX = (2147483647) -INT_FAST32_MAX = (2147483647) -INT_FAST64_MAX = (__INT64_C(9223372036854775807)) -UINT_FAST8_MAX = (255) -UINT_FAST64_MAX = (__UINT64_C(18446744073709551615)) -INTPTR_MIN = (-9223372036854775807-1) -INTPTR_MAX = (9223372036854775807) -INTPTR_MIN = (-2147483647-1) -INTPTR_MAX = (2147483647) -INTMAX_MIN = (-__INT64_C(9223372036854775807)-1) -INTMAX_MAX = (__INT64_C(9223372036854775807)) -UINTMAX_MAX = (__UINT64_C(18446744073709551615)) -PTRDIFF_MIN = (-9223372036854775807-1) -PTRDIFF_MAX = (9223372036854775807) -PTRDIFF_MIN = (-2147483647-1) -PTRDIFF_MAX = (2147483647) -SIG_ATOMIC_MIN = (-2147483647-1) -SIG_ATOMIC_MAX = (2147483647) -WCHAR_MIN = __WCHAR_MIN -WCHAR_MAX = __WCHAR_MAX -def INT8_C(c): return c - -def INT16_C(c): return c - -def INT32_C(c): return c - -def INT64_C(c): return c ## L - -def INT64_C(c): return c ## LL - -def UINT8_C(c): return c ## U - -def UINT16_C(c): return c ## U - -def UINT32_C(c): return c ## U - -def UINT64_C(c): return c ## UL - -def UINT64_C(c): return c ## ULL - -def INTMAX_C(c): return c ## L - -def UINTMAX_C(c): return c ## UL - -def INTMAX_C(c): return c ## LL - -def UINTMAX_C(c): return c ## ULL - - -# Included from bits/types.h -_BITS_TYPES_H = 1 -__FD_SETSIZE = 1024 - -# Included from bits/pthreadtypes.h -_BITS_PTHREADTYPES_H = 1 - -# Included from bits/sched.h -SCHED_OTHER = 0 -SCHED_FIFO = 1 -SCHED_RR = 2 -CSIGNAL = 0x000000ff -CLONE_VM = 0x00000100 -CLONE_FS = 0x00000200 -CLONE_FILES = 0x00000400 -CLONE_SIGHAND = 0x00000800 -CLONE_PID = 0x00001000 -CLONE_PTRACE = 0x00002000 -CLONE_VFORK = 0x00004000 -__defined_schedparam = 1 -def IN_CLASSA(a): return ((((in_addr_t)(a)) & (-2147483648)) == 0) - -IN_CLASSA_NET = (-16777216) -IN_CLASSA_NSHIFT = 24 -IN_CLASSA_HOST = ((-1) & ~IN_CLASSA_NET) -IN_CLASSA_MAX = 128 -def IN_CLASSB(a): return ((((in_addr_t)(a)) & (-1073741824)) == (-2147483648)) - -IN_CLASSB_NET = (-65536) -IN_CLASSB_NSHIFT = 16 -IN_CLASSB_HOST = ((-1) & ~IN_CLASSB_NET) -IN_CLASSB_MAX = 65536 -def IN_CLASSC(a): return ((((in_addr_t)(a)) & (-536870912)) == (-1073741824)) - -IN_CLASSC_NET = (-256) -IN_CLASSC_NSHIFT = 8 -IN_CLASSC_HOST = ((-1) & ~IN_CLASSC_NET) -def IN_CLASSD(a): return ((((in_addr_t)(a)) & (-268435456)) == (-536870912)) - -def IN_MULTICAST(a): return IN_CLASSD(a) - -def IN_EXPERIMENTAL(a): return ((((in_addr_t)(a)) & (-536870912)) == (-536870912)) - -def IN_BADCLASS(a): return ((((in_addr_t)(a)) & (-268435456)) == (-268435456)) - -IN_LOOPBACKNET = 127 -INET_ADDRSTRLEN = 16 -INET6_ADDRSTRLEN = 46 - -# Included from bits/socket.h - -# Included from limits.h -_LIBC_LIMITS_H_ = 1 -MB_LEN_MAX = 16 -_LIMITS_H = 1 -CHAR_BIT = 8 -SCHAR_MIN = (-128) -SCHAR_MAX = 127 -UCHAR_MAX = 255 -CHAR_MIN = 0 -CHAR_MAX = UCHAR_MAX -CHAR_MIN = SCHAR_MIN -CHAR_MAX = SCHAR_MAX -SHRT_MIN = (-32768) -SHRT_MAX = 32767 -USHRT_MAX = 65535 -INT_MAX = 2147483647 -LONG_MAX = 9223372036854775807 -LONG_MAX = 2147483647 -LONG_MIN = (-LONG_MAX - 1) - -# Included from bits/posix1_lim.h -_BITS_POSIX1_LIM_H = 1 -_POSIX_AIO_LISTIO_MAX = 2 -_POSIX_AIO_MAX = 1 -_POSIX_ARG_MAX = 4096 -_POSIX_CHILD_MAX = 6 -_POSIX_DELAYTIMER_MAX = 32 -_POSIX_LINK_MAX = 8 -_POSIX_MAX_CANON = 255 -_POSIX_MAX_INPUT = 255 -_POSIX_MQ_OPEN_MAX = 8 -_POSIX_MQ_PRIO_MAX = 32 -_POSIX_NGROUPS_MAX = 0 -_POSIX_OPEN_MAX = 16 -_POSIX_FD_SETSIZE = _POSIX_OPEN_MAX -_POSIX_NAME_MAX = 14 -_POSIX_PATH_MAX = 256 -_POSIX_PIPE_BUF = 512 -_POSIX_RTSIG_MAX = 8 -_POSIX_SEM_NSEMS_MAX = 256 -_POSIX_SEM_VALUE_MAX = 32767 -_POSIX_SIGQUEUE_MAX = 32 -_POSIX_SSIZE_MAX = 32767 -_POSIX_STREAM_MAX = 8 -_POSIX_TZNAME_MAX = 6 -_POSIX_QLIMIT = 1 -_POSIX_HIWAT = _POSIX_PIPE_BUF -_POSIX_UIO_MAXIOV = 16 -_POSIX_TTY_NAME_MAX = 9 -_POSIX_TIMER_MAX = 32 -_POSIX_LOGIN_NAME_MAX = 9 -_POSIX_CLOCKRES_MIN = 20000000 - -# Included from bits/local_lim.h - -# Included from linux/limits.h -NR_OPEN = 1024 -NGROUPS_MAX = 32 -ARG_MAX = 131072 -CHILD_MAX = 999 -OPEN_MAX = 256 -LINK_MAX = 127 -MAX_CANON = 255 -MAX_INPUT = 255 -NAME_MAX = 255 -PATH_MAX = 4096 -PIPE_BUF = 4096 -RTSIG_MAX = 32 -_POSIX_THREAD_KEYS_MAX = 128 -PTHREAD_KEYS_MAX = 1024 -_POSIX_THREAD_DESTRUCTOR_ITERATIONS = 4 -PTHREAD_DESTRUCTOR_ITERATIONS = _POSIX_THREAD_DESTRUCTOR_ITERATIONS -_POSIX_THREAD_THREADS_MAX = 64 -PTHREAD_THREADS_MAX = 1024 -AIO_PRIO_DELTA_MAX = 20 -PTHREAD_STACK_MIN = 16384 -TIMER_MAX = 256 -SSIZE_MAX = LONG_MAX -NGROUPS_MAX = _POSIX_NGROUPS_MAX - -# Included from bits/posix2_lim.h -_BITS_POSIX2_LIM_H = 1 -_POSIX2_BC_BASE_MAX = 99 -_POSIX2_BC_DIM_MAX = 2048 -_POSIX2_BC_SCALE_MAX = 99 -_POSIX2_BC_STRING_MAX = 1000 -_POSIX2_COLL_WEIGHTS_MAX = 2 -_POSIX2_EXPR_NEST_MAX = 32 -_POSIX2_LINE_MAX = 2048 -_POSIX2_RE_DUP_MAX = 255 -_POSIX2_CHARCLASS_NAME_MAX = 14 -BC_BASE_MAX = _POSIX2_BC_BASE_MAX -BC_DIM_MAX = _POSIX2_BC_DIM_MAX -BC_SCALE_MAX = _POSIX2_BC_SCALE_MAX -BC_STRING_MAX = _POSIX2_BC_STRING_MAX -COLL_WEIGHTS_MAX = 255 -EXPR_NEST_MAX = _POSIX2_EXPR_NEST_MAX -LINE_MAX = _POSIX2_LINE_MAX -CHARCLASS_NAME_MAX = 2048 -RE_DUP_MAX = (0x7fff) - -# Included from bits/xopen_lim.h -_XOPEN_LIM_H = 1 - -# Included from bits/stdio_lim.h -L_tmpnam = 20 -TMP_MAX = 238328 -FILENAME_MAX = 4096 -L_ctermid = 9 -L_cuserid = 9 -FOPEN_MAX = 16 -IOV_MAX = 1024 -_XOPEN_IOV_MAX = _POSIX_UIO_MAXIOV -NL_ARGMAX = _POSIX_ARG_MAX -NL_LANGMAX = _POSIX2_LINE_MAX -NL_MSGMAX = INT_MAX -NL_NMAX = INT_MAX -NL_SETMAX = INT_MAX -NL_TEXTMAX = INT_MAX -NZERO = 20 -WORD_BIT = 16 -WORD_BIT = 32 -WORD_BIT = 64 -WORD_BIT = 16 -WORD_BIT = 32 -WORD_BIT = 64 -WORD_BIT = 32 -LONG_BIT = 32 -LONG_BIT = 64 -LONG_BIT = 32 -LONG_BIT = 64 -LONG_BIT = 64 -LONG_BIT = 32 -from TYPES import * -PF_UNSPEC = 0 -PF_LOCAL = 1 -PF_UNIX = PF_LOCAL -PF_FILE = PF_LOCAL -PF_INET = 2 -PF_AX25 = 3 -PF_IPX = 4 -PF_APPLETALK = 5 -PF_NETROM = 6 -PF_BRIDGE = 7 -PF_ATMPVC = 8 -PF_X25 = 9 -PF_INET6 = 10 -PF_ROSE = 11 -PF_DECnet = 12 -PF_NETBEUI = 13 -PF_SECURITY = 14 -PF_KEY = 15 -PF_NETLINK = 16 -PF_ROUTE = PF_NETLINK -PF_PACKET = 17 -PF_ASH = 18 -PF_ECONET = 19 -PF_ATMSVC = 20 -PF_SNA = 22 -PF_IRDA = 23 -PF_PPPOX = 24 -PF_WANPIPE = 25 -PF_BLUETOOTH = 31 -PF_MAX = 32 -AF_UNSPEC = PF_UNSPEC -AF_LOCAL = PF_LOCAL -AF_UNIX = PF_UNIX -AF_FILE = PF_FILE -AF_INET = PF_INET -AF_AX25 = PF_AX25 -AF_IPX = PF_IPX -AF_APPLETALK = PF_APPLETALK -AF_NETROM = PF_NETROM -AF_BRIDGE = PF_BRIDGE -AF_ATMPVC = PF_ATMPVC -AF_X25 = PF_X25 -AF_INET6 = PF_INET6 -AF_ROSE = PF_ROSE -AF_DECnet = PF_DECnet -AF_NETBEUI = PF_NETBEUI -AF_SECURITY = PF_SECURITY -AF_KEY = PF_KEY -AF_NETLINK = PF_NETLINK -AF_ROUTE = PF_ROUTE -AF_PACKET = PF_PACKET -AF_ASH = PF_ASH -AF_ECONET = PF_ECONET -AF_ATMSVC = PF_ATMSVC -AF_SNA = PF_SNA -AF_IRDA = PF_IRDA -AF_PPPOX = PF_PPPOX -AF_WANPIPE = PF_WANPIPE -AF_BLUETOOTH = PF_BLUETOOTH -AF_MAX = PF_MAX -SOL_RAW = 255 -SOL_DECNET = 261 -SOL_X25 = 262 -SOL_PACKET = 263 -SOL_ATM = 264 -SOL_AAL = 265 -SOL_IRDA = 266 -SOMAXCONN = 128 - -# Included from bits/sockaddr.h -_BITS_SOCKADDR_H = 1 -def __SOCKADDR_COMMON(sa_prefix): return \ - -_SS_SIZE = 128 -def CMSG_FIRSTHDR(mhdr): return \ - - -# Included from asm/socket.h - -# Included from asm/sockios.h -FIOSETOWN = 0x8901 -SIOCSPGRP = 0x8902 -FIOGETOWN = 0x8903 -SIOCGPGRP = 0x8904 -SIOCATMARK = 0x8905 -SIOCGSTAMP = 0x8906 -SOL_SOCKET = 1 -SO_DEBUG = 1 -SO_REUSEADDR = 2 -SO_TYPE = 3 -SO_ERROR = 4 -SO_DONTROUTE = 5 -SO_BROADCAST = 6 -SO_SNDBUF = 7 -SO_RCVBUF = 8 -SO_KEEPALIVE = 9 -SO_OOBINLINE = 10 -SO_NO_CHECK = 11 -SO_PRIORITY = 12 -SO_LINGER = 13 -SO_BSDCOMPAT = 14 -SO_PASSCRED = 16 -SO_PEERCRED = 17 -SO_RCVLOWAT = 18 -SO_SNDLOWAT = 19 -SO_RCVTIMEO = 20 -SO_SNDTIMEO = 21 -SO_SECURITY_AUTHENTICATION = 22 -SO_SECURITY_ENCRYPTION_TRANSPORT = 23 -SO_SECURITY_ENCRYPTION_NETWORK = 24 -SO_BINDTODEVICE = 25 -SO_ATTACH_FILTER = 26 -SO_DETACH_FILTER = 27 -SO_PEERNAME = 28 -SO_TIMESTAMP = 29 -SCM_TIMESTAMP = SO_TIMESTAMP -SO_ACCEPTCONN = 30 -SOCK_STREAM = 1 -SOCK_DGRAM = 2 -SOCK_RAW = 3 -SOCK_RDM = 4 -SOCK_SEQPACKET = 5 -SOCK_PACKET = 10 -SOCK_MAX = (SOCK_PACKET+1) - -# Included from bits/in.h -IP_TOS = 1 -IP_TTL = 2 -IP_HDRINCL = 3 -IP_OPTIONS = 4 -IP_ROUTER_ALERT = 5 -IP_RECVOPTS = 6 -IP_RETOPTS = 7 -IP_PKTINFO = 8 -IP_PKTOPTIONS = 9 -IP_PMTUDISC = 10 -IP_MTU_DISCOVER = 10 -IP_RECVERR = 11 -IP_RECVTTL = 12 -IP_RECVTOS = 13 -IP_MULTICAST_IF = 32 -IP_MULTICAST_TTL = 33 -IP_MULTICAST_LOOP = 34 -IP_ADD_MEMBERSHIP = 35 -IP_DROP_MEMBERSHIP = 36 -IP_RECVRETOPTS = IP_RETOPTS -IP_PMTUDISC_DONT = 0 -IP_PMTUDISC_WANT = 1 -IP_PMTUDISC_DO = 2 -SOL_IP = 0 -IP_DEFAULT_MULTICAST_TTL = 1 -IP_DEFAULT_MULTICAST_LOOP = 1 -IP_MAX_MEMBERSHIPS = 20 -IPV6_ADDRFORM = 1 -IPV6_PKTINFO = 2 -IPV6_HOPOPTS = 3 -IPV6_DSTOPTS = 4 -IPV6_RTHDR = 5 -IPV6_PKTOPTIONS = 6 -IPV6_CHECKSUM = 7 -IPV6_HOPLIMIT = 8 -IPV6_NEXTHOP = 9 -IPV6_AUTHHDR = 10 -IPV6_UNICAST_HOPS = 16 -IPV6_MULTICAST_IF = 17 -IPV6_MULTICAST_HOPS = 18 -IPV6_MULTICAST_LOOP = 19 -IPV6_JOIN_GROUP = 20 -IPV6_LEAVE_GROUP = 21 -IPV6_ROUTER_ALERT = 22 -IPV6_MTU_DISCOVER = 23 -IPV6_MTU = 24 -IPV6_RECVERR = 25 -IPV6_RXHOPOPTS = IPV6_HOPOPTS -IPV6_RXDSTOPTS = IPV6_DSTOPTS -IPV6_ADD_MEMBERSHIP = IPV6_JOIN_GROUP -IPV6_DROP_MEMBERSHIP = IPV6_LEAVE_GROUP -IPV6_PMTUDISC_DONT = 0 -IPV6_PMTUDISC_WANT = 1 -IPV6_PMTUDISC_DO = 2 -SOL_IPV6 = 41 -SOL_ICMPV6 = 58 -IPV6_RTHDR_LOOSE = 0 -IPV6_RTHDR_STRICT = 1 -IPV6_RTHDR_TYPE_0 = 0 - -# Included from endian.h -_ENDIAN_H = 1 -__LITTLE_ENDIAN = 1234 -__BIG_ENDIAN = 4321 -__PDP_ENDIAN = 3412 - -# Included from bits/endian.h -__BYTE_ORDER = __LITTLE_ENDIAN -__FLOAT_WORD_ORDER = __BYTE_ORDER -LITTLE_ENDIAN = __LITTLE_ENDIAN -BIG_ENDIAN = __BIG_ENDIAN -PDP_ENDIAN = __PDP_ENDIAN -BYTE_ORDER = __BYTE_ORDER - -# Included from bits/byteswap.h -_BITS_BYTESWAP_H = 1 -def __bswap_constant_16(x): return \ - -def __bswap_16(x): return \ - -def __bswap_16(x): return __bswap_constant_16 (x) - -def __bswap_constant_32(x): return \ - -def __bswap_32(x): return \ - -def __bswap_32(x): return \ - -def __bswap_32(x): return __bswap_constant_32 (x) - -def __bswap_constant_64(x): return \ - -def __bswap_64(x): return \ - -def ntohl(x): return (x) - -def ntohs(x): return (x) - -def htonl(x): return (x) - -def htons(x): return (x) - -def ntohl(x): return __bswap_32 (x) - -def ntohs(x): return __bswap_16 (x) - -def htonl(x): return __bswap_32 (x) - -def htons(x): return __bswap_16 (x) - -def IN6_IS_ADDR_UNSPECIFIED(a): return \ - -def IN6_IS_ADDR_LOOPBACK(a): return \ - -def IN6_IS_ADDR_LINKLOCAL(a): return \ - -def IN6_IS_ADDR_SITELOCAL(a): return \ - -def IN6_IS_ADDR_V4MAPPED(a): return \ - -def IN6_IS_ADDR_V4COMPAT(a): return \ - -def IN6_IS_ADDR_MC_NODELOCAL(a): return \ - -def IN6_IS_ADDR_MC_LINKLOCAL(a): return \ - -def IN6_IS_ADDR_MC_SITELOCAL(a): return \ - -def IN6_IS_ADDR_MC_ORGLOCAL(a): return \ - -def IN6_IS_ADDR_MC_GLOBAL(a): return diff --git a/Lib/plat-linux/TYPES.py b/Lib/plat-linux/TYPES.py deleted file mode 100644 index e7a324b25a..0000000000 --- a/Lib/plat-linux/TYPES.py +++ /dev/null @@ -1,170 +0,0 @@ -# Generated by h2py from /usr/include/sys/types.h -_SYS_TYPES_H = 1 - -# Included from features.h -_FEATURES_H = 1 -__USE_ANSI = 1 -__FAVOR_BSD = 1 -_ISOC99_SOURCE = 1 -_POSIX_SOURCE = 1 -_POSIX_C_SOURCE = 199506 -_XOPEN_SOURCE = 600 -_XOPEN_SOURCE_EXTENDED = 1 -_LARGEFILE64_SOURCE = 1 -_BSD_SOURCE = 1 -_SVID_SOURCE = 1 -_BSD_SOURCE = 1 -_SVID_SOURCE = 1 -__USE_ISOC99 = 1 -_POSIX_SOURCE = 1 -_POSIX_C_SOURCE = 2 -_POSIX_C_SOURCE = 199506 -__USE_POSIX = 1 -__USE_POSIX2 = 1 -__USE_POSIX199309 = 1 -__USE_POSIX199506 = 1 -__USE_XOPEN = 1 -__USE_XOPEN_EXTENDED = 1 -__USE_UNIX98 = 1 -_LARGEFILE_SOURCE = 1 -__USE_XOPEN2K = 1 -__USE_ISOC99 = 1 -__USE_XOPEN_EXTENDED = 1 -__USE_LARGEFILE = 1 -__USE_LARGEFILE64 = 1 -__USE_FILE_OFFSET64 = 1 -__USE_MISC = 1 -__USE_BSD = 1 -__USE_SVID = 1 -__USE_GNU = 1 -__USE_REENTRANT = 1 -__STDC_IEC_559__ = 1 -__STDC_IEC_559_COMPLEX__ = 1 -__STDC_ISO_10646__ = 200009 -__GNU_LIBRARY__ = 6 -__GLIBC__ = 2 -__GLIBC_MINOR__ = 2 - -# Included from sys/cdefs.h -_SYS_CDEFS_H = 1 -def __PMT(args): return args - -def __P(args): return args - -def __PMT(args): return args - -def __STRING(x): return #x - -__flexarr = [] -__flexarr = [0] -__flexarr = [] -__flexarr = [1] -def __ASMNAME(cname): return __ASMNAME2 (__USER_LABEL_PREFIX__, cname) - -def __attribute__(xyz): return - -def __attribute_format_arg__(x): return __attribute__ ((__format_arg__ (x))) - -def __attribute_format_arg__(x): return - -__USE_LARGEFILE = 1 -__USE_LARGEFILE64 = 1 -__USE_EXTERN_INLINES = 1 - -# Included from gnu/stubs.h - -# Included from bits/types.h -_BITS_TYPES_H = 1 -__FD_SETSIZE = 1024 - -# Included from bits/pthreadtypes.h -_BITS_PTHREADTYPES_H = 1 - -# Included from bits/sched.h -SCHED_OTHER = 0 -SCHED_FIFO = 1 -SCHED_RR = 2 -CSIGNAL = 0x000000ff -CLONE_VM = 0x00000100 -CLONE_FS = 0x00000200 -CLONE_FILES = 0x00000400 -CLONE_SIGHAND = 0x00000800 -CLONE_PID = 0x00001000 -CLONE_PTRACE = 0x00002000 -CLONE_VFORK = 0x00004000 -__defined_schedparam = 1 - -# Included from time.h -_TIME_H = 1 - -# Included from bits/time.h -_BITS_TIME_H = 1 -CLOCKS_PER_SEC = 1000000 -CLOCK_REALTIME = 0 -CLOCK_PROCESS_CPUTIME_ID = 2 -CLOCK_THREAD_CPUTIME_ID = 3 -TIMER_ABSTIME = 1 -_STRUCT_TIMEVAL = 1 -CLK_TCK = CLOCKS_PER_SEC -__clock_t_defined = 1 -__time_t_defined = 1 -__clockid_t_defined = 1 -__timer_t_defined = 1 -__timespec_defined = 1 -def __isleap(year): return \ - -__BIT_TYPES_DEFINED__ = 1 - -# Included from endian.h -_ENDIAN_H = 1 -__LITTLE_ENDIAN = 1234 -__BIG_ENDIAN = 4321 -__PDP_ENDIAN = 3412 - -# Included from bits/endian.h -__BYTE_ORDER = __LITTLE_ENDIAN -__FLOAT_WORD_ORDER = __BYTE_ORDER -LITTLE_ENDIAN = __LITTLE_ENDIAN -BIG_ENDIAN = __BIG_ENDIAN -PDP_ENDIAN = __PDP_ENDIAN -BYTE_ORDER = __BYTE_ORDER - -# Included from sys/select.h -_SYS_SELECT_H = 1 - -# Included from bits/select.h -def __FD_ZERO(fdsp): return \ - -def __FD_ZERO(set): return \ - - -# Included from bits/sigset.h -_SIGSET_H_types = 1 -_SIGSET_H_fns = 1 -def __sigmask(sig): return \ - -def __sigemptyset(set): return \ - -def __sigfillset(set): return \ - -def __sigisemptyset(set): return \ - -def __FDELT(d): return ((d) / __NFDBITS) - -FD_SETSIZE = __FD_SETSIZE -def FD_ZERO(fdsetp): return __FD_ZERO (fdsetp) - - -# Included from sys/sysmacros.h -_SYS_SYSMACROS_H = 1 -def major(dev): return ((int)(((dev) >> 8) & 0xff)) - -def minor(dev): return ((int)((dev) & 0xff)) - -def major(dev): return (((dev).__val[1] >> 8) & 0xff) - -def minor(dev): return ((dev).__val[1] & 0xff) - -def major(dev): return (((dev).__val[0] >> 8) & 0xff) - -def minor(dev): return ((dev).__val[0] & 0xff) diff --git a/Lib/plat-linux/regen b/Lib/plat-linux/regen deleted file mode 100755 index 10633cbc9a..0000000000 --- a/Lib/plat-linux/regen +++ /dev/null @@ -1,33 +0,0 @@ -#! /bin/sh -case `uname` in -Linux*|GNU*) ;; -*) echo Probably not on a Linux system 1>&2 - exit 1;; -esac -if [ -z "$CC" ]; then - echo >&2 "$(basename $0): CC is not set" - exit 1 -fi -headers="sys/types.h netinet/in.h dlfcn.h" -incdirs="$(echo $($CC -v -E - < /dev/null 2>&1|awk '/^#include/, /^End of search/' | grep '^ '))" -if [ -z "$incdirs" ]; then - incdirs="/usr/include" -fi -for h in $headers; do - absh= - for d in $incdirs; do - if [ -f "$d/$h" ]; then - absh="$d/$h" - break - fi - done - if [ -n "$absh" ]; then - absheaders="$absheaders $absh" - else - echo >&2 "$(basename $0): header $h not found" - exit 1 - fi -done - -set -x -${H2PY:-h2py} -i '(u_long)' $absheaders diff --git a/Lib/plat-netbsd1/IN.py b/Lib/plat-netbsd1/IN.py deleted file mode 100644 index 474c51e6d9..0000000000 --- a/Lib/plat-netbsd1/IN.py +++ /dev/null @@ -1,56 +0,0 @@ -# Generated by h2py from /usr/include/netinet/in.h -IPPROTO_IP = 0 -IPPROTO_ICMP = 1 -IPPROTO_IGMP = 2 -IPPROTO_GGP = 3 -IPPROTO_IPIP = 4 -IPPROTO_TCP = 6 -IPPROTO_EGP = 8 -IPPROTO_PUP = 12 -IPPROTO_UDP = 17 -IPPROTO_IDP = 22 -IPPROTO_TP = 29 -IPPROTO_EON = 80 -IPPROTO_ENCAP = 98 -IPPROTO_RAW = 255 -IPPROTO_MAX = 256 -IPPORT_RESERVED = 1024 -IPPORT_USERRESERVED = 5000 -def __IPADDR(x): return ((u_int32_t)(x)) - -IN_CLASSA_NSHIFT = 24 -IN_CLASSA_MAX = 128 -IN_CLASSB_NSHIFT = 16 -IN_CLASSB_MAX = 65536 -IN_CLASSC_NSHIFT = 8 -IN_CLASSD_NSHIFT = 28 -def IN_MULTICAST(i): return IN_CLASSD(i) - -IN_LOOPBACKNET = 127 -IP_OPTIONS = 1 -IP_HDRINCL = 2 -IP_TOS = 3 -IP_TTL = 4 -IP_RECVOPTS = 5 -IP_RECVRETOPTS = 6 -IP_RECVDSTADDR = 7 -IP_RETOPTS = 8 -IP_MULTICAST_IF = 9 -IP_MULTICAST_TTL = 10 -IP_MULTICAST_LOOP = 11 -IP_ADD_MEMBERSHIP = 12 -IP_DROP_MEMBERSHIP = 13 -IP_RECVIF = 20 -IP_DEFAULT_MULTICAST_TTL = 1 -IP_DEFAULT_MULTICAST_LOOP = 1 -IP_MAX_MEMBERSHIPS = 20 -IPPROTO_MAXID = (IPPROTO_IDP + 1) -IPCTL_FORWARDING = 1 -IPCTL_SENDREDIRECTS = 2 -IPCTL_DEFTTL = 3 -IPCTL_DEFMTU = 4 -IPCTL_FORWSRCRT = 5 -IPCTL_DIRECTEDBCAST = 6 -IPCTL_ALLOWSRCRT = 7 -IPCTL_MAXID = 8 -def in_nullhost(x): return ((x).s_addr == INADDR_ANY) diff --git a/Lib/plat-netbsd1/regen b/Lib/plat-netbsd1/regen deleted file mode 100755 index 8aa6898c6a..0000000000 --- a/Lib/plat-netbsd1/regen +++ /dev/null @@ -1,3 +0,0 @@ -#! /bin/sh -set -v -python ../../Tools/scripts/h2py.py -i '(u_long)' /usr/include/netinet/in.h diff --git a/Lib/plat-next3/regen b/Lib/plat-next3/regen deleted file mode 100755 index 7a036135a3..0000000000 --- a/Lib/plat-next3/regen +++ /dev/null @@ -1,6 +0,0 @@ -#! /bin/sh -set -v -INCLUDE="/NextDeveloper/Headers;/NextDeveloper/Headers/ansi;/NextDeveloper/Headers/bsd" -export INCLUDE - -python ../../Tools/scripts/h2py.py -i '(u_long)' /usr/include/bsd/netinet/in.h diff --git a/Lib/plat-sunos5/CDIO.py b/Lib/plat-sunos5/CDIO.py deleted file mode 100644 index d766b50264..0000000000 --- a/Lib/plat-sunos5/CDIO.py +++ /dev/null @@ -1,73 +0,0 @@ -# Generated by h2py from /usr/include/sys/cdio.h -CDROM_LBA = 0x01 -CDROM_MSF = 0x02 -CDROM_DATA_TRACK = 0x04 -CDROM_LEADOUT = 0xAA -CDROM_AUDIO_INVALID = 0x00 -CDROM_AUDIO_PLAY = 0x11 -CDROM_AUDIO_PAUSED = 0x12 -CDROM_AUDIO_COMPLETED = 0x13 -CDROM_AUDIO_ERROR = 0x14 -CDROM_AUDIO_NO_STATUS = 0x15 -CDROM_DA_NO_SUBCODE = 0x00 -CDROM_DA_SUBQ = 0x01 -CDROM_DA_ALL_SUBCODE = 0x02 -CDROM_DA_SUBCODE_ONLY = 0x03 -CDROM_XA_DATA = 0x00 -CDROM_XA_SECTOR_DATA = 0x01 -CDROM_XA_DATA_W_ERROR = 0x02 -CDROM_BLK_512 = 512 -CDROM_BLK_1024 = 1024 -CDROM_BLK_2048 = 2048 -CDROM_BLK_2056 = 2056 -CDROM_BLK_2336 = 2336 -CDROM_BLK_2340 = 2340 -CDROM_BLK_2352 = 2352 -CDROM_BLK_2368 = 2368 -CDROM_BLK_2448 = 2448 -CDROM_BLK_2646 = 2646 -CDROM_BLK_2647 = 2647 -CDROM_BLK_SUBCODE = 96 -CDROM_NORMAL_SPEED = 0x00 -CDROM_DOUBLE_SPEED = 0x01 -CDROM_QUAD_SPEED = 0x03 -CDROM_TWELVE_SPEED = 0x0C -CDROM_MAXIMUM_SPEED = 0xff -CDIOC = (0x04 << 8) -CDROMPAUSE = (CDIOC|151) -CDROMRESUME = (CDIOC|152) -CDROMPLAYMSF = (CDIOC|153) -CDROMPLAYTRKIND = (CDIOC|154) -CDROMREADTOCHDR = (CDIOC|155) -CDROMREADTOCENTRY = (CDIOC|156) -CDROMSTOP = (CDIOC|157) -CDROMSTART = (CDIOC|158) -CDROMEJECT = (CDIOC|159) -CDROMVOLCTRL = (CDIOC|160) -CDROMSUBCHNL = (CDIOC|161) -CDROMREADMODE2 = (CDIOC|162) -CDROMREADMODE1 = (CDIOC|163) -CDROMREADOFFSET = (CDIOC|164) -CDROMGBLKMODE = (CDIOC|165) -CDROMSBLKMODE = (CDIOC|166) -CDROMCDDA = (CDIOC|167) -CDROMCDXA = (CDIOC|168) -CDROMSUBCODE = (CDIOC|169) -CDROMGDRVSPEED = (CDIOC|170) -CDROMSDRVSPEED = (CDIOC|171) -SCMD_READ_TOC = 0x43 -SCMD_PLAYAUDIO_MSF = 0x47 -SCMD_PLAYAUDIO_TI = 0x48 -SCMD_PAUSE_RESUME = 0x4B -SCMD_READ_SUBCHANNEL = 0x42 -SCMD_PLAYAUDIO10 = 0x45 -SCMD_PLAYTRACK_REL10 = 0x49 -SCMD_READ_HEADER = 0x44 -SCMD_PLAYAUDIO12 = 0xA5 -SCMD_PLAYTRACK_REL12 = 0xA9 -SCMD_CD_PLAYBACK_CONTROL = 0xC9 -SCMD_CD_PLAYBACK_STATUS = 0xC4 -SCMD_READ_CDDA = 0xD8 -SCMD_READ_CDXA = 0xDB -SCMD_READ_ALL_SUBCODES = 0xDF -CDROM_MODE2_SIZE = 2336 diff --git a/Lib/plat-sunos5/DLFCN.py b/Lib/plat-sunos5/DLFCN.py deleted file mode 100644 index f492350674..0000000000 --- a/Lib/plat-sunos5/DLFCN.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by h2py from /usr/include/dlfcn.h -from TYPES import * -RTLD_LAZY = 0x00001 -RTLD_NOW = 0x00002 -RTLD_NOLOAD = 0x00004 -RTLD_GLOBAL = 0x00100 -RTLD_LOCAL = 0x00000 -RTLD_PARENT = 0x00200 -RTLD_GROUP = 0x00400 -RTLD_WORLD = 0x00800 -RTLD_NODELETE = 0x01000 -RTLD_CONFGEN = 0x10000 -RTLD_REL_RELATIVE = 0x00001 -RTLD_REL_EXEC = 0x00002 -RTLD_REL_DEPENDS = 0x00004 -RTLD_REL_PRELOAD = 0x00008 -RTLD_REL_SELF = 0x00010 -RTLD_REL_WEAK = 0x00020 -RTLD_REL_ALL = 0x00fff -RTLD_MEMORY = 0x01000 -RTLD_STRIP = 0x02000 -RTLD_NOHEAP = 0x04000 -RTLD_CONFSET = 0x10000 -RTLD_DI_LMID = 1 -RTLD_DI_LINKMAP = 2 -RTLD_DI_CONFIGADDR = 3 -RTLD_DI_MAX = 3 diff --git a/Lib/plat-sunos5/IN.py b/Lib/plat-sunos5/IN.py deleted file mode 100755 index 9572ead8a2..0000000000 --- a/Lib/plat-sunos5/IN.py +++ /dev/null @@ -1,1421 +0,0 @@ -# Generated by h2py from /usr/include/netinet/in.h - -# Included from sys/feature_tests.h - -# Included from sys/isa_defs.h -_CHAR_ALIGNMENT = 1 -_SHORT_ALIGNMENT = 2 -_INT_ALIGNMENT = 4 -_LONG_ALIGNMENT = 8 -_LONG_LONG_ALIGNMENT = 8 -_DOUBLE_ALIGNMENT = 8 -_LONG_DOUBLE_ALIGNMENT = 16 -_POINTER_ALIGNMENT = 8 -_MAX_ALIGNMENT = 16 -_ALIGNMENT_REQUIRED = 1 -_CHAR_ALIGNMENT = 1 -_SHORT_ALIGNMENT = 2 -_INT_ALIGNMENT = 4 -_LONG_ALIGNMENT = 4 -_LONG_LONG_ALIGNMENT = 4 -_DOUBLE_ALIGNMENT = 4 -_LONG_DOUBLE_ALIGNMENT = 4 -_POINTER_ALIGNMENT = 4 -_MAX_ALIGNMENT = 4 -_ALIGNMENT_REQUIRED = 0 -_CHAR_ALIGNMENT = 1 -_SHORT_ALIGNMENT = 2 -_INT_ALIGNMENT = 4 -_LONG_LONG_ALIGNMENT = 8 -_DOUBLE_ALIGNMENT = 8 -_ALIGNMENT_REQUIRED = 1 -_LONG_ALIGNMENT = 4 -_LONG_DOUBLE_ALIGNMENT = 8 -_POINTER_ALIGNMENT = 4 -_MAX_ALIGNMENT = 8 -_LONG_ALIGNMENT = 8 -_LONG_DOUBLE_ALIGNMENT = 16 -_POINTER_ALIGNMENT = 8 -_MAX_ALIGNMENT = 16 -_POSIX_C_SOURCE = 1 -_LARGEFILE64_SOURCE = 1 -_LARGEFILE_SOURCE = 1 -_FILE_OFFSET_BITS = 64 -_FILE_OFFSET_BITS = 32 -_POSIX_C_SOURCE = 199506 -_POSIX_PTHREAD_SEMANTICS = 1 -_XOPEN_VERSION = 500 -_XOPEN_VERSION = 4 -_XOPEN_VERSION = 3 -from TYPES import * - -# Included from sys/stream.h - -# Included from sys/vnode.h -from TYPES import * - -# Included from sys/t_lock.h - -# Included from sys/machlock.h -from TYPES import * -LOCK_HELD_VALUE = 0xff -def SPIN_LOCK(pl): return ((pl) > ipltospl(LOCK_LEVEL)) - -def LOCK_SAMPLE_INTERVAL(i): return (((i) & 0xff) == 0) - -CLOCK_LEVEL = 10 -LOCK_LEVEL = 10 -DISP_LEVEL = (LOCK_LEVEL + 1) -PTR24_LSB = 5 -PTR24_MSB = (PTR24_LSB + 24) -PTR24_ALIGN = 32 -PTR24_BASE = 0xe0000000 - -# Included from sys/param.h -from TYPES import * -_POSIX_VDISABLE = 0 -MAX_INPUT = 512 -MAX_CANON = 256 -UID_NOBODY = 60001 -GID_NOBODY = UID_NOBODY -UID_NOACCESS = 60002 -MAX_TASKID = 999999 -MAX_MAXPID = 999999 -DEFAULT_MAXPID = 999999 -DEFAULT_JUMPPID = 100000 -DEFAULT_MAXPID = 30000 -DEFAULT_JUMPPID = 0 -MAXUID = 2147483647 -MAXPROJID = MAXUID -MAXLINK = 32767 -NMOUNT = 40 -CANBSIZ = 256 -NOFILE = 20 -NGROUPS_UMIN = 0 -NGROUPS_UMAX = 32 -NGROUPS_MAX_DEFAULT = 16 -NZERO = 20 -NULL = 0 -NULL = 0 -CMASK = 0o22 -CDLIMIT = (1<<11) -NBPS = 0x20000 -NBPSCTR = 512 -UBSIZE = 512 -SCTRSHFT = 9 -SYSNAME = 9 -PREMOTE = 39 -MAXPATHLEN = 1024 -MAXSYMLINKS = 20 -MAXNAMELEN = 256 -NADDR = 13 -PIPE_BUF = 5120 -PIPE_MAX = 5120 -NBBY = 8 -MAXBSIZE = 8192 -DEV_BSIZE = 512 -DEV_BSHIFT = 9 -MAXFRAG = 8 -MAXOFF32_T = 0x7fffffff -MAXOFF_T = 0x7fffffffffffffff -MAXOFFSET_T = 0x7fffffffffffffff -MAXOFF_T = 0x7fffffff -MAXOFFSET_T = 0x7fffffff -def btodb(bytes): return \ - -def dbtob(db): return \ - -def lbtodb(bytes): return \ - -def ldbtob(db): return \ - -NCARGS32 = 0x100000 -NCARGS64 = 0x200000 -NCARGS = NCARGS64 -NCARGS = NCARGS32 -FSHIFT = 8 -FSCALE = (1<> MMU_PAGESHIFT) - -def mmu_btopr(x): return ((((x) + MMU_PAGEOFFSET) >> MMU_PAGESHIFT)) - -def mmu_ptod(x): return ((x) << (MMU_PAGESHIFT - DEV_BSHIFT)) - -def ptod(x): return ((x) << (PAGESHIFT - DEV_BSHIFT)) - -def ptob(x): return ((x) << PAGESHIFT) - -def btop(x): return (((x) >> PAGESHIFT)) - -def btopr(x): return ((((x) + PAGEOFFSET) >> PAGESHIFT)) - -def dtop(DD): return (((DD) + NDPP - 1) >> (PAGESHIFT - DEV_BSHIFT)) - -def dtopt(DD): return ((DD) >> (PAGESHIFT - DEV_BSHIFT)) - -_AIO_LISTIO_MAX = (4096) -_AIO_MAX = (-1) -_MQ_OPEN_MAX = (32) -_MQ_PRIO_MAX = (32) -_SEM_NSEMS_MAX = INT_MAX -_SEM_VALUE_MAX = INT_MAX - -# Included from sys/unistd.h -_CS_PATH = 65 -_CS_LFS_CFLAGS = 68 -_CS_LFS_LDFLAGS = 69 -_CS_LFS_LIBS = 70 -_CS_LFS_LINTFLAGS = 71 -_CS_LFS64_CFLAGS = 72 -_CS_LFS64_LDFLAGS = 73 -_CS_LFS64_LIBS = 74 -_CS_LFS64_LINTFLAGS = 75 -_CS_XBS5_ILP32_OFF32_CFLAGS = 700 -_CS_XBS5_ILP32_OFF32_LDFLAGS = 701 -_CS_XBS5_ILP32_OFF32_LIBS = 702 -_CS_XBS5_ILP32_OFF32_LINTFLAGS = 703 -_CS_XBS5_ILP32_OFFBIG_CFLAGS = 705 -_CS_XBS5_ILP32_OFFBIG_LDFLAGS = 706 -_CS_XBS5_ILP32_OFFBIG_LIBS = 707 -_CS_XBS5_ILP32_OFFBIG_LINTFLAGS = 708 -_CS_XBS5_LP64_OFF64_CFLAGS = 709 -_CS_XBS5_LP64_OFF64_LDFLAGS = 710 -_CS_XBS5_LP64_OFF64_LIBS = 711 -_CS_XBS5_LP64_OFF64_LINTFLAGS = 712 -_CS_XBS5_LPBIG_OFFBIG_CFLAGS = 713 -_CS_XBS5_LPBIG_OFFBIG_LDFLAGS = 714 -_CS_XBS5_LPBIG_OFFBIG_LIBS = 715 -_CS_XBS5_LPBIG_OFFBIG_LINTFLAGS = 716 -_SC_ARG_MAX = 1 -_SC_CHILD_MAX = 2 -_SC_CLK_TCK = 3 -_SC_NGROUPS_MAX = 4 -_SC_OPEN_MAX = 5 -_SC_JOB_CONTROL = 6 -_SC_SAVED_IDS = 7 -_SC_VERSION = 8 -_SC_PASS_MAX = 9 -_SC_LOGNAME_MAX = 10 -_SC_PAGESIZE = 11 -_SC_XOPEN_VERSION = 12 -_SC_NPROCESSORS_CONF = 14 -_SC_NPROCESSORS_ONLN = 15 -_SC_STREAM_MAX = 16 -_SC_TZNAME_MAX = 17 -_SC_AIO_LISTIO_MAX = 18 -_SC_AIO_MAX = 19 -_SC_AIO_PRIO_DELTA_MAX = 20 -_SC_ASYNCHRONOUS_IO = 21 -_SC_DELAYTIMER_MAX = 22 -_SC_FSYNC = 23 -_SC_MAPPED_FILES = 24 -_SC_MEMLOCK = 25 -_SC_MEMLOCK_RANGE = 26 -_SC_MEMORY_PROTECTION = 27 -_SC_MESSAGE_PASSING = 28 -_SC_MQ_OPEN_MAX = 29 -_SC_MQ_PRIO_MAX = 30 -_SC_PRIORITIZED_IO = 31 -_SC_PRIORITY_SCHEDULING = 32 -_SC_REALTIME_SIGNALS = 33 -_SC_RTSIG_MAX = 34 -_SC_SEMAPHORES = 35 -_SC_SEM_NSEMS_MAX = 36 -_SC_SEM_VALUE_MAX = 37 -_SC_SHARED_MEMORY_OBJECTS = 38 -_SC_SIGQUEUE_MAX = 39 -_SC_SIGRT_MIN = 40 -_SC_SIGRT_MAX = 41 -_SC_SYNCHRONIZED_IO = 42 -_SC_TIMERS = 43 -_SC_TIMER_MAX = 44 -_SC_2_C_BIND = 45 -_SC_2_C_DEV = 46 -_SC_2_C_VERSION = 47 -_SC_2_FORT_DEV = 48 -_SC_2_FORT_RUN = 49 -_SC_2_LOCALEDEF = 50 -_SC_2_SW_DEV = 51 -_SC_2_UPE = 52 -_SC_2_VERSION = 53 -_SC_BC_BASE_MAX = 54 -_SC_BC_DIM_MAX = 55 -_SC_BC_SCALE_MAX = 56 -_SC_BC_STRING_MAX = 57 -_SC_COLL_WEIGHTS_MAX = 58 -_SC_EXPR_NEST_MAX = 59 -_SC_LINE_MAX = 60 -_SC_RE_DUP_MAX = 61 -_SC_XOPEN_CRYPT = 62 -_SC_XOPEN_ENH_I18N = 63 -_SC_XOPEN_SHM = 64 -_SC_2_CHAR_TERM = 66 -_SC_XOPEN_XCU_VERSION = 67 -_SC_ATEXIT_MAX = 76 -_SC_IOV_MAX = 77 -_SC_XOPEN_UNIX = 78 -_SC_PAGE_SIZE = _SC_PAGESIZE -_SC_T_IOV_MAX = 79 -_SC_PHYS_PAGES = 500 -_SC_AVPHYS_PAGES = 501 -_SC_COHER_BLKSZ = 503 -_SC_SPLIT_CACHE = 504 -_SC_ICACHE_SZ = 505 -_SC_DCACHE_SZ = 506 -_SC_ICACHE_LINESZ = 507 -_SC_DCACHE_LINESZ = 508 -_SC_ICACHE_BLKSZ = 509 -_SC_DCACHE_BLKSZ = 510 -_SC_DCACHE_TBLKSZ = 511 -_SC_ICACHE_ASSOC = 512 -_SC_DCACHE_ASSOC = 513 -_SC_MAXPID = 514 -_SC_STACK_PROT = 515 -_SC_THREAD_DESTRUCTOR_ITERATIONS = 568 -_SC_GETGR_R_SIZE_MAX = 569 -_SC_GETPW_R_SIZE_MAX = 570 -_SC_LOGIN_NAME_MAX = 571 -_SC_THREAD_KEYS_MAX = 572 -_SC_THREAD_STACK_MIN = 573 -_SC_THREAD_THREADS_MAX = 574 -_SC_TTY_NAME_MAX = 575 -_SC_THREADS = 576 -_SC_THREAD_ATTR_STACKADDR = 577 -_SC_THREAD_ATTR_STACKSIZE = 578 -_SC_THREAD_PRIORITY_SCHEDULING = 579 -_SC_THREAD_PRIO_INHERIT = 580 -_SC_THREAD_PRIO_PROTECT = 581 -_SC_THREAD_PROCESS_SHARED = 582 -_SC_THREAD_SAFE_FUNCTIONS = 583 -_SC_XOPEN_LEGACY = 717 -_SC_XOPEN_REALTIME = 718 -_SC_XOPEN_REALTIME_THREADS = 719 -_SC_XBS5_ILP32_OFF32 = 720 -_SC_XBS5_ILP32_OFFBIG = 721 -_SC_XBS5_LP64_OFF64 = 722 -_SC_XBS5_LPBIG_OFFBIG = 723 -_PC_LINK_MAX = 1 -_PC_MAX_CANON = 2 -_PC_MAX_INPUT = 3 -_PC_NAME_MAX = 4 -_PC_PATH_MAX = 5 -_PC_PIPE_BUF = 6 -_PC_NO_TRUNC = 7 -_PC_VDISABLE = 8 -_PC_CHOWN_RESTRICTED = 9 -_PC_ASYNC_IO = 10 -_PC_PRIO_IO = 11 -_PC_SYNC_IO = 12 -_PC_FILESIZEBITS = 67 -_PC_LAST = 67 -_POSIX_VERSION = 199506 -_POSIX2_VERSION = 199209 -_POSIX2_C_VERSION = 199209 -_XOPEN_XCU_VERSION = 4 -_XOPEN_REALTIME = 1 -_XOPEN_ENH_I18N = 1 -_XOPEN_SHM = 1 -_POSIX2_C_BIND = 1 -_POSIX2_CHAR_TERM = 1 -_POSIX2_LOCALEDEF = 1 -_POSIX2_C_DEV = 1 -_POSIX2_SW_DEV = 1 -_POSIX2_UPE = 1 - -# Included from sys/mutex.h -from TYPES import * -def MUTEX_HELD(x): return (mutex_owned(x)) - - -# Included from sys/rwlock.h -from TYPES import * -def RW_READ_HELD(x): return (rw_read_held((x))) - -def RW_WRITE_HELD(x): return (rw_write_held((x))) - -def RW_LOCK_HELD(x): return (rw_lock_held((x))) - -def RW_ISWRITER(x): return (rw_iswriter(x)) - - -# Included from sys/semaphore.h - -# Included from sys/thread.h -from TYPES import * - -# Included from sys/klwp.h -from TYPES import * - -# Included from sys/condvar.h -from TYPES import * - -# Included from sys/time.h - -# Included from sys/types32.h - -# Included from sys/int_types.h -TIME32_MAX = INT32_MAX -TIME32_MIN = INT32_MIN -def TIMEVAL_OVERFLOW(tv): return \ - -from TYPES import * -DST_NONE = 0 -DST_USA = 1 -DST_AUST = 2 -DST_WET = 3 -DST_MET = 4 -DST_EET = 5 -DST_CAN = 6 -DST_GB = 7 -DST_RUM = 8 -DST_TUR = 9 -DST_AUSTALT = 10 -ITIMER_REAL = 0 -ITIMER_VIRTUAL = 1 -ITIMER_PROF = 2 -ITIMER_REALPROF = 3 -def ITIMERVAL_OVERFLOW(itv): return \ - -SEC = 1 -MILLISEC = 1000 -MICROSEC = 1000000 -NANOSEC = 1000000000 - -# Included from sys/time_impl.h -def TIMESPEC_OVERFLOW(ts): return \ - -def ITIMERSPEC_OVERFLOW(it): return \ - -__CLOCK_REALTIME0 = 0 -CLOCK_VIRTUAL = 1 -CLOCK_PROF = 2 -__CLOCK_REALTIME3 = 3 -CLOCK_HIGHRES = 4 -CLOCK_MAX = 5 -CLOCK_REALTIME = __CLOCK_REALTIME3 -CLOCK_REALTIME = __CLOCK_REALTIME0 -TIMER_RELTIME = 0x0 -TIMER_ABSTIME = 0x1 -def TICK_TO_SEC(tick): return ((tick) / hz) - -def SEC_TO_TICK(sec): return ((sec) * hz) - -def TICK_TO_MSEC(tick): return \ - -def MSEC_TO_TICK(msec): return \ - -def MSEC_TO_TICK_ROUNDUP(msec): return \ - -def TICK_TO_USEC(tick): return ((tick) * usec_per_tick) - -def USEC_TO_TICK(usec): return ((usec) / usec_per_tick) - -def USEC_TO_TICK_ROUNDUP(usec): return \ - -def TICK_TO_NSEC(tick): return ((tick) * nsec_per_tick) - -def NSEC_TO_TICK(nsec): return ((nsec) / nsec_per_tick) - -def NSEC_TO_TICK_ROUNDUP(nsec): return \ - -def TIMEVAL_TO_TICK(tvp): return \ - -def TIMESTRUC_TO_TICK(tsp): return \ - - -# Included from time.h -from TYPES import * - -# Included from iso/time_iso.h -NULL = 0 -NULL = 0 -CLOCKS_PER_SEC = 1000000 - -# Included from sys/select.h -FD_SETSIZE = 65536 -FD_SETSIZE = 1024 -_NBBY = 8 -NBBY = _NBBY -def FD_ZERO(p): return bzero((p), sizeof (*(p))) - - -# Included from sys/signal.h - -# Included from sys/iso/signal_iso.h -SIGHUP = 1 -SIGINT = 2 -SIGQUIT = 3 -SIGILL = 4 -SIGTRAP = 5 -SIGIOT = 6 -SIGABRT = 6 -SIGEMT = 7 -SIGFPE = 8 -SIGKILL = 9 -SIGBUS = 10 -SIGSEGV = 11 -SIGSYS = 12 -SIGPIPE = 13 -SIGALRM = 14 -SIGTERM = 15 -SIGUSR1 = 16 -SIGUSR2 = 17 -SIGCLD = 18 -SIGCHLD = 18 -SIGPWR = 19 -SIGWINCH = 20 -SIGURG = 21 -SIGPOLL = 22 -SIGIO = SIGPOLL -SIGSTOP = 23 -SIGTSTP = 24 -SIGCONT = 25 -SIGTTIN = 26 -SIGTTOU = 27 -SIGVTALRM = 28 -SIGPROF = 29 -SIGXCPU = 30 -SIGXFSZ = 31 -SIGWAITING = 32 -SIGLWP = 33 -SIGFREEZE = 34 -SIGTHAW = 35 -SIGCANCEL = 36 -SIGLOST = 37 -_SIGRTMIN = 38 -_SIGRTMAX = 45 -SIG_BLOCK = 1 -SIG_UNBLOCK = 2 -SIG_SETMASK = 3 -SIGNO_MASK = 0xFF -SIGDEFER = 0x100 -SIGHOLD = 0x200 -SIGRELSE = 0x400 -SIGIGNORE = 0x800 -SIGPAUSE = 0x1000 - -# Included from sys/siginfo.h -from TYPES import * -SIGEV_NONE = 1 -SIGEV_SIGNAL = 2 -SIGEV_THREAD = 3 -SI_NOINFO = 32767 -SI_USER = 0 -SI_LWP = (-1) -SI_QUEUE = (-2) -SI_TIMER = (-3) -SI_ASYNCIO = (-4) -SI_MESGQ = (-5) - -# Included from sys/machsig.h -ILL_ILLOPC = 1 -ILL_ILLOPN = 2 -ILL_ILLADR = 3 -ILL_ILLTRP = 4 -ILL_PRVOPC = 5 -ILL_PRVREG = 6 -ILL_COPROC = 7 -ILL_BADSTK = 8 -NSIGILL = 8 -EMT_TAGOVF = 1 -EMT_CPCOVF = 2 -NSIGEMT = 2 -FPE_INTDIV = 1 -FPE_INTOVF = 2 -FPE_FLTDIV = 3 -FPE_FLTOVF = 4 -FPE_FLTUND = 5 -FPE_FLTRES = 6 -FPE_FLTINV = 7 -FPE_FLTSUB = 8 -NSIGFPE = 8 -SEGV_MAPERR = 1 -SEGV_ACCERR = 2 -NSIGSEGV = 2 -BUS_ADRALN = 1 -BUS_ADRERR = 2 -BUS_OBJERR = 3 -NSIGBUS = 3 -TRAP_BRKPT = 1 -TRAP_TRACE = 2 -TRAP_RWATCH = 3 -TRAP_WWATCH = 4 -TRAP_XWATCH = 5 -NSIGTRAP = 5 -CLD_EXITED = 1 -CLD_KILLED = 2 -CLD_DUMPED = 3 -CLD_TRAPPED = 4 -CLD_STOPPED = 5 -CLD_CONTINUED = 6 -NSIGCLD = 6 -POLL_IN = 1 -POLL_OUT = 2 -POLL_MSG = 3 -POLL_ERR = 4 -POLL_PRI = 5 -POLL_HUP = 6 -NSIGPOLL = 6 -PROF_SIG = 1 -NSIGPROF = 1 -SI_MAXSZ = 256 -SI_MAXSZ = 128 - -# Included from sys/time_std_impl.h -from TYPES import * -SI32_MAXSZ = 128 -def SI_CANQUEUE(c): return ((c) <= SI_QUEUE) - -SA_NOCLDSTOP = 0x00020000 -SA_ONSTACK = 0x00000001 -SA_RESETHAND = 0x00000002 -SA_RESTART = 0x00000004 -SA_SIGINFO = 0x00000008 -SA_NODEFER = 0x00000010 -SA_NOCLDWAIT = 0x00010000 -SA_WAITSIG = 0x00010000 -NSIG = 46 -MAXSIG = 45 -S_SIGNAL = 1 -S_SIGSET = 2 -S_SIGACTION = 3 -S_NONE = 4 -MINSIGSTKSZ = 2048 -SIGSTKSZ = 8192 -SS_ONSTACK = 0x00000001 -SS_DISABLE = 0x00000002 -SN_PROC = 1 -SN_CANCEL = 2 -SN_SEND = 3 - -# Included from sys/ucontext.h -from TYPES import * - -# Included from sys/regset.h -REG_CCR = (0) -REG_PSR = (0) -REG_PSR = (0) -REG_PC = (1) -REG_nPC = (2) -REG_Y = (3) -REG_G1 = (4) -REG_G2 = (5) -REG_G3 = (6) -REG_G4 = (7) -REG_G5 = (8) -REG_G6 = (9) -REG_G7 = (10) -REG_O0 = (11) -REG_O1 = (12) -REG_O2 = (13) -REG_O3 = (14) -REG_O4 = (15) -REG_O5 = (16) -REG_O6 = (17) -REG_O7 = (18) -REG_ASI = (19) -REG_FPRS = (20) -REG_PS = REG_PSR -REG_SP = REG_O6 -REG_R0 = REG_O0 -REG_R1 = REG_O1 -_NGREG = 21 -_NGREG = 19 -NGREG = _NGREG -_NGREG32 = 19 -_NGREG64 = 21 -SPARC_MAXREGWINDOW = 31 -MAXFPQ = 16 -XRS_ID = 0x78727300 - -# Included from v7/sys/privregs.h - -# Included from v7/sys/psr.h -PSR_CWP = 0x0000001F -PSR_ET = 0x00000020 -PSR_PS = 0x00000040 -PSR_S = 0x00000080 -PSR_PIL = 0x00000F00 -PSR_EF = 0x00001000 -PSR_EC = 0x00002000 -PSR_RSV = 0x000FC000 -PSR_ICC = 0x00F00000 -PSR_C = 0x00100000 -PSR_V = 0x00200000 -PSR_Z = 0x00400000 -PSR_N = 0x00800000 -PSR_VER = 0x0F000000 -PSR_IMPL = 0xF0000000 -PSL_ALLCC = PSR_ICC -PSL_USER = (PSR_S) -PSL_USERMASK = (PSR_ICC) -PSL_UBITS = (PSR_ICC|PSR_EF) -def USERMODE(ps): return (((ps) & PSR_PS) == 0) - - -# Included from sys/fsr.h -FSR_CEXC = 0x0000001f -FSR_AEXC = 0x000003e0 -FSR_FCC = 0x00000c00 -FSR_PR = 0x00001000 -FSR_QNE = 0x00002000 -FSR_FTT = 0x0001c000 -FSR_VER = 0x000e0000 -FSR_TEM = 0x0f800000 -FSR_RP = 0x30000000 -FSR_RD = 0xc0000000 -FSR_VER_SHIFT = 17 -FSR_FCC1 = 0x00000003 -FSR_FCC2 = 0x0000000C -FSR_FCC3 = 0x00000030 -FSR_CEXC_NX = 0x00000001 -FSR_CEXC_DZ = 0x00000002 -FSR_CEXC_UF = 0x00000004 -FSR_CEXC_OF = 0x00000008 -FSR_CEXC_NV = 0x00000010 -FSR_AEXC_NX = (0x1 << 5) -FSR_AEXC_DZ = (0x2 << 5) -FSR_AEXC_UF = (0x4 << 5) -FSR_AEXC_OF = (0x8 << 5) -FSR_AEXC_NV = (0x10 << 5) -FTT_NONE = 0 -FTT_IEEE = 1 -FTT_UNFIN = 2 -FTT_UNIMP = 3 -FTT_SEQ = 4 -FTT_ALIGN = 5 -FTT_DFAULT = 6 -FSR_FTT_SHIFT = 14 -FSR_FTT_IEEE = (FTT_IEEE << FSR_FTT_SHIFT) -FSR_FTT_UNFIN = (FTT_UNFIN << FSR_FTT_SHIFT) -FSR_FTT_UNIMP = (FTT_UNIMP << FSR_FTT_SHIFT) -FSR_FTT_SEQ = (FTT_SEQ << FSR_FTT_SHIFT) -FSR_FTT_ALIGN = (FTT_ALIGN << FSR_FTT_SHIFT) -FSR_FTT_DFAULT = (FTT_DFAULT << FSR_FTT_SHIFT) -FSR_TEM_NX = (0x1 << 23) -FSR_TEM_DZ = (0x2 << 23) -FSR_TEM_UF = (0x4 << 23) -FSR_TEM_OF = (0x8 << 23) -FSR_TEM_NV = (0x10 << 23) -RP_DBLEXT = 0 -RP_SINGLE = 1 -RP_DOUBLE = 2 -RP_RESERVED = 3 -RD_NEAR = 0 -RD_ZER0 = 1 -RD_POSINF = 2 -RD_NEGINF = 3 -FPRS_DL = 0x1 -FPRS_DU = 0x2 -FPRS_FEF = 0x4 -PIL_MAX = 0xf -def SAVE_GLOBALS(RP): return \ - -def RESTORE_GLOBALS(RP): return \ - -def SAVE_OUTS(RP): return \ - -def RESTORE_OUTS(RP): return \ - -def SAVE_WINDOW(SBP): return \ - -def RESTORE_WINDOW(SBP): return \ - -def STORE_FPREGS(FP): return \ - -def LOAD_FPREGS(FP): return \ - -_SPARC_MAXREGWINDOW = 31 -_XRS_ID = 0x78727300 -GETCONTEXT = 0 -SETCONTEXT = 1 -UC_SIGMASK = 0o01 -UC_STACK = 0o02 -UC_CPU = 0o04 -UC_MAU = 0o10 -UC_FPU = UC_MAU -UC_INTR = 0o20 -UC_ASR = 0o40 -UC_MCONTEXT = (UC_CPU|UC_FPU|UC_ASR) -UC_ALL = (UC_SIGMASK|UC_STACK|UC_MCONTEXT) -_SIGQUEUE_MAX = 32 -_SIGNOTIFY_MAX = 32 - -# Included from sys/pcb.h -INSTR_VALID = 0x02 -NORMAL_STEP = 0x04 -WATCH_STEP = 0x08 -CPC_OVERFLOW = 0x10 -ASYNC_HWERR = 0x20 -STEP_NONE = 0 -STEP_REQUESTED = 1 -STEP_ACTIVE = 2 -STEP_WASACTIVE = 3 - -# Included from sys/msacct.h -LMS_USER = 0 -LMS_SYSTEM = 1 -LMS_TRAP = 2 -LMS_TFAULT = 3 -LMS_DFAULT = 4 -LMS_KFAULT = 5 -LMS_USER_LOCK = 6 -LMS_SLEEP = 7 -LMS_WAIT_CPU = 8 -LMS_STOPPED = 9 -NMSTATES = 10 - -# Included from sys/lwp.h - -# Included from sys/synch.h -from TYPES import * -USYNC_THREAD = 0x00 -USYNC_PROCESS = 0x01 -LOCK_NORMAL = 0x00 -LOCK_ERRORCHECK = 0x02 -LOCK_RECURSIVE = 0x04 -USYNC_PROCESS_ROBUST = 0x08 -LOCK_PRIO_NONE = 0x00 -LOCK_PRIO_INHERIT = 0x10 -LOCK_PRIO_PROTECT = 0x20 -LOCK_STALL_NP = 0x00 -LOCK_ROBUST_NP = 0x40 -LOCK_OWNERDEAD = 0x1 -LOCK_NOTRECOVERABLE = 0x2 -LOCK_INITED = 0x4 -LOCK_UNMAPPED = 0x8 -LWP_DETACHED = 0x00000040 -LWP_SUSPENDED = 0x00000080 -__LWP_ASLWP = 0x00000100 -MAXSYSARGS = 8 -NORMALRETURN = 0 -JUSTRETURN = 1 -LWP_USER = 0x01 -LWP_SYS = 0x02 -TS_FREE = 0x00 -TS_SLEEP = 0x01 -TS_RUN = 0x02 -TS_ONPROC = 0x04 -TS_ZOMB = 0x08 -TS_STOPPED = 0x10 -T_INTR_THREAD = 0x0001 -T_WAKEABLE = 0x0002 -T_TOMASK = 0x0004 -T_TALLOCSTK = 0x0008 -T_WOULDBLOCK = 0x0020 -T_DONTBLOCK = 0x0040 -T_DONTPEND = 0x0080 -T_SYS_PROF = 0x0100 -T_WAITCVSEM = 0x0200 -T_WATCHPT = 0x0400 -T_PANIC = 0x0800 -TP_HOLDLWP = 0x0002 -TP_TWAIT = 0x0004 -TP_LWPEXIT = 0x0008 -TP_PRSTOP = 0x0010 -TP_CHKPT = 0x0020 -TP_EXITLWP = 0x0040 -TP_PRVSTOP = 0x0080 -TP_MSACCT = 0x0100 -TP_STOPPING = 0x0200 -TP_WATCHPT = 0x0400 -TP_PAUSE = 0x0800 -TP_CHANGEBIND = 0x1000 -TS_LOAD = 0x0001 -TS_DONT_SWAP = 0x0002 -TS_SWAPENQ = 0x0004 -TS_ON_SWAPQ = 0x0008 -TS_CSTART = 0x0100 -TS_UNPAUSE = 0x0200 -TS_XSTART = 0x0400 -TS_PSTART = 0x0800 -TS_RESUME = 0x1000 -TS_CREATE = 0x2000 -TS_ALLSTART = \ - (TS_CSTART|TS_UNPAUSE|TS_XSTART|TS_PSTART|TS_RESUME|TS_CREATE) -def CPR_VSTOPPED(t): return \ - -def THREAD_TRANSITION(tp): return thread_transition(tp); - -def THREAD_STOP(tp): return \ - -def THREAD_ZOMB(tp): return THREAD_SET_STATE(tp, TS_ZOMB, NULL) - -def SEMA_HELD(x): return (sema_held((x))) - -NO_LOCKS_HELD = 1 -NO_COMPETING_THREADS = 1 - -# Included from sys/cred.h - -# Included from sys/uio.h -from TYPES import * - -# Included from sys/resource.h -from TYPES import * -PRIO_PROCESS = 0 -PRIO_PGRP = 1 -PRIO_USER = 2 -RLIMIT_CPU = 0 -RLIMIT_FSIZE = 1 -RLIMIT_DATA = 2 -RLIMIT_STACK = 3 -RLIMIT_CORE = 4 -RLIMIT_NOFILE = 5 -RLIMIT_VMEM = 6 -RLIMIT_AS = RLIMIT_VMEM -RLIM_NLIMITS = 7 -RLIM_INFINITY = (-3) -RLIM_SAVED_MAX = (-2) -RLIM_SAVED_CUR = (-1) -RLIM_INFINITY = 0x7fffffff -RLIM_SAVED_MAX = 0x7ffffffe -RLIM_SAVED_CUR = 0x7ffffffd -RLIM32_INFINITY = 0x7fffffff -RLIM32_SAVED_MAX = 0x7ffffffe -RLIM32_SAVED_CUR = 0x7ffffffd - -# Included from sys/model.h - -# Included from sys/debug.h -def ASSERT64(x): return ASSERT(x) - -def ASSERT32(x): return ASSERT(x) - -DATAMODEL_MASK = 0x0FF00000 -DATAMODEL_ILP32 = 0x00100000 -DATAMODEL_LP64 = 0x00200000 -DATAMODEL_NONE = 0 -DATAMODEL_NATIVE = DATAMODEL_LP64 -DATAMODEL_NATIVE = DATAMODEL_ILP32 -def STRUCT_SIZE(handle): return \ - -def STRUCT_BUF(handle): return ((handle).ptr.m64) - -def SIZEOF_PTR(umodel): return \ - -def STRUCT_SIZE(handle): return (sizeof (*(handle).ptr)) - -def STRUCT_BUF(handle): return ((handle).ptr) - -def SIZEOF_PTR(umodel): return sizeof (caddr_t) - -def lwp_getdatamodel(t): return DATAMODEL_ILP32 - -RUSAGE_SELF = 0 -RUSAGE_CHILDREN = -1 - -# Included from vm/seg_enum.h - -# Included from sys/buf.h - -# Included from sys/kstat.h -from TYPES import * -KSTAT_STRLEN = 31 -def KSTAT_ENTER(k): return \ - -def KSTAT_EXIT(k): return \ - -KSTAT_TYPE_RAW = 0 -KSTAT_TYPE_NAMED = 1 -KSTAT_TYPE_INTR = 2 -KSTAT_TYPE_IO = 3 -KSTAT_TYPE_TIMER = 4 -KSTAT_NUM_TYPES = 5 -KSTAT_FLAG_VIRTUAL = 0x01 -KSTAT_FLAG_VAR_SIZE = 0x02 -KSTAT_FLAG_WRITABLE = 0x04 -KSTAT_FLAG_PERSISTENT = 0x08 -KSTAT_FLAG_DORMANT = 0x10 -KSTAT_FLAG_INVALID = 0x20 -KSTAT_READ = 0 -KSTAT_WRITE = 1 -KSTAT_DATA_CHAR = 0 -KSTAT_DATA_INT32 = 1 -KSTAT_DATA_UINT32 = 2 -KSTAT_DATA_INT64 = 3 -KSTAT_DATA_UINT64 = 4 -KSTAT_DATA_LONG = KSTAT_DATA_INT32 -KSTAT_DATA_ULONG = KSTAT_DATA_UINT32 -KSTAT_DATA_LONG = KSTAT_DATA_INT64 -KSTAT_DATA_ULONG = KSTAT_DATA_UINT64 -KSTAT_DATA_LONG = 7 -KSTAT_DATA_ULONG = 8 -KSTAT_DATA_LONGLONG = KSTAT_DATA_INT64 -KSTAT_DATA_ULONGLONG = KSTAT_DATA_UINT64 -KSTAT_DATA_FLOAT = 5 -KSTAT_DATA_DOUBLE = 6 -KSTAT_INTR_HARD = 0 -KSTAT_INTR_SOFT = 1 -KSTAT_INTR_WATCHDOG = 2 -KSTAT_INTR_SPURIOUS = 3 -KSTAT_INTR_MULTSVC = 4 -KSTAT_NUM_INTRS = 5 -B_BUSY = 0x0001 -B_DONE = 0x0002 -B_ERROR = 0x0004 -B_PAGEIO = 0x0010 -B_PHYS = 0x0020 -B_READ = 0x0040 -B_WRITE = 0x0100 -B_KERNBUF = 0x0008 -B_WANTED = 0x0080 -B_AGE = 0x000200 -B_ASYNC = 0x000400 -B_DELWRI = 0x000800 -B_STALE = 0x001000 -B_DONTNEED = 0x002000 -B_REMAPPED = 0x004000 -B_FREE = 0x008000 -B_INVAL = 0x010000 -B_FORCE = 0x020000 -B_HEAD = 0x040000 -B_NOCACHE = 0x080000 -B_TRUNC = 0x100000 -B_SHADOW = 0x200000 -B_RETRYWRI = 0x400000 -def notavail(bp): return \ - -def BWRITE(bp): return \ - -def BWRITE2(bp): return \ - -VROOT = 0x01 -VNOCACHE = 0x02 -VNOMAP = 0x04 -VDUP = 0x08 -VNOSWAP = 0x10 -VNOMOUNT = 0x20 -VISSWAP = 0x40 -VSWAPLIKE = 0x80 -VVFSLOCK = 0x100 -VVFSWAIT = 0x200 -VVMLOCK = 0x400 -VDIROPEN = 0x800 -VVMEXEC = 0x1000 -VPXFS = 0x2000 -AT_TYPE = 0x0001 -AT_MODE = 0x0002 -AT_UID = 0x0004 -AT_GID = 0x0008 -AT_FSID = 0x0010 -AT_NODEID = 0x0020 -AT_NLINK = 0x0040 -AT_SIZE = 0x0080 -AT_ATIME = 0x0100 -AT_MTIME = 0x0200 -AT_CTIME = 0x0400 -AT_RDEV = 0x0800 -AT_BLKSIZE = 0x1000 -AT_NBLOCKS = 0x2000 -AT_VCODE = 0x4000 -AT_ALL = (AT_TYPE|AT_MODE|AT_UID|AT_GID|AT_FSID|AT_NODEID|\ - AT_NLINK|AT_SIZE|AT_ATIME|AT_MTIME|AT_CTIME|\ - AT_RDEV|AT_BLKSIZE|AT_NBLOCKS|AT_VCODE) -AT_STAT = (AT_MODE|AT_UID|AT_GID|AT_FSID|AT_NODEID|AT_NLINK|\ - AT_SIZE|AT_ATIME|AT_MTIME|AT_CTIME|AT_RDEV) -AT_TIMES = (AT_ATIME|AT_MTIME|AT_CTIME) -AT_NOSET = (AT_NLINK|AT_RDEV|AT_FSID|AT_NODEID|AT_TYPE|\ - AT_BLKSIZE|AT_NBLOCKS|AT_VCODE) -VSUID = 0o4000 -VSGID = 0o2000 -VSVTX = 0o1000 -VREAD = 0o0400 -VWRITE = 0o0200 -VEXEC = 0o0100 -MODEMASK = 0o7777 -PERMMASK = 0o0777 -def MANDMODE(mode): return (((mode) & (VSGID|(VEXEC>>3))) == VSGID) - -VSA_ACL = 0x0001 -VSA_ACLCNT = 0x0002 -VSA_DFACL = 0x0004 -VSA_DFACLCNT = 0x0008 -LOOKUP_DIR = 0x01 -DUMP_ALLOC = 0 -DUMP_FREE = 1 -DUMP_SCAN = 2 -ATTR_UTIME = 0x01 -ATTR_EXEC = 0x02 -ATTR_COMM = 0x04 -ATTR_HINT = 0x08 -ATTR_REAL = 0x10 - -# Included from sys/poll.h -POLLIN = 0x0001 -POLLPRI = 0x0002 -POLLOUT = 0x0004 -POLLRDNORM = 0x0040 -POLLWRNORM = POLLOUT -POLLRDBAND = 0x0080 -POLLWRBAND = 0x0100 -POLLNORM = POLLRDNORM -POLLERR = 0x0008 -POLLHUP = 0x0010 -POLLNVAL = 0x0020 -POLLREMOVE = 0x0800 -POLLRDDATA = 0x0200 -POLLNOERR = 0x0400 -POLLCLOSED = 0x8000 - -# Included from sys/strmdep.h -def str_aligned(X): return (((ulong_t)(X) & (sizeof (int) - 1)) == 0) - - -# Included from sys/strft.h -tdelta_t_sz = 12 -FTEV_MASK = 0x1FFF -FTEV_ISWR = 0x8000 -FTEV_CS = 0x4000 -FTEV_PS = 0x2000 -FTEV_QMASK = 0x1F00 -FTEV_ALLOCMASK = 0x1FF8 -FTEV_ALLOCB = 0x0000 -FTEV_ESBALLOC = 0x0001 -FTEV_DESBALLOC = 0x0002 -FTEV_ESBALLOCA = 0x0003 -FTEV_DESBALLOCA = 0x0004 -FTEV_ALLOCBIG = 0x0005 -FTEV_ALLOCBW = 0x0006 -FTEV_FREEB = 0x0008 -FTEV_DUPB = 0x0009 -FTEV_COPYB = 0x000A -FTEV_CALLER = 0x000F -FTEV_PUT = 0x0100 -FTEV_FSYNCQ = 0x0103 -FTEV_DSYNCQ = 0x0104 -FTEV_PUTQ = 0x0105 -FTEV_GETQ = 0x0106 -FTEV_RMVQ = 0x0107 -FTEV_INSQ = 0x0108 -FTEV_PUTBQ = 0x0109 -FTEV_FLUSHQ = 0x010A -FTEV_REPLYQ = 0x010B -FTEV_PUTNEXT = 0x010D -FTEV_RWNEXT = 0x010E -FTEV_QWINNER = 0x010F -FTEV_GEWRITE = 0x0101 -def FTFLW_HASH(h): return (((unsigned)(h))%ftflw_hash_sz) - -FTBLK_EVNTS = 0x9 -QENAB = 0x00000001 -QWANTR = 0x00000002 -QWANTW = 0x00000004 -QFULL = 0x00000008 -QREADR = 0x00000010 -QUSE = 0x00000020 -QNOENB = 0x00000040 -QBACK = 0x00000100 -QHLIST = 0x00000200 -QPAIR = 0x00000800 -QPERQ = 0x00001000 -QPERMOD = 0x00002000 -QMTSAFE = 0x00004000 -QMTOUTPERIM = 0x00008000 -QMT_TYPEMASK = (QPAIR|QPERQ|QPERMOD|QMTSAFE|QMTOUTPERIM) -QINSERVICE = 0x00010000 -QWCLOSE = 0x00020000 -QEND = 0x00040000 -QWANTWSYNC = 0x00080000 -QSYNCSTR = 0x00100000 -QISDRV = 0x00200000 -QHOT = 0x00400000 -QNEXTHOT = 0x00800000 -_QINSERTING = 0x04000000 -_QREMOVING = 0x08000000 -Q_SQQUEUED = 0x01 -Q_SQDRAINING = 0x02 -QB_FULL = 0x01 -QB_WANTW = 0x02 -QB_BACK = 0x04 -NBAND = 256 -STRUIOT_NONE = -1 -STRUIOT_DONTCARE = 0 -STRUIOT_STANDARD = 1 -STRUIOT_IP = 2 -DBLK_REFMIN = 0x01 -STRUIO_SPEC = 0x01 -STRUIO_DONE = 0x02 -STRUIO_IP = 0x04 -STRUIO_ZC = 0x08 -STRUIO_ICK = 0x10 -MSGMARK = 0x01 -MSGNOLOOP = 0x02 -MSGDELIM = 0x04 -MSGNOGET = 0x08 -MSGMARKNEXT = 0x10 -MSGNOTMARKNEXT = 0x20 -M_DATA = 0x00 -M_PROTO = 0x01 -M_BREAK = 0x08 -M_PASSFP = 0x09 -M_EVENT = 0x0a -M_SIG = 0x0b -M_DELAY = 0x0c -M_CTL = 0x0d -M_IOCTL = 0x0e -M_SETOPTS = 0x10 -M_RSE = 0x11 -M_IOCACK = 0x81 -M_IOCNAK = 0x82 -M_PCPROTO = 0x83 -M_PCSIG = 0x84 -M_READ = 0x85 -M_FLUSH = 0x86 -M_STOP = 0x87 -M_START = 0x88 -M_HANGUP = 0x89 -M_ERROR = 0x8a -M_COPYIN = 0x8b -M_COPYOUT = 0x8c -M_IOCDATA = 0x8d -M_PCRSE = 0x8e -M_STOPI = 0x8f -M_STARTI = 0x90 -M_PCEVENT = 0x91 -M_UNHANGUP = 0x92 -QNORM = 0x00 -QPCTL = 0x80 -IOC_MODELS = DATAMODEL_MASK -IOC_ILP32 = DATAMODEL_ILP32 -IOC_LP64 = DATAMODEL_LP64 -IOC_NATIVE = DATAMODEL_NATIVE -IOC_NONE = DATAMODEL_NONE -STRCANON = 0x01 -RECOPY = 0x02 -SO_ALL = 0x003f -SO_READOPT = 0x0001 -SO_WROFF = 0x0002 -SO_MINPSZ = 0x0004 -SO_MAXPSZ = 0x0008 -SO_HIWAT = 0x0010 -SO_LOWAT = 0x0020 -SO_MREADON = 0x0040 -SO_MREADOFF = 0x0080 -SO_NDELON = 0x0100 -SO_NDELOFF = 0x0200 -SO_ISTTY = 0x0400 -SO_ISNTTY = 0x0800 -SO_TOSTOP = 0x1000 -SO_TONSTOP = 0x2000 -SO_BAND = 0x4000 -SO_DELIM = 0x8000 -SO_NODELIM = 0x010000 -SO_STRHOLD = 0x020000 -SO_ERROPT = 0x040000 -SO_COPYOPT = 0x080000 -SO_MAXBLK = 0x100000 -DEF_IOV_MAX = 16 -INFOD_FIRSTBYTES = 0x02 -INFOD_BYTES = 0x04 -INFOD_COUNT = 0x08 -INFOD_COPYOUT = 0x10 -MODOPEN = 0x1 -CLONEOPEN = 0x2 -CONSOPEN = 0x4 -OPENFAIL = -1 -BPRI_LO = 1 -BPRI_MED = 2 -BPRI_HI = 3 -BPRI_FT = 4 -INFPSZ = -1 -FLUSHALL = 1 -FLUSHDATA = 0 -STRHIGH = 5120 -STRLOW = 1024 -MAXIOCBSZ = 1024 -PERIM_INNER = 1 -PERIM_OUTER = 2 -def datamsg(type): return \ - -def straln(a): return (caddr_t)((intptr_t)(a) & ~(sizeof (int)-1)) - - -# Included from sys/byteorder.h -def ntohl(x): return (x) - -def ntohs(x): return (x) - -def htonl(x): return (x) - -def htons(x): return (x) - -IPPROTO_IP = 0 -IPPROTO_HOPOPTS = 0 -IPPROTO_ICMP = 1 -IPPROTO_IGMP = 2 -IPPROTO_GGP = 3 -IPPROTO_ENCAP = 4 -IPPROTO_TCP = 6 -IPPROTO_EGP = 8 -IPPROTO_PUP = 12 -IPPROTO_UDP = 17 -IPPROTO_IDP = 22 -IPPROTO_IPV6 = 41 -IPPROTO_ROUTING = 43 -IPPROTO_FRAGMENT = 44 -IPPROTO_RSVP = 46 -IPPROTO_ESP = 50 -IPPROTO_AH = 51 -IPPROTO_ICMPV6 = 58 -IPPROTO_NONE = 59 -IPPROTO_DSTOPTS = 60 -IPPROTO_HELLO = 63 -IPPROTO_ND = 77 -IPPROTO_EON = 80 -IPPROTO_PIM = 103 -IPPROTO_RAW = 255 -IPPROTO_MAX = 256 -IPPORT_ECHO = 7 -IPPORT_DISCARD = 9 -IPPORT_SYSTAT = 11 -IPPORT_DAYTIME = 13 -IPPORT_NETSTAT = 15 -IPPORT_FTP = 21 -IPPORT_TELNET = 23 -IPPORT_SMTP = 25 -IPPORT_TIMESERVER = 37 -IPPORT_NAMESERVER = 42 -IPPORT_WHOIS = 43 -IPPORT_MTP = 57 -IPPORT_BOOTPS = 67 -IPPORT_BOOTPC = 68 -IPPORT_TFTP = 69 -IPPORT_RJE = 77 -IPPORT_FINGER = 79 -IPPORT_TTYLINK = 87 -IPPORT_SUPDUP = 95 -IPPORT_EXECSERVER = 512 -IPPORT_LOGINSERVER = 513 -IPPORT_CMDSERVER = 514 -IPPORT_EFSSERVER = 520 -IPPORT_BIFFUDP = 512 -IPPORT_WHOSERVER = 513 -IPPORT_ROUTESERVER = 520 -IPPORT_RESERVED = 1024 -IPPORT_USERRESERVED = 5000 -IMPLINK_IP = 155 -IMPLINK_LOWEXPER = 156 -IMPLINK_HIGHEXPER = 158 -IN_CLASSA_NSHIFT = 24 -IN_CLASSA_MAX = 128 -IN_CLASSB_NSHIFT = 16 -IN_CLASSB_MAX = 65536 -IN_CLASSC_NSHIFT = 8 -IN_CLASSD_NSHIFT = 28 -def IN_MULTICAST(i): return IN_CLASSD(i) - -IN_LOOPBACKNET = 127 -def IN_SET_LOOPBACK_ADDR(a): return \ - -def IN6_IS_ADDR_UNSPECIFIED(addr): return \ - -def IN6_IS_ADDR_LOOPBACK(addr): return \ - -def IN6_IS_ADDR_LOOPBACK(addr): return \ - -def IN6_IS_ADDR_MULTICAST(addr): return \ - -def IN6_IS_ADDR_MULTICAST(addr): return \ - -def IN6_IS_ADDR_LINKLOCAL(addr): return \ - -def IN6_IS_ADDR_LINKLOCAL(addr): return \ - -def IN6_IS_ADDR_SITELOCAL(addr): return \ - -def IN6_IS_ADDR_SITELOCAL(addr): return \ - -def IN6_IS_ADDR_V4MAPPED(addr): return \ - -def IN6_IS_ADDR_V4MAPPED(addr): return \ - -def IN6_IS_ADDR_V4MAPPED_ANY(addr): return \ - -def IN6_IS_ADDR_V4MAPPED_ANY(addr): return \ - -def IN6_IS_ADDR_V4COMPAT(addr): return \ - -def IN6_IS_ADDR_V4COMPAT(addr): return \ - -def IN6_IS_ADDR_MC_RESERVED(addr): return \ - -def IN6_IS_ADDR_MC_RESERVED(addr): return \ - -def IN6_IS_ADDR_MC_NODELOCAL(addr): return \ - -def IN6_IS_ADDR_MC_NODELOCAL(addr): return \ - -def IN6_IS_ADDR_MC_LINKLOCAL(addr): return \ - -def IN6_IS_ADDR_MC_LINKLOCAL(addr): return \ - -def IN6_IS_ADDR_MC_SITELOCAL(addr): return \ - -def IN6_IS_ADDR_MC_SITELOCAL(addr): return \ - -def IN6_IS_ADDR_MC_ORGLOCAL(addr): return \ - -def IN6_IS_ADDR_MC_ORGLOCAL(addr): return \ - -def IN6_IS_ADDR_MC_GLOBAL(addr): return \ - -def IN6_IS_ADDR_MC_GLOBAL(addr): return \ - -IP_OPTIONS = 1 -IP_HDRINCL = 2 -IP_TOS = 3 -IP_TTL = 4 -IP_RECVOPTS = 5 -IP_RECVRETOPTS = 6 -IP_RECVDSTADDR = 7 -IP_RETOPTS = 8 -IP_MULTICAST_IF = 0x10 -IP_MULTICAST_TTL = 0x11 -IP_MULTICAST_LOOP = 0x12 -IP_ADD_MEMBERSHIP = 0x13 -IP_DROP_MEMBERSHIP = 0x14 -IP_SEC_OPT = 0x22 -IPSEC_PREF_NEVER = 0x01 -IPSEC_PREF_REQUIRED = 0x02 -IPSEC_PREF_UNIQUE = 0x04 -IP_ADD_PROXY_ADDR = 0x40 -IP_BOUND_IF = 0x41 -IP_UNSPEC_SRC = 0x42 -IP_REUSEADDR = 0x104 -IP_DONTROUTE = 0x105 -IP_BROADCAST = 0x106 -IP_DEFAULT_MULTICAST_TTL = 1 -IP_DEFAULT_MULTICAST_LOOP = 1 -IPV6_RTHDR_TYPE_0 = 0 -IPV6_UNICAST_HOPS = 0x5 -IPV6_MULTICAST_IF = 0x6 -IPV6_MULTICAST_HOPS = 0x7 -IPV6_MULTICAST_LOOP = 0x8 -IPV6_JOIN_GROUP = 0x9 -IPV6_LEAVE_GROUP = 0xa -IPV6_ADD_MEMBERSHIP = 0x9 -IPV6_DROP_MEMBERSHIP = 0xa -IPV6_PKTINFO = 0xb -IPV6_HOPLIMIT = 0xc -IPV6_NEXTHOP = 0xd -IPV6_HOPOPTS = 0xe -IPV6_DSTOPTS = 0xf -IPV6_RTHDR = 0x10 -IPV6_RTHDRDSTOPTS = 0x11 -IPV6_RECVPKTINFO = 0x12 -IPV6_RECVHOPLIMIT = 0x13 -IPV6_RECVHOPOPTS = 0x14 -IPV6_RECVDSTOPTS = 0x15 -IPV6_RECVRTHDR = 0x16 -IPV6_RECVRTHDRDSTOPTS = 0x17 -IPV6_CHECKSUM = 0x18 -IPV6_BOUND_IF = 0x41 -IPV6_UNSPEC_SRC = 0x42 -INET_ADDRSTRLEN = 16 -INET6_ADDRSTRLEN = 46 -IPV6_PAD1_OPT = 0 diff --git a/Lib/plat-sunos5/STROPTS.py b/Lib/plat-sunos5/STROPTS.py deleted file mode 100644 index 8f735c4f84..0000000000 --- a/Lib/plat-sunos5/STROPTS.py +++ /dev/null @@ -1,1813 +0,0 @@ -# Generated by h2py from /usr/include/sys/stropts.h - -# Included from sys/feature_tests.h - -# Included from sys/isa_defs.h -_CHAR_ALIGNMENT = 1 -_SHORT_ALIGNMENT = 2 -_INT_ALIGNMENT = 4 -_LONG_ALIGNMENT = 8 -_LONG_LONG_ALIGNMENT = 8 -_DOUBLE_ALIGNMENT = 8 -_LONG_DOUBLE_ALIGNMENT = 16 -_POINTER_ALIGNMENT = 8 -_MAX_ALIGNMENT = 16 -_ALIGNMENT_REQUIRED = 1 -_CHAR_ALIGNMENT = 1 -_SHORT_ALIGNMENT = 2 -_INT_ALIGNMENT = 4 -_LONG_ALIGNMENT = 4 -_LONG_LONG_ALIGNMENT = 4 -_DOUBLE_ALIGNMENT = 4 -_LONG_DOUBLE_ALIGNMENT = 4 -_POINTER_ALIGNMENT = 4 -_MAX_ALIGNMENT = 4 -_ALIGNMENT_REQUIRED = 0 -_CHAR_ALIGNMENT = 1 -_SHORT_ALIGNMENT = 2 -_INT_ALIGNMENT = 4 -_LONG_LONG_ALIGNMENT = 8 -_DOUBLE_ALIGNMENT = 8 -_ALIGNMENT_REQUIRED = 1 -_LONG_ALIGNMENT = 4 -_LONG_DOUBLE_ALIGNMENT = 8 -_POINTER_ALIGNMENT = 4 -_MAX_ALIGNMENT = 8 -_LONG_ALIGNMENT = 8 -_LONG_DOUBLE_ALIGNMENT = 16 -_POINTER_ALIGNMENT = 8 -_MAX_ALIGNMENT = 16 -_POSIX_C_SOURCE = 1 -_LARGEFILE64_SOURCE = 1 -_LARGEFILE_SOURCE = 1 -_FILE_OFFSET_BITS = 64 -_FILE_OFFSET_BITS = 32 -_POSIX_C_SOURCE = 199506 -_POSIX_PTHREAD_SEMANTICS = 1 -_XOPEN_VERSION = 500 -_XOPEN_VERSION = 4 -_XOPEN_VERSION = 3 -from TYPES import * - -# Included from sys/conf.h - -# Included from sys/t_lock.h - -# Included from sys/machlock.h -from TYPES import * -LOCK_HELD_VALUE = 0xff -def SPIN_LOCK(pl): return ((pl) > ipltospl(LOCK_LEVEL)) - -def LOCK_SAMPLE_INTERVAL(i): return (((i) & 0xff) == 0) - -CLOCK_LEVEL = 10 -LOCK_LEVEL = 10 -DISP_LEVEL = (LOCK_LEVEL + 1) -PTR24_LSB = 5 -PTR24_MSB = (PTR24_LSB + 24) -PTR24_ALIGN = 32 -PTR24_BASE = 0xe0000000 - -# Included from sys/param.h -from TYPES import * -_POSIX_VDISABLE = 0 -MAX_INPUT = 512 -MAX_CANON = 256 -UID_NOBODY = 60001 -GID_NOBODY = UID_NOBODY -UID_NOACCESS = 60002 -MAX_TASKID = 999999 -MAX_MAXPID = 999999 -DEFAULT_MAXPID = 999999 -DEFAULT_JUMPPID = 100000 -DEFAULT_MAXPID = 30000 -DEFAULT_JUMPPID = 0 -MAXUID = 2147483647 -MAXPROJID = MAXUID -MAXLINK = 32767 -NMOUNT = 40 -CANBSIZ = 256 -NOFILE = 20 -NGROUPS_UMIN = 0 -NGROUPS_UMAX = 32 -NGROUPS_MAX_DEFAULT = 16 -NZERO = 20 -NULL = 0 -NULL = 0 -CMASK = 0o22 -CDLIMIT = (1<<11) -NBPS = 0x20000 -NBPSCTR = 512 -UBSIZE = 512 -SCTRSHFT = 9 -SYSNAME = 9 -PREMOTE = 39 -MAXPATHLEN = 1024 -MAXSYMLINKS = 20 -MAXNAMELEN = 256 -NADDR = 13 -PIPE_BUF = 5120 -PIPE_MAX = 5120 -NBBY = 8 -MAXBSIZE = 8192 -DEV_BSIZE = 512 -DEV_BSHIFT = 9 -MAXFRAG = 8 -MAXOFF32_T = 0x7fffffff -MAXOFF_T = 0x7fffffffffffffff -MAXOFFSET_T = 0x7fffffffffffffff -MAXOFF_T = 0x7fffffff -MAXOFFSET_T = 0x7fffffff -def btodb(bytes): return \ - -def dbtob(db): return \ - -def lbtodb(bytes): return \ - -def ldbtob(db): return \ - -NCARGS32 = 0x100000 -NCARGS64 = 0x200000 -NCARGS = NCARGS64 -NCARGS = NCARGS32 -FSHIFT = 8 -FSCALE = (1<> MMU_PAGESHIFT) - -def mmu_btopr(x): return ((((x) + MMU_PAGEOFFSET) >> MMU_PAGESHIFT)) - -def mmu_ptod(x): return ((x) << (MMU_PAGESHIFT - DEV_BSHIFT)) - -def ptod(x): return ((x) << (PAGESHIFT - DEV_BSHIFT)) - -def ptob(x): return ((x) << PAGESHIFT) - -def btop(x): return (((x) >> PAGESHIFT)) - -def btopr(x): return ((((x) + PAGEOFFSET) >> PAGESHIFT)) - -def dtop(DD): return (((DD) + NDPP - 1) >> (PAGESHIFT - DEV_BSHIFT)) - -def dtopt(DD): return ((DD) >> (PAGESHIFT - DEV_BSHIFT)) - -_AIO_LISTIO_MAX = (4096) -_AIO_MAX = (-1) -_MQ_OPEN_MAX = (32) -_MQ_PRIO_MAX = (32) -_SEM_NSEMS_MAX = INT_MAX -_SEM_VALUE_MAX = INT_MAX - -# Included from sys/unistd.h -_CS_PATH = 65 -_CS_LFS_CFLAGS = 68 -_CS_LFS_LDFLAGS = 69 -_CS_LFS_LIBS = 70 -_CS_LFS_LINTFLAGS = 71 -_CS_LFS64_CFLAGS = 72 -_CS_LFS64_LDFLAGS = 73 -_CS_LFS64_LIBS = 74 -_CS_LFS64_LINTFLAGS = 75 -_CS_XBS5_ILP32_OFF32_CFLAGS = 700 -_CS_XBS5_ILP32_OFF32_LDFLAGS = 701 -_CS_XBS5_ILP32_OFF32_LIBS = 702 -_CS_XBS5_ILP32_OFF32_LINTFLAGS = 703 -_CS_XBS5_ILP32_OFFBIG_CFLAGS = 705 -_CS_XBS5_ILP32_OFFBIG_LDFLAGS = 706 -_CS_XBS5_ILP32_OFFBIG_LIBS = 707 -_CS_XBS5_ILP32_OFFBIG_LINTFLAGS = 708 -_CS_XBS5_LP64_OFF64_CFLAGS = 709 -_CS_XBS5_LP64_OFF64_LDFLAGS = 710 -_CS_XBS5_LP64_OFF64_LIBS = 711 -_CS_XBS5_LP64_OFF64_LINTFLAGS = 712 -_CS_XBS5_LPBIG_OFFBIG_CFLAGS = 713 -_CS_XBS5_LPBIG_OFFBIG_LDFLAGS = 714 -_CS_XBS5_LPBIG_OFFBIG_LIBS = 715 -_CS_XBS5_LPBIG_OFFBIG_LINTFLAGS = 716 -_SC_ARG_MAX = 1 -_SC_CHILD_MAX = 2 -_SC_CLK_TCK = 3 -_SC_NGROUPS_MAX = 4 -_SC_OPEN_MAX = 5 -_SC_JOB_CONTROL = 6 -_SC_SAVED_IDS = 7 -_SC_VERSION = 8 -_SC_PASS_MAX = 9 -_SC_LOGNAME_MAX = 10 -_SC_PAGESIZE = 11 -_SC_XOPEN_VERSION = 12 -_SC_NPROCESSORS_CONF = 14 -_SC_NPROCESSORS_ONLN = 15 -_SC_STREAM_MAX = 16 -_SC_TZNAME_MAX = 17 -_SC_AIO_LISTIO_MAX = 18 -_SC_AIO_MAX = 19 -_SC_AIO_PRIO_DELTA_MAX = 20 -_SC_ASYNCHRONOUS_IO = 21 -_SC_DELAYTIMER_MAX = 22 -_SC_FSYNC = 23 -_SC_MAPPED_FILES = 24 -_SC_MEMLOCK = 25 -_SC_MEMLOCK_RANGE = 26 -_SC_MEMORY_PROTECTION = 27 -_SC_MESSAGE_PASSING = 28 -_SC_MQ_OPEN_MAX = 29 -_SC_MQ_PRIO_MAX = 30 -_SC_PRIORITIZED_IO = 31 -_SC_PRIORITY_SCHEDULING = 32 -_SC_REALTIME_SIGNALS = 33 -_SC_RTSIG_MAX = 34 -_SC_SEMAPHORES = 35 -_SC_SEM_NSEMS_MAX = 36 -_SC_SEM_VALUE_MAX = 37 -_SC_SHARED_MEMORY_OBJECTS = 38 -_SC_SIGQUEUE_MAX = 39 -_SC_SIGRT_MIN = 40 -_SC_SIGRT_MAX = 41 -_SC_SYNCHRONIZED_IO = 42 -_SC_TIMERS = 43 -_SC_TIMER_MAX = 44 -_SC_2_C_BIND = 45 -_SC_2_C_DEV = 46 -_SC_2_C_VERSION = 47 -_SC_2_FORT_DEV = 48 -_SC_2_FORT_RUN = 49 -_SC_2_LOCALEDEF = 50 -_SC_2_SW_DEV = 51 -_SC_2_UPE = 52 -_SC_2_VERSION = 53 -_SC_BC_BASE_MAX = 54 -_SC_BC_DIM_MAX = 55 -_SC_BC_SCALE_MAX = 56 -_SC_BC_STRING_MAX = 57 -_SC_COLL_WEIGHTS_MAX = 58 -_SC_EXPR_NEST_MAX = 59 -_SC_LINE_MAX = 60 -_SC_RE_DUP_MAX = 61 -_SC_XOPEN_CRYPT = 62 -_SC_XOPEN_ENH_I18N = 63 -_SC_XOPEN_SHM = 64 -_SC_2_CHAR_TERM = 66 -_SC_XOPEN_XCU_VERSION = 67 -_SC_ATEXIT_MAX = 76 -_SC_IOV_MAX = 77 -_SC_XOPEN_UNIX = 78 -_SC_PAGE_SIZE = _SC_PAGESIZE -_SC_T_IOV_MAX = 79 -_SC_PHYS_PAGES = 500 -_SC_AVPHYS_PAGES = 501 -_SC_COHER_BLKSZ = 503 -_SC_SPLIT_CACHE = 504 -_SC_ICACHE_SZ = 505 -_SC_DCACHE_SZ = 506 -_SC_ICACHE_LINESZ = 507 -_SC_DCACHE_LINESZ = 508 -_SC_ICACHE_BLKSZ = 509 -_SC_DCACHE_BLKSZ = 510 -_SC_DCACHE_TBLKSZ = 511 -_SC_ICACHE_ASSOC = 512 -_SC_DCACHE_ASSOC = 513 -_SC_MAXPID = 514 -_SC_STACK_PROT = 515 -_SC_THREAD_DESTRUCTOR_ITERATIONS = 568 -_SC_GETGR_R_SIZE_MAX = 569 -_SC_GETPW_R_SIZE_MAX = 570 -_SC_LOGIN_NAME_MAX = 571 -_SC_THREAD_KEYS_MAX = 572 -_SC_THREAD_STACK_MIN = 573 -_SC_THREAD_THREADS_MAX = 574 -_SC_TTY_NAME_MAX = 575 -_SC_THREADS = 576 -_SC_THREAD_ATTR_STACKADDR = 577 -_SC_THREAD_ATTR_STACKSIZE = 578 -_SC_THREAD_PRIORITY_SCHEDULING = 579 -_SC_THREAD_PRIO_INHERIT = 580 -_SC_THREAD_PRIO_PROTECT = 581 -_SC_THREAD_PROCESS_SHARED = 582 -_SC_THREAD_SAFE_FUNCTIONS = 583 -_SC_XOPEN_LEGACY = 717 -_SC_XOPEN_REALTIME = 718 -_SC_XOPEN_REALTIME_THREADS = 719 -_SC_XBS5_ILP32_OFF32 = 720 -_SC_XBS5_ILP32_OFFBIG = 721 -_SC_XBS5_LP64_OFF64 = 722 -_SC_XBS5_LPBIG_OFFBIG = 723 -_PC_LINK_MAX = 1 -_PC_MAX_CANON = 2 -_PC_MAX_INPUT = 3 -_PC_NAME_MAX = 4 -_PC_PATH_MAX = 5 -_PC_PIPE_BUF = 6 -_PC_NO_TRUNC = 7 -_PC_VDISABLE = 8 -_PC_CHOWN_RESTRICTED = 9 -_PC_ASYNC_IO = 10 -_PC_PRIO_IO = 11 -_PC_SYNC_IO = 12 -_PC_FILESIZEBITS = 67 -_PC_LAST = 67 -_POSIX_VERSION = 199506 -_POSIX2_VERSION = 199209 -_POSIX2_C_VERSION = 199209 -_XOPEN_XCU_VERSION = 4 -_XOPEN_REALTIME = 1 -_XOPEN_ENH_I18N = 1 -_XOPEN_SHM = 1 -_POSIX2_C_BIND = 1 -_POSIX2_CHAR_TERM = 1 -_POSIX2_LOCALEDEF = 1 -_POSIX2_C_DEV = 1 -_POSIX2_SW_DEV = 1 -_POSIX2_UPE = 1 - -# Included from sys/mutex.h -from TYPES import * -def MUTEX_HELD(x): return (mutex_owned(x)) - - -# Included from sys/rwlock.h -from TYPES import * -def RW_READ_HELD(x): return (rw_read_held((x))) - -def RW_WRITE_HELD(x): return (rw_write_held((x))) - -def RW_LOCK_HELD(x): return (rw_lock_held((x))) - -def RW_ISWRITER(x): return (rw_iswriter(x)) - - -# Included from sys/semaphore.h - -# Included from sys/thread.h -from TYPES import * - -# Included from sys/klwp.h -from TYPES import * - -# Included from sys/condvar.h -from TYPES import * - -# Included from sys/time.h - -# Included from sys/types32.h - -# Included from sys/int_types.h -TIME32_MAX = INT32_MAX -TIME32_MIN = INT32_MIN -def TIMEVAL_OVERFLOW(tv): return \ - -from TYPES import * -DST_NONE = 0 -DST_USA = 1 -DST_AUST = 2 -DST_WET = 3 -DST_MET = 4 -DST_EET = 5 -DST_CAN = 6 -DST_GB = 7 -DST_RUM = 8 -DST_TUR = 9 -DST_AUSTALT = 10 -ITIMER_REAL = 0 -ITIMER_VIRTUAL = 1 -ITIMER_PROF = 2 -ITIMER_REALPROF = 3 -def ITIMERVAL_OVERFLOW(itv): return \ - -SEC = 1 -MILLISEC = 1000 -MICROSEC = 1000000 -NANOSEC = 1000000000 - -# Included from sys/time_impl.h -def TIMESPEC_OVERFLOW(ts): return \ - -def ITIMERSPEC_OVERFLOW(it): return \ - -__CLOCK_REALTIME0 = 0 -CLOCK_VIRTUAL = 1 -CLOCK_PROF = 2 -__CLOCK_REALTIME3 = 3 -CLOCK_HIGHRES = 4 -CLOCK_MAX = 5 -CLOCK_REALTIME = __CLOCK_REALTIME3 -CLOCK_REALTIME = __CLOCK_REALTIME0 -TIMER_RELTIME = 0x0 -TIMER_ABSTIME = 0x1 -def TICK_TO_SEC(tick): return ((tick) / hz) - -def SEC_TO_TICK(sec): return ((sec) * hz) - -def TICK_TO_MSEC(tick): return \ - -def MSEC_TO_TICK(msec): return \ - -def MSEC_TO_TICK_ROUNDUP(msec): return \ - -def TICK_TO_USEC(tick): return ((tick) * usec_per_tick) - -def USEC_TO_TICK(usec): return ((usec) / usec_per_tick) - -def USEC_TO_TICK_ROUNDUP(usec): return \ - -def TICK_TO_NSEC(tick): return ((tick) * nsec_per_tick) - -def NSEC_TO_TICK(nsec): return ((nsec) / nsec_per_tick) - -def NSEC_TO_TICK_ROUNDUP(nsec): return \ - -def TIMEVAL_TO_TICK(tvp): return \ - -def TIMESTRUC_TO_TICK(tsp): return \ - - -# Included from time.h -from TYPES import * - -# Included from iso/time_iso.h -NULL = 0 -NULL = 0 -CLOCKS_PER_SEC = 1000000 - -# Included from sys/select.h -FD_SETSIZE = 65536 -FD_SETSIZE = 1024 -_NBBY = 8 -NBBY = _NBBY -def FD_ZERO(p): return bzero((p), sizeof (*(p))) - - -# Included from sys/signal.h - -# Included from sys/iso/signal_iso.h -SIGHUP = 1 -SIGINT = 2 -SIGQUIT = 3 -SIGILL = 4 -SIGTRAP = 5 -SIGIOT = 6 -SIGABRT = 6 -SIGEMT = 7 -SIGFPE = 8 -SIGKILL = 9 -SIGBUS = 10 -SIGSEGV = 11 -SIGSYS = 12 -SIGPIPE = 13 -SIGALRM = 14 -SIGTERM = 15 -SIGUSR1 = 16 -SIGUSR2 = 17 -SIGCLD = 18 -SIGCHLD = 18 -SIGPWR = 19 -SIGWINCH = 20 -SIGURG = 21 -SIGPOLL = 22 -SIGIO = SIGPOLL -SIGSTOP = 23 -SIGTSTP = 24 -SIGCONT = 25 -SIGTTIN = 26 -SIGTTOU = 27 -SIGVTALRM = 28 -SIGPROF = 29 -SIGXCPU = 30 -SIGXFSZ = 31 -SIGWAITING = 32 -SIGLWP = 33 -SIGFREEZE = 34 -SIGTHAW = 35 -SIGCANCEL = 36 -SIGLOST = 37 -_SIGRTMIN = 38 -_SIGRTMAX = 45 -SIG_BLOCK = 1 -SIG_UNBLOCK = 2 -SIG_SETMASK = 3 -SIGNO_MASK = 0xFF -SIGDEFER = 0x100 -SIGHOLD = 0x200 -SIGRELSE = 0x400 -SIGIGNORE = 0x800 -SIGPAUSE = 0x1000 - -# Included from sys/siginfo.h -from TYPES import * -SIGEV_NONE = 1 -SIGEV_SIGNAL = 2 -SIGEV_THREAD = 3 -SI_NOINFO = 32767 -SI_USER = 0 -SI_LWP = (-1) -SI_QUEUE = (-2) -SI_TIMER = (-3) -SI_ASYNCIO = (-4) -SI_MESGQ = (-5) - -# Included from sys/machsig.h -ILL_ILLOPC = 1 -ILL_ILLOPN = 2 -ILL_ILLADR = 3 -ILL_ILLTRP = 4 -ILL_PRVOPC = 5 -ILL_PRVREG = 6 -ILL_COPROC = 7 -ILL_BADSTK = 8 -NSIGILL = 8 -EMT_TAGOVF = 1 -EMT_CPCOVF = 2 -NSIGEMT = 2 -FPE_INTDIV = 1 -FPE_INTOVF = 2 -FPE_FLTDIV = 3 -FPE_FLTOVF = 4 -FPE_FLTUND = 5 -FPE_FLTRES = 6 -FPE_FLTINV = 7 -FPE_FLTSUB = 8 -NSIGFPE = 8 -SEGV_MAPERR = 1 -SEGV_ACCERR = 2 -NSIGSEGV = 2 -BUS_ADRALN = 1 -BUS_ADRERR = 2 -BUS_OBJERR = 3 -NSIGBUS = 3 -TRAP_BRKPT = 1 -TRAP_TRACE = 2 -TRAP_RWATCH = 3 -TRAP_WWATCH = 4 -TRAP_XWATCH = 5 -NSIGTRAP = 5 -CLD_EXITED = 1 -CLD_KILLED = 2 -CLD_DUMPED = 3 -CLD_TRAPPED = 4 -CLD_STOPPED = 5 -CLD_CONTINUED = 6 -NSIGCLD = 6 -POLL_IN = 1 -POLL_OUT = 2 -POLL_MSG = 3 -POLL_ERR = 4 -POLL_PRI = 5 -POLL_HUP = 6 -NSIGPOLL = 6 -PROF_SIG = 1 -NSIGPROF = 1 -SI_MAXSZ = 256 -SI_MAXSZ = 128 - -# Included from sys/time_std_impl.h -from TYPES import * -SI32_MAXSZ = 128 -def SI_CANQUEUE(c): return ((c) <= SI_QUEUE) - -SA_NOCLDSTOP = 0x00020000 -SA_ONSTACK = 0x00000001 -SA_RESETHAND = 0x00000002 -SA_RESTART = 0x00000004 -SA_SIGINFO = 0x00000008 -SA_NODEFER = 0x00000010 -SA_NOCLDWAIT = 0x00010000 -SA_WAITSIG = 0x00010000 -NSIG = 46 -MAXSIG = 45 -S_SIGNAL = 1 -S_SIGSET = 2 -S_SIGACTION = 3 -S_NONE = 4 -MINSIGSTKSZ = 2048 -SIGSTKSZ = 8192 -SS_ONSTACK = 0x00000001 -SS_DISABLE = 0x00000002 -SN_PROC = 1 -SN_CANCEL = 2 -SN_SEND = 3 - -# Included from sys/ucontext.h -from TYPES import * - -# Included from sys/regset.h -REG_CCR = (0) -REG_PSR = (0) -REG_PSR = (0) -REG_PC = (1) -REG_nPC = (2) -REG_Y = (3) -REG_G1 = (4) -REG_G2 = (5) -REG_G3 = (6) -REG_G4 = (7) -REG_G5 = (8) -REG_G6 = (9) -REG_G7 = (10) -REG_O0 = (11) -REG_O1 = (12) -REG_O2 = (13) -REG_O3 = (14) -REG_O4 = (15) -REG_O5 = (16) -REG_O6 = (17) -REG_O7 = (18) -REG_ASI = (19) -REG_FPRS = (20) -REG_PS = REG_PSR -REG_SP = REG_O6 -REG_R0 = REG_O0 -REG_R1 = REG_O1 -_NGREG = 21 -_NGREG = 19 -NGREG = _NGREG -_NGREG32 = 19 -_NGREG64 = 21 -SPARC_MAXREGWINDOW = 31 -MAXFPQ = 16 -XRS_ID = 0x78727300 - -# Included from v7/sys/privregs.h - -# Included from v7/sys/psr.h -PSR_CWP = 0x0000001F -PSR_ET = 0x00000020 -PSR_PS = 0x00000040 -PSR_S = 0x00000080 -PSR_PIL = 0x00000F00 -PSR_EF = 0x00001000 -PSR_EC = 0x00002000 -PSR_RSV = 0x000FC000 -PSR_ICC = 0x00F00000 -PSR_C = 0x00100000 -PSR_V = 0x00200000 -PSR_Z = 0x00400000 -PSR_N = 0x00800000 -PSR_VER = 0x0F000000 -PSR_IMPL = 0xF0000000 -PSL_ALLCC = PSR_ICC -PSL_USER = (PSR_S) -PSL_USERMASK = (PSR_ICC) -PSL_UBITS = (PSR_ICC|PSR_EF) -def USERMODE(ps): return (((ps) & PSR_PS) == 0) - - -# Included from sys/fsr.h -FSR_CEXC = 0x0000001f -FSR_AEXC = 0x000003e0 -FSR_FCC = 0x00000c00 -FSR_PR = 0x00001000 -FSR_QNE = 0x00002000 -FSR_FTT = 0x0001c000 -FSR_VER = 0x000e0000 -FSR_TEM = 0x0f800000 -FSR_RP = 0x30000000 -FSR_RD = 0xc0000000 -FSR_VER_SHIFT = 17 -FSR_FCC1 = 0x00000003 -FSR_FCC2 = 0x0000000C -FSR_FCC3 = 0x00000030 -FSR_CEXC_NX = 0x00000001 -FSR_CEXC_DZ = 0x00000002 -FSR_CEXC_UF = 0x00000004 -FSR_CEXC_OF = 0x00000008 -FSR_CEXC_NV = 0x00000010 -FSR_AEXC_NX = (0x1 << 5) -FSR_AEXC_DZ = (0x2 << 5) -FSR_AEXC_UF = (0x4 << 5) -FSR_AEXC_OF = (0x8 << 5) -FSR_AEXC_NV = (0x10 << 5) -FTT_NONE = 0 -FTT_IEEE = 1 -FTT_UNFIN = 2 -FTT_UNIMP = 3 -FTT_SEQ = 4 -FTT_ALIGN = 5 -FTT_DFAULT = 6 -FSR_FTT_SHIFT = 14 -FSR_FTT_IEEE = (FTT_IEEE << FSR_FTT_SHIFT) -FSR_FTT_UNFIN = (FTT_UNFIN << FSR_FTT_SHIFT) -FSR_FTT_UNIMP = (FTT_UNIMP << FSR_FTT_SHIFT) -FSR_FTT_SEQ = (FTT_SEQ << FSR_FTT_SHIFT) -FSR_FTT_ALIGN = (FTT_ALIGN << FSR_FTT_SHIFT) -FSR_FTT_DFAULT = (FTT_DFAULT << FSR_FTT_SHIFT) -FSR_TEM_NX = (0x1 << 23) -FSR_TEM_DZ = (0x2 << 23) -FSR_TEM_UF = (0x4 << 23) -FSR_TEM_OF = (0x8 << 23) -FSR_TEM_NV = (0x10 << 23) -RP_DBLEXT = 0 -RP_SINGLE = 1 -RP_DOUBLE = 2 -RP_RESERVED = 3 -RD_NEAR = 0 -RD_ZER0 = 1 -RD_POSINF = 2 -RD_NEGINF = 3 -FPRS_DL = 0x1 -FPRS_DU = 0x2 -FPRS_FEF = 0x4 -PIL_MAX = 0xf -def SAVE_GLOBALS(RP): return \ - -def RESTORE_GLOBALS(RP): return \ - -def SAVE_OUTS(RP): return \ - -def RESTORE_OUTS(RP): return \ - -def SAVE_WINDOW(SBP): return \ - -def RESTORE_WINDOW(SBP): return \ - -def STORE_FPREGS(FP): return \ - -def LOAD_FPREGS(FP): return \ - -_SPARC_MAXREGWINDOW = 31 -_XRS_ID = 0x78727300 -GETCONTEXT = 0 -SETCONTEXT = 1 -UC_SIGMASK = 0o01 -UC_STACK = 0o02 -UC_CPU = 0o04 -UC_MAU = 0o10 -UC_FPU = UC_MAU -UC_INTR = 0o20 -UC_ASR = 0o40 -UC_MCONTEXT = (UC_CPU|UC_FPU|UC_ASR) -UC_ALL = (UC_SIGMASK|UC_STACK|UC_MCONTEXT) -_SIGQUEUE_MAX = 32 -_SIGNOTIFY_MAX = 32 - -# Included from sys/pcb.h -INSTR_VALID = 0x02 -NORMAL_STEP = 0x04 -WATCH_STEP = 0x08 -CPC_OVERFLOW = 0x10 -ASYNC_HWERR = 0x20 -STEP_NONE = 0 -STEP_REQUESTED = 1 -STEP_ACTIVE = 2 -STEP_WASACTIVE = 3 - -# Included from sys/msacct.h -LMS_USER = 0 -LMS_SYSTEM = 1 -LMS_TRAP = 2 -LMS_TFAULT = 3 -LMS_DFAULT = 4 -LMS_KFAULT = 5 -LMS_USER_LOCK = 6 -LMS_SLEEP = 7 -LMS_WAIT_CPU = 8 -LMS_STOPPED = 9 -NMSTATES = 10 - -# Included from sys/lwp.h - -# Included from sys/synch.h -from TYPES import * -USYNC_THREAD = 0x00 -USYNC_PROCESS = 0x01 -LOCK_NORMAL = 0x00 -LOCK_ERRORCHECK = 0x02 -LOCK_RECURSIVE = 0x04 -USYNC_PROCESS_ROBUST = 0x08 -LOCK_PRIO_NONE = 0x00 -LOCK_PRIO_INHERIT = 0x10 -LOCK_PRIO_PROTECT = 0x20 -LOCK_STALL_NP = 0x00 -LOCK_ROBUST_NP = 0x40 -LOCK_OWNERDEAD = 0x1 -LOCK_NOTRECOVERABLE = 0x2 -LOCK_INITED = 0x4 -LOCK_UNMAPPED = 0x8 -LWP_DETACHED = 0x00000040 -LWP_SUSPENDED = 0x00000080 -__LWP_ASLWP = 0x00000100 -MAXSYSARGS = 8 -NORMALRETURN = 0 -JUSTRETURN = 1 -LWP_USER = 0x01 -LWP_SYS = 0x02 -TS_FREE = 0x00 -TS_SLEEP = 0x01 -TS_RUN = 0x02 -TS_ONPROC = 0x04 -TS_ZOMB = 0x08 -TS_STOPPED = 0x10 -T_INTR_THREAD = 0x0001 -T_WAKEABLE = 0x0002 -T_TOMASK = 0x0004 -T_TALLOCSTK = 0x0008 -T_WOULDBLOCK = 0x0020 -T_DONTBLOCK = 0x0040 -T_DONTPEND = 0x0080 -T_SYS_PROF = 0x0100 -T_WAITCVSEM = 0x0200 -T_WATCHPT = 0x0400 -T_PANIC = 0x0800 -TP_HOLDLWP = 0x0002 -TP_TWAIT = 0x0004 -TP_LWPEXIT = 0x0008 -TP_PRSTOP = 0x0010 -TP_CHKPT = 0x0020 -TP_EXITLWP = 0x0040 -TP_PRVSTOP = 0x0080 -TP_MSACCT = 0x0100 -TP_STOPPING = 0x0200 -TP_WATCHPT = 0x0400 -TP_PAUSE = 0x0800 -TP_CHANGEBIND = 0x1000 -TS_LOAD = 0x0001 -TS_DONT_SWAP = 0x0002 -TS_SWAPENQ = 0x0004 -TS_ON_SWAPQ = 0x0008 -TS_CSTART = 0x0100 -TS_UNPAUSE = 0x0200 -TS_XSTART = 0x0400 -TS_PSTART = 0x0800 -TS_RESUME = 0x1000 -TS_CREATE = 0x2000 -TS_ALLSTART = \ - (TS_CSTART|TS_UNPAUSE|TS_XSTART|TS_PSTART|TS_RESUME|TS_CREATE) -def CPR_VSTOPPED(t): return \ - -def THREAD_TRANSITION(tp): return thread_transition(tp); - -def THREAD_STOP(tp): return \ - -def THREAD_ZOMB(tp): return THREAD_SET_STATE(tp, TS_ZOMB, NULL) - -def SEMA_HELD(x): return (sema_held((x))) - -NO_LOCKS_HELD = 1 -NO_COMPETING_THREADS = 1 -FMNAMESZ = 8 - -# Included from sys/systm.h -from TYPES import * - -# Included from sys/proc.h - -# Included from sys/cred.h - -# Included from sys/user.h -from TYPES import * - -# Included from sys/resource.h -from TYPES import * -PRIO_PROCESS = 0 -PRIO_PGRP = 1 -PRIO_USER = 2 -RLIMIT_CPU = 0 -RLIMIT_FSIZE = 1 -RLIMIT_DATA = 2 -RLIMIT_STACK = 3 -RLIMIT_CORE = 4 -RLIMIT_NOFILE = 5 -RLIMIT_VMEM = 6 -RLIMIT_AS = RLIMIT_VMEM -RLIM_NLIMITS = 7 -RLIM_INFINITY = (-3) -RLIM_SAVED_MAX = (-2) -RLIM_SAVED_CUR = (-1) -RLIM_INFINITY = 0x7fffffff -RLIM_SAVED_MAX = 0x7ffffffe -RLIM_SAVED_CUR = 0x7ffffffd -RLIM32_INFINITY = 0x7fffffff -RLIM32_SAVED_MAX = 0x7ffffffe -RLIM32_SAVED_CUR = 0x7ffffffd - -# Included from sys/model.h - -# Included from sys/debug.h -def ASSERT64(x): return ASSERT(x) - -def ASSERT32(x): return ASSERT(x) - -DATAMODEL_MASK = 0x0FF00000 -DATAMODEL_ILP32 = 0x00100000 -DATAMODEL_LP64 = 0x00200000 -DATAMODEL_NONE = 0 -DATAMODEL_NATIVE = DATAMODEL_LP64 -DATAMODEL_NATIVE = DATAMODEL_ILP32 -def STRUCT_SIZE(handle): return \ - -def STRUCT_BUF(handle): return ((handle).ptr.m64) - -def SIZEOF_PTR(umodel): return \ - -def STRUCT_SIZE(handle): return (sizeof (*(handle).ptr)) - -def STRUCT_BUF(handle): return ((handle).ptr) - -def SIZEOF_PTR(umodel): return sizeof (caddr_t) - -def lwp_getdatamodel(t): return DATAMODEL_ILP32 - -RUSAGE_SELF = 0 -RUSAGE_CHILDREN = -1 - -# Included from sys/auxv.h -AT_NULL = 0 -AT_IGNORE = 1 -AT_EXECFD = 2 -AT_PHDR = 3 -AT_PHENT = 4 -AT_PHNUM = 5 -AT_PAGESZ = 6 -AT_BASE = 7 -AT_FLAGS = 8 -AT_ENTRY = 9 -AT_DCACHEBSIZE = 10 -AT_ICACHEBSIZE = 11 -AT_UCACHEBSIZE = 12 -AT_SUN_UID = 2000 -AT_SUN_RUID = 2001 -AT_SUN_GID = 2002 -AT_SUN_RGID = 2003 -AT_SUN_LDELF = 2004 -AT_SUN_LDSHDR = 2005 -AT_SUN_LDNAME = 2006 -AT_SUN_LPAGESZ = 2007 -AT_SUN_PLATFORM = 2008 -AT_SUN_HWCAP = 2009 -AT_SUN_IFLUSH = 2010 -AT_SUN_CPU = 2011 -AT_SUN_EMUL_ENTRY = 2012 -AT_SUN_EMUL_EXECFD = 2013 -AT_SUN_EXECNAME = 2014 -AT_SUN_MMU = 2015 - -# Included from sys/errno.h -EPERM = 1 -ENOENT = 2 -ESRCH = 3 -EINTR = 4 -EIO = 5 -ENXIO = 6 -E2BIG = 7 -ENOEXEC = 8 -EBADF = 9 -ECHILD = 10 -EAGAIN = 11 -ENOMEM = 12 -EACCES = 13 -EFAULT = 14 -ENOTBLK = 15 -EBUSY = 16 -EEXIST = 17 -EXDEV = 18 -ENODEV = 19 -ENOTDIR = 20 -EISDIR = 21 -EINVAL = 22 -ENFILE = 23 -EMFILE = 24 -ENOTTY = 25 -ETXTBSY = 26 -EFBIG = 27 -ENOSPC = 28 -ESPIPE = 29 -EROFS = 30 -EMLINK = 31 -EPIPE = 32 -EDOM = 33 -ERANGE = 34 -ENOMSG = 35 -EIDRM = 36 -ECHRNG = 37 -EL2NSYNC = 38 -EL3HLT = 39 -EL3RST = 40 -ELNRNG = 41 -EUNATCH = 42 -ENOCSI = 43 -EL2HLT = 44 -EDEADLK = 45 -ENOLCK = 46 -ECANCELED = 47 -ENOTSUP = 48 -EDQUOT = 49 -EBADE = 50 -EBADR = 51 -EXFULL = 52 -ENOANO = 53 -EBADRQC = 54 -EBADSLT = 55 -EDEADLOCK = 56 -EBFONT = 57 -EOWNERDEAD = 58 -ENOTRECOVERABLE = 59 -ENOSTR = 60 -ENODATA = 61 -ETIME = 62 -ENOSR = 63 -ENONET = 64 -ENOPKG = 65 -EREMOTE = 66 -ENOLINK = 67 -EADV = 68 -ESRMNT = 69 -ECOMM = 70 -EPROTO = 71 -ELOCKUNMAPPED = 72 -ENOTACTIVE = 73 -EMULTIHOP = 74 -EBADMSG = 77 -ENAMETOOLONG = 78 -EOVERFLOW = 79 -ENOTUNIQ = 80 -EBADFD = 81 -EREMCHG = 82 -ELIBACC = 83 -ELIBBAD = 84 -ELIBSCN = 85 -ELIBMAX = 86 -ELIBEXEC = 87 -EILSEQ = 88 -ENOSYS = 89 -ELOOP = 90 -ERESTART = 91 -ESTRPIPE = 92 -ENOTEMPTY = 93 -EUSERS = 94 -ENOTSOCK = 95 -EDESTADDRREQ = 96 -EMSGSIZE = 97 -EPROTOTYPE = 98 -ENOPROTOOPT = 99 -EPROTONOSUPPORT = 120 -ESOCKTNOSUPPORT = 121 -EOPNOTSUPP = 122 -EPFNOSUPPORT = 123 -EAFNOSUPPORT = 124 -EADDRINUSE = 125 -EADDRNOTAVAIL = 126 -ENETDOWN = 127 -ENETUNREACH = 128 -ENETRESET = 129 -ECONNABORTED = 130 -ECONNRESET = 131 -ENOBUFS = 132 -EISCONN = 133 -ENOTCONN = 134 -ESHUTDOWN = 143 -ETOOMANYREFS = 144 -ETIMEDOUT = 145 -ECONNREFUSED = 146 -EHOSTDOWN = 147 -EHOSTUNREACH = 148 -EWOULDBLOCK = EAGAIN -EALREADY = 149 -EINPROGRESS = 150 -ESTALE = 151 -PSARGSZ = 80 -PSCOMSIZ = 14 -MAXCOMLEN = 16 -__KERN_NAUXV_IMPL = 19 -__KERN_NAUXV_IMPL = 21 -__KERN_NAUXV_IMPL = 21 -PSARGSZ = 80 - -# Included from sys/watchpoint.h -from TYPES import * - -# Included from vm/seg_enum.h - -# Included from sys/copyops.h -from TYPES import * - -# Included from sys/buf.h - -# Included from sys/kstat.h -from TYPES import * -KSTAT_STRLEN = 31 -def KSTAT_ENTER(k): return \ - -def KSTAT_EXIT(k): return \ - -KSTAT_TYPE_RAW = 0 -KSTAT_TYPE_NAMED = 1 -KSTAT_TYPE_INTR = 2 -KSTAT_TYPE_IO = 3 -KSTAT_TYPE_TIMER = 4 -KSTAT_NUM_TYPES = 5 -KSTAT_FLAG_VIRTUAL = 0x01 -KSTAT_FLAG_VAR_SIZE = 0x02 -KSTAT_FLAG_WRITABLE = 0x04 -KSTAT_FLAG_PERSISTENT = 0x08 -KSTAT_FLAG_DORMANT = 0x10 -KSTAT_FLAG_INVALID = 0x20 -KSTAT_READ = 0 -KSTAT_WRITE = 1 -KSTAT_DATA_CHAR = 0 -KSTAT_DATA_INT32 = 1 -KSTAT_DATA_UINT32 = 2 -KSTAT_DATA_INT64 = 3 -KSTAT_DATA_UINT64 = 4 -KSTAT_DATA_LONG = KSTAT_DATA_INT32 -KSTAT_DATA_ULONG = KSTAT_DATA_UINT32 -KSTAT_DATA_LONG = KSTAT_DATA_INT64 -KSTAT_DATA_ULONG = KSTAT_DATA_UINT64 -KSTAT_DATA_LONG = 7 -KSTAT_DATA_ULONG = 8 -KSTAT_DATA_LONGLONG = KSTAT_DATA_INT64 -KSTAT_DATA_ULONGLONG = KSTAT_DATA_UINT64 -KSTAT_DATA_FLOAT = 5 -KSTAT_DATA_DOUBLE = 6 -KSTAT_INTR_HARD = 0 -KSTAT_INTR_SOFT = 1 -KSTAT_INTR_WATCHDOG = 2 -KSTAT_INTR_SPURIOUS = 3 -KSTAT_INTR_MULTSVC = 4 -KSTAT_NUM_INTRS = 5 -B_BUSY = 0x0001 -B_DONE = 0x0002 -B_ERROR = 0x0004 -B_PAGEIO = 0x0010 -B_PHYS = 0x0020 -B_READ = 0x0040 -B_WRITE = 0x0100 -B_KERNBUF = 0x0008 -B_WANTED = 0x0080 -B_AGE = 0x000200 -B_ASYNC = 0x000400 -B_DELWRI = 0x000800 -B_STALE = 0x001000 -B_DONTNEED = 0x002000 -B_REMAPPED = 0x004000 -B_FREE = 0x008000 -B_INVAL = 0x010000 -B_FORCE = 0x020000 -B_HEAD = 0x040000 -B_NOCACHE = 0x080000 -B_TRUNC = 0x100000 -B_SHADOW = 0x200000 -B_RETRYWRI = 0x400000 -def notavail(bp): return \ - -def BWRITE(bp): return \ - -def BWRITE2(bp): return \ - - -# Included from sys/aio_req.h - -# Included from sys/uio.h -from TYPES import * -WP_NOWATCH = 0x01 -WP_SETPROT = 0x02 - -# Included from sys/timer.h -from TYPES import * -_TIMER_MAX = 32 -ITLK_LOCKED = 0x01 -ITLK_WANTED = 0x02 -ITLK_REMOVE = 0x04 -IT_PERLWP = 0x01 -IT_SIGNAL = 0x02 - -# Included from sys/utrap.h -UT_INSTRUCTION_DISABLED = 1 -UT_INSTRUCTION_ERROR = 2 -UT_INSTRUCTION_PROTECTION = 3 -UT_ILLTRAP_INSTRUCTION = 4 -UT_ILLEGAL_INSTRUCTION = 5 -UT_PRIVILEGED_OPCODE = 6 -UT_FP_DISABLED = 7 -UT_FP_EXCEPTION_IEEE_754 = 8 -UT_FP_EXCEPTION_OTHER = 9 -UT_TAG_OVERFLOW = 10 -UT_DIVISION_BY_ZERO = 11 -UT_DATA_EXCEPTION = 12 -UT_DATA_ERROR = 13 -UT_DATA_PROTECTION = 14 -UT_MEM_ADDRESS_NOT_ALIGNED = 15 -UT_PRIVILEGED_ACTION = 16 -UT_ASYNC_DATA_ERROR = 17 -UT_TRAP_INSTRUCTION_16 = 18 -UT_TRAP_INSTRUCTION_17 = 19 -UT_TRAP_INSTRUCTION_18 = 20 -UT_TRAP_INSTRUCTION_19 = 21 -UT_TRAP_INSTRUCTION_20 = 22 -UT_TRAP_INSTRUCTION_21 = 23 -UT_TRAP_INSTRUCTION_22 = 24 -UT_TRAP_INSTRUCTION_23 = 25 -UT_TRAP_INSTRUCTION_24 = 26 -UT_TRAP_INSTRUCTION_25 = 27 -UT_TRAP_INSTRUCTION_26 = 28 -UT_TRAP_INSTRUCTION_27 = 29 -UT_TRAP_INSTRUCTION_28 = 30 -UT_TRAP_INSTRUCTION_29 = 31 -UT_TRAP_INSTRUCTION_30 = 32 -UT_TRAP_INSTRUCTION_31 = 33 -UTRAP_V8P_FP_DISABLED = UT_FP_DISABLED -UTRAP_V8P_MEM_ADDRESS_NOT_ALIGNED = UT_MEM_ADDRESS_NOT_ALIGNED -UT_PRECISE_MAXTRAPS = 33 - -# Included from sys/refstr.h - -# Included from sys/task.h -from TYPES import * -TASK_NORMAL = 0x0 -TASK_FINAL = 0x1 -TASK_FINALITY = 0x1 - -# Included from sys/id_space.h -from TYPES import * - -# Included from sys/vmem.h -from TYPES import * -VM_SLEEP = 0x00000000 -VM_NOSLEEP = 0x00000001 -VM_PANIC = 0x00000002 -VM_KMFLAGS = 0x000000ff -VM_BESTFIT = 0x00000100 -VMEM_ALLOC = 0x01 -VMEM_FREE = 0x02 -VMEM_SPAN = 0x10 -ISP_NORMAL = 0x0 -ISP_RESERVE = 0x1 - -# Included from sys/exacct_impl.h -from TYPES import * - -# Included from sys/kmem.h -from TYPES import * -KM_SLEEP = 0x0000 -KM_NOSLEEP = 0x0001 -KM_PANIC = 0x0002 -KM_VMFLAGS = 0x00ff -KM_FLAGS = 0xffff -KMC_NOTOUCH = 0x00010000 -KMC_NODEBUG = 0x00020000 -KMC_NOMAGAZINE = 0x00040000 -KMC_NOHASH = 0x00080000 -KMC_QCACHE = 0x00100000 -_ISA_IA32 = 0 -_ISA_IA64 = 1 -SSLEEP = 1 -SRUN = 2 -SZOMB = 3 -SSTOP = 4 -SIDL = 5 -SONPROC = 6 -CLDPEND = 0x0001 -CLDCONT = 0x0002 -SSYS = 0x00000001 -STRC = 0x00000002 -SLOAD = 0x00000008 -SLOCK = 0x00000010 -SPREXEC = 0x00000020 -SPROCTR = 0x00000040 -SPRFORK = 0x00000080 -SKILLED = 0x00000100 -SULOAD = 0x00000200 -SRUNLCL = 0x00000400 -SBPTADJ = 0x00000800 -SKILLCL = 0x00001000 -SOWEUPC = 0x00002000 -SEXECED = 0x00004000 -SPASYNC = 0x00008000 -SJCTL = 0x00010000 -SNOWAIT = 0x00020000 -SVFORK = 0x00040000 -SVFWAIT = 0x00080000 -EXITLWPS = 0x00100000 -HOLDFORK = 0x00200000 -SWAITSIG = 0x00400000 -HOLDFORK1 = 0x00800000 -COREDUMP = 0x01000000 -SMSACCT = 0x02000000 -ASLWP = 0x04000000 -SPRLOCK = 0x08000000 -NOCD = 0x10000000 -HOLDWATCH = 0x20000000 -SMSFORK = 0x40000000 -SDOCORE = 0x80000000 -FORREAL = 0 -JUSTLOOKING = 1 -SUSPEND_NORMAL = 0 -SUSPEND_PAUSE = 1 -NOCLASS = (-1) - -# Included from sys/dditypes.h -DDI_DEVICE_ATTR_V0 = 0x0001 -DDI_NEVERSWAP_ACC = 0x00 -DDI_STRUCTURE_LE_ACC = 0x01 -DDI_STRUCTURE_BE_ACC = 0x02 -DDI_STRICTORDER_ACC = 0x00 -DDI_UNORDERED_OK_ACC = 0x01 -DDI_MERGING_OK_ACC = 0x02 -DDI_LOADCACHING_OK_ACC = 0x03 -DDI_STORECACHING_OK_ACC = 0x04 -DDI_DATA_SZ01_ACC = 1 -DDI_DATA_SZ02_ACC = 2 -DDI_DATA_SZ04_ACC = 4 -DDI_DATA_SZ08_ACC = 8 -VERS_ACCHDL = 0x0001 -DEVID_NONE = 0 -DEVID_SCSI3_WWN = 1 -DEVID_SCSI_SERIAL = 2 -DEVID_FAB = 3 -DEVID_ENCAP = 4 -DEVID_MAXTYPE = 4 - -# Included from sys/varargs.h - -# Included from sys/va_list.h -VA_ALIGN = 8 -def _ARGSIZEOF(t): return ((sizeof (t) + VA_ALIGN - 1) & ~(VA_ALIGN - 1)) - -VA_ALIGN = 8 -def _ARGSIZEOF(t): return ((sizeof (t) + VA_ALIGN - 1) & ~(VA_ALIGN - 1)) - -NSYSCALL = 256 -SE_32RVAL1 = 0x0 -SE_32RVAL2 = 0x1 -SE_64RVAL = 0x2 -SE_RVAL_MASK = 0x3 -SE_LOADABLE = 0x08 -SE_LOADED = 0x10 -SE_NOUNLOAD = 0x20 -SE_ARGC = 0x40 - -# Included from sys/devops.h -from TYPES import * - -# Included from sys/poll.h -POLLIN = 0x0001 -POLLPRI = 0x0002 -POLLOUT = 0x0004 -POLLRDNORM = 0x0040 -POLLWRNORM = POLLOUT -POLLRDBAND = 0x0080 -POLLWRBAND = 0x0100 -POLLNORM = POLLRDNORM -POLLERR = 0x0008 -POLLHUP = 0x0010 -POLLNVAL = 0x0020 -POLLREMOVE = 0x0800 -POLLRDDATA = 0x0200 -POLLNOERR = 0x0400 -POLLCLOSED = 0x8000 - -# Included from vm/as.h - -# Included from vm/seg.h - -# Included from sys/vnode.h -from TYPES import * -VROOT = 0x01 -VNOCACHE = 0x02 -VNOMAP = 0x04 -VDUP = 0x08 -VNOSWAP = 0x10 -VNOMOUNT = 0x20 -VISSWAP = 0x40 -VSWAPLIKE = 0x80 -VVFSLOCK = 0x100 -VVFSWAIT = 0x200 -VVMLOCK = 0x400 -VDIROPEN = 0x800 -VVMEXEC = 0x1000 -VPXFS = 0x2000 -AT_TYPE = 0x0001 -AT_MODE = 0x0002 -AT_UID = 0x0004 -AT_GID = 0x0008 -AT_FSID = 0x0010 -AT_NODEID = 0x0020 -AT_NLINK = 0x0040 -AT_SIZE = 0x0080 -AT_ATIME = 0x0100 -AT_MTIME = 0x0200 -AT_CTIME = 0x0400 -AT_RDEV = 0x0800 -AT_BLKSIZE = 0x1000 -AT_NBLOCKS = 0x2000 -AT_VCODE = 0x4000 -AT_ALL = (AT_TYPE|AT_MODE|AT_UID|AT_GID|AT_FSID|AT_NODEID|\ - AT_NLINK|AT_SIZE|AT_ATIME|AT_MTIME|AT_CTIME|\ - AT_RDEV|AT_BLKSIZE|AT_NBLOCKS|AT_VCODE) -AT_STAT = (AT_MODE|AT_UID|AT_GID|AT_FSID|AT_NODEID|AT_NLINK|\ - AT_SIZE|AT_ATIME|AT_MTIME|AT_CTIME|AT_RDEV) -AT_TIMES = (AT_ATIME|AT_MTIME|AT_CTIME) -AT_NOSET = (AT_NLINK|AT_RDEV|AT_FSID|AT_NODEID|AT_TYPE|\ - AT_BLKSIZE|AT_NBLOCKS|AT_VCODE) -VSUID = 0o4000 -VSGID = 0o2000 -VSVTX = 0o1000 -VREAD = 0o0400 -VWRITE = 0o0200 -VEXEC = 0o0100 -MODEMASK = 0o7777 -PERMMASK = 0o0777 -def MANDMODE(mode): return (((mode) & (VSGID|(VEXEC>>3))) == VSGID) - -VSA_ACL = 0x0001 -VSA_ACLCNT = 0x0002 -VSA_DFACL = 0x0004 -VSA_DFACLCNT = 0x0008 -LOOKUP_DIR = 0x01 -DUMP_ALLOC = 0 -DUMP_FREE = 1 -DUMP_SCAN = 2 -ATTR_UTIME = 0x01 -ATTR_EXEC = 0x02 -ATTR_COMM = 0x04 -ATTR_HINT = 0x08 -ATTR_REAL = 0x10 - -# Included from vm/faultcode.h -FC_HWERR = 0x1 -FC_ALIGN = 0x2 -FC_OBJERR = 0x3 -FC_PROT = 0x4 -FC_NOMAP = 0x5 -FC_NOSUPPORT = 0x6 -def FC_MAKE_ERR(e): return (((e) << 8) | FC_OBJERR) - -def FC_CODE(fc): return ((fc) & 0xff) - -def FC_ERRNO(fc): return ((unsigned)(fc) >> 8) - - -# Included from vm/hat.h -from TYPES import * - -# Included from vm/page.h -PAGE_HASHAVELEN = 4 -PAGE_HASHVPSHIFT = 6 -PG_EXCL = 0x0001 -PG_WAIT = 0x0002 -PG_PHYSCONTIG = 0x0004 -PG_MATCH_COLOR = 0x0008 -PG_NORELOC = 0x0010 -PG_FREE_LIST = 1 -PG_CACHE_LIST = 2 -PG_LIST_TAIL = 0 -PG_LIST_HEAD = 1 -def page_next_raw(PP): return page_nextn_raw((PP), 1) - -PAGE_IO_INUSE = 0x1 -PAGE_IO_WANTED = 0x2 -PGREL_NOTREL = 0x1 -PGREL_CLEAN = 0x2 -PGREL_MOD = 0x3 -P_FREE = 0x80 -P_NORELOC = 0x40 -def PP_SETAGED(pp): return ASSERT(PP_ISAGED(pp)) - -HAT_FLAGS_RESV = 0xFF000000 -HAT_LOAD = 0x00 -HAT_LOAD_LOCK = 0x01 -HAT_LOAD_ADV = 0x04 -HAT_LOAD_CONTIG = 0x10 -HAT_LOAD_NOCONSIST = 0x20 -HAT_LOAD_SHARE = 0x40 -HAT_LOAD_REMAP = 0x80 -HAT_RELOAD_SHARE = 0x100 -HAT_PLAT_ATTR_MASK = 0xF00000 -HAT_PROT_MASK = 0x0F -HAT_NOFAULT = 0x10 -HAT_NOSYNC = 0x20 -HAT_STRICTORDER = 0x0000 -HAT_UNORDERED_OK = 0x0100 -HAT_MERGING_OK = 0x0200 -HAT_LOADCACHING_OK = 0x0300 -HAT_STORECACHING_OK = 0x0400 -HAT_ORDER_MASK = 0x0700 -HAT_NEVERSWAP = 0x0000 -HAT_STRUCTURE_BE = 0x1000 -HAT_STRUCTURE_LE = 0x2000 -HAT_ENDIAN_MASK = 0x3000 -HAT_COW = 0x0001 -HAT_UNLOAD = 0x00 -HAT_UNLOAD_NOSYNC = 0x02 -HAT_UNLOAD_UNLOCK = 0x04 -HAT_UNLOAD_OTHER = 0x08 -HAT_UNLOAD_UNMAP = 0x10 -HAT_SYNC_DONTZERO = 0x00 -HAT_SYNC_ZERORM = 0x01 -HAT_SYNC_STOPON_REF = 0x02 -HAT_SYNC_STOPON_MOD = 0x04 -HAT_SYNC_STOPON_RM = (HAT_SYNC_STOPON_REF | HAT_SYNC_STOPON_MOD) -HAT_DUP_ALL = 1 -HAT_DUP_COW = 2 -HAT_MAP = 0x00 -HAT_ADV_PGUNLOAD = 0x00 -HAT_FORCE_PGUNLOAD = 0x01 -P_MOD = 0x1 -P_REF = 0x2 -P_RO = 0x4 -def hat_ismod(pp): return (hat_page_getattr(pp, P_MOD)) - -def hat_isref(pp): return (hat_page_getattr(pp, P_REF)) - -def hat_isro(pp): return (hat_page_getattr(pp, P_RO)) - -def hat_setmod(pp): return (hat_page_setattr(pp, P_MOD)) - -def hat_setref(pp): return (hat_page_setattr(pp, P_REF)) - -def hat_setrefmod(pp): return (hat_page_setattr(pp, P_REF|P_MOD)) - -def hat_clrmod(pp): return (hat_page_clrattr(pp, P_MOD)) - -def hat_clrref(pp): return (hat_page_clrattr(pp, P_REF)) - -def hat_clrrefmod(pp): return (hat_page_clrattr(pp, P_REF|P_MOD)) - -def hat_page_is_mapped(pp): return (hat_page_getshare(pp)) - -HAT_DONTALLOC = 0 -HAT_ALLOC = 1 -HRM_SHIFT = 4 -HRM_BYTES = (1 << HRM_SHIFT) -HRM_PAGES = ((HRM_BYTES * NBBY) / 2) -HRM_PGPERBYTE = (NBBY/2) -HRM_PGBYTEMASK = (HRM_PGPERBYTE-1) -HRM_HASHSIZE = 0x200 -HRM_HASHMASK = (HRM_HASHSIZE - 1) -HRM_BLIST_INCR = 0x200 -HRM_SWSMONID = 1 -SSL_NLEVELS = 4 -SSL_BFACTOR = 4 -SSL_LOG2BF = 2 -SEGP_ASYNC_FLUSH = 0x1 -SEGP_FORCE_WIRED = 0x2 -SEGP_SUCCESS = 0 -SEGP_FAIL = 1 -def seg_pages(seg): return \ - -IE_NOMEM = -1 -AS_PAGLCK = 0x80 -AS_CLAIMGAP = 0x40 -AS_UNMAPWAIT = 0x20 -def AS_TYPE_64BIT(as_): return \ - -AS_LREP_LINKEDLIST = 0 -AS_LREP_SKIPLIST = 1 -AS_MUTATION_THRESH = 225 -AH_DIR = 0x1 -AH_LO = 0x0 -AH_HI = 0x1 -AH_CONTAIN = 0x2 - -# Included from sys/ddidmareq.h -DMA_UNIT_8 = 1 -DMA_UNIT_16 = 2 -DMA_UNIT_32 = 4 -DMALIM_VER0 = ((0x86000000) + 0) -DDI_DMA_FORCE_PHYSICAL = 0x0100 -DMA_ATTR_V0 = 0 -DMA_ATTR_VERSION = DMA_ATTR_V0 -DDI_DMA_CALLBACK_RUNOUT = 0 -DDI_DMA_CALLBACK_DONE = 1 -DDI_DMA_WRITE = 0x0001 -DDI_DMA_READ = 0x0002 -DDI_DMA_RDWR = (DDI_DMA_READ | DDI_DMA_WRITE) -DDI_DMA_REDZONE = 0x0004 -DDI_DMA_PARTIAL = 0x0008 -DDI_DMA_CONSISTENT = 0x0010 -DDI_DMA_EXCLUSIVE = 0x0020 -DDI_DMA_STREAMING = 0x0040 -DDI_DMA_SBUS_64BIT = 0x2000 -DDI_DMA_MAPPED = 0 -DDI_DMA_MAPOK = 0 -DDI_DMA_PARTIAL_MAP = 1 -DDI_DMA_DONE = 2 -DDI_DMA_NORESOURCES = -1 -DDI_DMA_NOMAPPING = -2 -DDI_DMA_TOOBIG = -3 -DDI_DMA_TOOSMALL = -4 -DDI_DMA_LOCKED = -5 -DDI_DMA_BADLIMITS = -6 -DDI_DMA_STALE = -7 -DDI_DMA_BADATTR = -8 -DDI_DMA_INUSE = -9 -DDI_DMA_SYNC_FORDEV = 0x0 -DDI_DMA_SYNC_FORCPU = 0x1 -DDI_DMA_SYNC_FORKERNEL = 0x2 - -# Included from sys/ddimapreq.h - -# Included from sys/mman.h -PROT_READ = 0x1 -PROT_WRITE = 0x2 -PROT_EXEC = 0x4 -PROT_USER = 0x8 -PROT_ZFOD = (PROT_READ | PROT_WRITE | PROT_EXEC | PROT_USER) -PROT_ALL = (PROT_READ | PROT_WRITE | PROT_EXEC | PROT_USER) -PROT_NONE = 0x0 -MAP_SHARED = 1 -MAP_PRIVATE = 2 -MAP_TYPE = 0xf -MAP_FIXED = 0x10 -MAP_NORESERVE = 0x40 -MAP_ANON = 0x100 -MAP_ANONYMOUS = MAP_ANON -MAP_RENAME = 0x20 -PROC_TEXT = (PROT_EXEC | PROT_READ) -PROC_DATA = (PROT_READ | PROT_WRITE | PROT_EXEC) -SHARED = 0x10 -PRIVATE = 0x20 -VALID_ATTR = (PROT_READ|PROT_WRITE|PROT_EXEC|SHARED|PRIVATE) -PROT_EXCL = 0x20 -_MAP_LOW32 = 0x80 -_MAP_NEW = 0x80000000 -from TYPES import * -MADV_NORMAL = 0 -MADV_RANDOM = 1 -MADV_SEQUENTIAL = 2 -MADV_WILLNEED = 3 -MADV_DONTNEED = 4 -MADV_FREE = 5 -MS_OLDSYNC = 0x0 -MS_SYNC = 0x4 -MS_ASYNC = 0x1 -MS_INVALIDATE = 0x2 -MC_SYNC = 1 -MC_LOCK = 2 -MC_UNLOCK = 3 -MC_ADVISE = 4 -MC_LOCKAS = 5 -MC_UNLOCKAS = 6 -MCL_CURRENT = 0x1 -MCL_FUTURE = 0x2 -DDI_MAP_VERSION = 0x0001 -DDI_MF_USER_MAPPING = 0x1 -DDI_MF_KERNEL_MAPPING = 0x2 -DDI_MF_DEVICE_MAPPING = 0x4 -DDI_ME_GENERIC = (-1) -DDI_ME_UNIMPLEMENTED = (-2) -DDI_ME_NORESOURCES = (-3) -DDI_ME_UNSUPPORTED = (-4) -DDI_ME_REGSPEC_RANGE = (-5) -DDI_ME_RNUMBER_RANGE = (-6) -DDI_ME_INVAL = (-7) - -# Included from sys/ddipropdefs.h -def CELLS_1275_TO_BYTES(n): return ((n) * PROP_1275_CELL_SIZE) - -def BYTES_TO_1275_CELLS(n): return ((n) / PROP_1275_CELL_SIZE) - -PH_FROM_PROM = 0x01 -DDI_PROP_SUCCESS = 0 -DDI_PROP_NOT_FOUND = 1 -DDI_PROP_UNDEFINED = 2 -DDI_PROP_NO_MEMORY = 3 -DDI_PROP_INVAL_ARG = 4 -DDI_PROP_BUF_TOO_SMALL = 5 -DDI_PROP_CANNOT_DECODE = 6 -DDI_PROP_CANNOT_ENCODE = 7 -DDI_PROP_END_OF_DATA = 8 -DDI_PROP_FOUND_1275 = 255 -PROP_1275_INT_SIZE = 4 -DDI_PROP_DONTPASS = 0x0001 -DDI_PROP_CANSLEEP = 0x0002 -DDI_PROP_SYSTEM_DEF = 0x0004 -DDI_PROP_NOTPROM = 0x0008 -DDI_PROP_DONTSLEEP = 0x0010 -DDI_PROP_STACK_CREATE = 0x0020 -DDI_PROP_UNDEF_IT = 0x0040 -DDI_PROP_HW_DEF = 0x0080 -DDI_PROP_TYPE_INT = 0x0100 -DDI_PROP_TYPE_STRING = 0x0200 -DDI_PROP_TYPE_BYTE = 0x0400 -DDI_PROP_TYPE_COMPOSITE = 0x0800 -DDI_PROP_TYPE_ANY = (DDI_PROP_TYPE_INT | \ - DDI_PROP_TYPE_STRING | \ - DDI_PROP_TYPE_BYTE | \ - DDI_PROP_TYPE_COMPOSITE) -DDI_PROP_TYPE_MASK = (DDI_PROP_TYPE_INT | \ - DDI_PROP_TYPE_STRING | \ - DDI_PROP_TYPE_BYTE | \ - DDI_PROP_TYPE_COMPOSITE) -DDI_RELATIVE_ADDRESSING = "relative-addressing" -DDI_GENERIC_ADDRESSING = "generic-addressing" - -# Included from sys/ddidevmap.h -KMEM_PAGEABLE = 0x100 -KMEM_NON_PAGEABLE = 0x200 -UMEM_LOCKED = 0x400 -UMEM_TRASH = 0x800 -DEVMAP_OPS_REV = 1 -DEVMAP_DEFAULTS = 0x00 -DEVMAP_MAPPING_INVALID = 0x01 -DEVMAP_ALLOW_REMAP = 0x02 -DEVMAP_USE_PAGESIZE = 0x04 -DEVMAP_SETUP_FLAGS = \ - (DEVMAP_MAPPING_INVALID | DEVMAP_ALLOW_REMAP | DEVMAP_USE_PAGESIZE) -DEVMAP_SETUP_DONE = 0x100 -DEVMAP_LOCK_INITED = 0x200 -DEVMAP_FAULTING = 0x400 -DEVMAP_LOCKED = 0x800 -DEVMAP_FLAG_LARGE = 0x1000 -DDI_UMEM_SLEEP = 0x0 -DDI_UMEM_NOSLEEP = 0x01 -DDI_UMEM_PAGEABLE = 0x02 -DDI_UMEM_TRASH = 0x04 -DDI_UMEMLOCK_READ = 0x01 -DDI_UMEMLOCK_WRITE = 0x02 - -# Included from sys/nexusdefs.h - -# Included from sys/nexusintr.h -BUSO_REV = 4 -BUSO_REV_3 = 3 -BUSO_REV_4 = 4 -DEVO_REV = 3 -CB_REV = 1 -DDI_IDENTIFIED = (0) -DDI_NOT_IDENTIFIED = (-1) -DDI_PROBE_FAILURE = ENXIO -DDI_PROBE_DONTCARE = 0 -DDI_PROBE_PARTIAL = 1 -DDI_PROBE_SUCCESS = 2 -MAPDEV_REV = 1 -from TYPES import * -D_NEW = 0x00 -_D_OLD = 0x01 -D_TAPE = 0x08 -D_MTSAFE = 0x0020 -_D_QNEXTLESS = 0x0040 -_D_MTOCSHARED = 0x0080 -D_MTOCEXCL = 0x0800 -D_MTPUTSHARED = 0x1000 -D_MTPERQ = 0x2000 -D_MTQPAIR = 0x4000 -D_MTPERMOD = 0x6000 -D_MTOUTPERIM = 0x8000 -_D_MTCBSHARED = 0x10000 -D_MTINNER_MOD = (D_MTPUTSHARED|_D_MTOCSHARED|_D_MTCBSHARED) -D_MTOUTER_MOD = (D_MTOCEXCL) -D_MP = D_MTSAFE -D_64BIT = 0x200 -D_SYNCSTR = 0x400 -D_DEVMAP = 0x100 -D_HOTPLUG = 0x4 -SNDZERO = 0x001 -SNDPIPE = 0x002 -RNORM = 0x000 -RMSGD = 0x001 -RMSGN = 0x002 -RMODEMASK = 0x003 -RPROTDAT = 0x004 -RPROTDIS = 0x008 -RPROTNORM = 0x010 -RPROTMASK = 0x01c -RFLUSHMASK = 0x020 -RFLUSHPCPROT = 0x020 -RERRNORM = 0x001 -RERRNONPERSIST = 0x002 -RERRMASK = (RERRNORM|RERRNONPERSIST) -WERRNORM = 0x004 -WERRNONPERSIST = 0x008 -WERRMASK = (WERRNORM|WERRNONPERSIST) -FLUSHR = 0x01 -FLUSHW = 0x02 -FLUSHRW = 0x03 -FLUSHBAND = 0x04 -MAPINOK = 0x01 -NOMAPIN = 0x02 -REMAPOK = 0x04 -NOREMAP = 0x08 -S_INPUT = 0x0001 -S_HIPRI = 0x0002 -S_OUTPUT = 0x0004 -S_MSG = 0x0008 -S_ERROR = 0x0010 -S_HANGUP = 0x0020 -S_RDNORM = 0x0040 -S_WRNORM = S_OUTPUT -S_RDBAND = 0x0080 -S_WRBAND = 0x0100 -S_BANDURG = 0x0200 -RS_HIPRI = 0x01 -STRUIO_POSTPONE = 0x08 -STRUIO_MAPIN = 0x10 -MSG_HIPRI = 0x01 -MSG_ANY = 0x02 -MSG_BAND = 0x04 -MSG_XPG4 = 0x08 -MSG_IPEEK = 0x10 -MSG_DISCARDTAIL = 0x20 -MSG_HOLDSIG = 0x40 -MSG_IGNERROR = 0x80 -MSG_DELAYERROR = 0x100 -MSG_IGNFLOW = 0x200 -MSG_NOMARK = 0x400 -MORECTL = 1 -MOREDATA = 2 -MUXID_ALL = (-1) -ANYMARK = 0x01 -LASTMARK = 0x02 -_INFTIM = -1 -INFTIM = _INFTIM diff --git a/Lib/plat-sunos5/TYPES.py b/Lib/plat-sunos5/TYPES.py deleted file mode 100644 index da4e6b1786..0000000000 --- a/Lib/plat-sunos5/TYPES.py +++ /dev/null @@ -1,313 +0,0 @@ -# Generated by h2py from /usr/include/sys/types.h - -# Included from sys/isa_defs.h -_CHAR_ALIGNMENT = 1 -_SHORT_ALIGNMENT = 2 -_INT_ALIGNMENT = 4 -_LONG_ALIGNMENT = 8 -_LONG_LONG_ALIGNMENT = 8 -_DOUBLE_ALIGNMENT = 8 -_LONG_DOUBLE_ALIGNMENT = 16 -_POINTER_ALIGNMENT = 8 -_MAX_ALIGNMENT = 16 -_ALIGNMENT_REQUIRED = 1 -_CHAR_ALIGNMENT = 1 -_SHORT_ALIGNMENT = 2 -_INT_ALIGNMENT = 4 -_LONG_ALIGNMENT = 4 -_LONG_LONG_ALIGNMENT = 4 -_DOUBLE_ALIGNMENT = 4 -_LONG_DOUBLE_ALIGNMENT = 4 -_POINTER_ALIGNMENT = 4 -_MAX_ALIGNMENT = 4 -_ALIGNMENT_REQUIRED = 0 -_CHAR_ALIGNMENT = 1 -_SHORT_ALIGNMENT = 2 -_INT_ALIGNMENT = 4 -_LONG_LONG_ALIGNMENT = 8 -_DOUBLE_ALIGNMENT = 8 -_ALIGNMENT_REQUIRED = 1 -_LONG_ALIGNMENT = 4 -_LONG_DOUBLE_ALIGNMENT = 8 -_POINTER_ALIGNMENT = 4 -_MAX_ALIGNMENT = 8 -_LONG_ALIGNMENT = 8 -_LONG_DOUBLE_ALIGNMENT = 16 -_POINTER_ALIGNMENT = 8 -_MAX_ALIGNMENT = 16 - -# Included from sys/feature_tests.h -_POSIX_C_SOURCE = 1 -_LARGEFILE64_SOURCE = 1 -_LARGEFILE_SOURCE = 1 -_FILE_OFFSET_BITS = 64 -_FILE_OFFSET_BITS = 32 -_POSIX_C_SOURCE = 199506 -_POSIX_PTHREAD_SEMANTICS = 1 -_XOPEN_VERSION = 500 -_XOPEN_VERSION = 4 -_XOPEN_VERSION = 3 - -# Included from sys/machtypes.h - -# Included from sys/inttypes.h - -# Included from sys/int_types.h - -# Included from sys/int_limits.h -INT8_MAX = (127) -INT16_MAX = (32767) -INT32_MAX = (2147483647) -INTMAX_MAX = INT32_MAX -INT_LEAST8_MAX = INT8_MAX -INT_LEAST16_MAX = INT16_MAX -INT_LEAST32_MAX = INT32_MAX -INT8_MIN = (-128) -INT16_MIN = (-32767-1) -INT32_MIN = (-2147483647-1) -INTMAX_MIN = INT32_MIN -INT_LEAST8_MIN = INT8_MIN -INT_LEAST16_MIN = INT16_MIN -INT_LEAST32_MIN = INT32_MIN - -# Included from sys/int_const.h -def INT8_C(c): return (c) - -def INT16_C(c): return (c) - -def INT32_C(c): return (c) - -def INT64_C(c): return __CONCAT__(c,l) - -def INT64_C(c): return __CONCAT__(c,ll) - -def UINT8_C(c): return __CONCAT__(c,u) - -def UINT16_C(c): return __CONCAT__(c,u) - -def UINT32_C(c): return __CONCAT__(c,u) - -def UINT64_C(c): return __CONCAT__(c,ul) - -def UINT64_C(c): return __CONCAT__(c,ull) - -def INTMAX_C(c): return __CONCAT__(c,l) - -def UINTMAX_C(c): return __CONCAT__(c,ul) - -def INTMAX_C(c): return __CONCAT__(c,ll) - -def UINTMAX_C(c): return __CONCAT__(c,ull) - -def INTMAX_C(c): return (c) - -def UINTMAX_C(c): return (c) - - -# Included from sys/int_fmtio.h -PRId8 = "d" -PRId16 = "d" -PRId32 = "d" -PRId64 = "ld" -PRId64 = "lld" -PRIdLEAST8 = "d" -PRIdLEAST16 = "d" -PRIdLEAST32 = "d" -PRIdLEAST64 = "ld" -PRIdLEAST64 = "lld" -PRIi8 = "i" -PRIi16 = "i" -PRIi32 = "i" -PRIi64 = "li" -PRIi64 = "lli" -PRIiLEAST8 = "i" -PRIiLEAST16 = "i" -PRIiLEAST32 = "i" -PRIiLEAST64 = "li" -PRIiLEAST64 = "lli" -PRIo8 = "o" -PRIo16 = "o" -PRIo32 = "o" -PRIo64 = "lo" -PRIo64 = "llo" -PRIoLEAST8 = "o" -PRIoLEAST16 = "o" -PRIoLEAST32 = "o" -PRIoLEAST64 = "lo" -PRIoLEAST64 = "llo" -PRIx8 = "x" -PRIx16 = "x" -PRIx32 = "x" -PRIx64 = "lx" -PRIx64 = "llx" -PRIxLEAST8 = "x" -PRIxLEAST16 = "x" -PRIxLEAST32 = "x" -PRIxLEAST64 = "lx" -PRIxLEAST64 = "llx" -PRIX8 = "X" -PRIX16 = "X" -PRIX32 = "X" -PRIX64 = "lX" -PRIX64 = "llX" -PRIXLEAST8 = "X" -PRIXLEAST16 = "X" -PRIXLEAST32 = "X" -PRIXLEAST64 = "lX" -PRIXLEAST64 = "llX" -PRIu8 = "u" -PRIu16 = "u" -PRIu32 = "u" -PRIu64 = "lu" -PRIu64 = "llu" -PRIuLEAST8 = "u" -PRIuLEAST16 = "u" -PRIuLEAST32 = "u" -PRIuLEAST64 = "lu" -PRIuLEAST64 = "llu" -SCNd16 = "hd" -SCNd32 = "d" -SCNd64 = "ld" -SCNd64 = "lld" -SCNi16 = "hi" -SCNi32 = "i" -SCNi64 = "li" -SCNi64 = "lli" -SCNo16 = "ho" -SCNo32 = "o" -SCNo64 = "lo" -SCNo64 = "llo" -SCNu16 = "hu" -SCNu32 = "u" -SCNu64 = "lu" -SCNu64 = "llu" -SCNx16 = "hx" -SCNx32 = "x" -SCNx64 = "lx" -SCNx64 = "llx" -PRIdMAX = "ld" -PRIoMAX = "lo" -PRIxMAX = "lx" -PRIuMAX = "lu" -PRIdMAX = "lld" -PRIoMAX = "llo" -PRIxMAX = "llx" -PRIuMAX = "llu" -PRIdMAX = "d" -PRIoMAX = "o" -PRIxMAX = "x" -PRIuMAX = "u" -SCNiMAX = "li" -SCNdMAX = "ld" -SCNoMAX = "lo" -SCNxMAX = "lx" -SCNiMAX = "lli" -SCNdMAX = "lld" -SCNoMAX = "llo" -SCNxMAX = "llx" -SCNiMAX = "i" -SCNdMAX = "d" -SCNoMAX = "o" -SCNxMAX = "x" - -# Included from sys/types32.h -SHRT_MIN = (-32768) -SHRT_MAX = 32767 -USHRT_MAX = 65535 -INT_MIN = (-2147483647-1) -INT_MAX = 2147483647 -LONG_MIN = (-9223372036854775807-1) -LONG_MAX = 9223372036854775807 -LONG_MIN = (-2147483647-1) -LONG_MAX = 2147483647 -P_MYID = (-1) - -# Included from sys/select.h - -# Included from sys/time.h -TIME32_MAX = INT32_MAX -TIME32_MIN = INT32_MIN -def TIMEVAL_OVERFLOW(tv): return \ - -from TYPES import * -DST_NONE = 0 -DST_USA = 1 -DST_AUST = 2 -DST_WET = 3 -DST_MET = 4 -DST_EET = 5 -DST_CAN = 6 -DST_GB = 7 -DST_RUM = 8 -DST_TUR = 9 -DST_AUSTALT = 10 -ITIMER_REAL = 0 -ITIMER_VIRTUAL = 1 -ITIMER_PROF = 2 -ITIMER_REALPROF = 3 -def ITIMERVAL_OVERFLOW(itv): return \ - -SEC = 1 -MILLISEC = 1000 -MICROSEC = 1000000 -NANOSEC = 1000000000 - -# Included from sys/time_impl.h -def TIMESPEC_OVERFLOW(ts): return \ - -def ITIMERSPEC_OVERFLOW(it): return \ - -__CLOCK_REALTIME0 = 0 -CLOCK_VIRTUAL = 1 -CLOCK_PROF = 2 -__CLOCK_REALTIME3 = 3 -CLOCK_HIGHRES = 4 -CLOCK_MAX = 5 -CLOCK_REALTIME = __CLOCK_REALTIME3 -CLOCK_REALTIME = __CLOCK_REALTIME0 -TIMER_RELTIME = 0x0 -TIMER_ABSTIME = 0x1 - -# Included from sys/mutex.h -from TYPES import * -def MUTEX_HELD(x): return (mutex_owned(x)) - -def TICK_TO_SEC(tick): return ((tick) / hz) - -def SEC_TO_TICK(sec): return ((sec) * hz) - -def TICK_TO_MSEC(tick): return \ - -def MSEC_TO_TICK(msec): return \ - -def MSEC_TO_TICK_ROUNDUP(msec): return \ - -def TICK_TO_USEC(tick): return ((tick) * usec_per_tick) - -def USEC_TO_TICK(usec): return ((usec) / usec_per_tick) - -def USEC_TO_TICK_ROUNDUP(usec): return \ - -def TICK_TO_NSEC(tick): return ((tick) * nsec_per_tick) - -def NSEC_TO_TICK(nsec): return ((nsec) / nsec_per_tick) - -def NSEC_TO_TICK_ROUNDUP(nsec): return \ - -def TIMEVAL_TO_TICK(tvp): return \ - -def TIMESTRUC_TO_TICK(tsp): return \ - - -# Included from time.h -from TYPES import * - -# Included from iso/time_iso.h -NULL = 0 -NULL = 0 -CLOCKS_PER_SEC = 1000000 -FD_SETSIZE = 65536 -FD_SETSIZE = 1024 -_NBBY = 8 -NBBY = _NBBY -def FD_ZERO(p): return bzero((p), sizeof (*(p))) diff --git a/Lib/plat-sunos5/regen b/Lib/plat-sunos5/regen deleted file mode 100755 index 78cb7de148..0000000000 --- a/Lib/plat-sunos5/regen +++ /dev/null @@ -1,9 +0,0 @@ -#! /bin/sh -case `uname -sr` in -'SunOS 5.'*) ;; -*) echo Probably not on a Solaris 2 system 1>&2 - exit 1;; -esac -set -v -h2py -i '(u_long)' /usr/include/sys/types.h /usr/include/netinet/in.h /usr/include/sys/stropts.h /usr/include/dlfcn.h - diff --git a/Lib/plat-unixware7/IN.py b/Lib/plat-unixware7/IN.py deleted file mode 100644 index af023b4f2d..0000000000 --- a/Lib/plat-unixware7/IN.py +++ /dev/null @@ -1,836 +0,0 @@ -# Generated by h2py from /usr/include/netinet/in.h - -# Included from netinet/in_f.h -def IN_CLASSA(i): return (((int)(i) & 0x80000000) == 0) - -IN_CLASSA_NET = 0xff000000 -IN_CLASSA_NSHIFT = 24 -IN_CLASSA_HOST = 0x00ffffff -IN_CLASSA_MAX = 128 -def IN_CLASSB(i): return (((int)(i) & 0xc0000000) == 0x80000000) - -IN_CLASSB_NET = 0xffff0000 -IN_CLASSB_NSHIFT = 16 -IN_CLASSB_HOST = 0x0000ffff -IN_CLASSB_MAX = 65536 -def IN_CLASSC(i): return (((int)(i) & 0xe0000000) == 0xc0000000) - -IN_CLASSC_NET = 0xffffff00 -IN_CLASSC_NSHIFT = 8 -IN_CLASSC_HOST = 0x000000ff -def IN_CLASSD(i): return (((int)(i) & 0xf0000000) == 0xe0000000) - -IN_CLASSD_NET = 0xf0000000 -IN_CLASSD_NSHIFT = 28 -IN_CLASSD_HOST = 0x0fffffff -def IN_MULTICAST(i): return IN_CLASSD(i) - -def IN_EXPERIMENTAL(i): return (((int)(i) & 0xe0000000) == 0xe0000000) - -def IN_BADCLASS(i): return (((int)(i) & 0xf0000000) == 0xf0000000) - -INADDR_ANY = 0x00000000 -INADDR_LOOPBACK = 0x7f000001 -INADDR_BROADCAST = 0xffffffff -INADDR_NONE = 0xffffffff -IN_LOOPBACKNET = 127 -INADDR_UNSPEC_GROUP = 0xe0000000 -INADDR_ALLHOSTS_GROUP = 0xe0000001 -INADDR_ALLRTRS_GROUP = 0xe0000002 -INADDR_MAX_LOCAL_GROUP = 0xe00000ff - -# Included from netinet/in6.h - -# Included from sys/types.h -def quad_low(x): return x.val[0] - -ADT_EMASKSIZE = 8 -SHRT_MIN = -32768 -SHRT_MAX = 32767 -INT_MIN = (-2147483647-1) -INT_MAX = 2147483647 -LONG_MIN = (-2147483647-1) -LONG_MAX = 2147483647 -OFF32_MAX = LONG_MAX -ISTAT_ASSERTED = 0 -ISTAT_ASSUMED = 1 -ISTAT_NONE = 2 -OFF_MAX = OFF32_MAX -CLOCK_MAX = LONG_MAX -P_MYID = (-1) -P_MYHOSTID = (-1) - -# Included from sys/select.h -FD_SETSIZE = 4096 -NBBY = 8 -NULL = 0 - -# Included from sys/bitypes.h - -# Included from netinet/in6_f.h -def IN6_IS_ADDR_UNSPECIFIED(a): return IN6_ADDR_EQUAL_L(a, 0, 0, 0, 0) - -def IN6_SET_ADDR_UNSPECIFIED(a): return IN6_ADDR_COPY_L(a, 0, 0, 0, 0) - -def IN6_IS_ADDR_ANY(a): return IN6_ADDR_EQUAL_L(a, 0, 0, 0, 0) - -def IN6_SET_ADDR_ANY(a): return IN6_ADDR_COPY_L(a, 0, 0, 0, 0) - -def IN6_IS_ADDR_LOOPBACK(a): return IN6_ADDR_EQUAL_L(a, 0, 0, 0, 0x01000000) - -def IN6_SET_ADDR_LOOPBACK(a): return IN6_ADDR_COPY_L(a, 0, 0, 0, 0x01000000) - -IN6_MC_FLAG_PERMANENT = 0x0 -IN6_MC_FLAG_TRANSIENT = 0x1 -IN6_MC_SCOPE_NODELOCAL = 0x1 -IN6_MC_SCOPE_LINKLOCAL = 0x2 -IN6_MC_SCOPE_SITELOCAL = 0x5 -IN6_MC_SCOPE_ORGLOCAL = 0x8 -IN6_MC_SCOPE_GLOBAL = 0xE -def IN6_IS_ADDR_MC_NODELOCAL(a): return \ - -def IN6_IS_ADDR_MC_LINKLOCAL(a): return \ - -def IN6_IS_ADDR_MC_SITELOCAL(a): return \ - -def IN6_IS_ADDR_MC_ORGLOCAL(a): return \ - -def IN6_IS_ADDR_MC_GLOBAL(a): return \ - - -# Included from sys/convsa.h -__NETLIB_UW211_SVR4 = 1 -__NETLIB_UW211_XPG4 = 2 -__NETLIB_GEMINI_SVR4 = 3 -__NETLIB_GEMINI_XPG4 = 4 -__NETLIB_FP1_SVR4 = 5 -__NETLIB_FP1_XPG4 = 6 -__NETLIB_BASE_VERSION__ = __NETLIB_UW211_SVR4 -__NETLIB_VERSION__ = __NETLIB_FP1_SVR4 -__NETLIB_VERSION__ = __NETLIB_FP1_XPG4 -__NETLIB_VERSION__ = __NETLIB_GEMINI_SVR4 -__NETLIB_VERSION__ = __NETLIB_GEMINI_XPG4 -__NETLIB_VERSION__ = __NETLIB_UW211_SVR4 -__NETLIB_VERSION__ = __NETLIB_UW211_XPG4 -__NETLIB_VERSION__ = __NETLIB_FP1_XPG4 - -# Included from sys/byteorder.h -LITTLE_ENDIAN = 1234 -BIG_ENDIAN = 4321 -PDP_ENDIAN = 3412 - -# Included from sys/byteorder_f.h -BYTE_ORDER = LITTLE_ENDIAN -def htonl(hl): return __htonl(hl) - -def ntohl(nl): return __ntohl(nl) - -def htons(hs): return __htons(hs) - -def ntohs(ns): return __ntohs(ns) - -def ntohl(x): return (x) - -def ntohs(x): return (x) - -def htonl(x): return (x) - -def htons(x): return (x) - -def __NETLIB_VERSION_IS_XPG4(version): return (((version) % 2) == 0) - -def __NETLIB_VERSION_HAS_SALEN(version): return ((version) >= __NETLIB_GEMINI_SVR4) - -def __NETLIB_VERSION_IS_IKS(version): return ((version) >= __NETLIB_FP1_SVR4) - -def SA_FAMILY_GET(sa): return \ - -INET6_ADDRSTRLEN = 46 -IPV6_UNICAST_HOPS = 3 -IPV6_ADDRFORM = 24 -IPV6_MULTICAST_HOPS = 25 -IPV6_MULTICAST_IF = 26 -IPV6_MULTICAST_LOOP = 27 -IPV6_ADD_MEMBERSHIP = 28 -IPV6_DROP_MEMBERSHIP = 29 - -# Included from sys/insrem.h -def LIST_INIT(head): return \ - -def LIST_INIT(head): return \ - -def remque(a): return REMQUE(a) - - -# Included from sys/socket.h - -# Included from sys/uio.h -SHUT_RD = 0 -SHUT_WR = 1 -SHUT_RDWR = 2 - -# Included from sys/netconfig.h - -# Included from sys/cdefs.h -def __P(protos): return protos - -def __STRING(x): return #x - -def __P(protos): return () - -def __STRING(x): return "x" - -NETCONFIG = "/etc/netconfig" -NETPATH = "NETPATH" -NC_TPI_CLTS = 1 -NC_TPI_COTS = 2 -NC_TPI_COTS_ORD = 3 -NC_TPI_RAW = 4 -NC_NOFLAG = 00 -NC_VISIBLE = 0o1 -NC_BROADCAST = 0o2 -NC_NOPROTOFMLY = "-" -NC_LOOPBACK = "loopback" -NC_INET = "inet" -NC_INET6 = "inet6" -NC_IMPLINK = "implink" -NC_PUP = "pup" -NC_CHAOS = "chaos" -NC_NS = "ns" -NC_NBS = "nbs" -NC_ECMA = "ecma" -NC_DATAKIT = "datakit" -NC_CCITT = "ccitt" -NC_SNA = "sna" -NC_DECNET = "decnet" -NC_DLI = "dli" -NC_LAT = "lat" -NC_HYLINK = "hylink" -NC_APPLETALK = "appletalk" -NC_NIT = "nit" -NC_IEEE802 = "ieee802" -NC_OSI = "osi" -NC_X25 = "x25" -NC_OSINET = "osinet" -NC_GOSIP = "gosip" -NC_NETWARE = "netware" -NC_NOPROTO = "-" -NC_TCP = "tcp" -NC_UDP = "udp" -NC_ICMP = "icmp" -NC_IPX = "ipx" -NC_SPX = "spx" -NC_TPI_CLTS = 1 -NC_TPI_COTS = 2 -NC_TPI_COTS_ORD = 3 -NC_TPI_RAW = 4 -SOCK_STREAM = 2 -SOCK_DGRAM = 1 -SOCK_RAW = 4 -SOCK_RDM = 5 -SOCK_SEQPACKET = 6 -SO_DEBUG = 0x0001 -SO_ACCEPTCONN = 0x0002 -SO_REUSEADDR = 0x0004 -SO_KEEPALIVE = 0x0008 -SO_DONTROUTE = 0x0010 -SO_BROADCAST = 0x0020 -SO_USELOOPBACK = 0x0040 -SO_LINGER = 0x0080 -SO_OOBINLINE = 0x0100 -SO_ORDREL = 0x0200 -SO_IMASOCKET = 0x0400 -SO_MGMT = 0x0800 -SO_REUSEPORT = 0x1000 -SO_LISTENING = 0x2000 -SO_RDWR = 0x4000 -SO_SEMA = 0x8000 -SO_DONTLINGER = (~SO_LINGER) -SO_SNDBUF = 0x1001 -SO_RCVBUF = 0x1002 -SO_SNDLOWAT = 0x1003 -SO_RCVLOWAT = 0x1004 -SO_SNDTIMEO = 0x1005 -SO_RCVTIMEO = 0x1006 -SO_ERROR = 0x1007 -SO_TYPE = 0x1008 -SO_PROTOTYPE = 0x1009 -SO_ALLRAW = 0x100a -SOL_SOCKET = 0xffff -AF_UNSPEC = 0 -AF_UNIX = 1 -AF_LOCAL = AF_UNIX -AF_INET = 2 -AF_IMPLINK = 3 -AF_PUP = 4 -AF_CHAOS = 5 -AF_NS = 6 -AF_NBS = 7 -AF_ECMA = 8 -AF_DATAKIT = 9 -AF_CCITT = 10 -AF_SNA = 11 -AF_DECnet = 12 -AF_DLI = 13 -AF_LAT = 14 -AF_HYLINK = 15 -AF_APPLETALK = 16 -AF_NIT = 17 -AF_802 = 18 -AF_OSI = 19 -AF_ISO = AF_OSI -AF_X25 = 20 -AF_OSINET = 21 -AF_GOSIP = 22 -AF_YNET = 23 -AF_ROUTE = 24 -AF_LINK = 25 -pseudo_AF_XTP = 26 -AF_INET6 = 27 -AF_MAX = 27 -AF_INET_BSWAP = 0x0200 -PF_UNSPEC = AF_UNSPEC -PF_UNIX = AF_UNIX -PF_LOCAL = AF_LOCAL -PF_INET = AF_INET -PF_IMPLINK = AF_IMPLINK -PF_PUP = AF_PUP -PF_CHAOS = AF_CHAOS -PF_NS = AF_NS -PF_NBS = AF_NBS -PF_ECMA = AF_ECMA -PF_DATAKIT = AF_DATAKIT -PF_CCITT = AF_CCITT -PF_SNA = AF_SNA -PF_DECnet = AF_DECnet -PF_DLI = AF_DLI -PF_LAT = AF_LAT -PF_HYLINK = AF_HYLINK -PF_APPLETALK = AF_APPLETALK -PF_NIT = AF_NIT -PF_802 = AF_802 -PF_OSI = AF_OSI -PF_ISO = PF_OSI -PF_X25 = AF_X25 -PF_OSINET = AF_OSINET -PF_GOSIP = AF_GOSIP -PF_YNET = AF_YNET -PF_ROUTE = AF_ROUTE -PF_LINK = AF_LINK -pseudo_PF_XTP = pseudo_AF_XTP -PF_INET6 = AF_INET6 -PF_MAX = AF_MAX -SOMAXCONN = 5 -SCM_RIGHTS = 1 -MSG_OOB = 0x1 -MSG_PEEK = 0x2 -MSG_DONTROUTE = 0x4 -MSG_CTRUNC = 0x8 -MSG_TRUNC = 0x10 -MSG_EOR = 0x30 -MSG_WAITALL = 0x20 -MSG_MAXIOVLEN = 16 -def OPTLEN(x): return ((((x) + sizeof(int) - 1) / sizeof(int)) * sizeof(int)) - -GIARG = 0x1 -CONTI = 0x2 -GITAB = 0x4 -SOCKETSYS = 88 -SOCKETSYS = 83 -SO_ACCEPT = 1 -SO_BIND = 2 -SO_CONNECT = 3 -SO_GETPEERNAME = 4 -SO_GETSOCKNAME = 5 -SO_GETSOCKOPT = 6 -SO_LISTEN = 7 -SO_RECV = 8 -SO_RECVFROM = 9 -SO_SEND = 10 -SO_SENDTO = 11 -SO_SETSOCKOPT = 12 -SO_SHUTDOWN = 13 -SO_SOCKET = 14 -SO_SOCKPOLL = 15 -SO_GETIPDOMAIN = 16 -SO_SETIPDOMAIN = 17 -SO_ADJTIME = 18 - -# Included from sys/stream.h - -# Included from sys/cred.h - -# Included from sys/ksynch.h - -# Included from sys/dl.h -SIGNBIT = 0x80000000 - -# Included from sys/ipl.h - -# Included from sys/disp_p.h - -# Included from sys/trap.h -DIVERR = 0 -SGLSTP = 1 -NMIFLT = 2 -BPTFLT = 3 -INTOFLT = 4 -BOUNDFLT = 5 -INVOPFLT = 6 -NOEXTFLT = 7 -DBLFLT = 8 -EXTOVRFLT = 9 -INVTSSFLT = 10 -SEGNPFLT = 11 -STKFLT = 12 -GPFLT = 13 -PGFLT = 14 -EXTERRFLT = 16 -ALIGNFLT = 17 -MCEFLT = 18 -USERFLT = 0x100 -TRP_PREEMPT = 0x200 -TRP_UNUSED = 0x201 -PF_ERR_MASK = 0x01 -PF_ERR_PAGE = 0 -PF_ERR_PROT = 1 -PF_ERR_WRITE = 2 -PF_ERR_USER = 4 -EVT_STRSCHED = 0x04 -EVT_GLOBCALLOUT = 0x08 -EVT_LCLCALLOUT = 0x10 -EVT_SOFTINTMASK = (EVT_STRSCHED|EVT_GLOBCALLOUT|EVT_LCLCALLOUT) -PL0 = 0 -PL1 = 1 -PL2 = 2 -PL3 = 3 -PL4 = 4 -PL5 = 5 -PL6 = 6 -PLHI = 8 -PL7 = PLHI -PLBASE = PL0 -PLTIMEOUT = PL1 -PLDISK = PL5 -PLSTR = PL6 -PLTTY = PLSTR -PLMIN = PL0 -PLMIN = PL1 -MAX_INTR_LEVELS = 10 -MAX_INTR_NESTING = 50 -STRSCHED = EVT_STRSCHED -GLOBALSOFTINT = EVT_GLOBCALLOUT -LOCALSOFTINT = EVT_LCLCALLOUT - -# Included from sys/ksynch_p.h -def GET_TIME(timep): return \ - -LK_THRESHOLD = 500000 - -# Included from sys/list.h - -# Included from sys/listasm.h -def remque_null(e): return \ - -def LS_ISEMPTY(listp): return \ - -LK_BASIC = 0x1 -LK_SLEEP = 0x2 -LK_NOSTATS = 0x4 -def CYCLES_SINCE(c): return CYCLES_BETWEEN((c), CYCLES()) - -LSB_NLKDS = 92 -EVT_RUNRUN = 0x01 -EVT_KPRUNRUN = 0x02 -SP_UNLOCKED = 0 -SP_LOCKED = 1 -KS_LOCKTEST = 0x01 -KS_MPSTATS = 0x02 -KS_DEINITED = 0x04 -KS_NVLTTRACE = 0x08 -RWS_READ = (ord('r')) -RWS_WRITE = (ord('w')) -RWS_UNLOCKED = (ord('u')) -RWS_BUSY = (ord('b')) -def SLEEP_LOCKOWNED(lkp): return \ - -def SLEEP_DISOWN(lkp): return \ - -KS_NOPRMPT = 0x00000001 -__KS_LOCKTEST = KS_LOCKTEST -__KS_LOCKTEST = 0 -__KS_MPSTATS = KS_MPSTATS -__KS_MPSTATS = 0 -__KS_NVLTTRACE = KS_NVLTTRACE -__KS_NVLTTRACE = 0 -KSFLAGS = (__KS_LOCKTEST|__KS_MPSTATS|__KS_NVLTTRACE) -KSVUNIPROC = 1 -KSVMPDEBUG = 2 -KSVMPNODEBUG = 3 -KSVFLAG = KSVUNIPROC -KSVFLAG = KSVMPDEBUG -KSVFLAG = KSVMPNODEBUG - -# Included from sys/ksinline.h -_A_SP_LOCKED = 1 -_A_SP_UNLOCKED = 0 -_A_INVPL = -1 -def _ATOMIC_INT_INCR(atomic_intp): return \ - -def _ATOMIC_INT_DECR(atomic_intp): return \ - -def ATOMIC_INT_READ(atomic_intp): return _ATOMIC_INT_READ(atomic_intp) - -def ATOMIC_INT_INCR(atomic_intp): return _ATOMIC_INT_INCR(atomic_intp) - -def ATOMIC_INT_DECR(atomic_intp): return _ATOMIC_INT_DECR(atomic_intp) - -def FSPIN_INIT(lp): return - -def FSPIN_LOCK(l): return DISABLE() - -def FSPIN_TRYLOCK(l): return (DISABLE(), B_TRUE) - -def FSPIN_UNLOCK(l): return ENABLE() - -def LOCK_DEINIT(lp): return - -def LOCK_DEALLOC(lp): return - -def LOCK_OWNED(lp): return (B_TRUE) - -def RW_DEINIT(lp): return - -def RW_DEALLOC(lp): return - -def RW_OWNED(lp): return (B_TRUE) - -def IS_LOCKED(lockp): return B_FALSE - -def LOCK_PLMIN(lockp): return \ - -def TRYLOCK_PLMIN(lockp): return LOCK_PLMIN(lockp) - -def LOCK_SH_PLMIN(lockp): return LOCK_PLMIN(lockp) - -def RW_RDLOCK_PLMIN(lockp): return LOCK_PLMIN(lockp) - -def RW_WRLOCK_PLMIN(lockp): return LOCK_PLMIN(lockp) - -def LOCK_DEINIT(l): return - -def LOCK_PLMIN(lockp): return LOCK((lockp), PLMIN) - -def TRYLOCK_PLMIN(lockp): return TRYLOCK((lockp), PLMIN) - -def LOCK_SH_PLMIN(lockp): return LOCK_SH((lockp), PLMIN) - -def RW_RDLOCK_PLMIN(lockp): return RW_RDLOCK((lockp), PLMIN) - -def RW_WRLOCK_PLMIN(lockp): return RW_WRLOCK((lockp), PLMIN) - -def FSPIN_IS_LOCKED(fsp): return B_FALSE - -def SPIN_IS_LOCKED(lockp): return B_FALSE - -def FSPIN_OWNED(l): return (B_TRUE) - -CR_MLDREAL = 0x00000001 -CR_RDUMP = 0x00000002 -def crhold(credp): return crholdn((credp), 1) - -def crfree(credp): return crfreen((credp), 1) - - -# Included from sys/strmdep.h -def str_aligned(X): return (((uint)(X) & (sizeof(int) - 1)) == 0) - - -# Included from sys/engine.h - -# Included from sys/clock.h - -# Included from sys/time.h -DST_NONE = 0 -DST_USA = 1 -DST_AUST = 2 -DST_WET = 3 -DST_MET = 4 -DST_EET = 5 -DST_CAN = 6 -DST_GB = 7 -DST_RUM = 8 -DST_TUR = 9 -DST_AUSTALT = 10 -ITIMER_REAL = 0 -ITIMER_VIRTUAL = 1 -ITIMER_PROF = 2 -FD_SETSIZE = 4096 -FD_NBBY = 8 - -# Included from time.h -NULL = 0 -CLOCKS_PER_SEC = 1000000 - -# Included from sys/clock_p.h -CGBITS = 4 -IDBITS = 28 -def toid_unpackcg(idval): return (((idval) >> IDBITS) & 0xf) - -def toid_unpackid(idval): return ((idval) & 0xfffffff) - -def toid_unpackcg(idval): return 0 - -def toid_unpackid(idval): return (idval) - -NCALLOUT_HASH = 1024 -CALLOUT_MAXVAL = 0x7fffffff -TO_PERIODIC = 0x80000000 -TO_IMMEDIATE = 0x80000000 -SEC = 1 -MILLISEC = 1000 -MICROSEC = 1000000 -NANOSEC = 1000000000 -SECHR = (60*60) -SECDAY = (24*SECHR) -SECYR = (365*SECDAY) -def TIME_OWNED_R(cgnum): return (B_TRUE) - -LOOPSECONDS = 1800 -LOOPMICROSECONDS = (LOOPSECONDS * MICROSEC) -def TICKS_SINCE(t): return TICKS_BETWEEN(t, TICKS()) - -MAXRQS = 2 -E_OFFLINE = 0x01 -E_BAD = 0x02 -E_SHUTDOWN = 0x04 -E_DRIVER = 0x08 -E_DEFAULTKEEP = 0x100 -E_DRIVERBOUND = 0x200 -E_EXCLUSIVE = 0x400 -E_CGLEADER = 0x800 -E_NOWAY = (E_OFFLINE|E_BAD|E_SHUTDOWN) -E_BOUND = 0x01 -E_GLOBAL = 0x00 -E_UNAVAIL = -1 -ENGINE_ONLINE = 1 -def PROCESSOR_UNMAP(e): return ((e) - engine) - -BOOTENG = 0 -QMOVED = 0x0001 -QWANTR = 0x0002 -QWANTW = 0x0004 -QFULL = 0x0008 -QREADR = 0x0010 -QUSE = 0x0020 -QNOENB = 0x0040 -QUP = 0x0080 -QBACK = 0x0100 -QINTER = 0x0200 -QPROCSON = 0x0400 -QTOENAB = 0x0800 -QFREEZE = 0x1000 -QBOUND = 0x2000 -QDEFCNT = 0x4000 -QENAB = 0x0001 -QSVCBUSY = 0x0002 -STRM_PUTCNT_TABLES = 31 -def STRM_MYENG_PUTCNT(sdp): return STRM_PUTCNT(l.eng_num, sdp) - -QB_FULL = 0x01 -QB_WANTW = 0x02 -QB_BACK = 0x04 -NBAND = 256 -DB_WASDUPED = 0x1 -DB_2PIECE = 0x2 -STRLEAKHASHSZ = 1021 -MSGMARK = 0x01 -MSGNOLOOP = 0x02 -MSGDELIM = 0x04 -MSGNOGET = 0x08 -MSGLOG = 0x10 -M_DATA = 0x00 -M_PROTO = 0x01 -M_BREAK = 0x08 -M_PASSFP = 0x09 -M_SIG = 0x0b -M_DELAY = 0x0c -M_CTL = 0x0d -M_IOCTL = 0x0e -M_SETOPTS = 0x10 -M_RSE = 0x11 -M_TRAIL = 0x12 -M_IOCACK = 0x81 -M_IOCNAK = 0x82 -M_PCPROTO = 0x83 -M_PCSIG = 0x84 -M_READ = 0x85 -M_FLUSH = 0x86 -M_STOP = 0x87 -M_START = 0x88 -M_HANGUP = 0x89 -M_ERROR = 0x8a -M_COPYIN = 0x8b -M_COPYOUT = 0x8c -M_IOCDATA = 0x8d -M_PCRSE = 0x8e -M_STOPI = 0x8f -M_STARTI = 0x90 -M_PCCTL = 0x91 -M_PCSETOPTS = 0x92 -QNORM = 0x00 -QPCTL = 0x80 -STRCANON = 0x01 -RECOPY = 0x02 -SO_ALL = 0x003f -SO_READOPT = 0x0001 -SO_WROFF = 0x0002 -SO_MINPSZ = 0x0004 -SO_MAXPSZ = 0x0008 -SO_HIWAT = 0x0010 -SO_LOWAT = 0x0020 -SO_MREADON = 0x0040 -SO_MREADOFF = 0x0080 -SO_NDELON = 0x0100 -SO_NDELOFF = 0x0200 -SO_ISTTY = 0x0400 -SO_ISNTTY = 0x0800 -SO_TOSTOP = 0x1000 -SO_TONSTOP = 0x2000 -SO_BAND = 0x4000 -SO_DELIM = 0x8000 -SO_NODELIM = 0x010000 -SO_STRHOLD = 0x020000 -SO_LOOP = 0x040000 -DRVOPEN = 0x0 -MODOPEN = 0x1 -CLONEOPEN = 0x2 -OPENFAIL = -1 -BPRI_LO = 1 -BPRI_MED = 2 -BPRI_HI = 3 -INFPSZ = -1 -FLUSHALL = 1 -FLUSHDATA = 0 -STRHIGH = 5120 -STRLOW = 1024 -MAXIOCBSZ = 1024 -def straln(a): return (caddr_t)((int)(a) & ~(sizeof(int)-1)) - -IPM_ID = 200 -ICMPM_ID = 201 -TCPM_ID = 202 -UDPM_ID = 203 -ARPM_ID = 204 -APPM_ID = 205 -RIPM_ID = 206 -PPPM_ID = 207 -AHDLCM_ID = 208 -MHDLCRIPM_ID = 209 -HDLCM_ID = 210 -PPCID_ID = 211 -IGMPM_ID = 212 -IPIPM_ID = 213 -IPPROTO_IP = 0 -IPPROTO_HOPOPTS = 0 -IPPROTO_ICMP = 1 -IPPROTO_IGMP = 2 -IPPROTO_GGP = 3 -IPPROTO_IPIP = 4 -IPPROTO_TCP = 6 -IPPROTO_EGP = 8 -IPPROTO_PUP = 12 -IPPROTO_UDP = 17 -IPPROTO_IDP = 22 -IPPROTO_TP = 29 -IPPROTO_IPV6 = 41 -IPPROTO_ROUTING = 43 -IPPROTO_FRAGMENT = 44 -IPPROTO_ESP = 50 -IPPROTO_AH = 51 -IPPROTO_ICMPV6 = 58 -IPPROTO_NONE = 59 -IPPROTO_DSTOPTS = 60 -IPPROTO_HELLO = 63 -IPPROTO_ND = 77 -IPPROTO_EON = 80 -IPPROTO_RAW = 255 -IPPROTO_MAX = 256 -IPPORT_ECHO = 7 -IPPORT_DISCARD = 9 -IPPORT_SYSTAT = 11 -IPPORT_DAYTIME = 13 -IPPORT_NETSTAT = 15 -IPPORT_FTP = 21 -IPPORT_TELNET = 23 -IPPORT_SMTP = 25 -IPPORT_TIMESERVER = 37 -IPPORT_NAMESERVER = 42 -IPPORT_WHOIS = 43 -IPPORT_MTP = 57 -IPPORT_TFTP = 69 -IPPORT_RJE = 77 -IPPORT_FINGER = 79 -IPPORT_TTYLINK = 87 -IPPORT_SUPDUP = 95 -IPPORT_EXECSERVER = 512 -IPPORT_LOGINSERVER = 513 -IPPORT_CMDSERVER = 514 -IPPORT_EFSSERVER = 520 -IPPORT_BIFFUDP = 512 -IPPORT_WHOSERVER = 513 -IPPORT_ROUTESERVER = 520 -IPPORT_RESERVED = 1024 -IPPORT_USERRESERVED = 65535 -IPPORT_RESERVED_LOW = 512 -IPPORT_RESERVED_HIGH = 1023 -IPPORT_USERRESERVED_LOW = 32768 -IPPORT_USERRESERVED_HIGH = 65535 -INET_ADDRSTRLEN = 16 -IP_OPTIONS = 1 -IP_TOS = 2 -IP_TTL = 3 -IP_HDRINCL = 4 -IP_RECVOPTS = 5 -IP_RECVRETOPTS = 6 -IP_RECVDSTADDR = 7 -IP_RETOPTS = 8 -IP_MULTICAST_IF = 9 -IP_MULTICAST_LOOP = 10 -IP_ADD_MEMBERSHIP = 11 -IP_DROP_MEMBERSHIP = 12 -IP_BROADCAST_IF = 14 -IP_RECVIFINDEX = 15 -IP_MULTICAST_TTL = 16 -MRT_INIT = 17 -MRT_DONE = 18 -MRT_ADD_VIF = 19 -MRT_DEL_VIF = 20 -MRT_ADD_MFC = 21 -MRT_DEL_MFC = 22 -MRT_VERSION = 23 -IP_DEFAULT_MULTICAST_TTL = 1 -IP_DEFAULT_MULTICAST_LOOP = 1 -IP_MAX_MEMBERSHIPS = 20 -INADDR_UNSPEC_GROUP = 0xe0000000 -INADDR_ALLHOSTS_GROUP = 0xe0000001 -INADDR_ALLRTRS_GROUP = 0xe0000002 -INADDR_MAX_LOCAL_GROUP = 0xe00000ff - -# Included from netinet/in_mp.h - -# Included from netinet/in_mp_ddi.h - -# Included from sys/inline.h -IP_HIER_BASE = (20) -def ASSERT_LOCK(x): return - -def ASSERT_WRLOCK(x): return - -def ASSERT_UNLOCK(x): return - -def CANPUT(q): return canput((q)) - -def CANPUTNEXT(q): return canputnext((q)) - -INET_DEBUG = 1 diff --git a/Lib/plat-unixware7/STROPTS.py b/Lib/plat-unixware7/STROPTS.py deleted file mode 100644 index ef50a9cb2b..0000000000 --- a/Lib/plat-unixware7/STROPTS.py +++ /dev/null @@ -1,328 +0,0 @@ -# Generated by h2py from /usr/include/sys/stropts.h - -# Included from sys/types.h -def quad_low(x): return x.val[0] - -ADT_EMASKSIZE = 8 -SHRT_MIN = -32768 -SHRT_MAX = 32767 -INT_MIN = (-2147483647-1) -INT_MAX = 2147483647 -LONG_MIN = (-2147483647-1) -LONG_MAX = 2147483647 -OFF32_MAX = LONG_MAX -ISTAT_ASSERTED = 0 -ISTAT_ASSUMED = 1 -ISTAT_NONE = 2 -OFF_MAX = OFF32_MAX -CLOCK_MAX = LONG_MAX -P_MYID = (-1) -P_MYHOSTID = (-1) - -# Included from sys/select.h -FD_SETSIZE = 4096 -NBBY = 8 -NULL = 0 - -# Included from sys/conf.h -D_NEW = 0x00 -D_OLD = 0x01 -D_DMA = 0x02 -D_BLKOFF = 0x400 -D_LFS = 0x8000 -D_STR = 0x0800 -D_MOD = 0x1000 -D_PSEUDO = 0x2000 -D_RANDOM = 0x4000 -D_HOT = 0x10000 -D_SEEKNEG = 0x04 -D_TAPE = 0x08 -D_NOBRKUP = 0x10 -D_INITPUB = 0x20 -D_NOSPECMACDATA = 0x40 -D_RDWEQ = 0x80 -SECMASK = (D_INITPUB|D_NOSPECMACDATA|D_RDWEQ) -DAF_REQDMA = 0x1 -DAF_PHYSREQ = 0x2 -DAF_PRE8 = 0x4 -DAF_STATIC = 0x8 -DAF_STR = 0x10 -D_MP = 0x100 -D_UPF = 0x200 -ROOTFS_NAMESZ = 7 -FMNAMESZ = 8 -MCD_VERSION = 1 -DI_BCBP = 0 -DI_MEDIA = 1 - -# Included from sys/secsys.h -ES_MACOPENLID = 1 -ES_MACSYSLID = 2 -ES_MACROOTLID = 3 -ES_PRVINFO = 4 -ES_PRVSETCNT = 5 -ES_PRVSETS = 6 -ES_MACADTLID = 7 -ES_PRVID = 8 -ES_TPGETMAJOR = 9 -SA_EXEC = 0o01 -SA_WRITE = 0o02 -SA_READ = 0o04 -SA_SUBSIZE = 0o10 - -# Included from sys/stropts_f.h -X_STR = (ord('S')<<8) -X_I_BASE = (X_STR|0o200) -X_I_NREAD = (X_STR|0o201) -X_I_PUSH = (X_STR|0o202) -X_I_POP = (X_STR|0o203) -X_I_LOOK = (X_STR|0o204) -X_I_FLUSH = (X_STR|0o205) -X_I_SRDOPT = (X_STR|0o206) -X_I_GRDOPT = (X_STR|0o207) -X_I_STR = (X_STR|0o210) -X_I_SETSIG = (X_STR|0o211) -X_I_GETSIG = (X_STR|0o212) -X_I_FIND = (X_STR|0o213) -X_I_LINK = (X_STR|0o214) -X_I_UNLINK = (X_STR|0o215) -X_I_PEEK = (X_STR|0o217) -X_I_FDINSERT = (X_STR|0o220) -X_I_SENDFD = (X_STR|0o221) -X_I_RECVFD = (X_STR|0o222) - -# Included from unistd.h - -# Included from sys/unistd.h -R_OK = 0o04 -W_OK = 0o02 -X_OK = 0o01 -F_OK = 000 -EFF_ONLY_OK = 0o10 -EX_OK = 0o20 -SEEK_SET = 0 -SEEK_CUR = 1 -SEEK_END = 2 -_SC_ARG_MAX = 1 -_SC_CHILD_MAX = 2 -_SC_CLK_TCK = 3 -_SC_NGROUPS_MAX = 4 -_SC_OPEN_MAX = 5 -_SC_JOB_CONTROL = 6 -_SC_SAVED_IDS = 7 -_SC_VERSION = 8 -_SC_PASS_MAX = 9 -_SC_LOGNAME_MAX = 10 -_SC_PAGESIZE = 11 -_SC_PAGE_SIZE = _SC_PAGESIZE -_SC_XOPEN_VERSION = 12 -_SC_NACLS_MAX = 13 -_SC_NPROCESSORS_CONF = 14 -_SC_NPROCESSORS_ONLN = 15 -_SC_NPROCESSES = 39 -_SC_TOTAL_MEMORY = 40 -_SC_USEABLE_MEMORY = 41 -_SC_GENERAL_MEMORY = 42 -_SC_DEDICATED_MEMORY = 43 -_SC_NCGS_CONF = 44 -_SC_NCGS_ONLN = 45 -_SC_MAX_CPUS_PER_CG = 46 -_SC_CG_SIMPLE_IMPL = 47 -_SC_CACHE_LINE = 48 -_SC_SYSTEM_ID = 49 -_SC_THREADS = 51 -_SC_THREAD_ATTR_STACKADDR = 52 -_SC_THREAD_ATTR_STACKSIZE = 53 -_SC_THREAD_DESTRUCTOR_ITERATIONS = 54 -_SC_THREAD_KEYS_MAX = 55 -_SC_THREAD_PRIORITY_SCHEDULING = 56 -_SC_THREAD_PRIO_INHERIT = 57 -_SC_THREAD_PRIO_PROTECT = 58 -_SC_THREAD_STACK_MIN = 59 -_SC_THREAD_PROCESS_SHARED = 60 -_SC_THREAD_SAFE_FUNCTIONS = 61 -_SC_THREAD_THREADS_MAX = 62 -_SC_KERNEL_VM = 63 -_SC_TZNAME_MAX = 320 -_SC_STREAM_MAX = 321 -_SC_XOPEN_CRYPT = 323 -_SC_XOPEN_ENH_I18N = 324 -_SC_XOPEN_SHM = 325 -_SC_XOPEN_XCU_VERSION = 327 -_SC_AES_OS_VERSION = 330 -_SC_ATEXIT_MAX = 331 -_SC_2_C_BIND = 350 -_SC_2_C_DEV = 351 -_SC_2_C_VERSION = 352 -_SC_2_CHAR_TERM = 353 -_SC_2_FORT_DEV = 354 -_SC_2_FORT_RUN = 355 -_SC_2_LOCALEDEF = 356 -_SC_2_SW_DEV = 357 -_SC_2_UPE = 358 -_SC_2_VERSION = 359 -_SC_BC_BASE_MAX = 370 -_SC_BC_DIM_MAX = 371 -_SC_BC_SCALE_MAX = 372 -_SC_BC_STRING_MAX = 373 -_SC_COLL_WEIGHTS_MAX = 380 -_SC_EXPR_NEST_MAX = 381 -_SC_LINE_MAX = 382 -_SC_RE_DUP_MAX = 383 -_SC_IOV_MAX = 390 -_SC_NPROC_CONF = 391 -_SC_NPROC_ONLN = 392 -_SC_XOPEN_UNIX = 400 -_SC_SEMAPHORES = 440 -_CS_PATH = 1 -__O_CS_HOSTNAME = 2 -_CS_RELEASE = 3 -_CS_VERSION = 4 -__O_CS_MACHINE = 5 -__O_CS_ARCHITECTURE = 6 -_CS_HW_SERIAL = 7 -__O_CS_HW_PROVIDER = 8 -_CS_SRPC_DOMAIN = 9 -_CS_INITTAB_NAME = 10 -__O_CS_SYSNAME = 11 -_CS_LFS_CFLAGS = 20 -_CS_LFS_LDFLAGS = 21 -_CS_LFS_LIBS = 22 -_CS_LFS_LINTFLAGS = 23 -_CS_LFS64_CFLAGS = 24 -_CS_LFS64_LDFLAGS = 25 -_CS_LFS64_LIBS = 26 -_CS_LFS64_LINTFLAGS = 27 -_CS_ARCHITECTURE = 100 -_CS_BUSTYPES = 101 -_CS_HOSTNAME = 102 -_CS_HW_PROVIDER = 103 -_CS_KERNEL_STAMP = 104 -_CS_MACHINE = 105 -_CS_OS_BASE = 106 -_CS_OS_PROVIDER = 107 -_CS_SYSNAME = 108 -_CS_USER_LIMIT = 109 -_PC_LINK_MAX = 1 -_PC_MAX_CANON = 2 -_PC_MAX_INPUT = 3 -_PC_NAME_MAX = 4 -_PC_PATH_MAX = 5 -_PC_PIPE_BUF = 6 -_PC_NO_TRUNC = 7 -_PC_VDISABLE = 8 -_PC_CHOWN_RESTRICTED = 9 -_PC_FILESIZEBITS = 10 -_POSIX_VERSION = 199009 -_XOPEN_VERSION = 4 -GF_PATH = "/etc/group" -PF_PATH = "/etc/passwd" -F_ULOCK = 0 -F_LOCK = 1 -F_TLOCK = 2 -F_TEST = 3 -_POSIX_JOB_CONTROL = 1 -_POSIX_SAVED_IDS = 1 -_POSIX_VDISABLE = 0 -NULL = 0 -STDIN_FILENO = 0 -STDOUT_FILENO = 1 -STDERR_FILENO = 2 -_XOPEN_UNIX = 1 -_XOPEN_ENH_I18N = 1 -_XOPEN_XPG4 = 1 -_POSIX2_C_VERSION = 199209 -_POSIX2_VERSION = 199209 -_XOPEN_XCU_VERSION = 4 -_POSIX_SEMAPHORES = 1 -_POSIX_THREADS = 1 -_POSIX_THREAD_ATTR_STACKADDR = 1 -_POSIX_THREAD_ATTR_STACKSIZE = 1 -_POSIX_THREAD_PRIORITY_SCHEDULING = 1 -_POSIX_THREAD_PROCESS_SHARED = 1 -_POSIX_THREAD_SAFE_FUNCTIONS = 1 -_POSIX2_C_BIND = 1 -_POSIX2_CHAR_TERM = 1 -_POSIX2_FORT_RUN = 1 -_POSIX2_LOCALEDEF = 1 -_POSIX2_UPE = 1 -_LFS_ASYNCHRONOUS_IO = 1 -_LFS_LARGEFILE = 1 -_LFS64_ASYNCHRONOUS_IO = 1 -_LFS64_LARGEFILE = 1 -_LFS64_STDIO = 1 -FMNAMESZ = 8 -SNDZERO = 0x001 -SNDPIPE = 0x002 -RNORM = 0x000 -RMSGD = 0x001 -RMSGN = 0x002 -RMODEMASK = 0x003 -RPROTDAT = 0x004 -RPROTDIS = 0x008 -RPROTNORM = 0x010 -RPROTMASK = 0x01c -FLUSHR = 0x01 -FLUSHW = 0x02 -FLUSHRW = 0x03 -FLUSHBAND = 0x04 -S_INPUT = 0x0001 -S_HIPRI = 0x0002 -S_OUTPUT = 0x0004 -S_MSG = 0x0008 -S_ERROR = 0x0010 -S_HANGUP = 0x0020 -S_RDNORM = 0x0040 -S_WRNORM = S_OUTPUT -S_RDBAND = 0x0080 -S_WRBAND = 0x0100 -S_BANDURG = 0x0200 -RS_HIPRI = 0x01 -MSG_HIPRI = 0x01 -MSG_ANY = 0x02 -MSG_BAND = 0x04 -MSG_DISCARD = 0x08 -MSG_PEEKIOCTL = 0x10 -MORECTL = 1 -MOREDATA = 2 -MUXID_ALL = (-1) -ANYMARK = 0x01 -LASTMARK = 0x02 -STR = (ord('S')<<8) -I_NREAD = (STR|0o1) -I_PUSH = (STR|0o2) -I_POP = (STR|0o3) -I_LOOK = (STR|0o4) -I_FLUSH = (STR|0o5) -I_SRDOPT = (STR|0o6) -I_GRDOPT = (STR|0o7) -I_STR = (STR|0o10) -I_SETSIG = (STR|0o11) -I_GETSIG = (STR|0o12) -I_FIND = (STR|0o13) -I_LINK = (STR|0o14) -I_UNLINK = (STR|0o15) -I_PEEK = (STR|0o17) -I_FDINSERT = (STR|0o20) -I_SENDFD = (STR|0o21) -I_RECVFD = (STR|0o22) -I_E_RECVFD = (STR|0o16) -I_RECVFD = (STR|0o16) -I_RECVFD = (STR|0o22) -I_SWROPT = (STR|0o23) -I_GWROPT = (STR|0o24) -I_LIST = (STR|0o25) -I_PLINK = (STR|0o26) -I_PUNLINK = (STR|0o27) -I_FLUSHBAND = (STR|0o34) -I_CKBAND = (STR|0o35) -I_GETBAND = (STR|0o36) -I_ATMARK = (STR|0o37) -I_SETCLTIME = (STR|0o40) -I_GETCLTIME = (STR|0o41) -I_CANPUT = (STR|0o42) -I_S_RECVFD = (STR|0o43) -I_STATS = (STR|0o44) -I_BIGPIPE = (STR|0o45) -I_GETTP = (STR|0o46) -INFTIM = -1 diff --git a/Lib/plat-unixware7/regen b/Lib/plat-unixware7/regen deleted file mode 100755 index 68998a7a5c..0000000000 --- a/Lib/plat-unixware7/regen +++ /dev/null @@ -1,9 +0,0 @@ -#! /bin/sh -case `uname -sr` in -UnixWare*) ;; -*) echo Probably not on a UnixWare system 1>&2 - exit 1;; -esac -set -v -h2py -i '(u_long)' /usr/include/netinet/in.h -h2py /usr/include/sys/stropts.h -- cgit v1.2.1 From c4045fbe1bd2845aee7ecf7d3ee0bdefa2b3a4ff Mon Sep 17 00:00:00 2001 From: R David Murray Date: Thu, 8 Sep 2016 15:34:08 -0400 Subject: #27364: Deprecate invalid escape strings in str/byutes. Patch by Emanuel Barry, reviewed by Serhiy Storchaka and Martin Panter. --- Lib/test/test_codecs.py | 35 ++++++++++++++++++++++++----------- Lib/test/test_unicode.py | 7 +++++++ 2 files changed, 31 insertions(+), 11 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index 1af552405c..4d91a07868 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -1175,7 +1175,7 @@ class EscapeDecodeTest(unittest.TestCase): check(b"[\\\n]", b"[]") check(br'[\"]', b'["]') check(br"[\']", b"[']") - check(br"[\\]", br"[\]") + check(br"[\\]", b"[\\]") check(br"[\a]", b"[\x07]") check(br"[\b]", b"[\x08]") check(br"[\t]", b"[\x09]") @@ -1184,7 +1184,6 @@ class EscapeDecodeTest(unittest.TestCase): check(br"[\f]", b"[\x0c]") check(br"[\r]", b"[\x0d]") check(br"[\7]", b"[\x07]") - check(br"[\8]", br"[\8]") check(br"[\78]", b"[\x078]") check(br"[\41]", b"[!]") check(br"[\418]", b"[!8]") @@ -1192,12 +1191,18 @@ class EscapeDecodeTest(unittest.TestCase): check(br"[\1010]", b"[A0]") check(br"[\501]", b"[A]") check(br"[\x41]", b"[A]") - check(br"[\X41]", br"[\X41]") check(br"[\x410]", b"[A0]") - for b in range(256): - if b not in b'\n"\'\\abtnvfr01234567x': - b = bytes([b]) - check(b'\\' + b, b'\\' + b) + for i in range(97, 123): + b = bytes([i]) + if b not in b'abfnrtvx': + with self.assertWarns(DeprecationWarning): + check(b"\\" + b, b"\\" + b) + with self.assertWarns(DeprecationWarning): + check(b"\\" + b.upper(), b"\\" + b.upper()) + with self.assertWarns(DeprecationWarning): + check(br"\8", b"\\8") + with self.assertWarns(DeprecationWarning): + check(br"\9", b"\\9") def test_errors(self): decode = codecs.escape_decode @@ -2448,7 +2453,6 @@ class UnicodeEscapeTest(unittest.TestCase): check(br"[\f]", "[\x0c]") check(br"[\r]", "[\x0d]") check(br"[\7]", "[\x07]") - check(br"[\8]", r"[\8]") check(br"[\78]", "[\x078]") check(br"[\41]", "[!]") check(br"[\418]", "[!8]") @@ -2458,9 +2462,18 @@ class UnicodeEscapeTest(unittest.TestCase): check(br"[\x410]", "[A0]") check(br"\u20ac", "\u20ac") check(br"\U0001d120", "\U0001d120") - for b in range(256): - if b not in b'\n"\'\\abtnvfr01234567xuUN': - check(b'\\' + bytes([b]), '\\' + chr(b)) + for i in range(97, 123): + b = bytes([i]) + if b not in b'abfnrtuvx': + with self.assertWarns(DeprecationWarning): + check(b"\\" + b, "\\" + chr(i)) + if b.upper() not in b'UN': + with self.assertWarns(DeprecationWarning): + check(b"\\" + b.upper(), "\\" + chr(i-32)) + with self.assertWarns(DeprecationWarning): + check(br"\8", "\\8") + with self.assertWarns(DeprecationWarning): + check(br"\9", "\\9") def test_decode_errors(self): decode = codecs.unicode_escape_decode diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py index 9ab624e6fc..2684b940ef 100644 --- a/Lib/test/test_unicode.py +++ b/Lib/test/test_unicode.py @@ -10,6 +10,7 @@ import codecs import itertools import operator import struct +import string import sys import unittest import warnings @@ -2752,6 +2753,12 @@ class UnicodeTest(string_tests.CommonTest, support.check_free_after_iterating(self, iter, str) support.check_free_after_iterating(self, reversed, str) + def test_invalid_sequences(self): + for letter in string.ascii_letters + "89": # 0-7 are octal escapes + if letter in "abfnrtuvxNU": + continue + with self.assertWarns(DeprecationWarning): + eval(r"'\%s'" % letter) class StringModuleTest(unittest.TestCase): def test_formatter_parser(self): -- cgit v1.2.1 From 1c4aac5fbe8b35924cb164acb8e4c39dba9451c1 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 8 Sep 2016 12:51:24 -0700 Subject: Add a new private version to the builtin dict type Issue #26058: Add a new private version to the builtin dict type, incremented at each dictionary creation and at each dictionary change. Implementation of the PEP 509. --- Lib/test/test_ordered_dict.py | 2 +- Lib/test/test_pep509.py | 186 ++++++++++++++++++++++++++++++++++++++++++ Lib/test/test_sys.py | 6 +- 3 files changed, 190 insertions(+), 4 deletions(-) create mode 100644 Lib/test/test_pep509.py (limited to 'Lib') diff --git a/Lib/test/test_ordered_dict.py b/Lib/test/test_ordered_dict.py index 43600e4fc8..d6e72a6ed2 100644 --- a/Lib/test/test_ordered_dict.py +++ b/Lib/test/test_ordered_dict.py @@ -655,7 +655,7 @@ class CPythonOrderedDictTests(OrderedDictTests, unittest.TestCase): size = support.calcobjsize check = self.check_sizeof - basicsize = size('n2P' + '3PnPn2P') + calcsize('2nP2n') + basicsize = size('n2P3PnPn2P') + 8 + calcsize('2nP2n') entrysize = calcsize('n2P') p = calcsize('P') nodesize = calcsize('Pn2P') diff --git a/Lib/test/test_pep509.py b/Lib/test/test_pep509.py new file mode 100644 index 0000000000..5671f9fc7c --- /dev/null +++ b/Lib/test/test_pep509.py @@ -0,0 +1,186 @@ +""" +Test implementation of the PEP 509: dictionary versionning. +""" +import unittest +from test import support + +# PEP 509 is implemented in CPython but other Python implementations +# don't require to implement it +_testcapi = support.import_module('_testcapi') + + +class DictVersionTests(unittest.TestCase): + type2test = dict + + def setUp(self): + self.seen_versions = set() + self.dict = None + + def check_version_unique(self, mydict): + version = _testcapi.dict_get_version(mydict) + self.assertNotIn(version, self.seen_versions) + self.seen_versions.add(version) + + def check_version_changed(self, mydict, method, *args, **kw): + result = method(*args, **kw) + self.check_version_unique(mydict) + return result + + def check_version_dont_change(self, mydict, method, *args, **kw): + version1 = _testcapi.dict_get_version(mydict) + self.seen_versions.add(version1) + + result = method(*args, **kw) + + version2 = _testcapi.dict_get_version(mydict) + self.assertEqual(version2, version1, "version changed") + + return result + + def new_dict(self, *args, **kw): + d = self.type2test(*args, **kw) + self.check_version_unique(d) + return d + + def test_constructor(self): + # new empty dictionaries must all have an unique version + empty1 = self.new_dict() + empty2 = self.new_dict() + empty3 = self.new_dict() + + # non-empty dictionaries must also have an unique version + nonempty1 = self.new_dict(x='x') + nonempty2 = self.new_dict(x='x', y='y') + + def test_copy(self): + d = self.new_dict(a=1, b=2) + + d2 = self.check_version_dont_change(d, d.copy) + + # dict.copy() must create a dictionary with a new unique version + self.check_version_unique(d2) + + def test_setitem(self): + d = self.new_dict() + + # creating new keys must change the version + self.check_version_changed(d, d.__setitem__, 'x', 'x') + self.check_version_changed(d, d.__setitem__, 'y', 'y') + + # changing values must change the version + self.check_version_changed(d, d.__setitem__, 'x', 1) + self.check_version_changed(d, d.__setitem__, 'y', 2) + + def test_setitem_same_value(self): + value = object() + d = self.new_dict() + + # setting a key must change the version + self.check_version_changed(d, d.__setitem__, 'key', value) + + # setting a key to the same value with dict.__setitem__ + # must change the version + self.check_version_changed(d, d.__setitem__, 'key', value) + + # setting a key to the same value with dict.update + # must change the version + self.check_version_changed(d, d.update, key=value) + + d2 = self.new_dict(key=value) + self.check_version_changed(d, d.update, d2) + + def test_setitem_equal(self): + class AlwaysEqual: + def __eq__(self, other): + return True + + value1 = AlwaysEqual() + value2 = AlwaysEqual() + self.assertTrue(value1 == value2) + self.assertFalse(value1 != value2) + + d = self.new_dict() + self.check_version_changed(d, d.__setitem__, 'key', value1) + + # setting a key to a value equal to the current value + # with dict.__setitem__() must change the version + self.check_version_changed(d, d.__setitem__, 'key', value2) + + # setting a key to a value equal to the current value + # with dict.update() must change the version + self.check_version_changed(d, d.update, key=value1) + + d2 = self.new_dict(key=value2) + self.check_version_changed(d, d.update, d2) + + def test_setdefault(self): + d = self.new_dict() + + # setting a key with dict.setdefault() must change the version + self.check_version_changed(d, d.setdefault, 'key', 'value1') + + # don't change the version if the key already exists + self.check_version_dont_change(d, d.setdefault, 'key', 'value2') + + def test_delitem(self): + d = self.new_dict(key='value') + + # deleting a key with dict.__delitem__() must change the version + self.check_version_changed(d, d.__delitem__, 'key') + + # don't change the version if the key doesn't exist + self.check_version_dont_change(d, self.assertRaises, KeyError, + d.__delitem__, 'key') + + def test_pop(self): + d = self.new_dict(key='value') + + # pop() must change the version if the key exists + self.check_version_changed(d, d.pop, 'key') + + # pop() must not change the version if the key does not exist + self.check_version_dont_change(d, self.assertRaises, KeyError, + d.pop, 'key') + + def test_popitem(self): + d = self.new_dict(key='value') + + # popitem() must change the version if the dict is not empty + self.check_version_changed(d, d.popitem) + + # popitem() must not change the version if the dict is empty + self.check_version_dont_change(d, self.assertRaises, KeyError, + d.popitem) + + def test_update(self): + d = self.new_dict(key='value') + + # update() calling with no argument must not change the version + self.check_version_dont_change(d, d.update) + + # update() must change the version + self.check_version_changed(d, d.update, key='new value') + + d2 = self.new_dict(key='value 3') + self.check_version_changed(d, d.update, d2) + + def test_clear(self): + d = self.new_dict(key='value') + + # clear() must change the version if the dict is not empty + self.check_version_changed(d, d.clear) + + # clear() must not change the version if the dict is empty + self.check_version_dont_change(d, d.clear) + + +class Dict(dict): + pass + + +class DictSubtypeVersionTests(DictVersionTests): + type2test = Dict + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 6084d2d70d..aa89506966 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -937,9 +937,9 @@ class SizeofTest(unittest.TestCase): # method-wrapper (descriptor object) check({}.__iter__, size('2P')) # dict - check({}, size('n2P') + calcsize('2nP2n') + 8 + (8*2//3)*calcsize('n2P')) + check({}, size('n2P') + 8 + calcsize('2nP2n') + 8 + (8*2//3)*calcsize('n2P')) longdict = {1:1, 2:2, 3:3, 4:4, 5:5, 6:6, 7:7, 8:8} - check(longdict, size('n2P') + calcsize('2nP2n') + 16 + (16*2//3)*calcsize('n2P')) + check(longdict, size('n2P') + 8 + calcsize('2nP2n') + 16 + (16*2//3)*calcsize('n2P')) # dictionary-keyview check({}.keys(), size('P')) # dictionary-valueview @@ -1103,7 +1103,7 @@ class SizeofTest(unittest.TestCase): class newstyleclass(object): pass check(newstyleclass, s) # dict with shared keys - check(newstyleclass().__dict__, size('n2P' + '2nP2n')) + check(newstyleclass().__dict__, size('n2P' + '2nP2n') + 8) # unicode # each tuple contains a string and its expected character size # don't put any static strings here, as they may contain -- cgit v1.2.1 From 89b185481a16054baec15dc0056afe2f58f781ec Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Thu, 8 Sep 2016 13:47:41 -0700 Subject: Remove legacy "from __future__ import with_statement" lines. --- Lib/lib2to3/refactor.py | 2 -- Lib/lib2to3/tests/test_parser.py | 2 -- Lib/lib2to3/tests/test_pytree.py | 2 -- Lib/lib2to3/tests/test_refactor.py | 2 -- 4 files changed, 8 deletions(-) (limited to 'Lib') diff --git a/Lib/lib2to3/refactor.py b/Lib/lib2to3/refactor.py index b60b9def4d..c5a1aa2d0c 100644 --- a/Lib/lib2to3/refactor.py +++ b/Lib/lib2to3/refactor.py @@ -8,8 +8,6 @@ recursively descend down directories. Imported as a module, this provides infrastructure to write your own refactoring tool. """ -from __future__ import with_statement - __author__ = "Guido van Rossum " diff --git a/Lib/lib2to3/tests/test_parser.py b/Lib/lib2to3/tests/test_parser.py index 7fc5537edd..9adb0317fa 100644 --- a/Lib/lib2to3/tests/test_parser.py +++ b/Lib/lib2to3/tests/test_parser.py @@ -6,8 +6,6 @@ parts of the grammar we've changed, we also make sure we can parse the test_grammar.py files from both Python 2 and Python 3. """ -from __future__ import with_statement - # Testing imports from . import support from .support import driver diff --git a/Lib/lib2to3/tests/test_pytree.py b/Lib/lib2to3/tests/test_pytree.py index a611d1715e..177126d545 100644 --- a/Lib/lib2to3/tests/test_pytree.py +++ b/Lib/lib2to3/tests/test_pytree.py @@ -9,8 +9,6 @@ more helpful than printing of (the first line of) the docstring, especially when debugging a test. """ -from __future__ import with_statement - # Testing imports from . import support diff --git a/Lib/lib2to3/tests/test_refactor.py b/Lib/lib2to3/tests/test_refactor.py index 94dc135fac..e9bae5e45d 100644 --- a/Lib/lib2to3/tests/test_refactor.py +++ b/Lib/lib2to3/tests/test_refactor.py @@ -2,8 +2,6 @@ Unit tests for refactor.py. """ -from __future__ import with_statement - import sys import os import codecs -- cgit v1.2.1 From 73392d64a0328bce848b448206b668f695640538 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Tue, 30 Aug 2016 21:22:36 -0700 Subject: Issue #1602: Windows console doesn't input or print Unicode (PEP 528) Closes #17602: Adds a readline implementation for the Windows console --- Lib/io.py | 7 +++++ Lib/test/test_os.py | 2 +- Lib/test/test_winconsoleio.py | 72 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 Lib/test/test_winconsoleio.py (limited to 'Lib') diff --git a/Lib/io.py b/Lib/io.py index e03db97e4d..968ee5073d 100644 --- a/Lib/io.py +++ b/Lib/io.py @@ -90,3 +90,10 @@ for klass in (BytesIO, BufferedReader, BufferedWriter, BufferedRandom, for klass in (StringIO, TextIOWrapper): TextIOBase.register(klass) del klass + +try: + from _io import _WindowsConsoleIO +except ImportError: + pass +else: + RawIOBase.register(_WindowsConsoleIO) diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index b504cf7976..95c74824d7 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -1518,7 +1518,7 @@ class TestInvalidFD(unittest.TestCase): singles = ["fchdir", "dup", "fdopen", "fdatasync", "fstat", "fstatvfs", "fsync", "tcgetpgrp", "ttyname"] #singles.append("close") - #We omit close because it doesn'r raise an exception on some platforms + #We omit close because it doesn't raise an exception on some platforms def get_single(f): def helper(self): if hasattr(os, f): diff --git a/Lib/test/test_winconsoleio.py b/Lib/test/test_winconsoleio.py new file mode 100644 index 0000000000..9e932fef1f --- /dev/null +++ b/Lib/test/test_winconsoleio.py @@ -0,0 +1,72 @@ +'''Tests for WindowsConsoleIO + +Unfortunately, most testing requires interactive use, since we have no +API to read back from a real console, and this class is only for use +with real consoles. + +Instead, we validate that basic functionality such as opening, closing +and in particular fileno() work, but are forced to leave real testing +to real people with real keyborads. +''' + +import io +import unittest +import sys + +if sys.platform != 'win32': + raise unittest.SkipTest("test only relevant on win32") + +ConIO = io._WindowsConsoleIO + +class WindowsConsoleIOTests(unittest.TestCase): + def test_abc(self): + self.assertTrue(issubclass(ConIO, io.RawIOBase)) + self.assertFalse(issubclass(ConIO, io.BufferedIOBase)) + self.assertFalse(issubclass(ConIO, io.TextIOBase)) + + def test_open_fd(self): + f = ConIO(0) + self.assertTrue(f.readable()) + self.assertFalse(f.writable()) + self.assertEqual(0, f.fileno()) + f.close() # multiple close should not crash + f.close() + + f = ConIO(1, 'w') + self.assertFalse(f.readable()) + self.assertTrue(f.writable()) + self.assertEqual(1, f.fileno()) + f.close() + f.close() + + f = ConIO(2, 'w') + self.assertFalse(f.readable()) + self.assertTrue(f.writable()) + self.assertEqual(2, f.fileno()) + f.close() + f.close() + + def test_open_name(self): + f = ConIO("CON") + self.assertTrue(f.readable()) + self.assertFalse(f.writable()) + self.assertIsNotNone(f.fileno()) + f.close() # multiple close should not crash + f.close() + + f = ConIO('CONIN$') + self.assertTrue(f.readable()) + self.assertFalse(f.writable()) + self.assertIsNotNone(f.fileno()) + f.close() + f.close() + + f = ConIO('CONOUT$', 'w') + self.assertFalse(f.readable()) + self.assertTrue(f.writable()) + self.assertIsNotNone(f.fileno()) + f.close() + f.close() + +if __name__ == "__main__": + unittest.main() -- cgit v1.2.1 From 1b83d52337d90736e2ceb3e2bbf5b39d4e34332e Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Thu, 8 Sep 2016 14:34:24 -0700 Subject: Skips console open_fd tests when we don't have real consoles. --- Lib/test/test_winconsoleio.py | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_winconsoleio.py b/Lib/test/test_winconsoleio.py index 9e932fef1f..ea3a712339 100644 --- a/Lib/test/test_winconsoleio.py +++ b/Lib/test/test_winconsoleio.py @@ -25,26 +25,29 @@ class WindowsConsoleIOTests(unittest.TestCase): self.assertFalse(issubclass(ConIO, io.TextIOBase)) def test_open_fd(self): - f = ConIO(0) - self.assertTrue(f.readable()) - self.assertFalse(f.writable()) - self.assertEqual(0, f.fileno()) - f.close() # multiple close should not crash - f.close() + if sys.stdin.fileno() == 0: + f = ConIO(0) + self.assertTrue(f.readable()) + self.assertFalse(f.writable()) + self.assertEqual(0, f.fileno()) + f.close() # multiple close should not crash + f.close() - f = ConIO(1, 'w') - self.assertFalse(f.readable()) - self.assertTrue(f.writable()) - self.assertEqual(1, f.fileno()) - f.close() - f.close() + if sys.stdout.fileno() == 1: + f = ConIO(1, 'w') + self.assertFalse(f.readable()) + self.assertTrue(f.writable()) + self.assertEqual(1, f.fileno()) + f.close() + f.close() - f = ConIO(2, 'w') - self.assertFalse(f.readable()) - self.assertTrue(f.writable()) - self.assertEqual(2, f.fileno()) - f.close() - f.close() + if sys.stderr.fileno() == 2: + f = ConIO(2, 'w') + self.assertFalse(f.readable()) + self.assertTrue(f.writable()) + self.assertEqual(2, f.fileno()) + f.close() + f.close() def test_open_name(self): f = ConIO("CON") -- cgit v1.2.1 From e0916e5e6b71a9097961e64e4ba2823276857761 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Thu, 8 Sep 2016 14:36:18 -0700 Subject: More lenient skipping of console tests. --- Lib/test/test_winconsoleio.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_winconsoleio.py b/Lib/test/test_winconsoleio.py index ea3a712339..ec26f068fc 100644 --- a/Lib/test/test_winconsoleio.py +++ b/Lib/test/test_winconsoleio.py @@ -25,24 +25,36 @@ class WindowsConsoleIOTests(unittest.TestCase): self.assertFalse(issubclass(ConIO, io.TextIOBase)) def test_open_fd(self): - if sys.stdin.fileno() == 0: + try: f = ConIO(0) + except ValueError: + # cannot open console because it's not a real console + pass + else: self.assertTrue(f.readable()) self.assertFalse(f.writable()) self.assertEqual(0, f.fileno()) f.close() # multiple close should not crash f.close() - if sys.stdout.fileno() == 1: + try: f = ConIO(1, 'w') + except ValueError: + # cannot open console because it's not a real console + pass + else: self.assertFalse(f.readable()) self.assertTrue(f.writable()) self.assertEqual(1, f.fileno()) f.close() f.close() - if sys.stderr.fileno() == 2: + try: f = ConIO(2, 'w') + except ValueError: + # cannot open console because it's not a real console + pass + else: self.assertFalse(f.readable()) self.assertTrue(f.writable()) self.assertEqual(2, f.fileno()) -- cgit v1.2.1 From 703d5e702019a06489e01aa120c50783e6496ef6 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Thu, 8 Sep 2016 14:45:40 -0700 Subject: Merge --- Lib/test/test_set.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py index 49abfb3e71..afa6e7f141 100644 --- a/Lib/test/test_set.py +++ b/Lib/test/test_set.py @@ -389,6 +389,21 @@ class TestSet(TestJointOps, unittest.TestCase): t = {1,2,3} self.assertEqual(s, t) + def test_set_literal_insertion_order(self): + # SF Issue #26020 -- Expect left to right insertion + s = {1, 1.0, True} + self.assertEqual(len(s), 1) + stored_value = s.pop() + self.assertEqual(type(stored_value), int) + + def test_set_literal_evaluation_order(self): + # Expect left to right expression evaluation + events = [] + def record(obj): + events.append(obj) + s = {record(1), record(2), record(3)} + self.assertEqual(events, [1, 2, 3]) + def test_hash(self): self.assertRaises(TypeError, hash, self.s) -- cgit v1.2.1 From 552a35af2ff406928c8d86564163502b9c968ebe Mon Sep 17 00:00:00 2001 From: R David Murray Date: Thu, 8 Sep 2016 17:57:06 -0400 Subject: Add policy keyword to email.generator.DecodedGenerator. --- Lib/email/generator.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/email/generator.py b/Lib/email/generator.py index 7c3cdc96d5..ae42cdfe22 100644 --- a/Lib/email/generator.py +++ b/Lib/email/generator.py @@ -452,7 +452,8 @@ class DecodedGenerator(Generator): Like the Generator base class, except that non-text parts are substituted with a format string representing the part. """ - def __init__(self, outfp, mangle_from_=None, maxheaderlen=78, fmt=None): + def __init__(self, outfp, mangle_from_=None, maxheaderlen=None, fmt=None, *, + policy=None): """Like Generator.__init__() except that an additional optional argument is allowed. @@ -474,7 +475,8 @@ class DecodedGenerator(Generator): [Non-text (%(type)s) part of message omitted, filename %(filename)s] """ - Generator.__init__(self, outfp, mangle_from_, maxheaderlen) + Generator.__init__(self, outfp, mangle_from_, maxheaderlen, + policy=policy) if fmt is None: self._fmt = _FMT else: -- cgit v1.2.1 From 761928cf31626534ebb81a6326c61b42050ef388 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 8 Sep 2016 15:11:11 -0700 Subject: Issue #24254: Drop cls.__definition_order__. --- Lib/test/test_builtin.py | 192 +-------------------------------------------- Lib/test/test_metaclass.py | 11 +-- Lib/test/test_pydoc.py | 8 +- Lib/test/test_sys.py | 2 +- Lib/test/test_types.py | 22 ------ Lib/types.py | 5 +- Lib/typing.py | 1 - 7 files changed, 7 insertions(+), 234 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index c0343eff68..e0cffe1d9c 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -16,10 +16,8 @@ import traceback import types import unittest import warnings -from collections import OrderedDict from operator import neg -from test.support import (TESTFN, unlink, run_unittest, check_warnings, - cpython_only) +from test.support import TESTFN, unlink, run_unittest, check_warnings from test.support.script_helper import assert_python_ok try: import pty, signal @@ -1780,194 +1778,6 @@ class TestType(unittest.TestCase): A.__doc__ = doc self.assertEqual(A.__doc__, doc) - def test_type_definition_order_nonempty(self): - class Spam: - b = 1 - c = 3 - a = 2 - d = 4 - eggs = 2 - e = 5 - b = 42 - - self.assertEqual(Spam.__definition_order__, - ('__module__', '__qualname__', - 'b', 'c', 'a', 'd', 'eggs', 'e')) - - def test_type_definition_order_empty(self): - class Empty: - pass - - self.assertEqual(Empty.__definition_order__, - ('__module__', '__qualname__')) - - def test_type_definition_order_on_instance(self): - class Spam: - a = 2 - b = 1 - c = 3 - with self.assertRaises(AttributeError): - Spam().__definition_order__ - - def test_type_definition_order_set_to_None(self): - class Spam: - a = 2 - b = 1 - c = 3 - Spam.__definition_order__ = None - self.assertEqual(Spam.__definition_order__, None) - - def test_type_definition_order_set_to_tuple(self): - class Spam: - a = 2 - b = 1 - c = 3 - Spam.__definition_order__ = ('x', 'y', 'z') - self.assertEqual(Spam.__definition_order__, ('x', 'y', 'z')) - - def test_type_definition_order_deleted(self): - class Spam: - a = 2 - b = 1 - c = 3 - del Spam.__definition_order__ - self.assertEqual(Spam.__definition_order__, None) - - def test_type_definition_order_set_to_bad_type(self): - class Spam: - a = 2 - b = 1 - c = 3 - Spam.__definition_order__ = 42 - self.assertEqual(Spam.__definition_order__, 42) - - def test_type_definition_order_builtins(self): - self.assertEqual(object.__definition_order__, None) - self.assertEqual(type.__definition_order__, None) - self.assertEqual(dict.__definition_order__, None) - self.assertEqual(type(None).__definition_order__, None) - - def test_type_definition_order_dunder_names_included(self): - class Dunder: - VAR = 3 - def __init__(self): - pass - - self.assertEqual(Dunder.__definition_order__, - ('__module__', '__qualname__', - 'VAR', '__init__')) - - def test_type_definition_order_only_dunder_names(self): - class DunderOnly: - __xyz__ = None - def __init__(self): - pass - - self.assertEqual(DunderOnly.__definition_order__, - ('__module__', '__qualname__', - '__xyz__', '__init__')) - - def test_type_definition_order_underscore_names(self): - class HalfDunder: - __whether_to_be = True - or_not_to_be__ = False - - self.assertEqual(HalfDunder.__definition_order__, - ('__module__', '__qualname__', - '_HalfDunder__whether_to_be', 'or_not_to_be__')) - - def test_type_definition_order_with_slots(self): - class Slots: - __slots__ = ('x', 'y') - a = 1 - b = 2 - - self.assertEqual(Slots.__definition_order__, - ('__module__', '__qualname__', - '__slots__', 'a', 'b')) - - def test_type_definition_order_overwritten_None(self): - class OverwrittenNone: - __definition_order__ = None - a = 1 - b = 2 - c = 3 - - self.assertEqual(OverwrittenNone.__definition_order__, None) - - def test_type_definition_order_overwritten_tuple(self): - class OverwrittenTuple: - __definition_order__ = ('x', 'y', 'z') - a = 1 - b = 2 - c = 3 - - self.assertEqual(OverwrittenTuple.__definition_order__, - ('x', 'y', 'z')) - - def test_type_definition_order_overwritten_bad_item(self): - with self.assertRaises(TypeError): - class PoorlyOverwritten: - __definition_order__ = ('a', 2, 'c') - a = 1 - b = 2 - c = 3 - - def test_type_definition_order_overwritten_bad_type(self): - with self.assertRaises(TypeError): - class PoorlyOverwritten: - __definition_order__ = ['a', 2, 'c'] - a = 1 - b = 2 - c = 3 - - def test_type_definition_order_metaclass(self): - class Meta(type): - SPAM = 42 - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - self.assertEqual(Meta.__definition_order__, - ('__module__', '__qualname__', - 'SPAM', '__init__')) - - def test_type_definition_order_OrderedDict(self): - class Meta(type): - def __prepare__(self, *args, **kwargs): - return OrderedDict() - - class WithODict(metaclass=Meta): - x='y' - - self.assertEqual(WithODict.__definition_order__, - ('__module__', '__qualname__', 'x')) - - class Meta(type): - def __prepare__(self, *args, **kwargs): - class ODictSub(OrderedDict): - pass - return ODictSub() - - class WithODictSub(metaclass=Meta): - x='y' - - self.assertEqual(WithODictSub.__definition_order__, - ('__module__', '__qualname__', 'x')) - - @cpython_only - def test_type_definition_order_cpython(self): - # some implementations will have an ordered-by-default dict. - - class Meta(type): - def __prepare__(self, *args, **kwargs): - return {} - - class NotOrdered(metaclass=Meta): - x='y' - - self.assertEqual(NotOrdered.__definition_order__, None) - def test_bad_args(self): with self.assertRaises(TypeError): type() diff --git a/Lib/test/test_metaclass.py b/Lib/test/test_metaclass.py index 4db792ecef..e6fe20a107 100644 --- a/Lib/test/test_metaclass.py +++ b/Lib/test/test_metaclass.py @@ -180,7 +180,7 @@ Use a metaclass that doesn't derive from type. meta: C () ns: [('__module__', 'test.test_metaclass'), ('__qualname__', 'C'), ('a', 42), ('b', 24)] kw: [] - >>> type(C) is types._DefaultClassNamespaceType + >>> type(C) is dict True >>> print(sorted(C.items())) [('__module__', 'test.test_metaclass'), ('__qualname__', 'C'), ('a', 42), ('b', 24)] @@ -211,11 +211,8 @@ And again, with a __prepare__ attribute. The default metaclass must define a __prepare__() method. - >>> ns = type.__prepare__() - >>> type(ns) is types._DefaultClassNamespaceType - True - >>> list(ns) == [] - True + >>> type.__prepare__() + {} >>> Make sure it works with subclassing. @@ -251,9 +248,7 @@ Test failures in looking up the __prepare__ method work. """ -from collections import OrderedDict import sys -import types # Trace function introduces __locals__ which causes various tests to fail. if hasattr(sys, 'gettrace') and sys.gettrace(): diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py index 229fff47c9..17a82c269a 100644 --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -427,7 +427,6 @@ class PydocDocTest(unittest.TestCase): expected_html = expected_html_pattern % ( (mod_url, mod_file, doc_loc) + expected_html_data_docstrings) - self.maxDiff = None self.assertEqual(result, expected_html) @unittest.skipIf(sys.flags.optimize >= 2, @@ -474,18 +473,13 @@ class PydocDocTest(unittest.TestCase): def test_non_str_name(self): # issue14638 # Treat illegal (non-str) name like no name - # Definition order is set to None so it looks the same in both - # cases. class A: - __definition_order__ = None __name__ = 42 class B: pass adoc = pydoc.render_doc(A()) bdoc = pydoc.render_doc(B()) - self.maxDiff = None - expected = adoc.replace("A", "B") - self.assertEqual(bdoc, expected) + self.assertEqual(adoc.replace("A", "B"), bdoc) def test_not_here(self): missing_module = "test.i_am_not_here" diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index aa89506966..a6cd95ed49 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1085,7 +1085,7 @@ class SizeofTest(unittest.TestCase): check((1,2,3), vsize('') + 3*self.P) # type # static type: PyTypeObject - fmt = 'P2n15Pl4Pn9Pn11PIPP' + fmt = 'P2n15Pl4Pn9Pn11PIP' if hasattr(sys, 'getcounts'): fmt += '3n2P' s = vsize(fmt) diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index e5e110f9c2..a202196bd2 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -825,28 +825,6 @@ class ClassCreationTests(unittest.TestCase): self.assertEqual(C.y, 1) self.assertEqual(C.z, 2) - def test_new_class_deforder(self): - C = types.new_class("C") - self.assertEqual(C.__definition_order__, tuple()) - - Meta = self.Meta - def func(ns): - ns["x"] = 0 - D = types.new_class("D", (), {"metaclass": Meta, "z": 2}, func) - self.assertEqual(D.__definition_order__, ('y', 'z', 'x')) - - def func(ns): - ns["__definition_order__"] = None - ns["x"] = 0 - D = types.new_class("D", (), {"metaclass": Meta, "z": 2}, func) - self.assertEqual(D.__definition_order__, None) - - def func(ns): - ns["__definition_order__"] = ('a', 'b', 'c') - ns["x"] = 0 - D = types.new_class("D", (), {"metaclass": Meta, "z": 2}, func) - self.assertEqual(D.__definition_order__, ('a', 'b', 'c')) - # Many of the following tests are derived from test_descr.py def test_prepare_class(self): # Basic test of metaclass derivation diff --git a/Lib/types.py b/Lib/types.py index cc093cb403..48891cd3f6 100644 --- a/Lib/types.py +++ b/Lib/types.py @@ -25,11 +25,8 @@ CoroutineType = type(_c) _c.close() # Prevent ResourceWarning class _C: - _nsType = type(locals()) def _m(self): pass MethodType = type(_C()._m) -# In CPython, this should end up as OrderedDict. -_DefaultClassNamespaceType = _C._nsType BuiltinFunctionType = type(len) BuiltinMethodType = type([].append) # Same as BuiltinFunctionType @@ -88,7 +85,7 @@ def prepare_class(name, bases=(), kwds=None): if hasattr(meta, '__prepare__'): ns = meta.__prepare__(name, bases, **kwds) else: - ns = _DefaultClassNamespaceType() + ns = {} return meta, ns, kwds def _calculate_meta(meta, bases): diff --git a/Lib/typing.py b/Lib/typing.py index 5f451b0ca1..5573a1fbf9 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1301,7 +1301,6 @@ class _ProtocolMeta(GenericMeta): if (not attr.startswith('_abc_') and attr != '__abstractmethods__' and attr != '_is_protocol' and - attr != '__definition_order__' and attr != '__dict__' and attr != '__args__' and attr != '__slots__' and -- cgit v1.2.1 From b164476aaf77ceffac36ddbbdc7f4df90fbfebd3 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Thu, 8 Sep 2016 20:50:03 -0700 Subject: Issue #27985: Implement PEP 526 -- Syntax for Variable Annotations. Patch by Ivan Levkivskyi. --- Lib/importlib/_bootstrap_external.py | 4 +- Lib/lib2to3/Grammar.txt | 5 +- Lib/lib2to3/tests/test_parser.py | 30 ++++ Lib/opcode.py | 3 +- Lib/symbol.py | 137 +++++++++--------- Lib/symtable.py | 5 +- Lib/test/ann_module.py | 53 +++++++ Lib/test/ann_module2.py | 36 +++++ Lib/test/ann_module3.py | 18 +++ Lib/test/pydoc_mod.py | 2 +- Lib/test/test___all__.py | 2 + Lib/test/test_dis.py | 33 +++++ Lib/test/test_grammar.py | 178 ++++++++++++++++++++++++ Lib/test/test_opcodes.py | 27 ++++ Lib/test/test_parser.py | 39 ++++++ Lib/test/test_pydoc.py | 4 + Lib/test/test_symtable.py | 11 ++ Lib/test/test_tools/test_com2ann.py | 260 +++++++++++++++++++++++++++++++++++ Lib/test/test_typing.py | 83 ++++++++++- Lib/typing.py | 146 ++++++++++++++++++-- 20 files changed, 987 insertions(+), 89 deletions(-) create mode 100644 Lib/test/ann_module.py create mode 100644 Lib/test/ann_module2.py create mode 100644 Lib/test/ann_module3.py create mode 100644 Lib/test/test_tools/test_com2ann.py (limited to 'Lib') diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 828246cf9c..ffb93255b2 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -234,6 +234,8 @@ _code_type = type(_write_atomic.__code__) # Python 3.6a1 3372 (MAKE_FUNCTION simplification, remove MAKE_CLOSURE # #27095) # Python 3.6b1 3373 (add BUILD_STRING opcode #27078) +# Python 3.6b1 3375 (add SETUP_ANNOTATIONS and STORE_ANNOTATION opcodes +# #27985) # # MAGIC must change whenever the bytecode emitted by the compiler may no # longer be understood by older implementations of the eval loop (usually @@ -242,7 +244,7 @@ _code_type = type(_write_atomic.__code__) # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # in PC/launcher.c must also be updated. -MAGIC_NUMBER = (3373).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3375).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c _PYCACHE = '__pycache__' diff --git a/Lib/lib2to3/Grammar.txt b/Lib/lib2to3/Grammar.txt index c954669e8a..abe1268c27 100644 --- a/Lib/lib2to3/Grammar.txt +++ b/Lib/lib2to3/Grammar.txt @@ -54,12 +54,13 @@ stmt: simple_stmt | compound_stmt simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE small_stmt: (expr_stmt | print_stmt | del_stmt | pass_stmt | flow_stmt | import_stmt | global_stmt | exec_stmt | assert_stmt) -expr_stmt: testlist_star_expr (augassign (yield_expr|testlist) | +expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) | ('=' (yield_expr|testlist_star_expr))*) +annassign: ':' test ['=' test] testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [','] augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' | '<<=' | '>>=' | '**=' | '//=') -# For normal assignments, additional restrictions enforced by the interpreter +# For normal and annotated assignments, additional restrictions enforced by the interpreter print_stmt: 'print' ( [ test (',' test)* [','] ] | '>>' test [ (',' test)+ [','] ] ) del_stmt: 'del' exprlist diff --git a/Lib/lib2to3/tests/test_parser.py b/Lib/lib2to3/tests/test_parser.py index 9adb0317fa..b37816374f 100644 --- a/Lib/lib2to3/tests/test_parser.py +++ b/Lib/lib2to3/tests/test_parser.py @@ -237,6 +237,36 @@ class TestFunctionAnnotations(GrammarTest): self.validate(s) +# Adapted from Python 3's Lib/test/test_grammar.py:GrammarTests.test_var_annot +class TestFunctionAnnotations(GrammarTest): + def test_1(self): + self.validate("var1: int = 5") + + def test_2(self): + self.validate("var2: [int, str]") + + def test_3(self): + self.validate("def f():\n" + " st: str = 'Hello'\n" + " a.b: int = (1, 2)\n" + " return st\n") + + def test_4(self): + self.validate("def fbad():\n" + " x: int\n" + " print(x)\n") + + def test_5(self): + self.validate("class C:\n" + " x: int\n" + " s: str = 'attr'\n" + " z = 2\n" + " def __init__(self, x):\n" + " self.x: int = x\n") + + def test_6(self): + self.validate("lst: List[int] = []") + class TestExcept(GrammarTest): def test_new(self): s = """ diff --git a/Lib/opcode.py b/Lib/opcode.py index d9202e8353..31d15345e9 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -119,7 +119,7 @@ def_op('WITH_CLEANUP_FINISH', 82) def_op('RETURN_VALUE', 83) def_op('IMPORT_STAR', 84) - +def_op('SETUP_ANNOTATIONS', 85) def_op('YIELD_VALUE', 86) def_op('POP_BLOCK', 87) def_op('END_FINALLY', 88) @@ -169,6 +169,7 @@ def_op('STORE_FAST', 125) # Local variable number haslocal.append(125) def_op('DELETE_FAST', 126) # Local variable number haslocal.append(126) +name_op('STORE_ANNOTATION', 127) # Index in name list def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3) def_op('CALL_FUNCTION', 131) # #args + (#kwargs << 8) diff --git a/Lib/symbol.py b/Lib/symbol.py index 7541497163..d9f01e081a 100755 --- a/Lib/symbol.py +++ b/Lib/symbol.py @@ -27,74 +27,75 @@ stmt = 269 simple_stmt = 270 small_stmt = 271 expr_stmt = 272 -testlist_star_expr = 273 -augassign = 274 -del_stmt = 275 -pass_stmt = 276 -flow_stmt = 277 -break_stmt = 278 -continue_stmt = 279 -return_stmt = 280 -yield_stmt = 281 -raise_stmt = 282 -import_stmt = 283 -import_name = 284 -import_from = 285 -import_as_name = 286 -dotted_as_name = 287 -import_as_names = 288 -dotted_as_names = 289 -dotted_name = 290 -global_stmt = 291 -nonlocal_stmt = 292 -assert_stmt = 293 -compound_stmt = 294 -async_stmt = 295 -if_stmt = 296 -while_stmt = 297 -for_stmt = 298 -try_stmt = 299 -with_stmt = 300 -with_item = 301 -except_clause = 302 -suite = 303 -test = 304 -test_nocond = 305 -lambdef = 306 -lambdef_nocond = 307 -or_test = 308 -and_test = 309 -not_test = 310 -comparison = 311 -comp_op = 312 -star_expr = 313 -expr = 314 -xor_expr = 315 -and_expr = 316 -shift_expr = 317 -arith_expr = 318 -term = 319 -factor = 320 -power = 321 -atom_expr = 322 -atom = 323 -testlist_comp = 324 -trailer = 325 -subscriptlist = 326 -subscript = 327 -sliceop = 328 -exprlist = 329 -testlist = 330 -dictorsetmaker = 331 -classdef = 332 -arglist = 333 -argument = 334 -comp_iter = 335 -comp_for = 336 -comp_if = 337 -encoding_decl = 338 -yield_expr = 339 -yield_arg = 340 +annassign = 273 +testlist_star_expr = 274 +augassign = 275 +del_stmt = 276 +pass_stmt = 277 +flow_stmt = 278 +break_stmt = 279 +continue_stmt = 280 +return_stmt = 281 +yield_stmt = 282 +raise_stmt = 283 +import_stmt = 284 +import_name = 285 +import_from = 286 +import_as_name = 287 +dotted_as_name = 288 +import_as_names = 289 +dotted_as_names = 290 +dotted_name = 291 +global_stmt = 292 +nonlocal_stmt = 293 +assert_stmt = 294 +compound_stmt = 295 +async_stmt = 296 +if_stmt = 297 +while_stmt = 298 +for_stmt = 299 +try_stmt = 300 +with_stmt = 301 +with_item = 302 +except_clause = 303 +suite = 304 +test = 305 +test_nocond = 306 +lambdef = 307 +lambdef_nocond = 308 +or_test = 309 +and_test = 310 +not_test = 311 +comparison = 312 +comp_op = 313 +star_expr = 314 +expr = 315 +xor_expr = 316 +and_expr = 317 +shift_expr = 318 +arith_expr = 319 +term = 320 +factor = 321 +power = 322 +atom_expr = 323 +atom = 324 +testlist_comp = 325 +trailer = 326 +subscriptlist = 327 +subscript = 328 +sliceop = 329 +exprlist = 330 +testlist = 331 +dictorsetmaker = 332 +classdef = 333 +arglist = 334 +argument = 335 +comp_iter = 336 +comp_for = 337 +comp_if = 338 +encoding_decl = 339 +yield_expr = 340 +yield_arg = 341 #--end constants-- sym_name = {} diff --git a/Lib/symtable.py b/Lib/symtable.py index 84fec4aa66..b0e52603dc 100644 --- a/Lib/symtable.py +++ b/Lib/symtable.py @@ -2,7 +2,7 @@ import _symtable from _symtable import (USE, DEF_GLOBAL, DEF_LOCAL, DEF_PARAM, - DEF_IMPORT, DEF_BOUND, SCOPE_OFF, SCOPE_MASK, FREE, + DEF_IMPORT, DEF_BOUND, DEF_ANNOT, SCOPE_OFF, SCOPE_MASK, FREE, LOCAL, GLOBAL_IMPLICIT, GLOBAL_EXPLICIT, CELL) import weakref @@ -190,6 +190,9 @@ class Symbol(object): def is_local(self): return bool(self.__flags & DEF_BOUND) + def is_annotated(self): + return bool(self.__flags & DEF_ANNOT) + def is_free(self): return bool(self.__scope == FREE) diff --git a/Lib/test/ann_module.py b/Lib/test/ann_module.py new file mode 100644 index 0000000000..9e6b87dac4 --- /dev/null +++ b/Lib/test/ann_module.py @@ -0,0 +1,53 @@ + + +""" +The module for testing variable annotations. +Empty lines above are for good reason (testing for correct line numbers) +""" + +from typing import Optional + +__annotations__[1] = 2 + +class C: + + x = 5; y: Optional['C'] = None + +from typing import Tuple +x: int = 5; y: str = x; f: Tuple[int, int] + +class M(type): + + __annotations__['123'] = 123 + o: type = object + +(pars): bool = True + +class D(C): + j: str = 'hi'; k: str= 'bye' + +from types import new_class +h_class = new_class('H', (C,)) +j_class = new_class('J') + +class F(): + z: int = 5 + def __init__(self, x): + pass + +class Y(F): + def __init__(self): + super(F, self).__init__(123) + +class Meta(type): + def __new__(meta, name, bases, namespace): + return super().__new__(meta, name, bases, namespace) + +class S(metaclass = Meta): + x: str = 'something' + y: str = 'something else' + +def foo(x: int = 10): + def bar(y: List[str]): + x: str = 'yes' + bar() diff --git a/Lib/test/ann_module2.py b/Lib/test/ann_module2.py new file mode 100644 index 0000000000..76cf5b3ad9 --- /dev/null +++ b/Lib/test/ann_module2.py @@ -0,0 +1,36 @@ +""" +Some correct syntax for variable annotation here. +More examples are in test_grammar and test_parser. +""" + +from typing import no_type_check, ClassVar + +i: int = 1 +j: int +x: float = i/10 + +def f(): + class C: ... + return C() + +f().new_attr: object = object() + +class C: + def __init__(self, x: int) -> None: + self.x = x + +c = C(5) +c.new_attr: int = 10 + +__annotations__ = {} + + +@no_type_check +class NTC: + def meth(self, param: complex) -> None: + ... + +class CV: + var: ClassVar['CV'] + +CV.var = CV() diff --git a/Lib/test/ann_module3.py b/Lib/test/ann_module3.py new file mode 100644 index 0000000000..eccd7be22d --- /dev/null +++ b/Lib/test/ann_module3.py @@ -0,0 +1,18 @@ +""" +Correct syntax for variable annotation that should fail at runtime +in a certain manner. More examples are in test_grammar and test_parser. +""" + +def f_bad_ann(): + __annotations__[1] = 2 + +class C_OK: + def __init__(self, x: int) -> None: + self.x: no_such_name = x # This one is OK as proposed by Guido + +class D_bad_ann: + def __init__(self, x: int) -> None: + sfel.y: int = 0 + +def g_bad_ann(): + no_such_name.attr: int = 0 diff --git a/Lib/test/pydoc_mod.py b/Lib/test/pydoc_mod.py index cda1c9e231..9c1fff5c2f 100644 --- a/Lib/test/pydoc_mod.py +++ b/Lib/test/pydoc_mod.py @@ -12,7 +12,7 @@ class A: pass class B(object): - NO_MEANING = "eggs" + NO_MEANING: str = "eggs" pass class C(object): diff --git a/Lib/test/test___all__.py b/Lib/test/test___all__.py index e94d984f2b..ae9114e5f0 100644 --- a/Lib/test/test___all__.py +++ b/Lib/test/test___all__.py @@ -38,6 +38,8 @@ class AllTest(unittest.TestCase): modname, e.__class__.__name__, e)) if "__builtins__" in names: del names["__builtins__"] + if '__annotations__' in names: + del names['__annotations__'] keys = set(names) all_list = sys.modules[modname].__all__ all_set = set(all_list) diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 09e68ce70a..60810732c5 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -207,6 +207,38 @@ dis_simple_stmt_str = """\ 10 RETURN_VALUE """ +annot_stmt_str = """\ + +x: int = 1 +y: fun(1) +lst[fun(0)]: int = 1 +""" +# leading newline is for a reason (tests lineno) + +dis_annot_stmt_str = """\ + 2 0 SETUP_ANNOTATIONS + 2 LOAD_CONST 0 (1) + 4 STORE_NAME 0 (x) + 6 LOAD_NAME 1 (int) + 8 STORE_ANNOTATION 0 (x) + + 3 10 LOAD_NAME 2 (fun) + 12 LOAD_CONST 0 (1) + 14 CALL_FUNCTION 1 (1 positional, 0 keyword pair) + 16 STORE_ANNOTATION 3 (y) + + 4 18 LOAD_CONST 0 (1) + 20 LOAD_NAME 4 (lst) + 22 LOAD_NAME 2 (fun) + 24 LOAD_CONST 1 (0) + 26 CALL_FUNCTION 1 (1 positional, 0 keyword pair) + 28 STORE_SUBSCR + 30 LOAD_NAME 1 (int) + 32 POP_TOP + 34 LOAD_CONST 2 (None) + 36 RETURN_VALUE +""" + compound_stmt_str = """\ x = 0 while 1: @@ -345,6 +377,7 @@ class DisTests(unittest.TestCase): def test_disassemble_str(self): self.do_disassembly_test(expr_str, dis_expr_str) self.do_disassembly_test(simple_stmt_str, dis_simple_stmt_str) + self.do_disassembly_test(annot_stmt_str, dis_annot_stmt_str) self.do_disassembly_test(compound_stmt_str, dis_compound_stmt_str) def test_disassemble_bytes(self): diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py index bfe5225f77..109013f5e2 100644 --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -8,6 +8,14 @@ import sys # testing import * from sys import * +# different import patterns to check that __annotations__ does not interfere +# with import machinery +import test.ann_module as ann_module +import typing +from collections import ChainMap +from test import ann_module2 +import test + class TokenTests(unittest.TestCase): @@ -139,6 +147,19 @@ the \'lazy\' dog.\n\ compile(s, "", "exec") self.assertIn("unexpected EOF", str(cm.exception)) +var_annot_global: int # a global annotated is necessary for test_var_annot + +# custom namespace for testing __annotations__ + +class CNS: + def __init__(self): + self._dct = {} + def __setitem__(self, item, value): + self._dct[item.lower()] = value + def __getitem__(self, item): + return self._dct[item] + + class GrammarTests(unittest.TestCase): # single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE @@ -154,6 +175,163 @@ class GrammarTests(unittest.TestCase): # testlist ENDMARKER x = eval('1, 0 or 1') + def test_var_annot_basics(self): + # all these should be allowed + var1: int = 5 + var2: [int, str] + my_lst = [42] + def one(): + return 1 + int.new_attr: int + [list][0]: type + my_lst[one()-1]: int = 5 + self.assertEqual(my_lst, [5]) + + def test_var_annot_syntax_errors(self): + # parser pass + check_syntax_error(self, "def f: int") + check_syntax_error(self, "x: int: str") + check_syntax_error(self, "def f():\n" + " nonlocal x: int\n") + # AST pass + check_syntax_error(self, "[x, 0]: int\n") + check_syntax_error(self, "f(): int\n") + check_syntax_error(self, "(x,): int") + check_syntax_error(self, "def f():\n" + " (x, y): int = (1, 2)\n") + # symtable pass + check_syntax_error(self, "def f():\n" + " x: int\n" + " global x\n") + check_syntax_error(self, "def f():\n" + " global x\n" + " x: int\n") + + def test_var_annot_basic_semantics(self): + # execution order + with self.assertRaises(ZeroDivisionError): + no_name[does_not_exist]: no_name_again = 1/0 + with self.assertRaises(NameError): + no_name[does_not_exist]: 1/0 = 0 + global var_annot_global + + # function semantics + def f(): + st: str = "Hello" + a.b: int = (1, 2) + return st + self.assertEqual(f.__annotations__, {}) + def f_OK(): + x: 1/0 + f_OK() + def fbad(): + x: int + print(x) + with self.assertRaises(UnboundLocalError): + fbad() + def f2bad(): + (no_such_global): int + print(no_such_global) + try: + f2bad() + except Exception as e: + self.assertIs(type(e), NameError) + + # class semantics + class C: + x: int + s: str = "attr" + z = 2 + def __init__(self, x): + self.x: int = x + self.assertEqual(C.__annotations__, {'x': int, 's': str}) + with self.assertRaises(NameError): + class CBad: + no_such_name_defined.attr: int = 0 + with self.assertRaises(NameError): + class Cbad2(C): + x: int + x.y: list = [] + + def test_var_annot_metaclass_semantics(self): + class CMeta(type): + @classmethod + def __prepare__(metacls, name, bases, **kwds): + return {'__annotations__': CNS()} + class CC(metaclass=CMeta): + XX: 'ANNOT' + self.assertEqual(CC.__annotations__['xx'], 'ANNOT') + + def test_var_annot_module_semantics(self): + with self.assertRaises(AttributeError): + print(test.__annotations__) + self.assertEqual(ann_module.__annotations__, + {1: 2, 'x': int, 'y': str, 'f': typing.Tuple[int, int]}) + self.assertEqual(ann_module.M.__annotations__, + {'123': 123, 'o': type}) + self.assertEqual(ann_module2.__annotations__, {}) + self.assertEqual(typing.get_type_hints(ann_module2.CV, + ann_module2.__dict__), + ChainMap({'var': typing.ClassVar[ann_module2.CV]}, {})) + + def test_var_annot_in_module(self): + # check that functions fail the same way when executed + # outside of module where they were defined + from test.ann_module3 import f_bad_ann, g_bad_ann, D_bad_ann + with self.assertRaises(NameError): + f_bad_ann() + with self.assertRaises(NameError): + g_bad_ann() + with self.assertRaises(NameError): + D_bad_ann(5) + + def test_var_annot_simple_exec(self): + gns = {}; lns= {} + exec("'docstring'\n" + "__annotations__[1] = 2\n" + "x: int = 5\n", gns, lns) + self.assertEqual(lns["__annotations__"], {1: 2, 'x': int}) + with self.assertRaises(KeyError): + gns['__annotations__'] + + def test_var_annot_custom_maps(self): + # tests with custom locals() and __annotations__ + ns = {'__annotations__': CNS()} + exec('X: int; Z: str = "Z"; (w): complex = 1j', ns) + self.assertEqual(ns['__annotations__']['x'], int) + self.assertEqual(ns['__annotations__']['z'], str) + with self.assertRaises(KeyError): + ns['__annotations__']['w'] + nonloc_ns = {} + class CNS2: + def __init__(self): + self._dct = {} + def __setitem__(self, item, value): + nonlocal nonloc_ns + self._dct[item] = value + nonloc_ns[item] = value + def __getitem__(self, item): + return self._dct[item] + exec('x: int = 1', {}, CNS2()) + self.assertEqual(nonloc_ns['__annotations__']['x'], int) + + def test_var_annot_refleak(self): + # complex case: custom locals plus custom __annotations__ + # this was causing refleak + cns = CNS() + nonloc_ns = {'__annotations__': cns} + class CNS2: + def __init__(self): + self._dct = {'__annotations__': cns} + def __setitem__(self, item, value): + nonlocal nonloc_ns + self._dct[item] = value + nonloc_ns[item] = value + def __getitem__(self, item): + return self._dct[item] + exec('X: str', {}, CNS2()) + self.assertEqual(nonloc_ns['__annotations__']['x'], str) + def test_funcdef(self): ### [decorators] 'def' NAME parameters ['->' test] ':' suite ### decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE diff --git a/Lib/test/test_opcodes.py b/Lib/test/test_opcodes.py index 6ef93d9500..6806c616cb 100644 --- a/Lib/test/test_opcodes.py +++ b/Lib/test/test_opcodes.py @@ -1,6 +1,7 @@ # Python test set -- part 2, opcodes import unittest +from test import ann_module class OpcodeTest(unittest.TestCase): @@ -20,6 +21,32 @@ class OpcodeTest(unittest.TestCase): if n != 90: self.fail('try inside for') + def test_setup_annotations_line(self): + # check that SETUP_ANNOTATIONS does not create spurious line numbers + try: + with open(ann_module.__file__) as f: + txt = f.read() + co = compile(txt, ann_module.__file__, 'exec') + self.assertEqual(co.co_firstlineno, 6) + except OSError: + pass + + def test_no_annotations_if_not_needed(self): + class C: pass + with self.assertRaises(AttributeError): + C.__annotations__ + + def test_use_existing_annotations(self): + ns = {'__annotations__': {1: 2}} + exec('x: int', ns) + self.assertEqual(ns['__annotations__'], {'x': int, 1: 2}) + + def test_do_not_recreate_annotations(self): + class C: + del __annotations__ + with self.assertRaises(NameError): + x: int + def test_raise_class_exceptions(self): class AClass(Exception): pass diff --git a/Lib/test/test_parser.py b/Lib/test/test_parser.py index e2a42f9715..d6e6f71577 100644 --- a/Lib/test/test_parser.py +++ b/Lib/test/test_parser.py @@ -138,6 +138,45 @@ class RoundtripLegalSyntaxTestCase(unittest.TestCase): self.check_suite("a = b") self.check_suite("a = b = c = d = e") + def test_var_annot(self): + self.check_suite("x: int = 5") + self.check_suite("y: List[T] = []; z: [list] = fun()") + self.check_suite("x: tuple = (1, 2)") + self.check_suite("d[f()]: int = 42") + self.check_suite("f(d[x]): str = 'abc'") + self.check_suite("x.y.z.w: complex = 42j") + self.check_suite("x: int") + self.check_suite("def f():\n" + " x: str\n" + " y: int = 5\n") + self.check_suite("class C:\n" + " x: str\n" + " y: int = 5\n") + self.check_suite("class C:\n" + " def __init__(self, x: int) -> None:\n" + " self.x: int = x\n") + # double check for nonsense + with self.assertRaises(SyntaxError): + exec("2+2: int", {}, {}) + with self.assertRaises(SyntaxError): + exec("[]: int = 5", {}, {}) + with self.assertRaises(SyntaxError): + exec("x, *y, z: int = range(5)", {}, {}) + with self.assertRaises(SyntaxError): + exec("t: tuple = 1, 2", {}, {}) + with self.assertRaises(SyntaxError): + exec("u = v: int", {}, {}) + with self.assertRaises(SyntaxError): + exec("False: int", {}, {}) + with self.assertRaises(SyntaxError): + exec("x.False: int", {}, {}) + with self.assertRaises(SyntaxError): + exec("x.y,: int", {}, {}) + with self.assertRaises(SyntaxError): + exec("[0]: int", {}, {}) + with self.assertRaises(SyntaxError): + exec("f(): int", {}, {}) + def test_simple_augmented_assignments(self): self.check_suite("a += b") self.check_suite("a -= b") diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py index 17a82c269a..5174d561bd 100644 --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -83,6 +83,8 @@ CLASSES | Data and other attributes defined here: |\x20\x20 | NO_MEANING = 'eggs' + |\x20\x20 + | __annotations__ = {'NO_MEANING': } \x20\x20\x20\x20 class C(builtins.object) | Methods defined here: @@ -195,6 +197,8 @@ Data descriptors defined here:
Data and other attributes defined here:
NO_MEANING = 'eggs'
+
__annotations__ = {'NO_MEANING': <class 'str'>}
+

diff --git a/Lib/test/test_symtable.py b/Lib/test/test_symtable.py index bf99505623..30471653c3 100644 --- a/Lib/test/test_symtable.py +++ b/Lib/test/test_symtable.py @@ -133,6 +133,17 @@ class SymtableTest(unittest.TestCase): self.assertTrue(self.Mine.lookup("a_method").is_assigned()) self.assertFalse(self.internal.lookup("x").is_assigned()) + def test_annotated(self): + st1 = symtable.symtable('def f():\n x: int\n', 'test', 'exec') + st2 = st1.get_children()[0] + self.assertTrue(st2.lookup('x').is_local()) + self.assertTrue(st2.lookup('x').is_annotated()) + self.assertFalse(st2.lookup('x').is_global()) + st3 = symtable.symtable('def f():\n x = 1\n', 'test', 'exec') + st4 = st3.get_children()[0] + self.assertTrue(st4.lookup('x').is_local()) + self.assertFalse(st4.lookup('x').is_annotated()) + def test_imported(self): self.assertTrue(self.top.lookup("sys").is_imported()) diff --git a/Lib/test/test_tools/test_com2ann.py b/Lib/test/test_tools/test_com2ann.py new file mode 100644 index 0000000000..2731f82ce7 --- /dev/null +++ b/Lib/test/test_tools/test_com2ann.py @@ -0,0 +1,260 @@ +"""Tests for the com2ann.py script in the Tools/parser directory.""" + +import unittest +import test.support +import os +import re + +from test.test_tools import basepath, toolsdir, skip_if_missing + +skip_if_missing() + +parser_path = os.path.join(toolsdir, "parser") + +with test.support.DirsOnSysPath(parser_path): + from com2ann import * + +class BaseTestCase(unittest.TestCase): + + def check(self, code, expected, n=False, e=False): + self.assertEqual(com2ann(code, + drop_None=n, drop_Ellipsis=e, silent=True), + expected) + +class SimpleTestCase(BaseTestCase): + # Tests for basic conversions + + def test_basics(self): + self.check("z = 5", "z = 5") + self.check("z: int = 5", "z: int = 5") + self.check("z = 5 # type: int", "z: int = 5") + self.check("z = 5 # type: int # comment", + "z: int = 5 # comment") + + def test_type_ignore(self): + self.check("foobar = foobaz() #type: ignore", + "foobar = foobaz() #type: ignore") + self.check("a = 42 #type: ignore #comment", + "a = 42 #type: ignore #comment") + + def test_complete_tuple(self): + self.check("t = 1, 2, 3 # type: Tuple[int, ...]", + "t: Tuple[int, ...] = (1, 2, 3)") + self.check("t = 1, # type: Tuple[int]", + "t: Tuple[int] = (1,)") + self.check("t = (1, 2, 3) # type: Tuple[int, ...]", + "t: Tuple[int, ...] = (1, 2, 3)") + + def test_drop_None(self): + self.check("x = None # type: int", + "x: int", True) + self.check("x = None # type: int # another", + "x: int # another", True) + self.check("x = None # type: int # None", + "x: int # None", True) + + def test_drop_Ellipsis(self): + self.check("x = ... # type: int", + "x: int", False, True) + self.check("x = ... # type: int # another", + "x: int # another", False, True) + self.check("x = ... # type: int # ...", + "x: int # ...", False, True) + + def test_newline(self): + self.check("z = 5 # type: int\r\n", "z: int = 5\r\n") + self.check("z = 5 # type: int # comment\x85", + "z: int = 5 # comment\x85") + + def test_wrong(self): + self.check("#type : str", "#type : str") + self.check("x==y #type: bool", "x==y #type: bool") + + def test_pattern(self): + for line in ["#type: int", " # type: str[:] # com"]: + self.assertTrue(re.search(TYPE_COM, line)) + for line in ["", "#", "# comment", "#type", "type int:"]: + self.assertFalse(re.search(TYPE_COM, line)) + +class BigTestCase(BaseTestCase): + # Tests for really crazy formatting, to be sure + # that script works reasonably in extreme situations + + def test_crazy(self): + self.maxDiff = None + self.check(crazy_code, big_result, False, False) + self.check(crazy_code, big_result_ne, True, True) + +crazy_code = """\ +# -*- coding: utf-8 -*- # this should not be spoiled +''' +Docstring here +''' + +import testmod +x = 5 #type : int # this one is OK +ttt \\ + = \\ + 1.0, \\ + 2.0, \\ + 3.0, #type: Tuple[float, float, float] +with foo(x==1) as f: #type: str + print(f) + +for i, j in my_inter(x=1): # type: ignore + i + j # type: int # what about this + +x = y = z = 1 # type: int +x, y, z = [], [], [] # type: (List[int], List[int], List[str]) +class C: + + + l[f(x + =1)] = [ + + 1, + 2, + ] # type: List[int] + + + (C.x[1]) = \\ + 42 == 5# type: bool +lst[...] = \\ + ((\\ +...)) # type: int # comment .. + +y = ... # type: int # comment ... +z = ... +#type: int + + +#DONE placement of annotation after target rather than before = + +TD.x[1] \\ + = 0 == 5# type: bool + +TD.y[1] =5 == 5# type: bool # one more here +F[G(x == y, + +# hm... + + z)]\\ + = None # type: OMG[int] # comment: None +x = None#type:int #comment : None""" + +big_result = """\ +# -*- coding: utf-8 -*- # this should not be spoiled +''' +Docstring here +''' + +import testmod +x: int = 5 # this one is OK +ttt: Tuple[float, float, float] \\ + = \\ + (1.0, \\ + 2.0, \\ + 3.0,) +with foo(x==1) as f: #type: str + print(f) + +for i, j in my_inter(x=1): # type: ignore + i + j # type: int # what about this + +x = y = z = 1 # type: int +x, y, z = [], [], [] # type: (List[int], List[int], List[str]) +class C: + + + l[f(x + =1)]: List[int] = [ + + 1, + 2, + ] + + + (C.x[1]): bool = \\ + 42 == 5 +lst[...]: int = \\ + ((\\ +...)) # comment .. + +y: int = ... # comment ... +z = ... +#type: int + + +#DONE placement of annotation after target rather than before = + +TD.x[1]: bool \\ + = 0 == 5 + +TD.y[1]: bool =5 == 5 # one more here +F[G(x == y, + +# hm... + + z)]: OMG[int]\\ + = None # comment: None +x: int = None #comment : None""" + +big_result_ne = """\ +# -*- coding: utf-8 -*- # this should not be spoiled +''' +Docstring here +''' + +import testmod +x: int = 5 # this one is OK +ttt: Tuple[float, float, float] \\ + = \\ + (1.0, \\ + 2.0, \\ + 3.0,) +with foo(x==1) as f: #type: str + print(f) + +for i, j in my_inter(x=1): # type: ignore + i + j # type: int # what about this + +x = y = z = 1 # type: int +x, y, z = [], [], [] # type: (List[int], List[int], List[str]) +class C: + + + l[f(x + =1)]: List[int] = [ + + 1, + 2, + ] + + + (C.x[1]): bool = \\ + 42 == 5 +lst[...]: int \\ + \\ + # comment .. + +y: int # comment ... +z = ... +#type: int + + +#DONE placement of annotation after target rather than before = + +TD.x[1]: bool \\ + = 0 == 5 + +TD.y[1]: bool =5 == 5 # one more here +F[G(x == y, + +# hm... + + z)]: OMG[int]\\ + # comment: None +x: int #comment : None""" + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 72afe67097..e3904b1baa 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -4,14 +4,16 @@ import pickle import re import sys from unittest import TestCase, main, skipUnless, SkipTest +from collections import ChainMap +from test import ann_module, ann_module2, ann_module3 from typing import Any from typing import TypeVar, AnyStr from typing import T, KT, VT # Not in __all__. from typing import Union, Optional -from typing import Tuple +from typing import Tuple, List from typing import Callable -from typing import Generic +from typing import Generic, ClassVar from typing import cast from typing import get_type_hints from typing import no_type_check, no_type_check_decorator @@ -827,6 +829,43 @@ class GenericTests(BaseTestCase): with self.assertRaises(Exception): D[T] +class ClassVarTests(BaseTestCase): + + def test_basics(self): + with self.assertRaises(TypeError): + ClassVar[1] + with self.assertRaises(TypeError): + ClassVar[int, str] + with self.assertRaises(TypeError): + ClassVar[int][str] + + def test_repr(self): + self.assertEqual(repr(ClassVar), 'typing.ClassVar') + cv = ClassVar[int] + self.assertEqual(repr(cv), 'typing.ClassVar[int]') + cv = ClassVar[Employee] + self.assertEqual(repr(cv), 'typing.ClassVar[%s.Employee]' % __name__) + + def test_cannot_subclass(self): + with self.assertRaises(TypeError): + class C(type(ClassVar)): + pass + with self.assertRaises(TypeError): + class C(type(ClassVar[int])): + pass + + def test_cannot_init(self): + with self.assertRaises(TypeError): + type(ClassVar)() + with self.assertRaises(TypeError): + type(ClassVar[Optional[int]])() + + def test_no_isinstance(self): + with self.assertRaises(TypeError): + isinstance(1, ClassVar[int]) + with self.assertRaises(TypeError): + issubclass(int, ClassVar) + class VarianceTests(BaseTestCase): @@ -930,6 +969,46 @@ class ForwardRefTests(BaseTestCase): right_hints = get_type_hints(t.add_right, globals(), locals()) self.assertEqual(right_hints['node'], Optional[Node[T]]) + def test_get_type_hints(self): + gth = get_type_hints + self.assertEqual(gth(ann_module), {'x': int, 'y': str}) + self.assertEqual(gth(ann_module.C, ann_module.__dict__), + ChainMap({'y': Optional[ann_module.C]}, {})) + self.assertEqual(gth(ann_module2), {}) + self.assertEqual(gth(ann_module3), {}) + self.assertEqual(repr(gth(ann_module.j_class)), 'ChainMap({}, {})') + self.assertEqual(gth(ann_module.M), ChainMap({'123': 123, 'o': type}, + {}, {})) + self.assertEqual(gth(ann_module.D), + ChainMap({'j': str, 'k': str, + 'y': Optional[ann_module.C]}, {})) + self.assertEqual(gth(ann_module.Y), ChainMap({'z': int}, {})) + self.assertEqual(gth(ann_module.h_class), + ChainMap({}, {'y': Optional[ann_module.C]}, {})) + self.assertEqual(gth(ann_module.S), ChainMap({'x': str, 'y': str}, + {})) + self.assertEqual(gth(ann_module.foo), {'x': int}) + + def testf(x, y): ... + testf.__annotations__['x'] = 'int' + self.assertEqual(gth(testf), {'x': int}) + self.assertEqual(gth(ann_module2.NTC.meth), {}) + + # interactions with ClassVar + class B: + x: ClassVar[Optional['B']] = None + y: int + class C(B): + z: ClassVar['C'] = B() + class G(Generic[T]): + lst: ClassVar[List[T]] = [] + self.assertEqual(gth(B, locals()), + ChainMap({'y': int, 'x': ClassVar[Optional[B]]}, {})) + self.assertEqual(gth(C, locals()), + ChainMap({'z': ClassVar[C]}, + {'y': int, 'x': ClassVar[Optional[B]]}, {})) + self.assertEqual(gth(G), ChainMap({'lst': ClassVar[List[T]]},{},{})) + def test_forwardref_instance_type_error(self): fr = typing._ForwardRef('int') with self.assertRaises(TypeError): diff --git a/Lib/typing.py b/Lib/typing.py index 5573a1fbf9..6ce74fc9f1 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -6,6 +6,7 @@ import functools import re as stdlib_re # Avoid confusion with the re we export. import sys import types + try: import collections.abc as collections_abc except ImportError: @@ -17,6 +18,7 @@ __all__ = [ # Super-special typing primitives. 'Any', 'Callable', + 'ClassVar', 'Generic', 'Optional', 'Tuple', @@ -270,7 +272,7 @@ class _TypeAlias: def _get_type_vars(types, tvars): for t in types: - if isinstance(t, TypingMeta): + if isinstance(t, TypingMeta) or isinstance(t, _ClassVar): t._get_type_vars(tvars) @@ -281,7 +283,7 @@ def _type_vars(types): def _eval_type(t, globalns, localns): - if isinstance(t, TypingMeta): + if isinstance(t, TypingMeta) or isinstance(t, _ClassVar): return t._eval_type(globalns, localns) else: return t @@ -1114,6 +1116,67 @@ class Generic(metaclass=GenericMeta): return obj +class _ClassVar(metaclass=TypingMeta, _root=True): + """Special type construct to mark class variables. + + An annotation wrapped in ClassVar indicates that a given + attribute is intended to be used as a class variable and + should not be set on instances of that class. Usage:: + + class Starship: + stats: ClassVar[Dict[str, int]] = {} # class variable + damage: int = 10 # instance variable + + ClassVar accepts only types and cannot be further subscribed. + + Note that ClassVar is not a class itself, and should not + be used with isinstance() or issubclass(). + """ + + def __init__(self, tp=None, _root=False): + cls = type(self) + if _root: + self.__type__ = tp + else: + raise TypeError('Cannot initialize {}'.format(cls.__name__[1:])) + + def __getitem__(self, item): + cls = type(self) + if self.__type__ is None: + return cls(_type_check(item, + '{} accepts only types.'.format(cls.__name__[1:])), + _root=True) + raise TypeError('{} cannot be further subscripted' + .format(cls.__name__[1:])) + + def _eval_type(self, globalns, localns): + return type(self)(_eval_type(self.__type__, globalns, localns), + _root=True) + + def _get_type_vars(self, tvars): + if self.__type__: + _get_type_vars(self.__type__, tvars) + + def __repr__(self): + cls = type(self) + if not self.__type__: + return '{}.{}'.format(cls.__module__, cls.__name__[1:]) + return '{}.{}[{}]'.format(cls.__module__, cls.__name__[1:], + _type_repr(self.__type__)) + + def __hash__(self): + return hash((type(self).__name__, self.__type__)) + + def __eq__(self, other): + if not isinstance(other, _ClassVar): + return NotImplemented + if self.__type__ is not None: + return self.__type__ == other.__type__ + return self is other + +ClassVar = _ClassVar(_root=True) + + def cast(typ, val): """Cast a value to a type. @@ -1142,12 +1205,20 @@ def _get_defaults(func): def get_type_hints(obj, globalns=None, localns=None): - """Return type hints for a function or method object. + """Return type hints for an object. This is often the same as obj.__annotations__, but it handles forward references encoded as string literals, and if necessary adds Optional[t] if a default value equal to None is set. + The argument may be a module, class, method, or function. The annotations + are returned as a dictionary, or in the case of a class, a ChainMap of + dictionaries. + + TypeError is raised if the argument is not of a type that can contain + annotations, and an empty dictionary is returned if no annotations are + present. + BEWARE -- the behavior of globalns and localns is counterintuitive (unless you are familiar with how eval() and exec() work). The search order is locals first, then globals. @@ -1162,6 +1233,7 @@ def get_type_hints(obj, globalns=None, localns=None): - If two dict arguments are passed, they specify globals and locals, respectively. """ + if getattr(obj, '__no_type_check__', None): return {} if globalns is None: @@ -1170,16 +1242,62 @@ def get_type_hints(obj, globalns=None, localns=None): localns = globalns elif localns is None: localns = globalns - defaults = _get_defaults(obj) - hints = dict(obj.__annotations__) - for name, value in hints.items(): - if isinstance(value, str): - value = _ForwardRef(value) - value = _eval_type(value, globalns, localns) - if name in defaults and defaults[name] is None: - value = Optional[value] - hints[name] = value - return hints + + if (isinstance(obj, types.FunctionType) or + isinstance(obj, types.BuiltinFunctionType) or + isinstance(obj, types.MethodType)): + defaults = _get_defaults(obj) + hints = obj.__annotations__ + for name, value in hints.items(): + if value is None: + value = type(None) + if isinstance(value, str): + value = _ForwardRef(value) + value = _eval_type(value, globalns, localns) + if name in defaults and defaults[name] is None: + value = Optional[value] + hints[name] = value + return hints + + if isinstance(obj, types.ModuleType): + try: + hints = obj.__annotations__ + except AttributeError: + return {} + # we keep only those annotations that can be accessed on module + members = obj.__dict__ + hints = {name: value for name, value in hints.items() + if name in members} + for name, value in hints.items(): + if value is None: + value = type(None) + if isinstance(value, str): + value = _ForwardRef(value) + value = _eval_type(value, globalns, localns) + hints[name] = value + return hints + + if isinstance(object, type): + cmap = None + for base in reversed(obj.__mro__): + new_map = collections.ChainMap if cmap is None else cmap.new_child + try: + hints = base.__dict__['__annotations__'] + except KeyError: + cmap = new_map() + else: + for name, value in hints.items(): + if value is None: + value = type(None) + if isinstance(value, str): + value = _ForwardRef(value) + value = _eval_type(value, globalns, localns) + hints[name] = value + cmap = new_map(hints) + return cmap + + raise TypeError('{!r} is not a module, class, method, ' + 'or function.'.format(obj)) def no_type_check(arg): @@ -1300,6 +1418,8 @@ class _ProtocolMeta(GenericMeta): else: if (not attr.startswith('_abc_') and attr != '__abstractmethods__' and + attr != '__annotations__' and + attr != '__weakref__' and attr != '_is_protocol' and attr != '__dict__' and attr != '__args__' and -- cgit v1.2.1 From a360bf1bc1331830525d228d1cba50162bb78c79 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 8 Sep 2016 21:46:56 -0700 Subject: regrtest: log FS and locale encodings --- Lib/test/libregrtest/main.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index dbbf72f1b5..f0effc973c 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -1,5 +1,6 @@ import datetime import faulthandler +import locale import os import platform import random @@ -392,7 +393,10 @@ class Regrtest: "%s-endian" % sys.byteorder) print("== ", "hash algorithm:", sys.hash_info.algorithm, "64bit" if sys.maxsize > 2**32 else "32bit") - print("== ", os.getcwd()) + print("== cwd:", os.getcwd()) + print("== encodings: locale=%s, FS=%s" + % (locale.getpreferredencoding(False), + sys.getfilesystemencoding())) print("Testing with flags:", sys.flags) if self.ns.randomize: -- cgit v1.2.1 From 17668cfa5e0e6f50376cc233d9b063b908a19845 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Thu, 8 Sep 2016 22:01:51 -0700 Subject: Issue #28003: Implement PEP 525 -- Asynchronous Generators. --- Lib/asyncio/base_events.py | 57 ++- Lib/asyncio/coroutines.py | 5 +- Lib/asyncio/events.py | 4 + Lib/dis.py | 1 + Lib/inspect.py | 7 + Lib/test/badsyntax_async6.py | 2 - Lib/test/test_asyncgen.py | 823 +++++++++++++++++++++++++++++++++++++++++++ Lib/test/test_coroutines.py | 6 - Lib/test/test_dis.py | 2 +- Lib/test/test_inspect.py | 12 +- Lib/test/test_sys.py | 26 ++ Lib/types.py | 5 + 12 files changed, 937 insertions(+), 13 deletions(-) delete mode 100644 Lib/test/badsyntax_async6.py create mode 100644 Lib/test/test_asyncgen.py (limited to 'Lib') diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index 918b869526..b420586a13 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -13,7 +13,6 @@ conscious design decision, leaving the door open for keyword arguments to modify the meaning of the API call itself. """ - import collections import concurrent.futures import heapq @@ -28,6 +27,7 @@ import time import traceback import sys import warnings +import weakref from . import compat from . import coroutines @@ -242,6 +242,13 @@ class BaseEventLoop(events.AbstractEventLoop): self._task_factory = None self._coroutine_wrapper_set = False + # A weak set of all asynchronous generators that are being iterated + # by the loop. + self._asyncgens = weakref.WeakSet() + + # Set to True when `loop.shutdown_asyncgens` is called. + self._asyncgens_shutdown_called = False + def __repr__(self): return ('<%s running=%s closed=%s debug=%s>' % (self.__class__.__name__, self.is_running(), @@ -333,6 +340,46 @@ class BaseEventLoop(events.AbstractEventLoop): if self._closed: raise RuntimeError('Event loop is closed') + def _asyncgen_finalizer_hook(self, agen): + self._asyncgens.discard(agen) + if not self.is_closed(): + self.create_task(agen.aclose()) + + def _asyncgen_firstiter_hook(self, agen): + if self._asyncgens_shutdown_called: + warnings.warn( + "asynchronous generator {!r} was scheduled after " + "loop.shutdown_asyncgens() call".format(agen), + ResourceWarning, source=self) + + self._asyncgens.add(agen) + + @coroutine + def shutdown_asyncgens(self): + """Shutdown all active asynchronous generators.""" + self._asyncgens_shutdown_called = True + + if not len(self._asyncgens): + return + + closing_agens = list(self._asyncgens) + self._asyncgens.clear() + + shutdown_coro = tasks.gather( + *[ag.aclose() for ag in closing_agens], + return_exceptions=True, + loop=self) + + results = yield from shutdown_coro + for result, agen in zip(results, closing_agens): + if isinstance(result, Exception): + self.call_exception_handler({ + 'message': 'an error occurred during closing of ' + 'asynchronous generator {!r}'.format(agen), + 'exception': result, + 'asyncgen': agen + }) + def run_forever(self): """Run until stop() is called.""" self._check_closed() @@ -340,6 +387,9 @@ class BaseEventLoop(events.AbstractEventLoop): raise RuntimeError('Event loop is running.') self._set_coroutine_wrapper(self._debug) self._thread_id = threading.get_ident() + old_agen_hooks = sys.get_asyncgen_hooks() + sys.set_asyncgen_hooks(firstiter=self._asyncgen_firstiter_hook, + finalizer=self._asyncgen_finalizer_hook) try: while True: self._run_once() @@ -349,6 +399,7 @@ class BaseEventLoop(events.AbstractEventLoop): self._stopping = False self._thread_id = None self._set_coroutine_wrapper(False) + sys.set_asyncgen_hooks(*old_agen_hooks) def run_until_complete(self, future): """Run until the Future is done. @@ -1179,7 +1230,9 @@ class BaseEventLoop(events.AbstractEventLoop): - 'handle' (optional): Handle instance; - 'protocol' (optional): Protocol instance; - 'transport' (optional): Transport instance; - - 'socket' (optional): Socket instance. + - 'socket' (optional): Socket instance; + - 'asyncgen' (optional): Asynchronous generator that caused + the exception. New keys maybe introduced in the future. diff --git a/Lib/asyncio/coroutines.py b/Lib/asyncio/coroutines.py index 71bc6fb2ea..9c338b0c32 100644 --- a/Lib/asyncio/coroutines.py +++ b/Lib/asyncio/coroutines.py @@ -276,7 +276,10 @@ def _format_coroutine(coro): try: coro_code = coro.gi_code except AttributeError: - coro_code = coro.cr_code + try: + coro_code = coro.cr_code + except AttributeError: + return repr(coro) try: coro_frame = coro.gi_frame diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py index c48c5bed73..cc9a986b99 100644 --- a/Lib/asyncio/events.py +++ b/Lib/asyncio/events.py @@ -248,6 +248,10 @@ class AbstractEventLoop: """ raise NotImplementedError + def shutdown_asyncgens(self): + """Shutdown all active asynchronous generators.""" + raise NotImplementedError + # Methods scheduling callbacks. All these return Handles. def _timer_handle_cancelled(self, handle): diff --git a/Lib/dis.py b/Lib/dis.py index 59886f1e37..556d84eb4a 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -87,6 +87,7 @@ COMPILER_FLAG_NAMES = { 64: "NOFREE", 128: "COROUTINE", 256: "ITERABLE_COROUTINE", + 512: "ASYNC_GENERATOR", } def pretty_flags(flags): diff --git a/Lib/inspect.py b/Lib/inspect.py index 72c1691b38..2380095e76 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -185,6 +185,13 @@ def iscoroutinefunction(object): return bool((isfunction(object) or ismethod(object)) and object.__code__.co_flags & CO_COROUTINE) +def isasyncgenfunction(object): + return bool((isfunction(object) or ismethod(object)) and + object.__code__.co_flags & CO_ASYNC_GENERATOR) + +def isasyncgen(object): + return isinstance(object, types.AsyncGeneratorType) + def isgenerator(object): """Return true if the object is a generator. diff --git a/Lib/test/badsyntax_async6.py b/Lib/test/badsyntax_async6.py deleted file mode 100644 index cb0a23d5f0..0000000000 --- a/Lib/test/badsyntax_async6.py +++ /dev/null @@ -1,2 +0,0 @@ -async def foo(): - yield diff --git a/Lib/test/test_asyncgen.py b/Lib/test/test_asyncgen.py new file mode 100644 index 0000000000..41b1b4f4b4 --- /dev/null +++ b/Lib/test/test_asyncgen.py @@ -0,0 +1,823 @@ +import asyncio +import inspect +import sys +import types +import unittest + +from unittest import mock + + +class AwaitException(Exception): + pass + + +@types.coroutine +def awaitable(*, throw=False): + if throw: + yield ('throw',) + else: + yield ('result',) + + +def run_until_complete(coro): + exc = False + while True: + try: + if exc: + exc = False + fut = coro.throw(AwaitException) + else: + fut = coro.send(None) + except StopIteration as ex: + return ex.args[0] + + if fut == ('throw',): + exc = True + + +def to_list(gen): + async def iterate(): + res = [] + async for i in gen: + res.append(i) + return res + + return run_until_complete(iterate()) + + +class AsyncGenSyntaxTest(unittest.TestCase): + + def test_async_gen_syntax_01(self): + code = '''async def foo(): + await abc + yield from 123 + ''' + + with self.assertRaisesRegex(SyntaxError, 'yield from.*inside async'): + exec(code, {}, {}) + + def test_async_gen_syntax_02(self): + code = '''async def foo(): + yield from 123 + ''' + + with self.assertRaisesRegex(SyntaxError, 'yield from.*inside async'): + exec(code, {}, {}) + + def test_async_gen_syntax_03(self): + code = '''async def foo(): + await abc + yield + return 123 + ''' + + with self.assertRaisesRegex(SyntaxError, 'return.*value.*async gen'): + exec(code, {}, {}) + + def test_async_gen_syntax_04(self): + code = '''async def foo(): + yield + return 123 + ''' + + with self.assertRaisesRegex(SyntaxError, 'return.*value.*async gen'): + exec(code, {}, {}) + + def test_async_gen_syntax_05(self): + code = '''async def foo(): + if 0: + yield + return 12 + ''' + + with self.assertRaisesRegex(SyntaxError, 'return.*value.*async gen'): + exec(code, {}, {}) + + +class AsyncGenTest(unittest.TestCase): + + def compare_generators(self, sync_gen, async_gen): + def sync_iterate(g): + res = [] + while True: + try: + res.append(g.__next__()) + except StopIteration: + res.append('STOP') + break + except Exception as ex: + res.append(str(type(ex))) + return res + + def async_iterate(g): + res = [] + while True: + try: + g.__anext__().__next__() + except StopAsyncIteration: + res.append('STOP') + break + except StopIteration as ex: + if ex.args: + res.append(ex.args[0]) + else: + res.append('EMPTY StopIteration') + break + except Exception as ex: + res.append(str(type(ex))) + return res + + sync_gen_result = sync_iterate(sync_gen) + async_gen_result = async_iterate(async_gen) + self.assertEqual(sync_gen_result, async_gen_result) + return async_gen_result + + def test_async_gen_iteration_01(self): + async def gen(): + await awaitable() + a = yield 123 + self.assertIs(a, None) + await awaitable() + yield 456 + await awaitable() + yield 789 + + self.assertEqual(to_list(gen()), [123, 456, 789]) + + def test_async_gen_iteration_02(self): + async def gen(): + await awaitable() + yield 123 + await awaitable() + + g = gen() + ai = g.__aiter__() + self.assertEqual(ai.__anext__().__next__(), ('result',)) + + try: + ai.__anext__().__next__() + except StopIteration as ex: + self.assertEqual(ex.args[0], 123) + else: + self.fail('StopIteration was not raised') + + self.assertEqual(ai.__anext__().__next__(), ('result',)) + + try: + ai.__anext__().__next__() + except StopAsyncIteration as ex: + self.assertFalse(ex.args) + else: + self.fail('StopAsyncIteration was not raised') + + def test_async_gen_exception_03(self): + async def gen(): + await awaitable() + yield 123 + await awaitable(throw=True) + yield 456 + + with self.assertRaises(AwaitException): + to_list(gen()) + + def test_async_gen_exception_04(self): + async def gen(): + await awaitable() + yield 123 + 1 / 0 + + g = gen() + ai = g.__aiter__() + self.assertEqual(ai.__anext__().__next__(), ('result',)) + + try: + ai.__anext__().__next__() + except StopIteration as ex: + self.assertEqual(ex.args[0], 123) + else: + self.fail('StopIteration was not raised') + + with self.assertRaises(ZeroDivisionError): + ai.__anext__().__next__() + + def test_async_gen_exception_05(self): + async def gen(): + yield 123 + raise StopAsyncIteration + + with self.assertRaisesRegex(RuntimeError, + 'async generator.*StopAsyncIteration'): + to_list(gen()) + + def test_async_gen_exception_06(self): + async def gen(): + yield 123 + raise StopIteration + + with self.assertRaisesRegex(RuntimeError, + 'async generator.*StopIteration'): + to_list(gen()) + + def test_async_gen_exception_07(self): + def sync_gen(): + try: + yield 1 + 1 / 0 + finally: + yield 2 + yield 3 + + yield 100 + + async def async_gen(): + try: + yield 1 + 1 / 0 + finally: + yield 2 + yield 3 + + yield 100 + + self.compare_generators(sync_gen(), async_gen()) + + def test_async_gen_exception_08(self): + def sync_gen(): + try: + yield 1 + finally: + yield 2 + 1 / 0 + yield 3 + + yield 100 + + async def async_gen(): + try: + yield 1 + await awaitable() + finally: + await awaitable() + yield 2 + 1 / 0 + yield 3 + + yield 100 + + self.compare_generators(sync_gen(), async_gen()) + + def test_async_gen_exception_09(self): + def sync_gen(): + try: + yield 1 + 1 / 0 + finally: + yield 2 + yield 3 + + yield 100 + + async def async_gen(): + try: + await awaitable() + yield 1 + 1 / 0 + finally: + yield 2 + await awaitable() + yield 3 + + yield 100 + + self.compare_generators(sync_gen(), async_gen()) + + def test_async_gen_exception_10(self): + async def gen(): + yield 123 + with self.assertRaisesRegex(TypeError, + "non-None value .* async generator"): + gen().__anext__().send(100) + + def test_async_gen_api_01(self): + async def gen(): + yield 123 + + g = gen() + + self.assertEqual(g.__name__, 'gen') + g.__name__ = '123' + self.assertEqual(g.__name__, '123') + + self.assertIn('.gen', g.__qualname__) + g.__qualname__ = '123' + self.assertEqual(g.__qualname__, '123') + + self.assertIsNone(g.ag_await) + self.assertIsInstance(g.ag_frame, types.FrameType) + self.assertFalse(g.ag_running) + self.assertIsInstance(g.ag_code, types.CodeType) + + self.assertTrue(inspect.isawaitable(g.aclose())) + + +class AsyncGenAsyncioTest(unittest.TestCase): + + def setUp(self): + self.loop = asyncio.new_event_loop() + asyncio.set_event_loop(None) + + def tearDown(self): + self.loop.close() + self.loop = None + + async def to_list(self, gen): + res = [] + async for i in gen: + res.append(i) + return res + + def test_async_gen_asyncio_01(self): + async def gen(): + yield 1 + await asyncio.sleep(0.01, loop=self.loop) + yield 2 + await asyncio.sleep(0.01, loop=self.loop) + return + yield 3 + + res = self.loop.run_until_complete(self.to_list(gen())) + self.assertEqual(res, [1, 2]) + + def test_async_gen_asyncio_02(self): + async def gen(): + yield 1 + await asyncio.sleep(0.01, loop=self.loop) + yield 2 + 1 / 0 + yield 3 + + with self.assertRaises(ZeroDivisionError): + self.loop.run_until_complete(self.to_list(gen())) + + def test_async_gen_asyncio_03(self): + loop = self.loop + + class Gen: + async def __aiter__(self): + yield 1 + await asyncio.sleep(0.01, loop=loop) + yield 2 + + res = loop.run_until_complete(self.to_list(Gen())) + self.assertEqual(res, [1, 2]) + + def test_async_gen_asyncio_anext_04(self): + async def foo(): + yield 1 + await asyncio.sleep(0.01, loop=self.loop) + try: + yield 2 + yield 3 + except ZeroDivisionError: + yield 1000 + await asyncio.sleep(0.01, loop=self.loop) + yield 4 + + async def run1(): + it = foo().__aiter__() + + self.assertEqual(await it.__anext__(), 1) + self.assertEqual(await it.__anext__(), 2) + self.assertEqual(await it.__anext__(), 3) + self.assertEqual(await it.__anext__(), 4) + with self.assertRaises(StopAsyncIteration): + await it.__anext__() + with self.assertRaises(StopAsyncIteration): + await it.__anext__() + + async def run2(): + it = foo().__aiter__() + + self.assertEqual(await it.__anext__(), 1) + self.assertEqual(await it.__anext__(), 2) + try: + it.__anext__().throw(ZeroDivisionError) + except StopIteration as ex: + self.assertEqual(ex.args[0], 1000) + else: + self.fail('StopIteration was not raised') + self.assertEqual(await it.__anext__(), 4) + with self.assertRaises(StopAsyncIteration): + await it.__anext__() + + self.loop.run_until_complete(run1()) + self.loop.run_until_complete(run2()) + + def test_async_gen_asyncio_anext_05(self): + async def foo(): + v = yield 1 + v = yield v + yield v * 100 + + async def run(): + it = foo().__aiter__() + + try: + it.__anext__().send(None) + except StopIteration as ex: + self.assertEqual(ex.args[0], 1) + else: + self.fail('StopIteration was not raised') + + try: + it.__anext__().send(10) + except StopIteration as ex: + self.assertEqual(ex.args[0], 10) + else: + self.fail('StopIteration was not raised') + + try: + it.__anext__().send(12) + except StopIteration as ex: + self.assertEqual(ex.args[0], 1200) + else: + self.fail('StopIteration was not raised') + + with self.assertRaises(StopAsyncIteration): + await it.__anext__() + + self.loop.run_until_complete(run()) + + def test_async_gen_asyncio_aclose_06(self): + async def foo(): + try: + yield 1 + 1 / 0 + finally: + await asyncio.sleep(0.01, loop=self.loop) + yield 12 + + async def run(): + gen = foo() + it = gen.__aiter__() + await it.__anext__() + await gen.aclose() + + with self.assertRaisesRegex( + RuntimeError, + "async generator ignored GeneratorExit"): + self.loop.run_until_complete(run()) + + def test_async_gen_asyncio_aclose_07(self): + DONE = 0 + + async def foo(): + nonlocal DONE + try: + yield 1 + 1 / 0 + finally: + await asyncio.sleep(0.01, loop=self.loop) + await asyncio.sleep(0.01, loop=self.loop) + DONE += 1 + DONE += 1000 + + async def run(): + gen = foo() + it = gen.__aiter__() + await it.__anext__() + await gen.aclose() + + self.loop.run_until_complete(run()) + self.assertEqual(DONE, 1) + + def test_async_gen_asyncio_aclose_08(self): + DONE = 0 + + fut = asyncio.Future(loop=self.loop) + + async def foo(): + nonlocal DONE + try: + yield 1 + await fut + DONE += 1000 + yield 2 + finally: + await asyncio.sleep(0.01, loop=self.loop) + await asyncio.sleep(0.01, loop=self.loop) + DONE += 1 + DONE += 1000 + + async def run(): + gen = foo() + it = gen.__aiter__() + self.assertEqual(await it.__anext__(), 1) + t = self.loop.create_task(it.__anext__()) + await asyncio.sleep(0.01, loop=self.loop) + await gen.aclose() + return t + + t = self.loop.run_until_complete(run()) + self.assertEqual(DONE, 1) + + # Silence ResourceWarnings + fut.cancel() + t.cancel() + self.loop.run_until_complete(asyncio.sleep(0.01, loop=self.loop)) + + def test_async_gen_asyncio_gc_aclose_09(self): + DONE = 0 + + async def gen(): + nonlocal DONE + try: + while True: + yield 1 + finally: + await asyncio.sleep(0.01, loop=self.loop) + await asyncio.sleep(0.01, loop=self.loop) + DONE = 1 + + async def run(): + g = gen() + await g.__anext__() + await g.__anext__() + del g + + await asyncio.sleep(0.1, loop=self.loop) + + self.loop.run_until_complete(run()) + self.assertEqual(DONE, 1) + + def test_async_gen_asyncio_asend_01(self): + DONE = 0 + + # Sanity check: + def sgen(): + v = yield 1 + yield v * 2 + sg = sgen() + v = sg.send(None) + self.assertEqual(v, 1) + v = sg.send(100) + self.assertEqual(v, 200) + + async def gen(): + nonlocal DONE + try: + await asyncio.sleep(0.01, loop=self.loop) + v = yield 1 + await asyncio.sleep(0.01, loop=self.loop) + yield v * 2 + await asyncio.sleep(0.01, loop=self.loop) + return + finally: + await asyncio.sleep(0.01, loop=self.loop) + await asyncio.sleep(0.01, loop=self.loop) + DONE = 1 + + async def run(): + g = gen() + + v = await g.asend(None) + self.assertEqual(v, 1) + + v = await g.asend(100) + self.assertEqual(v, 200) + + with self.assertRaises(StopAsyncIteration): + await g.asend(None) + + self.loop.run_until_complete(run()) + self.assertEqual(DONE, 1) + + def test_async_gen_asyncio_asend_02(self): + DONE = 0 + + async def sleep_n_crash(delay): + await asyncio.sleep(delay, loop=self.loop) + 1 / 0 + + async def gen(): + nonlocal DONE + try: + await asyncio.sleep(0.01, loop=self.loop) + v = yield 1 + await sleep_n_crash(0.01) + DONE += 1000 + yield v * 2 + finally: + await asyncio.sleep(0.01, loop=self.loop) + await asyncio.sleep(0.01, loop=self.loop) + DONE = 1 + + async def run(): + g = gen() + + v = await g.asend(None) + self.assertEqual(v, 1) + + await g.asend(100) + + with self.assertRaises(ZeroDivisionError): + self.loop.run_until_complete(run()) + self.assertEqual(DONE, 1) + + def test_async_gen_asyncio_asend_03(self): + DONE = 0 + + async def sleep_n_crash(delay): + fut = asyncio.ensure_future(asyncio.sleep(delay, loop=self.loop), + loop=self.loop) + self.loop.call_later(delay / 2, lambda: fut.cancel()) + return await fut + + async def gen(): + nonlocal DONE + try: + await asyncio.sleep(0.01, loop=self.loop) + v = yield 1 + await sleep_n_crash(0.01) + DONE += 1000 + yield v * 2 + finally: + await asyncio.sleep(0.01, loop=self.loop) + await asyncio.sleep(0.01, loop=self.loop) + DONE = 1 + + async def run(): + g = gen() + + v = await g.asend(None) + self.assertEqual(v, 1) + + await g.asend(100) + + with self.assertRaises(asyncio.CancelledError): + self.loop.run_until_complete(run()) + self.assertEqual(DONE, 1) + + def test_async_gen_asyncio_athrow_01(self): + DONE = 0 + + class FooEr(Exception): + pass + + # Sanity check: + def sgen(): + try: + v = yield 1 + except FooEr: + v = 1000 + yield v * 2 + sg = sgen() + v = sg.send(None) + self.assertEqual(v, 1) + v = sg.throw(FooEr) + self.assertEqual(v, 2000) + with self.assertRaises(StopIteration): + sg.send(None) + + async def gen(): + nonlocal DONE + try: + await asyncio.sleep(0.01, loop=self.loop) + try: + v = yield 1 + except FooEr: + v = 1000 + await asyncio.sleep(0.01, loop=self.loop) + yield v * 2 + await asyncio.sleep(0.01, loop=self.loop) + # return + finally: + await asyncio.sleep(0.01, loop=self.loop) + await asyncio.sleep(0.01, loop=self.loop) + DONE = 1 + + async def run(): + g = gen() + + v = await g.asend(None) + self.assertEqual(v, 1) + + v = await g.athrow(FooEr) + self.assertEqual(v, 2000) + + with self.assertRaises(StopAsyncIteration): + await g.asend(None) + + self.loop.run_until_complete(run()) + self.assertEqual(DONE, 1) + + def test_async_gen_asyncio_athrow_02(self): + DONE = 0 + + class FooEr(Exception): + pass + + async def sleep_n_crash(delay): + fut = asyncio.ensure_future(asyncio.sleep(delay, loop=self.loop), + loop=self.loop) + self.loop.call_later(delay / 2, lambda: fut.cancel()) + return await fut + + async def gen(): + nonlocal DONE + try: + await asyncio.sleep(0.01, loop=self.loop) + try: + v = yield 1 + except FooEr: + await sleep_n_crash(0.01) + yield v * 2 + await asyncio.sleep(0.01, loop=self.loop) + # return + finally: + await asyncio.sleep(0.01, loop=self.loop) + await asyncio.sleep(0.01, loop=self.loop) + DONE = 1 + + async def run(): + g = gen() + + v = await g.asend(None) + self.assertEqual(v, 1) + + try: + await g.athrow(FooEr) + except asyncio.CancelledError: + self.assertEqual(DONE, 1) + raise + else: + self.fail('CancelledError was not raised') + + with self.assertRaises(asyncio.CancelledError): + self.loop.run_until_complete(run()) + self.assertEqual(DONE, 1) + + def test_async_gen_asyncio_shutdown_01(self): + finalized = 0 + + async def waiter(timeout): + nonlocal finalized + try: + await asyncio.sleep(timeout, loop=self.loop) + yield 1 + finally: + await asyncio.sleep(0, loop=self.loop) + finalized += 1 + + async def wait(): + async for _ in waiter(1): + pass + + t1 = self.loop.create_task(wait()) + t2 = self.loop.create_task(wait()) + + self.loop.run_until_complete(asyncio.sleep(0.1, loop=self.loop)) + + self.loop.run_until_complete(self.loop.shutdown_asyncgens()) + self.assertEqual(finalized, 2) + + # Silence warnings + t1.cancel() + t2.cancel() + self.loop.run_until_complete(asyncio.sleep(0.1, loop=self.loop)) + + def test_async_gen_asyncio_shutdown_02(self): + logged = 0 + + def logger(loop, context): + nonlocal logged + self.assertIn('asyncgen', context) + expected = 'an error occurred during closing of asynchronous' + if expected in context['message']: + logged += 1 + + async def waiter(timeout): + try: + await asyncio.sleep(timeout, loop=self.loop) + yield 1 + finally: + 1 / 0 + + async def wait(): + async for _ in waiter(1): + pass + + t = self.loop.create_task(wait()) + self.loop.run_until_complete(asyncio.sleep(0.1, loop=self.loop)) + + self.loop.set_exception_handler(logger) + self.loop.run_until_complete(self.loop.shutdown_asyncgens()) + + self.assertEqual(logged, 1) + + # Silence warnings + t.cancel() + self.loop.run_until_complete(asyncio.sleep(0.1, loop=self.loop)) + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py index ee2482d2a3..fee9ae3b71 100644 --- a/Lib/test/test_coroutines.py +++ b/Lib/test/test_coroutines.py @@ -88,12 +88,6 @@ class AsyncBadSyntaxTest(unittest.TestCase): with self.assertRaisesRegex(SyntaxError, 'invalid syntax'): import test.badsyntax_async5 - def test_badsyntax_6(self): - with self.assertRaisesRegex( - SyntaxError, "'yield' inside async function"): - - import test.badsyntax_async6 - def test_badsyntax_7(self): with self.assertRaisesRegex( SyntaxError, "'yield from' inside async function"): diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 60810732c5..21b8cb7935 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -574,7 +574,7 @@ Argument count: 0 Kw-only arguments: 0 Number of locals: 2 Stack size: 17 -Flags: OPTIMIZED, NEWLOCALS, GENERATOR, NOFREE, COROUTINE +Flags: OPTIMIZED, NEWLOCALS, NOFREE, COROUTINE Constants: 0: None 1: 1""" diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index 47244aeaa4..97634e591c 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -65,7 +65,8 @@ class IsTestBase(unittest.TestCase): inspect.isframe, inspect.isfunction, inspect.ismethod, inspect.ismodule, inspect.istraceback, inspect.isgenerator, inspect.isgeneratorfunction, - inspect.iscoroutine, inspect.iscoroutinefunction]) + inspect.iscoroutine, inspect.iscoroutinefunction, + inspect.isasyncgen, inspect.isasyncgenfunction]) def istest(self, predicate, exp): obj = eval(exp) @@ -73,6 +74,7 @@ class IsTestBase(unittest.TestCase): for other in self.predicates - set([predicate]): if (predicate == inspect.isgeneratorfunction or \ + predicate == inspect.isasyncgenfunction or \ predicate == inspect.iscoroutinefunction) and \ other == inspect.isfunction: continue @@ -82,6 +84,10 @@ def generator_function_example(self): for i in range(2): yield i +async def async_generator_function_example(self): + async for i in range(2): + yield i + async def coroutine_function_example(self): return 'spam' @@ -122,6 +128,10 @@ class TestPredicates(IsTestBase): self.istest(inspect.isdatadescriptor, 'collections.defaultdict.default_factory') self.istest(inspect.isgenerator, '(x for x in range(2))') self.istest(inspect.isgeneratorfunction, 'generator_function_example') + self.istest(inspect.isasyncgen, + 'async_generator_function_example(1)') + self.istest(inspect.isasyncgenfunction, + 'async_generator_function_example') with warnings.catch_warnings(): warnings.simplefilter("ignore") diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index a6cd95ed49..37ee0b0324 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1192,6 +1192,32 @@ class SizeofTest(unittest.TestCase): # sys.flags check(sys.flags, vsize('') + self.P * len(sys.flags)) + def test_asyncgen_hooks(self): + old = sys.get_asyncgen_hooks() + self.assertIsNone(old.firstiter) + self.assertIsNone(old.finalizer) + + firstiter = lambda *a: None + sys.set_asyncgen_hooks(firstiter=firstiter) + hooks = sys.get_asyncgen_hooks() + self.assertIs(hooks.firstiter, firstiter) + self.assertIs(hooks[0], firstiter) + self.assertIs(hooks.finalizer, None) + self.assertIs(hooks[1], None) + + finalizer = lambda *a: None + sys.set_asyncgen_hooks(finalizer=finalizer) + hooks = sys.get_asyncgen_hooks() + self.assertIs(hooks.firstiter, firstiter) + self.assertIs(hooks[0], firstiter) + self.assertIs(hooks.finalizer, finalizer) + self.assertIs(hooks[1], finalizer) + + sys.set_asyncgen_hooks(*old) + cur = sys.get_asyncgen_hooks() + self.assertIsNone(cur.firstiter) + self.assertIsNone(cur.finalizer) + def test_main(): test.support.run_unittest(SysModuleTest, SizeofTest) diff --git a/Lib/types.py b/Lib/types.py index 48891cd3f6..d8d84709e1 100644 --- a/Lib/types.py +++ b/Lib/types.py @@ -24,6 +24,11 @@ _c = _c() CoroutineType = type(_c) _c.close() # Prevent ResourceWarning +async def _ag(): + yield +_ag = _ag() +AsyncGeneratorType = type(_ag) + class _C: def _m(self): pass MethodType = type(_C()._m) -- cgit v1.2.1 From ed60e0fd6966f31a1d53fbec9a1a10b943b824d2 Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Fri, 9 Sep 2016 06:46:48 +0000 Subject: Issue #27106: Add test for configparser.__all__ Patch by Jacek Ko?odziej. The Error class is deliberately omitted because it is a generic name and of limited use. --- Lib/test/test_configparser.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_configparser.py b/Lib/test/test_configparser.py index 71a8f3f8d9..2b08198657 100644 --- a/Lib/test/test_configparser.py +++ b/Lib/test/test_configparser.py @@ -2052,5 +2052,11 @@ class BlatantOverrideConvertersTestCase(unittest.TestCase): self.assertEqual(cfg['two'].getlen('one'), 5) +class MiscTestCase(unittest.TestCase): + def test__all__(self): + blacklist = {"Error"} + support.check__all__(self, configparser, blacklist=blacklist) + + if __name__ == '__main__': unittest.main() -- cgit v1.2.1 From 7d06a0f3444cb84bdf3a0f51db12f136ff2bf9b6 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Fri, 9 Sep 2016 08:56:37 -0700 Subject: Revert #27959: ImportError within an encoding module should also skip the encoding --- Lib/encodings/__init__.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'Lib') diff --git a/Lib/encodings/__init__.py b/Lib/encodings/__init__.py index cf90568605..aa2fb7c2b9 100644 --- a/Lib/encodings/__init__.py +++ b/Lib/encodings/__init__.py @@ -98,9 +98,10 @@ def search_function(encoding): # module with side-effects that is not in the 'encodings' package. mod = __import__('encodings.' + modname, fromlist=_import_tail, level=0) - except ModuleNotFoundError as ex: - if ex.name != 'encodings.' + modname: - raise + except ImportError: + # ImportError may occur because 'encodings.(modname)' does not exist, + # or because it imports a name that does not exist (see mbcs and oem) + pass else: break else: -- cgit v1.2.1 From ad5f4b13022b4658932dfb38b8d8a0ff37cf5f1b Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 9 Sep 2016 09:06:11 -0700 Subject: Issue #28038: Remove Tools/parser/com2ann.py and its unit test. Development is moving to https://github.com/ilevkivskyi/com2ann --- Lib/test/test_tools/test_com2ann.py | 260 ------------------------------------ 1 file changed, 260 deletions(-) delete mode 100644 Lib/test/test_tools/test_com2ann.py (limited to 'Lib') diff --git a/Lib/test/test_tools/test_com2ann.py b/Lib/test/test_tools/test_com2ann.py deleted file mode 100644 index 2731f82ce7..0000000000 --- a/Lib/test/test_tools/test_com2ann.py +++ /dev/null @@ -1,260 +0,0 @@ -"""Tests for the com2ann.py script in the Tools/parser directory.""" - -import unittest -import test.support -import os -import re - -from test.test_tools import basepath, toolsdir, skip_if_missing - -skip_if_missing() - -parser_path = os.path.join(toolsdir, "parser") - -with test.support.DirsOnSysPath(parser_path): - from com2ann import * - -class BaseTestCase(unittest.TestCase): - - def check(self, code, expected, n=False, e=False): - self.assertEqual(com2ann(code, - drop_None=n, drop_Ellipsis=e, silent=True), - expected) - -class SimpleTestCase(BaseTestCase): - # Tests for basic conversions - - def test_basics(self): - self.check("z = 5", "z = 5") - self.check("z: int = 5", "z: int = 5") - self.check("z = 5 # type: int", "z: int = 5") - self.check("z = 5 # type: int # comment", - "z: int = 5 # comment") - - def test_type_ignore(self): - self.check("foobar = foobaz() #type: ignore", - "foobar = foobaz() #type: ignore") - self.check("a = 42 #type: ignore #comment", - "a = 42 #type: ignore #comment") - - def test_complete_tuple(self): - self.check("t = 1, 2, 3 # type: Tuple[int, ...]", - "t: Tuple[int, ...] = (1, 2, 3)") - self.check("t = 1, # type: Tuple[int]", - "t: Tuple[int] = (1,)") - self.check("t = (1, 2, 3) # type: Tuple[int, ...]", - "t: Tuple[int, ...] = (1, 2, 3)") - - def test_drop_None(self): - self.check("x = None # type: int", - "x: int", True) - self.check("x = None # type: int # another", - "x: int # another", True) - self.check("x = None # type: int # None", - "x: int # None", True) - - def test_drop_Ellipsis(self): - self.check("x = ... # type: int", - "x: int", False, True) - self.check("x = ... # type: int # another", - "x: int # another", False, True) - self.check("x = ... # type: int # ...", - "x: int # ...", False, True) - - def test_newline(self): - self.check("z = 5 # type: int\r\n", "z: int = 5\r\n") - self.check("z = 5 # type: int # comment\x85", - "z: int = 5 # comment\x85") - - def test_wrong(self): - self.check("#type : str", "#type : str") - self.check("x==y #type: bool", "x==y #type: bool") - - def test_pattern(self): - for line in ["#type: int", " # type: str[:] # com"]: - self.assertTrue(re.search(TYPE_COM, line)) - for line in ["", "#", "# comment", "#type", "type int:"]: - self.assertFalse(re.search(TYPE_COM, line)) - -class BigTestCase(BaseTestCase): - # Tests for really crazy formatting, to be sure - # that script works reasonably in extreme situations - - def test_crazy(self): - self.maxDiff = None - self.check(crazy_code, big_result, False, False) - self.check(crazy_code, big_result_ne, True, True) - -crazy_code = """\ -# -*- coding: utf-8 -*- # this should not be spoiled -''' -Docstring here -''' - -import testmod -x = 5 #type : int # this one is OK -ttt \\ - = \\ - 1.0, \\ - 2.0, \\ - 3.0, #type: Tuple[float, float, float] -with foo(x==1) as f: #type: str - print(f) - -for i, j in my_inter(x=1): # type: ignore - i + j # type: int # what about this - -x = y = z = 1 # type: int -x, y, z = [], [], [] # type: (List[int], List[int], List[str]) -class C: - - - l[f(x - =1)] = [ - - 1, - 2, - ] # type: List[int] - - - (C.x[1]) = \\ - 42 == 5# type: bool -lst[...] = \\ - ((\\ -...)) # type: int # comment .. - -y = ... # type: int # comment ... -z = ... -#type: int - - -#DONE placement of annotation after target rather than before = - -TD.x[1] \\ - = 0 == 5# type: bool - -TD.y[1] =5 == 5# type: bool # one more here -F[G(x == y, - -# hm... - - z)]\\ - = None # type: OMG[int] # comment: None -x = None#type:int #comment : None""" - -big_result = """\ -# -*- coding: utf-8 -*- # this should not be spoiled -''' -Docstring here -''' - -import testmod -x: int = 5 # this one is OK -ttt: Tuple[float, float, float] \\ - = \\ - (1.0, \\ - 2.0, \\ - 3.0,) -with foo(x==1) as f: #type: str - print(f) - -for i, j in my_inter(x=1): # type: ignore - i + j # type: int # what about this - -x = y = z = 1 # type: int -x, y, z = [], [], [] # type: (List[int], List[int], List[str]) -class C: - - - l[f(x - =1)]: List[int] = [ - - 1, - 2, - ] - - - (C.x[1]): bool = \\ - 42 == 5 -lst[...]: int = \\ - ((\\ -...)) # comment .. - -y: int = ... # comment ... -z = ... -#type: int - - -#DONE placement of annotation after target rather than before = - -TD.x[1]: bool \\ - = 0 == 5 - -TD.y[1]: bool =5 == 5 # one more here -F[G(x == y, - -# hm... - - z)]: OMG[int]\\ - = None # comment: None -x: int = None #comment : None""" - -big_result_ne = """\ -# -*- coding: utf-8 -*- # this should not be spoiled -''' -Docstring here -''' - -import testmod -x: int = 5 # this one is OK -ttt: Tuple[float, float, float] \\ - = \\ - (1.0, \\ - 2.0, \\ - 3.0,) -with foo(x==1) as f: #type: str - print(f) - -for i, j in my_inter(x=1): # type: ignore - i + j # type: int # what about this - -x = y = z = 1 # type: int -x, y, z = [], [], [] # type: (List[int], List[int], List[str]) -class C: - - - l[f(x - =1)]: List[int] = [ - - 1, - 2, - ] - - - (C.x[1]): bool = \\ - 42 == 5 -lst[...]: int \\ - \\ - # comment .. - -y: int # comment ... -z = ... -#type: int - - -#DONE placement of annotation after target rather than before = - -TD.x[1]: bool \\ - = 0 == 5 - -TD.y[1]: bool =5 == 5 # one more here -F[G(x == y, - -# hm... - - z)]: OMG[int]\\ - # comment: None -x: int #comment : None""" - -if __name__ == '__main__': - unittest.main() -- cgit v1.2.1 From e74ae1493c69617408dcd5769b110b8e1ad8a468 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Fri, 9 Sep 2016 09:17:35 -0700 Subject: Changes pyvenv.cfg trick into an actual sys.path file. --- Lib/site.py | 6 ------ 1 file changed, 6 deletions(-) (limited to 'Lib') diff --git a/Lib/site.py b/Lib/site.py index a536ef17ca..5250266755 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -463,12 +463,6 @@ def venv(known_paths): system_site = value.lower() elif key == 'home': sys._home = value - elif key == 'applocal' and value.lower() == 'true': - # App-local installs use the exe_dir as prefix, - # not one level higher, and do not use system - # site packages. - site_prefix = exe_dir - system_site = 'false' sys.prefix = sys.exec_prefix = site_prefix -- cgit v1.2.1 From 25a17c0525559046dd33e42b2750d929fdb02732 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Fri, 9 Sep 2016 09:21:01 -0700 Subject: Fixes expected error when getting encoding while shutting down. --- Lib/test/test_io.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 1115d9f1c2..c48ec3a239 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -3276,7 +3276,7 @@ class CTextIOWrapperTest(TextIOWrapperTest): class PyTextIOWrapperTest(TextIOWrapperTest): io = pyio - shutdown_error = "ImportError: sys.meta_path is None, Python is likely shutting down" + shutdown_error = "LookupError: unknown encoding: ascii" class IncrementalNewlineDecoderTest(unittest.TestCase): -- cgit v1.2.1 From 28f90ff4e19dd2030155b50984419a302d94421c Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 9 Sep 2016 09:36:26 -0700 Subject: Issue #27999: Make "global after use" a SyntaxError, and ditto for nonlocal. Patch by Ivan Levkivskyi. --- Lib/test/test_syntax.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index f47bdf9672..80e36fd46e 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -366,7 +366,23 @@ build. The number of blocks must be greater than CO_MAXBLOCKS. SF #1565514 ... SyntaxError: too many statically nested blocks -Misuse of the nonlocal statement can lead to a few unique syntax errors. +Misuse of the nonlocal and global statement can lead to a few unique syntax errors. + + >>> def f(): + ... x = 1 + ... global x + Traceback (most recent call last): + ... + SyntaxError: name 'x' is assigned to before global declaration + + >>> def f(): + ... x = 1 + ... def g(): + ... print(x) + ... nonlocal x + Traceback (most recent call last): + ... + SyntaxError: name 'x' is used prior to nonlocal declaration >>> def f(x): ... nonlocal x -- cgit v1.2.1 From b26bd4a4bf3de9dbedbbbd1209fea77cd7af7689 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Fri, 9 Sep 2016 10:36:01 -0700 Subject: Issue #28008: Implement PEP 530 -- asynchronous comprehensions. --- Lib/lib2to3/Grammar.txt | 2 +- Lib/lib2to3/tests/test_parser.py | 18 +++ Lib/test/badsyntax_async1.py | 2 - Lib/test/badsyntax_async2.py | 2 - Lib/test/badsyntax_async3.py | 2 - Lib/test/badsyntax_async4.py | 2 - Lib/test/badsyntax_async5.py | 2 - Lib/test/badsyntax_async7.py | 2 - Lib/test/badsyntax_async8.py | 2 - Lib/test/test_ast.py | 35 +++-- Lib/test/test_coroutines.py | 324 +++++++++++++++++++++++++++++++++++---- 11 files changed, 330 insertions(+), 63 deletions(-) delete mode 100644 Lib/test/badsyntax_async1.py delete mode 100644 Lib/test/badsyntax_async2.py delete mode 100644 Lib/test/badsyntax_async3.py delete mode 100644 Lib/test/badsyntax_async4.py delete mode 100644 Lib/test/badsyntax_async5.py delete mode 100644 Lib/test/badsyntax_async7.py delete mode 100644 Lib/test/badsyntax_async8.py (limited to 'Lib') diff --git a/Lib/lib2to3/Grammar.txt b/Lib/lib2to3/Grammar.txt index abe1268c27..dcdd02d662 100644 --- a/Lib/lib2to3/Grammar.txt +++ b/Lib/lib2to3/Grammar.txt @@ -150,7 +150,7 @@ arglist: (argument ',')* (argument [','] argument: test [comp_for] | test '=' test # Really [keyword '='] test comp_iter: comp_for | comp_if -comp_for: 'for' exprlist 'in' testlist_safe [comp_iter] +comp_for: [ASYNC] 'for' exprlist 'in' testlist_safe [comp_iter] comp_if: 'if' old_test [comp_iter] testlist1: test (',' test)* diff --git a/Lib/lib2to3/tests/test_parser.py b/Lib/lib2to3/tests/test_parser.py index b37816374f..7613f53927 100644 --- a/Lib/lib2to3/tests/test_parser.py +++ b/Lib/lib2to3/tests/test_parser.py @@ -133,6 +133,24 @@ class TestAsyncAwait(GrammarTest): await x """) + self.validate("""async def foo(): + [i async for i in b] + """) + + self.validate("""async def foo(): + {i for i in b + async for i in a if await i + for b in i} + """) + + self.validate("""async def foo(): + [await i for i in b if await c] + """) + + self.validate("""async def foo(): + [ i for i in b if c] + """) + self.validate("""async def foo(): def foo(): pass diff --git a/Lib/test/badsyntax_async1.py b/Lib/test/badsyntax_async1.py deleted file mode 100644 index fb85e29052..0000000000 --- a/Lib/test/badsyntax_async1.py +++ /dev/null @@ -1,2 +0,0 @@ -async def foo(a=await something()): - pass diff --git a/Lib/test/badsyntax_async2.py b/Lib/test/badsyntax_async2.py deleted file mode 100644 index fb85e29052..0000000000 --- a/Lib/test/badsyntax_async2.py +++ /dev/null @@ -1,2 +0,0 @@ -async def foo(a=await something()): - pass diff --git a/Lib/test/badsyntax_async3.py b/Lib/test/badsyntax_async3.py deleted file mode 100644 index dde1bc5955..0000000000 --- a/Lib/test/badsyntax_async3.py +++ /dev/null @@ -1,2 +0,0 @@ -async def foo(): - [i async for i in els] diff --git a/Lib/test/badsyntax_async4.py b/Lib/test/badsyntax_async4.py deleted file mode 100644 index d033b28114..0000000000 --- a/Lib/test/badsyntax_async4.py +++ /dev/null @@ -1,2 +0,0 @@ -async def foo(): - await diff --git a/Lib/test/badsyntax_async5.py b/Lib/test/badsyntax_async5.py deleted file mode 100644 index 9d19af6109..0000000000 --- a/Lib/test/badsyntax_async5.py +++ /dev/null @@ -1,2 +0,0 @@ -def foo(): - await something() diff --git a/Lib/test/badsyntax_async7.py b/Lib/test/badsyntax_async7.py deleted file mode 100644 index 51e4bf9b5b..0000000000 --- a/Lib/test/badsyntax_async7.py +++ /dev/null @@ -1,2 +0,0 @@ -async def foo(): - yield from [] diff --git a/Lib/test/badsyntax_async8.py b/Lib/test/badsyntax_async8.py deleted file mode 100644 index 3c636f9ac4..0000000000 --- a/Lib/test/badsyntax_async8.py +++ /dev/null @@ -1,2 +0,0 @@ -async def foo(): - await await fut diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index e032f6d27a..8c62408bd8 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -116,6 +116,8 @@ exec_tests = [ # PEP 448: Additional Unpacking Generalizations "{**{1:2}, 2:3}", "{*{1, 2}, 3}", + # Asynchronous comprehensions + "async def f():\n [i async for b in c]", ] # These are compiled through "single" @@ -809,21 +811,21 @@ class ASTValidatorTests(unittest.TestCase): def _check_comprehension(self, fac): self.expr(fac([]), "comprehension with no generators") g = ast.comprehension(ast.Name("x", ast.Load()), - ast.Name("x", ast.Load()), []) + ast.Name("x", ast.Load()), [], 0) self.expr(fac([g]), "must have Store context") g = ast.comprehension(ast.Name("x", ast.Store()), - ast.Name("x", ast.Store()), []) + ast.Name("x", ast.Store()), [], 0) self.expr(fac([g]), "must have Load context") x = ast.Name("x", ast.Store()) y = ast.Name("y", ast.Load()) - g = ast.comprehension(x, y, [None]) + g = ast.comprehension(x, y, [None], 0) self.expr(fac([g]), "None disallowed") - g = ast.comprehension(x, y, [ast.Name("x", ast.Store())]) + g = ast.comprehension(x, y, [ast.Name("x", ast.Store())], 0) self.expr(fac([g]), "must have Load context") def _simple_comp(self, fac): g = ast.comprehension(ast.Name("x", ast.Store()), - ast.Name("x", ast.Load()), []) + ast.Name("x", ast.Load()), [], 0) self.expr(fac(ast.Name("x", ast.Store()), [g]), "must have Load context") def wrap(gens): @@ -841,7 +843,7 @@ class ASTValidatorTests(unittest.TestCase): def test_dictcomp(self): g = ast.comprehension(ast.Name("y", ast.Store()), - ast.Name("p", ast.Load()), []) + ast.Name("p", ast.Load()), [], 0) c = ast.DictComp(ast.Name("x", ast.Store()), ast.Name("y", ast.Load()), [g]) self.expr(c, "must have Load context") @@ -1111,19 +1113,20 @@ exec_results = [ ('Module', [('For', (1, 0), ('Name', (1, 4), 'v', ('Store',)), ('Name', (1, 9), 'v', ('Load',)), [('Break', (1, 11))], [])]), ('Module', [('For', (1, 0), ('Name', (1, 4), 'v', ('Store',)), ('Name', (1, 9), 'v', ('Load',)), [('Continue', (1, 11))], [])]), ('Module', [('For', (1, 0), ('Tuple', (1, 4), [('Name', (1, 4), 'a', ('Store',)), ('Name', (1, 6), 'b', ('Store',))], ('Store',)), ('Name', (1, 11), 'c', ('Load',)), [('Pass', (1, 14))], [])]), -('Module', [('Expr', (1, 0), ('ListComp', (1, 1), ('Tuple', (1, 2), [('Name', (1, 2), 'a', ('Load',)), ('Name', (1, 4), 'b', ('Load',))], ('Load',)), [('comprehension', ('Tuple', (1, 11), [('Name', (1, 11), 'a', ('Store',)), ('Name', (1, 13), 'b', ('Store',))], ('Store',)), ('Name', (1, 18), 'c', ('Load',)), [])]))]), -('Module', [('Expr', (1, 0), ('GeneratorExp', (1, 1), ('Tuple', (1, 2), [('Name', (1, 2), 'a', ('Load',)), ('Name', (1, 4), 'b', ('Load',))], ('Load',)), [('comprehension', ('Tuple', (1, 11), [('Name', (1, 11), 'a', ('Store',)), ('Name', (1, 13), 'b', ('Store',))], ('Store',)), ('Name', (1, 18), 'c', ('Load',)), [])]))]), -('Module', [('Expr', (1, 0), ('GeneratorExp', (1, 1), ('Tuple', (1, 2), [('Name', (1, 2), 'a', ('Load',)), ('Name', (1, 4), 'b', ('Load',))], ('Load',)), [('comprehension', ('Tuple', (1, 12), [('Name', (1, 12), 'a', ('Store',)), ('Name', (1, 14), 'b', ('Store',))], ('Store',)), ('Name', (1, 20), 'c', ('Load',)), [])]))]), -('Module', [('Expr', (1, 0), ('GeneratorExp', (2, 4), ('Tuple', (3, 4), [('Name', (3, 4), 'Aa', ('Load',)), ('Name', (5, 7), 'Bb', ('Load',))], ('Load',)), [('comprehension', ('Tuple', (8, 4), [('Name', (8, 4), 'Aa', ('Store',)), ('Name', (10, 4), 'Bb', ('Store',))], ('Store',)), ('Name', (10, 10), 'Cc', ('Load',)), [])]))]), -('Module', [('Expr', (1, 0), ('DictComp', (1, 0), ('Name', (1, 1), 'a', ('Load',)), ('Name', (1, 5), 'b', ('Load',)), [('comprehension', ('Name', (1, 11), 'w', ('Store',)), ('Name', (1, 16), 'x', ('Load',)), []), ('comprehension', ('Name', (1, 22), 'm', ('Store',)), ('Name', (1, 27), 'p', ('Load',)), [('Name', (1, 32), 'g', ('Load',))])]))]), -('Module', [('Expr', (1, 0), ('DictComp', (1, 0), ('Name', (1, 1), 'a', ('Load',)), ('Name', (1, 5), 'b', ('Load',)), [('comprehension', ('Tuple', (1, 11), [('Name', (1, 11), 'v', ('Store',)), ('Name', (1, 13), 'w', ('Store',))], ('Store',)), ('Name', (1, 18), 'x', ('Load',)), [])]))]), -('Module', [('Expr', (1, 0), ('SetComp', (1, 0), ('Name', (1, 1), 'r', ('Load',)), [('comprehension', ('Name', (1, 7), 'l', ('Store',)), ('Name', (1, 12), 'x', ('Load',)), [('Name', (1, 17), 'g', ('Load',))])]))]), -('Module', [('Expr', (1, 0), ('SetComp', (1, 0), ('Name', (1, 1), 'r', ('Load',)), [('comprehension', ('Tuple', (1, 7), [('Name', (1, 7), 'l', ('Store',)), ('Name', (1, 9), 'm', ('Store',))], ('Store',)), ('Name', (1, 14), 'x', ('Load',)), [])]))]), +('Module', [('Expr', (1, 0), ('ListComp', (1, 1), ('Tuple', (1, 2), [('Name', (1, 2), 'a', ('Load',)), ('Name', (1, 4), 'b', ('Load',))], ('Load',)), [('comprehension', ('Tuple', (1, 11), [('Name', (1, 11), 'a', ('Store',)), ('Name', (1, 13), 'b', ('Store',))], ('Store',)), ('Name', (1, 18), 'c', ('Load',)), [], 0)]))]), +('Module', [('Expr', (1, 0), ('GeneratorExp', (1, 1), ('Tuple', (1, 2), [('Name', (1, 2), 'a', ('Load',)), ('Name', (1, 4), 'b', ('Load',))], ('Load',)), [('comprehension', ('Tuple', (1, 11), [('Name', (1, 11), 'a', ('Store',)), ('Name', (1, 13), 'b', ('Store',))], ('Store',)), ('Name', (1, 18), 'c', ('Load',)), [], 0)]))]), +('Module', [('Expr', (1, 0), ('GeneratorExp', (1, 1), ('Tuple', (1, 2), [('Name', (1, 2), 'a', ('Load',)), ('Name', (1, 4), 'b', ('Load',))], ('Load',)), [('comprehension', ('Tuple', (1, 12), [('Name', (1, 12), 'a', ('Store',)), ('Name', (1, 14), 'b', ('Store',))], ('Store',)), ('Name', (1, 20), 'c', ('Load',)), [], 0)]))]), +('Module', [('Expr', (1, 0), ('GeneratorExp', (2, 4), ('Tuple', (3, 4), [('Name', (3, 4), 'Aa', ('Load',)), ('Name', (5, 7), 'Bb', ('Load',))], ('Load',)), [('comprehension', ('Tuple', (8, 4), [('Name', (8, 4), 'Aa', ('Store',)), ('Name', (10, 4), 'Bb', ('Store',))], ('Store',)), ('Name', (10, 10), 'Cc', ('Load',)), [], 0)]))]), +('Module', [('Expr', (1, 0), ('DictComp', (1, 0), ('Name', (1, 1), 'a', ('Load',)), ('Name', (1, 5), 'b', ('Load',)), [('comprehension', ('Name', (1, 11), 'w', ('Store',)), ('Name', (1, 16), 'x', ('Load',)), [], 0), ('comprehension', ('Name', (1, 22), 'm', ('Store',)), ('Name', (1, 27), 'p', ('Load',)), [('Name', (1, 32), 'g', ('Load',))], 0)]))]), +('Module', [('Expr', (1, 0), ('DictComp', (1, 0), ('Name', (1, 1), 'a', ('Load',)), ('Name', (1, 5), 'b', ('Load',)), [('comprehension', ('Tuple', (1, 11), [('Name', (1, 11), 'v', ('Store',)), ('Name', (1, 13), 'w', ('Store',))], ('Store',)), ('Name', (1, 18), 'x', ('Load',)), [], 0)]))]), +('Module', [('Expr', (1, 0), ('SetComp', (1, 0), ('Name', (1, 1), 'r', ('Load',)), [('comprehension', ('Name', (1, 7), 'l', ('Store',)), ('Name', (1, 12), 'x', ('Load',)), [('Name', (1, 17), 'g', ('Load',))], 0)]))]), +('Module', [('Expr', (1, 0), ('SetComp', (1, 0), ('Name', (1, 1), 'r', ('Load',)), [('comprehension', ('Tuple', (1, 7), [('Name', (1, 7), 'l', ('Store',)), ('Name', (1, 9), 'm', ('Store',))], ('Store',)), ('Name', (1, 14), 'x', ('Load',)), [], 0)]))]), ('Module', [('AsyncFunctionDef', (1, 6), 'f', ('arguments', [], None, [], [], None, []), [('Expr', (2, 1), ('Await', (2, 1), ('Call', (2, 7), ('Name', (2, 7), 'something', ('Load',)), [], [])))], [], None)]), ('Module', [('AsyncFunctionDef', (1, 6), 'f', ('arguments', [], None, [], [], None, []), [('AsyncFor', (2, 7), ('Name', (2, 11), 'e', ('Store',)), ('Name', (2, 16), 'i', ('Load',)), [('Expr', (2, 19), ('Num', (2, 19), 1))], [('Expr', (3, 7), ('Num', (3, 7), 2))])], [], None)]), ('Module', [('AsyncFunctionDef', (1, 6), 'f', ('arguments', [], None, [], [], None, []), [('AsyncWith', (2, 7), [('withitem', ('Name', (2, 12), 'a', ('Load',)), ('Name', (2, 17), 'b', ('Store',)))], [('Expr', (2, 20), ('Num', (2, 20), 1))])], [], None)]), ('Module', [('Expr', (1, 0), ('Dict', (1, 0), [None, ('Num', (1, 10), 2)], [('Dict', (1, 3), [('Num', (1, 4), 1)], [('Num', (1, 6), 2)]), ('Num', (1, 12), 3)]))]), ('Module', [('Expr', (1, 0), ('Set', (1, 0), [('Starred', (1, 1), ('Set', (1, 2), [('Num', (1, 3), 1), ('Num', (1, 6), 2)]), ('Load',)), ('Num', (1, 10), 3)]))]), +('Module', [('AsyncFunctionDef', (1, 6), 'f', ('arguments', [], None, [], [], None, []), [('Expr', (2, 1), ('ListComp', (2, 2), ('Name', (2, 2), 'i', ('Load',)), [('comprehension', ('Name', (2, 14), 'b', ('Store',)), ('Name', (2, 19), 'c', ('Load',)), [], 1)]))], [], None)]), ] single_results = [ ('Interactive', [('Expr', (1, 0), ('BinOp', (1, 0), ('Num', (1, 0), 1), ('Add',), ('Num', (1, 2), 2)))]), @@ -1138,8 +1141,8 @@ eval_results = [ ('Expression', ('Dict', (1, 0), [], [])), ('Expression', ('Set', (1, 0), [('NameConstant', (1, 1), None)])), ('Expression', ('Dict', (1, 0), [('Num', (2, 6), 1)], [('Num', (4, 10), 2)])), -('Expression', ('ListComp', (1, 1), ('Name', (1, 1), 'a', ('Load',)), [('comprehension', ('Name', (1, 7), 'b', ('Store',)), ('Name', (1, 12), 'c', ('Load',)), [('Name', (1, 17), 'd', ('Load',))])])), -('Expression', ('GeneratorExp', (1, 1), ('Name', (1, 1), 'a', ('Load',)), [('comprehension', ('Name', (1, 7), 'b', ('Store',)), ('Name', (1, 12), 'c', ('Load',)), [('Name', (1, 17), 'd', ('Load',))])])), +('Expression', ('ListComp', (1, 1), ('Name', (1, 1), 'a', ('Load',)), [('comprehension', ('Name', (1, 7), 'b', ('Store',)), ('Name', (1, 12), 'c', ('Load',)), [('Name', (1, 17), 'd', ('Load',))], 0)])), +('Expression', ('GeneratorExp', (1, 1), ('Name', (1, 1), 'a', ('Load',)), [('comprehension', ('Name', (1, 7), 'b', ('Store',)), ('Name', (1, 12), 'c', ('Load',)), [('Name', (1, 17), 'd', ('Load',))], 0)])), ('Expression', ('Compare', (1, 0), ('Num', (1, 0), 1), [('Lt',), ('Lt',)], [('Num', (1, 4), 2), ('Num', (1, 8), 3)])), ('Expression', ('Call', (1, 0), ('Name', (1, 0), 'f', ('Load',)), [('Num', (1, 2), 1), ('Num', (1, 4), 2), ('Starred', (1, 10), ('Name', (1, 11), 'd', ('Load',)), ('Load',))], [('keyword', 'c', ('Num', (1, 8), 3)), ('keyword', None, ('Name', (1, 15), 'e', ('Load',)))])), ('Expression', ('Num', (1, 0), 10)), diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py index fee9ae3b71..154ce7fdee 100644 --- a/Lib/test/test_coroutines.py +++ b/Lib/test/test_coroutines.py @@ -69,49 +69,130 @@ def silence_coro_gc(): class AsyncBadSyntaxTest(unittest.TestCase): def test_badsyntax_1(self): - with self.assertRaisesRegex(SyntaxError, "'await' outside"): - import test.badsyntax_async1 + samples = [ + """def foo(): + await something() + """, - def test_badsyntax_2(self): - with self.assertRaisesRegex(SyntaxError, "'await' outside"): - import test.badsyntax_async2 + """await something()""", - def test_badsyntax_3(self): - with self.assertRaisesRegex(SyntaxError, 'invalid syntax'): - import test.badsyntax_async3 + """async def foo(): + yield from [] + """, - def test_badsyntax_4(self): - with self.assertRaisesRegex(SyntaxError, 'invalid syntax'): - import test.badsyntax_async4 + """async def foo(): + await await fut + """, - def test_badsyntax_5(self): - with self.assertRaisesRegex(SyntaxError, 'invalid syntax'): - import test.badsyntax_async5 + """async def foo(a=await something()): + pass + """, - def test_badsyntax_7(self): - with self.assertRaisesRegex( - SyntaxError, "'yield from' inside async function"): + """async def foo(a:await something()): + pass + """, + + """async def foo(): + def bar(): + [i async for i in els] + """, - import test.badsyntax_async7 + """async def foo(): + def bar(): + [await i for i in els] + """, - def test_badsyntax_8(self): - with self.assertRaisesRegex(SyntaxError, 'invalid syntax'): - import test.badsyntax_async8 + """async def foo(): + def bar(): + [i for i in els + async for b in els] + """, - def test_badsyntax_9(self): - ns = {} - for comp in {'(await a for a in b)', - '[await a for a in b]', - '{await a for a in b}', - '{await a: c for a in b}'}: + """async def foo(): + def bar(): + [i for i in els + for c in b + async for b in els] + """, - with self.assertRaisesRegex(SyntaxError, 'await.*in comprehen'): - exec('async def f():\n\t{}'.format(comp), ns, ns) + """async def foo(): + def bar(): + [i for i in els + async for b in els + for c in b] + """, - def test_badsyntax_10(self): - # Tests for issue 24619 + """async def foo(): + def bar(): + [i for i in els + for b in await els] + """, + + """async def foo(): + def bar(): + [i for i in els + for b in els + if await b] + """, + + """async def foo(): + def bar(): + [i for i in await els] + """, + + """async def foo(): + def bar(): + [i for i in els if await i] + """, + + """def bar(): + [i async for i in els] + """, + + """def bar(): + [await i for i in els] + """, + + """def bar(): + [i for i in els + async for b in els] + """, + + """def bar(): + [i for i in els + for c in b + async for b in els] + """, + + """def bar(): + [i for i in els + async for b in els + for c in b] + """, + + """def bar(): + [i for i in els + for b in await els] + """, + + """def bar(): + [i for i in els + for b in els + if await b] + """, + + """def bar(): + [i for i in await els] + """, + + """def bar(): + [i for i in els if await i] + """, + + """async def foo(): + await + """, - samples = [ """async def foo(): def bar(): pass await = 1 @@ -1531,6 +1612,185 @@ class CoroutineTest(unittest.TestCase): warnings.simplefilter("error") run_async(foo()) + def test_comp_1(self): + async def f(i): + return i + + async def run_list(): + return [await c for c in [f(1), f(41)]] + + async def run_set(): + return {await c for c in [f(1), f(41)]} + + async def run_dict1(): + return {await c: 'a' for c in [f(1), f(41)]} + + async def run_dict2(): + return {i: await c for i, c in enumerate([f(1), f(41)])} + + self.assertEqual(run_async(run_list()), ([], [1, 41])) + self.assertEqual(run_async(run_set()), ([], {1, 41})) + self.assertEqual(run_async(run_dict1()), ([], {1: 'a', 41: 'a'})) + self.assertEqual(run_async(run_dict2()), ([], {0: 1, 1: 41})) + + def test_comp_2(self): + async def f(i): + return i + + async def run_list(): + return [s for c in [f(''), f('abc'), f(''), f(['de', 'fg'])] + for s in await c] + + self.assertEqual( + run_async(run_list()), + ([], ['a', 'b', 'c', 'de', 'fg'])) + + async def run_set(): + return {d + for c in [f([f([10, 30]), + f([20])])] + for s in await c + for d in await s} + + self.assertEqual( + run_async(run_set()), + ([], {10, 20, 30})) + + async def run_set2(): + return {await s + for c in [f([f(10), f(20)])] + for s in await c} + + self.assertEqual( + run_async(run_set2()), + ([], {10, 20})) + + def test_comp_3(self): + async def f(it): + for i in it: + yield i + + async def run_list(): + return [i + 1 async for i in f([10, 20])] + self.assertEqual( + run_async(run_list()), + ([], [11, 21])) + + async def run_set(): + return {i + 1 async for i in f([10, 20])} + self.assertEqual( + run_async(run_set()), + ([], {11, 21})) + + async def run_dict(): + return {i + 1: i + 2 async for i in f([10, 20])} + self.assertEqual( + run_async(run_dict()), + ([], {11: 12, 21: 22})) + + async def run_gen(): + gen = (i + 1 async for i in f([10, 20])) + return [g + 100 async for g in gen] + self.assertEqual( + run_async(run_gen()), + ([], [111, 121])) + + def test_comp_4(self): + async def f(it): + for i in it: + yield i + + async def run_list(): + return [i + 1 async for i in f([10, 20]) if i > 10] + self.assertEqual( + run_async(run_list()), + ([], [21])) + + async def run_set(): + return {i + 1 async for i in f([10, 20]) if i > 10} + self.assertEqual( + run_async(run_set()), + ([], {21})) + + async def run_dict(): + return {i + 1: i + 2 async for i in f([10, 20]) if i > 10} + self.assertEqual( + run_async(run_dict()), + ([], {21: 22})) + + async def run_gen(): + gen = (i + 1 async for i in f([10, 20]) if i > 10) + return [g + 100 async for g in gen] + self.assertEqual( + run_async(run_gen()), + ([], [121])) + + def test_comp_5(self): + async def f(it): + for i in it: + yield i + + async def run_list(): + return [i + 1 for pair in ([10, 20], [30, 40]) if pair[0] > 10 + async for i in f(pair) if i > 30] + self.assertEqual( + run_async(run_list()), + ([], [41])) + + def test_comp_6(self): + async def f(it): + for i in it: + yield i + + async def run_list(): + return [i + 1 async for seq in f([(10, 20), (30,)]) + for i in seq] + + self.assertEqual( + run_async(run_list()), + ([], [11, 21, 31])) + + def test_comp_7(self): + async def f(): + yield 1 + yield 2 + raise Exception('aaa') + + async def run_list(): + return [i async for i in f()] + + with self.assertRaisesRegex(Exception, 'aaa'): + run_async(run_list()) + + def test_comp_8(self): + async def f(): + return [i for i in [1, 2, 3]] + + self.assertEqual( + run_async(f()), + ([], [1, 2, 3])) + + def test_comp_9(self): + async def gen(): + yield 1 + yield 2 + async def f(): + l = [i async for i in gen()] + return [i for i in l] + + self.assertEqual( + run_async(f()), + ([], [1, 2])) + + def test_comp_10(self): + async def f(): + xx = {i for i in [1, 2, 3]} + return {x: x for x in xx} + + self.assertEqual( + run_async(f()), + ([], {1: 1, 2: 2, 3: 3})) + def test_copy(self): async def func(): pass coro = func() -- cgit v1.2.1 From 1e01ae8b519c8f8dc064f4a9f7538a484d0d447d Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Fri, 9 Sep 2016 11:14:59 -0700 Subject: tests: use subTest in test_unparse.test_files --- Lib/test/test_tools/test_unparse.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_tools/test_unparse.py b/Lib/test/test_tools/test_unparse.py index 517f2613aa..ed0001a15a 100644 --- a/Lib/test/test_tools/test_unparse.py +++ b/Lib/test/test_tools/test_unparse.py @@ -293,8 +293,9 @@ class DirectoryTestCase(ASTTestCase): print(f'Skipping {filename}: see issue 27921') continue - source = read_pyfile(filename) - self.check_roundtrip(source) + with self.subTest(filename=filename): + source = read_pyfile(filename) + self.check_roundtrip(source) if __name__ == '__main__': -- cgit v1.2.1 From 43801e247b698f530724b503e571d74b2def5bea Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Fri, 9 Sep 2016 11:38:38 -0700 Subject: Remove Lib/test/test_pep247.py This test file is a holdover from the days before hashlib, and doesn't seem to have anything of value in it. --- Lib/test/test_pep247.py | 66 ------------------------------------------------- 1 file changed, 66 deletions(-) delete mode 100644 Lib/test/test_pep247.py (limited to 'Lib') diff --git a/Lib/test/test_pep247.py b/Lib/test/test_pep247.py deleted file mode 100644 index c17ceed810..0000000000 --- a/Lib/test/test_pep247.py +++ /dev/null @@ -1,66 +0,0 @@ -""" -Test suite to check compliance with PEP 247, the standard API -for hashing algorithms -""" - -import hmac -import unittest -from hashlib import md5, sha1, sha224, sha256, sha384, sha512 - -class Pep247Test(unittest.TestCase): - - def check_module(self, module, key=None): - self.assertTrue(hasattr(module, 'digest_size')) - self.assertTrue(module.digest_size is None or module.digest_size > 0) - self.check_object(module.new, module.digest_size, key) - - def check_object(self, cls, digest_size, key, digestmod=None): - if key is not None: - if digestmod is None: - digestmod = md5 - obj1 = cls(key, digestmod=digestmod) - obj2 = cls(key, b'string', digestmod=digestmod) - h1 = cls(key, b'string', digestmod=digestmod).digest() - obj3 = cls(key, digestmod=digestmod) - obj3.update(b'string') - h2 = obj3.digest() - else: - obj1 = cls() - obj2 = cls(b'string') - h1 = cls(b'string').digest() - obj3 = cls() - obj3.update(b'string') - h2 = obj3.digest() - self.assertEqual(h1, h2) - self.assertTrue(hasattr(obj1, 'digest_size')) - - if digest_size is not None: - self.assertEqual(obj1.digest_size, digest_size) - - self.assertEqual(obj1.digest_size, len(h1)) - obj1.update(b'string') - obj_copy = obj1.copy() - self.assertEqual(obj1.digest(), obj_copy.digest()) - self.assertEqual(obj1.hexdigest(), obj_copy.hexdigest()) - - digest, hexdigest = obj1.digest(), obj1.hexdigest() - hd2 = "" - for byte in digest: - hd2 += '%02x' % byte - self.assertEqual(hd2, hexdigest) - - def test_md5(self): - self.check_object(md5, None, None) - - def test_sha(self): - self.check_object(sha1, None, None) - self.check_object(sha224, None, None) - self.check_object(sha256, None, None) - self.check_object(sha384, None, None) - self.check_object(sha512, None, None) - - def test_hmac(self): - self.check_module(hmac, key=b'abc') - -if __name__ == '__main__': - unittest.main() -- cgit v1.2.1 From 4db82a225deae98b1f4a25e3fbccb05b94dce3cf Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Fri, 9 Sep 2016 11:59:08 -0700 Subject: Issue #27576: Fix call order in OrderedDict.__init__(). --- Lib/test/test_ordered_dict.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_ordered_dict.py b/Lib/test/test_ordered_dict.py index d6e72a6ed2..2da36d3032 100644 --- a/Lib/test/test_ordered_dict.py +++ b/Lib/test/test_ordered_dict.py @@ -98,6 +98,19 @@ class OrderedDictTests: self.assertRaises(TypeError, OrderedDict().update, (), ()) self.assertRaises(TypeError, OrderedDict.update) + def test_init_calls(self): + calls = [] + class Spam: + def keys(self): + calls.append('keys') + return () + def items(self): + calls.append('items') + return () + + self.OrderedDict(Spam()) + self.assertEqual(calls, ['keys']) + def test_fromkeys(self): OrderedDict = self.OrderedDict od = OrderedDict.fromkeys('abc') -- cgit v1.2.1 From 2b5865ab2c78d3bdaf8ad7047762b5d0d41d8654 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 9 Sep 2016 10:17:08 -0700 Subject: Rework CALL_FUNCTION* opcodes Issue #27213: Rework CALL_FUNCTION* opcodes to produce shorter and more efficient bytecode: * CALL_FUNCTION now only accepts position arguments * CALL_FUNCTION_KW accepts position arguments and keyword arguments, but keys of keyword arguments are packed into a constant tuple. * CALL_FUNCTION_EX is the most generic, it expects a tuple and a dict for positional and keyword arguments. CALL_FUNCTION_VAR and CALL_FUNCTION_VAR_KW opcodes have been removed. 2 tests of test_traceback are currently broken: skip test, the issue #28050 was created to track the issue. Patch by Demur Rumed, design by Serhiy Storchaka, reviewed by Serhiy Storchaka and Victor Stinner. --- Lib/dis.py | 2 +- Lib/importlib/_bootstrap_external.py | 3 ++- Lib/opcode.py | 13 ++++--------- Lib/test/test_dis.py | 36 ++++++++++++++++++------------------ Lib/test/test_extcall.py | 18 ++++++++---------- Lib/test/test_traceback.py | 1 + 6 files changed, 34 insertions(+), 39 deletions(-) (limited to 'Lib') diff --git a/Lib/dis.py b/Lib/dis.py index 556d84eb4a..e958c8ad1c 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -314,7 +314,7 @@ def _get_instructions_bytes(code, varnames=None, names=None, constants=None, argrepr = argval elif op in hasfree: argval, argrepr = _get_name_info(arg, cells) - elif op in hasnargs: + elif op in hasnargs: # unused argrepr = "%d positional, %d keyword pair" % (arg%256, arg//256) yield Instruction(opname[op], op, arg, argval, argrepr, diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index ffb93255b2..4340c3b84d 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -236,6 +236,7 @@ _code_type = type(_write_atomic.__code__) # Python 3.6b1 3373 (add BUILD_STRING opcode #27078) # Python 3.6b1 3375 (add SETUP_ANNOTATIONS and STORE_ANNOTATION opcodes # #27985) +# Python 3.6a1 3376 (simplify CALL_FUNCTIONs & BUILD_MAP_UNPACK_WITH_CALL) # # MAGIC must change whenever the bytecode emitted by the compiler may no # longer be understood by older implementations of the eval loop (usually @@ -244,7 +245,7 @@ _code_type = type(_write_atomic.__code__) # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # in PC/launcher.c must also be updated. -MAGIC_NUMBER = (3375).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3376).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c _PYCACHE = '__pycache__' diff --git a/Lib/opcode.py b/Lib/opcode.py index 31d15345e9..be2647502e 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -31,7 +31,7 @@ hasjabs = [] haslocal = [] hascompare = [] hasfree = [] -hasnargs = [] +hasnargs = [] # unused opmap = {} opname = ['<%r>' % (op,) for op in range(256)] @@ -172,8 +172,7 @@ haslocal.append(126) name_op('STORE_ANNOTATION', 127) # Index in name list def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3) -def_op('CALL_FUNCTION', 131) # #args + (#kwargs << 8) -hasnargs.append(131) +def_op('CALL_FUNCTION', 131) # #args def_op('MAKE_FUNCTION', 132) # Flags def_op('BUILD_SLICE', 133) # Number of items def_op('LOAD_CLOSURE', 135) @@ -185,12 +184,8 @@ hasfree.append(137) def_op('DELETE_DEREF', 138) hasfree.append(138) -def_op('CALL_FUNCTION_VAR', 140) # #args + (#kwargs << 8) -hasnargs.append(140) -def_op('CALL_FUNCTION_KW', 141) # #args + (#kwargs << 8) -hasnargs.append(141) -def_op('CALL_FUNCTION_VAR_KW', 142) # #args + (#kwargs << 8) -hasnargs.append(142) +def_op('CALL_FUNCTION_KW', 141) # #args + #kwargs +def_op('CALL_FUNCTION_EX', 142) # Flags jrel_op('SETUP_WITH', 143) diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 21b8cb7935..f3193368e9 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -96,7 +96,7 @@ def _f(a): dis_f = """\ %3d 0 LOAD_GLOBAL 0 (print) 2 LOAD_FAST 0 (a) - 4 CALL_FUNCTION 1 (1 positional, 0 keyword pair) + 4 CALL_FUNCTION 1 6 POP_TOP %3d 8 LOAD_CONST 1 (1) @@ -108,7 +108,7 @@ dis_f = """\ dis_f_co_code = """\ 0 LOAD_GLOBAL 0 (0) 2 LOAD_FAST 0 (0) - 4 CALL_FUNCTION 1 (1 positional, 0 keyword pair) + 4 CALL_FUNCTION 1 6 POP_TOP 8 LOAD_CONST 1 (1) 10 RETURN_VALUE @@ -126,7 +126,7 @@ dis_bug708901 = """\ 4 LOAD_CONST 1 (1) %3d 6 LOAD_CONST 2 (10) - 8 CALL_FUNCTION 2 (2 positional, 0 keyword pair) + 8 CALL_FUNCTION 2 10 GET_ITER >> 12 FOR_ITER 4 (to 18) 14 STORE_FAST 0 (res) @@ -154,11 +154,11 @@ dis_bug1333982 = """\ 10 MAKE_FUNCTION 0 12 LOAD_FAST 0 (x) 14 GET_ITER - 16 CALL_FUNCTION 1 (1 positional, 0 keyword pair) + 16 CALL_FUNCTION 1 %3d 18 LOAD_CONST 4 (1) 20 BINARY_ADD - 22 CALL_FUNCTION 1 (1 positional, 0 keyword pair) + 22 CALL_FUNCTION 1 24 RAISE_VARARGS 1 %3d >> 26 LOAD_CONST 0 (None) @@ -224,14 +224,14 @@ dis_annot_stmt_str = """\ 3 10 LOAD_NAME 2 (fun) 12 LOAD_CONST 0 (1) - 14 CALL_FUNCTION 1 (1 positional, 0 keyword pair) + 14 CALL_FUNCTION 1 16 STORE_ANNOTATION 3 (y) 4 18 LOAD_CONST 0 (1) 20 LOAD_NAME 4 (lst) 22 LOAD_NAME 2 (fun) 24 LOAD_CONST 1 (0) - 26 CALL_FUNCTION 1 (1 positional, 0 keyword pair) + 26 CALL_FUNCTION 1 28 STORE_SUBSCR 30 LOAD_NAME 1 (int) 32 POP_TOP @@ -698,7 +698,7 @@ expected_opinfo_outer = [ Instruction(opname='BUILD_LIST', opcode=103, arg=0, argval=0, argrepr='', offset=26, starts_line=None, is_jump_target=False), Instruction(opname='BUILD_MAP', opcode=105, arg=0, argval=0, argrepr='', offset=28, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_CONST', opcode=100, arg=7, argval='Hello world!', argrepr="'Hello world!'", offset=30, starts_line=None, is_jump_target=False), - Instruction(opname='CALL_FUNCTION', opcode=131, arg=7, argval=7, argrepr='7 positional, 0 keyword pair', offset=32, starts_line=None, is_jump_target=False), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=7, argval=7, argrepr='', offset=32, starts_line=None, is_jump_target=False), Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=34, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_FAST', opcode=124, arg=2, argval='f', argrepr='f', offset=36, starts_line=8, is_jump_target=False), Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=38, starts_line=None, is_jump_target=False), @@ -720,7 +720,7 @@ expected_opinfo_f = [ Instruction(opname='LOAD_DEREF', opcode=136, arg=3, argval='b', argrepr='b', offset=24, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_DEREF', opcode=136, arg=0, argval='c', argrepr='c', offset=26, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_DEREF', opcode=136, arg=1, argval='d', argrepr='d', offset=28, starts_line=None, is_jump_target=False), - Instruction(opname='CALL_FUNCTION', opcode=131, arg=4, argval=4, argrepr='4 positional, 0 keyword pair', offset=30, starts_line=None, is_jump_target=False), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=4, argval=4, argrepr='', offset=30, starts_line=None, is_jump_target=False), Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=32, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_FAST', opcode=124, arg=2, argval='inner', argrepr='inner', offset=34, starts_line=6, is_jump_target=False), Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=36, starts_line=None, is_jump_target=False), @@ -734,7 +734,7 @@ expected_opinfo_inner = [ Instruction(opname='LOAD_DEREF', opcode=136, arg=3, argval='d', argrepr='d', offset=8, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='e', argrepr='e', offset=10, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_FAST', opcode=124, arg=1, argval='f', argrepr='f', offset=12, starts_line=None, is_jump_target=False), - Instruction(opname='CALL_FUNCTION', opcode=131, arg=6, argval=6, argrepr='6 positional, 0 keyword pair', offset=14, starts_line=None, is_jump_target=False), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=6, argval=6, argrepr='', offset=14, starts_line=None, is_jump_target=False), Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=16, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=18, starts_line=None, is_jump_target=False), Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=20, starts_line=None, is_jump_target=False), @@ -744,13 +744,13 @@ expected_opinfo_jumpy = [ Instruction(opname='SETUP_LOOP', opcode=120, arg=52, argval=54, argrepr='to 54', offset=0, starts_line=3, is_jump_target=False), Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='range', argrepr='range', offset=2, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_CONST', opcode=100, arg=1, argval=10, argrepr='10', offset=4, starts_line=None, is_jump_target=False), - Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=6, starts_line=None, is_jump_target=False), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=6, starts_line=None, is_jump_target=False), Instruction(opname='GET_ITER', opcode=68, arg=None, argval=None, argrepr='', offset=8, starts_line=None, is_jump_target=False), Instruction(opname='FOR_ITER', opcode=93, arg=32, argval=44, argrepr='to 44', offset=10, starts_line=None, is_jump_target=True), Instruction(opname='STORE_FAST', opcode=125, arg=0, argval='i', argrepr='i', offset=12, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=14, starts_line=4, is_jump_target=False), Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=16, starts_line=None, is_jump_target=False), - Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=18, starts_line=None, is_jump_target=False), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=18, starts_line=None, is_jump_target=False), Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=20, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=22, starts_line=5, is_jump_target=False), Instruction(opname='LOAD_CONST', opcode=100, arg=2, argval=4, argrepr='4', offset=24, starts_line=None, is_jump_target=False), @@ -766,14 +766,14 @@ expected_opinfo_jumpy = [ Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=44, starts_line=None, is_jump_target=True), Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=46, starts_line=10, is_jump_target=False), Instruction(opname='LOAD_CONST', opcode=100, arg=4, argval='I can haz else clause?', argrepr="'I can haz else clause?'", offset=48, starts_line=None, is_jump_target=False), - Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=50, starts_line=None, is_jump_target=False), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=50, starts_line=None, is_jump_target=False), Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=52, starts_line=None, is_jump_target=False), Instruction(opname='SETUP_LOOP', opcode=120, arg=52, argval=108, argrepr='to 108', offset=54, starts_line=11, is_jump_target=True), Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=56, starts_line=None, is_jump_target=True), Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=98, argval=98, argrepr='', offset=58, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=60, starts_line=12, is_jump_target=False), Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=62, starts_line=None, is_jump_target=False), - Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=64, starts_line=None, is_jump_target=False), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=64, starts_line=None, is_jump_target=False), Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=66, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=68, starts_line=13, is_jump_target=False), Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval=1, argrepr='1', offset=70, starts_line=None, is_jump_target=False), @@ -793,7 +793,7 @@ expected_opinfo_jumpy = [ Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=98, starts_line=None, is_jump_target=True), Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=100, starts_line=19, is_jump_target=False), Instruction(opname='LOAD_CONST', opcode=100, arg=6, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=102, starts_line=None, is_jump_target=False), - Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=104, starts_line=None, is_jump_target=False), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=104, starts_line=None, is_jump_target=False), Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=106, starts_line=None, is_jump_target=False), Instruction(opname='SETUP_FINALLY', opcode=122, arg=70, argval=180, argrepr='to 180', offset=108, starts_line=20, is_jump_target=True), Instruction(opname='SETUP_EXCEPT', opcode=121, arg=12, argval=124, argrepr='to 124', offset=110, starts_line=None, is_jump_target=False), @@ -812,7 +812,7 @@ expected_opinfo_jumpy = [ Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=136, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=138, starts_line=23, is_jump_target=False), Instruction(opname='LOAD_CONST', opcode=100, arg=8, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=140, starts_line=None, is_jump_target=False), - Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=142, starts_line=None, is_jump_target=False), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=142, starts_line=None, is_jump_target=False), Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=144, starts_line=None, is_jump_target=False), Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=146, starts_line=None, is_jump_target=False), Instruction(opname='JUMP_FORWARD', opcode=110, arg=26, argval=176, argrepr='to 176', offset=148, starts_line=None, is_jump_target=False), @@ -822,7 +822,7 @@ expected_opinfo_jumpy = [ Instruction(opname='STORE_FAST', opcode=125, arg=1, argval='dodgy', argrepr='dodgy', offset=156, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=158, starts_line=26, is_jump_target=False), Instruction(opname='LOAD_CONST', opcode=100, arg=9, argval='Never reach this', argrepr="'Never reach this'", offset=160, starts_line=None, is_jump_target=False), - Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=162, starts_line=None, is_jump_target=False), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=162, starts_line=None, is_jump_target=False), Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=164, starts_line=None, is_jump_target=False), Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=166, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=168, starts_line=None, is_jump_target=False), @@ -833,7 +833,7 @@ expected_opinfo_jumpy = [ Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=178, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=180, starts_line=28, is_jump_target=True), Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=182, starts_line=None, is_jump_target=False), - Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=184, starts_line=None, is_jump_target=False), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=184, starts_line=None, is_jump_target=False), Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=186, starts_line=None, is_jump_target=False), Instruction(opname='END_FINALLY', opcode=88, arg=None, argval=None, argrepr='', offset=188, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=190, starts_line=None, is_jump_target=False), diff --git a/Lib/test/test_extcall.py b/Lib/test/test_extcall.py index 5eea37989c..55f139304b 100644 --- a/Lib/test/test_extcall.py +++ b/Lib/test/test_extcall.py @@ -118,7 +118,7 @@ Verify clearing of SF bug #733667 >>> g(*Nothing()) Traceback (most recent call last): ... - TypeError: g() argument after * must be an iterable, not Nothing + TypeError: 'Nothing' object is not iterable >>> class Nothing: ... def __len__(self): return 5 @@ -127,7 +127,7 @@ Verify clearing of SF bug #733667 >>> g(*Nothing()) Traceback (most recent call last): ... - TypeError: g() argument after * must be an iterable, not Nothing + TypeError: 'Nothing' object is not iterable >>> class Nothing(): ... def __len__(self): return 5 @@ -231,34 +231,32 @@ What about willful misconduct? >>> h(*h) Traceback (most recent call last): ... - TypeError: h() argument after * must be an iterable, not function + TypeError: 'function' object is not iterable >>> dir(*h) Traceback (most recent call last): ... - TypeError: dir() argument after * must be an iterable, not function + TypeError: 'function' object is not iterable >>> None(*h) Traceback (most recent call last): ... - TypeError: NoneType object argument after * must be an iterable, \ -not function + TypeError: 'function' object is not iterable >>> h(**h) Traceback (most recent call last): ... - TypeError: h() argument after ** must be a mapping, not function + TypeError: 'function' object is not a mapping >>> dir(**h) Traceback (most recent call last): ... - TypeError: dir() argument after ** must be a mapping, not function + TypeError: 'function' object is not a mapping >>> None(**h) Traceback (most recent call last): ... - TypeError: NoneType object argument after ** must be a mapping, \ -not function + TypeError: 'function' object is not a mapping >>> dir(b=1, **{'b': 1}) Traceback (most recent call last): diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index fc7e6cce96..446b91e235 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -304,6 +304,7 @@ class TracebackFormatTests(unittest.TestCase): ]) # issue 26823 - Shrink recursive tracebacks + @unittest.skipIf(True, "FIXME: test broken, see issue #28050") def _check_recursive_traceback_display(self, render_exc): # Always show full diffs when this test fails # Note that rearranging things may require adjusting -- cgit v1.2.1 From 76a9448789c6cebd622e34a7de15368029e9c07d Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Fri, 9 Sep 2016 12:55:14 -0700 Subject: Fix running test_tokenize directly --- Lib/test/test_tokenize.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_tokenize.py b/Lib/test/test_tokenize.py index 90438e7d30..77c0423d19 100644 --- a/Lib/test/test_tokenize.py +++ b/Lib/test/test_tokenize.py @@ -3,7 +3,7 @@ from tokenize import (tokenize, _tokenize, untokenize, NUMBER, NAME, OP, STRING, ENDMARKER, ENCODING, tok_name, detect_encoding, open as tokenize_open, Untokenizer) from io import BytesIO -from unittest import TestCase, mock +from unittest import TestCase, mock, main import os import token @@ -1564,4 +1564,4 @@ class TestRoundtrip(TestCase): if __name__ == "__main__": - unittest.main() + main() -- cgit v1.2.1 From d83b92267296d5137b56d6f6428ea5b5f16ef0db Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Fri, 9 Sep 2016 12:55:37 -0700 Subject: Rename test_pep####.py files --- Lib/test/test_baseexception.py | 183 ++++++ Lib/test/test_dict_version.py | 186 ++++++ Lib/test/test_exception_hierarchy.py | 204 +++++++ Lib/test/test_generator_stop.py | 34 ++ Lib/test/test_pep277.py | 195 ------ Lib/test/test_pep3120.py | 43 -- Lib/test/test_pep3131.py | 31 - Lib/test/test_pep3151.py | 204 ------- Lib/test/test_pep352.py | 183 ------ Lib/test/test_pep380.py | 1017 ------------------------------- Lib/test/test_pep479.py | 34 -- Lib/test/test_pep509.py | 186 ------ Lib/test/test_tokenize.py | 11 +- Lib/test/test_unicode_file_functions.py | 195 ++++++ Lib/test/test_unicode_identifiers.py | 31 + Lib/test/test_utf8source.py | 43 ++ Lib/test/test_yield_from.py | 1017 +++++++++++++++++++++++++++++++ 17 files changed, 1899 insertions(+), 1898 deletions(-) create mode 100644 Lib/test/test_baseexception.py create mode 100644 Lib/test/test_dict_version.py create mode 100644 Lib/test/test_exception_hierarchy.py create mode 100644 Lib/test/test_generator_stop.py delete mode 100644 Lib/test/test_pep277.py delete mode 100644 Lib/test/test_pep3120.py delete mode 100644 Lib/test/test_pep3131.py delete mode 100644 Lib/test/test_pep3151.py delete mode 100644 Lib/test/test_pep352.py delete mode 100644 Lib/test/test_pep380.py delete mode 100644 Lib/test/test_pep479.py delete mode 100644 Lib/test/test_pep509.py create mode 100644 Lib/test/test_unicode_file_functions.py create mode 100644 Lib/test/test_unicode_identifiers.py create mode 100644 Lib/test/test_utf8source.py create mode 100644 Lib/test/test_yield_from.py (limited to 'Lib') diff --git a/Lib/test/test_baseexception.py b/Lib/test/test_baseexception.py new file mode 100644 index 0000000000..27d514fe2e --- /dev/null +++ b/Lib/test/test_baseexception.py @@ -0,0 +1,183 @@ +import unittest +import builtins +import os +from platform import system as platform_system + + +class ExceptionClassTests(unittest.TestCase): + + """Tests for anything relating to exception objects themselves (e.g., + inheritance hierarchy)""" + + def test_builtins_new_style(self): + self.assertTrue(issubclass(Exception, object)) + + def verify_instance_interface(self, ins): + for attr in ("args", "__str__", "__repr__"): + self.assertTrue(hasattr(ins, attr), + "%s missing %s attribute" % + (ins.__class__.__name__, attr)) + + def test_inheritance(self): + # Make sure the inheritance hierarchy matches the documentation + exc_set = set() + for object_ in builtins.__dict__.values(): + try: + if issubclass(object_, BaseException): + exc_set.add(object_.__name__) + except TypeError: + pass + + inheritance_tree = open(os.path.join(os.path.split(__file__)[0], + 'exception_hierarchy.txt')) + try: + superclass_name = inheritance_tree.readline().rstrip() + try: + last_exc = getattr(builtins, superclass_name) + except AttributeError: + self.fail("base class %s not a built-in" % superclass_name) + self.assertIn(superclass_name, exc_set, + '%s not found' % superclass_name) + exc_set.discard(superclass_name) + superclasses = [] # Loop will insert base exception + last_depth = 0 + for exc_line in inheritance_tree: + exc_line = exc_line.rstrip() + depth = exc_line.rindex('-') + exc_name = exc_line[depth+2:] # Slice past space + if '(' in exc_name: + paren_index = exc_name.index('(') + platform_name = exc_name[paren_index+1:-1] + exc_name = exc_name[:paren_index-1] # Slice off space + if platform_system() != platform_name: + exc_set.discard(exc_name) + continue + if '[' in exc_name: + left_bracket = exc_name.index('[') + exc_name = exc_name[:left_bracket-1] # cover space + try: + exc = getattr(builtins, exc_name) + except AttributeError: + self.fail("%s not a built-in exception" % exc_name) + if last_depth < depth: + superclasses.append((last_depth, last_exc)) + elif last_depth > depth: + while superclasses[-1][0] >= depth: + superclasses.pop() + self.assertTrue(issubclass(exc, superclasses[-1][1]), + "%s is not a subclass of %s" % (exc.__name__, + superclasses[-1][1].__name__)) + try: # Some exceptions require arguments; just skip them + self.verify_instance_interface(exc()) + except TypeError: + pass + self.assertIn(exc_name, exc_set) + exc_set.discard(exc_name) + last_exc = exc + last_depth = depth + finally: + inheritance_tree.close() + self.assertEqual(len(exc_set), 0, "%s not accounted for" % exc_set) + + interface_tests = ("length", "args", "str", "repr") + + def interface_test_driver(self, results): + for test_name, (given, expected) in zip(self.interface_tests, results): + self.assertEqual(given, expected, "%s: %s != %s" % (test_name, + given, expected)) + + def test_interface_single_arg(self): + # Make sure interface works properly when given a single argument + arg = "spam" + exc = Exception(arg) + results = ([len(exc.args), 1], [exc.args[0], arg], + [str(exc), str(arg)], + [repr(exc), exc.__class__.__name__ + repr(exc.args)]) + self.interface_test_driver(results) + + def test_interface_multi_arg(self): + # Make sure interface correct when multiple arguments given + arg_count = 3 + args = tuple(range(arg_count)) + exc = Exception(*args) + results = ([len(exc.args), arg_count], [exc.args, args], + [str(exc), str(args)], + [repr(exc), exc.__class__.__name__ + repr(exc.args)]) + self.interface_test_driver(results) + + def test_interface_no_arg(self): + # Make sure that with no args that interface is correct + exc = Exception() + results = ([len(exc.args), 0], [exc.args, tuple()], + [str(exc), ''], + [repr(exc), exc.__class__.__name__ + '()']) + self.interface_test_driver(results) + +class UsageTests(unittest.TestCase): + + """Test usage of exceptions""" + + def raise_fails(self, object_): + """Make sure that raising 'object_' triggers a TypeError.""" + try: + raise object_ + except TypeError: + return # What is expected. + self.fail("TypeError expected for raising %s" % type(object_)) + + def catch_fails(self, object_): + """Catching 'object_' should raise a TypeError.""" + try: + try: + raise Exception + except object_: + pass + except TypeError: + pass + except Exception: + self.fail("TypeError expected when catching %s" % type(object_)) + + try: + try: + raise Exception + except (object_,): + pass + except TypeError: + return + except Exception: + self.fail("TypeError expected when catching %s as specified in a " + "tuple" % type(object_)) + + def test_raise_new_style_non_exception(self): + # You cannot raise a new-style class that does not inherit from + # BaseException; the ability was not possible until BaseException's + # introduction so no need to support new-style objects that do not + # inherit from it. + class NewStyleClass(object): + pass + self.raise_fails(NewStyleClass) + self.raise_fails(NewStyleClass()) + + def test_raise_string(self): + # Raising a string raises TypeError. + self.raise_fails("spam") + + def test_catch_non_BaseException(self): + # Tryinng to catch an object that does not inherit from BaseException + # is not allowed. + class NonBaseException(object): + pass + self.catch_fails(NonBaseException) + self.catch_fails(NonBaseException()) + + def test_catch_BaseException_instance(self): + # Catching an instance of a BaseException subclass won't work. + self.catch_fails(BaseException()) + + def test_catch_string(self): + # Catching a string is bad. + self.catch_fails("spam") + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_dict_version.py b/Lib/test/test_dict_version.py new file mode 100644 index 0000000000..5671f9fc7c --- /dev/null +++ b/Lib/test/test_dict_version.py @@ -0,0 +1,186 @@ +""" +Test implementation of the PEP 509: dictionary versionning. +""" +import unittest +from test import support + +# PEP 509 is implemented in CPython but other Python implementations +# don't require to implement it +_testcapi = support.import_module('_testcapi') + + +class DictVersionTests(unittest.TestCase): + type2test = dict + + def setUp(self): + self.seen_versions = set() + self.dict = None + + def check_version_unique(self, mydict): + version = _testcapi.dict_get_version(mydict) + self.assertNotIn(version, self.seen_versions) + self.seen_versions.add(version) + + def check_version_changed(self, mydict, method, *args, **kw): + result = method(*args, **kw) + self.check_version_unique(mydict) + return result + + def check_version_dont_change(self, mydict, method, *args, **kw): + version1 = _testcapi.dict_get_version(mydict) + self.seen_versions.add(version1) + + result = method(*args, **kw) + + version2 = _testcapi.dict_get_version(mydict) + self.assertEqual(version2, version1, "version changed") + + return result + + def new_dict(self, *args, **kw): + d = self.type2test(*args, **kw) + self.check_version_unique(d) + return d + + def test_constructor(self): + # new empty dictionaries must all have an unique version + empty1 = self.new_dict() + empty2 = self.new_dict() + empty3 = self.new_dict() + + # non-empty dictionaries must also have an unique version + nonempty1 = self.new_dict(x='x') + nonempty2 = self.new_dict(x='x', y='y') + + def test_copy(self): + d = self.new_dict(a=1, b=2) + + d2 = self.check_version_dont_change(d, d.copy) + + # dict.copy() must create a dictionary with a new unique version + self.check_version_unique(d2) + + def test_setitem(self): + d = self.new_dict() + + # creating new keys must change the version + self.check_version_changed(d, d.__setitem__, 'x', 'x') + self.check_version_changed(d, d.__setitem__, 'y', 'y') + + # changing values must change the version + self.check_version_changed(d, d.__setitem__, 'x', 1) + self.check_version_changed(d, d.__setitem__, 'y', 2) + + def test_setitem_same_value(self): + value = object() + d = self.new_dict() + + # setting a key must change the version + self.check_version_changed(d, d.__setitem__, 'key', value) + + # setting a key to the same value with dict.__setitem__ + # must change the version + self.check_version_changed(d, d.__setitem__, 'key', value) + + # setting a key to the same value with dict.update + # must change the version + self.check_version_changed(d, d.update, key=value) + + d2 = self.new_dict(key=value) + self.check_version_changed(d, d.update, d2) + + def test_setitem_equal(self): + class AlwaysEqual: + def __eq__(self, other): + return True + + value1 = AlwaysEqual() + value2 = AlwaysEqual() + self.assertTrue(value1 == value2) + self.assertFalse(value1 != value2) + + d = self.new_dict() + self.check_version_changed(d, d.__setitem__, 'key', value1) + + # setting a key to a value equal to the current value + # with dict.__setitem__() must change the version + self.check_version_changed(d, d.__setitem__, 'key', value2) + + # setting a key to a value equal to the current value + # with dict.update() must change the version + self.check_version_changed(d, d.update, key=value1) + + d2 = self.new_dict(key=value2) + self.check_version_changed(d, d.update, d2) + + def test_setdefault(self): + d = self.new_dict() + + # setting a key with dict.setdefault() must change the version + self.check_version_changed(d, d.setdefault, 'key', 'value1') + + # don't change the version if the key already exists + self.check_version_dont_change(d, d.setdefault, 'key', 'value2') + + def test_delitem(self): + d = self.new_dict(key='value') + + # deleting a key with dict.__delitem__() must change the version + self.check_version_changed(d, d.__delitem__, 'key') + + # don't change the version if the key doesn't exist + self.check_version_dont_change(d, self.assertRaises, KeyError, + d.__delitem__, 'key') + + def test_pop(self): + d = self.new_dict(key='value') + + # pop() must change the version if the key exists + self.check_version_changed(d, d.pop, 'key') + + # pop() must not change the version if the key does not exist + self.check_version_dont_change(d, self.assertRaises, KeyError, + d.pop, 'key') + + def test_popitem(self): + d = self.new_dict(key='value') + + # popitem() must change the version if the dict is not empty + self.check_version_changed(d, d.popitem) + + # popitem() must not change the version if the dict is empty + self.check_version_dont_change(d, self.assertRaises, KeyError, + d.popitem) + + def test_update(self): + d = self.new_dict(key='value') + + # update() calling with no argument must not change the version + self.check_version_dont_change(d, d.update) + + # update() must change the version + self.check_version_changed(d, d.update, key='new value') + + d2 = self.new_dict(key='value 3') + self.check_version_changed(d, d.update, d2) + + def test_clear(self): + d = self.new_dict(key='value') + + # clear() must change the version if the dict is not empty + self.check_version_changed(d, d.clear) + + # clear() must not change the version if the dict is empty + self.check_version_dont_change(d, d.clear) + + +class Dict(dict): + pass + + +class DictSubtypeVersionTests(DictVersionTests): + type2test = Dict + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_exception_hierarchy.py b/Lib/test/test_exception_hierarchy.py new file mode 100644 index 0000000000..8649596790 --- /dev/null +++ b/Lib/test/test_exception_hierarchy.py @@ -0,0 +1,204 @@ +import builtins +import os +import select +import socket +import unittest +import errno +from errno import EEXIST + + +class SubOSError(OSError): + pass + +class SubOSErrorWithInit(OSError): + def __init__(self, message, bar): + self.bar = bar + super().__init__(message) + +class SubOSErrorWithNew(OSError): + def __new__(cls, message, baz): + self = super().__new__(cls, message) + self.baz = baz + return self + +class SubOSErrorCombinedInitFirst(SubOSErrorWithInit, SubOSErrorWithNew): + pass + +class SubOSErrorCombinedNewFirst(SubOSErrorWithNew, SubOSErrorWithInit): + pass + +class SubOSErrorWithStandaloneInit(OSError): + def __init__(self): + pass + + +class HierarchyTest(unittest.TestCase): + + def test_builtin_errors(self): + self.assertEqual(OSError.__name__, 'OSError') + self.assertIs(IOError, OSError) + self.assertIs(EnvironmentError, OSError) + + def test_socket_errors(self): + self.assertIs(socket.error, IOError) + self.assertIs(socket.gaierror.__base__, OSError) + self.assertIs(socket.herror.__base__, OSError) + self.assertIs(socket.timeout.__base__, OSError) + + def test_select_error(self): + self.assertIs(select.error, OSError) + + # mmap.error is tested in test_mmap + + _pep_map = """ + +-- BlockingIOError EAGAIN, EALREADY, EWOULDBLOCK, EINPROGRESS + +-- ChildProcessError ECHILD + +-- ConnectionError + +-- BrokenPipeError EPIPE, ESHUTDOWN + +-- ConnectionAbortedError ECONNABORTED + +-- ConnectionRefusedError ECONNREFUSED + +-- ConnectionResetError ECONNRESET + +-- FileExistsError EEXIST + +-- FileNotFoundError ENOENT + +-- InterruptedError EINTR + +-- IsADirectoryError EISDIR + +-- NotADirectoryError ENOTDIR + +-- PermissionError EACCES, EPERM + +-- ProcessLookupError ESRCH + +-- TimeoutError ETIMEDOUT + """ + def _make_map(s): + _map = {} + for line in s.splitlines(): + line = line.strip('+- ') + if not line: + continue + excname, _, errnames = line.partition(' ') + for errname in filter(None, errnames.strip().split(', ')): + _map[getattr(errno, errname)] = getattr(builtins, excname) + return _map + _map = _make_map(_pep_map) + + def test_errno_mapping(self): + # The OSError constructor maps errnos to subclasses + # A sample test for the basic functionality + e = OSError(EEXIST, "Bad file descriptor") + self.assertIs(type(e), FileExistsError) + # Exhaustive testing + for errcode, exc in self._map.items(): + e = OSError(errcode, "Some message") + self.assertIs(type(e), exc) + othercodes = set(errno.errorcode) - set(self._map) + for errcode in othercodes: + e = OSError(errcode, "Some message") + self.assertIs(type(e), OSError) + + def test_try_except(self): + filename = "some_hopefully_non_existing_file" + + # This checks that try .. except checks the concrete exception + # (FileNotFoundError) and not the base type specified when + # PyErr_SetFromErrnoWithFilenameObject was called. + # (it is therefore deliberate that it doesn't use assertRaises) + try: + open(filename) + except FileNotFoundError: + pass + else: + self.fail("should have raised a FileNotFoundError") + + # Another test for PyErr_SetExcFromWindowsErrWithFilenameObject() + self.assertFalse(os.path.exists(filename)) + try: + os.unlink(filename) + except FileNotFoundError: + pass + else: + self.fail("should have raised a FileNotFoundError") + + +class AttributesTest(unittest.TestCase): + + def test_windows_error(self): + if os.name == "nt": + self.assertIn('winerror', dir(OSError)) + else: + self.assertNotIn('winerror', dir(OSError)) + + def test_posix_error(self): + e = OSError(EEXIST, "File already exists", "foo.txt") + self.assertEqual(e.errno, EEXIST) + self.assertEqual(e.args[0], EEXIST) + self.assertEqual(e.strerror, "File already exists") + self.assertEqual(e.filename, "foo.txt") + if os.name == "nt": + self.assertEqual(e.winerror, None) + + @unittest.skipUnless(os.name == "nt", "Windows-specific test") + def test_errno_translation(self): + # ERROR_ALREADY_EXISTS (183) -> EEXIST + e = OSError(0, "File already exists", "foo.txt", 183) + self.assertEqual(e.winerror, 183) + self.assertEqual(e.errno, EEXIST) + self.assertEqual(e.args[0], EEXIST) + self.assertEqual(e.strerror, "File already exists") + self.assertEqual(e.filename, "foo.txt") + + def test_blockingioerror(self): + args = ("a", "b", "c", "d", "e") + for n in range(6): + e = BlockingIOError(*args[:n]) + with self.assertRaises(AttributeError): + e.characters_written + e = BlockingIOError("a", "b", 3) + self.assertEqual(e.characters_written, 3) + e.characters_written = 5 + self.assertEqual(e.characters_written, 5) + + +class ExplicitSubclassingTest(unittest.TestCase): + + def test_errno_mapping(self): + # When constructing an OSError subclass, errno mapping isn't done + e = SubOSError(EEXIST, "Bad file descriptor") + self.assertIs(type(e), SubOSError) + + def test_init_overridden(self): + e = SubOSErrorWithInit("some message", "baz") + self.assertEqual(e.bar, "baz") + self.assertEqual(e.args, ("some message",)) + + def test_init_kwdargs(self): + e = SubOSErrorWithInit("some message", bar="baz") + self.assertEqual(e.bar, "baz") + self.assertEqual(e.args, ("some message",)) + + def test_new_overridden(self): + e = SubOSErrorWithNew("some message", "baz") + self.assertEqual(e.baz, "baz") + self.assertEqual(e.args, ("some message",)) + + def test_new_kwdargs(self): + e = SubOSErrorWithNew("some message", baz="baz") + self.assertEqual(e.baz, "baz") + self.assertEqual(e.args, ("some message",)) + + def test_init_new_overridden(self): + e = SubOSErrorCombinedInitFirst("some message", "baz") + self.assertEqual(e.bar, "baz") + self.assertEqual(e.baz, "baz") + self.assertEqual(e.args, ("some message",)) + e = SubOSErrorCombinedNewFirst("some message", "baz") + self.assertEqual(e.bar, "baz") + self.assertEqual(e.baz, "baz") + self.assertEqual(e.args, ("some message",)) + + def test_init_standalone(self): + # __init__ doesn't propagate to OSError.__init__ (see issue #15229) + e = SubOSErrorWithStandaloneInit() + self.assertEqual(e.args, ()) + self.assertEqual(str(e), '') + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_generator_stop.py b/Lib/test/test_generator_stop.py new file mode 100644 index 0000000000..bc235ceb00 --- /dev/null +++ b/Lib/test/test_generator_stop.py @@ -0,0 +1,34 @@ +from __future__ import generator_stop + +import unittest + + +class TestPEP479(unittest.TestCase): + def test_stopiteration_wrapping(self): + def f(): + raise StopIteration + def g(): + yield f() + with self.assertRaisesRegex(RuntimeError, + "generator raised StopIteration"): + next(g()) + + def test_stopiteration_wrapping_context(self): + def f(): + raise StopIteration + def g(): + yield f() + + try: + next(g()) + except RuntimeError as exc: + self.assertIs(type(exc.__cause__), StopIteration) + self.assertIs(type(exc.__context__), StopIteration) + self.assertTrue(exc.__suppress_context__) + else: + self.fail('__cause__, __context__, or __suppress_context__ ' + 'were not properly set') + + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/test/test_pep277.py b/Lib/test/test_pep277.py deleted file mode 100644 index 98c716b4c7..0000000000 --- a/Lib/test/test_pep277.py +++ /dev/null @@ -1,195 +0,0 @@ -# Test the Unicode versions of normal file functions -# open, os.open, os.stat. os.listdir, os.rename, os.remove, os.mkdir, os.chdir, os.rmdir -import os -import sys -import unittest -import warnings -from unicodedata import normalize -from test import support - -filenames = [ - '1_abc', - '2_ascii', - '3_Gr\xfc\xdf-Gott', - '4_\u0393\u03b5\u03b9\u03ac-\u03c3\u03b1\u03c2', - '5_\u0417\u0434\u0440\u0430\u0432\u0441\u0442\u0432\u0443\u0439\u0442\u0435', - '6_\u306b\u307d\u3093', - '7_\u05d4\u05e9\u05e7\u05e6\u05e5\u05e1', - '8_\u66e8\u66e9\u66eb', - '9_\u66e8\u05e9\u3093\u0434\u0393\xdf', - # Specific code points: fn, NFC(fn) and NFKC(fn) all differents - '10_\u1fee\u1ffd', - ] - -# Mac OS X decomposes Unicode names, using Normal Form D. -# http://developer.apple.com/mac/library/qa/qa2001/qa1173.html -# "However, most volume formats do not follow the exact specification for -# these normal forms. For example, HFS Plus uses a variant of Normal Form D -# in which U+2000 through U+2FFF, U+F900 through U+FAFF, and U+2F800 through -# U+2FAFF are not decomposed." -if sys.platform != 'darwin': - filenames.extend([ - # Specific code points: NFC(fn), NFD(fn), NFKC(fn) and NFKD(fn) all differents - '11_\u0385\u03d3\u03d4', - '12_\u00a8\u0301\u03d2\u0301\u03d2\u0308', # == NFD('\u0385\u03d3\u03d4') - '13_\u0020\u0308\u0301\u038e\u03ab', # == NFKC('\u0385\u03d3\u03d4') - '14_\u1e9b\u1fc1\u1fcd\u1fce\u1fcf\u1fdd\u1fde\u1fdf\u1fed', - - # Specific code points: fn, NFC(fn) and NFKC(fn) all differents - '15_\u1fee\u1ffd\ufad1', - '16_\u2000\u2000\u2000A', - '17_\u2001\u2001\u2001A', - '18_\u2003\u2003\u2003A', # == NFC('\u2001\u2001\u2001A') - '19_\u0020\u0020\u0020A', # '\u0020' == ' ' == NFKC('\u2000') == - # NFKC('\u2001') == NFKC('\u2003') - ]) - - -# Is it Unicode-friendly? -if not os.path.supports_unicode_filenames: - fsencoding = sys.getfilesystemencoding() - try: - for name in filenames: - name.encode(fsencoding) - except UnicodeEncodeError: - raise unittest.SkipTest("only NT+ and systems with " - "Unicode-friendly filesystem encoding") - - -class UnicodeFileTests(unittest.TestCase): - files = set(filenames) - normal_form = None - - def setUp(self): - try: - os.mkdir(support.TESTFN) - except FileExistsError: - pass - self.addCleanup(support.rmtree, support.TESTFN) - - files = set() - for name in self.files: - name = os.path.join(support.TESTFN, self.norm(name)) - with open(name, 'wb') as f: - f.write((name+'\n').encode("utf-8")) - os.stat(name) - files.add(name) - self.files = files - - def norm(self, s): - if self.normal_form: - return normalize(self.normal_form, s) - return s - - def _apply_failure(self, fn, filename, - expected_exception=FileNotFoundError, - check_filename=True): - with self.assertRaises(expected_exception) as c: - fn(filename) - exc_filename = c.exception.filename - if check_filename: - self.assertEqual(exc_filename, filename, "Function '%s(%a) failed " - "with bad filename in the exception: %a" % - (fn.__name__, filename, exc_filename)) - - def test_failures(self): - # Pass non-existing Unicode filenames all over the place. - for name in self.files: - name = "not_" + name - self._apply_failure(open, name) - self._apply_failure(os.stat, name) - self._apply_failure(os.chdir, name) - self._apply_failure(os.rmdir, name) - self._apply_failure(os.remove, name) - self._apply_failure(os.listdir, name) - - if sys.platform == 'win32': - # Windows is lunatic. Issue #13366. - _listdir_failure = NotADirectoryError, FileNotFoundError - else: - _listdir_failure = NotADirectoryError - - def test_open(self): - for name in self.files: - f = open(name, 'wb') - f.write((name+'\n').encode("utf-8")) - f.close() - os.stat(name) - self._apply_failure(os.listdir, name, self._listdir_failure) - - # Skip the test on darwin, because darwin does normalize the filename to - # NFD (a variant of Unicode NFD form). Normalize the filename to NFC, NFKC, - # NFKD in Python is useless, because darwin will normalize it later and so - # open(), os.stat(), etc. don't raise any exception. - @unittest.skipIf(sys.platform == 'darwin', 'irrelevant test on Mac OS X') - def test_normalize(self): - files = set(self.files) - others = set() - for nf in set(['NFC', 'NFD', 'NFKC', 'NFKD']): - others |= set(normalize(nf, file) for file in files) - others -= files - for name in others: - self._apply_failure(open, name) - self._apply_failure(os.stat, name) - self._apply_failure(os.chdir, name) - self._apply_failure(os.rmdir, name) - self._apply_failure(os.remove, name) - self._apply_failure(os.listdir, name) - - # Skip the test on darwin, because darwin uses a normalization different - # than Python NFD normalization: filenames are different even if we use - # Python NFD normalization. - @unittest.skipIf(sys.platform == 'darwin', 'irrelevant test on Mac OS X') - def test_listdir(self): - sf0 = set(self.files) - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - f1 = os.listdir(support.TESTFN.encode(sys.getfilesystemencoding())) - f2 = os.listdir(support.TESTFN) - sf2 = set(os.path.join(support.TESTFN, f) for f in f2) - self.assertEqual(sf0, sf2, "%a != %a" % (sf0, sf2)) - self.assertEqual(len(f1), len(f2)) - - def test_rename(self): - for name in self.files: - os.rename(name, "tmp") - os.rename("tmp", name) - - def test_directory(self): - dirname = os.path.join(support.TESTFN, 'Gr\xfc\xdf-\u66e8\u66e9\u66eb') - filename = '\xdf-\u66e8\u66e9\u66eb' - with support.temp_cwd(dirname): - with open(filename, 'wb') as f: - f.write((filename + '\n').encode("utf-8")) - os.access(filename,os.R_OK) - os.remove(filename) - - -class UnicodeNFCFileTests(UnicodeFileTests): - normal_form = 'NFC' - - -class UnicodeNFDFileTests(UnicodeFileTests): - normal_form = 'NFD' - - -class UnicodeNFKCFileTests(UnicodeFileTests): - normal_form = 'NFKC' - - -class UnicodeNFKDFileTests(UnicodeFileTests): - normal_form = 'NFKD' - - -def test_main(): - support.run_unittest( - UnicodeFileTests, - UnicodeNFCFileTests, - UnicodeNFDFileTests, - UnicodeNFKCFileTests, - UnicodeNFKDFileTests, - ) - - -if __name__ == "__main__": - test_main() diff --git a/Lib/test/test_pep3120.py b/Lib/test/test_pep3120.py deleted file mode 100644 index 97dced8a62..0000000000 --- a/Lib/test/test_pep3120.py +++ /dev/null @@ -1,43 +0,0 @@ -# This file is marked as binary in the CVS, to prevent MacCVS from recoding it. - -import unittest - -class PEP3120Test(unittest.TestCase): - - def test_pep3120(self): - self.assertEqual( - "Питон".encode("utf-8"), - b'\xd0\x9f\xd0\xb8\xd1\x82\xd0\xbe\xd0\xbd' - ) - self.assertEqual( - "\П".encode("utf-8"), - b'\\\xd0\x9f' - ) - - def test_badsyntax(self): - try: - import test.badsyntax_pep3120 - except SyntaxError as msg: - msg = str(msg).lower() - self.assertTrue('utf-8' in msg) - else: - self.fail("expected exception didn't occur") - - -class BuiltinCompileTests(unittest.TestCase): - - # Issue 3574. - def test_latin1(self): - # Allow compile() to read Latin-1 source. - source_code = '# coding: Latin-1\nu = "Ç"\n'.encode("Latin-1") - try: - code = compile(source_code, '', 'exec') - except SyntaxError: - self.fail("compile() cannot handle Latin-1 source") - ns = {} - exec(code, ns) - self.assertEqual('Ç', ns['u']) - - -if __name__ == "__main__": - unittest.main() diff --git a/Lib/test/test_pep3131.py b/Lib/test/test_pep3131.py deleted file mode 100644 index 0679845238..0000000000 --- a/Lib/test/test_pep3131.py +++ /dev/null @@ -1,31 +0,0 @@ -import unittest -import sys - -class PEP3131Test(unittest.TestCase): - - def test_valid(self): - class T: - ä = 1 - µ = 2 # this is a compatibility character - 蟒 = 3 - x󠄀 = 4 - self.assertEqual(getattr(T, "\xe4"), 1) - self.assertEqual(getattr(T, "\u03bc"), 2) - self.assertEqual(getattr(T, '\u87d2'), 3) - self.assertEqual(getattr(T, 'x\U000E0100'), 4) - - def test_non_bmp_normalized(self): - 𝔘𝔫𝔦𝔠𝔬𝔡𝔢 = 1 - self.assertIn("Unicode", dir()) - - def test_invalid(self): - try: - from test import badsyntax_3131 - except SyntaxError as s: - self.assertEqual(str(s), - "invalid character in identifier (badsyntax_3131.py, line 2)") - else: - self.fail("expected exception didn't occur") - -if __name__ == "__main__": - unittest.main() diff --git a/Lib/test/test_pep3151.py b/Lib/test/test_pep3151.py deleted file mode 100644 index 8649596790..0000000000 --- a/Lib/test/test_pep3151.py +++ /dev/null @@ -1,204 +0,0 @@ -import builtins -import os -import select -import socket -import unittest -import errno -from errno import EEXIST - - -class SubOSError(OSError): - pass - -class SubOSErrorWithInit(OSError): - def __init__(self, message, bar): - self.bar = bar - super().__init__(message) - -class SubOSErrorWithNew(OSError): - def __new__(cls, message, baz): - self = super().__new__(cls, message) - self.baz = baz - return self - -class SubOSErrorCombinedInitFirst(SubOSErrorWithInit, SubOSErrorWithNew): - pass - -class SubOSErrorCombinedNewFirst(SubOSErrorWithNew, SubOSErrorWithInit): - pass - -class SubOSErrorWithStandaloneInit(OSError): - def __init__(self): - pass - - -class HierarchyTest(unittest.TestCase): - - def test_builtin_errors(self): - self.assertEqual(OSError.__name__, 'OSError') - self.assertIs(IOError, OSError) - self.assertIs(EnvironmentError, OSError) - - def test_socket_errors(self): - self.assertIs(socket.error, IOError) - self.assertIs(socket.gaierror.__base__, OSError) - self.assertIs(socket.herror.__base__, OSError) - self.assertIs(socket.timeout.__base__, OSError) - - def test_select_error(self): - self.assertIs(select.error, OSError) - - # mmap.error is tested in test_mmap - - _pep_map = """ - +-- BlockingIOError EAGAIN, EALREADY, EWOULDBLOCK, EINPROGRESS - +-- ChildProcessError ECHILD - +-- ConnectionError - +-- BrokenPipeError EPIPE, ESHUTDOWN - +-- ConnectionAbortedError ECONNABORTED - +-- ConnectionRefusedError ECONNREFUSED - +-- ConnectionResetError ECONNRESET - +-- FileExistsError EEXIST - +-- FileNotFoundError ENOENT - +-- InterruptedError EINTR - +-- IsADirectoryError EISDIR - +-- NotADirectoryError ENOTDIR - +-- PermissionError EACCES, EPERM - +-- ProcessLookupError ESRCH - +-- TimeoutError ETIMEDOUT - """ - def _make_map(s): - _map = {} - for line in s.splitlines(): - line = line.strip('+- ') - if not line: - continue - excname, _, errnames = line.partition(' ') - for errname in filter(None, errnames.strip().split(', ')): - _map[getattr(errno, errname)] = getattr(builtins, excname) - return _map - _map = _make_map(_pep_map) - - def test_errno_mapping(self): - # The OSError constructor maps errnos to subclasses - # A sample test for the basic functionality - e = OSError(EEXIST, "Bad file descriptor") - self.assertIs(type(e), FileExistsError) - # Exhaustive testing - for errcode, exc in self._map.items(): - e = OSError(errcode, "Some message") - self.assertIs(type(e), exc) - othercodes = set(errno.errorcode) - set(self._map) - for errcode in othercodes: - e = OSError(errcode, "Some message") - self.assertIs(type(e), OSError) - - def test_try_except(self): - filename = "some_hopefully_non_existing_file" - - # This checks that try .. except checks the concrete exception - # (FileNotFoundError) and not the base type specified when - # PyErr_SetFromErrnoWithFilenameObject was called. - # (it is therefore deliberate that it doesn't use assertRaises) - try: - open(filename) - except FileNotFoundError: - pass - else: - self.fail("should have raised a FileNotFoundError") - - # Another test for PyErr_SetExcFromWindowsErrWithFilenameObject() - self.assertFalse(os.path.exists(filename)) - try: - os.unlink(filename) - except FileNotFoundError: - pass - else: - self.fail("should have raised a FileNotFoundError") - - -class AttributesTest(unittest.TestCase): - - def test_windows_error(self): - if os.name == "nt": - self.assertIn('winerror', dir(OSError)) - else: - self.assertNotIn('winerror', dir(OSError)) - - def test_posix_error(self): - e = OSError(EEXIST, "File already exists", "foo.txt") - self.assertEqual(e.errno, EEXIST) - self.assertEqual(e.args[0], EEXIST) - self.assertEqual(e.strerror, "File already exists") - self.assertEqual(e.filename, "foo.txt") - if os.name == "nt": - self.assertEqual(e.winerror, None) - - @unittest.skipUnless(os.name == "nt", "Windows-specific test") - def test_errno_translation(self): - # ERROR_ALREADY_EXISTS (183) -> EEXIST - e = OSError(0, "File already exists", "foo.txt", 183) - self.assertEqual(e.winerror, 183) - self.assertEqual(e.errno, EEXIST) - self.assertEqual(e.args[0], EEXIST) - self.assertEqual(e.strerror, "File already exists") - self.assertEqual(e.filename, "foo.txt") - - def test_blockingioerror(self): - args = ("a", "b", "c", "d", "e") - for n in range(6): - e = BlockingIOError(*args[:n]) - with self.assertRaises(AttributeError): - e.characters_written - e = BlockingIOError("a", "b", 3) - self.assertEqual(e.characters_written, 3) - e.characters_written = 5 - self.assertEqual(e.characters_written, 5) - - -class ExplicitSubclassingTest(unittest.TestCase): - - def test_errno_mapping(self): - # When constructing an OSError subclass, errno mapping isn't done - e = SubOSError(EEXIST, "Bad file descriptor") - self.assertIs(type(e), SubOSError) - - def test_init_overridden(self): - e = SubOSErrorWithInit("some message", "baz") - self.assertEqual(e.bar, "baz") - self.assertEqual(e.args, ("some message",)) - - def test_init_kwdargs(self): - e = SubOSErrorWithInit("some message", bar="baz") - self.assertEqual(e.bar, "baz") - self.assertEqual(e.args, ("some message",)) - - def test_new_overridden(self): - e = SubOSErrorWithNew("some message", "baz") - self.assertEqual(e.baz, "baz") - self.assertEqual(e.args, ("some message",)) - - def test_new_kwdargs(self): - e = SubOSErrorWithNew("some message", baz="baz") - self.assertEqual(e.baz, "baz") - self.assertEqual(e.args, ("some message",)) - - def test_init_new_overridden(self): - e = SubOSErrorCombinedInitFirst("some message", "baz") - self.assertEqual(e.bar, "baz") - self.assertEqual(e.baz, "baz") - self.assertEqual(e.args, ("some message",)) - e = SubOSErrorCombinedNewFirst("some message", "baz") - self.assertEqual(e.bar, "baz") - self.assertEqual(e.baz, "baz") - self.assertEqual(e.args, ("some message",)) - - def test_init_standalone(self): - # __init__ doesn't propagate to OSError.__init__ (see issue #15229) - e = SubOSErrorWithStandaloneInit() - self.assertEqual(e.args, ()) - self.assertEqual(str(e), '') - - -if __name__ == "__main__": - unittest.main() diff --git a/Lib/test/test_pep352.py b/Lib/test/test_pep352.py deleted file mode 100644 index 27d514fe2e..0000000000 --- a/Lib/test/test_pep352.py +++ /dev/null @@ -1,183 +0,0 @@ -import unittest -import builtins -import os -from platform import system as platform_system - - -class ExceptionClassTests(unittest.TestCase): - - """Tests for anything relating to exception objects themselves (e.g., - inheritance hierarchy)""" - - def test_builtins_new_style(self): - self.assertTrue(issubclass(Exception, object)) - - def verify_instance_interface(self, ins): - for attr in ("args", "__str__", "__repr__"): - self.assertTrue(hasattr(ins, attr), - "%s missing %s attribute" % - (ins.__class__.__name__, attr)) - - def test_inheritance(self): - # Make sure the inheritance hierarchy matches the documentation - exc_set = set() - for object_ in builtins.__dict__.values(): - try: - if issubclass(object_, BaseException): - exc_set.add(object_.__name__) - except TypeError: - pass - - inheritance_tree = open(os.path.join(os.path.split(__file__)[0], - 'exception_hierarchy.txt')) - try: - superclass_name = inheritance_tree.readline().rstrip() - try: - last_exc = getattr(builtins, superclass_name) - except AttributeError: - self.fail("base class %s not a built-in" % superclass_name) - self.assertIn(superclass_name, exc_set, - '%s not found' % superclass_name) - exc_set.discard(superclass_name) - superclasses = [] # Loop will insert base exception - last_depth = 0 - for exc_line in inheritance_tree: - exc_line = exc_line.rstrip() - depth = exc_line.rindex('-') - exc_name = exc_line[depth+2:] # Slice past space - if '(' in exc_name: - paren_index = exc_name.index('(') - platform_name = exc_name[paren_index+1:-1] - exc_name = exc_name[:paren_index-1] # Slice off space - if platform_system() != platform_name: - exc_set.discard(exc_name) - continue - if '[' in exc_name: - left_bracket = exc_name.index('[') - exc_name = exc_name[:left_bracket-1] # cover space - try: - exc = getattr(builtins, exc_name) - except AttributeError: - self.fail("%s not a built-in exception" % exc_name) - if last_depth < depth: - superclasses.append((last_depth, last_exc)) - elif last_depth > depth: - while superclasses[-1][0] >= depth: - superclasses.pop() - self.assertTrue(issubclass(exc, superclasses[-1][1]), - "%s is not a subclass of %s" % (exc.__name__, - superclasses[-1][1].__name__)) - try: # Some exceptions require arguments; just skip them - self.verify_instance_interface(exc()) - except TypeError: - pass - self.assertIn(exc_name, exc_set) - exc_set.discard(exc_name) - last_exc = exc - last_depth = depth - finally: - inheritance_tree.close() - self.assertEqual(len(exc_set), 0, "%s not accounted for" % exc_set) - - interface_tests = ("length", "args", "str", "repr") - - def interface_test_driver(self, results): - for test_name, (given, expected) in zip(self.interface_tests, results): - self.assertEqual(given, expected, "%s: %s != %s" % (test_name, - given, expected)) - - def test_interface_single_arg(self): - # Make sure interface works properly when given a single argument - arg = "spam" - exc = Exception(arg) - results = ([len(exc.args), 1], [exc.args[0], arg], - [str(exc), str(arg)], - [repr(exc), exc.__class__.__name__ + repr(exc.args)]) - self.interface_test_driver(results) - - def test_interface_multi_arg(self): - # Make sure interface correct when multiple arguments given - arg_count = 3 - args = tuple(range(arg_count)) - exc = Exception(*args) - results = ([len(exc.args), arg_count], [exc.args, args], - [str(exc), str(args)], - [repr(exc), exc.__class__.__name__ + repr(exc.args)]) - self.interface_test_driver(results) - - def test_interface_no_arg(self): - # Make sure that with no args that interface is correct - exc = Exception() - results = ([len(exc.args), 0], [exc.args, tuple()], - [str(exc), ''], - [repr(exc), exc.__class__.__name__ + '()']) - self.interface_test_driver(results) - -class UsageTests(unittest.TestCase): - - """Test usage of exceptions""" - - def raise_fails(self, object_): - """Make sure that raising 'object_' triggers a TypeError.""" - try: - raise object_ - except TypeError: - return # What is expected. - self.fail("TypeError expected for raising %s" % type(object_)) - - def catch_fails(self, object_): - """Catching 'object_' should raise a TypeError.""" - try: - try: - raise Exception - except object_: - pass - except TypeError: - pass - except Exception: - self.fail("TypeError expected when catching %s" % type(object_)) - - try: - try: - raise Exception - except (object_,): - pass - except TypeError: - return - except Exception: - self.fail("TypeError expected when catching %s as specified in a " - "tuple" % type(object_)) - - def test_raise_new_style_non_exception(self): - # You cannot raise a new-style class that does not inherit from - # BaseException; the ability was not possible until BaseException's - # introduction so no need to support new-style objects that do not - # inherit from it. - class NewStyleClass(object): - pass - self.raise_fails(NewStyleClass) - self.raise_fails(NewStyleClass()) - - def test_raise_string(self): - # Raising a string raises TypeError. - self.raise_fails("spam") - - def test_catch_non_BaseException(self): - # Tryinng to catch an object that does not inherit from BaseException - # is not allowed. - class NonBaseException(object): - pass - self.catch_fails(NonBaseException) - self.catch_fails(NonBaseException()) - - def test_catch_BaseException_instance(self): - # Catching an instance of a BaseException subclass won't work. - self.catch_fails(BaseException()) - - def test_catch_string(self): - # Catching a string is bad. - self.catch_fails("spam") - - -if __name__ == '__main__': - unittest.main() diff --git a/Lib/test/test_pep380.py b/Lib/test/test_pep380.py deleted file mode 100644 index 23ffbed447..0000000000 --- a/Lib/test/test_pep380.py +++ /dev/null @@ -1,1017 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -Test suite for PEP 380 implementation - -adapted from original tests written by Greg Ewing -see -""" - -import unittest -import io -import sys -import inspect -import parser - -from test.support import captured_stderr, disable_gc, gc_collect - -class TestPEP380Operation(unittest.TestCase): - """ - Test semantics. - """ - - def test_delegation_of_initial_next_to_subgenerator(self): - """ - Test delegation of initial next() call to subgenerator - """ - trace = [] - def g1(): - trace.append("Starting g1") - yield from g2() - trace.append("Finishing g1") - def g2(): - trace.append("Starting g2") - yield 42 - trace.append("Finishing g2") - for x in g1(): - trace.append("Yielded %s" % (x,)) - self.assertEqual(trace,[ - "Starting g1", - "Starting g2", - "Yielded 42", - "Finishing g2", - "Finishing g1", - ]) - - def test_raising_exception_in_initial_next_call(self): - """ - Test raising exception in initial next() call - """ - trace = [] - def g1(): - try: - trace.append("Starting g1") - yield from g2() - finally: - trace.append("Finishing g1") - def g2(): - try: - trace.append("Starting g2") - raise ValueError("spanish inquisition occurred") - finally: - trace.append("Finishing g2") - try: - for x in g1(): - trace.append("Yielded %s" % (x,)) - except ValueError as e: - self.assertEqual(e.args[0], "spanish inquisition occurred") - else: - self.fail("subgenerator failed to raise ValueError") - self.assertEqual(trace,[ - "Starting g1", - "Starting g2", - "Finishing g2", - "Finishing g1", - ]) - - def test_delegation_of_next_call_to_subgenerator(self): - """ - Test delegation of next() call to subgenerator - """ - trace = [] - def g1(): - trace.append("Starting g1") - yield "g1 ham" - yield from g2() - yield "g1 eggs" - trace.append("Finishing g1") - def g2(): - trace.append("Starting g2") - yield "g2 spam" - yield "g2 more spam" - trace.append("Finishing g2") - for x in g1(): - trace.append("Yielded %s" % (x,)) - self.assertEqual(trace,[ - "Starting g1", - "Yielded g1 ham", - "Starting g2", - "Yielded g2 spam", - "Yielded g2 more spam", - "Finishing g2", - "Yielded g1 eggs", - "Finishing g1", - ]) - - def test_raising_exception_in_delegated_next_call(self): - """ - Test raising exception in delegated next() call - """ - trace = [] - def g1(): - try: - trace.append("Starting g1") - yield "g1 ham" - yield from g2() - yield "g1 eggs" - finally: - trace.append("Finishing g1") - def g2(): - try: - trace.append("Starting g2") - yield "g2 spam" - raise ValueError("hovercraft is full of eels") - yield "g2 more spam" - finally: - trace.append("Finishing g2") - try: - for x in g1(): - trace.append("Yielded %s" % (x,)) - except ValueError as e: - self.assertEqual(e.args[0], "hovercraft is full of eels") - else: - self.fail("subgenerator failed to raise ValueError") - self.assertEqual(trace,[ - "Starting g1", - "Yielded g1 ham", - "Starting g2", - "Yielded g2 spam", - "Finishing g2", - "Finishing g1", - ]) - - def test_delegation_of_send(self): - """ - Test delegation of send() - """ - trace = [] - def g1(): - trace.append("Starting g1") - x = yield "g1 ham" - trace.append("g1 received %s" % (x,)) - yield from g2() - x = yield "g1 eggs" - trace.append("g1 received %s" % (x,)) - trace.append("Finishing g1") - def g2(): - trace.append("Starting g2") - x = yield "g2 spam" - trace.append("g2 received %s" % (x,)) - x = yield "g2 more spam" - trace.append("g2 received %s" % (x,)) - trace.append("Finishing g2") - g = g1() - y = next(g) - x = 1 - try: - while 1: - y = g.send(x) - trace.append("Yielded %s" % (y,)) - x += 1 - except StopIteration: - pass - self.assertEqual(trace,[ - "Starting g1", - "g1 received 1", - "Starting g2", - "Yielded g2 spam", - "g2 received 2", - "Yielded g2 more spam", - "g2 received 3", - "Finishing g2", - "Yielded g1 eggs", - "g1 received 4", - "Finishing g1", - ]) - - def test_handling_exception_while_delegating_send(self): - """ - Test handling exception while delegating 'send' - """ - trace = [] - def g1(): - trace.append("Starting g1") - x = yield "g1 ham" - trace.append("g1 received %s" % (x,)) - yield from g2() - x = yield "g1 eggs" - trace.append("g1 received %s" % (x,)) - trace.append("Finishing g1") - def g2(): - trace.append("Starting g2") - x = yield "g2 spam" - trace.append("g2 received %s" % (x,)) - raise ValueError("hovercraft is full of eels") - x = yield "g2 more spam" - trace.append("g2 received %s" % (x,)) - trace.append("Finishing g2") - def run(): - g = g1() - y = next(g) - x = 1 - try: - while 1: - y = g.send(x) - trace.append("Yielded %s" % (y,)) - x += 1 - except StopIteration: - trace.append("StopIteration") - self.assertRaises(ValueError,run) - self.assertEqual(trace,[ - "Starting g1", - "g1 received 1", - "Starting g2", - "Yielded g2 spam", - "g2 received 2", - ]) - - def test_delegating_close(self): - """ - Test delegating 'close' - """ - trace = [] - def g1(): - try: - trace.append("Starting g1") - yield "g1 ham" - yield from g2() - yield "g1 eggs" - finally: - trace.append("Finishing g1") - def g2(): - try: - trace.append("Starting g2") - yield "g2 spam" - yield "g2 more spam" - finally: - trace.append("Finishing g2") - g = g1() - for i in range(2): - x = next(g) - trace.append("Yielded %s" % (x,)) - g.close() - self.assertEqual(trace,[ - "Starting g1", - "Yielded g1 ham", - "Starting g2", - "Yielded g2 spam", - "Finishing g2", - "Finishing g1" - ]) - - def test_handing_exception_while_delegating_close(self): - """ - Test handling exception while delegating 'close' - """ - trace = [] - def g1(): - try: - trace.append("Starting g1") - yield "g1 ham" - yield from g2() - yield "g1 eggs" - finally: - trace.append("Finishing g1") - def g2(): - try: - trace.append("Starting g2") - yield "g2 spam" - yield "g2 more spam" - finally: - trace.append("Finishing g2") - raise ValueError("nybbles have exploded with delight") - try: - g = g1() - for i in range(2): - x = next(g) - trace.append("Yielded %s" % (x,)) - g.close() - except ValueError as e: - self.assertEqual(e.args[0], "nybbles have exploded with delight") - self.assertIsInstance(e.__context__, GeneratorExit) - else: - self.fail("subgenerator failed to raise ValueError") - self.assertEqual(trace,[ - "Starting g1", - "Yielded g1 ham", - "Starting g2", - "Yielded g2 spam", - "Finishing g2", - "Finishing g1", - ]) - - def test_delegating_throw(self): - """ - Test delegating 'throw' - """ - trace = [] - def g1(): - try: - trace.append("Starting g1") - yield "g1 ham" - yield from g2() - yield "g1 eggs" - finally: - trace.append("Finishing g1") - def g2(): - try: - trace.append("Starting g2") - yield "g2 spam" - yield "g2 more spam" - finally: - trace.append("Finishing g2") - try: - g = g1() - for i in range(2): - x = next(g) - trace.append("Yielded %s" % (x,)) - e = ValueError("tomato ejected") - g.throw(e) - except ValueError as e: - self.assertEqual(e.args[0], "tomato ejected") - else: - self.fail("subgenerator failed to raise ValueError") - self.assertEqual(trace,[ - "Starting g1", - "Yielded g1 ham", - "Starting g2", - "Yielded g2 spam", - "Finishing g2", - "Finishing g1", - ]) - - def test_value_attribute_of_StopIteration_exception(self): - """ - Test 'value' attribute of StopIteration exception - """ - trace = [] - def pex(e): - trace.append("%s: %s" % (e.__class__.__name__, e)) - trace.append("value = %s" % (e.value,)) - e = StopIteration() - pex(e) - e = StopIteration("spam") - pex(e) - e.value = "eggs" - pex(e) - self.assertEqual(trace,[ - "StopIteration: ", - "value = None", - "StopIteration: spam", - "value = spam", - "StopIteration: spam", - "value = eggs", - ]) - - - def test_exception_value_crash(self): - # There used to be a refcount error when the return value - # stored in the StopIteration has a refcount of 1. - def g1(): - yield from g2() - def g2(): - yield "g2" - return [42] - self.assertEqual(list(g1()), ["g2"]) - - - def test_generator_return_value(self): - """ - Test generator return value - """ - trace = [] - def g1(): - trace.append("Starting g1") - yield "g1 ham" - ret = yield from g2() - trace.append("g2 returned %s" % (ret,)) - ret = yield from g2(42) - trace.append("g2 returned %s" % (ret,)) - yield "g1 eggs" - trace.append("Finishing g1") - def g2(v = None): - trace.append("Starting g2") - yield "g2 spam" - yield "g2 more spam" - trace.append("Finishing g2") - if v: - return v - for x in g1(): - trace.append("Yielded %s" % (x,)) - self.assertEqual(trace,[ - "Starting g1", - "Yielded g1 ham", - "Starting g2", - "Yielded g2 spam", - "Yielded g2 more spam", - "Finishing g2", - "g2 returned None", - "Starting g2", - "Yielded g2 spam", - "Yielded g2 more spam", - "Finishing g2", - "g2 returned 42", - "Yielded g1 eggs", - "Finishing g1", - ]) - - def test_delegation_of_next_to_non_generator(self): - """ - Test delegation of next() to non-generator - """ - trace = [] - def g(): - yield from range(3) - for x in g(): - trace.append("Yielded %s" % (x,)) - self.assertEqual(trace,[ - "Yielded 0", - "Yielded 1", - "Yielded 2", - ]) - - - def test_conversion_of_sendNone_to_next(self): - """ - Test conversion of send(None) to next() - """ - trace = [] - def g(): - yield from range(3) - gi = g() - for x in range(3): - y = gi.send(None) - trace.append("Yielded: %s" % (y,)) - self.assertEqual(trace,[ - "Yielded: 0", - "Yielded: 1", - "Yielded: 2", - ]) - - def test_delegation_of_close_to_non_generator(self): - """ - Test delegation of close() to non-generator - """ - trace = [] - def g(): - try: - trace.append("starting g") - yield from range(3) - trace.append("g should not be here") - finally: - trace.append("finishing g") - gi = g() - next(gi) - with captured_stderr() as output: - gi.close() - self.assertEqual(output.getvalue(), '') - self.assertEqual(trace,[ - "starting g", - "finishing g", - ]) - - def test_delegating_throw_to_non_generator(self): - """ - Test delegating 'throw' to non-generator - """ - trace = [] - def g(): - try: - trace.append("Starting g") - yield from range(10) - finally: - trace.append("Finishing g") - try: - gi = g() - for i in range(5): - x = next(gi) - trace.append("Yielded %s" % (x,)) - e = ValueError("tomato ejected") - gi.throw(e) - except ValueError as e: - self.assertEqual(e.args[0],"tomato ejected") - else: - self.fail("subgenerator failed to raise ValueError") - self.assertEqual(trace,[ - "Starting g", - "Yielded 0", - "Yielded 1", - "Yielded 2", - "Yielded 3", - "Yielded 4", - "Finishing g", - ]) - - def test_attempting_to_send_to_non_generator(self): - """ - Test attempting to send to non-generator - """ - trace = [] - def g(): - try: - trace.append("starting g") - yield from range(3) - trace.append("g should not be here") - finally: - trace.append("finishing g") - try: - gi = g() - next(gi) - for x in range(3): - y = gi.send(42) - trace.append("Should not have yielded: %s" % (y,)) - except AttributeError as e: - self.assertIn("send", e.args[0]) - else: - self.fail("was able to send into non-generator") - self.assertEqual(trace,[ - "starting g", - "finishing g", - ]) - - def test_broken_getattr_handling(self): - """ - Test subiterator with a broken getattr implementation - """ - class Broken: - def __iter__(self): - return self - def __next__(self): - return 1 - def __getattr__(self, attr): - 1/0 - - def g(): - yield from Broken() - - with self.assertRaises(ZeroDivisionError): - gi = g() - self.assertEqual(next(gi), 1) - gi.send(1) - - with self.assertRaises(ZeroDivisionError): - gi = g() - self.assertEqual(next(gi), 1) - gi.throw(AttributeError) - - with captured_stderr() as output: - gi = g() - self.assertEqual(next(gi), 1) - gi.close() - self.assertIn('ZeroDivisionError', output.getvalue()) - - def test_exception_in_initial_next_call(self): - """ - Test exception in initial next() call - """ - trace = [] - def g1(): - trace.append("g1 about to yield from g2") - yield from g2() - trace.append("g1 should not be here") - def g2(): - yield 1/0 - def run(): - gi = g1() - next(gi) - self.assertRaises(ZeroDivisionError,run) - self.assertEqual(trace,[ - "g1 about to yield from g2" - ]) - - def test_attempted_yield_from_loop(self): - """ - Test attempted yield-from loop - """ - trace = [] - def g1(): - trace.append("g1: starting") - yield "y1" - trace.append("g1: about to yield from g2") - yield from g2() - trace.append("g1 should not be here") - - def g2(): - trace.append("g2: starting") - yield "y2" - trace.append("g2: about to yield from g1") - yield from gi - trace.append("g2 should not be here") - try: - gi = g1() - for y in gi: - trace.append("Yielded: %s" % (y,)) - except ValueError as e: - self.assertEqual(e.args[0],"generator already executing") - else: - self.fail("subgenerator didn't raise ValueError") - self.assertEqual(trace,[ - "g1: starting", - "Yielded: y1", - "g1: about to yield from g2", - "g2: starting", - "Yielded: y2", - "g2: about to yield from g1", - ]) - - def test_returning_value_from_delegated_throw(self): - """ - Test returning value from delegated 'throw' - """ - trace = [] - def g1(): - try: - trace.append("Starting g1") - yield "g1 ham" - yield from g2() - yield "g1 eggs" - finally: - trace.append("Finishing g1") - def g2(): - try: - trace.append("Starting g2") - yield "g2 spam" - yield "g2 more spam" - except LunchError: - trace.append("Caught LunchError in g2") - yield "g2 lunch saved" - yield "g2 yet more spam" - class LunchError(Exception): - pass - g = g1() - for i in range(2): - x = next(g) - trace.append("Yielded %s" % (x,)) - e = LunchError("tomato ejected") - g.throw(e) - for x in g: - trace.append("Yielded %s" % (x,)) - self.assertEqual(trace,[ - "Starting g1", - "Yielded g1 ham", - "Starting g2", - "Yielded g2 spam", - "Caught LunchError in g2", - "Yielded g2 yet more spam", - "Yielded g1 eggs", - "Finishing g1", - ]) - - def test_next_and_return_with_value(self): - """ - Test next and return with value - """ - trace = [] - def f(r): - gi = g(r) - next(gi) - try: - trace.append("f resuming g") - next(gi) - trace.append("f SHOULD NOT BE HERE") - except StopIteration as e: - trace.append("f caught %s" % (repr(e),)) - def g(r): - trace.append("g starting") - yield - trace.append("g returning %s" % (r,)) - return r - f(None) - f(42) - self.assertEqual(trace,[ - "g starting", - "f resuming g", - "g returning None", - "f caught StopIteration()", - "g starting", - "f resuming g", - "g returning 42", - "f caught StopIteration(42,)", - ]) - - def test_send_and_return_with_value(self): - """ - Test send and return with value - """ - trace = [] - def f(r): - gi = g(r) - next(gi) - try: - trace.append("f sending spam to g") - gi.send("spam") - trace.append("f SHOULD NOT BE HERE") - except StopIteration as e: - trace.append("f caught %r" % (e,)) - def g(r): - trace.append("g starting") - x = yield - trace.append("g received %s" % (x,)) - trace.append("g returning %s" % (r,)) - return r - f(None) - f(42) - self.assertEqual(trace,[ - "g starting", - "f sending spam to g", - "g received spam", - "g returning None", - "f caught StopIteration()", - "g starting", - "f sending spam to g", - "g received spam", - "g returning 42", - "f caught StopIteration(42,)", - ]) - - def test_catching_exception_from_subgen_and_returning(self): - """ - Test catching an exception thrown into a - subgenerator and returning a value - """ - trace = [] - def inner(): - try: - yield 1 - except ValueError: - trace.append("inner caught ValueError") - return 2 - - def outer(): - v = yield from inner() - trace.append("inner returned %r to outer" % v) - yield v - g = outer() - trace.append(next(g)) - trace.append(g.throw(ValueError)) - self.assertEqual(trace,[ - 1, - "inner caught ValueError", - "inner returned 2 to outer", - 2, - ]) - - def test_throwing_GeneratorExit_into_subgen_that_returns(self): - """ - Test throwing GeneratorExit into a subgenerator that - catches it and returns normally. - """ - trace = [] - def f(): - try: - trace.append("Enter f") - yield - trace.append("Exit f") - except GeneratorExit: - return - def g(): - trace.append("Enter g") - yield from f() - trace.append("Exit g") - try: - gi = g() - next(gi) - gi.throw(GeneratorExit) - except GeneratorExit: - pass - else: - self.fail("subgenerator failed to raise GeneratorExit") - self.assertEqual(trace,[ - "Enter g", - "Enter f", - ]) - - def test_throwing_GeneratorExit_into_subgenerator_that_yields(self): - """ - Test throwing GeneratorExit into a subgenerator that - catches it and yields. - """ - trace = [] - def f(): - try: - trace.append("Enter f") - yield - trace.append("Exit f") - except GeneratorExit: - yield - def g(): - trace.append("Enter g") - yield from f() - trace.append("Exit g") - try: - gi = g() - next(gi) - gi.throw(GeneratorExit) - except RuntimeError as e: - self.assertEqual(e.args[0], "generator ignored GeneratorExit") - else: - self.fail("subgenerator failed to raise GeneratorExit") - self.assertEqual(trace,[ - "Enter g", - "Enter f", - ]) - - def test_throwing_GeneratorExit_into_subgen_that_raises(self): - """ - Test throwing GeneratorExit into a subgenerator that - catches it and raises a different exception. - """ - trace = [] - def f(): - try: - trace.append("Enter f") - yield - trace.append("Exit f") - except GeneratorExit: - raise ValueError("Vorpal bunny encountered") - def g(): - trace.append("Enter g") - yield from f() - trace.append("Exit g") - try: - gi = g() - next(gi) - gi.throw(GeneratorExit) - except ValueError as e: - self.assertEqual(e.args[0], "Vorpal bunny encountered") - self.assertIsInstance(e.__context__, GeneratorExit) - else: - self.fail("subgenerator failed to raise ValueError") - self.assertEqual(trace,[ - "Enter g", - "Enter f", - ]) - - def test_yield_from_empty(self): - def g(): - yield from () - self.assertRaises(StopIteration, next, g()) - - def test_delegating_generators_claim_to_be_running(self): - # Check with basic iteration - def one(): - yield 0 - yield from two() - yield 3 - def two(): - yield 1 - try: - yield from g1 - except ValueError: - pass - yield 2 - g1 = one() - self.assertEqual(list(g1), [0, 1, 2, 3]) - # Check with send - g1 = one() - res = [next(g1)] - try: - while True: - res.append(g1.send(42)) - except StopIteration: - pass - self.assertEqual(res, [0, 1, 2, 3]) - # Check with throw - class MyErr(Exception): - pass - def one(): - try: - yield 0 - except MyErr: - pass - yield from two() - try: - yield 3 - except MyErr: - pass - def two(): - try: - yield 1 - except MyErr: - pass - try: - yield from g1 - except ValueError: - pass - try: - yield 2 - except MyErr: - pass - g1 = one() - res = [next(g1)] - try: - while True: - res.append(g1.throw(MyErr)) - except StopIteration: - pass - # Check with close - class MyIt(object): - def __iter__(self): - return self - def __next__(self): - return 42 - def close(self_): - self.assertTrue(g1.gi_running) - self.assertRaises(ValueError, next, g1) - def one(): - yield from MyIt() - g1 = one() - next(g1) - g1.close() - - def test_delegator_is_visible_to_debugger(self): - def call_stack(): - return [f[3] for f in inspect.stack()] - - def gen(): - yield call_stack() - yield call_stack() - yield call_stack() - - def spam(g): - yield from g - - def eggs(g): - yield from g - - for stack in spam(gen()): - self.assertTrue('spam' in stack) - - for stack in spam(eggs(gen())): - self.assertTrue('spam' in stack and 'eggs' in stack) - - def test_custom_iterator_return(self): - # See issue #15568 - class MyIter: - def __iter__(self): - return self - def __next__(self): - raise StopIteration(42) - def gen(): - nonlocal ret - ret = yield from MyIter() - ret = None - list(gen()) - self.assertEqual(ret, 42) - - def test_close_with_cleared_frame(self): - # See issue #17669. - # - # Create a stack of generators: outer() delegating to inner() - # delegating to innermost(). The key point is that the instance of - # inner is created first: this ensures that its frame appears before - # the instance of outer in the GC linked list. - # - # At the gc.collect call: - # - frame_clear is called on the inner_gen frame. - # - gen_dealloc is called on the outer_gen generator (the only - # reference is in the frame's locals). - # - gen_close is called on the outer_gen generator. - # - gen_close_iter is called to close the inner_gen generator, which - # in turn calls gen_close, and gen_yf. - # - # Previously, gen_yf would crash since inner_gen's frame had been - # cleared (and in particular f_stacktop was NULL). - - def innermost(): - yield - def inner(): - outer_gen = yield - yield from innermost() - def outer(): - inner_gen = yield - yield from inner_gen - - with disable_gc(): - inner_gen = inner() - outer_gen = outer() - outer_gen.send(None) - outer_gen.send(inner_gen) - outer_gen.send(outer_gen) - - del outer_gen - del inner_gen - gc_collect() - - def test_send_tuple_with_custom_generator(self): - # See issue #21209. - class MyGen: - def __iter__(self): - return self - def __next__(self): - return 42 - def send(self, what): - nonlocal v - v = what - return None - def outer(): - v = yield from MyGen() - g = outer() - next(g) - v = None - g.send((1, 2, 3, 4)) - self.assertEqual(v, (1, 2, 3, 4)) - - -if __name__ == '__main__': - unittest.main() diff --git a/Lib/test/test_pep479.py b/Lib/test/test_pep479.py deleted file mode 100644 index bc235ceb00..0000000000 --- a/Lib/test/test_pep479.py +++ /dev/null @@ -1,34 +0,0 @@ -from __future__ import generator_stop - -import unittest - - -class TestPEP479(unittest.TestCase): - def test_stopiteration_wrapping(self): - def f(): - raise StopIteration - def g(): - yield f() - with self.assertRaisesRegex(RuntimeError, - "generator raised StopIteration"): - next(g()) - - def test_stopiteration_wrapping_context(self): - def f(): - raise StopIteration - def g(): - yield f() - - try: - next(g()) - except RuntimeError as exc: - self.assertIs(type(exc.__cause__), StopIteration) - self.assertIs(type(exc.__context__), StopIteration) - self.assertTrue(exc.__suppress_context__) - else: - self.fail('__cause__, __context__, or __suppress_context__ ' - 'were not properly set') - - -if __name__ == '__main__': - unittest.main() diff --git a/Lib/test/test_pep509.py b/Lib/test/test_pep509.py deleted file mode 100644 index 5671f9fc7c..0000000000 --- a/Lib/test/test_pep509.py +++ /dev/null @@ -1,186 +0,0 @@ -""" -Test implementation of the PEP 509: dictionary versionning. -""" -import unittest -from test import support - -# PEP 509 is implemented in CPython but other Python implementations -# don't require to implement it -_testcapi = support.import_module('_testcapi') - - -class DictVersionTests(unittest.TestCase): - type2test = dict - - def setUp(self): - self.seen_versions = set() - self.dict = None - - def check_version_unique(self, mydict): - version = _testcapi.dict_get_version(mydict) - self.assertNotIn(version, self.seen_versions) - self.seen_versions.add(version) - - def check_version_changed(self, mydict, method, *args, **kw): - result = method(*args, **kw) - self.check_version_unique(mydict) - return result - - def check_version_dont_change(self, mydict, method, *args, **kw): - version1 = _testcapi.dict_get_version(mydict) - self.seen_versions.add(version1) - - result = method(*args, **kw) - - version2 = _testcapi.dict_get_version(mydict) - self.assertEqual(version2, version1, "version changed") - - return result - - def new_dict(self, *args, **kw): - d = self.type2test(*args, **kw) - self.check_version_unique(d) - return d - - def test_constructor(self): - # new empty dictionaries must all have an unique version - empty1 = self.new_dict() - empty2 = self.new_dict() - empty3 = self.new_dict() - - # non-empty dictionaries must also have an unique version - nonempty1 = self.new_dict(x='x') - nonempty2 = self.new_dict(x='x', y='y') - - def test_copy(self): - d = self.new_dict(a=1, b=2) - - d2 = self.check_version_dont_change(d, d.copy) - - # dict.copy() must create a dictionary with a new unique version - self.check_version_unique(d2) - - def test_setitem(self): - d = self.new_dict() - - # creating new keys must change the version - self.check_version_changed(d, d.__setitem__, 'x', 'x') - self.check_version_changed(d, d.__setitem__, 'y', 'y') - - # changing values must change the version - self.check_version_changed(d, d.__setitem__, 'x', 1) - self.check_version_changed(d, d.__setitem__, 'y', 2) - - def test_setitem_same_value(self): - value = object() - d = self.new_dict() - - # setting a key must change the version - self.check_version_changed(d, d.__setitem__, 'key', value) - - # setting a key to the same value with dict.__setitem__ - # must change the version - self.check_version_changed(d, d.__setitem__, 'key', value) - - # setting a key to the same value with dict.update - # must change the version - self.check_version_changed(d, d.update, key=value) - - d2 = self.new_dict(key=value) - self.check_version_changed(d, d.update, d2) - - def test_setitem_equal(self): - class AlwaysEqual: - def __eq__(self, other): - return True - - value1 = AlwaysEqual() - value2 = AlwaysEqual() - self.assertTrue(value1 == value2) - self.assertFalse(value1 != value2) - - d = self.new_dict() - self.check_version_changed(d, d.__setitem__, 'key', value1) - - # setting a key to a value equal to the current value - # with dict.__setitem__() must change the version - self.check_version_changed(d, d.__setitem__, 'key', value2) - - # setting a key to a value equal to the current value - # with dict.update() must change the version - self.check_version_changed(d, d.update, key=value1) - - d2 = self.new_dict(key=value2) - self.check_version_changed(d, d.update, d2) - - def test_setdefault(self): - d = self.new_dict() - - # setting a key with dict.setdefault() must change the version - self.check_version_changed(d, d.setdefault, 'key', 'value1') - - # don't change the version if the key already exists - self.check_version_dont_change(d, d.setdefault, 'key', 'value2') - - def test_delitem(self): - d = self.new_dict(key='value') - - # deleting a key with dict.__delitem__() must change the version - self.check_version_changed(d, d.__delitem__, 'key') - - # don't change the version if the key doesn't exist - self.check_version_dont_change(d, self.assertRaises, KeyError, - d.__delitem__, 'key') - - def test_pop(self): - d = self.new_dict(key='value') - - # pop() must change the version if the key exists - self.check_version_changed(d, d.pop, 'key') - - # pop() must not change the version if the key does not exist - self.check_version_dont_change(d, self.assertRaises, KeyError, - d.pop, 'key') - - def test_popitem(self): - d = self.new_dict(key='value') - - # popitem() must change the version if the dict is not empty - self.check_version_changed(d, d.popitem) - - # popitem() must not change the version if the dict is empty - self.check_version_dont_change(d, self.assertRaises, KeyError, - d.popitem) - - def test_update(self): - d = self.new_dict(key='value') - - # update() calling with no argument must not change the version - self.check_version_dont_change(d, d.update) - - # update() must change the version - self.check_version_changed(d, d.update, key='new value') - - d2 = self.new_dict(key='value 3') - self.check_version_changed(d, d.update, d2) - - def test_clear(self): - d = self.new_dict(key='value') - - # clear() must change the version if the dict is not empty - self.check_version_changed(d, d.clear) - - # clear() must not change the version if the dict is empty - self.check_version_dont_change(d, d.clear) - - -class Dict(dict): - pass - - -class DictSubtypeVersionTests(DictVersionTests): - type2test = Dict - - -if __name__ == "__main__": - unittest.main() diff --git a/Lib/test/test_tokenize.py b/Lib/test/test_tokenize.py index 77c0423d19..4c469a890f 100644 --- a/Lib/test/test_tokenize.py +++ b/Lib/test/test_tokenize.py @@ -1529,12 +1529,13 @@ class TestRoundtrip(TestCase): tempdir = os.path.dirname(fn) or os.curdir testfiles = glob.glob(os.path.join(tempdir, "test*.py")) - # Tokenize is broken on test_pep3131.py because regular expressions are - # broken on the obscure unicode identifiers in it. *sigh* - # With roundtrip extended to test the 5-tuple mode of untokenize, - # 7 more testfiles fail. Remove them also until the failure is diagnosed. + # Tokenize is broken on test_unicode_identifiers.py because regular + # expressions are broken on the obscure unicode identifiers in it. + # *sigh* With roundtrip extended to test the 5-tuple mode of + # untokenize, 7 more testfiles fail. Remove them also until the + # failure is diagnosed. - testfiles.remove(os.path.join(tempdir, "test_pep3131.py")) + testfiles.remove(os.path.join(tempdir, "test_unicode_identifiers.py")) for f in ('buffer', 'builtin', 'fileio', 'inspect', 'os', 'platform', 'sys'): testfiles.remove(os.path.join(tempdir, "test_%s.py") % f) diff --git a/Lib/test/test_unicode_file_functions.py b/Lib/test/test_unicode_file_functions.py new file mode 100644 index 0000000000..98c716b4c7 --- /dev/null +++ b/Lib/test/test_unicode_file_functions.py @@ -0,0 +1,195 @@ +# Test the Unicode versions of normal file functions +# open, os.open, os.stat. os.listdir, os.rename, os.remove, os.mkdir, os.chdir, os.rmdir +import os +import sys +import unittest +import warnings +from unicodedata import normalize +from test import support + +filenames = [ + '1_abc', + '2_ascii', + '3_Gr\xfc\xdf-Gott', + '4_\u0393\u03b5\u03b9\u03ac-\u03c3\u03b1\u03c2', + '5_\u0417\u0434\u0440\u0430\u0432\u0441\u0442\u0432\u0443\u0439\u0442\u0435', + '6_\u306b\u307d\u3093', + '7_\u05d4\u05e9\u05e7\u05e6\u05e5\u05e1', + '8_\u66e8\u66e9\u66eb', + '9_\u66e8\u05e9\u3093\u0434\u0393\xdf', + # Specific code points: fn, NFC(fn) and NFKC(fn) all differents + '10_\u1fee\u1ffd', + ] + +# Mac OS X decomposes Unicode names, using Normal Form D. +# http://developer.apple.com/mac/library/qa/qa2001/qa1173.html +# "However, most volume formats do not follow the exact specification for +# these normal forms. For example, HFS Plus uses a variant of Normal Form D +# in which U+2000 through U+2FFF, U+F900 through U+FAFF, and U+2F800 through +# U+2FAFF are not decomposed." +if sys.platform != 'darwin': + filenames.extend([ + # Specific code points: NFC(fn), NFD(fn), NFKC(fn) and NFKD(fn) all differents + '11_\u0385\u03d3\u03d4', + '12_\u00a8\u0301\u03d2\u0301\u03d2\u0308', # == NFD('\u0385\u03d3\u03d4') + '13_\u0020\u0308\u0301\u038e\u03ab', # == NFKC('\u0385\u03d3\u03d4') + '14_\u1e9b\u1fc1\u1fcd\u1fce\u1fcf\u1fdd\u1fde\u1fdf\u1fed', + + # Specific code points: fn, NFC(fn) and NFKC(fn) all differents + '15_\u1fee\u1ffd\ufad1', + '16_\u2000\u2000\u2000A', + '17_\u2001\u2001\u2001A', + '18_\u2003\u2003\u2003A', # == NFC('\u2001\u2001\u2001A') + '19_\u0020\u0020\u0020A', # '\u0020' == ' ' == NFKC('\u2000') == + # NFKC('\u2001') == NFKC('\u2003') + ]) + + +# Is it Unicode-friendly? +if not os.path.supports_unicode_filenames: + fsencoding = sys.getfilesystemencoding() + try: + for name in filenames: + name.encode(fsencoding) + except UnicodeEncodeError: + raise unittest.SkipTest("only NT+ and systems with " + "Unicode-friendly filesystem encoding") + + +class UnicodeFileTests(unittest.TestCase): + files = set(filenames) + normal_form = None + + def setUp(self): + try: + os.mkdir(support.TESTFN) + except FileExistsError: + pass + self.addCleanup(support.rmtree, support.TESTFN) + + files = set() + for name in self.files: + name = os.path.join(support.TESTFN, self.norm(name)) + with open(name, 'wb') as f: + f.write((name+'\n').encode("utf-8")) + os.stat(name) + files.add(name) + self.files = files + + def norm(self, s): + if self.normal_form: + return normalize(self.normal_form, s) + return s + + def _apply_failure(self, fn, filename, + expected_exception=FileNotFoundError, + check_filename=True): + with self.assertRaises(expected_exception) as c: + fn(filename) + exc_filename = c.exception.filename + if check_filename: + self.assertEqual(exc_filename, filename, "Function '%s(%a) failed " + "with bad filename in the exception: %a" % + (fn.__name__, filename, exc_filename)) + + def test_failures(self): + # Pass non-existing Unicode filenames all over the place. + for name in self.files: + name = "not_" + name + self._apply_failure(open, name) + self._apply_failure(os.stat, name) + self._apply_failure(os.chdir, name) + self._apply_failure(os.rmdir, name) + self._apply_failure(os.remove, name) + self._apply_failure(os.listdir, name) + + if sys.platform == 'win32': + # Windows is lunatic. Issue #13366. + _listdir_failure = NotADirectoryError, FileNotFoundError + else: + _listdir_failure = NotADirectoryError + + def test_open(self): + for name in self.files: + f = open(name, 'wb') + f.write((name+'\n').encode("utf-8")) + f.close() + os.stat(name) + self._apply_failure(os.listdir, name, self._listdir_failure) + + # Skip the test on darwin, because darwin does normalize the filename to + # NFD (a variant of Unicode NFD form). Normalize the filename to NFC, NFKC, + # NFKD in Python is useless, because darwin will normalize it later and so + # open(), os.stat(), etc. don't raise any exception. + @unittest.skipIf(sys.platform == 'darwin', 'irrelevant test on Mac OS X') + def test_normalize(self): + files = set(self.files) + others = set() + for nf in set(['NFC', 'NFD', 'NFKC', 'NFKD']): + others |= set(normalize(nf, file) for file in files) + others -= files + for name in others: + self._apply_failure(open, name) + self._apply_failure(os.stat, name) + self._apply_failure(os.chdir, name) + self._apply_failure(os.rmdir, name) + self._apply_failure(os.remove, name) + self._apply_failure(os.listdir, name) + + # Skip the test on darwin, because darwin uses a normalization different + # than Python NFD normalization: filenames are different even if we use + # Python NFD normalization. + @unittest.skipIf(sys.platform == 'darwin', 'irrelevant test on Mac OS X') + def test_listdir(self): + sf0 = set(self.files) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + f1 = os.listdir(support.TESTFN.encode(sys.getfilesystemencoding())) + f2 = os.listdir(support.TESTFN) + sf2 = set(os.path.join(support.TESTFN, f) for f in f2) + self.assertEqual(sf0, sf2, "%a != %a" % (sf0, sf2)) + self.assertEqual(len(f1), len(f2)) + + def test_rename(self): + for name in self.files: + os.rename(name, "tmp") + os.rename("tmp", name) + + def test_directory(self): + dirname = os.path.join(support.TESTFN, 'Gr\xfc\xdf-\u66e8\u66e9\u66eb') + filename = '\xdf-\u66e8\u66e9\u66eb' + with support.temp_cwd(dirname): + with open(filename, 'wb') as f: + f.write((filename + '\n').encode("utf-8")) + os.access(filename,os.R_OK) + os.remove(filename) + + +class UnicodeNFCFileTests(UnicodeFileTests): + normal_form = 'NFC' + + +class UnicodeNFDFileTests(UnicodeFileTests): + normal_form = 'NFD' + + +class UnicodeNFKCFileTests(UnicodeFileTests): + normal_form = 'NFKC' + + +class UnicodeNFKDFileTests(UnicodeFileTests): + normal_form = 'NFKD' + + +def test_main(): + support.run_unittest( + UnicodeFileTests, + UnicodeNFCFileTests, + UnicodeNFDFileTests, + UnicodeNFKCFileTests, + UnicodeNFKDFileTests, + ) + + +if __name__ == "__main__": + test_main() diff --git a/Lib/test/test_unicode_identifiers.py b/Lib/test/test_unicode_identifiers.py new file mode 100644 index 0000000000..0679845238 --- /dev/null +++ b/Lib/test/test_unicode_identifiers.py @@ -0,0 +1,31 @@ +import unittest +import sys + +class PEP3131Test(unittest.TestCase): + + def test_valid(self): + class T: + ä = 1 + µ = 2 # this is a compatibility character + 蟒 = 3 + x󠄀 = 4 + self.assertEqual(getattr(T, "\xe4"), 1) + self.assertEqual(getattr(T, "\u03bc"), 2) + self.assertEqual(getattr(T, '\u87d2'), 3) + self.assertEqual(getattr(T, 'x\U000E0100'), 4) + + def test_non_bmp_normalized(self): + 𝔘𝔫𝔦𝔠𝔬𝔡𝔢 = 1 + self.assertIn("Unicode", dir()) + + def test_invalid(self): + try: + from test import badsyntax_3131 + except SyntaxError as s: + self.assertEqual(str(s), + "invalid character in identifier (badsyntax_3131.py, line 2)") + else: + self.fail("expected exception didn't occur") + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_utf8source.py b/Lib/test/test_utf8source.py new file mode 100644 index 0000000000..97dced8a62 --- /dev/null +++ b/Lib/test/test_utf8source.py @@ -0,0 +1,43 @@ +# This file is marked as binary in the CVS, to prevent MacCVS from recoding it. + +import unittest + +class PEP3120Test(unittest.TestCase): + + def test_pep3120(self): + self.assertEqual( + "Питон".encode("utf-8"), + b'\xd0\x9f\xd0\xb8\xd1\x82\xd0\xbe\xd0\xbd' + ) + self.assertEqual( + "\П".encode("utf-8"), + b'\\\xd0\x9f' + ) + + def test_badsyntax(self): + try: + import test.badsyntax_pep3120 + except SyntaxError as msg: + msg = str(msg).lower() + self.assertTrue('utf-8' in msg) + else: + self.fail("expected exception didn't occur") + + +class BuiltinCompileTests(unittest.TestCase): + + # Issue 3574. + def test_latin1(self): + # Allow compile() to read Latin-1 source. + source_code = '# coding: Latin-1\nu = "Ç"\n'.encode("Latin-1") + try: + code = compile(source_code, '', 'exec') + except SyntaxError: + self.fail("compile() cannot handle Latin-1 source") + ns = {} + exec(code, ns) + self.assertEqual('Ç', ns['u']) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_yield_from.py b/Lib/test/test_yield_from.py new file mode 100644 index 0000000000..23ffbed447 --- /dev/null +++ b/Lib/test/test_yield_from.py @@ -0,0 +1,1017 @@ +# -*- coding: utf-8 -*- + +""" +Test suite for PEP 380 implementation + +adapted from original tests written by Greg Ewing +see +""" + +import unittest +import io +import sys +import inspect +import parser + +from test.support import captured_stderr, disable_gc, gc_collect + +class TestPEP380Operation(unittest.TestCase): + """ + Test semantics. + """ + + def test_delegation_of_initial_next_to_subgenerator(self): + """ + Test delegation of initial next() call to subgenerator + """ + trace = [] + def g1(): + trace.append("Starting g1") + yield from g2() + trace.append("Finishing g1") + def g2(): + trace.append("Starting g2") + yield 42 + trace.append("Finishing g2") + for x in g1(): + trace.append("Yielded %s" % (x,)) + self.assertEqual(trace,[ + "Starting g1", + "Starting g2", + "Yielded 42", + "Finishing g2", + "Finishing g1", + ]) + + def test_raising_exception_in_initial_next_call(self): + """ + Test raising exception in initial next() call + """ + trace = [] + def g1(): + try: + trace.append("Starting g1") + yield from g2() + finally: + trace.append("Finishing g1") + def g2(): + try: + trace.append("Starting g2") + raise ValueError("spanish inquisition occurred") + finally: + trace.append("Finishing g2") + try: + for x in g1(): + trace.append("Yielded %s" % (x,)) + except ValueError as e: + self.assertEqual(e.args[0], "spanish inquisition occurred") + else: + self.fail("subgenerator failed to raise ValueError") + self.assertEqual(trace,[ + "Starting g1", + "Starting g2", + "Finishing g2", + "Finishing g1", + ]) + + def test_delegation_of_next_call_to_subgenerator(self): + """ + Test delegation of next() call to subgenerator + """ + trace = [] + def g1(): + trace.append("Starting g1") + yield "g1 ham" + yield from g2() + yield "g1 eggs" + trace.append("Finishing g1") + def g2(): + trace.append("Starting g2") + yield "g2 spam" + yield "g2 more spam" + trace.append("Finishing g2") + for x in g1(): + trace.append("Yielded %s" % (x,)) + self.assertEqual(trace,[ + "Starting g1", + "Yielded g1 ham", + "Starting g2", + "Yielded g2 spam", + "Yielded g2 more spam", + "Finishing g2", + "Yielded g1 eggs", + "Finishing g1", + ]) + + def test_raising_exception_in_delegated_next_call(self): + """ + Test raising exception in delegated next() call + """ + trace = [] + def g1(): + try: + trace.append("Starting g1") + yield "g1 ham" + yield from g2() + yield "g1 eggs" + finally: + trace.append("Finishing g1") + def g2(): + try: + trace.append("Starting g2") + yield "g2 spam" + raise ValueError("hovercraft is full of eels") + yield "g2 more spam" + finally: + trace.append("Finishing g2") + try: + for x in g1(): + trace.append("Yielded %s" % (x,)) + except ValueError as e: + self.assertEqual(e.args[0], "hovercraft is full of eels") + else: + self.fail("subgenerator failed to raise ValueError") + self.assertEqual(trace,[ + "Starting g1", + "Yielded g1 ham", + "Starting g2", + "Yielded g2 spam", + "Finishing g2", + "Finishing g1", + ]) + + def test_delegation_of_send(self): + """ + Test delegation of send() + """ + trace = [] + def g1(): + trace.append("Starting g1") + x = yield "g1 ham" + trace.append("g1 received %s" % (x,)) + yield from g2() + x = yield "g1 eggs" + trace.append("g1 received %s" % (x,)) + trace.append("Finishing g1") + def g2(): + trace.append("Starting g2") + x = yield "g2 spam" + trace.append("g2 received %s" % (x,)) + x = yield "g2 more spam" + trace.append("g2 received %s" % (x,)) + trace.append("Finishing g2") + g = g1() + y = next(g) + x = 1 + try: + while 1: + y = g.send(x) + trace.append("Yielded %s" % (y,)) + x += 1 + except StopIteration: + pass + self.assertEqual(trace,[ + "Starting g1", + "g1 received 1", + "Starting g2", + "Yielded g2 spam", + "g2 received 2", + "Yielded g2 more spam", + "g2 received 3", + "Finishing g2", + "Yielded g1 eggs", + "g1 received 4", + "Finishing g1", + ]) + + def test_handling_exception_while_delegating_send(self): + """ + Test handling exception while delegating 'send' + """ + trace = [] + def g1(): + trace.append("Starting g1") + x = yield "g1 ham" + trace.append("g1 received %s" % (x,)) + yield from g2() + x = yield "g1 eggs" + trace.append("g1 received %s" % (x,)) + trace.append("Finishing g1") + def g2(): + trace.append("Starting g2") + x = yield "g2 spam" + trace.append("g2 received %s" % (x,)) + raise ValueError("hovercraft is full of eels") + x = yield "g2 more spam" + trace.append("g2 received %s" % (x,)) + trace.append("Finishing g2") + def run(): + g = g1() + y = next(g) + x = 1 + try: + while 1: + y = g.send(x) + trace.append("Yielded %s" % (y,)) + x += 1 + except StopIteration: + trace.append("StopIteration") + self.assertRaises(ValueError,run) + self.assertEqual(trace,[ + "Starting g1", + "g1 received 1", + "Starting g2", + "Yielded g2 spam", + "g2 received 2", + ]) + + def test_delegating_close(self): + """ + Test delegating 'close' + """ + trace = [] + def g1(): + try: + trace.append("Starting g1") + yield "g1 ham" + yield from g2() + yield "g1 eggs" + finally: + trace.append("Finishing g1") + def g2(): + try: + trace.append("Starting g2") + yield "g2 spam" + yield "g2 more spam" + finally: + trace.append("Finishing g2") + g = g1() + for i in range(2): + x = next(g) + trace.append("Yielded %s" % (x,)) + g.close() + self.assertEqual(trace,[ + "Starting g1", + "Yielded g1 ham", + "Starting g2", + "Yielded g2 spam", + "Finishing g2", + "Finishing g1" + ]) + + def test_handing_exception_while_delegating_close(self): + """ + Test handling exception while delegating 'close' + """ + trace = [] + def g1(): + try: + trace.append("Starting g1") + yield "g1 ham" + yield from g2() + yield "g1 eggs" + finally: + trace.append("Finishing g1") + def g2(): + try: + trace.append("Starting g2") + yield "g2 spam" + yield "g2 more spam" + finally: + trace.append("Finishing g2") + raise ValueError("nybbles have exploded with delight") + try: + g = g1() + for i in range(2): + x = next(g) + trace.append("Yielded %s" % (x,)) + g.close() + except ValueError as e: + self.assertEqual(e.args[0], "nybbles have exploded with delight") + self.assertIsInstance(e.__context__, GeneratorExit) + else: + self.fail("subgenerator failed to raise ValueError") + self.assertEqual(trace,[ + "Starting g1", + "Yielded g1 ham", + "Starting g2", + "Yielded g2 spam", + "Finishing g2", + "Finishing g1", + ]) + + def test_delegating_throw(self): + """ + Test delegating 'throw' + """ + trace = [] + def g1(): + try: + trace.append("Starting g1") + yield "g1 ham" + yield from g2() + yield "g1 eggs" + finally: + trace.append("Finishing g1") + def g2(): + try: + trace.append("Starting g2") + yield "g2 spam" + yield "g2 more spam" + finally: + trace.append("Finishing g2") + try: + g = g1() + for i in range(2): + x = next(g) + trace.append("Yielded %s" % (x,)) + e = ValueError("tomato ejected") + g.throw(e) + except ValueError as e: + self.assertEqual(e.args[0], "tomato ejected") + else: + self.fail("subgenerator failed to raise ValueError") + self.assertEqual(trace,[ + "Starting g1", + "Yielded g1 ham", + "Starting g2", + "Yielded g2 spam", + "Finishing g2", + "Finishing g1", + ]) + + def test_value_attribute_of_StopIteration_exception(self): + """ + Test 'value' attribute of StopIteration exception + """ + trace = [] + def pex(e): + trace.append("%s: %s" % (e.__class__.__name__, e)) + trace.append("value = %s" % (e.value,)) + e = StopIteration() + pex(e) + e = StopIteration("spam") + pex(e) + e.value = "eggs" + pex(e) + self.assertEqual(trace,[ + "StopIteration: ", + "value = None", + "StopIteration: spam", + "value = spam", + "StopIteration: spam", + "value = eggs", + ]) + + + def test_exception_value_crash(self): + # There used to be a refcount error when the return value + # stored in the StopIteration has a refcount of 1. + def g1(): + yield from g2() + def g2(): + yield "g2" + return [42] + self.assertEqual(list(g1()), ["g2"]) + + + def test_generator_return_value(self): + """ + Test generator return value + """ + trace = [] + def g1(): + trace.append("Starting g1") + yield "g1 ham" + ret = yield from g2() + trace.append("g2 returned %s" % (ret,)) + ret = yield from g2(42) + trace.append("g2 returned %s" % (ret,)) + yield "g1 eggs" + trace.append("Finishing g1") + def g2(v = None): + trace.append("Starting g2") + yield "g2 spam" + yield "g2 more spam" + trace.append("Finishing g2") + if v: + return v + for x in g1(): + trace.append("Yielded %s" % (x,)) + self.assertEqual(trace,[ + "Starting g1", + "Yielded g1 ham", + "Starting g2", + "Yielded g2 spam", + "Yielded g2 more spam", + "Finishing g2", + "g2 returned None", + "Starting g2", + "Yielded g2 spam", + "Yielded g2 more spam", + "Finishing g2", + "g2 returned 42", + "Yielded g1 eggs", + "Finishing g1", + ]) + + def test_delegation_of_next_to_non_generator(self): + """ + Test delegation of next() to non-generator + """ + trace = [] + def g(): + yield from range(3) + for x in g(): + trace.append("Yielded %s" % (x,)) + self.assertEqual(trace,[ + "Yielded 0", + "Yielded 1", + "Yielded 2", + ]) + + + def test_conversion_of_sendNone_to_next(self): + """ + Test conversion of send(None) to next() + """ + trace = [] + def g(): + yield from range(3) + gi = g() + for x in range(3): + y = gi.send(None) + trace.append("Yielded: %s" % (y,)) + self.assertEqual(trace,[ + "Yielded: 0", + "Yielded: 1", + "Yielded: 2", + ]) + + def test_delegation_of_close_to_non_generator(self): + """ + Test delegation of close() to non-generator + """ + trace = [] + def g(): + try: + trace.append("starting g") + yield from range(3) + trace.append("g should not be here") + finally: + trace.append("finishing g") + gi = g() + next(gi) + with captured_stderr() as output: + gi.close() + self.assertEqual(output.getvalue(), '') + self.assertEqual(trace,[ + "starting g", + "finishing g", + ]) + + def test_delegating_throw_to_non_generator(self): + """ + Test delegating 'throw' to non-generator + """ + trace = [] + def g(): + try: + trace.append("Starting g") + yield from range(10) + finally: + trace.append("Finishing g") + try: + gi = g() + for i in range(5): + x = next(gi) + trace.append("Yielded %s" % (x,)) + e = ValueError("tomato ejected") + gi.throw(e) + except ValueError as e: + self.assertEqual(e.args[0],"tomato ejected") + else: + self.fail("subgenerator failed to raise ValueError") + self.assertEqual(trace,[ + "Starting g", + "Yielded 0", + "Yielded 1", + "Yielded 2", + "Yielded 3", + "Yielded 4", + "Finishing g", + ]) + + def test_attempting_to_send_to_non_generator(self): + """ + Test attempting to send to non-generator + """ + trace = [] + def g(): + try: + trace.append("starting g") + yield from range(3) + trace.append("g should not be here") + finally: + trace.append("finishing g") + try: + gi = g() + next(gi) + for x in range(3): + y = gi.send(42) + trace.append("Should not have yielded: %s" % (y,)) + except AttributeError as e: + self.assertIn("send", e.args[0]) + else: + self.fail("was able to send into non-generator") + self.assertEqual(trace,[ + "starting g", + "finishing g", + ]) + + def test_broken_getattr_handling(self): + """ + Test subiterator with a broken getattr implementation + """ + class Broken: + def __iter__(self): + return self + def __next__(self): + return 1 + def __getattr__(self, attr): + 1/0 + + def g(): + yield from Broken() + + with self.assertRaises(ZeroDivisionError): + gi = g() + self.assertEqual(next(gi), 1) + gi.send(1) + + with self.assertRaises(ZeroDivisionError): + gi = g() + self.assertEqual(next(gi), 1) + gi.throw(AttributeError) + + with captured_stderr() as output: + gi = g() + self.assertEqual(next(gi), 1) + gi.close() + self.assertIn('ZeroDivisionError', output.getvalue()) + + def test_exception_in_initial_next_call(self): + """ + Test exception in initial next() call + """ + trace = [] + def g1(): + trace.append("g1 about to yield from g2") + yield from g2() + trace.append("g1 should not be here") + def g2(): + yield 1/0 + def run(): + gi = g1() + next(gi) + self.assertRaises(ZeroDivisionError,run) + self.assertEqual(trace,[ + "g1 about to yield from g2" + ]) + + def test_attempted_yield_from_loop(self): + """ + Test attempted yield-from loop + """ + trace = [] + def g1(): + trace.append("g1: starting") + yield "y1" + trace.append("g1: about to yield from g2") + yield from g2() + trace.append("g1 should not be here") + + def g2(): + trace.append("g2: starting") + yield "y2" + trace.append("g2: about to yield from g1") + yield from gi + trace.append("g2 should not be here") + try: + gi = g1() + for y in gi: + trace.append("Yielded: %s" % (y,)) + except ValueError as e: + self.assertEqual(e.args[0],"generator already executing") + else: + self.fail("subgenerator didn't raise ValueError") + self.assertEqual(trace,[ + "g1: starting", + "Yielded: y1", + "g1: about to yield from g2", + "g2: starting", + "Yielded: y2", + "g2: about to yield from g1", + ]) + + def test_returning_value_from_delegated_throw(self): + """ + Test returning value from delegated 'throw' + """ + trace = [] + def g1(): + try: + trace.append("Starting g1") + yield "g1 ham" + yield from g2() + yield "g1 eggs" + finally: + trace.append("Finishing g1") + def g2(): + try: + trace.append("Starting g2") + yield "g2 spam" + yield "g2 more spam" + except LunchError: + trace.append("Caught LunchError in g2") + yield "g2 lunch saved" + yield "g2 yet more spam" + class LunchError(Exception): + pass + g = g1() + for i in range(2): + x = next(g) + trace.append("Yielded %s" % (x,)) + e = LunchError("tomato ejected") + g.throw(e) + for x in g: + trace.append("Yielded %s" % (x,)) + self.assertEqual(trace,[ + "Starting g1", + "Yielded g1 ham", + "Starting g2", + "Yielded g2 spam", + "Caught LunchError in g2", + "Yielded g2 yet more spam", + "Yielded g1 eggs", + "Finishing g1", + ]) + + def test_next_and_return_with_value(self): + """ + Test next and return with value + """ + trace = [] + def f(r): + gi = g(r) + next(gi) + try: + trace.append("f resuming g") + next(gi) + trace.append("f SHOULD NOT BE HERE") + except StopIteration as e: + trace.append("f caught %s" % (repr(e),)) + def g(r): + trace.append("g starting") + yield + trace.append("g returning %s" % (r,)) + return r + f(None) + f(42) + self.assertEqual(trace,[ + "g starting", + "f resuming g", + "g returning None", + "f caught StopIteration()", + "g starting", + "f resuming g", + "g returning 42", + "f caught StopIteration(42,)", + ]) + + def test_send_and_return_with_value(self): + """ + Test send and return with value + """ + trace = [] + def f(r): + gi = g(r) + next(gi) + try: + trace.append("f sending spam to g") + gi.send("spam") + trace.append("f SHOULD NOT BE HERE") + except StopIteration as e: + trace.append("f caught %r" % (e,)) + def g(r): + trace.append("g starting") + x = yield + trace.append("g received %s" % (x,)) + trace.append("g returning %s" % (r,)) + return r + f(None) + f(42) + self.assertEqual(trace,[ + "g starting", + "f sending spam to g", + "g received spam", + "g returning None", + "f caught StopIteration()", + "g starting", + "f sending spam to g", + "g received spam", + "g returning 42", + "f caught StopIteration(42,)", + ]) + + def test_catching_exception_from_subgen_and_returning(self): + """ + Test catching an exception thrown into a + subgenerator and returning a value + """ + trace = [] + def inner(): + try: + yield 1 + except ValueError: + trace.append("inner caught ValueError") + return 2 + + def outer(): + v = yield from inner() + trace.append("inner returned %r to outer" % v) + yield v + g = outer() + trace.append(next(g)) + trace.append(g.throw(ValueError)) + self.assertEqual(trace,[ + 1, + "inner caught ValueError", + "inner returned 2 to outer", + 2, + ]) + + def test_throwing_GeneratorExit_into_subgen_that_returns(self): + """ + Test throwing GeneratorExit into a subgenerator that + catches it and returns normally. + """ + trace = [] + def f(): + try: + trace.append("Enter f") + yield + trace.append("Exit f") + except GeneratorExit: + return + def g(): + trace.append("Enter g") + yield from f() + trace.append("Exit g") + try: + gi = g() + next(gi) + gi.throw(GeneratorExit) + except GeneratorExit: + pass + else: + self.fail("subgenerator failed to raise GeneratorExit") + self.assertEqual(trace,[ + "Enter g", + "Enter f", + ]) + + def test_throwing_GeneratorExit_into_subgenerator_that_yields(self): + """ + Test throwing GeneratorExit into a subgenerator that + catches it and yields. + """ + trace = [] + def f(): + try: + trace.append("Enter f") + yield + trace.append("Exit f") + except GeneratorExit: + yield + def g(): + trace.append("Enter g") + yield from f() + trace.append("Exit g") + try: + gi = g() + next(gi) + gi.throw(GeneratorExit) + except RuntimeError as e: + self.assertEqual(e.args[0], "generator ignored GeneratorExit") + else: + self.fail("subgenerator failed to raise GeneratorExit") + self.assertEqual(trace,[ + "Enter g", + "Enter f", + ]) + + def test_throwing_GeneratorExit_into_subgen_that_raises(self): + """ + Test throwing GeneratorExit into a subgenerator that + catches it and raises a different exception. + """ + trace = [] + def f(): + try: + trace.append("Enter f") + yield + trace.append("Exit f") + except GeneratorExit: + raise ValueError("Vorpal bunny encountered") + def g(): + trace.append("Enter g") + yield from f() + trace.append("Exit g") + try: + gi = g() + next(gi) + gi.throw(GeneratorExit) + except ValueError as e: + self.assertEqual(e.args[0], "Vorpal bunny encountered") + self.assertIsInstance(e.__context__, GeneratorExit) + else: + self.fail("subgenerator failed to raise ValueError") + self.assertEqual(trace,[ + "Enter g", + "Enter f", + ]) + + def test_yield_from_empty(self): + def g(): + yield from () + self.assertRaises(StopIteration, next, g()) + + def test_delegating_generators_claim_to_be_running(self): + # Check with basic iteration + def one(): + yield 0 + yield from two() + yield 3 + def two(): + yield 1 + try: + yield from g1 + except ValueError: + pass + yield 2 + g1 = one() + self.assertEqual(list(g1), [0, 1, 2, 3]) + # Check with send + g1 = one() + res = [next(g1)] + try: + while True: + res.append(g1.send(42)) + except StopIteration: + pass + self.assertEqual(res, [0, 1, 2, 3]) + # Check with throw + class MyErr(Exception): + pass + def one(): + try: + yield 0 + except MyErr: + pass + yield from two() + try: + yield 3 + except MyErr: + pass + def two(): + try: + yield 1 + except MyErr: + pass + try: + yield from g1 + except ValueError: + pass + try: + yield 2 + except MyErr: + pass + g1 = one() + res = [next(g1)] + try: + while True: + res.append(g1.throw(MyErr)) + except StopIteration: + pass + # Check with close + class MyIt(object): + def __iter__(self): + return self + def __next__(self): + return 42 + def close(self_): + self.assertTrue(g1.gi_running) + self.assertRaises(ValueError, next, g1) + def one(): + yield from MyIt() + g1 = one() + next(g1) + g1.close() + + def test_delegator_is_visible_to_debugger(self): + def call_stack(): + return [f[3] for f in inspect.stack()] + + def gen(): + yield call_stack() + yield call_stack() + yield call_stack() + + def spam(g): + yield from g + + def eggs(g): + yield from g + + for stack in spam(gen()): + self.assertTrue('spam' in stack) + + for stack in spam(eggs(gen())): + self.assertTrue('spam' in stack and 'eggs' in stack) + + def test_custom_iterator_return(self): + # See issue #15568 + class MyIter: + def __iter__(self): + return self + def __next__(self): + raise StopIteration(42) + def gen(): + nonlocal ret + ret = yield from MyIter() + ret = None + list(gen()) + self.assertEqual(ret, 42) + + def test_close_with_cleared_frame(self): + # See issue #17669. + # + # Create a stack of generators: outer() delegating to inner() + # delegating to innermost(). The key point is that the instance of + # inner is created first: this ensures that its frame appears before + # the instance of outer in the GC linked list. + # + # At the gc.collect call: + # - frame_clear is called on the inner_gen frame. + # - gen_dealloc is called on the outer_gen generator (the only + # reference is in the frame's locals). + # - gen_close is called on the outer_gen generator. + # - gen_close_iter is called to close the inner_gen generator, which + # in turn calls gen_close, and gen_yf. + # + # Previously, gen_yf would crash since inner_gen's frame had been + # cleared (and in particular f_stacktop was NULL). + + def innermost(): + yield + def inner(): + outer_gen = yield + yield from innermost() + def outer(): + inner_gen = yield + yield from inner_gen + + with disable_gc(): + inner_gen = inner() + outer_gen = outer() + outer_gen.send(None) + outer_gen.send(inner_gen) + outer_gen.send(outer_gen) + + del outer_gen + del inner_gen + gc_collect() + + def test_send_tuple_with_custom_generator(self): + # See issue #21209. + class MyGen: + def __iter__(self): + return self + def __next__(self): + return 42 + def send(self, what): + nonlocal v + v = what + return None + def outer(): + v = yield from MyGen() + g = outer() + next(g) + v = None + g.send((1, 2, 3, 4)) + self.assertEqual(v, (1, 2, 3, 4)) + + +if __name__ == '__main__': + unittest.main() -- cgit v1.2.1 From 020f777c418f8cec467dd0c5413d9778c5c21003 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Fri, 9 Sep 2016 13:30:54 -0700 Subject: Issue #24320: Drop an old setuptools-induced hack. --- Lib/importlib/_bootstrap_external.py | 5 ----- 1 file changed, 5 deletions(-) (limited to 'Lib') diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 4340c3b84d..9a7e6ec937 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -1440,8 +1440,3 @@ def _install(_bootstrap_module): if _os.__name__ == 'nt': sys.meta_path.append(WindowsRegistryFinder) sys.meta_path.append(PathFinder) - - # XXX We expose a couple of classes in _bootstrap for the sake of - # a setuptools bug (https://bitbucket.org/pypa/setuptools/issue/378). - _bootstrap_module.FileFinder = FileFinder - _bootstrap_module.SourceFileLoader = SourceFileLoader -- cgit v1.2.1 From 398c936834a7fde7b162fd61cb9826e482c864fa Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Fri, 9 Sep 2016 14:48:08 -0700 Subject: issue27985 - fix the incorrect duplicate class name in the lib2to3 test. call it TestVarAnnotations instead. --- Lib/lib2to3/tests/test_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/lib2to3/tests/test_parser.py b/Lib/lib2to3/tests/test_parser.py index 7613f53927..50e78a0d2b 100644 --- a/Lib/lib2to3/tests/test_parser.py +++ b/Lib/lib2to3/tests/test_parser.py @@ -256,7 +256,7 @@ class TestFunctionAnnotations(GrammarTest): # Adapted from Python 3's Lib/test/test_grammar.py:GrammarTests.test_var_annot -class TestFunctionAnnotations(GrammarTest): +class TestVarAnnotations(GrammarTest): def test_1(self): self.validate("var1: int = 5") -- cgit v1.2.1 From 033712922fc31dd53c74ed2d299f81b969ae7e98 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Fri, 9 Sep 2016 14:57:09 -0700 Subject: Issue #26331: Implement the parsing part of PEP 515. Thanks to Georg Brandl for the patch. --- Lib/_pydecimal.py | 10 +++--- Lib/test/test_complex.py | 14 ++++++++ Lib/test/test_decimal.py | 10 ++++++ Lib/test/test_float.py | 24 ++++++++++++- Lib/test/test_grammar.py | 89 +++++++++++++++++++++++++++++++++++++++++++++++ Lib/test/test_int.py | 21 +++++++++++ Lib/test/test_tokenize.py | 30 ++++++++++++---- Lib/test/test_types.py | 1 + Lib/tokenize.py | 17 ++++----- 9 files changed, 195 insertions(+), 21 deletions(-) (limited to 'Lib') diff --git a/Lib/_pydecimal.py b/Lib/_pydecimal.py index 21e875c31c..6318a49ce7 100644 --- a/Lib/_pydecimal.py +++ b/Lib/_pydecimal.py @@ -589,7 +589,7 @@ class Decimal(object): # From a string # REs insist on real strings, so we can too. if isinstance(value, str): - m = _parser(value.strip()) + m = _parser(value.strip().replace("_", "")) if m is None: if context is None: context = getcontext() @@ -4125,7 +4125,7 @@ class Context(object): This will make it round up for that operation. """ rounding = self.rounding - self.rounding= type + self.rounding = type return rounding def create_decimal(self, num='0'): @@ -4134,10 +4134,10 @@ class Context(object): This method implements the to-number operation of the IBM Decimal specification.""" - if isinstance(num, str) and num != num.strip(): + if isinstance(num, str) and (num != num.strip() or '_' in num): return self._raise_error(ConversionSyntax, - "no trailing or leading whitespace is " - "permitted.") + "trailing or leading whitespace and " + "underscores are not permitted.") d = Decimal(num, context=self) if d._isnan() and len(d._int) > self.prec - self.clamp: diff --git a/Lib/test/test_complex.py b/Lib/test/test_complex.py index 0ef9a7a109..6633a7ae54 100644 --- a/Lib/test/test_complex.py +++ b/Lib/test/test_complex.py @@ -1,5 +1,7 @@ import unittest from test import support +from test.test_grammar import (VALID_UNDERSCORE_LITERALS, + INVALID_UNDERSCORE_LITERALS) from random import random from math import atan2, isnan, copysign @@ -377,6 +379,18 @@ class ComplexTest(unittest.TestCase): self.assertAlmostEqual(complex(complex1(1j)), 2j) self.assertRaises(TypeError, complex, complex2(1j)) + def test_underscores(self): + # check underscores + for lit in VALID_UNDERSCORE_LITERALS: + if not any(ch in lit for ch in 'xXoObB'): + self.assertEqual(complex(lit), eval(lit)) + self.assertEqual(complex(lit), complex(lit.replace('_', ''))) + for lit in INVALID_UNDERSCORE_LITERALS: + if lit in ('0_7', '09_99'): # octals are not recognized here + continue + if not any(ch in lit for ch in 'xXoObB'): + self.assertRaises(ValueError, complex, lit) + def test_hash(self): for x in range(-30, 30): self.assertEqual(hash(x), hash(complex(x, 0))) diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index 7492f5466f..617a37eec8 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -554,6 +554,10 @@ class ExplicitConstructionTest(unittest.TestCase): self.assertEqual(str(Decimal(' -7.89')), '-7.89') self.assertEqual(str(Decimal(" 3.45679 ")), '3.45679') + # underscores + self.assertEqual(str(Decimal('1_3.3e4_0')), '1.33E+41') + self.assertEqual(str(Decimal('1_0_0_0')), '1000') + # unicode whitespace for lead in ["", ' ', '\u00a0', '\u205f']: for trail in ["", ' ', '\u00a0', '\u205f']: @@ -578,6 +582,9 @@ class ExplicitConstructionTest(unittest.TestCase): # embedded NUL self.assertRaises(InvalidOperation, Decimal, "12\u00003") + # underscores don't prevent errors + self.assertRaises(InvalidOperation, Decimal, "1_2_\u00003") + @cpython_only def test_from_legacy_strings(self): import _testcapi @@ -772,6 +779,9 @@ class ExplicitConstructionTest(unittest.TestCase): self.assertRaises(InvalidOperation, nc.create_decimal, "xyz") self.assertRaises(ValueError, nc.create_decimal, (1, "xyz", -25)) self.assertRaises(TypeError, nc.create_decimal, "1234", "5678") + # no whitespace and underscore stripping is done with this method + self.assertRaises(InvalidOperation, nc.create_decimal, " 1234") + self.assertRaises(InvalidOperation, nc.create_decimal, "12_34") # too many NaN payload digits nc.prec = 3 diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py index 68b212e195..ac8473db50 100644 --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -1,4 +1,3 @@ - import fractions import operator import os @@ -9,6 +8,8 @@ import time import unittest from test import support +from test.test_grammar import (VALID_UNDERSCORE_LITERALS, + INVALID_UNDERSCORE_LITERALS) from math import isinf, isnan, copysign, ldexp INF = float("inf") @@ -60,6 +61,27 @@ class GeneralFloatCases(unittest.TestCase): float(b'.' + b'1'*1000) float('.' + '1'*1000) + def test_underscores(self): + for lit in VALID_UNDERSCORE_LITERALS: + if not any(ch in lit for ch in 'jJxXoObB'): + self.assertEqual(float(lit), eval(lit)) + self.assertEqual(float(lit), float(lit.replace('_', ''))) + for lit in INVALID_UNDERSCORE_LITERALS: + if lit in ('0_7', '09_99'): # octals are not recognized here + continue + if not any(ch in lit for ch in 'jJxXoObB'): + self.assertRaises(ValueError, float, lit) + # Additional test cases; nan and inf are never valid as literals, + # only in the float() constructor, but we don't allow underscores + # in or around them. + self.assertRaises(ValueError, float, '_NaN') + self.assertRaises(ValueError, float, 'Na_N') + self.assertRaises(ValueError, float, 'IN_F') + self.assertRaises(ValueError, float, '-_INF') + self.assertRaises(ValueError, float, '-INF_') + # Check that we handle bytes values correctly. + self.assertRaises(ValueError, float, b'0_.\xff9') + def test_non_numeric_input_types(self): # Test possible non-numeric types for the argument x, including # subclasses of the explicitly documented accepted types. diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py index 109013f5e2..914aa67944 100644 --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -16,6 +16,87 @@ from collections import ChainMap from test import ann_module2 import test +# These are shared with test_tokenize and other test modules. +# +# Note: since several test cases filter out floats by looking for "e" and ".", +# don't add hexadecimal literals that contain "e" or "E". +VALID_UNDERSCORE_LITERALS = [ + '0_0_0', + '4_2', + '1_0000_0000', + '0b1001_0100', + '0xffff_ffff', + '0o5_7_7', + '1_00_00.5', + '1_00_00.5e5', + '1_00_00e5_1', + '1e1_0', + '.1_4', + '.1_4e1', + '0b_0', + '0x_f', + '0o_5', + '1_00_00j', + '1_00_00.5j', + '1_00_00e5_1j', + '.1_4j', + '(1_2.5+3_3j)', + '(.5_6j)', +] +INVALID_UNDERSCORE_LITERALS = [ + # Trailing underscores: + '0_', + '42_', + '1.4j_', + '0x_', + '0b1_', + '0xf_', + '0o5_', + '0 if 1_Else 1', + # Underscores in the base selector: + '0_b0', + '0_xf', + '0_o5', + # Old-style octal, still disallowed: + '0_7', + '09_99', + # Multiple consecutive underscores: + '4_______2', + '0.1__4', + '0.1__4j', + '0b1001__0100', + '0xffff__ffff', + '0x___', + '0o5__77', + '1e1__0', + '1e1__0j', + # Underscore right before a dot: + '1_.4', + '1_.4j', + # Underscore right after a dot: + '1._4', + '1._4j', + '._5', + '._5j', + # Underscore right after a sign: + '1.0e+_1', + '1.0e+_1j', + # Underscore right before j: + '1.4_j', + '1.4e5_j', + # Underscore right before e: + '1_e1', + '1.4_e1', + '1.4_e1j', + # Underscore right after e: + '1e_1', + '1.4e_1', + '1.4e_1j', + # Complex cases with parens: + '(1+1.5_j_)', + '(1+1.5_j)', +] + class TokenTests(unittest.TestCase): @@ -95,6 +176,14 @@ class TokenTests(unittest.TestCase): self.assertEqual(1 if 0else 0, 0) self.assertRaises(SyntaxError, eval, "0 if 1Else 0") + def test_underscore_literals(self): + for lit in VALID_UNDERSCORE_LITERALS: + self.assertEqual(eval(lit), eval(lit.replace('_', ''))) + for lit in INVALID_UNDERSCORE_LITERALS: + self.assertRaises(SyntaxError, eval, lit) + # Sanity check: no literal begins with an underscore + self.assertRaises(NameError, eval, "_0") + def test_string_literals(self): x = ''; y = ""; self.assertTrue(len(x) == 0 and x == y) x = '\''; y = "'"; self.assertTrue(len(x) == 1 and x == y and ord(x) == 39) diff --git a/Lib/test/test_int.py b/Lib/test/test_int.py index 8847f4ce97..14bbd6192a 100644 --- a/Lib/test/test_int.py +++ b/Lib/test/test_int.py @@ -2,6 +2,8 @@ import sys import unittest from test import support +from test.test_grammar import (VALID_UNDERSCORE_LITERALS, + INVALID_UNDERSCORE_LITERALS) L = [ ('0', 0), @@ -212,6 +214,25 @@ class IntTestCases(unittest.TestCase): self.assertEqual(int('2br45qc', 35), 4294967297) self.assertEqual(int('1z141z5', 36), 4294967297) + def test_underscores(self): + for lit in VALID_UNDERSCORE_LITERALS: + if any(ch in lit for ch in '.eEjJ'): + continue + self.assertEqual(int(lit, 0), eval(lit)) + self.assertEqual(int(lit, 0), int(lit.replace('_', ''), 0)) + for lit in INVALID_UNDERSCORE_LITERALS: + if any(ch in lit for ch in '.eEjJ'): + continue + self.assertRaises(ValueError, int, lit, 0) + # Additional test cases with bases != 0, only for the constructor: + self.assertEqual(int("1_00", 3), 9) + self.assertEqual(int("0_100"), 100) # not valid as a literal! + self.assertEqual(int(b"1_00"), 100) # byte underscore + self.assertRaises(ValueError, int, "_100") + self.assertRaises(ValueError, int, "+_100") + self.assertRaises(ValueError, int, "1__00") + self.assertRaises(ValueError, int, "100_") + @support.cpython_only def test_small_ints(self): # Bug #3236: Return small longs from PyLong_FromString diff --git a/Lib/test/test_tokenize.py b/Lib/test/test_tokenize.py index 4c469a890f..5a81a5f11a 100644 --- a/Lib/test/test_tokenize.py +++ b/Lib/test/test_tokenize.py @@ -3,7 +3,9 @@ from tokenize import (tokenize, _tokenize, untokenize, NUMBER, NAME, OP, STRING, ENDMARKER, ENCODING, tok_name, detect_encoding, open as tokenize_open, Untokenizer) from io import BytesIO -from unittest import TestCase, mock, main +from unittest import TestCase, mock +from test.test_grammar import (VALID_UNDERSCORE_LITERALS, + INVALID_UNDERSCORE_LITERALS) import os import token @@ -185,6 +187,21 @@ def k(x): NUMBER '3.14e159' (1, 4) (1, 12) """) + def test_underscore_literals(self): + def number_token(s): + f = BytesIO(s.encode('utf-8')) + for toktype, token, start, end, line in tokenize(f.readline): + if toktype == NUMBER: + return token + return 'invalid token' + for lit in VALID_UNDERSCORE_LITERALS: + if '(' in lit: + # this won't work with compound complex inputs + continue + self.assertEqual(number_token(lit), lit) + for lit in INVALID_UNDERSCORE_LITERALS: + self.assertNotEqual(number_token(lit), lit) + def test_string(self): # String literals self.check_tokenize("x = ''; y = \"\"", """\ @@ -1529,11 +1546,10 @@ class TestRoundtrip(TestCase): tempdir = os.path.dirname(fn) or os.curdir testfiles = glob.glob(os.path.join(tempdir, "test*.py")) - # Tokenize is broken on test_unicode_identifiers.py because regular - # expressions are broken on the obscure unicode identifiers in it. - # *sigh* With roundtrip extended to test the 5-tuple mode of - # untokenize, 7 more testfiles fail. Remove them also until the - # failure is diagnosed. + # Tokenize is broken on test_pep3131.py because regular expressions are + # broken on the obscure unicode identifiers in it. *sigh* + # With roundtrip extended to test the 5-tuple mode of untokenize, + # 7 more testfiles fail. Remove them also until the failure is diagnosed. testfiles.remove(os.path.join(tempdir, "test_unicode_identifiers.py")) for f in ('buffer', 'builtin', 'fileio', 'inspect', 'os', 'platform', 'sys'): @@ -1565,4 +1581,4 @@ class TestRoundtrip(TestCase): if __name__ == "__main__": - main() + unittest.main() diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index a202196bd2..382ca03e5a 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -48,6 +48,7 @@ class TypesTests(unittest.TestCase): def test_float_constructor(self): self.assertRaises(ValueError, float, '') self.assertRaises(ValueError, float, '5\0') + self.assertRaises(ValueError, float, '5_5\0') def test_zero_division(self): try: 5.0 / 0.0 diff --git a/Lib/tokenize.py b/Lib/tokenize.py index ec79ec886d..825aa90646 100644 --- a/Lib/tokenize.py +++ b/Lib/tokenize.py @@ -120,16 +120,17 @@ Comment = r'#[^\r\n]*' Ignore = Whitespace + any(r'\\\r?\n' + Whitespace) + maybe(Comment) Name = r'\w+' -Hexnumber = r'0[xX][0-9a-fA-F]+' -Binnumber = r'0[bB][01]+' -Octnumber = r'0[oO][0-7]+' -Decnumber = r'(?:0+|[1-9][0-9]*)' +Hexnumber = r'0[xX](?:_?[0-9a-fA-F])+' +Binnumber = r'0[bB](?:_?[01])+' +Octnumber = r'0[oO](?:_?[0-7])+' +Decnumber = r'(?:0(?:_?0)*|[1-9](?:_?[0-9])*)' Intnumber = group(Hexnumber, Binnumber, Octnumber, Decnumber) -Exponent = r'[eE][-+]?[0-9]+' -Pointfloat = group(r'[0-9]+\.[0-9]*', r'\.[0-9]+') + maybe(Exponent) -Expfloat = r'[0-9]+' + Exponent +Exponent = r'[eE][-+]?[0-9](?:_?[0-9])*' +Pointfloat = group(r'[0-9](?:_?[0-9])*\.(?:[0-9](?:_?[0-9])*)?', + r'\.[0-9](?:_?[0-9])*') + maybe(Exponent) +Expfloat = r'[0-9](?:_?[0-9])*' + Exponent Floatnumber = group(Pointfloat, Expfloat) -Imagnumber = group(r'[0-9]+[jJ]', Floatnumber + r'[jJ]') +Imagnumber = group(r'[0-9](?:_?[0-9])*[jJ]', Floatnumber + r'[jJ]') Number = group(Imagnumber, Floatnumber, Intnumber) # Return the empty string, plus all of the valid string prefixes. -- cgit v1.2.1 From b61d3136e06f286d7c70a79d5928eb859ea55f6b Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 10 Sep 2016 00:57:55 +0300 Subject: Issue #433028: Added support of modifier spans in regular expressions. --- Lib/re.py | 2 +- Lib/sre_compile.py | 69 +++++++++++++++++-------------- Lib/sre_parse.py | 114 ++++++++++++++++++++++++++++++++++++++-------------- Lib/test/test_re.py | 40 ++++++++++++++++-- 4 files changed, 159 insertions(+), 66 deletions(-) (limited to 'Lib') diff --git a/Lib/re.py b/Lib/re.py index 661929e76b..b78da8939f 100644 --- a/Lib/re.py +++ b/Lib/re.py @@ -352,7 +352,7 @@ class Scanner: for phrase, action in lexicon: gid = s.opengroup() p.append(sre_parse.SubPattern(s, [ - (SUBPATTERN, (gid, sre_parse.parse(phrase, flags))), + (SUBPATTERN, (gid, 0, 0, sre_parse.parse(phrase, flags))), ])) s.closegroup(gid, p[-1]) p = sre_parse.SubPattern(s, [(BRANCH, (None, p))]) diff --git a/Lib/sre_compile.py b/Lib/sre_compile.py index 4edb03fa30..420d83de63 100644 --- a/Lib/sre_compile.py +++ b/Lib/sre_compile.py @@ -71,7 +71,8 @@ def _compile(code, pattern, flags): ASSERT_CODES = _ASSERT_CODES if (flags & SRE_FLAG_IGNORECASE and not (flags & SRE_FLAG_LOCALE) and - flags & SRE_FLAG_UNICODE): + flags & SRE_FLAG_UNICODE and + not (flags & SRE_FLAG_ASCII)): fixes = _ignorecase_fixes else: fixes = None @@ -137,14 +138,15 @@ def _compile(code, pattern, flags): else: emit(MIN_UNTIL) elif op is SUBPATTERN: - if av[0]: + group, add_flags, del_flags, p = av + if group: emit(MARK) - emit((av[0]-1)*2) - # _compile_info(code, av[1], flags) - _compile(code, av[1], flags) - if av[0]: + emit((group-1)*2) + # _compile_info(code, p, (flags | add_flags) & ~del_flags) + _compile(code, p, (flags | add_flags) & ~del_flags) + if group: emit(MARK) - emit((av[0]-1)*2+1) + emit((group-1)*2+1) elif op in SUCCESS_CODES: emit(op) elif op in ASSERT_CODES: @@ -172,7 +174,7 @@ def _compile(code, pattern, flags): av = AT_MULTILINE.get(av, av) if flags & SRE_FLAG_LOCALE: av = AT_LOCALE.get(av, av) - elif flags & SRE_FLAG_UNICODE: + elif (flags & SRE_FLAG_UNICODE) and not (flags & SRE_FLAG_ASCII): av = AT_UNICODE.get(av, av) emit(av) elif op is BRANCH: @@ -193,7 +195,7 @@ def _compile(code, pattern, flags): emit(op) if flags & SRE_FLAG_LOCALE: av = CH_LOCALE[av] - elif flags & SRE_FLAG_UNICODE: + elif (flags & SRE_FLAG_UNICODE) and not (flags & SRE_FLAG_ASCII): av = CH_UNICODE[av] emit(av) elif op is GROUPREF: @@ -237,7 +239,7 @@ def _compile_charset(charset, flags, code, fixup=None, fixes=None): elif op is CATEGORY: if flags & SRE_FLAG_LOCALE: emit(CH_LOCALE[av]) - elif flags & SRE_FLAG_UNICODE: + elif (flags & SRE_FLAG_UNICODE) and not (flags & SRE_FLAG_ASCII): emit(CH_UNICODE[av]) else: emit(av) @@ -414,14 +416,16 @@ def _get_literal_prefix(pattern): prefix = [] prefixappend = prefix.append prefix_skip = None - got_all = True for op, av in pattern.data: if op is LITERAL: prefixappend(av) elif op is SUBPATTERN: - prefix1, prefix_skip1, got_all = _get_literal_prefix(av[1]) + group, add_flags, del_flags, p = av + if add_flags & SRE_FLAG_IGNORECASE: + break + prefix1, prefix_skip1, got_all = _get_literal_prefix(p) if prefix_skip is None: - if av[0] is not None: + if group is not None: prefix_skip = len(prefix) elif prefix_skip1 is not None: prefix_skip = len(prefix) + prefix_skip1 @@ -429,32 +433,35 @@ def _get_literal_prefix(pattern): if not got_all: break else: - got_all = False break - return prefix, prefix_skip, got_all + else: + return prefix, prefix_skip, True + return prefix, prefix_skip, False def _get_charset_prefix(pattern): charset = [] # not used charsetappend = charset.append if pattern.data: op, av = pattern.data[0] - if op is SUBPATTERN and av[1]: - op, av = av[1][0] - if op is LITERAL: - charsetappend((op, av)) - elif op is BRANCH: - c = [] - cappend = c.append - for p in av[1]: - if not p: - break - op, av = p[0] - if op is LITERAL: - cappend((op, av)) + if op is SUBPATTERN: + group, add_flags, del_flags, p = av + if p and not (add_flags & SRE_FLAG_IGNORECASE): + op, av = p[0] + if op is LITERAL: + charsetappend((op, av)) + elif op is BRANCH: + c = [] + cappend = c.append + for p in av[1]: + if not p: + break + op, av = p[0] + if op is LITERAL: + cappend((op, av)) + else: + break else: - break - else: - charset = c + charset = c elif op is BRANCH: c = [] cappend = c.append diff --git a/Lib/sre_parse.py b/Lib/sre_parse.py index 521e379e72..09f3be25ab 100644 --- a/Lib/sre_parse.py +++ b/Lib/sre_parse.py @@ -65,6 +65,12 @@ FLAGS = { "u": SRE_FLAG_UNICODE, } +GLOBAL_FLAGS = (SRE_FLAG_ASCII | SRE_FLAG_LOCALE | SRE_FLAG_UNICODE | + SRE_FLAG_DEBUG | SRE_FLAG_TEMPLATE) + +class Verbose(Exception): + pass + class Pattern: # master pattern object. keeps track of global attributes def __init__(self): @@ -184,7 +190,7 @@ class SubPattern: lo = lo + i hi = hi + j elif op is SUBPATTERN: - i, j = av[1].getwidth() + i, j = av[-1].getwidth() lo = lo + i hi = hi + j elif op in _REPEATCODES: @@ -395,7 +401,7 @@ def _escape(source, escape, state): pass raise source.error("bad escape %s" % escape, len(escape)) -def _parse_sub(source, state, nested=True): +def _parse_sub(source, state, verbose, nested=True): # parse an alternation: a|b|c items = [] @@ -403,7 +409,7 @@ def _parse_sub(source, state, nested=True): sourcematch = source.match start = source.tell() while True: - itemsappend(_parse(source, state)) + itemsappend(_parse(source, state, verbose)) if not sourcematch("|"): break @@ -445,10 +451,10 @@ def _parse_sub(source, state, nested=True): subpattern.append((BRANCH, (None, items))) return subpattern -def _parse_sub_cond(source, state, condgroup): - item_yes = _parse(source, state) +def _parse_sub_cond(source, state, condgroup, verbose): + item_yes = _parse(source, state, verbose) if source.match("|"): - item_no = _parse(source, state) + item_no = _parse(source, state, verbose) if source.next == "|": raise source.error("conditional backref with more than two branches") else: @@ -457,7 +463,7 @@ def _parse_sub_cond(source, state, condgroup): subpattern.append((GROUPREF_EXISTS, (condgroup, item_yes, item_no))) return subpattern -def _parse(source, state): +def _parse(source, state, verbose): # parse a simple pattern subpattern = SubPattern(state) @@ -467,7 +473,6 @@ def _parse(source, state): sourcematch = source.match _len = len _ord = ord - verbose = state.flags & SRE_FLAG_VERBOSE while True: @@ -621,6 +626,8 @@ def _parse(source, state): group = True name = None condgroup = None + add_flags = 0 + del_flags = 0 if sourcematch("?"): # options char = sourceget() @@ -682,7 +689,7 @@ def _parse(source, state): lookbehindgroups = state.lookbehindgroups if lookbehindgroups is None: state.lookbehindgroups = state.groups - p = _parse_sub(source, state) + p = _parse_sub(source, state, verbose) if dir < 0: if lookbehindgroups is None: state.lookbehindgroups = None @@ -718,19 +725,13 @@ def _parse(source, state): raise source.error("invalid group reference", len(condname) + 1) state.checklookbehindgroup(condgroup, source) - elif char in FLAGS: + elif char in FLAGS or char == "-": # flags - while True: - state.flags |= FLAGS[char] - char = sourceget() - if char is None: - raise source.error("missing )") - if char == ")": - break - if char not in FLAGS: - raise source.error("unknown flag", len(char)) - verbose = state.flags & SRE_FLAG_VERBOSE - continue + flags = _parse_flags(source, state, char) + if flags is None: # global flags + continue + add_flags, del_flags = flags + group = None else: raise source.error("unknown extension ?" + char, len(char) + 1) @@ -742,15 +743,17 @@ def _parse(source, state): except error as err: raise source.error(err.msg, len(name) + 1) from None if condgroup: - p = _parse_sub_cond(source, state, condgroup) + p = _parse_sub_cond(source, state, condgroup, verbose) else: - p = _parse_sub(source, state) + sub_verbose = ((verbose or (add_flags & SRE_FLAG_VERBOSE)) and + not (del_flags & SRE_FLAG_VERBOSE)) + p = _parse_sub(source, state, sub_verbose) if not source.match(")"): raise source.error("missing ), unterminated subpattern", source.tell() - start) if group is not None: state.closegroup(group, p) - subpatternappend((SUBPATTERN, (group, p))) + subpatternappend((SUBPATTERN, (group, add_flags, del_flags, p))) elif this == "^": subpatternappend((AT, AT_BEGINNING)) @@ -763,6 +766,53 @@ def _parse(source, state): return subpattern +def _parse_flags(source, state, char): + sourceget = source.get + add_flags = 0 + del_flags = 0 + if char != "-": + while True: + add_flags |= FLAGS[char] + char = sourceget() + if char is None: + raise source.error("missing -, : or )") + if char in ")-:": + break + if char not in FLAGS: + msg = "unknown flag" if char.isalpha() else "missing -, : or )" + raise source.error(msg, len(char)) + if char == ")": + if ((add_flags & SRE_FLAG_VERBOSE) and + not (state.flags & SRE_FLAG_VERBOSE)): + raise Verbose + state.flags |= add_flags + return None + if add_flags & GLOBAL_FLAGS: + raise source.error("bad inline flags: cannot turn on global flag", 1) + if char == "-": + char = sourceget() + if char is None: + raise source.error("missing flag") + if char not in FLAGS: + msg = "unknown flag" if char.isalpha() else "missing flag" + raise source.error(msg, len(char)) + while True: + del_flags |= FLAGS[char] + char = sourceget() + if char is None: + raise source.error("missing :") + if char == ":": + break + if char not in FLAGS: + msg = "unknown flag" if char.isalpha() else "missing :" + raise source.error(msg, len(char)) + assert char == ":" + if del_flags & GLOBAL_FLAGS: + raise source.error("bad inline flags: cannot turn off global flag", 1) + if add_flags & del_flags: + raise source.error("bad inline flags: flag turned on and off", 1) + return add_flags, del_flags + def fix_flags(src, flags): # Check and fix flags according to the type of pattern (str or bytes) if isinstance(src, str): @@ -789,18 +839,22 @@ def parse(str, flags=0, pattern=None): pattern.flags = flags pattern.str = str - p = _parse_sub(source, pattern, 0) + try: + p = _parse_sub(source, pattern, flags & SRE_FLAG_VERBOSE, False) + except Verbose: + # the VERBOSE flag was switched on inside the pattern. to be + # on the safe side, we'll parse the whole thing again... + pattern = Pattern() + pattern.flags = flags | SRE_FLAG_VERBOSE + pattern.str = str + p = _parse_sub(source, pattern, True, False) + p.pattern.flags = fix_flags(str, p.pattern.flags) if source.next is not None: assert source.next == ")" raise source.error("unbalanced parenthesis") - if not (flags & SRE_FLAG_VERBOSE) and p.pattern.flags & SRE_FLAG_VERBOSE: - # the VERBOSE flag was switched on inside the pattern. to be - # on the safe side, we'll parse the whole thing again... - return parse(str, p.pattern.flags) - if flags & SRE_FLAG_DEBUG: p.dump() diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index 02fed21992..2322ca9bb4 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -1376,6 +1376,38 @@ class ReTests(unittest.TestCase): self.assertRaises(ValueError, re.compile, b'(?a)', re.LOCALE) self.assertRaises(ValueError, re.compile, b'(?aL)') + def test_scoped_flags(self): + self.assertTrue(re.match(r'(?i:a)b', 'Ab')) + self.assertIsNone(re.match(r'(?i:a)b', 'aB')) + self.assertIsNone(re.match(r'(?-i:a)b', 'Ab', re.IGNORECASE)) + self.assertTrue(re.match(r'(?-i:a)b', 'aB', re.IGNORECASE)) + self.assertIsNone(re.match(r'(?i:(?-i:a)b)', 'Ab')) + self.assertTrue(re.match(r'(?i:(?-i:a)b)', 'aB')) + + self.assertTrue(re.match(r'(?x: a) b', 'a b')) + self.assertIsNone(re.match(r'(?x: a) b', ' a b')) + self.assertTrue(re.match(r'(?-x: a) b', ' ab', re.VERBOSE)) + self.assertIsNone(re.match(r'(?-x: a) b', 'ab', re.VERBOSE)) + + self.checkPatternError(r'(?a:\w)', + 'bad inline flags: cannot turn on global flag', 3) + self.checkPatternError(r'(?a)(?-a:\w)', + 'bad inline flags: cannot turn off global flag', 8) + self.checkPatternError(r'(?i-i:a)', + 'bad inline flags: flag turned on and off', 5) + + self.checkPatternError(r'(?-', 'missing flag', 3) + self.checkPatternError(r'(?-+', 'missing flag', 3) + self.checkPatternError(r'(?-z', 'unknown flag', 3) + self.checkPatternError(r'(?-i', 'missing :', 4) + self.checkPatternError(r'(?-i)', 'missing :', 4) + self.checkPatternError(r'(?-i+', 'missing :', 4) + self.checkPatternError(r'(?-iz', 'unknown flag', 4) + self.checkPatternError(r'(?i:', 'missing ), unterminated subpattern', 0) + self.checkPatternError(r'(?i', 'missing -, : or )', 3) + self.checkPatternError(r'(?i+', 'missing -, : or )', 3) + self.checkPatternError(r'(?iz', 'unknown flag', 3) + def test_bug_6509(self): # Replacement strings of both types must parse properly. # all strings @@ -1538,9 +1570,9 @@ class ReTests(unittest.TestCase): with captured_stdout() as out: re.compile(pat, re.DEBUG) dump = '''\ -SUBPATTERN 1 +SUBPATTERN 1 0 0 LITERAL 46 -SUBPATTERN None +SUBPATTERN None 0 0 BRANCH IN LITERAL 99 @@ -1548,7 +1580,7 @@ SUBPATTERN None OR LITERAL 112 LITERAL 121 -SUBPATTERN None +SUBPATTERN None 0 0 GROUPREF_EXISTS 1 AT AT_END ELSE @@ -1664,7 +1696,7 @@ SUBPATTERN None self.checkPatternError(r'(?P', 'unexpected end of pattern', 3) self.checkPatternError(r'(?z)', 'unknown extension ?z', 1) self.checkPatternError(r'(?iz)', 'unknown flag', 3) - self.checkPatternError(r'(?i', 'missing )', 3) + self.checkPatternError(r'(?i', 'missing -, : or )', 3) self.checkPatternError(r'(?#abc', 'missing ), unterminated comment', 0) self.checkPatternError(r'(?<', 'unexpected end of pattern', 3) self.checkPatternError(r'(?<>)', 'unknown extension ?<>', 1) -- cgit v1.2.1 From 4c08bbdc7c3d50f58256dbd1f6b03f309a55b96c Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sat, 10 Sep 2016 00:19:35 +0200 Subject: Issue #28025: Convert all ssl module constants to IntEnum and IntFlags. --- Lib/ssl.py | 80 +++++++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 61 insertions(+), 19 deletions(-) (limited to 'Lib') diff --git a/Lib/ssl.py b/Lib/ssl.py index 42ca1686d9..5f33849146 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -94,7 +94,7 @@ import re import sys import os from collections import namedtuple -from enum import Enum as _Enum, IntEnum as _IntEnum +from enum import Enum as _Enum, IntEnum as _IntEnum, IntFlag as _IntFlag import _ssl # if we can't import it, let the error propagate @@ -104,7 +104,6 @@ from _ssl import ( SSLError, SSLZeroReturnError, SSLWantReadError, SSLWantWriteError, SSLSyscallError, SSLEOFError, ) -from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED from _ssl import txt2obj as _txt2obj, nid2obj as _nid2obj from _ssl import RAND_status, RAND_add, RAND_bytes, RAND_pseudo_bytes try: @@ -113,32 +112,47 @@ except ImportError: # LibreSSL does not provide RAND_egd pass -def _import_symbols(prefix): - for n in dir(_ssl): - if n.startswith(prefix): - globals()[n] = getattr(_ssl, n) - -_import_symbols('OP_') -_import_symbols('ALERT_DESCRIPTION_') -_import_symbols('SSL_ERROR_') -_import_symbols('VERIFY_') from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN - from _ssl import _OPENSSL_API_VERSION + +_IntEnum._convert( + '_SSLMethod', __name__, + lambda name: name.startswith('PROTOCOL_') and name != 'PROTOCOL_SSLv23', + source=_ssl) + +_IntFlag._convert( + 'Options', __name__, + lambda name: name.startswith('OP_'), + source=_ssl) + _IntEnum._convert( - '_SSLMethod', __name__, - lambda name: name.startswith('PROTOCOL_') and name != 'PROTOCOL_SSLv23', - source=_ssl) + 'AlertDescription', __name__, + lambda name: name.startswith('ALERT_DESCRIPTION_'), + source=_ssl) + +_IntEnum._convert( + 'SSLErrorNumber', __name__, + lambda name: name.startswith('SSL_ERROR_'), + source=_ssl) + +_IntFlag._convert( + 'VerifyFlags', __name__, + lambda name: name.startswith('VERIFY_'), + source=_ssl) + +_IntEnum._convert( + 'VerifyMode', __name__, + lambda name: name.startswith('CERT_'), + source=_ssl) + PROTOCOL_SSLv23 = _SSLMethod.PROTOCOL_SSLv23 = _SSLMethod.PROTOCOL_TLS _PROTOCOL_NAMES = {value: name for name, value in _SSLMethod.__members__.items()} -try: - _SSLv2_IF_EXISTS = PROTOCOL_SSLv2 -except NameError: - _SSLv2_IF_EXISTS = None +_SSLv2_IF_EXISTS = getattr(_SSLMethod, 'PROTOCOL_SSLv2', None) + if sys.platform == "win32": from _ssl import enum_certificates, enum_crls @@ -434,6 +448,34 @@ class SSLContext(_SSLContext): self._load_windows_store_certs(storename, purpose) self.set_default_verify_paths() + @property + def options(self): + return Options(super().options) + + @options.setter + def options(self, value): + super(SSLContext, SSLContext).options.__set__(self, value) + + @property + def verify_flags(self): + return VerifyFlags(super().verify_flags) + + @verify_flags.setter + def verify_flags(self, value): + super(SSLContext, SSLContext).verify_flags.__set__(self, value) + + @property + def verify_mode(self): + value = super().verify_mode + try: + return VerifyMode(value) + except ValueError: + return value + + @verify_mode.setter + def verify_mode(self, value): + super(SSLContext, SSLContext).verify_mode.__set__(self, value) + def create_default_context(purpose=Purpose.SERVER_AUTH, *, cafile=None, capath=None, cadata=None): -- cgit v1.2.1 From ed08adcacb35a3b05228d71b6fb6853c41e7a8ca Mon Sep 17 00:00:00 2001 From: R David Murray Date: Fri, 9 Sep 2016 18:39:18 -0400 Subject: #20476: add a message_factory policy attribute to email. --- Lib/email/_policybase.py | 4 ++ Lib/email/feedparser.py | 9 +--- Lib/email/message.py | 8 ++-- Lib/email/policy.py | 2 + Lib/test/test_email/test_parser.py | 91 ++++++++++++++++++++++++++------------ Lib/test/test_email/test_policy.py | 42 +++++++++++------- 6 files changed, 99 insertions(+), 57 deletions(-) (limited to 'Lib') diff --git a/Lib/email/_policybase.py b/Lib/email/_policybase.py index c0d98a4f54..d6994844e1 100644 --- a/Lib/email/_policybase.py +++ b/Lib/email/_policybase.py @@ -154,6 +154,8 @@ class Policy(_PolicyBase, metaclass=abc.ABCMeta): them. This is used when the message is being serialized by a generator. Default: True. + message_factory -- the class to use to create new message objects. + """ raise_on_defect = False @@ -161,6 +163,8 @@ class Policy(_PolicyBase, metaclass=abc.ABCMeta): cte_type = '8bit' max_line_length = 78 mangle_from_ = False + # XXX To avoid circular imports, this is set in email.message. + message_factory = None def handle_defect(self, obj, defect): """Based on policy, either raise defect or call register_defect. diff --git a/Lib/email/feedparser.py b/Lib/email/feedparser.py index 2fa77d7afc..3d74978cdb 100644 --- a/Lib/email/feedparser.py +++ b/Lib/email/feedparser.py @@ -24,7 +24,6 @@ __all__ = ['FeedParser', 'BytesFeedParser'] import re from email import errors -from email import message from email._policybase import compat32 from collections import deque from io import StringIO @@ -148,13 +147,7 @@ class FeedParser: self.policy = policy self._old_style_factory = False if _factory is None: - # What this should be: - #self._factory = policy.default_message_factory - # but, because we are post 3.4 feature freeze, fix with temp hack: - if self.policy is compat32: - self._factory = message.Message - else: - self._factory = message.EmailMessage + self._factory = policy.message_factory else: self._factory = _factory try: diff --git a/Lib/email/message.py b/Lib/email/message.py index c07da436ac..f4380d931a 100644 --- a/Lib/email/message.py +++ b/Lib/email/message.py @@ -4,18 +4,17 @@ """Basic message object for the email package object model.""" -__all__ = ['Message'] +__all__ = ['Message', 'EmailMessage'] import re import uu import quopri -import warnings from io import BytesIO, StringIO # Intrapackage imports from email import utils from email import errors -from email._policybase import compat32 +from email._policybase import Policy, compat32 from email import charset as _charset from email._encoded_words import decode_b Charset = _charset.Charset @@ -1163,3 +1162,6 @@ class EmailMessage(MIMEPart): super().set_content(*args, **kw) if 'MIME-Version' not in self: self['MIME-Version'] = '1.0' + +# Set message_factory on Policy here to avoid a circular import. +Policy.message_factory = Message diff --git a/Lib/email/policy.py b/Lib/email/policy.py index 35d0e699c6..5131311ac5 100644 --- a/Lib/email/policy.py +++ b/Lib/email/policy.py @@ -7,6 +7,7 @@ from email._policybase import Policy, Compat32, compat32, _extend_docstrings from email.utils import _has_surrogates from email.headerregistry import HeaderRegistry as HeaderRegistry from email.contentmanager import raw_data_manager +from email.message import EmailMessage __all__ = [ 'Compat32', @@ -82,6 +83,7 @@ class EmailPolicy(Policy): """ + message_factory = EmailMessage utf8 = False refold_source = 'long' header_factory = HeaderRegistry() diff --git a/Lib/test/test_email/test_parser.py b/Lib/test/test_email/test_parser.py index 8ddc176389..06c86408ab 100644 --- a/Lib/test/test_email/test_parser.py +++ b/Lib/test/test_email/test_parser.py @@ -1,7 +1,7 @@ import io import email import unittest -from email.message import Message +from email.message import Message, EmailMessage from email.policy import default from test.test_email import TestEmailBase @@ -39,38 +39,71 @@ class TestParserBase: # The unicode line splitter splits on unicode linebreaks, which are # more numerous than allowed by the email RFCs; make sure we are only # splitting on those two. - msg = self.parser( - "Next-Line: not\x85broken\r\n" - "Null: not\x00broken\r\n" - "Vertical-Tab: not\vbroken\r\n" - "Form-Feed: not\fbroken\r\n" - "File-Separator: not\x1Cbroken\r\n" - "Group-Separator: not\x1Dbroken\r\n" - "Record-Separator: not\x1Ebroken\r\n" - "Line-Separator: not\u2028broken\r\n" - "Paragraph-Separator: not\u2029broken\r\n" - "\r\n", - policy=default, - ) - self.assertEqual(msg.items(), [ - ("Next-Line", "not\x85broken"), - ("Null", "not\x00broken"), - ("Vertical-Tab", "not\vbroken"), - ("Form-Feed", "not\fbroken"), - ("File-Separator", "not\x1Cbroken"), - ("Group-Separator", "not\x1Dbroken"), - ("Record-Separator", "not\x1Ebroken"), - ("Line-Separator", "not\u2028broken"), - ("Paragraph-Separator", "not\u2029broken"), - ]) - self.assertEqual(msg.get_payload(), "") + for parser in self.parsers: + with self.subTest(parser=parser.__name__): + msg = parser( + "Next-Line: not\x85broken\r\n" + "Null: not\x00broken\r\n" + "Vertical-Tab: not\vbroken\r\n" + "Form-Feed: not\fbroken\r\n" + "File-Separator: not\x1Cbroken\r\n" + "Group-Separator: not\x1Dbroken\r\n" + "Record-Separator: not\x1Ebroken\r\n" + "Line-Separator: not\u2028broken\r\n" + "Paragraph-Separator: not\u2029broken\r\n" + "\r\n", + policy=default, + ) + self.assertEqual(msg.items(), [ + ("Next-Line", "not\x85broken"), + ("Null", "not\x00broken"), + ("Vertical-Tab", "not\vbroken"), + ("Form-Feed", "not\fbroken"), + ("File-Separator", "not\x1Cbroken"), + ("Group-Separator", "not\x1Dbroken"), + ("Record-Separator", "not\x1Ebroken"), + ("Line-Separator", "not\u2028broken"), + ("Paragraph-Separator", "not\u2029broken"), + ]) + self.assertEqual(msg.get_payload(), "") + + class MyMessage(EmailMessage): + pass + + def test_custom_message_factory_on_policy(self): + for parser in self.parsers: + with self.subTest(parser=parser.__name__): + MyPolicy = default.clone(message_factory=self.MyMessage) + msg = parser("To: foo\n\ntest", policy=MyPolicy) + self.assertIsInstance(msg, self.MyMessage) + + def test_factory_arg_overrides_policy(self): + for parser in self.parsers: + with self.subTest(parser=parser.__name__): + MyPolicy = default.clone(message_factory=self.MyMessage) + msg = parser("To: foo\n\ntest", Message, policy=MyPolicy) + self.assertNotIsInstance(msg, self.MyMessage) + self.assertIsInstance(msg, Message) + +# Play some games to get nice output in subTest. This code could be clearer +# if staticmethod supported __name__. + +def message_from_file(s, *args, **kw): + f = io.StringIO(s) + return email.message_from_file(f, *args, **kw) class TestParser(TestParserBase, TestEmailBase): - parser = staticmethod(email.message_from_string) + parsers = (email.message_from_string, message_from_file) + +def message_from_bytes(s, *args, **kw): + return email.message_from_bytes(s.encode(), *args, **kw) + +def message_from_binary_file(s, *args, **kw): + f = io.BytesIO(s.encode()) + return email.message_from_binary_file(f, *args, **kw) class TestBytesParser(TestParserBase, TestEmailBase): - def parser(self, s, *args, **kw): - return email.message_from_bytes(s.encode(), *args, **kw) + parsers = (message_from_bytes, message_from_binary_file) if __name__ == '__main__': diff --git a/Lib/test/test_email/test_policy.py b/Lib/test/test_email/test_policy.py index 70ac4db8b0..1d95d03bf5 100644 --- a/Lib/test/test_email/test_policy.py +++ b/Lib/test/test_email/test_policy.py @@ -5,6 +5,7 @@ import unittest import email.policy import email.parser import email.generator +import email.message from email import headerregistry def make_defaults(base_defaults, differences): @@ -23,6 +24,7 @@ class PolicyAPITests(unittest.TestCase): 'cte_type': '8bit', 'raise_on_defect': False, 'mangle_from_': True, + 'message_factory': email.message.Message, } # These default values are the ones set on email.policy.default. # If any of these defaults change, the docs must be updated. @@ -34,6 +36,7 @@ class PolicyAPITests(unittest.TestCase): 'refold_source': 'long', 'content_manager': email.policy.EmailPolicy.content_manager, 'mangle_from_': False, + 'message_factory': email.message.EmailMessage, }) # For each policy under test, we give here what we expect the defaults to @@ -62,20 +65,22 @@ class PolicyAPITests(unittest.TestCase): def test_defaults(self): for policy, expected in self.policies.items(): for attr, value in expected.items(): - self.assertEqual(getattr(policy, attr), value, - ("change {} docs/docstrings if defaults have " - "changed").format(policy)) + with self.subTest(policy=policy, attr=attr): + self.assertEqual(getattr(policy, attr), value, + ("change {} docs/docstrings if defaults have " + "changed").format(policy)) def test_all_attributes_covered(self): for policy, expected in self.policies.items(): for attr in dir(policy): - if (attr.startswith('_') or - isinstance(getattr(email.policy.EmailPolicy, attr), - types.FunctionType)): - continue - else: - self.assertIn(attr, expected, - "{} is not fully tested".format(attr)) + with self.subTest(policy=policy, attr=attr): + if (attr.startswith('_') or + isinstance(getattr(email.policy.EmailPolicy, attr), + types.FunctionType)): + continue + else: + self.assertIn(attr, expected, + "{} is not fully tested".format(attr)) def test_abc(self): with self.assertRaises(TypeError) as cm: @@ -237,6 +242,9 @@ class PolicyAPITests(unittest.TestCase): # wins), but that the order still works (right overrides left). +class TestException(Exception): + pass + class TestPolicyPropagation(unittest.TestCase): # The abstract methods are used by the parser but not by the wrapper @@ -244,40 +252,40 @@ class TestPolicyPropagation(unittest.TestCase): # policy was actually propagated all the way to feedparser. class MyPolicy(email.policy.Policy): def badmethod(self, *args, **kw): - raise Exception("test") + raise TestException("test") fold = fold_binary = header_fetch_parser = badmethod header_source_parse = header_store_parse = badmethod def test_message_from_string(self): - with self.assertRaisesRegex(Exception, "^test$"): + with self.assertRaisesRegex(TestException, "^test$"): email.message_from_string("Subject: test\n\n", policy=self.MyPolicy) def test_message_from_bytes(self): - with self.assertRaisesRegex(Exception, "^test$"): + with self.assertRaisesRegex(TestException, "^test$"): email.message_from_bytes(b"Subject: test\n\n", policy=self.MyPolicy) def test_message_from_file(self): f = io.StringIO('Subject: test\n\n') - with self.assertRaisesRegex(Exception, "^test$"): + with self.assertRaisesRegex(TestException, "^test$"): email.message_from_file(f, policy=self.MyPolicy) def test_message_from_binary_file(self): f = io.BytesIO(b'Subject: test\n\n') - with self.assertRaisesRegex(Exception, "^test$"): + with self.assertRaisesRegex(TestException, "^test$"): email.message_from_binary_file(f, policy=self.MyPolicy) # These are redundant, but we need them for black-box completeness. def test_parser(self): p = email.parser.Parser(policy=self.MyPolicy) - with self.assertRaisesRegex(Exception, "^test$"): + with self.assertRaisesRegex(TestException, "^test$"): p.parsestr('Subject: test\n\n') def test_bytes_parser(self): p = email.parser.BytesParser(policy=self.MyPolicy) - with self.assertRaisesRegex(Exception, "^test$"): + with self.assertRaisesRegex(TestException, "^test$"): p.parsebytes(b'Subject: test\n\n') # Now that we've established that all the parse methods get the -- cgit v1.2.1 From e14a08d93ffbf29505301b7bc1a7585e69dc1689 Mon Sep 17 00:00:00 2001 From: Davin Potts Date: Fri, 9 Sep 2016 18:03:10 -0500 Subject: Issue #28053: Applying refactorings, docs and other cleanup to follow. --- Lib/multiprocessing/connection.py | 8 ++++---- Lib/multiprocessing/context.py | 13 ++++++++++-- Lib/multiprocessing/forkserver.py | 2 +- Lib/multiprocessing/heap.py | 5 ++--- Lib/multiprocessing/managers.py | 5 ++--- Lib/multiprocessing/popen_forkserver.py | 7 +++---- Lib/multiprocessing/popen_spawn_posix.py | 7 +++---- Lib/multiprocessing/popen_spawn_win32.py | 9 ++++----- Lib/multiprocessing/queues.py | 10 +++++----- Lib/multiprocessing/reduction.py | 34 ++++++++++++++++++++++++++++++++ Lib/multiprocessing/resource_sharer.py | 2 +- Lib/multiprocessing/sharedctypes.py | 6 +++--- Lib/multiprocessing/spawn.py | 9 ++++----- 13 files changed, 77 insertions(+), 40 deletions(-) (limited to 'Lib') diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py index d0a1b86b13..d49e8f0d32 100644 --- a/Lib/multiprocessing/connection.py +++ b/Lib/multiprocessing/connection.py @@ -20,11 +20,11 @@ import itertools import _multiprocessing -from . import reduction from . import util from . import AuthenticationError, BufferTooShort -from .reduction import ForkingPickler +from .context import reduction +_ForkingPickler = reduction.ForkingPickler try: import _winapi @@ -203,7 +203,7 @@ class _ConnectionBase: """Send a (picklable) object""" self._check_closed() self._check_writable() - self._send_bytes(ForkingPickler.dumps(obj)) + self._send_bytes(_ForkingPickler.dumps(obj)) def recv_bytes(self, maxlength=None): """ @@ -248,7 +248,7 @@ class _ConnectionBase: self._check_closed() self._check_readable() buf = self._recv_bytes() - return ForkingPickler.loads(buf.getbuffer()) + return _ForkingPickler.loads(buf.getbuffer()) def poll(self, timeout=0.0): """Whether there is any input available to be read""" diff --git a/Lib/multiprocessing/context.py b/Lib/multiprocessing/context.py index 63849f9d16..09455e2ec7 100644 --- a/Lib/multiprocessing/context.py +++ b/Lib/multiprocessing/context.py @@ -3,6 +3,7 @@ import sys import threading from . import process +from . import reduction __all__ = [] # things are copied from here to __init__.py @@ -198,6 +199,16 @@ class BaseContext(object): def set_start_method(self, method=None): raise ValueError('cannot set start method of concrete context') + @property + def reducer(self): + '''Controls how objects will be reduced to a form that can be + shared with other processes.''' + return globals().get('reduction') + + @reducer.setter + def reducer(self, reduction): + globals()['reduction'] = reduction + def _check_available(self): pass @@ -245,7 +256,6 @@ class DefaultContext(BaseContext): if sys.platform == 'win32': return ['spawn'] else: - from . import reduction if reduction.HAVE_SEND_HANDLE: return ['fork', 'spawn', 'forkserver'] else: @@ -292,7 +302,6 @@ if sys.platform != 'win32': _name = 'forkserver' Process = ForkServerProcess def _check_available(self): - from . import reduction if not reduction.HAVE_SEND_HANDLE: raise ValueError('forkserver start method not available') diff --git a/Lib/multiprocessing/forkserver.py b/Lib/multiprocessing/forkserver.py index ad01ede0e0..f2c179e4e0 100644 --- a/Lib/multiprocessing/forkserver.py +++ b/Lib/multiprocessing/forkserver.py @@ -9,7 +9,7 @@ import threading from . import connection from . import process -from . import reduction +from .context import reduction from . import semaphore_tracker from . import spawn from . import util diff --git a/Lib/multiprocessing/heap.py b/Lib/multiprocessing/heap.py index 44d9638ff6..443321535e 100644 --- a/Lib/multiprocessing/heap.py +++ b/Lib/multiprocessing/heap.py @@ -14,8 +14,7 @@ import sys import tempfile import threading -from . import context -from . import reduction +from .context import reduction, assert_spawning from . import util __all__ = ['BufferWrapper'] @@ -48,7 +47,7 @@ if sys.platform == 'win32': self._state = (self.size, self.name) def __getstate__(self): - context.assert_spawning(self) + assert_spawning(self) return self._state def __setstate__(self, state): diff --git a/Lib/multiprocessing/managers.py b/Lib/multiprocessing/managers.py index c4dc972b80..b9ce84b2d8 100644 --- a/Lib/multiprocessing/managers.py +++ b/Lib/multiprocessing/managers.py @@ -23,10 +23,9 @@ from time import time as _time from traceback import format_exc from . import connection -from . import context +from .context import reduction, get_spawning_popen from . import pool from . import process -from . import reduction from . import util from . import get_context @@ -833,7 +832,7 @@ class BaseProxy(object): def __reduce__(self): kwds = {} - if context.get_spawning_popen() is not None: + if get_spawning_popen() is not None: kwds['authkey'] = self._authkey if getattr(self, '_isauto', False): diff --git a/Lib/multiprocessing/popen_forkserver.py b/Lib/multiprocessing/popen_forkserver.py index e792194f44..222db2d90a 100644 --- a/Lib/multiprocessing/popen_forkserver.py +++ b/Lib/multiprocessing/popen_forkserver.py @@ -1,10 +1,9 @@ import io import os -from . import reduction +from .context import reduction, set_spawning_popen if not reduction.HAVE_SEND_HANDLE: raise ImportError('No support for sending fds between processes') -from . import context from . import forkserver from . import popen_fork from . import spawn @@ -42,12 +41,12 @@ class Popen(popen_fork.Popen): def _launch(self, process_obj): prep_data = spawn.get_preparation_data(process_obj._name) buf = io.BytesIO() - context.set_spawning_popen(self) + set_spawning_popen(self) try: reduction.dump(prep_data, buf) reduction.dump(process_obj, buf) finally: - context.set_spawning_popen(None) + set_spawning_popen(None) self.sentinel, w = forkserver.connect_to_new_process(self._fds) util.Finalize(self, os.close, (self.sentinel,)) diff --git a/Lib/multiprocessing/popen_spawn_posix.py b/Lib/multiprocessing/popen_spawn_posix.py index 6b0a8d635f..98f8f0ab33 100644 --- a/Lib/multiprocessing/popen_spawn_posix.py +++ b/Lib/multiprocessing/popen_spawn_posix.py @@ -1,9 +1,8 @@ import io import os -from . import context +from .context import reduction, set_spawning_popen from . import popen_fork -from . import reduction from . import spawn from . import util @@ -42,12 +41,12 @@ class Popen(popen_fork.Popen): self._fds.append(tracker_fd) prep_data = spawn.get_preparation_data(process_obj._name) fp = io.BytesIO() - context.set_spawning_popen(self) + set_spawning_popen(self) try: reduction.dump(prep_data, fp) reduction.dump(process_obj, fp) finally: - context.set_spawning_popen(None) + set_spawning_popen(None) parent_r = child_w = child_r = parent_w = None try: diff --git a/Lib/multiprocessing/popen_spawn_win32.py b/Lib/multiprocessing/popen_spawn_win32.py index 3b53068be4..6fd588f542 100644 --- a/Lib/multiprocessing/popen_spawn_win32.py +++ b/Lib/multiprocessing/popen_spawn_win32.py @@ -4,9 +4,8 @@ import signal import sys import _winapi -from . import context +from .context import reduction, get_spawning_popen, set_spawning_popen from . import spawn -from . import reduction from . import util __all__ = ['Popen'] @@ -60,15 +59,15 @@ class Popen(object): util.Finalize(self, _winapi.CloseHandle, (self.sentinel,)) # send information to child - context.set_spawning_popen(self) + set_spawning_popen(self) try: reduction.dump(prep_data, to_child) reduction.dump(process_obj, to_child) finally: - context.set_spawning_popen(None) + set_spawning_popen(None) def duplicate_for_child(self, handle): - assert self is context.get_spawning_popen() + assert self is get_spawning_popen() return reduction.duplicate(handle, self.sentinel) def wait(self, timeout=None): diff --git a/Lib/multiprocessing/queues.py b/Lib/multiprocessing/queues.py index 786a303b33..dda03ddf54 100644 --- a/Lib/multiprocessing/queues.py +++ b/Lib/multiprocessing/queues.py @@ -23,9 +23,9 @@ import _multiprocessing from . import connection from . import context +_ForkingPickler = context.reduction.ForkingPickler from .util import debug, info, Finalize, register_after_fork, is_exiting -from .reduction import ForkingPickler # # Queue type using a pipe, buffer and thread @@ -110,7 +110,7 @@ class Queue(object): finally: self._rlock.release() # unserialize the data after having released the lock - return ForkingPickler.loads(res) + return _ForkingPickler.loads(res) def qsize(self): # Raises NotImplementedError on Mac OSX because of broken sem_getvalue() @@ -238,7 +238,7 @@ class Queue(object): return # serialize the data before acquiring the lock - obj = ForkingPickler.dumps(obj) + obj = _ForkingPickler.dumps(obj) if wacquire is None: send_bytes(obj) else: @@ -342,11 +342,11 @@ class SimpleQueue(object): with self._rlock: res = self._reader.recv_bytes() # unserialize the data after having released the lock - return ForkingPickler.loads(res) + return _ForkingPickler.loads(res) def put(self, obj): # serialize the data before acquiring the lock - obj = ForkingPickler.dumps(obj) + obj = _ForkingPickler.dumps(obj) if self._wlock is None: # writes to a message oriented win32 pipe are atomic self._writer.send_bytes(obj) diff --git a/Lib/multiprocessing/reduction.py b/Lib/multiprocessing/reduction.py index 8f209b47da..c043c9a0dc 100644 --- a/Lib/multiprocessing/reduction.py +++ b/Lib/multiprocessing/reduction.py @@ -7,6 +7,7 @@ # Licensed to PSF under a Contributor Agreement. # +from abc import ABCMeta, abstractmethod import copyreg import functools import io @@ -238,3 +239,36 @@ else: fd = df.detach() return socket.socket(family, type, proto, fileno=fd) register(socket.socket, _reduce_socket) + + +class AbstractReducer(metaclass=ABCMeta): + '''Abstract base class for use in implementing a Reduction class + suitable for use in replacing the standard reduction mechanism + used in multiprocessing.''' + ForkingPickler = ForkingPickler + register = register + dump = dump + send_handle = send_handle + recv_handle = recv_handle + + if sys.platform == 'win32': + steal_handle = steal_handle + duplicate = duplicate + DupHandle = DupHandle + else: + sendfds = sendfds + recvfds = recvfds + DupFd = DupFd + + _reduce_method = _reduce_method + _reduce_method_descriptor = _reduce_method_descriptor + _rebuild_partial = _rebuild_partial + _reduce_socket = _reduce_socket + _rebuild_socket = _rebuild_socket + + def __init__(self, *args): + register(type(_C().f), _reduce_method) + register(type(list.append), _reduce_method_descriptor) + register(type(int.__add__), _reduce_method_descriptor) + register(functools.partial, _reduce_partial) + register(socket.socket, _reduce_socket) diff --git a/Lib/multiprocessing/resource_sharer.py b/Lib/multiprocessing/resource_sharer.py index 5e46fc65b4..e44a728fa9 100644 --- a/Lib/multiprocessing/resource_sharer.py +++ b/Lib/multiprocessing/resource_sharer.py @@ -15,7 +15,7 @@ import sys import threading from . import process -from . import reduction +from .context import reduction from . import util __all__ = ['stop'] diff --git a/Lib/multiprocessing/sharedctypes.py b/Lib/multiprocessing/sharedctypes.py index 4258f591c4..25cbcf2ae4 100644 --- a/Lib/multiprocessing/sharedctypes.py +++ b/Lib/multiprocessing/sharedctypes.py @@ -13,8 +13,8 @@ import weakref from . import heap from . import get_context -from .context import assert_spawning -from .reduction import ForkingPickler +from .context import reduction, assert_spawning +_ForkingPickler = reduction.ForkingPickler __all__ = ['RawValue', 'RawArray', 'Value', 'Array', 'copy', 'synchronized'] @@ -134,7 +134,7 @@ def reduce_ctype(obj): def rebuild_ctype(type_, wrapper, length): if length is not None: type_ = type_ * length - ForkingPickler.register(type_, reduce_ctype) + _ForkingPickler.register(type_, reduce_ctype) buf = wrapper.create_memoryview() obj = type_.from_buffer(buf) obj._wrapper = wrapper diff --git a/Lib/multiprocessing/spawn.py b/Lib/multiprocessing/spawn.py index 4d769516cd..dfb9f65270 100644 --- a/Lib/multiprocessing/spawn.py +++ b/Lib/multiprocessing/spawn.py @@ -9,13 +9,13 @@ # import os -import pickle import sys import runpy import types from . import get_start_method, set_start_method from . import process +from .context import reduction from . import util __all__ = ['_main', 'freeze_support', 'set_executable', 'get_executable', @@ -96,8 +96,7 @@ def spawn_main(pipe_handle, parent_pid=None, tracker_fd=None): assert is_forking(sys.argv) if sys.platform == 'win32': import msvcrt - from .reduction import steal_handle - new_handle = steal_handle(parent_pid, pipe_handle) + new_handle = reduction.steal_handle(parent_pid, pipe_handle) fd = msvcrt.open_osfhandle(new_handle, os.O_RDONLY) else: from . import semaphore_tracker @@ -111,9 +110,9 @@ def _main(fd): with os.fdopen(fd, 'rb', closefd=True) as from_parent: process.current_process()._inheriting = True try: - preparation_data = pickle.load(from_parent) + preparation_data = reduction.pickle.load(from_parent) prepare(preparation_data) - self = pickle.load(from_parent) + self = reduction.pickle.load(from_parent) finally: del process.current_process()._inheriting return self._bootstrap() -- cgit v1.2.1 From dfed23bfd9ea75f545b69bb98ace0a7f90615c7b Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Fri, 9 Sep 2016 16:15:03 -0700 Subject: Rename test_strlit -> test_string_literals --- Lib/test/test_string_literals.py | 202 +++++++++++++++++++++++++++++++++++++++ Lib/test/test_strlit.py | 202 --------------------------------------- 2 files changed, 202 insertions(+), 202 deletions(-) create mode 100644 Lib/test/test_string_literals.py delete mode 100644 Lib/test/test_strlit.py (limited to 'Lib') diff --git a/Lib/test/test_string_literals.py b/Lib/test/test_string_literals.py new file mode 100644 index 0000000000..37ace230f5 --- /dev/null +++ b/Lib/test/test_string_literals.py @@ -0,0 +1,202 @@ +r"""Test correct treatment of various string literals by the parser. + +There are four types of string literals: + + 'abc' -- normal str + r'abc' -- raw str + b'xyz' -- normal bytes + br'xyz' | rb'xyz' -- raw bytes + +The difference between normal and raw strings is of course that in a +raw string, \ escapes (while still used to determine the end of the +literal) are not interpreted, so that r'\x00' contains four +characters: a backslash, an x, and two zeros; while '\x00' contains a +single character (code point zero). + +The tricky thing is what should happen when non-ASCII bytes are used +inside literals. For bytes literals, this is considered illegal. But +for str literals, those bytes are supposed to be decoded using the +encoding declared for the file (UTF-8 by default). + +We have to test this with various file encodings. We also test it with +exec()/eval(), which uses a different code path. + +This file is really about correct treatment of encodings and +backslashes. It doesn't concern itself with issues like single +vs. double quotes or singly- vs. triply-quoted strings: that's dealt +with elsewhere (I assume). +""" + +import os +import sys +import shutil +import tempfile +import unittest + + +TEMPLATE = r"""# coding: %s +a = 'x' +assert ord(a) == 120 +b = '\x01' +assert ord(b) == 1 +c = r'\x01' +assert list(map(ord, c)) == [92, 120, 48, 49] +d = '\x81' +assert ord(d) == 0x81 +e = r'\x81' +assert list(map(ord, e)) == [92, 120, 56, 49] +f = '\u1881' +assert ord(f) == 0x1881 +g = r'\u1881' +assert list(map(ord, g)) == [92, 117, 49, 56, 56, 49] +h = '\U0001d120' +assert ord(h) == 0x1d120 +i = r'\U0001d120' +assert list(map(ord, i)) == [92, 85, 48, 48, 48, 49, 100, 49, 50, 48] +""" + + +def byte(i): + return bytes([i]) + + +class TestLiterals(unittest.TestCase): + + def setUp(self): + self.save_path = sys.path[:] + self.tmpdir = tempfile.mkdtemp() + sys.path.insert(0, self.tmpdir) + + def tearDown(self): + sys.path[:] = self.save_path + shutil.rmtree(self.tmpdir, ignore_errors=True) + + def test_template(self): + # Check that the template doesn't contain any non-printables + # except for \n. + for c in TEMPLATE: + assert c == '\n' or ' ' <= c <= '~', repr(c) + + def test_eval_str_normal(self): + self.assertEqual(eval(""" 'x' """), 'x') + self.assertEqual(eval(r""" '\x01' """), chr(1)) + self.assertEqual(eval(""" '\x01' """), chr(1)) + self.assertEqual(eval(r""" '\x81' """), chr(0x81)) + self.assertEqual(eval(""" '\x81' """), chr(0x81)) + self.assertEqual(eval(r""" '\u1881' """), chr(0x1881)) + self.assertEqual(eval(""" '\u1881' """), chr(0x1881)) + self.assertEqual(eval(r""" '\U0001d120' """), chr(0x1d120)) + self.assertEqual(eval(""" '\U0001d120' """), chr(0x1d120)) + + def test_eval_str_incomplete(self): + self.assertRaises(SyntaxError, eval, r""" '\x' """) + self.assertRaises(SyntaxError, eval, r""" '\x0' """) + self.assertRaises(SyntaxError, eval, r""" '\u' """) + self.assertRaises(SyntaxError, eval, r""" '\u0' """) + self.assertRaises(SyntaxError, eval, r""" '\u00' """) + self.assertRaises(SyntaxError, eval, r""" '\u000' """) + self.assertRaises(SyntaxError, eval, r""" '\U' """) + self.assertRaises(SyntaxError, eval, r""" '\U0' """) + self.assertRaises(SyntaxError, eval, r""" '\U00' """) + self.assertRaises(SyntaxError, eval, r""" '\U000' """) + self.assertRaises(SyntaxError, eval, r""" '\U0000' """) + self.assertRaises(SyntaxError, eval, r""" '\U00000' """) + self.assertRaises(SyntaxError, eval, r""" '\U000000' """) + self.assertRaises(SyntaxError, eval, r""" '\U0000000' """) + + def test_eval_str_raw(self): + self.assertEqual(eval(""" r'x' """), 'x') + self.assertEqual(eval(r""" r'\x01' """), '\\' + 'x01') + self.assertEqual(eval(""" r'\x01' """), chr(1)) + self.assertEqual(eval(r""" r'\x81' """), '\\' + 'x81') + self.assertEqual(eval(""" r'\x81' """), chr(0x81)) + self.assertEqual(eval(r""" r'\u1881' """), '\\' + 'u1881') + self.assertEqual(eval(""" r'\u1881' """), chr(0x1881)) + self.assertEqual(eval(r""" r'\U0001d120' """), '\\' + 'U0001d120') + self.assertEqual(eval(""" r'\U0001d120' """), chr(0x1d120)) + + def test_eval_bytes_normal(self): + self.assertEqual(eval(""" b'x' """), b'x') + self.assertEqual(eval(r""" b'\x01' """), byte(1)) + self.assertEqual(eval(""" b'\x01' """), byte(1)) + self.assertEqual(eval(r""" b'\x81' """), byte(0x81)) + self.assertRaises(SyntaxError, eval, """ b'\x81' """) + self.assertEqual(eval(r""" br'\u1881' """), b'\\' + b'u1881') + self.assertRaises(SyntaxError, eval, """ b'\u1881' """) + self.assertEqual(eval(r""" br'\U0001d120' """), b'\\' + b'U0001d120') + self.assertRaises(SyntaxError, eval, """ b'\U0001d120' """) + + def test_eval_bytes_incomplete(self): + self.assertRaises(SyntaxError, eval, r""" b'\x' """) + self.assertRaises(SyntaxError, eval, r""" b'\x0' """) + + def test_eval_bytes_raw(self): + self.assertEqual(eval(""" br'x' """), b'x') + self.assertEqual(eval(""" rb'x' """), b'x') + self.assertEqual(eval(r""" br'\x01' """), b'\\' + b'x01') + self.assertEqual(eval(r""" rb'\x01' """), b'\\' + b'x01') + self.assertEqual(eval(""" br'\x01' """), byte(1)) + self.assertEqual(eval(""" rb'\x01' """), byte(1)) + self.assertEqual(eval(r""" br'\x81' """), b"\\" + b"x81") + self.assertEqual(eval(r""" rb'\x81' """), b"\\" + b"x81") + self.assertRaises(SyntaxError, eval, """ br'\x81' """) + self.assertRaises(SyntaxError, eval, """ rb'\x81' """) + self.assertEqual(eval(r""" br'\u1881' """), b"\\" + b"u1881") + self.assertEqual(eval(r""" rb'\u1881' """), b"\\" + b"u1881") + self.assertRaises(SyntaxError, eval, """ br'\u1881' """) + self.assertRaises(SyntaxError, eval, """ rb'\u1881' """) + self.assertEqual(eval(r""" br'\U0001d120' """), b"\\" + b"U0001d120") + self.assertEqual(eval(r""" rb'\U0001d120' """), b"\\" + b"U0001d120") + self.assertRaises(SyntaxError, eval, """ br'\U0001d120' """) + self.assertRaises(SyntaxError, eval, """ rb'\U0001d120' """) + self.assertRaises(SyntaxError, eval, """ bb'' """) + self.assertRaises(SyntaxError, eval, """ rr'' """) + self.assertRaises(SyntaxError, eval, """ brr'' """) + self.assertRaises(SyntaxError, eval, """ bbr'' """) + self.assertRaises(SyntaxError, eval, """ rrb'' """) + self.assertRaises(SyntaxError, eval, """ rbb'' """) + + def test_eval_str_u(self): + self.assertEqual(eval(""" u'x' """), 'x') + self.assertEqual(eval(""" U'\u00e4' """), 'ä') + self.assertEqual(eval(""" u'\N{LATIN SMALL LETTER A WITH DIAERESIS}' """), 'ä') + self.assertRaises(SyntaxError, eval, """ ur'' """) + self.assertRaises(SyntaxError, eval, """ ru'' """) + self.assertRaises(SyntaxError, eval, """ bu'' """) + self.assertRaises(SyntaxError, eval, """ ub'' """) + + def check_encoding(self, encoding, extra=""): + modname = "xx_" + encoding.replace("-", "_") + fn = os.path.join(self.tmpdir, modname + ".py") + f = open(fn, "w", encoding=encoding) + try: + f.write(TEMPLATE % encoding) + f.write(extra) + finally: + f.close() + __import__(modname) + del sys.modules[modname] + + def test_file_utf_8(self): + extra = "z = '\u1234'; assert ord(z) == 0x1234\n" + self.check_encoding("utf-8", extra) + + def test_file_utf_8_error(self): + extra = "b'\x80'\n" + self.assertRaises(SyntaxError, self.check_encoding, "utf-8", extra) + + def test_file_utf8(self): + self.check_encoding("utf-8") + + def test_file_iso_8859_1(self): + self.check_encoding("iso-8859-1") + + def test_file_latin_1(self): + self.check_encoding("latin-1") + + def test_file_latin9(self): + self.check_encoding("latin9") + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_strlit.py b/Lib/test/test_strlit.py deleted file mode 100644 index 37ace230f5..0000000000 --- a/Lib/test/test_strlit.py +++ /dev/null @@ -1,202 +0,0 @@ -r"""Test correct treatment of various string literals by the parser. - -There are four types of string literals: - - 'abc' -- normal str - r'abc' -- raw str - b'xyz' -- normal bytes - br'xyz' | rb'xyz' -- raw bytes - -The difference between normal and raw strings is of course that in a -raw string, \ escapes (while still used to determine the end of the -literal) are not interpreted, so that r'\x00' contains four -characters: a backslash, an x, and two zeros; while '\x00' contains a -single character (code point zero). - -The tricky thing is what should happen when non-ASCII bytes are used -inside literals. For bytes literals, this is considered illegal. But -for str literals, those bytes are supposed to be decoded using the -encoding declared for the file (UTF-8 by default). - -We have to test this with various file encodings. We also test it with -exec()/eval(), which uses a different code path. - -This file is really about correct treatment of encodings and -backslashes. It doesn't concern itself with issues like single -vs. double quotes or singly- vs. triply-quoted strings: that's dealt -with elsewhere (I assume). -""" - -import os -import sys -import shutil -import tempfile -import unittest - - -TEMPLATE = r"""# coding: %s -a = 'x' -assert ord(a) == 120 -b = '\x01' -assert ord(b) == 1 -c = r'\x01' -assert list(map(ord, c)) == [92, 120, 48, 49] -d = '\x81' -assert ord(d) == 0x81 -e = r'\x81' -assert list(map(ord, e)) == [92, 120, 56, 49] -f = '\u1881' -assert ord(f) == 0x1881 -g = r'\u1881' -assert list(map(ord, g)) == [92, 117, 49, 56, 56, 49] -h = '\U0001d120' -assert ord(h) == 0x1d120 -i = r'\U0001d120' -assert list(map(ord, i)) == [92, 85, 48, 48, 48, 49, 100, 49, 50, 48] -""" - - -def byte(i): - return bytes([i]) - - -class TestLiterals(unittest.TestCase): - - def setUp(self): - self.save_path = sys.path[:] - self.tmpdir = tempfile.mkdtemp() - sys.path.insert(0, self.tmpdir) - - def tearDown(self): - sys.path[:] = self.save_path - shutil.rmtree(self.tmpdir, ignore_errors=True) - - def test_template(self): - # Check that the template doesn't contain any non-printables - # except for \n. - for c in TEMPLATE: - assert c == '\n' or ' ' <= c <= '~', repr(c) - - def test_eval_str_normal(self): - self.assertEqual(eval(""" 'x' """), 'x') - self.assertEqual(eval(r""" '\x01' """), chr(1)) - self.assertEqual(eval(""" '\x01' """), chr(1)) - self.assertEqual(eval(r""" '\x81' """), chr(0x81)) - self.assertEqual(eval(""" '\x81' """), chr(0x81)) - self.assertEqual(eval(r""" '\u1881' """), chr(0x1881)) - self.assertEqual(eval(""" '\u1881' """), chr(0x1881)) - self.assertEqual(eval(r""" '\U0001d120' """), chr(0x1d120)) - self.assertEqual(eval(""" '\U0001d120' """), chr(0x1d120)) - - def test_eval_str_incomplete(self): - self.assertRaises(SyntaxError, eval, r""" '\x' """) - self.assertRaises(SyntaxError, eval, r""" '\x0' """) - self.assertRaises(SyntaxError, eval, r""" '\u' """) - self.assertRaises(SyntaxError, eval, r""" '\u0' """) - self.assertRaises(SyntaxError, eval, r""" '\u00' """) - self.assertRaises(SyntaxError, eval, r""" '\u000' """) - self.assertRaises(SyntaxError, eval, r""" '\U' """) - self.assertRaises(SyntaxError, eval, r""" '\U0' """) - self.assertRaises(SyntaxError, eval, r""" '\U00' """) - self.assertRaises(SyntaxError, eval, r""" '\U000' """) - self.assertRaises(SyntaxError, eval, r""" '\U0000' """) - self.assertRaises(SyntaxError, eval, r""" '\U00000' """) - self.assertRaises(SyntaxError, eval, r""" '\U000000' """) - self.assertRaises(SyntaxError, eval, r""" '\U0000000' """) - - def test_eval_str_raw(self): - self.assertEqual(eval(""" r'x' """), 'x') - self.assertEqual(eval(r""" r'\x01' """), '\\' + 'x01') - self.assertEqual(eval(""" r'\x01' """), chr(1)) - self.assertEqual(eval(r""" r'\x81' """), '\\' + 'x81') - self.assertEqual(eval(""" r'\x81' """), chr(0x81)) - self.assertEqual(eval(r""" r'\u1881' """), '\\' + 'u1881') - self.assertEqual(eval(""" r'\u1881' """), chr(0x1881)) - self.assertEqual(eval(r""" r'\U0001d120' """), '\\' + 'U0001d120') - self.assertEqual(eval(""" r'\U0001d120' """), chr(0x1d120)) - - def test_eval_bytes_normal(self): - self.assertEqual(eval(""" b'x' """), b'x') - self.assertEqual(eval(r""" b'\x01' """), byte(1)) - self.assertEqual(eval(""" b'\x01' """), byte(1)) - self.assertEqual(eval(r""" b'\x81' """), byte(0x81)) - self.assertRaises(SyntaxError, eval, """ b'\x81' """) - self.assertEqual(eval(r""" br'\u1881' """), b'\\' + b'u1881') - self.assertRaises(SyntaxError, eval, """ b'\u1881' """) - self.assertEqual(eval(r""" br'\U0001d120' """), b'\\' + b'U0001d120') - self.assertRaises(SyntaxError, eval, """ b'\U0001d120' """) - - def test_eval_bytes_incomplete(self): - self.assertRaises(SyntaxError, eval, r""" b'\x' """) - self.assertRaises(SyntaxError, eval, r""" b'\x0' """) - - def test_eval_bytes_raw(self): - self.assertEqual(eval(""" br'x' """), b'x') - self.assertEqual(eval(""" rb'x' """), b'x') - self.assertEqual(eval(r""" br'\x01' """), b'\\' + b'x01') - self.assertEqual(eval(r""" rb'\x01' """), b'\\' + b'x01') - self.assertEqual(eval(""" br'\x01' """), byte(1)) - self.assertEqual(eval(""" rb'\x01' """), byte(1)) - self.assertEqual(eval(r""" br'\x81' """), b"\\" + b"x81") - self.assertEqual(eval(r""" rb'\x81' """), b"\\" + b"x81") - self.assertRaises(SyntaxError, eval, """ br'\x81' """) - self.assertRaises(SyntaxError, eval, """ rb'\x81' """) - self.assertEqual(eval(r""" br'\u1881' """), b"\\" + b"u1881") - self.assertEqual(eval(r""" rb'\u1881' """), b"\\" + b"u1881") - self.assertRaises(SyntaxError, eval, """ br'\u1881' """) - self.assertRaises(SyntaxError, eval, """ rb'\u1881' """) - self.assertEqual(eval(r""" br'\U0001d120' """), b"\\" + b"U0001d120") - self.assertEqual(eval(r""" rb'\U0001d120' """), b"\\" + b"U0001d120") - self.assertRaises(SyntaxError, eval, """ br'\U0001d120' """) - self.assertRaises(SyntaxError, eval, """ rb'\U0001d120' """) - self.assertRaises(SyntaxError, eval, """ bb'' """) - self.assertRaises(SyntaxError, eval, """ rr'' """) - self.assertRaises(SyntaxError, eval, """ brr'' """) - self.assertRaises(SyntaxError, eval, """ bbr'' """) - self.assertRaises(SyntaxError, eval, """ rrb'' """) - self.assertRaises(SyntaxError, eval, """ rbb'' """) - - def test_eval_str_u(self): - self.assertEqual(eval(""" u'x' """), 'x') - self.assertEqual(eval(""" U'\u00e4' """), 'ä') - self.assertEqual(eval(""" u'\N{LATIN SMALL LETTER A WITH DIAERESIS}' """), 'ä') - self.assertRaises(SyntaxError, eval, """ ur'' """) - self.assertRaises(SyntaxError, eval, """ ru'' """) - self.assertRaises(SyntaxError, eval, """ bu'' """) - self.assertRaises(SyntaxError, eval, """ ub'' """) - - def check_encoding(self, encoding, extra=""): - modname = "xx_" + encoding.replace("-", "_") - fn = os.path.join(self.tmpdir, modname + ".py") - f = open(fn, "w", encoding=encoding) - try: - f.write(TEMPLATE % encoding) - f.write(extra) - finally: - f.close() - __import__(modname) - del sys.modules[modname] - - def test_file_utf_8(self): - extra = "z = '\u1234'; assert ord(z) == 0x1234\n" - self.check_encoding("utf-8", extra) - - def test_file_utf_8_error(self): - extra = "b'\x80'\n" - self.assertRaises(SyntaxError, self.check_encoding, "utf-8", extra) - - def test_file_utf8(self): - self.check_encoding("utf-8") - - def test_file_iso_8859_1(self): - self.check_encoding("iso-8859-1") - - def test_file_latin_1(self): - self.check_encoding("latin-1") - - def test_file_latin9(self): - self.check_encoding("latin9") - - -if __name__ == "__main__": - unittest.main() -- cgit v1.2.1 From a48be2f1f400d1d58196b72dc7c6b4bc1817da1a Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Fri, 9 Sep 2016 16:44:53 -0700 Subject: Merge --- Lib/urllib/request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py index 30bf6e051e..cc4f0bf089 100644 --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -1686,7 +1686,7 @@ class URLopener: self.proxies = proxies self.key_file = x509.get('key_file') self.cert_file = x509.get('cert_file') - self.addheaders = [('User-Agent', self.version)] + self.addheaders = [('User-Agent', self.version), ('Accept', '*/*')] self.__tempfiles = [] self.__unlink = os.unlink # See cleanup() self.tempcache = None -- cgit v1.2.1 From 414b977ce1d59f146d418873589ff0aa69c30abe Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Fri, 9 Sep 2016 17:03:58 -0700 Subject: Remove 2to3's fix_callable... We reintroduced the callable built-in pretty early on in the 3.x series (3.1 or 3.2?). --- Lib/lib2to3/fixes/fix_callable.py | 37 ---------------- Lib/lib2to3/tests/test_fixers.py | 92 --------------------------------------- 2 files changed, 129 deletions(-) delete mode 100644 Lib/lib2to3/fixes/fix_callable.py (limited to 'Lib') diff --git a/Lib/lib2to3/fixes/fix_callable.py b/Lib/lib2to3/fixes/fix_callable.py deleted file mode 100644 index 4c92b9c650..0000000000 --- a/Lib/lib2to3/fixes/fix_callable.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright 2007 Google, Inc. All Rights Reserved. -# Licensed to PSF under a Contributor Agreement. - -"""Fixer for callable(). - -This converts callable(obj) into isinstance(obj, collections.Callable), adding a -collections import if needed.""" - -# Local imports -from lib2to3 import fixer_base -from lib2to3.fixer_util import Call, Name, String, Attr, touch_import - -class FixCallable(fixer_base.BaseFix): - BM_compatible = True - - order = "pre" - - # Ignore callable(*args) or use of keywords. - # Either could be a hint that the builtin callable() is not being used. - PATTERN = """ - power< 'callable' - trailer< lpar='(' - ( not(arglist | argument) any ','> ) - rpar=')' > - after=any* - > - """ - - def transform(self, node, results): - func = results['func'] - - touch_import(None, 'collections', node=node) - - args = [func.clone(), String(', ')] - args.extend(Attr(Name('collections'), Name('Callable'))) - return Call(Name('isinstance'), args, prefix=node.prefix) diff --git a/Lib/lib2to3/tests/test_fixers.py b/Lib/lib2to3/tests/test_fixers.py index b97b73ab60..f349c6589a 100644 --- a/Lib/lib2to3/tests/test_fixers.py +++ b/Lib/lib2to3/tests/test_fixers.py @@ -2919,98 +2919,6 @@ class Test_unicode(FixerTestCase): a = f + r"""r'\\\u20ac\U0001d121\\u20ac'""" self.check(b, a) -class Test_callable(FixerTestCase): - fixer = "callable" - - def test_prefix_preservation(self): - b = """callable( x)""" - a = """import collections\nisinstance( x, collections.Callable)""" - self.check(b, a) - - b = """if callable(x): pass""" - a = """import collections -if isinstance(x, collections.Callable): pass""" - self.check(b, a) - - def test_callable_call(self): - b = """callable(x)""" - a = """import collections\nisinstance(x, collections.Callable)""" - self.check(b, a) - - def test_global_import(self): - b = """ -def spam(foo): - callable(foo)"""[1:] - a = """ -import collections -def spam(foo): - isinstance(foo, collections.Callable)"""[1:] - self.check(b, a) - - b = """ -import collections -def spam(foo): - callable(foo)"""[1:] - # same output if it was already imported - self.check(b, a) - - b = """ -from collections import * -def spam(foo): - callable(foo)"""[1:] - a = """ -from collections import * -import collections -def spam(foo): - isinstance(foo, collections.Callable)"""[1:] - self.check(b, a) - - b = """ -do_stuff() -do_some_other_stuff() -assert callable(do_stuff)"""[1:] - a = """ -import collections -do_stuff() -do_some_other_stuff() -assert isinstance(do_stuff, collections.Callable)"""[1:] - self.check(b, a) - - b = """ -if isinstance(do_stuff, Callable): - assert callable(do_stuff) - do_stuff(do_stuff) - if not callable(do_stuff): - exit(1) - else: - assert callable(do_stuff) -else: - assert not callable(do_stuff)"""[1:] - a = """ -import collections -if isinstance(do_stuff, Callable): - assert isinstance(do_stuff, collections.Callable) - do_stuff(do_stuff) - if not isinstance(do_stuff, collections.Callable): - exit(1) - else: - assert isinstance(do_stuff, collections.Callable) -else: - assert not isinstance(do_stuff, collections.Callable)"""[1:] - self.check(b, a) - - def test_callable_should_not_change(self): - a = """callable(*x)""" - self.unchanged(a) - - a = """callable(x, y)""" - self.unchanged(a) - - a = """callable(x, kw=y)""" - self.unchanged(a) - - a = """callable()""" - self.unchanged(a) class Test_filter(FixerTestCase): fixer = "filter" -- cgit v1.2.1 From 7d9e9f40d8903147693be8d2bbb4a129005ca49e Mon Sep 17 00:00:00 2001 From: ?ukasz Langa Date: Fri, 9 Sep 2016 17:37:37 -0700 Subject: DTrace support: function calls, GC activity, line execution Tested on macOS 10.11 dtrace, Ubuntu 16.04 SystemTap, and libbcc. Largely based by an initial patch by Jes?s Cea Avi?n, with some influence from Dave Malcolm's SystemTap patch and Nikhil Benesch's unification patch. Things deliberately left out for simplicity: - ustack helpers, I have no way of testing them at this point since they are Solaris-specific - PyFrameObject * in function__entry/function__return, this is SystemTap-specific - SPARC support - dynamic tracing - sys module dtrace facility introspection All of those might be added later. --- Lib/test/dtracedata/assert_usable.d | 5 + Lib/test/dtracedata/assert_usable.stp | 5 + Lib/test/dtracedata/call_stack.d | 31 +++++ Lib/test/dtracedata/call_stack.d.expected | 18 +++ Lib/test/dtracedata/call_stack.py | 30 +++++ Lib/test/dtracedata/call_stack.stp | 41 +++++++ Lib/test/dtracedata/call_stack.stp.expected | 14 +++ Lib/test/dtracedata/gc.d | 18 +++ Lib/test/dtracedata/gc.d.expected | 8 ++ Lib/test/dtracedata/gc.py | 13 ++ Lib/test/dtracedata/gc.stp | 26 ++++ Lib/test/dtracedata/gc.stp.expected | 8 ++ Lib/test/dtracedata/instance.py | 24 ++++ Lib/test/dtracedata/line.d | 7 ++ Lib/test/dtracedata/line.d.expected | 20 ++++ Lib/test/dtracedata/line.py | 17 +++ Lib/test/test_dtrace.py | 178 ++++++++++++++++++++++++++++ 17 files changed, 463 insertions(+) create mode 100644 Lib/test/dtracedata/assert_usable.d create mode 100644 Lib/test/dtracedata/assert_usable.stp create mode 100644 Lib/test/dtracedata/call_stack.d create mode 100644 Lib/test/dtracedata/call_stack.d.expected create mode 100644 Lib/test/dtracedata/call_stack.py create mode 100644 Lib/test/dtracedata/call_stack.stp create mode 100644 Lib/test/dtracedata/call_stack.stp.expected create mode 100644 Lib/test/dtracedata/gc.d create mode 100644 Lib/test/dtracedata/gc.d.expected create mode 100644 Lib/test/dtracedata/gc.py create mode 100644 Lib/test/dtracedata/gc.stp create mode 100644 Lib/test/dtracedata/gc.stp.expected create mode 100644 Lib/test/dtracedata/instance.py create mode 100644 Lib/test/dtracedata/line.d create mode 100644 Lib/test/dtracedata/line.d.expected create mode 100644 Lib/test/dtracedata/line.py create mode 100644 Lib/test/test_dtrace.py (limited to 'Lib') diff --git a/Lib/test/dtracedata/assert_usable.d b/Lib/test/dtracedata/assert_usable.d new file mode 100644 index 0000000000..0b2d4da66e --- /dev/null +++ b/Lib/test/dtracedata/assert_usable.d @@ -0,0 +1,5 @@ +BEGIN +{ + printf("probe: success\n"); + exit(0); +} diff --git a/Lib/test/dtracedata/assert_usable.stp b/Lib/test/dtracedata/assert_usable.stp new file mode 100644 index 0000000000..88e7e68e2c --- /dev/null +++ b/Lib/test/dtracedata/assert_usable.stp @@ -0,0 +1,5 @@ +probe begin +{ + println("probe: success") + exit () +} diff --git a/Lib/test/dtracedata/call_stack.d b/Lib/test/dtracedata/call_stack.d new file mode 100644 index 0000000000..450e939d4f --- /dev/null +++ b/Lib/test/dtracedata/call_stack.d @@ -0,0 +1,31 @@ +self int indent; + +python$target:::function-entry +/copyinstr(arg1) == "start"/ +{ + self->trace = 1; +} + +python$target:::function-entry +/self->trace/ +{ + printf("%d\t%*s:", timestamp, 15, probename); + printf("%*s", self->indent, ""); + printf("%s:%s:%d\n", basename(copyinstr(arg0)), copyinstr(arg1), arg2); + self->indent++; +} + +python$target:::function-return +/self->trace/ +{ + self->indent--; + printf("%d\t%*s:", timestamp, 15, probename); + printf("%*s", self->indent, ""); + printf("%s:%s:%d\n", basename(copyinstr(arg0)), copyinstr(arg1), arg2); +} + +python$target:::function-return +/copyinstr(arg1) == "start"/ +{ + self->trace = 0; +} diff --git a/Lib/test/dtracedata/call_stack.d.expected b/Lib/test/dtracedata/call_stack.d.expected new file mode 100644 index 0000000000..27849d1549 --- /dev/null +++ b/Lib/test/dtracedata/call_stack.d.expected @@ -0,0 +1,18 @@ + function-entry:call_stack.py:start:23 + function-entry: call_stack.py:function_1:1 + function-entry: call_stack.py:function_3:9 +function-return: call_stack.py:function_3:10 +function-return: call_stack.py:function_1:2 + function-entry: call_stack.py:function_2:5 + function-entry: call_stack.py:function_1:1 + function-entry: call_stack.py:function_3:9 +function-return: call_stack.py:function_3:10 +function-return: call_stack.py:function_1:2 +function-return: call_stack.py:function_2:6 + function-entry: call_stack.py:function_3:9 +function-return: call_stack.py:function_3:10 + function-entry: call_stack.py:function_4:13 +function-return: call_stack.py:function_4:14 + function-entry: call_stack.py:function_5:18 +function-return: call_stack.py:function_5:21 +function-return:call_stack.py:start:28 diff --git a/Lib/test/dtracedata/call_stack.py b/Lib/test/dtracedata/call_stack.py new file mode 100644 index 0000000000..ee9f3ae8d6 --- /dev/null +++ b/Lib/test/dtracedata/call_stack.py @@ -0,0 +1,30 @@ +def function_1(): + function_3(1, 2) + +# Check stacktrace +def function_2(): + function_1() + +# CALL_FUNCTION_VAR +def function_3(dummy, dummy2): + pass + +# CALL_FUNCTION_KW +def function_4(**dummy): + return 1 + return 2 # unreachable + +# CALL_FUNCTION_VAR_KW +def function_5(dummy, dummy2, **dummy3): + if False: + return 7 + return 8 + +def start(): + function_1() + function_2() + function_3(1, 2) + function_4(test=42) + function_5(*(1, 2), **{"test": 42}) + +start() diff --git a/Lib/test/dtracedata/call_stack.stp b/Lib/test/dtracedata/call_stack.stp new file mode 100644 index 0000000000..54082c202f --- /dev/null +++ b/Lib/test/dtracedata/call_stack.stp @@ -0,0 +1,41 @@ +global tracing + +function basename:string(path:string) +{ + last_token = token = tokenize(path, "/"); + while (token != "") { + last_token = token; + token = tokenize("", "/"); + } + return last_token; +} + +probe process.mark("function__entry") +{ + funcname = user_string($arg2); + + if (funcname == "start") { + tracing = 1; + } +} + +probe process.mark("function__entry"), process.mark("function__return") +{ + filename = user_string($arg1); + funcname = user_string($arg2); + lineno = $arg3; + + if (tracing) { + printf("%d\t%s:%s:%s:%d\n", gettimeofday_us(), $$name, + basename(filename), funcname, lineno); + } +} + +probe process.mark("function__return") +{ + funcname = user_string($arg2); + + if (funcname == "start") { + tracing = 0; + } +} diff --git a/Lib/test/dtracedata/call_stack.stp.expected b/Lib/test/dtracedata/call_stack.stp.expected new file mode 100644 index 0000000000..32cf396f82 --- /dev/null +++ b/Lib/test/dtracedata/call_stack.stp.expected @@ -0,0 +1,14 @@ +function__entry:call_stack.py:start:23 +function__entry:call_stack.py:function_1:1 +function__return:call_stack.py:function_1:2 +function__entry:call_stack.py:function_2:5 +function__entry:call_stack.py:function_1:1 +function__return:call_stack.py:function_1:2 +function__return:call_stack.py:function_2:6 +function__entry:call_stack.py:function_3:9 +function__return:call_stack.py:function_3:10 +function__entry:call_stack.py:function_4:13 +function__return:call_stack.py:function_4:14 +function__entry:call_stack.py:function_5:18 +function__return:call_stack.py:function_5:21 +function__return:call_stack.py:start:28 diff --git a/Lib/test/dtracedata/gc.d b/Lib/test/dtracedata/gc.d new file mode 100644 index 0000000000..4d91487b7e --- /dev/null +++ b/Lib/test/dtracedata/gc.d @@ -0,0 +1,18 @@ +python$target:::function-entry +/copyinstr(arg1) == "start"/ +{ + self->trace = 1; +} + +python$target:::gc-start, +python$target:::gc-done +/self->trace/ +{ + printf("%d\t%s:%ld\n", timestamp, probename, arg0); +} + +python$target:::function-return +/copyinstr(arg1) == "start"/ +{ + self->trace = 0; +} diff --git a/Lib/test/dtracedata/gc.d.expected b/Lib/test/dtracedata/gc.d.expected new file mode 100644 index 0000000000..8e5ac2a6d5 --- /dev/null +++ b/Lib/test/dtracedata/gc.d.expected @@ -0,0 +1,8 @@ +gc-start:0 +gc-done:0 +gc-start:1 +gc-done:0 +gc-start:2 +gc-done:0 +gc-start:2 +gc-done:1 diff --git a/Lib/test/dtracedata/gc.py b/Lib/test/dtracedata/gc.py new file mode 100644 index 0000000000..144a783b7b --- /dev/null +++ b/Lib/test/dtracedata/gc.py @@ -0,0 +1,13 @@ +import gc + +def start(): + gc.collect(0) + gc.collect(1) + gc.collect(2) + l = [] + l.append(l) + del l + gc.collect(2) + +gc.collect() +start() diff --git a/Lib/test/dtracedata/gc.stp b/Lib/test/dtracedata/gc.stp new file mode 100644 index 0000000000..162c6d3a22 --- /dev/null +++ b/Lib/test/dtracedata/gc.stp @@ -0,0 +1,26 @@ +global tracing + +probe process.mark("function__entry") +{ + funcname = user_string($arg2); + + if (funcname == "start") { + tracing = 1; + } +} + +probe process.mark("gc__start"), process.mark("gc__done") +{ + if (tracing) { + printf("%d\t%s:%ld\n", gettimeofday_us(), $$name, $arg1); + } +} + +probe process.mark("function__return") +{ + funcname = user_string($arg2); + + if (funcname == "start") { + tracing = 0; + } +} diff --git a/Lib/test/dtracedata/gc.stp.expected b/Lib/test/dtracedata/gc.stp.expected new file mode 100644 index 0000000000..7e6e6227fb --- /dev/null +++ b/Lib/test/dtracedata/gc.stp.expected @@ -0,0 +1,8 @@ +gc__start:0 +gc__done:0 +gc__start:1 +gc__done:0 +gc__start:2 +gc__done:0 +gc__start:2 +gc__done:1 diff --git a/Lib/test/dtracedata/instance.py b/Lib/test/dtracedata/instance.py new file mode 100644 index 0000000000..f1421378b0 --- /dev/null +++ b/Lib/test/dtracedata/instance.py @@ -0,0 +1,24 @@ +import gc + +class old_style_class(): + pass +class new_style_class(object): + pass + +a = old_style_class() +del a +gc.collect() +b = new_style_class() +del b +gc.collect() + +a = old_style_class() +del old_style_class +gc.collect() +b = new_style_class() +del new_style_class +gc.collect() +del a +gc.collect() +del b +gc.collect() diff --git a/Lib/test/dtracedata/line.d b/Lib/test/dtracedata/line.d new file mode 100644 index 0000000000..03f22db6fc --- /dev/null +++ b/Lib/test/dtracedata/line.d @@ -0,0 +1,7 @@ +python$target:::line +/(copyinstr(arg1)=="test_line")/ +{ + printf("%d\t%s:%s:%s:%d\n", timestamp, + probename, basename(copyinstr(arg0)), + copyinstr(arg1), arg2); +} diff --git a/Lib/test/dtracedata/line.d.expected b/Lib/test/dtracedata/line.d.expected new file mode 100644 index 0000000000..9b16ce76ee --- /dev/null +++ b/Lib/test/dtracedata/line.d.expected @@ -0,0 +1,20 @@ +line:line.py:test_line:2 +line:line.py:test_line:3 +line:line.py:test_line:4 +line:line.py:test_line:5 +line:line.py:test_line:6 +line:line.py:test_line:7 +line:line.py:test_line:8 +line:line.py:test_line:9 +line:line.py:test_line:10 +line:line.py:test_line:11 +line:line.py:test_line:4 +line:line.py:test_line:5 +line:line.py:test_line:6 +line:line.py:test_line:7 +line:line.py:test_line:8 +line:line.py:test_line:10 +line:line.py:test_line:11 +line:line.py:test_line:4 +line:line.py:test_line:12 +line:line.py:test_line:13 diff --git a/Lib/test/dtracedata/line.py b/Lib/test/dtracedata/line.py new file mode 100644 index 0000000000..0930ff391f --- /dev/null +++ b/Lib/test/dtracedata/line.py @@ -0,0 +1,17 @@ +def test_line(): + a = 1 + print('# Preamble', a) + for i in range(2): + a = i + b = i+2 + c = i+3 + if c < 4: + a = c + d = a + b +c + print('#', a, b, c, d) + a = 1 + print('# Epilogue', a) + + +if __name__ == '__main__': + test_line() diff --git a/Lib/test/test_dtrace.py b/Lib/test/test_dtrace.py new file mode 100644 index 0000000000..ca239b321b --- /dev/null +++ b/Lib/test/test_dtrace.py @@ -0,0 +1,178 @@ +import dis +import os.path +import re +import subprocess +import sys +import types +import unittest + +from test.support import findfile, run_unittest + + +def abspath(filename): + return os.path.abspath(findfile(filename, subdir="dtracedata")) + + +def normalize_trace_output(output): + """Normalize DTrace output for comparison. + + DTrace keeps a per-CPU buffer, and when showing the fired probes, buffers + are concatenated. So if the operating system moves our thread around, the + straight result can be "non-causal". So we add timestamps to the probe + firing, sort by that field, then strip it from the output""" + + # When compiling with '--with-pydebug', strip '[# refs]' debug output. + output = re.sub(r"\[[0-9]+ refs\]", "", output) + try: + result = [ + row.split("\t") + for row in output.splitlines() + if row and not row.startswith('#') + ] + result.sort(key=lambda row: int(row[0])) + result = [row[1] for row in result] + return "\n".join(result) + except (IndexError, ValueError): + raise AssertionError( + "tracer produced unparseable output:\n{}".format(output) + ) + + +class TraceBackend: + EXTENSION = None + COMMAND = None + COMMAND_ARGS = [] + + def run_case(self, name, optimize_python=None): + actual_output = normalize_trace_output(self.trace_python( + script_file=abspath(name + self.EXTENSION), + python_file=abspath(name + ".py"), + optimize_python=optimize_python)) + + with open(abspath(name + self.EXTENSION + ".expected")) as f: + expected_output = f.read().rstrip() + + return (expected_output, actual_output) + + def generate_trace_command(self, script_file, subcommand=None): + command = self.COMMAND + [script_file] + if subcommand: + command += ["-c", subcommand] + return command + + def trace(self, script_file, subcommand=None): + command = self.generate_trace_command(script_file, subcommand) + stdout, _ = subprocess.Popen(command, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True).communicate() + return stdout + + def trace_python(self, script_file, python_file, optimize_python=None): + python_flags = [] + if optimize_python: + python_flags.extend(["-O"] * optimize_python) + subcommand = " ".join([sys.executable] + python_flags + [python_file]) + return self.trace(script_file, subcommand) + + def assert_usable(self): + try: + output = self.trace(abspath("assert_usable" + self.EXTENSION)) + output = output.strip() + except FileNotFoundError as fnfe: + output = str(fnfe) + if output != "probe: success": + raise unittest.SkipTest( + "{}(1) failed: {}".format(self.COMMAND[0], output) + ) + + +class DTraceBackend(TraceBackend): + EXTENSION = ".d" + COMMAND = ["dtrace", "-q", "-s"] + + +class SystemTapBackend(TraceBackend): + EXTENSION = ".stp" + COMMAND = ["stap", "-g"] + + +class TraceTests(unittest.TestCase): + # unittest.TestCase options + maxDiff = None + + # TraceTests options + backend = None + optimize_python = 0 + + @classmethod + def setUpClass(self): + self.backend.assert_usable() + + def run_case(self, name): + actual_output, expected_output = self.backend.run_case( + name, optimize_python=self.optimize_python) + self.assertEqual(actual_output, expected_output) + + def test_function_entry_return(self): + self.run_case("call_stack") + + def test_verify_call_opcodes(self): + """Ensure our call stack test hits all function call opcodes""" + + opcodes = set(["CALL_FUNCTION", "CALL_FUNCTION_EX", "CALL_FUNCTION_KW"]) + + with open(abspath("call_stack.py")) as f: + code_string = f.read() + + def get_function_instructions(funcname): + # Recompile with appropriate optimization setting + code = compile(source=code_string, + filename="", + mode="exec", + optimize=self.optimize_python) + + for c in code.co_consts: + if isinstance(c, types.CodeType) and c.co_name == funcname: + return dis.get_instructions(c) + return [] + + for instruction in get_function_instructions('start'): + opcodes.discard(instruction.opname) + + self.assertEqual(set(), opcodes) + + def test_gc(self): + self.run_case("gc") + + def test_line(self): + self.run_case("line") + + +class DTraceNormalTests(TraceTests): + backend = DTraceBackend() + optimize_python = 0 + + +class DTraceOptimizedTests(TraceTests): + backend = DTraceBackend() + optimize_python = 2 + + +class SystemTapNormalTests(TraceTests): + backend = SystemTapBackend() + optimize_python = 0 + + +class SystemTapOptimizedTests(TraceTests): + backend = SystemTapBackend() + optimize_python = 2 + + +def test_main(): + run_unittest(DTraceNormalTests, DTraceOptimizedTests, SystemTapNormalTests, + SystemTapOptimizedTests) + + +if __name__ == '__main__': + test_main() -- cgit v1.2.1 From a5d8092435155dfc8bd6b6c58fb56e22bdb72831 Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Fri, 9 Sep 2016 17:59:49 -0700 Subject: Issue #28046: Remove platform-specific directories from sys.path --- Lib/sysconfig.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/sysconfig.py b/Lib/sysconfig.py index 7b78440ed3..c2f28f5454 100644 --- a/Lib/sysconfig.py +++ b/Lib/sysconfig.py @@ -341,6 +341,15 @@ def get_makefile_filename(): config_dir_name += '-%s' % sys.implementation._multiarch return os.path.join(get_path('stdlib'), config_dir_name, 'Makefile') + +def _get_sysconfigdata_name(): + return '_sysconfigdata_{abi}_{platform}_{multiarch}'.format( + abi=sys.abiflags, + platform=sys.platform, + multiarch=getattr(sys.implementation, '_multiarch', ''), + ) + + def _generate_posix_vars(): """Generate the Python module containing build-time variables.""" import pprint @@ -381,7 +390,7 @@ def _generate_posix_vars(): # _sysconfigdata module manually and populate it with the build vars. # This is more than sufficient for ensuring the subsequent call to # get_platform() succeeds. - name = '_sysconfigdata_' + sys.abiflags + name = _get_sysconfigdata_name() if 'darwin' in sys.platform: import types module = types.ModuleType(name) @@ -407,7 +416,7 @@ def _generate_posix_vars(): def _init_posix(vars): """Initialize the module as appropriate for POSIX systems.""" # _sysconfigdata is generated at build time, see _generate_posix_vars() - name = '_sysconfigdata_' + sys.abiflags + name = _get_sysconfigdata_name() _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0) build_time_vars = _temp.build_time_vars vars.update(build_time_vars) -- cgit v1.2.1 From d8eea9e1ca3787861b65da2fde7fcf79d3e8a3e8 Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Fri, 9 Sep 2016 18:29:10 -0700 Subject: Issue #28046: Fix distutils Why do we have two sysconfig modules again? --- Lib/distutils/sysconfig.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/distutils/sysconfig.py b/Lib/distutils/sysconfig.py index 681359870c..229626e1b4 100644 --- a/Lib/distutils/sysconfig.py +++ b/Lib/distutils/sysconfig.py @@ -418,7 +418,11 @@ _config_vars = None def _init_posix(): """Initialize the module as appropriate for POSIX systems.""" # _sysconfigdata is generated at build time, see the sysconfig module - name = '_sysconfigdata_' + sys.abiflags + name = '_sysconfigdata_{abi}_{platform}_{multiarch}'.format( + abi=sys.abiflags, + platform=sys.platform, + multiarch=getattr(sys.implementation, '_multiarch', ''), + ) _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0) build_time_vars = _temp.build_time_vars global _config_vars -- cgit v1.2.1 From 66bb1f875a7615c255128fb1e2fd6e9e50e4cefa Mon Sep 17 00:00:00 2001 From: "Eric V. Smith" Date: Fri, 9 Sep 2016 21:56:20 -0400 Subject: Issue 27948: Allow backslashes in the literal string portion of f-strings, but not in the expressions. Also, require expressions to begin and end with literal curly braces. --- Lib/http/client.py | 2 +- Lib/test/libregrtest/save_env.py | 2 +- Lib/test/test_faulthandler.py | 4 +- Lib/test/test_fstring.py | 132 +++++++++++++++++++++++------------- Lib/test/test_tools/test_unparse.py | 10 +-- Lib/test/test_traceback.py | 28 ++++---- Lib/traceback.py | 4 +- 7 files changed, 111 insertions(+), 71 deletions(-) (limited to 'Lib') diff --git a/Lib/http/client.py b/Lib/http/client.py index ad8f4104f4..6ee1913545 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -1060,7 +1060,7 @@ class HTTPConnection: if encode_chunked and self._http_vsn == 11: # chunked encoding - chunk = f'{len(chunk):X}''\r\n'.encode('ascii') + chunk \ + chunk = f'{len(chunk):X}\r\n'.encode('ascii') + chunk \ + b'\r\n' self.send(chunk) diff --git a/Lib/test/libregrtest/save_env.py b/Lib/test/libregrtest/save_env.py index eefbc14ad2..96ad3af8df 100644 --- a/Lib/test/libregrtest/save_env.py +++ b/Lib/test/libregrtest/save_env.py @@ -280,6 +280,6 @@ class saved_test_environment: print(f"Warning -- {name} was modified by {self.testname}", file=sys.stderr, flush=True) if self.verbose > 1: - print(f" Before: {original}""\n"f" After: {current} ", + print(f" Before: {original}\n After: {current} ", file=sys.stderr, flush=True) return False diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py index d2bd2d21e8..22ccbc9062 100644 --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py @@ -735,11 +735,11 @@ class FaultHandlerTests(unittest.TestCase): ('EXCEPTION_INT_DIVIDE_BY_ZERO', 'int divide by zero'), ('EXCEPTION_STACK_OVERFLOW', 'stack overflow'), ): - self.check_windows_exception(""" + self.check_windows_exception(f""" import faulthandler faulthandler.enable() faulthandler._raise_exception(faulthandler._{exc}) - """.format(exc=exc), + """, 3, name) diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index 2ba1b2169f..e61f63594f 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -119,6 +119,14 @@ f'{a * x()}'""" self.assertEqual(f'a}}', 'a}') self.assertEqual(f'}}b', '}b') self.assertEqual(f'a}}b', 'a}b') + self.assertEqual(f'{{}}', '{}') + self.assertEqual(f'a{{}}', 'a{}') + self.assertEqual(f'{{b}}', '{b}') + self.assertEqual(f'{{}}c', '{}c') + self.assertEqual(f'a{{b}}', 'a{b}') + self.assertEqual(f'a{{}}c', 'a{}c') + self.assertEqual(f'{{b}}c', '{b}c') + self.assertEqual(f'a{{b}}c', 'a{b}c') self.assertEqual(f'{{{10}', '{10') self.assertEqual(f'}}{10}', '}10') @@ -302,56 +310,79 @@ f'{a * x()}'""" ["f'{\n}'", ]) - def test_no_backslashes(self): - # See issue 27921 - - # These should work, but currently don't - self.assertAllRaise(SyntaxError, 'backslashes not allowed', - [r"f'\t'", - r"f'{2}\t'", - r"f'{2}\t{3}'", - r"f'\t{3}'", - - r"f'\N{GREEK CAPITAL LETTER DELTA}'", - r"f'{2}\N{GREEK CAPITAL LETTER DELTA}'", - r"f'{2}\N{GREEK CAPITAL LETTER DELTA}{3}'", - r"f'\N{GREEK CAPITAL LETTER DELTA}{3}'", - - r"f'\u0394'", - r"f'{2}\u0394'", - r"f'{2}\u0394{3}'", - r"f'\u0394{3}'", - - r"f'\U00000394'", - r"f'{2}\U00000394'", - r"f'{2}\U00000394{3}'", - r"f'\U00000394{3}'", - - r"f'\x20'", - r"f'{2}\x20'", - r"f'{2}\x20{3}'", - r"f'\x20{3}'", - - r"f'2\x20'", - r"f'2\x203'", - r"f'2\x203'", + def test_backslashes_in_string_part(self): + self.assertEqual(f'\t', '\t') + self.assertEqual(r'\t', '\\t') + self.assertEqual(rf'\t', '\\t') + self.assertEqual(f'{2}\t', '2\t') + self.assertEqual(f'{2}\t{3}', '2\t3') + self.assertEqual(f'\t{3}', '\t3') + + self.assertEqual(f'\u0394', '\u0394') + self.assertEqual(r'\u0394', '\\u0394') + self.assertEqual(rf'\u0394', '\\u0394') + self.assertEqual(f'{2}\u0394', '2\u0394') + self.assertEqual(f'{2}\u0394{3}', '2\u03943') + self.assertEqual(f'\u0394{3}', '\u03943') + + self.assertEqual(f'\U00000394', '\u0394') + self.assertEqual(r'\U00000394', '\\U00000394') + self.assertEqual(rf'\U00000394', '\\U00000394') + self.assertEqual(f'{2}\U00000394', '2\u0394') + self.assertEqual(f'{2}\U00000394{3}', '2\u03943') + self.assertEqual(f'\U00000394{3}', '\u03943') + + self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}', '\u0394') + self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}', '2\u0394') + self.assertEqual(f'{2}\N{GREEK CAPITAL LETTER DELTA}{3}', '2\u03943') + self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}{3}', '\u03943') + self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}', '2\u0394') + self.assertEqual(f'2\N{GREEK CAPITAL LETTER DELTA}3', '2\u03943') + self.assertEqual(f'\N{GREEK CAPITAL LETTER DELTA}3', '\u03943') + + self.assertEqual(f'\x20', ' ') + self.assertEqual(r'\x20', '\\x20') + self.assertEqual(rf'\x20', '\\x20') + self.assertEqual(f'{2}\x20', '2 ') + self.assertEqual(f'{2}\x20{3}', '2 3') + self.assertEqual(f'\x20{3}', ' 3') + + self.assertEqual(f'2\x20', '2 ') + self.assertEqual(f'2\x203', '2 3') + self.assertEqual(f'\x203', ' 3') + + def test_misformed_unicode_character_name(self): + # These test are needed because unicode names are parsed + # differently inside f-strings. + self.assertAllRaise(SyntaxError, r"\(unicode error\) 'unicodeescape' codec can't decode bytes in position .*: malformed \\N character escape", + [r"f'\N'", + r"f'\N{'", + r"f'\N{GREEK CAPITAL LETTER DELTA'", + + # Here are the non-f-string versions, + # which should give the same errors. + r"'\N'", + r"'\N{'", + r"'\N{GREEK CAPITAL LETTER DELTA'", ]) - # And these don't work now, and shouldn't work in the future. - self.assertAllRaise(SyntaxError, 'backslashes not allowed', + def test_no_backslashes_in_expression_part(self): + self.assertAllRaise(SyntaxError, 'f-string expression part cannot include a backslash', [r"f'{\'a\'}'", r"f'{\t3}'", + r"f'{\}'", + r"rf'{\'a\'}'", + r"rf'{\t3}'", + r"rf'{\}'", + r"""rf'{"\N{LEFT CURLY BRACKET}"}'""", ]) - # add this when backslashes are allowed again. see issue 27921 - # these test will be needed because unicode names will be parsed - # differently once backslashes are allowed inside expressions - ## def test_misformed_unicode_character_name(self): - ## self.assertAllRaise(SyntaxError, 'xx', - ## [r"f'\N'", - ## [r"f'\N{'", - ## [r"f'\N{GREEK CAPITAL LETTER DELTA'", - ## ]) + def test_no_escapes_for_braces(self): + # \x7b is '{'. Make sure it doesn't start an expression. + self.assertEqual(f'\x7b2}}', '{2}') + self.assertEqual(f'\x7b2', '{2') + self.assertEqual(f'\u007b2', '{2') + self.assertEqual(f'\N{LEFT CURLY BRACKET}2\N{RIGHT CURLY BRACKET}', '{2}') def test_newlines_in_expressions(self): self.assertEqual(f'{0}', '0') @@ -509,6 +540,14 @@ f'{a * x()}'""" "ruf''", "FUR''", "Fur''", + "fb''", + "fB''", + "Fb''", + "FB''", + "bf''", + "bF''", + "Bf''", + "BF''", ]) def test_leading_trailing_spaces(self): @@ -551,8 +590,8 @@ f'{a * x()}'""" self.assertAllRaise(SyntaxError, 'f-string: invalid conversion character', ["f'{3!g}'", "f'{3!A}'", - "f'{3!A}'", - "f'{3!A}'", + "f'{3!3}'", + "f'{3!G}'", "f'{3!!}'", "f'{3!:}'", "f'{3! s}'", # no space before conversion char @@ -601,6 +640,7 @@ f'{a * x()}'""" "f'{3!s:3'", "f'x{'", "f'x{x'", + "f'{x'", "f'{3:s'", "f'{{{'", "f'{{}}{'", diff --git a/Lib/test/test_tools/test_unparse.py b/Lib/test/test_tools/test_unparse.py index ed0001a15a..65dee1b5ae 100644 --- a/Lib/test/test_tools/test_unparse.py +++ b/Lib/test/test_tools/test_unparse.py @@ -285,12 +285,12 @@ class DirectoryTestCase(ASTTestCase): if test.support.verbose: print('Testing %s' % filename) - # it's very much a hack that I'm skipping these files, but - # I can't figure out why they fail. I'll fix it when I - # address issue #27948. - if os.path.basename(filename) in ('test_fstring.py', 'test_traceback.py'): + # Some f-strings are not correctly round-tripped by + # Tools/parser/unparse.py. See issue 28002 for details. + # We need to skip files that contain such f-strings. + if os.path.basename(filename) in ('test_fstring.py', ): if test.support.verbose: - print(f'Skipping {filename}: see issue 27921') + print(f'Skipping {filename}: see issue 28002') continue with self.subTest(filename=filename): diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 446b91e235..037d883ed4 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -326,13 +326,13 @@ class TracebackFormatTests(unittest.TestCase): lineno_f = f.__code__.co_firstlineno result_f = ( 'Traceback (most recent call last):\n' - f' File "{__file__}", line {lineno_f+5}, in _check_recursive_traceback_display''\n' + f' File "{__file__}", line {lineno_f+5}, in _check_recursive_traceback_display\n' ' f()\n' - f' File "{__file__}", line {lineno_f+1}, in f''\n' + f' File "{__file__}", line {lineno_f+1}, in f\n' ' f()\n' - f' File "{__file__}", line {lineno_f+1}, in f''\n' + f' File "{__file__}", line {lineno_f+1}, in f\n' ' f()\n' - f' File "{__file__}", line {lineno_f+1}, in f''\n' + f' File "{__file__}", line {lineno_f+1}, in f\n' ' f()\n' # XXX: The following line changes depending on whether the tests # are run through the interactive interpreter or with -m @@ -371,20 +371,20 @@ class TracebackFormatTests(unittest.TestCase): lineno_g = g.__code__.co_firstlineno result_g = ( - f' File "{__file__}", line {lineno_g+2}, in g''\n' + f' File "{__file__}", line {lineno_g+2}, in g\n' ' return g(count-1)\n' - f' File "{__file__}", line {lineno_g+2}, in g''\n' + f' File "{__file__}", line {lineno_g+2}, in g\n' ' return g(count-1)\n' - f' File "{__file__}", line {lineno_g+2}, in g''\n' + f' File "{__file__}", line {lineno_g+2}, in g\n' ' return g(count-1)\n' ' [Previous line repeated 6 more times]\n' - f' File "{__file__}", line {lineno_g+3}, in g''\n' + f' File "{__file__}", line {lineno_g+3}, in g\n' ' raise ValueError\n' 'ValueError\n' ) tb_line = ( 'Traceback (most recent call last):\n' - f' File "{__file__}", line {lineno_g+7}, in _check_recursive_traceback_display''\n' + f' File "{__file__}", line {lineno_g+7}, in _check_recursive_traceback_display\n' ' g()\n' ) expected = (tb_line + result_g).splitlines() @@ -408,16 +408,16 @@ class TracebackFormatTests(unittest.TestCase): lineno_h = h.__code__.co_firstlineno result_h = ( 'Traceback (most recent call last):\n' - f' File "{__file__}", line {lineno_h+7}, in _check_recursive_traceback_display''\n' + f' File "{__file__}", line {lineno_h+7}, in _check_recursive_traceback_display\n' ' h()\n' - f' File "{__file__}", line {lineno_h+2}, in h''\n' + f' File "{__file__}", line {lineno_h+2}, in h\n' ' return h(count-1)\n' - f' File "{__file__}", line {lineno_h+2}, in h''\n' + f' File "{__file__}", line {lineno_h+2}, in h\n' ' return h(count-1)\n' - f' File "{__file__}", line {lineno_h+2}, in h''\n' + f' File "{__file__}", line {lineno_h+2}, in h\n' ' return h(count-1)\n' ' [Previous line repeated 6 more times]\n' - f' File "{__file__}", line {lineno_h+3}, in h''\n' + f' File "{__file__}", line {lineno_h+3}, in h\n' ' g()\n' ) expected = (result_h + result_g).splitlines() diff --git a/Lib/traceback.py b/Lib/traceback.py index a15b818565..a1cb5fb1ef 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -402,7 +402,7 @@ class StackSummary(list): count += 1 else: if count > 3: - result.append(f' [Previous line repeated {count-3} more times]'+'\n') + result.append(f' [Previous line repeated {count-3} more times]\n') last_file = frame.filename last_line = frame.lineno last_name = frame.name @@ -419,7 +419,7 @@ class StackSummary(list): row.append(' {name} = {value}\n'.format(name=name, value=value)) result.append(''.join(row)) if count > 3: - result.append(f' [Previous line repeated {count-3} more times]'+'\n') + result.append(f' [Previous line repeated {count-3} more times]\n') return result -- cgit v1.2.1 From 4f05fcfb622ac6ed16a0b5a84cf1e657d98ed54e Mon Sep 17 00:00:00 2001 From: ?ukasz Langa Date: Fri, 9 Sep 2016 19:48:14 -0700 Subject: Issue #27199: TarFile expose copyfileobj bufsize to improve throughput Patch by Jason Fried. --- Lib/tarfile.py | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) (limited to 'Lib') diff --git a/Lib/tarfile.py b/Lib/tarfile.py index 960c673067..a62ab82545 100755 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -228,21 +228,21 @@ def calc_chksums(buf): signed_chksum = 256 + sum(struct.unpack_from("148b8x356b", buf)) return unsigned_chksum, signed_chksum -def copyfileobj(src, dst, length=None, exception=OSError): +def copyfileobj(src, dst, length=None, exception=OSError, bufsize=None): """Copy length bytes from fileobj src to fileobj dst. If length is None, copy the entire content. """ + bufsize = bufsize or 16 * 1024 if length == 0: return if length is None: - shutil.copyfileobj(src, dst) + shutil.copyfileobj(src, dst, bufsize) return - BUFSIZE = 16 * 1024 - blocks, remainder = divmod(length, BUFSIZE) + blocks, remainder = divmod(length, bufsize) for b in range(blocks): - buf = src.read(BUFSIZE) - if len(buf) < BUFSIZE: + buf = src.read(bufsize) + if len(buf) < bufsize: raise exception("unexpected end of data") dst.write(buf) @@ -1403,7 +1403,8 @@ class TarFile(object): def __init__(self, name=None, mode="r", fileobj=None, format=None, tarinfo=None, dereference=None, ignore_zeros=None, encoding=None, - errors="surrogateescape", pax_headers=None, debug=None, errorlevel=None): + errors="surrogateescape", pax_headers=None, debug=None, + errorlevel=None, copybufsize=None): """Open an (uncompressed) tar archive `name'. `mode' is either 'r' to read from an existing archive, 'a' to append data to an existing file or 'w' to create a new file overwriting an existing one. `mode' @@ -1459,6 +1460,7 @@ class TarFile(object): self.errorlevel = errorlevel # Init datastructures. + self.copybufsize = copybufsize self.closed = False self.members = [] # list of members as TarInfo objects self._loaded = False # flag if all members have been read @@ -1558,7 +1560,7 @@ class TarFile(object): saved_pos = fileobj.tell() try: return func(name, "r", fileobj, **kwargs) - except (ReadError, CompressionError) as e: + except (ReadError, CompressionError): if fileobj is not None: fileobj.seek(saved_pos) continue @@ -1963,10 +1965,10 @@ class TarFile(object): buf = tarinfo.tobuf(self.format, self.encoding, self.errors) self.fileobj.write(buf) self.offset += len(buf) - + bufsize=self.copybufsize # If there's data to follow, append it. if fileobj is not None: - copyfileobj(fileobj, self.fileobj, tarinfo.size) + copyfileobj(fileobj, self.fileobj, tarinfo.size, bufsize=bufsize) blocks, remainder = divmod(tarinfo.size, BLOCKSIZE) if remainder > 0: self.fileobj.write(NUL * (BLOCKSIZE - remainder)) @@ -2148,15 +2150,16 @@ class TarFile(object): """ source = self.fileobj source.seek(tarinfo.offset_data) + bufsize = self.copybufsize with bltn_open(targetpath, "wb") as target: if tarinfo.sparse is not None: for offset, size in tarinfo.sparse: target.seek(offset) - copyfileobj(source, target, size, ReadError) + copyfileobj(source, target, size, ReadError, bufsize) target.seek(tarinfo.size) target.truncate() else: - copyfileobj(source, target, tarinfo.size, ReadError) + copyfileobj(source, target, tarinfo.size, ReadError, bufsize) def makeunknown(self, tarinfo, targetpath): """Make a file from a TarInfo object with an unknown type @@ -2235,7 +2238,7 @@ class TarFile(object): os.lchown(targetpath, u, g) else: os.chown(targetpath, u, g) - except OSError as e: + except OSError: raise ExtractError("could not change owner") def chmod(self, tarinfo, targetpath): @@ -2244,7 +2247,7 @@ class TarFile(object): if hasattr(os, 'chmod'): try: os.chmod(targetpath, tarinfo.mode) - except OSError as e: + except OSError: raise ExtractError("could not change mode") def utime(self, tarinfo, targetpath): @@ -2254,7 +2257,7 @@ class TarFile(object): return try: os.utime(targetpath, (tarinfo.mtime, tarinfo.mtime)) - except OSError as e: + except OSError: raise ExtractError("could not change modification time") #-------------------------------------------------------------------------- -- cgit v1.2.1 From 9c78a93118dbae1dda426dcdc34a694d810f13ed Mon Sep 17 00:00:00 2001 From: "Eric V. Smith" Date: Fri, 9 Sep 2016 23:06:47 -0400 Subject: Issue 27080: PEP 515: add '_' formatting option. --- Lib/test/test_long.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py index 4cc4b05c7f..fd15f04ace 100644 --- a/Lib/test/test_long.py +++ b/Lib/test/test_long.py @@ -621,6 +621,8 @@ class LongTest(unittest.TestCase): def test__format__(self): self.assertEqual(format(123456789, 'd'), '123456789') self.assertEqual(format(123456789, 'd'), '123456789') + self.assertEqual(format(123456789, ','), '123,456,789') + self.assertEqual(format(123456789, '_'), '123_456_789') # sign and aligning are interdependent self.assertEqual(format(1, "-"), '1') @@ -649,8 +651,25 @@ class LongTest(unittest.TestCase): self.assertEqual(format(int('be', 16), "X"), "BE") self.assertEqual(format(-int('be', 16), "x"), "-be") self.assertEqual(format(-int('be', 16), "X"), "-BE") + self.assertRaises(ValueError, format, 1234567890, ',x') + self.assertEqual(format(1234567890, '_x'), '4996_02d2') + self.assertEqual(format(1234567890, '_X'), '4996_02D2') # octal + self.assertEqual(format(3, "o"), "3") + self.assertEqual(format(-3, "o"), "-3") + self.assertEqual(format(1234, "o"), "2322") + self.assertEqual(format(-1234, "o"), "-2322") + self.assertEqual(format(1234, "-o"), "2322") + self.assertEqual(format(-1234, "-o"), "-2322") + self.assertEqual(format(1234, " o"), " 2322") + self.assertEqual(format(-1234, " o"), "-2322") + self.assertEqual(format(1234, "+o"), "+2322") + self.assertEqual(format(-1234, "+o"), "-2322") + self.assertRaises(ValueError, format, 1234567890, ',o') + self.assertEqual(format(1234567890, '_o'), '111_4540_1322') + + # binary self.assertEqual(format(3, "b"), "11") self.assertEqual(format(-3, "b"), "-11") self.assertEqual(format(1234, "b"), "10011010010") @@ -661,12 +680,21 @@ class LongTest(unittest.TestCase): self.assertEqual(format(-1234, " b"), "-10011010010") self.assertEqual(format(1234, "+b"), "+10011010010") self.assertEqual(format(-1234, "+b"), "-10011010010") + self.assertRaises(ValueError, format, 1234567890, ',b') + self.assertEqual(format(12345, '_b'), '11_0000_0011_1001') # make sure these are errors self.assertRaises(ValueError, format, 3, "1.3") # precision disallowed + self.assertRaises(ValueError, format, 3, "_c") # underscore, + self.assertRaises(ValueError, format, 3, ",c") # comma, and self.assertRaises(ValueError, format, 3, "+c") # sign not allowed # with 'c' + self.assertRaisesRegex(ValueError, 'Cannot specify both', format, 3, '_,') + self.assertRaisesRegex(ValueError, 'Cannot specify both', format, 3, ',_') + self.assertRaisesRegex(ValueError, 'Cannot specify both', format, 3, '_,d') + self.assertRaisesRegex(ValueError, 'Cannot specify both', format, 3, ',_d') + # ensure that only int and float type specifiers work for format_spec in ([chr(x) for x in range(ord('a'), ord('z')+1)] + [chr(x) for x in range(ord('A'), ord('Z')+1)]): -- cgit v1.2.1 From 3543dd9d797b6d23a3ccf522c549f999ff7b1f33 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 9 Sep 2016 19:28:36 -0700 Subject: Fix SystemError in compact dict Issue #28040: Fix _PyDict_DelItem_KnownHash() and _PyDict_Pop(): convert splitted table to combined table to be able to delete the item. Write an unit test for the issue. Patch by INADA Naoki. --- Lib/test/test_dict.py | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py index 6c4706636e..fb954c8132 100644 --- a/Lib/test/test_dict.py +++ b/Lib/test/test_dict.py @@ -4,6 +4,7 @@ import gc import pickle import random import string +import sys import unittest import weakref from test import support @@ -838,6 +839,74 @@ class DictTest(unittest.TestCase): pass self._tracked(MyDict()) + def make_shared_key_dict(self, n): + class C: + pass + + dicts = [] + for i in range(n): + a = C() + a.x, a.y, a.z = 1, 2, 3 + dicts.append(a.__dict__) + + return dicts + + @support.cpython_only + def test_splittable_del(self): + """split table must be combined when del d[k]""" + a, b = self.make_shared_key_dict(2) + + orig_size = sys.getsizeof(a) + + del a['y'] # split table is combined + with self.assertRaises(KeyError): + del a['y'] + + self.assertGreater(sys.getsizeof(a), orig_size) + self.assertEqual(list(a), ['x', 'z']) + self.assertEqual(list(b), ['x', 'y', 'z']) + + # Two dicts have different insertion order. + a['y'] = 42 + self.assertEqual(list(a), ['x', 'z', 'y']) + self.assertEqual(list(b), ['x', 'y', 'z']) + + @support.cpython_only + def test_splittable_pop(self): + """split table must be combined when d.pop(k)""" + a, b = self.make_shared_key_dict(2) + + orig_size = sys.getsizeof(a) + + a.pop('y') # split table is combined + with self.assertRaises(KeyError): + a.pop('y') + + self.assertGreater(sys.getsizeof(a), orig_size) + self.assertEqual(list(a), ['x', 'z']) + self.assertEqual(list(b), ['x', 'y', 'z']) + + # Two dicts have different insertion order. + a['y'] = 42 + self.assertEqual(list(a), ['x', 'z', 'y']) + self.assertEqual(list(b), ['x', 'y', 'z']) + + @support.cpython_only + def test_splittable_popitem(self): + """split table must be combined when d.popitem()""" + a, b = self.make_shared_key_dict(2) + + orig_size = sys.getsizeof(a) + + item = a.popitem() # split table is combined + self.assertEqual(item, ('z', 3)) + with self.assertRaises(KeyError): + del a['z'] + + self.assertGreater(sys.getsizeof(a), orig_size) + self.assertEqual(list(a), ['x', 'y']) + self.assertEqual(list(b), ['x', 'y', 'z']) + def test_iterator_pickling(self): for proto in range(pickle.HIGHEST_PROTOCOL + 1): data = {1:"a", 2:"b", 3:"c"} -- cgit v1.2.1 From bba256f0139ad32e7ba24d4a0d7a00165f3c38f3 Mon Sep 17 00:00:00 2001 From: R David Murray Date: Sat, 10 Sep 2016 00:22:25 -0400 Subject: #20476: Deal with the message_factory circular import differently. It turns out we can't depend on email.message getting imported every place message_factory is needed, so to avoid a circular import we need to special case Policy.message_factory=None in the parser instead of using monkey patching. I had a feeling that was a bad idea when I did it. --- Lib/email/_policybase.py | 2 +- Lib/email/feedparser.py | 6 +++++- Lib/email/message.py | 3 --- Lib/test/test_email/test_policy.py | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) (limited to 'Lib') diff --git a/Lib/email/_policybase.py b/Lib/email/_policybase.py index d6994844e1..df4649676a 100644 --- a/Lib/email/_policybase.py +++ b/Lib/email/_policybase.py @@ -155,6 +155,7 @@ class Policy(_PolicyBase, metaclass=abc.ABCMeta): serialized by a generator. Default: True. message_factory -- the class to use to create new message objects. + If the value is None, the default is Message. """ @@ -163,7 +164,6 @@ class Policy(_PolicyBase, metaclass=abc.ABCMeta): cte_type = '8bit' max_line_length = 78 mangle_from_ = False - # XXX To avoid circular imports, this is set in email.message. message_factory = None def handle_defect(self, obj, defect): diff --git a/Lib/email/feedparser.py b/Lib/email/feedparser.py index 3d74978cdb..7c07ca8645 100644 --- a/Lib/email/feedparser.py +++ b/Lib/email/feedparser.py @@ -147,7 +147,11 @@ class FeedParser: self.policy = policy self._old_style_factory = False if _factory is None: - self._factory = policy.message_factory + if policy.message_factory is None: + from email.message import Message + self._factory = Message + else: + self._factory = policy.message_factory else: self._factory = _factory try: diff --git a/Lib/email/message.py b/Lib/email/message.py index f4380d931a..b6512f2198 100644 --- a/Lib/email/message.py +++ b/Lib/email/message.py @@ -1162,6 +1162,3 @@ class EmailMessage(MIMEPart): super().set_content(*args, **kw) if 'MIME-Version' not in self: self['MIME-Version'] = '1.0' - -# Set message_factory on Policy here to avoid a circular import. -Policy.message_factory = Message diff --git a/Lib/test/test_email/test_policy.py b/Lib/test/test_email/test_policy.py index 1d95d03bf5..8fecb8a5fc 100644 --- a/Lib/test/test_email/test_policy.py +++ b/Lib/test/test_email/test_policy.py @@ -24,7 +24,7 @@ class PolicyAPITests(unittest.TestCase): 'cte_type': '8bit', 'raise_on_defect': False, 'mangle_from_': True, - 'message_factory': email.message.Message, + 'message_factory': None, } # These default values are the ones set on email.policy.default. # If any of these defaults change, the docs must be updated. -- cgit v1.2.1 From 8f848964101f46df19c3cf6be771845efce76842 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 9 Sep 2016 21:51:19 -0700 Subject: Try to fix sizeof unit tests on dict Issue #28056 and issue #26058. --- Lib/test/test_ordered_dict.py | 3 ++- Lib/test/test_sys.py | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_ordered_dict.py b/Lib/test/test_ordered_dict.py index 2da36d3032..a35ed12436 100644 --- a/Lib/test/test_ordered_dict.py +++ b/Lib/test/test_ordered_dict.py @@ -668,7 +668,8 @@ class CPythonOrderedDictTests(OrderedDictTests, unittest.TestCase): size = support.calcobjsize check = self.check_sizeof - basicsize = size('n2P3PnPn2P') + 8 + calcsize('2nP2n') + basicsize = size('nQ2P' + '3PnPn2P') + calcsize('2nP2n') + entrysize = calcsize('n2P') p = calcsize('P') nodesize = calcsize('Pn2P') diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 37ee0b0324..df9ebd4085 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -937,9 +937,9 @@ class SizeofTest(unittest.TestCase): # method-wrapper (descriptor object) check({}.__iter__, size('2P')) # dict - check({}, size('n2P') + 8 + calcsize('2nP2n') + 8 + (8*2//3)*calcsize('n2P')) + check({}, size('nQ2P') + calcsize('2nP2n') + 8 + (8*2//3)*calcsize('n2P')) longdict = {1:1, 2:2, 3:3, 4:4, 5:5, 6:6, 7:7, 8:8} - check(longdict, size('n2P') + 8 + calcsize('2nP2n') + 16 + (16*2//3)*calcsize('n2P')) + check(longdict, size('nQ2P') + calcsize('2nP2n') + 16 + (16*2//3)*calcsize('n2P')) # dictionary-keyview check({}.keys(), size('P')) # dictionary-valueview @@ -1103,7 +1103,7 @@ class SizeofTest(unittest.TestCase): class newstyleclass(object): pass check(newstyleclass, s) # dict with shared keys - check(newstyleclass().__dict__, size('n2P' + '2nP2n') + 8) + check(newstyleclass().__dict__, size('nQ2P' + '2nP2n')) # unicode # each tuple contains a string and its expected character size # don't put any static strings here, as they may contain -- cgit v1.2.1 From a67d22124d294b6b165eda9fd65bb08997ab6f53 Mon Sep 17 00:00:00 2001 From: ?ukasz Langa Date: Fri, 9 Sep 2016 22:21:17 -0700 Subject: Issue #18401: pdb tests don't read ~/.pdbrc anymore Patch by Martin Matusiak and Sam Kimbrel. --- Lib/pdb.py | 22 +++++++++------- Lib/test/test_pdb.py | 74 ++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 67 insertions(+), 29 deletions(-) (limited to 'Lib') diff --git a/Lib/pdb.py b/Lib/pdb.py index b11ac0abd1..7eb78b922a 100755 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -52,7 +52,8 @@ If a file ".pdbrc" exists in your home directory or in the current directory, it is read in and executed as if it had been typed at the debugger prompt. This is particularly useful for aliases. If both files exist, the one in the home directory is read first and aliases -defined there can be overridden by the local file. +defined there can be overridden by the local file. This behavior can be +disabled by passing the "readrc=False" argument to the Pdb constructor. Aside from aliases, the debugger is not directly programmable; but it is implemented as a class from which you can derive your own debugger @@ -135,7 +136,7 @@ line_prefix = '\n-> ' # Probably a better default class Pdb(bdb.Bdb, cmd.Cmd): def __init__(self, completekey='tab', stdin=None, stdout=None, skip=None, - nosigint=False): + nosigint=False, readrc=True): bdb.Bdb.__init__(self, skip=skip) cmd.Cmd.__init__(self, completekey, stdin, stdout) if stdout: @@ -158,18 +159,19 @@ class Pdb(bdb.Bdb, cmd.Cmd): # Read $HOME/.pdbrc and ./.pdbrc self.rcLines = [] - if 'HOME' in os.environ: - envHome = os.environ['HOME'] + if readrc: + if 'HOME' in os.environ: + envHome = os.environ['HOME'] + try: + with open(os.path.join(envHome, ".pdbrc")) as rcFile: + self.rcLines.extend(rcFile) + except OSError: + pass try: - with open(os.path.join(envHome, ".pdbrc")) as rcFile: + with open(".pdbrc") as rcFile: self.rcLines.extend(rcFile) except OSError: pass - try: - with open(".pdbrc") as rcFile: - self.rcLines.extend(rcFile) - except OSError: - pass self.commands = {} # associates a command list to breakpoint numbers self.commands_doprompt = {} # for each bp num, tells if the prompt diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index a63ccd83ca..c39cc97fe1 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -1,8 +1,10 @@ # A test suite for pdb; not very comprehensive at the moment. import doctest +import os import pdb import sys +import tempfile import types import unittest import subprocess @@ -34,7 +36,7 @@ def test_pdb_displayhook(): """This tests the custom displayhook for pdb. >>> def test_function(foo, bar): - ... import pdb; pdb.Pdb(nosigint=True).set_trace() + ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() ... pass >>> with PdbTestInput([ @@ -74,7 +76,7 @@ def test_pdb_basic_commands(): ... return foo.upper() >>> def test_function(): - ... import pdb; pdb.Pdb(nosigint=True).set_trace() + ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() ... ret = test_function_2('baz') ... print(ret) @@ -173,7 +175,7 @@ def test_pdb_breakpoint_commands(): """Test basic commands related to breakpoints. >>> def test_function(): - ... import pdb; pdb.Pdb(nosigint=True).set_trace() + ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() ... print(1) ... print(2) ... print(3) @@ -305,7 +307,7 @@ def test_list_commands(): ... return foo >>> def test_function(): - ... import pdb; pdb.Pdb(nosigint=True).set_trace() + ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() ... ret = test_function_2('baz') >>> with PdbTestInput([ # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE @@ -328,7 +330,7 @@ def test_list_commands(): -> ret = test_function_2('baz') (Pdb) list 1 def test_function(): - 2 import pdb; pdb.Pdb(nosigint=True).set_trace() + 2 import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() 3 -> ret = test_function_2('baz') [EOF] (Pdb) step @@ -391,7 +393,7 @@ def test_post_mortem(): ... print('Exception!') >>> def test_function(): - ... import pdb; pdb.Pdb(nosigint=True).set_trace() + ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() ... test_function_2() ... print('Not reached.') @@ -424,7 +426,7 @@ def test_post_mortem(): -> 1/0 (Pdb) list 1 def test_function(): - 2 import pdb; pdb.Pdb(nosigint=True).set_trace() + 2 import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() 3 -> test_function_2() 4 print('Not reached.') [EOF] @@ -448,7 +450,7 @@ def test_pdb_skip_modules(): >>> def skip_module(): ... import string - ... import pdb; pdb.Pdb(skip=['stri*'], nosigint=True).set_trace() + ... import pdb; pdb.Pdb(skip=['stri*'], nosigint=True, readrc=False).set_trace() ... string.capwords('FOO') >>> with PdbTestInput([ @@ -477,7 +479,7 @@ def test_pdb_skip_modules_with_callback(): >>> def skip_module(): ... def callback(): ... return None - ... import pdb; pdb.Pdb(skip=['module_to_skip*'], nosigint=True).set_trace() + ... import pdb; pdb.Pdb(skip=['module_to_skip*'], nosigint=True, readrc=False).set_trace() ... mod.foo_pony(callback) >>> with PdbTestInput([ @@ -518,7 +520,7 @@ def test_pdb_continue_in_bottomframe(): """Test that "continue" and "next" work properly in bottom frame (issue #5294). >>> def test_function(): - ... import pdb, sys; inst = pdb.Pdb(nosigint=True) + ... import pdb, sys; inst = pdb.Pdb(nosigint=True, readrc=False) ... inst.set_trace() ... inst.botframe = sys._getframe() # hackery to get the right botframe ... print(1) @@ -558,7 +560,7 @@ def test_pdb_continue_in_bottomframe(): def pdb_invoke(method, arg): """Run pdb.method(arg).""" - getattr(pdb.Pdb(nosigint=True), method)(arg) + getattr(pdb.Pdb(nosigint=True, readrc=False), method)(arg) def test_pdb_run_with_incorrect_argument(): @@ -607,7 +609,7 @@ def test_next_until_return_at_return_event(): ... x = 2 >>> def test_function(): - ... import pdb; pdb.Pdb(nosigint=True).set_trace() + ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() ... test_function_2() ... test_function_2() ... test_function_2() @@ -673,7 +675,7 @@ def test_pdb_next_command_for_generator(): ... yield 2 >>> def test_function(): - ... import pdb; pdb.Pdb(nosigint=True).set_trace() + ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() ... it = test_gen() ... try: ... if next(it) != 0: @@ -733,7 +735,7 @@ def test_pdb_return_command_for_generator(): ... yield 2 >>> def test_function(): - ... import pdb; pdb.Pdb(nosigint=True).set_trace() + ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() ... it = test_gen() ... try: ... if next(it) != 0: @@ -788,7 +790,7 @@ def test_pdb_until_command_for_generator(): ... yield 2 >>> def test_function(): - ... import pdb; pdb.Pdb(nosigint=True).set_trace() + ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() ... for i in test_gen(): ... print(i) ... print("finished") @@ -830,7 +832,7 @@ def test_pdb_next_command_in_generator_for_loop(): ... return 1 >>> def test_function(): - ... import pdb; pdb.Pdb(nosigint=True).set_trace() + ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() ... for i in test_gen(): ... print('value', i) ... x = 123 @@ -875,7 +877,7 @@ def test_pdb_next_command_subiterator(): ... return x >>> def test_function(): - ... import pdb; pdb.Pdb(nosigint=True).set_trace() + ... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace() ... for i in test_gen(): ... print('value', i) ... x = 123 @@ -1025,7 +1027,7 @@ class PdbTestCase(unittest.TestCase): import pdb def start_pdb(): - pdb.Pdb().set_trace() + pdb.Pdb(readrc=False).set_trace() x = 1 y = 1 @@ -1054,13 +1056,47 @@ class PdbTestCase(unittest.TestCase): .format(expected, stdout)) + def test_readrc_kwarg(self): + save_home = os.environ['HOME'] + save_dir = os.getcwd() + script = """import pdb; pdb.Pdb(readrc=False).set_trace() + +print('hello') +""" + del os.environ['HOME'] + try: + with tempfile.TemporaryDirectory() as dirname: + os.chdir(dirname) + with open('.pdbrc', 'w') as f: + f.write("invalid\n") + + with open('main.py', 'w') as f: + f.write(script) + + cmd = [sys.executable, 'main.py'] + proc = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stdin=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + self.addCleanup(proc.stdout.close) + self.addCleanup(proc.stderr.close) + stdout, stderr = proc.communicate(b'q\n') + self.assertNotIn("NameError: name 'invalid' is not defined", + stdout.decode()) + + finally: + os.environ['HOME'] = save_home + os.chdir(save_dir) + def tearDown(self): support.unlink(support.TESTFN) def load_tests(*args): from test import test_pdb - suites = [unittest.makeSuite(PdbTestCase), doctest.DocTestSuite(test_pdb)] + suites = [unittest.makeSuite(PdbTestCase)] return unittest.TestSuite(suites) -- cgit v1.2.1 From 7223c72a329aeac30466409f9453cb676abcffdf Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 9 Sep 2016 22:56:54 -0700 Subject: Issue #18401: Fix test_pdb if $HOME is not set HOME is not set on Windows for example. Use also textwrap.dedent() for the script. --- Lib/test/test_pdb.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index c39cc97fe1..2076e2f3e0 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -1057,14 +1057,17 @@ class PdbTestCase(unittest.TestCase): def test_readrc_kwarg(self): - save_home = os.environ['HOME'] + save_home = os.environ.get('HOME', None) save_dir = os.getcwd() - script = """import pdb; pdb.Pdb(readrc=False).set_trace() + script = textwrap.dedent(""" + import pdb; pdb.Pdb(readrc=False).set_trace() -print('hello') -""" - del os.environ['HOME'] + print('hello') + """) try: + if save_home is not None: + del os.environ['HOME'] + with tempfile.TemporaryDirectory() as dirname: os.chdir(dirname) with open('.pdbrc', 'w') as f: @@ -1087,7 +1090,8 @@ print('hello') stdout.decode()) finally: - os.environ['HOME'] = save_home + if save_home is not None: + os.environ['HOME'] = save_home os.chdir(save_dir) def tearDown(self): -- cgit v1.2.1 From fd9560d15fcbf4f05c99ee56531a73987284f37d Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 9 Sep 2016 23:22:09 -0700 Subject: Issue #18401: Fix test_pdb on Windows * Use "with Popen" to cleanup properly the process * Use support.temp_cwd() to properly change the working directory * Use environ.pop() to cleanup the code --- Lib/test/test_pdb.py | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 2076e2f3e0..74417c15bf 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -4,7 +4,6 @@ import doctest import os import pdb import sys -import tempfile import types import unittest import subprocess @@ -1057,19 +1056,15 @@ class PdbTestCase(unittest.TestCase): def test_readrc_kwarg(self): - save_home = os.environ.get('HOME', None) - save_dir = os.getcwd() script = textwrap.dedent(""" import pdb; pdb.Pdb(readrc=False).set_trace() print('hello') """) - try: - if save_home is not None: - del os.environ['HOME'] - with tempfile.TemporaryDirectory() as dirname: - os.chdir(dirname) + save_home = os.environ.pop('HOME', None) + try: + with support.temp_cwd(): with open('.pdbrc', 'w') as f: f.write("invalid\n") @@ -1083,16 +1078,14 @@ class PdbTestCase(unittest.TestCase): stdin=subprocess.PIPE, stderr=subprocess.PIPE, ) - self.addCleanup(proc.stdout.close) - self.addCleanup(proc.stderr.close) - stdout, stderr = proc.communicate(b'q\n') - self.assertNotIn("NameError: name 'invalid' is not defined", - stdout.decode()) + with proc: + stdout, stderr = proc.communicate(b'q\n') + self.assertNotIn("NameError: name 'invalid' is not defined", + stdout.decode()) finally: if save_home is not None: os.environ['HOME'] = save_home - os.chdir(save_dir) def tearDown(self): support.unlink(support.TESTFN) -- cgit v1.2.1 From c1336d4bb9b4669055ff421145d698aedc53478c Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 10 Sep 2016 04:19:48 -0400 Subject: test_eintr: Fix ResourceWarning warnings --- Lib/test/eintrdata/eintr_tester.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'Lib') diff --git a/Lib/test/eintrdata/eintr_tester.py b/Lib/test/eintrdata/eintr_tester.py index 4fc79b13a1..9fbe04de9c 100644 --- a/Lib/test/eintrdata/eintr_tester.py +++ b/Lib/test/eintrdata/eintr_tester.py @@ -83,6 +83,9 @@ class OSEINTRTest(EINTRBaseTest): processes = [self.new_sleep_process() for _ in range(num)] for _ in range(num): wait_func() + # Call the Popen method to avoid a ResourceWarning + for proc in processes: + proc.wait() def test_wait(self): self._test_wait_multiple(os.wait) @@ -94,6 +97,8 @@ class OSEINTRTest(EINTRBaseTest): def _test_wait_single(self, wait_func): proc = self.new_sleep_process() wait_func(proc.pid) + # Call the Popen method to avoid a ResourceWarning + proc.wait() def test_waitpid(self): self._test_wait_single(lambda pid: os.waitpid(pid, 0)) -- cgit v1.2.1 From c82774eb054aaaf80c65e2dd72c56b5d4735993b Mon Sep 17 00:00:00 2001 From: Nick Coghlan Date: Sat, 10 Sep 2016 20:00:02 +1000 Subject: Issue #27137: align Python & C implementations of functools.partial The pure Python fallback implementation of functools.partial now matches the behaviour of its accelerated C counterpart for subclassing, pickling and text representation purposes. Patch by Emanuel Barry and Serhiy Storchaka. --- Lib/functools.py | 92 +++++++++++++++++++----- Lib/test/test_functools.py | 175 ++++++++++++++++++++++++++------------------- 2 files changed, 176 insertions(+), 91 deletions(-) (limited to 'Lib') diff --git a/Lib/functools.py b/Lib/functools.py index 214523cbc2..9845df224d 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -21,6 +21,7 @@ from abc import get_cache_token from collections import namedtuple from types import MappingProxyType from weakref import WeakKeyDictionary +from reprlib import recursive_repr try: from _thread import RLock except ImportError: @@ -237,26 +238,83 @@ except ImportError: ################################################################################ # Purely functional, no descriptor behaviour -def partial(func, *args, **keywords): +class partial: """New function with partial application of the given arguments and keywords. """ - if hasattr(func, 'func'): - args = func.args + args - tmpkw = func.keywords.copy() - tmpkw.update(keywords) - keywords = tmpkw - del tmpkw - func = func.func - - def newfunc(*fargs, **fkeywords): - newkeywords = keywords.copy() - newkeywords.update(fkeywords) - return func(*(args + fargs), **newkeywords) - newfunc.func = func - newfunc.args = args - newfunc.keywords = keywords - return newfunc + + __slots__ = "func", "args", "keywords", "__dict__", "__weakref__" + + def __new__(*args, **keywords): + if not args: + raise TypeError("descriptor '__new__' of partial needs an argument") + if len(args) < 2: + raise TypeError("type 'partial' takes at least one argument") + cls, func, *args = args + if not callable(func): + raise TypeError("the first argument must be callable") + args = tuple(args) + + if hasattr(func, "func"): + args = func.args + args + tmpkw = func.keywords.copy() + tmpkw.update(keywords) + keywords = tmpkw + del tmpkw + func = func.func + + self = super(partial, cls).__new__(cls) + + self.func = func + self.args = args + self.keywords = keywords + return self + + def __call__(*args, **keywords): + if not args: + raise TypeError("descriptor '__call__' of partial needs an argument") + self, *args = args + newkeywords = self.keywords.copy() + newkeywords.update(keywords) + return self.func(*self.args, *args, **newkeywords) + + @recursive_repr() + def __repr__(self): + qualname = type(self).__qualname__ + args = [repr(self.func)] + args.extend(repr(x) for x in self.args) + args.extend(f"{k}={v!r}" for (k, v) in self.keywords.items()) + if type(self).__module__ == "functools": + return f"functools.{qualname}({', '.join(args)})" + return f"{qualname}({', '.join(args)})" + + def __reduce__(self): + return type(self), (self.func,), (self.func, self.args, + self.keywords or None, self.__dict__ or None) + + def __setstate__(self, state): + if not isinstance(state, tuple): + raise TypeError("argument to __setstate__ must be a tuple") + if len(state) != 4: + raise TypeError(f"expected 4 items in state, got {len(state)}") + func, args, kwds, namespace = state + if (not callable(func) or not isinstance(args, tuple) or + (kwds is not None and not isinstance(kwds, dict)) or + (namespace is not None and not isinstance(namespace, dict))): + raise TypeError("invalid partial state") + + args = tuple(args) # just in case it's a subclass + if kwds is None: + kwds = {} + elif type(kwds) is not dict: # XXX does it need to be *exactly* dict? + kwds = dict(kwds) + if namespace is None: + namespace = {} + + self.__dict__ = namespace + self.func = func + self.args = args + self.keywords = kwds try: from _functools import partial diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index 40f2234a7f..fa66510bf1 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -8,6 +8,7 @@ import sys from test import support import unittest from weakref import proxy +import contextlib try: import threading except ImportError: @@ -20,6 +21,14 @@ c_functools = support.import_fresh_module('functools', fresh=['_functools']) decimal = support.import_fresh_module('decimal', fresh=['_decimal']) +@contextlib.contextmanager +def replaced_module(name, replacement): + original_module = sys.modules[name] + sys.modules[name] = replacement + try: + yield + finally: + sys.modules[name] = original_module def capture(*args, **kw): """capture all positional and keyword arguments""" @@ -167,58 +176,35 @@ class TestPartial: p2.new_attr = 'spam' self.assertEqual(p2.new_attr, 'spam') - -@unittest.skipUnless(c_functools, 'requires the C _functools module') -class TestPartialC(TestPartial, unittest.TestCase): - if c_functools: - partial = c_functools.partial - - def test_attributes_unwritable(self): - # attributes should not be writable - p = self.partial(capture, 1, 2, a=10, b=20) - self.assertRaises(AttributeError, setattr, p, 'func', map) - self.assertRaises(AttributeError, setattr, p, 'args', (1, 2)) - self.assertRaises(AttributeError, setattr, p, 'keywords', dict(a=1, b=2)) - - p = self.partial(hex) - try: - del p.__dict__ - except TypeError: - pass - else: - self.fail('partial object allowed __dict__ to be deleted') - def test_repr(self): args = (object(), object()) args_repr = ', '.join(repr(a) for a in args) kwargs = {'a': object(), 'b': object()} kwargs_reprs = ['a={a!r}, b={b!r}'.format_map(kwargs), 'b={b!r}, a={a!r}'.format_map(kwargs)] - if self.partial is c_functools.partial: + if self.partial in (c_functools.partial, py_functools.partial): name = 'functools.partial' else: name = self.partial.__name__ f = self.partial(capture) - self.assertEqual('{}({!r})'.format(name, capture), - repr(f)) + self.assertEqual(f'{name}({capture!r})', repr(f)) f = self.partial(capture, *args) - self.assertEqual('{}({!r}, {})'.format(name, capture, args_repr), - repr(f)) + self.assertEqual(f'{name}({capture!r}, {args_repr})', repr(f)) f = self.partial(capture, **kwargs) self.assertIn(repr(f), - ['{}({!r}, {})'.format(name, capture, kwargs_repr) + [f'{name}({capture!r}, {kwargs_repr})' for kwargs_repr in kwargs_reprs]) f = self.partial(capture, *args, **kwargs) self.assertIn(repr(f), - ['{}({!r}, {}, {})'.format(name, capture, args_repr, kwargs_repr) + [f'{name}({capture!r}, {args_repr}, {kwargs_repr})' for kwargs_repr in kwargs_reprs]) def test_recursive_repr(self): - if self.partial is c_functools.partial: + if self.partial in (c_functools.partial, py_functools.partial): name = 'functools.partial' else: name = self.partial.__name__ @@ -226,30 +212,31 @@ class TestPartialC(TestPartial, unittest.TestCase): f = self.partial(capture) f.__setstate__((f, (), {}, {})) try: - self.assertEqual(repr(f), '%s(%s(...))' % (name, name)) + self.assertEqual(repr(f), '%s(...)' % (name,)) finally: f.__setstate__((capture, (), {}, {})) f = self.partial(capture) f.__setstate__((capture, (f,), {}, {})) try: - self.assertEqual(repr(f), '%s(%r, %s(...))' % (name, capture, name)) + self.assertEqual(repr(f), '%s(%r, ...)' % (name, capture,)) finally: f.__setstate__((capture, (), {}, {})) f = self.partial(capture) f.__setstate__((capture, (), {'a': f}, {})) try: - self.assertEqual(repr(f), '%s(%r, a=%s(...))' % (name, capture, name)) + self.assertEqual(repr(f), '%s(%r, a=...)' % (name, capture,)) finally: f.__setstate__((capture, (), {}, {})) def test_pickle(self): - f = self.partial(signature, ['asdf'], bar=[True]) - f.attr = [] - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - f_copy = pickle.loads(pickle.dumps(f, proto)) - self.assertEqual(signature(f_copy), signature(f)) + with self.AllowPickle(): + f = self.partial(signature, ['asdf'], bar=[True]) + f.attr = [] + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + f_copy = pickle.loads(pickle.dumps(f, proto)) + self.assertEqual(signature(f_copy), signature(f)) def test_copy(self): f = self.partial(signature, ['asdf'], bar=[True]) @@ -274,11 +261,13 @@ class TestPartialC(TestPartial, unittest.TestCase): def test_setstate(self): f = self.partial(signature) f.__setstate__((capture, (1,), dict(a=10), dict(attr=[]))) + self.assertEqual(signature(f), (capture, (1,), dict(a=10), dict(attr=[]))) self.assertEqual(f(2, b=20), ((1, 2), {'a': 10, 'b': 20})) f.__setstate__((capture, (1,), dict(a=10), None)) + self.assertEqual(signature(f), (capture, (1,), dict(a=10), {})) self.assertEqual(f(2, b=20), ((1, 2), {'a': 10, 'b': 20})) @@ -325,38 +314,39 @@ class TestPartialC(TestPartial, unittest.TestCase): self.assertIs(type(r[0]), tuple) def test_recursive_pickle(self): - f = self.partial(capture) - f.__setstate__((f, (), {}, {})) - try: - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - with self.assertRaises(RecursionError): - pickle.dumps(f, proto) - finally: - f.__setstate__((capture, (), {}, {})) - - f = self.partial(capture) - f.__setstate__((capture, (f,), {}, {})) - try: - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - f_copy = pickle.loads(pickle.dumps(f, proto)) - try: - self.assertIs(f_copy.args[0], f_copy) - finally: - f_copy.__setstate__((capture, (), {}, {})) - finally: - f.__setstate__((capture, (), {}, {})) - - f = self.partial(capture) - f.__setstate__((capture, (), {'a': f}, {})) - try: - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - f_copy = pickle.loads(pickle.dumps(f, proto)) - try: - self.assertIs(f_copy.keywords['a'], f_copy) - finally: - f_copy.__setstate__((capture, (), {}, {})) - finally: - f.__setstate__((capture, (), {}, {})) + with self.AllowPickle(): + f = self.partial(capture) + f.__setstate__((f, (), {}, {})) + try: + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + with self.assertRaises(RecursionError): + pickle.dumps(f, proto) + finally: + f.__setstate__((capture, (), {}, {})) + + f = self.partial(capture) + f.__setstate__((capture, (f,), {}, {})) + try: + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + f_copy = pickle.loads(pickle.dumps(f, proto)) + try: + self.assertIs(f_copy.args[0], f_copy) + finally: + f_copy.__setstate__((capture, (), {}, {})) + finally: + f.__setstate__((capture, (), {}, {})) + + f = self.partial(capture) + f.__setstate__((capture, (), {'a': f}, {})) + try: + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + f_copy = pickle.loads(pickle.dumps(f, proto)) + try: + self.assertIs(f_copy.keywords['a'], f_copy) + finally: + f_copy.__setstate__((capture, (), {}, {})) + finally: + f.__setstate__((capture, (), {}, {})) # Issue 6083: Reference counting bug def test_setstate_refcount(self): @@ -375,24 +365,60 @@ class TestPartialC(TestPartial, unittest.TestCase): f = self.partial(object) self.assertRaises(TypeError, f.__setstate__, BadSequence()) +@unittest.skipUnless(c_functools, 'requires the C _functools module') +class TestPartialC(TestPartial, unittest.TestCase): + if c_functools: + partial = c_functools.partial + + class AllowPickle: + def __enter__(self): + return self + def __exit__(self, type, value, tb): + return False + + def test_attributes_unwritable(self): + # attributes should not be writable + p = self.partial(capture, 1, 2, a=10, b=20) + self.assertRaises(AttributeError, setattr, p, 'func', map) + self.assertRaises(AttributeError, setattr, p, 'args', (1, 2)) + self.assertRaises(AttributeError, setattr, p, 'keywords', dict(a=1, b=2)) + + p = self.partial(hex) + try: + del p.__dict__ + except TypeError: + pass + else: + self.fail('partial object allowed __dict__ to be deleted') class TestPartialPy(TestPartial, unittest.TestCase): - partial = staticmethod(py_functools.partial) + partial = py_functools.partial + class AllowPickle: + def __init__(self): + self._cm = replaced_module("functools", py_functools) + def __enter__(self): + return self._cm.__enter__() + def __exit__(self, type, value, tb): + return self._cm.__exit__(type, value, tb) if c_functools: - class PartialSubclass(c_functools.partial): + class CPartialSubclass(c_functools.partial): pass +class PyPartialSubclass(py_functools.partial): + pass @unittest.skipUnless(c_functools, 'requires the C _functools module') class TestPartialCSubclass(TestPartialC): if c_functools: - partial = PartialSubclass + partial = CPartialSubclass # partial subclasses are not optimized for nested calls test_nested_optimization = None +class TestPartialPySubclass(TestPartialPy): + partial = PyPartialSubclass class TestPartialMethod(unittest.TestCase): @@ -683,9 +709,10 @@ class TestWraps(TestUpdateWrapper): self.assertEqual(wrapper.attr, 'This is a different test') self.assertEqual(wrapper.dict_attr, f.dict_attr) - +@unittest.skipUnless(c_functools, 'requires the C _functools module') class TestReduce(unittest.TestCase): - func = functools.reduce + if c_functools: + func = c_functools.reduce def test_reduce(self): class Squares: -- cgit v1.2.1 From faf823304938a6071bd50f71c28b2bd73271279b Mon Sep 17 00:00:00 2001 From: Nick Coghlan Date: Sat, 10 Sep 2016 20:16:18 +1000 Subject: Issue #17909: Accept binary input in json.loads json.loads (and hence json.load) now support binary input encoded as UTF-8, UTF-16 or UTF-32. Patch by Serhiy Storchaka. --- Lib/json/__init__.py | 50 ++++++++++++++++++++++++++++++++------ Lib/test/test_json/test_decode.py | 4 +-- Lib/test/test_json/test_unicode.py | 16 +++++++++--- 3 files changed, 56 insertions(+), 14 deletions(-) (limited to 'Lib') diff --git a/Lib/json/__init__.py b/Lib/json/__init__.py index f2c0d23a32..8dcc6786e2 100644 --- a/Lib/json/__init__.py +++ b/Lib/json/__init__.py @@ -105,6 +105,7 @@ __author__ = 'Bob Ippolito ' from .decoder import JSONDecoder, JSONDecodeError from .encoder import JSONEncoder +import codecs _default_encoder = JSONEncoder( skipkeys=False, @@ -240,6 +241,35 @@ def dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True, _default_decoder = JSONDecoder(object_hook=None, object_pairs_hook=None) +def detect_encoding(b): + bstartswith = b.startswith + if bstartswith((codecs.BOM_UTF32_BE, codecs.BOM_UTF32_LE)): + return 'utf-32' + if bstartswith((codecs.BOM_UTF16_BE, codecs.BOM_UTF16_LE)): + return 'utf-16' + if bstartswith(codecs.BOM_UTF8): + return 'utf-8-sig' + + if len(b) >= 4: + if not b[0]: + # 00 00 -- -- - utf-32-be + # 00 XX -- -- - utf-16-be + return 'utf-16-be' if b[1] else 'utf-32-be' + if not b[1]: + # XX 00 00 00 - utf-32-le + # XX 00 XX XX - utf-16-le + return 'utf-16-le' if b[2] or b[3] else 'utf-32-le' + elif len(b) == 2: + if not b[0]: + # 00 XX - utf-16-be + return 'utf-16-be' + if not b[1]: + # XX 00 - utf-16-le + return 'utf-16-le' + # default + return 'utf-8' + + def load(fp, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw): """Deserialize ``fp`` (a ``.read()``-supporting file-like object containing @@ -270,8 +300,8 @@ def load(fp, *, cls=None, object_hook=None, parse_float=None, def loads(s, *, encoding=None, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw): - """Deserialize ``s`` (a ``str`` instance containing a JSON - document) to a Python object. + """Deserialize ``s`` (a ``str``, ``bytes`` or ``bytearray`` instance + containing a JSON document) to a Python object. ``object_hook`` is an optional function that will be called with the result of any object literal decode (a ``dict``). The return value of @@ -307,12 +337,16 @@ def loads(s, *, encoding=None, cls=None, object_hook=None, parse_float=None, The ``encoding`` argument is ignored and deprecated. """ - if not isinstance(s, str): - raise TypeError('the JSON object must be str, not {!r}'.format( - s.__class__.__name__)) - if s.startswith(u'\ufeff'): - raise JSONDecodeError("Unexpected UTF-8 BOM (decode using utf-8-sig)", - s, 0) + if isinstance(s, str): + if s.startswith('\ufeff'): + raise JSONDecodeError("Unexpected UTF-8 BOM (decode using utf-8-sig)", + s, 0) + else: + if not isinstance(s, (bytes, bytearray)): + raise TypeError('the JSON object must be str, bytes or bytearray, ' + 'not {!r}'.format(s.__class__.__name__)) + s = s.decode(detect_encoding(s), 'surrogatepass') + if (cls is None and object_hook is None and parse_int is None and parse_float is None and parse_constant is None and object_pairs_hook is None and not kw): diff --git a/Lib/test/test_json/test_decode.py b/Lib/test/test_json/test_decode.py index fdafeb6d8f..7e568be409 100644 --- a/Lib/test/test_json/test_decode.py +++ b/Lib/test/test_json/test_decode.py @@ -72,10 +72,8 @@ class TestDecode: def test_invalid_input_type(self): msg = 'the JSON object must be str' - for value in [1, 3.14, b'bytes', b'\xff\x00', [], {}, None]: + for value in [1, 3.14, [], {}, None]: self.assertRaisesRegex(TypeError, msg, self.loads, value) - with self.assertRaisesRegex(TypeError, msg): - self.json.load(BytesIO(b'[1,2,3]')) def test_string_with_utf8_bom(self): # see #18958 diff --git a/Lib/test/test_json/test_unicode.py b/Lib/test/test_json/test_unicode.py index c7cc8a7e92..eda177aa68 100644 --- a/Lib/test/test_json/test_unicode.py +++ b/Lib/test/test_json/test_unicode.py @@ -1,3 +1,4 @@ +import codecs from collections import OrderedDict from test.test_json import PyTest, CTest @@ -52,9 +53,18 @@ class TestUnicode: self.assertRaises(TypeError, self.dumps, [b"hi"]) def test_bytes_decode(self): - self.assertRaises(TypeError, self.loads, b'"hi"') - self.assertRaises(TypeError, self.loads, b'["hi"]') - + for encoding, bom in [ + ('utf-8', codecs.BOM_UTF8), + ('utf-16be', codecs.BOM_UTF16_BE), + ('utf-16le', codecs.BOM_UTF16_LE), + ('utf-32be', codecs.BOM_UTF32_BE), + ('utf-32le', codecs.BOM_UTF32_LE), + ]: + data = ["a\xb5\u20ac\U0001d120"] + encoded = self.dumps(data).encode(encoding) + self.assertEqual(self.loads(bom + encoded), data) + self.assertEqual(self.loads(encoded), data) + self.assertRaises(UnicodeDecodeError, self.loads, b'["\x80"]') def test_object_pairs_hook_with_unicode(self): s = '{"xkd":1, "kcw":2, "art":3, "hxm":4, "qrt":5, "pad":6, "hoy":7}' -- cgit v1.2.1 From 1bd323c5b71f49fe7d2a344f98ec9179ad51c021 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 10 Sep 2016 06:24:47 -0400 Subject: test_platform: Save/restore os.environ on Windows --- Lib/test/test_platform.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py index 8eed7c00eb..3f29613026 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py @@ -18,6 +18,12 @@ class PlatformTest(unittest.TestCase): # On Windows, the EXE needs to know where pythonXY.dll is at so we have # to add the directory to the path. if sys.platform == "win32": + def restore_environ(old_env): + os.environ.clear() + os.environ.update(old_env) + + self.addCleanup(restore_environ, dict(os.environ)) + os.environ["Path"] = "{};{}".format( os.path.dirname(sys.executable), os.environ["Path"]) @@ -26,6 +32,7 @@ class PlatformTest(unittest.TestCase): 'import platform; print(platform.architecture())'] p = subprocess.Popen(cmd, stdout=subprocess.PIPE) return p.communicate() + real = os.path.realpath(sys.executable) link = os.path.abspath(support.TESTFN) os.symlink(real, link) -- cgit v1.2.1 From 3bf33820a0bce9fb43fcdcc226cc5714c3069b29 Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Sat, 10 Sep 2016 16:19:45 +0200 Subject: Issue #28046: Fix get_sysconfigdata_name(). --- Lib/sysconfig.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'Lib') diff --git a/Lib/sysconfig.py b/Lib/sysconfig.py index c2f28f5454..13275dea34 100644 --- a/Lib/sysconfig.py +++ b/Lib/sysconfig.py @@ -342,12 +342,19 @@ def get_makefile_filename(): return os.path.join(get_path('stdlib'), config_dir_name, 'Makefile') -def _get_sysconfigdata_name(): - return '_sysconfigdata_{abi}_{platform}_{multiarch}'.format( - abi=sys.abiflags, - platform=sys.platform, - multiarch=getattr(sys.implementation, '_multiarch', ''), - ) +def _get_sysconfigdata_name(vars=None): + if vars is None: + return '_sysconfigdata_{abi}_{platform}_{multiarch}'.format( + abi=sys.abiflags, + platform=sys.platform, + multiarch=getattr(sys.implementation, '_multiarch', ''), + ) + else: + return '_sysconfigdata_{abi}_{platform}_{multiarch}'.format( + abi=vars['ABIFLAGS'], + platform=vars['MACHDEP'], + multiarch=vars.get('MULTIARCH', ''), + ) def _generate_posix_vars(): @@ -390,7 +397,7 @@ def _generate_posix_vars(): # _sysconfigdata module manually and populate it with the build vars. # This is more than sufficient for ensuring the subsequent call to # get_platform() succeeds. - name = _get_sysconfigdata_name() + name = _get_sysconfigdata_name(vars) if 'darwin' in sys.platform: import types module = types.ModuleType(name) -- cgit v1.2.1 From 4ec56a2fdbb143c98628ae4f714cb1224882ea47 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 10 Sep 2016 21:28:07 +0300 Subject: Issue #24693: Changed some RuntimeError's in the zipfile module to more appropriate types. Improved some error messages and debugging output. --- Lib/test/test_zipfile.py | 40 +++++++++++++++++----------------- Lib/zipfile.py | 56 ++++++++++++++++++++++++------------------------ 2 files changed, 48 insertions(+), 48 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index ef3c3d8d67..ff4a6d65ef 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -449,15 +449,15 @@ class StoredTestsWithSourceFile(AbstractTestsWithSourceFile, def test_write_to_readonly(self): """Check that trying to call write() on a readonly ZipFile object - raises a RuntimeError.""" + raises a ValueError.""" with zipfile.ZipFile(TESTFN2, mode="w") as zipfp: zipfp.writestr("somefile.txt", "bogus") with zipfile.ZipFile(TESTFN2, mode="r") as zipfp: - self.assertRaises(RuntimeError, zipfp.write, TESTFN) + self.assertRaises(ValueError, zipfp.write, TESTFN) with zipfile.ZipFile(TESTFN2, mode="r") as zipfp: - with self.assertRaises(RuntimeError): + with self.assertRaises(ValueError): zipfp.open(TESTFN, mode='w') def test_add_file_before_1980(self): @@ -1210,27 +1210,27 @@ class OtherTests(unittest.TestCase): fp.write("short file") self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN) - def test_closed_zip_raises_RuntimeError(self): + def test_closed_zip_raises_ValueError(self): """Verify that testzip() doesn't swallow inappropriate exceptions.""" data = io.BytesIO() with zipfile.ZipFile(data, mode="w") as zipf: zipf.writestr("foo.txt", "O, for a Muse of Fire!") # This is correct; calling .read on a closed ZipFile should raise - # a RuntimeError, and so should calling .testzip. An earlier + # a ValueError, and so should calling .testzip. An earlier # version of .testzip would swallow this exception (and any other) # and report that the first file in the archive was corrupt. - self.assertRaises(RuntimeError, zipf.read, "foo.txt") - self.assertRaises(RuntimeError, zipf.open, "foo.txt") - self.assertRaises(RuntimeError, zipf.testzip) - self.assertRaises(RuntimeError, zipf.writestr, "bogus.txt", "bogus") + self.assertRaises(ValueError, zipf.read, "foo.txt") + self.assertRaises(ValueError, zipf.open, "foo.txt") + self.assertRaises(ValueError, zipf.testzip) + self.assertRaises(ValueError, zipf.writestr, "bogus.txt", "bogus") with open(TESTFN, 'w') as f: f.write('zipfile test data') - self.assertRaises(RuntimeError, zipf.write, TESTFN) + self.assertRaises(ValueError, zipf.write, TESTFN) def test_bad_constructor_mode(self): """Check that bad modes passed to ZipFile constructor are caught.""" - self.assertRaises(RuntimeError, zipfile.ZipFile, TESTFN, "q") + self.assertRaises(ValueError, zipfile.ZipFile, TESTFN, "q") def test_bad_open_mode(self): """Check that bad modes passed to ZipFile.open are caught.""" @@ -1240,10 +1240,10 @@ class OtherTests(unittest.TestCase): with zipfile.ZipFile(TESTFN, mode="r") as zipf: # read the data to make sure the file is there zipf.read("foo.txt") - self.assertRaises(RuntimeError, zipf.open, "foo.txt", "q") + self.assertRaises(ValueError, zipf.open, "foo.txt", "q") # universal newlines support is removed - self.assertRaises(RuntimeError, zipf.open, "foo.txt", "U") - self.assertRaises(RuntimeError, zipf.open, "foo.txt", "rU") + self.assertRaises(ValueError, zipf.open, "foo.txt", "U") + self.assertRaises(ValueError, zipf.open, "foo.txt", "rU") def test_read0(self): """Check that calling read(0) on a ZipExtFile object returns an empty @@ -1266,7 +1266,7 @@ class OtherTests(unittest.TestCase): def test_bad_compression_mode(self): """Check that bad compression methods passed to ZipFile.open are caught.""" - self.assertRaises(RuntimeError, zipfile.ZipFile, TESTFN, "w", -1) + self.assertRaises(NotImplementedError, zipfile.ZipFile, TESTFN, "w", -1) def test_unsupported_compression(self): # data is declared as shrunk, but actually deflated @@ -1423,15 +1423,15 @@ class OtherTests(unittest.TestCase): with zipf.open('foo', mode='w') as w2: w2.write(msg1) with zipf.open('bar', mode='w') as w1: - with self.assertRaises(RuntimeError): + with self.assertRaises(ValueError): zipf.open('handle', mode='w') - with self.assertRaises(RuntimeError): + with self.assertRaises(ValueError): zipf.open('foo', mode='r') - with self.assertRaises(RuntimeError): + with self.assertRaises(ValueError): zipf.writestr('str', 'abcde') - with self.assertRaises(RuntimeError): + with self.assertRaises(ValueError): zipf.write(__file__, 'file') - with self.assertRaises(RuntimeError): + with self.assertRaises(ValueError): zipf.close() w1.write(msg2) with zipf.open('baz', mode='w') as w2: diff --git a/Lib/zipfile.py b/Lib/zipfile.py index 8dd064a2c6..7ba4e5980b 100644 --- a/Lib/zipfile.py +++ b/Lib/zipfile.py @@ -449,7 +449,7 @@ class ZipInfo (object): elif ln == 0: counts = () else: - raise RuntimeError("Corrupt extra field %s"%(ln,)) + raise BadZipFile("Corrupt extra field %04x (size=%d)" % (tp, ln)) idx = 0 @@ -654,7 +654,7 @@ def _check_compression(compression): raise RuntimeError( "Compression requires the (missing) lzma module") else: - raise RuntimeError("That compression method is not supported") + raise NotImplementedError("That compression method is not supported") def _get_compressor(compress_type): @@ -697,7 +697,7 @@ class _SharedFile: def read(self, n=-1): with self._lock: if self._writing(): - raise RuntimeError("Can't read from the ZIP file while there " + raise ValueError("Can't read from the ZIP file while there " "is an open writing handle on it. " "Close the writing handle before trying to read.") self._file.seek(self._pos) @@ -1055,7 +1055,7 @@ class ZipFile: """Open the ZIP file with mode read 'r', write 'w', exclusive create 'x', or append 'a'.""" if mode not in ('r', 'w', 'x', 'a'): - raise RuntimeError("ZipFile requires mode 'r', 'w', 'x', or 'a'") + raise ValueError("ZipFile requires mode 'r', 'w', 'x', or 'a'") _check_compression(compression) @@ -1129,7 +1129,7 @@ class ZipFile: self._didModify = True self.start_dir = self.fp.tell() else: - raise RuntimeError("Mode must be 'r', 'w', 'x', or 'a'") + raise ValueError("Mode must be 'r', 'w', 'x', or 'a'") except: fp = self.fp self.fp = None @@ -1277,7 +1277,7 @@ class ZipFile: def setpassword(self, pwd): """Set default password for encrypted files.""" if pwd and not isinstance(pwd, bytes): - raise TypeError("pwd: expected bytes, got %s" % type(pwd)) + raise TypeError("pwd: expected bytes, got %s" % type(pwd).__name__) if pwd: self.pwd = pwd else: @@ -1291,7 +1291,7 @@ class ZipFile: @comment.setter def comment(self, comment): if not isinstance(comment, bytes): - raise TypeError("comment: expected bytes, got %s" % type(comment)) + raise TypeError("comment: expected bytes, got %s" % type(comment).__name__) # check for valid comment length if len(comment) > ZIP_MAX_COMMENT: import warnings @@ -1323,13 +1323,13 @@ class ZipFile: instance for name, with zinfo.file_size set. """ if mode not in {"r", "w"}: - raise RuntimeError('open() requires mode "r" or "w"') + raise ValueError('open() requires mode "r" or "w"') if pwd and not isinstance(pwd, bytes): - raise TypeError("pwd: expected bytes, got %s" % type(pwd)) + raise TypeError("pwd: expected bytes, got %s" % type(pwd).__name__) if pwd and (mode == "w"): raise ValueError("pwd is only supported for reading files") if not self.fp: - raise RuntimeError( + raise ValueError( "Attempt to use ZIP archive that was already closed") # Make sure we have an info object @@ -1347,7 +1347,7 @@ class ZipFile: return self._open_to_write(zinfo, force_zip64=force_zip64) if self._writing: - raise RuntimeError("Can't read from the ZIP file while there " + raise ValueError("Can't read from the ZIP file while there " "is an open writing handle on it. " "Close the writing handle before trying to read.") @@ -1394,7 +1394,7 @@ class ZipFile: if not pwd: pwd = self.pwd if not pwd: - raise RuntimeError("File %s is encrypted, password " + raise RuntimeError("File %r is encrypted, password " "required for extraction" % name) zd = _ZipDecrypter(pwd) @@ -1412,7 +1412,7 @@ class ZipFile: # compare against the CRC otherwise check_byte = (zinfo.CRC >> 24) & 0xff if h[11] != check_byte: - raise RuntimeError("Bad password for file", name) + raise RuntimeError("Bad password for file %r" % name) return ZipExtFile(zef_file, mode, zinfo, zd, True) except: @@ -1426,9 +1426,9 @@ class ZipFile: "the ZIP file." ) if self._writing: - raise RuntimeError("Can't write to the ZIP file while there is " - "another write handle open on it. " - "Close the first handle before opening another.") + raise ValueError("Can't write to the ZIP file while there is " + "another write handle open on it. " + "Close the first handle before opening another.") # Sizes and CRC are overwritten with correct data after processing the file if not hasattr(zinfo, 'file_size'): @@ -1548,9 +1548,9 @@ class ZipFile: import warnings warnings.warn('Duplicate name: %r' % zinfo.filename, stacklevel=3) if self.mode not in ('w', 'x', 'a'): - raise RuntimeError("write() requires mode 'w', 'x', or 'a'") + raise ValueError("write() requires mode 'w', 'x', or 'a'") if not self.fp: - raise RuntimeError( + raise ValueError( "Attempt to write ZIP archive that was already closed") _check_compression(zinfo.compress_type) if not self._allowZip64: @@ -1569,10 +1569,10 @@ class ZipFile: """Put the bytes from filename into the archive under the name arcname.""" if not self.fp: - raise RuntimeError( + raise ValueError( "Attempt to write to ZIP archive that was already closed") if self._writing: - raise RuntimeError( + raise ValueError( "Can't write to ZIP archive while an open writing handle exists" ) @@ -1628,10 +1628,10 @@ class ZipFile: zinfo = zinfo_or_arcname if not self.fp: - raise RuntimeError( + raise ValueError( "Attempt to write to ZIP archive that was already closed") if self._writing: - raise RuntimeError( + raise ValueError( "Can't write to ZIP archive while an open writing handle exists." ) @@ -1654,9 +1654,9 @@ class ZipFile: return if self._writing: - raise RuntimeError("Can't close the ZIP file while there is " - "an open writing handle on it. " - "Close the writing handle before closing the zip.") + raise ValueError("Can't close the ZIP file while there is " + "an open writing handle on it. " + "Close the writing handle before closing the zip.") try: if self.mode in ('w', 'x', 'a') and self._didModify: # write ending records @@ -1803,7 +1803,7 @@ class PyZipFile(ZipFile): if filterfunc and not filterfunc(pathname): if self.debug: label = 'path' if os.path.isdir(pathname) else 'file' - print('%s "%s" skipped by filterfunc' % (label, pathname)) + print('%s %r skipped by filterfunc' % (label, pathname)) return dir, name = os.path.split(pathname) if os.path.isdir(pathname): @@ -1834,7 +1834,7 @@ class PyZipFile(ZipFile): elif ext == ".py": if filterfunc and not filterfunc(path): if self.debug: - print('file "%s" skipped by filterfunc' % path) + print('file %r skipped by filterfunc' % path) continue fname, arcname = self._get_codename(path[0:-3], basename) @@ -1851,7 +1851,7 @@ class PyZipFile(ZipFile): if ext == ".py": if filterfunc and not filterfunc(path): if self.debug: - print('file "%s" skipped by filterfunc' % path) + print('file %r skipped by filterfunc' % path) continue fname, arcname = self._get_codename(path[0:-3], basename) -- cgit v1.2.1 From 945180b06b9c2a5b60c68d3b950d29b13beada58 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sat, 10 Sep 2016 22:43:48 +0200 Subject: Issue 28043: SSLContext has improved default settings The options OP_NO_COMPRESSION, OP_CIPHER_SERVER_PREFERENCE, OP_SINGLE_DH_USE, OP_SINGLE_ECDH_USE, OP_NO_SSLv2 (except for PROTOCOL_SSLv2), and OP_NO_SSLv3 (except for PROTOCOL_SSLv3) are set by default. The initial cipher suite list contains only HIGH ciphers, no NULL ciphers and MD5 ciphers (except for PROTOCOL_SSLv2). --- Lib/ssl.py | 30 +++++-------------------- Lib/test/test_ssl.py | 62 ++++++++++++++++++++++++++++------------------------ 2 files changed, 39 insertions(+), 53 deletions(-) (limited to 'Lib') diff --git a/Lib/ssl.py b/Lib/ssl.py index 5f33849146..3400b7f3f0 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -488,32 +488,16 @@ def create_default_context(purpose=Purpose.SERVER_AUTH, *, cafile=None, if not isinstance(purpose, _ASN1Object): raise TypeError(purpose) + # SSLContext sets OP_NO_SSLv2, OP_NO_SSLv3, OP_NO_COMPRESSION, + # OP_CIPHER_SERVER_PREFERENCE, OP_SINGLE_DH_USE and OP_SINGLE_ECDH_USE + # by default. context = SSLContext(PROTOCOL_TLS) - # SSLv2 considered harmful. - context.options |= OP_NO_SSLv2 - - # SSLv3 has problematic security and is only required for really old - # clients such as IE6 on Windows XP - context.options |= OP_NO_SSLv3 - - # disable compression to prevent CRIME attacks (OpenSSL 1.0+) - context.options |= getattr(_ssl, "OP_NO_COMPRESSION", 0) - if purpose == Purpose.SERVER_AUTH: # verify certs and host name in client mode context.verify_mode = CERT_REQUIRED context.check_hostname = True elif purpose == Purpose.CLIENT_AUTH: - # Prefer the server's ciphers by default so that we get stronger - # encryption - context.options |= getattr(_ssl, "OP_CIPHER_SERVER_PREFERENCE", 0) - - # Use single use keys in order to improve forward secrecy - context.options |= getattr(_ssl, "OP_SINGLE_DH_USE", 0) - context.options |= getattr(_ssl, "OP_SINGLE_ECDH_USE", 0) - - # disallow ciphers with known vulnerabilities context.set_ciphers(_RESTRICTED_SERVER_CIPHERS) if cafile or capath or cadata: @@ -539,12 +523,10 @@ def _create_unverified_context(protocol=PROTOCOL_TLS, *, cert_reqs=None, if not isinstance(purpose, _ASN1Object): raise TypeError(purpose) + # SSLContext sets OP_NO_SSLv2, OP_NO_SSLv3, OP_NO_COMPRESSION, + # OP_CIPHER_SERVER_PREFERENCE, OP_SINGLE_DH_USE and OP_SINGLE_ECDH_USE + # by default. context = SSLContext(protocol) - # SSLv2 considered harmful. - context.options |= OP_NO_SSLv2 - # SSLv3 has problematic security and is only required for really old - # clients such as IE6 on Windows XP - context.options |= OP_NO_SSLv3 if cert_reqs is not None: context.verify_mode = cert_reqs diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 0bdb86d5f2..7488dc7baa 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -80,6 +80,12 @@ NULLBYTECERT = data_file("nullbytecert.pem") DHFILE = data_file("dh1024.pem") BYTES_DHFILE = os.fsencode(DHFILE) +# Not defined in all versions of OpenSSL +OP_NO_COMPRESSION = getattr(ssl, "OP_NO_COMPRESSION", 0) +OP_SINGLE_DH_USE = getattr(ssl, "OP_SINGLE_DH_USE", 0) +OP_SINGLE_ECDH_USE = getattr(ssl, "OP_SINGLE_ECDH_USE", 0) +OP_CIPHER_SERVER_PREFERENCE = getattr(ssl, "OP_CIPHER_SERVER_PREFERENCE", 0) + def handle_error(prefix): exc_format = ' '.join(traceback.format_exception(*sys.exc_info())) @@ -870,8 +876,9 @@ class ContextTests(unittest.TestCase): ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) # OP_ALL | OP_NO_SSLv2 | OP_NO_SSLv3 is the default value default = (ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3) - if not IS_LIBRESSL and ssl.OPENSSL_VERSION_INFO >= (1, 1, 0): - default |= ssl.OP_NO_COMPRESSION + # SSLContext also enables these by default + default |= (OP_NO_COMPRESSION | OP_CIPHER_SERVER_PREFERENCE | + OP_SINGLE_DH_USE | OP_SINGLE_ECDH_USE) self.assertEqual(default, ctx.options) ctx.options |= ssl.OP_NO_TLSv1 self.assertEqual(default | ssl.OP_NO_TLSv1, ctx.options) @@ -1236,16 +1243,29 @@ class ContextTests(unittest.TestCase): stats["x509"] += 1 self.assertEqual(ctx.cert_store_stats(), stats) + def _assert_context_options(self, ctx): + self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) + if OP_NO_COMPRESSION != 0: + self.assertEqual(ctx.options & OP_NO_COMPRESSION, + OP_NO_COMPRESSION) + if OP_SINGLE_DH_USE != 0: + self.assertEqual(ctx.options & OP_SINGLE_DH_USE, + OP_SINGLE_DH_USE) + if OP_SINGLE_ECDH_USE != 0: + self.assertEqual(ctx.options & OP_SINGLE_ECDH_USE, + OP_SINGLE_ECDH_USE) + if OP_CIPHER_SERVER_PREFERENCE != 0: + self.assertEqual(ctx.options & OP_CIPHER_SERVER_PREFERENCE, + OP_CIPHER_SERVER_PREFERENCE) + def test_create_default_context(self): ctx = ssl.create_default_context() + self.assertEqual(ctx.protocol, ssl.PROTOCOL_SSLv23) self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED) self.assertTrue(ctx.check_hostname) - self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) - self.assertEqual( - ctx.options & getattr(ssl, "OP_NO_COMPRESSION", 0), - getattr(ssl, "OP_NO_COMPRESSION", 0), - ) + self._assert_context_options(ctx) + with open(SIGNING_CA) as f: cadata = f.read() @@ -1253,40 +1273,24 @@ class ContextTests(unittest.TestCase): cadata=cadata) self.assertEqual(ctx.protocol, ssl.PROTOCOL_SSLv23) self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED) - self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) - self.assertEqual( - ctx.options & getattr(ssl, "OP_NO_COMPRESSION", 0), - getattr(ssl, "OP_NO_COMPRESSION", 0), - ) + self._assert_context_options(ctx) ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) self.assertEqual(ctx.protocol, ssl.PROTOCOL_SSLv23) self.assertEqual(ctx.verify_mode, ssl.CERT_NONE) - self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) - self.assertEqual( - ctx.options & getattr(ssl, "OP_NO_COMPRESSION", 0), - getattr(ssl, "OP_NO_COMPRESSION", 0), - ) - self.assertEqual( - ctx.options & getattr(ssl, "OP_SINGLE_DH_USE", 0), - getattr(ssl, "OP_SINGLE_DH_USE", 0), - ) - self.assertEqual( - ctx.options & getattr(ssl, "OP_SINGLE_ECDH_USE", 0), - getattr(ssl, "OP_SINGLE_ECDH_USE", 0), - ) + self._assert_context_options(ctx) def test__create_stdlib_context(self): ctx = ssl._create_stdlib_context() self.assertEqual(ctx.protocol, ssl.PROTOCOL_SSLv23) self.assertEqual(ctx.verify_mode, ssl.CERT_NONE) self.assertFalse(ctx.check_hostname) - self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) + self._assert_context_options(ctx) ctx = ssl._create_stdlib_context(ssl.PROTOCOL_TLSv1) self.assertEqual(ctx.protocol, ssl.PROTOCOL_TLSv1) self.assertEqual(ctx.verify_mode, ssl.CERT_NONE) - self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) + self._assert_context_options(ctx) ctx = ssl._create_stdlib_context(ssl.PROTOCOL_TLSv1, cert_reqs=ssl.CERT_REQUIRED, @@ -1294,12 +1298,12 @@ class ContextTests(unittest.TestCase): self.assertEqual(ctx.protocol, ssl.PROTOCOL_TLSv1) self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED) self.assertTrue(ctx.check_hostname) - self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) + self._assert_context_options(ctx) ctx = ssl._create_stdlib_context(purpose=ssl.Purpose.CLIENT_AUTH) self.assertEqual(ctx.protocol, ssl.PROTOCOL_SSLv23) self.assertEqual(ctx.verify_mode, ssl.CERT_NONE) - self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) + self._assert_context_options(ctx) def test_check_hostname(self): ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) -- cgit v1.2.1 From de56c23b9a770f4872dd5a89478b0b334cc7de86 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sat, 10 Sep 2016 23:23:33 +0200 Subject: Issue #28022: Deprecate ssl-related arguments in favor of SSLContext. The deprecation include manual creation of SSLSocket and certfile/keyfile (or similar) in ftplib, httplib, imaplib, smtplib, poplib and urllib. ssl.wrap_socket() is not marked as deprecated yet. --- Lib/asyncio/test_utils.py | 8 ++-- Lib/ftplib.py | 4 ++ Lib/http/client.py | 6 +++ Lib/imaplib.py | 5 ++- Lib/poplib.py | 4 ++ Lib/smtplib.py | 8 ++++ Lib/ssl.py | 1 - Lib/test/test_ftplib.py | 10 +++-- Lib/test/test_imaplib.py | 6 +-- Lib/test/test_nntplib.py | 6 ++- Lib/test/test_poplib.py | 10 +++-- Lib/test/test_ssl.py | 87 +++++++++++++++++++++++---------------- Lib/test/test_urllib.py | 9 ++-- Lib/test/test_urllib2_localnet.py | 34 ++++++++------- Lib/urllib/request.py | 3 ++ 15 files changed, 126 insertions(+), 75 deletions(-) (limited to 'Lib') diff --git a/Lib/asyncio/test_utils.py b/Lib/asyncio/test_utils.py index 396e6aed56..ac8a8ef752 100644 --- a/Lib/asyncio/test_utils.py +++ b/Lib/asyncio/test_utils.py @@ -117,10 +117,10 @@ class SSLWSGIServerMixin: 'test', 'test_asyncio') keyfile = os.path.join(here, 'ssl_key.pem') certfile = os.path.join(here, 'ssl_cert.pem') - ssock = ssl.wrap_socket(request, - keyfile=keyfile, - certfile=certfile, - server_side=True) + context = ssl.SSLContext() + context.load_cert_chain(certfile, keyfile) + + ssock = context.wrap_socket(request, server_side=True) try: self.RequestHandlerClass(ssock, client_address, self) ssock.close() diff --git a/Lib/ftplib.py b/Lib/ftplib.py index ee2a137a5c..8f36f537e8 100644 --- a/Lib/ftplib.py +++ b/Lib/ftplib.py @@ -728,6 +728,10 @@ else: if context is not None and certfile is not None: raise ValueError("context and certfile arguments are mutually " "exclusive") + if keyfile is not None or certfile is not None: + import warnings + warnings.warn("keyfile and certfile are deprecated, use a" + "custom context instead", DeprecationWarning, 2) self.keyfile = keyfile self.certfile = certfile if context is None: diff --git a/Lib/http/client.py b/Lib/http/client.py index 6ee1913545..a8e59b9561 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -1365,6 +1365,12 @@ else: check_hostname=None): super(HTTPSConnection, self).__init__(host, port, timeout, source_address) + if (key_file is not None or cert_file is not None or + check_hostname is not None): + import warnings + warnings.warn("key_file, cert_file and check_hostname are " + "deprecated, use a custom context instead.", + DeprecationWarning, 2) self.key_file = key_file self.cert_file = cert_file if context is None: diff --git a/Lib/imaplib.py b/Lib/imaplib.py index 965ed83f2b..cad508add8 100644 --- a/Lib/imaplib.py +++ b/Lib/imaplib.py @@ -1267,7 +1267,10 @@ if HAVE_SSL: if ssl_context is not None and certfile is not None: raise ValueError("ssl_context and certfile arguments are mutually " "exclusive") - + if keyfile is not None or certfile is not None: + import warnings + warnings.warn("keyfile and certfile are deprecated, use a" + "custom ssl_context instead", DeprecationWarning, 2) self.keyfile = keyfile self.certfile = certfile if ssl_context is None: diff --git a/Lib/poplib.py b/Lib/poplib.py index f6723904e8..cae6950eb6 100644 --- a/Lib/poplib.py +++ b/Lib/poplib.py @@ -431,6 +431,10 @@ if HAVE_SSL: if context is not None and certfile is not None: raise ValueError("context and certfile arguments are mutually " "exclusive") + if keyfile is not None or certfile is not None: + import warnings + warnings.warn("keyfile and certfile are deprecated, use a" + "custom context instead", DeprecationWarning, 2) self.keyfile = keyfile self.certfile = certfile if context is None: diff --git a/Lib/smtplib.py b/Lib/smtplib.py index 5b9e66536a..f7c2c77ab4 100755 --- a/Lib/smtplib.py +++ b/Lib/smtplib.py @@ -759,6 +759,10 @@ class SMTP: if context is not None and certfile is not None: raise ValueError("context and certfile arguments are mutually " "exclusive") + if keyfile is not None or certfile is not None: + import warnings + warnings.warn("keyfile and certfile are deprecated, use a" + "custom context instead", DeprecationWarning, 2) if context is None: context = ssl._create_stdlib_context(certfile=certfile, keyfile=keyfile) @@ -1011,6 +1015,10 @@ if _have_ssl: if context is not None and certfile is not None: raise ValueError("context and certfile arguments are mutually " "exclusive") + if keyfile is not None or certfile is not None: + import warnings + warnings.warn("keyfile and certfile are deprecated, use a" + "custom context instead", DeprecationWarning, 2) self.keyfile = keyfile self.certfile = certfile if context is None: diff --git a/Lib/ssl.py b/Lib/ssl.py index 3400b7f3f0..f3da464986 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -1091,7 +1091,6 @@ def wrap_socket(sock, keyfile=None, certfile=None, do_handshake_on_connect=True, suppress_ragged_eofs=True, ciphers=None): - return SSLSocket(sock=sock, keyfile=keyfile, certfile=certfile, server_side=server_side, cert_reqs=cert_reqs, ssl_version=ssl_version, ca_certs=ca_certs, diff --git a/Lib/test/test_ftplib.py b/Lib/test/test_ftplib.py index 9d8de211df..12fabc5e8b 100644 --- a/Lib/test/test_ftplib.py +++ b/Lib/test/test_ftplib.py @@ -311,10 +311,12 @@ if ssl is not None: _ssl_closing = False def secure_connection(self): - socket = ssl.wrap_socket(self.socket, suppress_ragged_eofs=False, - certfile=CERTFILE, server_side=True, - do_handshake_on_connect=False, - ssl_version=ssl.PROTOCOL_SSLv23) + context = ssl.SSLContext() + context.load_cert_chain(CERTFILE) + socket = context.wrap_socket(self.socket, + suppress_ragged_eofs=False, + server_side=True, + do_handshake_on_connect=False) self.del_channel() self.set_socket(socket) self._ssl_accepting = True diff --git a/Lib/test/test_imaplib.py b/Lib/test/test_imaplib.py index 8e4990b3cf..f95ebf4757 100644 --- a/Lib/test/test_imaplib.py +++ b/Lib/test/test_imaplib.py @@ -77,9 +77,9 @@ if ssl: def get_request(self): newsocket, fromaddr = self.socket.accept() - connstream = ssl.wrap_socket(newsocket, - server_side=True, - certfile=CERTFILE) + context = ssl.SSLContext() + context.load_cert_chain(CERTFILE) + connstream = context.wrap_socket(newsocket, server_side=True) return connstream, fromaddr IMAP4_SSL = imaplib.IMAP4_SSL diff --git a/Lib/test/test_nntplib.py b/Lib/test/test_nntplib.py index 2ef6d02c2b..feaba3c3c0 100644 --- a/Lib/test/test_nntplib.py +++ b/Lib/test/test_nntplib.py @@ -1542,8 +1542,10 @@ class LocalServerTests(unittest.TestCase): elif cmd == b'STARTTLS\r\n': reader.close() client.sendall(b'382 Begin TLS negotiation now\r\n') - client = ssl.wrap_socket( - client, server_side=True, certfile=certfile) + context = ssl.SSLContext() + context.load_cert_chain(certfile) + client = context.wrap_socket( + client, server_side=True) cleanup.enter_context(client) reader = cleanup.enter_context(client.makefile('rb')) elif cmd == b'QUIT\r\n': diff --git a/Lib/test/test_poplib.py b/Lib/test/test_poplib.py index 7b9606d359..e5b16dc98a 100644 --- a/Lib/test/test_poplib.py +++ b/Lib/test/test_poplib.py @@ -152,10 +152,12 @@ class DummyPOP3Handler(asynchat.async_chat): def cmd_stls(self, arg): if self.tls_active is False: self.push('+OK Begin TLS negotiation') - tls_sock = ssl.wrap_socket(self.socket, certfile=CERTFILE, - server_side=True, - do_handshake_on_connect=False, - suppress_ragged_eofs=False) + context = ssl.SSLContext() + context.load_cert_chain(CERTFILE) + tls_sock = context.wrap_socket(self.socket, + server_side=True, + do_handshake_on_connect=False, + suppress_ragged_eofs=False) self.del_channel() self.set_socket(tls_sock) self.tls_active = True diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 7488dc7baa..aed226c7e1 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -143,6 +143,21 @@ def skip_if_broken_ubuntu_ssl(func): needs_sni = unittest.skipUnless(ssl.HAS_SNI, "SNI support needed for this test") +def test_wrap_socket(sock, ssl_version=ssl.PROTOCOL_TLS, *, + cert_reqs=ssl.CERT_NONE, ca_certs=None, + ciphers=None, certfile=None, keyfile=None, + **kwargs): + context = ssl.SSLContext(ssl_version) + if cert_reqs is not None: + context.verify_mode = cert_reqs + if ca_certs is not None: + context.load_verify_locations(ca_certs) + if certfile is not None or keyfile is not None: + context.load_cert_chain(certfile, keyfile) + if ciphers is not None: + context.set_ciphers(ciphers) + return context.wrap_socket(sock, **kwargs) + class BasicSocketTests(unittest.TestCase): def test_constants(self): @@ -363,7 +378,7 @@ class BasicSocketTests(unittest.TestCase): # Issue #7943: an SSL object doesn't create reference cycles with # itself. s = socket.socket(socket.AF_INET) - ss = ssl.wrap_socket(s) + ss = test_wrap_socket(s) wr = weakref.ref(ss) with support.check_warnings(("", ResourceWarning)): del ss @@ -373,7 +388,7 @@ class BasicSocketTests(unittest.TestCase): # Methods on an unconnected SSLSocket propagate the original # OSError raise by the underlying socket object. s = socket.socket(socket.AF_INET) - with ssl.wrap_socket(s) as ss: + with test_wrap_socket(s) as ss: self.assertRaises(OSError, ss.recv, 1) self.assertRaises(OSError, ss.recv_into, bytearray(b'x')) self.assertRaises(OSError, ss.recvfrom, 1) @@ -387,10 +402,10 @@ class BasicSocketTests(unittest.TestCase): for timeout in (None, 0.0, 5.0): s = socket.socket(socket.AF_INET) s.settimeout(timeout) - with ssl.wrap_socket(s) as ss: + with test_wrap_socket(s) as ss: self.assertEqual(timeout, ss.gettimeout()) - def test_errors(self): + def test_errors_sslwrap(self): sock = socket.socket() self.assertRaisesRegex(ValueError, "certfile must be specified", @@ -400,10 +415,10 @@ class BasicSocketTests(unittest.TestCase): ssl.wrap_socket, sock, server_side=True) self.assertRaisesRegex(ValueError, "certfile must be specified for server-side operations", - ssl.wrap_socket, sock, server_side=True, certfile="") + ssl.wrap_socket, sock, server_side=True, certfile="") with ssl.wrap_socket(sock, server_side=True, certfile=CERTFILE) as s: self.assertRaisesRegex(ValueError, "can't connect in server-side mode", - s.connect, (HOST, 8080)) + s.connect, (HOST, 8080)) with self.assertRaises(OSError) as cm: with socket.socket() as sock: ssl.wrap_socket(sock, certfile=NONEXISTINGCERT) @@ -426,7 +441,7 @@ class BasicSocketTests(unittest.TestCase): sock = socket.socket() self.addCleanup(sock.close) with self.assertRaises(ssl.SSLError): - ssl.wrap_socket(sock, + test_wrap_socket(sock, certfile=certfile, ssl_version=ssl.PROTOCOL_TLSv1) @@ -613,7 +628,7 @@ class BasicSocketTests(unittest.TestCase): s.listen() c = socket.socket(socket.AF_INET) c.connect(s.getsockname()) - with ssl.wrap_socket(c, do_handshake_on_connect=False) as ss: + with test_wrap_socket(c, do_handshake_on_connect=False) as ss: with self.assertRaises(ValueError): ss.get_channel_binding("unknown-type") s.close() @@ -623,15 +638,15 @@ class BasicSocketTests(unittest.TestCase): def test_tls_unique_channel_binding(self): # unconnected should return None for known type s = socket.socket(socket.AF_INET) - with ssl.wrap_socket(s) as ss: + with test_wrap_socket(s) as ss: self.assertIsNone(ss.get_channel_binding("tls-unique")) # the same for server-side s = socket.socket(socket.AF_INET) - with ssl.wrap_socket(s, server_side=True, certfile=CERTFILE) as ss: + with test_wrap_socket(s, server_side=True, certfile=CERTFILE) as ss: self.assertIsNone(ss.get_channel_binding("tls-unique")) def test_dealloc_warn(self): - ss = ssl.wrap_socket(socket.socket(socket.AF_INET)) + ss = test_wrap_socket(socket.socket(socket.AF_INET)) r = repr(ss) with self.assertWarns(ResourceWarning) as cm: ss = None @@ -750,7 +765,7 @@ class BasicSocketTests(unittest.TestCase): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.addCleanup(s.close) with self.assertRaises(NotImplementedError) as cx: - ssl.wrap_socket(s, cert_reqs=ssl.CERT_NONE) + test_wrap_socket(s, cert_reqs=ssl.CERT_NONE) self.assertEqual(str(cx.exception), "only stream sockets are supported") ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) with self.assertRaises(NotImplementedError) as cx: @@ -826,7 +841,7 @@ class BasicSocketTests(unittest.TestCase): server = socket.socket(socket.AF_INET) self.addCleanup(server.close) port = support.bind_port(server) # Reserve port but don't listen - s = ssl.wrap_socket(socket.socket(socket.AF_INET), + s = test_wrap_socket(socket.socket(socket.AF_INET), cert_reqs=ssl.CERT_REQUIRED) self.addCleanup(s.close) rc = s.connect_ex((HOST, port)) @@ -1444,13 +1459,13 @@ class SimpleBackgroundTests(unittest.TestCase): self.addCleanup(server.__exit__, None, None, None) def test_connect(self): - with ssl.wrap_socket(socket.socket(socket.AF_INET), + with test_wrap_socket(socket.socket(socket.AF_INET), cert_reqs=ssl.CERT_NONE) as s: s.connect(self.server_addr) self.assertEqual({}, s.getpeercert()) # this should succeed because we specify the root cert - with ssl.wrap_socket(socket.socket(socket.AF_INET), + with test_wrap_socket(socket.socket(socket.AF_INET), cert_reqs=ssl.CERT_REQUIRED, ca_certs=SIGNING_CA) as s: s.connect(self.server_addr) @@ -1460,7 +1475,7 @@ class SimpleBackgroundTests(unittest.TestCase): # This should fail because we have no verification certs. Connection # failure crashes ThreadedEchoServer, so run this in an independent # test method. - s = ssl.wrap_socket(socket.socket(socket.AF_INET), + s = test_wrap_socket(socket.socket(socket.AF_INET), cert_reqs=ssl.CERT_REQUIRED) self.addCleanup(s.close) self.assertRaisesRegex(ssl.SSLError, "certificate verify failed", @@ -1468,7 +1483,7 @@ class SimpleBackgroundTests(unittest.TestCase): def test_connect_ex(self): # Issue #11326: check connect_ex() implementation - s = ssl.wrap_socket(socket.socket(socket.AF_INET), + s = test_wrap_socket(socket.socket(socket.AF_INET), cert_reqs=ssl.CERT_REQUIRED, ca_certs=SIGNING_CA) self.addCleanup(s.close) @@ -1478,7 +1493,7 @@ class SimpleBackgroundTests(unittest.TestCase): def test_non_blocking_connect_ex(self): # Issue #11326: non-blocking connect_ex() should allow handshake # to proceed after the socket gets ready. - s = ssl.wrap_socket(socket.socket(socket.AF_INET), + s = test_wrap_socket(socket.socket(socket.AF_INET), cert_reqs=ssl.CERT_REQUIRED, ca_certs=SIGNING_CA, do_handshake_on_connect=False) @@ -1578,7 +1593,7 @@ class SimpleBackgroundTests(unittest.TestCase): # Issue #5238: creating a file-like object with makefile() shouldn't # delay closing the underlying "real socket" (here tested with its # file descriptor, hence skipping the test under Windows). - ss = ssl.wrap_socket(socket.socket(socket.AF_INET)) + ss = test_wrap_socket(socket.socket(socket.AF_INET)) ss.connect(self.server_addr) fd = ss.fileno() f = ss.makefile() @@ -1596,7 +1611,7 @@ class SimpleBackgroundTests(unittest.TestCase): s = socket.socket(socket.AF_INET) s.connect(self.server_addr) s.setblocking(False) - s = ssl.wrap_socket(s, + s = test_wrap_socket(s, cert_reqs=ssl.CERT_NONE, do_handshake_on_connect=False) self.addCleanup(s.close) @@ -1622,16 +1637,16 @@ class SimpleBackgroundTests(unittest.TestCase): _test_get_server_certificate_fail(self, *self.server_addr) def test_ciphers(self): - with ssl.wrap_socket(socket.socket(socket.AF_INET), + with test_wrap_socket(socket.socket(socket.AF_INET), cert_reqs=ssl.CERT_NONE, ciphers="ALL") as s: s.connect(self.server_addr) - with ssl.wrap_socket(socket.socket(socket.AF_INET), + with test_wrap_socket(socket.socket(socket.AF_INET), cert_reqs=ssl.CERT_NONE, ciphers="DEFAULT") as s: s.connect(self.server_addr) # Error checking can happen at instantiation or when connecting with self.assertRaisesRegex(ssl.SSLError, "No cipher can be selected"): with socket.socket(socket.AF_INET) as sock: - s = ssl.wrap_socket(sock, + s = test_wrap_socket(sock, cert_reqs=ssl.CERT_NONE, ciphers="^$:,;?*'dorothyx") s.connect(self.server_addr) @@ -1749,7 +1764,7 @@ class NetworkedTests(unittest.TestCase): # Issue #12065: on a timeout, connect_ex() should return the original # errno (mimicking the behaviour of non-SSL sockets). with support.transient_internet(REMOTE_HOST): - s = ssl.wrap_socket(socket.socket(socket.AF_INET), + s = test_wrap_socket(socket.socket(socket.AF_INET), cert_reqs=ssl.CERT_REQUIRED, do_handshake_on_connect=False) self.addCleanup(s.close) @@ -2040,7 +2055,7 @@ if _have_threads: class ConnectionHandler (asyncore.dispatcher_with_send): def __init__(self, conn, certfile): - self.socket = ssl.wrap_socket(conn, server_side=True, + self.socket = test_wrap_socket(conn, server_side=True, certfile=certfile, do_handshake_on_connect=False) asyncore.dispatcher_with_send.__init__(self, self.socket) @@ -2401,7 +2416,7 @@ if _have_threads: connectionchatty=False) with server, \ socket.socket() as sock, \ - ssl.wrap_socket(sock, + test_wrap_socket(sock, certfile=certfile, ssl_version=ssl.PROTOCOL_TLSv1) as s: try: @@ -2448,7 +2463,7 @@ if _have_threads: c.connect((HOST, port)) listener_gone.wait() try: - ssl_sock = ssl.wrap_socket(c) + ssl_sock = test_wrap_socket(c) except OSError: pass else: @@ -2638,7 +2653,7 @@ if _have_threads: sys.stdout.write( " client: read %r from server, starting TLS...\n" % msg) - conn = ssl.wrap_socket(s, ssl_version=ssl.PROTOCOL_TLSv1) + conn = test_wrap_socket(s, ssl_version=ssl.PROTOCOL_TLSv1) wrapped = True elif indata == b"ENDTLS" and msg.startswith(b"ok"): # ENDTLS ok, switch back to clear text @@ -2699,7 +2714,7 @@ if _have_threads: indata = b"FOO\n" server = AsyncoreEchoServer(CERTFILE) with server: - s = ssl.wrap_socket(socket.socket()) + s = test_wrap_socket(socket.socket()) s.connect(('127.0.0.1', server.port)) if support.verbose: sys.stdout.write( @@ -2732,7 +2747,7 @@ if _have_threads: chatty=True, connectionchatty=False) with server: - s = ssl.wrap_socket(socket.socket(), + s = test_wrap_socket(socket.socket(), server_side=False, certfile=CERTFILE, ca_certs=CERTFILE, @@ -2856,7 +2871,7 @@ if _have_threads: self.addCleanup(server.__exit__, None, None) s = socket.create_connection((HOST, server.port)) self.addCleanup(s.close) - s = ssl.wrap_socket(s, suppress_ragged_eofs=False) + s = test_wrap_socket(s, suppress_ragged_eofs=False) self.addCleanup(s.close) # recv/read(0) should return no data @@ -2878,7 +2893,7 @@ if _have_threads: chatty=True, connectionchatty=False) with server: - s = ssl.wrap_socket(socket.socket(), + s = test_wrap_socket(socket.socket(), server_side=False, certfile=CERTFILE, ca_certs=CERTFILE, @@ -2932,12 +2947,12 @@ if _have_threads: c.connect((host, port)) # Will attempt handshake and time out self.assertRaisesRegex(socket.timeout, "timed out", - ssl.wrap_socket, c) + test_wrap_socket, c) finally: c.close() try: c = socket.socket(socket.AF_INET) - c = ssl.wrap_socket(c) + c = test_wrap_socket(c) c.settimeout(0.2) # Will attempt handshake and time out self.assertRaisesRegex(socket.timeout, "timed out", @@ -3062,7 +3077,7 @@ if _have_threads: chatty=True, connectionchatty=False) with server: - s = ssl.wrap_socket(socket.socket(), + s = test_wrap_socket(socket.socket(), server_side=False, certfile=CERTFILE, ca_certs=CERTFILE, @@ -3087,7 +3102,7 @@ if _have_threads: s.close() # now, again - s = ssl.wrap_socket(socket.socket(), + s = test_wrap_socket(socket.socket(), server_side=False, certfile=CERTFILE, ca_certs=CERTFILE, diff --git a/Lib/test/test_urllib.py b/Lib/test/test_urllib.py index 8f06b08afa..43ea6b8b57 100644 --- a/Lib/test/test_urllib.py +++ b/Lib/test/test_urllib.py @@ -469,10 +469,11 @@ Connection: close @unittest.skipUnless(ssl, "ssl module required") def test_cafile_and_context(self): context = ssl.create_default_context() - with self.assertRaises(ValueError): - urllib.request.urlopen( - "https://localhost", cafile="/nonexistent/path", context=context - ) + with support.check_warnings(('', DeprecationWarning)): + with self.assertRaises(ValueError): + urllib.request.urlopen( + "https://localhost", cafile="/nonexistent/path", context=context + ) class urlopen_DataTests(unittest.TestCase): """Test urlopen() opening a data URL.""" diff --git a/Lib/test/test_urllib2_localnet.py b/Lib/test/test_urllib2_localnet.py index e9564fde62..e6a522c6c5 100644 --- a/Lib/test/test_urllib2_localnet.py +++ b/Lib/test/test_urllib2_localnet.py @@ -548,26 +548,28 @@ class TestUrlopen(unittest.TestCase): def test_https_with_cafile(self): handler = self.start_https_server(certfile=CERT_localhost) - # Good cert - data = self.urlopen("https://localhost:%s/bizarre" % handler.port, - cafile=CERT_localhost) - self.assertEqual(data, b"we care a bit") - # Bad cert - with self.assertRaises(urllib.error.URLError) as cm: - self.urlopen("https://localhost:%s/bizarre" % handler.port, - cafile=CERT_fakehostname) - # Good cert, but mismatching hostname - handler = self.start_https_server(certfile=CERT_fakehostname) - with self.assertRaises(ssl.CertificateError) as cm: - self.urlopen("https://localhost:%s/bizarre" % handler.port, - cafile=CERT_fakehostname) + with support.check_warnings(('', DeprecationWarning)): + # Good cert + data = self.urlopen("https://localhost:%s/bizarre" % handler.port, + cafile=CERT_localhost) + self.assertEqual(data, b"we care a bit") + # Bad cert + with self.assertRaises(urllib.error.URLError) as cm: + self.urlopen("https://localhost:%s/bizarre" % handler.port, + cafile=CERT_fakehostname) + # Good cert, but mismatching hostname + handler = self.start_https_server(certfile=CERT_fakehostname) + with self.assertRaises(ssl.CertificateError) as cm: + self.urlopen("https://localhost:%s/bizarre" % handler.port, + cafile=CERT_fakehostname) def test_https_with_cadefault(self): handler = self.start_https_server(certfile=CERT_localhost) # Self-signed cert should fail verification with system certificate store - with self.assertRaises(urllib.error.URLError) as cm: - self.urlopen("https://localhost:%s/bizarre" % handler.port, - cadefault=True) + with support.check_warnings(('', DeprecationWarning)): + with self.assertRaises(urllib.error.URLError) as cm: + self.urlopen("https://localhost:%s/bizarre" % handler.port, + cadefault=True) def test_https_sni(self): if ssl is None: diff --git a/Lib/urllib/request.py b/Lib/urllib/request.py index cc4f0bf089..5f15b74f4d 100644 --- a/Lib/urllib/request.py +++ b/Lib/urllib/request.py @@ -198,6 +198,9 @@ def urlopen(url, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, ''' global _opener if cafile or capath or cadefault: + import warnings + warnings.warn("cafile, cpath and cadefault are deprecated, use a " + "custom context instead.", DeprecationWarning, 2) if context is not None: raise ValueError( "You can't pass both context and any of cafile, capath, and " -- cgit v1.2.1 From 7f85dccc70da5218cbd16e4c0a35e9b7cf349276 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sat, 10 Sep 2016 23:44:53 +0200 Subject: Issue #19500: Add client-side SSL session resumption to the ssl module. --- Lib/ssl.py | 65 ++++++++++++++++++++++++------ Lib/test/test_ssl.py | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 163 insertions(+), 14 deletions(-) (limited to 'Lib') diff --git a/Lib/ssl.py b/Lib/ssl.py index f3da464986..df5e98efc7 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -99,7 +99,7 @@ from enum import Enum as _Enum, IntEnum as _IntEnum, IntFlag as _IntFlag import _ssl # if we can't import it, let the error propagate from _ssl import OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_INFO, OPENSSL_VERSION -from _ssl import _SSLContext, MemoryBIO +from _ssl import _SSLContext, MemoryBIO, SSLSession from _ssl import ( SSLError, SSLZeroReturnError, SSLWantReadError, SSLWantWriteError, SSLSyscallError, SSLEOFError, @@ -391,18 +391,18 @@ class SSLContext(_SSLContext): def wrap_socket(self, sock, server_side=False, do_handshake_on_connect=True, suppress_ragged_eofs=True, - server_hostname=None): + server_hostname=None, session=None): return SSLSocket(sock=sock, server_side=server_side, do_handshake_on_connect=do_handshake_on_connect, suppress_ragged_eofs=suppress_ragged_eofs, server_hostname=server_hostname, - _context=self) + _context=self, _session=session) def wrap_bio(self, incoming, outgoing, server_side=False, - server_hostname=None): + server_hostname=None, session=None): sslobj = self._wrap_bio(incoming, outgoing, server_side=server_side, server_hostname=server_hostname) - return SSLObject(sslobj) + return SSLObject(sslobj, session=session) def set_npn_protocols(self, npn_protocols): protos = bytearray() @@ -572,10 +572,12 @@ class SSLObject: * The ``do_handshake_on_connect`` and ``suppress_ragged_eofs`` machinery. """ - def __init__(self, sslobj, owner=None): + def __init__(self, sslobj, owner=None, session=None): self._sslobj = sslobj # Note: _sslobj takes a weak reference to owner self._sslobj.owner = owner or self + if session is not None: + self._sslobj.session = session @property def context(self): @@ -586,6 +588,20 @@ class SSLObject: def context(self, ctx): self._sslobj.context = ctx + @property + def session(self): + """The SSLSession for client socket.""" + return self._sslobj.session + + @session.setter + def session(self, session): + self._sslobj.session = session + + @property + def session_reused(self): + """Was the client session reused during handshake""" + return self._sslobj.session_reused + @property def server_side(self): """Whether this is a server-side socket.""" @@ -703,7 +719,7 @@ class SSLSocket(socket): family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None, suppress_ragged_eofs=True, npn_protocols=None, ciphers=None, server_hostname=None, - _context=None): + _context=None, _session=None): if _context: self._context = _context @@ -735,11 +751,16 @@ class SSLSocket(socket): # mixed in. if sock.getsockopt(SOL_SOCKET, SO_TYPE) != SOCK_STREAM: raise NotImplementedError("only stream sockets are supported") - if server_side and server_hostname: - raise ValueError("server_hostname can only be specified " - "in client mode") + if server_side: + if server_hostname: + raise ValueError("server_hostname can only be specified " + "in client mode") + if _session is not None: + raise ValueError("session can only be specified in " + "client mode") if self._context.check_hostname and not server_hostname: raise ValueError("check_hostname requires server_hostname") + self._session = _session self.server_side = server_side self.server_hostname = server_hostname self.do_handshake_on_connect = do_handshake_on_connect @@ -775,7 +796,8 @@ class SSLSocket(socket): try: sslobj = self._context._wrap_socket(self, server_side, server_hostname) - self._sslobj = SSLObject(sslobj, owner=self) + self._sslobj = SSLObject(sslobj, owner=self, + session=self._session) if do_handshake_on_connect: timeout = self.gettimeout() if timeout == 0.0: @@ -796,6 +818,24 @@ class SSLSocket(socket): self._context = ctx self._sslobj.context = ctx + @property + def session(self): + """The SSLSession for client socket.""" + if self._sslobj is not None: + return self._sslobj.session + + @session.setter + def session(self, session): + self._session = session + if self._sslobj is not None: + self._sslobj.session = session + + @property + def session_reused(self): + """Was the client session reused during handshake""" + if self._sslobj is not None: + return self._sslobj.session_reused + def dup(self): raise NotImplemented("Can't dup() %s instances" % self.__class__.__name__) @@ -1028,7 +1068,8 @@ class SSLSocket(socket): if self._connected: raise ValueError("attempt to connect already-connected SSLSocket!") sslobj = self.context._wrap_socket(self, False, self.server_hostname) - self._sslobj = SSLObject(sslobj, owner=self) + self._sslobj = SSLObject(sslobj, owner=self, + session=self._session) try: if connect_ex: rc = socket.connect_ex(self, addr) diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index aed226c7e1..61744ae95a 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -2163,7 +2163,8 @@ if _have_threads: self.server.close() def server_params_test(client_context, server_context, indata=b"FOO\n", - chatty=True, connectionchatty=False, sni_name=None): + chatty=True, connectionchatty=False, sni_name=None, + session=None): """ Launch a server, connect a client to it and try various reads and writes. @@ -2174,7 +2175,7 @@ if _have_threads: connectionchatty=False) with server: with client_context.wrap_socket(socket.socket(), - server_hostname=sni_name) as s: + server_hostname=sni_name, session=session) as s: s.connect((HOST, server.port)) for arg in [indata, bytearray(indata), memoryview(indata)]: if connectionchatty: @@ -2202,6 +2203,8 @@ if _have_threads: 'client_alpn_protocol': s.selected_alpn_protocol(), 'client_npn_protocol': s.selected_npn_protocol(), 'version': s.version(), + 'session_reused': s.session_reused, + 'session': s.session, }) s.close() stats['server_alpn_protocols'] = server.selected_alpn_protocols @@ -3412,6 +3415,111 @@ if _have_threads: s.sendfile(file) self.assertEqual(s.recv(1024), TEST_DATA) + def test_session(self): + server_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + server_context.load_cert_chain(SIGNED_CERTFILE) + client_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + client_context.verify_mode = ssl.CERT_REQUIRED + client_context.load_verify_locations(SIGNING_CA) + + # first conncetion without session + stats = server_params_test(client_context, server_context) + session = stats['session'] + self.assertTrue(session.id) + self.assertGreater(session.time, 0) + self.assertGreater(session.timeout, 0) + self.assertTrue(session.has_ticket) + if ssl.OPENSSL_VERSION_INFO > (1, 0, 1): + self.assertGreater(session.ticket_lifetime_hint, 0) + self.assertFalse(stats['session_reused']) + sess_stat = server_context.session_stats() + self.assertEqual(sess_stat['accept'], 1) + self.assertEqual(sess_stat['hits'], 0) + + # reuse session + stats = server_params_test(client_context, server_context, session=session) + sess_stat = server_context.session_stats() + self.assertEqual(sess_stat['accept'], 2) + self.assertEqual(sess_stat['hits'], 1) + self.assertTrue(stats['session_reused']) + session2 = stats['session'] + self.assertEqual(session2.id, session.id) + self.assertEqual(session2, session) + self.assertIsNot(session2, session) + self.assertGreaterEqual(session2.time, session.time) + self.assertGreaterEqual(session2.timeout, session.timeout) + + # another one without session + stats = server_params_test(client_context, server_context) + self.assertFalse(stats['session_reused']) + session3 = stats['session'] + self.assertNotEqual(session3.id, session.id) + self.assertNotEqual(session3, session) + sess_stat = server_context.session_stats() + self.assertEqual(sess_stat['accept'], 3) + self.assertEqual(sess_stat['hits'], 1) + + # reuse session again + stats = server_params_test(client_context, server_context, session=session) + self.assertTrue(stats['session_reused']) + session4 = stats['session'] + self.assertEqual(session4.id, session.id) + self.assertEqual(session4, session) + self.assertGreaterEqual(session4.time, session.time) + self.assertGreaterEqual(session4.timeout, session.timeout) + sess_stat = server_context.session_stats() + self.assertEqual(sess_stat['accept'], 4) + self.assertEqual(sess_stat['hits'], 2) + + def test_session_handling(self): + context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + context.verify_mode = ssl.CERT_REQUIRED + context.load_verify_locations(CERTFILE) + context.load_cert_chain(CERTFILE) + + context2 = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + context2.verify_mode = ssl.CERT_REQUIRED + context2.load_verify_locations(CERTFILE) + context2.load_cert_chain(CERTFILE) + + server = ThreadedEchoServer(context=context, chatty=False) + with server: + with context.wrap_socket(socket.socket()) as s: + # session is None before handshake + self.assertEqual(s.session, None) + self.assertEqual(s.session_reused, None) + s.connect((HOST, server.port)) + session = s.session + self.assertTrue(session) + with self.assertRaises(TypeError) as e: + s.session = object + self.assertEqual(str(e.exception), 'Value is not a SSLSession.') + + with context.wrap_socket(socket.socket()) as s: + s.connect((HOST, server.port)) + # cannot set session after handshake + with self.assertRaises(ValueError) as e: + s.session = session + self.assertEqual(str(e.exception), + 'Cannot set session after handshake.') + + with context.wrap_socket(socket.socket()) as s: + # can set session before handshake and before the + # connection was established + s.session = session + s.connect((HOST, server.port)) + self.assertEqual(s.session.id, session.id) + self.assertEqual(s.session, session) + self.assertEqual(s.session_reused, True) + + with context2.wrap_socket(socket.socket()) as s: + # cannot re-use session with a different SSLContext + with self.assertRaises(ValueError) as e: + s.session = session + s.connect((HOST, server.port)) + self.assertEqual(str(e.exception), + 'Session refers to a different SSLContext.') + def test_main(verbose=False): if support.verbose: -- cgit v1.2.1 From 1ca2ef0241a1d778e507f519d2a939d625d31daa Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 11 Sep 2016 01:39:01 +0300 Subject: Issue #28070: Fixed parsing inline verbose flag in regular expressions. --- Lib/sre_parse.py | 1 + Lib/test/test_re.py | 3 +++ 2 files changed, 4 insertions(+) (limited to 'Lib') diff --git a/Lib/sre_parse.py b/Lib/sre_parse.py index 09f3be25ab..d74e93ff5c 100644 --- a/Lib/sre_parse.py +++ b/Lib/sre_parse.py @@ -847,6 +847,7 @@ def parse(str, flags=0, pattern=None): pattern = Pattern() pattern.flags = flags | SRE_FLAG_VERBOSE pattern.str = str + source.seek(0) p = _parse_sub(source, pattern, True, False) p.pattern.flags = fix_flags(str, p.pattern.flags) diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index 2322ca9bb4..afe8738e83 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -1276,6 +1276,9 @@ class ReTests(unittest.TestCase): q = p.match(upper_char) self.assertTrue(q) + self.assertTrue(re.match('(?ixu) ' + upper_char, lower_char)) + self.assertTrue(re.match('(?ixu) ' + lower_char, upper_char)) + def test_dollar_matches_twice(self): "$ matches the end of string, and just before the terminating \n" pattern = re.compile('$') -- cgit v1.2.1 From 0780d6cb0e6e406d0030dee40156a6dd00066147 Mon Sep 17 00:00:00 2001 From: Nick Coghlan Date: Sun, 11 Sep 2016 14:45:49 +1000 Subject: Issue #23722: Initialize __class__ from type.__new__() The __class__ cell used by zero-argument super() is now initialized from type.__new__ rather than __build_class__, so class methods relying on that will now work correctly when called from metaclass methods during class creation. Patch by Martin Teichmann. --- Lib/importlib/_bootstrap_external.py | 5 ++- Lib/test/test_super.py | 81 ++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 9a7e6ec937..bfb89dacbb 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -236,7 +236,8 @@ _code_type = type(_write_atomic.__code__) # Python 3.6b1 3373 (add BUILD_STRING opcode #27078) # Python 3.6b1 3375 (add SETUP_ANNOTATIONS and STORE_ANNOTATION opcodes # #27985) -# Python 3.6a1 3376 (simplify CALL_FUNCTIONs & BUILD_MAP_UNPACK_WITH_CALL) +# Python 3.6b1 3376 (simplify CALL_FUNCTIONs & BUILD_MAP_UNPACK_WITH_CALL) +# Python 3.6b1 3377 (set __class__ cell from type.__new__ #23722) # # MAGIC must change whenever the bytecode emitted by the compiler may no # longer be understood by older implementations of the eval loop (usually @@ -245,7 +246,7 @@ _code_type = type(_write_atomic.__code__) # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # in PC/launcher.c must also be updated. -MAGIC_NUMBER = (3376).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3377).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c _PYCACHE = '__pycache__' diff --git a/Lib/test/test_super.py b/Lib/test/test_super.py index b84863fe53..a7ceded0b8 100644 --- a/Lib/test/test_super.py +++ b/Lib/test/test_super.py @@ -143,6 +143,87 @@ class TestSuper(unittest.TestCase): return __class__ self.assertIs(X.f(), X) + def test___class___new(self): + test_class = None + + class Meta(type): + def __new__(cls, name, bases, namespace): + nonlocal test_class + self = super().__new__(cls, name, bases, namespace) + test_class = self.f() + return self + + class A(metaclass=Meta): + @staticmethod + def f(): + return __class__ + + self.assertIs(test_class, A) + + def test___class___delayed(self): + test_namespace = None + + class Meta(type): + def __new__(cls, name, bases, namespace): + nonlocal test_namespace + test_namespace = namespace + return None + + class A(metaclass=Meta): + @staticmethod + def f(): + return __class__ + + self.assertIs(A, None) + + B = type("B", (), test_namespace) + self.assertIs(B.f(), B) + + def test___class___mro(self): + test_class = None + + class Meta(type): + def mro(self): + # self.f() doesn't work yet... + self.__dict__["f"]() + return super().mro() + + class A(metaclass=Meta): + def f(): + nonlocal test_class + test_class = __class__ + + self.assertIs(test_class, A) + + def test___classcell___deleted(self): + class Meta(type): + def __new__(cls, name, bases, namespace): + del namespace['__classcell__'] + return super().__new__(cls, name, bases, namespace) + + class A(metaclass=Meta): + @staticmethod + def f(): + __class__ + + with self.assertRaises(NameError): + A.f() + + def test___classcell___reset(self): + class Meta(type): + def __new__(cls, name, bases, namespace): + namespace['__classcell__'] = 0 + return super().__new__(cls, name, bases, namespace) + + class A(metaclass=Meta): + @staticmethod + def f(): + __class__ + + with self.assertRaises(NameError): + A.f() + self.assertEqual(A.__classcell__, 0) + def test_obscure_super_errors(self): def f(): super() -- cgit v1.2.1 From 4c21c04f36ab2a2b2a6a5112950fccebdebb5319 Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Sat, 10 Sep 2016 23:36:59 -0700 Subject: issue23591: add auto() for auto-generating Enum member values --- Lib/enum.py | 50 ++++++++++++++++++++++++--------- Lib/test/test_enum.py | 77 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 112 insertions(+), 15 deletions(-) (limited to 'Lib') diff --git a/Lib/enum.py b/Lib/enum.py index 6a1899941f..1f8766479e 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -10,7 +10,11 @@ except ImportError: from collections import OrderedDict -__all__ = ['EnumMeta', 'Enum', 'IntEnum', 'Flag', 'IntFlag', 'unique'] +__all__ = [ + 'EnumMeta', + 'Enum', 'IntEnum', 'Flag', 'IntFlag', + 'auto', 'unique', + ] def _is_descriptor(obj): @@ -36,7 +40,6 @@ def _is_sunder(name): name[-2:-1] != '_' and len(name) > 2) - def _make_class_unpicklable(cls): """Make the given class un-picklable.""" def _break_on_call_reduce(self, proto): @@ -44,6 +47,12 @@ def _make_class_unpicklable(cls): cls.__reduce_ex__ = _break_on_call_reduce cls.__module__ = '' +class auto: + """ + Instances are replaced with an appropriate value in Enum class suites. + """ + pass + class _EnumDict(dict): """Track enum member order and ensure member names are not reused. @@ -55,6 +64,7 @@ class _EnumDict(dict): def __init__(self): super().__init__() self._member_names = [] + self._last_values = [] def __setitem__(self, key, value): """Changes anything not dundered or not a descriptor. @@ -71,6 +81,8 @@ class _EnumDict(dict): '_generate_next_value_', '_missing_', ): raise ValueError('_names_ are reserved for future Enum use') + if key == '_generate_next_value_': + setattr(self, '_generate_next_value', value) elif _is_dunder(key): if key == '__order__': key = '_order_' @@ -81,11 +93,13 @@ class _EnumDict(dict): if key in self: # enum overwriting a descriptor? raise TypeError('%r already defined as: %r' % (key, self[key])) + if isinstance(value, auto): + value = self._generate_next_value(key, 1, len(self._member_names), self._last_values[:]) self._member_names.append(key) + self._last_values.append(value) super().__setitem__(key, value) - # Dummy value for Enum as EnumMeta explicitly checks for it, but of course # until EnumMeta finishes running the first time the Enum class doesn't exist. # This is also why there are checks in EnumMeta like `if Enum is not None` @@ -366,10 +380,11 @@ class EnumMeta(type): names = names.replace(',', ' ').split() if isinstance(names, (tuple, list)) and isinstance(names[0], str): original_names, names = names, [] - last_value = None + last_values = [] for count, name in enumerate(original_names): - last_value = first_enum._generate_next_value_(name, start, count, last_value) - names.append((name, last_value)) + value = first_enum._generate_next_value_(name, start, count, last_values[:]) + last_values.append(value) + names.append((name, value)) # Here, names is either an iterable of (name, value) or a mapping. for item in names: @@ -514,11 +529,15 @@ class Enum(metaclass=EnumMeta): # still not found -- try _missing_ hook return cls._missing_(value) - @staticmethod - def _generate_next_value_(name, start, count, last_value): - if not count: + def _generate_next_value_(name, start, count, last_values): + for last_value in reversed(last_values): + try: + return last_value + 1 + except TypeError: + pass + else: return start - return last_value + 1 + @classmethod def _missing_(cls, value): raise ValueError("%r is not a valid %s" % (value, cls.__name__)) @@ -616,8 +635,8 @@ def _reduce_ex_by_name(self, proto): class Flag(Enum): """Support for flags""" - @staticmethod - def _generate_next_value_(name, start, count, last_value): + + def _generate_next_value_(name, start, count, last_values): """ Generate the next value when not given. @@ -628,7 +647,12 @@ class Flag(Enum): """ if not count: return start if start is not None else 1 - high_bit = _high_bit(last_value) + for last_value in reversed(last_values): + try: + high_bit = _high_bit(last_value) + break + except TypeError: + raise TypeError('Invalid Flag value: %r' % last_value) from None return 2 ** (high_bit+1) @classmethod diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index 698fd307a0..153bfb40a7 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -3,7 +3,7 @@ import inspect import pydoc import unittest from collections import OrderedDict -from enum import Enum, IntEnum, EnumMeta, Flag, IntFlag, unique +from enum import Enum, IntEnum, EnumMeta, Flag, IntFlag, unique, auto from io import StringIO from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL from test import support @@ -113,6 +113,7 @@ class TestHelpers(unittest.TestCase): '__', '___', '____', '_____',): self.assertFalse(enum._is_dunder(s)) +# tests class TestEnum(unittest.TestCase): @@ -1578,6 +1579,61 @@ class TestEnum(unittest.TestCase): self.assertEqual(LabelledList.unprocessed, 1) self.assertEqual(LabelledList(1), LabelledList.unprocessed) + def test_auto_number(self): + class Color(Enum): + red = auto() + blue = auto() + green = auto() + + self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) + self.assertEqual(Color.red.value, 1) + self.assertEqual(Color.blue.value, 2) + self.assertEqual(Color.green.value, 3) + + def test_auto_name(self): + class Color(Enum): + def _generate_next_value_(name, start, count, last): + return name + red = auto() + blue = auto() + green = auto() + + self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) + self.assertEqual(Color.red.value, 'red') + self.assertEqual(Color.blue.value, 'blue') + self.assertEqual(Color.green.value, 'green') + + def test_auto_name_inherit(self): + class AutoNameEnum(Enum): + def _generate_next_value_(name, start, count, last): + return name + class Color(AutoNameEnum): + red = auto() + blue = auto() + green = auto() + + self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) + self.assertEqual(Color.red.value, 'red') + self.assertEqual(Color.blue.value, 'blue') + self.assertEqual(Color.green.value, 'green') + + def test_auto_garbage(self): + class Color(Enum): + red = 'red' + blue = auto() + self.assertEqual(Color.blue.value, 1) + + def test_auto_garbage_corrected(self): + class Color(Enum): + red = 'red' + blue = 2 + green = auto() + + self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) + self.assertEqual(Color.red.value, 'red') + self.assertEqual(Color.blue.value, 2) + self.assertEqual(Color.green.value, 3) + class TestOrder(unittest.TestCase): @@ -1856,7 +1912,6 @@ class TestFlag(unittest.TestCase): test_pickle_dump_load(self.assertIs, FlagStooges.CURLY|FlagStooges.MOE) test_pickle_dump_load(self.assertIs, FlagStooges) - def test_containment(self): Perm = self.Perm R, W, X = Perm @@ -1877,6 +1932,24 @@ class TestFlag(unittest.TestCase): self.assertFalse(W in RX) self.assertFalse(X in RW) + def test_auto_number(self): + class Color(Flag): + red = auto() + blue = auto() + green = auto() + + self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) + self.assertEqual(Color.red.value, 1) + self.assertEqual(Color.blue.value, 2) + self.assertEqual(Color.green.value, 4) + + def test_auto_number_garbage(self): + with self.assertRaisesRegex(TypeError, 'Invalid Flag value: .not an int.'): + class Color(Flag): + red = 'not an int' + blue = auto() + + class TestIntFlag(unittest.TestCase): """Tests of the IntFlags.""" -- cgit v1.2.1 From 1383ce2312c21c2a66f47fd5acc96bd58162710a Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 11 Sep 2016 11:23:38 +0300 Subject: Issue #26885: xmlrpc now supports unmarshalling additional data types used by Apache XML-RPC implementation for numerics and None. --- Lib/test/test_xmlrpc.py | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ Lib/xmlrpc/client.py | 32 ++++++++++++++++++++++++++------ 2 files changed, 75 insertions(+), 6 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py index 29a9878dd0..df9c79e3df 100644 --- a/Lib/test/test_xmlrpc.py +++ b/Lib/test/test_xmlrpc.py @@ -1,5 +1,6 @@ import base64 import datetime +import decimal import sys import time import unittest @@ -237,6 +238,54 @@ class XMLRPCTestCase(unittest.TestCase): '') self.assertRaises(ResponseError, xmlrpclib.loads, data) + def check_loads(self, s, value, **kwargs): + dump = '%s' % s + result, m = xmlrpclib.loads(dump, **kwargs) + (newvalue,) = result + self.assertEqual(newvalue, value) + self.assertIs(type(newvalue), type(value)) + self.assertIsNone(m) + + def test_load_standard_types(self): + check = self.check_loads + check('string', 'string') + check('string', 'string') + check('𝔘𝔫𝔦𝔠𝔬𝔡𝔢 string', '𝔘𝔫𝔦𝔠𝔬𝔡𝔢 string') + check('2056183947', 2056183947) + check('-2056183947', -2056183947) + check('2056183947', 2056183947) + check('46093.78125', 46093.78125) + check('0', False) + check('AGJ5dGUgc3RyaW5n/w==', + xmlrpclib.Binary(b'\x00byte string\xff')) + check('AGJ5dGUgc3RyaW5n/w==', + b'\x00byte string\xff', use_builtin_types=True) + check('20050210T11:41:23', + xmlrpclib.DateTime('20050210T11:41:23')) + check('20050210T11:41:23', + datetime.datetime(2005, 2, 10, 11, 41, 23), + use_builtin_types=True) + check('' + '12' + '', [1, 2]) + check('' + 'b2' + 'a1' + '', {'a': 1, 'b': 2}) + + def test_load_extension_types(self): + check = self.check_loads + check('', None) + check('', None) + check('205', 205) + check('20561', 20561) + check('9876543210', 9876543210) + check('98765432100123456789', + 98765432100123456789) + check('93.78125', 93.78125) + check('9876543210.0123456789', + decimal.Decimal('9876543210.0123456789')) + def test_get_host_info(self): # see bug #3613, this raised a TypeError transp = xmlrpc.client.Transport() diff --git a/Lib/xmlrpc/client.py b/Lib/xmlrpc/client.py index 581a3b92ac..bd3278e005 100644 --- a/Lib/xmlrpc/client.py +++ b/Lib/xmlrpc/client.py @@ -132,6 +132,7 @@ import base64 import sys import time from datetime import datetime +from decimal import Decimal import http.client import urllib.parse from xml.parsers import expat @@ -667,6 +668,8 @@ class Unmarshaller: def start(self, tag, attrs): # prepare to handle this element + if ':' in tag: + tag = tag.split(':')[-1] if tag == "array" or tag == "struct": self._marks.append(len(self._stack)) self._data = [] @@ -682,9 +685,13 @@ class Unmarshaller: try: f = self.dispatch[tag] except KeyError: - pass # unknown tag ? - else: - return f(self, "".join(self._data)) + if ':' not in tag: + return # unknown tag ? + try: + f = self.dispatch[tag.split(':')[-1]] + except KeyError: + return # unknown tag ? + return f(self, "".join(self._data)) # # accelerator support @@ -694,9 +701,13 @@ class Unmarshaller: try: f = self.dispatch[tag] except KeyError: - pass # unknown tag ? - else: - return f(self, data) + if ':' not in tag: + return # unknown tag ? + try: + f = self.dispatch[tag.split(':')[-1]] + except KeyError: + return # unknown tag ? + return f(self, data) # # element decoders @@ -721,14 +732,23 @@ class Unmarshaller: def end_int(self, data): self.append(int(data)) self._value = 0 + dispatch["i1"] = end_int + dispatch["i2"] = end_int dispatch["i4"] = end_int dispatch["i8"] = end_int dispatch["int"] = end_int + dispatch["biginteger"] = end_int def end_double(self, data): self.append(float(data)) self._value = 0 dispatch["double"] = end_double + dispatch["float"] = end_double + + def end_bigdecimal(self, data): + self.append(Decimal(data)) + self._value = 0 + dispatch["bigdecimal"] = end_bigdecimal def end_string(self, data): if self._encoding: -- cgit v1.2.1 From 03d61e6976c4e3899fc84576c91427b60e78cf7a Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 11 Sep 2016 12:50:02 +0300 Subject: Issue #22493: Inline flags now should be used only at the start of the regular expression. Deprecation warning is emitted if uses them in the middle of the regular expression. --- Lib/distutils/filelist.py | 15 ++++++++++----- Lib/distutils/tests/test_filelist.py | 14 +++++++------- Lib/fnmatch.py | 2 +- Lib/http/cookies.py | 3 +-- Lib/sre_parse.py | 8 ++++++++ Lib/test/re_tests.py | 8 ++++---- Lib/test/test_fnmatch.py | 16 ++++++++-------- Lib/test/test_pyclbr.py | 2 +- Lib/test/test_re.py | 3 +++ 9 files changed, 43 insertions(+), 28 deletions(-) (limited to 'Lib') diff --git a/Lib/distutils/filelist.py b/Lib/distutils/filelist.py index 6522e69f06..c92d5fdba3 100644 --- a/Lib/distutils/filelist.py +++ b/Lib/distutils/filelist.py @@ -302,21 +302,26 @@ def translate_pattern(pattern, anchor=1, prefix=None, is_regex=0): else: return pattern + # ditch start and end characters + start, _, end = glob_to_re('_').partition('_') + if pattern: pattern_re = glob_to_re(pattern) + assert pattern_re.startswith(start) and pattern_re.endswith(end) else: pattern_re = '' if prefix is not None: - # ditch end of pattern character - empty_pattern = glob_to_re('') - prefix_re = glob_to_re(prefix)[:-len(empty_pattern)] + prefix_re = glob_to_re(prefix) + assert prefix_re.startswith(start) and prefix_re.endswith(end) + prefix_re = prefix_re[len(start): len(prefix_re) - len(end)] sep = os.sep if os.sep == '\\': sep = r'\\' - pattern_re = "^" + sep.join((prefix_re, ".*" + pattern_re)) + pattern_re = pattern_re[len(start): len(pattern_re) - len(end)] + pattern_re = r'%s\A%s%s.*%s%s' % (start, prefix_re, sep, pattern_re, end) else: # no prefix -- respect anchor flag if anchor: - pattern_re = "^" + pattern_re + pattern_re = r'%s\A%s' % (start, pattern_re[len(start):]) return re.compile(pattern_re) diff --git a/Lib/distutils/tests/test_filelist.py b/Lib/distutils/tests/test_filelist.py index 391af3cba2..c71342d0dc 100644 --- a/Lib/distutils/tests/test_filelist.py +++ b/Lib/distutils/tests/test_filelist.py @@ -51,14 +51,14 @@ class FileListTestCase(support.LoggingSilencer, for glob, regex in ( # simple cases - ('foo*', r'foo[^%(sep)s]*\Z(?ms)'), - ('foo?', r'foo[^%(sep)s]\Z(?ms)'), - ('foo??', r'foo[^%(sep)s][^%(sep)s]\Z(?ms)'), + ('foo*', r'(?s:foo[^%(sep)s]*)\Z'), + ('foo?', r'(?s:foo[^%(sep)s])\Z'), + ('foo??', r'(?s:foo[^%(sep)s][^%(sep)s])\Z'), # special cases - (r'foo\\*', r'foo\\\\[^%(sep)s]*\Z(?ms)'), - (r'foo\\\*', r'foo\\\\\\[^%(sep)s]*\Z(?ms)'), - ('foo????', r'foo[^%(sep)s][^%(sep)s][^%(sep)s][^%(sep)s]\Z(?ms)'), - (r'foo\\??', r'foo\\\\[^%(sep)s][^%(sep)s]\Z(?ms)')): + (r'foo\\*', r'(?s:foo\\\\[^%(sep)s]*)\Z'), + (r'foo\\\*', r'(?s:foo\\\\\\[^%(sep)s]*)\Z'), + ('foo????', r'(?s:foo[^%(sep)s][^%(sep)s][^%(sep)s][^%(sep)s])\Z'), + (r'foo\\??', r'(?s:foo\\\\[^%(sep)s][^%(sep)s])\Z')): regex = regex % {'sep': sep} self.assertEqual(glob_to_re(glob), regex) diff --git a/Lib/fnmatch.py b/Lib/fnmatch.py index 07b12295df..fd3b5142e3 100644 --- a/Lib/fnmatch.py +++ b/Lib/fnmatch.py @@ -106,4 +106,4 @@ def translate(pat): res = '%s[%s]' % (res, stuff) else: res = res + re.escape(c) - return res + r'\Z(?ms)' + return r'(?s:%s)\Z' % res diff --git a/Lib/http/cookies.py b/Lib/http/cookies.py index f078da5469..be3b080aa3 100644 --- a/Lib/http/cookies.py +++ b/Lib/http/cookies.py @@ -458,7 +458,6 @@ class Morsel(dict): _LegalKeyChars = r"\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=" _LegalValueChars = _LegalKeyChars + r'\[\]' _CookiePattern = re.compile(r""" - (?x) # This is a verbose pattern \s* # Optional whitespace at start of cookie (?P # Start of group 'key' [""" + _LegalKeyChars + r"""]+? # Any word of at least one letter @@ -475,7 +474,7 @@ _CookiePattern = re.compile(r""" )? # End of optional value group \s* # Any number of spaces. (\s+|;|$) # Ending either at space, semicolon, or EOS. - """, re.ASCII) # May be removed if safe. + """, re.ASCII | re.VERBOSE) # re.ASCII may be removed if safe. # At long last, here is the cookie class. Using this class is almost just like diff --git a/Lib/sre_parse.py b/Lib/sre_parse.py index d74e93ff5c..4a77f0c9a7 100644 --- a/Lib/sre_parse.py +++ b/Lib/sre_parse.py @@ -279,6 +279,9 @@ class Tokenizer: break result += c return result + @property + def pos(self): + return self.index - len(self.next or '') def tell(self): return self.index - len(self.next or '') def seek(self, index): @@ -727,8 +730,13 @@ def _parse(source, state, verbose): state.checklookbehindgroup(condgroup, source) elif char in FLAGS or char == "-": # flags + pos = source.pos flags = _parse_flags(source, state, char) if flags is None: # global flags + if pos != 3: # "(?x" + import warnings + warnings.warn('Flags not at the start of the expression', + DeprecationWarning, stacklevel=7) continue add_flags, del_flags = flags group = None diff --git a/Lib/test/re_tests.py b/Lib/test/re_tests.py index d3692f859a..a379d33aec 100755 --- a/Lib/test/re_tests.py +++ b/Lib/test/re_tests.py @@ -106,8 +106,8 @@ tests = [ ('a.*b', 'acc\nccb', FAIL), ('a.{4,5}b', 'acc\nccb', FAIL), ('a.b', 'a\rb', SUCCEED, 'found', 'a\rb'), - ('a.b(?s)', 'a\nb', SUCCEED, 'found', 'a\nb'), - ('a.*(?s)b', 'acc\nccb', SUCCEED, 'found', 'acc\nccb'), + ('(?s)a.b', 'a\nb', SUCCEED, 'found', 'a\nb'), + ('(?s)a.*b', 'acc\nccb', SUCCEED, 'found', 'acc\nccb'), ('(?s)a.{4,5}b', 'acc\nccb', SUCCEED, 'found', 'acc\nccb'), ('(?s)a.b', 'a\nb', SUCCEED, 'found', 'a\nb'), @@ -563,7 +563,7 @@ tests = [ # Check odd placement of embedded pattern modifiers # not an error under PCRE/PRE: - ('w(?i)', 'W', SUCCEED, 'found', 'W'), + ('(?i)w', 'W', SUCCEED, 'found', 'W'), # ('w(?i)', 'W', SYNTAX_ERROR), # Comments using the x embedded pattern modifier @@ -627,7 +627,7 @@ xyzabc # bug 114033: nothing to repeat (r'(x?)?', 'x', SUCCEED, 'found', 'x'), # bug 115040: rescan if flags are modified inside pattern - (r' (?x)foo ', 'foo', SUCCEED, 'found', 'foo'), + (r'(?x) foo ', 'foo', SUCCEED, 'found', 'foo'), # bug 115618: negative lookahead (r'(? Date: Sun, 11 Sep 2016 12:57:15 +0300 Subject: Issue #10740: sqlite3 no longer implicitly commit an open transaction before DDL statements This commit contains the following commits from ghaering/pysqlite: * https://github.com/ghaering/pysqlite/commit/f254c534948c41c0ceb8cbabf0d4a2f547754739 * https://github.com/ghaering/pysqlite/commit/796b3afe38cfdac5d7d5ec260826b0a596554631 * https://github.com/ghaering/pysqlite/commit/cae87ee68613697a5f4947b4a0941f59a28da1b6 * https://github.com/ghaering/pysqlite/commit/3567b31bb5e5b226ba006213a9c69dde3f155faf With the following additions: * Fixed a refcount error * Fixed a compiler warning * Made the string comparison a little more robust * Added a whatsnew entry --- Lib/sqlite3/test/transactions.py | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) (limited to 'Lib') diff --git a/Lib/sqlite3/test/transactions.py b/Lib/sqlite3/test/transactions.py index a25360a7d8..45f1b04c69 100644 --- a/Lib/sqlite3/test/transactions.py +++ b/Lib/sqlite3/test/transactions.py @@ -52,13 +52,13 @@ class TransactionTests(unittest.TestCase): except OSError: pass - def CheckDMLdoesAutoCommitBefore(self): + def CheckDMLDoesNotAutoCommitBefore(self): self.cur1.execute("create table test(i)") self.cur1.execute("insert into test(i) values (5)") self.cur1.execute("create table test2(j)") self.cur2.execute("select i from test") res = self.cur2.fetchall() - self.assertEqual(len(res), 1) + self.assertEqual(len(res), 0) def CheckInsertStartsTransaction(self): self.cur1.execute("create table test(i)") @@ -153,11 +153,6 @@ class SpecialCommandTests(unittest.TestCase): self.con = sqlite.connect(":memory:") self.cur = self.con.cursor() - def CheckVacuum(self): - self.cur.execute("create table test(i)") - self.cur.execute("insert into test(i) values (5)") - self.cur.execute("vacuum") - def CheckDropTable(self): self.cur.execute("create table test(i)") self.cur.execute("insert into test(i) values (5)") @@ -172,10 +167,35 @@ class SpecialCommandTests(unittest.TestCase): self.cur.close() self.con.close() +class TransactionalDDL(unittest.TestCase): + def setUp(self): + self.con = sqlite.connect(":memory:") + + def CheckDdlDoesNotAutostartTransaction(self): + # For backwards compatibility reasons, DDL statements should not + # implicitly start a transaction. + self.con.execute("create table test(i)") + self.con.rollback() + result = self.con.execute("select * from test").fetchall() + self.assertEqual(result, []) + + def CheckTransactionalDDL(self): + # You can achieve transactional DDL by issuing a BEGIN + # statement manually. + self.con.execute("begin") + self.con.execute("create table test(i)") + self.con.rollback() + with self.assertRaises(sqlite.OperationalError): + self.con.execute("select * from test") + + def tearDown(self): + self.con.close() + def suite(): default_suite = unittest.makeSuite(TransactionTests, "Check") special_command_suite = unittest.makeSuite(SpecialCommandTests, "Check") - return unittest.TestSuite((default_suite, special_command_suite)) + ddl_suite = unittest.makeSuite(TransactionalDDL, "Check") + return unittest.TestSuite((default_suite, special_command_suite, ddl_suite)) def test(): runner = unittest.TextTestRunner() -- cgit v1.2.1 From 4993177a475f564b3a9b5dfb812fdf23cb49d81d Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 11 Sep 2016 14:41:02 +0300 Subject: Use sequence repetition instead of bytes constructor with integer argument. --- Lib/base64.py | 2 +- Lib/gzip.py | 4 ++-- Lib/hmac.py | 2 +- Lib/pickletools.py | 2 +- Lib/test/test_bufio.py | 2 +- Lib/test/test_bz2.py | 4 ++-- Lib/test/test_gzip.py | 4 ++-- Lib/test/test_io.py | 2 +- Lib/test/test_ipaddress.py | 10 +++++----- Lib/test/test_lzma.py | 4 ++-- Lib/test/test_socketserver.py | 2 +- Lib/test/test_wsgiref.py | 2 +- 12 files changed, 20 insertions(+), 20 deletions(-) (limited to 'Lib') diff --git a/Lib/base64.py b/Lib/base64.py index 67e54f021a..58f6ad6816 100755 --- a/Lib/base64.py +++ b/Lib/base64.py @@ -155,7 +155,7 @@ def b32encode(s): leftover = len(s) % 5 # Pad the last quantum with zero bits if necessary if leftover: - s = s + bytes(5 - leftover) # Don't use += ! + s = s + b'\0' * (5 - leftover) # Don't use += ! encoded = bytearray() from_bytes = int.from_bytes b32tab2 = _b32tab2 diff --git a/Lib/gzip.py b/Lib/gzip.py index da4479e9d0..ddf7668d90 100644 --- a/Lib/gzip.py +++ b/Lib/gzip.py @@ -357,10 +357,10 @@ class GzipFile(_compression.BaseStream): if offset < self.offset: raise OSError('Negative seek in write mode') count = offset - self.offset - chunk = bytes(1024) + chunk = b'\0' * 1024 for i in range(count // 1024): self.write(chunk) - self.write(bytes(count % 1024)) + self.write(b'\0' * (count % 1024)) elif self.mode == READ: self._check_not_closed() return self._buffer.seek(offset, whence) diff --git a/Lib/hmac.py b/Lib/hmac.py index 77785a2d9b..121029aa67 100644 --- a/Lib/hmac.py +++ b/Lib/hmac.py @@ -77,7 +77,7 @@ class HMAC: if len(key) > blocksize: key = self.digest_cons(key).digest() - key = key + bytes(blocksize - len(key)) + key = key.ljust(blocksize, b'\0') self.outer.update(key.translate(trans_5C)) self.inner.update(key.translate(trans_36)) if msg is not None: diff --git a/Lib/pickletools.py b/Lib/pickletools.py index 4eefc19fdb..5e129b5b56 100644 --- a/Lib/pickletools.py +++ b/Lib/pickletools.py @@ -707,7 +707,7 @@ def read_unicodestring8(f): >>> enc = s.encode('utf-8') >>> enc b'abcd\xea\xaf\x8d' - >>> n = bytes([len(enc)]) + bytes(7) # little-endian 8-byte length + >>> n = bytes([len(enc)]) + b'\0' * 7 # little-endian 8-byte length >>> t = read_unicodestring8(io.BytesIO(n + enc + b'junk')) >>> s == t True diff --git a/Lib/test/test_bufio.py b/Lib/test/test_bufio.py index 9931c84680..fea6da491e 100644 --- a/Lib/test/test_bufio.py +++ b/Lib/test/test_bufio.py @@ -59,7 +59,7 @@ class BufferSizeTest: self.drive_one(b"1234567890\00\01\02\03\04\05\06") def test_nullpat(self): - self.drive_one(bytes(1000)) + self.drive_one(b'\0' * 1000) class CBufferSizeTest(BufferSizeTest, unittest.TestCase): diff --git a/Lib/test/test_bz2.py b/Lib/test/test_bz2.py index a1e4b8d8e2..450ab2e23a 100644 --- a/Lib/test/test_bz2.py +++ b/Lib/test/test_bz2.py @@ -562,11 +562,11 @@ class BZ2FileTest(BaseTest): def testDecompressLimited(self): """Decompressed data buffering should be limited""" - bomb = bz2.compress(bytes(int(2e6)), compresslevel=9) + bomb = bz2.compress(b'\0' * int(2e6), compresslevel=9) self.assertLess(len(bomb), _compression.BUFFER_SIZE) decomp = BZ2File(BytesIO(bomb)) - self.assertEqual(bytes(1), decomp.read(1)) + self.assertEqual(decomp.read(1), b'\0') max_decomp = 1 + DEFAULT_BUFFER_SIZE self.assertLessEqual(decomp._buffer.raw.tell(), max_decomp, "Excessive amount of data was decompressed") diff --git a/Lib/test/test_gzip.py b/Lib/test/test_gzip.py index 3c51673a92..07a9f6ff44 100644 --- a/Lib/test/test_gzip.py +++ b/Lib/test/test_gzip.py @@ -434,12 +434,12 @@ class TestGzip(BaseTest): def test_decompress_limited(self): """Decompressed data buffering should be limited""" - bomb = gzip.compress(bytes(int(2e6)), compresslevel=9) + bomb = gzip.compress(b'\0' * int(2e6), compresslevel=9) self.assertLess(len(bomb), io.DEFAULT_BUFFER_SIZE) bomb = io.BytesIO(bomb) decomp = gzip.GzipFile(fileobj=bomb) - self.assertEqual(bytes(1), decomp.read(1)) + self.assertEqual(decomp.read(1), b'\0') max_decomp = 1 + io.DEFAULT_BUFFER_SIZE self.assertLessEqual(decomp._buffer.raw.tell(), max_decomp, "Excessive amount of data was decompressed") diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index c48ec3a239..8a2111cbd7 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -1812,7 +1812,7 @@ class BufferedRWPairTest(unittest.TestCase): with self.subTest(method): pair = self.tp(self.BytesIO(b"abcdef"), self.MockRawIO()) - data = byteslike(5) + data = byteslike(b'\0' * 5) self.assertEqual(getattr(pair, method)(data), 5) self.assertEqual(bytes(data), b"abcde") diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py index 5f08f0c295..0e39516dc0 100644 --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -119,7 +119,7 @@ class CommonTestMixin_v4(CommonTestMixin): def test_bad_packed_length(self): def assertBadLength(length): - addr = bytes(length) + addr = b'\0' * length msg = "%r (len %d != 4) is not permitted as an IPv4 address" with self.assertAddressError(re.escape(msg % (addr, length))): self.factory(addr) @@ -139,11 +139,11 @@ class CommonTestMixin_v6(CommonTestMixin): self.assertInstancesEqual(3232235521, "::c0a8:1") def test_packed(self): - addr = bytes(12) + bytes.fromhex("00000000") + addr = b'\0'*12 + bytes.fromhex("00000000") self.assertInstancesEqual(addr, "::") - addr = bytes(12) + bytes.fromhex("c0a80001") + addr = b'\0'*12 + bytes.fromhex("c0a80001") self.assertInstancesEqual(addr, "::c0a8:1") - addr = bytes.fromhex("c0a80001") + bytes(12) + addr = bytes.fromhex("c0a80001") + b'\0'*12 self.assertInstancesEqual(addr, "c0a8:1::") def test_negative_ints_rejected(self): @@ -158,7 +158,7 @@ class CommonTestMixin_v6(CommonTestMixin): def test_bad_packed_length(self): def assertBadLength(length): - addr = bytes(length) + addr = b'\0' * length msg = "%r (len %d != 16) is not permitted as an IPv6 address" with self.assertAddressError(re.escape(msg % (addr, length))): self.factory(addr) diff --git a/Lib/test/test_lzma.py b/Lib/test/test_lzma.py index 6c698e2f0e..d310bea2b1 100644 --- a/Lib/test/test_lzma.py +++ b/Lib/test/test_lzma.py @@ -928,11 +928,11 @@ class FileTestCase(unittest.TestCase): def test_decompress_limited(self): """Decompressed data buffering should be limited""" - bomb = lzma.compress(bytes(int(2e6)), preset=6) + bomb = lzma.compress(b'\0' * int(2e6), preset=6) self.assertLess(len(bomb), _compression.BUFFER_SIZE) decomp = LZMAFile(BytesIO(bomb)) - self.assertEqual(bytes(1), decomp.read(1)) + self.assertEqual(decomp.read(1), b'\0') max_decomp = 1 + DEFAULT_BUFFER_SIZE self.assertLessEqual(decomp._buffer.raw.tell(), max_decomp, "Excessive amount of data was decompressed") diff --git a/Lib/test/test_socketserver.py b/Lib/test/test_socketserver.py index 3f4dfa1aa7..140a6abf9e 100644 --- a/Lib/test/test_socketserver.py +++ b/Lib/test/test_socketserver.py @@ -406,7 +406,7 @@ class SocketWriterTest(unittest.TestCase): self.server.sent1 = self.wfile.write(b'write data\n') # Should be sent immediately, without requiring flush() self.server.received = self.rfile.readline() - big_chunk = bytes(test.support.SOCK_MAX_SIZE) + big_chunk = b'\0' * test.support.SOCK_MAX_SIZE self.server.sent2 = self.wfile.write(big_chunk) server = socketserver.TCPServer((HOST, 0), Handler) diff --git a/Lib/test/test_wsgiref.py b/Lib/test/test_wsgiref.py index 61a750c622..7708e20684 100644 --- a/Lib/test/test_wsgiref.py +++ b/Lib/test/test_wsgiref.py @@ -258,7 +258,7 @@ class IntegrationTests(TestCase): def app(environ, start_response): start_response("200 OK", []) - return [bytes(support.SOCK_MAX_SIZE)] + return [b'\0' * support.SOCK_MAX_SIZE] class WsgiHandler(NoLogRequestHandler, WSGIRequestHandler): pass -- cgit v1.2.1 From b0d248693f78c723b504d0feae0ebbfe402d3853 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Sun, 11 Sep 2016 14:53:16 +0300 Subject: Issue #25497: Rewrite test_robotparser to use a class based design --- Lib/test/test_robotparser.py | 359 +++++++++++++++++-------------------------- 1 file changed, 139 insertions(+), 220 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_robotparser.py b/Lib/test/test_robotparser.py index 76f4f7c614..f09622a982 100644 --- a/Lib/test/test_robotparser.py +++ b/Lib/test/test_robotparser.py @@ -10,84 +10,49 @@ except ImportError: threading = None -class RobotTestCase(unittest.TestCase): - def __init__(self, index=None, parser=None, url=None, good=None, - agent=None, request_rate=None, crawl_delay=None): - # workaround to make unittest discovery work (see #17066) - if not isinstance(index, int): - return - unittest.TestCase.__init__(self) - if good: - self.str = "RobotTest(%d, good, %s)" % (index, url) - else: - self.str = "RobotTest(%d, bad, %s)" % (index, url) - self.parser = parser - self.url = url - self.good = good - self.agent = agent - self.request_rate = request_rate - self.crawl_delay = crawl_delay - - def runTest(self): - if isinstance(self.url, tuple): - agent, url = self.url - else: - url = self.url - agent = self.agent - if self.good: - self.assertTrue(self.parser.can_fetch(agent, url)) - self.assertEqual(self.parser.crawl_delay(agent), self.crawl_delay) - # if we have actual values for request rate - if self.request_rate and self.parser.request_rate(agent): - self.assertEqual( - self.parser.request_rate(agent).requests, - self.request_rate.requests - ) - self.assertEqual( - self.parser.request_rate(agent).seconds, - self.request_rate.seconds - ) - self.assertEqual(self.parser.request_rate(agent), self.request_rate) - else: - self.assertFalse(self.parser.can_fetch(agent, url)) - - def __str__(self): - return self.str - -tests = unittest.TestSuite() - -def RobotTest(index, robots_txt, good_urls, bad_urls, - request_rate, crawl_delay, agent="test_robotparser"): - - lines = io.StringIO(robots_txt).readlines() - parser = urllib.robotparser.RobotFileParser() - parser.parse(lines) - for url in good_urls: - tests.addTest(RobotTestCase(index, parser, url, 1, agent, - request_rate, crawl_delay)) - for url in bad_urls: - tests.addTest(RobotTestCase(index, parser, url, 0, agent, - request_rate, crawl_delay)) - -# Examples from http://www.robotstxt.org/wc/norobots.html (fetched 2002) - -# 1. -doc = """ +class BaseRobotTest: + robots_txt = '' + agent = 'test_robotparser' + good = [] + bad = [] + + def setUp(self): + lines = io.StringIO(self.robots_txt).readlines() + self.parser = urllib.robotparser.RobotFileParser() + self.parser.parse(lines) + + def get_agent_and_url(self, url): + if isinstance(url, tuple): + agent, url = url + return agent, url + return self.agent, url + + def test_good_urls(self): + for url in self.good: + agent, url = self.get_agent_and_url(url) + with self.subTest(url=url, agent=agent): + self.assertTrue(self.parser.can_fetch(agent, url)) + + def test_bad_urls(self): + for url in self.bad: + agent, url = self.get_agent_and_url(url) + with self.subTest(url=url, agent=agent): + self.assertFalse(self.parser.can_fetch(agent, url)) + + +class UserAgentWildcardTest(BaseRobotTest, unittest.TestCase): + robots_txt = """\ User-agent: * Disallow: /cyberworld/map/ # This is an infinite virtual URL space Disallow: /tmp/ # these will soon disappear Disallow: /foo.html -""" - -good = ['/','/test.html'] -bad = ['/cyberworld/map/index.html','/tmp/xxx','/foo.html'] -request_rate = None -crawl_delay = None + """ + good = ['/', '/test.html'] + bad = ['/cyberworld/map/index.html', '/tmp/xxx', '/foo.html'] -RobotTest(1, doc, good, bad, request_rate, crawl_delay) -# 2. -doc = """ +class CrawlDelayAndCustomAgentTest(BaseRobotTest, unittest.TestCase): + robots_txt = """\ # robots.txt for http://www.example.com/ User-agent: * @@ -98,34 +63,23 @@ Disallow: /cyberworld/map/ # This is an infinite virtual URL space # Cybermapper knows where to go. User-agent: cybermapper Disallow: + """ + good = ['/', '/test.html', ('cybermapper', '/cyberworld/map/index.html')] + bad = ['/cyberworld/map/index.html'] -""" - -good = ['/','/test.html',('cybermapper','/cyberworld/map/index.html')] -bad = ['/cyberworld/map/index.html'] -request_rate = None # The parameters should be equal to None since they -crawl_delay = None # don't apply to the cybermapper user agent - -RobotTest(2, doc, good, bad, request_rate, crawl_delay) -# 3. -doc = """ +class RejectAllRobotsTest(BaseRobotTest, unittest.TestCase): + robots_txt = """\ # go away User-agent: * Disallow: / -""" + """ + good = [] + bad = ['/cyberworld/map/index.html', '/', '/tmp/'] -good = [] -bad = ['/cyberworld/map/index.html','/','/tmp/'] -request_rate = None -crawl_delay = None -RobotTest(3, doc, good, bad, request_rate, crawl_delay) - -# Examples from http://www.robotstxt.org/wc/norobots-rfc.html (fetched 2002) - -# 4. -doc = """ +class CrawlDelayAndRequestRateTest(BaseRobotTest, unittest.TestCase): + robots_txt = """\ User-agent: figtree Crawl-delay: 3 Request-rate: 9/30 @@ -133,28 +87,43 @@ Disallow: /tmp Disallow: /a%3cd.html Disallow: /a%2fb.html Disallow: /%7ejoe/index.html -""" - -good = [] # XFAIL '/a/b.html' -bad = ['/tmp','/tmp.html','/tmp/a.html', - '/a%3cd.html','/a%3Cd.html','/a%2fb.html', - '/~joe/index.html' - ] - -request_rate = namedtuple('req_rate', 'requests seconds') -request_rate.requests = 9 -request_rate.seconds = 30 -crawl_delay = 3 -request_rate_bad = None # not actually tested, but we still need to parse it -crawl_delay_bad = None # in order to accommodate the input parameters - - -RobotTest(4, doc, good, bad, request_rate, crawl_delay, 'figtree' ) -RobotTest(5, doc, good, bad, request_rate_bad, crawl_delay_bad, - 'FigTree Robot libwww-perl/5.04') - -# 6. -doc = """ + """ + agent = 'figtree' + request_rate = namedtuple('req_rate', 'requests seconds')(9, 30) + crawl_delay = 3 + good = [('figtree', '/foo.html')] + bad = ['/tmp', '/tmp.html', '/tmp/a.html', '/a%3cd.html', '/a%3Cd.html', + '/a%2fb.html', '/~joe/index.html'] + + def test_request_rate(self): + for url in self.good: + agent, url = self.get_agent_and_url(url) + with self.subTest(url=url, agent=agent): + if self.crawl_delay: + self.assertEqual( + self.parser.crawl_delay(agent), self.crawl_delay + ) + if self.request_rate and self.parser.request_rate(agent): + self.assertEqual( + self.parser.request_rate(agent).requests, + self.request_rate.requests + ) + self.assertEqual( + self.parser.request_rate(agent).seconds, + self.request_rate.seconds + ) + + +class DifferentAgentTest(CrawlDelayAndRequestRateTest): + agent = 'FigTree Robot libwww-perl/5.04' + # these are not actually tested, but we still need to parse it + # in order to accommodate the input parameters + request_rate = None + crawl_delay = None + + +class InvalidRequestRateTest(BaseRobotTest, unittest.TestCase): + robots_txt = """\ User-agent: * Disallow: /tmp/ Disallow: /a%3Cd.html @@ -162,141 +131,102 @@ Disallow: /a/b.html Disallow: /%7ejoe/index.html Crawl-delay: 3 Request-rate: 9/banana -""" - -good = ['/tmp',] # XFAIL: '/a%2fb.html' -bad = ['/tmp/','/tmp/a.html', - '/a%3cd.html','/a%3Cd.html',"/a/b.html", - '/%7Ejoe/index.html'] -crawl_delay = 3 -request_rate = None # since request rate has invalid syntax, return None + """ + good = ['/tmp'] + bad = ['/tmp/', '/tmp/a.html', '/a%3cd.html', '/a%3Cd.html', '/a/b.html', + '/%7Ejoe/index.html'] + crawl_delay = 3 -RobotTest(6, doc, good, bad, None, None) -# From bug report #523041 - -# 7. -doc = """ +class InvalidCrawlDelayTest(BaseRobotTest, unittest.TestCase): + # From bug report #523041 + robots_txt = """\ User-Agent: * Disallow: /. Crawl-delay: pears -""" - -good = ['/foo.html'] -bad = [] # bug report says "/" should be denied, but that is not in the RFC + """ + good = ['/foo.html'] + # bug report says "/" should be denied, but that is not in the RFC + bad = [] -crawl_delay = None # since crawl delay has invalid syntax, return None -request_rate = None -RobotTest(7, doc, good, bad, crawl_delay, request_rate) - -# From Google: http://www.google.com/support/webmasters/bin/answer.py?hl=en&answer=40364 - -# 8. -doc = """ +class AnotherInvalidRequestRateTest(BaseRobotTest, unittest.TestCase): + # also test that Allow and Diasallow works well with each other + robots_txt = """\ User-agent: Googlebot Allow: /folder1/myfile.html Disallow: /folder1/ Request-rate: whale/banana -""" + """ + agent = 'Googlebot' + good = ['/folder1/myfile.html'] + bad = ['/folder1/anotherfile.html'] -good = ['/folder1/myfile.html'] -bad = ['/folder1/anotherfile.html'] -crawl_delay = None -request_rate = None # invalid syntax, return none -RobotTest(8, doc, good, bad, crawl_delay, request_rate, agent="Googlebot") - -# 9. This file is incorrect because "Googlebot" is a substring of -# "Googlebot-Mobile", so test 10 works just like test 9. -doc = """ +class UserAgentOrderingTest(BaseRobotTest, unittest.TestCase): + # the order of User-agent should be correct. note + # that this file is incorrect because "Googlebot" is a + # substring of "Googlebot-Mobile" + robots_txt = """\ User-agent: Googlebot Disallow: / User-agent: Googlebot-Mobile Allow: / -""" - -good = [] -bad = ['/something.jpg'] - -RobotTest(9, doc, good, bad, None, None, agent="Googlebot") - -good = [] -bad = ['/something.jpg'] - -RobotTest(10, doc, good, bad, None, None, agent="Googlebot-Mobile") - -# 11. Get the order correct. -doc = """ -User-agent: Googlebot-Mobile -Allow: / - -User-agent: Googlebot -Disallow: / -""" - -good = [] -bad = ['/something.jpg'] + """ + agent = 'Googlebot' + bad = ['/something.jpg'] -RobotTest(11, doc, good, bad, None, None, agent="Googlebot") -good = ['/something.jpg'] -bad = [] +class UserAgentGoogleMobileTest(UserAgentOrderingTest): + agent = 'Googlebot-Mobile' -RobotTest(12, doc, good, bad, None, None, agent="Googlebot-Mobile") - -# 13. Google also got the order wrong in #8. You need to specify the -# URLs from more specific to more general. -doc = """ +class GoogleURLOrderingTest(BaseRobotTest, unittest.TestCase): + # Google also got the order wrong. You need + # to specify the URLs from more specific to more general + robots_txt = """\ User-agent: Googlebot Allow: /folder1/myfile.html Disallow: /folder1/ -""" - -good = ['/folder1/myfile.html'] -bad = ['/folder1/anotherfile.html'] - -RobotTest(13, doc, good, bad, None, None, agent="googlebot") + """ + agent = 'googlebot' + good = ['/folder1/myfile.html'] + bad = ['/folder1/anotherfile.html'] -# 14. For issue #6325 (query string support) -doc = """ +class DisallowQueryStringTest(BaseRobotTest, unittest.TestCase): + # see issue #6325 for details + robots_txt = """\ User-agent: * Disallow: /some/path?name=value -""" + """ + good = ['/some/path'] + bad = ['/some/path?name=value'] -good = ['/some/path'] -bad = ['/some/path?name=value'] -RobotTest(14, doc, good, bad, None, None) - -# 15. For issue #4108 (obey first * entry) -doc = """ +class UseFirstUserAgentWildcardTest(BaseRobotTest, unittest.TestCase): + # obey first * entry (#4108) + robots_txt = """\ User-agent: * Disallow: /some/path User-agent: * Disallow: /another/path -""" - -good = ['/another/path'] -bad = ['/some/path'] + """ + good = ['/another/path'] + bad = ['/some/path'] -RobotTest(15, doc, good, bad, None, None) -# 16. Empty query (issue #17403). Normalizing the url first. -doc = """ +class EmptyQueryStringTest(BaseRobotTest, unittest.TestCase): + # normalize the URL first (#17403) + robots_txt = """\ User-agent: * Allow: /some/path? Disallow: /another/path? -""" - -good = ['/some/path?'] -bad = ['/another/path?'] - -RobotTest(16, doc, good, bad, None, None) + """ + good = ['/some/path?'] + bad = ['/another/path?'] class RobotHandler(BaseHTTPRequestHandler): @@ -329,9 +259,6 @@ class PasswordProtectedSiteTestCase(unittest.TestCase): self.t.join() self.server.server_close() - def runTest(self): - self.testPasswordProtectedSite() - def testPasswordProtectedSite(self): addr = self.server.server_address url = 'http://' + support.HOST + ':' + str(addr[1]) @@ -341,8 +268,6 @@ class PasswordProtectedSiteTestCase(unittest.TestCase): parser.read() self.assertFalse(parser.can_fetch("*", robots_url)) - def __str__(self): - return '%s' % self.__class__.__name__ class NetworkTestCase(unittest.TestCase): @@ -356,11 +281,5 @@ class NetworkTestCase(unittest.TestCase): self.assertTrue( parser.can_fetch("*", "http://www.python.org/robots.txt")) -def load_tests(loader, suite, pattern): - suite = unittest.makeSuite(NetworkTestCase) - suite.addTest(tests) - suite.addTest(PasswordProtectedSiteTestCase()) - return suite - if __name__=='__main__': unittest.main() -- cgit v1.2.1 From 962b8ef41e896ce76234318e25502deecfe19811 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Sun, 11 Sep 2016 15:17:53 +0300 Subject: Wrap testPasswordProtectedSite with @reap_threads --- Lib/test/test_robotparser.py | 1 + 1 file changed, 1 insertion(+) (limited to 'Lib') diff --git a/Lib/test/test_robotparser.py b/Lib/test/test_robotparser.py index f09622a982..4082199792 100644 --- a/Lib/test/test_robotparser.py +++ b/Lib/test/test_robotparser.py @@ -259,6 +259,7 @@ class PasswordProtectedSiteTestCase(unittest.TestCase): self.t.join() self.server.server_close() + @support.reap_threads def testPasswordProtectedSite(self): addr = self.server.server_address url = 'http://' + support.HOST + ':' + str(addr[1]) -- cgit v1.2.1 From add9b1714b0be00de2a1095b5286e9eb1884cb44 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Sun, 11 Sep 2016 15:27:07 +0300 Subject: Unskip testPythonOrg in test_robotparser We should probably use pythontest.net for this. --- Lib/test/test_robotparser.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_robotparser.py b/Lib/test/test_robotparser.py index 4082199792..27201a0670 100644 --- a/Lib/test/test_robotparser.py +++ b/Lib/test/test_robotparser.py @@ -272,12 +272,11 @@ class PasswordProtectedSiteTestCase(unittest.TestCase): class NetworkTestCase(unittest.TestCase): - @unittest.skip('does not handle the gzip encoding delivered by pydotorg') def testPythonOrg(self): support.requires('network') with support.transient_internet('www.python.org'): parser = urllib.robotparser.RobotFileParser( - "http://www.python.org/robots.txt") + "https://www.python.org/robots.txt") parser.read() self.assertTrue( parser.can_fetch("*", "http://www.python.org/robots.txt")) -- cgit v1.2.1 From ae4db5f50a8cab513f7f2a15b02fb84d33b20e91 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Sun, 11 Sep 2016 15:46:47 +0300 Subject: Use HTTP in testPythonOrg --- Lib/test/test_robotparser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_robotparser.py b/Lib/test/test_robotparser.py index 27201a0670..d4bf45376a 100644 --- a/Lib/test/test_robotparser.py +++ b/Lib/test/test_robotparser.py @@ -276,7 +276,7 @@ class NetworkTestCase(unittest.TestCase): support.requires('network') with support.transient_internet('www.python.org'): parser = urllib.robotparser.RobotFileParser( - "https://www.python.org/robots.txt") + "http://www.python.org/robots.txt") parser.read() self.assertTrue( parser.can_fetch("*", "http://www.python.org/robots.txt")) -- cgit v1.2.1 From e45f1d22ce9cb300fa52ba1037a83efa5dc90b3d Mon Sep 17 00:00:00 2001 From: "Eric V. Smith" Date: Sun, 11 Sep 2016 08:55:43 -0400 Subject: Issue 24454: Improve the usability of the re match object named group API --- Lib/test/test_re.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index 79a7a057a0..eb1aba39c3 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -441,6 +441,50 @@ class ReTests(unittest.TestCase): self.assertEqual(m.group(2, 1), ('b', 'a')) self.assertEqual(m.group(Index(2), Index(1)), ('b', 'a')) + def test_match_getitem(self): + pat = re.compile('(?:(?Pa)|(?Pb))(?Pc)?') + + m = pat.match('a') + self.assertEqual(m['a1'], 'a') + self.assertEqual(m['b2'], None) + self.assertEqual(m['c3'], None) + self.assertEqual('a1={a1} b2={b2} c3={c3}'.format_map(m), 'a1=a b2=None c3=None') + self.assertEqual(m[0], 'a') + self.assertEqual(m[1], 'a') + self.assertEqual(m[2], None) + self.assertEqual(m[3], None) + with self.assertRaisesRegex(IndexError, 'no such group'): + m['X'] + with self.assertRaisesRegex(IndexError, 'no such group'): + m[-1] + with self.assertRaisesRegex(IndexError, 'no such group'): + m[4] + with self.assertRaisesRegex(IndexError, 'no such group'): + m[0, 1] + with self.assertRaisesRegex(IndexError, 'no such group'): + m[(0,)] + with self.assertRaisesRegex(IndexError, 'no such group'): + m[(0, 1)] + with self.assertRaisesRegex(KeyError, 'a2'): + 'a1={a2}'.format_map(m) + + m = pat.match('ac') + self.assertEqual(m['a1'], 'a') + self.assertEqual(m['b2'], None) + self.assertEqual(m['c3'], 'c') + self.assertEqual('a1={a1} b2={b2} c3={c3}'.format_map(m), 'a1=a b2=None c3=c') + self.assertEqual(m[0], 'ac') + self.assertEqual(m[1], 'a') + self.assertEqual(m[2], None) + self.assertEqual(m[3], 'c') + + # Cannot assign. + with self.assertRaises(TypeError): + m[0] = 1 + + # No len(). + self.assertRaises(TypeError, len, m) + def test_re_fullmatch(self): # Issue 16203: Proposal: add re.fullmatch() method. self.assertEqual(re.fullmatch(r"a", "a").span(), (0, 1)) -- cgit v1.2.1 From dbb07196c8bebd4c6563e3416eac0e3c866303c4 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Sun, 11 Sep 2016 09:45:24 -0700 Subject: Issue #28076: Variable annotations should be mangled for private names. By Ivan Levkivskyi. --- Lib/test/test_grammar.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py index 914aa67944..67a61d4ab5 100644 --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -328,12 +328,12 @@ class GrammarTests(unittest.TestCase): # class semantics class C: - x: int + __foo: int s: str = "attr" z = 2 def __init__(self, x): self.x: int = x - self.assertEqual(C.__annotations__, {'x': int, 's': str}) + self.assertEqual(C.__annotations__, {'_C__foo': int, 's': str}) with self.assertRaises(NameError): class CBad: no_such_name_defined.attr: int = 0 -- cgit v1.2.1 From a3347efeac41b4c7f6d8417015e57f15232691b9 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sun, 11 Sep 2016 19:49:56 +0200 Subject: Issue #28078: Silence resource warnings in test_socket. Initial patch by Xiang Zhang, thanks --- Lib/test/test_socket.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index b3632e9af9..8fc7290c91 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -5347,8 +5347,10 @@ class LinuxKernelCryptoAPI(unittest.TestCase): sock.bind((typ, name)) except FileNotFoundError as e: # type / algorithm is not available + sock.close() raise unittest.SkipTest(str(e), typ, name) - return sock + else + return sock def test_sha256(self): expected = bytes.fromhex("ba7816bf8f01cfea414140de5dae2223b00361a396" @@ -5494,20 +5496,22 @@ class LinuxKernelCryptoAPI(unittest.TestCase): def test_sendmsg_afalg_args(self): sock = socket.socket(socket.AF_ALG, socket.SOCK_SEQPACKET, 0) - with self.assertRaises(TypeError): - sock.sendmsg_afalg() + with sock: + with self.assertRaises(TypeError): + sock.sendmsg_afalg() + + with self.assertRaises(TypeError): + sock.sendmsg_afalg(op=None) - with self.assertRaises(TypeError): - sock.sendmsg_afalg(op=None) + with self.assertRaises(TypeError): + sock.sendmsg_afalg(1) - with self.assertRaises(TypeError): - sock.sendmsg_afalg(1) + with self.assertRaises(TypeError): + sock.sendmsg_afalg(op=socket.ALG_OP_ENCRYPT, assoclen=None) - with self.assertRaises(TypeError): - sock.sendmsg_afalg(op=socket.ALG_OP_ENCRYPT, assoclen=None) + with self.assertRaises(TypeError): + sock.sendmsg_afalg(op=socket.ALG_OP_ENCRYPT, assoclen=-1) - with self.assertRaises(TypeError): - sock.sendmsg_afalg(op=socket.ALG_OP_ENCRYPT, assoclen=-1) def test_main(): tests = [GeneralModuleTests, BasicTCPTest, TCPCloserTest, TCPTimeoutTest, -- cgit v1.2.1 From 89dc43b742b0c15b59b543f057253e91d52b6a1b Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sun, 11 Sep 2016 19:54:43 +0200 Subject: Issue 28022: Catch deprecation warning in test_httplib, reported by Martin Panter --- Lib/test/test_httplib.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index 3fc02ea01a..c613c57723 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -1642,14 +1642,16 @@ class HTTPSTest(TestCase): with self.assertRaises(ssl.CertificateError): h.request('GET', '/') # Same with explicit check_hostname=True - h = client.HTTPSConnection('localhost', server.port, context=context, - check_hostname=True) + with support.check_warnings(('', DeprecationWarning)): + h = client.HTTPSConnection('localhost', server.port, + context=context, check_hostname=True) with self.assertRaises(ssl.CertificateError): h.request('GET', '/') # With check_hostname=False, the mismatching is ignored context.check_hostname = False - h = client.HTTPSConnection('localhost', server.port, context=context, - check_hostname=False) + with support.check_warnings(('', DeprecationWarning)): + h = client.HTTPSConnection('localhost', server.port, + context=context, check_hostname=False) h.request('GET', '/nonexistent') resp = h.getresponse() resp.close() @@ -1666,8 +1668,9 @@ class HTTPSTest(TestCase): h.close() # Passing check_hostname to HTTPSConnection should override the # context's setting. - h = client.HTTPSConnection('localhost', server.port, context=context, - check_hostname=True) + with support.check_warnings(('', DeprecationWarning)): + h = client.HTTPSConnection('localhost', server.port, + context=context, check_hostname=True) with self.assertRaises(ssl.CertificateError): h.request('GET', '/') -- cgit v1.2.1 From b702a9f11b1d87572100d9f2c62f76bcb7c1d78e Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sun, 11 Sep 2016 20:03:46 +0200 Subject: Issue #28078: Add missing colon --- Lib/test/test_socket.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 8fc7290c91..c5d5d258b9 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -5349,7 +5349,7 @@ class LinuxKernelCryptoAPI(unittest.TestCase): # type / algorithm is not available sock.close() raise unittest.SkipTest(str(e), typ, name) - else + else: return sock def test_sha256(self): -- cgit v1.2.1 From c60948862aec72385fb45bc95635a3f730122faf Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sun, 11 Sep 2016 20:11:30 +0200 Subject: Issue 27744: test_aes_cbc is blocking x86-64 Ubuntu 15.10 Skylake CPU 3.x for a while. Require Kernel 4.3+ for now --- Lib/test/test_socket.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index c5d5d258b9..441926f9ec 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -5378,7 +5378,9 @@ class LinuxKernelCryptoAPI(unittest.TestCase): op.sendall(b"what do ya want for nothing?") self.assertEqual(op.recv(512), expected) - @support.requires_linux_version(3, 19) + # Although it should work with 3.19 and newer the test blocks on + # Ubuntu 15.10 with Kernel 4.2.0-19. + @support.requires_linux_version(4, 3) def test_aes_cbc(self): key = bytes.fromhex('06a9214036b8a15b512e03d534120006') iv = bytes.fromhex('3dafba429d9eb430b422da802c9fac41') @@ -5419,7 +5421,7 @@ class LinuxKernelCryptoAPI(unittest.TestCase): self.assertEqual(len(dec), msglen * multiplier) self.assertEqual(dec, msg * multiplier) - @support.requires_linux_version(3, 19) + @support.requires_linux_version(4, 3) # see test_aes_cbc def test_aead_aes_gcm(self): key = bytes.fromhex('c939cc13397c1d37de6ae0e1cb7c423c') iv = bytes.fromhex('b3d8cc017cbb89b39e0f67e2') @@ -5483,7 +5485,7 @@ class LinuxKernelCryptoAPI(unittest.TestCase): res = op.recv(len(msg)) self.assertEqual(plain, res[assoclen:-taglen]) - @support.requires_linux_version(3, 19) + @support.requires_linux_version(4, 3) # see test_aes_cbc def test_drbg_pr_sha256(self): # deterministic random bit generator, prediction resistance, sha256 with self.create_alg('rng', 'drbg_pr_sha256') as algo: -- cgit v1.2.1 From 3b23dcaec8960ca1ce4cf2ee783c40c3a56d06ad Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Sun, 11 Sep 2016 21:39:17 +0200 Subject: Issue #27917: Fix test_triplet_in_ext_suffix for the 'x86' Android platform. --- Lib/test/test_sysconfig.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py index a23bf06af8..091e90522d 100644 --- a/Lib/test/test_sysconfig.py +++ b/Lib/test/test_sysconfig.py @@ -394,8 +394,9 @@ class TestSysConfig(unittest.TestCase): self.assertTrue('linux' in suffix, suffix) if re.match('(i[3-6]86|x86_64)$', machine): if ctypes.sizeof(ctypes.c_char_p()) == 4: - self.assertTrue(suffix.endswith('i386-linux-gnu.so') \ - or suffix.endswith('x86_64-linux-gnux32.so'), + self.assertTrue(suffix.endswith('i386-linux-gnu.so') or + suffix.endswith('i686-linux-android.so') or + suffix.endswith('x86_64-linux-gnux32.so'), suffix) else: # 8 byte pointer size self.assertTrue(suffix.endswith('x86_64-linux-gnu.so'), suffix) -- cgit v1.2.1 From ebadb5ed14af411ee0199a9c3f5d69da0bb3f190 Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Sun, 11 Sep 2016 22:22:24 +0200 Subject: Issue #28046: get_sysconfigdata_name() uses the _PYTHON_SYSCONFIGDATA_NAME environment variable that is defined when cross-compiling. --- Lib/distutils/sysconfig.py | 5 +++-- Lib/sysconfig.py | 22 ++++++++-------------- 2 files changed, 11 insertions(+), 16 deletions(-) (limited to 'Lib') diff --git a/Lib/distutils/sysconfig.py b/Lib/distutils/sysconfig.py index 229626e1b4..8bf1a7016b 100644 --- a/Lib/distutils/sysconfig.py +++ b/Lib/distutils/sysconfig.py @@ -418,11 +418,12 @@ _config_vars = None def _init_posix(): """Initialize the module as appropriate for POSIX systems.""" # _sysconfigdata is generated at build time, see the sysconfig module - name = '_sysconfigdata_{abi}_{platform}_{multiarch}'.format( + name = os.environ.get('_PYTHON_SYSCONFIGDATA_NAME', + '_sysconfigdata_{abi}_{platform}_{multiarch}'.format( abi=sys.abiflags, platform=sys.platform, multiarch=getattr(sys.implementation, '_multiarch', ''), - ) + )) _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0) build_time_vars = _temp.build_time_vars global _config_vars diff --git a/Lib/sysconfig.py b/Lib/sysconfig.py index 13275dea34..9314e71a2f 100644 --- a/Lib/sysconfig.py +++ b/Lib/sysconfig.py @@ -342,19 +342,13 @@ def get_makefile_filename(): return os.path.join(get_path('stdlib'), config_dir_name, 'Makefile') -def _get_sysconfigdata_name(vars=None): - if vars is None: - return '_sysconfigdata_{abi}_{platform}_{multiarch}'.format( - abi=sys.abiflags, - platform=sys.platform, - multiarch=getattr(sys.implementation, '_multiarch', ''), - ) - else: - return '_sysconfigdata_{abi}_{platform}_{multiarch}'.format( - abi=vars['ABIFLAGS'], - platform=vars['MACHDEP'], - multiarch=vars.get('MULTIARCH', ''), - ) +def _get_sysconfigdata_name(): + return os.environ.get('_PYTHON_SYSCONFIGDATA_NAME', + '_sysconfigdata_{abi}_{platform}_{multiarch}'.format( + abi=sys.abiflags, + platform=sys.platform, + multiarch=getattr(sys.implementation, '_multiarch', ''), + )) def _generate_posix_vars(): @@ -397,7 +391,7 @@ def _generate_posix_vars(): # _sysconfigdata module manually and populate it with the build vars. # This is more than sufficient for ensuring the subsequent call to # get_platform() succeeds. - name = _get_sysconfigdata_name(vars) + name = _get_sysconfigdata_name() if 'darwin' in sys.platform: import types module = types.ModuleType(name) -- cgit v1.2.1 From 19b6b87388235a5b578cdfcfff8109d002fc58d6 Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Sun, 11 Sep 2016 13:25:26 -0700 Subject: Enum._convert: sort by value, then by name --- Lib/enum.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'Lib') diff --git a/Lib/enum.py b/Lib/enum.py index 1f8766479e..d8303204ee 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -616,9 +616,16 @@ class Enum(metaclass=EnumMeta): # for a consistent reverse mapping of number to name when there # are multiple names for the same number rather than varying # between runs due to hash randomization of the module dictionary. - members = OrderedDict((name, source[name]) - for name in sorted(source.keys()) - if filter(name)) + members = [ + (name, source[name]) + for name in source.keys() + if filter(name)] + try: + # sort by value + members.sort(key=lambda t: (t[1], t[0])) + except TypeError: + # unless some values aren't comparable, in which case sort by name + members.sort(key=lambda t: t[0]) cls = cls(name, members, module=module) cls.__reduce_ex__ = _reduce_ex_by_name module_globals.update(cls.__members__) -- cgit v1.2.1 From e3b139facce6fa6fd82c25e28d583885a8051b9e Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Sun, 11 Sep 2016 13:30:08 -0700 Subject: issue28082: use IntFlag for re constants --- Lib/re.py | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) (limited to 'Lib') diff --git a/Lib/re.py b/Lib/re.py index b78da8939f..ad59640633 100644 --- a/Lib/re.py +++ b/Lib/re.py @@ -119,6 +119,7 @@ This module also defines an exception 'error'. """ +import enum import sre_compile import sre_parse try: @@ -137,18 +138,26 @@ __all__ = [ __version__ = "2.2.1" -# flags -A = ASCII = sre_compile.SRE_FLAG_ASCII # assume ascii "locale" -I = IGNORECASE = sre_compile.SRE_FLAG_IGNORECASE # ignore case -L = LOCALE = sre_compile.SRE_FLAG_LOCALE # assume current 8-bit locale -U = UNICODE = sre_compile.SRE_FLAG_UNICODE # assume unicode "locale" -M = MULTILINE = sre_compile.SRE_FLAG_MULTILINE # make anchors look for newline -S = DOTALL = sre_compile.SRE_FLAG_DOTALL # make dot match newline -X = VERBOSE = sre_compile.SRE_FLAG_VERBOSE # ignore whitespace and comments - -# sre extensions (experimental, don't rely on these) -T = TEMPLATE = sre_compile.SRE_FLAG_TEMPLATE # disable backtracking -DEBUG = sre_compile.SRE_FLAG_DEBUG # dump pattern after compilation +class Flag(enum.IntFlag): + ASCII = sre_compile.SRE_FLAG_ASCII # assume ascii "locale" + IGNORECASE = sre_compile.SRE_FLAG_IGNORECASE # ignore case + LOCALE = sre_compile.SRE_FLAG_LOCALE # assume current 8-bit locale + UNICODE = sre_compile.SRE_FLAG_UNICODE # assume unicode "locale" + MULTILINE = sre_compile.SRE_FLAG_MULTILINE # make anchors look for newline + DOTALL = sre_compile.SRE_FLAG_DOTALL # make dot match newline + VERBOSE = sre_compile.SRE_FLAG_VERBOSE # ignore whitespace and comments + A = ASCII + I = IGNORECASE + L = LOCALE + U = UNICODE + M = MULTILINE + S = DOTALL + X = VERBOSE + # sre extensions (experimental, don't rely on these) + TEMPLATE = sre_compile.SRE_FLAG_TEMPLATE # disable backtracking + T = TEMPLATE + DEBUG = sre_compile.SRE_FLAG_DEBUG # dump pattern after compilation +globals().update(Flag.__members__) # sre exception error = sre_compile.error -- cgit v1.2.1 From 405a8d24afe8a924ccffaebb610ff8625c52222c Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Sun, 11 Sep 2016 13:34:42 -0700 Subject: issue28083: add IntFlag constants --- Lib/socket.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/socket.py b/Lib/socket.py index 6dddfe1d34..bc8f4671c9 100644 --- a/Lib/socket.py +++ b/Lib/socket.py @@ -50,7 +50,7 @@ import _socket from _socket import * import os, sys, io, selectors -from enum import IntEnum +from enum import IntEnum, IntFlag try: import errno @@ -80,6 +80,16 @@ IntEnum._convert( __name__, lambda C: C.isupper() and C.startswith('SOCK_')) +IntFlag._convert( + 'MsgFlag', + __name__, + lambda C: C.isupper() and C.startswith('MSG_')) + +IntFlag._convert( + 'AddressInfo', + __name__, + lambda C: C.isupper() and C.startswith('AI_')) + _LOCALHOST = '127.0.0.1' _LOCALHOST_V6 = '::1' -- cgit v1.2.1 From e4dfccfe3f0ad94e1090b0a6cd1d4b0b2289ab98 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sun, 11 Sep 2016 22:47:02 +0200 Subject: Issue #28022: Catch another deprecation warning in imaplib --- Lib/test/test_imaplib.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_imaplib.py b/Lib/test/test_imaplib.py index f95ebf4757..63ac810cdd 100644 --- a/Lib/test/test_imaplib.py +++ b/Lib/test/test_imaplib.py @@ -638,8 +638,10 @@ class RemoteIMAP_SSLTest(RemoteIMAPTest): def test_logincapa_with_client_certfile(self): with transient_internet(self.host): - _server = self.imap_class(self.host, self.port, certfile=CERTFILE) - self.check_logincapa(_server) + with support.check_warnings(('', DeprecationWarning)): + _server = self.imap_class(self.host, self.port, + certfile=CERTFILE) + self.check_logincapa(_server) def test_logincapa_with_client_ssl_context(self): with transient_internet(self.host): -- cgit v1.2.1 From d93b5cefee3854917dca813a1392590734fd9ba8 Mon Sep 17 00:00:00 2001 From: R David Murray Date: Sun, 11 Sep 2016 17:23:33 -0400 Subject: Merge: #19003: Only replace \r and/or \n line endings in email.generator. --- Lib/email/generator.py | 16 ++++++++++------ Lib/test/test_email/test_email.py | 12 ++++++++++++ 2 files changed, 22 insertions(+), 6 deletions(-) (limited to 'Lib') diff --git a/Lib/email/generator.py b/Lib/email/generator.py index 51109f9131..ae670c2353 100644 --- a/Lib/email/generator.py +++ b/Lib/email/generator.py @@ -18,6 +18,7 @@ from email.utils import _has_surrogates UNDERSCORE = '_' NL = '\n' # XXX: no longer used by the code below. +NLCRE = re.compile(r'\r\n|\r|\n') fcre = re.compile(r'^From ', re.MULTILINE) @@ -149,14 +150,17 @@ class Generator: # We have to transform the line endings. if not lines: return - lines = lines.splitlines(True) + lines = NLCRE.split(lines) for line in lines[:-1]: - self.write(line.rstrip('\r\n')) - self.write(self._NL) - laststripped = lines[-1].rstrip('\r\n') - self.write(laststripped) - if len(lines[-1]) != len(laststripped): + self.write(line) self.write(self._NL) + if lines[-1]: + self.write(lines[-1]) + # XXX logic tells me this else should be needed, but the tests fail + # with it and pass without it. (NLCRE.split ends with a blank element + # if and only if there was a trailing newline.) + #else: + # self.write(self._NL) def _write(self, msg): # We can't write the headers yet because of the following scenario: diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py index e95f08df7d..daa12858b6 100644 --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -1599,6 +1599,18 @@ class TestMIMEApplication(unittest.TestCase): self.assertEqual(msg.get_payload(), '\uFFFD' * len(bytesdata)) self.assertEqual(msg2.get_payload(decode=True), bytesdata) + def test_binary_body_with_unicode_linend_encode_noop(self): + # Issue 19003: This is a variation on #16564. + bytesdata = b'\x0b\xfa\xfb\xfc\xfd\xfe\xff' + msg = MIMEApplication(bytesdata, _encoder=encoders.encode_noop) + self.assertEqual(msg.get_payload(decode=True), bytesdata) + s = BytesIO() + g = BytesGenerator(s) + g.flatten(msg) + wireform = s.getvalue() + msg2 = email.message_from_bytes(wireform) + self.assertEqual(msg2.get_payload(decode=True), bytesdata) + def test_binary_body_with_encode_quopri(self): # Issue 14360. bytesdata = b'\xfa\xfb\xfc\xfd\xfe\xff ' -- cgit v1.2.1 From e74d571e49d3fdb4389b79fa1392dd75e2acb916 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 12 Sep 2016 00:52:40 +0300 Subject: Issue #27213: Fixed different issues with reworked CALL_FUNCTION* opcodes. * BUILD_TUPLE_UNPACK and BUILD_MAP_UNPACK_WITH_CALL no longer generated with single tuple or dict. * Restored more informative error messages for incorrect var-positional and var-keyword arguments. * Removed code duplications in _PyEval_EvalCodeWithName(). * Removed redundant runtime checks and parameters in _PyStack_AsDict(). * Added a workaround and enabled previously disabled test in test_traceback. * Removed dead code from the dis module. --- Lib/dis.py | 2 -- Lib/test/test_extcall.py | 18 ++++++++++-------- Lib/test/test_traceback.py | 3 +-- 3 files changed, 11 insertions(+), 12 deletions(-) (limited to 'Lib') diff --git a/Lib/dis.py b/Lib/dis.py index e958c8ad1c..3a706be3c8 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -314,8 +314,6 @@ def _get_instructions_bytes(code, varnames=None, names=None, constants=None, argrepr = argval elif op in hasfree: argval, argrepr = _get_name_info(arg, cells) - elif op in hasnargs: # unused - argrepr = "%d positional, %d keyword pair" % (arg%256, arg//256) yield Instruction(opname[op], op, arg, argval, argrepr, offset, starts_line, is_jump_target) diff --git a/Lib/test/test_extcall.py b/Lib/test/test_extcall.py index 55f139304b..5eea37989c 100644 --- a/Lib/test/test_extcall.py +++ b/Lib/test/test_extcall.py @@ -118,7 +118,7 @@ Verify clearing of SF bug #733667 >>> g(*Nothing()) Traceback (most recent call last): ... - TypeError: 'Nothing' object is not iterable + TypeError: g() argument after * must be an iterable, not Nothing >>> class Nothing: ... def __len__(self): return 5 @@ -127,7 +127,7 @@ Verify clearing of SF bug #733667 >>> g(*Nothing()) Traceback (most recent call last): ... - TypeError: 'Nothing' object is not iterable + TypeError: g() argument after * must be an iterable, not Nothing >>> class Nothing(): ... def __len__(self): return 5 @@ -231,32 +231,34 @@ What about willful misconduct? >>> h(*h) Traceback (most recent call last): ... - TypeError: 'function' object is not iterable + TypeError: h() argument after * must be an iterable, not function >>> dir(*h) Traceback (most recent call last): ... - TypeError: 'function' object is not iterable + TypeError: dir() argument after * must be an iterable, not function >>> None(*h) Traceback (most recent call last): ... - TypeError: 'function' object is not iterable + TypeError: NoneType object argument after * must be an iterable, \ +not function >>> h(**h) Traceback (most recent call last): ... - TypeError: 'function' object is not a mapping + TypeError: h() argument after ** must be a mapping, not function >>> dir(**h) Traceback (most recent call last): ... - TypeError: 'function' object is not a mapping + TypeError: dir() argument after ** must be a mapping, not function >>> None(**h) Traceback (most recent call last): ... - TypeError: 'function' object is not a mapping + TypeError: NoneType object argument after ** must be a mapping, \ +not function >>> dir(b=1, **{'b': 1}) Traceback (most recent call last): diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 037d883ed4..ac067bf030 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -304,7 +304,6 @@ class TracebackFormatTests(unittest.TestCase): ]) # issue 26823 - Shrink recursive tracebacks - @unittest.skipIf(True, "FIXME: test broken, see issue #28050") def _check_recursive_traceback_display(self, render_exc): # Always show full diffs when this test fails # Note that rearranging things may require adjusting @@ -353,7 +352,7 @@ class TracebackFormatTests(unittest.TestCase): # Check the recursion count is roughly as expected rec_limit = sys.getrecursionlimit() - self.assertIn(int(re.search(r"\d+", actual[-2]).group()), range(rec_limit-50, rec_limit)) + self.assertIn(int(re.search(r"\d+", actual[-2]).group()), range(rec_limit-60, rec_limit)) # Check a known (limited) number of recursive invocations def g(count=10): -- cgit v1.2.1 From 813f60a476f032bd16a11cfb2cfa422ef313fa3a Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Sun, 11 Sep 2016 14:54:27 -0700 Subject: issue28082: better name for Flag --- Lib/re.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/re.py b/Lib/re.py index ad59640633..0850f0db8d 100644 --- a/Lib/re.py +++ b/Lib/re.py @@ -138,7 +138,7 @@ __all__ = [ __version__ = "2.2.1" -class Flag(enum.IntFlag): +class RegexFlag(enum.IntFlag): ASCII = sre_compile.SRE_FLAG_ASCII # assume ascii "locale" IGNORECASE = sre_compile.SRE_FLAG_IGNORECASE # ignore case LOCALE = sre_compile.SRE_FLAG_LOCALE # assume current 8-bit locale @@ -157,7 +157,7 @@ class Flag(enum.IntFlag): TEMPLATE = sre_compile.SRE_FLAG_TEMPLATE # disable backtracking T = TEMPLATE DEBUG = sre_compile.SRE_FLAG_DEBUG # dump pattern after compilation -globals().update(Flag.__members__) +globals().update(RegexFlag.__members__) # sre exception error = sre_compile.error -- cgit v1.2.1 From 99829a612c71c4fed8ac55e921f27a19a74bc634 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Mon, 12 Sep 2016 00:01:11 +0200 Subject: Issue #28085: Add PROTOCOL_TLS_CLIENT and PROTOCOL_TLS_SERVER for SSLContext --- Lib/ssl.py | 2 ++ Lib/test/test_ssl.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) (limited to 'Lib') diff --git a/Lib/ssl.py b/Lib/ssl.py index df5e98efc7..8ad4a339a9 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -52,6 +52,8 @@ PROTOCOL_SSLv2 PROTOCOL_SSLv3 PROTOCOL_SSLv23 PROTOCOL_TLS +PROTOCOL_TLS_CLIENT +PROTOCOL_TLS_SERVER PROTOCOL_TLSv1 PROTOCOL_TLSv1_1 PROTOCOL_TLSv1_2 diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 61744ae95a..557b6dec5b 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -1342,6 +1342,17 @@ class ContextTests(unittest.TestCase): ctx.check_hostname = False self.assertFalse(ctx.check_hostname) + def test_context_client_server(self): + # PROTOCOL_TLS_CLIENT has sane defaults + ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + self.assertTrue(ctx.check_hostname) + self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED) + + # PROTOCOL_TLS_SERVER has different but also sane defaults + ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + self.assertFalse(ctx.check_hostname) + self.assertEqual(ctx.verify_mode, ssl.CERT_NONE) + class SSLErrorTests(unittest.TestCase): @@ -2280,12 +2291,33 @@ if _have_threads: if support.verbose: sys.stdout.write("\n") for protocol in PROTOCOLS: + if protocol in {ssl.PROTOCOL_TLS_CLIENT, ssl.PROTOCOL_TLS_SERVER}: + continue with self.subTest(protocol=ssl._PROTOCOL_NAMES[protocol]): context = ssl.SSLContext(protocol) context.load_cert_chain(CERTFILE) server_params_test(context, context, chatty=True, connectionchatty=True) + client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + client_context.load_verify_locations(SIGNING_CA) + server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + # server_context.load_verify_locations(SIGNING_CA) + server_context.load_cert_chain(SIGNED_CERTFILE2) + + with self.subTest(client='PROTOCOL_TLS_CLIENT', server='PROTOCOL_TLS_SERVER'): + server_params_test(client_context=client_context, + server_context=server_context, + chatty=True, connectionchatty=True, + sni_name='fakehostname') + + with self.subTest(client='PROTOCOL_TLS_SERVER', server='PROTOCOL_TLS_CLIENT'): + with self.assertRaises(ssl.SSLError): + server_params_test(client_context=server_context, + server_context=client_context, + chatty=True, connectionchatty=True, + sni_name='fakehostname') + def test_getpeercert(self): if support.verbose: sys.stdout.write("\n") -- cgit v1.2.1 From d8cb736ab2eaa637b422968e5dc2706cc1da44a3 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Sun, 11 Sep 2016 15:31:27 -0700 Subject: Issue #28079: Update typing and test typing from python/typing repo. Ivan Levkivskyi (3.6 version) --- Lib/test/test_typing.py | 143 +++++++++++++++++------- Lib/typing.py | 290 +++++++++++++++++++++++++++++++----------------- 2 files changed, 289 insertions(+), 144 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index e3904b1baa..3b99060fac 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -4,8 +4,6 @@ import pickle import re import sys from unittest import TestCase, main, skipUnless, SkipTest -from collections import ChainMap -from test import ann_module, ann_module2, ann_module3 from typing import Any from typing import TypeVar, AnyStr @@ -969,46 +967,6 @@ class ForwardRefTests(BaseTestCase): right_hints = get_type_hints(t.add_right, globals(), locals()) self.assertEqual(right_hints['node'], Optional[Node[T]]) - def test_get_type_hints(self): - gth = get_type_hints - self.assertEqual(gth(ann_module), {'x': int, 'y': str}) - self.assertEqual(gth(ann_module.C, ann_module.__dict__), - ChainMap({'y': Optional[ann_module.C]}, {})) - self.assertEqual(gth(ann_module2), {}) - self.assertEqual(gth(ann_module3), {}) - self.assertEqual(repr(gth(ann_module.j_class)), 'ChainMap({}, {})') - self.assertEqual(gth(ann_module.M), ChainMap({'123': 123, 'o': type}, - {}, {})) - self.assertEqual(gth(ann_module.D), - ChainMap({'j': str, 'k': str, - 'y': Optional[ann_module.C]}, {})) - self.assertEqual(gth(ann_module.Y), ChainMap({'z': int}, {})) - self.assertEqual(gth(ann_module.h_class), - ChainMap({}, {'y': Optional[ann_module.C]}, {})) - self.assertEqual(gth(ann_module.S), ChainMap({'x': str, 'y': str}, - {})) - self.assertEqual(gth(ann_module.foo), {'x': int}) - - def testf(x, y): ... - testf.__annotations__['x'] = 'int' - self.assertEqual(gth(testf), {'x': int}) - self.assertEqual(gth(ann_module2.NTC.meth), {}) - - # interactions with ClassVar - class B: - x: ClassVar[Optional['B']] = None - y: int - class C(B): - z: ClassVar['C'] = B() - class G(Generic[T]): - lst: ClassVar[List[T]] = [] - self.assertEqual(gth(B, locals()), - ChainMap({'y': int, 'x': ClassVar[Optional[B]]}, {})) - self.assertEqual(gth(C, locals()), - ChainMap({'z': ClassVar[C]}, - {'y': int, 'x': ClassVar[Optional[B]]}, {})) - self.assertEqual(gth(G), ChainMap({'lst': ClassVar[List[T]]},{},{})) - def test_forwardref_instance_type_error(self): fr = typing._ForwardRef('int') with self.assertRaises(TypeError): @@ -1198,6 +1156,84 @@ class AsyncIteratorWrapper(typing.AsyncIterator[T_a]): if PY35: exec(PY35_TESTS) +PY36 = sys.version_info[:2] >= (3, 6) + +PY36_TESTS = """ +from test import ann_module, ann_module2, ann_module3 +from collections import ChainMap + +class B: + x: ClassVar[Optional['B']] = None + y: int +class CSub(B): + z: ClassVar['CSub'] = B() +class G(Generic[T]): + lst: ClassVar[List[T]] = [] + +class CoolEmployee(NamedTuple): + name: str + cool: int +""" + +if PY36: + exec(PY36_TESTS) + +gth = get_type_hints + +class GetTypeHintTests(BaseTestCase): + @skipUnless(PY36, 'Python 3.6 required') + def test_get_type_hints_modules(self): + self.assertEqual(gth(ann_module), {'x': int, 'y': str}) + self.assertEqual(gth(ann_module2), {}) + self.assertEqual(gth(ann_module3), {}) + + @skipUnless(PY36, 'Python 3.6 required') + def test_get_type_hints_classes(self): + self.assertEqual(gth(ann_module.C, ann_module.__dict__), + ChainMap({'y': Optional[ann_module.C]}, {})) + self.assertEqual(repr(gth(ann_module.j_class)), 'ChainMap({}, {})') + self.assertEqual(gth(ann_module.M), ChainMap({'123': 123, 'o': type}, + {}, {})) + self.assertEqual(gth(ann_module.D), + ChainMap({'j': str, 'k': str, + 'y': Optional[ann_module.C]}, {})) + self.assertEqual(gth(ann_module.Y), ChainMap({'z': int}, {})) + self.assertEqual(gth(ann_module.h_class), + ChainMap({}, {'y': Optional[ann_module.C]}, {})) + self.assertEqual(gth(ann_module.S), ChainMap({'x': str, 'y': str}, + {})) + self.assertEqual(gth(ann_module.foo), {'x': int}) + + @skipUnless(PY36, 'Python 3.6 required') + def test_respect_no_type_check(self): + @no_type_check + class NoTpCheck: + class Inn: + def __init__(self, x: 'not a type'): ... + self.assertTrue(NoTpCheck.__no_type_check__) + self.assertTrue(NoTpCheck.Inn.__init__.__no_type_check__) + self.assertEqual(gth(ann_module2.NTC.meth), {}) + class ABase(Generic[T]): + def meth(x: int): ... + @no_type_check + class Der(ABase): ... + self.assertEqual(gth(ABase.meth), {'x': int}) + + + def test_previous_behavior(self): + def testf(x, y): ... + testf.__annotations__['x'] = 'int' + self.assertEqual(gth(testf), {'x': int}) + + @skipUnless(PY36, 'Python 3.6 required') + def test_get_type_hints_ClassVar(self): + self.assertEqual(gth(B, globals()), + ChainMap({'y': int, 'x': ClassVar[Optional[B]]}, {})) + self.assertEqual(gth(CSub, globals()), + ChainMap({'z': ClassVar[CSub]}, + {'y': int, 'x': ClassVar[Optional[B]]}, {})) + self.assertEqual(gth(G), ChainMap({'lst': ClassVar[List[T]]},{},{})) + class CollectionsAbcTests(BaseTestCase): @@ -1505,6 +1541,18 @@ class TypeTests(BaseTestCase): joe = new_user(BasicUser) + def test_type_optional(self): + A = Optional[Type[BaseException]] + + def foo(a: A) -> Optional[BaseException]: + if a is None: + return None + else: + return a() + + assert isinstance(foo(KeyboardInterrupt), KeyboardInterrupt) + assert foo(None) is None + class NewTypeTests(BaseTestCase): @@ -1542,6 +1590,17 @@ class NamedTupleTests(BaseTestCase): self.assertEqual(Emp._fields, ('name', 'id')) self.assertEqual(Emp._field_types, dict(name=str, id=int)) + @skipUnless(PY36, 'Python 3.6 required') + def test_annotation_usage(self): + tim = CoolEmployee('Tim', 9000) + self.assertIsInstance(tim, CoolEmployee) + self.assertIsInstance(tim, tuple) + self.assertEqual(tim.name, 'Tim') + self.assertEqual(tim.cool, 9000) + self.assertEqual(CoolEmployee.__name__, 'CoolEmployee') + self.assertEqual(CoolEmployee._fields, ('name', 'cool')) + self.assertEqual(CoolEmployee._field_types, dict(name=str, cool=int)) + def test_pickle(self): global Emp # pickle wants to reference the class by name Emp = NamedTuple('Emp', [('name', str), ('id', int)]) diff --git a/Lib/typing.py b/Lib/typing.py index 6ce74fc9f1..4676d28c8e 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -6,11 +6,12 @@ import functools import re as stdlib_re # Avoid confusion with the re we export. import sys import types - try: import collections.abc as collections_abc except ImportError: import collections as collections_abc # Fallback for PY3.2. +if sys.version_info[:2] >= (3, 3): + from collections import ChainMap # Please keep __all__ alphabetized within each category. @@ -1204,53 +1205,135 @@ def _get_defaults(func): return res -def get_type_hints(obj, globalns=None, localns=None): - """Return type hints for an object. +if sys.version_info[:2] >= (3, 3): + def get_type_hints(obj, globalns=None, localns=None): + """Return type hints for an object. - This is often the same as obj.__annotations__, but it handles - forward references encoded as string literals, and if necessary - adds Optional[t] if a default value equal to None is set. + This is often the same as obj.__annotations__, but it handles + forward references encoded as string literals, and if necessary + adds Optional[t] if a default value equal to None is set. - The argument may be a module, class, method, or function. The annotations - are returned as a dictionary, or in the case of a class, a ChainMap of - dictionaries. + The argument may be a module, class, method, or function. The annotations + are returned as a dictionary, or in the case of a class, a ChainMap of + dictionaries. - TypeError is raised if the argument is not of a type that can contain - annotations, and an empty dictionary is returned if no annotations are - present. + TypeError is raised if the argument is not of a type that can contain + annotations, and an empty dictionary is returned if no annotations are + present. - BEWARE -- the behavior of globalns and localns is counterintuitive - (unless you are familiar with how eval() and exec() work). The - search order is locals first, then globals. + BEWARE -- the behavior of globalns and localns is counterintuitive + (unless you are familiar with how eval() and exec() work). The + search order is locals first, then globals. - - If no dict arguments are passed, an attempt is made to use the - globals from obj, and these are also used as the locals. If the - object does not appear to have globals, an exception is raised. + - If no dict arguments are passed, an attempt is made to use the + globals from obj, and these are also used as the locals. If the + object does not appear to have globals, an exception is raised. - - If one dict argument is passed, it is used for both globals and - locals. + - If one dict argument is passed, it is used for both globals and + locals. - - If two dict arguments are passed, they specify globals and - locals, respectively. - """ + - If two dict arguments are passed, they specify globals and + locals, respectively. + """ - if getattr(obj, '__no_type_check__', None): - return {} - if globalns is None: - globalns = getattr(obj, '__globals__', {}) - if localns is None: + if getattr(obj, '__no_type_check__', None): + return {} + if globalns is None: + globalns = getattr(obj, '__globals__', {}) + if localns is None: + localns = globalns + elif localns is None: localns = globalns - elif localns is None: - localns = globalns - if (isinstance(obj, types.FunctionType) or - isinstance(obj, types.BuiltinFunctionType) or - isinstance(obj, types.MethodType)): + if (isinstance(obj, types.FunctionType) or + isinstance(obj, types.BuiltinFunctionType) or + isinstance(obj, types.MethodType)): + defaults = _get_defaults(obj) + hints = obj.__annotations__ + for name, value in hints.items(): + if value is None: + value = type(None) + if isinstance(value, str): + value = _ForwardRef(value) + value = _eval_type(value, globalns, localns) + if name in defaults and defaults[name] is None: + value = Optional[value] + hints[name] = value + return hints + + if isinstance(obj, types.ModuleType): + try: + hints = obj.__annotations__ + except AttributeError: + return {} + # we keep only those annotations that can be accessed on module + members = obj.__dict__ + hints = {name: value for name, value in hints.items() + if name in members} + for name, value in hints.items(): + if value is None: + value = type(None) + if isinstance(value, str): + value = _ForwardRef(value) + value = _eval_type(value, globalns, localns) + hints[name] = value + return hints + + if isinstance(object, type): + cmap = None + for base in reversed(obj.__mro__): + new_map = collections.ChainMap if cmap is None else cmap.new_child + try: + hints = base.__dict__['__annotations__'] + except KeyError: + cmap = new_map() + else: + for name, value in hints.items(): + if value is None: + value = type(None) + if isinstance(value, str): + value = _ForwardRef(value) + value = _eval_type(value, globalns, localns) + hints[name] = value + cmap = new_map(hints) + return cmap + + raise TypeError('{!r} is not a module, class, method, ' + 'or function.'.format(obj)) + +else: + def get_type_hints(obj, globalns=None, localns=None): + """Return type hints for a function or method object. + + This is often the same as obj.__annotations__, but it handles + forward references encoded as string literals, and if necessary + adds Optional[t] if a default value equal to None is set. + + BEWARE -- the behavior of globalns and localns is counterintuitive + (unless you are familiar with how eval() and exec() work). The + search order is locals first, then globals. + + - If no dict arguments are passed, an attempt is made to use the + globals from obj, and these are also used as the locals. If the + object does not appear to have globals, an exception is raised. + + - If one dict argument is passed, it is used for both globals and + locals. + + - If two dict arguments are passed, they specify globals and + locals, respectively. + """ + if getattr(obj, '__no_type_check__', None): + return {} + if globalns is None: + globalns = getattr(obj, '__globals__', {}) + if localns is None: + localns = globalns + elif localns is None: + localns = globalns defaults = _get_defaults(obj) - hints = obj.__annotations__ + hints = dict(obj.__annotations__) for name, value in hints.items(): - if value is None: - value = type(None) if isinstance(value, str): value = _ForwardRef(value) value = _eval_type(value, globalns, localns) @@ -1259,62 +1342,30 @@ def get_type_hints(obj, globalns=None, localns=None): hints[name] = value return hints - if isinstance(obj, types.ModuleType): - try: - hints = obj.__annotations__ - except AttributeError: - return {} - # we keep only those annotations that can be accessed on module - members = obj.__dict__ - hints = {name: value for name, value in hints.items() - if name in members} - for name, value in hints.items(): - if value is None: - value = type(None) - if isinstance(value, str): - value = _ForwardRef(value) - value = _eval_type(value, globalns, localns) - hints[name] = value - return hints - - if isinstance(object, type): - cmap = None - for base in reversed(obj.__mro__): - new_map = collections.ChainMap if cmap is None else cmap.new_child - try: - hints = base.__dict__['__annotations__'] - except KeyError: - cmap = new_map() - else: - for name, value in hints.items(): - if value is None: - value = type(None) - if isinstance(value, str): - value = _ForwardRef(value) - value = _eval_type(value, globalns, localns) - hints[name] = value - cmap = new_map(hints) - return cmap - - raise TypeError('{!r} is not a module, class, method, ' - 'or function.'.format(obj)) - def no_type_check(arg): """Decorator to indicate that annotations are not type hints. The argument must be a class or function; if it is a class, it - applies recursively to all methods defined in that class (but not - to methods defined in its superclasses or subclasses). + applies recursively to all methods and classes defined in that class + (but not to methods defined in its superclasses or subclasses). - This mutates the function(s) in place. + This mutates the function(s) or class(es) in place. """ if isinstance(arg, type): - for obj in arg.__dict__.values(): + arg_attrs = arg.__dict__.copy() + for attr, val in arg.__dict__.items(): + if val in arg.__bases__: + arg_attrs.pop(attr) + for obj in arg_attrs.values(): if isinstance(obj, types.FunctionType): obj.__no_type_check__ = True - else: + if isinstance(obj, type): + no_type_check(obj) + try: arg.__no_type_check__ = True + except TypeError: # built-in classes + pass return arg @@ -1725,7 +1776,7 @@ CT_co = TypeVar('CT_co', covariant=True, bound=type) # This is not a real generic class. Don't use outside annotations. -class Type(type, Generic[CT_co], extra=type): +class Type(Generic[CT_co], extra=type): """A special construct usable to annotate class objects. For example, suppose we have the following classes:: @@ -1750,31 +1801,66 @@ class Type(type, Generic[CT_co], extra=type): """ -def NamedTuple(typename, fields): - """Typed version of namedtuple. +def _make_nmtuple(name, types): + nm_tpl = collections.namedtuple(name, [n for n, t in types]) + nm_tpl._field_types = dict(types) + try: + nm_tpl.__module__ = sys._getframe(2).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + pass + return nm_tpl - Usage:: - Employee = typing.NamedTuple('Employee', [('name', str), 'id', int)]) +if sys.version_info[:2] >= (3, 6): + class NamedTupleMeta(type): - This is equivalent to:: + def __new__(cls, typename, bases, ns, *, _root=False): + if _root: + return super().__new__(cls, typename, bases, ns) + types = ns.get('__annotations__', {}) + return _make_nmtuple(typename, types.items()) - Employee = collections.namedtuple('Employee', ['name', 'id']) + class NamedTuple(metaclass=NamedTupleMeta, _root=True): + """Typed version of namedtuple. - The resulting class has one extra attribute: _field_types, - giving a dict mapping field names to types. (The field names - are in the _fields attribute, which is part of the namedtuple - API.) - """ - fields = [(n, t) for n, t in fields] - cls = collections.namedtuple(typename, [n for n, t in fields]) - cls._field_types = dict(fields) - # Set the module to the caller's module (otherwise it'd be 'typing'). - try: - cls.__module__ = sys._getframe(1).f_globals.get('__name__', '__main__') - except (AttributeError, ValueError): - pass - return cls + Usage:: + + class Employee(NamedTuple): + name: str + id: int + + This is equivalent to:: + + Employee = collections.namedtuple('Employee', ['name', 'id']) + + The resulting class has one extra attribute: _field_types, + giving a dict mapping field names to types. (The field names + are in the _fields attribute, which is part of the namedtuple + API.) Backward-compatible usage:: + + Employee = NamedTuple('Employee', [('name', str), ('id', int)]) + """ + + def __new__(self, typename, fields): + return _make_nmtuple(typename, fields) +else: + def NamedTuple(typename, fields): + """Typed version of namedtuple. + + Usage:: + + Employee = typing.NamedTuple('Employee', [('name', str), 'id', int)]) + + This is equivalent to:: + + Employee = collections.namedtuple('Employee', ['name', 'id']) + + The resulting class has one extra attribute: _field_types, + giving a dict mapping field names to types. (The field names + are in the _fields attribute, which is part of the namedtuple + API.) + """ + return _make_nmtuple(typename, fields) def NewType(name, tp): -- cgit v1.2.1 From f44bfdae681b2ece4e8f1dec0bf4a46d98fe533b Mon Sep 17 00:00:00 2001 From: "Eric V. Smith" Date: Sun, 11 Sep 2016 18:58:20 -0400 Subject: Make an f-string error message more exact and consistent. --- Lib/test/test_fstring.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index e61f63594f..655819425d 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -182,9 +182,10 @@ f'{a * x()}'""" self.assertEqual(f'{"#"}', '#') self.assertEqual(f'{d["#"]}', 'hash') - self.assertAllRaise(SyntaxError, "f-string cannot include '#'", + self.assertAllRaise(SyntaxError, "f-string expression part cannot include '#'", ["f'{1#}'", # error because the expression becomes "(1#)" "f'{3(#)}'", + "f'{#}'", ]) def test_many_expressions(self): -- cgit v1.2.1 From e6a0fd306f91764da3eb7f3cde738baf6f850386 Mon Sep 17 00:00:00 2001 From: "Eric V. Smith" Date: Sun, 11 Sep 2016 19:01:22 -0400 Subject: Add another f-string comment test, to make sure # are being caught in the right place. --- Lib/test/test_fstring.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index 655819425d..92995fd83e 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -186,6 +186,8 @@ f'{a * x()}'""" ["f'{1#}'", # error because the expression becomes "(1#)" "f'{3(#)}'", "f'{#}'", + "f'{)#}'", # When wrapped in parens, this becomes + # '()#)'. Make sure that doesn't compile. ]) def test_many_expressions(self): -- cgit v1.2.1 From 19ac8dcff772853e5c2e1dea65d1bf86a3d240c5 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Sun, 11 Sep 2016 19:43:51 -0700 Subject: Fixes test_getargs2 to get the buildbots working again. --- Lib/test/test_getargs2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_getargs2.py b/Lib/test/test_getargs2.py index 8a194aa03d..5750bfa5f8 100644 --- a/Lib/test/test_getargs2.py +++ b/Lib/test/test_getargs2.py @@ -471,7 +471,7 @@ class Tuple_TestCase(unittest.TestCase): ret = get_args(*TupleSubclass([1, 2])) self.assertEqual(ret, (1, 2)) - self.assertIs(type(ret), tuple) + self.assertIsInstance(ret, tuple) ret = get_args() self.assertIn(ret, ((), None)) -- cgit v1.2.1 From 1c168f15e46f8a07858b206188446fd9a8eb31d5 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Mon, 12 Sep 2016 00:26:20 -0400 Subject: Issue #28095: Temporarily disable part of test_startup_imports on OS X. --- Lib/test/test_site.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index 0720230f24..9afa56eb73 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -470,7 +470,9 @@ class StartupImportTests(unittest.TestCase): 'heapq', 'itertools', 'keyword', 'operator', 'reprlib', 'types', 'weakref' }.difference(sys.builtin_module_names) - self.assertFalse(modules.intersection(collection_mods), stderr) + # http://bugs.python.org/issue28095 + if sys.platform != 'darwin': + self.assertFalse(modules.intersection(collection_mods), stderr) if __name__ == "__main__": -- cgit v1.2.1 From fcdb77fd2fea386c8a1c753b0367354627502a9f Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Mon, 12 Sep 2016 00:18:31 -0700 Subject: Issue #17941: Add a *module* parameter to collections.namedtuple() --- Lib/collections/__init__.py | 16 ++++++++++------ Lib/test/test_collections.py | 4 ++++ 2 files changed, 14 insertions(+), 6 deletions(-) (limited to 'Lib') diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py index 03ecea27cc..bcc429195d 100644 --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -353,7 +353,7 @@ _field_template = '''\ {name} = _property(_itemgetter({index:d}), doc='Alias for field number {index:d}') ''' -def namedtuple(typename, field_names, *, verbose=False, rename=False): +def namedtuple(typename, field_names, *, verbose=False, rename=False, module=None): """Returns a new subclass of tuple with named fields. >>> Point = namedtuple('Point', ['x', 'y']) @@ -434,11 +434,15 @@ def namedtuple(typename, field_names, *, verbose=False, rename=False): # For pickling to work, the __module__ variable needs to be set to the frame # where the named tuple is created. Bypass this step in environments where # sys._getframe is not defined (Jython for example) or sys._getframe is not - # defined for arguments greater than 0 (IronPython). - try: - result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__') - except (AttributeError, ValueError): - pass + # defined for arguments greater than 0 (IronPython), or where the user has + # specified a particular module. + if module is None: + try: + module = _sys._getframe(1).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + pass + if module is not None: + result.__module__ = module return result diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index f1fb011266..52ff256eb9 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -242,6 +242,10 @@ class TestNamedTuple(unittest.TestCase): ]: self.assertEqual(namedtuple('NT', spec, rename=True)._fields, renamed) + def test_module_parameter(self): + NT = namedtuple('NT', ['x', 'y'], module=collections) + self.assertEqual(NT.__module__, collections) + def test_instance(self): Point = namedtuple('Point', 'x y') p = Point(11, 22) -- cgit v1.2.1 From 419ef272447233d9921a146ba6f03316f32fc549 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Mon, 12 Sep 2016 10:48:20 +0200 Subject: Issue #28093: Check more invalid combinations of PROTOCOL_TLS_CLIENT / PROTOCOL_TLS_SERVER --- Lib/test/test_ssl.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 557b6dec5b..46ec8223e8 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -2305,18 +2305,38 @@ if _have_threads: # server_context.load_verify_locations(SIGNING_CA) server_context.load_cert_chain(SIGNED_CERTFILE2) - with self.subTest(client='PROTOCOL_TLS_CLIENT', server='PROTOCOL_TLS_SERVER'): + with self.subTest(client=ssl.PROTOCOL_TLS_CLIENT, server=ssl.PROTOCOL_TLS_SERVER): server_params_test(client_context=client_context, server_context=server_context, chatty=True, connectionchatty=True, sni_name='fakehostname') - with self.subTest(client='PROTOCOL_TLS_SERVER', server='PROTOCOL_TLS_CLIENT'): - with self.assertRaises(ssl.SSLError): + client_context.check_hostname = False + with self.subTest(client=ssl.PROTOCOL_TLS_SERVER, server=ssl.PROTOCOL_TLS_CLIENT): + with self.assertRaises(ssl.SSLError) as e: server_params_test(client_context=server_context, server_context=client_context, chatty=True, connectionchatty=True, sni_name='fakehostname') + self.assertIn('called a function you should not call', + str(e.exception)) + + with self.subTest(client=ssl.PROTOCOL_TLS_SERVER, server=ssl.PROTOCOL_TLS_SERVER): + with self.assertRaises(ssl.SSLError) as e: + server_params_test(client_context=server_context, + server_context=server_context, + chatty=True, connectionchatty=True) + self.assertIn('called a function you should not call', + str(e.exception)) + + with self.subTest(client=ssl.PROTOCOL_TLS_CLIENT, server=ssl.PROTOCOL_TLS_CLIENT): + with self.assertRaises(ssl.SSLError) as e: + server_params_test(client_context=server_context, + server_context=client_context, + chatty=True, connectionchatty=True) + self.assertIn('called a function you should not call', + str(e.exception)) + def test_getpeercert(self): if support.verbose: -- cgit v1.2.1 From f0f5eb55f39780719935ea41d3d6b7c746e0ecfc Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Mon, 12 Sep 2016 15:08:32 +0200 Subject: Issue #27322: skip test_compile_path when sys.path is not writeable. --- Lib/test/test_compileall.py | 1 + 1 file changed, 1 insertion(+) (limited to 'Lib') diff --git a/Lib/test/test_compileall.py b/Lib/test/test_compileall.py index 9b424a7250..ff29c91fdb 100644 --- a/Lib/test/test_compileall.py +++ b/Lib/test/test_compileall.py @@ -105,6 +105,7 @@ class CompileallTests(unittest.TestCase): def test_compile_path(self): # Exclude Lib/test/ which contains invalid Python files like # Lib/test/badsyntax_pep3120.py + self._skip_if_sys_path_not_writable() testdir = os.path.realpath(os.path.dirname(__file__)) if testdir in sys.path: self.addCleanup(setattr, sys, 'path', sys.path) -- cgit v1.2.1 From af801581e05f7b8d76ef7304fd6b3633867b5a6c Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Mon, 12 Sep 2016 15:22:25 +0200 Subject: Issue #27322: back out the commit. needs to be addressed after beta1. --- Lib/test/test_compileall.py | 1 - 1 file changed, 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_compileall.py b/Lib/test/test_compileall.py index ff29c91fdb..9b424a7250 100644 --- a/Lib/test/test_compileall.py +++ b/Lib/test/test_compileall.py @@ -105,7 +105,6 @@ class CompileallTests(unittest.TestCase): def test_compile_path(self): # Exclude Lib/test/ which contains invalid Python files like # Lib/test/badsyntax_pep3120.py - self._skip_if_sys_path_not_writable() testdir = os.path.realpath(os.path.dirname(__file__)) if testdir in sys.path: self.addCleanup(setattr, sys, 'path', sys.path) -- cgit v1.2.1 From 9b63804c5b7cab279467fc278480718382056913 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Mon, 12 Sep 2016 10:48:44 -0400 Subject: Update pydoc topics for 3.6.0b1 --- Lib/pydoc_data/topics.py | 313 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 226 insertions(+), 87 deletions(-) (limited to 'Lib') diff --git a/Lib/pydoc_data/topics.py b/Lib/pydoc_data/topics.py index 590f6135cf..3579484fd0 100644 --- a/Lib/pydoc_data/topics.py +++ b/Lib/pydoc_data/topics.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Autogenerated by Sphinx on Mon Aug 15 16:11:20 2016 +# Autogenerated by Sphinx on Mon Sep 12 10:47:11 2016 topics = {'assert': '\n' 'The "assert" statement\n' '**********************\n' @@ -353,7 +353,58 @@ topics = {'assert': '\n' 'For targets which are attribute references, the same caveat ' 'about\n' 'class and instance attributes applies as for regular ' - 'assignments.\n', + 'assignments.\n' + '\n' + '\n' + 'Annotated assignment statements\n' + '===============================\n' + '\n' + 'Annotation assignment is the combination, in a single ' + 'statement, of a\n' + 'variable or attribute annotation and an optional assignment ' + 'statement:\n' + '\n' + ' annotated_assignment_stmt ::= augtarget ":" expression ["=" ' + 'expression]\n' + '\n' + 'The difference from normal Assignment statements is that only ' + 'single\n' + 'target and only single right hand side value is allowed.\n' + '\n' + 'For simple names as assignment targets, if in class or module ' + 'scope,\n' + 'the annotations are evaluated and stored in a special class or ' + 'module\n' + 'attribute "__annotations__" that is a dictionary mapping from ' + 'variable\n' + 'names (mangled if private) to evaluated annotations. This ' + 'attribute is\n' + 'writable and is automatically created at the start of class or ' + 'module\n' + 'body execution, if annotations are found statically.\n' + '\n' + 'For expressions as assignment targets, the annotations are ' + 'evaluated\n' + 'if in class or module scope, but not stored.\n' + '\n' + 'If a name is annotated in a function scope, then this name is ' + 'local\n' + 'for that scope. Annotations are never evaluated and stored in ' + 'function\n' + 'scopes.\n' + '\n' + 'If the right hand side is present, an annotated assignment ' + 'performs\n' + 'the actual assignment before evaluating annotations (where\n' + 'applicable). If the right hand side is not present for an ' + 'expression\n' + 'target, then the interpreter evaluates the target except for ' + 'the last\n' + '"__setitem__()" or "__setattr__()" call.\n' + '\n' + 'See also: **PEP 526** - Variable and attribute annotation ' + 'syntax\n' + ' **PEP 484** - Type hints\n', 'atom-identifiers': '\n' 'Identifiers (Names)\n' '*******************\n' @@ -1375,6 +1426,13 @@ topics = {'assert': '\n' 'The class name is bound to this class object in the original local\n' 'namespace.\n' '\n' + 'The order in which attributes are defined in the class body is\n' + 'preserved in the new class\'s "__dict__". Note that this is ' + 'reliable\n' + 'only right after the class is created and only for classes that ' + 'were\n' + 'defined using the definition syntax.\n' + '\n' 'Class creation can be customized heavily using metaclasses.\n' '\n' 'Classes can also be decorated: just like when decorating ' @@ -1770,9 +1828,11 @@ topics = {'assert': '\n' '\n' 'The operators "is" and "is not" test for object identity: "x ' 'is y" is\n' - 'true if and only if *x* and *y* are the same object. "x is ' - 'not y"\n' - 'yields the inverse truth value. [4]\n', + 'true if and only if *x* and *y* are the same object. Object ' + 'identity\n' + 'is determined using the "id()" function. "x is not y" yields ' + 'the\n' + 'inverse truth value. [4]\n', 'compound': '\n' 'Compound statements\n' '*******************\n' @@ -2375,14 +2435,14 @@ topics = {'assert': '\n' 'is\n' 'present, it is initialized to a tuple receiving any excess ' 'positional\n' - 'parameters, defaulting to the empty tuple. If the form\n' - '""**identifier"" is present, it is initialized to a new ' - 'dictionary\n' - 'receiving any excess keyword arguments, defaulting to a new ' - 'empty\n' - 'dictionary. Parameters after ""*"" or ""*identifier"" are ' - 'keyword-only\n' - 'parameters and may only be passed used keyword arguments.\n' + 'parameters, defaulting to the empty tuple. If the form\n' + '""**identifier"" is present, it is initialized to a new ordered\n' + 'mapping receiving any excess keyword arguments, defaulting to a ' + 'new\n' + 'empty mapping of the same type. Parameters after ""*"" or\n' + '""*identifier"" are keyword-only parameters and may only be ' + 'passed\n' + 'used keyword arguments.\n' '\n' 'Parameters may have annotations of the form "": expression"" ' 'following\n' @@ -2481,6 +2541,13 @@ topics = {'assert': '\n' 'local\n' 'namespace.\n' '\n' + 'The order in which attributes are defined in the class body is\n' + 'preserved in the new class\'s "__dict__". Note that this is ' + 'reliable\n' + 'only right after the class is created and only for classes that ' + 'were\n' + 'defined using the definition syntax.\n' + '\n' 'Class creation can be customized heavily using metaclasses.\n' '\n' 'Classes can also be decorated: just like when decorating ' @@ -2832,7 +2899,7 @@ topics = {'assert': '\n' ' Because "__new__()" and "__init__()" work together in ' 'constructing\n' ' objects ("__new__()" to create it, and "__init__()" to ' - 'customise\n' + 'customize\n' ' it), no non-"None" value may be returned by ' '"__init__()"; doing so\n' ' will cause a "TypeError" to be raised at runtime.\n' @@ -3376,7 +3443,7 @@ topics = {'assert': '\n' 'to access further features, you have to do this yourself:\n' '\n' "class pdb.Pdb(completekey='tab', stdin=None, stdout=None, " - 'skip=None, nosigint=False)\n' + 'skip=None, nosigint=False, readrc=True)\n' '\n' ' "Pdb" is the debugger class.\n' '\n' @@ -3399,7 +3466,11 @@ topics = {'assert': '\n' 'debugger\n' ' again by pressing "Ctrl-C". If you want Pdb not to touch ' 'the\n' - ' SIGINT handler, set *nosigint* tot true.\n' + ' SIGINT handler, set *nosigint* to true.\n' + '\n' + ' The *readrc* argument defaults to true and controls whether ' + 'Pdb\n' + ' will load .pdbrc files from the filesystem.\n' '\n' ' Example call to enable tracing with *skip*:\n' '\n' @@ -3411,6 +3482,8 @@ topics = {'assert': '\n' 'SIGINT\n' ' handler was never set by Pdb.\n' '\n' + ' Changed in version 3.6: The *readrc* argument.\n' + '\n' ' run(statement, globals=None, locals=None)\n' ' runeval(expression, globals=None, locals=None)\n' ' runcall(function, *args, **kwds)\n' @@ -4450,27 +4523,35 @@ topics = {'assert': '\n' 'definitions:\n' '\n' ' floatnumber ::= pointfloat | exponentfloat\n' - ' pointfloat ::= [intpart] fraction | intpart "."\n' - ' exponentfloat ::= (intpart | pointfloat) exponent\n' - ' intpart ::= digit+\n' - ' fraction ::= "." digit+\n' - ' exponent ::= ("e" | "E") ["+" | "-"] digit+\n' + ' pointfloat ::= [digitpart] fraction | digitpart "."\n' + ' exponentfloat ::= (digitpart | pointfloat) exponent\n' + ' digitpart ::= digit (["_"] digit)*\n' + ' fraction ::= "." digitpart\n' + ' exponent ::= ("e" | "E") ["+" | "-"] digitpart\n' '\n' 'Note that the integer and exponent parts are always interpreted ' 'using\n' 'radix 10. For example, "077e010" is legal, and denotes the same ' 'number\n' 'as "77e10". The allowed range of floating point literals is\n' - 'implementation-dependent. Some examples of floating point ' - 'literals:\n' + 'implementation-dependent. As in integer literals, underscores ' + 'are\n' + 'supported for digit grouping.\n' '\n' - ' 3.14 10. .001 1e100 3.14e-10 0e0\n' + 'Some examples of floating point literals:\n' + '\n' + ' 3.14 10. .001 1e100 3.14e-10 0e0 ' + '3.14_15_93\n' '\n' 'Note that numeric literals do not include a sign; a phrase like ' '"-1"\n' 'is actually an expression composed of the unary operator "-" and ' 'the\n' - 'literal "1".\n', + 'literal "1".\n' + '\n' + 'Changed in version 3.6: Underscores are now allowed for ' + 'grouping\n' + 'purposes in literals.\n', 'for': '\n' 'The "for" statement\n' '*******************\n' @@ -4730,15 +4811,16 @@ topics = {'assert': '\n' '\n' 'The general form of a *standard format specifier* is:\n' '\n' - ' format_spec ::= ' - '[[fill]align][sign][#][0][width][,][.precision][type]\n' - ' fill ::= \n' - ' align ::= "<" | ">" | "=" | "^"\n' - ' sign ::= "+" | "-" | " "\n' - ' width ::= integer\n' - ' precision ::= integer\n' - ' type ::= "b" | "c" | "d" | "e" | "E" | "f" | "F" ' - '| "g" | "G" | "n" | "o" | "s" | "x" | "X" | "%"\n' + ' format_spec ::= ' + '[[fill]align][sign][#][0][width][grouping_option][.precision][type]\n' + ' fill ::= \n' + ' align ::= "<" | ">" | "=" | "^"\n' + ' sign ::= "+" | "-" | " "\n' + ' width ::= integer\n' + ' grouping_option ::= "_" | ","\n' + ' precision ::= integer\n' + ' type ::= "b" | "c" | "d" | "e" | "E" | "f" | ' + '"F" | "g" | "G" | "n" | "o" | "s" | "x" | "X" | "%"\n' '\n' 'If a valid *align* value is specified, it can be preceded ' 'by a *fill*\n' @@ -4864,6 +4946,20 @@ topics = {'assert': '\n' 'Changed in version 3.1: Added the "\',\'" option (see also ' '**PEP 378**).\n' '\n' + 'The "\'_\'" option signals the use of an underscore for a ' + 'thousands\n' + 'separator for floating point presentation types and for ' + 'integer\n' + 'presentation type "\'d\'". For integer presentation types ' + '"\'b\'", "\'o\'",\n' + '"\'x\'", and "\'X\'", underscores will be inserted every 4 ' + 'digits. For\n' + 'other presentation types, specifying this option is an ' + 'error.\n' + '\n' + 'Changed in version 3.6: Added the "\'_\'" option (see also ' + '**PEP 515**).\n' + '\n' '*width* is a decimal integer defining the minimum field ' 'width. If not\n' 'specified, then the field width will be determined by the ' @@ -5361,14 +5457,14 @@ topics = {'assert': '\n' 'is\n' 'present, it is initialized to a tuple receiving any excess ' 'positional\n' - 'parameters, defaulting to the empty tuple. If the form\n' - '""**identifier"" is present, it is initialized to a new ' - 'dictionary\n' - 'receiving any excess keyword arguments, defaulting to a new ' - 'empty\n' - 'dictionary. Parameters after ""*"" or ""*identifier"" are ' - 'keyword-only\n' - 'parameters and may only be passed used keyword arguments.\n' + 'parameters, defaulting to the empty tuple. If the form\n' + '""**identifier"" is present, it is initialized to a new ordered\n' + 'mapping receiving any excess keyword arguments, defaulting to a ' + 'new\n' + 'empty mapping of the same type. Parameters after ""*"" or\n' + '""*identifier"" are keyword-only parameters and may only be ' + 'passed\n' + 'used keyword arguments.\n' '\n' 'Parameters may have annotations of the form "": expression"" ' 'following\n' @@ -5441,11 +5537,12 @@ topics = {'assert': '\n' 'Names listed in a "global" statement must not be defined as ' 'formal\n' 'parameters or in a "for" loop control target, "class" definition,\n' - 'function definition, or "import" statement.\n' + 'function definition, "import" statement, or variable annotation.\n' '\n' '**CPython implementation detail:** The current implementation does ' 'not\n' - 'enforce the two restrictions, but programs should not abuse this\n' + 'enforce some of these restriction, but programs should not abuse ' + 'this\n' 'freedom, as future implementations may enforce them or silently ' 'change\n' 'the meaning of the program.\n' @@ -5685,7 +5782,7 @@ topics = {'assert': '\n' 'Imaginary literals are described by the following lexical ' 'definitions:\n' '\n' - ' imagnumber ::= (floatnumber | intpart) ("j" | "J")\n' + ' imagnumber ::= (floatnumber | digitpart) ("j" | "J")\n' '\n' 'An imaginary literal yields a complex number with a real part ' 'of 0.0.\n' @@ -5697,7 +5794,8 @@ topics = {'assert': '\n' 'it,\n' 'e.g., "(3+4j)". Some examples of imaginary literals:\n' '\n' - ' 3.14j 10.j 10j .001j 1e100j 3.14e-10j\n', + ' 3.14j 10.j 10j .001j 1e100j 3.14e-10j ' + '3.14_15_93j\n', 'import': '\n' 'The "import" statement\n' '**********************\n' @@ -6003,22 +6101,31 @@ topics = {'assert': '\n' 'Integer literals are described by the following lexical ' 'definitions:\n' '\n' - ' integer ::= decimalinteger | octinteger | hexinteger | ' - 'bininteger\n' - ' decimalinteger ::= nonzerodigit digit* | "0"+\n' - ' nonzerodigit ::= "1"..."9"\n' - ' digit ::= "0"..."9"\n' - ' octinteger ::= "0" ("o" | "O") octdigit+\n' - ' hexinteger ::= "0" ("x" | "X") hexdigit+\n' - ' bininteger ::= "0" ("b" | "B") bindigit+\n' - ' octdigit ::= "0"..."7"\n' - ' hexdigit ::= digit | "a"..."f" | "A"..."F"\n' - ' bindigit ::= "0" | "1"\n' + ' integer ::= decinteger | bininteger | octinteger | ' + 'hexinteger\n' + ' decinteger ::= nonzerodigit (["_"] digit)* | "0"+ (["_"] ' + '"0")*\n' + ' bininteger ::= "0" ("b" | "B") (["_"] bindigit)+\n' + ' octinteger ::= "0" ("o" | "O") (["_"] octdigit)+\n' + ' hexinteger ::= "0" ("x" | "X") (["_"] hexdigit)+\n' + ' nonzerodigit ::= "1"..."9"\n' + ' digit ::= "0"..."9"\n' + ' bindigit ::= "0" | "1"\n' + ' octdigit ::= "0"..."7"\n' + ' hexdigit ::= digit | "a"..."f" | "A"..."F"\n' '\n' 'There is no limit for the length of integer literals apart from ' 'what\n' 'can be stored in available memory.\n' '\n' + 'Underscores are ignored for determining the numeric value of ' + 'the\n' + 'literal. They can be used to group digits for enhanced ' + 'readability.\n' + 'One underscore can occur between digits, and after base ' + 'specifiers\n' + 'like "0x".\n' + '\n' 'Note that leading zeros in a non-zero decimal number are not ' 'allowed.\n' 'This is for disambiguation with C-style octal literals, which ' @@ -6028,7 +6135,12 @@ topics = {'assert': '\n' 'Some examples of integer literals:\n' '\n' ' 7 2147483647 0o177 0b100110111\n' - ' 3 79228162514264337593543950336 0o377 0xdeadbeef\n', + ' 3 79228162514264337593543950336 0o377 0xdeadbeef\n' + ' 100_000_000_000 0b_1110_0101\n' + '\n' + 'Changed in version 3.6: Underscores are now allowed for ' + 'grouping\n' + 'purposes in literals.\n', 'lambda': '\n' 'Lambdas\n' '*******\n' @@ -6406,9 +6518,9 @@ topics = {'assert': '\n' '(swapped)\n' ' operands. These functions are only called if the left ' 'operand does\n' - ' not support the corresponding operation and the operands ' - 'are of\n' - ' different types. [2] For instance, to evaluate the ' + ' not support the corresponding operation [3] and the ' + 'operands are of\n' + ' different types. [4] For instance, to evaluate the ' 'expression "x -\n' ' y", where *y* is an instance of a class that has an ' '"__rsub__()"\n' @@ -7384,6 +7496,15 @@ topics = {'assert': '\n' 'exception when no appropriate method is defined (typically\n' '"AttributeError" or "TypeError").\n' '\n' + 'Setting a special method to "None" indicates that the ' + 'corresponding\n' + 'operation is not available. For example, if a class sets ' + '"__iter__()"\n' + 'to "None", the class is not iterable, so calling "iter()" on ' + 'its\n' + 'instances will raise a "TypeError" (without falling back to\n' + '"__getitem__()"). [2]\n' + '\n' 'When implementing a class that emulates any built-in type, ' 'it is\n' 'important that the emulation only be implemented to the ' @@ -7463,7 +7584,7 @@ topics = {'assert': '\n' ' Because "__new__()" and "__init__()" work together in ' 'constructing\n' ' objects ("__new__()" to create it, and "__init__()" to ' - 'customise\n' + 'customize\n' ' it), no non-"None" value may be returned by "__init__()"; ' 'doing so\n' ' will cause a "TypeError" to be raised at runtime.\n' @@ -8272,7 +8393,7 @@ topics = {'assert': '\n' 'locally to the\n' 'result of "type(name, bases, namespace)".\n' '\n' - 'The class creation process can be customised by passing the\n' + 'The class creation process can be customized by passing the\n' '"metaclass" keyword argument in the class definition line, ' 'or by\n' 'inheriting from an existing class that included such an ' @@ -8355,7 +8476,7 @@ topics = {'assert': '\n' '\n' 'If the metaclass has no "__prepare__" attribute, then the ' 'class\n' - 'namespace is initialised as an empty "dict()" instance.\n' + 'namespace is initialised as an empty ordered mapping.\n' '\n' 'See also:\n' '\n' @@ -8423,11 +8544,12 @@ topics = {'assert': '\n' '\n' 'When a new class is created by "type.__new__", the object ' 'provided as\n' - 'the namespace parameter is copied to a standard Python ' - 'dictionary and\n' - 'the original object is discarded. The new copy becomes the ' - '"__dict__"\n' - 'attribute of the class object.\n' + 'the namespace parameter is copied to a new ordered mapping ' + 'and the\n' + 'original object is discarded. The new copy is wrapped in a ' + 'read-only\n' + 'proxy, which becomes the "__dict__" attribute of the class ' + 'object.\n' '\n' 'See also:\n' '\n' @@ -8849,9 +8971,9 @@ topics = {'assert': '\n' '(swapped)\n' ' operands. These functions are only called if the left ' 'operand does\n' - ' not support the corresponding operation and the operands ' - 'are of\n' - ' different types. [2] For instance, to evaluate the ' + ' not support the corresponding operation [3] and the ' + 'operands are of\n' + ' different types. [4] For instance, to evaluate the ' 'expression "x -\n' ' y", where *y* is an instance of a class that has an ' '"__rsub__()"\n' @@ -10121,6 +10243,12 @@ topics = {'assert': '\n' 'bytes\n' 'literals.\n' '\n' + ' Changed in version 3.6: Unrecognized escape sequences produce ' + 'a\n' + ' DeprecationWarning. In some future version of Python they ' + 'will be\n' + ' a SyntaxError.\n' + '\n' 'Even in a raw literal, quotes can be escaped with a backslash, ' 'but the\n' 'backslash remains in the result; for example, "r"\\""" is a ' @@ -10995,6 +11123,21 @@ topics = {'assert': '\n' " Attribute assignment updates the module's namespace dictionary,\n" ' e.g., "m.x = 1" is equivalent to "m.__dict__["x"] = 1".\n' '\n' + ' Predefined (writable) attributes: "__name__" is the module\'s ' + 'name;\n' + ' "__doc__" is the module\'s documentation string, or "None" if\n' + ' unavailable; "__annotations__" (optional) is a dictionary\n' + ' containing *variable annotations* collected during module body\n' + ' execution; "__file__" is the pathname of the file from which ' + 'the\n' + ' module was loaded, if it was loaded from a file. The "__file__"\n' + ' attribute may be missing for certain types of modules, such as ' + 'C\n' + ' modules that are statically linked into the interpreter; for\n' + ' extension modules loaded dynamically from a shared library, it ' + 'is\n' + ' the pathname of the shared library file.\n' + '\n' ' Special read-only attribute: "__dict__" is the module\'s ' 'namespace\n' ' as a dictionary object.\n' @@ -11008,19 +11151,6 @@ topics = {'assert': '\n' 'the\n' ' module around while using its dictionary directly.\n' '\n' - ' Predefined (writable) attributes: "__name__" is the module\'s ' - 'name;\n' - ' "__doc__" is the module\'s documentation string, or "None" if\n' - ' unavailable; "__file__" is the pathname of the file from which ' - 'the\n' - ' module was loaded, if it was loaded from a file. The "__file__"\n' - ' attribute may be missing for certain types of modules, such as ' - 'C\n' - ' modules that are statically linked into the interpreter; for\n' - ' extension modules loaded dynamically from a shared library, it ' - 'is\n' - ' the pathname of the shared library file.\n' - '\n' 'Custom classes\n' ' Custom class types are typically created by class definitions ' '(see\n' @@ -11074,7 +11204,10 @@ topics = {'assert': '\n' 'the\n' ' order of their occurrence in the base class list; "__doc__" is ' 'the\n' - " class's documentation string, or None if undefined.\n" + " class's documentation string, or None if undefined;\n" + ' "__annotations__" (optional) is a dictionary containing ' + '*variable\n' + ' annotations* collected during class body execution.\n' '\n' 'Class instances\n' ' A class instance is created by calling a class object (see ' @@ -12512,7 +12645,13 @@ topics = {'assert': '\n' 'comparing\n' 'based on object identity).\n' '\n' - 'New in version 3.3: The "start", "stop" and "step" attributes.\n', + 'New in version 3.3: The "start", "stop" and "step" attributes.\n' + '\n' + 'See also:\n' + '\n' + ' * The linspace recipe shows how to implement a lazy version ' + 'of\n' + ' range that suitable for floating point applications.\n', 'typesseq-mutable': '\n' 'Mutable Sequence Types\n' '**********************\n' -- cgit v1.2.1 From 11b4c90a54eab8b1aa7ef03570734ca73312c462 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 13 Sep 2016 16:56:38 +0200 Subject: Fix _PyDict_Pop() on pending key Issue #28120: Fix dict.pop() for splitted dictionary when trying to remove a "pending key" (Not yet inserted in split-table). Patch by Xiang Zhang. --- Lib/test/test_dict.py | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py index fb954c8132..ed66ddbcb4 100644 --- a/Lib/test/test_dict.py +++ b/Lib/test/test_dict.py @@ -891,6 +891,15 @@ class DictTest(unittest.TestCase): self.assertEqual(list(a), ['x', 'z', 'y']) self.assertEqual(list(b), ['x', 'y', 'z']) + @support.cpython_only + def test_splittable_pop_pending(self): + """pop a pending key in a splitted table should not crash""" + a, b = self.make_shared_key_dict(2) + + a['a'] = 4 + with self.assertRaises(KeyError): + b.pop('a') + @support.cpython_only def test_splittable_popitem(self): """split table must be combined when d.popitem()""" -- cgit v1.2.1 From ea907a5fdf5fee1b52e2ab55aa6320ca74e80361 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 14 Sep 2016 10:57:00 +0200 Subject: Issue #28114: Add unit tests on os.spawn*() --- Lib/test/test_os.py | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 95c74824d7..d82a4a7f78 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -1413,6 +1413,7 @@ def _execvpe_mockup(defpath=None): os.execve = orig_execve os.defpath = orig_defpath + class ExecTests(unittest.TestCase): @unittest.skipIf(USING_LINUXTHREADS, "avoid triggering a linuxthreads bug: see issue #4970") @@ -2163,6 +2164,91 @@ class PidTests(unittest.TestCase): self.assertEqual(status, (pid, 0)) +class SpawnTests(unittest.TestCase): + def create_args(self, with_env=False): + self.exitcode = 17 + + filename = support.TESTFN + self.addCleanup(support.unlink, filename) + + if not with_env: + code = 'import sys; sys.exit(%s)' % self.exitcode + else: + self.env = dict(os.environ) + # create an unique key + self.key = str(uuid.uuid4()) + self.env[self.key] = self.key + # read the variable from os.environ to check that it exists + code = ('import sys, os; magic = os.environ[%r]; sys.exit(%s)' + % (self.key, self.exitcode)) + + with open(filename, "w") as fp: + fp.write(code) + + return [sys.executable, filename] + + @unittest.skipUnless(hasattr(os, 'spawnl'), 'need os.spawnl') + def test_spawnl(self): + args = self.create_args() + exitcode = os.spawnl(os.P_WAIT, args[0], *args) + self.assertEqual(exitcode, self.exitcode) + + @unittest.skipUnless(hasattr(os, 'spawnle'), 'need os.spawnle') + def test_spawnle(self): + args = self.create_args(True) + exitcode = os.spawnle(os.P_WAIT, args[0], *args, self.env) + self.assertEqual(exitcode, self.exitcode) + + @unittest.skipUnless(hasattr(os, 'spawnlp'), 'need os.spawnlp') + def test_spawnlp(self): + args = self.create_args() + exitcode = os.spawnlp(os.P_WAIT, args[0], *args) + self.assertEqual(exitcode, self.exitcode) + + @unittest.skipUnless(hasattr(os, 'spawnlpe'), 'need os.spawnlpe') + def test_spawnlpe(self): + args = self.create_args(True) + exitcode = os.spawnlpe(os.P_WAIT, args[0], *args, self.env) + self.assertEqual(exitcode, self.exitcode) + + @unittest.skipUnless(hasattr(os, 'spawnv'), 'need os.spawnv') + def test_spawnv(self): + args = self.create_args() + exitcode = os.spawnv(os.P_WAIT, args[0], args) + self.assertEqual(exitcode, self.exitcode) + + @unittest.skipUnless(hasattr(os, 'spawnve'), 'need os.spawnve') + def test_spawnve(self): + args = self.create_args(True) + exitcode = os.spawnve(os.P_WAIT, args[0], args, self.env) + self.assertEqual(exitcode, self.exitcode) + + @unittest.skipUnless(hasattr(os, 'spawnvp'), 'need os.spawnvp') + def test_spawnvp(self): + args = self.create_args() + exitcode = os.spawnvp(os.P_WAIT, args[0], args) + self.assertEqual(exitcode, self.exitcode) + + @unittest.skipUnless(hasattr(os, 'spawnvpe'), 'need os.spawnvpe') + def test_spawnvpe(self): + args = self.create_args(True) + exitcode = os.spawnvpe(os.P_WAIT, args[0], args, self.env) + self.assertEqual(exitcode, self.exitcode) + + @unittest.skipUnless(hasattr(os, 'spawnv'), 'need os.spawnv') + def test_nowait(self): + args = self.create_args() + pid = os.spawnv(os.P_NOWAIT, args[0], args) + result = os.waitpid(pid, 0) + self.assertEqual(result[0], pid) + status = result[1] + if hasattr(os, 'WIFEXITED'): + self.assertTrue(os.WIFEXITED(status)) + self.assertEqual(os.WEXITSTATUS(status), self.exitcode) + else: + self.assertEqual(status, self.exitcode << 8) + + # The introduction of this TestCase caused at least two different errors on # *nix buildbots. Temporarily skip this to let the buildbots move along. @unittest.skip("Skip due to platform/environment differences on *NIX buildbots") -- cgit v1.2.1 From d734fb01a2767352005604619186787d4fad1810 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Wed, 14 Sep 2016 23:53:47 -0700 Subject: Unicode 9.0.0 Not completely mechanical since support for East Asian Width changes?emoji codepoints became Wide?had to be added to unicodedata. --- Lib/test/test_unicodedata.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_unicodedata.py b/Lib/test/test_unicodedata.py index 6ecc91362d..4fc11ec32f 100644 --- a/Lib/test/test_unicodedata.py +++ b/Lib/test/test_unicodedata.py @@ -20,7 +20,7 @@ errors = 'surrogatepass' class UnicodeMethodsTest(unittest.TestCase): # update this, if the database changes - expectedchecksum = '5971760872b2f98bb9c701e6c0db3273d756b3ec' + expectedchecksum = 'c1fa98674a683aa8a8d8dee0c84494f8d36346e6' def test_method_checksum(self): h = hashlib.sha1() @@ -80,7 +80,7 @@ class UnicodeFunctionsTest(UnicodeDatabaseTest): # Update this if the database changes. Make sure to do a full rebuild # (e.g. 'make distclean && make') to get the correct checksum. - expectedchecksum = '5e74827cd07f9e546a30f34b7bcf6cc2eac38c8c' + expectedchecksum = 'f891b1e6430c712531b9bc935a38e22d78ba1bf3' def test_function_checksum(self): data = [] h = hashlib.sha1() @@ -222,6 +222,10 @@ class UnicodeFunctionsTest(UnicodeDatabaseTest): self.assertEqual(eaw('\u2010'), 'A') self.assertEqual(eaw('\U00020000'), 'W') + def test_east_asian_width_9_0_changes(self): + self.assertEqual(self.db.ucd_3_2_0.east_asian_width('\u231a'), 'N') + self.assertEqual(self.db.east_asian_width('\u231a'), 'W') + class UnicodeMiscTest(UnicodeDatabaseTest): def test_failed_import_during_compiling(self): -- cgit v1.2.1 From 75a546e4742311e7ed442e47410c55e57de71fd6 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Thu, 15 Sep 2016 12:50:23 -0400 Subject: Issue #26182: Raise DeprecationWarning for improper use of async/await keywords --- Lib/asyncio/tasks.py | 7 ++- Lib/test/test_coroutines.py | 137 ++++++++++++++++++++++++++++++-------------- Lib/test/test_grammar.py | 12 ---- Lib/xml/dom/xmlbuilder.py | 6 +- 4 files changed, 104 insertions(+), 58 deletions(-) (limited to 'Lib') diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py index 35c945c436..4c66546428 100644 --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -519,7 +519,7 @@ def sleep(delay, result=None, *, loop=None): h.cancel() -def async(coro_or_future, *, loop=None): +def async_(coro_or_future, *, loop=None): """Wrap a coroutine in a future. If the argument is a Future, it is returned directly. @@ -532,6 +532,11 @@ def async(coro_or_future, *, loop=None): return ensure_future(coro_or_future, loop=loop) +# Silence DeprecationWarning: +globals()['async'] = async_ +async_.__name__ = 'async' +del async_ + def ensure_future(coro_or_future, *, loop=None): """Wrap a coroutine or an awaitable in a future. diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py index 154ce7fdee..f2839a719a 100644 --- a/Lib/test/test_coroutines.py +++ b/Lib/test/test_coroutines.py @@ -358,57 +358,110 @@ class AsyncBadSyntaxTest(unittest.TestCase): with self.subTest(code=code), self.assertRaises(SyntaxError): compile(code, "", "exec") - def test_goodsyntax_1(self): - # Tests for issue 24619 + def test_badsyntax_2(self): + samples = [ + """def foo(): + await = 1 + """, + + """class Bar: + def async(): pass + """, - def foo(await): - async def foo(): pass - async def foo(): + """class Bar: + async = 1 + """, + + """class async: pass - return await + 1 - self.assertEqual(foo(10), 11) + """, - def foo(await): - async def foo(): pass - async def foo(): pass - return await + 2 - self.assertEqual(foo(20), 22) + """class await: + pass + """, - def foo(await): + """import math as await""", - async def foo(): pass + """def async(): + pass""", - async def foo(): pass + """def foo(*, await=1): + pass""" - return await + 2 - self.assertEqual(foo(20), 22) + """async = 1""", - def foo(await): - """spam""" - async def foo(): \ - pass - # 123 - async def foo(): pass - # 456 - return await + 2 - self.assertEqual(foo(20), 22) - - def foo(await): - def foo(): pass - def foo(): pass - async def bar(): return await_ - await_ = await - try: - bar().send(None) - except StopIteration as ex: - return ex.args[0] - self.assertEqual(foo(42), 42) + """print(await=1)""" + ] - async def f(): - async def g(): pass - await z - await = 1 - self.assertTrue(inspect.iscoroutinefunction(f)) + for code in samples: + with self.subTest(code=code), self.assertWarnsRegex( + DeprecationWarning, + "'await' will become reserved keywords"): + compile(code, "", "exec") + + def test_badsyntax_3(self): + with self.assertRaises(DeprecationWarning): + with warnings.catch_warnings(): + warnings.simplefilter("error") + compile("async = 1", "", "exec") + + def test_goodsyntax_1(self): + # Tests for issue 24619 + + samples = [ + '''def foo(await): + async def foo(): pass + async def foo(): + pass + return await + 1 + ''', + + '''def foo(await): + async def foo(): pass + async def foo(): pass + return await + 1 + ''', + + '''def foo(await): + + async def foo(): pass + + async def foo(): pass + + return await + 1 + ''', + + '''def foo(await): + """spam""" + async def foo(): \ + pass + # 123 + async def foo(): pass + # 456 + return await + 1 + ''', + + '''def foo(await): + def foo(): pass + def foo(): pass + async def bar(): return await_ + await_ = await + try: + bar().send(None) + except StopIteration as ex: + return ex.args[0] + 1 + ''' + ] + + for code in samples: + with self.subTest(code=code): + loc = {} + + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + exec(code, loc, loc) + + self.assertEqual(loc['foo'](10), 11) class TokenizerRegrTest(unittest.TestCase): diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py index 67a61d4ab5..03f2c84211 100644 --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -1345,18 +1345,6 @@ class GrammarTests(unittest.TestCase): self.assertEqual(m.other, 42) def test_async_await(self): - async = 1 - await = 2 - self.assertEqual(async, 1) - - def async(): - nonlocal await - await = 10 - async() - self.assertEqual(await, 10) - - self.assertFalse(bool(async.__code__.co_flags & inspect.CO_COROUTINE)) - async def test(): def sum(): pass diff --git a/Lib/xml/dom/xmlbuilder.py b/Lib/xml/dom/xmlbuilder.py index 444f0b2a57..e9a1536472 100644 --- a/Lib/xml/dom/xmlbuilder.py +++ b/Lib/xml/dom/xmlbuilder.py @@ -353,14 +353,14 @@ class _AsyncDeprecatedProperty: class DocumentLS: """Mixin to create documents that conform to the load/save spec.""" - async = _AsyncDeprecatedProperty() async_ = False + locals()['async'] = _AsyncDeprecatedProperty() # Avoid DeprecationWarning def _get_async(self): return False - def _set_async(self, async): - if async: + def _set_async(self, flag): + if flag: raise xml.dom.NotSupportedErr( "asynchronous document loading is not supported") -- cgit v1.2.1 From 4f3920fccc1a41608c9615ec78038232b0382f8f Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Thu, 15 Sep 2016 20:19:47 +0300 Subject: Issue #28114: Fix a crash in parse_envlist() when env contains byte strings Patch by Eryk Sun. --- Lib/test/test_os.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index d82a4a7f78..9096665b6c 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2165,7 +2165,7 @@ class PidTests(unittest.TestCase): class SpawnTests(unittest.TestCase): - def create_args(self, with_env=False): + def create_args(self, with_env=False, use_bytes=False): self.exitcode = 17 filename = support.TESTFN @@ -2185,7 +2185,13 @@ class SpawnTests(unittest.TestCase): with open(filename, "w") as fp: fp.write(code) - return [sys.executable, filename] + args = [sys.executable, filename] + if use_bytes: + args = [os.fsencode(a) for a in args] + self.env = {os.fsencode(k): os.fsencode(v) + for k, v in self.env.items()} + + return args @unittest.skipUnless(hasattr(os, 'spawnl'), 'need os.spawnl') def test_spawnl(self): @@ -2248,6 +2254,13 @@ class SpawnTests(unittest.TestCase): else: self.assertEqual(status, self.exitcode << 8) + @unittest.skipUnless(hasattr(os, 'spawnve'), 'need os.spawnve') + def test_spawnve_bytes(self): + # Test bytes handling in parse_arglist and parse_envlist (#28114) + args = self.create_args(with_env=True, use_bytes=True) + exitcode = os.spawnve(os.P_WAIT, args[0], args, self.env) + self.assertEqual(exitcode, self.exitcode) + # The introduction of this TestCase caused at least two different errors on # *nix buildbots. Temporarily skip this to let the buildbots move along. -- cgit v1.2.1 From 24c130f08c2e2a5fcc4dbdb06a368ba9d0e7f1ea Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Thu, 15 Sep 2016 20:23:55 +0300 Subject: Make SpawnTest.create_args() keyword-only --- Lib/test/test_os.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 9096665b6c..3391e1e368 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2165,7 +2165,7 @@ class PidTests(unittest.TestCase): class SpawnTests(unittest.TestCase): - def create_args(self, with_env=False, use_bytes=False): + def create_args(self, *, with_env=False, use_bytes=False): self.exitcode = 17 filename = support.TESTFN @@ -2201,7 +2201,7 @@ class SpawnTests(unittest.TestCase): @unittest.skipUnless(hasattr(os, 'spawnle'), 'need os.spawnle') def test_spawnle(self): - args = self.create_args(True) + args = self.create_args(with_env=True) exitcode = os.spawnle(os.P_WAIT, args[0], *args, self.env) self.assertEqual(exitcode, self.exitcode) @@ -2213,7 +2213,7 @@ class SpawnTests(unittest.TestCase): @unittest.skipUnless(hasattr(os, 'spawnlpe'), 'need os.spawnlpe') def test_spawnlpe(self): - args = self.create_args(True) + args = self.create_args(with_env=True) exitcode = os.spawnlpe(os.P_WAIT, args[0], *args, self.env) self.assertEqual(exitcode, self.exitcode) @@ -2225,7 +2225,7 @@ class SpawnTests(unittest.TestCase): @unittest.skipUnless(hasattr(os, 'spawnve'), 'need os.spawnve') def test_spawnve(self): - args = self.create_args(True) + args = self.create_args(with_env=True) exitcode = os.spawnve(os.P_WAIT, args[0], args, self.env) self.assertEqual(exitcode, self.exitcode) @@ -2237,7 +2237,7 @@ class SpawnTests(unittest.TestCase): @unittest.skipUnless(hasattr(os, 'spawnvpe'), 'need os.spawnvpe') def test_spawnvpe(self): - args = self.create_args(True) + args = self.create_args(with_env=True) exitcode = os.spawnvpe(os.P_WAIT, args[0], args, self.env) self.assertEqual(exitcode, self.exitcode) -- cgit v1.2.1 From 7ae56e9058eb9fc24a8d203f913be6e8ae7e6823 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Thu, 15 Sep 2016 13:24:03 -0400 Subject: Merge 3.5 (asyncio) --- Lib/asyncio/base_events.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'Lib') diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index 9c2fa12486..154fd95513 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -242,9 +242,13 @@ class BaseEventLoop(events.AbstractEventLoop): self._task_factory = None self._coroutine_wrapper_set = False - # A weak set of all asynchronous generators that are being iterated - # by the loop. - self._asyncgens = weakref.WeakSet() + if hasattr(sys, 'get_asyncgen_hooks'): + # Python >= 3.6 + # A weak set of all asynchronous generators that are + # being iterated by the loop. + self._asyncgens = weakref.WeakSet() + else: + self._asyncgens = None # Set to True when `loop.shutdown_asyncgens` is called. self._asyncgens_shutdown_called = False @@ -359,7 +363,9 @@ class BaseEventLoop(events.AbstractEventLoop): """Shutdown all active asynchronous generators.""" self._asyncgens_shutdown_called = True - if not len(self._asyncgens): + if self._asyncgens is None or not len(self._asyncgens): + # If Python version is <3.6 or we don't have any asynchronous + # generators alive. return closing_agens = list(self._asyncgens) @@ -387,9 +393,10 @@ class BaseEventLoop(events.AbstractEventLoop): raise RuntimeError('Event loop is running.') self._set_coroutine_wrapper(self._debug) self._thread_id = threading.get_ident() - old_agen_hooks = sys.get_asyncgen_hooks() - sys.set_asyncgen_hooks(firstiter=self._asyncgen_firstiter_hook, - finalizer=self._asyncgen_finalizer_hook) + if self._asyncgens is not None: + old_agen_hooks = sys.get_asyncgen_hooks() + sys.set_asyncgen_hooks(firstiter=self._asyncgen_firstiter_hook, + finalizer=self._asyncgen_finalizer_hook) try: while True: self._run_once() @@ -399,7 +406,8 @@ class BaseEventLoop(events.AbstractEventLoop): self._stopping = False self._thread_id = None self._set_coroutine_wrapper(False) - sys.set_asyncgen_hooks(*old_agen_hooks) + if self._asyncgens is not None: + sys.set_asyncgen_hooks(*old_agen_hooks) def run_until_complete(self, future): """Run until the Future is done. -- cgit v1.2.1 From a55c6ea7f19f627718b33c66ef5f5f4075d4ab42 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Thu, 15 Sep 2016 20:32:44 +0300 Subject: Use requires_os_func() to skip SpawnTests --- Lib/test/test_os.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 3391e1e368..afeefcb093 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -90,6 +90,10 @@ def ignore_deprecation_warnings(msg_regex, quiet=False): yield +def requires_os_func(name): + return unittest.skipUnless(hasattr(os, name), 'requires os.%s' % name) + + class _PathLike(os.PathLike): def __init__(self, path=""): @@ -2193,55 +2197,55 @@ class SpawnTests(unittest.TestCase): return args - @unittest.skipUnless(hasattr(os, 'spawnl'), 'need os.spawnl') + @requires_os_func('spawnl') def test_spawnl(self): args = self.create_args() exitcode = os.spawnl(os.P_WAIT, args[0], *args) self.assertEqual(exitcode, self.exitcode) - @unittest.skipUnless(hasattr(os, 'spawnle'), 'need os.spawnle') + @requires_os_func('spawnle') def test_spawnle(self): args = self.create_args(with_env=True) exitcode = os.spawnle(os.P_WAIT, args[0], *args, self.env) self.assertEqual(exitcode, self.exitcode) - @unittest.skipUnless(hasattr(os, 'spawnlp'), 'need os.spawnlp') + @requires_os_func('spawnlp') def test_spawnlp(self): args = self.create_args() exitcode = os.spawnlp(os.P_WAIT, args[0], *args) self.assertEqual(exitcode, self.exitcode) - @unittest.skipUnless(hasattr(os, 'spawnlpe'), 'need os.spawnlpe') + @requires_os_func('spawnlpe') def test_spawnlpe(self): args = self.create_args(with_env=True) exitcode = os.spawnlpe(os.P_WAIT, args[0], *args, self.env) self.assertEqual(exitcode, self.exitcode) - @unittest.skipUnless(hasattr(os, 'spawnv'), 'need os.spawnv') + @requires_os_func('spawnv') def test_spawnv(self): args = self.create_args() exitcode = os.spawnv(os.P_WAIT, args[0], args) self.assertEqual(exitcode, self.exitcode) - @unittest.skipUnless(hasattr(os, 'spawnve'), 'need os.spawnve') + @requires_os_func('spawnve') def test_spawnve(self): args = self.create_args(with_env=True) exitcode = os.spawnve(os.P_WAIT, args[0], args, self.env) self.assertEqual(exitcode, self.exitcode) - @unittest.skipUnless(hasattr(os, 'spawnvp'), 'need os.spawnvp') + @requires_os_func('spawnvp') def test_spawnvp(self): args = self.create_args() exitcode = os.spawnvp(os.P_WAIT, args[0], args) self.assertEqual(exitcode, self.exitcode) - @unittest.skipUnless(hasattr(os, 'spawnvpe'), 'need os.spawnvpe') + @requires_os_func('spawnvpe') def test_spawnvpe(self): args = self.create_args(with_env=True) exitcode = os.spawnvpe(os.P_WAIT, args[0], args, self.env) self.assertEqual(exitcode, self.exitcode) - @unittest.skipUnless(hasattr(os, 'spawnv'), 'need os.spawnv') + @requires_os_func('spawnv') def test_nowait(self): args = self.create_args() pid = os.spawnv(os.P_NOWAIT, args[0], args) @@ -2254,7 +2258,7 @@ class SpawnTests(unittest.TestCase): else: self.assertEqual(status, self.exitcode << 8) - @unittest.skipUnless(hasattr(os, 'spawnve'), 'need os.spawnve') + @requires_os_func('spawnve') def test_spawnve_bytes(self): # Test bytes handling in parse_arglist and parse_envlist (#28114) args = self.create_args(with_env=True, use_bytes=True) -- cgit v1.2.1 From 0bdece6e2f7a8b82144a8e2bf52b2c2eaa40dd6d Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Thu, 15 Sep 2016 13:35:41 -0400 Subject: asyncio: Drop debug code --- Lib/asyncio/coroutines.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'Lib') diff --git a/Lib/asyncio/coroutines.py b/Lib/asyncio/coroutines.py index d92f67d590..72ffb44e95 100644 --- a/Lib/asyncio/coroutines.py +++ b/Lib/asyncio/coroutines.py @@ -276,10 +276,7 @@ def _format_coroutine(coro): try: coro_code = coro.gi_code except AttributeError: - try: - coro_code = coro.cr_code - except AttributeError: - return repr(coro) + coro_code = coro.cr_code try: coro_frame = coro.gi_frame -- cgit v1.2.1 From 7e6c9ee7cf3302d359264b6df1258b46bac0f3c1 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 17 Sep 2016 01:29:58 +0300 Subject: Issue #22493: Warning message emitted by using inline flags in the middle of regular expression now contains a (truncated) regex pattern. Patch by Tim Graham. --- Lib/sre_parse.py | 9 +++++++-- Lib/test/test_re.py | 17 +++++++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) (limited to 'Lib') diff --git a/Lib/sre_parse.py b/Lib/sre_parse.py index 4a77f0c9a7..3d38673c32 100644 --- a/Lib/sre_parse.py +++ b/Lib/sre_parse.py @@ -735,8 +735,13 @@ def _parse(source, state, verbose): if flags is None: # global flags if pos != 3: # "(?x" import warnings - warnings.warn('Flags not at the start of the expression', - DeprecationWarning, stacklevel=7) + warnings.warn( + 'Flags not at the start of the expression %s%s' % ( + source.string[:20], # truncate long regexes + ' (truncated)' if len(source.string) > 20 else '', + ), + DeprecationWarning, stacklevel=7 + ) continue add_flags, del_flags = flags group = None diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index eb1aba39c3..6803e02c0e 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -1323,8 +1323,21 @@ class ReTests(unittest.TestCase): self.assertTrue(re.match('(?ixu) ' + upper_char, lower_char)) self.assertTrue(re.match('(?ixu) ' + lower_char, upper_char)) - with self.assertWarns(DeprecationWarning): - self.assertTrue(re.match(upper_char + '(?i)', lower_char)) + p = upper_char + '(?i)' + with self.assertWarns(DeprecationWarning) as warns: + self.assertTrue(re.match(p, lower_char)) + self.assertEqual( + str(warns.warnings[0].message), + 'Flags not at the start of the expression %s' % p + ) + + p = upper_char + '(?i)%s' % ('.?' * 100) + with self.assertWarns(DeprecationWarning) as warns: + self.assertTrue(re.match(p, lower_char)) + self.assertEqual( + str(warns.warnings[0].message), + 'Flags not at the start of the expression %s (truncated)' % p[:20] + ) def test_dollar_matches_twice(self): "$ matches the end of string, and just before the terminating \n" -- cgit v1.2.1 From 8d9c31e48a0eda1a5f16fca31aebd970074fcd27 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Sat, 17 Sep 2016 12:22:41 -0700 Subject: Issue #28192: Don't import readline in isolated mode --- Lib/site.py | 12 +++++++----- Lib/test/test_site.py | 8 ++------ 2 files changed, 9 insertions(+), 11 deletions(-) (limited to 'Lib') diff --git a/Lib/site.py b/Lib/site.py index 5250266755..b6376357ea 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -60,7 +60,8 @@ omitted because it is not mentioned in either path configuration file. The readline module is also automatically configured to enable completion for systems that support it. This can be overridden in -sitecustomize, usercustomize or PYTHONSTARTUP. +sitecustomize, usercustomize or PYTHONSTARTUP. Starting Python in +isolated mode (-I) disables automatic readline configuration. After these operations, an attempt is made to import a module named sitecustomize, which can perform arbitrary additional @@ -491,7 +492,7 @@ def execsitecustomize(): else: raise except Exception as err: - if os.environ.get("PYTHONVERBOSE"): + if sys.flags.verbose: sys.excepthook(*sys.exc_info()) else: sys.stderr.write( @@ -511,7 +512,7 @@ def execusercustomize(): else: raise except Exception as err: - if os.environ.get("PYTHONVERBOSE"): + if sys.flags.verbose: sys.excepthook(*sys.exc_info()) else: sys.stderr.write( @@ -538,12 +539,13 @@ def main(): setquit() setcopyright() sethelper() - enablerlcompleter() + if not sys.flags.isolated: + enablerlcompleter() execsitecustomize() if ENABLE_USER_SITE: execusercustomize() -# Prevent edition of sys.path when python was started with -S and +# Prevent extending of sys.path when python was started with -S and # site is imported later. if not sys.flags.no_site: main() diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index 9afa56eb73..b048648226 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -140,8 +140,6 @@ class HelperFunctionsTests(unittest.TestCase): self.assertRegex(err_out.getvalue(), 'Traceback') self.assertRegex(err_out.getvalue(), 'ModuleNotFoundError') - @unittest.skipIf(sys.platform == "win32", "Windows does not raise an " - "error for file paths containing null characters") def test_addpackage_import_bad_pth_file(self): # Issue 5258 pth_dir, pth_fn = self.make_pth("abc\x00def\n") @@ -447,10 +445,9 @@ class StartupImportTests(unittest.TestCase): popen = subprocess.Popen([sys.executable, '-I', '-v', '-c', 'import sys; print(set(sys.modules))'], stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + stderr=subprocess.PIPE, + encoding='utf-8') stdout, stderr = popen.communicate() - stdout = stdout.decode('utf-8') - stderr = stderr.decode('utf-8') modules = eval(stdout) self.assertIn('site', modules) @@ -474,6 +471,5 @@ class StartupImportTests(unittest.TestCase): if sys.platform != 'darwin': self.assertFalse(modules.intersection(collection_mods), stderr) - if __name__ == "__main__": unittest.main() -- cgit v1.2.1 From ff605386abd423364dac82969699789fd8016aa5 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Sat, 17 Sep 2016 14:35:32 -0700 Subject: Issue #28192: Adds tests for hook in isolated mode --- Lib/test/test_site.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index b048648226..d2fbb7ba2e 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -471,5 +471,23 @@ class StartupImportTests(unittest.TestCase): if sys.platform != 'darwin': self.assertFalse(modules.intersection(collection_mods), stderr) + def test_startup_interactivehook(self): + r = subprocess.Popen([sys.executable, '-c', + 'import sys; sys.exit(hasattr(sys, "__interactivehook__"))']).wait() + self.assertTrue(r, "'__interactivehook__' not added by site") + + def test_startup_interactivehook_isolated(self): + # issue28192 readline is not automatically enabled in isolated mode + r = subprocess.Popen([sys.executable, '-I', '-c', + 'import sys; sys.exit(hasattr(sys, "__interactivehook__"))']).wait() + self.assertFalse(r, "'__interactivehook__' added in isolated mode") + + def test_startup_interactivehook_isolated_explicit(self): + # issue28192 readline can be explicitly enabled in isolated mode + r = subprocess.Popen([sys.executable, '-I', '-c', + 'import site, sys; site.enablerlcompleter(); sys.exit(hasattr(sys, "__interactivehook__"))']).wait() + self.assertTrue(r, "'__interactivehook__' not added by enablerlcompleter()") + + if __name__ == "__main__": unittest.main() -- cgit v1.2.1 From acffd14de543f9752af1ee29c36739c14b05767c Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Sat, 17 Sep 2016 17:27:48 -0700 Subject: Issue #27932: Prevent memory leak in win32_ver(). --- Lib/platform.py | 61 +-------------------------------------------------------- 1 file changed, 1 insertion(+), 60 deletions(-) (limited to 'Lib') diff --git a/Lib/platform.py b/Lib/platform.py index ee315fa319..e48ad0b6e7 100755 --- a/Lib/platform.py +++ b/Lib/platform.py @@ -497,65 +497,6 @@ _WIN32_SERVER_RELEASES = { (6, None): "post2012ServerR2", } -def _get_real_winver(maj, min, build): - if maj < 6 or (maj == 6 and min < 2): - return maj, min, build - - from ctypes import (c_buffer, POINTER, byref, create_unicode_buffer, - Structure, WinDLL) - from ctypes.wintypes import DWORD, HANDLE - - class VS_FIXEDFILEINFO(Structure): - _fields_ = [ - ("dwSignature", DWORD), - ("dwStrucVersion", DWORD), - ("dwFileVersionMS", DWORD), - ("dwFileVersionLS", DWORD), - ("dwProductVersionMS", DWORD), - ("dwProductVersionLS", DWORD), - ("dwFileFlagsMask", DWORD), - ("dwFileFlags", DWORD), - ("dwFileOS", DWORD), - ("dwFileType", DWORD), - ("dwFileSubtype", DWORD), - ("dwFileDateMS", DWORD), - ("dwFileDateLS", DWORD), - ] - - kernel32 = WinDLL('kernel32') - version = WinDLL('version') - - # We will immediately double the length up to MAX_PATH, but the - # path may be longer, so we retry until the returned string is - # shorter than our buffer. - name_len = actual_len = 130 - while actual_len == name_len: - name_len *= 2 - name = create_unicode_buffer(name_len) - actual_len = kernel32.GetModuleFileNameW(HANDLE(kernel32._handle), - name, len(name)) - if not actual_len: - return maj, min, build - - size = version.GetFileVersionInfoSizeW(name, None) - if not size: - return maj, min, build - - ver_block = c_buffer(size) - if (not version.GetFileVersionInfoW(name, None, size, ver_block) or - not ver_block): - return maj, min, build - - pvi = POINTER(VS_FIXEDFILEINFO)() - if not version.VerQueryValueW(ver_block, "", byref(pvi), byref(DWORD())): - return maj, min, build - - maj = pvi.contents.dwProductVersionMS >> 16 - min = pvi.contents.dwProductVersionMS & 0xFFFF - build = pvi.contents.dwProductVersionLS >> 16 - - return maj, min, build - def win32_ver(release='', version='', csd='', ptype=''): try: from sys import getwindowsversion @@ -567,7 +508,7 @@ def win32_ver(release='', version='', csd='', ptype=''): from _winreg import OpenKeyEx, QueryValueEx, CloseKey, HKEY_LOCAL_MACHINE winver = getwindowsversion() - maj, min, build = _get_real_winver(*winver[:3]) + maj, min, build = winver.platform_version or winver[:3] version = '{0}.{1}.{2}'.format(maj, min, build) release = (_WIN32_CLIENT_RELEASES.get((maj, min)) or -- cgit v1.2.1 From 128c263c07d2b6b17b494cfa4a83c71ddc331c8d Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Sun, 18 Sep 2016 11:21:57 +0300 Subject: Issue #28151: Use pythontest.net in test_robotparser --- Lib/test/test_robotparser.py | 43 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_robotparser.py b/Lib/test/test_robotparser.py index d4bf45376a..51b48ce53c 100644 --- a/Lib/test/test_robotparser.py +++ b/Lib/test/test_robotparser.py @@ -1,4 +1,5 @@ import io +import os import unittest import urllib.robotparser from collections import namedtuple @@ -272,14 +273,42 @@ class PasswordProtectedSiteTestCase(unittest.TestCase): class NetworkTestCase(unittest.TestCase): - def testPythonOrg(self): + base_url = 'http://www.pythontest.net/' + robots_txt = '{}elsewhere/robots.txt'.format(base_url) + + @classmethod + def setUpClass(cls): support.requires('network') - with support.transient_internet('www.python.org'): - parser = urllib.robotparser.RobotFileParser( - "http://www.python.org/robots.txt") - parser.read() - self.assertTrue( - parser.can_fetch("*", "http://www.python.org/robots.txt")) + with support.transient_internet(cls.base_url): + cls.parser = urllib.robotparser.RobotFileParser(cls.robots_txt) + cls.parser.read() + + def url(self, path): + return '{}{}{}'.format( + self.base_url, path, '/' if not os.path.splitext(path)[1] else '' + ) + + def test_basic(self): + self.assertFalse(self.parser.disallow_all) + self.assertFalse(self.parser.allow_all) + self.assertGreater(self.parser.mtime(), 0) + self.assertFalse(self.parser.crawl_delay('*')) + self.assertFalse(self.parser.request_rate('*')) + + def test_can_fetch(self): + self.assertTrue(self.parser.can_fetch('*', self.url('elsewhere'))) + self.assertFalse(self.parser.can_fetch('Nutch', self.base_url)) + self.assertFalse(self.parser.can_fetch('Nutch', self.url('brian'))) + self.assertFalse(self.parser.can_fetch('Nutch', self.url('webstats'))) + self.assertFalse(self.parser.can_fetch('*', self.url('webstats'))) + self.assertTrue(self.parser.can_fetch('*', self.base_url)) + + def test_read_404(self): + parser = urllib.robotparser.RobotFileParser(self.url('i-robot.txt')) + parser.read() + self.assertTrue(parser.allow_all) + self.assertFalse(parser.disallow_all) + self.assertEqual(parser.mtime(), 0) if __name__=='__main__': unittest.main() -- cgit v1.2.1 From d7200f1d8929b46547a89adf56c0f7f93dcd275c Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Sun, 18 Sep 2016 20:17:58 +0300 Subject: Issue #25400: RobotFileParser now correctly returns default values for crawl_delay and request_rate Initial patch by Peter Wirtz. --- Lib/test/test_robotparser.py | 54 +++++++++++++++++++++++++++++--------------- Lib/urllib/robotparser.py | 8 +++++-- 2 files changed, 42 insertions(+), 20 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_robotparser.py b/Lib/test/test_robotparser.py index 51b48ce53c..0f64ba8b06 100644 --- a/Lib/test/test_robotparser.py +++ b/Lib/test/test_robotparser.py @@ -79,32 +79,17 @@ Disallow: / bad = ['/cyberworld/map/index.html', '/', '/tmp/'] -class CrawlDelayAndRequestRateTest(BaseRobotTest, unittest.TestCase): - robots_txt = """\ -User-agent: figtree -Crawl-delay: 3 -Request-rate: 9/30 -Disallow: /tmp -Disallow: /a%3cd.html -Disallow: /a%2fb.html -Disallow: /%7ejoe/index.html - """ - agent = 'figtree' - request_rate = namedtuple('req_rate', 'requests seconds')(9, 30) - crawl_delay = 3 - good = [('figtree', '/foo.html')] - bad = ['/tmp', '/tmp.html', '/tmp/a.html', '/a%3cd.html', '/a%3Cd.html', - '/a%2fb.html', '/~joe/index.html'] +class BaseRequestRateTest(BaseRobotTest): def test_request_rate(self): - for url in self.good: + for url in self.good + self.bad: agent, url = self.get_agent_and_url(url) with self.subTest(url=url, agent=agent): if self.crawl_delay: self.assertEqual( self.parser.crawl_delay(agent), self.crawl_delay ) - if self.request_rate and self.parser.request_rate(agent): + if self.request_rate: self.assertEqual( self.parser.request_rate(agent).requests, self.request_rate.requests @@ -115,6 +100,24 @@ Disallow: /%7ejoe/index.html ) +class CrawlDelayAndRequestRateTest(BaseRequestRateTest, unittest.TestCase): + robots_txt = """\ +User-agent: figtree +Crawl-delay: 3 +Request-rate: 9/30 +Disallow: /tmp +Disallow: /a%3cd.html +Disallow: /a%2fb.html +Disallow: /%7ejoe/index.html + """ + agent = 'figtree' + request_rate = namedtuple('req_rate', 'requests seconds')(9, 30) + crawl_delay = 3 + good = [('figtree', '/foo.html')] + bad = ['/tmp', '/tmp.html', '/tmp/a.html', '/a%3cd.html', '/a%3Cd.html', + '/a%2fb.html', '/~joe/index.html'] + + class DifferentAgentTest(CrawlDelayAndRequestRateTest): agent = 'FigTree Robot libwww-perl/5.04' # these are not actually tested, but we still need to parse it @@ -230,6 +233,19 @@ Disallow: /another/path? bad = ['/another/path?'] +class DefaultEntryTest(BaseRequestRateTest, unittest.TestCase): + robots_txt = """\ +User-agent: * +Crawl-delay: 1 +Request-rate: 3/15 +Disallow: /cyberworld/map/ + """ + request_rate = namedtuple('req_rate', 'requests seconds')(3, 15) + crawl_delay = 1 + good = ['/', '/test.html'] + bad = ['/cyberworld/map/index.html'] + + class RobotHandler(BaseHTTPRequestHandler): def do_GET(self): @@ -309,6 +325,8 @@ class NetworkTestCase(unittest.TestCase): self.assertTrue(parser.allow_all) self.assertFalse(parser.disallow_all) self.assertEqual(parser.mtime(), 0) + self.assertIsNone(parser.crawl_delay('*')) + self.assertIsNone(parser.request_rate('*')) if __name__=='__main__': unittest.main() diff --git a/Lib/urllib/robotparser.py b/Lib/urllib/robotparser.py index 85add1624a..9dab4c1c3a 100644 --- a/Lib/urllib/robotparser.py +++ b/Lib/urllib/robotparser.py @@ -175,16 +175,20 @@ class RobotFileParser: return True def crawl_delay(self, useragent): + if not self.mtime(): + return None for entry in self.entries: if entry.applies_to(useragent): return entry.delay - return None + return self.default_entry.delay def request_rate(self, useragent): + if not self.mtime(): + return None for entry in self.entries: if entry.applies_to(useragent): return entry.req_rate - return None + return self.default_entry.req_rate def __str__(self): return ''.join([str(entry) + "\n" for entry in self.entries]) -- cgit v1.2.1 From 12e5f7fc42e0deed0efc513aa19613d10cfc9a21 Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Sun, 18 Sep 2016 13:15:41 -0700 Subject: issue23591: fix flag decomposition and repr --- Lib/enum.py | 152 +++++++++++++++++++++++++++++++------------------- Lib/test/test_enum.py | 105 ++++++++++++++++++++++++---------- 2 files changed, 171 insertions(+), 86 deletions(-) (limited to 'Lib') diff --git a/Lib/enum.py b/Lib/enum.py index d8303204ee..4beb187a4a 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -1,7 +1,7 @@ import sys from types import MappingProxyType, DynamicClassAttribute from functools import reduce -from operator import or_ as _or_ +from operator import or_ as _or_, and_ as _and_, xor, neg # try _collections first to reduce startup cost try: @@ -47,11 +47,12 @@ def _make_class_unpicklable(cls): cls.__reduce_ex__ = _break_on_call_reduce cls.__module__ = '' +_auto_null = object() class auto: """ Instances are replaced with an appropriate value in Enum class suites. """ - pass + value = _auto_null class _EnumDict(dict): @@ -77,7 +78,7 @@ class _EnumDict(dict): """ if _is_sunder(key): if key not in ( - '_order_', '_create_pseudo_member_', '_decompose_', + '_order_', '_create_pseudo_member_', '_generate_next_value_', '_missing_', ): raise ValueError('_names_ are reserved for future Enum use') @@ -94,7 +95,9 @@ class _EnumDict(dict): # enum overwriting a descriptor? raise TypeError('%r already defined as: %r' % (key, self[key])) if isinstance(value, auto): - value = self._generate_next_value(key, 1, len(self._member_names), self._last_values[:]) + if value.value == _auto_null: + value.value = self._generate_next_value(key, 1, len(self._member_names), self._last_values[:]) + value = value.value self._member_names.append(key) self._last_values.append(value) super().__setitem__(key, value) @@ -658,7 +661,7 @@ class Flag(Enum): try: high_bit = _high_bit(last_value) break - except TypeError: + except Exception: raise TypeError('Invalid Flag value: %r' % last_value) from None return 2 ** (high_bit+1) @@ -668,61 +671,38 @@ class Flag(Enum): if value < 0: value = ~value possible_member = cls._create_pseudo_member_(value) - for member in possible_member._decompose_(): - if member._name_ is None and member._value_ != 0: - raise ValueError('%r is not a valid %s' % (original_value, cls.__name__)) if original_value < 0: possible_member = ~possible_member return possible_member @classmethod def _create_pseudo_member_(cls, value): + """ + Create a composite member iff value contains only members. + """ pseudo_member = cls._value2member_map_.get(value, None) if pseudo_member is None: - # construct a non-singleton enum pseudo-member + # verify all bits are accounted for + _, extra_flags = _decompose(cls, value) + if extra_flags: + raise ValueError("%r is not a valid %s" % (value, cls.__name__)) + # construct a singleton enum pseudo-member pseudo_member = object.__new__(cls) pseudo_member._name_ = None pseudo_member._value_ = value cls._value2member_map_[value] = pseudo_member return pseudo_member - def _decompose_(self): - """Extract all members from the value.""" - value = self._value_ - members = [] - cls = self.__class__ - for member in sorted(cls, key=lambda m: m._value_, reverse=True): - while _high_bit(value) > _high_bit(member._value_): - unknown = self._create_pseudo_member_(2 ** _high_bit(value)) - members.append(unknown) - value &= ~unknown._value_ - if ( - (value & member._value_ == member._value_) - and (member._value_ or not members) - ): - value &= ~member._value_ - members.append(member) - if not members or value: - members.append(self._create_pseudo_member_(value)) - members = list(members) - return members - def __contains__(self, other): if not isinstance(other, self.__class__): return NotImplemented return other._value_ & self._value_ == other._value_ - def __iter__(self): - if self.value == 0: - return iter([]) - else: - return iter(self._decompose_()) - def __repr__(self): cls = self.__class__ if self._name_ is not None: return '<%s.%s: %r>' % (cls.__name__, self._name_, self._value_) - members = self._decompose_() + members, uncovered = _decompose(cls, self._value_) return '<%s.%s: %r>' % ( cls.__name__, '|'.join([str(m._name_ or m._value_) for m in members]), @@ -733,7 +713,7 @@ class Flag(Enum): cls = self.__class__ if self._name_ is not None: return '%s.%s' % (cls.__name__, self._name_) - members = self._decompose_() + members, uncovered = _decompose(cls, self._value_) if len(members) == 1 and members[0]._name_ is None: return '%s.%r' % (cls.__name__, members[0]._value_) else: @@ -761,8 +741,11 @@ class Flag(Enum): return self.__class__(self._value_ ^ other._value_) def __invert__(self): - members = self._decompose_() - inverted_members = [m for m in self.__class__ if m not in members and not m._value_ & self._value_] + members, uncovered = _decompose(self.__class__, self._value_) + inverted_members = [ + m for m in self.__class__ + if m not in members and not m._value_ & self._value_ + ] inverted = reduce(_or_, inverted_members, self.__class__(0)) return self.__class__(inverted) @@ -770,26 +753,46 @@ class Flag(Enum): class IntFlag(int, Flag): """Support for integer-based Flags""" + @classmethod + def _missing_(cls, value): + if not isinstance(value, int): + raise ValueError("%r is not a valid %s" % (value, cls.__name__)) + new_member = cls._create_pseudo_member_(value) + return new_member + @classmethod def _create_pseudo_member_(cls, value): pseudo_member = cls._value2member_map_.get(value, None) if pseudo_member is None: - # construct a non-singleton enum pseudo-member - pseudo_member = int.__new__(cls, value) - pseudo_member._name_ = None - pseudo_member._value_ = value - cls._value2member_map_[value] = pseudo_member + need_to_create = [value] + # get unaccounted for bits + _, extra_flags = _decompose(cls, value) + # timer = 10 + while extra_flags: + # timer -= 1 + bit = _high_bit(extra_flags) + flag_value = 2 ** bit + if (flag_value not in cls._value2member_map_ and + flag_value not in need_to_create + ): + need_to_create.append(flag_value) + if extra_flags == -flag_value: + extra_flags = 0 + else: + extra_flags ^= flag_value + for value in reversed(need_to_create): + # construct singleton pseudo-members + pseudo_member = int.__new__(cls, value) + pseudo_member._name_ = None + pseudo_member._value_ = value + cls._value2member_map_[value] = pseudo_member return pseudo_member - @classmethod - def _missing_(cls, value): - possible_member = cls._create_pseudo_member_(value) - return possible_member - def __or__(self, other): if not isinstance(other, (self.__class__, int)): return NotImplemented - return self.__class__(self._value_ | self.__class__(other)._value_) + result = self.__class__(self._value_ | self.__class__(other)._value_) + return result def __and__(self, other): if not isinstance(other, (self.__class__, int)): @@ -806,17 +809,13 @@ class IntFlag(int, Flag): __rxor__ = __xor__ def __invert__(self): - # members = self._decompose_() - # inverted_members = [m for m in self.__class__ if m not in members and not m._value_ & self._value_] - # inverted = reduce(_or_, inverted_members, self.__class__(0)) - return self.__class__(~self._value_) - - + result = self.__class__(~self._value_) + return result def _high_bit(value): """returns index of highest bit, or -1 if value is zero or negative""" - return value.bit_length() - 1 if value > 0 else -1 + return value.bit_length() - 1 def unique(enumeration): """Class decorator for enumerations ensuring unique member values.""" @@ -830,3 +829,40 @@ def unique(enumeration): raise ValueError('duplicate values found in %r: %s' % (enumeration, alias_details)) return enumeration + +def _decompose(flag, value): + """Extract all members from the value.""" + # _decompose is only called if the value is not named + not_covered = value + negative = value < 0 + if negative: + # only check for named flags + flags_to_check = [ + (m, v) + for v, m in flag._value2member_map_.items() + if m.name is not None + ] + else: + # check for named flags and powers-of-two flags + flags_to_check = [ + (m, v) + for v, m in flag._value2member_map_.items() + if m.name is not None or _power_of_two(v) + ] + members = [] + for member, member_value in flags_to_check: + if member_value and member_value & value == member_value: + members.append(member) + not_covered &= ~member_value + if not members and value in flag._value2member_map_: + members.append(flag._value2member_map_[value]) + members.sort(key=lambda m: m._value_, reverse=True) + if len(members) > 1 and members[0].value == value: + # we have the breakdown, don't need the value member itself + members.pop(0) + return members, not_covered + +def _power_of_two(value): + if value < 1: + return False + return value == 2 ** _high_bit(value) diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index 153bfb40a7..2b3bfea916 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -1634,6 +1634,13 @@ class TestEnum(unittest.TestCase): self.assertEqual(Color.blue.value, 2) self.assertEqual(Color.green.value, 3) + def test_duplicate_auto(self): + class Dupes(Enum): + first = primero = auto() + second = auto() + third = auto() + self.assertEqual([Dupes.first, Dupes.second, Dupes.third], list(Dupes)) + class TestOrder(unittest.TestCase): @@ -1731,7 +1738,7 @@ class TestFlag(unittest.TestCase): self.assertEqual(str(Open.AC), 'Open.AC') self.assertEqual(str(Open.RO | Open.CE), 'Open.CE') self.assertEqual(str(Open.WO | Open.CE), 'Open.CE|WO') - self.assertEqual(str(~Open.RO), 'Open.CE|AC') + self.assertEqual(str(~Open.RO), 'Open.CE|AC|RW|WO') self.assertEqual(str(~Open.WO), 'Open.CE|RW') self.assertEqual(str(~Open.AC), 'Open.CE') self.assertEqual(str(~(Open.RO | Open.CE)), 'Open.AC') @@ -1758,7 +1765,7 @@ class TestFlag(unittest.TestCase): self.assertEqual(repr(Open.AC), '') self.assertEqual(repr(Open.RO | Open.CE), '') self.assertEqual(repr(Open.WO | Open.CE), '') - self.assertEqual(repr(~Open.RO), '') + self.assertEqual(repr(~Open.RO), '') self.assertEqual(repr(~Open.WO), '') self.assertEqual(repr(~Open.AC), '') self.assertEqual(repr(~(Open.RO | Open.CE)), '') @@ -1949,6 +1956,33 @@ class TestFlag(unittest.TestCase): red = 'not an int' blue = auto() + def test_cascading_failure(self): + class Bizarre(Flag): + c = 3 + d = 4 + f = 6 + # Bizarre.c | Bizarre.d + self.assertRaisesRegex(ValueError, "5 is not a valid Bizarre", Bizarre, 5) + self.assertRaisesRegex(ValueError, "5 is not a valid Bizarre", Bizarre, 5) + self.assertRaisesRegex(ValueError, "2 is not a valid Bizarre", Bizarre, 2) + self.assertRaisesRegex(ValueError, "2 is not a valid Bizarre", Bizarre, 2) + self.assertRaisesRegex(ValueError, "1 is not a valid Bizarre", Bizarre, 1) + self.assertRaisesRegex(ValueError, "1 is not a valid Bizarre", Bizarre, 1) + + def test_duplicate_auto(self): + class Dupes(Enum): + first = primero = auto() + second = auto() + third = auto() + self.assertEqual([Dupes.first, Dupes.second, Dupes.third], list(Dupes)) + + def test_bizarre(self): + class Bizarre(Flag): + b = 3 + c = 4 + d = 6 + self.assertEqual(repr(Bizarre(7)), '') + class TestIntFlag(unittest.TestCase): """Tests of the IntFlags.""" @@ -1965,6 +1999,21 @@ class TestIntFlag(unittest.TestCase): AC = 3 CE = 1<<19 + def test_type(self): + Perm = self.Perm + Open = self.Open + for f in Perm: + self.assertTrue(isinstance(f, Perm)) + self.assertEqual(f, f.value) + self.assertTrue(isinstance(Perm.W | Perm.X, Perm)) + self.assertEqual(Perm.W | Perm.X, 3) + for f in Open: + self.assertTrue(isinstance(f, Open)) + self.assertEqual(f, f.value) + self.assertTrue(isinstance(Open.WO | Open.RW, Open)) + self.assertEqual(Open.WO | Open.RW, 3) + + def test_str(self): Perm = self.Perm self.assertEqual(str(Perm.R), 'Perm.R') @@ -1975,14 +2024,14 @@ class TestIntFlag(unittest.TestCase): self.assertEqual(str(Perm.R | 8), 'Perm.8|R') self.assertEqual(str(Perm(0)), 'Perm.0') self.assertEqual(str(Perm(8)), 'Perm.8') - self.assertEqual(str(~Perm.R), 'Perm.W|X|-8') - self.assertEqual(str(~Perm.W), 'Perm.R|X|-8') - self.assertEqual(str(~Perm.X), 'Perm.R|W|-8') - self.assertEqual(str(~(Perm.R | Perm.W)), 'Perm.X|-8') + self.assertEqual(str(~Perm.R), 'Perm.W|X') + self.assertEqual(str(~Perm.W), 'Perm.R|X') + self.assertEqual(str(~Perm.X), 'Perm.R|W') + self.assertEqual(str(~(Perm.R | Perm.W)), 'Perm.X') self.assertEqual(str(~(Perm.R | Perm.W | Perm.X)), 'Perm.-8') - self.assertEqual(str(~(Perm.R | 8)), 'Perm.W|X|-16') - self.assertEqual(str(Perm(~0)), 'Perm.R|W|X|-8') - self.assertEqual(str(Perm(~8)), 'Perm.R|W|X|-16') + self.assertEqual(str(~(Perm.R | 8)), 'Perm.W|X') + self.assertEqual(str(Perm(~0)), 'Perm.R|W|X') + self.assertEqual(str(Perm(~8)), 'Perm.R|W|X') Open = self.Open self.assertEqual(str(Open.RO), 'Open.RO') @@ -1991,12 +2040,12 @@ class TestIntFlag(unittest.TestCase): self.assertEqual(str(Open.RO | Open.CE), 'Open.CE') self.assertEqual(str(Open.WO | Open.CE), 'Open.CE|WO') self.assertEqual(str(Open(4)), 'Open.4') - self.assertEqual(str(~Open.RO), 'Open.CE|AC|-524292') - self.assertEqual(str(~Open.WO), 'Open.CE|RW|-524292') - self.assertEqual(str(~Open.AC), 'Open.CE|-524292') - self.assertEqual(str(~(Open.RO | Open.CE)), 'Open.AC|-524292') - self.assertEqual(str(~(Open.WO | Open.CE)), 'Open.RW|-524292') - self.assertEqual(str(Open(~4)), 'Open.CE|AC|-524296') + self.assertEqual(str(~Open.RO), 'Open.CE|AC|RW|WO') + self.assertEqual(str(~Open.WO), 'Open.CE|RW') + self.assertEqual(str(~Open.AC), 'Open.CE') + self.assertEqual(str(~(Open.RO | Open.CE)), 'Open.AC|RW|WO') + self.assertEqual(str(~(Open.WO | Open.CE)), 'Open.RW') + self.assertEqual(str(Open(~4)), 'Open.CE|AC|RW|WO') def test_repr(self): Perm = self.Perm @@ -2008,14 +2057,14 @@ class TestIntFlag(unittest.TestCase): self.assertEqual(repr(Perm.R | 8), '') self.assertEqual(repr(Perm(0)), '') self.assertEqual(repr(Perm(8)), '') - self.assertEqual(repr(~Perm.R), '') - self.assertEqual(repr(~Perm.W), '') - self.assertEqual(repr(~Perm.X), '') - self.assertEqual(repr(~(Perm.R | Perm.W)), '') + self.assertEqual(repr(~Perm.R), '') + self.assertEqual(repr(~Perm.W), '') + self.assertEqual(repr(~Perm.X), '') + self.assertEqual(repr(~(Perm.R | Perm.W)), '') self.assertEqual(repr(~(Perm.R | Perm.W | Perm.X)), '') - self.assertEqual(repr(~(Perm.R | 8)), '') - self.assertEqual(repr(Perm(~0)), '') - self.assertEqual(repr(Perm(~8)), '') + self.assertEqual(repr(~(Perm.R | 8)), '') + self.assertEqual(repr(Perm(~0)), '') + self.assertEqual(repr(Perm(~8)), '') Open = self.Open self.assertEqual(repr(Open.RO), '') @@ -2024,12 +2073,12 @@ class TestIntFlag(unittest.TestCase): self.assertEqual(repr(Open.RO | Open.CE), '') self.assertEqual(repr(Open.WO | Open.CE), '') self.assertEqual(repr(Open(4)), '') - self.assertEqual(repr(~Open.RO), '') - self.assertEqual(repr(~Open.WO), '') - self.assertEqual(repr(~Open.AC), '') - self.assertEqual(repr(~(Open.RO | Open.CE)), '') - self.assertEqual(repr(~(Open.WO | Open.CE)), '') - self.assertEqual(repr(Open(~4)), '') + self.assertEqual(repr(~Open.RO), '') + self.assertEqual(repr(~Open.WO), '') + self.assertEqual(repr(~Open.AC), '') + self.assertEqual(repr(~(Open.RO | Open.CE)), '') + self.assertEqual(repr(~(Open.WO | Open.CE)), '') + self.assertEqual(repr(Open(~4)), '') def test_or(self): Perm = self.Perm -- cgit v1.2.1 From afce931a5149925eab386dadb2a3297771280909 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 19 Sep 2016 00:11:30 +0200 Subject: Fix test_huntrleaks_fd_leak() of test_regrtest Issue #28195: Don't expect the fd leak message to be on a specific line number, just make sure that the line is present in the output. --- Lib/test/test_regrtest.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py index 7c95b64243..5de2a6f12e 100644 --- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -772,14 +772,11 @@ class ArgsTestCase(BaseTestCase): self.check_line(output, re.escape(line)) line2 = '%s leaked [1, 1, 1] file descriptors, sum=3\n' % test - self.check_line(output, re.escape(line2)) + self.assertIn(line2, output) with open(filename) as fp: reflog = fp.read() - if hasattr(sys, 'getcounts'): - # Types are immportal if COUNT_ALLOCS is defined - reflog = reflog.splitlines(True)[-1] - self.assertEqual(reflog, line2) + self.assertIn(line2, reflog) def test_list_tests(self): # test --list-tests -- cgit v1.2.1 From 11fdd171ec455c7602b0c69a5412e9725185aecb Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sun, 18 Sep 2016 20:17:21 -0700 Subject: Issue #28193: Use lru_cache in the re module. --- Lib/re.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) (limited to 'Lib') diff --git a/Lib/re.py b/Lib/re.py index 0850f0db8d..d321cff92c 100644 --- a/Lib/re.py +++ b/Lib/re.py @@ -122,6 +122,7 @@ This module also defines an exception 'error'. import enum import sre_compile import sre_parse +import functools try: import _locale except ImportError: @@ -234,7 +235,7 @@ def compile(pattern, flags=0): def purge(): "Clear the regular expression caches" _cache.clear() - _cache_repl.clear() + _compile_repl.cache_clear() def template(pattern, flags=0): "Compile a template pattern, returning a pattern object" @@ -278,7 +279,6 @@ def escape(pattern): # internals _cache = {} -_cache_repl = {} _pattern_type = type(sre_compile.compile("", 0)) @@ -311,17 +311,10 @@ def _compile(pattern, flags): _cache[type(pattern), pattern, flags] = p, loc return p +@functools.lru_cache(_MAXCACHE) def _compile_repl(repl, pattern): # internal: compile replacement pattern - try: - return _cache_repl[repl, pattern] - except KeyError: - pass - p = sre_parse.parse_template(repl, pattern) - if len(_cache_repl) >= _MAXCACHE: - _cache_repl.clear() - _cache_repl[repl, pattern] = p - return p + return sre_parse.parse_template(repl, pattern) def _expand(pattern, match, template): # internal: match.expand implementation hook -- cgit v1.2.1 From eeeaf9556f5032e2870e00e67611caded463e6dc Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sun, 18 Sep 2016 21:46:08 -0700 Subject: merge --- Lib/test/test_dictviews.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_dictviews.py b/Lib/test/test_dictviews.py index 4c290ffaa1..49a9e9c007 100644 --- a/Lib/test/test_dictviews.py +++ b/Lib/test/test_dictviews.py @@ -210,6 +210,32 @@ class DictSetTest(unittest.TestCase): self.assertRaises(TypeError, copy.copy, d.values()) self.assertRaises(TypeError, copy.copy, d.items()) + def test_compare_error(self): + class Exc(Exception): + pass + + class BadEq: + def __hash__(self): + return 7 + def __eq__(self, other): + raise Exc + + k1, k2 = BadEq(), BadEq() + v1, v2 = BadEq(), BadEq() + d = {k1: v1} + + self.assertIn(k1, d) + self.assertIn(k1, d.keys()) + self.assertIn(v1, d.values()) + self.assertIn((k1, v1), d.items()) + + self.assertRaises(Exc, d.__contains__, k2) + self.assertRaises(Exc, d.keys().__contains__, k2) + self.assertRaises(Exc, d.items().__contains__, (k2, v1)) + self.assertRaises(Exc, d.items().__contains__, (k1, v2)) + with self.assertRaises(Exc): + v2 in d.values() + def test_pickle(self): d = {1: 10, "a": "ABC"} for proto in range(pickle.HIGHEST_PROTOCOL + 1): -- cgit v1.2.1 From 16582c66bcf5e1af5baee6ee429caa52007c9f88 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 21 Sep 2016 15:54:59 +0300 Subject: Issue #28214: Now __set_name__ is looked up on the class instead of the instance. --- Lib/test/test_subclassinit.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_subclassinit.py b/Lib/test/test_subclassinit.py index ea6de757c6..0267e41717 100644 --- a/Lib/test/test_subclassinit.py +++ b/Lib/test/test_subclassinit.py @@ -148,6 +148,18 @@ class Test(unittest.TestCase): class A: d = Descriptor() + def test_set_name_lookup(self): + resolved = [] + class NonDescriptor: + def __getattr__(self, name): + resolved.append(name) + + class A: + d = NonDescriptor() + + self.assertNotIn('__set_name__', resolved, + '__set_name__ is looked up in instance dict') + def test_set_name_init_subclass(self): class Descriptor: def __set_name__(self, owner, name): -- cgit v1.2.1 From f66fc0cf7578db63bce994ce14465f7b61aaa614 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 22 Sep 2016 19:41:20 +0300 Subject: Issue #28086: Single var-positional argument of tuple subtype was passed unscathed to the C-defined function. Now it is converted to exact tuple. --- Lib/test/test_getargs2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_getargs2.py b/Lib/test/test_getargs2.py index 5750bfa5f8..8a194aa03d 100644 --- a/Lib/test/test_getargs2.py +++ b/Lib/test/test_getargs2.py @@ -471,7 +471,7 @@ class Tuple_TestCase(unittest.TestCase): ret = get_args(*TupleSubclass([1, 2])) self.assertEqual(ret, (1, 2)) - self.assertIsInstance(ret, tuple) + self.assertIs(type(ret), tuple) ret = get_args() self.assertIn(ret, ((), None)) -- cgit v1.2.1 From d2d414eeef367bf730e6959004b68a0dc710ab3a Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Fri, 23 Sep 2016 11:32:30 +0200 Subject: Add test cases for internal SHA3 helpers --- Lib/test/test_hashlib.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py index 5ae8c07037..f748b46190 100644 --- a/Lib/test/test_hashlib.py +++ b/Lib/test/test_hashlib.py @@ -339,6 +339,9 @@ class HashLibTestCase(unittest.TestCase): self.check_blocksize_name('sha256', 64, 32) self.check_blocksize_name('sha384', 128, 48) self.check_blocksize_name('sha512', 128, 64) + + @requires_sha3 + def test_blocksize_name_sha3(self): self.check_blocksize_name('sha3_224', 144, 28) self.check_blocksize_name('sha3_256', 136, 32) self.check_blocksize_name('sha3_384', 104, 48) @@ -346,6 +349,24 @@ class HashLibTestCase(unittest.TestCase): self.check_blocksize_name('shake_128', 168, 0, 32) self.check_blocksize_name('shake_256', 136, 0, 64) + def check_sha3(self, name, capacity, rate, suffix): + constructors = self.constructors_to_test[name] + for hash_object_constructor in constructors: + m = hash_object_constructor() + self.assertEqual(capacity + rate, 1600) + self.assertEqual(m._capacity_bits, capacity) + self.assertEqual(m._rate_bits, rate) + self.assertEqual(m._suffix, suffix) + + @requires_sha3 + def test_extra_sha3(self): + self.check_sha3('sha3_224', 448, 1152, b'\x06') + self.check_sha3('sha3_256', 512, 1088, b'\x06') + self.check_sha3('sha3_384', 768, 832, b'\x06') + self.check_sha3('sha3_512', 1024, 576, b'\x06') + self.check_sha3('shake_128', 256, 1344, b'\x1f') + self.check_sha3('shake_256', 512, 1088, b'\x1f') + @requires_blake2 def test_blocksize_name_blake2(self): self.check_blocksize_name('blake2b', 128, 64) -- cgit v1.2.1 From 491968ae74c1e7faf2822ad42a8eddcb615343b7 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sat, 24 Sep 2016 10:48:05 +0200 Subject: Finish GC code for SSLSession and increase test coverage --- Lib/test/test_ssl.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 631c8c187d..ad30105b0f 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -1474,6 +1474,7 @@ class SimpleBackgroundTests(unittest.TestCase): cert_reqs=ssl.CERT_NONE) as s: s.connect(self.server_addr) self.assertEqual({}, s.getpeercert()) + self.assertFalse(s.server_side) # this should succeed because we specify the root cert with test_wrap_socket(socket.socket(socket.AF_INET), @@ -1481,6 +1482,7 @@ class SimpleBackgroundTests(unittest.TestCase): ca_certs=SIGNING_CA) as s: s.connect(self.server_addr) self.assertTrue(s.getpeercert()) + self.assertFalse(s.server_side) def test_connect_fail(self): # This should fail because we have no verification certs. Connection @@ -3028,6 +3030,7 @@ if _have_threads: host = "127.0.0.1" port = support.bind_port(server) server = context.wrap_socket(server, server_side=True) + self.assertTrue(server.server_side) evt = threading.Event() remote = None -- cgit v1.2.1 From 59eea80adc214183bd1364c4b97a7a214e760a28 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Mon, 26 Sep 2016 21:45:57 -0700 Subject: Issue #18844: Make the number of selections a keyword-only argument for random.choices(). --- Lib/random.py | 2 +- Lib/test/test_random.py | 42 +++++++++++++++++++++--------------------- 2 files changed, 22 insertions(+), 22 deletions(-) (limited to 'Lib') diff --git a/Lib/random.py b/Lib/random.py index cd8583fe4a..ef8cb05601 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -337,7 +337,7 @@ class Random(_random.Random): result[i] = population[j] return result - def choices(self, k, population, weights=None, *, cum_weights=None): + def choices(self, population, weights=None, *, cum_weights=None, k=1): """Return a k sized list of population elements chosen with replacement. If the relative weights or cumulative weights are not specified, diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py index 9c1383d7db..0dfc290824 100644 --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -151,8 +151,8 @@ class TestBasicOps: # basic functionality for sample in [ - choices(5, data), - choices(5, data, range(4)), + choices(data, k=5), + choices(data, range(4), k=5), choices(k=5, population=data, weights=range(4)), choices(k=5, population=data, cum_weights=range(4)), ]: @@ -164,50 +164,50 @@ class TestBasicOps: with self.assertRaises(TypeError): # missing arguments choices(2) - self.assertEqual(choices(0, data), []) # k == 0 - self.assertEqual(choices(-1, data), []) # negative k behaves like ``[0] * -1`` + self.assertEqual(choices(data, k=0), []) # k == 0 + self.assertEqual(choices(data, k=-1), []) # negative k behaves like ``[0] * -1`` with self.assertRaises(TypeError): - choices(2.5, data) # k is a float + choices(data, k=2.5) # k is a float - self.assertTrue(set(choices(5, str_data)) <= set(str_data)) # population is a string sequence - self.assertTrue(set(choices(5, range_data)) <= set(range_data)) # population is a range + self.assertTrue(set(choices(str_data, k=5)) <= set(str_data)) # population is a string sequence + self.assertTrue(set(choices(range_data, k=5)) <= set(range_data)) # population is a range with self.assertRaises(TypeError): - choices(2.5, set_data) # population is not a sequence + choices(set_data, k=2) # population is not a sequence - self.assertTrue(set(choices(5, data, None)) <= set(data)) # weights is None - self.assertTrue(set(choices(5, data, weights=None)) <= set(data)) + self.assertTrue(set(choices(data, None, k=5)) <= set(data)) # weights is None + self.assertTrue(set(choices(data, weights=None, k=5)) <= set(data)) with self.assertRaises(ValueError): - choices(5, data, [1,2]) # len(weights) != len(population) + choices(data, [1,2], k=5) # len(weights) != len(population) with self.assertRaises(IndexError): - choices(5, data, [0]*4) # weights sum to zero + choices(data, [0]*4, k=5) # weights sum to zero with self.assertRaises(TypeError): - choices(5, data, 10) # non-iterable weights + choices(data, 10, k=5) # non-iterable weights with self.assertRaises(TypeError): - choices(5, data, [None]*4) # non-numeric weights + choices(data, [None]*4, k=5) # non-numeric weights for weights in [ [15, 10, 25, 30], # integer weights [15.1, 10.2, 25.2, 30.3], # float weights [Fraction(1, 3), Fraction(2, 6), Fraction(3, 6), Fraction(4, 6)], # fractional weights [True, False, True, False] # booleans (include / exclude) ]: - self.assertTrue(set(choices(5, data, weights)) <= set(data)) + self.assertTrue(set(choices(data, weights, k=5)) <= set(data)) with self.assertRaises(ValueError): - choices(5, data, cum_weights=[1,2]) # len(weights) != len(population) + choices(data, cum_weights=[1,2], k=5) # len(weights) != len(population) with self.assertRaises(IndexError): - choices(5, data, cum_weights=[0]*4) # cum_weights sum to zero + choices(data, cum_weights=[0]*4, k=5) # cum_weights sum to zero with self.assertRaises(TypeError): - choices(5, data, cum_weights=10) # non-iterable cum_weights + choices(data, cum_weights=10, k=5) # non-iterable cum_weights with self.assertRaises(TypeError): - choices(5, data, cum_weights=[None]*4) # non-numeric cum_weights + choices(data, cum_weights=[None]*4, k=5) # non-numeric cum_weights with self.assertRaises(TypeError): - choices(5, data, range(4), cum_weights=range(4)) # both weights and cum_weights + choices(data, range(4), cum_weights=range(4), k=5) # both weights and cum_weights for weights in [ [15, 10, 25, 30], # integer cum_weights [15.1, 10.2, 25.2, 30.3], # float cum_weights [Fraction(1, 3), Fraction(2, 6), Fraction(3, 6), Fraction(4, 6)], # fractional cum_weights ]: - self.assertTrue(set(choices(5, data, cum_weights=weights)) <= set(data)) + self.assertTrue(set(choices(data, cum_weights=weights, k=5)) <= set(data)) def test_gauss(self): # Ensure that the seed() method initializes all the hidden state. In -- cgit v1.2.1 From 36402377a7562e5080e37d3c1c7e39c3d9144ce4 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Wed, 28 Sep 2016 17:38:53 +0300 Subject: Issue #27322: Set sys.path to a temp dir in test_compile_path --- Lib/test/test_compileall.py | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_compileall.py b/Lib/test/test_compileall.py index 9b424a7250..1f05e78c23 100644 --- a/Lib/test/test_compileall.py +++ b/Lib/test/test_compileall.py @@ -103,19 +103,8 @@ class CompileallTests(unittest.TestCase): force=False, quiet=2)) def test_compile_path(self): - # Exclude Lib/test/ which contains invalid Python files like - # Lib/test/badsyntax_pep3120.py - testdir = os.path.realpath(os.path.dirname(__file__)) - if testdir in sys.path: - self.addCleanup(setattr, sys, 'path', sys.path) - - sys.path = list(sys.path) - try: - sys.path.remove(testdir) - except ValueError: - pass - - self.assertTrue(compileall.compile_path(quiet=2)) + with test.test_importlib.util.import_state(path=[self.directory]): + self.assertTrue(compileall.compile_path(quiet=2)) with test.test_importlib.util.import_state(path=[self.directory]): self.add_bad_source_file() -- cgit v1.2.1 From 7aab54e92d8d0ef137d1dca782bba739f4d3d424 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Sat, 1 Oct 2016 00:54:18 +0300 Subject: Issue #28226: compileall now supports pathlib --- Lib/compileall.py | 4 ++++ Lib/test/test_compileall.py | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+) (limited to 'Lib') diff --git a/Lib/compileall.py b/Lib/compileall.py index 67c5f5ac72..3e45785a66 100644 --- a/Lib/compileall.py +++ b/Lib/compileall.py @@ -25,6 +25,8 @@ from functools import partial __all__ = ["compile_dir","compile_file","compile_path"] def _walk_dir(dir, ddir=None, maxlevels=10, quiet=0): + if quiet < 2 and isinstance(dir, os.PathLike): + dir = os.fspath(dir) if not quiet: print('Listing {!r}...'.format(dir)) try: @@ -105,6 +107,8 @@ def compile_file(fullname, ddir=None, force=False, rx=None, quiet=0, optimize: optimization level or -1 for level of the interpreter """ success = True + if quiet < 2 and isinstance(fullname, os.PathLike): + fullname = os.fspath(fullname) name = os.path.basename(fullname) if ddir is not None: dfile = os.path.join(ddir, name) diff --git a/Lib/test/test_compileall.py b/Lib/test/test_compileall.py index 1f05e78c23..30ca3feee4 100644 --- a/Lib/test/test_compileall.py +++ b/Lib/test/test_compileall.py @@ -102,6 +102,22 @@ class CompileallTests(unittest.TestCase): self.assertFalse(compileall.compile_dir(self.directory, force=False, quiet=2)) + def test_compile_file_pathlike(self): + self.assertFalse(os.path.isfile(self.bc_path)) + # we should also test the output + with support.captured_stdout() as stdout: + self.assertTrue(compileall.compile_file(pathlib.Path(self.source_path))) + self.assertEqual(stdout.getvalue(), + "Compiling '{}'...\n".format(self.source_path)) + self.assertTrue(os.path.isfile(self.bc_path)) + + def test_compile_file_pathlike_ddir(self): + self.assertFalse(os.path.isfile(self.bc_path)) + self.assertTrue(compileall.compile_file(pathlib.Path(self.source_path), + ddir=pathlib.Path('ddir_path'), + quiet=2)) + self.assertTrue(os.path.isfile(self.bc_path)) + def test_compile_path(self): with test.test_importlib.util.import_state(path=[self.directory]): self.assertTrue(compileall.compile_path(quiet=2)) @@ -138,6 +154,13 @@ class CompileallTests(unittest.TestCase): optimization=opt) self.assertTrue(os.path.isfile(cached3)) + def test_compile_dir_pathlike(self): + self.assertFalse(os.path.isfile(self.bc_path)) + with support.captured_stdout() as stdout: + compileall.compile_dir(pathlib.Path(self.directory)) + self.assertIn("Listing '{}'...".format(self.directory), stdout.getvalue()) + self.assertTrue(os.path.isfile(self.bc_path)) + @mock.patch('compileall.ProcessPoolExecutor') def test_compile_pool_called(self, pool_mock): compileall.compile_dir(self.directory, quiet=True, workers=5) -- cgit v1.2.1 From 6080713ccb3f11ff686fb19aa205951eee086c6d Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Sat, 1 Oct 2016 02:44:37 +0300 Subject: Issue #28226: Fix test_compileall on Windows --- Lib/test/test_compileall.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_compileall.py b/Lib/test/test_compileall.py index 30ca3feee4..2356efcaec 100644 --- a/Lib/test/test_compileall.py +++ b/Lib/test/test_compileall.py @@ -107,8 +107,7 @@ class CompileallTests(unittest.TestCase): # we should also test the output with support.captured_stdout() as stdout: self.assertTrue(compileall.compile_file(pathlib.Path(self.source_path))) - self.assertEqual(stdout.getvalue(), - "Compiling '{}'...\n".format(self.source_path)) + self.assertRegex(stdout.getvalue(), r'Compiling ([^WindowsPath|PosixPath].*)') self.assertTrue(os.path.isfile(self.bc_path)) def test_compile_file_pathlike_ddir(self): @@ -158,7 +157,8 @@ class CompileallTests(unittest.TestCase): self.assertFalse(os.path.isfile(self.bc_path)) with support.captured_stdout() as stdout: compileall.compile_dir(pathlib.Path(self.directory)) - self.assertIn("Listing '{}'...".format(self.directory), stdout.getvalue()) + line = stdout.getvalue().splitlines()[0] + self.assertRegex(line, r'Listing ([^WindowsPath|PosixPath].*)') self.assertTrue(os.path.isfile(self.bc_path)) @mock.patch('compileall.ProcessPoolExecutor') -- cgit v1.2.1 From a0be625be517f6bdcb5863a4d690a8bbeccc34fe Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Sat, 1 Oct 2016 05:01:54 +0300 Subject: Issue #28228: imghdr now supports pathlib --- Lib/imghdr.py | 4 +++- Lib/test/test_imghdr.py | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/imghdr.py b/Lib/imghdr.py index b26792539d..76e8abb2d5 100644 --- a/Lib/imghdr.py +++ b/Lib/imghdr.py @@ -1,5 +1,7 @@ """Recognize image file formats based on their first few bytes.""" +from os import PathLike + __all__ = ["what"] #-------------------------# @@ -10,7 +12,7 @@ def what(file, h=None): f = None try: if h is None: - if isinstance(file, str): + if isinstance(file, (str, PathLike)): f = open(file, 'rb') h = f.read(32) else: diff --git a/Lib/test/test_imghdr.py b/Lib/test/test_imghdr.py index b54daf8e2c..476ba95f17 100644 --- a/Lib/test/test_imghdr.py +++ b/Lib/test/test_imghdr.py @@ -1,6 +1,7 @@ import imghdr import io import os +import pathlib import unittest import warnings from test.support import findfile, TESTFN, unlink @@ -49,6 +50,12 @@ class TestImghdr(unittest.TestCase): self.assertEqual(imghdr.what(None, data), expected) self.assertEqual(imghdr.what(None, bytearray(data)), expected) + def test_pathlike_filename(self): + for filename, expected in TEST_FILES: + with self.subTest(filename=filename): + filename = findfile(filename, subdir='imghdrdata') + self.assertEqual(imghdr.what(pathlib.Path(filename)), expected) + def test_register_test(self): def test_jumbo(h, file): if h.startswith(b'eggs'): -- cgit v1.2.1 From 0c0a0b421d34b455a11661e24cd840ffd8d13d38 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Sat, 1 Oct 2016 21:12:16 -0400 Subject: Issue #28323: Remove vestigal MacOS 9 checks from exit() and quit(). Patch by Chi Hsuan Yen. --- Lib/site.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'Lib') diff --git a/Lib/site.py b/Lib/site.py index b6376357ea..0859f28ce1 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -336,9 +336,7 @@ def setquit(): The repr of each object contains a hint at how it works. """ - if os.sep == ':': - eof = 'Cmd-Q' - elif os.sep == '\\': + if os.sep == '\\': eof = 'Ctrl-Z plus Return' else: eof = 'Ctrl-D (i.e. EOF)' -- cgit v1.2.1 From 262bd80c88cdf31d7cbb65d9e05f4613d4d22ef3 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Sat, 1 Oct 2016 21:12:35 -0400 Subject: Issue #28324: Remove vestigal MacOS 9 references in os.py docstring. Patch by Chi Hsuan Yen. --- Lib/os.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Lib') diff --git a/Lib/os.py b/Lib/os.py index 7379dad41a..3e5f8cfda7 100644 --- a/Lib/os.py +++ b/Lib/os.py @@ -4,9 +4,9 @@ This exports: - all functions from posix or nt, e.g. unlink, stat, etc. - os.path is either posixpath or ntpath - os.name is either 'posix' or 'nt' - - os.curdir is a string representing the current directory ('.' or ':') - - os.pardir is a string representing the parent directory ('..' or '::') - - os.sep is the (or a most common) pathname separator ('/' or ':' or '\\') + - os.curdir is a string representing the current directory (always '.') + - os.pardir is a string representing the parent directory (always '..') + - os.sep is the (or a most common) pathname separator ('/' or '\\') - os.extsep is the extension separator (always '.') - os.altsep is the alternate pathname separator (None or '/') - os.pathsep is the component separator used in $PATH etc -- cgit v1.2.1 From a9d40323166c43221f8a4f57170cb057f95ad2dc Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 2 Oct 2016 10:33:46 +0300 Subject: Issue #28257: Improved error message when pass a non-iterable as a var-positional argument. Added opcode BUILD_TUPLE_UNPACK_WITH_CALL. --- Lib/importlib/_bootstrap_external.py | 3 ++- Lib/opcode.py | 5 +++-- Lib/test/test_extcall.py | 10 ++++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) (limited to 'Lib') diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index bfb89dacbb..5cb58ab2f5 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -238,6 +238,7 @@ _code_type = type(_write_atomic.__code__) # #27985) # Python 3.6b1 3376 (simplify CALL_FUNCTIONs & BUILD_MAP_UNPACK_WITH_CALL) # Python 3.6b1 3377 (set __class__ cell from type.__new__ #23722) +# Python 3.6b2 3378 (add BUILD_TUPLE_UNPACK_WITH_CALL #28257) # # MAGIC must change whenever the bytecode emitted by the compiler may no # longer be understood by older implementations of the eval loop (usually @@ -246,7 +247,7 @@ _code_type = type(_write_atomic.__code__) # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # in PC/launcher.c must also be updated. -MAGIC_NUMBER = (3377).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3378).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c _PYCACHE = '__pycache__' diff --git a/Lib/opcode.py b/Lib/opcode.py index be2647502e..b5916b6619 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -196,8 +196,6 @@ def_op('MAP_ADD', 147) def_op('LOAD_CLASSDEREF', 148) hasfree.append(148) -jrel_op('SETUP_ASYNC_WITH', 154) - def_op('EXTENDED_ARG', 144) EXTENDED_ARG = 144 @@ -207,8 +205,11 @@ def_op('BUILD_MAP_UNPACK_WITH_CALL', 151) def_op('BUILD_TUPLE_UNPACK', 152) def_op('BUILD_SET_UNPACK', 153) +jrel_op('SETUP_ASYNC_WITH', 154) + def_op('FORMAT_VALUE', 155) def_op('BUILD_CONST_KEY_MAP', 156) def_op('BUILD_STRING', 157) +def_op('BUILD_TUPLE_UNPACK_WITH_CALL', 158) del def_op, name_op, jrel_op, jabs_op diff --git a/Lib/test/test_extcall.py b/Lib/test/test_extcall.py index 5eea37989c..96f3ede9a3 100644 --- a/Lib/test/test_extcall.py +++ b/Lib/test/test_extcall.py @@ -233,6 +233,16 @@ What about willful misconduct? ... TypeError: h() argument after * must be an iterable, not function + >>> h(1, *h) + Traceback (most recent call last): + ... + TypeError: h() argument after * must be an iterable, not function + + >>> h(*[1], *h) + Traceback (most recent call last): + ... + TypeError: h() argument after * must be an iterable, not function + >>> dir(*h) Traceback (most recent call last): ... -- cgit v1.2.1 From 5ca9d515bdb90ee5588987dd8f8b2b723b21f4c3 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 2 Oct 2016 11:06:43 +0300 Subject: Issue #27358: Optimized merging var-keyword arguments and improved error message when pass a non-mapping as a var-keyword argument. --- Lib/test/test_extcall.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_extcall.py b/Lib/test/test_extcall.py index 96f3ede9a3..043df01311 100644 --- a/Lib/test/test_extcall.py +++ b/Lib/test/test_extcall.py @@ -259,6 +259,31 @@ not function ... TypeError: h() argument after ** must be a mapping, not function + >>> h(**[]) + Traceback (most recent call last): + ... + TypeError: h() argument after ** must be a mapping, not list + + >>> h(a=1, **h) + Traceback (most recent call last): + ... + TypeError: h() argument after ** must be a mapping, not function + + >>> h(a=1, **[]) + Traceback (most recent call last): + ... + TypeError: h() argument after ** must be a mapping, not list + + >>> h(**{'a': 1}, **h) + Traceback (most recent call last): + ... + TypeError: h() argument after ** must be a mapping, not function + + >>> h(**{'a': 1}, **[]) + Traceback (most recent call last): + ... + TypeError: h() argument after ** must be a mapping, not list + >>> dir(**h) Traceback (most recent call last): ... -- cgit v1.2.1 From 2117222ea636bdc5245ac9e9f1de290ab709e1d8 Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Sun, 2 Oct 2016 11:42:22 +0200 Subject: Issue #28338: Restore test_pdb doctests. --- Lib/test/test_pdb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 74417c15bf..994e088d30 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -1093,7 +1093,7 @@ class PdbTestCase(unittest.TestCase): def load_tests(*args): from test import test_pdb - suites = [unittest.makeSuite(PdbTestCase)] + suites = [unittest.makeSuite(PdbTestCase), doctest.DocTestSuite(test_pdb)] return unittest.TestSuite(suites) -- cgit v1.2.1 From 764e7aedf09e15504612e86c92f0d0a8bdbe0668 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Sun, 2 Oct 2016 13:47:58 +0300 Subject: Issue #28227: gzip now supports pathlib Patch by Ethan Furman. --- Lib/gzip.py | 4 +++- Lib/test/test_gzip.py | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/gzip.py b/Lib/gzip.py index ddf7668d90..76ab497853 100644 --- a/Lib/gzip.py +++ b/Lib/gzip.py @@ -49,7 +49,7 @@ def open(filename, mode="rb", compresslevel=9, raise ValueError("Argument 'newline' not supported in binary mode") gz_mode = mode.replace("t", "") - if isinstance(filename, (str, bytes)): + if isinstance(filename, (str, bytes, os.PathLike)): binary_file = GzipFile(filename, gz_mode, compresslevel) elif hasattr(filename, "read") or hasattr(filename, "write"): binary_file = GzipFile(None, gz_mode, compresslevel, filename) @@ -165,6 +165,8 @@ class GzipFile(_compression.BaseStream): filename = getattr(fileobj, 'name', '') if not isinstance(filename, (str, bytes)): filename = '' + else: + filename = os.fspath(filename) if mode is None: mode = getattr(fileobj, 'mode', 'rb') diff --git a/Lib/test/test_gzip.py b/Lib/test/test_gzip.py index 07a9f6ff44..b457bd3f44 100644 --- a/Lib/test/test_gzip.py +++ b/Lib/test/test_gzip.py @@ -5,6 +5,7 @@ import unittest from test import support from test.support import bigmemtest, _4G import os +import pathlib import io import struct import array @@ -67,6 +68,18 @@ class TestGzip(BaseTest): # Test multiple close() calls. f.close() + def test_write_read_with_pathlike_file(self): + filename = pathlib.Path(self.filename) + with gzip.GzipFile(filename, 'w') as f: + f.write(data1 * 50) + self.assertIsInstance(f.name, str) + with gzip.GzipFile(filename, 'a') as f: + f.write(data1) + with gzip.GzipFile(filename) as f: + d = f.read() + self.assertEqual(d, data1 * 51) + self.assertIsInstance(f.name, str) + # The following test_write_xy methods test that write accepts # the corresponding bytes-like object type as input # and that the data written equals bytes(xy) in all cases. @@ -521,6 +534,15 @@ class TestOpen(BaseTest): file_data = gzip.decompress(f.read()) self.assertEqual(file_data, uncompressed) + def test_pathlike_file(self): + filename = pathlib.Path(self.filename) + with gzip.open(filename, "wb") as f: + f.write(data1 * 50) + with gzip.open(filename, "ab") as f: + f.write(data1) + with gzip.open(filename) as f: + self.assertEqual(f.read(), data1 * 51) + def test_implicit_binary_modes(self): # Test implicit binary modes (no "b" or "t" in mode string). uncompressed = data1 * 50 -- cgit v1.2.1 From 1c51cd1435ed8e7999c0933691abe9801b5e2deb Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Sun, 2 Oct 2016 20:07:06 +0300 Subject: Issue #28225: bz2 module now supports pathlib Initial patch by Ethan Furman. --- Lib/bz2.py | 16 +++++++++------- Lib/test/test_bz2.py | 8 ++++++++ 2 files changed, 17 insertions(+), 7 deletions(-) (limited to 'Lib') diff --git a/Lib/bz2.py b/Lib/bz2.py index bc78c54485..6f56328b9b 100644 --- a/Lib/bz2.py +++ b/Lib/bz2.py @@ -11,6 +11,7 @@ __author__ = "Nadeem Vawda " from builtins import open as _builtin_open import io +import os import warnings import _compression @@ -42,9 +43,9 @@ class BZ2File(_compression.BaseStream): def __init__(self, filename, mode="r", buffering=None, compresslevel=9): """Open a bzip2-compressed file. - If filename is a str or bytes object, it gives the name - of the file to be opened. Otherwise, it should be a file object, - which will be used to read or write the compressed data. + If filename is a str, bytes, or PathLike object, it gives the + name of the file to be opened. Otherwise, it should be a file + object, which will be used to read or write the compressed data. mode can be 'r' for reading (default), 'w' for (over)writing, 'x' for creating exclusively, or 'a' for appending. These can @@ -91,7 +92,7 @@ class BZ2File(_compression.BaseStream): else: raise ValueError("Invalid mode: %r" % (mode,)) - if isinstance(filename, (str, bytes)): + if isinstance(filename, (str, bytes, os.PathLike)): self._fp = _builtin_open(filename, mode) self._closefp = True self._mode = mode_code @@ -99,7 +100,7 @@ class BZ2File(_compression.BaseStream): self._fp = filename self._mode = mode_code else: - raise TypeError("filename must be a str or bytes object, or a file") + raise TypeError("filename must be a str, bytes, file or PathLike object") if self._mode == _MODE_READ: raw = _compression.DecompressReader(self._fp, @@ -289,8 +290,9 @@ def open(filename, mode="rb", compresslevel=9, encoding=None, errors=None, newline=None): """Open a bzip2-compressed file in binary or text mode. - The filename argument can be an actual filename (a str or bytes - object), or an existing file object to read from or write to. + The filename argument can be an actual filename (a str, bytes, or + PathLike object), or an existing file object to read from or write + to. The mode argument can be "r", "rb", "w", "wb", "x", "xb", "a" or "ab" for binary mode, or "rt", "wt", "xt" or "at" for text mode. diff --git a/Lib/test/test_bz2.py b/Lib/test/test_bz2.py index 46ad2c44d8..482242c00e 100644 --- a/Lib/test/test_bz2.py +++ b/Lib/test/test_bz2.py @@ -6,6 +6,7 @@ from io import BytesIO, DEFAULT_BUFFER_SIZE import os import pickle import glob +import pathlib import random import subprocess import sys @@ -560,6 +561,13 @@ class BZ2FileTest(BaseTest): with BZ2File(str_filename, "rb") as f: self.assertEqual(f.read(), self.DATA) + def testOpenPathLikeFilename(self): + filename = pathlib.Path(self.filename) + with BZ2File(filename, "wb") as f: + f.write(self.DATA) + with BZ2File(filename, "rb") as f: + self.assertEqual(f.read(), self.DATA) + def testDecompressLimited(self): """Decompressed data buffering should be limited""" bomb = bz2.compress(b'\0' * int(2e6), compresslevel=9) -- cgit v1.2.1 From 91a3011f734ddd67f8ef4ef35fd2ed9140b6fa9b Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 2 Oct 2016 21:59:44 +0300 Subject: test_invalid_sequences seems don't have to stay in CAPITest. Reported by Xiang Zhang. --- Lib/test/test_unicode.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py index c98cc14024..432a001e7c 100644 --- a/Lib/test/test_unicode.py +++ b/Lib/test/test_unicode.py @@ -2413,6 +2413,13 @@ class UnicodeTest(string_tests.CommonTest, support.check_free_after_iterating(self, iter, str) support.check_free_after_iterating(self, reversed, str) + def test_invalid_sequences(self): + for letter in string.ascii_letters + "89": # 0-7 are octal escapes + if letter in "abfnrtuvxNU": + continue + with self.assertWarns(DeprecationWarning): + eval(r"'\%s'" % letter) + class CAPITest(unittest.TestCase): @@ -2773,13 +2780,6 @@ class CAPITest(unittest.TestCase): # Check that the second call returns the same result self.assertEqual(getargs_s_hash(s), chr(k).encode() * (i + 1)) - def test_invalid_sequences(self): - for letter in string.ascii_letters + "89": # 0-7 are octal escapes - if letter in "abfnrtuvxNU": - continue - with self.assertWarns(DeprecationWarning): - eval(r"'\%s'" % letter) - class StringModuleTest(unittest.TestCase): def test_formatter_parser(self): def parse(format): -- cgit v1.2.1 From 3d0d1e31a864f8eac69b4fd63ccd947fb0d7c58b Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Mon, 3 Oct 2016 09:04:58 -0700 Subject: Issue #28217: Adds _testconsole module to test console input. Fixes some issues found by the tests. --- Lib/test/test_winconsoleio.py | 70 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 8 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_winconsoleio.py b/Lib/test/test_winconsoleio.py index ec26f068fc..f2cccbc0fe 100644 --- a/Lib/test/test_winconsoleio.py +++ b/Lib/test/test_winconsoleio.py @@ -1,12 +1,4 @@ '''Tests for WindowsConsoleIO - -Unfortunately, most testing requires interactive use, since we have no -API to read back from a real console, and this class is only for use -with real consoles. - -Instead, we validate that basic functionality such as opening, closing -and in particular fileno() work, but are forced to leave real testing -to real people with real keyborads. ''' import io @@ -16,6 +8,8 @@ import sys if sys.platform != 'win32': raise unittest.SkipTest("test only relevant on win32") +from _testconsole import write_input + ConIO = io._WindowsConsoleIO class WindowsConsoleIOTests(unittest.TestCase): @@ -83,5 +77,65 @@ class WindowsConsoleIOTests(unittest.TestCase): f.close() f.close() + def assertStdinRoundTrip(self, text): + stdin = open('CONIN$', 'r') + old_stdin = sys.stdin + try: + sys.stdin = stdin + write_input( + stdin.buffer.raw, + (text + '\r\n').encode('utf-16-le', 'surrogatepass') + ) + actual = input() + finally: + sys.stdin = old_stdin + self.assertEqual(actual, text) + + def test_input(self): + # ASCII + self.assertStdinRoundTrip('abc123') + # Non-ASCII + self.assertStdinRoundTrip('ϼўТλФЙ') + # Combining characters + self.assertStdinRoundTrip('A͏B ﬖ̳AA̝') + # Non-BMP + self.assertStdinRoundTrip('\U00100000\U0010ffff\U0010fffd') + + def test_partial_reads(self): + # Test that reading less than 1 full character works when stdin + # contains multibyte UTF-8 sequences + source = 'ϼўТλФЙ\r\n'.encode('utf-16-le') + expected = 'ϼўТλФЙ\r\n'.encode('utf-8') + for read_count in range(1, 16): + stdin = open('CONIN$', 'rb', buffering=0) + write_input(stdin, source) + + actual = b'' + while not actual.endswith(b'\n'): + b = stdin.read(read_count) + actual += b + + self.assertEqual(actual, expected, 'stdin.read({})'.format(read_count)) + stdin.close() + + def test_partial_surrogate_reads(self): + # Test that reading less than 1 full character works when stdin + # contains surrogate pairs that cannot be decoded to UTF-8 without + # reading an extra character. + source = '\U00101FFF\U00101001\r\n'.encode('utf-16-le') + expected = '\U00101FFF\U00101001\r\n'.encode('utf-8') + for read_count in range(1, 16): + stdin = open('CONIN$', 'rb', buffering=0) + write_input(stdin, source) + + actual = b'' + while not actual.endswith(b'\n'): + b = stdin.read(read_count) + actual += b + + self.assertEqual(actual, expected, 'stdin.read({})'.format(read_count)) + stdin.close() + + if __name__ == "__main__": unittest.main() -- cgit v1.2.1 From e275d927247135d4d66e8fd0bd6533fe8537ba83 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Tue, 4 Oct 2016 20:41:20 +0300 Subject: Issue #28229: lzma module now supports pathlib --- Lib/lzma.py | 18 ++++++++++-------- Lib/test/test_lzma.py | 22 ++++++++++++++++++++++ 2 files changed, 32 insertions(+), 8 deletions(-) (limited to 'Lib') diff --git a/Lib/lzma.py b/Lib/lzma.py index 7dff1c319a..0817b872d2 100644 --- a/Lib/lzma.py +++ b/Lib/lzma.py @@ -23,6 +23,7 @@ __all__ = [ import builtins import io +import os from _lzma import * from _lzma import _encode_filter_properties, _decode_filter_properties import _compression @@ -49,9 +50,10 @@ class LZMAFile(_compression.BaseStream): format=None, check=-1, preset=None, filters=None): """Open an LZMA-compressed file in binary mode. - filename can be either an actual file name (given as a str or - bytes object), in which case the named file is opened, or it can - be an existing file object to read from or write to. + filename can be either an actual file name (given as a str, + bytes, or PathLike object), in which case the named file is + opened, or it can be an existing file object to read from or + write to. mode can be "r" for reading (default), "w" for (over)writing, "x" for creating exclusively, or "a" for appending. These can @@ -112,7 +114,7 @@ class LZMAFile(_compression.BaseStream): else: raise ValueError("Invalid mode: {!r}".format(mode)) - if isinstance(filename, (str, bytes)): + if isinstance(filename, (str, bytes, os.PathLike)): if "b" not in mode: mode += "b" self._fp = builtins.open(filename, mode) @@ -122,7 +124,7 @@ class LZMAFile(_compression.BaseStream): self._fp = filename self._mode = mode_code else: - raise TypeError("filename must be a str or bytes object, or a file") + raise TypeError("filename must be a str, bytes, file or PathLike object") if self._mode == _MODE_READ: raw = _compression.DecompressReader(self._fp, LZMADecompressor, @@ -263,9 +265,9 @@ def open(filename, mode="rb", *, encoding=None, errors=None, newline=None): """Open an LZMA-compressed file in binary or text mode. - filename can be either an actual file name (given as a str or bytes - object), in which case the named file is opened, or it can be an - existing file object to read from or write to. + filename can be either an actual file name (given as a str, bytes, + or PathLike object), in which case the named file is opened, or it + can be an existing file object to read from or write to. The mode argument can be "r", "rb" (default), "w", "wb", "x", "xb", "a", or "ab" for binary mode, or "rt", "wt", "xt", or "at" for text diff --git a/Lib/test/test_lzma.py b/Lib/test/test_lzma.py index 228db66c43..fdc8e11f63 100644 --- a/Lib/test/test_lzma.py +++ b/Lib/test/test_lzma.py @@ -1,6 +1,7 @@ import _compression from io import BytesIO, UnsupportedOperation, DEFAULT_BUFFER_SIZE import os +import pathlib import pickle import random import unittest @@ -488,6 +489,16 @@ class FileTestCase(unittest.TestCase): with LZMAFile(BytesIO(), "a") as f: pass + def test_init_with_PathLike_filename(self): + filename = pathlib.Path(TESTFN) + with TempFile(filename, COMPRESSED_XZ): + with LZMAFile(filename) as f: + self.assertEqual(f.read(), INPUT) + with LZMAFile(filename, "a") as f: + f.write(INPUT) + with LZMAFile(filename) as f: + self.assertEqual(f.read(), INPUT * 2) + def test_init_with_filename(self): with TempFile(TESTFN, COMPRESSED_XZ): with LZMAFile(TESTFN) as f: @@ -1180,6 +1191,17 @@ class OpenTestCase(unittest.TestCase): with lzma.open(TESTFN, "rb") as f: self.assertEqual(f.read(), INPUT * 2) + def test_with_pathlike_filename(self): + filename = pathlib.Path(TESTFN) + with TempFile(filename): + with lzma.open(filename, "wb") as f: + f.write(INPUT) + with open(filename, "rb") as f: + file_data = lzma.decompress(f.read()) + self.assertEqual(file_data, INPUT) + with lzma.open(filename, "rb") as f: + self.assertEqual(f.read(), INPUT) + def test_bad_params(self): # Test invalid parameter combinations. with self.assertRaises(ValueError): -- cgit v1.2.1 From bdae36ef359da689d1fed626c9d9d2dad5dc069c Mon Sep 17 00:00:00 2001 From: Steven D'Aprano Date: Wed, 5 Oct 2016 03:24:45 +1100 Subject: Issue #27181 remove geometric_mean and defer for 3.7. --- Lib/statistics.py | 269 +------------------------------------------- Lib/test/test_statistics.py | 267 ------------------------------------------- 2 files changed, 1 insertion(+), 535 deletions(-) (limited to 'Lib') diff --git a/Lib/statistics.py b/Lib/statistics.py index 7d53e0c0e2..30fe55c86a 100644 --- a/Lib/statistics.py +++ b/Lib/statistics.py @@ -11,7 +11,6 @@ Calculating averages Function Description ================== ============================================= mean Arithmetic mean (average) of data. -geometric_mean Geometric mean of data. harmonic_mean Harmonic mean of data. median Median (middle value) of data. median_low Low median of data. @@ -80,7 +79,7 @@ A single exception is defined: StatisticsError is a subclass of ValueError. __all__ = [ 'StatisticsError', 'pstdev', 'pvariance', 'stdev', 'variance', 'median', 'median_low', 'median_high', 'median_grouped', - 'mean', 'mode', 'geometric_mean', 'harmonic_mean', + 'mean', 'mode', 'harmonic_mean', ] import collections @@ -287,229 +286,6 @@ def _fail_neg(values, errmsg='negative value'): yield x -class _nroot_NS: - """Hands off! Don't touch! - - Everything inside this namespace (class) is an even-more-private - implementation detail of the private _nth_root function. - """ - # This class exists only to be used as a namespace, for convenience - # of being able to keep the related functions together, and to - # collapse the group in an editor. If this were C# or C++, I would - # use a Namespace, but the closest Python has is a class. - # - # FIXME possibly move this out into a separate module? - # That feels like overkill, and may encourage people to treat it as - # a public feature. - def __init__(self): - raise TypeError('namespace only, do not instantiate') - - def nth_root(x, n): - """Return the positive nth root of numeric x. - - This may be more accurate than ** or pow(): - - >>> math.pow(1000, 1.0/3) #doctest:+SKIP - 9.999999999999998 - - >>> _nth_root(1000, 3) - 10.0 - >>> _nth_root(11**5, 5) - 11.0 - >>> _nth_root(2, 12) - 1.0594630943592953 - - """ - if not isinstance(n, int): - raise TypeError('degree n must be an int') - if n < 2: - raise ValueError('degree n must be 2 or more') - if isinstance(x, decimal.Decimal): - return _nroot_NS.decimal_nroot(x, n) - elif isinstance(x, numbers.Real): - return _nroot_NS.float_nroot(x, n) - else: - raise TypeError('expected a number, got %s') % type(x).__name__ - - def float_nroot(x, n): - """Handle nth root of Reals, treated as a float.""" - assert isinstance(n, int) and n > 1 - if x < 0: - raise ValueError('domain error: root of negative number') - elif x == 0: - return math.copysign(0.0, x) - elif x > 0: - try: - isinfinity = math.isinf(x) - except OverflowError: - return _nroot_NS.bignum_nroot(x, n) - else: - if isinfinity: - return float('inf') - else: - return _nroot_NS.nroot(x, n) - else: - assert math.isnan(x) - return float('nan') - - def nroot(x, n): - """Calculate x**(1/n), then improve the answer.""" - # This uses math.pow() to calculate an initial guess for the root, - # then uses the iterated nroot algorithm to improve it. - # - # By my testing, about 8% of the time the iterated algorithm ends - # up converging to a result which is less accurate than the initial - # guess. [FIXME: is this still true?] In that case, we use the - # guess instead of the "improved" value. This way, we're never - # less accurate than math.pow(). - r1 = math.pow(x, 1.0/n) - eps1 = abs(r1**n - x) - if eps1 == 0.0: - # r1 is the exact root, so we're done. By my testing, this - # occurs about 80% of the time for x < 1 and 30% of the - # time for x > 1. - return r1 - else: - try: - r2 = _nroot_NS.iterated_nroot(x, n, r1) - except RuntimeError: - return r1 - else: - eps2 = abs(r2**n - x) - if eps1 < eps2: - return r1 - return r2 - - def iterated_nroot(a, n, g): - """Return the nth root of a, starting with guess g. - - This is a special case of Newton's Method. - https://en.wikipedia.org/wiki/Nth_root_algorithm - """ - np = n - 1 - def iterate(r): - try: - return (np*r + a/math.pow(r, np))/n - except OverflowError: - # If r is large enough, r**np may overflow. If that - # happens, r**-np will be small, but not necessarily zero. - return (np*r + a*math.pow(r, -np))/n - # With a good guess, such as g = a**(1/n), this will converge in - # only a few iterations. However a poor guess can take thousands - # of iterations to converge, if at all. We guard against poor - # guesses by setting an upper limit to the number of iterations. - r1 = g - r2 = iterate(g) - for i in range(1000): - if r1 == r2: - break - # Use Floyd's cycle-finding algorithm to avoid being trapped - # in a cycle. - # https://en.wikipedia.org/wiki/Cycle_detection#Tortoise_and_hare - r1 = iterate(r1) - r2 = iterate(iterate(r2)) - else: - # If the guess is particularly bad, the above may fail to - # converge in any reasonable time. - raise RuntimeError('nth-root failed to converge') - return r2 - - def decimal_nroot(x, n): - """Handle nth root of Decimals.""" - assert isinstance(x, decimal.Decimal) - assert isinstance(n, int) - if x.is_snan(): - # Signalling NANs always raise. - raise decimal.InvalidOperation('nth-root of snan') - if x.is_qnan(): - # Quiet NANs only raise if the context is set to raise, - # otherwise return a NAN. - ctx = decimal.getcontext() - if ctx.traps[decimal.InvalidOperation]: - raise decimal.InvalidOperation('nth-root of nan') - else: - # Preserve the input NAN. - return x - if x < 0: - raise ValueError('domain error: root of negative number') - if x.is_infinite(): - return x - # FIXME this hasn't had the extensive testing of the float - # version _iterated_nroot so there's possibly some buggy - # corner cases buried in here. Can it overflow? Fail to - # converge or get trapped in a cycle? Converge to a less - # accurate root? - np = n - 1 - def iterate(r): - return (np*r + x/r**np)/n - r0 = x**(decimal.Decimal(1)/n) - assert isinstance(r0, decimal.Decimal) - r1 = iterate(r0) - while True: - if r1 == r0: - return r1 - r0, r1 = r1, iterate(r1) - - def bignum_nroot(x, n): - """Return the nth root of a positive huge number.""" - assert x > 0 - # I state without proof that ⁿ√x ≈ ⁿ√2·ⁿ√(x//2) - # and that for sufficiently big x the error is acceptable. - # We now halve x until it is small enough to get the root. - m = 0 - while True: - x //= 2 - m += 1 - try: - y = float(x) - except OverflowError: - continue - break - a = _nroot_NS.nroot(y, n) - # At this point, we want the nth-root of 2**m, or 2**(m/n). - # We can write that as 2**(q + r/n) = 2**q * ⁿ√2**r where q = m//n. - q, r = divmod(m, n) - b = 2**q * _nroot_NS.nroot(2**r, n) - return a * b - - -# This is the (private) function for calculating nth roots: -_nth_root = _nroot_NS.nth_root -assert type(_nth_root) is type(lambda: None) - - -def _product(values): - """Return product of values as (exponent, mantissa).""" - errmsg = 'mixed Decimal and float is not supported' - prod = 1 - for x in values: - if isinstance(x, float): - break - prod *= x - else: - return (0, prod) - if isinstance(prod, Decimal): - raise TypeError(errmsg) - # Since floats can overflow easily, we calculate the product as a - # sort of poor-man's BigFloat. Given that: - # - # x = 2**p * m # p == power or exponent (scale), m = mantissa - # - # we can calculate the product of two (or more) x values as: - # - # x1*x2 = 2**p1*m1 * 2**p2*m2 = 2**(p1+p2)*(m1*m2) - # - mant, scale = 1, 0 #math.frexp(prod) # FIXME - for y in chain([x], values): - if isinstance(y, Decimal): - raise TypeError(errmsg) - m1, e1 = math.frexp(y) - m2, e2 = math.frexp(mant) - scale += (e1 + e2) - mant = m1*m2 - return (scale, mant) - - # === Measures of central tendency (averages) === def mean(data): @@ -538,49 +314,6 @@ def mean(data): return _convert(total/n, T) -def geometric_mean(data): - """Return the geometric mean of data. - - The geometric mean is appropriate when averaging quantities which - are multiplied together rather than added, for example growth rates. - Suppose an investment grows by 10% in the first year, falls by 5% in - the second, then grows by 12% in the third, what is the average rate - of growth over the three years? - - >>> geometric_mean([1.10, 0.95, 1.12]) - 1.0538483123382172 - - giving an average growth of 5.385%. Using the arithmetic mean will - give approximately 5.667%, which is too high. - - ``StatisticsError`` will be raised if ``data`` is empty, or any - element is less than zero. - """ - if iter(data) is data: - data = list(data) - errmsg = 'geometric mean does not support negative values' - n = len(data) - if n < 1: - raise StatisticsError('geometric_mean requires at least one data point') - elif n == 1: - x = data[0] - if isinstance(g, (numbers.Real, Decimal)): - if x < 0: - raise StatisticsError(errmsg) - return x - else: - raise TypeError('unsupported type') - else: - scale, prod = _product(_fail_neg(data, errmsg)) - r = _nth_root(prod, n) - if scale: - p, q = divmod(scale, n) - s = 2**p * _nth_root(2**q, n) - else: - s = 1 - return s*r - - def harmonic_mean(data): """Return the harmonic mean of data. diff --git a/Lib/test/test_statistics.py b/Lib/test/test_statistics.py index 6cac7095c2..4b3fd364a7 100644 --- a/Lib/test/test_statistics.py +++ b/Lib/test/test_statistics.py @@ -1010,273 +1010,6 @@ class FailNegTest(unittest.TestCase): self.assertEqual(errmsg, msg) -class Test_Product(NumericTestCase): - """Test the private _product function.""" - - def test_ints(self): - data = [1, 2, 5, 7, 9] - self.assertEqual(statistics._product(data), (0, 630)) - self.assertEqual(statistics._product(data*100), (0, 630**100)) - - def test_floats(self): - data = [1.0, 2.0, 4.0, 8.0] - self.assertEqual(statistics._product(data), (8, 0.25)) - - def test_overflow(self): - # Test with floats that overflow. - data = [1e300]*5 - self.assertEqual(statistics._product(data), (5980, 0.6928287951283193)) - - def test_fractions(self): - F = Fraction - data = [F(14, 23), F(69, 1), F(665, 529), F(299, 105), F(1683, 39)] - exp, mant = statistics._product(data) - self.assertEqual(exp, 0) - self.assertEqual(mant, F(2*3*7*11*17*19, 23)) - self.assertTrue(isinstance(mant, F)) - # Mixed Fraction and int. - data = [3, 25, F(2, 15)] - exp, mant = statistics._product(data) - self.assertEqual(exp, 0) - self.assertEqual(mant, F(10)) - self.assertTrue(isinstance(mant, F)) - - def test_decimal(self): - D = Decimal - data = [D('24.5'), D('17.6'), D('0.025'), D('1.3')] - expected = D('14.014000') - self.assertEqual(statistics._product(data), (0, expected)) - - def test_mixed_decimal_float(self): - # Test that mixed Decimal and float raises. - self.assertRaises(TypeError, statistics._product, [1.0, Decimal(1)]) - self.assertRaises(TypeError, statistics._product, [Decimal(1), 1.0]) - - -@unittest.skipIf(True, "FIXME: tests known to fail, see issue #27181") -class Test_Nth_Root(NumericTestCase): - """Test the functionality of the private _nth_root function.""" - - def setUp(self): - self.nroot = statistics._nth_root - - # --- Special values (infinities, NANs, zeroes) --- - - def test_float_NAN(self): - # Test that the root of a float NAN is a float NAN. - NAN = float('nan') - for n in range(2, 9): - with self.subTest(n=n): - result = self.nroot(NAN, n) - self.assertTrue(math.isnan(result)) - - def test_decimal_QNAN(self): - # Test the behaviour when taking the root of a Decimal quiet NAN. - NAN = decimal.Decimal('nan') - with decimal.localcontext() as ctx: - ctx.traps[decimal.InvalidOperation] = 1 - self.assertRaises(decimal.InvalidOperation, self.nroot, NAN, 5) - ctx.traps[decimal.InvalidOperation] = 0 - self.assertTrue(self.nroot(NAN, 5).is_qnan()) - - def test_decimal_SNAN(self): - # Test that taking the root of a Decimal sNAN always raises. - sNAN = decimal.Decimal('snan') - with decimal.localcontext() as ctx: - ctx.traps[decimal.InvalidOperation] = 1 - self.assertRaises(decimal.InvalidOperation, self.nroot, sNAN, 5) - ctx.traps[decimal.InvalidOperation] = 0 - self.assertRaises(decimal.InvalidOperation, self.nroot, sNAN, 5) - - def test_inf(self): - # Test that the root of infinity is infinity. - for INF in (float('inf'), decimal.Decimal('inf')): - for n in range(2, 9): - with self.subTest(n=n, inf=INF): - self.assertEqual(self.nroot(INF, n), INF) - - # FIXME: need to check Decimal zeroes too. - def test_zero(self): - # Test that the root of +0.0 is +0.0. - for n in range(2, 11): - with self.subTest(n=n): - result = self.nroot(+0.0, n) - self.assertEqual(result, 0.0) - self.assertEqual(sign(result), +1) - - # FIXME: need to check Decimal zeroes too. - def test_neg_zero(self): - # Test that the root of -0.0 is -0.0. - for n in range(2, 11): - with self.subTest(n=n): - result = self.nroot(-0.0, n) - self.assertEqual(result, 0.0) - self.assertEqual(sign(result), -1) - - # --- Test return types --- - - def check_result_type(self, x, n, outtype): - self.assertIsInstance(self.nroot(x, n), outtype) - class MySubclass(type(x)): - pass - self.assertIsInstance(self.nroot(MySubclass(x), n), outtype) - - def testDecimal(self): - # Test that Decimal arguments return Decimal results. - self.check_result_type(decimal.Decimal('33.3'), 3, decimal.Decimal) - - def testFloat(self): - # Test that other arguments return float results. - for x in (0.2, Fraction(11, 7), 91): - self.check_result_type(x, 6, float) - - # --- Test bad input --- - - def testBadOrderTypes(self): - # Test that nroot raises correctly when n has the wrong type. - for n in (5.0, 2j, None, 'x', b'x', [], {}, set(), sign): - with self.subTest(n=n): - self.assertRaises(TypeError, self.nroot, 2.5, n) - - def testBadOrderValues(self): - # Test that nroot raises correctly when n has a wrong value. - for n in (1, 0, -1, -2, -87): - with self.subTest(n=n): - self.assertRaises(ValueError, self.nroot, 2.5, n) - - def testBadTypes(self): - # Test that nroot raises correctly when x has the wrong type. - for x in (None, 'x', b'x', [], {}, set(), sign): - with self.subTest(x=x): - self.assertRaises(TypeError, self.nroot, x, 3) - - def testNegativeError(self): - # Test negative x raises correctly. - x = random.uniform(-20.0, -0.1) - assert x < 0 - for n in range(3, 7): - with self.subTest(x=x, n=n): - self.assertRaises(ValueError, self.nroot, x, n) - # And Decimal. - self.assertRaises(ValueError, self.nroot, Decimal(-27), 3) - - # --- Test that nroot is never worse than calling math.pow() --- - - def check_error_is_no_worse(self, x, n): - y = math.pow(x, n) - with self.subTest(x=x, n=n, y=y): - err1 = abs(self.nroot(y, n) - x) - err2 = abs(math.pow(y, 1.0/n) - x) - self.assertLessEqual(err1, err2) - - def testCompareWithPowSmall(self): - # Compare nroot with pow for small values of x. - for i in range(200): - x = random.uniform(1e-9, 1.0-1e-9) - n = random.choice(range(2, 16)) - self.check_error_is_no_worse(x, n) - - def testCompareWithPowMedium(self): - # Compare nroot with pow for medium-sized values of x. - for i in range(200): - x = random.uniform(1.0, 100.0) - n = random.choice(range(2, 16)) - self.check_error_is_no_worse(x, n) - - def testCompareWithPowLarge(self): - # Compare nroot with pow for largish values of x. - for i in range(200): - x = random.uniform(100.0, 10000.0) - n = random.choice(range(2, 16)) - self.check_error_is_no_worse(x, n) - - def testCompareWithPowHuge(self): - # Compare nroot with pow for huge values of x. - for i in range(200): - x = random.uniform(1e20, 1e50) - # We restrict the order here to avoid an Overflow error. - n = random.choice(range(2, 7)) - self.check_error_is_no_worse(x, n) - - # --- Test for numerically correct answers --- - - def testExactPowers(self): - # Test that small integer powers are calculated exactly. - for i in range(1, 51): - for n in range(2, 16): - if (i, n) == (35, 13): - # See testExpectedFailure35p13 - continue - with self.subTest(i=i, n=n): - x = i**n - self.assertEqual(self.nroot(x, n), i) - - def testExpectedFailure35p13(self): - # Test the expected failure 35**13 is almost exact. - x = 35**13 - err = abs(self.nroot(x, 13) - 35) - self.assertLessEqual(err, 0.000000001) - - def testOne(self): - # Test that the root of 1.0 is 1.0. - for n in range(2, 11): - with self.subTest(n=n): - self.assertEqual(self.nroot(1.0, n), 1.0) - - def testFraction(self): - # Test Fraction results. - x = Fraction(89, 75) - self.assertEqual(self.nroot(x**12, 12), float(x)) - - def testInt(self): - # Test int results. - x = 276 - self.assertEqual(self.nroot(x**24, 24), x) - - def testBigInt(self): - # Test that ints too big to convert to floats work. - bignum = 10**20 # That's not that big... - self.assertEqual(self.nroot(bignum**280, 280), bignum) - # Can we make it bigger? - hugenum = bignum**50 - # Make sure that it is too big to convert to a float. - try: - y = float(hugenum) - except OverflowError: - pass - else: - raise AssertionError('hugenum is not big enough') - self.assertEqual(self.nroot(hugenum, 50), float(bignum)) - - def testDecimal(self): - # Test Decimal results. - for s in '3.759 64.027 5234.338'.split(): - x = decimal.Decimal(s) - with self.subTest(x=x): - a = self.nroot(x**5, 5) - self.assertEqual(a, x) - a = self.nroot(x**17, 17) - self.assertEqual(a, x) - - def testFloat(self): - # Test float results. - for x in (3.04e-16, 18.25, 461.3, 1.9e17): - with self.subTest(x=x): - self.assertEqual(self.nroot(x**3, 3), x) - self.assertEqual(self.nroot(x**8, 8), x) - self.assertEqual(self.nroot(x**11, 11), x) - - -class Test_NthRoot_NS(unittest.TestCase): - """Test internals of the nth_root function, hidden in _nroot_NS.""" - - def test_class_cannot_be_instantiated(self): - # Test that _nroot_NS cannot be instantiated. - # It should be a namespace, like in C++ or C#, but Python - # lacks that feature and so we have to make do with a class. - self.assertRaises(TypeError, statistics._nroot_NS) - - # === Tests for public functions === class UnivariateCommonMixin: -- cgit v1.2.1 From 0ce5b57ce907fa0094ead2ed645c97f7bdaeaa23 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 5 Oct 2016 23:17:10 +0300 Subject: Issue #27998: Removed workarounds for supporting bytes paths on Windows in os.walk() function and glob module since os.scandir() now directly supports them. --- Lib/glob.py | 23 +++++++------------- Lib/os.py | 70 +++---------------------------------------------------------- 2 files changed, 10 insertions(+), 83 deletions(-) (limited to 'Lib') diff --git a/Lib/glob.py b/Lib/glob.py index 7c3cccb740..002cd92019 100644 --- a/Lib/glob.py +++ b/Lib/glob.py @@ -118,22 +118,13 @@ def _iterdir(dirname, dironly): else: dirname = os.curdir try: - if os.name == 'nt' and isinstance(dirname, bytes): - names = os.listdir(dirname) - if dironly: - for name in names: - if os.path.isdir(os.path.join(dirname, name)): - yield name - else: - yield from names - else: - with os.scandir(dirname) as it: - for entry in it: - try: - if not dironly or entry.is_dir(): - yield entry.name - except OSError: - pass + with os.scandir(dirname) as it: + for entry in it: + try: + if not dironly or entry.is_dir(): + yield entry.name + except OSError: + pass except OSError: return diff --git a/Lib/os.py b/Lib/os.py index 3e5f8cfda7..0c9107ca00 100644 --- a/Lib/os.py +++ b/Lib/os.py @@ -343,12 +343,9 @@ def walk(top, topdown=True, onerror=None, followlinks=False): # minor reason when (say) a thousand readable directories are still # left to visit. That logic is copied here. try: - if name == 'nt' and isinstance(top, bytes): - scandir_it = _dummy_scandir(top) - else: - # Note that scandir is global in this module due - # to earlier import-*. - scandir_it = scandir(top) + # Note that scandir is global in this module due + # to earlier import-*. + scandir_it = scandir(top) except OSError as error: if onerror is not None: onerror(error) @@ -417,67 +414,6 @@ def walk(top, topdown=True, onerror=None, followlinks=False): # Yield after recursion if going bottom up yield top, dirs, nondirs -class _DummyDirEntry: - """Dummy implementation of DirEntry - - Only used internally by os.walk(bytes). Since os.walk() doesn't need the - follow_symlinks parameter: don't implement it, always follow symbolic - links. - """ - - def __init__(self, dir, name): - self.name = name - self.path = path.join(dir, name) - # Mimick FindFirstFile/FindNextFile: we should get file attributes - # while iterating on a directory - self._stat = None - self._lstat = None - try: - self.stat(follow_symlinks=False) - except OSError: - pass - - def stat(self, *, follow_symlinks=True): - if follow_symlinks: - if self._stat is None: - self._stat = stat(self.path) - return self._stat - else: - if self._lstat is None: - self._lstat = stat(self.path, follow_symlinks=False) - return self._lstat - - def is_dir(self): - if self._lstat is not None and not self.is_symlink(): - # use the cache lstat - stat = self.stat(follow_symlinks=False) - return st.S_ISDIR(stat.st_mode) - - stat = self.stat() - return st.S_ISDIR(stat.st_mode) - - def is_symlink(self): - stat = self.stat(follow_symlinks=False) - return st.S_ISLNK(stat.st_mode) - -class _dummy_scandir: - # listdir-based implementation for bytes patches on Windows - def __init__(self, dir): - self.dir = dir - self.it = iter(listdir(dir)) - - def __iter__(self): - return self - - def __next__(self): - return _DummyDirEntry(self.dir, next(self.it)) - - def __enter__(self): - return self - - def __exit__(self, *args): - self.it = iter(()) - __all__.append("walk") if {open, stat} <= supports_dir_fd and {listdir, stat} <= supports_fd: -- cgit v1.2.1 From b6b0460c1217b17d1528a3d9ead508d8be6071ed Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Thu, 6 Oct 2016 14:31:23 -0700 Subject: Fixes issue28380: unittest.mock Mock autospec functions now properly support assert_called, assert_not_called, and assert_called_once. --- Lib/unittest/mock.py | 9 +++++++++ Lib/unittest/test/testmock/testpatch.py | 6 ++++++ 2 files changed, 15 insertions(+) (limited to 'Lib') diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index eaa9c3d585..f134919888 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -193,6 +193,12 @@ def _setup_func(funcopy, mock): def assert_called_with(*args, **kwargs): return mock.assert_called_with(*args, **kwargs) + def assert_called(*args, **kwargs): + return mock.assert_called(*args, **kwargs) + def assert_not_called(*args, **kwargs): + return mock.assert_not_called(*args, **kwargs) + def assert_called_once(*args, **kwargs): + return mock.assert_called_once(*args, **kwargs) def assert_called_once_with(*args, **kwargs): return mock.assert_called_once_with(*args, **kwargs) def assert_has_calls(*args, **kwargs): @@ -223,6 +229,9 @@ def _setup_func(funcopy, mock): funcopy.assert_has_calls = assert_has_calls funcopy.assert_any_call = assert_any_call funcopy.reset_mock = reset_mock + funcopy.assert_called = assert_called + funcopy.assert_not_called = assert_not_called + funcopy.assert_called_once = assert_called_once mock._mock_delegate = funcopy diff --git a/Lib/unittest/test/testmock/testpatch.py b/Lib/unittest/test/testmock/testpatch.py index 2e0c08f35f..fe4ecefd44 100644 --- a/Lib/unittest/test/testmock/testpatch.py +++ b/Lib/unittest/test/testmock/testpatch.py @@ -969,8 +969,14 @@ class PatchTest(unittest.TestCase): def test_autospec_function(self): @patch('%s.function' % __name__, autospec=True) def test(mock): + function.assert_not_called() + self.assertRaises(AssertionError, function.assert_called) + self.assertRaises(AssertionError, function.assert_called_once) function(1) + self.assertRaises(AssertionError, function.assert_not_called) function.assert_called_with(1) + function.assert_called() + function.assert_called_once() function(2, 3) function.assert_called_with(2, 3) -- cgit v1.2.1 From 0a6295517e3c339b27e602d3816746bf43506dc3 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 8 Oct 2016 12:34:25 +0300 Subject: Issue #28317: The disassembler now decodes FORMAT_VALUE argument. --- Lib/dis.py | 9 +++++++++ Lib/test/test_dis.py | 24 ++++++++++++++++++++++++ 2 files changed, 33 insertions(+) (limited to 'Lib') diff --git a/Lib/dis.py b/Lib/dis.py index 3a706be3c8..0794b7f743 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -16,6 +16,8 @@ del _opcodes_all _have_code = (types.MethodType, types.FunctionType, types.CodeType, classmethod, staticmethod, type) +FORMAT_VALUE = opmap['FORMAT_VALUE'] + def _try_compile(source, name): """Attempts to compile the given source, first as an expression and then as a statement if the first approach fails. @@ -314,6 +316,13 @@ def _get_instructions_bytes(code, varnames=None, names=None, constants=None, argrepr = argval elif op in hasfree: argval, argrepr = _get_name_info(arg, cells) + elif op == FORMAT_VALUE: + argval = ((None, str, repr, ascii)[arg & 0x3], bool(arg & 0x4)) + argrepr = ('', 'str', 'repr', 'ascii')[arg & 0x3] + if argval[1]: + if argrepr: + argrepr += ', ' + argrepr += 'with format' yield Instruction(opname[op], op, arg, argval, argrepr, offset, starts_line, is_jump_target) diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index f3193368e9..b9b5ac2667 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -301,6 +301,27 @@ dis_traceback = """\ TRACEBACK_CODE.co_firstlineno + 4, TRACEBACK_CODE.co_firstlineno + 5) +def _fstring(a, b, c, d): + return f'{a} {b:4} {c!r} {d!r:4}' + +dis_fstring = """\ +%3d 0 LOAD_FAST 0 (a) + 2 FORMAT_VALUE 0 + 4 LOAD_CONST 1 (' ') + 6 LOAD_FAST 1 (b) + 8 LOAD_CONST 2 ('4') + 10 FORMAT_VALUE 4 (with format) + 12 LOAD_CONST 1 (' ') + 14 LOAD_FAST 2 (c) + 16 FORMAT_VALUE 2 (repr) + 18 LOAD_CONST 1 (' ') + 20 LOAD_FAST 3 (d) + 22 LOAD_CONST 2 ('4') + 24 FORMAT_VALUE 6 (repr, with format) + 26 BUILD_STRING 7 + 28 RETURN_VALUE +""" % (_fstring.__code__.co_firstlineno + 1,) + def _g(x): yield x @@ -404,6 +425,9 @@ class DisTests(unittest.TestCase): gen_disas = self.get_disassembly(_g(1)) # Disassemble generator itself self.assertEqual(gen_disas, gen_func_disas) + def test_disassemble_fstring(self): + self.do_disassembly_test(_fstring, dis_fstring) + def test_dis_none(self): try: del sys.last_traceback -- cgit v1.2.1 From 4f2cc8050e711d172531ad0034bb4fb6524f4550 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 8 Oct 2016 21:50:45 +0300 Subject: Issue #28376: Creating instances of range_iterator by calling range_iterator type now is deprecated. Patch by Oren Milman. --- Lib/test/test_range.py | 49 ++++++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 23 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_range.py b/Lib/test/test_range.py index 7f1e985101..9e11e518f6 100644 --- a/Lib/test/test_range.py +++ b/Lib/test/test_range.py @@ -499,29 +499,32 @@ class RangeTest(unittest.TestCase): import _testcapi rangeiter_type = type(iter(range(0))) - # rangeiter_new doesn't take keyword arguments - with self.assertRaises(TypeError): - rangeiter_type(a=1) - - # rangeiter_new takes exactly 3 arguments - self.assertRaises(TypeError, rangeiter_type) - self.assertRaises(TypeError, rangeiter_type, 1) - self.assertRaises(TypeError, rangeiter_type, 1, 1) - self.assertRaises(TypeError, rangeiter_type, 1, 1, 1, 1) - - # start, stop and stop must fit in C long - for good_val in [_testcapi.LONG_MAX, _testcapi.LONG_MIN]: - rangeiter_type(good_val, good_val, good_val) - for bad_val in [_testcapi.LONG_MAX + 1, _testcapi.LONG_MIN - 1]: - self.assertRaises(OverflowError, - rangeiter_type, bad_val, 1, 1) - self.assertRaises(OverflowError, - rangeiter_type, 1, bad_val, 1) - self.assertRaises(OverflowError, - rangeiter_type, 1, 1, bad_val) - - # step mustn't be zero - self.assertRaises(ValueError, rangeiter_type, 1, 1, 0) + self.assertWarns(DeprecationWarning, rangeiter_type, 1, 3, 1) + + with test.support.check_warnings(('', DeprecationWarning)): + # rangeiter_new doesn't take keyword arguments + with self.assertRaises(TypeError): + rangeiter_type(a=1) + + # rangeiter_new takes exactly 3 arguments + self.assertRaises(TypeError, rangeiter_type) + self.assertRaises(TypeError, rangeiter_type, 1) + self.assertRaises(TypeError, rangeiter_type, 1, 1) + self.assertRaises(TypeError, rangeiter_type, 1, 1, 1, 1) + + # start, stop and stop must fit in C long + for good_val in [_testcapi.LONG_MAX, _testcapi.LONG_MIN]: + rangeiter_type(good_val, good_val, good_val) + for bad_val in [_testcapi.LONG_MAX + 1, _testcapi.LONG_MIN - 1]: + self.assertRaises(OverflowError, + rangeiter_type, bad_val, 1, 1) + self.assertRaises(OverflowError, + rangeiter_type, 1, bad_val, 1) + self.assertRaises(OverflowError, + rangeiter_type, 1, 1, bad_val) + + # step mustn't be zero + self.assertRaises(ValueError, rangeiter_type, 1, 1, 0) def test_slice(self): def check(start, stop, step=None): -- cgit v1.2.1 From 9f35fd9eea76fa28832eb7a934ba8698f87e2037 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Sat, 8 Oct 2016 12:37:33 -0700 Subject: Issue #28162: Fixes Ctrl+Z handling in console readall() --- Lib/test/test_winconsoleio.py | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_winconsoleio.py b/Lib/test/test_winconsoleio.py index f2cccbc0fe..b1a2f7a302 100644 --- a/Lib/test/test_winconsoleio.py +++ b/Lib/test/test_winconsoleio.py @@ -107,16 +107,15 @@ class WindowsConsoleIOTests(unittest.TestCase): source = 'ϼўТλФЙ\r\n'.encode('utf-16-le') expected = 'ϼўТλФЙ\r\n'.encode('utf-8') for read_count in range(1, 16): - stdin = open('CONIN$', 'rb', buffering=0) - write_input(stdin, source) + with open('CONIN$', 'rb', buffering=0) as stdin: + write_input(stdin, source) - actual = b'' - while not actual.endswith(b'\n'): - b = stdin.read(read_count) - actual += b + actual = b'' + while not actual.endswith(b'\n'): + b = stdin.read(read_count) + actual += b - self.assertEqual(actual, expected, 'stdin.read({})'.format(read_count)) - stdin.close() + self.assertEqual(actual, expected, 'stdin.read({})'.format(read_count)) def test_partial_surrogate_reads(self): # Test that reading less than 1 full character works when stdin @@ -125,17 +124,24 @@ class WindowsConsoleIOTests(unittest.TestCase): source = '\U00101FFF\U00101001\r\n'.encode('utf-16-le') expected = '\U00101FFF\U00101001\r\n'.encode('utf-8') for read_count in range(1, 16): - stdin = open('CONIN$', 'rb', buffering=0) - write_input(stdin, source) + with open('CONIN$', 'rb', buffering=0) as stdin: + write_input(stdin, source) - actual = b'' - while not actual.endswith(b'\n'): - b = stdin.read(read_count) - actual += b + actual = b'' + while not actual.endswith(b'\n'): + b = stdin.read(read_count) + actual += b - self.assertEqual(actual, expected, 'stdin.read({})'.format(read_count)) - stdin.close() + self.assertEqual(actual, expected, 'stdin.read({})'.format(read_count)) + def test_ctrl_z(self): + with open('CONIN$', 'rb', buffering=0) as stdin: + source = '\xC4\x1A\r\n'.encode('utf-16-le') + expected = '\xC4'.encode('utf-8') + write_input(stdin, source) + a, b = stdin.read(1), stdin.readall() + self.assertEqual(expected[0:1], a) + self.assertEqual(expected[1:], b) if __name__ == "__main__": unittest.main() -- cgit v1.2.1 From 553d3108ffde8deed850a944a66a344c2fb14fd2 Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Sun, 9 Oct 2016 14:44:47 +0900 Subject: Issue #26801: Added C implementation of asyncio.Future. Original patch by Yury Selivanov. --- Lib/asyncio/futures.py | 94 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 37 deletions(-) (limited to 'Lib') diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py index bcd4d16b9d..7c5b1aa745 100644 --- a/Lib/asyncio/futures.py +++ b/Lib/asyncio/futures.py @@ -120,6 +120,46 @@ def isfuture(obj): return getattr(obj, '_asyncio_future_blocking', None) is not None +def _format_callbacks(cb): + """helper function for Future.__repr__""" + size = len(cb) + if not size: + cb = '' + + def format_cb(callback): + return events._format_callback_source(callback, ()) + + if size == 1: + cb = format_cb(cb[0]) + elif size == 2: + cb = '{}, {}'.format(format_cb(cb[0]), format_cb(cb[1])) + elif size > 2: + cb = '{}, <{} more>, {}'.format(format_cb(cb[0]), + size-2, + format_cb(cb[-1])) + return 'cb=[%s]' % cb + + +def _future_repr_info(future): + # (Future) -> str + """helper function for Future.__repr__""" + info = [future._state.lower()] + if future._state == _FINISHED: + if future._exception is not None: + info.append('exception={!r}'.format(future._exception)) + else: + # use reprlib to limit the length of the output, especially + # for very long strings + result = reprlib.repr(future._result) + info.append('result={}'.format(result)) + if future._callbacks: + info.append(_format_callbacks(future._callbacks)) + if future._source_traceback: + frame = future._source_traceback[-1] + info.append('created at %s:%s' % (frame[0], frame[1])) + return info + + class Future: """This class is *almost* compatible with concurrent.futures.Future. @@ -172,45 +212,10 @@ class Future: if self._loop.get_debug(): self._source_traceback = traceback.extract_stack(sys._getframe(1)) - def __format_callbacks(self): - cb = self._callbacks - size = len(cb) - if not size: - cb = '' - - def format_cb(callback): - return events._format_callback_source(callback, ()) - - if size == 1: - cb = format_cb(cb[0]) - elif size == 2: - cb = '{}, {}'.format(format_cb(cb[0]), format_cb(cb[1])) - elif size > 2: - cb = '{}, <{} more>, {}'.format(format_cb(cb[0]), - size-2, - format_cb(cb[-1])) - return 'cb=[%s]' % cb - - def _repr_info(self): - info = [self._state.lower()] - if self._state == _FINISHED: - if self._exception is not None: - info.append('exception={!r}'.format(self._exception)) - else: - # use reprlib to limit the length of the output, especially - # for very long strings - result = reprlib.repr(self._result) - info.append('result={}'.format(result)) - if self._callbacks: - info.append(self.__format_callbacks()) - if self._source_traceback: - frame = self._source_traceback[-1] - info.append('created at %s:%s' % (frame[0], frame[1])) - return info + _repr_info = _future_repr_info def __repr__(self): - info = self._repr_info() - return '<%s %s>' % (self.__class__.__name__, ' '.join(info)) + return '<%s %s>' % (self.__class__.__name__, ' '.join(self._repr_info())) # On Python 3.3 and older, objects with a destructor part of a reference # cycle are never destroyed. It's not more the case on Python 3.4 thanks @@ -426,6 +431,21 @@ def _copy_future_state(source, dest): dest.set_result(result) +try: + import _futures +except ImportError: + pass +else: + _futures._init_module( + traceback.extract_stack, + events.get_event_loop, + _future_repr_info, + InvalidStateError, + CancelledError) + + Future = _futures.Future + + def _chain_future(source, destination): """Chain two futures so that when one completes, so does the other. -- cgit v1.2.1 From 01c29c2b0cbe7645628250701cdda38f6d751d66 Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Mon, 10 Oct 2016 00:38:21 +0000 Subject: Issue #28394: More typo fixes for 3.6+ --- Lib/test/test_ssl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index ad30105b0f..d203cddbdf 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -3475,7 +3475,7 @@ if _have_threads: client_context.verify_mode = ssl.CERT_REQUIRED client_context.load_verify_locations(SIGNING_CA) - # first conncetion without session + # first connection without session stats = server_params_test(client_context, server_context) session = stats['session'] self.assertTrue(session.id) -- cgit v1.2.1 From f1d7368a6f18a8906b7322f6c757ca688cbd7c00 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Mon, 10 Oct 2016 16:02:26 -0400 Subject: Update pydoc topics for 3.6.0b2 --- Lib/pydoc_data/topics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/pydoc_data/topics.py b/Lib/pydoc_data/topics.py index 3579484fd0..aaf2ce65f1 100644 --- a/Lib/pydoc_data/topics.py +++ b/Lib/pydoc_data/topics.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Autogenerated by Sphinx on Mon Sep 12 10:47:11 2016 +# Autogenerated by Sphinx on Mon Oct 10 15:59:17 2016 topics = {'assert': '\n' 'The "assert" statement\n' '**********************\n' -- cgit v1.2.1 From 88187a0c9e4aae7921906ff2d53de7c3a29c9258 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Wed, 12 Oct 2016 01:42:10 -0400 Subject: Issue #18844: Fix-up examples for random.choices(). Remove over-specified test. --- Lib/test/test_random.py | 4 ---- 1 file changed, 4 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py index 0dfc290824..840f3e7ce8 100644 --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -178,8 +178,6 @@ class TestBasicOps: self.assertTrue(set(choices(data, weights=None, k=5)) <= set(data)) with self.assertRaises(ValueError): choices(data, [1,2], k=5) # len(weights) != len(population) - with self.assertRaises(IndexError): - choices(data, [0]*4, k=5) # weights sum to zero with self.assertRaises(TypeError): choices(data, 10, k=5) # non-iterable weights with self.assertRaises(TypeError): @@ -194,8 +192,6 @@ class TestBasicOps: with self.assertRaises(ValueError): choices(data, cum_weights=[1,2], k=5) # len(weights) != len(population) - with self.assertRaises(IndexError): - choices(data, cum_weights=[0]*4, k=5) # cum_weights sum to zero with self.assertRaises(TypeError): choices(data, cum_weights=10, k=5) # non-iterable cum_weights with self.assertRaises(TypeError): -- cgit v1.2.1 From dd5f5b622be7217e99b327e8c4ab4660141df1b8 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Fri, 14 Oct 2016 01:19:38 -0400 Subject: Issue #18844: Add more tests --- Lib/test/test_random.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py index 840f3e7ce8..4d5a8749c7 100644 --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -205,6 +205,20 @@ class TestBasicOps: ]: self.assertTrue(set(choices(data, cum_weights=weights, k=5)) <= set(data)) + # Test weight focused on a single element of the population + self.assertEqual(choices('abcd', [1, 0, 0, 0]), ['a']) + self.assertEqual(choices('abcd', [0, 1, 0, 0]), ['b']) + self.assertEqual(choices('abcd', [0, 0, 1, 0]), ['c']) + self.assertEqual(choices('abcd', [0, 0, 0, 1]), ['d']) + + # Test consistency with random.choice() for empty population + with self.assertRaises(IndexError): + choices([], k=1) + with self.assertRaises(IndexError): + choices([], weights=[], k=1) + with self.assertRaises(IndexError): + choices([], cum_weights=[], k=5) + def test_gauss(self): # Ensure that the seed() method initializes all the hidden state. In # particular, through 2.2.1 it failed to reset a piece of state used -- cgit v1.2.1 From a691252a91c4f93fd5bb5f7149f3ae15ec8734cf Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Sat, 15 Oct 2016 15:39:19 +0900 Subject: Issue #28428: Rename _futures module to _asyncio. It will have more speedup functions or classes other than asyncio.Future. --- Lib/asyncio/futures.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Lib') diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py index 7c5b1aa745..87ae30aa09 100644 --- a/Lib/asyncio/futures.py +++ b/Lib/asyncio/futures.py @@ -432,18 +432,18 @@ def _copy_future_state(source, dest): try: - import _futures + import _asyncio except ImportError: pass else: - _futures._init_module( + _asyncio._init_module( traceback.extract_stack, events.get_event_loop, _future_repr_info, InvalidStateError, CancelledError) - Future = _futures.Future + Future = _asyncio.Future def _chain_future(source, destination): -- cgit v1.2.1 From d339e1514cdf5dfaf7e16e5b5341bd5f9a199b58 Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Tue, 18 Oct 2016 11:48:14 +0900 Subject: Issue #28452: Remove _asyncio._init_module function --- Lib/asyncio/futures.py | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) (limited to 'Lib') diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py index 87ae30aa09..73215f50ef 100644 --- a/Lib/asyncio/futures.py +++ b/Lib/asyncio/futures.py @@ -431,21 +431,6 @@ def _copy_future_state(source, dest): dest.set_result(result) -try: - import _asyncio -except ImportError: - pass -else: - _asyncio._init_module( - traceback.extract_stack, - events.get_event_loop, - _future_repr_info, - InvalidStateError, - CancelledError) - - Future = _asyncio.Future - - def _chain_future(source, destination): """Chain two futures so that when one completes, so does the other. @@ -496,3 +481,11 @@ def wrap_future(future, *, loop=None): new_future = loop.create_future() _chain_future(future, new_future) return new_future + + +try: + import _asyncio +except ImportError: + pass +else: + Future = _asyncio.Future -- cgit v1.2.1 From c891f6c70b79a47f8c807fd501ab10254ae16108 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Tue, 18 Oct 2016 16:03:52 -0400 Subject: Issue #28471: Fix crash (GIL state related) in socket.setblocking --- Lib/test/test_socket.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index c9add6cc98..0cf7bfe293 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -4552,6 +4552,18 @@ class TestExceptions(unittest.TestCase): self.assertTrue(issubclass(socket.gaierror, OSError)) self.assertTrue(issubclass(socket.timeout, OSError)) + def test_setblocking_invalidfd(self): + # Regression test for issue #28471 + + sock0 = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) + sock = socket.socket( + socket.AF_INET, socket.SOCK_STREAM, 0, sock0.fileno()) + sock0.close() + + with self.assertRaises(OSError): + sock.setblocking(False) + + @unittest.skipUnless(sys.platform == 'linux', 'Linux specific test') class TestLinuxAbstractNamespace(unittest.TestCase): -- cgit v1.2.1 From c612b99cfb7dc6e58fa313ed998ec36b3930815c Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Wed, 19 Oct 2016 11:00:26 +0200 Subject: Issue #26944: Fix test_posix for Android where 'id -G' is entirely wrong or missing the effective gid. --- Lib/test/test_posix.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index d2f58baae6..63c74cd80d 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -799,7 +799,11 @@ class PosixTester(unittest.TestCase): groups = idg.read().strip() ret = idg.close() - if ret is not None or not groups: + try: + idg_groups = set(int(g) for g in groups.split()) + except ValueError: + idg_groups = set() + if ret is not None or not idg_groups: raise unittest.SkipTest("need working 'id -G'") # Issues 16698: OS X ABIs prior to 10.6 have limits on getgroups() @@ -810,12 +814,11 @@ class PosixTester(unittest.TestCase): raise unittest.SkipTest("getgroups(2) is broken prior to 10.6") # 'id -G' and 'os.getgroups()' should return the same - # groups, ignoring order and duplicates. - # #10822 - it is implementation defined whether posix.getgroups() - # includes the effective gid so we include it anyway, since id -G does - self.assertEqual( - set([int(x) for x in groups.split()]), - set(posix.getgroups() + [posix.getegid()])) + # groups, ignoring order, duplicates, and the effective gid. + # #10822/#26944 - It is implementation defined whether + # posix.getgroups() includes the effective gid. + symdiff = idg_groups.symmetric_difference(posix.getgroups()) + self.assertTrue(not symdiff or symdiff == {posix.getegid()}) # tests for the posix *at functions follow -- cgit v1.2.1 From 3cb5752ff2b666ebe4bb08dfa92449e85074427e Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Thu, 20 Oct 2016 05:10:44 +0000 Subject: Issue #28480: Adjust or skip tests if multithreading is disabled --- Lib/test/test_asyncgen.py | 4 +++- Lib/test/test_logging.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_asyncgen.py b/Lib/test/test_asyncgen.py index 41b1b4f4b4..c24fbea5b0 100644 --- a/Lib/test/test_asyncgen.py +++ b/Lib/test/test_asyncgen.py @@ -1,4 +1,3 @@ -import asyncio import inspect import sys import types @@ -6,6 +5,9 @@ import unittest from unittest import mock +from test.support import import_module +asyncio = import_module("asyncio") + class AwaitException(Exception): pass diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index e45a982dbf..078a86b359 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -4304,7 +4304,7 @@ class MiscTestCase(unittest.TestCase): 'logProcesses', 'currentframe', 'PercentStyle', 'StrFormatStyle', 'StringTemplateStyle', 'Filterer', 'PlaceHolder', 'Manager', 'RootLogger', - 'root'} + 'root', 'threading'} support.check__all__(self, logging, blacklist=blacklist) -- cgit v1.2.1 From 50b3560ec96d48ae8588fb7cae40e00df7ecb0a3 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Thu, 20 Oct 2016 15:54:20 -0400 Subject: Issue #28492: Fix how StopIteration is raised in _asyncio.Future --- Lib/test/test_asyncio/test_futures.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py index d20eb687f9..6916b513e8 100644 --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -464,6 +464,19 @@ class FutureTests(test_utils.TestCase): futures._set_result_unless_cancelled(fut, 2) self.assertTrue(fut.cancelled()) + def test_future_stop_iteration_args(self): + fut = asyncio.Future(loop=self.loop) + fut.set_result((1, 2)) + fi = fut.__iter__() + result = None + try: + fi.send(None) + except StopIteration as ex: + result = ex.args[0] + else: + self.fail('StopIteration was expected') + self.assertEqual(result, (1, 2)) + class FutureDoneCallbackTests(test_utils.TestCase): -- cgit v1.2.1 From 10932fdf9bf1d675e49c8fdf083b46f9bc98b8c7 Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Thu, 20 Oct 2016 07:44:29 +0000 Subject: Issue #28471: Avoid ResourceWarning by detaching test socket --- Lib/test/test_socket.py | 1 + 1 file changed, 1 insertion(+) (limited to 'Lib') diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 0cf7bfe293..a56d8a4101 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -4559,6 +4559,7 @@ class TestExceptions(unittest.TestCase): sock = socket.socket( socket.AF_INET, socket.SOCK_STREAM, 0, sock0.fileno()) sock0.close() + self.addCleanup(sock.detach) with self.assertRaises(OSError): sock.setblocking(False) -- cgit v1.2.1 From 326e7fefd5a8fb6ea3ab39d64be424b23d7479b9 Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Thu, 20 Oct 2016 21:45:49 +0000 Subject: Issue #28484: Skip tests if GIL is not used or multithreading is disabled --- Lib/test/test_capi.py | 1 + Lib/test/test_regrtest.py | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index 5521e76112..d4faeb375e 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -630,6 +630,7 @@ class PyMemDebugTests(unittest.TestCase): regex = regex.format(ptr=self.PTR_REGEX) self.assertRegex(out, regex) + @unittest.skipUnless(threading, 'Test requires a GIL (multithreading)') def check_malloc_without_gil(self, code): out = self.check(code) expected = ('Fatal Python error: Python memory allocator called ' diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py index d43160470f..52909d833e 100644 --- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -696,7 +696,12 @@ class ArgsTestCase(BaseTestCase): code = TEST_INTERRUPTED test = self.create_test("sigint", code=code) - for multiprocessing in (False, True): + try: + import threading + tests = (False, True) + except ImportError: + tests = (False,) + for multiprocessing in tests: if multiprocessing: args = ("--slowest", "-j2", test) else: -- cgit v1.2.1 From a05b2b02b67c25551ab72b9544d3b3f6c05682df Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Fri, 21 Oct 2016 12:30:15 +0900 Subject: Issue #28448: Fix C implemented asyncio.Future didn't work on Windows --- Lib/asyncio/futures.py | 8 ++++---- Lib/asyncio/windows_events.py | 9 +++++++-- 2 files changed, 11 insertions(+), 6 deletions(-) (limited to 'Lib') diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py index 73215f50ef..e45d6aa387 100644 --- a/Lib/asyncio/futures.py +++ b/Lib/asyncio/futures.py @@ -247,10 +247,10 @@ class Future: if self._state != _PENDING: return False self._state = _CANCELLED - self._schedule_callbacks() + self.__schedule_callbacks() return True - def _schedule_callbacks(self): + def __schedule_callbacks(self): """Internal: Ask the event loop to call all callbacks. The callbacks are scheduled to be called as soon as possible. Also @@ -352,7 +352,7 @@ class Future: raise InvalidStateError('{}: {!r}'.format(self._state, self)) self._result = result self._state = _FINISHED - self._schedule_callbacks() + self.__schedule_callbacks() def set_exception(self, exception): """Mark the future done and set an exception. @@ -369,7 +369,7 @@ class Future: "and cannot be raised into a Future") self._exception = exception self._state = _FINISHED - self._schedule_callbacks() + self.__schedule_callbacks() if compat.PY34: self._log_traceback = True else: diff --git a/Lib/asyncio/windows_events.py b/Lib/asyncio/windows_events.py index 668fe1451b..b777dd065a 100644 --- a/Lib/asyncio/windows_events.py +++ b/Lib/asyncio/windows_events.py @@ -171,8 +171,13 @@ class _WaitCancelFuture(_BaseWaitHandleFuture): def cancel(self): raise RuntimeError("_WaitCancelFuture must not be cancelled") - def _schedule_callbacks(self): - super(_WaitCancelFuture, self)._schedule_callbacks() + def set_result(self, result): + super().set_result(result) + if self._done_callback is not None: + self._done_callback(self) + + def set_exception(self, exception): + super().set_exception(exception) if self._done_callback is not None: self._done_callback(self) -- cgit v1.2.1 From 1c5a7b9be54055018447d61a270b7c3ac4850b19 Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Fri, 21 Oct 2016 19:47:57 +0900 Subject: Issue #18219: Optimize csv.DictWriter for large number of columns. Patch by Mariatta Wijaya. --- Lib/csv.py | 2 +- Lib/test/test_csv.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/csv.py b/Lib/csv.py index 0481ea5586..0349e0bd11 100644 --- a/Lib/csv.py +++ b/Lib/csv.py @@ -145,7 +145,7 @@ class DictWriter: def _dict_to_list(self, rowdict): if self.extrasaction == "raise": - wrong_fields = [k for k in rowdict if k not in self.fieldnames] + wrong_fields = rowdict.keys() - self.fieldnames if wrong_fields: raise ValueError("dict contains fields not in fieldnames: " + ", ".join([repr(x) for x in wrong_fields])) diff --git a/Lib/test/test_csv.py b/Lib/test/test_csv.py index 7dcea9ccb3..03ab1840dd 100644 --- a/Lib/test/test_csv.py +++ b/Lib/test/test_csv.py @@ -626,6 +626,24 @@ class TestDictFields(unittest.TestCase): self.assertNotIn("'f2'", exception) self.assertIn("1", exception) + def test_typo_in_extrasaction_raises_error(self): + fileobj = StringIO() + self.assertRaises(ValueError, csv.DictWriter, fileobj, ['f1', 'f2'], + extrasaction="raised") + + def test_write_field_not_in_field_names_raise(self): + fileobj = StringIO() + writer = csv.DictWriter(fileobj, ['f1', 'f2'], extrasaction="raise") + dictrow = {'f0': 0, 'f1': 1, 'f2': 2, 'f3': 3} + self.assertRaises(ValueError, csv.DictWriter.writerow, writer, dictrow) + + def test_write_field_not_in_field_names_ignore(self): + fileobj = StringIO() + writer = csv.DictWriter(fileobj, ['f1', 'f2'], extrasaction="ignore") + dictrow = {'f0': 0, 'f1': 1, 'f2': 2, 'f3': 3} + csv.DictWriter.writerow(writer, dictrow) + self.assertEqual(fileobj.getvalue(), "1,2\r\n") + def test_read_dict_fields(self): with TemporaryFile("w+") as fileobj: fileobj.write("1,2,abc\r\n") -- cgit v1.2.1 From 3888fec2a7d9d5688a9ada16652bdbc11449869f Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 21 Oct 2016 17:09:17 +0300 Subject: Issue #28410: Added _PyErr_FormatFromCause() -- the helper for raising new exception with setting current exception as __cause__. _PyErr_FormatFromCause(exception, format, args...) is equivalent to Python raise exception(format % args) from sys.exc_info()[1] --- Lib/test/test_capi.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index d4faeb375e..6c3625d487 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -222,8 +222,8 @@ class CAPITest(unittest.TestCase): br'result with an error set\n' br'ValueError\n' br'\n' - br'During handling of the above exception, ' - br'another exception occurred:\n' + br'The above exception was the direct cause ' + br'of the following exception:\n' br'\n' br'SystemError: ' -- cgit v1.2.1 From d83848ee57d8584e4709d42eb3350b946450fb2c Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 21 Oct 2016 17:13:31 +0300 Subject: Issue #28214: Improved exception reporting for problematic __set_name__ attributes. --- Lib/test/test_subclassinit.py | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_subclassinit.py b/Lib/test/test_subclassinit.py index 0267e41717..6a12fb1ee0 100644 --- a/Lib/test/test_subclassinit.py +++ b/Lib/test/test_subclassinit.py @@ -133,20 +133,32 @@ class Test(unittest.TestCase): def test_set_name_error(self): class Descriptor: def __set_name__(self, owner, name): - raise RuntimeError + 1/0 - with self.assertRaises(RuntimeError): - class A: - d = Descriptor() + with self.assertRaises(RuntimeError) as cm: + class NotGoingToWork: + attr = Descriptor() + + exc = cm.exception + self.assertRegex(str(exc), r'\bNotGoingToWork\b') + self.assertRegex(str(exc), r'\battr\b') + self.assertRegex(str(exc), r'\bDescriptor\b') + self.assertIsInstance(exc.__cause__, ZeroDivisionError) def test_set_name_wrong(self): class Descriptor: def __set_name__(self): pass - with self.assertRaises(TypeError): - class A: - d = Descriptor() + with self.assertRaises(RuntimeError) as cm: + class NotGoingToWork: + attr = Descriptor() + + exc = cm.exception + self.assertRegex(str(exc), r'\bNotGoingToWork\b') + self.assertRegex(str(exc), r'\battr\b') + self.assertRegex(str(exc), r'\bDescriptor\b') + self.assertIsInstance(exc.__cause__, TypeError) def test_set_name_lookup(self): resolved = [] -- cgit v1.2.1 From 223140a058475830a2b00723ff03fefe2589db77 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Fri, 21 Oct 2016 17:13:40 -0400 Subject: Issue #28500: Fix asyncio to handle async gens GC from another thread. --- Lib/asyncio/base_events.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'Lib') diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index 5b5fcde438..e59b2b75ac 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -351,6 +351,9 @@ class BaseEventLoop(events.AbstractEventLoop): self._asyncgens.discard(agen) if not self.is_closed(): self.create_task(agen.aclose()) + # Wake up the loop if the finalizer was called from + # a different thread. + self._write_to_self() def _asyncgen_firstiter_hook(self, agen): if self._asyncgens_shutdown_called: -- cgit v1.2.1 From 2560ab5e3f648e30b521ee1e086a064e74373e26 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 23 Oct 2016 12:11:19 +0300 Subject: Issue #25953: re.sub() now raises an error for invalid numerical group reference in replacement template even if the pattern is not found in the string. Error message for invalid group reference now includes the group index and the position of the reference. Based on patch by SilentGhost. --- Lib/sre_parse.py | 18 ++++++++++-------- Lib/test/test_re.py | 43 ++++++++++++++++++++++--------------------- 2 files changed, 32 insertions(+), 29 deletions(-) (limited to 'Lib') diff --git a/Lib/sre_parse.py b/Lib/sre_parse.py index 3d38673c32..ab37fd3fe2 100644 --- a/Lib/sre_parse.py +++ b/Lib/sre_parse.py @@ -395,7 +395,7 @@ def _escape(source, escape, state): len(escape)) state.checklookbehindgroup(group, source) return GROUPREF, group - raise source.error("invalid group reference", len(escape)) + raise source.error("invalid group reference %d" % group, len(escape) - 1) if len(escape) == 2: if c in ASCIILETTERS: raise source.error("bad escape %s" % escape, len(escape)) @@ -725,8 +725,8 @@ def _parse(source, state, verbose): raise source.error("bad group number", len(condname) + 1) if condgroup >= MAXGROUPS: - raise source.error("invalid group reference", - len(condname) + 1) + msg = "invalid group reference %d" % condgroup + raise source.error(msg, len(condname) + 1) state.checklookbehindgroup(condgroup, source) elif char in FLAGS or char == "-": # flags @@ -883,7 +883,9 @@ def parse_template(source, pattern): literals = [] literal = [] lappend = literal.append - def addgroup(index): + def addgroup(index, pos): + if index > pattern.groups: + raise s.error("invalid group reference %d" % index, pos) if literal: literals.append(''.join(literal)) del literal[:] @@ -916,9 +918,9 @@ def parse_template(source, pattern): raise s.error("bad character in group name %r" % name, len(name) + 1) from None if index >= MAXGROUPS: - raise s.error("invalid group reference", + raise s.error("invalid group reference %d" % index, len(name) + 1) - addgroup(index) + addgroup(index, len(name) + 1) elif c == "0": if s.next in OCTDIGITS: this += sget() @@ -939,7 +941,7 @@ def parse_template(source, pattern): 'range 0-0o377' % this, len(this)) lappend(chr(c)) if not isoctal: - addgroup(int(this[1:])) + addgroup(int(this[1:]), len(this) - 1) else: try: this = chr(ESCAPES[this][1]) @@ -966,5 +968,5 @@ def expand_template(template, match): for index, group in groups: literals[index] = g(group) or empty except IndexError: - raise error("invalid group reference") + raise error("invalid group reference %d" % index) return empty.join(literals) diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index 4cdc59127a..3bd6d7b461 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -5,7 +5,6 @@ import locale import re from re import Scanner import sre_compile -import sre_constants import sys import string import traceback @@ -186,18 +185,19 @@ class ReTests(unittest.TestCase): r'octal escape value \777 outside of ' r'range 0-0o377', 0) - self.checkTemplateError('x', r'\1', 'x', 'invalid group reference') - self.checkTemplateError('x', r'\8', 'x', 'invalid group reference') - self.checkTemplateError('x', r'\9', 'x', 'invalid group reference') - self.checkTemplateError('x', r'\11', 'x', 'invalid group reference') - self.checkTemplateError('x', r'\18', 'x', 'invalid group reference') - self.checkTemplateError('x', r'\1a', 'x', 'invalid group reference') - self.checkTemplateError('x', r'\90', 'x', 'invalid group reference') - self.checkTemplateError('x', r'\99', 'x', 'invalid group reference') - self.checkTemplateError('x', r'\118', 'x', 'invalid group reference') # r'\11' + '8' - self.checkTemplateError('x', r'\11a', 'x', 'invalid group reference') - self.checkTemplateError('x', r'\181', 'x', 'invalid group reference') # r'\18' + '1' - self.checkTemplateError('x', r'\800', 'x', 'invalid group reference') # r'\80' + '0' + self.checkTemplateError('x', r'\1', 'x', 'invalid group reference 1', 1) + self.checkTemplateError('x', r'\8', 'x', 'invalid group reference 8', 1) + self.checkTemplateError('x', r'\9', 'x', 'invalid group reference 9', 1) + self.checkTemplateError('x', r'\11', 'x', 'invalid group reference 11', 1) + self.checkTemplateError('x', r'\18', 'x', 'invalid group reference 18', 1) + self.checkTemplateError('x', r'\1a', 'x', 'invalid group reference 1', 1) + self.checkTemplateError('x', r'\90', 'x', 'invalid group reference 90', 1) + self.checkTemplateError('x', r'\99', 'x', 'invalid group reference 99', 1) + self.checkTemplateError('x', r'\118', 'x', 'invalid group reference 11', 1) + self.checkTemplateError('x', r'\11a', 'x', 'invalid group reference 11', 1) + self.checkTemplateError('x', r'\181', 'x', 'invalid group reference 18', 1) + self.checkTemplateError('x', r'\800', 'x', 'invalid group reference 80', 1) + self.checkTemplateError('x', r'\8', '', 'invalid group reference 8', 1) # in python2.3 (etc), these loop endlessly in sre_parser.py self.assertEqual(re.sub('(((((((((((x)))))))))))', r'\11', 'x'), 'x') @@ -271,9 +271,9 @@ class ReTests(unittest.TestCase): self.checkTemplateError('(?Px)', r'\g<1a1>', 'xx', "bad character in group name '1a1'", 3) self.checkTemplateError('(?Px)', r'\g<2>', 'xx', - 'invalid group reference') + 'invalid group reference 2', 3) self.checkTemplateError('(?Px)', r'\2', 'xx', - 'invalid group reference') + 'invalid group reference 2', 1) with self.assertRaisesRegex(IndexError, "unknown group name 'ab'"): re.sub('(?Px)', r'\g', 'xx') self.assertEqual(re.sub('(?Px)|(?Py)', r'\g', 'xx'), '') @@ -558,10 +558,11 @@ class ReTests(unittest.TestCase): 'two branches', 10) def test_re_groupref_overflow(self): - self.checkTemplateError('()', r'\g<%s>' % sre_constants.MAXGROUPS, 'xx', - 'invalid group reference', 3) - self.checkPatternError(r'(?P)(?(%d))' % sre_constants.MAXGROUPS, - 'invalid group reference', 10) + from sre_constants import MAXGROUPS + self.checkTemplateError('()', r'\g<%s>' % MAXGROUPS, 'xx', + 'invalid group reference %d' % MAXGROUPS, 3) + self.checkPatternError(r'(?P)(?(%d))' % MAXGROUPS, + 'invalid group reference %d' % MAXGROUPS, 10) def test_re_groupref(self): self.assertEqual(re.match(r'^(\|)?([^()]+)\1$', '|a|').groups(), @@ -1007,7 +1008,7 @@ class ReTests(unittest.TestCase): self.checkPatternError(r"\567", r'octal escape value \567 outside of ' r'range 0-0o377', 0) - self.checkPatternError(r"\911", 'invalid group reference', 0) + self.checkPatternError(r"\911", 'invalid group reference 91', 1) self.checkPatternError(r"\x1", r'incomplete escape \x1', 0) self.checkPatternError(r"\x1z", r'incomplete escape \x1', 0) self.checkPatternError(r"\u123", r'incomplete escape \u123', 0) @@ -1061,7 +1062,7 @@ class ReTests(unittest.TestCase): self.checkPatternError(br"\567", r'octal escape value \567 outside of ' r'range 0-0o377', 0) - self.checkPatternError(br"\911", 'invalid group reference', 0) + self.checkPatternError(br"\911", 'invalid group reference 91', 1) self.checkPatternError(br"\x1", r'incomplete escape \x1', 0) self.checkPatternError(br"\x1z", r'incomplete escape \x1', 0) -- cgit v1.2.1 From e122bec5e8db911ed19a44ea4b0965572fb90c2b Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Sun, 23 Oct 2016 22:34:35 -0400 Subject: asyncio: Increase asyncio.Future test coverage; test both implementations. Also, add 'isfuture' to 'asyncio.futures.__all__', so that it's exposed as 'asyncio.isfuture'. --- Lib/asyncio/futures.py | 9 +- Lib/test/test_asyncio/test_futures.py | 156 ++++++++++++++++------------------ 2 files changed, 82 insertions(+), 83 deletions(-) (limited to 'Lib') diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py index e45d6aa387..b571130514 100644 --- a/Lib/asyncio/futures.py +++ b/Lib/asyncio/futures.py @@ -2,7 +2,7 @@ __all__ = ['CancelledError', 'TimeoutError', 'InvalidStateError', - 'Future', 'wrap_future', + 'Future', 'wrap_future', 'isfuture' ] import concurrent.futures._base @@ -389,6 +389,10 @@ class Future: __await__ = __iter__ # make compatible with 'await' expression +# Needed for testing purposes. +_PyFuture = Future + + def _set_result_unless_cancelled(fut, result): """Helper setting the result only if the future was not cancelled.""" if fut.cancelled(): @@ -488,4 +492,5 @@ try: except ImportError: pass else: - Future = _asyncio.Future + # _CFuture is needed for tests. + Future = _CFuture = _asyncio.Future diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py index 6916b513e8..2c17ef2c27 100644 --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -9,6 +9,7 @@ from unittest import mock import asyncio from asyncio import test_utils +from asyncio import futures try: from test import support except ImportError: @@ -93,14 +94,17 @@ class DuckTests(test_utils.TestCase): assert g is f -class FutureTests(test_utils.TestCase): +class BaseFutureTests: + + def _new_future(self, loop=None): + raise NotImplementedError def setUp(self): self.loop = self.new_test_loop() self.addCleanup(self.loop.close) def test_initial_state(self): - f = asyncio.Future(loop=self.loop) + f = self._new_future(loop=self.loop) self.assertFalse(f.cancelled()) self.assertFalse(f.done()) f.cancel() @@ -108,15 +112,15 @@ class FutureTests(test_utils.TestCase): def test_init_constructor_default_loop(self): asyncio.set_event_loop(self.loop) - f = asyncio.Future() + f = self._new_future() self.assertIs(f._loop, self.loop) def test_constructor_positional(self): # Make sure Future doesn't accept a positional argument - self.assertRaises(TypeError, asyncio.Future, 42) + self.assertRaises(TypeError, self._new_future, 42) def test_cancel(self): - f = asyncio.Future(loop=self.loop) + f = self._new_future(loop=self.loop) self.assertTrue(f.cancel()) self.assertTrue(f.cancelled()) self.assertTrue(f.done()) @@ -127,7 +131,7 @@ class FutureTests(test_utils.TestCase): self.assertFalse(f.cancel()) def test_result(self): - f = asyncio.Future(loop=self.loop) + f = self._new_future(loop=self.loop) self.assertRaises(asyncio.InvalidStateError, f.result) f.set_result(42) @@ -141,7 +145,7 @@ class FutureTests(test_utils.TestCase): def test_exception(self): exc = RuntimeError() - f = asyncio.Future(loop=self.loop) + f = self._new_future(loop=self.loop) self.assertRaises(asyncio.InvalidStateError, f.exception) # StopIteration cannot be raised into a Future - CPython issue26221 @@ -158,12 +162,12 @@ class FutureTests(test_utils.TestCase): self.assertFalse(f.cancel()) def test_exception_class(self): - f = asyncio.Future(loop=self.loop) + f = self._new_future(loop=self.loop) f.set_exception(RuntimeError) self.assertIsInstance(f.exception(), RuntimeError) def test_yield_from_twice(self): - f = asyncio.Future(loop=self.loop) + f = self._new_future(loop=self.loop) def fixture(): yield 'A' @@ -182,7 +186,7 @@ class FutureTests(test_utils.TestCase): def test_future_repr(self): self.loop.set_debug(True) - f_pending_debug = asyncio.Future(loop=self.loop) + f_pending_debug = self._new_future(loop=self.loop) frame = f_pending_debug._source_traceback[-1] self.assertEqual(repr(f_pending_debug), '' @@ -190,21 +194,21 @@ class FutureTests(test_utils.TestCase): f_pending_debug.cancel() self.loop.set_debug(False) - f_pending = asyncio.Future(loop=self.loop) + f_pending = self._new_future(loop=self.loop) self.assertEqual(repr(f_pending), '') f_pending.cancel() - f_cancelled = asyncio.Future(loop=self.loop) + f_cancelled = self._new_future(loop=self.loop) f_cancelled.cancel() self.assertEqual(repr(f_cancelled), '') - f_result = asyncio.Future(loop=self.loop) + f_result = self._new_future(loop=self.loop) f_result.set_result(4) self.assertEqual(repr(f_result), '') self.assertEqual(f_result.result(), 4) exc = RuntimeError() - f_exception = asyncio.Future(loop=self.loop) + f_exception = self._new_future(loop=self.loop) f_exception.set_exception(exc) self.assertEqual(repr(f_exception), '') @@ -215,7 +219,7 @@ class FutureTests(test_utils.TestCase): text = '%s() at %s:%s' % (func.__qualname__, filename, lineno) return re.escape(text) - f_one_callbacks = asyncio.Future(loop=self.loop) + f_one_callbacks = self._new_future(loop=self.loop) f_one_callbacks.add_done_callback(_fakefunc) fake_repr = func_repr(_fakefunc) self.assertRegex(repr(f_one_callbacks), @@ -224,7 +228,7 @@ class FutureTests(test_utils.TestCase): self.assertEqual(repr(f_one_callbacks), '') - f_two_callbacks = asyncio.Future(loop=self.loop) + f_two_callbacks = self._new_future(loop=self.loop) f_two_callbacks.add_done_callback(first_cb) f_two_callbacks.add_done_callback(last_cb) first_repr = func_repr(first_cb) @@ -233,7 +237,7 @@ class FutureTests(test_utils.TestCase): r'' % (first_repr, last_repr)) - f_many_callbacks = asyncio.Future(loop=self.loop) + f_many_callbacks = self._new_future(loop=self.loop) f_many_callbacks.add_done_callback(first_cb) for i in range(8): f_many_callbacks.add_done_callback(_fakefunc) @@ -248,31 +252,31 @@ class FutureTests(test_utils.TestCase): def test_copy_state(self): from asyncio.futures import _copy_future_state - f = asyncio.Future(loop=self.loop) + f = self._new_future(loop=self.loop) f.set_result(10) - newf = asyncio.Future(loop=self.loop) + newf = self._new_future(loop=self.loop) _copy_future_state(f, newf) self.assertTrue(newf.done()) self.assertEqual(newf.result(), 10) - f_exception = asyncio.Future(loop=self.loop) + f_exception = self._new_future(loop=self.loop) f_exception.set_exception(RuntimeError()) - newf_exception = asyncio.Future(loop=self.loop) + newf_exception = self._new_future(loop=self.loop) _copy_future_state(f_exception, newf_exception) self.assertTrue(newf_exception.done()) self.assertRaises(RuntimeError, newf_exception.result) - f_cancelled = asyncio.Future(loop=self.loop) + f_cancelled = self._new_future(loop=self.loop) f_cancelled.cancel() - newf_cancelled = asyncio.Future(loop=self.loop) + newf_cancelled = self._new_future(loop=self.loop) _copy_future_state(f_cancelled, newf_cancelled) self.assertTrue(newf_cancelled.cancelled()) def test_iter(self): - fut = asyncio.Future(loop=self.loop) + fut = self._new_future(loop=self.loop) def coro(): yield from fut @@ -285,20 +289,20 @@ class FutureTests(test_utils.TestCase): @mock.patch('asyncio.base_events.logger') def test_tb_logger_abandoned(self, m_log): - fut = asyncio.Future(loop=self.loop) + fut = self._new_future(loop=self.loop) del fut self.assertFalse(m_log.error.called) @mock.patch('asyncio.base_events.logger') def test_tb_logger_result_unretrieved(self, m_log): - fut = asyncio.Future(loop=self.loop) + fut = self._new_future(loop=self.loop) fut.set_result(42) del fut self.assertFalse(m_log.error.called) @mock.patch('asyncio.base_events.logger') def test_tb_logger_result_retrieved(self, m_log): - fut = asyncio.Future(loop=self.loop) + fut = self._new_future(loop=self.loop) fut.set_result(42) fut.result() del fut @@ -306,7 +310,7 @@ class FutureTests(test_utils.TestCase): @mock.patch('asyncio.base_events.logger') def test_tb_logger_exception_unretrieved(self, m_log): - fut = asyncio.Future(loop=self.loop) + fut = self._new_future(loop=self.loop) fut.set_exception(RuntimeError('boom')) del fut test_utils.run_briefly(self.loop) @@ -315,7 +319,7 @@ class FutureTests(test_utils.TestCase): @mock.patch('asyncio.base_events.logger') def test_tb_logger_exception_retrieved(self, m_log): - fut = asyncio.Future(loop=self.loop) + fut = self._new_future(loop=self.loop) fut.set_exception(RuntimeError('boom')) fut.exception() del fut @@ -323,7 +327,7 @@ class FutureTests(test_utils.TestCase): @mock.patch('asyncio.base_events.logger') def test_tb_logger_exception_result_retrieved(self, m_log): - fut = asyncio.Future(loop=self.loop) + fut = self._new_future(loop=self.loop) fut.set_exception(RuntimeError('boom')) self.assertRaises(RuntimeError, fut.result) del fut @@ -337,12 +341,12 @@ class FutureTests(test_utils.TestCase): f1 = ex.submit(run, 'oi') f2 = asyncio.wrap_future(f1, loop=self.loop) res, ident = self.loop.run_until_complete(f2) - self.assertIsInstance(f2, asyncio.Future) + self.assertTrue(asyncio.isfuture(f2)) self.assertEqual(res, 'oi') self.assertNotEqual(ident, threading.get_ident()) def test_wrap_future_future(self): - f1 = asyncio.Future(loop=self.loop) + f1 = self._new_future(loop=self.loop) f2 = asyncio.wrap_future(f1) self.assertIs(f1, f2) @@ -377,10 +381,10 @@ class FutureTests(test_utils.TestCase): def test_future_source_traceback(self): self.loop.set_debug(True) - future = asyncio.Future(loop=self.loop) + future = self._new_future(loop=self.loop) lineno = sys._getframe().f_lineno - 1 self.assertIsInstance(future._source_traceback, list) - self.assertEqual(future._source_traceback[-1][:3], + self.assertEqual(future._source_traceback[-2][:3], (__file__, lineno, 'test_future_source_traceback')) @@ -396,57 +400,18 @@ class FutureTests(test_utils.TestCase): return exc exc = memory_error() - future = asyncio.Future(loop=self.loop) - if debug: - source_traceback = future._source_traceback + future = self._new_future(loop=self.loop) future.set_exception(exc) future = None test_utils.run_briefly(self.loop) support.gc_collect() if sys.version_info >= (3, 4): - if debug: - frame = source_traceback[-1] - regex = (r'^Future exception was never retrieved\n' - r'future: \n' - r'source_traceback: Object ' - r'created at \(most recent call last\):\n' - r' File' - r'.*\n' - r' File "{filename}", line {lineno}, ' - r'in check_future_exception_never_retrieved\n' - r' future = asyncio\.Future\(loop=self\.loop\)$' - ).format(filename=re.escape(frame[0]), - lineno=frame[1]) - else: - regex = (r'^Future exception was never retrieved\n' - r'future: ' - r'$' - ) + regex = r'^Future exception was never retrieved\n' exc_info = (type(exc), exc, exc.__traceback__) m_log.error.assert_called_once_with(mock.ANY, exc_info=exc_info) else: - if debug: - frame = source_traceback[-1] - regex = (r'^Future/Task exception was never retrieved\n' - r'Future/Task created at \(most recent call last\):\n' - r' File' - r'.*\n' - r' File "{filename}", line {lineno}, ' - r'in check_future_exception_never_retrieved\n' - r' future = asyncio\.Future\(loop=self\.loop\)\n' - r'Traceback \(most recent call last\):\n' - r'.*\n' - r'MemoryError$' - ).format(filename=re.escape(frame[0]), - lineno=frame[1]) - else: - regex = (r'^Future/Task exception was never retrieved\n' - r'Traceback \(most recent call last\):\n' - r'.*\n' - r'MemoryError$' - ) + regex = r'^Future/Task exception was never retrieved\n' m_log.error.assert_called_once_with(mock.ANY, exc_info=False) message = m_log.error.call_args[0][0] self.assertRegex(message, re.compile(regex, re.DOTALL)) @@ -458,14 +423,13 @@ class FutureTests(test_utils.TestCase): self.check_future_exception_never_retrieved(True) def test_set_result_unless_cancelled(self): - from asyncio import futures - fut = asyncio.Future(loop=self.loop) + fut = self._new_future(loop=self.loop) fut.cancel() futures._set_result_unless_cancelled(fut, 2) self.assertTrue(fut.cancelled()) def test_future_stop_iteration_args(self): - fut = asyncio.Future(loop=self.loop) + fut = self._new_future(loop=self.loop) fut.set_result((1, 2)) fi = fut.__iter__() result = None @@ -478,7 +442,21 @@ class FutureTests(test_utils.TestCase): self.assertEqual(result, (1, 2)) -class FutureDoneCallbackTests(test_utils.TestCase): +@unittest.skipUnless(hasattr(futures, '_CFuture'), + 'requires the C _asyncio module') +class CFutureTests(BaseFutureTests, test_utils.TestCase): + + def _new_future(self, *args, **kwargs): + return futures._CFuture(*args, **kwargs) + + +class PyFutureTests(BaseFutureTests, test_utils.TestCase): + + def _new_future(self, *args, **kwargs): + return futures._PyFuture(*args, **kwargs) + + +class BaseFutureDoneCallbackTests(): def setUp(self): self.loop = self.new_test_loop() @@ -493,7 +471,7 @@ class FutureDoneCallbackTests(test_utils.TestCase): return bag_appender def _new_future(self): - return asyncio.Future(loop=self.loop) + raise NotImplementedError def test_callbacks_invoked_on_set_result(self): bag = [] @@ -557,5 +535,21 @@ class FutureDoneCallbackTests(test_utils.TestCase): self.assertEqual(f.result(), 'foo') +@unittest.skipUnless(hasattr(futures, '_CFuture'), + 'requires the C _asyncio module') +class CFutureDoneCallbackTests(BaseFutureDoneCallbackTests, + test_utils.TestCase): + + def _new_future(self): + return futures._CFuture(loop=self.loop) + + +class PyFutureDoneCallbackTests(BaseFutureDoneCallbackTests, + test_utils.TestCase): + + def _new_future(self): + return futures._PyFuture(loop=self.loop) + + if __name__ == '__main__': unittest.main() -- cgit v1.2.1 From 50cb3d8e81305ebfcd1eb2aa3cc9546bcdff06d6 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Mon, 24 Oct 2016 07:31:55 -0700 Subject: Issue #5830: Remove old comment. Add empty slots. --- Lib/sched.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'Lib') diff --git a/Lib/sched.py b/Lib/sched.py index bd7c0f1b6f..3d8c011a5c 100644 --- a/Lib/sched.py +++ b/Lib/sched.py @@ -23,11 +23,6 @@ The action function may be an instance method so it has another way to reference private data (besides global variables). """ -# XXX The timefunc and delayfunc should have been defined as methods -# XXX so you can define new kinds of schedulers using subclassing -# XXX instead of having to define a module or class just to hold -# XXX the global state of your particular time and delay functions. - import time import heapq from collections import namedtuple @@ -40,6 +35,7 @@ from time import monotonic as _time __all__ = ["scheduler"] class Event(namedtuple('Event', 'time, priority, action, argument, kwargs')): + __slots__ = [] def __eq__(s, o): return (s.time, s.priority) == (o.time, o.priority) def __lt__(s, o): return (s.time, s.priority) < (o.time, o.priority) def __le__(s, o): return (s.time, s.priority) <= (o.time, o.priority) -- cgit v1.2.1 From 710770e3d91a2f03bb63bcf3744b38c0d51c2d36 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 25 Oct 2016 09:43:48 +0300 Subject: Issue #27025: Generated names for Tkinter widgets now start by the "!" prefix for readability (was "`"). --- Lib/tkinter/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index 99ad2a7c01..25fe645a93 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -2261,9 +2261,9 @@ class BaseWidget(Misc): count = master._last_child_ids.get(name, 0) + 1 master._last_child_ids[name] = count if count == 1: - name = '`%s' % (name,) + name = '!%s' % (name,) else: - name = '`%s%d' % (name, count) + name = '!%s%d' % (name, count) self._name = name if master._w=='.': self._w = '.' + name -- cgit v1.2.1 From 3e0362dc668a088cc0dedfa352b24d9f484406eb Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 25 Oct 2016 08:49:13 -0700 Subject: Issue 25002: Deprecate asyncore/asynchat. Patch by Mariatta. --- Lib/asynchat.py | 5 +++++ Lib/asyncore.py | 4 ++++ 2 files changed, 9 insertions(+) (limited to 'Lib') diff --git a/Lib/asynchat.py b/Lib/asynchat.py index fc1146adbb..fede592126 100644 --- a/Lib/asynchat.py +++ b/Lib/asynchat.py @@ -46,8 +46,13 @@ method) up to the terminator, and then control will be returned to you - by calling your self.found_terminator() method. """ import asyncore +import warnings + from collections import deque +warnings.warn( + 'asynchat module is deprecated in 3.6. Use asyncio instead.', + PendingDeprecationWarning, stacklevel=2) class async_chat(asyncore.dispatcher): """This is an abstract class. You must derive from this class, and add diff --git a/Lib/asyncore.py b/Lib/asyncore.py index 705e406813..f17b31ad40 100644 --- a/Lib/asyncore.py +++ b/Lib/asyncore.py @@ -60,6 +60,10 @@ from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, EINVAL, \ _DISCONNECTED = frozenset({ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED, EPIPE, EBADF}) +warnings.warn( + 'asyncore module is deprecated in 3.6. Use asyncio instead.', + PendingDeprecationWarning, stacklevel=2) + try: socket_map except NameError: -- cgit v1.2.1 From eb2e264a6d22d45f4fbe7db2eddc28f18c1fc1c5 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 25 Oct 2016 19:01:41 +0300 Subject: Issue #28353: Try to fix tests. --- Lib/test/test_os.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 88598387d1..9bfb2446b5 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -892,14 +892,22 @@ class WalkTests(unittest.TestCase): os.symlink('broken', broken_link_path, True) os.symlink(join('tmp3', 'broken'), broken_link2_path, True) os.symlink(join('SUB21', 'tmp5'), broken_link3_path, True) - self.sub2_tree = (sub2_path, ["link", "SUB21"], + self.sub2_tree = (sub2_path, ["SUB21", "link"], ["broken_link", "broken_link2", "broken_link3", "tmp3"]) else: self.sub2_tree = (sub2_path, [], ["tmp3"]) os.chmod(sub21_path, 0) - self.addCleanup(os.chmod, sub21_path, stat.S_IRWXU) + try: + os.listdir(sub21_path) + except PermissionError: + self.addCleanup(os.chmod, sub21_path, stat.S_IRWXU) + else: + os.chmod(sub21_path, stat.S_IRWXU) + os.unlink(tmp5_path) + os.rmdir(sub21_path) + del self.sub2_tree[1][:1] def test_walk_topdown(self): # Walk top-down. @@ -912,6 +920,7 @@ class WalkTests(unittest.TestCase): flipped = all[0][1][0] != "SUB1" all[0][1].sort() all[3 - 2 * flipped][-1].sort() + all[3 - 2 * flipped][1].sort() self.assertEqual(all[0], (self.walk_path, ["SUB1", "SUB2"], ["tmp1"])) self.assertEqual(all[1 + flipped], (self.sub1_path, ["SUB11"], ["tmp2"])) self.assertEqual(all[2 + flipped], (self.sub11_path, [], [])) @@ -934,6 +943,7 @@ class WalkTests(unittest.TestCase): (str(walk_path), ["SUB2"], ["tmp1"])) all[1][-1].sort() + all[1][1].sort() self.assertEqual(all[1], self.sub2_tree) def test_file_like_path(self): @@ -950,6 +960,7 @@ class WalkTests(unittest.TestCase): flipped = all[3][1][0] != "SUB1" all[3][1].sort() all[2 - 2 * flipped][-1].sort() + all[2 - 2 * flipped][1].sort() self.assertEqual(all[3], (self.walk_path, ["SUB1", "SUB2"], ["tmp1"])) self.assertEqual(all[flipped], -- cgit v1.2.1 From 8d4b109d27d764a9bd517a2685e97e035b1e6548 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 25 Oct 2016 18:42:51 -0700 Subject: Issue #25002: Back out asyncore/asynchat deprecation. --- Lib/asynchat.py | 5 ----- Lib/asyncore.py | 4 ---- 2 files changed, 9 deletions(-) (limited to 'Lib') diff --git a/Lib/asynchat.py b/Lib/asynchat.py index fede592126..fc1146adbb 100644 --- a/Lib/asynchat.py +++ b/Lib/asynchat.py @@ -46,13 +46,8 @@ method) up to the terminator, and then control will be returned to you - by calling your self.found_terminator() method. """ import asyncore -import warnings - from collections import deque -warnings.warn( - 'asynchat module is deprecated in 3.6. Use asyncio instead.', - PendingDeprecationWarning, stacklevel=2) class async_chat(asyncore.dispatcher): """This is an abstract class. You must derive from this class, and add diff --git a/Lib/asyncore.py b/Lib/asyncore.py index f17b31ad40..705e406813 100644 --- a/Lib/asyncore.py +++ b/Lib/asyncore.py @@ -60,10 +60,6 @@ from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, EINVAL, \ _DISCONNECTED = frozenset({ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED, EPIPE, EBADF}) -warnings.warn( - 'asyncore module is deprecated in 3.6. Use asyncio instead.', - PendingDeprecationWarning, stacklevel=2) - try: socket_map except NameError: -- cgit v1.2.1 From 6240b8fef6d5c716a7b56e63bb556e6fcab1c2f5 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Thu, 27 Oct 2016 14:28:07 -0700 Subject: Issue #28522: Fixes mishandled buffer reallocation in getpathp.c --- Lib/test/test_site.py | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index d2fbb7ba2e..5aedbdb801 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -488,6 +488,58 @@ class StartupImportTests(unittest.TestCase): 'import site, sys; site.enablerlcompleter(); sys.exit(hasattr(sys, "__interactivehook__"))']).wait() self.assertTrue(r, "'__interactivehook__' not added by enablerlcompleter()") + @unittest.skipUnless(sys.platform == 'win32', "only supported on Windows") + def test_underpth_nosite_file(self): + _pth_file = os.path.splitext(sys.executable)[0] + '._pth' + try: + libpath = os.path.dirname(os.path.dirname(encodings.__file__)) + with open(_pth_file, 'w') as f: + print('fake-path-name', file=f) + # Ensure the generated path is very long so that buffer + # resizing in getpathp.c is exercised + for _ in range(200): + print(libpath, file=f) + print('# comment', file=f) + + env = os.environ.copy() + env['PYTHONPATH'] = 'from-env' + rc = subprocess.call([sys.executable, '-c', + 'import sys; sys.exit(sys.flags.no_site and ' + 'len(sys.path) > 200 and ' + '%r in sys.path and %r in sys.path and %r not in sys.path)' % ( + os.path.join(sys.prefix, 'fake-path-name'), + libpath, + os.path.join(sys.prefix, 'from-env'), + )], env=env) + self.assertEqual(rc, 0) + finally: + os.unlink(_pth_file) + + @unittest.skipUnless(sys.platform == 'win32', "only supported on Windows") + def test_underpth_file(self): + _pth_file = os.path.splitext(sys.executable)[0] + '._pth' + try: + libpath = os.path.dirname(os.path.dirname(encodings.__file__)) + with open(_pth_file, 'w') as f: + print('fake-path-name', file=f) + for _ in range(200): + print(libpath, file=f) + print('# comment', file=f) + print('import site', file=f) + + env = os.environ.copy() + env['PYTHONPATH'] = 'from-env' + rc = subprocess.call([sys.executable, '-c', + 'import sys; sys.exit(not sys.flags.no_site and ' + '%r in sys.path and %r in sys.path and %r not in sys.path)' % ( + os.path.join(sys.prefix, 'fake-path-name'), + libpath, + os.path.join(sys.prefix, 'from-env'), + )], env=env) + self.assertEqual(rc, 0) + finally: + os.unlink(_pth_file) + if __name__ == "__main__": unittest.main() -- cgit v1.2.1 From 4b9446e7b8800e914a2180cfb8f701f7e6a382d1 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Fri, 28 Oct 2016 12:52:37 -0400 Subject: Issue #28544: Implement asyncio.Task in C. This implementation provides additional 10-20% speed boost for asyncio programs. The patch also fixes _asynciomodule.c to use Arguments Clinic, and makes '_schedule_callbacks' an overridable method (as it was in 3.5). --- Lib/asyncio/base_events.py | 2 +- Lib/asyncio/base_futures.py | 70 ++++++ Lib/asyncio/base_tasks.py | 76 +++++++ Lib/asyncio/coroutines.py | 4 +- Lib/asyncio/futures.py | 89 ++------ Lib/asyncio/tasks.py | 80 ++----- Lib/test/test_asyncio/test_tasks.py | 439 +++++++++++++++++++++++++----------- 7 files changed, 488 insertions(+), 272 deletions(-) create mode 100644 Lib/asyncio/base_futures.py create mode 100644 Lib/asyncio/base_tasks.py (limited to 'Lib') diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index 58800617d9..cc9994d66f 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -57,7 +57,7 @@ _FATAL_ERROR_IGNORE = (BrokenPipeError, def _format_handle(handle): cb = handle._callback - if inspect.ismethod(cb) and isinstance(cb.__self__, tasks.Task): + if isinstance(getattr(cb, '__self__', None), tasks.Task): # format the task return repr(cb.__self__) else: diff --git a/Lib/asyncio/base_futures.py b/Lib/asyncio/base_futures.py new file mode 100644 index 0000000000..64f7845458 --- /dev/null +++ b/Lib/asyncio/base_futures.py @@ -0,0 +1,70 @@ +__all__ = [] + +import concurrent.futures._base +import reprlib + +from . import events + +Error = concurrent.futures._base.Error +CancelledError = concurrent.futures.CancelledError +TimeoutError = concurrent.futures.TimeoutError + + +class InvalidStateError(Error): + """The operation is not allowed in this state.""" + + +# States for Future. +_PENDING = 'PENDING' +_CANCELLED = 'CANCELLED' +_FINISHED = 'FINISHED' + + +def isfuture(obj): + """Check for a Future. + + This returns True when obj is a Future instance or is advertising + itself as duck-type compatible by setting _asyncio_future_blocking. + See comment in Future for more details. + """ + return getattr(obj, '_asyncio_future_blocking', None) is not None + + +def _format_callbacks(cb): + """helper function for Future.__repr__""" + size = len(cb) + if not size: + cb = '' + + def format_cb(callback): + return events._format_callback_source(callback, ()) + + if size == 1: + cb = format_cb(cb[0]) + elif size == 2: + cb = '{}, {}'.format(format_cb(cb[0]), format_cb(cb[1])) + elif size > 2: + cb = '{}, <{} more>, {}'.format(format_cb(cb[0]), + size - 2, + format_cb(cb[-1])) + return 'cb=[%s]' % cb + + +def _future_repr_info(future): + # (Future) -> str + """helper function for Future.__repr__""" + info = [future._state.lower()] + if future._state == _FINISHED: + if future._exception is not None: + info.append('exception={!r}'.format(future._exception)) + else: + # use reprlib to limit the length of the output, especially + # for very long strings + result = reprlib.repr(future._result) + info.append('result={}'.format(result)) + if future._callbacks: + info.append(_format_callbacks(future._callbacks)) + if future._source_traceback: + frame = future._source_traceback[-1] + info.append('created at %s:%s' % (frame[0], frame[1])) + return info diff --git a/Lib/asyncio/base_tasks.py b/Lib/asyncio/base_tasks.py new file mode 100644 index 0000000000..5f34434c57 --- /dev/null +++ b/Lib/asyncio/base_tasks.py @@ -0,0 +1,76 @@ +import linecache +import traceback + +from . import base_futures +from . import coroutines + + +def _task_repr_info(task): + info = base_futures._future_repr_info(task) + + if task._must_cancel: + # replace status + info[0] = 'cancelling' + + coro = coroutines._format_coroutine(task._coro) + info.insert(1, 'coro=<%s>' % coro) + + if task._fut_waiter is not None: + info.insert(2, 'wait_for=%r' % task._fut_waiter) + return info + + +def _task_get_stack(task, limit): + frames = [] + try: + # 'async def' coroutines + f = task._coro.cr_frame + except AttributeError: + f = task._coro.gi_frame + if f is not None: + while f is not None: + if limit is not None: + if limit <= 0: + break + limit -= 1 + frames.append(f) + f = f.f_back + frames.reverse() + elif task._exception is not None: + tb = task._exception.__traceback__ + while tb is not None: + if limit is not None: + if limit <= 0: + break + limit -= 1 + frames.append(tb.tb_frame) + tb = tb.tb_next + return frames + + +def _task_print_stack(task, limit, file): + extracted_list = [] + checked = set() + for f in task.get_stack(limit=limit): + lineno = f.f_lineno + co = f.f_code + filename = co.co_filename + name = co.co_name + if filename not in checked: + checked.add(filename) + linecache.checkcache(filename) + line = linecache.getline(filename, lineno, f.f_globals) + extracted_list.append((filename, lineno, name, line)) + exc = task._exception + if not extracted_list: + print('No stack for %r' % task, file=file) + elif exc is not None: + print('Traceback for %r (most recent call last):' % task, + file=file) + else: + print('Stack for %r (most recent call last):' % task, + file=file) + traceback.print_list(extracted_list, file=file) + if exc is not None: + for line in traceback.format_exception_only(exc.__class__, exc): + print(line, file=file, end='') diff --git a/Lib/asyncio/coroutines.py b/Lib/asyncio/coroutines.py index 1db7030205..167c1e44e2 100644 --- a/Lib/asyncio/coroutines.py +++ b/Lib/asyncio/coroutines.py @@ -11,7 +11,7 @@ import types from . import compat from . import events -from . import futures +from . import base_futures from .log import logger @@ -204,7 +204,7 @@ def coroutine(func): @functools.wraps(func) def coro(*args, **kw): res = func(*args, **kw) - if (futures.isfuture(res) or inspect.isgenerator(res) or + if (base_futures.isfuture(res) or inspect.isgenerator(res) or isinstance(res, CoroWrapper)): res = yield from res elif _AwaitableABC is not None: diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py index b571130514..d11d289307 100644 --- a/Lib/asyncio/futures.py +++ b/Lib/asyncio/futures.py @@ -1,33 +1,30 @@ """A Future class similar to the one in PEP 3148.""" -__all__ = ['CancelledError', 'TimeoutError', - 'InvalidStateError', - 'Future', 'wrap_future', 'isfuture' - ] +__all__ = ['CancelledError', 'TimeoutError', 'InvalidStateError', + 'Future', 'wrap_future', 'isfuture'] -import concurrent.futures._base +import concurrent.futures import logging -import reprlib import sys import traceback +from . import base_futures from . import compat from . import events -# States for Future. -_PENDING = 'PENDING' -_CANCELLED = 'CANCELLED' -_FINISHED = 'FINISHED' -Error = concurrent.futures._base.Error -CancelledError = concurrent.futures.CancelledError -TimeoutError = concurrent.futures.TimeoutError +CancelledError = base_futures.CancelledError +InvalidStateError = base_futures.InvalidStateError +TimeoutError = base_futures.TimeoutError +isfuture = base_futures.isfuture -STACK_DEBUG = logging.DEBUG - 1 # heavy-duty debugging + +_PENDING = base_futures._PENDING +_CANCELLED = base_futures._CANCELLED +_FINISHED = base_futures._FINISHED -class InvalidStateError(Error): - """The operation is not allowed in this state.""" +STACK_DEBUG = logging.DEBUG - 1 # heavy-duty debugging class _TracebackLogger: @@ -110,56 +107,6 @@ class _TracebackLogger: self.loop.call_exception_handler({'message': msg}) -def isfuture(obj): - """Check for a Future. - - This returns True when obj is a Future instance or is advertising - itself as duck-type compatible by setting _asyncio_future_blocking. - See comment in Future for more details. - """ - return getattr(obj, '_asyncio_future_blocking', None) is not None - - -def _format_callbacks(cb): - """helper function for Future.__repr__""" - size = len(cb) - if not size: - cb = '' - - def format_cb(callback): - return events._format_callback_source(callback, ()) - - if size == 1: - cb = format_cb(cb[0]) - elif size == 2: - cb = '{}, {}'.format(format_cb(cb[0]), format_cb(cb[1])) - elif size > 2: - cb = '{}, <{} more>, {}'.format(format_cb(cb[0]), - size-2, - format_cb(cb[-1])) - return 'cb=[%s]' % cb - - -def _future_repr_info(future): - # (Future) -> str - """helper function for Future.__repr__""" - info = [future._state.lower()] - if future._state == _FINISHED: - if future._exception is not None: - info.append('exception={!r}'.format(future._exception)) - else: - # use reprlib to limit the length of the output, especially - # for very long strings - result = reprlib.repr(future._result) - info.append('result={}'.format(result)) - if future._callbacks: - info.append(_format_callbacks(future._callbacks)) - if future._source_traceback: - frame = future._source_traceback[-1] - info.append('created at %s:%s' % (frame[0], frame[1])) - return info - - class Future: """This class is *almost* compatible with concurrent.futures.Future. @@ -212,7 +159,7 @@ class Future: if self._loop.get_debug(): self._source_traceback = traceback.extract_stack(sys._getframe(1)) - _repr_info = _future_repr_info + _repr_info = base_futures._future_repr_info def __repr__(self): return '<%s %s>' % (self.__class__.__name__, ' '.join(self._repr_info())) @@ -247,10 +194,10 @@ class Future: if self._state != _PENDING: return False self._state = _CANCELLED - self.__schedule_callbacks() + self._schedule_callbacks() return True - def __schedule_callbacks(self): + def _schedule_callbacks(self): """Internal: Ask the event loop to call all callbacks. The callbacks are scheduled to be called as soon as possible. Also @@ -352,7 +299,7 @@ class Future: raise InvalidStateError('{}: {!r}'.format(self._state, self)) self._result = result self._state = _FINISHED - self.__schedule_callbacks() + self._schedule_callbacks() def set_exception(self, exception): """Mark the future done and set an exception. @@ -369,7 +316,7 @@ class Future: "and cannot be raised into a Future") self._exception = exception self._state = _FINISHED - self.__schedule_callbacks() + self._schedule_callbacks() if compat.PY34: self._log_traceback = True else: diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py index 8852aa5ad2..5a43ef257f 100644 --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -9,11 +9,10 @@ __all__ = ['Task', import concurrent.futures import functools import inspect -import linecache -import traceback import warnings import weakref +from . import base_tasks from . import compat from . import coroutines from . import events @@ -93,18 +92,7 @@ class Task(futures.Future): futures.Future.__del__(self) def _repr_info(self): - info = super()._repr_info() - - if self._must_cancel: - # replace status - info[0] = 'cancelling' - - coro = coroutines._format_coroutine(self._coro) - info.insert(1, 'coro=<%s>' % coro) - - if self._fut_waiter is not None: - info.insert(2, 'wait_for=%r' % self._fut_waiter) - return info + return base_tasks._task_repr_info(self) def get_stack(self, *, limit=None): """Return the list of stack frames for this task's coroutine. @@ -127,31 +115,7 @@ class Task(futures.Future): For reasons beyond our control, only one stack frame is returned for a suspended coroutine. """ - frames = [] - try: - # 'async def' coroutines - f = self._coro.cr_frame - except AttributeError: - f = self._coro.gi_frame - if f is not None: - while f is not None: - if limit is not None: - if limit <= 0: - break - limit -= 1 - frames.append(f) - f = f.f_back - frames.reverse() - elif self._exception is not None: - tb = self._exception.__traceback__ - while tb is not None: - if limit is not None: - if limit <= 0: - break - limit -= 1 - frames.append(tb.tb_frame) - tb = tb.tb_next - return frames + return base_tasks._task_get_stack(self, limit) def print_stack(self, *, limit=None, file=None): """Print the stack or traceback for this task's coroutine. @@ -162,31 +126,7 @@ class Task(futures.Future): to which the output is written; by default output is written to sys.stderr. """ - extracted_list = [] - checked = set() - for f in self.get_stack(limit=limit): - lineno = f.f_lineno - co = f.f_code - filename = co.co_filename - name = co.co_name - if filename not in checked: - checked.add(filename) - linecache.checkcache(filename) - line = linecache.getline(filename, lineno, f.f_globals) - extracted_list.append((filename, lineno, name, line)) - exc = self._exception - if not extracted_list: - print('No stack for %r' % self, file=file) - elif exc is not None: - print('Traceback for %r (most recent call last):' % self, - file=file) - else: - print('Stack for %r (most recent call last):' % self, - file=file) - traceback.print_list(extracted_list, file=file) - if exc is not None: - for line in traceback.format_exception_only(exc.__class__, exc): - print(line, file=file, end='') + return base_tasks._task_print_stack(self, limit, file) def cancel(self): """Request that this task cancel itself. @@ -316,6 +256,18 @@ class Task(futures.Future): self = None # Needed to break cycles when an exception occurs. +_PyTask = Task + + +try: + import _asyncio +except ImportError: + pass +else: + # _CTask is needed for tests. + Task = _CTask = _asyncio.Task + + # wait() and as_completed() similar to those in PEP 3148. FIRST_COMPLETED = concurrent.futures.FIRST_COMPLETED diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index 1ceb9b28cb..d8862fccc7 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -1,5 +1,6 @@ """Tests for tasks.py.""" +import collections import contextlib import functools import io @@ -14,6 +15,8 @@ from unittest import mock import asyncio from asyncio import coroutines +from asyncio import futures +from asyncio import tasks from asyncio import test_utils try: from test import support @@ -72,14 +75,25 @@ class Dummy: pass -class TaskTests(test_utils.TestCase): +class BaseTaskTests: + + Task = None + Future = None + + def new_task(self, loop, coro): + return self.__class__.Task(coro, loop=loop) + + def new_future(self, loop): + return self.__class__.Future(loop=loop) def setUp(self): self.loop = self.new_test_loop() + self.loop.set_task_factory(self.new_task) + self.loop.create_future = lambda: self.new_future(self.loop) def test_other_loop_future(self): other_loop = asyncio.new_event_loop() - fut = asyncio.Future(loop=other_loop) + fut = self.new_future(other_loop) @asyncio.coroutine def run(fut): @@ -107,7 +121,7 @@ class TaskTests(test_utils.TestCase): @asyncio.coroutine def notmuch(): return 'ok' - t = asyncio.Task(notmuch(), loop=self.loop) + t = self.new_task(self.loop, notmuch()) self.loop.run_until_complete(t) self.assertTrue(t.done()) self.assertEqual(t.result(), 'ok') @@ -115,7 +129,7 @@ class TaskTests(test_utils.TestCase): loop = asyncio.new_event_loop() self.set_event_loop(loop) - t = asyncio.Task(notmuch(), loop=loop) + t = self.new_task(loop, notmuch()) self.assertIs(t._loop, loop) loop.run_until_complete(t) loop.close() @@ -138,7 +152,7 @@ class TaskTests(test_utils.TestCase): loop.close() def test_ensure_future_future(self): - f_orig = asyncio.Future(loop=self.loop) + f_orig = self.new_future(self.loop) f_orig.set_result('ko') f = asyncio.ensure_future(f_orig) @@ -162,7 +176,7 @@ class TaskTests(test_utils.TestCase): @asyncio.coroutine def notmuch(): return 'ok' - t_orig = asyncio.Task(notmuch(), loop=self.loop) + t_orig = self.new_task(self.loop, notmuch()) t = asyncio.ensure_future(t_orig) self.loop.run_until_complete(t) self.assertTrue(t.done()) @@ -203,7 +217,7 @@ class TaskTests(test_utils.TestCase): asyncio.ensure_future('ok') def test_async_warning(self): - f = asyncio.Future(loop=self.loop) + f = self.new_future(self.loop) with self.assertWarnsRegex(DeprecationWarning, 'function is deprecated, use ensure_'): self.assertIs(f, asyncio.async(f)) @@ -250,8 +264,8 @@ class TaskTests(test_utils.TestCase): # test coroutine function self.assertEqual(notmuch.__name__, 'notmuch') if PY35: - self.assertEqual(notmuch.__qualname__, - 'TaskTests.test_task_repr..notmuch') + self.assertRegex(notmuch.__qualname__, + r'\w+.test_task_repr..notmuch') self.assertEqual(notmuch.__module__, __name__) filename, lineno = test_utils.get_function_source(notmuch) @@ -260,7 +274,7 @@ class TaskTests(test_utils.TestCase): # test coroutine object gen = notmuch() if coroutines._DEBUG or PY35: - coro_qualname = 'TaskTests.test_task_repr..notmuch' + coro_qualname = 'BaseTaskTests.test_task_repr..notmuch' else: coro_qualname = 'notmuch' self.assertEqual(gen.__name__, 'notmuch') @@ -269,7 +283,7 @@ class TaskTests(test_utils.TestCase): coro_qualname) # test pending Task - t = asyncio.Task(gen, loop=self.loop) + t = self.new_task(self.loop, gen) t.add_done_callback(Dummy()) coro = format_coroutine(coro_qualname, 'running', src, @@ -291,7 +305,7 @@ class TaskTests(test_utils.TestCase): '' % coro) # test finished Task - t = asyncio.Task(notmuch(), loop=self.loop) + t = self.new_task(self.loop, notmuch()) self.loop.run_until_complete(t) coro = format_coroutine(coro_qualname, 'done', src, t._source_traceback) @@ -310,9 +324,9 @@ class TaskTests(test_utils.TestCase): # test coroutine function self.assertEqual(notmuch.__name__, 'notmuch') if PY35: - self.assertEqual(notmuch.__qualname__, - 'TaskTests.test_task_repr_coro_decorator' - '..notmuch') + self.assertRegex(notmuch.__qualname__, + r'\w+.test_task_repr_coro_decorator' + r'\.\.notmuch') self.assertEqual(notmuch.__module__, __name__) # test coroutine object @@ -322,7 +336,7 @@ class TaskTests(test_utils.TestCase): # function, as expected, and have a qualified name (__qualname__ # attribute). coro_name = 'notmuch' - coro_qualname = ('TaskTests.test_task_repr_coro_decorator' + coro_qualname = ('BaseTaskTests.test_task_repr_coro_decorator' '..notmuch') else: # On Python < 3.5, generators inherit the name of the code, not of @@ -350,7 +364,7 @@ class TaskTests(test_utils.TestCase): self.assertEqual(repr(gen), '' % coro) # test pending Task - t = asyncio.Task(gen, loop=self.loop) + t = self.new_task(self.loop, gen) t.add_done_callback(Dummy()) # format the coroutine object @@ -373,8 +387,8 @@ class TaskTests(test_utils.TestCase): def wait_for(fut): return (yield from fut) - fut = asyncio.Future(loop=self.loop) - task = asyncio.Task(wait_for(fut), loop=self.loop) + fut = self.new_future(self.loop) + task = self.new_task(self.loop, wait_for(fut)) test_utils.run_briefly(self.loop) self.assertRegex(repr(task), '' % re.escape(repr(fut))) @@ -400,10 +414,11 @@ class TaskTests(test_utils.TestCase): self.addCleanup(task._coro.close) coro_repr = repr(task._coro) - expected = ('.func(1)() running, ') - self.assertTrue(coro_repr.startswith(expected), - coro_repr) + expected = ( + r'\.func\(1\)\(\) running, ' + ) + self.assertRegex(coro_repr, expected) def test_task_basics(self): @asyncio.coroutine @@ -437,7 +452,7 @@ class TaskTests(test_utils.TestCase): yield from asyncio.sleep(10.0, loop=loop) return 12 - t = asyncio.Task(task(), loop=loop) + t = self.new_task(loop, task()) loop.call_soon(t.cancel) with self.assertRaises(asyncio.CancelledError): loop.run_until_complete(t) @@ -452,7 +467,7 @@ class TaskTests(test_utils.TestCase): yield return 12 - t = asyncio.Task(task(), loop=self.loop) + t = self.new_task(self.loop, task()) test_utils.run_briefly(self.loop) # start coro t.cancel() self.assertRaises( @@ -462,14 +477,14 @@ class TaskTests(test_utils.TestCase): self.assertFalse(t.cancel()) def test_cancel_inner_future(self): - f = asyncio.Future(loop=self.loop) + f = self.new_future(self.loop) @asyncio.coroutine def task(): yield from f return 12 - t = asyncio.Task(task(), loop=self.loop) + t = self.new_task(self.loop, task()) test_utils.run_briefly(self.loop) # start task f.cancel() with self.assertRaises(asyncio.CancelledError): @@ -478,14 +493,14 @@ class TaskTests(test_utils.TestCase): self.assertTrue(t.cancelled()) def test_cancel_both_task_and_inner_future(self): - f = asyncio.Future(loop=self.loop) + f = self.new_future(self.loop) @asyncio.coroutine def task(): yield from f return 12 - t = asyncio.Task(task(), loop=self.loop) + t = self.new_task(self.loop, task()) test_utils.run_briefly(self.loop) f.cancel() @@ -499,8 +514,8 @@ class TaskTests(test_utils.TestCase): self.assertTrue(t.cancelled()) def test_cancel_task_catching(self): - fut1 = asyncio.Future(loop=self.loop) - fut2 = asyncio.Future(loop=self.loop) + fut1 = self.new_future(self.loop) + fut2 = self.new_future(self.loop) @asyncio.coroutine def task(): @@ -510,7 +525,7 @@ class TaskTests(test_utils.TestCase): except asyncio.CancelledError: return 42 - t = asyncio.Task(task(), loop=self.loop) + t = self.new_task(self.loop, task()) test_utils.run_briefly(self.loop) self.assertIs(t._fut_waiter, fut1) # White-box test. fut1.set_result(None) @@ -523,9 +538,9 @@ class TaskTests(test_utils.TestCase): self.assertFalse(t.cancelled()) def test_cancel_task_ignoring(self): - fut1 = asyncio.Future(loop=self.loop) - fut2 = asyncio.Future(loop=self.loop) - fut3 = asyncio.Future(loop=self.loop) + fut1 = self.new_future(self.loop) + fut2 = self.new_future(self.loop) + fut3 = self.new_future(self.loop) @asyncio.coroutine def task(): @@ -537,7 +552,7 @@ class TaskTests(test_utils.TestCase): res = yield from fut3 return res - t = asyncio.Task(task(), loop=self.loop) + t = self.new_task(self.loop, task()) test_utils.run_briefly(self.loop) self.assertIs(t._fut_waiter, fut1) # White-box test. fut1.set_result(None) @@ -565,7 +580,7 @@ class TaskTests(test_utils.TestCase): yield from asyncio.sleep(100, loop=loop) return 12 - t = asyncio.Task(task(), loop=loop) + t = self.new_task(loop, task()) self.assertRaises( asyncio.CancelledError, loop.run_until_complete, t) self.assertTrue(t.done()) @@ -598,7 +613,7 @@ class TaskTests(test_utils.TestCase): if x == 2: loop.stop() - t = asyncio.Task(task(), loop=loop) + t = self.new_task(loop, task()) with self.assertRaises(RuntimeError) as cm: loop.run_until_complete(t) self.assertEqual(str(cm.exception), @@ -636,7 +651,7 @@ class TaskTests(test_utils.TestCase): foo_running = False return 'done' - fut = asyncio.Task(foo(), loop=loop) + fut = self.new_task(loop, foo()) with self.assertRaises(asyncio.TimeoutError): loop.run_until_complete(asyncio.wait_for(fut, 0.1, loop=loop)) @@ -676,7 +691,7 @@ class TaskTests(test_utils.TestCase): asyncio.set_event_loop(loop) try: - fut = asyncio.Task(foo(), loop=loop) + fut = self.new_task(loop, foo()) with self.assertRaises(asyncio.TimeoutError): loop.run_until_complete(asyncio.wait_for(fut, 0.01)) finally: @@ -695,7 +710,7 @@ class TaskTests(test_utils.TestCase): loop = self.new_test_loop(gen) - fut = asyncio.Future(loop=loop) + fut = self.new_future(loop) task = asyncio.wait_for(fut, timeout=0.2, loop=loop) loop.call_later(0.1, fut.set_result, "ok") res = loop.run_until_complete(task) @@ -712,8 +727,8 @@ class TaskTests(test_utils.TestCase): loop = self.new_test_loop(gen) - a = asyncio.Task(asyncio.sleep(0.1, loop=loop), loop=loop) - b = asyncio.Task(asyncio.sleep(0.15, loop=loop), loop=loop) + a = self.new_task(loop, asyncio.sleep(0.1, loop=loop)) + b = self.new_task(loop, asyncio.sleep(0.15, loop=loop)) @asyncio.coroutine def foo(): @@ -722,12 +737,12 @@ class TaskTests(test_utils.TestCase): self.assertEqual(pending, set()) return 42 - res = loop.run_until_complete(asyncio.Task(foo(), loop=loop)) + res = loop.run_until_complete(self.new_task(loop, foo())) self.assertEqual(res, 42) self.assertAlmostEqual(0.15, loop.time()) # Doing it again should take no time and exercise a different path. - res = loop.run_until_complete(asyncio.Task(foo(), loop=loop)) + res = loop.run_until_complete(self.new_task(loop, foo())) self.assertAlmostEqual(0.15, loop.time()) self.assertEqual(res, 42) @@ -742,8 +757,8 @@ class TaskTests(test_utils.TestCase): loop = self.new_test_loop(gen) - a = asyncio.Task(asyncio.sleep(0.01, loop=loop), loop=loop) - b = asyncio.Task(asyncio.sleep(0.015, loop=loop), loop=loop) + a = self.new_task(loop, asyncio.sleep(0.01, loop=loop)) + b = self.new_task(loop, asyncio.sleep(0.015, loop=loop)) @asyncio.coroutine def foo(): @@ -754,7 +769,7 @@ class TaskTests(test_utils.TestCase): asyncio.set_event_loop(loop) res = loop.run_until_complete( - asyncio.Task(foo(), loop=loop)) + self.new_task(loop, foo())) self.assertEqual(res, 42) @@ -764,9 +779,9 @@ class TaskTests(test_utils.TestCase): return s c = coro('test') - task = asyncio.Task( - asyncio.wait([c, c, coro('spam')], loop=self.loop), - loop=self.loop) + task =self.new_task( + self.loop, + asyncio.wait([c, c, coro('spam')], loop=self.loop)) done, pending = self.loop.run_until_complete(task) @@ -797,12 +812,12 @@ class TaskTests(test_utils.TestCase): loop = self.new_test_loop(gen) - a = asyncio.Task(asyncio.sleep(10.0, loop=loop), loop=loop) - b = asyncio.Task(asyncio.sleep(0.1, loop=loop), loop=loop) - task = asyncio.Task( + a = self.new_task(loop, asyncio.sleep(10.0, loop=loop)) + b = self.new_task(loop, asyncio.sleep(0.1, loop=loop)) + task = self.new_task( + loop, asyncio.wait([b, a], return_when=asyncio.FIRST_COMPLETED, - loop=loop), - loop=loop) + loop=loop)) done, pending = loop.run_until_complete(task) self.assertEqual({b}, done) @@ -829,12 +844,12 @@ class TaskTests(test_utils.TestCase): yield yield - a = asyncio.Task(coro1(), loop=self.loop) - b = asyncio.Task(coro2(), loop=self.loop) - task = asyncio.Task( + a = self.new_task(self.loop, coro1()) + b = self.new_task(self.loop, coro2()) + task = self.new_task( + self.loop, asyncio.wait([b, a], return_when=asyncio.FIRST_COMPLETED, - loop=self.loop), - loop=self.loop) + loop=self.loop)) done, pending = self.loop.run_until_complete(task) self.assertEqual({a, b}, done) @@ -853,17 +868,17 @@ class TaskTests(test_utils.TestCase): loop = self.new_test_loop(gen) # first_exception, task already has exception - a = asyncio.Task(asyncio.sleep(10.0, loop=loop), loop=loop) + a = self.new_task(loop, asyncio.sleep(10.0, loop=loop)) @asyncio.coroutine def exc(): raise ZeroDivisionError('err') - b = asyncio.Task(exc(), loop=loop) - task = asyncio.Task( + b = self.new_task(loop, exc()) + task = self.new_task( + loop, asyncio.wait([b, a], return_when=asyncio.FIRST_EXCEPTION, - loop=loop), - loop=loop) + loop=loop)) done, pending = loop.run_until_complete(task) self.assertEqual({b}, done) @@ -886,14 +901,14 @@ class TaskTests(test_utils.TestCase): loop = self.new_test_loop(gen) # first_exception, exception during waiting - a = asyncio.Task(asyncio.sleep(10.0, loop=loop), loop=loop) + a = self.new_task(loop, asyncio.sleep(10.0, loop=loop)) @asyncio.coroutine def exc(): yield from asyncio.sleep(0.01, loop=loop) raise ZeroDivisionError('err') - b = asyncio.Task(exc(), loop=loop) + b = self.new_task(loop, exc()) task = asyncio.wait([b, a], return_when=asyncio.FIRST_EXCEPTION, loop=loop) @@ -917,14 +932,14 @@ class TaskTests(test_utils.TestCase): loop = self.new_test_loop(gen) - a = asyncio.Task(asyncio.sleep(0.1, loop=loop), loop=loop) + a = self.new_task(loop, asyncio.sleep(0.1, loop=loop)) @asyncio.coroutine def sleeper(): yield from asyncio.sleep(0.15, loop=loop) raise ZeroDivisionError('really') - b = asyncio.Task(sleeper(), loop=loop) + b = self.new_task(loop, sleeper()) @asyncio.coroutine def foo(): @@ -934,10 +949,10 @@ class TaskTests(test_utils.TestCase): errors = set(f for f in done if f.exception() is not None) self.assertEqual(len(errors), 1) - loop.run_until_complete(asyncio.Task(foo(), loop=loop)) + loop.run_until_complete(self.new_task(loop, foo())) self.assertAlmostEqual(0.15, loop.time()) - loop.run_until_complete(asyncio.Task(foo(), loop=loop)) + loop.run_until_complete(self.new_task(loop, foo())) self.assertAlmostEqual(0.15, loop.time()) def test_wait_with_timeout(self): @@ -953,8 +968,8 @@ class TaskTests(test_utils.TestCase): loop = self.new_test_loop(gen) - a = asyncio.Task(asyncio.sleep(0.1, loop=loop), loop=loop) - b = asyncio.Task(asyncio.sleep(0.15, loop=loop), loop=loop) + a = self.new_task(loop, asyncio.sleep(0.1, loop=loop)) + b = self.new_task(loop, asyncio.sleep(0.15, loop=loop)) @asyncio.coroutine def foo(): @@ -963,7 +978,7 @@ class TaskTests(test_utils.TestCase): self.assertEqual(done, set([a])) self.assertEqual(pending, set([b])) - loop.run_until_complete(asyncio.Task(foo(), loop=loop)) + loop.run_until_complete(self.new_task(loop, foo())) self.assertAlmostEqual(0.11, loop.time()) # move forward to close generator @@ -983,8 +998,8 @@ class TaskTests(test_utils.TestCase): loop = self.new_test_loop(gen) - a = asyncio.Task(asyncio.sleep(0.1, loop=loop), loop=loop) - b = asyncio.Task(asyncio.sleep(0.15, loop=loop), loop=loop) + a = self.new_task(loop, asyncio.sleep(0.1, loop=loop)) + b = self.new_task(loop, asyncio.sleep(0.15, loop=loop)) done, pending = loop.run_until_complete( asyncio.wait([b, a], timeout=0.1, loop=loop)) @@ -1032,14 +1047,14 @@ class TaskTests(test_utils.TestCase): values.append((yield from f)) return values - res = loop.run_until_complete(asyncio.Task(foo(), loop=loop)) + res = loop.run_until_complete(self.new_task(loop, foo())) self.assertAlmostEqual(0.15, loop.time()) self.assertTrue('a' in res[:2]) self.assertTrue('b' in res[:2]) self.assertEqual(res[2], 'c') # Doing it again should take no time and exercise a different path. - res = loop.run_until_complete(asyncio.Task(foo(), loop=loop)) + res = loop.run_until_complete(self.new_task(loop, foo())) self.assertAlmostEqual(0.15, loop.time()) def test_as_completed_with_timeout(self): @@ -1068,7 +1083,7 @@ class TaskTests(test_utils.TestCase): values.append((2, exc)) return values - res = loop.run_until_complete(asyncio.Task(foo(), loop=loop)) + res = loop.run_until_complete(self.new_task(loop, foo())) self.assertEqual(len(res), 2, res) self.assertEqual(res[0], (1, 'a')) self.assertEqual(res[1][0], 2) @@ -1096,7 +1111,7 @@ class TaskTests(test_utils.TestCase): v = yield from f self.assertEqual(v, 'a') - loop.run_until_complete(asyncio.Task(foo(), loop=loop)) + loop.run_until_complete(self.new_task(loop, foo())) def test_as_completed_reverse_wait(self): @@ -1156,7 +1171,7 @@ class TaskTests(test_utils.TestCase): result.append((yield from f)) return result - fut = asyncio.Task(runner(), loop=self.loop) + fut = self.new_task(self.loop, runner()) self.loop.run_until_complete(fut) result = fut.result() self.assertEqual(set(result), {'ham', 'spam'}) @@ -1179,7 +1194,7 @@ class TaskTests(test_utils.TestCase): res = yield from asyncio.sleep(dt/2, arg, loop=loop) return res - t = asyncio.Task(sleeper(0.1, 'yeah'), loop=loop) + t = self.new_task(loop, sleeper(0.1, 'yeah')) loop.run_until_complete(t) self.assertTrue(t.done()) self.assertEqual(t.result(), 'yeah') @@ -1194,8 +1209,7 @@ class TaskTests(test_utils.TestCase): loop = self.new_test_loop(gen) - t = asyncio.Task(asyncio.sleep(10.0, 'yeah', loop=loop), - loop=loop) + t = self.new_task(loop, asyncio.sleep(10.0, 'yeah', loop=loop)) handle = None orig_call_later = loop.call_later @@ -1231,7 +1245,7 @@ class TaskTests(test_utils.TestCase): @asyncio.coroutine def doit(): - sleeper = asyncio.Task(sleep(5000), loop=loop) + sleeper = self.new_task(loop, sleep(5000)) loop.call_later(0.1, sleeper.cancel) try: yield from sleeper @@ -1245,13 +1259,13 @@ class TaskTests(test_utils.TestCase): self.assertAlmostEqual(0.1, loop.time()) def test_task_cancel_waiter_future(self): - fut = asyncio.Future(loop=self.loop) + fut = self.new_future(self.loop) @asyncio.coroutine def coro(): yield from fut - task = asyncio.Task(coro(), loop=self.loop) + task = self.new_task(self.loop, coro()) test_utils.run_briefly(self.loop) self.assertIs(task._fut_waiter, fut) @@ -1268,7 +1282,7 @@ class TaskTests(test_utils.TestCase): return 'ko' gen = notmuch() - task = asyncio.Task(gen, loop=self.loop) + task = self.new_task(self.loop, gen) task.set_result('ok') self.assertRaises(AssertionError, task._step) @@ -1304,7 +1318,7 @@ class TaskTests(test_utils.TestCase): nonlocal result result = yield from fut - t = asyncio.Task(wait_for_future(), loop=self.loop) + t = self.new_task(self.loop, wait_for_future()) test_utils.run_briefly(self.loop) self.assertTrue(fut.cb_added) @@ -1320,7 +1334,7 @@ class TaskTests(test_utils.TestCase): def notmutch(): raise BaseException() - task = asyncio.Task(notmutch(), loop=self.loop) + task = self.new_task(self.loop, notmutch()) self.assertRaises(BaseException, task._step) self.assertTrue(task.done()) @@ -1348,7 +1362,7 @@ class TaskTests(test_utils.TestCase): except asyncio.CancelledError: raise base_exc - task = asyncio.Task(notmutch(), loop=loop) + task = self.new_task(loop, notmutch()) test_utils.run_briefly(loop) task.cancel() @@ -1376,7 +1390,7 @@ class TaskTests(test_utils.TestCase): self.assertTrue(asyncio.iscoroutinefunction(fn2)) def test_yield_vs_yield_from(self): - fut = asyncio.Future(loop=self.loop) + fut = self.new_future(self.loop) @asyncio.coroutine def wait_for_future(): @@ -1420,7 +1434,7 @@ class TaskTests(test_utils.TestCase): self.assertEqual(res, 'test') def test_coroutine_non_gen_function_return_future(self): - fut = asyncio.Future(loop=self.loop) + fut = self.new_future(self.loop) @asyncio.coroutine def func(): @@ -1430,49 +1444,53 @@ class TaskTests(test_utils.TestCase): def coro(): fut.set_result('test') - t1 = asyncio.Task(func(), loop=self.loop) - t2 = asyncio.Task(coro(), loop=self.loop) + t1 = self.new_task(self.loop, func()) + t2 = self.new_task(self.loop, coro()) res = self.loop.run_until_complete(t1) self.assertEqual(res, 'test') self.assertIsNone(t2.result()) def test_current_task(self): - self.assertIsNone(asyncio.Task.current_task(loop=self.loop)) + Task = self.__class__.Task + + self.assertIsNone(Task.current_task(loop=self.loop)) @asyncio.coroutine def coro(loop): - self.assertTrue(asyncio.Task.current_task(loop=loop) is task) + self.assertTrue(Task.current_task(loop=loop) is task) - task = asyncio.Task(coro(self.loop), loop=self.loop) + task = self.new_task(self.loop, coro(self.loop)) self.loop.run_until_complete(task) - self.assertIsNone(asyncio.Task.current_task(loop=self.loop)) + self.assertIsNone(Task.current_task(loop=self.loop)) def test_current_task_with_interleaving_tasks(self): - self.assertIsNone(asyncio.Task.current_task(loop=self.loop)) + Task = self.__class__.Task + + self.assertIsNone(Task.current_task(loop=self.loop)) - fut1 = asyncio.Future(loop=self.loop) - fut2 = asyncio.Future(loop=self.loop) + fut1 = self.new_future(self.loop) + fut2 = self.new_future(self.loop) @asyncio.coroutine def coro1(loop): - self.assertTrue(asyncio.Task.current_task(loop=loop) is task1) + self.assertTrue(Task.current_task(loop=loop) is task1) yield from fut1 - self.assertTrue(asyncio.Task.current_task(loop=loop) is task1) + self.assertTrue(Task.current_task(loop=loop) is task1) fut2.set_result(True) @asyncio.coroutine def coro2(loop): - self.assertTrue(asyncio.Task.current_task(loop=loop) is task2) + self.assertTrue(Task.current_task(loop=loop) is task2) fut1.set_result(True) yield from fut2 - self.assertTrue(asyncio.Task.current_task(loop=loop) is task2) + self.assertTrue(Task.current_task(loop=loop) is task2) - task1 = asyncio.Task(coro1(self.loop), loop=self.loop) - task2 = asyncio.Task(coro2(self.loop), loop=self.loop) + task1 = self.new_task(self.loop, coro1(self.loop)) + task2 = self.new_task(self.loop, coro2(self.loop)) self.loop.run_until_complete(asyncio.wait((task1, task2), loop=self.loop)) - self.assertIsNone(asyncio.Task.current_task(loop=self.loop)) + self.assertIsNone(Task.current_task(loop=self.loop)) # Some thorough tests for cancellation propagation through # coroutines, tasks and wait(). @@ -1480,7 +1498,7 @@ class TaskTests(test_utils.TestCase): def test_yield_future_passes_cancel(self): # Cancelling outer() cancels inner() cancels waiter. proof = 0 - waiter = asyncio.Future(loop=self.loop) + waiter = self.new_future(self.loop) @asyncio.coroutine def inner(): @@ -1514,7 +1532,7 @@ class TaskTests(test_utils.TestCase): # Cancelling outer() makes wait() return early, leaves inner() # running. proof = 0 - waiter = asyncio.Future(loop=self.loop) + waiter = self.new_future(self.loop) @asyncio.coroutine def inner(): @@ -1538,14 +1556,14 @@ class TaskTests(test_utils.TestCase): self.assertEqual(proof, 1) def test_shield_result(self): - inner = asyncio.Future(loop=self.loop) + inner = self.new_future(self.loop) outer = asyncio.shield(inner) inner.set_result(42) res = self.loop.run_until_complete(outer) self.assertEqual(res, 42) def test_shield_exception(self): - inner = asyncio.Future(loop=self.loop) + inner = self.new_future(self.loop) outer = asyncio.shield(inner) test_utils.run_briefly(self.loop) exc = RuntimeError('expected') @@ -1554,7 +1572,7 @@ class TaskTests(test_utils.TestCase): self.assertIs(outer.exception(), exc) def test_shield_cancel(self): - inner = asyncio.Future(loop=self.loop) + inner = self.new_future(self.loop) outer = asyncio.shield(inner) test_utils.run_briefly(self.loop) inner.cancel() @@ -1562,7 +1580,7 @@ class TaskTests(test_utils.TestCase): self.assertTrue(outer.cancelled()) def test_shield_shortcut(self): - fut = asyncio.Future(loop=self.loop) + fut = self.new_future(self.loop) fut.set_result(42) res = self.loop.run_until_complete(asyncio.shield(fut)) self.assertEqual(res, 42) @@ -1570,7 +1588,7 @@ class TaskTests(test_utils.TestCase): def test_shield_effect(self): # Cancelling outer() does not affect inner(). proof = 0 - waiter = asyncio.Future(loop=self.loop) + waiter = self.new_future(self.loop) @asyncio.coroutine def inner(): @@ -1594,8 +1612,8 @@ class TaskTests(test_utils.TestCase): self.assertEqual(proof, 1) def test_shield_gather(self): - child1 = asyncio.Future(loop=self.loop) - child2 = asyncio.Future(loop=self.loop) + child1 = self.new_future(self.loop) + child2 = self.new_future(self.loop) parent = asyncio.gather(child1, child2, loop=self.loop) outer = asyncio.shield(parent, loop=self.loop) test_utils.run_briefly(self.loop) @@ -1608,8 +1626,8 @@ class TaskTests(test_utils.TestCase): self.assertEqual(parent.result(), [1, 2]) def test_gather_shield(self): - child1 = asyncio.Future(loop=self.loop) - child2 = asyncio.Future(loop=self.loop) + child1 = self.new_future(self.loop) + child2 = self.new_future(self.loop) inner1 = asyncio.shield(child1, loop=self.loop) inner2 = asyncio.shield(child2, loop=self.loop) parent = asyncio.gather(inner1, inner2, loop=self.loop) @@ -1625,7 +1643,7 @@ class TaskTests(test_utils.TestCase): test_utils.run_briefly(self.loop) def test_as_completed_invalid_args(self): - fut = asyncio.Future(loop=self.loop) + fut = self.new_future(self.loop) # as_completed() expects a list of futures, not a future instance self.assertRaises(TypeError, self.loop.run_until_complete, @@ -1636,7 +1654,7 @@ class TaskTests(test_utils.TestCase): coro.close() def test_wait_invalid_args(self): - fut = asyncio.Future(loop=self.loop) + fut = self.new_future(self.loop) # wait() expects a list of futures, not a future instance self.assertRaises(TypeError, self.loop.run_until_complete, @@ -1663,7 +1681,7 @@ class TaskTests(test_utils.TestCase): yield from fut # A completed Future used to run the coroutine. - fut = asyncio.Future(loop=self.loop) + fut = self.new_future(self.loop) fut.set_result(None) # Call the coroutine. @@ -1697,15 +1715,15 @@ class TaskTests(test_utils.TestCase): @asyncio.coroutine def t2(): - f = asyncio.Future(loop=self.loop) - asyncio.Task(t3(f), loop=self.loop) + f = self.new_future(self.loop) + self.new_task(self.loop, t3(f)) return (yield from f) @asyncio.coroutine def t3(f): f.set_result((1, 2, 3)) - task = asyncio.Task(t1(), loop=self.loop) + task = self.new_task(self.loop, t1()) val = self.loop.run_until_complete(task) self.assertEqual(val, (1, 2, 3)) @@ -1768,9 +1786,11 @@ class TaskTests(test_utils.TestCase): @unittest.skipUnless(PY34, 'need python 3.4 or later') def test_log_destroyed_pending_task(self): + Task = self.__class__.Task + @asyncio.coroutine def kill_me(loop): - future = asyncio.Future(loop=loop) + future = self.new_future(loop) yield from future # at this point, the only reference to kill_me() task is # the Task._wakeup() method in future._callbacks @@ -1783,7 +1803,7 @@ class TaskTests(test_utils.TestCase): # schedule the task coro = kill_me(self.loop) task = asyncio.ensure_future(coro, loop=self.loop) - self.assertEqual(asyncio.Task.all_tasks(loop=self.loop), {task}) + self.assertEqual(Task.all_tasks(loop=self.loop), {task}) # execute the task so it waits for future self.loop._run_once() @@ -1798,7 +1818,7 @@ class TaskTests(test_utils.TestCase): # no more reference to kill_me() task: the task is destroyed by the GC support.gc_collect() - self.assertEqual(asyncio.Task.all_tasks(loop=self.loop), set()) + self.assertEqual(Task.all_tasks(loop=self.loop), set()) mock_handler.assert_called_with(self.loop, { 'message': 'Task was destroyed but it is pending!', @@ -1863,10 +1883,10 @@ class TaskTests(test_utils.TestCase): def test_task_source_traceback(self): self.loop.set_debug(True) - task = asyncio.Task(coroutine_function(), loop=self.loop) + task = self.new_task(self.loop, coroutine_function()) lineno = sys._getframe().f_lineno - 1 self.assertIsInstance(task._source_traceback, list) - self.assertEqual(task._source_traceback[-1][:3], + self.assertEqual(task._source_traceback[-2][:3], (__file__, lineno, 'test_task_source_traceback')) @@ -1878,7 +1898,7 @@ class TaskTests(test_utils.TestCase): @asyncio.coroutine def blocking_coroutine(): - fut = asyncio.Future(loop=loop) + fut = self.new_future(loop) # Block: fut result is never set yield from fut @@ -1905,7 +1925,7 @@ class TaskTests(test_utils.TestCase): loop = asyncio.new_event_loop() self.addCleanup(loop.close) - fut = asyncio.Future(loop=loop) + fut = self.new_future(loop) # The indirection fut->child_coro is needed since otherwise the # gathering task is done at the same time as the child future def child_coro(): @@ -1929,6 +1949,157 @@ class TaskTests(test_utils.TestCase): self.assertFalse(gather_task.cancelled()) self.assertEqual(gather_task.result(), [42]) + @mock.patch('asyncio.base_events.logger') + def test_error_in_call_soon(self, m_log): + def call_soon(callback, *args): + raise ValueError + self.loop.call_soon = call_soon + + @asyncio.coroutine + def coro(): + pass + + self.assertFalse(m_log.error.called) + + with self.assertRaises(ValueError): + self.new_task(self.loop, coro()) + + self.assertTrue(m_log.error.called) + message = m_log.error.call_args[0][0] + self.assertIn('Task was destroyed but it is pending', message) + + self.assertEqual(self.Task.all_tasks(self.loop), set()) + + +def add_subclass_tests(cls): + BaseTask = cls.Task + BaseFuture = cls.Future + + if BaseTask is None or BaseFuture is None: + return cls + + class CommonFuture: + def __init__(self, *args, **kwargs): + self.calls = collections.defaultdict(lambda: 0) + super().__init__(*args, **kwargs) + + def _schedule_callbacks(self): + self.calls['_schedule_callbacks'] += 1 + return super()._schedule_callbacks() + + def add_done_callback(self, *args): + self.calls['add_done_callback'] += 1 + return super().add_done_callback(*args) + + class Task(CommonFuture, BaseTask): + def _step(self, *args): + self.calls['_step'] += 1 + return super()._step(*args) + + def _wakeup(self, *args): + self.calls['_wakeup'] += 1 + return super()._wakeup(*args) + + class Future(CommonFuture, BaseFuture): + pass + + def test_subclasses_ctask_cfuture(self): + fut = self.Future(loop=self.loop) + + async def func(): + self.loop.call_soon(lambda: fut.set_result('spam')) + return await fut + + task = self.Task(func(), loop=self.loop) + + result = self.loop.run_until_complete(task) + + self.assertEqual(result, 'spam') + + self.assertEqual( + dict(task.calls), + {'_step': 2, '_wakeup': 1, 'add_done_callback': 1, + '_schedule_callbacks': 1}) + + self.assertEqual( + dict(fut.calls), + {'add_done_callback': 1, '_schedule_callbacks': 1}) + + # Add patched Task & Future back to the test case + cls.Task = Task + cls.Future = Future + + # Add an extra unit-test + cls.test_subclasses_ctask_cfuture = test_subclasses_ctask_cfuture + + # Disable the "test_task_source_traceback" test + # (the test is hardcoded for a particular call stack, which + # is slightly different for Task subclasses) + cls.test_task_source_traceback = None + + return cls + + +@unittest.skipUnless(hasattr(futures, '_CFuture'), + 'requires the C _asyncio module') +class CTask_CFuture_Tests(BaseTaskTests, test_utils.TestCase): + Task = getattr(tasks, '_CTask', None) + Future = getattr(futures, '_CFuture', None) + + +@unittest.skipUnless(hasattr(futures, '_CFuture'), + 'requires the C _asyncio module') +@add_subclass_tests +class CTask_CFuture_SubclassTests(BaseTaskTests, test_utils.TestCase): + Task = getattr(tasks, '_CTask', None) + Future = getattr(futures, '_CFuture', None) + + +@unittest.skipUnless(hasattr(futures, '_CFuture'), + 'requires the C _asyncio module') +class CTask_PyFuture_Tests(BaseTaskTests, test_utils.TestCase): + Task = getattr(tasks, '_CTask', None) + Future = futures._PyFuture + + +@unittest.skipUnless(hasattr(futures, '_CFuture'), + 'requires the C _asyncio module') +class PyTask_CFuture_Tests(BaseTaskTests, test_utils.TestCase): + Task = tasks._PyTask + Future = getattr(futures, '_CFuture', None) + + +class PyTask_PyFuture_Tests(BaseTaskTests, test_utils.TestCase): + Task = tasks._PyTask + Future = futures._PyFuture + + +@add_subclass_tests +class PyTask_PyFuture_SubclassTests(BaseTaskTests, test_utils.TestCase): + Task = tasks._PyTask + Future = futures._PyFuture + + +class GenericTaskTests(test_utils.TestCase): + + def test_future_subclass(self): + self.assertTrue(issubclass(asyncio.Task, asyncio.Future)) + + def test_asyncio_module_compiled(self): + # Because of circular imports it's easy to make _asyncio + # module non-importable. This is a simple test that will + # fail on systems where C modules were successfully compiled + # (hence the test for _functools), but _asyncio somehow didn't. + try: + import _functools + except ImportError: + pass + else: + try: + import _asyncio + except ImportError: + self.fail('_asyncio module is missing') + class GatherTestsBase: -- cgit v1.2.1 From b2dc62c41891db3469edecdae61bb2a40f2169c0 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Sat, 29 Oct 2016 08:50:31 -0700 Subject: Makes test_underpth* tests more robust by copying the executable. --- Lib/test/test_site.py | 73 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 25 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index 5aedbdb801..d245fd5e1b 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -14,6 +14,7 @@ import re import encodings import urllib.request import urllib.error +import shutil import subprocess import sysconfig from copy import copy @@ -488,22 +489,44 @@ class StartupImportTests(unittest.TestCase): 'import site, sys; site.enablerlcompleter(); sys.exit(hasattr(sys, "__interactivehook__"))']).wait() self.assertTrue(r, "'__interactivehook__' not added by enablerlcompleter()") - @unittest.skipUnless(sys.platform == 'win32', "only supported on Windows") - def test_underpth_nosite_file(self): - _pth_file = os.path.splitext(sys.executable)[0] + '._pth' + @classmethod + def _create_underpth_exe(self, lines): + exe_file = os.path.join(os.getenv('TEMP'), os.path.split(sys.executable)[1]) + shutil.copy(sys.executable, exe_file) + + _pth_file = os.path.splitext(exe_file)[0] + '._pth' try: - libpath = os.path.dirname(os.path.dirname(encodings.__file__)) with open(_pth_file, 'w') as f: - print('fake-path-name', file=f) - # Ensure the generated path is very long so that buffer - # resizing in getpathp.c is exercised - for _ in range(200): - print(libpath, file=f) - print('# comment', file=f) + for line in lines: + print(line, file=f) + return exe_file + except: + os.unlink(_pth_file) + os.unlink(exe_file) + raise + + @classmethod + def _cleanup_underpth_exe(self, exe_file): + _pth_file = os.path.splitext(exe_file)[0] + '._pth' + os.unlink(_pth_file) + os.unlink(exe_file) + + @unittest.skipUnless(sys.platform == 'win32', "only supported on Windows") + def test_underpth_nosite_file(self): + libpath = os.path.dirname(os.path.dirname(encodings.__file__)) + exe_prefix = os.path.dirname(sys.executable) + exe_file = self._create_underpth_exe([ + 'fake-path-name', + *[libpath for _ in range(200)], + '# comment', + 'import site' + ]) + try: env = os.environ.copy() env['PYTHONPATH'] = 'from-env' - rc = subprocess.call([sys.executable, '-c', + env['PATH'] = '{};{}'.format(exe_prefix, os.getenv('PATH')) + rc = subprocess.call([exe_file, '-c', 'import sys; sys.exit(sys.flags.no_site and ' 'len(sys.path) > 200 and ' '%r in sys.path and %r in sys.path and %r not in sys.path)' % ( @@ -511,34 +534,34 @@ class StartupImportTests(unittest.TestCase): libpath, os.path.join(sys.prefix, 'from-env'), )], env=env) - self.assertEqual(rc, 0) finally: - os.unlink(_pth_file) + self._cleanup_underpth_exe(exe_file) + self.assertEqual(rc, 0) @unittest.skipUnless(sys.platform == 'win32', "only supported on Windows") def test_underpth_file(self): - _pth_file = os.path.splitext(sys.executable)[0] + '._pth' + libpath = os.path.dirname(os.path.dirname(encodings.__file__)) + exe_prefix = os.path.dirname(sys.executable) + exe_file = self._create_underpth_exe([ + 'fake-path-name', + *[libpath for _ in range(200)], + '# comment', + 'import site' + ]) try: - libpath = os.path.dirname(os.path.dirname(encodings.__file__)) - with open(_pth_file, 'w') as f: - print('fake-path-name', file=f) - for _ in range(200): - print(libpath, file=f) - print('# comment', file=f) - print('import site', file=f) - env = os.environ.copy() env['PYTHONPATH'] = 'from-env' - rc = subprocess.call([sys.executable, '-c', + env['PATH'] = '{};{}'.format(exe_prefix, os.getenv('PATH')) + rc = subprocess.call([exe_file, '-c', 'import sys; sys.exit(not sys.flags.no_site and ' '%r in sys.path and %r in sys.path and %r not in sys.path)' % ( os.path.join(sys.prefix, 'fake-path-name'), libpath, os.path.join(sys.prefix, 'from-env'), )], env=env) - self.assertEqual(rc, 0) finally: - os.unlink(_pth_file) + self._cleanup_underpth_exe(exe_file) + self.assertEqual(rc, 0) if __name__ == "__main__": -- cgit v1.2.1 From 83e729ee48906dd1146dff2677ba8850df3667c9 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sat, 29 Oct 2016 16:55:36 -0700 Subject: Issue #18844: Make the various ways for specifing weights produce the same results. --- Lib/random.py | 7 ++++--- Lib/test/test_random.py | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) (limited to 'Lib') diff --git a/Lib/random.py b/Lib/random.py index ef8cb05601..a047444502 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -344,10 +344,12 @@ class Random(_random.Random): the selections are made with equal probability. """ + random = self.random if cum_weights is None: if weights is None: - choice = self.choice - return [choice(population) for i in range(k)] + _int = int + total = len(population) + return [population[_int(random() * total)] for i in range(k)] else: cum_weights = list(_itertools.accumulate(weights)) elif weights is not None: @@ -355,7 +357,6 @@ class Random(_random.Random): if len(cum_weights) != len(population): raise ValueError('The number of weights does not match the population') bisect = _bisect.bisect - random = self.random total = cum_weights[-1] return [population[bisect(cum_weights, random() * total)] for i in range(k)] diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py index 4d5a8749c7..dd2715288a 100644 --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -629,6 +629,22 @@ class MersenneTwister_TestBasicOps(TestBasicOps, unittest.TestCase): self.assertTrue(stop < x <= start) self.assertEqual((x+stop)%step, 0) + def test_choices_algorithms(self): + # The various ways of specifing weights should produce the same results + choices = self.gen.choices + n = 13132817 + + self.gen.seed(8675309) + a = self.gen.choices(range(n), k=10000) + + self.gen.seed(8675309) + b = self.gen.choices(range(n), [1]*n, k=10000) + self.assertEqual(a, b) + + self.gen.seed(8675309) + c = self.gen.choices(range(n), cum_weights=range(1, n+1), k=10000) + self.assertEqual(a, c) + def gamma(z, sqrt2pi=(2.0*pi)**0.5): # Reflection to right half of complex plane if z < 0.5: -- cgit v1.2.1 From f43d6134fa2523175aaded4b2d4a2662236d2bfd Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sat, 29 Oct 2016 17:42:36 -0700 Subject: Issue #18844: Strengthen tests to include a case with unequal weighting --- Lib/test/test_random.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py index dd2715288a..a4413b5146 100644 --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -645,6 +645,23 @@ class MersenneTwister_TestBasicOps(TestBasicOps, unittest.TestCase): c = self.gen.choices(range(n), cum_weights=range(1, n+1), k=10000) self.assertEqual(a, c) + # Amerian Roulette + population = ['Red', 'Black', 'Green'] + weights = [18, 18, 2] + cum_weights = [18, 36, 38] + expanded_population = ['Red'] * 18 + ['Black'] * 18 + ['Green'] * 2 + + self.gen.seed(9035768) + a = self.gen.choices(expanded_population, k=10000) + + self.gen.seed(9035768) + b = self.gen.choices(population, weights, k=10000) + self.assertEqual(a, b) + + self.gen.seed(9035768) + c = self.gen.choices(population, cum_weights=cum_weights, k=10000) + self.assertEqual(a, c) + def gamma(z, sqrt2pi=(2.0*pi)**0.5): # Reflection to right half of complex plane if z < 0.5: -- cgit v1.2.1 From 3671ca2d2c23e33abf5723720545b836adeb8efa Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 30 Oct 2016 23:00:01 +0200 Subject: Issue #28541: Improve test coverage for encoding detection in json library. Original patch by Eric Appelt. --- Lib/json/__init__.py | 3 ++- Lib/test/test_json/test_unicode.py | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/json/__init__.py b/Lib/json/__init__.py index 8dcc6786e2..94397aa4e9 100644 --- a/Lib/json/__init__.py +++ b/Lib/json/__init__.py @@ -257,7 +257,8 @@ def detect_encoding(b): return 'utf-16-be' if b[1] else 'utf-32-be' if not b[1]: # XX 00 00 00 - utf-32-le - # XX 00 XX XX - utf-16-le + # XX 00 00 XX - utf-16-le + # XX 00 XX -- - utf-16-le return 'utf-16-le' if b[2] or b[3] else 'utf-32-le' elif len(b) == 2: if not b[0]: diff --git a/Lib/test/test_json/test_unicode.py b/Lib/test/test_json/test_unicode.py index eda177aa68..2e8bba2775 100644 --- a/Lib/test/test_json/test_unicode.py +++ b/Lib/test/test_json/test_unicode.py @@ -65,6 +65,19 @@ class TestUnicode: self.assertEqual(self.loads(bom + encoded), data) self.assertEqual(self.loads(encoded), data) self.assertRaises(UnicodeDecodeError, self.loads, b'["\x80"]') + # RFC-7159 and ECMA-404 extend JSON to allow documents that + # consist of only a string, which can present a special case + # not covered by the encoding detection patterns specified in + # RFC-4627 for utf-16-le (XX 00 XX 00). + self.assertEqual(self.loads('"\u2600"'.encode('utf-16-le')), + '\u2600') + # Encoding detection for small (<4) bytes objects + # is implemented as a special case. RFC-7159 and ECMA-404 + # allow single codepoint JSON documents which are only two + # bytes in utf-16 encodings w/o BOM. + self.assertEqual(self.loads(b'5\x00'), 5) + self.assertEqual(self.loads(b'\x007'), 7) + self.assertEqual(self.loads(b'57'), 57) def test_object_pairs_hook_with_unicode(self): s = '{"xkd":1, "kcw":2, "art":3, "hxm":4, "qrt":5, "pad":6, "hoy":7}' -- cgit v1.2.1 From 3c0fabc4a54394eb81cabed1f33e077e83011d8d Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 31 Oct 2016 08:13:00 +0200 Subject: Update the f-string test broken in issue #28385. --- Lib/test/test_fstring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index 92995fd83e..0af93e088f 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -720,7 +720,7 @@ f'{a * x()}'""" def test_errors(self): # see issue 26287 - self.assertAllRaise(TypeError, 'non-empty', + self.assertAllRaise(TypeError, 'unsupported', [r"f'{(lambda: 0):x}'", r"f'{(0,):x}'", ]) -- cgit v1.2.1 From ef9f0416d8818ee0728b56b29ff148623101784a Mon Sep 17 00:00:00 2001 From: "Eric V. Smith" Date: Mon, 31 Oct 2016 14:46:26 -0400 Subject: Issue 28128: Print out better error/warning messages for invalid string escapes. Backport to 3.6. --- Lib/test/test_string_literals.py | 27 +++++++++++++++++++++++++++ Lib/test/test_unicode.py | 7 ------- 2 files changed, 27 insertions(+), 7 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_string_literals.py b/Lib/test/test_string_literals.py index 37ace230f5..54f2be3598 100644 --- a/Lib/test/test_string_literals.py +++ b/Lib/test/test_string_literals.py @@ -31,6 +31,7 @@ import os import sys import shutil import tempfile +import warnings import unittest @@ -104,6 +105,19 @@ class TestLiterals(unittest.TestCase): self.assertRaises(SyntaxError, eval, r""" '\U000000' """) self.assertRaises(SyntaxError, eval, r""" '\U0000000' """) + def test_eval_str_invalid_escape(self): + for b in range(1, 128): + if b in b"""\n\r"'01234567NU\\abfnrtuvx""": + continue + with self.assertWarns(DeprecationWarning): + self.assertEqual(eval(r"'\%c'" % b), '\\' + chr(b)) + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter('always', category=DeprecationWarning) + eval("'''\n\\z'''") + self.assertEqual(len(w), 1) + self.assertEqual(w[0].filename, '') + self.assertEqual(w[0].lineno, 2) + def test_eval_str_raw(self): self.assertEqual(eval(""" r'x' """), 'x') self.assertEqual(eval(r""" r'\x01' """), '\\' + 'x01') @@ -130,6 +144,19 @@ class TestLiterals(unittest.TestCase): self.assertRaises(SyntaxError, eval, r""" b'\x' """) self.assertRaises(SyntaxError, eval, r""" b'\x0' """) + def test_eval_bytes_invalid_escape(self): + for b in range(1, 128): + if b in b"""\n\r"'01234567\\abfnrtvx""": + continue + with self.assertWarns(DeprecationWarning): + self.assertEqual(eval(r"b'\%c'" % b), b'\\' + bytes([b])) + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter('always', category=DeprecationWarning) + eval("b'''\n\\z'''") + self.assertEqual(len(w), 1) + self.assertEqual(w[0].filename, '') + self.assertEqual(w[0].lineno, 2) + def test_eval_bytes_raw(self): self.assertEqual(eval(""" br'x' """), b'x') self.assertEqual(eval(""" rb'x' """), b'x') diff --git a/Lib/test/test_unicode.py b/Lib/test/test_unicode.py index fe6cd28c63..0737140ccf 100644 --- a/Lib/test/test_unicode.py +++ b/Lib/test/test_unicode.py @@ -2413,13 +2413,6 @@ class UnicodeTest(string_tests.CommonTest, support.check_free_after_iterating(self, iter, str) support.check_free_after_iterating(self, reversed, str) - def test_invalid_sequences(self): - for letter in string.ascii_letters + "89": # 0-7 are octal escapes - if letter in "abfnrtuvxNU": - continue - with self.assertWarns(DeprecationWarning): - eval(r"'\%s'" % letter) - class CAPITest(unittest.TestCase): -- cgit v1.2.1 From 3d2a33ba717d6c2c3d58a1fc36aecbc59b78025d Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Mon, 31 Oct 2016 20:39:38 -0400 Subject: Update pydoc topics for 3.6.0b3 --- Lib/pydoc_data/topics.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'Lib') diff --git a/Lib/pydoc_data/topics.py b/Lib/pydoc_data/topics.py index aaf2ce65f1..914e8184f6 100644 --- a/Lib/pydoc_data/topics.py +++ b/Lib/pydoc_data/topics.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Autogenerated by Sphinx on Mon Oct 10 15:59:17 2016 +# Autogenerated by Sphinx on Mon Oct 31 20:37:53 2016 topics = {'assert': '\n' 'The "assert" statement\n' '**********************\n' @@ -5066,9 +5066,9 @@ topics = {'assert': '\n' 'be formatted\n' 'with the floating point presentation types listed below ' '(except "\'n\'"\n' - 'and None). When doing so, "float()" is used to convert the ' - 'integer to\n' - 'a floating point number before formatting.\n' + 'and "None"). When doing so, "float()" is used to convert ' + 'the integer\n' + 'to a floating point number before formatting.\n' '\n' 'The available presentation types for floating point and ' 'decimal values\n' @@ -9626,7 +9626,7 @@ topics = {'assert': '\n' ' Unicode ordinals (integers) or characters (strings of ' 'length 1) to\n' ' Unicode ordinals, strings (of arbitrary lengths) or ' - 'None.\n' + '"None".\n' ' Character keys will then be converted to ordinals.\n' '\n' ' If there are two arguments, they must be strings of ' @@ -9637,7 +9637,7 @@ topics = {'assert': '\n' 'is a third\n' ' argument, it must be a string, whose characters will be ' 'mapped to\n' - ' None in the result.\n' + ' "None" in the result.\n' '\n' 'str.partition(sep)\n' '\n' @@ -11204,7 +11204,7 @@ topics = {'assert': '\n' 'the\n' ' order of their occurrence in the base class list; "__doc__" is ' 'the\n' - " class's documentation string, or None if undefined;\n" + ' class\'s documentation string, or "None" if undefined;\n' ' "__annotations__" (optional) is a dictionary containing ' '*variable\n' ' annotations* collected during class body execution.\n' -- cgit v1.2.1 From c69a53274fc0797114e1f8518df8bc11eff1661a Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 1 Nov 2016 22:23:11 -0700 Subject: Minor code beautification --- Lib/random.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/random.py b/Lib/random.py index a047444502..d557acc92b 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -350,8 +350,7 @@ class Random(_random.Random): _int = int total = len(population) return [population[_int(random() * total)] for i in range(k)] - else: - cum_weights = list(_itertools.accumulate(weights)) + cum_weights = list(_itertools.accumulate(weights)) elif weights is not None: raise TypeError('Cannot specify both weights and cumulative_weights') if len(cum_weights) != len(population): -- cgit v1.2.1 From 66219784a7c7c2319dab4ae2387788441f51b581 Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Wed, 2 Nov 2016 18:45:16 +0900 Subject: Issue #28583: PyDict_SetDefault didn't combine split table when needed. Patch by Xiang Zhang. --- Lib/test/test_dict.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py index ed66ddbcb4..20547df3bb 100644 --- a/Lib/test/test_dict.py +++ b/Lib/test/test_dict.py @@ -851,6 +851,23 @@ class DictTest(unittest.TestCase): return dicts + @support.cpython_only + def test_splittable_setdefault(self): + """split table must be combined when setdefault() + breaks insertion order""" + a, b = self.make_shared_key_dict(2) + + a['a'] = 1 + size_a = sys.getsizeof(a) + a['b'] = 2 + b.setdefault('b', 2) + size_b = sys.getsizeof(b) + b['a'] = 1 + + self.assertGreater(size_b, size_a) + self.assertEqual(list(a), ['x', 'y', 'z', 'a', 'b']) + self.assertEqual(list(b), ['x', 'y', 'z', 'b', 'a']) + @support.cpython_only def test_splittable_del(self): """split table must be combined when del d[k]""" -- cgit v1.2.1 From ca0deeab016a16be6120ea6d9450cb0b46447694 Mon Sep 17 00:00:00 2001 From: Donald Stufft Date: Wed, 2 Nov 2016 20:32:37 -0400 Subject: Allow ensurepip even when ssl is unavailable --- Lib/ensurepip/__init__.py | 20 -------------- Lib/test/test_ensurepip.py | 65 ---------------------------------------------- Lib/test/test_venv.py | 8 ------ 3 files changed, 93 deletions(-) (limited to 'Lib') diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py index 68853ef012..7fd81c4cea 100644 --- a/Lib/ensurepip/__init__.py +++ b/Lib/ensurepip/__init__.py @@ -12,19 +12,6 @@ _SETUPTOOLS_VERSION = "28.7.1" _PIP_VERSION = "9.0.0" -# pip currently requires ssl support, so we try to provide a nicer -# error message when that is missing (http://bugs.python.org/issue19744) -_MISSING_SSL_MESSAGE = ("pip {} requires SSL/TLS".format(_PIP_VERSION)) -try: - import ssl -except ImportError: - ssl = None - def _require_ssl_for_pip(): - raise RuntimeError(_MISSING_SSL_MESSAGE) -else: - def _require_ssl_for_pip(): - pass - _PROJECTS = [ ("setuptools", _SETUPTOOLS_VERSION), ("pip", _PIP_VERSION), @@ -71,7 +58,6 @@ def bootstrap(*, root=None, upgrade=False, user=False, if altinstall and default_pip: raise ValueError("Cannot use altinstall and default_pip together") - _require_ssl_for_pip() _disable_pip_configuration_settings() # By default, installing pip and setuptools installs all of the @@ -133,7 +119,6 @@ def _uninstall_helper(*, verbosity=0): print(msg.format(pip.__version__, _PIP_VERSION), file=sys.stderr) return - _require_ssl_for_pip() _disable_pip_configuration_settings() # Construct the arguments to be passed to the pip command @@ -145,11 +130,6 @@ def _uninstall_helper(*, verbosity=0): def _main(argv=None): - if ssl is None: - print("Ignoring ensurepip failure: {}".format(_MISSING_SSL_MESSAGE), - file=sys.stderr) - return - import argparse parser = argparse.ArgumentParser(prog="python -m ensurepip") parser.add_argument( diff --git a/Lib/test/test_ensurepip.py b/Lib/test/test_ensurepip.py index a78ca14886..9b04c18b0e 100644 --- a/Lib/test/test_ensurepip.py +++ b/Lib/test/test_ensurepip.py @@ -9,19 +9,6 @@ import sys import ensurepip import ensurepip._uninstall -# pip currently requires ssl support, so we ensure we handle -# it being missing (http://bugs.python.org/issue19744) -ensurepip_no_ssl = test.support.import_fresh_module("ensurepip", - blocked=["ssl"]) -try: - import ssl -except ImportError: - def requires_usable_pip(f): - deco = unittest.skip(ensurepip._MISSING_SSL_MESSAGE) - return deco(f) -else: - def requires_usable_pip(f): - return f class TestEnsurePipVersion(unittest.TestCase): @@ -47,7 +34,6 @@ class EnsurepipMixin: class TestBootstrap(EnsurepipMixin, unittest.TestCase): - @requires_usable_pip def test_basic_bootstrapping(self): ensurepip.bootstrap() @@ -62,7 +48,6 @@ class TestBootstrap(EnsurepipMixin, unittest.TestCase): additional_paths = self.run_pip.call_args[0][1] self.assertEqual(len(additional_paths), 2) - @requires_usable_pip def test_bootstrapping_with_root(self): ensurepip.bootstrap(root="/foo/bar/") @@ -75,7 +60,6 @@ class TestBootstrap(EnsurepipMixin, unittest.TestCase): unittest.mock.ANY, ) - @requires_usable_pip def test_bootstrapping_with_user(self): ensurepip.bootstrap(user=True) @@ -87,7 +71,6 @@ class TestBootstrap(EnsurepipMixin, unittest.TestCase): unittest.mock.ANY, ) - @requires_usable_pip def test_bootstrapping_with_upgrade(self): ensurepip.bootstrap(upgrade=True) @@ -99,7 +82,6 @@ class TestBootstrap(EnsurepipMixin, unittest.TestCase): unittest.mock.ANY, ) - @requires_usable_pip def test_bootstrapping_with_verbosity_1(self): ensurepip.bootstrap(verbosity=1) @@ -111,7 +93,6 @@ class TestBootstrap(EnsurepipMixin, unittest.TestCase): unittest.mock.ANY, ) - @requires_usable_pip def test_bootstrapping_with_verbosity_2(self): ensurepip.bootstrap(verbosity=2) @@ -123,7 +104,6 @@ class TestBootstrap(EnsurepipMixin, unittest.TestCase): unittest.mock.ANY, ) - @requires_usable_pip def test_bootstrapping_with_verbosity_3(self): ensurepip.bootstrap(verbosity=3) @@ -135,17 +115,14 @@ class TestBootstrap(EnsurepipMixin, unittest.TestCase): unittest.mock.ANY, ) - @requires_usable_pip def test_bootstrapping_with_regular_install(self): ensurepip.bootstrap() self.assertEqual(self.os_environ["ENSUREPIP_OPTIONS"], "install") - @requires_usable_pip def test_bootstrapping_with_alt_install(self): ensurepip.bootstrap(altinstall=True) self.assertEqual(self.os_environ["ENSUREPIP_OPTIONS"], "altinstall") - @requires_usable_pip def test_bootstrapping_with_default_pip(self): ensurepip.bootstrap(default_pip=True) self.assertNotIn("ENSUREPIP_OPTIONS", self.os_environ) @@ -155,7 +132,6 @@ class TestBootstrap(EnsurepipMixin, unittest.TestCase): ensurepip.bootstrap(altinstall=True, default_pip=True) self.assertFalse(self.run_pip.called) - @requires_usable_pip def test_pip_environment_variables_removed(self): # ensurepip deliberately ignores all pip environment variables # See http://bugs.python.org/issue19734 for details @@ -163,7 +139,6 @@ class TestBootstrap(EnsurepipMixin, unittest.TestCase): ensurepip.bootstrap() self.assertNotIn("PIP_THIS_SHOULD_GO_AWAY", self.os_environ) - @requires_usable_pip def test_pip_config_file_disabled(self): # ensurepip deliberately ignores the pip config file # See http://bugs.python.org/issue20053 for details @@ -205,7 +180,6 @@ class TestUninstall(EnsurepipMixin, unittest.TestCase): self.assertFalse(self.run_pip.called) - @requires_usable_pip def test_uninstall(self): with fake_pip(): ensurepip._uninstall_helper() @@ -217,7 +191,6 @@ class TestUninstall(EnsurepipMixin, unittest.TestCase): ] ) - @requires_usable_pip def test_uninstall_with_verbosity_1(self): with fake_pip(): ensurepip._uninstall_helper(verbosity=1) @@ -229,7 +202,6 @@ class TestUninstall(EnsurepipMixin, unittest.TestCase): ] ) - @requires_usable_pip def test_uninstall_with_verbosity_2(self): with fake_pip(): ensurepip._uninstall_helper(verbosity=2) @@ -241,7 +213,6 @@ class TestUninstall(EnsurepipMixin, unittest.TestCase): ] ) - @requires_usable_pip def test_uninstall_with_verbosity_3(self): with fake_pip(): ensurepip._uninstall_helper(verbosity=3) @@ -253,7 +224,6 @@ class TestUninstall(EnsurepipMixin, unittest.TestCase): ] ) - @requires_usable_pip def test_pip_environment_variables_removed(self): # ensurepip deliberately ignores all pip environment variables # See http://bugs.python.org/issue19734 for details @@ -262,7 +232,6 @@ class TestUninstall(EnsurepipMixin, unittest.TestCase): ensurepip._uninstall_helper() self.assertNotIn("PIP_THIS_SHOULD_GO_AWAY", self.os_environ) - @requires_usable_pip def test_pip_config_file_disabled(self): # ensurepip deliberately ignores the pip config file # See http://bugs.python.org/issue20053 for details @@ -271,44 +240,12 @@ class TestUninstall(EnsurepipMixin, unittest.TestCase): self.assertEqual(self.os_environ["PIP_CONFIG_FILE"], os.devnull) -class TestMissingSSL(EnsurepipMixin, unittest.TestCase): - - def setUp(self): - sys.modules["ensurepip"] = ensurepip_no_ssl - @self.addCleanup - def restore_module(): - sys.modules["ensurepip"] = ensurepip - super().setUp() - - def test_bootstrap_requires_ssl(self): - self.os_environ["PIP_THIS_SHOULD_STAY"] = "test fodder" - with self.assertRaisesRegex(RuntimeError, "requires SSL/TLS"): - ensurepip_no_ssl.bootstrap() - self.assertFalse(self.run_pip.called) - self.assertIn("PIP_THIS_SHOULD_STAY", self.os_environ) - - def test_uninstall_requires_ssl(self): - self.os_environ["PIP_THIS_SHOULD_STAY"] = "test fodder" - with self.assertRaisesRegex(RuntimeError, "requires SSL/TLS"): - with fake_pip(): - ensurepip_no_ssl._uninstall_helper() - self.assertFalse(self.run_pip.called) - self.assertIn("PIP_THIS_SHOULD_STAY", self.os_environ) - - def test_main_exits_early_with_warning(self): - with test.support.captured_stderr() as stderr: - ensurepip_no_ssl._main(["--version"]) - warning = stderr.getvalue().strip() - self.assertTrue(warning.endswith("requires SSL/TLS"), warning) - self.assertFalse(self.run_pip.called) - # Basic testing of the main functions and their argument parsing EXPECTED_VERSION_OUTPUT = "pip " + ensurepip._PIP_VERSION class TestBootstrappingMainFunction(EnsurepipMixin, unittest.TestCase): - @requires_usable_pip def test_bootstrap_version(self): with test.support.captured_stdout() as stdout: with self.assertRaises(SystemExit): @@ -317,7 +254,6 @@ class TestBootstrappingMainFunction(EnsurepipMixin, unittest.TestCase): self.assertEqual(result, EXPECTED_VERSION_OUTPUT) self.assertFalse(self.run_pip.called) - @requires_usable_pip def test_basic_bootstrapping(self): ensurepip._main([]) @@ -342,7 +278,6 @@ class TestUninstallationMainFunction(EnsurepipMixin, unittest.TestCase): self.assertEqual(result, EXPECTED_VERSION_OUTPUT) self.assertFalse(self.run_pip.called) - @requires_usable_pip def test_basic_uninstall(self): with fake_pip(): ensurepip._uninstall._main([]) diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index f4ad7c7c5c..0ff978fc8c 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -18,12 +18,6 @@ from test.support import (captured_stdout, captured_stderr, import unittest import venv -# pip currently requires ssl support, so we ensure we handle -# it being missing (http://bugs.python.org/issue19744) -try: - import ssl -except ImportError: - ssl = None try: import threading @@ -337,8 +331,6 @@ class EnsurePipTest(BaseTest): self.assertTrue(os.path.exists(os.devnull)) - # Requesting pip fails without SSL (http://bugs.python.org/issue19744) - @unittest.skipIf(ssl is None, ensurepip._MISSING_SSL_MESSAGE) @unittest.skipUnless(threading, 'some dependencies of pip import threading' ' module unconditionally') # Issue #26610: pip/pep425tags.py requires ctypes -- cgit v1.2.1 From 93efefd659c461bf8e8468136f9cd9c4476f522b Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 6 Nov 2016 13:18:24 +0200 Subject: Issue #28123: _PyDict_GetItem_KnownHash() now can raise an exception as PyDict_GetItemWithError(). Patch by Xiang Zhang. --- Lib/test/test_dict.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py index 20547df3bb..cd077ff0e0 100644 --- a/Lib/test/test_dict.py +++ b/Lib/test/test_dict.py @@ -1055,6 +1055,37 @@ class DictTest(unittest.TestCase): support.check_free_after_iterating(self, lambda d: iter(d.values()), dict) support.check_free_after_iterating(self, lambda d: iter(d.items()), dict) + +class CAPITest(unittest.TestCase): + + # Test _PyDict_GetItem_KnownHash() + @support.cpython_only + def test_getitem_knownhash(self): + from _testcapi import dict_getitem_knownhash + + d = {'x': 1, 'y': 2, 'z': 3} + self.assertEqual(dict_getitem_knownhash(d, 'x', hash('x')), 1) + self.assertEqual(dict_getitem_knownhash(d, 'y', hash('y')), 2) + self.assertEqual(dict_getitem_knownhash(d, 'z', hash('z')), 3) + + # not a dict + self.assertRaises(SystemError, dict_getitem_knownhash, [], 1, hash(1)) + # key does not exist + self.assertRaises(KeyError, dict_getitem_knownhash, {}, 1, hash(1)) + + class Exc(Exception): pass + class BadEq: + def __eq__(self, other): + raise Exc + def __hash__(self): + return 7 + + k1, k2 = BadEq(), BadEq() + d = {k1: 1} + self.assertEqual(dict_getitem_knownhash(d, k1, hash(k1)), 1) + self.assertRaises(Exc, dict_getitem_knownhash, d, k2, hash(k2)) + + from test import mapping_tests class GeneralMappingTests(mapping_tests.BasicTestMappingProtocol): -- cgit v1.2.1 From 0ea03fe368d969c4c65900b57b5f4852a2675083 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 6 Nov 2016 11:01:08 -0500 Subject: Add an additional test with a newline, one that's very similar to test_parens_in_expressions, but because the newline is not a literal newline, but a backslash en, this error is triggered. --- Lib/test/test_fstring.py | 1 + 1 file changed, 1 insertion(+) (limited to 'Lib') diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index 0af93e088f..68aa526639 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -378,6 +378,7 @@ f'{a * x()}'""" r"rf'{\t3}'", r"rf'{\}'", r"""rf'{"\N{LEFT CURLY BRACKET}"}'""", + r"f'{\n}'", ]) def test_no_escapes_for_braces(self): -- cgit v1.2.1 From f0d7a0a06f9605e6fe384e0aae1ddf33806586ec Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 6 Nov 2016 11:14:48 -0500 Subject: Additionally show that a backslash-escaped opening brace is treated as a literal and thus triggers the single closing brace error, clarifying #28590. --- Lib/test/test_fstring.py | 1 + 1 file changed, 1 insertion(+) (limited to 'Lib') diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index 68aa526639..45f768ce2b 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -628,6 +628,7 @@ f'{a * x()}'""" "f'}'", "f'x}'", "f'x}x'", + r"f'\u007b}'", # Can't have { or } in a format spec. "f'{3:}>10}'", -- cgit v1.2.1 From 6a840004d72e1f179446653a2b9a243dd7f47b45 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 6 Nov 2016 11:25:54 -0500 Subject: Update test_no_escapes_for_braces to clarify behavior with a docstring and expressions that clearly are not evaluated. --- Lib/test/test_fstring.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index 45f768ce2b..086bf673f9 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -382,11 +382,14 @@ f'{a * x()}'""" ]) def test_no_escapes_for_braces(self): - # \x7b is '{'. Make sure it doesn't start an expression. - self.assertEqual(f'\x7b2}}', '{2}') - self.assertEqual(f'\x7b2', '{2') - self.assertEqual(f'\u007b2', '{2') - self.assertEqual(f'\N{LEFT CURLY BRACKET}2\N{RIGHT CURLY BRACKET}', '{2}') + """ + Only literal curly braces begin an expression. + """ + # \x7b is '{'. + self.assertEqual(f'\x7b1+1}}', '{1+1}') + self.assertEqual(f'\x7b1+1', '{1+1') + self.assertEqual(f'\u007b1+1', '{1+1') + self.assertEqual(f'\N{LEFT CURLY BRACKET}1+1\N{RIGHT CURLY BRACKET}', '{1+1}') def test_newlines_in_expressions(self): self.assertEqual(f'{0}', '0') -- cgit v1.2.1 From 566dfe9e6cf0d679d7f62710444fa263cb7fb7d3 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Sun, 6 Nov 2016 18:25:39 -0800 Subject: issue #28622: Remove redundant variable annotation test from test_grammar. Ivan L. --- Lib/test/test_grammar.py | 3 --- 1 file changed, 3 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py index 03f2c84211..65e26bfd38 100644 --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -359,9 +359,6 @@ class GrammarTests(unittest.TestCase): self.assertEqual(ann_module.M.__annotations__, {'123': 123, 'o': type}) self.assertEqual(ann_module2.__annotations__, {}) - self.assertEqual(typing.get_type_hints(ann_module2.CV, - ann_module2.__dict__), - ChainMap({'var': typing.ClassVar[ann_module2.CV]}, {})) def test_var_annot_in_module(self): # check that functions fail the same way when executed -- cgit v1.2.1 From 05074c5eba7ab5e023f3bc92528e18a8fecab2c4 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Sun, 6 Nov 2016 19:35:08 -0800 Subject: Closes #27781: Removes special cases for the experimental aspect of PEP 529 --- Lib/test/test_os.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 638244f04a..628c61e591 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2860,13 +2860,8 @@ class OSErrorTests(unittest.TestCase): func(name, *func_args) except OSError as err: self.assertIs(err.filename, name, str(func)) - except RuntimeError as err: - if sys.platform != 'win32': - raise - - # issue27781: undecodable bytes currently raise RuntimeError - # by 3.6.0b4 this will become UnicodeDecodeError or nothing - self.assertIsInstance(err.__context__, UnicodeDecodeError) + except UnicodeDecodeError: + pass else: self.fail("No exception thrown by {}".format(func)) -- cgit v1.2.1 From 8538dad424df02b1ec208e917efe17b36a3cf988 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Mon, 7 Nov 2016 17:15:01 -0500 Subject: Issue #28572: Add 10% to coverage of IDLE's test_configdialog. Update and augment description of the configuration system. --- Lib/idlelib/config-main.def | 68 ++++++++------- Lib/idlelib/config.py | 27 +++--- Lib/idlelib/configdialog.py | 32 +++---- Lib/idlelib/idle_test/test_configdialog.py | 129 +++++++++++++++++++++++++---- 4 files changed, 181 insertions(+), 75 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/config-main.def b/Lib/idlelib/config-main.def index a61bba7ef3..330c0154a7 100644 --- a/Lib/idlelib/config-main.def +++ b/Lib/idlelib/config-main.def @@ -4,44 +4,50 @@ # When IDLE starts, it will look in # the following two sets of files, in order: # -# default configuration -# --------------------- -# config-main.def the default general config file -# config-extensions.def the default extension config file -# config-highlight.def the default highlighting config file -# config-keys.def the default keybinding config file +# default configuration files in idlelib +# -------------------------------------- +# config-main.def default general config file +# config-extensions.def default extension config file +# config-highlight.def default highlighting config file +# config-keys.def default keybinding config file # -# user configuration -# ------------------- -# ~/.idlerc/config-main.cfg the user general config file -# ~/.idlerc/config-extensions.cfg the user extension config file -# ~/.idlerc/config-highlight.cfg the user highlighting config file -# ~/.idlerc/config-keys.cfg the user keybinding config file +# user configuration files in ~/.idlerc +# ------------------------------------- +# config-main.cfg user general config file +# config-extensions.cfg user extension config file +# config-highlight.cfg user highlighting config file +# config-keys.cfg user keybinding config file # -# On Windows2000 and Windows XP the .idlerc directory is at -# Documents and Settings\\.idlerc -# -# On Windows98 it is at c:\.idlerc +# On Windows, the default location of the home directory ('~' above) +# depends on the version. For Windows 10, it is C:\Users\. # # Any options the user saves through the config dialog will be saved to -# the relevant user config file. Reverting any general setting to the -# default causes that entry to be wiped from the user file and re-read -# from the default file. User highlighting themes or keybinding sets are -# retained unless specifically deleted within the config dialog. Choosing -# one of the default themes or keysets just applies the relevant settings -# from the default file. +# the relevant user config file. Reverting any general or extension +# setting to the default causes that entry to be wiped from the user +# file and re-read from the default file. This rule applies to each +# item, except that the three editor font items are saved as a group. +# +# User highlighting themes and keybinding sets must have (section) names +# distinct from the default names. All items are added and saved as a +# group. They are retained unless specifically deleted within the config +# dialog. Choosing one of the default themes or keysets just applies the +# relevant settings from the default file. +# +# Additional help sources are listed in the [HelpFiles] section below +# and should be viewable by a web browser (or the Windows Help viewer in +# the case of .chm files). These sources will be listed on the Help +# menu. The pattern, and two examples, are # -# Additional help sources are listed in the [HelpFiles] section and must be -# viewable by a web browser (or the Windows Help viewer in the case of .chm -# files). These sources will be listed on the Help menu. The pattern is # -# You can't use a semi-colon in a menu item or path. The path will be platform -# specific because of path separators, drive specs etc. +# 1 = IDLE;C:/Programs/Python36/Lib/idlelib/help.html +# 2 = Pillow;https://pillow.readthedocs.io/en/latest/ +# +# You can't use a semi-colon in a menu item or path. The path will be +# platform specific because of path separators, drive specs etc. # -# It is best to use the Configuration GUI to set up additional help sources! -# Example: -#1 = My Extra Help Source;/usr/share/doc/foo/index.html -#2 = Another Help Source;/path/to/another.pdf +# The default files should not be edited except to add new sections to +# config-extensions.def for added extensions . The user files should be +# modified through the Settings dialog. [General] editor-on-startup= 0 diff --git a/Lib/idlelib/config.py b/Lib/idlelib/config.py index 10fe3baceb..358bee4803 100644 --- a/Lib/idlelib/config.py +++ b/Lib/idlelib/config.py @@ -1,13 +1,20 @@ -"""Provides access to stored IDLE configuration information. - -Refer to the comments at the beginning of config-main.def for a description of -the available configuration files and the design implemented to update user -configuration information. In particular, user configuration choices which -duplicate the defaults will be removed from the user's configuration files, -and if a file becomes empty, it will be deleted. - -The contents of the user files may be altered using the Options/Configure IDLE -menu to access the configuration GUI (configdialog.py), or manually. +"""idlelib.config -- Manage IDLE configuration information. + +The comments at the beginning of config-main.def describe the +configuration files and the design implemented to update user +configuration information. In particular, user configuration choices +which duplicate the defaults will be removed from the user's +configuration files, and if a user file becomes empty, it will be +deleted. + +The configuration database maps options to values. Comceptually, the +database keys are tuples (config-type, section, item). As implemented, +there are separate dicts for default and user values. Each has +config-type keys 'main', 'extensions', 'highlight', and 'keys'. The +value for each key is a ConfigParser instance that maps section and item +to values. For 'main' and 'extenstons', user values override +default values. For 'highlight' and 'keys', user sections augment the +default sections (and must, therefore, have distinct names). Throughout this module there is an emphasis on returning useable defaults when a problem occurs in returning a requested configuration value back to diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index fa47aaad14..8184582a3e 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -392,28 +392,28 @@ class ConfigDialog(Toplevel): text=' Additional Help Sources ') #frameRun labelRunChoiceTitle = Label(frameRun, text='At Startup') - radioStartupEdit = Radiobutton( + self.radioStartupEdit = Radiobutton( frameRun, variable=self.startupEdit, value=1, - command=self.SetKeysType, text="Open Edit Window") - radioStartupShell = Radiobutton( + text="Open Edit Window") + self.radioStartupShell = Radiobutton( frameRun, variable=self.startupEdit, value=0, - command=self.SetKeysType, text='Open Shell Window') + text='Open Shell Window') #frameSave labelRunSaveTitle = Label(frameSave, text='At Start of Run (F5) ') - radioSaveAsk = Radiobutton( + self.radioSaveAsk = Radiobutton( frameSave, variable=self.autoSave, value=0, - command=self.SetKeysType, text="Prompt to Save") - radioSaveAuto = Radiobutton( + text="Prompt to Save") + self.radioSaveAuto = Radiobutton( frameSave, variable=self.autoSave, value=1, - command=self.SetKeysType, text='No Prompt') + text='No Prompt') #frameWinSize labelWinSizeTitle = Label( frameWinSize, text='Initial Window Size (in characters)') labelWinWidthTitle = Label(frameWinSize, text='Width') - entryWinWidth = Entry( + self.entryWinWidth = Entry( frameWinSize, textvariable=self.winWidth, width=3) labelWinHeightTitle = Label(frameWinSize, text='Height') - entryWinHeight = Entry( + self.entryWinHeight = Entry( frameWinSize, textvariable=self.winHeight, width=3) #frameHelp frameHelpList = Frame(frameHelp) @@ -443,17 +443,17 @@ class ConfigDialog(Toplevel): frameHelp.pack(side=TOP, padx=5, pady=5, expand=TRUE, fill=BOTH) #frameRun labelRunChoiceTitle.pack(side=LEFT, anchor=W, padx=5, pady=5) - radioStartupShell.pack(side=RIGHT, anchor=W, padx=5, pady=5) - radioStartupEdit.pack(side=RIGHT, anchor=W, padx=5, pady=5) + self.radioStartupShell.pack(side=RIGHT, anchor=W, padx=5, pady=5) + self.radioStartupEdit.pack(side=RIGHT, anchor=W, padx=5, pady=5) #frameSave labelRunSaveTitle.pack(side=LEFT, anchor=W, padx=5, pady=5) - radioSaveAuto.pack(side=RIGHT, anchor=W, padx=5, pady=5) - radioSaveAsk.pack(side=RIGHT, anchor=W, padx=5, pady=5) + self.radioSaveAuto.pack(side=RIGHT, anchor=W, padx=5, pady=5) + self.radioSaveAsk.pack(side=RIGHT, anchor=W, padx=5, pady=5) #frameWinSize labelWinSizeTitle.pack(side=LEFT, anchor=W, padx=5, pady=5) - entryWinHeight.pack(side=RIGHT, anchor=E, padx=10, pady=5) + self.entryWinHeight.pack(side=RIGHT, anchor=E, padx=10, pady=5) labelWinHeightTitle.pack(side=RIGHT, anchor=E, pady=5) - entryWinWidth.pack(side=RIGHT, anchor=E, padx=10, pady=5) + self.entryWinWidth.pack(side=RIGHT, anchor=E, padx=10, pady=5) labelWinWidthTitle.pack(side=RIGHT, anchor=E, pady=5) #frameHelp frameHelpListButtons.pack(side=RIGHT, padx=5, pady=5, fill=Y) diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py index 70bd14e851..81c57e863e 100644 --- a/Lib/idlelib/idle_test/test_configdialog.py +++ b/Lib/idlelib/idle_test/test_configdialog.py @@ -1,30 +1,123 @@ -'''Test idlelib.configdialog. +"""Test idlelib.configdialog. -Coverage: 46% just by creating dialog. -The other half is code for working with user customizations. -''' -from idlelib.configdialog import ConfigDialog # always test import +Half the class creates dialog, half works with user customizations. +Coverage: 46% just by creating dialog, 56% with current tests. +""" +from idlelib.configdialog import ConfigDialog, idleConf # test import from test.support import requires requires('gui') from tkinter import Tk import unittest +import idlelib.config as config -class ConfigDialogTest(unittest.TestCase): +# Tests should not depend on fortuitous user configurations. +# They must not affect actual user .cfg files. +# Use solution from test_config: empty parsers with no filename. +usercfg = idleConf.userCfg +testcfg = { + 'main': config.IdleUserConfParser(''), + 'highlight': config.IdleUserConfParser(''), + 'keys': config.IdleUserConfParser(''), + 'extensions': config.IdleUserConfParser(''), +} - @classmethod - def setUpClass(cls): - cls.root = Tk() - cls.root.withdraw() +# ConfigDialog.changedItems is a 3-level hierarchical dictionary of +# pending changes that mirrors the multilevel user config dict. +# For testing, record args in a list for comparison with expected. +changes = [] +class TestDialog(ConfigDialog): + def AddChangedItem(self, *args): + changes.append(args) - @classmethod - def tearDownClass(cls): - cls.root.update_idletasks() - cls.root.destroy() - del cls.root +def setUpModule(): + global root, configure + idleConf.userCfg = testcfg + root = Tk() + root.withdraw() + configure = TestDialog(root, 'Test', _utest=True) - def test_configdialog(self): - d = ConfigDialog(self.root, 'Test', _utest=True) - d.remove_var_callbacks() + +def tearDownModule(): + global root, configure + idleConf.userCfg = testcfg + configure.remove_var_callbacks() + del configure + root.update_idletasks() + root.destroy() + del root + + +class FontTabTest(unittest.TestCase): + + + def setUp(self): + changes.clear() + + def test_font(self): + configure.fontName.set('Test Font') + expected = [ + ('main', 'EditorWindow', 'font', 'Test Font'), + ('main', 'EditorWindow', 'font-size', '10'), + ('main', 'EditorWindow', 'font-bold', False)] + self.assertEqual(changes, expected) + changes.clear() + configure.fontSize.set(12) + expected = [ + ('main', 'EditorWindow', 'font', 'Test Font'), + ('main', 'EditorWindow', 'font-size', '12'), + ('main', 'EditorWindow', 'font-bold', False)] + self.assertEqual(changes, expected) + changes.clear() + configure.fontBold.set(True) + expected = [ + ('main', 'EditorWindow', 'font', 'Test Font'), + ('main', 'EditorWindow', 'font-size', '12'), + ('main', 'EditorWindow', 'font-bold', True)] + self.assertEqual(changes, expected) + + #def test_sample(self): pass # TODO + + def test_tabspace(self): + configure.spaceNum.set(6) + self.assertEqual(changes, [('main', 'Indent', 'num-spaces', 6)]) + + +class HighlightTest(unittest.TestCase): + + def setUp(self): + changes.clear() + + #def test_colorchoose(self): pass # TODO + + +class KeysTest(unittest.TestCase): + + def setUp(self): + changes.clear() + + +class GeneralTest(unittest.TestCase): + + def setUp(self): + changes.clear() + + def test_startup(self): + configure.radioStartupEdit.invoke() + self.assertEqual(changes, + [('main', 'General', 'editor-on-startup', 1)]) + + def test_autosave(self): + configure.radioSaveAuto.invoke() + self.assertEqual(changes, [('main', 'General', 'autosave', 1)]) + + def test_editor_size(self): + configure.entryWinHeight.insert(0, '1') + self.assertEqual(changes, [('main', 'EditorWindow', 'height', '140')]) + changes.clear() + configure.entryWinWidth.insert(0, '1') + self.assertEqual(changes, [('main', 'EditorWindow', 'width', '180')]) + + #def test_help_sources(self): pass # TODO if __name__ == '__main__': -- cgit v1.2.1 From abe4b1fd6b97191893e76c74fc2e2c9b69ce9418 Mon Sep 17 00:00:00 2001 From: "Eric V. Smith" Date: Mon, 7 Nov 2016 17:54:01 -0500 Subject: Fixed issue #28633: segfault when concatenating bytes literal and f-string. --- Lib/test/test_fstring.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index 086bf673f9..82050835f6 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -92,6 +92,13 @@ f'{a * x()}'""" exec(c) self.assertEqual(x[0], 'foo3') + def test_compile_time_concat_errors(self): + self.assertAllRaise(SyntaxError, + 'cannot mix bytes and nonbytes literals', + [r"""f'' b''""", + r"""b'' f''""", + ]) + def test_literal(self): self.assertEqual(f'', '') self.assertEqual(f'a', 'a') -- cgit v1.2.1 From 0d5765ed985e50eb544728c8ba47ee2d4ad49d42 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 8 Nov 2016 00:42:46 +0100 Subject: Issue #28637: Revert issue #28082, don't import enum in re Importing the enum module in the re module slows down Python startup by 34% when Python is run from a virtual environment, or more generally when the re module is imported at startup but not the enum module. --- Lib/re.py | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) (limited to 'Lib') diff --git a/Lib/re.py b/Lib/re.py index d321cff92c..3fd600c5c5 100644 --- a/Lib/re.py +++ b/Lib/re.py @@ -119,7 +119,6 @@ This module also defines an exception 'error'. """ -import enum import sre_compile import sre_parse import functools @@ -139,26 +138,18 @@ __all__ = [ __version__ = "2.2.1" -class RegexFlag(enum.IntFlag): - ASCII = sre_compile.SRE_FLAG_ASCII # assume ascii "locale" - IGNORECASE = sre_compile.SRE_FLAG_IGNORECASE # ignore case - LOCALE = sre_compile.SRE_FLAG_LOCALE # assume current 8-bit locale - UNICODE = sre_compile.SRE_FLAG_UNICODE # assume unicode "locale" - MULTILINE = sre_compile.SRE_FLAG_MULTILINE # make anchors look for newline - DOTALL = sre_compile.SRE_FLAG_DOTALL # make dot match newline - VERBOSE = sre_compile.SRE_FLAG_VERBOSE # ignore whitespace and comments - A = ASCII - I = IGNORECASE - L = LOCALE - U = UNICODE - M = MULTILINE - S = DOTALL - X = VERBOSE - # sre extensions (experimental, don't rely on these) - TEMPLATE = sre_compile.SRE_FLAG_TEMPLATE # disable backtracking - T = TEMPLATE - DEBUG = sre_compile.SRE_FLAG_DEBUG # dump pattern after compilation -globals().update(RegexFlag.__members__) +# flags +A = ASCII = sre_compile.SRE_FLAG_ASCII # assume ascii "locale" +I = IGNORECASE = sre_compile.SRE_FLAG_IGNORECASE # ignore case +L = LOCALE = sre_compile.SRE_FLAG_LOCALE # assume current 8-bit locale +U = UNICODE = sre_compile.SRE_FLAG_UNICODE # assume unicode "locale" +M = MULTILINE = sre_compile.SRE_FLAG_MULTILINE # make anchors look for newline +S = DOTALL = sre_compile.SRE_FLAG_DOTALL # make dot match newline +X = VERBOSE = sre_compile.SRE_FLAG_VERBOSE # ignore whitespace and comments + +# sre extensions (experimental, don't rely on these) +T = TEMPLATE = sre_compile.SRE_FLAG_TEMPLATE # disable backtracking +DEBUG = sre_compile.SRE_FLAG_DEBUG # dump pattern after compilation # sre exception error = sre_compile.error -- cgit v1.2.1 From b393c30c0542243056f5dd531b0d2a72ba3c77aa Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Mon, 7 Nov 2016 23:14:53 -0500 Subject: Issue #28572: Use system-specific values for configdialog font test --- Lib/idlelib/config-main.def | 2 ++ Lib/idlelib/idle_test/test_configdialog.py | 20 ++++++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/config-main.def b/Lib/idlelib/config-main.def index 330c0154a7..16f4b0959c 100644 --- a/Lib/idlelib/config-main.def +++ b/Lib/idlelib/config-main.def @@ -60,6 +60,8 @@ delete-exitfunc= 1 width= 80 height= 40 font= TkFixedFont +# For TkFixedFont, the actual size and boldness are obtained from tk +# and override 10 and 0. See idlelib.config.IdleConf.GetFont font-size= 10 font-bold= 0 encoding= none diff --git a/Lib/idlelib/idle_test/test_configdialog.py b/Lib/idlelib/idle_test/test_configdialog.py index 81c57e863e..3f94493733 100644 --- a/Lib/idlelib/idle_test/test_configdialog.py +++ b/Lib/idlelib/idle_test/test_configdialog.py @@ -54,25 +54,29 @@ class FontTabTest(unittest.TestCase): changes.clear() def test_font(self): + # Set values guaranteed not to be defaults. + dfont = idleConf.GetFont(root, 'main', 'EditorWindow') + dsize = str(dfont[1]) + dbold = dfont[2] == 'bold' configure.fontName.set('Test Font') expected = [ ('main', 'EditorWindow', 'font', 'Test Font'), - ('main', 'EditorWindow', 'font-size', '10'), - ('main', 'EditorWindow', 'font-bold', False)] + ('main', 'EditorWindow', 'font-size', dsize), + ('main', 'EditorWindow', 'font-bold', dbold)] self.assertEqual(changes, expected) changes.clear() - configure.fontSize.set(12) + configure.fontSize.set(20) expected = [ ('main', 'EditorWindow', 'font', 'Test Font'), - ('main', 'EditorWindow', 'font-size', '12'), - ('main', 'EditorWindow', 'font-bold', False)] + ('main', 'EditorWindow', 'font-size', '20'), + ('main', 'EditorWindow', 'font-bold', dbold)] self.assertEqual(changes, expected) changes.clear() - configure.fontBold.set(True) + configure.fontBold.set(not dbold) expected = [ ('main', 'EditorWindow', 'font', 'Test Font'), - ('main', 'EditorWindow', 'font-size', '12'), - ('main', 'EditorWindow', 'font-bold', True)] + ('main', 'EditorWindow', 'font-size', '20'), + ('main', 'EditorWindow', 'font-bold', not dbold)] self.assertEqual(changes, expected) #def test_sample(self): pass # TODO -- cgit v1.2.1 From d6d80eaaec55a8ce4ca171124278d727469cd529 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Tue, 8 Nov 2016 12:23:09 -0500 Subject: docs/inspect: clarify iscoroutinefunction; add docs for isasyncgen* --- Lib/inspect.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/inspect.py b/Lib/inspect.py index 2923d6dacc..f01dd1de5d 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -179,17 +179,22 @@ def isgeneratorfunction(object): def iscoroutinefunction(object): """Return true if the object is a coroutine function. - Coroutine functions are defined with "async def" syntax, - or generators decorated with "types.coroutine". + Coroutine functions are defined with "async def" syntax. """ return bool((isfunction(object) or ismethod(object)) and object.__code__.co_flags & CO_COROUTINE) def isasyncgenfunction(object): + """Return true if the object is an asynchronous generator function. + + Asynchronous generator functions are defined with "async def" + syntax and have "yield" expressions in their body. + """ return bool((isfunction(object) or ismethod(object)) and object.__code__.co_flags & CO_ASYNC_GENERATOR) def isasyncgen(object): + """Return true if the object is an asynchronous generator.""" return isinstance(object, types.AsyncGeneratorType) def isgenerator(object): -- cgit v1.2.1 From 107c9bbf88fa8b3ba034c676f9e8d2a77ae87e2f Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 8 Nov 2016 20:17:35 +0200 Subject: Issue #28637: No longer use re in site.py. This makes Python startup from a virtual environment a little faster. --- Lib/site.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'Lib') diff --git a/Lib/site.py b/Lib/site.py index 0859f28ce1..0fc92009e1 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -422,8 +422,6 @@ def enablerlcompleter(): sys.__interactivehook__ = register_readline -CONFIG_LINE = r'^(?P(\w|[-_])+)\s*=\s*(?P.*)\s*$' - def venv(known_paths): global PREFIXES, ENABLE_USER_SITE @@ -445,19 +443,16 @@ def venv(known_paths): ] if candidate_confs: - import re - config_line = re.compile(CONFIG_LINE) virtual_conf = candidate_confs[0] system_site = "true" # Issue 25185: Use UTF-8, as that's what the venv module uses when # writing the file. with open(virtual_conf, encoding='utf-8') as f: for line in f: - line = line.strip() - m = config_line.match(line) - if m: - d = m.groupdict() - key, value = d['key'].lower(), d['value'] + if '=' in line: + key, _, value = line.partition('=') + key = key.strip().lower() + value = value.strip() if key == 'include-system-site-packages': system_site = value.lower() elif key == 'home': -- cgit v1.2.1 From 1407e3dac2e80b37f8c292cb06d92f39b912d615 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Tue, 8 Nov 2016 15:13:07 -0500 Subject: Issue #27243: Change PendingDeprecationWarning -> DeprecationWarning. As it was agreed in the issue, __aiter__ returning an awaitable should result in PendingDeprecationWarning in 3.5 and in DeprecationWarning in 3.6. --- Lib/test/test_coroutines.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py index 50e439af45..78439a2aca 100644 --- a/Lib/test/test_coroutines.py +++ b/Lib/test/test_coroutines.py @@ -1398,7 +1398,7 @@ class CoroutineTest(unittest.TestCase): buffer = [] async def test1(): - with self.assertWarnsRegex(PendingDeprecationWarning, "legacy"): + with self.assertWarnsRegex(DeprecationWarning, "legacy"): async for i1, i2 in AsyncIter(): buffer.append(i1 + i2) @@ -1412,7 +1412,7 @@ class CoroutineTest(unittest.TestCase): buffer = [] async def test2(): nonlocal buffer - with self.assertWarnsRegex(PendingDeprecationWarning, "legacy"): + with self.assertWarnsRegex(DeprecationWarning, "legacy"): async for i in AsyncIter(): buffer.append(i[0]) if i[0] == 20: @@ -1431,7 +1431,7 @@ class CoroutineTest(unittest.TestCase): buffer = [] async def test3(): nonlocal buffer - with self.assertWarnsRegex(PendingDeprecationWarning, "legacy"): + with self.assertWarnsRegex(DeprecationWarning, "legacy"): async for i in AsyncIter(): if i[0] > 20: continue @@ -1514,7 +1514,7 @@ class CoroutineTest(unittest.TestCase): return 123 async def foo(): - with self.assertWarnsRegex(PendingDeprecationWarning, "legacy"): + with self.assertWarnsRegex(DeprecationWarning, "legacy"): async for i in I(): print('never going to happen') @@ -1623,7 +1623,7 @@ class CoroutineTest(unittest.TestCase): 1/0 async def foo(): nonlocal CNT - with self.assertWarnsRegex(PendingDeprecationWarning, "legacy"): + with self.assertWarnsRegex(DeprecationWarning, "legacy"): async for i in AI(): CNT += 1 CNT += 10 @@ -1650,7 +1650,7 @@ class CoroutineTest(unittest.TestCase): self.assertEqual(CNT, 0) def test_for_9(self): - # Test that PendingDeprecationWarning can safely be converted into + # Test that DeprecationWarning can safely be converted into # an exception (__aiter__ should not have a chance to raise # a ZeroDivisionError.) class AI: @@ -1660,13 +1660,13 @@ class CoroutineTest(unittest.TestCase): async for i in AI(): pass - with self.assertRaises(PendingDeprecationWarning): + with self.assertRaises(DeprecationWarning): with warnings.catch_warnings(): warnings.simplefilter("error") run_async(foo()) def test_for_10(self): - # Test that PendingDeprecationWarning can safely be converted into + # Test that DeprecationWarning can safely be converted into # an exception. class AI: async def __aiter__(self): @@ -1675,7 +1675,7 @@ class CoroutineTest(unittest.TestCase): async for i in AI(): pass - with self.assertRaises(PendingDeprecationWarning): + with self.assertRaises(DeprecationWarning): with warnings.catch_warnings(): warnings.simplefilter("error") run_async(foo()) -- cgit v1.2.1 From 916834c241fbeb151d804c3a0157f79a95ae965f Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Wed, 9 Nov 2016 12:58:17 -0800 Subject: Issue #19717: Makes Path.resolve() succeed on paths that do not exist (patch by Vajrasky Kok) --- Lib/pathlib.py | 29 ++++++++++++++++++++------ Lib/test/test_pathlib.py | 54 +++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 70 insertions(+), 13 deletions(-) (limited to 'Lib') diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 1b5ab387a6..69653938ef 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -178,12 +178,26 @@ class _WindowsFlavour(_Flavour): def casefold_parts(self, parts): return [p.lower() for p in parts] - def resolve(self, path): + def resolve(self, path, strict=False): s = str(path) if not s: return os.getcwd() + previous_s = None if _getfinalpathname is not None: - return self._ext_to_normal(_getfinalpathname(s)) + if strict: + return self._ext_to_normal(_getfinalpathname(s)) + else: + while True: + try: + s = self._ext_to_normal(_getfinalpathname(s)) + except FileNotFoundError: + previous_s = s + s = os.path.abspath(os.path.join(s, os.pardir)) + else: + if previous_s is None: + return s + else: + return s + os.path.sep + os.path.basename(previous_s) # Means fallback on absolute return None @@ -285,7 +299,7 @@ class _PosixFlavour(_Flavour): def casefold_parts(self, parts): return parts - def resolve(self, path): + def resolve(self, path, strict=False): sep = self.sep accessor = path._accessor seen = {} @@ -315,7 +329,10 @@ class _PosixFlavour(_Flavour): target = accessor.readlink(newpath) except OSError as e: if e.errno != EINVAL: - raise + if strict: + raise + else: + return newpath # Not a symlink path = newpath else: @@ -1092,7 +1109,7 @@ class Path(PurePath): obj._init(template=self) return obj - def resolve(self): + def resolve(self, strict=False): """ Make the path absolute, resolving all symlinks on the way and also normalizing it (for example turning slashes into backslashes under @@ -1100,7 +1117,7 @@ class Path(PurePath): """ if self._closed: self._raise_closed() - s = self._flavour.resolve(self) + s = self._flavour.resolve(self, strict=strict) if s is None: # No symlink resolution => for consistency, raise an error if # the path doesn't exist or is forbidden diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index 2f2ba3cbfc..f98c1febb5 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -1486,8 +1486,8 @@ class _BasePathTest(object): self.assertEqual(set(p.glob("../xyzzy")), set()) - def _check_resolve(self, p, expected): - q = p.resolve() + def _check_resolve(self, p, expected, strict=True): + q = p.resolve(strict) self.assertEqual(q, expected) # this can be used to check both relative and absolute resolutions @@ -1498,8 +1498,17 @@ class _BasePathTest(object): P = self.cls p = P(BASE, 'foo') with self.assertRaises(OSError) as cm: - p.resolve() + p.resolve(strict=True) self.assertEqual(cm.exception.errno, errno.ENOENT) + # Non-strict + self.assertEqual(str(p.resolve(strict=False)), + os.path.join(BASE, 'foo')) + p = P(BASE, 'foo', 'in', 'spam') + self.assertEqual(str(p.resolve(strict=False)), + os.path.join(BASE, 'foo')) + p = P(BASE, '..', 'foo', 'in', 'spam') + self.assertEqual(str(p.resolve(strict=False)), + os.path.abspath(os.path.join('foo'))) # These are all relative symlinks p = P(BASE, 'dirB', 'fileB') self._check_resolve_relative(p, p) @@ -1509,6 +1518,18 @@ class _BasePathTest(object): self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB')) p = P(BASE, 'dirB', 'linkD', 'fileB') self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB')) + # Non-strict + p = P(BASE, 'dirA', 'linkC', 'fileB', 'foo', 'in', 'spam') + self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB', 'foo'), False) + p = P(BASE, 'dirA', 'linkC', '..', 'foo', 'in', 'spam') + if os.name == 'nt': + # In Windows, if linkY points to dirB, 'dirA\linkY\..' + # resolves to 'dirA' without resolving linkY first. + self._check_resolve_relative(p, P(BASE, 'dirA', 'foo'), False) + else: + # In Posix, if linkY points to dirB, 'dirA/linkY/..' + # resolves to 'dirB/..' first before resolving to parent of dirB. + self._check_resolve_relative(p, P(BASE, 'foo'), False) # Now create absolute symlinks d = tempfile.mkdtemp(suffix='-dirD') self.addCleanup(support.rmtree, d) @@ -1516,6 +1537,18 @@ class _BasePathTest(object): os.symlink(join('dirB'), os.path.join(d, 'linkY')) p = P(BASE, 'dirA', 'linkX', 'linkY', 'fileB') self._check_resolve_absolute(p, P(BASE, 'dirB', 'fileB')) + # Non-strict + p = P(BASE, 'dirA', 'linkX', 'linkY', 'foo', 'in', 'spam') + self._check_resolve_relative(p, P(BASE, 'dirB', 'foo'), False) + p = P(BASE, 'dirA', 'linkX', 'linkY', '..', 'foo', 'in', 'spam') + if os.name == 'nt': + # In Windows, if linkY points to dirB, 'dirA\linkY\..' + # resolves to 'dirA' without resolving linkY first. + self._check_resolve_relative(p, P(d, 'foo'), False) + else: + # In Posix, if linkY points to dirB, 'dirA/linkY/..' + # resolves to 'dirB/..' first before resolving to parent of dirB. + self._check_resolve_relative(p, P(BASE, 'foo'), False) @with_symlinks def test_resolve_dot(self): @@ -1525,7 +1558,11 @@ class _BasePathTest(object): self.dirlink(os.path.join('0', '0'), join('1')) self.dirlink(os.path.join('1', '1'), join('2')) q = p / '2' - self.assertEqual(q.resolve(), p) + self.assertEqual(q.resolve(strict=True), p) + r = q / '3' / '4' + self.assertRaises(FileNotFoundError, r.resolve, strict=True) + # Non-strict + self.assertEqual(r.resolve(strict=False), p / '3') def test_with(self): p = self.cls(BASE) @@ -1972,10 +2009,10 @@ class PathTest(_BasePathTest, unittest.TestCase): class PosixPathTest(_BasePathTest, unittest.TestCase): cls = pathlib.PosixPath - def _check_symlink_loop(self, *args): + def _check_symlink_loop(self, *args, strict=True): path = self.cls(*args) with self.assertRaises(RuntimeError): - print(path.resolve()) + print(path.resolve(strict)) def test_open_mode(self): old_mask = os.umask(0) @@ -2008,7 +2045,6 @@ class PosixPathTest(_BasePathTest, unittest.TestCase): @with_symlinks def test_resolve_loop(self): - # Loop detection for broken symlinks under POSIX # Loops with relative symlinks os.symlink('linkX/inside', join('linkX')) self._check_symlink_loop(BASE, 'linkX') @@ -2016,6 +2052,8 @@ class PosixPathTest(_BasePathTest, unittest.TestCase): self._check_symlink_loop(BASE, 'linkY') os.symlink('linkZ/../linkZ', join('linkZ')) self._check_symlink_loop(BASE, 'linkZ') + # Non-strict + self._check_symlink_loop(BASE, 'linkZ', 'foo', strict=False) # Loops with absolute symlinks os.symlink(join('linkU/inside'), join('linkU')) self._check_symlink_loop(BASE, 'linkU') @@ -2023,6 +2061,8 @@ class PosixPathTest(_BasePathTest, unittest.TestCase): self._check_symlink_loop(BASE, 'linkV') os.symlink(join('linkW/../linkW'), join('linkW')) self._check_symlink_loop(BASE, 'linkW') + # Non-strict + self._check_symlink_loop(BASE, 'linkW', 'foo', strict=False) def test_glob(self): P = self.cls -- cgit v1.2.1 From 365055a55ece7b17e87abcec09a7ee7b2257b38f Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sun, 13 Nov 2016 00:42:56 -0500 Subject: Fix typos --- Lib/random.py | 2 +- Lib/test/test_random.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/random.py b/Lib/random.py index d557acc92b..ca90e1459d 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -352,7 +352,7 @@ class Random(_random.Random): return [population[_int(random() * total)] for i in range(k)] cum_weights = list(_itertools.accumulate(weights)) elif weights is not None: - raise TypeError('Cannot specify both weights and cumulative_weights') + raise TypeError('Cannot specify both weights and cumulative weights') if len(cum_weights) != len(population): raise ValueError('The number of weights does not match the population') bisect = _bisect.bisect diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py index a4413b5146..fd0d2e319e 100644 --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -630,7 +630,7 @@ class MersenneTwister_TestBasicOps(TestBasicOps, unittest.TestCase): self.assertEqual((x+stop)%step, 0) def test_choices_algorithms(self): - # The various ways of specifing weights should produce the same results + # The various ways of specifying weights should produce the same results choices = self.gen.choices n = 13132817 -- cgit v1.2.1 From 4c8b8c6298651e1036595b54a80bc2a710073876 Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Sun, 13 Nov 2016 20:46:46 +0100 Subject: Fix test_faulthandler on Android where raise() exits with 0 --- Lib/test/support/__init__.py | 12 ++++++++++-- Lib/test/test_faulthandler.py | 14 +++++++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) (limited to 'Lib') diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 8b66f95ee4..961b735591 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -90,7 +90,7 @@ __all__ = [ "bigmemtest", "bigaddrspacetest", "cpython_only", "get_attribute", "requires_IEEE_754", "skip_unless_xattr", "requires_zlib", "anticipate_failure", "load_package_tests", "detect_api_mismatch", - "check__all__", + "check__all__", "requires_android_level", # sys "is_jython", "is_android", "check_impl_detail", "unix_shell", # network @@ -735,7 +735,8 @@ requires_lzma = unittest.skipUnless(lzma, 'requires lzma') is_jython = sys.platform.startswith('java') -is_android = bool(sysconfig.get_config_var('ANDROID_API_LEVEL')) +_ANDROID_API_LEVEL = sysconfig.get_config_var('ANDROID_API_LEVEL') +is_android = (_ANDROID_API_LEVEL > 0) if sys.platform != 'win32': unix_shell = '/system/bin/sh' if is_android else '/bin/sh' @@ -1725,6 +1726,13 @@ def requires_resource(resource): else: return unittest.skip("resource {0!r} is not enabled".format(resource)) +def requires_android_level(level, reason): + if is_android and _ANDROID_API_LEVEL < level: + return unittest.skip('%s at Android API level %d' % + (reason, _ANDROID_API_LEVEL)) + else: + return _id + def cpython_only(test): """ Decorator for tests only applicable on CPython. diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py index 22ccbc9062..bdd8d1a2a6 100644 --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py @@ -7,7 +7,7 @@ import signal import subprocess import sys from test import support -from test.support import script_helper +from test.support import script_helper, is_android, requires_android_level import tempfile import unittest from textwrap import dedent @@ -42,6 +42,10 @@ def temporary_filename(): finally: support.unlink(filename) +def requires_raise(test): + return (test if not is_android else + requires_android_level(24, 'raise() is buggy')(test)) + class FaultHandlerTests(unittest.TestCase): def get_output(self, code, filename=None, fd=None): """ @@ -141,6 +145,7 @@ class FaultHandlerTests(unittest.TestCase): 3, 'access violation') + @requires_raise def test_sigsegv(self): self.check_fatal_error(""" import faulthandler @@ -183,6 +188,7 @@ class FaultHandlerTests(unittest.TestCase): @unittest.skipIf(_testcapi is None, 'need _testcapi') @unittest.skipUnless(hasattr(signal, 'SIGBUS'), 'need signal.SIGBUS') + @requires_raise def test_sigbus(self): self.check_fatal_error(""" import _testcapi @@ -197,6 +203,7 @@ class FaultHandlerTests(unittest.TestCase): @unittest.skipIf(_testcapi is None, 'need _testcapi') @unittest.skipUnless(hasattr(signal, 'SIGILL'), 'need signal.SIGILL') + @requires_raise def test_sigill(self): self.check_fatal_error(""" import _testcapi @@ -240,6 +247,7 @@ class FaultHandlerTests(unittest.TestCase): '(?:Segmentation fault|Bus error)', other_regex='unable to raise a stack overflow') + @requires_raise def test_gil_released(self): self.check_fatal_error(""" import faulthandler @@ -249,6 +257,7 @@ class FaultHandlerTests(unittest.TestCase): 3, 'Segmentation fault') + @requires_raise def test_enable_file(self): with temporary_filename() as filename: self.check_fatal_error(""" @@ -263,6 +272,7 @@ class FaultHandlerTests(unittest.TestCase): @unittest.skipIf(sys.platform == "win32", "subprocess doesn't support pass_fds on Windows") + @requires_raise def test_enable_fd(self): with tempfile.TemporaryFile('wb+') as fp: fd = fp.fileno() @@ -276,6 +286,7 @@ class FaultHandlerTests(unittest.TestCase): 'Segmentation fault', fd=fd) + @requires_raise def test_enable_single_thread(self): self.check_fatal_error(""" import faulthandler @@ -286,6 +297,7 @@ class FaultHandlerTests(unittest.TestCase): 'Segmentation fault', all_threads=False) + @requires_raise def test_disable(self): code = """ import faulthandler -- cgit v1.2.1 From 616fe478b7c19709d3262213bc594df4c0f0dd81 Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Sun, 13 Nov 2016 21:55:52 +0100 Subject: Issue #26934: Handle _ANDROID_API_LEVEL is None on Windows --- Lib/test/support/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 961b735591..d862d8b457 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -736,7 +736,7 @@ requires_lzma = unittest.skipUnless(lzma, 'requires lzma') is_jython = sys.platform.startswith('java') _ANDROID_API_LEVEL = sysconfig.get_config_var('ANDROID_API_LEVEL') -is_android = (_ANDROID_API_LEVEL > 0) +is_android = (_ANDROID_API_LEVEL is not None and _ANDROID_API_LEVEL > 0) if sys.platform != 'win32': unix_shell = '/system/bin/sh' if is_android else '/bin/sh' -- cgit v1.2.1 From e55f77ab4d77946c2cb298fe1a80e4e86db9a165 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Mon, 14 Nov 2016 00:15:44 -0800 Subject: correctly emulate error semantics of gen.throw in FutureIter_throw --- Lib/test/test_asyncio/test_futures.py | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py index c1476089f4..89afdcaff2 100644 --- a/Lib/test/test_asyncio/test_futures.py +++ b/Lib/test/test_asyncio/test_futures.py @@ -466,6 +466,15 @@ class BaseFutureTests: self.fail('StopIteration was expected') self.assertEqual(result, (1, 2)) + def test_future_iter_throw(self): + fut = self._new_future(loop=self.loop) + fi = iter(fut) + self.assertRaises(TypeError, fi.throw, + Exception, Exception("elephant"), 32) + self.assertRaises(TypeError, fi.throw, + Exception("elephant"), Exception("elephant")) + self.assertRaises(TypeError, fi.throw, list) + @unittest.skipUnless(hasattr(futures, '_CFuture'), 'requires the C _asyncio module') -- cgit v1.2.1 From 83abd8121978ef559110fa97128f14c56e80c40c Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 14 Nov 2016 12:35:55 +0100 Subject: Issue #28637: Reapply changeset 223731925d06 "issue28082: use IntFlag for re constants" by Ethan Furman. The re module is not more used in the site module and so adding "import enum" to re.py doesn't impact python_startup benchmark anymore. --- Lib/re.py | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) (limited to 'Lib') diff --git a/Lib/re.py b/Lib/re.py index 3fd600c5c5..d321cff92c 100644 --- a/Lib/re.py +++ b/Lib/re.py @@ -119,6 +119,7 @@ This module also defines an exception 'error'. """ +import enum import sre_compile import sre_parse import functools @@ -138,18 +139,26 @@ __all__ = [ __version__ = "2.2.1" -# flags -A = ASCII = sre_compile.SRE_FLAG_ASCII # assume ascii "locale" -I = IGNORECASE = sre_compile.SRE_FLAG_IGNORECASE # ignore case -L = LOCALE = sre_compile.SRE_FLAG_LOCALE # assume current 8-bit locale -U = UNICODE = sre_compile.SRE_FLAG_UNICODE # assume unicode "locale" -M = MULTILINE = sre_compile.SRE_FLAG_MULTILINE # make anchors look for newline -S = DOTALL = sre_compile.SRE_FLAG_DOTALL # make dot match newline -X = VERBOSE = sre_compile.SRE_FLAG_VERBOSE # ignore whitespace and comments - -# sre extensions (experimental, don't rely on these) -T = TEMPLATE = sre_compile.SRE_FLAG_TEMPLATE # disable backtracking -DEBUG = sre_compile.SRE_FLAG_DEBUG # dump pattern after compilation +class RegexFlag(enum.IntFlag): + ASCII = sre_compile.SRE_FLAG_ASCII # assume ascii "locale" + IGNORECASE = sre_compile.SRE_FLAG_IGNORECASE # ignore case + LOCALE = sre_compile.SRE_FLAG_LOCALE # assume current 8-bit locale + UNICODE = sre_compile.SRE_FLAG_UNICODE # assume unicode "locale" + MULTILINE = sre_compile.SRE_FLAG_MULTILINE # make anchors look for newline + DOTALL = sre_compile.SRE_FLAG_DOTALL # make dot match newline + VERBOSE = sre_compile.SRE_FLAG_VERBOSE # ignore whitespace and comments + A = ASCII + I = IGNORECASE + L = LOCALE + U = UNICODE + M = MULTILINE + S = DOTALL + X = VERBOSE + # sre extensions (experimental, don't rely on these) + TEMPLATE = sre_compile.SRE_FLAG_TEMPLATE # disable backtracking + T = TEMPLATE + DEBUG = sre_compile.SRE_FLAG_DEBUG # dump pattern after compilation +globals().update(RegexFlag.__members__) # sre exception error = sre_compile.error -- cgit v1.2.1 From cd20b88a791a24efac2b77d06987e90d4d590260 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 14 Nov 2016 12:38:43 +0100 Subject: Issue #28082: Add basic unit tests on re enums --- Lib/test/test_re.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index 3bd6d7b461..aac3a2cbab 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -1771,6 +1771,12 @@ SUBPATTERN None 0 0 self.checkPatternError(r'(?<>)', 'unknown extension ?<>', 1) self.checkPatternError(r'(?', 'unexpected end of pattern', 2) + def test_enum(self): + # Issue #28082: Check that str(flag) returns a human readable string + # instead of an integer + self.assertIn('ASCII', str(re.A)) + self.assertIn('DOTALL', str(re.S)) + class PatternReprTests(unittest.TestCase): def check(self, pattern, expected): -- cgit v1.2.1 From a6e5719ef4bc2db1b6e9795b0a2b5e4926c7c2a6 Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Mon, 14 Nov 2016 17:14:42 +0100 Subject: Issue #28662: Catch PermissionError in tests when spawning a non existent program --- Lib/test/test_dtrace.py | 2 +- Lib/test/test_shutil.py | 3 ++- Lib/test/test_subprocess.py | 5 +++-- 3 files changed, 6 insertions(+), 4 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_dtrace.py b/Lib/test/test_dtrace.py index ca239b321b..47a501018a 100644 --- a/Lib/test/test_dtrace.py +++ b/Lib/test/test_dtrace.py @@ -79,7 +79,7 @@ class TraceBackend: try: output = self.trace(abspath("assert_usable" + self.EXTENSION)) output = output.strip() - except FileNotFoundError as fnfe: + except (FileNotFoundError, PermissionError) as fnfe: output = str(fnfe) if output != "probe: success": raise unittest.SkipTest( diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index d93efb8867..af5f00fdf0 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -1869,7 +1869,8 @@ class TermsizeTests(unittest.TestCase): """ try: size = subprocess.check_output(['stty', 'size']).decode().split() - except (FileNotFoundError, subprocess.CalledProcessError): + except (FileNotFoundError, PermissionError, + subprocess.CalledProcessError): self.skipTest("stty invocation failed") expected = (int(size[1]), int(size[0])) # reversed order diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 2d9239c759..73da1956ea 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -293,7 +293,8 @@ class ProcessTestCase(BaseTestCase): # Verify first that the call succeeds without the executable arg. pre_args = [sys.executable, "-c"] self._assert_python(pre_args) - self.assertRaises(FileNotFoundError, self._assert_python, pre_args, + self.assertRaises((FileNotFoundError, PermissionError), + self._assert_python, pre_args, executable="doesnotexist") @unittest.skipIf(mswindows, "executable argument replaces shell") @@ -2753,7 +2754,7 @@ class ContextManagerTests(BaseTestCase): self.assertEqual(proc.returncode, 1) def test_invalid_args(self): - with self.assertRaises(FileNotFoundError) as c: + with self.assertRaises((FileNotFoundError, PermissionError)) as c: with subprocess.Popen(['nonexisting_i_hope'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc: -- cgit v1.2.1 From da22e7f2daa3cdcae574441f05526e8231a8ad3b Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 15 Nov 2016 09:12:10 +0100 Subject: Fix warn_invalid_escape_sequence() Issue #28691: Fix warn_invalid_escape_sequence(): handle correctly DeprecationWarning raised as an exception. First clear the current exception to replace the DeprecationWarning exception with a SyntaxError exception. Unit test written by Serhiy Storchaka. --- Lib/test/test_string_literals.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_string_literals.py b/Lib/test/test_string_literals.py index 54f2be3598..aba4fc4667 100644 --- a/Lib/test/test_string_literals.py +++ b/Lib/test/test_string_literals.py @@ -111,6 +111,7 @@ class TestLiterals(unittest.TestCase): continue with self.assertWarns(DeprecationWarning): self.assertEqual(eval(r"'\%c'" % b), '\\' + chr(b)) + with warnings.catch_warnings(record=True) as w: warnings.simplefilter('always', category=DeprecationWarning) eval("'''\n\\z'''") @@ -118,6 +119,15 @@ class TestLiterals(unittest.TestCase): self.assertEqual(w[0].filename, '') self.assertEqual(w[0].lineno, 2) + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter('error', category=DeprecationWarning) + with self.assertRaises(SyntaxError) as cm: + eval("'''\n\\z'''") + exc = cm.exception + self.assertEqual(w, []) + self.assertEqual(exc.filename, '') + self.assertEqual(exc.lineno, 2) + def test_eval_str_raw(self): self.assertEqual(eval(""" r'x' """), 'x') self.assertEqual(eval(r""" r'\x01' """), '\\' + 'x01') @@ -150,6 +160,7 @@ class TestLiterals(unittest.TestCase): continue with self.assertWarns(DeprecationWarning): self.assertEqual(eval(r"b'\%c'" % b), b'\\' + bytes([b])) + with warnings.catch_warnings(record=True) as w: warnings.simplefilter('always', category=DeprecationWarning) eval("b'''\n\\z'''") @@ -157,6 +168,15 @@ class TestLiterals(unittest.TestCase): self.assertEqual(w[0].filename, '') self.assertEqual(w[0].lineno, 2) + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter('error', category=DeprecationWarning) + with self.assertRaises(SyntaxError) as cm: + eval("b'''\n\\z'''") + exc = cm.exception + self.assertEqual(w, []) + self.assertEqual(exc.filename, '') + self.assertEqual(exc.lineno, 2) + def test_eval_bytes_raw(self): self.assertEqual(eval(""" br'x' """), b'x') self.assertEqual(eval(""" rb'x' """), b'x') -- cgit v1.2.1 From 84e6bd64dd98985c415420d7615077ff7a7cd4ac Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Tue, 15 Nov 2016 17:24:42 +0100 Subject: Issue #26929: Skip some test_strptime tests failing on Android that incorrectly formats %V or %G for the last or the first incomplete week in a year --- Lib/test/test_strptime.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_strptime.py b/Lib/test/test_strptime.py index 22eac32917..2cf0926239 100644 --- a/Lib/test/test_strptime.py +++ b/Lib/test/test_strptime.py @@ -472,11 +472,24 @@ class CalculationTests(unittest.TestCase): "Calculation of day of the week failed;" "%s != %s" % (result.tm_wday, self.time_tuple.tm_wday)) + if support.is_android: + # Issue #26929: strftime() on Android incorrectly formats %V or %G for + # the last or the first incomplete week in a year. + _ymd_excluded = ((1905, 1, 1), (1906, 12, 31), (2008, 12, 29), + (1917, 12, 31)) + _formats_excluded = ('%G %V',) + else: + _ymd_excluded = () + _formats_excluded = () + def test_week_of_year_and_day_of_week_calculation(self): # Should be able to infer date if given year, week of year (%U or %W) # and day of the week def test_helper(ymd_tuple, test_reason): for year_week_format in ('%Y %W', '%Y %U', '%G %V'): + if (year_week_format in self._formats_excluded and + ymd_tuple in self._ymd_excluded): + return for weekday_format in ('%w', '%u', '%a', '%A'): format_string = year_week_format + ' ' + weekday_format with self.subTest(test_reason, -- cgit v1.2.1 From cf0a090abb7d7c2a3baf33a5183c0842dd316608 Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Wed, 16 Nov 2016 08:05:27 +0100 Subject: Issue #26935: Fix broken Android dup2() in test_os --- Lib/test/test_os.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 628c61e591..b3d0b1e1c3 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -56,7 +56,7 @@ except ImportError: try: import pwd all_users = [u.pw_uid for u in pwd.getpwall()] -except ImportError: +except (ImportError, AttributeError): all_users = [] try: from _testcapi import INT_MAX, PY_SSIZE_T_MAX @@ -1423,7 +1423,12 @@ class URandomFDTests(unittest.TestCase): break os.closerange(3, 256) with open({TESTFN!r}, 'rb') as f: - os.dup2(f.fileno(), fd) + new_fd = f.fileno() + # Issue #26935: posix allows new_fd and fd to be equal but + # some libc implementations have dup2 return an error in this + # case. + if new_fd != fd: + os.dup2(new_fd, fd) sys.stdout.buffer.write(os.urandom(4)) sys.stdout.buffer.write(os.urandom(4)) """.format(TESTFN=support.TESTFN) -- cgit v1.2.1 From cc830f1d735521ea2cc27d1240b543dfbdd29c28 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Wed, 16 Nov 2016 18:16:17 -0500 Subject: Issue #28721: Fix asynchronous generators aclose() and athrow() --- Lib/test/test_asyncgen.py | 140 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_asyncgen.py b/Lib/test/test_asyncgen.py index 68d202956f..34ab8a04ee 100644 --- a/Lib/test/test_asyncgen.py +++ b/Lib/test/test_asyncgen.py @@ -450,6 +450,41 @@ class AsyncGenAsyncioTest(unittest.TestCase): self.loop.run_until_complete(run()) + def test_async_gen_asyncio_anext_06(self): + DONE = 0 + + # test synchronous generators + def foo(): + try: + yield + except: + pass + g = foo() + g.send(None) + with self.assertRaises(StopIteration): + g.send(None) + + # now with asynchronous generators + + async def gen(): + nonlocal DONE + try: + yield + except: + pass + DONE = 1 + + async def run(): + nonlocal DONE + g = gen() + await g.asend(None) + with self.assertRaises(StopAsyncIteration): + await g.asend(None) + DONE += 10 + + self.loop.run_until_complete(run()) + self.assertEqual(DONE, 11) + def test_async_gen_asyncio_anext_tuple(self): async def foo(): try: @@ -594,6 +629,76 @@ class AsyncGenAsyncioTest(unittest.TestCase): self.loop.run_until_complete(run()) self.assertEqual(DONE, 1) + def test_async_gen_asyncio_aclose_10(self): + DONE = 0 + + # test synchronous generators + def foo(): + try: + yield + except: + pass + g = foo() + g.send(None) + g.close() + + # now with asynchronous generators + + async def gen(): + nonlocal DONE + try: + yield + except: + pass + DONE = 1 + + async def run(): + nonlocal DONE + g = gen() + await g.asend(None) + await g.aclose() + DONE += 10 + + self.loop.run_until_complete(run()) + self.assertEqual(DONE, 11) + + def test_async_gen_asyncio_aclose_11(self): + DONE = 0 + + # test synchronous generators + def foo(): + try: + yield + except: + pass + yield + g = foo() + g.send(None) + with self.assertRaisesRegex(RuntimeError, 'ignored GeneratorExit'): + g.close() + + # now with asynchronous generators + + async def gen(): + nonlocal DONE + try: + yield + except: + pass + yield + DONE += 1 + + async def run(): + nonlocal DONE + g = gen() + await g.asend(None) + with self.assertRaisesRegex(RuntimeError, 'ignored GeneratorExit'): + await g.aclose() + DONE += 10 + + self.loop.run_until_complete(run()) + self.assertEqual(DONE, 10) + def test_async_gen_asyncio_asend_01(self): DONE = 0 @@ -801,6 +906,41 @@ class AsyncGenAsyncioTest(unittest.TestCase): self.loop.run_until_complete(run()) self.assertEqual(DONE, 1) + def test_async_gen_asyncio_athrow_03(self): + DONE = 0 + + # test synchronous generators + def foo(): + try: + yield + except: + pass + g = foo() + g.send(None) + with self.assertRaises(StopIteration): + g.throw(ValueError) + + # now with asynchronous generators + + async def gen(): + nonlocal DONE + try: + yield + except: + pass + DONE = 1 + + async def run(): + nonlocal DONE + g = gen() + await g.asend(None) + with self.assertRaises(StopAsyncIteration): + await g.athrow(ValueError) + DONE += 10 + + self.loop.run_until_complete(run()) + self.assertEqual(DONE, 11) + def test_async_gen_asyncio_athrow_tuple(self): async def gen(): try: -- cgit v1.2.1 From 680b42e350b95f13cee17b9f7aba651e10422e1b Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Wed, 16 Nov 2016 18:25:04 -0500 Subject: Issue #28720: Add collections.abc.AsyncGenerator. --- Lib/_collections_abc.py | 59 ++++++++++++++++++++++++++++++- Lib/test/test_collections.py | 84 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 141 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/_collections_abc.py b/Lib/_collections_abc.py index ee7d0f18f8..b172f3f360 100644 --- a/Lib/_collections_abc.py +++ b/Lib/_collections_abc.py @@ -9,7 +9,8 @@ Unit tests are in test_collections. from abc import ABCMeta, abstractmethod import sys -__all__ = ["Awaitable", "Coroutine", "AsyncIterable", "AsyncIterator", +__all__ = ["Awaitable", "Coroutine", + "AsyncIterable", "AsyncIterator", "AsyncGenerator", "Hashable", "Iterable", "Iterator", "Generator", "Reversible", "Sized", "Container", "Callable", "Collection", "Set", "MutableSet", @@ -59,6 +60,11 @@ _coro = _coro() coroutine = type(_coro) _coro.close() # Prevent ResourceWarning del _coro +## asynchronous generator ## +async def _ag(): yield +_ag = _ag() +async_generator = type(_ag) +del _ag ### ONE-TRICK PONIES ### @@ -183,6 +189,57 @@ class AsyncIterator(AsyncIterable): return NotImplemented +class AsyncGenerator(AsyncIterator): + + __slots__ = () + + async def __anext__(self): + """Return the next item from the asynchronous generator. + When exhausted, raise StopAsyncIteration. + """ + return await self.asend(None) + + @abstractmethod + async def asend(self, value): + """Send a value into the asynchronous generator. + Return next yielded value or raise StopAsyncIteration. + """ + raise StopAsyncIteration + + @abstractmethod + async def athrow(self, typ, val=None, tb=None): + """Raise an exception in the asynchronous generator. + Return next yielded value or raise StopAsyncIteration. + """ + if val is None: + if tb is None: + raise typ + val = typ() + if tb is not None: + val = val.with_traceback(tb) + raise val + + async def aclose(self): + """Raise GeneratorExit inside coroutine. + """ + try: + await self.athrow(GeneratorExit) + except (GeneratorExit, StopAsyncIteration): + pass + else: + raise RuntimeError("asynchronous generator ignored GeneratorExit") + + @classmethod + def __subclasshook__(cls, C): + if cls is AsyncGenerator: + return _check_methods(C, '__aiter__', '__anext__', + 'asend', 'athrow', 'aclose') + return NotImplemented + + +AsyncGenerator.register(async_generator) + + class Iterable(metaclass=ABCMeta): __slots__ = () diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index 52ff256eb9..87454cc670 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -19,7 +19,8 @@ from collections import namedtuple, Counter, OrderedDict, _count_elements from collections import UserDict, UserString, UserList from collections import ChainMap from collections import deque -from collections.abc import Awaitable, Coroutine, AsyncIterator, AsyncIterable +from collections.abc import Awaitable, Coroutine +from collections.abc import AsyncIterator, AsyncIterable, AsyncGenerator from collections.abc import Hashable, Iterable, Iterator, Generator, Reversible from collections.abc import Sized, Container, Callable, Collection from collections.abc import Set, MutableSet @@ -959,6 +960,87 @@ class TestOneTrickPonyABCs(ABCTestCase): self.assertRaises(RuntimeError, IgnoreGeneratorExit().close) + def test_AsyncGenerator(self): + class NonAGen1: + def __aiter__(self): return self + def __anext__(self): return None + def aclose(self): pass + def athrow(self, typ, val=None, tb=None): pass + + class NonAGen2: + def __aiter__(self): return self + def __anext__(self): return None + def aclose(self): pass + def asend(self, value): return value + + class NonAGen3: + def aclose(self): pass + def asend(self, value): return value + def athrow(self, typ, val=None, tb=None): pass + + non_samples = [ + None, 42, 3.14, 1j, b"", "", (), [], {}, set(), + iter(()), iter([]), NonAGen1(), NonAGen2(), NonAGen3()] + for x in non_samples: + self.assertNotIsInstance(x, AsyncGenerator) + self.assertFalse(issubclass(type(x), AsyncGenerator), repr(type(x))) + + class Gen: + def __aiter__(self): return self + async def __anext__(self): return None + async def aclose(self): pass + async def asend(self, value): return value + async def athrow(self, typ, val=None, tb=None): pass + + class MinimalAGen(AsyncGenerator): + async def asend(self, value): + return value + async def athrow(self, typ, val=None, tb=None): + await super().athrow(typ, val, tb) + + async def gen(): + yield 1 + + samples = [gen(), Gen(), MinimalAGen()] + for x in samples: + self.assertIsInstance(x, AsyncIterator) + self.assertIsInstance(x, AsyncGenerator) + self.assertTrue(issubclass(type(x), AsyncGenerator), repr(type(x))) + self.validate_abstract_methods(AsyncGenerator, 'asend', 'athrow') + + def run_async(coro): + result = None + while True: + try: + coro.send(None) + except StopIteration as ex: + result = ex.args[0] if ex.args else None + break + return result + + # mixin tests + mgen = MinimalAGen() + self.assertIs(mgen, mgen.__aiter__()) + self.assertIs(run_async(mgen.asend(None)), run_async(mgen.__anext__())) + self.assertEqual(2, run_async(mgen.asend(2))) + self.assertIsNone(run_async(mgen.aclose())) + with self.assertRaises(ValueError): + run_async(mgen.athrow(ValueError)) + + class FailOnClose(AsyncGenerator): + async def asend(self, value): return value + async def athrow(self, *args): raise ValueError + + with self.assertRaises(ValueError): + run_async(FailOnClose().aclose()) + + class IgnoreGeneratorExit(AsyncGenerator): + async def asend(self, value): return value + async def athrow(self, *args): pass + + with self.assertRaises(RuntimeError): + run_async(IgnoreGeneratorExit().aclose()) + def test_Sized(self): non_samples = [None, 42, 3.14, 1j, _test_gen(), -- cgit v1.2.1 From 0d7cbc0265f573f4f3aaad4c93cf2c5175c8ee0a Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Thu, 17 Nov 2016 09:00:19 +0100 Subject: Issue 26931: Skip the test_distutils tests using a compiler executable that is not found --- Lib/distutils/tests/test_build_clib.py | 20 ++++++-------------- Lib/distutils/tests/test_build_ext.py | 6 ++++++ Lib/distutils/tests/test_config_cmd.py | 5 ++++- Lib/distutils/tests/test_install.py | 4 ++++ Lib/distutils/tests/test_sysconfig.py | 9 --------- Lib/test/support/__init__.py | 27 ++++++++++++++++++++++++++- 6 files changed, 46 insertions(+), 25 deletions(-) (limited to 'Lib') diff --git a/Lib/distutils/tests/test_build_clib.py b/Lib/distutils/tests/test_build_clib.py index acc99e78c1..85d09906f2 100644 --- a/Lib/distutils/tests/test_build_clib.py +++ b/Lib/distutils/tests/test_build_clib.py @@ -3,7 +3,7 @@ import unittest import os import sys -from test.support import run_unittest +from test.support import run_unittest, missing_compiler_executable from distutils.command.build_clib import build_clib from distutils.errors import DistutilsSetupError @@ -116,19 +116,11 @@ class BuildCLibTestCase(support.TempdirManager, cmd.build_temp = build_temp cmd.build_clib = build_temp - # before we run the command, we want to make sure - # all commands are present on the system - # by creating a compiler and checking its executables - from distutils.ccompiler import new_compiler - from distutils.sysconfig import customize_compiler - - compiler = new_compiler() - customize_compiler(compiler) - for ccmd in compiler.executables.values(): - if ccmd is None: - continue - if find_executable(ccmd[0]) is None: - self.skipTest('The %r command is not found' % ccmd[0]) + # Before we run the command, we want to make sure + # all commands are present on the system. + ccmd = missing_compiler_executable() + if ccmd is not None: + self.skipTest('The %r command is not found' % ccmd) # this should work cmd.run() diff --git a/Lib/distutils/tests/test_build_ext.py b/Lib/distutils/tests/test_build_ext.py index 6be0ca233b..be7f5f38aa 100644 --- a/Lib/distutils/tests/test_build_ext.py +++ b/Lib/distutils/tests/test_build_ext.py @@ -41,6 +41,9 @@ class BuildExtTestCase(TempdirManager, return build_ext(*args, **kwargs) def test_build_ext(self): + cmd = support.missing_compiler_executable() + if cmd is not None: + self.skipTest('The %r command is not found' % cmd) global ALREADY_TESTED copy_xxmodule_c(self.tmp_dir) xx_c = os.path.join(self.tmp_dir, 'xxmodule.c') @@ -295,6 +298,9 @@ class BuildExtTestCase(TempdirManager, self.assertEqual(cmd.compiler, 'unix') def test_get_outputs(self): + cmd = support.missing_compiler_executable() + if cmd is not None: + self.skipTest('The %r command is not found' % cmd) tmp_dir = self.mkdtemp() c_file = os.path.join(tmp_dir, 'foo.c') self.write_file(c_file, 'void PyInit_foo(void) {}\n') diff --git a/Lib/distutils/tests/test_config_cmd.py b/Lib/distutils/tests/test_config_cmd.py index 0c8dbd8269..6e566e7915 100644 --- a/Lib/distutils/tests/test_config_cmd.py +++ b/Lib/distutils/tests/test_config_cmd.py @@ -2,7 +2,7 @@ import unittest import os import sys -from test.support import run_unittest +from test.support import run_unittest, missing_compiler_executable from distutils.command.config import dump_file, config from distutils.tests import support @@ -39,6 +39,9 @@ class ConfigTestCase(support.LoggingSilencer, @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") def test_search_cpp(self): + cmd = missing_compiler_executable(['preprocessor']) + if cmd is not None: + self.skipTest('The %r command is not found' % cmd) pkg_dir, dist = self.create_dist() cmd = config(dist) diff --git a/Lib/distutils/tests/test_install.py b/Lib/distutils/tests/test_install.py index 9313330e2b..287ab1989e 100644 --- a/Lib/distutils/tests/test_install.py +++ b/Lib/distutils/tests/test_install.py @@ -17,6 +17,7 @@ from distutils.errors import DistutilsOptionError from distutils.extension import Extension from distutils.tests import support +from test import support as test_support def _make_ext_name(modname): @@ -196,6 +197,9 @@ class InstallTestCase(support.TempdirManager, self.assertEqual(found, expected) def test_record_extensions(self): + cmd = test_support.missing_compiler_executable() + if cmd is not None: + self.skipTest('The %r command is not found' % cmd) install_dir = self.mkdtemp() project_dir, dist = self.create_dist(ext_modules=[ Extension('xx', ['xxmodule.c'])]) diff --git a/Lib/distutils/tests/test_sysconfig.py b/Lib/distutils/tests/test_sysconfig.py index fc4d1de185..fe4a2994e3 100644 --- a/Lib/distutils/tests/test_sysconfig.py +++ b/Lib/distutils/tests/test_sysconfig.py @@ -39,15 +39,6 @@ class SysconfigTestCase(support.EnvironGuard, unittest.TestCase): self.assertNotEqual(sysconfig.get_python_lib(), sysconfig.get_python_lib(prefix=TESTFN)) - def test_get_python_inc(self): - inc_dir = sysconfig.get_python_inc() - # This is not much of a test. We make sure Python.h exists - # in the directory returned by get_python_inc() but we don't know - # it is the correct file. - self.assertTrue(os.path.isdir(inc_dir), inc_dir) - python_h = os.path.join(inc_dir, "Python.h") - self.assertTrue(os.path.isfile(python_h), python_h) - def test_get_config_vars(self): cvars = sysconfig.get_config_vars() self.assertIsInstance(cvars, dict) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 81136100f0..f10672383e 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -105,7 +105,7 @@ __all__ = [ "check_warnings", "check_no_resource_warning", "EnvironmentVarGuard", "run_with_locale", "swap_item", "swap_attr", "Matcher", "set_memlimit", "SuppressCrashReport", "sortdict", - "run_with_tz", "PGO", + "run_with_tz", "PGO", "missing_compiler_executable", ] class Error(Exception): @@ -2491,3 +2491,28 @@ def check_free_after_iterating(test, iter, cls, args=()): # The sequence should be deallocated just after the end of iterating gc_collect() test.assertTrue(done) + + +def missing_compiler_executable(cmd_names=[]): + """Check if the compiler components used to build the interpreter exist. + + Check for the existence of the compiler executables whose names are listed + in 'cmd_names' or all the compiler executables when 'cmd_names' is empty + and return the first missing executable or None when none is found + missing. + + """ + from distutils import ccompiler, sysconfig, spawn + compiler = ccompiler.new_compiler() + sysconfig.customize_compiler(compiler) + for name in compiler.executables: + if cmd_names and name not in cmd_names: + continue + cmd = getattr(compiler, name) + if cmd_names: + assert cmd is not None, \ + "the '%s' executable is not configured" % name + elif cmd is None: + continue + if spawn.find_executable(cmd[0]) is None: + return cmd[0] -- cgit v1.2.1 From e373b4ae7b2641f493f911858def34a066cbe543 Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Thu, 17 Nov 2016 09:20:28 +0100 Subject: Issue #26926: Skip some test_io tests on platforms without large file support --- Lib/test/test_io.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 8a2111cbd7..aaa64eadff 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -350,7 +350,10 @@ class IOTest(unittest.TestCase): def large_file_ops(self, f): assert f.readable() assert f.writable() - self.assertEqual(f.seek(self.LARGE), self.LARGE) + try: + self.assertEqual(f.seek(self.LARGE), self.LARGE) + except (OverflowError, ValueError): + self.skipTest("no largefile support") self.assertEqual(f.tell(), self.LARGE) self.assertEqual(f.write(b"xxx"), 3) self.assertEqual(f.tell(), self.LARGE + 3) -- cgit v1.2.1 From f67383d9e5dac2d1f1cb8d1d3cbbf28b8faca9fa Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Sat, 19 Nov 2016 18:53:19 -0800 Subject: Issue #28732: Raise ValueError when os.spawn*() is passed an empty tuple of arguments --- Lib/test/test_os.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index b3d0b1e1c3..9194a8a2be 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2321,6 +2321,27 @@ class SpawnTests(unittest.TestCase): exitcode = os.spawnve(os.P_WAIT, args[0], args, self.env) self.assertEqual(exitcode, self.exitcode) + @requires_os_func('spawnl') + def test_spawnl_noargs(self): + args = self.create_args() + self.assertRaises(ValueError, os.spawnl, os.P_NOWAIT, args[0]) + + @requires_os_func('spawnle') + def test_spawnl_noargs(self): + args = self.create_args() + self.assertRaises(ValueError, os.spawnle, os.P_NOWAIT, args[0], {}) + + @requires_os_func('spawnv') + def test_spawnv_noargs(self): + args = self.create_args() + self.assertRaises(ValueError, os.spawnv, os.P_NOWAIT, args[0], ()) + self.assertRaises(ValueError, os.spawnv, os.P_NOWAIT, args[0], []) + + @requires_os_func('spawnve') + def test_spawnv_noargs(self): + args = self.create_args() + self.assertRaises(ValueError, os.spawnve, os.P_NOWAIT, args[0], (), {}) + self.assertRaises(ValueError, os.spawnve, os.P_NOWAIT, args[0], [], {}) # The introduction of this TestCase caused at least two different errors on # *nix buildbots. Temporarily skip this to let the buildbots move along. -- cgit v1.2.1 From 95e2a7c1b572638dac3783d376d8c0655c2ca0bd Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Sat, 19 Nov 2016 20:11:56 -0800 Subject: Issue #28732: Adds new errors to spawnv emulation for platforms that only have fork and execv --- Lib/os.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'Lib') diff --git a/Lib/os.py b/Lib/os.py index 71ed0885b0..a704fb218c 100644 --- a/Lib/os.py +++ b/Lib/os.py @@ -832,6 +832,10 @@ if _exists("fork") and not _exists("spawnv") and _exists("execv"): def _spawnvef(mode, file, args, env, func): # Internal helper; func is the exec*() function to use + if not isinstance(args, (tuple, list)): + raise TypeError('argv must be a tuple or a list') + if not args[0]: + raise ValueError('argv first element cannot be empty') pid = fork() if not pid: # Child -- cgit v1.2.1 From d13af9f039cc5c06be3d34a83e66b149607372b6 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Sat, 19 Nov 2016 21:14:27 -0800 Subject: Fixes empty tuple case. --- Lib/os.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/os.py b/Lib/os.py index a704fb218c..fa06f3937b 100644 --- a/Lib/os.py +++ b/Lib/os.py @@ -834,7 +834,7 @@ if _exists("fork") and not _exists("spawnv") and _exists("execv"): # Internal helper; func is the exec*() function to use if not isinstance(args, (tuple, list)): raise TypeError('argv must be a tuple or a list') - if not args[0]: + if not args or not args[0]: raise ValueError('argv first element cannot be empty') pid = fork() if not pid: -- cgit v1.2.1 From 4d1a9e7cd0574ee116a8fd9cc09e7b65b052e6f0 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sun, 20 Nov 2016 16:25:14 -0800 Subject: Issue #20572: The subprocess.Popen.wait method's undocumented endtime parameter now raises a DeprecationWarning. It was deprecated in 3.4. It was never documented prior to that. --- Lib/subprocess.py | 11 +++++++++-- Lib/test/test_subprocess.py | 27 +++++++++++++-------------- 2 files changed, 22 insertions(+), 16 deletions(-) (limited to 'Lib') diff --git a/Lib/subprocess.py b/Lib/subprocess.py index e742a4e199..0b880f68d9 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -1031,6 +1031,10 @@ class Popen(object): """Wait for child process to terminate. Returns returncode attribute.""" if endtime is not None: + warnings.warn( + "'endtime' argument is deprecated; use 'timeout'.", + DeprecationWarning, + stacklevel=2) timeout = self._remaining_time(endtime) if timeout is None: timeout_millis = _winapi.INFINITE @@ -1392,8 +1396,11 @@ class Popen(object): if self.returncode is not None: return self.returncode - # endtime is preferred to timeout. timeout is only used for - # printing. + if endtime is not None: + warnings.warn( + "'endtime' argument is deprecated; use 'timeout'.", + DeprecationWarning, + stacklevel=2) if endtime is not None or timeout is not None: if endtime is None: endtime = _time() + timeout diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 73da1956ea..89de6d1b1a 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1015,6 +1015,19 @@ class ProcessTestCase(BaseTestCase): # time to start. self.assertEqual(p.wait(timeout=3), 0) + def test_wait_endtime(self): + """Confirm that the deprecated endtime parameter warns.""" + p = subprocess.Popen([sys.executable, "-c", "pass"]) + try: + with self.assertWarns(DeprecationWarning) as warn_cm: + p.wait(endtime=time.time()+0.01) + except subprocess.TimeoutExpired: + pass # We're not testing endtime timeout behavior. + finally: + p.kill() + self.assertIn('test_subprocess.py', warn_cm.filename) + self.assertIn('endtime', str(warn_cm.warning)) + def test_invalid_bufsize(self): # an invalid type of the bufsize argument should raise # TypeError. @@ -2777,19 +2790,5 @@ class ContextManagerTests(BaseTestCase): self.assertTrue(proc.stdin.closed) -def test_main(): - unit_tests = (ProcessTestCase, - POSIXProcessTestCase, - Win32ProcessTestCase, - MiscTests, - ProcessTestCaseNoPoll, - CommandsWithSpaces, - ContextManagerTests, - RunFuncTestCase, - ) - - support.run_unittest(*unit_tests) - support.reap_children() - if __name__ == "__main__": unittest.main() -- cgit v1.2.1 From 457668cddd4cb03d8ae6865ec49c3d7ffe9a8da8 Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Mon, 21 Nov 2016 20:57:14 +0900 Subject: Issue #28532: Show sys.version when -V option is supplied twice --- Lib/test/test_cmd_line.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py index 87571d3073..b71bb9f7ee 100644 --- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py @@ -43,7 +43,7 @@ class CmdLineTest(unittest.TestCase): def test_version(self): version = ('Python %d.%d' % sys.version_info[:2]).encode("ascii") - for switch in '-V', '--version': + for switch in '-V', '--version', '-VV': rc, out, err = assert_python_ok(switch) self.assertFalse(err.startswith(version)) self.assertTrue(out.startswith(version)) -- cgit v1.2.1 From 3481b2443d72c41a7356937952f69d647284a3c0 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 21 Nov 2016 16:35:08 +0100 Subject: Implement rich comparison for _sre.SRE_Pattern Issue #28727: Regular expression patterns, _sre.SRE_Pattern objects created by re.compile(), become comparable (only x==y and x!=y operators). This change should fix the issue #18383: don't duplicate warning filters when the warnings module is reloaded (thing usually only done in unit tests). --- Lib/test/test_re.py | 47 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index aac3a2cbab..4fcd2d463d 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -3,12 +3,13 @@ from test.support import verbose, run_unittest, gc_collect, bigmemtest, _2G, \ import io import locale import re -from re import Scanner import sre_compile -import sys import string +import sys import traceback import unittest +import warnings +from re import Scanner from weakref import proxy # Misc tests from Tim Peters' re.doc @@ -1777,6 +1778,48 @@ SUBPATTERN None 0 0 self.assertIn('ASCII', str(re.A)) self.assertIn('DOTALL', str(re.S)) + def test_pattern_compare(self): + pattern1 = re.compile('abc', re.IGNORECASE) + + # equal + re.purge() + pattern2 = re.compile('abc', re.IGNORECASE) + self.assertEqual(hash(pattern2), hash(pattern1)) + self.assertEqual(pattern2, pattern1) + + # not equal: different pattern + re.purge() + pattern3 = re.compile('XYZ', re.IGNORECASE) + # Don't test hash(pattern3) != hash(pattern1) because there is no + # warranty that hash values are different + self.assertNotEqual(pattern3, pattern1) + + # not equal: different flag (flags=0) + re.purge() + pattern4 = re.compile('abc') + self.assertNotEqual(pattern4, pattern1) + + # only == and != comparison operators are supported + with self.assertRaises(TypeError): + pattern1 < pattern2 + + def test_pattern_compare_bytes(self): + pattern1 = re.compile(b'abc') + + # equal: test bytes patterns + re.purge() + pattern2 = re.compile(b'abc') + self.assertEqual(hash(pattern2), hash(pattern1)) + self.assertEqual(pattern2, pattern1) + + # not equal: pattern of a different types (str vs bytes), + # comparison must not raise a BytesWarning + re.purge() + pattern3 = re.compile('abc') + with warnings.catch_warnings(): + warnings.simplefilter('error', BytesWarning) + self.assertNotEqual(pattern3, pattern1) + class PatternReprTests(unittest.TestCase): def check(self, pattern, expected): -- cgit v1.2.1 From a9574054e1ab0180db9b8d06a533728e5bd1d365 Mon Sep 17 00:00:00 2001 From: Ethan Furman Date: Mon, 21 Nov 2016 09:22:05 -0800 Subject: close issue28172: Change all example enum member names to uppercase, per Guido; patch by Chris Angelico. --- Lib/enum.py | 4 ++-- Lib/test/test_enum.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'Lib') diff --git a/Lib/enum.py b/Lib/enum.py index 4beb187a4a..3f5ecbb5ce 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -267,7 +267,7 @@ class EnumMeta(type): This method is used both when an enum class is given a value to match to an enumeration member (i.e. Color(3)) and for the functional API - (i.e. Color = Enum('Color', names='red green blue')). + (i.e. Color = Enum('Color', names='RED GREEN BLUE')). When used for the functional API: @@ -517,7 +517,7 @@ class Enum(metaclass=EnumMeta): # without calling this method; this method is called by the metaclass' # __call__ (i.e. Color(3) ), and by pickle if type(value) is cls: - # For lookups like Color(Color.red) + # For lookups like Color(Color.RED) return value # by-value search for a matching enum member # see if it's in the reverse mapping (for hashable values) diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index 2b3bfea916..e97ef947b1 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -69,9 +69,9 @@ except Exception as exc: # for doctests try: class Fruit(Enum): - tomato = 1 - banana = 2 - cherry = 3 + TOMATO = 1 + BANANA = 2 + CHERRY = 3 except Exception: pass -- cgit v1.2.1 From 75b90aa64b1879fb7681a2d3fb64340aa7eaf625 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 22 Nov 2016 00:29:42 +0200 Subject: Issue #28752: Restored the __reduce__() methods of datetime objects. --- Lib/datetime.py | 12 +++++++++--- Lib/test/datetimetester.py | 7 +++++++ 2 files changed, 16 insertions(+), 3 deletions(-) (limited to 'Lib') diff --git a/Lib/datetime.py b/Lib/datetime.py index 36374aa94c..7540109684 100644 --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -932,7 +932,7 @@ class date: # Pickle support. - def _getstate(self, protocol=3): + def _getstate(self): yhi, ylo = divmod(self._year, 256) return bytes([yhi, ylo, self._month, self._day]), @@ -940,8 +940,8 @@ class date: yhi, ylo, self._month, self._day = string self._year = yhi * 256 + ylo - def __reduce_ex__(self, protocol): - return (self.__class__, self._getstate(protocol)) + def __reduce__(self): + return (self.__class__, self._getstate()) _date_class = date # so functions w/ args named "date" can get at the class @@ -1348,6 +1348,9 @@ class time: def __reduce_ex__(self, protocol): return (time, self._getstate(protocol)) + def __reduce__(self): + return self.__reduce_ex__(2) + _time_class = time # so functions w/ args named "time" can get at the class time.min = time(0, 0, 0) @@ -1923,6 +1926,9 @@ class datetime(date): def __reduce_ex__(self, protocol): return (self.__class__, self._getstate(protocol)) + def __reduce__(self): + return self.__reduce_ex__(2) + datetime.min = datetime(1, 1, 1) datetime.max = datetime(9999, 12, 31, 23, 59, 59, 999999) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 988c6f722e..d65186db0c 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -1329,6 +1329,7 @@ class TestDate(HarmlessMixedComparison, unittest.TestCase): green = pickler.dumps(orig, proto) derived = unpickler.loads(green) self.assertEqual(orig, derived) + self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2)) def test_compare(self): t1 = self.theclass(2, 3, 4) @@ -1830,6 +1831,7 @@ class TestDateTime(TestDate): green = pickler.dumps(orig, proto) derived = unpickler.loads(green) self.assertEqual(orig, derived) + self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2)) def test_more_pickling(self): a = self.theclass(2003, 2, 7, 16, 48, 37, 444116) @@ -2469,6 +2471,7 @@ class TestTime(HarmlessMixedComparison, unittest.TestCase): green = pickler.dumps(orig, proto) derived = unpickler.loads(green) self.assertEqual(orig, derived) + self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2)) def test_pickling_subclass_time(self): args = 20, 59, 16, 64**2 @@ -2829,6 +2832,7 @@ class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase): green = pickler.dumps(orig, proto) derived = unpickler.loads(green) self.assertEqual(orig, derived) + self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2)) # Try one with a tzinfo. tinfo = PicklableFixedOffset(-300, 'cookie') @@ -2840,6 +2844,7 @@ class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase): self.assertIsInstance(derived.tzinfo, PicklableFixedOffset) self.assertEqual(derived.utcoffset(), timedelta(minutes=-300)) self.assertEqual(derived.tzname(), 'cookie') + self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2)) def test_more_bool(self): # time is always True. @@ -3043,6 +3048,7 @@ class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase): green = pickler.dumps(orig, proto) derived = unpickler.loads(green) self.assertEqual(orig, derived) + self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2)) # Try one with a tzinfo. tinfo = PicklableFixedOffset(-300, 'cookie') @@ -3055,6 +3061,7 @@ class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase): self.assertIsInstance(derived.tzinfo, PicklableFixedOffset) self.assertEqual(derived.utcoffset(), timedelta(minutes=-300)) self.assertEqual(derived.tzname(), 'cookie') + self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2)) def test_extreme_hashes(self): # If an attempt is made to hash these via subtracting the offset -- cgit v1.2.1 From c416f1c27a9f13542f8da1b9362696efeba997fd Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Mon, 21 Nov 2016 14:34:33 -0800 Subject: Issue 28475: Improve error message for random.sample() with k < 0. (Contributed by Francisco Couzo). --- Lib/random.py | 2 +- Lib/test/test_random.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/random.py b/Lib/random.py index ca90e1459d..49b0f149a5 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -314,7 +314,7 @@ class Random(_random.Random): randbelow = self._randbelow n = len(population) if not 0 <= k <= n: - raise ValueError("Sample larger than population") + raise ValueError("Sample larger than population or is negative") result = [None] * k setsize = 21 # size of a small set minus size of an empty list if k > 5: diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py index fd0d2e319e..84a1e831fd 100644 --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -110,6 +110,7 @@ class TestBasicOps: self.assertEqual(self.gen.sample([], 0), []) # test edge case N==k==0 # Exception raised if size of sample exceeds that of population self.assertRaises(ValueError, self.gen.sample, population, N+1) + self.assertRaises(ValueError, self.gen.sample, [], -1) def test_sample_distribution(self): # For the entire allowable range of 0 <= k <= N, validate that -- cgit v1.2.1 From aee9582b16c1eedb193b871cc6bb5ef01d3b3fa9 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Mon, 21 Nov 2016 15:32:08 -0800 Subject: Issue #28743: Reduce memory consumption for random module tests --- Lib/test/test_random.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py index 84a1e831fd..5b6a4f06ba 100644 --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -633,7 +633,7 @@ class MersenneTwister_TestBasicOps(TestBasicOps, unittest.TestCase): def test_choices_algorithms(self): # The various ways of specifying weights should produce the same results choices = self.gen.choices - n = 13132817 + n = 104729 self.gen.seed(8675309) a = self.gen.choices(range(n), k=10000) -- cgit v1.2.1 From 3202fbb9ff68c07c8dfa097410740c12ab0664f0 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Mon, 21 Nov 2016 16:48:10 -0800 Subject: Issue #5830: Add test for ee476248a74a. (Contributed by Serhiy Storchaka.) --- Lib/test/test_sched.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_sched.py b/Lib/test/test_sched.py index f86f599afc..ebf8856462 100644 --- a/Lib/test/test_sched.py +++ b/Lib/test/test_sched.py @@ -172,17 +172,23 @@ class TestCase(unittest.TestCase): self.assertEqual(scheduler.queue, [e1, e2, e3, e4, e5]) def test_args_kwargs(self): - flag = [] - + seq = [] def fun(*a, **b): - flag.append(None) - self.assertEqual(a, (1,2,3)) - self.assertEqual(b, {"foo":1}) + seq.append((a, b)) + now = time.time() scheduler = sched.scheduler(time.time, time.sleep) - z = scheduler.enterabs(0.01, 1, fun, argument=(1,2,3), kwargs={"foo":1}) + scheduler.enterabs(now, 1, fun) + scheduler.enterabs(now, 1, fun, argument=(1, 2)) + scheduler.enterabs(now, 1, fun, argument=('a', 'b')) + scheduler.enterabs(now, 1, fun, argument=(1, 2), kwargs={"foo": 3}) scheduler.run() - self.assertEqual(flag, [None]) + self.assertCountEqual(seq, [ + ((), {}), + ((1, 2), {}), + (('a', 'b'), {}), + ((1, 2), {'foo': 3}) + ]) def test_run_non_blocking(self): l = [] -- cgit v1.2.1 From 457f365ab250df6f014ef3339825d485e51b779a Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Mon, 21 Nov 2016 16:59:04 -0800 Subject: Issue #26163: Disable periodically failing test which was overly demanding of the frozenset hash function effectiveness --- Lib/test/test_set.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py index afa6e7f141..0202981d67 100644 --- a/Lib/test/test_set.py +++ b/Lib/test/test_set.py @@ -730,9 +730,6 @@ class TestFrozenSet(TestJointOps, unittest.TestCase): addhashvalue(hash(frozenset([e for e, m in elemmasks if m&i]))) self.assertEqual(len(hashvalues), 2**n) - def letter_range(n): - return string.ascii_letters[:n] - def zf_range(n): # https://en.wikipedia.org/wiki/Set-theoretic_definition_of_natural_numbers nums = [frozenset()] @@ -748,7 +745,7 @@ class TestFrozenSet(TestJointOps, unittest.TestCase): for n in range(18): t = 2 ** n mask = t - 1 - for nums in (range, letter_range, zf_range): + for nums in (range, zf_range): u = len({h & mask for h in map(hash, powerset(nums(n)))}) self.assertGreater(4*u, t) -- cgit v1.2.1 From a016e87125f3ab6157fea573e77a44a24c3565c8 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Mon, 21 Nov 2016 17:24:23 -0800 Subject: Issue #27100: With statement reports missing __enter__ before __exit__. (Contributed by Jonathan Ellington.) --- Lib/test/test_with.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_with.py b/Lib/test/test_with.py index e247ff6a61..cbb85da154 100644 --- a/Lib/test/test_with.py +++ b/Lib/test/test_with.py @@ -109,7 +109,7 @@ class FailureTestCase(unittest.TestCase): with foo: pass self.assertRaises(NameError, fooNotDeclared) - def testEnterAttributeError(self): + def testEnterAttributeError1(self): class LacksEnter(object): def __exit__(self, type, value, traceback): pass @@ -117,7 +117,16 @@ class FailureTestCase(unittest.TestCase): def fooLacksEnter(): foo = LacksEnter() with foo: pass - self.assertRaises(AttributeError, fooLacksEnter) + self.assertRaisesRegexp(AttributeError, '__enter__', fooLacksEnter) + + def testEnterAttributeError2(self): + class LacksEnterAndExit(object): + pass + + def fooLacksEnterAndExit(): + foo = LacksEnterAndExit() + with foo: pass + self.assertRaisesRegexp(AttributeError, '__enter__', fooLacksEnterAndExit) def testExitAttributeError(self): class LacksExit(object): @@ -127,7 +136,7 @@ class FailureTestCase(unittest.TestCase): def fooLacksExit(): foo = LacksExit() with foo: pass - self.assertRaises(AttributeError, fooLacksExit) + self.assertRaisesRegexp(AttributeError, '__exit__', fooLacksExit) def assertRaisesSyntaxError(self, codestr): def shouldRaiseSyntaxError(s): -- cgit v1.2.1 From 5ef177c11aa088d482dcdc9e770bce7d22cbf1f7 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Mon, 21 Nov 2016 23:24:38 -0500 Subject: Update pydoc topics for 3.6.0b4 --- Lib/pydoc_data/topics.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/pydoc_data/topics.py b/Lib/pydoc_data/topics.py index 914e8184f6..36b4c8c19e 100644 --- a/Lib/pydoc_data/topics.py +++ b/Lib/pydoc_data/topics.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Autogenerated by Sphinx on Mon Oct 31 20:37:53 2016 +# Autogenerated by Sphinx on Mon Nov 21 23:22:05 2016 topics = {'assert': '\n' 'The "assert" statement\n' '**********************\n' @@ -3702,7 +3702,7 @@ topics = {'assert': '\n' ' end). This is because any time you resume execution (even ' 'with a\n' ' simple next or step), you may encounter another ' - 'breakpoint--which\n' + 'breakpoint—which\n' ' could have its own command list, leading to ambiguities about ' 'which\n' ' list to execute.\n' -- cgit v1.2.1 From 3048149b67ba04d150c6e6303d3d1b7fea73e467 Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Tue, 22 Nov 2016 19:40:58 +0900 Subject: Issue #28023: Fix python-gdb.py didn't support new dict implementation --- Lib/test/test_gdb.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_gdb.py b/Lib/test/test_gdb.py index 5fbf154662..2bd4457e61 100644 --- a/Lib/test/test_gdb.py +++ b/Lib/test/test_gdb.py @@ -11,9 +11,6 @@ import sysconfig import unittest import locale -# FIXME: issue #28023 -raise unittest.SkipTest("FIXME: issue #28023, compact dict (issue #27350) broke python-gdb.py") - # Is this Python configured to support threads? try: import _thread @@ -296,9 +293,8 @@ class PrettyPrintTests(DebuggerTests): 'Verify the pretty-printing of dictionaries' self.assertGdbRepr({}) self.assertGdbRepr({'foo': 'bar'}, "{'foo': 'bar'}") - # PYTHONHASHSEED is need to get the exact item order - if not sys.flags.ignore_environment: - self.assertGdbRepr({'foo': 'bar', 'douglas': 42}, "{'douglas': 42, 'foo': 'bar'}") + # Python preserves insertion order since 3.6 + self.assertGdbRepr({'foo': 'bar', 'douglas': 42}, "{'foo': 'bar', 'douglas': 42}") def test_lists(self): 'Verify the pretty-printing of lists' @@ -819,6 +815,7 @@ id(42) ) self.assertIn('Garbage-collecting', gdb_output) + @unittest.skip("FIXME: builtin method is not shown in py-bt and py-bt-full") @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") # Some older versions of gdb will fail with -- cgit v1.2.1 From 4d83386bceca38593641c1921fb175a541fb88c2 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 22 Nov 2016 15:30:38 +0100 Subject: Issue #28727: Optimize pattern_richcompare() for a==a A pattern is equal to itself. --- Lib/test/test_re.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index 4fcd2d463d..84131d2b92 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -1781,6 +1781,10 @@ SUBPATTERN None 0 0 def test_pattern_compare(self): pattern1 = re.compile('abc', re.IGNORECASE) + # equal to itself + self.assertEqual(pattern1, pattern1) + self.assertFalse(pattern1 != pattern1) + # equal re.purge() pattern2 = re.compile('abc', re.IGNORECASE) -- cgit v1.2.1 From d0216891a88d9e2ad970b50a307421347474340f Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Thu, 24 Nov 2016 10:50:34 -0800 Subject: Issue #27100: Silence deprecation warning in Lib/test/test_with.py --- Lib/test/test_with.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_with.py b/Lib/test/test_with.py index cbb85da154..c70f6859b4 100644 --- a/Lib/test/test_with.py +++ b/Lib/test/test_with.py @@ -117,7 +117,7 @@ class FailureTestCase(unittest.TestCase): def fooLacksEnter(): foo = LacksEnter() with foo: pass - self.assertRaisesRegexp(AttributeError, '__enter__', fooLacksEnter) + self.assertRaisesRegex(AttributeError, '__enter__', fooLacksEnter) def testEnterAttributeError2(self): class LacksEnterAndExit(object): @@ -126,7 +126,7 @@ class FailureTestCase(unittest.TestCase): def fooLacksEnterAndExit(): foo = LacksEnterAndExit() with foo: pass - self.assertRaisesRegexp(AttributeError, '__enter__', fooLacksEnterAndExit) + self.assertRaisesRegex(AttributeError, '__enter__', fooLacksEnterAndExit) def testExitAttributeError(self): class LacksExit(object): @@ -136,7 +136,7 @@ class FailureTestCase(unittest.TestCase): def fooLacksExit(): foo = LacksExit() with foo: pass - self.assertRaisesRegexp(AttributeError, '__exit__', fooLacksExit) + self.assertRaisesRegex(AttributeError, '__exit__', fooLacksExit) def assertRaisesSyntaxError(self, codestr): def shouldRaiseSyntaxError(s): -- cgit v1.2.1 From 5d1b8611159db32c7a142602fdd7c1bbbac8b2f6 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 29 Nov 2016 09:54:17 +0200 Subject: Issue #28797: Modifying the class __dict__ inside the __set_name__ method of a descriptor that is used inside that class no longer prevents calling the __set_name__ method of other descriptors. --- Lib/test/test_subclassinit.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_subclassinit.py b/Lib/test/test_subclassinit.py index 6a12fb1ee0..3c331bb98f 100644 --- a/Lib/test/test_subclassinit.py +++ b/Lib/test/test_subclassinit.py @@ -198,6 +198,22 @@ class Test(unittest.TestCase): self.assertIs(B.meta_owner, B) self.assertEqual(B.name, 'd') + def test_set_name_modifying_dict(self): + notified = [] + class Descriptor: + def __set_name__(self, owner, name): + setattr(owner, name + 'x', None) + notified.append(name) + + class A: + a = Descriptor() + b = Descriptor() + c = Descriptor() + d = Descriptor() + e = Descriptor() + + self.assertCountEqual(notified, ['a', 'b', 'c', 'd', 'e']) + def test_errors(self): class MyMeta(type): pass -- cgit v1.2.1 From 720946323027f5bfb31c006cc9b3ce983af8291f Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Thu, 1 Dec 2016 11:36:22 -0500 Subject: Issue #28843: Fix asyncio C Task to handle exceptions __traceback__. --- Lib/test/test_asyncio/test_tasks.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index e048380e98..a18d49ae37 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -1952,6 +1952,21 @@ class BaseTaskTests: self.assertFalse(gather_task.cancelled()) self.assertEqual(gather_task.result(), [42]) + def test_exception_traceback(self): + # See http://bugs.python.org/issue28843 + + @asyncio.coroutine + def foo(): + 1 / 0 + + @asyncio.coroutine + def main(): + task = self.new_task(self.loop, foo()) + yield # skip one loop iteration + self.assertIsNotNone(task.exception().__traceback__) + + self.loop.run_until_complete(main()) + @mock.patch('asyncio.base_events.logger') def test_error_in_call_soon(self, m_log): def call_soon(callback, *args): -- cgit v1.2.1 From b9210b0f24ee0ba5861dffbaa42559a409a71a3e Mon Sep 17 00:00:00 2001 From: Nick Coghlan Date: Fri, 2 Dec 2016 20:29:57 +1000 Subject: Issue #27172: Undeprecate inspect.getfullargspec() This is still useful for single source Python 2/3 code migrating away from inspect.getargspec(), but that wasn't clear with the documented deprecation in place. --- Lib/inspect.py | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) (limited to 'Lib') diff --git a/Lib/inspect.py b/Lib/inspect.py index 6640375263..e08e9f578e 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -1019,24 +1019,30 @@ def _getfullargs(co): ArgSpec = namedtuple('ArgSpec', 'args varargs keywords defaults') def getargspec(func): - """Get the names and default values of a function's arguments. + """Get the names and default values of a function's parameters. A tuple of four things is returned: (args, varargs, keywords, defaults). 'args' is a list of the argument names, including keyword-only argument names. - 'varargs' and 'keywords' are the names of the * and ** arguments or None. - 'defaults' is an n-tuple of the default values of the last n arguments. + 'varargs' and 'keywords' are the names of the * and ** parameters or None. + 'defaults' is an n-tuple of the default values of the last n parameters. - Use the getfullargspec() API for Python 3 code, as annotations - and keyword arguments are supported. getargspec() will raise ValueError - if the func has either annotations or keyword arguments. + This function is deprecated, as it does not support annotations or + keyword-only parameters and will raise ValueError if either is present + on the supplied callable. + + For a more structured introspection API, use inspect.signature() instead. + + Alternatively, use getfullargspec() for an API with a similar namedtuple + based interface, but full support for annotations and keyword-only + parameters. """ warnings.warn("inspect.getargspec() is deprecated, " - "use inspect.signature() instead", DeprecationWarning, - stacklevel=2) + "use inspect.signature() or inspect.getfullargspec()", + DeprecationWarning, stacklevel=2) args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, ann = \ getfullargspec(func) if kwonlyargs or ann: - raise ValueError("Function has keyword-only arguments or annotations" + raise ValueError("Function has keyword-only parameters or annotations" ", use getfullargspec() API which can support them") return ArgSpec(args, varargs, varkw, defaults) @@ -1044,18 +1050,20 @@ FullArgSpec = namedtuple('FullArgSpec', 'args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, annotations') def getfullargspec(func): - """Get the names and default values of a callable object's arguments. + """Get the names and default values of a callable object's parameters. A tuple of seven things is returned: - (args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults annotations). - 'args' is a list of the argument names. - 'varargs' and 'varkw' are the names of the * and ** arguments or None. - 'defaults' is an n-tuple of the default values of the last n arguments. - 'kwonlyargs' is a list of keyword-only argument names. + (args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, annotations). + 'args' is a list of the parameter names. + 'varargs' and 'varkw' are the names of the * and ** parameters or None. + 'defaults' is an n-tuple of the default values of the last n parameters. + 'kwonlyargs' is a list of keyword-only parameter names. 'kwonlydefaults' is a dictionary mapping names from kwonlyargs to defaults. - 'annotations' is a dictionary mapping argument names to annotations. + 'annotations' is a dictionary mapping parameter names to annotations. - This function is deprecated, use inspect.signature() instead. + Notable differences from inspect.signature(): + - the "self" parameter is always reported, even for bound methods + - wrapper chains defined by __wrapped__ *not* unwrapped automatically """ try: -- cgit v1.2.1 From f1b02866add58317c7f9c6c813c282659981a147 Mon Sep 17 00:00:00 2001 From: Nick Coghlan Date: Mon, 5 Dec 2016 16:47:55 +1000 Subject: Issue #23722: improve __classcell__ compatibility Handling zero-argument super() in __init_subclass__ and __set_name__ involved moving __class__ initialisation to type.__new__. This requires cooperation from custom metaclasses to ensure that the new __classcell__ entry is passed along appropriately. The initial implementation of that change resulted in abruptly broken zero-argument super() support in metaclasses that didn't adhere to the new requirements (such as Django's metaclass for Model definitions). The updated approach adopted here instead emits a deprecation warning for those cases, and makes them work the same way they did in Python 3.5. This patch also improves the related class machinery documentation to cover these details and to include more reader-friendly cross-references and index entries. --- Lib/importlib/_bootstrap_external.py | 3 +- Lib/test/test_super.py | 127 ++++++++++++++++++++++++++++------- 2 files changed, 106 insertions(+), 24 deletions(-) (limited to 'Lib') diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 5cb58ab2f5..ab434460c2 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -239,6 +239,7 @@ _code_type = type(_write_atomic.__code__) # Python 3.6b1 3376 (simplify CALL_FUNCTIONs & BUILD_MAP_UNPACK_WITH_CALL) # Python 3.6b1 3377 (set __class__ cell from type.__new__ #23722) # Python 3.6b2 3378 (add BUILD_TUPLE_UNPACK_WITH_CALL #28257) +# Python 3.6rc1 3379 (more thorough __class__ validation #23722) # # MAGIC must change whenever the bytecode emitted by the compiler may no # longer be understood by older implementations of the eval loop (usually @@ -247,7 +248,7 @@ _code_type = type(_write_atomic.__code__) # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # in PC/launcher.c must also be updated. -MAGIC_NUMBER = (3378).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3379).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c _PYCACHE = '__pycache__' diff --git a/Lib/test/test_super.py b/Lib/test/test_super.py index a7ceded0b8..447dec9130 100644 --- a/Lib/test/test_super.py +++ b/Lib/test/test_super.py @@ -1,7 +1,9 @@ -"""Unit tests for new super() implementation.""" +"""Unit tests for zero-argument super() & related machinery.""" import sys import unittest +import warnings +from test.support import check_warnings class A: @@ -144,6 +146,8 @@ class TestSuper(unittest.TestCase): self.assertIs(X.f(), X) def test___class___new(self): + # See issue #23722 + # Ensure zero-arg super() works as soon as type.__new__() is completed test_class = None class Meta(type): @@ -161,6 +165,7 @@ class TestSuper(unittest.TestCase): self.assertIs(test_class, A) def test___class___delayed(self): + # See issue #23722 test_namespace = None class Meta(type): @@ -169,10 +174,14 @@ class TestSuper(unittest.TestCase): test_namespace = namespace return None - class A(metaclass=Meta): - @staticmethod - def f(): - return __class__ + # This case shouldn't trigger the __classcell__ deprecation warning + with check_warnings() as w: + warnings.simplefilter("always", DeprecationWarning) + class A(metaclass=Meta): + @staticmethod + def f(): + return __class__ + self.assertEqual(w.warnings, []) self.assertIs(A, None) @@ -180,6 +189,7 @@ class TestSuper(unittest.TestCase): self.assertIs(B.f(), B) def test___class___mro(self): + # See issue #23722 test_class = None class Meta(type): @@ -195,34 +205,105 @@ class TestSuper(unittest.TestCase): self.assertIs(test_class, A) - def test___classcell___deleted(self): + def test___classcell___expected_behaviour(self): + # See issue #23722 class Meta(type): def __new__(cls, name, bases, namespace): - del namespace['__classcell__'] + nonlocal namespace_snapshot + namespace_snapshot = namespace.copy() return super().__new__(cls, name, bases, namespace) - class A(metaclass=Meta): - @staticmethod - def f(): - __class__ - - with self.assertRaises(NameError): - A.f() + # __classcell__ is injected into the class namespace by the compiler + # when at least one method needs it, and should be omitted otherwise + namespace_snapshot = None + class WithoutClassRef(metaclass=Meta): + pass + self.assertNotIn("__classcell__", namespace_snapshot) + + # With zero-arg super() or an explicit __class__ reference, + # __classcell__ is the exact cell reference to be populated by + # type.__new__ + namespace_snapshot = None + class WithClassRef(metaclass=Meta): + def f(self): + return __class__ - def test___classcell___reset(self): + class_cell = namespace_snapshot["__classcell__"] + method_closure = WithClassRef.f.__closure__ + self.assertEqual(len(method_closure), 1) + self.assertIs(class_cell, method_closure[0]) + # Ensure the cell reference *doesn't* get turned into an attribute + with self.assertRaises(AttributeError): + WithClassRef.__classcell__ + + def test___classcell___missing(self): + # See issue #23722 + # Some metaclasses may not pass the original namespace to type.__new__ + # We test that case here by forcibly deleting __classcell__ class Meta(type): def __new__(cls, name, bases, namespace): - namespace['__classcell__'] = 0 + namespace.pop('__classcell__', None) return super().__new__(cls, name, bases, namespace) - class A(metaclass=Meta): - @staticmethod - def f(): - __class__ + # The default case should continue to work without any warnings + with check_warnings() as w: + warnings.simplefilter("always", DeprecationWarning) + class WithoutClassRef(metaclass=Meta): + pass + self.assertEqual(w.warnings, []) + + # With zero-arg super() or an explicit __class__ reference, we expect + # __build_class__ to emit a DeprecationWarning complaining that + # __class__ was not set, and asking if __classcell__ was propagated + # to type.__new__. + # In Python 3.7, that warning will become a RuntimeError. + expected_warning = ( + '__class__ not set.*__classcell__ propagated', + DeprecationWarning + ) + with check_warnings(expected_warning): + warnings.simplefilter("always", DeprecationWarning) + class WithClassRef(metaclass=Meta): + def f(self): + return __class__ + # Check __class__ still gets set despite the warning + self.assertIs(WithClassRef().f(), WithClassRef) + + # Check the warning is turned into an error as expected + with warnings.catch_warnings(): + warnings.simplefilter("error", DeprecationWarning) + with self.assertRaises(DeprecationWarning): + class WithClassRef(metaclass=Meta): + def f(self): + return __class__ + + def test___classcell___overwrite(self): + # See issue #23722 + # Overwriting __classcell__ with nonsense is explicitly prohibited + class Meta(type): + def __new__(cls, name, bases, namespace, cell): + namespace['__classcell__'] = cell + return super().__new__(cls, name, bases, namespace) + + for bad_cell in (None, 0, "", object()): + with self.subTest(bad_cell=bad_cell): + with self.assertRaises(TypeError): + class A(metaclass=Meta, cell=bad_cell): + pass - with self.assertRaises(NameError): - A.f() - self.assertEqual(A.__classcell__, 0) + def test___classcell___wrong_cell(self): + # See issue #23722 + # Pointing the cell reference at the wrong class is also prohibited + class Meta(type): + def __new__(cls, name, bases, namespace): + cls = super().__new__(cls, name, bases, namespace) + B = type("B", (), namespace) + return cls + + with self.assertRaises(TypeError): + class A(metaclass=Meta): + def f(self): + return __class__ def test_obscure_super_errors(self): def f(): -- cgit v1.2.1 From 31eefcc327e07ccc168347795ac5d4b0fcc84cc4 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 6 Dec 2016 10:53:52 +0100 Subject: catch_warnings() calls showwarning() if overriden Issue #28089: Fix a regression introduced in warnings.catch_warnings(): call warnings.showwarning() if it was overriden inside the context manager. --- Lib/test/test_warnings/__init__.py | 45 ++++++++++++++++++++++++++++++++++++++ Lib/warnings.py | 14 ++++++++++-- 2 files changed, 57 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py index c11ee400b5..60fa780633 100644 --- a/Lib/test/test_warnings/__init__.py +++ b/Lib/test/test_warnings/__init__.py @@ -944,6 +944,51 @@ class CatchWarningTests(BaseTest): self.assertTrue(wmod.filters is not orig_filters) self.assertTrue(wmod.filters is orig_filters) + def test_record_override_showwarning_before(self): + # Issue #28089: If warnings.showwarning() was overriden, make sure + # that catch_warnings(record=True) overrides it again. + text = "This is a warning" + wmod = self.module + my_log = [] + + def my_logger(message, category, filename, lineno, file=None, line=None): + nonlocal my_log + my_log.append(message) + + # Override warnings.showwarning() before calling catch_warnings() + with support.swap_attr(wmod, 'showwarning', my_logger): + with wmod.catch_warnings(module=wmod, record=True) as log: + self.assertIsNot(wmod.showwarning, my_logger) + + wmod.simplefilter("always") + wmod.warn(text) + + self.assertIs(wmod.showwarning, my_logger) + + self.assertEqual(len(log), 1, log) + self.assertEqual(log[0].message.args[0], text) + self.assertEqual(my_log, []) + + def test_record_override_showwarning_inside(self): + # Issue #28089: It is possible to override warnings.showwarning() + # in the catch_warnings(record=True) context manager. + text = "This is a warning" + wmod = self.module + my_log = [] + + def my_logger(message, category, filename, lineno, file=None, line=None): + nonlocal my_log + my_log.append(message) + + with wmod.catch_warnings(module=wmod, record=True) as log: + wmod.simplefilter("always") + wmod.showwarning = my_logger + wmod.warn(text) + + self.assertEqual(len(my_log), 1, my_log) + self.assertEqual(my_log[0].args[0], text) + self.assertEqual(log, []) + def test_check_warnings(self): # Explicit tests for the test.support convenience wrapper wmod = self.module diff --git a/Lib/warnings.py b/Lib/warnings.py index 2b407ffed9..0d167667d1 100644 --- a/Lib/warnings.py +++ b/Lib/warnings.py @@ -447,11 +447,20 @@ class catch_warnings(object): self._module._filters_mutated() self._showwarning = self._module.showwarning self._showwarnmsg = self._module._showwarnmsg + self._showwarnmsg_impl = self._module._showwarnmsg_impl if self._record: log = [] - def showarnmsg(msg): + + def showarnmsg_logger(msg): + nonlocal log log.append(msg) - self._module._showwarnmsg = showarnmsg + + self._module._showwarnmsg_impl = showarnmsg_logger + + # Reset showwarning() to the default implementation to make sure + # that _showwarnmsg() calls _showwarnmsg_impl() + self._module.showwarning = self._module._showwarning + return log else: return None @@ -463,6 +472,7 @@ class catch_warnings(object): self._module._filters_mutated() self._module.showwarning = self._showwarning self._module._showwarnmsg = self._showwarnmsg + self._module._showwarnmsg_impl = self._showwarnmsg_impl # filters contains a sequence of filter 5-tuples -- cgit v1.2.1 From c1e88b95f36c57be30d3dd5437089b777ac46252 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 6 Dec 2016 11:02:12 +0100 Subject: warnings: Fix the issue number The fix for catch_warnings() is the issue #28835 (not the issue #28089). --- Lib/test/test_warnings/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py index 60fa780633..0cddf4a2f4 100644 --- a/Lib/test/test_warnings/__init__.py +++ b/Lib/test/test_warnings/__init__.py @@ -945,7 +945,7 @@ class CatchWarningTests(BaseTest): self.assertTrue(wmod.filters is orig_filters) def test_record_override_showwarning_before(self): - # Issue #28089: If warnings.showwarning() was overriden, make sure + # Issue #28835: If warnings.showwarning() was overriden, make sure # that catch_warnings(record=True) overrides it again. text = "This is a warning" wmod = self.module @@ -970,7 +970,7 @@ class CatchWarningTests(BaseTest): self.assertEqual(my_log, []) def test_record_override_showwarning_inside(self): - # Issue #28089: It is possible to override warnings.showwarning() + # Issue #28835: It is possible to override warnings.showwarning() # in the catch_warnings(record=True) context manager. text = "This is a warning" wmod = self.module -- cgit v1.2.1 From 9bd3adc7445d23ed7a85245d090278a452699aec Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 6 Dec 2016 19:15:29 +0200 Subject: Issue #27030: Unknown escapes in re.sub() replacement template are allowed again. But they still are deprecated and will be disabled in 3.7. --- Lib/sre_parse.py | 4 +++- Lib/test/test_re.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/sre_parse.py b/Lib/sre_parse.py index ab37fd3fe2..6aa49c3bf6 100644 --- a/Lib/sre_parse.py +++ b/Lib/sre_parse.py @@ -947,7 +947,9 @@ def parse_template(source, pattern): this = chr(ESCAPES[this][1]) except KeyError: if c in ASCIILETTERS: - raise s.error('bad escape %s' % this, len(this)) + import warnings + warnings.warn('bad escape %s' % this, + DeprecationWarning, stacklevel=4) lappend(this) else: lappend(this) diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index 84131d2b92..4bdaa4b6c6 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -126,7 +126,7 @@ class ReTests(unittest.TestCase): (chr(9)+chr(10)+chr(11)+chr(13)+chr(12)+chr(7)+chr(8))) for c in 'cdehijklmopqsuwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ': with self.subTest(c): - with self.assertRaises(re.error): + with self.assertWarns(DeprecationWarning): self.assertEqual(re.sub('a', '\\' + c, 'a'), '\\' + c) self.assertEqual(re.sub(r'^\s*', 'X', 'test'), 'Xtest') -- cgit v1.2.1 From b2efbe86dc6740c33bdf6b516d1e19621170d24a Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Tue, 6 Dec 2016 17:12:47 -0500 Subject: Issue #28835: Tidy previous showwarning changes based on review comments. Patch by Serhiy Storchaka. --- Lib/warnings.py | 55 +++++++++++++++++++++++++++---------------------------- 1 file changed, 27 insertions(+), 28 deletions(-) (limited to 'Lib') diff --git a/Lib/warnings.py b/Lib/warnings.py index 0d167667d1..5badb0be37 100644 --- a/Lib/warnings.py +++ b/Lib/warnings.py @@ -80,32 +80,40 @@ def _formatwarnmsg_impl(msg): return s # Keep a reference to check if the function was replaced -_showwarning = showwarning +_showwarning_orig = showwarning def _showwarnmsg(msg): """Hook to write a warning to a file; replace if you like.""" - showwarning = globals().get('showwarning', _showwarning) - if showwarning is not _showwarning: - # warnings.showwarning() was replaced - if not callable(showwarning): - raise TypeError("warnings.showwarning() must be set to a " - "function or method") - - showwarning(msg.message, msg.category, msg.filename, msg.lineno, - msg.file, msg.line) - return + try: + sw = showwarning + except NameError: + pass + else: + if sw is not _showwarning_orig: + # warnings.showwarning() was replaced + if not callable(sw): + raise TypeError("warnings.showwarning() must be set to a " + "function or method") + + sw(msg.message, msg.category, msg.filename, msg.lineno, + msg.file, msg.line) + return _showwarnmsg_impl(msg) # Keep a reference to check if the function was replaced -_formatwarning = formatwarning +_formatwarning_orig = formatwarning def _formatwarnmsg(msg): """Function to format a warning the standard way.""" - formatwarning = globals().get('formatwarning', _formatwarning) - if formatwarning is not _formatwarning: - # warnings.formatwarning() was replaced - return formatwarning(msg.message, msg.category, - msg.filename, msg.lineno, line=msg.line) + try: + fw = formatwarning + except NameError: + pass + else: + if fw is not _formatwarning_orig: + # warnings.formatwarning() was replaced + return fw(msg.message, msg.category, + msg.filename, msg.lineno, line=msg.line) return _formatwarnmsg_impl(msg) def filterwarnings(action, message="", category=Warning, module="", lineno=0, @@ -446,21 +454,13 @@ class catch_warnings(object): self._module.filters = self._filters[:] self._module._filters_mutated() self._showwarning = self._module.showwarning - self._showwarnmsg = self._module._showwarnmsg self._showwarnmsg_impl = self._module._showwarnmsg_impl if self._record: log = [] - - def showarnmsg_logger(msg): - nonlocal log - log.append(msg) - - self._module._showwarnmsg_impl = showarnmsg_logger - + self._module._showwarnmsg_impl = log.append # Reset showwarning() to the default implementation to make sure # that _showwarnmsg() calls _showwarnmsg_impl() - self._module.showwarning = self._module._showwarning - + self._module.showwarning = self._module._showwarning_orig return log else: return None @@ -471,7 +471,6 @@ class catch_warnings(object): self._module.filters = self._filters self._module._filters_mutated() self._module.showwarning = self._showwarning - self._module._showwarnmsg = self._showwarnmsg self._module._showwarnmsg_impl = self._showwarnmsg_impl -- cgit v1.2.1 From bda7d728896da7ad329c8dd86cc7056f18c2881c Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Tue, 6 Dec 2016 18:53:16 -0500 Subject: Update pydoc topics for 3.6.0rc1 --- Lib/pydoc_data/topics.py | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) (limited to 'Lib') diff --git a/Lib/pydoc_data/topics.py b/Lib/pydoc_data/topics.py index 36b4c8c19e..c7fac3395b 100644 --- a/Lib/pydoc_data/topics.py +++ b/Lib/pydoc_data/topics.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Autogenerated by Sphinx on Mon Nov 21 23:22:05 2016 +# Autogenerated by Sphinx on Tue Dec 6 18:51:51 2016 topics = {'assert': '\n' 'The "assert" statement\n' '**********************\n' @@ -8503,9 +8503,10 @@ topics = {'assert': '\n' 'defined at the\n' 'class scope. Class variables must be accessed through the ' 'first\n' - 'parameter of instance or class methods, and cannot be ' - 'accessed at all\n' - 'from static methods.\n' + 'parameter of instance or class methods, or through the ' + 'implicit\n' + 'lexically scoped "__class__" reference described in the next ' + 'section.\n' '\n' '\n' 'Creating the class object\n' @@ -8535,6 +8536,38 @@ topics = {'assert': '\n' 'passed to the\n' 'method.\n' '\n' + '**CPython implementation detail:** In CPython 3.6 and later, ' + 'the\n' + '"__class__" cell is passed to the metaclass as a ' + '"__classcell__" entry\n' + 'in the class namespace. If present, this must be propagated ' + 'up to the\n' + '"type.__new__" call in order for the class to be ' + 'initialised\n' + 'correctly. Failing to do so will result in a ' + '"DeprecationWarning" in\n' + 'Python 3.6, and a "RuntimeWarning" in the future.\n' + '\n' + 'When using the default metaclass "type", or any metaclass ' + 'that\n' + 'ultimately calls "type.__new__", the following additional\n' + 'customisation steps are invoked after creating the class ' + 'object:\n' + '\n' + '* first, "type.__new__" collects all of the descriptors in ' + 'the class\n' + ' namespace that define a "__set_name__()" method;\n' + '\n' + '* second, all of these "__set_name__" methods are called ' + 'with the\n' + ' class being defined and the assigned name of that ' + 'particular\n' + ' descriptor; and\n' + '\n' + '* finally, the "__init_subclass__()" hook is called on the ' + 'immediate\n' + ' parent of the new class in its method resolution order.\n' + '\n' 'After the class object is created, it is passed to the ' 'class\n' 'decorators included in the class definition (if any) and the ' -- cgit v1.2.1 From 7b0db7349755e9fdcded21a865e1f81655a7ae6d Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Thu, 8 Dec 2016 11:06:56 +0100 Subject: Issue #26939: Add the support.setswitchinterval() function to fix test_functools hanging on the Android armv7 qemu emulator. --- Lib/test/support/__init__.py | 16 ++++++++++++++++ Lib/test/test_functools.py | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 1e27777517..fa344ece23 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -93,6 +93,7 @@ __all__ = [ "check__all__", "requires_android_level", "requires_multiprocessing_queue", # sys "is_jython", "is_android", "check_impl_detail", "unix_shell", + "setswitchinterval", # network "HOST", "IPV6_ENABLED", "find_unused_port", "bind_port", "open_urlresource", # processes @@ -2547,3 +2548,18 @@ def missing_compiler_executable(cmd_names=[]): continue if spawn.find_executable(cmd[0]) is None: return cmd[0] + + +_is_android_emulator = None +def setswitchinterval(interval): + # Setting a very low gil interval on the Android emulator causes python + # to hang (issue #26939). + minimum_interval = 1e-5 + if is_android and interval < minimum_interval: + global _is_android_emulator + if _is_android_emulator is None: + _is_android_emulator = (subprocess.check_output( + ['getprop', 'ro.kernel.qemu']).strip() == b'1') + if _is_android_emulator: + interval = minimum_interval + return sys.setswitchinterval(interval) diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index 75427dfad3..ba2a52fb73 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -1322,7 +1322,7 @@ class TestLRU: f.cache_clear() orig_si = sys.getswitchinterval() - sys.setswitchinterval(1e-6) + support.setswitchinterval(1e-6) try: # create n threads in order to fill cache threads = [threading.Thread(target=full, args=[k]) -- cgit v1.2.1 From b0af5752804f6cddd5a82c99c8a9311bd1f1b0b5 Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Thu, 8 Dec 2016 11:26:18 +0100 Subject: Issue #26940: Fix test_importlib that hangs on the Android armv7 qemu emulator. --- Lib/test/test_importlib/test_locks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_importlib/test_locks.py b/Lib/test/test_importlib/test_locks.py index b2aadff289..dbce9c2dfd 100644 --- a/Lib/test/test_importlib/test_locks.py +++ b/Lib/test/test_importlib/test_locks.py @@ -57,7 +57,7 @@ if threading is not None: def setUp(self): try: self.old_switchinterval = sys.getswitchinterval() - sys.setswitchinterval(0.000001) + support.setswitchinterval(0.000001) except AttributeError: self.old_switchinterval = None -- cgit v1.2.1 From ed89f007658643760751b3be1a9cfe7715360e52 Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Thu, 8 Dec 2016 12:21:00 +0100 Subject: Issue #26941: Fix test_threading that hangs on the Android armv7 qemu emulator. --- Lib/test/test_threading.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 845e7d4bd4..2c2914fd6d 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -462,7 +462,7 @@ class ThreadTests(BaseTestCase): self.addCleanup(sys.setswitchinterval, old_interval) # Make the bug more likely to manifest. - sys.setswitchinterval(1e-6) + test.support.setswitchinterval(1e-6) for i in range(20): t = threading.Thread(target=lambda: None) -- cgit v1.2.1 From cca2f7c4a65e8ca40680added015863207ab6bbe Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 22 Nov 2016 22:53:18 +0100 Subject: Issue #28770: Update python-gdb.py for fastcalls Frame.is_other_python_frame() now also handles _PyCFunction_FastCallDict() frames. Thanks to the new code to handle fast calls, python-gdb.py is now also able to detect the frame. --- Lib/test/test_gdb.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_gdb.py b/Lib/test/test_gdb.py index 2bd4457e61..60f1d92846 100644 --- a/Lib/test/test_gdb.py +++ b/Lib/test/test_gdb.py @@ -679,7 +679,7 @@ class StackNavigationTests(DebuggerTests): def test_pyup_command(self): 'Verify that the "py-up" command works' bt = self.get_stack_trace(script=self.get_sample_script(), - cmds_after_breakpoint=['py-up']) + cmds_after_breakpoint=['py-up', 'py-up']) self.assertMultilineMatches(bt, r'''^.* #[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\) @@ -698,7 +698,7 @@ $''') def test_up_at_top(self): 'Verify handling of "py-up" at the top of the stack' bt = self.get_stack_trace(script=self.get_sample_script(), - cmds_after_breakpoint=['py-up'] * 4) + cmds_after_breakpoint=['py-up'] * 5) self.assertEndsWith(bt, 'Unable to find an older python frame\n') @@ -708,7 +708,7 @@ $''') def test_up_then_down(self): 'Verify "py-up" followed by "py-down"' bt = self.get_stack_trace(script=self.get_sample_script(), - cmds_after_breakpoint=['py-up', 'py-down']) + cmds_after_breakpoint=['py-up', 'py-up', 'py-down']) self.assertMultilineMatches(bt, r'''^.* #[0-9]+ Frame 0x-?[0-9a-f]+, for file .*gdb_sample.py, line 7, in bar \(a=1, b=2, c=3\) @@ -727,6 +727,7 @@ class PyBtTests(DebuggerTests): self.assertMultilineMatches(bt, r'''^.* Traceback \(most recent call first\): + File ".*gdb_sample.py", line 10, in baz id\(42\) File ".*gdb_sample.py", line 7, in bar @@ -815,7 +816,6 @@ id(42) ) self.assertIn('Garbage-collecting', gdb_output) - @unittest.skip("FIXME: builtin method is not shown in py-bt and py-bt-full") @unittest.skipIf(python_is_optimized(), "Python was compiled with optimizations") # Some older versions of gdb will fail with @@ -854,7 +854,7 @@ class PyPrintTests(DebuggerTests): def test_basic_command(self): 'Verify that the "py-print" command works' bt = self.get_stack_trace(script=self.get_sample_script(), - cmds_after_breakpoint=['py-print args']) + cmds_after_breakpoint=['py-up', 'py-print args']) self.assertMultilineMatches(bt, r".*\nlocal 'args' = \(1, 2, 3\)\n.*") @@ -863,7 +863,7 @@ class PyPrintTests(DebuggerTests): @unittest.skipUnless(HAS_PYUP_PYDOWN, "test requires py-up/py-down commands") def test_print_after_up(self): bt = self.get_stack_trace(script=self.get_sample_script(), - cmds_after_breakpoint=['py-up', 'py-print c', 'py-print b', 'py-print a']) + cmds_after_breakpoint=['py-up', 'py-up', 'py-print c', 'py-print b', 'py-print a']) self.assertMultilineMatches(bt, r".*\nlocal 'c' = 3\nlocal 'b' = 2\nlocal 'a' = 1\n.*") @@ -871,7 +871,7 @@ class PyPrintTests(DebuggerTests): "Python was compiled with optimizations") def test_printing_global(self): bt = self.get_stack_trace(script=self.get_sample_script(), - cmds_after_breakpoint=['py-print __name__']) + cmds_after_breakpoint=['py-up', 'py-print __name__']) self.assertMultilineMatches(bt, r".*\nglobal '__name__' = '__main__'\n.*") @@ -879,7 +879,7 @@ class PyPrintTests(DebuggerTests): "Python was compiled with optimizations") def test_printing_builtin(self): bt = self.get_stack_trace(script=self.get_sample_script(), - cmds_after_breakpoint=['py-print len']) + cmds_after_breakpoint=['py-up', 'py-print len']) self.assertMultilineMatches(bt, r".*\nbuiltin 'len' = \n.*") @@ -888,7 +888,7 @@ class PyLocalsTests(DebuggerTests): "Python was compiled with optimizations") def test_basic_command(self): bt = self.get_stack_trace(script=self.get_sample_script(), - cmds_after_breakpoint=['py-locals']) + cmds_after_breakpoint=['py-up', 'py-locals']) self.assertMultilineMatches(bt, r".*\nargs = \(1, 2, 3\)\n.*") @@ -897,7 +897,7 @@ class PyLocalsTests(DebuggerTests): "Python was compiled with optimizations") def test_locals_after_up(self): bt = self.get_stack_trace(script=self.get_sample_script(), - cmds_after_breakpoint=['py-up', 'py-locals']) + cmds_after_breakpoint=['py-up', 'py-up', 'py-locals']) self.assertMultilineMatches(bt, r".*\na = 1\nb = 2\nc = 3\n.*") -- cgit v1.2.1 From 12201a6874ee891b263f9c405d93a4359f038e67 Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Fri, 9 Dec 2016 09:33:09 +0100 Subject: Issue #26937: The chown() method of the tarfile.TarFile class does not fail now when the grp module cannot be imported, as for example on Android platforms. --- Lib/tarfile.py | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) (limited to 'Lib') diff --git a/Lib/tarfile.py b/Lib/tarfile.py index b78b1b1f4b..5d4c86ce36 100755 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -50,9 +50,13 @@ import copy import re try: - import grp, pwd + import pwd except ImportError: - grp = pwd = None + pwd = None +try: + import grp +except ImportError: + grp = None # os.symlink on Windows prior to 6.0 raises NotImplementedError symlink_exception = (AttributeError, NotImplementedError) @@ -2219,22 +2223,25 @@ class TarFile(object): def chown(self, tarinfo, targetpath, numeric_owner): """Set owner of targetpath according to tarinfo. If numeric_owner - is True, use .gid/.uid instead of .gname/.uname. + is True, use .gid/.uid instead of .gname/.uname. If numeric_owner + is False, fall back to .gid/.uid when the search based on name + fails. """ - if pwd and hasattr(os, "geteuid") and os.geteuid() == 0: + if hasattr(os, "geteuid") and os.geteuid() == 0: # We have to be root to do so. - if numeric_owner: - g = tarinfo.gid - u = tarinfo.uid - else: + g = tarinfo.gid + u = tarinfo.uid + if not numeric_owner: try: - g = grp.getgrnam(tarinfo.gname)[2] + if grp: + g = grp.getgrnam(tarinfo.gname)[2] except KeyError: - g = tarinfo.gid + pass try: - u = pwd.getpwnam(tarinfo.uname)[2] + if pwd: + u = pwd.getpwnam(tarinfo.uname)[2] except KeyError: - u = tarinfo.uid + pass try: if tarinfo.issym() and hasattr(os, "lchown"): os.lchown(targetpath, u, g) -- cgit v1.2.1 From 7480ba43f3a27b2d9c9cc76bcbf07872fd1c1c22 Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Sat, 10 Dec 2016 17:31:28 +0100 Subject: Issue #28849: Do not define sys.implementation._multiarch on Android. --- Lib/test/test_sysconfig.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py index 091e90522d..747b2e5815 100644 --- a/Lib/test/test_sysconfig.py +++ b/Lib/test/test_sysconfig.py @@ -385,7 +385,8 @@ class TestSysConfig(unittest.TestCase): self.assertIsNotNone(vars['SO']) self.assertEqual(vars['SO'], vars['EXT_SUFFIX']) - @unittest.skipUnless(sys.platform == 'linux', 'Linux-specific test') + @unittest.skipUnless(hasattr(sys.implementation, '_multiarch'), + 'multiarch-specific test') def test_triplet_in_ext_suffix(self): import ctypes, platform, re machine = platform.machine() @@ -395,7 +396,6 @@ class TestSysConfig(unittest.TestCase): if re.match('(i[3-6]86|x86_64)$', machine): if ctypes.sizeof(ctypes.c_char_p()) == 4: self.assertTrue(suffix.endswith('i386-linux-gnu.so') or - suffix.endswith('i686-linux-android.so') or suffix.endswith('x86_64-linux-gnux32.so'), suffix) else: # 8 byte pointer size -- cgit v1.2.1 From 3759dfc49056abec76793c75729aa85eea4f32cd Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 11 Dec 2016 19:37:19 +0200 Subject: Issue #28739: f-string expressions no longer accepted as docstrings and by ast.literal_eval() even if they do not include subexpressions. --- Lib/test/test_fstring.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index 82050835f6..708ed25b52 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -70,18 +70,18 @@ f'{a * x()}'""" # Make sure x was called. self.assertTrue(x.called) - def test_literal_eval(self): - # With no expressions, an f-string is okay. - self.assertEqual(ast.literal_eval("f'x'"), 'x') - self.assertEqual(ast.literal_eval("f'x' 'y'"), 'xy') - - # But this should raise an error. - with self.assertRaisesRegex(ValueError, 'malformed node or string'): - ast.literal_eval("f'x{3}'") + def test_docstring(self): + def f(): + f'''Not a docstring''' + self.assertIsNone(f.__doc__) + def g(): + '''Not a docstring''' \ + f'' + self.assertIsNone(g.__doc__) - # As should this, which uses a different ast node + def test_literal_eval(self): with self.assertRaisesRegex(ValueError, 'malformed node or string'): - ast.literal_eval("f'{3}'") + ast.literal_eval("f'x'") def test_ast_compile_time_concat(self): x = [''] -- cgit v1.2.1 From 7aa9589ed672656185eca3f8406438d6a2b7efb1 Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Mon, 12 Dec 2016 09:55:57 +0100 Subject: Issue #28764: Fix a test_mailbox failure on Android API 24 when run as a non-root user. --- Lib/mailbox.py | 21 ++++++++++++--------- Lib/test/test_mailbox.py | 4 ++-- 2 files changed, 14 insertions(+), 11 deletions(-) (limited to 'Lib') diff --git a/Lib/mailbox.py b/Lib/mailbox.py index 0e23987ce7..39f24f9a72 100644 --- a/Lib/mailbox.py +++ b/Lib/mailbox.py @@ -313,11 +313,12 @@ class Maildir(Mailbox): # final position in order to prevent race conditions with changes # from other programs try: - if hasattr(os, 'link'): + try: os.link(tmp_file.name, dest) - os.remove(tmp_file.name) - else: + except (AttributeError, PermissionError): os.rename(tmp_file.name, dest) + else: + os.remove(tmp_file.name) except OSError as e: os.remove(tmp_file.name) if e.errno == errno.EEXIST: @@ -1200,13 +1201,14 @@ class MH(Mailbox): for key in self.iterkeys(): if key - 1 != prev: changes.append((key, prev + 1)) - if hasattr(os, 'link'): + try: os.link(os.path.join(self._path, str(key)), os.path.join(self._path, str(prev + 1))) - os.unlink(os.path.join(self._path, str(key))) - else: + except (AttributeError, PermissionError): os.rename(os.path.join(self._path, str(key)), os.path.join(self._path, str(prev + 1))) + else: + os.unlink(os.path.join(self._path, str(key))) prev += 1 self._next_key = prev + 1 if len(changes) == 0: @@ -2076,13 +2078,14 @@ def _lock_file(f, dotlock=True): else: raise try: - if hasattr(os, 'link'): + try: os.link(pre_lock.name, f.name + '.lock') dotlock_done = True - os.unlink(pre_lock.name) - else: + except (AttributeError, PermissionError): os.rename(pre_lock.name, f.name + '.lock') dotlock_done = True + else: + os.unlink(pre_lock.name) except FileExistsError: os.remove(pre_lock.name) raise ExternalClashError('dot lock unavailable: %s' % diff --git a/Lib/test/test_mailbox.py b/Lib/test/test_mailbox.py index aeabdbb2b5..2ba944335a 100644 --- a/Lib/test/test_mailbox.py +++ b/Lib/test/test_mailbox.py @@ -2137,9 +2137,9 @@ class MaildirTestCase(unittest.TestCase): if mbox: fp.write(FROM_) fp.write(DUMMY_MESSAGE) - if hasattr(os, "link"): + try: os.link(tmpname, newname) - else: + except (AttributeError, PermissionError): with open(newname, "w") as fp: fp.write(DUMMY_MESSAGE) self._msgfiles.append(newname) -- cgit v1.2.1 From 40232e96fe1a1d5a925e8f2c850fdcd4cf09c8d3 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Mon, 12 Dec 2016 11:17:59 -0800 Subject: Issue #28896: Disable WindowsRegistryFinder by default. --- Lib/importlib/_bootstrap_external.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'Lib') diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index ab434460c2..9feec50842 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -1440,6 +1440,4 @@ def _install(_bootstrap_module): _setup(_bootstrap_module) supported_loaders = _get_supported_file_loaders() sys.path_hooks.extend([FileFinder.path_hook(*supported_loaders)]) - if _os.__name__ == 'nt': - sys.meta_path.append(WindowsRegistryFinder) sys.meta_path.append(PathFinder) -- cgit v1.2.1 From af2e15f3fc92f3f4818e5be35f3173a129804206 Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Tue, 13 Dec 2016 09:11:38 +0100 Subject: Issue #26856: Fix the tests assuming that the pwd module has getpwall() and assuming some invariants about uids that are not valid for Android. --- Lib/test/test_pathlib.py | 2 ++ Lib/test/test_pwd.py | 26 ++++++++++++++++++++------ 2 files changed, 22 insertions(+), 6 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index f98c1febb5..d25b1336f6 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -2080,6 +2080,8 @@ class PosixPathTest(_BasePathTest, unittest.TestCase): self.assertEqual(given, expect) self.assertEqual(set(p.rglob("FILEd*")), set()) + @unittest.skipUnless(hasattr(pwd, 'getpwall'), + 'pwd module does not expose getpwall()') def test_expanduser(self): P = self.cls support.import_module('pwd') diff --git a/Lib/test/test_pwd.py b/Lib/test/test_pwd.py index b7b1a4a5f6..ac9cff789e 100644 --- a/Lib/test/test_pwd.py +++ b/Lib/test/test_pwd.py @@ -4,10 +4,19 @@ from test import support pwd = support.import_module('pwd') +def _getpwall(): + # Android does not have getpwall. + if hasattr(pwd, 'getpwall'): + return pwd.getpwall() + elif hasattr(pwd, 'getpwuid'): + return [pwd.getpwuid(0)] + else: + return [] + class PwdTest(unittest.TestCase): def test_values(self): - entries = pwd.getpwall() + entries = _getpwall() for e in entries: self.assertEqual(len(e), 7) @@ -33,7 +42,7 @@ class PwdTest(unittest.TestCase): # and check afterwards (done in test_values_extended) def test_values_extended(self): - entries = pwd.getpwall() + entries = _getpwall() entriesbyname = {} entriesbyuid = {} @@ -57,12 +66,13 @@ class PwdTest(unittest.TestCase): self.assertRaises(TypeError, pwd.getpwuid, 3.14) self.assertRaises(TypeError, pwd.getpwnam) self.assertRaises(TypeError, pwd.getpwnam, 42) - self.assertRaises(TypeError, pwd.getpwall, 42) + if hasattr(pwd, 'getpwall'): + self.assertRaises(TypeError, pwd.getpwall, 42) # try to get some errors bynames = {} byuids = {} - for (n, p, u, g, gecos, d, s) in pwd.getpwall(): + for (n, p, u, g, gecos, d, s) in _getpwall(): bynames[n] = u byuids[u] = n @@ -96,13 +106,17 @@ class PwdTest(unittest.TestCase): # loop, say), pwd.getpwuid() might still be able to find data for that # uid. Using sys.maxint may provoke the same problems, but hopefully # it will be a more repeatable failure. + # Android accepts a very large span of uids including sys.maxsize and + # -1; it raises KeyError with 1 or 2 for example. fakeuid = sys.maxsize self.assertNotIn(fakeuid, byuids) - self.assertRaises(KeyError, pwd.getpwuid, fakeuid) + if not support.is_android: + self.assertRaises(KeyError, pwd.getpwuid, fakeuid) # -1 shouldn't be a valid uid because it has a special meaning in many # uid-related functions - self.assertRaises(KeyError, pwd.getpwuid, -1) + if not support.is_android: + self.assertRaises(KeyError, pwd.getpwuid, -1) # should be out of uid_t range self.assertRaises(KeyError, pwd.getpwuid, 2**128) self.assertRaises(KeyError, pwd.getpwuid, -2**128) -- cgit v1.2.1 From eab15ffef9e7b28b1274aa48f0175efb43547d84 Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Tue, 13 Dec 2016 10:00:01 +0100 Subject: Issue #28759: Fix the tests that fail with PermissionError when run as a non-root user on Android where access rights are controled by SELinux MAC. --- Lib/test/eintrdata/eintr_tester.py | 2 ++ Lib/test/support/__init__.py | 3 ++- Lib/test/test_genericpath.py | 3 +++ Lib/test/test_pathlib.py | 2 ++ Lib/test/test_posix.py | 6 ++++++ Lib/test/test_shutil.py | 6 +++++- Lib/test/test_stat.py | 3 ++- 7 files changed, 22 insertions(+), 3 deletions(-) (limited to 'Lib') diff --git a/Lib/test/eintrdata/eintr_tester.py b/Lib/test/eintrdata/eintr_tester.py index 9fbe04de9c..d194e775ea 100644 --- a/Lib/test/eintrdata/eintr_tester.py +++ b/Lib/test/eintrdata/eintr_tester.py @@ -20,6 +20,7 @@ import time import unittest from test import support +android_not_root = support.android_not_root @contextlib.contextmanager def kill_on_error(proc): @@ -311,6 +312,7 @@ class SocketEINTRTest(EINTRBaseTest): # https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=203162 @support.requires_freebsd_version(10, 3) @unittest.skipUnless(hasattr(os, 'mkfifo'), 'needs mkfifo()') + @unittest.skipIf(android_not_root, "mkfifo not allowed, non root user") def _test_open(self, do_open_close_reader, do_open_close_writer): filename = support.TESTFN diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 1a38c4ab32..ed1af2b5a5 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -93,7 +93,7 @@ __all__ = [ "check__all__", "requires_android_level", "requires_multiprocessing_queue", # sys "is_jython", "is_android", "check_impl_detail", "unix_shell", - "setswitchinterval", + "setswitchinterval", "android_not_root", # network "HOST", "IPV6_ENABLED", "find_unused_port", "bind_port", "open_urlresource", # processes @@ -769,6 +769,7 @@ is_jython = sys.platform.startswith('java') _ANDROID_API_LEVEL = sysconfig.get_config_var('ANDROID_API_LEVEL') is_android = (_ANDROID_API_LEVEL is not None and _ANDROID_API_LEVEL > 0) +android_not_root = (is_android and os.geteuid() != 0) if sys.platform != 'win32': unix_shell = '/system/bin/sh' if is_android else '/bin/sh' diff --git a/Lib/test/test_genericpath.py b/Lib/test/test_genericpath.py index ae5dd6a5f7..f698e13f68 100644 --- a/Lib/test/test_genericpath.py +++ b/Lib/test/test_genericpath.py @@ -8,6 +8,7 @@ import sys import unittest import warnings from test import support +android_not_root = support.android_not_root def create_file(filename, data=b'foo'): @@ -212,6 +213,7 @@ class GenericTest: def test_samefile_on_symlink(self): self._test_samefile_on_link_func(os.symlink) + @unittest.skipIf(android_not_root, "hard links not allowed, non root user") def test_samefile_on_link(self): self._test_samefile_on_link_func(os.link) @@ -251,6 +253,7 @@ class GenericTest: def test_samestat_on_symlink(self): self._test_samestat_on_link_func(os.symlink) + @unittest.skipIf(android_not_root, "hard links not allowed, non root user") def test_samestat_on_link(self): self._test_samestat_on_link_func(os.link) diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index d25b1336f6..65b2d5abda 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -10,6 +10,7 @@ import tempfile import unittest from test import support +android_not_root = support.android_not_root TESTFN = support.TESTFN try: @@ -1864,6 +1865,7 @@ class _BasePathTest(object): self.assertFalse((P / 'fileA' / 'bah').is_fifo()) @unittest.skipUnless(hasattr(os, "mkfifo"), "os.mkfifo() required") + @unittest.skipIf(android_not_root, "mkfifo not allowed, non root user") def test_is_fifo_true(self): P = self.cls(BASE, 'myfifo') os.mkfifo(str(P)) diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index 63c74cd80d..029d0815e9 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -1,6 +1,7 @@ "Test posix functions" from test import support +android_not_root = support.android_not_root # Skip these tests if there is no posix module. posix = support.import_module('posix') @@ -422,6 +423,7 @@ class PosixTester(unittest.TestCase): posix.stat, list(os.fsencode(support.TESTFN))) @unittest.skipUnless(hasattr(posix, 'mkfifo'), "don't have mkfifo()") + @unittest.skipIf(android_not_root, "mkfifo not allowed, non root user") def test_mkfifo(self): support.unlink(support.TESTFN) posix.mkfifo(support.TESTFN, stat.S_IRUSR | stat.S_IWUSR) @@ -429,6 +431,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless(hasattr(posix, 'mknod') and hasattr(stat, 'S_IFIFO'), "don't have mknod()/S_IFIFO") + @unittest.skipIf(android_not_root, "mknod not allowed, non root user") def test_mknod(self): # Test using mknod() to create a FIFO (the only use specified # by POSIX). @@ -907,6 +910,7 @@ class PosixTester(unittest.TestCase): posix.close(f) @unittest.skipUnless(os.link in os.supports_dir_fd, "test needs dir_fd support in os.link()") + @unittest.skipIf(android_not_root, "hard link not allowed, non root user") def test_link_dir_fd(self): f = posix.open(posix.getcwd(), posix.O_RDONLY) try: @@ -930,6 +934,7 @@ class PosixTester(unittest.TestCase): @unittest.skipUnless((os.mknod in os.supports_dir_fd) and hasattr(stat, 'S_IFIFO'), "test requires both stat.S_IFIFO and dir_fd support for os.mknod()") + @unittest.skipIf(android_not_root, "mknod not allowed, non root user") def test_mknod_dir_fd(self): # Test using mknodat() to create a FIFO (the only use specified # by POSIX). @@ -1013,6 +1018,7 @@ class PosixTester(unittest.TestCase): posix.close(f) @unittest.skipUnless(os.mkfifo in os.supports_dir_fd, "test needs dir_fd support in os.mkfifo()") + @unittest.skipIf(android_not_root, "mkfifo not allowed, non root user") def test_mkfifo_dir_fd(self): support.unlink(support.TESTFN) f = posix.open(posix.getcwd(), posix.O_RDONLY) diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index af5f00fdf0..46e2c57746 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -22,7 +22,8 @@ import tarfile import warnings from test import support -from test.support import TESTFN, check_warnings, captured_stdout, requires_zlib +from test.support import (TESTFN, check_warnings, captured_stdout, + requires_zlib, android_not_root) try: import bz2 @@ -787,6 +788,7 @@ class TestShutil(unittest.TestCase): @unittest.skipIf(os.name == 'nt', 'temporarily disabled on Windows') @unittest.skipUnless(hasattr(os, 'link'), 'requires os.link') + @unittest.skipIf(android_not_root, "hard links not allowed, non root user") def test_dont_copy_file_onto_link_to_itself(self): # bug 851123. os.mkdir(TESTFN) @@ -839,6 +841,7 @@ class TestShutil(unittest.TestCase): # Issue #3002: copyfile and copytree block indefinitely on named pipes @unittest.skipUnless(hasattr(os, "mkfifo"), 'requires os.mkfifo()') + @unittest.skipIf(android_not_root, "mkfifo not allowed, non root user") def test_copyfile_named_pipe(self): os.mkfifo(TESTFN) try: @@ -849,6 +852,7 @@ class TestShutil(unittest.TestCase): finally: os.remove(TESTFN) + @unittest.skipIf(android_not_root, "mkfifo not allowed, non root user") @unittest.skipUnless(hasattr(os, "mkfifo"), 'requires os.mkfifo()') @support.skip_unless_symlink def test_copytree_named_pipe(self): diff --git a/Lib/test/test_stat.py b/Lib/test/test_stat.py index f1a5938a39..cd02a6ee37 100644 --- a/Lib/test/test_stat.py +++ b/Lib/test/test_stat.py @@ -1,7 +1,7 @@ import unittest import os import sys -from test.support import TESTFN, import_fresh_module +from test.support import TESTFN, import_fresh_module, android_not_root c_stat = import_fresh_module('stat', fresh=['_stat']) py_stat = import_fresh_module('stat', blocked=['_stat']) @@ -168,6 +168,7 @@ class TestFilemode: self.assertS_IS("LNK", st_mode) @unittest.skipUnless(hasattr(os, 'mkfifo'), 'os.mkfifo not available') + @unittest.skipIf(android_not_root, "mkfifo not allowed, non root user") def test_fifo(self): os.mkfifo(TESTFN, 0o700) st_mode, modestr = self.get_mode() -- cgit v1.2.1 From bb2e2b4d5fbea49cdd0f9fe842272a4ae8626092 Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Wed, 14 Dec 2016 11:52:28 +0100 Subject: Issue #28683: Fix the tests that bind() a unix socket and raise PermissionError on Android for a non-root user. --- Lib/test/support/__init__.py | 10 ++++++++++ Lib/test/test_asyncore.py | 4 +++- Lib/test/test_pathlib.py | 3 ++- Lib/test/test_socket.py | 21 +++++++++++++++------ 4 files changed, 30 insertions(+), 8 deletions(-) (limited to 'Lib') diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index ed1af2b5a5..15d8fc849b 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -96,6 +96,7 @@ __all__ = [ "setswitchinterval", "android_not_root", # network "HOST", "IPV6_ENABLED", "find_unused_port", "bind_port", "open_urlresource", + "bind_unix_socket", # processes 'temp_umask', "reap_children", # logging @@ -708,6 +709,15 @@ def bind_port(sock, host=HOST): port = sock.getsockname()[1] return port +def bind_unix_socket(sock, addr): + """Bind a unix socket, raising SkipTest if PermissionError is raised.""" + assert sock.family == socket.AF_UNIX + try: + sock.bind(addr) + except PermissionError: + sock.close() + raise unittest.SkipTest('cannot bind AF_UNIX sockets') + def _is_ipv6_enabled(): """Check whether IPv6 is enabled on this host.""" if socket.has_ipv6: diff --git a/Lib/test/test_asyncore.py b/Lib/test/test_asyncore.py index dbee593c54..51c65737a4 100644 --- a/Lib/test/test_asyncore.py +++ b/Lib/test/test_asyncore.py @@ -95,7 +95,9 @@ def bind_af_aware(sock, addr): if HAS_UNIX_SOCKETS and sock.family == socket.AF_UNIX: # Make sure the path doesn't exist. support.unlink(addr) - sock.bind(addr) + support.bind_unix_socket(sock, addr) + else: + sock.bind(addr) class HelperFunctionTests(unittest.TestCase): diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py index 65b2d5abda..ce1ca153ed 100644 --- a/Lib/test/test_pathlib.py +++ b/Lib/test/test_pathlib.py @@ -1888,7 +1888,8 @@ class _BasePathTest(object): try: sock.bind(str(P)) except OSError as e: - if "AF_UNIX path too long" in str(e): + if (isinstance(e, PermissionError) or + "AF_UNIX path too long" in str(e)): self.skipTest("cannot bind Unix socket: " + str(e)) self.assertTrue(P.is_socket()) self.assertFalse(P.is_fifo()) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 59564c9063..6b29e18234 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -278,8 +278,14 @@ class ThreadableTest: def clientRun(self, test_func): self.server_ready.wait() - self.clientSetUp() - self.client_ready.set() + try: + self.clientSetUp() + except BaseException as e: + self.queue.put(e) + self.clientTearDown() + return + finally: + self.client_ready.set() if self.server_crashed: self.clientTearDown() return @@ -520,8 +526,11 @@ class ConnectedStreamTestMixin(SocketListeningTestMixin, self.serv_conn = self.cli def clientTearDown(self): - self.serv_conn.close() - self.serv_conn = None + try: + self.serv_conn.close() + self.serv_conn = None + except AttributeError: + pass super().clientTearDown() @@ -540,7 +549,7 @@ class UnixSocketTestBase(SocketTestBase): def bindSock(self, sock): path = tempfile.mktemp(dir=self.dir_path) - sock.bind(path) + support.bind_unix_socket(sock, path) self.addCleanup(support.unlink, path) class UnixStreamBase(UnixSocketTestBase): @@ -4631,7 +4640,7 @@ class TestUnixDomain(unittest.TestCase): def bind(self, sock, path): # Bind the socket try: - sock.bind(path) + support.bind_unix_socket(sock, path) except OSError as e: if str(e) == "AF_UNIX path too long": self.skipTest( -- cgit v1.2.1 From 781a86eb961eb5c62397d210d4ea7301c5476e7c Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Wed, 14 Dec 2016 20:37:10 +0100 Subject: Issue #28849: Skip test_sysconfig.test_triplet_in_ext_suffix on non linux platforms. --- Lib/test/test_sysconfig.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py index 747b2e5815..355bc614da 100644 --- a/Lib/test/test_sysconfig.py +++ b/Lib/test/test_sysconfig.py @@ -385,7 +385,8 @@ class TestSysConfig(unittest.TestCase): self.assertIsNotNone(vars['SO']) self.assertEqual(vars['SO'], vars['EXT_SUFFIX']) - @unittest.skipUnless(hasattr(sys.implementation, '_multiarch'), + @unittest.skipUnless(sys.platform == 'linux' and + hasattr(sys.implementation, '_multiarch'), 'multiarch-specific test') def test_triplet_in_ext_suffix(self): import ctypes, platform, re -- cgit v1.2.1 From 88be25781a6930a8ef45fb23eb1f2d4025f29799 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 15 Dec 2016 17:21:23 +0100 Subject: Fix a memory leak in split-table dictionaries Issue #28147: Fix a memory leak in split-table dictionaries: setattr() must not convert combined table into split table. Patch written by INADA Naoki. --- Lib/test/test_dict.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py index cd077ff0e0..832bb9c8e2 100644 --- a/Lib/test/test_dict.py +++ b/Lib/test/test_dict.py @@ -933,6 +933,36 @@ class DictTest(unittest.TestCase): self.assertEqual(list(a), ['x', 'y']) self.assertEqual(list(b), ['x', 'y', 'z']) + @support.cpython_only + def test_splittable_setattr_after_pop(self): + """setattr() must not convert combined table into split table.""" + # Issue 28147 + import _testcapi + + class C: + pass + a = C() + + a.a = 1 + self.assertTrue(_testcapi.dict_hassplittable(a.__dict__)) + + # dict.pop() convert it to combined table + a.__dict__.pop('a') + self.assertFalse(_testcapi.dict_hassplittable(a.__dict__)) + + # But C should not convert a.__dict__ to split table again. + a.a = 1 + self.assertFalse(_testcapi.dict_hassplittable(a.__dict__)) + + # Same for popitem() + a = C() + a.a = 2 + self.assertTrue(_testcapi.dict_hassplittable(a.__dict__)) + a.__dict__.popitem() + self.assertFalse(_testcapi.dict_hassplittable(a.__dict__)) + a.a = 3 + self.assertFalse(_testcapi.dict_hassplittable(a.__dict__)) + def test_iterator_pickling(self): for proto in range(pickle.HIGHEST_PROTOCOL + 1): data = {1:"a", 2:"b", 3:"c"} -- cgit v1.2.1 From 7ce4d17c0d9aa59119a8a9b730be0cfd2c593ef7 Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Thu, 15 Dec 2016 20:59:58 +0100 Subject: Issue #26919: On Android, operating system data is now always encoded/decoded to/from UTF-8, instead of the locale encoding to avoid inconsistencies with os.fsencode() and os.fsdecode() which are already using UTF-8. --- Lib/test/test_cmd_line.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py index b71bb9f7ee..ae2bcd4375 100644 --- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py @@ -8,7 +8,7 @@ import shutil import sys import subprocess import tempfile -from test.support import script_helper +from test.support import script_helper, is_android from test.support.script_helper import (spawn_python, kill_python, assert_python_ok, assert_python_failure) @@ -178,15 +178,16 @@ class CmdLineTest(unittest.TestCase): if not stdout.startswith(pattern): raise AssertionError("%a doesn't start with %a" % (stdout, pattern)) - @unittest.skipUnless(sys.platform == 'darwin', 'test specific to Mac OS X') - def test_osx_utf8(self): + @unittest.skipUnless((sys.platform == 'darwin' or + is_android), 'test specific to Mac OS X and Android') + def test_osx_android_utf8(self): def check_output(text): decoded = text.decode('utf-8', 'surrogateescape') expected = ascii(decoded).encode('ascii') + b'\n' env = os.environ.copy() # C locale gives ASCII locale encoding, but Python uses UTF-8 - # to parse the command line arguments on Mac OS X + # to parse the command line arguments on Mac OS X and Android. env['LC_ALL'] = 'C' p = subprocess.Popen( -- cgit v1.2.1 From 8c537f03dd3a32b887541168252d7cd8e13154a1 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Sat, 17 Dec 2016 13:30:27 -0800 Subject: Issue #25778: winreg does not truncase string correctly (Patch by Eryk Sun) --- Lib/test/test_winreg.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/test/test_winreg.py b/Lib/test/test_winreg.py index d642b13f68..2be61ae15d 100644 --- a/Lib/test/test_winreg.py +++ b/Lib/test/test_winreg.py @@ -57,7 +57,7 @@ class BaseWinregTests(unittest.TestCase): def delete_tree(self, root, subkey): try: - hkey = OpenKey(root, subkey, KEY_ALL_ACCESS) + hkey = OpenKey(root, subkey, 0, KEY_ALL_ACCESS) except OSError: # subkey does not exist return @@ -368,6 +368,18 @@ class LocalWinregTests(BaseWinregTests): finally: DeleteKey(HKEY_CURRENT_USER, test_key_name) + def test_read_string_containing_null(self): + # Test for issue 25778: REG_SZ should not contain null characters + try: + with CreateKey(HKEY_CURRENT_USER, test_key_name) as ck: + self.assertNotEqual(ck.handle, 0) + test_val = "A string\x00 with a null" + SetValueEx(ck, "test_name", 0, REG_SZ, test_val) + ret_val, ret_type = QueryValueEx(ck, "test_name") + self.assertEqual(ret_type, REG_SZ) + self.assertEqual(ret_val, "A string") + finally: + DeleteKey(HKEY_CURRENT_USER, test_key_name) @unittest.skipUnless(REMOTE_NAME, "Skipping remote registry tests") -- cgit v1.2.1 From 6683ebae061d174b61db78bd2f40ba6c153eb8eb Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Mon, 19 Dec 2016 10:46:14 +0100 Subject: Issue #28996: Skip two tests that fail on Android with the locale strcoll() and strxfrm() functions. --- Lib/test/test_locale.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_locale.py b/Lib/test/test_locale.py index fae2c3dabb..99fab58cb9 100644 --- a/Lib/test/test_locale.py +++ b/Lib/test/test_locale.py @@ -1,4 +1,4 @@ -from test.support import verbose +from test.support import verbose, is_android import unittest import locale import sys @@ -353,7 +353,7 @@ class TestEnUSCollation(BaseLocalizedTest, TestCollation): enc = codecs.lookup(locale.getpreferredencoding(False) or 'ascii').name if enc not in ('utf-8', 'iso8859-1', 'cp1252'): raise unittest.SkipTest('encoding not suitable') - if enc != 'iso8859-1' and (sys.platform == 'darwin' or + if enc != 'iso8859-1' and (sys.platform == 'darwin' or is_android or sys.platform.startswith('freebsd')): raise unittest.SkipTest('wcscoll/wcsxfrm have known bugs') BaseLocalizedTest.setUp(self) -- cgit v1.2.1 From a068c1896da0c200742c2a192071365aca6b97d1 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Tue, 27 Dec 2016 00:05:26 -0500 Subject: Issue #29071: IDLE now colors f-string prefixes (but not invalid ur prefixes). --- Lib/idlelib/colorizer.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/colorizer.py b/Lib/idlelib/colorizer.py index 7310bb2cc8..7a919f164f 100644 --- a/Lib/idlelib/colorizer.py +++ b/Lib/idlelib/colorizer.py @@ -21,7 +21,8 @@ def make_pat(): # 1st 'file' colorized normal, 2nd as builtin, 3rd as string builtin = r"([^.'\"\\#]\b|^)" + any("BUILTIN", builtinlist) + r"\b" comment = any("COMMENT", [r"#[^\n]*"]) - stringprefix = r"(\br|u|ur|R|U|UR|Ur|uR|b|B|br|Br|bR|BR|rb|rB|Rb|RB)?" + stringprefix = (r"(\br|R|u|U|f|F|fr|Fr|fR|FR|rf|rF|Rf|RF" + "|b|B|br|Br|bR|BR|rb|rB|Rb|RB)?") sqstring = stringprefix + r"'[^'\\\n]*(\\.[^'\\\n]*)*'?" dqstring = stringprefix + r'"[^"\\\n]*(\\.[^"\\\n]*)*"?' sq3string = stringprefix + r"'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?" @@ -261,8 +262,14 @@ def _color_delegator(parent): # htest # top = Toplevel(parent) top.title("Test ColorDelegator") x, y = map(int, parent.geometry().split('+')[1:]) - top.geometry("200x100+%d+%d" % (x + 250, y + 175)) - source = "if somename: x = 'abc' # comment\nprint\n" + top.geometry("600x200+%d+%d" % (x + 100, y + 175)) + source = ("# Following has syntax errors\n" + "if True: then int 1\nelif False: print 0\nelse: float(None)\n" + "#unicode and byte strings, valid prefixes should be colored\n" + "'x', '''x''', \"x\", \"\"\"x\"\"\"\n" + "r'x', u'x', R'x', U'x', f'x', F'x', ur'is invalid'\n" + "fr'x', Fr'x', fR'x', FR'x', rf'x', rF'x', Rf'x', RF'x'\n" + "b'x',B'x', br'x',Br'x',bR'x',BR'x', rb'x'.rB'x',Rb'x',RB'x'\n") text = Text(top, background="white") text.pack(expand=1, fill="both") text.insert("insert", source) -- cgit v1.2.1 From 25cdae93f2ad79ad8a3dfd400835fe1fe10b9a47 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 27 Dec 2016 01:06:52 -0800 Subject: Issue #29055: Suppress upstream exception for random.choice() --- Lib/random.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/random.py b/Lib/random.py index 49b0f149a5..ad1c9167b0 100644 --- a/Lib/random.py +++ b/Lib/random.py @@ -254,7 +254,7 @@ class Random(_random.Random): try: i = self._randbelow(len(seq)) except ValueError: - raise IndexError('Cannot choose from an empty sequence') + raise IndexError('Cannot choose from an empty sequence') from None return seq[i] def shuffle(self, x, random=None): -- cgit v1.2.1 From 9cc01c067740c1f831c405db033632efd320c24d Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Wed, 28 Dec 2016 16:02:59 -0800 Subject: Issue #29079: Prevent infinite loop in pathlib.resolve() on Windows --- Lib/pathlib.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 69653938ef..0484dacd79 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -192,7 +192,9 @@ class _WindowsFlavour(_Flavour): s = self._ext_to_normal(_getfinalpathname(s)) except FileNotFoundError: previous_s = s - s = os.path.abspath(os.path.join(s, os.pardir)) + s = os.path.dirname(s) + if previous_s == s: + return path else: if previous_s is None: return s -- cgit v1.2.1 From f1ea52dc25720b914dd57910df22ac3a35a5ff00 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Thu, 29 Dec 2016 22:54:25 -0700 Subject: Issue #29061: secrets.randbelow() would hang with a negative input --- Lib/secrets.py | 2 ++ Lib/test/test_secrets.py | 1 + 2 files changed, 3 insertions(+) (limited to 'Lib') diff --git a/Lib/secrets.py b/Lib/secrets.py index 27fa4503d7..130434229e 100644 --- a/Lib/secrets.py +++ b/Lib/secrets.py @@ -26,6 +26,8 @@ choice = _sysrand.choice def randbelow(exclusive_upper_bound): """Return a random int in the range [0, n).""" + if exclusive_upper_bound <= 0: + raise ValueError("Upper bound must be positive.") return _sysrand._randbelow(exclusive_upper_bound) DEFAULT_ENTROPY = 32 # number of bytes to return by default diff --git a/Lib/test/test_secrets.py b/Lib/test/test_secrets.py index 4c65cf00cd..d31d07e01f 100644 --- a/Lib/test/test_secrets.py +++ b/Lib/test/test_secrets.py @@ -70,6 +70,7 @@ class Random_Tests(unittest.TestCase): for i in range(2, 10): self.assertIn(secrets.randbelow(i), range(i)) self.assertRaises(ValueError, secrets.randbelow, 0) + self.assertRaises(ValueError, secrets.randbelow, -1) class Token_Tests(unittest.TestCase): -- cgit v1.2.1 From 68166f2323eaada70006676fd5ef3bf969290c5d Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Thu, 29 Dec 2016 23:57:12 -0700 Subject: Issue #29103: Remove unused import. Noticed by Jean-Sebastien Bevilacqua. --- Lib/enum.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/enum.py b/Lib/enum.py index 3f5ecbb5ce..e79b0382eb 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -1,7 +1,7 @@ import sys from types import MappingProxyType, DynamicClassAttribute from functools import reduce -from operator import or_ as _or_, and_ as _and_, xor, neg +from operator import or_ as _or_ # try _collections first to reduce startup cost try: -- cgit v1.2.1 From 440e0cf88bde8bdd14bcd38aac8b5724b8d6f39b Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Sun, 1 Jan 2017 21:21:39 -0500 Subject: Issue #29071: Use local flags for IDLE colorizer string prefix matcher. Revised re by Serhiy Storchaka. --- Lib/idlelib/colorizer.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/colorizer.py b/Lib/idlelib/colorizer.py index 7a919f164f..ff40845288 100644 --- a/Lib/idlelib/colorizer.py +++ b/Lib/idlelib/colorizer.py @@ -21,8 +21,7 @@ def make_pat(): # 1st 'file' colorized normal, 2nd as builtin, 3rd as string builtin = r"([^.'\"\\#]\b|^)" + any("BUILTIN", builtinlist) + r"\b" comment = any("COMMENT", [r"#[^\n]*"]) - stringprefix = (r"(\br|R|u|U|f|F|fr|Fr|fR|FR|rf|rF|Rf|RF" - "|b|B|br|Br|bR|BR|rb|rB|Rb|RB)?") + stringprefix = r"(?i:\br|u|f|fr|rf|b|br|rb)?" sqstring = stringprefix + r"'[^'\\\n]*(\\.[^'\\\n]*)*'?" dqstring = stringprefix + r'"[^"\\\n]*(\\.[^"\\\n]*)*"?' sq3string = stringprefix + r"'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?" @@ -262,10 +261,11 @@ def _color_delegator(parent): # htest # top = Toplevel(parent) top.title("Test ColorDelegator") x, y = map(int, parent.geometry().split('+')[1:]) - top.geometry("600x200+%d+%d" % (x + 100, y + 175)) + top.geometry("700x250+%d+%d" % (x + 20, y + 175)) source = ("# Following has syntax errors\n" "if True: then int 1\nelif False: print 0\nelse: float(None)\n" - "#unicode and byte strings, valid prefixes should be colored\n" + "if iF + If + IF: 'keywork matching must respect case'\n" + "# All valid prefixes for unicode and byte strings should be colored\n" "'x', '''x''', \"x\", \"\"\"x\"\"\"\n" "r'x', u'x', R'x', U'x', f'x', F'x', ur'is invalid'\n" "fr'x', Fr'x', fR'x', FR'x', rf'x', rF'x', Rf'x', RF'x'\n" -- cgit v1.2.1 From 7a71df4bf8d7267d036b7ff69f69bf815d7508bf Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 4 Jan 2017 12:01:16 +0100 Subject: Issue #24773: fix datetime.time constructor docstring The default value of fold is zero, not True. Fix the docstring of the Python implementation. --- Lib/datetime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Lib') diff --git a/Lib/datetime.py b/Lib/datetime.py index 7540109684..5d5579c1c6 100644 --- a/Lib/datetime.py +++ b/Lib/datetime.py @@ -1053,7 +1053,7 @@ class time: hour, minute (required) second, microsecond (default to zero) tzinfo (default to None) - fold (keyword only, default to True) + fold (keyword only, default to zero) """ if isinstance(hour, bytes) and len(hour) == 6 and hour[0]&0x7F < 24: # Pickle support -- cgit v1.2.1 From 8411ac5205248b58f337f921ad26c9b5445f5a6f Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Wed, 4 Jan 2017 23:17:47 -0500 Subject: Issue #29162: Don't depend on 'from tkinter import *' importing sys. Fix error in format string. --- Lib/idlelib/pyshell.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Lib') diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py index e1eade1eea..f3ee391ca0 100755 --- a/Lib/idlelib/pyshell.py +++ b/Lib/idlelib/pyshell.py @@ -5,15 +5,15 @@ try: except ImportError: print("** IDLE can't import Tkinter.\n" "Your Python may not be configured for Tk. **", file=sys.__stderr__) - sys.exit(1) + raise SystemExit(1) import tkinter.messagebox as tkMessageBox if TkVersion < 8.5: root = Tk() # otherwise create root in main root.withdraw() tkMessageBox.showerror("Idle Cannot Start", - "Idle requires tcl/tk 8.5+, not $s." % TkVersion, + "Idle requires tcl/tk 8.5+, not %s." % TkVersion, parent=root) - sys.exit(1) + raise SystemExit(1) from code import InteractiveInterpreter import getopt -- cgit v1.2.1 From 4a409ba347a7454d73db9802ab31f7fe17e5e6bc Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Fri, 6 Jan 2017 17:32:01 +0900 Subject: Issue #29159: Fix regression in bytes(x) when x.__index__() raises Exception. --- Lib/test/test_bytes.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index b396a76a40..a103a7d39c 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -4,6 +4,7 @@ XXX This is a mess. Common tests should be unified with string_tests.py (and the latter should be modernized). """ +import array import os import re import sys @@ -81,6 +82,18 @@ class BaseBytesTest: self.assertRaises(ValueError, self.type2test, [Indexable(-1)]) self.assertRaises(ValueError, self.type2test, [Indexable(256)]) + def test_from_buffer(self): + a = self.type2test(array.array('B', [1, 2, 3])) + self.assertEqual(a, b"\x01\x02\x03") + + # http://bugs.python.org/issue29159 + # Fallback when __index__ raises exception other than OverflowError + class B(bytes): + def __index__(self): + raise TypeError + + self.assertEqual(self.type2test(B(b"foobar")), b"foobar") + def test_from_ssize(self): self.assertEqual(self.type2test(0), b'') self.assertEqual(self.type2test(1), b'\x00') -- cgit v1.2.1 From 3a2730f9bd5713020e5e3b1d60a71b048728af0a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 6 Jan 2017 10:44:44 +0100 Subject: Fix subprocess.Popen.__del__() fox Python shutdown Issue #29174, #26741: subprocess.Popen.__del__() now keeps a strong reference to warnings.warn() function. --- Lib/subprocess.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Lib') diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 0b880f68d9..13b9d44d31 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -750,15 +750,15 @@ class Popen(object): # Wait for the process to terminate, to avoid zombies. self.wait() - def __del__(self, _maxsize=sys.maxsize): + def __del__(self, _maxsize=sys.maxsize, _warn=warnings.warn): if not self._child_created: # We didn't get to successfully create a child process. return if self.returncode is None: # Not reading subprocess exit status creates a zombi process which # is only destroyed at the parent python process exit - warnings.warn("subprocess %s is still running" % self.pid, - ResourceWarning, source=self) + _warn("subprocess %s is still running" % self.pid, + ResourceWarning, source=self) # In case the child hasn't been waited on, check if it's done. self._internal_poll(_deadstate=_maxsize) if self.returncode is None and _active is not None: -- cgit v1.2.1 From 4b5a4553c7513a3de9891cf3634fcc1dcd8f1175 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 6 Jan 2017 18:15:51 +0100 Subject: Fix unittest.mock._Call: don't ignore name Issue #28961: Fix unittest.mock._Call helper: don't ignore the name parameter anymore. Patch written by Jiajun Huang. --- Lib/unittest/mock.py | 3 +-- Lib/unittest/test/testmock/testhelpers.py | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) (limited to 'Lib') diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index f134919888..dcac5a2925 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -1961,9 +1961,8 @@ class _Call(tuple): If the _Call has no name then it will match any name. """ - def __new__(cls, value=(), name=None, parent=None, two=False, + def __new__(cls, value=(), name='', parent=None, two=False, from_kall=True): - name = '' args = () kwargs = {} _len = len(value) diff --git a/Lib/unittest/test/testmock/testhelpers.py b/Lib/unittest/test/testmock/testhelpers.py index 34776347da..d5f9e7c1d6 100644 --- a/Lib/unittest/test/testmock/testhelpers.py +++ b/Lib/unittest/test/testmock/testhelpers.py @@ -306,6 +306,20 @@ class CallTest(unittest.TestCase): other_args = _Call(((1, 2), {'a': 3})) self.assertEqual(args, other_args) + def test_call_with_name(self): + self.assertEqual( + 'foo', + _Call((), 'foo')[0], + ) + self.assertEqual( + '', + _Call((('bar', 'barz'), ), )[0] + ) + self.assertEqual( + '', + _Call((('bar', 'barz'), {'hello': 'world'}), )[0] + ) + class SpecSignatureTest(unittest.TestCase): -- cgit v1.2.1 From bf113833d23a3bc171c1fecd28dd85b14b7be85e Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sun, 8 Jan 2017 00:37:13 -0800 Subject: Add OrderedDict test for PEP 468 (guaranteed ordered of kwargs) --- Lib/test/test_ordered_dict.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'Lib') diff --git a/Lib/test/test_ordered_dict.py b/Lib/test/test_ordered_dict.py index a35ed12436..93f812a530 100644 --- a/Lib/test/test_ordered_dict.py +++ b/Lib/test/test_ordered_dict.py @@ -52,6 +52,14 @@ class OrderedDictTests: self.assertEqual(list(d.items()), [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6), ('g', 7)]) + def test_468(self): + OrderedDict = self.OrderedDict + items = [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6), ('g', 7)] + shuffle(items) + argdict = OrderedDict(items) + d = OrderedDict(**argdict) + self.assertEqual(list(d.items()), items) + def test_update(self): OrderedDict = self.OrderedDict with self.assertRaises(TypeError): -- cgit v1.2.1 From 01a053991ffed3ca4a8cb22838b36782df70629f Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sun, 8 Jan 2017 17:28:20 -0800 Subject: Issue #29203: functools.lru_cache() now respects PEP 468 --- Lib/functools.py | 7 +++---- Lib/test/test_functools.py | 10 ++++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) (limited to 'Lib') diff --git a/Lib/functools.py b/Lib/functools.py index 45e5f87ede..030c91b057 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -421,7 +421,7 @@ class _HashedSeq(list): def _make_key(args, kwds, typed, kwd_mark = (object(),), fasttypes = {int, str, frozenset, type(None)}, - sorted=sorted, tuple=tuple, type=type, len=len): + tuple=tuple, type=type, len=len): """Make a cache key from optionally typed positional and keyword arguments The key is constructed in a way that is flat as possible rather than @@ -434,14 +434,13 @@ def _make_key(args, kwds, typed, """ key = args if kwds: - sorted_items = sorted(kwds.items()) key += kwd_mark - for item in sorted_items: + for item in kwds.items(): key += item if typed: key += tuple(type(v) for v in args) if kwds: - key += tuple(type(v) for k, v in sorted_items) + key += tuple(type(v) for v in kwds.values()) elif len(key) == 1 and type(key[0]) in fasttypes: return key[0] return _HashedSeq(key) diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index 3a40861594..27eb57685a 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -1306,6 +1306,16 @@ class TestLRU: self.assertEqual(fib.cache_info(), self.module._CacheInfo(hits=0, misses=0, maxsize=None, currsize=0)) + def test_kwargs_order(self): + # PEP 468: Preserving Keyword Argument Order + @self.module.lru_cache(maxsize=10) + def f(**kwargs): + return list(kwargs.items()) + self.assertEqual(f(a=1, b=2), [('a', 1), ('b', 2)]) + self.assertEqual(f(b=2, a=1), [('b', 2), ('a', 1)]) + self.assertEqual(f.cache_info(), + self.module._CacheInfo(hits=0, misses=2, maxsize=10, currsize=2)) + def test_lru_cache_decoration(self): def f(zomg: 'zomg_annotation'): """f doc string""" -- cgit v1.2.1 From da870a7007749ed0f18383e4db31d6dcf521953b Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sun, 8 Jan 2017 18:22:24 -0800 Subject: Sync-up with 3.7 by backporting minor lru_cache code beautification --- Lib/functools.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'Lib') diff --git a/Lib/functools.py b/Lib/functools.py index 030c91b057..89f2cf4f5f 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -492,6 +492,7 @@ def _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo): hits = misses = 0 full = False cache_get = cache.get # bound method to lookup a key or return None + cache_len = cache.__len__ # get cache size without calling len() lock = RLock() # because linkedlist updates aren't threadsafe root = [] # root of the circular doubly linked list root[:] = [root, root, None, None] # initialize by pointing to self @@ -573,16 +574,16 @@ def _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo): last = root[PREV] link = [last, root, key, result] last[NEXT] = root[PREV] = cache[key] = link - # Use the __len__() method instead of the len() function + # Use the cache_len bound method instead of the len() function # which could potentially be wrapped in an lru_cache itself. - full = (cache.__len__() >= maxsize) + full = (cache_len() >= maxsize) misses += 1 return result def cache_info(): """Report cache statistics""" with lock: - return _CacheInfo(hits, misses, maxsize, cache.__len__()) + return _CacheInfo(hits, misses, maxsize, cache_len()) def cache_clear(): """Clear the cache and cache statistics""" -- cgit v1.2.1 From 35e3dd49de9618fa26d2c26ecd8506c04ef633f5 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Mon, 9 Jan 2017 16:54:12 +0000 Subject: Fixes #29177: Improved resilience of logging tests which use socket servers. Thanks to Xavier de Gaye for the report and patch improvements. --- Lib/test/test_logging.py | 86 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 62 insertions(+), 24 deletions(-) (limited to 'Lib') diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 08cdd7f3eb..079f58482a 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -1,4 +1,4 @@ -# Copyright 2001-2016 by Vinay Sajip. All Rights Reserved. +# Copyright 2001-2017 by Vinay Sajip. All Rights Reserved. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose and without fee is hereby granted, @@ -16,7 +16,7 @@ """Test harness for the logging module. Run all tests. -Copyright (C) 2001-2016 Vinay Sajip. All Rights Reserved. +Copyright (C) 2001-2017 Vinay Sajip. All Rights Reserved. """ import logging @@ -1440,9 +1440,17 @@ class SocketHandlerTest(BaseTest): """Set up a TCP server to receive log messages, and a SocketHandler pointing to that server's address and port.""" BaseTest.setUp(self) - self.server = server = self.server_class(self.address, - self.handle_socket, 0.01) - server.start() + # Issue #29177: deal with errors that happen during setup + self.server = self.sock_hdlr = self.server_exception = None + try: + self.server = server = self.server_class(self.address, + self.handle_socket, 0.01) + server.start() + # Uncomment next line to test error recovery in setUp() + # raise OSError('dummy error raised') + except OSError as e: + self.server_exception = e + return server.ready.wait() hcls = logging.handlers.SocketHandler if isinstance(server.server_address, tuple): @@ -1457,9 +1465,11 @@ class SocketHandlerTest(BaseTest): def tearDown(self): """Shutdown the TCP server.""" try: - self.server.stop(2.0) - self.root_logger.removeHandler(self.sock_hdlr) - self.sock_hdlr.close() + if self.server: + self.server.stop(2.0) + if self.sock_hdlr: + self.root_logger.removeHandler(self.sock_hdlr) + self.sock_hdlr.close() finally: BaseTest.tearDown(self) @@ -1480,6 +1490,8 @@ class SocketHandlerTest(BaseTest): def test_output(self): # The log message sent to the SocketHandler is properly received. + if self.server_exception: + self.skipTest(self.server_exception) logger = logging.getLogger("tcp") logger.error("spam") self.handled.acquire() @@ -1488,6 +1500,8 @@ class SocketHandlerTest(BaseTest): self.assertEqual(self.log_output, "spam\neggs\n") def test_noserver(self): + if self.server_exception: + self.skipTest(self.server_exception) # Avoid timing-related failures due to SocketHandler's own hard-wired # one-second timeout on socket.create_connection() (issue #16264). self.sock_hdlr.retryStart = 2.5 @@ -1528,7 +1542,7 @@ class UnixSocketHandlerTest(SocketHandlerTest): def tearDown(self): SocketHandlerTest.tearDown(self) - os.remove(self.address) + support.unlink(self.address) @unittest.skipUnless(threading, 'Threading required for this test.') class DatagramHandlerTest(BaseTest): @@ -1543,9 +1557,17 @@ class DatagramHandlerTest(BaseTest): """Set up a UDP server to receive log messages, and a DatagramHandler pointing to that server's address and port.""" BaseTest.setUp(self) - self.server = server = self.server_class(self.address, - self.handle_datagram, 0.01) - server.start() + # Issue #29177: deal with errors that happen during setup + self.server = self.sock_hdlr = self.server_exception = None + try: + self.server = server = self.server_class(self.address, + self.handle_datagram, 0.01) + server.start() + # Uncomment next line to test error recovery in setUp() + # raise OSError('dummy error raised') + except OSError as e: + self.server_exception = e + return server.ready.wait() hcls = logging.handlers.DatagramHandler if isinstance(server.server_address, tuple): @@ -1560,9 +1582,11 @@ class DatagramHandlerTest(BaseTest): def tearDown(self): """Shutdown the UDP server.""" try: - self.server.stop(2.0) - self.root_logger.removeHandler(self.sock_hdlr) - self.sock_hdlr.close() + if self.server: + self.server.stop(2.0) + if self.sock_hdlr: + self.root_logger.removeHandler(self.sock_hdlr) + self.sock_hdlr.close() finally: BaseTest.tearDown(self) @@ -1576,6 +1600,8 @@ class DatagramHandlerTest(BaseTest): def test_output(self): # The log message sent to the DatagramHandler is properly received. + if self.server_exception: + self.skipTest(self.server_exception) logger = logging.getLogger("udp") logger.error("spam") self.handled.wait() @@ -1600,7 +1626,7 @@ class UnixDatagramHandlerTest(DatagramHandlerTest): def tearDown(self): DatagramHandlerTest.tearDown(self) - os.remove(self.address) + support.unlink(self.address) @unittest.skipUnless(threading, 'Threading required for this test.') class SysLogHandlerTest(BaseTest): @@ -1615,9 +1641,17 @@ class SysLogHandlerTest(BaseTest): """Set up a UDP server to receive log messages, and a SysLogHandler pointing to that server's address and port.""" BaseTest.setUp(self) - self.server = server = self.server_class(self.address, - self.handle_datagram, 0.01) - server.start() + # Issue #29177: deal with errors that happen during setup + self.server = self.sl_hdlr = self.server_exception = None + try: + self.server = server = self.server_class(self.address, + self.handle_datagram, 0.01) + server.start() + # Uncomment next line to test error recovery in setUp() + # raise OSError('dummy error raised') + except OSError as e: + self.server_exception = e + return server.ready.wait() hcls = logging.handlers.SysLogHandler if isinstance(server.server_address, tuple): @@ -1630,11 +1664,13 @@ class SysLogHandlerTest(BaseTest): self.handled = threading.Event() def tearDown(self): - """Shutdown the UDP server.""" + """Shutdown the server.""" try: - self.server.stop(2.0) - self.root_logger.removeHandler(self.sl_hdlr) - self.sl_hdlr.close() + if self.server: + self.server.stop(2.0) + if self.sl_hdlr: + self.root_logger.removeHandler(self.sl_hdlr) + self.sl_hdlr.close() finally: BaseTest.tearDown(self) @@ -1643,6 +1679,8 @@ class SysLogHandlerTest(BaseTest): self.handled.set() def test_output(self): + if self.server_exception: + self.skipTest(self.server_exception) # The log message sent to the SysLogHandler is properly received. logger = logging.getLogger("slh") logger.error("sp\xe4m") @@ -1675,7 +1713,7 @@ class UnixSysLogHandlerTest(SysLogHandlerTest): def tearDown(self): SysLogHandlerTest.tearDown(self) - os.remove(self.address) + support.unlink(self.address) @unittest.skipUnless(threading, 'Threading required for this test.') class HTTPHandlerTest(BaseTest): -- cgit v1.2.1