From solipsis at pitrou.net Wed Aug 1 05:15:23 2018 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Wed, 01 Aug 2018 09:15:23 +0000 Subject: [Python-checkins] Daily reference leaks (4243df51fe43): sum=6 Message-ID: <20180801091523.1.7316055FC78368BE@psf.io> results for 4243df51fe43 on branch "default" -------------------------------------------- test_functools leaked [0, 3, 1] memory blocks, sum=4 test_multiprocessing_spawn leaked [3, -1, 0] memory blocks, sum=2 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/psf-users/antoine/refleaks/refloggi_yWj', '--timeout', '7200'] From webhook-mailer at python.org Wed Aug 1 10:16:52 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Wed, 01 Aug 2018 14:16:52 -0000 Subject: [Python-checkins] bpo-33499: Fix pymain_init_pycache_prefix() (GH-8596) Message-ID: https://github.com/python/cpython/commit/fc96437db4fa95dff99d43d4500beaf12bad5bff commit: fc96437db4fa95dff99d43d4500beaf12bad5bff branch: master author: Victor Stinner committer: GitHub date: 2018-08-01T16:16:46+02:00 summary: bpo-33499: Fix pymain_init_pycache_prefix() (GH-8596) Fix a memory leak in pymain_init_pycache_prefix() when PYTHONPYCACHEPREFIX and -X pycache_prefix are used. files: M Modules/main.c diff --git a/Modules/main.c b/Modules/main.c index 664a70ad5ebb..34069e1b2d31 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -1768,16 +1768,6 @@ pymain_init_tracemalloc(_PyCoreConfig *config) static _PyInitError pymain_init_pycache_prefix(_PyCoreConfig *config) { - wchar_t *env; - - int res = config_get_env_var_dup(config, &env, - L"PYTHONPYCACHEPREFIX", "PYTHONPYCACHEPREFIX"); - if (res < 0) { - return DECODE_LOCALE_ERR("PYTHONPYCACHEPREFIX", res); - } else if (env) { - config->pycache_prefix = env; - } - const wchar_t *xoption = config_get_xoption(config, L"pycache_prefix"); if (xoption) { const wchar_t *sep = wcschr(xoption, L'='); @@ -1790,6 +1780,16 @@ pymain_init_pycache_prefix(_PyCoreConfig *config) // -X pycache_prefix= can cancel the env var config->pycache_prefix = NULL; } + return _Py_INIT_OK(); + } + + wchar_t *env; + int res = config_get_env_var_dup(config, &env, + L"PYTHONPYCACHEPREFIX", "PYTHONPYCACHEPREFIX"); + if (res < 0) { + return DECODE_LOCALE_ERR("PYTHONPYCACHEPREFIX", res); + } else if (env) { + config->pycache_prefix = env; } return _Py_INIT_OK(); From webhook-mailer at python.org Wed Aug 1 10:41:30 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Wed, 01 Aug 2018 14:41:30 -0000 Subject: [Python-checkins] Define _Py_NO_RETURN for Microsoft C compiler (GH-8606) Message-ID: https://github.com/python/cpython/commit/cfc8831f5ed607048679427f7d76d6cb4f8a2e8a commit: cfc8831f5ed607048679427f7d76d6cb4f8a2e8a branch: master author: Victor Stinner committer: GitHub date: 2018-08-01T16:41:25+02:00 summary: Define _Py_NO_RETURN for Microsoft C compiler (GH-8606) files: M Include/pyerrors.h M Include/pylifecycle.h M Python/pylifecycle.c diff --git a/Include/pyerrors.h b/Include/pyerrors.h index 416d750d9b1f..4e2995469f3c 100644 --- a/Include/pyerrors.h +++ b/Include/pyerrors.h @@ -97,13 +97,15 @@ PyAPI_FUNC(void) PyErr_SetExcInfo(PyObject *, PyObject *, PyObject *); (defined(__GNUC_MAJOR__) && \ ((__GNUC_MAJOR__ >= 3) || \ (__GNUC_MAJOR__ == 2) && (__GNUC_MINOR__ >= 5))) -#define _Py_NO_RETURN __attribute__((__noreturn__)) +# define _Py_NO_RETURN __attribute__((__noreturn__)) +#elif defined(_MSC_VER) +# define _Py_NO_RETURN __declspec(noreturn) #else -#define _Py_NO_RETURN +# define _Py_NO_RETURN #endif /* Defined in Python/pylifecycle.c */ -PyAPI_FUNC(void) Py_FatalError(const char *message) _Py_NO_RETURN; +PyAPI_FUNC(void) _Py_NO_RETURN Py_FatalError(const char *message); #if defined(Py_DEBUG) || defined(Py_LIMITED_API) #define _PyErr_OCCURRED() PyErr_Occurred() diff --git a/Include/pylifecycle.h b/Include/pylifecycle.h index 78f01eef1166..c58c7ec95d9d 100644 --- a/Include/pylifecycle.h +++ b/Include/pylifecycle.h @@ -86,7 +86,7 @@ PyAPI_FUNC(void) Py_InitializeEx(int); #ifndef Py_LIMITED_API PyAPI_FUNC(_PyInitError) _Py_InitializeFromConfig( const _PyCoreConfig *config); -PyAPI_FUNC(void) _Py_FatalInitError(_PyInitError err) _Py_NO_RETURN; +PyAPI_FUNC(void) _Py_NO_RETURN _Py_FatalInitError(_PyInitError err); #endif PyAPI_FUNC(void) Py_Finalize(void); PyAPI_FUNC(int) Py_FinalizeEx(void); @@ -105,7 +105,7 @@ PyAPI_FUNC(void) _Py_PyAtExit(void (*func)(PyObject *), PyObject *); #endif PyAPI_FUNC(int) Py_AtExit(void (*func)(void)); -PyAPI_FUNC(void) Py_Exit(int) _Py_NO_RETURN; +PyAPI_FUNC(void) _Py_NO_RETURN Py_Exit(int); /* Restore signals that the interpreter has called SIG_IGN on to SIG_DFL. */ #ifndef Py_LIMITED_API diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 0729a5f83b52..63f461ab0432 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -2212,7 +2212,7 @@ call_ll_exitfuncs(void) fflush(stderr); } -void +void _Py_NO_RETURN Py_Exit(int sts) { if (Py_FinalizeEx() < 0) { From webhook-mailer at python.org Wed Aug 1 11:56:20 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Wed, 01 Aug 2018 15:56:20 -0000 Subject: [Python-checkins] bpo-34170: Add Python/coreconfig.c for _PyCoreConfig (GH-8607) Message-ID: https://github.com/python/cpython/commit/6c785c0ebdadc84d80a53d896c38fd7ada8ae1f6 commit: 6c785c0ebdadc84d80a53d896c38fd7ada8ae1f6 branch: master author: Victor Stinner committer: GitHub date: 2018-08-01T17:56:14+02:00 summary: bpo-34170: Add Python/coreconfig.c for _PyCoreConfig (GH-8607) * Add Include/coreconfig.h * Move config_*() and _PyCoreConfig_*() functions from Modules/main.c to a new Python/coreconfig.c file. * Inline _Py_ReadHashSeed() into config_init_hash_seed() * Move global configuration variables to coreconfig.c files: A Include/coreconfig.h A Python/coreconfig.c M Include/Python.h M Include/internal/pystate.h M Include/pylifecycle.h M Include/pystate.h M Makefile.pre.in M Modules/main.c M PCbuild/pythoncore.vcxproj M PCbuild/pythoncore.vcxproj.filters M Python/bltinmodule.c M Python/bootstrap_hash.c M Python/import.c M Python/pylifecycle.c diff --git a/Include/Python.h b/Include/Python.h index 1feb1531cc94..bf1ca6cfe98e 100644 --- a/Include/Python.h +++ b/Include/Python.h @@ -111,6 +111,7 @@ #include "codecs.h" #include "pyerrors.h" +#include "coreconfig.h" #include "pystate.h" #include "context.h" diff --git a/Include/coreconfig.h b/Include/coreconfig.h new file mode 100644 index 000000000000..4401729d76f7 --- /dev/null +++ b/Include/coreconfig.h @@ -0,0 +1,318 @@ +#ifndef Py_PYCORECONFIG_H +#define Py_PYCORECONFIG_H +#ifdef __cplusplus +extern "C" { +#endif + + +#ifndef Py_LIMITED_API +typedef struct { + const char *prefix; + const char *msg; + int user_err; +} _PyInitError; + +/* Almost all errors causing Python initialization to fail */ +#ifdef _MSC_VER + /* Visual Studio 2015 doesn't implement C99 __func__ in C */ +# define _Py_INIT_GET_FUNC() __FUNCTION__ +#else +# define _Py_INIT_GET_FUNC() __func__ +#endif + +#define _Py_INIT_OK() \ + (_PyInitError){.prefix = NULL, .msg = NULL, .user_err = 0} +#define _Py_INIT_ERR(MSG) \ + (_PyInitError){.prefix = _Py_INIT_GET_FUNC(), .msg = (MSG), .user_err = 0} +/* Error that can be fixed by the user like invalid input parameter. + Don't abort() the process on such error. */ +#define _Py_INIT_USER_ERR(MSG) \ + (_PyInitError){.prefix = _Py_INIT_GET_FUNC(), .msg = (MSG), .user_err = 1} +#define _Py_INIT_NO_MEMORY() _Py_INIT_USER_ERR("memory allocation failed") +#define _Py_INIT_FAILED(err) \ + (err.msg != NULL) + +#endif /* !defined(Py_LIMITED_API) */ + + +typedef struct { + /* Install signal handlers? Yes by default. */ + int install_signal_handlers; + + /* If greater than 0: use environment variables. + Set to 0 by -E command line option. If set to -1 (default), it is + set to !Py_IgnoreEnvironmentFlag. */ + int use_environment; + + int use_hash_seed; /* PYTHONHASHSEED=x */ + unsigned long hash_seed; + + const char *allocator; /* Memory allocator: PYTHONMALLOC */ + int dev_mode; /* PYTHONDEVMODE, -X dev */ + + /* Enable faulthandler? + Set to 1 by -X faulthandler and PYTHONFAULTHANDLER. -1 means unset. */ + int faulthandler; + + /* Enable tracemalloc? + Set by -X tracemalloc=N and PYTHONTRACEMALLOC. -1 means unset */ + int tracemalloc; + + int import_time; /* PYTHONPROFILEIMPORTTIME, -X importtime */ + int show_ref_count; /* -X showrefcount */ + int show_alloc_count; /* -X showalloccount */ + int dump_refs; /* PYTHONDUMPREFS */ + int malloc_stats; /* PYTHONMALLOCSTATS */ + int coerce_c_locale; /* PYTHONCOERCECLOCALE, -1 means unknown */ + int coerce_c_locale_warn; /* PYTHONCOERCECLOCALE=warn */ + + /* Enable UTF-8 mode? + Set by -X utf8 command line option and PYTHONUTF8 environment variable. + If set to -1 (default), inherit Py_UTF8Mode value. */ + int utf8_mode; + + wchar_t *pycache_prefix; /* PYTHONPYCACHEPREFIX, -X pycache_prefix=PATH */ + + wchar_t *program_name; /* Program name, see also Py_GetProgramName() */ + int argc; /* Number of command line arguments, + -1 means unset */ + wchar_t **argv; /* Command line arguments */ + wchar_t *program; /* argv[0] or "" */ + + int nxoption; /* Number of -X options */ + wchar_t **xoptions; /* -X options */ + + int nwarnoption; /* Number of warnings options */ + wchar_t **warnoptions; /* Warnings options */ + + /* Path configuration inputs */ + wchar_t *module_search_path_env; /* PYTHONPATH environment variable */ + wchar_t *home; /* PYTHONHOME environment variable, + see also Py_SetPythonHome(). */ + + /* Path configuration outputs */ + int nmodule_search_path; /* Number of sys.path paths, + -1 means unset */ + wchar_t **module_search_paths; /* sys.path paths */ + wchar_t *executable; /* sys.executable */ + wchar_t *prefix; /* sys.prefix */ + wchar_t *base_prefix; /* sys.base_prefix */ + wchar_t *exec_prefix; /* sys.exec_prefix */ + wchar_t *base_exec_prefix; /* sys.base_exec_prefix */ +#ifdef MS_WINDOWS + wchar_t *dll_path; /* Windows DLL path */ +#endif + + /* If greater than 0, enable isolated mode: sys.path contains + neither the script's directory nor the user's site-packages directory. + + Set to 1 by the -I command line option. If set to -1 (default), inherit + Py_IsolatedFlag value. */ + int isolated; + + /* If equal to zero, disable the import of the module site and the + site-dependent manipulations of sys.path that it entails. Also disable + these manipulations if site is explicitly imported later (call + site.main() if you want them to be triggered). + + Set to 0 by the -S command line option. If set to -1 (default), it is + set to !Py_NoSiteFlag. */ + int site_import; + + /* Bytes warnings: + + * If equal to 1, issue a warning when comparing bytes or bytearray with + str or bytes with int. + * If equal or greater to 2, issue an error. + + Incremented by the -b command line option. If set to -1 (default), inherit + Py_BytesWarningFlag value. */ + int bytes_warning; + + /* If greater than 0, enable inspect: when a script is passed as first + argument or the -c option is used, enter interactive mode after + executing the script or the command, even when sys.stdin does not appear + to be a terminal. + + Incremented by the -i command line option. Set to 1 if the PYTHONINSPECT + environment variable is non-empty. If set to -1 (default), inherit + Py_InspectFlag value. */ + int inspect; + + /* If greater than 0: enable the interactive mode (REPL). + + Incremented by the -i command line option. If set to -1 (default), + inherit Py_InteractiveFlag value. */ + int interactive; + + /* Optimization level. + + Incremented by the -O command line option. Set by the PYTHONOPTIMIZE + environment variable. If set to -1 (default), inherit Py_OptimizeFlag + value. */ + int optimization_level; + + /* If greater than 0, enable the debug mode: turn on parser debugging + output (for expert only, depending on compilation options). + + Incremented by the -d command line option. Set by the PYTHONDEBUG + environment variable. If set to -1 (default), inherit Py_DebugFlag + value. */ + int parser_debug; + + /* If equal to 0, Python won't try to write ``.pyc`` files on the + import of source modules. + + Set to 0 by the -B command line option and the PYTHONDONTWRITEBYTECODE + environment variable. If set to -1 (default), it is set to + !Py_DontWriteBytecodeFlag. */ + int write_bytecode; + + /* If greater than 0, enable the verbose mode: print a message each time a + module is initialized, showing the place (filename or built-in module) + from which it is loaded. + + If greater or equal to 2, print a message for each file that is checked + for when searching for a module. Also provides information on module + cleanup at exit. + + Incremented by the -v option. Set by the PYTHONVERBOSE environment + variable. If set to -1 (default), inherit Py_VerboseFlag value. */ + int verbose; + + /* If greater than 0, enable the quiet mode: Don't display the copyright + and version messages even in interactive mode. + + Incremented by the -q option. If set to -1 (default), inherit + Py_QuietFlag value. */ + int quiet; + + /* If greater than 0, don't add the user site-packages directory to + sys.path. + + Set to 0 by the -s and -I command line options , and the PYTHONNOUSERSITE + environment variable. If set to -1 (default), it is set to + !Py_NoUserSiteDirectory. */ + int user_site_directory; + + /* If equal to 0, enable unbuffered mode: force the stdout and stderr + streams to be unbuffered. + + Set to 0 by the -u option. Set by the PYTHONUNBUFFERED environment + variable. + If set to -1 (default), it is set to !Py_UnbufferedStdioFlag. */ + int buffered_stdio; + +#ifdef MS_WINDOWS + /* If greater than 1, use the "mbcs" encoding instead of the UTF-8 + encoding for the filesystem encoding. + + Set to 1 if the PYTHONLEGACYWINDOWSFSENCODING environment variable is + set to a non-empty string. If set to -1 (default), inherit + Py_LegacyWindowsFSEncodingFlag value. + + See PEP 529 for more details. */ + int legacy_windows_fs_encoding; + + /* If greater than zero, use io.FileIO instead of WindowsConsoleIO for sys + standard streams. + + Set to 1 if the PYTHONLEGACYWINDOWSSTDIO environment variable is set to + a non-empty string. If set to -1 (default), inherit + Py_LegacyWindowsStdioFlag value. + + See PEP 528 for more details. */ + int legacy_windows_stdio; +#endif + + /* --- Private fields -------- */ + + /* Install importlib? If set to 0, importlib is not initialized at all. + Needed by freeze_importlib. */ + int _install_importlib; + + /* Value of the --check-hash-based-pycs configure option. Valid values: + + - "default" means the 'check_source' flag in hash-based pycs + determines invalidation + - "always" causes the interpreter to hash the source file for + invalidation regardless of value of 'check_source' bit + - "never" causes the interpreter to always assume hash-based pycs are + valid + + Set by the --check-hash-based-pycs command line option. + If set to NULL (default), inherit _Py_CheckHashBasedPycsMode value. + + See PEP 552 "Deterministic pycs" for more details. */ + const char *_check_hash_pycs_mode; + + /* If greater than 0, suppress _PyPathConfig_Calculate() warnings. + + If set to -1 (default), inherit Py_FrozenFlag value. */ + int _frozen; + +} _PyCoreConfig; + +#ifdef MS_WINDOWS +# define _PyCoreConfig_WINDOWS_INIT \ + .legacy_windows_fs_encoding = -1, \ + .legacy_windows_stdio = -1, +#else +# define _PyCoreConfig_WINDOWS_INIT +#endif + +#define _PyCoreConfig_INIT \ + (_PyCoreConfig){ \ + .install_signal_handlers = 1, \ + .use_environment = -1, \ + .use_hash_seed = -1, \ + .faulthandler = -1, \ + .tracemalloc = -1, \ + .coerce_c_locale = -1, \ + .utf8_mode = -1, \ + .argc = -1, \ + .nmodule_search_path = -1, \ + .isolated = -1, \ + .site_import = -1, \ + .bytes_warning = -1, \ + .inspect = -1, \ + .interactive = -1, \ + .optimization_level = -1, \ + .parser_debug= -1, \ + .write_bytecode = -1, \ + .verbose = -1, \ + .quiet = -1, \ + .user_site_directory = -1, \ + .buffered_stdio = -1, \ + _PyCoreConfig_WINDOWS_INIT \ + ._install_importlib = 1, \ + ._frozen = -1} +/* Note: _PyCoreConfig_INIT sets other fields to 0/NULL */ + + +#ifndef Py_LIMITED_API +PyAPI_FUNC(_PyInitError) _PyCoreConfig_Read(_PyCoreConfig *config); +PyAPI_FUNC(void) _PyCoreConfig_Clear(_PyCoreConfig *); +PyAPI_FUNC(int) _PyCoreConfig_Copy( + _PyCoreConfig *config, + const _PyCoreConfig *config2); +PyAPI_FUNC(_PyInitError) _PyCoreConfig_InitPathConfig(_PyCoreConfig *config); +PyAPI_FUNC(_PyInitError) _PyCoreConfig_SetPathConfig( + const _PyCoreConfig *config); +PyAPI_FUNC(void) _PyCoreConfig_GetGlobalConfig(_PyCoreConfig *config); +PyAPI_FUNC(void) _PyCoreConfig_SetGlobalConfig(const _PyCoreConfig *config); +PyAPI_FUNC(const char*) _PyCoreConfig_GetEnv( + const _PyCoreConfig *config, + const char *name); +PyAPI_FUNC(int) _PyCoreConfig_GetEnvDup( + const _PyCoreConfig *config, + wchar_t **dest, + wchar_t *wname, + char *name); +#endif + + +#ifdef __cplusplus +} +#endif +#endif /* !Py_PYCORECONFIG_H */ diff --git a/Include/internal/pystate.h b/Include/internal/pystate.h index e041d3768467..ef83af59323e 100644 --- a/Include/internal/pystate.h +++ b/Include/internal/pystate.h @@ -72,6 +72,12 @@ PyAPI_FUNC(_PyInitError) _PyPathConfig_Calculate_impl( const _PyCoreConfig *core_config); PyAPI_FUNC(void) _PyPathConfig_ClearGlobal(void); +PyAPI_FUNC(void) _Py_wstrlist_clear( + int len, + wchar_t **list); +PyAPI_FUNC(wchar_t**) _Py_wstrlist_copy( + int len, + wchar_t **list); PyAPI_FUNC(_PyInitError) _Py_wstrlist_append( int *len, wchar_t ***list, diff --git a/Include/pylifecycle.h b/Include/pylifecycle.h index c58c7ec95d9d..4965d7cbf06c 100644 --- a/Include/pylifecycle.h +++ b/Include/pylifecycle.h @@ -7,36 +7,6 @@ extern "C" { #endif -#ifndef Py_LIMITED_API -typedef struct { - const char *prefix; - const char *msg; - int user_err; -} _PyInitError; - -/* Almost all errors causing Python initialization to fail */ -#ifdef _MSC_VER - /* Visual Studio 2015 doesn't implement C99 __func__ in C */ -# define _Py_INIT_GET_FUNC() __FUNCTION__ -#else -# define _Py_INIT_GET_FUNC() __func__ -#endif - -#define _Py_INIT_OK() \ - (_PyInitError){.prefix = NULL, .msg = NULL, .user_err = 0} -#define _Py_INIT_ERR(MSG) \ - (_PyInitError){.prefix = _Py_INIT_GET_FUNC(), .msg = (MSG), .user_err = 0} -/* Error that can be fixed by the user like invalid input parameter. - Don't abort() the process on such error. */ -#define _Py_INIT_USER_ERR(MSG) \ - (_PyInitError){.prefix = _Py_INIT_GET_FUNC(), .msg = (MSG), .user_err = 1} -#define _Py_INIT_NO_MEMORY() _Py_INIT_USER_ERR("memory allocation failed") -#define _Py_INIT_FAILED(err) \ - (err.msg != NULL) - -#endif - - PyAPI_FUNC(void) Py_SetProgramName(const wchar_t *); PyAPI_FUNC(wchar_t *) Py_GetProgramName(void); @@ -56,16 +26,6 @@ PyAPI_FUNC(_PyInitError) _Py_InitializeCore( const _PyCoreConfig *); PyAPI_FUNC(int) _Py_IsCoreInitialized(void); -PyAPI_FUNC(_PyInitError) _PyCoreConfig_Read(_PyCoreConfig *config); -PyAPI_FUNC(void) _PyCoreConfig_Clear(_PyCoreConfig *); -PyAPI_FUNC(int) _PyCoreConfig_Copy( - _PyCoreConfig *config, - const _PyCoreConfig *config2); -PyAPI_FUNC(_PyInitError) _PyCoreConfig_InitPathConfig(_PyCoreConfig *config); -PyAPI_FUNC(_PyInitError) _PyCoreConfig_SetPathConfig( - const _PyCoreConfig *config); -PyAPI_FUNC(void) _PyCoreConfig_SetGlobalConfig(const _PyCoreConfig *config); - PyAPI_FUNC(_PyInitError) _PyMainInterpreterConfig_Read( _PyMainInterpreterConfig *config, @@ -165,12 +125,6 @@ PyAPI_FUNC(int) _PyFloat_Init(void); PyAPI_FUNC(int) PyByteArray_Init(void); PyAPI_FUNC(_PyInitError) _Py_HashRandomization_Init(const _PyCoreConfig *); #endif -#ifdef Py_BUILD_CORE -PyAPI_FUNC(int) _Py_ReadHashSeed( - const char *seed_text, - int *use_hash_seed, - unsigned long *hash_seed); -#endif /* Various internal finalizers */ diff --git a/Include/pystate.h b/Include/pystate.h index b90084321141..bbeea9d51b82 100644 --- a/Include/pystate.h +++ b/Include/pystate.h @@ -8,6 +8,7 @@ extern "C" { #endif #include "pythread.h" +#include "coreconfig.h" /* This limitation is for performance and simplicity. If needed it can be removed (with effort). */ @@ -24,261 +25,6 @@ typedef struct _is PyInterpreterState; #else typedef PyObject* (*_PyFrameEvalFunction)(struct _frame *, int); - -typedef struct { - /* Install signal handlers? Yes by default. */ - int install_signal_handlers; - - /* If greater than 0: use environment variables. - Set to 0 by -E command line option. If set to -1 (default), it is - set to !Py_IgnoreEnvironmentFlag. */ - int use_environment; - - int use_hash_seed; /* PYTHONHASHSEED=x */ - unsigned long hash_seed; - - const char *allocator; /* Memory allocator: PYTHONMALLOC */ - int dev_mode; /* PYTHONDEVMODE, -X dev */ - - /* Enable faulthandler? - Set to 1 by -X faulthandler and PYTHONFAULTHANDLER. -1 means unset. */ - int faulthandler; - - /* Enable tracemalloc? - Set by -X tracemalloc=N and PYTHONTRACEMALLOC. -1 means unset */ - int tracemalloc; - - int import_time; /* PYTHONPROFILEIMPORTTIME, -X importtime */ - int show_ref_count; /* -X showrefcount */ - int show_alloc_count; /* -X showalloccount */ - int dump_refs; /* PYTHONDUMPREFS */ - int malloc_stats; /* PYTHONMALLOCSTATS */ - int coerce_c_locale; /* PYTHONCOERCECLOCALE, -1 means unknown */ - int coerce_c_locale_warn; /* PYTHONCOERCECLOCALE=warn */ - - /* Enable UTF-8 mode? - Set by -X utf8 command line option and PYTHONUTF8 environment variable. - If set to -1 (default), inherit Py_UTF8Mode value. */ - int utf8_mode; - - wchar_t *pycache_prefix; /* PYTHONPYCACHEPREFIX, -X pycache_prefix=PATH */ - - wchar_t *program_name; /* Program name, see also Py_GetProgramName() */ - int argc; /* Number of command line arguments, - -1 means unset */ - wchar_t **argv; /* Command line arguments */ - wchar_t *program; /* argv[0] or "" */ - - int nxoption; /* Number of -X options */ - wchar_t **xoptions; /* -X options */ - - int nwarnoption; /* Number of warnings options */ - wchar_t **warnoptions; /* Warnings options */ - - /* Path configuration inputs */ - wchar_t *module_search_path_env; /* PYTHONPATH environment variable */ - wchar_t *home; /* PYTHONHOME environment variable, - see also Py_SetPythonHome(). */ - - /* Path configuration outputs */ - int nmodule_search_path; /* Number of sys.path paths, - -1 means unset */ - wchar_t **module_search_paths; /* sys.path paths */ - wchar_t *executable; /* sys.executable */ - wchar_t *prefix; /* sys.prefix */ - wchar_t *base_prefix; /* sys.base_prefix */ - wchar_t *exec_prefix; /* sys.exec_prefix */ - wchar_t *base_exec_prefix; /* sys.base_exec_prefix */ -#ifdef MS_WINDOWS - wchar_t *dll_path; /* Windows DLL path */ -#endif - - /* If greater than 0, enable isolated mode: sys.path contains - neither the script's directory nor the user's site-packages directory. - - Set to 1 by the -I command line option. If set to -1 (default), inherit - Py_IsolatedFlag value. */ - int isolated; - - /* If equal to zero, disable the import of the module site and the - site-dependent manipulations of sys.path that it entails. Also disable - these manipulations if site is explicitly imported later (call - site.main() if you want them to be triggered). - - Set to 0 by the -S command line option. If set to -1 (default), it is - set to !Py_NoSiteFlag. */ - int site_import; - - /* Bytes warnings: - - * If equal to 1, issue a warning when comparing bytes or bytearray with - str or bytes with int. - * If equal or greater to 2, issue an error. - - Incremented by the -b command line option. If set to -1 (default), inherit - Py_BytesWarningFlag value. */ - int bytes_warning; - - /* If greater than 0, enable inspect: when a script is passed as first - argument or the -c option is used, enter interactive mode after - executing the script or the command, even when sys.stdin does not appear - to be a terminal. - - Incremented by the -i command line option. Set to 1 if the PYTHONINSPECT - environment variable is non-empty. If set to -1 (default), inherit - Py_InspectFlag value. */ - int inspect; - - /* If greater than 0: enable the interactive mode (REPL). - - Incremented by the -i command line option. If set to -1 (default), - inherit Py_InteractiveFlag value. */ - int interactive; - - /* Optimization level. - - Incremented by the -O command line option. Set by the PYTHONOPTIMIZE - environment variable. If set to -1 (default), inherit Py_OptimizeFlag - value. */ - int optimization_level; - - /* If greater than 0, enable the debug mode: turn on parser debugging - output (for expert only, depending on compilation options). - - Incremented by the -d command line option. Set by the PYTHONDEBUG - environment variable. If set to -1 (default), inherit Py_DebugFlag - value. */ - int parser_debug; - - /* If equal to 0, Python won't try to write ``.pyc`` files on the - import of source modules. - - Set to 0 by the -B command line option and the PYTHONDONTWRITEBYTECODE - environment variable. If set to -1 (default), it is set to - !Py_DontWriteBytecodeFlag. */ - int write_bytecode; - - /* If greater than 0, enable the verbose mode: print a message each time a - module is initialized, showing the place (filename or built-in module) - from which it is loaded. - - If greater or equal to 2, print a message for each file that is checked - for when searching for a module. Also provides information on module - cleanup at exit. - - Incremented by the -v option. Set by the PYTHONVERBOSE environment - variable. If set to -1 (default), inherit Py_VerboseFlag value. */ - int verbose; - - /* If greater than 0, enable the quiet mode: Don't display the copyright - and version messages even in interactive mode. - - Incremented by the -q option. If set to -1 (default), inherit - Py_QuietFlag value. */ - int quiet; - - /* If greater than 0, don't add the user site-packages directory to - sys.path. - - Set to 0 by the -s and -I command line options , and the PYTHONNOUSERSITE - environment variable. If set to -1 (default), it is set to - !Py_NoUserSiteDirectory. */ - int user_site_directory; - - /* If equal to 0, enable unbuffered mode: force the stdout and stderr - streams to be unbuffered. - - Set to 0 by the -u option. Set by the PYTHONUNBUFFERED environment - variable. - If set to -1 (default), it is set to !Py_UnbufferedStdioFlag. */ - int buffered_stdio; - -#ifdef MS_WINDOWS - /* If greater than 1, use the "mbcs" encoding instead of the UTF-8 - encoding for the filesystem encoding. - - Set to 1 if the PYTHONLEGACYWINDOWSFSENCODING environment variable is - set to a non-empty string. If set to -1 (default), inherit - Py_LegacyWindowsFSEncodingFlag value. - - See PEP 529 for more details. */ - int legacy_windows_fs_encoding; - - /* If greater than zero, use io.FileIO instead of WindowsConsoleIO for sys - standard streams. - - Set to 1 if the PYTHONLEGACYWINDOWSSTDIO environment variable is set to - a non-empty string. If set to -1 (default), inherit - Py_LegacyWindowsStdioFlag value. - - See PEP 528 for more details. */ - int legacy_windows_stdio; -#endif - - /* --- Private fields -------- */ - - /* Install importlib? If set to 0, importlib is not initialized at all. - Needed by freeze_importlib. */ - int _install_importlib; - - /* Value of the --check-hash-based-pycs configure option. Valid values: - - - "default" means the 'check_source' flag in hash-based pycs - determines invalidation - - "always" causes the interpreter to hash the source file for - invalidation regardless of value of 'check_source' bit - - "never" causes the interpreter to always assume hash-based pycs are - valid - - Set by the --check-hash-based-pycs command line option. - If set to NULL (default), inherit _Py_CheckHashBasedPycsMode value. - - See PEP 552 "Deterministic pycs" for more details. */ - const char *_check_hash_pycs_mode; - - /* If greater than 0, suppress _PyPathConfig_Calculate() warnings. - - If set to -1 (default), inherit Py_FrozenFlag value. */ - int _frozen; - -} _PyCoreConfig; - -#ifdef MS_WINDOWS -# define _PyCoreConfig_WINDOWS_INIT \ - .legacy_windows_fs_encoding = -1, \ - .legacy_windows_stdio = -1, -#else -# define _PyCoreConfig_WINDOWS_INIT -#endif - -#define _PyCoreConfig_INIT \ - (_PyCoreConfig){ \ - .install_signal_handlers = 1, \ - .use_environment = -1, \ - .use_hash_seed = -1, \ - .faulthandler = -1, \ - .tracemalloc = -1, \ - .coerce_c_locale = -1, \ - .utf8_mode = -1, \ - .argc = -1, \ - .nmodule_search_path = -1, \ - .isolated = -1, \ - .site_import = -1, \ - .bytes_warning = -1, \ - .inspect = -1, \ - .interactive = -1, \ - .optimization_level = -1, \ - .parser_debug= -1, \ - .write_bytecode = -1, \ - .verbose = -1, \ - .quiet = -1, \ - .user_site_directory = -1, \ - .buffered_stdio = -1, \ - _PyCoreConfig_WINDOWS_INIT \ - ._install_importlib = 1, \ - ._frozen = -1} -/* Note: _PyCoreConfig_INIT sets other fields to 0/NULL */ - /* Placeholders while working on the new configuration API * * See PEP 432 for final anticipated contents diff --git a/Makefile.pre.in b/Makefile.pre.in index 51cf2fdb009b..d60d48e5f9bb 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -329,8 +329,9 @@ PYTHON_OBJS= \ Python/ast_unparse.o \ Python/bltinmodule.o \ Python/ceval.o \ - Python/compile.o \ Python/codecs.o \ + Python/compile.o \ + Python/coreconfig.o \ Python/dynamic_annotations.o \ Python/errors.o \ Python/frozenmain.o \ @@ -944,6 +945,7 @@ PYTHON_HEADERS= \ $(srcdir)/Include/codecs.h \ $(srcdir)/Include/compile.h \ $(srcdir)/Include/complexobject.h \ + $(srcdir)/Include/coreconfig.h \ $(srcdir)/Include/descrobject.h \ $(srcdir)/Include/dictobject.h \ $(srcdir)/Include/dtoa.h \ diff --git a/Modules/main.c b/Modules/main.c index 34069e1b2d31..f99ff86b749d 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -2,7 +2,6 @@ #include "Python.h" #include "osdefs.h" -#include "internal/import.h" #include "internal/pygetopt.h" #include "internal/pystate.h" @@ -46,16 +45,6 @@ extern "C" { : _Py_INIT_NO_MEMORY()) -#define SET_DECODE_ERROR(NAME, LEN) \ - do { \ - if ((LEN) == (size_t)-2) { \ - pymain->err = _Py_INIT_USER_ERR("cannot decode " NAME); \ - } \ - else { \ - pymain->err = _Py_INIT_NO_MEMORY(); \ - } \ - } while (0) - #ifdef MS_WINDOWS #define WCSTOK wcstok_s #else @@ -168,71 +157,6 @@ pymain_usage(int error, const wchar_t* program) } -static const char* -config_get_env_var(const _PyCoreConfig *config, const char *name) -{ - assert(config->use_environment >= 0); - - if (!config->use_environment) { - return NULL; - } - - const char *var = getenv(name); - if (var && var[0] != '\0') { - return var; - } - else { - return NULL; - } -} - - -static int -config_get_env_var_dup(const _PyCoreConfig *config, wchar_t **dest, wchar_t *wname, char *name) -{ - assert(config->use_environment >= 0); - - if (!config->use_environment) { - *dest = NULL; - return 0; - } - -#ifdef MS_WINDOWS - const wchar_t *var = _wgetenv(wname); - if (!var || var[0] == '\0') { - *dest = NULL; - return 0; - } - - wchar_t *copy = _PyMem_RawWcsdup(var); - if (copy == NULL) { - return -1; - } - - *dest = copy; -#else - const char *var = getenv(name); - if (!var || var[0] == '\0') { - *dest = NULL; - return 0; - } - - size_t len; - wchar_t *wvar = Py_DecodeLocale(var, &len); - if (!wvar) { - if (len == (size_t)-2) { - return -2; - } - else { - return -1; - } - } - *dest = wvar; -#endif - return 0; -} - - static void pymain_run_interactive_hook(void) { @@ -457,34 +381,6 @@ typedef struct { || pymain->module != NULL) -static void -clear_wstrlist(int len, wchar_t **list) -{ - for (int i=0; i < len; i++) { - PyMem_RawFree(list[i]); - } - PyMem_RawFree(list); -} - - -static wchar_t** -copy_wstrlist(int len, wchar_t **list) -{ - assert((len > 0 && list != NULL) || len == 0); - size_t size = len * sizeof(list[0]); - wchar_t **list_copy = PyMem_RawMalloc(size); - for (int i=0; i < len; i++) { - wchar_t* arg = _PyMem_RawWcsdup(list[i]); - if (arg == NULL) { - clear_wstrlist(i, list); - return NULL; - } - list_copy[i] = arg; - } - return list_copy; -} - - static wchar_t* pymain_wstrdup(_PyMain *pymain, const wchar_t *str) { @@ -516,7 +412,7 @@ pymain_init_cmdline_argv(_PyMain *pymain, _PyCoreConfig *config, size_t len; wchar_t *arg = Py_DecodeLocale(pymain->bytes_argv[i], &len); if (arg == NULL) { - clear_wstrlist(i, argv); + _Py_wstrlist_clear(i, argv); pymain->err = DECODE_LOCALE_ERR("command line arguments", (Py_ssize_t)len); return -1; @@ -547,244 +443,22 @@ pymain_init_cmdline_argv(_PyMain *pymain, _PyCoreConfig *config, } -static void -_PyCoreConfig_GetGlobalConfig(_PyCoreConfig *config) -{ -#define COPY_FLAG(ATTR, VALUE) \ - if (config->ATTR == -1) { \ - config->ATTR = VALUE; \ - } -#define COPY_NOT_FLAG(ATTR, VALUE) \ - if (config->ATTR == -1) { \ - config->ATTR = !(VALUE); \ - } - - COPY_FLAG(utf8_mode, Py_UTF8Mode); - COPY_FLAG(isolated, Py_IsolatedFlag); - COPY_FLAG(bytes_warning, Py_BytesWarningFlag); - COPY_FLAG(inspect, Py_InspectFlag); - COPY_FLAG(interactive, Py_InteractiveFlag); - COPY_FLAG(optimization_level, Py_OptimizeFlag); - COPY_FLAG(parser_debug, Py_DebugFlag); - COPY_FLAG(verbose, Py_VerboseFlag); - COPY_FLAG(quiet, Py_QuietFlag); -#ifdef MS_WINDOWS - COPY_FLAG(legacy_windows_fs_encoding, Py_LegacyWindowsFSEncodingFlag); - COPY_FLAG(legacy_windows_stdio, Py_LegacyWindowsStdioFlag); -#endif - COPY_FLAG(_frozen, Py_FrozenFlag); - - COPY_NOT_FLAG(use_environment, Py_IgnoreEnvironmentFlag); - COPY_NOT_FLAG(buffered_stdio, Py_UnbufferedStdioFlag); - COPY_NOT_FLAG(site_import, Py_NoSiteFlag); - COPY_NOT_FLAG(write_bytecode, Py_DontWriteBytecodeFlag); - COPY_NOT_FLAG(user_site_directory, Py_NoUserSiteDirectory); - - if (config->_check_hash_pycs_mode == NULL) { - config->_check_hash_pycs_mode = _Py_CheckHashBasedPycsMode; - } - -#undef COPY_FLAG -#undef COPY_NOT_FLAG -} - - -/* Set Py_xxx global configuration variables from 'config' configuration. */ -void -_PyCoreConfig_SetGlobalConfig(const _PyCoreConfig *config) -{ -#define COPY_FLAG(ATTR, VAR) \ - if (config->ATTR != -1) { \ - VAR = config->ATTR; \ - } -#define COPY_NOT_FLAG(ATTR, VAR) \ - if (config->ATTR != -1) { \ - VAR = !config->ATTR; \ - } - - COPY_FLAG(utf8_mode, Py_UTF8Mode); - COPY_FLAG(isolated, Py_IsolatedFlag); - COPY_FLAG(bytes_warning, Py_BytesWarningFlag); - COPY_FLAG(inspect, Py_InspectFlag); - COPY_FLAG(interactive, Py_InteractiveFlag); - COPY_FLAG(optimization_level, Py_OptimizeFlag); - COPY_FLAG(parser_debug, Py_DebugFlag); - COPY_FLAG(verbose, Py_VerboseFlag); - COPY_FLAG(quiet, Py_QuietFlag); -#ifdef MS_WINDOWS - COPY_FLAG(legacy_windows_fs_encoding, Py_LegacyWindowsFSEncodingFlag); - COPY_FLAG(legacy_windows_stdio, Py_LegacyWindowsStdioFlag); -#endif - - COPY_NOT_FLAG(use_environment, Py_IgnoreEnvironmentFlag); - COPY_NOT_FLAG(buffered_stdio, Py_UnbufferedStdioFlag); - COPY_NOT_FLAG(site_import, Py_NoSiteFlag); - COPY_NOT_FLAG(write_bytecode, Py_DontWriteBytecodeFlag); - COPY_NOT_FLAG(user_site_directory, Py_NoUserSiteDirectory); - - if (config->_check_hash_pycs_mode != NULL) { - _Py_CheckHashBasedPycsMode = config->_check_hash_pycs_mode; - } - - /* Random or non-zero hash seed */ - Py_HashRandomizationFlag = (config->use_hash_seed == 0 || - config->hash_seed != 0); - -#undef COPY_FLAG -#undef COPY_NOT_FLAG -} - - -/* Free memory allocated in config, but don't clear all attributes */ -void -_PyCoreConfig_Clear(_PyCoreConfig *config) -{ -#define CLEAR(ATTR) \ - do { \ - PyMem_RawFree(ATTR); \ - ATTR = NULL; \ - } while (0) -#define CLEAR_WSTRLIST(LEN, LIST) \ - do { \ - clear_wstrlist(LEN, LIST); \ - LEN = 0; \ - LIST = NULL; \ - } while (0) - - CLEAR(config->pycache_prefix); - CLEAR(config->module_search_path_env); - CLEAR(config->home); - CLEAR(config->program_name); - CLEAR(config->program); - - CLEAR_WSTRLIST(config->argc, config->argv); - config->argc = -1; - - CLEAR_WSTRLIST(config->nwarnoption, config->warnoptions); - CLEAR_WSTRLIST(config->nxoption, config->xoptions); - CLEAR_WSTRLIST(config->nmodule_search_path, config->module_search_paths); - config->nmodule_search_path = -1; - - CLEAR(config->executable); - CLEAR(config->prefix); - CLEAR(config->base_prefix); - CLEAR(config->exec_prefix); -#ifdef MS_WINDOWS - CLEAR(config->dll_path); -#endif - CLEAR(config->base_exec_prefix); -#undef CLEAR -#undef CLEAR_WSTRLIST -} - - -int -_PyCoreConfig_Copy(_PyCoreConfig *config, const _PyCoreConfig *config2) -{ - _PyCoreConfig_Clear(config); - -#define COPY_ATTR(ATTR) config->ATTR = config2->ATTR -#define COPY_STR_ATTR(ATTR) \ - do { \ - if (config2->ATTR != NULL) { \ - config->ATTR = _PyMem_RawWcsdup(config2->ATTR); \ - if (config->ATTR == NULL) { \ - return -1; \ - } \ - } \ - } while (0) -#define COPY_WSTRLIST(LEN, LIST) \ - do { \ - if (config2->LIST != NULL) { \ - config->LIST = copy_wstrlist(config2->LEN, config2->LIST); \ - if (config->LIST == NULL) { \ - return -1; \ - } \ - } \ - config->LEN = config2->LEN; \ - } while (0) - - COPY_ATTR(install_signal_handlers); - COPY_ATTR(use_environment); - COPY_ATTR(use_hash_seed); - COPY_ATTR(hash_seed); - COPY_ATTR(_install_importlib); - COPY_ATTR(allocator); - COPY_ATTR(dev_mode); - COPY_ATTR(faulthandler); - COPY_ATTR(tracemalloc); - COPY_ATTR(import_time); - COPY_ATTR(show_ref_count); - COPY_ATTR(show_alloc_count); - COPY_ATTR(dump_refs); - COPY_ATTR(malloc_stats); - - COPY_ATTR(coerce_c_locale); - COPY_ATTR(coerce_c_locale_warn); - COPY_ATTR(utf8_mode); - - COPY_STR_ATTR(pycache_prefix); - COPY_STR_ATTR(module_search_path_env); - COPY_STR_ATTR(home); - COPY_STR_ATTR(program_name); - COPY_STR_ATTR(program); - - COPY_WSTRLIST(argc, argv); - COPY_WSTRLIST(nwarnoption, warnoptions); - COPY_WSTRLIST(nxoption, xoptions); - COPY_WSTRLIST(nmodule_search_path, module_search_paths); - - COPY_STR_ATTR(executable); - COPY_STR_ATTR(prefix); - COPY_STR_ATTR(base_prefix); - COPY_STR_ATTR(exec_prefix); -#ifdef MS_WINDOWS - COPY_STR_ATTR(dll_path); -#endif - COPY_STR_ATTR(base_exec_prefix); - - COPY_ATTR(isolated); - COPY_ATTR(site_import); - COPY_ATTR(bytes_warning); - COPY_ATTR(inspect); - COPY_ATTR(interactive); - COPY_ATTR(optimization_level); - COPY_ATTR(parser_debug); - COPY_ATTR(write_bytecode); - COPY_ATTR(verbose); - COPY_ATTR(quiet); - COPY_ATTR(user_site_directory); - COPY_ATTR(buffered_stdio); -#ifdef MS_WINDOWS - COPY_ATTR(legacy_windows_fs_encoding); - COPY_ATTR(legacy_windows_stdio); -#endif - COPY_ATTR(_check_hash_pycs_mode); - COPY_ATTR(_frozen); - -#undef COPY_ATTR -#undef COPY_STR_ATTR -#undef COPY_WSTRLIST - return 0; -} - - static void pymain_clear_cmdline(_PyMain *pymain, _PyCmdline *cmdline) { PyMemAllocatorEx old_alloc; _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - clear_wstrlist(cmdline->nwarnoption, cmdline->warnoptions); + _Py_wstrlist_clear(cmdline->nwarnoption, cmdline->warnoptions); cmdline->nwarnoption = 0; cmdline->warnoptions = NULL; - clear_wstrlist(cmdline->nenv_warnoption, cmdline->env_warnoptions); + _Py_wstrlist_clear(cmdline->nenv_warnoption, cmdline->env_warnoptions); cmdline->nenv_warnoption = 0; cmdline->env_warnoptions = NULL; if (pymain->use_bytes_argv && cmdline->argv != NULL) { - clear_wstrlist(pymain->argc, cmdline->argv); + _Py_wstrlist_clear(pymain->argc, cmdline->argv); } cmdline->argv = NULL; @@ -838,7 +512,7 @@ pymain_free(_PyMain *pymain) pymain_clear_pymain(pymain); - clear_wstrlist(orig_argc, orig_argv); + _Py_wstrlist_clear(orig_argc, orig_argv); orig_argc = 0; orig_argv = NULL; @@ -1245,8 +919,8 @@ cmdline_init_env_warnoptions(_PyMain *pymain, const _PyCoreConfig *config, _PyCmdline *cmdline) { wchar_t *env; - int res = config_get_env_var_dup(config, &env, - L"PYTHONWARNINGS", "PYTHONWARNINGS"); + int res = _PyCoreConfig_GetEnvDup(config, &env, + L"PYTHONWARNINGS", "PYTHONWARNINGS"); if (res < 0) { return DECODE_LOCALE_ERR("PYTHONWARNINGS", res); } @@ -1314,85 +988,6 @@ pymain_init_stdio(_PyMain *pymain, _PyCoreConfig *config) } -/* Get the program name: use PYTHONEXECUTABLE and __PYVENV_LAUNCHER__ - environment variables on macOS if available. */ -static _PyInitError -config_init_program_name(_PyCoreConfig *config) -{ - /* If Py_SetProgramName() was called, use its value */ - const wchar_t *program_name = _Py_path_config.program_name; - if (program_name != NULL) { - config->program_name = _PyMem_RawWcsdup(program_name); - if (config->program_name == NULL) { - return _Py_INIT_NO_MEMORY(); - } - return _Py_INIT_OK(); - } - -#ifdef __APPLE__ - /* On MacOS X, when the Python interpreter is embedded in an - application bundle, it gets executed by a bootstrapping script - that does os.execve() with an argv[0] that's different from the - actual Python executable. This is needed to keep the Finder happy, - or rather, to work around Apple's overly strict requirements of - the process name. However, we still need a usable sys.executable, - so the actual executable path is passed in an environment variable. - See Lib/plat-mac/bundlebuiler.py for details about the bootstrap - script. */ - const char *p = config_get_env_var(config, "PYTHONEXECUTABLE"); - if (p != NULL) { - size_t len; - wchar_t* program_name = Py_DecodeLocale(p, &len); - if (program_name == NULL) { - return DECODE_LOCALE_ERR("PYTHONEXECUTABLE environment " - "variable", (Py_ssize_t)len); - } - config->program_name = program_name; - return _Py_INIT_OK(); - } -#ifdef WITH_NEXT_FRAMEWORK - else { - const char* pyvenv_launcher = getenv("__PYVENV_LAUNCHER__"); - if (pyvenv_launcher && *pyvenv_launcher) { - /* Used by Mac/Tools/pythonw.c to forward - * the argv0 of the stub executable - */ - size_t len; - wchar_t* program_name = Py_DecodeLocale(pyvenv_launcher, &len); - if (program_name == NULL) { - return DECODE_LOCALE_ERR("__PYVENV_LAUNCHER__ environment " - "variable", (Py_ssize_t)len); - } - config->program_name = program_name; - return _Py_INIT_OK(); - } - } -#endif /* WITH_NEXT_FRAMEWORK */ -#endif /* __APPLE__ */ - - /* Use argv[0] by default, if available */ - if (config->program != NULL) { - config->program_name = _PyMem_RawWcsdup(config->program); - if (config->program_name == NULL) { - return _Py_INIT_NO_MEMORY(); - } - return _Py_INIT_OK(); - } - - /* Last fall back: hardcoded string */ -#ifdef MS_WINDOWS - const wchar_t *default_program_name = L"python"; -#else - const wchar_t *default_program_name = L"python3"; -#endif - config->program_name = _PyMem_RawWcsdup(default_program_name); - if (config->program_name == NULL) { - return _Py_INIT_NO_MEMORY(); - } - return _Py_INIT_OK(); -} - - static void pymain_header(_PyMain *pymain) { @@ -1422,10 +1017,10 @@ pymain_init_core_argv(_PyMain *pymain, _PyCoreConfig *config, _PyCmdline *cmdlin /* Ensure at least one (empty) argument is seen */ static wchar_t *empty_argv[1] = {L""}; argc = 1; - argv = copy_wstrlist(1, empty_argv); + argv = _Py_wstrlist_copy(1, empty_argv); } else { - argv = copy_wstrlist(argc, &cmdline->argv[_PyOS_optind]); + argv = _Py_wstrlist_copy(argc, &cmdline->argv[_PyOS_optind]); } if (argv == NULL) { @@ -1445,7 +1040,7 @@ pymain_init_core_argv(_PyMain *pymain, _PyCoreConfig *config, _PyCmdline *cmdlin if (arg0 != NULL) { arg0 = _PyMem_RawWcsdup(arg0); if (arg0 == NULL) { - clear_wstrlist(argc, argv); + _Py_wstrlist_clear(argc, argv); pymain->err = _Py_INIT_NO_MEMORY(); return -1; } @@ -1558,7 +1153,7 @@ pymain_open_filename(_PyMain *pymain, _PyCoreConfig *config) static void pymain_run_startup(_PyMain *pymain, _PyCoreConfig *config, PyCompilerFlags *cf) { - const char *startup = config_get_env_var(config, "PYTHONSTARTUP"); + const char *startup = _PyCoreConfig_GetEnv(config, "PYTHONSTARTUP"); if (startup == NULL) { return; } @@ -1613,7 +1208,7 @@ pymain_repl(_PyMain *pymain, _PyCoreConfig *config, PyCompilerFlags *cf) { /* Check this environment variable at the end, to give programs the opportunity to set it from Python. */ - if (!Py_InspectFlag && config_get_env_var(config, "PYTHONINSPECT")) { + if (!Py_InspectFlag && _PyCoreConfig_GetEnv(config, "PYTHONINSPECT")) { Py_InspectFlag = 1; config->inspect = 1; } @@ -1660,359 +1255,6 @@ pymain_parse_cmdline(_PyMain *pymain, _PyCoreConfig *config, } -static const wchar_t* -config_get_xoption(_PyCoreConfig *config, wchar_t *name) -{ - int nxoption = config->nxoption; - wchar_t **xoptions = config->xoptions; - for (int i=0; i < nxoption; i++) { - wchar_t *option = xoptions[i]; - size_t len; - wchar_t *sep = wcschr(option, L'='); - if (sep != NULL) { - len = (sep - option); - } - else { - len = wcslen(option); - } - if (wcsncmp(option, name, len) == 0 && name[len] == L'\0') { - return option; - } - } - return NULL; -} - - -static int -pymain_str_to_int(const char *str, int *result) -{ - errno = 0; - const char *endptr = str; - long value = strtol(str, (char **)&endptr, 10); - if (*endptr != '\0' || errno == ERANGE) { - return -1; - } - if (value < INT_MIN || value > INT_MAX) { - return -1; - } - - *result = (int)value; - return 0; -} - - -static int -pymain_wstr_to_int(const wchar_t *wstr, int *result) -{ - errno = 0; - const wchar_t *endptr = wstr; - long value = wcstol(wstr, (wchar_t **)&endptr, 10); - if (*endptr != '\0' || errno == ERANGE) { - return -1; - } - if (value < INT_MIN || value > INT_MAX) { - return -1; - } - - *result = (int)value; - return 0; -} - - -static _PyInitError -pymain_init_tracemalloc(_PyCoreConfig *config) -{ - int nframe; - int valid; - - const char *env = config_get_env_var(config, "PYTHONTRACEMALLOC"); - if (env) { - if (!pymain_str_to_int(env, &nframe)) { - valid = (nframe >= 0); - } - else { - valid = 0; - } - if (!valid) { - return _Py_INIT_USER_ERR("PYTHONTRACEMALLOC: invalid number " - "of frames"); - } - config->tracemalloc = nframe; - } - - const wchar_t *xoption = config_get_xoption(config, L"tracemalloc"); - if (xoption) { - const wchar_t *sep = wcschr(xoption, L'='); - if (sep) { - if (!pymain_wstr_to_int(sep + 1, &nframe)) { - valid = (nframe >= 0); - } - else { - valid = 0; - } - if (!valid) { - return _Py_INIT_USER_ERR("-X tracemalloc=NFRAME: " - "invalid number of frames"); - } - } - else { - /* -X tracemalloc behaves as -X tracemalloc=1 */ - nframe = 1; - } - config->tracemalloc = nframe; - } - return _Py_INIT_OK(); -} - - -static _PyInitError -pymain_init_pycache_prefix(_PyCoreConfig *config) -{ - const wchar_t *xoption = config_get_xoption(config, L"pycache_prefix"); - if (xoption) { - const wchar_t *sep = wcschr(xoption, L'='); - if (sep && wcslen(sep) > 1) { - config->pycache_prefix = _PyMem_RawWcsdup(sep + 1); - if (config->pycache_prefix == NULL) { - return _Py_INIT_NO_MEMORY(); - } - } else { - // -X pycache_prefix= can cancel the env var - config->pycache_prefix = NULL; - } - return _Py_INIT_OK(); - } - - wchar_t *env; - int res = config_get_env_var_dup(config, &env, - L"PYTHONPYCACHEPREFIX", "PYTHONPYCACHEPREFIX"); - if (res < 0) { - return DECODE_LOCALE_ERR("PYTHONPYCACHEPREFIX", res); - } else if (env) { - config->pycache_prefix = env; - } - - return _Py_INIT_OK(); -} - - -static void -get_env_flag(_PyCoreConfig *config, int *flag, const char *name) -{ - const char *var = config_get_env_var(config, name); - if (!var) { - return; - } - int value; - if (pymain_str_to_int(var, &value) < 0 || value < 0) { - /* PYTHONDEBUG=text and PYTHONDEBUG=-2 behave as PYTHONDEBUG=1 */ - value = 1; - } - if (*flag < value) { - *flag = value; - } -} - - -static _PyInitError -config_init_home(_PyCoreConfig *config) -{ - wchar_t *home; - - /* If Py_SetPythonHome() was called, use its value */ - home = _Py_path_config.home; - if (home) { - config->home = _PyMem_RawWcsdup(home); - if (config->home == NULL) { - return _Py_INIT_NO_MEMORY(); - } - return _Py_INIT_OK(); - } - - int res = config_get_env_var_dup(config, &home, L"PYTHONHOME", "PYTHONHOME"); - if (res < 0) { - return DECODE_LOCALE_ERR("PYTHONHOME", res); - } - config->home = home; - return _Py_INIT_OK(); -} - - -static _PyInitError -config_init_hash_seed(_PyCoreConfig *config) -{ - if (config->use_hash_seed < 0) { - const char *seed_text = config_get_env_var(config, "PYTHONHASHSEED"); - int use_hash_seed; - unsigned long hash_seed; - if (_Py_ReadHashSeed(seed_text, &use_hash_seed, &hash_seed) < 0) { - return _Py_INIT_USER_ERR("PYTHONHASHSEED must be \"random\" " - "or an integer in range [0; 4294967295]"); - } - config->use_hash_seed = use_hash_seed; - config->hash_seed = hash_seed; - } - return _Py_INIT_OK(); -} - - -static _PyInitError -config_init_utf8_mode(_PyCoreConfig *config) -{ - const wchar_t *xopt = config_get_xoption(config, L"utf8"); - if (xopt) { - wchar_t *sep = wcschr(xopt, L'='); - if (sep) { - xopt = sep + 1; - if (wcscmp(xopt, L"1") == 0) { - config->utf8_mode = 1; - } - else if (wcscmp(xopt, L"0") == 0) { - config->utf8_mode = 0; - } - else { - return _Py_INIT_USER_ERR("invalid -X utf8 option value"); - } - } - else { - config->utf8_mode = 1; - } - return _Py_INIT_OK(); - } - - const char *opt = config_get_env_var(config, "PYTHONUTF8"); - if (opt) { - if (strcmp(opt, "1") == 0) { - config->utf8_mode = 1; - } - else if (strcmp(opt, "0") == 0) { - config->utf8_mode = 0; - } - else { - return _Py_INIT_USER_ERR("invalid PYTHONUTF8 environment " - "variable value"); - } - return _Py_INIT_OK(); - } - - return _Py_INIT_OK(); -} - - -static _PyInitError -config_read_env_vars(_PyCoreConfig *config) -{ - assert(config->use_environment > 0); - - /* Get environment variables */ - get_env_flag(config, &config->parser_debug, "PYTHONDEBUG"); - get_env_flag(config, &config->verbose, "PYTHONVERBOSE"); - get_env_flag(config, &config->optimization_level, "PYTHONOPTIMIZE"); - get_env_flag(config, &config->inspect, "PYTHONINSPECT"); - - int dont_write_bytecode = 0; - get_env_flag(config, &dont_write_bytecode, "PYTHONDONTWRITEBYTECODE"); - if (dont_write_bytecode) { - config->write_bytecode = 0; - } - - int no_user_site_directory = 0; - get_env_flag(config, &no_user_site_directory, "PYTHONNOUSERSITE"); - if (no_user_site_directory) { - config->user_site_directory = 0; - } - - int unbuffered_stdio = 0; - get_env_flag(config, &unbuffered_stdio, "PYTHONUNBUFFERED"); - if (unbuffered_stdio) { - config->buffered_stdio = 0; - } - -#ifdef MS_WINDOWS - get_env_flag(config, &config->legacy_windows_fs_encoding, - "PYTHONLEGACYWINDOWSFSENCODING"); - get_env_flag(config, &config->legacy_windows_stdio, - "PYTHONLEGACYWINDOWSSTDIO"); -#endif - - if (config->allocator == NULL) { - config->allocator = config_get_env_var(config, "PYTHONMALLOC"); - } - - if (config_get_env_var(config, "PYTHONDUMPREFS")) { - config->dump_refs = 1; - } - if (config_get_env_var(config, "PYTHONMALLOCSTATS")) { - config->malloc_stats = 1; - } - - const char *env = config_get_env_var(config, "PYTHONCOERCECLOCALE"); - if (env) { - if (strcmp(env, "0") == 0) { - config->coerce_c_locale = 0; - } - else if (strcmp(env, "warn") == 0) { - config->coerce_c_locale_warn = 1; - } - else { - config->coerce_c_locale = 1; - } - } - - wchar_t *path; - int res = config_get_env_var_dup(config, &path, L"PYTHONPATH", "PYTHONPATH"); - if (res < 0) { - return DECODE_LOCALE_ERR("PYTHONPATH", res); - } - config->module_search_path_env = path; - - _PyInitError err = config_init_hash_seed(config); - if (_Py_INIT_FAILED(err)) { - return err; - } - - return _Py_INIT_OK(); -} - - -static _PyInitError -config_read_complex_options(_PyCoreConfig *config) -{ - /* More complex options configured by env var and -X option */ - if (config->faulthandler < 0) { - if (config_get_env_var(config, "PYTHONFAULTHANDLER") - || config_get_xoption(config, L"faulthandler")) { - config->faulthandler = 1; - } - } - if (config_get_env_var(config, "PYTHONPROFILEIMPORTTIME") - || config_get_xoption(config, L"importtime")) { - config->import_time = 1; - } - if (config_get_xoption(config, L"dev" ) || - config_get_env_var(config, "PYTHONDEVMODE")) - { - config->dev_mode = 1; - } - - _PyInitError err; - if (config->tracemalloc < 0) { - err = pymain_init_tracemalloc(config); - if (_Py_INIT_FAILED(err)) { - return err; - } - } - - if (config->pycache_prefix == NULL) { - err = pymain_init_pycache_prefix(config); - if (_Py_INIT_FAILED(err)) { - return err; - } - } - return _Py_INIT_OK(); -} - - /* Parse command line options and environment variables. This code must not use Python runtime apart PyMem_Raw memory allocator. @@ -2165,143 +1407,6 @@ pymain_read_conf(_PyMain *pymain, _PyCoreConfig *config, } -static void -config_init_locale(_PyCoreConfig *config) -{ - if (config->utf8_mode >= 0 && config->coerce_c_locale >= 0) { - return; - } - - if (_Py_LegacyLocaleDetected()) { - /* POSIX locale: enable C locale coercion and UTF-8 Mode */ - if (config->utf8_mode < 0) { - config->utf8_mode = 1; - } - if (config->coerce_c_locale < 0) { - config->coerce_c_locale = 1; - } - return; - } -} - - -/* Read configuration settings from standard locations - * - * This function doesn't make any changes to the interpreter state - it - * merely populates any missing configuration settings. This allows an - * embedding application to completely override a config option by - * setting it before calling this function, or else modify the default - * setting before passing the fully populated config to Py_EndInitialization. - * - * More advanced selective initialization tricks are possible by calling - * this function multiple times with various preconfigured settings. - */ - -_PyInitError -_PyCoreConfig_Read(_PyCoreConfig *config) -{ - _PyInitError err; - - _PyCoreConfig_GetGlobalConfig(config); - - if (config->isolated > 0) { - config->use_environment = 0; - config->user_site_directory = 0; - } - -#ifdef MS_WINDOWS - if (config->legacy_windows_fs_encoding) { - config->utf8_mode = 0; - } -#endif - - assert(config->use_environment >= 0); - if (config->use_environment) { - err = config_read_env_vars(config); - if (_Py_INIT_FAILED(err)) { - return err; - } - } - - /* -X options */ - if (config_get_xoption(config, L"showrefcount")) { - config->show_ref_count = 1; - } - if (config_get_xoption(config, L"showalloccount")) { - config->show_alloc_count = 1; - } - - err = config_read_complex_options(config); - if (_Py_INIT_FAILED(err)) { - return err; - } - - if (config->utf8_mode < 0) { - err = config_init_utf8_mode(config); - if (_Py_INIT_FAILED(err)) { - return err; - } - } - - if (config->home == NULL) { - err = config_init_home(config); - if (_Py_INIT_FAILED(err)) { - return err; - } - } - - if (config->program_name == NULL) { - err = config_init_program_name(config); - if (_Py_INIT_FAILED(err)) { - return err; - } - } - - config_init_locale(config); - - if (config->_install_importlib) { - err = _PyCoreConfig_InitPathConfig(config); - if (_Py_INIT_FAILED(err)) { - return err; - } - } - - /* default values */ - if (config->dev_mode) { - if (config->faulthandler < 0) { - config->faulthandler = 1; - } - if (config->allocator == NULL) { - config->allocator = "debug"; - } - } - if (config->use_hash_seed < 0) { - config->use_hash_seed = 0; - config->hash_seed = 0; - } - if (config->faulthandler < 0) { - config->faulthandler = 0; - } - if (config->tracemalloc < 0) { - config->tracemalloc = 0; - } - if (config->coerce_c_locale < 0) { - config->coerce_c_locale = 0; - } - if (config->utf8_mode < 0) { - config->utf8_mode = 0; - } - if (config->_frozen < 0) { - config->_frozen = 0; - } - if (config->argc < 0) { - config->argc = 0; - } - - return _Py_INIT_OK(); -} - - void _PyMainInterpreterConfig_Clear(_PyMainInterpreterConfig *config) { @@ -2373,8 +1478,6 @@ _PyMainInterpreterConfig_Copy(_PyMainInterpreterConfig *config, } - - _PyInitError _PyMainInterpreterConfig_Read(_PyMainInterpreterConfig *main_config, const _PyCoreConfig *config) @@ -2557,7 +1660,7 @@ pymain_cmdline_impl(_PyMain *pymain, _PyCoreConfig *config, } /* For Py_GetArgcArgv(). Cleared by pymain_free(). */ - orig_argv = copy_wstrlist(pymain->argc, cmdline->argv); + orig_argv = _Py_wstrlist_copy(pymain->argc, cmdline->argv); if (orig_argv == NULL) { pymain->err = _Py_INIT_NO_MEMORY(); return -1; diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 90330faa0cf2..42455f5c7da6 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -95,6 +95,7 @@ + @@ -367,6 +368,7 @@ + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index b51fd54f8b4d..582114f6f475 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -84,6 +84,9 @@ Include + + Include + Include @@ -857,6 +860,9 @@ Python + + Python + Python diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 7bb5687d9ae0..4b12048f9d56 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -11,29 +11,6 @@ #include -/* The default encoding used by the platform file system APIs - Can remain NULL for all platforms that don't have such a concept - - Don't forget to modify PyUnicode_DecodeFSDefault() if you touch any of the - values for Py_FileSystemDefaultEncoding! -*/ -#if defined(__APPLE__) -const char *Py_FileSystemDefaultEncoding = "utf-8"; -int Py_HasFileSystemDefaultEncoding = 1; -#elif defined(MS_WINDOWS) -/* may be changed by initfsencoding(), but should never be free()d */ -const char *Py_FileSystemDefaultEncoding = "utf-8"; -int Py_HasFileSystemDefaultEncoding = 1; -#else -const char *Py_FileSystemDefaultEncoding = NULL; /* set by initfsencoding() */ -int Py_HasFileSystemDefaultEncoding = 0; -#endif -const char *Py_FileSystemDefaultEncodeErrors = "surrogateescape"; -/* UTF-8 mode (PEP 540): if equals to 1, use the UTF-8 encoding, and change - stdin and stdout error handler to "surrogateescape". It is equal to - -1 by default: unknown, will be set by Py_Main() */ -int Py_UTF8Mode = -1; - _Py_IDENTIFIER(__builtins__); _Py_IDENTIFIER(__dict__); _Py_IDENTIFIER(__prepare__); diff --git a/Python/bootstrap_hash.c b/Python/bootstrap_hash.c index 073b2ea8dbda..7b187f18877f 100644 --- a/Python/bootstrap_hash.c +++ b/Python/bootstrap_hash.c @@ -538,35 +538,6 @@ _PyOS_URandomNonblock(void *buffer, Py_ssize_t size) return pyurandom(buffer, size, 0, 1); } -int -_Py_ReadHashSeed(const char *seed_text, - int *use_hash_seed, - unsigned long *hash_seed) -{ - Py_BUILD_ASSERT(sizeof(_Py_HashSecret_t) == sizeof(_Py_HashSecret.uc)); - /* Convert a text seed to a numeric one */ - if (seed_text && *seed_text != '\0' && strcmp(seed_text, "random") != 0) { - const char *endptr = seed_text; - unsigned long seed; - seed = strtoul(seed_text, (char **)&endptr, 10); - if (*endptr != '\0' - || seed > 4294967295UL - || (errno == ERANGE && seed == ULONG_MAX)) - { - return -1; - } - /* Use a specific hash */ - *use_hash_seed = 1; - *hash_seed = seed; - } - else { - /* Use a random hash */ - *use_hash_seed = 0; - *hash_seed = 0; - } - return 0; -} - _PyInitError _Py_HashRandomization_Init(const _PyCoreConfig *config) diff --git a/Python/coreconfig.c b/Python/coreconfig.c new file mode 100644 index 000000000000..7dabe5f96447 --- /dev/null +++ b/Python/coreconfig.c @@ -0,0 +1,964 @@ +#include "Python.h" +#include "internal/import.h" +#include "internal/pystate.h" + + +#define DECODE_LOCALE_ERR(NAME, LEN) \ + (((LEN) == -2) \ + ? _Py_INIT_USER_ERR("cannot decode " NAME) \ + : _Py_INIT_NO_MEMORY()) + + +/* Global configuration variables */ + +/* The default encoding used by the platform file system APIs + Can remain NULL for all platforms that don't have such a concept + + Don't forget to modify PyUnicode_DecodeFSDefault() if you touch any of the + values for Py_FileSystemDefaultEncoding! +*/ +#if defined(__APPLE__) +const char *Py_FileSystemDefaultEncoding = "utf-8"; +int Py_HasFileSystemDefaultEncoding = 1; +#elif defined(MS_WINDOWS) +/* may be changed by initfsencoding(), but should never be free()d */ +const char *Py_FileSystemDefaultEncoding = "utf-8"; +int Py_HasFileSystemDefaultEncoding = 1; +#else +const char *Py_FileSystemDefaultEncoding = NULL; /* set by initfsencoding() */ +int Py_HasFileSystemDefaultEncoding = 0; +#endif +const char *Py_FileSystemDefaultEncodeErrors = "surrogateescape"; +/* UTF-8 mode (PEP 540): if equals to 1, use the UTF-8 encoding, and change + stdin and stdout error handler to "surrogateescape". It is equal to + -1 by default: unknown, will be set by Py_Main() */ +int Py_UTF8Mode = -1; +int Py_DebugFlag = 0; /* Needed by parser.c */ +int Py_VerboseFlag = 0; /* Needed by import.c */ +int Py_QuietFlag = 0; /* Needed by sysmodule.c */ +int Py_InteractiveFlag = 0; /* Needed by Py_FdIsInteractive() below */ +int Py_InspectFlag = 0; /* Needed to determine whether to exit at SystemExit */ +int Py_OptimizeFlag = 0; /* Needed by compile.c */ +int Py_NoSiteFlag = 0; /* Suppress 'import site' */ +int Py_BytesWarningFlag = 0; /* Warn on str(bytes) and str(buffer) */ +int Py_FrozenFlag = 0; /* Needed by getpath.c */ +int Py_IgnoreEnvironmentFlag = 0; /* e.g. PYTHONPATH, PYTHONHOME */ +int Py_DontWriteBytecodeFlag = 0; /* Suppress writing bytecode files (*.pyc) */ +int Py_NoUserSiteDirectory = 0; /* for -s and site.py */ +int Py_UnbufferedStdioFlag = 0; /* Unbuffered binary std{in,out,err} */ +int Py_HashRandomizationFlag = 0; /* for -R and PYTHONHASHSEED */ +int Py_IsolatedFlag = 0; /* for -I, isolate from user's env */ +#ifdef MS_WINDOWS +int Py_LegacyWindowsFSEncodingFlag = 0; /* Uses mbcs instead of utf-8 */ +int Py_LegacyWindowsStdioFlag = 0; /* Uses FileIO instead of WindowsConsoleIO */ +#endif +const char *_Py_CheckHashBasedPycsMode = "default"; + + +void +_Py_wstrlist_clear(int len, wchar_t **list) +{ + for (int i=0; i < len; i++) { + PyMem_RawFree(list[i]); + } + PyMem_RawFree(list); +} + + +wchar_t** +_Py_wstrlist_copy(int len, wchar_t **list) +{ + assert((len > 0 && list != NULL) || len == 0); + size_t size = len * sizeof(list[0]); + wchar_t **list_copy = PyMem_RawMalloc(size); + for (int i=0; i < len; i++) { + wchar_t* arg = _PyMem_RawWcsdup(list[i]); + if (arg == NULL) { + _Py_wstrlist_clear(i, list); + return NULL; + } + list_copy[i] = arg; + } + return list_copy; +} + + +/* Free memory allocated in config, but don't clear all attributes */ +void +_PyCoreConfig_Clear(_PyCoreConfig *config) +{ +#define CLEAR(ATTR) \ + do { \ + PyMem_RawFree(ATTR); \ + ATTR = NULL; \ + } while (0) +#define CLEAR_WSTRLIST(LEN, LIST) \ + do { \ + _Py_wstrlist_clear(LEN, LIST); \ + LEN = 0; \ + LIST = NULL; \ + } while (0) + + CLEAR(config->pycache_prefix); + CLEAR(config->module_search_path_env); + CLEAR(config->home); + CLEAR(config->program_name); + CLEAR(config->program); + + CLEAR_WSTRLIST(config->argc, config->argv); + config->argc = -1; + + CLEAR_WSTRLIST(config->nwarnoption, config->warnoptions); + CLEAR_WSTRLIST(config->nxoption, config->xoptions); + CLEAR_WSTRLIST(config->nmodule_search_path, config->module_search_paths); + config->nmodule_search_path = -1; + + CLEAR(config->executable); + CLEAR(config->prefix); + CLEAR(config->base_prefix); + CLEAR(config->exec_prefix); +#ifdef MS_WINDOWS + CLEAR(config->dll_path); +#endif + CLEAR(config->base_exec_prefix); +#undef CLEAR +#undef CLEAR_WSTRLIST +} + + +int +_PyCoreConfig_Copy(_PyCoreConfig *config, const _PyCoreConfig *config2) +{ + _PyCoreConfig_Clear(config); + +#define COPY_ATTR(ATTR) config->ATTR = config2->ATTR +#define COPY_STR_ATTR(ATTR) \ + do { \ + if (config2->ATTR != NULL) { \ + config->ATTR = _PyMem_RawWcsdup(config2->ATTR); \ + if (config->ATTR == NULL) { \ + return -1; \ + } \ + } \ + } while (0) +#define COPY_WSTRLIST(LEN, LIST) \ + do { \ + if (config2->LIST != NULL) { \ + config->LIST = _Py_wstrlist_copy(config2->LEN, config2->LIST); \ + if (config->LIST == NULL) { \ + return -1; \ + } \ + } \ + config->LEN = config2->LEN; \ + } while (0) + + COPY_ATTR(install_signal_handlers); + COPY_ATTR(use_environment); + COPY_ATTR(use_hash_seed); + COPY_ATTR(hash_seed); + COPY_ATTR(_install_importlib); + COPY_ATTR(allocator); + COPY_ATTR(dev_mode); + COPY_ATTR(faulthandler); + COPY_ATTR(tracemalloc); + COPY_ATTR(import_time); + COPY_ATTR(show_ref_count); + COPY_ATTR(show_alloc_count); + COPY_ATTR(dump_refs); + COPY_ATTR(malloc_stats); + + COPY_ATTR(coerce_c_locale); + COPY_ATTR(coerce_c_locale_warn); + COPY_ATTR(utf8_mode); + + COPY_STR_ATTR(pycache_prefix); + COPY_STR_ATTR(module_search_path_env); + COPY_STR_ATTR(home); + COPY_STR_ATTR(program_name); + COPY_STR_ATTR(program); + + COPY_WSTRLIST(argc, argv); + COPY_WSTRLIST(nwarnoption, warnoptions); + COPY_WSTRLIST(nxoption, xoptions); + COPY_WSTRLIST(nmodule_search_path, module_search_paths); + + COPY_STR_ATTR(executable); + COPY_STR_ATTR(prefix); + COPY_STR_ATTR(base_prefix); + COPY_STR_ATTR(exec_prefix); +#ifdef MS_WINDOWS + COPY_STR_ATTR(dll_path); +#endif + COPY_STR_ATTR(base_exec_prefix); + + COPY_ATTR(isolated); + COPY_ATTR(site_import); + COPY_ATTR(bytes_warning); + COPY_ATTR(inspect); + COPY_ATTR(interactive); + COPY_ATTR(optimization_level); + COPY_ATTR(parser_debug); + COPY_ATTR(write_bytecode); + COPY_ATTR(verbose); + COPY_ATTR(quiet); + COPY_ATTR(user_site_directory); + COPY_ATTR(buffered_stdio); +#ifdef MS_WINDOWS + COPY_ATTR(legacy_windows_fs_encoding); + COPY_ATTR(legacy_windows_stdio); +#endif + COPY_ATTR(_check_hash_pycs_mode); + COPY_ATTR(_frozen); + +#undef COPY_ATTR +#undef COPY_STR_ATTR +#undef COPY_WSTRLIST + return 0; +} + + +const char* +_PyCoreConfig_GetEnv(const _PyCoreConfig *config, const char *name) +{ + assert(config->use_environment >= 0); + + if (!config->use_environment) { + return NULL; + } + + const char *var = getenv(name); + if (var && var[0] != '\0') { + return var; + } + else { + return NULL; + } +} + + +int +_PyCoreConfig_GetEnvDup(const _PyCoreConfig *config, + wchar_t **dest, + wchar_t *wname, char *name) +{ + assert(config->use_environment >= 0); + + if (!config->use_environment) { + *dest = NULL; + return 0; + } + +#ifdef MS_WINDOWS + const wchar_t *var = _wgetenv(wname); + if (!var || var[0] == '\0') { + *dest = NULL; + return 0; + } + + wchar_t *copy = _PyMem_RawWcsdup(var); + if (copy == NULL) { + return -1; + } + + *dest = copy; +#else + const char *var = getenv(name); + if (!var || var[0] == '\0') { + *dest = NULL; + return 0; + } + + size_t len; + wchar_t *wvar = Py_DecodeLocale(var, &len); + if (!wvar) { + if (len == (size_t)-2) { + return -2; + } + else { + return -1; + } + } + *dest = wvar; +#endif + return 0; +} + + +void +_PyCoreConfig_GetGlobalConfig(_PyCoreConfig *config) +{ +#define COPY_FLAG(ATTR, VALUE) \ + if (config->ATTR == -1) { \ + config->ATTR = VALUE; \ + } +#define COPY_NOT_FLAG(ATTR, VALUE) \ + if (config->ATTR == -1) { \ + config->ATTR = !(VALUE); \ + } + + COPY_FLAG(utf8_mode, Py_UTF8Mode); + COPY_FLAG(isolated, Py_IsolatedFlag); + COPY_FLAG(bytes_warning, Py_BytesWarningFlag); + COPY_FLAG(inspect, Py_InspectFlag); + COPY_FLAG(interactive, Py_InteractiveFlag); + COPY_FLAG(optimization_level, Py_OptimizeFlag); + COPY_FLAG(parser_debug, Py_DebugFlag); + COPY_FLAG(verbose, Py_VerboseFlag); + COPY_FLAG(quiet, Py_QuietFlag); +#ifdef MS_WINDOWS + COPY_FLAG(legacy_windows_fs_encoding, Py_LegacyWindowsFSEncodingFlag); + COPY_FLAG(legacy_windows_stdio, Py_LegacyWindowsStdioFlag); +#endif + COPY_FLAG(_frozen, Py_FrozenFlag); + + COPY_NOT_FLAG(use_environment, Py_IgnoreEnvironmentFlag); + COPY_NOT_FLAG(buffered_stdio, Py_UnbufferedStdioFlag); + COPY_NOT_FLAG(site_import, Py_NoSiteFlag); + COPY_NOT_FLAG(write_bytecode, Py_DontWriteBytecodeFlag); + COPY_NOT_FLAG(user_site_directory, Py_NoUserSiteDirectory); + + if (config->_check_hash_pycs_mode == NULL) { + config->_check_hash_pycs_mode = _Py_CheckHashBasedPycsMode; + } + +#undef COPY_FLAG +#undef COPY_NOT_FLAG +} + + +/* Set Py_xxx global configuration variables from 'config' configuration. */ +void +_PyCoreConfig_SetGlobalConfig(const _PyCoreConfig *config) +{ +#define COPY_FLAG(ATTR, VAR) \ + if (config->ATTR != -1) { \ + VAR = config->ATTR; \ + } +#define COPY_NOT_FLAG(ATTR, VAR) \ + if (config->ATTR != -1) { \ + VAR = !config->ATTR; \ + } + + COPY_FLAG(utf8_mode, Py_UTF8Mode); + COPY_FLAG(isolated, Py_IsolatedFlag); + COPY_FLAG(bytes_warning, Py_BytesWarningFlag); + COPY_FLAG(inspect, Py_InspectFlag); + COPY_FLAG(interactive, Py_InteractiveFlag); + COPY_FLAG(optimization_level, Py_OptimizeFlag); + COPY_FLAG(parser_debug, Py_DebugFlag); + COPY_FLAG(verbose, Py_VerboseFlag); + COPY_FLAG(quiet, Py_QuietFlag); +#ifdef MS_WINDOWS + COPY_FLAG(legacy_windows_fs_encoding, Py_LegacyWindowsFSEncodingFlag); + COPY_FLAG(legacy_windows_stdio, Py_LegacyWindowsStdioFlag); +#endif + + COPY_NOT_FLAG(use_environment, Py_IgnoreEnvironmentFlag); + COPY_NOT_FLAG(buffered_stdio, Py_UnbufferedStdioFlag); + COPY_NOT_FLAG(site_import, Py_NoSiteFlag); + COPY_NOT_FLAG(write_bytecode, Py_DontWriteBytecodeFlag); + COPY_NOT_FLAG(user_site_directory, Py_NoUserSiteDirectory); + + if (config->_check_hash_pycs_mode != NULL) { + _Py_CheckHashBasedPycsMode = config->_check_hash_pycs_mode; + } + + /* Random or non-zero hash seed */ + Py_HashRandomizationFlag = (config->use_hash_seed == 0 || + config->hash_seed != 0); + +#undef COPY_FLAG +#undef COPY_NOT_FLAG +} + + +/* Get the program name: use PYTHONEXECUTABLE and __PYVENV_LAUNCHER__ + environment variables on macOS if available. */ +static _PyInitError +config_init_program_name(_PyCoreConfig *config) +{ + /* If Py_SetProgramName() was called, use its value */ + const wchar_t *program_name = _Py_path_config.program_name; + if (program_name != NULL) { + config->program_name = _PyMem_RawWcsdup(program_name); + if (config->program_name == NULL) { + return _Py_INIT_NO_MEMORY(); + } + return _Py_INIT_OK(); + } + +#ifdef __APPLE__ + /* On MacOS X, when the Python interpreter is embedded in an + application bundle, it gets executed by a bootstrapping script + that does os.execve() with an argv[0] that's different from the + actual Python executable. This is needed to keep the Finder happy, + or rather, to work around Apple's overly strict requirements of + the process name. However, we still need a usable sys.executable, + so the actual executable path is passed in an environment variable. + See Lib/plat-mac/bundlebuiler.py for details about the bootstrap + script. */ + const char *p = _PyCoreConfig_GetEnv(config, "PYTHONEXECUTABLE"); + if (p != NULL) { + size_t len; + wchar_t* program_name = Py_DecodeLocale(p, &len); + if (program_name == NULL) { + return DECODE_LOCALE_ERR("PYTHONEXECUTABLE environment " + "variable", (Py_ssize_t)len); + } + config->program_name = program_name; + return _Py_INIT_OK(); + } +#ifdef WITH_NEXT_FRAMEWORK + else { + const char* pyvenv_launcher = getenv("__PYVENV_LAUNCHER__"); + if (pyvenv_launcher && *pyvenv_launcher) { + /* Used by Mac/Tools/pythonw.c to forward + * the argv0 of the stub executable + */ + size_t len; + wchar_t* program_name = Py_DecodeLocale(pyvenv_launcher, &len); + if (program_name == NULL) { + return DECODE_LOCALE_ERR("__PYVENV_LAUNCHER__ environment " + "variable", (Py_ssize_t)len); + } + config->program_name = program_name; + return _Py_INIT_OK(); + } + } +#endif /* WITH_NEXT_FRAMEWORK */ +#endif /* __APPLE__ */ + + /* Use argv[0] by default, if available */ + if (config->program != NULL) { + config->program_name = _PyMem_RawWcsdup(config->program); + if (config->program_name == NULL) { + return _Py_INIT_NO_MEMORY(); + } + return _Py_INIT_OK(); + } + + /* Last fall back: hardcoded string */ +#ifdef MS_WINDOWS + const wchar_t *default_program_name = L"python"; +#else + const wchar_t *default_program_name = L"python3"; +#endif + config->program_name = _PyMem_RawWcsdup(default_program_name); + if (config->program_name == NULL) { + return _Py_INIT_NO_MEMORY(); + } + return _Py_INIT_OK(); +} + + +static const wchar_t* +config_get_xoption(const _PyCoreConfig *config, wchar_t *name) +{ + int nxoption = config->nxoption; + wchar_t **xoptions = config->xoptions; + for (int i=0; i < nxoption; i++) { + wchar_t *option = xoptions[i]; + size_t len; + wchar_t *sep = wcschr(option, L'='); + if (sep != NULL) { + len = (sep - option); + } + else { + len = wcslen(option); + } + if (wcsncmp(option, name, len) == 0 && name[len] == L'\0') { + return option; + } + } + return NULL; +} + + +static _PyInitError +config_init_home(_PyCoreConfig *config) +{ + wchar_t *home; + + /* If Py_SetPythonHome() was called, use its value */ + home = _Py_path_config.home; + if (home) { + config->home = _PyMem_RawWcsdup(home); + if (config->home == NULL) { + return _Py_INIT_NO_MEMORY(); + } + return _Py_INIT_OK(); + } + + int res = _PyCoreConfig_GetEnvDup(config, &home, + L"PYTHONHOME", "PYTHONHOME"); + if (res < 0) { + return DECODE_LOCALE_ERR("PYTHONHOME", res); + } + config->home = home; + return _Py_INIT_OK(); +} + + +static _PyInitError +config_init_hash_seed(_PyCoreConfig *config) +{ + const char *seed_text = _PyCoreConfig_GetEnv(config, "PYTHONHASHSEED"); + + Py_BUILD_ASSERT(sizeof(_Py_HashSecret_t) == sizeof(_Py_HashSecret.uc)); + /* Convert a text seed to a numeric one */ + if (seed_text && strcmp(seed_text, "random") != 0) { + const char *endptr = seed_text; + unsigned long seed; + errno = 0; + seed = strtoul(seed_text, (char **)&endptr, 10); + if (*endptr != '\0' + || seed > 4294967295UL + || (errno == ERANGE && seed == ULONG_MAX)) + { + return _Py_INIT_USER_ERR("PYTHONHASHSEED must be \"random\" " + "or an integer in range [0; 4294967295]"); + } + /* Use a specific hash */ + config->use_hash_seed = 1; + config->hash_seed = seed; + } + else { + /* Use a random hash */ + config->use_hash_seed = 0; + config->hash_seed = 0; + } + return _Py_INIT_OK(); +} + + +static _PyInitError +config_init_utf8_mode(_PyCoreConfig *config) +{ + const wchar_t *xopt = config_get_xoption(config, L"utf8"); + if (xopt) { + wchar_t *sep = wcschr(xopt, L'='); + if (sep) { + xopt = sep + 1; + if (wcscmp(xopt, L"1") == 0) { + config->utf8_mode = 1; + } + else if (wcscmp(xopt, L"0") == 0) { + config->utf8_mode = 0; + } + else { + return _Py_INIT_USER_ERR("invalid -X utf8 option value"); + } + } + else { + config->utf8_mode = 1; + } + return _Py_INIT_OK(); + } + + const char *opt = _PyCoreConfig_GetEnv(config, "PYTHONUTF8"); + if (opt) { + if (strcmp(opt, "1") == 0) { + config->utf8_mode = 1; + } + else if (strcmp(opt, "0") == 0) { + config->utf8_mode = 0; + } + else { + return _Py_INIT_USER_ERR("invalid PYTHONUTF8 environment " + "variable value"); + } + return _Py_INIT_OK(); + } + + return _Py_INIT_OK(); +} + + +static int +config_str_to_int(const char *str, int *result) +{ + const char *endptr = str; + errno = 0; + long value = strtol(str, (char **)&endptr, 10); + if (*endptr != '\0' || errno == ERANGE) { + return -1; + } + if (value < INT_MIN || value > INT_MAX) { + return -1; + } + + *result = (int)value; + return 0; +} + + +static int +config_wstr_to_int(const wchar_t *wstr, int *result) +{ + const wchar_t *endptr = wstr; + errno = 0; + long value = wcstol(wstr, (wchar_t **)&endptr, 10); + if (*endptr != '\0' || errno == ERANGE) { + return -1; + } + if (value < INT_MIN || value > INT_MAX) { + return -1; + } + + *result = (int)value; + return 0; +} + + +static void +get_env_flag(_PyCoreConfig *config, int *flag, const char *name) +{ + const char *var = _PyCoreConfig_GetEnv(config, name); + if (!var) { + return; + } + int value; + if (config_str_to_int(var, &value) < 0 || value < 0) { + /* PYTHONDEBUG=text and PYTHONDEBUG=-2 behave as PYTHONDEBUG=1 */ + value = 1; + } + if (*flag < value) { + *flag = value; + } +} + + +static _PyInitError +config_read_env_vars(_PyCoreConfig *config) +{ + assert(config->use_environment > 0); + + /* Get environment variables */ + get_env_flag(config, &config->parser_debug, "PYTHONDEBUG"); + get_env_flag(config, &config->verbose, "PYTHONVERBOSE"); + get_env_flag(config, &config->optimization_level, "PYTHONOPTIMIZE"); + get_env_flag(config, &config->inspect, "PYTHONINSPECT"); + + int dont_write_bytecode = 0; + get_env_flag(config, &dont_write_bytecode, "PYTHONDONTWRITEBYTECODE"); + if (dont_write_bytecode) { + config->write_bytecode = 0; + } + + int no_user_site_directory = 0; + get_env_flag(config, &no_user_site_directory, "PYTHONNOUSERSITE"); + if (no_user_site_directory) { + config->user_site_directory = 0; + } + + int unbuffered_stdio = 0; + get_env_flag(config, &unbuffered_stdio, "PYTHONUNBUFFERED"); + if (unbuffered_stdio) { + config->buffered_stdio = 0; + } + +#ifdef MS_WINDOWS + get_env_flag(config, &config->legacy_windows_fs_encoding, + "PYTHONLEGACYWINDOWSFSENCODING"); + get_env_flag(config, &config->legacy_windows_stdio, + "PYTHONLEGACYWINDOWSSTDIO"); +#endif + + if (config->allocator == NULL) { + config->allocator = _PyCoreConfig_GetEnv(config, "PYTHONMALLOC"); + } + + if (_PyCoreConfig_GetEnv(config, "PYTHONDUMPREFS")) { + config->dump_refs = 1; + } + if (_PyCoreConfig_GetEnv(config, "PYTHONMALLOCSTATS")) { + config->malloc_stats = 1; + } + + const char *env = _PyCoreConfig_GetEnv(config, "PYTHONCOERCECLOCALE"); + if (env) { + if (strcmp(env, "0") == 0) { + config->coerce_c_locale = 0; + } + else if (strcmp(env, "warn") == 0) { + config->coerce_c_locale_warn = 1; + } + else { + config->coerce_c_locale = 1; + } + } + + wchar_t *path; + int res = _PyCoreConfig_GetEnvDup(config, &path, + L"PYTHONPATH", "PYTHONPATH"); + if (res < 0) { + return DECODE_LOCALE_ERR("PYTHONPATH", res); + } + config->module_search_path_env = path; + + if (config->use_hash_seed < 0) { + _PyInitError err = config_init_hash_seed(config); + if (_Py_INIT_FAILED(err)) { + return err; + } + } + + return _Py_INIT_OK(); +} + + +static _PyInitError +config_init_tracemalloc(_PyCoreConfig *config) +{ + int nframe; + int valid; + + const char *env = _PyCoreConfig_GetEnv(config, "PYTHONTRACEMALLOC"); + if (env) { + if (!config_str_to_int(env, &nframe)) { + valid = (nframe >= 0); + } + else { + valid = 0; + } + if (!valid) { + return _Py_INIT_USER_ERR("PYTHONTRACEMALLOC: invalid number " + "of frames"); + } + config->tracemalloc = nframe; + } + + const wchar_t *xoption = config_get_xoption(config, L"tracemalloc"); + if (xoption) { + const wchar_t *sep = wcschr(xoption, L'='); + if (sep) { + if (!config_wstr_to_int(sep + 1, &nframe)) { + valid = (nframe >= 0); + } + else { + valid = 0; + } + if (!valid) { + return _Py_INIT_USER_ERR("-X tracemalloc=NFRAME: " + "invalid number of frames"); + } + } + else { + /* -X tracemalloc behaves as -X tracemalloc=1 */ + nframe = 1; + } + config->tracemalloc = nframe; + } + return _Py_INIT_OK(); +} + + +static _PyInitError +config_init_pycache_prefix(_PyCoreConfig *config) +{ + assert(config->pycache_prefix == NULL); + + const wchar_t *xoption = config_get_xoption(config, L"pycache_prefix"); + if (xoption) { + const wchar_t *sep = wcschr(xoption, L'='); + if (sep && wcslen(sep) > 1) { + config->pycache_prefix = _PyMem_RawWcsdup(sep + 1); + if (config->pycache_prefix == NULL) { + return _Py_INIT_NO_MEMORY(); + } + } + else { + // -X pycache_prefix= can cancel the env var + config->pycache_prefix = NULL; + } + } + else { + wchar_t *env; + int res = _PyCoreConfig_GetEnvDup(config, &env, + L"PYTHONPYCACHEPREFIX", + "PYTHONPYCACHEPREFIX"); + if (res < 0) { + return DECODE_LOCALE_ERR("PYTHONPYCACHEPREFIX", res); + } + + if (env) { + config->pycache_prefix = env; + } + } + return _Py_INIT_OK(); +} + + +static _PyInitError +config_read_complex_options(_PyCoreConfig *config) +{ + /* More complex options configured by env var and -X option */ + if (config->faulthandler < 0) { + if (_PyCoreConfig_GetEnv(config, "PYTHONFAULTHANDLER") + || config_get_xoption(config, L"faulthandler")) { + config->faulthandler = 1; + } + } + if (_PyCoreConfig_GetEnv(config, "PYTHONPROFILEIMPORTTIME") + || config_get_xoption(config, L"importtime")) { + config->import_time = 1; + } + if (config_get_xoption(config, L"dev" ) || + _PyCoreConfig_GetEnv(config, "PYTHONDEVMODE")) + { + config->dev_mode = 1; + } + + _PyInitError err; + if (config->tracemalloc < 0) { + err = config_init_tracemalloc(config); + if (_Py_INIT_FAILED(err)) { + return err; + } + } + + if (config->pycache_prefix == NULL) { + err = config_init_pycache_prefix(config); + if (_Py_INIT_FAILED(err)) { + return err; + } + } + return _Py_INIT_OK(); +} + + +static void +config_init_locale(_PyCoreConfig *config) +{ + if (config->utf8_mode >= 0 && config->coerce_c_locale >= 0) { + return; + } + + if (_Py_LegacyLocaleDetected()) { + /* POSIX locale: enable C locale coercion and UTF-8 Mode */ + if (config->utf8_mode < 0) { + config->utf8_mode = 1; + } + if (config->coerce_c_locale < 0) { + config->coerce_c_locale = 1; + } + return; + } +} + + +/* Read configuration settings from standard locations + * + * This function doesn't make any changes to the interpreter state - it + * merely populates any missing configuration settings. This allows an + * embedding application to completely override a config option by + * setting it before calling this function, or else modify the default + * setting before passing the fully populated config to Py_EndInitialization. + * + * More advanced selective initialization tricks are possible by calling + * this function multiple times with various preconfigured settings. + */ + +_PyInitError +_PyCoreConfig_Read(_PyCoreConfig *config) +{ + _PyInitError err; + + _PyCoreConfig_GetGlobalConfig(config); + + if (config->isolated > 0) { + config->use_environment = 0; + config->user_site_directory = 0; + } + +#ifdef MS_WINDOWS + if (config->legacy_windows_fs_encoding) { + config->utf8_mode = 0; + } +#endif + + assert(config->use_environment >= 0); + if (config->use_environment) { + err = config_read_env_vars(config); + if (_Py_INIT_FAILED(err)) { + return err; + } + } + + /* -X options */ + if (config_get_xoption(config, L"showrefcount")) { + config->show_ref_count = 1; + } + if (config_get_xoption(config, L"showalloccount")) { + config->show_alloc_count = 1; + } + + err = config_read_complex_options(config); + if (_Py_INIT_FAILED(err)) { + return err; + } + + if (config->utf8_mode < 0) { + err = config_init_utf8_mode(config); + if (_Py_INIT_FAILED(err)) { + return err; + } + } + + if (config->home == NULL) { + err = config_init_home(config); + if (_Py_INIT_FAILED(err)) { + return err; + } + } + + if (config->program_name == NULL) { + err = config_init_program_name(config); + if (_Py_INIT_FAILED(err)) { + return err; + } + } + + config_init_locale(config); + + if (config->_install_importlib) { + err = _PyCoreConfig_InitPathConfig(config); + if (_Py_INIT_FAILED(err)) { + return err; + } + } + + /* default values */ + if (config->dev_mode) { + if (config->faulthandler < 0) { + config->faulthandler = 1; + } + if (config->allocator == NULL) { + config->allocator = "debug"; + } + } + if (config->use_hash_seed < 0) { + config->use_hash_seed = 0; + config->hash_seed = 0; + } + if (config->faulthandler < 0) { + config->faulthandler = 0; + } + if (config->tracemalloc < 0) { + config->tracemalloc = 0; + } + if (config->coerce_c_locale < 0) { + config->coerce_c_locale = 0; + } + if (config->utf8_mode < 0) { + config->utf8_mode = 0; + } + if (config->_frozen < 0) { + config->_frozen = 0; + } + if (config->argc < 0) { + config->argc = 0; + } + + return _Py_INIT_OK(); +} diff --git a/Python/import.c b/Python/import.c index 3a591836654b..de132a347e05 100644 --- a/Python/import.c +++ b/Python/import.c @@ -2279,8 +2279,6 @@ static struct PyModuleDef impmodule = { NULL }; -const char *_Py_CheckHashBasedPycsMode = "default"; - PyMODINIT_FUNC PyInit__imp(void) { diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 63f461ab0432..95fb8616b52d 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -108,28 +108,6 @@ _Py_IsFinalizing(void) return _PyRuntime.finalizing != NULL; } -/* Global configuration variable declarations are in pydebug.h */ -/* XXX (ncoghlan): move those declarations to pylifecycle.h? */ -int Py_DebugFlag = 0; /* Needed by parser.c */ -int Py_VerboseFlag = 0; /* Needed by import.c */ -int Py_QuietFlag = 0; /* Needed by sysmodule.c */ -int Py_InteractiveFlag = 0; /* Needed by Py_FdIsInteractive() below */ -int Py_InspectFlag = 0; /* Needed to determine whether to exit at SystemExit */ -int Py_OptimizeFlag = 0; /* Needed by compile.c */ -int Py_NoSiteFlag = 0; /* Suppress 'import site' */ -int Py_BytesWarningFlag = 0; /* Warn on str(bytes) and str(buffer) */ -int Py_FrozenFlag = 0; /* Needed by getpath.c */ -int Py_IgnoreEnvironmentFlag = 0; /* e.g. PYTHONPATH, PYTHONHOME */ -int Py_DontWriteBytecodeFlag = 0; /* Suppress writing bytecode files (*.pyc) */ -int Py_NoUserSiteDirectory = 0; /* for -s and site.py */ -int Py_UnbufferedStdioFlag = 0; /* Unbuffered binary std{in,out,err} */ -int Py_HashRandomizationFlag = 0; /* for -R and PYTHONHASHSEED */ -int Py_IsolatedFlag = 0; /* for -I, isolate from user's env */ -#ifdef MS_WINDOWS -int Py_LegacyWindowsFSEncodingFlag = 0; /* Uses mbcs instead of utf-8 */ -int Py_LegacyWindowsStdioFlag = 0; /* Uses FileIO instead of WindowsConsoleIO */ -#endif - /* Hack to force loading of object files */ int (*_PyOS_mystrnicmp_hack)(const char *, const char *, Py_ssize_t) = \ PyOS_mystrnicmp; /* Python/pystrcmp.o */ From webhook-mailer at python.org Wed Aug 1 12:18:11 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Wed, 01 Aug 2018 16:18:11 -0000 Subject: [Python-checkins] bpo-31650: Remove _Py_CheckHashBasedPycsMode global config var (GH-8608) Message-ID: https://github.com/python/cpython/commit/80b762f010097ab8137782e5fbdc89c5c620ed4e commit: 80b762f010097ab8137782e5fbdc89c5c620ed4e branch: master author: Victor Stinner committer: GitHub date: 2018-08-01T18:18:07+02:00 summary: bpo-31650: Remove _Py_CheckHashBasedPycsMode global config var (GH-8608) bpo-31650, bpo-34170: Replace _Py_CheckHashBasedPycsMode with _PyCoreConfig._check_hash_pycs_mode. Modify PyInit__imp() and zipimport to get the parameter from the current interpreter core configuration. Remove Include/internal/import.h file. files: D Include/internal/import.h M Include/coreconfig.h M Modules/zipimport.c M Programs/_testembed.c M Python/coreconfig.c M Python/import.c diff --git a/Include/coreconfig.h b/Include/coreconfig.h index 4401729d76f7..b2799075f930 100644 --- a/Include/coreconfig.h +++ b/Include/coreconfig.h @@ -241,7 +241,7 @@ typedef struct { valid Set by the --check-hash-based-pycs command line option. - If set to NULL (default), inherit _Py_CheckHashBasedPycsMode value. + The default value is "default". See PEP 552 "Deterministic pycs" for more details. */ const char *_check_hash_pycs_mode; @@ -286,6 +286,7 @@ typedef struct { .buffered_stdio = -1, \ _PyCoreConfig_WINDOWS_INIT \ ._install_importlib = 1, \ + ._check_hash_pycs_mode = "default", \ ._frozen = -1} /* Note: _PyCoreConfig_INIT sets other fields to 0/NULL */ diff --git a/Include/internal/import.h b/Include/internal/import.h deleted file mode 100644 index 4746e755755f..000000000000 --- a/Include/internal/import.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef Py_INTERNAL_IMPORT_H -#define Py_INTERNAL_IMPORT_H - -extern const char *_Py_CheckHashBasedPycsMode; - -#endif diff --git a/Modules/zipimport.c b/Modules/zipimport.c index 85013665d1f4..57e7a13137f4 100644 --- a/Modules/zipimport.c +++ b/Modules/zipimport.c @@ -1,5 +1,4 @@ #include "Python.h" -#include "internal/import.h" #include "internal/pystate.h" #include "structmember.h" #include "osdefs.h" @@ -1352,12 +1351,13 @@ unmarshal_code(PyObject *pathname, PyObject *data, time_t mtime) uint32_t flags = get_uint32(buf + 4); if (flags != 0) { + _PyCoreConfig *config = &PyThreadState_GET()->interp->core_config; // Hash-based pyc. We currently refuse to handle checked hash-based // pycs. We could validate hash-based pycs against the source, but it // seems likely that most people putting hash-based pycs in a zipfile // will use unchecked ones. - if (strcmp(_Py_CheckHashBasedPycsMode, "never") && - (flags != 0x1 || !strcmp(_Py_CheckHashBasedPycsMode, "always"))) + if (strcmp(config->_check_hash_pycs_mode, "never") && + (flags != 0x1 || !strcmp(config->_check_hash_pycs_mode, "always"))) Py_RETURN_NONE; } else if ((mtime != 0 && !eq_mtime(get_uint32(buf + 8), mtime))) { if (Py_VerboseFlag) { diff --git a/Programs/_testembed.c b/Programs/_testembed.c index 1c72580b9c67..3ed42d67f834 100644 --- a/Programs/_testembed.c +++ b/Programs/_testembed.c @@ -436,8 +436,6 @@ static int test_init_global_config(void) /* FIXME: test Py_LegacyWindowsFSEncodingFlag */ /* FIXME: test Py_LegacyWindowsStdioFlag */ - /* _Py_CheckHashBasedPycsMode is not public, and so not tested */ - Py_Initialize(); dump_config(); Py_Finalize(); diff --git a/Python/coreconfig.c b/Python/coreconfig.c index 7dabe5f96447..829592c3c9b2 100644 --- a/Python/coreconfig.c +++ b/Python/coreconfig.c @@ -1,5 +1,4 @@ #include "Python.h" -#include "internal/import.h" #include "internal/pystate.h" @@ -52,7 +51,6 @@ int Py_IsolatedFlag = 0; /* for -I, isolate from user's env */ int Py_LegacyWindowsFSEncodingFlag = 0; /* Uses mbcs instead of utf-8 */ int Py_LegacyWindowsStdioFlag = 0; /* Uses FileIO instead of WindowsConsoleIO */ #endif -const char *_Py_CheckHashBasedPycsMode = "default"; void @@ -317,10 +315,6 @@ _PyCoreConfig_GetGlobalConfig(_PyCoreConfig *config) COPY_NOT_FLAG(write_bytecode, Py_DontWriteBytecodeFlag); COPY_NOT_FLAG(user_site_directory, Py_NoUserSiteDirectory); - if (config->_check_hash_pycs_mode == NULL) { - config->_check_hash_pycs_mode = _Py_CheckHashBasedPycsMode; - } - #undef COPY_FLAG #undef COPY_NOT_FLAG } @@ -359,10 +353,6 @@ _PyCoreConfig_SetGlobalConfig(const _PyCoreConfig *config) COPY_NOT_FLAG(write_bytecode, Py_DontWriteBytecodeFlag); COPY_NOT_FLAG(user_site_directory, Py_NoUserSiteDirectory); - if (config->_check_hash_pycs_mode != NULL) { - _Py_CheckHashBasedPycsMode = config->_check_hash_pycs_mode; - } - /* Random or non-zero hash seed */ Py_HashRandomizationFlag = (config->use_hash_seed == 0 || config->hash_seed != 0); diff --git a/Python/import.c b/Python/import.c index de132a347e05..71d5ea192dbf 100644 --- a/Python/import.c +++ b/Python/import.c @@ -5,7 +5,6 @@ #include "Python-ast.h" #undef Yield /* undefine macro conflicting with winbase.h */ #include "internal/hash.h" -#include "internal/import.h" #include "internal/pystate.h" #include "errcode.h" #include "marshal.h" @@ -2290,7 +2289,8 @@ PyInit__imp(void) d = PyModule_GetDict(m); if (d == NULL) goto failure; - PyObject *pyc_mode = PyUnicode_FromString(_Py_CheckHashBasedPycsMode); + _PyCoreConfig *config = &PyThreadState_GET()->interp->core_config; + PyObject *pyc_mode = PyUnicode_FromString(config->_check_hash_pycs_mode); if (pyc_mode == NULL) { goto failure; } From webhook-mailer at python.org Thu Aug 2 02:18:34 2018 From: webhook-mailer at python.org (Tal Einat) Date: Thu, 02 Aug 2018 06:18:34 -0000 Subject: [Python-checkins] bpo-34120: fix IDLE freezing after closing dialogs (GH-8603) Message-ID: https://github.com/python/cpython/commit/10ea9409ceb5da83cb380b610750551e26561044 commit: 10ea9409ceb5da83cb380b610750551e26561044 branch: master author: Tal Einat committer: GitHub date: 2018-08-02T09:18:29+03:00 summary: bpo-34120: fix IDLE freezing after closing dialogs (GH-8603) Added missing .grab_release() calls to all places where we call .grab_set(). files: A Misc/NEWS.d/next/IDLE/2018-08-01-23-25-38.bpo-34120.HgsIz-.rst M Lib/idlelib/config_key.py M Lib/idlelib/configdialog.py M Lib/idlelib/help_about.py M Lib/idlelib/query.py M Lib/idlelib/textview.py diff --git a/Lib/idlelib/config_key.py b/Lib/idlelib/config_key.py index 7f4bb49ec8a6..abab74265f3e 100644 --- a/Lib/idlelib/config_key.py +++ b/Lib/idlelib/config_key.py @@ -235,10 +235,12 @@ def OK(self, event=None): return if (self.advanced or self.KeysOK(keys)) and self.bind_ok(keys): self.result = keys + self.grab_release() self.destroy() def Cancel(self, event=None): self.result='' + self.grab_release() self.destroy() def KeysOK(self, keys): diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index c78323809403..e682ec0da320 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -191,6 +191,7 @@ def cancel(self): def destroy(self): global font_sample_text font_sample_text = self.fontpage.font_sample.get('1.0', 'end') + self.grab_release() super().destroy() def help(self): diff --git a/Lib/idlelib/help_about.py b/Lib/idlelib/help_about.py index 2a274a930482..64b13ac2abb3 100644 --- a/Lib/idlelib/help_about.py +++ b/Lib/idlelib/help_about.py @@ -195,6 +195,7 @@ def display_file_text(self, title, filename, encoding=None): def ok(self, event=None): "Dismiss help_about dialog." + self.grab_release() self.destroy() diff --git a/Lib/idlelib/query.py b/Lib/idlelib/query.py index 15add6d12748..c2628cceb739 100644 --- a/Lib/idlelib/query.py +++ b/Lib/idlelib/query.py @@ -143,6 +143,10 @@ def cancel(self, event=None): # Do not replace. self.result = None self.destroy() + def destroy(self): + self.grab_release() + super().destroy() + class SectionName(Query): "Get a name for a config file section name." diff --git a/Lib/idlelib/textview.py b/Lib/idlelib/textview.py index d9260e6c7183..e78e297ab993 100644 --- a/Lib/idlelib/textview.py +++ b/Lib/idlelib/textview.py @@ -91,6 +91,7 @@ def __init__(self, parent, title, text, modal=True, def ok(self, event=None): """Dismiss text viewer dialog.""" + self.grab_release() self.destroy() diff --git a/Misc/NEWS.d/next/IDLE/2018-08-01-23-25-38.bpo-34120.HgsIz-.rst b/Misc/NEWS.d/next/IDLE/2018-08-01-23-25-38.bpo-34120.HgsIz-.rst new file mode 100644 index 000000000000..f9954f7c1af0 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2018-08-01-23-25-38.bpo-34120.HgsIz-.rst @@ -0,0 +1 @@ +Fix unresponsiveness after closing certain windows and dialogs. \ No newline at end of file From webhook-mailer at python.org Thu Aug 2 02:37:54 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 02 Aug 2018 06:37:54 -0000 Subject: [Python-checkins] bpo-34120: fix IDLE freezing after closing dialogs (GH-8603) Message-ID: https://github.com/python/cpython/commit/d9fc795487f74531ea43760469cc215858d0d908 commit: d9fc795487f74531ea43760469cc215858d0d908 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-08-01T23:37:50-07:00 summary: bpo-34120: fix IDLE freezing after closing dialogs (GH-8603) Added missing .grab_release() calls to all places where we call .grab_set(). (cherry picked from commit 10ea9409ceb5da83cb380b610750551e26561044) Co-authored-by: Tal Einat files: A Misc/NEWS.d/next/IDLE/2018-08-01-23-25-38.bpo-34120.HgsIz-.rst M Lib/idlelib/config_key.py M Lib/idlelib/configdialog.py M Lib/idlelib/help_about.py M Lib/idlelib/query.py M Lib/idlelib/textview.py diff --git a/Lib/idlelib/config_key.py b/Lib/idlelib/config_key.py index 7f4bb49ec8a6..abab74265f3e 100644 --- a/Lib/idlelib/config_key.py +++ b/Lib/idlelib/config_key.py @@ -235,10 +235,12 @@ def OK(self, event=None): return if (self.advanced or self.KeysOK(keys)) and self.bind_ok(keys): self.result = keys + self.grab_release() self.destroy() def Cancel(self, event=None): self.result='' + self.grab_release() self.destroy() def KeysOK(self, keys): diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index c78323809403..e682ec0da320 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -191,6 +191,7 @@ def cancel(self): def destroy(self): global font_sample_text font_sample_text = self.fontpage.font_sample.get('1.0', 'end') + self.grab_release() super().destroy() def help(self): diff --git a/Lib/idlelib/help_about.py b/Lib/idlelib/help_about.py index 2a274a930482..64b13ac2abb3 100644 --- a/Lib/idlelib/help_about.py +++ b/Lib/idlelib/help_about.py @@ -195,6 +195,7 @@ def display_file_text(self, title, filename, encoding=None): def ok(self, event=None): "Dismiss help_about dialog." + self.grab_release() self.destroy() diff --git a/Lib/idlelib/query.py b/Lib/idlelib/query.py index 15add6d12748..c2628cceb739 100644 --- a/Lib/idlelib/query.py +++ b/Lib/idlelib/query.py @@ -143,6 +143,10 @@ def cancel(self, event=None): # Do not replace. self.result = None self.destroy() + def destroy(self): + self.grab_release() + super().destroy() + class SectionName(Query): "Get a name for a config file section name." diff --git a/Lib/idlelib/textview.py b/Lib/idlelib/textview.py index d9260e6c7183..e78e297ab993 100644 --- a/Lib/idlelib/textview.py +++ b/Lib/idlelib/textview.py @@ -91,6 +91,7 @@ def __init__(self, parent, title, text, modal=True, def ok(self, event=None): """Dismiss text viewer dialog.""" + self.grab_release() self.destroy() diff --git a/Misc/NEWS.d/next/IDLE/2018-08-01-23-25-38.bpo-34120.HgsIz-.rst b/Misc/NEWS.d/next/IDLE/2018-08-01-23-25-38.bpo-34120.HgsIz-.rst new file mode 100644 index 000000000000..f9954f7c1af0 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2018-08-01-23-25-38.bpo-34120.HgsIz-.rst @@ -0,0 +1 @@ +Fix unresponsiveness after closing certain windows and dialogs. \ No newline at end of file From webhook-mailer at python.org Thu Aug 2 03:14:28 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 02 Aug 2018 07:14:28 -0000 Subject: [Python-checkins] bpo-34120: fix IDLE freezing after closing dialogs (GH-8603) Message-ID: https://github.com/python/cpython/commit/9fcfb7b010bd41d4ebaeed372df92b6962253fed commit: 9fcfb7b010bd41d4ebaeed372df92b6962253fed branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-08-02T00:14:24-07:00 summary: bpo-34120: fix IDLE freezing after closing dialogs (GH-8603) Added missing .grab_release() calls to all places where we call .grab_set(). (cherry picked from commit 10ea9409ceb5da83cb380b610750551e26561044) Co-authored-by: Tal Einat files: A Misc/NEWS.d/next/IDLE/2018-08-01-23-25-38.bpo-34120.HgsIz-.rst M Lib/idlelib/config_key.py M Lib/idlelib/configdialog.py M Lib/idlelib/help_about.py M Lib/idlelib/query.py M Lib/idlelib/textview.py diff --git a/Lib/idlelib/config_key.py b/Lib/idlelib/config_key.py index 7f4bb49ec8a6..abab74265f3e 100644 --- a/Lib/idlelib/config_key.py +++ b/Lib/idlelib/config_key.py @@ -235,10 +235,12 @@ def OK(self, event=None): return if (self.advanced or self.KeysOK(keys)) and self.bind_ok(keys): self.result = keys + self.grab_release() self.destroy() def Cancel(self, event=None): self.result='' + self.grab_release() self.destroy() def KeysOK(self, keys): diff --git a/Lib/idlelib/configdialog.py b/Lib/idlelib/configdialog.py index c78323809403..e682ec0da320 100644 --- a/Lib/idlelib/configdialog.py +++ b/Lib/idlelib/configdialog.py @@ -191,6 +191,7 @@ def cancel(self): def destroy(self): global font_sample_text font_sample_text = self.fontpage.font_sample.get('1.0', 'end') + self.grab_release() super().destroy() def help(self): diff --git a/Lib/idlelib/help_about.py b/Lib/idlelib/help_about.py index 2a274a930482..64b13ac2abb3 100644 --- a/Lib/idlelib/help_about.py +++ b/Lib/idlelib/help_about.py @@ -195,6 +195,7 @@ def display_file_text(self, title, filename, encoding=None): def ok(self, event=None): "Dismiss help_about dialog." + self.grab_release() self.destroy() diff --git a/Lib/idlelib/query.py b/Lib/idlelib/query.py index 15add6d12748..c2628cceb739 100644 --- a/Lib/idlelib/query.py +++ b/Lib/idlelib/query.py @@ -143,6 +143,10 @@ def cancel(self, event=None): # Do not replace. self.result = None self.destroy() + def destroy(self): + self.grab_release() + super().destroy() + class SectionName(Query): "Get a name for a config file section name." diff --git a/Lib/idlelib/textview.py b/Lib/idlelib/textview.py index d9260e6c7183..e78e297ab993 100644 --- a/Lib/idlelib/textview.py +++ b/Lib/idlelib/textview.py @@ -91,6 +91,7 @@ def __init__(self, parent, title, text, modal=True, def ok(self, event=None): """Dismiss text viewer dialog.""" + self.grab_release() self.destroy() diff --git a/Misc/NEWS.d/next/IDLE/2018-08-01-23-25-38.bpo-34120.HgsIz-.rst b/Misc/NEWS.d/next/IDLE/2018-08-01-23-25-38.bpo-34120.HgsIz-.rst new file mode 100644 index 000000000000..f9954f7c1af0 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2018-08-01-23-25-38.bpo-34120.HgsIz-.rst @@ -0,0 +1 @@ +Fix unresponsiveness after closing certain windows and dialogs. \ No newline at end of file From webhook-mailer at python.org Thu Aug 2 03:21:53 2018 From: webhook-mailer at python.org (Tal Einat) Date: Thu, 02 Aug 2018 07:21:53 -0000 Subject: [Python-checkins] [2.7] bpo-34120: fix IDLE freezing after closing dialogs (GH-8603) Message-ID: https://github.com/python/cpython/commit/894940b1099677c1ca0aa527dbb935e47d3d591a commit: 894940b1099677c1ca0aa527dbb935e47d3d591a branch: 2.7 author: Tal Einat committer: GitHub date: 2018-08-02T10:21:49+03:00 summary: [2.7] bpo-34120: fix IDLE freezing after closing dialogs (GH-8603) Added missing .grab_release() calls to all places where we call .grab_set(). (cherry picked from commit 10ea9409ceb5da83cb380b610750551e26561044) files: A Misc/NEWS.d/next/IDLE/2018-08-01-23-25-38.bpo-34120.HgsIz-.rst M Lib/idlelib/aboutDialog.py M Lib/idlelib/configDialog.py M Lib/idlelib/configHelpSourceEdit.py M Lib/idlelib/configSectionNameDialog.py M Lib/idlelib/idle_test/test_config_name.py M Lib/idlelib/keybindingDialog.py M Lib/idlelib/textView.py diff --git a/Lib/idlelib/aboutDialog.py b/Lib/idlelib/aboutDialog.py index c9adc08bb2fc..87d6c3cb9c08 100644 --- a/Lib/idlelib/aboutDialog.py +++ b/Lib/idlelib/aboutDialog.py @@ -141,6 +141,7 @@ def display_file_text(self, title, filename, encoding=None): textView.view_file(self, title, fn, encoding) def Ok(self, event=None): + self.grab_release() self.destroy() if __name__ == '__main__': diff --git a/Lib/idlelib/configDialog.py b/Lib/idlelib/configDialog.py index d53f5ff2008d..3c29af13763c 100644 --- a/Lib/idlelib/configDialog.py +++ b/Lib/idlelib/configDialog.py @@ -1197,10 +1197,12 @@ def ActivateConfigChanges(self): instance.reset_help_menu_entries() def Cancel(self): + self.grab_release() self.destroy() def Ok(self): self.Apply() + self.grab_release() self.destroy() def Apply(self): diff --git a/Lib/idlelib/configHelpSourceEdit.py b/Lib/idlelib/configHelpSourceEdit.py index 5816449caf5e..62b010a0ca20 100644 --- a/Lib/idlelib/configHelpSourceEdit.py +++ b/Lib/idlelib/configHelpSourceEdit.py @@ -155,10 +155,12 @@ def Ok(self, event=None): # Mac Safari insists on using the URI form for local files self.result = list(self.result) self.result[1] = "file://" + path + self.grab_release() self.destroy() def Cancel(self, event=None): self.result = None + self.grab_release() self.destroy() if __name__ == '__main__': diff --git a/Lib/idlelib/configSectionNameDialog.py b/Lib/idlelib/configSectionNameDialog.py index c09dca896b0c..f28dc1a28363 100644 --- a/Lib/idlelib/configSectionNameDialog.py +++ b/Lib/idlelib/configSectionNameDialog.py @@ -80,10 +80,13 @@ def Ok(self, event=None): name = self.name_ok() if name: self.result = name + self.grab_release() self.destroy() def Cancel(self, event=None): self.result = '' + self.grab_release() self.destroy() + if __name__ == '__main__': import unittest unittest.main('idlelib.idle_test.test_config_name', verbosity=2, exit=False) diff --git a/Lib/idlelib/idle_test/test_config_name.py b/Lib/idlelib/idle_test/test_config_name.py index 4403f87fd3b1..2a4df6a74458 100644 --- a/Lib/idlelib/idle_test/test_config_name.py +++ b/Lib/idlelib/idle_test/test_config_name.py @@ -15,6 +15,8 @@ class Dummy_name_dialog(object): name = Var() result = None destroyed = False + def grab_release(self): + pass def destroy(self): self.destroyed = True diff --git a/Lib/idlelib/keybindingDialog.py b/Lib/idlelib/keybindingDialog.py index 4d32ca947698..755f1af47e21 100644 --- a/Lib/idlelib/keybindingDialog.py +++ b/Lib/idlelib/keybindingDialog.py @@ -217,10 +217,12 @@ def TranslateKey(self, key, modifiers): def OK(self, event=None): if self.advanced or self.KeysOK(): # doesn't check advanced string yet self.result=self.keyString.get() + self.grab_release() self.destroy() def Cancel(self, event=None): self.result='' + self.grab_release() self.destroy() def KeysOK(self): diff --git a/Lib/idlelib/textView.py b/Lib/idlelib/textView.py index b8c4ac1821d7..ec837f810c30 100644 --- a/Lib/idlelib/textView.py +++ b/Lib/idlelib/textView.py @@ -39,7 +39,8 @@ def __init__(self, parent, title, text, modal=True, _htest=False): self.textView.insert(0.0, text) self.textView.config(state=DISABLED) - if modal: + self.is_modal = modal + if self.is_modal: self.transient(parent) self.grab_set() self.wait_window() @@ -62,6 +63,8 @@ def CreateWidgets(self): frameText.pack(side=TOP,expand=TRUE,fill=BOTH) def Ok(self, event=None): + if self.is_modal: + self.grab_release() self.destroy() diff --git a/Misc/NEWS.d/next/IDLE/2018-08-01-23-25-38.bpo-34120.HgsIz-.rst b/Misc/NEWS.d/next/IDLE/2018-08-01-23-25-38.bpo-34120.HgsIz-.rst new file mode 100644 index 000000000000..f9954f7c1af0 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2018-08-01-23-25-38.bpo-34120.HgsIz-.rst @@ -0,0 +1 @@ +Fix unresponsiveness after closing certain windows and dialogs. \ No newline at end of file From webhook-mailer at python.org Thu Aug 2 03:30:10 2018 From: webhook-mailer at python.org (Tal Einat) Date: Thu, 02 Aug 2018 07:30:10 -0000 Subject: [Python-checkins] bpo-34120: fix text viewer to call grab_release() only when needed (GH-8616) Message-ID: https://github.com/python/cpython/commit/dd74369cb7b230b07ac3a031563406c8f2aae17f commit: dd74369cb7b230b07ac3a031563406c8f2aae17f branch: master author: Tal Einat committer: GitHub date: 2018-08-02T10:30:06+03:00 summary: bpo-34120: fix text viewer to call grab_release() only when needed (GH-8616) files: M Lib/idlelib/textview.py diff --git a/Lib/idlelib/textview.py b/Lib/idlelib/textview.py index e78e297ab993..75b24703b4c3 100644 --- a/Lib/idlelib/textview.py +++ b/Lib/idlelib/textview.py @@ -83,7 +83,8 @@ def __init__(self, parent, title, text, modal=True, command=self.ok, takefocus=False) self.viewframe.pack(side='top', expand=True, fill='both') - if modal: + self.is_modal = modal + if self.is_modal: self.transient(parent) self.grab_set() if not _utest: @@ -91,7 +92,8 @@ def __init__(self, parent, title, text, modal=True, def ok(self, event=None): """Dismiss text viewer dialog.""" - self.grab_release() + if self.is_modal: + self.grab_release() self.destroy() From webhook-mailer at python.org Thu Aug 2 03:52:28 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 02 Aug 2018 07:52:28 -0000 Subject: [Python-checkins] bpo-34120: fix text viewer to call grab_release() only when needed (GH-8616) Message-ID: https://github.com/python/cpython/commit/60586de02de074a33c015e5a013d85d0b17e7e61 commit: 60586de02de074a33c015e5a013d85d0b17e7e61 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-08-02T00:52:22-07:00 summary: bpo-34120: fix text viewer to call grab_release() only when needed (GH-8616) (cherry picked from commit dd74369cb7b230b07ac3a031563406c8f2aae17f) Co-authored-by: Tal Einat files: M Lib/idlelib/textview.py diff --git a/Lib/idlelib/textview.py b/Lib/idlelib/textview.py index e78e297ab993..75b24703b4c3 100644 --- a/Lib/idlelib/textview.py +++ b/Lib/idlelib/textview.py @@ -83,7 +83,8 @@ def __init__(self, parent, title, text, modal=True, command=self.ok, takefocus=False) self.viewframe.pack(side='top', expand=True, fill='both') - if modal: + self.is_modal = modal + if self.is_modal: self.transient(parent) self.grab_set() if not _utest: @@ -91,7 +92,8 @@ def __init__(self, parent, title, text, modal=True, def ok(self, event=None): """Dismiss text viewer dialog.""" - self.grab_release() + if self.is_modal: + self.grab_release() self.destroy() From webhook-mailer at python.org Thu Aug 2 04:31:25 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Thu, 02 Aug 2018 08:31:25 -0000 Subject: [Python-checkins] bpo-34120: fix text viewer to call grab_release() only when needed (GH-8616) Message-ID: https://github.com/python/cpython/commit/8c4a0059accb5cb33e90ec5b2f3e9dc08e2f3048 commit: 8c4a0059accb5cb33e90ec5b2f3e9dc08e2f3048 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-08-02T01:31:14-07:00 summary: bpo-34120: fix text viewer to call grab_release() only when needed (GH-8616) (cherry picked from commit dd74369cb7b230b07ac3a031563406c8f2aae17f) Co-authored-by: Tal Einat files: M Lib/idlelib/textview.py diff --git a/Lib/idlelib/textview.py b/Lib/idlelib/textview.py index e78e297ab993..75b24703b4c3 100644 --- a/Lib/idlelib/textview.py +++ b/Lib/idlelib/textview.py @@ -83,7 +83,8 @@ def __init__(self, parent, title, text, modal=True, command=self.ok, takefocus=False) self.viewframe.pack(side='top', expand=True, fill='both') - if modal: + self.is_modal = modal + if self.is_modal: self.transient(parent) self.grab_set() if not _utest: @@ -91,7 +92,8 @@ def __init__(self, parent, title, text, modal=True, def ok(self, event=None): """Dismiss text viewer dialog.""" - self.grab_release() + if self.is_modal: + self.grab_release() self.destroy() From solipsis at pitrou.net Thu Aug 2 05:11:03 2018 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Thu, 02 Aug 2018 09:11:03 +0000 Subject: [Python-checkins] Daily reference leaks (4243df51fe43): sum=-2 Message-ID: <20180802091103.1.EB7D5E6C334410F8@psf.io> results for 4243df51fe43 on branch "default" -------------------------------------------- test_collections leaked [-7, 1, 0] memory blocks, sum=-6 test_functools leaked [0, 3, 1] memory blocks, sum=4 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/psf-users/antoine/refleaks/reflogBPJGbd', '--timeout', '7200'] From webhook-mailer at python.org Thu Aug 2 07:14:58 2018 From: webhook-mailer at python.org (INADA Naoki) Date: Thu, 02 Aug 2018 11:14:58 -0000 Subject: [Python-checkins] bpo-34287: Do not use second argument of METH_NOARGS functions (GH-8582) Message-ID: https://github.com/python/cpython/commit/fc512e3e0663f7f325862fcd42aef765fd34a453 commit: fc512e3e0663f7f325862fcd42aef765fd34a453 branch: master author: jdemeyer committer: INADA Naoki date: 2018-08-02T20:14:54+09:00 summary: bpo-34287: Do not use second argument of METH_NOARGS functions (GH-8582) files: M Modules/_io/bufferedio.c diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index 358a654a98ca..9d3b4466493c 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -403,7 +403,7 @@ buffered_dealloc(buffered *self) } static PyObject * -buffered_sizeof(buffered *self, void *unused) +buffered_sizeof(buffered *self, PyObject *Py_UNUSED(ignored)) { Py_ssize_t res; @@ -540,7 +540,7 @@ buffered_close(buffered *self, PyObject *args) /* detach */ static PyObject * -buffered_detach(buffered *self, PyObject *args) +buffered_detach(buffered *self, PyObject *Py_UNUSED(ignored)) { PyObject *raw, *res; CHECK_INITIALIZED(self) @@ -558,21 +558,21 @@ buffered_detach(buffered *self, PyObject *args) /* Inquiries */ static PyObject * -buffered_seekable(buffered *self, PyObject *args) +buffered_seekable(buffered *self, PyObject *Py_UNUSED(ignored)) { CHECK_INITIALIZED(self) return PyObject_CallMethodObjArgs(self->raw, _PyIO_str_seekable, NULL); } static PyObject * -buffered_readable(buffered *self, PyObject *args) +buffered_readable(buffered *self, PyObject *Py_UNUSED(ignored)) { CHECK_INITIALIZED(self) return PyObject_CallMethodObjArgs(self->raw, _PyIO_str_readable, NULL); } static PyObject * -buffered_writable(buffered *self, PyObject *args) +buffered_writable(buffered *self, PyObject *Py_UNUSED(ignored)) { CHECK_INITIALIZED(self) return PyObject_CallMethodObjArgs(self->raw, _PyIO_str_writable, NULL); @@ -595,14 +595,14 @@ buffered_mode_get(buffered *self, void *context) /* Lower-level APIs */ static PyObject * -buffered_fileno(buffered *self, PyObject *args) +buffered_fileno(buffered *self, PyObject *Py_UNUSED(ignored)) { CHECK_INITIALIZED(self) return PyObject_CallMethodObjArgs(self->raw, _PyIO_str_fileno, NULL); } static PyObject * -buffered_isatty(buffered *self, PyObject *args) +buffered_isatty(buffered *self, PyObject *Py_UNUSED(ignored)) { CHECK_INITIALIZED(self) return PyObject_CallMethodObjArgs(self->raw, _PyIO_str_isatty, NULL); @@ -611,7 +611,7 @@ buffered_isatty(buffered *self, PyObject *args) /* Serialization */ static PyObject * -buffered_getstate(buffered *self, PyObject *args) +buffered_getstate(buffered *self, PyObject *Py_UNUSED(ignored)) { PyErr_Format(PyExc_TypeError, "cannot serialize '%s' object", Py_TYPE(self)->tp_name); @@ -1202,7 +1202,7 @@ _io__Buffered_readline_impl(buffered *self, Py_ssize_t size) static PyObject * -buffered_tell(buffered *self, PyObject *args) +buffered_tell(buffered *self, PyObject *Py_UNUSED(ignored)) { Py_off_t pos; @@ -2207,33 +2207,33 @@ bufferedrwpair_write(rwpair *self, PyObject *args) } static PyObject * -bufferedrwpair_flush(rwpair *self, PyObject *args) +bufferedrwpair_flush(rwpair *self, PyObject *Py_UNUSED(ignored)) { - return _forward_call(self->writer, &PyId_flush, args); + return _forward_call(self->writer, &PyId_flush, NULL); } static PyObject * -bufferedrwpair_readable(rwpair *self, PyObject *args) +bufferedrwpair_readable(rwpair *self, PyObject *Py_UNUSED(ignored)) { - return _forward_call(self->reader, &PyId_readable, args); + return _forward_call(self->reader, &PyId_readable, NULL); } static PyObject * -bufferedrwpair_writable(rwpair *self, PyObject *args) +bufferedrwpair_writable(rwpair *self, PyObject *Py_UNUSED(ignored)) { - return _forward_call(self->writer, &PyId_writable, args); + return _forward_call(self->writer, &PyId_writable, NULL); } static PyObject * -bufferedrwpair_close(rwpair *self, PyObject *args) +bufferedrwpair_close(rwpair *self, PyObject *Py_UNUSED(ignored)) { PyObject *exc = NULL, *val, *tb; - PyObject *ret = _forward_call(self->writer, &PyId_close, args); + PyObject *ret = _forward_call(self->writer, &PyId_close, NULL); if (ret == NULL) PyErr_Fetch(&exc, &val, &tb); else Py_DECREF(ret); - ret = _forward_call(self->reader, &PyId_close, args); + ret = _forward_call(self->reader, &PyId_close, NULL); if (exc != NULL) { _PyErr_ChainExceptions(exc, val, tb); Py_CLEAR(ret); @@ -2242,9 +2242,9 @@ bufferedrwpair_close(rwpair *self, PyObject *args) } static PyObject * -bufferedrwpair_isatty(rwpair *self, PyObject *args) +bufferedrwpair_isatty(rwpair *self, PyObject *Py_UNUSED(ignored)) { - PyObject *ret = _forward_call(self->writer, &PyId_isatty, args); + PyObject *ret = _forward_call(self->writer, &PyId_isatty, NULL); if (ret != Py_False) { /* either True or exception */ @@ -2252,7 +2252,7 @@ bufferedrwpair_isatty(rwpair *self, PyObject *args) } Py_DECREF(ret); - return _forward_call(self->reader, &PyId_isatty, args); + return _forward_call(self->reader, &PyId_isatty, NULL); } static PyObject * From webhook-mailer at python.org Thu Aug 2 09:04:57 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Thu, 02 Aug 2018 13:04:57 -0000 Subject: [Python-checkins] bpo-34097: Add support for zipping files older than 1980-01-01 (GH-8270) Message-ID: https://github.com/python/cpython/commit/a2fe1e52eb94c41d9ebce1ab284180d7b1faa2a4 commit: a2fe1e52eb94c41d9ebce1ab284180d7b1faa2a4 branch: master author: Marcel Plch committer: Victor Stinner date: 2018-08-02T15:04:52+02:00 summary: bpo-34097: Add support for zipping files older than 1980-01-01 (GH-8270) ZipFile can zip files older than 1980-01-01 and newer than 2107-12-31 using a new strict_timestamps parameter at the cost of setting the timestamp to the limit. files: A Misc/NEWS.d/next/Library/2018-07-13-13-42-10.bpo-34097.F5Dk5o.rst M Doc/library/zipfile.rst M Lib/test/test_zipfile.py M Lib/zipfile.py diff --git a/Doc/library/zipfile.rst b/Doc/library/zipfile.rst index c0f2a89a3a17..a2fa9666acc1 100644 --- a/Doc/library/zipfile.rst +++ b/Doc/library/zipfile.rst @@ -368,7 +368,7 @@ ZipFile Objects .. method:: ZipFile.write(filename, arcname=None, compress_type=None, \ - compresslevel=None) + compresslevel=None, *, strict_timestamps=True) Write the file named *filename* to the archive, giving it the archive name *arcname* (by default, this will be the same as *filename*, but without a drive @@ -377,6 +377,11 @@ ZipFile Objects the new entry. Similarly, *compresslevel* will override the constructor if given. The archive must be open with mode ``'w'``, ``'x'`` or ``'a'``. + The *strict_timestamps* argument, when set to ``False``, allows to + zip files older than 1980-01-01 at the cost of setting the + timestamp to 1980-01-01. + Similar behavior occurs with files newer than 2107-12-31, + the timestamp is also set to the limit. .. note:: @@ -400,6 +405,9 @@ ZipFile Objects a closed ZipFile will raise a :exc:`ValueError`. Previously, a :exc:`RuntimeError` was raised. + .. versionadded:: 3.8 + The *strict_timestamps* keyword-only argument + .. method:: ZipFile.writestr(zinfo_or_arcname, data, compress_type=None, \ compresslevel=None) @@ -540,7 +548,8 @@ information about a single member of the ZIP archive. There is one classmethod to make a :class:`ZipInfo` instance for a filesystem file: -.. classmethod:: ZipInfo.from_file(filename, arcname=None) +.. classmethod:: ZipInfo.from_file(filename, arcname=None, *, \ + strict_timestamps=True) Construct a :class:`ZipInfo` instance for a file on the filesystem, in preparation for adding it to a zip file. @@ -551,11 +560,20 @@ file: If *arcname* is not specified, the name will be the same as *filename*, but with any drive letter and leading path separators removed. + The *strict_timestamps* argument, when set to ``False``, allows to + zip files older than 1980-01-01 at the cost of setting the + timestamp to 1980-01-01. + Similar behavior occurs with files newer than 2107-12-31, + the timestamp is also set to the limit. + .. versionadded:: 3.6 .. versionchanged:: 3.6.2 The *filename* parameter accepts a :term:`path-like object`. + .. versionadded:: 3.8 + The *strict_timestamps* keyword-only argument + Instances have the following methods and attributes: diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index ac9a4ff6fef0..3b78b7f16759 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -549,6 +549,22 @@ def test_add_file_before_1980(self): with zipfile.ZipFile(TESTFN2, "w") as zipfp: self.assertRaises(ValueError, zipfp.write, TESTFN) + with zipfile.ZipFile(TESTFN2, "w") as zipfp: + zipfp.write(TESTFN, strict_timestamps=False) + zinfo = zipfp.getinfo(TESTFN) + self.assertEqual(zinfo.date_time, (1980, 1, 1, 0, 0, 0)) + + def test_add_file_after_2107(self): + # Set atime and mtime to 2108-12-30 + os.utime(TESTFN, (4386268800, 4386268800)) + with zipfile.ZipFile(TESTFN2, "w") as zipfp: + self.assertRaises(struct.error, zipfp.write, TESTFN) + + with zipfile.ZipFile(TESTFN2, "w") as zipfp: + zipfp.write(TESTFN, strict_timestamps=False) + zinfo = zipfp.getinfo(TESTFN) + self.assertEqual(zinfo.date_time, (2107, 12, 31, 23, 59, 59)) + @requires_zlib class DeflateTestsWithSourceFile(AbstractTestsWithSourceFile, diff --git a/Lib/zipfile.py b/Lib/zipfile.py index 2757ce91cf48..6da1778873dd 100644 --- a/Lib/zipfile.py +++ b/Lib/zipfile.py @@ -469,7 +469,7 @@ def _decodeExtra(self): extra = extra[ln+4:] @classmethod - def from_file(cls, filename, arcname=None): + def from_file(cls, filename, arcname=None, *, strict_timestamps=True): """Construct an appropriate ZipInfo for a file on the filesystem. filename should be the path to a file or directory on the filesystem. @@ -484,6 +484,10 @@ def from_file(cls, filename, arcname=None): isdir = stat.S_ISDIR(st.st_mode) mtime = time.localtime(st.st_mtime) date_time = mtime[0:6] + if not strict_timestamps and date_time[0] < 1980: + date_time = (1980, 1, 1, 0, 0, 0) + elif not strict_timestamps and date_time[0] > 2107: + date_time = (2107, 12, 31, 23, 59, 59) # Create ZipInfo instance to store file information if arcname is None: arcname = filename @@ -1673,7 +1677,8 @@ def _writecheck(self, zinfo): " would require ZIP64 extensions") def write(self, filename, arcname=None, - compress_type=None, compresslevel=None): + compress_type=None, compresslevel=None, *, + strict_timestamps=True): """Put the bytes from filename into the archive under the name arcname.""" if not self.fp: @@ -1684,7 +1689,8 @@ def write(self, filename, arcname=None, "Can't write to ZIP archive while an open writing handle exists" ) - zinfo = ZipInfo.from_file(filename, arcname) + zinfo = ZipInfo.from_file(filename, arcname, + strict_timestamps=strict_timestamps) if zinfo.is_dir(): zinfo.compress_size = 0 diff --git a/Misc/NEWS.d/next/Library/2018-07-13-13-42-10.bpo-34097.F5Dk5o.rst b/Misc/NEWS.d/next/Library/2018-07-13-13-42-10.bpo-34097.F5Dk5o.rst new file mode 100644 index 000000000000..397149559af5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-07-13-13-42-10.bpo-34097.F5Dk5o.rst @@ -0,0 +1,3 @@ +ZipFile can zip files older than 1980-01-01 and newer than 2107-12-31 using +a new ``strict_timestamps`` parameter at the cost of setting the timestamp +to the limit. From webhook-mailer at python.org Thu Aug 2 09:58:41 2018 From: webhook-mailer at python.org (Larry Hastings) Date: Thu, 02 Aug 2018 13:58:41 -0000 Subject: [Python-checkins] Add Blurb entry for 3.5.6 final. Message-ID: https://github.com/python/cpython/commit/b86a50f8a04473822cda25dfcadad09e70f1d810 commit: b86a50f8a04473822cda25dfcadad09e70f1d810 branch: 3.5 author: Larry Hastings committer: Larry Hastings date: 2018-08-02T02:16:53-07:00 summary: Add Blurb entry for 3.5.6 final. files: A Misc/NEWS.d/3.5.6.rst diff --git a/Misc/NEWS.d/3.5.6.rst b/Misc/NEWS.d/3.5.6.rst new file mode 100644 index 000000000000..b03d965bcac3 --- /dev/null +++ b/Misc/NEWS.d/3.5.6.rst @@ -0,0 +1,8 @@ +.. bpo: 0 +.. date: 2018-08-02 +.. no changes: True +.. nonce: zrt4E5 +.. release date: 2018-08-02 +.. section: Library + +There were no changes between 3.5.6rc1 and 3.5.6 final. From webhook-mailer at python.org Thu Aug 2 10:47:31 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Thu, 02 Aug 2018 14:47:31 -0000 Subject: [Python-checkins] bpo-29565: Corrected ctypes passing of large structs by value on Windows AMD64 (GH-168) (GH-8625) Message-ID: https://github.com/python/cpython/commit/3243f8c1fb16b6de73f1d7a30f5d09047553bce3 commit: 3243f8c1fb16b6de73f1d7a30f5d09047553bce3 branch: 2.7 author: Victor Stinner committer: GitHub date: 2018-08-02T16:47:26+02:00 summary: bpo-29565: Corrected ctypes passing of large structs by value on Windows AMD64 (GH-168) (GH-8625) Fixed bpo-29565: Corrected ctypes passing of large structs by value. Added code and test to check that when a structure passed by value is large enough to need to be passed by reference, a copy of the original structure is passed. The callee updates the passed-in value, and the test verifies that the caller's copy is unchanged. A similar change was also added to the test added for bpo-20160 (that test was passing, but the changes should guard against regressions). (cherry picked from commit a86339b83fbd0932e0529a3c91935e997a234582) files: M Lib/ctypes/test/test_callbacks.py M Lib/ctypes/test/test_structures.py M Modules/_ctypes/_ctypes_test.c M Modules/_ctypes/libffi_msvc/ffi.c diff --git a/Lib/ctypes/test/test_callbacks.py b/Lib/ctypes/test/test_callbacks.py index bf894d481c3e..db3d9e7b198b 100644 --- a/Lib/ctypes/test/test_callbacks.py +++ b/Lib/ctypes/test/test_callbacks.py @@ -250,6 +250,7 @@ def callback(a, b, c, d, e): def test_callback_large_struct(self): class Check: pass + # This should mirror the structure in Modules/_ctypes/_ctypes_test.c class X(Structure): _fields_ = [ ('first', c_ulong), @@ -261,6 +262,11 @@ def callback(check, s): check.first = s.first check.second = s.second check.third = s.third + # See issue #29565. + # The structure should be passed by value, so + # any changes to it should not be reflected in + # the value passed + s.first = s.second = s.third = 0x0badf00d check = Check() s = X() @@ -281,6 +287,11 @@ def callback(check, s): self.assertEqual(check.first, 0xdeadbeef) self.assertEqual(check.second, 0xcafebabe) self.assertEqual(check.third, 0x0bad1dea) + # See issue #29565. + # Ensure that the original struct is unchanged. + self.assertEqual(s.first, check.first) + self.assertEqual(s.second, check.second) + self.assertEqual(s.third, check.third) ################################################################ diff --git a/Lib/ctypes/test/test_structures.py b/Lib/ctypes/test/test_structures.py index 5650189f8a89..9a863c94dafc 100644 --- a/Lib/ctypes/test/test_structures.py +++ b/Lib/ctypes/test/test_structures.py @@ -3,6 +3,7 @@ from ctypes.test import need_symbol from struct import calcsize import _testcapi +import _ctypes_test class SubclassesTest(unittest.TestCase): def test_subclass(self): @@ -401,6 +402,28 @@ class Z(Y): (1, 0, 0, 0, 0, 0)) self.assertRaises(TypeError, lambda: Z(1, 2, 3, 4, 5, 6, 7)) + def test_pass_by_value(self): + # This should mirror the structure in Modules/_ctypes/_ctypes_test.c + class X(Structure): + _fields_ = [ + ('first', c_ulong), + ('second', c_ulong), + ('third', c_ulong), + ] + + s = X() + s.first = 0xdeadbeef + s.second = 0xcafebabe + s.third = 0x0bad1dea + dll = CDLL(_ctypes_test.__file__) + func = dll._testfunc_large_struct_update_value + func.argtypes = (X,) + func.restype = None + func(s) + self.assertEqual(s.first, 0xdeadbeef) + self.assertEqual(s.second, 0xcafebabe) + self.assertEqual(s.third, 0x0bad1dea) + class PointerMemberTestCase(unittest.TestCase): def test(self): diff --git a/Modules/_ctypes/_ctypes_test.c b/Modules/_ctypes/_ctypes_test.c index f295c6fc4c69..94678f3189ce 100644 --- a/Modules/_ctypes/_ctypes_test.c +++ b/Modules/_ctypes/_ctypes_test.c @@ -52,6 +52,19 @@ _testfunc_cbk_large_struct(Test in, void (*func)(Test)) func(in); } +/* + * See issue 29565. Update a structure passed by value; + * the caller should not see any change. + */ + +EXPORT(void) +_testfunc_large_struct_update_value(Test in) +{ + in.first = 0x0badf00d; + in.second = 0x0badf00d; + in.third = 0x0badf00d; +} + EXPORT(void)testfunc_array(int values[4]) { printf("testfunc_array %d %d %d %d\n", diff --git a/Modules/_ctypes/libffi_msvc/ffi.c b/Modules/_ctypes/libffi_msvc/ffi.c index 515d802fd894..75dada56133d 100644 --- a/Modules/_ctypes/libffi_msvc/ffi.c +++ b/Modules/_ctypes/libffi_msvc/ffi.c @@ -220,6 +220,16 @@ ffi_call(/*@dependent@*/ ffi_cif *cif, break; #else case FFI_SYSV: + /* If a single argument takes more than 8 bytes, + then a copy is passed by reference. */ + for (unsigned i = 0; i < cif->nargs; i++) { + size_t z = cif->arg_types[i]->size; + if (z > 8) { + void *temp = alloca(z); + memcpy(temp, avalue[i], z); + avalue[i] = temp; + } + } /*@-usedef@*/ return ffi_call_AMD64(ffi_prep_args, &ecif, cif->bytes, cif->flags, ecif.rvalue, fn); From webhook-mailer at python.org Thu Aug 2 11:40:23 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Thu, 02 Aug 2018 15:40:23 -0000 Subject: [Python-checkins] bpo-29565: Fix compilation for C89 (GH-8626) Message-ID: https://github.com/python/cpython/commit/6a6b2483479a1ad0ab82300452f0ce71fa90b2d7 commit: 6a6b2483479a1ad0ab82300452f0ce71fa90b2d7 branch: 2.7 author: Victor Stinner committer: GitHub date: 2018-08-02T17:40:11+02:00 summary: bpo-29565: Fix compilation for C89 (GH-8626) Use a local scope for the 'i' variable. files: M Modules/_ctypes/libffi_msvc/ffi.c diff --git a/Modules/_ctypes/libffi_msvc/ffi.c b/Modules/_ctypes/libffi_msvc/ffi.c index 75dada56133d..f28c3e0a3859 100644 --- a/Modules/_ctypes/libffi_msvc/ffi.c +++ b/Modules/_ctypes/libffi_msvc/ffi.c @@ -220,14 +220,18 @@ ffi_call(/*@dependent@*/ ffi_cif *cif, break; #else case FFI_SYSV: - /* If a single argument takes more than 8 bytes, - then a copy is passed by reference. */ - for (unsigned i = 0; i < cif->nargs; i++) { - size_t z = cif->arg_types[i]->size; - if (z > 8) { - void *temp = alloca(z); - memcpy(temp, avalue[i], z); - avalue[i] = temp; + /* use a local scope for the 'i' variable */ + { + unsigned i; + /* If a single argument takes more than 8 bytes, + then a copy is passed by reference. */ + for (i = 0; i < cif->nargs; i++) { + size_t z = cif->arg_types[i]->size; + if (z > 8) { + void *temp = alloca(z); + memcpy(temp, avalue[i], z); + avalue[i] = temp; + } } } /*@-usedef@*/ From webhook-mailer at python.org Thu Aug 2 12:03:03 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Thu, 02 Aug 2018 16:03:03 -0000 Subject: [Python-checkins] Fix compilation warnings on Windows (GH-8627) Message-ID: https://github.com/python/cpython/commit/dd4d8b4d80065409dae69f966fd7617e5b3d97f1 commit: dd4d8b4d80065409dae69f966fd7617e5b3d97f1 branch: 2.7 author: Victor Stinner committer: GitHub date: 2018-08-02T18:02:59+02:00 summary: Fix compilation warnings on Windows (GH-8627) * Fix compilation warning in _ctypes module on Window (cherry picked from commit 20f11fe43c47b68c8b9dd6539d2d40b66c9957f9) * Fix compilation warnings on Windows 64-bit (cherry picked from commit 725e4212229bf68f87d4f66c1815d444ddfc7aa5) * Fix compiler warning in unicodeobject.c Explicitly case to Py_UNICODE to fix the warning: Objects\unicodeobject.c(4225): warning C4244: '=' : conversion from 'long' to 'Py_UNICODE', possible loss of data The downcast cannot overflow since we check that value <= 0x10ffff. files: M Modules/_ctypes/libffi_msvc/ffi.c M Modules/_sqlite/util.c M Objects/unicodeobject.c diff --git a/Modules/_ctypes/libffi_msvc/ffi.c b/Modules/_ctypes/libffi_msvc/ffi.c index f28c3e0a3859..587c94b7e61f 100644 --- a/Modules/_ctypes/libffi_msvc/ffi.c +++ b/Modules/_ctypes/libffi_msvc/ffi.c @@ -119,7 +119,7 @@ void ffi_prep_args(char *stack, extended_cif *ecif) argp += z; } - if (argp - stack > ecif->cif->bytes) + if (argp >= stack && (unsigned)(argp - stack) > ecif->cif->bytes) { Py_FatalError("FFI BUG: not enough stack space for arguments"); } diff --git a/Modules/_sqlite/util.c b/Modules/_sqlite/util.c index a24dd8c63473..73772250f914 100644 --- a/Modules/_sqlite/util.c +++ b/Modules/_sqlite/util.c @@ -132,7 +132,7 @@ _pysqlite_long_from_int64(sqlite_int64 value) } # endif #endif - return PyInt_FromLong(value); + return PyInt_FromLong(Py_SAFE_DOWNCAST(value, sqlite_int64, long)); } sqlite_int64 diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 8bf04df2c86c..b76db619ad76 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -4222,7 +4222,7 @@ PyObject *PyUnicode_DecodeCharmap(const char *s, p = PyUnicode_AS_UNICODE(v) + oldpos; } value -= 0x10000; - *p++ = 0xD800 | (value >> 10); + *p++ = 0xD800 | (Py_UNICODE)(value >> 10); *p++ = 0xDC00 | (value & 0x3FF); extrachars -= 2; } From webhook-mailer at python.org Thu Aug 2 12:09:03 2018 From: webhook-mailer at python.org (Berker Peksag) Date: Thu, 02 Aug 2018 16:09:03 -0000 Subject: [Python-checkins] bpo-27910: Update documentation of traceback module (GH-6116) Message-ID: https://github.com/python/cpython/commit/f394ee5eaf6d6d8f45e0478e77d4dbff25c6bea7 commit: f394ee5eaf6d6d8f45e0478e77d4dbff25c6bea7 branch: master author: torsava committer: Berker Peksag date: 2018-08-02T19:08:59+03:00 summary: bpo-27910: Update documentation of traceback module (GH-6116) In the documentation for the traceback module, the definitions of functions extract_tb(), format_list() and classmethod StackSummary.from_list() mention the old style 4-tuples that these functions used to return or accept. Since Python 3.5, however, they return or accept a FrameSummary object instead of a 4-tuple, or a StackSummary object instead of a list of 4-tuples. Co-Authored-By: Berker Peksag files: M Doc/library/traceback.rst M Lib/traceback.py diff --git a/Doc/library/traceback.rst b/Doc/library/traceback.rst index 55d331c996a1..7ac3cacd3d1c 100644 --- a/Doc/library/traceback.rst +++ b/Doc/library/traceback.rst @@ -88,14 +88,16 @@ The module defines the following functions: .. function:: extract_tb(tb, limit=None) - Return a list of "pre-processed" stack trace entries extracted from the - traceback object *tb*. It is useful for alternate formatting of - stack traces. The optional *limit* argument has the same meaning as for - :func:`print_tb`. A "pre-processed" stack trace entry is a 4-tuple - (*filename*, *line number*, *function name*, *text*) representing the - information that is usually printed for a stack trace. The *text* is a - string with leading and trailing whitespace stripped; if the source is - not available it is ``None``. + Return a :class:`StackSummary` object representing a list of "pre-processed" + stack trace entries extracted from the traceback object *tb*. It is useful + for alternate formatting of stack traces. The optional *limit* argument has + the same meaning as for :func:`print_tb`. A "pre-processed" stack trace + entry is a :class:`FrameSummary` object containing attributes + :attr:`~FrameSummary.filename`, :attr:`~FrameSummary.lineno`, + :attr:`~FrameSummary.name`, and :attr:`~FrameSummary.line` representing the + information that is usually printed for a stack trace. The + :attr:`~FrameSummary.line` is a string with leading and trailing + whitespace stripped; if the source is not available it is ``None``. .. function:: extract_stack(f=None, limit=None) @@ -107,12 +109,12 @@ The module defines the following functions: .. function:: format_list(extracted_list) - Given a list of tuples as returned by :func:`extract_tb` or - :func:`extract_stack`, return a list of strings ready for printing. Each - string in the resulting list corresponds to the item with the same index in - the argument list. Each string ends in a newline; the strings may contain - internal newlines as well, for those items whose source text line is not - ``None``. + Given a list of tuples or :class:`FrameSummary` objects as returned by + :func:`extract_tb` or :func:`extract_stack`, return a list of strings ready + for printing. Each string in the resulting list corresponds to the item with + the same index in the argument list. Each string ends in a newline; the + strings may contain internal newlines as well, for those items whose source + text line is not ``None``. .. function:: format_exception_only(etype, value) @@ -293,9 +295,9 @@ capture data for later printing in a lightweight fashion. .. classmethod:: from_list(a_list) - Construct a :class:`StackSummary` object from a supplied old-style list - of tuples. Each tuple should be a 4-tuple with filename, lineno, name, - line as the elements. + Construct a :class:`StackSummary` object from a supplied list of + :class:`FrameSummary` objects or old-style list of tuples. Each tuple + should be a 4-tuple with filename, lineno, name, line as the elements. .. method:: format() diff --git a/Lib/traceback.py b/Lib/traceback.py index ee8c73914032..afab0a4b91f1 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -25,10 +25,12 @@ def print_list(extracted_list, file=None): print(item, file=file, end="") def format_list(extracted_list): - """Format a list of traceback entry tuples for printing. + """Format a list of tuples or FrameSummary objects for printing. + + Given a list of tuples or FrameSummary objects as returned by + extract_tb() or extract_stack(), return a list of strings ready + for printing. - Given a list of tuples as returned by extract_tb() or - extract_stack(), return a list of strings ready for printing. Each string in the resulting list corresponds to the item with the same index in the argument list. Each string ends in a newline; the strings may contain internal newlines as well, for those items @@ -55,15 +57,17 @@ def format_tb(tb, limit=None): return extract_tb(tb, limit=limit).format() def extract_tb(tb, limit=None): - """Return list of up to limit pre-processed entries from traceback. + """ + Return a StackSummary object representing a list of + pre-processed entries from traceback. This is useful for alternate formatting of stack traces. If 'limit' is omitted or None, all entries are extracted. A - pre-processed stack trace entry is a quadruple (filename, line - number, function name, text) representing the information that is - usually printed for a stack trace. The text is a string with - leading and trailing whitespace stripped; if the source is not - available it is None. + pre-processed stack trace entry is a FrameSummary object + containing attributes filename, lineno, name, and line + representing the information that is usually printed for a stack + trace. The line is a string with leading and trailing + whitespace stripped; if the source is not available it is None. """ return StackSummary.extract(walk_tb(tb), limit=limit) @@ -359,10 +363,9 @@ def extract(klass, frame_gen, *, limit=None, lookup_lines=True, @classmethod def from_list(klass, a_list): - """Create a StackSummary from a simple list of tuples. - - This method supports the older Python API. Each tuple should be a - 4-tuple with (filename, lineno, name, line) elements. + """ + Create a StackSummary object from a supplied list of + FrameSummary objects or old-style list of tuples. """ # While doing a fast-path check for isinstance(a_list, StackSummary) is # appealing, idlelib.run.cleanup_traceback and other similar code may From webhook-mailer at python.org Thu Aug 2 12:51:52 2018 From: webhook-mailer at python.org (Berker Peksag) Date: Thu, 02 Aug 2018 16:51:52 -0000 Subject: [Python-checkins] bpo-27910: Update documentation of traceback module (GH-6116) Message-ID: https://github.com/python/cpython/commit/0f9df886d6d1c6b239a2861a0ad0d56bb59e3922 commit: 0f9df886d6d1c6b239a2861a0ad0d56bb59e3922 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Berker Peksag date: 2018-08-02T19:51:48+03:00 summary: bpo-27910: Update documentation of traceback module (GH-6116) In the documentation for the traceback module, the definitions of functions extract_tb(), format_list() and classmethod StackSummary.from_list() mention the old style 4-tuples that these functions used to return or accept. Since Python 3.5, however, they return or accept a FrameSummary object instead of a 4-tuple, or a StackSummary object instead of a list of 4-tuples. Co-authored-by: torsava Co-Authored-By: Berker Peksag (cherry picked from commit f394ee5eaf6d6d8f45e0478e77d4dbff25c6bea7) files: M Doc/library/traceback.rst M Lib/traceback.py diff --git a/Doc/library/traceback.rst b/Doc/library/traceback.rst index 55d331c996a1..7ac3cacd3d1c 100644 --- a/Doc/library/traceback.rst +++ b/Doc/library/traceback.rst @@ -88,14 +88,16 @@ The module defines the following functions: .. function:: extract_tb(tb, limit=None) - Return a list of "pre-processed" stack trace entries extracted from the - traceback object *tb*. It is useful for alternate formatting of - stack traces. The optional *limit* argument has the same meaning as for - :func:`print_tb`. A "pre-processed" stack trace entry is a 4-tuple - (*filename*, *line number*, *function name*, *text*) representing the - information that is usually printed for a stack trace. The *text* is a - string with leading and trailing whitespace stripped; if the source is - not available it is ``None``. + Return a :class:`StackSummary` object representing a list of "pre-processed" + stack trace entries extracted from the traceback object *tb*. It is useful + for alternate formatting of stack traces. The optional *limit* argument has + the same meaning as for :func:`print_tb`. A "pre-processed" stack trace + entry is a :class:`FrameSummary` object containing attributes + :attr:`~FrameSummary.filename`, :attr:`~FrameSummary.lineno`, + :attr:`~FrameSummary.name`, and :attr:`~FrameSummary.line` representing the + information that is usually printed for a stack trace. The + :attr:`~FrameSummary.line` is a string with leading and trailing + whitespace stripped; if the source is not available it is ``None``. .. function:: extract_stack(f=None, limit=None) @@ -107,12 +109,12 @@ The module defines the following functions: .. function:: format_list(extracted_list) - Given a list of tuples as returned by :func:`extract_tb` or - :func:`extract_stack`, return a list of strings ready for printing. Each - string in the resulting list corresponds to the item with the same index in - the argument list. Each string ends in a newline; the strings may contain - internal newlines as well, for those items whose source text line is not - ``None``. + Given a list of tuples or :class:`FrameSummary` objects as returned by + :func:`extract_tb` or :func:`extract_stack`, return a list of strings ready + for printing. Each string in the resulting list corresponds to the item with + the same index in the argument list. Each string ends in a newline; the + strings may contain internal newlines as well, for those items whose source + text line is not ``None``. .. function:: format_exception_only(etype, value) @@ -293,9 +295,9 @@ capture data for later printing in a lightweight fashion. .. classmethod:: from_list(a_list) - Construct a :class:`StackSummary` object from a supplied old-style list - of tuples. Each tuple should be a 4-tuple with filename, lineno, name, - line as the elements. + Construct a :class:`StackSummary` object from a supplied list of + :class:`FrameSummary` objects or old-style list of tuples. Each tuple + should be a 4-tuple with filename, lineno, name, line as the elements. .. method:: format() diff --git a/Lib/traceback.py b/Lib/traceback.py index ee8c73914032..afab0a4b91f1 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -25,10 +25,12 @@ def print_list(extracted_list, file=None): print(item, file=file, end="") def format_list(extracted_list): - """Format a list of traceback entry tuples for printing. + """Format a list of tuples or FrameSummary objects for printing. + + Given a list of tuples or FrameSummary objects as returned by + extract_tb() or extract_stack(), return a list of strings ready + for printing. - Given a list of tuples as returned by extract_tb() or - extract_stack(), return a list of strings ready for printing. Each string in the resulting list corresponds to the item with the same index in the argument list. Each string ends in a newline; the strings may contain internal newlines as well, for those items @@ -55,15 +57,17 @@ def format_tb(tb, limit=None): return extract_tb(tb, limit=limit).format() def extract_tb(tb, limit=None): - """Return list of up to limit pre-processed entries from traceback. + """ + Return a StackSummary object representing a list of + pre-processed entries from traceback. This is useful for alternate formatting of stack traces. If 'limit' is omitted or None, all entries are extracted. A - pre-processed stack trace entry is a quadruple (filename, line - number, function name, text) representing the information that is - usually printed for a stack trace. The text is a string with - leading and trailing whitespace stripped; if the source is not - available it is None. + pre-processed stack trace entry is a FrameSummary object + containing attributes filename, lineno, name, and line + representing the information that is usually printed for a stack + trace. The line is a string with leading and trailing + whitespace stripped; if the source is not available it is None. """ return StackSummary.extract(walk_tb(tb), limit=limit) @@ -359,10 +363,9 @@ def extract(klass, frame_gen, *, limit=None, lookup_lines=True, @classmethod def from_list(klass, a_list): - """Create a StackSummary from a simple list of tuples. - - This method supports the older Python API. Each tuple should be a - 4-tuple with (filename, lineno, name, line) elements. + """ + Create a StackSummary object from a supplied list of + FrameSummary objects or old-style list of tuples. """ # While doing a fast-path check for isinstance(a_list, StackSummary) is # appealing, idlelib.run.cleanup_traceback and other similar code may From webhook-mailer at python.org Thu Aug 2 13:01:01 2018 From: webhook-mailer at python.org (Berker Peksag) Date: Thu, 02 Aug 2018 17:01:01 -0000 Subject: [Python-checkins] bpo-27910: Update documentation of traceback module (GH-6116) Message-ID: https://github.com/python/cpython/commit/295342adbfd905d5b4a77f960ea39649df7d9997 commit: 295342adbfd905d5b4a77f960ea39649df7d9997 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Berker Peksag date: 2018-08-02T20:00:57+03:00 summary: bpo-27910: Update documentation of traceback module (GH-6116) In the documentation for the traceback module, the definitions of functions extract_tb(), format_list() and classmethod StackSummary.from_list() mention the old style 4-tuples that these functions used to return or accept. Since Python 3.5, however, they return or accept a FrameSummary object instead of a 4-tuple, or a StackSummary object instead of a list of 4-tuples. Co-authored-by: torsava Co-Authored-By: Berker Peksag (cherry picked from commit f394ee5eaf6d6d8f45e0478e77d4dbff25c6bea7) files: M Doc/library/traceback.rst M Lib/traceback.py diff --git a/Doc/library/traceback.rst b/Doc/library/traceback.rst index 55d331c996a1..7ac3cacd3d1c 100644 --- a/Doc/library/traceback.rst +++ b/Doc/library/traceback.rst @@ -88,14 +88,16 @@ The module defines the following functions: .. function:: extract_tb(tb, limit=None) - Return a list of "pre-processed" stack trace entries extracted from the - traceback object *tb*. It is useful for alternate formatting of - stack traces. The optional *limit* argument has the same meaning as for - :func:`print_tb`. A "pre-processed" stack trace entry is a 4-tuple - (*filename*, *line number*, *function name*, *text*) representing the - information that is usually printed for a stack trace. The *text* is a - string with leading and trailing whitespace stripped; if the source is - not available it is ``None``. + Return a :class:`StackSummary` object representing a list of "pre-processed" + stack trace entries extracted from the traceback object *tb*. It is useful + for alternate formatting of stack traces. The optional *limit* argument has + the same meaning as for :func:`print_tb`. A "pre-processed" stack trace + entry is a :class:`FrameSummary` object containing attributes + :attr:`~FrameSummary.filename`, :attr:`~FrameSummary.lineno`, + :attr:`~FrameSummary.name`, and :attr:`~FrameSummary.line` representing the + information that is usually printed for a stack trace. The + :attr:`~FrameSummary.line` is a string with leading and trailing + whitespace stripped; if the source is not available it is ``None``. .. function:: extract_stack(f=None, limit=None) @@ -107,12 +109,12 @@ The module defines the following functions: .. function:: format_list(extracted_list) - Given a list of tuples as returned by :func:`extract_tb` or - :func:`extract_stack`, return a list of strings ready for printing. Each - string in the resulting list corresponds to the item with the same index in - the argument list. Each string ends in a newline; the strings may contain - internal newlines as well, for those items whose source text line is not - ``None``. + Given a list of tuples or :class:`FrameSummary` objects as returned by + :func:`extract_tb` or :func:`extract_stack`, return a list of strings ready + for printing. Each string in the resulting list corresponds to the item with + the same index in the argument list. Each string ends in a newline; the + strings may contain internal newlines as well, for those items whose source + text line is not ``None``. .. function:: format_exception_only(etype, value) @@ -293,9 +295,9 @@ capture data for later printing in a lightweight fashion. .. classmethod:: from_list(a_list) - Construct a :class:`StackSummary` object from a supplied old-style list - of tuples. Each tuple should be a 4-tuple with filename, lineno, name, - line as the elements. + Construct a :class:`StackSummary` object from a supplied list of + :class:`FrameSummary` objects or old-style list of tuples. Each tuple + should be a 4-tuple with filename, lineno, name, line as the elements. .. method:: format() diff --git a/Lib/traceback.py b/Lib/traceback.py index 41fefff9c2dc..d2b102b73c40 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -25,10 +25,12 @@ def print_list(extracted_list, file=None): print(item, file=file, end="") def format_list(extracted_list): - """Format a list of traceback entry tuples for printing. + """Format a list of tuples or FrameSummary objects for printing. + + Given a list of tuples or FrameSummary objects as returned by + extract_tb() or extract_stack(), return a list of strings ready + for printing. - Given a list of tuples as returned by extract_tb() or - extract_stack(), return a list of strings ready for printing. Each string in the resulting list corresponds to the item with the same index in the argument list. Each string ends in a newline; the strings may contain internal newlines as well, for those items @@ -55,15 +57,17 @@ def format_tb(tb, limit=None): return extract_tb(tb, limit=limit).format() def extract_tb(tb, limit=None): - """Return list of up to limit pre-processed entries from traceback. + """ + Return a StackSummary object representing a list of + pre-processed entries from traceback. This is useful for alternate formatting of stack traces. If 'limit' is omitted or None, all entries are extracted. A - pre-processed stack trace entry is a quadruple (filename, line - number, function name, text) representing the information that is - usually printed for a stack trace. The text is a string with - leading and trailing whitespace stripped; if the source is not - available it is None. + pre-processed stack trace entry is a FrameSummary object + containing attributes filename, lineno, name, and line + representing the information that is usually printed for a stack + trace. The line is a string with leading and trailing + whitespace stripped; if the source is not available it is None. """ return StackSummary.extract(walk_tb(tb), limit=limit) @@ -360,10 +364,9 @@ def extract(klass, frame_gen, *, limit=None, lookup_lines=True, @classmethod def from_list(klass, a_list): - """Create a StackSummary from a simple list of tuples. - - This method supports the older Python API. Each tuple should be a - 4-tuple with (filename, lineno, name, line) elements. + """ + Create a StackSummary object from a supplied list of + FrameSummary objects or old-style list of tuples. """ # While doing a fast-path check for isinstance(a_list, StackSummary) is # appealing, idlelib.run.cleanup_traceback and other similar code may From webhook-mailer at python.org Thu Aug 2 13:34:23 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Thu, 02 Aug 2018 17:34:23 -0000 Subject: [Python-checkins] bpo-34170: Cleanup pymain_run_filename() (GH-8631) Message-ID: https://github.com/python/cpython/commit/72ec3193b5118a2ccc8be8bf03d7b74691c6a264 commit: 72ec3193b5118a2ccc8be8bf03d7b74691c6a264 branch: master author: Victor Stinner committer: GitHub date: 2018-08-02T19:34:20+02:00 summary: bpo-34170: Cleanup pymain_run_filename() (GH-8631) * Inline pymain_run_file() and pymain_open_filename() into pymain_run_filename() * Created pymain_run_stdin() which is pymain_run_filename() with filename=NULL * Rename pymain_run_filename() to pymain_run_file() files: M Modules/main.c diff --git a/Modules/main.c b/Modules/main.c index f99ff86b749d..06aa1124c796 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -300,43 +300,6 @@ pymain_run_command(wchar_t *command, PyCompilerFlags *cf) } -static int -pymain_run_file(FILE *fp, const wchar_t *filename, PyCompilerFlags *p_cf) -{ - PyObject *unicode, *bytes = NULL; - const char *filename_str; - int run; - - /* call pending calls like signal handlers (SIGINT) */ - if (Py_MakePendingCalls() == -1) { - PyErr_Print(); - return 1; - } - - if (filename) { - unicode = PyUnicode_FromWideChar(filename, wcslen(filename)); - if (unicode != NULL) { - bytes = PyUnicode_EncodeFSDefault(unicode); - Py_DECREF(unicode); - } - if (bytes != NULL) { - filename_str = PyBytes_AsString(bytes); - } - else { - PyErr_Clear(); - filename_str = ""; - } - } - else { - filename_str = ""; - } - - run = PyRun_AnyFileExFlags(fp, filename_str, filename != NULL, p_cf); - Py_XDECREF(bytes); - return run != 0; -} - - /* Main program */ typedef struct { @@ -1101,17 +1064,43 @@ pymain_import_readline(_PyMain *pymain) } -static FILE* -pymain_open_filename(_PyMain *pymain, _PyCoreConfig *config) +static void +pymain_run_startup(_PyMain *pymain, _PyCoreConfig *config, PyCompilerFlags *cf) { - FILE* fp; + const char *startup = _PyCoreConfig_GetEnv(config, "PYTHONSTARTUP"); + if (startup == NULL) { + return; + } - fp = _Py_wfopen(pymain->filename, L"r"); + FILE *fp = _Py_fopen(startup, "r"); + if (fp == NULL) { + int save_errno = errno; + PySys_WriteStderr("Could not open PYTHONSTARTUP\n"); + errno = save_errno; + + PyErr_SetFromErrnoWithFilename(PyExc_OSError, + startup); + PyErr_Print(); + PyErr_Clear(); + return; + } + + (void) PyRun_SimpleFileExFlags(fp, startup, 0, cf); + PyErr_Clear(); + fclose(fp); +} + + +static void +pymain_run_file(_PyMain *pymain, _PyCoreConfig *config, PyCompilerFlags *cf) +{ + const wchar_t *filename = pymain->filename; + FILE *fp = _Py_wfopen(filename, L"r"); if (fp == NULL) { char *cfilename_buffer; const char *cfilename; int err = errno; - cfilename_buffer = _Py_EncodeLocaleRaw(pymain->filename, NULL); + cfilename_buffer = _Py_EncodeLocaleRaw(filename, NULL); if (cfilename_buffer != NULL) cfilename = cfilename_buffer; else @@ -1120,13 +1109,12 @@ pymain_open_filename(_PyMain *pymain, _PyCoreConfig *config) config->program, cfilename, err, strerror(err)); PyMem_RawFree(cfilename_buffer); pymain->status = 2; - return NULL; + return; } if (pymain->skip_first_line) { int ch; - /* Push back first newline so line numbers - remain the same */ + /* Push back first newline so line numbers remain the same */ while ((ch = getc(fp)) != EOF) { if (ch == '\n') { (void)ungetc(ch, fp); @@ -1136,70 +1124,65 @@ pymain_open_filename(_PyMain *pymain, _PyCoreConfig *config) } struct _Py_stat_struct sb; - if (_Py_fstat_noraise(fileno(fp), &sb) == 0 && - S_ISDIR(sb.st_mode)) { + if (_Py_fstat_noraise(fileno(fp), &sb) == 0 && S_ISDIR(sb.st_mode)) { fprintf(stderr, "%ls: '%ls' is a directory, cannot continue\n", - config->program, pymain->filename); - fclose(fp); + config->program, filename); pymain->status = 1; - return NULL; + goto done; } - return fp; -} - - -static void -pymain_run_startup(_PyMain *pymain, _PyCoreConfig *config, PyCompilerFlags *cf) -{ - const char *startup = _PyCoreConfig_GetEnv(config, "PYTHONSTARTUP"); - if (startup == NULL) { - return; + /* call pending calls like signal handlers (SIGINT) */ + if (Py_MakePendingCalls() == -1) { + PyErr_Print(); + pymain->status = 1; + goto done; } - FILE *fp = _Py_fopen(startup, "r"); - if (fp == NULL) { - int save_errno = errno; - PySys_WriteStderr("Could not open PYTHONSTARTUP\n"); - errno = save_errno; + PyObject *unicode, *bytes = NULL; + const char *filename_str; - PyErr_SetFromErrnoWithFilename(PyExc_OSError, - startup); - PyErr_Print(); + unicode = PyUnicode_FromWideChar(filename, wcslen(filename)); + if (unicode != NULL) { + bytes = PyUnicode_EncodeFSDefault(unicode); + Py_DECREF(unicode); + } + if (bytes != NULL) { + filename_str = PyBytes_AsString(bytes); + } + else { PyErr_Clear(); - return; + filename_str = ""; } - (void) PyRun_SimpleFileExFlags(fp, startup, 0, cf); - PyErr_Clear(); + int run = PyRun_AnyFileExFlags(fp, filename_str, 0, cf); + Py_XDECREF(bytes); + pymain->status = (run != 0); + +done: fclose(fp); } static void -pymain_run_filename(_PyMain *pymain, _PyCoreConfig *config, - PyCompilerFlags *cf) +pymain_run_stdin(_PyMain *pymain, _PyCoreConfig *config, PyCompilerFlags *cf) { - if (pymain->filename == NULL && pymain->stdin_is_interactive) { + if (pymain->stdin_is_interactive) { Py_InspectFlag = 0; /* do exit on SystemExit */ config->inspect = 0; pymain_run_startup(pymain, config, cf); pymain_run_interactive_hook(); } - FILE *fp; - if (pymain->filename != NULL) { - fp = pymain_open_filename(pymain, config); - if (fp == NULL) { - return; - } - } - else { - fp = stdin; + /* call pending calls like signal handlers (SIGINT) */ + if (Py_MakePendingCalls() == -1) { + PyErr_Print(); + pymain->status = 1; + return; } - pymain->status = pymain_run_file(fp, pymain->filename, cf); + int run = PyRun_AnyFileExFlags(stdin, "", 0, cf); + pymain->status = (run != 0); } @@ -1618,8 +1601,11 @@ pymain_run_python(_PyMain *pymain, PyInterpreterState *interp) int sts = pymain_run_module(L"__main__", 0); pymain->status = (sts != 0); } + else if (pymain->filename != NULL) { + pymain_run_file(pymain, config, &cf); + } else { - pymain_run_filename(pymain, config, &cf); + pymain_run_stdin(pymain, config, &cf); } pymain_repl(pymain, config, &cf); From webhook-mailer at python.org Thu Aug 2 20:09:05 2018 From: webhook-mailer at python.org (Pablo Galindo) Date: Fri, 03 Aug 2018 00:09:05 -0000 Subject: [Python-checkins] bpo-30317: Fix multiprocessing test_timeout() (GH-8621) Message-ID: https://github.com/python/cpython/commit/5640d030e100aade54210034828b711c3b506b18 commit: 5640d030e100aade54210034828b711c3b506b18 branch: master author: Victor Stinner committer: Pablo Galindo date: 2018-08-03T01:09:00+01:00 summary: bpo-30317: Fix multiprocessing test_timeout() (GH-8621) Multiprocessing test_timeout() now accepts a delta of 100 ms instead of just 50 ms, since the test failed with 135.8 ms instead of the expected 200 ms. files: M Lib/test/_test_multiprocessing.py diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index f446ef34fe67..749cf8c4fd2c 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -1039,9 +1039,10 @@ def test_timeout(self): start = time.time() self.assertRaises(pyqueue.Empty, q.get, True, 0.200) delta = time.time() - start - # Tolerate a delta of 50 ms because of the bad clock resolution on - # Windows (usually 15.6 ms) - self.assertGreaterEqual(delta, 0.150) + # bpo-30317: Tolerate a delta of 100 ms because of the bad clock + # resolution on Windows (usually 15.6 ms). x86 Windows7 3.x once + # failed because the delta was only 135.8 ms. + self.assertGreaterEqual(delta, 0.100) close_queue(q) def test_queue_feeder_donot_stop_onexc(self): From webhook-mailer at python.org Thu Aug 2 20:27:01 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 03 Aug 2018 00:27:01 -0000 Subject: [Python-checkins] bpo-30317: Fix multiprocessing test_timeout() (GH-8621) Message-ID: https://github.com/python/cpython/commit/50d78677899d315c17dc9a14d2f94a013046c71a commit: 50d78677899d315c17dc9a14d2f94a013046c71a branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-08-02T17:26:57-07:00 summary: bpo-30317: Fix multiprocessing test_timeout() (GH-8621) Multiprocessing test_timeout() now accepts a delta of 100 ms instead of just 50 ms, since the test failed with 135.8 ms instead of the expected 200 ms. (cherry picked from commit 5640d030e100aade54210034828b711c3b506b18) Co-authored-by: Victor Stinner files: M Lib/test/_test_multiprocessing.py diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index a3891f1f04f5..2ece58b305d3 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -1039,9 +1039,10 @@ def test_timeout(self): start = time.time() self.assertRaises(pyqueue.Empty, q.get, True, 0.200) delta = time.time() - start - # Tolerate a delta of 50 ms because of the bad clock resolution on - # Windows (usually 15.6 ms) - self.assertGreaterEqual(delta, 0.150) + # bpo-30317: Tolerate a delta of 100 ms because of the bad clock + # resolution on Windows (usually 15.6 ms). x86 Windows7 3.x once + # failed because the delta was only 135.8 ms. + self.assertGreaterEqual(delta, 0.100) close_queue(q) def test_queue_feeder_donot_stop_onexc(self): From webhook-mailer at python.org Thu Aug 2 22:44:09 2018 From: webhook-mailer at python.org (Mariatta) Date: Fri, 03 Aug 2018 02:44:09 -0000 Subject: [Python-checkins] bpo-34317: Fix a dead url to Windows documentation (GH-8622) Message-ID: https://github.com/python/cpython/commit/46ebe61c7f3511b97268b44d5373a9e9cf0b5cc7 commit: 46ebe61c7f3511b97268b44d5373a9e9cf0b5cc7 branch: master author: HiyashiChuka <41299525+hiyashichuka at users.noreply.github.com> committer: Mariatta date: 2018-08-02T19:44:06-07:00 summary: bpo-34317: Fix a dead url to Windows documentation (GH-8622) files: M Doc/using/windows.rst diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst index eb278a5e5eb4..e93fa0a78d1f 100644 --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -381,7 +381,7 @@ System variables, you need non-restricted access to your machine .. seealso:: - https://support.microsoft.com/en-us/help/100843/environment-variables-in-windows-nt + https://www.microsoft.com/en-us/wdsi/help/folder-variables Environment variables in Windows NT https://technet.microsoft.com/en-us/library/cc754250.aspx From webhook-mailer at python.org Thu Aug 2 22:45:51 2018 From: webhook-mailer at python.org (Mariatta) Date: Fri, 03 Aug 2018 02:45:51 -0000 Subject: [Python-checkins] Update list.remove(x) documentation (GH-8636) Message-ID: https://github.com/python/cpython/commit/bcd1d971b66f529cbd450aca438275b868031605 commit: bcd1d971b66f529cbd450aca438275b868031605 branch: master author: Lysandros Nikolaou committer: Mariatta date: 2018-08-02T19:45:48-07:00 summary: Update list.remove(x) documentation (GH-8636) Rephrase it to "It raises a `ValueError`" files: M Doc/tutorial/datastructures.rst diff --git a/Doc/tutorial/datastructures.rst b/Doc/tutorial/datastructures.rst index 7855ef2d2882..95dc0f98ba51 100644 --- a/Doc/tutorial/datastructures.rst +++ b/Doc/tutorial/datastructures.rst @@ -40,8 +40,8 @@ objects: .. method:: list.remove(x) :noindex: - Remove the first item from the list whose value is equal to *x*. It is an error if - there is no such item. + Remove the first item from the list whose value is equal to *x*. It raises a + ``ValueError`` if there is no such item. .. method:: list.pop([i]) From webhook-mailer at python.org Thu Aug 2 22:50:03 2018 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Fri, 03 Aug 2018 02:50:03 -0000 Subject: [Python-checkins] bpo-34275: Make IDLE calltips always visible on Mac. (GH-8639) Message-ID: https://github.com/python/cpython/commit/9beaef6225cdae972b1cccd5310828cc29bf9d33 commit: 9beaef6225cdae972b1cccd5310828cc29bf9d33 branch: master author: Terry Jan Reedy committer: GitHub date: 2018-08-02T22:50:01-04:00 summary: bpo-34275: Make IDLE calltips always visible on Mac. (GH-8639) Some MacOS-tk combinations need .update_idletasks(). The call is both unneeded and innocuous on Linux and Windows. Patch by Kevin Waltzer. files: A Misc/NEWS.d/next/IDLE/2018-08-02-22-16-42.bpo-34275.Iu0d7t.rst M Lib/idlelib/calltip_w.py diff --git a/Lib/idlelib/calltip_w.py b/Lib/idlelib/calltip_w.py index 34531f431a67..1b1ffc59483d 100644 --- a/Lib/idlelib/calltip_w.py +++ b/Lib/idlelib/calltip_w.py @@ -72,6 +72,7 @@ def showtip(self, text, parenleft, parenright): background="#ffffe0", relief=SOLID, borderwidth=1, font = self.widget['font']) self.label.pack() + tw.update_idletasks() tw.lift() # work around bug in Tk 8.5.18+ (issue #24570) self.checkhideid = self.widget.bind(CHECKHIDE_VIRTUAL_EVENT_NAME, diff --git a/Misc/NEWS.d/next/IDLE/2018-08-02-22-16-42.bpo-34275.Iu0d7t.rst b/Misc/NEWS.d/next/IDLE/2018-08-02-22-16-42.bpo-34275.Iu0d7t.rst new file mode 100644 index 000000000000..d7eba7ad3cf4 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2018-08-02-22-16-42.bpo-34275.Iu0d7t.rst @@ -0,0 +1,2 @@ +Make IDLE calltips always visible on Mac. Some MacOS-tk combinations need +.update_idletasks(). Patch by Kevin Walzer. From webhook-mailer at python.org Thu Aug 2 23:03:23 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 03 Aug 2018 03:03:23 -0000 Subject: [Python-checkins] Update list.remove(x) documentation (GH-8636) Message-ID: https://github.com/python/cpython/commit/66ff9d270c0ff026c13667a4ca7e51ec7494fe62 commit: 66ff9d270c0ff026c13667a4ca7e51ec7494fe62 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-08-02T20:03:19-07:00 summary: Update list.remove(x) documentation (GH-8636) Rephrase it to "It raises a `ValueError`" (cherry picked from commit bcd1d971b66f529cbd450aca438275b868031605) Co-authored-by: Lysandros Nikolaou files: M Doc/tutorial/datastructures.rst diff --git a/Doc/tutorial/datastructures.rst b/Doc/tutorial/datastructures.rst index 7855ef2d2882..95dc0f98ba51 100644 --- a/Doc/tutorial/datastructures.rst +++ b/Doc/tutorial/datastructures.rst @@ -40,8 +40,8 @@ objects: .. method:: list.remove(x) :noindex: - Remove the first item from the list whose value is equal to *x*. It is an error if - there is no such item. + Remove the first item from the list whose value is equal to *x*. It raises a + ``ValueError`` if there is no such item. .. method:: list.pop([i]) From webhook-mailer at python.org Thu Aug 2 23:29:44 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 03 Aug 2018 03:29:44 -0000 Subject: [Python-checkins] bpo-34317: Fix a dead url to Windows documentation (GH-8622) Message-ID: https://github.com/python/cpython/commit/34f59a7a7843d8e2c1922c830a99b5b7a022f4be commit: 34f59a7a7843d8e2c1922c830a99b5b7a022f4be branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-08-02T20:29:39-07:00 summary: bpo-34317: Fix a dead url to Windows documentation (GH-8622) (cherry picked from commit 46ebe61c7f3511b97268b44d5373a9e9cf0b5cc7) Co-authored-by: HiyashiChuka <41299525+hiyashichuka at users.noreply.github.com> files: M Doc/using/windows.rst diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst index eb278a5e5eb4..e93fa0a78d1f 100644 --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -381,7 +381,7 @@ System variables, you need non-restricted access to your machine .. seealso:: - https://support.microsoft.com/en-us/help/100843/environment-variables-in-windows-nt + https://www.microsoft.com/en-us/wdsi/help/folder-variables Environment variables in Windows NT https://technet.microsoft.com/en-us/library/cc754250.aspx From webhook-mailer at python.org Thu Aug 2 23:33:50 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 03 Aug 2018 03:33:50 -0000 Subject: [Python-checkins] bpo-34275: Make IDLE calltips always visible on Mac. (GH-8639) Message-ID: https://github.com/python/cpython/commit/363105e546709339e3419ada9daf5efa0b3b1e18 commit: 363105e546709339e3419ada9daf5efa0b3b1e18 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-08-02T20:33:47-07:00 summary: bpo-34275: Make IDLE calltips always visible on Mac. (GH-8639) Some MacOS-tk combinations need .update_idletasks(). The call is both unneeded and innocuous on Linux and Windows. Patch by Kevin Waltzer. (cherry picked from commit 9beaef6225cdae972b1cccd5310828cc29bf9d33) Co-authored-by: Terry Jan Reedy files: A Misc/NEWS.d/next/IDLE/2018-08-02-22-16-42.bpo-34275.Iu0d7t.rst M Lib/idlelib/calltip_w.py diff --git a/Lib/idlelib/calltip_w.py b/Lib/idlelib/calltip_w.py index 34531f431a67..1b1ffc59483d 100644 --- a/Lib/idlelib/calltip_w.py +++ b/Lib/idlelib/calltip_w.py @@ -72,6 +72,7 @@ def showtip(self, text, parenleft, parenright): background="#ffffe0", relief=SOLID, borderwidth=1, font = self.widget['font']) self.label.pack() + tw.update_idletasks() tw.lift() # work around bug in Tk 8.5.18+ (issue #24570) self.checkhideid = self.widget.bind(CHECKHIDE_VIRTUAL_EVENT_NAME, diff --git a/Misc/NEWS.d/next/IDLE/2018-08-02-22-16-42.bpo-34275.Iu0d7t.rst b/Misc/NEWS.d/next/IDLE/2018-08-02-22-16-42.bpo-34275.Iu0d7t.rst new file mode 100644 index 000000000000..d7eba7ad3cf4 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2018-08-02-22-16-42.bpo-34275.Iu0d7t.rst @@ -0,0 +1,2 @@ +Make IDLE calltips always visible on Mac. Some MacOS-tk combinations need +.update_idletasks(). Patch by Kevin Walzer. From webhook-mailer at python.org Thu Aug 2 23:39:41 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 03 Aug 2018 03:39:41 -0000 Subject: [Python-checkins] bpo-34275: Make IDLE calltips always visible on Mac. (GH-8639) Message-ID: https://github.com/python/cpython/commit/ffd6364745dbeb7269c0e4376b63bf76bc44bcc6 commit: ffd6364745dbeb7269c0e4376b63bf76bc44bcc6 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-08-02T20:39:36-07:00 summary: bpo-34275: Make IDLE calltips always visible on Mac. (GH-8639) Some MacOS-tk combinations need .update_idletasks(). The call is both unneeded and innocuous on Linux and Windows. Patch by Kevin Waltzer. (cherry picked from commit 9beaef6225cdae972b1cccd5310828cc29bf9d33) Co-authored-by: Terry Jan Reedy files: A Misc/NEWS.d/next/IDLE/2018-08-02-22-16-42.bpo-34275.Iu0d7t.rst M Lib/idlelib/calltip_w.py diff --git a/Lib/idlelib/calltip_w.py b/Lib/idlelib/calltip_w.py index 34531f431a67..1b1ffc59483d 100644 --- a/Lib/idlelib/calltip_w.py +++ b/Lib/idlelib/calltip_w.py @@ -72,6 +72,7 @@ def showtip(self, text, parenleft, parenright): background="#ffffe0", relief=SOLID, borderwidth=1, font = self.widget['font']) self.label.pack() + tw.update_idletasks() tw.lift() # work around bug in Tk 8.5.18+ (issue #24570) self.checkhideid = self.widget.bind(CHECKHIDE_VIRTUAL_EVENT_NAME, diff --git a/Misc/NEWS.d/next/IDLE/2018-08-02-22-16-42.bpo-34275.Iu0d7t.rst b/Misc/NEWS.d/next/IDLE/2018-08-02-22-16-42.bpo-34275.Iu0d7t.rst new file mode 100644 index 000000000000..d7eba7ad3cf4 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2018-08-02-22-16-42.bpo-34275.Iu0d7t.rst @@ -0,0 +1,2 @@ +Make IDLE calltips always visible on Mac. Some MacOS-tk combinations need +.update_idletasks(). Patch by Kevin Walzer. From webhook-mailer at python.org Thu Aug 2 23:40:24 2018 From: webhook-mailer at python.org (Terry Jan Reedy) Date: Fri, 03 Aug 2018 03:40:24 -0000 Subject: [Python-checkins] [2.7] bpo-34275: Make IDLE calltips always visible on Mac. (GH-8639) (GH-8644) Message-ID: https://github.com/python/cpython/commit/24a54da9452efb21d30dc56c6b9d0977d08ae452 commit: 24a54da9452efb21d30dc56c6b9d0977d08ae452 branch: 2.7 author: Terry Jan Reedy committer: GitHub date: 2018-08-02T23:40:20-04:00 summary: [2.7] bpo-34275: Make IDLE calltips always visible on Mac. (GH-8639) (GH-8644) Some MacOS-tk combinations need .update_idletasks(). The call is both unneeded and innocuous on Linux and Windows. Patch by Kevin Walzer. (cherry picked from commit 9beaef6225cdae972b1cccd5310828cc29bf9d33) Co-authored-by: Terry Jan Reedy files: A Misc/NEWS.d/next/IDLE/2018-08-02-22-16-42.bpo-34275.Iu0d7t.rst M Lib/idlelib/CallTipWindow.py diff --git a/Lib/idlelib/CallTipWindow.py b/Lib/idlelib/CallTipWindow.py index e63164b5a00a..2a453d06f5b9 100644 --- a/Lib/idlelib/CallTipWindow.py +++ b/Lib/idlelib/CallTipWindow.py @@ -72,6 +72,7 @@ def showtip(self, text, parenleft, parenright): background="#ffffe0", relief=SOLID, borderwidth=1, font = self.widget['font']) self.label.pack() + tw.update_idletasks() tw.lift() # work around bug in Tk 8.5.18+ (issue #24570) self.checkhideid = self.widget.bind(CHECKHIDE_VIRTUAL_EVENT_NAME, diff --git a/Misc/NEWS.d/next/IDLE/2018-08-02-22-16-42.bpo-34275.Iu0d7t.rst b/Misc/NEWS.d/next/IDLE/2018-08-02-22-16-42.bpo-34275.Iu0d7t.rst new file mode 100644 index 000000000000..d7eba7ad3cf4 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2018-08-02-22-16-42.bpo-34275.Iu0d7t.rst @@ -0,0 +1,2 @@ +Make IDLE calltips always visible on Mac. Some MacOS-tk combinations need +.update_idletasks(). Patch by Kevin Walzer. From webhook-mailer at python.org Fri Aug 3 01:12:54 2018 From: webhook-mailer at python.org (Mariatta) Date: Fri, 03 Aug 2018 05:12:54 -0000 Subject: [Python-checkins] Improve the grammar in `range` documentation. (GH-8628) Message-ID: https://github.com/python/cpython/commit/b6efc2cf9701adfb901eec2fe6a418893739877a commit: b6efc2cf9701adfb901eec2fe6a418893739877a branch: master author: Andr?s Delfino committer: Mariatta date: 2018-08-02T22:12:51-07:00 summary: Improve the grammar in `range` documentation. (GH-8628) Remove unnecessary "that" in the sentence. files: M Doc/library/stdtypes.rst diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index e6f7b2c0b7f9..128c1bd24a4f 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -1371,7 +1371,7 @@ objects that compare equal might have different :attr:`~range.start`, .. seealso:: * The `linspace recipe `_ - shows how to implement a lazy version of range that suitable for floating + shows how to implement a lazy version of range suitable for floating point applications. .. index:: From webhook-mailer at python.org Fri Aug 3 02:16:11 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 03 Aug 2018 06:16:11 -0000 Subject: [Python-checkins] Improve the grammar in `range` documentation. (GH-8628) Message-ID: https://github.com/python/cpython/commit/b4d71c91498da4ae3a5c6196b44f28a05076fb80 commit: b4d71c91498da4ae3a5c6196b44f28a05076fb80 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-08-02T23:16:07-07:00 summary: Improve the grammar in `range` documentation. (GH-8628) Remove unnecessary "that" in the sentence. (cherry picked from commit b6efc2cf9701adfb901eec2fe6a418893739877a) Co-authored-by: Andr?s Delfino files: M Doc/library/stdtypes.rst diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 93786696056c..0232b66bd196 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -1371,7 +1371,7 @@ objects that compare equal might have different :attr:`~range.start`, .. seealso:: * The `linspace recipe `_ - shows how to implement a lazy version of range that suitable for floating + shows how to implement a lazy version of range suitable for floating point applications. .. index:: From webhook-mailer at python.org Fri Aug 3 02:16:32 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 03 Aug 2018 06:16:32 -0000 Subject: [Python-checkins] Improve the grammar in `range` documentation. (GH-8628) Message-ID: https://github.com/python/cpython/commit/72449547410bfcef55d0561fcec533ba9840b611 commit: 72449547410bfcef55d0561fcec533ba9840b611 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-08-02T23:16:29-07:00 summary: Improve the grammar in `range` documentation. (GH-8628) Remove unnecessary "that" in the sentence. (cherry picked from commit b6efc2cf9701adfb901eec2fe6a418893739877a) Co-authored-by: Andr?s Delfino files: M Doc/library/stdtypes.rst diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index d740688d88a2..e23324fe697e 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -1371,7 +1371,7 @@ objects that compare equal might have different :attr:`~range.start`, .. seealso:: * The `linspace recipe `_ - shows how to implement a lazy version of range that suitable for floating + shows how to implement a lazy version of range suitable for floating point applications. .. index:: From webhook-mailer at python.org Fri Aug 3 05:10:10 2018 From: webhook-mailer at python.org (INADA Naoki) Date: Fri, 03 Aug 2018 09:10:10 -0000 Subject: [Python-checkins] Fix docstring of Profiler class (GH-8651) Message-ID: https://github.com/python/cpython/commit/2ebd3813af9172fe1f9b2f6004edf6f1e1e5d9f1 commit: 2ebd3813af9172fe1f9b2f6004edf6f1e1e5d9f1 branch: master author: INADA Naoki committer: GitHub date: 2018-08-03T18:09:57+09:00 summary: Fix docstring of Profiler class (GH-8651) files: M Lib/cProfile.py M Modules/_lsprof.c diff --git a/Lib/cProfile.py b/Lib/cProfile.py index c8045043cbcf..3c9be3cbd224 100755 --- a/Lib/cProfile.py +++ b/Lib/cProfile.py @@ -25,11 +25,11 @@ def runctx(statement, globals, locals, filename=None, sort=-1): # ____________________________________________________________ class Profile(_lsprof.Profiler): - """Profile(custom_timer=None, time_unit=None, subcalls=True, builtins=True) + """Profile(timer=None, timeunit=None, subcalls=True, builtins=True) Builds a profiler object using the specified timer function. The default timer is a fast built-in one based on real time. - For custom timer functions returning integers, time_unit can + For custom timer functions returning integers, timeunit can be a float specifying a scale (i.e. how long each integer unit is, in seconds). """ diff --git a/Modules/_lsprof.c b/Modules/_lsprof.c index 602747098bb6..233f62fe2f14 100644 --- a/Modules/_lsprof.c +++ b/Modules/_lsprof.c @@ -788,11 +788,11 @@ static PyMethodDef profiler_methods[] = { }; PyDoc_STRVAR(profiler_doc, "\ -Profiler(custom_timer=None, time_unit=None, subcalls=True, builtins=True)\n\ +Profiler(timer=None, timeunit=None, subcalls=True, builtins=True)\n\ \n\ Builds a profiler object using the specified timer function.\n\ The default timer is a fast built-in one based on real time.\n\ - For custom timer functions returning integers, time_unit can\n\ + For custom timer functions returning integers, timeunit can\n\ be a float specifying a scale (i.e. how long each integer unit\n\ is, in seconds).\n\ "); From solipsis at pitrou.net Fri Aug 3 05:13:01 2018 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Fri, 03 Aug 2018 09:13:01 +0000 Subject: [Python-checkins] Daily reference leaks (4243df51fe43): sum=4 Message-ID: <20180803091301.1.5F29F499377B849E@psf.io> results for 4243df51fe43 on branch "default" -------------------------------------------- test_collections leaked [7, -7, 1] memory blocks, sum=1 test_functools leaked [0, 3, 1] memory blocks, sum=4 test_multiprocessing_forkserver leaked [-2, 2, -1] memory blocks, sum=-1 test_multiprocessing_spawn leaked [-2, 0, 2] memory blocks, sum=0 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/psf-users/antoine/refleaks/reflog9yVpMw', '--timeout', '7200'] From webhook-mailer at python.org Fri Aug 3 05:30:53 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 03 Aug 2018 09:30:53 -0000 Subject: [Python-checkins] Fix docstring of Profiler class (GH-8651) Message-ID: https://github.com/python/cpython/commit/28dbfb2fd343f404d0e45cc185f01181e08c7ac5 commit: 28dbfb2fd343f404d0e45cc185f01181e08c7ac5 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-08-03T02:30:49-07:00 summary: Fix docstring of Profiler class (GH-8651) (cherry picked from commit 2ebd3813af9172fe1f9b2f6004edf6f1e1e5d9f1) Co-authored-by: INADA Naoki files: M Lib/cProfile.py M Modules/_lsprof.c diff --git a/Lib/cProfile.py b/Lib/cProfile.py index f166a1c43752..c044be8bf76e 100755 --- a/Lib/cProfile.py +++ b/Lib/cProfile.py @@ -25,11 +25,11 @@ def runctx(statement, globals, locals, filename=None, sort=-1): # ____________________________________________________________ class Profile(_lsprof.Profiler): - """Profile(custom_timer=None, time_unit=None, subcalls=True, builtins=True) + """Profile(timer=None, timeunit=None, subcalls=True, builtins=True) Builds a profiler object using the specified timer function. The default timer is a fast built-in one based on real time. - For custom timer functions returning integers, time_unit can + For custom timer functions returning integers, timeunit can be a float specifying a scale (i.e. how long each integer unit is, in seconds). """ diff --git a/Modules/_lsprof.c b/Modules/_lsprof.c index 602747098bb6..233f62fe2f14 100644 --- a/Modules/_lsprof.c +++ b/Modules/_lsprof.c @@ -788,11 +788,11 @@ static PyMethodDef profiler_methods[] = { }; PyDoc_STRVAR(profiler_doc, "\ -Profiler(custom_timer=None, time_unit=None, subcalls=True, builtins=True)\n\ +Profiler(timer=None, timeunit=None, subcalls=True, builtins=True)\n\ \n\ Builds a profiler object using the specified timer function.\n\ The default timer is a fast built-in one based on real time.\n\ - For custom timer functions returning integers, time_unit can\n\ + For custom timer functions returning integers, timeunit can\n\ be a float specifying a scale (i.e. how long each integer unit\n\ is, in seconds).\n\ "); From webhook-mailer at python.org Fri Aug 3 05:43:22 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 03 Aug 2018 09:43:22 -0000 Subject: [Python-checkins] Fix docstring of Profiler class (GH-8651) Message-ID: https://github.com/python/cpython/commit/396afbf7711aff2c3d5a34da1724980c5db82724 commit: 396afbf7711aff2c3d5a34da1724980c5db82724 branch: 2.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-08-03T02:43:18-07:00 summary: Fix docstring of Profiler class (GH-8651) (cherry picked from commit 2ebd3813af9172fe1f9b2f6004edf6f1e1e5d9f1) Co-authored-by: INADA Naoki files: M Lib/cProfile.py M Modules/_lsprof.c diff --git a/Lib/cProfile.py b/Lib/cProfile.py index b2efd047d397..d3770946db11 100755 --- a/Lib/cProfile.py +++ b/Lib/cProfile.py @@ -64,11 +64,11 @@ def help(): # ____________________________________________________________ class Profile(_lsprof.Profiler): - """Profile(custom_timer=None, time_unit=None, subcalls=True, builtins=True) + """Profile(timer=None, timeunit=None, subcalls=True, builtins=True) Builds a profiler object using the specified timer function. The default timer is a fast built-in one based on real time. - For custom timer functions returning integers, time_unit can + For custom timer functions returning integers, timeunit can be a float specifying a scale (i.e. how long each integer unit is, in seconds). """ diff --git a/Modules/_lsprof.c b/Modules/_lsprof.c index 6090c7dd2365..945354fcfab4 100644 --- a/Modules/_lsprof.c +++ b/Modules/_lsprof.c @@ -807,11 +807,11 @@ static PyMethodDef profiler_methods[] = { }; PyDoc_STRVAR(profiler_doc, "\ -Profiler(custom_timer=None, time_unit=None, subcalls=True, builtins=True)\n\ +Profiler(timer=None, timeunit=None, subcalls=True, builtins=True)\n\ \n\ Builds a profiler object using the specified timer function.\n\ The default timer is a fast built-in one based on real time.\n\ - For custom timer functions returning integers, time_unit can\n\ + For custom timer functions returning integers, timeunit can\n\ be a float specifying a scale (i.e. how long each integer unit\n\ is, in seconds).\n\ "); From webhook-mailer at python.org Fri Aug 3 06:18:50 2018 From: webhook-mailer at python.org (Miss Islington (bot)) Date: Fri, 03 Aug 2018 10:18:50 -0000 Subject: [Python-checkins] Fix docstring of Profiler class (GH-8651) Message-ID: https://github.com/python/cpython/commit/aae872ece18dafb55250e4b622efd1c74484d747 commit: aae872ece18dafb55250e4b622efd1c74484d747 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: GitHub date: 2018-08-03T03:18:45-07:00 summary: Fix docstring of Profiler class (GH-8651) (cherry picked from commit 2ebd3813af9172fe1f9b2f6004edf6f1e1e5d9f1) Co-authored-by: INADA Naoki files: M Lib/cProfile.py M Modules/_lsprof.c diff --git a/Lib/cProfile.py b/Lib/cProfile.py index 1184385ae1f1..6ae8512ddd2e 100755 --- a/Lib/cProfile.py +++ b/Lib/cProfile.py @@ -25,11 +25,11 @@ def runctx(statement, globals, locals, filename=None, sort=-1): # ____________________________________________________________ class Profile(_lsprof.Profiler): - """Profile(custom_timer=None, time_unit=None, subcalls=True, builtins=True) + """Profile(timer=None, timeunit=None, subcalls=True, builtins=True) Builds a profiler object using the specified timer function. The default timer is a fast built-in one based on real time. - For custom timer functions returning integers, time_unit can + For custom timer functions returning integers, timeunit can be a float specifying a scale (i.e. how long each integer unit is, in seconds). """ diff --git a/Modules/_lsprof.c b/Modules/_lsprof.c index 0e7623ddbc96..5f2eeea6dfcc 100644 --- a/Modules/_lsprof.c +++ b/Modules/_lsprof.c @@ -791,11 +791,11 @@ static PyMethodDef profiler_methods[] = { }; PyDoc_STRVAR(profiler_doc, "\ -Profiler(custom_timer=None, time_unit=None, subcalls=True, builtins=True)\n\ +Profiler(timer=None, timeunit=None, subcalls=True, builtins=True)\n\ \n\ Builds a profiler object using the specified timer function.\n\ The default timer is a fast built-in one based on real time.\n\ - For custom timer functions returning integers, time_unit can\n\ + For custom timer functions returning integers, timeunit can\n\ be a float specifying a scale (i.e. how long each integer unit\n\ is, in seconds).\n\ "); From webhook-mailer at python.org Fri Aug 3 09:33:57 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Fri, 03 Aug 2018 13:33:57 -0000 Subject: [Python-checkins] bpo-34301: Add _PyInterpreterState_Get() helper function (GH-8592) Message-ID: https://github.com/python/cpython/commit/caba55b3b735405b280273f7d99866a046c18281 commit: caba55b3b735405b280273f7d99866a046c18281 branch: master author: Victor Stinner committer: GitHub date: 2018-08-03T15:33:52+02:00 summary: bpo-34301: Add _PyInterpreterState_Get() helper function (GH-8592) sys_setcheckinterval() now uses a local variable to parse arguments, before writing into interp->check_interval. files: M Include/pystate.h M Modules/_threadmodule.c M Modules/_xxsubinterpretersmodule.c M Modules/posixmodule.c M Modules/zipimport.c M Objects/codeobject.c M Objects/listobject.c M Objects/moduleobject.c M Objects/object.c M Objects/tupleobject.c M Objects/unicodeobject.c M Programs/_testembed.c M Python/_warnings.c M Python/ceval.c M Python/codecs.c M Python/dynload_shlib.c M Python/import.c M Python/pylifecycle.c M Python/pystate.c M Python/pythonrun.c M Python/sysmodule.c M Python/thread.c diff --git a/Include/pystate.h b/Include/pystate.h index bbeea9d51b82..e410665e35d4 100644 --- a/Include/pystate.h +++ b/Include/pystate.h @@ -245,6 +245,13 @@ typedef struct _ts { PyAPI_FUNC(PyInterpreterState *) PyInterpreterState_New(void); PyAPI_FUNC(void) PyInterpreterState_Clear(PyInterpreterState *); PyAPI_FUNC(void) PyInterpreterState_Delete(PyInterpreterState *); +#if !defined(Py_LIMITED_API) +PyAPI_FUNC(PyInterpreterState *) _PyInterpreterState_Get(void); +#endif +#ifdef Py_BUILD_CORE + /* Macro which should only be used for performance critical code */ +# define _PyInterpreterState_GET_UNSAFE() (PyThreadState_GET()->interp) +#endif #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03070000 /* New in 3.7 */ PyAPI_FUNC(int64_t) PyInterpreterState_GetID(PyInterpreterState *); diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index 69e27be9cc81..f6b39defbc80 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -1049,7 +1049,7 @@ thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs) boot = PyMem_NEW(struct bootstate, 1); if (boot == NULL) return PyErr_NoMemory(); - boot->interp = PyThreadState_GET()->interp; + boot->interp = _PyInterpreterState_Get(); boot->func = func; boot->args = args; boot->keyw = keyw; @@ -1154,8 +1154,8 @@ A thread's identity may be reused for another thread after it exits."); static PyObject * thread__count(PyObject *self, PyObject *Py_UNUSED(ignored)) { - PyThreadState *tstate = PyThreadState_Get(); - return PyLong_FromLong(tstate->interp->num_threads); + PyInterpreterState *interp = _PyInterpreterState_Get(); + return PyLong_FromLong(interp->num_threads); } PyDoc_STRVAR(_count_doc, @@ -1348,7 +1348,7 @@ PyInit__thread(void) PyObject *m, *d, *v; double time_max; double timeout_max; - PyThreadState *tstate = PyThreadState_Get(); + PyInterpreterState *interp = _PyInterpreterState_Get(); /* Initialize types: */ if (PyType_Ready(&localdummytype) < 0) @@ -1395,7 +1395,7 @@ PyInit__thread(void) if (PyModule_AddObject(m, "_local", (PyObject *)&localtype) < 0) return NULL; - tstate->interp->num_threads = 0; + interp->num_threads = 0; str_dict = PyUnicode_InternFromString("__dict__"); if (str_dict == NULL) diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 8567e4fd970c..6162c53b9e6f 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -26,10 +26,9 @@ _copy_raw_string(PyObject *strobj) static PyInterpreterState * _get_current(void) { - PyThreadState *tstate = PyThreadState_Get(); - // PyThreadState_Get() aborts if lookup fails, so we don't need + // _PyInterpreterState_Get() aborts if lookup fails, so don't need // to check the result for NULL. - return tstate->interp; + return _PyInterpreterState_Get(); } static int64_t @@ -1941,7 +1940,7 @@ _run_script_in_interpreter(PyInterpreterState *interp, const char *codestr, // Switch to interpreter. PyThreadState *save_tstate = NULL; - if (interp != PyThreadState_Get()->interp) { + if (interp != _PyInterpreterState_Get()) { // XXX Using the "head" thread isn't strictly correct. PyThreadState *tstate = PyInterpreterState_ThreadHead(interp); // XXX Possible GILState issues? diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 6104c0f74dee..3e03039f15b1 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -437,7 +437,7 @@ run_at_forkers(PyObject *lst, int reverse) void PyOS_BeforeFork(void) { - run_at_forkers(PyThreadState_Get()->interp->before_forkers, 1); + run_at_forkers(_PyInterpreterState_Get()->before_forkers, 1); _PyImport_AcquireLock(); } @@ -448,7 +448,7 @@ PyOS_AfterFork_Parent(void) if (_PyImport_ReleaseLock() <= 0) Py_FatalError("failed releasing import lock after fork"); - run_at_forkers(PyThreadState_Get()->interp->after_forkers_parent, 0); + run_at_forkers(_PyInterpreterState_Get()->after_forkers_parent, 0); } void @@ -459,7 +459,7 @@ PyOS_AfterFork_Child(void) _PyImport_ReInitLock(); _PySignal_AfterFork(); - run_at_forkers(PyThreadState_Get()->interp->after_forkers_child, 0); + run_at_forkers(_PyInterpreterState_Get()->after_forkers_child, 0); } static int @@ -5655,7 +5655,7 @@ os_register_at_fork_impl(PyObject *module, PyObject *before, check_null_or_callable(after_in_parent, "after_in_parent")) { return NULL; } - interp = PyThreadState_Get()->interp; + interp = _PyInterpreterState_Get(); if (register_at_forker(&interp->before_forkers, before)) { return NULL; diff --git a/Modules/zipimport.c b/Modules/zipimport.c index 57e7a13137f4..4df4ba9f8b82 100644 --- a/Modules/zipimport.c +++ b/Modules/zipimport.c @@ -1074,7 +1074,7 @@ read_directory(PyObject *archive) if (flags & 0x0800) { charset = "utf-8"; } - else if (!PyThreadState_GET()->interp->codecs_initialized) { + else if (!_PyInterpreterState_Get()->codecs_initialized) { /* During bootstrap, we may need to load the encodings package from a ZIP file. But the cp437 encoding is implemented in Python in the encodings package. @@ -1351,7 +1351,7 @@ unmarshal_code(PyObject *pathname, PyObject *data, time_t mtime) uint32_t flags = get_uint32(buf + 4); if (flags != 0) { - _PyCoreConfig *config = &PyThreadState_GET()->interp->core_config; + _PyCoreConfig *config = &_PyInterpreterState_Get()->core_config; // Hash-based pyc. We currently refuse to handle checked hash-based // pycs. We could validate hash-based pycs against the source, but it // seems likely that most people putting hash-based pycs in a zipfile diff --git a/Objects/codeobject.c b/Objects/codeobject.c index b07667c28df1..cedf11ee8af8 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -3,6 +3,7 @@ #include "Python.h" #include "code.h" #include "structmember.h" +#include "internal/pystate.h" /* Holder for co_extra information */ typedef struct { @@ -428,7 +429,7 @@ static void code_dealloc(PyCodeObject *co) { if (co->co_extra != NULL) { - PyInterpreterState *interp = PyThreadState_Get()->interp; + PyInterpreterState *interp = _PyInterpreterState_GET_UNSAFE(); _PyCodeObjectExtra *co_extra = co->co_extra; for (Py_ssize_t i = 0; i < co_extra->ce_size; i++) { @@ -871,7 +872,7 @@ _PyCode_GetExtra(PyObject *code, Py_ssize_t index, void **extra) int _PyCode_SetExtra(PyObject *code, Py_ssize_t index, void *extra) { - PyInterpreterState *interp = PyThreadState_Get()->interp; + PyInterpreterState *interp = _PyInterpreterState_GET_UNSAFE(); if (!PyCode_Check(code) || index < 0 || index >= interp->co_extra_user_count) { diff --git a/Objects/listobject.c b/Objects/listobject.c index 4108f502023a..308ae83fa6fa 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -85,7 +85,7 @@ static size_t count_reuse = 0; static void show_alloc(void) { - PyInterpreterState *interp = PyThreadState_GET()->interp; + PyInterpreterState *interp = _PyInterpreterState_Get(); if (!interp->core_config.show_alloc_count) { return; } diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index 5fad4474be0f..2156ca0765a0 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -173,7 +173,7 @@ _add_methods_to_object(PyObject *module, PyObject *name, PyMethodDef *functions) PyObject * PyModule_Create2(struct PyModuleDef* module, int module_api_version) { - if (!_PyImport_IsInitialized(PyThreadState_GET()->interp)) + if (!_PyImport_IsInitialized(_PyInterpreterState_Get())) Py_FatalError("Python import machinery not initialized"); return _PyModule_CreateInitialized(module, module_api_version); } @@ -693,8 +693,7 @@ module_dealloc(PyModuleObject *m) static PyObject * module_repr(PyModuleObject *m) { - PyThreadState *tstate = PyThreadState_GET(); - PyInterpreterState *interp = tstate->interp; + PyInterpreterState *interp = _PyInterpreterState_Get(); return PyObject_CallMethod(interp->importlib, "_module_repr", "O", m); } diff --git a/Objects/object.c b/Objects/object.c index 2471f6b0588c..6498756c92bd 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -96,7 +96,7 @@ extern Py_ssize_t null_strings, one_strings; void dump_counts(FILE* f) { - PyInterpreterState *interp = PyThreadState_GET()->interp; + PyInterpreterState *interp = _PyInterpreterState_Get(); if (!interp->core_config.show_alloc_count) { return; } diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index e268f75be717..eaf92d57f3f6 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -44,7 +44,7 @@ static Py_ssize_t count_tracked = 0; static void show_track(void) { - PyInterpreterState *interp = PyThreadState_GET()->interp; + PyInterpreterState *interp = _PyInterpreterState_Get(); if (!interp->core_config.show_alloc_count) { return; } diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 2b06f15f6c65..04fd6d03b464 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -3413,7 +3413,7 @@ PyUnicode_EncodeFSDefault(PyObject *unicode) #if defined(__APPLE__) return _PyUnicode_AsUTF8String(unicode, Py_FileSystemDefaultEncodeErrors); #else - PyInterpreterState *interp = PyThreadState_GET()->interp; + PyInterpreterState *interp = _PyInterpreterState_GET_UNSAFE(); /* Bootstrap check: if the filesystem codec is implemented in Python, we cannot use it to encode and decode filenames before it is loaded. Load the Python codec requires to encode at least its own filename. Use the C @@ -3639,7 +3639,7 @@ PyUnicode_DecodeFSDefaultAndSize(const char *s, Py_ssize_t size) #if defined(__APPLE__) return PyUnicode_DecodeUTF8Stateful(s, size, Py_FileSystemDefaultEncodeErrors, NULL); #else - PyInterpreterState *interp = PyThreadState_GET()->interp; + PyInterpreterState *interp = _PyInterpreterState_GET_UNSAFE(); /* Bootstrap check: if the filesystem codec is implemented in Python, we cannot use it to encode and decode filenames before it is loaded. Load the Python codec requires to encode at least its own filename. Use the C diff --git a/Programs/_testembed.c b/Programs/_testembed.c index 3ed42d67f834..027de484545f 100644 --- a/Programs/_testembed.c +++ b/Programs/_testembed.c @@ -306,7 +306,7 @@ dump_config(void) exit(1); \ } - PyInterpreterState *interp = PyThreadState_Get()->interp; + PyInterpreterState *interp = _PyInterpreterState_Get(); _PyCoreConfig *config = &interp->core_config; printf("install_signal_handlers = %i\n", config->install_signal_handlers); diff --git a/Python/_warnings.c b/Python/_warnings.c index d6614b2cf6d4..2229206b25c9 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -78,7 +78,7 @@ get_warnings_attr(_Py_Identifier *attr_id, int try_import) gone, then we can't even use PyImport_GetModule without triggering an interpreter abort. */ - if (!PyThreadState_GET()->interp->modules) { + if (!_PyInterpreterState_GET_UNSAFE()->modules) { return NULL; } warnings_module = PyImport_GetModule(warnings_str); @@ -686,7 +686,7 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno, } if (f == NULL) { - globals = PyThreadState_Get()->interp->sysdict; + globals = _PyInterpreterState_GET_UNSAFE()->sysdict; *filename = PyUnicode_FromString("sys"); *lineno = 1; } diff --git a/Python/ceval.c b/Python/ceval.c index 46da295aac06..f3a74b00a2b6 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -532,8 +532,8 @@ PyEval_EvalFrame(PyFrameObject *f) { PyObject * PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) { - PyThreadState *tstate = PyThreadState_GET(); - return tstate->interp->eval_frame(f, throwflag); + PyInterpreterState *interp = _PyInterpreterState_GET_UNSAFE(); + return interp->eval_frame(f, throwflag); } PyObject* _Py_HOT_FUNCTION @@ -4435,7 +4435,7 @@ PyEval_GetBuiltins(void) { PyFrameObject *current_frame = PyEval_GetFrame(); if (current_frame == NULL) - return PyThreadState_GET()->interp->builtins; + return _PyInterpreterState_GET_UNSAFE()->builtins; else return current_frame->f_builtins; } @@ -4769,7 +4769,7 @@ import_name(PyFrameObject *f, PyObject *name, PyObject *fromlist, PyObject *leve } /* Fast path for not overloaded __import__. */ - if (import_func == PyThreadState_GET()->interp->import_func) { + if (import_func == _PyInterpreterState_GET_UNSAFE()->import_func) { int ilevel = _PyLong_AsInt(level); if (ilevel == -1 && PyErr_Occurred()) { return NULL; @@ -5136,7 +5136,7 @@ _Py_GetDXProfile(PyObject *self, PyObject *args) Py_ssize_t _PyEval_RequestCodeExtraIndex(freefunc free) { - PyInterpreterState *interp = PyThreadState_Get()->interp; + PyInterpreterState *interp = _PyInterpreterState_GET_UNSAFE(); Py_ssize_t new_index; if (interp->co_extra_user_count == MAX_CO_EXTRA_USERS - 1) { diff --git a/Python/codecs.c b/Python/codecs.c index eb3cd35fb8e2..4062429fe354 100644 --- a/Python/codecs.c +++ b/Python/codecs.c @@ -32,7 +32,7 @@ static int _PyCodecRegistry_Init(void); /* Forward */ int PyCodec_Register(PyObject *search_function) { - PyInterpreterState *interp = PyThreadState_GET()->interp; + PyInterpreterState *interp = _PyInterpreterState_Get(); if (interp->codec_search_path == NULL && _PyCodecRegistry_Init()) goto onError; if (search_function == NULL) { @@ -99,7 +99,6 @@ PyObject *normalizestring(const char *string) PyObject *_PyCodec_Lookup(const char *encoding) { - PyInterpreterState *interp; PyObject *result, *args = NULL, *v; Py_ssize_t i, len; @@ -108,7 +107,7 @@ PyObject *_PyCodec_Lookup(const char *encoding) goto onError; } - interp = PyThreadState_GET()->interp; + PyInterpreterState *interp = _PyInterpreterState_GET_UNSAFE(); if (interp->codec_search_path == NULL && _PyCodecRegistry_Init()) goto onError; @@ -187,11 +186,10 @@ PyObject *_PyCodec_Lookup(const char *encoding) int _PyCodec_Forget(const char *encoding) { - PyInterpreterState *interp; PyObject *v; int result; - interp = PyThreadState_GET()->interp; + PyInterpreterState *interp = _PyInterpreterState_Get(); if (interp->codec_search_path == NULL) { return -1; } @@ -624,7 +622,7 @@ PyObject *_PyCodec_DecodeText(PyObject *object, Return 0 on success, -1 on error */ int PyCodec_RegisterError(const char *name, PyObject *error) { - PyInterpreterState *interp = PyThreadState_GET()->interp; + PyInterpreterState *interp = _PyInterpreterState_Get(); if (interp->codec_search_path == NULL && _PyCodecRegistry_Init()) return -1; if (!PyCallable_Check(error)) { @@ -642,7 +640,7 @@ PyObject *PyCodec_LookupError(const char *name) { PyObject *handler = NULL; - PyInterpreterState *interp = PyThreadState_GET()->interp; + PyInterpreterState *interp = _PyInterpreterState_GET_UNSAFE(); if (interp->codec_search_path == NULL && _PyCodecRegistry_Init()) return NULL; @@ -1494,7 +1492,7 @@ static int _PyCodecRegistry_Init(void) } }; - PyInterpreterState *interp = PyThreadState_GET()->interp; + PyInterpreterState *interp = _PyInterpreterState_Get(); PyObject *mod; unsigned i; diff --git a/Python/dynload_shlib.c b/Python/dynload_shlib.c index f27119319088..73ae26d13146 100644 --- a/Python/dynload_shlib.c +++ b/Python/dynload_shlib.c @@ -91,7 +91,7 @@ _PyImport_FindSharedFuncptr(const char *prefix, } } - dlopenflags = PyThreadState_GET()->interp->dlopenflags; + dlopenflags = _PyInterpreterState_Get()->dlopenflags; handle = dlopen(pathname, dlopenflags); diff --git a/Python/import.c b/Python/import.c index 71d5ea192dbf..319b661f1f9b 100644 --- a/Python/import.c +++ b/Python/import.c @@ -304,7 +304,7 @@ _PyImport_Fini2(void) PyObject * PyImport_GetModuleDict(void) { - PyInterpreterState *interp = PyThreadState_GET()->interp; + PyInterpreterState *interp = _PyInterpreterState_GET_UNSAFE(); if (interp->modules == NULL) { Py_FatalError("PyImport_GetModuleDict: no module dictionary!"); } @@ -397,7 +397,7 @@ PyImport_Cleanup(void) { Py_ssize_t pos; PyObject *key, *value, *dict; - PyInterpreterState *interp = PyThreadState_GET()->interp; + PyInterpreterState *interp = _PyInterpreterState_Get(); PyObject *modules = PyImport_GetModuleDict(); PyObject *weaklist = NULL; const char * const *p; @@ -592,7 +592,7 @@ long PyImport_GetMagicNumber(void) { long res; - PyInterpreterState *interp = PyThreadState_Get()->interp; + PyInterpreterState *interp = _PyInterpreterState_Get(); PyObject *external, *pyc_magic; external = PyObject_GetAttrString(interp->importlib, "_bootstrap_external"); @@ -892,7 +892,7 @@ PyImport_ExecCodeModuleWithPathnames(const char *name, PyObject *co, goto error; } else if (cpathobj != NULL) { - PyInterpreterState *interp = PyThreadState_GET()->interp; + PyInterpreterState *interp = _PyInterpreterState_GET_UNSAFE(); _Py_IDENTIFIER(_get_sourcefile); if (interp == NULL) { @@ -972,7 +972,7 @@ PyImport_ExecCodeModuleObject(PyObject *name, PyObject *co, PyObject *pathname, PyObject *cpathname) { PyObject *d, *external, *res; - PyInterpreterState *interp = PyThreadState_GET()->interp; + PyInterpreterState *interp = _PyInterpreterState_GET_UNSAFE(); _Py_IDENTIFIER(_fix_up_module); d = module_dict_for_exec(name); @@ -1619,7 +1619,7 @@ import_find_and_load(PyObject *abs_name) { _Py_IDENTIFIER(_find_and_load); PyObject *mod = NULL; - PyInterpreterState *interp = PyThreadState_GET()->interp; + PyInterpreterState *interp = _PyInterpreterState_GET_UNSAFE(); int import_time = interp->core_config.import_time; static int import_level; static _PyTime_t accumulated; @@ -1680,7 +1680,7 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals, PyObject *final_mod = NULL; PyObject *mod = NULL; PyObject *package = NULL; - PyInterpreterState *interp = PyThreadState_GET()->interp; + PyInterpreterState *interp = _PyInterpreterState_GET_UNSAFE(); int has_from; if (name == NULL) { @@ -2289,7 +2289,7 @@ PyInit__imp(void) d = PyModule_GetDict(m); if (d == NULL) goto failure; - _PyCoreConfig *config = &PyThreadState_GET()->interp->core_config; + _PyCoreConfig *config = &_PyInterpreterState_Get()->core_config; PyObject *pyc_mode = PyUnicode_FromString(config->_check_hash_pycs_mode); if (pyc_mode == NULL) { goto failure; diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 95fb8616b52d..c2ee4ffc4681 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -2123,11 +2123,7 @@ _Py_FatalInitError(_PyInitError err) /* For the atexit module. */ void _Py_PyAtExit(void (*func)(PyObject *), PyObject *module) { - PyThreadState *ts; - PyInterpreterState *is; - - ts = PyThreadState_GET(); - is = ts->interp; + PyInterpreterState *is = _PyInterpreterState_Get(); /* Guard against API misuse (see bpo-17852) */ assert(is->pyexitfunc == NULL || is->pyexitfunc == func); diff --git a/Python/pystate.c b/Python/pystate.c index e8d390dfcfb4..7a4cd48077f3 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -264,6 +264,21 @@ PyInterpreterState_Delete(PyInterpreterState *interp) } +PyInterpreterState * +_PyInterpreterState_Get(void) +{ + PyThreadState *tstate = GET_TSTATE(); + if (tstate == NULL) { + Py_FatalError("_PyInterpreterState_Get(): no current thread state"); + } + PyInterpreterState *interp = tstate->interp; + if (interp == NULL) { + Py_FatalError("_PyInterpreterState_Get(): no current interpreter"); + } + return interp; +} + + int64_t PyInterpreterState_GetID(PyInterpreterState *interp) { @@ -1184,10 +1199,9 @@ _check_xidata(_PyCrossInterpreterData *data) int _PyObject_GetCrossInterpreterData(PyObject *obj, _PyCrossInterpreterData *data) { - PyThreadState *tstate = PyThreadState_Get(); - // PyThreadState_Get() aborts if lookup fails, so we don't need + // _PyInterpreterState_Get() aborts if lookup fails, so we don't need // to check the result for NULL. - PyInterpreterState *interp = tstate->interp; + PyInterpreterState *interp = _PyInterpreterState_Get(); // Reset data before re-populating. *data = (_PyCrossInterpreterData){0}; @@ -1235,7 +1249,7 @@ _call_in_interpreter(PyInterpreterState *interp, * naive approach. */ PyThreadState *save_tstate = NULL; - if (interp != PyThreadState_Get()->interp) { + if (interp != _PyInterpreterState_Get()) { // XXX Using the "head" thread isn't strictly correct. PyThreadState *tstate = PyInterpreterState_ThreadHead(interp); // XXX Possible GILState issues? diff --git a/Python/pythonrun.c b/Python/pythonrun.c index f6f06c7640a4..273eae25e2e1 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -92,7 +92,7 @@ PyRun_InteractiveLoopFlags(FILE *fp, const char *filename_str, PyCompilerFlags * PyCompilerFlags local_flags; int nomem_count = 0; #ifdef Py_REF_DEBUG - int show_ref_count = PyThreadState_GET()->interp->core_config.show_ref_count; + int show_ref_count = _PyInterpreterState_Get()->core_config.show_ref_count; #endif filename = PyUnicode_DecodeFSDefault(filename_str); @@ -336,17 +336,13 @@ maybe_pyc_file(FILE *fp, const char* filename, const char* ext, int closeit) static int set_main_loader(PyObject *d, const char *filename, const char *loader_name) { - PyInterpreterState *interp; - PyThreadState *tstate; PyObject *filename_obj, *bootstrap, *loader_type = NULL, *loader; int result = 0; filename_obj = PyUnicode_DecodeFSDefault(filename); if (filename_obj == NULL) return -1; - /* Get current thread state and interpreter pointer */ - tstate = PyThreadState_GET(); - interp = tstate->interp; + PyInterpreterState *interp = _PyInterpreterState_Get(); bootstrap = PyObject_GetAttrString(interp->importlib, "_bootstrap_external"); if (bootstrap != NULL) { diff --git a/Python/sysmodule.c b/Python/sysmodule.c index b6c816e9f628..177b8307626d 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -56,51 +56,55 @@ _Py_IDENTIFIER(write); PyObject * _PySys_GetObjectId(_Py_Identifier *key) { - PyThreadState *tstate = PyThreadState_GET(); - PyObject *sd = tstate->interp->sysdict; - if (sd == NULL) + PyObject *sd = _PyInterpreterState_GET_UNSAFE()->sysdict; + if (sd == NULL) { return NULL; + } return _PyDict_GetItemId(sd, key); } PyObject * PySys_GetObject(const char *name) { - PyThreadState *tstate = PyThreadState_GET(); - PyObject *sd = tstate->interp->sysdict; - if (sd == NULL) + PyObject *sd = _PyInterpreterState_GET_UNSAFE()->sysdict; + if (sd == NULL) { return NULL; + } return PyDict_GetItemString(sd, name); } int _PySys_SetObjectId(_Py_Identifier *key, PyObject *v) { - PyThreadState *tstate = PyThreadState_GET(); - PyObject *sd = tstate->interp->sysdict; + PyObject *sd = _PyInterpreterState_GET_UNSAFE()->sysdict; if (v == NULL) { - if (_PyDict_GetItemId(sd, key) == NULL) + if (_PyDict_GetItemId(sd, key) == NULL) { return 0; - else + } + else { return _PyDict_DelItemId(sd, key); + } } - else + else { return _PyDict_SetItemId(sd, key, v); + } } int PySys_SetObject(const char *name, PyObject *v) { - PyThreadState *tstate = PyThreadState_GET(); - PyObject *sd = tstate->interp->sysdict; + PyObject *sd = _PyInterpreterState_GET_UNSAFE()->sysdict; if (v == NULL) { - if (PyDict_GetItemString(sd, name) == NULL) + if (PyDict_GetItemString(sd, name) == NULL) { return 0; - else + } + else { return PyDict_DelItemString(sd, name); + } } - else + else { return PyDict_SetItemString(sd, name, v); + } } static PyObject * @@ -626,9 +630,13 @@ sys_setcheckinterval(PyObject *self, PyObject *args) "are deprecated. Use sys.setswitchinterval() " "instead.", 1) < 0) return NULL; - PyInterpreterState *interp = PyThreadState_GET()->interp; - if (!PyArg_ParseTuple(args, "i:setcheckinterval", &interp->check_interval)) + + int check_interval; + if (!PyArg_ParseTuple(args, "i:setcheckinterval", &check_interval)) return NULL; + + PyInterpreterState *interp = _PyInterpreterState_Get(); + interp->check_interval = check_interval; Py_RETURN_NONE; } @@ -647,7 +655,7 @@ sys_getcheckinterval(PyObject *self, PyObject *args) "are deprecated. Use sys.getswitchinterval() " "instead.", 1) < 0) return NULL; - PyInterpreterState *interp = PyThreadState_GET()->interp; + PyInterpreterState *interp = _PyInterpreterState_Get(); return PyLong_FromLong(interp->check_interval); } @@ -1154,12 +1162,10 @@ static PyObject * sys_setdlopenflags(PyObject *self, PyObject *args) { int new_val; - PyThreadState *tstate = PyThreadState_GET(); if (!PyArg_ParseTuple(args, "i:setdlopenflags", &new_val)) return NULL; - if (!tstate) - return NULL; - tstate->interp->dlopenflags = new_val; + PyInterpreterState *interp = _PyInterpreterState_Get(); + interp->dlopenflags = new_val; Py_RETURN_NONE; } @@ -1176,10 +1182,8 @@ can be found in the os module (RTLD_xxx constants, e.g. os.RTLD_LAZY)."); static PyObject * sys_getdlopenflags(PyObject *self, PyObject *args) { - PyThreadState *tstate = PyThreadState_GET(); - if (!tstate) - return NULL; - return PyLong_FromLong(tstate->interp->dlopenflags); + PyInterpreterState *interp = _PyInterpreterState_Get(); + return PyLong_FromLong(interp->dlopenflags); } PyDoc_STRVAR(getdlopenflags_doc, diff --git a/Python/thread.c b/Python/thread.c index 07743840693b..a1e1fa64f120 100644 --- a/Python/thread.c +++ b/Python/thread.c @@ -92,7 +92,7 @@ PyThread_init_thread(void) size_t PyThread_get_stacksize(void) { - return PyThreadState_GET()->interp->pythread_stacksize; + return _PyInterpreterState_Get()->pythread_stacksize; } /* Only platforms defining a THREAD_SET_STACKSIZE() macro From webhook-mailer at python.org Fri Aug 3 11:59:24 2018 From: webhook-mailer at python.org (Petr Viktorin) Date: Fri, 03 Aug 2018 15:59:24 -0000 Subject: [Python-checkins] bpo-34325: Skip zipfile test for large timestamps when filesystem don't support them. (GH-8656) Message-ID: https://github.com/python/cpython/commit/7b41dbad78c6b03ca2f98800a92a1977d3946643 commit: 7b41dbad78c6b03ca2f98800a92a1977d3946643 branch: master author: Marcel Plch committer: Petr Viktorin date: 2018-08-03T17:59:19+02:00 summary: bpo-34325: Skip zipfile test for large timestamps when filesystem don't support them. (GH-8656) When the filesystem doesn't support files with large timestamps, skip testing that such files can be zipped. files: M Lib/test/test_zipfile.py diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py index 3b78b7f16759..68b56a0cd988 100644 --- a/Lib/test/test_zipfile.py +++ b/Lib/test/test_zipfile.py @@ -556,7 +556,11 @@ def test_add_file_before_1980(self): def test_add_file_after_2107(self): # Set atime and mtime to 2108-12-30 - os.utime(TESTFN, (4386268800, 4386268800)) + try: + os.utime(TESTFN, (4386268800, 4386268800)) + except OverflowError: + self.skipTest('Host fs cannot set timestamp to required value.') + with zipfile.ZipFile(TESTFN2, "w") as zipfp: self.assertRaises(struct.error, zipfp.write, TESTFN) From webhook-mailer at python.org Fri Aug 3 16:49:10 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Fri, 03 Aug 2018 20:49:10 -0000 Subject: [Python-checkins] bpo-34170: _PyCoreConfig_Read() don't replace coerce_c_locale (GH-8658) Message-ID: https://github.com/python/cpython/commit/5a953fd0ab4d0f792f4c1537616b2ce8ee40d976 commit: 5a953fd0ab4d0f792f4c1537616b2ce8ee40d976 branch: master author: Victor Stinner committer: GitHub date: 2018-08-03T22:49:07+02:00 summary: bpo-34170: _PyCoreConfig_Read() don't replace coerce_c_locale (GH-8658) If coerce_c_locale is already set (>= 0), use its value: don't override it. files: M Programs/_testembed.c M Python/coreconfig.c M Python/pylifecycle.c diff --git a/Programs/_testembed.c b/Programs/_testembed.c index 027de484545f..f1d30f6c54dd 100644 --- a/Programs/_testembed.c +++ b/Programs/_testembed.c @@ -373,6 +373,8 @@ dump_config(void) printf("quiet = %i\n", config->quiet); printf("user_site_directory = %i\n", config->user_site_directory); printf("buffered_stdio = %i\n", config->buffered_stdio); + ASSERT_EQUAL(config->buffered_stdio, !Py_UnbufferedStdioFlag); + /* FIXME: test legacy_windows_fs_encoding */ /* FIXME: test legacy_windows_stdio */ diff --git a/Python/coreconfig.c b/Python/coreconfig.c index 829592c3c9b2..1a32525b7b31 100644 --- a/Python/coreconfig.c +++ b/Python/coreconfig.c @@ -367,6 +367,8 @@ _PyCoreConfig_SetGlobalConfig(const _PyCoreConfig *config) static _PyInitError config_init_program_name(_PyCoreConfig *config) { + assert(config->program_name == NULL); + /* If Py_SetProgramName() was called, use its value */ const wchar_t *program_name = _Py_path_config.program_name; if (program_name != NULL) { @@ -665,16 +667,18 @@ config_read_env_vars(_PyCoreConfig *config) config->malloc_stats = 1; } - const char *env = _PyCoreConfig_GetEnv(config, "PYTHONCOERCECLOCALE"); - if (env) { - if (strcmp(env, "0") == 0) { - config->coerce_c_locale = 0; - } - else if (strcmp(env, "warn") == 0) { - config->coerce_c_locale_warn = 1; - } - else { - config->coerce_c_locale = 1; + if (config->coerce_c_locale < 0) { + const char *env = _PyCoreConfig_GetEnv(config, "PYTHONCOERCECLOCALE"); + if (env) { + if (strcmp(env, "0") == 0) { + config->coerce_c_locale = 0; + } + else if (strcmp(env, "warn") == 0) { + config->coerce_c_locale_warn = 1; + } + else { + config->coerce_c_locale = 1; + } } } @@ -820,10 +824,6 @@ config_read_complex_options(_PyCoreConfig *config) static void config_init_locale(_PyCoreConfig *config) { - if (config->utf8_mode >= 0 && config->coerce_c_locale >= 0) { - return; - } - if (_Py_LegacyLocaleDetected()) { /* POSIX locale: enable C locale coercion and UTF-8 Mode */ if (config->utf8_mode < 0) { @@ -832,7 +832,6 @@ config_init_locale(_PyCoreConfig *config) if (config->coerce_c_locale < 0) { config->coerce_c_locale = 1; } - return; } } @@ -909,7 +908,9 @@ _PyCoreConfig_Read(_PyCoreConfig *config) } } - config_init_locale(config); + if (config->utf8_mode < 0 || config->coerce_c_locale < 0) { + config_init_locale(config); + } if (config->_install_importlib) { err = _PyCoreConfig_InitPathConfig(config); diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index c2ee4ffc4681..28704c1c228b 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -805,7 +805,6 @@ _Py_InitializeCore(PyInterpreterState **interp_p, { assert(src_config != NULL); - PyMemAllocatorEx old_alloc; _PyInitError err; From webhook-mailer at python.org Fri Aug 3 16:49:45 2018 From: webhook-mailer at python.org (Berker Peksag) Date: Fri, 03 Aug 2018 20:49:45 -0000 Subject: [Python-checkins] bpo-34329: Doc'd how to remove suffix of pathlib.Path() (GH-8655) Message-ID: https://github.com/python/cpython/commit/46dc4e34ed8005a688d7f3512844ef227a3465f4 commit: 46dc4e34ed8005a688d7f3512844ef227a3465f4 branch: master author: Stefan Otte committer: Berker Peksag date: 2018-08-03T23:49:42+03:00 summary: bpo-34329: Doc'd how to remove suffix of pathlib.Path() (GH-8655) files: M Doc/library/pathlib.rst M Lib/pathlib.py diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index 3d28a1bea303..0929ee27c19e 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -559,7 +559,8 @@ Pure paths provide the following methods and properties: .. method:: PurePath.with_suffix(suffix) Return a new path with the :attr:`suffix` changed. If the original path - doesn't have a suffix, the new *suffix* is appended instead:: + doesn't have a suffix, the new *suffix* is appended instead. If the + *suffix* is an empty string, the original suffix is removed:: >>> p = PureWindowsPath('c:/Downloads/pathlib.tar.gz') >>> p.with_suffix('.bz2') @@ -567,6 +568,9 @@ Pure paths provide the following methods and properties: >>> p = PureWindowsPath('README') >>> p.with_suffix('.txt') PureWindowsPath('README.txt') + >>> p = PureWindowsPath('README.txt') + >>> p.with_suffix('') + PureWindowsPath('README') .. _concrete-paths: diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 8431c29c1d65..4fe9d4aefc3b 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -807,8 +807,10 @@ def with_name(self, name): self._parts[:-1] + [name]) def with_suffix(self, suffix): - """Return a new path with the file suffix changed (or added, if none).""" - # XXX if suffix is None, should the current suffix be removed? + """Return a new path with the file suffix changed. If the path + has no suffix, add given suffix. If the given suffix is an empty + string, remove the suffix from the path. + """ f = self._flavour if f.sep in suffix or f.altsep and f.altsep in suffix: raise ValueError("Invalid suffix %r" % (suffix)) From webhook-mailer at python.org Fri Aug 3 17:00:59 2018 From: webhook-mailer at python.org (Pablo Galindo) Date: Fri, 03 Aug 2018 21:00:59 -0000 Subject: [Python-checkins] bpo-30317: Fix multiprocessing test_timeout() (GH-8621) Message-ID: https://github.com/python/cpython/commit/f0e3da8814259283e8f3d53ad3d9735ffc273e00 commit: f0e3da8814259283e8f3d53ad3d9735ffc273e00 branch: 3.6 author: Pablo Galindo committer: GitHub date: 2018-08-03T22:00:53+01:00 summary: bpo-30317: Fix multiprocessing test_timeout() (GH-8621) Multiprocessing test_timeout() now accepts a delta of 100 ms instead of just 50 ms, since the test failed with 135.8 ms instead of the expected 200 ms. (cherry picked from commit 5640d030e100aade54210034828b711c3b506b18) Co-authored-by: Victor Stinner files: M Lib/test/_test_multiprocessing.py diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index c0030ee2f49a..5c625dd49541 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -882,9 +882,10 @@ def test_timeout(self): start = time.time() self.assertRaises(pyqueue.Empty, q.get, True, 0.200) delta = time.time() - start - # Tolerate a delta of 50 ms because of the bad clock resolution on - # Windows (usually 15.6 ms) - self.assertGreaterEqual(delta, 0.150) + # bpo-30317: Tolerate a delta of 100 ms because of the bad clock + # resolution on Windows (usually 15.6 ms). x86 Windows7 3.x once + # failed because the delta was only 135.8 ms. + self.assertGreaterEqual(delta, 0.100) close_queue(q) def test_queue_feeder_donot_stop_onexc(self): From webhook-mailer at python.org Fri Aug 3 17:45:24 2018 From: webhook-mailer at python.org (Berker Peksag) Date: Fri, 03 Aug 2018 21:45:24 -0000 Subject: [Python-checkins] bpo-34329: Doc'd how to remove suffix of pathlib.Path() (GH-8655) Message-ID: https://github.com/python/cpython/commit/764f9d09b03654d7cd70d59afafafd51d24f2750 commit: 764f9d09b03654d7cd70d59afafafd51d24f2750 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Berker Peksag date: 2018-08-04T00:45:20+03:00 summary: bpo-34329: Doc'd how to remove suffix of pathlib.Path() (GH-8655) (cherry picked from commit 46dc4e34ed8005a688d7f3512844ef227a3465f4) Co-authored-by: Stefan Otte files: M Doc/library/pathlib.rst M Lib/pathlib.py diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index 34ab3b8edf96..7b3c9ecc2f09 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -555,7 +555,8 @@ Pure paths provide the following methods and properties: .. method:: PurePath.with_suffix(suffix) Return a new path with the :attr:`suffix` changed. If the original path - doesn't have a suffix, the new *suffix* is appended instead:: + doesn't have a suffix, the new *suffix* is appended instead. If the + *suffix* is an empty string, the original suffix is removed:: >>> p = PureWindowsPath('c:/Downloads/pathlib.tar.gz') >>> p.with_suffix('.bz2') @@ -563,6 +564,9 @@ Pure paths provide the following methods and properties: >>> p = PureWindowsPath('README') >>> p.with_suffix('.txt') PureWindowsPath('README.txt') + >>> p = PureWindowsPath('README.txt') + >>> p.with_suffix('') + PureWindowsPath('README') .. _concrete-paths: diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 44788c02c2f3..5e130110f2e8 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -823,8 +823,10 @@ def with_name(self, name): self._parts[:-1] + [name]) def with_suffix(self, suffix): - """Return a new path with the file suffix changed (or added, if none).""" - # XXX if suffix is None, should the current suffix be removed? + """Return a new path with the file suffix changed. If the path + has no suffix, add given suffix. If the given suffix is an empty + string, remove the suffix from the path. + """ f = self._flavour if f.sep in suffix or f.altsep and f.altsep in suffix: raise ValueError("Invalid suffix %r" % (suffix)) From webhook-mailer at python.org Fri Aug 3 17:45:50 2018 From: webhook-mailer at python.org (Berker Peksag) Date: Fri, 03 Aug 2018 21:45:50 -0000 Subject: [Python-checkins] bpo-34329: Doc'd how to remove suffix of pathlib.Path() (GH-8655) Message-ID: https://github.com/python/cpython/commit/39fcd9949832323b672f7ff05fd1498b8844a329 commit: 39fcd9949832323b672f7ff05fd1498b8844a329 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Berker Peksag date: 2018-08-04T00:45:46+03:00 summary: bpo-34329: Doc'd how to remove suffix of pathlib.Path() (GH-8655) (cherry picked from commit 46dc4e34ed8005a688d7f3512844ef227a3465f4) Co-authored-by: Stefan Otte files: M Doc/library/pathlib.rst M Lib/pathlib.py diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index 3d28a1bea303..0929ee27c19e 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -559,7 +559,8 @@ Pure paths provide the following methods and properties: .. method:: PurePath.with_suffix(suffix) Return a new path with the :attr:`suffix` changed. If the original path - doesn't have a suffix, the new *suffix* is appended instead:: + doesn't have a suffix, the new *suffix* is appended instead. If the + *suffix* is an empty string, the original suffix is removed:: >>> p = PureWindowsPath('c:/Downloads/pathlib.tar.gz') >>> p.with_suffix('.bz2') @@ -567,6 +568,9 @@ Pure paths provide the following methods and properties: >>> p = PureWindowsPath('README') >>> p.with_suffix('.txt') PureWindowsPath('README.txt') + >>> p = PureWindowsPath('README.txt') + >>> p.with_suffix('') + PureWindowsPath('README') .. _concrete-paths: diff --git a/Lib/pathlib.py b/Lib/pathlib.py index 8431c29c1d65..4fe9d4aefc3b 100644 --- a/Lib/pathlib.py +++ b/Lib/pathlib.py @@ -807,8 +807,10 @@ def with_name(self, name): self._parts[:-1] + [name]) def with_suffix(self, suffix): - """Return a new path with the file suffix changed (or added, if none).""" - # XXX if suffix is None, should the current suffix be removed? + """Return a new path with the file suffix changed. If the path + has no suffix, add given suffix. If the given suffix is an empty + string, remove the suffix from the path. + """ f = self._flavour if f.sep in suffix or f.altsep and f.altsep in suffix: raise ValueError("Invalid suffix %r" % (suffix)) From webhook-mailer at python.org Fri Aug 3 17:54:08 2018 From: webhook-mailer at python.org (Victor Stinner) Date: Fri, 03 Aug 2018 21:54:08 -0000 Subject: [Python-checkins] bpo-34170: Fix pymain_run_file() (GH-8660) Message-ID: https://github.com/python/cpython/commit/d8078626770a8d358eb83d7928c12d75ff4e821a commit: d8078626770a8d358eb83d7928c12d75ff4e821a branch: master author: Victor Stinner committer: GitHub date: 2018-08-03T23:54:06+02:00 summary: bpo-34170: Fix pymain_run_file() (GH-8660) bpo-34170, bpo-34326: Fix pymain_run_file(): use PyRun_AnyFileExFlags(closeit=1) instead of calling fclose(fp) explicitly to close the input file before running the code. files: M Modules/main.c diff --git a/Modules/main.c b/Modules/main.c index 06aa1124c796..da3441f3c96e 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -1129,14 +1129,16 @@ pymain_run_file(_PyMain *pymain, _PyCoreConfig *config, PyCompilerFlags *cf) "%ls: '%ls' is a directory, cannot continue\n", config->program, filename); pymain->status = 1; - goto done; + fclose(fp); + return; } /* call pending calls like signal handlers (SIGINT) */ if (Py_MakePendingCalls() == -1) { PyErr_Print(); pymain->status = 1; - goto done; + fclose(fp); + return; } PyObject *unicode, *bytes = NULL; @@ -1155,12 +1157,10 @@ pymain_run_file(_PyMain *pymain, _PyCoreConfig *config, PyCompilerFlags *cf) filename_str = ""; } - int run = PyRun_AnyFileExFlags(fp, filename_str, 0, cf); + /* PyRun_AnyFileExFlags(closeit=1) calls fclose(fp) before running code */ + int run = PyRun_AnyFileExFlags(fp, filename_str, 1, cf); Py_XDECREF(bytes); pymain->status = (run != 0); - -done: - fclose(fp); } From webhook-mailer at python.org Fri Aug 3 17:56:58 2018 From: webhook-mailer at python.org (Berker Peksag) Date: Fri, 03 Aug 2018 21:56:58 -0000 Subject: [Python-checkins] Fix reST markup in unittest documentation (GH-8665) Message-ID: https://github.com/python/cpython/commit/2e5566d9e774dcde81e8139b486730917816e045 commit: 2e5566d9e774dcde81e8139b486730917816e045 branch: master author: Berker Peksag committer: GitHub date: 2018-08-04T00:56:55+03:00 summary: Fix reST markup in unittest documentation (GH-8665) files: M Doc/library/unittest.rst diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index 086d93782562..224adf08567e 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -412,7 +412,7 @@ run whether the test method succeeded or not. Such a working environment for the testing code is called a :dfn:`test fixture`. A new TestCase instance is created as a unique test fixture used to execute each individual test method. Thus -`~TestCase.setUp`, `~TestCase.tearDown`, and `~TestCase.__init__` +:meth:`~TestCase.setUp`, :meth:`~TestCase.tearDown`, and :meth:`~TestCase.__init__` will be called once per test. It is recommended that you use TestCase implementations to group tests together From webhook-mailer at python.org Fri Aug 3 18:13:56 2018 From: webhook-mailer at python.org (Berker Peksag) Date: Fri, 03 Aug 2018 22:13:56 -0000 Subject: [Python-checkins] Fix reST markup in unittest documentation (GH-8665) Message-ID: https://github.com/python/cpython/commit/2a2a3f56f510844a5266f74f347a3fa3baee8ba2 commit: 2a2a3f56f510844a5266f74f347a3fa3baee8ba2 branch: 3.7 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Berker Peksag date: 2018-08-04T01:13:52+03:00 summary: Fix reST markup in unittest documentation (GH-8665) (cherry picked from commit 2e5566d9e774dcde81e8139b486730917816e045) Co-authored-by: Berker Peksag files: M Doc/library/unittest.rst diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index 086d93782562..224adf08567e 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -412,7 +412,7 @@ run whether the test method succeeded or not. Such a working environment for the testing code is called a :dfn:`test fixture`. A new TestCase instance is created as a unique test fixture used to execute each individual test method. Thus -`~TestCase.setUp`, `~TestCase.tearDown`, and `~TestCase.__init__` +:meth:`~TestCase.setUp`, :meth:`~TestCase.tearDown`, and :meth:`~TestCase.__init__` will be called once per test. It is recommended that you use TestCase implementations to group tests together From webhook-mailer at python.org Fri Aug 3 18:17:18 2018 From: webhook-mailer at python.org (Berker Peksag) Date: Fri, 03 Aug 2018 22:17:18 -0000 Subject: [Python-checkins] Fix reST markup in unittest documentation (GH-8665) Message-ID: https://github.com/python/cpython/commit/c01807436ab7c1ae9a3f6ecb955b28c2c743c4d2 commit: c01807436ab7c1ae9a3f6ecb955b28c2c743c4d2 branch: 3.6 author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com> committer: Berker Peksag date: 2018-08-04T01:17:15+03:00 summary: Fix reST markup in unittest documentation (GH-8665) (cherry picked from commit 2e5566d9e774dcde81e8139b486730917816e045) Co-authored-by: Berker Peksag files: M Doc/library/unittest.rst diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index f0de85f76157..16e1049b7a18 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -393,7 +393,7 @@ run whether the test method succeeded or not. Such a working environment for the testing code is called a :dfn:`test fixture`. A new TestCase instance is created as a unique test fixture used to execute each individual test method. Thus -`~TestCase.setUp`, `~TestCase.tearDown`, and `~TestCase.__init__` +:meth:`~TestCase.setUp`, :meth:`~TestCase.tearDown`, and :meth:`~TestCase.__init__` will be called once per test. It is recommended that you use TestCase implementations to group tests together From solipsis at pitrou.net Sat Aug 4 05:09:54 2018 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sat, 04 Aug 2018 09:09:54 +0000 Subject: [Python-checkins] Daily reference leaks (4243df51fe43): sum=12 Message-ID: <20180804090954.1.7E9EC296DBE6F95A@psf.io> results for 4243df51fe43 on branch "default" -------------------------------------------- test_collections leaked [0, 7, 0] memory blocks, sum=7 test_functools leaked [0, 3, 1] memory blocks, sum=4 test_multiprocessing_fork leaked [1, 2, -2] memory blocks, sum=1 test_multiprocessing_forkserver leaked [2, 0, -2] memory blocks, sum=0 Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/psf-users/antoine/refleaks/reflogQtPtqD', '--timeout', '7200'] From webhook-mailer at python.org Sun Aug 5 02:21:12 2018 From: webhook-mailer at python.org (Tal Einat) Date: Sun, 05 Aug 2018 06:21:12 -0000 Subject: [Python-checkins] bpo-33839: refactor IDLE's tooltips & calltips, add docstrings and tests (GH-7683) Message-ID: https://github.com/python/cpython/commit/87e59ac11ee074b0dc1bc864c74fac0660b27f6e commit: 87e59ac11ee074b0dc1bc864c74fac0660b27f6e branch: master author: Tal Einat committer: GitHub date: 2018-08-05T09:21:08+03:00 summary: bpo-33839: refactor IDLE's tooltips & calltips, add docstrings and tests (GH-7683) * make CallTip and ToolTip sub-classes of a common abstract base class * remove ListboxToolTip (unused and ugly) * greatly increase test coverage * tested on Windows, Linux and macOS files: A Lib/idlelib/idle_test/test_tooltip.py A Misc/NEWS.d/next/IDLE/2018-06-14-13-23-55.bpo-33839.ZlJzHa.rst M Lib/idlelib/calltip.py M Lib/idlelib/calltip_w.py M Lib/idlelib/idle_test/htest.py M Lib/idlelib/idle_test/test_calltip_w.py M Lib/idlelib/tooltip.py diff --git a/Lib/idlelib/calltip.py b/Lib/idlelib/calltip.py index 596d2bcb2775..758569a45fdf 100644 --- a/Lib/idlelib/calltip.py +++ b/Lib/idlelib/calltip.py @@ -51,7 +51,7 @@ def try_open_calltip_event(self, event): self.open_calltip(False) def refresh_calltip_event(self, event): - if self.active_calltip and self.active_calltip.is_active(): + if self.active_calltip and self.active_calltip.tipwindow: self.open_calltip(False) def open_calltip(self, evalfuncs): diff --git a/Lib/idlelib/calltip_w.py b/Lib/idlelib/calltip_w.py index 1b1ffc59483d..7553dfefc55c 100644 --- a/Lib/idlelib/calltip_w.py +++ b/Lib/idlelib/calltip_w.py @@ -1,111 +1,118 @@ -"""A calltip window class for Tkinter/IDLE. +"""A call-tip window class for Tkinter/IDLE. -After tooltip.py, which uses ideas gleaned from PySol -Used by calltip. +After tooltip.py, which uses ideas gleaned from PySol. +Used by calltip.py. """ -from tkinter import Toplevel, Label, LEFT, SOLID, TclError +from tkinter import Label, LEFT, SOLID, TclError -HIDE_VIRTUAL_EVENT_NAME = "<>" +from idlelib.tooltip import TooltipBase + +HIDE_EVENT = "<>" HIDE_SEQUENCES = ("", "") -CHECKHIDE_VIRTUAL_EVENT_NAME = "<>" +CHECKHIDE_EVENT = "<>" CHECKHIDE_SEQUENCES = ("", "") -CHECKHIDE_TIME = 100 # milliseconds +CHECKHIDE_TIME = 100 # milliseconds MARK_RIGHT = "calltipwindowregion_right" -class CalltipWindow: - def __init__(self, widget): - self.widget = widget - self.tipwindow = self.label = None - self.parenline = self.parencol = None - self.lastline = None +class CalltipWindow(TooltipBase): + """A call-tip widget for tkinter text widgets.""" + + def __init__(self, text_widget): + """Create a call-tip; shown by showtip(). + + text_widget: a Text widget with code for which call-tips are desired + """ + # Note: The Text widget will be accessible as self.anchor_widget + super(CalltipWindow, self).__init__(text_widget) + + self.label = self.text = None + self.parenline = self.parencol = self.lastline = None self.hideid = self.checkhideid = None self.checkhide_after_id = None - def position_window(self): - """Check if needs to reposition the window, and if so - do it.""" - curline = int(self.widget.index("insert").split('.')[0]) - if curline == self.lastline: - return - self.lastline = curline - self.widget.see("insert") + def get_position(self): + """Choose the position of the call-tip.""" + curline = int(self.anchor_widget.index("insert").split('.')[0]) if curline == self.parenline: - box = self.widget.bbox("%d.%d" % (self.parenline, - self.parencol)) + anchor_index = (self.parenline, self.parencol) else: - box = self.widget.bbox("%d.0" % curline) + anchor_index = (curline, 0) + box = self.anchor_widget.bbox("%d.%d" % anchor_index) if not box: - box = list(self.widget.bbox("insert")) + box = list(self.anchor_widget.bbox("insert")) # align to left of window box[0] = 0 box[2] = 0 - x = box[0] + self.widget.winfo_rootx() + 2 - y = box[1] + box[3] + self.widget.winfo_rooty() - self.tipwindow.wm_geometry("+%d+%d" % (x, y)) + return box[0] + 2, box[1] + box[3] + + def position_window(self): + "Reposition the window if needed." + curline = int(self.anchor_widget.index("insert").split('.')[0]) + if curline == self.lastline: + return + self.lastline = curline + self.anchor_widget.see("insert") + super(CalltipWindow, self).position_window() def showtip(self, text, parenleft, parenright): - """Show the calltip, bind events which will close it and reposition it. + """Show the call-tip, bind events which will close it and reposition it. + + text: the text to display in the call-tip + parenleft: index of the opening parenthesis in the text widget + parenright: index of the closing parenthesis in the text widget, + or the end of the line if there is no closing parenthesis """ # Only called in calltip.Calltip, where lines are truncated self.text = text if self.tipwindow or not self.text: return - self.widget.mark_set(MARK_RIGHT, parenright) + self.anchor_widget.mark_set(MARK_RIGHT, parenright) self.parenline, self.parencol = map( - int, self.widget.index(parenleft).split(".")) + int, self.anchor_widget.index(parenleft).split(".")) - self.tipwindow = tw = Toplevel(self.widget) - self.position_window() - # remove border on calltip window - tw.wm_overrideredirect(1) - try: - # This command is only needed and available on Tk >= 8.4.0 for OSX - # Without it, call tips intrude on the typing process by grabbing - # the focus. - tw.tk.call("::tk::unsupported::MacWindowStyle", "style", tw._w, - "help", "noActivates") - except TclError: - pass - self.label = Label(tw, text=self.text, justify=LEFT, + super(CalltipWindow, self).showtip() + + self._bind_events() + + def showcontents(self): + """Create the call-tip widget.""" + self.label = Label(self.tipwindow, text=self.text, justify=LEFT, background="#ffffe0", relief=SOLID, borderwidth=1, - font = self.widget['font']) + font=self.anchor_widget['font']) self.label.pack() - tw.update_idletasks() - tw.lift() # work around bug in Tk 8.5.18+ (issue #24570) - - self.checkhideid = self.widget.bind(CHECKHIDE_VIRTUAL_EVENT_NAME, - self.checkhide_event) - for seq in CHECKHIDE_SEQUENCES: - self.widget.event_add(CHECKHIDE_VIRTUAL_EVENT_NAME, seq) - self.widget.after(CHECKHIDE_TIME, self.checkhide_event) - self.hideid = self.widget.bind(HIDE_VIRTUAL_EVENT_NAME, - self.hide_event) - for seq in HIDE_SEQUENCES: - self.widget.event_add(HIDE_VIRTUAL_EVENT_NAME, seq) def checkhide_event(self, event=None): + """Handle CHECK_HIDE_EVENT: call hidetip or reschedule.""" if not self.tipwindow: - # If the event was triggered by the same event that unbinded + # If the event was triggered by the same event that unbound # this function, the function will be called nevertheless, # so do nothing in this case. return None - curline, curcol = map(int, self.widget.index("insert").split('.')) + + # Hide the call-tip if the insertion cursor moves outside of the + # parenthesis. + curline, curcol = map(int, self.anchor_widget.index("insert").split('.')) if curline < self.parenline or \ (curline == self.parenline and curcol <= self.parencol) or \ - self.widget.compare("insert", ">", MARK_RIGHT): + self.anchor_widget.compare("insert", ">", MARK_RIGHT): self.hidetip() return "break" - else: - self.position_window() - if self.checkhide_after_id is not None: - self.widget.after_cancel(self.checkhide_after_id) - self.checkhide_after_id = \ - self.widget.after(CHECKHIDE_TIME, self.checkhide_event) - return None + + # Not hiding the call-tip. + + self.position_window() + # Re-schedule this function to be called again in a short while. + if self.checkhide_after_id is not None: + self.anchor_widget.after_cancel(self.checkhide_after_id) + self.checkhide_after_id = \ + self.anchor_widget.after(CHECKHIDE_TIME, self.checkhide_event) + return None def hide_event(self, event): + """Handle HIDE_EVENT by calling hidetip.""" if not self.tipwindow: # See the explanation in checkhide_event. return None @@ -113,51 +120,76 @@ def hide_event(self, event): return "break" def hidetip(self): + """Hide the call-tip.""" if not self.tipwindow: return - for seq in CHECKHIDE_SEQUENCES: - self.widget.event_delete(CHECKHIDE_VIRTUAL_EVENT_NAME, seq) - self.widget.unbind(CHECKHIDE_VIRTUAL_EVENT_NAME, self.checkhideid) - self.checkhideid = None - for seq in HIDE_SEQUENCES: - self.widget.event_delete(HIDE_VIRTUAL_EVENT_NAME, seq) - self.widget.unbind(HIDE_VIRTUAL_EVENT_NAME, self.hideid) - self.hideid = None - - self.label.destroy() + try: + self.label.destroy() + except TclError: + pass self.label = None - self.tipwindow.destroy() - self.tipwindow = None - self.widget.mark_unset(MARK_RIGHT) self.parenline = self.parencol = self.lastline = None + try: + self.anchor_widget.mark_unset(MARK_RIGHT) + except TclError: + pass - def is_active(self): - return bool(self.tipwindow) + try: + self._unbind_events() + except (TclError, ValueError): + # ValueError may be raised by MultiCall + pass + + super(CalltipWindow, self).hidetip() + + def _bind_events(self): + """Bind event handlers.""" + self.checkhideid = self.anchor_widget.bind(CHECKHIDE_EVENT, + self.checkhide_event) + for seq in CHECKHIDE_SEQUENCES: + self.anchor_widget.event_add(CHECKHIDE_EVENT, seq) + self.anchor_widget.after(CHECKHIDE_TIME, self.checkhide_event) + self.hideid = self.anchor_widget.bind(HIDE_EVENT, + self.hide_event) + for seq in HIDE_SEQUENCES: + self.anchor_widget.event_add(HIDE_EVENT, seq) + + def _unbind_events(self): + """Unbind event handlers.""" + for seq in CHECKHIDE_SEQUENCES: + self.anchor_widget.event_delete(CHECKHIDE_EVENT, seq) + self.anchor_widget.unbind(CHECKHIDE_EVENT, self.checkhideid) + self.checkhideid = None + for seq in HIDE_SEQUENCES: + self.anchor_widget.event_delete(HIDE_EVENT, seq) + self.anchor_widget.unbind(HIDE_EVENT, self.hideid) + self.hideid = None def _calltip_window(parent): # htest # from tkinter import Toplevel, Text, LEFT, BOTH top = Toplevel(parent) - top.title("Test calltips") + top.title("Test call-tips") x, y = map(int, parent.geometry().split('+')[1:]) - top.geometry("200x100+%d+%d" % (x + 250, y + 175)) + top.geometry("250x100+%d+%d" % (x + 175, y + 150)) text = Text(top) text.pack(side=LEFT, fill=BOTH, expand=1) text.insert("insert", "string.split") top.update() - calltip = CalltipWindow(text) + calltip = CalltipWindow(text) def calltip_show(event): - calltip.showtip("(s=Hello world)", "insert", "end") + calltip.showtip("(s='Hello world')", "insert", "end") def calltip_hide(event): calltip.hidetip() text.event_add("<>", "(") text.event_add("<>", ")") text.bind("<>", calltip_show) text.bind("<>", calltip_hide) + text.focus_set() if __name__ == '__main__': diff --git a/Lib/idlelib/idle_test/htest.py b/Lib/idlelib/idle_test/htest.py index 95f627439297..03bee5170735 100644 --- a/Lib/idlelib/idle_test/htest.py +++ b/Lib/idlelib/idle_test/htest.py @@ -80,11 +80,14 @@ def _wrapper(parent): # htest # "are correctly displayed.\n [Close] to exit.", } +# TODO implement ^\; adding '' to function does not work. _calltip_window_spec = { 'file': 'calltip_w', 'kwds': {}, 'msg': "Typing '(' should display a calltip.\n" "Typing ') should hide the calltip.\n" + "So should moving cursor out of argument area.\n" + "Force-open-calltip does not work here.\n" } _module_browser_spec = { diff --git a/Lib/idlelib/idle_test/test_calltip_w.py b/Lib/idlelib/idle_test/test_calltip_w.py index 59e69677e679..a5ec76e15ffd 100644 --- a/Lib/idlelib/idle_test/test_calltip_w.py +++ b/Lib/idlelib/idle_test/test_calltip_w.py @@ -23,7 +23,7 @@ def tearDownClass(cls): del cls.text, cls.root def test_init(self): - self.assertEqual(self.calltip.widget, self.text) + self.assertEqual(self.calltip.anchor_widget, self.text) if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/Lib/idlelib/idle_test/test_tooltip.py b/Lib/idlelib/idle_test/test_tooltip.py new file mode 100644 index 000000000000..44ea1110e155 --- /dev/null +++ b/Lib/idlelib/idle_test/test_tooltip.py @@ -0,0 +1,146 @@ +from idlelib.tooltip import TooltipBase, Hovertip +from test.support import requires +requires('gui') + +from functools import wraps +import time +from tkinter import Button, Tk, Toplevel +import unittest + + +def setUpModule(): + global root + root = Tk() + +def root_update(): + global root + root.update() + +def tearDownModule(): + global root + root.update_idletasks() + root.destroy() + del root + +def add_call_counting(func): + @wraps(func) + def wrapped_func(*args, **kwargs): + wrapped_func.call_args_list.append((args, kwargs)) + return func(*args, **kwargs) + wrapped_func.call_args_list = [] + return wrapped_func + + +def _make_top_and_button(testobj): + global root + top = Toplevel(root) + testobj.addCleanup(top.destroy) + top.title("Test tooltip") + button = Button(top, text='ToolTip test button') + button.pack() + testobj.addCleanup(button.destroy) + top.lift() + return top, button + + +class ToolTipBaseTest(unittest.TestCase): + def setUp(self): + self.top, self.button = _make_top_and_button(self) + + def test_base_class_is_unusable(self): + global root + top = Toplevel(root) + self.addCleanup(top.destroy) + + button = Button(top, text='ToolTip test button') + button.pack() + self.addCleanup(button.destroy) + + with self.assertRaises(NotImplementedError): + tooltip = TooltipBase(button) + tooltip.showtip() + + +class HovertipTest(unittest.TestCase): + def setUp(self): + self.top, self.button = _make_top_and_button(self) + + def test_showtip(self): + tooltip = Hovertip(self.button, 'ToolTip text') + self.addCleanup(tooltip.hidetip) + self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + tooltip.showtip() + self.assertTrue(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + + def test_showtip_twice(self): + tooltip = Hovertip(self.button, 'ToolTip text') + self.addCleanup(tooltip.hidetip) + self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + tooltip.showtip() + self.assertTrue(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + orig_tipwindow = tooltip.tipwindow + tooltip.showtip() + self.assertTrue(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + self.assertIs(tooltip.tipwindow, orig_tipwindow) + + def test_hidetip(self): + tooltip = Hovertip(self.button, 'ToolTip text') + self.addCleanup(tooltip.hidetip) + tooltip.showtip() + tooltip.hidetip() + self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + + def test_showtip_on_mouse_enter_no_delay(self): + tooltip = Hovertip(self.button, 'ToolTip text', hover_delay=None) + self.addCleanup(tooltip.hidetip) + tooltip.showtip = add_call_counting(tooltip.showtip) + root_update() + self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + self.button.event_generate('', x=0, y=0) + root_update() + self.assertTrue(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + self.assertGreater(len(tooltip.showtip.call_args_list), 0) + + def test_showtip_on_mouse_enter_hover_delay(self): + tooltip = Hovertip(self.button, 'ToolTip text', hover_delay=50) + self.addCleanup(tooltip.hidetip) + tooltip.showtip = add_call_counting(tooltip.showtip) + root_update() + self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + self.button.event_generate('', x=0, y=0) + root_update() + self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + time.sleep(0.1) + root_update() + self.assertTrue(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + self.assertGreater(len(tooltip.showtip.call_args_list), 0) + + def test_hidetip_on_mouse_leave(self): + tooltip = Hovertip(self.button, 'ToolTip text', hover_delay=None) + self.addCleanup(tooltip.hidetip) + tooltip.showtip = add_call_counting(tooltip.showtip) + root_update() + self.button.event_generate('', x=0, y=0) + root_update() + self.button.event_generate('', x=0, y=0) + root_update() + self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + self.assertGreater(len(tooltip.showtip.call_args_list), 0) + + def test_dont_show_on_mouse_leave_before_delay(self): + tooltip = Hovertip(self.button, 'ToolTip text', hover_delay=50) + self.addCleanup(tooltip.hidetip) + tooltip.showtip = add_call_counting(tooltip.showtip) + root_update() + self.button.event_generate('', x=0, y=0) + root_update() + self.button.event_generate('', x=0, y=0) + root_update() + time.sleep(0.1) + root_update() + self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + self.assertEqual(tooltip.showtip.call_args_list, []) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/Lib/idlelib/tooltip.py b/Lib/idlelib/tooltip.py index 843fb4a7d0b7..f54ea36f059d 100644 --- a/Lib/idlelib/tooltip.py +++ b/Lib/idlelib/tooltip.py @@ -1,80 +1,167 @@ -# general purpose 'tooltip' routines - currently unused in idlelib -# (although the 'calltips' extension is partly based on this code) -# may be useful for some purposes in (or almost in ;) the current project scope -# Ideas gleaned from PySol +"""Tools for displaying tool-tips. +This includes: + * an abstract base-class for different kinds of tooltips + * a simple text-only Tooltip class +""" from tkinter import * -class ToolTipBase: - def __init__(self, button): - self.button = button - self.tipwindow = None - self.id = None - self.x = self.y = 0 - self._id1 = self.button.bind("", self.enter) - self._id2 = self.button.bind("", self.leave) - self._id3 = self.button.bind("", self.leave) +class TooltipBase(object): + """abstract base class for tooltips""" - def enter(self, event=None): - self.schedule() + def __init__(self, anchor_widget): + """Create a tooltip. - def leave(self, event=None): - self.unschedule() - self.hidetip() + anchor_widget: the widget next to which the tooltip will be shown - def schedule(self): - self.unschedule() - self.id = self.button.after(1500, self.showtip) + Note that a widget will only be shown when showtip() is called. + """ + self.anchor_widget = anchor_widget + self.tipwindow = None - def unschedule(self): - id = self.id - self.id = None - if id: - self.button.after_cancel(id) + def __del__(self): + self.hidetip() def showtip(self): + """display the tooltip""" if self.tipwindow: return - # The tip window must be completely outside the button; + self.tipwindow = tw = Toplevel(self.anchor_widget) + # show no border on the top level window + tw.wm_overrideredirect(1) + try: + # This command is only needed and available on Tk >= 8.4.0 for OSX. + # Without it, call tips intrude on the typing process by grabbing + # the focus. + tw.tk.call("::tk::unsupported::MacWindowStyle", "style", tw._w, + "help", "noActivates") + except TclError: + pass + + self.position_window() + self.showcontents() + self.tipwindow.update_idletasks() # Needed on MacOS -- see #34275. + self.tipwindow.lift() # work around bug in Tk 8.5.18+ (issue #24570) + + def position_window(self): + """(re)-set the tooltip's screen position""" + x, y = self.get_position() + root_x = self.anchor_widget.winfo_rootx() + x + root_y = self.anchor_widget.winfo_rooty() + y + self.tipwindow.wm_geometry("+%d+%d" % (root_x, root_y)) + + def get_position(self): + """choose a screen position for the tooltip""" + # The tip window must be completely outside the anchor widget; # otherwise when the mouse enters the tip window we get # a leave event and it disappears, and then we get an enter # event and it reappears, and so on forever :-( - x = self.button.winfo_rootx() + 20 - y = self.button.winfo_rooty() + self.button.winfo_height() + 1 - self.tipwindow = tw = Toplevel(self.button) - tw.wm_overrideredirect(1) - tw.wm_geometry("+%d+%d" % (x, y)) - self.showcontents() + # + # Note: This is a simplistic implementation; sub-classes will likely + # want to override this. + return 20, self.anchor_widget.winfo_height() + 1 - def showcontents(self, text="Your text here"): - # Override this in derived class - label = Label(self.tipwindow, text=text, justify=LEFT, - background="#ffffe0", relief=SOLID, borderwidth=1) - label.pack() + def showcontents(self): + """content display hook for sub-classes""" + # See ToolTip for an example + raise NotImplementedError def hidetip(self): + """hide the tooltip""" + # Note: This is called by __del__, so careful when overriding/extending tw = self.tipwindow self.tipwindow = None if tw: - tw.destroy() + try: + tw.destroy() + except TclError: + pass + + +class OnHoverTooltipBase(TooltipBase): + """abstract base class for tooltips, with delayed on-hover display""" + + def __init__(self, anchor_widget, hover_delay=1000): + """Create a tooltip with a mouse hover delay. + + anchor_widget: the widget next to which the tooltip will be shown + hover_delay: time to delay before showing the tooltip, in milliseconds -class ToolTip(ToolTipBase): - def __init__(self, button, text): - ToolTipBase.__init__(self, button) + Note that a widget will only be shown when showtip() is called, + e.g. after hovering over the anchor widget with the mouse for enough + time. + """ + super(OnHoverTooltipBase, self).__init__(anchor_widget) + self.hover_delay = hover_delay + + self._after_id = None + self._id1 = self.anchor_widget.bind("", self._show_event) + self._id2 = self.anchor_widget.bind("", self._hide_event) + self._id3 = self.anchor_widget.bind("