bpo-41842: Add codecs.unregister() function (GH-22360)

Add codecs.unregister() and PyCodec_Unregister() functions
to unregister a codec search function.
This commit is contained in:
Hai Shi 2020-09-29 05:41:11 +08:00 committed by GitHub
parent 24ba3b0df5
commit d332e7b816
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 108 additions and 5 deletions

View file

@ -10,6 +10,14 @@ Codec registry and support functions
As side effect, this tries to load the :mod:`encodings` package, if not yet As side effect, this tries to load the :mod:`encodings` package, if not yet
done, to make sure that it is always first in the list of search functions. done, to make sure that it is always first in the list of search functions.
.. c:function:: int PyCodec_Unregister(PyObject *search_function)
Unregister a codec search function and clear the registry's cache.
If the search function is not registered, do nothing.
Return 0 on success. Raise an exception and return -1 on error.
.. versionadded:: 3.10
.. c:function:: int PyCodec_KnownEncoding(const char *encoding) .. c:function:: int PyCodec_KnownEncoding(const char *encoding)
Return ``1`` or ``0`` depending on whether there is a registered codec for Return ``1`` or ``0`` depending on whether there is a registered codec for

View file

@ -163,11 +163,14 @@ function:
:class:`CodecInfo` object. In case a search function cannot find :class:`CodecInfo` object. In case a search function cannot find
a given encoding, it should return ``None``. a given encoding, it should return ``None``.
.. note::
Search function registration is not currently reversible, .. function:: unregister(search_function)
which may cause problems in some cases, such as unit testing or
module reloading. Unregister a codec search function and clear the registry's cache.
If the search function is not registered, do nothing.
.. versionadded:: 3.10
While the builtin :func:`open` and the associated :mod:`io` module are the While the builtin :func:`open` and the associated :mod:`io` module are the
recommended approach for working with encoded text files, this module recommended approach for working with encoded text files, this module

View file

@ -109,6 +109,12 @@ base64
Add :func:`base64.b32hexencode` and :func:`base64.b32hexdecode` to support the Add :func:`base64.b32hexencode` and :func:`base64.b32hexdecode` to support the
Base32 Encoding with Extended Hex Alphabet. Base32 Encoding with Extended Hex Alphabet.
codecs
------
Add a :func:`codecs.unregister` function to unregister a codec search function.
(Contributed by Hai Shi in :issue:`41842`.)
curses curses
------ ------
@ -237,6 +243,10 @@ New Features
:class:`datetime.time` objects. :class:`datetime.time` objects.
(Contributed by Zackery Spytz in :issue:`30155`.) (Contributed by Zackery Spytz in :issue:`30155`.)
* Add a :c:func:`PyCodec_Unregister` function to unregister a codec
search function.
(Contributed by Hai Shi in :issue:`41842`.)
Porting to Python 3.10 Porting to Python 3.10
---------------------- ----------------------

View file

@ -27,6 +27,14 @@ PyAPI_FUNC(int) PyCodec_Register(
PyObject *search_function PyObject *search_function
); );
/* Unregister a codec search function and clear the registry's cache.
If the search function is not registered, do nothing.
Return 0 on success. Raise an exception and return -1 on error. */
PyAPI_FUNC(int) PyCodec_Unregister(
PyObject *search_function
);
/* Codec registry lookup API. /* Codec registry lookup API.
Looks up the given encoding and returns a CodecInfo object with Looks up the given encoding and returns a CodecInfo object with

View file

@ -1641,6 +1641,18 @@ class CodecsModuleTest(unittest.TestCase):
self.assertRaises(TypeError, codecs.register) self.assertRaises(TypeError, codecs.register)
self.assertRaises(TypeError, codecs.register, 42) self.assertRaises(TypeError, codecs.register, 42)
def test_unregister(self):
name = "nonexistent_codec_name"
search_function = mock.Mock()
codecs.register(search_function)
self.assertRaises(TypeError, codecs.lookup, name)
search_function.assert_called_with(name)
search_function.reset_mock()
codecs.unregister(search_function)
self.assertRaises(LookupError, codecs.lookup, name)
search_function.assert_not_called()
def test_lookup(self): def test_lookup(self):
self.assertRaises(TypeError, codecs.lookup) self.assertRaises(TypeError, codecs.lookup)
self.assertRaises(LookupError, codecs.lookup, "__spam__") self.assertRaises(LookupError, codecs.lookup, "__spam__")

View file

@ -1575,6 +1575,7 @@ Akash Shende
Charlie Shepherd Charlie Shepherd
Bruce Sherwood Bruce Sherwood
Gregory Shevchenko Gregory Shevchenko
Hai Shi
Alexander Shigin Alexander Shigin
Pete Shinners Pete Shinners
Michael Shiplett Michael Shiplett

View file

@ -0,0 +1,2 @@
Add :c:func:`PyCodec_Unregister` function to unregister a codec search
function.

View file

@ -0,0 +1 @@
Add :func:`codecs.unregister` function to unregister a codec search function.

View file

@ -68,6 +68,27 @@ _codecs_register(PyObject *module, PyObject *search_function)
Py_RETURN_NONE; Py_RETURN_NONE;
} }
/*[clinic input]
_codecs.unregister
search_function: object
/
Unregister a codec search function and clear the registry's cache.
If the search function is not registered, do nothing.
[clinic start generated code]*/
static PyObject *
_codecs_unregister(PyObject *module, PyObject *search_function)
/*[clinic end generated code: output=1f0edee9cf246399 input=dd7c004c652d345e]*/
{
if (PyCodec_Unregister(search_function) < 0) {
return NULL;
}
Py_RETURN_NONE;
}
/*[clinic input] /*[clinic input]
_codecs.lookup _codecs.lookup
encoding: str encoding: str
@ -992,6 +1013,7 @@ _codecs_lookup_error_impl(PyObject *module, const char *name)
static PyMethodDef _codecs_functions[] = { static PyMethodDef _codecs_functions[] = {
_CODECS_REGISTER_METHODDEF _CODECS_REGISTER_METHODDEF
_CODECS_UNREGISTER_METHODDEF
_CODECS_LOOKUP_METHODDEF _CODECS_LOOKUP_METHODDEF
_CODECS_ENCODE_METHODDEF _CODECS_ENCODE_METHODDEF
_CODECS_DECODE_METHODDEF _CODECS_DECODE_METHODDEF

View file

@ -15,6 +15,17 @@ PyDoc_STRVAR(_codecs_register__doc__,
#define _CODECS_REGISTER_METHODDEF \ #define _CODECS_REGISTER_METHODDEF \
{"register", (PyCFunction)_codecs_register, METH_O, _codecs_register__doc__}, {"register", (PyCFunction)_codecs_register, METH_O, _codecs_register__doc__},
PyDoc_STRVAR(_codecs_unregister__doc__,
"unregister($module, search_function, /)\n"
"--\n"
"\n"
"Unregister a codec search function and clear the registry\'s cache.\n"
"\n"
"If the search function is not registered, do nothing.");
#define _CODECS_UNREGISTER_METHODDEF \
{"unregister", (PyCFunction)_codecs_unregister, METH_O, _codecs_unregister__doc__},
PyDoc_STRVAR(_codecs_lookup__doc__, PyDoc_STRVAR(_codecs_lookup__doc__,
"lookup($module, encoding, /)\n" "lookup($module, encoding, /)\n"
"--\n" "--\n"
@ -2827,4 +2838,4 @@ exit:
#ifndef _CODECS_CODE_PAGE_ENCODE_METHODDEF #ifndef _CODECS_CODE_PAGE_ENCODE_METHODDEF
#define _CODECS_CODE_PAGE_ENCODE_METHODDEF #define _CODECS_CODE_PAGE_ENCODE_METHODDEF
#endif /* !defined(_CODECS_CODE_PAGE_ENCODE_METHODDEF) */ #endif /* !defined(_CODECS_CODE_PAGE_ENCODE_METHODDEF) */
/*[clinic end generated code: output=eeead01414be6e42 input=a9049054013a1b77]*/ /*[clinic end generated code: output=9a97e2ddf3e69072 input=a9049054013a1b77]*/

View file

@ -50,6 +50,31 @@ int PyCodec_Register(PyObject *search_function)
return -1; return -1;
} }
int
PyCodec_Unregister(PyObject *search_function)
{
PyInterpreterState *interp = PyInterpreterState_Get();
PyObject *codec_search_path = interp->codec_search_path;
/* Do nothing if codec_search_path is not created yet or was cleared. */
if (codec_search_path == NULL) {
return 0;
}
assert(PyList_CheckExact(codec_search_path));
Py_ssize_t n = PyList_GET_SIZE(codec_search_path);
for (Py_ssize_t i = 0; i < n; i++) {
PyObject *item = PyList_GET_ITEM(codec_search_path, i);
if (item == search_function) {
if (interp->codec_search_cache != NULL) {
assert(PyDict_CheckExact(interp->codec_search_cache));
PyDict_Clear(interp->codec_search_cache);
}
return PyList_SetSlice(codec_search_path, i, i+1, NULL);
}
}
return 0;
}
extern int _Py_normalize_encoding(const char *, char *, size_t); extern int _Py_normalize_encoding(const char *, char *, size_t);
/* Convert a string to a normalized Python string(decoded from UTF-8): all characters are /* Convert a string to a normalized Python string(decoded from UTF-8): all characters are