From 2baaf6c2e973bc79cab3f23cc86210119eb6bb09 Mon Sep 17 00:00:00 2001
From: Hai Shi
Date: Wed, 8 Jul 2020 20:13:10 +0800
Subject: [PATCH 001/197] PyType_GetSlot() could accept static types
---
Doc/c-api/type.rst | 5 +++--
Include/typeslots.h | 21 +++++++++++++++++++
.../2020-07-08-21-01-49.bpo-41073.VqQZON.rst | 1 +
Objects/typeobject.c | 2 +-
Objects/typeslots.inc | 18 ++++++++++++++++
5 files changed, 44 insertions(+), 3 deletions(-)
create mode 100644 Misc/NEWS.d/next/C API/2020-07-08-21-01-49.bpo-41073.VqQZON.rst
diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst
index f387279d143eec..b1666a8813ab6f 100644
--- a/Doc/c-api/type.rst
+++ b/Doc/c-api/type.rst
@@ -105,10 +105,11 @@ Type Objects
See :c:member:`PyType_Slot.slot` for possible values of the *slot* argument.
- An exception is raised if *type* is not a heap type.
-
.. versionadded:: 3.4
+ .. versionchanged:: 3.10
+ :c:func:`PyType_GetSlot` could accept static types.
+
.. c:function:: PyObject* PyType_GetModule(PyTypeObject *type)
Return the module object associated with the given type when the type was
diff --git a/Include/typeslots.h b/Include/typeslots.h
index 64f6fff5144493..e50c66c7c5dcc3 100644
--- a/Include/typeslots.h
+++ b/Include/typeslots.h
@@ -88,3 +88,24 @@
/* New in 3.5 */
#define Py_tp_finalize 80
#endif
+#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x031000a1
+/* New in 3.10 */
+#define Py_tp_as_async 81
+#define Py_tp_as_buffer 82
+#define Py_tp_as_mapping 83
+#define Py_tp_as_number 84
+#define Py_tp_as_sequence 85
+#define Py_tp_basicsize 86
+#define Py_tp_cache 87
+#define Py_tp_dict 88
+#define Py_tp_dictoffset 89
+#define Py_tp_flags 90
+#define Py_tp_itemsize 91
+#define Py_tp_mro 92
+#define Py_tp_name 93
+#define Py_tp_subclasses 94
+#define Py_tp_vectorcall_offset 95
+#define Py_tp_version_tag 96
+#define Py_tp_weaklist 97
+#define Py_tp_weaklistoffset 98
+#endif
diff --git a/Misc/NEWS.d/next/C API/2020-07-08-21-01-49.bpo-41073.VqQZON.rst b/Misc/NEWS.d/next/C API/2020-07-08-21-01-49.bpo-41073.VqQZON.rst
new file mode 100644
index 00000000000000..668bfb2c2e2791
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2020-07-08-21-01-49.bpo-41073.VqQZON.rst
@@ -0,0 +1 @@
+:c:func:`PyType_GetSlot()` could accept static types.
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 16f95f0e1bf7f9..e2bb2273caceaa 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -3110,7 +3110,7 @@ PyType_FromSpec(PyType_Spec *spec)
void *
PyType_GetSlot(PyTypeObject *type, int slot)
{
- if (!_PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE) || slot < 0) {
+ if (slot < 0) {
PyErr_BadInternalCall();
return NULL;
}
diff --git a/Objects/typeslots.inc b/Objects/typeslots.inc
index ffc9bb2e1c7710..58b11d678827a2 100644
--- a/Objects/typeslots.inc
+++ b/Objects/typeslots.inc
@@ -79,3 +79,21 @@ offsetof(PyHeapTypeObject, as_async.am_await),
offsetof(PyHeapTypeObject, as_async.am_aiter),
offsetof(PyHeapTypeObject, as_async.am_anext),
offsetof(PyHeapTypeObject, ht_type.tp_finalize),
+offsetof(PyHeapTypeObject, ht_type.tp_as_async),
+offsetof(PyHeapTypeObject, ht_type.tp_as_buffer),
+offsetof(PyHeapTypeObject, ht_type.tp_as_mapping),
+offsetof(PyHeapTypeObject, ht_type.tp_as_number),
+offsetof(PyHeapTypeObject, ht_type.tp_as_sequence),
+offsetof(PyHeapTypeObject, ht_type.tp_basicsize),
+offsetof(PyHeapTypeObject, ht_type.tp_cache),
+offsetof(PyHeapTypeObject, ht_type.tp_dict),
+offsetof(PyHeapTypeObject, ht_type.tp_dictoffset),
+offsetof(PyHeapTypeObject, ht_type.tp_flags),
+offsetof(PyHeapTypeObject, ht_type.tp_itemsize),
+offsetof(PyHeapTypeObject, ht_type.tp_mro),
+offsetof(PyHeapTypeObject, ht_type.tp_name),
+offsetof(PyHeapTypeObject, ht_type.tp_subclasses),
+offsetof(PyHeapTypeObject, ht_type.tp_vectorcall_offset),
+offsetof(PyHeapTypeObject, ht_type.tp_version_tag),
+offsetof(PyHeapTypeObject, ht_type.tp_weaklist),
+offsetof(PyHeapTypeObject, ht_type.tp_weaklistoffset),
From 9fb5ed491a4b029f953f42d307838701cdce3883 Mon Sep 17 00:00:00 2001
From: Hai Shi
Date: Wed, 8 Jul 2020 21:45:45 +0800
Subject: [PATCH 002/197] update docs' style
---
Doc/c-api/type.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst
index b1666a8813ab6f..2694585660d286 100644
--- a/Doc/c-api/type.rst
+++ b/Doc/c-api/type.rst
@@ -108,7 +108,7 @@ Type Objects
.. versionadded:: 3.4
.. versionchanged:: 3.10
- :c:func:`PyType_GetSlot` could accept static types.
+ :c:func:`PyType_GetSlot` could accept static types.
.. c:function:: PyObject* PyType_GetModule(PyTypeObject *type)
From ddb08f59b09e2010ba61cd2fa55281e6e1d3c934 Mon Sep 17 00:00:00 2001
From: Victor Stinner
Date: Wed, 8 Jul 2020 00:20:37 +0200
Subject: [PATCH 003/197] bpo-29778: test_embed tests the path configuration
(GH-21306)
---
Include/internal/pycore_pathconfig.h | 1 +
Lib/test/test_embed.py | 82 +++++++++++++++++++++++-----
Modules/_testinternalcapi.c | 45 +--------------
Python/initconfig.c | 14 ++++-
Python/pathconfig.c | 74 +++++++++++++++++++++++++
5 files changed, 154 insertions(+), 62 deletions(-)
diff --git a/Include/internal/pycore_pathconfig.h b/Include/internal/pycore_pathconfig.h
index 42d61b1ca26bdf..15447f54490fb4 100644
--- a/Include/internal/pycore_pathconfig.h
+++ b/Include/internal/pycore_pathconfig.h
@@ -65,6 +65,7 @@ extern wchar_t* _Py_GetDLLPath(void);
extern PyStatus _PyConfig_WritePathConfig(const PyConfig *config);
extern void _Py_DumpPathConfig(PyThreadState *tstate);
+extern PyObject* _PyPathConfig_AsDict(void);
#ifdef __cplusplus
}
diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py
index 44d2596d9eb30f..2b740521e723ca 100644
--- a/Lib/test/test_embed.py
+++ b/Lib/test/test_embed.py
@@ -471,6 +471,31 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
('Py_LegacyWindowsStdioFlag', 'legacy_windows_stdio'),
))
+ # path config
+ if MS_WINDOWS:
+ PATH_CONFIG = {
+ 'isolated': -1,
+ 'site_import': -1,
+ 'python3_dll': GET_DEFAULT_CONFIG,
+ }
+ else:
+ PATH_CONFIG = {}
+ # other keys are copied by COPY_PATH_CONFIG
+
+ COPY_PATH_CONFIG = [
+ # Copy core config to global config for expected values
+ 'prefix',
+ 'exec_prefix',
+ 'program_name',
+ 'home',
+ # program_full_path and module_search_path are copied indirectly from
+ # the core configuration in check_path_config().
+ ]
+ if MS_WINDOWS:
+ COPY_PATH_CONFIG.extend((
+ 'base_executable',
+ ))
+
EXPECTED_CONFIG = None
@classmethod
@@ -535,7 +560,8 @@ def _get_expected_config(self):
configs[config_key] = config
return configs
- def get_expected_config(self, expected_preconfig, expected, env, api,
+ def get_expected_config(self, expected_preconfig, expected,
+ expected_pathconfig, env, api,
modify_path_cb=None):
cls = self.__class__
configs = self._get_expected_config()
@@ -545,6 +571,11 @@ def get_expected_config(self, expected_preconfig, expected, env, api,
if value is self.GET_DEFAULT_CONFIG:
expected_preconfig[key] = pre_config[key]
+ path_config = configs['path_config']
+ for key, value in expected_pathconfig.items():
+ if value is self.GET_DEFAULT_CONFIG:
+ expected_pathconfig[key] = path_config[key]
+
if not expected_preconfig['configure_locale'] or api == API_COMPAT:
# there is no easy way to get the locale encoding before
# setlocale(LC_CTYPE, "") is called: don't test encodings
@@ -637,8 +668,19 @@ def check_global_config(self, configs):
self.assertEqual(configs['global_config'], expected)
+ def check_path_config(self, configs, expected):
+ config = configs['config']
+
+ for key in self.COPY_PATH_CONFIG:
+ expected[key] = config[key]
+ expected['module_search_path'] = os.path.pathsep.join(config['module_search_paths'])
+ expected['program_full_path'] = config['executable']
+
+ self.assertEqual(configs['path_config'], expected)
+
def check_all_configs(self, testname, expected_config=None,
- expected_preconfig=None, modify_path_cb=None,
+ expected_preconfig=None, expected_pathconfig=None,
+ modify_path_cb=None,
stderr=None, *, api, preconfig_api=None,
env=None, ignore_stderr=False, cwd=None):
new_env = remove_python_envvars()
@@ -657,9 +699,14 @@ def check_all_configs(self, testname, expected_config=None,
if expected_preconfig is None:
expected_preconfig = {}
expected_preconfig = dict(default_preconfig, **expected_preconfig)
+
if expected_config is None:
expected_config = {}
+ if expected_pathconfig is None:
+ expected_pathconfig = {}
+ expected_pathconfig = dict(self.PATH_CONFIG, **expected_pathconfig)
+
if api == API_PYTHON:
default_config = self.CONFIG_PYTHON
elif api == API_ISOLATED:
@@ -669,7 +716,9 @@ def check_all_configs(self, testname, expected_config=None,
expected_config = dict(default_config, **expected_config)
self.get_expected_config(expected_preconfig,
- expected_config, env,
+ expected_config,
+ expected_pathconfig,
+ env,
api, modify_path_cb)
out, err = self.run_embedded_interpreter(testname,
@@ -686,6 +735,7 @@ def check_all_configs(self, testname, expected_config=None,
self.check_pre_config(configs, expected_preconfig)
self.check_config(configs, expected_config)
self.check_global_config(configs)
+ self.check_path_config(configs, expected_pathconfig)
return configs
def test_init_default_config(self):
@@ -1258,22 +1308,24 @@ def test_init_pyvenv_cfg(self):
'executable': executable,
'module_search_paths': paths,
}
+ path_config = {}
if MS_WINDOWS:
config['base_prefix'] = pyvenv_home
config['prefix'] = pyvenv_home
- env = self.copy_paths_by_env(config)
- actual = self.check_all_configs("test_init_compat_config", config,
- api=API_COMPAT, env=env,
- ignore_stderr=True, cwd=tmpdir)
- if MS_WINDOWS:
- self.assertEqual(
- actual['windows']['python3_dll'],
- os.path.join(
- tmpdir,
- os.path.basename(self.EXPECTED_CONFIG['windows']['python3_dll'])
- )
- )
+ ver = sys.version_info
+ dll = f'python{ver.major}'
+ if debug_build(executable):
+ dll += '_d'
+ dll += '.DLL'
+ dll = os.path.join(os.path.dirname(executable), dll)
+ path_config['python3_dll'] = dll
+
+ env = self.copy_paths_by_env(config)
+ self.check_all_configs("test_init_compat_config", config,
+ expected_pathconfig=path_config,
+ api=API_COMPAT, env=env,
+ ignore_stderr=True, cwd=tmpdir)
def test_global_pathconfig(self):
# Test C API functions getting the path configuration:
diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c
index f08bf8d83d4743..ad74af8363ef45 100644
--- a/Modules/_testinternalcapi.c
+++ b/Modules/_testinternalcapi.c
@@ -18,53 +18,10 @@
#include "pycore_gc.h" // PyGC_Head
-#ifdef MS_WINDOWS
-#include
-
-static int
-_add_windows_config(PyObject *configs)
-{
- HMODULE hPython3;
- wchar_t py3path[MAX_PATH];
- PyObject *dict = PyDict_New();
- PyObject *obj = NULL;
- if (!dict) {
- return -1;
- }
-
- hPython3 = GetModuleHandleW(PY3_DLLNAME);
- if (hPython3 && GetModuleFileNameW(hPython3, py3path, MAX_PATH)) {
- obj = PyUnicode_FromWideChar(py3path, -1);
- } else {
- obj = Py_None;
- Py_INCREF(obj);
- }
- if (obj &&
- !PyDict_SetItemString(dict, "python3_dll", obj) &&
- !PyDict_SetItemString(configs, "windows", dict)) {
- Py_DECREF(obj);
- Py_DECREF(dict);
- return 0;
- }
- Py_DECREF(obj);
- Py_DECREF(dict);
- return -1;
-}
-#endif
-
-
static PyObject *
get_configs(PyObject *self, PyObject *Py_UNUSED(args))
{
- PyObject *dict = _Py_GetConfigsAsDict();
-#ifdef MS_WINDOWS
- if (dict) {
- if (_add_windows_config(dict) < 0) {
- Py_CLEAR(dict);
- }
- }
-#endif
- return dict;
+ return _Py_GetConfigsAsDict();
}
diff --git a/Python/initconfig.c b/Python/initconfig.c
index 86285c77e2307c..64286763b621ed 100644
--- a/Python/initconfig.c
+++ b/Python/initconfig.c
@@ -868,9 +868,7 @@ _PyConfig_Copy(PyConfig *config, const PyConfig *config2)
static PyObject *
config_as_dict(const PyConfig *config)
{
- PyObject *dict;
-
- dict = PyDict_New();
+ PyObject *dict = PyDict_New();
if (dict == NULL) {
return NULL;
}
@@ -2643,6 +2641,16 @@ _Py_GetConfigsAsDict(void)
}
Py_CLEAR(dict);
+ /* path config */
+ dict = _PyPathConfig_AsDict();
+ if (dict == NULL) {
+ goto error;
+ }
+ if (PyDict_SetItemString(result, "path_config", dict) < 0) {
+ goto error;
+ }
+ Py_CLEAR(dict);
+
return result;
error:
diff --git a/Python/pathconfig.c b/Python/pathconfig.c
index 9a302213e77b65..12a684a66b718e 100644
--- a/Python/pathconfig.c
+++ b/Python/pathconfig.c
@@ -186,6 +186,80 @@ pathconfig_set_from_config(_PyPathConfig *pathconfig, const PyConfig *config)
return status;
}
+PyObject *
+_PyPathConfig_AsDict(void)
+{
+ PyObject *dict = PyDict_New();
+ if (dict == NULL) {
+ return NULL;
+ }
+
+#define SET_ITEM(KEY, EXPR) \
+ do { \
+ PyObject *obj = (EXPR); \
+ if (obj == NULL) { \
+ goto fail; \
+ } \
+ int res = PyDict_SetItemString(dict, KEY, obj); \
+ Py_DECREF(obj); \
+ if (res < 0) { \
+ goto fail; \
+ } \
+ } while (0)
+#define SET_ITEM_STR(KEY) \
+ SET_ITEM(#KEY, \
+ (_Py_path_config.KEY \
+ ? PyUnicode_FromWideChar(_Py_path_config.KEY, -1) \
+ : (Py_INCREF(Py_None), Py_None)))
+#define SET_ITEM_INT(KEY) \
+ SET_ITEM(#KEY, PyLong_FromLong(_Py_path_config.KEY))
+
+ SET_ITEM_STR(program_full_path);
+ SET_ITEM_STR(prefix);
+ SET_ITEM_STR(exec_prefix);
+ SET_ITEM_STR(module_search_path);
+ SET_ITEM_STR(program_name);
+ SET_ITEM_STR(home);
+#ifdef MS_WINDOWS
+ SET_ITEM_INT(isolated);
+ SET_ITEM_INT(site_import);
+ SET_ITEM_STR(base_executable);
+
+ {
+ wchar_t py3path[MAX_PATH];
+ HMODULE hPython3 = GetModuleHandleW(PY3_DLLNAME);
+ PyObject *obj;
+ if (hPython3
+ && GetModuleFileNameW(hPython3, py3path, Py_ARRAY_LENGTH(py3path)))
+ {
+ obj = PyUnicode_FromWideChar(py3path, -1);
+ if (obj == NULL) {
+ goto fail;
+ }
+ }
+ else {
+ obj = Py_None;
+ Py_INCREF(obj);
+ }
+ if (PyDict_SetItemString(dict, "python3_dll", obj) < 0) {
+ Py_DECREF(obj);
+ goto fail;
+ }
+ Py_DECREF(obj);
+ }
+#endif
+
+#undef SET_ITEM
+#undef SET_ITEM_STR
+#undef SET_ITEM_INT
+
+ return dict;
+
+fail:
+ Py_DECREF(dict);
+ return NULL;
+}
+
PyStatus
_PyConfig_WritePathConfig(const PyConfig *config)
From 72c57f5d3b9bb3e62b056fbcd9148de8097c61cb Mon Sep 17 00:00:00 2001
From: Joannah Nanjekye <33177550+nanjekyejoannah@users.noreply.github.com>
Date: Tue, 7 Jul 2020 20:09:56 -0300
Subject: [PATCH 004/197] bpo-41224: Document is_annotated() in symtable module
and update doc strings (GH-21369)
* Document is_annotate() and update doc strings
* Move quotes to the next line.
Co-authored-by: Pablo Galindo
Co-authored-by: Pablo Galindo
---
Doc/library/symtable.rst | 4 +++
Lib/symtable.py | 78 ++++++++++++++++++++++++++++++++++++++--
2 files changed, 80 insertions(+), 2 deletions(-)
diff --git a/Doc/library/symtable.rst b/Doc/library/symtable.rst
index 3efdecb5af7102..c9521d649b893e 100644
--- a/Doc/library/symtable.rst
+++ b/Doc/library/symtable.rst
@@ -156,6 +156,10 @@ Examining Symbol Tables
Return ``True`` if the symbol is local to its block.
+ .. method:: is_annotated()
+
+ Return ``True`` if the symbol is annotated.
+
.. method:: is_free()
Return ``True`` if the symbol is referenced in its block, but not assigned
diff --git a/Lib/symtable.py b/Lib/symtable.py
index a711676582f649..9ff27ef74ffe83 100644
--- a/Lib/symtable.py
+++ b/Lib/symtable.py
@@ -10,6 +10,11 @@
__all__ = ["symtable", "SymbolTable", "Class", "Function", "Symbol"]
def symtable(code, filename, compile_type):
+ """ Return the toplevel *SymbolTable* for the source code.
+
+ *filename* is the name of the file with the code
+ and *compile_type* is the *compile()* mode argument.
+ """
top = _symtable.symtable(code, filename, compile_type)
return _newSymbolTable(top, filename)
@@ -55,6 +60,11 @@ def __repr__(self):
self._filename)
def get_type(self):
+ """Return the type of the symbol table.
+
+ The values retuned are 'class', 'module' and
+ 'function'.
+ """
if self._table.type == _symtable.TYPE_MODULE:
return "module"
if self._table.type == _symtable.TYPE_FUNCTION:
@@ -65,27 +75,51 @@ def get_type(self):
"unexpected type: {0}".format(self._table.type)
def get_id(self):
+ """Return an identifier for the table.
+ """
return self._table.id
def get_name(self):
+ """Return the table's name.
+
+ This corresponds to the name of the class, function
+ or 'top' if the table is for a class, function or
+ global respectively.
+ """
return self._table.name
def get_lineno(self):
+ """Return the number of the first line in the
+ block for the table.
+ """
return self._table.lineno
def is_optimized(self):
+ """Return *True* if the locals in the table
+ are optimizable.
+ """
return bool(self._table.type == _symtable.TYPE_FUNCTION)
def is_nested(self):
+ """Return *True* if the block is a nested class
+ or function."""
return bool(self._table.nested)
def has_children(self):
+ """Return *True* if the block has nested namespaces.
+ """
return bool(self._table.children)
def get_identifiers(self):
+ """Return a list of names of symbols in the table.
+ """
return self._table.symbols.keys()
def lookup(self, name):
+ """Lookup a *name* in the table.
+
+ Returns a *Symbol* instance.
+ """
sym = self._symbols.get(name)
if sym is None:
flags = self._table.symbols[name]
@@ -94,6 +128,9 @@ def lookup(self, name):
return sym
def get_symbols(self):
+ """Return a list of *Symbol* instances for
+ names in the table.
+ """
return [self.lookup(ident) for ident in self.get_identifiers()]
def __check_children(self, name):
@@ -102,6 +139,8 @@ def __check_children(self, name):
if st.name == name]
def get_children(self):
+ """Return a list of the nested symbol tables.
+ """
return [_newSymbolTable(st, self._filename)
for st in self._table.children]
@@ -120,11 +159,15 @@ def __idents_matching(self, test_func):
if test_func(self._table.symbols[ident]))
def get_parameters(self):
+ """Return a tuple of parameters to the function.
+ """
if self.__params is None:
self.__params = self.__idents_matching(lambda x:x & DEF_PARAM)
return self.__params
def get_locals(self):
+ """Return a tuple of locals in the function.
+ """
if self.__locals is None:
locs = (LOCAL, CELL)
test = lambda x: ((x >> SCOPE_OFF) & SCOPE_MASK) in locs
@@ -132,6 +175,8 @@ def get_locals(self):
return self.__locals
def get_globals(self):
+ """Return a tuple of globals in the function.
+ """
if self.__globals is None:
glob = (GLOBAL_IMPLICIT, GLOBAL_EXPLICIT)
test = lambda x:((x >> SCOPE_OFF) & SCOPE_MASK) in glob
@@ -139,11 +184,15 @@ def get_globals(self):
return self.__globals
def get_nonlocals(self):
+ """Return a tuple of nonlocals in the function.
+ """
if self.__nonlocals is None:
self.__nonlocals = self.__idents_matching(lambda x:x & DEF_NONLOCAL)
return self.__nonlocals
def get_frees(self):
+ """Return a tuple of free variables in the function.
+ """
if self.__frees is None:
is_free = lambda x:((x >> SCOPE_OFF) & SCOPE_MASK) == FREE
self.__frees = self.__idents_matching(is_free)
@@ -155,6 +204,8 @@ class Class(SymbolTable):
__methods = None
def get_methods(self):
+ """Return a tuple of methods declared in the class.
+ """
if self.__methods is None:
d = {}
for st in self._table.children:
@@ -175,40 +226,63 @@ def __repr__(self):
return "".format(self.__name)
def get_name(self):
+ """Return a name of a symbol.
+ """
return self.__name
def is_referenced(self):
+ """Return *True* if the symbol is used in
+ its block.
+ """
return bool(self.__flags & _symtable.USE)
def is_parameter(self):
+ """Return *True* if the symbol is a parameter.
+ """
return bool(self.__flags & DEF_PARAM)
def is_global(self):
+ """Return *True* if the sysmbol is global.
+ """
return bool(self.__scope in (GLOBAL_IMPLICIT, GLOBAL_EXPLICIT))
def is_nonlocal(self):
+ """Return *True* if the symbol is nonlocal."""
return bool(self.__flags & DEF_NONLOCAL)
def is_declared_global(self):
+ """Return *True* if the symbol is declared global
+ with a global statement."""
return bool(self.__scope == GLOBAL_EXPLICIT)
def is_local(self):
+ """Return *True* if the symbol is local.
+ """
return bool(self.__scope in (LOCAL, CELL))
def is_annotated(self):
+ """Return *True* if the symbol is annotated.
+ """
return bool(self.__flags & DEF_ANNOT)
def is_free(self):
+ """Return *True* if a referenced symbol is
+ not assigned to.
+ """
return bool(self.__scope == FREE)
def is_imported(self):
+ """Return *True* if the symbol is created from
+ an import statement.
+ """
return bool(self.__flags & DEF_IMPORT)
def is_assigned(self):
+ """Return *True* if a symbol is assigned to."""
return bool(self.__flags & DEF_LOCAL)
def is_namespace(self):
- """Returns true if name binding introduces new namespace.
+ """Returns *True* if name binding introduces new namespace.
If the name is used as the target of a function or class
statement, this will be true.
@@ -225,7 +299,7 @@ def get_namespaces(self):
return self.__namespaces
def get_namespace(self):
- """Returns the single namespace bound to this name.
+ """Return the single namespace bound to this name.
Raises ValueError if the name is bound to multiple namespaces.
"""
From 4f2a589bdc70eadd52cbe7709c946304074c7ff9 Mon Sep 17 00:00:00 2001
From: Steve Dower
Date: Wed, 8 Jul 2020 00:24:39 +0100
Subject: [PATCH 005/197] bpo-41173: Copy test results file from ARM worker
before uploading (GH-21305)
---
Tools/buildbot/test.bat | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/Tools/buildbot/test.bat b/Tools/buildbot/test.bat
index a0fc6b9a9458bc..25c796a60e173d 100644
--- a/Tools/buildbot/test.bat
+++ b/Tools/buildbot/test.bat
@@ -36,8 +36,10 @@ if NOT "%REMOTE_PYTHON_DIR:~-1,1%"=="\" (set REMOTE_PYTHON_DIR=%REMOTE_PYTHON_DI
set TEMP_ARGS=--temp %REMOTE_PYTHON_DIR%temp
set rt_args=%rt_opts% %dashU% -rwW --slowest --timeout=1200 --fail-env-changed %regrtest_args% %TEMP_ARGS%
-ssh %SSH_SERVER% "set TEMP=%REMOTE_PYTHON_DIR%temp& %REMOTE_PYTHON_DIR%PCbuild\rt.bat" %rt_args%
-exit /b %ERRORLEVEL%
+ssh %SSH_SERVER% "set TEMP=%REMOTE_PYTHON_DIR%temp& cd %REMOTE_PYTHON_DIR% & %REMOTE_PYTHON_DIR%PCbuild\rt.bat" %rt_args%
+set ERR=%ERRORLEVEL%
+scp %SSH_SERVER%:"%REMOTE_PYTHON_DIR%test-results.xml" "%PYTHON_SOURCE%\test-results.xml"
+exit /b %ERR%
:Arm32SshHelp
echo SSH_SERVER environment variable must be set to administrator@[ip address]
From 4165234f44e2dc63eccaba4f290873a2ef14141b Mon Sep 17 00:00:00 2001
From: Joannah Nanjekye <33177550+nanjekyejoannah@users.noreply.github.com>
Date: Tue, 7 Jul 2020 21:45:45 -0300
Subject: [PATCH 006/197] Add a test for get_id() (GH-21370)
---
Lib/test/test_symtable.py | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/Lib/test/test_symtable.py b/Lib/test/test_symtable.py
index d19355888e4b34..fa514917a1f021 100644
--- a/Lib/test/test_symtable.py
+++ b/Lib/test/test_symtable.py
@@ -63,6 +63,13 @@ def test_type(self):
self.assertEqual(self.spam.get_type(), "function")
self.assertEqual(self.internal.get_type(), "function")
+ def test_id(self):
+ self.assertGreater(self.top.get_id(), 0)
+ self.assertGreater(self.Mine.get_id(), 0)
+ self.assertGreater(self.a_method.get_id(), 0)
+ self.assertGreater(self.spam.get_id(), 0)
+ self.assertGreater(self.internal.get_id(), 0)
+
def test_optimized(self):
self.assertFalse(self.top.is_optimized())
From 6e362d861d7af05c29cd6b90e61a26ea386575a3 Mon Sep 17 00:00:00 2001
From: Zackery Spytz
Date: Tue, 7 Jul 2020 22:21:58 -0600
Subject: [PATCH 007/197] closes bpo-41235: Fix the error handling in
SSLContext.load_dh_params() (GH-21385)
---
.../next/Library/2020-07-07-21-56-26.bpo-41235.H2csMU.rst | 1 +
Modules/_ssl.c | 6 ++++--
2 files changed, 5 insertions(+), 2 deletions(-)
create mode 100644 Misc/NEWS.d/next/Library/2020-07-07-21-56-26.bpo-41235.H2csMU.rst
diff --git a/Misc/NEWS.d/next/Library/2020-07-07-21-56-26.bpo-41235.H2csMU.rst b/Misc/NEWS.d/next/Library/2020-07-07-21-56-26.bpo-41235.H2csMU.rst
new file mode 100644
index 00000000000000..c55275bb1c622b
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-07-07-21-56-26.bpo-41235.H2csMU.rst
@@ -0,0 +1 @@
+Fix the error handling in :meth:`ssl.SSLContext.load_dh_params`.
diff --git a/Modules/_ssl.c b/Modules/_ssl.c
index 5e82fe41a76ec2..58064178a1a343 100644
--- a/Modules/_ssl.c
+++ b/Modules/_ssl.c
@@ -4309,8 +4309,10 @@ _ssl__SSLContext_load_dh_params(PySSLContext *self, PyObject *filepath)
}
return NULL;
}
- if (SSL_CTX_set_tmp_dh(self->ctx, dh) == 0)
- _setSSLError(NULL, 0, __FILE__, __LINE__);
+ if (!SSL_CTX_set_tmp_dh(self->ctx, dh)) {
+ DH_free(dh);
+ return _setSSLError(NULL, 0, __FILE__, __LINE__);
+ }
DH_free(dh);
Py_RETURN_NONE;
}
From e4512c06961b4799bd129f2a2748b526c841a96b Mon Sep 17 00:00:00 2001
From: Victor Stinner
Date: Wed, 8 Jul 2020 11:02:23 +0200
Subject: [PATCH 008/197] Revert "bpo-40170: PyType_HasFeature() now always
calls PyType_GetFlags() (GH-19378)" (GH-21390)
This partially reverts commit 45ec5b99aefa54552947049086e87ec01bc2fc9a.
---
Include/object.h | 12 ++++++++++--
.../C API/2020-07-08-10-14-52.bpo-40170.N6Qx1i.rst | 4 ++++
2 files changed, 14 insertions(+), 2 deletions(-)
create mode 100644 Misc/NEWS.d/next/C API/2020-07-08-10-14-52.bpo-40170.N6Qx1i.rst
diff --git a/Include/object.h b/Include/object.h
index 537567040f9871..10f1d6a3dff2dd 100644
--- a/Include/object.h
+++ b/Include/object.h
@@ -637,8 +637,16 @@ times.
static inline int
-PyType_HasFeature(PyTypeObject *type, unsigned long feature) {
- return ((PyType_GetFlags(type) & feature) != 0);
+PyType_HasFeature(PyTypeObject *type, unsigned long feature)
+{
+ unsigned long flags;
+#ifdef Py_LIMITED_API
+ // PyTypeObject is opaque in the limited C API
+ flags = PyType_GetFlags(type);
+#else
+ flags = type->tp_flags;
+#endif
+ return ((flags & feature) != 0);
}
#define PyType_FastSubclass(type, flag) PyType_HasFeature(type, flag)
diff --git a/Misc/NEWS.d/next/C API/2020-07-08-10-14-52.bpo-40170.N6Qx1i.rst b/Misc/NEWS.d/next/C API/2020-07-08-10-14-52.bpo-40170.N6Qx1i.rst
new file mode 100644
index 00000000000000..760a3ff4d17b44
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2020-07-08-10-14-52.bpo-40170.N6Qx1i.rst
@@ -0,0 +1,4 @@
+Revert :c:func:`PyType_HasFeature` change: it reads again directly the
+:c:member:`PyTypeObject.tp_flags` member when the limited C API is not used,
+rather than always calling :c:func:`PyType_GetFlags` which hides implementation
+details.
From a747cbb1fb08406a4e0a87616b778b581de25d8f Mon Sep 17 00:00:00 2001
From: Tony Solomonik
Date: Wed, 8 Jul 2020 22:27:31 +0300
Subject: [PATCH 009/197] bpo-41247: asyncio.set_running_loop() cache running
loop holder (GH-21401)
The running loop holder cache variable was always set to NULL when
calling set_running_loop.
Now set_running_loop saves the newly created running loop holder in the
cache variable for faster access in get_running_loop.
Automerge-Triggered-By: @1st1
---
.../2020-07-08-22-03-54.bpo-41247.PndYIk.rst | 2 ++
Modules/_asynciomodule.c | 12 +++++++++---
2 files changed, 11 insertions(+), 3 deletions(-)
create mode 100644 Misc/NEWS.d/next/Core and Builtins/2020-07-08-22-03-54.bpo-41247.PndYIk.rst
diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-07-08-22-03-54.bpo-41247.PndYIk.rst b/Misc/NEWS.d/next/Core and Builtins/2020-07-08-22-03-54.bpo-41247.PndYIk.rst
new file mode 100644
index 00000000000000..08699b6e4a1f01
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2020-07-08-22-03-54.bpo-41247.PndYIk.rst
@@ -0,0 +1,2 @@
+Always cache the running loop holder when running
+``asyncio.set_running_loop``.
diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c
index b378742648b2a2..4a1c91e9eddd67 100644
--- a/Modules/_asynciomodule.c
+++ b/Modules/_asynciomodule.c
@@ -291,10 +291,13 @@ get_running_loop(PyObject **loop)
static int
set_running_loop(PyObject *loop)
{
- cached_running_holder = NULL;
- cached_running_holder_tsid = 0;
+ PyObject *ts_dict = NULL;
+
+ PyThreadState *tstate = PyThreadState_Get();
+ if (tstate != NULL) {
+ ts_dict = _PyThreadState_GetDict(tstate); // borrowed
+ }
- PyObject *ts_dict = PyThreadState_GetDict(); // borrowed
if (ts_dict == NULL) {
PyErr_SetString(
PyExc_RuntimeError, "thread-local storage is not available");
@@ -314,6 +317,9 @@ set_running_loop(PyObject *loop)
}
Py_DECREF(rl);
+ cached_running_holder = (PyObject *)rl;
+ cached_running_holder_tsid = PyThreadState_GetID(tstate);
+
return 0;
}
From badce8af39f78ffa13ea2a1d9438103efaae7ce6 Mon Sep 17 00:00:00 2001
From: stratakis
Date: Wed, 8 Jul 2020 22:39:41 +0200
Subject: [PATCH 010/197] bpo-41175: Guard against a NULL pointer dereference
within bytearrayobject (GH-21240)
The issue is triggered by the bytearray() + bytearray() operation.
Detected by GCC 10 static analysis tool.
---
.../2020-06-30-20-17-31.bpo-41175.acJoXB.rst | 2 ++
Objects/bytearrayobject.c | 4 +++-
2 files changed, 5 insertions(+), 1 deletion(-)
create mode 100644 Misc/NEWS.d/next/Core and Builtins/2020-06-30-20-17-31.bpo-41175.acJoXB.rst
diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-06-30-20-17-31.bpo-41175.acJoXB.rst b/Misc/NEWS.d/next/Core and Builtins/2020-06-30-20-17-31.bpo-41175.acJoXB.rst
new file mode 100644
index 00000000000000..844fb804c0c8d4
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2020-06-30-20-17-31.bpo-41175.acJoXB.rst
@@ -0,0 +1,2 @@
+Guard against a NULL pointer dereference within bytearrayobject triggered by
+the ``bytearray() + bytearray()`` operation.
diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c
index 83c79b200a0a13..70350619330983 100644
--- a/Objects/bytearrayobject.c
+++ b/Objects/bytearrayobject.c
@@ -266,7 +266,9 @@ PyByteArray_Concat(PyObject *a, PyObject *b)
result = (PyByteArrayObject *) \
PyByteArray_FromStringAndSize(NULL, va.len + vb.len);
- if (result != NULL) {
+ // result->ob_bytes is NULL if result is an empty string:
+ // if va.len + vb.len equals zero.
+ if (result != NULL && result->ob_bytes != NULL) {
memcpy(result->ob_bytes, va.buf, va.len);
memcpy(result->ob_bytes + va.len, vb.buf, vb.len);
}
From aee8f10e9898d21602397f35c9040c00fd3caae4 Mon Sep 17 00:00:00 2001
From: Mark Sapiro
Date: Wed, 8 Jul 2020 14:00:35 -0700
Subject: [PATCH 011/197] bpo-40597: Allow email.contextmanager set_content()
to set a null string. (GH-20542)
---
Lib/email/contentmanager.py | 2 +-
Lib/test/test_email/test_contentmanager.py | 13 +++++++++++++
.../2020-05-30-12-44-29.bpo-39384.Iqxy3q.rst | 1 +
3 files changed, 15 insertions(+), 1 deletion(-)
create mode 100644 Misc/NEWS.d/next/Library/2020-05-30-12-44-29.bpo-39384.Iqxy3q.rst
diff --git a/Lib/email/contentmanager.py b/Lib/email/contentmanager.py
index 2b4b8757f46f62..b91fb0e5bca7a8 100644
--- a/Lib/email/contentmanager.py
+++ b/Lib/email/contentmanager.py
@@ -146,7 +146,7 @@ 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.
- if max(len(x) for x in lines) <= policy.max_line_length:
+ if max((len(x) for x in lines), default=0) <= policy.max_line_length:
try:
return '7bit', normal_body(lines).decode('ascii')
except UnicodeDecodeError:
diff --git a/Lib/test/test_email/test_contentmanager.py b/Lib/test/test_email/test_contentmanager.py
index 64dca2d017e629..f4f6bb715acdce 100644
--- a/Lib/test/test_email/test_contentmanager.py
+++ b/Lib/test/test_email/test_contentmanager.py
@@ -303,6 +303,19 @@ def test_set_text_plain(self):
self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content)
self.assertEqual(m.get_content(), content)
+ def test_set_text_plain_null(self):
+ m = self._make_message()
+ content = ''
+ raw_data_manager.set_content(m, content)
+ self.assertEqual(str(m), textwrap.dedent("""\
+ Content-Type: text/plain; charset="utf-8"
+ Content-Transfer-Encoding: 7bit
+
+
+ """))
+ self.assertEqual(m.get_payload(decode=True).decode('utf-8'), '\n')
+ self.assertEqual(m.get_content(), '\n')
+
def test_set_text_html(self):
m = self._make_message()
content = "Simple message.
\n"
diff --git a/Misc/NEWS.d/next/Library/2020-05-30-12-44-29.bpo-39384.Iqxy3q.rst b/Misc/NEWS.d/next/Library/2020-05-30-12-44-29.bpo-39384.Iqxy3q.rst
new file mode 100644
index 00000000000000..482ae624da079d
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-05-30-12-44-29.bpo-39384.Iqxy3q.rst
@@ -0,0 +1 @@
+Fixed email.contentmanager to allow set_content() to set a null string.
From e8ab7093c316e560e14b80be4451b5505369a567 Mon Sep 17 00:00:00 2001
From: Julien Palard
Date: Thu, 9 Jul 2020 11:38:41 +0200
Subject: [PATCH 012/197] Doc: Builtins functions: faster jump table (GH-21376)
---
Doc/library/functions.rst | 50 +++++++++++++++++++++++++--------------
1 file changed, 32 insertions(+), 18 deletions(-)
diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst
index f4110c3585a0bc..3c36b59befab91 100644
--- a/Doc/library/functions.rst
+++ b/Doc/library/functions.rst
@@ -7,24 +7,38 @@ Built-in Functions
The Python interpreter has a number of functions and types built into it that
are always available. They are listed here in alphabetical order.
-=================== ================= ================== ================== ====================
-.. .. Built-in Functions .. ..
-=================== ================= ================== ================== ====================
-:func:`abs` :func:`delattr` :func:`hash` |func-memoryview|_ |func-set|_
-:func:`all` |func-dict|_ :func:`help` :func:`min` :func:`setattr`
-:func:`any` :func:`dir` :func:`hex` :func:`next` :func:`slice`
-:func:`ascii` :func:`divmod` :func:`id` :func:`object` :func:`sorted`
-:func:`bin` :func:`enumerate` :func:`input` :func:`oct` :func:`staticmethod`
-:func:`bool` :func:`eval` :func:`int` :func:`open` |func-str|_
-:func:`breakpoint` :func:`exec` :func:`isinstance` :func:`ord` :func:`sum`
-|func-bytearray|_ :func:`filter` :func:`issubclass` :func:`pow` :func:`super`
-|func-bytes|_ :func:`float` :func:`iter` :func:`print` |func-tuple|_
-:func:`callable` :func:`format` :func:`len` :func:`property` :func:`type`
-:func:`chr` |func-frozenset|_ |func-list|_ |func-range|_ :func:`vars`
-:func:`classmethod` :func:`getattr` :func:`locals` :func:`repr` :func:`zip`
-:func:`compile` :func:`globals` :func:`map` :func:`reversed` :func:`__import__`
-:func:`complex` :func:`hasattr` :func:`max` :func:`round`
-=================== ================= ================== ================== ====================
++---------------------------------------------------------------------------------------------------+
+| Built-in Functions |
++=========================+=======================+=======================+=========================+
+| | **A** | | **E** | | **L** | | **R** |
+| | :func:`abs` | | :func:`enumerate` | | :func:`len` | | |func-range|_ |
+| | :func:`all` | | :func:`eval` | | |func-list|_ | | :func:`repr` |
+| | :func:`any` | | :func:`exec` | | :func:`locals` | | :func:`reversed` |
+| | :func:`ascii` | | | | | | :func:`round` |
+| | | | **F** | | **M** | | |
+| | **B** | | :func:`filter` | | :func:`map` | | **S** |
+| | :func:`bin` | | :func:`float` | | :func:`max` | | |func-set|_ |
+| | :func:`bool` | | :func:`format` | | |func-memoryview|_ | | :func:`setattr` |
+| | :func:`breakpoint` | | |func-frozenset|_ | | :func:`min` | | :func:`slice` |
+| | |func-bytearray|_ | | | | | | :func:`sorted` |
+| | |func-bytes|_ | | **G** | | **N** | | :func:`staticmethod` |
+| | | | :func:`getattr` | | :func:`next` | | |func-str|_ |
+| | **C** | | :func:`globals` | | | | :func:`sum` |
+| | :func:`callable` | | | | **O** | | :func:`super` |
+| | :func:`chr` | | **H** | | :func:`object` | | |
+| | :func:`classmethod` | | :func:`hasattr` | | :func:`oct` | | **T** |
+| | :func:`compile` | | :func:`hash` | | :func:`open` | | |func-tuple|_ |
+| | :func:`complex` | | :func:`help` | | :func:`ord` | | :func:`type` |
+| | | | :func:`hex` | | | | |
+| | **D** | | | | **P** | | **V** |
+| | :func:`delattr` | | **I** | | :func:`pow` | | :func:`vars` |
+| | |func-dict|_ | | :func:`id` | | :func:`print` | | |
+| | :func:`dir` | | :func:`input` | | :func:`property` | | **Z** |
+| | :func:`divmod` | | :func:`int` | | | | :func:`zip` |
+| | | | :func:`isinstance` | | | | |
+| | | | :func:`issubclass` | | | | **_** |
+| | | | :func:`iter` | | | | :func:`__import__` |
++-------------------------+-----------------------+-----------------------+-------------------------+
.. using :func:`dict` would create a link to another page, so local targets are
used, with replacement texts to make the output in the table consistent
From e8e841462533f1d2858a9750c47abe698c277839 Mon Sep 17 00:00:00 2001
From: Zackery Spytz
Date: Thu, 9 Jul 2020 04:00:21 -0600
Subject: [PATCH 013/197] bpo-41252: Fix incorrect refcounting in _ssl.c's
_servername_callback() (GH-21407)
---
.../Core and Builtins/2020-07-08-21-55-23.bpo-41252.nBWL-Y.rst | 1 +
Modules/_ssl.c | 3 ++-
2 files changed, 3 insertions(+), 1 deletion(-)
create mode 100644 Misc/NEWS.d/next/Core and Builtins/2020-07-08-21-55-23.bpo-41252.nBWL-Y.rst
diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-07-08-21-55-23.bpo-41252.nBWL-Y.rst b/Misc/NEWS.d/next/Core and Builtins/2020-07-08-21-55-23.bpo-41252.nBWL-Y.rst
new file mode 100644
index 00000000000000..65f3189c83ec64
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2020-07-08-21-55-23.bpo-41252.nBWL-Y.rst
@@ -0,0 +1 @@
+Fix incorrect refcounting in _ssl.c's ``_servername_callback()``.
diff --git a/Modules/_ssl.c b/Modules/_ssl.c
index 58064178a1a343..a8c339d9f33344 100644
--- a/Modules/_ssl.c
+++ b/Modules/_ssl.c
@@ -4545,11 +4545,12 @@ _servername_callback(SSL *s, int *al, void *args)
* back into a str object, but still as an A-label (bpo-28414)
*/
servername_str = PyUnicode_FromEncodedObject(servername_bytes, "ascii", NULL);
- Py_DECREF(servername_bytes);
if (servername_str == NULL) {
PyErr_WriteUnraisable(servername_bytes);
+ Py_DECREF(servername_bytes);
goto error;
}
+ Py_DECREF(servername_bytes);
result = PyObject_CallFunctionObjArgs(
ssl_ctx->set_sni_cb, ssl_socket, servername_str,
ssl_ctx, NULL);
From 10ddc13626bb0e0316dd4861efb6d6689a229395 Mon Sep 17 00:00:00 2001
From: marload
Date: Thu, 9 Jul 2020 21:13:47 +0900
Subject: [PATCH 014/197] bpo-41199: Docstring convention not followed for
dataclasses documentation page (GH-21413)
Automerge-Triggered-By: @ericvsmith
---
Doc/library/dataclasses.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Doc/library/dataclasses.rst b/Doc/library/dataclasses.rst
index fe63d20671dd74..6e74af062d9e72 100644
--- a/Doc/library/dataclasses.rst
+++ b/Doc/library/dataclasses.rst
@@ -23,7 +23,7 @@ using :pep:`526` type annotations. For example this code::
@dataclass
class InventoryItem:
- '''Class for keeping track of an item in inventory.'''
+ """Class for keeping track of an item in inventory."""
name: str
unit_price: float
quantity_on_hand: int = 0
From d4ae3c02adfd784ff7305916c289893a6677b448 Mon Sep 17 00:00:00 2001
From: Hai Shi
Date: Thu, 9 Jul 2020 21:25:10 +0800
Subject: [PATCH 015/197] bpo-40275: Use new test.support helper submodules in
tests (GH-21412)
---
Lib/test/test_cmd_line_script.py | 100 +++++++++---------
Lib/test/test_codeop.py | 3 +-
Lib/test/test_contextlib.py | 3 +-
Lib/test/test_dbm_ndbm.py | 3 +-
Lib/test/test_getargs2.py | 3 +-
Lib/test/test_gettext.py | 5 +-
Lib/test/test_glob.py | 4 +-
Lib/test/test_import/__init__.py | 23 ++--
Lib/test/test_locale.py | 3 +-
Lib/test/test_mailbox.py | 41 +++----
.../test_multiprocessing_main_handling.py | 36 ++++---
Lib/test/test_osx_env.py | 2 +-
Lib/test/test_pkgutil.py | 4 +-
Lib/test/test_plistlib.py | 7 +-
Lib/test/test_ttk_guionly.py | 3 +-
Lib/test/test_turtle.py | 4 +-
Lib/test/test_xxtestfuzz.py | 4 +-
Lib/test/test_zipimport.py | 40 +++----
18 files changed, 154 insertions(+), 134 deletions(-)
diff --git a/Lib/test/test_cmd_line_script.py b/Lib/test/test_cmd_line_script.py
index 15fca7b8a5191e..6c8c28f40b564b 100644
--- a/Lib/test/test_cmd_line_script.py
+++ b/Lib/test/test_cmd_line_script.py
@@ -14,6 +14,8 @@
import textwrap
from test import support
+from test.support import import_helper
+from test.support import os_helper
from test.support.script_helper import (
make_pkg, make_script, make_zip_pkg, make_zip_script,
assert_python_ok, assert_python_failure, spawn_python, kill_python)
@@ -214,7 +216,7 @@ def test_repl_stderr_flush_separate_stderr(self):
self.check_repl_stderr_flush(True)
def test_basic_script(self):
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
script_name = _make_test_script(script_dir, 'script')
self._check_script(script_name, script_name, script_name,
script_dir, None,
@@ -224,7 +226,7 @@ def test_basic_script(self):
def test_script_abspath(self):
# pass the script using the relative path, expect the absolute path
# in __file__
- with support.temp_cwd() as script_dir:
+ with os_helper.temp_cwd() as script_dir:
self.assertTrue(os.path.isabs(script_dir), script_dir)
script_name = _make_test_script(script_dir, 'script')
@@ -234,46 +236,46 @@ def test_script_abspath(self):
importlib.machinery.SourceFileLoader)
def test_script_compiled(self):
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
script_name = _make_test_script(script_dir, 'script')
py_compile.compile(script_name, doraise=True)
os.remove(script_name)
- pyc_file = support.make_legacy_pyc(script_name)
+ pyc_file = import_helper.make_legacy_pyc(script_name)
self._check_script(pyc_file, pyc_file,
pyc_file, script_dir, None,
importlib.machinery.SourcelessFileLoader)
def test_directory(self):
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
script_name = _make_test_script(script_dir, '__main__')
self._check_script(script_dir, script_name, script_dir,
script_dir, '',
importlib.machinery.SourceFileLoader)
def test_directory_compiled(self):
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
script_name = _make_test_script(script_dir, '__main__')
py_compile.compile(script_name, doraise=True)
os.remove(script_name)
- pyc_file = support.make_legacy_pyc(script_name)
+ pyc_file = import_helper.make_legacy_pyc(script_name)
self._check_script(script_dir, pyc_file, script_dir,
script_dir, '',
importlib.machinery.SourcelessFileLoader)
def test_directory_error(self):
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
msg = "can't find '__main__' module in %r" % script_dir
self._check_import_error(script_dir, msg)
def test_zipfile(self):
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
script_name = _make_test_script(script_dir, '__main__')
zip_name, run_name = make_zip_script(script_dir, 'test_zip', script_name)
self._check_script(zip_name, run_name, zip_name, zip_name, '',
zipimport.zipimporter)
def test_zipfile_compiled_timestamp(self):
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
script_name = _make_test_script(script_dir, '__main__')
compiled_name = py_compile.compile(
script_name, doraise=True,
@@ -283,7 +285,7 @@ def test_zipfile_compiled_timestamp(self):
zipimport.zipimporter)
def test_zipfile_compiled_checked_hash(self):
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
script_name = _make_test_script(script_dir, '__main__')
compiled_name = py_compile.compile(
script_name, doraise=True,
@@ -293,7 +295,7 @@ def test_zipfile_compiled_checked_hash(self):
zipimport.zipimporter)
def test_zipfile_compiled_unchecked_hash(self):
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
script_name = _make_test_script(script_dir, '__main__')
compiled_name = py_compile.compile(
script_name, doraise=True,
@@ -303,14 +305,14 @@ def test_zipfile_compiled_unchecked_hash(self):
zipimport.zipimporter)
def test_zipfile_error(self):
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
script_name = _make_test_script(script_dir, 'not_main')
zip_name, run_name = make_zip_script(script_dir, 'test_zip', script_name)
msg = "can't find '__main__' module in %r" % zip_name
self._check_import_error(zip_name, msg)
def test_module_in_package(self):
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
pkg_dir = os.path.join(script_dir, 'test_pkg')
make_pkg(pkg_dir)
script_name = _make_test_script(pkg_dir, 'script')
@@ -320,14 +322,14 @@ def test_module_in_package(self):
cwd=script_dir)
def test_module_in_package_in_zipfile(self):
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
zip_name, run_name = _make_test_zip_pkg(script_dir, 'test_zip', 'test_pkg', 'script')
self._check_script(["-m", "test_pkg.script"], run_name, run_name,
script_dir, 'test_pkg', zipimport.zipimporter,
PYTHONPATH=zip_name, cwd=script_dir)
def test_module_in_subpackage_in_zipfile(self):
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
zip_name, run_name = _make_test_zip_pkg(script_dir, 'test_zip', 'test_pkg', 'script', depth=2)
self._check_script(["-m", "test_pkg.test_pkg.script"], run_name, run_name,
script_dir, 'test_pkg.test_pkg',
@@ -335,7 +337,7 @@ def test_module_in_subpackage_in_zipfile(self):
PYTHONPATH=zip_name, cwd=script_dir)
def test_package(self):
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
pkg_dir = os.path.join(script_dir, 'test_pkg')
make_pkg(pkg_dir)
script_name = _make_test_script(pkg_dir, '__main__')
@@ -345,20 +347,20 @@ def test_package(self):
cwd=script_dir)
def test_package_compiled(self):
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
pkg_dir = os.path.join(script_dir, 'test_pkg')
make_pkg(pkg_dir)
script_name = _make_test_script(pkg_dir, '__main__')
compiled_name = py_compile.compile(script_name, doraise=True)
os.remove(script_name)
- pyc_file = support.make_legacy_pyc(script_name)
+ pyc_file = import_helper.make_legacy_pyc(script_name)
self._check_script(["-m", "test_pkg"], pyc_file,
pyc_file, script_dir, 'test_pkg',
importlib.machinery.SourcelessFileLoader,
cwd=script_dir)
def test_package_error(self):
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
pkg_dir = os.path.join(script_dir, 'test_pkg')
make_pkg(pkg_dir)
msg = ("'test_pkg' is a package and cannot "
@@ -366,7 +368,7 @@ def test_package_error(self):
self._check_import_error(["-m", "test_pkg"], msg, cwd=script_dir)
def test_package_recursion(self):
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
pkg_dir = os.path.join(script_dir, 'test_pkg')
make_pkg(pkg_dir)
main_dir = os.path.join(pkg_dir, '__main__')
@@ -379,8 +381,8 @@ def test_package_recursion(self):
def test_issue8202(self):
# Make sure package __init__ modules see "-m" in sys.argv0 while
# searching for the module to execute
- with support.temp_dir() as script_dir:
- with support.change_cwd(path=script_dir):
+ with os_helper.temp_dir() as script_dir:
+ with os_helper.change_cwd(path=script_dir):
pkg_dir = os.path.join(script_dir, 'test_pkg')
make_pkg(pkg_dir, "import sys; print('init_argv0==%r' % sys.argv[0])")
script_name = _make_test_script(pkg_dir, 'script')
@@ -396,8 +398,8 @@ def test_issue8202(self):
def test_issue8202_dash_c_file_ignored(self):
# Make sure a "-c" file in the current directory
# does not alter the value of sys.path[0]
- with support.temp_dir() as script_dir:
- with support.change_cwd(path=script_dir):
+ with os_helper.temp_dir() as script_dir:
+ with os_helper.change_cwd(path=script_dir):
with open("-c", "w") as f:
f.write("data")
rc, out, err = assert_python_ok('-c',
@@ -411,9 +413,9 @@ def test_issue8202_dash_c_file_ignored(self):
def test_issue8202_dash_m_file_ignored(self):
# Make sure a "-m" file in the current directory
# does not alter the value of sys.path[0]
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
script_name = _make_test_script(script_dir, 'other')
- with support.change_cwd(path=script_dir):
+ with os_helper.change_cwd(path=script_dir):
with open("-m", "w") as f:
f.write("data")
rc, out, err = assert_python_ok('-m', 'other', *example_args,
@@ -425,7 +427,7 @@ def test_issue8202_dash_m_file_ignored(self):
def test_issue20884(self):
# On Windows, script with encoding cookie and LF line ending
# will be failed.
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
script_name = os.path.join(script_dir, "issue20884.py")
with open(script_name, "w", newline='\n') as f:
f.write("#coding: iso-8859-1\n")
@@ -434,15 +436,15 @@ def test_issue20884(self):
f.write('x'*80 + '\n')
f.write('"""\n')
- with support.change_cwd(path=script_dir):
+ with os_helper.change_cwd(path=script_dir):
rc, out, err = assert_python_ok(script_name)
self.assertEqual(b"", out)
self.assertEqual(b"", err)
@contextlib.contextmanager
def setup_test_pkg(self, *args):
- with support.temp_dir() as script_dir, \
- support.change_cwd(path=script_dir):
+ with os_helper.temp_dir() as script_dir, \
+ os_helper.change_cwd(path=script_dir):
pkg_dir = os.path.join(script_dir, 'test_pkg')
make_pkg(pkg_dir, *args)
yield pkg_dir
@@ -486,8 +488,8 @@ def test_dash_m_errors(self):
self.assertNotIn(b'Traceback', err)
def test_dash_m_bad_pyc(self):
- with support.temp_dir() as script_dir, \
- support.change_cwd(path=script_dir):
+ with os_helper.temp_dir() as script_dir, \
+ os_helper.change_cwd(path=script_dir):
os.mkdir('test_pkg')
# Create invalid *.pyc as empty file
with open('test_pkg/__init__.pyc', 'wb'):
@@ -500,8 +502,8 @@ def test_dash_m_bad_pyc(self):
self.assertNotIn(b'Traceback', err)
def test_hint_when_triying_to_import_a_py_file(self):
- with support.temp_dir() as script_dir, \
- support.change_cwd(path=script_dir):
+ with os_helper.temp_dir() as script_dir, \
+ os_helper.change_cwd(path=script_dir):
# Create invalid *.pyc as empty file
with open('asyncio.py', 'wb'):
pass
@@ -542,7 +544,7 @@ def test_pep_409_verbiage(self):
except:
raise NameError from None
""")
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
script_name = _make_test_script(script_dir, 'script', script)
exitcode, stdout, stderr = assert_python_failure(script_name)
text = stderr.decode('ascii').split('\n')
@@ -555,18 +557,18 @@ def test_non_ascii(self):
# Mac OS X denies the creation of a file with an invalid UTF-8 name.
# Windows allows creating a name with an arbitrary bytes name, but
# Python cannot a undecodable bytes argument to a subprocess.
- if (support.TESTFN_UNDECODABLE
+ if (os_helper.TESTFN_UNDECODABLE
and sys.platform not in ('win32', 'darwin')):
- name = os.fsdecode(support.TESTFN_UNDECODABLE)
- elif support.TESTFN_NONASCII:
- name = support.TESTFN_NONASCII
+ name = os.fsdecode(os_helper.TESTFN_UNDECODABLE)
+ elif os_helper.TESTFN_NONASCII:
+ name = os_helper.TESTFN_NONASCII
else:
- self.skipTest("need support.TESTFN_NONASCII")
+ self.skipTest("need os_helper.TESTFN_NONASCII")
# Issue #16218
source = 'print(ascii(__file__))\n'
script_name = _make_test_script(os.getcwd(), name, source)
- self.addCleanup(support.unlink, script_name)
+ self.addCleanup(os_helper.unlink, script_name)
rc, stdout, stderr = assert_python_ok(script_name)
self.assertEqual(
ascii(script_name),
@@ -586,7 +588,7 @@ def test_issue20500_exit_with_exception_value(self):
if error:
sys.exit(error)
""")
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
script_name = _make_test_script(script_dir, 'script', script)
exitcode, stdout, stderr = assert_python_failure(script_name)
text = stderr.decode('ascii')
@@ -594,7 +596,7 @@ def test_issue20500_exit_with_exception_value(self):
def test_syntaxerror_unindented_caret_position(self):
script = "1 + 1 = 2\n"
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
script_name = _make_test_script(script_dir, 'script', script)
exitcode, stdout, stderr = assert_python_failure(script_name)
text = io.TextIOWrapper(io.BytesIO(stderr), 'ascii').read()
@@ -606,7 +608,7 @@ def test_syntaxerror_indented_caret_position(self):
if True:
1 + 1 = 2
""")
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
script_name = _make_test_script(script_dir, 'script', script)
exitcode, stdout, stderr = assert_python_failure(script_name)
text = io.TextIOWrapper(io.BytesIO(stderr), 'ascii').read()
@@ -626,7 +628,7 @@ def test_syntaxerror_indented_caret_position(self):
def test_syntaxerror_multi_line_fstring(self):
script = 'foo = f"""{}\nfoo"""\n'
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
script_name = _make_test_script(script_dir, 'script', script)
exitcode, stdout, stderr = assert_python_failure(script_name)
self.assertEqual(
@@ -640,7 +642,7 @@ def test_syntaxerror_multi_line_fstring(self):
def test_syntaxerror_invalid_escape_sequence_multi_line(self):
script = 'foo = """\\q"""\n'
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
script_name = _make_test_script(script_dir, 'script', script)
exitcode, stdout, stderr = assert_python_failure(
'-Werror', script_name,
@@ -667,7 +669,7 @@ def test_consistent_sys_path_for_direct_execution(self):
""")
# Always show full path diffs on errors
self.maxDiff = None
- with support.temp_dir() as work_dir, support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as work_dir, os_helper.temp_dir() as script_dir:
script_name = _make_test_script(script_dir, '__main__', script)
# Reference output comes from directly executing __main__.py
# We omit PYTHONPATH and user site to align with isolated mode
@@ -699,7 +701,7 @@ def test_consistent_sys_path_for_module_execution(self):
""")
# Always show full path diffs on errors
self.maxDiff = None
- with support.temp_dir() as work_dir:
+ with os_helper.temp_dir() as work_dir:
script_dir = os.path.join(work_dir, "script_pkg")
os.mkdir(script_dir)
script_name = _make_test_script(script_dir, '__main__', script)
diff --git a/Lib/test/test_codeop.py b/Lib/test/test_codeop.py
index 45cb1a7b74e903..6e821df6b0e707 100644
--- a/Lib/test/test_codeop.py
+++ b/Lib/test/test_codeop.py
@@ -5,6 +5,7 @@
import sys
import unittest
from test import support
+from test.support import warnings_helper
from codeop import compile_command, PyCF_DONT_IMPLY_DEDENT
import io
@@ -305,7 +306,7 @@ def test_filename(self):
def test_warning(self):
# Test that the warning is only returned once.
- with support.check_warnings((".*literal", SyntaxWarning)) as w:
+ with warnings_helper.check_warnings((".*literal", SyntaxWarning)) as w:
compile_command("0 is 0")
self.assertEqual(len(w.warnings), 1)
diff --git a/Lib/test/test_contextlib.py b/Lib/test/test_contextlib.py
index 024f912647295a..50943c1a17e9cb 100644
--- a/Lib/test/test_contextlib.py
+++ b/Lib/test/test_contextlib.py
@@ -7,6 +7,7 @@
import unittest
from contextlib import * # Tests __all__
from test import support
+from test.support import os_helper
import weakref
@@ -327,7 +328,7 @@ def testWithOpen(self):
1 / 0
self.assertTrue(f.closed)
finally:
- support.unlink(tfn)
+ os_helper.unlink(tfn)
class LockContextTestCase(unittest.TestCase):
diff --git a/Lib/test/test_dbm_ndbm.py b/Lib/test/test_dbm_ndbm.py
index 7ac75c5fea1b5e..278fca2cf23152 100644
--- a/Lib/test/test_dbm_ndbm.py
+++ b/Lib/test/test_dbm_ndbm.py
@@ -1,5 +1,6 @@
from test import support
-support.import_module("dbm.ndbm") #skip if not supported
+from test.support import import_helper
+import_helper.import_module("dbm.ndbm") #skip if not supported
import os
import unittest
import dbm.ndbm
diff --git a/Lib/test/test_getargs2.py b/Lib/test/test_getargs2.py
index 0dec5b1874e6d6..d39ea56ae9e9ca 100644
--- a/Lib/test/test_getargs2.py
+++ b/Lib/test/test_getargs2.py
@@ -3,8 +3,9 @@
import string
import sys
from test import support
+from test.support import import_helper
# Skip this test if the _testcapi module isn't available.
-_testcapi = support.import_module('_testcapi')
+_testcapi = import_helper.import_module('_testcapi')
from _testcapi import getargs_keywords, getargs_keyword_only
# > How about the following counterproposal. This also changes some of
diff --git a/Lib/test/test_gettext.py b/Lib/test/test_gettext.py
index baf300b0572495..df9eae39eac3e3 100644
--- a/Lib/test/test_gettext.py
+++ b/Lib/test/test_gettext.py
@@ -5,6 +5,7 @@
import unittest
from test import support
+from test.support import os_helper
# TODO:
@@ -129,14 +130,14 @@ def setUp(self):
fp.write(base64.decodebytes(UMO_DATA))
with open(MMOFILE, 'wb') as fp:
fp.write(base64.decodebytes(MMO_DATA))
- self.env = support.EnvironmentVarGuard()
+ self.env = os_helper.EnvironmentVarGuard()
self.env['LANGUAGE'] = 'xx'
gettext._translations.clear()
def tearDown(self):
self.env.__exit__()
del self.env
- support.rmtree(os.path.split(LOCALEDIR)[0])
+ os_helper.rmtree(os.path.split(LOCALEDIR)[0])
GNU_MO_DATA_ISSUE_17898 = b'''\
3hIElQAAAAABAAAAHAAAACQAAAAAAAAAAAAAAAAAAAAsAAAAggAAAC0AAAAAUGx1cmFsLUZvcm1z
diff --git a/Lib/test/test_glob.py b/Lib/test/test_glob.py
index f8158523a04695..96db31b26814b1 100644
--- a/Lib/test/test_glob.py
+++ b/Lib/test/test_glob.py
@@ -4,8 +4,8 @@
import sys
import unittest
-from test.support import (TESTFN, skip_unless_symlink,
- can_symlink, create_empty_file, change_cwd)
+from test.support.os_helper import (TESTFN, skip_unless_symlink,
+ can_symlink, create_empty_file, change_cwd)
class GlobTests(unittest.TestCase):
diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py
index a04cf65945e935..f4a83d2e7a13a1 100644
--- a/Lib/test/test_import/__init__.py
+++ b/Lib/test/test_import/__init__.py
@@ -19,11 +19,12 @@
from unittest import mock
import test.support
-from test.support import (
- TESTFN, forget, is_jython,
- make_legacy_pyc, rmtree, swap_attr, swap_item, temp_umask,
- unlink, unload, cpython_only, TESTFN_UNENCODABLE,
- temp_dir, DirsOnSysPath)
+from test.support import os_helper
+from test.support import (is_jython, swap_attr, swap_item, cpython_only)
+from test.support.import_helper import (
+ forget, make_legacy_pyc, unlink, unload, DirsOnSysPath)
+from test.support.os_helper import (
+ TESTFN, rmtree, temp_umask, TESTFN_UNENCODABLE, temp_dir)
from test.support import script_helper
from test.support import threading_helper
from test.test_importlib.util import uncache
@@ -997,22 +998,22 @@ class TestSymbolicallyLinkedPackage(unittest.TestCase):
tagged = package_name + '-tagged'
def setUp(self):
- test.support.rmtree(self.tagged)
- test.support.rmtree(self.package_name)
+ os_helper.rmtree(self.tagged)
+ os_helper.rmtree(self.package_name)
self.orig_sys_path = sys.path[:]
# create a sample package; imagine you have a package with a tag and
# you want to symbolically link it from its untagged name.
os.mkdir(self.tagged)
- self.addCleanup(test.support.rmtree, self.tagged)
+ self.addCleanup(os_helper.rmtree, self.tagged)
init_file = os.path.join(self.tagged, '__init__.py')
- test.support.create_empty_file(init_file)
+ os_helper.create_empty_file(init_file)
assert os.path.exists(init_file)
# now create a symlink to the tagged package
# sample -> sample-tagged
os.symlink(self.tagged, self.package_name, target_is_directory=True)
- self.addCleanup(test.support.unlink, self.package_name)
+ self.addCleanup(os_helper.unlink, self.package_name)
importlib.invalidate_caches()
self.assertEqual(os.path.isdir(self.package_name), True)
@@ -1027,7 +1028,7 @@ def tearDown(self):
not hasattr(sys, 'getwindowsversion')
or sys.getwindowsversion() >= (6, 0),
"Windows Vista or later required")
- @test.support.skip_unless_symlink
+ @os_helper.skip_unless_symlink
def test_symlinked_dir_importable(self):
# make sure sample can only be imported from the current directory.
sys.path[:] = ['.']
diff --git a/Lib/test/test_locale.py b/Lib/test/test_locale.py
index 2863d200e25c2e..bd08c4f3007fc7 100644
--- a/Lib/test/test_locale.py
+++ b/Lib/test/test_locale.py
@@ -1,4 +1,5 @@
-from test.support import verbose, is_android, check_warnings
+from test.support import verbose, is_android
+from test.support.warnings_helper import check_warnings
import unittest
import locale
import sys
diff --git a/Lib/test/test_mailbox.py b/Lib/test/test_mailbox.py
index 6f891d413cd8f1..346c9f1c559cd5 100644
--- a/Lib/test/test_mailbox.py
+++ b/Lib/test/test_mailbox.py
@@ -9,6 +9,7 @@
import io
import tempfile
from test import support
+from test.support import os_helper
import unittest
import textwrap
import mailbox
@@ -38,9 +39,9 @@ def _check_sample(self, msg):
def _delete_recursively(self, target):
# Delete a file or delete a directory recursively
if os.path.isdir(target):
- support.rmtree(target)
+ os_helper.rmtree(target)
elif os.path.exists(target):
- support.unlink(target)
+ os_helper.unlink(target)
class TestMailbox(TestBase):
@@ -51,7 +52,7 @@ class TestMailbox(TestBase):
_template = 'From: foo\n\n%s\n'
def setUp(self):
- self._path = support.TESTFN
+ self._path = os_helper.TESTFN
self._delete_recursively(self._path)
self._box = self._factory(self._path)
@@ -926,7 +927,7 @@ def refreshed():
# the mtime and should cause a re-read. Note that "sleep
# emulation" is still in effect, as skewfactor is -3.
filename = os.path.join(self._path, 'cur', 'stray-file')
- support.create_empty_file(filename)
+ os_helper.create_empty_file(filename)
os.unlink(filename)
self._box._refresh()
self.assertTrue(refreshed())
@@ -980,7 +981,7 @@ def tearDown(self):
self._box.close()
self._delete_recursively(self._path)
for lock_remnant in glob.glob(glob.escape(self._path) + '.*'):
- support.unlink(lock_remnant)
+ os_helper.unlink(lock_remnant)
def assertMailboxEmpty(self):
with open(self._path) as f:
@@ -1312,7 +1313,7 @@ def tearDown(self):
self._box.close()
self._delete_recursively(self._path)
for lock_remnant in glob.glob(glob.escape(self._path) + '.*'):
- support.unlink(lock_remnant)
+ os_helper.unlink(lock_remnant)
def test_labels(self):
# Get labels from the mailbox
@@ -1369,7 +1370,7 @@ class TestMessage(TestBase, unittest.TestCase):
_factory = mailbox.Message # Overridden by subclasses to reuse tests
def setUp(self):
- self._path = support.TESTFN
+ self._path = os_helper.TESTFN
def tearDown(self):
self._delete_recursively(self._path)
@@ -2019,7 +2020,7 @@ def _test_close(self, proxy):
class TestProxyFile(TestProxyFileBase, unittest.TestCase):
def setUp(self):
- self._path = support.TESTFN
+ self._path = os_helper.TESTFN
self._file = open(self._path, 'wb+')
def tearDown(self):
@@ -2068,7 +2069,7 @@ def test_close(self):
class TestPartialFile(TestProxyFileBase, unittest.TestCase):
def setUp(self):
- self._path = support.TESTFN
+ self._path = os_helper.TESTFN
self._file = open(self._path, 'wb+')
def tearDown(self):
@@ -2131,11 +2132,11 @@ class MaildirTestCase(unittest.TestCase):
def setUp(self):
# create a new maildir mailbox to work with:
- self._dir = support.TESTFN
+ self._dir = os_helper.TESTFN
if os.path.isdir(self._dir):
- support.rmtree(self._dir)
+ os_helper.rmtree(self._dir)
elif os.path.isfile(self._dir):
- support.unlink(self._dir)
+ os_helper.unlink(self._dir)
os.mkdir(self._dir)
os.mkdir(os.path.join(self._dir, "cur"))
os.mkdir(os.path.join(self._dir, "tmp"))
@@ -2145,10 +2146,10 @@ def setUp(self):
def tearDown(self):
list(map(os.unlink, self._msgfiles))
- support.rmdir(os.path.join(self._dir, "cur"))
- support.rmdir(os.path.join(self._dir, "tmp"))
- support.rmdir(os.path.join(self._dir, "new"))
- support.rmdir(self._dir)
+ os_helper.rmdir(os.path.join(self._dir, "cur"))
+ os_helper.rmdir(os.path.join(self._dir, "tmp"))
+ os_helper.rmdir(os.path.join(self._dir, "new"))
+ os_helper.rmdir(self._dir)
def createMessage(self, dir, mbox=False):
t = int(time.time() % 1000000)
@@ -2174,7 +2175,7 @@ def test_empty_maildir(self):
"""Test an empty maildir mailbox"""
# Test for regression on bug #117490:
# Make sure the boxes attribute actually gets set.
- self.mbox = mailbox.Maildir(support.TESTFN)
+ self.mbox = mailbox.Maildir(os_helper.TESTFN)
#self.assertTrue(hasattr(self.mbox, "boxes"))
#self.assertEqual(len(self.mbox.boxes), 0)
self.assertIsNone(self.mbox.next())
@@ -2182,7 +2183,7 @@ def test_empty_maildir(self):
def test_nonempty_maildir_cur(self):
self.createMessage("cur")
- self.mbox = mailbox.Maildir(support.TESTFN)
+ self.mbox = mailbox.Maildir(os_helper.TESTFN)
#self.assertEqual(len(self.mbox.boxes), 1)
self.assertIsNotNone(self.mbox.next())
self.assertIsNone(self.mbox.next())
@@ -2190,7 +2191,7 @@ def test_nonempty_maildir_cur(self):
def test_nonempty_maildir_new(self):
self.createMessage("new")
- self.mbox = mailbox.Maildir(support.TESTFN)
+ self.mbox = mailbox.Maildir(os_helper.TESTFN)
#self.assertEqual(len(self.mbox.boxes), 1)
self.assertIsNotNone(self.mbox.next())
self.assertIsNone(self.mbox.next())
@@ -2199,7 +2200,7 @@ def test_nonempty_maildir_new(self):
def test_nonempty_maildir_both(self):
self.createMessage("cur")
self.createMessage("new")
- self.mbox = mailbox.Maildir(support.TESTFN)
+ self.mbox = mailbox.Maildir(os_helper.TESTFN)
#self.assertEqual(len(self.mbox.boxes), 2)
self.assertIsNotNone(self.mbox.next())
self.assertIsNotNone(self.mbox.next())
diff --git a/Lib/test/test_multiprocessing_main_handling.py b/Lib/test/test_multiprocessing_main_handling.py
index be1ff10e03a55a..510d8d3a7597e1 100644
--- a/Lib/test/test_multiprocessing_main_handling.py
+++ b/Lib/test/test_multiprocessing_main_handling.py
@@ -1,7 +1,8 @@
# tests __main__ module handling in multiprocessing
from test import support
+from test.support import import_helper
# Skip tests if _multiprocessing wasn't built.
-support.import_module('_multiprocessing')
+import_helper.import_module('_multiprocessing')
import importlib
import importlib.machinery
@@ -11,6 +12,7 @@
import os.path
import py_compile
+from test.support import os_helper
from test.support.script_helper import (
make_pkg, make_script, make_zip_pkg, make_zip_script,
assert_python_ok)
@@ -167,12 +169,12 @@ def _check_script(self, script_name, *cmd_line_switches):
self._check_output(script_name, rc, out, err)
def test_basic_script(self):
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
script_name = _make_test_script(script_dir, 'script')
self._check_script(script_name)
def test_basic_script_no_suffix(self):
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
script_name = _make_test_script(script_dir, 'script',
omit_suffix=True)
self._check_script(script_name)
@@ -183,7 +185,7 @@ def test_ipython_workaround(self):
# a workaround for that case
# See https://github.com/ipython/ipython/issues/4698
source = test_source_main_skipped_in_children
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
script_name = _make_test_script(script_dir, 'ipython',
source=source)
self._check_script(script_name)
@@ -193,33 +195,33 @@ def test_ipython_workaround(self):
self._check_script(script_no_suffix)
def test_script_compiled(self):
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
script_name = _make_test_script(script_dir, 'script')
py_compile.compile(script_name, doraise=True)
os.remove(script_name)
- pyc_file = support.make_legacy_pyc(script_name)
+ pyc_file = import_helper.make_legacy_pyc(script_name)
self._check_script(pyc_file)
def test_directory(self):
source = self.main_in_children_source
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
script_name = _make_test_script(script_dir, '__main__',
source=source)
self._check_script(script_dir)
def test_directory_compiled(self):
source = self.main_in_children_source
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
script_name = _make_test_script(script_dir, '__main__',
source=source)
py_compile.compile(script_name, doraise=True)
os.remove(script_name)
- pyc_file = support.make_legacy_pyc(script_name)
+ pyc_file = import_helper.make_legacy_pyc(script_name)
self._check_script(script_dir)
def test_zipfile(self):
source = self.main_in_children_source
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
script_name = _make_test_script(script_dir, '__main__',
source=source)
zip_name, run_name = make_zip_script(script_dir, 'test_zip', script_name)
@@ -227,7 +229,7 @@ def test_zipfile(self):
def test_zipfile_compiled(self):
source = self.main_in_children_source
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
script_name = _make_test_script(script_dir, '__main__',
source=source)
compiled_name = py_compile.compile(script_name, doraise=True)
@@ -235,7 +237,7 @@ def test_zipfile_compiled(self):
self._check_script(zip_name)
def test_module_in_package(self):
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
pkg_dir = os.path.join(script_dir, 'test_pkg')
make_pkg(pkg_dir)
script_name = _make_test_script(pkg_dir, 'check_sibling')
@@ -244,20 +246,20 @@ def test_module_in_package(self):
self._check_script(launch_name)
def test_module_in_package_in_zipfile(self):
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
zip_name, run_name = _make_test_zip_pkg(script_dir, 'test_zip', 'test_pkg', 'script')
launch_name = _make_launch_script(script_dir, 'launch', 'test_pkg.script', zip_name)
self._check_script(launch_name)
def test_module_in_subpackage_in_zipfile(self):
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
zip_name, run_name = _make_test_zip_pkg(script_dir, 'test_zip', 'test_pkg', 'script', depth=2)
launch_name = _make_launch_script(script_dir, 'launch', 'test_pkg.test_pkg.script', zip_name)
self._check_script(launch_name)
def test_package(self):
source = self.main_in_children_source
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
pkg_dir = os.path.join(script_dir, 'test_pkg')
make_pkg(pkg_dir)
script_name = _make_test_script(pkg_dir, '__main__',
@@ -267,14 +269,14 @@ def test_package(self):
def test_package_compiled(self):
source = self.main_in_children_source
- with support.temp_dir() as script_dir:
+ with os_helper.temp_dir() as script_dir:
pkg_dir = os.path.join(script_dir, 'test_pkg')
make_pkg(pkg_dir)
script_name = _make_test_script(pkg_dir, '__main__',
source=source)
compiled_name = py_compile.compile(script_name, doraise=True)
os.remove(script_name)
- pyc_file = support.make_legacy_pyc(script_name)
+ pyc_file = import_helper.make_legacy_pyc(script_name)
launch_name = _make_launch_script(script_dir, 'launch', 'test_pkg')
self._check_script(launch_name)
diff --git a/Lib/test/test_osx_env.py b/Lib/test/test_osx_env.py
index 8a3bc5a46e547b..80198edcb80b73 100644
--- a/Lib/test/test_osx_env.py
+++ b/Lib/test/test_osx_env.py
@@ -2,7 +2,7 @@
Test suite for OS X interpreter environment variables.
"""
-from test.support import EnvironmentVarGuard
+from test.support.os_helper import EnvironmentVarGuard
import subprocess
import sys
import sysconfig
diff --git a/Lib/test/test_pkgutil.py b/Lib/test/test_pkgutil.py
index b162f9949ff697..bf9722a229611d 100644
--- a/Lib/test/test_pkgutil.py
+++ b/Lib/test/test_pkgutil.py
@@ -1,4 +1,6 @@
-from test.support import run_unittest, unload, check_warnings, CleanImport
+from test.support import run_unittest
+from test.support.import_helper import unload, CleanImport
+from test.support.warnings_helper import check_warnings
import unittest
import sys
import importlib
diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py
index e82a53c533df03..e5038d2e7f10a2 100644
--- a/Lib/test/test_plistlib.py
+++ b/Lib/test/test_plistlib.py
@@ -10,6 +10,7 @@
import binascii
import collections
from test import support
+from test.support import os_helper
from io import BytesIO
from plistlib import UID
@@ -110,7 +111,7 @@ class TestPlistlib(unittest.TestCase):
def tearDown(self):
try:
- os.unlink(support.TESTFN)
+ os.unlink(os_helper.TESTFN)
except:
pass
@@ -148,10 +149,10 @@ def test_create(self):
def test_io(self):
pl = self._create()
- with open(support.TESTFN, 'wb') as fp:
+ with open(os_helper.TESTFN, 'wb') as fp:
plistlib.dump(pl, fp)
- with open(support.TESTFN, 'rb') as fp:
+ with open(os_helper.TESTFN, 'rb') as fp:
pl2 = plistlib.load(fp)
self.assertEqual(dict(pl), dict(pl2))
diff --git a/Lib/test/test_ttk_guionly.py b/Lib/test/test_ttk_guionly.py
index 462665db5f3e75..abb26433652f17 100644
--- a/Lib/test/test_ttk_guionly.py
+++ b/Lib/test/test_ttk_guionly.py
@@ -1,8 +1,9 @@
import unittest
from test import support
+from test.support import import_helper
# Skip this test if _tkinter wasn't built.
-support.import_module('_tkinter')
+import_helper.import_module('_tkinter')
# Skip test if tk cannot be initialized.
support.requires('gui')
diff --git a/Lib/test/test_turtle.py b/Lib/test/test_turtle.py
index 38448c791be66c..39b3d96fb43bbb 100644
--- a/Lib/test/test_turtle.py
+++ b/Lib/test/test_turtle.py
@@ -1,8 +1,10 @@
import pickle
import unittest
from test import support
+from test.support import import_helper
-turtle = support.import_module('turtle')
+
+turtle = import_helper.import_module('turtle')
Vec2D = turtle.Vec2D
test_config = """\
diff --git a/Lib/test/test_xxtestfuzz.py b/Lib/test/test_xxtestfuzz.py
index 15924aaeff3851..3304c6e703a173 100644
--- a/Lib/test/test_xxtestfuzz.py
+++ b/Lib/test/test_xxtestfuzz.py
@@ -1,8 +1,8 @@
import faulthandler
-import test.support
+from test.support import import_helper
import unittest
-_xxtestfuzz = test.support.import_module('_xxtestfuzz')
+_xxtestfuzz = import_helper.import_module('_xxtestfuzz')
class TestFuzzer(unittest.TestCase):
diff --git a/Lib/test/test_zipimport.py b/Lib/test/test_zipimport.py
index 560286071c6902..8df7489f754d3f 100644
--- a/Lib/test/test_zipimport.py
+++ b/Lib/test/test_zipimport.py
@@ -9,6 +9,8 @@
import unittest.mock
from test import support
+from test.support import import_helper
+from test.support import os_helper
from zipfile import ZipFile, ZipInfo, ZIP_STORED, ZIP_DEFLATED
@@ -68,14 +70,14 @@ def setUp(self):
self.meta_path = sys.meta_path[:]
self.path_hooks = sys.path_hooks[:]
sys.path_importer_cache.clear()
- self.modules_before = support.modules_setup()
+ self.modules_before = import_helper.modules_setup()
def tearDown(self):
sys.path[:] = self.path
sys.meta_path[:] = self.meta_path
sys.path_hooks[:] = self.path_hooks
sys.path_importer_cache.clear()
- support.modules_cleanup(*self.modules_before)
+ import_helper.modules_cleanup(*self.modules_before)
class UncompressedZipImportTestCase(ImportHooksBaseTestCase):
@@ -92,7 +94,7 @@ def setUp(self):
def makeTree(self, files, dirName=TEMP_DIR):
# Create a filesystem based set of modules/packages
# defined by files under the directory dirName.
- self.addCleanup(support.rmtree, dirName)
+ self.addCleanup(os_helper.rmtree, dirName)
for name, (mtime, data) in files.items():
path = os.path.join(dirName, name)
@@ -110,7 +112,7 @@ def makeZip(self, files, zipName=TEMP_ZIP, **kw):
# Create a zip archive based set of modules/packages
# defined by files in the zip file zipName. If the
# key 'stuff' exists in kw it is prepended to the archive.
- self.addCleanup(support.unlink, zipName)
+ self.addCleanup(os_helper.unlink, zipName)
with ZipFile(zipName, "w") as z:
for name, (mtime, data) in files.items():
@@ -438,7 +440,7 @@ def testZipImporterMethods(self):
packdir2 + TESTMOD + pyc_ext: (NOW, test_pyc),
"spam" + pyc_ext: (NOW, test_pyc)}
- self.addCleanup(support.unlink, TEMP_ZIP)
+ self.addCleanup(os_helper.unlink, TEMP_ZIP)
with ZipFile(TEMP_ZIP, "w") as z:
for name, (mtime, data) in files.items():
zinfo = ZipInfo(name, time.localtime(mtime))
@@ -492,7 +494,7 @@ def testZipImporterMethodsInSubDirectory(self):
files = {packdir2 + "__init__" + pyc_ext: (NOW, test_pyc),
packdir2 + TESTMOD + pyc_ext: (NOW, test_pyc)}
- self.addCleanup(support.unlink, TEMP_ZIP)
+ self.addCleanup(os_helper.unlink, TEMP_ZIP)
with ZipFile(TEMP_ZIP, "w") as z:
for name, (mtime, data) in files.items():
zinfo = ZipInfo(name, time.localtime(mtime))
@@ -536,7 +538,7 @@ def testZipImporterMethodsInSubDirectory(self):
self.assertEqual(loader.get_filename(mod_name), mod.__file__)
def testGetData(self):
- self.addCleanup(support.unlink, TEMP_ZIP)
+ self.addCleanup(os_helper.unlink, TEMP_ZIP)
with ZipFile(TEMP_ZIP, "w") as z:
z.compression = self.compression
name = "testdata.dat"
@@ -644,11 +646,11 @@ def testTraceback(self):
files = {TESTMOD + ".py": (NOW, raise_src)}
self.doTest(None, files, TESTMOD, call=self.doTraceback)
- @unittest.skipIf(support.TESTFN_UNENCODABLE is None,
+ @unittest.skipIf(os_helper.TESTFN_UNENCODABLE is None,
"need an unencodable filename")
def testUnencodable(self):
- filename = support.TESTFN_UNENCODABLE + ".zip"
- self.addCleanup(support.unlink, filename)
+ filename = os_helper.TESTFN_UNENCODABLE + ".zip"
+ self.addCleanup(os_helper.unlink, filename)
with ZipFile(filename, "w") as z:
zinfo = ZipInfo(TESTMOD + ".py", time.localtime(NOW))
zinfo.compress_type = self.compression
@@ -656,8 +658,8 @@ def testUnencodable(self):
zipimport.zipimporter(filename).load_module(TESTMOD)
def testBytesPath(self):
- filename = support.TESTFN + ".zip"
- self.addCleanup(support.unlink, filename)
+ filename = os_helper.TESTFN + ".zip"
+ self.addCleanup(os_helper.unlink, filename)
with ZipFile(filename, "w") as z:
zinfo = ZipInfo(TESTMOD + ".py", time.localtime(NOW))
zinfo.compress_type = self.compression
@@ -709,12 +711,12 @@ def testFilenameTooLong(self):
self.assertZipFailure('A' * 33000)
def testEmptyFile(self):
- support.unlink(TESTMOD)
- support.create_empty_file(TESTMOD)
+ os_helper.unlink(TESTMOD)
+ os_helper.create_empty_file(TESTMOD)
self.assertZipFailure(TESTMOD)
def testFileUnreadable(self):
- support.unlink(TESTMOD)
+ os_helper.unlink(TESTMOD)
fd = os.open(TESTMOD, os.O_CREAT, 000)
try:
os.close(fd)
@@ -725,10 +727,10 @@ def testFileUnreadable(self):
# If we leave "the read-only bit" set on Windows, nothing can
# delete TESTMOD, and later tests suffer bogus failures.
os.chmod(TESTMOD, 0o666)
- support.unlink(TESTMOD)
+ os_helper.unlink(TESTMOD)
def testNotZipFile(self):
- support.unlink(TESTMOD)
+ os_helper.unlink(TESTMOD)
fp = open(TESTMOD, 'w+')
fp.write('a' * 22)
fp.close()
@@ -736,7 +738,7 @@ def testNotZipFile(self):
# XXX: disabled until this works on Big-endian machines
def _testBogusZipFile(self):
- support.unlink(TESTMOD)
+ os_helper.unlink(TESTMOD)
fp = open(TESTMOD, 'w+')
fp.write(struct.pack('=I', 0x06054B50))
fp.write('a' * 18)
@@ -771,7 +773,7 @@ def test_main():
BadFileZipImportTestCase,
)
finally:
- support.unlink(TESTMOD)
+ os_helper.unlink(TESTMOD)
if __name__ == "__main__":
test_main()
From 3d19985fe18a48506b97cdcfd5d46ae2401e426d Mon Sep 17 00:00:00 2001
From: Chris Jerdonek
Date: Thu, 9 Jul 2020 06:27:23 -0700
Subject: [PATCH 016/197] bpo-29590: fix stack trace for gen.throw() with yield
from (#19896)
* Add failing test.
* bpo-29590: fix stack trace for gen.throw() with yield from (GH-NNNN)
When gen.throw() is called on a generator after a "yield from", the
intermediate stack trace entries are lost. This commit fixes that.
---
Lib/test/test_generators.py | 49 +++++++++++++++++++
.../2020-05-03-22-26-00.bpo-29590.aRz3l7.rst | 2 +
Objects/genobject.c | 10 ++++
3 files changed, 61 insertions(+)
create mode 100644 Misc/NEWS.d/next/Core and Builtins/2020-05-03-22-26-00.bpo-29590.aRz3l7.rst
diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py
index bf482213c178a5..3bf152280868e8 100644
--- a/Lib/test/test_generators.py
+++ b/Lib/test/test_generators.py
@@ -415,6 +415,55 @@ def g():
gen.throw(ValueError)
+class GeneratorStackTraceTest(unittest.TestCase):
+
+ def check_stack_names(self, frame, expected):
+ names = []
+ while frame:
+ name = frame.f_code.co_name
+ # Stop checking frames when we get to our test helper.
+ if name.startswith('check_') or name.startswith('call_'):
+ break
+
+ names.append(name)
+ frame = frame.f_back
+
+ self.assertEqual(names, expected)
+
+ def check_yield_from_example(self, call_method):
+ def f():
+ self.check_stack_names(sys._getframe(), ['f', 'g'])
+ try:
+ yield
+ except Exception:
+ pass
+ self.check_stack_names(sys._getframe(), ['f', 'g'])
+
+ def g():
+ self.check_stack_names(sys._getframe(), ['g'])
+ yield from f()
+ self.check_stack_names(sys._getframe(), ['g'])
+
+ gen = g()
+ gen.send(None)
+ try:
+ call_method(gen)
+ except StopIteration:
+ pass
+
+ def test_send_with_yield_from(self):
+ def call_send(gen):
+ gen.send(None)
+
+ self.check_yield_from_example(call_send)
+
+ def test_throw_with_yield_from(self):
+ def call_throw(gen):
+ gen.throw(RuntimeError)
+
+ self.check_yield_from_example(call_throw)
+
+
class YieldFromTests(unittest.TestCase):
def test_generator_gi_yieldfrom(self):
def a():
diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-05-03-22-26-00.bpo-29590.aRz3l7.rst b/Misc/NEWS.d/next/Core and Builtins/2020-05-03-22-26-00.bpo-29590.aRz3l7.rst
new file mode 100644
index 00000000000000..2570c4f2c7c0fd
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2020-05-03-22-26-00.bpo-29590.aRz3l7.rst
@@ -0,0 +1,2 @@
+Make the stack trace correct after calling :meth:`generator.throw`
+on a generator that has yielded from a ``yield from``.
diff --git a/Objects/genobject.c b/Objects/genobject.c
index 6a68c9484a6ae9..a379fa6088e16a 100644
--- a/Objects/genobject.c
+++ b/Objects/genobject.c
@@ -415,11 +415,21 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
}
if (PyGen_CheckExact(yf) || PyCoro_CheckExact(yf)) {
/* `yf` is a generator or a coroutine. */
+ PyThreadState *tstate = _PyThreadState_GET();
+ PyFrameObject *f = tstate->frame;
+
gen->gi_running = 1;
+ /* Since we are fast-tracking things by skipping the eval loop,
+ we need to update the current frame so the stack trace
+ will be reported correctly to the user. */
+ /* XXX We should probably be updating the current frame
+ somewhere in ceval.c. */
+ tstate->frame = gen->gi_frame;
/* Close the generator that we are currently iterating with
'yield from' or awaiting on with 'await'. */
ret = _gen_throw((PyGenObject *)yf, close_on_genexit,
typ, val, tb);
+ tstate->frame = f;
gen->gi_running = 0;
} else {
/* `yf` is an iterator or a coroutine-like object. */
From 10a0fd679ee429de4ef3ee706c55ecf845d4a470 Mon Sep 17 00:00:00 2001
From: Steve Dower
Date: Thu, 9 Jul 2020 18:52:43 +0100
Subject: [PATCH 017/197] bpo-41172: Fix check for compiler in test suite
(GH-21400)
---
Lib/test/support/__init__.py | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
index f8f60fb6c27b91..b21978a61cd2f1 100644
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -1673,9 +1673,15 @@ def missing_compiler_executable(cmd_names=[]):
missing.
"""
- from distutils import ccompiler, sysconfig, spawn
+ from distutils import ccompiler, sysconfig, spawn, errors
compiler = ccompiler.new_compiler()
sysconfig.customize_compiler(compiler)
+ if compiler.compiler_type == "msvc":
+ # MSVC has no executables, so check whether initialization succeeds
+ try:
+ compiler.initialize()
+ except errors.DistutilsPlatformError:
+ return "msvc"
for name in compiler.executables:
if cmd_names and name not in cmd_names:
continue
From ae339e3e7b2593c7d5c1438595aa88dd9ae081b4 Mon Sep 17 00:00:00 2001
From: E-Paine <63801254+E-Paine@users.noreply.github.com>
Date: Thu, 9 Jul 2020 21:18:34 +0200
Subject: [PATCH 018/197] Remove trailing >>> in enum docs (GH-21358)
The >>> as the last line serve no purpose and are not colored correctly by Sphinx.
---
Doc/library/enum.rst | 1 -
Misc/ACKS | 1 +
2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst
index 4b4f5eb1944cc5..b327a0ad15f96c 100644
--- a/Doc/library/enum.rst
+++ b/Doc/library/enum.rst
@@ -113,7 +113,6 @@ The *type* of an enumeration member is the enumeration it belongs to::
>>> isinstance(Color.GREEN, Color)
True
- >>>
Enum members also have a property that contains just their item name::
diff --git a/Misc/ACKS b/Misc/ACKS
index 641ef0cace00e2..b585769608f4e9 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -1266,6 +1266,7 @@ Richard Oudkerk
Russel Owen
Joonas Paalasmaa
Martin Packman
+Elisha Paine
Shriphani Palakodety
Julien Palard
Aviv Palivoda
From 4afdfebfb38506fba3e740e689d35a5a59d79edc Mon Sep 17 00:00:00 2001
From: Terry Jan Reedy
Date: Thu, 9 Jul 2020 18:08:33 -0400
Subject: [PATCH 019/197] bpo-37765: Add keywords to IDLE tab completions
(GH-15138)
Keywords are present in the main module tab completion lists generated by rlcompleter, which is used by REPLs on *nix. Add all keywords to IDLE's main module name list except those already added from builtins (True, False, and None) . This list may also be used by Show Completions on the Edit menu, and its hot key.
Rewrite Completions doc.
Co-authored-by: Cheryl Sabella
---
Doc/library/idle.rst | 90 ++++++++++---------
Lib/idlelib/NEWS.txt | 3 +
Lib/idlelib/autocomplete.py | 6 +-
Lib/idlelib/help.html | 85 ++++++++++--------
Lib/idlelib/idle_test/test_autocomplete.py | 7 +-
.../2020-07-07-18-44-30.bpo-37765.umc1o8.rst | 2 +
6 files changed, 110 insertions(+), 83 deletions(-)
create mode 100644 Misc/NEWS.d/next/IDLE/2020-07-07-18-44-30.bpo-37765.umc1o8.rst
diff --git a/Doc/library/idle.rst b/Doc/library/idle.rst
index b1192e7bb46552..75b6fa3861b23d 100644
--- a/Doc/library/idle.rst
+++ b/Doc/library/idle.rst
@@ -147,7 +147,7 @@ Go to Line
Clear any selection and update the line and column status.
Show Completions
- Open a scrollable list allowing selection of keywords and attributes. See
+ Open a scrollable list allowing selection of existing names. See
:ref:`Completions ` in the Editing and navigation section below.
Expand Word
@@ -469,52 +469,58 @@ are restricted to four spaces due to Tcl/Tk limitations.
See also the indent/dedent region commands on the
:ref:`Format menu `.
-
.. _completions:
Completions
^^^^^^^^^^^
-Completions are supplied for functions, classes, and attributes of classes,
-both built-in and user-defined. Completions are also provided for
-filenames.
-
-The AutoCompleteWindow (ACW) will open after a predefined delay (default is
-two seconds) after a '.' or (in a string) an os.sep is typed. If after one
-of those characters (plus zero or more other characters) a tab is typed
-the ACW will open immediately if a possible continuation is found.
-
-If there is only one possible completion for the characters entered, a
-:kbd:`Tab` will supply that completion without opening the ACW.
-
-'Show Completions' will force open a completions window, by default the
-:kbd:`C-space` will open a completions window. In an empty
-string, this will contain the files in the current directory. On a
-blank line, it will contain the built-in and user-defined functions and
-classes in the current namespaces, plus any modules imported. If some
-characters have been entered, the ACW will attempt to be more specific.
-
-If a string of characters is typed, the ACW selection will jump to the
-entry most closely matching those characters. Entering a :kbd:`tab` will
-cause the longest non-ambiguous match to be entered in the Editor window or
-Shell. Two :kbd:`tab` in a row will supply the current ACW selection, as
-will return or a double click. Cursor keys, Page Up/Down, mouse selection,
-and the scroll wheel all operate on the ACW.
-
-"Hidden" attributes can be accessed by typing the beginning of hidden
-name after a '.', e.g. '_'. This allows access to modules with
-``__all__`` set, or to class-private attributes.
-
-Completions and the 'Expand Word' facility can save a lot of typing!
-
-Completions are currently limited to those in the namespaces. Names in
-an Editor window which are not via ``__main__`` and :data:`sys.modules` will
-not be found. Run the module once with your imports to correct this situation.
-Note that IDLE itself places quite a few modules in sys.modules, so
-much can be found by default, e.g. the re module.
-
-If you don't like the ACW popping up unbidden, simply make the delay
-longer or disable the extension.
+Completions are supplied, when requested and available, for module
+names, attributes of classes or functions, or filenames. Each request
+method displays a completion box with existing names. (See tab
+completions below for an exception.) For any box, change the name
+being completed and the item highlighted in the box by
+typing and deleting characters; by hitting :kbd:`Up`, :kbd:`Down`,
+:kbd:`PageUp`, :kbd:`PageDown`, :kbd:`Home`, and :kbd:`End` keys;
+and by a single click within the box. Close the box with :kbd:`Escape`,
+:kbd:`Enter`, and double :kbd:`Tab` keys or clicks outside the box.
+A double click within the box selects and closes.
+
+One way to open a box is to type a key character and wait for a
+predefined interval. This defaults to 2 seconds; customize it
+in the settings dialog. (To prevent auto popups, set the delay to a
+large number of milliseconds, such as 100000000.) For imported module
+names or class or function attributes, type '.'.
+For filenames in the root directory, type :data:`os.sep` or
+data:`os.altsep` immediately after an opening quote. (On Windows,
+one can specify a drive first.) Move into subdirectories by typing a
+directory name and a separator.
+
+Instead of waiting, or after a box is closed, open a completion box
+immediately with Show Completions on the Edit menu. The default hot
+key is :kbd:`C-space`. If one types a prefix for the desired name
+before opening the box, the first match or near miss is made visible.
+The result is the same as if one enters a prefix
+after the box is displayed. Show Completions after a quote completes
+filenames in the current directory instead of a root directory.
+
+Hitting :kbd:`Tab` after a prefix usually has the same effect as Show
+Completions. (With no prefix, it indents.) However, if there is only
+one match to the prefix, that match is immediately added to the editor
+text without opening a box.
+
+Invoking 'Show Completions', or hitting :kbd:`Tab` after a prefix,
+outside of a string and without a preceding '.' opens a box with
+keywords, builtin names, and available module-level names.
+
+When editing code in an editor (as oppose to Shell), increase the
+available module-level names by running your code
+and not restarting the Shell thereafter. This is especially useful
+after adding imports at the top of a file. This also increases
+possible attribute completions.
+
+Completion boxes intially exclude names beginning with '_' or, for
+modules, not included in '__all__'. The hidden names can be accessed
+by typing '_' after '.', either before or after the box is opened.
.. _calltips:
diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt
index 7ae29af0b30ce3..1c5c03da86efc5 100644
--- a/Lib/idlelib/NEWS.txt
+++ b/Lib/idlelib/NEWS.txt
@@ -3,6 +3,9 @@ Released on 2020-10-05?
======================================
+bpo-37765: Add keywords to module name completion list. Rewrite
+Completions section of IDLE doc.
+
bpo-41152: The encoding of ``stdin``, ``stdout`` and ``stderr`` in IDLE
is now always UTF-8.
diff --git a/Lib/idlelib/autocomplete.py b/Lib/idlelib/autocomplete.py
index c623d45a153423..e1e9e17311eda1 100644
--- a/Lib/idlelib/autocomplete.py
+++ b/Lib/idlelib/autocomplete.py
@@ -4,6 +4,7 @@
pop up a list of candidates.
"""
import __main__
+import keyword
import os
import string
import sys
@@ -171,10 +172,13 @@ def fetch_completions(self, what, mode):
(what, mode), {})
else:
if mode == ATTRS:
- if what == "":
+ if what == "": # Main module names.
namespace = {**__main__.__builtins__.__dict__,
**__main__.__dict__}
bigl = eval("dir()", namespace)
+ kwds = (s for s in keyword.kwlist
+ if s not in {'True', 'False', 'None'})
+ bigl.extend(kwds)
bigl.sort()
if "__all__" in bigl:
smalll = sorted(eval("__all__", namespace))
diff --git a/Lib/idlelib/help.html b/Lib/idlelib/help.html
index 424c6b50f339e1..81ce5100bb8ad5 100644
--- a/Lib/idlelib/help.html
+++ b/Lib/idlelib/help.html
@@ -4,7 +4,7 @@
- IDLE — Python 3.9.0a4 documentation
+ IDLE — Python 3.10.0a0 documentation
@@ -17,7 +17,7 @@
@@ -71,7 +71,7 @@ Navigation
- 3.9.0a4 Documentation »
+ 3.10.0a0 Documentation »
The Python Standard Library »
@@ -201,7 +201,7 @@ Edit menu (Shell and Editor) in the Editing and navigation section below.
Expand Word Expand a prefix you have typed to match a full word in the same window;
@@ -465,38 +465,47 @@
Automatic indentation