Skip to content

Commit a0f3375

Browse files
committed
Merge fix for issue #22166 from 3.4
2 parents 0b894b4 + 8fad167 commit a0f3375

File tree

5 files changed

+102
-0
lines changed

5 files changed

+102
-0
lines changed

Include/codecs.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ PyAPI_FUNC(int) PyCodec_Register(
4949
PyAPI_FUNC(PyObject *) _PyCodec_Lookup(
5050
const char *encoding
5151
);
52+
53+
PyAPI_FUNC(int) _PyCodec_Forget(
54+
const char *encoding
55+
);
5256
#endif
5357

5458
/* Codec registry encoding check API.

Lib/test/test_codecs.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2586,6 +2586,14 @@ def _get_test_codec(codec_name):
25862586
return _TEST_CODECS.get(codec_name)
25872587
codecs.register(_get_test_codec) # Returns None, not usable as a decorator
25882588

2589+
try:
2590+
# Issue #22166: Also need to clear the internal cache in CPython
2591+
from _codecs import _forget_codec
2592+
except ImportError:
2593+
def _forget_codec(codec_name):
2594+
pass
2595+
2596+
25892597
class ExceptionChainingTest(unittest.TestCase):
25902598

25912599
def setUp(self):
@@ -2611,6 +2619,12 @@ def setUp(self):
26112619

26122620
def tearDown(self):
26132621
_TEST_CODECS.pop(self.codec_name, None)
2622+
# Issue #22166: Also pop from caches to avoid appearance of ref leaks
2623+
encodings._cache.pop(self.codec_name, None)
2624+
try:
2625+
_forget_codec(self.codec_name)
2626+
except KeyError:
2627+
pass
26142628

26152629
def set_codec(self, encode, decode):
26162630
codec_info = codecs.CodecInfo(encode, decode,

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,10 @@ _ Issue #21597: The separator between the turtledemo text pane and the drawing
525525

526526
- Issue #21525: Most Tkinter methods which accepted tuples now accept lists too.
527527

528+
- Issue #22166: with the assistance of a new internal _codecs._forget_codec
529+
helping function, test_codecs now clears the encoding caches to avoid the
530+
appearance of a reference leak
531+
528532
- Issue #22236: Tkinter tests now don't reuse default root window. New root
529533
window is created for every test class.
530534

Modules/_codecsmodule.c

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ Copyright (c) Corporation for National Research Initiatives.
4242
#include <windows.h>
4343
#endif
4444

45+
/*[clinic input]
46+
module _codecs
47+
[clinic start generated code]*/
48+
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=e1390e3da3cb9deb]*/
49+
50+
4551
/* --- Registry ----------------------------------------------------------- */
4652

4753
PyDoc_STRVAR(register__doc__,
@@ -138,6 +144,53 @@ codec_decode(PyObject *self, PyObject *args, PyObject *kwargs)
138144

139145
/* --- Helpers ------------------------------------------------------------ */
140146

147+
/*[clinic input]
148+
_codecs._forget_codec
149+
150+
encoding: str
151+
/
152+
153+
Purge the named codec from the internal codec lookup cache
154+
[clinic start generated code]*/
155+
156+
PyDoc_STRVAR(_codecs__forget_codec__doc__,
157+
"_forget_codec($module, encoding, /)\n"
158+
"--\n"
159+
"\n"
160+
"Purge the named codec from the internal codec lookup cache");
161+
162+
#define _CODECS__FORGET_CODEC_METHODDEF \
163+
{"_forget_codec", (PyCFunction)_codecs__forget_codec, METH_VARARGS, _codecs__forget_codec__doc__},
164+
165+
static PyObject *
166+
_codecs__forget_codec_impl(PyModuleDef *module, const char *encoding);
167+
168+
static PyObject *
169+
_codecs__forget_codec(PyModuleDef *module, PyObject *args)
170+
{
171+
PyObject *return_value = NULL;
172+
const char *encoding;
173+
174+
if (!PyArg_ParseTuple(args,
175+
"s:_forget_codec",
176+
&encoding))
177+
goto exit;
178+
return_value = _codecs__forget_codec_impl(module, encoding);
179+
180+
exit:
181+
return return_value;
182+
}
183+
184+
static PyObject *
185+
_codecs__forget_codec_impl(PyModuleDef *module, const char *encoding)
186+
/*[clinic end generated code: output=a75e631591702a5c input=18d5d92d0e386c38]*/
187+
{
188+
if (_PyCodec_Forget(encoding) < 0) {
189+
return NULL;
190+
};
191+
Py_RETURN_NONE;
192+
}
193+
141194
static
142195
PyObject *codec_tuple(PyObject *unicode,
143196
Py_ssize_t len)
@@ -1172,6 +1225,7 @@ static PyMethodDef _codecs_functions[] = {
11721225
register_error__doc__},
11731226
{"lookup_error", lookup_error, METH_VARARGS,
11741227
lookup_error__doc__},
1228+
_CODECS__FORGET_CODEC_METHODDEF
11751229
{NULL, NULL} /* sentinel */
11761230
};
11771231

Python/codecs.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,32 @@ PyObject *_PyCodec_Lookup(const char *encoding)
185185
return NULL;
186186
}
187187

188+
int _PyCodec_Forget(const char *encoding)
189+
{
190+
PyInterpreterState *interp;
191+
PyObject *v;
192+
int result;
193+
194+
interp = PyThreadState_GET()->interp;
195+
if (interp->codec_search_path == NULL) {
196+
return -1;
197+
}
198+
199+
/* Convert the encoding to a normalized Python string: all
200+
characters are converted to lower case, spaces and hyphens are
201+
replaced with underscores. */
202+
v = normalizestring(encoding);
203+
if (v == NULL) {
204+
return -1;
205+
}
206+
207+
/* Drop the named codec from the internal cache */
208+
result = PyDict_DelItem(interp->codec_search_cache, v);
209+
Py_DECREF(v);
210+
211+
return result;
212+
}
213+
188214
/* Codec registry encoding check API. */
189215

190216
int PyCodec_KnownEncoding(const char *encoding)

0 commit comments

Comments
 (0)