Python-checkins
Threads by month
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
July 2020
- 1 participants
- 218 discussions
To: python-checkins(a)python.org
Subject: bpo-40967: Remove deprecated asyncio.Task.current_task() and
asyncio.Task.all_tasks() (GH-20874)
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: quoted-printable
MIME-Version: 1.0
https://github.com/python/cpython/commit/004e64e8059fe68a72890314673282f2e6…
5ce1
commit: 004e64e8059fe68a72890314673282f2e60d5ce1
branch: master
author: R=C3=A9mi Lapeyre <remi.lapeyre(a)lenstra.fr>
committer: GitHub <noreply(a)github.com>
date: 2020-07-01T20:41:21-07:00
summary:
bpo-40967: Remove deprecated asyncio.Task.current_task() and asyncio.Task.all=
_tasks() (GH-20874)
files:
A Misc/NEWS.d/next/Library/2020-06-15-00-13-57.bpo-40967._dx3OO.rst
M Doc/library/asyncio-task.rst
M Doc/whatsnew/3.9.rst
M Lib/asyncio/tasks.py
M Lib/test/test_asyncio/test_tasks.py
M Modules/_asynciomodule.c
M Modules/clinic/_asynciomodule.c.h
diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst
index 21824ca537f77..bac99b7950444 100644
--- a/Doc/library/asyncio-task.rst
+++ b/Doc/library/asyncio-task.rst
@@ -962,31 +962,6 @@ Task Object
=20
.. versionadded:: 3.8
=20
- .. classmethod:: all_tasks(loop=3DNone)
-
- Return a set of all tasks for an event loop.
-
- By default all tasks for the current event loop are returned.
- If *loop* is ``None``, the :func:`get_event_loop` function
- is used to get the current loop.
-
- .. deprecated-removed:: 3.7 3.9
-
- Do not call this as a task method. Use the :func:`asyncio.all_tasks`
- function instead.
-
- .. classmethod:: current_task(loop=3DNone)
-
- Return the currently running task or ``None``.
-
- If *loop* is ``None``, the :func:`get_event_loop` function
- is used to get the current loop.
-
- .. deprecated-removed:: 3.7 3.9
-
- Do not call this as a task method. Use the
- :func:`asyncio.current_task` function instead.
-
=20
.. _asyncio_generator_based_coro:
=20
diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst
index 15fca8fa9d4c9..165ce69e59e1d 100644
--- a/Doc/whatsnew/3.9.rst
+++ b/Doc/whatsnew/3.9.rst
@@ -878,6 +878,11 @@ Removed
deprecated since 2006, and only returning ``False`` when it's called.
(Contributed by Batuhan Taskaya in :issue:`40208`)
=20
+* The :meth:`asyncio.Task.current_task` and :meth:`asyncio.Task.all_tasks` h=
ave
+ have been removed. They were deprecated since Python 3.7 and you can use
+ :func:`asyncio.current_task` and :func:`asyncio.all_tasks` instead.
+ (Contributed by R=C3=A9mi Lapeyre in :issue:`40967`)
+
=20
Porting to Python 3.9
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py
index 21b98b6647bd9..5e0692ef7778a 100644
--- a/Lib/asyncio/tasks.py
+++ b/Lib/asyncio/tasks.py
@@ -113,34 +113,6 @@ class Task(futures._PyFuture): # Inherit Python Task im=
plementation
# status is still pending
_log_destroy_pending =3D True
=20
- @classmethod
- def current_task(cls, loop=3DNone):
- """Return the currently running task in an event loop or None.
-
- By default the current task for the current event loop is returned.
-
- None is returned when called not in the context of a Task.
- """
- warnings.warn("Task.current_task() is deprecated since Python 3.7, "
- "use asyncio.current_task() instead",
- DeprecationWarning,
- stacklevel=3D2)
- if loop is None:
- loop =3D events.get_event_loop()
- return current_task(loop)
-
- @classmethod
- def all_tasks(cls, loop=3DNone):
- """Return a set of all tasks for an event loop.
-
- By default all tasks for the current event loop are returned.
- """
- warnings.warn("Task.all_tasks() is deprecated since Python 3.7, "
- "use asyncio.all_tasks() instead",
- DeprecationWarning,
- stacklevel=3D2)
- return _all_tasks_compat(loop)
-
def __init__(self, coro, *, loop=3DNone, name=3DNone):
super().__init__(loop=3Dloop)
if self._source_traceback:
diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test=
_tasks.py
index 3734013fad989..f9db066ce8983 100644
--- a/Lib/test/test_asyncio/test_tasks.py
+++ b/Lib/test/test_asyncio/test_tasks.py
@@ -1943,32 +1943,6 @@ async def coro():
self.assertEqual(res, 'test')
self.assertIsNone(t2.result())
=20
-
- def test_current_task_deprecated(self):
- Task =3D self.__class__.Task
-
- with self.assertWarns(DeprecationWarning):
- self.assertIsNone(Task.current_task(loop=3Dself.loop))
-
- async def coro(loop):
- with self.assertWarns(DeprecationWarning):
- self.assertIs(Task.current_task(loop=3Dloop), task)
-
- # See http://bugs.python.org/issue29271 for details:
- asyncio.set_event_loop(loop)
- try:
- with self.assertWarns(DeprecationWarning):
- self.assertIs(Task.current_task(None), task)
- with self.assertWarns(DeprecationWarning):
- self.assertIs(Task.current_task(), task)
- finally:
- asyncio.set_event_loop(None)
-
- task =3D self.new_task(self.loop, coro(self.loop))
- self.loop.run_until_complete(task)
- with self.assertWarns(DeprecationWarning):
- self.assertIsNone(Task.current_task(loop=3Dself.loop))
-
def test_current_task(self):
self.assertIsNone(asyncio.current_task(loop=3Dself.loop))
=20
@@ -2305,16 +2279,6 @@ def foo():
self.assertIsInstance(exception, Exception)
self.assertEqual(exception.args, ("foo", ))
=20
- def test_all_tasks_deprecated(self):
- Task =3D self.__class__.Task
-
- async def coro():
- with self.assertWarns(DeprecationWarning):
- assert Task.all_tasks(self.loop) =3D=3D {t}
-
- t =3D self.new_task(self.loop, coro())
- self.loop.run_until_complete(t)
-
def test_log_destroyed_pending_task(self):
Task =3D self.__class__.Task
=20
@@ -2337,15 +2301,7 @@ def kill_me(loop):
=20
self.assertEqual(asyncio.all_tasks(loop=3Dself.loop), {task})
=20
- # See http://bugs.python.org/issue29271 for details:
- asyncio.set_event_loop(self.loop)
- try:
- with self.assertWarns(DeprecationWarning):
- self.assertEqual(Task.all_tasks(), {task})
- with self.assertWarns(DeprecationWarning):
- self.assertEqual(Task.all_tasks(None), {task})
- finally:
- asyncio.set_event_loop(None)
+ asyncio.set_event_loop(None)
=20
# execute the task so it waits for future
self.loop._run_once()
@@ -3043,8 +2999,6 @@ def done(self):
self.assertEqual(asyncio.all_tasks(loop), set())
self._register_task(task)
self.assertEqual(asyncio.all_tasks(loop), set())
- with self.assertWarns(DeprecationWarning):
- self.assertEqual(asyncio.Task.all_tasks(loop), {task})
self._unregister_task(task)
=20
def test__enter_task(self):
diff --git a/Misc/NEWS.d/next/Library/2020-06-15-00-13-57.bpo-40967._dx3OO.rs=
t b/Misc/NEWS.d/next/Library/2020-06-15-00-13-57.bpo-40967._dx3OO.rst
new file mode 100644
index 0000000000000..4694d991babd7
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-06-15-00-13-57.bpo-40967._dx3OO.rst
@@ -0,0 +1,2 @@
+Removed :meth:`asyncio.Task.current_task` and
+:meth:`asyncio.Task.all_tasks`. Patch contributed by R=C3=A9mi Lapeyre.
diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c
index 0454f9c6824bf..b378742648b2a 100644
--- a/Modules/_asynciomodule.c
+++ b/Modules/_asynciomodule.c
@@ -13,10 +13,8 @@ module _asyncio
_Py_IDENTIFIER(__asyncio_running_event_loop__);
_Py_IDENTIFIER(_asyncio_future_blocking);
_Py_IDENTIFIER(add_done_callback);
-_Py_IDENTIFIER(_all_tasks_compat);
_Py_IDENTIFIER(call_soon);
_Py_IDENTIFIER(cancel);
-_Py_IDENTIFIER(current_task);
_Py_IDENTIFIER(get_event_loop);
_Py_IDENTIFIER(send);
_Py_IDENTIFIER(throw);
@@ -2182,91 +2180,6 @@ TaskObj_get_fut_waiter(TaskObj *task, void *Py_UNUSED(=
ignored))
Py_RETURN_NONE;
}
=20
-/*[clinic input]
-@classmethod
-_asyncio.Task.current_task
-
- loop: object =3D None
-
-Return the currently running task in an event loop or None.
-
-By default the current task for the current event loop is returned.
-
-None is returned when called not in the context of a Task.
-[clinic start generated code]*/
-
-static PyObject *
-_asyncio_Task_current_task_impl(PyTypeObject *type, PyObject *loop)
-/*[clinic end generated code: output=3D99fbe7332c516e03 input=3Dcd14770c5b79=
c7eb]*/
-{
- PyObject *ret;
- PyObject *current_task_func;
-
- if (PyErr_WarnEx(PyExc_DeprecationWarning,
- "Task.current_task() is deprecated, " \
- "use asyncio.current_task() instead",
- 1) < 0) {
- return NULL;
- }
-
- current_task_func =3D _PyObject_GetAttrId(asyncio_mod, &PyId_current_tas=
k);
- if (current_task_func =3D=3D NULL) {
- return NULL;
- }
-
- if (loop =3D=3D Py_None) {
- loop =3D get_event_loop();
- if (loop =3D=3D NULL) {
- Py_DECREF(current_task_func);
- return NULL;
- }
- ret =3D PyObject_CallOneArg(current_task_func, loop);
- Py_DECREF(current_task_func);
- Py_DECREF(loop);
- return ret;
- }
- else {
- ret =3D PyObject_CallOneArg(current_task_func, loop);
- Py_DECREF(current_task_func);
- return ret;
- }
-}
-
-/*[clinic input]
-@classmethod
-_asyncio.Task.all_tasks
-
- loop: object =3D None
-
-Return a set of all tasks for an event loop.
-
-By default all tasks for the current event loop are returned.
-[clinic start generated code]*/
-
-static PyObject *
-_asyncio_Task_all_tasks_impl(PyTypeObject *type, PyObject *loop)
-/*[clinic end generated code: output=3D11f9b20749ccca5d input=3D497f80bc9ce7=
26b5]*/
-{
- PyObject *res;
- PyObject *all_tasks_func;
-
- if (PyErr_WarnEx(PyExc_DeprecationWarning,
- "Task.all_tasks() is deprecated, " \
- "use asyncio.all_tasks() instead",
- 1) < 0) {
- return NULL;
- }
-
- all_tasks_func =3D _PyObject_GetAttrId(asyncio_mod, &PyId__all_tasks_com=
pat);
- if (all_tasks_func =3D=3D NULL) {
- return NULL;
- }
-
- res =3D PyObject_CallOneArg(all_tasks_func, loop);
- Py_DECREF(all_tasks_func);
- return res;
-}
-
/*[clinic input]
_asyncio.Task._make_cancelled_error
=20
@@ -2587,8 +2500,6 @@ static PyMethodDef TaskType_methods[] =3D {
_ASYNCIO_FUTURE_DONE_METHODDEF
_ASYNCIO_TASK_SET_RESULT_METHODDEF
_ASYNCIO_TASK_SET_EXCEPTION_METHODDEF
- _ASYNCIO_TASK_CURRENT_TASK_METHODDEF
- _ASYNCIO_TASK_ALL_TASKS_METHODDEF
_ASYNCIO_TASK_CANCEL_METHODDEF
_ASYNCIO_TASK_GET_STACK_METHODDEF
_ASYNCIO_TASK_PRINT_STACK_METHODDEF
diff --git a/Modules/clinic/_asynciomodule.c.h b/Modules/clinic/_asynciomodul=
e.c.h
index d3e59a4bc7822..a071efc1e2be3 100644
--- a/Modules/clinic/_asynciomodule.c.h
+++ b/Modules/clinic/_asynciomodule.c.h
@@ -355,86 +355,6 @@ _asyncio_Task___init__(PyObject *self, PyObject *args, P=
yObject *kwargs)
return return_value;
}
=20
-PyDoc_STRVAR(_asyncio_Task_current_task__doc__,
-"current_task($type, /, loop=3DNone)\n"
-"--\n"
-"\n"
-"Return the currently running task in an event loop or None.\n"
-"\n"
-"By default the current task for the current event loop is returned.\n"
-"\n"
-"None is returned when called not in the context of a Task.");
-
-#define _ASYNCIO_TASK_CURRENT_TASK_METHODDEF \
- {"current_task", (PyCFunction)(void(*)(void))_asyncio_Task_current_task,=
METH_FASTCALL|METH_KEYWORDS|METH_CLASS, _asyncio_Task_current_task__doc__},
-
-static PyObject *
-_asyncio_Task_current_task_impl(PyTypeObject *type, PyObject *loop);
-
-static PyObject *
-_asyncio_Task_current_task(PyTypeObject *type, PyObject *const *args, Py_ssi=
ze_t nargs, PyObject *kwnames)
-{
- PyObject *return_value =3D NULL;
- static const char * const _keywords[] =3D {"loop", NULL};
- static _PyArg_Parser _parser =3D {NULL, _keywords, "current_task", 0};
- PyObject *argsbuf[1];
- Py_ssize_t noptargs =3D nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0=
) - 0;
- PyObject *loop =3D Py_None;
-
- args =3D _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, =
1, 0, argsbuf);
- if (!args) {
- goto exit;
- }
- if (!noptargs) {
- goto skip_optional_pos;
- }
- loop =3D args[0];
-skip_optional_pos:
- return_value =3D _asyncio_Task_current_task_impl(type, loop);
-
-exit:
- return return_value;
-}
-
-PyDoc_STRVAR(_asyncio_Task_all_tasks__doc__,
-"all_tasks($type, /, loop=3DNone)\n"
-"--\n"
-"\n"
-"Return a set of all tasks for an event loop.\n"
-"\n"
-"By default all tasks for the current event loop are returned.");
-
-#define _ASYNCIO_TASK_ALL_TASKS_METHODDEF \
- {"all_tasks", (PyCFunction)(void(*)(void))_asyncio_Task_all_tasks, METH_=
FASTCALL|METH_KEYWORDS|METH_CLASS, _asyncio_Task_all_tasks__doc__},
-
-static PyObject *
-_asyncio_Task_all_tasks_impl(PyTypeObject *type, PyObject *loop);
-
-static PyObject *
-_asyncio_Task_all_tasks(PyTypeObject *type, PyObject *const *args, Py_ssize_=
t nargs, PyObject *kwnames)
-{
- PyObject *return_value =3D NULL;
- static const char * const _keywords[] =3D {"loop", NULL};
- static _PyArg_Parser _parser =3D {NULL, _keywords, "all_tasks", 0};
- PyObject *argsbuf[1];
- Py_ssize_t noptargs =3D nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0=
) - 0;
- PyObject *loop =3D Py_None;
-
- args =3D _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, =
1, 0, argsbuf);
- if (!args) {
- goto exit;
- }
- if (!noptargs) {
- goto skip_optional_pos;
- }
- loop =3D args[0];
-skip_optional_pos:
- return_value =3D _asyncio_Task_all_tasks_impl(type, loop);
-
-exit:
- return return_value;
-}
-
PyDoc_STRVAR(_asyncio_Task__make_cancelled_error__doc__,
"_make_cancelled_error($self, /)\n"
"--\n"
@@ -912,4 +832,4 @@ _asyncio__leave_task(PyObject *module, PyObject *const *a=
rgs, Py_ssize_t nargs,
exit:
return return_value;
}
-/*[clinic end generated code: output=3D0e5c1eb8b692977b input=3Da9049054013a=
1b77]*/
+/*[clinic end generated code: output=3Dd0fc522bcbff9d61 input=3Da9049054013a=
1b77]*/
1
0
July 1, 2020
https://github.com/python/cpython/commit/666ecfb0957a2fa0df5e2bd03804195de7…
commit: 666ecfb0957a2fa0df5e2bd03804195de74bdfbf
branch: master
author: Victor Stinner <vstinner(a)python.org>
committer: GitHub <noreply(a)github.com>
date: 2020-07-02T01:19:57+02:00
summary:
bpo-1635741: Release Unicode interned strings at exit (GH-21269)
* PyUnicode_InternInPlace() now ensures that interned strings are
ready.
* Add _PyUnicode_ClearInterned().
* Py_Finalize() now releases Unicode interned strings:
call _PyUnicode_ClearInterned().
files:
M Include/internal/pycore_pylifecycle.h
M Objects/unicodeobject.c
M Python/pylifecycle.c
diff --git a/Include/internal/pycore_pylifecycle.h b/Include/internal/pycore_pylifecycle.h
index bffc95b27e946..22def3dbc8b66 100644
--- a/Include/internal/pycore_pylifecycle.h
+++ b/Include/internal/pycore_pylifecycle.h
@@ -78,6 +78,7 @@ extern void _PyGC_Fini(PyThreadState *tstate);
extern void _PyType_Fini(void);
extern void _Py_HashRandomization_Fini(void);
extern void _PyUnicode_Fini(PyThreadState *tstate);
+extern void _PyUnicode_ClearInterned(PyThreadState *tstate);
extern void _PyLong_Fini(PyThreadState *tstate);
extern void _PyFaulthandler_Fini(void);
extern void _PyHash_Fini(void);
diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c
index fe46de2ae4743..37e7fe5c0eff2 100644
--- a/Objects/unicodeobject.c
+++ b/Objects/unicodeobject.c
@@ -55,8 +55,8 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <windows.h>
#endif
-/* Uncomment to display statistics on interned strings at exit when
- using Valgrind or Insecure++. */
+/* Uncomment to display statistics on interned strings at exit
+ in _PyUnicode_ClearInterned(). */
/* #define INTERNED_STATS 1 */
@@ -15681,6 +15681,11 @@ PyUnicode_InternInPlace(PyObject **p)
}
#ifdef INTERNED_STRINGS
+ if (PyUnicode_READY(s) == -1) {
+ PyErr_Clear();
+ return;
+ }
+
if (interned == NULL) {
interned = PyDict_New();
if (interned == NULL) {
@@ -15733,23 +15738,29 @@ PyUnicode_InternFromString(const char *cp)
}
-#if defined(WITH_VALGRIND) || defined(__INSURE__)
-static void
-unicode_release_interned(void)
+void
+_PyUnicode_ClearInterned(PyThreadState *tstate)
{
- if (interned == NULL || !PyDict_Check(interned)) {
+ if (!_Py_IsMainInterpreter(tstate)) {
+ // interned dict is shared by all interpreters
+ return;
+ }
+
+ if (interned == NULL) {
return;
}
+ assert(PyDict_CheckExact(interned));
+
PyObject *keys = PyDict_Keys(interned);
- if (keys == NULL || !PyList_Check(keys)) {
+ if (keys == NULL) {
PyErr_Clear();
return;
}
+ assert(PyList_CheckExact(keys));
- /* Since unicode_release_interned() is intended to help a leak
- detector, interned unicode strings are not forcibly deallocated;
- rather, we give them their stolen references back, and then clear
- and DECREF the interned dict. */
+ /* Interned unicode strings are not forcibly deallocated; rather, we give
+ them their stolen references back, and then clear and DECREF the
+ interned dict. */
Py_ssize_t n = PyList_GET_SIZE(keys);
#ifdef INTERNED_STATS
@@ -15759,9 +15770,8 @@ unicode_release_interned(void)
#endif
for (Py_ssize_t i = 0; i < n; i++) {
PyObject *s = PyList_GET_ITEM(keys, i);
- if (PyUnicode_READY(s) == -1) {
- Py_UNREACHABLE();
- }
+ assert(PyUnicode_IS_READY(s));
+
switch (PyUnicode_CHECK_INTERNED(s)) {
case SSTATE_INTERNED_IMMORTAL:
Py_SET_REFCNT(s, Py_REFCNT(s) + 1);
@@ -15788,10 +15798,10 @@ unicode_release_interned(void)
mortal_size, immortal_size);
#endif
Py_DECREF(keys);
+
PyDict_Clear(interned);
Py_CLEAR(interned);
}
-#endif
/********************* Unicode Iterator **************************/
@@ -16160,23 +16170,9 @@ _PyUnicode_EnableLegacyWindowsFSEncoding(void)
void
_PyUnicode_Fini(PyThreadState *tstate)
{
- struct _Py_unicode_state *state = &tstate->interp->unicode;
+ // _PyUnicode_ClearInterned() must be called before
- int is_main_interp = _Py_IsMainInterpreter(tstate);
- if (is_main_interp) {
-#if defined(WITH_VALGRIND) || defined(__INSURE__)
- /* Insure++ is a memory analysis tool that aids in discovering
- * memory leaks and other memory problems. On Python exit, the
- * interned string dictionaries are flagged as being in use at exit
- * (which it is). Under normal circumstances, this is fine because
- * the memory will be automatically reclaimed by the system. Under
- * memory debugging, it's a huge source of useless noise, so we
- * trade off slower shutdown for less distraction in the memory
- * reports. -baw
- */
- unicode_release_interned();
-#endif /* __INSURE__ */
- }
+ struct _Py_unicode_state *state = &tstate->interp->unicode;
Py_CLEAR(state->empty_string);
@@ -16184,7 +16180,7 @@ _PyUnicode_Fini(PyThreadState *tstate)
Py_CLEAR(state->latin1[i]);
}
- if (is_main_interp) {
+ if (_Py_IsMainInterpreter(tstate)) {
unicode_clear_static_strings();
}
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index cfbaf21960b91..3ce2c41ef1ff6 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -1263,6 +1263,7 @@ finalize_interp_types(PyThreadState *tstate)
_PyFrame_Fini(tstate);
_PyAsyncGen_Fini(tstate);
_PyContext_Fini(tstate);
+ _PyUnicode_ClearInterned(tstate);
_PyDict_Fini(tstate);
_PyList_Fini(tstate);
1
0
https://github.com/python/cpython/commit/90db4653ae37ef90754cfd2cd6ec6857b8…
commit: 90db4653ae37ef90754cfd2cd6ec6857b87a88e6
branch: master
author: Victor Stinner <vstinner(a)python.org>
committer: GitHub <noreply(a)github.com>
date: 2020-07-01T23:21:36+02:00
summary:
bpo-40521: Cleanup finalize_interp_types() (GH-21265)
Remove the now unused is_main_interp parameter of
finalize_interp_types().
files:
M Python/pylifecycle.c
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index cd993ea13418f..cfbaf21960b91 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -1257,7 +1257,7 @@ flush_std_files(void)
static void
-finalize_interp_types(PyThreadState *tstate, int is_main_interp)
+finalize_interp_types(PyThreadState *tstate)
{
_PyExc_Fini(tstate);
_PyFrame_Fini(tstate);
@@ -1300,7 +1300,7 @@ finalize_interp_clear(PyThreadState *tstate)
_PyWarnings_Fini(tstate->interp);
- finalize_interp_types(tstate, is_main_interp);
+ finalize_interp_types(tstate);
}
1
0
bpo-39385: Add an assertNoLogs context manager to unittest.TestCase (GH-18067)
by Kit Choi July 1, 2020
by Kit Choi July 1, 2020
July 1, 2020
https://github.com/python/cpython/commit/6b34d7b51e33fcb21b8827d927474ce9ed…
commit: 6b34d7b51e33fcb21b8827d927474ce9ed1f605c
branch: master
author: Kit Choi <kitchoi(a)users.noreply.github.com>
committer: GitHub <noreply(a)github.com>
date: 2020-07-01T22:08:38+01:00
summary:
bpo-39385: Add an assertNoLogs context manager to unittest.TestCase (GH-18067)
Co-authored-by: Rémi Lapeyre <remi.lapeyre(a)henki.fr>
files:
A Misc/NEWS.d/next/Library/2020-04-23-18-21-19.bpo-39385.MIAyS7.rst
M Doc/library/unittest.rst
M Lib/unittest/_log.py
M Lib/unittest/case.py
M Lib/unittest/test/test_case.py
diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst
index b2e16cf331e03..0dddbd25d991b 100644
--- a/Doc/library/unittest.rst
+++ b/Doc/library/unittest.rst
@@ -950,6 +950,9 @@ Test cases
| :meth:`assertLogs(logger, level) | The ``with`` block logs on *logger* | 3.4 |
| <TestCase.assertLogs>` | with minimum *level* | |
+---------------------------------------------------------+--------------------------------------+------------+
+ | :meth:`assertNoLogs(logger, level) | The ``with`` block does not log on | 3.10 |
+ | <TestCase.assertNoLogs>` | *logger* with minimum *level* | |
+ +---------------------------------------------------------+--------------------------------------+------------+
.. method:: assertRaises(exception, callable, *args, **kwds)
assertRaises(exception, *, msg=None)
@@ -1121,6 +1124,24 @@ Test cases
.. versionadded:: 3.4
+ .. method:: assertNoLogs(logger=None, level=None)
+
+ A context manager to test that no messages are logged on
+ the *logger* or one of its children, with at least the given
+ *level*.
+
+ If given, *logger* should be a :class:`logging.Logger` object or a
+ :class:`str` giving the name of a logger. The default is the root
+ logger, which will catch all messages.
+
+ If given, *level* should be either a numeric logging level or
+ its string equivalent (for example either ``"ERROR"`` or
+ :attr:`logging.ERROR`). The default is :attr:`logging.INFO`.
+
+ Unlike :meth:`assertLogs`, nothing will be returned by the context
+ manager.
+
+ .. versionadded:: 3.10
There are also other methods used to perform more specific checks, such as:
diff --git a/Lib/unittest/_log.py b/Lib/unittest/_log.py
index 94e7e758bd9a0..961c448a7fb35 100644
--- a/Lib/unittest/_log.py
+++ b/Lib/unittest/_log.py
@@ -26,11 +26,11 @@ def emit(self, record):
class _AssertLogsContext(_BaseTestCaseContext):
- """A context manager used to implement TestCase.assertLogs()."""
+ """A context manager for assertLogs() and assertNoLogs() """
LOGGING_FORMAT = "%(levelname)s:%(name)s:%(message)s"
- def __init__(self, test_case, logger_name, level):
+ def __init__(self, test_case, logger_name, level, no_logs):
_BaseTestCaseContext.__init__(self, test_case)
self.logger_name = logger_name
if level:
@@ -38,6 +38,7 @@ def __init__(self, test_case, logger_name, level):
else:
self.level = logging.INFO
self.msg = None
+ self.no_logs = no_logs
def __enter__(self):
if isinstance(self.logger_name, logging.Logger):
@@ -54,16 +55,31 @@ def __enter__(self):
logger.handlers = [handler]
logger.setLevel(self.level)
logger.propagate = False
+ if self.no_logs:
+ return
return handler.watcher
def __exit__(self, exc_type, exc_value, tb):
self.logger.handlers = self.old_handlers
self.logger.propagate = self.old_propagate
self.logger.setLevel(self.old_level)
+
if exc_type is not None:
# let unexpected exceptions pass through
return False
- if len(self.watcher.records) == 0:
- self._raiseFailure(
- "no logs of level {} or higher triggered on {}"
- .format(logging.getLevelName(self.level), self.logger.name))
+
+ if self.no_logs:
+ # assertNoLogs
+ if len(self.watcher.records) > 0:
+ self._raiseFailure(
+ "Unexpected logs found: {!r}".format(
+ self.watcher.output
+ )
+ )
+
+ else:
+ # assertLogs
+ if len(self.watcher.records) == 0:
+ self._raiseFailure(
+ "no logs of level {} or higher triggered on {}"
+ .format(logging.getLevelName(self.level), self.logger.name))
diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py
index 52eb7d05ed143..872f12112755e 100644
--- a/Lib/unittest/case.py
+++ b/Lib/unittest/case.py
@@ -295,7 +295,6 @@ def __exit__(self, exc_type, exc_value, tb):
self._raiseFailure("{} not triggered".format(exc_name))
-
class _OrderedChainMap(collections.ChainMap):
def __iter__(self):
seen = set()
@@ -788,7 +787,16 @@ def assertLogs(self, logger=None, level=None):
"""
# Lazy import to avoid importing logging if it is not needed.
from ._log import _AssertLogsContext
- return _AssertLogsContext(self, logger, level)
+ return _AssertLogsContext(self, logger, level, no_logs=False)
+
+ def assertNoLogs(self, logger=None, level=None):
+ """ Fail unless no log messages of level *level* or higher are emitted
+ on *logger_name* or its children.
+
+ This method must be used as a context manager.
+ """
+ from ._log import _AssertLogsContext
+ return _AssertLogsContext(self, logger, level, no_logs=True)
def _getAssertEqualityFunc(self, first, second):
"""Get a detailed comparison function for the types of the two args.
diff --git a/Lib/unittest/test/test_case.py b/Lib/unittest/test/test_case.py
index 3dedcbe6aad5f..0e416967a3086 100644
--- a/Lib/unittest/test/test_case.py
+++ b/Lib/unittest/test/test_case.py
@@ -1681,6 +1681,81 @@ def testAssertLogsFailureMismatchingLogger(self):
with self.assertLogs('foo'):
log_quux.error("1")
+ def testAssertLogsUnexpectedException(self):
+ # Check unexpected exception will go through.
+ with self.assertRaises(ZeroDivisionError):
+ with self.assertLogs():
+ raise ZeroDivisionError("Unexpected")
+
+ def testAssertNoLogsDefault(self):
+ with self.assertRaises(self.failureException) as cm:
+ with self.assertNoLogs():
+ log_foo.info("1")
+ log_foobar.debug("2")
+ self.assertEqual(
+ str(cm.exception),
+ "Unexpected logs found: ['INFO:foo:1']",
+ )
+
+ def testAssertNoLogsFailureFoundLogs(self):
+ with self.assertRaises(self.failureException) as cm:
+ with self.assertNoLogs():
+ log_quux.error("1")
+ log_foo.error("foo")
+
+ self.assertEqual(
+ str(cm.exception),
+ "Unexpected logs found: ['ERROR:quux:1', 'ERROR:foo:foo']",
+ )
+
+ def testAssertNoLogsPerLogger(self):
+ with self.assertNoStderr():
+ with self.assertLogs(log_quux):
+ with self.assertNoLogs(logger=log_foo):
+ log_quux.error("1")
+
+ def testAssertNoLogsFailurePerLogger(self):
+ # Failure due to unexpected logs for the given logger or its
+ # children.
+ with self.assertRaises(self.failureException) as cm:
+ with self.assertLogs(log_quux):
+ with self.assertNoLogs(logger=log_foo):
+ log_quux.error("1")
+ log_foobar.info("2")
+ self.assertEqual(
+ str(cm.exception),
+ "Unexpected logs found: ['INFO:foo.bar:2']",
+ )
+
+ def testAssertNoLogsPerLevel(self):
+ # Check per-level filtering
+ with self.assertNoStderr():
+ with self.assertNoLogs(level="ERROR"):
+ log_foo.info("foo")
+ log_quux.debug("1")
+
+ def testAssertNoLogsFailurePerLevel(self):
+ # Failure due to unexpected logs at the specified level.
+ with self.assertRaises(self.failureException) as cm:
+ with self.assertNoLogs(level="DEBUG"):
+ log_foo.debug("foo")
+ log_quux.debug("1")
+ self.assertEqual(
+ str(cm.exception),
+ "Unexpected logs found: ['DEBUG:foo:foo', 'DEBUG:quux:1']",
+ )
+
+ def testAssertNoLogsUnexpectedException(self):
+ # Check unexpected exception will go through.
+ with self.assertRaises(ZeroDivisionError):
+ with self.assertNoLogs():
+ raise ZeroDivisionError("Unexpected")
+
+ def testAssertNoLogsYieldsNone(self):
+ with self.assertNoLogs() as value:
+ pass
+ self.assertIsNone(value)
+
def testDeprecatedMethodNames(self):
"""
Test that the deprecated methods raise a DeprecationWarning. See #9424.
diff --git a/Misc/NEWS.d/next/Library/2020-04-23-18-21-19.bpo-39385.MIAyS7.rst b/Misc/NEWS.d/next/Library/2020-04-23-18-21-19.bpo-39385.MIAyS7.rst
new file mode 100644
index 0000000000000..e6c5c0dd4380b
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-04-23-18-21-19.bpo-39385.MIAyS7.rst
@@ -0,0 +1,3 @@
+A new test assertion context-manager, :func:`unittest.assertNoLogs` will
+ensure a given block of code emits no log messages using the logging module.
+Contributed by Kit Yan Choi.
1
0
July 1, 2020
https://github.com/python/cpython/commit/5d5c84ef78b19211671c2bfa68fe073485…
commit: 5d5c84ef78b19211671c2bfa68fe073485135eed
branch: master
author: Serhiy Storchaka <storchaka(a)gmail.com>
committer: GitHub <noreply(a)github.com>
date: 2020-07-01T21:53:07+03:00
summary:
bpo-41187: Convert the _msi module to Argument Clinic (GH-21264)
files:
A PC/clinic/_msi.c.h
M PC/_msi.c
diff --git a/PC/_msi.c b/PC/_msi.c
index 60a0c3aebb1e7..9f1015845acff 100644
--- a/PC/_msi.c
+++ b/PC/_msi.c
@@ -12,10 +12,26 @@
#include <msidefs.h>
#include <rpc.h>
+/*[clinic input]
+module _msi
+class _msi.Record "msiobj *" "&record_Type"
+class _msi.SummaryInformation "msiobj *" "&summary_Type"
+class _msi.View "msiobj *" "&msiview_Type"
+class _msi.Database "msiobj *" "&msidb_Type"
+[clinic start generated code]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=89a3605762cf4bdc]*/
+
static PyObject *MSIError;
-static PyObject*
-uuidcreate(PyObject* obj, PyObject*args)
+/*[clinic input]
+_msi.UuidCreate
+
+Return the string representation of a new unique identifier.
+[clinic start generated code]*/
+
+static PyObject *
+_msi_UuidCreate_impl(PyObject *module)
+/*[clinic end generated code: output=534ecf36f10af98e input=168024ab4b3e832b]*/
{
UUID result;
wchar_t *cresult;
@@ -225,19 +241,28 @@ static FNFCIGETOPENINFO(cb_getopeninfo)
return result;
}
-static PyObject* fcicreate(PyObject* obj, PyObject* args)
+/*[clinic input]
+_msi.FCICreate
+ cabname: str
+ the name of the CAB file
+ files: object
+ a list of tuples, each containing the name of the file on disk,
+ and the name of the file inside the CAB file
+ /
+
+Create a new CAB file.
+[clinic start generated code]*/
+
+static PyObject *
+_msi_FCICreate_impl(PyObject *module, const char *cabname, PyObject *files)
+/*[clinic end generated code: output=55dc05728361b799 input=1d2d75fdc8b44b71]*/
{
- char *cabname, *p;
- PyObject *files;
+ const char *p;
CCAB ccab;
HFCI hfci;
ERF erf;
Py_ssize_t i;
-
- if (!PyArg_ParseTuple(args, "sO:FCICreate", &cabname, &files))
- return NULL;
-
if (!PyList_Check(files)) {
PyErr_SetString(PyExc_TypeError, "FCICreate expects a list");
return NULL;
@@ -387,34 +412,56 @@ msierror(int status)
return NULL;
}
-static PyObject*
-msidb_close(msiobj* msidb, PyObject *args)
+#include "clinic/_msi.c.h"
+
+/*[clinic input]
+_msi.Database.Close
+
+Close the database object.
+[clinic start generated code]*/
+
+static PyObject *
+_msi_Database_Close_impl(msiobj *self)
+/*[clinic end generated code: output=ddf2d7712ea804f1 input=104330ce4a486187]*/
{
int status;
- if ((status = MsiCloseHandle(msidb->h)) != ERROR_SUCCESS) {
+ if ((status = MsiCloseHandle(self->h)) != ERROR_SUCCESS) {
return msierror(status);
}
- msidb->h = 0;
+ self->h = 0;
Py_RETURN_NONE;
}
/*************************** Record objects **********************/
-static PyObject*
-record_getfieldcount(msiobj* record, PyObject* args)
+/*[clinic input]
+_msi.Record.GetFieldCount
+
+Return the number of fields of the record.
+[clinic start generated code]*/
+
+static PyObject *
+_msi_Record_GetFieldCount_impl(msiobj *self)
+/*[clinic end generated code: output=112795079c904398 input=5fb9d4071b28897b]*/
{
- return PyLong_FromLong(MsiRecordGetFieldCount(record->h));
+ return PyLong_FromLong(MsiRecordGetFieldCount(self->h));
}
-static PyObject*
-record_getinteger(msiobj* record, PyObject* args)
+/*[clinic input]
+_msi.Record.GetInteger
+ field: unsigned_int(bitwise=True)
+ /
+
+Return the value of field as an integer where possible.
+[clinic start generated code]*/
+
+static PyObject *
+_msi_Record_GetInteger_impl(msiobj *self, unsigned int field)
+/*[clinic end generated code: output=7174ebb6e8ed1c79 input=d19209947e2bfe61]*/
{
- unsigned int field;
int status;
- if (!PyArg_ParseTuple(args, "I:GetInteger", &field))
- return NULL;
- status = MsiRecordGetInteger(record->h, field);
+ status = MsiRecordGetInteger(self->h, field);
if (status == MSI_NULL_INTEGER){
PyErr_SetString(MSIError, "could not convert record field to integer");
return NULL;
@@ -422,24 +469,30 @@ record_getinteger(msiobj* record, PyObject* args)
return PyLong_FromLong((long) status);
}
-static PyObject*
-record_getstring(msiobj* record, PyObject* args)
+/*[clinic input]
+_msi.Record.GetString
+ field: unsigned_int(bitwise=True)
+ /
+
+Return the value of field as a string where possible.
+[clinic start generated code]*/
+
+static PyObject *
+_msi_Record_GetString_impl(msiobj *self, unsigned int field)
+/*[clinic end generated code: output=f670d1b484cfa47c input=ffa11f21450b77d8]*/
{
- unsigned int field;
unsigned int status;
WCHAR buf[2000];
WCHAR *res = buf;
DWORD size = sizeof(buf);
PyObject* string;
- if (!PyArg_ParseTuple(args, "I:GetString", &field))
- return NULL;
- status = MsiRecordGetStringW(record->h, field, res, &size);
+ status = MsiRecordGetStringW(self->h, field, res, &size);
if (status == ERROR_MORE_DATA) {
res = (WCHAR*) malloc((size + 1)*sizeof(WCHAR));
if (res == NULL)
return PyErr_NoMemory();
- status = MsiRecordGetStringW(record->h, field, res, &size);
+ status = MsiRecordGetStringW(self->h, field, res, &size);
}
if (status != ERROR_SUCCESS)
return msierror((int) status);
@@ -449,59 +502,81 @@ record_getstring(msiobj* record, PyObject* args)
return string;
}
-static PyObject*
-record_cleardata(msiobj* record, PyObject *args)
+/*[clinic input]
+_msi.Record.ClearData
+
+Set all fields of the record to 0.
+[clinic start generated code]*/
+
+static PyObject *
+_msi_Record_ClearData_impl(msiobj *self)
+/*[clinic end generated code: output=1891467214b977f4 input=2a911c95aaded102]*/
{
- int status = MsiRecordClearData(record->h);
+ int status = MsiRecordClearData(self->h);
if (status != ERROR_SUCCESS)
return msierror(status);
Py_RETURN_NONE;
}
-static PyObject*
-record_setstring(msiobj* record, PyObject *args)
+/*[clinic input]
+_msi.Record.SetString
+ field: int
+ value: Py_UNICODE
+ /
+
+Set field to a string value.
+[clinic start generated code]*/
+
+static PyObject *
+_msi_Record_SetString_impl(msiobj *self, int field, const Py_UNICODE *value)
+/*[clinic end generated code: output=2e37505b0f11f985 input=fb8ec70a2a6148e0]*/
{
int status;
- int field;
- wchar_t *data;
-
- if (!PyArg_ParseTuple(args, "iu:SetString", &field, &data))
- return NULL;
- if ((status = MsiRecordSetStringW(record->h, field, data)) != ERROR_SUCCESS)
+ if ((status = MsiRecordSetStringW(self->h, field, value)) != ERROR_SUCCESS)
return msierror(status);
Py_RETURN_NONE;
}
-static PyObject*
-record_setstream(msiobj* record, PyObject *args)
+/*[clinic input]
+_msi.Record.SetStream
+ field: int
+ value: Py_UNICODE
+ /
+
+Set field to the contents of the file named value.
+[clinic start generated code]*/
+
+static PyObject *
+_msi_Record_SetStream_impl(msiobj *self, int field, const Py_UNICODE *value)
+/*[clinic end generated code: output=442facac16913b48 input=a07aa19b865e8292]*/
{
int status;
- int field;
- wchar_t *data;
- if (!PyArg_ParseTuple(args, "iu:SetStream", &field, &data))
- return NULL;
-
- if ((status = MsiRecordSetStreamW(record->h, field, data)) != ERROR_SUCCESS)
+ if ((status = MsiRecordSetStreamW(self->h, field, value)) != ERROR_SUCCESS)
return msierror(status);
Py_RETURN_NONE;
}
-static PyObject*
-record_setinteger(msiobj* record, PyObject *args)
+/*[clinic input]
+_msi.Record.SetInteger
+ field: int
+ value: int
+ /
+
+Set field to an integer value.
+[clinic start generated code]*/
+
+static PyObject *
+_msi_Record_SetInteger_impl(msiobj *self, int field, int value)
+/*[clinic end generated code: output=669e8647775d0ce7 input=c571aa775e7e451b]*/
{
int status;
- int field;
- int data;
-
- if (!PyArg_ParseTuple(args, "ii:SetInteger", &field, &data))
- return NULL;
- if ((status = MsiRecordSetInteger(record->h, field, data)) != ERROR_SUCCESS)
+ if ((status = MsiRecordSetInteger(self->h, field, value)) != ERROR_SUCCESS)
return msierror(status);
Py_RETURN_NONE;
@@ -510,20 +585,13 @@ record_setinteger(msiobj* record, PyObject *args)
static PyMethodDef record_methods[] = {
- { "GetFieldCount", (PyCFunction)record_getfieldcount, METH_NOARGS,
- PyDoc_STR("GetFieldCount() -> int\nWraps MsiRecordGetFieldCount")},
- { "GetInteger", (PyCFunction)record_getinteger, METH_VARARGS,
- PyDoc_STR("GetInteger(field) -> int\nWraps MsiRecordGetInteger")},
- { "GetString", (PyCFunction)record_getstring, METH_VARARGS,
- PyDoc_STR("GetString(field) -> string\nWraps MsiRecordGetString")},
- { "SetString", (PyCFunction)record_setstring, METH_VARARGS,
- PyDoc_STR("SetString(field,str) -> None\nWraps MsiRecordSetString")},
- { "SetStream", (PyCFunction)record_setstream, METH_VARARGS,
- PyDoc_STR("SetStream(field,filename) -> None\nWraps MsiRecordSetInteger")},
- { "SetInteger", (PyCFunction)record_setinteger, METH_VARARGS,
- PyDoc_STR("SetInteger(field,int) -> None\nWraps MsiRecordSetInteger")},
- { "ClearData", (PyCFunction)record_cleardata, METH_NOARGS,
- PyDoc_STR("ClearData() -> int\nWraps MsiRecordGClearData")},
+ _MSI_RECORD_GETFIELDCOUNT_METHODDEF
+ _MSI_RECORD_GETINTEGER_METHODDEF
+ _MSI_RECORD_GETSTRING_METHODDEF
+ _MSI_RECORD_SETSTRING_METHODDEF
+ _MSI_RECORD_SETSTREAM_METHODDEF
+ _MSI_RECORD_SETINTEGER_METHODDEF
+ _MSI_RECORD_CLEARDATA_METHODDEF
{ NULL, NULL }
};
@@ -587,11 +655,20 @@ record_new(MSIHANDLE h)
/*************************** SummaryInformation objects **************/
-static PyObject*
-summary_getproperty(msiobj* si, PyObject *args)
+/*[clinic input]
+_msi.SummaryInformation.GetProperty
+ field: int
+ the name of the property, one of the PID_* constants
+ /
+
+Return a property of the summary.
+[clinic start generated code]*/
+
+static PyObject *
+_msi_SummaryInformation_GetProperty_impl(msiobj *self, int field)
+/*[clinic end generated code: output=f8946a33ee14f6ef input=f8dfe2c890d6cb8b]*/
{
int status;
- int field;
PyObject *result;
UINT type;
INT ival;
@@ -600,10 +677,7 @@ summary_getproperty(msiobj* si, PyObject *args)
char *sval = sbuf;
DWORD ssize = sizeof(sbuf);
- if (!PyArg_ParseTuple(args, "i:GetProperty", &field))
- return NULL;
-
- status = MsiSummaryInfoGetProperty(si->h, field, &type, &ival,
+ status = MsiSummaryInfoGetProperty(self->h, field, &type, &ival,
&fval, sval, &ssize);
if (status == ERROR_MORE_DATA) {
ssize++;
@@ -611,7 +685,7 @@ summary_getproperty(msiobj* si, PyObject *args)
if (sval == NULL) {
return PyErr_NoMemory();
}
- status = MsiSummaryInfoGetProperty(si->h, field, &type, &ival,
+ status = MsiSummaryInfoGetProperty(self->h, field, &type, &ival,
&fval, sval, &ssize);
}
if (status != ERROR_SUCCESS) {
@@ -644,42 +718,67 @@ summary_getproperty(msiobj* si, PyObject *args)
return result;
}
-static PyObject*
-summary_getpropertycount(msiobj* si, PyObject *args)
+/*[clinic input]
+_msi.SummaryInformation.GetPropertyCount
+
+Return the number of summary properties.
+[clinic start generated code]*/
+
+static PyObject *
+_msi_SummaryInformation_GetPropertyCount_impl(msiobj *self)
+/*[clinic end generated code: output=68e94b2aeee92b3d input=2e71e985586d82dc]*/
{
int status;
UINT result;
- status = MsiSummaryInfoGetPropertyCount(si->h, &result);
+ status = MsiSummaryInfoGetPropertyCount(self->h, &result);
if (status != ERROR_SUCCESS)
return msierror(status);
return PyLong_FromLong(result);
}
-static PyObject*
-summary_setproperty(msiobj* si, PyObject *args)
+/*[clinic input]
+_msi.SummaryInformation.SetProperty
+ field: int
+ the name of the property, one of the PID_* constants
+ value as data: object
+ the new value of the property (integer or string)
+ /
+
+Set a property.
+[clinic start generated code]*/
+
+static PyObject *
+_msi_SummaryInformation_SetProperty_impl(msiobj *self, int field,
+ PyObject *data)
+/*[clinic end generated code: output=3d4692c8984bb675 input=f2a7811b905abbed]*/
{
int status;
- int field;
- PyObject* data;
-
- if (!PyArg_ParseTuple(args, "iO:SetProperty", &field, &data))
- return NULL;
if (PyUnicode_Check(data)) {
+#if USE_UNICODE_WCHAR_CACHE
+_Py_COMP_DIAG_PUSH
+_Py_COMP_DIAG_IGNORE_DEPR_DECLS
const WCHAR *value = _PyUnicode_AsUnicode(data);
+_Py_COMP_DIAG_POP
+#else /* USE_UNICODE_WCHAR_CACHE */
+ WCHAR *value = PyUnicode_AsWideCharString(data, NULL);
+#endif /* USE_UNICODE_WCHAR_CACHE */
if (value == NULL) {
return NULL;
}
- status = MsiSummaryInfoSetPropertyW(si->h, field, VT_LPSTR,
+ status = MsiSummaryInfoSetPropertyW(self->h, field, VT_LPSTR,
0, NULL, value);
+#if !USE_UNICODE_WCHAR_CACHE
+ PyMem_Free(value);
+#endif /* USE_UNICODE_WCHAR_CACHE */
} else if (PyLong_CheckExact(data)) {
long value = PyLong_AsLong(data);
if (value == -1 && PyErr_Occurred()) {
return NULL;
}
- status = MsiSummaryInfoSetProperty(si->h, field, VT_I4,
+ status = MsiSummaryInfoSetProperty(self->h, field, VT_I4,
value, NULL, NULL);
} else {
PyErr_SetString(PyExc_TypeError, "unsupported type");
@@ -693,26 +792,29 @@ summary_setproperty(msiobj* si, PyObject *args)
}
-static PyObject*
-summary_persist(msiobj* si, PyObject *args)
+/*[clinic input]
+_msi.SummaryInformation.Persist
+
+Write the modified properties to the summary information stream.
+[clinic start generated code]*/
+
+static PyObject *
+_msi_SummaryInformation_Persist_impl(msiobj *self)
+/*[clinic end generated code: output=c564bd17f5e122c9 input=e3dda9d530095ef7]*/
{
int status;
- status = MsiSummaryInfoPersist(si->h);
+ status = MsiSummaryInfoPersist(self->h);
if (status != ERROR_SUCCESS)
return msierror(status);
Py_RETURN_NONE;
}
static PyMethodDef summary_methods[] = {
- { "GetProperty", (PyCFunction)summary_getproperty, METH_VARARGS,
- PyDoc_STR("GetProperty(propid) -> value\nWraps MsiSummaryInfoGetProperty")},
- { "GetPropertyCount", (PyCFunction)summary_getpropertycount, METH_NOARGS,
- PyDoc_STR("GetProperty() -> int\nWraps MsiSummaryInfoGetPropertyCount")},
- { "SetProperty", (PyCFunction)summary_setproperty, METH_VARARGS,
- PyDoc_STR("SetProperty(value) -> None\nWraps MsiSummaryInfoProperty")},
- { "Persist", (PyCFunction)summary_persist, METH_NOARGS,
- PyDoc_STR("Persist() -> None\nWraps MsiSummaryInfoPersist")},
+ _MSI_SUMMARYINFORMATION_GETPROPERTY_METHODDEF
+ _MSI_SUMMARYINFORMATION_GETPROPERTYCOUNT_METHODDEF
+ _MSI_SUMMARYINFORMATION_SETPROPERTY_METHODDEF
+ _MSI_SUMMARYINFORMATION_PERSIST_METHODDEF
{ NULL, NULL }
};
@@ -762,15 +864,22 @@ static PyTypeObject summary_Type = {
/*************************** View objects **************/
-static PyObject*
-view_execute(msiobj *view, PyObject*args)
+/*[clinic input]
+_msi.View.Execute
+ params as oparams: object
+ a record describing actual values of the parameter tokens
+ in the query or None
+ /
+
+Execute the SQL query of the view.
+[clinic start generated code]*/
+
+static PyObject *
+_msi_View_Execute(msiobj *self, PyObject *oparams)
+/*[clinic end generated code: output=f0f65fd2900bcb4e input=cb163a15d453348e]*/
{
int status;
MSIHANDLE params = 0;
- PyObject *oparams = Py_None;
-
- if (!PyArg_ParseTuple(args, "O:Execute", &oparams))
- return NULL;
if (oparams != Py_None) {
if (oparams->ob_type != &record_Type) {
@@ -780,20 +889,27 @@ view_execute(msiobj *view, PyObject*args)
params = ((msiobj*)oparams)->h;
}
- status = MsiViewExecute(view->h, params);
+ status = MsiViewExecute(self->h, params);
if (status != ERROR_SUCCESS)
return msierror(status);
Py_RETURN_NONE;
}
-static PyObject*
-view_fetch(msiobj *view, PyObject*args)
+/*[clinic input]
+_msi.View.Fetch
+
+Return a result record of the query.
+[clinic start generated code]*/
+
+static PyObject *
+_msi_View_Fetch_impl(msiobj *self)
+/*[clinic end generated code: output=ba154a3794537d4e input=7f3e3d06c449001c]*/
{
int status;
MSIHANDLE result;
- status = MsiViewFetch(view->h, &result);
+ status = MsiViewFetch(self->h, &result);
if (status == ERROR_NO_MORE_ITEMS) {
Py_RETURN_NONE;
} else if (status != ERROR_SUCCESS) {
@@ -803,65 +919,80 @@ view_fetch(msiobj *view, PyObject*args)
return record_new(result);
}
-static PyObject*
-view_getcolumninfo(msiobj *view, PyObject *args)
+/*[clinic input]
+_msi.View.GetColumnInfo
+ kind: int
+ MSICOLINFO_NAMES or MSICOLINFO_TYPES
+ /
+
+Return a record describing the columns of the view.
+[clinic start generated code]*/
+
+static PyObject *
+_msi_View_GetColumnInfo_impl(msiobj *self, int kind)
+/*[clinic end generated code: output=e7c1697db9403660 input=afedb892bf564a3b]*/
{
int status;
- int kind;
MSIHANDLE result;
- if (!PyArg_ParseTuple(args, "i:GetColumnInfo", &kind))
- return NULL;
-
- if ((status = MsiViewGetColumnInfo(view->h, kind, &result)) != ERROR_SUCCESS)
+ if ((status = MsiViewGetColumnInfo(self->h, kind, &result)) != ERROR_SUCCESS)
return msierror(status);
return record_new(result);
}
-static PyObject*
-view_modify(msiobj *view, PyObject *args)
+/*[clinic input]
+_msi.View.Modify
+ kind: int
+ one of the MSIMODIFY_* constants
+ data: object
+ a record describing the new data
+ /
+
+Modify the view.
+[clinic start generated code]*/
+
+static PyObject *
+_msi_View_Modify_impl(msiobj *self, int kind, PyObject *data)
+/*[clinic end generated code: output=69aaf3ce8ddac0ba input=2828de22de0d47b4]*/
{
- int kind;
- PyObject *data;
int status;
- if (!PyArg_ParseTuple(args, "iO:Modify", &kind, &data))
- return NULL;
-
if (data->ob_type != &record_Type) {
PyErr_SetString(PyExc_TypeError, "Modify expects a record object");
return NULL;
}
- if ((status = MsiViewModify(view->h, kind, ((msiobj*)data)->h)) != ERROR_SUCCESS)
+ if ((status = MsiViewModify(self->h, kind, ((msiobj*)data)->h)) != ERROR_SUCCESS)
return msierror(status);
Py_RETURN_NONE;
}
-static PyObject*
-view_close(msiobj *view, PyObject*args)
+/*[clinic input]
+_msi.View.Close
+
+Close the view.
+[clinic start generated code]*/
+
+static PyObject *
+_msi_View_Close_impl(msiobj *self)
+/*[clinic end generated code: output=488f7b8645ca104a input=de6927d1308c401c]*/
{
int status;
- if ((status = MsiViewClose(view->h)) != ERROR_SUCCESS)
+ if ((status = MsiViewClose(self->h)) != ERROR_SUCCESS)
return msierror(status);
Py_RETURN_NONE;
}
static PyMethodDef view_methods[] = {
- { "Execute", (PyCFunction)view_execute, METH_VARARGS,
- PyDoc_STR("Execute(params=None) -> None\nWraps MsiViewExecute")},
- { "GetColumnInfo", (PyCFunction)view_getcolumninfo, METH_VARARGS,
- PyDoc_STR("GetColumnInfo() -> result\nWraps MsiGetColumnInfo")},
- { "Fetch", (PyCFunction)view_fetch, METH_NOARGS,
- PyDoc_STR("Fetch() -> result\nWraps MsiViewFetch")},
- { "Modify", (PyCFunction)view_modify, METH_VARARGS,
- PyDoc_STR("Modify(mode,record) -> None\nWraps MsiViewModify")},
- { "Close", (PyCFunction)view_close, METH_NOARGS,
- PyDoc_STR("Close() -> result\nWraps MsiViewClose")},
+ _MSI_VIEW_EXECUTE_METHODDEF
+ _MSI_VIEW_GETCOLUMNINFO_METHODDEF
+ _MSI_VIEW_FETCH_METHODDEF
+ _MSI_VIEW_MODIFY_METHODDEF
+ _MSI_VIEW_CLOSE_METHODDEF
{ NULL, NULL }
};
@@ -911,18 +1042,24 @@ static PyTypeObject msiview_Type = {
/*************************** Database objects **************/
-static PyObject*
-msidb_openview(msiobj *msidb, PyObject *args)
+/*[clinic input]
+_msi.Database.OpenView
+ sql: Py_UNICODE
+ the SQL statement to execute
+ /
+
+Return a view object.
+[clinic start generated code]*/
+
+static PyObject *
+_msi_Database_OpenView_impl(msiobj *self, const Py_UNICODE *sql)
+/*[clinic end generated code: output=e712e6a11229abfd input=50f1771f37e500df]*/
{
int status;
- const wchar_t *sql;
MSIHANDLE hView;
msiobj *result;
- if (!PyArg_ParseTuple(args, "u:OpenView", &sql))
- return NULL;
-
- if ((status = MsiDatabaseOpenViewW(msidb->h, sql, &hView)) != ERROR_SUCCESS)
+ if ((status = MsiDatabaseOpenViewW(self->h, sql, &hView)) != ERROR_SUCCESS)
return msierror(status);
result = PyObject_New(struct msiobj, &msiview_Type);
@@ -935,29 +1072,42 @@ msidb_openview(msiobj *msidb, PyObject *args)
return (PyObject*)result;
}
-static PyObject*
-msidb_commit(msiobj *msidb, PyObject *args)
+/*[clinic input]
+_msi.Database.Commit
+
+Commit the changes pending in the current transaction.
+[clinic start generated code]*/
+
+static PyObject *
+_msi_Database_Commit_impl(msiobj *self)
+/*[clinic end generated code: output=f33021feb8b0cdd8 input=375bb120d402266d]*/
{
int status;
- if ((status = MsiDatabaseCommit(msidb->h)) != ERROR_SUCCESS)
+ if ((status = MsiDatabaseCommit(self->h)) != ERROR_SUCCESS)
return msierror(status);
Py_RETURN_NONE;
}
-static PyObject*
-msidb_getsummaryinformation(msiobj *db, PyObject *args)
+/*[clinic input]
+_msi.Database.GetSummaryInformation
+ count: int
+ the maximum number of updated values
+ /
+
+Return a new summary information object.
+[clinic start generated code]*/
+
+static PyObject *
+_msi_Database_GetSummaryInformation_impl(msiobj *self, int count)
+/*[clinic end generated code: output=781e51a4ea4da847 input=18a899ead6521735]*/
{
int status;
- int count;
MSIHANDLE result;
msiobj *oresult;
- if (!PyArg_ParseTuple(args, "i:GetSummaryInformation", &count))
- return NULL;
-
- status = MsiGetSummaryInformation(db->h, NULL, count, &result);
+ status = MsiGetSummaryInformation(self->h, NULL, count, &result);
if (status != ERROR_SUCCESS)
return msierror(status);
@@ -972,14 +1122,10 @@ msidb_getsummaryinformation(msiobj *db, PyObject *args)
}
static PyMethodDef db_methods[] = {
- { "OpenView", (PyCFunction)msidb_openview, METH_VARARGS,
- PyDoc_STR("OpenView(sql) -> viewobj\nWraps MsiDatabaseOpenView")},
- { "Commit", (PyCFunction)msidb_commit, METH_NOARGS,
- PyDoc_STR("Commit() -> None\nWraps MsiDatabaseCommit")},
- { "GetSummaryInformation", (PyCFunction)msidb_getsummaryinformation, METH_VARARGS,
- PyDoc_STR("GetSummaryInformation(updateCount) -> viewobj\nWraps MsiGetSummaryInformation")},
- { "Close", (PyCFunction)msidb_close, METH_NOARGS,
- PyDoc_STR("Close() -> None\nWraps MsiCloseHandle")},
+ _MSI_DATABASE_OPENVIEW_METHODDEF
+ _MSI_DATABASE_COMMIT_METHODDEF
+ _MSI_DATABASE_GETSUMMARYINFORMATION_METHODDEF
+ _MSI_DATABASE_CLOSE_METHODDEF
{ NULL, NULL }
};
@@ -1038,15 +1184,25 @@ static PyTypeObject msidb_Type = {
Py_NOT_PERSIST(x, MSIDBOPEN_CREATE) && \
Py_NOT_PERSIST(x, MSIDBOPEN_CREATEDIRECT))
-static PyObject* msiopendb(PyObject *obj, PyObject *args)
+/*[clinic input]
+_msi.OpenDatabase
+ path: Py_UNICODE
+ the file name of the MSI file
+ persist: int
+ the persistence mode
+ /
+
+Return a new database object.
+[clinic start generated code]*/
+
+static PyObject *
+_msi_OpenDatabase_impl(PyObject *module, const Py_UNICODE *path, int persist)
+/*[clinic end generated code: output=d34b7202b745de05 input=1300f3b97659559b]*/
{
int status;
- const wchar_t *path;
- int persist;
MSIHANDLE h;
msiobj *result;
- if (!PyArg_ParseTuple(args, "ui:MSIOpenDatabase", &path, &persist))
- return NULL;
+
/* We need to validate that persist is a valid MSIDBOPEN_* value. Otherwise,
MsiOpenDatabase may treat the value as a pointer, leading to unexpected
behavior. */
@@ -1065,15 +1221,21 @@ static PyObject* msiopendb(PyObject *obj, PyObject *args)
return (PyObject*)result;
}
-static PyObject*
-createrecord(PyObject *o, PyObject *args)
+/*[clinic input]
+_msi.CreateRecord
+ count: int
+ the number of fields of the record
+ /
+
+Return a new record object.
+[clinic start generated code]*/
+
+static PyObject *
+_msi_CreateRecord_impl(PyObject *module, int count)
+/*[clinic end generated code: output=0ba0a00beea3e99e input=53f17d5b5d9b077d]*/
{
- int count;
MSIHANDLE h;
- if (!PyArg_ParseTuple(args, "i:CreateRecord", &count))
- return NULL;
-
h = MsiCreateRecord(count);
if (h == 0)
return msierror(0);
@@ -1083,14 +1245,10 @@ createrecord(PyObject *o, PyObject *args)
static PyMethodDef msi_methods[] = {
- {"UuidCreate", (PyCFunction)uuidcreate, METH_NOARGS,
- PyDoc_STR("UuidCreate() -> string")},
- {"FCICreate", (PyCFunction)fcicreate, METH_VARARGS,
- PyDoc_STR("fcicreate(cabname,files) -> None")},
- {"OpenDatabase", (PyCFunction)msiopendb, METH_VARARGS,
- PyDoc_STR("OpenDatabase(name, flags) -> dbobj\nWraps MsiOpenDatabase")},
- {"CreateRecord", (PyCFunction)createrecord, METH_VARARGS,
- PyDoc_STR("OpenDatabase(name, flags) -> dbobj\nWraps MsiCreateRecord")},
+ _MSI_UUIDCREATE_METHODDEF
+ _MSI_FCICREATE_METHODDEF
+ _MSI_OPENDATABASE_METHODDEF
+ _MSI_CREATERECORD_METHODDEF
{NULL, NULL} /* sentinel */
};
diff --git a/PC/clinic/_msi.c.h b/PC/clinic/_msi.c.h
new file mode 100644
index 0000000000000..895bf39a779f4
--- /dev/null
+++ b/PC/clinic/_msi.c.h
@@ -0,0 +1,728 @@
+/*[clinic input]
+preserve
+[clinic start generated code]*/
+
+PyDoc_STRVAR(_msi_UuidCreate__doc__,
+"UuidCreate($module, /)\n"
+"--\n"
+"\n"
+"Return the string representation of a new unique identifier.");
+
+#define _MSI_UUIDCREATE_METHODDEF \
+ {"UuidCreate", (PyCFunction)_msi_UuidCreate, METH_NOARGS, _msi_UuidCreate__doc__},
+
+static PyObject *
+_msi_UuidCreate_impl(PyObject *module);
+
+static PyObject *
+_msi_UuidCreate(PyObject *module, PyObject *Py_UNUSED(ignored))
+{
+ return _msi_UuidCreate_impl(module);
+}
+
+PyDoc_STRVAR(_msi_FCICreate__doc__,
+"FCICreate($module, cabname, files, /)\n"
+"--\n"
+"\n"
+"Create a new CAB file.\n"
+"\n"
+" cabname\n"
+" the name of the CAB file\n"
+" files\n"
+" a list of tuples, each containing the name of the file on disk,\n"
+" and the name of the file inside the CAB file");
+
+#define _MSI_FCICREATE_METHODDEF \
+ {"FCICreate", (PyCFunction)(void(*)(void))_msi_FCICreate, METH_FASTCALL, _msi_FCICreate__doc__},
+
+static PyObject *
+_msi_FCICreate_impl(PyObject *module, const char *cabname, PyObject *files);
+
+static PyObject *
+_msi_FCICreate(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ const char *cabname;
+ PyObject *files;
+
+ if (!_PyArg_CheckPositional("FCICreate", nargs, 2, 2)) {
+ goto exit;
+ }
+ if (!PyUnicode_Check(args[0])) {
+ _PyArg_BadArgument("FCICreate", "argument 1", "str", args[0]);
+ goto exit;
+ }
+ Py_ssize_t cabname_length;
+ cabname = PyUnicode_AsUTF8AndSize(args[0], &cabname_length);
+ if (cabname == NULL) {
+ goto exit;
+ }
+ if (strlen(cabname) != (size_t)cabname_length) {
+ PyErr_SetString(PyExc_ValueError, "embedded null character");
+ goto exit;
+ }
+ files = args[1];
+ return_value = _msi_FCICreate_impl(module, cabname, files);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_msi_Database_Close__doc__,
+"Close($self, /)\n"
+"--\n"
+"\n"
+"Close the database object.");
+
+#define _MSI_DATABASE_CLOSE_METHODDEF \
+ {"Close", (PyCFunction)_msi_Database_Close, METH_NOARGS, _msi_Database_Close__doc__},
+
+static PyObject *
+_msi_Database_Close_impl(msiobj *self);
+
+static PyObject *
+_msi_Database_Close(msiobj *self, PyObject *Py_UNUSED(ignored))
+{
+ return _msi_Database_Close_impl(self);
+}
+
+PyDoc_STRVAR(_msi_Record_GetFieldCount__doc__,
+"GetFieldCount($self, /)\n"
+"--\n"
+"\n"
+"Return the number of fields of the record.");
+
+#define _MSI_RECORD_GETFIELDCOUNT_METHODDEF \
+ {"GetFieldCount", (PyCFunction)_msi_Record_GetFieldCount, METH_NOARGS, _msi_Record_GetFieldCount__doc__},
+
+static PyObject *
+_msi_Record_GetFieldCount_impl(msiobj *self);
+
+static PyObject *
+_msi_Record_GetFieldCount(msiobj *self, PyObject *Py_UNUSED(ignored))
+{
+ return _msi_Record_GetFieldCount_impl(self);
+}
+
+PyDoc_STRVAR(_msi_Record_GetInteger__doc__,
+"GetInteger($self, field, /)\n"
+"--\n"
+"\n"
+"Return the value of field as an integer where possible.");
+
+#define _MSI_RECORD_GETINTEGER_METHODDEF \
+ {"GetInteger", (PyCFunction)_msi_Record_GetInteger, METH_O, _msi_Record_GetInteger__doc__},
+
+static PyObject *
+_msi_Record_GetInteger_impl(msiobj *self, unsigned int field);
+
+static PyObject *
+_msi_Record_GetInteger(msiobj *self, PyObject *arg)
+{
+ PyObject *return_value = NULL;
+ unsigned int field;
+
+ field = (unsigned int)PyLong_AsUnsignedLongMask(arg);
+ if (field == (unsigned int)-1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ return_value = _msi_Record_GetInteger_impl(self, field);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_msi_Record_GetString__doc__,
+"GetString($self, field, /)\n"
+"--\n"
+"\n"
+"Return the value of field as a string where possible.");
+
+#define _MSI_RECORD_GETSTRING_METHODDEF \
+ {"GetString", (PyCFunction)_msi_Record_GetString, METH_O, _msi_Record_GetString__doc__},
+
+static PyObject *
+_msi_Record_GetString_impl(msiobj *self, unsigned int field);
+
+static PyObject *
+_msi_Record_GetString(msiobj *self, PyObject *arg)
+{
+ PyObject *return_value = NULL;
+ unsigned int field;
+
+ field = (unsigned int)PyLong_AsUnsignedLongMask(arg);
+ if (field == (unsigned int)-1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ return_value = _msi_Record_GetString_impl(self, field);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_msi_Record_ClearData__doc__,
+"ClearData($self, /)\n"
+"--\n"
+"\n"
+"Set all fields of the record to 0.");
+
+#define _MSI_RECORD_CLEARDATA_METHODDEF \
+ {"ClearData", (PyCFunction)_msi_Record_ClearData, METH_NOARGS, _msi_Record_ClearData__doc__},
+
+static PyObject *
+_msi_Record_ClearData_impl(msiobj *self);
+
+static PyObject *
+_msi_Record_ClearData(msiobj *self, PyObject *Py_UNUSED(ignored))
+{
+ return _msi_Record_ClearData_impl(self);
+}
+
+PyDoc_STRVAR(_msi_Record_SetString__doc__,
+"SetString($self, field, value, /)\n"
+"--\n"
+"\n"
+"Set field to a string value.");
+
+#define _MSI_RECORD_SETSTRING_METHODDEF \
+ {"SetString", (PyCFunction)(void(*)(void))_msi_Record_SetString, METH_FASTCALL, _msi_Record_SetString__doc__},
+
+static PyObject *
+_msi_Record_SetString_impl(msiobj *self, int field, const Py_UNICODE *value);
+
+static PyObject *
+_msi_Record_SetString(msiobj *self, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ int field;
+ const Py_UNICODE *value;
+
+ if (!_PyArg_CheckPositional("SetString", nargs, 2, 2)) {
+ goto exit;
+ }
+ field = _PyLong_AsInt(args[0]);
+ if (field == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ if (!PyUnicode_Check(args[1])) {
+ _PyArg_BadArgument("SetString", "argument 2", "str", args[1]);
+ goto exit;
+ }
+ #if USE_UNICODE_WCHAR_CACHE
+ _Py_COMP_DIAG_PUSH
+ _Py_COMP_DIAG_IGNORE_DEPR_DECLS
+ value = _PyUnicode_AsUnicode(args[1]);
+ _Py_COMP_DIAG_POP
+ #else /* USE_UNICODE_WCHAR_CACHE */
+ value = PyUnicode_AsWideCharString(args[1], NULL);
+ #endif /* USE_UNICODE_WCHAR_CACHE */
+ if (value == NULL) {
+ goto exit;
+ }
+ return_value = _msi_Record_SetString_impl(self, field, value);
+
+exit:
+ /* Cleanup for value */
+ #if !USE_UNICODE_WCHAR_CACHE
+ PyMem_Free((void *)value);
+ #endif /* USE_UNICODE_WCHAR_CACHE */
+
+ return return_value;
+}
+
+PyDoc_STRVAR(_msi_Record_SetStream__doc__,
+"SetStream($self, field, value, /)\n"
+"--\n"
+"\n"
+"Set field to the contents of the file named value.");
+
+#define _MSI_RECORD_SETSTREAM_METHODDEF \
+ {"SetStream", (PyCFunction)(void(*)(void))_msi_Record_SetStream, METH_FASTCALL, _msi_Record_SetStream__doc__},
+
+static PyObject *
+_msi_Record_SetStream_impl(msiobj *self, int field, const Py_UNICODE *value);
+
+static PyObject *
+_msi_Record_SetStream(msiobj *self, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ int field;
+ const Py_UNICODE *value;
+
+ if (!_PyArg_CheckPositional("SetStream", nargs, 2, 2)) {
+ goto exit;
+ }
+ field = _PyLong_AsInt(args[0]);
+ if (field == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ if (!PyUnicode_Check(args[1])) {
+ _PyArg_BadArgument("SetStream", "argument 2", "str", args[1]);
+ goto exit;
+ }
+ #if USE_UNICODE_WCHAR_CACHE
+ _Py_COMP_DIAG_PUSH
+ _Py_COMP_DIAG_IGNORE_DEPR_DECLS
+ value = _PyUnicode_AsUnicode(args[1]);
+ _Py_COMP_DIAG_POP
+ #else /* USE_UNICODE_WCHAR_CACHE */
+ value = PyUnicode_AsWideCharString(args[1], NULL);
+ #endif /* USE_UNICODE_WCHAR_CACHE */
+ if (value == NULL) {
+ goto exit;
+ }
+ return_value = _msi_Record_SetStream_impl(self, field, value);
+
+exit:
+ /* Cleanup for value */
+ #if !USE_UNICODE_WCHAR_CACHE
+ PyMem_Free((void *)value);
+ #endif /* USE_UNICODE_WCHAR_CACHE */
+
+ return return_value;
+}
+
+PyDoc_STRVAR(_msi_Record_SetInteger__doc__,
+"SetInteger($self, field, value, /)\n"
+"--\n"
+"\n"
+"Set field to an integer value.");
+
+#define _MSI_RECORD_SETINTEGER_METHODDEF \
+ {"SetInteger", (PyCFunction)(void(*)(void))_msi_Record_SetInteger, METH_FASTCALL, _msi_Record_SetInteger__doc__},
+
+static PyObject *
+_msi_Record_SetInteger_impl(msiobj *self, int field, int value);
+
+static PyObject *
+_msi_Record_SetInteger(msiobj *self, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ int field;
+ int value;
+
+ if (!_PyArg_CheckPositional("SetInteger", nargs, 2, 2)) {
+ goto exit;
+ }
+ field = _PyLong_AsInt(args[0]);
+ if (field == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ value = _PyLong_AsInt(args[1]);
+ if (value == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ return_value = _msi_Record_SetInteger_impl(self, field, value);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_msi_SummaryInformation_GetProperty__doc__,
+"GetProperty($self, field, /)\n"
+"--\n"
+"\n"
+"Return a property of the summary.\n"
+"\n"
+" field\n"
+" the name of the property, one of the PID_* constants");
+
+#define _MSI_SUMMARYINFORMATION_GETPROPERTY_METHODDEF \
+ {"GetProperty", (PyCFunction)_msi_SummaryInformation_GetProperty, METH_O, _msi_SummaryInformation_GetProperty__doc__},
+
+static PyObject *
+_msi_SummaryInformation_GetProperty_impl(msiobj *self, int field);
+
+static PyObject *
+_msi_SummaryInformation_GetProperty(msiobj *self, PyObject *arg)
+{
+ PyObject *return_value = NULL;
+ int field;
+
+ field = _PyLong_AsInt(arg);
+ if (field == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ return_value = _msi_SummaryInformation_GetProperty_impl(self, field);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_msi_SummaryInformation_GetPropertyCount__doc__,
+"GetPropertyCount($self, /)\n"
+"--\n"
+"\n"
+"Return the number of summary properties.");
+
+#define _MSI_SUMMARYINFORMATION_GETPROPERTYCOUNT_METHODDEF \
+ {"GetPropertyCount", (PyCFunction)_msi_SummaryInformation_GetPropertyCount, METH_NOARGS, _msi_SummaryInformation_GetPropertyCount__doc__},
+
+static PyObject *
+_msi_SummaryInformation_GetPropertyCount_impl(msiobj *self);
+
+static PyObject *
+_msi_SummaryInformation_GetPropertyCount(msiobj *self, PyObject *Py_UNUSED(ignored))
+{
+ return _msi_SummaryInformation_GetPropertyCount_impl(self);
+}
+
+PyDoc_STRVAR(_msi_SummaryInformation_SetProperty__doc__,
+"SetProperty($self, field, value, /)\n"
+"--\n"
+"\n"
+"Set a property.\n"
+"\n"
+" field\n"
+" the name of the property, one of the PID_* constants\n"
+" value\n"
+" the new value of the property (integer or string)");
+
+#define _MSI_SUMMARYINFORMATION_SETPROPERTY_METHODDEF \
+ {"SetProperty", (PyCFunction)(void(*)(void))_msi_SummaryInformation_SetProperty, METH_FASTCALL, _msi_SummaryInformation_SetProperty__doc__},
+
+static PyObject *
+_msi_SummaryInformation_SetProperty_impl(msiobj *self, int field,
+ PyObject *data);
+
+static PyObject *
+_msi_SummaryInformation_SetProperty(msiobj *self, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ int field;
+ PyObject *data;
+
+ if (!_PyArg_CheckPositional("SetProperty", nargs, 2, 2)) {
+ goto exit;
+ }
+ field = _PyLong_AsInt(args[0]);
+ if (field == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ data = args[1];
+ return_value = _msi_SummaryInformation_SetProperty_impl(self, field, data);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_msi_SummaryInformation_Persist__doc__,
+"Persist($self, /)\n"
+"--\n"
+"\n"
+"Write the modified properties to the summary information stream.");
+
+#define _MSI_SUMMARYINFORMATION_PERSIST_METHODDEF \
+ {"Persist", (PyCFunction)_msi_SummaryInformation_Persist, METH_NOARGS, _msi_SummaryInformation_Persist__doc__},
+
+static PyObject *
+_msi_SummaryInformation_Persist_impl(msiobj *self);
+
+static PyObject *
+_msi_SummaryInformation_Persist(msiobj *self, PyObject *Py_UNUSED(ignored))
+{
+ return _msi_SummaryInformation_Persist_impl(self);
+}
+
+PyDoc_STRVAR(_msi_View_Execute__doc__,
+"Execute($self, params, /)\n"
+"--\n"
+"\n"
+"Execute the SQL query of the view.\n"
+"\n"
+" params\n"
+" a record describing actual values of the parameter tokens\n"
+" in the query or None");
+
+#define _MSI_VIEW_EXECUTE_METHODDEF \
+ {"Execute", (PyCFunction)_msi_View_Execute, METH_O, _msi_View_Execute__doc__},
+
+PyDoc_STRVAR(_msi_View_Fetch__doc__,
+"Fetch($self, /)\n"
+"--\n"
+"\n"
+"Return a result record of the query.");
+
+#define _MSI_VIEW_FETCH_METHODDEF \
+ {"Fetch", (PyCFunction)_msi_View_Fetch, METH_NOARGS, _msi_View_Fetch__doc__},
+
+static PyObject *
+_msi_View_Fetch_impl(msiobj *self);
+
+static PyObject *
+_msi_View_Fetch(msiobj *self, PyObject *Py_UNUSED(ignored))
+{
+ return _msi_View_Fetch_impl(self);
+}
+
+PyDoc_STRVAR(_msi_View_GetColumnInfo__doc__,
+"GetColumnInfo($self, kind, /)\n"
+"--\n"
+"\n"
+"Return a record describing the columns of the view.\n"
+"\n"
+" kind\n"
+" MSICOLINFO_NAMES or MSICOLINFO_TYPES");
+
+#define _MSI_VIEW_GETCOLUMNINFO_METHODDEF \
+ {"GetColumnInfo", (PyCFunction)_msi_View_GetColumnInfo, METH_O, _msi_View_GetColumnInfo__doc__},
+
+static PyObject *
+_msi_View_GetColumnInfo_impl(msiobj *self, int kind);
+
+static PyObject *
+_msi_View_GetColumnInfo(msiobj *self, PyObject *arg)
+{
+ PyObject *return_value = NULL;
+ int kind;
+
+ kind = _PyLong_AsInt(arg);
+ if (kind == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ return_value = _msi_View_GetColumnInfo_impl(self, kind);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_msi_View_Modify__doc__,
+"Modify($self, kind, data, /)\n"
+"--\n"
+"\n"
+"Modify the view.\n"
+"\n"
+" kind\n"
+" one of the MSIMODIFY_* constants\n"
+" data\n"
+" a record describing the new data");
+
+#define _MSI_VIEW_MODIFY_METHODDEF \
+ {"Modify", (PyCFunction)(void(*)(void))_msi_View_Modify, METH_FASTCALL, _msi_View_Modify__doc__},
+
+static PyObject *
+_msi_View_Modify_impl(msiobj *self, int kind, PyObject *data);
+
+static PyObject *
+_msi_View_Modify(msiobj *self, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ int kind;
+ PyObject *data;
+
+ if (!_PyArg_CheckPositional("Modify", nargs, 2, 2)) {
+ goto exit;
+ }
+ kind = _PyLong_AsInt(args[0]);
+ if (kind == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ data = args[1];
+ return_value = _msi_View_Modify_impl(self, kind, data);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_msi_View_Close__doc__,
+"Close($self, /)\n"
+"--\n"
+"\n"
+"Close the view.");
+
+#define _MSI_VIEW_CLOSE_METHODDEF \
+ {"Close", (PyCFunction)_msi_View_Close, METH_NOARGS, _msi_View_Close__doc__},
+
+static PyObject *
+_msi_View_Close_impl(msiobj *self);
+
+static PyObject *
+_msi_View_Close(msiobj *self, PyObject *Py_UNUSED(ignored))
+{
+ return _msi_View_Close_impl(self);
+}
+
+PyDoc_STRVAR(_msi_Database_OpenView__doc__,
+"OpenView($self, sql, /)\n"
+"--\n"
+"\n"
+"Return a view object.\n"
+"\n"
+" sql\n"
+" the SQL statement to execute");
+
+#define _MSI_DATABASE_OPENVIEW_METHODDEF \
+ {"OpenView", (PyCFunction)_msi_Database_OpenView, METH_O, _msi_Database_OpenView__doc__},
+
+static PyObject *
+_msi_Database_OpenView_impl(msiobj *self, const Py_UNICODE *sql);
+
+static PyObject *
+_msi_Database_OpenView(msiobj *self, PyObject *arg)
+{
+ PyObject *return_value = NULL;
+ const Py_UNICODE *sql;
+
+ if (!PyUnicode_Check(arg)) {
+ _PyArg_BadArgument("OpenView", "argument", "str", arg);
+ goto exit;
+ }
+ #if USE_UNICODE_WCHAR_CACHE
+ _Py_COMP_DIAG_PUSH
+ _Py_COMP_DIAG_IGNORE_DEPR_DECLS
+ sql = _PyUnicode_AsUnicode(arg);
+ _Py_COMP_DIAG_POP
+ #else /* USE_UNICODE_WCHAR_CACHE */
+ sql = PyUnicode_AsWideCharString(arg, NULL);
+ #endif /* USE_UNICODE_WCHAR_CACHE */
+ if (sql == NULL) {
+ goto exit;
+ }
+ return_value = _msi_Database_OpenView_impl(self, sql);
+
+exit:
+ /* Cleanup for sql */
+ #if !USE_UNICODE_WCHAR_CACHE
+ PyMem_Free((void *)sql);
+ #endif /* USE_UNICODE_WCHAR_CACHE */
+
+ return return_value;
+}
+
+PyDoc_STRVAR(_msi_Database_Commit__doc__,
+"Commit($self, /)\n"
+"--\n"
+"\n"
+"Commit the changes pending in the current transaction.");
+
+#define _MSI_DATABASE_COMMIT_METHODDEF \
+ {"Commit", (PyCFunction)_msi_Database_Commit, METH_NOARGS, _msi_Database_Commit__doc__},
+
+static PyObject *
+_msi_Database_Commit_impl(msiobj *self);
+
+static PyObject *
+_msi_Database_Commit(msiobj *self, PyObject *Py_UNUSED(ignored))
+{
+ return _msi_Database_Commit_impl(self);
+}
+
+PyDoc_STRVAR(_msi_Database_GetSummaryInformation__doc__,
+"GetSummaryInformation($self, count, /)\n"
+"--\n"
+"\n"
+"Return a new summary information object.\n"
+"\n"
+" count\n"
+" the maximum number of updated values");
+
+#define _MSI_DATABASE_GETSUMMARYINFORMATION_METHODDEF \
+ {"GetSummaryInformation", (PyCFunction)_msi_Database_GetSummaryInformation, METH_O, _msi_Database_GetSummaryInformation__doc__},
+
+static PyObject *
+_msi_Database_GetSummaryInformation_impl(msiobj *self, int count);
+
+static PyObject *
+_msi_Database_GetSummaryInformation(msiobj *self, PyObject *arg)
+{
+ PyObject *return_value = NULL;
+ int count;
+
+ count = _PyLong_AsInt(arg);
+ if (count == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ return_value = _msi_Database_GetSummaryInformation_impl(self, count);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_msi_OpenDatabase__doc__,
+"OpenDatabase($module, path, persist, /)\n"
+"--\n"
+"\n"
+"Return a new database object.\n"
+"\n"
+" path\n"
+" the file name of the MSI file\n"
+" persist\n"
+" the persistence mode");
+
+#define _MSI_OPENDATABASE_METHODDEF \
+ {"OpenDatabase", (PyCFunction)(void(*)(void))_msi_OpenDatabase, METH_FASTCALL, _msi_OpenDatabase__doc__},
+
+static PyObject *
+_msi_OpenDatabase_impl(PyObject *module, const Py_UNICODE *path, int persist);
+
+static PyObject *
+_msi_OpenDatabase(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ const Py_UNICODE *path;
+ int persist;
+
+ if (!_PyArg_CheckPositional("OpenDatabase", nargs, 2, 2)) {
+ goto exit;
+ }
+ if (!PyUnicode_Check(args[0])) {
+ _PyArg_BadArgument("OpenDatabase", "argument 1", "str", args[0]);
+ goto exit;
+ }
+ #if USE_UNICODE_WCHAR_CACHE
+ _Py_COMP_DIAG_PUSH
+ _Py_COMP_DIAG_IGNORE_DEPR_DECLS
+ path = _PyUnicode_AsUnicode(args[0]);
+ _Py_COMP_DIAG_POP
+ #else /* USE_UNICODE_WCHAR_CACHE */
+ path = PyUnicode_AsWideCharString(args[0], NULL);
+ #endif /* USE_UNICODE_WCHAR_CACHE */
+ if (path == NULL) {
+ goto exit;
+ }
+ persist = _PyLong_AsInt(args[1]);
+ if (persist == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ return_value = _msi_OpenDatabase_impl(module, path, persist);
+
+exit:
+ /* Cleanup for path */
+ #if !USE_UNICODE_WCHAR_CACHE
+ PyMem_Free((void *)path);
+ #endif /* USE_UNICODE_WCHAR_CACHE */
+
+ return return_value;
+}
+
+PyDoc_STRVAR(_msi_CreateRecord__doc__,
+"CreateRecord($module, count, /)\n"
+"--\n"
+"\n"
+"Return a new record object.\n"
+"\n"
+" count\n"
+" the number of fields of the record");
+
+#define _MSI_CREATERECORD_METHODDEF \
+ {"CreateRecord", (PyCFunction)_msi_CreateRecord, METH_O, _msi_CreateRecord__doc__},
+
+static PyObject *
+_msi_CreateRecord_impl(PyObject *module, int count);
+
+static PyObject *
+_msi_CreateRecord(PyObject *module, PyObject *arg)
+{
+ PyObject *return_value = NULL;
+ int count;
+
+ count = _PyLong_AsInt(arg);
+ if (count == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ return_value = _msi_CreateRecord_impl(module, count);
+
+exit:
+ return return_value;
+}
+/*[clinic end generated code: output=39807788326ad0e9 input=a9049054013a1b77]*/
1
0
bpo-41158: IDLE: rewrite the code for handling file encoding (GH-21215)
by Miss Islington (bot) July 1, 2020
by Miss Islington (bot) July 1, 2020
July 1, 2020
https://github.com/python/cpython/commit/c3fa7534c7173d338880a1727f17795670…
commit: c3fa7534c7173d338880a1727f17795670518610
branch: 3.8
author: Miss Islington (bot) <31488909+miss-islington(a)users.noreply.github.com>
committer: GitHub <noreply(a)github.com>
date: 2020-07-01T11:22:45-07:00
summary:
bpo-41158: IDLE: rewrite the code for handling file encoding (GH-21215)
(cherry picked from commit 694d31e714074176f0c324f95948b75dc768c091)
Co-authored-by: Serhiy Storchaka <storchaka(a)gmail.com>
files:
M Lib/idlelib/iomenu.py
diff --git a/Lib/idlelib/iomenu.py b/Lib/idlelib/iomenu.py
index 7f3f656ee2874..7641d866858a1 100644
--- a/Lib/idlelib/iomenu.py
+++ b/Lib/idlelib/iomenu.py
@@ -1,10 +1,8 @@
-import codecs
-from codecs import BOM_UTF8
import os
-import re
import shlex
import sys
import tempfile
+import tokenize
import tkinter.filedialog as tkFileDialog
import tkinter.messagebox as tkMessageBox
@@ -20,49 +18,6 @@
errors = 'surrogateescape'
-coding_re = re.compile(r'^[ \t\f]*#.*?coding[:=][ \t]*([-\w.]+)', re.ASCII)
-blank_re = re.compile(r'^[ \t\f]*(?:[#\r\n]|$)', re.ASCII)
-
-def coding_spec(data):
- """Return the encoding declaration according to PEP 263.
-
- When checking encoded data, only the first two lines should be passed
- in to avoid a UnicodeDecodeError if the rest of the data is not unicode.
- The first two lines would contain the encoding specification.
-
- Raise a LookupError if the encoding is declared but unknown.
- """
- if isinstance(data, bytes):
- # This encoding might be wrong. However, the coding
- # spec must be ASCII-only, so any non-ASCII characters
- # around here will be ignored. Decoding to Latin-1 should
- # never fail (except for memory outage)
- lines = data.decode('iso-8859-1')
- else:
- lines = data
- # consider only the first two lines
- if '\n' in lines:
- lst = lines.split('\n', 2)[:2]
- elif '\r' in lines:
- lst = lines.split('\r', 2)[:2]
- else:
- lst = [lines]
- for line in lst:
- match = coding_re.match(line)
- if match is not None:
- break
- if not blank_re.match(line):
- return None
- else:
- return None
- name = match.group(1)
- try:
- codecs.lookup(name)
- except LookupError:
- # The standard encoding error does not indicate the encoding
- raise LookupError("Unknown encoding: "+name)
- return name
-
class IOBinding:
# One instance per editor Window so methods know which to save, close.
@@ -78,7 +33,7 @@ def __init__(self, editwin):
self.save_as)
self.__id_savecopy = self.text.bind("<<save-copy-of-window-as-file>>",
self.save_a_copy)
- self.fileencoding = None
+ self.fileencoding = 'utf-8'
self.__id_print = self.text.bind("<<print-window>>", self.print_window)
def close(self):
@@ -165,34 +120,44 @@ def open(self, event=None, editFile=None):
self.text.focus_set()
return "break"
- eol = r"(\r\n)|\n|\r" # \r\n (Windows), \n (UNIX), or \r (Mac)
- eol_re = re.compile(eol)
eol_convention = os.linesep # default
def loadfile(self, filename):
try:
- # open the file in binary mode so that we can handle
- # end-of-line convention ourselves.
- with open(filename, 'rb') as f:
- two_lines = f.readline() + f.readline()
- f.seek(0)
- bytes = f.read()
- except OSError as msg:
- tkMessageBox.showerror("I/O Error", str(msg), parent=self.text)
+ try:
+ with tokenize.open(filename) as f:
+ chars = f.read()
+ fileencoding = f.encoding
+ eol_convention = f.newlines
+ converted = False
+ except (UnicodeDecodeError, SyntaxError):
+ # Wait for the editor window to appear
+ self.editwin.text.update()
+ enc = askstring(
+ "Specify file encoding",
+ "The file's encoding is invalid for Python 3.x.\n"
+ "IDLE will convert it to UTF-8.\n"
+ "What is the current encoding of the file?",
+ initialvalue='utf-8',
+ parent=self.editwin.text)
+ with open(filename, encoding=enc) as f:
+ chars = f.read()
+ fileencoding = f.encoding
+ eol_convention = f.newlines
+ converted = True
+ except OSError as err:
+ tkMessageBox.showerror("I/O Error", str(err), parent=self.text)
return False
- chars, converted = self._decode(two_lines, bytes)
- if chars is None:
+ except UnicodeDecodeError:
tkMessageBox.showerror("Decoding Error",
"File %s\nFailed to Decode" % filename,
parent=self.text)
return False
- # We now convert all end-of-lines to '\n's
- firsteol = self.eol_re.search(chars)
- if firsteol:
- self.eol_convention = firsteol.group(0)
- chars = self.eol_re.sub(r"\n", chars)
+
self.text.delete("1.0", "end")
self.set_filename(None)
+ self.fileencoding = fileencoding
+ self.eol_convention = eol_convention
self.text.insert("1.0", chars)
self.reset_undo()
self.set_filename(filename)
@@ -205,74 +170,6 @@ def loadfile(self, filename):
self.updaterecentfileslist(filename)
return True
- def _decode(self, two_lines, bytes):
- "Create a Unicode string."
- chars = None
- # Check presence of a UTF-8 signature first
- if bytes.startswith(BOM_UTF8):
- try:
- chars = bytes[3:].decode("utf-8")
- except UnicodeDecodeError:
- # has UTF-8 signature, but fails to decode...
- return None, False
- else:
- # Indicates that this file originally had a BOM
- self.fileencoding = 'BOM'
- return chars, False
- # Next look for coding specification
- try:
- enc = coding_spec(two_lines)
- except LookupError as name:
- tkMessageBox.showerror(
- title="Error loading the file",
- message="The encoding '%s' is not known to this Python "\
- "installation. The file may not display correctly" % name,
- parent = self.text)
- enc = None
- except UnicodeDecodeError:
- return None, False
- if enc:
- try:
- chars = str(bytes, enc)
- self.fileencoding = enc
- return chars, False
- except UnicodeDecodeError:
- pass
- # Try ascii:
- try:
- chars = str(bytes, 'ascii')
- self.fileencoding = None
- return chars, False
- except UnicodeDecodeError:
- pass
- # Try utf-8:
- try:
- chars = str(bytes, 'utf-8')
- self.fileencoding = 'utf-8'
- return chars, False
- except UnicodeDecodeError:
- pass
- # Finally, try the locale's encoding. This is deprecated;
- # the user should declare a non-ASCII encoding
- try:
- # Wait for the editor window to appear
- self.editwin.text.update()
- enc = askstring(
- "Specify file encoding",
- "The file's encoding is invalid for Python 3.x.\n"
- "IDLE will convert it to UTF-8.\n"
- "What is the current encoding of the file?",
- initialvalue = encoding,
- parent = self.editwin.text)
-
- if enc:
- chars = str(bytes, enc)
- self.fileencoding = None
- return chars, True
- except (UnicodeDecodeError, LookupError):
- pass
- return None, False # None on failure
-
def maybesave(self):
if self.get_saved():
return "yes"
@@ -360,38 +257,30 @@ def encode(self, chars):
# text to us. Don't try to guess further.
return chars
# Preserve a BOM that might have been present on opening
- if self.fileencoding == 'BOM':
- return BOM_UTF8 + chars.encode("utf-8")
+ if self.fileencoding == 'utf-8-sig':
+ return chars.encode('utf-8-sig')
# See whether there is anything non-ASCII in it.
# If not, no need to figure out the encoding.
try:
return chars.encode('ascii')
- except UnicodeError:
+ except UnicodeEncodeError:
pass
# Check if there is an encoding declared
try:
- # a string, let coding_spec slice it to the first two lines
- enc = coding_spec(chars)
- failed = None
- except LookupError as msg:
- failed = msg
- enc = None
- else:
- if not enc:
- # PEP 3120: default source encoding is UTF-8
- enc = 'utf-8'
- if enc:
- try:
- return chars.encode(enc)
- except UnicodeError:
- failed = "Invalid encoding '%s'" % enc
+ encoded = chars.encode('ascii', 'replace')
+ enc, _ = tokenize.detect_encoding(io.BytesIO(encoded).readline)
+ return chars.encode(enc)
+ except SyntaxError as err:
+ failed = str(err)
+ except UnicodeEncodeError:
+ failed = "Invalid encoding '%s'" % enc
tkMessageBox.showerror(
"I/O Error",
"%s.\nSaving as UTF-8" % failed,
- parent = self.text)
+ parent=self.text)
# Fallback: save as UTF-8, with BOM - ignoring the incorrect
# declared encoding
- return BOM_UTF8 + chars.encode("utf-8")
+ return chars.encode('utf-8-sig')
def print_window(self, event):
confirm = tkMessageBox.askokcancel(
1
0
July 1, 2020
https://github.com/python/cpython/commit/42f05e62927d00b7255e7cced808148388…
commit: 42f05e62927d00b7255e7cced808148388652fcd
branch: 3.8
author: Miss Islington (bot) <31488909+miss-islington(a)users.noreply.github.com>
committer: GitHub <noreply(a)github.com>
date: 2020-07-01T02:40:55-07:00
summary:
Doc: Minor fix to init config C API documentation (GH-21198)
Co-authored-by: Tomer Vromen <tomer.vromen(a)intel.com>
(cherry picked from commit 741008a57bdc95090b8be6ded5a9fd3f17f7bf21)
Co-authored-by: tomerv <tomerv(a)gmail.com>
files:
M Doc/c-api/init_config.rst
diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst
index 79a8815ed4199..ff4ccb8dbbb5f 100644
--- a/Doc/c-api/init_config.rst
+++ b/Doc/c-api/init_config.rst
@@ -196,12 +196,12 @@ PyPreConfig
Function to initialize a preconfiguration:
- .. c:function:: void PyPreConfig_InitIsolatedConfig(PyPreConfig *preconfig)
+ .. c:function:: void PyPreConfig_InitPythonConfig(PyPreConfig *preconfig)
Initialize the preconfiguration with :ref:`Python Configuration
<init-python-config>`.
- .. c:function:: void PyPreConfig_InitPythonConfig(PyPreConfig *preconfig)
+ .. c:function:: void PyPreConfig_InitIsolatedConfig(PyPreConfig *preconfig)
Initialize the preconfiguration with :ref:`Isolated Configuration
<init-isolated-conf>`.
1
0
https://github.com/python/cpython/commit/741008a57bdc95090b8be6ded5a9fd3f17…
commit: 741008a57bdc95090b8be6ded5a9fd3f17f7bf21
branch: master
author: tomerv <tomerv(a)gmail.com>
committer: GitHub <noreply(a)github.com>
date: 2020-07-01T11:32:54+02:00
summary:
Doc: Minor fix to init config C API documentation (GH-21198)
Co-authored-by: Tomer Vromen <tomer.vromen(a)intel.com>
files:
M Doc/c-api/init_config.rst
diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst
index 84064d93ea3b1..37f5b9f880bf1 100644
--- a/Doc/c-api/init_config.rst
+++ b/Doc/c-api/init_config.rst
@@ -197,12 +197,12 @@ PyPreConfig
Function to initialize a preconfiguration:
- .. c:function:: void PyPreConfig_InitIsolatedConfig(PyPreConfig *preconfig)
+ .. c:function:: void PyPreConfig_InitPythonConfig(PyPreConfig *preconfig)
Initialize the preconfiguration with :ref:`Python Configuration
<init-python-config>`.
- .. c:function:: void PyPreConfig_InitPythonConfig(PyPreConfig *preconfig)
+ .. c:function:: void PyPreConfig_InitIsolatedConfig(PyPreConfig *preconfig)
Initialize the preconfiguration with :ref:`Isolated Configuration
<init-isolated-conf>`.
1
0