| 
									
										
										
										
											2015-10-03 00:20:56 +02:00
										 |  |  | import errno | 
					
						
							| 
									
										
										
										
											2015-09-26 10:38:01 +02:00
										 |  |  | import os | 
					
						
							|  |  |  | import re | 
					
						
							|  |  |  | import sys | 
					
						
							|  |  |  | import warnings | 
					
						
							|  |  |  | from inspect import isabstract | 
					
						
							|  |  |  | from test import support | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-03 00:20:56 +02:00
										 |  |  | try: | 
					
						
							|  |  |  |     MAXFD = os.sysconf("SC_OPEN_MAX") | 
					
						
							|  |  |  | except Exception: | 
					
						
							|  |  |  |     MAXFD = 256 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def fd_count(): | 
					
						
							|  |  |  |     """Count the number of open file descriptors""" | 
					
						
							|  |  |  |     if sys.platform.startswith(('linux', 'freebsd')): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             names = os.listdir("/proc/self/fd") | 
					
						
							|  |  |  |             return len(names) | 
					
						
							|  |  |  |         except FileNotFoundError: | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     count = 0 | 
					
						
							|  |  |  |     for fd in range(MAXFD): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             # Prefer dup() over fstat(). fstat() can require input/output | 
					
						
							|  |  |  |             # whereas dup() doesn't. | 
					
						
							|  |  |  |             fd2 = os.dup(fd) | 
					
						
							|  |  |  |         except OSError as e: | 
					
						
							|  |  |  |             if e.errno != errno.EBADF: | 
					
						
							|  |  |  |                 raise | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             os.close(fd2) | 
					
						
							|  |  |  |             count += 1 | 
					
						
							|  |  |  |     return count | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-26 10:38:01 +02:00
										 |  |  | def dash_R(the_module, test, indirect_test, huntrleaks): | 
					
						
							|  |  |  |     """Run a test multiple times, looking for reference leaks.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Returns: | 
					
						
							|  |  |  |         False if the test didn't leak references; True if we detected refleaks. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     # This code is hackish and inelegant, but it seems to do the job. | 
					
						
							|  |  |  |     import copyreg | 
					
						
							|  |  |  |     import collections.abc | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if not hasattr(sys, 'gettotalrefcount'): | 
					
						
							|  |  |  |         raise Exception("Tracking reference leaks requires a debug build " | 
					
						
							|  |  |  |                         "of Python") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Save current values for dash_R_cleanup() to restore. | 
					
						
							|  |  |  |     fs = warnings.filters[:] | 
					
						
							|  |  |  |     ps = copyreg.dispatch_table.copy() | 
					
						
							|  |  |  |     pic = sys.path_importer_cache.copy() | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         import zipimport | 
					
						
							|  |  |  |     except ImportError: | 
					
						
							|  |  |  |         zdc = None # Run unmodified on platforms without zipimport support | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         zdc = zipimport._zip_directory_cache.copy() | 
					
						
							|  |  |  |     abcs = {} | 
					
						
							|  |  |  |     for abc in [getattr(collections.abc, a) for a in collections.abc.__all__]: | 
					
						
							|  |  |  |         if not isabstract(abc): | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |         for obj in abc.__subclasses__() + [abc]: | 
					
						
							|  |  |  |             abcs[obj] = obj._abc_registry.copy() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     nwarmup, ntracked, fname = huntrleaks | 
					
						
							|  |  |  |     fname = os.path.join(support.SAVEDCWD, fname) | 
					
						
							|  |  |  |     repcount = nwarmup + ntracked | 
					
						
							|  |  |  |     rc_deltas = [0] * repcount | 
					
						
							|  |  |  |     alloc_deltas = [0] * repcount | 
					
						
							| 
									
										
										
										
											2015-10-03 00:20:56 +02:00
										 |  |  |     fd_deltas = [0] * repcount | 
					
						
							| 
									
										
										
										
											2015-09-26 10:38:01 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     print("beginning", repcount, "repetitions", file=sys.stderr) | 
					
						
							| 
									
										
										
										
											2015-09-30 00:48:27 +02:00
										 |  |  |     print(("1234567890"*(repcount//10 + 1))[:repcount], file=sys.stderr, | 
					
						
							|  |  |  |           flush=True) | 
					
						
							| 
									
										
										
										
											2015-09-30 13:51:17 +02:00
										 |  |  |     # initialize variables to make pyflakes quiet | 
					
						
							| 
									
										
										
										
											2015-10-03 00:20:56 +02:00
										 |  |  |     rc_before = alloc_before = fd_before = 0 | 
					
						
							| 
									
										
										
										
											2015-09-26 10:38:01 +02:00
										 |  |  |     for i in range(repcount): | 
					
						
							|  |  |  |         indirect_test() | 
					
						
							| 
									
										
										
										
											2015-10-03 00:20:56 +02:00
										 |  |  |         alloc_after, rc_after, fd_after = dash_R_cleanup(fs, ps, pic, zdc, | 
					
						
							|  |  |  |                                                          abcs) | 
					
						
							| 
									
										
										
										
											2017-02-08 13:06:08 +01:00
										 |  |  |         print('.', end='', file=sys.stderr, flush=True) | 
					
						
							| 
									
										
										
										
											2015-09-26 10:38:01 +02:00
										 |  |  |         if i >= nwarmup: | 
					
						
							|  |  |  |             rc_deltas[i] = rc_after - rc_before | 
					
						
							|  |  |  |             alloc_deltas[i] = alloc_after - alloc_before | 
					
						
							| 
									
										
										
										
											2015-10-03 00:20:56 +02:00
										 |  |  |             fd_deltas[i] = fd_after - fd_before | 
					
						
							|  |  |  |         alloc_before = alloc_after | 
					
						
							|  |  |  |         rc_before = rc_after | 
					
						
							|  |  |  |         fd_before = fd_after | 
					
						
							| 
									
										
										
										
											2015-09-26 10:38:01 +02:00
										 |  |  |     print(file=sys.stderr) | 
					
						
							|  |  |  |     # These checkers return False on success, True on failure | 
					
						
							|  |  |  |     def check_rc_deltas(deltas): | 
					
						
							|  |  |  |         return any(deltas) | 
					
						
							|  |  |  |     def check_alloc_deltas(deltas): | 
					
						
							|  |  |  |         # At least 1/3rd of 0s | 
					
						
							|  |  |  |         if 3 * deltas.count(0) < len(deltas): | 
					
						
							|  |  |  |             return True | 
					
						
							|  |  |  |         # Nothing else than 1s, 0s and -1s | 
					
						
							|  |  |  |         if not set(deltas) <= {1,0,-1}: | 
					
						
							|  |  |  |             return True | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  |     failed = False | 
					
						
							|  |  |  |     for deltas, item_name, checker in [ | 
					
						
							|  |  |  |         (rc_deltas, 'references', check_rc_deltas), | 
					
						
							| 
									
										
										
										
											2015-10-03 00:20:56 +02:00
										 |  |  |         (alloc_deltas, 'memory blocks', check_alloc_deltas), | 
					
						
							|  |  |  |         (fd_deltas, 'file descriptors', check_rc_deltas)]: | 
					
						
							| 
									
										
										
										
											2015-09-26 10:38:01 +02:00
										 |  |  |         if checker(deltas): | 
					
						
							|  |  |  |             msg = '%s leaked %s %s, sum=%s' % ( | 
					
						
							|  |  |  |                 test, deltas[nwarmup:], item_name, sum(deltas)) | 
					
						
							| 
									
										
										
										
											2015-09-30 00:48:27 +02:00
										 |  |  |             print(msg, file=sys.stderr, flush=True) | 
					
						
							| 
									
										
										
										
											2015-09-26 10:38:01 +02:00
										 |  |  |             with open(fname, "a") as refrep: | 
					
						
							|  |  |  |                 print(msg, file=refrep) | 
					
						
							|  |  |  |                 refrep.flush() | 
					
						
							|  |  |  |             failed = True | 
					
						
							|  |  |  |     return failed | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def dash_R_cleanup(fs, ps, pic, zdc, abcs): | 
					
						
							|  |  |  |     import gc, copyreg | 
					
						
							| 
									
										
										
										
											2016-11-11 11:46:44 +02:00
										 |  |  |     import collections.abc | 
					
						
							| 
									
										
										
										
											2015-09-26 10:38:01 +02:00
										 |  |  |     from weakref import WeakSet | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Restore some original values. | 
					
						
							|  |  |  |     warnings.filters[:] = fs | 
					
						
							|  |  |  |     copyreg.dispatch_table.clear() | 
					
						
							|  |  |  |     copyreg.dispatch_table.update(ps) | 
					
						
							|  |  |  |     sys.path_importer_cache.clear() | 
					
						
							|  |  |  |     sys.path_importer_cache.update(pic) | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         import zipimport | 
					
						
							|  |  |  |     except ImportError: | 
					
						
							|  |  |  |         pass # Run unmodified on platforms without zipimport support | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         zipimport._zip_directory_cache.clear() | 
					
						
							|  |  |  |         zipimport._zip_directory_cache.update(zdc) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # clear type cache | 
					
						
							|  |  |  |     sys._clear_type_cache() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Clear ABC registries, restoring previously saved ABC registries. | 
					
						
							| 
									
										
										
										
											2017-03-05 19:15:20 +01:00
										 |  |  |     abs_classes = [getattr(collections.abc, a) for a in collections.abc.__all__] | 
					
						
							|  |  |  |     abs_classes = filter(isabstract, abs_classes) | 
					
						
							|  |  |  |     if 'typing' in sys.modules: | 
					
						
							|  |  |  |         t = sys.modules['typing'] | 
					
						
							|  |  |  |         # These classes require special treatment because they do not appear | 
					
						
							|  |  |  |         # in direct subclasses of collections.abc classes | 
					
						
							|  |  |  |         abs_classes = list(abs_classes) + [t.ChainMap, t.Counter, t.DefaultDict] | 
					
						
							|  |  |  |     for abc in abs_classes: | 
					
						
							| 
									
										
										
										
											2015-09-26 10:38:01 +02:00
										 |  |  |         for obj in abc.__subclasses__() + [abc]: | 
					
						
							|  |  |  |             obj._abc_registry = abcs.get(obj, WeakSet()).copy() | 
					
						
							|  |  |  |             obj._abc_cache.clear() | 
					
						
							|  |  |  |             obj._abc_negative_cache.clear() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-11 11:46:44 +02:00
										 |  |  |     clear_caches() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Collect cyclic trash and read memory statistics immediately after. | 
					
						
							|  |  |  |     func1 = sys.getallocatedblocks | 
					
						
							|  |  |  |     func2 = sys.gettotalrefcount | 
					
						
							|  |  |  |     gc.collect() | 
					
						
							|  |  |  |     return func1(), func2(), fd_count() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def clear_caches(): | 
					
						
							|  |  |  |     import gc | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Clear the warnings registry, so they can be displayed again | 
					
						
							|  |  |  |     for mod in sys.modules.values(): | 
					
						
							|  |  |  |         if hasattr(mod, '__warningregistry__'): | 
					
						
							|  |  |  |             del mod.__warningregistry__ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-26 10:38:01 +02:00
										 |  |  |     # Flush standard output, so that buffered data is sent to the OS and | 
					
						
							|  |  |  |     # associated Python objects are reclaimed. | 
					
						
							|  |  |  |     for stream in (sys.stdout, sys.stderr, sys.__stdout__, sys.__stderr__): | 
					
						
							|  |  |  |         if stream is not None: | 
					
						
							|  |  |  |             stream.flush() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Clear assorted module caches. | 
					
						
							| 
									
										
										
										
											2016-11-11 11:46:44 +02:00
										 |  |  |     # Don't worry about resetting the cache if the module is not loaded | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         distutils_dir_util = sys.modules['distutils.dir_util'] | 
					
						
							|  |  |  |     except KeyError: | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         distutils_dir_util._path_created.clear() | 
					
						
							| 
									
										
										
										
											2015-09-26 10:38:01 +02:00
										 |  |  |     re.purge() | 
					
						
							| 
									
										
										
										
											2016-11-11 11:46:44 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-26 10:38:01 +02:00
										 |  |  |     try: | 
					
						
							| 
									
										
										
										
											2016-11-11 11:46:44 +02:00
										 |  |  |         _strptime = sys.modules['_strptime'] | 
					
						
							|  |  |  |     except KeyError: | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         _strptime._regex_cache.clear() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         urllib_parse = sys.modules['urllib.parse'] | 
					
						
							|  |  |  |     except KeyError: | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         urllib_parse.clear_cache() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         urllib_request = sys.modules['urllib.request'] | 
					
						
							|  |  |  |     except KeyError: | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         urllib_request.urlcleanup() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         linecache = sys.modules['linecache'] | 
					
						
							|  |  |  |     except KeyError: | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         linecache.clearcache() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         mimetypes = sys.modules['mimetypes'] | 
					
						
							|  |  |  |     except KeyError: | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         mimetypes._default_mime_types() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         filecmp = sys.modules['filecmp'] | 
					
						
							|  |  |  |     except KeyError: | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         filecmp._cache.clear() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         struct = sys.modules['struct'] | 
					
						
							|  |  |  |     except KeyError: | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         struct._clearcache() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         doctest = sys.modules['doctest'] | 
					
						
							|  |  |  |     except KeyError: | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         doctest.master = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         ctypes = sys.modules['ctypes'] | 
					
						
							|  |  |  |     except KeyError: | 
					
						
							| 
									
										
										
										
											2015-09-26 10:38:01 +02:00
										 |  |  |         pass | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         ctypes._reset_cache() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-09 23:51:54 +02:00
										 |  |  |     try: | 
					
						
							|  |  |  |         typing = sys.modules['typing'] | 
					
						
							|  |  |  |     except KeyError: | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         for f in typing._cleanups: | 
					
						
							|  |  |  |             f() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-26 10:38:01 +02:00
										 |  |  |     gc.collect() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def warm_caches(): | 
					
						
							|  |  |  |     # char cache | 
					
						
							|  |  |  |     s = bytes(range(256)) | 
					
						
							|  |  |  |     for i in range(256): | 
					
						
							|  |  |  |         s[i:i+1] | 
					
						
							|  |  |  |     # unicode cache | 
					
						
							| 
									
										
										
										
											2015-09-30 13:51:17 +02:00
										 |  |  |     [chr(i) for i in range(256)] | 
					
						
							| 
									
										
										
										
											2015-09-26 10:38:01 +02:00
										 |  |  |     # int cache | 
					
						
							| 
									
										
										
										
											2015-09-30 13:51:17 +02:00
										 |  |  |     list(range(-5, 257)) |